From 16f501ee3382ce3805b9c9198c10076145521718 Mon Sep 17 00:00:00 2001 From: Stephen Sigwart Date: Sat, 6 Nov 2021 12:02:54 -0400 Subject: [PATCH 001/926] Fix single line if/else and loop indentation --- extensions/php/language-configuration.json | 9 +++++++++ extensions/typescript-basics/language-configuration.json | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/extensions/php/language-configuration.json b/extensions/php/language-configuration.json index e585ebf1c91..f44d7a25cb6 100644 --- a/extensions/php/language-configuration.json +++ b/extensions/php/language-configuration.json @@ -76,6 +76,15 @@ "indent": "none", "removeText": 1 } + }, + { + // Decrease indentation after single line if/else if/else, for, foreach, or while + "previousLineText": "^\\s*(((else ?)?if|for(each)?|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" + } } ] } diff --git a/extensions/typescript-basics/language-configuration.json b/extensions/typescript-basics/language-configuration.json index 07260718b64..d940228f3e4 100644 --- a/extensions/typescript-basics/language-configuration.json +++ b/extensions/typescript-basics/language-configuration.json @@ -188,6 +188,15 @@ "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": "indentOutdent" + } } ] } From 3894c48a02af26e5ddadcaacb5efcb2577a3e4dc Mon Sep 17 00:00:00 2001 From: Stephen Sigwart Date: Mon, 21 Nov 2022 21:41:18 -0500 Subject: [PATCH 002/926] JS/TS fixes --- .../javascript/javascript-language-configuration.json | 9 +++++++++ extensions/typescript-basics/language-configuration.json | 6 +++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/extensions/javascript/javascript-language-configuration.json b/extensions/javascript/javascript-language-configuration.json index abfef6e8c4c..4029985233a 100644 --- a/extensions/javascript/javascript-language-configuration.json +++ b/extensions/javascript/javascript-language-configuration.json @@ -188,6 +188,15 @@ "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" + } } ] } diff --git a/extensions/typescript-basics/language-configuration.json b/extensions/typescript-basics/language-configuration.json index e0f21535797..03f06fa04d5 100644 --- a/extensions/typescript-basics/language-configuration.json +++ b/extensions/typescript-basics/language-configuration.json @@ -209,11 +209,11 @@ }, { // Decrease indentation after single line if/else if/else, for, or while - "previousLineText": "/^\\s*(((else )?if|for|while)\\s*\\(.*\\)\\s*|else\\s*)$/", + "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))/", + "beforeText": "^\\s+([^{i\\s]|i(?!f\\b))", "action": { - "indent": "indentOutdent" + "indent": "outdent" } } ] From a99fb2c5c313207a127e5adf534d10329df772ec Mon Sep 17 00:00:00 2001 From: Stephen Sigwart Date: Tue, 28 Feb 2023 21:17:35 -0500 Subject: [PATCH 003/926] Add to C/C++ --- extensions/cpp/language-configuration.json | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/extensions/cpp/language-configuration.json b/extensions/cpp/language-configuration.json index 0f365702269..3a5459401f9 100644 --- a/extensions/cpp/language-configuration.json +++ b/extensions/cpp/language-configuration.json @@ -30,5 +30,16 @@ "start": "^\\s*#pragma\\s+region\\b", "end": "^\\s*#pragma\\s+endregion\\b" } - } + }, + "onEnterRules": [ + { + // 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" + } + } + ] } From 439f7e5bcc4deff7ee2576a3c400a7874fe5e230 Mon Sep 17 00:00:00 2001 From: Malte Werschy Date: Thu, 23 Mar 2023 08:19:21 +0100 Subject: [PATCH 004/926] Implement indent guide colorization options. --- .../lib/stylelint/vscode-known-variables.json | 14 ++++++- .../viewParts/indentGuides/indentGuides.css | 8 ---- .../viewParts/indentGuides/indentGuides.ts | 41 ++++++++++++++++++- .../editor/common/core/editorColorRegistry.ts | 16 +++++++- src/vs/editor/standalone/common/themes.ts | 18 ++++---- .../themes/common/themeCompatibility.ts | 4 +- 6 files changed, 76 insertions(+), 25 deletions(-) diff --git a/build/lib/stylelint/vscode-known-variables.json b/build/lib/stylelint/vscode-known-variables.json index 593ef241b40..a536f51563d 100644 --- a/build/lib/stylelint/vscode-known-variables.json +++ b/build/lib/stylelint/vscode-known-variables.json @@ -203,8 +203,18 @@ "--vscode-editorHoverWidget-foreground", "--vscode-editorHoverWidget-highlightForeground", "--vscode-editorHoverWidget-statusBarBackground", - "--vscode-editorIndentGuide-activeBackground", - "--vscode-editorIndentGuide-background", + "--vscode-editorIndentGuide-activeBackground1", + "--vscode-editorIndentGuide-activeBackground2", + "--vscode-editorIndentGuide-activeBackground3", + "--vscode-editorIndentGuide-activeBackground4", + "--vscode-editorIndentGuide-activeBackground5", + "--vscode-editorIndentGuide-activeBackground6", + "--vscode-editorIndentGuide-background1", + "--vscode-editorIndentGuide-background2", + "--vscode-editorIndentGuide-background3", + "--vscode-editorIndentGuide-background4", + "--vscode-editorIndentGuide-background5", + "--vscode-editorIndentGuide-background6", "--vscode-editorInfo-background", "--vscode-editorInfo-border", "--vscode-editorInfo-foreground", diff --git a/src/vs/editor/browser/viewParts/indentGuides/indentGuides.css b/src/vs/editor/browser/viewParts/indentGuides/indentGuides.css index fa78807bf71..ed132669757 100644 --- a/src/vs/editor/browser/viewParts/indentGuides/indentGuides.css +++ b/src/vs/editor/browser/viewParts/indentGuides/indentGuides.css @@ -7,11 +7,3 @@ position: absolute; box-sizing: border-box; } - -.monaco-editor .lines-content .core-guide-indent { - box-shadow: 1px 0 0 0 var(--vscode-editorIndentGuide-background) inset; -} - -.monaco-editor .lines-content .core-guide-indent-active { - box-shadow: 1px 0 0 0 var(--vscode-editorIndentGuide-activeBackground, --vscode-editorIndentGuide-background) inset; -} diff --git a/src/vs/editor/browser/viewParts/indentGuides/indentGuides.ts b/src/vs/editor/browser/viewParts/indentGuides/indentGuides.ts index 43caef8a5cd..a93cf75a530 100644 --- a/src/vs/editor/browser/viewParts/indentGuides/indentGuides.ts +++ b/src/vs/editor/browser/viewParts/indentGuides/indentGuides.ts @@ -5,7 +5,7 @@ import 'vs/css!./indentGuides'; import { DynamicViewOverlay } from 'vs/editor/browser/view/dynamicViewOverlay'; -import { editorBracketHighlightingForeground1, editorBracketHighlightingForeground2, editorBracketHighlightingForeground3, editorBracketHighlightingForeground4, editorBracketHighlightingForeground5, editorBracketHighlightingForeground6, editorBracketPairGuideActiveBackground1, editorBracketPairGuideActiveBackground2, editorBracketPairGuideActiveBackground3, editorBracketPairGuideActiveBackground4, editorBracketPairGuideActiveBackground5, editorBracketPairGuideActiveBackground6, editorBracketPairGuideBackground1, editorBracketPairGuideBackground2, editorBracketPairGuideBackground3, editorBracketPairGuideBackground4, editorBracketPairGuideBackground5, editorBracketPairGuideBackground6 } from 'vs/editor/common/core/editorColorRegistry'; +import { editorBracketHighlightingForeground1, editorBracketHighlightingForeground2, editorBracketHighlightingForeground3, editorBracketHighlightingForeground4, editorBracketHighlightingForeground5, editorBracketHighlightingForeground6, editorBracketPairGuideActiveBackground1, editorBracketPairGuideActiveBackground2, editorBracketPairGuideActiveBackground3, editorBracketPairGuideActiveBackground4, editorBracketPairGuideActiveBackground5, editorBracketPairGuideActiveBackground6, editorBracketPairGuideBackground1, editorBracketPairGuideBackground2, editorBracketPairGuideBackground3, editorBracketPairGuideBackground4, editorBracketPairGuideBackground5, editorBracketPairGuideBackground6, editorIndentGuide1, editorIndentGuide2, editorIndentGuide3, editorIndentGuide4, editorIndentGuide5, editorIndentGuide6, editorActiveIndentGuide1, editorActiveIndentGuide2, editorActiveIndentGuide3, editorActiveIndentGuide4, editorActiveIndentGuide5, editorActiveIndentGuide6 } from 'vs/editor/common/core/editorColorRegistry'; import { RenderingContext } from 'vs/editor/browser/view/renderingContext'; import { ViewContext } from 'vs/editor/common/viewModel/viewContext'; import * as viewEvents from 'vs/editor/common/viewEvents'; @@ -224,7 +224,7 @@ export class IndentGuidesOverlay extends DynamicViewOverlay { new IndentGuide( indentGuide, -1, - isActive ? 'core-guide-indent-active' : 'core-guide-indent', + `core-guide-indent lvl-${(indentLvl - 1) % 30}` + (isActive ? ' indent-active' : ''), null, -1, -1, @@ -270,6 +270,14 @@ registerThemingParticipant((theme, collector) => { ]; const colorProvider = new BracketPairGuidesClassNames(); + const indentColors = [ + { indentColor: editorIndentGuide1, indentColorActive: editorActiveIndentGuide1 }, + { indentColor: editorIndentGuide2, indentColorActive: editorActiveIndentGuide2 }, + { indentColor: editorIndentGuide3, indentColorActive: editorActiveIndentGuide3 }, + { indentColor: editorIndentGuide4, indentColorActive: editorActiveIndentGuide4 }, + { indentColor: editorIndentGuide5, indentColorActive: editorActiveIndentGuide5 }, + { indentColor: editorIndentGuide6, indentColorActive: editorActiveIndentGuide6 }, + ]; const colorValues = colors .map(c => { @@ -291,6 +299,25 @@ registerThemingParticipant((theme, collector) => { }) .filter(isDefined); + const indentColorValues = indentColors + .map(c => { + const indentColor = theme.getColor(c.indentColor); + const indentColorActive = theme.getColor(c.indentColorActive); + + const effectiveIndentColor = transparentToUndefined(indentColor); + const effectiveIndentColorActive = transparentToUndefined(indentColorActive); + + if (!effectiveIndentColor || !effectiveIndentColorActive) { + return undefined; + } + + return { + indentColor: effectiveIndentColor, + indentColorActive: effectiveIndentColorActive, + }; + }) + .filter(isDefined); + if (colorValues.length > 0) { for (let level = 0; level < 30; level++) { const colors = colorValues[level % colorValues.length]; @@ -305,4 +332,14 @@ registerThemingParticipant((theme, collector) => { collector.addRule(`.monaco-editor .horizontal-top.${colorProvider.activeClassName} { border-top: 1px solid var(--guide-color-active); }`); collector.addRule(`.monaco-editor .horizontal-bottom.${colorProvider.activeClassName} { border-bottom: 1px solid var(--guide-color-active); }`); } + + if (indentColorValues.length > 0) { + for (let level = 0; level < 30; level++) { + const colors = indentColorValues[level % indentColorValues.length]; + collector.addRule(`.monaco-editor .lines-content .core-guide-indent.lvl-${level} { --indent-color: ${colors.indentColor}; --indent-color-active: ${colors.indentColorActive}; }`); + } + + collector.addRule(`.monaco-editor .lines-content .core-guide-indent { box-shadow: 1px 0 0 0 var(--indent-color) inset; }`); + collector.addRule(`.monaco-editor .lines-content .core-guide-indent.indent-active { box-shadow: 1px 0 0 0 var(--indent-color-active) inset; }`); + } }); diff --git a/src/vs/editor/common/core/editorColorRegistry.ts b/src/vs/editor/common/core/editorColorRegistry.ts index 30e2cdd1dd4..a5c766a9019 100644 --- a/src/vs/editor/common/core/editorColorRegistry.ts +++ b/src/vs/editor/common/core/editorColorRegistry.ts @@ -21,10 +21,22 @@ export const editorSymbolHighlightBorder = registerColor('editor.symbolHighlight export const editorCursorForeground = registerColor('editorCursor.foreground', { dark: '#AEAFAD', light: Color.black, hcDark: Color.white, hcLight: '#0F4A85' }, nls.localize('caret', 'Color of the editor cursor.')); export const editorCursorBackground = registerColor('editorCursor.background', null, nls.localize('editorCursorBackground', 'The background color of the editor cursor. Allows customizing the color of a character overlapped by a block cursor.')); export const editorWhitespaces = registerColor('editorWhitespace.foreground', { dark: '#e3e4e229', light: '#33333333', hcDark: '#e3e4e229', hcLight: '#CCCCCC' }, nls.localize('editorWhitespaces', 'Color of whitespace characters in the editor.')); -export const editorIndentGuides = registerColor('editorIndentGuide.background', { dark: editorWhitespaces, light: editorWhitespaces, hcDark: editorWhitespaces, hcLight: editorWhitespaces }, nls.localize('editorIndentGuides', 'Color of the editor indentation guides.')); -export const editorActiveIndentGuides = registerColor('editorIndentGuide.activeBackground', { dark: editorWhitespaces, light: editorWhitespaces, hcDark: editorWhitespaces, hcLight: editorWhitespaces }, nls.localize('editorActiveIndentGuide', 'Color of the active editor indentation guides.')); export const editorLineNumbers = registerColor('editorLineNumber.foreground', { dark: '#858585', light: '#237893', hcDark: Color.white, hcLight: '#292929' }, nls.localize('editorLineNumbers', 'Color of editor line numbers.')); +export const editorIndentGuide1 = registerColor('editorIndentGuide.background1', { dark: editorWhitespaces, light: editorWhitespaces, hcDark: editorWhitespaces, hcLight: editorWhitespaces }, nls.localize('editorIndentGuides1', 'Color of the editor indentation guides (1).')); +export const editorIndentGuide2 = registerColor('editorIndentGuide.background2', { dark: '#00000000', light: '#00000000', hcDark: '#00000000', hcLight: '#00000000' }, nls.localize('editorIndentGuides2', 'Color of the editor indentation guides (2).')); +export const editorIndentGuide3 = registerColor('editorIndentGuide.background3', { dark: '#00000000', light: '#00000000', hcDark: '#00000000', hcLight: '#00000000' }, nls.localize('editorIndentGuides3', 'Color of the editor indentation guides (3).')); +export const editorIndentGuide4 = registerColor('editorIndentGuide.background4', { dark: '#00000000', light: '#00000000', hcDark: '#00000000', hcLight: '#00000000' }, nls.localize('editorIndentGuides4', 'Color of the editor indentation guides (4).')); +export const editorIndentGuide5 = registerColor('editorIndentGuide.background5', { dark: '#00000000', light: '#00000000', hcDark: '#00000000', hcLight: '#00000000' }, nls.localize('editorIndentGuides5', 'Color of the editor indentation guides (5).')); +export const editorIndentGuide6 = registerColor('editorIndentGuide.background6', { dark: '#00000000', light: '#00000000', hcDark: '#00000000', hcLight: '#00000000' }, nls.localize('editorIndentGuides6', 'Color of the editor indentation guides (6).')); + +export const editorActiveIndentGuide1 = registerColor('editorIndentGuide.activeBackground1', { dark: editorWhitespaces, light: editorWhitespaces, hcDark: editorWhitespaces, hcLight: editorWhitespaces }, nls.localize('editorActiveIndentGuide1', 'Color of the active editor indentation guides (1).')); +export const editorActiveIndentGuide2 = registerColor('editorIndentGuide.activeBackground2', { dark: '#00000000', light: '#00000000', hcDark: '#00000000', hcLight: '#00000000' }, nls.localize('editorActiveIndentGuide2', 'Color of the active editor indentation guides (2).')); +export const editorActiveIndentGuide3 = registerColor('editorIndentGuide.activeBackground3', { dark: '#00000000', light: '#00000000', hcDark: '#00000000', hcLight: '#00000000' }, nls.localize('editorActiveIndentGuide3', 'Color of the active editor indentation guides (3).')); +export const editorActiveIndentGuide4 = registerColor('editorIndentGuide.activeBackground4', { dark: '#00000000', light: '#00000000', hcDark: '#00000000', hcLight: '#00000000' }, nls.localize('editorActiveIndentGuide4', 'Color of the active editor indentation guides (4).')); +export const editorActiveIndentGuide5 = registerColor('editorIndentGuide.activeBackground5', { dark: '#00000000', light: '#00000000', hcDark: '#00000000', hcLight: '#00000000' }, nls.localize('editorActiveIndentGuide5', 'Color of the active editor indentation guides (5).')); +export const editorActiveIndentGuide6 = registerColor('editorIndentGuide.activeBackground6', { dark: '#00000000', light: '#00000000', hcDark: '#00000000', hcLight: '#00000000' }, nls.localize('editorActiveIndentGuide6', 'Color of the active editor indentation guides (6).')); + const deprecatedEditorActiveLineNumber = registerColor('editorActiveLineNumber.foreground', { dark: '#c6c6c6', light: '#0B216F', hcDark: activeContrastBorder, hcLight: activeContrastBorder }, nls.localize('editorActiveLineNumber', 'Color of editor active line number'), false, nls.localize('deprecatedEditorActiveLineNumber', 'Id is deprecated. Use \'editorLineNumber.activeForeground\' instead.')); export const editorActiveLineNumber = registerColor('editorLineNumber.activeForeground', { dark: deprecatedEditorActiveLineNumber, light: deprecatedEditorActiveLineNumber, hcDark: deprecatedEditorActiveLineNumber, hcLight: deprecatedEditorActiveLineNumber }, nls.localize('editorActiveLineNumber', 'Color of editor active line number')); export const editorDimmedLineNumber = registerColor('editorLineNumber.dimmedForeground', { dark: null, light: null, hcDark: null, hcLight: null }, nls.localize('editorDimmedLineNumber', 'Color of the final editor line when editor.renderFinalNewline is set to dimmed.')); diff --git a/src/vs/editor/standalone/common/themes.ts b/src/vs/editor/standalone/common/themes.ts index e283e315d2c..e5f9b91bf22 100644 --- a/src/vs/editor/standalone/common/themes.ts +++ b/src/vs/editor/standalone/common/themes.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { editorActiveIndentGuides, editorIndentGuides } from 'vs/editor/common/core/editorColorRegistry'; +import { editorActiveIndentGuide1, editorIndentGuide1 } from 'vs/editor/common/core/editorColorRegistry'; import { IStandaloneThemeData } from 'vs/editor/standalone/common/standaloneTheme'; import { editorBackground, editorForeground, editorInactiveSelection, editorSelectionHighlight } from 'vs/platform/theme/common/colorRegistry'; @@ -71,8 +71,8 @@ export const vs: IStandaloneThemeData = { [editorBackground]: '#FFFFFE', [editorForeground]: '#000000', [editorInactiveSelection]: '#E5EBF1', - [editorIndentGuides]: '#D3D3D3', - [editorActiveIndentGuides]: '#939393', + [editorIndentGuide1]: '#D3D3D3', + [editorActiveIndentGuide1]: '#939393', [editorSelectionHighlight]: '#ADD6FF4D' } }; @@ -142,8 +142,8 @@ export const vs_dark: IStandaloneThemeData = { [editorBackground]: '#1E1E1E', [editorForeground]: '#D4D4D4', [editorInactiveSelection]: '#3A3D41', - [editorIndentGuides]: '#404040', - [editorActiveIndentGuides]: '#707070', + [editorIndentGuide1]: '#404040', + [editorActiveIndentGuide1]: '#707070', [editorSelectionHighlight]: '#ADD6FF26' } }; @@ -204,8 +204,8 @@ export const hc_black: IStandaloneThemeData = { colors: { [editorBackground]: '#000000', [editorForeground]: '#FFFFFF', - [editorIndentGuides]: '#FFFFFF', - [editorActiveIndentGuides]: '#FFFFFF', + [editorIndentGuide1]: '#FFFFFF', + [editorActiveIndentGuide1]: '#FFFFFF', } }; /* -------------------------------- End hc-black theme -------------------------------- */ @@ -263,8 +263,8 @@ export const hc_light: IStandaloneThemeData = { colors: { [editorBackground]: '#FFFFFF', [editorForeground]: '#292929', - [editorIndentGuides]: '#292929', - [editorActiveIndentGuides]: '#292929', + [editorIndentGuide1]: '#292929', + [editorActiveIndentGuide1]: '#292929', } }; /* -------------------------------- End hc-light theme -------------------------------- */ diff --git a/src/vs/workbench/services/themes/common/themeCompatibility.ts b/src/vs/workbench/services/themes/common/themeCompatibility.ts index 503d6fa50dd..21c643584f0 100644 --- a/src/vs/workbench/services/themes/common/themeCompatibility.ts +++ b/src/vs/workbench/services/themes/common/themeCompatibility.ts @@ -64,8 +64,8 @@ addSettingMapping('lineHighlight', editorColorRegistry.editorLineHighlight); addSettingMapping('rangeHighlight', editorColorRegistry.editorRangeHighlight); addSettingMapping('caret', editorColorRegistry.editorCursorForeground); addSettingMapping('invisibles', editorColorRegistry.editorWhitespaces); -addSettingMapping('guide', editorColorRegistry.editorIndentGuides); -addSettingMapping('activeGuide', editorColorRegistry.editorActiveIndentGuides); +addSettingMapping('guide', editorColorRegistry.editorIndentGuide1); +addSettingMapping('activeGuide', editorColorRegistry.editorActiveIndentGuide1); const ansiColorMap = ['ansiBlack', 'ansiRed', 'ansiGreen', 'ansiYellow', 'ansiBlue', 'ansiMagenta', 'ansiCyan', 'ansiWhite', 'ansiBrightBlack', 'ansiBrightRed', 'ansiBrightGreen', 'ansiBrightYellow', 'ansiBrightBlue', 'ansiBrightMagenta', 'ansiBrightCyan', 'ansiBrightWhite' From f187064e5677d3512589d40289386a18569cb4fb Mon Sep 17 00:00:00 2001 From: Malte Werschy Date: Tue, 28 Mar 2023 00:07:18 +0200 Subject: [PATCH 005/926] deprecate old indent guide colors --- src/vs/editor/common/core/editorColorRegistry.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/common/core/editorColorRegistry.ts b/src/vs/editor/common/core/editorColorRegistry.ts index a5c766a9019..3d6668c6b60 100644 --- a/src/vs/editor/common/core/editorColorRegistry.ts +++ b/src/vs/editor/common/core/editorColorRegistry.ts @@ -23,14 +23,17 @@ export const editorCursorBackground = registerColor('editorCursor.background', n export const editorWhitespaces = registerColor('editorWhitespace.foreground', { dark: '#e3e4e229', light: '#33333333', hcDark: '#e3e4e229', hcLight: '#CCCCCC' }, nls.localize('editorWhitespaces', 'Color of whitespace characters in the editor.')); export const editorLineNumbers = registerColor('editorLineNumber.foreground', { dark: '#858585', light: '#237893', hcDark: Color.white, hcLight: '#292929' }, nls.localize('editorLineNumbers', 'Color of editor line numbers.')); -export const editorIndentGuide1 = registerColor('editorIndentGuide.background1', { dark: editorWhitespaces, light: editorWhitespaces, hcDark: editorWhitespaces, hcLight: editorWhitespaces }, nls.localize('editorIndentGuides1', 'Color of the editor indentation guides (1).')); +export const deprecatedEditorIndentGuides = registerColor('editorIndentGuide.background', { dark: editorWhitespaces, light: editorWhitespaces, hcDark: editorWhitespaces, hcLight: editorWhitespaces }, nls.localize('editorIndentGuides', 'Color of the editor indentation guides.'), false, nls.localize('deprecatedEditorIndentGuides', '\'editorIndentGuide.background\' is deprecated. Use \'editorIndentGuide.background1\' instead.')); +export const deprecatedEditorActiveIndentGuides = registerColor('editorIndentGuide.activeBackground', { dark: editorWhitespaces, light: editorWhitespaces, hcDark: editorWhitespaces, hcLight: editorWhitespaces }, nls.localize('editorActiveIndentGuide', 'Color of the active editor indentation guides.'), false, nls.localize('deprecatedEditorActiveIndentGuide', '\'editorIndentGuide.activeBackground\' is deprecated. Use \'editorIndentGuide.activeBackground1\' instead.')); + +export const editorIndentGuide1 = registerColor('editorIndentGuide.background1', { dark: deprecatedEditorIndentGuides, light: deprecatedEditorIndentGuides, hcDark: deprecatedEditorIndentGuides, hcLight: deprecatedEditorIndentGuides }, nls.localize('editorIndentGuides1', 'Color of the editor indentation guides (1).')); export const editorIndentGuide2 = registerColor('editorIndentGuide.background2', { dark: '#00000000', light: '#00000000', hcDark: '#00000000', hcLight: '#00000000' }, nls.localize('editorIndentGuides2', 'Color of the editor indentation guides (2).')); export const editorIndentGuide3 = registerColor('editorIndentGuide.background3', { dark: '#00000000', light: '#00000000', hcDark: '#00000000', hcLight: '#00000000' }, nls.localize('editorIndentGuides3', 'Color of the editor indentation guides (3).')); export const editorIndentGuide4 = registerColor('editorIndentGuide.background4', { dark: '#00000000', light: '#00000000', hcDark: '#00000000', hcLight: '#00000000' }, nls.localize('editorIndentGuides4', 'Color of the editor indentation guides (4).')); export const editorIndentGuide5 = registerColor('editorIndentGuide.background5', { dark: '#00000000', light: '#00000000', hcDark: '#00000000', hcLight: '#00000000' }, nls.localize('editorIndentGuides5', 'Color of the editor indentation guides (5).')); export const editorIndentGuide6 = registerColor('editorIndentGuide.background6', { dark: '#00000000', light: '#00000000', hcDark: '#00000000', hcLight: '#00000000' }, nls.localize('editorIndentGuides6', 'Color of the editor indentation guides (6).')); -export const editorActiveIndentGuide1 = registerColor('editorIndentGuide.activeBackground1', { dark: editorWhitespaces, light: editorWhitespaces, hcDark: editorWhitespaces, hcLight: editorWhitespaces }, nls.localize('editorActiveIndentGuide1', 'Color of the active editor indentation guides (1).')); +export const editorActiveIndentGuide1 = registerColor('editorIndentGuide.activeBackground1', { dark: deprecatedEditorActiveIndentGuides, light: deprecatedEditorActiveIndentGuides, hcDark: deprecatedEditorActiveIndentGuides, hcLight: deprecatedEditorActiveIndentGuides }, nls.localize('editorActiveIndentGuide1', 'Color of the active editor indentation guides (1).')); export const editorActiveIndentGuide2 = registerColor('editorIndentGuide.activeBackground2', { dark: '#00000000', light: '#00000000', hcDark: '#00000000', hcLight: '#00000000' }, nls.localize('editorActiveIndentGuide2', 'Color of the active editor indentation guides (2).')); export const editorActiveIndentGuide3 = registerColor('editorIndentGuide.activeBackground3', { dark: '#00000000', light: '#00000000', hcDark: '#00000000', hcLight: '#00000000' }, nls.localize('editorActiveIndentGuide3', 'Color of the active editor indentation guides (3).')); export const editorActiveIndentGuide4 = registerColor('editorIndentGuide.activeBackground4', { dark: '#00000000', light: '#00000000', hcDark: '#00000000', hcLight: '#00000000' }, nls.localize('editorActiveIndentGuide4', 'Color of the active editor indentation guides (4).')); From 2043283a650dd7b3f9789d63770e9c5dad558fc0 Mon Sep 17 00:00:00 2001 From: rebornix Date: Wed, 29 Mar 2023 14:21:47 -0700 Subject: [PATCH 006/926] List View top padding. --- src/vs/base/browser/ui/list/listView.ts | 24 +++++- src/vs/base/browser/ui/list/listWidget.ts | 1 + src/vs/base/browser/ui/list/rangeMap.ts | 25 +++++- .../test/browser/ui/list/rangeMap.test.ts | 80 +++++++++++++++++++ .../notebook/browser/notebookEditorWidget.ts | 19 ++--- 5 files changed, 130 insertions(+), 19 deletions(-) diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index abb5957922f..eaf35198f7d 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -62,6 +62,7 @@ export interface IListViewOptionsUpdate { readonly scrollByPage?: boolean; readonly mouseWheelScrollSensitivity?: number; readonly fastScrollSensitivity?: number; + readonly topPadding?: number; } export interface IListViewOptions extends IListViewOptionsUpdate { @@ -278,6 +279,7 @@ export class ListView implements IListView { private items: IItem[]; private itemId: number; private rangeMap: RangeMap; + private topPadding: number; private cache: RowCache; private renderers = new Map>(); private lastRenderTop: number; @@ -359,7 +361,8 @@ export class ListView implements IListView { this.items = []; this.itemId = 0; - this.rangeMap = new RangeMap(); + this.topPadding = options.topPadding ?? 0; + this.rangeMap = new RangeMap(this.topPadding); for (const renderer of renderers) { this.renderers.set(renderer.templateId, renderer); @@ -466,6 +469,23 @@ export class ListView implements IListView { if (scrollableOptions) { this.scrollableElement.updateOptions(scrollableOptions); } + + if (options.topPadding !== undefined && options.topPadding !== this.topPadding) { + // trigger a rerender + this.topPadding = options.topPadding; + const lastRenderRange = this.getRenderRange(this.lastRenderTop, this.lastRenderHeight); + const offset = options.topPadding - this.rangeMap.topPadding; + this.rangeMap.topPadding = options.topPadding; + + this.render(lastRenderRange, Math.max(0, this.lastRenderTop + offset), this.lastRenderHeight, undefined, undefined, true); + this.setScrollTop(this.lastRenderTop); + + this.eventuallyUpdateScrollDimensions(); + + if (this.supportDynamicHeights) { + this._rerender(this.lastRenderTop, this.lastRenderHeight); + } + } } delegateScrollFromMouseWheelEvent(browserEvent: IMouseWheelEvent) { @@ -597,7 +617,7 @@ export class ListView implements IListView { // TODO@joao: improve this optimization to catch even more cases if (start === 0 && deleteCount >= this.items.length) { - this.rangeMap = new RangeMap(); + this.rangeMap = new RangeMap(this.topPadding); this.rangeMap.splice(0, 0, inserted); deleted = this.items; this.items = inserted; diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index c8ff425cbb9..59c49687c15 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -972,6 +972,7 @@ export interface IListOptions extends IListOptionsUpdate { readonly scrollableElementChangeOptions?: ScrollableElementChangeOptions; readonly alwaysConsumeMouseWheel?: boolean; readonly initialSize?: Dimension; + readonly topPadding?: number; } export interface IListStyles { diff --git a/src/vs/base/browser/ui/list/rangeMap.ts b/src/vs/base/browser/ui/list/rangeMap.ts index 5656a3b32de..bd77b9a24b2 100644 --- a/src/vs/base/browser/ui/list/rangeMap.ts +++ b/src/vs/base/browser/ui/list/rangeMap.ts @@ -91,6 +91,21 @@ export class RangeMap { private groups: IRangedGroup[] = []; private _size = 0; + private _topPadding = 0; + + get topPadding() { + return this._topPadding; + } + + set topPadding(topPadding: number) { + this._topPadding = topPadding; + this._size = this._topPadding + this.groups.reduce((t, g) => t + (g.size * (g.range.end - g.range.start)), 0); + } + + constructor(topPadding?: number) { + this._topPadding = topPadding || 0; + this._size = this._topPadding; + } splice(index: number, deleteCount: number, items: IItem[] = []): void { const diff = items.length - deleteCount; @@ -104,7 +119,7 @@ export class RangeMap { })); this.groups = concat(before, middle, after); - this._size = this.groups.reduce((t, g) => t + (g.size * (g.range.end - g.range.start)), 0); + this._size = this._topPadding + this.groups.reduce((t, g) => t + (g.size * (g.range.end - g.range.start)), 0); } /** @@ -135,8 +150,12 @@ export class RangeMap { return -1; } + if (position < this._topPadding) { + return 0; + } + let index = 0; - let size = 0; + let size = this._topPadding; for (const group of this.groups) { const count = group.range.end - group.range.start; @@ -177,7 +196,7 @@ export class RangeMap { const newCount = count + groupCount; if (index < newCount) { - return position + ((index - count) * group.size); + return this._topPadding + position + ((index - count) * group.size); } position += groupCount * group.size; diff --git a/src/vs/base/test/browser/ui/list/rangeMap.test.ts b/src/vs/base/test/browser/ui/list/rangeMap.test.ts index 3b451445a63..518b250a943 100644 --- a/src/vs/base/test/browser/ui/list/rangeMap.test.ts +++ b/src/vs/base/test/browser/ui/list/rangeMap.test.ts @@ -343,3 +343,83 @@ suite('RangeMap', () => { }); }); }); + +suite('RangeMap with top padding', () => { + let rangeMap: RangeMap; + + setup(() => { + rangeMap = new RangeMap(10); + }); + + test('empty', () => { + assert.strictEqual(rangeMap.size, 10); + assert.strictEqual(rangeMap.count, 0); + }); + + const one = { size: 1 }; + const five = { size: 5 }; + const ten = { size: 10 }; + + test('length & count', () => { + rangeMap.splice(0, 0, [one]); + assert.strictEqual(rangeMap.size, 11); + assert.strictEqual(rangeMap.count, 1); + }); + + test('length & count #2', () => { + rangeMap.splice(0, 0, [one, one, one, one, one]); + assert.strictEqual(rangeMap.size, 15); + assert.strictEqual(rangeMap.count, 5); + }); + + test('length & count #3', () => { + rangeMap.splice(0, 0, [five]); + assert.strictEqual(rangeMap.size, 15); + assert.strictEqual(rangeMap.count, 1); + }); + + test('length & count #4', () => { + rangeMap.splice(0, 0, [five, five, five, five, five]); + assert.strictEqual(rangeMap.size, 35); + assert.strictEqual(rangeMap.count, 5); + }); + + test('insert', () => { + rangeMap.splice(0, 0, [five, five, five, five, five]); + assert.strictEqual(rangeMap.size, 35); + assert.strictEqual(rangeMap.count, 5); + + rangeMap.splice(0, 0, [five, five, five, five, five]); + assert.strictEqual(rangeMap.size, 60); + assert.strictEqual(rangeMap.count, 10); + + rangeMap.splice(5, 0, [ten, ten]); + assert.strictEqual(rangeMap.size, 80); + assert.strictEqual(rangeMap.count, 12); + + rangeMap.splice(12, 0, [{ size: 200 }]); + assert.strictEqual(rangeMap.size, 280); + assert.strictEqual(rangeMap.count, 13); + }); + + suite('indexAt, positionAt', () => { + test('empty', () => { + assert.strictEqual(rangeMap.indexAt(0), 0); + assert.strictEqual(rangeMap.indexAt(10), 0); + assert.strictEqual(rangeMap.indexAt(-1), -1); + assert.strictEqual(rangeMap.positionAt(0), -1); + assert.strictEqual(rangeMap.positionAt(10), -1); + assert.strictEqual(rangeMap.positionAt(-1), -1); + }); + + test('simple', () => { + rangeMap.splice(0, 0, [one]); + assert.strictEqual(rangeMap.indexAt(0), 0); + assert.strictEqual(rangeMap.indexAt(1), 0); + assert.strictEqual(rangeMap.indexAt(10), 0); + assert.strictEqual(rangeMap.indexAt(11), 1); + assert.strictEqual(rangeMap.positionAt(0), 10); + assert.strictEqual(rangeMap.positionAt(1), -1); + }); + }); +}); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index f69d5876a14..4ff0b3fe0d6 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -749,15 +749,6 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD }`); } - // top insert toolbar - const topInsertToolbarHeight = this._notebookOptions.computeTopInsertToolbarHeight(this.viewModel?.viewType); - styleSheets.push(`.notebookOverlay .cell-list-top-cell-toolbar-container { top: -${topInsertToolbarHeight - 3}px }`); - styleSheets.push(`.notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element, - .notebookOverlay > .cell-list-container > .notebook-gutter > .monaco-list > .monaco-scrollable-element { - padding-top: ${topInsertToolbarHeight}px !important; - box-sizing: border-box; - }`); - styleSheets.push(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .code-cell-row div.cell.code { margin-left: ${codeCellLeftMargin + cellRunGutter}px; }`); styleSheets.push(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row div.cell { margin-right: ${cellRightMargin}px; }`); styleSheets.push(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row > .cell-inner-container { padding-top: ${cellTopMargin}px; }`); @@ -869,6 +860,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD additionalScrollHeight: 0, transformOptimization: false, //(isMacintosh && isNative) || getTitleBarStyle(this.configurationService, this.environmentService) === 'native', initialSize: this._dimension, + topPadding: this._notebookOptions.computeTopInsertToolbarHeight(this.viewModel?.viewType), styleController: (_suffix: string) => { return this._list; }, overrideStyles: { listBackground: notebookEditorBackground, @@ -1462,8 +1454,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD })); if (this._dimension) { - const topInserToolbarHeight = this._notebookOptions.computeTopInsertToolbarHeight(this.viewModel?.viewType); - this._list.layout(this._dimension.height - topInserToolbarHeight, this._dimension.width); + this._list.layout(this._dimension.height, this._dimension.width); } else { this._list.layout(); } @@ -1757,15 +1748,15 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD DOM.size(this._body, dimension.width, newBodyHeight); const topInserToolbarHeight = this._notebookOptions.computeTopInsertToolbarHeight(this.viewModel?.viewType); - const newCellListHeight = Math.max(newBodyHeight - topInserToolbarHeight, 0); + const newCellListHeight = newBodyHeight; if (this._list.getRenderHeight() < newCellListHeight) { // the new dimension is larger than the list viewport, update its additional height first, otherwise the list view will move down a bit (as the `scrollBottom` will move down) - this._list.updateOptions({ additionalScrollHeight: this._allowScrollBeyondLastLine() ? Math.max(0, (newCellListHeight - 50)) : topInserToolbarHeight }); + this._list.updateOptions({ additionalScrollHeight: this._allowScrollBeyondLastLine() ? Math.max(0, (newCellListHeight - 50)) : 0, topPadding: topInserToolbarHeight }); this._list.layout(newCellListHeight, dimension.width); } else { // the new dimension is smaller than the list viewport, if we update the additional height, the `scrollBottom` will move up, which moves the whole list view upwards a bit. So we run a layout first. this._list.layout(newCellListHeight, dimension.width); - this._list.updateOptions({ additionalScrollHeight: this._allowScrollBeyondLastLine() ? Math.max(0, (newCellListHeight - 50)) : topInserToolbarHeight }); + this._list.updateOptions({ additionalScrollHeight: this._allowScrollBeyondLastLine() ? Math.max(0, (newCellListHeight - 50)) : 0, topPadding: topInserToolbarHeight }); } this._overlayContainer.style.visibility = 'visible'; From 8d1a21408fd25c8977fb417b68b3197b879262da Mon Sep 17 00:00:00 2001 From: Daniel McCormack <52194107+demccormack@users.noreply.github.com> Date: Sat, 17 Jun 2023 17:46:01 +1200 Subject: [PATCH 007/926] Enable zsh and bash shell integration with set -u --- .../terminal/browser/media/shellIntegration-bash.sh | 10 +++++----- .../terminal/browser/media/shellIntegration-rc.zsh | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-bash.sh b/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-bash.sh index 8c4f4fbbef0..292b77cdfdc 100755 --- a/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-bash.sh +++ b/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-bash.sh @@ -46,7 +46,7 @@ if [ -z "$VSCODE_SHELL_INTEGRATION" ]; then fi # Apply EnvironmentVariableCollections if needed -if [ -n "$VSCODE_ENV_REPLACE" ]; then +if [ -n "${VSCODE_ENV_REPLACE:-}" ]; then IFS=':' read -ra ADDR <<< "$VSCODE_ENV_REPLACE" for ITEM in "${ADDR[@]}"; do VARNAME="$(echo $ITEM | cut -d "=" -f 1)" @@ -55,7 +55,7 @@ if [ -n "$VSCODE_ENV_REPLACE" ]; then done builtin unset VSCODE_ENV_REPLACE fi -if [ -n "$VSCODE_ENV_PREPEND" ]; then +if [ -n "${VSCODE_ENV_PREPEND:-}" ]; then IFS=':' read -ra ADDR <<< "$VSCODE_ENV_PREPEND" for ITEM in "${ADDR[@]}"; do VARNAME="$(echo $ITEM | cut -d "=" -f 1)" @@ -64,7 +64,7 @@ if [ -n "$VSCODE_ENV_PREPEND" ]; then done builtin unset VSCODE_ENV_PREPEND fi -if [ -n "$VSCODE_ENV_APPEND" ]; then +if [ -n "${VSCODE_ENV_APPEND:-}" ]; then IFS=':' read -ra ADDR <<< "$VSCODE_ENV_APPEND" for ITEM in "${ADDR[@]}"; do VARNAME="$(echo $ITEM | cut -d "=" -f 1)" @@ -275,10 +275,10 @@ __vsc_prompt_cmd() { # PROMPT_COMMAND arrays and strings seem to be handled the same (handling only the first entry of # the array?) -__vsc_original_prompt_command=$PROMPT_COMMAND +__vsc_original_prompt_command=${PROMPT_COMMAND:-} if [[ -z "${bash_preexec_imported:-}" ]]; then - if [[ -n "$__vsc_original_prompt_command" && "$__vsc_original_prompt_command" != "__vsc_prompt_cmd" ]]; then + if [[ -n "${__vsc_original_prompt_command:-}" && "${__vsc_original_prompt_command:-}" != "__vsc_prompt_cmd" ]]; then PROMPT_COMMAND=__vsc_prompt_cmd_original else PROMPT_COMMAND=__vsc_prompt_cmd diff --git a/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-rc.zsh b/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-rc.zsh index d4e21dacda7..09718cfbab2 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-rc.zsh +++ b/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-rc.zsh @@ -39,7 +39,7 @@ if [ -z "$VSCODE_SHELL_INTEGRATION" ]; then fi # Apply EnvironmentVariableCollections if needed -if [ -n "$VSCODE_ENV_REPLACE" ]; then +if [ -n "${VSCODE_ENV_REPLACE:-}" ]; then IFS=':' read -rA ADDR <<< "$VSCODE_ENV_REPLACE" for ITEM in "${ADDR[@]}"; do VARNAME="$(echo ${ITEM%%=*})" @@ -47,7 +47,7 @@ if [ -n "$VSCODE_ENV_REPLACE" ]; then done unset VSCODE_ENV_REPLACE fi -if [ -n "$VSCODE_ENV_PREPEND" ]; then +if [ -n "${VSCODE_ENV_PREPEND:-}" ]; then IFS=':' read -rA ADDR <<< "$VSCODE_ENV_PREPEND" for ITEM in "${ADDR[@]}"; do VARNAME="$(echo ${ITEM%%=*})" @@ -55,7 +55,7 @@ if [ -n "$VSCODE_ENV_PREPEND" ]; then done unset VSCODE_ENV_PREPEND fi -if [ -n "$VSCODE_ENV_APPEND" ]; then +if [ -n "${VSCODE_ENV_APPEND:-}" ]; then IFS=':' read -rA ADDR <<< "$VSCODE_ENV_APPEND" for ITEM in "${ADDR[@]}"; do VARNAME="$(echo ${ITEM%%=*})" From f0c543908ad0435605ee5845bb6339cb9d8d7790 Mon Sep 17 00:00:00 2001 From: weartist Date: Sat, 17 Jun 2023 16:31:21 +0800 Subject: [PATCH 008/926] Fix #185343 --- .../contrib/terminal/common/terminalConfiguration.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts index f247f219344..62275b88a3a 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts @@ -503,16 +503,13 @@ const terminalConfiguration: IConfigurationNode = { [TerminalSettingId.LocalEchoStyle]: { description: localize('terminal.integrated.localEchoStyle', "Terminal style of locally echoed text; either a font style or an RGB color."), default: 'dim', - oneOf: [ + anyOf: [ { - type: 'string', - default: 'dim', - enum: ['bold', 'dim', 'italic', 'underlined', 'inverted'], + enum: ['bold', 'dim', 'italic', 'underlined', 'inverted', '#ff0000'], }, { type: 'string', format: 'color-hex', - default: '#ff0000', } ] }, From 55e99a5b77cc7449e32832e1ea2a00da3762961c Mon Sep 17 00:00:00 2001 From: Raymond Zhao <7199958+rzhao271@users.noreply.github.com> Date: Fri, 23 Jun 2023 14:57:24 -0700 Subject: [PATCH 009/926] Directly compare first element --- src/vs/platform/configuration/common/configurationModels.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/configuration/common/configurationModels.ts b/src/vs/platform/configuration/common/configurationModels.ts index f05974df9ef..2a092978e01 100644 --- a/src/vs/platform/configuration/common/configurationModels.ts +++ b/src/vs/platform/configuration/common/configurationModels.ts @@ -211,7 +211,7 @@ export class ConfigurationModel implements IConfigurationModel { } }; for (const override of this.overrides) { - if (arrays.equals(override.identifiers, [identifier])) { + if (override.identifiers[0] === identifier) { contentsForIdentifierOnly = override.contents; } else if (override.identifiers.includes(identifier)) { mergeContents(override.contents); From f444d09fd3fe4354cc4d0af6b02a2c0af4c2221d Mon Sep 17 00:00:00 2001 From: Raymond Zhao <7199958+rzhao271@users.noreply.github.com> Date: Fri, 23 Jun 2023 15:02:28 -0700 Subject: [PATCH 010/926] Add len check in case there is more than one id --- src/vs/platform/configuration/common/configurationModels.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/configuration/common/configurationModels.ts b/src/vs/platform/configuration/common/configurationModels.ts index 2a092978e01..9e171f9b6cb 100644 --- a/src/vs/platform/configuration/common/configurationModels.ts +++ b/src/vs/platform/configuration/common/configurationModels.ts @@ -211,7 +211,7 @@ export class ConfigurationModel implements IConfigurationModel { } }; for (const override of this.overrides) { - if (override.identifiers[0] === identifier) { + if (override.identifiers.length === 1 && override.identifiers[0] === identifier) { contentsForIdentifierOnly = override.contents; } else if (override.identifiers.includes(identifier)) { mergeContents(override.contents); From 96fba00ae8ecda6f4602127317d54db6f965e6a7 Mon Sep 17 00:00:00 2001 From: Ashriel Date: Mon, 19 Jun 2023 21:05:26 +0530 Subject: [PATCH 011/926] ISO dates [yyyy-mm-dd] ISO Date reconstruction for Locale Migrated from toISOStrings to prevent UTC Dates returned Updated Date format Added yyyy-mm-dd format in Extensions Info --- .../contrib/extensions/browser/extensionEditor.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index e75ba9e986f..5408889b97b 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -945,22 +945,25 @@ export class ExtensionEditor extends EditorPane { append(moreInfoContainer, $('.additional-details-title', undefined, localize('Marketplace Info', "More Info"))); const moreInfo = append(moreInfoContainer, $('.more-info')); if (gallery) { + const pub = new Date(gallery.releaseDate); + const last_rel = new Date(gallery.lastUpdated); append(moreInfo, $('.more-info-entry', undefined, $('div', undefined, localize('published', "Published")), - $('div', undefined, new Date(gallery.releaseDate).toLocaleString(language, { hourCycle: 'h23' })) + $('div', undefined, [pub.getFullYear(), pub.getMonth() + 1, pub.getDate()].join('-').concat(', ' + pub.toLocaleTimeString(language, { hourCycle: 'h23' }))) ), $('.more-info-entry', undefined, $('div', undefined, localize('last released', "Last released")), - $('div', undefined, new Date(gallery.lastUpdated).toLocaleString(language, { hourCycle: 'h23' })) + $('div', undefined, [last_rel.getFullYear(), last_rel.getMonth() + 1, last_rel.getDate()].join('-').concat(', ' + last_rel.toLocaleTimeString(language, { hourCycle: 'h23' }))) ) ); } if (extension.local && extension.local.installedTimestamp) { + const last_up = new Date(extension.local.installedTimestamp); append(moreInfo, $('.more-info-entry', undefined, $('div', undefined, localize('last updated', "Last updated")), - $('div', undefined, new Date(extension.local.installedTimestamp).toLocaleString(language, { hourCycle: 'h23' })) + $('div', undefined, [last_up.getFullYear(), last_up.getMonth() + 1, last_up.getDate()].join('-').concat(', ' + last_up.toLocaleTimeString(language, { hourCycle: 'h23' }))) ) ); } From c6bcad53df7bef121bf804aa7add13f861469caf Mon Sep 17 00:00:00 2001 From: Sandeep Sen Date: Mon, 26 Jun 2023 21:02:27 +0000 Subject: [PATCH 012/926] Initial code for go telemetry --- .../electron-sandbox/workspaceTagsService.ts | 71 ++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/tags/electron-sandbox/workspaceTagsService.ts b/src/vs/workbench/contrib/tags/electron-sandbox/workspaceTagsService.ts index 39a65eac896..b832b1d639d 100644 --- a/src/vs/workbench/contrib/tags/electron-sandbox/workspaceTagsService.ts +++ b/src/vs/workbench/contrib/tags/electron-sandbox/workspaceTagsService.ts @@ -244,6 +244,32 @@ const PyModulesToLookFor = [ 'playwright' ]; +const GoMetaModulesToLookFor = [ + 'github.com/Azure/azure-sdk-for-go/sdk/' +]; + +const GoModulesToLookFor = [ + 'github.com/Azure/azure-sdk-for-go/sdk/storage/azblob', + 'github.com/Azure/azure-sdk-for-go/sdk/storage/azfile', + 'github.com/Azure/azure-sdk-for-go/sdk/storage/azqueue', + 'github.com/Azure/azure-sdk-for-go/sdk/tracing/azotel', + 'github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azadmin', + 'github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azcertificates', + 'github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys', + 'github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets', + 'github.com/Azure/azure-sdk-for-go/sdk/monitor/azquery', + 'github.com/Azure/azure-sdk-for-go/sdk/messaging/azeventhubs', + 'github.com/Azure/azure-sdk-for-go/sdk/messaging/azservicebus', + 'github.com/Azure/azure-sdk-for-go/sdk/data/azappconfig', + 'github.com/Azure/azure-sdk-for-go/sdk/data/azcosmos', + 'github.com/Azure/azure-sdk-for-go/sdk/data/aztables', + 'github.com/Azure/azure-sdk-for-go/sdk/containers/azcontainerregistry', + 'github.com/Azure/azure-sdk-for-go/sdk/cognitiveservices/azopenai', + 'github.com/Azure/azure-sdk-for-go/sdk/azidentity', + 'github.com/Azure/azure-sdk-for-go/sdk/azcore' +]; + + export class WorkspaceTagsService implements IWorkspaceTagsService { declare readonly _serviceBrand: undefined; private _tags: Tags | undefined; @@ -566,6 +592,24 @@ export class WorkspaceTagsService implements IWorkspaceTagsService { "workspace.py.azure-security-attestation" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.py.azure-data-nspkg" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.py.azure-data-tables" : { "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 } + "workspace.go.mod.github.com/Azure/azure-sdk-for-go/sdk/tracing/azotel" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } + "workspace.go.mod.github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azadmin" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } + "workspace.go.mod.github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azcertificates" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } + "workspace.go.mod.github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } + "workspace.go.mod.github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } + "workspace.go.mod.github.com/Azure/azure-sdk-for-go/sdk/monitor/azquery" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } + "workspace.go.mod.github.com/Azure/azure-sdk-for-go/sdk/messaging/azeventhubs" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } + "workspace.go.mod.github.com/Azure/azure-sdk-for-go/sdk/messaging/azservicebus" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } + "workspace.go.mod.github.com/Azure/azure-sdk-for-go/sdk/data/azappconfig" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } + "workspace.go.mod.github.com/Azure/azure-sdk-for-go/sdk/data/azcosmos" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } + "workspace.go.mod.github.com/Azure/azure-sdk-for-go/sdk/data/aztables" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } + "workspace.go.mod.github.com/Azure/azure-sdk-for-go/sdk/containers/azcontainerregistry" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } + "workspace.go.mod.github.com/Azure/azure-sdk-for-go/sdk/cognitiveservices/azopenai" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } + "workspace.go.mod.github.com/Azure/azure-sdk-for-go/sdk/azidentity" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } + "workspace.go.mod.github.com/Azure/azure-sdk-for-go/sdk/azcore" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } } */ private async resolveWorkspaceTags(): Promise { @@ -624,6 +668,8 @@ export class WorkspaceTagsService implements IWorkspaceTagsService { tags['workspace.py.app'] = nameSet.has('app.py'); tags['workspace.py.pyproject'] = nameSet.has('pyproject.toml'); + tags['workspace.go.mod'] = nameSet.has('go.mod'); + const mainActivity = nameSet.has('mainactivity.cs') || nameSet.has('mainactivity.fs'); const appDelegate = nameSet.has('appdelegate.cs') || nameSet.has('appdelegate.fs'); const androidManifest = nameSet.has('androidmanifest.xml'); @@ -752,6 +798,29 @@ export class WorkspaceTagsService implements IWorkspaceTagsService { } }); + const goModPromises = getFilePromises('go.mod', this.fileService, this.textFileService, content => { + // TODO: Richard to write the code for parsing the go.mod file + // look for everything in require() and get the string value only discard version + const dependencies: string[] = splitLines(content.value); + for (const dependency of dependencies) { + // Dependencies in requirements.txt can have 3 formats: `foo==3.1, foo>=3.1, foo` + const format1 = dependency.split('=='); + const format2 = dependency.split('>='); + const packageName = (format1.length === 2 ? format1[0] : format2[0]).trim(); + + // copied from line 728 function addPythonTags + if (GoModulesToLookFor.indexOf(packageName) > -1) { + tags['workspace.go.mod' + packageName] = true; + } + // not sure if we should keep this + for (const metaModule of GoMetaModulesToLookFor) { + if (packageName.startsWith(metaModule)) { + tags['workspace.go.mod' + metaModule] = true; + } + } + } + }); + const pomPromises = getFilePromises('pom.xml', this.fileService, this.textFileService, content => { try { let dependenciesContent; @@ -792,7 +861,7 @@ export class WorkspaceTagsService implements IWorkspaceTagsService { }); }); - return Promise.all([...packageJsonPromises, ...requirementsTxtPromises, ...pipfilePromises, ...pomPromises, ...gradlePromises, ...androidPromises]).then(() => tags); + return Promise.all([...packageJsonPromises, ...requirementsTxtPromises, ...pipfilePromises, ...pomPromises, ...gradlePromises, ...androidPromises, ...goModPromises]).then(() => tags); }); } From b62087b19d5fd3525dab5481500cde91c795a4cb Mon Sep 17 00:00:00 2001 From: Sandeep Sen Date: Mon, 26 Jun 2023 21:58:54 +0000 Subject: [PATCH 013/926] Adding go mod parser --- .../electron-sandbox/workspaceTagsService.ts | 41 +++++++++++++------ 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/contrib/tags/electron-sandbox/workspaceTagsService.ts b/src/vs/workbench/contrib/tags/electron-sandbox/workspaceTagsService.ts index b832b1d639d..4d5ff69fa1d 100644 --- a/src/vs/workbench/contrib/tags/electron-sandbox/workspaceTagsService.ts +++ b/src/vs/workbench/contrib/tags/electron-sandbox/workspaceTagsService.ts @@ -801,21 +801,36 @@ export class WorkspaceTagsService implements IWorkspaceTagsService { const goModPromises = getFilePromises('go.mod', this.fileService, this.textFileService, content => { // TODO: Richard to write the code for parsing the go.mod file // look for everything in require() and get the string value only discard version - const dependencies: string[] = splitLines(content.value); - for (const dependency of dependencies) { - // Dependencies in requirements.txt can have 3 formats: `foo==3.1, foo>=3.1, foo` - const format1 = dependency.split('=='); - const format2 = dependency.split('>='); - const packageName = (format1.length === 2 ? format1[0] : format2[0]).trim(); + const lines: string[] = splitLines(content.value); + let firstRequireBlockFound: boolean = false; - // copied from line 728 function addPythonTags - if (GoModulesToLookFor.indexOf(packageName) > -1) { - tags['workspace.go.mod' + packageName] = true; + for (let i = 0; i < lines.length; i++) { + const line: string = lines[i].trim(); + + if (line.startsWith('require (')) { + if (!firstRequireBlockFound) { + firstRequireBlockFound = true; + continue; + } else { + break; } - // not sure if we should keep this - for (const metaModule of GoMetaModulesToLookFor) { - if (packageName.startsWith(metaModule)) { - tags['workspace.go.mod' + metaModule] = true; + } + + if (line.startsWith(')')) { + break; + } + + if (firstRequireBlockFound && line !== '') { + const packageName: string = line.split(' ')[0].trim(); + // copied from line 728 function addPythonTags + if (GoModulesToLookFor.indexOf(packageName) > -1) { + tags['workspace.go.mod' + packageName] = true; + } + // not sure if we should keep this + for (const metaModule of GoMetaModulesToLookFor) { + if (packageName.startsWith(metaModule)) { + tags['workspace.go.mod' + metaModule] = true; + } } } } From af15aac09c3ed2df82bdc774fa539aa9e4751417 Mon Sep 17 00:00:00 2001 From: Sandeep Sen Date: Tue, 27 Jun 2023 20:54:56 +0000 Subject: [PATCH 014/926] Removing meta modules as Go does not need it --- .../electron-sandbox/workspaceTagsService.ts | 56 ++++++++----------- 1 file changed, 22 insertions(+), 34 deletions(-) diff --git a/src/vs/workbench/contrib/tags/electron-sandbox/workspaceTagsService.ts b/src/vs/workbench/contrib/tags/electron-sandbox/workspaceTagsService.ts index 4d5ff69fa1d..10be76329c4 100644 --- a/src/vs/workbench/contrib/tags/electron-sandbox/workspaceTagsService.ts +++ b/src/vs/workbench/contrib/tags/electron-sandbox/workspaceTagsService.ts @@ -244,10 +244,6 @@ const PyModulesToLookFor = [ 'playwright' ]; -const GoMetaModulesToLookFor = [ - 'github.com/Azure/azure-sdk-for-go/sdk/' -]; - const GoModulesToLookFor = [ 'github.com/Azure/azure-sdk-for-go/sdk/storage/azblob', 'github.com/Azure/azure-sdk-for-go/sdk/storage/azfile', @@ -799,41 +795,33 @@ export class WorkspaceTagsService implements IWorkspaceTagsService { }); const goModPromises = getFilePromises('go.mod', this.fileService, this.textFileService, content => { - // TODO: Richard to write the code for parsing the go.mod file - // look for everything in require() and get the string value only discard version - const lines: string[] = splitLines(content.value); - let firstRequireBlockFound: boolean = false; - - for (let i = 0; i < lines.length; i++) { - const line: string = lines[i].trim(); - - if (line.startsWith('require (')) { - if (!firstRequireBlockFound) { - firstRequireBlockFound = true; - continue; - } else { - break; - } - } - - if (line.startsWith(')')) { - break; - } - - if (firstRequireBlockFound && line !== '') { - const packageName: string = line.split(' ')[0].trim(); - // copied from line 728 function addPythonTags - if (GoModulesToLookFor.indexOf(packageName) > -1) { - tags['workspace.go.mod' + packageName] = true; + try { + const lines: string[] = splitLines(content.value); + let firstRequireBlockFound: boolean = false; + for (let i = 0; i < lines.length; i++) { + const line: string = lines[i].trim(); + if (line.startsWith('require (')) { + if (!firstRequireBlockFound) { + firstRequireBlockFound = true; + continue; + } else { + break; + } } - // not sure if we should keep this - for (const metaModule of GoMetaModulesToLookFor) { - if (packageName.startsWith(metaModule)) { - tags['workspace.go.mod' + metaModule] = true; + if (line.startsWith(')')) { + break; + } + if (firstRequireBlockFound && line !== '') { + const packageName: string = line.split(' ')[0].trim(); + if (GoModulesToLookFor.indexOf(packageName) > -1) { + tags['workspace.go.mod' + packageName] = true; } } } } + catch (e) { + // Ignore errors when resolving file or parsing file contents + } }); const pomPromises = getFilePromises('pom.xml', this.fileService, this.textFileService, content => { From a98595156a09ccdad70b12844653e3c0632be59c Mon Sep 17 00:00:00 2001 From: Sandeep Sen Date: Tue, 27 Jun 2023 21:57:31 -0700 Subject: [PATCH 015/926] adding missing commas to GDPR segment --- .../electron-sandbox/workspaceTagsService.ts | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/vs/workbench/contrib/tags/electron-sandbox/workspaceTagsService.ts b/src/vs/workbench/contrib/tags/electron-sandbox/workspaceTagsService.ts index 10be76329c4..04d93982b2a 100644 --- a/src/vs/workbench/contrib/tags/electron-sandbox/workspaceTagsService.ts +++ b/src/vs/workbench/contrib/tags/electron-sandbox/workspaceTagsService.ts @@ -587,24 +587,24 @@ export class WorkspaceTagsService implements IWorkspaceTagsService { "workspace.py.azure-communication-administration" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.py.azure-security-attestation" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.py.azure-data-nspkg" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workspace.py.azure-data-tables" : { "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 } - "workspace.go.mod.github.com/Azure/azure-sdk-for-go/sdk/tracing/azotel" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } - "workspace.go.mod.github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azadmin" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } - "workspace.go.mod.github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azcertificates" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } - "workspace.go.mod.github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } - "workspace.go.mod.github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } - "workspace.go.mod.github.com/Azure/azure-sdk-for-go/sdk/monitor/azquery" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } - "workspace.go.mod.github.com/Azure/azure-sdk-for-go/sdk/messaging/azeventhubs" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } - "workspace.go.mod.github.com/Azure/azure-sdk-for-go/sdk/messaging/azservicebus" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } - "workspace.go.mod.github.com/Azure/azure-sdk-for-go/sdk/data/azappconfig" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } - "workspace.go.mod.github.com/Azure/azure-sdk-for-go/sdk/data/azcosmos" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } - "workspace.go.mod.github.com/Azure/azure-sdk-for-go/sdk/data/aztables" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } - "workspace.go.mod.github.com/Azure/azure-sdk-for-go/sdk/containers/azcontainerregistry" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } - "workspace.go.mod.github.com/Azure/azure-sdk-for-go/sdk/cognitiveservices/azopenai" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } - "workspace.go.mod.github.com/Azure/azure-sdk-for-go/sdk/azidentity" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } + "workspace.py.azure-data-tables" : { "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 }, + "workspace.go.mod.github.com/Azure/azure-sdk-for-go/sdk/tracing/azotel" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.go.mod.github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azadmin" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.go.mod.github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azcertificates" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.go.mod.github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.go.mod.github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.go.mod.github.com/Azure/azure-sdk-for-go/sdk/monitor/azquery" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.go.mod.github.com/Azure/azure-sdk-for-go/sdk/messaging/azeventhubs" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.go.mod.github.com/Azure/azure-sdk-for-go/sdk/messaging/azservicebus" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.go.mod.github.com/Azure/azure-sdk-for-go/sdk/data/azappconfig" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.go.mod.github.com/Azure/azure-sdk-for-go/sdk/data/azcosmos" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.go.mod.github.com/Azure/azure-sdk-for-go/sdk/data/aztables" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.go.mod.github.com/Azure/azure-sdk-for-go/sdk/containers/azcontainerregistry" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.go.mod.github.com/Azure/azure-sdk-for-go/sdk/cognitiveservices/azopenai" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.go.mod.github.com/Azure/azure-sdk-for-go/sdk/azidentity" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.go.mod.github.com/Azure/azure-sdk-for-go/sdk/azcore" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } } */ From c092aebae03a9f6d28854d3f9bb319d26a53411b Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Wed, 28 Jun 2023 12:49:16 +0600 Subject: [PATCH 016/926] fixing the error --- .../contrib/hover/browser/contentHover.ts | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/vs/editor/contrib/hover/browser/contentHover.ts b/src/vs/editor/contrib/hover/browser/contentHover.ts index 825e60a7fd7..63a04fbb174 100644 --- a/src/vs/editor/contrib/hover/browser/contentHover.ts +++ b/src/vs/editor/contrib/hover/browser/contentHover.ts @@ -721,6 +721,19 @@ export class ContentHoverWidget extends ResizableContentWidget { // See https://github.com/microsoft/vscode/issues/140339 // TODO: Doing a second layout of the hover after force rendering the editor this.onContentsChanged(); + + // If the normal rendering dimensions surpass the resizable node max dimensions + // then update the dimensions of the hover + this._setResizableNodeMaxDimensions(); + const containerDomNode = this._hover.containerDomNode; + const currentHeight = containerDomNode.clientHeight; + const currentWidth = containerDomNode.clientWidth; + const maxHeight = Math.min(this._resizableNode.maxSize.height, currentHeight); + const maxWidth = Math.min(this._resizableNode.maxSize.width, currentWidth); + if (currentHeight >= maxHeight || currentWidth >= maxWidth) { + this._layoutWidget(maxHeight, maxWidth); + } + if (hoverData.stoleFocus) { this._hover.containerDomNode.focus(); } @@ -757,12 +770,8 @@ export class ContentHoverWidget extends ResizableContentWidget { this._setContentsDomNodeDimensions(dom.getTotalWidth(contentsDomNode), Math.min(maxRenderingHeight, height - SCROLLBAR_WIDTH)); } - public onContentsChanged(): void { - this._removeConstraintsRenderNormally(); + private _layoutWidget(height: number, width: number) { const containerDomNode = this._hover.containerDomNode; - - let height = dom.getTotalHeight(containerDomNode); - let width = dom.getTotalWidth(containerDomNode); this._resizableNode.layout(height, width); this._setHoverWidgetDimensions(width, height); @@ -778,6 +787,12 @@ export class ContentHoverWidget extends ResizableContentWidget { this._layoutContentWidget(); } + public onContentsChanged(): void { + this._removeConstraintsRenderNormally(); + const containerDomNode = this._hover.containerDomNode; + this._layoutWidget(dom.getTotalHeight(containerDomNode), dom.getTotalWidth(containerDomNode)); + } + public focus(): void { this._hover.containerDomNode.focus(); } From 118be5cf1eb503e4cb34ac6a79ecbe9613b4a5fe Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Wed, 28 Jun 2023 17:15:28 +0600 Subject: [PATCH 017/926] just changing the maximum rendering width to make it larger --- .../contrib/hover/browser/contentHover.ts | 34 ++++++------------- 1 file changed, 10 insertions(+), 24 deletions(-) diff --git a/src/vs/editor/contrib/hover/browser/contentHover.ts b/src/vs/editor/contrib/hover/browser/contentHover.ts index 63a04fbb174..c0a6bdf7541 100644 --- a/src/vs/editor/contrib/hover/browser/contentHover.ts +++ b/src/vs/editor/contrib/hover/browser/contentHover.ts @@ -618,10 +618,11 @@ export class ContentHoverWidget extends ResizableContentWidget { if (!this._editor || !this._editor.hasModel()) { return; } - const editorBox = dom.getDomNodePagePosition(this._editor.getDomNode()); - const glyphMarginWidth = this._editor.getLayoutInfo().glyphMarginWidth; - const leftOfContainer = this._hover.containerDomNode.offsetLeft; - return editorBox.width + editorBox.left - leftOfContainer - glyphMarginWidth; + const editorLayoutInfo = this._editor.getLayoutInfo(); + const glyphMarginRight = editorLayoutInfo.glyphMarginLeft + editorLayoutInfo.glyphMarginWidth; + const cursorPosition = this._editor._getViewModel().getPrimaryCursorState().viewState.position; + const bodyBoxWidth = dom.getClientArea(document.body).width; + return bodyBoxWidth - glyphMarginRight - this._editor.getOffsetForColumn(cursorPosition.lineNumber, cursorPosition.column); } public isMouseGettingCloser(posx: number, posy: number): boolean { @@ -721,19 +722,6 @@ export class ContentHoverWidget extends ResizableContentWidget { // See https://github.com/microsoft/vscode/issues/140339 // TODO: Doing a second layout of the hover after force rendering the editor this.onContentsChanged(); - - // If the normal rendering dimensions surpass the resizable node max dimensions - // then update the dimensions of the hover - this._setResizableNodeMaxDimensions(); - const containerDomNode = this._hover.containerDomNode; - const currentHeight = containerDomNode.clientHeight; - const currentWidth = containerDomNode.clientWidth; - const maxHeight = Math.min(this._resizableNode.maxSize.height, currentHeight); - const maxWidth = Math.min(this._resizableNode.maxSize.width, currentWidth); - if (currentHeight >= maxHeight || currentWidth >= maxWidth) { - this._layoutWidget(maxHeight, maxWidth); - } - if (hoverData.stoleFocus) { this._hover.containerDomNode.focus(); } @@ -770,8 +758,12 @@ export class ContentHoverWidget extends ResizableContentWidget { this._setContentsDomNodeDimensions(dom.getTotalWidth(contentsDomNode), Math.min(maxRenderingHeight, height - SCROLLBAR_WIDTH)); } - private _layoutWidget(height: number, width: number) { + public onContentsChanged(): void { + this._removeConstraintsRenderNormally(); const containerDomNode = this._hover.containerDomNode; + + let height = dom.getTotalHeight(containerDomNode); + let width = dom.getTotalWidth(containerDomNode); this._resizableNode.layout(height, width); this._setHoverWidgetDimensions(width, height); @@ -787,12 +779,6 @@ export class ContentHoverWidget extends ResizableContentWidget { this._layoutContentWidget(); } - public onContentsChanged(): void { - this._removeConstraintsRenderNormally(); - const containerDomNode = this._hover.containerDomNode; - this._layoutWidget(dom.getTotalHeight(containerDomNode), dom.getTotalWidth(containerDomNode)); - } - public focus(): void { this._hover.containerDomNode.focus(); } From 4a349954cf49ad38c253e97622fab9b451a6a2cc Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 28 Jun 2023 05:08:58 -0700 Subject: [PATCH 018/926] Mention link detection in terminal wordSeparators setting Fixes #186096 --- .../workbench/contrib/terminal/common/terminalConfiguration.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts index f247f219344..6d05f5781d6 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts @@ -448,7 +448,7 @@ const terminalConfiguration: IConfigurationNode = { default: true }, [TerminalSettingId.WordSeparators]: { - description: localize('terminal.integrated.wordSeparators', "A string containing all characters to be considered word separators by the double-click to select word feature."), + markdownDescription: localize('terminal.integrated.wordSeparators', "A string containing all characters to be considered word separators when double-clicking to select word and in the fallback 'word' link detection. Since this is used for link detection, including characters such as `:` that are used when detecting links will cause the line and column part of links like `file:10:5` to be ignored."), type: 'string', // allow-any-unicode-next-line default: ' ()[]{}\',"`─‘’|' From e133f004ed3321422e80a6c441c3c0af57a6363a Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Wed, 28 Jun 2023 16:31:53 -0700 Subject: [PATCH 019/926] Don't require a reload for Share setting (#186572) --- .../share/browser/share.contribution.ts | 136 ++++++++++-------- 1 file changed, 75 insertions(+), 61 deletions(-) diff --git a/src/vs/workbench/contrib/share/browser/share.contribution.ts b/src/vs/workbench/contrib/share/browser/share.contribution.ts index a36548c8bd0..8230ad5beb4 100644 --- a/src/vs/workbench/contrib/share/browser/share.contribution.ts +++ b/src/vs/workbench/contrib/share/browser/share.contribution.ts @@ -32,6 +32,7 @@ import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/ import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuration'; +import { DisposableStore } from 'vs/base/common/lifecycle'; const targetMenus = [ MenuId.EditorContextShare, @@ -46,6 +47,8 @@ const targetMenus = [ class ShareWorkbenchContribution { private static SHARE_ENABLED_SETTING = 'workbench.experimental.share.enabled'; + private _disposables = new DisposableStore(); + constructor( @IShareService private readonly shareService: IShareService, @IConfigurationService private readonly configurationService: IConfigurationService @@ -53,75 +56,86 @@ class ShareWorkbenchContribution { if (this.configurationService.getValue(ShareWorkbenchContribution.SHARE_ENABLED_SETTING)) { this.registerActions(); } - } - - private registerActions() { - registerAction2(class ShareAction extends Action2 { - static readonly ID = 'workbench.action.share'; - static readonly LABEL = localize('share', 'Share...'); - - constructor() { - super({ - id: ShareAction.ID, - title: { value: ShareAction.LABEL, original: 'Share...' }, - f1: true, - icon: Codicon.linkExternal, - precondition: ContextKeyExpr.and(ShareProviderCountContext.notEqualsTo(0), WorkspaceFolderCountContext.notEqualsTo(0)), - keybinding: { - weight: KeybindingWeight.WorkbenchContrib, - primary: KeyMod.Alt | KeyMod.CtrlCmd | KeyCode.KeyS, - }, - menu: [ - { id: MenuId.CommandCenter, order: 1000 } - ] - }); - } - - override async run(accessor: ServicesAccessor, ...args: any[]): Promise { - const shareService = accessor.get(IShareService); - const activeEditor = accessor.get(IEditorService)?.activeEditor; - const resourceUri = (activeEditor && EditorResourceAccessor.getOriginalUri(activeEditor, { supportSideBySide: SideBySideEditor.PRIMARY })) - ?? accessor.get(IWorkspaceContextService).getWorkspace().folders[0].uri; - const clipboardService = accessor.get(IClipboardService); - const dialogService = accessor.get(IDialogService); - const urlService = accessor.get(IOpenerService); - const progressService = accessor.get(IProgressService); - const selection = accessor.get(ICodeEditorService).getActiveCodeEditor()?.getSelection() ?? undefined; - - const result = await progressService.withProgress({ - location: ProgressLocation.Window, - detail: localize('generating link', 'Generating link...') - }, async () => shareService.provideShare({ resourceUri, selection }, new CancellationTokenSource().token)); - - if (result) { - const uriText = result.toString(); - const isResultText = typeof result === 'string'; - await clipboardService.writeText(uriText); - - dialogService.prompt( - { - type: Severity.Info, - message: isResultText ? localize('shareTextSuccess', 'Copied text to clipboard!') : localize('shareSuccess', 'Copied link to clipboard!'), - custom: { - icon: Codicon.check, - markdownDetails: [{ - markdown: new MarkdownString(`
${uriText}
`, { supportHtml: true }), - classes: [isResultText ? 'share-dialog-input-text' : 'share-dialog-input-link'] - }] - }, - cancelButton: localize('close', 'Close'), - buttons: isResultText ? [] : [{ label: localize('open link', 'Open Link'), run: () => { urlService.open(result, { openExternal: true }); } }] - } - ); + this.configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration(ShareWorkbenchContribution.SHARE_ENABLED_SETTING)) { + if (this.configurationService.getValue(ShareWorkbenchContribution.SHARE_ENABLED_SETTING)) { + this.registerActions(); + } else { + this._disposables.clear(); } } }); + } + + private registerActions() { + this._disposables.add( + registerAction2(class ShareAction extends Action2 { + static readonly ID = 'workbench.action.share'; + static readonly LABEL = localize('share', 'Share...'); + + constructor() { + super({ + id: ShareAction.ID, + title: { value: ShareAction.LABEL, original: 'Share...' }, + f1: true, + icon: Codicon.linkExternal, + precondition: ContextKeyExpr.and(ShareProviderCountContext.notEqualsTo(0), WorkspaceFolderCountContext.notEqualsTo(0)), + keybinding: { + weight: KeybindingWeight.WorkbenchContrib, + primary: KeyMod.Alt | KeyMod.CtrlCmd | KeyCode.KeyS, + }, + menu: [ + { id: MenuId.CommandCenter, order: 1000 } + ] + }); + } + + override async run(accessor: ServicesAccessor, ...args: any[]): Promise { + const shareService = accessor.get(IShareService); + const activeEditor = accessor.get(IEditorService)?.activeEditor; + const resourceUri = (activeEditor && EditorResourceAccessor.getOriginalUri(activeEditor, { supportSideBySide: SideBySideEditor.PRIMARY })) + ?? accessor.get(IWorkspaceContextService).getWorkspace().folders[0].uri; + const clipboardService = accessor.get(IClipboardService); + const dialogService = accessor.get(IDialogService); + const urlService = accessor.get(IOpenerService); + const progressService = accessor.get(IProgressService); + const selection = accessor.get(ICodeEditorService).getActiveCodeEditor()?.getSelection() ?? undefined; + + const result = await progressService.withProgress({ + location: ProgressLocation.Window, + detail: localize('generating link', 'Generating link...') + }, async () => shareService.provideShare({ resourceUri, selection }, new CancellationTokenSource().token)); + + if (result) { + const uriText = result.toString(); + const isResultText = typeof result === 'string'; + await clipboardService.writeText(uriText); + + dialogService.prompt( + { + type: Severity.Info, + message: isResultText ? localize('shareTextSuccess', 'Copied text to clipboard!') : localize('shareSuccess', 'Copied link to clipboard!'), + custom: { + icon: Codicon.check, + markdownDetails: [{ + markdown: new MarkdownString(`
${uriText}
`, { supportHtml: true }), + classes: [isResultText ? 'share-dialog-input-text' : 'share-dialog-input-link'] + }] + }, + cancelButton: localize('close', 'Close'), + buttons: isResultText ? [] : [{ label: localize('open link', 'Open Link'), run: () => { urlService.open(result, { openExternal: true }); } }] + } + ); + } + } + }) + ); const actions = this.shareService.getShareActions(); for (const menuId of targetMenus) { for (const action of actions) { // todo@joyceerhl avoid duplicates - MenuRegistry.appendMenuItem(menuId, action); + this._disposables.add(MenuRegistry.appendMenuItem(menuId, action)); } } } From bce3304e9841e4f54863af6c11c1348cef8700be Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Wed, 28 Jun 2023 16:41:01 -0700 Subject: [PATCH 020/926] Fix vscode-jupyter#13522. Clear attachment if it does not exit. (#186578) Fix #184630. Clear attachment if it does not exit. --- extensions/ipynb/src/notebookAttachmentCleaner.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/ipynb/src/notebookAttachmentCleaner.ts b/extensions/ipynb/src/notebookAttachmentCleaner.ts index 8468b0cffb2..cad19f07b29 100644 --- a/extensions/ipynb/src/notebookAttachmentCleaner.ts +++ b/extensions/ipynb/src/notebookAttachmentCleaner.ts @@ -232,7 +232,7 @@ export class AttachmentCleaner implements vscode.CodeActionProvider { if (cell.index > -1 && !objectEquals(markdownAttachmentsInUse, cell.metadata.attachments)) { const updateMetadata: { [key: string]: any } = deepClone(cell.metadata); if (Object.keys(markdownAttachmentsInUse).length === 0) { - updateMetadata.attachments = null; + updateMetadata.attachments = undefined; } else { updateMetadata.attachments = markdownAttachmentsInUse; } From 52b72a22f939cf2fa485dcd8dcc5c927397bee5c Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Wed, 28 Jun 2023 16:43:18 -0700 Subject: [PATCH 021/926] Share > Copy vscode.dev Link polish (#186576) * Prompt when copying link and behind remote * Make dialog actions explicit re: copy behavior --- extensions/github/src/links.ts | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/extensions/github/src/links.ts b/extensions/github/src/links.ts index bd8cdfc5c82..b270792404f 100644 --- a/extensions/github/src/links.ts +++ b/extensions/github/src/links.ts @@ -195,7 +195,7 @@ export async function ensurePublished(repository: Repository, file: vscode.Uri) // If HEAD is not published, make sure it is && !repository?.state.HEAD?.upstream ) { - const publishBranch = vscode.l10n.t('Publish Branch'); + const publishBranch = vscode.l10n.t('Publish Branch & Copy Link'); const selection = await vscode.window.showInformationMessage( vscode.l10n.t('The current branch is not published to the remote. Would you like to publish your branch before copying a link?'), { modal: true }, @@ -209,7 +209,7 @@ export async function ensurePublished(repository: Repository, file: vscode.Uri) } const uncommittedChanges = [...repository.state.workingTreeChanges, ...repository.state.indexChanges]; - if (uncommittedChanges.find((c) => c.uri.toString() === file.toString())) { + if (uncommittedChanges.find((c) => c.uri.toString() === file.toString()) && !repository.state.HEAD?.ahead && !repository.state.HEAD?.behind) { const commitChanges = vscode.l10n.t('Commit Changes'); const copyAnyway = vscode.l10n.t('Copy Anyway'); const selection = await vscode.window.showWarningMessage( @@ -225,7 +225,7 @@ export async function ensurePublished(repository: Repository, file: vscode.Uri) throw new vscode.CancellationError(); } } else if (repository.state.HEAD?.ahead) { - const pushCommits = vscode.l10n.t('Push Commits'); + const pushCommits = vscode.l10n.t('Push Commits & Copy Link'); const selection = await vscode.window.showInformationMessage( vscode.l10n.t('The current branch has unpublished commits. Would you like to push your commits before copying a link?'), { modal: true }, @@ -236,6 +236,18 @@ export async function ensurePublished(repository: Repository, file: vscode.Uri) } await repository.push(); + } else if (repository.state.HEAD?.behind) { + const pull = vscode.l10n.t('Pull Changes & Copy Link'); + const selection = await vscode.window.showInformationMessage( + vscode.l10n.t('The current branch is not up to date. Would you like to pull before copying a link?'), + { modal: true }, + pull + ); + if (selection !== pull) { + throw new vscode.CancellationError(); + } + + await repository.pull(); } await repository.status(); From 1c21765ab51ec573b5d39e24042b494fe8b770b4 Mon Sep 17 00:00:00 2001 From: Bhavya U Date: Wed, 28 Jun 2023 16:52:10 -0700 Subject: [PATCH 022/926] Remove experimentation service from featured extensions on welcome page. (#186569) Remove experimentation service from featured extensions --- .../browser/featuredExtensionService.ts | 81 +++++++------------ 1 file changed, 31 insertions(+), 50 deletions(-) diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/browser/featuredExtensionService.ts b/src/vs/workbench/contrib/welcomeGettingStarted/browser/featuredExtensionService.ts index a505099365e..d36253420a8 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/browser/featuredExtensionService.ts +++ b/src/vs/workbench/contrib/welcomeGettingStarted/browser/featuredExtensionService.ts @@ -12,11 +12,9 @@ import { IFeaturedExtension } from 'vs/base/common/product'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { localize } from 'vs/nls'; -import { IWorkbenchAssignmentService } from 'vs/workbench/services/assignment/common/assignmentService'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; -import { raceTimeout } from 'vs/base/common/async'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -type FeaturedExtensionTreatment = { extensions: string[]; showAsList?: string }; type FeaturedExtensionStorageData = { title: string; description: string; imagePath: string; date: number }; export const IFeaturedExtensionsService = createDecorator('featuredExtensionsService'); @@ -38,20 +36,19 @@ export class FeaturedExtensionsService extends Disposable implements IFeaturedEx declare readonly _serviceBrand: undefined; private ignoredExtensions: Set = new Set(); - private treatment: FeaturedExtensionTreatment | undefined; private _isInitialized: boolean = false; private static readonly STORAGE_KEY = 'workbench.welcomePage.extensionMetadata'; constructor( @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, - @IWorkbenchAssignmentService private readonly tasExperimentService: IWorkbenchAssignmentService, + @IExtensionService private readonly extensionService: IExtensionService, @IStorageService private readonly storageService: IStorageService, @IProductService private readonly productService: IProductService, @IExtensionGalleryService private readonly galleryService: IExtensionGalleryService, ) { super(); - this.title = localize('gettingStarted.featuredTitle', 'Featured'); + this.title = localize('gettingStarted.featuredTitle', 'Recommended'); } title: string; @@ -60,19 +57,11 @@ export class FeaturedExtensionsService extends Disposable implements IFeaturedEx await this._init(); - let treatments = this.treatment?.extensions?.filter(extension => !this.ignoredExtensions.has(extension)) ?? new Array(); - const featuredExtensions: IFeaturedExtension[] = new Array(); - - if (this.treatment?.showAsList !== 'true' && treatments.length > 0) { - // pick a random extensionId for display - const treatment = treatments[Math.floor(Math.random() * treatments.length)]; - treatments = [treatment]; - } - - for (const treatment of treatments) { - const extension = await this.resolveExtension(treatment); - if (extension) { - featuredExtensions.push(extension); + const featuredExtensions: IFeaturedExtension[] = []; + for (const extension of this.productService.featuredExtensions?.filter(e => !this.ignoredExtensions.has(e.id)) ?? []) { + const resolvedExtension = await this.resolveExtension(extension); + if (resolvedExtension) { + featuredExtensions.push(resolvedExtension); } } @@ -85,50 +74,42 @@ export class FeaturedExtensionsService extends Disposable implements IFeaturedEx return; } - const [extensions, extensionListTitle] = await Promise.all([raceTimeout(this.tasExperimentService?.getTreatment('welcome.featured.item'), 2000), - raceTimeout(this.tasExperimentService?.getTreatment('welcome.featured.title'), 2000)]); - - try { - this.treatment = extensions ? JSON.parse(extensions) : { extensions: [] }; - } catch { + const featuredExtensions = this.productService.featuredExtensions; + if (!featuredExtensions) { + this._isInitialized = true; + return; } - this.title = extensionListTitle ?? localize('gettingStarted.featuredTitle', 'Featured'); - - if (this.treatment?.extensions && Array.isArray(this.treatment.extensions)) { - const installed = await this.extensionManagementService.getInstalled(); - for (const extension of this.treatment.extensions) { - if (installed.some(e => ExtensionIdentifier.equals(e.identifier.id, extension))) { - this.ignoredExtensions.add(extension); + await this.extensionService.whenInstalledExtensionsRegistered(); + const installed = await this.extensionManagementService.getInstalled(); + for (const extension of featuredExtensions) { + if (installed.some(e => ExtensionIdentifier.equals(e.identifier.id, extension.id))) { + this.ignoredExtensions.add(extension.id); + } + else { + let galleryExtension: IGalleryExtension | undefined; + try { + galleryExtension = (await this.galleryService.getExtensions([{ id: extension.id }], CancellationToken.None))[0]; + } catch (err) { + continue; } - else { - let galleryExtension: IGalleryExtension | undefined; - try { - galleryExtension = (await this.galleryService.getExtensions([{ id: extension }], CancellationToken.None))[0]; - } catch (err) { - continue; - } - if (!await this.extensionManagementService.canInstall(galleryExtension)) { - this.ignoredExtensions.add(extension); - } + if (!await this.extensionManagementService.canInstall(galleryExtension)) { + this.ignoredExtensions.add(extension.id); } } } - this._isInitialized = true; } - private async resolveExtension(extensionId: string): Promise { + private async resolveExtension(productMetadata: IFeaturedExtension): Promise { - const productMetadata = this.productService.featuredExtensions?.find(e => ExtensionIdentifier.equals(e.id, extensionId)); - - const title = productMetadata?.title ?? await this.getMetadata(extensionId, FeaturedExtensionMetadataType.Title); - const description = productMetadata?.description ?? await this.getMetadata(extensionId, FeaturedExtensionMetadataType.Description); - const imagePath = productMetadata?.imagePath ?? await this.getMetadata(extensionId, FeaturedExtensionMetadataType.ImagePath); + const title = productMetadata.title ?? await this.getMetadata(productMetadata.id, FeaturedExtensionMetadataType.Title); + const description = productMetadata.description ?? await this.getMetadata(productMetadata.id, FeaturedExtensionMetadataType.Description); + const imagePath = productMetadata.imagePath ?? await this.getMetadata(productMetadata.id, FeaturedExtensionMetadataType.ImagePath); if (title && description && imagePath) { return { - id: extensionId, + id: productMetadata.id, title: title, description: description, imagePath: imagePath, From 7fc5c6aa9a77de7a0271b8a2bc1d9a821106fe10 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 28 Jun 2023 16:57:54 -0700 Subject: [PATCH 023/926] Fix wrong width of chat input in chat editors (#186580) --- src/vs/workbench/contrib/chat/browser/chatEditor.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatEditor.ts b/src/vs/workbench/contrib/chat/browser/chatEditor.ts index c98b0b826fd..e9eb2f42b2c 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditor.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditor.ts @@ -117,8 +117,7 @@ export class ChatEditor extends EditorPane { override layout(dimension: dom.Dimension, position?: dom.IDomPosition | undefined): void { if (this.widget) { - const width = Math.min(dimension.width, 600); - this.widget.layout(dimension.height, width); + this.widget.layout(dimension.height, dimension.width); } } } From 715334ba8c8ac91802053cc1e3ed44b99e5b0bb4 Mon Sep 17 00:00:00 2001 From: Meghan Kulkarni Date: Wed, 28 Jun 2023 17:14:23 -0700 Subject: [PATCH 024/926] small changes (#186574) * bug fixes * added label to copyPaste.ts * added localized label to copyPasteLinks file --- extensions/markdown-language-features/package.nls.json | 2 +- .../src/languageFeatures/copyFiles/copyPaste.ts | 4 ++-- .../src/languageFeatures/copyFiles/copyPasteLinks.ts | 6 +++--- .../src/languageFeatures/copyFiles/shared.ts | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/extensions/markdown-language-features/package.nls.json b/extensions/markdown-language-features/package.nls.json index c97971764ea..e39e597b02f 100644 --- a/extensions/markdown-language-features/package.nls.json +++ b/extensions/markdown-language-features/package.nls.json @@ -41,7 +41,7 @@ "configuration.markdown.editor.drop.copyIntoWorkspace": "Controls if files outside of the workspace that are dropped into a Markdown editor should be copied into the workspace.\n\nUse `#markdown.copyFiles.destination#` to configure where copied dropped files should be created", "configuration.markdown.editor.filePaste.enabled": "Enable pasting files into a Markdown editor to create Markdown links. Requires enabling `#editor.pasteAs.enabled#`.", "configuration.markdown.editor.filePaste.copyIntoWorkspace": "Controls if files outside of the workspace that are pasted into a Markdown editor should be copied into the workspace.\n\nUse `#markdown.copyFiles.destination#` to configure where copied files should be created.", - "configuration.markdown.editor.pasteUrlAsFormattedLink.enabled": "Controls if a Markdown link is created when a URL is pasted into the Markdown editor.", + "configuration.markdown.editor.pasteUrlAsFormattedLink.enabled": "Controls if a Markdown link is created when a URL is pasted into the Markdown editor. Requires enabling `#editor.pasteAs.enabled#`.", "configuration.copyIntoWorkspace.mediaFiles": "Try to copy external image and video files into the workspace.", "configuration.copyIntoWorkspace.never": "Do not copy external files into the workspace.", "configuration.markdown.validate.enabled.description": "Enable all error reporting in Markdown files.", diff --git a/extensions/markdown-language-features/src/languageFeatures/copyFiles/copyPaste.ts b/extensions/markdown-language-features/src/languageFeatures/copyFiles/copyPaste.ts index 9d1bc003106..24e23758d25 100644 --- a/extensions/markdown-language-features/src/languageFeatures/copyFiles/copyPaste.ts +++ b/extensions/markdown-language-features/src/languageFeatures/copyFiles/copyPaste.ts @@ -27,8 +27,7 @@ class PasteEditProvider implements vscode.DocumentPasteEditProvider { return createEdit; } - const label = vscode.l10n.t('Insert Markdown Media'); - const uriEdit = new vscode.DocumentPasteEdit('', this._id, label); + const uriEdit = new vscode.DocumentPasteEdit('', this._id, ''); const urlList = await dataTransfer.get('text/uri-list')?.asString(); if (!urlList) { return; @@ -38,6 +37,7 @@ class PasteEditProvider implements vscode.DocumentPasteEditProvider { return; } + uriEdit.label = pasteEdit.label; uriEdit.additionalEdit = pasteEdit.additionalEdits; uriEdit.priority = this._getPriority(dataTransfer); return uriEdit; diff --git a/extensions/markdown-language-features/src/languageFeatures/copyFiles/copyPasteLinks.ts b/extensions/markdown-language-features/src/languageFeatures/copyFiles/copyPasteLinks.ts index d6946ff6fbe..313a3916688 100644 --- a/extensions/markdown-language-features/src/languageFeatures/copyFiles/copyPasteLinks.ts +++ b/extensions/markdown-language-features/src/languageFeatures/copyFiles/copyPasteLinks.ts @@ -8,7 +8,7 @@ import { getMarkdownLink } from './shared'; class PasteLinkEditProvider implements vscode.DocumentPasteEditProvider { - private readonly _id = 'insertMarkdownLink'; + readonly id = 'insertMarkdownLink'; async provideDocumentPasteEdits( document: vscode.TextDocument, ranges: readonly vscode.Range[], @@ -28,8 +28,7 @@ class PasteLinkEditProvider implements vscode.DocumentPasteEditProvider { return; } - const label = vscode.l10n.t('Insert Markdown Link'); - const uriEdit = new vscode.DocumentPasteEdit('', this._id, label); + const uriEdit = new vscode.DocumentPasteEdit('', this.id, ''); const urlList = await item?.asString(); if (!urlList) { return undefined; @@ -39,6 +38,7 @@ class PasteLinkEditProvider implements vscode.DocumentPasteEditProvider { return; } + uriEdit.label = pasteEdit.label; uriEdit.additionalEdit = pasteEdit.additionalEdits; return uriEdit; } diff --git a/extensions/markdown-language-features/src/languageFeatures/copyFiles/shared.ts b/extensions/markdown-language-features/src/languageFeatures/copyFiles/shared.ts index 402ed072798..c9d2aabf568 100644 --- a/extensions/markdown-language-features/src/languageFeatures/copyFiles/shared.ts +++ b/extensions/markdown-language-features/src/languageFeatures/copyFiles/shared.ts @@ -153,7 +153,7 @@ export function createUriListSnippet( if (insertAsMedia) { insertedImageCount++; snippet.appendText('!['); - const placeholderText = options?.placeholderText ? (escapeBrackets(title) || 'Alt text') : 'label'; + const placeholderText = escapeBrackets(title) || options?.placeholderText || 'Alt text'; const placeholderIndex = typeof options?.placeholderStartIndex !== 'undefined' ? options?.placeholderStartIndex + i : (placeholderValue === 0 ? undefined : placeholderValue); snippet.appendPlaceholder(placeholderText, placeholderIndex); snippet.appendText(`](${escapeMarkdownLinkPath(mdPath)})`); From c1bca6d7cc2c5f71ae04deda90c0cf50819ffde4 Mon Sep 17 00:00:00 2001 From: Bhavya U Date: Wed, 28 Jun 2023 17:18:50 -0700 Subject: [PATCH 025/926] Set editorId for GettingStartedInput (#186583) --- .../welcomeGettingStarted/browser/gettingStartedInput.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedInput.ts b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedInput.ts index 23df7b598be..de0fc8b997a 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedInput.ts +++ b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedInput.ts @@ -26,6 +26,10 @@ export class GettingStartedInput extends EditorInput { return GettingStartedInput.ID; } + override get editorId(): string | undefined { + return this.typeId; + } + override toUntyped(): IUntypedEditorInput { return { resource: GettingStartedInput.RESOURCE, From 8c9bbe5bff8921318241b17cf321db9223e386e9 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 28 Jun 2023 21:46:50 -0700 Subject: [PATCH 026/926] Move single terminal tab to hover service Fixes #177774 --- .../contrib/terminal/browser/terminalView.ts | 56 ++++++++++++++----- 1 file changed, 43 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalView.ts b/src/vs/workbench/contrib/terminal/browser/terminalView.ts index c7806425d77..c5b3308b7d4 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalView.ts @@ -40,11 +40,13 @@ import { getColorClass, getUriClasses } from 'vs/workbench/contrib/terminal/brow import { withNullAsUndefined } from 'vs/base/common/types'; import { getTerminalActionBarArgs } from 'vs/workbench/contrib/terminal/browser/terminalMenus'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; -import { getShellIntegrationTooltip, getShellProcessTooltip } from 'vs/workbench/contrib/terminal/browser/terminalTooltip'; +import { getInstanceHoverInfo } from 'vs/workbench/contrib/terminal/browser/terminalTooltip'; import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities'; import { defaultSelectBoxStyles } from 'vs/platform/theme/browser/defaultStyles'; import { Event } from 'vs/base/common/event'; +import { IHoverDelegate, IHoverDelegateOptions } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; +import { IHoverService } from 'vs/workbench/services/hover/browser/hover'; export class TerminalViewPane extends ViewPane { private _fontStyleElement: HTMLElement | undefined; @@ -351,7 +353,10 @@ class SingleTerminalTabActionViewItem extends MenuEntryActionViewItem { @ICommandService private readonly _commandService: ICommandService, @IInstantiationService private readonly _instantiationService: IInstantiationService, ) { - super(action, { draggable: true }, keybindingService, notificationService, contextKeyService, themeService, contextMenuService); + super(action, { + draggable: true, + hoverDelegate: _instantiationService.createInstance(SingleTabHoverDelegate) + }, keybindingService, notificationService, contextKeyService, themeService, contextMenuService); // Register listeners to update the tab this._register(Event.debounce>(Event.any( @@ -468,7 +473,6 @@ class SingleTerminalTabActionViewItem extends MenuEntryActionViewItem { this._altCommand = `alt-command`; label.classList.add(this._altCommand); } - this._action.tooltip = getSingleTabTooltip(instance, this._terminalService.configHelper.config.tabs.separator); this.updateTooltip(); } } @@ -498,16 +502,6 @@ function getSingleTabLabel(accessor: ServicesAccessor, instance: ITerminalInstan return `${label} $(${primaryStatus.icon.id})`; } -function getSingleTabTooltip(instance: ITerminalInstance | undefined, separator: string): string { - if (!instance) { - return ''; - } - const parts: string[] = []; - parts.push(getSingleTabTitle(instance, separator) + getShellProcessTooltip(instance, false) + getShellIntegrationTooltip(instance, false)); - parts.push(instance.statusList.primary?.tooltip || ''); - return parts.filter(e => e).join('\n\n'); -} - function getSingleTabTitle(instance: ITerminalInstance | undefined, separator: string): string { if (!instance) { return ''; @@ -585,3 +579,39 @@ class TerminalThemeIconStyle extends Themable { this._styleElement.textContent = css; } } + +class SingleTabHoverDelegate implements IHoverDelegate { + private _lastHoverHideTime: number = 0; + + readonly placement = 'element'; + + constructor( + @IConfigurationService private readonly _configurationService: IConfigurationService, + @IHoverService private readonly _hoverService: IHoverService, + @ITerminalGroupService private readonly _terminalGroupService: ITerminalGroupService + ) { + } + + get delay(): number { + return Date.now() - this._lastHoverHideTime < 200 + ? 0 // show instantly when a hover was recently shown + : this._configurationService.getValue('workbench.hover.delay'); + } + + showHover(options: IHoverDelegateOptions, focus?: boolean) { + const instance = this._terminalGroupService.activeInstance; + if (!instance) { + return; + } + const hoverInfo = getInstanceHoverInfo(instance); + return this._hoverService.showHover({ + ...options, + content: hoverInfo.content, + actions: hoverInfo.actions + }, focus); + } + + onDidHideHover() { + this._lastHoverHideTime = Date.now(); + } +} From aa6eac54123cc20797d2d2804d79dabc8d633e1e Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Thu, 29 Jun 2023 10:56:08 +0600 Subject: [PATCH 027/926] simplifying the width, not taking into account the cursor position --- src/vs/editor/contrib/hover/browser/contentHover.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/vs/editor/contrib/hover/browser/contentHover.ts b/src/vs/editor/contrib/hover/browser/contentHover.ts index c0a6bdf7541..f03cf308b61 100644 --- a/src/vs/editor/contrib/hover/browser/contentHover.ts +++ b/src/vs/editor/contrib/hover/browser/contentHover.ts @@ -618,11 +618,9 @@ export class ContentHoverWidget extends ResizableContentWidget { if (!this._editor || !this._editor.hasModel()) { return; } - const editorLayoutInfo = this._editor.getLayoutInfo(); - const glyphMarginRight = editorLayoutInfo.glyphMarginLeft + editorLayoutInfo.glyphMarginWidth; - const cursorPosition = this._editor._getViewModel().getPrimaryCursorState().viewState.position; const bodyBoxWidth = dom.getClientArea(document.body).width; - return bodyBoxWidth - glyphMarginRight - this._editor.getOffsetForColumn(cursorPosition.lineNumber, cursorPosition.column); + const horizontalPadding = 14; + return bodyBoxWidth - horizontalPadding; } public isMouseGettingCloser(posx: number, posy: number): boolean { From 01ad4ddaaae8663be9ad0219ae674d7ee39718aa Mon Sep 17 00:00:00 2001 From: Bhavya U Date: Thu, 29 Jun 2023 01:07:42 -0700 Subject: [PATCH 028/926] Dont show welcome widget when notifications are visible (#186592) --- .../browser/welcomeDialog.contribution.ts | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/contrib/welcomeDialog/browser/welcomeDialog.contribution.ts b/src/vs/workbench/contrib/welcomeDialog/browser/welcomeDialog.contribution.ts index e5286635bb5..9fcc7bb449f 100644 --- a/src/vs/workbench/contrib/welcomeDialog/browser/welcomeDialog.contribution.ts +++ b/src/vs/workbench/contrib/welcomeDialog/browser/welcomeDialog.contribution.ts @@ -10,19 +10,13 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { Disposable } from 'vs/base/common/lifecycle'; -import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { WelcomeWidget } from 'vs/workbench/contrib/welcomeDialog/browser/welcomeWidget'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { IWebviewService } from 'vs/workbench/contrib/webview/browser/webview'; -import { IFileService } from 'vs/platform/files/common/files'; -import { INotificationService } from 'vs/platform/notification/common/notification'; -import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { LanguageService } from 'vs/editor/common/services/languageService'; -import { ILanguageService } from 'vs/editor/common/languages/language'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; import { localize } from 'vs/nls'; import { applicationConfigurationNodeBase } from 'vs/workbench/common/configuration'; @@ -45,11 +39,6 @@ class WelcomeDialogContribution extends Disposable implements IWorkbenchContribu @ICommandService readonly commandService: ICommandService, @ITelemetryService readonly telemetryService: ITelemetryService, @IOpenerService readonly openerService: IOpenerService, - @IWebviewService readonly webviewService: IWebviewService, - @IFileService readonly fileService: IFileService, - @INotificationService readonly notificationService: INotificationService, - @IExtensionService readonly extensionService: IExtensionService, - @ILanguageService readonly languageService: LanguageService, @IEditorService readonly editorService: IEditorService ) { super(); @@ -74,7 +63,9 @@ class WelcomeDialogContribution extends Disposable implements IWorkbenchContribu const codeEditor = codeEditorService.getActiveCodeEditor(); if (codeEditor?.hasModel()) { const scheduler = new RunOnceScheduler(() => { - if (codeEditor === codeEditorService.getActiveCodeEditor()) { + const notificationsVisible = contextService.contextMatchesRules(ContextKeyExpr.deserialize('notificationCenterVisible')) || + contextService.contextMatchesRules(ContextKeyExpr.deserialize('notificationToastsVisible')); + if (codeEditor === codeEditorService.getActiveCodeEditor() && !notificationsVisible) { this.isRendered = true; const welcomeWidget = new WelcomeWidget( From eded89bc04c70333d58e301af393c9b6b9b95648 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 29 Jun 2023 10:12:02 +0200 Subject: [PATCH 029/926] fix #186269 (#186602) --- .../issue/browser/issueTroubleshoot.ts | 2 +- .../userDataProfileImportExportService.ts | 83 ++++++++++--------- .../userDataProfile/common/userDataProfile.ts | 2 +- 3 files changed, 48 insertions(+), 39 deletions(-) diff --git a/src/vs/workbench/services/issue/browser/issueTroubleshoot.ts b/src/vs/workbench/services/issue/browser/issueTroubleshoot.ts index 188f77ea115..2d2e398a91f 100644 --- a/src/vs/workbench/services/issue/browser/issueTroubleshoot.ts +++ b/src/vs/workbench/services/issue/browser/issueTroubleshoot.ts @@ -120,7 +120,7 @@ class TroubleshootIssueService extends Disposable implements ITroubleshootIssueS } const originalProfile = this.userDataProfileService.currentProfile; - await this.userDataProfileImportExportService.createTemporaryProfile(this.userDataProfileService.currentProfile, localize('troubleshoot issue', "Troubleshoot Issue"), true); + await this.userDataProfileImportExportService.createTroubleshootProfile(); this.state = new TroubleShootState(TroubleshootStage.EXTENSIONS, originalProfile.id); await this.resume(); } diff --git a/src/vs/workbench/services/userDataProfile/browser/userDataProfileImportExportService.ts b/src/vs/workbench/services/userDataProfile/browser/userDataProfileImportExportService.ts index e08825b377a..eb091f8a9c5 100644 --- a/src/vs/workbench/services/userDataProfile/browser/userDataProfileImportExportService.ts +++ b/src/vs/workbench/services/userDataProfile/browser/userDataProfileImportExportService.ts @@ -256,11 +256,16 @@ export class UserDataProfileImportExportService extends Disposable implements IU } } - async createTemporaryProfile(profile: IUserDataProfile, name: string, extensionsDisabled: boolean): Promise { - const userDataProfilesExportState = this.instantiationService.createInstance(UserDataProfileExportState, profile, extensionsDisabled); + async createTroubleshootProfile(): Promise { + const userDataProfilesExportState = this.instantiationService.createInstance(UserDataProfileExportState, this.userDataProfileService.currentProfile, true); try { - const profileTemplate = await userDataProfilesExportState.getProfileTemplate(name, undefined); - await this.importAndSwitch(profileTemplate, true, true, extensionsDisabled, localize('import', "Create Profile")); + const profileTemplate = await userDataProfilesExportState.getProfileTemplate(localize('troubleshoot issue', "Troubleshoot Issue"), undefined); + await this.progressService.withProgress({ + location: ProgressLocation.Notification, + delay: 1000, + sticky: true, + }, progress => + this.importAndSwitchWithProgress(profileTemplate, true, true, true, message => progress.report({ message: localize('troubleshoot profile progress', "Setting up Troubleshoot Profile: {0}", message) }))); } finally { userDataProfilesExportState.dispose(); } @@ -464,42 +469,46 @@ export class UserDataProfileImportExportService extends Disposable implements IU command: showWindowLogActionId, }, async (progress) => { progress.report({ message: localize('Importing profile', "{0} ({1})...", title, profileTemplate.name) }); - const profile = await this.getProfileToImport(profileTemplate, temporaryProfile); - if (!profile) { - return undefined; - } - - if (profileTemplate.settings) { - progress.report({ message: localize('progress settings', "{0} ({1}): Applying Settings...", title, profileTemplate.name) }); - await this.instantiationService.createInstance(SettingsResource).apply(profileTemplate.settings, profile); - } - if (profileTemplate.keybindings) { - progress.report({ message: localize('progress keybindings', "{0} ({1}): Applying Keyboard Shortcuts...", title, profileTemplate.name) }); - await this.instantiationService.createInstance(KeybindingsResource).apply(profileTemplate.keybindings, profile); - } - if (profileTemplate.tasks) { - progress.report({ message: localize('progress tasks', "{0} ({1}): Applying Tasks...", title, profileTemplate.name) }); - await this.instantiationService.createInstance(TasksResource).apply(profileTemplate.tasks, profile); - } - if (profileTemplate.snippets) { - progress.report({ message: localize('progress snippets', "{0} ({1}): Applying Snippets...", title, profileTemplate.name) }); - await this.instantiationService.createInstance(SnippetsResource).apply(profileTemplate.snippets, profile); - } - if (profileTemplate.globalState) { - progress.report({ message: localize('progress global state', "{0} ({1}): Applying State...", title, profileTemplate.name) }); - await this.instantiationService.createInstance(GlobalStateResource).apply(profileTemplate.globalState, profile); - } - if (profileTemplate.extensions && extensions) { - progress.report({ message: localize('progress extensions', "{0} ({1}): Applying Extensions...", title, profileTemplate.name) }); - await this.instantiationService.createInstance(ExtensionsResource, extensionsDisabled).apply(profileTemplate.extensions, profile); - } - - progress.report({ message: localize('switching profile', "{0} ({1}): Applying...", title, profileTemplate.name) }); - await this.userDataProfileManagementService.switchProfile(profile); - return profile; + return this.importAndSwitchWithProgress(profileTemplate, temporaryProfile, extensions, extensionsDisabled, message => progress.report({ message: `${title} (${profileTemplate.name}): ${message}` })); }); } + private async importAndSwitchWithProgress(profileTemplate: IUserDataProfileTemplate, temporaryProfile: boolean, extensions: boolean, extensionsDisabled: boolean, progress: (message: string) => void): Promise { + const profile = await this.getProfileToImport(profileTemplate, temporaryProfile); + if (!profile) { + return undefined; + } + + if (profileTemplate.settings) { + progress(localize('progress settings', "Applying Settings...")); + await this.instantiationService.createInstance(SettingsResource).apply(profileTemplate.settings, profile); + } + if (profileTemplate.keybindings) { + progress(localize('progress keybindings', "{0}ying Keyboard Shortcuts...")); + await this.instantiationService.createInstance(KeybindingsResource).apply(profileTemplate.keybindings, profile); + } + if (profileTemplate.tasks) { + progress(localize('progress tasks', "Applying Tasks...")); + await this.instantiationService.createInstance(TasksResource).apply(profileTemplate.tasks, profile); + } + if (profileTemplate.snippets) { + progress(localize('progress snippets', "Applying Snippets...")); + await this.instantiationService.createInstance(SnippetsResource).apply(profileTemplate.snippets, profile); + } + if (profileTemplate.globalState) { + progress(localize('progress global state', "Applying State...")); + await this.instantiationService.createInstance(GlobalStateResource).apply(profileTemplate.globalState, profile); + } + if (profileTemplate.extensions && extensions) { + progress(localize('progress extensions', "Applying Extensions...")); + await this.instantiationService.createInstance(ExtensionsResource, extensionsDisabled).apply(profileTemplate.extensions, profile); + } + + progress(localize('switching profile', " Applying...")); + await this.userDataProfileManagementService.switchProfile(profile); + return profile; + } + private async resolveProfileContent(resource: URI): Promise { if (await this.fileUserDataProfileContentHandler.canHandle(resource)) { return this.fileUserDataProfileContentHandler.readProfile(resource, CancellationToken.None); diff --git a/src/vs/workbench/services/userDataProfile/common/userDataProfile.ts b/src/vs/workbench/services/userDataProfile/common/userDataProfile.ts index ac7c6b8f417..87b0b2edc53 100644 --- a/src/vs/workbench/services/userDataProfile/common/userDataProfile.ts +++ b/src/vs/workbench/services/userDataProfile/common/userDataProfile.ts @@ -88,7 +88,7 @@ export interface IUserDataProfileImportExportService { importProfile(uri: URI, options?: IProfileImportOptions): Promise; showProfileContents(): Promise; createFromCurrentProfile(name: string): Promise; - createTemporaryProfile(from: IUserDataProfile, name: string, extensionsDisabled: boolean): Promise; + createTroubleshootProfile(): Promise; setProfile(profile: IUserDataProfileTemplate): Promise; } From 001ee209348a7f0b79d0dab1dd7d0054ab29cc1c Mon Sep 17 00:00:00 2001 From: yiliang114 <1204183885@qq.com> Date: Thu, 29 Jun 2023 16:52:29 +0800 Subject: [PATCH 030/926] fix: typos --- src/vs/base/common/console.ts | 2 +- .../browser/extensionEnablementService.ts | 2 +- ...extensionManifestPropertiesService.test.ts | 56 +++++++++---------- 3 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/vs/base/common/console.ts b/src/vs/base/common/console.ts index 54bfadefbae..6527f72f5e1 100644 --- a/src/vs/base/common/console.ts +++ b/src/vs/base/common/console.ts @@ -125,7 +125,7 @@ export function log(entry: IRemoteConsoleLog, label: string): void { consoleArgs = [`%c[${label}]%`, color('blue'), ...args]; } - // Stack: add to args unless already aded + // Stack: add to args unless already added if (topFrame && !isOneStringArg) { consoleArgs.push(topFrame); } diff --git a/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts b/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts index 92e20067052..aa5eaed8814 100644 --- a/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts +++ b/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts @@ -264,7 +264,7 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench const index = extensionsToEnable.findIndex(e => areSameExtensions(e.identifier, extension.identifier)); - // Extension is not aded to the disablement list so add it + // Extension is not added to the disablement list so add it if (index === -1) { extensionsToEnable.push(extension); } diff --git a/src/vs/workbench/services/extensions/test/common/extensionManifestPropertiesService.test.ts b/src/vs/workbench/services/extensions/test/common/extensionManifestPropertiesService.test.ts index b03d975f3fd..9c890337351 100644 --- a/src/vs/workbench/services/extensions/test/common/extensionManifestPropertiesService.test.ts +++ b/src/vs/workbench/services/extensions/test/common/extensionManifestPropertiesService.test.ts @@ -108,9 +108,9 @@ if (!isWeb) { teardown(() => testObject.dispose()); - function assertUntrustedWorkspaceSupport(extensionMaifest: IExtensionManifest, expected: ExtensionUntrustedWorkspaceSupportType): void { + function assertUntrustedWorkspaceSupport(extensionManifest: IExtensionManifest, expected: ExtensionUntrustedWorkspaceSupportType): void { testObject = instantiationService.createInstance(ExtensionManifestPropertiesService); - const untrustedWorkspaceSupport = testObject.getExtensionUntrustedWorkspaceSupportType(extensionMaifest); + const untrustedWorkspaceSupport = testObject.getExtensionUntrustedWorkspaceSupportType(extensionManifest); assert.strictEqual(untrustedWorkspaceSupport, expected); } @@ -123,16 +123,16 @@ if (!isWeb) { instantiationService.stub(IProductService, >{}); instantiationService.stub(IWorkspaceTrustEnablementService, new TestWorkspaceTrustEnablementService()); - const extensionMaifest = getExtensionManifest(); - assertUntrustedWorkspaceSupport(extensionMaifest, true); + const extensionManifest = getExtensionManifest(); + assertUntrustedWorkspaceSupport(extensionManifest, true); }); test('test extension workspace trust request when workspace trust is disabled', async () => { instantiationService.stub(IProductService, >{}); instantiationService.stub(IWorkspaceTrustEnablementService, new TestWorkspaceTrustEnablementService(false)); - const extensionMaifest = getExtensionManifest({ main: './out/extension.js' }); - assertUntrustedWorkspaceSupport(extensionMaifest, true); + const extensionManifest = getExtensionManifest({ main: './out/extension.js' }); + assertUntrustedWorkspaceSupport(extensionManifest, true); }); test('test extension workspace trust request when "true" override exists in settings.json', async () => { @@ -140,8 +140,8 @@ if (!isWeb) { instantiationService.stub(IWorkspaceTrustEnablementService, new TestWorkspaceTrustEnablementService()); await testConfigurationService.setUserConfiguration('extensions', { supportUntrustedWorkspaces: { 'pub.a': { supported: true } } }); - const extensionMaifest = getExtensionManifest({ main: './out/extension.js', capabilities: { untrustedWorkspaces: { supported: 'limited' } } }); - assertUntrustedWorkspaceSupport(extensionMaifest, true); + const extensionManifest = getExtensionManifest({ main: './out/extension.js', capabilities: { untrustedWorkspaces: { supported: 'limited' } } }); + assertUntrustedWorkspaceSupport(extensionManifest, true); }); test('test extension workspace trust request when override (false) exists in settings.json', async () => { @@ -149,8 +149,8 @@ if (!isWeb) { instantiationService.stub(IWorkspaceTrustEnablementService, new TestWorkspaceTrustEnablementService()); await testConfigurationService.setUserConfiguration('extensions', { supportUntrustedWorkspaces: { 'pub.a': { supported: false } } }); - const extensionMaifest = getExtensionManifest({ main: './out/extension.js', capabilities: { untrustedWorkspaces: { supported: 'limited' } } }); - assertUntrustedWorkspaceSupport(extensionMaifest, false); + const extensionManifest = getExtensionManifest({ main: './out/extension.js', capabilities: { untrustedWorkspaces: { supported: 'limited' } } }); + assertUntrustedWorkspaceSupport(extensionManifest, false); }); test('test extension workspace trust request when override (true) for the version exists in settings.json', async () => { @@ -158,8 +158,8 @@ if (!isWeb) { instantiationService.stub(IWorkspaceTrustEnablementService, new TestWorkspaceTrustEnablementService()); await testConfigurationService.setUserConfiguration('extensions', { supportUntrustedWorkspaces: { 'pub.a': { supported: true, version: '1.0.0' } } }); - const extensionMaifest = getExtensionManifest({ main: './out/extension.js', capabilities: { untrustedWorkspaces: { supported: 'limited' } } }); - assertUntrustedWorkspaceSupport(extensionMaifest, true); + const extensionManifest = getExtensionManifest({ main: './out/extension.js', capabilities: { untrustedWorkspaces: { supported: 'limited' } } }); + assertUntrustedWorkspaceSupport(extensionManifest, true); }); test('test extension workspace trust request when override (false) for the version exists in settings.json', async () => { @@ -167,8 +167,8 @@ if (!isWeb) { instantiationService.stub(IWorkspaceTrustEnablementService, new TestWorkspaceTrustEnablementService()); await testConfigurationService.setUserConfiguration('extensions', { supportUntrustedWorkspaces: { 'pub.a': { supported: false, version: '1.0.0' } } }); - const extensionMaifest = getExtensionManifest({ main: './out/extension.js', capabilities: { untrustedWorkspaces: { supported: 'limited' } } }); - assertUntrustedWorkspaceSupport(extensionMaifest, false); + const extensionManifest = getExtensionManifest({ main: './out/extension.js', capabilities: { untrustedWorkspaces: { supported: 'limited' } } }); + assertUntrustedWorkspaceSupport(extensionManifest, false); }); test('test extension workspace trust request when override for a different version exists in settings.json', async () => { @@ -176,56 +176,56 @@ if (!isWeb) { instantiationService.stub(IWorkspaceTrustEnablementService, new TestWorkspaceTrustEnablementService()); await testConfigurationService.setUserConfiguration('extensions', { supportUntrustedWorkspaces: { 'pub.a': { supported: true, version: '2.0.0' } } }); - const extensionMaifest = getExtensionManifest({ main: './out/extension.js', capabilities: { untrustedWorkspaces: { supported: 'limited' } } }); - assertUntrustedWorkspaceSupport(extensionMaifest, 'limited'); + const extensionManifest = getExtensionManifest({ main: './out/extension.js', capabilities: { untrustedWorkspaces: { supported: 'limited' } } }); + assertUntrustedWorkspaceSupport(extensionManifest, 'limited'); }); test('test extension workspace trust request when default (true) exists in product.json', () => { instantiationService.stub(IProductService, >{ extensionUntrustedWorkspaceSupport: { 'pub.a': { default: true } } }); instantiationService.stub(IWorkspaceTrustEnablementService, new TestWorkspaceTrustEnablementService()); - const extensionMaifest = getExtensionManifest({ main: './out/extension.js' }); - assertUntrustedWorkspaceSupport(extensionMaifest, true); + const extensionManifest = getExtensionManifest({ main: './out/extension.js' }); + assertUntrustedWorkspaceSupport(extensionManifest, true); }); test('test extension workspace trust request when default (false) exists in product.json', () => { instantiationService.stub(IProductService, >{ extensionUntrustedWorkspaceSupport: { 'pub.a': { default: false } } }); instantiationService.stub(IWorkspaceTrustEnablementService, new TestWorkspaceTrustEnablementService()); - const extensionMaifest = getExtensionManifest({ main: './out/extension.js' }); - assertUntrustedWorkspaceSupport(extensionMaifest, false); + const extensionManifest = getExtensionManifest({ main: './out/extension.js' }); + assertUntrustedWorkspaceSupport(extensionManifest, false); }); test('test extension workspace trust request when override (limited) exists in product.json', () => { instantiationService.stub(IProductService, >{ extensionUntrustedWorkspaceSupport: { 'pub.a': { override: 'limited' } } }); instantiationService.stub(IWorkspaceTrustEnablementService, new TestWorkspaceTrustEnablementService()); - const extensionMaifest = getExtensionManifest({ main: './out/extension.js', capabilities: { untrustedWorkspaces: { supported: true } } }); - assertUntrustedWorkspaceSupport(extensionMaifest, 'limited'); + const extensionManifest = getExtensionManifest({ main: './out/extension.js', capabilities: { untrustedWorkspaces: { supported: true } } }); + assertUntrustedWorkspaceSupport(extensionManifest, 'limited'); }); test('test extension workspace trust request when override (false) exists in product.json', () => { instantiationService.stub(IProductService, >{ extensionUntrustedWorkspaceSupport: { 'pub.a': { override: false } } }); instantiationService.stub(IWorkspaceTrustEnablementService, new TestWorkspaceTrustEnablementService()); - const extensionMaifest = getExtensionManifest({ main: './out/extension.js', capabilities: { untrustedWorkspaces: { supported: true } } }); - assertUntrustedWorkspaceSupport(extensionMaifest, false); + const extensionManifest = getExtensionManifest({ main: './out/extension.js', capabilities: { untrustedWorkspaces: { supported: true } } }); + assertUntrustedWorkspaceSupport(extensionManifest, false); }); test('test extension workspace trust request when value exists in package.json', () => { instantiationService.stub(IProductService, >{}); instantiationService.stub(IWorkspaceTrustEnablementService, new TestWorkspaceTrustEnablementService()); - const extensionMaifest = getExtensionManifest({ main: './out/extension.js', capabilities: { untrustedWorkspaces: { supported: 'limited' } } }); - assertUntrustedWorkspaceSupport(extensionMaifest, 'limited'); + const extensionManifest = getExtensionManifest({ main: './out/extension.js', capabilities: { untrustedWorkspaces: { supported: 'limited' } } }); + assertUntrustedWorkspaceSupport(extensionManifest, 'limited'); }); test('test extension workspace trust request when no value exists in package.json', () => { instantiationService.stub(IProductService, >{}); instantiationService.stub(IWorkspaceTrustEnablementService, new TestWorkspaceTrustEnablementService()); - const extensionMaifest = getExtensionManifest({ main: './out/extension.js' }); - assertUntrustedWorkspaceSupport(extensionMaifest, false); + const extensionManifest = getExtensionManifest({ main: './out/extension.js' }); + assertUntrustedWorkspaceSupport(extensionManifest, false); }); }); } From 9eec0fe8146ec8489916f64c71fb84f267bd91c4 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 29 Jun 2023 11:23:07 +0200 Subject: [PATCH 031/926] New `auto` split sizing does something weird (fix #186559) (#186609) --- .../browser/parts/editor/editorDropTarget.ts | 38 ++++++++++++------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorDropTarget.ts b/src/vs/workbench/browser/parts/editor/editorDropTarget.ts index fab30cd1c0c..73da73d5f3a 100644 --- a/src/vs/workbench/browser/parts/editor/editorDropTarget.ts +++ b/src/vs/workbench/browser/parts/editor/editorDropTarget.ts @@ -310,25 +310,37 @@ class DropOverlay extends Themable { const data = this.editorTransfer.getData(DraggedEditorIdentifier.prototype); if (Array.isArray(data)) { const draggedEditor = data[0].identifier; - const targetGroup = ensureTargetGroup(); const sourceGroup = this.accessor.getGroup(draggedEditor.groupId); if (sourceGroup) { - if (sourceGroup === targetGroup) { - return; + const copyEditor = this.isCopyOperation(event, draggedEditor); + let targetGroup: IEditorGroupView | undefined = undefined; + + // Optimization: if we move the last editor of an editor group + // and we are configured to close empty editor groups, we can + // rather move the entire editor group according to the direction + if (this.editorGroupService.partOptions.closeEmptyGroups && sourceGroup.count === 1 && typeof splitDirection === 'number' && !copyEditor) { + targetGroup = this.accessor.moveGroup(sourceGroup, this.groupView, splitDirection); } - // Open in target group - const options = fillActiveEditorViewState(sourceGroup, draggedEditor.editor, { - pinned: true, // always pin dropped editor - sticky: sourceGroup.isSticky(draggedEditor.editor), // preserve sticky state - }); + // In any other case do a normal move/copy operation + else { + targetGroup = ensureTargetGroup(); + if (sourceGroup === targetGroup) { + return; + } - const copyEditor = this.isCopyOperation(event, draggedEditor); - if (!copyEditor) { - sourceGroup.moveEditor(draggedEditor.editor, targetGroup, options); - } else { - sourceGroup.copyEditor(draggedEditor.editor, targetGroup, options); + // Open in target group + const options = fillActiveEditorViewState(sourceGroup, draggedEditor.editor, { + pinned: true, // always pin dropped editor + sticky: sourceGroup.isSticky(draggedEditor.editor), // preserve sticky state + }); + + if (!copyEditor) { + sourceGroup.moveEditor(draggedEditor.editor, targetGroup, options); + } else { + sourceGroup.copyEditor(draggedEditor.editor, targetGroup, options); + } } // Ensure target has focus From 9fe9cea876f85fff196ede8984b167567a3b816b Mon Sep 17 00:00:00 2001 From: Ulugbek Abdullaev Date: Thu, 29 Jun 2023 11:12:13 +0200 Subject: [PATCH 032/926] fix localization fixes #186317 --- .../contrib/chat/browser/actions/chatAccessibilityHelp.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatAccessibilityHelp.ts b/src/vs/workbench/contrib/chat/browser/actions/chatAccessibilityHelp.ts index 67d2dcc505c..983e6b6f86b 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatAccessibilityHelp.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatAccessibilityHelp.ts @@ -33,7 +33,7 @@ export function getAccessibilityHelpText(accessor: ServicesAccessor, type: 'pane const upHistoryKeybinding = keybindingService.lookupKeybinding('inlineChat.previousFromHistory')?.getAriaLabel(); const downHistoryKeybinding = keybindingService.lookupKeybinding('inlineChat.nextFromHistory')?.getAriaLabel(); if (upHistoryKeybinding && downHistoryKeybinding) { - content.push(localize('inlineChat.requestHistory', 'In the input box, use {0} and {1} to navigate your request history. Edit input and use enter or the submit button to run a new request.'), upHistoryKeybinding, downHistoryKeybinding); + content.push(localize('inlineChat.requestHistory', 'In the input box, use {0} and {1} to navigate your request history. Edit input and use enter or the submit button to run a new request.', upHistoryKeybinding, downHistoryKeybinding)); } content.push(localize('inlineChat.contextActions', "Context menu actions may run a request prefixed with /fix or /explain. Type / to discover more ready-made commands.")); content.push(localize('inlineChat.fix', "When a request is prefixed with /fix, a response will indicate the problem with the current code. A diff editor will be rendered and can be reached by tabbing.")); From c6567bcf69d8fb74e1d42018f29ccd3bc34fb516 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 29 Jun 2023 12:16:46 +0200 Subject: [PATCH 033/926] notifications - compute height properly when progress is enabled (#186612) --- .../browser/parts/notifications/notificationsList.ts | 2 +- .../browser/parts/notifications/notificationsToasts.ts | 8 ++++---- .../browser/parts/notifications/notificationsViewer.ts | 8 ++++++-- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/browser/parts/notifications/notificationsList.ts b/src/vs/workbench/browser/parts/notifications/notificationsList.ts index 57c31863c48..f54137e9371 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsList.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsList.ts @@ -21,7 +21,7 @@ import { AriaRole } from 'vs/base/browser/ui/aria/aria'; import { NotificationActionRunner } from 'vs/workbench/browser/parts/notifications/notificationsCommands'; export interface INotificationsListOptions extends IListOptions { - widgetAriaLabel?: string; + readonly widgetAriaLabel?: string; } export class NotificationsList extends Disposable { diff --git a/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts b/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts index 55c691f02a6..62d27bfc8ec 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts @@ -27,10 +27,10 @@ import { assertIsDefined } from 'vs/base/common/types'; import { NotificationsToastsVisibleContext } from 'vs/workbench/common/contextkeys'; interface INotificationToast { - item: INotificationViewItem; - list: NotificationsList; - container: HTMLElement; - toast: HTMLElement; + readonly item: INotificationViewItem; + readonly list: NotificationsList; + readonly container: HTMLElement; + readonly toast: HTMLElement; } enum ToastVisibility { diff --git a/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts b/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts index 03a4bb4f404..0be6eaf64c0 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts @@ -83,7 +83,10 @@ export class NotificationsListDelegate implements IListVirtualDelegate void; - toDispose: DisposableStore; } class NotificationMessageRenderer { From cf30025bd71c48b0ede8943f10cb96b7f6fd82fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Thu, 29 Jun 2023 15:18:02 +0200 Subject: [PATCH 034/926] Fix sash corners when moving views around (#186624) fixes #186508 --- src/vs/base/browser/ui/grid/gridview.ts | 80 ++++++++----------------- src/vs/base/browser/ui/sash/sash.ts | 8 +++ 2 files changed, 34 insertions(+), 54 deletions(-) diff --git a/src/vs/base/browser/ui/grid/gridview.ts b/src/vs/base/browser/ui/grid/gridview.ts index d24e1d26811..c89c6a7a063 100644 --- a/src/vs/base/browser/ui/grid/gridview.ts +++ b/src/vs/base/browser/ui/grid/gridview.ts @@ -356,6 +356,13 @@ class BranchNode implements ISplitView, IDisposable { private _boundarySashes: IRelativeBoundarySashes = {}; get boundarySashes(): IRelativeBoundarySashes { return this._boundarySashes; } set boundarySashes(boundarySashes: IRelativeBoundarySashes) { + if (this._boundarySashes.start === boundarySashes.start + && this._boundarySashes.end === boundarySashes.end + && this._boundarySashes.orthogonalStart === boundarySashes.orthogonalStart + && this._boundarySashes.orthogonalEnd === boundarySashes.orthogonalEnd) { + return; + } + this._boundarySashes = boundarySashes; this.splitview.orthogonalStartSash = boundarySashes.orthogonalStart; @@ -498,67 +505,22 @@ class BranchNode implements ISplitView, IDisposable { index = validateIndex(index, this.children.length); this.splitview.addView(node, size, index, skipLayout); - this._addChild(node, index); - this.onDidChildrenChange(); - } - - private _addChild(node: Node, index: number): void { - const first = index === 0; - const last = index === this.children.length; this.children.splice(index, 0, node); - node.boundarySashes = { - start: this.boundarySashes.orthogonalStart, - end: this.boundarySashes.orthogonalEnd, - orthogonalStart: first ? this.boundarySashes.start : this.splitview.sashes[index - 1], - orthogonalEnd: last ? this.boundarySashes.end : this.splitview.sashes[index], - }; - - if (!first) { - this.children[index - 1].boundarySashes = { - ...this.children[index - 1].boundarySashes, - orthogonalEnd: this.splitview.sashes[index - 1] - }; - } - - if (!last) { - this.children[index + 1].boundarySashes = { - ...this.children[index + 1].boundarySashes, - orthogonalStart: this.splitview.sashes[index] - }; - } + this.updateBoundarySashes(); + this.onDidChildrenChange(); } removeChild(index: number, sizing?: Sizing): void { index = validateIndex(index, this.children.length); this.splitview.removeView(index, sizing); - this._removeChild(index); + this.children.splice(index, 1); + + this.updateBoundarySashes(); this.onDidChildrenChange(); } - private _removeChild(index: number): Node { - const first = index === 0; - const last = index === this.children.length - 1; - const [child] = this.children.splice(index, 1); - - if (!first) { - this.children[index - 1].boundarySashes = { - ...this.children[index - 1].boundarySashes, - orthogonalEnd: this.splitview.sashes[index - 1] - }; - } - - if (!last) { // [0,1,2,3] (2) => [0,1,3] - this.children[index].boundarySashes = { - ...this.children[index].boundarySashes, - orthogonalStart: this.splitview.sashes[Math.max(index - 1, 0)] - }; - } - - return child; - } - moveChild(from: number, to: number): void { from = validateIndex(from, this.children.length); to = validateIndex(to, this.children.length); @@ -568,14 +530,13 @@ class BranchNode implements ISplitView, IDisposable { } if (from < to) { - to--; + to -= 1; } this.splitview.moveView(from, to); + this.children.splice(to, 0, this.children.splice(from, 1)[0]); - const child = this._removeChild(from); - this._addChild(child, to); - + this.updateBoundarySashes(); this.onDidChildrenChange(); } @@ -649,6 +610,17 @@ class BranchNode implements ISplitView, IDisposable { return this.splitview.getViewCachedVisibleSize(index); } + private updateBoundarySashes(): void { + for (let i = 0; i < this.children.length; i++) { + this.children[i].boundarySashes = { + start: this.boundarySashes.orthogonalStart, + end: this.boundarySashes.orthogonalEnd, + orthogonalStart: i === 0 ? this.boundarySashes.start : this.splitview.sashes[i - 1], + orthogonalEnd: i === this.children.length - 1 ? this.boundarySashes.end : this.splitview.sashes[i], + }; + } + } + private onDidChildrenChange(): void { this.updateChildrenEvents(); this._onDidChange.fire(undefined); diff --git a/src/vs/base/browser/ui/sash/sash.ts b/src/vs/base/browser/ui/sash/sash.ts index b20c2185169..b82b4508211 100644 --- a/src/vs/base/browser/ui/sash/sash.ts +++ b/src/vs/base/browser/ui/sash/sash.ts @@ -328,6 +328,10 @@ export class Sash extends Disposable { * The start of a vertical sash is its top-most position. */ set orthogonalStartSash(sash: Sash | undefined) { + if (this._orthogonalStartSash === sash) { + return; + } + this.orthogonalStartDragHandleDisposables.clear(); this.orthogonalStartSashDisposables.clear(); @@ -362,6 +366,10 @@ export class Sash extends Disposable { */ set orthogonalEndSash(sash: Sash | undefined) { + if (this._orthogonalEndSash === sash) { + return; + } + this.orthogonalEndDragHandleDisposables.clear(); this.orthogonalEndSashDisposables.clear(); From ed9fc30385e14699b5eb5d3289465bff5e5d1fe6 Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Thu, 29 Jun 2023 07:53:09 -0700 Subject: [PATCH 035/926] Avoid duplicate action registrations (#186582) * Avoid duplicate action registrations * Use a local variable * Fix syntax error --- .../contrib/share/browser/share.contribution.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/share/browser/share.contribution.ts b/src/vs/workbench/contrib/share/browser/share.contribution.ts index 8230ad5beb4..2eb7e5595d5 100644 --- a/src/vs/workbench/contrib/share/browser/share.contribution.ts +++ b/src/vs/workbench/contrib/share/browser/share.contribution.ts @@ -47,7 +47,7 @@ const targetMenus = [ class ShareWorkbenchContribution { private static SHARE_ENABLED_SETTING = 'workbench.experimental.share.enabled'; - private _disposables = new DisposableStore(); + private _disposables: DisposableStore | undefined; constructor( @IShareService private readonly shareService: IShareService, @@ -58,16 +58,22 @@ class ShareWorkbenchContribution { } this.configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration(ShareWorkbenchContribution.SHARE_ENABLED_SETTING)) { - if (this.configurationService.getValue(ShareWorkbenchContribution.SHARE_ENABLED_SETTING)) { + const settingValue = this.configurationService.getValue(ShareWorkbenchContribution.SHARE_ENABLED_SETTING); + if (settingValue === true && this._disposables === undefined) { this.registerActions(); - } else { - this._disposables.clear(); + } else if (settingValue === false && this._disposables !== undefined) { + this._disposables?.clear(); + this._disposables = undefined; } } }); } private registerActions() { + if (!this._disposables) { + this._disposables = new DisposableStore(); + } + this._disposables.add( registerAction2(class ShareAction extends Action2 { static readonly ID = 'workbench.action.share'; From 66375a636f474750b9b4349981ddb315e989041a Mon Sep 17 00:00:00 2001 From: meganrogge Date: Thu, 29 Jun 2023 09:14:41 -0700 Subject: [PATCH 036/926] fix #186434 --- .../accessibility/browser/accessibleView.ts | 18 ++++++++++++++++-- .../browser/accessibility/accessibility.ts | 7 +++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index bbe750e99af..e885f918af1 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -16,6 +16,7 @@ import { AccessibilityHelpNLS } from 'vs/editor/common/standaloneStrings'; import { LinkDetector } from 'vs/editor/contrib/links/browser/links'; import { localize } from 'vs/nls'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IContextViewDelegate, IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService, createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IOpenerService } from 'vs/platform/opener/common/opener'; @@ -56,20 +57,23 @@ export interface IAccessibleViewOptions { type: AccessibleViewType; } +export const accessibilityHelpIsShown = new RawContextKey('accessibilityHelpIsShown', false, true); class AccessibleView extends Disposable { private _editorWidget: CodeEditorWidget; + private _accessiblityHelpIsShown: IContextKey; get editorWidget() { return this._editorWidget; } private _editorContainer: HTMLElement; private _keyListener: IDisposable | undefined; - constructor( @IOpenerService private readonly _openerService: IOpenerService, @IInstantiationService private readonly _instantiationService: IInstantiationService, @IConfigurationService private readonly _configurationService: IConfigurationService, @IModelService private readonly _modelService: IModelService, - @IContextViewService private readonly _contextViewService: IContextViewService + @IContextViewService private readonly _contextViewService: IContextViewService, + @IContextKeyService private readonly _contextKeyService: IContextKeyService ) { super(); + this._accessiblityHelpIsShown = accessibilityHelpIsShown.bindTo(this._contextKeyService); this._editorContainer = document.createElement('div'); this._editorContainer.classList.add('accessible-view'); const codeEditorWidgetOptions: ICodeEditorWidgetOptions = { @@ -96,9 +100,17 @@ class AccessibleView extends Disposable { getAnchor: () => this._editorContainer, render: (container) => { return this._render(provider, container); + }, + onHide: () => { + if (provider.options.type === AccessibleViewType.HelpMenu) { + this._accessiblityHelpIsShown.reset(); + } } }; this._contextViewService.showContextView(delegate); + if (provider.options.type === AccessibleViewType.HelpMenu) { + this._accessiblityHelpIsShown.set(true); + } } private _render(provider: IAccessibleContentProvider, container: HTMLElement): IDisposable { @@ -133,6 +145,8 @@ class AccessibleView extends Disposable { const url: string = provider.options.readMoreUrl!; alert(AccessibilityHelpNLS.openingDocs); this._openerService.open(URI.parse(url)); + } else if (e.ctrlKey && e.keyCode === KeyCode.KeyM) { + } e.stopPropagation(); provider.onKeyDown?.(e); diff --git a/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.ts b/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.ts index 54a9b01c1e8..1e1f27e76d6 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.ts @@ -26,6 +26,8 @@ import { IOpenerService } from 'vs/platform/opener/common/opener'; import { AccessibilitySupport, IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; import { TabFocus, TabFocusContext } from 'vs/editor/browser/config/tabFocus'; +import { accessibilityHelpIsShown } from 'vs/workbench/contrib/accessibility/browser/accessibleView'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; const CONTEXT_ACCESSIBILITY_WIDGET_VISIBLE = new RawContextKey('accessibilityHelpWidgetVisible', false); @@ -279,6 +281,11 @@ class ToggleScreenReaderMode extends Action2 { id: 'editor.action.toggleScreenReaderAccessibilityMode', title: { value: nls.localize('toggleScreenReaderMode', "Toggle Screen Reader Accessibility Mode"), original: 'Toggle Screen Reader Accessibility Mode' }, f1: true, + keybinding: { + primary: KeyMod.CtrlCmd | KeyCode.KeyE, + weight: KeybindingWeight.WorkbenchContrib + 10, + when: accessibilityHelpIsShown + } }); } From e6ed074aab3480db5adc3d1710b80ba42bf87e30 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Thu, 29 Jun 2023 09:19:27 -0700 Subject: [PATCH 037/926] Update src/vs/workbench/contrib/accessibility/browser/accessibleView.ts --- .../workbench/contrib/accessibility/browser/accessibleView.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index e885f918af1..07b46bc5d3e 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -145,8 +145,6 @@ class AccessibleView extends Disposable { const url: string = provider.options.readMoreUrl!; alert(AccessibilityHelpNLS.openingDocs); this._openerService.open(URI.parse(url)); - } else if (e.ctrlKey && e.keyCode === KeyCode.KeyM) { - } e.stopPropagation(); provider.onKeyDown?.(e); From 2aa71dc9e0a57dff529fa3c08d3ba7b9989593c0 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Thu, 29 Jun 2023 09:52:25 -0700 Subject: [PATCH 038/926] fix #186352 --- .../accessibility/browser/accessibleView.ts | 53 +++++++++++-------- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index bbe750e99af..3b937a38a1b 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -5,7 +5,7 @@ import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; -import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { IEditorConstructionOptions } from 'vs/editor/browser/config/editorConfiguration'; import { EditorExtensionsRegistry } from 'vs/editor/browser/editorExtensions'; @@ -92,10 +92,15 @@ class AccessibleView extends Disposable { } show(provider: IAccessibleContentProvider): void { + let view: IDisposable | undefined; const delegate: IContextViewDelegate = { getAnchor: () => this._editorContainer, render: (container) => { - return this._render(provider, container); + view = this._render(provider, container); + return view; + }, + onHide: () => { + view?.dispose(); } }; this._contextViewService.showContextView(delegate); @@ -121,28 +126,34 @@ class AccessibleView extends Disposable { model.setLanguage(provider.options.language); } container.appendChild(this._editorContainer); - this._keyListener = this._register(this._editorWidget.onKeyUp((e) => { - if (e.keyCode === KeyCode.Escape) { - this._contextViewService.hideContextView(); - // Delay to allow the context view to hide #186514 - setTimeout(() => provider.onClose(), 100); - this._keyListener?.dispose(); - } else if (e.keyCode === KeyCode.KeyD && this._configurationService.getValue(settingKey)) { - this._configurationService.updateValue(settingKey, false); - } else if (e.keyCode === KeyCode.KeyH && provider.options.readMoreUrl) { - const url: string = provider.options.readMoreUrl!; - alert(AccessibilityHelpNLS.openingDocs); - this._openerService.open(URI.parse(url)); - } - e.stopPropagation(); - provider.onKeyDown?.(e); - })); - this._register(this._editorWidget.onDidBlurEditorText(() => this._contextViewService.hideContextView())); - this._register(this._editorWidget.onDidContentSizeChange(() => this._layout())); this._editorWidget.updateOptions({ ariaLabel: provider.options.ariaLabel }); this._editorWidget.focus(); }); - return toDisposable(() => { }); + const disposableStore = new DisposableStore(); + disposableStore.add(this._editorWidget.onKeyUp((e) => { + if (e.keyCode === KeyCode.Escape) { + this._contextViewService.hideContextView(); + // Delay to allow the context view to hide #186514 + setTimeout(() => provider.onClose(), 100); + this._keyListener?.dispose(); + } else if (e.keyCode === KeyCode.KeyD && this._configurationService.getValue(settingKey)) { + this._configurationService.updateValue(settingKey, false); + } + e.stopPropagation(); + provider.onKeyDown?.(e); + })); + disposableStore.add(this._editorWidget.onKeyDown((e) => { + if (e.keyCode === KeyCode.KeyH && provider.options.readMoreUrl) { + const url: string = provider.options.readMoreUrl!; + alert(AccessibilityHelpNLS.openingDocs); + this._openerService.open(URI.parse(url)); + e.preventDefault(); + e.stopPropagation(); + } + })); + disposableStore.add(this._editorWidget.onDidBlurEditorText(() => this._contextViewService.hideContextView())); + disposableStore.add(this._editorWidget.onDidContentSizeChange(() => this._layout())); + return toDisposable(() => { disposableStore.dispose(); }); } private _layout(): void { From 72f94506d4624736129e30438f4d660bc6f80738 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Thu, 29 Jun 2023 10:09:36 -0700 Subject: [PATCH 039/926] Update src/vs/workbench/contrib/accessibility/browser/accessibleView.ts Co-authored-by: Daniel Imms <2193314+Tyriar@users.noreply.github.com> --- .../workbench/contrib/accessibility/browser/accessibleView.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index c89ba5fda39..22ab3000dd0 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -163,7 +163,7 @@ class AccessibleView extends Disposable { })); disposableStore.add(this._editorWidget.onDidBlurEditorText(() => this._contextViewService.hideContextView())); disposableStore.add(this._editorWidget.onDidContentSizeChange(() => this._layout())); - return toDisposable(() => { disposableStore.dispose(); }); + return disposableStore; } private _layout(): void { From cfd8c45a8e58087b32d4ed0a41ce4a688c54b638 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Thu, 29 Jun 2023 10:11:23 -0700 Subject: [PATCH 040/926] don't explicitly dispose of view --- .../contrib/accessibility/browser/accessibleView.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index 22ab3000dd0..6f878bd8a81 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -96,18 +96,15 @@ class AccessibleView extends Disposable { } show(provider: IAccessibleContentProvider): void { - let view: IDisposable | undefined; const delegate: IContextViewDelegate = { getAnchor: () => this._editorContainer, render: (container) => { - view = this._render(provider, container); - return view; + return this._render(provider, container); }, onHide: () => { if (provider.options.type === AccessibleViewType.HelpMenu) { this._accessiblityHelpIsShown.reset(); } - view?.dispose(); } }; this._contextViewService.showContextView(delegate); @@ -163,7 +160,7 @@ class AccessibleView extends Disposable { })); disposableStore.add(this._editorWidget.onDidBlurEditorText(() => this._contextViewService.hideContextView())); disposableStore.add(this._editorWidget.onDidContentSizeChange(() => this._layout())); - return disposableStore; + return toDisposable(() => { disposableStore.dispose(); }); } private _layout(): void { From bfc73b6cfef9ba323f0ef520411ac718ab01000e Mon Sep 17 00:00:00 2001 From: meganrogge Date: Thu, 29 Jun 2023 10:18:08 -0700 Subject: [PATCH 041/926] rm unused var --- .../workbench/contrib/accessibility/browser/accessibleView.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index 6f878bd8a81..f10bfcb2d8d 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -63,7 +63,6 @@ class AccessibleView extends Disposable { private _accessiblityHelpIsShown: IContextKey; get editorWidget() { return this._editorWidget; } private _editorContainer: HTMLElement; - private _keyListener: IDisposable | undefined; constructor( @IOpenerService private readonly _openerService: IOpenerService, @IInstantiationService private readonly _instantiationService: IInstantiationService, @@ -142,7 +141,6 @@ class AccessibleView extends Disposable { this._contextViewService.hideContextView(); // Delay to allow the context view to hide #186514 setTimeout(() => provider.onClose(), 100); - this._keyListener?.dispose(); } else if (e.keyCode === KeyCode.KeyD && this._configurationService.getValue(settingKey)) { this._configurationService.updateValue(settingKey, false); } From 3a6c041afe55aa099c7e0f3decf7d10c5d060f50 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 29 Jun 2023 10:40:41 -0700 Subject: [PATCH 042/926] Add debug logs for reattaching to wrong terminal Part of #133542 --- src/vs/platform/terminal/node/ptyService.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/platform/terminal/node/ptyService.ts b/src/vs/platform/terminal/node/ptyService.ts index 260a641c91b..7fa15c80d40 100644 --- a/src/vs/platform/terminal/node/ptyService.ts +++ b/src/vs/platform/terminal/node/ptyService.ts @@ -240,6 +240,7 @@ export class PtyService extends Disposable implements IPtyService { ); // Don't start the process here as there's no terminal to answer CPR this._revivedPtyIdMap.set(terminal.id, { newId, state: terminal }); + this._logService.info(`Revived process, old id ${terminal.id} -> new id ${newId}`); } @traceRpc @@ -532,6 +533,7 @@ export class PtyService extends Disposable implements IPtyService { private async _expandTerminalInstance(t: ITerminalInstanceLayoutInfoById): Promise> { try { const revivedPtyId = this._revivedPtyIdMap.get(t.terminal)?.newId; + this._logService.info(`Expanding terminal instance, old id ${t.terminal} -> new id ${revivedPtyId}`); this._revivedPtyIdMap.delete(t.terminal); const persistentProcessId = revivedPtyId ?? t.terminal; const persistentProcess = this._throwIfNoPty(persistentProcessId); @@ -542,6 +544,8 @@ export class PtyService extends Disposable implements IPtyService { }; } catch (e) { this._logService.warn(`Couldn't get layout info, a terminal was probably disconnected`, e.message); + this._logService.info('Reattach to wrong terminal debug info - layout info by id', t); + this._logService.info('Reattach to wrong terminal debug info - _revivePtyIdMap', Array.from(this._revivedPtyIdMap.values())); // this will be filtered out and not reconnected return { terminal: null, From 1d607251f57711b0239095686db51c4d7c7687b6 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Thu, 29 Jun 2023 19:47:30 +0200 Subject: [PATCH 043/926] Fixes #186628 --- .../editor/common/cursor/cursorMoveOperations.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/vs/editor/common/cursor/cursorMoveOperations.ts b/src/vs/editor/common/cursor/cursorMoveOperations.ts index a57cc00fad5..5699917a4f4 100644 --- a/src/vs/editor/common/cursor/cursorMoveOperations.ts +++ b/src/vs/editor/common/cursor/cursorMoveOperations.ts @@ -3,13 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CursorConfiguration, ICursorSimpleModel, SelectionStartKind, SingleCursorState } from 'vs/editor/common/cursorCommon'; +import * as strings from 'vs/base/common/strings'; +import { Constants } from 'vs/base/common/uint'; import { CursorColumns } from 'vs/editor/common/core/cursorColumns'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; -import * as strings from 'vs/base/common/strings'; -import { Constants } from 'vs/base/common/uint'; import { AtomicTabMoveOperations, Direction } from 'vs/editor/common/cursor/cursorAtomicMoveOperations'; +import { CursorConfiguration, ICursorSimpleModel, SelectionStartKind, SingleCursorState } from 'vs/editor/common/cursorCommon'; import { PositionAffinity } from 'vs/editor/common/model'; export class CursorPosition { @@ -213,7 +213,15 @@ export class MoveOperations { column = cursor.position.column; } - const r = MoveOperations.down(config, model, lineNumber, column, cursor.leftoverVisibleColumns, linesCount, true); + let i = 0; + let r: CursorPosition; + do { + r = MoveOperations.down(config, model, lineNumber + i, column, cursor.leftoverVisibleColumns, linesCount, true); + const np = model.normalizePosition(new Position(r.lineNumber, r.column), PositionAffinity.None); + if (np.lineNumber > lineNumber) { + break; + } + } while (i++ < 10 && lineNumber + i < model.getLineCount()); return cursor.move(inSelectionMode, r.lineNumber, r.column, r.leftoverVisibleColumns); } From 1e4479db85bd2f8d7876f8c6852e08f31ebaedfd Mon Sep 17 00:00:00 2001 From: meganrogge Date: Thu, 29 Jun 2023 11:11:43 -0700 Subject: [PATCH 044/926] redo change --- .../workbench/contrib/accessibility/browser/accessibleView.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index f10bfcb2d8d..a84bf2907c4 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -158,7 +158,7 @@ class AccessibleView extends Disposable { })); disposableStore.add(this._editorWidget.onDidBlurEditorText(() => this._contextViewService.hideContextView())); disposableStore.add(this._editorWidget.onDidContentSizeChange(() => this._layout())); - return toDisposable(() => { disposableStore.dispose(); }); + return disposableStore; } private _layout(): void { From 1532010d8ec7f765cdaedc64efba634a09b5866b Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Thu, 29 Jun 2023 11:35:18 -0700 Subject: [PATCH 045/926] Update the triage flow (#186656) --- .github/workflows/deep-classifier-runner.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deep-classifier-runner.yml b/.github/workflows/deep-classifier-runner.yml index c951476c083..2d90770bd25 100644 --- a/.github/workflows/deep-classifier-runner.yml +++ b/.github/workflows/deep-classifier-runner.yml @@ -47,6 +47,8 @@ jobs: with: configPath: classifier allowLabels: "info-needed|new release|error-telemetry|*english-please|translation-required" - appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} - manifestDbConnectionString: ${{secrets.MANIFEST_DB_CONNECTION_STRING}} + tenantId: ${{secrets.TOOLS_TENANT_ID}} + clientId: ${{secrets.TOOLS_CLIENT_ID}} + clientSecret: ${{secrets.TOOLS_CLIENT_SECRET}} + clientScope: ${{secrets.TOOLS_CLIENT_SCOPE}} token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}} From a3231fc857c16cfca2674dfadedc71454f3a9566 Mon Sep 17 00:00:00 2001 From: David Dossett Date: Thu, 29 Jun 2023 12:49:50 -0700 Subject: [PATCH 046/926] Update inlay hint colors (#186658) * Update inlay hints colors * Update new themes --- extensions/theme-defaults/themes/dark_modern.json | 6 ++---- extensions/theme-defaults/themes/light_modern.json | 6 ++---- src/vs/platform/theme/common/colorRegistry.ts | 4 ++-- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/extensions/theme-defaults/themes/dark_modern.json b/extensions/theme-defaults/themes/dark_modern.json index 772b5993aa9..14fc09e91c1 100644 --- a/extensions/theme-defaults/themes/dark_modern.json +++ b/extensions/theme-defaults/themes/dark_modern.json @@ -40,10 +40,8 @@ "editorGutter.addedBackground": "#2ea043", "editorGutter.deletedBackground": "#f85149", "editorGutter.modifiedBackground": "#0078d4", - "editorInlayHint.background": "#8b949e33", - "editorInlayHint.foreground": "#8b949e", - "editorInlayHint.typeBackground": "#8b949e33", - "editorInlayHint.typeForeground": "#8b949e", + "editorInlayHint.background": "#8b949e1b", + "editorInlayHint.typeBackground": "#8b949e1b", "editorLineNumber.activeForeground": "#cccccc", "editorLineNumber.foreground": "#6e7681", "editorOverviewRuler.border": "#010409", diff --git a/extensions/theme-defaults/themes/light_modern.json b/extensions/theme-defaults/themes/light_modern.json index 8ca74d4e02f..5640a255dc1 100644 --- a/extensions/theme-defaults/themes/light_modern.json +++ b/extensions/theme-defaults/themes/light_modern.json @@ -41,10 +41,8 @@ "editorGutter.deletedBackground": "#f85149", "editorGutter.modifiedBackground": "#005FB8", "editorIndentGuide.background": "#D3D3D3", - "editorInlayHint.background": "#8b949e33", - "editorInlayHint.foreground": "#8b949e", - "editorInlayHint.typeBackground": "#8b949e33", - "editorInlayHint.typeForeground": "#8b949e", + "editorInlayHint.background": "#8b949e1b", + "editorInlayHint.typeBackground": "#8b949e1b", "editorLineNumber.activeForeground": "#171184", "editorLineNumber.foreground": "#6e7681", "editorOverviewRuler.border": "#0000001a", diff --git a/src/vs/platform/theme/common/colorRegistry.ts b/src/vs/platform/theme/common/colorRegistry.ts index f4cce8875a7..32cf1e5cc0d 100644 --- a/src/vs/platform/theme/common/colorRegistry.ts +++ b/src/vs/platform/theme/common/colorRegistry.ts @@ -387,8 +387,8 @@ export const editorActiveLinkForeground = registerColor('editorLink.activeForegr /** * Inline hints */ -export const editorInlayHintForeground = registerColor('editorInlayHint.foreground', { dark: badgeForeground, light: badgeForeground, hcDark: Color.black, hcLight: badgeForeground }, nls.localize('editorInlayHintForeground', 'Foreground color of inline hints')); -export const editorInlayHintBackground = registerColor('editorInlayHint.background', { dark: transparent(badgeBackground, .8), light: transparent(badgeBackground, .6), hcDark: '#f38518', hcLight: badgeBackground }, nls.localize('editorInlayHintBackground', 'Background color of inline hints')); +export const editorInlayHintForeground = registerColor('editorInlayHint.foreground', { dark: foreground, light: foreground, hcDark: Color.black, hcLight: badgeForeground }, nls.localize('editorInlayHintForeground', 'Foreground color of inline hints')); +export const editorInlayHintBackground = registerColor('editorInlayHint.background', { dark: transparent(badgeBackground, .25), light: transparent(badgeBackground, .3), hcDark: '#f38518', hcLight: badgeBackground }, nls.localize('editorInlayHintBackground', 'Background color of inline hints')); export const editorInlayHintTypeForeground = registerColor('editorInlayHint.typeForeground', { dark: editorInlayHintForeground, light: editorInlayHintForeground, hcDark: editorInlayHintForeground, hcLight: editorInlayHintForeground }, nls.localize('editorInlayHintForegroundTypes', 'Foreground color of inline hints for types')); export const editorInlayHintTypeBackground = registerColor('editorInlayHint.typeBackground', { dark: editorInlayHintBackground, light: editorInlayHintBackground, hcDark: editorInlayHintBackground, hcLight: editorInlayHintBackground }, nls.localize('editorInlayHintBackgroundTypes', 'Background color of inline hints for types')); export const editorInlayHintParameterForeground = registerColor('editorInlayHint.parameterForeground', { dark: editorInlayHintForeground, light: editorInlayHintForeground, hcDark: editorInlayHintForeground, hcLight: editorInlayHintForeground }, nls.localize('editorInlayHintForegroundParameter', 'Foreground color of inline hints for parameters')); From ebc70d445fd7fc77e84de200efebf2147170db91 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 29 Jun 2023 12:50:22 -0700 Subject: [PATCH 047/926] Fix regex and whole word terminal toggles Fixes #186375 --- .../find/browser/terminal.find.contribution.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/find/browser/terminal.find.contribution.ts b/src/vs/workbench/contrib/terminalContrib/find/browser/terminal.find.contribution.ts index 447dd9a0a81..5b9410b554e 100644 --- a/src/vs/workbench/contrib/terminalContrib/find/browser/terminal.find.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/find/browser/terminal.find.contribution.ts @@ -121,7 +121,7 @@ registerActiveInstanceAction({ precondition: ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), run: (activeInstance) => { const state = TerminalFindContribution.get(activeInstance)?.findWidget.state; - state?.change({ matchCase: !state.isRegex }, false); + state?.change({ isRegex: !state.isRegex }, false); } }); @@ -137,7 +137,7 @@ registerActiveInstanceAction({ precondition: ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), run: (activeInstance) => { const state = TerminalFindContribution.get(activeInstance)?.findWidget.state; - state?.change({ matchCase: !state.wholeWord }, false); + state?.change({ wholeWord: !state.wholeWord }, false); } }); From 661fbc9ecfffb930f7aba408749a193eaa4038bc Mon Sep 17 00:00:00 2001 From: Josh Abernathy Date: Thu, 29 Jun 2023 16:06:41 -0400 Subject: [PATCH 048/926] Fix removing tunnels when the tunnel factory throws an error (#186566) * Await the tunnel promise * Add null check * nit: use undefined --------- Co-authored-by: Alex Ross --- src/vs/platform/tunnel/common/tunnel.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/tunnel/common/tunnel.ts b/src/vs/platform/tunnel/common/tunnel.ts index 6e362a7be5c..8f46ec11b60 100644 --- a/src/vs/platform/tunnel/common/tunnel.ts +++ b/src/vs/platform/tunnel/common/tunnel.ts @@ -416,7 +416,7 @@ export abstract class AbstractTunnelService implements ITunnelService { const hostMap = this._tunnels.get(remoteHost); if (hostMap) { const tunnel = hostMap.get(remotePort); - const tunnelResult = await tunnel; + const tunnelResult = tunnel ? await tunnel.value : undefined; if (!tunnelResult) { hostMap.delete(remotePort); } From bb5c4d58af9350d3803e8a07a1a69db74eb380e7 Mon Sep 17 00:00:00 2001 From: Meghan Kulkarni Date: Thu, 29 Jun 2023 14:10:39 -0700 Subject: [PATCH 049/926] Fix: use default paste when only text is pasted (#186649) * bug fixes * added label to copyPaste.ts * added localized label to copyPasteLinks file * quick fix for pasting highlight bug * concise if-statement --- .../contrib/dropOrPasteInto/browser/copyPasteController.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/vs/editor/contrib/dropOrPasteInto/browser/copyPasteController.ts b/src/vs/editor/contrib/dropOrPasteInto/browser/copyPasteController.ts index 24bffebd3ca..d4205b9fd04 100644 --- a/src/vs/editor/contrib/dropOrPasteInto/browser/copyPasteController.ts +++ b/src/vs/editor/contrib/dropOrPasteInto/browser/copyPasteController.ts @@ -282,6 +282,12 @@ export class CopyPasteController extends Disposable implements IEditorContributi return; } + // If the only edit returned is a text edit, use the default paste handler + if (providerEdits.length === 1 && providerEdits[0].id === 'text') { + await this.applyDefaultPasteHandler(dataTransfer, metadata, tokenSource.token); + return; + } + if (providerEdits.length) { const canShowWidget = editor.getOption(EditorOption.pasteAs).showPasteSelector === 'afterPaste'; return this._postPasteWidgetManager.applyEditAndShowIfNeeded(selections, { activeEditIndex: 0, allEdits: providerEdits }, canShowWidget, tokenSource.token); From d209ad82d3904e31ed81d50730d2e745716b28ee Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 29 Jun 2023 14:21:47 -0700 Subject: [PATCH 050/926] Parallelize fetching each terms revive env Part of #186671 --- .../terminal/electron-sandbox/localTerminalBackend.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts b/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts index 3ac98aa9599..f93002b2db6 100644 --- a/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts +++ b/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts @@ -279,10 +279,12 @@ class LocalTerminalBackend extends BaseTerminalBackend implements ITerminalBacke // Re-resolve the environments and replace it on the state so local terminals use a fresh // environment mark('code/terminal/willGetReviveEnvironments'); - for (const state of parsed) { - const freshEnv = await this._resolveEnvironmentForRevive(variableResolver, state.shellLaunchConfig); - state.processLaunchConfig.env = freshEnv; - } + await Promise.all(parsed.map(state => new Promise(r => { + this._resolveEnvironmentForRevive(variableResolver, state.shellLaunchConfig).then(freshEnv => { + state.processLaunchConfig.env = freshEnv; + r(); + }); + }))); mark('code/terminal/didGetReviveEnvironments'); mark('code/terminal/willReviveTerminalProcesses'); From 5827187df1c6dfd2c3f77251f87875e94a2185eb Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Thu, 29 Jun 2023 14:59:20 -0700 Subject: [PATCH 051/926] Add Share provider usage telemetry (#186677) --- .../contrib/share/browser/shareService.ts | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/share/browser/shareService.ts b/src/vs/workbench/contrib/share/browser/shareService.ts index 201c31bf844..fd5d93cd25e 100644 --- a/src/vs/workbench/contrib/share/browser/shareService.ts +++ b/src/vs/workbench/contrib/share/browser/shareService.ts @@ -13,10 +13,19 @@ import { ISubmenuItem } from 'vs/platform/actions/common/actions'; import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { ILabelService } from 'vs/platform/label/common/label'; import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IShareProvider, IShareService, IShareableItem } from 'vs/workbench/contrib/share/common/share'; export const ShareProviderCountContext = new RawContextKey('shareProviderCount', 0, localize('shareProviderCount', "The number of available share providers")); +type ShareEvent = { + providerId: string; +}; +type ShareClassification = { + owner: 'joyceerhl'; comment: 'Reporting which share provider is invoked.'; + providerId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The id of the selected share provider.' }; +}; + export class ShareService implements IShareService { readonly _serviceBrand: undefined; @@ -28,6 +37,7 @@ export class ShareService implements IShareService { @ILabelService private readonly labelService: ILabelService, @IQuickInputService private quickInputService: IQuickInputService, @ICodeEditorService private readonly codeEditorService: ICodeEditorService, + @ITelemetryService private readonly telemetryService: ITelemetryService, ) { this.providerCount = ShareProviderCountContext.bindTo(this.contextKeyService); } @@ -59,11 +69,18 @@ export class ShareService implements IShareService { } if (providers.length === 1) { + this.telemetryService.publicLog2('shareService.share', { providerId: providers[0].id }); return providers[0].provideShare(item, token); } const items: (IQuickPickItem & { provider: IShareProvider })[] = providers.map((p) => ({ label: p.label, provider: p })); const selected = await this.quickInputService.pick(items, { canPickMany: false, placeHolder: localize('type to filter', 'Choose how to share {0}', this.labelService.getUriLabel(item.resourceUri)) }, token); - return selected?.provider.provideShare(item, token); + + if (selected !== undefined) { + this.telemetryService.publicLog2('shareService.share', { providerId: selected.provider.id }); + return selected.provider.provideShare(item, token); + } + + return; } } From 9d3af9636076673d77cb2e8d0339b621fe26209e Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 29 Jun 2023 15:30:57 -0700 Subject: [PATCH 052/926] Use mp for get layout info, memoize and eager fetch env --- .../terminal/electron-sandbox/localTerminalBackend.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts b/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts index f93002b2db6..4f695fde550 100644 --- a/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts +++ b/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts @@ -87,6 +87,9 @@ class LocalTerminalBackend extends BaseTerminalBackend implements ITerminalBacke this._proxy = ProxyChannel.toService(getDelayedChannel(clientEventually.p.then(client => client.getChannel(TerminalIpcChannels.PtyHostWindow)))); this._connectToDirectProxy(clientEventually); })); + + // Eagerly fetch the backend's environment for memoization + this.getEnvironment(); } private async _connectToDirectProxy(clientEventually: DeferredPromise): Promise { @@ -242,6 +245,7 @@ class LocalTerminalBackend extends BaseTerminalBackend implements ITerminalBacke return this._proxy.getEnvironment(); } + @memoize async getShellEnvironment(): Promise { return this._shellEnvironmentService.getShellEnv(); } @@ -288,7 +292,7 @@ class LocalTerminalBackend extends BaseTerminalBackend implements ITerminalBacke mark('code/terminal/didGetReviveEnvironments'); mark('code/terminal/willReviveTerminalProcesses'); - await this._localPtyService.reviveTerminalProcesses(parsed, Intl.DateTimeFormat().resolvedOptions().locale); + await this._proxy.reviveTerminalProcesses(parsed, Intl.DateTimeFormat().resolvedOptions().locale); mark('code/terminal/didReviveTerminalProcesses'); this._storageService.remove(TerminalStorageKeys.TerminalBufferState, StorageScope.WORKSPACE); // If reviving processes, send the terminal layout info back to the pty host as it @@ -296,7 +300,7 @@ class LocalTerminalBackend extends BaseTerminalBackend implements ITerminalBacke const layoutInfo = this._storageService.get(TerminalStorageKeys.TerminalLayoutInfo, StorageScope.WORKSPACE); if (layoutInfo) { mark('code/terminal/willSetTerminalLayoutInfo'); - await this._localPtyService.setTerminalLayoutInfo(JSON.parse(layoutInfo)); + await this._proxy.setTerminalLayoutInfo(JSON.parse(layoutInfo)); mark('code/terminal/didSetTerminalLayoutInfo'); this._storageService.remove(TerminalStorageKeys.TerminalLayoutInfo, StorageScope.WORKSPACE); } @@ -305,7 +309,7 @@ class LocalTerminalBackend extends BaseTerminalBackend implements ITerminalBacke } } - return this._localPtyService.getTerminalLayoutInfo(layoutArgs); + return this._proxy.getTerminalLayoutInfo(layoutArgs); } private async _resolveEnvironmentForRevive(variableResolver: terminalEnvironment.VariableResolver | undefined, shellLaunchConfig: IShellLaunchConfig): Promise { From 885697b32c9f703edf846273680f63c319a4427d Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 29 Jun 2023 15:55:43 -0700 Subject: [PATCH 053/926] Mark backends connected in parallel --- .../workbench/contrib/terminal/browser/terminalService.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index 34b62b8fb2b..fe0b350aff9 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -298,12 +298,12 @@ export class TerminalService implements ITerminalService { const instances = await this._reconnectedTerminalGroups?.then(groups => groups.map(e => e.terminalInstances).flat()) ?? []; await Promise.all(instances.map(e => new Promise(r => Event.once(e.onProcessReplayComplete)(r)))); mark('code/terminal/didReplay'); - for (const backend of this._terminalInstanceService.getRegisteredBackends()) { - mark('code/terminal/willGetPerformanceMarks'); + mark('code/terminal/willGetPerformanceMarks'); + await Promise.all(Array.from(this._terminalInstanceService.getRegisteredBackends()).map(async backend => { this._timerService.setPerformanceMarks(backend.remoteAuthority === undefined ? 'localPtyHost' : 'remotePtyHost', await backend.getPerformanceMarks()); - mark('code/terminal/didGetPerformanceMarks'); backend.setConnected(); - } + })); + mark('code/terminal/didGetPerformanceMarks'); this._whenConnected.complete(); }); From 9b710048023e546fbdfe9ef34dbfebf10fd71d27 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 29 Jun 2023 16:32:22 -0700 Subject: [PATCH 054/926] Validate local links that end with . Fixes #186661 --- .../terminalContrib/links/browser/terminalLocalLinkDetector.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLocalLinkDetector.ts b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLocalLinkDetector.ts index 36544760691..df3f60959c1 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLocalLinkDetector.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLocalLinkDetector.ts @@ -134,7 +134,7 @@ export class TerminalLocalLinkDetector implements ITerminalLinkDetector { // If any candidates end with special characters that are likely to not be part of the // link, add a candidate excluding them. - const specialEndCharRegex = /[\[\]"']$/; + const specialEndCharRegex = /[\[\]"'\.]$/; const trimRangeMap: Map = new Map(); const specialEndLinkCandidates: string[] = []; for (const candidate of linkCandidates) { From 7406a9bdfca18f82a3977ba5eac908d41df7ccac Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Thu, 29 Jun 2023 17:38:33 -0700 Subject: [PATCH 055/926] Show block cursor after response and before followups appear (#186684) --- .../contrib/chat/browser/chatListRenderer.ts | 21 ++++++------------ .../contrib/chat/common/chatModel.ts | 22 ++++++++++++++----- .../contrib/chat/common/chatServiceImpl.ts | 8 +++++-- 3 files changed, 30 insertions(+), 21 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts index 554e94409bb..bbac658da6f 100644 --- a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts @@ -350,15 +350,10 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer { model.setFollowups(request, withNullAsUndefined(followups)); + model.completeResponse(request); }); + } else { + model.completeResponse(request); } } }); @@ -579,7 +583,7 @@ export class ChatService extends Disposable implements IChatService { model.acceptResponseProgress(request, { content: response.message, }, true); - model.completeResponse(request, { + model.setResponse(request, { session: model.session!, errorDetails: response.errorDetails, }); From 6c4306859d336a7747548b41de2e8c2d2ba06343 Mon Sep 17 00:00:00 2001 From: personal Date: Thu, 29 Jun 2023 22:45:48 -0400 Subject: [PATCH 056/926] chore: removed unused accessibility widget and controller --- .../browser/accessibility/accessibility.ts | 266 +----------------- .../notebook/browser/diff/diffComponents.ts | 2 - 2 files changed, 2 insertions(+), 266 deletions(-) diff --git a/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.ts b/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.ts index 54a9b01c1e8..334aa4e9017 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.ts @@ -5,272 +5,10 @@ import 'vs/css!./accessibility'; import * as nls from 'vs/nls'; -import { $, append, addStandardDisposableListener, clearNode } from 'vs/base/browser/dom'; -import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; -import { alert } from 'vs/base/browser/ui/aria/aria'; -import { Widget } from 'vs/base/browser/ui/widget'; -import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import { Disposable } from 'vs/base/common/lifecycle'; -import * as platform from 'vs/base/common/platform'; -import * as strings from 'vs/base/common/strings'; -import { URI } from 'vs/base/common/uri'; -import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition } from 'vs/editor/browser/editorBrowser'; -import { IEditorOptions, EditorOption } from 'vs/editor/common/config/editorOptions'; -import { IEditorContribution } from 'vs/editor/common/editorCommon'; -import { ToggleTabFocusModeAction } from 'vs/editor/contrib/toggleTabFocusMode/browser/toggleTabFocusMode'; import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { AccessibilitySupport, IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; -import { TabFocus, TabFocusContext } from 'vs/editor/browser/config/tabFocus'; - -const CONTEXT_ACCESSIBILITY_WIDGET_VISIBLE = new RawContextKey('accessibilityHelpWidgetVisible', false); - -export class AccessibilityHelpController extends Disposable implements IEditorContribution { - - public static readonly ID = 'editor.contrib.accessibilityHelpController'; - - public static get(editor: ICodeEditor): AccessibilityHelpController | null { - return editor.getContribution(AccessibilityHelpController.ID); - } - - private _editor: ICodeEditor; - private _widget?: AccessibilityHelpWidget; - - constructor( - editor: ICodeEditor, - @IInstantiationService private readonly instantiationService: IInstantiationService - ) { - super(); - - this._editor = editor; - } - - public show(): void { - if (!this._widget) { - this._widget = this._register(this.instantiationService.createInstance(AccessibilityHelpWidget, this._editor)); - } - this._widget.show(); - } - - public hide(): void { - this._widget?.hide(); - } -} - -class AccessibilityHelpWidget extends Widget implements IOverlayWidget { - - private static readonly ID = 'editor.contrib.accessibilityHelpWidget'; - private static readonly WIDTH = 500; - private static readonly HEIGHT = 320; - - private _editor: ICodeEditor; - private _domNode: FastDomNode; - private _contentDomNode: FastDomNode; - private _isVisible: boolean; - private _isVisibleKey: IContextKey; - - constructor( - editor: ICodeEditor, - @IContextKeyService private readonly _contextKeyService: IContextKeyService, - @IKeybindingService private readonly _keybindingService: IKeybindingService, - @IConfigurationService private readonly _configurationService: IConfigurationService, - @IOpenerService private readonly _openerService: IOpenerService - ) { - super(); - - this._editor = editor; - this._isVisibleKey = CONTEXT_ACCESSIBILITY_WIDGET_VISIBLE.bindTo(this._contextKeyService); - - this._domNode = createFastDomNode(document.createElement('div')); - this._domNode.setClassName('accessibilityHelpWidget'); - this._domNode.setWidth(AccessibilityHelpWidget.WIDTH); - this._domNode.setHeight(AccessibilityHelpWidget.HEIGHT); - this._domNode.setDisplay('none'); - this._domNode.setAttribute('role', 'dialog'); - this._domNode.setAttribute('aria-modal', 'true'); - this._domNode.setAttribute('aria-hidden', 'true'); - - const heading = append(this._domNode.domNode, $('h1', undefined, nls.localize('accessibilityHelpTitle', "Accessibility Help"))); - heading.id = 'help-dialog-heading'; - this._domNode.setAttribute('aria-labelledby', heading.id); - - this._contentDomNode = createFastDomNode(document.createElement('div')); - this._contentDomNode.setAttribute('role', 'document'); - this._contentDomNode.domNode.id = 'help-dialog-content'; - this._domNode.appendChild(this._contentDomNode); - this._domNode.setAttribute('aria-describedby', this._contentDomNode.domNode.id); - - this._isVisible = false; - - this._register(this._editor.onDidLayoutChange(() => { - if (this._isVisible) { - this._layout(); - } - })); - - // Intentionally not configurable! - this._register(addStandardDisposableListener(this._contentDomNode.domNode, 'keydown', (e) => { - if (!this._isVisible) { - return; - } - - if (e.equals(KeyMod.CtrlCmd | KeyCode.KeyE)) { - alert(nls.localize('emergencyConfOn', "Now changing the setting `editor.accessibilitySupport` to 'on'.")); - - this._configurationService.updateValue('editor.accessibilitySupport', 'on', ConfigurationTarget.USER); - - e.preventDefault(); - e.stopPropagation(); - } - - if (e.equals(KeyMod.CtrlCmd | KeyCode.KeyH)) { - alert(nls.localize('openingDocs', "Now opening the VS Code Accessibility documentation page.")); - - this._openerService.open(URI.parse('https://go.microsoft.com/fwlink/?linkid=851010')); - - e.preventDefault(); - e.stopPropagation(); - } - })); - - this.onblur(this._contentDomNode.domNode, () => { - this.hide(); - }); - - this._editor.addOverlayWidget(this); - } - - public override dispose(): void { - this._editor.removeOverlayWidget(this); - super.dispose(); - } - - public getId(): string { - return AccessibilityHelpWidget.ID; - } - - public getDomNode(): HTMLElement { - return this._domNode.domNode; - } - - public getPosition(): IOverlayWidgetPosition { - return { - preference: null - }; - } - - public show(): void { - if (this._isVisible) { - return; - } - this._isVisible = true; - this._isVisibleKey.set(true); - this._layout(); - this._domNode.setDisplay('block'); - this._domNode.setAttribute('aria-hidden', 'false'); - this._contentDomNode.domNode.tabIndex = 0; - this._buildContent(); - this._contentDomNode.domNode.focus(); - } - - private _descriptionForCommand(commandId: string, msg: string, noKbMsg: string): string { - const kb = this._keybindingService.lookupKeybinding(commandId); - if (kb) { - return strings.format(msg, kb.getAriaLabel()); - } - return strings.format(noKbMsg, commandId); - } - - private _buildContent() { - const contentDomNode = this._contentDomNode.domNode; - const options = this._editor.getOptions(); - - append(contentDomNode, $('p', undefined, nls.localize('introMsg', "Thank you for trying out VS Code's accessibility options."))); - append(contentDomNode, $('p', undefined, nls.localize('status', "Status:"))); - - const configuredValue = this._configurationService.getValue('editor').accessibilitySupport; - const actualValue = options.get(EditorOption.accessibilitySupport); - - const emergencyTurnOnMessage = ( - platform.isMacintosh - ? nls.localize('changeConfigToOnMac', "To configure the editor to be permanently optimized for usage with a Screen Reader press Command+E now.") - : nls.localize('changeConfigToOnWinLinux', "To configure the editor to be permanently optimized for usage with a Screen Reader press Control+E now.") - ); - - const instructions = append(contentDomNode, $('ul')); - switch (configuredValue) { - case 'auto': - switch (actualValue) { - case AccessibilitySupport.Unknown: - // Should never happen in VS Code - append(instructions, $('li', undefined, nls.localize('auto_unknown', "The editor is configured to use platform APIs to detect when a Screen Reader is attached, but the current runtime does not support this."))); - break; - case AccessibilitySupport.Enabled: - append(instructions, $('li', undefined, nls.localize('auto_on', "The editor has automatically detected a Screen Reader is attached."))); - break; - case AccessibilitySupport.Disabled: - append(instructions, $('li', undefined, nls.localize('auto_off', "The editor is configured to automatically detect when a Screen Reader is attached, which is not the case at this time."), ' ' + emergencyTurnOnMessage)); - break; - } - break; - case 'on': - append(instructions, $('li', undefined, nls.localize('configuredOn', "The editor is configured to be permanently optimized for usage with a Screen Reader - you can change this via the command `Toggle Screen Reader Accessibility Mode` or by editing the setting `editor.accessibilitySupport`"))); - break; - case 'off': - append(instructions, $('li', undefined, nls.localize('configuredOff', "The editor is configured to never be optimized for usage with a Screen Reader.", ' ' + emergencyTurnOnMessage))); - break; - } - - const NLS_TAB_FOCUS_MODE_ON = nls.localize('tabFocusModeOnMsg', "Pressing Tab in the current editor will move focus to the next focusable element. Toggle this behavior by pressing {0}."); - const NLS_TAB_FOCUS_MODE_ON_NO_KB = nls.localize('tabFocusModeOnMsgNoKb', "Pressing Tab in the current editor will move focus to the next focusable element. The command {0} is currently not triggerable by a keybinding."); - const NLS_TAB_FOCUS_MODE_OFF = nls.localize('tabFocusModeOffMsg', "Pressing Tab in the current editor will insert the tab character. Toggle this behavior by pressing {0}."); - const NLS_TAB_FOCUS_MODE_OFF_NO_KB = nls.localize('tabFocusModeOffMsgNoKb', "Pressing Tab in the current editor will insert the tab character. The command {0} is currently not triggerable by a keybinding."); - - if (TabFocus.getTabFocusMode(TabFocusContext.Editor)) { - append(instructions, $('li', undefined, this._descriptionForCommand(ToggleTabFocusModeAction.ID, NLS_TAB_FOCUS_MODE_ON, NLS_TAB_FOCUS_MODE_ON_NO_KB))); - } else { - append(instructions, $('li', undefined, this._descriptionForCommand(ToggleTabFocusModeAction.ID, NLS_TAB_FOCUS_MODE_OFF, NLS_TAB_FOCUS_MODE_OFF_NO_KB))); - } - - append(contentDomNode, ( - platform.isMacintosh - ? nls.localize('openDocMac', "Press Command+H now to open a browser window with more VS Code information related to Accessibility.") - : nls.localize('openDocWinLinux', "Press Control+H now to open a browser window with more VS Code information related to Accessibility.") - )); - - append(contentDomNode, $('p', undefined, nls.localize('outroMsg', "You can dismiss this tooltip and return to the editor by pressing Escape or Shift+Escape."))); - } - - public hide(): void { - if (!this._isVisible) { - return; - } - this._isVisible = false; - this._isVisibleKey.reset(); - this._domNode.setDisplay('none'); - this._domNode.setAttribute('aria-hidden', 'true'); - this._contentDomNode.domNode.tabIndex = -1; - clearNode(this._contentDomNode.domNode); - - this._editor.focus(); - } - - private _layout(): void { - const editorLayout = this._editor.getLayoutInfo(); - - const width = Math.min(editorLayout.width - 40, AccessibilityHelpWidget.WIDTH); - const height = Math.min(editorLayout.height - 40, AccessibilityHelpWidget.HEIGHT); - - this._domNode.setTop(Math.round((editorLayout.height - height) / 2)); - this._domNode.setLeft(Math.round((editorLayout.width - width) / 2)); - this._domNode.setWidth(width); - this._domNode.setHeight(height); - } -} class ToggleScreenReaderMode extends Action2 { diff --git a/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts index 0bf60aef63b..afcefd23bb6 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts @@ -29,7 +29,6 @@ import { EditorExtensionsRegistry } from 'vs/editor/browser/editorExtensions'; import { ContextMenuController } from 'vs/editor/contrib/contextmenu/browser/contextmenu'; import { SnippetController2 } from 'vs/editor/contrib/snippet/browser/snippetController2'; import { SuggestController } from 'vs/editor/contrib/suggest/browser/suggestController'; -import { AccessibilityHelpController } from 'vs/workbench/contrib/codeEditor/browser/accessibility/accessibility'; import { MenuPreventer } from 'vs/workbench/contrib/codeEditor/browser/menuPreventer'; import { SelectionClipboardContributionID } from 'vs/workbench/contrib/codeEditor/browser/selectionClipboard'; import { TabCompletionController } from 'vs/workbench/contrib/snippets/browser/tabCompletion'; @@ -53,7 +52,6 @@ export function getOptimizedNestedCodeEditorWidgetOptions(): ICodeEditorWidgetOp SuggestController.ID, SnippetController2.ID, TabCompletionController.ID, - AccessibilityHelpController.ID ]) }; } From 32f9e3c42a3922e211d2ddd0a6e55b01fd3e679a Mon Sep 17 00:00:00 2001 From: jammel-yeboah Date: Thu, 29 Jun 2023 23:41:17 -0400 Subject: [PATCH 057/926] fix: merge issues --- .../contrib/codeEditor/browser/accessibility/accessibility.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.ts b/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.ts index 822ead91991..fd67c1689b1 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.ts @@ -9,7 +9,9 @@ import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configur import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; - +import { accessibilityHelpIsShown } from 'vs/workbench/contrib/accessibility/browser/accessibleView'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; class ToggleScreenReaderMode extends Action2 { constructor() { From 032b14c3ff412f9bc9193486d7c3e602ae56b7e9 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Fri, 30 Jun 2023 10:31:46 +0200 Subject: [PATCH 058/926] bump version (#186703) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 566a3838b20..a791545f055 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "code-oss-dev", - "version": "1.80.0", + "version": "1.81.0", "distro": "4ec8f427013e8d904920b6bdd931e78344401c19", "author": { "name": "Microsoft Corporation" From 5d94be354b529fa2b02175b6211fd562ece99c9d Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 30 Jun 2023 11:35:57 +0200 Subject: [PATCH 059/926] Make fs-methods on IHostUtils optional and handle that accordingly (#186709) fixes https://github.com/microsoft/vscode/issues/184446 --- .../api/common/extHostExtensionService.ts | 16 ++++++++-------- .../workbench/api/node/extensionHostProcess.ts | 4 ++-- .../workbench/api/worker/extensionHostWorker.ts | 6 ------ 3 files changed, 10 insertions(+), 16 deletions(-) diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts index 7bdc7111a4f..40e1f6cef51 100644 --- a/src/vs/workbench/api/common/extHostExtensionService.ts +++ b/src/vs/workbench/api/common/extHostExtensionService.ts @@ -62,8 +62,8 @@ export interface IHostUtils { readonly _serviceBrand: undefined; readonly pid: number | undefined; exit(code: number): void; - exists(path: string): Promise; - realpath(path: string): Promise; + fsExists?(path: string): Promise; + fsRealpath?(path: string): Promise; } type TelemetryActivationEventFragment = { @@ -325,11 +325,11 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme * Applies realpath to file-uris and returns all others uris unmodified */ private async _realPathExtensionUri(uri: URI): Promise { - if (uri.scheme !== Schemas.file) { - return uri; + if (uri.scheme === Schemas.file && this._hostUtils.fsRealpath) { + const realpathValue = await this._hostUtils.fsRealpath(uri.fsPath); + return URI.file(realpathValue); } - const realpathValue = await this._hostUtils.realpath(uri.fsPath); - return URI.file(realpathValue); + return uri; } // create trie to enable fast 'filename -> extension id' look up @@ -683,8 +683,8 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme const host: IExtensionActivationHost = { logService: this._logService, folders: folders.map(folder => folder.uri), - forceUsingSearch: localWithRemote, - exists: (uri) => this._hostUtils.exists(uri.fsPath), + forceUsingSearch: localWithRemote || !this._hostUtils.fsExists, + exists: (uri) => this._hostUtils.fsExists!(uri.fsPath), checkExists: (folders, includes, token) => this._mainThreadWorkspaceProxy.$checkExists(folders, includes, token) }; diff --git a/src/vs/workbench/api/node/extensionHostProcess.ts b/src/vs/workbench/api/node/extensionHostProcess.ts index 01fb6737163..bb3cbfbca7b 100644 --- a/src/vs/workbench/api/node/extensionHostProcess.ts +++ b/src/vs/workbench/api/node/extensionHostProcess.ts @@ -382,8 +382,8 @@ async function startExtensionHostProcess(): Promise { declare readonly _serviceBrand: undefined; public readonly pid = process.pid; exit(code: number) { nativeExit(code); } - exists(path: string) { return Promises.exists(path); } - realpath(path: string) { return realpath(path); } + fsExists(path: string) { return Promises.exists(path); } + fsRealpath(path: string) { return realpath(path); } }; // Attempt to load uri transformer diff --git a/src/vs/workbench/api/worker/extensionHostWorker.ts b/src/vs/workbench/api/worker/extensionHostWorker.ts index 998b53a64ce..6e6f8845da5 100644 --- a/src/vs/workbench/api/worker/extensionHostWorker.ts +++ b/src/vs/workbench/api/worker/extensionHostWorker.ts @@ -159,12 +159,6 @@ const hostUtil = new class implements IHostUtils { exit(_code?: number | undefined): void { nativeClose(); } - async exists(_path: string): Promise { - return true; - } - async realpath(path: string): Promise { - return path; - } }; From eeae481e471570cec2d47b17fc3d2372fa05aecd Mon Sep 17 00:00:00 2001 From: Johannes Date: Fri, 30 Jun 2023 11:43:19 +0200 Subject: [PATCH 060/926] set emit-target and libs to ES2022, avoid static-blocks, fix `Error#cause` conflict https://github.com/microsoft/vscode/issues/185991 --- build/lib/tsb/transpiler.js | 2 +- build/lib/tsb/transpiler.ts | 2 +- src/tsconfig.base.json | 25 +++---------------- src/tsconfig.json | 1 - src/vs/base/node/zip.ts | 1 - .../workbench/api/common/extensionHostMain.ts | 5 ++-- 6 files changed, 7 insertions(+), 29 deletions(-) diff --git a/build/lib/tsb/transpiler.js b/build/lib/tsb/transpiler.js index 701db315b94..0c704b66341 100644 --- a/build/lib/tsb/transpiler.js +++ b/build/lib/tsb/transpiler.js @@ -293,7 +293,7 @@ class SwcTranspiler { tsx: false, decorators: true }, - target: 'es2020', + target: 'es2022', loose: false, minify: { compress: false, diff --git a/build/lib/tsb/transpiler.ts b/build/lib/tsb/transpiler.ts index 312028fd1fa..a546ea63316 100644 --- a/build/lib/tsb/transpiler.ts +++ b/build/lib/tsb/transpiler.ts @@ -376,7 +376,7 @@ export class SwcTranspiler implements ITranspiler { tsx: false, decorators: true }, - target: 'es2020', + target: 'es2022', loose: false, minify: { compress: false, diff --git a/src/tsconfig.base.json b/src/tsconfig.base.json index a309a50adaa..c0a2e174591 100644 --- a/src/tsconfig.base.json +++ b/src/tsconfig.base.json @@ -17,29 +17,10 @@ "./vs/*" ] }, + "target": "es2022", + "useDefineForClassFields": false, "lib": [ - "ES2016", - "ES2017.Object", - "ES2017.String", - "ES2017.Intl", - "ES2017.TypedArrays", - "ES2018.AsyncIterable", - "ES2018.AsyncGenerator", - "ES2018.Promise", - "ES2018.Regexp", - "ES2018.Intl", - "ES2019.Array", - "ES2019.Object", - "ES2019.String", - "ES2019.Symbol", - "ES2020.BigInt", - "ES2020.Promise", - "ES2020.String", - "ES2020.Symbol.WellKnown", - "ES2020.Intl", - "ES2021.Promise", - "ES2021.String", - "ES2021.WeakRef", + "ES2022", "DOM", "DOM.Iterable", "WebWorker.ImportScripts" diff --git a/src/tsconfig.json b/src/tsconfig.json index 5587571ab70..df96ed71247 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -7,7 +7,6 @@ "allowJs": true, "resolveJsonModule": true, "outDir": "../out/vs", - "target": "es2021", "types": [ "keytar", "mocha", diff --git a/src/vs/base/node/zip.ts b/src/vs/base/node/zip.ts index 171b9e45bbc..767d0a5aec6 100644 --- a/src/vs/base/node/zip.ts +++ b/src/vs/base/node/zip.ts @@ -36,7 +36,6 @@ export type ExtractErrorType = 'CorruptZip' | 'Incomplete'; export class ExtractError extends Error { readonly type?: ExtractErrorType; - readonly cause: Error; constructor(type: ExtractErrorType | undefined, cause: Error) { let message = cause.message; diff --git a/src/vs/workbench/api/common/extensionHostMain.ts b/src/vs/workbench/api/common/extensionHostMain.ts index ee87ff55b64..a06c4e93d8f 100644 --- a/src/vs/workbench/api/common/extensionHostMain.ts +++ b/src/vs/workbench/api/common/extensionHostMain.ts @@ -34,12 +34,11 @@ export interface IConsolePatchFn { export abstract class ErrorHandler { - static { + static async installEarlyHandler(accessor: ServicesAccessor): Promise { + // increase number of stack frames (from 10, https://github.com/v8/v8/wiki/Stack-Trace-API) Error.stackTraceLimit = 100; - } - static async installEarlyHandler(accessor: ServicesAccessor): Promise { // does NOT dependent of extension information, can be installed immediately, and simply forwards // to the log service and main thread errors const logService = accessor.get(ILogService); From afef1aba55045ef03d12972b723cb2efd8f305fd Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Fri, 30 Jun 2023 11:44:31 +0200 Subject: [PATCH 061/926] Accept a trailing colon in --goto format. (#186174) * Accept a trailing colon in --goto format. This is useful because many tools add a comma after file[:line[:col]]. * Support trailing comma after line:col in search. --- src/vs/platform/environment/node/argvHelper.ts | 2 +- src/vs/workbench/contrib/search/common/search.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/platform/environment/node/argvHelper.ts b/src/vs/platform/environment/node/argvHelper.ts index 85ba80151bd..74a7369225d 100644 --- a/src/vs/platform/environment/node/argvHelper.ts +++ b/src/vs/platform/environment/node/argvHelper.ts @@ -42,7 +42,7 @@ function parseAndValidate(cmdLineArgs: string[], reportWarnings: boolean): Nativ const args = parseArgs(cmdLineArgs, OPTIONS, reportWarnings ? errorReporter : undefined); if (args.goto) { - args._.forEach(arg => assert(/^(\w:)?[^:]+(:\d*){0,2}$/.test(arg), localize('gotoValidation', "Arguments in `--goto` mode should be in the format of `FILE(:LINE(:CHARACTER))`."))); + args._.forEach(arg => assert(/^(\w:)?[^:]+(:\d*){0,2}:?$/.test(arg), localize('gotoValidation', "Arguments in `--goto` mode should be in the format of `FILE(:LINE(:CHARACTER))`."))); } return args; diff --git a/src/vs/workbench/contrib/search/common/search.ts b/src/vs/workbench/contrib/search/common/search.ts index 46ac5fe37fd..e94b9a99790 100644 --- a/src/vs/workbench/contrib/search/common/search.ts +++ b/src/vs/workbench/contrib/search/common/search.ts @@ -148,8 +148,8 @@ export function getOutOfWorkspaceEditorResources(accessor: ServicesAccessor): UR return resources as URI[]; } -// Supports patterns of <#|:|(><#|:|,> -const LINE_COLON_PATTERN = /\s?[#:\(](?:line )?(\d*)(?:[#:,](\d*))?\)?\s*$/; +// Supports patterns of <#|:|(><#|:|,><:?> +const LINE_COLON_PATTERN = /\s?[#:\(](?:line )?(\d*)(?:[#:,](\d*))?\)?:?\s*$/; export interface IFilterAndRange { filter: string; From 7a5f6f0007d43b772f5a1e5a0a2da2328b53d709 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Fri, 30 Jun 2023 16:12:57 +0600 Subject: [PATCH 062/926] when the content is not focused it should not disappear --- .../editor/contrib/hover/browser/contentHover.ts | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/vs/editor/contrib/hover/browser/contentHover.ts b/src/vs/editor/contrib/hover/browser/contentHover.ts index f03cf308b61..a00c9943d92 100644 --- a/src/vs/editor/contrib/hover/browser/contentHover.ts +++ b/src/vs/editor/contrib/hover/browser/contentHover.ts @@ -195,7 +195,9 @@ export class ContentHoverController extends Disposable { private _setCurrentResult(hoverResult: HoverResult | null): void { if (this._currentResult === hoverResult) { - // avoid updating the DOM to avoid resetting the user selection + if (hoverResult === null && !this._widget.isFocused) { + this._widget.hide(); + } return; } if (hoverResult && hoverResult.messages.length === 0) { @@ -205,6 +207,9 @@ export class ContentHoverController extends Disposable { if (this._currentResult) { this._renderMessages(this._currentResult.anchor, this._currentResult.messages); } else { + if (this._widget.isFocused) { + return; + } this._widget.hide(); } } @@ -479,6 +484,10 @@ export class ContentHoverWidget extends ResizableContentWidget { return this._hoverVisibleKey.get() ?? false; } + public get isFocused(): boolean { + return this._hoverFocusedKey.get() ?? false; + } + constructor( editor: ICodeEditor, @IContextKeyService contextKeyService: IContextKeyService @@ -727,10 +736,7 @@ export class ContentHoverWidget extends ResizableContentWidget { } public hide(): void { - if (!this._visibleData) { - return; - } - const stoleFocus = this._visibleData.stoleFocus; + const stoleFocus = this._visibleData?.stoleFocus; this._setHoverData(undefined); this._resizableNode.maxSize = new dom.Dimension(Infinity, Infinity); this._resizableNode.clearSashHoverState(); From e44296c03ea36ae833de82cc90754385fdc569b0 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Fri, 30 Jun 2023 16:37:15 +0600 Subject: [PATCH 063/926] placing back the focus on the editor in all cases and removing the escape action --- .../contrib/hover/browser/contentHover.ts | 13 +------ src/vs/editor/contrib/hover/browser/hover.ts | 35 ------------------- 2 files changed, 1 insertion(+), 47 deletions(-) diff --git a/src/vs/editor/contrib/hover/browser/contentHover.ts b/src/vs/editor/contrib/hover/browser/contentHover.ts index f03cf308b61..67b790508b2 100644 --- a/src/vs/editor/contrib/hover/browser/contentHover.ts +++ b/src/vs/editor/contrib/hover/browser/contentHover.ts @@ -395,10 +395,6 @@ export class ContentHoverController extends Disposable { public goToBottom(): void { this._widget.goToBottom(); } - - public escape(): void { - this._widget.escape(); - } } class HoverResult { @@ -730,15 +726,12 @@ export class ContentHoverWidget extends ResizableContentWidget { if (!this._visibleData) { return; } - const stoleFocus = this._visibleData.stoleFocus; this._setHoverData(undefined); this._resizableNode.maxSize = new dom.Dimension(Infinity, Infinity); this._resizableNode.clearSashHoverState(); this._hoverFocusedKey.set(false); this._editor.layoutContentWidget(this); - if (stoleFocus) { - this._editor.focus(); - } + this._editor.focus(); } private _removeConstraintsRenderNormally(): void { @@ -822,10 +815,6 @@ export class ContentHoverWidget extends ResizableContentWidget { public goToBottom(): void { this._hover.scrollbar.setScrollPosition({ scrollTop: this._hover.scrollbar.getScrollDimensions().scrollHeight }); } - - public escape(): void { - this._editor.focus(); - } } export class EditorHoverStatusBar extends Disposable implements IEditorHoverStatusBar { diff --git a/src/vs/editor/contrib/hover/browser/hover.ts b/src/vs/editor/contrib/hover/browser/hover.ts index 881435fe4c9..46c94e313df 100644 --- a/src/vs/editor/contrib/hover/browser/hover.ts +++ b/src/vs/editor/contrib/hover/browser/hover.ts @@ -302,10 +302,6 @@ export class ModesHoverController implements IEditorContribution { this._contentWidget?.goToBottom(); } - public escape(): void { - this._contentWidget?.escape(); - } - public isHoverVisible(): boolean | undefined { return this._contentWidget?.isVisible(); } @@ -665,36 +661,6 @@ class GoToBottomHoverAction extends EditorAction { } } -class EscapeFocusHoverAction extends EditorAction { - - constructor() { - super({ - id: 'editor.action.escapeFocusHover', - label: nls.localize({ - key: 'escapeFocusHover', - comment: [ - 'Action that allows to escape from the hover widget with the escape command when the hover widget is focused.' - ] - }, "Escape Focus Hover"), - alias: 'Escape Focus Hover', - precondition: EditorContextKeys.hoverFocused, - kbOpts: { - kbExpr: EditorContextKeys.hoverFocused, - primary: KeyCode.Escape, - weight: KeybindingWeight.EditorContrib - } - }); - } - - public run(accessor: ServicesAccessor, editor: ICodeEditor): void { - const controller = ModesHoverController.get(editor); - if (!controller) { - return; - } - controller.escape(); - } -} - registerEditorContribution(ModesHoverController.ID, ModesHoverController, EditorContributionInstantiation.BeforeFirstInteraction); registerEditorAction(ShowOrFocusHoverAction); registerEditorAction(ShowDefinitionPreviewHoverAction); @@ -706,7 +672,6 @@ registerEditorAction(PageUpHoverAction); registerEditorAction(PageDownHoverAction); registerEditorAction(GoToTopHoverAction); registerEditorAction(GoToBottomHoverAction); -registerEditorAction(EscapeFocusHoverAction); HoverParticipantRegistry.register(MarkdownHoverParticipant); HoverParticipantRegistry.register(MarkerHoverParticipant); From 804b56bbc66127606d15220f2321d0510b34fa4e Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 30 Jun 2023 12:38:31 +0200 Subject: [PATCH 064/926] Please update native-is-elevated package (fix #186606) (#186715) native-Please update native-is-elevated package (fix #186606) --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index a791545f055..cc50950130e 100644 --- a/package.json +++ b/package.json @@ -85,7 +85,7 @@ "jschardet": "3.0.0", "keytar": "7.9.0", "minimist": "^1.2.6", - "native-is-elevated": "0.6.0", + "native-is-elevated": "0.7.0", "native-keymap": "^3.3.2", "native-watchdog": "^1.4.1", "node-pty": "1.1.0-beta1", diff --git a/yarn.lock b/yarn.lock index be4114fa022..4168831c9a3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7027,10 +7027,10 @@ napi-build-utils@^1.0.1: resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-1.0.2.tgz#b1fddc0b2c46e380a0b7a76f984dd47c41a13806" integrity sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg== -native-is-elevated@0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/native-is-elevated/-/native-is-elevated-0.6.0.tgz#9fd9c9c53bfd87d6e6af15a5d6bcb8e592b7ec79" - integrity sha512-HW1wDo4WYZC7W68kaS0cs7FtDTrcFoAY1APc4SlU4Un/FuL7pjilXFEteBCA58EWClKk0912JLIoekOJ/NohnQ== +native-is-elevated@0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/native-is-elevated/-/native-is-elevated-0.7.0.tgz#77499639e232edad1886403969e2bf236294e7af" + integrity sha512-tp8hUqK7vexBiyIWKMvmRxdG6kqUtO+3eay9iB0i16NYgvCqE5wMe1Y0guHilpkmRgvVXEWNW4et1+qqcwpLBA== native-keymap@^3.3.2: version "3.3.3" From 721629bd1a78ef1f3facfae766f3ba2f05556eab Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Fri, 30 Jun 2023 17:15:37 +0600 Subject: [PATCH 065/926] removing the assert type on the active session to be able to spawn several sessions one after the other --- .../workbench/contrib/inlineChat/browser/inlineChatController.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts index ba825ebb44a..1e975917f3d 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -214,7 +214,6 @@ export class InlineChatController implements IEditorContribution { } private async [State.CREATE_SESSION](options: InlineChatRunOptions): Promise { - assertType(this._activeSession === undefined); assertType(this._editor.hasModel()); let session: Session | undefined = options.existingSession; From cc92e04a08a1d4f0b94ac26696a2aa1bebbbf142 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Fri, 30 Jun 2023 17:32:10 +0600 Subject: [PATCH 066/926] adding some code, need to verify what causes the error in dev tools --- .../contrib/inlineChat/browser/inlineChatController.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts index 1e975917f3d..8f6bc67f54a 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -214,6 +214,12 @@ export class InlineChatController implements IEditorContribution { } private async [State.CREATE_SESSION](options: InlineChatRunOptions): Promise { + if (this._activeSession) { + this._sessionStore.clear(); + this._inlineChatSessionService.releaseSession(this._activeSession); + await this[State.PAUSE](); + } + assertType(this._activeSession === undefined); assertType(this._editor.hasModel()); let session: Session | undefined = options.existingSession; From 329cfd399977b30a36d79bb0031b37a2426db694 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 30 Jun 2023 13:32:34 +0200 Subject: [PATCH 067/926] :up: `graceful-fs@4.2.11` (#186713) * :up: `graceful-fs@4.2.11` * also update remote --- 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 cc50950130e..c32e6ddea8d 100644 --- a/package.json +++ b/package.json @@ -79,7 +79,7 @@ "@vscode/windows-mutex": "^0.4.4", "@vscode/windows-process-tree": "^0.5.0", "@vscode/windows-registry": "^1.1.0", - "graceful-fs": "4.2.8", + "graceful-fs": "4.2.11", "http-proxy-agent": "^2.1.0", "https-proxy-agent": "^2.2.3", "jschardet": "3.0.0", diff --git a/remote/package.json b/remote/package.json index bb3ae0f3c5c..2f5f6ce57b8 100644 --- a/remote/package.json +++ b/remote/package.json @@ -14,7 +14,7 @@ "@vscode/windows-process-tree": "^0.5.0", "@vscode/windows-registry": "^1.1.0", "cookie": "^0.4.0", - "graceful-fs": "4.2.8", + "graceful-fs": "4.2.11", "http-proxy-agent": "^2.1.0", "https-proxy-agent": "^2.2.3", "jschardet": "3.0.0", diff --git a/remote/yarn.lock b/remote/yarn.lock index 91363ef48b6..e06f1e1adba 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -338,10 +338,10 @@ github-from-package@0.0.0: resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce" integrity sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4= -graceful-fs@4.2.8: - version "4.2.8" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a" - integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg== +graceful-fs@4.2.11: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== has-unicode@^2.0.0: version "2.0.1" diff --git a/yarn.lock b/yarn.lock index 4168831c9a3..477bba56d38 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4952,10 +4952,10 @@ got@^11.8.5: p-cancelable "^2.0.0" responselike "^2.0.0" -graceful-fs@4.2.8: - version "4.2.8" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a" - integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg== +graceful-fs@4.2.11: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== graceful-fs@^4.0.0, graceful-fs@^4.1.11, graceful-fs@^4.1.6, graceful-fs@^4.2.0: version "4.2.4" From eafde5a28081c115bb0b06e5199be8f2822a737c Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 30 Jun 2023 13:40:13 +0200 Subject: [PATCH 068/926] enable COI for desktop via `coep: credentialless`, change command line flag from "enable-coi" to "disable-coi" (#186720) fixes https://github.com/microsoft/vscode/issues/186614 --- src/vs/base/common/network.ts | 6 ++++-- src/vs/platform/environment/common/argv.ts | 2 +- .../environment/electron-main/environmentMainService.ts | 2 +- src/vs/platform/environment/node/argv.ts | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/vs/base/common/network.ts b/src/vs/base/common/network.ts index b00899c50db..e37d87f4e67 100644 --- a/src/vs/base/common/network.ts +++ b/src/vs/base/common/network.ts @@ -289,10 +289,12 @@ export const FileAccess = new FileAccessImpl(); export namespace COI { + const coepDefault = platform.isElectron ? 'credentialless' : 'require-corp'; + const coiHeaders = new Map<'3' | '2' | '1' | string, Record>([ ['1', { 'Cross-Origin-Opener-Policy': 'same-origin' }], - ['2', { 'Cross-Origin-Embedder-Policy': 'require-corp' }], - ['3', { 'Cross-Origin-Opener-Policy': 'same-origin', 'Cross-Origin-Embedder-Policy': 'require-corp' }], + ['2', { 'Cross-Origin-Embedder-Policy': coepDefault }], + ['3', { 'Cross-Origin-Opener-Policy': 'same-origin', 'Cross-Origin-Embedder-Policy': coepDefault }], ]); export const CoopAndCoep = Object.freeze(coiHeaders.get('3')); diff --git a/src/vs/platform/environment/common/argv.ts b/src/vs/platform/environment/common/argv.ts index 2bbfd487d75..5b43d16495e 100644 --- a/src/vs/platform/environment/common/argv.ts +++ b/src/vs/platform/environment/common/argv.ts @@ -111,7 +111,7 @@ export interface NativeParsedArgs { 'profile-temp'?: boolean; 'disable-chromium-sandbox'?: boolean; - 'enable-coi'?: boolean; + 'disable-coi'?: boolean; // chromium command line args: https://electronjs.org/docs/all#supported-chrome-command-line-switches 'no-proxy-server'?: boolean; diff --git a/src/vs/platform/environment/electron-main/environmentMainService.ts b/src/vs/platform/environment/electron-main/environmentMainService.ts index 1fd54c5997b..a7e03ab2403 100644 --- a/src/vs/platform/environment/electron-main/environmentMainService.ts +++ b/src/vs/platform/environment/electron-main/environmentMainService.ts @@ -63,7 +63,7 @@ export class EnvironmentMainService extends NativeEnvironmentService implements get disableKeytar(): boolean { return !!this.args['disable-keytar']; } @memoize - get crossOriginIsolated(): boolean { return !!this.args['enable-coi']; } + get crossOriginIsolated(): boolean { return !this.args['disable-coi']; } @memoize get codeCachePath(): string | undefined { return process.env['VSCODE_CODE_CACHE_PATH'] || undefined; } diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index fbae6c3e4ed..422fa2ce240 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -164,7 +164,7 @@ export const OPTIONS: OptionDescriptions> = { 'continueOn': { type: 'string' }, 'locate-shell-integration-path': { type: 'string', args: ['bash', 'pwsh', 'zsh', 'fish'] }, - 'enable-coi': { type: 'boolean' }, + 'disable-coi': { type: 'boolean' }, // chromium flags 'no-proxy-server': { type: 'boolean' }, From 13bf59025835dafa59b2ab43201727f6aa7aa62a Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 30 Jun 2023 05:23:41 -0700 Subject: [PATCH 069/926] Log errors in getTerminalLayoutInfo Part of #133542 --- .../contrib/terminal/electron-sandbox/localTerminalBackend.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts b/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts index 3ac98aa9599..28190d957fb 100644 --- a/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts +++ b/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts @@ -298,8 +298,8 @@ class LocalTerminalBackend extends BaseTerminalBackend implements ITerminalBacke mark('code/terminal/didSetTerminalLayoutInfo'); this._storageService.remove(TerminalStorageKeys.TerminalLayoutInfo, StorageScope.WORKSPACE); } - } catch { - // no-op + } catch (e: unknown) { + this._logService.warn('LocalTerminalBackend#getTerminalLayoutInfo Error', e && typeof e === 'object' && 'message' in e ? e.message : e); } } From 654c61dcb1313a14cacc84bc7c00326c28773914 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 30 Jun 2023 15:13:18 +0200 Subject: [PATCH 070/926] add lint-rule to discourage native private fields (#186735) --- .eslintplugin/code-no-native-private.ts | 33 +++++++++++++++++++ .eslintrc.json | 1 + .../browser/codeActionController.ts | 2 ++ .../codeAction/browser/codeActionModel.ts | 2 ++ .../workbench/api/common/extHostCommands.ts | 2 ++ .../api/common/extHostDiagnostics.ts | 2 ++ .../api/common/extHostExtensionService.ts | 2 ++ src/vs/workbench/api/common/extHostSCM.ts | 2 ++ src/vs/workbench/api/common/extHostSecrets.ts | 2 ++ .../workbench/api/common/extHostStatusBar.ts | 2 ++ src/vs/workbench/api/common/extHostTask.ts | 2 ++ src/vs/workbench/api/common/extHostTesting.ts | 3 +- src/vs/workbench/api/common/extHostTypes.ts | 2 ++ src/vs/workbench/api/common/extHostWebview.ts | 2 ++ .../api/common/extHostWebviewPanels.ts | 2 ++ .../api/common/extHostWebviewView.ts | 2 ++ .../browser/editSessionsStorageService.ts | 2 ++ .../interactive/browser/interactiveEditor.ts | 2 ++ .../browser/interactiveHistoryService.ts | 2 ++ .../browser/webviewWorkbenchService.ts | 2 ++ .../remote/common/remoteAgentService.ts | 3 +- .../common/embedderTerminalService.ts | 2 ++ 22 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 .eslintplugin/code-no-native-private.ts diff --git a/.eslintplugin/code-no-native-private.ts b/.eslintplugin/code-no-native-private.ts new file mode 100644 index 00000000000..4d6be23b8f3 --- /dev/null +++ b/.eslintplugin/code-no-native-private.ts @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as eslint from 'eslint'; + +export = new class ApiProviderNaming implements eslint.Rule.RuleModule { + + readonly meta: eslint.Rule.RuleMetaData = { + messages: { + slow: 'Native private fields are much slower and should only be used when needed. Ignore this warning if you know what you are doing, use compile-time private otherwise. See https://github.com/microsoft/vscode/issues/185991#issuecomment-1614468158 for details', + } + }; + + create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { + + return { + ['PropertyDefinition PrivateIdentifier']: (node: any) => { + context.report({ + node, + messageId: 'slow' + }); + }, + ['MethodDefinition PrivateIdentifier']: (node: any) => { + context.report({ + node, + messageId: 'slow' + }); + } + }; + } +}; diff --git a/.eslintrc.json b/.eslintrc.json index c28784041b4..cb94d8b3b86 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -69,6 +69,7 @@ } ], "local/code-translation-remind": "warn", + "local/code-no-native-private": "warn", "local/code-no-nls-in-standalone-editor": "warn", "local/code-no-standalone-editor": "warn", "local/code-no-unexternalized-strings": "warn", diff --git a/src/vs/editor/contrib/codeAction/browser/codeActionController.ts b/src/vs/editor/contrib/codeAction/browser/codeActionController.ts index d441eef1d2e..384902ff004 100644 --- a/src/vs/editor/contrib/codeAction/browser/codeActionController.ts +++ b/src/vs/editor/contrib/codeAction/browser/codeActionController.ts @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +/* eslint-disable local/code-no-native-private */ + import { getDomNodePagePosition } from 'vs/base/browser/dom'; import { IAnchor } from 'vs/base/browser/ui/contextview/contextview'; import { IAction } from 'vs/base/common/actions'; diff --git a/src/vs/editor/contrib/codeAction/browser/codeActionModel.ts b/src/vs/editor/contrib/codeAction/browser/codeActionModel.ts index 3979c4e4538..e36f94c275f 100644 --- a/src/vs/editor/contrib/codeAction/browser/codeActionModel.ts +++ b/src/vs/editor/contrib/codeAction/browser/codeActionModel.ts @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +/* eslint-disable local/code-no-native-private */ + import { CancelablePromise, createCancelablePromise, TimeoutTimer } from 'vs/base/common/async'; import { isCancellationError } from 'vs/base/common/errors'; import { Emitter } from 'vs/base/common/event'; diff --git a/src/vs/workbench/api/common/extHostCommands.ts b/src/vs/workbench/api/common/extHostCommands.ts index c2053ac99fb..e8fc48765c7 100644 --- a/src/vs/workbench/api/common/extHostCommands.ts +++ b/src/vs/workbench/api/common/extHostCommands.ts @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +/* eslint-disable local/code-no-native-private */ + import { validateConstraint } from 'vs/base/common/types'; import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands'; import * as extHostTypes from 'vs/workbench/api/common/extHostTypes'; diff --git a/src/vs/workbench/api/common/extHostDiagnostics.ts b/src/vs/workbench/api/common/extHostDiagnostics.ts index 8e76e228758..91f90bc5f91 100644 --- a/src/vs/workbench/api/common/extHostDiagnostics.ts +++ b/src/vs/workbench/api/common/extHostDiagnostics.ts @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +/* eslint-disable local/code-no-native-private */ + import { localize } from 'vs/nls'; import { IMarkerData, MarkerSeverity } from 'vs/platform/markers/common/markers'; import { URI, UriComponents } from 'vs/base/common/uri'; diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts index 40e1f6cef51..6e5e3e2c100 100644 --- a/src/vs/workbench/api/common/extHostExtensionService.ts +++ b/src/vs/workbench/api/common/extHostExtensionService.ts @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +/* eslint-disable local/code-no-native-private */ + import * as nls from 'vs/nls'; import * as path from 'vs/base/common/path'; import * as performance from 'vs/base/common/performance'; diff --git a/src/vs/workbench/api/common/extHostSCM.ts b/src/vs/workbench/api/common/extHostSCM.ts index 0280e5f2855..a8d22102648 100644 --- a/src/vs/workbench/api/common/extHostSCM.ts +++ b/src/vs/workbench/api/common/extHostSCM.ts @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +/* eslint-disable local/code-no-native-private */ + import { URI, UriComponents } from 'vs/base/common/uri'; import { Event, Emitter } from 'vs/base/common/event'; import { debounce } from 'vs/base/common/decorators'; diff --git a/src/vs/workbench/api/common/extHostSecrets.ts b/src/vs/workbench/api/common/extHostSecrets.ts index cf324e43576..d1af02ed1a2 100644 --- a/src/vs/workbench/api/common/extHostSecrets.ts +++ b/src/vs/workbench/api/common/extHostSecrets.ts @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +/* eslint-disable local/code-no-native-private */ + import type * as vscode from 'vscode'; import { ExtHostSecretState } from 'vs/workbench/api/common/extHostSecretState'; diff --git a/src/vs/workbench/api/common/extHostStatusBar.ts b/src/vs/workbench/api/common/extHostStatusBar.ts index a4864e16eae..7e8db34a599 100644 --- a/src/vs/workbench/api/common/extHostStatusBar.ts +++ b/src/vs/workbench/api/common/extHostStatusBar.ts @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +/* eslint-disable local/code-no-native-private */ + import { StatusBarAlignment as ExtHostStatusBarAlignment, Disposable, ThemeColor, asStatusBarItemIdentifier } from './extHostTypes'; import type * as vscode from 'vscode'; import { MainContext, MainThreadStatusBarShape, IMainContext, ICommandDto, ExtHostStatusBarShape, StatusBarItemDto } from './extHost.protocol'; diff --git a/src/vs/workbench/api/common/extHostTask.ts b/src/vs/workbench/api/common/extHostTask.ts index 6fc4ada5e09..49d668916a7 100644 --- a/src/vs/workbench/api/common/extHostTask.ts +++ b/src/vs/workbench/api/common/extHostTask.ts @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +/* eslint-disable local/code-no-native-private */ + import { URI, UriComponents } from 'vs/base/common/uri'; import { asPromise } from 'vs/base/common/async'; import { Event, Emitter } from 'vs/base/common/event'; diff --git a/src/vs/workbench/api/common/extHostTesting.ts b/src/vs/workbench/api/common/extHostTesting.ts index d17a5a2c76a..147394051c6 100644 --- a/src/vs/workbench/api/common/extHostTesting.ts +++ b/src/vs/workbench/api/common/extHostTesting.ts @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +/* eslint-disable local/code-no-native-private */ + import { mapFind } from 'vs/base/common/arrays'; import { RunOnceScheduler } from 'vs/base/common/async'; import { VSBuffer } from 'vs/base/common/buffer'; @@ -1090,4 +1092,3 @@ const profileGroupToBitset: { [K in TestRunProfileKind]: TestRunProfileBitset } [TestRunProfileKind.Debug]: TestRunProfileBitset.Debug, [TestRunProfileKind.Run]: TestRunProfileBitset.Run, }; - diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index b7c7df1f577..2647d41c5db 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +/* eslint-disable local/code-no-native-private */ + import { asArray, coalesceInPlace, equals } from 'vs/base/common/arrays'; import { illegalArgument } from 'vs/base/common/errors'; import { IRelativePattern } from 'vs/base/common/glob'; diff --git a/src/vs/workbench/api/common/extHostWebview.ts b/src/vs/workbench/api/common/extHostWebview.ts index 4755a00ed8a..9d65e82faa4 100644 --- a/src/vs/workbench/api/common/extHostWebview.ts +++ b/src/vs/workbench/api/common/extHostWebview.ts @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +/* eslint-disable local/code-no-native-private */ + import { VSBuffer } from 'vs/base/common/buffer'; import { Emitter, Event } from 'vs/base/common/event'; import { Schemas } from 'vs/base/common/network'; diff --git a/src/vs/workbench/api/common/extHostWebviewPanels.ts b/src/vs/workbench/api/common/extHostWebviewPanels.ts index 189d350428b..c7f1717ebf5 100644 --- a/src/vs/workbench/api/common/extHostWebviewPanels.ts +++ b/src/vs/workbench/api/common/extHostWebviewPanels.ts @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +/* eslint-disable local/code-no-native-private */ + import { Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; diff --git a/src/vs/workbench/api/common/extHostWebviewView.ts b/src/vs/workbench/api/common/extHostWebviewView.ts index d6ceadb2e7d..89ae50c50cf 100644 --- a/src/vs/workbench/api/common/extHostWebviewView.ts +++ b/src/vs/workbench/api/common/extHostWebviewView.ts @@ -13,6 +13,8 @@ import type * as vscode from 'vscode'; import * as extHostProtocol from './extHost.protocol'; import * as extHostTypes from './extHostTypes'; +/* eslint-disable local/code-no-native-private */ + class ExtHostWebviewView extends Disposable implements vscode.WebviewView { readonly #handle: extHostProtocol.WebviewHandle; diff --git a/src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts b/src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts index 4f7b9def689..6f9c20338e7 100644 --- a/src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts +++ b/src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +/* eslint-disable local/code-no-native-private */ + import { Disposable } from 'vs/base/common/lifecycle'; import { localize } from 'vs/nls'; import { Action2, MenuId, MenuRegistry, registerAction2 } from 'vs/platform/actions/common/actions'; diff --git a/src/vs/workbench/contrib/interactive/browser/interactiveEditor.ts b/src/vs/workbench/contrib/interactive/browser/interactiveEditor.ts index 205c787598c..c97adbabfe2 100644 --- a/src/vs/workbench/contrib/interactive/browser/interactiveEditor.ts +++ b/src/vs/workbench/contrib/interactive/browser/interactiveEditor.ts @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +/* eslint-disable local/code-no-native-private */ + import 'vs/css!./media/interactive'; import * as nls from 'vs/nls'; import * as DOM from 'vs/base/browser/dom'; diff --git a/src/vs/workbench/contrib/interactive/browser/interactiveHistoryService.ts b/src/vs/workbench/contrib/interactive/browser/interactiveHistoryService.ts index ad21831f2e6..f39479fa10f 100644 --- a/src/vs/workbench/contrib/interactive/browser/interactiveHistoryService.ts +++ b/src/vs/workbench/contrib/interactive/browser/interactiveHistoryService.ts @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +/* eslint-disable local/code-no-native-private */ + import { HistoryNavigator2 } from 'vs/base/common/history'; import { Disposable } from 'vs/base/common/lifecycle'; import { ResourceMap } from 'vs/base/common/map'; diff --git a/src/vs/workbench/contrib/webviewPanel/browser/webviewWorkbenchService.ts b/src/vs/workbench/contrib/webviewPanel/browser/webviewWorkbenchService.ts index d484930222e..263649a8b74 100644 --- a/src/vs/workbench/contrib/webviewPanel/browser/webviewWorkbenchService.ts +++ b/src/vs/workbench/contrib/webviewPanel/browser/webviewWorkbenchService.ts @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +/* eslint-disable local/code-no-native-private */ + import { CancelablePromise, createCancelablePromise, DeferredPromise } from 'vs/base/common/async'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { memoize } from 'vs/base/common/decorators'; diff --git a/src/vs/workbench/services/remote/common/remoteAgentService.ts b/src/vs/workbench/services/remote/common/remoteAgentService.ts index 3fc8be68725..868df20e8f4 100644 --- a/src/vs/workbench/services/remote/common/remoteAgentService.ts +++ b/src/vs/workbench/services/remote/common/remoteAgentService.ts @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +/* eslint-disable local/code-no-native-private */ + import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { RemoteAgentConnectionContext, IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment'; import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; @@ -150,4 +152,3 @@ export const remoteConnectionLatencyMeasurer = new class { return this.#lastMeasurement; } }; - diff --git a/src/vs/workbench/services/terminal/common/embedderTerminalService.ts b/src/vs/workbench/services/terminal/common/embedderTerminalService.ts index d9174af00fe..52e52a85acd 100644 --- a/src/vs/workbench/services/terminal/common/embedderTerminalService.ts +++ b/src/vs/workbench/services/terminal/common/embedderTerminalService.ts @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +/* eslint-disable local/code-no-native-private */ + import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { Emitter, Event } from 'vs/base/common/event'; From 1362a5634d778a7aab4af399b324093cebf73b23 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 30 Jun 2023 06:14:18 -0700 Subject: [PATCH 071/926] Remove native private fields from terminal/ Part of #186737 --- .../common/embedderTerminalService.ts | 34 +++++++++---------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/src/vs/workbench/services/terminal/common/embedderTerminalService.ts b/src/vs/workbench/services/terminal/common/embedderTerminalService.ts index 52e52a85acd..61c4a433e09 100644 --- a/src/vs/workbench/services/terminal/common/embedderTerminalService.ts +++ b/src/vs/workbench/services/terminal/common/embedderTerminalService.ts @@ -3,8 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -/* eslint-disable local/code-no-native-private */ - import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { Emitter, Event } from 'vs/base/common/event'; @@ -74,17 +72,17 @@ class EmbedderTerminalService implements IEmbedderTerminalService { class EmbedderTerminalProcess extends Disposable implements ITerminalChildProcess { - readonly #pty: IEmbedderTerminalPty; + private readonly _pty: IEmbedderTerminalPty; readonly shouldPersist = false; readonly onProcessData: Event; - readonly #onProcessReady = this._register(new Emitter()); - readonly onProcessReady = this.#onProcessReady.event; - readonly #onDidChangeProperty = this._register(new Emitter>()); - readonly onDidChangeProperty = this.#onDidChangeProperty.event; - readonly #onProcessExit = this._register(new Emitter()); - readonly onProcessExit = this.#onProcessExit.event; + private readonly _onProcessReady = this._register(new Emitter()); + readonly onProcessReady = this._onProcessReady.event; + private readonly _onDidChangeProperty = this._register(new Emitter>()); + readonly onDidChangeProperty = this._onDidChangeProperty.event; + private readonly _onProcessExit = this._register(new Emitter()); + readonly onProcessExit = this._onProcessExit.event; constructor( readonly id: number, @@ -92,13 +90,13 @@ class EmbedderTerminalProcess extends Disposable implements ITerminalChildProces ) { super(); - this.#pty = pty; - this.onProcessData = this.#pty.onDidWrite; - if (this.#pty.onDidClose) { - this._register(this.#pty.onDidClose(e => this.#onProcessExit.fire(e || undefined))); + this._pty = pty; + this.onProcessData = this._pty.onDidWrite; + if (this._pty.onDidClose) { + this._register(this._pty.onDidClose(e => this._onProcessExit.fire(e || undefined))); } - if (this.#pty.onDidChangeName) { - this._register(this.#pty.onDidChangeName(e => this.#onDidChangeProperty.fire({ + if (this._pty.onDidChangeName) { + this._register(this._pty.onDidChangeName(e => this._onDidChangeProperty.fire({ type: ProcessPropertyType.Title, value: e }))); @@ -106,12 +104,12 @@ class EmbedderTerminalProcess extends Disposable implements ITerminalChildProces } async start(): Promise { - this.#onProcessReady.fire({ pid: -1, cwd: '', windowsPty: undefined }); - this.#pty.open(); + this._onProcessReady.fire({ pid: -1, cwd: '', windowsPty: undefined }); + this._pty.open(); return undefined; } shutdown(): void { - this.#pty.close(); + this._pty.close(); } // TODO: A lot of these aren't useful for some implementations of ITerminalChildProcess, should From e5c9502c7fd5d18c9b3d7df66acba2f0850615f0 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Fri, 30 Jun 2023 15:17:02 +0200 Subject: [PATCH 072/926] Lazily resolved checkbox tree items inconsistently receive parent checked state (#186738) Fixes #186663 --- src/vs/workbench/browser/parts/views/treeView.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/views/treeView.ts b/src/vs/workbench/browser/parts/views/treeView.ts index 31d52627b64..df837b853b2 100644 --- a/src/vs/workbench/browser/parts/views/treeView.ts +++ b/src/vs/workbench/browser/parts/views/treeView.ts @@ -344,13 +344,20 @@ abstract class AbstractTreeView extends Disposable implements ITreeView { async getChildren(node?: ITreeItem): Promise { let children: ITreeItem[]; + const checkboxesUpdated: ITreeItem[] = []; if (node && node.children) { children = node.children; } else { node = node ?? self.root; node.children = await (node instanceof Root ? dataProvider.getChildren() : dataProvider.getChildren(node)); children = node.children ?? []; - children.forEach(child => child.parent = node); + children.forEach(child => { + child.parent = node; + if (!self.manuallyManageCheckboxes && (node?.checkbox?.isChecked === true) && (child.checkbox?.isChecked === false)) { + child.checkbox.isChecked = true; + checkboxesUpdated.push(child); + } + }); } if (node instanceof Root) { const oldEmpty = this._isEmpty; @@ -359,6 +366,9 @@ abstract class AbstractTreeView extends Disposable implements ITreeView { this._onDidChangeEmpty.fire(); } } + if (checkboxesUpdated.length > 0) { + self._onDidChangeCheckboxState.fire(checkboxesUpdated); + } return children; } }; From 80d23c9b9f879c9ab37b77125bc68fa19e35908d Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 30 Jun 2023 16:22:14 +0200 Subject: [PATCH 073/926] remove `_VSCODE_NODE_MODULES` magic from bootstrap-window because they are not sandboxed/node-free (#186744) --- src/bootstrap-window.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/bootstrap-window.js b/src/bootstrap-window.js index 778f1deb9a6..66da7fb0e41 100644 --- a/src/bootstrap-window.js +++ b/src/bootstrap-window.js @@ -88,9 +88,6 @@ window['MonacoEnvironment'] = {}; - // VSCODE_GLOBALS: node_modules - globalThis._VSCODE_NODE_MODULES = new Proxy(Object.create(null), { get: (_target, mod) => (require.__$__nodeRequire ?? require)(String(mod)) }); - const loaderConfig = { baseUrl: `${bootstrapLib.fileUriFromPath(configuration.appRoot, { isWindows: safeProcess.platform === 'win32', scheme: 'vscode-file', fallbackAuthority: 'vscode-app' })}/out`, 'vs/nls': nlsConfig, From 77e84b6db8477062cb5dfc260fd08a3e8f8c30d2 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Fri, 30 Jun 2023 16:22:45 +0200 Subject: [PATCH 074/926] SCM - add distinct commands/menu items to open a repository in the terminal + setting to control which menu item appears (#186742) --- .../externalTerminal.contribution.ts | 15 ++++++++++++ .../contrib/scm/browser/scm.contribution.ts | 24 ++++++++++++++++--- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/externalTerminal/electron-sandbox/externalTerminal.contribution.ts b/src/vs/workbench/contrib/externalTerminal/electron-sandbox/externalTerminal.contribution.ts index 88a88a34fb1..92be8da593c 100644 --- a/src/vs/workbench/contrib/externalTerminal/electron-sandbox/externalTerminal.contribution.ts +++ b/src/vs/workbench/contrib/externalTerminal/electron-sandbox/externalTerminal.contribution.ts @@ -112,6 +112,21 @@ export class ExternalTerminalContribution implements IWorkbenchContribution { description: nls.localize('explorer.openInTerminalKind', "When opening a file from the Explorer in a terminal, determines what kind of terminal will be launched"), default: 'integrated' }, + 'terminal.sourceControlRepositoriesKind': { + type: 'string', + enum: [ + 'integrated', + 'external', + 'both' + ], + enumDescriptions: [ + nls.localize('terminal.sourceControlRepositoriesKind.integrated', "Use VS Code's integrated terminal."), + nls.localize('terminal.sourceControlRepositoriesKind.external', "Use the configured external terminal."), + nls.localize('terminal.sourceControlRepositoriesKind.both', "Use the other two together.") + ], + description: nls.localize('sourceControlRepositories.openInTerminalKind', "When opening a repository from the Source Control Repositories view in a terminal, determines what kind of terminal will be launched"), + default: 'integrated' + }, 'terminal.external.windowsExec': { type: 'string', description: nls.localize('terminal.external.windowsExec', "Customizes which terminal to run on Windows."), diff --git a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts index 73c4cf62bb8..e034cf09200 100644 --- a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts +++ b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts @@ -367,7 +367,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ primary: KeyMod.Alt | KeyCode.UpArrow }); -CommandsRegistry.registerCommand('scm.openInTerminal', async (accessor, provider: ISCMProvider) => { +CommandsRegistry.registerCommand('scm.openInIntegratedTerminal', async (accessor, provider: ISCMProvider) => { if (!provider || !provider.rootUri) { return; } @@ -376,13 +376,31 @@ CommandsRegistry.registerCommand('scm.openInTerminal', async (accessor, provider await commandService.executeCommand('openInIntegratedTerminal', provider.rootUri); }); +CommandsRegistry.registerCommand('scm.openInTerminal', async (accessor, provider: ISCMProvider) => { + if (!provider || !provider.rootUri) { + return; + } + + const commandService = accessor.get(ICommandService); + await commandService.executeCommand('openInTerminal', provider.rootUri); +}); + MenuRegistry.appendMenuItem(MenuId.SCMSourceControl, { group: '100_end', command: { id: 'scm.openInTerminal', - title: localize('open in terminal', "Open In Terminal") + title: localize('open in external terminal', "Open in External Terminal") }, - when: ContextKeyExpr.equals('scmProviderHasRootUri', true) + when: ContextKeyExpr.and(ContextKeyExpr.equals('scmProviderHasRootUri', true), ContextKeyExpr.or(ContextKeyExpr.equals('config.terminal.sourceControlRepositoriesKind', 'external'), ContextKeyExpr.equals('config.terminal.sourceControlRepositoriesKind', 'both'))) +}); + +MenuRegistry.appendMenuItem(MenuId.SCMSourceControl, { + group: '100_end', + command: { + id: 'scm.openInIntegratedTerminal', + title: localize('open in integrated terminal', "Open in Integrated Terminal") + }, + when: ContextKeyExpr.and(ContextKeyExpr.equals('scmProviderHasRootUri', true), ContextKeyExpr.or(ContextKeyExpr.equals('config.terminal.sourceControlRepositoriesKind', 'integrated'), ContextKeyExpr.equals('config.terminal.sourceControlRepositoriesKind', 'both'))) }); registerSingleton(ISCMService, SCMService, InstantiationType.Delayed); From 2e5a2f0dd6e5dba6b509b01bddea6d7a14ce2f91 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 30 Jun 2023 16:44:09 +0200 Subject: [PATCH 075/926] :lipstick: use optional jsdoc param annotation (#186747) --- src/buildfile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/buildfile.js b/src/buildfile.js index 3f384d2a082..8917223094a 100644 --- a/src/buildfile.js +++ b/src/buildfile.js @@ -5,7 +5,7 @@ /** * @param {string} name - * @param {string[]} exclude + * @param {string[]=} exclude */ function createModuleDescription(name, exclude) { From 738e5c69c7460c07e1adad3ed7d76423b271a250 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 30 Jun 2023 16:53:30 +0200 Subject: [PATCH 076/926] eng - do not use native privates (#186737) (#186741) --- .../remote/common/remoteAgentService.ts | 50 +++++++++---------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/src/vs/workbench/services/remote/common/remoteAgentService.ts b/src/vs/workbench/services/remote/common/remoteAgentService.ts index 868df20e8f4..db2d598dd8d 100644 --- a/src/vs/workbench/services/remote/common/remoteAgentService.ts +++ b/src/vs/workbench/services/remote/common/remoteAgentService.ts @@ -3,8 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -/* eslint-disable local/code-no-native-private */ - import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { RemoteAgentConnectionContext, IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment'; import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; @@ -74,55 +72,55 @@ export interface IRemoteConnectionLatencyMeasurement { export const remoteConnectionLatencyMeasurer = new class { - readonly #maxSampleCount = 5; - readonly #sampleDelay = 2000; + readonly maxSampleCount = 5; + readonly sampleDelay = 2000; - readonly #initial: number[] = []; - readonly #maxInitialCount = 3; + readonly initial: number[] = []; + readonly maxInitialCount = 3; - readonly #average: number[] = []; - readonly #maxAverageCount = 100; + readonly average: number[] = []; + readonly maxAverageCount = 100; - readonly #highLatencyMultiple = 2; - readonly #highLatencyMinThreshold = 500; - readonly #highLatencyMaxThreshold = 1500; + readonly highLatencyMultiple = 2; + readonly highLatencyMinThreshold = 500; + readonly highLatencyMaxThreshold = 1500; - #lastMeasurement: IRemoteConnectionLatencyMeasurement | undefined = undefined; - get latency() { return this.#lastMeasurement; } + lastMeasurement: IRemoteConnectionLatencyMeasurement | undefined = undefined; + get latency() { return this.lastMeasurement; } async measure(remoteAgentService: IRemoteAgentService): Promise { let currentLatency = Infinity; // Measure up to samples count - for (let i = 0; i < this.#maxSampleCount; i++) { + for (let i = 0; i < this.maxSampleCount; i++) { const rtt = await remoteAgentService.getRoundTripTime(); if (rtt === undefined) { return undefined; } currentLatency = Math.min(currentLatency, rtt / 2 /* we want just one way, not round trip time */); - await timeout(this.#sampleDelay); + await timeout(this.sampleDelay); } // Keep track of average latency - this.#average.push(currentLatency); - if (this.#average.length > this.#maxAverageCount) { - this.#average.shift(); + this.average.push(currentLatency); + if (this.average.length > this.maxAverageCount) { + this.average.shift(); } // Keep track of initial latency let initialLatency: number | undefined = undefined; - if (this.#initial.length < this.#maxInitialCount) { - this.#initial.push(currentLatency); + if (this.initial.length < this.maxInitialCount) { + this.initial.push(currentLatency); } else { - initialLatency = this.#initial.reduce((sum, value) => sum + value, 0) / this.#initial.length; + initialLatency = this.initial.reduce((sum, value) => sum + value, 0) / this.initial.length; } // Remember as last measurement - this.#lastMeasurement = { + this.lastMeasurement = { initial: initialLatency, current: currentLatency, - average: this.#average.reduce((sum, value) => sum + value, 0) / this.#average.length, + average: this.average.reduce((sum, value) => sum + value, 0) / this.average.length, high: (() => { // based on the initial, average and current latency, try to decide @@ -137,11 +135,11 @@ export const remoteConnectionLatencyMeasurer = new class { return false; } - if (currentLatency > this.#highLatencyMaxThreshold) { + if (currentLatency > this.highLatencyMaxThreshold) { return true; } - if (currentLatency > this.#highLatencyMinThreshold && currentLatency > initialLatency * this.#highLatencyMultiple) { + if (currentLatency > this.highLatencyMinThreshold && currentLatency > initialLatency * this.highLatencyMultiple) { return true; } @@ -149,6 +147,6 @@ export const remoteConnectionLatencyMeasurer = new class { })() }; - return this.#lastMeasurement; + return this.lastMeasurement; } }; From 1b2248f5bda104c7034812bebd08af3792618a7c Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 30 Jun 2023 07:54:45 -0700 Subject: [PATCH 077/926] Remove attempt to persist lastPtyId This wasn't working and wasn't needed in the first place. Fixes #186645 --- .../terminal/electron-main/electronPtyHostStarter.ts | 7 +++---- src/vs/platform/terminal/node/nodePtyHostStarter.ts | 3 +-- src/vs/platform/terminal/node/ptyHost.ts | 5 +---- src/vs/platform/terminal/node/ptyHostMain.ts | 4 +--- src/vs/platform/terminal/node/ptyHostService.ts | 9 +-------- src/vs/platform/terminal/node/ptyService.ts | 3 ++- 6 files changed, 9 insertions(+), 22 deletions(-) diff --git a/src/vs/platform/terminal/electron-main/electronPtyHostStarter.ts b/src/vs/platform/terminal/electron-main/electronPtyHostStarter.ts index 887017449c8..747dc045386 100644 --- a/src/vs/platform/terminal/electron-main/electronPtyHostStarter.ts +++ b/src/vs/platform/terminal/electron-main/electronPtyHostStarter.ts @@ -40,7 +40,7 @@ export class ElectronPtyHostStarter implements IPtyHostStarter { validatedIpcMain.on('vscode:createPtyHostMessageChannel', (e, nonce) => this._onWindowConnection(e, nonce)); } - start(lastPtyId: number): IPtyHostConnection { + start(): IPtyHostConnection { this.utilityProcess = new UtilityProcess(this._logService, NullTelemetryService, this._lifecycleMainService); const inspectParams = parsePtyHostDebugPort(this._environmentMainService.args, this._environmentMainService.isBuilt); @@ -54,7 +54,7 @@ export class ElectronPtyHostStarter implements IPtyHostStarter { entryPoint: 'vs/platform/terminal/node/ptyHostMain', execArgv, args: ['--logsPath', this._environmentMainService.logsHome.fsPath], - env: this._createPtyHostConfiguration(lastPtyId) + env: this._createPtyHostConfiguration() }); const port = this.utilityProcess.connect(); @@ -75,11 +75,10 @@ export class ElectronPtyHostStarter implements IPtyHostStarter { }; } - private _createPtyHostConfiguration(lastPtyId: number) { + private _createPtyHostConfiguration() { this._environmentMainService.unsetSnapExportedVariables(); const config: { [key: string]: string } = { ...deepClone(process.env), - VSCODE_LAST_PTY_ID: String(lastPtyId), VSCODE_AMD_ENTRYPOINT: 'vs/platform/terminal/node/ptyHostMain', VSCODE_PIPE_LOGGING: 'true', VSCODE_VERBOSE_LOGGING: 'true', // transmit console logs from server to client, diff --git a/src/vs/platform/terminal/node/nodePtyHostStarter.ts b/src/vs/platform/terminal/node/nodePtyHostStarter.ts index 4a589c09a4d..2a1e96d3edc 100644 --- a/src/vs/platform/terminal/node/nodePtyHostStarter.ts +++ b/src/vs/platform/terminal/node/nodePtyHostStarter.ts @@ -18,12 +18,11 @@ export class NodePtyHostStarter implements IPtyHostStarter { ) { } - start(lastPtyId: number): IPtyHostConnection { + start(): IPtyHostConnection { const opts: IIPCOptions = { serverName: 'Pty Host', args: ['--type=ptyHost', '--logsPath', this._environmentService.logsHome.fsPath], env: { - VSCODE_LAST_PTY_ID: lastPtyId, VSCODE_AMD_ENTRYPOINT: 'vs/platform/terminal/node/ptyHostMain', VSCODE_PIPE_LOGGING: 'true', VSCODE_VERBOSE_LOGGING: 'true', // transmit console logs from server to client, diff --git a/src/vs/platform/terminal/node/ptyHost.ts b/src/vs/platform/terminal/node/ptyHost.ts index 1d7bb92a105..8135f489733 100644 --- a/src/vs/platform/terminal/node/ptyHost.ts +++ b/src/vs/platform/terminal/node/ptyHost.ts @@ -19,9 +19,6 @@ export interface IPtyHostStarter { /** * Creates a pty host and connects to it. - * - * @param lastPtyId Tracks the last terminal ID from the pty host so we can give it to the new - * pty host if it's restarted and avoid ID conflicts. */ - start(lastPtyId: number): IPtyHostConnection; + start(): IPtyHostConnection; } diff --git a/src/vs/platform/terminal/node/ptyHostMain.ts b/src/vs/platform/terminal/node/ptyHostMain.ts index 4e3146a38ef..cc7a85019f6 100644 --- a/src/vs/platform/terminal/node/ptyHostMain.ts +++ b/src/vs/platform/terminal/node/ptyHostMain.ts @@ -33,7 +33,6 @@ async function startPtyHost() { shortGraceTime: parseInt(process.env.VSCODE_RECONNECT_SHORT_GRACE_TIME || '0'), scrollback: parseInt(process.env.VSCODE_RECONNECT_SCROLLBACK || '100') }; - const lastPtyId = parseInt(process.env.VSCODE_LAST_PTY_ID || '0'); // Sanitize environment delete process.env.VSCODE_RECONNECT_GRACE_TIME; @@ -41,7 +40,6 @@ async function startPtyHost() { delete process.env.VSCODE_RECONNECT_SCROLLBACK; delete process.env.VSCODE_LATENCY; delete process.env.VSCODE_STARTUP_DELAY; - delete process.env.VSCODE_LAST_PTY_ID; // Delay startup if needed, this must occur before RPC is setup to avoid the channel from timing // out. @@ -79,7 +77,7 @@ async function startPtyHost() { server.registerChannel(TerminalIpcChannels.Heartbeat, ProxyChannel.fromService(heartbeatService)); // Init pty service - const ptyService = new PtyService(lastPtyId, logService, productService, reconnectConstants, simulatedLatency); + const ptyService = new PtyService(logService, productService, reconnectConstants, simulatedLatency); const ptyServiceChannel = ProxyChannel.fromService(ptyService); server.registerChannel(TerminalIpcChannels.PtyHost, ptyServiceChannel); diff --git a/src/vs/platform/terminal/node/ptyHostService.ts b/src/vs/platform/terminal/node/ptyHostService.ts index 6133ba6d8f7..3614710c0b3 100644 --- a/src/vs/platform/terminal/node/ptyHostService.ts +++ b/src/vs/platform/terminal/node/ptyHostService.ts @@ -24,12 +24,6 @@ enum Constants { MaxRestarts = 5 } -/** - * Tracks the last terminal ID from the pty host so we can give it to the new pty host if it's - * restarted and avoid ID conflicts. - */ -let lastPtyId = 0; - /** * This service implements IPtyService by launching a pty host process, forwarding messages to and * from the pty host process and manages the connection. @@ -142,7 +136,7 @@ export class PtyHostService extends Disposable implements IPtyService { } private _startPtyHost(): [IPtyHostConnection, IPtyService] { - const connection = this._ptyHostStarter.start(lastPtyId); + const connection = this._ptyHostStarter.start(); const client = connection.client; this._onPtyHostStart.fire(); @@ -212,7 +206,6 @@ export class PtyHostService extends Disposable implements IPtyService { const timeout = setTimeout(() => this._handleUnresponsiveCreateProcess(), HeartbeatConstants.CreateProcessTimeout); const id = await this._proxy.createProcess(shellLaunchConfig, cwd, cols, rows, unicodeVersion, env, executableEnv, options, shouldPersist, workspaceId, workspaceName); clearTimeout(timeout); - lastPtyId = Math.max(lastPtyId, id); return id; } updateTitle(id: number, title: string, titleSource: TitleEventSource): Promise { diff --git a/src/vs/platform/terminal/node/ptyService.ts b/src/vs/platform/terminal/node/ptyService.ts index 7fa15c80d40..2e98e0a888f 100644 --- a/src/vs/platform/terminal/node/ptyService.ts +++ b/src/vs/platform/terminal/node/ptyService.ts @@ -68,6 +68,8 @@ export class PtyService extends Disposable implements IPtyService { private readonly _revivedPtyIdMap: Map = new Map(); private readonly _autoReplies: Map = new Map(); + private _lastPtyId: number = 0; + private readonly _onHeartbeat = this._register(new Emitter()); readonly onHeartbeat = this._traceEvent('_onHeartbeat', this._onHeartbeat.event); @@ -104,7 +106,6 @@ export class PtyService extends Disposable implements IPtyService { } constructor( - private _lastPtyId: number, private readonly _logService: ILogService, private readonly _productService: IProductService, private readonly _reconnectConstants: IReconnectConstants, From 7bcd98506d1bb737cc3b5a2577550f87f6ec9179 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 30 Jun 2023 07:56:51 -0700 Subject: [PATCH 078/926] Remove unneeded ConsoleLogger from pty host --- src/vs/platform/terminal/node/ptyHostMain.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/terminal/node/ptyHostMain.ts b/src/vs/platform/terminal/node/ptyHostMain.ts index 4e3146a38ef..244033cf6cd 100644 --- a/src/vs/platform/terminal/node/ptyHostMain.ts +++ b/src/vs/platform/terminal/node/ptyHostMain.ts @@ -10,7 +10,7 @@ import { Server as UtilityProcessServer } from 'vs/base/parts/ipc/node/ipc.mp'; import { localize } from 'vs/nls'; import { OPTIONS, parseArgs } from 'vs/platform/environment/node/argv'; import { NativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; -import { ConsoleLogger, getLogLevel } from 'vs/platform/log/common/log'; +import { getLogLevel } from 'vs/platform/log/common/log'; import { LoggerChannel } from 'vs/platform/log/common/logIpc'; import { LogService } from 'vs/platform/log/common/logService'; import { LoggerService } from 'vs/platform/log/node/loggerService'; @@ -64,7 +64,7 @@ async function startPtyHost() { const loggerService = new LoggerService(getLogLevel(environmentService), environmentService.logsHome); server.registerChannel(TerminalIpcChannels.Logger, new LoggerChannel(loggerService, () => DefaultURITransformer)); const logger = loggerService.createLogger('ptyhost', { name: localize('ptyHost', "Pty Host") }); - const logService = new LogService(logger, [new ConsoleLogger()]); + const logService = new LogService(logger); // Log developer config if (startupDelay) { From 277d7593d12943ce5f0f0715cccab488ff190bd3 Mon Sep 17 00:00:00 2001 From: Abhijit Chikane <50770619+abhijit-chikane@users.noreply.github.com> Date: Fri, 30 Jun 2023 21:14:57 +0530 Subject: [PATCH 079/926] Fix the case of the breakpoint placeholder (#185753) Fix the case of the placeholder --- src/vs/workbench/contrib/debug/browser/breakpointWidget.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/breakpointWidget.ts b/src/vs/workbench/contrib/debug/browser/breakpointWidget.ts index f774ac54be0..dfb5cc41436 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointWidget.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointWidget.ts @@ -132,11 +132,11 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi private get placeholder(): string { switch (this.context) { case Context.LOG_MESSAGE: - return nls.localize('breakpointWidgetLogMessagePlaceholder', "Message to log when breakpoint is hit. Expressions within {} are interpolated. 'Enter' to accept, 'esc' to cancel."); + return nls.localize('breakpointWidgetLogMessagePlaceholder', "Message to log when breakpoint is hit. Expressions within {} are interpolated. 'Enter' to accept, 'Esc' to cancel."); case Context.HIT_COUNT: - return nls.localize('breakpointWidgetHitCountPlaceholder', "Break when hit count condition is met. 'Enter' to accept, 'esc' to cancel."); + return nls.localize('breakpointWidgetHitCountPlaceholder', "Break when hit count condition is met. 'Enter' to accept, 'Esc' to cancel."); default: - return nls.localize('breakpointWidgetExpressionPlaceholder', "Break when expression evaluates to true. 'Enter' to accept, 'esc' to cancel."); + return nls.localize('breakpointWidgetExpressionPlaceholder', "Break when expression evaluates to true. 'Enter' to accept, 'Esc' to cancel."); } } From 69cd1439e59e494827f7b20e82f28c88c0319d1e Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 30 Jun 2023 18:05:06 +0200 Subject: [PATCH 080/926] from ESM land bring over amdX-util and bring over one of its usages (#186748) * from ESM land bring over amdX-util and bring over one of its usages * fix tsec violation * one more exemption * grrrr * last try, fingers crossed --- .eslintplugin/code-import-patterns.ts | 3 +- .eslintrc.json | 6 + src/tsec.exemptions.json | 2 + src/typings/vscode-globals-modules.d.ts | 2 + src/vs/amdX.ts | 204 ++++++++++++++++++ src/vs/base/common/amd.ts | 7 + .../services/textfile/common/encoding.ts | 9 +- 7 files changed, 228 insertions(+), 5 deletions(-) create mode 100644 src/vs/amdX.ts diff --git a/.eslintplugin/code-import-patterns.ts b/.eslintplugin/code-import-patterns.ts index 07efb11b058..1f08bb4a4e8 100644 --- a/.eslintplugin/code-import-patterns.ts +++ b/.eslintplugin/code-import-patterns.ts @@ -180,8 +180,9 @@ export = new class implements eslint.Rule.RuleModule { const restrictions = (typeof option.restrictions === 'string' ? [option.restrictions] : option.restrictions).slice(0); if (targetIsVS) { - // Always add "vs/nls" + // Always add "vs/nls" and "vs/amdX" restrictions.push('vs/nls'); + restrictions.push('vs/amdX'); // TODO@jrieken remove after ESM is real } if (targetIsVS && option.layer) { diff --git a/.eslintrc.json b/.eslintrc.json index cb94d8b3b86..7e6ab36bef3 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -596,6 +596,12 @@ "vs/workbench/workbench.common.main" ] }, + { + "target": "src/vs/amdX.ts", + "restrictions": [ + "vs/base/common/*" + ] + }, { "target": "src/vs/workbench/{workbench.desktop.main.nls.js,workbench.web.main.nls.js}", "restrictions": [] diff --git a/src/tsec.exemptions.json b/src/tsec.exemptions.json index 1423e7cc4e6..eb8405c96cb 100644 --- a/src/tsec.exemptions.json +++ b/src/tsec.exemptions.json @@ -14,6 +14,7 @@ "vs/workbench/services/keybinding/test/node/keyboardMapperTestUtils.ts" ], "ban-trustedtypes-createpolicy": [ + "vs/amdX.ts", "vs/base/browser/trustedTypes.ts", "vs/base/worker/workerMain.ts", "vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts" @@ -23,6 +24,7 @@ "vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts" ], "ban-worker-importscripts": [ + "vs/amdX.ts", "vs/workbench/services/extensions/worker/polyfillNestedWorker.ts", "vs/workbench/api/worker/extensionHostWorker.ts", "vs/base/worker/workerMain.ts" diff --git a/src/typings/vscode-globals-modules.d.ts b/src/typings/vscode-globals-modules.d.ts index 443c2b687db..c538b99b63f 100644 --- a/src/typings/vscode-globals-modules.d.ts +++ b/src/typings/vscode-globals-modules.d.ts @@ -18,6 +18,8 @@ declare global { net: typeof import('net'); os: typeof import('os'); module: typeof import('module'); + fs: typeof import('fs'), + vm: typeof import('vm'), ['native-watchdog']: typeof import('native-watchdog') perf_hooks: typeof import('perf_hooks'); diff --git a/src/vs/amdX.ts b/src/vs/amdX.ts new file mode 100644 index 00000000000..56cebe68bfd --- /dev/null +++ b/src/vs/amdX.ts @@ -0,0 +1,204 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { isESM } from 'vs/base/common/amd'; +import { AppResourcePath, FileAccess, nodeModulesAsarPath, nodeModulesPath } from 'vs/base/common/network'; +import * as platform from 'vs/base/common/platform'; +import { IProductConfiguration } from 'vs/base/common/product'; +import { URI } from 'vs/base/common/uri'; + +class DefineCall { + constructor( + public readonly id: string | null | undefined, + public readonly dependencies: string[] | null | undefined, + public readonly callback: any + ) { } +} + +class AMDModuleImporter { + public static INSTANCE = new AMDModuleImporter(); + + private readonly _isWebWorker = (typeof self === 'object' && self.constructor && self.constructor.name === 'DedicatedWorkerGlobalScope'); + private readonly _isRenderer = typeof document === 'object'; + + private readonly _defineCalls: DefineCall[] = []; + private _initialized = false; + private _amdPolicy: Pick, 'name' | 'createScriptURL'> | undefined; + + constructor() { } + + private _initialize(): void { + if (this._initialized) { + return; + } + this._initialized = true; + + (globalThis).define = (id: any, dependencies: any, callback: any) => { + if (typeof id !== 'string') { + callback = dependencies; + dependencies = id; + id = null; + } + if (typeof dependencies !== 'object' || !Array.isArray(dependencies)) { + callback = dependencies; + dependencies = null; + } + // if (!dependencies) { + // dependencies = ['require', 'exports', 'module']; + // } + this._defineCalls.push(new DefineCall(id, dependencies, callback)); + }; + + (globalThis).define.amd = true; + + if (this._isRenderer) { + this._amdPolicy = window.trustedTypes?.createPolicy('amdLoader', { + createScriptURL(value) { + if (value.startsWith(window.location.origin)) { + return value; + } + if (value.startsWith('vscode-file://vscode-app')) { + return value; + } + throw new Error(`[trusted_script_src] Invalid script url: ${value}`); + } + }); + } else if (this._isWebWorker) { + this._amdPolicy = (globalThis).trustedTypes?.createPolicy('amdLoader', { + createScriptURL(value: string) { + return value; + } + }); + } + } + + public async load(scriptSrc: string): Promise { + 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}`); + } + // TODO require, exports, module + if (Array.isArray(defineCall.dependencies) && defineCall.dependencies.length > 0) { + throw new Error(`Cannot resolve dependencies for script ${scriptSrc}. The dependencies are: ${defineCall.dependencies.join(', ')}`); + } + if (typeof defineCall.callback === 'function') { + return defineCall.callback([]); + } else { + return defineCall.callback; + } + } + + private _rendererLoadScript(scriptSrc: string): Promise { + return new Promise((resolve, reject) => { + const scriptElement = document.createElement('script'); + scriptElement.setAttribute('async', 'async'); + scriptElement.setAttribute('type', 'text/javascript'); + + const unbind = () => { + scriptElement.removeEventListener('load', loadEventListener); + scriptElement.removeEventListener('error', errorEventListener); + }; + + const loadEventListener = (e: any) => { + unbind(); + resolve(this._defineCalls.pop()); + }; + + const errorEventListener = (e: any) => { + unbind(); + reject(e); + }; + + scriptElement.addEventListener('load', loadEventListener); + scriptElement.addEventListener('error', errorEventListener); + if (this._amdPolicy) { + scriptSrc = this._amdPolicy.createScriptURL(scriptSrc) as any as string; + } + scriptElement.setAttribute('src', scriptSrc); + document.getElementsByTagName('head')[0].appendChild(scriptElement); + }); + } + + private _workerLoadScript(scriptSrc: string): Promise { + return new Promise((resolve, reject) => { + try { + if (this._amdPolicy) { + scriptSrc = this._amdPolicy.createScriptURL(scriptSrc) as any as string; + } + importScripts(scriptSrc); + resolve(this._defineCalls.pop()); + } catch (err) { + reject(err); + } + }); + } + + private async _nodeJSLoadScript(scriptSrc: string): Promise { + try { + const fs = globalThis._VSCODE_NODE_MODULES['fs']; + const vm = globalThis._VSCODE_NODE_MODULES['vm']; + const module = globalThis._VSCODE_NODE_MODULES['module']; + + const filePath = URI.parse(scriptSrc).fsPath; + const content = fs.readFileSync(filePath).toString(); + const scriptSource = module.wrap(content.replace(/^#!.*/, '')); + const script = new vm.Script(scriptSource); + const compileWrapper = script.runInThisContext(); + compileWrapper.apply(); + return this._defineCalls.pop(); + + } catch (error) { + throw error; + } + } +} + +const cache = new Map>(); + +let _paths: Record = {}; +if (typeof globalThis.require === 'object') { + _paths = (>globalThis.require).paths ?? {}; +} + +/** + * e.g. pass in `vscode-textmate/release/main.js` + */ +export async function importAMDNodeModule(nodeModuleName: string, pathInsideNodeModule: string, isBuilt?: boolean): Promise { + if (isESM) { + + if (isBuilt === undefined) { + const product = globalThis._VSCODE_PRODUCT_JSON as unknown as IProductConfiguration; + isBuilt = !!product?.commit; + } + + if (_paths[nodeModuleName]) { + nodeModuleName = _paths[nodeModuleName]; + } + + const nodeModulePath = `${nodeModuleName}/${pathInsideNodeModule}`; + if (cache.has(nodeModulePath)) { + return cache.get(nodeModulePath)!; + } + let scriptSrc: string; + if (/^\w[\w\d+.-]*:\/\//.test(nodeModulePath)) { + // looks like a URL + // bit of a special case for: src/vs/workbench/services/languageDetection/browser/languageDetectionSimpleWorker.ts + scriptSrc = nodeModulePath; + } else { + const useASAR = (isBuilt && !platform.isWeb); + const actualNodeModulesPath = (useASAR ? nodeModulesAsarPath : nodeModulesPath); + const resourcePath: AppResourcePath = `${actualNodeModulesPath}/${nodeModulePath}`; + scriptSrc = FileAccess.asBrowserUri(resourcePath).toString(true); + } + const result = AMDModuleImporter.INSTANCE.load(scriptSrc); + cache.set(nodeModulePath, result); + return result; + } else { + return await import(nodeModuleName); + } +} diff --git a/src/vs/base/common/amd.ts b/src/vs/base/common/amd.ts index fdbdb2e1030..d5aa099d6b8 100644 --- a/src/vs/base/common/amd.ts +++ b/src/vs/base/common/amd.ts @@ -3,6 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +// ESM-comment-begin +export const isESM = false; +// ESM-comment-end +// ESM-uncomment-begin +// export const isESM = true; +// ESM-uncomment-end + export abstract class LoaderStats { abstract get amdLoad(): [string, number][]; abstract get amdInvoke(): [string, number][]; diff --git a/src/vs/workbench/services/textfile/common/encoding.ts b/src/vs/workbench/services/textfile/common/encoding.ts index d46f0fd893d..6675f732b0e 100644 --- a/src/vs/workbench/services/textfile/common/encoding.ts +++ b/src/vs/workbench/services/textfile/common/encoding.ts @@ -5,6 +5,7 @@ import { Readable, ReadableStream, newWriteableStream, listenStream } from 'vs/base/common/stream'; import { VSBuffer, VSBufferReadable, VSBufferReadableStream } from 'vs/base/common/buffer'; +import { importAMDNodeModule } from 'vs/amdX'; export const UTF8 = 'utf8'; export const UTF8_with_bom = 'utf8bom'; @@ -78,7 +79,7 @@ class DecoderStream implements IDecoderStream { static async create(encoding: string): Promise { let decoder: IDecoderStream | undefined = undefined; if (encoding !== UTF8) { - const iconv = await import('@vscode/iconv-lite-umd'); + const iconv = await importAMDNodeModule('@vscode/iconv-lite-umd', 'lib/iconv-lite-umd.js'); decoder = iconv.getDecoder(toNodeEncoding(encoding)); } else { const utf8TextDecoder = new TextDecoder(); @@ -209,7 +210,7 @@ export function toDecodeStream(source: VSBufferReadableStream, options: IDecodeS } export async function toEncodeReadable(readable: Readable, encoding: string, options?: { addBOM?: boolean }): Promise { - const iconv = await import('@vscode/iconv-lite-umd'); + const iconv = await importAMDNodeModule('@vscode/iconv-lite-umd', 'lib/iconv-lite-umd.js'); const encoder = iconv.getEncoder(toNodeEncoding(encoding), options); let bytesWritten = false; @@ -258,7 +259,7 @@ export async function toEncodeReadable(readable: Readable, encoding: str } export async function encodingExists(encoding: string): Promise { - const iconv = await import('@vscode/iconv-lite-umd'); + const iconv = await importAMDNodeModule('@vscode/iconv-lite-umd', 'lib/iconv-lite-umd.js'); return iconv.encodingExists(toNodeEncoding(encoding)); } @@ -314,7 +315,7 @@ const IGNORE_ENCODINGS = ['ascii', 'utf-16', 'utf-32']; * Guesses the encoding from buffer. */ async function guessEncodingByBuffer(buffer: VSBuffer): Promise { - const jschardet = await import('jschardet'); + const jschardet = await importAMDNodeModule('jschardet', 'dist/jschardet.min.js'); // ensure to limit buffer for guessing due to https://github.com/aadsm/jschardet/issues/53 const limitedBuffer = buffer.slice(0, AUTO_ENCODING_GUESS_MAX_BYTES); From c778cc6117ba3b243a8e2321b4cf33062546e312 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Fri, 30 Jun 2023 18:14:40 +0200 Subject: [PATCH 081/926] Update grammars (#186752) --- extensions/csharp/cgmanifest.json | 2 +- .../csharp/syntaxes/csharp.tmLanguage.json | 35 ++++-- extensions/dart/cgmanifest.json | 2 +- extensions/dart/syntaxes/dart.tmLanguage.json | 4 +- .../fsharp/syntaxes/fsharp.tmLanguage.json | 6 +- .../syntaxes/JavaScript.tmLanguage.json | 112 ++++++++++++++---- .../syntaxes/JavaScriptReact.tmLanguage.json | 112 ++++++++++++++---- extensions/julia/cgmanifest.json | 2 +- .../julia/syntaxes/julia.tmLanguage.json | 42 ++++++- extensions/latex/cgmanifest.json | 4 +- .../latex/syntaxes/Bibtex.tmLanguage.json | 4 +- .../latex/syntaxes/LaTeX.tmLanguage.json | 31 ++--- extensions/latex/syntaxes/TeX.tmLanguage.json | 14 +-- .../markdown-latex-combined.tmLanguage.json | 39 +++--- extensions/perl/package.json | 2 +- extensions/typescript-basics/cgmanifest.json | 2 +- .../syntaxes/TypeScript.tmLanguage.json | 112 ++++++++++++++---- .../syntaxes/TypeScriptReact.tmLanguage.json | 112 ++++++++++++++---- extensions/vb/package.json | 2 +- 19 files changed, 474 insertions(+), 165 deletions(-) diff --git a/extensions/csharp/cgmanifest.json b/extensions/csharp/cgmanifest.json index f5abb0738f5..733388a98a1 100644 --- a/extensions/csharp/cgmanifest.json +++ b/extensions/csharp/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "dotnet/csharp-tmLanguage", "repositoryUrl": "https://github.com/dotnet/csharp-tmLanguage", - "commitHash": "2918bd69cfcc658bd5b4ec1b106bb008e5c21120" + "commitHash": "878aefe73f942ac68dc4a46a0f154661bcb9eff4" } }, "license": "MIT", diff --git a/extensions/csharp/syntaxes/csharp.tmLanguage.json b/extensions/csharp/syntaxes/csharp.tmLanguage.json index d50cac7ae3c..27af054bc3b 100644 --- a/extensions/csharp/syntaxes/csharp.tmLanguage.json +++ b/extensions/csharp/syntaxes/csharp.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/dotnet/csharp-tmLanguage/commit/2918bd69cfcc658bd5b4ec1b106bb008e5c21120", + "version": "https://github.com/dotnet/csharp-tmLanguage/commit/878aefe73f942ac68dc4a46a0f154661bcb9eff4", "name": "C#", "scopeName": "source.cs", "patterns": [ @@ -381,13 +381,19 @@ "using-directive": { "patterns": [ { - "begin": "\\b(using)\\b\\s+(static)\\s+", + "begin": "(\\b(global)\\b\\s+)?\\b(using)\\b\\s+(static)\\b\\s+(\\b(unsafe)\\b\\s+)?", "beginCaptures": { - "1": { + "2": { + "name": "keyword.other.global.cs" + }, + "3": { "name": "keyword.other.using.cs" }, - "2": { + "4": { "name": "keyword.other.static.cs" + }, + "6": { + "name": "storage.modifier.cs" } }, "end": "(?=;)", @@ -398,12 +404,18 @@ ] }, { - "begin": "\\b(using)\\s+(?=(@?[_[:alpha:]][_[:alnum:]]*)\\s*=)", + "begin": "(\\b(global)\\b\\s+)?\\b(using)\\b\\s+(\\b(unsafe)\\b\\s+)?(?=(@?[_[:alpha:]][_[:alnum:]]*)\\s*=)", "beginCaptures": { - "1": { + "2": { + "name": "keyword.other.global.cs" + }, + "3": { "name": "keyword.other.using.cs" }, - "2": { + "5": { + "name": "storage.modifier.cs" + }, + "6": { "name": "entity.name.type.alias.cs" } }, @@ -421,9 +433,12 @@ ] }, { - "begin": "\\b(using)\\s*(?!\\(|\\s|var)", + "begin": "(\\b(global)\\b\\s+)?\\b(using)\\s*(?!\\(|\\s|var)", "beginCaptures": { - "1": { + "2": { + "name": "keyword.other.global.cs" + }, + "3": { "name": "keyword.other.using.cs" } }, @@ -574,7 +589,7 @@ }, "storage-modifier": { "name": "storage.modifier.cs", - "match": "(?|\\{|(^\\s*(export|function|class|interface|let|var|const|import|enum|namespace|module|type|abstract|declare)\\s+))", + "end": "(?==>|\\{|(^\\s*(export|function|class|interface|let|var|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|const|import|enum|namespace|module|type|abstract|declare)\\s+))", "patterns": [ { "include": "#comment" @@ -1808,7 +1876,7 @@ }, { "begin": "(?<=:)\\s*", - "end": "(?=\\s|[;),}\\]:\\-\\+]|;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?=\\s|[;),}\\]:\\-\\+]|;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#expression" @@ -1969,7 +2037,7 @@ "name": "storage.type.namespace.js" } }, - "end": "(?<=\\})|(?=;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?<=\\})|(?=;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#comment" @@ -2006,7 +2074,7 @@ "name": "entity.name.type.alias.js" } }, - "end": "(?=\\}|;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?=\\}|;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#comment" @@ -2024,7 +2092,7 @@ "name": "keyword.control.intrinsic.js" } }, - "end": "(?=\\}|;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?=\\}|;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#type" @@ -2038,7 +2106,7 @@ "name": "keyword.operator.assignment.js" } }, - "end": "(?=\\}|;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?=\\}|;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#type" @@ -2229,7 +2297,7 @@ "name": "keyword.control.default.js" } }, - "end": "(?=$|;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?=$|;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#interface-declaration" @@ -2241,7 +2309,7 @@ }, { "name": "meta.export.js", - "begin": "(?:&|{\\?]|(extends\\s+)|$|;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?=[,);}\\]=>:&|{\\?]|(extends\\s+)|$|;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#type-arguments" @@ -3904,7 +3972,7 @@ "name": "keyword.operator.type.annotation.js" } }, - "end": "(?==>|\\{|(^\\s*(export|function|class|interface|let|var|const|import|enum|namespace|module|type|abstract|declare)\\s+))", + "end": "(?==>|\\{|(^\\s*(export|function|class|interface|let|var|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|const|import|enum|namespace|module|type|abstract|declare)\\s+))", "patterns": [ { "include": "#arrow-return-type-body" @@ -3918,7 +3986,7 @@ "name": "meta.arrow.js meta.return.type.arrow.js keyword.operator.type.annotation.js" } }, - "end": "(?==>|\\{|(^\\s*(export|function|class|interface|let|var|const|import|enum|namespace|module|type|abstract|declare)\\s+))", + "end": "(?==>|\\{|(^\\s*(export|function|class|interface|let|var|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|const|import|enum|namespace|module|type|abstract|declare)\\s+))", "contentName": "meta.arrow.js meta.return.type.arrow.js", "patterns": [ { diff --git a/extensions/javascript/syntaxes/JavaScriptReact.tmLanguage.json b/extensions/javascript/syntaxes/JavaScriptReact.tmLanguage.json index b0fb54f1707..b9869694bdd 100644 --- a/extensions/javascript/syntaxes/JavaScriptReact.tmLanguage.json +++ b/extensions/javascript/syntaxes/JavaScriptReact.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/TypeScript-TmLanguage/commit/e0aefd8205cc9d1bc7859cc5babbee0d833dca0f", + "version": "https://github.com/microsoft/TypeScript-TmLanguage/commit/8c7482b94b548eab56da64dbfb30b82589b3f747", "name": "JavaScript (with React support)", "scopeName": "source.js.jsx", "patterns": [ @@ -134,7 +134,7 @@ "name": "keyword.control.flow.js.jsx" } }, - "end": "(?=[;}]|$|;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?=[;}]|$|;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#expression" @@ -299,7 +299,7 @@ { "name": "meta.var.expr.js.jsx", "begin": "(?=(?|\\{|(^\\s*(export|function|class|interface|let|var|const|import|enum|namespace|module|type|abstract|declare)\\s+))", + "end": "(?==>|\\{|(^\\s*(export|function|class|interface|let|var|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|const|import|enum|namespace|module|type|abstract|declare)\\s+))", "patterns": [ { "include": "#comment" @@ -1808,7 +1876,7 @@ }, { "begin": "(?<=:)\\s*", - "end": "(?=\\s|[;),}\\]:\\-\\+]|;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?=\\s|[;),}\\]:\\-\\+]|;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#expression" @@ -1969,7 +2037,7 @@ "name": "storage.type.namespace.js.jsx" } }, - "end": "(?<=\\})|(?=;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?<=\\})|(?=;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#comment" @@ -2006,7 +2074,7 @@ "name": "entity.name.type.alias.js.jsx" } }, - "end": "(?=\\}|;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?=\\}|;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#comment" @@ -2024,7 +2092,7 @@ "name": "keyword.control.intrinsic.js.jsx" } }, - "end": "(?=\\}|;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?=\\}|;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#type" @@ -2038,7 +2106,7 @@ "name": "keyword.operator.assignment.js.jsx" } }, - "end": "(?=\\}|;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?=\\}|;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#type" @@ -2229,7 +2297,7 @@ "name": "keyword.control.default.js.jsx" } }, - "end": "(?=$|;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?=$|;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#interface-declaration" @@ -2241,7 +2309,7 @@ }, { "name": "meta.export.js.jsx", - "begin": "(?:&|{\\?]|(extends\\s+)|$|;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?=[,);}\\]=>:&|{\\?]|(extends\\s+)|$|;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#type-arguments" @@ -3904,7 +3972,7 @@ "name": "keyword.operator.type.annotation.js.jsx" } }, - "end": "(?==>|\\{|(^\\s*(export|function|class|interface|let|var|const|import|enum|namespace|module|type|abstract|declare)\\s+))", + "end": "(?==>|\\{|(^\\s*(export|function|class|interface|let|var|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|const|import|enum|namespace|module|type|abstract|declare)\\s+))", "patterns": [ { "include": "#arrow-return-type-body" @@ -3918,7 +3986,7 @@ "name": "meta.arrow.js.jsx meta.return.type.arrow.js.jsx keyword.operator.type.annotation.js.jsx" } }, - "end": "(?==>|\\{|(^\\s*(export|function|class|interface|let|var|const|import|enum|namespace|module|type|abstract|declare)\\s+))", + "end": "(?==>|\\{|(^\\s*(export|function|class|interface|let|var|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|const|import|enum|namespace|module|type|abstract|declare)\\s+))", "contentName": "meta.arrow.js.jsx meta.return.type.arrow.js.jsx", "patterns": [ { diff --git a/extensions/julia/cgmanifest.json b/extensions/julia/cgmanifest.json index c6cc3e5c1f7..0dac126a5ae 100644 --- a/extensions/julia/cgmanifest.json +++ b/extensions/julia/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "JuliaEditorSupport/atom-language-julia", "repositoryUrl": "https://github.com/JuliaEditorSupport/atom-language-julia", - "commitHash": "7b7801f41ce4ac1303bd17e057dbe677e24f597f" + "commitHash": "ccc0277c9ee9af34a0b50e5fa27a6f5191601b8c" } }, "license": "MIT", diff --git a/extensions/julia/syntaxes/julia.tmLanguage.json b/extensions/julia/syntaxes/julia.tmLanguage.json index bd3acb8d6bb..c4e146ee5e7 100644 --- a/extensions/julia/syntaxes/julia.tmLanguage.json +++ b/extensions/julia/syntaxes/julia.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/JuliaEditorSupport/atom-language-julia/commit/7b7801f41ce4ac1303bd17e057dbe677e24f597f", + "version": "https://github.com/JuliaEditorSupport/atom-language-julia/commit/ccc0277c9ee9af34a0b50e5fa27a6f5191601b8c", "name": "Julia", "scopeName": "source.julia", "comment": "This grammar is used by Atom (Oniguruma), GitHub (PCRE), and VSCode (Oniguruma),\nso all regexps must be compatible with both engines.\n\nSpecs:\n- https://github.com/kkos/oniguruma/blob/master/doc/RE\n- https://www.pcre.org/current/doc/html/", @@ -120,6 +120,26 @@ } ] }, + "comment_tags": { + "patterns": [ + { + "match": "\\bTODO\\b", + "name": "keyword.other.comment-annotation.julia" + }, + { + "match": "\\bFIXME\\b", + "name": "keyword.other.comment-annotation.julia" + }, + { + "match": "\\bCHANGED\\b", + "name": "keyword.other.comment-annotation.julia" + }, + { + "match": "\\bXXX\\b", + "name": "keyword.other.comment-annotation.julia" + } + ] + }, "comment": { "patterns": [ { @@ -133,7 +153,12 @@ } }, "end": "\\n", - "name": "comment.line.number-sign.julia" + "name": "comment.line.number-sign.julia", + "patterns": [ + { + "include": "#comment_tags" + } + ] } ] }, @@ -154,6 +179,9 @@ }, "name": "comment.block.number-sign-equals.julia", "patterns": [ + { + "include": "#comment_tags" + }, { "include": "#comment_block" } @@ -852,6 +880,9 @@ "patterns": [ { "include": "#string_escaped_char" + }, + { + "include": "#string_dollar_sign_interpolate" } ] }, @@ -878,6 +909,9 @@ "patterns": [ { "include": "#string_escaped_char" + }, + { + "include": "#string_dollar_sign_interpolate" } ] } @@ -903,6 +937,10 @@ "name": "variable.interpolation.julia", "comment": "`punctuation.section.embedded`, `constant.escape`,\n& `meta.embedded.line` were considered but appear to have even spottier\nsupport among popular syntaxes.", "patterns": [ + { + "match": "\\bfor\\b", + "name": "keyword.control.julia" + }, { "include": "#parentheses" }, diff --git a/extensions/latex/cgmanifest.json b/extensions/latex/cgmanifest.json index c3031ee87cb..e63e475ecdd 100644 --- a/extensions/latex/cgmanifest.json +++ b/extensions/latex/cgmanifest.json @@ -6,11 +6,11 @@ "git": { "name": "jlelong/vscode-latex-basics", "repositoryUrl": "https://github.com/jlelong/vscode-latex-basics", - "commitHash": "580c019674f82ef887a6dcce7bfc75ab0b58486d" + "commitHash": "30adbfae9dcb0a6477584247ac477f13845d1f5f" } }, "license": "MIT", - "version": "1.5.2", + "version": "1.5.3", "description": "The files in syntaxes/ were originally part of https://github.com/James-Yu/LaTeX-Workshop. They have been extracted in the hope that they can useful outside of the LaTeX-Workshop extension.", "licenseDetail": [ "Copyright (c) vscode-latex-basics authors", diff --git a/extensions/latex/syntaxes/Bibtex.tmLanguage.json b/extensions/latex/syntaxes/Bibtex.tmLanguage.json index c5d2f337972..169136df5f2 100644 --- a/extensions/latex/syntaxes/Bibtex.tmLanguage.json +++ b/extensions/latex/syntaxes/Bibtex.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/jlelong/vscode-latex-basics/commit/4b19be579cb4a3c680f8b4bb613dcebfac826f8b", + "version": "https://github.com/jlelong/vscode-latex-basics/commit/7adad0868ecafbb1df978f1e052d6c3c85c38732", "name": "BibTeX", "scopeName": "text.bibtex", "comment": "Grammar based on description from http://artis.imag.fr/~Xavier.Decoret/resources/xdkbibtex/bibtex_summary.html#comment\n\t\n\tTODO: Does not support @preamble\n\t", @@ -345,7 +345,7 @@ "end": "(?!\\G)", "patterns": [ { - "begin": "%", + "begin": "(?]+?)(>)|(\\S+?)) # The url\n [ \\t]* # Optional whitespace\n (?:\n ((\\().+?(\\))) # Match title in parens…\n | ((\").+?(\")) # or in double quotes…\n | ((').+?(')) # or in single quotes.\n )? # Title is optional\n \\s* # Optional whitespace\n $\n", + "match": "(?x)\n \\s* # Leading whitespace\n (\\[)([^]]+?)(\\])(:) # Reference name\n [ \\t]* # Optional whitespace\n (?:(<)((?:\\\\[<>]|[^<>\\n])*)(>)|(\\S+?)) # The url\n [ \\t]* # Optional whitespace\n (?:\n ((\\().+?(\\))) # Match title in parens…\n | ((\").+?(\")) # or in double quotes…\n | ((').+?(')) # or in single quotes.\n )? # Title is optional\n \\s* # Optional whitespace\n $\n", "name": "meta.link.reference.def.markdown" }, "list_paragraph": { @@ -2641,47 +2641,50 @@ "5": { "name": "punctuation.definition.metadata.markdown" }, - "6": { - "name": "punctuation.definition.link.markdown" - }, "7": { - "name": "markup.underline.link.image.markdown" + "name": "punctuation.definition.link.markdown" }, "8": { - "name": "punctuation.definition.link.markdown" + "name": "markup.underline.link.image.markdown" }, "9": { - "name": "string.other.link.description.title.markdown" + "name": "punctuation.definition.link.markdown" }, "10": { - "name": "punctuation.definition.string.markdown" - }, - "11": { - "name": "punctuation.definition.string.markdown" + "name": "markup.underline.link.image.markdown" }, "12": { "name": "string.other.link.description.title.markdown" }, "13": { - "name": "punctuation.definition.string.markdown" + "name": "punctuation.definition.string.begin.markdown" }, "14": { - "name": "punctuation.definition.string.markdown" + "name": "punctuation.definition.string.end.markdown" }, "15": { "name": "string.other.link.description.title.markdown" }, "16": { - "name": "punctuation.definition.string.markdown" + "name": "punctuation.definition.string.begin.markdown" }, "17": { - "name": "punctuation.definition.string.markdown" + "name": "punctuation.definition.string.end.markdown" }, "18": { + "name": "string.other.link.description.title.markdown" + }, + "19": { + "name": "punctuation.definition.string.begin.markdown" + }, + "20": { + "name": "punctuation.definition.string.end.markdown" + }, + "21": { "name": "punctuation.definition.metadata.markdown" } }, - "match": "(?x)\n (\\!\\[)((?[^\\[\\]\\\\]|\\\\.|\\[\\g*+\\])*+)(\\])\n # Match the link text.\n (\\() # Opening paren for url\n (?) # The url\n [ \\t]* # Optional whitespace\n (?:\n ((\\().+?(\\))) # Match title in parens…\n | ((\").+?(\")) # or in double quotes…\n | ((').+?(')) # or in single quotes.\n )? # Title is optional\n \\s* # Optional whitespace\n (\\))\n", + "match": "(?x)\n (\\!\\[)((?[^\\[\\]\\\\]|\\\\.|\\[\\g*+\\])*+)(\\])\n # Match the link text.\n (\\() # Opening paren for url\n # The url\n [ \\t]*\n (\n (<)((?:\\\\[<>]|[^<>\\n])*)(>)\n | ((?(?>[^\\s()]+)|\\(\\g*\\))*)\n )\n [ \\t]*\n (?:\n ((\\().+?(\\))) # Match title in parens…\n | ((\").+?(\")) # or in double quotes…\n | ((').+?(')) # or in single quotes.\n )? # Title is optional\n \\s* # Optional whitespace\n (\\))\n", "name": "meta.image.inline.markdown" }, "image-ref": { @@ -2876,7 +2879,7 @@ "name": "punctuation.definition.metadata.markdown" } }, - "match": "(?x)\n (\\[)((?[^\\[\\]\\\\]|\\\\.|\\[\\g*+\\])*+)(\\])\n # Match the link text.\n (\\() # Opening paren for url\n # The url\n [ \\t]*\n (\n (<)([^<>\\n]*)(>)\n | ((?(?>[^\\s()]+)|\\(\\g*\\))*)\n )\n [ \\t]*\n # The title \n (?:\n ((\\()[^()]*(\\))) # Match title in parens…\n | ((\")[^\"]*(\")) # or in double quotes…\n | ((')[^']*(')) # or in single quotes.\n )? # Title is optional\n \\s* # Optional whitespace\n (\\))\n", + "match": "(?x)\n (\\[)((?[^\\[\\]\\\\]|\\\\.|\\[\\g*+\\])*+)(\\])\n # Match the link text.\n (\\() # Opening paren for url\n # The url\n [ \\t]*\n (\n (<)((?:\\\\[<>]|[^<>\\n])*)(>)\n | ((?(?>[^\\s()]+)|\\(\\g*\\))*)\n )\n [ \\t]*\n # The title \n (?:\n ((\\()[^()]*(\\))) # Match title in parens…\n | ((\")[^\"]*(\")) # or in double quotes…\n | ((')[^']*(')) # or in single quotes.\n )? # Title is optional\n \\s* # Optional whitespace\n (\\))\n", "name": "meta.link.inline.markdown" }, "link-ref": { diff --git a/extensions/perl/package.json b/extensions/perl/package.json index 8e3fed18d0c..3357e1ff3bd 100644 --- a/extensions/perl/package.json +++ b/extensions/perl/package.json @@ -9,7 +9,7 @@ "vscode": "*" }, "scripts": { - "update-grammar": "node ../node_modules/vscode-grammar-updater/bin textmate/perl.tmbundle Syntaxes/Perl.plist ./syntaxes/perl.tmLanguage.json Syntaxes/Perl%206.tmLanguage ./syntaxes/perl6.tmLanguage.json" + "update-grammar": "node ../node_modules/vscode-grammar-updater/bin textmate/perl.tmbundle Syntaxes/Perl.plist ./syntaxes/perl.tmLanguage.json Syntaxes/Perl%%206.tmLanguage ./syntaxes/perl6.tmLanguage.json" }, "contributes": { "languages": [ diff --git a/extensions/typescript-basics/cgmanifest.json b/extensions/typescript-basics/cgmanifest.json index d0261905ada..4b7b1b2d37a 100644 --- a/extensions/typescript-basics/cgmanifest.json +++ b/extensions/typescript-basics/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "TypeScript-TmLanguage", "repositoryUrl": "https://github.com/microsoft/TypeScript-TmLanguage", - "commitHash": "e0aefd8205cc9d1bc7859cc5babbee0d833dca0f" + "commitHash": "8c7482b94b548eab56da64dbfb30b82589b3f747" } }, "license": "MIT", diff --git a/extensions/typescript-basics/syntaxes/TypeScript.tmLanguage.json b/extensions/typescript-basics/syntaxes/TypeScript.tmLanguage.json index 7ad3622320e..81eeb5b5d96 100644 --- a/extensions/typescript-basics/syntaxes/TypeScript.tmLanguage.json +++ b/extensions/typescript-basics/syntaxes/TypeScript.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/TypeScript-TmLanguage/commit/e0aefd8205cc9d1bc7859cc5babbee0d833dca0f", + "version": "https://github.com/microsoft/TypeScript-TmLanguage/commit/8c7482b94b548eab56da64dbfb30b82589b3f747", "name": "TypeScript", "scopeName": "source.ts", "patterns": [ @@ -134,7 +134,7 @@ "name": "keyword.control.flow.ts" } }, - "end": "(?=[;}]|$|;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?=[;}]|$|;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#expression" @@ -296,7 +296,7 @@ { "name": "meta.var.expr.ts", "begin": "(?=(?|\\{|(^\\s*(export|function|class|interface|let|var|const|import|enum|namespace|module|type|abstract|declare)\\s+))", + "end": "(?==>|\\{|(^\\s*(export|function|class|interface|let|var|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|const|import|enum|namespace|module|type|abstract|declare)\\s+))", "patterns": [ { "include": "#comment" @@ -1805,7 +1873,7 @@ }, { "begin": "(?<=:)\\s*", - "end": "(?=\\s|[;),}\\]:\\-\\+]|;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?=\\s|[;),}\\]:\\-\\+]|;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#expression" @@ -1966,7 +2034,7 @@ "name": "storage.type.namespace.ts" } }, - "end": "(?<=\\})|(?=;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?<=\\})|(?=;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#comment" @@ -2003,7 +2071,7 @@ "name": "entity.name.type.alias.ts" } }, - "end": "(?=\\}|;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?=\\}|;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#comment" @@ -2021,7 +2089,7 @@ "name": "keyword.control.intrinsic.ts" } }, - "end": "(?=\\}|;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?=\\}|;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#type" @@ -2035,7 +2103,7 @@ "name": "keyword.operator.assignment.ts" } }, - "end": "(?=\\}|;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?=\\}|;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#type" @@ -2226,7 +2294,7 @@ "name": "keyword.control.default.ts" } }, - "end": "(?=$|;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?=$|;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#interface-declaration" @@ -2238,7 +2306,7 @@ }, { "name": "meta.export.ts", - "begin": "(?:&|{\\?]|(extends\\s+)|$|;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?=[,);}\\]=>:&|{\\?]|(extends\\s+)|$|;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#type-arguments" @@ -3953,7 +4021,7 @@ "name": "keyword.operator.type.annotation.ts" } }, - "end": "(?==>|\\{|(^\\s*(export|function|class|interface|let|var|const|import|enum|namespace|module|type|abstract|declare)\\s+))", + "end": "(?==>|\\{|(^\\s*(export|function|class|interface|let|var|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|const|import|enum|namespace|module|type|abstract|declare)\\s+))", "patterns": [ { "include": "#arrow-return-type-body" @@ -3967,7 +4035,7 @@ "name": "meta.arrow.ts meta.return.type.arrow.ts keyword.operator.type.annotation.ts" } }, - "end": "(?==>|\\{|(^\\s*(export|function|class|interface|let|var|const|import|enum|namespace|module|type|abstract|declare)\\s+))", + "end": "(?==>|\\{|(^\\s*(export|function|class|interface|let|var|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|const|import|enum|namespace|module|type|abstract|declare)\\s+))", "contentName": "meta.arrow.ts meta.return.type.arrow.ts", "patterns": [ { diff --git a/extensions/typescript-basics/syntaxes/TypeScriptReact.tmLanguage.json b/extensions/typescript-basics/syntaxes/TypeScriptReact.tmLanguage.json index 10b809d93b9..e516ea1a1fa 100644 --- a/extensions/typescript-basics/syntaxes/TypeScriptReact.tmLanguage.json +++ b/extensions/typescript-basics/syntaxes/TypeScriptReact.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/TypeScript-TmLanguage/commit/e0aefd8205cc9d1bc7859cc5babbee0d833dca0f", + "version": "https://github.com/microsoft/TypeScript-TmLanguage/commit/8c7482b94b548eab56da64dbfb30b82589b3f747", "name": "TypeScriptReact", "scopeName": "source.tsx", "patterns": [ @@ -134,7 +134,7 @@ "name": "keyword.control.flow.tsx" } }, - "end": "(?=[;}]|$|;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?=[;}]|$|;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#expression" @@ -299,7 +299,7 @@ { "name": "meta.var.expr.tsx", "begin": "(?=(?|\\{|(^\\s*(export|function|class|interface|let|var|const|import|enum|namespace|module|type|abstract|declare)\\s+))", + "end": "(?==>|\\{|(^\\s*(export|function|class|interface|let|var|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|const|import|enum|namespace|module|type|abstract|declare)\\s+))", "patterns": [ { "include": "#comment" @@ -1808,7 +1876,7 @@ }, { "begin": "(?<=:)\\s*", - "end": "(?=\\s|[;),}\\]:\\-\\+]|;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?=\\s|[;),}\\]:\\-\\+]|;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#expression" @@ -1969,7 +2037,7 @@ "name": "storage.type.namespace.tsx" } }, - "end": "(?<=\\})|(?=;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?<=\\})|(?=;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#comment" @@ -2006,7 +2074,7 @@ "name": "entity.name.type.alias.tsx" } }, - "end": "(?=\\}|;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?=\\}|;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#comment" @@ -2024,7 +2092,7 @@ "name": "keyword.control.intrinsic.tsx" } }, - "end": "(?=\\}|;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?=\\}|;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#type" @@ -2038,7 +2106,7 @@ "name": "keyword.operator.assignment.tsx" } }, - "end": "(?=\\}|;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?=\\}|;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#type" @@ -2229,7 +2297,7 @@ "name": "keyword.control.default.tsx" } }, - "end": "(?=$|;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?=$|;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#interface-declaration" @@ -2241,7 +2309,7 @@ }, { "name": "meta.export.tsx", - "begin": "(?:&|{\\?]|(extends\\s+)|$|;|^\\s*$|(?:^\\s*(?:abstract|async|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|var|while)\\b))", + "end": "(?=[,);}\\]=>:&|{\\?]|(extends\\s+)|$|;|^\\s*$|(?:^\\s*(?:abstract|async|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|var|while)\\b))", "patterns": [ { "include": "#type-arguments" @@ -3904,7 +3972,7 @@ "name": "keyword.operator.type.annotation.tsx" } }, - "end": "(?==>|\\{|(^\\s*(export|function|class|interface|let|var|const|import|enum|namespace|module|type|abstract|declare)\\s+))", + "end": "(?==>|\\{|(^\\s*(export|function|class|interface|let|var|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|const|import|enum|namespace|module|type|abstract|declare)\\s+))", "patterns": [ { "include": "#arrow-return-type-body" @@ -3918,7 +3986,7 @@ "name": "meta.arrow.tsx meta.return.type.arrow.tsx keyword.operator.type.annotation.tsx" } }, - "end": "(?==>|\\{|(^\\s*(export|function|class|interface|let|var|const|import|enum|namespace|module|type|abstract|declare)\\s+))", + "end": "(?==>|\\{|(^\\s*(export|function|class|interface|let|var|(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)|(?:\\bawait\\s+(?:\\busing(?=\\s+(?!in\\b|of\\b(?!\\s*(?:of\\b|=)))[_$[:alpha:]])\\b)\\b)|const|import|enum|namespace|module|type|abstract|declare)\\s+))", "contentName": "meta.arrow.tsx meta.return.type.arrow.tsx", "patterns": [ { diff --git a/extensions/vb/package.json b/extensions/vb/package.json index 801ef7180da..6e5dd080c0b 100644 --- a/extensions/vb/package.json +++ b/extensions/vb/package.json @@ -9,7 +9,7 @@ "vscode": "*" }, "scripts": { - "update-grammar": "node ../node_modules/vscode-grammar-updater/bin textmate/asp.vb.net.tmbundle Syntaxes/ASP%20VB.net.plist ./syntaxes/asp-vb-net.tmlanguage.json" + "update-grammar": "node ../node_modules/vscode-grammar-updater/bin textmate/asp.vb.net.tmbundle Syntaxes/ASP%%20VB.net.plist ./syntaxes/asp-vb-net.tmlanguage.json" }, "contributes": { "languages": [ From 5c98922b5220cad857f33cd96789c17370c17e32 Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Fri, 30 Jun 2023 09:16:21 -0700 Subject: [PATCH 082/926] Avoid use of native private variables (#186756) --- .../browser/editSessionsStorageService.ts | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts b/src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts index 6f9c20338e7..a3a69e50222 100644 --- a/src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts +++ b/src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts @@ -3,8 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -/* eslint-disable local/code-no-native-private */ - import { Disposable } from 'vs/base/common/lifecycle'; import { localize } from 'vs/nls'; import { Action2, MenuId, MenuRegistry, registerAction2 } from 'vs/platform/actions/common/actions'; @@ -40,7 +38,7 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes private serverConfiguration = this.productService['editSessions.store']; private machineClient: IUserDataSyncMachinesService | undefined; - #authenticationInfo: { sessionId: string; token: string; providerId: string } | undefined; + private authenticationInfo: { sessionId: string; token: string; providerId: string } | undefined; private static CACHED_SESSION_STORAGE_KEY = 'editSessionAccountPreference'; private initialized = false; @@ -227,13 +225,13 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes } // If we already have an existing auth session in memory, use that - if (this.#authenticationInfo !== undefined) { + if (this.authenticationInfo !== undefined) { return true; } const authenticationSession = await this.getAuthenticationSession(silent); if (authenticationSession !== undefined) { - this.#authenticationInfo = authenticationSession; + this.authenticationInfo = authenticationSession; this.storeClient.setAuthToken(authenticationSession.token, authenticationSession.providerId); } @@ -436,25 +434,25 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes && e.scope === StorageScope.APPLICATION ) { const newSessionId = this.existingSessionId; - const previousSessionId = this.#authenticationInfo?.sessionId; + const previousSessionId = this.authenticationInfo?.sessionId; if (previousSessionId !== newSessionId) { this.logService.trace(`Resetting authentication state because authentication session ID preference changed from ${previousSessionId} to ${newSessionId}.`); - this.#authenticationInfo = undefined; + this.authenticationInfo = undefined; this.initialized = false; } } } private clearAuthenticationPreference(): void { - this.#authenticationInfo = undefined; + this.authenticationInfo = undefined; this.initialized = false; this.existingSessionId = undefined; this.signedInContext.set(false); } private onDidChangeSessions(e: AuthenticationSessionsChangeEvent): void { - if (this.#authenticationInfo?.sessionId && e.removed.find(session => session.id === this.#authenticationInfo?.sessionId)) { + if (this.authenticationInfo?.sessionId && e.removed.find(session => session.id === this.authenticationInfo?.sessionId)) { this.clearAuthenticationPreference(); } } From 00bfb8704f92be25ef1b36b74ef8db0657dee680 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Fri, 30 Jun 2023 11:10:34 -0700 Subject: [PATCH 083/926] debug-server-ready: fix misbehavor in compound launch configs (#186766) debug-server-rady: fix misbehavor in compound launch configs Fixes #186765 --- extensions/debug-server-ready/src/extension.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/debug-server-ready/src/extension.ts b/extensions/debug-server-ready/src/extension.ts index d5f51150680..307b5e25475 100644 --- a/extensions/debug-server-ready/src/extension.ts +++ b/extensions/debug-server-ready/src/extension.ts @@ -308,8 +308,8 @@ class ServerReadyDetector extends vscode.Disposable { export function activate(context: vscode.ExtensionContext) { - context.subscriptions.push(vscode.debug.onDidChangeActiveDebugSession(session => { - if (session && session.configuration.serverReadyAction) { + context.subscriptions.push(vscode.debug.onDidStartDebugSession(session => { + if (session.configuration.serverReadyAction) { const detector = ServerReadyDetector.start(session); if (detector) { ServerReadyDetector.startListeningTerminalData(); From 13c86674425232969f6da42aa7fa1d748968e044 Mon Sep 17 00:00:00 2001 From: Simon Siefke Date: Fri, 30 Jun 2023 20:15:19 +0200 Subject: [PATCH 084/926] fix: gracefully handle process stdout/stderr EPIPE error (#186717) fix: implement alternative solution for epipe error --- src/vs/code/electron-main/app.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index b907a0693ff..212689e3c33 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -9,7 +9,7 @@ import { validatedIpcMain } from 'vs/base/parts/ipc/electron-main/ipcMain'; import { hostname, release } from 'os'; import { VSBuffer } from 'vs/base/common/buffer'; import { toErrorMessage } from 'vs/base/common/errorMessage'; -import { onUnexpectedError, setUnexpectedErrorHandler } from 'vs/base/common/errors'; +import { isSigPipeError, onUnexpectedError, setUnexpectedErrorHandler } from 'vs/base/common/errors'; import { isEqualOrParent } from 'vs/base/common/extpath'; import { once } from 'vs/base/common/functional'; import { stripComments } from 'vs/base/common/json'; @@ -336,7 +336,11 @@ export class CodeApplication extends Disposable { // We handle uncaught exceptions here to prevent electron from opening a dialog to the user setUnexpectedErrorHandler(error => this.onUnexpectedError(error)); - process.on('uncaughtException', error => onUnexpectedError(error)); + process.on('uncaughtException', error => { + if (!isSigPipeError(error)) { + onUnexpectedError(error); + } + }); process.on('unhandledRejection', (reason: unknown) => onUnexpectedError(reason)); // Dispose on shutdown From 23383fa050d9d4a0d07c61b7fdad5b8d0c4a33cd Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 30 Jun 2023 11:21:02 -0700 Subject: [PATCH 085/926] Dispose the Throttler on ThrottledDelayer (#186767) Fix #185689 --- src/vs/base/common/async.ts | 1 + src/vs/base/test/common/async.test.ts | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/src/vs/base/common/async.ts b/src/vs/base/common/async.ts index 6137c4751f6..e00f3d59653 100644 --- a/src/vs/base/common/async.ts +++ b/src/vs/base/common/async.ts @@ -417,6 +417,7 @@ export class ThrottledDelayer { dispose(): void { this.delayer.dispose(); + this.throttler.dispose(); } } diff --git a/src/vs/base/test/common/async.test.ts b/src/vs/base/test/common/async.test.ts index 2d871251d7c..144c119389a 100644 --- a/src/vs/base/test/common/async.test.ts +++ b/src/vs/base/test/common/async.test.ts @@ -255,6 +255,12 @@ suite('Async', () => { // OK } }); + + test('trigger after dispose throws', async () => { + const throttledDelayer = new async.ThrottledDelayer(100); + throttledDelayer.dispose(); + await assert.rejects(() => throttledDelayer.trigger(async () => { }, 0)); + }); }); test('simple cancel', function () { From d21c749d5235eb5d7b2c0b8d6eadc53cfaa75592 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 30 Jun 2023 11:28:30 -0700 Subject: [PATCH 086/926] Load breakpoint widget keybinding labels dynamically (#186758) * Load breakpoint widget keybinding labels dynamically * Set defaults --- .../contrib/debug/browser/breakpointWidget.ts | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/breakpointWidget.ts b/src/vs/workbench/contrib/debug/browser/breakpointWidget.ts index dfb5cc41436..8cfe6b3730c 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointWidget.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointWidget.ts @@ -39,6 +39,7 @@ import { IEditorOptions, EditorOption } from 'vs/editor/common/config/editorOpti import { PLAINTEXT_LANGUAGE_ID } from 'vs/editor/common/languages/modesRegistry'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; import { defaultSelectBoxStyles } from 'vs/platform/theme/browser/defaultStyles'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; const $ = dom.$; const IPrivateBreakpointWidgetService = createDecorator('privateBreakpointWidgetService'); @@ -96,6 +97,7 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi @ICodeEditorService private readonly codeEditorService: ICodeEditorService, @IConfigurationService private readonly _configurationService: IConfigurationService, @ILanguageFeaturesService private readonly languageFeaturesService: ILanguageFeaturesService, + @IKeybindingService private readonly keybindingService: IKeybindingService, ) { super(editor, { showFrame: true, showArrow: false, frameWidth: 1, isAccessible: true }); @@ -130,13 +132,15 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi } private get placeholder(): string { + const acceptString = this.keybindingService.lookupKeybinding(AcceptBreakpointWidgetInputAction.ID)?.getLabel() || 'Enter'; + const closeString = this.keybindingService.lookupKeybinding(CloseBreakpointWidgetCommand.ID)?.getLabel() || 'Escape'; switch (this.context) { case Context.LOG_MESSAGE: - return nls.localize('breakpointWidgetLogMessagePlaceholder', "Message to log when breakpoint is hit. Expressions within {} are interpolated. 'Enter' to accept, 'Esc' to cancel."); + return nls.localize('breakpointWidgetLogMessagePlaceholder', "Message to log when breakpoint is hit. Expressions within {} are interpolated. '{0}' to accept, '{1}' to cancel.", acceptString, closeString); case Context.HIT_COUNT: - return nls.localize('breakpointWidgetHitCountPlaceholder', "Break when hit count condition is met. 'Enter' to accept, 'Esc' to cancel."); + return nls.localize('breakpointWidgetHitCountPlaceholder', "Break when hit count condition is met. '{0}' to accept, '{1}' to cancel.", acceptString, closeString); default: - return nls.localize('breakpointWidgetExpressionPlaceholder', "Break when expression evaluates to true. 'Enter' to accept, 'Esc' to cancel."); + return nls.localize('breakpointWidgetExpressionPlaceholder', "Break when expression evaluates to true. '{0}' to accept, '{1}' to cancel.", acceptString, closeString); } } @@ -364,10 +368,10 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi } class AcceptBreakpointWidgetInputAction extends EditorCommand { - + static ID = 'breakpointWidget.action.acceptInput'; constructor() { super({ - id: 'breakpointWidget.action.acceptInput', + id: AcceptBreakpointWidgetInputAction.ID, precondition: CONTEXT_BREAKPOINT_WIDGET_VISIBLE, kbOpts: { kbExpr: CONTEXT_IN_BREAKPOINT_WIDGET, @@ -383,10 +387,10 @@ class AcceptBreakpointWidgetInputAction extends EditorCommand { } class CloseBreakpointWidgetCommand extends EditorCommand { - + static ID = 'closeBreakpointWidget'; constructor() { super({ - id: 'closeBreakpointWidget', + id: CloseBreakpointWidgetCommand.ID, precondition: CONTEXT_BREAKPOINT_WIDGET_VISIBLE, kbOpts: { kbExpr: EditorContextKeys.textInputFocus, From e1455259172724474da34966c51f9a58e0f169bf Mon Sep 17 00:00:00 2001 From: Bhavya U Date: Fri, 30 Jun 2023 13:02:14 -0700 Subject: [PATCH 087/926] Walkthrough image fixes. (#186777) * Remove height and width from profiles.svg * Keep non svg images centered * Fix indent --- .../welcomeGettingStarted/browser/media/gettingStarted.css | 2 ++ .../contrib/welcomeGettingStarted/common/media/profiles.svg | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/browser/media/gettingStarted.css b/src/vs/workbench/contrib/welcomeGettingStarted/browser/media/gettingStarted.css index 5019352f008..9c5207bfcfc 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/browser/media/gettingStarted.css +++ b/src/vs/workbench/contrib/welcomeGettingStarted/browser/media/gettingStarted.css @@ -585,6 +585,8 @@ .monaco-workbench .part.editor>.content .gettingStartedContainer .gettingStartedSlideDetails .gettingStartedDetailsContent.image>.getting-started-media { grid-area: title-start / media-start / steps-end / media-end; align-self: unset; + display: flex; + justify-content: center; } .monaco-workbench .part.editor>.content .gettingStartedContainer.width-semi-constrained .gettingStartedSlideDetails .gettingStartedDetailsContent.image>.getting-started-media { diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/common/media/profiles.svg b/src/vs/workbench/contrib/welcomeGettingStarted/common/media/profiles.svg index 19360d21e14..c35a2d9dc5f 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/common/media/profiles.svg +++ b/src/vs/workbench/contrib/welcomeGettingStarted/common/media/profiles.svg @@ -1,4 +1,4 @@ - + @@ -70,4 +70,4 @@ - \ No newline at end of file + From 111fc441e5f2344687b8853da48b744108815102 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 30 Jun 2023 13:16:17 -0700 Subject: [PATCH 088/926] Fix terminal data buffering from pty host Fixes #186782 --- src/vs/platform/terminal/node/ptyService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/terminal/node/ptyService.ts b/src/vs/platform/terminal/node/ptyService.ts index 2e98e0a888f..3d5a98b97cd 100644 --- a/src/vs/platform/terminal/node/ptyService.ts +++ b/src/vs/platform/terminal/node/ptyService.ts @@ -270,7 +270,6 @@ export class PtyService extends Disposable implements IPtyService { } const id = ++this._lastPtyId; const process = new TerminalProcess(shellLaunchConfig, cwd, cols, rows, env, executableEnv, options, this._logService, this._productService); - process.onProcessData(event => this._onProcessData.fire({ id, event })); const processLaunchOptions: IPersistentTerminalProcessLaunchConfig = { env, executableEnv, @@ -282,6 +281,7 @@ export class PtyService extends Disposable implements IPtyService { this._ptys.delete(id); this._onProcessExit.fire({ id, event }); }); + persistentProcess.onProcessData(event => this._onProcessData.fire({ id, event })); persistentProcess.onProcessReplay(event => this._onProcessReplay.fire({ id, event })); persistentProcess.onProcessReady(event => this._onProcessReady.fire({ id, event })); persistentProcess.onProcessOrphanQuestion(() => this._onProcessOrphanQuestion.fire({ id })); From 82e9a14903af04fa2bed56794c04c2f43085e8e9 Mon Sep 17 00:00:00 2001 From: Meghan Kulkarni Date: Fri, 30 Jun 2023 13:19:03 -0700 Subject: [PATCH 089/926] Fix: do not encode external links (#186778) * bug fixes * added label to copyPaste.ts * added localized label to copyPasteLinks file * quick fix for pasting highlight bug * concise if-statement * external urls are not automatically encoded --- .../src/languageFeatures/copyFiles/shared.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/extensions/markdown-language-features/src/languageFeatures/copyFiles/shared.ts b/extensions/markdown-language-features/src/languageFeatures/copyFiles/shared.ts index c9d2aabf568..28a86a22d70 100644 --- a/extensions/markdown-language-features/src/languageFeatures/copyFiles/shared.ts +++ b/extensions/markdown-language-features/src/languageFeatures/copyFiles/shared.ts @@ -17,6 +17,11 @@ enum MediaKind { Audio, } +const externalUriSchemes = [ + 'http', + 'https', +]; + export const mediaFileExtensions = new Map([ // Images ['bmp', MediaKind.Image], @@ -161,7 +166,12 @@ export function createUriListSnippet( insertedLinkCount++; snippet.appendText('['); snippet.appendPlaceholder(escapeBrackets(title) || 'Title', placeholderValue); - snippet.appendText(`](${escapeMarkdownLinkPath(mdPath)})`); + if (externalUriSchemes.includes(uri.scheme)) { + const uriString = uri.toString(true); + snippet.appendText(`](${uriString})`); + } else { + snippet.appendText(`](${escapeMarkdownLinkPath(mdPath)})`); + } } } @@ -292,7 +302,6 @@ function escapeMarkdownLinkPath(mdPath: string): string { function escapeBrackets(value: string): string { value = value.replace(/[\[\]]/g, '\\$&'); - // value = value.replace(/\r\n\r\n/g, '\n\n'); return value; } From 70db8a2e8dde61ede4043507415dcc467e7c0128 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 30 Jun 2023 13:20:15 -0700 Subject: [PATCH 090/926] Log errors in pty host channel Fixes #186775 --- src/vs/platform/terminal/node/ptyService.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/vs/platform/terminal/node/ptyService.ts b/src/vs/platform/terminal/node/ptyService.ts index 2e98e0a888f..0befe7fb7a9 100644 --- a/src/vs/platform/terminal/node/ptyService.ts +++ b/src/vs/platform/terminal/node/ptyService.ts @@ -46,7 +46,13 @@ export function traceRpc(_target: any, key: string, descriptor: any) { if (this.traceRpcArgs.simulatedLatency) { await timeout(this.traceRpcArgs.simulatedLatency); } - const result = await fn.apply(this, args); + let result: any; + try { + result = await fn.apply(this, args); + } catch (e) { + this.traceRpcArgs.logService.error(`[RPC Response] PtyService#${fn.name}`, e); + throw e; + } if (this.traceRpcArgs.logService.getLevel() === LogLevel.Trace) { this.traceRpcArgs.logService.trace(`[RPC Response] PtyService#${fn.name}`, result); } From 7c4b87be5602ef0da470493643669d1ed4322c70 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 30 Jun 2023 13:29:33 -0700 Subject: [PATCH 091/926] Fix renderer losing connection on pty host restart Fixes #186772 Fixes #186773 --- .../electron-main/electronPtyHostStarter.ts | 13 +++++++++---- src/vs/platform/terminal/node/nodePtyHostStarter.ts | 5 +++-- src/vs/platform/terminal/node/ptyHost.ts | 4 ++-- src/vs/platform/terminal/node/ptyHostService.ts | 8 ++++---- .../contrib/terminal/browser/baseTerminalBackend.ts | 7 +++++-- .../electron-sandbox/localTerminalBackend.ts | 2 +- 6 files changed, 24 insertions(+), 15 deletions(-) diff --git a/src/vs/platform/terminal/electron-main/electronPtyHostStarter.ts b/src/vs/platform/terminal/electron-main/electronPtyHostStarter.ts index 887017449c8..d1ecded75fb 100644 --- a/src/vs/platform/terminal/electron-main/electronPtyHostStarter.ts +++ b/src/vs/platform/terminal/electron-main/electronPtyHostStarter.ts @@ -14,12 +14,12 @@ import { UtilityProcess } from 'vs/platform/utilityProcess/electron-main/utility import { Client as MessagePortClient } from 'vs/base/parts/ipc/electron-main/ipc.mp'; import { IpcMainEvent } from 'electron'; import { validatedIpcMain } from 'vs/base/parts/ipc/electron-main/ipcMain'; -import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; import { Emitter } from 'vs/base/common/event'; import { deepClone } from 'vs/base/common/objects'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -export class ElectronPtyHostStarter implements IPtyHostStarter { +export class ElectronPtyHostStarter extends Disposable implements IPtyHostStarter { private utilityProcess: UtilityProcess | undefined = undefined; @@ -35,9 +35,14 @@ export class ElectronPtyHostStarter implements IPtyHostStarter { @ILifecycleMainService private readonly _lifecycleMainService: ILifecycleMainService, @ILogService private readonly _logService: ILogService ) { + super(); + this._lifecycleMainService.onWillShutdown(() => this._onWillShutdown.fire()); // Listen for new windows to establish connection directly to pty host validatedIpcMain.on('vscode:createPtyHostMessageChannel', (e, nonce) => this._onWindowConnection(e, nonce)); + this._register(toDisposable(() => { + validatedIpcMain.removeHandler('vscode:createPtyHostMessageChannel'); + })); } start(lastPtyId: number): IPtyHostConnection { @@ -62,9 +67,9 @@ export class ElectronPtyHostStarter implements IPtyHostStarter { const store = new DisposableStore(); store.add(client); - store.add(this.utilityProcess); store.add(toDisposable(() => { - validatedIpcMain.removeHandler('vscode:createPtyHostMessageChannel'); + this.utilityProcess?.kill(); + this.utilityProcess?.dispose(); this.utilityProcess = undefined; })); diff --git a/src/vs/platform/terminal/node/nodePtyHostStarter.ts b/src/vs/platform/terminal/node/nodePtyHostStarter.ts index 4a589c09a4d..e874dd48a9f 100644 --- a/src/vs/platform/terminal/node/nodePtyHostStarter.ts +++ b/src/vs/platform/terminal/node/nodePtyHostStarter.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { DisposableStore } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { FileAccess } from 'vs/base/common/network'; import { Client, IIPCOptions } from 'vs/base/parts/ipc/node/ipc.cp'; import { IEnvironmentService, INativeEnvironmentService } from 'vs/platform/environment/common/environment'; @@ -11,11 +11,12 @@ import { parsePtyHostDebugPort } from 'vs/platform/environment/node/environmentS import { IReconnectConstants } from 'vs/platform/terminal/common/terminal'; import { IPtyHostConnection, IPtyHostStarter } from 'vs/platform/terminal/node/ptyHost'; -export class NodePtyHostStarter implements IPtyHostStarter { +export class NodePtyHostStarter extends Disposable implements IPtyHostStarter { constructor( private readonly _reconnectConstants: IReconnectConstants, @IEnvironmentService private readonly _environmentService: INativeEnvironmentService ) { + super(); } start(lastPtyId: number): IPtyHostConnection { diff --git a/src/vs/platform/terminal/node/ptyHost.ts b/src/vs/platform/terminal/node/ptyHost.ts index 1d7bb92a105..98212b06a9f 100644 --- a/src/vs/platform/terminal/node/ptyHost.ts +++ b/src/vs/platform/terminal/node/ptyHost.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Event } from 'vs/base/common/event'; -import { DisposableStore } from 'vs/base/common/lifecycle'; +import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { IChannelClient } from 'vs/base/parts/ipc/common/ipc'; export interface IPtyHostConnection { @@ -13,7 +13,7 @@ export interface IPtyHostConnection { readonly onDidProcessExit: Event<{ code: number; signal: string }>; } -export interface IPtyHostStarter { +export interface IPtyHostStarter extends IDisposable { onBeforeWindowConnection?: Event; onWillShutdown?: Event; diff --git a/src/vs/platform/terminal/node/ptyHostService.ts b/src/vs/platform/terminal/node/ptyHostService.ts index 6133ba6d8f7..deb02da1c92 100644 --- a/src/vs/platform/terminal/node/ptyHostService.ts +++ b/src/vs/platform/terminal/node/ptyHostService.ts @@ -52,7 +52,7 @@ export class PtyHostService extends Disposable implements IPtyService { private _ensurePtyHost() { if (!this.__connection) { - [this.__connection, this.__proxy] = this._startPtyHost(); + this._startPtyHost(); } } @@ -102,9 +102,9 @@ export class PtyHostService extends Disposable implements IPtyService { // remote server). registerTerminalPlatformConfiguration(); + this._register(this._ptyHostStarter); this._register(toDisposable(() => this._disposePtyHost())); - this._resolveVariablesRequestStore = this._register(new RequestStore(undefined, this._logService)); this._resolveVariablesRequestStore.onCreateRequest(this._onPtyHostRequestResolveVariables.fire, this._onPtyHostRequestResolveVariables); @@ -145,8 +145,6 @@ export class PtyHostService extends Disposable implements IPtyService { const connection = this._ptyHostStarter.start(lastPtyId); const client = connection.client; - this._onPtyHostStart.fire(); - // Setup heartbeat service and trigger a heartbeat immediately to reset the timeouts const heartbeatService = ProxyChannel.toService(client.getChannel(TerminalIpcChannels.Heartbeat)); heartbeatService.onBeat(() => this._handleHeartbeat()); @@ -181,6 +179,8 @@ export class PtyHostService extends Disposable implements IPtyService { this.__connection = connection; this.__proxy = proxy; + this._onPtyHostStart.fire(); + this._register(this._configurationService.onDidChangeConfiguration(async e => { if (e.affectsConfiguration(TerminalSettingId.IgnoreProcessNames)) { await this._refreshIgnoreProcessNames(); diff --git a/src/vs/workbench/contrib/terminal/browser/baseTerminalBackend.ts b/src/vs/workbench/contrib/terminal/browser/baseTerminalBackend.ts index 7c864ba67f6..3b5ab5a0fad 100644 --- a/src/vs/workbench/contrib/terminal/browser/baseTerminalBackend.ts +++ b/src/vs/workbench/contrib/terminal/browser/baseTerminalBackend.ts @@ -22,6 +22,8 @@ export abstract class BaseTerminalBackend extends Disposable { get isResponsive(): boolean { return !this._isPtyHostUnresponsive; } + protected readonly _onPtyHostConnected = this._register(new Emitter()); + readonly onPtyHostConnected = this._onPtyHostConnected.event; protected readonly _onPtyHostRestart = this._register(new Emitter()); readonly onPtyHostRestart = this._onPtyHostRestart.event; protected readonly _onPtyHostUnresponsive = this._register(new Emitter()); @@ -50,13 +52,14 @@ export abstract class BaseTerminalBackend extends Disposable { })); } if (this._ptyHostController.onPtyHostStart) { + this.onPtyHostConnected(() => hasStarted = true); this._register(this._ptyHostController.onPtyHostStart(() => { this._logService.debug(`The terminal's pty host process is starting`); - // Only fire the event on the 2nd + // Only fire the _restart_ event after it has started if (hasStarted) { + this._logService.trace('IPtyHostController#onPtyHostRestart'); this._onPtyHostRestart.fire(); } - hasStarted = true; statusBarAccessor?.dispose(); this._isPtyHostUnresponsive = false; })); diff --git a/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts b/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts index 1a6142880a3..aec12e8d335 100644 --- a/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts +++ b/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts @@ -107,6 +107,7 @@ class LocalTerminalBackend extends BaseTerminalBackend implements ITerminalBacke // separate interface/service for this one. const client = new MessagePortClient(port, `window:${this._environmentService.window.id}`); clientEventually.complete(client); + this._onPtyHostConnected.fire(); // Attach process listeners this._proxy.onProcessData(e => this._ptys.get(e.id)?.handleData(e.event)); @@ -190,7 +191,6 @@ class LocalTerminalBackend extends BaseTerminalBackend implements ITerminalBacke shouldPersist: boolean ): Promise { const executableEnv = await this._shellEnvironmentService.getShellEnv(); - // TODO: Using _proxy here bypasses the lastPtyId tracking on the main process const id = await this._proxy.createProcess(shellLaunchConfig, cwd, cols, rows, unicodeVersion, env, executableEnv, options, shouldPersist, this._getWorkspaceId(), this._getWorkspaceName()); const pty = new LocalPty(id, shouldPersist, this._proxy); this._ptys.set(id, pty); From 01e8da32452c076acef48f18c7f254e457c2d993 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Fri, 30 Jun 2023 14:13:32 -0700 Subject: [PATCH 092/926] testing: improve performance when ending test with large number of results (#186779) Takes `markTaskComplete` 11,200ms to 70ms when running a 10k test suite, by maintaining a count of computed states for children and avoiding refreshing nodes unnecessarily. For https://github.com/microsoft/vscode-python/issues/21507 --- .../browser/testingProgressUiService.ts | 4 +- .../testing/common/getComputedState.ts | 62 +++++++++++++++---- .../contrib/testing/common/testResult.ts | 18 +----- .../contrib/testing/common/testingStates.ts | 10 +++ .../test/common/testResultService.test.ts | 53 +++++++--------- 5 files changed, 87 insertions(+), 60 deletions(-) diff --git a/src/vs/workbench/contrib/testing/browser/testingProgressUiService.ts b/src/vs/workbench/contrib/testing/browser/testingProgressUiService.ts index 570572ded8f..a3ffe5863c2 100644 --- a/src/vs/workbench/contrib/testing/browser/testingProgressUiService.ts +++ b/src/vs/workbench/contrib/testing/browser/testingProgressUiService.ts @@ -13,8 +13,8 @@ import { ProgressLocation, UnmanagedProgress } from 'vs/platform/progress/common import { IViewsService } from 'vs/workbench/common/views'; import { AutoOpenTesting, getTestingConfiguration, TestingConfigKeys } from 'vs/workbench/contrib/testing/common/configuration'; import { Testing } from 'vs/workbench/contrib/testing/common/constants'; -import { isFailedState } from 'vs/workbench/contrib/testing/common/testingStates'; -import { LiveTestResult, TestResultItemChangeReason, TestStateCount } from 'vs/workbench/contrib/testing/common/testResult'; +import { isFailedState, TestStateCount } from 'vs/workbench/contrib/testing/common/testingStates'; +import { LiveTestResult, TestResultItemChangeReason } from 'vs/workbench/contrib/testing/common/testResult'; import { ITestResultService } from 'vs/workbench/contrib/testing/common/testResultService'; import { TestResultState } from 'vs/workbench/contrib/testing/common/testTypes'; diff --git a/src/vs/workbench/contrib/testing/common/getComputedState.ts b/src/vs/workbench/contrib/testing/common/getComputedState.ts index ad35eaf381e..bcffb8fb522 100644 --- a/src/vs/workbench/contrib/testing/common/getComputedState.ts +++ b/src/vs/workbench/contrib/testing/common/getComputedState.ts @@ -5,7 +5,7 @@ import { Iterable } from 'vs/base/common/iterator'; import { TestResultState } from 'vs/workbench/contrib/testing/common/testTypes'; -import { maxPriority, statePriority } from 'vs/workbench/contrib/testing/common/testingStates'; +import { makeEmptyCounts, maxPriority, statePriority } from 'vs/workbench/contrib/testing/common/testingStates'; /** * Accessor for nodes in get and refresh computed state. @@ -32,18 +32,28 @@ const isDurationAccessor = (accessor: IComputedStateAccessor): accessor is * if it was previously set. */ -const getComputedState = (accessor: IComputedStateAccessor, node: T, force = false) => { +const getComputedState = (accessor: IComputedStateAccessor, node: T, force = false) => { let computed = accessor.getCurrentComputedState(node); if (computed === undefined || force) { computed = accessor.getOwnState(node) ?? TestResultState.Unset; + let childrenCount = 0; + const stateMap = makeEmptyCounts(); + for (const child of accessor.getChildren(node)) { const childComputed = getComputedState(accessor, child); + childrenCount++; + stateMap[childComputed]++; + // If all children are skipped, make the current state skipped too if unset (#131537) computed = childComputed === TestResultState.Skipped && computed === TestResultState.Unset ? TestResultState.Skipped : maxPriority(computed, childComputed); } + if (childrenCount > LARGE_NODE_THRESHOLD) { + largeNodeChildrenStates.set(node, stateMap); + } + accessor.setComputedState(node, computed); } @@ -72,11 +82,19 @@ const getComputedDuration = (accessor: IComputedStateAndDurationAccessor, return computed; }; +const LARGE_NODE_THRESHOLD = 64; + +/** + * Map of how many nodes have in each state. This is used to optimize state + * computation in large nodes with children above the `LARGE_NODE_THRESHOLD`. + */ +const largeNodeChildrenStates = new WeakMap(); + /** * Refreshes the computed state for the node and its parents. Any changes * elements cause `addUpdated` to be called. */ -export const refreshComputedState = ( +export const refreshComputedState = ( accessor: IComputedStateAccessor, node: T, explicitNewComputedState?: TestResultState, @@ -92,28 +110,46 @@ export const refreshComputedState = ( accessor.setComputedState(node, newState); toUpdate.add(node); - if (newPriority > oldPriority) { - // Update all parents to ensure they're at least this priority. - for (const parent of accessor.getParents(node)) { - const prev = accessor.getCurrentComputedState(parent); + let moveFromState = oldState; + let moveToState = newState; + + for (const parent of accessor.getParents(node)) { + const lnm = largeNodeChildrenStates.get(parent); + if (lnm) { + lnm[moveFromState]--; + lnm[moveToState]++; + } + + const prev = accessor.getCurrentComputedState(parent); + if (newPriority > oldPriority) { + // Update all parents to ensure they're at least this priority. if (prev !== undefined && statePriority[prev] >= newPriority) { break; } + if (lnm && lnm[moveToState] > 1) { + break; + } + + // moveToState remains the same, the new higher priority node state accessor.setComputedState(parent, newState); toUpdate.add(parent); - } - } else if (newPriority < oldPriority) { - // Re-render all parents of this node whose computed priority might have come from this node - for (const parent of accessor.getParents(node)) { - const prev = accessor.getCurrentComputedState(parent); + } else /* newProirity < oldPriority */ { + // Update all parts whose statese might have been based on this one if (prev === undefined || statePriority[prev] > oldPriority) { break; } - accessor.setComputedState(parent, getComputedState(accessor, parent, true)); + if (lnm && lnm[moveFromState] > 0) { + break; + } + + moveToState = getComputedState(accessor, parent, true); + accessor.setComputedState(parent, moveToState); toUpdate.add(parent); } + + moveFromState = prev; } } diff --git a/src/vs/workbench/contrib/testing/common/testResult.ts b/src/vs/workbench/contrib/testing/common/testResult.ts index c2130600cdb..fad1eefcadb 100644 --- a/src/vs/workbench/contrib/testing/common/testResult.ts +++ b/src/vs/workbench/contrib/testing/common/testResult.ts @@ -14,7 +14,7 @@ import { IComputedStateAccessor, refreshComputedState } from 'vs/workbench/contr import { IObservableValue, MutableObservableValue, staticObservableValue } from 'vs/workbench/contrib/testing/common/observableValue'; import { TestCoverage } from 'vs/workbench/contrib/testing/common/testCoverage'; import { TestId } from 'vs/workbench/contrib/testing/common/testId'; -import { maxPriority, statesInOrder, terminalStatePriorities } from 'vs/workbench/contrib/testing/common/testingStates'; +import { makeEmptyCounts, maxPriority, statesInOrder, terminalStatePriorities, TestStateCount } from 'vs/workbench/contrib/testing/common/testingStates'; import { getMarkId, IRichLocation, ISerializedTestResults, ITestItem, ITestMessage, ITestOutputMessage, ITestRunTask, ITestTaskState, ResolvedTestRunRequest, TestItemExpandState, TestMessageType, TestResultItem, TestResultState } from 'vs/workbench/contrib/testing/common/testTypes'; export interface ITestRunTaskResults extends ITestRunTask { @@ -185,20 +185,6 @@ export const resultItemParents = function* (results: ITestResult, item: TestResu } }; -/** - * Count of the number of tests in each run state. - */ -export type TestStateCount = { [K in TestResultState]: number }; - -export const makeEmptyCounts = () => { - const o: Partial = {}; - for (const state of statesInOrder) { - o[state] = 0; - } - - return o as TestStateCount; -}; - export const maxCountPriority = (counts: Readonly) => { for (const state of statesInOrder) { if (counts[state] > 0) { @@ -266,7 +252,7 @@ export class LiveTestResult implements ITestResult { /** * @inheritdoc */ - public readonly counts: { [K in TestResultState]: number } = makeEmptyCounts(); + public readonly counts = makeEmptyCounts(); /** * @inheritdoc diff --git a/src/vs/workbench/contrib/testing/common/testingStates.ts b/src/vs/workbench/contrib/testing/common/testingStates.ts index 0ec1fb91432..98b246bda2f 100644 --- a/src/vs/workbench/contrib/testing/common/testingStates.ts +++ b/src/vs/workbench/contrib/testing/common/testingStates.ts @@ -69,3 +69,13 @@ export const terminalStatePriorities: { [key in TestResultState]?: number } = { [TestResultState.Failed]: 2, [TestResultState.Errored]: 3, }; + +/** + * Count of the number of tests in each run state. + */ +export type TestStateCount = { [K in TestResultState]: number }; + +export const makeEmptyCounts = (): TestStateCount => { + // shh! don't tell anyone this is actually an array! + return new Uint32Array(statesInOrder.length) as any as { [K in TestResultState]: number }; +}; diff --git a/src/vs/workbench/contrib/testing/test/common/testResultService.test.ts b/src/vs/workbench/contrib/testing/test/common/testResultService.test.ts index 6acc4b85847..6e3dbf4ee54 100644 --- a/src/vs/workbench/contrib/testing/test/common/testResultService.test.ts +++ b/src/vs/workbench/contrib/testing/test/common/testResultService.test.ts @@ -10,10 +10,11 @@ import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKe import { NullLogService } from 'vs/platform/log/common/log'; import { TestId } from 'vs/workbench/contrib/testing/common/testId'; import { TestProfileService } from 'vs/workbench/contrib/testing/common/testProfileService'; -import { HydratedTestResult, LiveTestResult, makeEmptyCounts, resultItemParents, TaskRawOutput, TestResultItemChange, TestResultItemChangeReason } from 'vs/workbench/contrib/testing/common/testResult'; +import { HydratedTestResult, LiveTestResult, resultItemParents, TaskRawOutput, TestResultItemChange, TestResultItemChangeReason } from 'vs/workbench/contrib/testing/common/testResult'; import { TestResultService } from 'vs/workbench/contrib/testing/common/testResultService'; import { InMemoryResultStorage, ITestResultStorage } from 'vs/workbench/contrib/testing/common/testResultStorage'; import { ITestTaskState, ResolvedTestRunRequest, TestResultItem, TestResultState, TestRunProfileBitset } from 'vs/workbench/contrib/testing/common/testTypes'; +import { makeEmptyCounts } from 'vs/workbench/contrib/testing/common/testingStates'; import { getInitializedMainTestCollection, testStubs, TestTestCollection } from 'vs/workbench/contrib/testing/test/common/testStubs'; import { TestStorageService } from 'vs/workbench/test/common/workbenchTestServices'; @@ -93,27 +94,24 @@ suite('Workbench - Test Results Service', () => { }); test('initializes with valid counts', () => { - assert.deepStrictEqual(r.counts, { - ...makeEmptyCounts(), - [TestResultState.Unset]: 4, - }); + const c = makeEmptyCounts(); + c[TestResultState.Unset] = 4; + assert.deepStrictEqual(r.counts, c); }); test('setAllToState', () => { changed.clear(); r.setAllToStatePublic(TestResultState.Queued, 't', (_, t) => t.item.label !== 'root'); - assert.deepStrictEqual(r.counts, { - ...makeEmptyCounts(), - [TestResultState.Unset]: 1, - [TestResultState.Queued]: 3, - }); + const c = makeEmptyCounts(); + c[TestResultState.Unset] = 1; + c[TestResultState.Queued] = 3; + assert.deepStrictEqual(r.counts, c); r.setAllToStatePublic(TestResultState.Failed, 't', (_, t) => t.item.label !== 'root'); - assert.deepStrictEqual(r.counts, { - ...makeEmptyCounts(), - [TestResultState.Unset]: 1, - [TestResultState.Failed]: 3, - }); + const c2 = makeEmptyCounts(); + c2[TestResultState.Unset] = 1; + c2[TestResultState.Failed] = 3; + assert.deepStrictEqual(r.counts, c2); assert.deepStrictEqual(r.getStateById(new TestId(['ctrlId', 'id-a']).toString())?.ownComputedState, TestResultState.Failed); assert.deepStrictEqual(r.getStateById(new TestId(['ctrlId', 'id-a']).toString())?.tasks[0].state, TestResultState.Failed); @@ -134,11 +132,10 @@ suite('Workbench - Test Results Service', () => { changed.clear(); const testId = new TestId(['ctrlId', 'id-a', 'id-aa']).toString(); r.updateState(testId, 't', TestResultState.Running); - assert.deepStrictEqual(r.counts, { - ...makeEmptyCounts(), - [TestResultState.Unset]: 3, - [TestResultState.Running]: 1, - }); + const c = makeEmptyCounts(); + c[TestResultState.Running] = 1; + c[TestResultState.Unset] = 3; + assert.deepStrictEqual(r.counts, c); assert.deepStrictEqual(r.getStateById(testId)?.ownComputedState, TestResultState.Running); // update computed state: assert.deepStrictEqual(r.getStateById(tests.root.id)?.computedState, TestResultState.Running); @@ -161,10 +158,9 @@ suite('Workbench - Test Results Service', () => { test('ignores outside run', () => { changed.clear(); r.updateState(new TestId(['ctrlId', 'id-b']).toString(), 't', TestResultState.Running); - assert.deepStrictEqual(r.counts, { - ...makeEmptyCounts(), - [TestResultState.Unset]: 4, - }); + const c = makeEmptyCounts(); + c[TestResultState.Unset] = 4; + assert.deepStrictEqual(r.counts, c); assert.deepStrictEqual(r.getStateById(new TestId(['ctrlId', 'id-b']).toString()), undefined); }); @@ -175,11 +171,10 @@ suite('Workbench - Test Results Service', () => { r.markComplete(); - assert.deepStrictEqual(r.counts, { - ...makeEmptyCounts(), - [TestResultState.Passed]: 1, - [TestResultState.Unset]: 3, - }); + const c = makeEmptyCounts(); + c[TestResultState.Unset] = 3; + c[TestResultState.Passed] = 1; + assert.deepStrictEqual(r.counts, c); assert.deepStrictEqual(r.getStateById(tests.root.id)?.ownComputedState, TestResultState.Unset); assert.deepStrictEqual(r.getStateById(new TestId(['ctrlId', 'id-a', 'id-aa']).toString())?.ownComputedState, TestResultState.Passed); From 303f9044a8a71b92def7de93266787193b8d0938 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 30 Jun 2023 14:34:47 -0700 Subject: [PATCH 093/926] Avoid localizing markdown syntax in a couple strings (#186795) * Avoid localizing markdown syntax in a couple strings Fix #172578 * Non-link text prevents this from showing as a button --- .../contrib/debug/browser/welcomeView.ts | 22 ++----------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/welcomeView.ts b/src/vs/workbench/contrib/debug/browser/welcomeView.ts index 3d993dd5066..78d741bf5ff 100644 --- a/src/vs/workbench/contrib/debug/browser/welcomeView.ts +++ b/src/vs/workbench/contrib/debug/browser/welcomeView.ts @@ -123,16 +123,7 @@ viewsRegistry.registerViewWelcomeContent(WelcomeView.ID, { let debugKeybindingLabel = ''; viewsRegistry.registerViewWelcomeContent(WelcomeView.ID, { - content: localize( - { - key: 'runAndDebugAction', - comment: [ - '{0} will be replaced with a keybinding', - 'Please do not translate the word "command", it is part of our internal syntax which must not change', - '{Locked="](command:{1})"}' - ] - }, - "[Run and Debug{0}](command:{1})", debugKeybindingLabel, DEBUG_START_COMMAND_ID), + content: `[${localize('runAndDebugAction', "Run and Debug")}${debugKeybindingLabel}](command:${DEBUG_START_COMMAND_ID})`, when: CONTEXT_DEBUGGERS_AVAILABLE, group: ViewContentGroups.Debug, // Allow inserting more buttons directly after this one (by setting order to 1). @@ -140,16 +131,7 @@ viewsRegistry.registerViewWelcomeContent(WelcomeView.ID, { }); viewsRegistry.registerViewWelcomeContent(WelcomeView.ID, { - content: localize( - { - key: 'detectThenRunAndDebug', - comment: [ - 'Please do not translate the word "command", it is part of our internal syntax which must not change', - '{Locked="](command:{0})"}', - - ] - }, - "[Show all automatic debug configurations](command:{0}).", SELECT_AND_START_ID), + content: `[${localize('detectThenRunAndDebug', "Show all automatic debug configurations")}](command:${SELECT_AND_START_ID}).`, when: CONTEXT_DEBUGGERS_AVAILABLE, group: ViewContentGroups.Debug, order: 10 From 448d14e907052a309c59698d1253c50aa5613dca Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 30 Jun 2023 15:04:59 -0700 Subject: [PATCH 094/926] Make breakpoint data available in EH immediately, not after a call to debug API (#186796) Fix #169699 --- .../src/singlefolder-tests/debug.test.ts | 12 ++- .../api/browser/mainThreadDebugService.ts | 73 +++++++++---------- .../workbench/api/common/extHost.protocol.ts | 1 - .../api/common/extHostDebugService.ts | 24 +----- 4 files changed, 46 insertions(+), 64 deletions(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/debug.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/debug.test.ts index 189bb8e3747..84226f62988 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/debug.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/debug.test.ts @@ -6,12 +6,22 @@ import * as assert from 'assert'; import { basename } from 'path'; import { commands, debug, Disposable, window, workspace } from 'vscode'; -import { assertNoRpc, disposeAll } from '../utils'; +import { assertNoRpc, createRandomFile, disposeAll } from '../utils'; suite('vscode API - debug', function () { teardown(assertNoRpc); + test('breakpoints are available before accessing debug extension API', async () => { + const file = await createRandomFile(undefined, undefined, '.js'); + const doc = await workspace.openTextDocument(file); + await window.showTextDocument(doc); + await commands.executeCommand('editor.debug.action.toggleBreakpoint'); + + assert.strictEqual(debug.breakpoints.length, 1); + await commands.executeCommand('editor.debug.action.toggleBreakpoint'); + }); + test('breakpoints', async function () { assert.strictEqual(debug.breakpoints.length, 0); let onDidChangeBreakpointsCounter = 0; diff --git a/src/vs/workbench/api/browser/mainThreadDebugService.ts b/src/vs/workbench/api/browser/mainThreadDebugService.ts index 6f64456060a..79fe81e21d6 100644 --- a/src/vs/workbench/api/browser/mainThreadDebugService.ts +++ b/src/vs/workbench/api/browser/mainThreadDebugService.ts @@ -22,7 +22,6 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb private readonly _proxy: ExtHostDebugServiceShape; private readonly _toDispose = new DisposableStore(); - private _breakpointEventsActive: boolean | undefined; private readonly _debugAdapters: Map; private _debugAdaptersHandleCounter = 1; private readonly _debugConfigurationProviders: Map; @@ -79,6 +78,40 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb this._proxy.$acceptStackFrameFocus(dto); } })); + this.sendBreakpointsAndListen(); + } + + private sendBreakpointsAndListen(): void { + // set up a handler to send more + this._toDispose.add(this.debugService.getModel().onDidChangeBreakpoints(e => { + // Ignore session only breakpoint events since they should only reflect in the UI + if (e && !e.sessionOnly) { + const delta: IBreakpointsDeltaDto = {}; + if (e.added) { + delta.added = this.convertToDto(e.added); + } + if (e.removed) { + delta.removed = e.removed.map(x => x.getId()); + } + if (e.changed) { + delta.changed = this.convertToDto(e.changed); + } + + if (delta.added || delta.removed || delta.changed) { + this._proxy.$acceptBreakpointsDelta(delta); + } + } + })); + + // send all breakpoints + const bps = this.debugService.getModel().getBreakpoints(); + const fbps = this.debugService.getModel().getFunctionBreakpoints(); + const dbps = this.debugService.getModel().getDataBreakpoints(); + if (bps.length > 0 || fbps.length > 0) { + this._proxy.$acceptBreakpointsDelta({ + added: this.convertToDto(bps).concat(this.convertToDto(fbps)).concat(this.convertToDto(dbps)) + }); + } } public dispose(): void { @@ -108,44 +141,6 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb this._toDispose.add(this.debugService.getAdapterManager().registerDebugAdapterFactory(debugTypes, this)); } - public $startBreakpointEvents(): void { - - if (!this._breakpointEventsActive) { - this._breakpointEventsActive = true; - - // set up a handler to send more - this._toDispose.add(this.debugService.getModel().onDidChangeBreakpoints(e => { - // Ignore session only breakpoint events since they should only reflect in the UI - if (e && !e.sessionOnly) { - const delta: IBreakpointsDeltaDto = {}; - if (e.added) { - delta.added = this.convertToDto(e.added); - } - if (e.removed) { - delta.removed = e.removed.map(x => x.getId()); - } - if (e.changed) { - delta.changed = this.convertToDto(e.changed); - } - - if (delta.added || delta.removed || delta.changed) { - this._proxy.$acceptBreakpointsDelta(delta); - } - } - })); - - // send all breakpoints - const bps = this.debugService.getModel().getBreakpoints(); - const fbps = this.debugService.getModel().getFunctionBreakpoints(); - const dbps = this.debugService.getModel().getDataBreakpoints(); - if (bps.length > 0 || fbps.length > 0) { - this._proxy.$acceptBreakpointsDelta({ - added: this.convertToDto(bps).concat(this.convertToDto(fbps)).concat(this.convertToDto(dbps)) - }); - } - } - } - public $registerBreakpoints(DTOs: Array): Promise { for (const dto of DTOs) { diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index eea591fd26c..c8b82a40e92 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1404,7 +1404,6 @@ export interface MainThreadDebugServiceShape extends IDisposable { $customDebugAdapterRequest(id: DebugSessionUUID, command: string, args: any): Promise; $getDebugProtocolBreakpoint(id: DebugSessionUUID, breakpoinId: string): Promise; $appendDebugConsole(value: string): void; - $startBreakpointEvents(): void; $registerBreakpoints(breakpoints: Array): Promise; $unregisterBreakpoints(breakpointIds: string[], functionBreakpointIds: string[], dataBreakpointIds: string[]): Promise; } diff --git a/src/vs/workbench/api/common/extHostDebugService.ts b/src/vs/workbench/api/common/extHostDebugService.ts index 6ba0e4f9b30..a9d439ecc47 100644 --- a/src/vs/workbench/api/common/extHostDebugService.ts +++ b/src/vs/workbench/api/common/extHostDebugService.ts @@ -89,7 +89,6 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E get activeDebugConsole(): vscode.DebugConsole { return this._activeDebugConsole.value; } private _breakpoints: Map; - private _breakpointEventsActive: boolean; private readonly _onDidChangeBreakpoints: Emitter; @@ -128,18 +127,13 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E this._debugServiceProxy = extHostRpcService.getProxy(MainContext.MainThreadDebugService); - this._onDidChangeBreakpoints = new Emitter({ - onWillAddFirstListener: () => { - this.startBreakpoints(); - } - }); + this._onDidChangeBreakpoints = new Emitter(); this._onDidChangeStackFrameFocus = new Emitter(); this._activeDebugConsole = new ExtHostDebugConsole(this._debugServiceProxy); this._breakpoints = new Map(); - this._breakpointEventsActive = false; this._extensionService.getExtensionRegistry().then((extensionRegistry: ExtensionDescriptionRegistry) => { extensionRegistry.onDidChange(_ => { @@ -211,18 +205,12 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E } get breakpoints(): vscode.Breakpoint[] { - - this.startBreakpoints(); - const result: vscode.Breakpoint[] = []; this._breakpoints.forEach(bp => result.push(bp)); return result; } public addBreakpoints(breakpoints0: vscode.Breakpoint[]): Promise { - - this.startBreakpoints(); - // filter only new breakpoints const breakpoints = breakpoints0.filter(bp => { const id = bp.id; @@ -278,9 +266,6 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E } public removeBreakpoints(breakpoints0: vscode.Breakpoint[]): Promise { - - this.startBreakpoints(); - // remove from array const breakpoints = breakpoints0.filter(b => this._breakpoints.delete(b.id)); @@ -835,13 +820,6 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E return undefined; } - private startBreakpoints() { - if (!this._breakpointEventsActive) { - this._breakpointEventsActive = true; - this._debugServiceProxy.$startBreakpointEvents(); - } - } - private fireBreakpointChanges(added: vscode.Breakpoint[], removed: vscode.Breakpoint[], changed: vscode.Breakpoint[]) { if (added.length > 0 || removed.length > 0 || changed.length > 0) { this._onDidChangeBreakpoints.fire(Object.freeze({ From 5556ac2dd568f8c24f12393c493a195e8986c66b Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 30 Jun 2023 15:10:49 -0700 Subject: [PATCH 095/926] Fix chat icon in light theme (#186797) --- src/vs/workbench/contrib/chat/browser/media/chatEditor.css | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/chat/browser/media/chatEditor.css b/src/vs/workbench/contrib/chat/browser/media/chatEditor.css index e4e59010a20..fb57658b021 100644 --- a/src/vs/workbench/contrib/chat/browser/media/chatEditor.css +++ b/src/vs/workbench/contrib/chat/browser/media/chatEditor.css @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +.vs .show-file-icons .chat-editor-label.file-icon::before, .show-file-icons .chat-editor-label.file-icon::before { content: '\EAC7'; font-family: 'codicon'; From 6b66c59b48bd2cd3153000122b6f7ed5282e57d0 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 30 Jun 2023 15:39:00 -0700 Subject: [PATCH 096/926] Reuse chat editor count in name (#186799) Fix microsoft/vscode-copilot#149 --- .../contrib/chat/browser/chatEditorInput.ts | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatEditorInput.ts b/src/vs/workbench/contrib/chat/browser/chatEditorInput.ts index 8ec44820262..3e7dbb511f5 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditorInput.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditorInput.ts @@ -5,7 +5,7 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { Emitter } from 'vs/base/common/event'; -import { Disposable } from 'vs/base/common/lifecycle'; +import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; import * as nls from 'vs/nls'; @@ -18,9 +18,10 @@ import { IChatModel } from 'vs/workbench/contrib/chat/common/chatModel'; import { IChatService } from 'vs/workbench/contrib/chat/common/chatService'; export class ChatEditorInput extends EditorInput { + static readonly countsInUse = new Set(); + static readonly TypeID: string = 'workbench.input.chatSession'; static readonly EditorID: string = 'workbench.editor.chatSession'; - static count = 0; private readonly inputCount: number; public sessionId: string | undefined; @@ -33,6 +34,15 @@ export class ChatEditorInput extends EditorInput { return ChatUri.generate(handle); } + static getNextCount(): number { + let count = 0; + while (ChatEditorInput.countsInUse.has(count)) { + count++; + } + + return count; + } + constructor( readonly resource: URI, readonly options: IChatEditorOptions, @@ -47,7 +57,9 @@ export class ChatEditorInput extends EditorInput { this.sessionId = 'sessionId' in options.target ? options.target.sessionId : undefined; this.providerId = 'providerId' in options.target ? options.target.providerId : undefined; - this.inputCount = ChatEditorInput.count++; + this.inputCount = ChatEditorInput.getNextCount(); + ChatEditorInput.countsInUse.add(this.inputCount); + this._register(toDisposable(() => ChatEditorInput.countsInUse.delete(this.inputCount))); } override get editorId(): string | undefined { From 61718f8b6d9bfeebd180933bc15f5b091e358a95 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 30 Jun 2023 15:39:35 -0700 Subject: [PATCH 097/926] Don't show chat toolbars when chat doesn't have DOM focus (#186801) Fix microsoft/vscode-copilot#139 --- src/vs/workbench/contrib/chat/browser/media/chat.css | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/browser/media/chat.css b/src/vs/workbench/contrib/chat/browser/media/chat.css index 8c43f0de917..75a3d125a98 100644 --- a/src/vs/workbench/contrib/chat/browser/media/chat.css +++ b/src/vs/workbench/contrib/chat/browser/media/chat.css @@ -66,7 +66,9 @@ } .monaco-list-row:not(.focused) .interactive-item-container:not(:hover) .header .monaco-toolbar, -.monaco-list-row:not(.focused) .interactive-item-container:not(:hover) .header .monaco-toolbar .action-label { +.monaco-list:not(:focus-within) .monaco-list-row .interactive-item-container:not(:hover) .header .monaco-toolbar, +.monaco-list-row:not(.focused) .interactive-item-container:not(:hover) .header .monaco-toolbar .action-label, +.monaco-list:not(:focus-within) .monaco-list-row .interactive-item-container:not(:hover) .header .monaco-toolbar .action-label { /* Also apply this rule to the .action-label directly to work around a strange issue- when the toolbar is hidden without that second rule, tabbing from the list container into a list item doesn't work and the tab key doesn't do anything. */ From de68ea3b1a3c4caec34ff9bb4a5a0a260400d1c1 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Fri, 30 Jun 2023 16:28:25 -0700 Subject: [PATCH 098/926] testing: fix state not updating when failing peek opens (#186802) * testing: improve performance when ending test with large number of results Takes `markTaskComplete` 11,200ms to 70ms when running a 10k test suite, by maintaining a count of computed states for children and avoiding refreshing nodes unnecessarily. For https://github.com/microsoft/vscode-python/issues/21507 * testing: fix state not updating when failing peek opens Fixes #186376 --- .../ui/tree/compressedObjectTreeModel.ts | 2 +- .../testing/browser/testingOutputPeek.ts | 23 ++++++++++++++----- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/vs/base/browser/ui/tree/compressedObjectTreeModel.ts b/src/vs/base/browser/ui/tree/compressedObjectTreeModel.ts index 241c26daf23..2104ebeb01d 100644 --- a/src/vs/base/browser/ui/tree/compressedObjectTreeModel.ts +++ b/src/vs/base/browser/ui/tree/compressedObjectTreeModel.ts @@ -146,7 +146,7 @@ export class CompressedObjectTreeModel, TFilterData e children: Iterable> = Iterable.empty(), options: IObjectTreeModelSetChildrenOptions, ): void { - // Diffs must be deem, since the compression can affect nested elements. + // Diffs must be deep, since the compression can affect nested elements. // @see https://github.com/microsoft/vscode/pull/114237#issuecomment-759425034 const diffIdentityProvider = options.diffIdentityProvider && wrapIdentityProvider(options.diffIdentityProvider); diff --git a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts index 1a848d6ea4b..134ec676516 100644 --- a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts +++ b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts @@ -1670,7 +1670,7 @@ class OutputPeekTree extends Disposable { taskChildrenToUpdate.clear(); }, 300)); - const handleNewResults = (result: LiveTestResult) => { + const attachToResults = (result: LiveTestResult) => { const resultNode = cc.get(result)! as TestResultElement; const disposable = new DisposableStore(); disposable.add(result.onNewTask(() => { @@ -1713,7 +1713,7 @@ class OutputPeekTree extends Disposable { disposable.dispose(); })); - this.tree.expand(resultNode, true); + return resultNode; }; this._register(results.onResultsChanged(e => { @@ -1737,7 +1737,7 @@ class OutputPeekTree extends Disposable { this.tree.collapse(child.element, false); } - handleNewResults(e.started); + this.tree.expand(attachToResults(e.started), true); } })); @@ -1749,7 +1749,7 @@ class OutputPeekTree extends Disposable { } }; - this._register(onDidReveal(({ subject, preserveFocus = false }) => { + this._register(onDidReveal(async ({ subject, preserveFocus = false }) => { if (subject instanceof TaskSubject) { const resultItem = this.tree.getNode(null).children.find(c => (c.element as TestResultElement)?.id === subject.resultId); if (resultItem) { @@ -1798,6 +1798,11 @@ class OutputPeekTree extends Disposable { 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) { @@ -1880,8 +1885,6 @@ class TestRunElementRenderer implements ICompressibleTreeRenderer, _index: number, templateData: TemplateData): void { - templateData.elementDisposable.clear(); - templateData.elementDisposable.add(element.element.onDidChange(() => this.doRender(element.element, templateData))); this.doRender(element.element, templateData); } @@ -1890,7 +1893,15 @@ class TestRunElementRenderer implements ICompressibleTreeRenderer this.doRender(element, templateData))); + this.doRenderInner(element, templateData); + } + + /** Called, and may be re-called, to render or re-render an element */ + private doRenderInner(element: ITreeElement, templateData: TemplateData) { if (element.labelWithIcons) { dom.reset(templateData.label, ...element.labelWithIcons); } else if (element.description) { From 77dc5070b253ed9c48e6740604a06d483c6b8e93 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Fri, 30 Jun 2023 16:29:04 -0700 Subject: [PATCH 099/926] tree: add fast path for children updates in compressedObjectTreeModel (#186804) I noticed in testing that more things were getting rerendered than they should have been. This adds a fast path so that, if the elements in a compressed node don't change when its children are updated, we only update the children instead of replacing the entire node from the parent collection. --- .../base/browser/ui/tree/compressedObjectTreeModel.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/vs/base/browser/ui/tree/compressedObjectTreeModel.ts b/src/vs/base/browser/ui/tree/compressedObjectTreeModel.ts index 2104ebeb01d..c7a6f5e0634 100644 --- a/src/vs/base/browser/ui/tree/compressedObjectTreeModel.ts +++ b/src/vs/base/browser/ui/tree/compressedObjectTreeModel.ts @@ -7,6 +7,7 @@ import { IIdentityProvider } from 'vs/base/browser/ui/list/list'; import { IIndexTreeModelSpliceOptions, IList } from 'vs/base/browser/ui/tree/indexTreeModel'; import { IObjectTreeModel, IObjectTreeModelOptions, IObjectTreeModelSetChildrenOptions, ObjectTreeModel } from 'vs/base/browser/ui/tree/objectTreeModel'; import { ICollapseStateChangeEvent, IObjectTreeElement, ITreeModel, ITreeModelSpliceEvent, ITreeNode, TreeError, TreeFilterResult, TreeVisibility, WeakMapper } from 'vs/base/browser/ui/tree/tree'; +import { equals } from 'vs/base/common/arrays'; import { Event } from 'vs/base/common/event'; import { Iterable } from 'vs/base/common/iterator'; @@ -170,6 +171,16 @@ export class CompressedObjectTreeModel, TFilterData e const splicedElement = splice(decompressedElement, element, children); const recompressedElement = (this.enabled ? compress : noCompress)(splicedElement); + // If the recompressed node is identical to the original, just set its children. + // Saves work and churn diffing the parent element. + const elementComparator = options.diffIdentityProvider + ? ((a: T, b: T) => options.diffIdentityProvider!.getId(a) === options.diffIdentityProvider!.getId(b)) + : undefined; + if (equals(recompressedElement.element.elements, node.element.elements, elementComparator)) { + this._setChildren(compressedNode, recompressedElement.children || Iterable.empty(), { diffIdentityProvider, diffDepth: 1 }); + return; + } + const parentChildren = parent.children .map(child => child === node ? recompressedElement : child); From 134e152d42ae5798e87c971e2c95fde8ee177ece Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Fri, 30 Jun 2023 16:31:43 -0700 Subject: [PATCH 100/926] Implement fs save options in exthost notebook. (#186805) --- .../api/common/extHostFileSystemConsumer.ts | 4 + .../workbench/api/common/extHostNotebook.ts | 99 ++++++++++++++++++- 2 files changed, 101 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/api/common/extHostFileSystemConsumer.ts b/src/vs/workbench/api/common/extHostFileSystemConsumer.ts index 8720a1ac3e7..ee009c2ab7e 100644 --- a/src/vs/workbench/api/common/extHostFileSystemConsumer.ts +++ b/src/vs/workbench/api/common/extHostFileSystemConsumer.ts @@ -250,6 +250,10 @@ export class ExtHostConsumerFileSystem { this._fileSystemProvider.set(scheme, { impl: provider, extUri: options?.isCaseSensitive ? extUri : extUriIgnorePathCase, isReadonly: !!options?.isReadonly }); return toDisposable(() => this._fileSystemProvider.delete(scheme)); } + + getFileSystemProviderExtUri(scheme: string) { + return this._fileSystemProvider.get(scheme)?.extUri ?? extUri; + } } export interface IExtHostConsumerFileSystem extends ExtHostConsumerFileSystem { } diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index 1bb4142e2e8..02e1dd91363 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { localize } from 'vs/nls'; import { VSBuffer } from 'vs/base/common/buffer'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Emitter, Event } from 'vs/base/common/event'; @@ -29,8 +30,8 @@ import { ExtHostCell, ExtHostNotebookDocument } from './extHostNotebookDocument' import { ExtHostNotebookEditor } from './extHostNotebookEditor'; import { onUnexpectedExternalError } from 'vs/base/common/errors'; import { IExtHostConsumerFileSystem } from 'vs/workbench/api/common/extHostFileSystemConsumer'; -import { basename } from 'vs/base/common/resources'; import { filter } from 'vs/base/common/objects'; +import { Schemas } from 'vs/base/common/network'; @@ -322,6 +323,17 @@ export class ExtHostNotebookController implements ExtHostNotebookShape { throw new Error('Document version mismatch'); } + if (!this._extHostFileSystem.value.isWritableFileSystem(uri.scheme)) { + throw new files.FileOperationError(localize('err.readonly', "Unable to modify read-only file '{0}'", this._resourceForError(uri)), files.FileOperationResult.FILE_PERMISSION_DENIED); + } + + // validate write + const statBeforeWrite = await this._validateWriteFile(uri, options); + + if (!statBeforeWrite) { + await this._mkdirp(uri); + } + const data: vscode.NotebookData = { metadata: filter(document.apiNotebook.metadata, key => !(serializer.options?.transientDocumentMetadata ?? {})[key]), cells: [], @@ -344,10 +356,11 @@ export class ExtHostNotebookController implements ExtHostNotebookShape { const bytes = await serializer.serializer.serializeNotebook(data, token); await this._extHostFileSystem.value.writeFile(uri, bytes); + const providerExtUri = this._extHostFileSystem.getFileSystemProviderExtUri(uri.scheme); const stat = await this._extHostFileSystem.value.stat(uri); const fileStats = { - name: basename(uri), // providerExtUri.basename(resource) + name: providerExtUri.basename(uri), isFile: (stat.type & files.FileType.File) !== 0, isDirectory: (stat.type & files.FileType.Directory) !== 0, isSymbolicLink: (stat.type & files.FileType.SymbolicLink) !== 0, @@ -363,6 +376,88 @@ export class ExtHostNotebookController implements ExtHostNotebookShape { return fileStats; } + private async _validateWriteFile(uri: URI, options: files.IWriteFileOptions) { + // File system provider registered in Extension Host doesn't have unlock or atomic support + // Validate via file stat meta data + const stat = await this._extHostFileSystem.value.stat(uri); + + // File cannot be directory + if ((stat.type & files.FileType.Directory) !== 0) { + throw new files.FileOperationError(localize('fileIsDirectoryWriteError', "Unable to write file '{0}' that is actually a directory", this._resourceForError(uri)), files.FileOperationResult.FILE_IS_DIRECTORY, options); + } + + // File cannot be readonly + if ((stat.permissions ?? 0) & files.FilePermission.Readonly) { + throw new files.FileOperationError(localize('err.readonly', "Unable to modify read-only file '{0}'", this._resourceForError(uri)), files.FileOperationResult.FILE_PERMISSION_DENIED); + } + + // Dirty write prevention + if ( + typeof options?.mtime === 'number' && typeof options.etag === 'string' && options.etag !== files.ETAG_DISABLED && + typeof stat.mtime === 'number' && typeof stat.size === 'number' && + options.mtime < stat.mtime && options.etag !== files.etag({ mtime: options.mtime /* not using stat.mtime for a reason, see above */, size: stat.size }) + ) { + throw new files.FileOperationError(localize('fileModifiedError', "File Modified Since"), files.FileOperationResult.FILE_MODIFIED_SINCE, options); + } + + return stat; + } + + private async _mkdirp(uri: URI) { + const providerExtUri = this._extHostFileSystem.getFileSystemProviderExtUri(uri.scheme); + let directory = providerExtUri.dirname(uri); + + const directoriesToCreate: string[] = []; + + while (!providerExtUri.isEqual(directory, providerExtUri.dirname(directory))) { + try { + const stat = await this._extHostFileSystem.value.stat(directory); + if ((stat.type & files.FileType.Directory) === 0) { + throw new Error(localize('mkdirExistsError', "Unable to create folder '{0}' that already exists but is not a directory", this._resourceForError(directory))); + } + + break; // we have hit a directory that exists -> good + } catch (error) { + + // Bubble up any other error that is not file not found + if (files.toFileSystemProviderErrorCode(error) !== files.FileSystemProviderErrorCode.FileNotFound) { + throw error; + } + + // Upon error, remember directories that need to be created + directoriesToCreate.push(providerExtUri.basename(directory)); + + // Continue up + directory = providerExtUri.dirname(directory); + } + } + + // Create directories as needed + for (let i = directoriesToCreate.length - 1; i >= 0; i--) { + directory = providerExtUri.joinPath(directory, directoriesToCreate[i]); + + try { + await this._extHostFileSystem.value.createDirectory(directory); + } catch (error) { + if (files.toFileSystemProviderErrorCode(error) !== files.FileSystemProviderErrorCode.FileExists) { + // For mkdirp() we tolerate that the mkdir() call fails + // in case the folder already exists. This follows node.js + // own implementation of fs.mkdir({ recursive: true }) and + // reduces the chances of race conditions leading to errors + // if multiple calls try to create the same folders + // As such, we only throw an error here if it is other than + // the fact that the file already exists. + // (see also https://github.com/microsoft/vscode/issues/89834) + throw error; + } + } + } + } + + private _resourceForError(uri: URI): string { + return uri.scheme === Schemas.file ? uri.fsPath : uri.toString(); + } + // --- open, save, saveAs, backup From 01d4949f36c7a5598f115adaea463dc30e4f9c12 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 30 Jun 2023 16:42:41 -0700 Subject: [PATCH 101/926] Continue chat session seamlessly when EH crashes. (#186807) Need to sync the existing ChatModel to the new EH and reinitialize it for the new chat provider instance. Fix microsoft/vscode-copilot#155 --- .../contrib/chat/browser/chatEditorInput.ts | 1 - .../contrib/chat/common/chatModel.ts | 6 +++- .../contrib/chat/common/chatServiceImpl.ts | 36 ++++++++++++------- 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatEditorInput.ts b/src/vs/workbench/contrib/chat/browser/chatEditorInput.ts index 3e7dbb511f5..cad8d9fddcc 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditorInput.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditorInput.ts @@ -101,7 +101,6 @@ export class ChatEditorInput extends EditorInput { this.sessionId = this.model.sessionId; this.providerId = this.model.providerId; - await this.model.waitForInitialization(); this._register(this.model.onDidChange(() => this._onDidChangeLabel.fire())); return this._register(new ChatEditorModel(this.model)); diff --git a/src/vs/workbench/contrib/chat/common/chatModel.ts b/src/vs/workbench/contrib/chat/common/chatModel.ts index 73c5194c4f1..875cc215055 100644 --- a/src/vs/workbench/contrib/chat/common/chatModel.ts +++ b/src/vs/workbench/contrib/chat/common/chatModel.ts @@ -195,7 +195,6 @@ export interface IChatModel { readonly requestInProgress: boolean; readonly inputPlaceholder?: string; getRequests(): IChatRequestModel[]; - waitForInitialization(): Promise; toExport(): IExportableChatData; toJSON(): ISerializableChatData; } @@ -385,6 +384,11 @@ export class ChatModel extends Disposable implements IChatModel { }); } + startReinitialize(): void { + this._session = undefined; + this._isInitializedDeferred = new DeferredPromise(); + } + initialize(session: IChat, welcomeMessage: ChatWelcomeMessageModel | undefined): void { if (this._session || this._isInitializedDeferred.isSettled) { throw new Error('ChatModel is already initialized'); diff --git a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts index 3c61d5dba8c..d05c1f1a828 100644 --- a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts +++ b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts @@ -287,13 +287,8 @@ export class ChatService extends Disposable implements IChatService { private _startSession(providerId: string, someSessionHistory: ISerializableChatData | undefined, token: CancellationToken): ChatModel { const model = this.instantiationService.createInstance(ChatModel, providerId, someSessionHistory); this._sessionModels.set(model.sessionId, model); - const modelInitPromise = this.initializeSession(model, someSessionHistory, token); - modelInitPromise.then(resolvedModel => { - if (!resolvedModel) { - model.dispose(); - this._sessionModels.delete(model.sessionId); - } - }).catch(err => { + const modelInitPromise = this.initializeSession(model, token); + modelInitPromise.catch(err => { this.trace('startSession', `initializeSession failed: ${err}`); model.setInitializationError(err); model.dispose(); @@ -303,7 +298,22 @@ export class ChatService extends Disposable implements IChatService { return model; } - private async initializeSession(model: ChatModel, sessionHistory: ISerializableChatData | undefined, token: CancellationToken): Promise { + private reinitializeModel(model: ChatModel): void { + model.startReinitialize(); + this.startSessionInit(model, CancellationToken.None); + } + + private startSessionInit(model: ChatModel, token: CancellationToken): void { + const modelInitPromise = this.initializeSession(model, token); + modelInitPromise.catch(err => { + this.trace('startSession', `initializeSession failed: ${err}`); + model.setInitializationError(err); + model.dispose(); + this._sessionModels.delete(model.sessionId); + }); + } + + private async initializeSession(model: ChatModel, token: CancellationToken): Promise { await this.extensionService.activateByEvent(`onInteractiveSession:${model.providerId}`); const provider = this._providers.get(model.providerId); @@ -319,18 +329,16 @@ export class ChatService extends Disposable implements IChatService { } if (!session) { - this.trace('startSession', 'Provider returned no session'); - return undefined; + throw new Error('Provider returned no session'); } this.trace('startSession', `Provider returned session`); - const welcomeMessage = sessionHistory ? undefined : withNullAsUndefined(await provider.provideWelcomeMessage?.(token)); + const welcomeMessage = model.welcomeMessage ? undefined : withNullAsUndefined(await provider.provideWelcomeMessage?.(token)); const welcomeModel = welcomeMessage && new ChatWelcomeMessageModel( welcomeMessage.map(item => typeof item === 'string' ? new MarkdownString(item) : item as IChatReplyFollowup[]), session.responderUsername, session.responderAvatarIconUri); model.initialize(session, welcomeModel); - return model; } getSession(sessionId: string): IChatModel | undefined { @@ -618,6 +626,10 @@ export class ChatService extends Disposable implements IChatService { this._providers.set(provider.id, provider); this._hasProvider.set(true); + Array.from(this._sessionModels.values()) + .filter(model => model.providerId === provider.id) + .forEach(model => this.reinitializeModel(model)); + return toDisposable(() => { this.trace('registerProvider', `Disposing chat provider`); this._providers.delete(provider.id); From 57608c9852633086a788780b68a9063bef679374 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 2 Jul 2023 09:16:21 -0700 Subject: [PATCH 102/926] Show env var description for all extensions Part of #186859 --- .../common/environmentVariableCollection.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/vs/platform/terminal/common/environmentVariableCollection.ts b/src/vs/platform/terminal/common/environmentVariableCollection.ts index 2d50644a58f..c9dcd52c172 100644 --- a/src/vs/platform/terminal/common/environmentVariableCollection.ts +++ b/src/vs/platform/terminal/common/environmentVariableCollection.ts @@ -138,25 +138,24 @@ export class MergedEnvironmentVariableCollection implements IMergedEnvironmentVa getVariableMap(scope: EnvironmentVariableScope | undefined): Map { const result = new Map(); - this.map.forEach((mutators, _key) => { + for (const mutators of this.map.values()) { const filteredMutators = mutators.filter(m => filterScope(m, scope)); if (filteredMutators.length > 0) { // All of these mutators are for the same variable because they are in the same scope, hence choose anyone to form a key. result.set(filteredMutators[0].variable, filteredMutators); } - }); + } return result; } getDescriptionMap(scope: EnvironmentVariableScope | undefined): Map { const result = new Map(); - this.descriptionMap.forEach((mutators, _key) => { + for (const mutators of this.descriptionMap.values()) { const filteredMutators = mutators.filter(m => filterScope(m, scope, true)); - if (filteredMutators.length > 0) { - // There should be exactly one description per extension per scope. - result.set(filteredMutators[0].extensionIdentifier, filteredMutators[0].description); + for (const mutator of filteredMutators) { + result.set(mutator.extensionIdentifier, mutator.description); } - }); + } return result; } From 14cc1f7a239d7062f20300f73b308d3ab9a7edfa Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 2 Jul 2023 09:35:02 -0700 Subject: [PATCH 103/926] Add env var description to show changes Fixes #186860 --- .../terminal.environmentChanges.contribution.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminalContrib/environmentChanges/browser/terminal.environmentChanges.contribution.ts b/src/vs/workbench/contrib/terminalContrib/environmentChanges/browser/terminal.environmentChanges.contribution.ts index 1ccc5c84251..c22d2802381 100644 --- a/src/vs/workbench/contrib/terminalContrib/environmentChanges/browser/terminal.environmentChanges.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/environmentChanges/browser/terminal.environmentChanges.contribution.ts @@ -51,7 +51,16 @@ function describeEnvironmentChanges(collection: IMergedEnvironmentVariableCollec for (const [ext, coll] of collection.collections) { content += `\n\n## ${localize('extension', 'Extension: {0}', ext)}`; content += '\n'; - for (const [_, mutator] of coll.map.entries()) { + if (coll.descriptionMap && coll.descriptionMap.size > 0) { + for (const desc of coll.descriptionMap.values()) { + content += `\n${desc.description}`; + if (desc.scope?.workspaceFolder) { + content += ` (${localize('ScopedEnvironmentContributionInfo', 'workspace')})`; + } + } + content += '\n'; + } + for (const mutator of coll.map.values()) { if (filterScope(mutator, scope) === false) { continue; } From 38c0e43f4a12a93aff203c9fc8394650dd8b20cd Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 2 Jul 2023 09:37:47 -0700 Subject: [PATCH 104/926] IEnvironmentDescriptionMutator -> IEnvironmentVariableCollectionDescription Fixes #186863 --- src/vs/platform/terminal/common/environmentVariable.ts | 8 ++++---- .../platform/terminal/common/environmentVariableShared.ts | 8 ++++---- src/vs/workbench/api/common/extHostTerminalService.ts | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/vs/platform/terminal/common/environmentVariable.ts b/src/vs/platform/terminal/common/environmentVariable.ts index 3798d1672c6..919c5c7b07d 100644 --- a/src/vs/platform/terminal/common/environmentVariable.ts +++ b/src/vs/platform/terminal/common/environmentVariable.ts @@ -19,7 +19,7 @@ export interface IEnvironmentVariableMutator { readonly options?: IEnvironmentVariableMutatorOptions; } -export interface IEnvironmentDescriptionMutator { +export interface IEnvironmentVariableCollectionDescription { readonly description: string | undefined; readonly scope?: EnvironmentVariableScope; } @@ -35,14 +35,14 @@ export type EnvironmentVariableScope = { export interface IEnvironmentVariableCollection { readonly map: ReadonlyMap; - readonly descriptionMap?: ReadonlyMap; + readonly descriptionMap?: ReadonlyMap; } /** [variable, mutator] */ export type ISerializableEnvironmentVariableCollection = [string, IEnvironmentVariableMutator][]; -export type ISerializableEnvironmentDescriptionMap = [string, IEnvironmentDescriptionMutator][]; -export interface IExtensionOwnedEnvironmentDescriptionMutator extends IEnvironmentDescriptionMutator { +export type ISerializableEnvironmentDescriptionMap = [string, IEnvironmentVariableCollectionDescription][]; +export interface IExtensionOwnedEnvironmentDescriptionMutator extends IEnvironmentVariableCollectionDescription { readonly extensionIdentifier: string; } diff --git a/src/vs/platform/terminal/common/environmentVariableShared.ts b/src/vs/platform/terminal/common/environmentVariableShared.ts index 5fbc5f36396..8d9ea66fe57 100644 --- a/src/vs/platform/terminal/common/environmentVariableShared.ts +++ b/src/vs/platform/terminal/common/environmentVariableShared.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IEnvironmentDescriptionMutator, IEnvironmentVariableCollection, IEnvironmentVariableMutator, ISerializableEnvironmentDescriptionMap as ISerializableEnvironmentDescriptionMap, ISerializableEnvironmentVariableCollection, ISerializableEnvironmentVariableCollections } from 'vs/platform/terminal/common/environmentVariable'; +import { IEnvironmentVariableCollectionDescription, IEnvironmentVariableCollection, IEnvironmentVariableMutator, ISerializableEnvironmentDescriptionMap as ISerializableEnvironmentDescriptionMap, ISerializableEnvironmentVariableCollection, ISerializableEnvironmentVariableCollections } from 'vs/platform/terminal/common/environmentVariable'; // This file is shared between the renderer and extension host @@ -11,7 +11,7 @@ export function serializeEnvironmentVariableCollection(collection: ReadonlyMap | undefined): ISerializableEnvironmentDescriptionMap { +export function serializeEnvironmentDescriptionMap(descriptionMap: ReadonlyMap | undefined): ISerializableEnvironmentDescriptionMap { return descriptionMap ? [...descriptionMap.entries()] : []; } @@ -23,8 +23,8 @@ export function deserializeEnvironmentVariableCollection( export function deserializeEnvironmentDescriptionMap( serializableEnvironmentDescription: ISerializableEnvironmentDescriptionMap | undefined -): Map { - return new Map(serializableEnvironmentDescription ?? []); +): Map { + return new Map(serializableEnvironmentDescription ?? []); } export function serializeEnvironmentVariableCollections(collections: ReadonlyMap): ISerializableEnvironmentVariableCollections { diff --git a/src/vs/workbench/api/common/extHostTerminalService.ts b/src/vs/workbench/api/common/extHostTerminalService.ts index 04b61d8b808..03d4899d196 100644 --- a/src/vs/workbench/api/common/extHostTerminalService.ts +++ b/src/vs/workbench/api/common/extHostTerminalService.ts @@ -17,7 +17,7 @@ import { NotSupportedError } from 'vs/base/common/errors'; import { serializeEnvironmentDescriptionMap, serializeEnvironmentVariableCollection } from 'vs/platform/terminal/common/environmentVariableShared'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { generateUuid } from 'vs/base/common/uuid'; -import { IEnvironmentDescriptionMutator, IEnvironmentVariableMutator, ISerializableEnvironmentVariableCollection } from 'vs/platform/terminal/common/environmentVariable'; +import { IEnvironmentVariableCollectionDescription, IEnvironmentVariableMutator, ISerializableEnvironmentVariableCollection } from 'vs/platform/terminal/common/environmentVariable'; import { ICreateContributedTerminalProfileOptions, IProcessReadyEvent, IShellLaunchConfigDto, ITerminalChildProcess, ITerminalLaunchError, ITerminalProfile, TerminalIcon, TerminalLocation, IProcessProperty, ProcessPropertyType, IProcessPropertyMap } from 'vs/platform/terminal/common/terminal'; import { TerminalDataBufferer } from 'vs/platform/terminal/common/terminalDataBuffering'; import { ThemeColor } from 'vs/base/common/themables'; @@ -875,7 +875,7 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I class UnifiedEnvironmentVariableCollection { readonly map: Map = new Map(); private readonly scopedCollections: Map = new Map(); - readonly descriptionMap: Map = new Map(); + readonly descriptionMap: Map = new Map(); private _persistent: boolean = true; public get persistent(): boolean { return this._persistent; } @@ -1024,7 +1024,7 @@ class UnifiedEnvironmentVariableCollection { // Only take the description before the first `\n\n`, so that the description doesn't mess up the UI descriptionStr = description?.value.split('\n\n')[0]; } - const value: IEnvironmentDescriptionMutator = { description: descriptionStr, scope }; + const value: IEnvironmentVariableCollectionDescription = { description: descriptionStr, scope }; this.descriptionMap.set(key, value); this._onDidChangeCollection.fire(); } From 6b83d6d01bf321debdb1165b821134ac2af8c9a2 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Sun, 2 Jul 2023 10:09:28 -0700 Subject: [PATCH 105/926] testing: use overflow ellipsis in test tree (#186806) * tree: add fast path for children updates in compressedObjectTreeModel I noticed in testing that more things were getting rerendered than they should have been. This adds a fast path so that, if the elements in a compressed node don't change when its children are updated, we only update the children instead of replacing the entire node from the parent collection. * testing: use overflow ellipsis in test tree Fixes #186320 Had to abandon flexbox for this, so do manual tweaks to make codicons show nicely: ![](https://memes.peet.io/img/23-06-321bf892-8f69-4548-9c76-465f33205c8e.png) --- .../contrib/testing/browser/media/testing.css | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/contrib/testing/browser/media/testing.css b/src/vs/workbench/contrib/testing/browser/media/testing.css index 11ae1f8784f..4d47d7ac55e 100644 --- a/src/vs/workbench/contrib/testing/browser/media/testing.css +++ b/src/vs/workbench/contrib/testing/browser/media/testing.css @@ -43,13 +43,19 @@ .test-explorer .test-item .label, .test-output-peek-tree .test-peek-item .name { - display: flex; - align-items: center; flex-grow: 1; - gap: 0.15em; width: 0; overflow: hidden; white-space: nowrap; + text-overflow: ellipsis; +} + +.test-output-peek-tree .test-peek-item .name .codicon, +.test-explorer .test-item .label .codicon { + vertical-align: middle; + font-size: 1em; + transform: scale(1.25); + margin: 0 0.125em; } .test-explorer .test-item, @@ -108,15 +114,6 @@ margin: 0; } -.test-explorer .name, -.test-output-peek-tree .name { - overflow: hidden; - text-overflow: ellipsis; - flex-grow: 1; - height: 22px; - align-items: center; -} - .test-explorer .computed-state, .test-output-peek-tree .computed-state { margin-right: 0.25em; From b0e5025ef5caf83e9a67a042a6f0e0ee2243fec0 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 3 Jul 2023 11:08:06 +0200 Subject: [PATCH 106/926] [Remote-SSH Bug]: Settings missing in remote settings view (fix microsoft/vscode-remote-release#8627) (#186828) --- .../browser/workbench.contribution.ts | 31 ++---------- src/vs/workbench/common/configuration.ts | 47 ++++++++++++++++++- 2 files changed, 50 insertions(+), 28 deletions(-) diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index e0c12723d33..98d32c500de 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -7,7 +7,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { localize } from 'vs/nls'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; import { isMacintosh, isWindows, isLinux, isWeb, isNative } from 'vs/base/common/platform'; -import { ConfigurationMigrationWorkbenchContribution, securityConfigurationNodeBase, workbenchConfigurationNodeBase } from 'vs/workbench/common/configuration'; +import { ConfigurationMigrationWorkbenchContribution, DynamicWorkbenchConfigurationWorkbenchContribution, workbenchConfigurationNodeBase } from 'vs/workbench/common/configuration'; import { isStandalone } from 'vs/base/browser/browser'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; @@ -20,6 +20,9 @@ const registry = Registry.as(ConfigurationExtensions.Con // Migration support Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(ConfigurationMigrationWorkbenchContribution, LifecyclePhase.Eventually); + // Dynamic Configuration + Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DynamicWorkbenchConfigurationWorkbenchContribution, LifecyclePhase.Ready); + // Workbench registry.registerConfiguration({ ...workbenchConfigurationNodeBase, @@ -709,30 +712,4 @@ const registry = Registry.as(ConfigurationExtensions.Con } } }); - - // Security - registry.registerConfiguration({ - ...securityConfigurationNodeBase, - 'properties': { - 'security.allowedUNCHosts': { - 'type': 'array', - 'items': { - 'type': 'string', - 'pattern': '^[^\\\\]+$', - 'patternErrorMessage': localize('security.allowedUNCHosts.patternErrorMessage', 'UNC host names must not contain backslashes.') - }, - 'default': [], - 'markdownDescription': localize('security.allowedUNCHosts', 'A set of UNC host names (without leading or trailing backslash, for example `192.168.0.1` or `my-server`) to allow without user confirmation. If a UNC host is being accessed that is not allowed via this setting or has not been acknowledged via user confirmation, an error will occur and the operation stopped. A restart is required when changing this setting. Find out more about this setting at https://aka.ms/vscode-windows-unc.'), - 'included': isWeb ? true /* web maybe connected to a windows machine */ : isWindows, - 'scope': ConfigurationScope.MACHINE - }, - 'security.restrictUNCAccess': { - 'type': 'boolean', - 'default': true, - 'markdownDescription': localize('security.restrictUNCAccess', 'If enabled, only allows access to UNC host names that are allowed by the `#security.allowedUNCHosts#` setting or after user confirmation. Find out more about this setting at https://aka.ms/vscode-windows-unc.'), - 'included': isWeb ? true /* web maybe connected to a windows machine */ : isWindows, - 'scope': ConfigurationScope.MACHINE - } - } - }); })(); diff --git a/src/vs/workbench/common/configuration.ts b/src/vs/workbench/common/configuration.ts index abc42742dd9..7efd6138cd8 100644 --- a/src/vs/workbench/common/configuration.ts +++ b/src/vs/workbench/common/configuration.ts @@ -4,13 +4,15 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from 'vs/nls'; -import { ConfigurationScope, IConfigurationNode } from 'vs/platform/configuration/common/configurationRegistry'; +import { ConfigurationScope, IConfigurationNode, IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { ConfigurationTarget, IConfigurationOverrides, IConfigurationService, IConfigurationValue } from 'vs/platform/configuration/common/configuration'; import { Disposable } from 'vs/base/common/lifecycle'; import { Emitter } from 'vs/base/common/event'; +import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; +import { OperatingSystem, isWindows } from 'vs/base/common/platform'; export const applicationConfigurationNodeBase = Object.freeze({ 'id': 'application', @@ -118,3 +120,46 @@ export class ConfigurationMigrationWorkbenchContribution extends Disposable impl await Promise.allSettled(keyValuePairs.map(async ([key, value]) => this.configurationService.updateValue(key, value.value, overrides, target))); } } + +export class DynamicWorkbenchConfigurationWorkbenchContribution extends Disposable implements IWorkbenchContribution { + + constructor( + @IRemoteAgentService remoteAgentService: IRemoteAgentService + ) { + super(); + + (async () => { + if (!isWindows) { + const remoteEnvironment = await remoteAgentService.getEnvironment(); + if (remoteEnvironment?.os !== OperatingSystem.Windows) { + return; + } + } + + // Windows: UNC allow list security configuration + const registry = Registry.as(ConfigurationExtensions.Configuration); + registry.registerConfiguration({ + ...securityConfigurationNodeBase, + 'properties': { + 'security.allowedUNCHosts': { + 'type': 'array', + 'items': { + 'type': 'string', + 'pattern': '^[^\\\\]+$', + 'patternErrorMessage': localize('security.allowedUNCHosts.patternErrorMessage', 'UNC host names must not contain backslashes.') + }, + 'default': [], + 'markdownDescription': localize('security.allowedUNCHosts', 'A set of UNC host names (without leading or trailing backslash, for example `192.168.0.1` or `my-server`) to allow without user confirmation. If a UNC host is being accessed that is not allowed via this setting or has not been acknowledged via user confirmation, an error will occur and the operation stopped. A restart is required when changing this setting. Find out more about this setting at https://aka.ms/vscode-windows-unc.'), + 'scope': ConfigurationScope.MACHINE + }, + 'security.restrictUNCAccess': { + 'type': 'boolean', + 'default': true, + 'markdownDescription': localize('security.restrictUNCAccess', 'If enabled, only allows access to UNC host names that are allowed by the `#security.allowedUNCHosts#` setting or after user confirmation. Find out more about this setting at https://aka.ms/vscode-windows-unc.'), + 'scope': ConfigurationScope.MACHINE + } + } + }); + })(); + } +} From ce708a97cefe1571c8ae5f7ebd092095dc788df6 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 3 Jul 2023 11:09:15 +0200 Subject: [PATCH 107/926] editors - only close disposed editors (#186736) (#186886) --- .../browser/parts/editor/editorGroupView.ts | 16 +++++++++++++--- .../workbench/common/editor/editorGroupModel.ts | 12 +++--------- .../parts/editor/editorGroupModel.test.ts | 2 ++ 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index bdabf746d1b..740e037f6d4 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -665,15 +665,25 @@ export class EditorGroupView extends Themable implements IEditorGroupView { this.disposedEditorsWorker.work(editor); } - private handleDisposedEditors(editors: EditorInput[]): void { + private handleDisposedEditors(disposedEditors: EditorInput[]): void { // Split between visible and hidden editors let activeEditor: EditorInput | undefined; const inactiveEditors: EditorInput[] = []; - for (const editor of editors) { + for (const disposedEditor of disposedEditors) { + const editorFindResult = this.model.findEditor(disposedEditor); + if (!editorFindResult) { + continue; // not part of the model anymore + } + + const editor = editorFindResult[0]; + if (!editor.isDisposed()) { + continue; // editor got reopened meanwhile + } + if (this.model.isActive(editor)) { activeEditor = editor; - } else if (this.model.contains(editor)) { + } else { inactiveEditors.push(editor); } } diff --git a/src/vs/workbench/common/editor/editorGroupModel.ts b/src/vs/workbench/common/editor/editorGroupModel.ts index a65dc729932..7fc1ae6cbc0 100644 --- a/src/vs/workbench/common/editor/editorGroupModel.ts +++ b/src/vs/workbench/common/editor/editorGroupModel.ts @@ -869,7 +869,7 @@ export class EditorGroupModel extends Disposable { } } - indexOf(candidate: EditorInput | null, editors = this.editors, options?: IMatchEditorOptions): number { + indexOf(candidate: EditorInput | IUntypedEditorInput | null, editors = this.editors, options?: IMatchEditorOptions): number { let index = -1; if (!candidate) { return index; @@ -894,7 +894,7 @@ export class EditorGroupModel extends Disposable { return index; } - private findEditor(candidate: EditorInput | null, options?: IMatchEditorOptions): [EditorInput, number /* index */] | undefined { + findEditor(candidate: EditorInput | null, options?: IMatchEditorOptions): [EditorInput, number /* index */] | undefined { const index = this.indexOf(candidate, this.editors, options); if (index === -1) { return undefined; @@ -912,13 +912,7 @@ export class EditorGroupModel extends Disposable { } contains(candidate: EditorInput | IUntypedEditorInput, options?: IMatchEditorOptions): boolean { - for (const editor of this.editors) { - if (this.matches(editor, candidate, options)) { - return true; - } - } - - return false; + return this.indexOf(candidate, this.editors, options) !== -1; } private matches(editor: EditorInput | null, candidate: EditorInput | IUntypedEditorInput | null, options?: IMatchEditorOptions): boolean { diff --git a/src/vs/workbench/test/browser/parts/editor/editorGroupModel.test.ts b/src/vs/workbench/test/browser/parts/editor/editorGroupModel.test.ts index 1b70d16389c..d5b2f9fa085 100644 --- a/src/vs/workbench/test/browser/parts/editor/editorGroupModel.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/editorGroupModel.test.ts @@ -814,6 +814,7 @@ suite('EditorGroupModel', () => { assert.strictEqual(group.count, 1); assert.strictEqual(group.getEditors(EditorsOrder.MOST_RECENTLY_ACTIVE).length, 1); + assert.strictEqual(group.findEditor(input1)![0], input1); assert.strictEqual(group.activeEditor, input1); assert.strictEqual(group.isActive(input1), true); assert.strictEqual(group.isPinned(input1), true); @@ -827,6 +828,7 @@ suite('EditorGroupModel', () => { assert.strictEqual(events.activated[0].editorIndex, 0); const index = group.indexOf(input1); + assert.strictEqual(group.findEditor(input1)![1], index); let event = group.closeEditor(input1, EditorCloseContext.UNPIN); assert.strictEqual(event?.editor, input1); assert.strictEqual(event?.editorIndex, index); From 480b4653ee4551d9a1e8c01421c8423f9edf5089 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 3 Jul 2023 11:10:08 +0200 Subject: [PATCH 108/926] backups - guard reading operations with IO queue as well (#186736) (#186889) --- .../common/workingCopyBackupService.ts | 195 ++++++++---------- .../workingCopyBackupService.test.ts | 49 ----- 2 files changed, 89 insertions(+), 155 deletions(-) diff --git a/src/vs/workbench/services/workingCopy/common/workingCopyBackupService.ts b/src/vs/workbench/services/workingCopy/common/workingCopyBackupService.ts index 9ccbda9d093..f9ec56ad11a 100644 --- a/src/vs/workbench/services/workingCopy/common/workingCopyBackupService.ts +++ b/src/vs/workbench/services/workingCopy/common/workingCopyBackupService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { basename, isEqual, joinPath } from 'vs/base/common/resources'; +import { joinPath } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { coalesce } from 'vs/base/common/arrays'; import { equals, deepClone } from 'vs/base/common/objects'; @@ -111,14 +111,6 @@ export class WorkingCopyBackupsModel { this.cache.delete(resource); } - move(source: URI, target: URI): void { - const entry = this.cache.get(source); - if (entry) { - this.cache.delete(source); - this.cache.set(target, entry); - } - } - clear(): void { this.cache.clear(); } @@ -230,43 +222,15 @@ class WorkingCopyBackupServiceImpl extends Disposable implements IWorkingCopyBac // Create backup model this.model = await WorkingCopyBackupsModel.create(this.backupWorkspaceHome, this.fileService); - // Migrate hashes as needed. We used to hash with a MD5 - // sum of the path but switched to our own simpler hash - // to avoid a node.js dependency. We still want to - // support the older hash to prevent dataloss, so we: - // - iterate over all backups - // - detect if the file name length is 32 (MD5 length) - // - read the backup's target file path - // - rename the backup to the new hash - // - update the backup in our model - for (const backupResource of this.model.get()) { - if (basename(backupResource).length !== 32) { - continue; // not a MD5 hash, already uses new hash function - } - - try { - const identifier = await this.resolveIdentifier(backupResource, this.model); - if (!identifier) { - this.logService.warn(`Backup: Unable to read target URI of backup ${backupResource} for migration to new hash.`); - continue; - } - - const expectedBackupResource = this.toBackupResource(identifier); - if (!isEqual(expectedBackupResource, backupResource)) { - await this.fileService.move(backupResource, expectedBackupResource, true); - this.model.move(backupResource, expectedBackupResource); - } - } catch (error) { - this.logService.error(`Backup: Unable to migrate backup ${backupResource} to new hash.`); - } - } - return this.model; } async hasBackups(): Promise { const model = await this.ready; + // Ensure to await any pending backup operations + await this.joinBackups(); + return model.count() > 0; } @@ -409,48 +373,60 @@ class WorkingCopyBackupServiceImpl extends Disposable implements IWorkingCopyBac async getBackups(): Promise { const model = await this.ready; + // Ensure to await any pending backup operations + await this.joinBackups(); + const backups = await Promise.all(model.get().map(backupResource => this.resolveIdentifier(backupResource, model))); return coalesce(backups); } private async resolveIdentifier(backupResource: URI, model: WorkingCopyBackupsModel): Promise { + let res: IWorkingCopyIdentifier | undefined = undefined; - // Read the entire backup preamble by reading up to - // `PREAMBLE_MAX_LENGTH` in the backup file until - // the `PREAMBLE_END_MARKER` is found - const backupPreamble = await this.readToMatchingString(backupResource, WorkingCopyBackupServiceImpl.PREAMBLE_END_MARKER, WorkingCopyBackupServiceImpl.PREAMBLE_MAX_LENGTH); - if (!backupPreamble) { - return undefined; - } + await this.ioOperationQueues.queueFor(backupResource).queue(async () => { + if (!model.has(backupResource)) { + return; // require backup to be present + } - // Figure out the offset in the preamble where meta - // information possibly starts. This can be `-1` for - // older backups without meta. - const metaStartIndex = backupPreamble.indexOf(WorkingCopyBackupServiceImpl.PREAMBLE_META_SEPARATOR); + // Read the entire backup preamble by reading up to + // `PREAMBLE_MAX_LENGTH` in the backup file until + // the `PREAMBLE_END_MARKER` is found + const backupPreamble = await this.readToMatchingString(backupResource, WorkingCopyBackupServiceImpl.PREAMBLE_END_MARKER, WorkingCopyBackupServiceImpl.PREAMBLE_MAX_LENGTH); + if (!backupPreamble) { + return; + } - // Extract the preamble content for resource and meta - let resourcePreamble: string; - let metaPreamble: string | undefined; - if (metaStartIndex > 0) { - resourcePreamble = backupPreamble.substring(0, metaStartIndex); - metaPreamble = backupPreamble.substr(metaStartIndex + 1); - } else { - resourcePreamble = backupPreamble; - metaPreamble = undefined; - } + // Figure out the offset in the preamble where meta + // information possibly starts. This can be `-1` for + // older backups without meta. + const metaStartIndex = backupPreamble.indexOf(WorkingCopyBackupServiceImpl.PREAMBLE_META_SEPARATOR); - // Try to parse the meta preamble for figuring out - // `typeId` and `meta` if defined. - const { typeId, meta } = this.parsePreambleMeta(metaPreamble); + // Extract the preamble content for resource and meta + let resourcePreamble: string; + let metaPreamble: string | undefined; + if (metaStartIndex > 0) { + resourcePreamble = backupPreamble.substring(0, metaStartIndex); + metaPreamble = backupPreamble.substr(metaStartIndex + 1); + } else { + resourcePreamble = backupPreamble; + metaPreamble = undefined; + } - // Update model entry with now resolved meta - model.update(backupResource, meta); + // Try to parse the meta preamble for figuring out + // `typeId` and `meta` if defined. + const { typeId, meta } = this.parsePreambleMeta(metaPreamble); - return { - typeId: typeId ?? NO_TYPE_ID, - resource: URI.parse(resourcePreamble) - }; + // Update model entry with now resolved meta + model.update(backupResource, meta); + + res = { + typeId: typeId ?? NO_TYPE_ID, + resource: URI.parse(resourcePreamble) + }; + }); + + return res; } private async readToMatchingString(backupResource: URI, matchingString: string, maximumBytesToRead: number): Promise { @@ -469,50 +445,57 @@ class WorkingCopyBackupServiceImpl extends Disposable implements IWorkingCopyBac const backupResource = this.toBackupResource(identifier); const model = await this.ready; - if (!model.has(backupResource)) { - return undefined; // require backup to be present - } - // Load the backup content and peek into the first chunk - // to be able to resolve the meta data - const backupStream = await this.fileService.readFileStream(backupResource); - const peekedBackupStream = await peekStream(backupStream.value, 1); - const firstBackupChunk = VSBuffer.concat(peekedBackupStream.buffer); + let res: IResolvedWorkingCopyBackup | undefined = undefined; - // We have seen reports (e.g. https://github.com/microsoft/vscode/issues/78500) where - // if VSCode goes down while writing the backup file, the file can turn empty because - // it always first gets truncated and then written to. In this case, we will not find - // the meta-end marker ('\n') and as such the backup can only be invalid. We bail out - // here if that is the case. - const preambleEndIndex = firstBackupChunk.buffer.indexOf(WorkingCopyBackupServiceImpl.PREAMBLE_END_MARKER_CHARCODE); - if (preambleEndIndex === -1) { - this.logService.trace(`Backup: Could not find meta end marker in ${backupResource}. The file is probably corrupt (filesize: ${backupStream.size}).`); + await this.ioOperationQueues.queueFor(backupResource).queue(async () => { + if (!model.has(backupResource)) { + return; // require backup to be present + } - return undefined; - } + // Load the backup content and peek into the first chunk + // to be able to resolve the meta data + const backupStream = await this.fileService.readFileStream(backupResource); + const peekedBackupStream = await peekStream(backupStream.value, 1); + const firstBackupChunk = VSBuffer.concat(peekedBackupStream.buffer); - const preambelRaw = firstBackupChunk.slice(0, preambleEndIndex).toString(); + // We have seen reports (e.g. https://github.com/microsoft/vscode/issues/78500) where + // if VSCode goes down while writing the backup file, the file can turn empty because + // it always first gets truncated and then written to. In this case, we will not find + // the meta-end marker ('\n') and as such the backup can only be invalid. We bail out + // here if that is the case. + const preambleEndIndex = firstBackupChunk.buffer.indexOf(WorkingCopyBackupServiceImpl.PREAMBLE_END_MARKER_CHARCODE); + if (preambleEndIndex === -1) { + this.logService.trace(`Backup: Could not find meta end marker in ${backupResource}. The file is probably corrupt (filesize: ${backupStream.size}).`); - // Extract meta data (if any) - let meta: T | undefined; - const metaStartIndex = preambelRaw.indexOf(WorkingCopyBackupServiceImpl.PREAMBLE_META_SEPARATOR); - if (metaStartIndex !== -1) { - meta = this.parsePreambleMeta(preambelRaw.substr(metaStartIndex + 1)).meta as T; - } + return undefined; + } - // Update model entry with now resolved meta - model.update(backupResource, meta); + const preambelRaw = firstBackupChunk.slice(0, preambleEndIndex).toString(); - // Build a new stream without the preamble - const firstBackupChunkWithoutPreamble = firstBackupChunk.slice(preambleEndIndex + 1); - let value: VSBufferReadableStream; - if (peekedBackupStream.ended) { - value = bufferToStream(firstBackupChunkWithoutPreamble); - } else { - value = prefixedBufferStream(firstBackupChunkWithoutPreamble, peekedBackupStream.stream); - } + // Extract meta data (if any) + let meta: T | undefined; + const metaStartIndex = preambelRaw.indexOf(WorkingCopyBackupServiceImpl.PREAMBLE_META_SEPARATOR); + if (metaStartIndex !== -1) { + meta = this.parsePreambleMeta(preambelRaw.substr(metaStartIndex + 1)).meta as T; + } - return { value, meta }; + // Update model entry with now resolved meta + model.update(backupResource, meta); + + // Build a new stream without the preamble + const firstBackupChunkWithoutPreamble = firstBackupChunk.slice(preambleEndIndex + 1); + let value: VSBufferReadableStream; + if (peekedBackupStream.ended) { + value = bufferToStream(firstBackupChunkWithoutPreamble); + } else { + value = prefixedBufferStream(firstBackupChunkWithoutPreamble, peekedBackupStream.stream); + } + + res = { value, meta }; + }); + + return res; } private parsePreambleMeta(preambleMetaRaw: string | undefined): { typeId: string | undefined; meta: T | undefined } { diff --git a/src/vs/workbench/services/workingCopy/test/electron-sandbox/workingCopyBackupService.test.ts b/src/vs/workbench/services/workingCopy/test/electron-sandbox/workingCopyBackupService.test.ts index 3d7f0bbe515..55c7d283f9a 100644 --- a/src/vs/workbench/services/workingCopy/test/electron-sandbox/workingCopyBackupService.test.ts +++ b/src/vs/workbench/services/workingCopy/test/electron-sandbox/workingCopyBackupService.test.ts @@ -1212,11 +1212,6 @@ suite('WorkingCopyBackupService', () => { model.update(resource4); assert.strictEqual(model.has(resource4), true); assert.strictEqual(model.has(resource4, undefined, { foo: 'nothing' }), false); - - const resource5 = URI.file('test4.html'); - model.move(resource4, resource5); - assert.strictEqual(model.has(resource4), false); - assert.strictEqual(model.has(resource5), true); }); test('create', async () => { @@ -1245,50 +1240,6 @@ suite('WorkingCopyBackupService', () => { }); }); - suite('Hash migration', () => { - - test('works', async () => { - const fooBackupId = toUntypedWorkingCopyId(fooFile); - const untitledBackupId = toUntypedWorkingCopyId(untitledFile); - const customBackupId = toUntypedWorkingCopyId(customFile); - - const fooBackupPath = joinPath(workspaceBackupPath, fooFile.scheme, hashIdentifier(fooBackupId)); - const untitledBackupPath = joinPath(workspaceBackupPath, untitledFile.scheme, hashIdentifier(untitledBackupId)); - const customFileBackupPath = joinPath(workspaceBackupPath, customFile.scheme, hashIdentifier(customBackupId)); - - // Prepare backups of the old MD5 hash format - await fileService.createFolder(joinPath(workspaceBackupPath, fooFile.scheme)); - await fileService.createFolder(joinPath(workspaceBackupPath, untitledFile.scheme)); - await fileService.createFolder(joinPath(workspaceBackupPath, customFile.scheme)); - await fileService.writeFile(joinPath(workspaceBackupPath, fooFile.scheme, '8a8589a2f1c9444b89add38166f50229'), VSBuffer.fromString(`${fooFile.toString()}\ntest file`)); - await fileService.writeFile(joinPath(workspaceBackupPath, untitledFile.scheme, '13264068d108c6901b3592ea654fcd57'), VSBuffer.fromString(`${untitledFile.toString()}\ntest untitled`)); - await fileService.writeFile(joinPath(workspaceBackupPath, customFile.scheme, 'bf018572af7b38746b502893bd0adf6c'), VSBuffer.fromString(`${customFile.toString()}\ntest custom`)); - - service.reinitialize(workspaceBackupPath); - - const backups = await service.getBackups(); - assert.strictEqual(backups.length, 3); - assert.ok(backups.some(backup => isEqual(backup.resource, fooFile))); - assert.ok(backups.some(backup => isEqual(backup.resource, untitledFile))); - assert.ok(backups.some(backup => isEqual(backup.resource, customFile))); - - assert.strictEqual((await fileService.resolve(joinPath(workspaceBackupPath, fooFile.scheme))).children?.length, 1); - assert.strictEqual((await fileService.exists(fooBackupPath)), true); - assert.strictEqual((await fileService.readFile(fooBackupPath)).value.toString(), `${fooFile.toString()}\ntest file`); - assert.ok(service.hasBackupSync(fooBackupId)); - - assert.strictEqual((await fileService.resolve(joinPath(workspaceBackupPath, untitledFile.scheme))).children?.length, 1); - assert.strictEqual((await fileService.exists(untitledBackupPath)), true); - assert.strictEqual((await fileService.readFile(untitledBackupPath)).value.toString(), `${untitledFile.toString()}\ntest untitled`); - assert.ok(service.hasBackupSync(untitledBackupId)); - - assert.strictEqual((await fileService.resolve(joinPath(workspaceBackupPath, customFile.scheme))).children?.length, 1); - assert.strictEqual((await fileService.exists(customFileBackupPath)), true); - assert.strictEqual((await fileService.readFile(customFileBackupPath)).value.toString(), `${customFile.toString()}\ntest custom`); - assert.ok(service.hasBackupSync(customBackupId)); - }); - }); - suite('typeId migration', () => { test('works (when meta is missing)', async () => { From d88c76383f83c60a4297277c665ea8a8ce4d72f1 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Mon, 3 Jul 2023 11:24:12 +0200 Subject: [PATCH 109/926] A view with id '~remote.helpPanel' is already registered (#186895) Fixes #186630 --- src/vs/workbench/contrib/remote/browser/remote.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/remote/browser/remote.ts b/src/vs/workbench/contrib/remote/browser/remote.ts index 41ff99b995c..c9cd0c348a2 100644 --- a/src/vs/workbench/contrib/remote/browser/remote.ts +++ b/src/vs/workbench/contrib/remote/browser/remote.ts @@ -583,6 +583,7 @@ class RemoteViewPaneContainer extends FilterViewPaneContainer implements IViewMo private _onDidChangeHelpInformation = new Emitter(); public onDidChangeHelpInformation: Event = this._onDidChangeHelpInformation.event; private hasSetSwitchForConnection: boolean = false; + private hasRegisteredHelpView: boolean = false; constructor( @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService, @@ -610,10 +611,12 @@ class RemoteViewPaneContainer extends FilterViewPaneContainer implements IViewMo this._onDidChangeHelpInformation.fire(); const viewsRegistry = Registry.as(Extensions.ViewsRegistry); - if (this.helpInformation.length) { + if (this.helpInformation.length && !this.hasRegisteredHelpView) { viewsRegistry.registerViews([this.helpPanelDescriptor], this.viewContainer); - } else { + this.hasRegisteredHelpView = true; + } else if (this.hasRegisteredHelpView) { viewsRegistry.deregisterViews([this.helpPanelDescriptor], this.viewContainer); + this.hasRegisteredHelpView = false; } }); } From d9877ca5d69c208217afc44773ffb158aa28ea74 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Mon, 3 Jul 2023 12:38:45 +0200 Subject: [PATCH 110/926] Inline codespan in comments widget uses wrong font (#186893) Fixes #186768 --- .../contrib/comments/browser/commentThreadZoneWidget.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/comments/browser/commentThreadZoneWidget.ts b/src/vs/workbench/contrib/comments/browser/commentThreadZoneWidget.ts index 8e87473cba9..6301479ad72 100644 --- a/src/vs/workbench/contrib/comments/browser/commentThreadZoneWidget.ts +++ b/src/vs/workbench/contrib/comments/browser/commentThreadZoneWidget.ts @@ -19,12 +19,13 @@ import { IColorTheme, IThemeService } from 'vs/platform/theme/common/themeServic import { CommentGlyphWidget } from 'vs/workbench/contrib/comments/browser/commentGlyphWidget'; import { ICommentService } from 'vs/workbench/contrib/comments/browser/commentService'; import { ICommentThreadWidget } from 'vs/workbench/contrib/comments/common/commentThreadWidget'; -import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { EDITOR_FONT_DEFAULTS, EditorOption, IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { CommentThreadWidget } from 'vs/workbench/contrib/comments/browser/commentThreadWidget'; import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; import { commentThreadStateBackgroundColorVar, commentThreadStateColorVar, getCommentThreadStateBorderColor } from 'vs/workbench/contrib/comments/browser/commentColors'; import { peekViewBorder } from 'vs/editor/contrib/peekView/browser/peekView'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; function getCommentThreadWidgetStateColor(thread: languages.CommentThreadState | undefined, theme: IColorTheme): Color | undefined { return getCommentThreadStateBorderColor(thread, theme) ?? theme.getColor(peekViewBorder); @@ -126,7 +127,8 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget @IInstantiationService instantiationService: IInstantiationService, @IThemeService private themeService: IThemeService, @ICommentService private commentService: ICommentService, - @IContextKeyService contextKeyService: IContextKeyService + @IContextKeyService contextKeyService: IContextKeyService, + @IConfigurationService private readonly configurationService: IConfigurationService ) { super(editor, { keepEditorSelection: true }); this._contextKeyService = contextKeyService.createScoped(this.domNode); @@ -223,7 +225,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget this._commentThread as unknown as languages.CommentThread, this._pendingComment, this._pendingEdits, - { editor: this.editor, codeBlockFontSize: '' }, + { editor: this.editor, codeBlockFontSize: '', codeBlockFontFamily: this.configurationService.getValue('editor').fontFamily || EDITOR_FONT_DEFAULTS.fontFamily }, this._commentOptions, { actionRunner: () => { From 7326ef7e8e3d303593b71056d46c579302b9f75e Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Mon, 3 Jul 2023 12:39:09 +0200 Subject: [PATCH 111/926] Cannot resolve tree item for element (#186896) Fixes #186627 --- src/vs/workbench/api/common/extHostTreeViews.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/api/common/extHostTreeViews.ts b/src/vs/workbench/api/common/extHostTreeViews.ts index c6accd54fe9..2950a3b5894 100644 --- a/src/vs/workbench/api/common/extHostTreeViews.ts +++ b/src/vs/workbench/api/common/extHostTreeViews.ts @@ -610,7 +610,7 @@ class ExtHostTreeView extends Disposable { return Promise.resolve(node); } } - throw new Error(`Cannot resolve tree item for element ${handle}`); + throw new Error(`Cannot resolve tree item for element ${handle} from extension ${this.extension.identifier.value}`); })); } From 7c34ea4452bf66116f7013047124bdd8a6909409 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Mon, 3 Jul 2023 13:29:29 +0200 Subject: [PATCH 112/926] Custom tree view looses focussed checkbox on toggle (#186906) Fixes #186306 --- .../workbench/browser/parts/views/treeView.ts | 152 ++++++++++-------- 1 file changed, 81 insertions(+), 71 deletions(-) diff --git a/src/vs/workbench/browser/parts/views/treeView.ts b/src/vs/workbench/browser/parts/views/treeView.ts index df837b853b2..5cacc80e6ba 100644 --- a/src/vs/workbench/browser/parts/views/treeView.ts +++ b/src/vs/workbench/browser/parts/views/treeView.ts @@ -630,68 +630,6 @@ abstract class AbstractTreeView extends Disposable implements ITreeView { this._register(focusTracker.onDidBlur(() => this.focused = false)); } - private updateCheckboxes(items: ITreeItem[]) { - const additionalItems: ITreeItem[] = []; - - if (!this.manuallyManageCheckboxes) { - for (const item of items) { - if (item.checkbox !== undefined) { - - function checkChildren(currentItem: ITreeItem) { - for (const child of (currentItem.children ?? [])) { - if ((child.checkbox !== undefined) && (currentItem.checkbox !== undefined) && (child.checkbox.isChecked !== currentItem.checkbox.isChecked)) { - child.checkbox.isChecked = currentItem.checkbox.isChecked; - additionalItems.push(child); - checkChildren(child); - } - } - } - checkChildren(item); - - const visitedParents: Set = new Set(); - function checkParents(currentItem: ITreeItem) { - if (currentItem.parent && (currentItem.parent.checkbox !== undefined) && currentItem.parent.children) { - if (visitedParents.has(currentItem.parent)) { - return; - } else { - visitedParents.add(currentItem.parent); - } - - let someUnchecked = false; - let someChecked = false; - for (const child of currentItem.parent.children) { - if (someUnchecked && someChecked) { - break; - } - if (child.checkbox !== undefined) { - if (child.checkbox.isChecked) { - someChecked = true; - } else { - someUnchecked = true; - } - } - } - if (someChecked && !someUnchecked && (currentItem.parent.checkbox.isChecked !== true)) { - currentItem.parent.checkbox.isChecked = true; - additionalItems.push(currentItem.parent); - checkParents(currentItem.parent); - } else if (someUnchecked && (currentItem.parent.checkbox.isChecked !== false)) { - currentItem.parent.checkbox.isChecked = false; - additionalItems.push(currentItem.parent); - checkParents(currentItem.parent); - } - } - } - checkParents(item); - } - } - } - items = items.concat(additionalItems); - items.forEach(item => this.tree?.rerender(item)); - this._onDidChangeCheckboxState.fire(items); - } - - protected createTree() { const actionViewItemProvider = createActionViewItem.bind(undefined, this.instantiationService); const treeMenus = this._register(this.instantiationService.createInstance(TreeMenus, this.id)); @@ -699,10 +637,9 @@ abstract class AbstractTreeView extends Disposable implements ITreeView { const dataSource = this.instantiationService.createInstance(TreeDataSource, this, (task: Promise) => this.progressService.withProgress({ location: this.id }, () => task)); const aligner = new Aligner(this.themeService); const checkboxStateHandler = this._register(new CheckboxStateHandler()); - this._register(checkboxStateHandler.onDidChangeCheckboxState(items => { - this.updateCheckboxes(items); - })); - const renderer = this.instantiationService.createInstance(TreeRenderer, this.id, treeMenus, this.treeLabels, actionViewItemProvider, aligner, checkboxStateHandler); + const renderer = this.instantiationService.createInstance(TreeRenderer, this.id, treeMenus, this.treeLabels, actionViewItemProvider, aligner, checkboxStateHandler, this.manuallyManageCheckboxes); + this._register(renderer.onDidChangeCheckboxState(e => this._onDidChangeCheckboxState.fire(e))); + const widgetAriaLabel = this._title; this.tree = this._register(this.instantiationService.createInstance(Tree, this.id, this.treeContainer!, new TreeViewDelegate(), [renderer], @@ -1125,10 +1062,13 @@ class TreeRenderer extends Disposable implements ITreeRenderer = this._register(new Emitter()); + readonly onDidChangeCheckboxState: Event = this._onDidChangeCheckboxState.event; + private _actionRunner: MultipleSelectionActionRunner | undefined; private _hoverDelegate: IHoverDelegate; private _hasCheckbox: boolean = false; - private _renderedElements = new Map, ITreeExplorerTemplateData>(); + private _renderedElements = new Map; rendered: ITreeExplorerTemplateData }>(); // tree item handle to template data constructor( private treeViewId: string, @@ -1137,6 +1077,7 @@ class TreeRenderer extends Disposable implements ITreeRenderer this.rerender())); this._register(this.themeService.onDidColorThemeChange(() => this.rerender())); + this._register(checkboxStateHandler.onDidChangeCheckboxState(items => { + this.updateCheckboxes(items); + })); } get templateId(): string { @@ -1302,7 +1246,7 @@ class TreeRenderer extends Disposable implements ITreeRenderer = new Set(); + function checkParents(currentItem: ITreeItem) { + if (currentItem.parent && (currentItem.parent.checkbox !== undefined) && currentItem.parent.children) { + if (visitedParents.has(currentItem.parent)) { + return; + } else { + visitedParents.add(currentItem.parent); + } + + let someUnchecked = false; + let someChecked = false; + for (const child of currentItem.parent.children) { + if (someUnchecked && someChecked) { + break; + } + if (child.checkbox !== undefined) { + if (child.checkbox.isChecked) { + someChecked = true; + } else { + someUnchecked = true; + } + } + } + if (someChecked && !someUnchecked && (currentItem.parent.checkbox.isChecked !== true)) { + currentItem.parent.checkbox.isChecked = true; + additionalItems.push(currentItem.parent); + checkParents(currentItem.parent); + } else if (someUnchecked && (currentItem.parent.checkbox.isChecked !== false)) { + currentItem.parent.checkbox.isChecked = false; + additionalItems.push(currentItem.parent); + checkParents(currentItem.parent); + } + } + } + checkParents(item); + } + } + } + items = items.concat(additionalItems); + items.forEach(item => { + const renderedItem = this._renderedElements.get(item.handle); + if (renderedItem) { + renderedItem.rendered.checkbox?.render(item); + } + }); + this._onDidChangeCheckboxState.fire(items); + } + disposeElement(resource: ITreeNode, index: number, templateData: ITreeExplorerTemplateData): void { templateData.elementDisposable.clear(); - this._renderedElements.delete(resource); + this._renderedElements.delete(resource.element.handle); this.treeViewsService.removeRenderedTreeItemElement(resource.element); templateData.checkbox?.dispose(); From 8265c3ec21184474684c6e0175dd2cff345230cd Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Mon, 3 Jul 2023 16:24:19 +0200 Subject: [PATCH 113/926] Git - introduce group is the long submenus (#186917) --- extensions/git/package.json | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/extensions/git/package.json b/extensions/git/package.json index c75c165c503..45a221730ca 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -1892,31 +1892,31 @@ "git.branch": [ { "command": "git.merge", - "group": "branch@1" + "group": "1_merge@1" }, { "command": "git.rebase", - "group": "branch@2" + "group": "1_merge@2" }, { "command": "git.branch", - "group": "branch@3" + "group": "2_branch@1" }, { "command": "git.branchFrom", - "group": "branch@4" + "group": "2_branch@2" }, { "command": "git.renameBranch", - "group": "branch@5" + "group": "3_modify@1" }, { "command": "git.deleteBranch", - "group": "branch@6" + "group": "3_modify@2" }, { "command": "git.publish", - "group": "branch@7" + "group": "4_publish@1" } ], "git.remotes": [ @@ -1932,40 +1932,40 @@ "git.stash": [ { "command": "git.stash", - "group": "stash@1" + "group": "1_stash@1" }, { "command": "git.stashIncludeUntracked", - "group": "stash@2" + "group": "1_stash@2" }, { "command": "git.stashStaged", "when": "gitVersion2.35", - "group": "stash@3" + "group": "1_stash@3" }, { "command": "git.stashApplyLatest", - "group": "stash@4" + "group": "2_apply@1" }, { "command": "git.stashApply", - "group": "stash@5" + "group": "2_apply@2" }, { "command": "git.stashPopLatest", - "group": "stash@6" + "group": "3_pop@1" }, { "command": "git.stashPop", - "group": "stash@7" + "group": "3_pop@2" }, { "command": "git.stashDrop", - "group": "stash@8" + "group": "4_drop@1" }, { "command": "git.stashDropAll", - "group": "stash@9" + "group": "4_drop@2" } ], "git.tags": [ From def8a208cb78184875c69ab714c0e9e6381fe09d Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Mon, 3 Jul 2023 17:03:58 +0200 Subject: [PATCH 114/926] Adresses #186918 (#186920) --- src/vs/editor/browser/widget/diffEditorWidget2/style.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/style.css b/src/vs/editor/browser/widget/diffEditorWidget2/style.css index cc28c99cdb6..f8243853b70 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/style.css +++ b/src/vs/editor/browser/widget/diffEditorWidget2/style.css @@ -78,12 +78,12 @@ border: 2px solid var(--vscode-diffEditor-move-border); } -.monaco-editor .moved-blocks-lines { +.monaco-diff-editor .moved-blocks-lines { position: absolute; pointer-events: none; } -.monaco-editor .moved-blocks-lines path { +.monaco-diff-editor .moved-blocks-lines path { fill: none; stroke: var(--vscode-diffEditor-move-border); stroke-width: 2; From 7389ceeaded34c2d9d7186bc9141522cd9376f85 Mon Sep 17 00:00:00 2001 From: yshaojun Date: Mon, 3 Jul 2023 23:43:31 +0800 Subject: [PATCH 115/926] fix: color hints may display twice(#175476) --- src/vs/editor/common/viewModel/modelLineProjection.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/common/viewModel/modelLineProjection.ts b/src/vs/editor/common/viewModel/modelLineProjection.ts index 81be5db0a10..e66499cd98e 100644 --- a/src/vs/editor/common/viewModel/modelLineProjection.ts +++ b/src/vs/editor/common/viewModel/modelLineProjection.ts @@ -193,7 +193,7 @@ class ModelLineProjection implements IModelLineProjection { if (options.inlineClassName) { const offset = (outputLineIndex > 0 ? lineBreakData.wrappedTextIndentLength : 0); const start = offset + Math.max(injectedTextStartOffsetInInputWithInjections - lineStartOffsetInInputWithInjections, 0); - const end = offset + Math.min(injectedTextEndOffsetInInputWithInjections - lineStartOffsetInInputWithInjections, lineEndOffsetInInputWithInjections); + const end = offset + Math.min(injectedTextEndOffsetInInputWithInjections - lineStartOffsetInInputWithInjections, lineEndOffsetInInputWithInjections - lineStartOffsetInInputWithInjections); if (start !== end) { inlineDecorations.push(new SingleLineInlineDecoration(start, end, options.inlineClassName, options.inlineClassNameAffectsLetterSpacing!)); } From a9409819329691186da597858b28fb15f12c06b8 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 3 Jul 2023 10:03:04 -0700 Subject: [PATCH 116/926] Only launch pty host when it's needed Part of #182637 --- src/vs/platform/terminal/common/terminal.ts | 2 + .../platform/terminal/node/ptyHostService.ts | 2 + .../browser/terminalProfileResolverService.ts | 17 ++- .../terminal/browser/terminalService.ts | 40 +++--- .../electron-sandbox/localTerminalBackend.ts | 121 +++++++++++------- .../terminalProfileResolverService.ts | 7 + 6 files changed, 122 insertions(+), 67 deletions(-) diff --git a/src/vs/platform/terminal/common/terminal.ts b/src/vs/platform/terminal/common/terminal.ts index 0a477ac20d0..edc2bce9be4 100644 --- a/src/vs/platform/terminal/common/terminal.ts +++ b/src/vs/platform/terminal/common/terminal.ts @@ -956,6 +956,8 @@ export interface ITerminalBackend { readonly whenConnected: Promise; setConnected(): void; + readonly whenPtyHostReady: Promise; + /** * Fired when the ptyHost process becomes non-responsive, this should disable stdin for all * terminals using this pty host connection and mark them as disconnected. diff --git a/src/vs/platform/terminal/node/ptyHostService.ts b/src/vs/platform/terminal/node/ptyHostService.ts index dd05cbe324a..f7698a475c7 100644 --- a/src/vs/platform/terminal/node/ptyHostService.ts +++ b/src/vs/platform/terminal/node/ptyHostService.ts @@ -279,10 +279,12 @@ export class PtyHostService extends Disposable implements IPtyService { getDefaultSystemShell(osOverride?: OperatingSystem): Promise { return this._proxy.getDefaultSystemShell(osOverride); } + // TODO: Should this only be available on the pty host service? async getProfiles(workspaceId: string, profiles: unknown, defaultProfile: unknown, includeDetectedProfiles: boolean = false): Promise { const shellEnv = await this._resolveShellEnv(); return detectAvailableProfiles(profiles, defaultProfile, includeDetectedProfiles, this._configurationService, shellEnv, undefined, this._logService, this._resolveVariables.bind(this, workspaceId)); } + // TODO: Should this only be available on the pty host service? getEnvironment(): Promise { return this._proxy.getEnvironment(); } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProfileResolverService.ts b/src/vs/workbench/contrib/terminal/browser/terminalProfileResolverService.ts index b8ee64b6a7f..30fef3e3651 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProfileResolverService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProfileResolverService.ts @@ -26,8 +26,9 @@ import { terminalProfileArgsMatch, isUriComponents } from 'vs/platform/terminal/ import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; export interface IProfileContextProvider { - getDefaultSystemShell: (remoteAuthority: string | undefined, os: OperatingSystem) => Promise; - getEnvironment: (remoteAuthority: string | undefined) => Promise; + getDefaultSystemShell(remoteAuthority: string | undefined, os: OperatingSystem): Promise; + getEnvironment(remoteAuthority: string | undefined): Promise; + getShellEnvironment(remoteAuthority: string | undefined): Promise; } const generatedProfileName = 'Generated'; @@ -274,11 +275,13 @@ export abstract class BaseTerminalProfileResolverService implements ITerminalPro } private async _resolveProfile(profile: ITerminalProfile, options: IShellLaunchConfigResolveOptions): Promise { + // TODO: is it a problem using the shell env here? + const env = await this._context.getShellEnvironment(options.remoteAuthority) || await this._context.getEnvironment(options.remoteAuthority); + if (options.os === OperatingSystem.Windows) { // Change Sysnative to System32 if the OS is Windows but NOT WoW64. It's // safe to assume that this was used by accident as Sysnative does not // exist and will break the terminal in non-WoW64 environments. - const env = await this._context.getEnvironment(options.remoteAuthority); const isWoW64 = !!env.hasOwnProperty('PROCESSOR_ARCHITEW6432'); const windir = env.windir; if (!isWoW64 && windir) { @@ -295,7 +298,6 @@ export abstract class BaseTerminalProfileResolverService implements ITerminalPro } // Resolve path variables - const env = await this._context.getEnvironment(options.remoteAuthority); const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot(options.remoteAuthority ? Schemas.vscodeRemote : Schemas.file); const lastActiveWorkspace = activeWorkspaceRootUri ? withNullAsUndefined(this._workspaceContextService.getWorkspaceFolder(activeWorkspaceRootUri)) : undefined; profile.path = await this._resolveVariables(profile.path, env, lastActiveWorkspace); @@ -427,6 +429,13 @@ export class BrowserTerminalProfileResolverService extends BaseTerminalProfileRe return env; } return backend.getEnvironment(); + }, + getShellEnvironment: async (remoteAuthority) => { + const backend = await terminalInstanceService.getBackend(remoteAuthority); + if (!remoteAuthority || !backend) { + return env; + } + return backend.getShellEnvironment(); } }, configurationService, diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index fe0b350aff9..e1707f65098 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -282,6 +282,25 @@ export class TerminalService implements ITerminalService { const isPersistentRemote = !!this._environmentService.remoteAuthority && enableTerminalReconnection; + this._primaryBackend?.onDidRequestDetach(async (e) => { + const instanceToDetach = this.getInstanceFromResource(getTerminalUri(e.workspaceId, e.instanceId)); + if (instanceToDetach) { + const persistentProcessId = instanceToDetach?.persistentProcessId; + if (persistentProcessId && !instanceToDetach.shellLaunchConfig.isFeatureTerminal && !instanceToDetach.shellLaunchConfig.customPtyImplementation) { + if (instanceToDetach.target === TerminalLocation.Editor) { + this._terminalEditorService.detachInstance(instanceToDetach); + } else { + this._terminalGroupService.getGroupForInstance(instanceToDetach)?.removeInstance(instanceToDetach); + } + await instanceToDetach.detachProcessAndDispose(TerminalExitReason.User); + await this._primaryBackend?.acceptDetachInstanceReply(e.requestId, persistentProcessId); + } else { + // will get rejected without a persistentProcessId to attach to + await this._primaryBackend?.acceptDetachInstanceReply(e.requestId, undefined); + } + } + }); + mark('code/terminal/willReconnect'); let reconnectedPromise: Promise; if (isPersistentRemote) { @@ -300,31 +319,12 @@ export class TerminalService implements ITerminalService { mark('code/terminal/didReplay'); mark('code/terminal/willGetPerformanceMarks'); await Promise.all(Array.from(this._terminalInstanceService.getRegisteredBackends()).map(async backend => { - this._timerService.setPerformanceMarks(backend.remoteAuthority === undefined ? 'localPtyHost' : 'remotePtyHost', await backend.getPerformanceMarks()); + this._timerService.setPerformanceMarks(backend.remoteAuthority === undefined ? 'localPtyHost' : 'remotePtyHost', (instances.length > 0 ? await backend.getPerformanceMarks() : [])); backend.setConnected(); })); mark('code/terminal/didGetPerformanceMarks'); this._whenConnected.complete(); }); - - this._primaryBackend?.onDidRequestDetach(async (e) => { - const instanceToDetach = this.getInstanceFromResource(getTerminalUri(e.workspaceId, e.instanceId)); - if (instanceToDetach) { - const persistentProcessId = instanceToDetach?.persistentProcessId; - if (persistentProcessId && !instanceToDetach.shellLaunchConfig.isFeatureTerminal && !instanceToDetach.shellLaunchConfig.customPtyImplementation) { - if (instanceToDetach.target === TerminalLocation.Editor) { - this._terminalEditorService.detachInstance(instanceToDetach); - } else { - this._terminalGroupService.getGroupForInstance(instanceToDetach)?.removeInstance(instanceToDetach); - } - await instanceToDetach.detachProcessAndDispose(TerminalExitReason.User); - await this._primaryBackend?.acceptDetachInstanceReply(e.requestId, persistentProcessId); - } else { - // will get rejected without a persistentProcessId to attach to - await this._primaryBackend?.acceptDetachInstanceReply(e.requestId, undefined); - } - } - }); } getPrimaryBackend(): ITerminalBackend | undefined { diff --git a/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts b/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts index aec12e8d335..3b903c4e107 100644 --- a/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts +++ b/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Emitter, Event } from 'vs/base/common/event'; +import { Emitter } from 'vs/base/common/event'; import { IProcessEnvironment, isMacintosh, isWindows, OperatingSystem } from 'vs/base/common/platform'; import { withNullAsUndefined } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; @@ -51,13 +51,19 @@ export class LocalTerminalBackendContribution implements IWorkbenchContribution class LocalTerminalBackend extends BaseTerminalBackend implements ITerminalBackend { readonly remoteAuthority = undefined; + // TODO: Add __proxy, _proxy can be a getter which connects private _proxy!: IPtyService; + private _clientEventually: DeferredPromise | undefined; + private readonly _ptys: Map = new Map(); private readonly _whenConnected = new DeferredPromise(); get whenConnected(): Promise { return this._whenConnected.p; } setConnected(): void { this._whenConnected.complete(); } + private readonly _whenPtyHostReady = new DeferredPromise(); + get whenPtyHostReady(): Promise { return this._whenPtyHostReady.p; } + private readonly _onDidRequestDetach = this._register(new Emitter<{ requestId: number; workspaceId: string; instanceId: number }>()); readonly onDidRequestDetach = this._onDidRequestDetach.event; @@ -81,19 +87,22 @@ class LocalTerminalBackend extends BaseTerminalBackend implements ITerminalBacke ) { super(_localPtyService, logService, historyService, _configurationResolverService, statusBarService, workspaceContextService); - this._register(Event.runAndSubscribe(this.onPtyHostRestart, () => { - this._logService.debug('Starting pty host'); - const clientEventually = new DeferredPromise(); - this._proxy = ProxyChannel.toService(getDelayedChannel(clientEventually.p.then(client => client.getChannel(TerminalIpcChannels.PtyHostWindow)))); - this._connectToDirectProxy(clientEventually); - })); - - // Eagerly fetch the backend's environment for memoization - this.getEnvironment(); + this.onPtyHostRestart(() => this._connectToDirectProxy()); } - private async _connectToDirectProxy(clientEventually: DeferredPromise): Promise { - // The pty host should not get launched until the first window restored phase + // TODO: Should this only be connected on createProcess and related calls? All others are routed to ILocalPtyService until direct is available? + private async _connectToDirectProxy(): Promise { + if (this._clientEventually) { + await this._clientEventually.p; + return; + } + + this._logService.debug('Starting pty host', new Error().stack); + const clientEventually = new DeferredPromise(); + this._clientEventually = clientEventually; + this._proxy = ProxyChannel.toService(getDelayedChannel(this._clientEventually.p.then(client => client.getChannel(TerminalIpcChannels.PtyHostWindow)))); + + // The pty host should not get launched until at least the window restored phase await this._lifecycleService.when(LifecyclePhase.Restored); mark('code/terminal/willConnectPtyHost'); @@ -147,14 +156,22 @@ class LocalTerminalBackend extends BaseTerminalBackend implements ITerminalBacke } } })); + + // Tell consumers the pty host has been connected + this._whenPtyHostReady.complete(); + + // Eagerly fetch the backend's environment for memoization + this.getEnvironment(); }); } async requestDetachInstance(workspaceId: string, instanceId: number): Promise { + await this._connectToDirectProxy(); return this._proxy.requestDetachInstance(workspaceId, instanceId); } async acceptDetachInstanceReply(requestId: number, persistentProcessId?: number): Promise { + await this._connectToDirectProxy(); if (!persistentProcessId) { this._logService.warn('Cannot attach to feature terminals, custom pty terminals, or those without a persistentProcessId'); return; @@ -163,20 +180,24 @@ class LocalTerminalBackend extends BaseTerminalBackend implements ITerminalBacke } async persistTerminalState(): Promise { + await this._connectToDirectProxy(); const ids = Array.from(this._ptys.keys()); const serialized = await this._proxy.serializeTerminalState(ids); this._storageService.store(TerminalStorageKeys.TerminalBufferState, serialized, StorageScope.WORKSPACE, StorageTarget.MACHINE); } async updateTitle(id: number, title: string, titleSource: TitleEventSource): Promise { + await this._connectToDirectProxy(); await this._proxy.updateTitle(id, title, titleSource); } async updateIcon(id: number, userInitiated: boolean, icon: URI | { light: URI; dark: URI } | { id: string; color?: { id: string } }, color?: string): Promise { + await this._connectToDirectProxy(); await this._proxy.updateIcon(id, userInitiated, icon, color); } async updateProperty(id: number, property: ProcessPropertyType, value: IProcessPropertyMap[T]): Promise { + await this._connectToDirectProxy(); return this._proxy.updateProperty(id, property, value); } @@ -190,6 +211,7 @@ class LocalTerminalBackend extends BaseTerminalBackend implements ITerminalBacke options: ITerminalProcessOptions, shouldPersist: boolean ): Promise { + await this._connectToDirectProxy(); const executableEnv = await this._shellEnvironmentService.getShellEnv(); const id = await this._proxy.createProcess(shellLaunchConfig, cwd, cols, rows, unicodeVersion, env, executableEnv, options, shouldPersist, this._getWorkspaceId(), this._getWorkspaceName()); const pty = new LocalPty(id, shouldPersist, this._proxy); @@ -198,6 +220,7 @@ class LocalTerminalBackend extends BaseTerminalBackend implements ITerminalBacke } async attachToProcess(id: number): Promise { + await this._connectToDirectProxy(); try { await this._proxy.attachToProcess(id); const pty = new LocalPty(id, true, this._proxy); @@ -210,6 +233,7 @@ class LocalTerminalBackend extends BaseTerminalBackend implements ITerminalBacke } async attachToRevivedProcess(id: number): Promise { + await this._connectToDirectProxy(); try { const newId = await this._proxy.getRevivedPtyNewId(id) ?? id; return await this.attachToProcess(newId); @@ -220,18 +244,22 @@ class LocalTerminalBackend extends BaseTerminalBackend implements ITerminalBacke } async listProcesses(): Promise { + await this._connectToDirectProxy(); return this._proxy.listProcesses(); } async getPerformanceMarks(): Promise { + await this._connectToDirectProxy(); return this._proxy.getPerformanceMarks(); } async reduceConnectionGraceTime(): Promise { + await this._connectToDirectProxy(); this._proxy.reduceConnectionGraceTime(); } async getDefaultSystemShell(osOverride?: OperatingSystem): Promise { + await this._connectToDirectProxy(); return this._proxy.getDefaultSystemShell(osOverride); } @@ -242,6 +270,7 @@ class LocalTerminalBackend extends BaseTerminalBackend implements ITerminalBacke @memoize async getEnvironment(): Promise { + await this._connectToDirectProxy(); return this._proxy.getEnvironment(); } @@ -251,10 +280,12 @@ class LocalTerminalBackend extends BaseTerminalBackend implements ITerminalBacke } async getWslPath(original: string, direction: 'unix-to-win' | 'win-to-unix'): Promise { + await this._connectToDirectProxy(); return this._proxy.getWslPath(original, direction); } async setTerminalLayoutInfo(layoutInfo?: ITerminalsLayoutInfoById): Promise { + await this._connectToDirectProxy(); const args: ISetTerminalLayoutInfoArgs = { workspaceId: this._getWorkspaceId(), tabs: layoutInfo ? layoutInfo.tabs : [] @@ -273,46 +304,50 @@ class LocalTerminalBackend extends BaseTerminalBackend implements ITerminalBacke // Revive processes if needed const serializedState = this._storageService.get(TerminalStorageKeys.TerminalBufferState, StorageScope.WORKSPACE); const parsed = this._deserializeTerminalState(serializedState); - if (parsed) { - try { - // Create variable resolver - const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot(); - const lastActiveWorkspace = activeWorkspaceRootUri ? withNullAsUndefined(this._workspaceContextService.getWorkspaceFolder(activeWorkspaceRootUri)) : undefined; - const variableResolver = terminalEnvironment.createVariableResolver(lastActiveWorkspace, await this._terminalProfileResolverService.getEnvironment(this.remoteAuthority), this._configurationResolverService); + if (!parsed) { + return undefined; + } - // Re-resolve the environments and replace it on the state so local terminals use a fresh - // environment - mark('code/terminal/willGetReviveEnvironments'); - await Promise.all(parsed.map(state => new Promise(r => { - this._resolveEnvironmentForRevive(variableResolver, state.shellLaunchConfig).then(freshEnv => { - state.processLaunchConfig.env = freshEnv; - r(); - }); - }))); - mark('code/terminal/didGetReviveEnvironments'); + await this._connectToDirectProxy(); + try { + // Create variable resolver + const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot(); + const lastActiveWorkspace = activeWorkspaceRootUri ? withNullAsUndefined(this._workspaceContextService.getWorkspaceFolder(activeWorkspaceRootUri)) : undefined; + const variableResolver = terminalEnvironment.createVariableResolver(lastActiveWorkspace, await this._terminalProfileResolverService.getEnvironment(this.remoteAuthority), this._configurationResolverService); - mark('code/terminal/willReviveTerminalProcesses'); - await this._proxy.reviveTerminalProcesses(parsed, Intl.DateTimeFormat().resolvedOptions().locale); - mark('code/terminal/didReviveTerminalProcesses'); - this._storageService.remove(TerminalStorageKeys.TerminalBufferState, StorageScope.WORKSPACE); - // If reviving processes, send the terminal layout info back to the pty host as it - // will not have been persisted on application exit - const layoutInfo = this._storageService.get(TerminalStorageKeys.TerminalLayoutInfo, StorageScope.WORKSPACE); - if (layoutInfo) { - mark('code/terminal/willSetTerminalLayoutInfo'); - await this._proxy.setTerminalLayoutInfo(JSON.parse(layoutInfo)); - mark('code/terminal/didSetTerminalLayoutInfo'); - this._storageService.remove(TerminalStorageKeys.TerminalLayoutInfo, StorageScope.WORKSPACE); - } - } catch (e: unknown) { - this._logService.warn('LocalTerminalBackend#getTerminalLayoutInfo Error', e && typeof e === 'object' && 'message' in e ? e.message : e); + // Re-resolve the environments and replace it on the state so local terminals use a fresh + // environment + mark('code/terminal/willGetReviveEnvironments'); + await Promise.all(parsed.map(state => new Promise(r => { + this._resolveEnvironmentForRevive(variableResolver, state.shellLaunchConfig).then(freshEnv => { + state.processLaunchConfig.env = freshEnv; + r(); + }); + }))); + mark('code/terminal/didGetReviveEnvironments'); + + mark('code/terminal/willReviveTerminalProcesses'); + await this._proxy.reviveTerminalProcesses(parsed, Intl.DateTimeFormat().resolvedOptions().locale); + mark('code/terminal/didReviveTerminalProcesses'); + this._storageService.remove(TerminalStorageKeys.TerminalBufferState, StorageScope.WORKSPACE); + // If reviving processes, send the terminal layout info back to the pty host as it + // will not have been persisted on application exit + const layoutInfo = this._storageService.get(TerminalStorageKeys.TerminalLayoutInfo, StorageScope.WORKSPACE); + if (layoutInfo) { + mark('code/terminal/willSetTerminalLayoutInfo'); + await this._proxy.setTerminalLayoutInfo(JSON.parse(layoutInfo)); + mark('code/terminal/didSetTerminalLayoutInfo'); + this._storageService.remove(TerminalStorageKeys.TerminalLayoutInfo, StorageScope.WORKSPACE); } + } catch (e: unknown) { + this._logService.warn('LocalTerminalBackend#getTerminalLayoutInfo Error', e && typeof e === 'object' && 'message' in e ? e.message : e); } return this._proxy.getTerminalLayoutInfo(layoutArgs); } private async _resolveEnvironmentForRevive(variableResolver: terminalEnvironment.VariableResolver | undefined, shellLaunchConfig: IShellLaunchConfig): Promise { + await this._connectToDirectProxy(); const platformKey = isWindows ? 'windows' : (isMacintosh ? 'osx' : 'linux'); const envFromConfigValue = this._configurationService.getValue(`terminal.integrated.env.${platformKey}`); const baseEnv = await (shellLaunchConfig.useShellEnvironment ? this.getShellEnvironment() : this.getEnvironment()); diff --git a/src/vs/workbench/contrib/terminal/electron-sandbox/terminalProfileResolverService.ts b/src/vs/workbench/contrib/terminal/electron-sandbox/terminalProfileResolverService.ts index 602b47386ba..e7890897bc7 100644 --- a/src/vs/workbench/contrib/terminal/electron-sandbox/terminalProfileResolverService.ts +++ b/src/vs/workbench/contrib/terminal/electron-sandbox/terminalProfileResolverService.ts @@ -41,6 +41,13 @@ export class ElectronTerminalProfileResolverService extends BaseTerminalProfileR throw new ErrorNoTelemetry(`Cannot get environment when there is no backend for remote authority '${remoteAuthority}'`); } return backend.getEnvironment(); + }, + getShellEnvironment: async (remoteAuthority) => { + const backend = await terminalInstanceService.getBackend(remoteAuthority); + if (!backend) { + throw new ErrorNoTelemetry(`Cannot get environment when there is no backend for remote authority '${remoteAuthority}'`); + } + return backend.getShellEnvironment(); } }, configurationService, From 7fd34784adde658e335489da69e71262ca1e1340 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 3 Jul 2023 10:24:58 -0700 Subject: [PATCH 117/926] defer buffer creation --- .../browser/terminal.accessibility.contribution.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts index a121b0c4019..23a3f640347 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts @@ -26,6 +26,7 @@ import { Terminal } from 'xterm'; class AccessibleBufferContribution extends DisposableStore implements ITerminalContribution { static readonly ID = 'terminal.accessible-buffer'; + private _xterm: IXtermTerminal & { raw: Terminal } | undefined; static get(instance: ITerminalInstance): AccessibleBufferContribution | null { return instance.getContribution(AccessibleBufferContribution.ID); } @@ -40,11 +41,15 @@ class AccessibleBufferContribution extends DisposableStore implements ITerminalC super(); } layout(xterm: IXtermTerminal & { raw: Terminal }): void { - if (!this._accessibleBufferWidget) { - this._accessibleBufferWidget = this.add(this._instantiationService.createInstance(AccessibleBufferWidget, this._instance, xterm)); - } + this._xterm = xterm; } async show(): Promise { + if (!this._xterm) { + return; + } + if (!this._accessibleBufferWidget) { + this._accessibleBufferWidget = this.add(this._instantiationService.createInstance(AccessibleBufferWidget, this._instance, this._xterm)); + } await this._accessibleBufferWidget?.show(); } From 0ef9e9a0fa1d13e8b20f96fb7f142daabc4b6e8e Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 3 Jul 2023 10:27:04 -0700 Subject: [PATCH 118/926] rm ? --- .../browser/terminal.accessibility.contribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts index 23a3f640347..c59c6db03e2 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts @@ -50,7 +50,7 @@ class AccessibleBufferContribution extends DisposableStore implements ITerminalC if (!this._accessibleBufferWidget) { this._accessibleBufferWidget = this.add(this._instantiationService.createInstance(AccessibleBufferWidget, this._instance, this._xterm)); } - await this._accessibleBufferWidget?.show(); + await this._accessibleBufferWidget.show(); } async createCommandQuickPick(): Promise | undefined> { From dfbba3846fda8d9edfafa0215cf4966e2569acf0 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 3 Jul 2023 11:10:47 -0700 Subject: [PATCH 119/926] Only connect to direct proxy when creating a process --- src/vs/platform/terminal/common/terminal.ts | 2 - .../electron-sandbox/localTerminalBackend.ts | 70 ++++++++----------- 2 files changed, 29 insertions(+), 43 deletions(-) diff --git a/src/vs/platform/terminal/common/terminal.ts b/src/vs/platform/terminal/common/terminal.ts index edc2bce9be4..0a477ac20d0 100644 --- a/src/vs/platform/terminal/common/terminal.ts +++ b/src/vs/platform/terminal/common/terminal.ts @@ -956,8 +956,6 @@ export interface ITerminalBackend { readonly whenConnected: Promise; setConnected(): void; - readonly whenPtyHostReady: Promise; - /** * Fired when the ptyHost process becomes non-responsive, this should disable stdin for all * terminals using this pty host connection and mark them as disconnected. diff --git a/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts b/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts index 3b903c4e107..4a98a4fc1a2 100644 --- a/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts +++ b/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts @@ -51,19 +51,22 @@ export class LocalTerminalBackendContribution implements IWorkbenchContribution class LocalTerminalBackend extends BaseTerminalBackend implements ITerminalBackend { readonly remoteAuthority = undefined; - // TODO: Add __proxy, _proxy can be a getter which connects - private _proxy!: IPtyService; - private _clientEventually: DeferredPromise | undefined; - private readonly _ptys: Map = new Map(); + private _directProxyClientEventually: DeferredPromise | undefined; + private _directProxy: IPtyService | undefined; + /** + * Communicate to the direct proxy (renderer<->ptyhost) is it's available, otherwise use the + * indirect proxy (renderer<->main<->ptyhost). The latter may not need to actually launch the + * pty host, for example when detecting profiles. + */ + private get _proxy(): IPtyService { return this._directProxy || this._localPtyService; } + + // TODO: Clarify naming, connected to what? private readonly _whenConnected = new DeferredPromise(); get whenConnected(): Promise { return this._whenConnected.p; } setConnected(): void { this._whenConnected.complete(); } - private readonly _whenPtyHostReady = new DeferredPromise(); - get whenPtyHostReady(): Promise { return this._whenPtyHostReady.p; } - private readonly _onDidRequestDetach = this._register(new Emitter<{ requestId: number; workspaceId: string; instanceId: number }>()); readonly onDidRequestDetach = this._onDidRequestDetach.event; @@ -92,15 +95,17 @@ class LocalTerminalBackend extends BaseTerminalBackend implements ITerminalBacke // TODO: Should this only be connected on createProcess and related calls? All others are routed to ILocalPtyService until direct is available? private async _connectToDirectProxy(): Promise { - if (this._clientEventually) { - await this._clientEventually.p; + // Check if connecting is in progress + if (this._directProxyClientEventually) { + await this._directProxyClientEventually.p; return; } - this._logService.debug('Starting pty host', new Error().stack); - const clientEventually = new DeferredPromise(); - this._clientEventually = clientEventually; - this._proxy = ProxyChannel.toService(getDelayedChannel(this._clientEventually.p.then(client => client.getChannel(TerminalIpcChannels.PtyHostWindow)))); + this._logService.debug('Starting pty host'); + const directProxyClientEventually = new DeferredPromise(); + this._directProxyClientEventually = directProxyClientEventually; + const directProxy = ProxyChannel.toService(getDelayedChannel(this._directProxyClientEventually.p.then(client => client.getChannel(TerminalIpcChannels.PtyHostWindow)))); + this._directProxy = directProxy; // The pty host should not get launched until at least the window restored phase await this._lifecycleService.when(LifecyclePhase.Restored); @@ -115,23 +120,23 @@ class LocalTerminalBackend extends BaseTerminalBackend implements ITerminalBacke // used for pty host management messages, it would make sense in the future to use a // separate interface/service for this one. const client = new MessagePortClient(port, `window:${this._environmentService.window.id}`); - clientEventually.complete(client); + directProxyClientEventually.complete(client); this._onPtyHostConnected.fire(); // Attach process listeners - this._proxy.onProcessData(e => this._ptys.get(e.id)?.handleData(e.event)); - this._proxy.onDidChangeProperty(e => this._ptys.get(e.id)?.handleDidChangeProperty(e.property)); - this._proxy.onProcessExit(e => { + directProxy.onProcessData(e => this._ptys.get(e.id)?.handleData(e.event)); + directProxy.onDidChangeProperty(e => this._ptys.get(e.id)?.handleDidChangeProperty(e.property)); + directProxy.onProcessExit(e => { const pty = this._ptys.get(e.id); if (pty) { pty.handleExit(e.event); this._ptys.delete(e.id); } }); - this._proxy.onProcessReady(e => this._ptys.get(e.id)?.handleReady(e.event)); - this._proxy.onProcessReplay(e => this._ptys.get(e.id)?.handleReplay(e.event)); - this._proxy.onProcessOrphanQuestion(e => this._ptys.get(e.id)?.handleOrphanQuestion()); - this._proxy.onDidRequestDetach(e => this._onDidRequestDetach.fire(e)); + directProxy.onProcessReady(e => this._ptys.get(e.id)?.handleReady(e.event)); + directProxy.onProcessReplay(e => this._ptys.get(e.id)?.handleReplay(e.event)); + directProxy.onProcessOrphanQuestion(e => this._ptys.get(e.id)?.handleOrphanQuestion()); + directProxy.onDidRequestDetach(e => this._onDidRequestDetach.fire(e)); // Listen for config changes const initialConfig = this._configurationService.getValue(TERMINAL_CONFIG_SECTION); @@ -139,13 +144,13 @@ class LocalTerminalBackend extends BaseTerminalBackend implements ITerminalBacke // Ensure the reply is value const reply = initialConfig.autoReplies[match] as string | null; if (reply) { - this._proxy.installAutoReply(match, reply); + directProxy.installAutoReply(match, reply); } } // TODO: Could simplify update to a single call this._register(this._configurationService.onDidChangeConfiguration(async e => { if (e.affectsConfiguration(TerminalSettingId.AutoReplies)) { - this._proxy.uninstallAllAutoReplies(); + directProxy.uninstallAllAutoReplies(); const config = this._configurationService.getValue(TERMINAL_CONFIG_SECTION); for (const match of Object.keys(config.autoReplies)) { // Ensure the reply is value @@ -157,21 +162,16 @@ class LocalTerminalBackend extends BaseTerminalBackend implements ITerminalBacke } })); - // Tell consumers the pty host has been connected - this._whenPtyHostReady.complete(); - // Eagerly fetch the backend's environment for memoization this.getEnvironment(); }); } async requestDetachInstance(workspaceId: string, instanceId: number): Promise { - await this._connectToDirectProxy(); return this._proxy.requestDetachInstance(workspaceId, instanceId); } async acceptDetachInstanceReply(requestId: number, persistentProcessId?: number): Promise { - await this._connectToDirectProxy(); if (!persistentProcessId) { this._logService.warn('Cannot attach to feature terminals, custom pty terminals, or those without a persistentProcessId'); return; @@ -180,24 +180,20 @@ class LocalTerminalBackend extends BaseTerminalBackend implements ITerminalBacke } async persistTerminalState(): Promise { - await this._connectToDirectProxy(); const ids = Array.from(this._ptys.keys()); const serialized = await this._proxy.serializeTerminalState(ids); this._storageService.store(TerminalStorageKeys.TerminalBufferState, serialized, StorageScope.WORKSPACE, StorageTarget.MACHINE); } async updateTitle(id: number, title: string, titleSource: TitleEventSource): Promise { - await this._connectToDirectProxy(); await this._proxy.updateTitle(id, title, titleSource); } async updateIcon(id: number, userInitiated: boolean, icon: URI | { light: URI; dark: URI } | { id: string; color?: { id: string } }, color?: string): Promise { - await this._connectToDirectProxy(); await this._proxy.updateIcon(id, userInitiated, icon, color); } async updateProperty(id: number, property: ProcessPropertyType, value: IProcessPropertyMap[T]): Promise { - await this._connectToDirectProxy(); return this._proxy.updateProperty(id, property, value); } @@ -244,33 +240,28 @@ class LocalTerminalBackend extends BaseTerminalBackend implements ITerminalBacke } async listProcesses(): Promise { - await this._connectToDirectProxy(); return this._proxy.listProcesses(); } async getPerformanceMarks(): Promise { - await this._connectToDirectProxy(); return this._proxy.getPerformanceMarks(); } async reduceConnectionGraceTime(): Promise { - await this._connectToDirectProxy(); this._proxy.reduceConnectionGraceTime(); } async getDefaultSystemShell(osOverride?: OperatingSystem): Promise { - await this._connectToDirectProxy(); return this._proxy.getDefaultSystemShell(osOverride); } async getProfiles(profiles: unknown, defaultProfile: unknown, includeDetectedProfiles?: boolean) { // TODO: Differentiate interfaces of direct to pty host and pty host service (or just move them all to pty host) - return this._localPtyService.getProfiles?.(this._workspaceContextService.getWorkspace().id, profiles, defaultProfile, includeDetectedProfiles) || []; + return this._proxy.getProfiles?.(this._workspaceContextService.getWorkspace().id, profiles, defaultProfile, includeDetectedProfiles) || []; } @memoize async getEnvironment(): Promise { - await this._connectToDirectProxy(); return this._proxy.getEnvironment(); } @@ -280,7 +271,6 @@ class LocalTerminalBackend extends BaseTerminalBackend implements ITerminalBacke } async getWslPath(original: string, direction: 'unix-to-win' | 'win-to-unix'): Promise { - await this._connectToDirectProxy(); return this._proxy.getWslPath(original, direction); } @@ -308,7 +298,6 @@ class LocalTerminalBackend extends BaseTerminalBackend implements ITerminalBacke return undefined; } - await this._connectToDirectProxy(); try { // Create variable resolver const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot(); @@ -347,7 +336,6 @@ class LocalTerminalBackend extends BaseTerminalBackend implements ITerminalBacke } private async _resolveEnvironmentForRevive(variableResolver: terminalEnvironment.VariableResolver | undefined, shellLaunchConfig: IShellLaunchConfig): Promise { - await this._connectToDirectProxy(); const platformKey = isWindows ? 'windows' : (isMacintosh ? 'osx' : 'linux'); const envFromConfigValue = this._configurationService.getValue(`terminal.integrated.env.${platformKey}`); const baseEnv = await (shellLaunchConfig.useShellEnvironment ? this.getShellEnvironment() : this.getEnvironment()); From 70e611de2847617363d93e28d5769d0656d4360f Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 3 Jul 2023 11:16:35 -0700 Subject: [PATCH 120/926] whenConnected -> whenReady --- src/vs/platform/terminal/common/terminal.ts | 12 ++++++++++-- .../terminal/browser/remoteTerminalBackend.ts | 4 ++-- .../contrib/terminal/browser/terminalService.ts | 2 +- .../electron-sandbox/localTerminalBackend.ts | 12 +++++++----- .../workbench/services/timer/browser/timerService.ts | 2 +- 5 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/vs/platform/terminal/common/terminal.ts b/src/vs/platform/terminal/common/terminal.ts index 0a477ac20d0..07efa691afd 100644 --- a/src/vs/platform/terminal/common/terminal.ts +++ b/src/vs/platform/terminal/common/terminal.ts @@ -953,8 +953,16 @@ export interface ITerminalBackend { readonly remoteAuthority: string | undefined; readonly isResponsive: boolean; - readonly whenConnected: Promise; - setConnected(): void; + + /** + * A promise that resolves when the backend is ready to be used, ie. after terminal persistence + * has been actioned. + */ + readonly whenReady: Promise; + /** + * Signal to the backend that persistence has been actioned and is ready for use. + */ + setReady(): void; /** * Fired when the ptyHost process becomes non-responsive, this should disable stdin for all diff --git a/src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts b/src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts index 753db8b5d71..fb741eebd25 100644 --- a/src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts +++ b/src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts @@ -50,8 +50,8 @@ class RemoteTerminalBackend extends BaseTerminalBackend implements ITerminalBack private readonly _ptys: Map = new Map(); private readonly _whenConnected = new DeferredPromise(); - get whenConnected(): Promise { return this._whenConnected.p; } - setConnected(): void { this._whenConnected.complete(); } + get whenReady(): Promise { return this._whenConnected.p; } + setReady(): void { this._whenConnected.complete(); } private readonly _onDidRequestDetach = this._register(new Emitter<{ requestId: number; workspaceId: string; instanceId: number }>()); readonly onDidRequestDetach = this._onDidRequestDetach.event; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index e1707f65098..4dcdb37f173 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -320,7 +320,7 @@ export class TerminalService implements ITerminalService { mark('code/terminal/willGetPerformanceMarks'); await Promise.all(Array.from(this._terminalInstanceService.getRegisteredBackends()).map(async backend => { this._timerService.setPerformanceMarks(backend.remoteAuthority === undefined ? 'localPtyHost' : 'remotePtyHost', (instances.length > 0 ? await backend.getPerformanceMarks() : [])); - backend.setConnected(); + backend.setReady(); })); mark('code/terminal/didGetPerformanceMarks'); this._whenConnected.complete(); diff --git a/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts b/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts index 4a98a4fc1a2..9fe0fd5aa21 100644 --- a/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts +++ b/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts @@ -62,10 +62,9 @@ class LocalTerminalBackend extends BaseTerminalBackend implements ITerminalBacke */ private get _proxy(): IPtyService { return this._directProxy || this._localPtyService; } - // TODO: Clarify naming, connected to what? - private readonly _whenConnected = new DeferredPromise(); - get whenConnected(): Promise { return this._whenConnected.p; } - setConnected(): void { this._whenConnected.complete(); } + private readonly _whenReady = new DeferredPromise(); + get whenReady(): Promise { return this._whenReady.p; } + setReady(): void { this._whenReady.complete(); } private readonly _onDidRequestDetach = this._register(new Emitter<{ requestId: number; workspaceId: string; instanceId: number }>()); readonly onDidRequestDetach = this._onDidRequestDetach.event; @@ -93,7 +92,9 @@ class LocalTerminalBackend extends BaseTerminalBackend implements ITerminalBacke this.onPtyHostRestart(() => this._connectToDirectProxy()); } - // TODO: Should this only be connected on createProcess and related calls? All others are routed to ILocalPtyService until direct is available? + /** + * Request a direct connection to the pty host, this will launch the pty host process if necessary. + */ private async _connectToDirectProxy(): Promise { // Check if connecting is in progress if (this._directProxyClientEventually) { @@ -240,6 +241,7 @@ class LocalTerminalBackend extends BaseTerminalBackend implements ITerminalBacke } async listProcesses(): Promise { + await this._connectToDirectProxy(); return this._proxy.listProcesses(); } diff --git a/src/vs/workbench/services/timer/browser/timerService.ts b/src/vs/workbench/services/timer/browser/timerService.ts index 0b03341f673..d70ec4df70a 100644 --- a/src/vs/workbench/services/timer/browser/timerService.ts +++ b/src/vs/workbench/services/timer/browser/timerService.ts @@ -504,7 +504,7 @@ export abstract class AbstractTimerService implements ITimerService { this._extensionService.whenInstalledExtensionsRegistered(), // extensions registered _lifecycleService.when(LifecyclePhase.Restored), // workbench created and parts restored layoutService.whenRestored, // layout restored (including visible editors resolved) - Promise.all(Array.from(Registry.as(TerminalExtensions.Backend).backends.values()).map(e => e.whenConnected)) + Promise.all(Array.from(Registry.as(TerminalExtensions.Backend).backends.values()).map(e => e.whenReady)) ]).then(() => { // set perf mark from renderer this.setPerformanceMarks('renderer', perf.getMarks()); From 6988c84829b1926f601843e0a7a3c41e91503c59 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 3 Jul 2023 11:23:28 -0700 Subject: [PATCH 121/926] Don't launch pty host on getEnvironment/getPerformanceMarks --- src/vs/platform/terminal/node/ptyHostService.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/vs/platform/terminal/node/ptyHostService.ts b/src/vs/platform/terminal/node/ptyHostService.ts index f7698a475c7..efe79cfb211 100644 --- a/src/vs/platform/terminal/node/ptyHostService.ts +++ b/src/vs/platform/terminal/node/ptyHostService.ts @@ -223,7 +223,10 @@ export class PtyHostService extends Disposable implements IPtyService { listProcesses(): Promise { return this._proxy.listProcesses(); } - getPerformanceMarks(): Promise { + async getPerformanceMarks(): Promise { + if (!this.__proxy) { + return []; + } return this._proxy.getPerformanceMarks(); } reduceConnectionGraceTime(): Promise { @@ -279,13 +282,16 @@ export class PtyHostService extends Disposable implements IPtyService { getDefaultSystemShell(osOverride?: OperatingSystem): Promise { return this._proxy.getDefaultSystemShell(osOverride); } - // TODO: Should this only be available on the pty host service? async getProfiles(workspaceId: string, profiles: unknown, defaultProfile: unknown, includeDetectedProfiles: boolean = false): Promise { const shellEnv = await this._resolveShellEnv(); return detectAvailableProfiles(profiles, defaultProfile, includeDetectedProfiles, this._configurationService, shellEnv, undefined, this._logService, this._resolveVariables.bind(this, workspaceId)); } - // TODO: Should this only be available on the pty host service? - getEnvironment(): Promise { + async getEnvironment(): Promise { + // If the pty host is yet to be launched, just return the environment of this process as it + // is essentially the same when used to evaluate terminal profiles. + if (!this.__proxy) { + return { ...process.env }; + } return this._proxy.getEnvironment(); } getWslPath(original: string, direction: 'unix-to-win' | 'win-to-unix'): Promise { From b7dc7ce41b5153d3316eaaccffe67464056937e3 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 3 Jul 2023 11:23:56 -0700 Subject: [PATCH 122/926] Defer to backend for whether to get perf marks or not --- src/vs/workbench/contrib/terminal/browser/terminalService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index 4dcdb37f173..90152cd6a12 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -319,7 +319,7 @@ export class TerminalService implements ITerminalService { mark('code/terminal/didReplay'); mark('code/terminal/willGetPerformanceMarks'); await Promise.all(Array.from(this._terminalInstanceService.getRegisteredBackends()).map(async backend => { - this._timerService.setPerformanceMarks(backend.remoteAuthority === undefined ? 'localPtyHost' : 'remotePtyHost', (instances.length > 0 ? await backend.getPerformanceMarks() : [])); + this._timerService.setPerformanceMarks(backend.remoteAuthority === undefined ? 'localPtyHost' : 'remotePtyHost', await backend.getPerformanceMarks()); backend.setReady(); })); mark('code/terminal/didGetPerformanceMarks'); From ca9192e80b096d715378e905fc074723924bad10 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 3 Jul 2023 11:29:02 -0700 Subject: [PATCH 123/926] Remove getShellEnv impl from profile resolver services --- .../browser/terminalProfileResolverService.ts | 11 +---------- .../terminalProfileResolverService.ts | 7 ------- 2 files changed, 1 insertion(+), 17 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProfileResolverService.ts b/src/vs/workbench/contrib/terminal/browser/terminalProfileResolverService.ts index 30fef3e3651..1b0aa408a81 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProfileResolverService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProfileResolverService.ts @@ -28,7 +28,6 @@ import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/ export interface IProfileContextProvider { getDefaultSystemShell(remoteAuthority: string | undefined, os: OperatingSystem): Promise; getEnvironment(remoteAuthority: string | undefined): Promise; - getShellEnvironment(remoteAuthority: string | undefined): Promise; } const generatedProfileName = 'Generated'; @@ -275,8 +274,7 @@ export abstract class BaseTerminalProfileResolverService implements ITerminalPro } private async _resolveProfile(profile: ITerminalProfile, options: IShellLaunchConfigResolveOptions): Promise { - // TODO: is it a problem using the shell env here? - const env = await this._context.getShellEnvironment(options.remoteAuthority) || await this._context.getEnvironment(options.remoteAuthority); + const env = await this._context.getEnvironment(options.remoteAuthority); if (options.os === OperatingSystem.Windows) { // Change Sysnative to System32 if the OS is Windows but NOT WoW64. It's @@ -429,13 +427,6 @@ export class BrowserTerminalProfileResolverService extends BaseTerminalProfileRe return env; } return backend.getEnvironment(); - }, - getShellEnvironment: async (remoteAuthority) => { - const backend = await terminalInstanceService.getBackend(remoteAuthority); - if (!remoteAuthority || !backend) { - return env; - } - return backend.getShellEnvironment(); } }, configurationService, diff --git a/src/vs/workbench/contrib/terminal/electron-sandbox/terminalProfileResolverService.ts b/src/vs/workbench/contrib/terminal/electron-sandbox/terminalProfileResolverService.ts index e7890897bc7..602b47386ba 100644 --- a/src/vs/workbench/contrib/terminal/electron-sandbox/terminalProfileResolverService.ts +++ b/src/vs/workbench/contrib/terminal/electron-sandbox/terminalProfileResolverService.ts @@ -41,13 +41,6 @@ export class ElectronTerminalProfileResolverService extends BaseTerminalProfileR throw new ErrorNoTelemetry(`Cannot get environment when there is no backend for remote authority '${remoteAuthority}'`); } return backend.getEnvironment(); - }, - getShellEnvironment: async (remoteAuthority) => { - const backend = await terminalInstanceService.getBackend(remoteAuthority); - if (!backend) { - throw new ErrorNoTelemetry(`Cannot get environment when there is no backend for remote authority '${remoteAuthority}'`); - } - return backend.getShellEnvironment(); } }, configurationService, From e6d16873c644735b8c6b04c7bec0344536fc37bb Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 3 Jul 2023 11:31:49 -0700 Subject: [PATCH 124/926] Remove unwanted direct connect call --- .../contrib/terminal/electron-sandbox/localTerminalBackend.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts b/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts index 9fe0fd5aa21..b3c1e00e59c 100644 --- a/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts +++ b/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts @@ -277,7 +277,6 @@ class LocalTerminalBackend extends BaseTerminalBackend implements ITerminalBacke } async setTerminalLayoutInfo(layoutInfo?: ITerminalsLayoutInfoById): Promise { - await this._connectToDirectProxy(); const args: ISetTerminalLayoutInfoArgs = { workspaceId: this._getWorkspaceId(), tabs: layoutInfo ? layoutInfo.tabs : [] From a0dc16744f42aa661473f180040b9d42aafd81ca Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 3 Jul 2023 12:30:14 -0700 Subject: [PATCH 125/926] Update src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts Co-authored-by: Megan Rogge --- .../contrib/terminal/electron-sandbox/localTerminalBackend.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts b/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts index b3c1e00e59c..7eda78aee63 100644 --- a/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts +++ b/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts @@ -56,7 +56,7 @@ class LocalTerminalBackend extends BaseTerminalBackend implements ITerminalBacke private _directProxyClientEventually: DeferredPromise | undefined; private _directProxy: IPtyService | undefined; /** - * Communicate to the direct proxy (renderer<->ptyhost) is it's available, otherwise use the + * Communicate to the direct proxy (renderer<->ptyhost) if it's available, otherwise use the * indirect proxy (renderer<->main<->ptyhost). The latter may not need to actually launch the * pty host, for example when detecting profiles. */ From 656782c7dc9ad9e2c94dacb7da9688d712062a77 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 3 Jul 2023 12:45:19 -0700 Subject: [PATCH 126/926] Prevent the same terminal being reconnected to twice Part of #186922 Part of #133542 --- src/vs/platform/terminal/node/ptyService.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/vs/platform/terminal/node/ptyService.ts b/src/vs/platform/terminal/node/ptyService.ts index ae9a5c7a7d3..a046543bad8 100644 --- a/src/vs/platform/terminal/node/ptyService.ts +++ b/src/vs/platform/terminal/node/ptyService.ts @@ -518,7 +518,8 @@ export class PtyService extends Disposable implements IPtyService { performance.mark('code/willGetTerminalLayoutInfo'); const layout = this._workspaceLayoutInfos.get(args.workspaceId); if (layout) { - const expandedTabs = await Promise.all(layout.tabs.map(async tab => this._expandTerminalTab(tab))); + const doneSet: Set = new Set(); + const expandedTabs = await Promise.all(layout.tabs.map(async tab => this._expandTerminalTab(tab, doneSet))); const tabs = expandedTabs.filter(t => t.terminals.length > 0); performance.mark('code/didGetTerminalLayoutInfo'); return { tabs }; @@ -527,8 +528,8 @@ export class PtyService extends Disposable implements IPtyService { return undefined; } - private async _expandTerminalTab(tab: ITerminalTabLayoutInfoById): Promise { - const expandedTerminals = (await Promise.all(tab.terminals.map(t => this._expandTerminalInstance(t)))); + private async _expandTerminalTab(tab: ITerminalTabLayoutInfoById, doneSet: Set): Promise { + const expandedTerminals = (await Promise.all(tab.terminals.map(t => this._expandTerminalInstance(t, doneSet)))); const filtered = expandedTerminals.filter(term => term.terminal !== null) as IRawTerminalInstanceLayoutInfo[]; return { isActive: tab.isActive, @@ -537,12 +538,16 @@ export class PtyService extends Disposable implements IPtyService { }; } - private async _expandTerminalInstance(t: ITerminalInstanceLayoutInfoById): Promise> { + private async _expandTerminalInstance(t: ITerminalInstanceLayoutInfoById, doneSet: Set): Promise> { try { const revivedPtyId = this._revivedPtyIdMap.get(t.terminal)?.newId; this._logService.info(`Expanding terminal instance, old id ${t.terminal} -> new id ${revivedPtyId}`); this._revivedPtyIdMap.delete(t.terminal); const persistentProcessId = revivedPtyId ?? t.terminal; + if (doneSet.has(persistentProcessId)) { + throw new Error(`Terminal ${persistentProcessId} has already been expanded`); + } + doneSet.add(persistentProcessId); const persistentProcess = this._throwIfNoPty(persistentProcessId); const processDetails = persistentProcess && await this._buildProcessDetails(t.terminal, persistentProcess, revivedPtyId !== undefined); return { From eb62586e8688361cd689ada532a00da2ebd142ab Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 3 Jul 2023 13:32:27 -0700 Subject: [PATCH 127/926] fix #186940 --- src/vs/platform/terminal/node/terminalProfiles.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/terminal/node/terminalProfiles.ts b/src/vs/platform/terminal/node/terminalProfiles.ts index 5cf990c401e..7be7e1c3663 100644 --- a/src/vs/platform/terminal/node/terminalProfiles.ts +++ b/src/vs/platform/terminal/node/terminalProfiles.ts @@ -140,7 +140,7 @@ async function detectAvailableWindowsProfiles( const resultProfiles: ITerminalProfile[] = await transformToTerminalProfiles(detectedProfiles.entries(), defaultProfileName, fsProvider, shellEnv, logService, variableResolver); - if (includeDetectedProfiles || useWslProfiles) { + if (includeDetectedProfiles && useWslProfiles) { try { const result = await getWslProfiles(`${system32Path}\\${useWSLexe ? 'wsl' : 'bash'}.exe`, defaultProfileName); for (const wslProfile of result) { From c70a6b02f10cb78b2f40cb1bbac3d245cfd6f84c Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 3 Jul 2023 13:51:44 -0700 Subject: [PATCH 128/926] fix #173384 --- .../workbench/contrib/tasks/browser/abstractTaskService.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index 9fc349342f5..11922589b08 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -2200,7 +2200,9 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return new Map(); } await this._waitForSupportedExecutions; - if (this._workspaceTasksPromise) { + // The build task might be run before folder open. On folder open, we need to update the tasks so that + // all tasks are parsed. #173384 + if (runSource !== TaskRunSource.FolderOpen && this._workspaceTasksPromise) { return this._workspaceTasksPromise; } return this._updateWorkspaceTasks(runSource); @@ -2245,6 +2247,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer if (userTasks) { result.set(USER_TASKS_GROUP_KEY, userTasks); } + console.log('result', result.entries()); return result; } From d08f4eaef9f457e1793761091296a82c689668a8 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Mon, 3 Jul 2023 13:54:28 -0700 Subject: [PATCH 129/926] Update src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts --- src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index 11922589b08..1f1780ebdac 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -2247,7 +2247,6 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer if (userTasks) { result.set(USER_TASKS_GROUP_KEY, userTasks); } - console.log('result', result.entries()); return result; } From e963d92b5828356700dc8b542c887dc7834aa0dd Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 3 Jul 2023 13:55:10 -0700 Subject: [PATCH 130/926] Use workspace ID in revive pty id map Fixes #133542 --- src/vs/platform/terminal/common/terminal.ts | 4 +- .../platform/terminal/node/ptyHostService.ts | 8 ++-- src/vs/platform/terminal/node/ptyService.ts | 38 +++++++++++-------- .../electron-sandbox/localTerminalBackend.ts | 9 ++--- 4 files changed, 32 insertions(+), 27 deletions(-) diff --git a/src/vs/platform/terminal/common/terminal.ts b/src/vs/platform/terminal/common/terminal.ts index 07efa691afd..890998623de 100644 --- a/src/vs/platform/terminal/common/terminal.ts +++ b/src/vs/platform/terminal/common/terminal.ts @@ -337,7 +337,7 @@ export interface IPtyService extends IPtyHostController { getProfiles?(workspaceId: string, profiles: unknown, defaultProfile: unknown, includeDetectedProfiles?: boolean): Promise; getEnvironment(): Promise; getWslPath(original: string, direction: 'unix-to-win' | 'win-to-unix'): Promise; - getRevivedPtyNewId(id: number): Promise; + getRevivedPtyNewId(workspaceId: string, id: number): Promise; setTerminalLayoutInfo(args: ISetTerminalLayoutInfoArgs): Promise; getTerminalLayoutInfo(args: IGetTerminalLayoutInfoArgs): Promise; reduceConnectionGraceTime(): Promise; @@ -353,7 +353,7 @@ export interface IPtyService extends IPtyHostController { * Revives a workspaces terminal processes, these can then be reconnected to using the normal * flow for restoring terminals after reloading. */ - reviveTerminalProcesses(state: ISerializedTerminalState[], dateTimeFormatLocate: string): Promise; + reviveTerminalProcesses(workspaceId: string, state: ISerializedTerminalState[], dateTimeFormatLocate: string): Promise; refreshProperty(id: number, property: T): Promise; updateProperty(id: number, property: T, value: IProcessPropertyMap[T]): Promise; diff --git a/src/vs/platform/terminal/node/ptyHostService.ts b/src/vs/platform/terminal/node/ptyHostService.ts index efe79cfb211..f250294c592 100644 --- a/src/vs/platform/terminal/node/ptyHostService.ts +++ b/src/vs/platform/terminal/node/ptyHostService.ts @@ -298,8 +298,8 @@ export class PtyHostService extends Disposable implements IPtyService { return this._proxy.getWslPath(original, direction); } - getRevivedPtyNewId(id: number): Promise { - return this._proxy.getRevivedPtyNewId(id); + getRevivedPtyNewId(workspaceId: string, id: number): Promise { + return this._proxy.getRevivedPtyNewId(workspaceId, id); } setTerminalLayoutInfo(args: ISetTerminalLayoutInfoArgs): Promise { @@ -328,8 +328,8 @@ export class PtyHostService extends Disposable implements IPtyService { return this._proxy.serializeTerminalState(ids); } - async reviveTerminalProcesses(state: ISerializedTerminalState[], dateTimeFormatLocate: string) { - return this._proxy.reviveTerminalProcesses(state, dateTimeFormatLocate); + async reviveTerminalProcesses(workspaceId: string, state: ISerializedTerminalState[], dateTimeFormatLocate: string) { + return this._proxy.reviveTerminalProcesses(workspaceId, state, dateTimeFormatLocate); } async refreshProperty(id: number, property: T): Promise { diff --git a/src/vs/platform/terminal/node/ptyService.ts b/src/vs/platform/terminal/node/ptyService.ts index ae9a5c7a7d3..ec71ad484b4 100644 --- a/src/vs/platform/terminal/node/ptyService.ts +++ b/src/vs/platform/terminal/node/ptyService.ts @@ -71,7 +71,7 @@ export class PtyService extends Disposable implements IPtyService { private readonly _ptys: Map = new Map(); private readonly _workspaceLayoutInfos = new Map(); private readonly _detachInstanceRequestStore: RequestStore; - private readonly _revivedPtyIdMap: Map = new Map(); + private readonly _revivedPtyIdMap: Map = new Map(); private readonly _autoReplies: Map = new Map(); private _lastPtyId: number = 0; @@ -210,15 +210,15 @@ export class PtyService extends Disposable implements IPtyService { } @traceRpc - async reviveTerminalProcesses(state: ISerializedTerminalState[], dateTimeFormatLocale: string) { + async reviveTerminalProcesses(workspaceId: string, state: ISerializedTerminalState[], dateTimeFormatLocale: string) { const promises: Promise[] = []; for (const terminal of state) { - promises.push(this._reviveTerminalProcess(terminal)); + promises.push(this._reviveTerminalProcess(workspaceId, terminal)); } await Promise.all(promises); } - private async _reviveTerminalProcess(terminal: ISerializedTerminalState): Promise { + private async _reviveTerminalProcess(workspaceId: string, terminal: ISerializedTerminalState): Promise { const restoreMessage = localize('terminal-history-restored', "History restored"); // TODO: We may at some point want to show date information in a hover via a custom sequence: // new Date(terminal.timestamp).toLocaleDateString(dateTimeFormatLocale) @@ -246,8 +246,9 @@ export class PtyService extends Disposable implements IPtyService { terminal.replayEvent.events[0].data ); // Don't start the process here as there's no terminal to answer CPR - this._revivedPtyIdMap.set(terminal.id, { newId, state: terminal }); - this._logService.info(`Revived process, old id ${terminal.id} -> new id ${newId}`); + const oldId = this._getRevivingProcessId(workspaceId, terminal.id); + this._revivedPtyIdMap.set(oldId, { newId, state: terminal }); + this._logService.info(`Revived process, old id ${oldId} -> new id ${newId}`); } @traceRpc @@ -499,11 +500,11 @@ export class PtyService extends Disposable implements IPtyService { } @traceRpc - async getRevivedPtyNewId(id: number): Promise { + async getRevivedPtyNewId(workspaceId: string, id: number): Promise { try { - return this._revivedPtyIdMap.get(id)?.newId; + return this._revivedPtyIdMap.get(this._getRevivingProcessId(workspaceId, id))?.newId; } catch (e) { - this._logService.warn(`Couldn't find terminal ID ${id}`, e.message); + this._logService.warn(`Couldn't find terminal ID ${workspaceId}-${id}`, e.message); } return undefined; } @@ -518,7 +519,7 @@ export class PtyService extends Disposable implements IPtyService { performance.mark('code/willGetTerminalLayoutInfo'); const layout = this._workspaceLayoutInfos.get(args.workspaceId); if (layout) { - const expandedTabs = await Promise.all(layout.tabs.map(async tab => this._expandTerminalTab(tab))); + const expandedTabs = await Promise.all(layout.tabs.map(async tab => this._expandTerminalTab(args.workspaceId, tab))); const tabs = expandedTabs.filter(t => t.terminals.length > 0); performance.mark('code/didGetTerminalLayoutInfo'); return { tabs }; @@ -527,8 +528,8 @@ export class PtyService extends Disposable implements IPtyService { return undefined; } - private async _expandTerminalTab(tab: ITerminalTabLayoutInfoById): Promise { - const expandedTerminals = (await Promise.all(tab.terminals.map(t => this._expandTerminalInstance(t)))); + private async _expandTerminalTab(workspaceId: string, tab: ITerminalTabLayoutInfoById): Promise { + const expandedTerminals = (await Promise.all(tab.terminals.map(t => this._expandTerminalInstance(workspaceId, t)))); const filtered = expandedTerminals.filter(term => term.terminal !== null) as IRawTerminalInstanceLayoutInfo[]; return { isActive: tab.isActive, @@ -537,11 +538,12 @@ export class PtyService extends Disposable implements IPtyService { }; } - private async _expandTerminalInstance(t: ITerminalInstanceLayoutInfoById): Promise> { + private async _expandTerminalInstance(workspaceId: string, t: ITerminalInstanceLayoutInfoById): Promise> { try { - const revivedPtyId = this._revivedPtyIdMap.get(t.terminal)?.newId; - this._logService.info(`Expanding terminal instance, old id ${t.terminal} -> new id ${revivedPtyId}`); - this._revivedPtyIdMap.delete(t.terminal); + const oldId = this._getRevivingProcessId(workspaceId, t.terminal); + const revivedPtyId = this._revivedPtyIdMap.get(oldId)?.newId; + this._logService.info(`Expanding terminal instance, old id ${oldId} -> new id ${revivedPtyId}`); + this._revivedPtyIdMap.delete(oldId); const persistentProcessId = revivedPtyId ?? t.terminal; const persistentProcess = this._throwIfNoPty(persistentProcessId); const processDetails = persistentProcess && await this._buildProcessDetails(t.terminal, persistentProcess, revivedPtyId !== undefined); @@ -561,6 +563,10 @@ export class PtyService extends Disposable implements IPtyService { } } + private _getRevivingProcessId(workspaceId: string, ptyId: number): string { + return `${workspaceId}-${ptyId}`; + } + private async _buildProcessDetails(id: number, persistentProcess: PersistentTerminalProcess, wasRevived: boolean = false): Promise { performance.mark(`code/willBuildProcessDetails/${id}`); // If the process was just revived, don't do the orphan check as it will diff --git a/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts b/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts index 7eda78aee63..ac4c6650e8b 100644 --- a/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts +++ b/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts @@ -232,7 +232,7 @@ class LocalTerminalBackend extends BaseTerminalBackend implements ITerminalBacke async attachToRevivedProcess(id: number): Promise { await this._connectToDirectProxy(); try { - const newId = await this._proxy.getRevivedPtyNewId(id) ?? id; + const newId = await this._proxy.getRevivedPtyNewId(this._getWorkspaceId(), id) ?? id; return await this.attachToProcess(newId); } catch (e) { this._logService.warn(`Couldn't attach to process ${e.message}`); @@ -288,9 +288,8 @@ class LocalTerminalBackend extends BaseTerminalBackend implements ITerminalBacke } async getTerminalLayoutInfo(): Promise { - const layoutArgs: IGetTerminalLayoutInfoArgs = { - workspaceId: this._getWorkspaceId() - }; + const workspaceId = this._getWorkspaceId(); + const layoutArgs: IGetTerminalLayoutInfoArgs = { workspaceId }; // Revive processes if needed const serializedState = this._storageService.get(TerminalStorageKeys.TerminalBufferState, StorageScope.WORKSPACE); @@ -317,7 +316,7 @@ class LocalTerminalBackend extends BaseTerminalBackend implements ITerminalBacke mark('code/terminal/didGetReviveEnvironments'); mark('code/terminal/willReviveTerminalProcesses'); - await this._proxy.reviveTerminalProcesses(parsed, Intl.DateTimeFormat().resolvedOptions().locale); + await this._proxy.reviveTerminalProcesses(workspaceId, parsed, Intl.DateTimeFormat().resolvedOptions().locale); mark('code/terminal/didReviveTerminalProcesses'); this._storageService.remove(TerminalStorageKeys.TerminalBufferState, StorageScope.WORKSPACE); // If reviving processes, send the terminal layout info back to the pty host as it From a20dfe0552b58dbe7c31815c49b9ab180561c2c5 Mon Sep 17 00:00:00 2001 From: Raymond Zhao <7199958+rzhao271@users.noreply.github.com> Date: Mon, 3 Jul 2023 13:58:55 -0700 Subject: [PATCH 131/926] Add CodeQL config file (#186936) * Add CodeQL config file * Remove test.js line --- CodeQL.yml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 CodeQL.yml diff --git a/CodeQL.yml b/CodeQL.yml new file mode 100644 index 00000000000..9ef07560af3 --- /dev/null +++ b/CodeQL.yml @@ -0,0 +1,23 @@ +path_classifiers: + test: + # Classify all files in the top-level directories test/ and testsuites/ as test code. + - test + # Classify all files with suffix `.test` as test code. + # Note: use only forward slash / as a path separator. + # * Matches any sequence of characters except a forward slash. + # ** Matches any sequence of characters, including a forward slash. + # This wildcard must either be surrounded by forward slash symbols, or used as the first segment of a path. + # It matches zero or more whole directory segments. There is no need to use a wildcard at the end of a directory path because all sub-directories are automatically matched. + # That is, /anything/ matches the anything directory and all its subdirectories. + # Always enclose the expression in double quotes if it includes *. + - "**/*.test.ts" + + # The default behavior is to tag all files created during the + # build as `generated`. Results are hidden for generated code. You can tag + # further files as being generated by adding them to the `generated` section. + generated: + # generated code. + - out + - "out-build" + - "out-vscode" + - "**/out/**" From 013132a5cde7733e8fc59f633c03018ca08db9a1 Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Mon, 3 Jul 2023 15:06:42 -0700 Subject: [PATCH 132/926] Re-work auth flows into separate file (#186939) The flows are crucial to the extension and I wanted to be able to declare which can be used when in a declarative way. This reworks the flows into that model. Additionally, it pulls in the Client ID and secret from a config which will allow us to not rely on the vscode.dev proxy on Desktop (because we can make a distro change that includes the secret... which isn't a real secret, says GitHub)... unfortunately we still need to rely on it for Web due to CORS but we're in a position where it will be easy to rip the proxy out when GH supports it. --- .../github-authentication/src/common/env.ts | 6 +- .../src/common/errors.ts | 10 + .../github-authentication/src/config.ts | 15 + extensions/github-authentication/src/flows.ts | 499 ++++++++++++++++++ .../github-authentication/src/github.ts | 57 +- .../github-authentication/src/githubServer.ts | 399 +------------- 6 files changed, 610 insertions(+), 376 deletions(-) create mode 100644 extensions/github-authentication/src/common/errors.ts create mode 100644 extensions/github-authentication/src/config.ts create mode 100644 extensions/github-authentication/src/flows.ts diff --git a/extensions/github-authentication/src/common/env.ts b/extensions/github-authentication/src/common/env.ts index 7b99a148373..ebc474936aa 100644 --- a/extensions/github-authentication/src/common/env.ts +++ b/extensions/github-authentication/src/common/env.ts @@ -29,6 +29,10 @@ export function isSupportedClient(uri: Uri): boolean { export function isSupportedTarget(type: AuthProviderType, gheUri?: Uri): boolean { return ( type === AuthProviderType.github || - /\.ghe\.com$/.test(gheUri!.authority) + isHostedGitHubEnterprise(gheUri!) ); } + +export function isHostedGitHubEnterprise(uri: Uri): boolean { + return /\.ghe\.com$/.test(uri.authority); +} diff --git a/extensions/github-authentication/src/common/errors.ts b/extensions/github-authentication/src/common/errors.ts new file mode 100644 index 00000000000..3ba3dfc006a --- /dev/null +++ b/extensions/github-authentication/src/common/errors.ts @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export const TIMED_OUT_ERROR = 'Timed out'; + +// These error messages are internal and should not be shown to the user in any way. +export const USER_CANCELLATION_ERROR = 'User Cancelled'; +export const NETWORK_ERROR = 'network error'; diff --git a/extensions/github-authentication/src/config.ts b/extensions/github-authentication/src/config.ts new file mode 100644 index 00000000000..30b9dd66265 --- /dev/null +++ b/extensions/github-authentication/src/config.ts @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export interface IConfig { + // The client ID of the GitHub OAuth app + gitHubClientId: string; + gitHubClientSecret?: string; +} + +// For easy access to mixin client ID and secret +export const Config: IConfig = { + gitHubClientId: '01ab8ac9400c4e429b23' +}; diff --git a/extensions/github-authentication/src/flows.ts b/extensions/github-authentication/src/flows.ts new file mode 100644 index 00000000000..f3f9277bdc1 --- /dev/null +++ b/extensions/github-authentication/src/flows.ts @@ -0,0 +1,499 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as path from 'path'; +import { ProgressLocation, Uri, commands, env, l10n, window } from 'vscode'; +import { Log } from './common/logger'; +import { Config } from './config'; +import { UriEventHandler } from './github'; +import { fetching } from './node/fetch'; +import { LoopbackAuthServer } from './node/authServer'; +import { promiseFromEvent } from './common/utils'; +import { isHostedGitHubEnterprise } from './common/env'; +import { NETWORK_ERROR, TIMED_OUT_ERROR, USER_CANCELLATION_ERROR } from './common/errors'; + +interface IGitHubDeviceCodeResponse { + device_code: string; + user_code: string; + verification_uri: string; + interval: number; +} + +interface IFlowOptions { + // GitHub.com + readonly supportsGitHubDotCom: boolean; + // A GitHub Enterprise Server that is hosted by an organization + readonly supportsGitHubEnterpriseServer: boolean; + // A GitHub Enterprise Server that is hosted by GitHub for an organization + readonly supportsHostedGitHubEnterprise: boolean; + + // Runtimes - there are constraints on which runtimes support which flows + readonly supportsWebWorkerExtensionHost: boolean; + readonly supportsRemoteExtensionHost: boolean; + + // Clients - see `isSupportedClient` in `common/env.ts` for what constitutes a supported client + readonly supportsSupportedClients: boolean; + readonly supportsUnsupportedClients: boolean; + + // Configurations - some flows require a client secret + readonly supportsNoClientSecret: boolean; +} + +export const enum GitHubTarget { + DotCom, + Enterprise, + HostedEnterprise +} + +export const enum ExtensionHost { + WebWorker, + Remote, + Local +} + +interface IFlowQuery { + target: GitHubTarget; + extensionHost: ExtensionHost; + isSupportedClient: boolean; +} + +interface IFlowTriggerOptions { + scopes: string; + baseUri: Uri; + logger: Log; + redirectUri: Uri; + nonce: string; + callbackUri: Uri; + uriHandler: UriEventHandler; + enterpriseUri?: Uri; +} + +interface IFlow { + label: string; + options: IFlowOptions; + trigger(options: IFlowTriggerOptions): Promise; +} + +async function exchangeCodeForToken( + logger: Log, + endpointUri: Uri, + redirectUri: Uri, + code: string, + enterpriseUri?: Uri +): Promise { + logger.info('Exchanging code for token...'); + + const clientSecret = Config.gitHubClientSecret; + if (!clientSecret) { + throw new Error('No client secret configured for GitHub authentication.'); + } + + const body = new URLSearchParams([ + ['code', code], + ['client_id', Config.gitHubClientId], + ['redirect_uri', redirectUri.toString(true)], + ['client_secret', clientSecret] + ]); + if (enterpriseUri) { + body.append('github_enterprise', enterpriseUri.toString(true)); + } + const result = await fetching(endpointUri.toString(true), { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/x-www-form-urlencoded', + 'Content-Length': body.toString() + + }, + body: body.toString() + }); + + if (result.ok) { + const json = await result.json(); + logger.info('Token exchange success!'); + return json.access_token; + } else { + const text = await result.text(); + const error = new Error(text); + error.name = 'GitHubTokenExchangeError'; + throw error; + } +} + +const allFlows: IFlow[] = [ + new class UrlHandlerFlow implements IFlow { + label = l10n.t('url handler'); + options: IFlowOptions = { + supportsGitHubDotCom: true, + // Supporting GHES would be challenging because different versions + // used a different client ID. We could try to detect the version + // and use the right one, but that's a lot of work when we have + // other flows that work well. + supportsGitHubEnterpriseServer: false, + supportsHostedGitHubEnterprise: true, + supportsRemoteExtensionHost: true, + supportsWebWorkerExtensionHost: true, + // exchanging a code for a token requires a client secret + supportsNoClientSecret: false, + supportsSupportedClients: true, + supportsUnsupportedClients: false + }; + + async trigger({ + scopes, + baseUri, + redirectUri, + logger, + nonce, + callbackUri, + uriHandler, + enterpriseUri + }: IFlowTriggerOptions): Promise { + logger.info(`Trying without local server... (${scopes})`); + return await window.withProgress({ + location: ProgressLocation.Notification, + title: l10n.t({ + message: 'Signing in to {0}...', + args: [baseUri.authority], + comment: ['The {0} will be a url, e.g. github.com'] + }), + cancellable: true + }, async (_, token) => { + const promise = uriHandler.waitForCode(logger, scopes, nonce, token); + + const searchParams = new URLSearchParams([ + ['client_id', Config.gitHubClientId], + ['redirect_uri', redirectUri.toString(true)], + ['scope', scopes], + ['state', encodeURIComponent(callbackUri.toString(true))] + ]); + + // The extra toString, parse is apparently needed for env.openExternal + // to open the correct URL. + const uri = Uri.parse(baseUri.with({ + path: '/login/oauth/authorize', + query: searchParams.toString() + }).toString(true)); + await env.openExternal(uri); + + const code = await promise; + + const proxyEndpoints: { [providerId: string]: string } | undefined = await commands.executeCommand('workbench.getCodeExchangeProxyEndpoints'); + const endpointUrl = proxyEndpoints?.github + ? Uri.parse(`${proxyEndpoints.github}login/oauth/access_token`) + : baseUri.with({ path: '/login/oauth/access_token' }); + + const accessToken = await exchangeCodeForToken(logger, endpointUrl, redirectUri, code, enterpriseUri); + return accessToken; + }); + } + }, + new class LocalServerFlow implements IFlow { + label = l10n.t('local server'); + options: IFlowOptions = { + supportsGitHubDotCom: true, + // Supporting GHES would be challenging because different versions + // used a different client ID. We could try to detect the version + // and use the right one, but that's a lot of work when we have + // other flows that work well. + supportsGitHubEnterpriseServer: false, + supportsHostedGitHubEnterprise: true, + supportsRemoteExtensionHost: true, + supportsWebWorkerExtensionHost: true, + // exchanging a code for a token requires a client secret + supportsNoClientSecret: false, + supportsSupportedClients: true, + supportsUnsupportedClients: true + }; + async trigger({ + scopes, + baseUri, + redirectUri, + logger, + enterpriseUri + }: IFlowTriggerOptions): Promise { + logger.info(`Trying with local server... (${scopes})`); + return await window.withProgress({ + location: ProgressLocation.Notification, + title: l10n.t({ + message: 'Signing in to {0}...', + args: [baseUri.authority], + comment: ['The {0} will be a url, e.g. github.com'] + }), + cancellable: true + }, async (_, token) => { + const searchParams = new URLSearchParams([ + ['client_id', Config.gitHubClientId], + ['redirect_uri', redirectUri.toString(true)], + ['scope', scopes], + ]); + + const loginUrl = baseUri.with({ + path: '/login/oauth/authorize', + query: searchParams.toString() + }); + const server = new LoopbackAuthServer(path.join(__dirname, '../media'), loginUrl.toString(true)); + const port = await server.start(); + + let codeToExchange; + try { + env.openExternal(Uri.parse(`http://127.0.0.1:${port}/signin?nonce=${encodeURIComponent(server.nonce)}`)); + const { code } = await Promise.race([ + server.waitForOAuthResponse(), + new Promise((_, reject) => setTimeout(() => reject(TIMED_OUT_ERROR), 300_000)), // 5min timeout + promiseFromEvent(token.onCancellationRequested, (_, __, reject) => { reject(USER_CANCELLATION_ERROR); }).promise + ]); + codeToExchange = code; + } finally { + setTimeout(() => { + void server.stop(); + }, 5000); + } + + const accessToken = await exchangeCodeForToken( + logger, + baseUri.with({ path: '/login/oauth/access_token' }), + redirectUri, + codeToExchange, + enterpriseUri); + return accessToken; + }); + } + }, + new class DeviceCodeFlow implements IFlow { + label = l10n.t('device code'); + options: IFlowOptions = { + supportsGitHubDotCom: true, + supportsGitHubEnterpriseServer: true, + supportsHostedGitHubEnterprise: true, + supportsRemoteExtensionHost: true, + // CORS prevents this from working in web workers + supportsWebWorkerExtensionHost: false, + supportsNoClientSecret: true, + supportsSupportedClients: true, + supportsUnsupportedClients: true + }; + async trigger({ scopes, baseUri, logger }: IFlowTriggerOptions) { + logger.info(`Trying device code flow... (${scopes})`); + + // Get initial device code + const uri = baseUri.with({ + path: '/login/device/code', + query: `client_id=${Config.gitHubClientId}&scope=${scopes}` + }); + const result = await fetching(uri.toString(true), { + method: 'POST', + headers: { + Accept: 'application/json' + } + }); + if (!result.ok) { + throw new Error(`Failed to get one-time code: ${await result.text()}`); + } + + const json = await result.json() as IGitHubDeviceCodeResponse; + + const button = l10n.t('Copy & Continue to GitHub'); + const modalResult = await window.showInformationMessage( + l10n.t({ message: 'Your Code: {0}', args: [json.user_code], comment: ['The {0} will be a code, e.g. 123-456'] }), + { + modal: true, + detail: l10n.t('To finish authenticating, navigate to GitHub and paste in the above one-time code.') + }, button); + + if (modalResult !== button) { + throw new Error(USER_CANCELLATION_ERROR); + } + + await env.clipboard.writeText(json.user_code); + + const uriToOpen = await env.asExternalUri(Uri.parse(json.verification_uri)); + await env.openExternal(uriToOpen); + + return await this.waitForDeviceCodeAccessToken(baseUri, json); + } + + private async waitForDeviceCodeAccessToken( + baseUri: Uri, + json: IGitHubDeviceCodeResponse, + ): Promise { + return await window.withProgress({ + location: ProgressLocation.Notification, + cancellable: true, + title: l10n.t({ + message: 'Open [{0}]({0}) in a new tab and paste your one-time code: {1}', + args: [json.verification_uri, json.user_code], + comment: [ + 'The [{0}]({0}) will be a url and the {1} will be a code, e.g. 123-456', + '{Locked="[{0}]({0})"}' + ] + }) + }, async (_, token) => { + const refreshTokenUri = baseUri.with({ + path: '/login/oauth/access_token', + query: `client_id=${Config.gitHubClientId}&device_code=${json.device_code}&grant_type=urn:ietf:params:oauth:grant-type:device_code` + }); + + // Try for 2 minutes + const attempts = 120 / json.interval; + for (let i = 0; i < attempts; i++) { + await new Promise(resolve => setTimeout(resolve, json.interval * 1000)); + if (token.isCancellationRequested) { + throw new Error(USER_CANCELLATION_ERROR); + } + let accessTokenResult; + try { + accessTokenResult = await fetching(refreshTokenUri.toString(true), { + method: 'POST', + headers: { + Accept: 'application/json' + } + }); + } catch { + continue; + } + + if (!accessTokenResult.ok) { + continue; + } + + const accessTokenJson = await accessTokenResult.json(); + + if (accessTokenJson.error === 'authorization_pending') { + continue; + } + + if (accessTokenJson.error) { + throw new Error(accessTokenJson.error_description); + } + + return accessTokenJson.access_token; + } + + throw new Error(TIMED_OUT_ERROR); + }); + } + }, + new class PatFlow implements IFlow { + label = l10n.t('personal access token'); + options: IFlowOptions = { + supportsGitHubDotCom: true, + supportsGitHubEnterpriseServer: true, + supportsHostedGitHubEnterprise: true, + supportsRemoteExtensionHost: true, + supportsWebWorkerExtensionHost: true, + supportsNoClientSecret: true, + // PATs can't be used with Settings Sync so we don't enable this flow + // for supported clients + supportsSupportedClients: false, + supportsUnsupportedClients: true + }; + + async trigger({ scopes, baseUri, logger, enterpriseUri }: IFlowTriggerOptions) { + logger.info(`Trying to retrieve PAT... (${scopes})`); + + const button = l10n.t('Continue to GitHub'); + const modalResult = await window.showInformationMessage( + l10n.t('Continue to GitHub to create a Personal Access Token (PAT)'), + { + modal: true, + detail: l10n.t('To finish authenticating, navigate to GitHub to create a PAT then paste the PAT into the input box.') + }, button); + + if (modalResult !== button) { + throw new Error(USER_CANCELLATION_ERROR); + } + + const description = `${env.appName} (${scopes})`; + const uriToOpen = await env.asExternalUri(baseUri.with({ path: '/settings/tokens/new', query: `description=${description}&scopes=${scopes.split(' ').join(',')}` })); + await env.openExternal(uriToOpen); + const token = await window.showInputBox({ placeHolder: `ghp_1a2b3c4...`, prompt: `GitHub Personal Access Token - ${scopes}`, ignoreFocusOut: true }); + if (!token) { throw new Error(USER_CANCELLATION_ERROR); } + + const appUri = !enterpriseUri || isHostedGitHubEnterprise(enterpriseUri) + ? Uri.parse(`${baseUri.scheme}://api.${baseUri.authority}`) + : Uri.parse(`${baseUri.scheme}://${baseUri.authority}/api/v3`); + + const tokenScopes = await this.getScopes(token, appUri, logger); // Example: ['repo', 'user'] + const scopesList = scopes.split(' '); // Example: 'read:user repo user:email' + if (!scopesList.every(scope => { + const included = tokenScopes.includes(scope); + if (included || !scope.includes(':')) { + return included; + } + + return scope.split(':').some(splitScopes => { + return tokenScopes.includes(splitScopes); + }); + })) { + throw new Error(`The provided token does not match the requested scopes: ${scopes}`); + } + + return token; + } + + private async getScopes(token: string, serverUri: Uri, logger: Log): Promise { + try { + logger.info('Getting token scopes...'); + const result = await fetching(serverUri.toString(), { + headers: { + Authorization: `token ${token}`, + 'User-Agent': `${env.appName} (${env.appHost})` + } + }); + + if (result.ok) { + const scopes = result.headers.get('X-OAuth-Scopes'); + return scopes ? scopes.split(',').map(scope => scope.trim()) : []; + } else { + logger.error(`Getting scopes failed: ${result.statusText}`); + throw new Error(result.statusText); + } + } catch (ex) { + logger.error(ex.message); + throw new Error(NETWORK_ERROR); + } + } + } +]; + +export function getFlows(query: IFlowQuery) { + return allFlows.filter(flow => { + let useFlow: boolean = true; + switch (query.target) { + case GitHubTarget.DotCom: + useFlow &&= flow.options.supportsGitHubDotCom; + break; + case GitHubTarget.Enterprise: + useFlow &&= flow.options.supportsGitHubEnterpriseServer; + break; + case GitHubTarget.HostedEnterprise: + useFlow &&= flow.options.supportsHostedGitHubEnterprise; + break; + } + + switch (query.extensionHost) { + case ExtensionHost.Remote: + useFlow &&= flow.options.supportsRemoteExtensionHost; + break; + case ExtensionHost.WebWorker: + useFlow &&= flow.options.supportsWebWorkerExtensionHost; + break; + } + + if (!Config.gitHubClientSecret) { + useFlow &&= flow.options.supportsNoClientSecret; + } + + if (query.isSupportedClient) { + // TODO: revisit how we support PAT in GHES but not DotCom... but this works for now since + // there isn't another flow that has supportsSupportedClients = false + useFlow &&= (flow.options.supportsSupportedClients || query.target !== GitHubTarget.DotCom); + } else { + useFlow &&= flow.options.supportsUnsupportedClients; + } + return useFlow; + }); +} diff --git a/extensions/github-authentication/src/github.ts b/extensions/github-authentication/src/github.ts index 0588065eacd..c710cbe4f2f 100644 --- a/extensions/github-authentication/src/github.ts +++ b/extensions/github-authentication/src/github.ts @@ -7,10 +7,11 @@ import * as vscode from 'vscode'; import TelemetryReporter from '@vscode/extension-telemetry'; import { Keychain } from './common/keychain'; import { GitHubServer, IGitHubServer } from './githubServer'; -import { arrayEquals } from './common/utils'; +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'; interface SessionData { id: string; @@ -29,9 +30,63 @@ export enum AuthProviderType { } export class UriEventHandler extends vscode.EventEmitter implements vscode.UriHandler { + private readonly _pendingNonces = new Map(); + private readonly _codeExchangePromises = new Map; cancel: vscode.EventEmitter }>(); + public handleUri(uri: vscode.Uri) { this.fire(uri); } + + public async waitForCode(logger: Log, scopes: string, nonce: string, token: vscode.CancellationToken) { + const existingNonces = this._pendingNonces.get(scopes) || []; + this._pendingNonces.set(scopes, [...existingNonces, nonce]); + + let codeExchangePromise = this._codeExchangePromises.get(scopes); + if (!codeExchangePromise) { + codeExchangePromise = promiseFromEvent(this.event, this.handleEvent(logger, scopes)); + this._codeExchangePromises.set(scopes, codeExchangePromise); + } + + try { + return await Promise.race([ + codeExchangePromise.promise, + new Promise((_, reject) => setTimeout(() => reject(TIMED_OUT_ERROR), 300_000)), // 5min timeout + promiseFromEvent(token.onCancellationRequested, (_, __, reject) => { reject(USER_CANCELLATION_ERROR); }).promise + ]); + } finally { + this._pendingNonces.delete(scopes); + codeExchangePromise?.cancel.fire(); + this._codeExchangePromises.delete(scopes); + } + } + + private handleEvent: (logger: Log, scopes: string) => PromiseAdapter = + (logger: Log, scopes) => (uri, resolve, reject) => { + const query = new URLSearchParams(uri.query); + const code = query.get('code'); + const nonce = query.get('nonce'); + if (!code) { + reject(new Error('No code')); + return; + } + if (!nonce) { + reject(new Error('No nonce')); + return; + } + + const acceptedNonces = this._pendingNonces.get(scopes) || []; + if (!acceptedNonces.includes(nonce)) { + // A common scenario of this happening is if you: + // 1. Trigger a sign in with one set of scopes + // 2. Before finishing 1, you trigger a sign in with a different set of scopes + // In this scenario we should just return and wait for the next UriHandler event + // to run as we are probably still waiting on the user to hit 'Continue' + logger.info('Nonce not found in accepted nonces. Skipping this execution...'); + return; + } + + resolve(code); + }; } export class GitHubAuthenticationProvider implements vscode.AuthenticationProvider, vscode.Disposable { diff --git a/extensions/github-authentication/src/githubServer.ts b/extensions/github-authentication/src/githubServer.ts index 98bb1dd822f..7ac5cd8c577 100644 --- a/extensions/github-authentication/src/githubServer.ts +++ b/extensions/github-authentication/src/githubServer.ts @@ -4,26 +4,18 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import * as path from 'path'; -import { PromiseAdapter, promiseFromEvent } from './common/utils'; import { ExperimentationTelemetry } from './common/experimentationService'; import { AuthProviderType, UriEventHandler } from './github'; import { Log } from './common/logger'; import { isSupportedClient, isSupportedTarget } from './common/env'; -import { LoopbackAuthServer } from './node/authServer'; import { crypto } from './node/crypto'; import { fetching } from './node/fetch'; - -const CLIENT_ID = '01ab8ac9400c4e429b23'; -const GITHUB_TOKEN_URL = 'https://vscode.dev/codeExchangeProxyEndpoints/github/login/oauth/access_token'; +import { ExtensionHost, GitHubTarget, getFlows } from './flows'; +import { NETWORK_ERROR, USER_CANCELLATION_ERROR } from './common/errors'; // This is the error message that we throw if the login was cancelled for any reason. Extensions // calling `getSession` can handle this error to know that the user cancelled the login. const CANCELLATION_ERROR = 'Cancelled'; -// These error messages are internal and should not be shown to the user in any way. -const TIMED_OUT_ERROR = 'Timed out'; -const USER_CANCELLATION_ERROR = 'User Cancelled'; -const NETWORK_ERROR = 'network error'; const REDIRECT_URL_STABLE = 'https://vscode.dev/redirect'; const REDIRECT_URL_INSIDERS = 'https://insiders.vscode.dev/redirect'; @@ -35,41 +27,10 @@ export interface IGitHubServer { friendlyName: string; } -interface IGitHubDeviceCodeResponse { - device_code: string; - user_code: string; - verification_uri: string; - interval: number; -} - -async function getScopes(token: string, serverUri: vscode.Uri, logger: Log): Promise { - try { - logger.info('Getting token scopes...'); - const result = await fetching(serverUri.toString(), { - headers: { - Authorization: `token ${token}`, - 'User-Agent': `${vscode.env.appName} (${vscode.env.appHost})` - } - }); - - if (result.ok) { - const scopes = result.headers.get('X-OAuth-Scopes'); - return scopes ? scopes.split(',').map(scope => scope.trim()) : []; - } else { - logger.error(`Getting scopes failed: ${result.statusText}`); - throw new Error(result.statusText); - } - } catch (ex) { - logger.error(ex.message); - throw new Error(NETWORK_ERROR); - } -} export class GitHubServer implements IGitHubServer { readonly friendlyName: string; - private readonly _pendingNonces = new Map(); - private readonly _codeExchangePromises = new Map; cancel: vscode.EventEmitter }>(); private readonly _type: AuthProviderType; private _redirectEndpoint: string | undefined; @@ -148,48 +109,33 @@ export class GitHubServer implements IGitHubServer { const supportedClient = isSupportedClient(callbackUri); const supportedTarget = isSupportedTarget(this._type, this._ghesUri); - if (supportedClient && supportedTarget) { - try { - return await this.doLoginWithoutLocalServer(scopes, nonce, callbackUri); - } catch (e) { - this._logger.error(e); - userCancelled = e.message ?? e === USER_CANCELLATION_ERROR; - } - } - // Starting a local server is only supported if: - // 1. We are in a UI extension because we need to open a port on the machine that has the browser - // 2. We are in a node runtime because we need to open a port on the machine - // 3. code exchange can only be done with a supported target - if ( - this._extensionKind === vscode.ExtensionKind.UI && - typeof navigator === 'undefined' && - supportedTarget - ) { - try { - await promptToContinue(vscode.l10n.t('local server')); - return await this.doLoginWithLocalServer(scopes); - } catch (e) { - userCancelled = this.processLoginError(e); - } - } + const flows = getFlows({ + target: this._type === AuthProviderType.github + ? GitHubTarget.DotCom + : supportedTarget ? GitHubTarget.HostedEnterprise : GitHubTarget.Enterprise, + extensionHost: typeof navigator === 'undefined' + ? this._extensionKind === vscode.ExtensionKind.UI ? ExtensionHost.Local : ExtensionHost.Remote + : ExtensionHost.WebWorker, + isSupportedClient: supportedClient + }); - // We only can use the Device Code flow when we have a full node environment because of CORS. - if (typeof navigator === 'undefined') { - try { - await promptToContinue(vscode.l10n.t('device code')); - return await this.doLoginDeviceCodeFlow(scopes); - } catch (e) { - userCancelled = this.processLoginError(e); - } - } - // In a supported environment, we can't use PAT auth because we use this auth for Settings Sync and it doesn't support PATs. - // With that said, GitHub Enterprise isn't used by Settings Sync so we can use PATs for that. - if (!supportedClient || this._type === AuthProviderType.githubEnterprise) { + for (const flow of flows) { try { - await promptToContinue(vscode.l10n.t('personal access token')); - return await this.doLoginWithPat(scopes); + if (flow !== flows[0]) { + await promptToContinue(flow.label); + } + return await flow.trigger({ + scopes, + callbackUri, + nonce, + baseUri: this.baseUri, + logger: this._logger, + uriHandler: this._uriHandler, + enterpriseUri: this._ghesUri, + redirectUri: vscode.Uri.parse(await this.getRedirectEndpoint()), + }); } catch (e) { userCancelled = this.processLoginError(e); } @@ -198,301 +144,6 @@ export class GitHubServer implements IGitHubServer { throw new Error(userCancelled ? CANCELLATION_ERROR : 'No auth flow succeeded.'); } - private async doLoginWithoutLocalServer(scopes: string, nonce: string, callbackUri: vscode.Uri): Promise { - this._logger.info(`Trying without local server... (${scopes})`); - return await vscode.window.withProgress({ - location: vscode.ProgressLocation.Notification, - title: vscode.l10n.t({ - message: 'Signing in to {0}...', - args: [this.baseUri.authority], - comment: ['The {0} will be a url, e.g. github.com'] - }), - cancellable: true - }, async (_, token) => { - const existingNonces = this._pendingNonces.get(scopes) || []; - this._pendingNonces.set(scopes, [...existingNonces, nonce]); - const redirectUri = await this.getRedirectEndpoint(); - const searchParams = new URLSearchParams([ - ['client_id', CLIENT_ID], - ['redirect_uri', redirectUri], - ['scope', scopes], - ['state', encodeURIComponent(callbackUri.toString(true))] - ]); - - const uri = vscode.Uri.parse(this.baseUri.with({ - path: '/login/oauth/authorize', - query: searchParams.toString() - }).toString(true)); - await vscode.env.openExternal(uri); - - // Register a single listener for the URI callback, in case the user starts the login process multiple times - // before completing it. - let codeExchangePromise = this._codeExchangePromises.get(scopes); - if (!codeExchangePromise) { - codeExchangePromise = promiseFromEvent(this._uriHandler!.event, this.handleUri(scopes)); - this._codeExchangePromises.set(scopes, codeExchangePromise); - } - - try { - return await Promise.race([ - codeExchangePromise.promise, - new Promise((_, reject) => setTimeout(() => reject(TIMED_OUT_ERROR), 300_000)), // 5min timeout - promiseFromEvent(token.onCancellationRequested, (_, __, reject) => { reject(USER_CANCELLATION_ERROR); }).promise - ]); - } finally { - this._pendingNonces.delete(scopes); - codeExchangePromise?.cancel.fire(); - this._codeExchangePromises.delete(scopes); - } - }); - } - - private async doLoginWithLocalServer(scopes: string): Promise { - this._logger.info(`Trying with local server... (${scopes})`); - return await vscode.window.withProgress({ - location: vscode.ProgressLocation.Notification, - title: vscode.l10n.t({ - message: 'Signing in to {0}...', - args: [this.baseUri.authority], - comment: ['The {0} will be a url, e.g. github.com'] - }), - cancellable: true - }, async (_, token) => { - const redirectUri = await this.getRedirectEndpoint(); - const searchParams = new URLSearchParams([ - ['client_id', CLIENT_ID], - ['redirect_uri', redirectUri], - ['scope', scopes], - ]); - - const loginUrl = this.baseUri.with({ - path: '/login/oauth/authorize', - query: searchParams.toString() - }); - const server = new LoopbackAuthServer(path.join(__dirname, '../media'), loginUrl.toString(true)); - const port = await server.start(); - - let codeToExchange; - try { - vscode.env.openExternal(vscode.Uri.parse(`http://127.0.0.1:${port}/signin?nonce=${encodeURIComponent(server.nonce)}`)); - const { code } = await Promise.race([ - server.waitForOAuthResponse(), - new Promise((_, reject) => setTimeout(() => reject(TIMED_OUT_ERROR), 300_000)), // 5min timeout - promiseFromEvent(token.onCancellationRequested, (_, __, reject) => { reject(USER_CANCELLATION_ERROR); }).promise - ]); - codeToExchange = code; - } finally { - setTimeout(() => { - void server.stop(); - }, 5000); - } - - const accessToken = await this.exchangeCodeForToken(codeToExchange); - return accessToken; - }); - } - - private async doLoginDeviceCodeFlow(scopes: string): Promise { - this._logger.info(`Trying device code flow... (${scopes})`); - - // Get initial device code - const uri = this.baseUri.with({ - path: '/login/device/code', - query: `client_id=${CLIENT_ID}&scope=${scopes}` - }); - const result = await fetching(uri.toString(true), { - method: 'POST', - headers: { - Accept: 'application/json' - } - }); - if (!result.ok) { - throw new Error(`Failed to get one-time code: ${await result.text()}`); - } - - const json = await result.json() as IGitHubDeviceCodeResponse; - - const button = vscode.l10n.t('Copy & Continue to GitHub'); - const modalResult = await vscode.window.showInformationMessage( - vscode.l10n.t({ message: 'Your Code: {0}', args: [json.user_code], comment: ['The {0} will be a code, e.g. 123-456'] }), - { - modal: true, - detail: vscode.l10n.t('To finish authenticating, navigate to GitHub and paste in the above one-time code.') - }, button); - - if (modalResult !== button) { - throw new Error(USER_CANCELLATION_ERROR); - } - - await vscode.env.clipboard.writeText(json.user_code); - - const uriToOpen = await vscode.env.asExternalUri(vscode.Uri.parse(json.verification_uri)); - await vscode.env.openExternal(uriToOpen); - - return await this.waitForDeviceCodeAccessToken(json); - } - - private async doLoginWithPat(scopes: string): Promise { - this._logger.info(`Trying to retrieve PAT... (${scopes})`); - - const button = vscode.l10n.t('Continue to GitHub'); - const modalResult = await vscode.window.showInformationMessage( - vscode.l10n.t('Continue to GitHub to create a Personal Access Token (PAT)'), - { - modal: true, - detail: vscode.l10n.t('To finish authenticating, navigate to GitHub to create a PAT then paste the PAT into the input box.') - }, button); - - if (modalResult !== button) { - throw new Error(USER_CANCELLATION_ERROR); - } - - const description = `${vscode.env.appName} (${scopes})`; - const uriToOpen = await vscode.env.asExternalUri(this.baseUri.with({ path: '/settings/tokens/new', query: `description=${description}&scopes=${scopes.split(' ').join(',')}` })); - await vscode.env.openExternal(uriToOpen); - const token = await vscode.window.showInputBox({ placeHolder: `ghp_1a2b3c4...`, prompt: `GitHub Personal Access Token - ${scopes}`, ignoreFocusOut: true }); - if (!token) { throw new Error(USER_CANCELLATION_ERROR); } - - const tokenScopes = await getScopes(token, this.getServerUri('/'), this._logger); // Example: ['repo', 'user'] - const scopesList = scopes.split(' '); // Example: 'read:user repo user:email' - if (!scopesList.every(scope => { - const included = tokenScopes.includes(scope); - if (included || !scope.includes(':')) { - return included; - } - - return scope.split(':').some(splitScopes => { - return tokenScopes.includes(splitScopes); - }); - })) { - throw new Error(`The provided token does not match the requested scopes: ${scopes}`); - } - - return token; - } - - private async waitForDeviceCodeAccessToken( - json: IGitHubDeviceCodeResponse, - ): Promise { - return await vscode.window.withProgress({ - location: vscode.ProgressLocation.Notification, - cancellable: true, - title: vscode.l10n.t({ - message: 'Open [{0}]({0}) in a new tab and paste your one-time code: {1}', - args: [json.verification_uri, json.user_code], - comment: [ - 'The [{0}]({0}) will be a url and the {1} will be a code, e.g. 123-456', - '{Locked="[{0}]({0})"}' - ] - }) - }, async (_, token) => { - const refreshTokenUri = this.baseUri.with({ - path: '/login/oauth/access_token', - query: `client_id=${CLIENT_ID}&device_code=${json.device_code}&grant_type=urn:ietf:params:oauth:grant-type:device_code` - }); - - // Try for 2 minutes - const attempts = 120 / json.interval; - for (let i = 0; i < attempts; i++) { - await new Promise(resolve => setTimeout(resolve, json.interval * 1000)); - if (token.isCancellationRequested) { - throw new Error(USER_CANCELLATION_ERROR); - } - let accessTokenResult; - try { - accessTokenResult = await fetching(refreshTokenUri.toString(true), { - method: 'POST', - headers: { - Accept: 'application/json' - } - }); - } catch { - continue; - } - - if (!accessTokenResult.ok) { - continue; - } - - const accessTokenJson = await accessTokenResult.json(); - - if (accessTokenJson.error === 'authorization_pending') { - continue; - } - - if (accessTokenJson.error) { - throw new Error(accessTokenJson.error_description); - } - - return accessTokenJson.access_token; - } - - throw new Error(TIMED_OUT_ERROR); - }); - } - - private handleUri: (scopes: string) => PromiseAdapter = - (scopes) => (uri, resolve, reject) => { - const query = new URLSearchParams(uri.query); - const code = query.get('code'); - const nonce = query.get('nonce'); - if (!code) { - reject(new Error('No code')); - return; - } - if (!nonce) { - reject(new Error('No nonce')); - return; - } - - const acceptedNonces = this._pendingNonces.get(scopes) || []; - if (!acceptedNonces.includes(nonce)) { - // A common scenario of this happening is if you: - // 1. Trigger a sign in with one set of scopes - // 2. Before finishing 1, you trigger a sign in with a different set of scopes - // In this scenario we should just return and wait for the next UriHandler event - // to run as we are probably still waiting on the user to hit 'Continue' - this._logger.info('Nonce not found in accepted nonces. Skipping this execution...'); - return; - } - - resolve(this.exchangeCodeForToken(code)); - }; - - private async exchangeCodeForToken(code: string): Promise { - this._logger.info('Exchanging code for token...'); - - const proxyEndpoints: { [providerId: string]: string } | undefined = await vscode.commands.executeCommand('workbench.getCodeExchangeProxyEndpoints'); - const endpointUrl = proxyEndpoints?.github ? `${proxyEndpoints.github}login/oauth/access_token` : GITHUB_TOKEN_URL; - - const body = new URLSearchParams([['code', code]]); - if (this._type === AuthProviderType.githubEnterprise) { - body.append('github_enterprise', this.baseUri.toString(true)); - body.append('redirect_uri', await this.getRedirectEndpoint()); - } - const result = await fetching(endpointUrl, { - method: 'POST', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/x-www-form-urlencoded', - 'Content-Length': body.toString() - - }, - body: body.toString() - }); - - if (result.ok) { - const json = await result.json(); - this._logger.info('Token exchange success!'); - return json.access_token; - } else { - const text = await result.text(); - const error = new Error(text); - error.name = 'GitHubTokenExchangeError'; - throw error; - } - } - private getServerUri(path: string = '') { const apiUri = this.baseUri; // github.com and Hosted GitHub Enterprise instances From 89229f752fc17e3a6bf4818e82aa604c18d1b104 Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Mon, 3 Jul 2023 15:42:03 -0700 Subject: [PATCH 133/926] bump distro (#186950) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c32e6ddea8d..2673e2279aa 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.81.0", - "distro": "4ec8f427013e8d904920b6bdd931e78344401c19", + "distro": "ce5df90ac966be777544d3f332705138a9e4d73e", "author": { "name": "Microsoft Corporation" }, From 300617543fb836ce5bcdee4850a1a93eb384fe19 Mon Sep 17 00:00:00 2001 From: Raymond Zhao <7199958+rzhao271@users.noreply.github.com> Date: Mon, 3 Jul 2023 19:48:04 -0700 Subject: [PATCH 134/926] chore: bump semver and openssl (#186949) --- cli/Cargo.lock | 9 ++++----- yarn.lock | 33 +++++++-------------------------- 2 files changed, 11 insertions(+), 31 deletions(-) diff --git a/cli/Cargo.lock b/cli/Cargo.lock index 67bdd9b6c07..bed1af5cdd7 100644 --- a/cli/Cargo.lock +++ b/cli/Cargo.lock @@ -1472,9 +1472,9 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.48" +version = "0.10.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "518915b97df115dd36109bfa429a48b8f737bd05508cf9588977b599648926d2" +checksum = "345df152bc43501c5eb9e4654ff05f794effb78d4efe3d53abc158baddc0703d" dependencies = [ "bitflags", "cfg-if", @@ -1504,11 +1504,10 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.83" +version = "0.9.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "666416d899cf077260dac8698d60a60b435a46d57e82acb1be3d0dad87284e5b" +checksum = "374533b0e45f3a7ced10fcaeccca020e66656bc03dac384f852e4e5a7a8104a6" dependencies = [ - "autocfg", "cc", "libc", "pkg-config", diff --git a/yarn.lock b/yarn.lock index 477bba56d38..689f60907af 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1079,9 +1079,9 @@ integrity sha512-41qEJgBH/TWgo5NFSvBCJ1qkoi3Q6ONSF2avrHq1LVEZfYpdHmj0y9SuTK+u9ZhG1sYQKBL1AWXKyLWP4RaUoQ== "@types/semver@^7.3.12": - version "7.3.13" - resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.13.tgz#da4bfd73f49bd541d28920ab0e2bf0ee80f71c91" - integrity sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw== + version "7.5.0" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.0.tgz#591c1ce3a702c45ee15f47a42ade72c2fd78978a" + integrity sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw== "@types/sinon-test@^2.4.2": version "2.4.2" @@ -8856,29 +8856,10 @@ semver@^6.2.0, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^7.3.2: - version "7.3.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" - integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== - -semver@^7.3.4, semver@^7.3.5: - version "7.3.5" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" - integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== - dependencies: - lru-cache "^6.0.0" - -semver@^7.3.7: - version "7.3.7" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" - integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== - dependencies: - lru-cache "^6.0.0" - -semver@^7.3.8: - version "7.3.8" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" - integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== +semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8: + version "7.5.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.3.tgz#161ce8c2c6b4b3bdca6caadc9fa3317a4c4fe88e" + integrity sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ== dependencies: lru-cache "^6.0.0" From 029fbbd4916e29dbc85a7f10e444e5c920c9f296 Mon Sep 17 00:00:00 2001 From: Johannes Date: Tue, 4 Jul 2023 10:47:36 +0200 Subject: [PATCH 135/926] bring over more "amdX" usages --- src/vs/amdX.ts | 3 +++ src/vs/base/test/common/filters.perf.test.ts | 13 ++++++++++--- .../assignment/common/assignmentService.ts | 3 ++- .../browser/textMateTokenizationFeatureImpl.ts | 5 +++-- .../browser/worker/textMateWorkerModel.ts | 13 ++++++++++--- .../textMateWorkerTokenizerController.ts | 18 ++++++++++++++---- .../test/node/encoding/encoding.test.ts | 8 +++++--- 7 files changed, 47 insertions(+), 16 deletions(-) diff --git a/src/vs/amdX.ts b/src/vs/amdX.ts index 56cebe68bfd..92fe85b7e09 100644 --- a/src/vs/amdX.ts +++ b/src/vs/amdX.ts @@ -166,6 +166,9 @@ if (typeof globalThis.require === 'object') { } /** + * Utility for importing an AMD node module. This util supports AMD and ESM contexts and should be used while the ESM adoption + * is on its way. + * * e.g. pass in `vscode-textmate/release/main.js` */ export async function importAMDNodeModule(nodeModuleName: string, pathInsideNodeModule: string, isBuilt?: boolean): Promise { diff --git a/src/vs/base/test/common/filters.perf.test.ts b/src/vs/base/test/common/filters.perf.test.ts index 8ac5a3c89d9..fd311ecd6f7 100644 --- a/src/vs/base/test/common/filters.perf.test.ts +++ b/src/vs/base/test/common/filters.perf.test.ts @@ -2,8 +2,9 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { importAMDNodeModule } from 'vs/amdX'; import * as filters from 'vs/base/common/filters'; -import { data } from 'vs/base/test/common/filters.perf.data'; +import { FileAccess } from 'vs/base/common/network'; const patterns = ['cci', 'ida', 'pos', 'CCI', 'enbled', 'callback', 'gGame', 'cons', 'zyx', 'aBc']; @@ -15,7 +16,10 @@ function perfSuite(name: string, callback: (this: Mocha.Suite) => void) { } } -perfSuite('Performance - fuzzyMatch', function () { +perfSuite('Performance - fuzzyMatch', async function () { + + const uri = FileAccess.asBrowserUri('vs/base/test/common/filters.perf.data').toString(true); + const { data } = await importAMDNodeModule(uri, ''); // suiteSetup(() => console.profile()); // suiteTeardown(() => console.profileEnd()); @@ -47,7 +51,10 @@ perfSuite('Performance - fuzzyMatch', function () { }); -perfSuite('Performance - IFilter', function () { +perfSuite('Performance - IFilter', async function () { + + const uri = FileAccess.asBrowserUri('vs/base/test/common/filters.perf.data').toString(true); + const { data } = await importAMDNodeModule(uri, ''); function perfTest(name: string, match: filters.IFilter) { test(name, () => { diff --git a/src/vs/platform/assignment/common/assignmentService.ts b/src/vs/platform/assignment/common/assignmentService.ts index 7c5287d6a32..67e34826627 100644 --- a/src/vs/platform/assignment/common/assignmentService.ts +++ b/src/vs/platform/assignment/common/assignmentService.ts @@ -9,6 +9,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { IProductService } from 'vs/platform/product/common/productService'; import { getTelemetryLevel } from 'vs/platform/telemetry/common/telemetryUtils'; import { AssignmentFilterProvider, ASSIGNMENT_REFETCH_INTERVAL, ASSIGNMENT_STORAGE_KEY, IAssignmentService, TargetPopulation } from 'vs/platform/assignment/common/assignment'; +import { importAMDNodeModule } from 'vs/amdX'; export abstract class BaseAssignmentService implements IAssignmentService { _serviceBrand: undefined; @@ -85,7 +86,7 @@ export abstract class BaseAssignmentService implements IAssignmentService { ); const tasConfig = this.productService.tasConfig!; - const tasClient = new (await import('tas-client-umd')).ExperimentationService({ + const tasClient = new (await importAMDNodeModule('tas-client-umd', 'lib/tas-client-umd.js')).ExperimentationService({ filterProviders: [filterProvider], telemetry: this.telemetry, storageKey: ASSIGNMENT_STORAGE_KEY, diff --git a/src/vs/workbench/services/textMate/browser/textMateTokenizationFeatureImpl.ts b/src/vs/workbench/services/textMate/browser/textMateTokenizationFeatureImpl.ts index ca939e5f2f2..b36b7e5910e 100644 --- a/src/vs/workbench/services/textMate/browser/textMateTokenizationFeatureImpl.ts +++ b/src/vs/workbench/services/textMate/browser/textMateTokenizationFeatureImpl.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 * as dom from 'vs/base/browser/dom'; import { equals as equalArray } from 'vs/base/common/arrays'; import { Color } from 'vs/base/common/color'; @@ -238,7 +239,7 @@ export class TextMateTokenizationFeature extends Disposable implements ITextMate return this._grammarFactory; } - const [vscodeTextmate, vscodeOniguruma] = await Promise.all([import('vscode-textmate'), this._getVSCodeOniguruma()]); + const [vscodeTextmate, vscodeOniguruma] = await Promise.all([importAMDNodeModule('vscode-textmate', 'release/main.js'), this._getVSCodeOniguruma()]); const onigLib: Promise = Promise.resolve({ createOnigScanner: (sources: string[]) => vscodeOniguruma.createOnigScanner(sources), createOnigString: (str: string) => vscodeOniguruma.createOnigString(str) @@ -348,7 +349,7 @@ export class TextMateTokenizationFeature extends Disposable implements ITextMate private _getVSCodeOniguruma(): Promise { if (!this._vscodeOniguruma) { this._vscodeOniguruma = (async () => { - const [vscodeOniguruma, wasm] = await Promise.all([import('vscode-oniguruma'), this._loadVSCodeOnigurumaWASM()]); + const [vscodeOniguruma, wasm] = await Promise.all([importAMDNodeModule('vscode-oniguruma', 'release/main.js'), this._loadVSCodeOnigurumaWASM()]); await vscodeOniguruma.loadWASM({ data: wasm, print: (str: string) => { diff --git a/src/vs/workbench/services/textMate/browser/worker/textMateWorkerModel.ts b/src/vs/workbench/services/textMate/browser/worker/textMateWorkerModel.ts index 7229aaa6280..cb2f98adff9 100644 --- a/src/vs/workbench/services/textMate/browser/worker/textMateWorkerModel.ts +++ b/src/vs/workbench/services/textMate/browser/worker/textMateWorkerModel.ts @@ -10,13 +10,14 @@ import { LineRange } from 'vs/editor/common/core/lineRange'; import { LanguageId } from 'vs/editor/common/encodedTokenAttributes'; import { IModelChangedEvent, MirrorTextModel } from 'vs/editor/common/model/mirrorTextModel'; import { TokenizerWithStateStore } from 'vs/editor/common/model/textModelTokens'; +import type { diffStateStacksRefEq, StateStack, StackDiff } from 'vscode-textmate'; import { ContiguousMultilineTokensBuilder } from 'vs/editor/common/tokens/contiguousMultilineTokensBuilder'; import { LineTokens } from 'vs/editor/common/tokens/lineTokens'; import { TextMateTokenizationSupport } from 'vs/workbench/services/textMate/browser/tokenizationSupport/textMateTokenizationSupport'; import { TokenizationSupportWithLineLimit } from 'vs/workbench/services/textMate/browser/tokenizationSupport/tokenizationSupportWithLineLimit'; import { StateDeltas } from 'vs/workbench/services/textMate/browser/workerHost/textMateWorkerHost'; -import { StackDiff, StateStack, diffStateStacksRefEq } from 'vscode-textmate'; import { TextMateTokenizationWorker } from './textMate.worker'; +import { importAMDNodeModule } from 'vs/amdX'; export class TextMateWorkerModel extends MirrorTextModel { private _tokenizationStateStore: TokenizerWithStateStore | null = null; @@ -25,6 +26,7 @@ export class TextMateWorkerModel extends MirrorTextModel { '_maxTokenizationLineLength', -1 ); + private _diffStateStacksRefEqFn?: typeof diffStateStacksRefEq; constructor( uri: URI, @@ -107,11 +109,16 @@ export class TextMateWorkerModel extends MirrorTextModel { }); } - private _tokenize(): void { + private async _tokenize(): Promise { if (this._isDisposed || !this._tokenizationStateStore) { return; } + if (!this._diffStateStacksRefEqFn) { + const { diffStateStacksRefEq } = await importAMDNodeModule('vscode-textmate', 'release/main.js'); + this._diffStateStacksRefEqFn = diffStateStacksRefEq; + } + const startTime = new Date().getTime(); while (true) { @@ -131,7 +138,7 @@ export class TextMateWorkerModel extends MirrorTextModel { const lineStartState = this._tokenizationStateStore.getStartState(lineNumberToTokenize)!; const r = this._tokenizationStateStore.tokenizationSupport.tokenizeEncoded(text, true, lineStartState); if (this._tokenizationStateStore.store.setEndState(lineNumberToTokenize, r.endState as StateStack)) { - const delta = diffStateStacksRefEq(lineStartState, r.endState as StateStack); + const delta = this._diffStateStacksRefEqFn(lineStartState, r.endState as StateStack); stateDeltaBuilder.setState(lineNumberToTokenize, delta); } else { stateDeltaBuilder.setState(lineNumberToTokenize, null); diff --git a/src/vs/workbench/services/textMate/browser/workerHost/textMateWorkerTokenizerController.ts b/src/vs/workbench/services/textMate/browser/workerHost/textMateWorkerTokenizerController.ts index ed1a2cabfa0..72fed613e41 100644 --- a/src/vs/workbench/services/textMate/browser/workerHost/textMateWorkerTokenizerController.ts +++ b/src/vs/workbench/services/textMate/browser/workerHost/textMateWorkerTokenizerController.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 { Disposable } from 'vs/base/common/lifecycle'; import { IObservable, autorun, keepAlive, observableFromEvent } from 'vs/base/common/observable'; import { countEOL } from 'vs/editor/common/core/eolCounter'; @@ -17,7 +18,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { ArrayEdit, MonotonousIndexTransformer, SingleArrayEdit } from 'vs/workbench/services/textMate/browser/arrayOperation'; import { TextMateTokenizationWorker } from 'vs/workbench/services/textMate/browser/worker/textMate.worker'; import type { StateDeltas } from 'vs/workbench/services/textMate/browser/workerHost/textMateWorkerHost'; -import { applyStateStackDiff, StateStack } from 'vscode-textmate'; +import type { applyStateStackDiff, StateStack } from 'vscode-textmate'; export class TextMateWorkerTokenizerController extends Disposable { private _pendingChanges: IModelContentChangedEvent[] = []; @@ -30,12 +31,14 @@ export class TextMateWorkerTokenizerController extends Disposable { private readonly _loggingEnabled = observableConfigValue('editor.experimental.asyncTokenizationLogging', false, this._configurationService); + private _applyStateStackDiffFn?: typeof applyStateStackDiff; + private _initialState?: StateStack; + constructor( private readonly _model: ITextModel, private readonly _worker: TextMateTokenizationWorker, private readonly _languageIdCodec: ILanguageIdCodec, private readonly _backgroundTokenizationStore: IBackgroundTokenizationStore, - private readonly _initialState: StateStack, private readonly _configurationService: IConfigurationService, private readonly _maxTokenizationLineLength: IObservable, ) { @@ -95,7 +98,7 @@ export class TextMateWorkerTokenizerController extends Disposable { /** * This method is called from the worker through the worker host. */ - public setTokensAndStates(versionId: number, rawTokens: ArrayBuffer, stateDeltas: StateDeltas[]): void { + public async setTokensAndStates(versionId: number, rawTokens: ArrayBuffer, stateDeltas: StateDeltas[]): Promise { // _states state, change{k}, ..., change{versionId}, state delta base & rawTokens, change{j}, ..., change{m}, current renderer state // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^ // | past changes | future states @@ -165,6 +168,13 @@ export class TextMateWorkerTokenizerController extends Disposable { this._pendingChanges.map((c) => fullLineArrayEditFromModelContentChange(c.changes)) ); + if (!this._applyStateStackDiffFn || !this._initialState) { + const { applyStateStackDiff, INITIAL } = await importAMDNodeModule('vscode-textmate', 'release/main.js'); + this._applyStateStackDiffFn = applyStateStackDiff; + this._initialState = INITIAL; + } + + // Apply state deltas to _states and _backgroundTokenizationStore for (const d of stateDeltas) { let prevState = d.startLineNumber <= 1 ? this._initialState : this._states.getEndState(d.startLineNumber - 1); @@ -172,7 +182,7 @@ export class TextMateWorkerTokenizerController extends Disposable { const delta = d.stateDeltas[i]; let state: StateStack; if (delta) { - state = applyStateStackDiff(prevState, delta)!; + state = this._applyStateStackDiffFn(prevState, delta)!; this._states.setEndState(d.startLineNumber + i, state); } else { state = this._states.getEndState(d.startLineNumber + i)!; diff --git a/src/vs/workbench/services/textfile/test/node/encoding/encoding.test.ts b/src/vs/workbench/services/textfile/test/node/encoding/encoding.test.ts index f3df7b40bea..bcd51283ba6 100644 --- a/src/vs/workbench/services/textfile/test/node/encoding/encoding.test.ts +++ b/src/vs/workbench/services/textfile/test/node/encoding/encoding.test.ts @@ -7,10 +7,10 @@ import * as assert from 'assert'; import * as fs from 'fs'; import * as encoding from 'vs/workbench/services/textfile/common/encoding'; import * as streams from 'vs/base/common/stream'; -import * as iconv from '@vscode/iconv-lite-umd'; import { newWriteableBufferStream, VSBuffer, VSBufferReadableStream, streamToBufferReadableStream } from 'vs/base/common/buffer'; import { splitLines } from 'vs/base/common/strings'; import { FileAccess } from 'vs/base/common/network'; +import { importAMDNodeModule } from 'vs/amdX'; export async function detectEncodingByBOM(file: string): Promise { try { @@ -213,7 +213,7 @@ suite('Encoding', () => { if (err) { reject(err); } else { - resolve(iconv.decode(data, encoding.toNodeEncoding(fileEncoding!))); + resolve(importAMDNodeModule('@vscode/iconv-lite-umd', 'lib/iconv-lite-umd.js').then(iconv => iconv.decode(data, encoding.toNodeEncoding(fileEncoding!)))); } }); }); @@ -385,6 +385,8 @@ suite('Encoding', () => { const path = FileAccess.asFileUri('vs/workbench/services/textfile/test/node/encoding/fixtures/some_utf16be.css').fsPath; const source = await readAndDecodeFromDisk(path, encoding.UTF16be); + const iconv = await importAMDNodeModule('@vscode/iconv-lite-umd', 'lib/iconv-lite-umd.js'); + const expected = VSBuffer.wrap( iconv.encode(source, encoding.toNodeEncoding(encoding.UTF16be)) ).toString(); @@ -446,7 +448,7 @@ suite('Encoding', () => { if (enc === encoding.UTF8_with_bom) { continue; // skip over encodings from us } - + const iconv = await importAMDNodeModule('@vscode/iconv-lite-umd', 'lib/iconv-lite-umd.js'); assert.strictEqual(iconv.encodingExists(enc), true, enc); } }); From 8c19d3f0e7bcb6689fa33c6f47661eefe7c66ac6 Mon Sep 17 00:00:00 2001 From: Johannes Date: Tue, 4 Jul 2023 11:31:36 +0200 Subject: [PATCH 136/926] add lint rule for AMD node imports --- .eslintplugin/code-amd-node-module.ts | 60 +++++++++++++++++++++++++++ .eslintrc.json | 8 ++++ 2 files changed, 68 insertions(+) create mode 100644 .eslintplugin/code-amd-node-module.ts diff --git a/.eslintplugin/code-amd-node-module.ts b/.eslintplugin/code-amd-node-module.ts new file mode 100644 index 00000000000..35c89dcb219 --- /dev/null +++ b/.eslintplugin/code-amd-node-module.ts @@ -0,0 +1,60 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as eslint from 'eslint'; +import { join } from 'path'; + + +export = new class ApiProviderNaming implements eslint.Rule.RuleModule { + + readonly meta: eslint.Rule.RuleMetaData = { + messages: { + amdX: 'Use `import type` for import declarations, use `amdX#importAMDNodeModule` for import expressions' + } + }; + + create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { + + const modules = new Set(); + + try { + const { dependencies, optionalDependencies } = require(join(__dirname, '../package.json')); + const all = Object.keys(dependencies).concat(Object.keys(optionalDependencies)); + for (const key of all) { + modules.add(key); + } + + } catch (e) { + console.error(e); + throw e; + } + + + const checkImport = (node: any) => { + + if (node.type !== 'Literal' || typeof node.value !== 'string') { + return; + } + + if (node.parent.importKind === 'type') { + return; + } + + if (!modules.has(node.value)) { + return; + } + + context.report({ + node, + messageId: 'amdX' + }); + } + + return { + ['ImportExpression Literal']: checkImport, + ['ImportDeclaration Literal']: checkImport + }; + } +}; diff --git a/.eslintrc.json b/.eslintrc.json index 7e6ab36bef3..2f52208fa18 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -194,6 +194,14 @@ ] } }, + { + "files": [ + "src/**/{common,browser}/**/*.ts" + ], + "rules": { + "local/code-amd-node-module": "warn" + } + }, { "files": [ "src/**/*.ts" From 85b5479c3773765ea0c4fe7d8da7c2830dfcfae4 Mon Sep 17 00:00:00 2001 From: Johannes Date: Tue, 4 Jul 2023 11:32:54 +0200 Subject: [PATCH 137/926] import our `IDisposable` not xterms --- .../workbench/contrib/accessibility/browser/accessibleView.ts | 3 +-- src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index a84bf2907c4..a9b03c3ed6f 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -5,7 +5,7 @@ import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; -import { Disposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { IEditorConstructionOptions } from 'vs/editor/browser/config/editorConfiguration'; import { EditorExtensionsRegistry } from 'vs/editor/browser/editorExtensions'; @@ -22,7 +22,6 @@ import { IInstantiationService, createDecorator } from 'vs/platform/instantiatio import { IOpenerService } from 'vs/platform/opener/common/opener'; import { SelectionClipboardContributionID } from 'vs/workbench/contrib/codeEditor/browser/selectionClipboard'; import { getSimpleEditorOptions } from 'vs/workbench/contrib/codeEditor/browser/simpleEditorOptions'; -import { IDisposable } from 'xterm'; const enum DIMENSION_DEFAULT { WIDTH = .6, diff --git a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts index f3b477d9c40..f10129a15c5 100644 --- a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts @@ -8,7 +8,7 @@ import * as Async from 'vs/base/common/async'; import { IStringDictionary } from 'vs/base/common/collections'; import { Emitter, Event } from 'vs/base/common/event'; import { isUNC } from 'vs/base/common/extpath'; -import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { LinkedMap, Touch } from 'vs/base/common/map'; import * as Objects from 'vs/base/common/objects'; import * as path from 'vs/base/common/path'; @@ -49,7 +49,6 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/ import { IOutputService } from 'vs/workbench/services/output/common/output'; import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite'; import { IPathService } from 'vs/workbench/services/path/common/pathService'; -import { IDisposable } from 'xterm'; interface ITerminalData { terminal: ITerminalInstance; From 06a27916c6ea9c1b7ab039afdd406cdecea1c1c3 Mon Sep 17 00:00:00 2001 From: Johannes Date: Tue, 4 Jul 2023 11:41:21 +0200 Subject: [PATCH 138/926] type-imports and importAMDNodeModule for textMate --- .../services/textMate/browser/worker/textMate.worker.ts | 2 +- .../textMate/browser/workerHost/textMateWorkerHost.ts | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/services/textMate/browser/worker/textMate.worker.ts b/src/vs/workbench/services/textMate/browser/worker/textMate.worker.ts index 67307a7d49d..9f4a7f42512 100644 --- a/src/vs/workbench/services/textMate/browser/worker/textMate.worker.ts +++ b/src/vs/workbench/services/textMate/browser/worker/textMate.worker.ts @@ -10,7 +10,7 @@ import { IWorkerContext } from 'vs/editor/common/services/editorSimpleWorker'; import type { StateDeltas, TextMateWorkerHost } from 'vs/workbench/services/textMate/browser/workerHost/textMateWorkerHost'; import { ICreateGrammarResult, TMGrammarFactory } from 'vs/workbench/services/textMate/common/TMGrammarFactory'; import { IValidEmbeddedLanguagesMap, IValidGrammarDefinition, IValidTokenTypeMap } from 'vs/workbench/services/textMate/common/TMScopeRegistry'; -import { IOnigLib, IRawTheme } from 'vscode-textmate'; +import type { IOnigLib, IRawTheme } from 'vscode-textmate'; import { TextMateWorkerModel } from './textMateWorkerModel'; export interface ICreateData { diff --git a/src/vs/workbench/services/textMate/browser/workerHost/textMateWorkerHost.ts b/src/vs/workbench/services/textMate/browser/workerHost/textMateWorkerHost.ts index 81900c837ba..71032a5943f 100644 --- a/src/vs/workbench/services/textMate/browser/workerHost/textMateWorkerHost.ts +++ b/src/vs/workbench/services/textMate/browser/workerHost/textMateWorkerHost.ts @@ -23,7 +23,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ICreateData, TextMateTokenizationWorker } from 'vs/workbench/services/textMate/browser/worker/textMate.worker'; import { TextMateWorkerTokenizerController } from 'vs/workbench/services/textMate/browser/workerHost/textMateWorkerTokenizerController'; import { IValidGrammarDefinition } from 'vs/workbench/services/textMate/common/TMScopeRegistry'; -import { INITIAL, IRawTheme, StackDiff } from 'vscode-textmate'; +import type { IRawTheme, StackDiff } from 'vscode-textmate'; export class TextMateWorkerHost implements IDisposable { private static _reportedMismatchingTokens = false; @@ -152,7 +152,7 @@ export class TextMateWorkerHost implements IDisposable { } store.add(keepAliveWhenAttached(textModel, () => { - const controller = new TextMateWorkerTokenizerController(textModel, workerProxy, this._languageService.languageIdCodec, tokenStore, INITIAL, this._configurationService, maxTokenizationLineLength); + const controller = new TextMateWorkerTokenizerController(textModel, workerProxy, this._languageService.languageIdCodec, tokenStore, this._configurationService, maxTokenizationLineLength); this._workerTokenizerControllers.set(textModel.uri.toString(), controller); return toDisposable(() => { @@ -235,4 +235,3 @@ function keepAliveWhenAttached(textModel: ITextModel, factory: () => IDisposable })); return disposableStore; } - From 0b233ad23e7880f4eb6a094c727ffe5bf5707214 Mon Sep 17 00:00:00 2001 From: Johannes Date: Tue, 4 Jul 2023 11:46:59 +0200 Subject: [PATCH 139/926] type-imports and importAMDNodeModule for xterm/terminal --- .../partialCommandDetectionCapability.ts | 2 +- .../contrib/terminal/browser/terminal.ts | 2 +- .../terminal/browser/terminalInstance.ts | 3 ++- .../terminal/browser/terminalTestHelpers.ts | 3 +-- .../terminal/browser/xterm/decorationAddon.ts | 2 +- .../terminal/browser/xterm/suggestAddon.ts | 2 +- .../terminal/browser/xterm/xtermTerminal.ts | 13 ++++++------ .../commandDetectionCapability.test.ts | 9 +++++--- .../partialCommandDetectionCapability.test.ts | 9 +++++--- .../browser/xterm/decorationAddon.test.ts | 21 +++++++++++-------- .../browser/xterm/lineDataEventAddon.test.ts | 8 ++++--- .../xterm/shellIntegrationAddon.test.ts | 9 +++++--- .../test/browser/xterm/xtermTerminal.test.ts | 16 ++++++++------ .../browser/bufferContentTracker.ts | 2 +- .../terminal.accessibility.contribution.ts | 2 +- .../browser/terminalAccessibilityHelp.ts | 2 +- .../test/browser/bufferContentTracker.test.ts | 3 +-- .../browser/terminal.find.contribution.ts | 2 +- .../find/browser/terminalFindWidget.ts | 2 +- .../terminalContrib/links/browser/links.ts | 2 +- .../browser/terminal.links.contribution.ts | 2 +- .../browser/terminalExternalLinkDetector.ts | 2 +- .../browser/terminalLinkDetectorAdapter.ts | 2 +- .../links/browser/terminalLinkQuickpick.ts | 2 +- .../browser/terminalLocalLinkDetector.ts | 2 +- .../browser/terminalMultiLineLinkDetector.ts | 2 +- .../links/browser/terminalUriLinkDetector.ts | 2 +- .../links/browser/terminalWordLinkDetector.ts | 2 +- .../links/test/browser/linkTestUtils.ts | 2 +- .../test/browser/terminalLinkHelpers.test.ts | 2 +- .../test/browser/terminalLinkManager.test.ts | 8 ++++--- .../test/browser/terminalLinkOpeners.test.ts | 8 ++++--- .../browser/terminalLocalLinkDetector.test.ts | 8 ++++--- .../terminalMultiLineLinkDetector.test.ts | 8 ++++--- .../browser/terminalUriLinkDetector.test.ts | 8 ++++--- .../browser/terminalWordLinkDetector.test.ts | 6 ++++-- .../quickFix/browser/quickFixAddon.ts | 2 +- .../browser/terminal.quickFix.contribution.ts | 2 +- .../test/browser/quickFixAddon.test.ts | 2 +- .../terminal.typeAhead.contribution.ts | 2 +- .../test/browser/terminalTypeAhead.test.ts | 2 +- 41 files changed, 110 insertions(+), 80 deletions(-) diff --git a/src/vs/platform/terminal/common/capabilities/partialCommandDetectionCapability.ts b/src/vs/platform/terminal/common/capabilities/partialCommandDetectionCapability.ts index a1b063d0961..5ec1e03c9b4 100644 --- a/src/vs/platform/terminal/common/capabilities/partialCommandDetectionCapability.ts +++ b/src/vs/platform/terminal/common/capabilities/partialCommandDetectionCapability.ts @@ -7,7 +7,7 @@ import { Emitter } from 'vs/base/common/event'; import { IPartialCommandDetectionCapability, TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities'; // Importing types is safe in any layer // eslint-disable-next-line local/code-import-patterns -import { IMarker, Terminal } from 'xterm-headless'; +import type { IMarker, Terminal } from 'xterm-headless'; const enum Constants { /** diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index 43137f9dd62..f57623f7ca8 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -25,7 +25,7 @@ import { XtermTerminal } from 'vs/workbench/contrib/terminal/browser/xterm/xterm import { IRegisterContributedProfileArgs, IRemoteTerminalAttachTarget, IStartExtensionTerminalRequest, ITerminalConfigHelper, ITerminalFont, ITerminalProcessExtHostProxy } from 'vs/workbench/contrib/terminal/common/terminal'; import { EditorGroupColumn } from 'vs/workbench/services/editor/common/editorGroupColumn'; import { ISimpleSelectedSuggestion } from 'vs/workbench/services/suggest/browser/simpleSuggestWidget'; -import { IMarker, Terminal as RawXtermTerminal } from 'xterm'; +import type { IMarker, Terminal as RawXtermTerminal } from 'xterm'; export const ITerminalService = createDecorator('terminalService'); export const ITerminalEditorService = createDecorator('terminalEditorService'); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 14e192023be..4f2ca596f77 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -85,6 +85,7 @@ import { IHistoryService } from 'vs/workbench/services/history/common/history'; import { IWorkbenchLayoutService, Position } 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'; import { ISimpleSelectedSuggestion } from 'vs/workbench/services/suggest/browser/simpleSuggestWidget'; import type { IMarker, Terminal as XTermTerminal } from 'xterm'; @@ -684,7 +685,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { return xtermConstructor; } xtermConstructor = Promises.withAsyncBody(async (resolve) => { - const Terminal = (await import('xterm')).Terminal; + const Terminal = (await importAMDNodeModule('xterm', 'lib/xterm.js')).Terminal; // Localize strings Terminal.strings.promptLabel = nls.localize('terminal.integrated.a11yPromptLabel', 'Terminal input'); Terminal.strings.tooMuchOutput = keybinding ? nls.localize('terminal.integrated.useAccessibleBuffer', 'Use the accessible buffer {0} to manually review output', keybinding.getLabel()) : nls.localize('terminal.integrated.useAccessibleBufferNoKb', 'Use the Terminal: Focus Accessible Buffer command to manually review output'); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTestHelpers.ts b/src/vs/workbench/contrib/terminal/browser/terminalTestHelpers.ts index 7b156978445..0a63552827c 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTestHelpers.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTestHelpers.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { timeout } from 'vs/base/common/async'; -import { Terminal } from 'xterm'; +import type { Terminal } from 'xterm'; export async function writeP(terminal: Terminal, data: string): Promise { return new Promise((resolve, reject) => { @@ -16,4 +16,3 @@ export async function writeP(terminal: Terminal, data: string): Promise { }); }); } - diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/decorationAddon.ts b/src/vs/workbench/contrib/terminal/browser/xterm/decorationAddon.ts index a02f3d705f0..b3f68aa4e6d 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/decorationAddon.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/decorationAddon.ts @@ -23,7 +23,7 @@ import { terminalDecorationError, terminalDecorationIncomplete, terminalDecorati import { DecorationSelector, TerminalDecorationHoverManager, updateLayout } from 'vs/workbench/contrib/terminal/browser/xterm/decorationStyles'; import { TERMINAL_COMMAND_DECORATION_DEFAULT_BACKGROUND_COLOR, TERMINAL_COMMAND_DECORATION_ERROR_BACKGROUND_COLOR, TERMINAL_COMMAND_DECORATION_SUCCESS_BACKGROUND_COLOR } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; -import { IDecoration, ITerminalAddon, Terminal } from 'xterm'; +import type { IDecoration, ITerminalAddon, Terminal } from 'xterm'; import { AudioCue, IAudioCueService } from 'vs/platform/audioCues/browser/audioCueService'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/suggestAddon.ts b/src/vs/workbench/contrib/terminal/browser/xterm/suggestAddon.ts index 3f1ba6c030e..362f70dcde8 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/suggestAddon.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/suggestAddon.ts @@ -19,7 +19,7 @@ import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storag import { activeContrastBorder } from 'vs/platform/theme/common/colorRegistry'; import { ISuggestController } from 'vs/workbench/contrib/terminal/browser/terminal'; import { TerminalStorageKeys } from 'vs/workbench/contrib/terminal/common/terminalStorageKeys'; -import { ITerminalAddon, Terminal } from 'xterm'; +import type { ITerminalAddon, Terminal } from 'xterm'; import { getListStyles } from 'vs/platform/theme/browser/defaultStyles'; const enum ShellIntegrationOscPs { diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts index f2b9464278e..ef2640b6490 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts @@ -35,6 +35,7 @@ import { DecorationAddon } from 'vs/workbench/contrib/terminal/browser/xterm/dec import { ITerminalCapabilityStore, ITerminalCommand, TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities'; import { Emitter } from 'vs/base/common/event'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { importAMDNodeModule } from 'vs/amdX'; import { SuggestAddon } from 'vs/workbench/contrib/terminal/browser/xterm/suggestAddon'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; @@ -682,7 +683,7 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal, ID protected async _getCanvasAddonConstructor(): Promise { if (!CanvasAddon) { - CanvasAddon = (await import('xterm-addon-canvas')).CanvasAddon; + CanvasAddon = (await importAMDNodeModule('xterm-addon-canvas', 'lib/xterm-addon-canvas.js')).CanvasAddon; } return CanvasAddon; } @@ -706,35 +707,35 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal, ID protected async _getImageAddonConstructor(): Promise { if (!ImageAddon) { - ImageAddon = (await import('xterm-addon-image')).ImageAddon; + ImageAddon = (await importAMDNodeModule('xterm-addon-image', 'lib/xterm-addon-image.js')).ImageAddon; } return ImageAddon; } protected async _getSearchAddonConstructor(): Promise { if (!SearchAddon) { - SearchAddon = (await import('xterm-addon-search')).SearchAddon; + SearchAddon = (await importAMDNodeModule('xterm-addon-search', 'lib/xterm-addon-search.js')).SearchAddon; } return SearchAddon; } protected async _getUnicode11Constructor(): Promise { if (!Unicode11Addon) { - Unicode11Addon = (await import('xterm-addon-unicode11')).Unicode11Addon; + Unicode11Addon = (await importAMDNodeModule('xterm-addon-unicode11', 'lib/xterm-addon-unicode11.js')).Unicode11Addon; } return Unicode11Addon; } protected async _getWebglAddonConstructor(): Promise { if (!WebglAddon) { - WebglAddon = (await import('xterm-addon-webgl')).WebglAddon; + WebglAddon = (await importAMDNodeModule('xterm-addon-webgl', 'lib/xterm-addon-webgl.js')).WebglAddon; } return WebglAddon; } protected async _getSerializeAddonConstructor(): Promise { if (!SerializeAddon) { - SerializeAddon = (await import('xterm-addon-serialize')).SerializeAddon; + SerializeAddon = (await importAMDNodeModule('xterm-addon-serialize', 'lib/xterm-addon-serialize.js')).SerializeAddon; } return SerializeAddon; } diff --git a/src/vs/workbench/contrib/terminal/test/browser/capabilities/commandDetectionCapability.test.ts b/src/vs/workbench/contrib/terminal/test/browser/capabilities/commandDetectionCapability.test.ts index 93c41ea31b6..b23f6773cd7 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/capabilities/commandDetectionCapability.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/capabilities/commandDetectionCapability.test.ts @@ -4,13 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import { deepStrictEqual, ok } from 'assert'; -import { Terminal } from 'xterm'; +import type { Terminal } from 'xterm'; import { CommandDetectionCapability } from 'vs/platform/terminal/common/capabilities/commandDetectionCapability'; import { NullLogService } from 'vs/platform/log/common/log'; import { ITerminalCommand } from 'vs/platform/terminal/common/capabilities/capabilities'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { IContextMenuDelegate } from 'vs/base/browser/contextmenu'; +import { importAMDNodeModule } from 'vs/amdX'; import { writeP } from 'vs/workbench/contrib/terminal/browser/terminalTestHelpers'; type TestTerminalCommandMatch = Pick & { marker: { line: number } }; @@ -54,8 +55,10 @@ suite('CommandDetectionCapability', () => { capability.handleCommandFinished(exitCode); } - setup(() => { - xterm = new Terminal({ allowProposedApi: true, cols: 80 }); + setup(async () => { + const TerminalCtor = (await importAMDNodeModule('xterm', 'lib/xterm.js')).Terminal; + + xterm = new TerminalCtor({ allowProposedApi: true, cols: 80 }); const instantiationService = new TestInstantiationService(); instantiationService.stub(IContextMenuService, { showContextMenu(delegate: IContextMenuDelegate): void { } } as Partial); capability = new TestCommandDetectionCapability(xterm, new NullLogService()); diff --git a/src/vs/workbench/contrib/terminal/test/browser/capabilities/partialCommandDetectionCapability.test.ts b/src/vs/workbench/contrib/terminal/test/browser/capabilities/partialCommandDetectionCapability.test.ts index 734a9de555e..947b6745852 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/capabilities/partialCommandDetectionCapability.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/capabilities/partialCommandDetectionCapability.test.ts @@ -5,8 +5,9 @@ import { deepStrictEqual } from 'assert'; import { PartialCommandDetectionCapability } from 'vs/platform/terminal/common/capabilities/partialCommandDetectionCapability'; -import { IMarker, Terminal } from 'xterm'; +import type { IMarker, Terminal } from 'xterm'; import { IXtermCore } from 'vs/workbench/contrib/terminal/browser/xterm-private'; +import { importAMDNodeModule } from 'vs/amdX'; import { writeP } from 'vs/workbench/contrib/terminal/browser/terminalTestHelpers'; interface TestTerminal extends Terminal { @@ -23,8 +24,10 @@ suite('PartialCommandDetectionCapability', () => { deepStrictEqual(addEvents.map(e => e.line), expectedLines); } - setup(() => { - xterm = new Terminal({ allowProposedApi: true, cols: 80 }) as TestTerminal; + setup(async () => { + const TerminalCtor = (await importAMDNodeModule('xterm', 'lib/xterm.js')).Terminal; + + xterm = new TerminalCtor({ allowProposedApi: true, cols: 80 }) as TestTerminal; capability = new PartialCommandDetectionCapability(xterm); addEvents = []; capability.onCommandFinished(e => addEvents.push(e)); diff --git a/src/vs/workbench/contrib/terminal/test/browser/xterm/decorationAddon.test.ts b/src/vs/workbench/contrib/terminal/test/browser/xterm/decorationAddon.test.ts index e30371c1690..4126dc9640d 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/xterm/decorationAddon.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/xterm/decorationAddon.test.ts @@ -10,7 +10,7 @@ import { ILogService, NullLogService } from 'vs/platform/log/common/log'; import { DecorationAddon } from 'vs/workbench/contrib/terminal/browser/xterm/decorationAddon'; import { TerminalCapabilityStore } from 'vs/platform/terminal/common/capabilities/terminalCapabilityStore'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; -import { IDecoration, IDecorationOptions, Terminal } from 'xterm'; +import type { IDecoration, IDecorationOptions } from 'xterm'; import { ITerminalCommand, TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities'; import { CommandDetectionCapability } from 'vs/platform/terminal/common/capabilities/commandDetectionCapability'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; @@ -19,18 +19,21 @@ import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService import { IThemeService } from 'vs/platform/theme/common/themeService'; import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { TestLifecycleService } from 'vs/workbench/test/browser/workbenchTestServices'; +import { importAMDNodeModule } from 'vs/amdX'; -class TestTerminal extends Terminal { - override registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined { - if (decorationOptions.marker.isDisposed) { - return undefined; +suite('DecorationAddon', async () => { + + const TerminalCtor = (await importAMDNodeModule('xterm', 'lib/xterm.js')).Terminal; + class TestTerminal extends TerminalCtor { + override registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined { + if (decorationOptions.marker.isDisposed) { + return undefined; + } + const element = document.createElement('div'); + return { marker: decorationOptions.marker, element, onDispose: () => { }, isDisposed: false, dispose: () => { }, onRender: (element: HTMLElement) => { return element; } } as unknown as IDecoration; } - const element = document.createElement('div'); - return { marker: decorationOptions.marker, element, onDispose: () => { }, isDisposed: false, dispose: () => { }, onRender: (element: HTMLElement) => { return element; } } as unknown as IDecoration; } -} -suite('DecorationAddon', () => { let decorationAddon: DecorationAddon; let xterm: TestTerminal; diff --git a/src/vs/workbench/contrib/terminal/test/browser/xterm/lineDataEventAddon.test.ts b/src/vs/workbench/contrib/terminal/test/browser/xterm/lineDataEventAddon.test.ts index 40b26b9ef1a..8953411f4a9 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/xterm/lineDataEventAddon.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/xterm/lineDataEventAddon.test.ts @@ -3,10 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Terminal } from 'xterm'; +import type { Terminal } from 'xterm'; import { LineDataEventAddon } from 'vs/workbench/contrib/terminal/browser/xterm/lineDataEventAddon'; import { OperatingSystem } from 'vs/base/common/platform'; import { deepStrictEqual } from 'assert'; +import { importAMDNodeModule } from 'vs/amdX'; import { writeP } from 'vs/workbench/contrib/terminal/browser/terminalTestHelpers'; suite('LineDataEventAddon', () => { @@ -16,8 +17,9 @@ suite('LineDataEventAddon', () => { suite('onLineData', () => { let events: string[]; - setup(() => { - xterm = new Terminal({ allowProposedApi: true, cols: 4 }); + setup(async () => { + const TerminalCtor = (await importAMDNodeModule('xterm', 'lib/xterm.js')).Terminal; + xterm = new TerminalCtor({ allowProposedApi: true, cols: 4 }); lineDataEventAddon = new LineDataEventAddon(); xterm.loadAddon(lineDataEventAddon); diff --git a/src/vs/workbench/contrib/terminal/test/browser/xterm/shellIntegrationAddon.test.ts b/src/vs/workbench/contrib/terminal/test/browser/xterm/shellIntegrationAddon.test.ts index 2943bede65e..6ddb4dd54a0 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/xterm/shellIntegrationAddon.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/xterm/shellIntegrationAddon.test.ts @@ -3,12 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Terminal } from 'xterm'; +import type { Terminal } from 'xterm'; import { strictEqual, deepStrictEqual, deepEqual } from 'assert'; import * as sinon from 'sinon'; import { parseKeyValueAssignment, parseMarkSequence, deserializeMessage, ShellIntegrationAddon } from 'vs/platform/terminal/common/xterm/shellIntegrationAddon'; import { ITerminalCapabilityStore, TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities'; import { NullLogService } from 'vs/platform/log/common/log'; +import { importAMDNodeModule } from 'vs/amdX'; import { writeP } from 'vs/workbench/contrib/terminal/browser/terminalTestHelpers'; class TestShellIntegrationAddon extends ShellIntegrationAddon { @@ -29,8 +30,10 @@ suite('ShellIntegrationAddon', () => { let shellIntegrationAddon: TestShellIntegrationAddon; let capabilities: ITerminalCapabilityStore; - setup(() => { - xterm = new Terminal({ allowProposedApi: true, cols: 80, rows: 30 }); + setup(async () => { + + const TerminalCtor = (await importAMDNodeModule('xterm', 'lib/xterm.js')).Terminal; + xterm = new TerminalCtor({ allowProposedApi: true, cols: 80, rows: 30 }); shellIntegrationAddon = new TestShellIntegrationAddon('', true, undefined, new NullLogService()); xterm.loadAddon(shellIntegrationAddon); capabilities = shellIntegrationAddon.capabilities; diff --git a/src/vs/workbench/contrib/terminal/test/browser/xterm/xtermTerminal.test.ts b/src/vs/workbench/contrib/terminal/test/browser/xterm/xtermTerminal.test.ts index bfad4abdad8..aab94b18d5d 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/xterm/xtermTerminal.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/xterm/xtermTerminal.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IEvent, Terminal } from 'xterm'; +import type { IEvent, Terminal } from 'xterm'; import { XtermTerminal } from 'vs/workbench/contrib/terminal/browser/xterm/xtermTerminal'; import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; @@ -18,7 +18,7 @@ import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { Emitter } from 'vs/base/common/event'; import { TERMINAL_BACKGROUND_COLOR, TERMINAL_FOREGROUND_COLOR, TERMINAL_CURSOR_FOREGROUND_COLOR, TERMINAL_CURSOR_BACKGROUND_COLOR, TERMINAL_SELECTION_BACKGROUND_COLOR, TERMINAL_SELECTION_FOREGROUND_COLOR, TERMINAL_INACTIVE_SELECTION_BACKGROUND_COLOR } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; import { PANEL_BACKGROUND, SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; -import { WebglAddon } from 'xterm-addon-webgl'; +import type { WebglAddon } from 'xterm-addon-webgl'; import { NullLogService } from 'vs/platform/log/common/log'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { TestStorageService } from 'vs/workbench/test/common/workbenchTestServices'; @@ -28,6 +28,7 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView import { ContextMenuService } from 'vs/platform/contextview/browser/contextMenuService'; import { TestLifecycleService } from 'vs/workbench/test/browser/workbenchTestServices'; import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; +import { importAMDNodeModule } from 'vs/amdX'; import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; import { Color, RGBA } from 'vs/base/common/color'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; @@ -97,8 +98,9 @@ suite('XtermTerminal', () => { let viewDescriptorService: TestViewDescriptorService; let xterm: TestXtermTerminal; let configHelper: TerminalConfigHelper; + let XTermBaseCtor: typeof Terminal; - setup(() => { + setup(async () => { configurationService = new TestConfigurationService({ editor: { fastScrollSensitivity: 2, @@ -122,7 +124,9 @@ suite('XtermTerminal', () => { instantiationService.stub(IContextKeyService, new MockContextKeyService()); configHelper = instantiationService.createInstance(TerminalConfigHelper); - xterm = instantiationService.createInstance(TestXtermTerminal, Terminal, configHelper, 80, 30, { getBackgroundColor: () => undefined }, new TerminalCapabilityStore(), '', new MockContextKeyService().createKey('', true)!, true); + XTermBaseCtor = (await importAMDNodeModule('xterm', 'lib/xterm.js')).Terminal; + + xterm = instantiationService.createInstance(TestXtermTerminal, XTermBaseCtor, configHelper, 80, 30, { getBackgroundColor: () => undefined }, new TerminalCapabilityStore(), '', new MockContextKeyService().createKey('', true)!, true); TestWebglAddon.shouldThrow = false; TestWebglAddon.isEnabled = false; @@ -139,7 +143,7 @@ suite('XtermTerminal', () => { [PANEL_BACKGROUND]: '#ff0000', [SIDE_BAR_BACKGROUND]: '#00ff00' })); - xterm = instantiationService.createInstance(XtermTerminal, Terminal, configHelper, 80, 30, { getBackgroundColor: () => new Color(new RGBA(255, 0, 0)) }, new TerminalCapabilityStore(), '', new MockContextKeyService().createKey('', true)!, true); + xterm = instantiationService.createInstance(XtermTerminal, XTermBaseCtor, configHelper, 80, 30, { getBackgroundColor: () => new Color(new RGBA(255, 0, 0)) }, new TerminalCapabilityStore(), '', new MockContextKeyService().createKey('', true)!, true); strictEqual(xterm.raw.options.theme?.background, '#ff0000'); }); test('should react to and apply theme changes', () => { @@ -168,7 +172,7 @@ suite('XtermTerminal', () => { 'terminal.ansiBrightCyan': '#150000', 'terminal.ansiBrightWhite': '#160000', })); - xterm = instantiationService.createInstance(XtermTerminal, Terminal, configHelper, 80, 30, { getBackgroundColor: () => undefined }, new TerminalCapabilityStore(), '', new MockContextKeyService().createKey('', true)!, true); + xterm = instantiationService.createInstance(XtermTerminal, XTermBaseCtor, configHelper, 80, 30, { getBackgroundColor: () => undefined }, new TerminalCapabilityStore(), '', new MockContextKeyService().createKey('', true)!, true); deepStrictEqual(xterm.raw.options.theme, { background: undefined, foreground: '#000200', diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/bufferContentTracker.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/bufferContentTracker.ts index 0afc8ef343e..53bc70995a9 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/bufferContentTracker.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/bufferContentTracker.ts @@ -6,7 +6,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ITerminalLogService, TerminalSettingId } from 'vs/platform/terminal/common/terminal'; import { IXtermTerminal } from 'vs/workbench/contrib/terminal/browser/terminal'; -import { IMarker, Terminal } from 'xterm'; +import type { IMarker, Terminal } from 'xterm'; export class BufferContentTracker { /** diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts index a121b0c4019..b096c56526a 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts @@ -22,7 +22,7 @@ import { ITerminalProcessManager, TerminalCommandId } from 'vs/workbench/contrib import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; import { TerminalAccessibleContentProvider } from 'vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibilityHelp'; import { AccessibleBufferWidget, NavigationType } from 'vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleBuffer'; -import { Terminal } from 'xterm'; +import type { Terminal } from 'xterm'; class AccessibleBufferContribution extends DisposableStore implements ITerminalContribution { static readonly ID = 'terminal.accessible-buffer'; diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibilityHelp.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibilityHelp.ts index 6c5ad676949..ac3d7f76b6b 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibilityHelp.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibilityHelp.ts @@ -13,7 +13,7 @@ import { ShellIntegrationStatus, WindowsShellType } from 'vs/platform/terminal/c import { AccessibleViewType, IAccessibleContentProvider, IAccessibleViewOptions } from 'vs/workbench/contrib/accessibility/browser/accessibleView'; import { ITerminalInstance, IXtermTerminal } from 'vs/workbench/contrib/terminal/browser/terminal'; import { TerminalCommandId } from 'vs/workbench/contrib/terminal/common/terminal'; -import { Terminal } from 'xterm'; +import type { Terminal } from 'xterm'; export const enum ClassName { AccessibleBuffer = 'terminal-accessibility-help', diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/test/browser/bufferContentTracker.test.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/test/browser/bufferContentTracker.test.ts index 27236773745..98df5b42f9b 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/test/browser/bufferContentTracker.test.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/test/browser/bufferContentTracker.test.ts @@ -26,7 +26,7 @@ import { BufferContentTracker } from 'vs/workbench/contrib/terminalContrib/acces import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { TestLifecycleService } from 'vs/workbench/test/browser/workbenchTestServices'; import { TestLoggerService } from 'vs/workbench/test/common/workbenchTestServices'; -import { Terminal } from 'xterm'; +import type { Terminal } from 'xterm'; const defaultTerminalConfig: Partial = { fontFamily: 'monospace', @@ -137,4 +137,3 @@ async function writeAndAssertBufferState(data: string, rows: number, terminal: T assert.strictEqual(bufferTracker.lines.length, rows); assert.deepStrictEqual(bufferTracker.lines, content.split('\r\n')); } - diff --git a/src/vs/workbench/contrib/terminalContrib/find/browser/terminal.find.contribution.ts b/src/vs/workbench/contrib/terminalContrib/find/browser/terminal.find.contribution.ts index 5b9410b554e..4de27fd31f5 100644 --- a/src/vs/workbench/contrib/terminalContrib/find/browser/terminal.find.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/find/browser/terminal.find.contribution.ts @@ -19,7 +19,7 @@ import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/wid import { ITerminalProcessManager, TerminalCommandId } from 'vs/workbench/contrib/terminal/common/terminal'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; import { TerminalFindWidget } from 'vs/workbench/contrib/terminalContrib/find/browser/terminalFindWidget'; -import { Terminal as RawXtermTerminal } from 'xterm'; +import type { Terminal as RawXtermTerminal } from 'xterm'; class TerminalFindContribution extends Disposable implements ITerminalContribution { static readonly ID = 'terminal.find'; diff --git a/src/vs/workbench/contrib/terminalContrib/find/browser/terminalFindWidget.ts b/src/vs/workbench/contrib/terminalContrib/find/browser/terminalFindWidget.ts index 94311826099..2a3587555c5 100644 --- a/src/vs/workbench/contrib/terminalContrib/find/browser/terminalFindWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/find/browser/terminalFindWidget.ts @@ -12,7 +12,7 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { Event } from 'vs/base/common/event'; -import { ISearchOptions } from 'xterm-addon-search'; +import type { ISearchOptions } from 'xterm-addon-search'; export class TerminalFindWidget extends SimpleFindWidget { private _findInputFocused: IContextKey; diff --git a/src/vs/workbench/contrib/terminalContrib/links/browser/links.ts b/src/vs/workbench/contrib/terminalContrib/links/browser/links.ts index b619f7c6a53..e7669035553 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/browser/links.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/browser/links.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IBufferLine, IBufferRange, Terminal } from 'xterm'; +import type { IBufferLine, IBufferRange, Terminal } from 'xterm'; import { URI } from 'vs/base/common/uri'; import { IHoverAction } from 'vs/workbench/services/hover/browser/hover'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; diff --git a/src/vs/workbench/contrib/terminalContrib/links/browser/terminal.links.contribution.ts b/src/vs/workbench/contrib/terminalContrib/links/browser/terminal.links.contribution.ts index dc3dc973921..3bca06c97e1 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/browser/terminal.links.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/browser/terminal.links.contribution.ts @@ -21,7 +21,7 @@ import { IDetectedLinks, TerminalLinkManager } from 'vs/workbench/contrib/termin import { TerminalLinkProviderService } from 'vs/workbench/contrib/terminalContrib/links/browser/terminalLinkProviderService'; import { TerminalLinkQuickpick } from 'vs/workbench/contrib/terminalContrib/links/browser/terminalLinkQuickpick'; import { TerminalLinkResolver } from 'vs/workbench/contrib/terminalContrib/links/browser/terminalLinkResolver'; -import { Terminal as RawXtermTerminal } from 'xterm'; +import type { Terminal as RawXtermTerminal } from 'xterm'; registerSingleton(ITerminalLinkProviderService, TerminalLinkProviderService, InstantiationType.Delayed); diff --git a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalExternalLinkDetector.ts b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalExternalLinkDetector.ts index 62d4f8ed497..4a25c52530d 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalExternalLinkDetector.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalExternalLinkDetector.ts @@ -6,7 +6,7 @@ import { ITerminalLinkDetector, ITerminalSimpleLink, OmitFirstArg } from 'vs/workbench/contrib/terminalContrib/links/browser/links'; import { convertLinkRangeToBuffer, getXtermLineContent } from 'vs/workbench/contrib/terminalContrib/links/browser/terminalLinkHelpers'; import { ITerminalExternalLinkProvider } from 'vs/workbench/contrib/terminal/browser/terminal'; -import { IBufferLine, Terminal } from 'xterm'; +import type { IBufferLine, Terminal } from 'xterm'; export class TerminalExternalLinkDetector implements ITerminalLinkDetector { readonly maxLinkLength = 2000; diff --git a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkDetectorAdapter.ts b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkDetectorAdapter.ts index 35d2d8e6f5b..73512c67f42 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkDetectorAdapter.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkDetectorAdapter.ts @@ -10,7 +10,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { ITerminalLinkDetector, ITerminalSimpleLink, TerminalBuiltinLinkType, TerminalLinkType } from 'vs/workbench/contrib/terminalContrib/links/browser/links'; import { TerminalLink } from 'vs/workbench/contrib/terminalContrib/links/browser/terminalLink'; import { XtermLinkMatcherHandler } from 'vs/workbench/contrib/terminalContrib/links/browser/terminalLinkManager'; -import { IBufferLine, ILink, ILinkProvider, IViewportRange } from 'xterm'; +import type { IBufferLine, ILink, ILinkProvider, IViewportRange } from 'xterm'; export interface IActivateLinkEvent { link: ITerminalSimpleLink; diff --git a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkQuickpick.ts b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkQuickpick.ts index b0ec423fa80..d76d1bee6ed 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkQuickpick.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkQuickpick.ts @@ -9,7 +9,7 @@ import { localize } from 'vs/nls'; import { QuickPickItem, IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; import { IDetectedLinks } from 'vs/workbench/contrib/terminalContrib/links/browser/terminalLinkManager'; import { TerminalLinkQuickPickEvent } from 'vs/workbench/contrib/terminal/browser/terminal'; -import { ILink } from 'xterm'; +import type { ILink } from 'xterm'; import { DisposableStore } from 'vs/base/common/lifecycle'; export class TerminalLinkQuickpick extends DisposableStore { diff --git a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLocalLinkDetector.ts b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLocalLinkDetector.ts index df3f60959c1..86847d17ec7 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLocalLinkDetector.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLocalLinkDetector.ts @@ -10,7 +10,7 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace import { ITerminalLinkDetector, ITerminalLinkResolver, ITerminalSimpleLink, ResolvedLink, TerminalBuiltinLinkType } from 'vs/workbench/contrib/terminalContrib/links/browser/links'; import { convertLinkRangeToBuffer, getXtermLineContent, getXtermRangesByAttr, osPathModule, updateLinkWithRelativeCwd } from 'vs/workbench/contrib/terminalContrib/links/browser/terminalLinkHelpers'; import { ITerminalCapabilityStore, TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities'; -import { IBufferLine, IBufferRange, Terminal } from 'xterm'; +import type { IBufferLine, IBufferRange, Terminal } from 'xterm'; import { ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal'; import { detectLinks } from 'vs/workbench/contrib/terminalContrib/links/browser/terminalLinkParsing'; import { ITerminalBackend, ITerminalLogService } from 'vs/platform/terminal/common/terminal'; diff --git a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalMultiLineLinkDetector.ts b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalMultiLineLinkDetector.ts index 3d808851241..55deec141de 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalMultiLineLinkDetector.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalMultiLineLinkDetector.ts @@ -8,7 +8,7 @@ import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity' import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { ITerminalLinkDetector, ITerminalLinkResolver, ITerminalSimpleLink, TerminalBuiltinLinkType } from 'vs/workbench/contrib/terminalContrib/links/browser/links'; import { convertLinkRangeToBuffer, getXtermLineContent } from 'vs/workbench/contrib/terminalContrib/links/browser/terminalLinkHelpers'; -import { IBufferLine, Terminal } from 'xterm'; +import type { IBufferLine, Terminal } from 'xterm'; import { ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal'; import { ITerminalBackend, ITerminalLogService } from 'vs/platform/terminal/common/terminal'; diff --git a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalUriLinkDetector.ts b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalUriLinkDetector.ts index 2d05f967c12..269f9dc2b75 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalUriLinkDetector.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalUriLinkDetector.ts @@ -11,7 +11,7 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace import { ITerminalLinkDetector, ITerminalLinkResolver, ITerminalSimpleLink, TerminalBuiltinLinkType } from 'vs/workbench/contrib/terminalContrib/links/browser/links'; import { convertLinkRangeToBuffer, getXtermLineContent } from 'vs/workbench/contrib/terminalContrib/links/browser/terminalLinkHelpers'; import { ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal'; -import { IBufferLine, Terminal } from 'xterm'; +import type { IBufferLine, Terminal } from 'xterm'; import { ITerminalBackend } from 'vs/platform/terminal/common/terminal'; const enum Constants { diff --git a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalWordLinkDetector.ts b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalWordLinkDetector.ts index 9bbdd4ebae6..c79f6389ad0 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalWordLinkDetector.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalWordLinkDetector.ts @@ -12,7 +12,7 @@ import { TerminalSettingId } from 'vs/platform/terminal/common/terminal'; import { ITerminalSimpleLink, ITerminalLinkDetector, TerminalBuiltinLinkType } from 'vs/workbench/contrib/terminalContrib/links/browser/links'; import { convertLinkRangeToBuffer, getXtermLineContent } from 'vs/workbench/contrib/terminalContrib/links/browser/terminalLinkHelpers'; import { ITerminalConfiguration, TERMINAL_CONFIG_SECTION } from 'vs/workbench/contrib/terminal/common/terminal'; -import { IBufferLine, Terminal } from 'xterm'; +import type { IBufferLine, Terminal } from 'xterm'; const enum Constants { /** diff --git a/src/vs/workbench/contrib/terminalContrib/links/test/browser/linkTestUtils.ts b/src/vs/workbench/contrib/terminalContrib/links/test/browser/linkTestUtils.ts index 51addbca645..eb2e4290863 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/test/browser/linkTestUtils.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/test/browser/linkTestUtils.ts @@ -6,7 +6,7 @@ import { deepStrictEqual } from 'assert'; import { ITerminalLinkDetector, TerminalLinkType } from 'vs/workbench/contrib/terminalContrib/links/browser/links'; import { URI } from 'vs/base/common/uri'; -import { IBufferLine } from 'xterm'; +import type { IBufferLine } from 'xterm'; export async function assertLinkHelper( text: string, diff --git a/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkHelpers.test.ts b/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkHelpers.test.ts index 9e2f52b2d4d..32bc485b448 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkHelpers.test.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkHelpers.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { IBufferLine, IBufferCell } from 'xterm'; +import type { IBufferLine, IBufferCell } from 'xterm'; import { convertLinkRangeToBuffer } from 'vs/workbench/contrib/terminalContrib/links/browser/terminalLinkHelpers'; suite('Workbench - Terminal Link Helpers', () => { diff --git a/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkManager.test.ts b/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkManager.test.ts index 2582beaefa6..6689f791380 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkManager.test.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkManager.test.ts @@ -21,8 +21,9 @@ import { ITerminalCapabilityImplMap, ITerminalCapabilityStore, TerminalCapabilit import { ITerminalConfiguration, ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal'; import { TestViewDescriptorService } from 'vs/workbench/contrib/terminal/test/browser/xterm/xtermTerminal.test'; import { TestStorageService } from 'vs/workbench/test/common/workbenchTestServices'; -import { ILink, Terminal } from 'xterm'; +import type { ILink, Terminal } from 'xterm'; import { TerminalLinkResolver } from 'vs/workbench/contrib/terminalContrib/links/browser/terminalLinkResolver'; +import { importAMDNodeModule } from 'vs/amdX'; const defaultTerminalConfig: Partial = { fontFamily: 'monospace', @@ -61,7 +62,7 @@ suite('TerminalLinkManager', () => { let xterm: Terminal; let linkManager: TestLinkManager; - setup(() => { + setup(async () => { configurationService = new TestConfigurationService({ editor: { fastScrollSensitivity: 2, @@ -82,7 +83,8 @@ suite('TerminalLinkManager', () => { instantiationService.stub(IThemeService, themeService); instantiationService.stub(IViewDescriptorService, viewDescriptorService); - xterm = new Terminal({ allowProposedApi: true, cols: 80, rows: 30 }); + const TerminalCtor = (await importAMDNodeModule('xterm', 'lib/xterm.js')).Terminal; + xterm = new TerminalCtor({ allowProposedApi: true, cols: 80, rows: 30 }); linkManager = instantiationService.createInstance(TestLinkManager, xterm, upcastPartial({ get initialCwd() { return ''; diff --git a/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkOpeners.test.ts b/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkOpeners.test.ts index c038ba95ce8..34a2c7250b1 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkOpeners.test.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkOpeners.test.ts @@ -22,10 +22,11 @@ import { TerminalCapabilityStore } from 'vs/platform/terminal/common/capabilitie import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { TestContextService } from 'vs/workbench/test/common/workbenchTestServices'; -import { Terminal } from 'xterm'; +import type { Terminal } from 'xterm'; import { IFileQuery, ISearchComplete, ISearchService } from 'vs/workbench/services/search/common/search'; import { SearchService } from 'vs/workbench/services/search/common/searchService'; import { ITerminalLogService, ITerminalOutputMatcher } from 'vs/platform/terminal/common/terminal'; +import { importAMDNodeModule } from 'vs/amdX'; interface ITerminalLinkActivationResult { source: 'editor' | 'search'; @@ -75,7 +76,7 @@ suite('Workbench - TerminalLinkOpeners', () => { let activationResult: ITerminalLinkActivationResult | undefined; let xterm: Terminal; - setup(() => { + setup(async () => { instantiationService = new TestInstantiationService(); fileService = new TestFileService(new NullLogService()); searchService = new TestSearchService(null!, null!, null!, null!, null!, null!, null!); @@ -108,7 +109,8 @@ suite('Workbench - TerminalLinkOpeners', () => { } } } as Partial); - xterm = new Terminal({ allowProposedApi: true }); + const TerminalCtor = (await importAMDNodeModule('xterm', 'lib/xterm.js')).Terminal; + xterm = new TerminalCtor({ allowProposedApi: true }); }); suite('TerminalSearchLinkOpener', () => { diff --git a/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLocalLinkDetector.test.ts b/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLocalLinkDetector.test.ts index 42ef45dc835..b29d05beaf0 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLocalLinkDetector.test.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLocalLinkDetector.test.ts @@ -12,7 +12,7 @@ import { TerminalBuiltinLinkType } from 'vs/workbench/contrib/terminalContrib/li import { TerminalLocalLinkDetector } from 'vs/workbench/contrib/terminalContrib/links/browser/terminalLocalLinkDetector'; import { TerminalCapabilityStore } from 'vs/platform/terminal/common/capabilities/terminalCapabilityStore'; import { assertLinkHelper } from 'vs/workbench/contrib/terminalContrib/links/test/browser/linkTestUtils'; -import { Terminal } from 'xterm'; +import type { Terminal } from 'xterm'; import { timeout } from 'vs/base/common/async'; import { strictEqual } from 'assert'; import { TerminalLinkResolver } from 'vs/workbench/contrib/terminalContrib/links/browser/terminalLinkResolver'; @@ -21,6 +21,7 @@ import { createFileStat } from 'vs/workbench/test/common/workbenchTestServices'; import { URI } from 'vs/base/common/uri'; import { NullLogService } from 'vs/platform/log/common/log'; import { ITerminalLogService } from 'vs/platform/terminal/common/terminal'; +import { importAMDNodeModule } from 'vs/amdX'; const unixLinks: (string | { link: string; resource: URI })[] = [ // Absolute @@ -171,7 +172,7 @@ suite('Workbench - TerminalLocalLinkDetector', () => { await assertLinks(TerminalBuiltinLinkType.LocalFile, `[${link}]`, [{ uri, range: [[2, 1], [link.length + 1, 1]] }]); } - setup(() => { + setup(async () => { instantiationService = new TestInstantiationService(); configurationService = new TestConfigurationService(); instantiationService.stub(IConfigurationService, configurationService); @@ -187,7 +188,8 @@ suite('Workbench - TerminalLocalLinkDetector', () => { resolver = instantiationService.createInstance(TerminalLinkResolver); validResources = []; - xterm = new Terminal({ allowProposedApi: true, cols: 80, rows: 30 }); + const TerminalCtor = (await importAMDNodeModule('xterm', 'lib/xterm.js')).Terminal; + xterm = new TerminalCtor({ allowProposedApi: true, cols: 80, rows: 30 }); }); suite('platform independent', () => { diff --git a/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalMultiLineLinkDetector.test.ts b/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalMultiLineLinkDetector.test.ts index 6f28ff2e885..0e7e686dc35 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalMultiLineLinkDetector.test.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalMultiLineLinkDetector.test.ts @@ -10,7 +10,7 @@ import { TestConfigurationService } from 'vs/platform/configuration/test/common/ import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { TerminalBuiltinLinkType } from 'vs/workbench/contrib/terminalContrib/links/browser/links'; import { assertLinkHelper } from 'vs/workbench/contrib/terminalContrib/links/test/browser/linkTestUtils'; -import { Terminal } from 'xterm'; +import type { Terminal } from 'xterm'; import { timeout } from 'vs/base/common/async'; import { strictEqual } from 'assert'; import { TerminalLinkResolver } from 'vs/workbench/contrib/terminalContrib/links/browser/terminalLinkResolver'; @@ -20,6 +20,7 @@ import { URI } from 'vs/base/common/uri'; import { NullLogService } from 'vs/platform/log/common/log'; import { ITerminalLogService } from 'vs/platform/terminal/common/terminal'; import { TerminalMultiLineLinkDetector } from 'vs/workbench/contrib/terminalContrib/links/browser/terminalMultiLineLinkDetector'; +import { importAMDNodeModule } from 'vs/amdX'; const unixLinks: (string | { link: string; resource: URI })[] = [ // Absolute @@ -130,7 +131,7 @@ suite('Workbench - TerminalMultiLineLinkDetector', () => { await assertLinks(TerminalBuiltinLinkType.LocalFile, link, [{ uri, range: [[1, lineCount], [lastLine.length, lineCount]] }]); } - setup(() => { + setup(async () => { instantiationService = new TestInstantiationService(); configurationService = new TestConfigurationService(); instantiationService.stub(IConfigurationService, configurationService); @@ -146,7 +147,8 @@ suite('Workbench - TerminalMultiLineLinkDetector', () => { resolver = instantiationService.createInstance(TerminalLinkResolver); validResources = []; - xterm = new Terminal({ allowProposedApi: true, cols: 80, rows: 30 }); + const TerminalCtor = (await importAMDNodeModule('xterm', 'lib/xterm.js')).Terminal; + xterm = new TerminalCtor({ allowProposedApi: true, cols: 80, rows: 30 }); }); suite('macOS/Linux', () => { diff --git a/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalUriLinkDetector.test.ts b/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalUriLinkDetector.test.ts index 7ef218cd43f..c1480524b43 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalUriLinkDetector.test.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalUriLinkDetector.test.ts @@ -13,8 +13,9 @@ import { TerminalUriLinkDetector } from 'vs/workbench/contrib/terminalContrib/li import { assertLinkHelper } from 'vs/workbench/contrib/terminalContrib/links/test/browser/linkTestUtils'; import { createFileStat } from 'vs/workbench/test/common/workbenchTestServices'; import { URI } from 'vs/base/common/uri'; -import { Terminal } from 'xterm'; +import type { Terminal } from 'xterm'; import { OperatingSystem } from 'vs/base/common/platform'; +import { importAMDNodeModule } from 'vs/amdX'; suite('Workbench - TerminalUriLinkDetector', () => { let configurationService: TestConfigurationService; @@ -22,7 +23,7 @@ suite('Workbench - TerminalUriLinkDetector', () => { let xterm: Terminal; let validResources: URI[] = []; - setup(() => { + setup(async () => { const instantiationService = new TestInstantiationService(); configurationService = new TestConfigurationService(); instantiationService.stub(IConfigurationService, configurationService); @@ -36,7 +37,8 @@ suite('Workbench - TerminalUriLinkDetector', () => { }); validResources = []; - xterm = new Terminal({ allowProposedApi: true, cols: 80, rows: 30 }); + const TerminalCtor = (await importAMDNodeModule('xterm', 'lib/xterm.js')).Terminal; + xterm = new TerminalCtor({ allowProposedApi: true, cols: 80, rows: 30 }); detector = instantiationService.createInstance(TerminalUriLinkDetector, xterm, { initialCwd: '/parent/cwd', os: OperatingSystem.Linux, diff --git a/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalWordLinkDetector.test.ts b/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalWordLinkDetector.test.ts index 6822f9d1bd1..1a103f07364 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalWordLinkDetector.test.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalWordLinkDetector.test.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 { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; @@ -11,7 +12,7 @@ import { ITerminalSimpleLink, TerminalBuiltinLinkType } from 'vs/workbench/contr import { TerminalWordLinkDetector } from 'vs/workbench/contrib/terminalContrib/links/browser/terminalWordLinkDetector'; import { assertLinkHelper } from 'vs/workbench/contrib/terminalContrib/links/test/browser/linkTestUtils'; import { TestProductService } from 'vs/workbench/test/common/workbenchTestServices'; -import { Terminal } from 'xterm'; +import type { Terminal } from 'xterm'; suite('Workbench - TerminalWordLinkDetector', () => { let configurationService: TestConfigurationService; @@ -26,7 +27,8 @@ suite('Workbench - TerminalWordLinkDetector', () => { instantiationService.stub(IConfigurationService, configurationService); instantiationService.set(IProductService, TestProductService); - xterm = new Terminal({ allowProposedApi: true, cols: 80, rows: 30 }); + const TerminalCtor = (await importAMDNodeModule('xterm', 'lib/xterm.js')).Terminal; + xterm = new TerminalCtor({ allowProposedApi: true, cols: 80, rows: 30 }); detector = instantiationService.createInstance(TerminalWordLinkDetector, xterm); }); diff --git a/src/vs/workbench/contrib/terminalContrib/quickFix/browser/quickFixAddon.ts b/src/vs/workbench/contrib/terminalContrib/quickFix/browser/quickFixAddon.ts index 540899f68f2..a5cd7f93a06 100644 --- a/src/vs/workbench/contrib/terminalContrib/quickFix/browser/quickFixAddon.ts +++ b/src/vs/workbench/contrib/terminalContrib/quickFix/browser/quickFixAddon.ts @@ -16,7 +16,7 @@ import { localize } from 'vs/nls'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { DecorationSelector, updateLayout } from 'vs/workbench/contrib/terminal/browser/xterm/decorationStyles'; -import { IDecoration, Terminal } from 'xterm'; +import type { IDecoration, Terminal } from 'xterm'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; diff --git a/src/vs/workbench/contrib/terminalContrib/quickFix/browser/terminal.quickFix.contribution.ts b/src/vs/workbench/contrib/terminalContrib/quickFix/browser/terminal.quickFix.contribution.ts index 205b2c5c7c2..f910bb1b7c3 100644 --- a/src/vs/workbench/contrib/terminalContrib/quickFix/browser/terminal.quickFix.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/quickFix/browser/terminal.quickFix.contribution.ts @@ -19,7 +19,7 @@ import { ITerminalQuickFixService } from 'vs/workbench/contrib/terminalContrib/q import { TerminalQuickFixAddon } from 'vs/workbench/contrib/terminalContrib/quickFix/browser/quickFixAddon'; import { freePort, gitCreatePr, gitPushSetUpstream, gitSimilar, gitTwoDashes, pwshGeneralError, pwshUnixCommandNotFoundError } from 'vs/workbench/contrib/terminalContrib/quickFix/browser/terminalQuickFixBuiltinActions'; import { TerminalQuickFixService } from 'vs/workbench/contrib/terminalContrib/quickFix/browser/terminalQuickFixService'; -import { Terminal as RawXtermTerminal } from 'xterm'; +import type { Terminal as RawXtermTerminal } from 'xterm'; // Services registerSingleton(ITerminalQuickFixService, TerminalQuickFixService, InstantiationType.Delayed); diff --git a/src/vs/workbench/contrib/terminalContrib/quickFix/test/browser/quickFixAddon.test.ts b/src/vs/workbench/contrib/terminalContrib/quickFix/test/browser/quickFixAddon.test.ts index 75c00ed81bf..dd20b4543da 100644 --- a/src/vs/workbench/contrib/terminalContrib/quickFix/test/browser/quickFixAddon.test.ts +++ b/src/vs/workbench/contrib/terminalContrib/quickFix/test/browser/quickFixAddon.test.ts @@ -19,7 +19,7 @@ import { ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/termina import { gitSimilar, freePort, FreePortOutputRegex, gitCreatePr, GitCreatePrOutputRegex, GitPushOutputRegex, gitPushSetUpstream, GitSimilarOutputRegex, gitTwoDashes, GitTwoDashesRegex, pwshUnixCommandNotFoundError, PwshUnixCommandNotFoundErrorOutputRegex, pwshGeneralError, PwshGeneralErrorOutputRegex } from 'vs/workbench/contrib/terminalContrib/quickFix/browser/terminalQuickFixBuiltinActions'; import { TerminalQuickFixAddon, getQuickFixesForCommand } from 'vs/workbench/contrib/terminalContrib/quickFix/browser/quickFixAddon'; import { URI } from 'vs/base/common/uri'; -import { Terminal } from 'xterm'; +import type { Terminal } from 'xterm'; import { Emitter } from 'vs/base/common/event'; import { LabelService } from 'vs/workbench/services/label/common/labelService'; import { ILabelService } from 'vs/platform/label/common/label'; diff --git a/src/vs/workbench/contrib/terminalContrib/typeAhead/browser/terminal.typeAhead.contribution.ts b/src/vs/workbench/contrib/terminalContrib/typeAhead/browser/terminal.typeAhead.contribution.ts index 77c3d4605d4..993edae783c 100644 --- a/src/vs/workbench/contrib/terminalContrib/typeAhead/browser/terminal.typeAhead.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/typeAhead/browser/terminal.typeAhead.contribution.ts @@ -12,7 +12,7 @@ import { registerTerminalContribution } from 'vs/workbench/contrib/terminal/brow import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/widgets/widgetManager'; import { TypeAheadAddon } from 'vs/workbench/contrib/terminalContrib/typeAhead/browser/terminalTypeAheadAddon'; import { ITerminalConfiguration, ITerminalProcessManager, TERMINAL_CONFIG_SECTION } from 'vs/workbench/contrib/terminal/common/terminal'; -import { Terminal as RawXtermTerminal } from 'xterm'; +import type { Terminal as RawXtermTerminal } from 'xterm'; class TerminalTypeAheadContribution extends DisposableStore implements ITerminalContribution { static readonly ID = 'terminal.typeAhead'; diff --git a/src/vs/workbench/contrib/terminalContrib/typeAhead/test/browser/terminalTypeAhead.test.ts b/src/vs/workbench/contrib/terminalContrib/typeAhead/test/browser/terminalTypeAhead.test.ts index 485ac6b174e..12fdba7c7d3 100644 --- a/src/vs/workbench/contrib/terminalContrib/typeAhead/test/browser/terminalTypeAhead.test.ts +++ b/src/vs/workbench/contrib/terminalContrib/typeAhead/test/browser/terminalTypeAhead.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { IBuffer, Terminal } from 'xterm'; +import type { IBuffer, Terminal } from 'xterm'; import { SinonStub, stub, useFakeTimers } from 'sinon'; import { Emitter } from 'vs/base/common/event'; import { CharPredictState, IPrediction, PredictionStats, TypeAheadAddon } from 'vs/workbench/contrib/terminalContrib/typeAhead/browser/terminalTypeAheadAddon'; From 21d269efec87c0805e61a5fcf3fe21d66218d947 Mon Sep 17 00:00:00 2001 From: Johannes Date: Tue, 4 Jul 2023 11:58:51 +0200 Subject: [PATCH 140/926] type-imports and importAMDNodeModule for telemetry --- src/vs/platform/telemetry/common/1dsAppender.ts | 5 +++-- src/vs/platform/telemetry/test/browser/1dsAppender.test.ts | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/vs/platform/telemetry/common/1dsAppender.ts b/src/vs/platform/telemetry/common/1dsAppender.ts index 5fe46b464b1..ccfbc3f7d9b 100644 --- a/src/vs/platform/telemetry/common/1dsAppender.ts +++ b/src/vs/platform/telemetry/common/1dsAppender.ts @@ -5,6 +5,7 @@ import type { IExtendedConfiguration, IExtendedTelemetryItem, ITelemetryItem, ITelemetryUnloadState } from '@microsoft/1ds-core-js'; import type { IChannelConfiguration, IXHROverride, PostChannel } from '@microsoft/1ds-post-js'; +import { importAMDNodeModule } from 'vs/amdX'; import { onUnexpectedError } from 'vs/base/common/errors'; import { mixin } from 'vs/base/common/objects'; import { ITelemetryAppender, validateTelemetryData } from 'vs/platform/telemetry/common/telemetryUtils'; @@ -21,8 +22,8 @@ const endpointUrl = 'https://mobile.events.data.microsoft.com/OneCollector/1.0'; const endpointHealthUrl = 'https://mobile.events.data.microsoft.com/ping'; async function getClient(instrumentationKey: string, addInternalFlag?: boolean, xhrOverride?: IXHROverride): Promise { - const oneDs = await import('@microsoft/1ds-core-js'); - const postPlugin = await import('@microsoft/1ds-post-js'); + const oneDs = await importAMDNodeModule('@microsoft/1ds-core-js', 'dist/ms.core.js'); + const postPlugin = await importAMDNodeModule('@microsoft/1ds-post-js', 'dist/ms.post.js'); const appInsightsCore = new oneDs.AppInsightsCore(); const collectorChannelPlugin: PostChannel = new postPlugin.PostChannel(); // Configure the app insights core to send to collector++ and disable logging of debug info diff --git a/src/vs/platform/telemetry/test/browser/1dsAppender.test.ts b/src/vs/platform/telemetry/test/browser/1dsAppender.test.ts index d0593425661..8c16031dede 100644 --- a/src/vs/platform/telemetry/test/browser/1dsAppender.test.ts +++ b/src/vs/platform/telemetry/test/browser/1dsAppender.test.ts @@ -2,7 +2,7 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ITelemetryItem, ITelemetryUnloadState } from '@microsoft/1ds-core-js'; +import type { ITelemetryItem, ITelemetryUnloadState } from '@microsoft/1ds-core-js'; import * as assert from 'assert'; import { OneDataSystemWebAppender } from 'vs/platform/telemetry/browser/1dsAppender'; import { IAppInsightsCore } from 'vs/platform/telemetry/common/1dsAppender'; From dc95a83da92d330ce0ee80e0303f87425d52bacd Mon Sep 17 00:00:00 2001 From: Johannes Date: Tue, 4 Jul 2023 12:02:13 +0200 Subject: [PATCH 141/926] type-imports and importAMDNodeModule for xterm/terminal --- .../accessibility/test/browser/bufferContentTracker.test.ts | 6 ++++-- .../quickFix/test/browser/quickFixAddon.test.ts | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/test/browser/bufferContentTracker.test.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/test/browser/bufferContentTracker.test.ts index 98df5b42f9b..cafcc7cd6c5 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/test/browser/bufferContentTracker.test.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/test/browser/bufferContentTracker.test.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; +import { importAMDNodeModule } from 'vs/amdX'; import { isWindows } from 'vs/base/common/platform'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; @@ -49,7 +50,7 @@ suite('Buffer Content Tracker', () => { let bufferTracker: BufferContentTracker; const prompt = 'vscode-git:(prompt/more-tests)'; const promptPlusData = 'vscode-git:(prompt/more-tests) ' + 'some data'; - setup(() => { + setup(async () => { configurationService = new TestConfigurationService({ terminal: { integrated: defaultTerminalConfig } }); instantiationService = new TestInstantiationService(); themeService = new TestThemeService(); @@ -66,7 +67,8 @@ suite('Buffer Content Tracker', () => { if (!isWindows) { capabilities.add(TerminalCapability.NaiveCwdDetection, null!); } - xterm = instantiationService.createInstance(XtermTerminal, Terminal, configHelper, 80, 30, { getBackgroundColor: () => undefined }, capabilities, '', new MockContextKeyService().createKey('', true)!, true); + const TerminalCtor = (await importAMDNodeModule('xterm', 'lib/xterm.js')).Terminal; + xterm = instantiationService.createInstance(XtermTerminal, TerminalCtor, configHelper, 80, 30, { getBackgroundColor: () => undefined }, capabilities, '', new MockContextKeyService().createKey('', true)!, true); const container = document.createElement('div'); xterm.raw.open(container); configurationService = new TestConfigurationService({ terminal: { integrated: { tabs: { separator: ' - ', title: '${cwd}', description: '${cwd}' } } } }); diff --git a/src/vs/workbench/contrib/terminalContrib/quickFix/test/browser/quickFixAddon.test.ts b/src/vs/workbench/contrib/terminalContrib/quickFix/test/browser/quickFixAddon.test.ts index dd20b4543da..b7f5b729fdf 100644 --- a/src/vs/workbench/contrib/terminalContrib/quickFix/test/browser/quickFixAddon.test.ts +++ b/src/vs/workbench/contrib/terminalContrib/quickFix/test/browser/quickFixAddon.test.ts @@ -29,6 +29,7 @@ import { IStorageService } from 'vs/platform/storage/common/storage'; import { TestStorageService } from 'vs/workbench/test/common/workbenchTestServices'; import { ITerminalQuickFixService } from 'vs/workbench/contrib/terminalContrib/quickFix/browser/quickFix'; import { ITerminalOutputMatcher } from 'vs/platform/terminal/common/terminal'; +import { importAMDNodeModule } from 'vs/amdX'; suite('QuickFixAddon', () => { let quickFixAddon: TerminalQuickFixAddon; @@ -37,9 +38,10 @@ suite('QuickFixAddon', () => { let openerService: OpenerService; let labelService: LabelService; let terminal: Terminal; - setup(() => { + setup(async () => { const instantiationService = new TestInstantiationService(); - terminal = new Terminal({ + const TerminalCtor = (await importAMDNodeModule('xterm', 'lib/xterm.js')).Terminal; + terminal = new TerminalCtor({ allowProposedApi: true, cols: 80, rows: 30 From 37b304f2f39b346014487c8f0a84ec12991839bd Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Tue, 4 Jul 2023 03:04:07 -0700 Subject: [PATCH 142/926] testing: refactor to reduce explorer complexity (#186954) Previously the two 'projections' in the test explorer, a list and tree view, inherited from each other. This was a pretty messy way to do it. There were other things with how we dealt with the tree that weren't optimal, and some debt from concepts that no longer exist. In this PR the now better-named ListProject and TreeProjection are mostly indepenent from each other. This reduces the complexity in the TestProjection and makes the ListProjection pretty simple. --- .../explorerProjections/hierarchalByName.ts | 145 ---------- .../explorerProjections/hierarchalNodes.ts | 41 --- .../browser/explorerProjections/index.ts | 136 ++++----- .../explorerProjections/listProjection.ts | 259 ++++++++++++++++++ .../browser/explorerProjections/nodeHelper.ts | 146 ---------- ...rarchalByLocation.ts => treeProjection.ts} | 186 +++++++------ .../testing/browser/testExplorerActions.ts | 48 +--- .../testing/browser/testingExplorerView.ts | 79 +++--- .../contrib/testing/common/testId.ts | 7 + .../hierarchalByLocation.test.ts | 4 +- .../hierarchalByName.test.ts | 6 +- .../testing/test/browser/testObjectTree.ts | 4 +- 12 files changed, 491 insertions(+), 570 deletions(-) delete mode 100644 src/vs/workbench/contrib/testing/browser/explorerProjections/hierarchalByName.ts delete mode 100644 src/vs/workbench/contrib/testing/browser/explorerProjections/hierarchalNodes.ts create mode 100644 src/vs/workbench/contrib/testing/browser/explorerProjections/listProjection.ts delete mode 100644 src/vs/workbench/contrib/testing/browser/explorerProjections/nodeHelper.ts rename src/vs/workbench/contrib/testing/browser/explorerProjections/{hierarchalByLocation.ts => treeProjection.ts} (61%) diff --git a/src/vs/workbench/contrib/testing/browser/explorerProjections/hierarchalByName.ts b/src/vs/workbench/contrib/testing/browser/explorerProjections/hierarchalByName.ts deleted file mode 100644 index 4ed11eb9e5d..00000000000 --- a/src/vs/workbench/contrib/testing/browser/explorerProjections/hierarchalByName.ts +++ /dev/null @@ -1,145 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { flatTestItemDelimiter } from 'vs/workbench/contrib/testing/browser/explorerProjections/display'; -import { HierarchicalByLocationProjection } from 'vs/workbench/contrib/testing/browser/explorerProjections/hierarchalByLocation'; -import { ByLocationTestItemElement } from 'vs/workbench/contrib/testing/browser/explorerProjections/hierarchalNodes'; -import { TestExplorerTreeElement } from 'vs/workbench/contrib/testing/browser/explorerProjections/index'; -import { NodeRenderDirective } from 'vs/workbench/contrib/testing/browser/explorerProjections/nodeHelper'; -import { ISerializedTestTreeCollapseState } from 'vs/workbench/contrib/testing/browser/explorerProjections/testingViewState'; -import { TestId } from 'vs/workbench/contrib/testing/common/testId'; -import { ITestResultService } from 'vs/workbench/contrib/testing/common/testResultService'; -import { ITestService } from 'vs/workbench/contrib/testing/common/testService'; -import { InternalTestItem } from 'vs/workbench/contrib/testing/common/testTypes'; - -/** - * Type of test element in the list. - */ -const enum ListElementType { - /** The element is a leaf test that should be shown in the list */ - Leaf, - /** The element is not runnable, but doesn't have any nested leaf tests */ - Branch, -} - -/** - * Version of the HierarchicalElement that is displayed as a list. - */ -export class ByNameTestItemElement extends ByLocationTestItemElement { - public elementType: ListElementType = ListElementType.Leaf; - public readonly isTestRoot = !this.actualParent; - public readonly actualChildren = new Set(); - - public override get description() { - let description: string | null = null; - for (let parent = this.actualParent; parent && !parent.isTestRoot; parent = parent.actualParent) { - description = description ? parent.label + flatTestItemDelimiter + description : parent.label; - } - - return description; - } - - /** - * @param actualParent Parent of the item in the test heirarchy - */ - constructor( - internal: InternalTestItem, - parentItem: null | ByLocationTestItemElement, - addedOrRemoved: (n: TestExplorerTreeElement) => void, - public readonly actualParent?: ByNameTestItemElement, - ) { - super(internal, parentItem, addedOrRemoved); - actualParent?.addChild(this); - } - - /** - * Should be called when the list element is removed. - */ - public remove() { - this.actualParent?.removeChild(this); - } - - private removeChild(element: ByNameTestItemElement) { - this.actualChildren.delete(element); - } - - private addChild(element: ByNameTestItemElement) { - this.actualChildren.add(element); - } -} - -/** - * Projection that shows tests in a flat list (grouped by provider). The only - * change is that, while creating the item, the item parent is set to the - * test root rather than the heirarchal parent. - */ -export class HierarchicalByNameProjection extends HierarchicalByLocationProjection { - constructor(lastState: ISerializedTestTreeCollapseState, @ITestService testService: ITestService, @ITestResultService results: ITestResultService) { - super(lastState, testService, results); - - const originalRenderNode = this.renderNode.bind(this); - this.renderNode = (node, recurse) => { - if (node instanceof ByNameTestItemElement && node.elementType !== ListElementType.Leaf && !node.isTestRoot) { - return NodeRenderDirective.Concat; - } - - const rendered = originalRenderNode(node, recurse); - if (typeof rendered !== 'number') { - (rendered as any).collapsible = false; - } - - return rendered; - }; - } - - /** - * @override - */ - protected override createItem(item: InternalTestItem): ByLocationTestItemElement { - const parentId = TestId.parentId(item.item.extId); - const actualParent = parentId ? this.items.get(parentId.toString()) as ByNameTestItemElement : undefined; - if (!actualParent) { - return new ByNameTestItemElement(item, null, r => this.changes.addedOrRemoved(r)); - } - - if (actualParent.elementType === ListElementType.Leaf) { - actualParent.elementType = ListElementType.Branch; - this.changes.addedOrRemoved(actualParent); - } - - return new ByNameTestItemElement( - item, - actualParent.parent as ByNameTestItemElement || actualParent, - r => this.changes.addedOrRemoved(r), - actualParent, - ); - } - - /** - * @override - */ - protected override unstoreItem(items: Map, item: ByLocationTestItemElement) { - const treeChildren = super.unstoreItem(items, item); - - if (item instanceof ByNameTestItemElement) { - if (item.actualParent && item.actualParent.actualChildren.size === 1) { - item.actualParent.elementType = ListElementType.Leaf; - this.changes.addedOrRemoved(item.actualParent); - } - - item.remove(); - return item.actualChildren; - } - - return treeChildren; - } - - /** - * @override - */ - protected override getRevealDepth(element: ByLocationTestItemElement) { - return element.depth === 0 ? Infinity : undefined; - } -} diff --git a/src/vs/workbench/contrib/testing/browser/explorerProjections/hierarchalNodes.ts b/src/vs/workbench/contrib/testing/browser/explorerProjections/hierarchalNodes.ts deleted file mode 100644 index ba97b188d41..00000000000 --- a/src/vs/workbench/contrib/testing/browser/explorerProjections/hierarchalNodes.ts +++ /dev/null @@ -1,41 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { TestExplorerTreeElement, TestItemTreeElement, TestTreeErrorMessage } from 'vs/workbench/contrib/testing/browser/explorerProjections/index'; -import { applyTestItemUpdate, InternalTestItem, ITestItemUpdate } from 'vs/workbench/contrib/testing/common/testTypes'; - -/** - * Test tree element element that groups be hierarchy. - */ -export class ByLocationTestItemElement extends TestItemTreeElement { - private errorChild?: TestTreeErrorMessage; - - constructor( - test: InternalTestItem, - parent: null | ByLocationTestItemElement, - protected readonly addedOrRemoved: (n: TestExplorerTreeElement) => void, - ) { - super({ ...test, item: { ...test.item } }, parent); - this.updateErrorVisibility(); - } - - public update(patch: ITestItemUpdate) { - applyTestItemUpdate(this.test, patch); - this.updateErrorVisibility(patch); - } - - private updateErrorVisibility(patch?: ITestItemUpdate) { - if (this.errorChild && (!this.test.item.error || patch?.item?.error)) { - this.addedOrRemoved(this.errorChild); - this.children.delete(this.errorChild); - this.errorChild = undefined; - } - if (this.test.item.error && !this.errorChild) { - this.errorChild = new TestTreeErrorMessage(this.test.item.error, this); - this.children.add(this.errorChild); - this.addedOrRemoved(this.errorChild); - } - } -} diff --git a/src/vs/workbench/contrib/testing/browser/explorerProjections/index.ts b/src/vs/workbench/contrib/testing/browser/explorerProjections/index.ts index 032d28e9401..7d14aa64730 100644 --- a/src/vs/workbench/contrib/testing/browser/explorerProjections/index.ts +++ b/src/vs/workbench/contrib/testing/browser/explorerProjections/index.ts @@ -3,15 +3,17 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { IIdentityProvider } from 'vs/base/browser/ui/list/list'; import { ObjectTree } from 'vs/base/browser/ui/tree/objectTree'; -import { Event } from 'vs/base/common/event'; +import { IObjectTreeElement, ObjectTreeElementCollapseState } from 'vs/base/browser/ui/tree/tree'; +import { Emitter, Event } from 'vs/base/common/event'; import { FuzzyScore } from 'vs/base/common/filters'; import { IMarkdownString } from 'vs/base/common/htmlContent'; import { Iterable } from 'vs/base/common/iterator'; import { IDisposable } from 'vs/base/common/lifecycle'; import { MarshalledId } from 'vs/base/common/marshallingIds'; -import { ISerializedTestTreeCollapseState } from 'vs/workbench/contrib/testing/browser/explorerProjections/testingViewState'; -import { InternalTestItem, ITestItemContext, TestResultState } from 'vs/workbench/contrib/testing/common/testTypes'; +import { ISerializedTestTreeCollapseState, isCollapsedInSerializedTestTree } from 'vs/workbench/contrib/testing/browser/explorerProjections/testingViewState'; +import { ITestItemContext, InternalTestItem, TestItemExpandState, TestResultState } from 'vs/workbench/contrib/testing/common/testTypes'; /** * Describes a rendering of tests in the explorer view. Different @@ -47,118 +49,60 @@ export interface ITestTreeProjection extends IDisposable { applyTo(tree: ObjectTree): void; } -/** - * Interface describing the workspace folder and test item tree elements. - */ -export interface IActionableTestTreeElement { - /** - * Parent tree item. - */ - parent: IActionableTestTreeElement | null; - - /** - * Unique ID of the element in the tree. - */ - treeId: string; - - /** - * Test children of this item. - */ - children: Set; - - /** - * Depth of the element in the tree. - */ - depth: number; - - /** - * Iterable of the tests this element contains. - */ - tests: Iterable; - - /** - * State to show on the item. This is generally the item's computed state - * from its children. - */ - state: TestResultState; - - /** - * Time it took this test/item to run. - */ - duration: number | undefined; - - /** - * Label for the item. - */ - label: string; -} - let idCounter = 0; const getId = () => String(idCounter++); -export class TestItemTreeElement implements IActionableTestTreeElement { +export abstract class TestItemTreeElement { + protected readonly changeEmitter = new Emitter(); + /** - * @inheritdoc + * Fired whenever the element or test properties change. + */ + public readonly onChange = this.changeEmitter.event; + + /** + * Tree children of this item. */ public readonly children = new Set(); /** - * @inheritdoc + * Unique ID of the element in the tree. */ public readonly treeId = getId(); /** - * @inheritdoc + * Depth of the element in the tree. */ public depth: number = this.parent ? this.parent.depth + 1 : 0; - public get tests() { - return Iterable.single(this.test); - } - - public get description() { - return this.test.item.description; - } - - public get sortText() { - return this.test.item.sortText; - } - /** * Whether the node's test result is 'retired' -- from an outdated test run. */ public retired = false; /** - * @inheritdoc + * State to show on the item. This is generally the item's computed state + * from its children. */ public state = TestResultState.Unset; - /** - * Own, non-computed state. - */ - public ownState = TestResultState.Unset; - - /** - * Own, non-computed duration. - */ - public ownDuration: number | undefined; - /** * Time it took this test/item to run. */ public duration: number | undefined; /** - * @inheritdoc + * Tree element description. */ - public get label() { - return this.test.item.label; - } + public abstract description: string | null; constructor( public readonly test: InternalTestItem, + /** + * Parent tree item. May not actually be the test item who owns this one + * in a 'flat' projection. + */ public readonly parent: TestItemTreeElement | null = null, ) { } @@ -195,3 +139,35 @@ export class TestTreeErrorMessage { } export type TestExplorerTreeElement = TestItemTreeElement | TestTreeErrorMessage; + +export const testIdentityProvider: IIdentityProvider = { + getId(element) { + return element.treeId + '\0' + (element instanceof TestTreeErrorMessage ? 'error' : element.test.expand); + } +}; + +export const getChildrenForParent = (serialized: ISerializedTestTreeCollapseState, rootsWithChildren: Iterable, node: TestExplorerTreeElement | null): Iterable> => { + let it: Iterable; + if (node === null) { // roots + const rootsWithChildrenArr = [...rootsWithChildren]; + if (rootsWithChildrenArr.length === 1) { + return getChildrenForParent(serialized, rootsWithChildrenArr, rootsWithChildrenArr[0]); + } + it = rootsWithChildrenArr; + } else { + it = node.children; + } + + return Iterable.map(it, element => ( + element instanceof TestTreeErrorMessage + ? { element } + : { + element, + collapsible: element.test.expand !== TestItemExpandState.NotExpandable, + collapsed: isCollapsedInSerializedTestTree(serialized, element.test.item.extId) ?? element.depth > 0 + ? ObjectTreeElementCollapseState.PreserveOrCollapsed + : ObjectTreeElementCollapseState.PreserveOrExpanded, + children: getChildrenForParent(serialized, rootsWithChildren, element), + } + )); +}; diff --git a/src/vs/workbench/contrib/testing/browser/explorerProjections/listProjection.ts b/src/vs/workbench/contrib/testing/browser/explorerProjections/listProjection.ts new file mode 100644 index 00000000000..c2dc436100a --- /dev/null +++ b/src/vs/workbench/contrib/testing/browser/explorerProjections/listProjection.ts @@ -0,0 +1,259 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ObjectTree } from 'vs/base/browser/ui/tree/objectTree'; +import { Emitter } from 'vs/base/common/event'; +import { FuzzyScore } from 'vs/base/common/filters'; +import { Iterable } from 'vs/base/common/iterator'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { flatTestItemDelimiter } from 'vs/workbench/contrib/testing/browser/explorerProjections/display'; +import { ITestTreeProjection, TestExplorerTreeElement, TestItemTreeElement, TestTreeErrorMessage, getChildrenForParent, testIdentityProvider } from 'vs/workbench/contrib/testing/browser/explorerProjections/index'; +import { ISerializedTestTreeCollapseState, isCollapsedInSerializedTestTree } from 'vs/workbench/contrib/testing/browser/explorerProjections/testingViewState'; +import { TestId } from 'vs/workbench/contrib/testing/common/testId'; +import { ITestResultService } from 'vs/workbench/contrib/testing/common/testResultService'; +import { ITestService } from 'vs/workbench/contrib/testing/common/testService'; +import { ITestItemUpdate, InternalTestItem, TestDiffOpType, TestItemExpandState, TestResultState, TestsDiff, applyTestItemUpdate } from 'vs/workbench/contrib/testing/common/testTypes'; + +/** + * Test tree element element that groups be hierarchy. + */ +class ListTestItemElement extends TestItemTreeElement { + private errorChild?: TestTreeErrorMessage; + + public descriptionParts: string[] = []; + + public override get description() { + return this.chain.map(c => c.item.label).join(flatTestItemDelimiter); + } + + constructor( + test: InternalTestItem, + parent: null | ListTestItemElement, + private readonly chain: InternalTestItem[], + ) { + super({ ...test, item: { ...test.item } }, parent); + this.updateErrorVisibility(); + } + + public update(patch: ITestItemUpdate) { + applyTestItemUpdate(this.test, patch); + this.updateErrorVisibility(patch); + this.fireChange(); + } + + public fireChange() { + this.changeEmitter.fire(); + } + + private updateErrorVisibility(patch?: ITestItemUpdate) { + if (this.errorChild && (!this.test.item.error || patch?.item?.error)) { + this.children.delete(this.errorChild); + this.errorChild = undefined; + } + if (this.test.item.error && !this.errorChild) { + this.errorChild = new TestTreeErrorMessage(this.test.item.error, this); + this.children.add(this.errorChild); + } + } +} + + +/** + * Projection that lists tests in their traditional tree view. + */ +export class ListProjection extends Disposable implements ITestTreeProjection { + private readonly updateEmitter = new Emitter(); + private readonly items = new Map(); + + /** + * Gets root elements of the tree. + */ + private get rootsWithChildren(): Iterable { + const rootsIt = Iterable.map(this.testService.collection.rootItems, r => this.items.get(r.item.extId)); + return Iterable.filter(rootsIt, (r): r is ListTestItemElement => !!r?.children.size); + } + + /** + * @inheritdoc + */ + public readonly onUpdate = this.updateEmitter.event; + + constructor( + public lastState: ISerializedTestTreeCollapseState, + @ITestService private readonly testService: ITestService, + @ITestResultService private readonly results: ITestResultService, + ) { + super(); + this._register(testService.onDidProcessDiff((diff) => this.applyDiff(diff))); + + // when test results are cleared, recalculate all state + this._register(results.onResultsChanged((evt) => { + if (!('removed' in evt)) { + return; + } + + for (const inTree of this.items.values()) { + // Simple logic here, because we know in this projection states + // are never inherited. + const lookup = this.results.getStateById(inTree.test.item.extId)?.[1]; + inTree.duration = lookup?.ownDuration; + inTree.state = lookup?.ownComputedState || TestResultState.Unset; + inTree.fireChange(); + } + })); + + // when test states change, reflect in the tree + this._register(results.onTestChanged(ev => { + let result = ev.item; + // if the state is unset, or the latest run is not making the change, + // double check that it's valid. Retire calls might cause previous + // emit a state change for a test run that's already long completed. + if (result.ownComputedState === TestResultState.Unset || ev.result !== results.results[0]) { + const fallback = results.getStateById(result.item.extId); + if (fallback) { + result = fallback[1]; + } + } + + const item = this.items.get(result.item.extId); + if (!item) { + return; + } + + item.retired = !!result.retired; + item.state = result.computedState; + item.duration = result.ownDuration; + item.fireChange(); + })); + + for (const test of testService.collection.all) { + this.storeItem(test); + } + } + + /** + * @inheritdoc + */ + public getElementByTestId(testId: string): TestItemTreeElement | undefined { + return this.items.get(testId); + } + + /** + * @inheritdoc + */ + private applyDiff(diff: TestsDiff) { + for (const op of diff) { + switch (op.op) { + case TestDiffOpType.Add: { + this.storeItem(op.item); + break; + } + + case TestDiffOpType.Update: { + this.items.get(op.item.extId)?.update(op.item); + break; + } + + case TestDiffOpType.Remove: { + for (const [id, item] of this.items) { + if (id === op.itemId || TestId.isChild(op.itemId, id)) { + this.unstoreItem(item); + } + } + break; + } + } + } + + if (diff.length !== 0) { + this.updateEmitter.fire(); + } + } + + /** + * @inheritdoc + */ + public applyTo(tree: ObjectTree) { + // We don't bother doing a very specific update like we do in the TreeProjection. + // It's a flat list, so chances are we need to render everything anyway. + // Let the diffIdentityProvider handle that. + tree.setChildren(null, getChildrenForParent(this.lastState, this.rootsWithChildren, null), { + diffIdentityProvider: testIdentityProvider, + diffDepth: Infinity + }); + } + + /** + * @inheritdoc + */ + public expandElement(element: TestItemTreeElement, depth: number): void { + if (!(element instanceof ListTestItemElement)) { + return; + } + + if (element.test.expand === TestItemExpandState.NotExpandable) { + return; + } + + this.testService.collection.expand(element.test.item.extId, depth); + } + + private unstoreItem(treeElement: ListTestItemElement) { + this.items.delete(treeElement.test.item.extId); + treeElement.parent?.children.delete(treeElement); + + const parentId = TestId.fromString(treeElement.test.item.extId).parentId; + if (!parentId) { + return; + } + + // create the parent if it's now its own leaf + for (const id of parentId.idsToRoot()) { + const parentTest = this.testService.collection.getNodeById(id.toString()); + if (parentTest) { + if (parentTest.children.size === 0 && !this.items.has(id.toString())) { + this._storeItem(parentId, parentTest); + } + break; + } + } + } + + private _storeItem(testId: TestId, item: InternalTestItem) { + const displayedParent = testId.isRoot ? null : this.items.get(item.controllerId)!; + const chain = [...testId.idsFromRoot()].slice(1, -1).map(id => this.testService.collection.getNodeById(id.toString())!); + const treeElement = new ListTestItemElement(item, displayedParent, chain); + displayedParent?.children.add(treeElement); + this.items.set(treeElement.test.item.extId, treeElement); + + if (treeElement.depth === 0 || isCollapsedInSerializedTestTree(this.lastState, treeElement.test.item.extId) === false) { + this.expandElement(treeElement, Infinity); + } + + const prevState = this.results.getStateById(treeElement.test.item.extId)?.[1]; + if (prevState) { + treeElement.retired = !!prevState.retired; + treeElement.state = prevState.computedState; + treeElement.duration = prevState.ownDuration; + } + } + + private storeItem(item: InternalTestItem) { + const testId = TestId.fromString(item.item.extId); + + // Remove any non-root parent of this item which is no longer a leaf. + for (const parentId of testId.idsToRoot()) { + if (!parentId.isRoot) { + const prevParent = this.items.get(parentId.toString()); + if (prevParent) { + this.unstoreItem(prevParent); + break; + } + } + } + + this._storeItem(testId, item); + } +} diff --git a/src/vs/workbench/contrib/testing/browser/explorerProjections/nodeHelper.ts b/src/vs/workbench/contrib/testing/browser/explorerProjections/nodeHelper.ts deleted file mode 100644 index 37b79258601..00000000000 --- a/src/vs/workbench/contrib/testing/browser/explorerProjections/nodeHelper.ts +++ /dev/null @@ -1,146 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { IIdentityProvider } from 'vs/base/browser/ui/list/list'; -import { ObjectTree } from 'vs/base/browser/ui/tree/objectTree'; -import { ITreeElement } from 'vs/base/browser/ui/tree/tree'; -import { IActionableTestTreeElement, TestExplorerTreeElement, TestItemTreeElement, TestTreeErrorMessage } from 'vs/workbench/contrib/testing/browser/explorerProjections/index'; - -const testIdentityProvider: IIdentityProvider = { - getId(element) { - return element.treeId + '\0' + (element instanceof TestTreeErrorMessage ? 'error' : element.test.expand); - } -}; - -/** - * Returns whether there are any children for other nodes besides this one - * in the tree. - * - * This is used for omitting test provider nodes if there's only a single - * test provider in the workspace (the common case) - */ -export const peersHaveChildren = (node: IActionableTestTreeElement, roots: () => Iterable) => { - for (const child of node.parent ? node.parent.children : roots()) { - if (child !== node && child.children.size) { - return true; - } - } - - return false; -}; - -export const enum NodeRenderDirective { - /** Omit node and all its children */ - Omit, - /** Concat children with parent */ - Concat -} - -export type NodeRenderFn = ( - n: TestExplorerTreeElement, - recurse: (items: Iterable) => Iterable>, -) => ITreeElement | NodeRenderDirective; - -const pruneNodesNotInTree = (nodes: Set, tree: ObjectTree) => { - for (const node of nodes) { - if (node && !tree.hasElement(node)) { - nodes.delete(node); - } - } -}; - -/** - * Helper to gather and bulk-apply tree updates. - */ -export class NodeChangeList { - private changedParents = new Set(); - private updatedNodes = new Set(); - private resortedNodes = new Set(); - private omittedNodes = new WeakSet(); - private isFirstApply = true; - - public updated(node: TestExplorerTreeElement) { - this.updatedNodes.add(node); - } - - public addedOrRemoved(node: TestExplorerTreeElement) { - this.changedParents.add(this.getNearestNotOmittedParent(node)); - } - - public sortKeyUpdated(node: TestExplorerTreeElement) { - this.resortedNodes.add(node.parent); - } - - public applyTo( - tree: ObjectTree, - renderNode: NodeRenderFn, - roots: () => Iterable, - ) { - pruneNodesNotInTree(this.changedParents, tree); - pruneNodesNotInTree(this.updatedNodes, tree); - pruneNodesNotInTree(this.resortedNodes, tree); - - const diffDepth = this.isFirstApply ? Infinity : 0; - this.isFirstApply = false; - - for (let parent of this.changedParents) { - while (parent && typeof renderNode(parent, () => []) !== 'object') { - parent = parent.parent as T | null; - } - - if (parent === null || tree.hasElement(parent)) { - tree.setChildren( - parent, - this.renderNodeList(renderNode, parent === null ? roots() : parent.children), - { diffIdentityProvider: testIdentityProvider, diffDepth }, - ); - } - } - - for (const node of this.updatedNodes) { - if (tree.hasElement(node)) { - tree.rerender(node); - } - } - - for (const node of this.resortedNodes) { - if (node && tree.hasElement(node)) { - tree.resort(node, false); - } - } - - this.changedParents.clear(); - this.updatedNodes.clear(); - this.resortedNodes.clear(); - } - - private getNearestNotOmittedParent(node: TestExplorerTreeElement | null) { - let parent = node && node.parent; - while (parent && this.omittedNodes.has(parent)) { - parent = parent.parent; - } - - return parent as T; - } - - private *renderNodeList(renderNode: NodeRenderFn, nodes: Iterable): Iterable> { - for (const node of nodes) { - const rendered = renderNode(node, this.renderNodeList.bind(this, renderNode)); - if (rendered === NodeRenderDirective.Omit) { - this.omittedNodes.add(node); - } else if (rendered === NodeRenderDirective.Concat) { - this.omittedNodes.add(node); - if ('children' in node) { - for (const nested of this.renderNodeList(renderNode, node.children)) { - yield nested; - } - } - } else { - this.omittedNodes.delete(node); - yield rendered; - } - } - } -} diff --git a/src/vs/workbench/contrib/testing/browser/explorerProjections/hierarchalByLocation.ts b/src/vs/workbench/contrib/testing/browser/explorerProjections/treeProjection.ts similarity index 61% rename from src/vs/workbench/contrib/testing/browser/explorerProjections/hierarchalByLocation.ts rename to src/vs/workbench/contrib/testing/browser/explorerProjections/treeProjection.ts index f1ae17cb789..253b27d1d4d 100644 --- a/src/vs/workbench/contrib/testing/browser/explorerProjections/hierarchalByLocation.ts +++ b/src/vs/workbench/contrib/testing/browser/explorerProjections/treeProjection.ts @@ -8,19 +8,16 @@ import { Emitter } from 'vs/base/common/event'; import { FuzzyScore } from 'vs/base/common/filters'; import { Iterable } from 'vs/base/common/iterator'; import { Disposable } from 'vs/base/common/lifecycle'; -import { isDefined } from 'vs/base/common/types'; -import { ByLocationTestItemElement } from 'vs/workbench/contrib/testing/browser/explorerProjections/hierarchalNodes'; -import { IActionableTestTreeElement, ITestTreeProjection, TestExplorerTreeElement, TestItemTreeElement, TestTreeErrorMessage } from 'vs/workbench/contrib/testing/browser/explorerProjections/index'; -import { NodeChangeList, NodeRenderDirective, NodeRenderFn, peersHaveChildren } from 'vs/workbench/contrib/testing/browser/explorerProjections/nodeHelper'; +import { ITestTreeProjection, TestExplorerTreeElement, TestItemTreeElement, TestTreeErrorMessage, getChildrenForParent, testIdentityProvider } from 'vs/workbench/contrib/testing/browser/explorerProjections/index'; import { ISerializedTestTreeCollapseState, isCollapsedInSerializedTestTree } from 'vs/workbench/contrib/testing/browser/explorerProjections/testingViewState'; import { IComputedStateAndDurationAccessor, refreshComputedState } from 'vs/workbench/contrib/testing/common/getComputedState'; import { TestId } from 'vs/workbench/contrib/testing/common/testId'; import { TestResultItemChangeReason } from 'vs/workbench/contrib/testing/common/testResult'; import { ITestResultService } from 'vs/workbench/contrib/testing/common/testResultService'; import { ITestService } from 'vs/workbench/contrib/testing/common/testService'; -import { InternalTestItem, TestDiffOpType, TestItemExpandState, TestResultState, TestsDiff } from 'vs/workbench/contrib/testing/common/testTypes'; +import { ITestItemUpdate, InternalTestItem, TestDiffOpType, TestItemExpandState, TestResultState, TestsDiff, applyTestItemUpdate } from 'vs/workbench/contrib/testing/common/testTypes'; -const computedStateAccessor: IComputedStateAndDurationAccessor = { +const computedStateAccessor: IComputedStateAndDurationAccessor = { getOwnState: i => i instanceof TestItemTreeElement ? i.ownState : TestResultState.Unset, getCurrentComputedState: i => i.state, setComputedState: (i, s) => i.state = s, @@ -31,29 +28,87 @@ const computedStateAccessor: IComputedStateAndDurationAccessor Iterable.filter( i.children.values(), - (t): t is TestItemTreeElement => t instanceof TestItemTreeElement, + (t): t is TreeTestItemElement => t instanceof TreeTestItemElement, ), *getParents(i) { for (let parent = i.parent; parent; parent = parent.parent) { - yield parent; + yield parent as TreeTestItemElement; } }, }; +/** + * Test tree element element that groups be hierarchy. + */ +class TreeTestItemElement extends TestItemTreeElement { + /** + * Own, non-computed state. + * @internal + */ + public ownState = TestResultState.Unset; + + /** + * Own, non-computed duration. + * @internal + */ + public ownDuration: number | undefined; + + public override get description() { + return this.test.item.description; + } + + private errorChild?: TestTreeErrorMessage; + + constructor( + test: InternalTestItem, + parent: null | TreeTestItemElement, + protected readonly addedOrRemoved: (n: TestItemTreeElement) => void, + ) { + super({ ...test, item: { ...test.item } }, parent); + this.updateErrorVisibility(); + } + + public update(patch: ITestItemUpdate) { + applyTestItemUpdate(this.test, patch); + this.updateErrorVisibility(patch); + this.fireChange(); + } + + public fireChange() { + this.changeEmitter.fire(); + } + + private updateErrorVisibility(patch?: ITestItemUpdate) { + if (this.errorChild && (!this.test.item.error || patch?.item?.error)) { + this.addedOrRemoved(this); + this.children.delete(this.errorChild); + this.errorChild = undefined; + } + if (this.test.item.error && !this.errorChild) { + this.errorChild = new TestTreeErrorMessage(this.test.item.error, this); + this.children.add(this.errorChild); + this.addedOrRemoved(this); + } + } +} + /** * Projection that lists tests in their traditional tree view. */ -export class HierarchicalByLocationProjection extends Disposable implements ITestTreeProjection { +export class TreeProjection extends Disposable implements ITestTreeProjection { private readonly updateEmitter = new Emitter(); - protected readonly changes = new NodeChangeList(); - protected readonly items = new Map(); + + private readonly changedParents = new Set(); + private readonly resortedParents = new Set(); + + private readonly items = new Map(); /** * Gets root elements of the tree. */ - protected get roots(): Iterable { + private get rootsWithChildren(): Iterable { const rootsIt = Iterable.map(this.testService.collection.rootItems, r => this.items.get(r.item.extId)); - return Iterable.filter(rootsIt, isDefined); + return Iterable.filter(rootsIt, (r): r is TreeTestItemElement => !!r?.children.size); } /** @@ -78,10 +133,8 @@ export class HierarchicalByLocationProjection extends Disposable implements ITes for (const inTree of [...this.items.values()].sort((a, b) => b.depth - a.depth)) { const lookup = this.results.getStateById(inTree.test.item.extId)?.[1]; inTree.ownDuration = lookup?.ownDuration; - refreshComputedState(computedStateAccessor, inTree, lookup?.ownComputedState ?? TestResultState.Unset).forEach(this.addUpdated); + refreshComputedState(computedStateAccessor, inTree, lookup?.ownComputedState ?? TestResultState.Unset).forEach(i => i.fireChange()); } - - this.updateEmitter.fire(); })); // when test states change, reflect in the tree @@ -112,10 +165,9 @@ export class HierarchicalByLocationProjection extends Disposable implements ITes item.retired = !!result.retired; item.ownState = result.ownComputedState; item.ownDuration = result.ownDuration; + item.fireChange(); - refreshComputedState(computedStateAccessor, item, explicitComputed, refreshDuration).forEach(this.addUpdated); - this.addUpdated(item); - this.updateEmitter.fire(); + refreshComputedState(computedStateAccessor, item, explicitComputed, refreshDuration).forEach(i => i.fireChange()); })); for (const test of testService.collection.all) { @@ -123,13 +175,6 @@ export class HierarchicalByLocationProjection extends Disposable implements ITes } } - /** - * Gets the depth of children to expanded automatically for the node, - */ - protected getRevealDepth(element: ByLocationTestItemElement): number | undefined { - return element.depth === 0 ? 0 : undefined; - } - /** * @inheritdoc */ @@ -161,12 +206,9 @@ export class HierarchicalByLocationProjection extends Disposable implements ITes const needsParentUpdate = existing.test.expand === TestItemExpandState.NotExpandable && patch.expand; existing.update(patch); if (needsParentUpdate) { - this.changes.addedOrRemoved(existing); + this.changedParents.add(existing.parent); } else { - this.changes.updated(existing); - } - if (patch.item?.sortText || (patch.item?.label && !existing.sortText)) { - this.changes.sortKeyUpdated(existing); + this.resortedParents.add(existing.parent); } break; } @@ -177,13 +219,15 @@ export class HierarchicalByLocationProjection extends Disposable implements ITes break; } - this.changes.addedOrRemoved(toRemove); + // The first element will cause the root to be hidden + const affectsRootElement = toRemove.parent?.children.size === 1; + this.changedParents.add(affectsRootElement ? null : toRemove.parent); const queue: Iterable[] = [[toRemove]]; while (queue.length) { for (const item of queue.pop()!) { - if (item instanceof ByLocationTestItemElement) { - queue.push(this.unstoreItem(this.items, item)); + if (item instanceof TreeTestItemElement) { + queue.push(this.unstoreItem(item)); } } } @@ -200,14 +244,28 @@ export class HierarchicalByLocationProjection extends Disposable implements ITes * @inheritdoc */ public applyTo(tree: ObjectTree) { - this.changes.applyTo(tree, this.renderNode, () => this.roots); + for (const s of [this.changedParents, this.resortedParents]) { + for (const element of s) { + if (element && !tree.hasElement(element)) { + s.delete(element); + } + } + } + + for (const parent of this.changedParents) { + tree.setChildren(parent, getChildrenForParent(this.lastState, this.rootsWithChildren, parent), { diffIdentityProvider: testIdentityProvider }); + } + + for (const parent of this.resortedParents) { + tree.resort(parent, false); + } } /** * @inheritdoc */ public expandElement(element: TestItemTreeElement, depth: number): void { - if (!(element instanceof ByLocationTestItemElement)) { + if (!(element instanceof TreeTestItemElement)) { return; } @@ -218,61 +276,33 @@ export class HierarchicalByLocationProjection extends Disposable implements ITes this.testService.collection.expand(element.test.item.extId, depth); } - protected createItem(item: InternalTestItem): ByLocationTestItemElement { + private createItem(item: InternalTestItem): TreeTestItemElement { const parentId = TestId.parentId(item.item.extId); const parent = parentId ? this.items.get(parentId)! : null; - return new ByLocationTestItemElement(item, parent, n => this.changes.addedOrRemoved(n)); + return new TreeTestItemElement(item, parent, n => this.changedParents.add(n)); } - protected readonly addUpdated = (item: IActionableTestTreeElement) => { - const cast = item as ByLocationTestItemElement; - this.changes.updated(cast); - }; - - protected renderNode: NodeRenderFn = (node, recurse) => { - if (node instanceof TestTreeErrorMessage) { - return { element: node }; - } - - if (node.depth === 0) { - // Omit the test controller root if there are no siblings - if (!peersHaveChildren(node, () => this.roots)) { - return NodeRenderDirective.Concat; - } - - // Omit roots that have no child tests - if (node.children.size === 0) { - return NodeRenderDirective.Omit; - } - } - - return { - element: node, - collapsible: node.test.expand !== TestItemExpandState.NotExpandable, - collapsed: isCollapsedInSerializedTestTree(this.lastState, node.test.item.extId) ?? node.depth > 0, - children: recurse(node.children), - }; - }; - - protected unstoreItem(items: Map, treeElement: ByLocationTestItemElement) { + private unstoreItem(treeElement: TreeTestItemElement) { const parent = treeElement.parent; parent?.children.delete(treeElement); - items.delete(treeElement.test.item.extId); - if (parent instanceof ByLocationTestItemElement) { - refreshComputedState(computedStateAccessor, parent, undefined, !!treeElement.duration).forEach(this.addUpdated); + this.items.delete(treeElement.test.item.extId); + if (parent instanceof TreeTestItemElement) { + refreshComputedState(computedStateAccessor, parent, undefined, !!treeElement.duration).forEach(i => i.fireChange()); } return treeElement.children; } - protected storeItem(treeElement: ByLocationTestItemElement) { + private storeItem(treeElement: TreeTestItemElement) { treeElement.parent?.children.add(treeElement); this.items.set(treeElement.test.item.extId, treeElement); - this.changes.addedOrRemoved(treeElement); - const reveal = this.getRevealDepth(treeElement); - if (reveal !== undefined || isCollapsedInSerializedTestTree(this.lastState, treeElement.test.item.extId) === false) { - this.expandElement(treeElement, reveal || 0); + // The first element will cause the root to be shown + const affectsRootElement = treeElement.parent?.children.size === 1; + this.changedParents.add(affectsRootElement ? null : treeElement.parent); + + if (treeElement.depth === 0 || isCollapsedInSerializedTestTree(this.lastState, treeElement.test.item.extId) === false) { + this.expandElement(treeElement, 0); } const prevState = this.results.getStateById(treeElement.test.item.extId)?.[1]; @@ -281,7 +311,7 @@ export class HierarchicalByLocationProjection extends Disposable implements ITes treeElement.ownState = prevState.computedState; treeElement.ownDuration = prevState.ownDuration; - refreshComputedState(computedStateAccessor, treeElement, undefined, !!treeElement.ownDuration).forEach(this.addUpdated); + refreshComputedState(computedStateAccessor, treeElement, undefined, !!treeElement.ownDuration).forEach(i => i.fireChange()); } } } diff --git a/src/vs/workbench/contrib/testing/browser/testExplorerActions.ts b/src/vs/workbench/contrib/testing/browser/testExplorerActions.ts index 1e490d507e1..a5bce0dab7e 100644 --- a/src/vs/workbench/contrib/testing/browser/testExplorerActions.ts +++ b/src/vs/workbench/contrib/testing/browser/testExplorerActions.ts @@ -28,7 +28,7 @@ import { ViewAction } from 'vs/workbench/browser/parts/views/viewPane'; import { FocusedViewContext } from 'vs/workbench/common/contextkeys'; import { IViewsService, ViewContainerLocation } from 'vs/workbench/common/views'; import { VIEWLET_ID as EXTENSIONS_VIEWLET_ID, IExtensionsViewPaneContainer } from 'vs/workbench/contrib/extensions/common/extensions'; -import { IActionableTestTreeElement, TestExplorerTreeElement, TestItemTreeElement } from 'vs/workbench/contrib/testing/browser/explorerProjections/index'; +import { TestExplorerTreeElement, TestItemTreeElement } from 'vs/workbench/contrib/testing/browser/explorerProjections/index'; import * as icons from 'vs/workbench/contrib/testing/browser/icons'; import { TestingExplorerView } from 'vs/workbench/contrib/testing/browser/testingExplorerView'; import { TestResultsView } from 'vs/workbench/contrib/testing/browser/testingOutputPeek'; @@ -83,12 +83,10 @@ export class HideTestAction extends Action2 { }); } - public override run(accessor: ServicesAccessor, ...elements: IActionableTestTreeElement[]) { + public override run(accessor: ServicesAccessor, ...elements: TestItemTreeElement[]) { const service = accessor.get(ITestService); for (const element of elements) { - if (element instanceof TestItemTreeElement) { - service.excluded.toggle(element.test, true); - } + service.excluded.toggle(element.test, true); } return Promise.resolve(); } @@ -157,9 +155,9 @@ export class DebugAction extends Action2 { }); } - public override run(acessor: ServicesAccessor, ...elements: IActionableTestTreeElement[]): Promise { + public override run(acessor: ServicesAccessor, ...elements: TestItemTreeElement[]): Promise { return acessor.get(ITestService).runTests({ - tests: elements.flatMap(e => [...e.tests]), + tests: elements.map(e => e.test), group: TestRunProfileBitset.Debug, }); } @@ -180,16 +178,11 @@ export class RunUsingProfileAction extends Action2 { }); } - public override async run(acessor: ServicesAccessor, ...elements: IActionableTestTreeElement[]): Promise { - const testElements = elements.filter((e): e is TestItemTreeElement => e instanceof TestItemTreeElement); - if (testElements.length === 0) { - return; - } - + public override async run(acessor: ServicesAccessor, ...elements: TestItemTreeElement[]): Promise { const commandService = acessor.get(ICommandService); const testService = acessor.get(ITestService); const profile: ITestRunProfile | undefined = await commandService.executeCommand('vscode.pickTestProfile', { - onlyForTest: testElements[0].test, + onlyForTest: elements[0].test, }); if (!profile) { return; @@ -200,7 +193,7 @@ export class RunUsingProfileAction extends Action2 { profileGroup: profile.group, profileId: profile.profileId, controllerId: profile.controllerId, - testIds: testElements.filter(t => canUseProfileWithTest(profile, t.test)).map(t => t.test.item.extId) + testIds: elements.filter(t => canUseProfileWithTest(profile, t.test)).map(t => t.test.item.extId) }] }); } @@ -219,9 +212,9 @@ export class RunAction extends Action2 { /** * @override */ - public override run(acessor: ServicesAccessor, ...elements: IActionableTestTreeElement[]): Promise { + public override run(acessor: ServicesAccessor, ...elements: TestItemTreeElement[]): Promise { return acessor.get(ITestService).runTests({ - tests: elements.flatMap(e => [...e.tests]), + tests: elements.map(e => e.test), group: TestRunProfileBitset.Run, }); } @@ -271,14 +264,10 @@ export class ContinuousRunTestAction extends Action2 { }); } - public override async run(accessor: ServicesAccessor, ...elements: IActionableTestTreeElement[]): Promise { + public override async run(accessor: ServicesAccessor, ...elements: TestItemTreeElement[]): Promise { const crService = accessor.get(ITestingContinuousRunService); const profileService = accessor.get(ITestProfileService); for (const element of elements) { - if (!(element instanceof TestItemTreeElement)) { - continue; - } - const id = element.test.item.extId; if (crService.isSpecificallyEnabledFor(id)) { crService.stop(id); @@ -316,17 +305,13 @@ export class ContinuousRunUsingProfileTestAction extends Action2 { }); } - public override async run(accessor: ServicesAccessor, ...elements: IActionableTestTreeElement[]): Promise { + public override async run(accessor: ServicesAccessor, ...elements: TestItemTreeElement[]): Promise { const crService = accessor.get(ITestingContinuousRunService); const profileService = accessor.get(ITestProfileService); const notificationService = accessor.get(INotificationService); const quickInputService = accessor.get(IQuickInputService); for (const element of elements) { - if (!(element instanceof TestItemTreeElement)) { - continue; - } - const selected = await selectContinuousRunProfiles(crService, notificationService, quickInputService, [{ profiles: profileService.getControllerProfiles(element.test.controllerId) }]); @@ -1432,16 +1417,11 @@ export class RefreshTestsAction extends Action2 { }); } - public async run(accessor: ServicesAccessor, ...elements: IActionableTestTreeElement[]) { + public async run(accessor: ServicesAccessor, ...elements: TestItemTreeElement[]) { const testService = accessor.get(ITestService); const progressService = accessor.get(IProgressService); - const controllerIds = distinct( - elements - .filter((e): e is TestItemTreeElement => e instanceof TestItemTreeElement) - .map(e => e.test.controllerId) - ); - + const controllerIds = distinct(elements.map(e => e.test.controllerId)); return progressService.withProgress({ location: Testing.ViewletId }, async () => { if (controllerIds.length) { await Promise.all(controllerIds.map(id => testService.refreshTests(id))); diff --git a/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts b/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts index 63d72afa5c3..2db684d11a5 100644 --- a/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts +++ b/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts @@ -17,8 +17,9 @@ import { Color, RGBA } from 'vs/base/common/color'; import { Emitter, Event } from 'vs/base/common/event'; import { FuzzyScore } from 'vs/base/common/filters'; import { KeyCode } from 'vs/base/common/keyCodes'; -import { Disposable, IDisposable, MutableDisposable, dispose } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore, IDisposable, MutableDisposable } from 'vs/base/common/lifecycle'; import { fuzzyContains } 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!./media/testing'; @@ -40,14 +41,13 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { defaultButtonStyles } from 'vs/platform/theme/browser/defaultStyles'; import { foreground } from 'vs/platform/theme/common/colorRegistry'; import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; -import { ThemeIcon } from 'vs/base/common/themables'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; import { ViewPane } from 'vs/workbench/browser/parts/views/viewPane'; import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; import { IViewDescriptorService } from 'vs/workbench/common/views'; -import { HierarchicalByLocationProjection } from 'vs/workbench/contrib/testing/browser/explorerProjections/hierarchalByLocation'; -import { ByNameTestItemElement, HierarchicalByNameProjection } from 'vs/workbench/contrib/testing/browser/explorerProjections/hierarchalByName'; +import { TreeProjection } from 'vs/workbench/contrib/testing/browser/explorerProjections/treeProjection'; +import { ListProjection } from 'vs/workbench/contrib/testing/browser/explorerProjections/listProjection'; import { ITestTreeProjection, TestExplorerTreeElement, TestItemTreeElement, TestTreeErrorMessage } from 'vs/workbench/contrib/testing/browser/explorerProjections/index'; import { getTestItemContextOverlay } from 'vs/workbench/contrib/testing/browser/explorerProjections/testItemContextOverlay'; import { TestingObjectTree } from 'vs/workbench/contrib/testing/browser/explorerProjections/testingObjectTree'; @@ -66,11 +66,11 @@ import { ITestResultService } from 'vs/workbench/contrib/testing/common/testResu import { IMainThreadTestCollection, ITestService, testCollectionIsEmpty } from 'vs/workbench/contrib/testing/common/testService'; import { ITestRunProfile, InternalTestItem, TestItemExpandState, TestResultState, TestRunProfileBitset } from 'vs/workbench/contrib/testing/common/testTypes'; import { TestingContextKeys } from 'vs/workbench/contrib/testing/common/testingContextKeys'; +import { ITestingContinuousRunService } from 'vs/workbench/contrib/testing/common/testingContinuousRunService'; import { ITestingPeekOpener } from 'vs/workbench/contrib/testing/common/testingPeekOpener'; import { cmpPriority, isFailedState, isStateWithResult } from 'vs/workbench/contrib/testing/common/testingStates'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { ITestingContinuousRunService } from 'vs/workbench/contrib/testing/common/testingContinuousRunService'; import { IActivityService, NumberBadge } from 'vs/workbench/services/activity/common/activity'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; const enum LastFocusState { Input, @@ -152,13 +152,6 @@ export class TestingExplorerView extends ViewPane { return { include: [], exclude: [] }; } - if (projection instanceof ByNameTestItemElement) { - return { - include: [...this.testService.collection.rootItems], - exclude: [], - }; - } - // To calculate includes and excludes, we include the first children that // have a majority of their items included too, and then apply exclusions. const include = new Set(); @@ -885,9 +878,9 @@ class TestingExplorerViewModel extends Disposable { const lastState = this.lastViewState.get({}); if (this._viewMode.get() === TestExplorerViewMode.List) { - this.projection.value = this.instantiationService.createInstance(HierarchicalByNameProjection, lastState); + this.projection.value = this.instantiationService.createInstance(ListProjection, lastState); } else { - this.projection.value = this.instantiationService.createInstance(HierarchicalByLocationProjection, lastState); + this.projection.value = this.instantiationService.createInstance(TreeProjection, lastState); } const scheduler = new RunOnceScheduler(() => this.applyProjectionChanges(), 200); @@ -1041,7 +1034,7 @@ class TestsFilter implements ITreeFilter { for (let e: TestItemTreeElement | null = element; e; e = e.parent) { // start as included if the first glob is a negation let included = this.state.globList[0].include === false ? FilterResult.Include : FilterResult.Inherit; - const data = e.label.toLowerCase(); + const data = e.test.item.label.toLowerCase(); for (const { include, text } of this.state.globList) { if (fuzzy ? fuzzyContains(data, text) : data.includes(text)) { @@ -1094,9 +1087,11 @@ class TreeSorter implements ITreeSorter { } } + const sa = a.test.item.sortText; + const sb = b.test.item.sortText; // If tests are in the same location and there's no preferred sortText, // keep the extension's insertion order (#163449). - return inSameLocation && !a.sortText && !b.sortText ? 0 : (a.sortText || a.label).localeCompare(b.sortText || b.label); + return inSameLocation && !sa && !sb ? 0 : (sa || a.test.item.label).localeCompare(sb || b.test.item.label); } } @@ -1140,7 +1135,7 @@ class TestExplorerActionRunner extends ActionRunner { } const getLabelForTestTreeElement = (element: TestItemTreeElement) => { - let label = labelForTestInState(element.description || element.label, element.state); + let label = labelForTestInState(element.description || element.test.item.label, element.state); if (element instanceof TestItemTreeElement) { if (element.duration !== undefined) { @@ -1175,7 +1170,7 @@ class ListAccessibilityProvider implements IListAccessibilityProvider { getKeyboardNavigationLabel(element: TestExplorerTreeElement) { - return element instanceof TestTreeErrorMessage ? element.message : element.label; + return element instanceof TestTreeErrorMessage ? element.message : element.test.item.label; } } @@ -1237,18 +1232,18 @@ class ErrorRenderer implements ITreeRenderer { + implements ITreeRenderer { public static readonly ID = 'testItem'; constructor( @@ -1271,48 +1266,47 @@ class TestItemRenderer extends Disposable /** * @inheritdoc */ - public renderTemplate(container: HTMLElement): IActionableElementTemplateData { + public renderTemplate(container: HTMLElement): ITestElementTemplateData { const wrapper = dom.append(container, dom.$('.test-item')); const icon = dom.append(wrapper, dom.$('.computed-state')); const label = dom.append(wrapper, dom.$('.label')); + const disposable = new DisposableStore(); dom.append(wrapper, dom.$(ThemeIcon.asCSSSelector(icons.testingHiddenIcon))); - const actionBar = new ActionBar(wrapper, { + const actionBar = disposable.add(new ActionBar(wrapper, { actionRunner: this.actionRunner, actionViewItemProvider: action => action instanceof MenuItemAction ? this.instantiationService.createInstance(MenuEntryActionViewItem, action, undefined) : undefined - }); + })); - const crListener = this.crService.onDidChange(changed => { + disposable.add(this.crService.onDidChange(changed => { if (templateData.current && (!changed || changed === templateData.current.test.item.extId)) { this.fillActionBar(templateData.current, templateData); } - }); + })); - const templateData: IActionableElementTemplateData = { wrapper, label, actionBar, icon, elementDisposable: [], templateDisposable: [actionBar, crListener] }; + const templateData: ITestElementTemplateData = { wrapper, label, actionBar, icon, elementDisposable: new DisposableStore(), templateDisposable: disposable }; return templateData; } /** * @inheritdoc */ - disposeTemplate(templateData: IActionableElementTemplateData): void { - dispose(templateData.templateDisposable); - templateData.templateDisposable = []; + disposeTemplate(templateData: ITestElementTemplateData): void { + templateData.templateDisposable.clear(); } /** * @inheritdoc */ - disposeElement(_element: ITreeNode, _: number, templateData: IActionableElementTemplateData): void { - dispose(templateData.elementDisposable); - templateData.elementDisposable = []; + disposeElement(_element: ITreeNode, _: number, templateData: ITestElementTemplateData): void { + templateData.elementDisposable.clear(); } - private fillActionBar(element: TestItemTreeElement, data: IActionableElementTemplateData) { + private fillActionBar(element: TestItemTreeElement, data: ITestElementTemplateData) { const { actions, contextOverlay } = getActionableElementActions(this.contextKeyService, this.menuService, this.testService, this.crService, this.profiles, element); data.actionBar.domNode.classList.toggle('testing-is-continuous-run', !!contextOverlay.getContextKeyValue(TestingContextKeys.isContinuousModeOn.key)); data.actionBar.clear(); @@ -1323,10 +1317,17 @@ class TestItemRenderer extends Disposable /** * @inheritdoc */ - public renderElement(node: ITreeNode, _depth: number, data: IActionableElementTemplateData): void { + public renderElement(node: ITreeNode, _depth: number, data: ITestElementTemplateData): void { + data.elementDisposable.clear(); data.current = node.element; this.fillActionBar(node.element, data); + + data.elementDisposable.add(node.element.onChange(() => this._renderElement(node, data))); + this._renderElement(node, data); + } + + public _renderElement(node: ITreeNode, data: ITestElementTemplateData): void { const testHidden = this.testService.excluded.contains(node.element.test); data.wrapper.classList.toggle('test-is-hidden', testHidden); @@ -1341,8 +1342,8 @@ class TestItemRenderer extends Disposable } data.label.title = getLabelForTestTreeElement(node.element); - if (node.element.label.trim()) { - dom.reset(data.label, ...renderLabelWithIcons(node.element.label)); + if (node.element.test.item.label.trim()) { + dom.reset(data.label, ...renderLabelWithIcons(node.element.test.item.label)); } else { data.label.textContent = String.fromCharCode(0xA0); //   } diff --git a/src/vs/workbench/contrib/testing/common/testId.ts b/src/vs/workbench/contrib/testing/common/testId.ts index 6e4929616b2..7b6f5f04dc7 100644 --- a/src/vs/workbench/contrib/testing/common/testId.ts +++ b/src/vs/workbench/contrib/testing/common/testId.ts @@ -135,6 +135,13 @@ export class TestId { } } + /** + * Gets the ID of the parent test. + */ + public get rootId(): TestId { + return new TestId(this.path, 1); + } + /** * Gets the ID of the parent test. */ diff --git a/src/vs/workbench/contrib/testing/test/browser/explorerProjections/hierarchalByLocation.test.ts b/src/vs/workbench/contrib/testing/test/browser/explorerProjections/hierarchalByLocation.test.ts index 502b66a9052..574835878e2 100644 --- a/src/vs/workbench/contrib/testing/test/browser/explorerProjections/hierarchalByLocation.test.ts +++ b/src/vs/workbench/contrib/testing/test/browser/explorerProjections/hierarchalByLocation.test.ts @@ -5,14 +5,14 @@ import * as assert from 'assert'; import { Emitter } from 'vs/base/common/event'; -import { HierarchicalByLocationProjection } from 'vs/workbench/contrib/testing/browser/explorerProjections/hierarchalByLocation'; +import { TreeProjection } from 'vs/workbench/contrib/testing/browser/explorerProjections/treeProjection'; import { TestId } from 'vs/workbench/contrib/testing/common/testId'; import { TestResultItemChange, TestResultItemChangeReason } from 'vs/workbench/contrib/testing/common/testResult'; import { TestDiffOpType, TestItemExpandState, TestResultItem, TestResultState } from 'vs/workbench/contrib/testing/common/testTypes'; import { TestTreeTestHarness } from 'vs/workbench/contrib/testing/test/browser/testObjectTree'; import { TestTestItem } from 'vs/workbench/contrib/testing/test/common/testStubs'; -class TestHierarchicalByLocationProjection extends HierarchicalByLocationProjection { +class TestHierarchicalByLocationProjection extends TreeProjection { } suite('Workbench - Testing Explorer Hierarchal by Location Projection', () => { diff --git a/src/vs/workbench/contrib/testing/test/browser/explorerProjections/hierarchalByName.test.ts b/src/vs/workbench/contrib/testing/test/browser/explorerProjections/hierarchalByName.test.ts index 8671b217c0d..498971646ed 100644 --- a/src/vs/workbench/contrib/testing/test/browser/explorerProjections/hierarchalByName.test.ts +++ b/src/vs/workbench/contrib/testing/test/browser/explorerProjections/hierarchalByName.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import { Emitter } from 'vs/base/common/event'; -import { HierarchicalByNameProjection } from 'vs/workbench/contrib/testing/browser/explorerProjections/hierarchalByName'; +import { ListProjection } from 'vs/workbench/contrib/testing/browser/explorerProjections/listProjection'; import { TestId } from 'vs/workbench/contrib/testing/common/testId'; import { TestResultItemChange } from 'vs/workbench/contrib/testing/common/testResult'; import { TestDiffOpType, TestItemExpandState } from 'vs/workbench/contrib/testing/common/testTypes'; @@ -13,7 +13,7 @@ import { TestTreeTestHarness } from 'vs/workbench/contrib/testing/test/browser/t import { TestTestItem } from 'vs/workbench/contrib/testing/test/common/testStubs'; suite('Workbench - Testing Explorer Hierarchal by Name Projection', () => { - let harness: TestTreeTestHarness; + let harness: TestTreeTestHarness; let onTestChanged: Emitter; let resultsService: any; @@ -25,7 +25,7 @@ suite('Workbench - Testing Explorer Hierarchal by Name Projection', () => { getStateById: () => ({ state: { state: 0 }, computedState: 0 }), }; - harness = new TestTreeTestHarness(l => new HierarchicalByNameProjection({}, l, resultsService as any)); + harness = new TestTreeTestHarness(l => new ListProjection({}, l, resultsService as any)); }); teardown(() => { diff --git a/src/vs/workbench/contrib/testing/test/browser/testObjectTree.ts b/src/vs/workbench/contrib/testing/test/browser/testObjectTree.ts index 4932446d721..c1792e29343 100644 --- a/src/vs/workbench/contrib/testing/test/browser/testObjectTree.ts +++ b/src/vs/workbench/contrib/testing/test/browser/testObjectTree.ts @@ -89,7 +89,7 @@ class ByLabelTreeSorter implements ITreeSorter { } } - return (a.sortText || a.label).localeCompare(b.sortText || b.label); + return (a.test.item.sortText || a.test.item.label).localeCompare(b.test.item.sortText || b.test.item.label); } } @@ -120,7 +120,7 @@ export class TestTreeTestHarness 'label' in t ? t.label : t.message.toString(), sorter)); + this.tree = this._register(new TestObjectTree(t => 'test' in t ? t.test.item.label : t.message.toString(), sorter)); this._register(this.tree.onDidChangeCollapseState(evt => { if (evt.node.element instanceof TestItemTreeElement) { this.projection.expandElement(evt.node.element, evt.deep ? Infinity : 0); From 0207c66c872632018748b469f5f19d6c2ac80462 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Tue, 4 Jul 2023 03:11:09 -0700 Subject: [PATCH 143/926] ports: support resolver tunnel factories on the web (#186937) @alexr00 I just copied the non-Node.js-specific code into the base port class and things seem to work. What do you think? Fixes https://github.com/microsoft/vscode-remote-release/issues/8595 --- cli/src/tunnels/paths.rs | 13 +- .../api/common/extHostTunnelService.ts | 189 ++++++++++++++-- .../api/node/extHost.node.services.ts | 4 +- .../api/node/extHostTunnelService.ts | 213 ++---------------- 4 files changed, 199 insertions(+), 220 deletions(-) diff --git a/cli/src/tunnels/paths.rs b/cli/src/tunnels/paths.rs index fa06db5dd7a..a0cd43cd83c 100644 --- a/cli/src/tunnels/paths.rs +++ b/cli/src/tunnels/paths.rs @@ -91,10 +91,15 @@ impl InstalledServer { pub fn server_paths(&self, p: &LauncherPaths) -> ServerPaths { let server_dir = self.get_install_folder(p); ServerPaths { - executable: server_dir - .join(SERVER_FOLDER_NAME) - .join("bin") - .join(self.quality.server_entrypoint()), + // allow using the OSS server in development via an override + executable: if let Some(p) = option_env!("VSCODE_CLI_OVERRIDE_SERVER_PATH") { + PathBuf::from(p) + } else { + server_dir + .join(SERVER_FOLDER_NAME) + .join("bin") + .join(self.quality.server_entrypoint()) + }, logfile: server_dir.join("log.txt"), pidfile: server_dir.join("pid.txt"), server_dir, diff --git a/src/vs/workbench/api/common/extHostTunnelService.ts b/src/vs/workbench/api/common/extHostTunnelService.ts index dfdfc5ab5a2..4df3fc4c600 100644 --- a/src/vs/workbench/api/common/extHostTunnelService.ts +++ b/src/vs/workbench/api/common/extHostTunnelService.ts @@ -3,15 +3,21 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ExtHostTunnelServiceShape, PortAttributesSelector, TunnelDto } from 'vs/workbench/api/common/extHost.protocol'; -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import * as vscode from 'vscode'; -import { ProvidedPortAttributes, RemoteTunnel, TunnelCreationOptions, TunnelOptions, TunnelPrivacyId } from 'vs/platform/tunnel/common/tunnel'; -import { IDisposable } from 'vs/base/common/lifecycle'; import { Emitter } from 'vs/base/common/event'; -import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; +import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import * as nls from 'vs/nls'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { ILogService } from 'vs/platform/log/common/log'; +import { DisposableTunnel, ProvidedOnAutoForward, ProvidedPortAttributes, RemoteTunnel, TunnelCreationOptions, TunnelOptions, TunnelPrivacyId } from 'vs/platform/tunnel/common/tunnel'; +import { ExtHostTunnelServiceShape, MainContext, MainThreadTunnelServiceShape, PortAttributesSelector, TunnelDto } from 'vs/workbench/api/common/extHost.protocol'; +import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; +import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; +import * as types from 'vs/workbench/api/common/extHostTypes'; import { CandidatePort } from 'vs/workbench/services/remote/common/remoteExplorerService'; +import * as vscode from 'vscode'; + +class ExtensionTunnel extends DisposableTunnel implements vscode.Tunnel { } export namespace TunnelDtoConverter { export function fromApiTunnel(tunnel: vscode.Tunnel): TunnelDto { @@ -53,37 +59,176 @@ export interface IExtHostTunnelService extends ExtHostTunnelServiceShape { export const IExtHostTunnelService = createDecorator('IExtHostTunnelService'); -export class ExtHostTunnelService implements IExtHostTunnelService { - declare readonly _serviceBrand: undefined; - onDidChangeTunnels: vscode.Event = (new Emitter()).event; +export class ExtHostTunnelService extends Disposable implements IExtHostTunnelService { + readonly _serviceBrand: undefined; + protected readonly _proxy: MainThreadTunnelServiceShape; + private _forwardPortProvider: ((tunnelOptions: TunnelOptions, tunnelCreationOptions: TunnelCreationOptions) => Thenable | undefined) | undefined; + private _showCandidatePort: (host: string, port: number, detail: string) => Thenable = () => { return Promise.resolve(true); }; + private _extensionTunnels: Map> = new Map(); + private _onDidChangeTunnels: Emitter = new Emitter(); + onDidChangeTunnels: vscode.Event = this._onDidChangeTunnels.event; + + private _providerHandleCounter: number = 0; + private _portAttributesProviders: Map = new Map(); constructor( @IExtHostRpcService extHostRpc: IExtHostRpcService, + @IExtHostInitDataService initData: IExtHostInitDataService, + @ILogService protected readonly logService: ILogService ) { - } - async $applyCandidateFilter(candidates: CandidatePort[]): Promise { - return candidates; + super(); + this._proxy = extHostRpc.getProxy(MainContext.MainThreadTunnelService); } async openTunnel(extension: IExtensionDescription, forward: TunnelOptions): Promise { + this.logService.trace(`ForwardedPorts: (ExtHostTunnelService) ${extension.identifier.value} called openTunnel API for ${forward.remoteAddress.host}:${forward.remoteAddress.port}.`); + const tunnel = await this._proxy.$openTunnel(forward, extension.displayName); + if (tunnel) { + const disposableTunnel: vscode.Tunnel = new ExtensionTunnel(tunnel.remoteAddress, tunnel.localAddress, () => { + return this._proxy.$closeTunnel(tunnel.remoteAddress); + }); + this._register(disposableTunnel); + return disposableTunnel; + } return undefined; } + async getTunnels(): Promise { - return []; + return this._proxy.$getTunnels(); } - async setTunnelFactory(provider: vscode.RemoteAuthorityResolver | undefined): Promise { - return { dispose: () => { } }; + private nextPortAttributesProviderHandle(): number { + return this._providerHandleCounter++; } - registerPortsAttributesProvider(portSelector: PortAttributesSelector, provider: vscode.PortAttributesProvider) { - return { dispose: () => { } }; + + registerPortsAttributesProvider(portSelector: PortAttributesSelector, provider: vscode.PortAttributesProvider): vscode.Disposable { + const providerHandle = this.nextPortAttributesProviderHandle(); + this._portAttributesProviders.set(providerHandle, { selector: portSelector, provider }); + + this._proxy.$registerPortsAttributesProvider(portSelector, providerHandle); + return new types.Disposable(() => { + this._portAttributesProviders.delete(providerHandle); + this._proxy.$unregisterPortsAttributesProvider(providerHandle); + }); } async $providePortAttributes(handles: number[], ports: number[], pid: number | undefined, commandline: string | undefined, cancellationToken: vscode.CancellationToken): Promise { - return []; + const providedAttributes: { providedAttributes: vscode.PortAttributes | null | undefined; port: number }[] = []; + for (const handle of handles) { + const provider = this._portAttributesProviders.get(handle); + if (!provider) { + return []; + } + providedAttributes.push(...(await Promise.all(ports.map(async (port) => { + return { providedAttributes: (await provider.provider.providePortAttributes(port, pid, commandline, cancellationToken)), port }; + })))); + } + + const allAttributes = <{ providedAttributes: vscode.PortAttributes; port: number }[]>providedAttributes.filter(attribute => !!attribute.providedAttributes); + + return (allAttributes.length > 0) ? allAttributes.map(attributes => { + return { + autoForwardAction: attributes.providedAttributes.autoForwardAction, + port: attributes.port + }; + }) : []; } - async $forwardPort(tunnelOptions: TunnelOptions, tunnelCreationOptions: TunnelCreationOptions): Promise { return undefined; } - async $closeTunnel(remote: { host: string; port: number }): Promise { } - async $onDidTunnelsChange(): Promise { } - async $registerCandidateFinder(): Promise { } + async $registerCandidateFinder(_enable: boolean): Promise { } + + async setTunnelFactory(provider: vscode.RemoteAuthorityResolver | undefined): Promise { + // Do not wait for any of the proxy promises here. + // It will delay startup and there is nothing that needs to be waited for. + if (provider) { + if (provider.candidatePortSource !== undefined) { + this._proxy.$setCandidatePortSource(provider.candidatePortSource); + } + if (provider.showCandidatePort) { + this._showCandidatePort = provider.showCandidatePort; + this._proxy.$setCandidateFilter(); + } + if (provider.tunnelFactory) { + this._forwardPortProvider = provider.tunnelFactory; + let privacyOptions = provider.tunnelFeatures?.privacyOptions ?? []; + if (provider.tunnelFeatures?.public && (privacyOptions.length === 0)) { + privacyOptions = [ + { + id: 'private', + label: nls.localize('tunnelPrivacy.private', "Private"), + themeIcon: 'lock' + }, + { + id: 'public', + label: nls.localize('tunnelPrivacy.public', "Public"), + themeIcon: 'eye' + } + ]; + } + + const tunnelFeatures = provider.tunnelFeatures ? { + elevation: !!provider.tunnelFeatures?.elevation, + public: !!provider.tunnelFeatures?.public, + privacyOptions + } : undefined; + + this._proxy.$setTunnelProvider(tunnelFeatures); + } + } else { + this._forwardPortProvider = undefined; + } + return toDisposable(() => { + this._forwardPortProvider = undefined; + }); + } + + async $closeTunnel(remote: { host: string; port: number }, silent?: boolean): Promise { + if (this._extensionTunnels.has(remote.host)) { + const hostMap = this._extensionTunnels.get(remote.host)!; + if (hostMap.has(remote.port)) { + if (silent) { + hostMap.get(remote.port)!.disposeListener.dispose(); + } + await hostMap.get(remote.port)!.tunnel.dispose(); + hostMap.delete(remote.port); + } + } + } + + async $onDidTunnelsChange(): Promise { + this._onDidChangeTunnels.fire(); + } + + async $forwardPort(tunnelOptions: TunnelOptions, tunnelCreationOptions: TunnelCreationOptions): Promise { + if (this._forwardPortProvider) { + try { + this.logService.trace('ForwardedPorts: (ExtHostTunnelService) Getting tunnel from provider.'); + const providedPort = this._forwardPortProvider(tunnelOptions, tunnelCreationOptions); + this.logService.trace('ForwardedPorts: (ExtHostTunnelService) Got tunnel promise from provider.'); + if (providedPort !== undefined) { + const tunnel = await providedPort; + this.logService.trace('ForwardedPorts: (ExtHostTunnelService) Successfully awaited tunnel from provider.'); + if (!this._extensionTunnels.has(tunnelOptions.remoteAddress.host)) { + this._extensionTunnels.set(tunnelOptions.remoteAddress.host, new Map()); + } + const disposeListener = this._register(tunnel.onDidDispose(() => { + this.logService.trace('ForwardedPorts: (ExtHostTunnelService) Extension fired tunnel\'s onDidDispose.'); + return this._proxy.$closeTunnel(tunnel.remoteAddress); + })); + this._extensionTunnels.get(tunnelOptions.remoteAddress.host)!.set(tunnelOptions.remoteAddress.port, { tunnel, disposeListener }); + return TunnelDtoConverter.fromApiTunnel(tunnel); + } else { + this.logService.trace('ForwardedPorts: (ExtHostTunnelService) Tunnel is undefined'); + } + } catch (e) { + this.logService.trace('ForwardedPorts: (ExtHostTunnelService) tunnel provider error'); + } + } + return undefined; + } + + async $applyCandidateFilter(candidates: CandidatePort[]): Promise { + const filter = await Promise.all(candidates.map(candidate => this._showCandidatePort(candidate.host, candidate.port, candidate.detail ?? ''))); + const result = candidates.filter((candidate, index) => filter[index]); + this.logService.trace(`ForwardedPorts: (ExtHostTunnelService) filtered from ${candidates.map(port => port.port).join(', ')} to ${result.map(port => port.port).join(', ')}`); + return result; + } } diff --git a/src/vs/workbench/api/node/extHost.node.services.ts b/src/vs/workbench/api/node/extHost.node.services.ts index 6785393c611..9a4ffa8b033 100644 --- a/src/vs/workbench/api/node/extHost.node.services.ts +++ b/src/vs/workbench/api/node/extHost.node.services.ts @@ -9,7 +9,7 @@ import { ExtHostTask } from 'vs/workbench/api/node/extHostTask'; import { ExtHostDebugService } from 'vs/workbench/api/node/extHostDebugService'; import { NativeExtHostSearch } from 'vs/workbench/api/node/extHostSearch'; import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionService'; -import { ExtHostTunnelService } from 'vs/workbench/api/node/extHostTunnelService'; +import { NodeExtHostTunnelService } from 'vs/workbench/api/node/extHostTunnelService'; import { IExtHostDebugService } from 'vs/workbench/api/common/extHostDebugService'; import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService'; import { IExtHostSearch } from 'vs/workbench/api/common/extHostSearch'; @@ -40,5 +40,5 @@ registerSingleton(IExtHostDebugService, ExtHostDebugService, InstantiationType.E registerSingleton(IExtHostSearch, NativeExtHostSearch, InstantiationType.Eager); registerSingleton(IExtHostTask, ExtHostTask, InstantiationType.Eager); registerSingleton(IExtHostTerminalService, ExtHostTerminalService, InstantiationType.Eager); -registerSingleton(IExtHostTunnelService, ExtHostTunnelService, InstantiationType.Eager); +registerSingleton(IExtHostTunnelService, NodeExtHostTunnelService, InstantiationType.Eager); registerSingleton(IExtHostVariableResolverProvider, NodeExtHostVariableResolverProviderService, InstantiationType.Eager); diff --git a/src/vs/workbench/api/node/extHostTunnelService.ts b/src/vs/workbench/api/node/extHostTunnelService.ts index ae3cf3568f8..ac5e6717293 100644 --- a/src/vs/workbench/api/node/extHostTunnelService.ts +++ b/src/vs/workbench/api/node/extHostTunnelService.ts @@ -3,27 +3,18 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { MainThreadTunnelServiceShape, MainContext, PortAttributesSelector, TunnelDto } from 'vs/workbench/api/common/extHost.protocol'; -import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; -import type * as vscode from 'vscode'; -import * as nls from 'vs/nls'; -import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; -import { URI } from 'vs/base/common/uri'; import { exec } from 'child_process'; -import * as resources from 'vs/base/common/resources'; -import * as pfs from 'vs/base/node/pfs'; -import * as types from 'vs/workbench/api/common/extHostTypes'; -import { isLinux } from 'vs/base/common/platform'; -import { IExtHostTunnelService, TunnelDtoConverter } from 'vs/workbench/api/common/extHostTunnelService'; -import { Emitter } from 'vs/base/common/event'; -import { TunnelOptions, TunnelCreationOptions, ProvidedPortAttributes, ProvidedOnAutoForward, isLocalhost, isAllInterfaces, DisposableTunnel } from 'vs/platform/tunnel/common/tunnel'; -import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { MovingAverage } from 'vs/base/common/numbers'; -import { CandidatePort } from 'vs/workbench/services/remote/common/remoteExplorerService'; +import { isLinux } from 'vs/base/common/platform'; +import * as resources from 'vs/base/common/resources'; +import { URI } from 'vs/base/common/uri'; +import * as pfs from 'vs/base/node/pfs'; import { ILogService } from 'vs/platform/log/common/log'; - -class ExtensionTunnel extends DisposableTunnel implements vscode.Tunnel { } +import { isAllInterfaces, isLocalhost } from 'vs/platform/tunnel/common/tunnel'; +import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; +import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; +import { ExtHostTunnelService } from 'vs/workbench/api/common/extHostTunnelService'; +import { CandidatePort } from 'vs/workbench/services/remote/common/remoteExplorerService'; export function getSockets(stdout: string): Record { const lines = stdout.trim().split('\n'); @@ -173,99 +164,24 @@ export function tryFindRootPorts(connections: { socket: number; ip: string; port return ports; } -export class ExtHostTunnelService extends Disposable implements IExtHostTunnelService { - readonly _serviceBrand: undefined; - private readonly _proxy: MainThreadTunnelServiceShape; - private _forwardPortProvider: ((tunnelOptions: TunnelOptions, tunnelCreationOptions: TunnelCreationOptions) => Thenable | undefined) | undefined; - private _showCandidatePort: (host: string, port: number, detail: string) => Thenable = () => { return Promise.resolve(true); }; - private _extensionTunnels: Map> = new Map(); - private _onDidChangeTunnels: Emitter = new Emitter(); - onDidChangeTunnels: vscode.Event = this._onDidChangeTunnels.event; - private _candidateFindingEnabled: boolean = false; - private _foundRootPorts: Map = new Map(); +export class NodeExtHostTunnelService extends ExtHostTunnelService { private _initialCandidates: CandidatePort[] | undefined = undefined; - - private _providerHandleCounter: number = 0; - private _portAttributesProviders: Map = new Map(); + private _foundRootPorts: Map = new Map(); + private _candidateFindingEnabled: boolean = false; constructor( @IExtHostRpcService extHostRpc: IExtHostRpcService, @IExtHostInitDataService initData: IExtHostInitDataService, - @ILogService private readonly logService: ILogService + @ILogService logService: ILogService ) { - super(); - this._proxy = extHostRpc.getProxy(MainContext.MainThreadTunnelService); + super(extHostRpc, initData, logService); if (isLinux && initData.remote.isRemote && initData.remote.authority) { this._proxy.$setRemoteTunnelService(process.pid); this.setInitialCandidates(); } } - async openTunnel(extension: IExtensionDescription, forward: TunnelOptions): Promise { - this.logService.trace(`ForwardedPorts: (ExtHostTunnelService) ${extension.identifier.value} called openTunnel API for ${forward.remoteAddress.host}:${forward.remoteAddress.port}.`); - const tunnel = await this._proxy.$openTunnel(forward, extension.displayName); - if (tunnel) { - const disposableTunnel: vscode.Tunnel = new ExtensionTunnel(tunnel.remoteAddress, tunnel.localAddress, () => { - return this._proxy.$closeTunnel(tunnel.remoteAddress); - }); - this._register(disposableTunnel); - return disposableTunnel; - } - return undefined; - } - - private async setInitialCandidates(): Promise { - this._initialCandidates = await this.findCandidatePorts(); - this.logService.trace(`ForwardedPorts: (ExtHostTunnelService) Initial candidates found: ${this._initialCandidates.map(c => c.port).join(', ')}`); - } - - async getTunnels(): Promise { - return this._proxy.$getTunnels(); - } - - private calculateDelay(movingAverage: number) { - // Some local testing indicated that the moving average might be between 50-100 ms. - return Math.max(movingAverage * 20, 2000); - } - - private nextPortAttributesProviderHandle(): number { - return this._providerHandleCounter++; - } - - registerPortsAttributesProvider(portSelector: PortAttributesSelector, provider: vscode.PortAttributesProvider): vscode.Disposable { - const providerHandle = this.nextPortAttributesProviderHandle(); - this._portAttributesProviders.set(providerHandle, { selector: portSelector, provider }); - - this._proxy.$registerPortsAttributesProvider(portSelector, providerHandle); - return new types.Disposable(() => { - this._portAttributesProviders.delete(providerHandle); - this._proxy.$unregisterPortsAttributesProvider(providerHandle); - }); - } - - async $providePortAttributes(handles: number[], ports: number[], pid: number | undefined, commandline: string | undefined, cancellationToken: vscode.CancellationToken): Promise { - const providedAttributes: { providedAttributes: vscode.PortAttributes | null | undefined; port: number }[] = []; - for (const handle of handles) { - const provider = this._portAttributesProviders.get(handle); - if (!provider) { - return []; - } - providedAttributes.push(...(await Promise.all(ports.map(async (port) => { - return { providedAttributes: (await provider.provider.providePortAttributes(port, pid, commandline, cancellationToken)), port }; - })))); - } - - const allAttributes = <{ providedAttributes: vscode.PortAttributes; port: number }[]>providedAttributes.filter(attribute => !!attribute.providedAttributes); - - return (allAttributes.length > 0) ? allAttributes.map(attributes => { - return { - autoForwardAction: attributes.providedAttributes.autoForwardAction, - port: attributes.port - }; - }) : []; - } - - async $registerCandidateFinder(enable: boolean): Promise { + override async $registerCandidateFinder(enable: boolean): Promise { if (enable && this._candidateFindingEnabled) { // already enabled return; @@ -303,101 +219,14 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe } } - async setTunnelFactory(provider: vscode.RemoteAuthorityResolver | undefined): Promise { - // Do not wait for any of the proxy promises here. - // It will delay startup and there is nothing that needs to be waited for. - if (provider) { - if (provider.candidatePortSource !== undefined) { - this._proxy.$setCandidatePortSource(provider.candidatePortSource); - } - if (provider.showCandidatePort) { - this._showCandidatePort = provider.showCandidatePort; - this._proxy.$setCandidateFilter(); - } - if (provider.tunnelFactory) { - this._forwardPortProvider = provider.tunnelFactory; - let privacyOptions = provider.tunnelFeatures?.privacyOptions ?? []; - if (provider.tunnelFeatures?.public && (privacyOptions.length === 0)) { - privacyOptions = [ - { - id: 'private', - label: nls.localize('tunnelPrivacy.private', "Private"), - themeIcon: 'lock' - }, - { - id: 'public', - label: nls.localize('tunnelPrivacy.public', "Public"), - themeIcon: 'eye' - } - ]; - } - - const tunnelFeatures = provider.tunnelFeatures ? { - elevation: !!provider.tunnelFeatures?.elevation, - public: !!provider.tunnelFeatures?.public, - privacyOptions - } : undefined; - - this._proxy.$setTunnelProvider(tunnelFeatures); - } - } else { - this._forwardPortProvider = undefined; - } - return toDisposable(() => { - this._forwardPortProvider = undefined; - }); + private calculateDelay(movingAverage: number) { + // Some local testing indicated that the moving average might be between 50-100 ms. + return Math.max(movingAverage * 20, 2000); } - async $closeTunnel(remote: { host: string; port: number }, silent?: boolean): Promise { - if (this._extensionTunnels.has(remote.host)) { - const hostMap = this._extensionTunnels.get(remote.host)!; - if (hostMap.has(remote.port)) { - if (silent) { - hostMap.get(remote.port)!.disposeListener.dispose(); - } - await hostMap.get(remote.port)!.tunnel.dispose(); - hostMap.delete(remote.port); - } - } - } - - async $onDidTunnelsChange(): Promise { - this._onDidChangeTunnels.fire(); - } - - async $forwardPort(tunnelOptions: TunnelOptions, tunnelCreationOptions: TunnelCreationOptions): Promise { - if (this._forwardPortProvider) { - try { - this.logService.trace('ForwardedPorts: (ExtHostTunnelService) Getting tunnel from provider.'); - const providedPort = this._forwardPortProvider(tunnelOptions, tunnelCreationOptions); - this.logService.trace('ForwardedPorts: (ExtHostTunnelService) Got tunnel promise from provider.'); - if (providedPort !== undefined) { - const tunnel = await providedPort; - this.logService.trace('ForwardedPorts: (ExtHostTunnelService) Successfully awaited tunnel from provider.'); - if (!this._extensionTunnels.has(tunnelOptions.remoteAddress.host)) { - this._extensionTunnels.set(tunnelOptions.remoteAddress.host, new Map()); - } - const disposeListener = this._register(tunnel.onDidDispose(() => { - this.logService.trace('ForwardedPorts: (ExtHostTunnelService) Extension fired tunnel\'s onDidDispose.'); - return this._proxy.$closeTunnel(tunnel.remoteAddress); - })); - this._extensionTunnels.get(tunnelOptions.remoteAddress.host)!.set(tunnelOptions.remoteAddress.port, { tunnel, disposeListener }); - return TunnelDtoConverter.fromApiTunnel(tunnel); - } else { - this.logService.trace('ForwardedPorts: (ExtHostTunnelService) Tunnel is undefined'); - } - } catch (e) { - this.logService.trace('ForwardedPorts: (ExtHostTunnelService) tunnel provider error'); - } - } - return undefined; - } - - async $applyCandidateFilter(candidates: CandidatePort[]): Promise { - const filter = await Promise.all(candidates.map(candidate => this._showCandidatePort(candidate.host, candidate.port, candidate.detail ?? ''))); - const result = candidates.filter((candidate, index) => filter[index]); - this.logService.trace(`ForwardedPorts: (ExtHostTunnelService) filtered from ${candidates.map(port => port.port).join(', ')} to ${result.map(port => port.port).join(', ')}`); - return result; + private async setInitialCandidates(): Promise { + this._initialCandidates = await this.findCandidatePorts(); + this.logService.trace(`ForwardedPorts: (ExtHostTunnelService) Initial candidates found: ${this._initialCandidates.map(c => c.port).join(', ')}`); } private async findCandidatePorts(): Promise { From d1e1e04d8bd68669b15dbf2f4bb1c73f1590dcd4 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 4 Jul 2023 12:41:31 +0200 Subject: [PATCH 144/926] update mywork notebook --- .vscode/notebooks/my-work.github-issues | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.vscode/notebooks/my-work.github-issues b/.vscode/notebooks/my-work.github-issues index 9ca12439a70..9f66c4771f6 100644 --- a/.vscode/notebooks/my-work.github-issues +++ b/.vscode/notebooks/my-work.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "// list of repos we work in\n$repos=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-unpkg repo:microsoft/vscode-references-view repo:microsoft/vscode-anycode repo:microsoft/vscode-hexeditor repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-livepreview repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remote-repositories-github repo:microsoft/monaco-editor repo:microsoft/vscode-vsce repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-python repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-l10n repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release\n\n// current milestone name\n$milestone=milestone:\"June 2023\"" + "value": "// list of repos we work in\n$repos=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-unpkg repo:microsoft/vscode-references-view repo:microsoft/vscode-anycode repo:microsoft/vscode-hexeditor repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-livepreview repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remote-repositories-github repo:microsoft/monaco-editor repo:microsoft/vscode-vsce repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-python repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-l10n repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release\n\n// current milestone name\n$milestone=milestone:\"July 2023\"" }, { "kind": 1, @@ -102,7 +102,7 @@ { "kind": 2, "language": "github-issues", - "value": "repo:microsoft/vscode assignee:@me is:open type:issue -label:\"info-needed\" -label:api -label:api-finalization -label:api-proposal -label:authentication -label:bisect-ext -label:bracket-pair-colorization -label:bracket-pair-guides -label:breadcrumbs -label:callhierarchy -label:chrome-devtools -label:cloud-changes -label:code-lens -label:command-center -label:comments -label:config -label:containers -label:context-keys -label:continue-working-on -label:css-less-scss -label:custom-editors -label:debug -label:debug-disassembly -label:dialogs -label:diff-editor -label:dropdown -label:editor-api -label:editor-autoclosing -label:editor-autoindent -label:editor-bracket-matching -label:editor-clipboard -label:editor-code-actions -label:editor-color-picker -label:editor-columnselect -label:editor-commands -label:editor-comments -label:editor-contrib -label:editor-core -label:editor-drag-and-drop -label:editor-error-widget -label:editor-find -label:editor-folding -label:editor-highlight -label:editor-hover -label:editor-indent-detection -label:editor-indent-guides -label:editor-input -label:editor-input-IME -label:editor-insets -label:editor-minimap -label:editor-multicursor -label:editor-parameter-hints -label:editor-render-whitespace -label:editor-rendering -label:editor-RTL -label:editor-scrollbar -label:editor-sorting -label:editor-sticky-scroll -label:editor-symbols -label:editor-synced-region -label:editor-textbuffer -label:editor-theming -label:editor-wordnav -label:editor-wrapping -label:emmet -label:emmet-parse -label:error-list -label:extension-activation -label:extension-host -label:extension-prerelease -label:extension-recommendations -label:extensions -label:extensions-development -label:file-decorations -label:file-encoding -label:file-explorer -label:file-glob -label:file-io -label:file-nesting -label:file-watcher -label:font-rendering -label:formatting -label:getting-started -label:ghost-text -label:git -label:github -label:github-repositories -label:gpu -label:grammar -label:grid-widget -label:html -label:htmlfs -label:icon-brand -label:icons-product -label:image-preview -label:inlay-hints -label:inline-completions -label:install-update -label:intellisense-config -label:interactive-playground -label:interactive-window -label:issue-bot -label:issue-reporter -label:javascript -label:json -label:keybindings -label:keybindings-editor -label:keybindings-json -label:keyboard-layout -label:L10N -label:l10n-platform -label:label-provider -label:languages-basic -label:languages-diagnostics -label:languages-guessing -label:layout -label:lcd-text-rendering -label:list-widget -label:live-preview -label:log -label:markdown -label:marketplace -label:menus -label:merge-conflict -label:merge-editor -label:merge-editor-workbench -label:monaco-editor -label:native-file-dialog -label:network -label:notebook -label:notebook-api -label:notebook-builtin-renderers -label:notebook-cell-editor -label:notebook-celltoolbar -label:notebook-clipboard -label:notebook-commenting -label:notebook-debugging -label:notebook-diff -label:notebook-dnd -label:notebook-execution -label:notebook-find -label:notebook-folding -label:notebook-getting-started -label:notebook-globaltoolbar -label:notebook-ipynb -label:notebook-kernel -label:notebook-kernel-picker -label:notebook-keybinding -label:notebook-language -label:notebook-layout -label:notebook-markdown -label:notebook-math -label:notebook-minimap -label:notebook-multiselect -label:notebook-output -label:notebook-perf -label:notebook-remote -label:notebook-rendering -label:notebook-serialization -label:notebook-serverless-web -label:notebook-statusbar -label:notebook-toc-outline -label:notebook-undo-redo -label:notebook-variables -label:notebook-workbench-integration -label:notebook-workflow -label:open-editors -label:opener -label:outline -label:output -label:packaging -label:perf -label:perf-bloat -label:perf-startup -label:php -label:portable-mode -label:proxy -label:quick-open -label:quick-pick -label:references-viewlet -label:release-notes -label:remote -label:remote-connection -label:remote-explorer -label:remote-tunnel -label:rename -label:runCommands -label:sandbox -label:sash-widget -label:scm -label:screencast-mode -label:search -label:search-api -label:search-editor -label:search-replace -label:semantic-tokens -label:server -label:settings-editor -label:settings-sync -label:settings-sync-server -label:shared-process -label:simple-file-dialog -label:smart-select -label:snap -label:snippets -label:splitview-widget -label:ssh -label:suggest -label:table-widget -label:tasks -label:telemetry -label:terminal -label:terminal-accessibility -label:terminal-conpty -label:terminal-editors -label:terminal-external -label:terminal-find -label:terminal-input -label:terminal-layout -label:terminal-links -label:terminal-local-echo -label:terminal-persistence -label:terminal-process -label:terminal-profiles -label:terminal-quick-fix -label:terminal-rendering -label:terminal-search -label:terminal-shell-bash -label:terminal-shell-cmd -label:terminal-shell-fish -label:terminal-shell-git-bash -label:terminal-shell-integration -label:terminal-shell-pwsh -label:terminal-shell-zsh -label:terminal-tabs -label:terminal-winpty -label:testing -label:themes -label:timeline -label:timeline-git -label:timeline-local-history -label:tips-and-tricks -label:titlebar -label:tokenization -label:touch/pointer -label:trackpad/scroll -label:tree-views -label:tree-widget -label:typescript -label:undo-redo -label:unicode-highlight -label:untitled-editor-hint -label:uri -label:user-profiles -label:ux -label:variable-resolving -label:VIM -label:virtual-workspaces -label:vscode-website -label:vscode.dev -label:web -label:webview -label:webview-views -label:workbench-actions -label:workbench-banner -label:workbench-cli -label:workbench-diagnostics -label:workbench-dnd -label:workbench-editor-grid -label:workbench-editor-groups -label:workbench-editor-resolver -label:workbench-editors -label:workbench-electron -label:workbench-feedback -label:workbench-fonts -label:workbench-history -label:workbench-hot-exit -label:workbench-hover -label:workbench-launch -label:workbench-link -label:workbench-multiroot -label:workbench-notifications -label:workbench-os-integration -label:workbench-rapid-render -label:workbench-run-as-admin -label:workbench-state -label:workbench-status -label:workbench-tabs -label:workbench-touchbar -label:workbench-untitled-editors -label:workbench-views -label:workbench-welcome -label:workbench-window -label:workbench-workspace -label:workbench-zen -label:workspace-edit -label:workspace-symbols -label:workspace-trust -label:zoom" + "value": "repo:microsoft/vscode assignee:@me is:open type:issue -label:\"info-needed\" -label:api -label:api-finalization -label:api-proposal -label:authentication -label:bisect-ext -label:bracket-pair-colorization -label:bracket-pair-guides -label:breadcrumbs -label:callhierarchy -label:chrome-devtools -label:cloud-changes -label:code-lens -label:command-center -label:comments -label:config -label:containers -label:context-keys -label:continue-working-on -label:css-less-scss -label:custom-editors -label:debug -label:debug-disassembly -label:dialogs -label:diff-editor -label:dropdown -label:editor-api -label:editor-autoclosing -label:editor-autoindent -label:editor-bracket-matching -label:editor-clipboard -label:editor-code-actions -label:editor-color-picker -label:editor-columnselect -label:editor-commands -label:editor-comments -label:editor-contrib -label:editor-core -label:editor-drag-and-drop -label:editor-error-widget -label:editor-find -label:editor-folding -label:editor-highlight -label:editor-hover -label:editor-indent-detection -label:editor-indent-guides -label:editor-input -label:editor-input-IME -label:editor-insets -label:editor-minimap -label:editor-multicursor -label:editor-parameter-hints -label:editor-render-whitespace -label:editor-rendering -label:editor-RTL -label:editor-scrollbar -label:editor-sorting -label:editor-sticky-scroll -label:editor-symbols -label:editor-synced-region -label:editor-textbuffer -label:editor-theming -label:editor-wordnav -label:editor-wrapping -label:emmet -label:emmet-parse -label:error-list -label:extension-activation -label:extension-host -label:extension-prerelease -label:extension-recommendations -label:extensions -label:extensions-development -label:file-decorations -label:file-encoding -label:file-explorer -label:file-glob -label:file-io -label:file-nesting -label:file-watcher -label:font-rendering -label:formatting -label:getting-started -label:ghost-text -label:git -label:github -label:github-repositories -label:gpu -label:grammar -label:grid-widget -label:html -label:icon-brand -label:icons-product -label:image-preview -label:inlay-hints -label:inline-completions -label:install-update -label:intellisense-config -label:interactive-playground -label:interactive-window -label:issue-bot -label:issue-reporter -label:javascript -label:json -label:keybindings -label:keybindings-editor -label:keybindings-json -label:keyboard-layout -label:L10N -label:l10n-platform -label:label-provider -label:languages-basic -label:languages-diagnostics -label:languages-guessing -label:layout -label:lcd-text-rendering -label:list-widget -label:live-preview -label:log -label:markdown -label:marketplace -label:menus -label:merge-conflict -label:merge-editor -label:merge-editor-workbench -label:monaco-editor -label:native-file-dialog -label:network -label:notebook -label:notebook-api -label:notebook-builtin-renderers -label:notebook-cell-editor -label:notebook-celltoolbar -label:notebook-clipboard -label:notebook-commenting -label:notebook-debugging -label:notebook-diff -label:notebook-dnd -label:notebook-execution -label:notebook-find -label:notebook-folding -label:notebook-getting-started -label:notebook-globaltoolbar -label:notebook-ipynb -label:notebook-kernel -label:notebook-kernel-picker -label:notebook-language -label:notebook-layout -label:notebook-markdown -label:notebook-math -label:notebook-minimap -label:notebook-multiselect -label:notebook-output -label:notebook-perf -label:notebook-remote -label:notebook-rendering -label:notebook-serialization -label:notebook-serverless-web -label:notebook-statusbar -label:notebook-toc-outline -label:notebook-undo-redo -label:notebook-variables -label:notebook-workbench-integration -label:notebook-workflow -label:open-editors -label:opener -label:outline -label:output -label:packaging -label:perf -label:perf-bloat -label:perf-startup -label:php -label:portable-mode -label:proxy -label:quick-open -label:quick-pick -label:references-viewlet -label:release-notes -label:remote -label:remote-connection -label:remote-explorer -label:remote-tunnel -label:rename -label:runCommands -label:sandbox -label:sash-widget -label:scm -label:screencast-mode -label:search -label:search-api -label:search-editor -label:search-replace -label:semantic-tokens -label:server -label:settings-editor -label:settings-sync -label:settings-sync-server -label:shared-process -label:simple-file-dialog -label:smart-select -label:snap -label:snippets -label:splitview-widget -label:ssh -label:suggest -label:table-widget -label:tasks -label:telemetry -label:terminal -label:terminal-accessibility -label:terminal-conpty -label:terminal-editors -label:terminal-external -label:terminal-find -label:terminal-input -label:terminal-layout -label:terminal-links -label:terminal-local-echo -label:terminal-persistence -label:terminal-process -label:terminal-profiles -label:terminal-quick-fix -label:terminal-rendering -label:terminal-shell-bash -label:terminal-shell-cmd -label:terminal-shell-fish -label:terminal-shell-git-bash -label:terminal-shell-integration -label:terminal-shell-pwsh -label:terminal-shell-zsh -label:terminal-tabs -label:terminal-winpty -label:testing -label:themes -label:timeline -label:timeline-git -label:timeline-local-history -label:titlebar -label:tokenization -label:touch/pointer -label:trackpad/scroll -label:tree-views -label:tree-widget -label:typescript -label:undo-redo -label:unicode-highlight -label:uri -label:user-profiles -label:ux -label:variable-resolving -label:VIM -label:virtual-workspaces -label:vscode-website -label:vscode.dev -label:web -label:webview -label:webview-views -label:workbench-actions -label:workbench-banner -label:workbench-cli -label:workbench-diagnostics -label:workbench-dnd -label:workbench-editor-grid -label:workbench-editor-groups -label:workbench-editor-resolver -label:workbench-editors -label:workbench-electron -label:workbench-feedback -label:workbench-fonts -label:workbench-history -label:workbench-hot-exit -label:workbench-hover -label:workbench-launch -label:workbench-link -label:workbench-multiroot -label:workbench-notifications -label:workbench-os-integration -label:workbench-rapid-render -label:workbench-run-as-admin -label:workbench-state -label:workbench-status -label:workbench-tabs -label:workbench-touchbar -label:workbench-untitled-editors -label:workbench-views -label:workbench-welcome -label:workbench-window -label:workbench-workspace -label:workbench-zen -label:workspace-edit -label:workspace-symbols -label:workspace-trust -label:zoom -label:inline-chat" }, { "kind": 1, From ab6ceb06ce87e8d2873d82ff35f0224c703a7064 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 4 Jul 2023 13:41:40 +0200 Subject: [PATCH 145/926] fix #186488 (#186975) --- .../services/configuration/browser/configurationService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/services/configuration/browser/configurationService.ts b/src/vs/workbench/services/configuration/browser/configurationService.ts index 256144f8b57..153cb26e39d 100644 --- a/src/vs/workbench/services/configuration/browser/configurationService.ts +++ b/src/vs/workbench/services/configuration/browser/configurationService.ts @@ -981,7 +981,7 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat await this.configurationEditing.writeConfiguration(editableConfigurationTarget, { key, value }, { scopes: overrides, ...options }); switch (editableConfigurationTarget) { case EditableConfigurationTarget.USER_LOCAL: - if (this.applicationConfiguration && this.configurationRegistry.getConfigurationProperties()[key].scope === ConfigurationScope.APPLICATION) { + if (this.applicationConfiguration && this.configurationRegistry.getConfigurationProperties()[key]?.scope === ConfigurationScope.APPLICATION) { await this.reloadApplicationConfiguration(); } else { await this.reloadLocalUserConfiguration(); From 97a56d6d33399ad9c4e1ebb4203aae639e8500df Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 4 Jul 2023 13:42:00 +0200 Subject: [PATCH 146/926] fix color name spelling (#186974) --- src/vs/workbench/contrib/inlineChat/common/inlineChat.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts b/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts index e6bf0fdee93..e8bbb5cb940 100644 --- a/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts +++ b/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts @@ -154,7 +154,7 @@ export const inlineChatInputPlaceholderForeground = registerColor('inlineChatInp export const inlineChatInputBackground = registerColor('inlineChatInput.background', { dark: inputBackground, light: inputBackground, hcDark: inputBackground, hcLight: inputBackground }, localize('inlineChatInput.background', "Background color of the interactive editor input")); export const inlineChatDiffInserted = registerColor('inlineChatDiff.inserted', { dark: transparent(diffInserted, .5), light: transparent(diffInserted, .5), hcDark: transparent(diffInserted, .5), hcLight: transparent(diffInserted, .5) }, localize('inlineChatDiff.inserted', "Background color of inserted text in the interactive editor input")); -export const inlineChatDiffRemoved = registerColor('inlineChatrDiff.removed', { dark: transparent(diffRemoved, .5), light: transparent(diffRemoved, .5), hcDark: transparent(diffRemoved, .5), hcLight: transparent(diffRemoved, .5) }, localize('inlineChatDiff.removed', "Background color of removed text in the interactive editor input")); +export const inlineChatDiffRemoved = registerColor('inlineChatDiff.removed', { dark: transparent(diffRemoved, .5), light: transparent(diffRemoved, .5), hcDark: transparent(diffRemoved, .5), hcLight: transparent(diffRemoved, .5) }, localize('inlineChatDiff.removed', "Background color of removed text in the interactive editor input")); // settings From a2d301ddd6b818e9bc55a50ef7f2d84f063c5449 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 4 Jul 2023 17:21:25 +0200 Subject: [PATCH 147/926] :lipstick: from ESM bring over graceful check for require being defined before use (#186982) --- src/vs/workbench/browser/workbench.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/browser/workbench.ts b/src/vs/workbench/browser/workbench.ts index 5c056286444..28863e8e434 100644 --- a/src/vs/workbench/browser/workbench.ts +++ b/src/vs/workbench/browser/workbench.ts @@ -101,8 +101,8 @@ export class Workbench extends Layout { } type AnnotatedError = AnnotatedLoadingError | AnnotatedFactoryError | AnnotatedValidationError; - if (typeof (window).require.config === 'function') { - (window).require.config({ + if (typeof window.require?.config === 'function') { + window.require.config({ onError: (err: AnnotatedError) => { if (err.phase === 'loading') { onUnexpectedError(new Error(localize('loaderErrorNative', "Failed to load a required file. Please restart the application to try again. Details: {0}", JSON.stringify(err)))); From 667bc1657a5e45f73576fffe32cf690d4f4882bf Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 4 Jul 2023 17:21:52 +0200 Subject: [PATCH 148/926] cleanup (#186985) --- .../contrib/extensions/browser/extensionEditor.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index 5408889b97b..366c4d28382 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -944,26 +944,24 @@ export class ExtensionEditor extends EditorPane { const moreInfoContainer = append(container, $('.more-info-container.additional-details-element')); append(moreInfoContainer, $('.additional-details-title', undefined, localize('Marketplace Info', "More Info"))); const moreInfo = append(moreInfoContainer, $('.more-info')); + const toDateString = (date: Date) => `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}, ${date.toLocaleTimeString(language, { hourCycle: 'h23' })}`; if (gallery) { - const pub = new Date(gallery.releaseDate); - const last_rel = new Date(gallery.lastUpdated); append(moreInfo, $('.more-info-entry', undefined, $('div', undefined, localize('published', "Published")), - $('div', undefined, [pub.getFullYear(), pub.getMonth() + 1, pub.getDate()].join('-').concat(', ' + pub.toLocaleTimeString(language, { hourCycle: 'h23' }))) + $('div', undefined, toDateString(new Date(gallery.releaseDate))) ), $('.more-info-entry', undefined, $('div', undefined, localize('last released', "Last released")), - $('div', undefined, [last_rel.getFullYear(), last_rel.getMonth() + 1, last_rel.getDate()].join('-').concat(', ' + last_rel.toLocaleTimeString(language, { hourCycle: 'h23' }))) + $('div', undefined, toDateString(new Date(gallery.lastUpdated))) ) ); } if (extension.local && extension.local.installedTimestamp) { - const last_up = new Date(extension.local.installedTimestamp); append(moreInfo, $('.more-info-entry', undefined, $('div', undefined, localize('last updated', "Last updated")), - $('div', undefined, [last_up.getFullYear(), last_up.getMonth() + 1, last_up.getDate()].join('-').concat(', ' + last_up.toLocaleTimeString(language, { hourCycle: 'h23' }))) + $('div', undefined, toDateString(new Date(extension.local.installedTimestamp))) ) ); } From 5795eec487201411b4ea333e8c4f17450a7ecead Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 4 Jul 2023 17:47:13 +0200 Subject: [PATCH 149/926] add resource timing stats, make require check graceful (#186978) --- src/vs/base/common/amd.ts | 2 +- .../performance/browser/perfviewEditor.ts | 23 +++++++++++++++---- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/vs/base/common/amd.ts b/src/vs/base/common/amd.ts index d5aa099d6b8..2ee86a98646 100644 --- a/src/vs/base/common/amd.ts +++ b/src/vs/base/common/amd.ts @@ -48,7 +48,7 @@ export abstract class LoaderStats { } let stats: readonly LoaderEvent[] = []; - if (typeof require.getStats === 'function') { + if (typeof require === 'function' && typeof require.getStats === 'function') { stats = require.getStats().slice(0).sort((a, b) => a.timestamp - b.timestamp); } diff --git a/src/vs/workbench/contrib/performance/browser/perfviewEditor.ts b/src/vs/workbench/contrib/performance/browser/perfviewEditor.ts index 758d6c2206e..e953502efac 100644 --- a/src/vs/workbench/contrib/performance/browser/perfviewEditor.ts +++ b/src/vs/workbench/contrib/performance/browser/perfviewEditor.ts @@ -17,7 +17,7 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { writeTransientState } from 'vs/workbench/contrib/codeEditor/browser/toggleWordWrap'; -import { LoaderStats } from 'vs/base/common/amd'; +import { LoaderStats, isESM } from 'vs/base/common/amd'; import { IProductService } from 'vs/platform/product/common/productService'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -132,10 +132,14 @@ class PerfModelContentProvider implements ITextModelContentProvider { this._addPerfMarksTable('Terminal Stats', md, this._timerService.getPerformanceMarks().find(e => e[0] === 'renderer')?.[1].filter(e => e.name.startsWith('code/terminal/'))); md.blank(); this._addRawPerfMarks(md); + if (!isESM) { + md.blank(); + this._addLoaderStats(md, stats); + md.blank(); + this._addCachedDataStats(md); + } md.blank(); - this._addLoaderStats(md, stats); - md.blank(); - this._addCachedDataStats(md); + this._addResourceTimingStats(md); this._model.setValue(md.value); } @@ -311,6 +315,17 @@ class PerfModelContentProvider implements ITextModelContentProvider { md.heading(3, 'cached data created (lazy, might need refreshes)'); printLists(map.get(LoaderEventType.CachedDataCreated)); } + + private _addResourceTimingStats(md: MarkdownBuilder) { + const stats = performance.getEntriesByType('resource').map(entry => { + return [entry.name, entry.duration]; + }); + if (!stats.length) { + return; + } + md.heading(2, 'Resource Timing Stats'); + md.table(['Name', 'Duration'], stats); + } } class MarkdownBuilder { From 9277451e04e1958bb11ed593b9570aee8684e3da Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Tue, 4 Jul 2023 19:43:03 +0200 Subject: [PATCH 150/926] Split up lines (#187001) --- .../browser/widget/diffEditorWidget2/diffEditorViewModel.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts index c7e2cebb687..002e062a506 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts @@ -158,7 +158,8 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo } this._lastDiff = result; - this._diff.set(DiffState.fromDiffResult(result), tx); + const state = DiffState.fromDiffResult(result); + this._diff.set(state, tx); this._isDiffUpToDate.set(true, tx); const currentSyncedMovedText = this.syncedMovedTexts.get(); this.syncedMovedTexts.set(currentSyncedMovedText ? this._lastDiff.moves.find(m => m.lineRangeMapping.modifiedRange.intersect(currentSyncedMovedText.lineRangeMapping.modifiedRange)) : undefined, tx); From 3df09039874b4a13e7396d46fd42d40dcc139fe5 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Tue, 4 Jul 2023 19:43:43 +0200 Subject: [PATCH 151/926] Fixes #185780 (#186999) --- .../diffEditorWidget2/overviewRulerPart.ts | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/overviewRulerPart.ts b/src/vs/editor/browser/widget/diffEditorWidget2/overviewRulerPart.ts index 7f43570c98c..e1e0f6ce8c5 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/overviewRulerPart.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/overviewRulerPart.ts @@ -6,6 +6,7 @@ import { EventType, addDisposableListener, addStandardDisposableListener, h } from 'vs/base/browser/dom'; import { createFastDomNode } from 'vs/base/browser/fastDomNode'; import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; +import { ScrollbarState } from 'vs/base/browser/ui/scrollbar/scrollbarState'; import { Color } from 'vs/base/common/color'; import { Disposable } from 'vs/base/common/lifecycle'; import { IObservable, autorun, derived, observableFromEvent, observableSignalFromEvent } from 'vs/base/common/observable'; @@ -14,7 +15,7 @@ import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; import { DiffEditorEditors } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors'; import { DiffEditorViewModel } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel'; import { appendRemoveOnDispose } from 'vs/editor/browser/widget/diffEditorWidget2/utils'; -import { EditorLayoutInfo } from 'vs/editor/common/config/editorOptions'; +import { EditorLayoutInfo, EditorOption } from 'vs/editor/common/config/editorOptions'; import { LineRange } from 'vs/editor/common/core/lineRange'; import { Position } from 'vs/editor/common/core/position'; import { OverviewRulerZone } from 'vs/editor/common/viewModel/overviewZoneManager'; @@ -146,15 +147,18 @@ export class OverviewRulerPart extends Disposable { const scrollTop = scrollTopObservable.read(reader); const scrollHeight = scrollHeightObservable.read(reader); - const computedAvailableSize = Math.max(0, layoutInfo.height); - const computedRepresentableSize = Math.max(0, computedAvailableSize - 2 * 0); - const computedRatio = scrollHeight > 0 ? (computedRepresentableSize / scrollHeight) : 0; + const scrollBarOptions = this._editors.modified.getOption(EditorOption.scrollbar); + const state = new ScrollbarState( + scrollBarOptions.verticalHasArrows ? scrollBarOptions.arrowSize : 0, + scrollBarOptions.verticalScrollbarSize, + 0, + layoutInfo.height, + scrollHeight, + scrollTop + ); - const computedSliderSize = Math.max(0, Math.floor(layoutInfo.height * computedRatio)); - const computedSliderPosition = Math.floor(scrollTop * computedRatio); - - viewportDomElement.setTop(computedSliderPosition); - viewportDomElement.setHeight(computedSliderSize); + viewportDomElement.setTop(state.getSliderPosition()); + viewportDomElement.setHeight(state.getSliderSize()); } else { viewportDomElement.setTop(0); viewportDomElement.setHeight(0); From 854108f7d2a8b40aa1ac0390f95088e61153ebc5 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Tue, 4 Jul 2023 19:44:14 +0200 Subject: [PATCH 152/926] Fixes #186335 (#186997) --- src/vs/editor/browser/widget/codeEditorWidget.ts | 6 +++++- .../widget/diffEditorWidget2/diffEditorEditors.ts | 10 ++-------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index 59764b9ea97..e78be4bd6b7 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -42,7 +42,7 @@ import { editorErrorForeground, editorHintForeground, editorInfoForeground, edit import { VerticalRevealType } from 'vs/editor/common/viewEvents'; import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl'; import { ICommandService } from 'vs/platform/commands/common/commands'; -import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { ContextKeyValue, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; @@ -1911,6 +1911,10 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE private removeDropIndicator(): void { this._dropIntoEditorDecorations.clear(); } + + public setContextValue(key: string, value: ContextKeyValue): void { + this._contextKeyService.createKey(key, value); + } } const enum BooleanEventValue { diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors.ts index a3c39014157..ac29835d101 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors.ts @@ -13,7 +13,6 @@ import { OverviewRulerPart } from 'vs/editor/browser/widget/diffEditorWidget2/ov import { EditorOptions, IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { IContentSizeChangedEvent } from 'vs/editor/common/editorCommon'; import { localize } from 'vs/nls'; -import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { DiffEditorOptions } from './diffEditorOptions'; @@ -30,7 +29,6 @@ export class DiffEditorEditors extends Disposable { private readonly _options: DiffEditorOptions, codeEditorWidgetOptions: IDiffCodeEditorWidgetOptions, private readonly _createInnerEditor: (instantiationService: IInstantiationService, container: HTMLElement, options: Readonly, editorWidgetOptions: ICodeEditorWidgetOptions) => CodeEditorWidget, - @IContextKeyService private readonly _contextKeyService: IContextKeyService, @IInstantiationService private readonly _instantiationService: IInstantiationService ) { super(); @@ -56,17 +54,13 @@ export class DiffEditorEditors extends Disposable { private _createLeftHandSideEditor(options: Readonly, codeEditorWidgetOptions: ICodeEditorWidgetOptions): CodeEditorWidget { const editor = this._constructInnerEditor(this._instantiationService, this.originalEditorElement, this._adjustOptionsForLeftHandSide(options), codeEditorWidgetOptions); - const isInDiffLeftEditorKey = this._contextKeyService.createKey('isInDiffLeftEditor', editor.hasWidgetFocus()); - this._register(editor.onDidFocusEditorWidget(() => isInDiffLeftEditorKey.set(true))); - this._register(editor.onDidBlurEditorWidget(() => isInDiffLeftEditorKey.set(false))); + editor.setContextValue('isInDiffLeftEditor', true); return editor; } private _createRightHandSideEditor(options: Readonly, codeEditorWidgetOptions: ICodeEditorWidgetOptions): CodeEditorWidget { const editor = this._constructInnerEditor(this._instantiationService, this.modifiedEditorElement, this._adjustOptionsForRightHandSide(options), codeEditorWidgetOptions); - const isInDiffRightEditorKey = this._contextKeyService.createKey('isInDiffRightEditor', editor.hasWidgetFocus()); - this._register(editor.onDidFocusEditorWidget(() => isInDiffRightEditorKey.set(true))); - this._register(editor.onDidBlurEditorWidget(() => isInDiffRightEditorKey.set(false))); + editor.setContextValue('isInDiffRightEditor', true); return editor; } From dca4956cc32c19519ed46d6974b1826c765e3ad2 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Tue, 4 Jul 2023 19:46:45 +0200 Subject: [PATCH 153/926] Fixes #186648 (#186994) --- .../common/diff/standardLinesDiffComputer.ts | 19 ++++++++++++ .../test/node/diffing/fixtures/deletion/1.tst | 29 +++++++++++++++++++ .../test/node/diffing/fixtures/deletion/2.tst | 0 .../deletion/advanced.expected.diff.json | 22 ++++++++++++++ .../deletion/legacy.expected.diff.json | 17 +++++++++++ 5 files changed, 87 insertions(+) create mode 100644 src/vs/editor/test/node/diffing/fixtures/deletion/1.tst create mode 100644 src/vs/editor/test/node/diffing/fixtures/deletion/2.tst create mode 100644 src/vs/editor/test/node/diffing/fixtures/deletion/advanced.expected.diff.json create mode 100644 src/vs/editor/test/node/diffing/fixtures/deletion/legacy.expected.diff.json diff --git a/src/vs/editor/common/diff/standardLinesDiffComputer.ts b/src/vs/editor/common/diff/standardLinesDiffComputer.ts index 876506a60e2..51f62509b02 100644 --- a/src/vs/editor/common/diff/standardLinesDiffComputer.ts +++ b/src/vs/editor/common/diff/standardLinesDiffComputer.ts @@ -20,6 +20,25 @@ export class StandardLinesDiffComputer implements ILinesDiffComputer { private readonly myersDiffingAlgorithm = new MyersDiffAlgorithm(); computeDiff(originalLines: string[], modifiedLines: string[], options: ILinesDiffComputerOptions): LinesDiff { + if (originalLines.length === 1 && originalLines[0].length === 0 || modifiedLines.length === 1 && modifiedLines[0].length === 0) { + return { + changes: [ + new LineRangeMapping( + new LineRange(1, originalLines.length + 1), + new LineRange(1, modifiedLines.length + 1), + [ + new RangeMapping( + new Range(1, 1, originalLines.length, originalLines[0].length + 1), + new Range(1, 1, modifiedLines.length, modifiedLines[0].length + 1) + ) + ] + ) + ], + hitTimeout: false, + moves: [], + }; + } + const timeout = options.maxComputationTimeMs === 0 ? InfiniteTimeout.instance : new DateTimeout(options.maxComputationTimeMs); const considerWhitespaceChanges = !options.ignoreTrimWhitespace; diff --git a/src/vs/editor/test/node/diffing/fixtures/deletion/1.tst b/src/vs/editor/test/node/diffing/fixtures/deletion/1.tst new file mode 100644 index 00000000000..67dec2f85b3 --- /dev/null +++ b/src/vs/editor/test/node/diffing/fixtures/deletion/1.tst @@ -0,0 +1,29 @@ +import { Link, List, Separator, Stack } from '@fluentui/react'; +import { View } from '../../layout/layout'; + +export const OtherToolsView = () => { + return ( + + + + { + if (!item?.name) { + return + } + return
{item!.name}
+ }} + > +
+
+
+
+ ); +} \ No newline at end of file diff --git a/src/vs/editor/test/node/diffing/fixtures/deletion/2.tst b/src/vs/editor/test/node/diffing/fixtures/deletion/2.tst new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/vs/editor/test/node/diffing/fixtures/deletion/advanced.expected.diff.json b/src/vs/editor/test/node/diffing/fixtures/deletion/advanced.expected.diff.json new file mode 100644 index 00000000000..77b3553a99c --- /dev/null +++ b/src/vs/editor/test/node/diffing/fixtures/deletion/advanced.expected.diff.json @@ -0,0 +1,22 @@ +{ + "original": { + "content": "import { Link, List, Separator, Stack } from '@fluentui/react';\nimport { View } from '../../layout/layout';\n\nexport const OtherToolsView = () => {\n\treturn (\n\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t {\n\t\t\t\t\t\t\tif (!item?.name) {\n\t\t\t\t\t\t\t\treturn \n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn
{item!.name}
\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t
\n\t\t
\n\t);\n}", + "fileName": "./1.tst" + }, + "modified": { + "content": "", + "fileName": "./2.tst" + }, + "diffs": [ + { + "originalRange": "[1,30)", + "modifiedRange": "[1,2)", + "innerChanges": [ + { + "originalRange": "[1,1 -> 29,64]", + "modifiedRange": "[1,1 -> 1,1]" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/vs/editor/test/node/diffing/fixtures/deletion/legacy.expected.diff.json b/src/vs/editor/test/node/diffing/fixtures/deletion/legacy.expected.diff.json new file mode 100644 index 00000000000..c114d7e583e --- /dev/null +++ b/src/vs/editor/test/node/diffing/fixtures/deletion/legacy.expected.diff.json @@ -0,0 +1,17 @@ +{ + "original": { + "content": "import { Link, List, Separator, Stack } from '@fluentui/react';\nimport { View } from '../../layout/layout';\n\nexport const OtherToolsView = () => {\n\treturn (\n\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t {\n\t\t\t\t\t\t\tif (!item?.name) {\n\t\t\t\t\t\t\t\treturn \n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn
{item!.name}
\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t
\n\t\t
\n\t);\n}", + "fileName": "./1.tst" + }, + "modified": { + "content": "", + "fileName": "./2.tst" + }, + "diffs": [ + { + "originalRange": "[1,30)", + "modifiedRange": "[1,2)", + "innerChanges": null + } + ] +} \ No newline at end of file From d607e9d07da223d1cd62a54e9049b3ad5d2489b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Tue, 4 Jul 2023 21:42:03 +0200 Subject: [PATCH 154/926] show notification when running as admin on user setup (#186986) fixes #186960 --- .../platform/menubar/electron-main/menubar.ts | 6 +++--- src/vs/platform/update/common/update.ts | 14 ++++++++++++- .../electron-main/abstractUpdateService.ts | 7 ++++++- .../electron-main/updateService.win32.ts | 3 ++- .../browser/parts/titlebar/menubarControl.ts | 6 +++--- .../contrib/update/browser/update.ts | 21 ++++++++++++++++++- 6 files changed, 47 insertions(+), 10 deletions(-) diff --git a/src/vs/platform/menubar/electron-main/menubar.ts b/src/vs/platform/menubar/electron-main/menubar.ts index d530756c5b5..3b3af1cb760 100644 --- a/src/vs/platform/menubar/electron-main/menubar.ts +++ b/src/vs/platform/menubar/electron-main/menubar.ts @@ -596,9 +596,6 @@ export class Menubar { const state = this.updateService.state; switch (state.type) { - case StateType.Uninitialized: - return []; - case StateType.Idle: return [new MenuItem({ label: this.mnemonicLabel(nls.localize('miCheckForUpdates', "Check for &&Updates...")), click: () => setTimeout(() => { @@ -638,6 +635,9 @@ export class Menubar { this.updateService.quitAndInstall(); } })]; + + default: + return []; } } diff --git a/src/vs/platform/update/common/update.ts b/src/vs/platform/update/common/update.ts index 7cd4a84084e..6fba75ee261 100644 --- a/src/vs/platform/update/common/update.ts +++ b/src/vs/platform/update/common/update.ts @@ -35,6 +35,7 @@ export interface IUpdate { export const enum StateType { Uninitialized = 'uninitialized', Idle = 'idle', + Disabled = 'disabled', CheckingForUpdates = 'checking for updates', AvailableForDownload = 'available for download', Downloading = 'downloading', @@ -49,7 +50,17 @@ export const enum UpdateType { Snap } +export const enum DisablementReason { + NotBuilt, + DisabledByEnvironment, + ManuallyDisabled, + MissingConfiguration, + InvalidConfiguration, + RunningAsAdmin, +} + export type Uninitialized = { type: StateType.Uninitialized }; +export type Disabled = { type: StateType.Disabled; reason: DisablementReason }; export type Idle = { type: StateType.Idle; updateType: UpdateType; error?: string }; export type CheckingForUpdates = { type: StateType.CheckingForUpdates; explicit: boolean }; export type AvailableForDownload = { type: StateType.AvailableForDownload; update: IUpdate }; @@ -58,10 +69,11 @@ export type Downloaded = { type: StateType.Downloaded; update: IUpdate }; export type Updating = { type: StateType.Updating; update: IUpdate }; export type Ready = { type: StateType.Ready; update: IUpdate }; -export type State = Uninitialized | Idle | CheckingForUpdates | AvailableForDownload | Downloading | Downloaded | Updating | Ready; +export type State = Uninitialized | Disabled | Idle | CheckingForUpdates | AvailableForDownload | Downloading | Downloaded | Updating | Ready; export const State = { Uninitialized: { type: StateType.Uninitialized } as Uninitialized, + Disabled: (reason: DisablementReason) => ({ type: StateType.Disabled, reason }) as Disabled, Idle: (updateType: UpdateType, error?: string) => ({ type: StateType.Idle, updateType, error }) as Idle, CheckingForUpdates: (explicit: boolean) => ({ type: StateType.CheckingForUpdates, explicit } as CheckingForUpdates), AvailableForDownload: (update: IUpdate) => ({ type: StateType.AvailableForDownload, update } as AvailableForDownload), diff --git a/src/vs/platform/update/electron-main/abstractUpdateService.ts b/src/vs/platform/update/electron-main/abstractUpdateService.ts index 42bb9cd047c..8555808caf9 100644 --- a/src/vs/platform/update/electron-main/abstractUpdateService.ts +++ b/src/vs/platform/update/electron-main/abstractUpdateService.ts @@ -12,7 +12,7 @@ import { ILifecycleMainService, LifecycleMainPhase } from 'vs/platform/lifecycle import { ILogService } from 'vs/platform/log/common/log'; import { IProductService } from 'vs/platform/product/common/productService'; import { IRequestService } from 'vs/platform/request/common/request'; -import { AvailableForDownload, IUpdateService, State, StateType, UpdateType } from 'vs/platform/update/common/update'; +import { AvailableForDownload, DisablementReason, IUpdateService, State, StateType, UpdateType } from 'vs/platform/update/common/update'; export function createUpdateURL(platform: string, quality: string, productService: IProductService): string { return `${productService.updateUrl}/api/update/${platform}/${quality}/${productService.commit}`; @@ -64,15 +64,18 @@ export abstract class AbstractUpdateService implements IUpdateService { */ protected async initialize(): Promise { if (!this.environmentMainService.isBuilt) { + this.setState(State.Disabled(DisablementReason.NotBuilt)); return; // updates are never enabled when running out of sources } if (this.environmentMainService.disableUpdates) { + this.setState(State.Disabled(DisablementReason.DisabledByEnvironment)); this.logService.info('update#ctor - updates are disabled by the environment'); return; } if (!this.productService.updateUrl || !this.productService.commit) { + this.setState(State.Disabled(DisablementReason.MissingConfiguration)); this.logService.info('update#ctor - updates are disabled as there is no update URL'); return; } @@ -81,12 +84,14 @@ export abstract class AbstractUpdateService implements IUpdateService { const quality = this.getProductQuality(updateMode); if (!quality) { + this.setState(State.Disabled(DisablementReason.ManuallyDisabled)); this.logService.info('update#ctor - updates are disabled by user preference'); return; } this.url = this.buildUpdateFeedUrl(quality); if (!this.url) { + this.setState(State.Disabled(DisablementReason.InvalidConfiguration)); this.logService.info('update#ctor - updates are disabled as the update URL is badly formed'); return; } diff --git a/src/vs/platform/update/electron-main/updateService.win32.ts b/src/vs/platform/update/electron-main/updateService.win32.ts index d45291e67e6..90e6a847ac4 100644 --- a/src/vs/platform/update/electron-main/updateService.win32.ts +++ b/src/vs/platform/update/electron-main/updateService.win32.ts @@ -22,7 +22,7 @@ import { INativeHostMainService } from 'vs/platform/native/electron-main/nativeH import { IProductService } from 'vs/platform/product/common/productService'; import { asJson, IRequestService } from 'vs/platform/request/common/request'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { AvailableForDownload, IUpdate, State, StateType, UpdateType } from 'vs/platform/update/common/update'; +import { AvailableForDownload, DisablementReason, IUpdate, State, StateType, UpdateType } from 'vs/platform/update/common/update'; import { AbstractUpdateService, createUpdateURL, UpdateNotAvailableClassification } from 'vs/platform/update/electron-main/abstractUpdateService'; async function pollUntil(fn: () => boolean, millis = 1000): Promise { @@ -73,6 +73,7 @@ export class Win32UpdateService extends AbstractUpdateService { protected override async initialize(): Promise { if (this.productService.target === 'user' && await this.nativeHostMainService.isAdmin(undefined)) { + this.setState(State.Disabled(DisablementReason.RunningAsAdmin)); this.logService.info('update#ctor - updates are disabled due to running as Admin in user setup'); return; } diff --git a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts index b27294ca65e..e96e773a916 100644 --- a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts +++ b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts @@ -459,9 +459,6 @@ export class CustomMenubarControl extends MenubarControl { const state = this.updateService.state; switch (state.type) { - case StateType.Uninitialized: - return null; - case StateType.Idle: return new Action('update.check', localize({ key: 'checkForUpdates', comment: ['&& denotes a mnemonic'] }, "Check for &&Updates..."), undefined, true, () => this.updateService.checkForUpdates(true)); @@ -486,6 +483,9 @@ export class CustomMenubarControl extends MenubarControl { case StateType.Ready: return new Action('update.restart', localize({ key: 'restartToUpdate', comment: ['&& denotes a mnemonic'] }, "Restart to &&Update"), undefined, true, () => this.updateService.quitAndInstall()); + + default: + return null; } } diff --git a/src/vs/workbench/contrib/update/browser/update.ts b/src/vs/workbench/contrib/update/browser/update.ts index 51000990668..a43125b04a6 100644 --- a/src/vs/workbench/contrib/update/browser/update.ts +++ b/src/vs/workbench/contrib/update/browser/update.ts @@ -12,7 +12,7 @@ import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiati import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; -import { IUpdateService, State as UpdateState, StateType, IUpdate } from 'vs/platform/update/common/update'; +import { IUpdateService, State as UpdateState, StateType, IUpdate, DisablementReason } from 'vs/platform/update/common/update'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService'; @@ -29,6 +29,7 @@ import { IsWebContext } from 'vs/platform/contextkey/common/contextkeys'; import { Promises } from 'vs/base/common/async'; import { IUserDataSyncWorkbenchService } from 'vs/workbench/services/userDataSync/common/userDataSync'; import { Event } from 'vs/base/common/event'; +import { Action } from 'vs/base/common/actions'; export const CONTEXT_UPDATE_STATE = new RawContextKey('updateState', StateType.Uninitialized); export const RELEASE_NOTES_URL = new RawContextKey('releaseNotesUrl', ''); @@ -169,6 +170,7 @@ export class UpdateContribution extends Disposable implements IWorkbenchContribu @IActivityService private readonly activityService: IActivityService, @IContextKeyService private readonly contextKeyService: IContextKeyService, @IProductService private readonly productService: IProductService, + @IOpenerService private readonly openerService: IOpenerService, @IHostService private readonly hostService: IHostService ) { super(); @@ -202,6 +204,23 @@ export class UpdateContribution extends Disposable implements IWorkbenchContribu this.updateStateContextKey.set(state.type); switch (state.type) { + case StateType.Disabled: + if (state.reason === DisablementReason.RunningAsAdmin) { + this.notificationService.notify({ + severity: Severity.Info, + message: nls.localize('update service disabled', "Updates are disabled because you are running the user-scope installation of {0} as Administrator.", this.productService.nameLong), + actions: { + primary: [ + new Action('', nls.localize('learn more', "Learn More"), undefined, undefined, () => { + this.openerService.open('https://aka.ms/vscode-windows-setup'); + }) + ] + }, + neverShowAgain: { id: 'no-updates-running-as-admin', } + }); + } + break; + case StateType.Idle: if (state.error) { this.onError(state.error); From 9e8df9f437175242bba34ffba11e43502adebc3c Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 4 Jul 2023 21:52:34 +0200 Subject: [PATCH 155/926] fix #182041 #173141 (#187005) --- src/vs/server/node/remoteExtensionsScanner.ts | 13 +-- .../extensionRecommendationsService.ts | 19 +-- .../extensionRecommendationsService.test.ts | 110 +++++++++--------- 3 files changed, 69 insertions(+), 73 deletions(-) diff --git a/src/vs/server/node/remoteExtensionsScanner.ts b/src/vs/server/node/remoteExtensionsScanner.ts index fe786a3add4..95e9c62afc8 100644 --- a/src/vs/server/node/remoteExtensionsScanner.ts +++ b/src/vs/server/node/remoteExtensionsScanner.ts @@ -28,7 +28,8 @@ export class RemoteExtensionsScannerService implements IRemoteExtensionsScannerS readonly _serviceBrand: undefined; - private readonly _whenExtensionsReady: Promise; + private readonly _whenBuiltinExtensionsReady = Promise.resolve(); + private readonly _whenExtensionsReady = Promise.resolve(); constructor( private readonly _extensionManagementCLI: ExtensionManagementCLI, @@ -44,21 +45,19 @@ export class RemoteExtensionsScannerService implements IRemoteExtensionsScannerS _logService.trace('Installing builtin extensions passed via args...'); const installOptions: InstallOptions = { isMachineScoped: !!environmentService.args['do-not-sync'], installPreReleaseVersion: !!environmentService.args['pre-release'] }; performance.mark('code/server/willInstallBuiltinExtensions'); - this._whenExtensionsReady = _extensionManagementCLI.installExtensions([], this._asExtensionIdOrVSIX(builtinExtensionsToInstall), installOptions, !!environmentService.args['force']) + this._whenExtensionsReady = this._whenBuiltinExtensionsReady = _extensionManagementCLI.installExtensions([], this._asExtensionIdOrVSIX(builtinExtensionsToInstall), installOptions, !!environmentService.args['force']) .then(() => { performance.mark('code/server/didInstallBuiltinExtensions'); _logService.trace('Finished installing builtin extensions'); }, error => { _logService.error(error); }); - } else { - this._whenExtensionsReady = Promise.resolve(); } const extensionsToInstall = environmentService.args['install-extension']; if (extensionsToInstall) { _logService.trace('Installing extensions passed via args...'); - this._whenExtensionsReady + this._whenExtensionsReady = this._whenBuiltinExtensionsReady .then(() => _extensionManagementCLI.installExtensions(this._asExtensionIdOrVSIX(extensionsToInstall), [], { isMachineScoped: !!environmentService.args['do-not-sync'], installPreReleaseVersion: !!environmentService.args['pre-release'], @@ -84,7 +83,7 @@ export class RemoteExtensionsScannerService implements IRemoteExtensionsScannerS performance.mark('code/server/willScanExtensions'); this._logService.trace(`Scanning extensions using UI language: ${language}`); - await this.whenExtensionsReady(); + await this._whenBuiltinExtensionsReady; const extensionDevelopmentPaths = extensionDevelopmentLocations ? extensionDevelopmentLocations.filter(url => url.scheme === Schemas.file).map(url => url.fsPath) : undefined; profileLocation = profileLocation ?? this._userDataProfilesService.defaultProfile.extensionsResource; @@ -99,7 +98,7 @@ export class RemoteExtensionsScannerService implements IRemoteExtensionsScannerS } async scanSingleExtension(extensionLocation: URI, isBuiltin: boolean, language?: string): Promise { - await this.whenExtensionsReady(); + await this._whenBuiltinExtensionsReady; const extensionPath = extensionLocation.scheme === Schemas.file ? extensionLocation.fsPath : null; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts index 95ed264b025..3dd31a217ff 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts @@ -26,6 +26,8 @@ import { WebRecommendations } from 'vs/workbench/contrib/extensions/browser/webR import { IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { RemoteRecommendations } from 'vs/workbench/contrib/extensions/browser/remoteRecommendations'; +import { IRemoteExtensionsScannerService } from 'vs/platform/remote/common/remoteExtensionsScanner'; +import { IUserDataInitializationService } from 'vs/workbench/services/userData/browser/userDataInit'; type IgnoreRecommendationClassification = { owner: 'sandy081'; @@ -64,6 +66,8 @@ export class ExtensionRecommendationsService extends Disposable implements IExte @IExtensionIgnoredRecommendationsService private readonly extensionRecommendationsManagementService: IExtensionIgnoredRecommendationsService, @IExtensionRecommendationNotificationService private readonly extensionRecommendationNotificationService: IExtensionRecommendationNotificationService, @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, + @IRemoteExtensionsScannerService private readonly remoteExtensionsScannerService: IRemoteExtensionsScannerService, + @IUserDataInitializationService private readonly userDataInitializationService: IUserDataInitializationService, ) { super(); @@ -91,7 +95,12 @@ export class ExtensionRecommendationsService extends Disposable implements IExte } private async activate(): Promise { - await this.lifecycleService.when(LifecyclePhase.Restored); + try { + await Promise.allSettled([ + this.remoteExtensionsScannerService.whenExtensionsReady(), + this.userDataInitializationService.whenInitializationFinished(), + this.lifecycleService.when(LifecyclePhase.Restored)]); + } catch (error) { /* ignore */ } // activate all recommendations await Promise.all([ @@ -256,12 +265,6 @@ export class ExtensionRecommendationsService extends Disposable implements IExte return !this.extensionRecommendationsManagementService.ignoredRecommendations.includes(extensionId.toLowerCase()); } - // for testing - protected get workbenchRecommendationDelay() { - // remote extensions might still being installed #124119 - return 5000; - } - private async promptWorkspaceRecommendations(): Promise { const installed = await this.extensionsWorkbenchService.queryLocal(); const allowedRecommendations = [ @@ -273,7 +276,7 @@ export class ExtensionRecommendationsService extends Disposable implements IExte .filter(extensionId => this.isExtensionAllowedToBeRecommended(extensionId)); if (allowedRecommendations.length) { - await timeout(this.workbenchRecommendationDelay); + await timeout(5000); await this.extensionRecommendationNotificationService.promptWorkspaceRecommendations(allowedRecommendations); } } diff --git a/src/vs/workbench/contrib/extensions/test/electron-sandbox/extensionRecommendationsService.test.ts b/src/vs/workbench/contrib/extensions/test/electron-sandbox/extensionRecommendationsService.test.ts index 6b4dd35354d..49f617e7019 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-sandbox/extensionRecommendationsService.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-sandbox/extensionRecommendationsService.test.ts @@ -60,6 +60,7 @@ import { joinPath } from 'vs/base/common/resources'; import { VSBuffer } from 'vs/base/common/buffer'; import { platform } from 'vs/base/common/platform'; import { arch } from 'vs/base/common/process'; +import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler'; const mockExtensionGallery: IGalleryExtension[] = [ aGalleryExtension('MockExtension1', { @@ -178,12 +179,6 @@ function aGalleryExtension(name: string, properties: any = {}, galleryExtensionP return galleryExtension; } -class TestExtensionRecommendationsService extends ExtensionRecommendationsService { - protected override get workbenchRecommendationDelay() { - return 0; - } -} - suite('ExtensionRecommendationsService Test', () => { let workspaceService: IWorkspaceContextService; let instantiationService: TestInstantiationService; @@ -346,7 +341,7 @@ suite('ExtensionRecommendationsService Test', () => { function testNoPromptForValidRecommendations(recommendations: string[]) { return setUpFolderWorkspace('myFolder', recommendations).then(() => { - testObject = instantiationService.createInstance(TestExtensionRecommendationsService); + testObject = instantiationService.createInstance(ExtensionRecommendationsService); return testObject.activationPromise.then(() => { assert.strictEqual(Object.keys(testObject.getAllRecommendationsWithReason()).length, recommendations.length); assert.ok(!prompted); @@ -356,7 +351,7 @@ suite('ExtensionRecommendationsService Test', () => { function testNoPromptOrRecommendationsForValidRecommendations(recommendations: string[]) { return setUpFolderWorkspace('myFolder', mockTestData.validRecommendedExtensions).then(() => { - testObject = instantiationService.createInstance(TestExtensionRecommendationsService); + testObject = instantiationService.createInstance(ExtensionRecommendationsService); assert.ok(!prompted); return testObject.getWorkspaceRecommendations().then(() => { @@ -366,26 +361,26 @@ suite('ExtensionRecommendationsService Test', () => { }); } - test('ExtensionRecommendationsService: No Prompt for valid workspace recommendations when galleryService is absent', () => { + test('ExtensionRecommendationsService: No Prompt for valid workspace recommendations when galleryService is absent', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const galleryQuerySpy = sinon.spy(); instantiationService.stub(IExtensionGalleryService, { query: galleryQuerySpy, isEnabled: () => false }); return testNoPromptOrRecommendationsForValidRecommendations(mockTestData.validRecommendedExtensions) .then(() => assert.ok(galleryQuerySpy.notCalled)); - }); + })); - test('ExtensionRecommendationsService: No Prompt for valid workspace recommendations during extension development', () => { + test('ExtensionRecommendationsService: No Prompt for valid workspace recommendations during extension development', () => runWithFakedTimers({ useFakeTimers: true }, async () => { instantiationService.stub(IEnvironmentService, { extensionDevelopmentLocationURI: [URI.file('/folder/file')], isExtensionDevelopment: true }); return testNoPromptOrRecommendationsForValidRecommendations(mockTestData.validRecommendedExtensions); - }); + })); - test('ExtensionRecommendationsService: No workspace recommendations or prompts when extensions.json has empty array', () => { + test('ExtensionRecommendationsService: No workspace recommendations or prompts when extensions.json has empty array', () => runWithFakedTimers({ useFakeTimers: true }, async () => { return testNoPromptForValidRecommendations([]); - }); + })); - test('ExtensionRecommendationsService: Prompt for valid workspace recommendations', async () => { + test('ExtensionRecommendationsService: Prompt for valid workspace recommendations', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await setUpFolderWorkspace('myFolder', mockTestData.recommendedExtensions); - testObject = instantiationService.createInstance(TestExtensionRecommendationsService); + testObject = instantiationService.createInstance(ExtensionRecommendationsService); await Event.toPromise(promptedEmitter.event); const recommendations = Object.keys(testObject.getAllRecommendationsWithReason()); @@ -394,45 +389,45 @@ suite('ExtensionRecommendationsService Test', () => { expected.forEach(x => { assert.strictEqual(recommendations.indexOf(x.toLowerCase()) > -1, true); }); - }); + })); - test('ExtensionRecommendationsService: No Prompt for valid workspace recommendations if they are already installed', () => { + test('ExtensionRecommendationsService: No Prompt for valid workspace recommendations if they are already installed', () => runWithFakedTimers({ useFakeTimers: true }, async () => { instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', mockExtensionLocal); return testNoPromptForValidRecommendations(mockTestData.validRecommendedExtensions); - }); + })); - test('ExtensionRecommendationsService: No Prompt for valid workspace recommendations with casing mismatch if they are already installed', () => { + test('ExtensionRecommendationsService: No Prompt for valid workspace recommendations with casing mismatch if they are already installed', () => runWithFakedTimers({ useFakeTimers: true }, async () => { instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', mockExtensionLocal); return testNoPromptForValidRecommendations(mockTestData.validRecommendedExtensions.map(x => x.toUpperCase())); - }); + })); - test('ExtensionRecommendationsService: No Prompt for valid workspace recommendations if ignoreRecommendations is set', () => { + test('ExtensionRecommendationsService: No Prompt for valid workspace recommendations if ignoreRecommendations is set', () => runWithFakedTimers({ useFakeTimers: true }, async () => { testConfigurationService.setUserConfiguration(ConfigurationKey, { ignoreRecommendations: true }); return testNoPromptForValidRecommendations(mockTestData.validRecommendedExtensions); - }); + })); - test('ExtensionRecommendationsService: No Prompt for valid workspace recommendations if showRecommendationsOnlyOnDemand is set', () => { + test('ExtensionRecommendationsService: No Prompt for valid workspace recommendations if showRecommendationsOnlyOnDemand is set', () => runWithFakedTimers({ useFakeTimers: true }, async () => { testConfigurationService.setUserConfiguration(ConfigurationKey, { showRecommendationsOnlyOnDemand: true }); return setUpFolderWorkspace('myFolder', mockTestData.validRecommendedExtensions).then(() => { - testObject = instantiationService.createInstance(TestExtensionRecommendationsService); + testObject = instantiationService.createInstance(ExtensionRecommendationsService); return testObject.activationPromise.then(() => { assert.ok(!prompted); }); }); - }); + })); - test('ExtensionRecommendationsService: No Prompt for valid workspace recommendations if ignoreRecommendations is set for current workspace', () => { + test('ExtensionRecommendationsService: No Prompt for valid workspace recommendations if ignoreRecommendations is set for current workspace', () => runWithFakedTimers({ useFakeTimers: true }, async () => { instantiationService.get(IStorageService).store('extensionsAssistant/workspaceRecommendationsIgnore', true, StorageScope.WORKSPACE, StorageTarget.MACHINE); return testNoPromptForValidRecommendations(mockTestData.validRecommendedExtensions); - }); + })); - test('ExtensionRecommendationsService: No Recommendations of globally ignored recommendations', () => { + test('ExtensionRecommendationsService: No Recommendations of globally ignored recommendations', () => runWithFakedTimers({ useFakeTimers: true }, async () => { instantiationService.get(IStorageService).store('extensionsAssistant/workspaceRecommendationsIgnore', true, StorageScope.WORKSPACE, StorageTarget.MACHINE); instantiationService.get(IStorageService).store('extensionsAssistant/recommendations', '["ms-dotnettools.csharp", "ms-python.python", "ms-vscode.vscode-typescript-tslint-plugin"]', StorageScope.PROFILE, StorageTarget.MACHINE); instantiationService.get(IStorageService).store('extensionsAssistant/ignored_recommendations', '["ms-dotnettools.csharp", "mockpublisher2.mockextension2"]', StorageScope.PROFILE, StorageTarget.MACHINE); return setUpFolderWorkspace('myFolder', mockTestData.validRecommendedExtensions).then(() => { - testObject = instantiationService.createInstance(TestExtensionRecommendationsService); + testObject = instantiationService.createInstance(ExtensionRecommendationsService); return testObject.activationPromise.then(() => { const recommendations = testObject.getAllRecommendationsWithReason(); assert.ok(!recommendations['ms-dotnettools.csharp']); // stored recommendation that has been globally ignored @@ -441,16 +436,16 @@ suite('ExtensionRecommendationsService Test', () => { assert.ok(!recommendations['mockpublisher2.mockextension2']); // workspace recommendation that has been globally ignored }); }); - }); + })); - test('ExtensionRecommendationsService: No Recommendations of workspace ignored recommendations', () => { + test('ExtensionRecommendationsService: No Recommendations of workspace ignored recommendations', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const ignoredRecommendations = ['ms-dotnettools.csharp', 'mockpublisher2.mockextension2']; // ignore a stored recommendation and a workspace recommendation. const storedRecommendations = '["ms-dotnettools.csharp", "ms-python.python"]'; instantiationService.get(IStorageService).store('extensionsAssistant/workspaceRecommendationsIgnore', true, StorageScope.WORKSPACE, StorageTarget.MACHINE); instantiationService.get(IStorageService).store('extensionsAssistant/recommendations', storedRecommendations, StorageScope.PROFILE, StorageTarget.MACHINE); return setUpFolderWorkspace('myFolder', mockTestData.validRecommendedExtensions, ignoredRecommendations).then(() => { - testObject = instantiationService.createInstance(TestExtensionRecommendationsService); + testObject = instantiationService.createInstance(ExtensionRecommendationsService); return testObject.activationPromise.then(() => { const recommendations = testObject.getAllRecommendationsWithReason(); assert.ok(!recommendations['ms-dotnettools.csharp']); // stored recommendation that has been workspace ignored @@ -459,9 +454,9 @@ suite('ExtensionRecommendationsService Test', () => { assert.ok(!recommendations['mockpublisher2.mockextension2']); // workspace recommendation that has been workspace ignored }); }); - }); + })); - test('ExtensionRecommendationsService: Able to retrieve collection of all ignored recommendations', async () => { + test('ExtensionRecommendationsService: Able to retrieve collection of all ignored recommendations', async () => runWithFakedTimers({ useFakeTimers: true }, async () => { const storageService = instantiationService.get(IStorageService); const workspaceIgnoredRecommendations = ['ms-dotnettools.csharp']; // ignore a stored recommendation and a workspace recommendation. @@ -472,14 +467,14 @@ suite('ExtensionRecommendationsService Test', () => { storageService.store('extensionsAssistant/ignored_recommendations', globallyIgnoredRecommendations, StorageScope.PROFILE, StorageTarget.MACHINE); await setUpFolderWorkspace('myFolder', mockTestData.validRecommendedExtensions, workspaceIgnoredRecommendations); - testObject = instantiationService.createInstance(TestExtensionRecommendationsService); + testObject = instantiationService.createInstance(ExtensionRecommendationsService); await testObject.activationPromise; const recommendations = testObject.getAllRecommendationsWithReason(); assert.deepStrictEqual(Object.keys(recommendations), ['ms-python.python', 'mockpublisher1.mockextension1']); - }); + })); - test('ExtensionRecommendationsService: Able to dynamically ignore/unignore global recommendations', async () => { + test('ExtensionRecommendationsService: Able to dynamically ignore/unignore global recommendations', async () => runWithFakedTimers({ useFakeTimers: true }, async () => { const storageService = instantiationService.get(IStorageService); const storedRecommendations = '["ms-dotnettools.csharp", "ms-python.python"]'; @@ -490,7 +485,7 @@ suite('ExtensionRecommendationsService Test', () => { await setUpFolderWorkspace('myFolder', mockTestData.validRecommendedExtensions); const extensionIgnoredRecommendationsService = instantiationService.get(IExtensionIgnoredRecommendationsService); - testObject = instantiationService.createInstance(TestExtensionRecommendationsService); + testObject = instantiationService.createInstance(ExtensionRecommendationsService); await testObject.activationPromise; let recommendations = testObject.getAllRecommendationsWithReason(); @@ -511,9 +506,9 @@ suite('ExtensionRecommendationsService Test', () => { assert.ok(recommendations['ms-python.python']); assert.ok(recommendations['mockpublisher1.mockextension1']); assert.ok(!recommendations['mockpublisher2.mockextension2']); - }); + })); - test('test global extensions are modified and recommendation change event is fired when an extension is ignored', async () => { + test('test global extensions are modified and recommendation change event is fired when an extension is ignored', async () => runWithFakedTimers({ useFakeTimers: true }, async () => { const storageService = instantiationService.get(IStorageService); const changeHandlerTarget = sinon.spy(); const ignoredExtensionId = 'Some.Extension'; @@ -522,7 +517,7 @@ suite('ExtensionRecommendationsService Test', () => { storageService.store('extensionsAssistant/ignored_recommendations', '["ms-vscode.vscode"]', StorageScope.PROFILE, StorageTarget.MACHINE); await setUpFolderWorkspace('myFolder', []); - testObject = instantiationService.createInstance(TestExtensionRecommendationsService); + testObject = instantiationService.createInstance(ExtensionRecommendationsService); const extensionIgnoredRecommendationsService = instantiationService.get(IExtensionIgnoredRecommendationsService); extensionIgnoredRecommendationsService.onDidChangeGlobalIgnoredRecommendation(changeHandlerTarget); extensionIgnoredRecommendationsService.toggleGlobalIgnoredRecommendation(ignoredExtensionId, true); @@ -530,14 +525,14 @@ suite('ExtensionRecommendationsService Test', () => { assert.ok(changeHandlerTarget.calledOnce); assert.ok(changeHandlerTarget.getCall(0).calledWithMatch({ extensionId: ignoredExtensionId.toLowerCase(), isRecommended: false })); - }); + })); - test('ExtensionRecommendationsService: Get file based recommendations from storage (old format)', () => { + test('ExtensionRecommendationsService: Get file based recommendations from storage (old format)', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const storedRecommendations = '["ms-dotnettools.csharp", "ms-python.python", "ms-vscode.vscode-typescript-tslint-plugin"]'; instantiationService.get(IStorageService).store('extensionsAssistant/recommendations', storedRecommendations, StorageScope.PROFILE, StorageTarget.MACHINE); return setUpFolderWorkspace('myFolder', []).then(() => { - testObject = instantiationService.createInstance(TestExtensionRecommendationsService); + testObject = instantiationService.createInstance(ExtensionRecommendationsService); return testObject.activationPromise.then(() => { const recommendations = testObject.getFileBasedRecommendations(); assert.strictEqual(recommendations.length, 2); @@ -546,25 +541,24 @@ suite('ExtensionRecommendationsService Test', () => { assert.ok(recommendations.every(extensionId => extensionId !== 'ms-vscode.vscode-typescript-tslint-plugin')); // stored recommendation that is no longer in neither product.extensionTips nor product.extensionImportantTips }); }); - }); + })); - test('ExtensionRecommendationsService: Get file based recommendations from storage (new format)', () => { + test('ExtensionRecommendationsService: Get file based recommendations from storage (new format)', async () => { const milliSecondsInADay = 1000 * 60 * 60 * 24; const now = Date.now(); const tenDaysOld = 10 * milliSecondsInADay; const storedRecommendations = `{"ms-dotnettools.csharp": ${now}, "ms-python.python": ${now}, "ms-vscode.vscode-typescript-tslint-plugin": ${now}, "lukehoban.Go": ${tenDaysOld}}`; instantiationService.get(IStorageService).store('extensionsAssistant/recommendations', storedRecommendations, StorageScope.PROFILE, StorageTarget.MACHINE); - return setUpFolderWorkspace('myFolder', []).then(() => { - testObject = instantiationService.createInstance(TestExtensionRecommendationsService); - return testObject.activationPromise.then(() => { - const recommendations = testObject.getFileBasedRecommendations(); - assert.strictEqual(recommendations.length, 2); - assert.ok(recommendations.some(extensionId => extensionId === 'ms-dotnettools.csharp')); // stored recommendation that exists in product.extensionTips - assert.ok(recommendations.some(extensionId => extensionId === 'ms-python.python')); // stored recommendation that exists in product.extensionImportantTips - assert.ok(recommendations.every(extensionId => extensionId !== 'ms-vscode.vscode-typescript-tslint-plugin')); // stored recommendation that is no longer in neither product.extensionTips nor product.extensionImportantTips - assert.ok(recommendations.every(extensionId => extensionId !== 'lukehoban.Go')); //stored recommendation that is older than a week - }); - }); + await setUpFolderWorkspace('myFolder', []); + testObject = instantiationService.createInstance(ExtensionRecommendationsService); + await testObject.activationPromise; + + const recommendations = testObject.getFileBasedRecommendations(); + assert.strictEqual(recommendations.length, 2); + assert.ok(recommendations.some(extensionId => extensionId === 'ms-dotnettools.csharp')); // stored recommendation that exists in product.extensionTips + assert.ok(recommendations.some(extensionId => extensionId === 'ms-python.python')); // stored recommendation that exists in product.extensionImportantTips + assert.ok(recommendations.every(extensionId => extensionId !== 'ms-vscode.vscode-typescript-tslint-plugin')); // stored recommendation that is no longer in neither product.extensionTips nor product.extensionImportantTips + assert.ok(recommendations.every(extensionId => extensionId !== 'lukehoban.Go')); //stored recommendation that is older than a week }); }); From bcfb26544296e155448337203c486da179f2bd18 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 4 Jul 2023 22:20:38 +0200 Subject: [PATCH 156/926] fix #186867 (#186983) --- .../extensionRecommendationNotificationService.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts index c08110b911b..c816b6686ea 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts @@ -188,17 +188,16 @@ export class ExtensionRecommendationNotificationService implements IExtensionRec return; } - const result = await this.promptRecommendationsNotification({ extensions: recommendations, source: RecommendationSource.WORKSPACE, name: localize({ key: 'this repository', comment: ['this repository means the current repository that is opened'] }, "this repository") }, { + await this.promptRecommendationsNotification({ extensions: recommendations, source: RecommendationSource.WORKSPACE, name: localize({ key: 'this repository', comment: ['this repository means the current repository that is opened'] }, "this repository") }, { onDidInstallRecommendedExtensions: () => this.telemetryService.publicLog2<{ userReaction: string }, ExtensionWorkspaceRecommendationsNotificationClassification>('extensionWorkspaceRecommendations:popup', { userReaction: 'install' }), onDidShowRecommendedExtensions: () => this.telemetryService.publicLog2<{ userReaction: string }, ExtensionWorkspaceRecommendationsNotificationClassification>('extensionWorkspaceRecommendations:popup', { userReaction: 'show' }), onDidCancelRecommendedExtensions: () => this.telemetryService.publicLog2<{ userReaction: string }, ExtensionWorkspaceRecommendationsNotificationClassification>('extensionWorkspaceRecommendations:popup', { userReaction: 'cancelled' }), - onDidNeverShowRecommendedExtensionsAgain: () => this.telemetryService.publicLog2<{ userReaction: string }, ExtensionWorkspaceRecommendationsNotificationClassification>('extensionWorkspaceRecommendations:popup', { userReaction: 'neverShowAgain' }), + onDidNeverShowRecommendedExtensionsAgain: () => { + this.telemetryService.publicLog2<{ userReaction: string }, ExtensionWorkspaceRecommendationsNotificationClassification>('extensionWorkspaceRecommendations:popup', { userReaction: 'neverShowAgain' }); + this.storageService.store(donotShowWorkspaceRecommendationsStorageKey, true, StorageScope.WORKSPACE, StorageTarget.MACHINE); + }, }); - if (result === RecommendationsNotificationResult.Accepted) { - this.storageService.store(donotShowWorkspaceRecommendationsStorageKey, true, StorageScope.WORKSPACE, StorageTarget.MACHINE); - } - } private async promptRecommendationsNotification({ extensions: extensionIds, source, name, searchValue }: IExtensionRecommendations, recommendationsNotificationActions: RecommendationsNotificationActions): Promise { From 51ffcad43304e38c1fc41f19d6e2a6c43a9f0f55 Mon Sep 17 00:00:00 2001 From: Andrea Mah <31675041+andreamah@users.noreply.github.com> Date: Tue, 4 Jul 2023 15:43:56 -0700 Subject: [PATCH 157/926] aria.alert -> aria.status in search (#187011) --- src/vs/workbench/contrib/search/browser/searchView.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 2ff831acb0a..eeaed2cc943 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -945,7 +945,7 @@ export class SearchView extends ViewPane { this.tree.setSelection([next], event); this.tree.reveal(next); const ariaLabel = this.treeAccessibilityProvider.getAriaLabel(next); - if (ariaLabel) { aria.alert(ariaLabel); } + if (ariaLabel) { aria.status(ariaLabel); } } } @@ -988,7 +988,7 @@ export class SearchView extends ViewPane { this.tree.setSelection([prev], event); this.tree.reveal(prev); const ariaLabel = this.treeAccessibilityProvider.getAriaLabel(prev); - if (ariaLabel) { aria.alert(ariaLabel); } + if (ariaLabel) { aria.status(ariaLabel); } } } From d71be027b2e35ccabfbbd798f46c6bfafb23163c Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 5 Jul 2023 10:22:04 +0200 Subject: [PATCH 158/926] clean up: do not use settings sync store in settings (#187006) * clean up: do not use settings sync store in settings * fix tests --- .../userDataSync/common/settingsSync.ts | 7 +-- .../userDataSync/common/userDataSync.ts | 6 +- .../common/userDataSyncStoreService.ts | 31 +++++----- .../common/userDataSyncStoreService.test.ts | 58 +------------------ 4 files changed, 23 insertions(+), 79 deletions(-) diff --git a/src/vs/platform/userDataSync/common/settingsSync.ts b/src/vs/platform/userDataSync/common/settingsSync.ts index 6a9be85ab7e..42eeb9df44d 100644 --- a/src/vs/platform/userDataSync/common/settingsSync.ts +++ b/src/vs/platform/userDataSync/common/settingsSync.ts @@ -18,9 +18,8 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; import { IUserDataProfile, IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { AbstractInitializer, AbstractJsonFileSynchroniser, IAcceptResult, IFileResourcePreview, IMergeResult } from 'vs/platform/userDataSync/common/abstractSynchronizer'; -import { edit } from 'vs/platform/userDataSync/common/content'; import { getIgnoredSettings, isEmpty, merge, updateIgnoredSettings } from 'vs/platform/userDataSync/common/settingsMerge'; -import { Change, CONFIGURATION_SYNC_STORE_KEY, IRemoteUserData, IUserDataSyncBackupStoreService, IUserDataSyncConfiguration, IUserDataSynchroniser, IUserDataSyncLogService, IUserDataSyncEnablementService, IUserDataSyncStoreService, IUserDataSyncUtilService, SyncResource, UserDataSyncError, UserDataSyncErrorCode, USER_DATA_SYNC_CONFIGURATION_SCOPE, USER_DATA_SYNC_SCHEME, IUserDataResourceManifest } from 'vs/platform/userDataSync/common/userDataSync'; +import { Change, IRemoteUserData, IUserDataSyncBackupStoreService, IUserDataSyncConfiguration, IUserDataSynchroniser, IUserDataSyncLogService, IUserDataSyncEnablementService, IUserDataSyncStoreService, IUserDataSyncUtilService, SyncResource, UserDataSyncError, UserDataSyncErrorCode, USER_DATA_SYNC_CONFIGURATION_SCOPE, USER_DATA_SYNC_SCHEME, IUserDataResourceManifest } from 'vs/platform/userDataSync/common/userDataSync'; interface ISettingsResourcePreview extends IFileResourcePreview { previewResult: IMergeResult; @@ -267,9 +266,7 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement try { const localFileContent = await this.getLocalFileContent(); if (localFileContent) { - const formatUtils = await this.getFormattingOptions(); - const content = edit(localFileContent.value.toString(), [CONFIGURATION_SYNC_STORE_KEY], undefined, formatUtils); - return !isEmpty(content); + return !isEmpty(localFileContent.value.toString()); } } catch (error) { if ((error).fileOperationResult !== FileOperationResult.FILE_NOT_FOUND) { diff --git a/src/vs/platform/userDataSync/common/userDataSync.ts b/src/vs/platform/userDataSync/common/userDataSync.ts index bd2ae810677..a0b8cddc6f6 100644 --- a/src/vs/platform/userDataSync/common/userDataSync.ts +++ b/src/vs/platform/userDataSync/common/userDataSync.ts @@ -23,8 +23,6 @@ import { ILogService } from 'vs/platform/log/common/log'; import { Registry } from 'vs/platform/registry/common/platform'; import { IUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; -export const CONFIGURATION_SYNC_STORE_KEY = 'configurationSync.store'; - export function getDisallowedIgnoredSettings(): string[] { const allSettings = Registry.as(ConfigurationExtensions.Configuration).getConfigurationProperties(); return Object.keys(allSettings).filter(setting => !!allSettings[setting].disallowSyncIgnore); @@ -35,7 +33,7 @@ export function getDefaultIgnoredSettings(): string[] { const ignoreSyncSettings = Object.keys(allSettings).filter(setting => !!allSettings[setting].ignoreSync); const machineSettings = Object.keys(allSettings).filter(setting => allSettings[setting].scope === ConfigurationScope.MACHINE || allSettings[setting].scope === ConfigurationScope.MACHINE_OVERRIDABLE); const disallowedSettings = getDisallowedIgnoredSettings(); - return distinct([CONFIGURATION_SYNC_STORE_KEY, ...ignoreSyncSettings, ...machineSettings, ...disallowedSettings]); + return distinct([...ignoreSyncSettings, ...machineSettings, ...disallowedSettings]); } export const USER_DATA_SYNC_CONFIGURATION_SCOPE = 'settingsSync'; @@ -94,7 +92,7 @@ export function registerConfiguration(): IDisposable { const jsonRegistry = Registry.as(JSONExtensions.JSONContribution); const registerIgnoredSettingsSchema = () => { const disallowedIgnoredSettings = getDisallowedIgnoredSettings(); - const defaultIgnoredSettings = getDefaultIgnoredSettings().filter(s => s !== CONFIGURATION_SYNC_STORE_KEY); + const defaultIgnoredSettings = getDefaultIgnoredSettings(); const settings = Object.keys(allSettings.properties).filter(setting => !defaultIgnoredSettings.includes(setting)); const ignoredSettings = defaultIgnoredSettings.filter(setting => !disallowedIgnoredSettings.includes(setting)); const ignoredSettingsSchema: IJSONSchema = { diff --git a/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts b/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts index ad761dcac47..58c71448a97 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts @@ -23,8 +23,9 @@ import { IProductService } from 'vs/platform/product/common/productService'; import { asJson, asText, asTextOrError, IRequestService, isSuccess as isSuccessContext } from 'vs/platform/request/common/request'; import { getServiceMachineId } from 'vs/platform/externalServices/common/serviceMachineId'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; -import { CONFIGURATION_SYNC_STORE_KEY, HEADER_EXECUTION_ID, HEADER_OPERATION_ID, IAuthenticationProvider, IResourceRefHandle, IUserData, IUserDataManifest, IUserDataSyncLogService, IUserDataSyncStore, IUserDataSyncStoreManagementService, IUserDataSyncStoreService, ServerResource, SYNC_SERVICE_URL_TYPE, UserDataSyncErrorCode, UserDataSyncStoreError, UserDataSyncStoreType } from 'vs/platform/userDataSync/common/userDataSync'; +import { HEADER_EXECUTION_ID, HEADER_OPERATION_ID, IAuthenticationProvider, IResourceRefHandle, IUserData, IUserDataManifest, IUserDataSyncLogService, IUserDataSyncStore, IUserDataSyncStoreManagementService, IUserDataSyncStoreService, ServerResource, SYNC_SERVICE_URL_TYPE, UserDataSyncErrorCode, UserDataSyncStoreError, UserDataSyncStoreType } from 'vs/platform/userDataSync/common/userDataSync'; +const CONFIGURATION_SYNC_STORE_KEY = 'configurationSync.store'; const SYNC_PREVIOUS_STORE = 'sync.previous.store'; const DONOT_MAKE_REQUESTS_UNTIL_KEY = 'sync.donot-make-requests-until'; const USER_SESSION_ID_KEY = 'sync.user-session-id'; @@ -61,27 +62,27 @@ export abstract class AbstractUserDataSyncStoreManagementService extends Disposa } protected updateUserDataSyncStore(): void { - this._userDataSyncStore = this.toUserDataSyncStore(this.productService[CONFIGURATION_SYNC_STORE_KEY], this.configurationService.getValue(CONFIGURATION_SYNC_STORE_KEY)); + this._userDataSyncStore = this.toUserDataSyncStore(this.productService[CONFIGURATION_SYNC_STORE_KEY]); this._onDidChangeUserDataSyncStore.fire(); } - protected toUserDataSyncStore(productStore: ConfigurationSyncStore & { web?: ConfigurationSyncStore } | undefined, configuredStore?: ConfigurationSyncStore): UserDataSyncStore | undefined { + protected toUserDataSyncStore(configurationSyncStore: ConfigurationSyncStore & { web?: ConfigurationSyncStore } | undefined): UserDataSyncStore | undefined { + if (!configurationSyncStore) { + return undefined; + } // Check for web overrides for backward compatibility while reading previous store - productStore = isWeb && productStore?.web ? { ...productStore, ...productStore.web } : productStore; - const value: Partial = { ...(productStore || {}), ...(configuredStore || {}) }; - if (value - && isString(value.url) - && isObject(value.authenticationProviders) - && Object.keys(value.authenticationProviders).every(authenticationProviderId => Array.isArray(value!.authenticationProviders![authenticationProviderId].scopes)) + configurationSyncStore = isWeb && configurationSyncStore.web ? { ...configurationSyncStore, ...configurationSyncStore.web } : configurationSyncStore; + if (isString(configurationSyncStore.url) + && isObject(configurationSyncStore.authenticationProviders) + && Object.keys(configurationSyncStore.authenticationProviders).every(authenticationProviderId => Array.isArray(configurationSyncStore!.authenticationProviders![authenticationProviderId].scopes)) ) { - const syncStore = value as ConfigurationSyncStore; - const canSwitch = !!syncStore.canSwitch && !configuredStore?.url; + const syncStore = configurationSyncStore as ConfigurationSyncStore; + const canSwitch = !!syncStore.canSwitch; const defaultType: UserDataSyncStoreType = syncStore.url === syncStore.insidersUrl ? 'insiders' : 'stable'; const type: UserDataSyncStoreType = (canSwitch ? this.userDataSyncStoreType : undefined) || defaultType; - const url = configuredStore?.url || - (type === 'insiders' ? syncStore.insidersUrl - : type === 'stable' ? syncStore.stableUrl - : syncStore.url); + const url = type === 'insiders' ? syncStore.insidersUrl + : type === 'stable' ? syncStore.stableUrl + : syncStore.url; return { url: URI.parse(url), type, diff --git a/src/vs/platform/userDataSync/test/common/userDataSyncStoreService.test.ts b/src/vs/platform/userDataSync/test/common/userDataSyncStoreService.test.ts index 08445ac0e43..83a6a6f0f36 100644 --- a/src/vs/platform/userDataSync/test/common/userDataSyncStoreService.test.ts +++ b/src/vs/platform/userDataSync/test/common/userDataSyncStoreService.test.ts @@ -5,71 +5,19 @@ import * as assert from 'assert'; import { timeout } from 'vs/base/common/async'; -import { newWriteableBufferStream, VSBuffer } from 'vs/base/common/buffer'; +import { newWriteableBufferStream } from 'vs/base/common/buffer'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Event } from 'vs/base/common/event'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { isWeb } from 'vs/base/common/platform'; -import { ConfigurationSyncStore } from 'vs/base/common/product'; -import { URI } from 'vs/base/common/uri'; import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IFileService } from 'vs/platform/files/common/files'; import { NullLogService } from 'vs/platform/log/common/log'; -import product from 'vs/platform/product/common/product'; import { IProductService } from 'vs/platform/product/common/productService'; import { IRequestService } from 'vs/platform/request/common/request'; -import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; -import { IUserDataSyncStore, IUserDataSyncStoreManagementService, IUserDataSyncStoreService, SyncResource, UserDataSyncErrorCode, UserDataSyncStoreError } from 'vs/platform/userDataSync/common/userDataSync'; -import { RequestsSession, UserDataSyncStoreManagementService, UserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSyncStoreService'; +import { IUserDataSyncStoreService, SyncResource, UserDataSyncErrorCode, UserDataSyncStoreError } from 'vs/platform/userDataSync/common/userDataSync'; +import { RequestsSession, UserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSyncStoreService'; import { UserDataSyncClient, UserDataSyncTestServer } from 'vs/platform/userDataSync/test/common/userDataSyncClient'; -suite('UserDataSyncStoreManagementService', () => { - const disposableStore = new DisposableStore(); - - teardown(() => disposableStore.clear()); - - test('test sync store is read from settings', async () => { - const client = disposableStore.add(new UserDataSyncClient(new UserDataSyncTestServer())); - await client.setUp(); - - client.instantiationService.stub(IProductService, { - _serviceBrand: undefined, ...product, ...{ - 'configurationSync.store': undefined - } - }); - - const configuredStore: ConfigurationSyncStore = { - url: 'http://configureHost:3000', - stableUrl: 'http://configureHost:3000', - insidersUrl: 'http://configureHost:3000', - canSwitch: false, - authenticationProviders: { 'configuredAuthProvider': { scopes: [] } } - }; - await client.instantiationService.get(IFileService).writeFile(client.instantiationService.get(IUserDataProfilesService).defaultProfile.settingsResource, VSBuffer.fromString(JSON.stringify({ - 'configurationSync.store': configuredStore - }))); - await client.instantiationService.get(IConfigurationService).reloadConfiguration(); - - const expected: IUserDataSyncStore = { - url: URI.parse('http://configureHost:3000'), - type: 'stable', - defaultUrl: URI.parse('http://configureHost:3000'), - stableUrl: URI.parse('http://configureHost:3000'), - insidersUrl: URI.parse('http://configureHost:3000'), - canSwitch: false, - authenticationProviders: [{ id: 'configuredAuthProvider', scopes: [] }] - }; - - const testObject: IUserDataSyncStoreManagementService = disposableStore.add(client.instantiationService.createInstance(UserDataSyncStoreManagementService)); - - assert.strictEqual(testObject.userDataSyncStore?.url.toString(), expected.url.toString()); - assert.strictEqual(testObject.userDataSyncStore?.defaultUrl.toString(), expected.defaultUrl.toString()); - assert.deepStrictEqual(testObject.userDataSyncStore?.authenticationProviders, expected.authenticationProviders); - }); - -}); - suite('UserDataSyncStoreService', () => { const disposableStore = new DisposableStore(); From a38349b3e82cdb57c79ef1e2d8ac995bf75dc376 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 5 Jul 2023 10:28:36 +0200 Subject: [PATCH 159/926] also use vscode.context.config... for sneaky access to product info (#187026) --- src/vs/amdX.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/amdX.ts b/src/vs/amdX.ts index 92fe85b7e09..f16d6ec6c1a 100644 --- a/src/vs/amdX.ts +++ b/src/vs/amdX.ts @@ -9,6 +9,7 @@ import * as platform from 'vs/base/common/platform'; import { IProductConfiguration } from 'vs/base/common/product'; import { URI } from 'vs/base/common/uri'; + class DefineCall { constructor( public readonly id: string | null | undefined, @@ -176,7 +177,7 @@ export async function importAMDNodeModule(nodeModuleName: string, pathInsideN if (isBuilt === undefined) { const product = globalThis._VSCODE_PRODUCT_JSON as unknown as IProductConfiguration; - isBuilt = !!product?.commit; + isBuilt = Boolean((product ?? (globalThis).vscode.context.configuration()?.product)?.commit); } if (_paths[nodeModuleName]) { From 32af6e755d3dc2e3b6a8babf18fce9653ad3657e Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 5 Jul 2023 12:25:25 +0200 Subject: [PATCH 160/926] graceful commit check (#187034) --- src/vs/amdX.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/amdX.ts b/src/vs/amdX.ts index f16d6ec6c1a..75b21c60828 100644 --- a/src/vs/amdX.ts +++ b/src/vs/amdX.ts @@ -177,7 +177,7 @@ export async function importAMDNodeModule(nodeModuleName: string, pathInsideN if (isBuilt === undefined) { const product = globalThis._VSCODE_PRODUCT_JSON as unknown as IProductConfiguration; - isBuilt = Boolean((product ?? (globalThis).vscode.context.configuration()?.product)?.commit); + isBuilt = Boolean((product ?? (globalThis).vscode?.context?.configuration()?.product)?.commit); } if (_paths[nodeModuleName]) { From 3b9f2825b79bed62a0439c26e9fe41546448be59 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Wed, 5 Jul 2023 12:56:26 +0200 Subject: [PATCH 161/926] Git - better handle symbolic links (#186716) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: João Moreno --- extensions/git/src/git.ts | 25 ++++++++++++++++++------- extensions/git/src/model.ts | 12 +++++++----- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index d0412aec7f7..ab03a354d11 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -12,7 +12,7 @@ import * as which from 'which'; import { EventEmitter } from 'events'; import * as iconv from '@vscode/iconv-lite-umd'; import * as filetype from 'file-type'; -import { assign, groupBy, IDisposable, toDisposable, dispose, mkdirp, readBytes, detectUnicodeEncoding, Encoding, onceEvent, splitInChunks, Limiter, Versions, isWindows, pathEquals, isMacintosh } from './util'; +import { assign, groupBy, IDisposable, toDisposable, dispose, mkdirp, readBytes, detectUnicodeEncoding, Encoding, onceEvent, splitInChunks, Limiter, Versions, isWindows, pathEquals, isMacintosh, isDescendant } from './util'; import { CancellationError, CancellationToken, ConfigurationChangeEvent, LogOutputChannel, Progress, Uri, workspace } from 'vscode'; import { detectEncoding } from './encoding'; import { Ref, RefType, Branch, Remote, ForcePushMode, GitErrorCodes, LogOptions, Change, Status, CommitOptions, RefQuery, InitOptions } from './api/git'; @@ -477,18 +477,18 @@ export class Git { return folderPath; } - async getRepositoryRoot(repositoryPath: string): Promise { - const result = await this.exec(repositoryPath, ['rev-parse', '--show-toplevel']); + async getRepositoryRoot(pathInsidePossibleRepository: string): Promise { + const result = await this.exec(pathInsidePossibleRepository, ['rev-parse', '--show-toplevel']); // Keep trailing spaces which are part of the directory name - const repoPath = path.normalize(result.stdout.trimLeft().replace(/[\r\n]+$/, '')); + const repositoryRootPath = path.normalize(result.stdout.trimLeft().replace(/[\r\n]+$/, '')); if (isWindows) { // On Git 2.25+ if you call `rev-parse --show-toplevel` on a mapped drive, instead of getting the mapped // drive path back, you get the UNC path for the mapped drive. So we will try to normalize it back to the // mapped drive path, if possible - const repoUri = Uri.file(repoPath); - const pathUri = Uri.file(repositoryPath); + const repoUri = Uri.file(repositoryRootPath); + const pathUri = Uri.file(pathInsidePossibleRepository); if (repoUri.authority.length !== 0 && pathUri.authority.length === 0) { // eslint-disable-next-line local/code-no-look-behind-regex const match = /(?<=^\/?)([a-zA-Z])(?=:\/)/.exec(pathUri.path); @@ -520,7 +520,18 @@ export class Git { } } - return repoPath; + // Handle symbolic links + // Git 2.31 added the `--path-format` flag to rev-parse which + // allows us to get the relative path of the repository root + if (!pathEquals(pathInsidePossibleRepository, repositoryRootPath) && + !isDescendant(repositoryRootPath, pathInsidePossibleRepository) && + !isDescendant(pathInsidePossibleRepository, repositoryRootPath) && + this.compareGitVersionTo('2.31.0') !== -1) { + const relativePathResult = await this.exec(pathInsidePossibleRepository, ['rev-parse', '--path-format=relative', '--show-toplevel',]); + return path.resolve(pathInsidePossibleRepository, relativePathResult.stdout.trimLeft().replace(/[\r\n]+$/, '')); + } + + return repositoryRootPath; } async getRepositoryDotGit(repositoryPath: string): Promise<{ path: string; commonPath?: string }> { diff --git a/extensions/git/src/model.ts b/extensions/git/src/model.ts index 0775f15855b..e8a496822f0 100644 --- a/extensions/git/src/model.ts +++ b/extensions/git/src/model.ts @@ -931,12 +931,14 @@ export class Model implements IBranchProtectionProviderRegistry, IRemoteSourcePu return true; } - const result = await Promise.all(workspaceFolders.map(async folder => { - const workspaceFolderRealPath = await this.getWorkspaceFolderRealPath(folder); - return workspaceFolderRealPath ? pathEquals(workspaceFolderRealPath, repositoryPath) || isDescendant(workspaceFolderRealPath, repositoryPath) : undefined; - })); + // The repository path may be a canonical path or it may contain a symbolic link so we have + // to match it against the workspace folders and the canonical paths of the workspace folders + const workspaceFolderPaths = new Set([ + ...workspaceFolders.map(folder => folder.uri.fsPath), + ...await Promise.all(workspaceFolders.map(folder => this.getWorkspaceFolderRealPath(folder))) + ]); - return !result.some(r => r); + return !Array.from(workspaceFolderPaths).some(folder => folder && (pathEquals(folder, repositoryPath) || isDescendant(folder, repositoryPath))); } private async getWorkspaceFolderRealPath(workspaceFolder: WorkspaceFolder): Promise { From 894068580f9407b165c2338162d31845123c0081 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 5 Jul 2023 15:22:37 +0200 Subject: [PATCH 162/926] Revert "enable COI for desktop via `coep: credentialless`, change command line flag from "enable-coi" to "disable-coi" (#186720)" (#187047) This reverts commit eafde5a28081c115bb0b06e5199be8f2822a737c. --- src/vs/base/common/network.ts | 6 ++---- src/vs/platform/environment/common/argv.ts | 2 +- .../environment/electron-main/environmentMainService.ts | 2 +- src/vs/platform/environment/node/argv.ts | 2 +- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/vs/base/common/network.ts b/src/vs/base/common/network.ts index e37d87f4e67..b00899c50db 100644 --- a/src/vs/base/common/network.ts +++ b/src/vs/base/common/network.ts @@ -289,12 +289,10 @@ export const FileAccess = new FileAccessImpl(); export namespace COI { - const coepDefault = platform.isElectron ? 'credentialless' : 'require-corp'; - const coiHeaders = new Map<'3' | '2' | '1' | string, Record>([ ['1', { 'Cross-Origin-Opener-Policy': 'same-origin' }], - ['2', { 'Cross-Origin-Embedder-Policy': coepDefault }], - ['3', { 'Cross-Origin-Opener-Policy': 'same-origin', 'Cross-Origin-Embedder-Policy': coepDefault }], + ['2', { 'Cross-Origin-Embedder-Policy': 'require-corp' }], + ['3', { 'Cross-Origin-Opener-Policy': 'same-origin', 'Cross-Origin-Embedder-Policy': 'require-corp' }], ]); export const CoopAndCoep = Object.freeze(coiHeaders.get('3')); diff --git a/src/vs/platform/environment/common/argv.ts b/src/vs/platform/environment/common/argv.ts index 5b43d16495e..2bbfd487d75 100644 --- a/src/vs/platform/environment/common/argv.ts +++ b/src/vs/platform/environment/common/argv.ts @@ -111,7 +111,7 @@ export interface NativeParsedArgs { 'profile-temp'?: boolean; 'disable-chromium-sandbox'?: boolean; - 'disable-coi'?: boolean; + 'enable-coi'?: boolean; // chromium command line args: https://electronjs.org/docs/all#supported-chrome-command-line-switches 'no-proxy-server'?: boolean; diff --git a/src/vs/platform/environment/electron-main/environmentMainService.ts b/src/vs/platform/environment/electron-main/environmentMainService.ts index a7e03ab2403..1fd54c5997b 100644 --- a/src/vs/platform/environment/electron-main/environmentMainService.ts +++ b/src/vs/platform/environment/electron-main/environmentMainService.ts @@ -63,7 +63,7 @@ export class EnvironmentMainService extends NativeEnvironmentService implements get disableKeytar(): boolean { return !!this.args['disable-keytar']; } @memoize - get crossOriginIsolated(): boolean { return !this.args['disable-coi']; } + get crossOriginIsolated(): boolean { return !!this.args['enable-coi']; } @memoize get codeCachePath(): string | undefined { return process.env['VSCODE_CODE_CACHE_PATH'] || undefined; } diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index 422fa2ce240..fbae6c3e4ed 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -164,7 +164,7 @@ export const OPTIONS: OptionDescriptions> = { 'continueOn': { type: 'string' }, 'locate-shell-integration-path': { type: 'string', args: ['bash', 'pwsh', 'zsh', 'fish'] }, - 'disable-coi': { type: 'boolean' }, + 'enable-coi': { type: 'boolean' }, // chromium flags 'no-proxy-server': { type: 'boolean' }, From fe1948fc619f922731f5e7d4ebd96961d8fcda4e Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 5 Jul 2023 16:06:50 +0200 Subject: [PATCH 163/926] Open folders "x unsaved" label text is displayed in multiple lines (fix #177601) (#186907) --- .../files/browser/media/explorerviewlet.css | 17 ----------------- .../files/browser/views/media/openeditors.css | 17 +++++++++++++++++ .../files/browser/views/openEditorsView.ts | 2 +- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/vs/workbench/contrib/files/browser/media/explorerviewlet.css b/src/vs/workbench/contrib/files/browser/media/explorerviewlet.css index fb6d440dfaa..3fb7600cc0f 100644 --- a/src/vs/workbench/contrib/files/browser/media/explorerviewlet.css +++ b/src/vs/workbench/contrib/files/browser/media/explorerviewlet.css @@ -57,23 +57,6 @@ flex: 0; /* do not steal space when label is hidden because we are in edit mode */ } -.explorer-folders-view .pane-header .count { - min-width: fit-content; - display: flex; - align-items: center; -} - -.pane.horizontal:not(.expanded) .pane-header .dirty-count.monaco-count-badge, -.pane-header .dirty-count.monaco-count-badge.hidden { - display: none; -} - -.dirty-count.monaco-count-badge { - padding: 2px 4px; - margin-left: 6px; - min-height: auto; -} - .explorer-folders-view .explorer-item.nonexistent-root { opacity: 0.5; } diff --git a/src/vs/workbench/contrib/files/browser/views/media/openeditors.css b/src/vs/workbench/contrib/files/browser/views/media/openeditors.css index b4542a2b765..12a175f0ebc 100644 --- a/src/vs/workbench/contrib/files/browser/views/media/openeditors.css +++ b/src/vs/workbench/contrib/files/browser/views/media/openeditors.css @@ -3,6 +3,23 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +.pane-header .open-editors-dirty-count-container { + min-width: fit-content; + display: flex; + align-items: center; +} + +.pane.horizontal:not(.expanded) .pane-header .open-editors-dirty-count-container > .dirty-count.monaco-count-badge, +.pane-header .open-editors-dirty-count-container > .dirty-count.monaco-count-badge.hidden { + display: none; +} + +.pane-header .open-editors-dirty-count-container > .dirty-count.monaco-count-badge { + padding: 2px 4px; + margin-left: 6px; + min-height: auto; +} + .open-editors .monaco-list .monaco-list-row:hover > .monaco-action-bar, .open-editors .monaco-list .monaco-list-row.focused > .monaco-action-bar, .open-editors .monaco-list .monaco-list-row.dirty > .monaco-action-bar, diff --git a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts index 554ee07c4f3..3409a3448dd 100644 --- a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts +++ b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts @@ -170,7 +170,7 @@ export class OpenEditorsView extends ViewPane { protected override renderHeaderTitle(container: HTMLElement): void { super.renderHeaderTitle(container, this.title); - const count = dom.append(container, $('.count')); + const count = dom.append(container, $('.open-editors-dirty-count-container')); this.dirtyCountElement = dom.append(count, $('.dirty-count.monaco-count-badge.long')); this.dirtyCountElement.style.backgroundColor = asCssVariable(badgeBackground); From 481c26a0275011aeca57b4c578c1cb8a326580f0 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 5 Jul 2023 16:22:20 +0200 Subject: [PATCH 164/926] `Reopen Editor With...` unavailable in `editor/title/context` menu (fix #186233) (#186914) --- src/vs/workbench/browser/contextkeys.ts | 17 +++------------ .../browser/parts/editor/tabsTitleControl.ts | 6 ++++-- .../browser/parts/editor/titleControl.ts | 12 +++++++++-- src/vs/workbench/common/contextkeys.ts | 21 +++++++++++++++++++ 4 files changed, 38 insertions(+), 18 deletions(-) diff --git a/src/vs/workbench/browser/contextkeys.ts b/src/vs/workbench/browser/contextkeys.ts index 09245ec08a3..feca5c8da58 100644 --- a/src/vs/workbench/browser/contextkeys.ts +++ b/src/vs/workbench/browser/contextkeys.ts @@ -7,8 +7,8 @@ import { Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { IContextKeyService, IContextKey, setConstant as setConstantContextKey } from 'vs/platform/contextkey/common/contextkey'; import { InputFocusedContext, IsMacContext, IsLinuxContext, IsWindowsContext, IsWebContext, IsMacNativeContext, IsDevelopmentContext, IsIOSContext, ProductQualityContext, IsMobileContext } from 'vs/platform/contextkey/common/contextkeys'; -import { SplitEditorsVertically, InEditorZenModeContext, ActiveEditorCanRevertContext, ActiveEditorGroupLockedContext, ActiveEditorCanSplitInGroupContext, SideBySideEditorActiveContext, AuxiliaryBarVisibleContext, SideBarVisibleContext, PanelAlignmentContext, PanelMaximizedContext, PanelVisibleContext, ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, EmbedderIdentifierContext, EditorTabsVisibleContext, IsCenteredLayoutContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorReadonlyContext, EditorAreaVisibleContext, ActiveEditorAvailableEditorIdsContext, DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, IsFullscreenContext, OpenFolderWorkspaceSupportContext, RemoteNameContext, VirtualWorkspaceContext, WorkbenchStateContext, WorkspaceFolderCountContext, PanelPositionContext, TemporaryWorkspaceContext, ActiveEditorCanToggleReadonlyContext } from 'vs/workbench/common/contextkeys'; -import { TEXT_DIFF_EDITOR_ID, EditorInputCapabilities, SIDE_BY_SIDE_EDITOR_ID, DEFAULT_EDITOR_ASSOCIATION, EditorResourceAccessor, SideBySideEditor } from 'vs/workbench/common/editor'; +import { SplitEditorsVertically, InEditorZenModeContext, ActiveEditorCanRevertContext, ActiveEditorGroupLockedContext, ActiveEditorCanSplitInGroupContext, SideBySideEditorActiveContext, AuxiliaryBarVisibleContext, SideBarVisibleContext, PanelAlignmentContext, PanelMaximizedContext, PanelVisibleContext, ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, EmbedderIdentifierContext, EditorTabsVisibleContext, IsCenteredLayoutContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorReadonlyContext, EditorAreaVisibleContext, ActiveEditorAvailableEditorIdsContext, DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, IsFullscreenContext, OpenFolderWorkspaceSupportContext, RemoteNameContext, VirtualWorkspaceContext, WorkbenchStateContext, WorkspaceFolderCountContext, PanelPositionContext, TemporaryWorkspaceContext, ActiveEditorCanToggleReadonlyContext, applyAvailableEditorIds } from 'vs/workbench/common/contextkeys'; +import { TEXT_DIFF_EDITOR_ID, EditorInputCapabilities, SIDE_BY_SIDE_EDITOR_ID, EditorResourceAccessor, SideBySideEditor } from 'vs/workbench/common/editor'; import { trackFocus, addDisposableListener, EventType } from 'vs/base/browser/dom'; import { preferredSideBySideGroupDirection, GroupDirection, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -22,7 +22,6 @@ import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/wo import { isNative } from 'vs/base/common/platform'; import { IEditorResolverService } from 'vs/workbench/services/editor/common/editorResolverService'; import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite'; -import { Schemas } from 'vs/base/common/network'; import { WebFileSystemAccess } from 'vs/platform/files/browser/webFileSystemAccess'; import { IProductService } from 'vs/platform/product/common/productService'; import { FileSystemProviderCapabilities, IFileService } from 'vs/platform/files/common/files'; @@ -296,17 +295,7 @@ export class WorkbenchContextKeysHandler extends Disposable { this.activeEditorContext.set(activeEditorPane.getId()); this.activeEditorCanRevert.set(!activeEditorPane.input.hasCapability(EditorInputCapabilities.Untitled)); this.activeEditorCanSplitInGroup.set(activeEditorPane.input.hasCapability(EditorInputCapabilities.CanSplitInGroup)); - - const activeEditorResource = activeEditorPane.input.resource; - const editors = activeEditorResource ? this.editorResolverService.getEditors(activeEditorResource).map(editor => editor.id) : []; - if (activeEditorResource?.scheme === Schemas.untitled && activeEditorPane.input.editorId !== DEFAULT_EDITOR_ASSOCIATION.id) { - // Non text editor untitled files cannot be easily serialized between extensions - // so instead we disable this context key to prevent common commands that act on the active editor - this.activeEditorAvailableEditorIds.set(''); - } else { - this.activeEditorAvailableEditorIds.set(editors.join(',')); - } - + applyAvailableEditorIds(this.activeEditorAvailableEditorIds, activeEditorPane.input, this.editorResolverService); this.activeEditorIsReadonly.set(!!activeEditorPane.input.isReadonly()); const primaryEditorResource = EditorResourceAccessor.getOriginalUri(activeEditorPane.input, { supportSideBySide: SideBySideEditor.PRIMARY }); this.activeEditorCanToggleReadonly.set(!!primaryEditorResource && this.fileService.hasProvider(primaryEditorResource) && !this.fileService.hasCapability(primaryEditorResource, FileSystemProviderCapabilities.Readonly)); diff --git a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts index 9492b5f46dc..ecb998d6274 100644 --- a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts @@ -54,6 +54,7 @@ import { UNLOCK_GROUP_COMMAND_ID } from 'vs/workbench/browser/parts/editor/edito import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { ITreeViewsDnDService } from 'vs/editor/common/services/treeViewsDndService'; import { DraggedTreeItemsIdentifier } from 'vs/editor/common/services/treeViewsDnd'; +import { IEditorResolverService } from 'vs/workbench/services/editor/common/editorResolverService'; interface IEditorInputLabel { editor: EditorInput; @@ -150,9 +151,10 @@ export class TabsTitleControl extends TitleControl { @IEditorService private readonly editorService: EditorServiceImpl, @IPathService private readonly pathService: IPathService, @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService, - @ITreeViewsDnDService private readonly treeViewsDragAndDropService: ITreeViewsDnDService + @ITreeViewsDnDService private readonly treeViewsDragAndDropService: ITreeViewsDnDService, + @IEditorResolverService editorResolverService: IEditorResolverService ) { - super(parent, accessor, group, contextMenuService, instantiationService, contextKeyService, keybindingService, notificationService, menuService, quickInputService, themeService, configurationService, fileService); + super(parent, accessor, group, contextMenuService, instantiationService, contextKeyService, keybindingService, notificationService, menuService, quickInputService, themeService, configurationService, fileService, editorResolverService); // Resolve the correct path library for the OS we are on // If we are connected to remote, this accounts for the diff --git a/src/vs/workbench/browser/parts/editor/titleControl.ts b/src/vs/workbench/browser/parts/editor/titleControl.ts index edbec3dffb7..5d77c66dfc5 100644 --- a/src/vs/workbench/browser/parts/editor/titleControl.ts +++ b/src/vs/workbench/browser/parts/editor/titleControl.ts @@ -30,7 +30,7 @@ import { BreadcrumbsControl, IBreadcrumbsControlOptions } from 'vs/workbench/bro import { IEditorGroupsAccessor, IEditorGroupTitleHeight, IEditorGroupView } from 'vs/workbench/browser/parts/editor/editor'; import { IEditorCommandsContext, EditorResourceAccessor, IEditorPartOptions, SideBySideEditor, EditorsOrder, EditorInputCapabilities } from 'vs/workbench/common/editor'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; -import { ResourceContextKey, ActiveEditorPinnedContext, ActiveEditorStickyContext, ActiveEditorGroupLockedContext, ActiveEditorCanSplitInGroupContext, SideBySideEditorActiveContext, ActiveEditorLastInGroupContext, ActiveEditorFirstInGroupContext } from 'vs/workbench/common/contextkeys'; +import { ResourceContextKey, ActiveEditorPinnedContext, ActiveEditorStickyContext, ActiveEditorGroupLockedContext, ActiveEditorCanSplitInGroupContext, SideBySideEditorActiveContext, ActiveEditorLastInGroupContext, ActiveEditorFirstInGroupContext, ActiveEditorAvailableEditorIdsContext, applyAvailableEditorIds } from 'vs/workbench/common/contextkeys'; import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; import { IFileService } from 'vs/platform/files/common/files'; import { withNullAsUndefined, withUndefinedAsNull, assertIsDefined } from 'vs/base/common/types'; @@ -40,6 +40,7 @@ import { SideBySideEditorInput } from 'vs/workbench/common/editor/sideBySideEdit import { WorkbenchToolBar } from 'vs/platform/actions/browser/toolbar'; import { LocalSelectionTransfer } from 'vs/platform/dnd/browser/dnd'; import { DraggedTreeItemsIdentifier } from 'vs/editor/common/services/treeViewsDnd'; +import { IEditorResolverService } from 'vs/workbench/services/editor/common/editorResolverService'; export interface IToolbarActions { primary: IAction[]; @@ -102,6 +103,7 @@ export abstract class TitleControl extends Themable { private editorIsFirstContext: IContextKey; private editorIsLastContext: IContextKey; private editorStickyContext: IContextKey; + private editorAvailableEditorIds: IContextKey; private editorCanSplitInGroupContext: IContextKey; private sideBySideEditorContext: IContextKey; @@ -125,7 +127,8 @@ export abstract class TitleControl extends Themable { @IQuickInputService protected quickInputService: IQuickInputService, @IThemeService themeService: IThemeService, @IConfigurationService protected configurationService: IConfigurationService, - @IFileService private readonly fileService: IFileService + @IFileService private readonly fileService: IFileService, + @IEditorResolverService private readonly editorResolverService: IEditorResolverService ) { super(themeService); @@ -135,6 +138,7 @@ export abstract class TitleControl extends Themable { this.editorIsFirstContext = ActiveEditorFirstInGroupContext.bindTo(contextKeyService); this.editorIsLastContext = ActiveEditorLastInGroupContext.bindTo(contextKeyService); this.editorStickyContext = ActiveEditorStickyContext.bindTo(contextKeyService); + this.editorAvailableEditorIds = ActiveEditorAvailableEditorIdsContext.bindTo(this.contextKeyService); this.editorCanSplitInGroupContext = ActiveEditorCanSplitInGroupContext.bindTo(contextKeyService); this.sideBySideEditorContext = SideBySideEditorActiveContext.bindTo(contextKeyService); @@ -253,6 +257,7 @@ export abstract class TitleControl extends Themable { this.editorIsFirstContext.set(activeEditor ? this.group.isFirst(activeEditor) : false); this.editorIsLastContext.set(activeEditor ? this.group.isLast(activeEditor) : false); this.editorStickyContext.set(activeEditor ? this.group.isSticky(activeEditor) : false); + applyAvailableEditorIds(this.editorAvailableEditorIds, activeEditor, this.editorResolverService); this.editorCanSplitInGroupContext.set(activeEditor ? activeEditor.hasCapability(EditorInputCapabilities.CanSplitInGroup) : false); this.sideBySideEditorContext.set(activeEditor?.typeId === SideBySideEditorInput.ID); @@ -370,6 +375,8 @@ export abstract class TitleControl extends Themable { this.editorCanSplitInGroupContext.set(editor.hasCapability(EditorInputCapabilities.CanSplitInGroup)); const currentSideBySideEditorContext = !!this.sideBySideEditorContext.get(); this.sideBySideEditorContext.set(editor.typeId === SideBySideEditorInput.ID); + const currentEditorAvailableEditorIds = this.editorAvailableEditorIds.get() ?? ''; + applyAvailableEditorIds(this.editorAvailableEditorIds, editor, this.editorResolverService); // Find target anchor let anchor: HTMLElement | { x: number; y: number } = node; @@ -397,6 +404,7 @@ export abstract class TitleControl extends Themable { this.groupLockedContext.set(currentGroupLockedContext); this.editorCanSplitInGroupContext.set(currentEditorCanSplitContext); this.sideBySideEditorContext.set(currentSideBySideEditorContext); + this.editorAvailableEditorIds.set(currentEditorAvailableEditorIds); // restore focus to active group this.accessor.activeGroup.focus(); diff --git a/src/vs/workbench/common/contextkeys.ts b/src/vs/workbench/common/contextkeys.ts index 4137738bb9e..6121cc4791b 100644 --- a/src/vs/workbench/common/contextkeys.ts +++ b/src/vs/workbench/common/contextkeys.ts @@ -12,6 +12,9 @@ import { ILanguageService } from 'vs/editor/common/languages/language'; import { IFileService } from 'vs/platform/files/common/files'; import { IModelService } from 'vs/editor/common/services/model'; import { Schemas } from 'vs/base/common/network'; +import { EditorInput } from 'vs/workbench/common/editor/editorInput'; +import { IEditorResolverService } from 'vs/workbench/services/editor/common/editorResolverService'; +import { DEFAULT_EDITOR_ASSOCIATION } from 'vs/workbench/common/editor'; //#region < --- Workbench --- > @@ -266,3 +269,21 @@ export class ResourceContextKey { } //#endregion + +export function applyAvailableEditorIds(contextKey: IContextKey, editor: EditorInput | undefined | null, editorResolverService: IEditorResolverService): void { + if (!editor) { + contextKey.set(''); + return; + } + + const editorResource = editor.resource; + const editors = editorResource ? editorResolverService.getEditors(editorResource).map(editor => editor.id) : []; + + if (editorResource?.scheme === Schemas.untitled && editor.editorId !== DEFAULT_EDITOR_ASSOCIATION.id) { + // Non text editor untitled files cannot be easily serialized between extensions + // so instead we disable this context key to prevent common commands that act on the active editor + contextKey.set(''); + } else { + contextKey.set(editors.join(',')); + } +} From 51cf6a04e0d18e73c79af8358c6da7d158a506de Mon Sep 17 00:00:00 2001 From: Andrea Mah <31675041+andreamah@users.noreply.github.com> Date: Wed, 5 Jul 2023 08:09:49 -0700 Subject: [PATCH 165/926] Notebook search highlights are not hidden when closing the search sidebar (#187017) Fixes #185289 --- .../contrib/search/browser/searchModel.ts | 29 +++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/searchModel.ts b/src/vs/workbench/contrib/search/browser/searchModel.ts index 6259951ead9..68b2ca8f905 100644 --- a/src/vs/workbench/contrib/search/browser/searchModel.ts +++ b/src/vs/workbench/contrib/search/browser/searchModel.ts @@ -723,10 +723,7 @@ export class FileMatch extends Disposable implements IFileMatch { } this._notebookUpdateScheduler.schedule(); }) ?? null; - - this._findMatchDecorationModel?.stopWebviewFind(); - this._findMatchDecorationModel?.dispose(); - this._findMatchDecorationModel = new FindMatchDecorationModel(this._notebookEditorWidget); + this._addNotebookHighlights(); } unbindNotebookEditorWidget(widget?: NotebookEditorWidget) { @@ -738,13 +735,34 @@ export class FileMatch extends Disposable implements IFileMatch { this._notebookUpdateScheduler.cancel(); this._editorWidgetListener?.dispose(); } + this._removeNotebookHighlights(); + this._notebookEditorWidget = null; + } + updateNotebookHighlights(): void { + if (this.parent().showHighlights) { + this._addNotebookHighlights(); + this.setNotebookFindMatchDecorationsUsingCellMatches(Array.from(this._cellMatches.values())); + } else { + this._removeNotebookHighlights(); + } + } + + private _addNotebookHighlights(): void { + if (!this._notebookEditorWidget) { + return; + } + this._findMatchDecorationModel?.stopWebviewFind(); + this._findMatchDecorationModel?.dispose(); + this._findMatchDecorationModel = new FindMatchDecorationModel(this._notebookEditorWidget); + } + + private _removeNotebookHighlights(): void { if (this._findMatchDecorationModel) { this._findMatchDecorationModel?.stopWebviewFind(); this._findMatchDecorationModel?.dispose(); this._findMatchDecorationModel = undefined; } - this._notebookEditorWidget = null; } private updateNotebookMatches(matches: CellFindMatchWithIndex[], modelChange: boolean): void { @@ -1808,6 +1826,7 @@ export class SearchResult extends Disposable { let selectedMatch: Match | null = null; this.matches().forEach((fileMatch: FileMatch) => { fileMatch.updateHighlights(); + fileMatch.updateNotebookHighlights(); if (!selectedMatch) { selectedMatch = fileMatch.getSelectedMatch(); } From ad17b84f868a60122da528610ad0eb8849867749 Mon Sep 17 00:00:00 2001 From: Bhavya U Date: Wed, 5 Jul 2023 08:34:16 -0700 Subject: [PATCH 166/926] Layout after setting content (#187019) --- .../browser/gettingStarted.ts | 5 + .../common/media/profiles.svg | 118 +++++++----------- 2 files changed, 51 insertions(+), 72 deletions(-) diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts index 67934834f05..a371f7fa64f 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts +++ b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts @@ -540,6 +540,7 @@ export class GettingStartedPage extends EditorPane { const media = stepToExpand.media; const mediaElement = $('img'); + clearNode(this.stepMediaComponent); this.stepMediaComponent.appendChild(mediaElement); mediaElement.setAttribute('alt', media.altText); this.updateMediaSourceForColorMode(mediaElement, media.path); @@ -1162,6 +1163,10 @@ export class GettingStartedPage extends EditorPane { this.container.classList.toggle('height-constrained', size.height <= 600); this.container.classList.toggle('width-constrained', size.width <= 400); this.container.classList.toggle('width-semi-constrained', size.width <= 800); + + this.categoriesPageScrollbar?.scanDomNode(); + this.detailsPageScrollbar?.scanDomNode(); + this.detailsScrollbar?.scanDomNode(); } private updateCategoryProgress() { diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/common/media/profiles.svg b/src/vs/workbench/contrib/welcomeGettingStarted/common/media/profiles.svg index c35a2d9dc5f..ad929bb04b4 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/common/media/profiles.svg +++ b/src/vs/workbench/contrib/welcomeGettingStarted/common/media/profiles.svg @@ -1,73 +1,47 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 6bc77ae5af14ed080c6a02d84151de06b7b84ee9 Mon Sep 17 00:00:00 2001 From: Andrea Mah <31675041+andreamah@users.noreply.github.com> Date: Wed, 5 Jul 2023 08:53:57 -0700 Subject: [PATCH 167/926] Pull new CSS grammar for colors, lengths, etc. (#187013) Fixes #187012 --- extensions/css/cgmanifest.json | 2 +- extensions/css/syntaxes/css.tmLanguage.json | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/extensions/css/cgmanifest.json b/extensions/css/cgmanifest.json index e64ba7e11ae..fe46b5c4acd 100644 --- a/extensions/css/cgmanifest.json +++ b/extensions/css/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "microsoft/vscode-css", "repositoryUrl": "https://github.com/microsoft/vscode-css", - "commitHash": "1452547185a1793c946cf67f8c7c9001716e32c3" + "commitHash": "3bd00206f6b0d16eb2eba53fb886462eb8c58baa" } }, "licenseDetail": [ diff --git a/extensions/css/syntaxes/css.tmLanguage.json b/extensions/css/syntaxes/css.tmLanguage.json index c50611017c2..c44db9bd68f 100644 --- a/extensions/css/syntaxes/css.tmLanguage.json +++ b/extensions/css/syntaxes/css.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-css/commit/1452547185a1793c946cf67f8c7c9001716e32c3", + "version": "https://github.com/microsoft/vscode-css/commit/3bd00206f6b0d16eb2eba53fb886462eb8c58baa", "name": "CSS", "scopeName": "source.css", "patterns": [ @@ -850,7 +850,7 @@ ] }, { - "begin": "(?i)(? Date: Wed, 5 Jul 2023 18:04:11 +0200 Subject: [PATCH 168/926] Fixes #186680 (#186989) --- .../diffEditorWidget2/diffEditorViewModel.ts | 17 +++--- .../browser/widget/diffEditorWidget2.test.ts | 54 +++++++++++++++++++ 2 files changed, 64 insertions(+), 7 deletions(-) create mode 100644 src/vs/editor/test/browser/widget/diffEditorWidget2.test.ts diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts index 002e062a506..bcb4c13371d 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts @@ -289,15 +289,18 @@ export class UnchangedRegion { let modStart = mapping.modifiedRange.startLineNumber; let length = mapping.originalRange.length; - if (origStart === 1 && modStart === 1 && length > minContext + minHiddenLineCount) { - if (length < originalLineCount) { + const atStart = origStart === 1 && modStart === 1; + const atEnd = origStart + length === originalLineCount + 1 && modStart + length === modifiedLineCount + 1; + + if ((atStart || atEnd) && length > minContext + minHiddenLineCount) { + if (atStart && !atEnd) { + length -= minContext; + } + if (atEnd && !atStart) { + origStart += minContext; + modStart += minContext; length -= minContext; } - result.push(new UnchangedRegion(origStart, modStart, length, 0, 0)); - } else if (origStart + length === originalLineCount + 1 && modStart + length === modifiedLineCount + 1 && length > minContext + minHiddenLineCount) { - origStart += minContext; - modStart += minContext; - length -= minContext; result.push(new UnchangedRegion(origStart, modStart, length, 0, 0)); } else if (length > minContext * 2 + minHiddenLineCount) { origStart += minContext; diff --git a/src/vs/editor/test/browser/widget/diffEditorWidget2.test.ts b/src/vs/editor/test/browser/widget/diffEditorWidget2.test.ts new file mode 100644 index 00000000000..85c9043e060 --- /dev/null +++ b/src/vs/editor/test/browser/widget/diffEditorWidget2.test.ts @@ -0,0 +1,54 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import assert = require('assert'); +import { UnchangedRegion } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel'; +import { LineRange } from 'vs/editor/common/core/lineRange'; +import { LineRangeMapping } from 'vs/editor/common/diff/linesDiffComputer'; + +suite('DiffEditorWidget2', () => { + suite('UnchangedRegion', () => { + function serialize(regions: UnchangedRegion[]): unknown { + return regions.map(r => `${r.originalRange} - ${r.modifiedRange}`); + } + + test('Everything changed', () => { + assert.deepStrictEqual(serialize(UnchangedRegion.fromDiffs( + [new LineRangeMapping(new LineRange(1, 10), new LineRange(1, 10), [])], + 10, + 10, + )), []); + }); + + test('Nothing changed', () => { + assert.deepStrictEqual(serialize(UnchangedRegion.fromDiffs( + [], + 10, + 10, + )), [ + "[1,11) - [1,11)" + ]); + }); + + test('Change in the middle', () => { + assert.deepStrictEqual(serialize(UnchangedRegion.fromDiffs( + [new LineRangeMapping(new LineRange(50, 60), new LineRange(50, 60), [])], + 100, + 100, + )), ([ + '[1,47) - [1,47)', + '[63,101) - [63,101)' + ])); + }); + + test('Change at the end', () => { + assert.deepStrictEqual(serialize(UnchangedRegion.fromDiffs( + [new LineRangeMapping(new LineRange(99, 100), new LineRange(100, 100), [])], + 100, + 100, + )), (["[1,96) - [1,96)"])); + }); + }); +}); From 48531d44ba76ee1f6e6867a9a21648ea65ed5510 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Wed, 5 Jul 2023 18:06:54 +0200 Subject: [PATCH 169/926] Adds diff algo test (#186995) --- .../node/diffing/fixtures/ws-alignment/1.tsx | 16 +++++ .../node/diffing/fixtures/ws-alignment/2.tsx | 20 ++++++ .../ws-alignment/advanced.expected.diff.json | 56 ++++++++++++++++ .../ws-alignment/legacy.expected.diff.json | 64 +++++++++++++++++++ 4 files changed, 156 insertions(+) create mode 100644 src/vs/editor/test/node/diffing/fixtures/ws-alignment/1.tsx create mode 100644 src/vs/editor/test/node/diffing/fixtures/ws-alignment/2.tsx create mode 100644 src/vs/editor/test/node/diffing/fixtures/ws-alignment/advanced.expected.diff.json create mode 100644 src/vs/editor/test/node/diffing/fixtures/ws-alignment/legacy.expected.diff.json diff --git a/src/vs/editor/test/node/diffing/fixtures/ws-alignment/1.tsx b/src/vs/editor/test/node/diffing/fixtures/ws-alignment/1.tsx new file mode 100644 index 00000000000..693ebcc1e12 --- /dev/null +++ b/src/vs/editor/test/node/diffing/fixtures/ws-alignment/1.tsx @@ -0,0 +1,16 @@ +import { Stack, Text } from '@fluentui/react'; +import { View } from '../../layout/layout'; + +export const WelcomeView = () => { + return ( + + + + + Welcome to the VS Code Tools application. + + + + + ); +} diff --git a/src/vs/editor/test/node/diffing/fixtures/ws-alignment/2.tsx b/src/vs/editor/test/node/diffing/fixtures/ws-alignment/2.tsx new file mode 100644 index 00000000000..cb911e799d4 --- /dev/null +++ b/src/vs/editor/test/node/diffing/fixtures/ws-alignment/2.tsx @@ -0,0 +1,20 @@ +import { Nav } from '@fluentui/react'; +import { View } from '../../layout/layout'; + +export const WelcomeView = () => { + return ( + + + + ); +} diff --git a/src/vs/editor/test/node/diffing/fixtures/ws-alignment/advanced.expected.diff.json b/src/vs/editor/test/node/diffing/fixtures/ws-alignment/advanced.expected.diff.json new file mode 100644 index 00000000000..92c6e475761 --- /dev/null +++ b/src/vs/editor/test/node/diffing/fixtures/ws-alignment/advanced.expected.diff.json @@ -0,0 +1,56 @@ +{ + "original": { + "content": "import { Stack, Text } from '@fluentui/react';\nimport { View } from '../../layout/layout';\n\nexport const WelcomeView = () => {\n\treturn (\n\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\tWelcome to the VS Code Tools application.\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t\n\t);\n}\n", + "fileName": "./1.tsx" + }, + "modified": { + "content": "import { Nav } from '@fluentui/react';\nimport { View } from '../../layout/layout';\n\nexport const WelcomeView = () => {\n\treturn (\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t);\n}\n", + "fileName": "./2.tsx" + }, + "diffs": [ + { + "originalRange": "[1,2)", + "modifiedRange": "[1,2)", + "innerChanges": [ + { + "originalRange": "[1,10 -> 1,21]", + "modifiedRange": "[1,10 -> 1,13]" + } + ] + }, + { + "originalRange": "[7,14)", + "modifiedRange": "[7,18)", + "innerChanges": [ + { + "originalRange": "[7,5 -> 7,43]", + "modifiedRange": "[7,5 -> 11,140]" + }, + { + "originalRange": "[8,5 -> 9,12]", + "modifiedRange": "[12,5 -> 12,131]" + }, + { + "originalRange": "[10,7 -> 10,22]", + "modifiedRange": "[13,7 -> 13,17]" + }, + { + "originalRange": "[10,30 -> 10,48]", + "modifiedRange": "[13,25 -> 13,118]" + }, + { + "originalRange": "[11,6 -> 11,13]", + "modifiedRange": "[14,6 -> 14,8]" + }, + { + "originalRange": "[12,5 -> 12,17]", + "modifiedRange": "[15,5 -> 16,7]" + }, + { + "originalRange": "[13,6 -> 13,11]", + "modifiedRange": "[17,6 -> 17,9]" + } + ] + } + ] +} diff --git a/src/vs/editor/test/node/diffing/fixtures/ws-alignment/legacy.expected.diff.json b/src/vs/editor/test/node/diffing/fixtures/ws-alignment/legacy.expected.diff.json new file mode 100644 index 00000000000..c248859e098 --- /dev/null +++ b/src/vs/editor/test/node/diffing/fixtures/ws-alignment/legacy.expected.diff.json @@ -0,0 +1,64 @@ +{ + "original": { + "content": "import { Stack, Text } from '@fluentui/react';\nimport { View } from '../../layout/layout';\n\nexport const WelcomeView = () => {\n\treturn (\n\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\tWelcome to the VS Code Tools application.\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t\n\t);\n}\n", + "fileName": "./1.tsx" + }, + "modified": { + "content": "import { Nav } from '@fluentui/react';\nimport { View } from '../../layout/layout';\n\nexport const WelcomeView = () => {\n\treturn (\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t);\n}\n", + "fileName": "./2.tsx" + }, + "diffs": [ + { + "originalRange": "[1,2)", + "modifiedRange": "[1,2)", + "innerChanges": [ + { + "originalRange": "[1,10 -> 1,21]", + "modifiedRange": "[1,10 -> 1,13]" + } + ] + }, + { + "originalRange": "[7,14)", + "modifiedRange": "[7,18)", + "innerChanges": [ + { + "originalRange": "[7,5 -> 7,11]", + "modifiedRange": "[7,5 -> 8,5]" + }, + { + "originalRange": "[7,14 -> 7,43]", + "modifiedRange": "[8,8 -> 11,140]" + }, + { + "originalRange": "[8,5 -> 8,6]", + "modifiedRange": "[12,5 -> 12,25]" + }, + { + "originalRange": "[8,9 -> 9,12]", + "modifiedRange": "[12,28 -> 12,131]" + }, + { + "originalRange": "[10,7 -> 10,22]", + "modifiedRange": "[13,7 -> 13,17]" + }, + { + "originalRange": "[10,30 -> 10,48]", + "modifiedRange": "[13,25 -> 14,8]" + }, + { + "originalRange": "[11,6 -> 11,13]", + "modifiedRange": "[15,6 -> 15,7]" + }, + { + "originalRange": "[12,5 -> 12,17]", + "modifiedRange": "[16,5 -> 16,7]" + }, + { + "originalRange": "[13,6 -> 13,11]", + "modifiedRange": "[17,6 -> 17,9]" + } + ] + } + ] +} \ No newline at end of file From e195ed84e970f825d887fab1bc5396468553e460 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 5 Jul 2023 18:16:21 +0200 Subject: [PATCH 170/926] adopt atomic delete for extensions (#187054) #180695 adopt atomic delete for extensions --- .../node/extensionManagementService.ts | 164 +++++++++--------- 1 file changed, 78 insertions(+), 86 deletions(-) diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index c0a62f89532..49ebc362edf 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -10,6 +10,7 @@ import { IStringDictionary } from 'vs/base/common/collections'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { getErrorMessage } from 'vs/base/common/errors'; import { Emitter } from 'vs/base/common/event'; +import { hash } from 'vs/base/common/hash'; import { Disposable } from 'vs/base/common/lifecycle'; import { ResourceSet } from 'vs/base/common/map'; import { Schemas } from 'vs/base/common/network'; @@ -19,7 +20,7 @@ import { joinPath } from 'vs/base/common/resources'; import * as semver from 'vs/base/common/semver/semver'; import { isBoolean, isUndefined } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; -import { generateUuid, isUUID } from 'vs/base/common/uuid'; +import { generateUuid } from 'vs/base/common/uuid'; import * as pfs from 'vs/base/node/pfs'; import { extract, ExtractError, IFile, zip } from 'vs/base/node/zip'; import * as nls from 'vs/nls'; @@ -62,6 +63,8 @@ export interface INativeServerExtensionManagementService extends IExtensionManag markAsUninstalled(...extensions: IExtension[]): Promise; } +const DELETED_FOLDER_POSTFIX = '.vsctmp'; + export class ExtensionManagementService extends AbstractExtensionManagementService implements INativeServerExtensionManagementService { private readonly extensionsScanner: ExtensionsScanner; @@ -417,14 +420,11 @@ export class ExtensionsScanner extends Disposable { private readonly _onExtract = this._register(new Emitter()); readonly onExtract = this._onExtract.event; - private cleanUpGeneratedFoldersPromise: Promise = Promise.resolve(); - constructor( private readonly beforeRemovingExtension: (e: ILocalExtension) => Promise, @IFileService private readonly fileService: IFileService, @IExtensionsScannerService private readonly extensionsScannerService: IExtensionsScannerService, @IExtensionsProfileScannerService private readonly extensionsProfileScannerService: IExtensionsProfileScannerService, - @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, @ILogService private readonly logService: ILogService, ) { super(); @@ -433,9 +433,8 @@ export class ExtensionsScanner extends Disposable { } async cleanUp(): Promise { + await this.removeTemporarilyDeletedFolders(); await this.removeUninstalledExtensions(); - this.cleanUpGeneratedFoldersPromise = this.cleanUpGeneratedFoldersPromise.then(() => this.removeGeneratedFolders()); - await this.cleanUpGeneratedFoldersPromise; } async scanExtensions(type: ExtensionType | null, profileLocation: URI): Promise { @@ -468,45 +467,65 @@ export class ExtensionsScanner extends Disposable { } async extractUserExtension(extensionKey: ExtensionKey, zipPath: string, metadata: Metadata, removeIfExists: boolean, token: CancellationToken): Promise { - await this.cleanUpGeneratedFoldersPromise.catch(() => undefined); - const folderName = extensionKey.toString(); - const tempPath = path.join(this.extensionsScannerService.userExtensionsLocation.fsPath, `.${generateUuid()}`); - const extensionPath = path.join(this.extensionsScannerService.userExtensionsLocation.fsPath, folderName); + const tempLocation = URI.file(path.join(this.extensionsScannerService.userExtensionsLocation.fsPath, `.${generateUuid()}`)); + const extensionLocation = URI.file(path.join(this.extensionsScannerService.userExtensionsLocation.fsPath, folderName)); - let exists = await this.fileService.exists(URI.file(extensionPath)); + let exists = await this.fileService.exists(extensionLocation); if (exists && removeIfExists) { try { - await pfs.Promises.rm(extensionPath); + await this.deleteExtensionFromLocation(extensionKey.id, extensionLocation, 'removeExisting'); } catch (error) { - throw new ExtensionManagementError(nls.localize('errorDeleting', "Unable to delete the existing folder '{0}' while installing the extension '{1}'. Please delete the folder manually and try again", extensionPath, extensionKey.id), ExtensionManagementErrorCode.Delete); + throw new ExtensionManagementError(nls.localize('errorDeleting', "Unable to delete the existing folder '{0}' while installing the extension '{1}'. Please delete the folder manually and try again", extensionLocation.fsPath, extensionKey.id), ExtensionManagementErrorCode.Delete); } exists = false; } if (!exists) { - await this.extractAtLocation(extensionKey, zipPath, tempPath, token); - await this.extensionsScannerService.updateMetadata(URI.file(tempPath), metadata); - try { - this._onExtract.fire(URI.file(extensionPath)); - await this.rename(extensionKey, tempPath, extensionPath, Date.now() + (2 * 60 * 1000) /* Retry for 2 minutes */); - this.logService.info('Renamed to', extensionPath); - } catch (error) { + // Extract try { - await pfs.Promises.rm(tempPath); - } catch (e) { /* ignore */ } - if (error.code === 'ENOTEMPTY') { - this.logService.info(`Rename failed because extension was installed by another source. So ignoring renaming.`, extensionKey.id); - } else { - this.logService.info(`Rename failed because of ${getErrorMessage(error)}. Deleted from extracted location`, tempPath); - throw error; + this.logService.trace(`Started extracting the extension from ${zipPath} to ${extensionLocation.fsPath}`); + await extract(zipPath, tempLocation.fsPath, { sourcePath: 'extension', overwrite: true }, token); + this.logService.info(`Extracted extension to ${extensionLocation}:`, extensionKey.id); + } catch (e) { + let errorCode = ExtensionManagementErrorCode.Extract; + if (e instanceof ExtractError) { + if (e.type === 'CorruptZip') { + errorCode = ExtensionManagementErrorCode.CorruptZip; + } else if (e.type === 'Incomplete') { + errorCode = ExtensionManagementErrorCode.IncompleteZip; + } + } + throw new ExtensionManagementError(e.message, errorCode); } + + await this.extensionsScannerService.updateMetadata(tempLocation, metadata); + + // Rename + try { + this.logService.trace(`Started renaming the extension from ${tempLocation.fsPath} to ${extensionLocation.fsPath}`); + await this.rename(extensionKey, tempLocation.fsPath, extensionLocation.fsPath, Date.now() + (2 * 60 * 1000) /* Retry for 2 minutes */); + this.logService.info('Renamed to', extensionLocation.fsPath); + } catch (error) { + if (error.code === 'ENOTEMPTY') { + this.logService.info(`Rename failed because extension was installed by another source. So ignoring renaming.`, extensionKey.id); + } else { + this.logService.info(`Rename failed because of ${getErrorMessage(error)}. Deleted from extracted location`, tempLocation); + throw error; + } + } + + this._onExtract.fire(extensionLocation); + + } catch (error) { + try { await this.fileService.del(tempLocation, { recursive: true }); } catch (e) { /* ignore */ } + throw error; } } - return this.scanLocalExtension(URI.file(extensionPath), ExtensionType.User); + return this.scanLocalExtension(extensionLocation, ExtensionType.User); } async scanMetadata(local: ILocalExtension, profileLocation?: URI): Promise { @@ -544,12 +563,8 @@ export class ExtensionsScanner extends Disposable { await this.withUninstalledExtensions(uninstalled => delete uninstalled[extensionKey.toString()]); } - async removeExtension(extension: ILocalExtension | IScannedExtension, type: string): Promise { - this.logService.trace(`Deleting ${type} extension from disk`, extension.identifier.id, extension.location.fsPath); - const renamedLocation = this.uriIdentityService.extUri.joinPath(this.uriIdentityService.extUri.dirname(extension.location), `._${generateUuid()}`); - await this.rename(extension.identifier, extension.location.fsPath, renamedLocation.fsPath, Date.now() + (2 * 60 * 1000) /* Retry for 2 minutes */); - await this.fileService.del(renamedLocation, { recursive: true }); - this.logService.info('Deleted from disk', extension.identifier.id, extension.location.fsPath); + removeExtension(extension: ILocalExtension | IScannedExtension, type: string): Promise { + return this.deleteExtensionFromLocation(extension.identifier.id, extension.location, type); } async removeUninstalledExtension(extension: ILocalExtension | IScannedExtension): Promise { @@ -565,6 +580,12 @@ export class ExtensionsScanner extends Disposable { await this.extensionsProfileScannerService.addExtensionsToProfile(extensions, toProfileLocation); } + private async deleteExtensionFromLocation(id: string, location: URI, type: string): Promise { + this.logService.trace(`Deleting ${type} extension from disk`, id, location.fsPath); + await this.fileService.del(location, { recursive: true, atomic: { postfix: `.${hash(generateUuid()).toString(16)}${DELETED_FOLDER_POSTFIX}` } }); + this.logService.info(`Deleted ${type} extension from disk`, id, location.fsPath); + } + private async withUninstalledExtensions(updateFn?: (uninstalled: IStringDictionary) => void): Promise> { return this.uninstalledFileLimiter.queue(async () => { let raw: string | undefined; @@ -597,33 +618,6 @@ export class ExtensionsScanner extends Disposable { }); } - private async extractAtLocation(identifier: IExtensionIdentifier, zipPath: string, location: string, token: CancellationToken): Promise { - this.logService.trace(`Started extracting the extension from ${zipPath} to ${location}`); - - // Clean the location - try { - await pfs.Promises.rm(location); - } catch (e) { - throw new ExtensionManagementError(this.joinErrors(e).message, ExtensionManagementErrorCode.Delete); - } - - try { - await extract(zipPath, location, { sourcePath: 'extension', overwrite: true }, token); - this.logService.info(`Extracted extension to ${location}:`, identifier.id); - } catch (e) { - try { await pfs.Promises.rm(location); } catch (e) { /* Ignore */ } - let errorCode = ExtensionManagementErrorCode.Extract; - if (e instanceof ExtractError) { - if (e.type === 'CorruptZip') { - errorCode = ExtensionManagementErrorCode.CorruptZip; - } else if (e.type === 'Incomplete') { - errorCode = ExtensionManagementErrorCode.IncompleteZip; - } - } - throw new ExtensionManagementError(e.message, errorCode); - } - } - private async rename(identifier: IExtensionIdentifier, extractPath: string, renamePath: string, retryUntil: number): Promise { try { await pfs.Promises.rename(extractPath, renamePath); @@ -709,9 +703,9 @@ export class ExtensionsScanner extends Disposable { await Promise.allSettled(toRemove.map(e => this.removeUninstalledExtension(e))); } - private async removeGeneratedFolders(): Promise { - this.logService.trace('ExtensionManagementService#removeGeneratedFolders'); - const promises: Promise[] = []; + private async removeTemporarilyDeletedFolders(): Promise { + this.logService.trace('ExtensionManagementService#removeTempDeleteFolders'); + let stat; try { stat = await this.fileService.resolve(this.extensionsScannerService.userExtensionsLocation); @@ -719,31 +713,29 @@ export class ExtensionsScanner extends Disposable { if (toFileOperationResult(error) !== FileOperationResult.FILE_NOT_FOUND) { this.logService.error(error); } + return; } - for (const child of stat?.children ?? []) { - if (child.isDirectory && child.name.startsWith('._') && isUUID(child.name.substring(2))) { - promises.push((async () => { - this.logService.trace('Deleting the generated extension folder', child.resource.toString()); - try { - await this.fileService.del(child.resource, { recursive: true }); - this.logService.info('Deleted the generated extension folder', child.resource.toString()); - } catch (error) { + + if (!stat?.children) { + return; + } + + try { + await Promise.allSettled(stat.children.map(async child => { + if (!child.isDirectory || !child.name.endsWith(DELETED_FOLDER_POSTFIX)) { + return; + } + this.logService.trace('Deleting the temporarily deleted folder', child.resource.toString()); + try { + await this.fileService.del(child.resource, { recursive: true }); + this.logService.trace('Deleted the temporarily deleted folder', child.resource.toString()); + } catch (error) { + if (toFileOperationResult(error) !== FileOperationResult.FILE_NOT_FOUND) { this.logService.error(error); } - })()); - } - } - await Promise.allSettled(promises); - } - - private joinErrors(errorOrErrors: (Error | string) | (Array)): Error { - const errors = Array.isArray(errorOrErrors) ? errorOrErrors : [errorOrErrors]; - if (errors.length === 1) { - return errors[0] instanceof Error ? errors[0] : new Error(errors[0]); - } - return errors.reduce((previousValue: Error, currentValue: Error | string) => { - return new Error(`${previousValue.message}${previousValue.message ? ',' : ''}${currentValue instanceof Error ? currentValue.message : currentValue}`); - }, new Error('')); + } + })); + } catch (error) { /* ignore */ } } } From 7e46f237d3de410c48a0132cb716902db5c88358 Mon Sep 17 00:00:00 2001 From: Raymond Zhao <7199958+rzhao271@users.noreply.github.com> Date: Wed, 5 Jul 2023 09:36:08 -0700 Subject: [PATCH 171/926] fix: clean up PreferencesActionContribution (#186952) --- .../browser/preferences.contribution.ts | 151 +++++------------- .../preferences/browser/preferencesWidgets.ts | 8 +- .../preferences/browser/settingsEditor2.ts | 10 +- .../contrib/preferences/common/preferences.ts | 1 - 4 files changed, 45 insertions(+), 125 deletions(-) diff --git a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts index 132afd37c47..ef83f66b598 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts @@ -34,7 +34,7 @@ import { ConfigureLanguageBasedSettingsAction } from 'vs/workbench/contrib/prefe import { SettingsEditorContribution } from 'vs/workbench/contrib/preferences/browser/preferencesEditor'; import { preferencesOpenSettingsIcon } from 'vs/workbench/contrib/preferences/browser/preferencesIcons'; import { SettingsEditor2, SettingsFocusContext } from 'vs/workbench/contrib/preferences/browser/settingsEditor2'; -import { CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDINGS_SEARCH_FOCUS, CONTEXT_KEYBINDING_FOCUS, CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_EDITOR_IN_USER_TAB, CONTEXT_SETTINGS_JSON_EDITOR, CONTEXT_SETTINGS_ROW_FOCUS, CONTEXT_SETTINGS_SEARCH_FOCUS, CONTEXT_TOC_ROW_FOCUS, CONTEXT_WHEN_FOCUS, KEYBINDINGS_EDITOR_COMMAND_ACCEPT_WHEN, KEYBINDINGS_EDITOR_COMMAND_ADD, KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_HISTORY, KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, KEYBINDINGS_EDITOR_COMMAND_COPY, KEYBINDINGS_EDITOR_COMMAND_COPY_COMMAND, KEYBINDINGS_EDITOR_COMMAND_COPY_COMMAND_TITLE, KEYBINDINGS_EDITOR_COMMAND_DEFINE, KEYBINDINGS_EDITOR_COMMAND_DEFINE_WHEN, KEYBINDINGS_EDITOR_COMMAND_FOCUS_KEYBINDINGS, KEYBINDINGS_EDITOR_COMMAND_RECORD_SEARCH_KEYS, KEYBINDINGS_EDITOR_COMMAND_REJECT_WHEN, KEYBINDINGS_EDITOR_COMMAND_REMOVE, KEYBINDINGS_EDITOR_COMMAND_RESET, KEYBINDINGS_EDITOR_COMMAND_SEARCH, KEYBINDINGS_EDITOR_COMMAND_SHOW_SIMILAR, KEYBINDINGS_EDITOR_COMMAND_SORTBY_PRECEDENCE, KEYBINDINGS_EDITOR_SHOW_DEFAULT_KEYBINDINGS, KEYBINDINGS_EDITOR_SHOW_EXTENSION_KEYBINDINGS, KEYBINDINGS_EDITOR_SHOW_USER_KEYBINDINGS, REQUIRE_TRUSTED_WORKSPACE_SETTING_TAG, SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU } from 'vs/workbench/contrib/preferences/common/preferences'; +import { CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDINGS_SEARCH_FOCUS, CONTEXT_KEYBINDING_FOCUS, CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_JSON_EDITOR, CONTEXT_SETTINGS_ROW_FOCUS, CONTEXT_SETTINGS_SEARCH_FOCUS, CONTEXT_TOC_ROW_FOCUS, CONTEXT_WHEN_FOCUS, KEYBINDINGS_EDITOR_COMMAND_ACCEPT_WHEN, KEYBINDINGS_EDITOR_COMMAND_ADD, KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_HISTORY, KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, KEYBINDINGS_EDITOR_COMMAND_COPY, KEYBINDINGS_EDITOR_COMMAND_COPY_COMMAND, KEYBINDINGS_EDITOR_COMMAND_COPY_COMMAND_TITLE, KEYBINDINGS_EDITOR_COMMAND_DEFINE, KEYBINDINGS_EDITOR_COMMAND_DEFINE_WHEN, KEYBINDINGS_EDITOR_COMMAND_FOCUS_KEYBINDINGS, KEYBINDINGS_EDITOR_COMMAND_RECORD_SEARCH_KEYS, KEYBINDINGS_EDITOR_COMMAND_REJECT_WHEN, KEYBINDINGS_EDITOR_COMMAND_REMOVE, KEYBINDINGS_EDITOR_COMMAND_RESET, KEYBINDINGS_EDITOR_COMMAND_SEARCH, KEYBINDINGS_EDITOR_COMMAND_SHOW_SIMILAR, KEYBINDINGS_EDITOR_COMMAND_SORTBY_PRECEDENCE, KEYBINDINGS_EDITOR_SHOW_DEFAULT_KEYBINDINGS, KEYBINDINGS_EDITOR_SHOW_EXTENSION_KEYBINDINGS, KEYBINDINGS_EDITOR_SHOW_USER_KEYBINDINGS, REQUIRE_TRUSTED_WORKSPACE_SETTING_TAG, SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU } from 'vs/workbench/contrib/preferences/common/preferences'; import { PreferencesContribution } from 'vs/workbench/contrib/preferences/common/preferencesContribution'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; @@ -57,8 +57,6 @@ const SETTINGS_EDITOR_COMMAND_FOCUS_CONTROL = 'settings.action.focusSettingContr const SETTINGS_EDITOR_COMMAND_FOCUS_UP = 'settings.action.focusLevelUp'; const SETTINGS_EDITOR_COMMAND_SWITCH_TO_JSON = 'settings.switchToJSON'; -const SETTINGS_EDITOR_COMMAND_SWITCH_TO_APPLICATION_JSON = 'settings.switchToApplicationJSON'; -const SETTINGS_EDITOR_COMMAND_SWITCH_TO_CURRENT_PROFILE_JSON = 'settings.switchToCurrentProfileJSON'; const SETTINGS_EDITOR_COMMAND_FILTER_ONLINE = 'settings.filterByOnline'; const SETTINGS_EDITOR_COMMAND_FILTER_UNTRUSTED = 'settings.filterUntrusted'; @@ -122,7 +120,7 @@ Registry.as(EditorExtensions.EditorFactory).registerEdit const OPEN_USER_SETTINGS_UI_TITLE = { value: nls.localize('openSettings2', "Open Settings (UI)"), original: 'Open Settings (UI)' }; const OPEN_USER_SETTINGS_JSON_TITLE = { value: nls.localize('openUserSettingsJson', "Open User Settings (JSON)"), original: 'Open User Settings (JSON)' }; -const OPEN_CURRENT_PROFILE_SETTINGS_JSON_TITLE = { value: nls.localize('openCurrentProfileSettingsJson', "Open Current Profile Settings (JSON)"), original: 'Open Current Profile Settings (JSON)' }; +const OPEN_APPLICATION_SETTINGS_JSON_TITLE = { value: nls.localize('openApplicationSettingsJson', "Open Application Settings (JSON)"), original: 'Open Application Settings (JSON)' }; const category = { value: nls.localize('preferences', "Preferences"), original: 'Preferences' }; interface IOpenSettingsActionOptions { @@ -209,30 +207,27 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon } }); - const that = this; - const registerOpenSettingsJsonCommandDisposable = this._register(new MutableDisposable()); - const registerOpenSettingsJsonCommand = () => { - registerOpenSettingsJsonCommandDisposable.value = registerAction2(class extends Action2 { - constructor() { - super({ - id: 'workbench.action.openSettingsJson', - title: that.userDataProfileService.currentProfile.isDefault ? OPEN_USER_SETTINGS_JSON_TITLE : OPEN_CURRENT_PROFILE_SETTINGS_JSON_TITLE, - category, - f1: true, - }); - } - run(accessor: ServicesAccessor, args: IOpenSettingsActionOptions) { - args = sanitizeOpenSettingsArgs(args); - return accessor.get(IPreferencesService).openSettings({ jsonEditor: true, ...args }); - } - }); - }; + registerAction2(class extends Action2 { + constructor() { + super({ + id: 'workbench.action.openSettingsJson', + title: OPEN_USER_SETTINGS_JSON_TITLE, + category, + f1: true, + }); + } + run(accessor: ServicesAccessor, args: IOpenSettingsActionOptions) { + args = sanitizeOpenSettingsArgs(args); + return accessor.get(IPreferencesService).openSettings({ jsonEditor: true, ...args }); + } + }); + const that = this; registerAction2(class extends Action2 { constructor() { super({ id: 'workbench.action.openApplicationSettingsJson', - title: OPEN_USER_SETTINGS_JSON_TITLE, + title: OPEN_APPLICATION_SETTINGS_JSON_TITLE, category, menu: { id: MenuId.CommandPalette, @@ -276,13 +271,11 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon }); const registerOpenUserSettingsEditorFromJsonActionDisposable = this._register(new MutableDisposable()); + const openUserSettingsEditorWhen = ContextKeyExpr.and( + ContextKeyExpr.or(ResourceContextKey.Resource.isEqualTo(this.userDataProfileService.currentProfile.settingsResource.toString()), + ResourceContextKey.Resource.isEqualTo(this.userDataProfilesService.defaultProfile.settingsResource.toString())), + ContextKeyExpr.not('isInDiffEditor')); const registerOpenUserSettingsEditorFromJsonAction = () => { - let when = ContextKeyExpr.and(ResourceContextKey.Resource.isEqualTo(this.userDataProfileService.currentProfile.settingsResource.toString()), ContextKeyExpr.not('isInDiffEditor')); - if (!this.userDataProfileService.currentProfile.isDefault) { - // If the default profile is not active, also show the action when we're in the - // default profile JSON file, which contains the application-scoped settings. - when = ContextKeyExpr.or(when, ContextKeyExpr.and(ResourceContextKey.Resource.isEqualTo(this.userDataProfilesService.defaultProfile.settingsResource.toString()), ContextKeyExpr.not('isInDiffEditor'))); - } registerOpenUserSettingsEditorFromJsonActionDisposable.value = registerAction2(class extends Action2 { constructor() { super({ @@ -291,7 +284,7 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon icon: preferencesOpenSettingsIcon, menu: [{ id: MenuId.EditorTitle, - when, + when: openUserSettingsEditorWhen, group: 'navigation', order: 1 }] @@ -304,93 +297,35 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon }); }; - const openJsonFromSettingsEditorDisposableStore = this._register(new DisposableStore()); - const registerOpenJsonFromSettingsEditorAction = () => { - openJsonFromSettingsEditorDisposableStore.clear(); - if (!this.userDataProfileService.currentProfile.isDefault) { - // When the default profile is not active, the action for the User tab needs a dropdown - // because User tab settings in that case are actually saved in two separate files. - const submenuId = MenuId.for('PreferencesSubMenu'); - openJsonFromSettingsEditorDisposableStore.add(registerAction2(class extends Action2 { - constructor() { - super({ - id: SETTINGS_EDITOR_COMMAND_SWITCH_TO_CURRENT_PROFILE_JSON, - title: OPEN_CURRENT_PROFILE_SETTINGS_JSON_TITLE, - menu: [{ id: submenuId, order: 1 }] - }); - } - run(accessor: ServicesAccessor) { - const editorPane = accessor.get(IEditorService).activeEditorPane; - if (editorPane instanceof SettingsEditor2) { - return editorPane.switchToSettingsFile(); - } - return null; - } - })); - openJsonFromSettingsEditorDisposableStore.add(registerAction2(class extends Action2 { - constructor() { - super({ - id: SETTINGS_EDITOR_COMMAND_SWITCH_TO_APPLICATION_JSON, - title: OPEN_USER_SETTINGS_JSON_TITLE, - menu: [{ id: submenuId, order: 2 }] - }); - } - run(accessor: ServicesAccessor) { - const editorPane = accessor.get(IEditorService).activeEditorPane; - if (editorPane instanceof SettingsEditor2) { - return editorPane.switchToApplicationSettingsFile(); - } - return null; - } - })); - openJsonFromSettingsEditorDisposableStore.add(MenuRegistry.appendMenuItem(MenuId.EditorTitle, { + const openSettingsJsonWhen = ContextKeyExpr.and(CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_JSON_EDITOR.toNegated()); + registerAction2(class extends Action2 { + constructor() { + super({ + id: SETTINGS_EDITOR_COMMAND_SWITCH_TO_JSON, title: { value: nls.localize('openSettingsJson', "Open Settings (JSON)"), original: 'Open Settings (JSON)' }, - submenu: submenuId, icon: preferencesOpenSettingsIcon, - when: ContextKeyExpr.and(CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_EDITOR_IN_USER_TAB, CONTEXT_SETTINGS_JSON_EDITOR.toNegated()), - group: 'navigation', - order: 1 - })); + menu: [{ + id: MenuId.EditorTitle, + when: openSettingsJsonWhen, + group: 'navigation', + order: 1 + }] + }); } - - let openSettingsJsonWhen = ContextKeyExpr.and(CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_JSON_EDITOR.toNegated()); - if (!this.userDataProfileService.currentProfile.isDefault) { - // If we're not in the default profile, we already created the action for the User tab above, - // so we want to make sure the user is not in the User tab for this more general action. - openSettingsJsonWhen = ContextKeyExpr.and(openSettingsJsonWhen, CONTEXT_SETTINGS_EDITOR_IN_USER_TAB.toNegated()); + run(accessor: ServicesAccessor) { + const editorPane = accessor.get(IEditorService).activeEditorPane; + if (editorPane instanceof SettingsEditor2) { + return editorPane.switchToSettingsFile(); + } + return null; } - openJsonFromSettingsEditorDisposableStore.add(registerAction2(class extends Action2 { - constructor() { - super({ - id: SETTINGS_EDITOR_COMMAND_SWITCH_TO_JSON, - title: { value: nls.localize('openSettingsJson', "Open Settings (JSON)"), original: 'Open Settings (JSON)' }, - icon: preferencesOpenSettingsIcon, - menu: [{ - id: MenuId.EditorTitle, - when: openSettingsJsonWhen, - group: 'navigation', - order: 1 - }] - }); - } - run(accessor: ServicesAccessor) { - const editorPane = accessor.get(IEditorService).activeEditorPane; - if (editorPane instanceof SettingsEditor2) { - return editorPane.switchToSettingsFile(); - } - return null; - } - })); - }; + }); registerOpenUserSettingsEditorFromJsonAction(); - registerOpenJsonFromSettingsEditorAction(); - registerOpenSettingsJsonCommand(); this._register(this.userDataProfileService.onDidChangeCurrentProfile(() => { + // Force the action to check the context again. registerOpenUserSettingsEditorFromJsonAction(); - registerOpenJsonFromSettingsEditorAction(); - registerOpenSettingsJsonCommand(); })); registerAction2(class extends Action2 { diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts b/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts index c4f4a6b1fdd..7383d1b5918 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts @@ -34,8 +34,6 @@ import { isWorkspaceFolder, IWorkspaceContextService, IWorkspaceFolder, Workbenc import { settingsEditIcon, settingsScopeDropDownIcon } from 'vs/workbench/contrib/preferences/browser/preferencesIcons'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { ILanguageService } from 'vs/editor/common/languages/language'; -import { CONTEXT_SETTINGS_EDITOR_IN_USER_TAB } from 'vs/workbench/contrib/preferences/common/preferences'; - export class FolderSettingsActionViewItem extends BaseActionViewItem { private _folder: IWorkspaceFolder | null; @@ -217,7 +215,6 @@ export class SettingsTargetsWidget extends Widget { private folderSettingsAction!: Action; private folderSettings!: FolderSettingsActionViewItem; private options: ISettingsTargetsWidgetOptions; - private inUserTab: IContextKey; private _settingsTarget: SettingsTarget | null = null; @@ -231,15 +228,13 @@ export class SettingsTargetsWidget extends Widget { @IInstantiationService private readonly instantiationService: IInstantiationService, @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, @ILabelService private readonly labelService: ILabelService, - @ILanguageService private readonly languageService: ILanguageService, - @IContextKeyService contextKeyService: IContextKeyService, + @ILanguageService private readonly languageService: ILanguageService ) { super(); this.options = options ?? {}; this.create(parent); this._register(this.contextService.onDidChangeWorkbenchState(() => this.onWorkbenchStateChanged())); this._register(this.contextService.onDidChangeWorkspaceFolders(() => this.update())); - this.inUserTab = CONTEXT_SETTINGS_EDITOR_IN_USER_TAB.bindTo(contextKeyService); } private resetLabels() { @@ -297,7 +292,6 @@ export class SettingsTargetsWidget extends Widget { } else { this.folderSettings.action.checked = false; } - this.inUserTab.set(this.userLocalSettings.checked); } setResultCount(settingsTarget: SettingsTarget, count: number): void { diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index a1b3f488f78..3ce681d6033 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -707,17 +707,12 @@ export class SettingsEditor2 extends EditorPane { } } - switchToApplicationSettingsFile(): Promise { - const query = parseQuery(this.searchWidget.getValue()).query; - return this.openSettingsFile({ query }, true); - } - switchToSettingsFile(): Promise { const query = parseQuery(this.searchWidget.getValue()).query; return this.openSettingsFile({ query }); } - private async openSettingsFile(options?: ISettingsEditorOptions, forceOpenApplicationSettings?: boolean): Promise { + private async openSettingsFile(options?: ISettingsEditorOptions): Promise { const currentSettingsTarget = this.settingsTargetsWidget.settingsTarget; const openOptions: IOpenSettingsOptions = { jsonEditor: true, ...options }; @@ -729,9 +724,6 @@ export class SettingsEditor2 extends EditorPane { return this.preferencesService.openApplicationSettings(openOptions); } } - if (forceOpenApplicationSettings) { - return this.preferencesService.openApplicationSettings(openOptions); - } return this.preferencesService.openUserSettings(openOptions); } else if (currentSettingsTarget === ConfigurationTarget.USER_REMOTE) { return this.preferencesService.openRemoteSettings(openOptions); diff --git a/src/vs/workbench/contrib/preferences/common/preferences.ts b/src/vs/workbench/contrib/preferences/common/preferences.ts index da96e117bb0..3ad2d9f0423 100644 --- a/src/vs/workbench/contrib/preferences/common/preferences.ts +++ b/src/vs/workbench/contrib/preferences/common/preferences.ts @@ -58,7 +58,6 @@ export const CONTEXT_KEYBINDINGS_EDITOR = new RawContextKey('inKeybindi export const CONTEXT_KEYBINDINGS_SEARCH_FOCUS = new RawContextKey('inKeybindingsSearch', false); export const CONTEXT_KEYBINDING_FOCUS = new RawContextKey('keybindingFocus', false); export const CONTEXT_WHEN_FOCUS = new RawContextKey('whenFocus', false); -export const CONTEXT_SETTINGS_EDITOR_IN_USER_TAB = new RawContextKey('inSettingsEditorUserTab', false); export const KEYBINDINGS_EDITOR_COMMAND_SEARCH = 'keybindings.editor.searchKeybindings'; export const KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS = 'keybindings.editor.clearSearchResults'; From 5a82dbde13ba694bfa174b35d9cb39507a35b171 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 5 Jul 2023 09:48:27 -0700 Subject: [PATCH 172/926] fix #173384 --- .../contrib/tasks/browser/abstractTaskService.ts | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index 1f1780ebdac..861e70331c9 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -2749,16 +2749,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return true; } - private async _ensureWorkspaceTasks(): Promise { - if (!this._workspaceTasksPromise) { - await this.getWorkspaceTasks(); - } else { - await this._workspaceTasksPromise; - } - } - private async _runTaskCommand(filter?: string | ITaskIdentifier): Promise { - await this._ensureWorkspaceTasks(); if (!filter) { return this._doRunTaskCommand(); } @@ -2916,7 +2907,6 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer title: strings.fetching }; const promise = (async () => { - await this._ensureWorkspaceTasks(); let taskGroupTasks: (Task | ConfiguringTask)[] = []; async function runSingleTask(task: Task | undefined, problemMatcherOptions: IProblemMatcherRunOptions | undefined, that: AbstractTaskService) { From 998481e913c466d7e23dcd089bb624a22bf1fecd Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 5 Jul 2023 10:17:19 -0700 Subject: [PATCH 173/926] wait for task system --- .../contrib/tasks/browser/abstractTaskService.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index 861e70331c9..3ae506cfaa7 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -373,12 +373,20 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer this._tasksReconnected = true; return; } - this._getTaskSystem(); this.getWorkspaceTasks().then(async () => { this._tasksReconnected = await this._reconnectTasks(); }); } + private async _waitForTaskSystem(): Promise { + if (this.hasTaskSystemInfo) { + return; + } + // Wait until we have task system info (the extension host and workspace folders are available). + this._logService.trace('RunBuildTask: Awaiting task system info.'); + await Event.toPromise(Event.once(this.onDidChangeTaskSystemInfo)); + } + private async _reconnectTasks(): Promise { const tasks = await this.getSavedTasks('persistent'); if (!tasks.length) { @@ -2199,6 +2207,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer if (!(await this._trust())) { return new Map(); } + await this._waitForTaskSystem(); await this._waitForSupportedExecutions; // The build task might be run before folder open. On folder open, we need to update the tasks so that // all tasks are parsed. #173384 @@ -2750,6 +2759,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } private async _runTaskCommand(filter?: string | ITaskIdentifier): Promise { + await this._waitForTaskSystem(); if (!filter) { return this._doRunTaskCommand(); } @@ -2907,6 +2917,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer title: strings.fetching }; const promise = (async () => { + await this._waitForTaskSystem(); let taskGroupTasks: (Task | ConfiguringTask)[] = []; async function runSingleTask(task: Task | undefined, problemMatcherOptions: IProblemMatcherRunOptions | undefined, that: AbstractTaskService) { From 75da3d5a6b82d668a1b7629d17fc3aad176376fc Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Wed, 5 Jul 2023 10:35:34 -0700 Subject: [PATCH 174/926] testing: fix setting of `testing.isInPeek` context key (#187066) Fixes #186979 --- .../contrib/testing/browser/testingOutputPeek.ts | 14 ++++++++++---- .../contrib/testing/common/testingContextKeys.ts | 2 +- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts index 134ec676516..e806d87008e 100644 --- a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts +++ b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts @@ -711,13 +711,10 @@ class TestResultsViewContent extends Disposable { historyVisible: IObservableValue; showRevealLocationOnMessages: boolean; }, - @IContextKeyService contextKeyService: IContextKeyService, @IInstantiationService private readonly instantiationService: IInstantiationService, @ITextModelService protected readonly modelService: ITextModelService, ) { super(); - - TestingContextKeys.isInPeek.bindTo(contextKeyService); } public fillBody(containerElement: HTMLElement): void { @@ -806,6 +803,7 @@ class TestResultsPeek extends PeekViewWidget { private readonly visibilityChange = this._disposables.add(new Emitter()); private readonly content: TestResultsViewContent; + private scopedContextKeyService?: IContextKeyService; private dimension?: dom.Dimension; public current?: InspectSubject; @@ -821,7 +819,6 @@ class TestResultsPeek extends PeekViewWidget { ) { super(editor, { showFrame: true, frameWidth: 1, showArrow: true, isResizeable: true, isAccessible: true, className: 'test-output-peek' }, instantiationService); - TestingContextKeys.isInPeek.bindTo(contextKeyService); this._disposables.add(themeService.onDidColorThemeChange(this.applyTheme, this)); this._disposables.add(this.onDidClose(() => this.visibilityChange.fire(false))); this.content = this._disposables.add(instantiationService.createInstance(TestResultsViewContent, editor, { historyVisible: testingPeek.historyVisible, showRevealLocationOnMessages: false })); @@ -841,6 +838,15 @@ class TestResultsPeek extends PeekViewWidget { }); } + protected override _fillContainer(container: HTMLElement): void { + if (!this.scopedContextKeyService) { + this.scopedContextKeyService = this._disposables.add(this.contextKeyService.createScoped(container)); + TestingContextKeys.isInPeek.bindTo(this.scopedContextKeyService).set(true); + } + + super._fillContainer(container); + } + protected override _fillHead(container: HTMLElement): void { super._fillHead(container); diff --git a/src/vs/workbench/contrib/testing/common/testingContextKeys.ts b/src/vs/workbench/contrib/testing/common/testingContextKeys.ts index 879483949e2..05e5536590e 100644 --- a/src/vs/workbench/contrib/testing/common/testingContextKeys.ts +++ b/src/vs/workbench/contrib/testing/common/testingContextKeys.ts @@ -35,7 +35,7 @@ export namespace TestingContextKeys { export const viewMode = new RawContextKey('testing.explorerViewMode', TestExplorerViewMode.List); export const viewSorting = new RawContextKey('testing.explorerViewSorting', TestExplorerViewSorting.ByLocation); export const isRunning = new RawContextKey('testing.isRunning', false); - export const isInPeek = new RawContextKey('testing.isInPeek', true); + export const isInPeek = new RawContextKey('testing.isInPeek', false); export const isPeekVisible = new RawContextKey('testing.isPeekVisible', false); export const peekItemType = new RawContextKey('peekItemType', undefined, { From bd847cf75d0a93a5619152728137f4917be31068 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 5 Jul 2023 10:41:55 -0700 Subject: [PATCH 175/926] swap order --- .../workbench/contrib/tasks/browser/abstractTaskService.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index 3ae506cfaa7..fd5cd393fc6 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -2207,11 +2207,9 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer if (!(await this._trust())) { return new Map(); } - await this._waitForTaskSystem(); await this._waitForSupportedExecutions; - // The build task might be run before folder open. On folder open, we need to update the tasks so that - // all tasks are parsed. #173384 - if (runSource !== TaskRunSource.FolderOpen && this._workspaceTasksPromise) { + await this._waitForTaskSystem(); + if (this._workspaceTasksPromise) { return this._workspaceTasksPromise; } return this._updateWorkspaceTasks(runSource); From d772a63452f080dcae80a727ca38cbe1e4e73807 Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Wed, 5 Jul 2023 10:48:10 -0700 Subject: [PATCH 176/926] remove redundant tag --- .../interactive/browser/interactive.contribution.ts | 5 +---- .../notebook/browser/services/notebookServiceImpl.ts | 8 ++++---- .../workbench/contrib/notebook/common/notebookCommon.ts | 2 -- .../workbench/contrib/notebook/common/notebookProvider.ts | 3 --- .../workbench/contrib/notebook/common/notebookService.ts | 1 + src/vs/workbench/services/extensions/common/extensions.ts | 3 ++- 6 files changed, 8 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/contrib/interactive/browser/interactive.contribution.ts b/src/vs/workbench/contrib/interactive/browser/interactive.contribution.ts index 86bf90f9304..da64485f909 100644 --- a/src/vs/workbench/contrib/interactive/browser/interactive.contribution.ts +++ b/src/vs/workbench/contrib/interactive/browser/interactive.contribution.ts @@ -89,15 +89,12 @@ export class InteractiveDocumentContribution extends Disposable implements IWork const info = notebookService.getContributedNotebookType('interactive'); // We need to contribute a notebook type for the Interactive Window to provide notebook models. - // Don't add a file selector for the notebook type to avoid having the notebook Service create an editor for it. - // The IW editor is registered below, and we don't want it overwritten by the notebook Service. if (!info) { this._register(notebookService.registerContributedNotebookType('interactive', { providerDisplayName: 'Interactive Notebook', displayName: 'Interactive', filenamePattern: ['*.interactive'], - exclusive: true, - externalEditor: true + exclusive: true })); } diff --git a/src/vs/workbench/contrib/notebook/browser/services/notebookServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/services/notebookServiceImpl.ts index 6afedbfff8e..0c82eb7cb7f 100644 --- a/src/vs/workbench/contrib/notebook/browser/services/notebookServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/services/notebookServiceImpl.ts @@ -279,8 +279,9 @@ export class NotebookProviderInfoStore extends Disposable { } this._contributedEditors.set(info.id, info); let editorRegistration: IDisposable | undefined; - // Don't overwrite editor contributions if they come from elsewhere - if (!info.externalEditor) { + + // built-in notebook providers contribute their own editors + if (info.extension) { editorRegistration = this._registerContributionPoint(info); this._contributedEditorDisposables.add(editorRegistration); } @@ -640,8 +641,7 @@ export class NotebookService extends Disposable implements INotebookService { providerDisplayName: data.providerDisplayName, exclusive: data.exclusive, priority: RegisteredEditorPriority.default, - selectors: [], - externalEditor: !!data.externalEditor + selectors: [] }); info.update({ selectors: data.filenamePattern }); diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index f9df8c3c694..e57bc24b374 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -533,8 +533,6 @@ export interface INotebookContributionData { displayName: string; filenamePattern: (string | glob.IRelativePattern | INotebookExclusiveDocumentFilter)[]; exclusive: boolean; - /// Editor contribution is handled elswhere e.g. interactive - externalEditor?: boolean; } diff --git a/src/vs/workbench/contrib/notebook/common/notebookProvider.ts b/src/vs/workbench/contrib/notebook/common/notebookProvider.ts index 8345ef61981..16a9ee3a57b 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookProvider.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookProvider.ts @@ -20,7 +20,6 @@ export interface NotebookEditorDescriptor { readonly priority: RegisteredEditorPriority; readonly providerDisplayName: string; readonly exclusive: boolean; - readonly externalEditor?: boolean; } export class NotebookProviderInfo { @@ -31,7 +30,6 @@ export class NotebookProviderInfo { readonly priority: RegisteredEditorPriority; readonly providerDisplayName: string; readonly exclusive: boolean; - readonly externalEditor: boolean; private _selectors: NotebookSelector[]; get selectors() { @@ -59,7 +57,6 @@ export class NotebookProviderInfo { transientOutputs: false, cellContentMetadata: {} }; - this.externalEditor = !!descriptor.externalEditor; } update(args: { selectors?: NotebookSelector[]; options?: TransientOptions }) { diff --git a/src/vs/workbench/contrib/notebook/common/notebookService.ts b/src/vs/workbench/contrib/notebook/common/notebookService.ts index 2024a6fb925..6614adc8fbf 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookService.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookService.ts @@ -79,6 +79,7 @@ export interface INotebookService { getNotebookTextModels(): Iterable; listNotebookDocuments(): readonly NotebookTextModel[]; + /** Register a notebook type that we will handle. The notebook editor will be registered for notebook types contributed by extensions */ registerContributedNotebookType(viewType: string, data: INotebookContributionData): IDisposable; getContributedNotebookType(viewType: string): NotebookProviderInfo | undefined; getContributedNotebookTypes(resource?: URI): readonly NotebookProviderInfo[]; diff --git a/src/vs/workbench/services/extensions/common/extensions.ts b/src/vs/workbench/services/extensions/common/extensions.ts index 436f780bd00..cb18e8ac6f3 100644 --- a/src/vs/workbench/services/extensions/common/extensions.ts +++ b/src/vs/workbench/services/extensions/common/extensions.ts @@ -260,7 +260,8 @@ function extensionDescriptionArrayToMap(extensions: IExtensionDescription[]): Ex } export function isProposedApiEnabled(extension: IExtensionDescription, proposal: ApiProposalName): boolean { - if (!extension.enabledApiProposals) { + if (!extension. + enabledApiProposals) { return false; } return extension.enabledApiProposals.includes(proposal); From 8e574b41c99da483f916de530f2189093899fa21 Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Wed, 5 Jul 2023 11:04:40 -0700 Subject: [PATCH 177/926] undo format --- src/vs/workbench/services/extensions/common/extensions.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/workbench/services/extensions/common/extensions.ts b/src/vs/workbench/services/extensions/common/extensions.ts index cb18e8ac6f3..436f780bd00 100644 --- a/src/vs/workbench/services/extensions/common/extensions.ts +++ b/src/vs/workbench/services/extensions/common/extensions.ts @@ -260,8 +260,7 @@ function extensionDescriptionArrayToMap(extensions: IExtensionDescription[]): Ex } export function isProposedApiEnabled(extension: IExtensionDescription, proposal: ApiProposalName): boolean { - if (!extension. - enabledApiProposals) { + if (!extension.enabledApiProposals) { return false; } return extension.enabledApiProposals.includes(proposal); From 4e224575d2bbfb4292c790ad4805264b1fa77fc9 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 5 Jul 2023 11:17:42 -0700 Subject: [PATCH 178/926] ensure workspace tasks --- .../contrib/tasks/browser/abstractTaskService.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index fd5cd393fc6..9597fd9689f 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -2756,8 +2756,16 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return true; } + private async _ensureWorkspaceTasks(): Promise { + if (!this._workspaceTasksPromise) { + await this.getWorkspaceTasks(); + } else { + await this._workspaceTasksPromise; + } + } + private async _runTaskCommand(filter?: string | ITaskIdentifier): Promise { - await this._waitForTaskSystem(); + await this._ensureWorkspaceTasks(); if (!filter) { return this._doRunTaskCommand(); } @@ -2915,7 +2923,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer title: strings.fetching }; const promise = (async () => { - await this._waitForTaskSystem(); + await this._ensureWorkspaceTasks(); let taskGroupTasks: (Task | ConfiguringTask)[] = []; async function runSingleTask(task: Task | undefined, problemMatcherOptions: IProblemMatcherRunOptions | undefined, that: AbstractTaskService) { From 57215881c5b277904df8e13d11dbc169fcd4aa9e Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Wed, 5 Jul 2023 11:18:01 -0700 Subject: [PATCH 179/926] testing: fix appendOutput not showing up in real time (#187068) Fire a test change event when output changes. Previously it only appeared when something else forced decorations to sync. Fixes #184825 --- .../explorerProjections/listProjection.ts | 5 +++++ .../explorerProjections/treeProjection.ts | 4 ++++ .../contrib/testing/browser/testingOutputPeek.ts | 4 +++- .../contrib/testing/common/testResult.ts | 16 +++++++--------- 4 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/contrib/testing/browser/explorerProjections/listProjection.ts b/src/vs/workbench/contrib/testing/browser/explorerProjections/listProjection.ts index c2dc436100a..4b12fc5de75 100644 --- a/src/vs/workbench/contrib/testing/browser/explorerProjections/listProjection.ts +++ b/src/vs/workbench/contrib/testing/browser/explorerProjections/listProjection.ts @@ -12,6 +12,7 @@ import { flatTestItemDelimiter } from 'vs/workbench/contrib/testing/browser/expl import { ITestTreeProjection, TestExplorerTreeElement, TestItemTreeElement, TestTreeErrorMessage, getChildrenForParent, testIdentityProvider } from 'vs/workbench/contrib/testing/browser/explorerProjections/index'; import { ISerializedTestTreeCollapseState, isCollapsedInSerializedTestTree } from 'vs/workbench/contrib/testing/browser/explorerProjections/testingViewState'; import { TestId } from 'vs/workbench/contrib/testing/common/testId'; +import { TestResultItemChangeReason } from 'vs/workbench/contrib/testing/common/testResult'; import { ITestResultService } from 'vs/workbench/contrib/testing/common/testResultService'; import { ITestService } from 'vs/workbench/contrib/testing/common/testService'; import { ITestItemUpdate, InternalTestItem, TestDiffOpType, TestItemExpandState, TestResultState, TestsDiff, applyTestItemUpdate } from 'vs/workbench/contrib/testing/common/testTypes'; @@ -106,6 +107,10 @@ export class ListProjection extends Disposable implements ITestTreeProjection { // when test states change, reflect in the tree this._register(results.onTestChanged(ev => { + if (ev.reason === TestResultItemChangeReason.NewMessage) { + return; // no effect in the tree + } + let result = ev.item; // if the state is unset, or the latest run is not making the change, // double check that it's valid. Retire calls might cause previous diff --git a/src/vs/workbench/contrib/testing/browser/explorerProjections/treeProjection.ts b/src/vs/workbench/contrib/testing/browser/explorerProjections/treeProjection.ts index 253b27d1d4d..fe492af83eb 100644 --- a/src/vs/workbench/contrib/testing/browser/explorerProjections/treeProjection.ts +++ b/src/vs/workbench/contrib/testing/browser/explorerProjections/treeProjection.ts @@ -139,6 +139,10 @@ export class TreeProjection extends Disposable implements ITestTreeProjection { // when test states change, reflect in the tree this._register(results.onTestChanged(ev => { + if (ev.reason === TestResultItemChangeReason.NewMessage) { + return; // no effect in the tree + } + let result = ev.item; // if the state is unset, or the latest run is not making the change, // double check that it's valid. Retire calls might cause previous diff --git a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts index e806d87008e..993ea6cd0a8 100644 --- a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts +++ b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts @@ -1702,7 +1702,9 @@ class OutputPeekTree extends Disposable { const itemNode = taskNode.itemsCache.get(e.item); if (itemNode && this.tree.hasElement(itemNode)) { - this.tree.setChildren(itemNode, getTestChildren(result, e.item, index), { diffIdentityProvider }); + if (e.reason === TestResultItemChangeReason.NewMessage) { + this.tree.setChildren(itemNode, getTestChildren(result, e.item, index), { diffIdentityProvider }); + } itemNode.changeEmitter.fire(); return; } diff --git a/src/vs/workbench/contrib/testing/common/testResult.ts b/src/vs/workbench/contrib/testing/common/testResult.ts index fad1eefcadb..ffb2ed27172 100644 --- a/src/vs/workbench/contrib/testing/common/testResult.ts +++ b/src/vs/workbench/contrib/testing/common/testResult.ts @@ -215,11 +215,13 @@ const itemToNode = (controllerId: string, item: ITestItem, parent: string | null export const enum TestResultItemChangeReason { ComputedStateChange, OwnStateChange, + NewMessage, } export type TestResultItemChange = { item: TestResultItem; result: ITestResult } & ( | { reason: TestResultItemChangeReason.ComputedStateChange } | { reason: TestResultItemChangeReason.OwnStateChange; previousState: TestResultState; previousOwnDuration: number | undefined } + | { reason: TestResultItemChangeReason.NewMessage } ); /** @@ -319,8 +321,10 @@ export class LiveTestResult implements ITestResult { type: TestMessageType.Output, }; - if (testId) { - this.testById.get(testId)?.tasks[index].messages.push(message); + const test = testId && this.testById.get(testId); + if (test) { + test.tasks[index].messages.push(message); + this.changeEmitter.fire({ item: test, result: this, reason: TestResultItemChangeReason.NewMessage }); } else { task.otherMessages.push(message); } @@ -390,13 +394,7 @@ export class LiveTestResult implements ITestResult { } entry.tasks[this.mustGetTaskIndex(taskId)].messages.push(message); - this.changeEmitter.fire({ - item: entry, - result: this, - reason: TestResultItemChangeReason.OwnStateChange, - previousState: entry.ownComputedState, - previousOwnDuration: entry.ownDuration, - }); + this.changeEmitter.fire({ item: entry, result: this, reason: TestResultItemChangeReason.NewMessage }); } /** From 1104706ceebdb5a6d5e5215c4d150563205ef03f Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 5 Jul 2023 11:23:08 -0700 Subject: [PATCH 180/926] use a promise --- .../contrib/tasks/browser/abstractTaskService.ts | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index 9597fd9689f..aab1904ed20 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -210,6 +210,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer protected _taskSystemInfos: Map; protected _workspaceTasksPromise?: Promise>; + protected _whenTaskSystemReady?: Promise; protected _taskSystem?: ITaskSystem; protected _taskSystemListeners?: IDisposable[] = []; @@ -267,7 +268,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer @IInstantiationService private readonly _instantiationService: IInstantiationService ) { super(); - + this._whenTaskSystemReady = Event.toPromise(this.onDidChangeTaskSystemInfo); this._workspaceTasksPromise = undefined; this._taskSystem = undefined; this._taskSystemListeners = undefined; @@ -378,15 +379,6 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer }); } - private async _waitForTaskSystem(): Promise { - if (this.hasTaskSystemInfo) { - return; - } - // Wait until we have task system info (the extension host and workspace folders are available). - this._logService.trace('RunBuildTask: Awaiting task system info.'); - await Event.toPromise(Event.once(this.onDidChangeTaskSystemInfo)); - } - private async _reconnectTasks(): Promise { const tasks = await this.getSavedTasks('persistent'); if (!tasks.length) { @@ -2208,7 +2200,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return new Map(); } await this._waitForSupportedExecutions; - await this._waitForTaskSystem(); + await this._whenTaskSystemReady; if (this._workspaceTasksPromise) { return this._workspaceTasksPromise; } From f939f628a12872f9cd1b33f84406123a8688d312 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Wed, 5 Jul 2023 11:31:57 -0700 Subject: [PATCH 181/926] Update src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts Co-authored-by: Daniel Imms <2193314+Tyriar@users.noreply.github.com> --- src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index aab1904ed20..3ca656e61ab 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -210,7 +210,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer protected _taskSystemInfos: Map; protected _workspaceTasksPromise?: Promise>; - protected _whenTaskSystemReady?: Promise; + protected readonly _whenTaskSystemReady: Promise; protected _taskSystem?: ITaskSystem; protected _taskSystemListeners?: IDisposable[] = []; From 0bebe30b759edf6889b21a524bbeb65505649f64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Wed, 5 Jul 2023 20:33:05 +0200 Subject: [PATCH 182/926] update inno_updater (#187073) --- build/win32/Cargo.lock | 79 ++++++++++++++++++++++++++++++----- build/win32/Cargo.toml | 28 +++++++++---- build/win32/inno_updater.exe | Bin 443904 -> 459776 bytes 3 files changed, 88 insertions(+), 19 deletions(-) diff --git a/build/win32/Cargo.lock b/build/win32/Cargo.lock index 0601c70fb9e..f83ae22dfc2 100644 --- a/build/win32/Cargo.lock +++ b/build/win32/Cargo.lock @@ -19,12 +19,6 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" -[[package]] -name = "build_const" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ae4235e6dac0694637c763029ecea1a2ec9e4e06ec2729bd21ba4d9c863eb7" - [[package]] name = "byteorder" version = "1.4.3" @@ -39,13 +33,19 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "crc" -version = "1.8.1" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb" +checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" dependencies = [ - "build_const", + "crc-catalog", ] +[[package]] +name = "crc-catalog" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cace84e55f07e7301bae1c519df89cdad8cc3cd868413d3fdbdeca9ff3db484" + [[package]] name = "crossbeam-channel" version = "0.5.5" @@ -109,14 +109,14 @@ dependencies = [ [[package]] name = "inno_updater" -version = "0.9.0" +version = "0.10.0" dependencies = [ "byteorder", "crc", "slog", "slog-async", "slog-term", - "winapi", + "windows-sys", ] [[package]] @@ -329,3 +329,60 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" diff --git a/build/win32/Cargo.toml b/build/win32/Cargo.toml index decae65f9e6..faf7e7fe6d1 100644 --- a/build/win32/Cargo.toml +++ b/build/win32/Cargo.toml @@ -1,18 +1,30 @@ [package] name = "inno_updater" -version = "0.9.0" +version = "0.10.0" authors = ["Microsoft "] build = "build.rs" [dependencies] -byteorder = "1" -crc = "^1.0.0" -slog = "2.1.1" -slog-async = "2.2.0" -slog-term = "2.3.0" +byteorder = "1.4.3" +crc = "3.0.1" +slog = "2.7.0" +slog-async = "2.7.0" +slog-term = "2.9.0" -[target.'cfg(windows)'.dependencies] -winapi = { version = "^0.3.9", features = ["winuser", "libloaderapi", "commctrl", "processthreadsapi", "tlhelp32", "handleapi", "psapi", "errhandlingapi", "winbase", "shellapi"] } +[target.'cfg(windows)'.dependencies.windows-sys] +version = "0.42" +features = [ + "Win32_Foundation", + "Win32_System_Shutdown", + "Win32_UI_WindowsAndMessaging", + "Win32_System_Threading", + "Win32_System_LibraryLoader", + "Win32_System_Diagnostics_Debug", + "Win32_Storage_FileSystem", + "Win32_Security", + "Win32_System_ProcessStatus", + "Win32_System_Diagnostics_ToolHelp" +] [profile.release] lto = true diff --git a/build/win32/inno_updater.exe b/build/win32/inno_updater.exe index fea47a59c9dae3b351f96a05c4d4d1e9040fe9df..941ebfe40814570787700b67b778d399a6b30c92 100644 GIT binary patch literal 459776 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
b;<@%^Zej$lYB^T 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°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%0A9)8TdoI|6;GG3~5i)v%cQ8QBLt|FyQov;x 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 VWWM(w1v!UrE)o16{~!PFe*tpjB0>NF From 6e6084328b84bcca9775d0448e4419135b5db982 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Wed, 5 Jul 2023 11:43:00 -0700 Subject: [PATCH 183/926] testing: fix misalignment of filter icon in input (#187074) Fixes #182648 --- src/vs/workbench/contrib/testing/browser/media/testing.css | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/vs/workbench/contrib/testing/browser/media/testing.css b/src/vs/workbench/contrib/testing/browser/media/testing.css index 4d47d7ac55e..ffe06a9b28e 100644 --- a/src/vs/workbench/contrib/testing/browser/media/testing.css +++ b/src/vs/workbench/contrib/testing/browser/media/testing.css @@ -229,10 +229,6 @@ max-width: none; } -.testing-filter-wrapper { - height: 27px; -} - .testing-filter-action-item .testing-filter-wrapper { flex-grow: 1; } From 8d7c5b99df193f20ba913de5ccbf6ef8217b5bc0 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Wed, 5 Jul 2023 11:49:44 -0700 Subject: [PATCH 184/926] testing: fix debounce terminal resizes (#187071) For #186328 --- .../contrib/testing/browser/media/testing.css | 4 ++++ .../contrib/testing/browser/testingOutputPeek.ts | 14 +++++++++----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/testing/browser/media/testing.css b/src/vs/workbench/contrib/testing/browser/media/testing.css index ffe06a9b28e..06569dd9c99 100644 --- a/src/vs/workbench/contrib/testing/browser/media/testing.css +++ b/src/vs/workbench/contrib/testing/browser/media/testing.css @@ -183,6 +183,10 @@ border-bottom-width: 2px; } +.test-output-peek-message-container { + overflow: hidden; +} + .test-output-peek-message-container, .test-output-peek-tree { height: 100%; diff --git a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts index 993ea6cd0a8..bc522e678f9 100644 --- a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts +++ b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts @@ -15,7 +15,7 @@ import { ICompressedTreeElement, ICompressedTreeNode } from 'vs/base/browser/ui/ 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 { Delayer, RunOnceScheduler } from 'vs/base/common/async'; import { Codicon } from 'vs/base/common/codicons'; import { Color } from 'vs/base/common/color'; import { Emitter, Event } from 'vs/base/common/event'; @@ -1228,6 +1228,7 @@ class PlainTextMessagePeek extends Disposable implements IPeekOutputRenderer { 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()); @@ -1343,6 +1344,7 @@ class TerminalMessagePeek extends Disposable implements IPeekOutputRenderer { private clear() { this.outputDataListener.clear(); + this.xtermLayoutDelayer.cancel(); this.terminal.clear(); } @@ -1359,10 +1361,12 @@ class TerminalMessagePeek extends Disposable implements IPeekOutputRenderer { height = this.dimensions?.height ?? this.container.clientHeight ) { width -= 10 + 20; // scrollbar width + margin - const scaled = getXtermScaledDimensions(xterm.getFont(), width, height); - if (scaled) { - xterm.resize(scaled.cols, scaled.rows); - } + this.xtermLayoutDelayer.trigger(() => { + const scaled = getXtermScaledDimensions(xterm.getFont(), width, height); + if (scaled) { + xterm.resize(scaled.cols, scaled.rows); + } + }); } } From bd915931bd54d77309b17564f2746d989ddb48e9 Mon Sep 17 00:00:00 2001 From: Svante Boberg Date: Wed, 5 Jul 2023 21:08:19 +0200 Subject: [PATCH 185/926] Dispose `TerminalWordLinkDetector` listeners --- .../links/browser/terminal.links.contribution.ts | 2 +- .../terminalContrib/links/browser/terminalLinkManager.ts | 2 +- .../links/browser/terminalWordLinkDetector.ts | 9 ++++++--- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/links/browser/terminal.links.contribution.ts b/src/vs/workbench/contrib/terminalContrib/links/browser/terminal.links.contribution.ts index 3bca06c97e1..dbd35c08605 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/browser/terminal.links.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/browser/terminal.links.contribution.ts @@ -54,7 +54,7 @@ class TerminalLinkContribution extends DisposableStore implements ITerminalContr this._processManager.onProcessReady(() => { linkManager.setWidgetManager(this._widgetManager); }); - this._linkManager = linkManager; + this._linkManager = this.add(linkManager); // Attach the link provider(s) to the instance and listen for changes for (const linkProvider of this._terminalLinkProviderService.linkProviders) { diff --git a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkManager.ts b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkManager.ts index 2016e7ca592..e100b3a68c0 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkManager.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkManager.ts @@ -77,7 +77,7 @@ export class TerminalLinkManager extends DisposableStore { this._setupLinkDetector(TerminalMultiLineLinkDetector.id, this._instantiationService.createInstance(TerminalMultiLineLinkDetector, this._xterm, this._processManager, this._linkResolver)); this._setupLinkDetector(TerminalLocalLinkDetector.id, this._instantiationService.createInstance(TerminalLocalLinkDetector, this._xterm, capabilities, this._processManager, this._linkResolver)); } - this._setupLinkDetector(TerminalWordLinkDetector.id, this._instantiationService.createInstance(TerminalWordLinkDetector, this._xterm)); + this._setupLinkDetector(TerminalWordLinkDetector.id, this.add(this._instantiationService.createInstance(TerminalWordLinkDetector, this._xterm))); // Setup link openers const localFileOpener = this._instantiationService.createInstance(TerminalLocalFileLinkOpener); diff --git a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalWordLinkDetector.ts b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalWordLinkDetector.ts index c79f6389ad0..1991ba84eac 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalWordLinkDetector.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalWordLinkDetector.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { Disposable } from 'vs/base/common/lifecycle'; import { escapeRegExpCharacters } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -27,7 +28,7 @@ interface Word { text: string; } -export class TerminalWordLinkDetector implements ITerminalLinkDetector { +export class TerminalWordLinkDetector extends Disposable implements ITerminalLinkDetector { static id = 'word'; // Word links typically search the workspace so it makes sense that their maximum link length is @@ -41,12 +42,14 @@ export class TerminalWordLinkDetector implements ITerminalLinkDetector { @IConfigurationService private readonly _configurationService: IConfigurationService, @IProductService private readonly _productService: IProductService, ) { + super(); + this._refreshSeparatorCodes(); - this._configurationService.onDidChangeConfiguration(e => { + this._register(this._configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration(TerminalSettingId.WordSeparators)) { this._refreshSeparatorCodes(); } - }); + })); } detect(lines: IBufferLine[], startLine: number, endLine: number): ITerminalSimpleLink[] { From ce3f06b9db174204ab5bd616b77447c32f2d8dc1 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 5 Jul 2023 12:23:30 -0700 Subject: [PATCH 186/926] Bring local terminal reconnect changes to remote Part of #187076 --- .../terminal/browser/remoteTerminalBackend.ts | 36 +++++++++++-------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts b/src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts index fb741eebd25..7c79670a1f1 100644 --- a/src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts +++ b/src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts @@ -6,7 +6,7 @@ import { DeferredPromise } from 'vs/base/common/async'; import { Emitter } from 'vs/base/common/event'; import { revive } from 'vs/base/common/marshalling'; -import { PerformanceMark } from 'vs/base/common/performance'; +import { PerformanceMark, mark } from 'vs/base/common/performance'; import { IProcessEnvironment, OperatingSystem } from 'vs/base/common/platform'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -324,22 +324,28 @@ class RemoteTerminalBackend extends BaseTerminalBackend implements ITerminalBack // Revive processes if needed const serializedState = this._storageService.get(TerminalStorageKeys.TerminalBufferState, StorageScope.WORKSPACE); const parsed = this._deserializeTerminalState(serializedState); - if (parsed) { - try { - // Note that remote terminals do not get their environment re-resolved unlike in local terminals + if (!parsed) { + return undefined; + } - await this._remoteTerminalChannel.reviveTerminalProcesses(parsed, Intl.DateTimeFormat().resolvedOptions().locale); - this._storageService.remove(TerminalStorageKeys.TerminalBufferState, StorageScope.WORKSPACE); - // If reviving processes, send the terminal layout info back to the pty host as it - // will not have been persisted on application exit - const layoutInfo = this._storageService.get(TerminalStorageKeys.TerminalLayoutInfo, StorageScope.WORKSPACE); - if (layoutInfo) { - await this._remoteTerminalChannel.setTerminalLayoutInfo(JSON.parse(layoutInfo)); - this._storageService.remove(TerminalStorageKeys.TerminalLayoutInfo, StorageScope.WORKSPACE); - } - } catch { - // no-op + try { + // Note that remote terminals do not get their environment re-resolved unlike in local terminals + + mark('code/terminal/willReviveTerminalProcessesRemote'); + await this._remoteTerminalChannel.reviveTerminalProcesses(parsed, Intl.DateTimeFormat().resolvedOptions().locale); + mark('code/terminal/didReviveTerminalProcessesRemote'); + this._storageService.remove(TerminalStorageKeys.TerminalBufferState, StorageScope.WORKSPACE); + // If reviving processes, send the terminal layout info back to the pty host as it + // will not have been persisted on application exit + const layoutInfo = this._storageService.get(TerminalStorageKeys.TerminalLayoutInfo, StorageScope.WORKSPACE); + if (layoutInfo) { + mark('code/terminal/willSetTerminalLayoutInfoRemote'); + await this._remoteTerminalChannel.setTerminalLayoutInfo(JSON.parse(layoutInfo)); + mark('code/terminal/didSetTerminalLayoutInfoRemote'); + this._storageService.remove(TerminalStorageKeys.TerminalLayoutInfo, StorageScope.WORKSPACE); } + } catch (e: unknown) { + this._logService.warn('RemoteTerminalBackend#getTerminalLayoutInfo Error', e && typeof e === 'object' && 'message' in e ? e.message : e); } return this._remoteTerminalChannel.getTerminalLayoutInfo(); From 9922b1cb8a63d87ce06729a898d0c1cb78f00e0a Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 5 Jul 2023 12:23:57 -0700 Subject: [PATCH 187/926] Improve logging of pty host startup/reconnection --- src/vs/platform/terminal/node/ptyHostService.ts | 7 ++++++- .../workbench/contrib/terminal/browser/terminalService.ts | 6 +++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/terminal/node/ptyHostService.ts b/src/vs/platform/terminal/node/ptyHostService.ts index f250294c592..ea53c6c81c1 100644 --- a/src/vs/platform/terminal/node/ptyHostService.ts +++ b/src/vs/platform/terminal/node/ptyHostService.ts @@ -8,7 +8,7 @@ import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { IProcessEnvironment, OperatingSystem, isWindows } from 'vs/base/common/platform'; import { ProxyChannel } from 'vs/base/parts/ipc/common/ipc'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ILogService, ILoggerService } from 'vs/platform/log/common/log'; +import { ILogService, ILoggerService, LogLevel } from 'vs/platform/log/common/log'; import { RemoteLoggerChannelClient } from 'vs/platform/log/common/logIpc'; import { getResolvedShellEnv } from 'vs/platform/shell/node/shellEnv'; import { IPtyHostProcessReplayEvent } from 'vs/platform/terminal/common/capabilities/capabilities'; @@ -139,6 +139,11 @@ export class PtyHostService extends Disposable implements IPtyService { const connection = this._ptyHostStarter.start(); const client = connection.client; + // Log a full stack trace which will tell the exact reason the pty host is starting up + if (this._logService.getLevel() === LogLevel.Trace) { + this._logService.trace('PtyHostService#_startPtyHost', new Error().stack?.replace(/^Error/, '')); + } + // Setup heartbeat service and trigger a heartbeat immediately to reset the timeouts const heartbeatService = ProxyChannel.toService(client.getChannel(TerminalIpcChannels.Heartbeat)); heartbeatService.onBeat(() => this._handleHeartbeat()); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index 90152cd6a12..c09c471e5cd 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -422,7 +422,7 @@ export class TerminalService implements ITerminalService { private _setConnected() { this._connectionState = TerminalConnectionState.Connected; this._onDidChangeConnectionState.fire(); - this._logService.trace('Reconnected to terminals'); + this._logService.trace('Pty host ready to use'); } private async _reconnectToRemoteTerminals(): Promise { @@ -444,6 +444,8 @@ export class TerminalService implements ITerminalService { // now that terminals have been restored, // attach listeners to update remote when terminals are changed this._attachProcessLayoutListeners(); + + this._logService.trace('Reconnected to remote terminals'); } private async _reconnectToLocalTerminals(): Promise { @@ -462,6 +464,8 @@ export class TerminalService implements ITerminalService { // now that terminals have been restored, // attach listeners to update local state when terminals are changed this._attachProcessLayoutListeners(); + + this._logService.trace('Reconnected to local terminals'); } private _recreateTerminalGroups(layoutInfo?: ITerminalsLayoutInfo): Promise { From 592302766f50858f0bf4dbfb9ac3b832d1efb60d Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 5 Jul 2023 12:25:05 -0700 Subject: [PATCH 188/926] Don't automatically start up pty host in remote --- src/vs/platform/terminal/node/ptyHostService.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/vs/platform/terminal/node/ptyHostService.ts b/src/vs/platform/terminal/node/ptyHostService.ts index ea53c6c81c1..60b6f1df0a4 100644 --- a/src/vs/platform/terminal/node/ptyHostService.ts +++ b/src/vs/platform/terminal/node/ptyHostService.ts @@ -106,8 +106,6 @@ export class PtyHostService extends Disposable implements IPtyService { // capability if (this._ptyHostStarter.onBeforeWindowConnection) { Event.once(this._ptyHostStarter.onBeforeWindowConnection)(() => this._ensurePtyHost()); - } else { - this._ensurePtyHost(); } this._ptyHostStarter.onWillShutdown?.(() => this._wasQuitRequested = true); From 86a4dad4a6259fae69faaf19d2440695da66bad0 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 5 Jul 2023 12:26:00 -0700 Subject: [PATCH 189/926] Improve name of pty host connection request --- .../terminal/electron-main/electronPtyHostStarter.ts | 6 +++--- src/vs/platform/terminal/node/ptyHost.ts | 2 +- src/vs/platform/terminal/node/ptyHostService.ts | 7 +++---- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/vs/platform/terminal/electron-main/electronPtyHostStarter.ts b/src/vs/platform/terminal/electron-main/electronPtyHostStarter.ts index acd36340ca8..a1599f50737 100644 --- a/src/vs/platform/terminal/electron-main/electronPtyHostStarter.ts +++ b/src/vs/platform/terminal/electron-main/electronPtyHostStarter.ts @@ -23,8 +23,8 @@ export class ElectronPtyHostStarter extends Disposable implements IPtyHostStarte private utilityProcess: UtilityProcess | undefined = undefined; - private readonly _onBeforeWindowConnection = new Emitter(); - readonly onBeforeWindowConnection = this._onBeforeWindowConnection.event; + private readonly _onRequestConnection = new Emitter(); + readonly onRequestConnection = this._onRequestConnection.event; private readonly _onWillShutdown = new Emitter(); readonly onWillShutdown = this._onWillShutdown.event; @@ -104,7 +104,7 @@ export class ElectronPtyHostStarter extends Disposable implements IPtyHostStarte } private _onWindowConnection(e: IpcMainEvent, nonce: string) { - this._onBeforeWindowConnection.fire(); + this._onRequestConnection.fire(); const port = this.utilityProcess!.connect(); diff --git a/src/vs/platform/terminal/node/ptyHost.ts b/src/vs/platform/terminal/node/ptyHost.ts index f9f7761b9e3..7c8b7318a67 100644 --- a/src/vs/platform/terminal/node/ptyHost.ts +++ b/src/vs/platform/terminal/node/ptyHost.ts @@ -14,7 +14,7 @@ export interface IPtyHostConnection { } export interface IPtyHostStarter extends IDisposable { - onBeforeWindowConnection?: Event; + onRequestConnection?: Event; onWillShutdown?: Event; /** diff --git a/src/vs/platform/terminal/node/ptyHostService.ts b/src/vs/platform/terminal/node/ptyHostService.ts index 60b6f1df0a4..78f0467bcb3 100644 --- a/src/vs/platform/terminal/node/ptyHostService.ts +++ b/src/vs/platform/terminal/node/ptyHostService.ts @@ -102,10 +102,9 @@ export class PtyHostService extends Disposable implements IPtyService { this._resolveVariablesRequestStore = this._register(new RequestStore(undefined, this._logService)); this._resolveVariablesRequestStore.onCreateRequest(this._onPtyHostRequestResolveVariables.fire, this._onPtyHostRequestResolveVariables); - // Force the pty host to start as the first window is starting if the starter has that - // capability - if (this._ptyHostStarter.onBeforeWindowConnection) { - Event.once(this._ptyHostStarter.onBeforeWindowConnection)(() => this._ensurePtyHost()); + // Start the pty host when a window requests a connection, if the starter has that capability. + if (this._ptyHostStarter.onRequestConnection) { + Event.once(this._ptyHostStarter.onRequestConnection)(() => this._ensurePtyHost()); } this._ptyHostStarter.onWillShutdown?.(() => this._wasQuitRequested = true); From 221013805985f49950b4fd2451bf22afd73548d2 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 5 Jul 2023 12:45:57 -0700 Subject: [PATCH 190/926] . --- src/vs/platform/terminal/node/ptyHostMain.ts | 1 + src/vs/platform/terminal/node/ptyHostService.ts | 2 ++ src/vs/platform/terminal/node/terminalProcess.ts | 4 +++- .../contrib/terminal/browser/remoteTerminalBackend.ts | 1 + src/vs/workbench/contrib/terminal/browser/terminalService.ts | 2 +- .../contrib/terminal/electron-sandbox/localTerminalBackend.ts | 1 + 6 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/terminal/node/ptyHostMain.ts b/src/vs/platform/terminal/node/ptyHostMain.ts index 46763b6e0f2..cd0faa5ff20 100644 --- a/src/vs/platform/terminal/node/ptyHostMain.ts +++ b/src/vs/platform/terminal/node/ptyHostMain.ts @@ -88,6 +88,7 @@ async function startPtyHost() { // Clean up process.once('exit', () => { + logService.trace('Pty host exiting'); logService.dispose(); heartbeatService.dispose(); ptyService.dispose(); diff --git a/src/vs/platform/terminal/node/ptyHostService.ts b/src/vs/platform/terminal/node/ptyHostService.ts index 78f0467bcb3..1dd232bd80d 100644 --- a/src/vs/platform/terminal/node/ptyHostService.ts +++ b/src/vs/platform/terminal/node/ptyHostService.ts @@ -105,6 +105,8 @@ export class PtyHostService extends Disposable implements IPtyService { // Start the pty host when a window requests a connection, if the starter has that capability. if (this._ptyHostStarter.onRequestConnection) { Event.once(this._ptyHostStarter.onRequestConnection)(() => this._ensurePtyHost()); + } else { + this._ensurePtyHost(); } this._ptyHostStarter.onWillShutdown?.(() => this._wasQuitRequested = true); diff --git a/src/vs/platform/terminal/node/terminalProcess.ts b/src/vs/platform/terminal/node/terminalProcess.ts index 84fbbc9d28e..701426ba768 100644 --- a/src/vs/platform/terminal/node/terminalProcess.ts +++ b/src/vs/platform/terminal/node/terminalProcess.ts @@ -13,7 +13,7 @@ import { IProcessEnvironment, isLinux, isMacintosh, isWindows } from 'vs/base/co import { URI } from 'vs/base/common/uri'; import { Promises } from 'vs/base/node/pfs'; import { localize } from 'vs/nls'; -import { ILogService } from 'vs/platform/log/common/log'; +import { ILogService, LogLevel } from 'vs/platform/log/common/log'; import { IProductService } from 'vs/platform/product/common/productService'; import { FlowControlConstants, IShellLaunchConfig, ITerminalChildProcess, ITerminalLaunchError, IProcessProperty, IProcessPropertyMap as IProcessPropertyMap, ProcessPropertyType, TerminalShellType, IProcessReadyEvent, ITerminalProcessOptions, PosixShellType, IProcessReadyWindowsPty } from 'vs/platform/terminal/common/terminal'; import { ChildProcessMonitor } from 'vs/platform/terminal/node/childProcessMonitor'; @@ -353,6 +353,7 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess // Allow any trailing data events to be sent before the exit event is sent. // See https://github.com/Tyriar/node-pty/issues/72 private _queueProcessExit() { + this._logService.trace('TerminalProcess#_queueProcessExit', new Error().stack?.replace(/^Error/, '')); if (this._closeTimeout) { clearTimeout(this._closeTimeout); } @@ -417,6 +418,7 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess } shutdown(immediate: boolean): void { + this._logService.trace('TerminalProcess#shutdown', new Error().stack?.replace(/^Error/, '')); // don't force immediate disposal of the terminal processes on Windows as an additional // mitigation for https://github.com/microsoft/vscode/issues/71966 which causes the pty host // to become unresponsive, disconnecting all terminals across all windows. diff --git a/src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts b/src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts index 7c79670a1f1..5c5c940be45 100644 --- a/src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts +++ b/src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts @@ -165,6 +165,7 @@ class RemoteTerminalBackend extends BaseTerminalBackend implements ITerminalBack } const ids = Array.from(this._ptys.keys()); const serialized = await this._remoteTerminalChannel.serializeTerminalState(ids); + this._logService.info('remote persistTerminalState serialied', serialized); this._storageService.store(TerminalStorageKeys.TerminalBufferState, serialized, StorageScope.WORKSPACE, StorageTarget.MACHINE); } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index c09c471e5cd..b0e8a2c08cd 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -422,7 +422,7 @@ export class TerminalService implements ITerminalService { private _setConnected() { this._connectionState = TerminalConnectionState.Connected; this._onDidChangeConnectionState.fire(); - this._logService.trace('Pty host ready to use'); + this._logService.trace('Pty host ready'); } private async _reconnectToRemoteTerminals(): Promise { diff --git a/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts b/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts index ac4c6650e8b..23f93fe7ac6 100644 --- a/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts +++ b/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts @@ -183,6 +183,7 @@ class LocalTerminalBackend extends BaseTerminalBackend implements ITerminalBacke async persistTerminalState(): Promise { const ids = Array.from(this._ptys.keys()); const serialized = await this._proxy.serializeTerminalState(ids); + this._logService.info('local persistTerminalState serialied', serialized); this._storageService.store(TerminalStorageKeys.TerminalBufferState, serialized, StorageScope.WORKSPACE, StorageTarget.MACHINE); } From d58f304c060ea0f2a15959da72630cd2a71e7371 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Wed, 5 Jul 2023 13:25:29 -0700 Subject: [PATCH 191/926] fix: gutter testing spinner icon broken due to line height (#187081) Getting the "wobble" entirely out was annoying. I initially assumed the countainer had to be either an odd or dimension for flexbox to center the icon, but this seemed to not be sufficient. For example, a line height of 36 and 40 were wobble-free, but 38 was not. So instead take a sledgehammer at spinning icons and center them with a transform position. Fixes #183269 --- .../browser/viewParts/glyphMargin/glyphMargin.css | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.css b/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.css index af5c8a71f23..7d1a4475960 100644 --- a/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.css +++ b/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.css @@ -16,4 +16,17 @@ position: absolute; display: flex; align-items: center; + justify-content: center; +} + +/* + Ensure spinning icons are pixel-perfectly centered and avoid wobble. + This is only applied to icons that spin to avoid unnecessary + GPU layers and blurry subpixel AA. +*/ +.monaco-editor .glyph-margin-widgets .cgmr.codicon-modifier-spin::before { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); } From 1157e96e241152529a1351fd93b33994bfb5e72d Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 5 Jul 2023 22:29:34 +0200 Subject: [PATCH 192/926] adopt atomic write for userdata (#187038) * #180695 - adopt atomic write for userdata * check if fsp has atomic write * fix tests --- src/vs/code/electron-main/main.ts | 5 +++++ .../code/node/sharedProcess/sharedProcessMain.ts | 4 ++-- .../environment/common/environmentService.ts | 6 +++--- .../common/extensionsProfileScannerService.ts | 5 +++-- .../node/sharedProcessEnvironmentService.ts | 16 ---------------- .../userData/common/fileUserDataProvider.ts | 6 +++++- src/vs/server/node/serverEnvironmentService.ts | 4 ++++ .../workbench/electron-sandbox/desktop.main.ts | 3 ++- .../electron-sandbox/environmentService.ts | 3 --- 9 files changed, 24 insertions(+), 28 deletions(-) delete mode 100644 src/vs/platform/sharedProcess/node/sharedProcessEnvironmentService.ts diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index 6e960fc47c7..0174a24c3c8 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -70,6 +70,7 @@ import { ILoggerMainService, LoggerMainService } from 'vs/platform/log/electron- import { LogService } from 'vs/platform/log/common/logService'; import { massageMessageBoxOptions } from 'vs/platform/dialogs/common/dialogs'; import { SaveStrategy, StateService } from 'vs/platform/state/node/stateService'; +import { FileUserDataProvider } from 'vs/platform/userData/common/fileUserDataProvider'; /** * The main VS Code entry point. @@ -178,6 +179,10 @@ class CodeMain { const diskFileSystemProvider = new DiskFileSystemProvider(logService); fileService.registerProvider(Schemas.file, diskFileSystemProvider); + // Use FileUserDataProvider for user data to + // enable atomic read / write operations. + fileService.registerProvider(Schemas.vscodeUserData, new FileUserDataProvider(Schemas.file, diskFileSystemProvider, Schemas.vscodeUserData, logService)); + // URI Identity const uriIdentityService = new UriIdentityService(fileService); services.set(IUriIdentityService, uriIdentityService); diff --git a/src/vs/code/node/sharedProcess/sharedProcessMain.ts b/src/vs/code/node/sharedProcess/sharedProcessMain.ts index 28053c1cdd6..d8b6d791e74 100644 --- a/src/vs/code/node/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/node/sharedProcess/sharedProcessMain.ts @@ -26,7 +26,6 @@ import { DiagnosticsService } from 'vs/platform/diagnostics/node/diagnosticsServ import { IDownloadService } from 'vs/platform/download/common/download'; import { DownloadService } from 'vs/platform/download/common/downloadService'; import { INativeEnvironmentService } from 'vs/platform/environment/common/environment'; -import { SharedProcessEnvironmentService } from 'vs/platform/sharedProcess/node/sharedProcessEnvironmentService'; import { GlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionEnablementService'; import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService'; import { IExtensionGalleryService, IExtensionManagementService, IExtensionTipsService, IGlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement'; @@ -112,6 +111,7 @@ import { RemoteStorageService } from 'vs/platform/storage/common/storageService' import { IRemoteSocketFactoryService, RemoteSocketFactoryService } from 'vs/platform/remote/common/remoteSocketFactoryService'; import { RemoteConnectionType } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { nodeSocketFactory } from 'vs/platform/remote/node/nodeSocketFactory'; +import { NativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; class SharedProcessMain extends Disposable { @@ -191,7 +191,7 @@ class SharedProcessMain extends Disposable { services.set(IPolicyService, policyService); // Environment - const environmentService = new SharedProcessEnvironmentService(this.configuration.args, productService); + const environmentService = new NativeEnvironmentService(this.configuration.args, productService); services.set(INativeEnvironmentService, environmentService); // Logger diff --git a/src/vs/platform/environment/common/environmentService.ts b/src/vs/platform/environment/common/environmentService.ts index 7f2e8e93b3e..78eccd30b05 100644 --- a/src/vs/platform/environment/common/environmentService.ts +++ b/src/vs/platform/environment/common/environmentService.ts @@ -5,7 +5,7 @@ import { toLocalISOString } from 'vs/base/common/date'; import { memoize } from 'vs/base/common/decorators'; -import { FileAccess } from 'vs/base/common/network'; +import { FileAccess, Schemas } from 'vs/base/common/network'; import { dirname, join, normalize, resolve } from 'vs/base/common/path'; import { env } from 'vs/base/common/process'; import { joinPath } from 'vs/base/common/resources'; @@ -65,10 +65,10 @@ export abstract class AbstractNativeEnvironmentService implements INativeEnviron get stateResource(): URI { return joinPath(this.appSettingsHome, 'globalStorage', 'storage.json'); } @memoize - get userRoamingDataHome(): URI { return this.appSettingsHome; } + get userRoamingDataHome(): URI { return this.appSettingsHome.with({ scheme: Schemas.vscodeUserData }); } @memoize - get userDataSyncHome(): URI { return joinPath(this.userRoamingDataHome, 'sync'); } + get userDataSyncHome(): URI { return joinPath(this.appSettingsHome, 'sync'); } get logsHome(): URI { if (!this.args.logsPath) { diff --git a/src/vs/platform/extensionManagement/common/extensionsProfileScannerService.ts b/src/vs/platform/extensionManagement/common/extensionsProfileScannerService.ts index 30e749ac312..925b9dd905c 100644 --- a/src/vs/platform/extensionManagement/common/extensionsProfileScannerService.ts +++ b/src/vs/platform/extensionManagement/common/extensionsProfileScannerService.ts @@ -12,7 +12,7 @@ import { URI, UriComponents } from 'vs/base/common/uri'; import { Metadata, isIExtensionIdentifier } from 'vs/platform/extensionManagement/common/extensionManagement'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IExtension, IExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; -import { FileOperationResult, IFileService, toFileOperationResult } from 'vs/platform/files/common/files'; +import { FileOperationResult, IFileService, hasFileAtomicWriteCapability, toFileOperationResult } from 'vs/platform/files/common/files'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; @@ -290,7 +290,8 @@ export abstract class AbstractExtensionsProfileScannerService extends Disposable relativeLocation: this.toRelativePath(e.location), metadata: e.metadata })); - await this.fileService.writeFile(file, VSBuffer.fromString(JSON.stringify(storedProfileExtensions))); + const fsp = this.fileService.getProvider(file.scheme); + await this.fileService.writeFile(file, VSBuffer.fromString(JSON.stringify(storedProfileExtensions)), fsp && hasFileAtomicWriteCapability(fsp) ? { atomic: { postfix: '.vsctmp' } } : undefined); } return extensions; diff --git a/src/vs/platform/sharedProcess/node/sharedProcessEnvironmentService.ts b/src/vs/platform/sharedProcess/node/sharedProcessEnvironmentService.ts deleted file mode 100644 index 7cb9f0d402f..00000000000 --- a/src/vs/platform/sharedProcess/node/sharedProcessEnvironmentService.ts +++ /dev/null @@ -1,16 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { memoize } from 'vs/base/common/decorators'; -import { Schemas } from 'vs/base/common/network'; -import { URI } from 'vs/base/common/uri'; -import { NativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; - -export class SharedProcessEnvironmentService extends NativeEnvironmentService { - - @memoize - override get userRoamingDataHome(): URI { return this.appSettingsHome.with({ scheme: Schemas.vscodeUserData }); } - -} diff --git a/src/vs/platform/userData/common/fileUserDataProvider.ts b/src/vs/platform/userData/common/fileUserDataProvider.ts index 539c8011f3e..9382a676acb 100644 --- a/src/vs/platform/userData/common/fileUserDataProvider.ts +++ b/src/vs/platform/userData/common/fileUserDataProvider.ts @@ -4,13 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import { Event, Emitter } from 'vs/base/common/event'; import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { IFileSystemProviderWithFileReadWriteCapability, IFileChange, IWatchOptions, IStat, IFileOverwriteOptions, FileType, IFileWriteOptions, IFileDeleteOptions, FileSystemProviderCapabilities, IFileSystemProviderWithFileReadStreamCapability, IFileReadStreamOptions, IFileSystemProviderWithFileAtomicReadCapability, IFileSystemProviderWithFileFolderCopyCapability, hasFileFolderCopyCapability } from 'vs/platform/files/common/files'; +import { IFileSystemProviderWithFileReadWriteCapability, IFileChange, IWatchOptions, IStat, IFileOverwriteOptions, FileType, IFileWriteOptions, IFileDeleteOptions, FileSystemProviderCapabilities, IFileSystemProviderWithFileReadStreamCapability, IFileReadStreamOptions, IFileSystemProviderWithFileAtomicReadCapability, IFileSystemProviderWithFileFolderCopyCapability, hasFileFolderCopyCapability, hasFileAtomicWriteCapability } from 'vs/platform/files/common/files'; import { URI } from 'vs/base/common/uri'; import { CancellationToken } from 'vs/base/common/cancellation'; import { newWriteableStream, ReadableStreamEvents } from 'vs/base/common/stream'; import { ILogService } from 'vs/platform/log/common/log'; import { TernarySearchTree } from 'vs/base/common/ternarySearchTree'; import { VSBuffer } from 'vs/base/common/buffer'; +import { isObject } from 'vs/base/common/types'; /** * This is a wrapper on top of the local filesystem provider which will @@ -85,6 +86,9 @@ export class FileUserDataProvider extends Disposable implements } writeFile(resource: URI, content: Uint8Array, opts: IFileWriteOptions): Promise { + if (!isObject(opts.atomic) && hasFileAtomicWriteCapability(this.fileSystemProvider)) { + opts = { ...opts, atomic: { postfix: '.vsctmp' } }; + } return this.fileSystemProvider.writeFile(this.toFileSystemResource(resource), content, opts); } diff --git a/src/vs/server/node/serverEnvironmentService.ts b/src/vs/server/node/serverEnvironmentService.ts index ea6cb346184..e3742efaa51 100644 --- a/src/vs/server/node/serverEnvironmentService.ts +++ b/src/vs/server/node/serverEnvironmentService.ts @@ -9,6 +9,8 @@ import { NativeEnvironmentService } from 'vs/platform/environment/node/environme import { OPTIONS, OptionDescriptions } from 'vs/platform/environment/node/argv'; import { refineServiceDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IEnvironmentService, INativeEnvironmentService } from 'vs/platform/environment/common/environment'; +import { memoize } from 'vs/base/common/decorators'; +import { URI } from 'vs/base/common/uri'; export const serverOptions: OptionDescriptions> = { @@ -211,5 +213,7 @@ export interface IServerEnvironmentService extends INativeEnvironmentService { } export class ServerEnvironmentService extends NativeEnvironmentService implements IServerEnvironmentService { + @memoize + override get userRoamingDataHome(): URI { return this.appSettingsHome; } override get args(): ServerParsedArgs { return super.args as ServerParsedArgs; } } diff --git a/src/vs/workbench/electron-sandbox/desktop.main.ts b/src/vs/workbench/electron-sandbox/desktop.main.ts index 87db0ff9fc1..9609e5f96bb 100644 --- a/src/vs/workbench/electron-sandbox/desktop.main.ts +++ b/src/vs/workbench/electron-sandbox/desktop.main.ts @@ -224,7 +224,8 @@ export class DesktopMain extends Disposable { const diskFileSystemProvider = this._register(new DiskFileSystemProvider(mainProcessService, utilityProcessWorkerWorkbenchService, logService)); fileService.registerProvider(Schemas.file, diskFileSystemProvider); - // User Data Provider + // Use FileUserDataProvider for user data to + // enable atomic read / write operations. fileService.registerProvider(Schemas.vscodeUserData, this._register(new FileUserDataProvider(Schemas.file, diskFileSystemProvider, Schemas.vscodeUserData, logService))); // URI Identity diff --git a/src/vs/workbench/services/environment/electron-sandbox/environmentService.ts b/src/vs/workbench/services/environment/electron-sandbox/environmentService.ts index 810a38b3866..26fedfa0219 100644 --- a/src/vs/workbench/services/environment/electron-sandbox/environmentService.ts +++ b/src/vs/workbench/services/environment/electron-sandbox/environmentService.ts @@ -84,9 +84,6 @@ export class NativeWorkbenchEnvironmentService extends AbstractNativeEnvironment }; } - @memoize - override get userRoamingDataHome(): URI { return this.appSettingsHome.with({ scheme: Schemas.vscodeUserData }); } - @memoize get windowLogsPath(): URI { return joinPath(this.logsHome, `window${this.configuration.windowId}`); } From 075cf4ac2b2b56511af2e4eefe8d946e144245b3 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 5 Jul 2023 13:38:53 -0700 Subject: [PATCH 193/926] getWorkspaceTasks --- .../contrib/tasks/browser/abstractTaskService.ts | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index 3ca656e61ab..979b207b9e5 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -2201,9 +2201,6 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } await this._waitForSupportedExecutions; await this._whenTaskSystemReady; - if (this._workspaceTasksPromise) { - return this._workspaceTasksPromise; - } return this._updateWorkspaceTasks(runSource); } @@ -2748,16 +2745,8 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return true; } - private async _ensureWorkspaceTasks(): Promise { - if (!this._workspaceTasksPromise) { - await this.getWorkspaceTasks(); - } else { - await this._workspaceTasksPromise; - } - } - private async _runTaskCommand(filter?: string | ITaskIdentifier): Promise { - await this._ensureWorkspaceTasks(); + await this.getWorkspaceTasks(); if (!filter) { return this._doRunTaskCommand(); } @@ -2915,7 +2904,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer title: strings.fetching }; const promise = (async () => { - await this._ensureWorkspaceTasks(); + await this.getWorkspaceTasks(); let taskGroupTasks: (Task | ConfiguringTask)[] = []; async function runSingleTask(task: Task | undefined, problemMatcherOptions: IProblemMatcherRunOptions | undefined, that: AbstractTaskService) { From 0c3fb42a1e31f63ec7f17906a19bbcebdbe8bb3e Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 5 Jul 2023 13:46:59 -0700 Subject: [PATCH 194/926] Disable seamless relaunch on Windows/remote/ConPTY The fast process spawn, kill, spawn was causing problems on remotes Fixes #187084 --- .../platform/terminal/node/terminalProcess.ts | 2 +- .../terminal/browser/terminalInstance.ts | 21 +++++++++++++------ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/vs/platform/terminal/node/terminalProcess.ts b/src/vs/platform/terminal/node/terminalProcess.ts index 701426ba768..c3bd57c1562 100644 --- a/src/vs/platform/terminal/node/terminalProcess.ts +++ b/src/vs/platform/terminal/node/terminalProcess.ts @@ -13,7 +13,7 @@ import { IProcessEnvironment, isLinux, isMacintosh, isWindows } from 'vs/base/co import { URI } from 'vs/base/common/uri'; import { Promises } from 'vs/base/node/pfs'; import { localize } from 'vs/nls'; -import { ILogService, LogLevel } from 'vs/platform/log/common/log'; +import { ILogService } from 'vs/platform/log/common/log'; import { IProductService } from 'vs/platform/product/common/productService'; import { FlowControlConstants, IShellLaunchConfig, ITerminalChildProcess, ITerminalLaunchError, IProcessProperty, IProcessPropertyMap as IProcessPropertyMap, ProcessPropertyType, TerminalShellType, IProcessReadyEvent, ITerminalProcessOptions, PosixShellType, IProcessReadyWindowsPty } from 'vs/platform/terminal/common/terminal'; import { ChildProcessMonitor } from 'vs/platform/terminal/node/childProcessMonitor'; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 4f2ca596f77..9ecf7d39c00 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -2080,7 +2080,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { this._refreshEnvironmentVariableInfoWidgetState(info); } - private _refreshEnvironmentVariableInfoWidgetState(info?: IEnvironmentVariableInfo): void { + private async _refreshEnvironmentVariableInfoWidgetState(info?: IEnvironmentVariableInfo): Promise { // Check if the status should exist if (!info) { this.statusList.remove(TerminalStatus.RelaunchNeeded); @@ -2088,16 +2088,25 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { return; } - // Recreate the process if the terminal has not yet been interacted with and it's not a - // special terminal (eg. extension terminal) + // Recreate the process seamlessly without informing the use if the following conditions are + // met. if ( + // The change requires a relaunch info.requiresAction && + // The feature is enabled this._configHelper.config.environmentChangesRelaunch && + // Has not been interacted with !this._processManager.hasWrittenData && + // Not a feature terminal or is a reconnecting task terminal (TODO: Need to explain the latter case) (!this._shellLaunchConfig.isFeatureTerminal || (this.reconnectionProperties && this._configurationService.getValue(TaskSettingId.Reconnection) === true)) && - !this._shellLaunchConfig.customPtyImplementation - && !this._shellLaunchConfig.isExtensionOwnedTerminal && - !this._shellLaunchConfig.attachPersistentProcess + // Not a custom pty + !this._shellLaunchConfig.customPtyImplementation && + // Not an extension owned terminal + !this._shellLaunchConfig.isExtensionOwnedTerminal && + // Not a reconnected or revived terminal + !this._shellLaunchConfig.attachPersistentProcess && + // Not a Windows remote using ConPTY (#187084) + !(this._processManager.remoteAuthority && this._configHelper.config.windowsEnableConpty && (await this._processManager.getBackendOS()) === OperatingSystem.Windows) ) { this.relaunch(); return; From 55d8d97b45cb0aac78cd5d0ad9e7a9838a4af45d Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Wed, 5 Jul 2023 14:06:38 -0700 Subject: [PATCH 195/926] testing: fix default size of inline peek diff (#187072) * testing: fix default size of inline peek diff Fixes #185331 * rm unused import --- .../testing/browser/testingOutputPeek.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts index bc522e678f9..761d9c4319c 100644 --- a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts +++ b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts @@ -26,7 +26,6 @@ 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, toDisposable } from 'vs/base/common/lifecycle'; -import { clamp } from 'vs/base/common/numbers'; import { count } from 'vs/base/common/strings'; import { ThemeIcon } from 'vs/base/common/themables'; import { URI } from 'vs/base/common/uri'; @@ -520,8 +519,8 @@ export class TestingOutputPeekController extends Disposable implements IEditorCo * Shows a peek for the message in the editor. */ public async show(uri: URI) { - const subjecet = this.retrieveTest(uri); - if (!subjecet) { + const subject = this.retrieveTest(uri); + if (!subject) { return; } @@ -537,12 +536,12 @@ export class TestingOutputPeekController extends Disposable implements IEditorCo this.peek.value!.create(); } - if (subjecet instanceof MessageSubject) { - const message = subjecet.messages[subjecet.messageIndex]; + if (subject instanceof MessageSubject) { + const message = subject.messages[subject.messageIndex]; alert(renderStringAsPlaintext(message.message)); } - this.peek.value.setModel(subjecet); + this.peek.value.setModel(subject); this.currentPeekUri = uri; } @@ -1022,7 +1021,7 @@ const diffEditorOptions: IDiffEditorConstructionOptions = { diffAlgorithm: 'advanced', }; -const isDiffable = (message: ITestMessage): message is ITestErrorMessage & { actualOutput: string; expectedOutput: string } => +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 { @@ -1381,8 +1380,9 @@ const firstLine = (str: string) => { }; const isMultiline = (str: string | undefined) => !!str && str.includes('\n'); -const hintPeekStrHeight = (str: string | undefined) => - clamp(str ? Math.max(count(str, '\n'), Math.ceil(str.length / 80)) + 3 : 0, 14, 24); + +// add 5ish lines for the size of the title and decorations in the peek. +const hintPeekStrHeight = (str: string) => Math.min(count(str, '\n') + 5, 24); class SimpleDiffEditorModel extends EditorModel { public readonly original = this._original.object.textEditorModel; From 406d3d5cf5488bacec8c7f97fd91b385e1d4b42f Mon Sep 17 00:00:00 2001 From: Michael Lively Date: Wed, 5 Jul 2023 14:08:37 -0700 Subject: [PATCH 196/926] fix #186651 -- notebook action tab border only on icon, not label (#187085) fix #186651 --- .../notebook/browser/media/notebookToolbar.css | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/vs/workbench/contrib/notebook/browser/media/notebookToolbar.css b/src/vs/workbench/contrib/notebook/browser/media/notebookToolbar.css index 53505527259..28ea1557a52 100644 --- a/src/vs/workbench/contrib/notebook/browser/media/notebookToolbar.css +++ b/src/vs/workbench/contrib/notebook/browser/media/notebookToolbar.css @@ -40,6 +40,18 @@ height: 22px; } +.monaco-workbench .notebookOverlay .notebook-toolbar-container .notebook-toolbar-left .monaco-action-bar li a[tabindex="0"]:focus { + outline: none !important; +} + +.monaco-workbench .notebookOverlay .notebook-toolbar-container .notebook-toolbar-left .monaco-action-bar li:has(a:focus) { + outline-width: 1px; + outline-style: solid; + outline-offset: -1px; + outline-color: var(--vscode-focusBorder); + opacity: 1; +} + .monaco-workbench .notebookOverlay .notebook-toolbar-container .notebook-toolbar-left .monaco-action-bar .action-item .action-label.separator { margin: 5px 0px !important; padding: 0px !important; From 72fb36496cde044d0334b3f2015565417efb0b88 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 5 Jul 2023 14:10:18 -0700 Subject: [PATCH 197/926] Fix pty host lazy spawning Fixes #187076 --- .../platform/terminal/node/ptyHostService.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/vs/platform/terminal/node/ptyHostService.ts b/src/vs/platform/terminal/node/ptyHostService.ts index 1dd232bd80d..ad7dd4be94d 100644 --- a/src/vs/platform/terminal/node/ptyHostService.ts +++ b/src/vs/platform/terminal/node/ptyHostService.ts @@ -43,6 +43,13 @@ export class PtyHostService extends Disposable implements IPtyService { this._ensurePtyHost(); return this.__proxy!; } + /** + * Get the proxy if it exists, otherwise undefined. This is used when calls are not needed to be + * passed through to the pty host if it has not yet been spawned. + */ + private get _optionalProxy(): IPtyService | undefined { + return this.__proxy; + } private _ensurePtyHost() { if (!this.__connection) { @@ -105,8 +112,6 @@ export class PtyHostService extends Disposable implements IPtyService { // Start the pty host when a window requests a connection, if the starter has that capability. if (this._ptyHostStarter.onRequestConnection) { Event.once(this._ptyHostStarter.onRequestConnection)(() => this._ensurePtyHost()); - } else { - this._ensurePtyHost(); } this._ptyHostStarter.onWillShutdown?.(() => this._wasQuitRequested = true); @@ -228,13 +233,10 @@ export class PtyHostService extends Disposable implements IPtyService { return this._proxy.listProcesses(); } async getPerformanceMarks(): Promise { - if (!this.__proxy) { - return []; - } - return this._proxy.getPerformanceMarks(); + return this._optionalProxy?.getPerformanceMarks() ?? []; } - reduceConnectionGraceTime(): Promise { - return this._proxy.reduceConnectionGraceTime(); + async reduceConnectionGraceTime(): Promise { + return this._optionalProxy?.reduceConnectionGraceTime(); } start(id: number): Promise { return this._proxy.start(id); From b540ba7b277b27f2481ba2a9dbf380019d4cffdd Mon Sep 17 00:00:00 2001 From: Andrea Mah <31675041+andreamah@users.noreply.github.com> Date: Wed, 5 Jul 2023 14:11:27 -0700 Subject: [PATCH 198/926] Unexpected link highlighting in search editor (#187091) Fixes #180813 --- extensions/search-result/src/extension.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/search-result/src/extension.ts b/extensions/search-result/src/extension.ts index 90443f16771..815bd8c1391 100644 --- a/extensions/search-result/src/extension.ts +++ b/extensions/search-result/src/extension.ts @@ -78,7 +78,7 @@ export function activate(context: vscode.ExtensionContext) { const lineResult = parseSearchResults(document, token)[position.line]; if (!lineResult) { return []; } if (lineResult.type === 'file') { - return lineResult.allLocations; + return lineResult.allLocations.map(l => ({ ...l, originSelectionRange: lineResult.location.originSelectionRange })); } const location = lineResult.locations.find(l => l.originSelectionRange.contains(position)); From f409c06e471383b382ac7501c895adb7752d4ab5 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 5 Jul 2023 14:12:48 -0700 Subject: [PATCH 199/926] Clean up trace logs --- src/vs/platform/terminal/node/terminalProcess.ts | 10 +++++++--- .../contrib/terminal/browser/remoteTerminalBackend.ts | 1 - .../terminal/electron-sandbox/localTerminalBackend.ts | 1 - 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/vs/platform/terminal/node/terminalProcess.ts b/src/vs/platform/terminal/node/terminalProcess.ts index c3bd57c1562..467007e8728 100644 --- a/src/vs/platform/terminal/node/terminalProcess.ts +++ b/src/vs/platform/terminal/node/terminalProcess.ts @@ -13,7 +13,7 @@ import { IProcessEnvironment, isLinux, isMacintosh, isWindows } from 'vs/base/co import { URI } from 'vs/base/common/uri'; import { Promises } from 'vs/base/node/pfs'; import { localize } from 'vs/nls'; -import { ILogService } from 'vs/platform/log/common/log'; +import { ILogService, LogLevel } from 'vs/platform/log/common/log'; import { IProductService } from 'vs/platform/product/common/productService'; import { FlowControlConstants, IShellLaunchConfig, ITerminalChildProcess, ITerminalLaunchError, IProcessProperty, IProcessPropertyMap as IProcessPropertyMap, ProcessPropertyType, TerminalShellType, IProcessReadyEvent, ITerminalProcessOptions, PosixShellType, IProcessReadyWindowsPty } from 'vs/platform/terminal/common/terminal'; import { ChildProcessMonitor } from 'vs/platform/terminal/node/childProcessMonitor'; @@ -353,7 +353,9 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess // Allow any trailing data events to be sent before the exit event is sent. // See https://github.com/Tyriar/node-pty/issues/72 private _queueProcessExit() { - this._logService.trace('TerminalProcess#_queueProcessExit', new Error().stack?.replace(/^Error/, '')); + if (this._logService.getLevel() === LogLevel.Trace) { + this._logService.trace('TerminalProcess#_queueProcessExit', new Error().stack?.replace(/^Error/, '')); + } if (this._closeTimeout) { clearTimeout(this._closeTimeout); } @@ -418,7 +420,9 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess } shutdown(immediate: boolean): void { - this._logService.trace('TerminalProcess#shutdown', new Error().stack?.replace(/^Error/, '')); + if (this._logService.getLevel() === LogLevel.Trace) { + this._logService.trace('TerminalProcess#shutdown', new Error().stack?.replace(/^Error/, '')); + } // don't force immediate disposal of the terminal processes on Windows as an additional // mitigation for https://github.com/microsoft/vscode/issues/71966 which causes the pty host // to become unresponsive, disconnecting all terminals across all windows. diff --git a/src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts b/src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts index 5c5c940be45..7c79670a1f1 100644 --- a/src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts +++ b/src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts @@ -165,7 +165,6 @@ class RemoteTerminalBackend extends BaseTerminalBackend implements ITerminalBack } const ids = Array.from(this._ptys.keys()); const serialized = await this._remoteTerminalChannel.serializeTerminalState(ids); - this._logService.info('remote persistTerminalState serialied', serialized); this._storageService.store(TerminalStorageKeys.TerminalBufferState, serialized, StorageScope.WORKSPACE, StorageTarget.MACHINE); } diff --git a/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts b/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts index 23f93fe7ac6..ac4c6650e8b 100644 --- a/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts +++ b/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts @@ -183,7 +183,6 @@ class LocalTerminalBackend extends BaseTerminalBackend implements ITerminalBacke async persistTerminalState(): Promise { const ids = Array.from(this._ptys.keys()); const serialized = await this._proxy.serializeTerminalState(ids); - this._logService.info('local persistTerminalState serialied', serialized); this._storageService.store(TerminalStorageKeys.TerminalBufferState, serialized, StorageScope.WORKSPACE, StorageTarget.MACHINE); } From 3d6d83950e6acabe71ca8a561ca5dc7e04efa7ec Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 5 Jul 2023 14:15:59 -0700 Subject: [PATCH 200/926] Don't spawn pty host on getDefaultSystemShell or refreshIgnoreProcessNames --- src/vs/platform/terminal/node/ptyHostService.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/vs/platform/terminal/node/ptyHostService.ts b/src/vs/platform/terminal/node/ptyHostService.ts index ad7dd4be94d..ecd550e3826 100644 --- a/src/vs/platform/terminal/node/ptyHostService.ts +++ b/src/vs/platform/terminal/node/ptyHostService.ts @@ -5,7 +5,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; -import { IProcessEnvironment, OperatingSystem, isWindows } from 'vs/base/common/platform'; +import { IProcessEnvironment, OS, OperatingSystem, isWindows } from 'vs/base/common/platform'; import { ProxyChannel } from 'vs/base/parts/ipc/common/ipc'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ILogService, ILoggerService, LogLevel } from 'vs/platform/log/common/log'; @@ -19,6 +19,7 @@ import { IGetTerminalLayoutInfoArgs, IProcessDetails, ISetTerminalLayoutInfoArgs import { IPtyHostConnection, IPtyHostStarter } from 'vs/platform/terminal/node/ptyHost'; import { detectAvailableProfiles } from 'vs/platform/terminal/node/terminalProfiles'; import * as performance from 'vs/base/common/performance'; +import { getSystemShell } from 'vs/base/node/shell'; enum Constants { MaxRestarts = 5 @@ -122,7 +123,7 @@ export class PtyHostService extends Disposable implements IPtyService { } private async _refreshIgnoreProcessNames(): Promise { - return this._proxy.refreshIgnoreProcessNames?.(this._ignoreProcessNames); + return this._optionalProxy?.refreshIgnoreProcessNames?.(this._ignoreProcessNames); } private async _resolveShellEnv(): Promise { @@ -286,7 +287,7 @@ export class PtyHostService extends Disposable implements IPtyService { } getDefaultSystemShell(osOverride?: OperatingSystem): Promise { - return this._proxy.getDefaultSystemShell(osOverride); + return this._optionalProxy?.getDefaultSystemShell(osOverride) ?? getSystemShell(osOverride ?? OS, process.env); } async getProfiles(workspaceId: string, profiles: unknown, defaultProfile: unknown, includeDetectedProfiles: boolean = false): Promise { const shellEnv = await this._resolveShellEnv(); From 26a45ba4d3ba4f89f61ef94747857bc5cc1e6967 Mon Sep 17 00:00:00 2001 From: Raymond Zhao <7199958+rzhao271@users.noreply.github.com> Date: Wed, 5 Jul 2023 14:31:37 -0700 Subject: [PATCH 201/926] fix: adjust widget overflow styling (#186951) Fixes microsoft/vscode-pull-request-github#4973 --- .../contrib/preferences/browser/media/settingsWidgets.css | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/preferences/browser/media/settingsWidgets.css b/src/vs/workbench/contrib/preferences/browser/media/settingsWidgets.css index 31d9112df2f..a77fedea1d9 100644 --- a/src/vs/workbench/contrib/preferences/browser/media/settingsWidgets.css +++ b/src/vs/workbench/contrib/preferences/browser/media/settingsWidgets.css @@ -18,9 +18,8 @@ .settings-editor > .settings-body .settings-tree-container .setting-item.setting-item-list .setting-list-sibling, .settings-editor > .settings-body .settings-tree-container .setting-item.setting-item-list .setting-list-object-widget .setting-list-object-key, .settings-editor > .settings-body .settings-tree-container .setting-item.setting-item-list .setting-list-object-widget .setting-list-object-value { - white-space: pre; - overflow: hidden; - text-overflow: ellipsis; + white-space: normal; + overflow-wrap: normal; } .settings-editor > .settings-body .settings-tree-container .setting-item-bool .setting-value-checkbox { From 753fdf84207db7023f197c84c52bbfed03350099 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Wed, 5 Jul 2023 14:34:38 -0700 Subject: [PATCH 202/926] testing: fix inconsistent action name (#187098) Fixes #186370 --- src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts index 761d9c4319c..9d737a61184 100644 --- a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts +++ b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts @@ -2002,7 +2002,7 @@ class TreeActionsProvider { const extId = element.test.item.extId; primary.push(new Action( 'testing.outputPeek.goToFile', - localize('testing.goToFile', "Go to File"), + localize('testing.goToFile', "Go to Source"), ThemeIcon.asClassName(Codicon.goToFile), undefined, () => this.commandService.executeCommand('vscode.revealTest', extId), From 856bc1e28de12896333edefe113b0311353d6d61 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Wed, 5 Jul 2023 14:38:29 -0700 Subject: [PATCH 203/926] testing: property make double click go to test (#187099) Fixes #183835 --- .../workbench/contrib/testing/browser/testingExplorerView.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts b/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts index 2db684d11a5..9b7feea3e14 100644 --- a/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts +++ b/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts @@ -566,7 +566,8 @@ class TestingExplorerViewModel extends Disposable { keyboardNavigationLabelProvider: instantiationService.createInstance(TreeKeyboardNavigationLabelProvider), accessibilityProvider: instantiationService.createInstance(ListAccessibilityProvider), filter: this.filter, - findWidgetEnabled: false + findWidgetEnabled: false, + openOnSingleClick: false, }) as TestingObjectTree; @@ -612,7 +613,7 @@ class TestingExplorerViewModel extends Disposable { testService.excluded.onTestExclusionsChanged, )(this.tree.refilter, this.tree)); - this._register(this.tree.onMouseDblClick(e => { + this._register(this.tree.onDidOpen(e => { if (e.element instanceof TestItemTreeElement && !e.element.children.size && e.element.test.item.uri) { commandService.executeCommand('vscode.revealTest', e.element.test.item.extId); } From c7d219b53f5e602f27d36dc1676709f5e49ab2c2 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Wed, 5 Jul 2023 14:55:10 -0700 Subject: [PATCH 204/926] testing: make running tests open the results view instead of explorer (#187100) Closes #153068 --- .../contrib/testing/browser/testingProgressUiService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/testing/browser/testingProgressUiService.ts b/src/vs/workbench/contrib/testing/browser/testingProgressUiService.ts index a3ffe5863c2..63a83e504dc 100644 --- a/src/vs/workbench/contrib/testing/browser/testingProgressUiService.ts +++ b/src/vs/workbench/contrib/testing/browser/testingProgressUiService.ts @@ -82,7 +82,7 @@ export class TestingProgressTrigger extends Disposable { } private openTestView() { - this.viewsService.openView(Testing.ExplorerViewId, false); + this.viewsService.openView(Testing.ResultsViewId, false); } } From 322bc2d7d86e8d0c2ccb1bfdabe594e2011d500f Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Wed, 5 Jul 2023 15:09:01 -0700 Subject: [PATCH 205/926] Have getCurrentAuthenticationSessionInfo take in a secretStorageService (#187092) To start the migration process, we have `getCurrentAuthenticationSessionInfo` take in a `secretStorageService` and also replaces the `LocalStorageCredentialProvider` with a `LocalStorageSecretStorageProvider`. After this goes in we can then update all embedders (vscode.dev, github.dev, Codespaces, vscode-web-test?) and replace ICredentialProvider usages with ISecretStorageProviders and then once we do that, we can get rid of a bunch of code! --- src/vs/code/browser/workbench/workbench.ts | 177 ++++-------------- .../parts/activitybar/activitybarActions.ts | 4 +- src/vs/workbench/browser/web.main.ts | 11 +- .../browser/editSessionsStorageService.ts | 4 +- .../browser/authenticationService.ts | 16 +- .../secrets/browser/secretStorageService.ts | 4 - .../userDataSync/browser/userDataSyncInit.ts | 4 +- .../browser/userDataSyncWorkbenchService.ts | 4 +- 8 files changed, 75 insertions(+), 149 deletions(-) diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts index 62a0406d7f5..3ceea02d892 100644 --- a/src/vs/code/browser/workbench/workbench.ts +++ b/src/vs/code/browser/workbench/workbench.ts @@ -4,38 +4,32 @@ *--------------------------------------------------------------------------------------------*/ import { isStandalone } from 'vs/base/browser/browser'; -import { CancellationToken } from 'vs/base/common/cancellation'; import { parse } from 'vs/base/common/marshalling'; import { Emitter } from 'vs/base/common/event'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; import { isEqual } from 'vs/base/common/resources'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { request } from 'vs/base/parts/request/browser/request'; import product from 'vs/platform/product/common/product'; import { isFolderToOpen, isWorkspaceToOpen } from 'vs/platform/window/common/window'; import { create } from 'vs/workbench/workbench.web.main'; import { posix } from 'vs/base/common/path'; import { ltrim } from 'vs/base/common/strings'; -import type { ICredentialsProvider } from 'vs/platform/credentials/common/credentials'; import type { IURLCallbackProvider } from 'vs/workbench/services/url/browser/urlService'; import type { IWorkbenchConstructionOptions } from 'vs/workbench/browser/web.api'; import type { IWorkspace, IWorkspaceProvider } from 'vs/workbench/services/host/browser/browserHostService'; +import { ISecretStorageProvider } from 'vs/platform/secrets/common/secrets'; +import { AuthenticationSessionInfo } from 'vs/workbench/services/authentication/browser/authenticationService'; -interface ICredential { - service: string; - account: string; - password: string; -} +class LocalStorageSecretStorageProvider implements ISecretStorageProvider { + private static readonly STORAGE_KEY = 'secrets.provider'; -class LocalStorageCredentialsProvider implements ICredentialsProvider { + private _secrets: Record | undefined; - private static readonly CREDENTIALS_STORAGE_KEY = 'credentials.provider'; - - private readonly authService: string | undefined; + type: 'in-memory' | 'persisted' | 'unknown' = 'persisted'; constructor() { - let authSessionInfo: { readonly id: string; readonly accessToken: string; readonly providerId: string; readonly canSignOut?: boolean; readonly scopes: string[][] } | undefined; + let authSessionInfo: (AuthenticationSessionInfo & { scopes: string[][] }) | undefined; const authSessionElement = document.getElementById('vscode-workbench-auth-session'); const authSessionElementAttribute = authSessionElement ? authSessionElement.getAttribute('data-settings') : undefined; if (authSessionElementAttribute) { @@ -46,11 +40,15 @@ class LocalStorageCredentialsProvider implements ICredentialsProvider { if (authSessionInfo) { // Settings Sync Entry - this.setPassword(`${product.urlProtocol}.login`, 'account', JSON.stringify(authSessionInfo)); + this.set(`${product.urlProtocol}.loginAccount`, JSON.stringify(authSessionInfo)); // Auth extension Entry - this.authService = `${product.urlProtocol}-${authSessionInfo.providerId}.login`; - this.setPassword(this.authService, 'account', JSON.stringify(authSessionInfo.scopes.map(scopes => ({ + if (authSessionInfo.providerId !== 'github') { + console.error(`Unexpected auth provider: ${authSessionInfo.providerId}. Expected 'github'.`); + return; + } + const authAccount = JSON.stringify({ extensionId: 'vscode.github-authentication', key: 'github.auth' }); + this.set(authAccount, JSON.stringify(authSessionInfo.scopes.map(scopes => ({ id: authSessionInfo!.id, scopes, accessToken: authSessionInfo!.accessToken @@ -58,121 +56,44 @@ class LocalStorageCredentialsProvider implements ICredentialsProvider { } } - private _credentials: ICredential[] | undefined; - private get credentials(): ICredential[] { - if (!this._credentials) { + get(key: string): Promise { + return Promise.resolve(this.secrets[key]); + } + set(key: string, value: string): Promise { + this.secrets[key] = value; + this.save(); + + return Promise.resolve(); + } + async delete(key: string): Promise { + delete this.secrets[key]; + + this.save(); + + return Promise.resolve(); + } + + private get secrets(): Record { + if (!this._secrets) { try { - const serializedCredentials = window.localStorage.getItem(LocalStorageCredentialsProvider.CREDENTIALS_STORAGE_KEY); + const serializedCredentials = window.localStorage.getItem(LocalStorageSecretStorageProvider.STORAGE_KEY); if (serializedCredentials) { - this._credentials = JSON.parse(serializedCredentials); + this._secrets = JSON.parse(serializedCredentials); } } catch (error) { // ignore } - if (!Array.isArray(this._credentials)) { - this._credentials = []; + if (!(this._secrets instanceof Object)) { + this._secrets = {}; } } - return this._credentials; + return this._secrets; } private save(): void { - window.localStorage.setItem(LocalStorageCredentialsProvider.CREDENTIALS_STORAGE_KEY, JSON.stringify(this.credentials)); - } - - async getPassword(service: string, account: string): Promise { - return this.doGetPassword(service, account); - } - - private async doGetPassword(service: string, account?: string): Promise { - for (const credential of this.credentials) { - if (credential.service === service) { - if (typeof account !== 'string' || account === credential.account) { - return credential.password; - } - } - } - - return null; - } - - async setPassword(service: string, account: string, password: string): Promise { - this.doDeletePassword(service, account); - - this.credentials.push({ service, account, password }); - - this.save(); - - try { - if (password && service === this.authService) { - const value = JSON.parse(password); - if (Array.isArray(value) && value.length === 0) { - await this.logout(service); - } - } - } catch (error) { - console.log(error); - } - } - - async deletePassword(service: string, account: string): Promise { - const result = await this.doDeletePassword(service, account); - - if (result && service === this.authService) { - try { - await this.logout(service); - } catch (error) { - console.log(error); - } - } - - return result; - } - - private async doDeletePassword(service: string, account: string): Promise { - let found = false; - - this._credentials = this.credentials.filter(credential => { - if (credential.service === service && credential.account === account) { - found = true; - - return false; - } - - return true; - }); - - if (found) { - this.save(); - } - - return found; - } - - async findPassword(service: string): Promise { - return this.doGetPassword(service); - } - - async findCredentials(service: string): Promise> { - return this.credentials - .filter(credential => credential.service === service) - .map(({ account, password }) => ({ account, password })); - } - - private async logout(service: string): Promise { - const queryValues: Map = new Map(); - queryValues.set('logout', String(true)); - queryValues.set('service', service); - - await request({ - url: doCreateUri('/auth/logout', queryValues).toString(true) - }, CancellationToken.None); - } - - async clear(): Promise { - window.localStorage.removeItem(LocalStorageCredentialsProvider.CREDENTIALS_STORAGE_KEY); + window.localStorage.setItem(LocalStorageSecretStorageProvider.STORAGE_KEY, JSON.stringify(this.secrets)); } } @@ -469,24 +390,6 @@ class WorkspaceProvider implements IWorkspaceProvider { } } -function doCreateUri(path: string, queryValues: Map): URI { - let query: string | undefined = undefined; - - if (queryValues) { - let index = 0; - queryValues.forEach((value, key) => { - if (!query) { - query = ''; - } - - const prefix = (index++ === 0) ? '' : '&'; - query += `${prefix}${key}=${encodeURIComponent(value)}`; - }); - } - - return URI.parse(window.location.href).with({ path, query }); -} - (function () { // Find config by checking for DOM @@ -504,6 +407,6 @@ function doCreateUri(path: string, queryValues: Map): URI { settingsSyncOptions: config.settingsSyncOptions ? { enabled: config.settingsSyncOptions.enabled, } : undefined, workspaceProvider: WorkspaceProvider.create(config), urlCallbackProvider: new LocalStorageURLCallbackProvider(config.callbackRoute), - credentialsProvider: config.remoteAuthority ? undefined /* with a remote, we don't use a local credentials provider */ : new LocalStorageCredentialsProvider() + secretStorageProvider: config.remoteAuthority ? undefined /* with a remote, we don't use a local secret storage provider */ : new LocalStorageSecretStorageProvider() }); })(); diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts index ce043278337..6fbd655eb67 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts @@ -41,6 +41,7 @@ import { ICredentialsService } from 'vs/platform/credentials/common/credentials' import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { ILogService } from 'vs/platform/log/common/log'; +import { ISecretStorageService } from 'vs/platform/secrets/common/secrets'; export class ViewContainerActivityAction extends ActivityAction { @@ -235,7 +236,7 @@ export class AccountsActivityActionViewItem extends MenuActivityActionViewItem { private readonly problematicProviders: Set = new Set(); private initialized = false; - private sessionFromEmbedder = getCurrentAuthenticationSessionInfo(this.credentialsService, this.productService); + private sessionFromEmbedder = getCurrentAuthenticationSessionInfo(this.credentialsService, this.secretStorageService, this.productService); constructor( action: ActivityAction, @@ -253,6 +254,7 @@ export class AccountsActivityActionViewItem extends MenuActivityActionViewItem { @IConfigurationService configurationService: IConfigurationService, @IStorageService private readonly storageService: IStorageService, @IKeybindingService keybindingService: IKeybindingService, + @ISecretStorageService private readonly secretStorageService: ISecretStorageService, @ICredentialsService private readonly credentialsService: ICredentialsService, @ILogService private readonly logService: ILogService ) { diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index e1e8c5de6c6..4cec41e3b9a 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -92,6 +92,10 @@ import { BrowserRemoteResourceLoader } from 'vs/workbench/services/remote/browse import { BufferLogger } from 'vs/platform/log/common/bufferLog'; import { FileLoggerService } from 'vs/platform/log/common/fileLog'; import { IEmbedderTerminalService } from 'vs/workbench/services/terminal/common/embedderTerminalService'; +import { BrowserSecretStorageService } from 'vs/workbench/services/secrets/browser/secretStorageService'; +import { EncryptionService } from 'vs/workbench/services/encryption/browser/encryptionService'; +import { IEncryptionService } from 'vs/platform/encryption/common/encryptionService'; +import { ISecretStorageService } from 'vs/platform/secrets/common/secrets'; export class BrowserMain extends Disposable { @@ -384,9 +388,14 @@ export class BrowserMain extends Disposable { const credentialsService = new BrowserCredentialsService(environmentService, remoteAgentService, productService); serviceCollection.set(ICredentialsService, credentialsService); + const encryptionService = new EncryptionService(); + serviceCollection.set(IEncryptionService, encryptionService); + const secretStorageService = new BrowserSecretStorageService(storageService, encryptionService, environmentService, logService); + serviceCollection.set(ISecretStorageService, secretStorageService); + // Userdata Initialize Service const userDataInitializers: IUserDataInitializer[] = []; - userDataInitializers.push(new UserDataSyncInitializer(environmentService, credentialsService, userDataSyncStoreManagementService, fileService, userDataProfilesService, storageService, productService, requestService, logService, uriIdentityService)); + userDataInitializers.push(new UserDataSyncInitializer(environmentService, secretStorageService, credentialsService, userDataSyncStoreManagementService, fileService, userDataProfilesService, storageService, productService, requestService, logService, uriIdentityService)); if (environmentService.options.profile) { userDataInitializers.push(new UserDataProfileInitializer(environmentService, fileService, userDataProfileService, storageService, logService, uriIdentityService, requestService)); } diff --git a/src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts b/src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts index a3a69e50222..f394515c6e2 100644 --- a/src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts +++ b/src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts @@ -25,6 +25,7 @@ import { IUserDataSyncMachinesService, UserDataSyncMachinesService } from 'vs/pl import { Emitter } from 'vs/base/common/event'; import { CancellationError } from 'vs/base/common/errors'; import { EditSessionsStoreClient } from 'vs/workbench/contrib/editSessions/common/editSessionsStorageClient'; +import { ISecretStorageService } from 'vs/platform/secrets/common/secrets'; type ExistingSession = IQuickPickItem & { session: AuthenticationSession & { providerId: string } }; type AuthenticationProviderOption = IQuickPickItem & { provider: IAuthenticationProvider }; @@ -81,6 +82,7 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes @IProductService private readonly productService: IProductService, @IContextKeyService private readonly contextKeyService: IContextKeyService, @IDialogService private readonly dialogService: IDialogService, + @ISecretStorageService private readonly secretStorageService: ISecretStorageService, @ICredentialsService private readonly credentialsService: ICredentialsService ) { super(); @@ -278,7 +280,7 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes // If settings sync is already enabled, avoid asking again to authenticate if (this.shouldAttemptEditSessionInit()) { this.logService.info(`Reusing user data sync enablement`); - const authenticationSessionInfo = await getCurrentAuthenticationSessionInfo(this.credentialsService, this.productService); + const authenticationSessionInfo = await getCurrentAuthenticationSessionInfo(this.credentialsService, this.secretStorageService, this.productService); if (authenticationSessionInfo !== undefined) { this.logService.info(`Using current authentication session with ID ${authenticationSessionInfo.id}`); this.existingSessionId = authenticationSessionInfo.id; diff --git a/src/vs/workbench/services/authentication/browser/authenticationService.ts b/src/vs/workbench/services/authentication/browser/authenticationService.ts index d149dc29f23..f6c2f14685a 100644 --- a/src/vs/workbench/services/authentication/browser/authenticationService.ts +++ b/src/vs/workbench/services/authentication/browser/authenticationService.ts @@ -19,6 +19,7 @@ import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/ import { Severity } from 'vs/platform/notification/common/notification'; import { IProductService } from 'vs/platform/product/common/productService'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; +import { ISecretStorageService } from 'vs/platform/secrets/common/secrets'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IActivityService, NumberBadge } from 'vs/workbench/services/activity/common/activity'; import { IAuthenticationCreateSessionOptions, AuthenticationProviderInformation, AuthenticationSession, AuthenticationSessionsChangeEvent, IAuthenticationProvider, IAuthenticationService } from 'vs/workbench/services/authentication/common/authentication'; @@ -76,9 +77,17 @@ export function addAccountUsage(storageService: IStorageService, providerId: str storageService.store(accountKey, JSON.stringify(usages), StorageScope.APPLICATION, StorageTarget.MACHINE); } +// TODO: pull this out into its own service export type AuthenticationSessionInfo = { readonly id: string; readonly accessToken: string; readonly providerId: string; readonly canSignOut?: boolean }; -export async function getCurrentAuthenticationSessionInfo(credentialsService: ICredentialsService, productService: IProductService): Promise { - const authenticationSessionValue = await credentialsService.getPassword(`${productService.urlProtocol}.login`, 'account'); +export async function getCurrentAuthenticationSessionInfo( + // TODO: Remove when all known embedders implement SecretStorageProviders instead of CredentialsProviders + credentialsService: ICredentialsService, + secretStorageService: ISecretStorageService, + productService: IProductService +): Promise { + const authenticationSessionValue = + await secretStorageService.get(`${productService.urlProtocol}.loginAccount`) + ?? await credentialsService.getPassword(`${productService.urlProtocol}.login`, 'account'); if (authenticationSessionValue) { try { const authenticationSessionInfo: AuthenticationSessionInfo = JSON.parse(authenticationSessionValue); @@ -90,7 +99,8 @@ export async function getCurrentAuthenticationSessionInfo(credentialsService: IC return authenticationSessionInfo; } } catch (e) { - // ignore as this is a best effort operation. + // This is a best effort operation. + console.error(`Failed parsing current auth session value: ${e}`); } } return undefined; diff --git a/src/vs/workbench/services/secrets/browser/secretStorageService.ts b/src/vs/workbench/services/secrets/browser/secretStorageService.ts index 0f023b4d0d4..cc101aec985 100644 --- a/src/vs/workbench/services/secrets/browser/secretStorageService.ts +++ b/src/vs/workbench/services/secrets/browser/secretStorageService.ts @@ -5,9 +5,7 @@ import { IEncryptionService } from 'vs/platform/encryption/common/encryptionService'; import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; -import { INotificationService } from 'vs/platform/notification/common/notification'; import { ISecretStorageProvider, ISecretStorageService, BaseSecretStorageService } from 'vs/platform/secrets/common/secrets'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService'; @@ -20,8 +18,6 @@ export class BrowserSecretStorageService extends BaseSecretStorageService { @IStorageService storageService: IStorageService, @IEncryptionService encryptionService: IEncryptionService, @IBrowserWorkbenchEnvironmentService environmentService: IBrowserWorkbenchEnvironmentService, - @IInstantiationService instantiationService: IInstantiationService, - @INotificationService notificationService: INotificationService, @ILogService logService: ILogService ) { super(storageService, encryptionService, logService); diff --git a/src/vs/workbench/services/userDataSync/browser/userDataSyncInit.ts b/src/vs/workbench/services/userDataSync/browser/userDataSyncInit.ts index f87046057c6..ef77a1a411d 100644 --- a/src/vs/workbench/services/userDataSync/browser/userDataSyncInit.ts +++ b/src/vs/workbench/services/userDataSync/browser/userDataSyncInit.ts @@ -35,6 +35,7 @@ import { TasksInitializer } from 'vs/platform/userDataSync/common/tasksSync'; import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService'; import { IUserDataInitializer } from 'vs/workbench/services/userData/browser/userDataInit'; +import { ISecretStorageService } from 'vs/platform/secrets/common/secrets'; export class UserDataSyncInitializer implements IUserDataInitializer { @@ -46,6 +47,7 @@ export class UserDataSyncInitializer implements IUserDataInitializer { constructor( @IBrowserWorkbenchEnvironmentService private readonly environmentService: IBrowserWorkbenchEnvironmentService, + @ISecretStorageService private readonly secretStorageService: ISecretStorageService, @ICredentialsService private readonly credentialsService: ICredentialsService, @IUserDataSyncStoreManagementService private readonly userDataSyncStoreManagementService: IUserDataSyncStoreManagementService, @IFileService private readonly fileService: IFileService, @@ -90,7 +92,7 @@ export class UserDataSyncInitializer implements IUserDataInitializer { let authenticationSession; try { - authenticationSession = await getCurrentAuthenticationSessionInfo(this.credentialsService, this.productService); + authenticationSession = await getCurrentAuthenticationSessionInfo(this.credentialsService, this.secretStorageService, this.productService); } catch (error) { this.logService.error(error); } diff --git a/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts b/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts index 556467309b5..ecc0fb248af 100644 --- a/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts +++ b/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts @@ -39,6 +39,7 @@ import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity' import { isDiffEditorInput } from 'vs/workbench/common/editor'; import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService'; import { IUserDataInitializationService } from 'vs/workbench/services/userData/browser/userDataInit'; +import { ISecretStorageService } from 'vs/platform/secrets/common/secrets'; type AccountQuickPickItem = { label: string; authenticationProvider: IAuthenticationProvider; account?: UserDataSyncAccount; description?: string }; @@ -105,6 +106,7 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat @IExtensionService private readonly extensionService: IExtensionService, @IBrowserWorkbenchEnvironmentService private readonly environmentService: IBrowserWorkbenchEnvironmentService, @ICredentialsService private readonly credentialsService: ICredentialsService, + @ISecretStorageService private readonly secretStorageService: ISecretStorageService, @INotificationService private readonly notificationService: INotificationService, @IProgressService private readonly progressService: IProgressService, @IDialogService private readonly dialogService: IDialogService, @@ -169,7 +171,7 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat } private async initialize(): Promise { - const authenticationSession = await getCurrentAuthenticationSessionInfo(this.credentialsService, this.productService); + const authenticationSession = await getCurrentAuthenticationSessionInfo(this.credentialsService, this.secretStorageService, this.productService); if (this.currentSessionId === undefined && authenticationSession?.id) { if (this.environmentService.options?.settingsSyncOptions?.authenticationProvider && this.environmentService.options.settingsSyncOptions.enabled) { this.currentSessionId = authenticationSession.id; From 99494a7a29e381c72103c3ccfcd31b4d7cdf259a Mon Sep 17 00:00:00 2001 From: Bhavya U Date: Wed, 5 Jul 2023 16:14:40 -0700 Subject: [PATCH 206/926] Show busy indicator in remote menu when initializing. (#187018) * Show progress bar in remote indicator when initializing * Remove placeholder change --- .../contrib/remote/browser/remoteIndicator.ts | 41 ++++++++----------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts b/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts index add2bda68e1..0244b7ad8e6 100644 --- a/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts +++ b/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts @@ -8,8 +8,8 @@ import { STATUS_BAR_HOST_NAME_BACKGROUND, STATUS_BAR_HOST_NAME_FOREGROUND } from import { themeColorFromId } from 'vs/platform/theme/common/themeService'; import { IRemoteAgentService, remoteConnectionLatencyMeasurer } from 'vs/workbench/services/remote/common/remoteAgentService'; import { RunOnceScheduler, retry } from 'vs/base/common/async'; -import { Event } from 'vs/base/common/event'; -import { Disposable, dispose } from 'vs/base/common/lifecycle'; +import { Emitter, Event } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; import { MenuId, IMenuService, MenuItemAction, MenuRegistry, registerAction2, Action2, SubmenuItemAction } from 'vs/platform/actions/common/actions'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { StatusbarAlignment, IStatusbarService, IStatusbarEntryAccessor, IStatusbarEntry } from 'vs/workbench/services/statusbar/browser/statusbar'; @@ -115,6 +115,9 @@ export class RemoteStatusIndicator extends Disposable implements IWorkbenchContr private loggedInvalidGroupNames: { [group: string]: boolean } = Object.create(null); private readonly remoteExtensionMetadata: RemoteExtensionMetadata[]; private remoteMetadataInitialized: boolean = false; + private readonly _onDidChangeEntries = this._register(new Emitter()); + private readonly onDidChangeEntries: Event = this._onDidChangeEntries.event; + constructor( @IStatusbarService private readonly statusbarService: IStatusbarService, @IBrowserWorkbenchEnvironmentService private readonly environmentService: IBrowserWorkbenchEnvironmentService, @@ -341,6 +344,7 @@ export class RemoteStatusIndicator extends Disposable implements IWorkbenchContr } this.remoteMetadataInitialized = true; + this._onDidChangeEntries.fire(); showRemoteStartEntry.bindTo(this.contextKeyService).set(true); this.updateRemoteStatusIndicator(); } @@ -551,15 +555,8 @@ export class RemoteStatusIndicator extends Disposable implements IWorkbenchContr } } - // Show when there are commands or installable remote extensions. - if (this.hasRemoteMenuCommands(true) || this.remoteExtensionMetadata.some(ext => !ext.installed && ext.isPlatformCompatible)) { - this.renderRemoteStatusIndicator(`$(remote)`, nls.localize('noHost.tooltip', "Open a Remote Window")); - return; - } - - // No Remote Extensions: hide status indicator - dispose(this.remoteStatusEntry); - this.remoteStatusEntry = undefined; + this.renderRemoteStatusIndicator(`$(remote)`, nls.localize('noHost.tooltip', "Open a Remote Window")); + return; } private renderRemoteStatusIndicator(initialText: string, initialTooltip?: string | MarkdownString, command?: string, showProgress?: boolean): void { @@ -573,7 +570,7 @@ export class RemoteStatusIndicator extends Disposable implements IWorkbenchContr text, showProgress, tooltip, - command: command ?? (this.hasRemoteMenuCommands(false) || this.remoteExtensionMetadata.some(ext => !ext.installed && ext.isPlatformCompatible)) ? RemoteStatusIndicator.REMOTE_ACTIONS_COMMAND_ID : undefined + command: command ?? RemoteStatusIndicator.REMOTE_ACTIONS_COMMAND_ID }; if (this.remoteStatusEntry) { @@ -834,17 +831,15 @@ export class RemoteStatusIndicator extends Disposable implements IWorkbenchContr const itemUpdater = this.remoteIndicatorMenu.onDidChange(() => quickPick.items = computeItems()); quickPick.onDidHide(itemUpdater.dispose); + if (!this.remoteMetadataInitialized) { + quickPick.busy = true; + this._register(this.onDidChangeEntries(() => { + // If quick pick is open, update the quick pick items after initialization. + quickPick.busy = false; + quickPick.items = computeItems(); + })); + } + quickPick.show(); } - - private hasRemoteMenuCommands(ignoreInstallAdditional: boolean): boolean { - if (this.remoteAuthority !== undefined || this.virtualWorkspaceLocation !== undefined) { - if (RemoteStatusIndicator.SHOW_CLOSE_REMOTE_COMMAND_ID) { - return true; - } - } else if (!ignoreInstallAdditional && this.extensionGalleryService.isEnabled()) { - return true; - } - return this.getRemoteMenuActions().length > 0; - } } From 6fa852f87a0bb83f951e9ac38b4481462b10e11c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 5 Jul 2023 16:29:29 -0700 Subject: [PATCH 207/926] Bump semver from 7.3.5 to 7.5.3 in /remote (#185981) Bumps [semver](https://github.com/npm/node-semver) from 7.3.5 to 7.5.3. - [Release notes](https://github.com/npm/node-semver/releases) - [Changelog](https://github.com/npm/node-semver/blob/main/CHANGELOG.md) - [Commits](https://github.com/npm/node-semver/compare/v7.3.5...v7.5.3) --- updated-dependencies: - dependency-name: semver dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- remote/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/remote/yarn.lock b/remote/yarn.lock index e06f1e1adba..0539567ca2d 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -661,9 +661,9 @@ safe-buffer@~5.1.0, safe-buffer@~5.1.1: integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== semver@^7.3.5: - version "7.3.5" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" - integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== + version "7.5.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.3.tgz#161ce8c2c6b4b3bdca6caadc9fa3317a4c4fe88e" + integrity sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ== dependencies: lru-cache "^6.0.0" From 9618e4fa2a80e3ea788a6098a72d46c8db058a91 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 5 Jul 2023 16:29:55 -0700 Subject: [PATCH 208/926] Bump semver from 7.3.8 to 7.5.3 in /build/npm/gyp (#186809) Bumps [semver](https://github.com/npm/node-semver) from 7.3.8 to 7.5.3. - [Release notes](https://github.com/npm/node-semver/releases) - [Changelog](https://github.com/npm/node-semver/blob/main/CHANGELOG.md) - [Commits](https://github.com/npm/node-semver/compare/v7.3.8...v7.5.3) --- updated-dependencies: - dependency-name: semver dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build/npm/gyp/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build/npm/gyp/yarn.lock b/build/npm/gyp/yarn.lock index d5d6bced114..1fe78aba8f3 100644 --- a/build/npm/gyp/yarn.lock +++ b/build/npm/gyp/yarn.lock @@ -520,9 +520,9 @@ safe-buffer@~5.2.0: integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== semver@^7.3.5: - version "7.3.8" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" - integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== + version "7.5.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.3.tgz#161ce8c2c6b4b3bdca6caadc9fa3317a4c4fe88e" + integrity sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ== dependencies: lru-cache "^6.0.0" From c9073ad0c8f57bcbea556d67d8044a068667f2d7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 6 Jul 2023 01:54:58 +0200 Subject: [PATCH 209/926] Bump semver from 7.3.7 to 7.5.3 in /extensions/css-language-features (#187107) Bumps [semver](https://github.com/npm/node-semver) from 7.3.7 to 7.5.3. - [Release notes](https://github.com/npm/node-semver/releases) - [Changelog](https://github.com/npm/node-semver/blob/main/CHANGELOG.md) - [Commits](https://github.com/npm/node-semver/compare/v7.3.7...v7.5.3) --- updated-dependencies: - dependency-name: semver dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- extensions/css-language-features/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/extensions/css-language-features/yarn.lock b/extensions/css-language-features/yarn.lock index f8ab6ac49a4..bad63f26ca1 100644 --- a/extensions/css-language-features/yarn.lock +++ b/extensions/css-language-features/yarn.lock @@ -34,9 +34,9 @@ minimatch@^5.1.0: brace-expansion "^2.0.1" semver@^7.3.7: - version "7.3.7" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" - integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== + version "7.5.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.3.tgz#161ce8c2c6b4b3bdca6caadc9fa3317a4c4fe88e" + integrity sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ== dependencies: lru-cache "^6.0.0" From 609e6369335d436a5fa14ec97467447d5608b51f Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Wed, 5 Jul 2023 17:19:33 -0700 Subject: [PATCH 210/926] Also check for secret storage provider before using old API (#187105) This allows a nice migration path for web environments: 1. Embedder implements a secret storage provider in addition to the credentials provider they already have 2. Wait a release or 2 3. Remove the credentials provider from the embedder Once all known embedders do number 1, we can delete the `_oldMainThreadSecretState` entirely. --- .../workbench/api/browser/mainThreadSecretState.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadSecretState.ts b/src/vs/workbench/api/browser/mainThreadSecretState.ts index 8bb4a91d976..d8fa95ac538 100644 --- a/src/vs/workbench/api/browser/mainThreadSecretState.ts +++ b/src/vs/workbench/api/browser/mainThreadSecretState.ts @@ -110,6 +110,7 @@ class OldMainThreadSecretState extends Disposable implements MainThreadSecretSta export class MainThreadSecretState extends Disposable implements MainThreadSecretStateShape { private readonly _proxy: ExtHostSecretStateShape; + // TODO: Remove this when all known embedders implement a secret storage provider private readonly _oldMainThreadSecretState: OldMainThreadSecretState | undefined; private readonly _sequencer = new SequencerByKey(); @@ -131,7 +132,11 @@ export class MainThreadSecretState extends Disposable implements MainThreadSecre this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostSecretState); - if (environmentService.options?.credentialsProvider) { + // If the embedder doesn't implement a secret storage provider, then we need to use the old API + // to ensure that secrets are still stored in a secure way. This is only temporary until all + // embedders implement a secret storage provider. + // TODO: Remove this when all known embedders implement a secret storage provider + if (environmentService.options?.credentialsProvider && !environmentService.options?.secretStorageProvider) { this._oldMainThreadSecretState = this._register(new OldMainThreadSecretState( this._proxy, credentialsService, @@ -158,7 +163,7 @@ export class MainThreadSecretState extends Disposable implements MainThreadSecre } private async doGetPassword(extensionId: string, key: string): Promise { - // TODO: Remove this when we remove the old API + // TODO: Remove this when all known embedders implement a secret storage provider if (this._oldMainThreadSecretState) { return await this._oldMainThreadSecretState.$getPassword(extensionId, key); } @@ -184,7 +189,7 @@ export class MainThreadSecretState extends Disposable implements MainThreadSecre } private async doSetPassword(extensionId: string, key: string, value: string): Promise { - // TODO: Remove this when we remove the old API + // TODO: Remove this when all known embedders implement a secret storage provider if (this._oldMainThreadSecretState) { return await this._oldMainThreadSecretState.$setPassword(extensionId, key, value); } @@ -200,7 +205,7 @@ export class MainThreadSecretState extends Disposable implements MainThreadSecre } private async doDeletePassword(extensionId: string, key: string): Promise { - // TODO: Remove this when we remove the old API + // TODO: Remove this when all known embedders implement a secret storage provider if (this._oldMainThreadSecretState) { return await this._oldMainThreadSecretState.$deletePassword(extensionId, key); } From 6c8cdf325eb1dc8a0e2ea9205a1d2ca05f69c101 Mon Sep 17 00:00:00 2001 From: Bhavya U Date: Wed, 5 Jul 2023 17:35:13 -0700 Subject: [PATCH 211/926] Remove galleryExtension check when initializing remote menu (#187109) Remove galleryExtension check --- .../contrib/remote/browser/remoteIndicator.ts | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts b/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts index 0244b7ad8e6..1450fb17d17 100644 --- a/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts +++ b/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts @@ -47,7 +47,7 @@ import { IProductService } from 'vs/platform/product/common/productService'; import { DomEmitter } from 'vs/base/browser/event'; import { registerColor } from 'vs/platform/theme/common/colorRegistry'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; -import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; +import { CancellationToken } from 'vs/base/common/cancellation'; import { ThemeIcon } from 'vs/base/common/themables'; import { infoIcon } from 'vs/workbench/contrib/extensions/browser/extensionsIcons'; import { IOpenerService } from 'vs/platform/opener/common/opener'; @@ -325,22 +325,18 @@ export class RemoteStatusIndicator extends Disposable implements IWorkbenchContr for (let i = 0; i < this.remoteExtensionMetadata.length; i++) { const extensionId = this.remoteExtensionMetadata[i].id; const supportedPlatforms = this.remoteExtensionMetadata[i].supportedPlatforms; - // Update compatibility - const token = new CancellationTokenSource(); - const galleryExtension = (await this.extensionGalleryService.getExtensions([{ id: extensionId }], token.token))[0]; - if (!await this.extensionManagementService.canInstall(galleryExtension)) { - this.remoteExtensionMetadata[i].isPlatformCompatible = false; + const isInstalled = (await this.extensionManagementService.getInstalled()).find(value => ExtensionIdentifier.equals(value.identifier.id, extensionId)) ? true : false; + + this.remoteExtensionMetadata[i].installed = isInstalled; + if (isInstalled) { + this.remoteExtensionMetadata[i].isPlatformCompatible = true; } else if (supportedPlatforms && !supportedPlatforms.includes(currentPlatform)) { this.remoteExtensionMetadata[i].isPlatformCompatible = false; } else { this.remoteExtensionMetadata[i].isPlatformCompatible = true; - this.remoteExtensionMetadata[i].dependencies = galleryExtension.properties.extensionPack ?? []; } - - // Check if installed and enabled - this.remoteExtensionMetadata[i].installed = (await this.extensionManagementService.getInstalled()).find(value => ExtensionIdentifier.equals(value.identifier.id, extensionId)) ? true : false; } this.remoteMetadataInitialized = true; From 4223cc994e10b1b4bb92ff8ee5373aaace39361d Mon Sep 17 00:00:00 2001 From: Marshall Roch Date: Tue, 30 May 2023 13:19:26 -0400 Subject: [PATCH 212/926] fix [object Object] in invalid decorator warning when there's an invalid decorator, you get a warning like this: ``` [warning] INVALID decoration from extension '[object Object]': Error: The decoration is empty ``` as is done elsewhere in this file, should use `extensionId.identifier.value` to get a string --- src/vs/workbench/api/common/extHostDecorations.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/api/common/extHostDecorations.ts b/src/vs/workbench/api/common/extHostDecorations.ts index 12dcb1072a5..7633574f065 100644 --- a/src/vs/workbench/api/common/extHostDecorations.ts +++ b/src/vs/workbench/api/common/extHostDecorations.ts @@ -106,7 +106,7 @@ export class ExtHostDecorations implements ExtHostDecorationsShape { } result[id] = [data.propagate, data.tooltip, data.badge, data.color]; } catch (e) { - this._logService.warn(`INVALID decoration from extension '${extensionId}': ${e}`); + this._logService.warn(`INVALID decoration from extension '${extensionId.identifier.value}': ${e}`); } } catch (err) { this._logService.error(err); From 42e9acc5a8efae6150f49926221171ca8f93ad54 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Thu, 6 Jul 2023 07:11:18 -0700 Subject: [PATCH 213/926] no need to explicitly get workspace tasks as they're always refreshed now --- .../contrib/tasks/browser/abstractTaskService.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index 979b207b9e5..801bcdf5e6f 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -374,7 +374,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer this._tasksReconnected = true; return; } - this.getWorkspaceTasks().then(async () => { + this.getWorkspaceTasks(TaskRunSource.Reconnect).then(async () => { this._tasksReconnected = await this._reconnectTasks(); }); } @@ -2349,11 +2349,11 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return { workspaceFolder, set: undefined, configurations: undefined, hasErrors: false }; } - private async _computeTasksForSingleConfig(workspaceFolder: IWorkspaceFolder | undefined, config: TaskConfig.IExternalTaskRunnerConfiguration | undefined, runSource: TaskRunSource, custom: CustomTask[], customized: IStringDictionary, source: TaskConfig.TaskConfigSource, isRecentTask: boolean = false): Promise { - if (!config || !workspaceFolder) { + private async _computeTasksForSingleConfig(workspaceFolder: IWorkspaceFolder, config: TaskConfig.IExternalTaskRunnerConfiguration | undefined, runSource: TaskRunSource, custom: CustomTask[], customized: IStringDictionary, source: TaskConfig.TaskConfigSource, isRecentTask: boolean = false): Promise { + if (!config) { return false; } - const taskSystemInfo: ITaskSystemInfo | undefined = workspaceFolder ? this._getTaskSystemInfo(workspaceFolder.uri.scheme) : undefined; + const taskSystemInfo: ITaskSystemInfo | undefined = this._getTaskSystemInfo(workspaceFolder.uri.scheme); const problemReporter = new ProblemReporter(this._outputChannel); if (!taskSystemInfo) { problemReporter.fatal(nls.localize('TaskSystem.workspaceFolderError', 'Workspace folder was undefined')); @@ -2746,7 +2746,6 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } private async _runTaskCommand(filter?: string | ITaskIdentifier): Promise { - await this.getWorkspaceTasks(); if (!filter) { return this._doRunTaskCommand(); } @@ -2904,7 +2903,6 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer title: strings.fetching }; const promise = (async () => { - await this.getWorkspaceTasks(); let taskGroupTasks: (Task | ConfiguringTask)[] = []; async function runSingleTask(task: Task | undefined, problemMatcherOptions: IProblemMatcherRunOptions | undefined, that: AbstractTaskService) { From e1021bf70d836a572d6de04c83e36ddac8c64ed4 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 6 Jul 2023 07:14:31 -0700 Subject: [PATCH 214/926] Create codespaces_issue.yml --- .github/commands/codespaces_issue.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .github/commands/codespaces_issue.yml diff --git a/.github/commands/codespaces_issue.yml b/.github/commands/codespaces_issue.yml new file mode 100644 index 00000000000..7abacafaf21 --- /dev/null +++ b/.github/commands/codespaces_issue.yml @@ -0,0 +1,11 @@ +# Learn more about the syntax here: +# https://docs.github.com/en/early-access/github/save-time-with-slash-commands/syntax-for-user-defined-slash-commands +--- +trigger: codespaces_issue +title: Codespaces Issue +description: Report downstream + +steps: + - type: fill + template: |- + This looks like an issue with the Codespaces service which we don't track in this repository. You can report this to the Codespaces team at https://github.com/orgs/community/discussions/categories/codespaces From 4f1df73b952ee3526c279b0aea348a4733bb128f Mon Sep 17 00:00:00 2001 From: meganrogge Date: Thu, 6 Jul 2023 09:23:11 -0700 Subject: [PATCH 215/926] when supported executions change, make sure to unset the promise so they get re-evaluated --- .../workbench/contrib/tasks/browser/abstractTaskService.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index 801bcdf5e6f..12fc22bd477 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -362,6 +362,8 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer const processContext = ProcessExecutionSupportedContext.bindTo(this._contextKeyService); processContext.set(process && !isVirtual); } + // update tasks so an incomplete list isn't returned when getWorkspaceTasks is called + this._workspaceTasksPromise = undefined; this._onDidRegisterSupportedExecutions.fire(); } @@ -2201,6 +2203,9 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } await this._waitForSupportedExecutions; await this._whenTaskSystemReady; + if (this._workspaceTasksPromise) { + return this._workspaceTasksPromise; + } return this._updateWorkspaceTasks(runSource); } From 001615e0b661554397e1af7c62e1ef10cc9ae96a Mon Sep 17 00:00:00 2001 From: rebornix Date: Thu, 6 Jul 2023 10:29:50 -0700 Subject: [PATCH 216/926] No need for additional readonly and missing file check. --- .../workbench/api/common/extHostNotebook.ts | 72 +------------------ 1 file changed, 2 insertions(+), 70 deletions(-) diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index 02e1dd91363..e61788f49e1 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -328,11 +328,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape { } // validate write - const statBeforeWrite = await this._validateWriteFile(uri, options); - - if (!statBeforeWrite) { - await this._mkdirp(uri); - } + await this._validateWriteFile(uri, options); const data: vscode.NotebookData = { metadata: filter(document.apiNotebook.metadata, key => !(serializer.options?.transientDocumentMetadata ?? {})[key]), @@ -377,20 +373,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape { } private async _validateWriteFile(uri: URI, options: files.IWriteFileOptions) { - // File system provider registered in Extension Host doesn't have unlock or atomic support - // Validate via file stat meta data const stat = await this._extHostFileSystem.value.stat(uri); - - // File cannot be directory - if ((stat.type & files.FileType.Directory) !== 0) { - throw new files.FileOperationError(localize('fileIsDirectoryWriteError', "Unable to write file '{0}' that is actually a directory", this._resourceForError(uri)), files.FileOperationResult.FILE_IS_DIRECTORY, options); - } - - // File cannot be readonly - if ((stat.permissions ?? 0) & files.FilePermission.Readonly) { - throw new files.FileOperationError(localize('err.readonly', "Unable to modify read-only file '{0}'", this._resourceForError(uri)), files.FileOperationResult.FILE_PERMISSION_DENIED); - } - // Dirty write prevention if ( typeof options?.mtime === 'number' && typeof options.etag === 'string' && options.etag !== files.ETAG_DISABLED && @@ -400,58 +383,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape { throw new files.FileOperationError(localize('fileModifiedError', "File Modified Since"), files.FileOperationResult.FILE_MODIFIED_SINCE, options); } - return stat; - } - - private async _mkdirp(uri: URI) { - const providerExtUri = this._extHostFileSystem.getFileSystemProviderExtUri(uri.scheme); - let directory = providerExtUri.dirname(uri); - - const directoriesToCreate: string[] = []; - - while (!providerExtUri.isEqual(directory, providerExtUri.dirname(directory))) { - try { - const stat = await this._extHostFileSystem.value.stat(directory); - if ((stat.type & files.FileType.Directory) === 0) { - throw new Error(localize('mkdirExistsError', "Unable to create folder '{0}' that already exists but is not a directory", this._resourceForError(directory))); - } - - break; // we have hit a directory that exists -> good - } catch (error) { - - // Bubble up any other error that is not file not found - if (files.toFileSystemProviderErrorCode(error) !== files.FileSystemProviderErrorCode.FileNotFound) { - throw error; - } - - // Upon error, remember directories that need to be created - directoriesToCreate.push(providerExtUri.basename(directory)); - - // Continue up - directory = providerExtUri.dirname(directory); - } - } - - // Create directories as needed - for (let i = directoriesToCreate.length - 1; i >= 0; i--) { - directory = providerExtUri.joinPath(directory, directoriesToCreate[i]); - - try { - await this._extHostFileSystem.value.createDirectory(directory); - } catch (error) { - if (files.toFileSystemProviderErrorCode(error) !== files.FileSystemProviderErrorCode.FileExists) { - // For mkdirp() we tolerate that the mkdir() call fails - // in case the folder already exists. This follows node.js - // own implementation of fs.mkdir({ recursive: true }) and - // reduces the chances of race conditions leading to errors - // if multiple calls try to create the same folders - // As such, we only throw an error here if it is other than - // the fact that the file already exists. - // (see also https://github.com/microsoft/vscode/issues/89834) - throw error; - } - } - } + return; } private _resourceForError(uri: URI): string { From 078d0fd6e3041364c0e9a686a4deb0a13980bb66 Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Thu, 6 Jul 2023 11:00:48 -0700 Subject: [PATCH 217/926] Fix capitalization in survey notification (#187168) --- src/vs/workbench/contrib/surveys/browser/ces.contribution.ts | 2 +- .../contrib/surveys/browser/languageSurveys.contribution.ts | 2 +- src/vs/workbench/contrib/surveys/browser/nps.contribution.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/surveys/browser/ces.contribution.ts b/src/vs/workbench/contrib/surveys/browser/ces.contribution.ts index 386247aa716..b24d91f8da3 100644 --- a/src/vs/workbench/contrib/surveys/browser/ces.contribution.ts +++ b/src/vs/workbench/contrib/surveys/browser/ces.contribution.ts @@ -98,7 +98,7 @@ class CESContribution extends Disposable implements IWorkbenchContribution { this.skipSurvey(); } }, { - label: nls.localize('remindLater', "Remind Me later"), + label: nls.localize('remindLater', "Remind Me Later"), run: () => { sendTelemetry('remindLater'); this.storageService.store(REMIND_LATER_DATE_KEY, new Date().toUTCString(), StorageScope.APPLICATION, StorageTarget.USER); diff --git a/src/vs/workbench/contrib/surveys/browser/languageSurveys.contribution.ts b/src/vs/workbench/contrib/surveys/browser/languageSurveys.contribution.ts index 300123f2761..b72e929518a 100644 --- a/src/vs/workbench/contrib/surveys/browser/languageSurveys.contribution.ts +++ b/src/vs/workbench/contrib/surveys/browser/languageSurveys.contribution.ts @@ -105,7 +105,7 @@ class LanguageSurvey extends Disposable { storageService.store(SKIP_VERSION_KEY, productService.version, StorageScope.APPLICATION, StorageTarget.USER); } }, { - label: localize('remindLater', "Remind Me later"), + label: localize('remindLater', "Remind Me Later"), run: () => { telemetryService.publicLog(`${data.surveyId}.survey/remindMeLater`); storageService.store(SESSION_COUNT_KEY, sessionCount - 3, StorageScope.APPLICATION, StorageTarget.USER); diff --git a/src/vs/workbench/contrib/surveys/browser/nps.contribution.ts b/src/vs/workbench/contrib/surveys/browser/nps.contribution.ts index bf961d2e22f..f3cea74430c 100644 --- a/src/vs/workbench/contrib/surveys/browser/nps.contribution.ts +++ b/src/vs/workbench/contrib/surveys/browser/nps.contribution.ts @@ -76,7 +76,7 @@ class NPSContribution implements IWorkbenchContribution { storageService.store(SKIP_VERSION_KEY, productService.version, StorageScope.APPLICATION, StorageTarget.USER); } }, { - label: nls.localize('remindLater', "Remind Me later"), + label: nls.localize('remindLater', "Remind Me Later"), run: () => storageService.store(SESSION_COUNT_KEY, sessionCount - 3, StorageScope.APPLICATION, StorageTarget.USER) }, { label: nls.localize('neverAgain', "Don't Show Again"), From ccbcba53e642136ce30465cd4f5605d60c760d7c Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Thu, 6 Jul 2023 11:04:59 -0700 Subject: [PATCH 218/926] debug: shut down debug sessions if an extension host goes away (#187169) Fixes #184552 --- src/vs/workbench/api/browser/mainThreadDebugService.ts | 7 ++++++- src/vs/workbench/contrib/debug/browser/rawDebugSession.ts | 3 ++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadDebugService.ts b/src/vs/workbench/api/browser/mainThreadDebugService.ts index 79fe81e21d6..6b833ab272e 100644 --- a/src/vs/workbench/api/browser/mainThreadDebugService.ts +++ b/src/vs/workbench/api/browser/mainThreadDebugService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { DisposableStore } from 'vs/base/common/lifecycle'; +import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; import { URI as uri, UriComponents } from 'vs/base/common/uri'; import { IDebugService, IConfig, IDebugConfigurationProvider, IBreakpoint, IFunctionBreakpoint, IBreakpointData, IDebugAdapter, IDebugAdapterDescriptorFactory, IDebugSession, IDebugAdapterFactory, IDataBreakpoint, IDebugSessionOptions, IInstructionBreakpoint, DebugConfigurationProviderTriggerKind } from 'vs/workbench/contrib/debug/common/debug'; import { @@ -50,6 +50,11 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb this._toDispose.add(debugService.getViewModel().onDidFocusSession(session => { this._proxy.$acceptDebugSessionActiveChanged(this.getSessionDto(session)); })); + this._toDispose.add(toDisposable(() => { + for (const [handle, da] of this._debugAdapters) { + da.fireError(handle, new Error('Extension host shut down')); + } + })); this._debugAdapters = new Map(); this._debugConfigurationProviders = new Map(); diff --git a/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts b/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts index 294aaffa3e9..db8cd73120e 100644 --- a/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts @@ -573,7 +573,8 @@ export class RawDebugSession implements IDisposable { args.suspendDebuggee = suspendDebuggee; } - await this.send('disconnect', args, undefined, 2000); + // if there's an error, the DA is probably already gone, so give it a much shorter timeout. + await this.send('disconnect', args, undefined, error ? 200 : 2000); } catch (e) { // Catch the potential 'disconnect' error - no need to show it to the user since the adapter is shutting down } finally { From c067a3bf81b79b0c29c983b847fd0db3839fc6d0 Mon Sep 17 00:00:00 2001 From: Raymond Zhao <7199958+rzhao271@users.noreply.github.com> Date: Thu, 6 Jul 2023 11:18:02 -0700 Subject: [PATCH 219/926] chore: bump semver 7 dependencies (#187171) --- build/yarn.lock | 15 ++++----------- extensions/html-language-features/yarn.lock | 6 +++--- extensions/json-language-features/yarn.lock | 6 +++--- extensions/markdown-language-features/yarn.lock | 6 +++--- test/smoke/yarn.lock | 6 +++--- 5 files changed, 16 insertions(+), 23 deletions(-) diff --git a/build/yarn.lock b/build/yarn.lock index c29bf14c70a..3c3bce1fbc5 100644 --- a/build/yarn.lock +++ b/build/yarn.lock @@ -2568,17 +2568,10 @@ semver@^6.2.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^7.3.2: - version "7.3.4" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97" - integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw== - dependencies: - lru-cache "^6.0.0" - -semver@^7.3.5, semver@^7.3.8: - version "7.3.8" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" - integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== +semver@^7.3.2, semver@^7.3.5, semver@^7.3.8: + version "7.5.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.3.tgz#161ce8c2c6b4b3bdca6caadc9fa3317a4c4fe88e" + integrity sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ== dependencies: lru-cache "^6.0.0" diff --git a/extensions/html-language-features/yarn.lock b/extensions/html-language-features/yarn.lock index 9b13e63e639..e4f74847429 100644 --- a/extensions/html-language-features/yarn.lock +++ b/extensions/html-language-features/yarn.lock @@ -354,9 +354,9 @@ semver@^5.3.0, semver@^5.4.1: integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== semver@^7.3.7: - version "7.3.7" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" - integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== + version "7.5.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.3.tgz#161ce8c2c6b4b3bdca6caadc9fa3317a4c4fe88e" + integrity sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ== dependencies: lru-cache "^6.0.0" diff --git a/extensions/json-language-features/yarn.lock b/extensions/json-language-features/yarn.lock index 5bec70a9925..dcf55538c9a 100644 --- a/extensions/json-language-features/yarn.lock +++ b/extensions/json-language-features/yarn.lock @@ -359,9 +359,9 @@ semver@^5.3.0, semver@^5.4.1: integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== semver@^7.3.7: - version "7.3.7" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" - integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== + version "7.5.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.3.tgz#161ce8c2c6b4b3bdca6caadc9fa3317a4c4fe88e" + integrity sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ== dependencies: lru-cache "^6.0.0" diff --git a/extensions/markdown-language-features/yarn.lock b/extensions/markdown-language-features/yarn.lock index e796e655bae..46f769556aa 100644 --- a/extensions/markdown-language-features/yarn.lock +++ b/extensions/markdown-language-features/yarn.lock @@ -480,9 +480,9 @@ semver@^5.3.0, semver@^5.4.1: integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== semver@^7.3.5: - version "7.3.7" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" - integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== + version "7.5.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.3.tgz#161ce8c2c6b4b3bdca6caadc9fa3317a4c4fe88e" + integrity sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ== dependencies: lru-cache "^6.0.0" diff --git a/test/smoke/yarn.lock b/test/smoke/yarn.lock index 42787b9e371..9c7dbcbe089 100644 --- a/test/smoke/yarn.lock +++ b/test/smoke/yarn.lock @@ -643,9 +643,9 @@ safe-buffer@~5.1.0, safe-buffer@~5.1.1: integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== semver@^7.3.8: - version "7.5.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.1.tgz#c90c4d631cf74720e46b21c1d37ea07edfab91ec" - integrity sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw== + version "7.5.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.3.tgz#161ce8c2c6b4b3bdca6caadc9fa3317a4c4fe88e" + integrity sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ== dependencies: lru-cache "^6.0.0" From ed59e75f3d4a3bf50d4d51edfb761afef18cb9dd Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 6 Jul 2023 11:35:45 -0700 Subject: [PATCH 220/926] Add IPtyHostService This is a new service which extends IPtyService and provides additional methods on the process that manages the connection to the pty host: - IPtyService: Talk to the pty host, this may be direct via a message port of via the main/server procs. - IPtyHostService: Talk to the pty host management interfaces on the main/server procs. These are no longer available as optional methods on IPtyService to be clear about where they happen (eg. getProfiles). Fixes #186935 --- src/vs/platform/terminal/common/terminal.ts | 43 ++++---- .../platform/terminal/node/ptyHostService.ts | 9 +- src/vs/platform/terminal/node/ptyService.ts | 8 +- src/vs/server/node/remoteTerminalChannel.ts | 102 +++++++++--------- src/vs/server/node/serverServices.ts | 9 +- .../electron-sandbox/localTerminalBackend.ts | 3 +- 6 files changed, 86 insertions(+), 88 deletions(-) diff --git a/src/vs/platform/terminal/common/terminal.ts b/src/vs/platform/terminal/common/terminal.ts index 890998623de..2f29ede8364 100644 --- a/src/vs/platform/terminal/common/terminal.ts +++ b/src/vs/platform/terminal/common/terminal.ts @@ -221,8 +221,6 @@ export enum TerminalIpcChannels { Heartbeat = 'heartbeat' } -export const IPtyService = createDecorator('ptyService'); - export const enum ProcessPropertyType { Cwd = 'cwd', InitialCwd = 'initialCwd', @@ -266,18 +264,8 @@ export interface IFixedTerminalDimensions { rows?: number; } -export interface IPtyHostController { - readonly onPtyHostExit?: Event; - readonly onPtyHostStart?: Event; - readonly onPtyHostUnresponsive?: Event; - readonly onPtyHostResponsive?: Event; - readonly onPtyHostRequestResolveVariables?: Event; - - restartPtyHost?(): void; - acceptPtyHostResolvedVariables?(requestId: number, resolved: string[]): Promise; -} - -export interface IPtyService extends IPtyHostController { +export const IPtyService = createDecorator('ptyService'); +export interface IPtyService { readonly _serviceBrand: undefined; readonly onProcessData: Event<{ id: number; event: IProcessDataEvent | string }>; @@ -288,10 +276,6 @@ export interface IPtyService extends IPtyHostController { readonly onDidChangeProperty: Event<{ id: number; property: IProcessProperty }>; readonly onProcessExit: Event<{ id: number; event: number | undefined }>; - restartPtyHost?(): Promise; - shutdownAll?(): Promise; - acceptPtyHostResolvedVariables?(requestId: number, resolved: string[]): Promise; - createProcess( shellLaunchConfig: IShellLaunchConfig, cwd: string, @@ -307,6 +291,7 @@ export interface IPtyService extends IPtyHostController { ): Promise; attachToProcess(id: number): Promise; detachFromProcess(id: number, forcePersist?: boolean): Promise; + shutdownAll(): Promise; /** * Lists all orphaned processes, ie. those without a connected frontend. @@ -334,7 +319,6 @@ export interface IPtyService extends IPtyHostController { uninstallAllAutoReplies(): Promise; uninstallAutoReply(match: string): Promise; getDefaultSystemShell(osOverride?: OperatingSystem): Promise; - getProfiles?(workspaceId: string, profiles: unknown, defaultProfile: unknown, includeDetectedProfiles?: boolean): Promise; getEnvironment(): Promise; getWslPath(original: string, direction: 'unix-to-win' | 'win-to-unix'): Promise; getRevivedPtyNewId(workspaceId: string, id: number): Promise; @@ -343,7 +327,7 @@ export interface IPtyService extends IPtyHostController { reduceConnectionGraceTime(): Promise; requestDetachInstance(workspaceId: string, instanceId: number): Promise; acceptDetachInstanceReply(requestId: number, persistentProcessId?: number): Promise; - freePortKillProcess?(port: string): Promise<{ port: string; processId: string }>; + freePortKillProcess(port: string): Promise<{ port: string; processId: string }>; /** * Serializes and returns terminal state. * @param ids The persistent terminal IDs to serialize. @@ -357,9 +341,26 @@ export interface IPtyService extends IPtyHostController { refreshProperty(id: number, property: T): Promise; updateProperty(id: number, property: T, value: IProcessPropertyMap[T]): Promise; + // TODO: Make mandatory and remove impl from pty host service refreshIgnoreProcessNames?(names: string[]): Promise; } +export interface IPtyHostController { + readonly onPtyHostExit: Event; + readonly onPtyHostStart: Event; + readonly onPtyHostUnresponsive: Event; + readonly onPtyHostResponsive: Event; + readonly onPtyHostRequestResolveVariables: Event; + + restartPtyHost(): Promise; + acceptPtyHostResolvedVariables(requestId: number, resolved: string[]): Promise; + getProfiles(workspaceId: string, profiles: unknown, defaultProfile: unknown, includeDetectedProfiles?: boolean): Promise; +} + +export const IPtyHostService = createDecorator('ptyHostService'); +export interface IPtyHostService extends IPtyService, IPtyHostController { +} + /** * Serialized terminal state matching the interface that can be used across versions, the version * should be verified before using the state payload. @@ -1066,7 +1067,7 @@ export const ILocalPtyService = createDecorator('localPtyServi * * **This service should only be used within the terminal component.** */ -export interface ILocalPtyService extends IPtyService { } +export interface ILocalPtyService extends IPtyHostService { } export const ITerminalLogService = createDecorator('terminalLogService'); export interface ITerminalLogService extends ILogService { diff --git a/src/vs/platform/terminal/node/ptyHostService.ts b/src/vs/platform/terminal/node/ptyHostService.ts index ecd550e3826..59b84827479 100644 --- a/src/vs/platform/terminal/node/ptyHostService.ts +++ b/src/vs/platform/terminal/node/ptyHostService.ts @@ -13,7 +13,7 @@ import { RemoteLoggerChannelClient } from 'vs/platform/log/common/logIpc'; import { getResolvedShellEnv } from 'vs/platform/shell/node/shellEnv'; import { IPtyHostProcessReplayEvent } from 'vs/platform/terminal/common/capabilities/capabilities'; import { RequestStore } from 'vs/platform/terminal/common/requestStore'; -import { HeartbeatConstants, IHeartbeatService, IProcessDataEvent, IProcessProperty, IProcessPropertyMap, IProcessReadyEvent, IPtyService, IRequestResolveVariablesEvent, ISerializedTerminalState, IShellLaunchConfig, ITerminalLaunchError, ITerminalProcessOptions, ITerminalProfile, ITerminalsLayoutInfo, ProcessPropertyType, TerminalIcon, TerminalIpcChannels, TerminalSettingId, TitleEventSource } from 'vs/platform/terminal/common/terminal'; +import { HeartbeatConstants, IHeartbeatService, IProcessDataEvent, IProcessProperty, IProcessPropertyMap, IProcessReadyEvent, IPtyHostService, IPtyService, IRequestResolveVariablesEvent, ISerializedTerminalState, IShellLaunchConfig, ITerminalLaunchError, ITerminalProcessOptions, ITerminalProfile, ITerminalsLayoutInfo, ProcessPropertyType, TerminalIcon, TerminalIpcChannels, TerminalSettingId, TitleEventSource } from 'vs/platform/terminal/common/terminal'; import { registerTerminalPlatformConfiguration } from 'vs/platform/terminal/common/terminalPlatformConfiguration'; import { IGetTerminalLayoutInfoArgs, IProcessDetails, ISetTerminalLayoutInfoArgs } from 'vs/platform/terminal/common/terminalProcess'; import { IPtyHostConnection, IPtyHostStarter } from 'vs/platform/terminal/node/ptyHost'; @@ -29,7 +29,7 @@ enum Constants { * This service implements IPtyService by launching a pty host process, forwarding messages to and * from the pty host process and manages the connection. */ -export class PtyHostService extends Disposable implements IPtyService { +export class PtyHostService extends Disposable implements IPtyHostService { declare readonly _serviceBrand: undefined; private __connection?: IPtyHostConnection; @@ -230,6 +230,9 @@ export class PtyHostService extends Disposable implements IPtyService { detachFromProcess(id: number, forcePersist?: boolean): Promise { return this._proxy.detachFromProcess(id, forcePersist); } + shutdownAll(): Promise { + return this._proxy.shutdownAll(); + } listProcesses(): Promise { return this._proxy.listProcesses(); } @@ -354,7 +357,7 @@ export class PtyHostService extends Disposable implements IPtyService { } private _disposePtyHost(): void { - this._proxy.shutdownAll?.(); + this._proxy.shutdownAll(); this._connection.store.dispose(); } diff --git a/src/vs/platform/terminal/node/ptyService.ts b/src/vs/platform/terminal/node/ptyService.ts index 89a37150b5a..564b87612f3 100644 --- a/src/vs/platform/terminal/node/ptyService.ts +++ b/src/vs/platform/terminal/node/ptyService.ts @@ -12,7 +12,7 @@ import { URI } from 'vs/base/common/uri'; import { getSystemShell } from 'vs/base/node/shell'; import { ILogService, LogLevel } from 'vs/platform/log/common/log'; import { RequestStore } from 'vs/platform/terminal/common/requestStore'; -import { IProcessDataEvent, IProcessReadyEvent, IPtyService, IRawTerminalInstanceLayoutInfo, IReconnectConstants, IRequestResolveVariablesEvent, IShellLaunchConfig, ITerminalInstanceLayoutInfoById, ITerminalLaunchError, ITerminalsLayoutInfo, ITerminalTabLayoutInfoById, TerminalIcon, IProcessProperty, TitleEventSource, ProcessPropertyType, IProcessPropertyMap, IFixedTerminalDimensions, IPersistentTerminalProcessLaunchConfig, ICrossVersionSerializedTerminalState, ISerializedTerminalState, ITerminalProcessOptions } from 'vs/platform/terminal/common/terminal'; +import { IProcessDataEvent, IProcessReadyEvent, IPtyService, IRawTerminalInstanceLayoutInfo, IReconnectConstants, IShellLaunchConfig, ITerminalInstanceLayoutInfoById, ITerminalLaunchError, ITerminalsLayoutInfo, ITerminalTabLayoutInfoById, TerminalIcon, IProcessProperty, TitleEventSource, ProcessPropertyType, IProcessPropertyMap, IFixedTerminalDimensions, IPersistentTerminalProcessLaunchConfig, ICrossVersionSerializedTerminalState, ISerializedTerminalState, ITerminalProcessOptions } from 'vs/platform/terminal/common/terminal'; import { TerminalDataBufferer } from 'vs/platform/terminal/common/terminalDataBuffering'; import { escapeNonWindowsPath } from 'vs/platform/terminal/common/terminalEnvironment'; import { Terminal as XtermTerminal } from 'xterm-headless'; @@ -136,12 +136,6 @@ export class PtyService extends Disposable implements IPtyService { ignoreProcessNames.push(...names); } - onPtyHostExit?: Event | undefined; - onPtyHostStart?: Event | undefined; - onPtyHostUnresponsive?: Event | undefined; - onPtyHostResponsive?: Event | undefined; - onPtyHostRequestResolveVariables?: Event | undefined; - @traceRpc async requestDetachInstance(workspaceId: string, instanceId: number): Promise { return this._detachInstanceRequestStore.createRequest({ workspaceId, instanceId }); diff --git a/src/vs/server/node/remoteTerminalChannel.ts b/src/vs/server/node/remoteTerminalChannel.ts index 426cb7f5a6f..8d396c86021 100644 --- a/src/vs/server/node/remoteTerminalChannel.ts +++ b/src/vs/server/node/remoteTerminalChannel.ts @@ -14,7 +14,7 @@ import { IURITransformer } from 'vs/base/common/uriIpc'; import { IServerChannel } from 'vs/base/parts/ipc/common/ipc'; import { createRandomIPCHandle } from 'vs/base/parts/ipc/node/ipc.net'; import { RemoteAgentConnectionContext } from 'vs/platform/remote/common/remoteAgentEnvironment'; -import { IPtyService, IShellLaunchConfig, ITerminalProfile } from 'vs/platform/terminal/common/terminal'; +import { IPtyHostService, IShellLaunchConfig, ITerminalProfile } from 'vs/platform/terminal/common/terminal'; import { IGetTerminalLayoutInfoArgs, ISetTerminalLayoutInfoArgs } from 'vs/platform/terminal/common/terminalProcess'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { createURITransformer } from 'vs/workbench/api/node/uriTransformer'; @@ -97,7 +97,7 @@ export class RemoteTerminalChannel extends Disposable implements IServerChannel< constructor( private readonly _environmentService: IServerEnvironmentService, private readonly _logService: ILogService, - private readonly _ptyService: IPtyService, + private readonly _ptyHostService: IPtyHostService, private readonly _productService: IProductService, private readonly _extensionManagementService: IExtensionManagementService, private readonly _configurationService: IConfigurationService @@ -107,52 +107,52 @@ export class RemoteTerminalChannel extends Disposable implements IServerChannel< async call(ctx: RemoteAgentConnectionContext, command: string, args?: any): Promise { switch (command) { - case '$restartPtyHost': return this._ptyService.restartPtyHost?.apply(this._ptyService, args); + case '$restartPtyHost': return this._ptyHostService.restartPtyHost?.apply(this._ptyHostService, args); case '$createProcess': { const uriTransformer = createURITransformer(ctx.remoteAuthority); return this._createProcess(uriTransformer, args); } - case '$attachToProcess': return this._ptyService.attachToProcess.apply(this._ptyService, args); - case '$detachFromProcess': return this._ptyService.detachFromProcess.apply(this._ptyService, args); + case '$attachToProcess': return this._ptyHostService.attachToProcess.apply(this._ptyHostService, args); + case '$detachFromProcess': return this._ptyHostService.detachFromProcess.apply(this._ptyHostService, args); - case '$listProcesses': return this._ptyService.listProcesses.apply(this._ptyService, args); - case '$getPerformanceMarks': return this._ptyService.getPerformanceMarks.apply(this._ptyService, args); - case '$orphanQuestionReply': return this._ptyService.orphanQuestionReply.apply(this._ptyService, args); - case '$acceptPtyHostResolvedVariables': return this._ptyService.acceptPtyHostResolvedVariables?.apply(this._ptyService, args); + case '$listProcesses': return this._ptyHostService.listProcesses.apply(this._ptyHostService, args); + case '$getPerformanceMarks': return this._ptyHostService.getPerformanceMarks.apply(this._ptyHostService, args); + case '$orphanQuestionReply': return this._ptyHostService.orphanQuestionReply.apply(this._ptyHostService, args); + case '$acceptPtyHostResolvedVariables': return this._ptyHostService.acceptPtyHostResolvedVariables?.apply(this._ptyHostService, args); - case '$start': return this._ptyService.start.apply(this._ptyService, args); - case '$input': return this._ptyService.input.apply(this._ptyService, args); - case '$acknowledgeDataEvent': return this._ptyService.acknowledgeDataEvent.apply(this._ptyService, args); - case '$shutdown': return this._ptyService.shutdown.apply(this._ptyService, args); - case '$resize': return this._ptyService.resize.apply(this._ptyService, args); - case '$clearBuffer': return this._ptyService.clearBuffer.apply(this._ptyService, args); - case '$getInitialCwd': return this._ptyService.getInitialCwd.apply(this._ptyService, args); - case '$getCwd': return this._ptyService.getCwd.apply(this._ptyService, args); + case '$start': return this._ptyHostService.start.apply(this._ptyHostService, args); + case '$input': return this._ptyHostService.input.apply(this._ptyHostService, args); + case '$acknowledgeDataEvent': return this._ptyHostService.acknowledgeDataEvent.apply(this._ptyHostService, args); + case '$shutdown': return this._ptyHostService.shutdown.apply(this._ptyHostService, args); + case '$resize': return this._ptyHostService.resize.apply(this._ptyHostService, args); + case '$clearBuffer': return this._ptyHostService.clearBuffer.apply(this._ptyHostService, args); + case '$getInitialCwd': return this._ptyHostService.getInitialCwd.apply(this._ptyHostService, args); + case '$getCwd': return this._ptyHostService.getCwd.apply(this._ptyHostService, args); - case '$processBinary': return this._ptyService.processBinary.apply(this._ptyService, args); + case '$processBinary': return this._ptyHostService.processBinary.apply(this._ptyHostService, args); case '$sendCommandResult': return this._sendCommandResult(args[0], args[1], args[2]); - case '$installAutoReply': return this._ptyService.installAutoReply.apply(this._ptyService, args); - case '$uninstallAllAutoReplies': return this._ptyService.uninstallAllAutoReplies.apply(this._ptyService, args); + case '$installAutoReply': return this._ptyHostService.installAutoReply.apply(this._ptyHostService, args); + case '$uninstallAllAutoReplies': return this._ptyHostService.uninstallAllAutoReplies.apply(this._ptyHostService, args); case '$getDefaultSystemShell': return this._getDefaultSystemShell.apply(this, args); case '$getProfiles': return this._getProfiles.apply(this, args); case '$getEnvironment': return this._getEnvironment(); case '$getWslPath': return this._getWslPath(args[0], args[1]); - case '$getTerminalLayoutInfo': return this._ptyService.getTerminalLayoutInfo(args); - case '$setTerminalLayoutInfo': return this._ptyService.setTerminalLayoutInfo(args); - case '$serializeTerminalState': return this._ptyService.serializeTerminalState.apply(this._ptyService, args); - case '$reviveTerminalProcesses': return this._ptyService.reviveTerminalProcesses.apply(this._ptyService, args); - case '$getRevivedPtyNewId': return this._ptyService.getRevivedPtyNewId.apply(this._ptyService, args); - case '$setUnicodeVersion': return this._ptyService.setUnicodeVersion.apply(this._ptyService, args); + case '$getTerminalLayoutInfo': return this._ptyHostService.getTerminalLayoutInfo(args); + case '$setTerminalLayoutInfo': return this._ptyHostService.setTerminalLayoutInfo(args); + case '$serializeTerminalState': return this._ptyHostService.serializeTerminalState.apply(this._ptyHostService, args); + case '$reviveTerminalProcesses': return this._ptyHostService.reviveTerminalProcesses.apply(this._ptyHostService, args); + case '$getRevivedPtyNewId': return this._ptyHostService.getRevivedPtyNewId.apply(this._ptyHostService, args); + case '$setUnicodeVersion': return this._ptyHostService.setUnicodeVersion.apply(this._ptyHostService, args); case '$reduceConnectionGraceTime': return this._reduceConnectionGraceTime(); - case '$updateIcon': return this._ptyService.updateIcon.apply(this._ptyService, args); - case '$updateTitle': return this._ptyService.updateTitle.apply(this._ptyService, args); - case '$updateProperty': return this._ptyService.updateProperty.apply(this._ptyService, args); - case '$refreshProperty': return this._ptyService.refreshProperty.apply(this._ptyService, args); - case '$requestDetachInstance': return this._ptyService.requestDetachInstance(args[0], args[1]); - case '$acceptDetachedInstance': return this._ptyService.acceptDetachInstanceReply(args[0], args[1]); - case '$freePortKillProcess': return this._ptyService.freePortKillProcess?.apply(this._ptyService, args); + case '$updateIcon': return this._ptyHostService.updateIcon.apply(this._ptyHostService, args); + case '$updateTitle': return this._ptyHostService.updateTitle.apply(this._ptyHostService, args); + case '$updateProperty': return this._ptyHostService.updateProperty.apply(this._ptyHostService, args); + case '$refreshProperty': return this._ptyHostService.refreshProperty.apply(this._ptyHostService, args); + case '$requestDetachInstance': return this._ptyHostService.requestDetachInstance(args[0], args[1]); + case '$acceptDetachedInstance': return this._ptyHostService.acceptDetachInstanceReply(args[0], args[1]); + case '$freePortKillProcess': return this._ptyHostService.freePortKillProcess?.apply(this._ptyHostService, args); } throw new Error(`IPC Command ${command} not found`); @@ -160,19 +160,19 @@ export class RemoteTerminalChannel extends Disposable implements IServerChannel< listen(_: any, event: string, arg: any): Event { switch (event) { - case '$onPtyHostExitEvent': return this._ptyService.onPtyHostExit || Event.None; - case '$onPtyHostStartEvent': return this._ptyService.onPtyHostStart || Event.None; - case '$onPtyHostUnresponsiveEvent': return this._ptyService.onPtyHostUnresponsive || Event.None; - case '$onPtyHostResponsiveEvent': return this._ptyService.onPtyHostResponsive || Event.None; - case '$onPtyHostRequestResolveVariablesEvent': return this._ptyService.onPtyHostRequestResolveVariables || Event.None; - case '$onProcessDataEvent': return this._ptyService.onProcessData; - case '$onProcessReadyEvent': return this._ptyService.onProcessReady; - case '$onProcessExitEvent': return this._ptyService.onProcessExit; - case '$onProcessReplayEvent': return this._ptyService.onProcessReplay; - case '$onProcessOrphanQuestion': return this._ptyService.onProcessOrphanQuestion; + case '$onPtyHostExitEvent': return this._ptyHostService.onPtyHostExit || Event.None; + case '$onPtyHostStartEvent': return this._ptyHostService.onPtyHostStart || Event.None; + case '$onPtyHostUnresponsiveEvent': return this._ptyHostService.onPtyHostUnresponsive || Event.None; + case '$onPtyHostResponsiveEvent': return this._ptyHostService.onPtyHostResponsive || Event.None; + case '$onPtyHostRequestResolveVariablesEvent': return this._ptyHostService.onPtyHostRequestResolveVariables || Event.None; + case '$onProcessDataEvent': return this._ptyHostService.onProcessData; + case '$onProcessReadyEvent': return this._ptyHostService.onProcessReady; + case '$onProcessExitEvent': return this._ptyHostService.onProcessExit; + case '$onProcessReplayEvent': return this._ptyHostService.onProcessReplay; + case '$onProcessOrphanQuestion': return this._ptyHostService.onProcessOrphanQuestion; case '$onExecuteCommand': return this.onExecuteCommand; - case '$onDidRequestDetach': return this._ptyService.onDidRequestDetach || Event.None; - case '$onDidChangeProperty': return this._ptyService.onDidChangeProperty; + case '$onDidRequestDetach': return this._ptyHostService.onDidRequestDetach || Event.None; + case '$onDidChangeProperty': return this._ptyHostService.onDidChangeProperty; default: break; } @@ -251,12 +251,12 @@ export class RemoteTerminalChannel extends Disposable implements IServerChannel< const ipcHandlePath = createRandomIPCHandle(); env.VSCODE_IPC_HOOK_CLI = ipcHandlePath; - const persistentProcessId = await this._ptyService.createProcess(shellLaunchConfig, initialCwd, args.cols, args.rows, args.unicodeVersion, env, baseEnv, args.options, args.shouldPersistTerminal, args.workspaceId, args.workspaceName); + const persistentProcessId = await this._ptyHostService.createProcess(shellLaunchConfig, initialCwd, args.cols, args.rows, args.unicodeVersion, env, baseEnv, args.options, args.shouldPersistTerminal, args.workspaceId, args.workspaceName); const commandsExecuter: ICommandsExecuter = { executeCommand: (id: string, ...args: any[]): Promise => this._executeCommand(persistentProcessId, id, args, uriTransformer) }; const cliServer = new CLIServerBase(commandsExecuter, this._logService, ipcHandlePath); - this._ptyService.onProcessExit(e => e.id === persistentProcessId && cliServer.dispose()); + this._ptyHostService.onProcessExit(e => e.id === persistentProcessId && cliServer.dispose()); return { persistentTerminalId: persistentProcessId, @@ -316,11 +316,11 @@ export class RemoteTerminalChannel extends Disposable implements IServerChannel< } private _getDefaultSystemShell(osOverride?: platform.OperatingSystem): Promise { - return this._ptyService.getDefaultSystemShell(osOverride); + return this._ptyHostService.getDefaultSystemShell(osOverride); } private async _getProfiles(workspaceId: string, profiles: unknown, defaultProfile: unknown, includeDetectedProfiles?: boolean): Promise { - return this._ptyService.getProfiles?.(workspaceId, profiles, defaultProfile, includeDetectedProfiles) || []; + return this._ptyHostService.getProfiles?.(workspaceId, profiles, defaultProfile, includeDetectedProfiles) || []; } private _getEnvironment(): platform.IProcessEnvironment { @@ -328,11 +328,11 @@ export class RemoteTerminalChannel extends Disposable implements IServerChannel< } private _getWslPath(original: string, direction: 'unix-to-win' | 'win-to-unix'): Promise { - return this._ptyService.getWslPath(original, direction); + return this._ptyHostService.getWslPath(original, direction); } private _reduceConnectionGraceTime(): Promise { - return this._ptyService.reduceConnectionGraceTime(); + return this._ptyHostService.reduceConnectionGraceTime(); } } diff --git a/src/vs/server/node/serverServices.ts b/src/vs/server/node/serverServices.ts index cc788df0163..af1425f61cb 100644 --- a/src/vs/server/node/serverServices.ts +++ b/src/vs/server/node/serverServices.ts @@ -48,7 +48,7 @@ import { ITelemetryService, TelemetryLevel } from 'vs/platform/telemetry/common/ import { ITelemetryServiceConfig } from 'vs/platform/telemetry/common/telemetryService'; import { getPiiPathsFromEnvironment, isInternalTelemetry, ITelemetryAppender, NullAppender, supportsTelemetry } from 'vs/platform/telemetry/common/telemetryUtils'; import ErrorTelemetry from 'vs/platform/telemetry/node/errorTelemetry'; -import { IPtyService, TerminalSettingId } from 'vs/platform/terminal/common/terminal'; +import { IPtyService, IPtyHostService, TerminalSettingId } from 'vs/platform/terminal/common/terminal'; import { PtyHostService } from 'vs/platform/terminal/node/ptyHostService'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; import { UriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentityService'; @@ -197,8 +197,9 @@ export async function setupServerServices(connectionToken: ServerConnectionToken scrollback: configurationService.getValue(TerminalSettingId.PersistentSessionScrollback) ?? 100 } ); - const ptyService = instantiationService.createInstance(PtyHostService, ptyHostStarter); - services.set(IPtyService, ptyService); + const ptyHostService = instantiationService.createInstance(PtyHostService, ptyHostStarter); + services.set(IPtyService, ptyHostService); + services.set(IPtyHostService, ptyHostService); services.set(ICredentialsMainService, new SyncDescriptor(CredentialsWebMainService)); @@ -213,7 +214,7 @@ export async function setupServerServices(connectionToken: ServerConnectionToken const telemetryChannel = new ServerTelemetryChannel(accessor.get(IServerTelemetryService), oneDsAppender); socketServer.registerChannel('telemetry', telemetryChannel); - socketServer.registerChannel(REMOTE_TERMINAL_CHANNEL_NAME, new RemoteTerminalChannel(environmentService, logService, ptyService, productService, extensionManagementService, configurationService)); + socketServer.registerChannel(REMOTE_TERMINAL_CHANNEL_NAME, new RemoteTerminalChannel(environmentService, logService, ptyHostService, productService, extensionManagementService, configurationService)); const remoteExtensionsScanner = new RemoteExtensionsScannerService(instantiationService.createInstance(ExtensionManagementCLI, logService), environmentService, userDataProfilesService, extensionsScannerService, logService, extensionGalleryService, languagePackService); socketServer.registerChannel(RemoteExtensionsScannerChannelName, new RemoteExtensionsScannerChannel(remoteExtensionsScanner, (ctx: RemoteAgentConnectionContext) => getUriTransformer(ctx.remoteAuthority))); diff --git a/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts b/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts index ac4c6650e8b..4f976936a55 100644 --- a/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts +++ b/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts @@ -258,8 +258,7 @@ class LocalTerminalBackend extends BaseTerminalBackend implements ITerminalBacke } async getProfiles(profiles: unknown, defaultProfile: unknown, includeDetectedProfiles?: boolean) { - // TODO: Differentiate interfaces of direct to pty host and pty host service (or just move them all to pty host) - return this._proxy.getProfiles?.(this._workspaceContextService.getWorkspace().id, profiles, defaultProfile, includeDetectedProfiles) || []; + return this._localPtyService.getProfiles(this._workspaceContextService.getWorkspace().id, profiles, defaultProfile, includeDetectedProfiles) || []; } @memoize From 074121ee30245e06e8b46d20a770285aa0793cde Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 6 Jul 2023 11:42:22 -0700 Subject: [PATCH 221/926] Remove now unneeded optional checks --- src/vs/server/node/remoteTerminalChannel.ts | 8 +- .../terminal/browser/baseTerminalBackend.ts | 118 ++++++++---------- 2 files changed, 58 insertions(+), 68 deletions(-) diff --git a/src/vs/server/node/remoteTerminalChannel.ts b/src/vs/server/node/remoteTerminalChannel.ts index 8d396c86021..494e2c35f86 100644 --- a/src/vs/server/node/remoteTerminalChannel.ts +++ b/src/vs/server/node/remoteTerminalChannel.ts @@ -107,7 +107,7 @@ export class RemoteTerminalChannel extends Disposable implements IServerChannel< async call(ctx: RemoteAgentConnectionContext, command: string, args?: any): Promise { switch (command) { - case '$restartPtyHost': return this._ptyHostService.restartPtyHost?.apply(this._ptyHostService, args); + case '$restartPtyHost': return this._ptyHostService.restartPtyHost.apply(this._ptyHostService, args); case '$createProcess': { const uriTransformer = createURITransformer(ctx.remoteAuthority); @@ -119,7 +119,7 @@ export class RemoteTerminalChannel extends Disposable implements IServerChannel< case '$listProcesses': return this._ptyHostService.listProcesses.apply(this._ptyHostService, args); case '$getPerformanceMarks': return this._ptyHostService.getPerformanceMarks.apply(this._ptyHostService, args); case '$orphanQuestionReply': return this._ptyHostService.orphanQuestionReply.apply(this._ptyHostService, args); - case '$acceptPtyHostResolvedVariables': return this._ptyHostService.acceptPtyHostResolvedVariables?.apply(this._ptyHostService, args); + case '$acceptPtyHostResolvedVariables': return this._ptyHostService.acceptPtyHostResolvedVariables.apply(this._ptyHostService, args); case '$start': return this._ptyHostService.start.apply(this._ptyHostService, args); case '$input': return this._ptyHostService.input.apply(this._ptyHostService, args); @@ -152,7 +152,7 @@ export class RemoteTerminalChannel extends Disposable implements IServerChannel< case '$refreshProperty': return this._ptyHostService.refreshProperty.apply(this._ptyHostService, args); case '$requestDetachInstance': return this._ptyHostService.requestDetachInstance(args[0], args[1]); case '$acceptDetachedInstance': return this._ptyHostService.acceptDetachInstanceReply(args[0], args[1]); - case '$freePortKillProcess': return this._ptyHostService.freePortKillProcess?.apply(this._ptyHostService, args); + case '$freePortKillProcess': return this._ptyHostService.freePortKillProcess.apply(this._ptyHostService, args); } throw new Error(`IPC Command ${command} not found`); @@ -320,7 +320,7 @@ export class RemoteTerminalChannel extends Disposable implements IServerChannel< } private async _getProfiles(workspaceId: string, profiles: unknown, defaultProfile: unknown, includeDetectedProfiles?: boolean): Promise { - return this._ptyHostService.getProfiles?.(workspaceId, profiles, defaultProfile, includeDetectedProfiles) || []; + return this._ptyHostService.getProfiles(workspaceId, profiles, defaultProfile, includeDetectedProfiles) || []; } private _getEnvironment(): platform.IProcessEnvironment { diff --git a/src/vs/workbench/contrib/terminal/browser/baseTerminalBackend.ts b/src/vs/workbench/contrib/terminal/browser/baseTerminalBackend.ts index 3b5ab5a0fad..ae7e0824e2e 100644 --- a/src/vs/workbench/contrib/terminal/browser/baseTerminalBackend.ts +++ b/src/vs/workbench/contrib/terminal/browser/baseTerminalBackend.ts @@ -46,73 +46,63 @@ export abstract class BaseTerminalBackend extends Disposable { let hasStarted = false; // Attach pty host listeners - if (this._ptyHostController.onPtyHostExit) { - this._register(this._ptyHostController.onPtyHostExit(() => { - this._logService.error(`The terminal's pty host process exited, the connection to all terminal processes was lost`); - })); - } - if (this._ptyHostController.onPtyHostStart) { - this.onPtyHostConnected(() => hasStarted = true); - this._register(this._ptyHostController.onPtyHostStart(() => { - this._logService.debug(`The terminal's pty host process is starting`); - // Only fire the _restart_ event after it has started - if (hasStarted) { - this._logService.trace('IPtyHostController#onPtyHostRestart'); - this._onPtyHostRestart.fire(); - } - statusBarAccessor?.dispose(); - this._isPtyHostUnresponsive = false; - })); - } - if (this._ptyHostController.onPtyHostUnresponsive) { - this._register(this._ptyHostController.onPtyHostUnresponsive(() => { - statusBarAccessor?.dispose(); - if (!unresponsiveStatusBarEntry) { - unresponsiveStatusBarEntry = { - name: localize('ptyHostStatus', 'Pty Host Status'), - text: `$(debug-disconnect) ${localize('ptyHostStatus.short', 'Pty Host')}`, - tooltip: localize('nonResponsivePtyHost', "The connection to the terminal's pty host process is unresponsive, terminals may stop working. Click to manually restart the pty host."), - ariaLabel: localize('ptyHostStatus.ariaLabel', 'Pty Host is unresponsive'), - command: TerminalCommandId.RestartPtyHost, - backgroundColor: themeColorFromId(STATUS_BAR_WARNING_ITEM_BACKGROUND), - color: themeColorFromId(STATUS_BAR_WARNING_ITEM_FOREGROUND), - }; - } - statusBarAccessor = statusBarService.addEntry(unresponsiveStatusBarEntry, 'ptyHostStatus', StatusbarAlignment.LEFT); - this._isPtyHostUnresponsive = true; - this._onPtyHostUnresponsive.fire(); - })); - } - if (this._ptyHostController.onPtyHostResponsive) { - this._register(this._ptyHostController.onPtyHostResponsive(() => { - if (!this._isPtyHostUnresponsive) { - return; - } - this._logService.info('The pty host became responsive again'); - statusBarAccessor?.dispose(); - this._isPtyHostUnresponsive = false; - this._onPtyHostResponsive.fire(); - })); - } - if (this._ptyHostController.onPtyHostRequestResolveVariables) { - this._register(this._ptyHostController.onPtyHostRequestResolveVariables(async e => { - // Only answer requests for this workspace - if (e.workspaceId !== this._workspaceContextService.getWorkspace().id) { - return; - } - const activeWorkspaceRootUri = historyService.getLastActiveWorkspaceRoot(Schemas.file); - const lastActiveWorkspaceRoot = activeWorkspaceRootUri ? withNullAsUndefined(this._workspaceContextService.getWorkspaceFolder(activeWorkspaceRootUri)) : undefined; - const resolveCalls: Promise[] = e.originalText.map(t => { - return configurationResolverService.resolveAsync(lastActiveWorkspaceRoot, t); - }); - const result = await Promise.all(resolveCalls); - this._ptyHostController.acceptPtyHostResolvedVariables?.(e.requestId, result); - })); - } + this._register(this._ptyHostController.onPtyHostExit(() => { + this._logService.error(`The terminal's pty host process exited, the connection to all terminal processes was lost`); + })); + this.onPtyHostConnected(() => hasStarted = true); + this._register(this._ptyHostController.onPtyHostStart(() => { + this._logService.debug(`The terminal's pty host process is starting`); + // Only fire the _restart_ event after it has started + if (hasStarted) { + this._logService.trace('IPtyHostController#onPtyHostRestart'); + this._onPtyHostRestart.fire(); + } + statusBarAccessor?.dispose(); + this._isPtyHostUnresponsive = false; + })); + this._register(this._ptyHostController.onPtyHostUnresponsive(() => { + statusBarAccessor?.dispose(); + if (!unresponsiveStatusBarEntry) { + unresponsiveStatusBarEntry = { + name: localize('ptyHostStatus', 'Pty Host Status'), + text: `$(debug-disconnect) ${localize('ptyHostStatus.short', 'Pty Host')}`, + tooltip: localize('nonResponsivePtyHost', "The connection to the terminal's pty host process is unresponsive, terminals may stop working. Click to manually restart the pty host."), + ariaLabel: localize('ptyHostStatus.ariaLabel', 'Pty Host is unresponsive'), + command: TerminalCommandId.RestartPtyHost, + backgroundColor: themeColorFromId(STATUS_BAR_WARNING_ITEM_BACKGROUND), + color: themeColorFromId(STATUS_BAR_WARNING_ITEM_FOREGROUND), + }; + } + statusBarAccessor = statusBarService.addEntry(unresponsiveStatusBarEntry, 'ptyHostStatus', StatusbarAlignment.LEFT); + this._isPtyHostUnresponsive = true; + this._onPtyHostUnresponsive.fire(); + })); + this._register(this._ptyHostController.onPtyHostResponsive(() => { + if (!this._isPtyHostUnresponsive) { + return; + } + this._logService.info('The pty host became responsive again'); + statusBarAccessor?.dispose(); + this._isPtyHostUnresponsive = false; + this._onPtyHostResponsive.fire(); + })); + this._register(this._ptyHostController.onPtyHostRequestResolveVariables(async e => { + // Only answer requests for this workspace + if (e.workspaceId !== this._workspaceContextService.getWorkspace().id) { + return; + } + const activeWorkspaceRootUri = historyService.getLastActiveWorkspaceRoot(Schemas.file); + const lastActiveWorkspaceRoot = activeWorkspaceRootUri ? withNullAsUndefined(this._workspaceContextService.getWorkspaceFolder(activeWorkspaceRootUri)) : undefined; + const resolveCalls: Promise[] = e.originalText.map(t => { + return configurationResolverService.resolveAsync(lastActiveWorkspaceRoot, t); + }); + const result = await Promise.all(resolveCalls); + this._ptyHostController.acceptPtyHostResolvedVariables(e.requestId, result); + })); } restartPtyHost(): void { - this._ptyHostController.restartPtyHost?.(); + this._ptyHostController.restartPtyHost(); } protected _deserializeTerminalState(serializedState: string | undefined): ISerializedTerminalState[] | undefined { From 0d1bc0818c65c189aae2514d3122ae66e6c12b4f Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 6 Jul 2023 11:45:42 -0700 Subject: [PATCH 222/926] Clean up pty services --- src/vs/platform/terminal/common/terminal.ts | 10 ++++++++-- src/vs/server/node/serverServices.ts | 3 +-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/vs/platform/terminal/common/terminal.ts b/src/vs/platform/terminal/common/terminal.ts index 2f29ede8364..550e3dff813 100644 --- a/src/vs/platform/terminal/common/terminal.ts +++ b/src/vs/platform/terminal/common/terminal.ts @@ -264,7 +264,9 @@ export interface IFixedTerminalDimensions { rows?: number; } -export const IPtyService = createDecorator('ptyService'); +/** + * A service that communicates with a pty host. +*/ export interface IPtyService { readonly _serviceBrand: undefined; @@ -344,6 +346,7 @@ export interface IPtyService { // TODO: Make mandatory and remove impl from pty host service refreshIgnoreProcessNames?(names: string[]): Promise; } +export const IPtyService = createDecorator('ptyService'); export interface IPtyHostController { readonly onPtyHostExit: Event; @@ -357,7 +360,10 @@ export interface IPtyHostController { getProfiles(workspaceId: string, profiles: unknown, defaultProfile: unknown, includeDetectedProfiles?: boolean): Promise; } -export const IPtyHostService = createDecorator('ptyHostService'); +/** + * A service that communicates with a pty host controller (eg. main or server + * process) and is able to launch and forward requests to the pty host. +*/ export interface IPtyHostService extends IPtyService, IPtyHostController { } diff --git a/src/vs/server/node/serverServices.ts b/src/vs/server/node/serverServices.ts index af1425f61cb..945743bcd51 100644 --- a/src/vs/server/node/serverServices.ts +++ b/src/vs/server/node/serverServices.ts @@ -48,7 +48,7 @@ import { ITelemetryService, TelemetryLevel } from 'vs/platform/telemetry/common/ import { ITelemetryServiceConfig } from 'vs/platform/telemetry/common/telemetryService'; import { getPiiPathsFromEnvironment, isInternalTelemetry, ITelemetryAppender, NullAppender, supportsTelemetry } from 'vs/platform/telemetry/common/telemetryUtils'; import ErrorTelemetry from 'vs/platform/telemetry/node/errorTelemetry'; -import { IPtyService, IPtyHostService, TerminalSettingId } from 'vs/platform/terminal/common/terminal'; +import { IPtyService, TerminalSettingId } from 'vs/platform/terminal/common/terminal'; import { PtyHostService } from 'vs/platform/terminal/node/ptyHostService'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; import { UriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentityService'; @@ -199,7 +199,6 @@ export async function setupServerServices(connectionToken: ServerConnectionToken ); const ptyHostService = instantiationService.createInstance(PtyHostService, ptyHostStarter); services.set(IPtyService, ptyHostService); - services.set(IPtyHostService, ptyHostService); services.set(ICredentialsMainService, new SyncDescriptor(CredentialsWebMainService)); From ab08a18321c4d8aa08fb1ce4713cbed2e59cba4b Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 6 Jul 2023 12:12:56 -0700 Subject: [PATCH 223/926] Use const enum for all remote terminal requests Part of #187176 --- src/vs/server/node/remoteTerminalChannel.ts | 114 +++++++++--------- src/vs/server/node/serverServices.ts | 2 +- .../contrib/terminal/browser/remotePty.ts | 2 +- .../terminal/browser/remoteTerminalBackend.ts | 2 +- .../{ => remote}/remoteTerminalChannel.ts | 97 +++++++-------- .../terminal/common/remote/terminal.ts | 99 +++++++++++++++ 6 files changed, 207 insertions(+), 109 deletions(-) rename src/vs/workbench/contrib/terminal/common/{ => remote}/remoteTerminalChannel.ts (73%) create mode 100644 src/vs/workbench/contrib/terminal/common/remote/terminal.ts diff --git a/src/vs/server/node/remoteTerminalChannel.ts b/src/vs/server/node/remoteTerminalChannel.ts index 494e2c35f86..245aa816891 100644 --- a/src/vs/server/node/remoteTerminalChannel.ts +++ b/src/vs/server/node/remoteTerminalChannel.ts @@ -22,7 +22,7 @@ import { CLIServerBase, ICommandsExecuter } from 'vs/workbench/api/node/extHostC import { IEnvironmentVariableCollection } from 'vs/platform/terminal/common/environmentVariable'; import { MergedEnvironmentVariableCollection } from 'vs/platform/terminal/common/environmentVariableCollection'; import { deserializeEnvironmentDescriptionMap, deserializeEnvironmentVariableCollection } from 'vs/platform/terminal/common/environmentVariableShared'; -import { ICreateTerminalProcessArguments, ICreateTerminalProcessResult, IWorkspaceFolderData } from 'vs/workbench/contrib/terminal/common/remoteTerminalChannel'; +import { ICreateTerminalProcessArguments, ICreateTerminalProcessResult, IWorkspaceFolderData, RemoteTerminalChannelEvent, RemoteTerminalChannelRequest } from 'vs/workbench/contrib/terminal/common/remote/terminal'; import * as terminalEnvironment from 'vs/workbench/contrib/terminal/common/terminalEnvironment'; import { AbstractVariableResolverService } from 'vs/workbench/services/configurationResolver/common/variableResolver'; import { buildUserEnvironment } from 'vs/server/node/extensionHostConnection'; @@ -105,78 +105,76 @@ export class RemoteTerminalChannel extends Disposable implements IServerChannel< super(); } - async call(ctx: RemoteAgentConnectionContext, command: string, args?: any): Promise { + async call(ctx: RemoteAgentConnectionContext, command: RemoteTerminalChannelRequest, args?: any): Promise { switch (command) { - case '$restartPtyHost': return this._ptyHostService.restartPtyHost.apply(this._ptyHostService, args); + case RemoteTerminalChannelRequest.RestartPtyHost: return this._ptyHostService.restartPtyHost.apply(this._ptyHostService, args); - case '$createProcess': { + case RemoteTerminalChannelRequest.CreateProcess: { const uriTransformer = createURITransformer(ctx.remoteAuthority); return this._createProcess(uriTransformer, args); } - case '$attachToProcess': return this._ptyHostService.attachToProcess.apply(this._ptyHostService, args); - case '$detachFromProcess': return this._ptyHostService.detachFromProcess.apply(this._ptyHostService, args); + case RemoteTerminalChannelRequest.AttachToProcess: return this._ptyHostService.attachToProcess.apply(this._ptyHostService, args); + case RemoteTerminalChannelRequest.DetachFromProcess: return this._ptyHostService.detachFromProcess.apply(this._ptyHostService, args); - case '$listProcesses': return this._ptyHostService.listProcesses.apply(this._ptyHostService, args); - case '$getPerformanceMarks': return this._ptyHostService.getPerformanceMarks.apply(this._ptyHostService, args); - case '$orphanQuestionReply': return this._ptyHostService.orphanQuestionReply.apply(this._ptyHostService, args); - case '$acceptPtyHostResolvedVariables': return this._ptyHostService.acceptPtyHostResolvedVariables.apply(this._ptyHostService, args); + case RemoteTerminalChannelRequest.ListProcesses: return this._ptyHostService.listProcesses.apply(this._ptyHostService, args); + case RemoteTerminalChannelRequest.GetPerformanceMarks: return this._ptyHostService.getPerformanceMarks.apply(this._ptyHostService, args); + case RemoteTerminalChannelRequest.OrphanQuestionReply: return this._ptyHostService.orphanQuestionReply.apply(this._ptyHostService, args); + case RemoteTerminalChannelRequest.AcceptPtyHostResolvedVariables: return this._ptyHostService.acceptPtyHostResolvedVariables.apply(this._ptyHostService, args); - case '$start': return this._ptyHostService.start.apply(this._ptyHostService, args); - case '$input': return this._ptyHostService.input.apply(this._ptyHostService, args); - case '$acknowledgeDataEvent': return this._ptyHostService.acknowledgeDataEvent.apply(this._ptyHostService, args); - case '$shutdown': return this._ptyHostService.shutdown.apply(this._ptyHostService, args); - case '$resize': return this._ptyHostService.resize.apply(this._ptyHostService, args); - case '$clearBuffer': return this._ptyHostService.clearBuffer.apply(this._ptyHostService, args); - case '$getInitialCwd': return this._ptyHostService.getInitialCwd.apply(this._ptyHostService, args); - case '$getCwd': return this._ptyHostService.getCwd.apply(this._ptyHostService, args); + case RemoteTerminalChannelRequest.Start: return this._ptyHostService.start.apply(this._ptyHostService, args); + case RemoteTerminalChannelRequest.Input: return this._ptyHostService.input.apply(this._ptyHostService, args); + case RemoteTerminalChannelRequest.AcknowledgeDataEvent: return this._ptyHostService.acknowledgeDataEvent.apply(this._ptyHostService, args); + case RemoteTerminalChannelRequest.Shutdown: return this._ptyHostService.shutdown.apply(this._ptyHostService, args); + case RemoteTerminalChannelRequest.Resize: return this._ptyHostService.resize.apply(this._ptyHostService, args); + case RemoteTerminalChannelRequest.ClearBuffer: return this._ptyHostService.clearBuffer.apply(this._ptyHostService, args); + case RemoteTerminalChannelRequest.GetInitialCwd: return this._ptyHostService.getInitialCwd.apply(this._ptyHostService, args); + case RemoteTerminalChannelRequest.GetCwd: return this._ptyHostService.getCwd.apply(this._ptyHostService, args); - case '$processBinary': return this._ptyHostService.processBinary.apply(this._ptyHostService, args); + case RemoteTerminalChannelRequest.ProcessBinary: return this._ptyHostService.processBinary.apply(this._ptyHostService, args); - case '$sendCommandResult': return this._sendCommandResult(args[0], args[1], args[2]); - case '$installAutoReply': return this._ptyHostService.installAutoReply.apply(this._ptyHostService, args); - case '$uninstallAllAutoReplies': return this._ptyHostService.uninstallAllAutoReplies.apply(this._ptyHostService, args); - case '$getDefaultSystemShell': return this._getDefaultSystemShell.apply(this, args); - case '$getProfiles': return this._getProfiles.apply(this, args); - case '$getEnvironment': return this._getEnvironment(); - case '$getWslPath': return this._getWslPath(args[0], args[1]); - case '$getTerminalLayoutInfo': return this._ptyHostService.getTerminalLayoutInfo(args); - case '$setTerminalLayoutInfo': return this._ptyHostService.setTerminalLayoutInfo(args); - case '$serializeTerminalState': return this._ptyHostService.serializeTerminalState.apply(this._ptyHostService, args); - case '$reviveTerminalProcesses': return this._ptyHostService.reviveTerminalProcesses.apply(this._ptyHostService, args); - case '$getRevivedPtyNewId': return this._ptyHostService.getRevivedPtyNewId.apply(this._ptyHostService, args); - case '$setUnicodeVersion': return this._ptyHostService.setUnicodeVersion.apply(this._ptyHostService, args); - case '$reduceConnectionGraceTime': return this._reduceConnectionGraceTime(); - case '$updateIcon': return this._ptyHostService.updateIcon.apply(this._ptyHostService, args); - case '$updateTitle': return this._ptyHostService.updateTitle.apply(this._ptyHostService, args); - case '$updateProperty': return this._ptyHostService.updateProperty.apply(this._ptyHostService, args); - case '$refreshProperty': return this._ptyHostService.refreshProperty.apply(this._ptyHostService, args); - case '$requestDetachInstance': return this._ptyHostService.requestDetachInstance(args[0], args[1]); - case '$acceptDetachedInstance': return this._ptyHostService.acceptDetachInstanceReply(args[0], args[1]); - case '$freePortKillProcess': return this._ptyHostService.freePortKillProcess.apply(this._ptyHostService, args); + case RemoteTerminalChannelRequest.SendCommandResult: return this._sendCommandResult(args[0], args[1], args[2]); + case RemoteTerminalChannelRequest.InstallAutoReply: return this._ptyHostService.installAutoReply.apply(this._ptyHostService, args); + case RemoteTerminalChannelRequest.UninstallAllAutoReplies: return this._ptyHostService.uninstallAllAutoReplies.apply(this._ptyHostService, args); + case RemoteTerminalChannelRequest.GetDefaultSystemShell: return this._getDefaultSystemShell.apply(this, args); + case RemoteTerminalChannelRequest.GetProfiles: return this._getProfiles.apply(this, args); + case RemoteTerminalChannelRequest.GetEnvironment: return this._getEnvironment(); + case RemoteTerminalChannelRequest.GetWslPath: return this._getWslPath(args[0], args[1]); + case RemoteTerminalChannelRequest.GetTerminalLayoutInfo: return this._ptyHostService.getTerminalLayoutInfo(args); + case RemoteTerminalChannelRequest.SetTerminalLayoutInfo: return this._ptyHostService.setTerminalLayoutInfo(args); + case RemoteTerminalChannelRequest.SerializeTerminalState: return this._ptyHostService.serializeTerminalState.apply(this._ptyHostService, args); + case RemoteTerminalChannelRequest.ReviveTerminalProcesses: return this._ptyHostService.reviveTerminalProcesses.apply(this._ptyHostService, args); + case RemoteTerminalChannelRequest.GetRevivedPtyNewId: return this._ptyHostService.getRevivedPtyNewId.apply(this._ptyHostService, args); + case RemoteTerminalChannelRequest.SetUnicodeVersion: return this._ptyHostService.setUnicodeVersion.apply(this._ptyHostService, args); + case RemoteTerminalChannelRequest.ReduceConnectionGraceTime: return this._reduceConnectionGraceTime(); + case RemoteTerminalChannelRequest.UpdateIcon: return this._ptyHostService.updateIcon.apply(this._ptyHostService, args); + case RemoteTerminalChannelRequest.UpdateTitle: return this._ptyHostService.updateTitle.apply(this._ptyHostService, args); + case RemoteTerminalChannelRequest.UpdateProperty: return this._ptyHostService.updateProperty.apply(this._ptyHostService, args); + case RemoteTerminalChannelRequest.RefreshProperty: return this._ptyHostService.refreshProperty.apply(this._ptyHostService, args); + case RemoteTerminalChannelRequest.RequestDetachInstance: return this._ptyHostService.requestDetachInstance(args[0], args[1]); + case RemoteTerminalChannelRequest.AcceptDetachedInstance: return this._ptyHostService.acceptDetachInstanceReply(args[0], args[1]); + case RemoteTerminalChannelRequest.FreePortKillProcess: return this._ptyHostService.freePortKillProcess.apply(this._ptyHostService, args); + default: break; } - throw new Error(`IPC Command ${command} not found`); } - listen(_: any, event: string, arg: any): Event { + listen(_: any, event: RemoteTerminalChannelEvent, arg: any): Event { switch (event) { - case '$onPtyHostExitEvent': return this._ptyHostService.onPtyHostExit || Event.None; - case '$onPtyHostStartEvent': return this._ptyHostService.onPtyHostStart || Event.None; - case '$onPtyHostUnresponsiveEvent': return this._ptyHostService.onPtyHostUnresponsive || Event.None; - case '$onPtyHostResponsiveEvent': return this._ptyHostService.onPtyHostResponsive || Event.None; - case '$onPtyHostRequestResolveVariablesEvent': return this._ptyHostService.onPtyHostRequestResolveVariables || Event.None; - case '$onProcessDataEvent': return this._ptyHostService.onProcessData; - case '$onProcessReadyEvent': return this._ptyHostService.onProcessReady; - case '$onProcessExitEvent': return this._ptyHostService.onProcessExit; - case '$onProcessReplayEvent': return this._ptyHostService.onProcessReplay; - case '$onProcessOrphanQuestion': return this._ptyHostService.onProcessOrphanQuestion; - case '$onExecuteCommand': return this.onExecuteCommand; - case '$onDidRequestDetach': return this._ptyHostService.onDidRequestDetach || Event.None; - case '$onDidChangeProperty': return this._ptyHostService.onDidChangeProperty; - default: - break; + case RemoteTerminalChannelEvent.OnPtyHostExitEvent: return this._ptyHostService.onPtyHostExit || Event.None; + case RemoteTerminalChannelEvent.OnPtyHostStartEvent: return this._ptyHostService.onPtyHostStart || Event.None; + case RemoteTerminalChannelEvent.OnPtyHostUnresponsiveEvent: return this._ptyHostService.onPtyHostUnresponsive || Event.None; + case RemoteTerminalChannelEvent.OnPtyHostResponsiveEvent: return this._ptyHostService.onPtyHostResponsive || Event.None; + case RemoteTerminalChannelEvent.OnPtyHostRequestResolveVariablesEvent: return this._ptyHostService.onPtyHostRequestResolveVariables || Event.None; + case RemoteTerminalChannelEvent.OnProcessDataEvent: return this._ptyHostService.onProcessData; + case RemoteTerminalChannelEvent.OnProcessReadyEvent: return this._ptyHostService.onProcessReady; + case RemoteTerminalChannelEvent.OnProcessExitEvent: return this._ptyHostService.onProcessExit; + case RemoteTerminalChannelEvent.OnProcessReplayEvent: return this._ptyHostService.onProcessReplay; + case RemoteTerminalChannelEvent.OnProcessOrphanQuestion: return this._ptyHostService.onProcessOrphanQuestion; + case RemoteTerminalChannelEvent.OnExecuteCommand: return this.onExecuteCommand; + case RemoteTerminalChannelEvent.OnDidRequestDetach: return this._ptyHostService.onDidRequestDetach || Event.None; + case RemoteTerminalChannelEvent.OnDidChangeProperty: return this._ptyHostService.onDidChangeProperty; + default: break; } - throw new Error('Not supported'); } diff --git a/src/vs/server/node/serverServices.ts b/src/vs/server/node/serverServices.ts index 945743bcd51..910bbe0e5d1 100644 --- a/src/vs/server/node/serverServices.ts +++ b/src/vs/server/node/serverServices.ts @@ -60,7 +60,7 @@ import { RemoteTerminalChannel } from 'vs/server/node/remoteTerminalChannel'; import { createURITransformer } from 'vs/workbench/api/node/uriTransformer'; import { ServerConnectionToken } from 'vs/server/node/serverConnectionToken'; import { ServerEnvironmentService, ServerParsedArgs } from 'vs/server/node/serverEnvironmentService'; -import { REMOTE_TERMINAL_CHANNEL_NAME } from 'vs/workbench/contrib/terminal/common/remoteTerminalChannel'; +import { REMOTE_TERMINAL_CHANNEL_NAME } from 'vs/workbench/contrib/terminal/common/remote/remoteTerminalChannel'; import { REMOTE_FILE_SYSTEM_CHANNEL_NAME } from 'vs/workbench/services/remote/common/remoteFileSystemProviderClient'; import { ExtensionHostStatusService, IExtensionHostStatusService } from 'vs/server/node/extensionHostStatusService'; import { IExtensionsScannerService } from 'vs/platform/extensionManagement/common/extensionsScannerService'; diff --git a/src/vs/workbench/contrib/terminal/browser/remotePty.ts b/src/vs/workbench/contrib/terminal/browser/remotePty.ts index c1d0428d18d..112b0bb80d2 100644 --- a/src/vs/workbench/contrib/terminal/browser/remotePty.ts +++ b/src/vs/workbench/contrib/terminal/browser/remotePty.ts @@ -10,7 +10,7 @@ import { mark } from 'vs/base/common/performance'; import { URI } from 'vs/base/common/uri'; import { IPtyHostProcessReplayEvent, ISerializedCommandDetectionCapability } from 'vs/platform/terminal/common/capabilities/capabilities'; import { IProcessDataEvent, ITerminalChildProcess, ITerminalLaunchError, IProcessProperty, IProcessPropertyMap, ProcessPropertyType, IProcessReadyEvent, ITerminalLogService } from 'vs/platform/terminal/common/terminal'; -import { RemoteTerminalChannelClient } from 'vs/workbench/contrib/terminal/common/remoteTerminalChannel'; +import { RemoteTerminalChannelClient } from 'vs/workbench/contrib/terminal/common/remote/remoteTerminalChannel'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; export class RemotePty extends Disposable implements ITerminalChildProcess { diff --git a/src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts b/src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts index 7c79670a1f1..3490820c749 100644 --- a/src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts +++ b/src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts @@ -22,7 +22,7 @@ import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { BaseTerminalBackend } from 'vs/workbench/contrib/terminal/browser/baseTerminalBackend'; import { RemotePty } from 'vs/workbench/contrib/terminal/browser/remotePty'; import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; -import { RemoteTerminalChannelClient, REMOTE_TERMINAL_CHANNEL_NAME } from 'vs/workbench/contrib/terminal/common/remoteTerminalChannel'; +import { RemoteTerminalChannelClient, REMOTE_TERMINAL_CHANNEL_NAME } from 'vs/workbench/contrib/terminal/common/remote/remoteTerminalChannel'; import { ICompleteTerminalConfiguration, ITerminalConfiguration, TERMINAL_CONFIG_SECTION } from 'vs/workbench/contrib/terminal/common/terminal'; import { TerminalStorageKeys } from 'vs/workbench/contrib/terminal/common/terminalStorageKeys'; import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; diff --git a/src/vs/workbench/contrib/terminal/common/remoteTerminalChannel.ts b/src/vs/workbench/contrib/terminal/common/remote/remoteTerminalChannel.ts similarity index 73% rename from src/vs/workbench/contrib/terminal/common/remoteTerminalChannel.ts rename to src/vs/workbench/contrib/terminal/common/remote/remoteTerminalChannel.ts index 7bc8afac147..c66a1ec185a 100644 --- a/src/vs/workbench/contrib/terminal/common/remoteTerminalChannel.ts +++ b/src/vs/workbench/contrib/terminal/common/remote/remoteTerminalChannel.ts @@ -24,6 +24,7 @@ import { ICompleteTerminalConfiguration } from 'vs/workbench/contrib/terminal/co import { IPtyHostProcessReplayEvent } from 'vs/platform/terminal/common/capabilities/capabilities'; import { ISerializableEnvironmentDescriptionMap as ISerializableEnvironmentDescriptionMap, ISerializableEnvironmentVariableCollection } from 'vs/platform/terminal/common/environmentVariable'; import type * as performance from 'vs/base/common/performance'; +import { RemoteTerminalChannelEvent, RemoteTerminalChannelRequest } from 'vs/workbench/contrib/terminal/common/remote/terminal'; export const REMOTE_TERMINAL_CHANNEL_NAME = 'remoteterminal'; @@ -60,43 +61,43 @@ export interface ICreateTerminalProcessResult { export class RemoteTerminalChannelClient implements IPtyHostController { get onPtyHostExit(): Event { - return this._channel.listen('$onPtyHostExitEvent'); + return this._channel.listen(RemoteTerminalChannelEvent.OnPtyHostExitEvent); } get onPtyHostStart(): Event { - return this._channel.listen('$onPtyHostStartEvent'); + return this._channel.listen(RemoteTerminalChannelEvent.OnPtyHostStartEvent); } get onPtyHostUnresponsive(): Event { - return this._channel.listen('$onPtyHostUnresponsiveEvent'); + return this._channel.listen(RemoteTerminalChannelEvent.OnPtyHostUnresponsiveEvent); } get onPtyHostResponsive(): Event { - return this._channel.listen('$onPtyHostResponsiveEvent'); + return this._channel.listen(RemoteTerminalChannelEvent.OnPtyHostResponsiveEvent); } get onPtyHostRequestResolveVariables(): Event { - return this._channel.listen('$onPtyHostRequestResolveVariablesEvent'); + return this._channel.listen(RemoteTerminalChannelEvent.OnPtyHostRequestResolveVariablesEvent); } get onProcessData(): Event<{ id: number; event: IProcessDataEvent | string }> { - return this._channel.listen<{ id: number; event: IProcessDataEvent | string }>('$onProcessDataEvent'); + return this._channel.listen<{ id: number; event: IProcessDataEvent | string }>(RemoteTerminalChannelEvent.OnProcessDataEvent); } get onProcessExit(): Event<{ id: number; event: number | undefined }> { - return this._channel.listen<{ id: number; event: number | undefined }>('$onProcessExitEvent'); + return this._channel.listen<{ id: number; event: number | undefined }>(RemoteTerminalChannelEvent.OnProcessExitEvent); } get onProcessReady(): Event<{ id: number; event: IProcessReadyEvent }> { - return this._channel.listen<{ id: number; event: IProcessReadyEvent }>('$onProcessReadyEvent'); + return this._channel.listen<{ id: number; event: IProcessReadyEvent }>(RemoteTerminalChannelEvent.OnProcessReadyEvent); } get onProcessReplay(): Event<{ id: number; event: IPtyHostProcessReplayEvent }> { - return this._channel.listen<{ id: number; event: IPtyHostProcessReplayEvent }>('$onProcessReplayEvent'); + return this._channel.listen<{ id: number; event: IPtyHostProcessReplayEvent }>(RemoteTerminalChannelEvent.OnProcessReplayEvent); } get onProcessOrphanQuestion(): Event<{ id: number }> { - return this._channel.listen<{ id: number }>('$onProcessOrphanQuestion'); + return this._channel.listen<{ id: number }>(RemoteTerminalChannelEvent.OnProcessOrphanQuestion); } get onExecuteCommand(): Event<{ reqId: number; persistentProcessId: number; commandId: string; commandArgs: any[] }> { - return this._channel.listen<{ reqId: number; persistentProcessId: number; commandId: string; commandArgs: any[] }>('$onExecuteCommand'); + return this._channel.listen<{ reqId: number; persistentProcessId: number; commandId: string; commandArgs: any[] }>(RemoteTerminalChannelEvent.OnExecuteCommand); } get onDidRequestDetach(): Event<{ requestId: number; workspaceId: string; instanceId: number }> { - return this._channel.listen<{ requestId: number; workspaceId: string; instanceId: number }>('$onDidRequestDetach'); + return this._channel.listen<{ requestId: number; workspaceId: string; instanceId: number }>(RemoteTerminalChannelEvent.OnDidRequestDetach); } get onDidChangeProperty(): Event<{ id: number; property: IProcessProperty }> { - return this._channel.listen<{ id: number; property: IProcessProperty }>('$onDidChangeProperty'); + return this._channel.listen<{ id: number; property: IProcessProperty }>(RemoteTerminalChannelEvent.OnDidChangeProperty); } constructor( @@ -113,7 +114,7 @@ export class RemoteTerminalChannelClient implements IPtyHostController { ) { } restartPtyHost(): Promise { - return this._channel.call('$restartPtyHost', []); + return this._channel.call(RemoteTerminalChannelRequest.RestartPtyHost, []); } async createProcess( @@ -188,87 +189,87 @@ export class RemoteTerminalChannelClient implements IPtyHostController { } requestDetachInstance(workspaceId: string, instanceId: number): Promise { - return this._channel.call('$requestDetachInstance', [workspaceId, instanceId]); + return this._channel.call(RemoteTerminalChannelRequest.RequestDetachInstance, [workspaceId, instanceId]); } acceptDetachInstanceReply(requestId: number, persistentProcessId: number): Promise { - return this._channel.call('$acceptDetachInstanceReply', [requestId, persistentProcessId]); + return this._channel.call(RemoteTerminalChannelRequest.AcceptDetachInstanceReply, [requestId, persistentProcessId]); } attachToProcess(id: number): Promise { - return this._channel.call('$attachToProcess', [id]); + return this._channel.call(RemoteTerminalChannelRequest.AttachToProcess, [id]); } detachFromProcess(id: number, forcePersist?: boolean): Promise { - return this._channel.call('$detachFromProcess', [id, forcePersist]); + return this._channel.call(RemoteTerminalChannelRequest.DetachFromProcess, [id, forcePersist]); } listProcesses(): Promise { - return this._channel.call('$listProcesses'); + return this._channel.call(RemoteTerminalChannelRequest.ListProcesses); } getPerformanceMarks(): Promise { - return this._channel.call('$getPerformanceMarks'); + return this._channel.call(RemoteTerminalChannelRequest.GetPerformanceMarks); } reduceConnectionGraceTime(): Promise { - return this._channel.call('$reduceConnectionGraceTime'); + return this._channel.call(RemoteTerminalChannelRequest.ReduceConnectionGraceTime); } processBinary(id: number, data: string): Promise { - return this._channel.call('$processBinary', [id, data]); + return this._channel.call(RemoteTerminalChannelRequest.ProcessBinary, [id, data]); } start(id: number): Promise { - return this._channel.call('$start', [id]); + return this._channel.call(RemoteTerminalChannelRequest.Start, [id]); } input(id: number, data: string): Promise { - return this._channel.call('$input', [id, data]); + return this._channel.call(RemoteTerminalChannelRequest.Input, [id, data]); } acknowledgeDataEvent(id: number, charCount: number): Promise { - return this._channel.call('$acknowledgeDataEvent', [id, charCount]); + return this._channel.call(RemoteTerminalChannelRequest.AcknowledgeDataEvent, [id, charCount]); } setUnicodeVersion(id: number, version: '6' | '11'): Promise { - return this._channel.call('$setUnicodeVersion', [id, version]); + return this._channel.call(RemoteTerminalChannelRequest.SetUnicodeVersion, [id, version]); } shutdown(id: number, immediate: boolean): Promise { - return this._channel.call('$shutdown', [id, immediate]); + return this._channel.call(RemoteTerminalChannelRequest.Shutdown, [id, immediate]); } resize(id: number, cols: number, rows: number): Promise { - return this._channel.call('$resize', [id, cols, rows]); + return this._channel.call(RemoteTerminalChannelRequest.Resize, [id, cols, rows]); } clearBuffer(id: number): Promise { - return this._channel.call('$clearBuffer', [id]); + return this._channel.call(RemoteTerminalChannelRequest.ClearBuffer, [id]); } getInitialCwd(id: number): Promise { - return this._channel.call('$getInitialCwd', [id]); + return this._channel.call(RemoteTerminalChannelRequest.GetInitialCwd, [id]); } getCwd(id: number): Promise { - return this._channel.call('$getCwd', [id]); + return this._channel.call(RemoteTerminalChannelRequest.GetCwd, [id]); } orphanQuestionReply(id: number): Promise { - return this._channel.call('$orphanQuestionReply', [id]); + return this._channel.call(RemoteTerminalChannelRequest.OrphanQuestionReply, [id]); } sendCommandResult(reqId: number, isError: boolean, payload: any): Promise { - return this._channel.call('$sendCommandResult', [reqId, isError, payload]); + return this._channel.call(RemoteTerminalChannelRequest.SendCommandResult, [reqId, isError, payload]); } freePortKillProcess(port: string): Promise<{ port: string; processId: string }> { - return this._channel.call('$freePortKillProcess', [port]); + return this._channel.call(RemoteTerminalChannelRequest.FreePortKillProcess, [port]); } installAutoReply(match: string, reply: string): Promise { - return this._channel.call('$installAutoReply', [match, reply]); + return this._channel.call(RemoteTerminalChannelRequest.InstallAutoReply, [match, reply]); } uninstallAllAutoReplies(): Promise { - return this._channel.call('$uninstallAllAutoReplies', []); + return this._channel.call(RemoteTerminalChannelRequest.UninstallAllAutoReplies, []); } getDefaultSystemShell(osOverride?: OperatingSystem): Promise { - return this._channel.call('$getDefaultSystemShell', [osOverride]); + return this._channel.call(RemoteTerminalChannelRequest.GetDefaultSystemShell, [osOverride]); } getProfiles(profiles: unknown, defaultProfile: unknown, includeDetectedProfiles?: boolean): Promise { - return this._channel.call('$getProfiles', [this._workspaceContextService.getWorkspace().id, profiles, defaultProfile, includeDetectedProfiles]); + return this._channel.call(RemoteTerminalChannelRequest.GetProfiles, [this._workspaceContextService.getWorkspace().id, profiles, defaultProfile, includeDetectedProfiles]); } acceptPtyHostResolvedVariables(requestId: number, resolved: string[]): Promise { - return this._channel.call('$acceptPtyHostResolvedVariables', [requestId, resolved]); + return this._channel.call(RemoteTerminalChannelRequest.AcceptPtyHostResolvedVariables, [requestId, resolved]); } getEnvironment(): Promise { - return this._channel.call('$getEnvironment'); + return this._channel.call(RemoteTerminalChannelRequest.GetEnvironment); } getWslPath(original: string, direction: 'unix-to-win' | 'win-to-unix'): Promise { - return this._channel.call('$getWslPath', [original, direction]); + return this._channel.call(RemoteTerminalChannelRequest.GetWslPath, [original, direction]); } setTerminalLayoutInfo(layout?: ITerminalsLayoutInfoById): Promise { @@ -281,19 +282,19 @@ export class RemoteTerminalChannelClient implements IPtyHostController { } updateTitle(id: number, title: string, titleSource: TitleEventSource): Promise { - return this._channel.call('$updateTitle', [id, title, titleSource]); + return this._channel.call(RemoteTerminalChannelRequest.UpdateTitle, [id, title, titleSource]); } updateIcon(id: number, userInitiated: boolean, icon: TerminalIcon, color?: string): Promise { - return this._channel.call('$updateIcon', [id, userInitiated, icon, color]); + return this._channel.call(RemoteTerminalChannelRequest.UpdateIcon, [id, userInitiated, icon, color]); } refreshProperty(id: number, property: T): Promise { - return this._channel.call('$refreshProperty', [id, property]); + return this._channel.call(RemoteTerminalChannelRequest.RefreshProperty, [id, property]); } updateProperty(id: number, property: T, value: IProcessPropertyMap[T]): Promise { - return this._channel.call('$updateProperty', [id, property, value]); + return this._channel.call(RemoteTerminalChannelRequest.UpdateProperty, [id, property, value]); } getTerminalLayoutInfo(): Promise { @@ -305,14 +306,14 @@ export class RemoteTerminalChannelClient implements IPtyHostController { } reviveTerminalProcesses(state: ISerializedTerminalState[], dateTimeFormatLocate: string): Promise { - return this._channel.call('$reviveTerminalProcesses', [state, dateTimeFormatLocate]); + return this._channel.call(RemoteTerminalChannelRequest.ReviveTerminalProcesses, [state, dateTimeFormatLocate]); } getRevivedPtyNewId(id: number): Promise { - return this._channel.call('$getRevivedPtyNewId', [id]); + return this._channel.call(RemoteTerminalChannelRequest.GetRevivedPtyNewId, [id]); } serializeTerminalState(ids: number[]): Promise { - return this._channel.call('$serializeTerminalState', [ids]); + return this._channel.call(RemoteTerminalChannelRequest.SerializeTerminalState, [ids]); } } diff --git a/src/vs/workbench/contrib/terminal/common/remote/terminal.ts b/src/vs/workbench/contrib/terminal/common/remote/terminal.ts new file mode 100644 index 00000000000..a4ddd1af68b --- /dev/null +++ b/src/vs/workbench/contrib/terminal/common/remote/terminal.ts @@ -0,0 +1,99 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { UriComponents } from 'vs/base/common/uri'; +import { IShellLaunchConfigDto, ITerminalProcessOptions } from 'vs/platform/terminal/common/terminal'; +import { ICompleteTerminalConfiguration } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ISerializableEnvironmentDescriptionMap as ISerializableEnvironmentDescriptionMap, ISerializableEnvironmentVariableCollection } from 'vs/platform/terminal/common/environmentVariable'; + +export const REMOTE_TERMINAL_CHANNEL_NAME = 'remoteterminal'; + +export type ITerminalEnvironmentVariableCollections = [string, ISerializableEnvironmentVariableCollection, ISerializableEnvironmentDescriptionMap][]; + +export interface IWorkspaceFolderData { + uri: UriComponents; + name: string; + index: number; +} + +export interface ICreateTerminalProcessArguments { + configuration: ICompleteTerminalConfiguration; + resolvedVariables: { [name: string]: string }; + envVariableCollections: ITerminalEnvironmentVariableCollections; + shellLaunchConfig: IShellLaunchConfigDto; + workspaceId: string; + workspaceName: string; + workspaceFolders: IWorkspaceFolderData[]; + activeWorkspaceFolder: IWorkspaceFolderData | null; + activeFileResource: UriComponents | undefined; + shouldPersistTerminal: boolean; + options: ITerminalProcessOptions; + cols: number; + rows: number; + unicodeVersion: '6' | '11'; + resolverEnv: { [key: string]: string | null } | undefined; +} + +export interface ICreateTerminalProcessResult { + persistentTerminalId: number; + resolvedShellLaunchConfig: IShellLaunchConfigDto; +} + +export const enum RemoteTerminalChannelEvent { + OnPtyHostExitEvent = '$onPtyHostExitEvent', + OnPtyHostStartEvent = '$onPtyHostStartEvent', + OnPtyHostUnresponsiveEvent = '$onPtyHostUnresponsiveEvent', + OnPtyHostResponsiveEvent = '$onPtyHostResponsiveEvent', + OnPtyHostRequestResolveVariablesEvent = '$onPtyHostRequestResolveVariablesEvent', + OnProcessDataEvent = '$onProcessDataEvent', + OnProcessReadyEvent = '$onProcessReadyEvent', + OnProcessExitEvent = '$onProcessExitEvent', + OnProcessReplayEvent = '$onProcessReplayEvent', + OnProcessOrphanQuestion = '$onProcessOrphanQuestion', + OnExecuteCommand = '$onExecuteCommand', + OnDidRequestDetach = '$onDidRequestDetach', + OnDidChangeProperty = '$onDidChangeProperty', +} + +export const enum RemoteTerminalChannelRequest { + RestartPtyHost = '$restartPtyHost', + CreateProcess = '$createProcess', + AttachToProcess = '$attachToProcess', + DetachFromProcess = '$detachFromProcess', + ListProcesses = '$listProcesses', + GetPerformanceMarks = '$getPerformanceMarks', + OrphanQuestionReply = '$orphanQuestionReply', + AcceptPtyHostResolvedVariables = '$acceptPtyHostResolvedVariables', + Start = '$start', + Input = '$input', + AcknowledgeDataEvent = '$acknowledgeDataEvent', + Shutdown = '$shutdown', + Resize = '$resize', + ClearBuffer = '$clearBuffer', + GetInitialCwd = '$getInitialCwd', + GetCwd = '$getCwd', + ProcessBinary = '$processBinary', + SendCommandResult = '$sendCommandResult', + InstallAutoReply = '$installAutoReply', + UninstallAllAutoReplies = '$uninstallAllAutoReplies', + GetDefaultSystemShell = '$getDefaultSystemShell', + GetProfiles = '$getProfiles', + GetEnvironment = '$getEnvironment', + GetWslPath = '$getWslPath', + GetTerminalLayoutInfo = '$getTerminalLayoutInfo', + SetTerminalLayoutInfo = '$setTerminalLayoutInfo', + SerializeTerminalState = '$serializeTerminalState', + ReviveTerminalProcesses = '$reviveTerminalProcesses', + GetRevivedPtyNewId = '$getRevivedPtyNewId', + SetUnicodeVersion = '$setUnicodeVersion', + ReduceConnectionGraceTime = '$reduceConnectionGraceTime', + UpdateIcon = '$updateIcon', + UpdateTitle = '$updateTitle', + UpdateProperty = '$updateProperty', + RefreshProperty = '$refreshProperty', + RequestDetachInstance = '$requestDetachInstance', + AcceptDetachedInstance = '$acceptDetachedInstance', + FreePortKillProcess = '$freePortKillProcess', +} From d630142d45eaea62c284b2d1ed93c5ff6a52016a Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 6 Jul 2023 12:24:18 -0700 Subject: [PATCH 224/926] Add AcceptDetachInstanceReply impl and add ts assert for future --- src/vs/server/node/remoteTerminalChannel.ts | 9 ++++++--- .../workbench/contrib/terminal/common/remote/terminal.ts | 1 + 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/vs/server/node/remoteTerminalChannel.ts b/src/vs/server/node/remoteTerminalChannel.ts index 245aa816891..45aa12024c4 100644 --- a/src/vs/server/node/remoteTerminalChannel.ts +++ b/src/vs/server/node/remoteTerminalChannel.ts @@ -153,8 +153,10 @@ export class RemoteTerminalChannel extends Disposable implements IServerChannel< case RemoteTerminalChannelRequest.RequestDetachInstance: return this._ptyHostService.requestDetachInstance(args[0], args[1]); case RemoteTerminalChannelRequest.AcceptDetachedInstance: return this._ptyHostService.acceptDetachInstanceReply(args[0], args[1]); case RemoteTerminalChannelRequest.FreePortKillProcess: return this._ptyHostService.freePortKillProcess.apply(this._ptyHostService, args); - default: break; + case RemoteTerminalChannelRequest.AcceptDetachInstanceReply: return this._ptyHostService.acceptDetachInstanceReply.apply(this._ptyHostService, args); } + + // @ts-expect-error Assert command is never to ensure all messages are handled throw new Error(`IPC Command ${command} not found`); } @@ -173,9 +175,10 @@ export class RemoteTerminalChannel extends Disposable implements IServerChannel< case RemoteTerminalChannelEvent.OnExecuteCommand: return this.onExecuteCommand; case RemoteTerminalChannelEvent.OnDidRequestDetach: return this._ptyHostService.onDidRequestDetach || Event.None; case RemoteTerminalChannelEvent.OnDidChangeProperty: return this._ptyHostService.onDidChangeProperty; - default: break; } - throw new Error('Not supported'); + + // @ts-expect-error Assert event is never to ensure all messages are handled + throw new Error(`IPC Command ${event} not found`); } private async _createProcess(uriTransformer: IURITransformer, args: ICreateTerminalProcessArguments): Promise { diff --git a/src/vs/workbench/contrib/terminal/common/remote/terminal.ts b/src/vs/workbench/contrib/terminal/common/remote/terminal.ts index a4ddd1af68b..22aa8cf6dc4 100644 --- a/src/vs/workbench/contrib/terminal/common/remote/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/remote/terminal.ts @@ -94,6 +94,7 @@ export const enum RemoteTerminalChannelRequest { UpdateProperty = '$updateProperty', RefreshProperty = '$refreshProperty', RequestDetachInstance = '$requestDetachInstance', + AcceptDetachInstanceReply = '$acceptDetachInstanceReply', AcceptDetachedInstance = '$acceptDetachedInstance', FreePortKillProcess = '$freePortKillProcess', } From 1391e9a686764da51d80cc17f5e050af736649f0 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 6 Jul 2023 12:41:03 -0700 Subject: [PATCH 225/926] Add workspace id to terminal logs Fixes #187080 --- .../terminal/common/terminalLogService.ts | 35 +++++++++++++++---- 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/src/vs/platform/terminal/common/terminalLogService.ts b/src/vs/platform/terminal/common/terminalLogService.ts index 318dce06221..3e1e1a5ca69 100644 --- a/src/vs/platform/terminal/common/terminalLogService.ts +++ b/src/vs/platform/terminal/common/terminalLogService.ts @@ -8,6 +8,7 @@ import { Event } from 'vs/base/common/event'; import { localize } from 'vs/nls'; import { ILogger, ILoggerService, LogLevel } from 'vs/platform/log/common/log'; import { ITerminalLogService } from 'vs/platform/terminal/common/terminal'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; export class TerminalLogService extends Disposable implements ITerminalLogService { declare _serviceBrand: undefined; @@ -15,19 +16,41 @@ export class TerminalLogService extends Disposable implements ITerminalLogServic private readonly _logger: ILogger; + private _workspaceId!: string; + get onDidChangeLogLevel(): Event { return this._logger.onDidChangeLogLevel; } - constructor(@ILoggerService private readonly _loggerService: ILoggerService) { + constructor( + @ILoggerService private readonly _loggerService: ILoggerService, + @IWorkspaceContextService workspaceContextService: IWorkspaceContextService + ) { super(); this._logger = this._loggerService.createLogger('terminal', { name: localize('terminalLoggerName', 'Terminal') }); + this._register(Event.runAndSubscribe(workspaceContextService.onDidChangeWorkspaceFolders, () => { + this._workspaceId = workspaceContextService.getWorkspace().id.substring(0, 7); + })); } getLevel(): LogLevel { return this._logger.getLevel(); } setLevel(level: LogLevel): void { this._logger.setLevel(level); } - trace(message: string, ...args: any[]): void { this._logger.trace(message, args); } - debug(message: string, ...args: any[]): void { this._logger.debug(message, args); } - info(message: string, ...args: any[]): void { this._logger.info(message, args); } - warn(message: string, ...args: any[]): void { this._logger.warn(message, args); } - error(message: string | Error, ...args: any[]): void { this._logger.error(message, args); } flush(): void { this._logger.flush(); } + + trace(message: string, ...args: any[]): void { this._logger.trace(this._formatMessage(message), args); } + debug(message: string, ...args: any[]): void { this._logger.debug(this._formatMessage(message), args); } + info(message: string, ...args: any[]): void { this._logger.info(this._formatMessage(message), args); } + warn(message: string, ...args: any[]): void { this._logger.warn(this._formatMessage(message), args); } + error(message: string | Error, ...args: any[]): void { + if (message instanceof Error) { + this._logger.error(this._formatMessage(''), message, args); + return; + } + this._logger.error(this._formatMessage(message), args); + } + + private _formatMessage(message: string): string { + if (this._logger.getLevel() === LogLevel.Trace) { + return `[${this._workspaceId}] ${message}`; + } + return message; + } } From a50fca31934ac6d93c2ba2122db2016be9be4617 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 6 Jul 2023 12:45:12 -0700 Subject: [PATCH 226/926] Clarify it's talking about the type never --- src/vs/server/node/remoteTerminalChannel.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/server/node/remoteTerminalChannel.ts b/src/vs/server/node/remoteTerminalChannel.ts index 45aa12024c4..df9943918ee 100644 --- a/src/vs/server/node/remoteTerminalChannel.ts +++ b/src/vs/server/node/remoteTerminalChannel.ts @@ -156,7 +156,7 @@ export class RemoteTerminalChannel extends Disposable implements IServerChannel< case RemoteTerminalChannelRequest.AcceptDetachInstanceReply: return this._ptyHostService.acceptDetachInstanceReply.apply(this._ptyHostService, args); } - // @ts-expect-error Assert command is never to ensure all messages are handled + // @ts-expect-error Assert command is the `never` type to ensure all messages are handled throw new Error(`IPC Command ${command} not found`); } @@ -177,7 +177,7 @@ export class RemoteTerminalChannel extends Disposable implements IServerChannel< case RemoteTerminalChannelEvent.OnDidChangeProperty: return this._ptyHostService.onDidChangeProperty; } - // @ts-expect-error Assert event is never to ensure all messages are handled + // @ts-expect-error Assert event is the `never` type to ensure all messages are handled throw new Error(`IPC Command ${event} not found`); } From 3f96146561ef788a8a7dd2ff80fc264276da167c Mon Sep 17 00:00:00 2001 From: rebornix Date: Thu, 6 Jul 2023 12:52:20 -0700 Subject: [PATCH 227/926] Register the notebook remote save setting --- .../contrib/notebook/browser/notebook.contribution.ts | 5 +++++ src/vs/workbench/contrib/notebook/common/notebookCommon.ts | 1 + .../workbench/contrib/notebook/common/notebookEditorModel.ts | 4 ++-- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index 1766a450421..a73901e182d 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -984,6 +984,11 @@ configurationRegistry.registerConfiguration({ codeOutput: true }, tags: ['notebookLayout'] + }, + [NotebookSetting.remoteSaving]: { + markdownDescription: nls.localize('notebook.remoteSaving', "Enables the incremental saving of notebooks in Remote environment. When enabled, only the changes to the notebook are sent to the extension host, improving performance for large notebooks and slow network connections."), + type: 'boolean', + default: typeof product.quality === 'string' && product.quality !== 'stable' // only enable as default in insiders } } }); diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index e57bc24b374..558f6c04765 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -952,6 +952,7 @@ export const NotebookSetting = { findScope: 'notebook.find.scope', logging: 'notebook.logging', confirmDeleteRunningCell: 'notebook.confirmDeleteRunningCell', + remoteSaving: 'notebook.experimental.remoteSave' } as const; export const enum CellStatusbarAlignment { diff --git a/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts b/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts index 56313cf61a0..6cf8c8f934c 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts @@ -18,7 +18,7 @@ import { IWriteFileOptions, IFileStatWithMetadata } from 'vs/platform/files/comm import { IRevertOptions, ISaveOptions, IUntypedEditorInput } from 'vs/workbench/common/editor'; import { EditorModel } from 'vs/workbench/common/editor/editorModel'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; -import { ICellDto2, INotebookEditorModel, INotebookLoadOptions, IResolvedNotebookEditorModel, NotebookCellsChangeType, NotebookData } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { ICellDto2, INotebookEditorModel, INotebookLoadOptions, IResolvedNotebookEditorModel, NotebookCellsChangeType, NotebookData, NotebookSetting } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookSerializer, INotebookService, SimpleNotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookService'; import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; import { IFileWorkingCopyModelConfiguration } from 'vs/workbench/services/workingCopy/common/fileWorkingCopy'; @@ -215,7 +215,7 @@ export class NotebookFileWorkingCopyModel extends Disposable implements IStoredF }; // Override save behavior to avoid transferring the buffer across the wire 3 times - if (this._configurationService.getValue('notebook.experimental.remoteSave')) { + if (this._configurationService.getValue(NotebookSetting.remoteSaving)) { this.save = async (options: IWriteFileOptions, token: CancellationToken) => { const serializer = await this.getNotebookSerializer(); From eec239dc354300777e464f4bd0cad64aeea4492a Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Thu, 6 Jul 2023 23:17:43 +0200 Subject: [PATCH 228/926] Fixes #187164 (#187192) --- .../diffEditorWidget2/diffEditorEditors.ts | 17 ++++++++++------ .../diffEditorWidget2/diffEditorWidget2.ts | 3 +++ .../widget/diffEditorWidget2/diffReview.ts | 20 +++++++++---------- 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors.ts index ac29835d101..e900b4e55ab 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors.ts @@ -15,6 +15,7 @@ import { IContentSizeChangedEvent } from 'vs/editor/common/editorCommon'; import { localize } from 'vs/nls'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { DiffEditorOptions } from './diffEditorOptions'; +import { IObservable, IReader } from 'vs/base/common/observable'; export class DiffEditorEditors extends Disposable { public readonly modified: CodeEditorWidget; @@ -29,6 +30,7 @@ export class DiffEditorEditors extends Disposable { private readonly _options: DiffEditorOptions, codeEditorWidgetOptions: IDiffCodeEditorWidgetOptions, private readonly _createInnerEditor: (instantiationService: IInstantiationService, container: HTMLElement, options: Readonly, editorWidgetOptions: ICodeEditorWidgetOptions) => CodeEditorWidget, + private readonly _modifiedReadOnlyOverride: IObservable, @IInstantiationService private readonly _instantiationService: IInstantiationService ) { super(); @@ -47,19 +49,21 @@ export class DiffEditorEditors extends Disposable { }, (reader, changeSummary) => { _options.editorOptions.read(reader); - this.modified.updateOptions(this._adjustOptionsForRightHandSide(changeSummary)); - this.original.updateOptions(this._adjustOptionsForLeftHandSide(changeSummary)); + this.modified.updateOptions(this._adjustOptionsForRightHandSide(reader, changeSummary)); + this.original.updateOptions(this._adjustOptionsForLeftHandSide(reader, changeSummary)); })); } private _createLeftHandSideEditor(options: Readonly, codeEditorWidgetOptions: ICodeEditorWidgetOptions): CodeEditorWidget { - const editor = this._constructInnerEditor(this._instantiationService, this.originalEditorElement, this._adjustOptionsForLeftHandSide(options), codeEditorWidgetOptions); + const leftHandSideOptions = this._adjustOptionsForLeftHandSide(undefined, options); + const editor = this._constructInnerEditor(this._instantiationService, this.originalEditorElement, leftHandSideOptions, codeEditorWidgetOptions); editor.setContextValue('isInDiffLeftEditor', true); return editor; } private _createRightHandSideEditor(options: Readonly, codeEditorWidgetOptions: ICodeEditorWidgetOptions): CodeEditorWidget { - const editor = this._constructInnerEditor(this._instantiationService, this.modifiedEditorElement, this._adjustOptionsForRightHandSide(options), codeEditorWidgetOptions); + const rightHandSideOptions = this._adjustOptionsForRightHandSide(undefined, options); + const editor = this._constructInnerEditor(this._instantiationService, this.modifiedEditorElement, rightHandSideOptions, codeEditorWidgetOptions); editor.setContextValue('isInDiffRightEditor', true); return editor; } @@ -81,7 +85,7 @@ export class DiffEditorEditors extends Disposable { return editor; } - private _adjustOptionsForLeftHandSide(changedOptions: Readonly): IEditorConstructionOptions { + private _adjustOptionsForLeftHandSide(_reader: IReader | undefined, changedOptions: Readonly): IEditorConstructionOptions { const result = this._adjustOptionsForSubEditor(changedOptions); if (!this._options.renderSideBySide.get()) { // never wrap hidden editor @@ -101,7 +105,7 @@ export class DiffEditorEditors extends Disposable { return result; } - private _adjustOptionsForRightHandSide(changedOptions: Readonly): IEditorConstructionOptions { + private _adjustOptionsForRightHandSide(reader: IReader | undefined, changedOptions: Readonly): IEditorConstructionOptions { const result = this._adjustOptionsForSubEditor(changedOptions); if (changedOptions.modifiedAriaLabel) { result.ariaLabel = changedOptions.modifiedAriaLabel; @@ -111,6 +115,7 @@ export class DiffEditorEditors extends Disposable { result.revealHorizontalRightPadding = EditorOptions.revealHorizontalRightPadding.defaultValue + OverviewRulerPart.ENTIRE_DIFF_OVERVIEW_WIDTH; result.scrollbar!.verticalHasArrows = false; result.extraEditorClassName = 'modified-in-monaco-diff-editor'; + result.readOnly = this._modifiedReadOnlyOverride.read(reader) || this._options.editorOptions.get().readOnly; return result; } diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts index 34fd95848c7..bf02f92991b 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts @@ -96,6 +96,7 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { this._rootSizeObserver = this._register(new ObservableElementSizeObserver(this.elements.root, options.dimension)); this._rootSizeObserver.setAutomaticLayout(options.automaticLayout ?? false); + const reviewPaneObservable = observableValue('reviewPane', undefined); this._editors = this._register(this._instantiationService.createInstance( DiffEditorEditors, this.elements.original, @@ -103,6 +104,7 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { this._options, codeEditorWidgetOptions, (i, c, o, o2) => this._createInnerEditor(i, c, o, o2), + reviewPaneObservable.map((r, reader) => r?.isVisible.read(reader) ?? false), )); this._sash = derivedWithStore('sash', (reader, store) => { @@ -159,6 +161,7 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { this.elements.root.appendChild(this._reviewPane.domNode.domNode); this.elements.root.appendChild(this._reviewPane.shadow.domNode); this.elements.root.appendChild(this._reviewPane.actionBarContainer.domNode); + reviewPaneObservable.set(this._reviewPane, undefined); this._createDiffEditorContributions(); diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffReview.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffReview.ts index 94d3a9cda2f..f9821f1f4a6 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffReview.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffReview.ts @@ -11,6 +11,7 @@ import { Action } from 'vs/base/common/actions'; import { Codicon } from 'vs/base/common/codicons'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { Disposable } from 'vs/base/common/lifecycle'; +import { IObservable, observableValue } from 'vs/base/common/observable'; import { ThemeIcon } from 'vs/base/common/themables'; import { Constants } from 'vs/base/common/uint'; import { applyFontInfo } from 'vs/editor/browser/config/domFontInfo'; @@ -85,7 +86,7 @@ export class DiffReview2 extends Disposable { private static _ttPolicy = DiffReview._ttPolicy; // TODO inline once DiffReview is deprecated. private readonly _diffEditor: DiffEditorWidget2; - private _isVisible: boolean; + private get _isVisible() { return this._isVisibleObs.get(); } public readonly shadow: FastDomNode; private readonly _actionBar: ActionBar; public readonly actionBarContainer: FastDomNode; @@ -95,6 +96,10 @@ export class DiffReview2 extends Disposable { private _diffs: Diff[]; private _currentDiff: Diff | null; + private readonly _isVisibleObs = observableValue('isVisible', false); + + public readonly isVisible: IObservable = this._isVisibleObs; + constructor( diffEditor: DiffEditorWidget2, @ILanguageService private readonly _languageService: ILanguageService, @@ -103,7 +108,6 @@ export class DiffReview2 extends Disposable { ) { super(); this._diffEditor = diffEditor; - this._isVisible = false; this.shadow = createFastDomNode(document.createElement('div')); this.shadow.setClassName('diff-review-shadow'); @@ -215,7 +219,7 @@ export class DiffReview2 extends Disposable { const entries = this._diffs[index].entries; this._diffEditor.setPosition(new Position(entries[0].modifiedLineStart, 1)); this._diffEditor.setSelection({ startColumn: 1, startLineNumber: entries[0].modifiedLineStart, endColumn: Constants.MAX_SAFE_SMALL_INTEGER, endLineNumber: entries[entries.length - 1].modifiedLineEnd }); - this._isVisible = true; + this._isVisibleObs.set(true, undefined); this.layout(); this._render(); this._goToRow(this._getPrevRow(), 'previous'); @@ -250,7 +254,7 @@ export class DiffReview2 extends Disposable { const entries = this._diffs[index].entries; this._diffEditor.setPosition(new Position(entries[0].modifiedLineStart, 1)); this._diffEditor.setSelection({ startColumn: 1, startLineNumber: entries[0].modifiedLineStart, endColumn: Constants.MAX_SAFE_SMALL_INTEGER, endLineNumber: entries[entries.length - 1].modifiedLineEnd }); - this._isVisible = true; + this._isVisibleObs.set(true, undefined); this.layout(); this._render(); this._goToRow(this._getNextRow(), 'next'); @@ -274,8 +278,7 @@ export class DiffReview2 extends Disposable { } private hide(): void { - this._isVisible = false; - this._diffEditor.updateOptions({ readOnly: false }); + this._isVisibleObs.set(false, undefined); this._diffEditor.focus(); this.layout(); this._render(); @@ -331,10 +334,6 @@ export class DiffReview2 extends Disposable { this.scrollbar.scanDomNode(); } - public isVisible(): boolean { - return this._isVisible; - } - private _width: number = 0; private _top: number = 0; private _height: number = 0; @@ -564,7 +563,6 @@ export class DiffReview2 extends Disposable { return; } - this._diffEditor.updateOptions({ readOnly: true }); const diffIndex = this._findDiffIndex(this._diffEditor.getPosition()!); if (this._diffs[diffIndex] === this._currentDiff) { From 30fde0d36907e2d8f7480cd4011e61d4c34c6db9 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Thu, 6 Jul 2023 14:49:31 -0700 Subject: [PATCH 229/926] fix: stall on "starting new singleton" on linux if keychain blocks (#187182) Adds a 5s timeout to keychain access on Linux. We had an issue about this a long time ago, but I never repro'd it until today and can't find the original... If this timeout is hit, it'll fall back to the file-based keychain. --- cli/src/auth.rs | 86 +++++++++++++++++++++++++++--------------- cli/src/util/errors.rs | 4 +- 2 files changed, 59 insertions(+), 31 deletions(-) diff --git a/cli/src/auth.rs b/cli/src/auth.rs index 604b1a6ced7..bc2737afc00 100644 --- a/cli/src/auth.rs +++ b/cli/src/auth.rs @@ -10,7 +10,8 @@ use crate::{ trace, util::{ errors::{ - wrap, AnyError, OAuthError, RefreshTokenNotAvailableError, StatusError, WrappedError, + wrap, AnyError, CodeError, OAuthError, RefreshTokenNotAvailableError, StatusError, + WrappedError, }, input::prompt_options, }, @@ -172,9 +173,9 @@ pub struct Auth { } trait StorageImplementation: Send + Sync { - fn read(&mut self) -> Result, WrappedError>; - fn store(&mut self, value: StoredCredential) -> Result<(), WrappedError>; - fn clear(&mut self) -> Result<(), WrappedError>; + fn read(&mut self) -> Result, AnyError>; + fn store(&mut self, value: StoredCredential) -> Result<(), AnyError>; + fn clear(&mut self) -> Result<(), AnyError>; } // unseal decrypts and deserializes the value @@ -217,16 +218,34 @@ struct ThreadKeyringStorage { } impl ThreadKeyringStorage { - fn thread_op(&mut self, f: Fn) -> R + fn thread_op(&mut self, f: Fn) -> Result where - Fn: 'static + Send + FnOnce(&mut KeyringStorage) -> R, + Fn: 'static + Send + FnOnce(&mut KeyringStorage) -> Result, R: 'static + Send, { - let mut s = self.s.take().unwrap(); - let handler = thread::spawn(move || (f(&mut s), s)); - let (r, s) = handler.join().unwrap(); - self.s = Some(s); - r + let mut s = match self.s.take() { + Some(s) => s, + None => return Err(CodeError::KeyringTimeout.into()), + }; + + // It seems like on Linux communication to the keyring can block indefinitely. + // Fall back after a 5 second timeout. + let (sender, receiver) = std::sync::mpsc::channel(); + let tsender = sender.clone(); + + thread::spawn(move || sender.send(Some((f(&mut s), s)))); + thread::spawn(move || { + thread::sleep(std::time::Duration::from_secs(5)); + let _ = tsender.send(None); + }); + + match receiver.recv().unwrap() { + Some((r, s)) => { + self.s = Some(s); + r + } + None => Err(CodeError::KeyringTimeout.into()), + } } } @@ -239,15 +258,15 @@ impl Default for ThreadKeyringStorage { } impl StorageImplementation for ThreadKeyringStorage { - fn read(&mut self) -> Result, WrappedError> { + fn read(&mut self) -> Result, AnyError> { self.thread_op(|s| s.read()) } - fn store(&mut self, value: StoredCredential) -> Result<(), WrappedError> { + fn store(&mut self, value: StoredCredential) -> Result<(), AnyError> { self.thread_op(move |s| s.store(value)) } - fn clear(&mut self) -> Result<(), WrappedError> { + fn clear(&mut self) -> Result<(), AnyError> { self.thread_op(|s| s.clear()) } } @@ -273,7 +292,7 @@ macro_rules! get_next_entry { } impl StorageImplementation for KeyringStorage { - fn read(&mut self) -> Result, WrappedError> { + fn read(&mut self) -> Result, AnyError> { let mut str = String::new(); for i in 0.. { @@ -281,7 +300,7 @@ impl StorageImplementation for KeyringStorage { let next_chunk = match entry.get_password() { Ok(value) => value, Err(keyring::Error::NoEntry) => return Ok(None), // missing entries? - Err(e) => return Err(wrap(e, "error reading keyring")), + Err(e) => return Err(wrap(e, "error reading keyring").into()), }; if next_chunk.ends_with(CONTINUE_MARKER) { @@ -295,7 +314,7 @@ impl StorageImplementation for KeyringStorage { Ok(unseal(&str)) } - fn store(&mut self, value: StoredCredential) -> Result<(), WrappedError> { + fn store(&mut self, value: StoredCredential) -> Result<(), AnyError> { let sealed = seal(&value); let step_size = KEYCHAIN_ENTRY_LIMIT - CONTINUE_MARKER.len(); @@ -312,14 +331,14 @@ impl StorageImplementation for KeyringStorage { }; if let Err(e) = stored { - return Err(wrap(e, "error updating keyring")); + return Err(wrap(e, "error updating keyring").into()); } } Ok(()) } - fn clear(&mut self) -> Result<(), WrappedError> { + fn clear(&mut self) -> Result<(), AnyError> { self.read().ok(); // make sure component parts are available for entry in self.entries.iter() { entry @@ -335,16 +354,16 @@ impl StorageImplementation for KeyringStorage { struct FileStorage(PersistedState>); impl StorageImplementation for FileStorage { - fn read(&mut self) -> Result, WrappedError> { + fn read(&mut self) -> Result, AnyError> { Ok(self.0.load().and_then(|s| unseal(&s))) } - fn store(&mut self, value: StoredCredential) -> Result<(), WrappedError> { - self.0.save(Some(seal(&value))) + fn store(&mut self, value: StoredCredential) -> Result<(), AnyError> { + self.0.save(Some(seal(&value))).map_err(|e| e.into()) } - fn clear(&mut self) -> Result<(), WrappedError> { - self.0.save(None) + fn clear(&mut self) -> Result<(), AnyError> { + self.0.save(None).map_err(|e| e.into()) } } @@ -374,7 +393,7 @@ impl Auth { let mut file_storage = FileStorage(PersistedState::new(self.file_storage_path.clone())); let keyring_storage_result = match std::env::var("VSCODE_CLI_USE_FILE_KEYCHAIN") { - Ok(_) => Err(wrap("", "user prefers file storage")), + Ok(_) => Err(wrap("", "user prefers file storage").into()), _ => keyring_storage.read(), }; @@ -383,10 +402,17 @@ impl Auth { last_read: Cell::new(Ok(v)), storage: Box::new(keyring_storage), }, - Err(_) => StorageWithLastRead { - last_read: Cell::new(file_storage.read()), - storage: Box::new(file_storage), - }, + Err(e) => { + debug!(self.log, "Using file keychain storage due to: {}", e); + StorageWithLastRead { + last_read: Cell::new( + file_storage + .read() + .map_err(|e| wrap(e, "could not read from file storage")), + ), + storage: Box::new(file_storage), + } + } }; let out = op(&mut storage); @@ -419,7 +445,7 @@ impl Auth { } /// Clears login info from the keyring. - pub fn clear_credentials(&self) -> Result<(), WrappedError> { + pub fn clear_credentials(&self) -> Result<(), AnyError> { self.with_storage(|storage| { storage.storage.clear()?; storage.last_read.set(Ok(None)); diff --git a/cli/src/util/errors.rs b/cli/src/util/errors.rs index 6f4630d0c54..ca6d4bf3d8a 100644 --- a/cli/src/util/errors.rs +++ b/cli/src/util/errors.rs @@ -2,7 +2,7 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -use crate::{ + use crate::{ constants::{APPLICATION_NAME, CONTROL_PORT, DOCUMENTATION_URL, QUALITYLESS_PRODUCT_NAME}, rpc::ResponseError, }; @@ -511,6 +511,8 @@ pub enum CodeError { AuthChallengeNotIssued, #[error("unauthorized client refused")] AuthMismatch, + #[error("keyring communication timed out after 5s")] + KeyringTimeout, } makeAnyError!( From 7061dbd1d2789a8ff9e5222e3d188b696aa22178 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Thu, 6 Jul 2023 15:47:50 -0700 Subject: [PATCH 230/926] debug: bump companion to 1.1.2 (#187203) --- product.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/product.json b/product.json index 5c83e0ff30d..6d015e98bd0 100644 --- a/product.json +++ b/product.json @@ -36,8 +36,8 @@ "builtInExtensions": [ { "name": "ms-vscode.js-debug-companion", - "version": "1.1.1", - "sha256": "095dc6d5d45490966dfa74d24add92036b42bf02a37d88c4b0f510dc91deeee6", + "version": "1.1.2", + "sha256": "e034b8b41beb4e97e02c70f7175bd88abe66048374c2bd629f54bb33354bc2aa", "repo": "https://github.com/microsoft/vscode-js-debug-companion", "metadata": { "id": "99cb0b7f-7354-4278-b8da-6cc79972169d", From 13b90156ab7aeb16fe4a3857f59ac8d08146f00e Mon Sep 17 00:00:00 2001 From: Raymond Zhao <7199958+rzhao271@users.noreply.github.com> Date: Thu, 6 Jul 2023 16:14:29 -0700 Subject: [PATCH 231/926] Strengthen Emmet tests (#187204) --- extensions/emmet/src/test/abbreviationAction.test.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/extensions/emmet/src/test/abbreviationAction.test.ts b/extensions/emmet/src/test/abbreviationAction.test.ts index 3f18a1ffc5d..17ccacfc94a 100644 --- a/extensions/emmet/src/test/abbreviationAction.test.ts +++ b/extensions/emmet/src/test/abbreviationAction.test.ts @@ -47,8 +47,6 @@ const invokeCompletionContext: CompletionContext = { }; suite('Tests for Expand Abbreviations (HTML)', () => { - const oldValueForExcludeLanguages = workspace.getConfiguration('emmet').inspect('excludeLanguages'); - const oldValueForIncludeLanguages = workspace.getConfiguration('emmet').inspect('includeLanguages'); teardown(closeAllEditors); test('Expand snippets (HTML)', () => { @@ -364,6 +362,7 @@ suite('Tests for Expand Abbreviations (HTML)', () => { }); test('Expand html when inside script tag with javascript type if js is mapped to html (HTML)', async () => { + const oldConfig = workspace.getConfiguration('emmet').inspect('includeLanguages')?.globalValue; await workspace.getConfiguration('emmet').update('includeLanguages', { 'javascript': 'html' }, ConfigurationTarget.Global); await withRandomFileEditor(htmlContents, 'html', async (editor, _doc) => { editor.selection = new Selection(24, 10, 24, 10); @@ -374,12 +373,13 @@ suite('Tests for Expand Abbreviations (HTML)', () => { await expandPromise; assert.strictEqual(editor.document.getText(), htmlContents.replace('span.bye', '')); }); - return workspace.getConfiguration('emmet').update('includeLanguages', oldValueForIncludeLanguages || {}, ConfigurationTarget.Global); + await workspace.getConfiguration('emmet').update('includeLanguages', oldConfig, ConfigurationTarget.Global); }); test('Expand html in completion list when inside script tag with javascript type if js is mapped to html (HTML)', async () => { const abbreviation = 'span.bye'; const expandedText = ''; + const oldConfig = workspace.getConfiguration('emmet').inspect('includeLanguages')?.globalValue; await workspace.getConfiguration('emmet').update('includeLanguages', { 'javascript': 'html' }, ConfigurationTarget.Global); await withRandomFileEditor(htmlContents, 'html', async (editor, _doc) => { editor.selection = new Selection(24, 10, 24, 10); @@ -399,7 +399,7 @@ suite('Tests for Expand Abbreviations (HTML)', () => { assert.strictEqual(((emmetCompletionItem.documentation) || '').replace(/\|/g, ''), expandedText, `Docs of completion item doesnt match.`); return Promise.resolve(); }); - return workspace.getConfiguration('emmet').update('includeLanguages', oldValueForIncludeLanguages || {}, ConfigurationTarget.Global); + await workspace.getConfiguration('emmet').update('includeLanguages', oldConfig, ConfigurationTarget.Global); }); // test('No expanding when html is excluded in the settings', () => { @@ -411,9 +411,10 @@ suite('Tests for Expand Abbreviations (HTML)', () => { // }); test('No expanding when html is excluded in the settings in completion list', async () => { + const oldConfig = workspace.getConfiguration('emmet').inspect('excludeLanguages')?.globalValue; await workspace.getConfiguration('emmet').update('excludeLanguages', ['html'], ConfigurationTarget.Global); await testHtmlCompletionProvider(new Selection(9, 6, 9, 6), '', '', true); - return workspace.getConfiguration('emmet').update('excludeLanguages', oldValueForExcludeLanguages ? oldValueForExcludeLanguages.globalValue : undefined, ConfigurationTarget.Global); + await workspace.getConfiguration('emmet').update('excludeLanguages', oldConfig, ConfigurationTarget.Global); }); // test('No expanding when php (mapped syntax) is excluded in the settings', () => { From 7ffb51f988ac5ec5c73f024603836c26da636852 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Thu, 6 Jul 2023 17:05:04 -0700 Subject: [PATCH 232/926] Avoid kernel picker progress when kernel extensions are disabled (#187210) --- .../notebookKernelQuickPickStrategy.ts | 42 +++++++++++++++---- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelQuickPickStrategy.ts b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelQuickPickStrategy.ts index 2da8f490a0e..473d46907b1 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelQuickPickStrategy.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelQuickPickStrategy.ts @@ -36,6 +36,7 @@ import { URI } from 'vs/base/common/uri'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { INotebookTextModel } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { SELECT_KERNEL_ID } from 'vs/workbench/contrib/notebook/browser/controller/coreActions'; +import { EnablementState } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; type KernelPick = IQuickPickItem & { kernel: INotebookKernel }; function isKernelPick(item: QuickPickInput): item is KernelPick { @@ -287,17 +288,22 @@ abstract class KernelPickerStrategyBase implements IKernelPickerStrategy { ) { // If extension id is provided attempt to install the extension as the user has requested the suggested ones be installed const extensionsToInstall: IExtension[] = []; + const extensionsToEnable: IExtension[] = []; for (const extId of extIds) { const extension = (await extensionWorkbenchService.getExtensions([{ id: extId }], CancellationToken.None))[0]; - const canInstall = await extensionWorkbenchService.canInstall(extension); - if (canInstall) { - extensionsToInstall.push(extension); + if (extension.enablementState === EnablementState.DisabledGlobally || extension.enablementState === EnablementState.DisabledWorkspace || extension.enablementState === EnablementState.DisabledByEnvironment) { + extensionsToEnable.push(extension); + } else { + const canInstall = await extensionWorkbenchService.canInstall(extension); + if (canInstall) { + extensionsToInstall.push(extension); + } } } - if (extensionsToInstall.length) { - await Promise.all(extensionsToInstall.map(async extension => { + if (extensionsToInstall.length || extensionsToEnable.length) { + await Promise.all([...extensionsToInstall.map(async extension => { await extensionWorkbenchService.install( extension, { @@ -306,7 +312,21 @@ abstract class KernelPickerStrategyBase implements IKernelPickerStrategy { }, ProgressLocation.Notification ); - })); + }), ...extensionsToEnable.map(async extension => { + switch (extension.enablementState) { + case EnablementState.DisabledWorkspace: + await extensionWorkbenchService.setEnablement([extension], EnablementState.EnabledWorkspace); + return; + case EnablementState.DisabledGlobally: + await extensionWorkbenchService.setEnablement([extension], EnablementState.EnabledGlobally); + return; + case EnablementState.DisabledByEnvironment: + await extensionWorkbenchService.setEnablement([extension], EnablementState.EnabledByEnvironment); + return; + default: + break; + } + })]); await extensionService.activateByEvent(`onNotebook:${viewType}`); return; @@ -348,7 +368,11 @@ abstract class KernelPickerStrategyBase implements IKernelPickerStrategy { const suggestedExtension: INotebookExtensionRecommendation | undefined = language ? this.getSuggestedKernelFromLanguage(notebookTextModel.viewType, language) : undefined; if (suggestedExtension) { await extensionWorkbenchService.queryLocal(); - const extensions = extensionWorkbenchService.installed.filter(e => suggestedExtension.extensionIds.includes(e.identifier.id)); + + const extensions = extensionWorkbenchService.installed.filter(e => + (e.enablementState === EnablementState.EnabledByEnvironment || e.enablementState === EnablementState.EnabledGlobally || e.enablementState === EnablementState.EnabledWorkspace) + && suggestedExtension.extensionIds.includes(e.identifier.id) + ); if (extensions.length === suggestedExtension.extensionIds.length) { // it's installed but might be detecting kernels @@ -359,7 +383,7 @@ abstract class KernelPickerStrategyBase implements IKernelPickerStrategy { quickPickItems.push({ id: 'installSuggested', description: suggestedExtension.displayName ?? suggestedExtension.extensionIds.join(', '), - label: `$(${Codicon.lightbulb.id}) ` + localize('installSuggestedKernel', 'Install suggested extensions'), + label: `$(${Codicon.lightbulb.id}) ` + localize('installSuggestedKernel', 'Install/Enable suggested extensions'), extensionIds: suggestedExtension.extensionIds } as InstallExtensionPick); } @@ -605,7 +629,7 @@ export class KernelPickerMRUStrategy extends KernelPickerStrategyBase { selectedKernelPickItem.extensionIds, this._productService.quality !== 'stable' ); - return true; + return this.displaySelectAnotherQuickPick(editor, false); } } From 31f183271b0d1f24a2052f6eccbb76bdef45d60b Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Fri, 7 Jul 2023 02:05:55 +0200 Subject: [PATCH 233/926] Fixes #187194 (#187211) --- .../inlineCompletions/browser/commands.ts | 4 +-- .../browser/inlineCompletionsModel.ts | 26 ++++++++++++------- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/vs/editor/contrib/inlineCompletions/browser/commands.ts b/src/vs/editor/contrib/inlineCompletions/browser/commands.ts index fbb7b657c8d..2370e210bf3 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/commands.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/commands.ts @@ -97,7 +97,7 @@ export class AcceptNextWordOfInlineCompletion extends EditorAction { public async run(accessor: ServicesAccessor | undefined, editor: ICodeEditor): Promise { const controller = InlineCompletionsController.get(editor); - controller?.model.get()?.acceptNextWord(controller.editor); + await controller?.model.get()?.acceptNextWord(controller.editor); } } @@ -122,7 +122,7 @@ export class AcceptNextLineOfInlineCompletion extends EditorAction { public async run(accessor: ServicesAccessor | undefined, editor: ICodeEditor): Promise { const controller = InlineCompletionsController.get(editor); - controller?.model.get()?.acceptNextLine(controller.editor); + await controller?.model.get()?.acceptNextLine(controller.editor); } } diff --git a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel.ts b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel.ts index 95346e787b6..c66ec2a6d56 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel.ts @@ -306,20 +306,28 @@ export class InlineCompletionsModel extends Disposable { } if (completion.command) { - await this._commandService - .executeCommand(completion.command.id, ...(completion.command.arguments || [])) - .then(undefined, onUnexpectedExternalError); + // Make sure the completion list will not be disposed. + completion.source.addRef(); } + + // Reset before invoking the command, since the command might cause a follow up trigger. transaction(tx => { this._source.clear(tx); // Potentially, isActive will get set back to true by the typing or accept inline suggest event // if automatic inline suggestions are enabled. this._isActive.set(false, tx); }); + + if (completion.command) { + await this._commandService + .executeCommand(completion.command.id, ...(completion.command.arguments || [])) + .then(undefined, onUnexpectedExternalError); + completion.source.removeRef(); + } } - public acceptNextWord(editor: ICodeEditor): void { - this._acceptNext(editor, (pos, text) => { + public async acceptNextWord(editor: ICodeEditor): Promise { + await this._acceptNext(editor, (pos, text) => { const langId = this.textModel.getLanguageIdAtPosition(pos.lineNumber, pos.column); const config = this._languageConfigurationService.getLanguageConfiguration(langId); const wordRegExp = new RegExp(config.wordDefinition.source, config.wordDefinition.flags.replace('g', '')); @@ -347,8 +355,8 @@ export class InlineCompletionsModel extends Disposable { }); } - public acceptNextLine(editor: ICodeEditor): void { - this._acceptNext(editor, (pos, text) => { + public async acceptNextLine(editor: ICodeEditor): Promise { + await this._acceptNext(editor, (pos, text) => { const m = text.match(/\n/); if (m && m.index !== undefined) { return m.index + 1; @@ -357,7 +365,7 @@ export class InlineCompletionsModel extends Disposable { }); } - private _acceptNext(editor: ICodeEditor, getAcceptUntilIndex: (position: Position, text: string) => number): void { + private async _acceptNext(editor: ICodeEditor, getAcceptUntilIndex: (position: Position, text: string) => number): Promise { if (editor.getModel() !== this.textModel) { throw new BugIndicatingError(); } @@ -370,7 +378,7 @@ export class InlineCompletionsModel extends Disposable { if (completion.snippetInfo || completion.filterText !== completion.insertText) { // not in WYSIWYG mode, partial commit might change completion, thus it is not supported - this.accept(editor); + await this.accept(editor); return; } From 2f2bc05fde94f40ec7ec68398c24e5bae71b30c1 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Thu, 6 Jul 2023 23:56:43 -0700 Subject: [PATCH 234/926] Don't log when not actually persisting sessions (#187214) --- .../contrib/chat/common/chatServiceImpl.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts index d05c1f1a828..c15379f06c6 100644 --- a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts +++ b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts @@ -152,10 +152,11 @@ export class ChatService extends Disposable implements IChatService { if (sessionData) { this._persistedSessions = this.deserializeChats(sessionData); const countsForLog = Object.keys(this._persistedSessions).length; - this.trace('constructor', `Restored ${countsForLog} persisted sessions`); + if (countsForLog > 0) { + this.trace('constructor', `Restored ${countsForLog} persisted sessions`); + } } else { this._persistedSessions = {}; - this.trace('constructor', 'No persisted sessions'); } this._transferred = this.getTransferredSession(); @@ -176,10 +177,16 @@ export class ChatService extends Disposable implements IChatService { .filter(session => session.requests.length)); allSessions.sort((a, b) => (b.creationDate ?? 0) - (a.creationDate ?? 0)); allSessions = allSessions.slice(0, maxPersistedSessions); - this.trace('onWillSaveState', `Persisting ${allSessions.length} sessions`); + if (allSessions.length) { + this.trace('onWillSaveState', `Persisting ${allSessions.length} sessions`); + } const serialized = JSON.stringify(allSessions); - this.trace('onWillSaveState', `Persisting ${serialized.length} chars`); + + if (allSessions.length) { + this.trace('onWillSaveState', `Persisting ${serialized.length} chars`); + } + this.storageService.store(serializedChatKey, serialized, StorageScope.WORKSPACE, StorageTarget.MACHINE); } From fa37b611fe9bd8c4a72249f88566a1acfbb953a8 Mon Sep 17 00:00:00 2001 From: Andrea Mah <31675041+andreamah@users.noreply.github.com> Date: Thu, 6 Jul 2023 23:57:51 -0700 Subject: [PATCH 235/926] search: fix resources with trailing slash (#187106) * search resources with trailing slash in root --- .../contrib/search/browser/searchModel.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/searchModel.ts b/src/vs/workbench/contrib/search/browser/searchModel.ts index 68b2ca8f905..f90c691f08b 100644 --- a/src/vs/workbench/contrib/search/browser/searchModel.ts +++ b/src/vs/workbench/contrib/search/browser/searchModel.ts @@ -1275,6 +1275,8 @@ export class FolderMatch extends Disposable { export class FolderMatchWithResource extends FolderMatch { + protected _normalizedResource: Lazy; + constructor(_resource: URI, _id: string, _index: number, _query: ITextQuery, _parent: SearchResult | FolderMatch, _searchModel: SearchModel, _closestRoot: FolderMatchWorkspaceRoot | null, @IReplaceService replaceService: IReplaceService, @IInstantiationService instantiationService: IInstantiationService, @@ -1282,11 +1284,17 @@ export class FolderMatchWithResource extends FolderMatch { @IUriIdentityService uriIdentityService: IUriIdentityService ) { super(_resource, _id, _index, _query, _parent, _searchModel, _closestRoot, replaceService, instantiationService, labelService, uriIdentityService); + this._normalizedResource = new Lazy(() => this.uriIdentityService.extUri.removeTrailingPathSeparator(this.uriIdentityService.extUri.normalizePath( + this.resource))); } override get resource(): URI { return this._resource!; } + + get normalizedResource(): URI { + return this._normalizedResource.value; + } } /** @@ -1334,15 +1342,14 @@ export class FolderMatchWorkspaceRoot extends FolderMatchWithResource { } const fileMatchParentParts: URI[] = []; - const normalizedResource = this.uriIdentityService.extUri.normalizePath(this.resource); let uri = this.normalizedUriParent(rawFileMatch.resource); - while (!this.uriEquals(normalizedResource, uri)) { + while (!this.uriEquals(this.normalizedResource, uri)) { fileMatchParentParts.unshift(uri); const prevUri = uri; - uri = this.normalizedUriParent(uri); + uri = this.uriIdentityService.extUri.removeTrailingPathSeparator(this.normalizedUriParent(uri)); if (this.uriEquals(prevUri, uri)) { - throw Error(`${rawFileMatch.resource} is not correctly configured as a child of ${normalizedResource}`); + throw Error(`${rawFileMatch.resource} is not correctly configured as a child of ${this.normalizedResource}`); } } From 91749da58973550134a50f32f135121cb2f9a14b Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 7 Jul 2023 11:09:19 +0200 Subject: [PATCH 236/926] use troubleshoot command for confirmation needed action (#187244) --- .github/commands.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/commands.json b/.github/commands.json index 200a47b18c0..15c6cd953c5 100644 --- a/.github/commands.json +++ b/.github/commands.json @@ -215,7 +215,7 @@ "action": "updateLabels", "addLabel": "info-needed", "removeLabel": "~confirmation-needed", - "comment": "Please perform the following **three tasks** to diagnose the root cause of the issue:\n\n* [ ] **1.) Disable Extensions**\n * Select `View` and pick `Command Palette...`\n * Run `Developer: Reload With Extensions Disabled`\n * 👉 See if the issue reproduces\n\n* [ ] **2.) Disable Configuration**\n * Select `View` and pick `Command Palette...`\n * Run `Profiles: Create a Temporary Profile`\n * 👉 See if the issue reproduces\n\n* [ ] **3.) Try VS Code Insiders**\n * Download [VS Code Insiders](https://code.visualstudio.com/insiders/)\n * Install and Run it\n * 👉 See if the issue reproduces\n \nThen pick one of the three resolutions depending on which step has helped:\n\n

\n Disabling my Extensions helped\n\nPlease run the command `Start Extension Bisect` and follow the instructions to find the extension that is causing this issue.\n\nimage\n\nPlease report the issue to the extension causing this.\n
\n\n
\n Disabling my configuration helped\nPlease report back more details about your configuration, including settings.\n
\n\n
\n Using VS Code Insiders has helped\n✅ This likely means that the issue has been addressed already and will be available in an upcoming release. You can safely use VS Code Insiders until the new stable version is available.\n
" + "comment": "Please diagnose the root cause of the issue by running the command `F1 > Help: Troubleshoot Issue` and following the instructions. Once you have done that, please update the issue with the results.\n\nHappy Coding!" }, { "type": "comment", From 4c1e4bb5c570d3e27d8d3df5eeb0a8f5da632b4e Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Fri, 7 Jul 2023 13:42:47 +0200 Subject: [PATCH 237/926] Allow additional socks schemes (#158669) --- src/vs/platform/request/common/request.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/request/common/request.ts b/src/vs/platform/request/common/request.ts index 58de4472569..42168d7a59c 100644 --- a/src/vs/platform/request/common/request.ts +++ b/src/vs/platform/request/common/request.ts @@ -138,7 +138,7 @@ function registerProxyConfigurations(scope: ConfigurationScope): void { properties: { 'http.proxy': { type: 'string', - pattern: '^(https?|socks5?)://([^:]*(:[^@]*)?@)?([^:]+|\\[[:0-9a-fA-F]+\\])(:\\d+)?/?$|^$', + pattern: '^(https?|socks|socks4a?|socks5h?)://([^:]*(:[^@]*)?@)?([^:]+|\\[[:0-9a-fA-F]+\\])(:\\d+)?/?$|^$', markdownDescription: localize('proxy', "The proxy setting to use. If not set, will be inherited from the `http_proxy` and `https_proxy` environment variables."), restricted: true }, From e7776fdf9c46a7ba612b049b2a923c594f4d9bf1 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Fri, 7 Jul 2023 14:42:49 +0200 Subject: [PATCH 238/926] Fixes #186402 (#187184) --- .../browser/widget/diffEditorWidget2/diffEditorWidget2.ts | 5 +++++ src/vs/editor/browser/widget/diffEditorWidget2/diffReview.ts | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts index bf02f92991b..7930581e426 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts @@ -315,6 +315,11 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { override getModel(): IDiffEditorModel | null { return this._diffModel.get()?.model ?? null; } override setModel(model: IDiffEditorModel | null | IDiffEditorViewModel): void { + if (!model && this._diffModel.get()) { + // Transitioning from a model to no-model + this._reviewPane.hide(); + } + const vm = model ? ('model' in model) ? model : this.createViewModel(model) : undefined; this._editors.original.setModel(vm ? vm.model.original : null); this._editors.modified.setModel(vm ? vm.model.modified : null); diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffReview.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffReview.ts index f9821f1f4a6..ed1c345f5e5 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffReview.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffReview.ts @@ -277,7 +277,7 @@ export class DiffReview2 extends Disposable { } } - private hide(): void { + public hide(): void { this._isVisibleObs.set(false, undefined); this._diffEditor.focus(); this.layout(); From d1ae8a6ecc5a48cc7ede36dc7ddc3d218be0e75e Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Fri, 7 Jul 2023 16:29:09 +0200 Subject: [PATCH 239/926] Inline Completions: Implements yieldsTo (#187156) Fixes #https://github.com/microsoft/vscode-internalbacklog/issues/4055 --- src/vs/base/common/collections.ts | 8 ++ src/vs/editor/common/languages.ts | 16 ++++ .../browser/provideInlineCompletions.ts | 92 +++++++++++++++++-- src/vs/monaco.d.ts | 13 +++ .../api/browser/mainThreadLanguageFeatures.ts | 7 +- .../workbench/api/common/extHost.api.impl.ts | 7 +- .../workbench/api/common/extHost.protocol.ts | 2 +- .../api/common/extHostLanguageFeatures.ts | 5 +- ...e.proposed.inlineCompletionsAdditions.d.ts | 24 +++++ 9 files changed, 158 insertions(+), 16 deletions(-) diff --git a/src/vs/base/common/collections.ts b/src/vs/base/common/collections.ts index d8ee92f757e..95def40789d 100644 --- a/src/vs/base/common/collections.ts +++ b/src/vs/base/common/collections.ts @@ -101,4 +101,12 @@ export class SetMap { values.forEach(fn); } + + get(key: K): ReadonlySet { + const values = this.map.get(key); + if (!values) { + return new Set(); + } + return new Set(values); + } } diff --git a/src/vs/editor/common/languages.ts b/src/vs/editor/common/languages.ts index 60a04c23d0a..364998995e3 100644 --- a/src/vs/editor/common/languages.ts +++ b/src/vs/editor/common/languages.ts @@ -703,6 +703,8 @@ export interface InlineCompletions { provideInlineCompletions(model: model.ITextModel, position: Position, context: InlineCompletionContext, token: CancellationToken): ProviderResult; @@ -721,6 +723,20 @@ export interface InlineCompletionsProvider { // Important: Don't use position after the await calls, as the model could have been changed in the meantime! const defaultReplaceRange = getDefaultRange(position, model); - const providers = registry.all(model); - const providerResults = await Promise.all(providers.map(async provider => { - try { - const completions = await provider.provideInlineCompletions(model, position, context, token); - return ({ provider, completions }); - } catch (e) { - onUnexpectedExternalError(e); + + const multiMap = new SetMap>(); + for (const provider of providers) { + if (provider.groupId) { + multiMap.add(provider.groupId, provider); } - return ({ provider, completions: undefined }); - })); + } + + function getPreferredProviders(provider: InlineCompletionsProvider): InlineCompletionsProvider[] { + if (!provider.yieldsToGroupIds) { return []; } + const result: InlineCompletionsProvider[] = []; + for (const groupId of provider.yieldsToGroupIds || []) { + const providers = multiMap.get(groupId); + for (const p of providers) { + result.push(p); + } + } + return result; + } + + type Result = Promise | null | undefined>; + const states = new Map>, Result>(); + + const seen = new Set>>(); + function findPreferredProviderCircle(provider: InlineCompletionsProvider, stack: InlineCompletionsProvider[]): InlineCompletionsProvider[] | undefined { + stack = [...stack, provider]; + if (seen.has(provider)) { return stack; } + + seen.add(provider); + try { + const preferred = getPreferredProviders(provider); + for (const p of preferred) { + const c = findPreferredProviderCircle(p, stack); + if (c) { return c; } + } + } finally { + seen.delete(provider); + } + return undefined; + } + + function processProvider(provider: InlineCompletionsProvider): Result { + const state = states.get(provider); + if (state) { + return state; + } + + const circle = findPreferredProviderCircle(provider, []); + if (circle) { + onUnexpectedExternalError(new Error(`Inline completions: cyclic yield-to dependency detected. Path: ${circle.map(s => s.toString ? s.toString() : ('' + s)).join(' -> ')}`)); + } + + const deferredPromise = new DeferredPromise | null | undefined>(); + states.set(provider, deferredPromise.p); + + (async () => { + if (!circle) { + const preferred = getPreferredProviders(provider); + for (const p of preferred) { + const result = await processProvider(p); + if (result && result.items.length > 0) { + // Skip provider + return undefined; + } + } + } + + try { + const completions = await provider.provideInlineCompletions(model, position, context, token); + return completions; + } catch (e) { + onUnexpectedExternalError(e); + return undefined; + } + })().then(c => deferredPromise.complete(c), e => deferredPromise.error(e)); + + return deferredPromise.p; + } + + const providerResults = await Promise.all(providers.map(async provider => ({ provider, completions: await processProvider(provider) }))); const itemsByHash = new Map(); const lists: InlineCompletionList[] = []; diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 001b8a6de24..d59db506ee1 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -7090,6 +7090,8 @@ declare namespace monaco.languages { readonly enableForwardStability?: boolean | undefined; } + export type InlineCompletionProviderGroupId = string; + export interface InlineCompletionsProvider { provideInlineCompletions(model: editor.ITextModel, position: Position, context: InlineCompletionContext, token: CancellationToken): ProviderResult; /** @@ -7105,6 +7107,17 @@ declare namespace monaco.languages { * Will be called when a completions list is no longer in use and can be garbage-collected. */ freeInlineCompletions(completions: T): void; + /** + * Only used for {@link yieldsToGroupIds}. + * Multiple providers can have the same group id. + */ + groupId?: InlineCompletionProviderGroupId; + /** + * Returns a list of preferred provider {@link groupId}s. + * The current provider is only requested for completions if no provider with a preferred group id returned a result. + */ + yieldsToGroupIds?: InlineCompletionProviderGroupId[]; + toString?(): string; } export interface CodeAction { diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index c34ff9c91da..732637669fb 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -557,7 +557,7 @@ export class MainThreadLanguageFeatures extends Disposable implements MainThread this._registrations.set(handle, this._languageFeaturesService.completionProvider.register(selector, provider)); } - $registerInlineCompletionsSupport(handle: number, selector: IDocumentFilterDto[], supportsHandleEvents: boolean): void { + $registerInlineCompletionsSupport(handle: number, selector: IDocumentFilterDto[], supportsHandleEvents: boolean, extensionId: string, yieldsToExtensionIds: string[]): void { const provider: languages.InlineCompletionsProvider = { provideInlineCompletions: async (model: ITextModel, position: EditorPosition, context: languages.InlineCompletionContext, token: CancellationToken): Promise => { return this._proxy.$provideInlineCompletions(handle, model.uri, position, context, token); @@ -574,6 +574,11 @@ export class MainThreadLanguageFeatures extends Disposable implements MainThread }, freeInlineCompletions: (completions: IdentifiableInlineCompletions): void => { this._proxy.$freeInlineCompletionsList(handle, completions.pid); + }, + groupId: extensionId, + yieldsToGroupIds: yieldsToExtensionIds, + toString() { + return `InlineCompletionsProvider(${extensionId})`; } }; this._registrations.set(handle, this._languageFeaturesService.inlineCompletionsProvider.register(selector, provider)); diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 56f66abde2f..1e585c0d3b0 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -578,14 +578,17 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I registerCompletionItemProvider(selector: vscode.DocumentSelector, provider: vscode.CompletionItemProvider, ...triggerCharacters: string[]): vscode.Disposable { return extHostLanguageFeatures.registerCompletionItemProvider(extension, checkSelector(selector), provider, triggerCharacters); }, - registerInlineCompletionItemProvider(selector: vscode.DocumentSelector, provider: vscode.InlineCompletionItemProvider): vscode.Disposable { + registerInlineCompletionItemProvider(selector: vscode.DocumentSelector, provider: vscode.InlineCompletionItemProvider, metadata?: vscode.InlineCompletionItemProviderMetadata): vscode.Disposable { if (provider.handleDidShowCompletionItem) { checkProposedApiEnabled(extension, 'inlineCompletionsAdditions'); } if (provider.handleDidPartiallyAcceptCompletionItem) { checkProposedApiEnabled(extension, 'inlineCompletionsAdditions'); } - return extHostLanguageFeatures.registerInlineCompletionsProvider(extension, checkSelector(selector), provider); + if (metadata) { + checkProposedApiEnabled(extension, 'inlineCompletionsAdditions'); + } + return extHostLanguageFeatures.registerInlineCompletionsProvider(extension, checkSelector(selector), provider, metadata); }, registerDocumentLinkProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentLinkProvider): vscode.Disposable { return extHostLanguageFeatures.registerDocumentLinkProvider(extension, checkSelector(selector), provider); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index c8b82a40e92..413980ad9b3 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -407,7 +407,7 @@ export interface MainThreadLanguageFeaturesShape extends IDisposable { $emitDocumentSemanticTokensEvent(eventHandle: number): void; $registerDocumentRangeSemanticTokensProvider(handle: number, selector: IDocumentFilterDto[], legend: languages.SemanticTokensLegend): void; $registerCompletionsProvider(handle: number, selector: IDocumentFilterDto[], triggerCharacters: string[], supportsResolveDetails: boolean, extensionId: ExtensionIdentifier): void; - $registerInlineCompletionsSupport(handle: number, selector: IDocumentFilterDto[], supportsHandleDidShowCompletionItem: boolean): void; + $registerInlineCompletionsSupport(handle: number, selector: IDocumentFilterDto[], supportsHandleDidShowCompletionItem: boolean, extensionId: string, yieldsToExtensionIds: string[]): void; $registerSignatureHelpProvider(handle: number, selector: IDocumentFilterDto[], metadata: ISignatureHelpProviderMetadataDto): void; $registerInlayHintsProvider(handle: number, selector: IDocumentFilterDto[], supportsResolve: boolean, eventHandle: number | undefined, displayName: string | undefined): void; $emitInlayHintsEvent(eventHandle: number): void; diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index 8c871feda78..78ecbea0a85 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -2249,10 +2249,11 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF // --- ghost test - registerInlineCompletionsProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.InlineCompletionItemProvider): vscode.Disposable { + registerInlineCompletionsProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.InlineCompletionItemProvider, metadata: vscode.InlineCompletionItemProviderMetadata | undefined): vscode.Disposable { const adapter = new InlineCompletionAdapter(extension, this._documents, provider, this._commands.converter); const handle = this._addNewAdapter(adapter, extension); - this._proxy.$registerInlineCompletionsSupport(handle, this._transformDocumentSelector(selector, extension), adapter.supportsHandleEvents); + this._proxy.$registerInlineCompletionsSupport(handle, this._transformDocumentSelector(selector, extension), adapter.supportsHandleEvents, + ExtensionIdentifier.toKey(extension.identifier.value), metadata?.yieldTo?.map(extId => ExtensionIdentifier.toKey(extId)) || []); return this._createDisposable(handle); } diff --git a/src/vscode-dts/vscode.proposed.inlineCompletionsAdditions.d.ts b/src/vscode-dts/vscode.proposed.inlineCompletionsAdditions.d.ts index 8412aca06ed..c38c4e23671 100644 --- a/src/vscode-dts/vscode.proposed.inlineCompletionsAdditions.d.ts +++ b/src/vscode-dts/vscode.proposed.inlineCompletionsAdditions.d.ts @@ -7,6 +7,22 @@ declare module 'vscode' { // https://github.com/microsoft/vscode/issues/124024 @hediet @alexdima + export namespace languages { + /** + * Registers an inline completion provider. + * + * Multiple providers can be registered for a language. In that case providers are asked in + * parallel and the results are merged. A failing provider (rejected promise or exception) will + * not cause a failure of the whole operation. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider An inline completion provider. + * @param metadata Metadata about the provider. + * @return A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerInlineCompletionItemProvider(selector: DocumentSelector, provider: InlineCompletionItemProvider, metadata: InlineCompletionItemProviderMetadata): Disposable; + } + export interface InlineCompletionItem { /** * If set to `true`, unopened closing brackets are removed and unclosed opening brackets are closed. @@ -15,6 +31,14 @@ declare module 'vscode' { completeBracketPairs?: boolean; } + export interface InlineCompletionItemProviderMetadata { + /** + * Specifies a list of extension ids that this provider yields to if they return a result. + * If some inline completion provider registered by such an extension returns a result, this provider is not asked. + */ + yieldTo: string[]; + } + export interface InlineCompletionItemProvider { /** * @param completionItem The completion item that was shown. From f639cabe2d5d11b4fc608e9008aa47dabf0f100c Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 7 Jul 2023 08:33:46 -0700 Subject: [PATCH 240/926] Log latency stats and remove old unused latency mechanism Fixes #187274 --- src/vs/platform/terminal/common/terminal.ts | 12 +++++++-- .../platform/terminal/node/ptyHostService.ts | 16 ++++++++--- src/vs/platform/terminal/node/ptyService.ts | 9 +++---- .../platform/terminal/node/terminalProcess.ts | 4 --- src/vs/server/node/remoteTerminalChannel.ts | 1 + .../api/browser/mainThreadTerminalService.ts | 23 ---------------- .../api/common/extHostTerminalService.ts | 4 --- .../contrib/terminal/browser/remotePty.ts | 4 --- .../terminal/browser/remoteTerminalBackend.ts | 16 ++++++++++- .../browser/terminalProcessExtHostProxy.ts | 17 ------------ .../browser/terminalProcessManager.ts | 25 +++++++---------- .../common/remote/remoteTerminalChannel.ts | 5 +++- .../terminal/common/remote/terminal.ts | 1 + .../contrib/terminal/common/terminal.ts | 3 --- .../terminal/electron-sandbox/localPty.ts | 4 --- .../electron-sandbox/localTerminalBackend.ts | 27 ++++++++++++++++++- .../browser/terminalProcessManager.test.ts | 1 - .../common/embedderTerminalService.ts | 3 --- 18 files changed, 82 insertions(+), 93 deletions(-) diff --git a/src/vs/platform/terminal/common/terminal.ts b/src/vs/platform/terminal/common/terminal.ts index 550e3dff813..a73f7a7f9fa 100644 --- a/src/vs/platform/terminal/common/terminal.ts +++ b/src/vs/platform/terminal/common/terminal.ts @@ -300,6 +300,10 @@ export interface IPtyService { */ listProcesses(): Promise; getPerformanceMarks(): Promise; + /** + * Measures and returns the latency of the current and all other processes to the pty host. + */ + getLatency(): Promise; start(id: number): Promise; shutdown(id: number, immediate: boolean): Promise; @@ -308,7 +312,6 @@ export interface IPtyService { clearBuffer(id: number): Promise; getInitialCwd(id: number): Promise; getCwd(id: number): Promise; - getLatency(id: number): Promise; acknowledgeDataEvent(id: number, charCount: number): Promise; setUnicodeVersion(id: number, version: '6' | '11'): Promise; processBinary(id: number, data: string): Promise; @@ -367,6 +370,11 @@ export interface IPtyHostController { export interface IPtyHostService extends IPtyService, IPtyHostController { } +export interface IPtyHostLatencyMeasurement { + label: string; + latency: number; +} + /** * Serialized terminal state matching the interface that can be used across versions, the version * should be verified before using the state payload. @@ -739,7 +747,6 @@ export interface ITerminalChildProcess { getInitialCwd(): Promise; getCwd(): Promise; - getLatency(): Promise; refreshProperty(property: T): Promise; updateProperty(property: T, value: IProcessPropertyMap[T]): Promise; } @@ -992,6 +999,7 @@ export interface ITerminalBackend { attachToProcess(id: number): Promise; attachToRevivedProcess(id: number): Promise; listProcesses(): Promise; + getLatency(): Promise; getDefaultSystemShell(osOverride?: OperatingSystem): Promise; getProfiles(profiles: unknown, defaultProfile: unknown, includeDetectedProfiles?: boolean): Promise; getWslPath(original: string, direction: 'unix-to-win' | 'win-to-unix'): Promise; diff --git a/src/vs/platform/terminal/node/ptyHostService.ts b/src/vs/platform/terminal/node/ptyHostService.ts index 59b84827479..a9b9a2e57ae 100644 --- a/src/vs/platform/terminal/node/ptyHostService.ts +++ b/src/vs/platform/terminal/node/ptyHostService.ts @@ -13,13 +13,14 @@ import { RemoteLoggerChannelClient } from 'vs/platform/log/common/logIpc'; import { getResolvedShellEnv } from 'vs/platform/shell/node/shellEnv'; import { IPtyHostProcessReplayEvent } from 'vs/platform/terminal/common/capabilities/capabilities'; import { RequestStore } from 'vs/platform/terminal/common/requestStore'; -import { HeartbeatConstants, IHeartbeatService, IProcessDataEvent, IProcessProperty, IProcessPropertyMap, IProcessReadyEvent, IPtyHostService, IPtyService, IRequestResolveVariablesEvent, ISerializedTerminalState, IShellLaunchConfig, ITerminalLaunchError, ITerminalProcessOptions, ITerminalProfile, ITerminalsLayoutInfo, ProcessPropertyType, TerminalIcon, TerminalIpcChannels, TerminalSettingId, TitleEventSource } from 'vs/platform/terminal/common/terminal'; +import { HeartbeatConstants, IHeartbeatService, IProcessDataEvent, IProcessProperty, IProcessPropertyMap, IProcessReadyEvent, IPtyHostLatencyMeasurement, IPtyHostService, IPtyService, IRequestResolveVariablesEvent, ISerializedTerminalState, IShellLaunchConfig, ITerminalLaunchError, ITerminalProcessOptions, ITerminalProfile, ITerminalsLayoutInfo, ProcessPropertyType, TerminalIcon, TerminalIpcChannels, TerminalSettingId, TitleEventSource } from 'vs/platform/terminal/common/terminal'; import { registerTerminalPlatformConfiguration } from 'vs/platform/terminal/common/terminalPlatformConfiguration'; import { IGetTerminalLayoutInfoArgs, IProcessDetails, ISetTerminalLayoutInfoArgs } from 'vs/platform/terminal/common/terminalProcess'; import { IPtyHostConnection, IPtyHostStarter } from 'vs/platform/terminal/node/ptyHost'; import { detectAvailableProfiles } from 'vs/platform/terminal/node/terminalProfiles'; import * as performance from 'vs/base/common/performance'; import { getSystemShell } from 'vs/base/node/shell'; +import { StopWatch } from 'vs/base/common/stopwatch'; enum Constants { MaxRestarts = 5 @@ -272,8 +273,17 @@ export class PtyHostService extends Disposable implements IPtyHostService { getCwd(id: number): Promise { return this._proxy.getCwd(id); } - getLatency(id: number): Promise { - return this._proxy.getLatency(id); + async getLatency(): Promise { + const sw = new StopWatch(); + const results = await this._proxy.getLatency(); + sw.stop(); + return [ + { + label: 'ptyhostservice<->ptyhost', + latency: sw.elapsed() + }, + ...results + ]; } orphanQuestionReply(id: number): Promise { return this._proxy.orphanQuestionReply(id); diff --git a/src/vs/platform/terminal/node/ptyService.ts b/src/vs/platform/terminal/node/ptyService.ts index 564b87612f3..4661924d5dc 100644 --- a/src/vs/platform/terminal/node/ptyService.ts +++ b/src/vs/platform/terminal/node/ptyService.ts @@ -12,7 +12,7 @@ import { URI } from 'vs/base/common/uri'; import { getSystemShell } from 'vs/base/node/shell'; import { ILogService, LogLevel } from 'vs/platform/log/common/log'; import { RequestStore } from 'vs/platform/terminal/common/requestStore'; -import { IProcessDataEvent, IProcessReadyEvent, IPtyService, IRawTerminalInstanceLayoutInfo, IReconnectConstants, IShellLaunchConfig, ITerminalInstanceLayoutInfoById, ITerminalLaunchError, ITerminalsLayoutInfo, ITerminalTabLayoutInfoById, TerminalIcon, IProcessProperty, TitleEventSource, ProcessPropertyType, IProcessPropertyMap, IFixedTerminalDimensions, IPersistentTerminalProcessLaunchConfig, ICrossVersionSerializedTerminalState, ISerializedTerminalState, ITerminalProcessOptions } from 'vs/platform/terminal/common/terminal'; +import { IProcessDataEvent, IProcessReadyEvent, IPtyService, IRawTerminalInstanceLayoutInfo, IReconnectConstants, IShellLaunchConfig, ITerminalInstanceLayoutInfoById, ITerminalLaunchError, ITerminalsLayoutInfo, ITerminalTabLayoutInfoById, TerminalIcon, IProcessProperty, TitleEventSource, ProcessPropertyType, IProcessPropertyMap, IFixedTerminalDimensions, IPersistentTerminalProcessLaunchConfig, ICrossVersionSerializedTerminalState, ISerializedTerminalState, ITerminalProcessOptions, IPtyHostLatencyMeasurement } from 'vs/platform/terminal/common/terminal'; import { TerminalDataBufferer } from 'vs/platform/terminal/common/terminalDataBuffering'; import { escapeNonWindowsPath } from 'vs/platform/terminal/common/terminalEnvironment'; import { Terminal as XtermTerminal } from 'xterm-headless'; @@ -399,8 +399,8 @@ export class PtyService extends Disposable implements IPtyService { return this._throwIfNoPty(id).setUnicodeVersion(version); } @traceRpc - async getLatency(id: number): Promise { - return 0; + async getLatency(): Promise { + return []; } @traceRpc async orphanQuestionReply(id: number): Promise { @@ -874,9 +874,6 @@ class PersistentTerminalProcess extends Disposable { getCwd(): Promise { return this._terminalProcess.getCwd(); } - getLatency(): Promise { - return this._terminalProcess.getLatency(); - } async triggerReplay(): Promise { if (this._interactionState.value === InteractionState.None) { diff --git a/src/vs/platform/terminal/node/terminalProcess.ts b/src/vs/platform/terminal/node/terminalProcess.ts index 467007e8728..bfd620a0d60 100644 --- a/src/vs/platform/terminal/node/terminalProcess.ts +++ b/src/vs/platform/terminal/node/terminalProcess.ts @@ -627,10 +627,6 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess return this._initialCwd; } - getLatency(): Promise { - return Promise.resolve(0); - } - getWindowsPty(): IProcessReadyWindowsPty | undefined { return isWindows ? { backend: 'useConpty' in this._ptyOptions && this._ptyOptions.useConpty ? 'conpty' : 'winpty', diff --git a/src/vs/server/node/remoteTerminalChannel.ts b/src/vs/server/node/remoteTerminalChannel.ts index df9943918ee..0918a4855a2 100644 --- a/src/vs/server/node/remoteTerminalChannel.ts +++ b/src/vs/server/node/remoteTerminalChannel.ts @@ -117,6 +117,7 @@ export class RemoteTerminalChannel extends Disposable implements IServerChannel< case RemoteTerminalChannelRequest.DetachFromProcess: return this._ptyHostService.detachFromProcess.apply(this._ptyHostService, args); case RemoteTerminalChannelRequest.ListProcesses: return this._ptyHostService.listProcesses.apply(this._ptyHostService, args); + case RemoteTerminalChannelRequest.GetLatency: return this._ptyHostService.getLatency.apply(this._ptyHostService, args); case RemoteTerminalChannelRequest.GetPerformanceMarks: return this._ptyHostService.getPerformanceMarks.apply(this._ptyHostService, args); case RemoteTerminalChannelRequest.OrphanQuestionReply: return this._ptyHostService.orphanQuestionReply.apply(this._ptyHostService, args); case RemoteTerminalChannelRequest.AcceptPtyHostResolvedVariables: return this._ptyHostService.acceptPtyHostResolvedVariables.apply(this._ptyHostService, args); diff --git a/src/vs/workbench/api/browser/mainThreadTerminalService.ts b/src/vs/workbench/api/browser/mainThreadTerminalService.ts index cedec7ba50a..b2f7c9a7dd3 100644 --- a/src/vs/workbench/api/browser/mainThreadTerminalService.ts +++ b/src/vs/workbench/api/browser/mainThreadTerminalService.ts @@ -7,7 +7,6 @@ import { DisposableStore, Disposable, IDisposable, MutableDisposable } from 'vs/ import { ExtHostContext, ExtHostTerminalServiceShape, MainThreadTerminalServiceShape, MainContext, TerminalLaunchConfig, ITerminalDimensionsDto, ExtHostTerminalIdentifier, TerminalQuickFix } from 'vs/workbench/api/common/extHost.protocol'; import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers'; import { URI } from 'vs/base/common/uri'; -import { StopWatch } from 'vs/base/common/stopwatch'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; import { IProcessProperty, IProcessReadyWindowsPty, IShellLaunchConfig, IShellLaunchConfigDto, ITerminalOutputMatch, ITerminalOutputMatcher, ProcessPropertyType, TerminalExitReason, TerminalLocation } from 'vs/platform/terminal/common/terminal'; @@ -368,7 +367,6 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape proxy.onShutdown(immediate => this._proxy.$acceptProcessShutdown(proxy.instanceId, immediate)); proxy.onRequestCwd(() => this._proxy.$acceptProcessRequestCwd(proxy.instanceId)); proxy.onRequestInitialCwd(() => this._proxy.$acceptProcessRequestInitialCwd(proxy.instanceId)); - proxy.onRequestLatency(() => this._onRequestLatency(proxy.instanceId)); } public $sendProcessData(terminalId: number, data: string): void { @@ -387,27 +385,6 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape this._terminalProcessProxies.get(terminalId)?.emitProcessProperty(property); } - private async _onRequestLatency(terminalId: number): Promise { - const COUNT = 2; - let sum = 0; - for (let i = 0; i < COUNT; i++) { - const sw = StopWatch.create(); - await this._proxy.$acceptProcessRequestLatency(terminalId); - sw.stop(); - sum += sw.elapsed(); - } - this._getTerminalProcess(terminalId)?.emitLatency(sum / COUNT); - } - - private _getTerminalProcess(terminalId: number): ITerminalProcessExtHostProxy | undefined { - const terminal = this._terminalProcessProxies.get(terminalId); - if (!terminal) { - this._logService.error(`Unknown terminal: ${terminalId}`); - return undefined; - } - return terminal; - } - $setEnvironmentVariableCollection(extensionIdentifier: string, persistent: boolean, collection: ISerializableEnvironmentVariableCollection | undefined, descriptionMap: ISerializableEnvironmentDescriptionMap): void { if (collection) { const translatedCollection = { diff --git a/src/vs/workbench/api/common/extHostTerminalService.ts b/src/vs/workbench/api/common/extHostTerminalService.ts index 03d4899d196..a6c7fd37e18 100644 --- a/src/vs/workbench/api/common/extHostTerminalService.ts +++ b/src/vs/workbench/api/common/extHostTerminalService.ts @@ -313,10 +313,6 @@ class ExtHostPseudoterminal implements ITerminalChildProcess { return Promise.resolve(''); } - getLatency(): Promise { - return Promise.resolve(0); - } - startSendingEvents(initialDimensions: ITerminalDimensionsDto | undefined): void { // Attach the listeners this._pty.onDidWrite(e => this._onProcessData.fire(e)); diff --git a/src/vs/workbench/contrib/terminal/browser/remotePty.ts b/src/vs/workbench/contrib/terminal/browser/remotePty.ts index 112b0bb80d2..8ca70a8726d 100644 --- a/src/vs/workbench/contrib/terminal/browser/remotePty.ts +++ b/src/vs/workbench/contrib/terminal/browser/remotePty.ts @@ -209,8 +209,4 @@ export class RemotePty extends Disposable implements ITerminalChildProcess { handleOrphanQuestion() { this._remoteTerminalChannel.orphanQuestionReply(this.id); } - - async getLatency(): Promise { - return 0; - } } diff --git a/src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts b/src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts index 3490820c749..4c6bfa6affc 100644 --- a/src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts +++ b/src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts @@ -8,6 +8,7 @@ import { Emitter } from 'vs/base/common/event'; import { revive } from 'vs/base/common/marshalling'; import { PerformanceMark, mark } from 'vs/base/common/performance'; import { IProcessEnvironment, OperatingSystem } from 'vs/base/common/platform'; +import { StopWatch } from 'vs/base/common/stopwatch'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -15,7 +16,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ISerializedTerminalCommand } from 'vs/platform/terminal/common/capabilities/capabilities'; -import { IShellLaunchConfig, IShellLaunchConfigDto, ITerminalBackend, ITerminalBackendRegistry, ITerminalChildProcess, ITerminalEnvironment, ITerminalLogService, ITerminalProcessOptions, ITerminalProfile, ITerminalsLayoutInfo, ITerminalsLayoutInfoById, ProcessPropertyType, TerminalExtensions, TerminalIcon, TerminalSettingId, TitleEventSource } from 'vs/platform/terminal/common/terminal'; +import { IPtyHostLatencyMeasurement, IShellLaunchConfig, IShellLaunchConfigDto, ITerminalBackend, ITerminalBackendRegistry, ITerminalChildProcess, ITerminalEnvironment, ITerminalLogService, ITerminalProcessOptions, ITerminalProfile, ITerminalsLayoutInfo, ITerminalsLayoutInfoById, ProcessPropertyType, TerminalExtensions, TerminalIcon, TerminalSettingId, TitleEventSource } from 'vs/platform/terminal/common/terminal'; import { IProcessDetails } from 'vs/platform/terminal/common/terminalProcess'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; @@ -260,6 +261,19 @@ class RemoteTerminalBackend extends BaseTerminalBackend implements ITerminalBack return this._remoteTerminalChannel.listProcesses(); } + async getLatency(): Promise { + const sw = new StopWatch(); + const results = await this._remoteTerminalChannel.getLatency(); + sw.stop(); + return [ + { + label: 'window<->ptyhost', + latency: sw.elapsed() + }, + ...results + ]; + } + async updateProperty(id: number, property: T, value: any): Promise { await this._remoteTerminalChannel.updateProperty(id, property, value); } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProcessExtHostProxy.ts b/src/vs/workbench/contrib/terminal/browser/terminalProcessExtHostProxy.ts index 3c3e980b9c6..2fc4c40f94c 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProcessExtHostProxy.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProcessExtHostProxy.ts @@ -34,17 +34,13 @@ export class TerminalProcessExtHostProxy extends Disposable implements ITerminal readonly onRequestInitialCwd: Event = this._onRequestInitialCwd.event; private readonly _onRequestCwd = this._register(new Emitter()); readonly onRequestCwd: Event = this._onRequestCwd.event; - private readonly _onRequestLatency = this._register(new Emitter()); - readonly onRequestLatency: Event = this._onRequestLatency.event; private readonly _onDidChangeProperty = this._register(new Emitter>()); readonly onDidChangeProperty = this._onDidChangeProperty.event; private readonly _onProcessExit = this._register(new Emitter()); readonly onProcessExit: Event = this._onProcessExit.event; - private _pendingInitialCwdRequests: ((value: string | PromiseLike) => void)[] = []; private _pendingCwdRequests: ((value: string | PromiseLike) => void)[] = []; - private _pendingLatencyRequests: ((value: number | PromiseLike) => void)[] = []; constructor( public instanceId: number, @@ -112,12 +108,6 @@ export class TerminalProcessExtHostProxy extends Disposable implements ITerminal } } - emitLatency(latency: number): void { - while (this._pendingLatencyRequests.length > 0) { - this._pendingLatencyRequests.pop()!(latency); - } - } - async start(): Promise { return this._terminalService.requestStartExtensionTerminal(this, this._cols, this._rows); } @@ -165,13 +155,6 @@ export class TerminalProcessExtHostProxy extends Disposable implements ITerminal }); } - getLatency(): Promise { - return new Promise(resolve => { - this._onRequestLatency.fire(); - this._pendingLatencyRequests.push(resolve); - }); - } - async refreshProperty(type: T): Promise { // throws if called in extHostTerminalService } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts index 16e37b75b15..b1add9a6c84 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts @@ -38,6 +38,7 @@ import Severity from 'vs/base/common/severity'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IEnvironmentVariableCollection, IMergedEnvironmentVariableCollection } from 'vs/platform/terminal/common/environmentVariable'; import { generateUuid } from 'vs/base/common/uuid'; +import { runWhenIdle } from 'vs/base/common/async'; const enum ProcessConstants { /** @@ -79,8 +80,6 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce private _process: ITerminalChildProcess | null = null; private _processType: ProcessType = ProcessType.Process; private _preLaunchInputQueue: string[] = []; - private _latency: number = -1; - private _latencyLastMeasured: number = 0; private _initialCwd: string | undefined; private _extEnvironmentVariableCollection: IMergedEnvironmentVariableCollection | undefined; private _ackDataBufferer: AckDataBufferer; @@ -152,7 +151,6 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce super(); this._cwdWorkspaceFolder = terminalEnvironment.getWorkspaceForTerminal(cwd, this._workspaceContextService, this._historyService); this.ptyProcessReady = this._createPtyProcessReadyPromise(); - this.getLatency(); this._ackDataBufferer = new AckDataBufferer(e => this._process?.acknowledgeDataEvent(e)); this._dataFilter = this._instantiationService.createInstance(SeamlessRelaunchDataFilter); this._dataFilter.onProcessData(ev => { @@ -394,6 +392,14 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce // Error return result; } + + // Report the latency to the pty host when idle + runWhenIdle(() => { + this.backend?.getLatency().then(measurements => { + this._logService.info(`Latency measurements for ${this.remoteAuthority ?? 'local'} backend\n${measurements.map(e => `${e.label}: ${e.latency.toFixed(2)}ms`).join('\n')}`); + }); + }); + return undefined; } @@ -605,19 +611,6 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce return this._initialCwd ?? ''; } - async getLatency(): Promise { - await this.ptyProcessReady; - if (!this._process) { - return Promise.resolve(0); - } - if (this._latencyLastMeasured === 0 || this._latencyLastMeasured + ProcessConstants.LatencyMeasuringInterval < Date.now()) { - const latencyRequest = this._process.getLatency(); - this._latency = await latencyRequest; - this._latencyLastMeasured = Date.now(); - } - return Promise.resolve(this._latency); - } - async refreshProperty(type: T): Promise { if (!this._process) { throw new Error('Cannot refresh property when process is not set'); diff --git a/src/vs/workbench/contrib/terminal/common/remote/remoteTerminalChannel.ts b/src/vs/workbench/contrib/terminal/common/remote/remoteTerminalChannel.ts index c66a1ec185a..11b60a4f92f 100644 --- a/src/vs/workbench/contrib/terminal/common/remote/remoteTerminalChannel.ts +++ b/src/vs/workbench/contrib/terminal/common/remote/remoteTerminalChannel.ts @@ -17,7 +17,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { Schemas } from 'vs/base/common/network'; import { ILabelService } from 'vs/platform/label/common/label'; import { IEnvironmentVariableService } from 'vs/workbench/contrib/terminal/common/environmentVariable'; -import { IProcessDataEvent, IRequestResolveVariablesEvent, IShellLaunchConfigDto, ITerminalLaunchError, ITerminalProfile, ITerminalsLayoutInfo, ITerminalsLayoutInfoById, TerminalIcon, IProcessProperty, ProcessPropertyType, IProcessPropertyMap, TitleEventSource, ISerializedTerminalState, IPtyHostController, ITerminalProcessOptions, IProcessReadyEvent, ITerminalLogService } from 'vs/platform/terminal/common/terminal'; +import { IProcessDataEvent, IRequestResolveVariablesEvent, IShellLaunchConfigDto, ITerminalLaunchError, ITerminalProfile, ITerminalsLayoutInfo, ITerminalsLayoutInfoById, TerminalIcon, IProcessProperty, ProcessPropertyType, IProcessPropertyMap, TitleEventSource, ISerializedTerminalState, IPtyHostController, ITerminalProcessOptions, IProcessReadyEvent, ITerminalLogService, IPtyHostLatencyMeasurement } from 'vs/platform/terminal/common/terminal'; import { IGetTerminalLayoutInfoArgs, IProcessDetails, ISetTerminalLayoutInfoArgs } from 'vs/platform/terminal/common/terminalProcess'; import { IProcessEnvironment, OperatingSystem } from 'vs/base/common/platform'; import { ICompleteTerminalConfiguration } from 'vs/workbench/contrib/terminal/common/terminal'; @@ -203,6 +203,9 @@ export class RemoteTerminalChannelClient implements IPtyHostController { listProcesses(): Promise { return this._channel.call(RemoteTerminalChannelRequest.ListProcesses); } + getLatency(): Promise { + return this._channel.call(RemoteTerminalChannelRequest.GetLatency); + } getPerformanceMarks(): Promise { return this._channel.call(RemoteTerminalChannelRequest.GetPerformanceMarks); } diff --git a/src/vs/workbench/contrib/terminal/common/remote/terminal.ts b/src/vs/workbench/contrib/terminal/common/remote/terminal.ts index 22aa8cf6dc4..54498e30cff 100644 --- a/src/vs/workbench/contrib/terminal/common/remote/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/remote/terminal.ts @@ -63,6 +63,7 @@ export const enum RemoteTerminalChannelRequest { AttachToProcess = '$attachToProcess', DetachFromProcess = '$detachFromProcess', ListProcesses = '$listProcesses', + GetLatency = '$getLatency', GetPerformanceMarks = '$getPerformanceMarks', OrphanQuestionReply = '$orphanQuestionReply', AcceptPtyHostResolvedVariables = '$acceptPtyHostResolvedVariables', diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index aae1d4721bd..c1d5c806b2f 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -299,7 +299,6 @@ export interface ITerminalProcessManager extends IDisposable { acknowledgeDataEvent(charCount: number): void; processBinary(data: string): void; - getLatency(): Promise; refreshProperty(type: T): Promise; updateProperty(property: T, value: IProcessPropertyMap[T]): Promise; getBackendOS(): Promise; @@ -331,7 +330,6 @@ export interface ITerminalProcessExtHostProxy extends IDisposable { emitData(data: string): void; emitProcessProperty(property: IProcessProperty): void; emitReady(pid: number, cwd: string, windowsPty: IProcessReadyWindowsPty | undefined): void; - emitLatency(latency: number): void; emitExit(exitCode: number | undefined): void; onInput: Event; @@ -341,7 +339,6 @@ export interface ITerminalProcessExtHostProxy extends IDisposable { onShutdown: Event; onRequestInitialCwd: Event; onRequestCwd: Event; - onRequestLatency: Event; } export interface IStartExtensionTerminalRequest { diff --git a/src/vs/workbench/contrib/terminal/electron-sandbox/localPty.ts b/src/vs/workbench/contrib/terminal/electron-sandbox/localPty.ts index bb6b8bd174b..fd083280d28 100644 --- a/src/vs/workbench/contrib/terminal/electron-sandbox/localPty.ts +++ b/src/vs/workbench/contrib/terminal/electron-sandbox/localPty.ts @@ -102,10 +102,6 @@ export class LocalPty extends Disposable implements ITerminalChildProcess { async updateProperty(type: T, value: IProcessPropertyMap[T]): Promise { return this._proxy.updateProperty(this.id, type, value); } - getLatency(): Promise { - // TODO: The idea here was to add the result plus the time it took to get the latency - return this._proxy.getLatency(this.id); - } acknowledgeDataEvent(charCount: number): void { if (this._inReplay) { return; diff --git a/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts b/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts index 4f976936a55..5636c0ad660 100644 --- a/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts +++ b/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts @@ -12,7 +12,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { ILabelService } from 'vs/platform/label/common/label'; import { Registry } from 'vs/platform/registry/common/platform'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; -import { ILocalPtyService, IProcessPropertyMap, IPtyService, IShellLaunchConfig, ITerminalBackend, ITerminalBackendRegistry, ITerminalChildProcess, ITerminalEnvironment, ITerminalLogService, ITerminalProcessOptions, ITerminalsLayoutInfo, ITerminalsLayoutInfoById, ProcessPropertyType, TerminalExtensions, TerminalIpcChannels, TerminalSettingId, TitleEventSource } from 'vs/platform/terminal/common/terminal'; +import { ILocalPtyService, IProcessPropertyMap, IPtyHostLatencyMeasurement, IPtyService, IShellLaunchConfig, ITerminalBackend, ITerminalBackendRegistry, ITerminalChildProcess, ITerminalEnvironment, ITerminalLogService, ITerminalProcessOptions, ITerminalsLayoutInfo, ITerminalsLayoutInfoById, ProcessPropertyType, TerminalExtensions, TerminalIpcChannels, TerminalSettingId, TitleEventSource } from 'vs/platform/terminal/common/terminal'; import { IGetTerminalLayoutInfoArgs, IProcessDetails, ISetTerminalLayoutInfoArgs } from 'vs/platform/terminal/common/terminalProcess'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; @@ -36,6 +36,7 @@ import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecyc import { DeferredPromise } from 'vs/base/common/async'; import { IStatusbarService } from 'vs/workbench/services/statusbar/browser/statusbar'; import { memoize } from 'vs/base/common/decorators'; +import { StopWatch } from 'vs/base/common/stopwatch'; export class LocalTerminalBackendContribution implements IWorkbenchContribution { constructor( @@ -245,6 +246,30 @@ class LocalTerminalBackend extends BaseTerminalBackend implements ITerminalBacke return this._proxy.listProcesses(); } + async getLatency(): Promise { + const measurements: IPtyHostLatencyMeasurement[] = []; + const sw = new StopWatch(); + if (this._directProxy) { + await this._directProxy.getLatency(); + sw.stop(); + measurements.push({ + label: 'window<->ptyhost (message port)', + latency: sw.elapsed() + }); + sw.reset(); + } + const results = await this._localPtyService.getLatency(); + sw.stop(); + measurements.push({ + label: 'window<->main<->ptyhost', + latency: sw.elapsed() + }); + return [ + ...measurements, + ...results + ]; + } + async getPerformanceMarks(): Promise { return this._proxy.getPerformanceMarks(); } diff --git a/src/vs/workbench/contrib/terminal/test/browser/terminalProcessManager.test.ts b/src/vs/workbench/contrib/terminal/test/browser/terminalProcessManager.test.ts index 4d8b79c9d98..b7291fc85f4 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/terminalProcessManager.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/terminalProcessManager.test.ts @@ -51,7 +51,6 @@ class TestTerminalChildProcess implements ITerminalChildProcess { async setUnicodeVersion(version: '6' | '11'): Promise { } async getInitialCwd(): Promise { return ''; } async getCwd(): Promise { return ''; } - async getLatency(): Promise { return 0; } async processBinary(data: string): Promise { } refreshProperty(property: any): Promise { return Promise.resolve(''); } } diff --git a/src/vs/workbench/services/terminal/common/embedderTerminalService.ts b/src/vs/workbench/services/terminal/common/embedderTerminalService.ts index 61c4a433e09..5e322af7a0f 100644 --- a/src/vs/workbench/services/terminal/common/embedderTerminalService.ts +++ b/src/vs/workbench/services/terminal/common/embedderTerminalService.ts @@ -139,9 +139,6 @@ class EmbedderTerminalProcess extends Disposable implements ITerminalChildProces async getCwd(): Promise { return ''; } - async getLatency(): Promise { - return 0; - } refreshProperty(property: ProcessPropertyType): Promise { throw new Error(`refreshProperty is not suppported in EmbedderTerminalProcess. property: ${property}`); } From 6ea4677f1411796d33faa67165915fd67c6aff94 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 7 Jul 2023 08:39:10 -0700 Subject: [PATCH 241/926] Make ptyhostservice label consistent --- .../workbench/contrib/terminal/browser/remoteTerminalBackend.ts | 2 +- .../contrib/terminal/electron-sandbox/localTerminalBackend.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts b/src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts index 4c6bfa6affc..855d21f39c9 100644 --- a/src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts +++ b/src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts @@ -267,7 +267,7 @@ class RemoteTerminalBackend extends BaseTerminalBackend implements ITerminalBack sw.stop(); return [ { - label: 'window<->ptyhost', + label: 'window<->ptyhostservice<->ptyhost', latency: sw.elapsed() }, ...results diff --git a/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts b/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts index 5636c0ad660..a07bea61575 100644 --- a/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts +++ b/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts @@ -261,7 +261,7 @@ class LocalTerminalBackend extends BaseTerminalBackend implements ITerminalBacke const results = await this._localPtyService.getLatency(); sw.stop(); measurements.push({ - label: 'window<->main<->ptyhost', + label: 'window<->ptyhostservice<->ptyhost', latency: sw.elapsed() }); return [ From dc189ed8c32ca0edcf10b1a2297319cd9cb503df Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 7 Jul 2023 09:53:58 -0700 Subject: [PATCH 242/926] Ensure layout info is fetched from the pty host on reconnects Fixes #187282 --- .../platform/terminal/node/ptyHostService.ts | 5 +- src/vs/platform/terminal/node/ptyService.ts | 5 +- .../terminal/browser/remoteTerminalBackend.ts | 40 ++++++----- .../electron-sandbox/localTerminalBackend.ts | 66 +++++++++---------- 4 files changed, 58 insertions(+), 58 deletions(-) diff --git a/src/vs/platform/terminal/node/ptyHostService.ts b/src/vs/platform/terminal/node/ptyHostService.ts index a9b9a2e57ae..324e4063d95 100644 --- a/src/vs/platform/terminal/node/ptyHostService.ts +++ b/src/vs/platform/terminal/node/ptyHostService.ts @@ -326,7 +326,10 @@ export class PtyHostService extends Disposable implements IPtyHostService { return this._proxy.setTerminalLayoutInfo(args); } async getTerminalLayoutInfo(args: IGetTerminalLayoutInfoArgs): Promise { - return await this._proxy.getTerminalLayoutInfo(args); + // This is optional as we want reconnect requests to go through only if the pty host exists. + // Revive is handled specially as reviveTerminalProcesses is guaranteed to be called before + // the request for layout info. + return this._optionalProxy?.getTerminalLayoutInfo(args); } async requestDetachInstance(workspaceId: string, instanceId: number): Promise { diff --git a/src/vs/platform/terminal/node/ptyService.ts b/src/vs/platform/terminal/node/ptyService.ts index 4661924d5dc..7e017ab5ccd 100644 --- a/src/vs/platform/terminal/node/ptyService.ts +++ b/src/vs/platform/terminal/node/ptyService.ts @@ -552,8 +552,9 @@ export class PtyService extends Disposable implements IPtyService { }; } catch (e) { this._logService.warn(`Couldn't get layout info, a terminal was probably disconnected`, e.message); - this._logService.info('Reattach to wrong terminal debug info - layout info by id', t); - this._logService.info('Reattach to wrong terminal debug info - _revivePtyIdMap', Array.from(this._revivedPtyIdMap.values())); + this._logService.debug('Reattach to wrong terminal debug info - layout info by id', t); + this._logService.debug('Reattach to wrong terminal debug info - _revivePtyIdMap', Array.from(this._revivedPtyIdMap.values())); + this._logService.debug('Reattach to wrong terminal debug info - _ptys ids', Array.from(this._ptys.keys())); // this will be filtered out and not reconnected return { terminal: null, diff --git a/src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts b/src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts index 855d21f39c9..a893c53f6ae 100644 --- a/src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts +++ b/src/vs/workbench/contrib/terminal/browser/remoteTerminalBackend.ts @@ -337,29 +337,27 @@ class RemoteTerminalBackend extends BaseTerminalBackend implements ITerminalBack // Revive processes if needed const serializedState = this._storageService.get(TerminalStorageKeys.TerminalBufferState, StorageScope.WORKSPACE); - const parsed = this._deserializeTerminalState(serializedState); - if (!parsed) { - return undefined; - } + const reviveBufferState = this._deserializeTerminalState(serializedState); + if (reviveBufferState && reviveBufferState.length > 0) { + try { + // Note that remote terminals do not get their environment re-resolved unlike in local terminals - try { - // Note that remote terminals do not get their environment re-resolved unlike in local terminals - - mark('code/terminal/willReviveTerminalProcessesRemote'); - await this._remoteTerminalChannel.reviveTerminalProcesses(parsed, Intl.DateTimeFormat().resolvedOptions().locale); - mark('code/terminal/didReviveTerminalProcessesRemote'); - this._storageService.remove(TerminalStorageKeys.TerminalBufferState, StorageScope.WORKSPACE); - // If reviving processes, send the terminal layout info back to the pty host as it - // will not have been persisted on application exit - const layoutInfo = this._storageService.get(TerminalStorageKeys.TerminalLayoutInfo, StorageScope.WORKSPACE); - if (layoutInfo) { - mark('code/terminal/willSetTerminalLayoutInfoRemote'); - await this._remoteTerminalChannel.setTerminalLayoutInfo(JSON.parse(layoutInfo)); - mark('code/terminal/didSetTerminalLayoutInfoRemote'); - this._storageService.remove(TerminalStorageKeys.TerminalLayoutInfo, StorageScope.WORKSPACE); + mark('code/terminal/willReviveTerminalProcessesRemote'); + await this._remoteTerminalChannel.reviveTerminalProcesses(reviveBufferState, Intl.DateTimeFormat().resolvedOptions().locale); + mark('code/terminal/didReviveTerminalProcessesRemote'); + this._storageService.remove(TerminalStorageKeys.TerminalBufferState, StorageScope.WORKSPACE); + // If reviving processes, send the terminal layout info back to the pty host as it + // will not have been persisted on application exit + const layoutInfo = this._storageService.get(TerminalStorageKeys.TerminalLayoutInfo, StorageScope.WORKSPACE); + if (layoutInfo) { + mark('code/terminal/willSetTerminalLayoutInfoRemote'); + await this._remoteTerminalChannel.setTerminalLayoutInfo(JSON.parse(layoutInfo)); + mark('code/terminal/didSetTerminalLayoutInfoRemote'); + this._storageService.remove(TerminalStorageKeys.TerminalLayoutInfo, StorageScope.WORKSPACE); + } + } catch (e: unknown) { + this._logService.warn('RemoteTerminalBackend#getTerminalLayoutInfo Error', e && typeof e === 'object' && 'message' in e ? e.message : e); } - } catch (e: unknown) { - this._logService.warn('RemoteTerminalBackend#getTerminalLayoutInfo Error', e && typeof e === 'object' && 'message' in e ? e.message : e); } return this._remoteTerminalChannel.getTerminalLayoutInfo(); diff --git a/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts b/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts index a07bea61575..d89cc49042a 100644 --- a/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts +++ b/src/vs/workbench/contrib/terminal/electron-sandbox/localTerminalBackend.ts @@ -317,43 +317,41 @@ class LocalTerminalBackend extends BaseTerminalBackend implements ITerminalBacke // Revive processes if needed const serializedState = this._storageService.get(TerminalStorageKeys.TerminalBufferState, StorageScope.WORKSPACE); - const parsed = this._deserializeTerminalState(serializedState); - if (!parsed) { - return undefined; - } + const reviveBufferState = this._deserializeTerminalState(serializedState); + if (reviveBufferState && reviveBufferState.length > 0) { + try { + // Create variable resolver + const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot(); + const lastActiveWorkspace = activeWorkspaceRootUri ? withNullAsUndefined(this._workspaceContextService.getWorkspaceFolder(activeWorkspaceRootUri)) : undefined; + const variableResolver = terminalEnvironment.createVariableResolver(lastActiveWorkspace, await this._terminalProfileResolverService.getEnvironment(this.remoteAuthority), this._configurationResolverService); - try { - // Create variable resolver - const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot(); - const lastActiveWorkspace = activeWorkspaceRootUri ? withNullAsUndefined(this._workspaceContextService.getWorkspaceFolder(activeWorkspaceRootUri)) : undefined; - const variableResolver = terminalEnvironment.createVariableResolver(lastActiveWorkspace, await this._terminalProfileResolverService.getEnvironment(this.remoteAuthority), this._configurationResolverService); + // Re-resolve the environments and replace it on the state so local terminals use a fresh + // environment + mark('code/terminal/willGetReviveEnvironments'); + await Promise.all(reviveBufferState.map(state => new Promise(r => { + this._resolveEnvironmentForRevive(variableResolver, state.shellLaunchConfig).then(freshEnv => { + state.processLaunchConfig.env = freshEnv; + r(); + }); + }))); + mark('code/terminal/didGetReviveEnvironments'); - // Re-resolve the environments and replace it on the state so local terminals use a fresh - // environment - mark('code/terminal/willGetReviveEnvironments'); - await Promise.all(parsed.map(state => new Promise(r => { - this._resolveEnvironmentForRevive(variableResolver, state.shellLaunchConfig).then(freshEnv => { - state.processLaunchConfig.env = freshEnv; - r(); - }); - }))); - mark('code/terminal/didGetReviveEnvironments'); - - mark('code/terminal/willReviveTerminalProcesses'); - await this._proxy.reviveTerminalProcesses(workspaceId, parsed, Intl.DateTimeFormat().resolvedOptions().locale); - mark('code/terminal/didReviveTerminalProcesses'); - this._storageService.remove(TerminalStorageKeys.TerminalBufferState, StorageScope.WORKSPACE); - // If reviving processes, send the terminal layout info back to the pty host as it - // will not have been persisted on application exit - const layoutInfo = this._storageService.get(TerminalStorageKeys.TerminalLayoutInfo, StorageScope.WORKSPACE); - if (layoutInfo) { - mark('code/terminal/willSetTerminalLayoutInfo'); - await this._proxy.setTerminalLayoutInfo(JSON.parse(layoutInfo)); - mark('code/terminal/didSetTerminalLayoutInfo'); - this._storageService.remove(TerminalStorageKeys.TerminalLayoutInfo, StorageScope.WORKSPACE); + mark('code/terminal/willReviveTerminalProcesses'); + await this._proxy.reviveTerminalProcesses(workspaceId, reviveBufferState, Intl.DateTimeFormat().resolvedOptions().locale); + mark('code/terminal/didReviveTerminalProcesses'); + this._storageService.remove(TerminalStorageKeys.TerminalBufferState, StorageScope.WORKSPACE); + // If reviving processes, send the terminal layout info back to the pty host as it + // will not have been persisted on application exit + const layoutInfo = this._storageService.get(TerminalStorageKeys.TerminalLayoutInfo, StorageScope.WORKSPACE); + if (layoutInfo) { + mark('code/terminal/willSetTerminalLayoutInfo'); + await this._proxy.setTerminalLayoutInfo(JSON.parse(layoutInfo)); + mark('code/terminal/didSetTerminalLayoutInfo'); + this._storageService.remove(TerminalStorageKeys.TerminalLayoutInfo, StorageScope.WORKSPACE); + } + } catch (e: unknown) { + this._logService.warn('LocalTerminalBackend#getTerminalLayoutInfo Error', e && typeof e === 'object' && 'message' in e ? e.message : e); } - } catch (e: unknown) { - this._logService.warn('LocalTerminalBackend#getTerminalLayoutInfo Error', e && typeof e === 'object' && 'message' in e ? e.message : e); } return this._proxy.getTerminalLayoutInfo(layoutArgs); From e1338bd060b46bcdf35c73580348645165f0fd56 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Fri, 7 Jul 2023 19:11:00 +0200 Subject: [PATCH 243/926] Update to @vscode/proxy-agent 0.15.0 --- 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 2673e2279aa..224789cf9ec 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "@parcel/watcher": "2.1.0", "@vscode/iconv-lite-umd": "0.7.0", "@vscode/policy-watcher": "^1.1.4", - "@vscode/proxy-agent": "^0.14.1", + "@vscode/proxy-agent": "^0.15.0", "@vscode/ripgrep": "^1.15.5", "@vscode/spdlog": "^0.13.10", "@vscode/sqlite3": "5.1.6-vscode", diff --git a/remote/package.json b/remote/package.json index 2f5f6ce57b8..832b8d5dbd3 100644 --- a/remote/package.json +++ b/remote/package.json @@ -7,7 +7,7 @@ "@microsoft/1ds-post-js": "^3.2.2", "@parcel/watcher": "2.1.0", "@vscode/iconv-lite-umd": "0.7.0", - "@vscode/proxy-agent": "^0.14.1", + "@vscode/proxy-agent": "^0.15.0", "@vscode/ripgrep": "^1.15.5", "@vscode/spdlog": "^0.13.10", "@vscode/vscode-languagedetection": "1.0.21", diff --git a/remote/yarn.lock b/remote/yarn.lock index 0539567ca2d..531ba983765 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -58,10 +58,10 @@ resolved "https://registry.yarnpkg.com/@vscode/iconv-lite-umd/-/iconv-lite-umd-0.7.0.tgz#d2f1e0664ee6036408f9743fee264ea0699b0e48" integrity sha512-bRRFxLfg5dtAyl5XyiVWz/ZBPahpOpPrNYnnHpOpUZvam4tKH35wdhP4Kj6PbM0+KdliOsPzbGWpkxcdpNB/sg== -"@vscode/proxy-agent@^0.14.1": - version "0.14.1" - resolved "https://registry.yarnpkg.com/@vscode/proxy-agent/-/proxy-agent-0.14.1.tgz#61866221a8fbd5143f73a14c29deccdf85f13113" - integrity sha512-bJxCO9E6zDpy90TiViAZgFjAgo83gS0Lh5CUIu/JZ8p5UwwQ37Y6LZH2f2l6kBr2RGdNSRbORTFrfmLtr1faRA== +"@vscode/proxy-agent@^0.15.0": + version "0.15.0" + resolved "https://registry.yarnpkg.com/@vscode/proxy-agent/-/proxy-agent-0.15.0.tgz#b8fb8b89180a71295a8f8682775f69ab1dcf6860" + integrity sha512-HpD4A9CUOwKbC6vLa0+MEsCo/qlgbue9U9s8Z7NzJDdf2YEGjUaYf9Mvj5T1LhJX20Hv1COvkGcc7zPhtIbgbA== dependencies: "@tootallnate/once" "^1.1.2" agent-base "^6.0.2" diff --git a/yarn.lock b/yarn.lock index 689f60907af..8ebb45b9118 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1309,10 +1309,10 @@ bindings "^1.5.0" node-addon-api "^6.0.0" -"@vscode/proxy-agent@^0.14.1": - version "0.14.1" - resolved "https://registry.yarnpkg.com/@vscode/proxy-agent/-/proxy-agent-0.14.1.tgz#61866221a8fbd5143f73a14c29deccdf85f13113" - integrity sha512-bJxCO9E6zDpy90TiViAZgFjAgo83gS0Lh5CUIu/JZ8p5UwwQ37Y6LZH2f2l6kBr2RGdNSRbORTFrfmLtr1faRA== +"@vscode/proxy-agent@^0.15.0": + version "0.15.0" + resolved "https://registry.yarnpkg.com/@vscode/proxy-agent/-/proxy-agent-0.15.0.tgz#b8fb8b89180a71295a8f8682775f69ab1dcf6860" + integrity sha512-HpD4A9CUOwKbC6vLa0+MEsCo/qlgbue9U9s8Z7NzJDdf2YEGjUaYf9Mvj5T1LhJX20Hv1COvkGcc7zPhtIbgbA== dependencies: "@tootallnate/once" "^1.1.2" agent-base "^6.0.2" From e954d50a4c77e919cd7513040c5eee3b7597819f Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Fri, 7 Jul 2023 20:02:26 +0200 Subject: [PATCH 244/926] Fixes #187140 (#187291) --- .../textMate/browser/worker/textMateWorkerModel.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/services/textMate/browser/worker/textMateWorkerModel.ts b/src/vs/workbench/services/textMate/browser/worker/textMateWorkerModel.ts index cb2f98adff9..db68091ac5f 100644 --- a/src/vs/workbench/services/textMate/browser/worker/textMateWorkerModel.ts +++ b/src/vs/workbench/services/textMate/browser/worker/textMateWorkerModel.ts @@ -3,21 +3,22 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { importAMDNodeModule } from 'vs/amdX'; import { RunOnceScheduler } from 'vs/base/common/async'; import { observableValue } from 'vs/base/common/observable'; +import { setTimeout0 } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; import { LineRange } from 'vs/editor/common/core/lineRange'; import { LanguageId } from 'vs/editor/common/encodedTokenAttributes'; import { IModelChangedEvent, MirrorTextModel } from 'vs/editor/common/model/mirrorTextModel'; import { TokenizerWithStateStore } from 'vs/editor/common/model/textModelTokens'; -import type { diffStateStacksRefEq, StateStack, StackDiff } from 'vscode-textmate'; import { ContiguousMultilineTokensBuilder } from 'vs/editor/common/tokens/contiguousMultilineTokensBuilder'; import { LineTokens } from 'vs/editor/common/tokens/lineTokens'; import { TextMateTokenizationSupport } from 'vs/workbench/services/textMate/browser/tokenizationSupport/textMateTokenizationSupport'; import { TokenizationSupportWithLineLimit } from 'vs/workbench/services/textMate/browser/tokenizationSupport/tokenizationSupportWithLineLimit'; import { StateDeltas } from 'vs/workbench/services/textMate/browser/workerHost/textMateWorkerHost'; +import type { StackDiff, StateStack, diffStateStacksRefEq } from 'vscode-textmate'; import { TextMateTokenizationWorker } from './textMate.worker'; -import { importAMDNodeModule } from 'vs/amdX'; export class TextMateWorkerModel extends MirrorTextModel { private _tokenizationStateStore: TokenizerWithStateStore | null = null; @@ -169,7 +170,7 @@ export class TextMateWorkerModel extends MirrorTextModel { const deltaMs = new Date().getTime() - startTime; if (deltaMs > 20) { // yield to check for changes - setTimeout(() => this._tokenize(), 3); + setTimeout0(() => this._tokenize()); return; } } From 66b0f928408be2f70fb477f12bd4501175f7d14b Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Fri, 7 Jul 2023 11:08:51 -0700 Subject: [PATCH 245/926] treat soon to be saved by auto-save as saving --- .../contrib/notebook/common/notebookEditorInput.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/notebook/common/notebookEditorInput.ts b/src/vs/workbench/contrib/notebook/common/notebookEditorInput.ts index 9db655f6a63..940d887c0f1 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookEditorInput.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookEditorInput.ts @@ -24,7 +24,7 @@ import { VSBuffer } from 'vs/base/common/buffer'; import { IWorkingCopyIdentifier } from 'vs/workbench/services/workingCopy/common/workingCopy'; import { NotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookProvider'; import { NotebookPerfMarks } from 'vs/workbench/contrib/notebook/common/notebookPerformance'; -import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; +import { AutoSaveMode, IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { localize } from 'vs/nls'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -216,6 +216,14 @@ export class NotebookEditorInput extends AbstractResourceEditorInput { return await this._editorModelReference.object.saveAs(target); } + override isSaving(): boolean { + if (this.isDirty() && !this.hasCapability(EditorInputCapabilities.Untitled) && this.filesConfigurationService.getAutoSaveMode() === AutoSaveMode.AFTER_SHORT_DELAY) { + return true; // will be saved soon + } + + return super.isSaving(); + } + private async _suggestName(provider: NotebookProviderInfo, suggestedFilename: string) { // guess file extensions const firstSelector = provider.selectors[0]; From c4ab63fb37198ce82609149be50c86d19d1a9a25 Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Fri, 7 Jul 2023 11:19:12 -0700 Subject: [PATCH 246/926] treat soon-to-be-autosaved as saving --- .../contrib/customEditor/browser/customEditorInput.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts b/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts index 964a6a0443b..3f40d264574 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts @@ -22,7 +22,7 @@ import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { ICustomEditorModel, ICustomEditorService } from 'vs/workbench/contrib/customEditor/common/customEditor'; import { IOverlayWebview, IWebviewService } from 'vs/workbench/contrib/webview/browser/webview'; import { IWebviewWorkbenchService, LazilyResolvedWebviewEditorInput } from 'vs/workbench/contrib/webviewPanel/browser/webviewWorkbenchService'; -import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; +import { AutoSaveMode, IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService'; interface CustomEditorInputInitInfo { @@ -302,6 +302,14 @@ export class CustomEditorInput extends LazilyResolvedWebviewEditorInput { return (await this.rename(groupId, target))?.editor; } + override isSaving(): boolean { + if (this.isDirty() && !this.hasCapability(EditorInputCapabilities.Untitled) && this.filesConfigurationService.getAutoSaveMode() === AutoSaveMode.AFTER_SHORT_DELAY) { + return true; // will be saved soon + } + + return super.isSaving(); + } + public override async revert(group: GroupIdentifier, options?: IRevertOptions): Promise { if (this._modelRef) { return this._modelRef.object.revert(options); From 437342409bf7e695ca03d9299c2661bfc9fc0376 Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Fri, 7 Jul 2023 11:27:09 -0700 Subject: [PATCH 247/926] Force new window for remote -> local Continue On (#187301) Force new window for remote -> local clone --- extensions/git/src/commands.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 4213aafa495..ef9b0a56fad 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -750,15 +750,22 @@ export class CommandCenter { if (uri !== undefined) { // Launch desktop client if currently in web + let target = `${env.uriScheme}://vscode.git/clone?url=${encodeURIComponent(uri)}`; if (env.uiKind === UIKind.Web) { - let target = `${env.uriScheme}://vscode.git/clone?url=${encodeURIComponent(uri)}`; if (ref !== undefined) { target += `&ref=${encodeURIComponent(ref)}`; } return Uri.parse(target); } - // If already in desktop client, directly clone + // If already in desktop client but in a remote window, we need to force a new window + // so that the git extension can access the local filesystem for cloning + if (env.remoteName !== undefined) { + target += `&windowId=_blank`; + return Uri.parse(target); + } + + // Otherwise, directly clone void this.clone(uri, undefined, { ref: ref }); } } From 9e390f32b43a43ee3f331b90e1a79db2c295431a Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Fri, 7 Jul 2023 20:28:14 +0200 Subject: [PATCH 248/926] Fixes #186393 (#187295) --- .../diffEditorDecorations.ts | 33 ++++++++++++------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorDecorations.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorDecorations.ts index 78f20246013..8a821c0b135 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorDecorations.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorDecorations.ts @@ -6,7 +6,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { IObservable, derived } from 'vs/base/common/observable'; import { isDefined } from 'vs/base/common/types'; -import { arrowRevertChange, diffAddDecoration, diffAddDecorationEmpty, diffDeleteDecoration, diffDeleteDecorationEmpty, diffLineAddDecorationBackground, diffLineAddDecorationBackgroundWithIndicator, diffLineDeleteDecorationBackground, diffLineDeleteDecorationBackgroundWithIndicator } from 'vs/editor/browser/widget/diffEditorWidget2/decorations'; +import { arrowRevertChange, diffAddDecoration, diffAddDecorationEmpty, diffDeleteDecoration, diffDeleteDecorationEmpty, diffLineAddDecorationBackground, diffLineAddDecorationBackgroundWithIndicator, diffLineDeleteDecorationBackground, diffLineDeleteDecorationBackgroundWithIndicator, diffWholeLineAddDecoration, diffWholeLineDeleteDecoration } from 'vs/editor/browser/widget/diffEditorWidget2/decorations'; import { DiffEditorEditors } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors'; import { DiffEditorOptions } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorOptions'; import { DiffEditorViewModel } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel'; @@ -54,19 +54,28 @@ export class DiffEditorDecorations extends Disposable { modifiedDecorations.push({ range, options: renderIndicators ? diffLineAddDecorationBackgroundWithIndicator : diffLineAddDecorationBackground }); } - for (const i of m.lineRangeMapping.innerChanges || []) { - if (currentMove - && (currentMove.lineRangeMapping.originalRange.intersect(new LineRange(i.originalRange.startLineNumber, i.originalRange.endLineNumber)) - || currentMove.lineRangeMapping.modifiedRange.intersect(new LineRange(i.modifiedRange.startLineNumber, i.modifiedRange.endLineNumber)))) { - continue; + if (m.lineRangeMapping.modifiedRange.isEmpty || m.lineRangeMapping.originalRange.isEmpty) { + for (const range of fullRangeOriginal) { + originalDecorations.push({ range, options: diffWholeLineDeleteDecoration }); } + for (const range of fullRangeModified) { + modifiedDecorations.push({ range, options: diffWholeLineAddDecoration }); + } + } else { + for (const i of m.lineRangeMapping.innerChanges || []) { + if (currentMove + && (currentMove.lineRangeMapping.originalRange.intersect(new LineRange(i.originalRange.startLineNumber, i.originalRange.endLineNumber)) + || currentMove.lineRangeMapping.modifiedRange.intersect(new LineRange(i.modifiedRange.startLineNumber, i.modifiedRange.endLineNumber)))) { + continue; + } - // Don't show empty markers outside the line range - if (m.lineRangeMapping.originalRange.contains(i.originalRange.startLineNumber)) { - originalDecorations.push({ range: i.originalRange, options: (i.originalRange.isEmpty() && showEmptyDecorations) ? diffDeleteDecorationEmpty : diffDeleteDecoration }); - } - if (m.lineRangeMapping.modifiedRange.contains(i.modifiedRange.startLineNumber)) { - modifiedDecorations.push({ range: i.modifiedRange, options: (i.modifiedRange.isEmpty() && showEmptyDecorations) ? diffAddDecorationEmpty : diffAddDecoration }); + // Don't show empty markers outside the line range + if (m.lineRangeMapping.originalRange.contains(i.originalRange.startLineNumber)) { + originalDecorations.push({ range: i.originalRange, options: (i.originalRange.isEmpty() && showEmptyDecorations) ? diffDeleteDecorationEmpty : diffDeleteDecoration }); + } + if (m.lineRangeMapping.modifiedRange.contains(i.modifiedRange.startLineNumber)) { + modifiedDecorations.push({ range: i.modifiedRange, options: (i.modifiedRange.isEmpty() && showEmptyDecorations) ? diffAddDecorationEmpty : diffAddDecoration }); + } } } From ad791ef3d26b99963da31636af697afd86575435 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 7 Jul 2023 20:42:45 +0200 Subject: [PATCH 249/926] fix installing extension from CLI (#187305) --- src/vs/code/node/cliProcessMain.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index 73268f714f7..0065a8da62e 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -62,6 +62,7 @@ import { ExtensionsProfileScannerService } from 'vs/platform/extensionManagement import { LogService } from 'vs/platform/log/common/logService'; import { LoggerService } from 'vs/platform/log/node/loggerService'; import { localize } from 'vs/nls'; +import { FileUserDataProvider } from 'vs/platform/userData/common/fileUserDataProvider'; class CliMain extends Disposable { @@ -145,6 +146,10 @@ class CliMain extends Disposable { const diskFileSystemProvider = this._register(new DiskFileSystemProvider(logService)); fileService.registerProvider(Schemas.file, diskFileSystemProvider); + // Use FileUserDataProvider for user data to + // enable atomic read / write operations. + fileService.registerProvider(Schemas.vscodeUserData, new FileUserDataProvider(Schemas.file, diskFileSystemProvider, Schemas.vscodeUserData, logService)); + // Uri Identity const uriIdentityService = new UriIdentityService(fileService); services.set(IUriIdentityService, uriIdentityService); From 0917209e9acd6a4fe11ee65ef762bce783a2c285 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Fri, 7 Jul 2023 20:45:35 +0200 Subject: [PATCH 250/926] Always publish build artifacts, if built (#187134) * always publish build artifacts, if built fixes #187122 * fix windows build * fix builds * fix * more fixes * further debug * fix yarn * remove debug * remove 7zip dependency * fix snap --- .../alpine/product-build-alpine.yml | 56 ++++----- .../darwin/product-build-darwin-test.yml | 10 +- .../darwin/product-build-darwin.yml | 59 ++++----- .../azure-pipelines/linux/prepare-publish.sh | 66 ---------- .../linux/product-build-linux-test.yml | 10 +- .../linux/product-build-linux.yml | 89 ++++++++++---- .../linux/snap-build-linux.yml | 6 +- .../azure-pipelines/web/product-build-web.yml | 25 ++-- .../azure-pipelines/win32/prepare-publish.ps1 | 40 ------- .../win32/product-build-win32-test.yml | 15 ++- .../win32/product-build-win32.yml | 113 +++++++++++++----- build/gulpfile.vscode.win32.js | 20 ---- package.json | 1 - yarn.lock | 5 - 14 files changed, 235 insertions(+), 280 deletions(-) delete mode 100755 build/azure-pipelines/linux/prepare-publish.sh delete mode 100644 build/azure-pipelines/win32/prepare-publish.ps1 diff --git a/build/azure-pipelines/alpine/product-build-alpine.yml b/build/azure-pipelines/alpine/product-build-alpine.yml index 04f9ad2fcf2..704f4814ae9 100644 --- a/build/azure-pipelines/alpine/product-build-alpine.yml +++ b/build/azure-pipelines/alpine/product-build-alpine.yml @@ -104,55 +104,47 @@ steps: - script: | set -e - TARGET=$([ "$VSCODE_ARCH" == "x64" ] && echo "linux-alpine" || echo "alpine-arm64") + TARGET=$([ "$VSCODE_ARCH" == "x64" ] && echo "linux-alpine" || echo "alpine-arm64") # TODO@joaomoreno yarn gulp vscode-reh-$TARGET-min-ci - yarn gulp vscode-reh-web-$TARGET-min-ci + (cd .. && mv vscode-reh-$TARGET vscode-server-$TARGET) # TODO@joaomoreno + ARCHIVE_PATH=".build/linux/server/vscode-server-$TARGET.tar.gz" + mkdir -p $(dirname $ARCHIVE_PATH) + tar --owner=0 --group=0 -czf $ARCHIVE_PATH -C .. vscode-server-$TARGET + echo "##vso[task.setvariable variable=SERVER_PATH]$ARCHIVE_PATH" env: GITHUB_TOKEN: "$(github-distro-mixin-password)" - displayName: Build + displayName: Build server - script: | set -e TARGET=$([ "$VSCODE_ARCH" == "x64" ] && echo "linux-alpine" || echo "alpine-arm64") - REPO="$(pwd)" - ROOT="$REPO/.." + yarn gulp vscode-reh-web-$TARGET-min-ci + (cd .. && mv vscode-reh-web-$TARGET vscode-server-$TARGET-web) # TODO@joaomoreno + ARCHIVE_PATH=".build/linux/web/vscode-server-$TARGET-web.tar.gz" + mkdir -p $(dirname $ARCHIVE_PATH) + tar --owner=0 --group=0 -czf $ARCHIVE_PATH -C .. vscode-server-$TARGET-web + echo "##vso[task.setvariable variable=WEB_PATH]$ARCHIVE_PATH" + env: + GITHUB_TOKEN: "$(github-distro-mixin-password)" + displayName: Build server (web) - # Publish Remote Extension Host - LEGACY_SERVER_BUILD_NAME="vscode-reh-$TARGET" - SERVER_BUILD_NAME="vscode-server-$TARGET" - SERVER_TARBALL_FILENAME="vscode-server-$TARGET.tar.gz" - SERVER_TARBALL_PATH="$ROOT/$SERVER_TARBALL_FILENAME" - - rm -rf $ROOT/vscode-server-*.tar.* - (cd $ROOT && mv $LEGACY_SERVER_BUILD_NAME $SERVER_BUILD_NAME && tar --owner=0 --group=0 -czf $SERVER_TARBALL_PATH $SERVER_BUILD_NAME) - - # Publish Remote Extension Host (Web) - LEGACY_SERVER_BUILD_NAME="vscode-reh-web-$TARGET" - SERVER_BUILD_NAME="vscode-server-$TARGET-web" - SERVER_TARBALL_FILENAME="vscode-server-$TARGET-web.tar.gz" - SERVER_TARBALL_PATH="$ROOT/$SERVER_TARBALL_FILENAME" - - rm -rf $ROOT/vscode-server-*-web.tar.* - (cd $ROOT && mv $LEGACY_SERVER_BUILD_NAME $SERVER_BUILD_NAME && tar --owner=0 --group=0 -czf $SERVER_TARBALL_PATH $SERVER_BUILD_NAME) - displayName: Prepare for publish - - - publish: $(Agent.BuildDirectory)/vscode-server-alpine-$(VSCODE_ARCH).tar.gz + - publish: $(SERVER_PATH) artifact: vscode_server_alpine_$(VSCODE_ARCH)_archive-unsigned displayName: Publish server archive - condition: and(succeeded(), ne(variables['VSCODE_ARCH'], 'x64')) + condition: and(succeededOrFailed(), ne(variables['SERVER_PATH'], ''), ne(variables['VSCODE_ARCH'], 'x64')) - - publish: $(Agent.BuildDirectory)/vscode-server-alpine-$(VSCODE_ARCH)-web.tar.gz + - publish: $(WEB_PATH) artifact: vscode_web_alpine_$(VSCODE_ARCH)_archive-unsigned displayName: Publish web server archive - condition: and(succeeded(), ne(variables['VSCODE_ARCH'], 'x64')) + condition: and(succeededOrFailed(), ne(variables['WEB_PATH'], ''), ne(variables['VSCODE_ARCH'], 'x64')) # Legacy x64 artifact name - - publish: $(Agent.BuildDirectory)/vscode-server-linux-alpine.tar.gz + - publish: $(SERVER_PATH) artifact: vscode_server_linux_alpine_archive-unsigned displayName: Publish x64 server archive - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64')) + condition: and(succeededOrFailed(), ne(variables['SERVER_PATH'], ''), eq(variables['VSCODE_ARCH'], 'x64')) - - publish: $(Agent.BuildDirectory)/vscode-server-linux-alpine-web.tar.gz + - publish: $(WEB_PATH) artifact: vscode_web_linux_alpine_archive-unsigned displayName: Publish x64 web server archive - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64')) + condition: and(succeededOrFailed(), ne(variables['WEB_PATH'], ''), eq(variables['VSCODE_ARCH'], 'x64')) diff --git a/build/azure-pipelines/darwin/product-build-darwin-test.yml b/build/azure-pipelines/darwin/product-build-darwin-test.yml index 0b445933265..955b19749a6 100644 --- a/build/azure-pipelines/darwin/product-build-darwin-test.yml +++ b/build/azure-pipelines/darwin/product-build-darwin-test.yml @@ -84,13 +84,13 @@ steps: INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME/Contents/MacOS/Electron" \ ./scripts/test-integration.sh --build --tfs "Integration Tests" env: - VSCODE_REMOTE_SERVER_PATH: $(agent.builddirectory)/vscode-reh-darwin-$(VSCODE_ARCH) + VSCODE_REMOTE_SERVER_PATH: $(agent.builddirectory)/vscode-server-darwin-$(VSCODE_ARCH) displayName: Run integration tests (Electron) timeoutInMinutes: 20 - script: ./scripts/test-web-integration.sh --browser webkit env: - VSCODE_REMOTE_SERVER_PATH: $(agent.builddirectory)/vscode-reh-web-darwin-$(VSCODE_ARCH) + VSCODE_REMOTE_SERVER_PATH: $(agent.builddirectory)/vscode-server-darwin-$(VSCODE_ARCH)-web displayName: Run integration tests (Browser, Webkit) timeoutInMinutes: 20 @@ -101,7 +101,7 @@ steps: INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME/Contents/MacOS/Electron" \ ./scripts/test-remote-integration.sh env: - VSCODE_REMOTE_SERVER_PATH: $(agent.builddirectory)/vscode-reh-darwin-$(VSCODE_ARCH) + VSCODE_REMOTE_SERVER_PATH: $(agent.builddirectory)/vscode-server-darwin-$(VSCODE_ARCH) displayName: Run integration tests (Remote) timeoutInMinutes: 20 @@ -133,7 +133,7 @@ steps: - script: yarn smoketest-no-compile --web --tracing --headless env: - VSCODE_REMOTE_SERVER_PATH: $(agent.builddirectory)/vscode-reh-web-darwin-$(VSCODE_ARCH) + VSCODE_REMOTE_SERVER_PATH: $(agent.builddirectory)/vscode-server-darwin-$(VSCODE_ARCH)-web timeoutInMinutes: 20 displayName: Run smoke tests (Browser, Chromium) @@ -144,7 +144,7 @@ steps: APP_NAME="`ls $APP_ROOT | head -n 1`" yarn smoketest-no-compile --tracing --remote --build "$APP_ROOT/$APP_NAME" env: - VSCODE_REMOTE_SERVER_PATH: $(agent.builddirectory)/vscode-reh-darwin-$(VSCODE_ARCH) + VSCODE_REMOTE_SERVER_PATH: $(agent.builddirectory)/vscode-server-darwin-$(VSCODE_ARCH) timeoutInMinutes: 20 displayName: Run smoke tests (Remote) diff --git a/build/azure-pipelines/darwin/product-build-darwin.yml b/build/azure-pipelines/darwin/product-build-darwin.yml index f7810b6f076..95d81431089 100644 --- a/build/azure-pipelines/darwin/product-build-darwin.yml +++ b/build/azure-pipelines/darwin/product-build-darwin.yml @@ -111,17 +111,34 @@ steps: - template: ../common/install-builtin-extensions.yml - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - - script: yarn gulp vscode-darwin-$(VSCODE_ARCH)-min-ci + - script: | + set -e + yarn gulp vscode-darwin-$(VSCODE_ARCH)-min-ci + echo "##vso[task.setvariable variable=BUILT_CLIENT]true" env: GITHUB_TOKEN: "$(github-distro-mixin-password)" displayName: Build client - - script: yarn gulp vscode-reh-darwin-$(VSCODE_ARCH)-min-ci + - script: | + set -e + yarn gulp vscode-reh-darwin-$(VSCODE_ARCH)-min-ci + mv ../vscode-reh-darwin-$(VSCODE_ARCH) ../vscode-server-darwin-$(VSCODE_ARCH) # TODO@joaomoreno + ARCHIVE_PATH=".build/darwin/server/vscode-server-darwin-$(VSCODE_ARCH).zip" + mkdir -p $(dirname $ARCHIVE_PATH) + (cd .. && zip -Xry $(Build.SourcesDirectory)/$ARCHIVE_PATH vscode-server-darwin-$(VSCODE_ARCH)) + echo "##vso[task.setvariable variable=SERVER_PATH]$ARCHIVE_PATH" env: GITHUB_TOKEN: "$(github-distro-mixin-password)" displayName: Build server - - script: yarn gulp vscode-reh-web-darwin-$(VSCODE_ARCH)-min-ci + - script: | + set -e + yarn gulp vscode-reh-web-darwin-$(VSCODE_ARCH)-min-ci + mv ../vscode-reh-web-darwin-$(VSCODE_ARCH) ../vscode-server-darwin-$(VSCODE_ARCH)-web # TODO@joaomoreno + ARCHIVE_PATH=".build/darwin/server/vscode-server-darwin-$(VSCODE_ARCH)-web.zip" + mkdir -p $(dirname $ARCHIVE_PATH) + (cd .. && zip -Xry $(Build.SourcesDirectory)/$ARCHIVE_PATH vscode-server-darwin-$(VSCODE_ARCH)-web) + echo "##vso[task.setvariable variable=WEB_PATH]$ARCHIVE_PATH" env: GITHUB_TOKEN: "$(github-distro-mixin-password)" displayName: Build server (web) @@ -176,18 +193,14 @@ steps: DEBUG=electron-osx-sign* node build/darwin/sign.js $(agent.builddirectory) displayName: Set Hardened Entitlements - - script: cd $(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) && zip -r -X -y $(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH).zip * - displayName: Archive build - - script: | set -e - - # package Remote Extension Host - pushd .. && mv vscode-reh-darwin-$(VSCODE_ARCH) vscode-server-darwin-$(VSCODE_ARCH) && zip -Xry vscode-server-darwin-$(VSCODE_ARCH).zip vscode-server-darwin-$(VSCODE_ARCH) && popd - - # package Remote Extension Host (Web) - pushd .. && mv vscode-reh-web-darwin-$(VSCODE_ARCH) vscode-server-darwin-$(VSCODE_ARCH)-web && zip -Xry vscode-server-darwin-$(VSCODE_ARCH)-web.zip vscode-server-darwin-$(VSCODE_ARCH)-web && popd - displayName: Prepare to publish servers + ARCHIVE_PATH=".build/darwin/client/VSCode-darwin-$(VSCODE_ARCH).zip" + mkdir -p $(dirname $ARCHIVE_PATH) + (cd ../VSCode-darwin-$(VSCODE_ARCH) && zip -Xry $(Build.SourcesDirectory)/$ARCHIVE_PATH *) + echo "##vso[task.setvariable variable=CLIENT_PATH]$ARCHIVE_PATH" + condition: and(succeededOrFailed(), eq(variables['BUILT_CLIENT'], 'true')) + displayName: Package client - task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0 displayName: Generate SBOM (client) @@ -209,25 +222,17 @@ steps: displayName: Publish SBOM (server) artifact: vscode_server_darwin_$(VSCODE_ARCH)_sbom - - publish: $(Agent.BuildDirectory)/VSCode-darwin-$(VSCODE_ARCH).zip + - publish: $(CLIENT_PATH) artifact: unsigned_vscode_client_darwin_$(VSCODE_ARCH)_archive + condition: and(succeededOrFailed(), ne(variables['CLIENT_PATH'], '')) displayName: Publish client archive - - publish: $(Agent.BuildDirectory)/vscode-server-darwin-$(VSCODE_ARCH).zip + - publish: $(SERVER_PATH) artifact: vscode_server_darwin_$(VSCODE_ARCH)_archive-unsigned + condition: and(succeededOrFailed(), ne(variables['SERVER_PATH'], '')) displayName: Publish server archive - - publish: $(Agent.BuildDirectory)/vscode-server-darwin-$(VSCODE_ARCH)-web.zip + - publish: $(WEB_PATH) artifact: vscode_web_darwin_$(VSCODE_ARCH)_archive-unsigned + condition: and(succeededOrFailed(), ne(variables['WEB_PATH'], '')) displayName: Publish web server archive - - - task: AzureCLI@2 - inputs: - azureSubscription: "vscode-builds-subscription" - scriptType: pscore - scriptLocation: inlineScript - addSpnToEnvironment: true - inlineScript: | - Write-Host "##vso[task.setvariable variable=AZURE_TENANT_ID]$env:tenantId" - Write-Host "##vso[task.setvariable variable=AZURE_CLIENT_ID]$env:servicePrincipalId" - Write-Host "##vso[task.setvariable variable=AZURE_CLIENT_SECRET;issecret=true]$env:servicePrincipalKey" diff --git a/build/azure-pipelines/linux/prepare-publish.sh b/build/azure-pipelines/linux/prepare-publish.sh deleted file mode 100755 index 891fa8024ef..00000000000 --- a/build/azure-pipelines/linux/prepare-publish.sh +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env bash -set -e -REPO="$(pwd)" -ROOT="$REPO/.." - -# Publish tarball -PLATFORM_LINUX="linux-$VSCODE_ARCH" -BUILDNAME="VSCode-$PLATFORM_LINUX" -BUILD_VERSION="$(date +%s)" -[ -z "$VSCODE_QUALITY" ] && TARBALL_FILENAME="code-$VSCODE_ARCH-$BUILD_VERSION.tar.gz" || TARBALL_FILENAME="code-$VSCODE_QUALITY-$VSCODE_ARCH-$BUILD_VERSION.tar.gz" -TARBALL_PATH="$ROOT/$TARBALL_FILENAME" - -rm -rf $ROOT/code-*.tar.* -(cd $ROOT && tar -czf $TARBALL_PATH $BUILDNAME) - -# Publish Remote Extension Host -LEGACY_SERVER_BUILD_NAME="vscode-reh-$PLATFORM_LINUX" -SERVER_BUILD_NAME="vscode-server-$PLATFORM_LINUX" -SERVER_TARBALL_FILENAME="vscode-server-$PLATFORM_LINUX.tar.gz" -SERVER_TARBALL_PATH="$ROOT/$SERVER_TARBALL_FILENAME" - -rm -rf $ROOT/vscode-server-*.tar.* -(cd $ROOT && mv $LEGACY_SERVER_BUILD_NAME $SERVER_BUILD_NAME && tar --owner=0 --group=0 -czf $SERVER_TARBALL_PATH $SERVER_BUILD_NAME) - -# Publish Remote Extension Host (Web) -LEGACY_SERVER_BUILD_NAME="vscode-reh-web-$PLATFORM_LINUX" -SERVER_BUILD_NAME="vscode-server-$PLATFORM_LINUX-web" -SERVER_TARBALL_FILENAME="vscode-server-$PLATFORM_LINUX-web.tar.gz" -SERVER_TARBALL_PATH="$ROOT/$SERVER_TARBALL_FILENAME" - -rm -rf $ROOT/vscode-server-*-web.tar.* -(cd $ROOT && mv $LEGACY_SERVER_BUILD_NAME $SERVER_BUILD_NAME && tar --owner=0 --group=0 -czf $SERVER_TARBALL_PATH $SERVER_BUILD_NAME) - -# Publish DEB -case $VSCODE_ARCH in - x64) DEB_ARCH="amd64" ;; - *) DEB_ARCH="$VSCODE_ARCH" ;; -esac - -PLATFORM_DEB="linux-deb-$VSCODE_ARCH" -DEB_FILENAME="$(ls $REPO/.build/linux/deb/$DEB_ARCH/deb/)" -DEB_PATH="$REPO/.build/linux/deb/$DEB_ARCH/deb/$DEB_FILENAME" - -# Publish RPM -case $VSCODE_ARCH in - x64) RPM_ARCH="x86_64" ;; - armhf) RPM_ARCH="armv7hl" ;; - arm64) RPM_ARCH="aarch64" ;; - *) RPM_ARCH="$VSCODE_ARCH" ;; -esac - -PLATFORM_RPM="linux-rpm-$VSCODE_ARCH" -RPM_FILENAME="$(ls $REPO/.build/linux/rpm/$RPM_ARCH/ | grep .rpm)" -RPM_PATH="$REPO/.build/linux/rpm/$RPM_ARCH/$RPM_FILENAME" - -# Publish Snap -# Pack snap tarball artifact, in order to preserve file perms -mkdir -p $REPO/.build/linux/snap-tarball -SNAP_TARBALL_PATH="$REPO/.build/linux/snap-tarball/snap-$VSCODE_ARCH.tar.gz" -rm -rf $SNAP_TARBALL_PATH -(cd .build/linux && tar -czf $SNAP_TARBALL_PATH snap) - -# Export DEB_PATH, RPM_PATH -echo "##vso[task.setvariable variable=DEB_PATH]$DEB_PATH" -echo "##vso[task.setvariable variable=RPM_PATH]$RPM_PATH" -echo "##vso[task.setvariable variable=TARBALL_PATH]$TARBALL_PATH" diff --git a/build/azure-pipelines/linux/product-build-linux-test.yml b/build/azure-pipelines/linux/product-build-linux-test.yml index 75e73b679a8..7532c43c7c9 100644 --- a/build/azure-pipelines/linux/product-build-linux-test.yml +++ b/build/azure-pipelines/linux/product-build-linux-test.yml @@ -122,13 +122,13 @@ steps: INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME" \ ./scripts/test-integration.sh --build --tfs "Integration Tests" env: - VSCODE_REMOTE_SERVER_PATH: $(agent.builddirectory)/vscode-reh-linux-$(VSCODE_ARCH) + VSCODE_REMOTE_SERVER_PATH: $(agent.builddirectory)/vscode-server-linux-$(VSCODE_ARCH) displayName: Run integration tests (Electron) timeoutInMinutes: 20 - script: ./scripts/test-web-integration.sh --browser chromium env: - VSCODE_REMOTE_SERVER_PATH: $(agent.builddirectory)/vscode-reh-web-linux-$(VSCODE_ARCH) + VSCODE_REMOTE_SERVER_PATH: $(agent.builddirectory)/vscode-server-linux-$(VSCODE_ARCH)-web displayName: Run integration tests (Browser, Chromium) timeoutInMinutes: 20 @@ -140,7 +140,7 @@ steps: INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME" \ ./scripts/test-remote-integration.sh env: - VSCODE_REMOTE_SERVER_PATH: $(agent.builddirectory)/vscode-reh-linux-$(VSCODE_ARCH) + VSCODE_REMOTE_SERVER_PATH: $(agent.builddirectory)/vscode-server-linux-$(VSCODE_ARCH) displayName: Run integration tests (Remote) timeoutInMinutes: 20 @@ -180,7 +180,7 @@ steps: - script: yarn smoketest-no-compile --web --tracing --headless --electronArgs="--disable-dev-shm-usage" env: - VSCODE_REMOTE_SERVER_PATH: $(agent.builddirectory)/vscode-reh-web-linux-$(VSCODE_ARCH) + VSCODE_REMOTE_SERVER_PATH: $(agent.builddirectory)/vscode-server-linux-$(VSCODE_ARCH)-web timeoutInMinutes: 20 displayName: Run smoke tests (Browser, Chromium) @@ -188,7 +188,7 @@ steps: set -e yarn gulp compile-extension:vscode-test-resolver APP_PATH=$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH) - VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-linux-$(VSCODE_ARCH)" \ + VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-server-linux-$(VSCODE_ARCH)" \ yarn smoketest-no-compile --tracing --remote --build "$APP_PATH" timeoutInMinutes: 20 displayName: Run smoke tests (Remote) diff --git a/build/azure-pipelines/linux/product-build-linux.yml b/build/azure-pipelines/linux/product-build-linux.yml index 38522b2d00e..fa307a37628 100644 --- a/build/azure-pipelines/linux/product-build-linux.yml +++ b/build/azure-pipelines/linux/product-build-linux.yml @@ -101,7 +101,7 @@ steps: azureSubscriptionEndpoint: "vscode-builds-subscription" azureContainerRegistry: vscodehub.azurecr.io command: "Run an image" - imageName: vscode-linux-build-agent:centos7-devtoolset8-${{ parameters.VSCODE_ARCH }} + imageName: vscode-linux-build-agent:centos7-devtoolset8-$(VSCODE_ARCH) containerCommand: uname condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) @@ -159,7 +159,7 @@ steps: PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 GITHUB_TOKEN: "$(github-distro-mixin-password)" ${{ if and(ne(parameters.VSCODE_QUALITY, 'oss'), or(eq(parameters.VSCODE_ARCH, 'x64'), eq(parameters.VSCODE_ARCH, 'arm64'))) }}: - VSCODE_REMOTE_DEPENDENCIES_CONTAINER_NAME: vscodehub.azurecr.io/vscode-linux-build-agent:centos7-devtoolset8-${{ parameters.VSCODE_ARCH }} + VSCODE_REMOTE_DEPENDENCIES_CONTAINER_NAME: vscodehub.azurecr.io/vscode-linux-build-agent:centos7-devtoolset8-$(VSCODE_ARCH) displayName: Install dependencies condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) @@ -183,17 +183,37 @@ steps: - template: ../common/install-builtin-extensions.yml - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - - script: yarn gulp vscode-linux-$(VSCODE_ARCH)-min-ci + - script: | + set -e + yarn gulp vscode-linux-$(VSCODE_ARCH)-min-ci + ARCHIVE_PATH=".build/linux/client/code-${{ parameters.VSCODE_QUALITY }}-$(VSCODE_ARCH)-$(date +%s).tar.gz" + mkdir -p $(dirname $ARCHIVE_PATH) + tar -czf $ARCHIVE_PATH -C .. VSCode-linux-$(VSCODE_ARCH) + echo "##vso[task.setvariable variable=CLIENT_PATH]$ARCHIVE_PATH" env: GITHUB_TOKEN: "$(github-distro-mixin-password)" displayName: Build client - - script: yarn gulp vscode-reh-linux-$(VSCODE_ARCH)-min-ci + - script: | + set -e + yarn gulp vscode-reh-linux-$(VSCODE_ARCH)-min-ci + mv ../vscode-reh-linux-$(VSCODE_ARCH) ../vscode-server-linux-$(VSCODE_ARCH) # TODO@joaomoreno + ARCHIVE_PATH=".build/linux/server/vscode-server-linux-$(VSCODE_ARCH).tar.gz" + mkdir -p $(dirname $ARCHIVE_PATH) + tar --owner=0 --group=0 -czf $ARCHIVE_PATH -C .. vscode-server-linux-$(VSCODE_ARCH) + echo "##vso[task.setvariable variable=SERVER_PATH]$ARCHIVE_PATH" env: GITHUB_TOKEN: "$(github-distro-mixin-password)" displayName: Build server - - script: yarn gulp vscode-reh-web-linux-$(VSCODE_ARCH)-min-ci + - script: | + set -e + yarn gulp vscode-reh-web-linux-$(VSCODE_ARCH)-min-ci + mv ../vscode-reh-web-linux-$(VSCODE_ARCH) ../vscode-server-linux-$(VSCODE_ARCH)-web # TODO@joaomoreno + ARCHIVE_PATH=".build/linux/web/vscode-server-linux-$(VSCODE_ARCH)-web.tar.gz" + mkdir -p $(dirname $ARCHIVE_PATH) + tar --owner=0 --group=0 -czf $ARCHIVE_PATH -C .. vscode-server-linux-$(VSCODE_ARCH)-web + echo "##vso[task.setvariable variable=WEB_PATH]$ARCHIVE_PATH" env: GITHUB_TOKEN: "$(github-distro-mixin-password)" displayName: Build server (web) @@ -228,13 +248,25 @@ steps: mv $(Build.ArtifactStagingDirectory)/cli/$APP_NAME $(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH)/bin/$CLI_APP_NAME displayName: Make CLI executable - - script: yarn gulp "vscode-linux-$(VSCODE_ARCH)-build-deb" + - script: | + set -e + yarn gulp "vscode-linux-$(VSCODE_ARCH)-build-deb" + echo "##vso[task.setvariable variable=DEB_PATH]$(ls .build/linux/deb/*/deb/*.deb)" displayName: Build deb package - - script: yarn gulp "vscode-linux-$(VSCODE_ARCH)-build-rpm" + - script: | + set -e + yarn gulp "vscode-linux-$(VSCODE_ARCH)-build-rpm" + echo "##vso[task.setvariable variable=RPM_PATH]$(ls .build/linux/rpm/*/*.rpm)" displayName: Build rpm package - - script: yarn gulp "vscode-linux-$(VSCODE_ARCH)-prepare-snap" + - script: | + set -e + yarn gulp "vscode-linux-$(VSCODE_ARCH)-prepare-snap" + ARCHIVE_PATH=".build/linux/snap-tarball/snap-$(VSCODE_ARCH).tar.gz" + mkdir -p $(dirname $ARCHIVE_PATH) + tar -czf $ARCHIVE_PATH -C .build/linux snap + echo "##vso[task.setvariable variable=SNAP_PATH]$ARCHIVE_PATH" displayName: Prepare snap package - task: UseDotNet@2 @@ -248,8 +280,8 @@ steps: - script: node build/azure-pipelines/common/sign $(Agent.ToolsDirectory)/esrpclient/*/*/net6.0/esrpcli.dll rpm $(ESRP-PKI) $(esrp-aad-username) $(esrp-aad-password) .build/linux/rpm '*.rpm' displayName: Codesign rpm - - script: ./build/azure-pipelines/linux/prepare-publish.sh - displayName: Prepare for Publish + # - script: ./build/azure-pipelines/linux/prepare-publish.sh + # displayName: Prepare for Publish - task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0 displayName: Generate SBOM (client) @@ -271,28 +303,33 @@ steps: displayName: Publish SBOM (server) artifact: vscode_server_linux_$(VSCODE_ARCH)_sbom + - publish: $(CLIENT_PATH) + artifact: vscode_client_linux_$(VSCODE_ARCH)_archive-unsigned + condition: and(succeededOrFailed(), ne(variables['CLIENT_PATH'], '')) + displayName: Publish client archive + + - publish: $(SERVER_PATH) + artifact: vscode_server_linux_$(VSCODE_ARCH)_archive-unsigned + condition: and(succeededOrFailed(), ne(variables['SERVER_PATH'], '')) + displayName: Publish server archive + + - publish: $(WEB_PATH) + artifact: vscode_web_linux_$(VSCODE_ARCH)_archive-unsigned + condition: and(succeededOrFailed(), ne(variables['WEB_PATH'], '')) + displayName: Publish web server archive + - publish: $(DEB_PATH) artifact: vscode_client_linux_$(VSCODE_ARCH)_deb-package + condition: and(succeededOrFailed(), ne(variables['DEB_PATH'], '')) displayName: Publish deb package - publish: $(RPM_PATH) artifact: vscode_client_linux_$(VSCODE_ARCH)_rpm-package + condition: and(succeededOrFailed(), ne(variables['RPM_PATH'], '')) displayName: Publish rpm package - - publish: $(TARBALL_PATH) - artifact: vscode_client_linux_$(VSCODE_ARCH)_archive-unsigned - displayName: Publish client archive + - publish: $(SNAP_PATH) + artifact: snap-$(VSCODE_ARCH) + condition: and(succeededOrFailed(), ne(variables['SNAP_PATH'], '')) + displayName: Publish snap pre-package - - publish: $(Agent.BuildDirectory)/vscode-server-linux-$(VSCODE_ARCH).tar.gz - artifact: vscode_server_linux_$(VSCODE_ARCH)_archive-unsigned - displayName: Publish server archive - - - publish: $(Agent.BuildDirectory)/vscode-server-linux-$(VSCODE_ARCH)-web.tar.gz - artifact: vscode_web_linux_$(VSCODE_ARCH)_archive-unsigned - displayName: Publish web server archive - - - task: PublishPipelineArtifact@0 - displayName: "Publish Pipeline Artifact" - inputs: - artifactName: "snap-$(VSCODE_ARCH)" - targetPath: .build/linux/snap-tarball diff --git a/build/azure-pipelines/linux/snap-build-linux.yml b/build/azure-pipelines/linux/snap-build-linux.yml index 9002fcff5d7..c028cc58e04 100644 --- a/build/azure-pipelines/linux/snap-build-linux.yml +++ b/build/azure-pipelines/linux/snap-build-linux.yml @@ -27,15 +27,13 @@ steps: sudo apt-get install -y yarn # Define variables - REPO="$(pwd)" - SNAP_ROOT="$REPO/.build/linux/snap/$(VSCODE_ARCH)" + SNAP_ROOT="$(pwd)/.build/linux/snap/$(VSCODE_ARCH)" # Install build dependencies (cd build && yarn) # Unpack snap tarball artifact, in order to preserve file perms - SNAP_TARBALL_PATH="$REPO/.build/linux/snap-tarball/snap-$(VSCODE_ARCH).tar.gz" - (cd .build/linux && tar -xzf $SNAP_TARBALL_PATH) + (cd .build/linux && tar -xzf snap-tarball/snap-$(VSCODE_ARCH).tar.gz) # Create snap package BUILD_VERSION="$(date +%s)" diff --git a/build/azure-pipelines/web/product-build-web.yml b/build/azure-pipelines/web/product-build-web.yml index 556f1f23efa..1b42ebaaaf9 100644 --- a/build/azure-pipelines/web/product-build-web.yml +++ b/build/azure-pipelines/web/product-build-web.yml @@ -87,7 +87,13 @@ steps: - template: ../common/install-builtin-extensions.yml - - script: yarn gulp vscode-web-min-ci + - script: | + set -e + yarn gulp vscode-web-min-ci + ARCHIVE_PATH=".build/web/vscode-web.tar.gz" + mkdir -p $(dirname $ARCHIVE_PATH) + tar --owner=0 --group=0 -czf $ARCHIVE_PATH -C .. vscode-web + echo "##vso[task.setvariable variable=WEB_PATH]$ARCHIVE_PATH" env: GITHUB_TOKEN: "$(github-distro-mixin-password)" displayName: Build @@ -133,20 +139,7 @@ steps: node build/azure-pipelines/upload-nlsmetadata displayName: Upload NLS Metadata - - script: | - set -e - REPO="$(pwd)" - ROOT="$REPO/.." - - WEB_BUILD_NAME="vscode-web" - WEB_TARBALL_FILENAME="vscode-web.tar.gz" - WEB_TARBALL_PATH="$ROOT/$WEB_TARBALL_FILENAME" - - rm -rf $ROOT/vscode-web.tar.* - - cd $ROOT && tar --owner=0 --group=0 -czf $WEB_TARBALL_PATH $WEB_BUILD_NAME - displayName: Prepare for publish - - - publish: $(Agent.BuildDirectory)/vscode-web.tar.gz + - publish: $(WEB_PATH) artifact: vscode_web_linux_standalone_archive-unsigned + condition: and(succeededOrFailed(), ne(variables['WEB_PATH'], '')) displayName: Publish web archive diff --git a/build/azure-pipelines/win32/prepare-publish.ps1 b/build/azure-pipelines/win32/prepare-publish.ps1 deleted file mode 100644 index d2870fe7871..00000000000 --- a/build/azure-pipelines/win32/prepare-publish.ps1 +++ /dev/null @@ -1,40 +0,0 @@ -. build/azure-pipelines/win32/exec.ps1 -$ErrorActionPreference = "Stop" - -$Arch = "$env:VSCODE_ARCH" -$Repo = "$(pwd)" -$Root = "$Repo\.." -$SystemExe = "$Repo\.build\win32-$Arch\system-setup\VSCodeSetup.exe" -$UserExe = "$Repo\.build\win32-$Arch\user-setup\VSCodeSetup.exe" -$Zip = "$Repo\.build\win32-$Arch\archive\VSCode-win32-$Arch.zip" -$LegacyServer = "$Root\vscode-reh-win32-$Arch" -$Server = "$Root\vscode-server-win32-$Arch" -$ServerZip = "$Repo\.build\vscode-server-win32-$Arch.zip" -$LegacyWeb = "$Root\vscode-reh-web-win32-$Arch" -$Web = "$Root\vscode-server-win32-$Arch-web" -$WebZip = "$Repo\.build\vscode-server-win32-$Arch-web.zip" -$Build = "$Root\VSCode-win32-$Arch" - -# Create server archive -if ("$Arch" -ne "arm64") { - exec { xcopy $LegacyServer $Server /H /E /I } - exec { .\node_modules\7zip\7zip-lite\7z.exe a -tzip $ServerZip $Server -r } - exec { xcopy $LegacyWeb $Web /H /E /I } - exec { .\node_modules\7zip\7zip-lite\7z.exe a -tzip $WebZip $Web -r } -} - -# get version -$PackageJson = Get-Content -Raw -Path "$Build\resources\app\package.json" | ConvertFrom-Json -$Version = $PackageJson.version - -$ARCHIVE_NAME = "VSCode-win32-$Arch-$Version.zip" -$SYSTEM_SETUP_NAME = "VSCodeSetup-$Arch-$Version.exe" -$USER_SETUP_NAME = "VSCodeUserSetup-$Arch-$Version.exe" - -# Set variables for upload -Move-Item $Zip "$Repo\.build\win32-$Arch\archive\$ARCHIVE_NAME" -Write-Host "##vso[task.setvariable variable=ARCHIVE_NAME]$ARCHIVE_NAME" -Move-Item $SystemExe "$Repo\.build\win32-$Arch\system-setup\$SYSTEM_SETUP_NAME" -Write-Host "##vso[task.setvariable variable=SYSTEM_SETUP_NAME]$SYSTEM_SETUP_NAME" -Move-Item $UserExe "$Repo\.build\win32-$Arch\user-setup\$USER_SETUP_NAME" -Write-Host "##vso[task.setvariable variable=USER_SETUP_NAME]$USER_SETUP_NAME" diff --git a/build/azure-pipelines/win32/product-build-win32-test.yml b/build/azure-pipelines/win32/product-build-win32-test.yml index 6ad4f2dffbc..ab4240f689c 100644 --- a/build/azure-pipelines/win32/product-build-win32-test.yml +++ b/build/azure-pipelines/win32/product-build-win32-test.yml @@ -89,14 +89,17 @@ steps: $AppRoot = "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)" $AppProductJson = Get-Content -Raw -Path "$AppRoot\resources\app\product.json" | ConvertFrom-Json $AppNameShort = $AppProductJson.nameShort - exec { $env:INTEGRATION_TEST_ELECTRON_PATH = "$AppRoot\$AppNameShort.exe"; $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-win32-$(VSCODE_ARCH)"; .\scripts\test-integration.bat --build --tfs "Integration Tests" } + $env:INTEGRATION_TEST_ELECTRON_PATH = "$AppRoot\$AppNameShort.exe" + $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-server-win32-$(VSCODE_ARCH)" + exec { .\scripts\test-integration.bat --build --tfs "Integration Tests" } displayName: Run integration tests (Electron) timeoutInMinutes: 20 - powershell: | . build/azure-pipelines/win32/exec.ps1 $ErrorActionPreference = "Stop" - exec { $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-web-win32-$(VSCODE_ARCH)"; .\scripts\test-web-integration.bat --browser firefox } + $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-server-win32-$(VSCODE_ARCH)-web" + exec { .\scripts\test-web-integration.bat --browser firefox } displayName: Run integration tests (Browser, Firefox) timeoutInMinutes: 20 @@ -106,7 +109,9 @@ steps: $AppRoot = "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)" $AppProductJson = Get-Content -Raw -Path "$AppRoot\resources\app\product.json" | ConvertFrom-Json $AppNameShort = $AppProductJson.nameShort - exec { $env:INTEGRATION_TEST_ELECTRON_PATH = "$AppRoot\$AppNameShort.exe"; $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-win32-$(VSCODE_ARCH)"; .\scripts\test-remote-integration.bat } + $env:INTEGRATION_TEST_ELECTRON_PATH = "$AppRoot\$AppNameShort.exe" + $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-server-win32-$(VSCODE_ARCH)" + exec { .\scripts\test-remote-integration.bat } displayName: Run integration tests (Remote) timeoutInMinutes: 20 @@ -134,7 +139,7 @@ steps: - powershell: yarn smoketest-no-compile --web --tracing --headless env: - VSCODE_REMOTE_SERVER_PATH: $(agent.builddirectory)\vscode-reh-web-win32-$(VSCODE_ARCH) + VSCODE_REMOTE_SERVER_PATH: $(agent.builddirectory)\vscode-server-win32-$(VSCODE_ARCH)-web displayName: Run smoke tests (Browser, Chromium) timeoutInMinutes: 20 @@ -144,7 +149,7 @@ steps: - powershell: yarn smoketest-no-compile --tracing --remote --build "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)" env: - VSCODE_REMOTE_SERVER_PATH: $(agent.builddirectory)\vscode-reh-win32-$(VSCODE_ARCH) + VSCODE_REMOTE_SERVER_PATH: $(agent.builddirectory)\vscode-server-win32-$(VSCODE_ARCH) displayName: Run smoke tests (Remote) timeoutInMinutes: 20 diff --git a/build/azure-pipelines/win32/product-build-win32.yml b/build/azure-pipelines/win32/product-build-win32.yml index ab985554bb7..7bca3e5ef8a 100644 --- a/build/azure-pipelines/win32/product-build-win32.yml +++ b/build/azure-pipelines/win32/product-build-win32.yml @@ -154,25 +154,34 @@ steps: . build/azure-pipelines/win32/exec.ps1 $ErrorActionPreference = "Stop" exec { yarn gulp "vscode-win32-$(VSCODE_ARCH)-min-ci" } + exec { yarn gulp "vscode-win32-$(VSCODE_ARCH)-inno-updater" } + echo "##vso[task.setvariable variable=BUILT_CLIENT]true" echo "##vso[task.setvariable variable=CodeSigningFolderPath]$(agent.builddirectory)/VSCode-win32-$(VSCODE_ARCH)" env: GITHUB_TOKEN: "$(github-distro-mixin-password)" - displayName: Build - - - powershell: yarn gulp "vscode-win32-$(VSCODE_ARCH)-inno-updater" - env: - GITHUB_TOKEN: "$(github-distro-mixin-password)" - displayName: Prepare Setup Package + displayName: Build client - powershell: | . build/azure-pipelines/win32/exec.ps1 $ErrorActionPreference = "Stop" exec { yarn gulp "vscode-reh-win32-$(VSCODE_ARCH)-min-ci" } - exec { yarn gulp "vscode-reh-web-win32-$(VSCODE_ARCH)-min-ci" } - echo "##vso[task.setvariable variable=CodeSigningFolderPath]$(CodeSigningFolderPath),$(agent.builddirectory)/vscode-reh-win32-$(VSCODE_ARCH)" + mv ..\vscode-reh-win32-$(VSCODE_ARCH) ..\vscode-server-win32-$(VSCODE_ARCH) # TODO@joaomoreno + echo "##vso[task.setvariable variable=BUILT_SERVER]true" + echo "##vso[task.setvariable variable=CodeSigningFolderPath]$(CodeSigningFolderPath),$(agent.builddirectory)/vscode-server-win32-$(VSCODE_ARCH)" env: GITHUB_TOKEN: "$(github-distro-mixin-password)" - displayName: Build Servers + displayName: Build server + condition: and(succeeded(), ne(variables['VSCODE_ARCH'], 'arm64')) + + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { yarn gulp "vscode-reh-web-win32-$(VSCODE_ARCH)-min-ci" } + mv ..\vscode-reh-web-win32-$(VSCODE_ARCH) ..\vscode-server-win32-$(VSCODE_ARCH)-web # TODO@joaomoreno + echo "##vso[task.setvariable variable=BUILT_WEB]true" + env: + GITHUB_TOKEN: "$(github-distro-mixin-password)" + displayName: Build server (web) condition: and(succeeded(), ne(variables['VSCODE_ARCH'], 'arm64')) - ${{ if or(eq(parameters.VSCODE_RUN_UNIT_TESTS, true), eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true), eq(parameters.VSCODE_RUN_SMOKE_TESTS, true)) }}: @@ -229,8 +238,42 @@ steps: displayName: Codesign context menu appx package - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - - powershell: yarn gulp "vscode-win32-$(VSCODE_ARCH)-archive" - displayName: Package archive + - powershell: | + $PackageJson = Get-Content -Raw -Path ..\VSCode-win32-$(VSCODE_ARCH)\resources\app\package.json | ConvertFrom-Json + $Version = $PackageJson.version + echo "##vso[task.setvariable variable=VSCODE_VERSION]$Version" + condition: succeededOrFailed() + displayName: Get product version + + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + $ArchivePath = ".build\win32-$(VSCODE_ARCH)\VSCode-win32-$(VSCODE_ARCH)-$(VSCODE_VERSION).zip" + New-Item -ItemType Directory -Path .build\win32-$(VSCODE_ARCH) -Force + exec { 7z.exe a -tzip $ArchivePath -x!CodeSignSummary*.md ..\VSCode-win32-$(VSCODE_ARCH) -r } + echo "##vso[task.setvariable variable=CLIENT_PATH]$ArchivePath" + condition: and(succeededOrFailed(), eq(variables['BUILT_CLIENT'], 'true')) + displayName: Package client + + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + $ArchivePath = ".build\win32-$(VSCODE_ARCH)\vscode-server-win32-$(VSCODE_ARCH).zip" + New-Item -ItemType Directory -Path .build\win32-$(VSCODE_ARCH) -Force + exec { 7z.exe a -tzip $ArchivePath ..\vscode-server-win32-$(VSCODE_ARCH) -r } + echo "##vso[task.setvariable variable=SERVER_PATH]$ArchivePath" + condition: and(succeededOrFailed(), eq(variables['BUILT_SERVER'], 'true')) + displayName: Package server + + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + $ArchivePath = ".build\win32-$(VSCODE_ARCH)\vscode-server-win32-$(VSCODE_ARCH)-web.zip" + New-Item -ItemType Directory -Path .build\win32-$(VSCODE_ARCH) -Force + exec { 7z.exe a -tzip $ArchivePath ..\vscode-server-win32-$(VSCODE_ARCH)-web -r } + echo "##vso[task.setvariable variable=WEB_PATH]$ArchivePath" + condition: and(succeededOrFailed(), eq(variables['BUILT_WEB'], 'true')) + displayName: Package server (web) - powershell: | . build/azure-pipelines/win32/exec.ps1 @@ -239,11 +282,22 @@ steps: $env:ESRPAADUsername = "$(esrp-aad-username)" $env:ESRPAADPassword = "$(esrp-aad-password)" exec { yarn gulp "vscode-win32-$(VSCODE_ARCH)-system-setup" --sign } - exec { yarn gulp "vscode-win32-$(VSCODE_ARCH)-user-setup" --sign } - displayName: Package setups + $SetupPath = ".build\win32-$(VSCODE_ARCH)\system-setup\VSCodeSetup-$(VSCODE_ARCH)-$(VSCODE_VERSION).exe" + mv .build\win32-$(VSCODE_ARCH)\system-setup\VSCodeSetup.exe $SetupPath + echo "##vso[task.setvariable variable=SYSTEM_SETUP_PATH]$SetupPath" + displayName: Build system setup - - powershell: .\build\azure-pipelines\win32\prepare-publish.ps1 - displayName: Publish + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + $env:ESRPPKI = "$(ESRP-PKI)" + $env:ESRPAADUsername = "$(esrp-aad-username)" + $env:ESRPAADPassword = "$(esrp-aad-password)" + exec { yarn gulp "vscode-win32-$(VSCODE_ARCH)-user-setup" --sign } + $SetupPath = ".build\win32-$(VSCODE_ARCH)\user-setup\VSCodeSetup-$(VSCODE_ARCH)-$(VSCODE_VERSION).exe" + mv .build\win32-$(VSCODE_ARCH)\user-setup\VSCodeSetup.exe $SetupPath + echo "##vso[task.setvariable variable=USER_SETUP_PATH]$SetupPath" + displayName: Build user setup - task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0 displayName: Generate SBOM (client) @@ -267,24 +321,27 @@ steps: artifact: vscode_server_win32_$(VSCODE_ARCH)_sbom condition: and(succeeded(), ne(variables['VSCODE_ARCH'], 'arm64')) - - publish: $(System.DefaultWorkingDirectory)\.build\win32-$(VSCODE_ARCH)\archive\$(ARCHIVE_NAME) + - publish: $(CLIENT_PATH) artifact: vscode_client_win32_$(VSCODE_ARCH)_archive + condition: and(succeededOrFailed(), ne(variables['CLIENT_PATH'], '')) displayName: Publish archive - - publish: $(System.DefaultWorkingDirectory)\.build\win32-$(VSCODE_ARCH)\system-setup\$(SYSTEM_SETUP_NAME) + - publish: $(SERVER_PATH) + artifact: vscode_server_win32_$(VSCODE_ARCH)_archive + condition: and(succeededOrFailed(), ne(variables['SERVER_PATH'], ''), ne(variables['VSCODE_ARCH'], 'arm64')) + displayName: Publish server archive + + - publish: $(WEB_PATH) + artifact: vscode_web_win32_$(VSCODE_ARCH)_archive + condition: and(succeededOrFailed(), ne(variables['WEB_PATH'], ''), ne(variables['VSCODE_ARCH'], 'arm64')) + displayName: Publish web server archive + + - publish: $(SYSTEM_SETUP_PATH) artifact: vscode_client_win32_$(VSCODE_ARCH)_setup + condition: and(succeededOrFailed(), ne(variables['SYSTEM_SETUP_PATH'], '')) displayName: Publish system setup - - publish: $(System.DefaultWorkingDirectory)\.build\win32-$(VSCODE_ARCH)\user-setup\$(USER_SETUP_NAME) + - publish: $(USER_SETUP_PATH) artifact: vscode_client_win32_$(VSCODE_ARCH)_user-setup + condition: and(succeededOrFailed(), ne(variables['USER_SETUP_PATH'], '')) displayName: Publish user setup - - - publish: $(System.DefaultWorkingDirectory)\.build\vscode-server-win32-$(VSCODE_ARCH).zip - artifact: vscode_server_win32_$(VSCODE_ARCH)_archive - displayName: Publish server archive - condition: and(succeeded(), ne(variables['VSCODE_ARCH'], 'arm64')) - - - publish: $(System.DefaultWorkingDirectory)\.build\vscode-server-win32-$(VSCODE_ARCH)-web.zip - artifact: vscode_web_win32_$(VSCODE_ARCH)_archive - displayName: Publish web server archive - condition: and(succeeded(), ne(variables['VSCODE_ARCH'], 'arm64')) diff --git a/build/gulpfile.vscode.win32.js b/build/gulpfile.vscode.win32.js index 6e9a6f331ba..2d6f14551b3 100644 --- a/build/gulpfile.vscode.win32.js +++ b/build/gulpfile.vscode.win32.js @@ -10,7 +10,6 @@ const path = require('path'); const fs = require('fs'); const assert = require('assert'); const cp = require('child_process'); -const _7z = require('7zip')['7z']; const util = require('./lib/util'); const task = require('./lib/task'); const pkg = require('../package.json'); @@ -21,8 +20,6 @@ const mkdirp = require('mkdirp'); const repoPath = path.dirname(__dirname); const buildPath = (/** @type {string} */ arch) => path.join(path.dirname(repoPath), `VSCode-win32-${arch}`); -const zipDir = (/** @type {string} */ arch) => path.join(repoPath, '.build', `win32-${arch}`, 'archive'); -const zipPath = (/** @type {string} */ arch) => path.join(zipDir(arch), `VSCode-win32-${arch}.zip`); const setupDir = (/** @type {string} */ arch, /** @type {string} */ target) => path.join(repoPath, '.build', `win32-${arch}`, `${target}-setup`); const issPath = path.join(__dirname, 'win32', 'code.iss'); const innoSetupPath = path.join(path.dirname(path.dirname(require.resolve('innosetup'))), 'bin', 'ISCC.exe'); @@ -142,23 +139,6 @@ defineWin32SetupTasks('ia32', 'user'); defineWin32SetupTasks('x64', 'user'); defineWin32SetupTasks('arm64', 'user'); -/** - * @param {string} arch - */ -function archiveWin32Setup(arch) { - return cb => { - const args = ['a', '-tzip', zipPath(arch), '-x!CodeSignSummary*.md', '.', '-r']; - - cp.spawn(_7z, args, { stdio: 'inherit', cwd: buildPath(arch) }) - .on('error', cb) - .on('exit', () => cb(null)); - }; -} - -gulp.task(task.define('vscode-win32-ia32-archive', task.series(util.rimraf(zipDir('ia32')), archiveWin32Setup('ia32')))); -gulp.task(task.define('vscode-win32-x64-archive', task.series(util.rimraf(zipDir('x64')), archiveWin32Setup('x64')))); -gulp.task(task.define('vscode-win32-arm64-archive', task.series(util.rimraf(zipDir('arm64')), archiveWin32Setup('arm64')))); - /** * @param {string} arch */ diff --git a/package.json b/package.json index 224789cf9ec..7f011f15bbd 100644 --- a/package.json +++ b/package.json @@ -106,7 +106,6 @@ "yazl": "^2.4.3" }, "devDependencies": { - "7zip": "0.0.6", "@playwright/test": "^1.34.3", "@swc/cli": "0.1.62", "@swc/core": "1.3.62", diff --git a/yarn.lock b/yarn.lock index 8ebb45b9118..1318986ce96 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,11 +2,6 @@ # yarn lockfile v1 -"7zip@0.0.6": - version "0.0.6" - resolved "https://registry.yarnpkg.com/7zip/-/7zip-0.0.6.tgz#9cafb171af82329490353b4816f03347aa150a30" - integrity sha1-nK+xca+CMpSQNTtIFvAzR6oVCjA= - "@ampproject/remapping@^2.1.0": version "2.2.0" resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d" From 8f5865475784bfb666e88732395fa2380a50445c Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Fri, 7 Jul 2023 20:47:17 +0200 Subject: [PATCH 251/926] Fixes #184848 (#187306) --- .../inlineCompletions/browser/inlineCompletionContextKeys.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionContextKeys.ts b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionContextKeys.ts index bcd18d70301..b027df1b39c 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionContextKeys.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionContextKeys.ts @@ -32,8 +32,7 @@ export class InlineCompletionContextKeys extends Disposable { const model = this.model.read(reader); const suggestion = model?.selectedInlineCompletion.read(reader); const ghostText = model?.ghostText.read(reader); - const selectedSuggestItem = model?.selectedSuggestItem.read(reader); - this.inlineCompletionVisible.set(selectedSuggestItem === undefined && ghostText !== undefined && !ghostText.isEmpty()); + this.inlineCompletionVisible.set(ghostText !== undefined && !ghostText.isEmpty()); if (ghostText && suggestion) { this.suppressSuggestions.set(suggestion.inlineCompletion.source.inlineCompletions.suppressSuggestions); From 020b2d5c8e8c2121c2ec484bdeed1fb1d71efbfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Fri, 7 Jul 2023 21:02:36 +0200 Subject: [PATCH 252/926] Make sure shift on Windows/Linux only works when hovering (#187308) fixes #187265 --- .../browser/menuEntryActionViewItem.ts | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/src/vs/platform/actions/browser/menuEntryActionViewItem.ts b/src/vs/platform/actions/browser/menuEntryActionViewItem.ts index f2e783f211f..54400220a8e 100644 --- a/src/vs/platform/actions/browser/menuEntryActionViewItem.ts +++ b/src/vs/platform/actions/browser/menuEntryActionViewItem.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { $, addDisposableListener, append, asCSSUrl, EventType, IModifierKeyStatus, ModifierKeyEmitter, prepend } from 'vs/base/browser/dom'; +import { $, addDisposableListener, append, asCSSUrl, EventType, ModifierKeyEmitter, prepend } from 'vs/base/browser/dom'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { ActionViewItem, BaseActionViewItem, SelectActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { DropdownMenuActionViewItem, IDropdownMenuActionViewItemOptions } from 'vs/base/browser/ui/dropdown/dropdownActionViewItem'; @@ -160,8 +160,13 @@ export class MenuEntryActionViewItem extends ActionViewItem { } if (this._menuItemAction.alt) { - const updateAltState = (keyStatus: IModifierKeyStatus) => { - const wantsAltCommand = !!this._commandAction.alt?.enabled && (keyStatus.altKey || ((isWindows || isLinux) && keyStatus.shiftKey)); + let mouseOverOnWindowsOrLinux = false; + + const updateAltState = () => { + const wantsAltCommand = !!this._menuItemAction.alt?.enabled && ( + this._altKey.keyStatus.altKey + || (this._altKey.keyStatus.shiftKey && mouseOverOnWindowsOrLinux) + ); if (wantsAltCommand !== this._wantsAltCommand) { this._wantsAltCommand = wantsAltCommand; @@ -172,7 +177,20 @@ export class MenuEntryActionViewItem extends ActionViewItem { }; this._register(this._altKey.event(updateAltState)); - updateAltState(this._altKey.keyStatus); + + if (isWindows || isLinux) { + this._register(addDisposableListener(container, 'mouseleave', _ => { + mouseOverOnWindowsOrLinux = false; + updateAltState(); + })); + + this._register(addDisposableListener(container, 'mouseenter', _ => { + mouseOverOnWindowsOrLinux = true; + updateAltState(); + })); + } + + updateAltState(); } } From 0e5c0111e868d4b270273f850c36ee334d690468 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Fri, 7 Jul 2023 13:18:13 -0700 Subject: [PATCH 253/926] fix task reconnection --- .../tasks/browser/abstractTaskService.ts | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index 12fc22bd477..87563e2b78c 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -47,7 +47,7 @@ import { ITextFileService } from 'vs/workbench/services/textfile/common/textfile import { ITerminalGroupService, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { ITerminalProfileResolverService } from 'vs/workbench/contrib/terminal/common/terminal'; -import { ConfiguringTask, ContributedTask, CustomTask, ExecutionEngine, InMemoryTask, ITaskEvent, ITaskIdentifier, ITaskSet, JsonSchemaVersion, KeyedTaskIdentifier, RuntimeType, Task, TaskDefinition, TaskEventKind, TaskGroup, TaskRunSource, TaskSettingId, TaskSorter, TaskSourceKind, TasksSchemaProperties, TASK_RUNNING_STATE, USER_TASKS_GROUP_KEY } from 'vs/workbench/contrib/tasks/common/tasks'; +import { ConfiguringTask, ContributedTask, CustomTask, ExecutionEngine, InMemoryTask, ITaskEvent, ITaskIdentifier, ITaskSet, JsonSchemaVersion, KeyedTaskIdentifier, RuntimeType, Task, TASK_RUNNING_STATE, TaskDefinition, TaskEventKind, TaskGroup, TaskRunSource, TaskSettingId, TaskSorter, TaskSourceKind, TasksSchemaProperties, USER_TASKS_GROUP_KEY } from 'vs/workbench/contrib/tasks/common/tasks'; import { CustomExecutionSupportedContext, ICustomizationProperties, IProblemMatcherRunOptions, ITaskFilter, ITaskProvider, ITaskService, IWorkspaceFolderTaskResult, ProcessExecutionSupportedContext, ServerlessWebContext, ShellExecutionSupportedContext, TaskCommandsRegistered, TaskExecutionSupportedContext } from 'vs/workbench/contrib/tasks/common/taskService'; import { ITaskExecuteResult, ITaskResolver, ITaskSummary, ITaskSystem, ITaskSystemInfo, ITaskTerminateResponse, TaskError, TaskErrors, TaskExecuteKind } from 'vs/workbench/contrib/tasks/common/taskSystem'; import { getTemplates as getTaskTemplates } from 'vs/workbench/contrib/tasks/common/taskTemplates'; @@ -60,15 +60,18 @@ import { IQuickInputService, IQuickPick, IQuickPickItem, IQuickPickSeparator, Qu import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { TaskDefinitionRegistry } from 'vs/workbench/contrib/tasks/common/taskDefinitionRegistry'; +import { raceTimeout } from 'vs/base/common/async'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { once } from 'vs/base/common/functional'; import { toFormattedString } from 'vs/base/common/jsonFormatter'; import { Schemas } from 'vs/base/common/network'; +import { ThemeIcon } from 'vs/base/common/themables'; import { IResolvedTextEditorModel, ITextModelService } from 'vs/editor/common/services/resolverService'; import { TextEditorSelectionRevealType } from 'vs/platform/editor/common/editor'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; +import { TerminalExitReason } from 'vs/platform/terminal/common/terminal'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { ThemeIcon } from 'vs/base/common/themables'; import { IWorkspaceTrustManagementService, IWorkspaceTrustRequestService } from 'vs/platform/workspace/common/workspaceTrust'; import { VirtualWorkspaceContext } from 'vs/workbench/common/contextkeys'; import { EditorResourceAccessor, SaveReason } from 'vs/workbench/common/editor'; @@ -79,10 +82,7 @@ import { ILifecycleService, ShutdownReason, StartupKind } from 'vs/workbench/ser import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite'; import { IPathService } from 'vs/workbench/services/path/common/pathService'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; -import { TerminalExitReason } from 'vs/platform/terminal/common/terminal'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { raceTimeout } from 'vs/base/common/async'; const QUICKOPEN_HISTORY_LIMIT_CONFIG = 'task.quickOpen.history'; const PROBLEM_MATCHER_NEVER_CONFIG = 'task.problemMatchers.neverPrompt'; @@ -224,7 +224,9 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer protected _outputChannel: IOutputChannel; protected readonly _onDidStateChange: Emitter; private _waitForSupportedExecutions: Promise; + private _waitForAllSupportedExecutions: Promise; private _onDidRegisterSupportedExecutions: Emitter = new Emitter(); + private _onDidRegisterAllSupportedExecutions: Emitter = new Emitter(); private _onDidChangeTaskSystemInfo: Emitter = new Emitter(); private _willRestart: boolean = false; public onDidChangeTaskSystemInfo: Event = this._onDidChangeTaskSystemInfo.event; @@ -336,6 +338,9 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer this._waitForSupportedExecutions = new Promise(resolve => { once(this._onDidRegisterSupportedExecutions.event)(() => resolve()); }); + this._waitForAllSupportedExecutions = new Promise(resolve => { + once(this._onDidRegisterAllSupportedExecutions.event)(() => resolve()); + }); if (this._terminalService.getReconnectedTerminals('Task')?.length) { this._attemptTaskReconnection(); } else { @@ -2202,6 +2207,11 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return new Map(); } await this._waitForSupportedExecutions; + if (runSource === TaskRunSource.Reconnect) { + await raceTimeout(this._waitForAllSupportedExecutions, 3000, () => { + console.warn('Timed out waiting for all supported executions for task reconnection'); + }); + } await this._whenTaskSystemReady; if (this._workspaceTasksPromise) { return this._workspaceTasksPromise; From dc37566b2ce107bd28a047d3450bc98ffe624453 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Fri, 7 Jul 2023 13:22:30 -0700 Subject: [PATCH 254/926] decrease time --- .../workbench/contrib/tasks/browser/abstractTaskService.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index 87563e2b78c..0bb0287040a 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -370,6 +370,9 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer // update tasks so an incomplete list isn't returned when getWorkspaceTasks is called this._workspaceTasksPromise = undefined; this._onDidRegisterSupportedExecutions.fire(); + if (custom && shell && process) { + this._onDidRegisterAllSupportedExecutions.fire(); + } } private _attemptTaskReconnection(): void { @@ -2208,7 +2211,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } await this._waitForSupportedExecutions; if (runSource === TaskRunSource.Reconnect) { - await raceTimeout(this._waitForAllSupportedExecutions, 3000, () => { + await raceTimeout(this._waitForAllSupportedExecutions, 500, () => { console.warn('Timed out waiting for all supported executions for task reconnection'); }); } From 9a665005673c04e9333fc1276901c4fcc9133784 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Fri, 7 Jul 2023 13:40:42 -0700 Subject: [PATCH 255/926] fix #187088 --- .../workbench/contrib/terminal/browser/terminalInstance.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 9ecf7d39c00..6889de6378d 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -59,7 +59,6 @@ import { IWorkspaceTrustRequestService } from 'vs/platform/workspace/common/work import { PANEL_BACKGROUND, SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; import { IViewDescriptorService, IViewsService, ViewContainerLocation } from 'vs/workbench/common/views'; import { AccessibilityVerbositySettingId } from 'vs/workbench/contrib/accessibility/browser/accessibilityContribution'; -import { TaskSettingId } from 'vs/workbench/contrib/tasks/common/tasks'; import { IRequestAddInstanceToGroupEvent, ITerminalContribution, ITerminalInstance, TerminalDataTransfers } from 'vs/workbench/contrib/terminal/browser/terminal'; import { TerminalLaunchHelpAction } from 'vs/workbench/contrib/terminal/browser/terminalActions'; import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; @@ -677,7 +676,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { } get persistentProcessId(): number | undefined { return this._processManager.persistentProcessId; } - get shouldPersist(): boolean { return this._processManager.shouldPersist && !this.shellLaunchConfig.isTransient && (!this.reconnectionProperties || this._configurationService.getValue(TaskSettingId.Reconnection) === true); } + get shouldPersist(): boolean { return this._processManager.shouldPersist && !this.shellLaunchConfig.isTransient && (!this.reconnectionProperties || this._configurationService.getValue('task.reconnection') === true); } public static getXtermConstructor(keybindingService: IKeybindingService, contextKeyService: IContextKeyService) { const keybinding = keybindingService.lookupKeybinding(TerminalCommandId.FocusAccessibleBuffer, contextKeyService); @@ -2098,7 +2097,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { // Has not been interacted with !this._processManager.hasWrittenData && // Not a feature terminal or is a reconnecting task terminal (TODO: Need to explain the latter case) - (!this._shellLaunchConfig.isFeatureTerminal || (this.reconnectionProperties && this._configurationService.getValue(TaskSettingId.Reconnection) === true)) && + (!this._shellLaunchConfig.isFeatureTerminal || (this.reconnectionProperties && this._configurationService.getValue('task.reconnection') === true)) && // Not a custom pty !this._shellLaunchConfig.customPtyImplementation && // Not an extension owned terminal From 19a963829361c19924a6a66c30458a7a4334d864 Mon Sep 17 00:00:00 2001 From: Viknet Date: Sat, 8 Jul 2023 01:07:50 +0300 Subject: [PATCH 256/926] resolvers: fallback to one authority when resolving nesting remote authorities fail (#187276) * resolvers: fallback to the old behaviour for nesting remote authorities * style fix * log warning * improve error handling v2 * fix condition --- .../remote/common/remoteAuthorityResolver.ts | 7 ++++- .../api/common/extHostExtensionService.ts | 27 ++++++++++++------- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/vs/platform/remote/common/remoteAuthorityResolver.ts b/src/vs/platform/remote/common/remoteAuthorityResolver.ts index c67541b0bc6..e3c17feb4c9 100644 --- a/src/vs/platform/remote/common/remoteAuthorityResolver.ts +++ b/src/vs/platform/remote/common/remoteAuthorityResolver.ts @@ -91,7 +91,8 @@ export enum RemoteAuthorityResolverErrorCode { Unknown = 'Unknown', NotAvailable = 'NotAvailable', TemporarilyNotAvailable = 'TemporarilyNotAvailable', - NoResolverFound = 'NoResolverFound' + NoResolverFound = 'NoResolverFound', + InvalidAuthority = 'InvalidAuthority' } export class RemoteAuthorityResolverError extends ErrorNoTelemetry { @@ -108,6 +109,10 @@ export class RemoteAuthorityResolverError extends ErrorNoTelemetry { return (err instanceof RemoteAuthorityResolverError) && err._code === RemoteAuthorityResolverErrorCode.NoResolverFound; } + public static isInvalidAuthority(err: any): boolean { + return (err instanceof RemoteAuthorityResolverError) && err._code === RemoteAuthorityResolverErrorCode.InvalidAuthority; + } + public static isHandled(err: any): boolean { return (err instanceof RemoteAuthorityResolverError) && err.isHandled; } diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts index 6e5e3e2c100..82618146832 100644 --- a/src/vs/workbench/api/common/extHostExtensionService.ts +++ b/src/vs/workbench/api/common/extHostExtensionService.ts @@ -795,7 +795,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme private async _activateAndGetResolver(remoteAuthority: string): Promise<{ authorityPrefix: string; resolver: vscode.RemoteAuthorityResolver | undefined }> { const authorityPlusIndex = remoteAuthority.indexOf('+'); if (authorityPlusIndex === -1) { - throw new Error(`Not an authority that can be resolved!`); + throw new RemoteAuthorityResolverError(`Not an authority that can be resolved!`, RemoteAuthorityResolverErrorCode.InvalidAuthority); } const authorityPrefix = remoteAuthority.substr(0, authorityPlusIndex); @@ -809,6 +809,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme const sw = StopWatch.create(false); const prefix = () => `[resolveAuthority(${getRemoteAuthorityPrefix(remoteAuthorityChain)},${resolveAttempt})][${sw.elapsed()}ms] `; const logInfo = (msg: string) => this._logService.info(`${prefix()}${msg}`); + const logWarning = (msg: string) => this._logService.warn(`${prefix()}${msg}`); const logError = (msg: string, err: any = undefined) => this._logService.error(`${prefix()}${msg}`, err); const normalizeError = (err: unknown) => { if (err instanceof RemoteAuthorityResolverError) { @@ -824,20 +825,26 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme throw err; }; + const getResolver = async (remoteAuthority: string) => { + logInfo(`activating resolver for ${remoteAuthority}...`); + const { resolver, authorityPrefix } = await this._activateAndGetResolver(remoteAuthority); + if (!resolver) { + logError(`no resolver for ${authorityPrefix}`); + throw new RemoteAuthorityResolverError(`No remote extension installed to resolve ${authorityPrefix}.`, RemoteAuthorityResolverErrorCode.NoResolverFound); + } + return { resolver, authorityPrefix, remoteAuthority }; + }; + const chain = remoteAuthorityChain.split(/@|%40/g).reverse(); logInfo(`activating remote resolvers ${chain.join(' -> ')}`); let resolvers; try { - resolvers = await Promise.all(chain.map(async remoteAuthority => { - logInfo(`activating resolver...`); - const { resolver, authorityPrefix } = await this._activateAndGetResolver(remoteAuthority); - if (!resolver) { - logError(`no resolver`); - throw new RemoteAuthorityResolverError(`No remote extension installed to resolve ${authorityPrefix}.`, RemoteAuthorityResolverErrorCode.NoResolverFound); - } - return { resolver, authorityPrefix, remoteAuthority }; - })); + resolvers = await Promise.all(chain.map(getResolver)).catch(async (e: Error) => { + if (!(e instanceof RemoteAuthorityResolverError) || e._code !== RemoteAuthorityResolverErrorCode.InvalidAuthority) { throw e; } + logWarning(`resolving nested authorities failed: ${e.message}`); + return [await getResolver(remoteAuthorityChain)]; + }); } catch (e) { return normalizeError(e); } From 5150ef0ce77ef5516e55af4b8272f25907b55953 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Sat, 8 Jul 2023 00:47:05 +0200 Subject: [PATCH 257/926] disable tests on win32-ia32 (#187313) --- build/azure-pipelines/product-build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build/azure-pipelines/product-build.yml b/build/azure-pipelines/product-build.yml index 7407cd1825b..d0a5f753431 100644 --- a/build/azure-pipelines/product-build.yml +++ b/build/azure-pipelines/product-build.yml @@ -346,9 +346,9 @@ stages: parameters: VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} VSCODE_CIBUILD: ${{ variables.VSCODE_CIBUILD }} - VSCODE_RUN_UNIT_TESTS: ${{ eq(parameters.VSCODE_STEP_ON_IT, false) }} - VSCODE_RUN_INTEGRATION_TESTS: ${{ eq(parameters.VSCODE_STEP_ON_IT, false) }} - VSCODE_RUN_SMOKE_TESTS: ${{ eq(parameters.VSCODE_STEP_ON_IT, false) }} + VSCODE_RUN_UNIT_TESTS: false + VSCODE_RUN_INTEGRATION_TESTS: false + VSCODE_RUN_SMOKE_TESTS: false - ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_WIN32_ARM64, true)) }}: - job: WindowsARM64 From afacb63ab5fbefa94b2fb76efe950fd1679f6989 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Jul 2023 10:03:51 +0200 Subject: [PATCH 258/926] Bump tough-cookie from 4.1.2 to 4.1.3 in /extensions/notebook-renderers (#187410) Bumps [tough-cookie](https://github.com/salesforce/tough-cookie) from 4.1.2 to 4.1.3. - [Release notes](https://github.com/salesforce/tough-cookie/releases) - [Changelog](https://github.com/salesforce/tough-cookie/blob/master/CHANGELOG.md) - [Commits](https://github.com/salesforce/tough-cookie/compare/v4.1.2...v4.1.3) --- updated-dependencies: - dependency-name: tough-cookie dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- extensions/notebook-renderers/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/extensions/notebook-renderers/yarn.lock b/extensions/notebook-renderers/yarn.lock index ba3ff6b48d9..fac17bd8f60 100644 --- a/extensions/notebook-renderers/yarn.lock +++ b/extensions/notebook-renderers/yarn.lock @@ -334,9 +334,9 @@ symbol-tree@^3.2.4: integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== tough-cookie@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.2.tgz#e53e84b85f24e0b65dd526f46628db6c85f6b874" - integrity sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ== + version "4.1.3" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.3.tgz#97b9adb0728b42280aa3d814b6b999b2ff0318bf" + integrity sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw== dependencies: psl "^1.1.33" punycode "^2.1.1" From dccc793f79fa850393e7a0668788340a1a6813d8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Jul 2023 10:04:17 +0200 Subject: [PATCH 259/926] Bump tough-cookie from 4.0.0 to 4.1.3 (#187385) Bumps [tough-cookie](https://github.com/salesforce/tough-cookie) from 4.0.0 to 4.1.3. - [Release notes](https://github.com/salesforce/tough-cookie/releases) - [Changelog](https://github.com/salesforce/tough-cookie/blob/master/CHANGELOG.md) - [Commits](https://github.com/salesforce/tough-cookie/compare/v4.0.0...v4.1.3) --- updated-dependencies: - dependency-name: tough-cookie dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/yarn.lock b/yarn.lock index 1318986ce96..13650bd8310 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8319,6 +8319,11 @@ q@^1.1.2: resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= +querystringify@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" + integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== + queue-tick@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/queue-tick/-/queue-tick-1.0.1.tgz#f6f07ac82c1fd60f82e098b417a80e52f1f4c142" @@ -8588,6 +8593,11 @@ require-main-filename@^2.0.0: resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== + resolve-alpn@^1.0.0: version "1.2.1" resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9" @@ -9851,13 +9861,14 @@ token-types@^5.0.0-alpha.2: ieee754 "^1.2.1" tough-cookie@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4" - integrity sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg== + version "4.1.3" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.3.tgz#97b9adb0728b42280aa3d814b6b999b2ff0318bf" + integrity sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw== dependencies: psl "^1.1.33" punycode "^2.1.1" - universalify "^0.1.2" + universalify "^0.2.0" + url-parse "^1.5.3" tr46@~0.0.3: version "0.0.3" @@ -10122,11 +10133,16 @@ universal-user-agent@^6.0.0: resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee" integrity sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w== -universalify@^0.1.0, universalify@^0.1.2: +universalify@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== +universalify@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0" + integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg== + unquote@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/unquote/-/unquote-1.1.1.tgz#8fded7324ec6e88a0ff8b905e7c098cdc086d544" @@ -10165,6 +10181,14 @@ urix@^0.1.0: resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= +url-parse@^1.5.3: + version "1.5.10" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" + integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== + dependencies: + querystringify "^2.1.1" + requires-port "^1.0.0" + use@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" From 8c8e8600b6ec20279e2fa977ff242d0238f486c0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Jul 2023 10:05:01 +0200 Subject: [PATCH 260/926] Bump semver from 5.5.1 to 7.5.2 in /extensions/typescript-language-features (#185968) Bump semver in /extensions/typescript-language-features Bumps [semver](https://github.com/npm/node-semver) from 5.5.1 to 7.5.2. - [Release notes](https://github.com/npm/node-semver/releases) - [Changelog](https://github.com/npm/node-semver/blob/main/CHANGELOG.md) - [Commits](https://github.com/npm/node-semver/compare/v5.5.1...v7.5.2) --- updated-dependencies: - dependency-name: semver dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .../typescript-language-features/package.json | 2 +- .../typescript-language-features/yarn.lock | 22 +++++++++++++++---- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index b9b63d61495..c7cf530dbb9 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -35,7 +35,7 @@ "dependencies": { "@vscode/extension-telemetry": "0.7.5", "jsonc-parser": "^3.2.0", - "semver": "5.5.1", + "semver": "7.5.2", "vscode-tas-client": "^0.1.63", "@vscode/sync-api-client": "^0.7.2", "@vscode/sync-api-common": "^0.7.2", diff --git a/extensions/typescript-language-features/yarn.lock b/extensions/typescript-language-features/yarn.lock index a86d12d0e3f..d3d03733ceb 100644 --- a/extensions/typescript-language-features/yarn.lock +++ b/extensions/typescript-language-features/yarn.lock @@ -348,6 +348,13 @@ jsonc-parser@^3.2.0: resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.0.tgz#31ff3f4c2b9793f89c67212627c51c6394f88e76" integrity sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w== +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + mime-db@1.52.0: version "1.52.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" @@ -365,10 +372,12 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -semver@5.5.1: - version "5.5.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.1.tgz#7dfdd8814bdb7cabc7be0fb1d734cfb66c940477" - integrity sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw== +semver@7.5.2: + version "7.5.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.2.tgz#5b851e66d1be07c1cdaf37dfc856f543325a2beb" + integrity sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ== + dependencies: + lru-cache "^6.0.0" semver@^5.3.0, semver@^5.4.1: version "5.7.1" @@ -413,3 +422,8 @@ vscode-uri@3.0.3, vscode-uri@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.0.3.tgz#a95c1ce2e6f41b7549f86279d19f47951e4f4d84" integrity sha512-EcswR2S8bpR7fD0YPeS7r2xXExrScVMxg4MedACaWHEtx9ftCF/qHG1xGkolzTPcEmjTavCQgbVzHUIdTMzFGA== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== From 7eaae680dc341be01f6563bdd0e43c7519ecd086 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Mon, 10 Jul 2023 10:59:42 +0200 Subject: [PATCH 261/926] adding some comments that will be removed later --- .../inlineChat/browser/inlineChatController.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts index 8f6bc67f54a..7a9d3d2ee99 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -215,10 +215,14 @@ export class InlineChatController implements IEditorContribution { private async [State.CREATE_SESSION](options: InlineChatRunOptions): Promise { if (this._activeSession) { + console.log('before clearing the session store'); this._sessionStore.clear(); + console.log('before releasing teh session'); this._inlineChatSessionService.releaseSession(this._activeSession); + console.log('before calling pause'); await this[State.PAUSE](); } + console.log('this._activeSession after the cleaning : ', this._activeSession); assertType(this._activeSession === undefined); assertType(this._editor.hasModel()); @@ -282,6 +286,8 @@ export class InlineChatController implements IEditorContribution { } private async [State.INIT_UI](options: InlineChatRunOptions): Promise { + console.log('inside of init ui'); + console.log('this._activeSession : ', this._activeSession); assertType(this._activeSession); // hide/cancel inline completions when invoking IE @@ -347,11 +353,14 @@ export class InlineChatController implements IEditorContribution { })); if (!this._activeSession.lastExchange) { + console.log('before waiting for input'); return State.WAIT_FOR_INPUT; } else if (options.isUnstashed) { delete options.isUnstashed; + console.log('before apply response'); return State.APPLY_RESPONSE; } else { + console.log('before show response'); return State.SHOW_RESPONSE; } } @@ -631,6 +640,8 @@ export class InlineChatController implements IEditorContribution { private async [State.PAUSE]() { + console.log('entered into pause state'); + this._ctxDidEdit.reset(); this._ctxUserDidEdit.reset(); this._ctxLastResponseType.reset(); @@ -650,6 +661,7 @@ export class InlineChatController implements IEditorContribution { } private async [State.ACCEPT]() { + console.log('inside of accept'); assertType(this._activeSession); assertType(this._strategy); this._sessionStore.clear(); From 00db67e44079d78850922701c0e63e3053d48640 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Mon, 10 Jul 2023 11:07:58 +0200 Subject: [PATCH 262/926] Skip failing integration tests (#187424) Part of #187245 --- extensions/emmet/src/test/abbreviationAction.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/emmet/src/test/abbreviationAction.test.ts b/extensions/emmet/src/test/abbreviationAction.test.ts index 17ccacfc94a..beb3bfa1ef3 100644 --- a/extensions/emmet/src/test/abbreviationAction.test.ts +++ b/extensions/emmet/src/test/abbreviationAction.test.ts @@ -410,7 +410,7 @@ suite('Tests for Expand Abbreviations (HTML)', () => { // }); // }); - test('No expanding when html is excluded in the settings in completion list', async () => { + test.skip('No expanding when html is excluded in the settings in completion list', async () => { const oldConfig = workspace.getConfiguration('emmet').inspect('excludeLanguages')?.globalValue; await workspace.getConfiguration('emmet').update('excludeLanguages', ['html'], ConfigurationTarget.Global); await testHtmlCompletionProvider(new Selection(9, 6, 9, 6), '', '', true); @@ -469,7 +469,7 @@ suite('Tests for jsx, xml and xsl', () => { }); }); - test('Expand abbreviation with no self closing tags for html', () => { + test.skip('Expand abbreviation with no self closing tags for html', () => { return withRandomFileEditor('img', 'html', async (editor, _doc) => { editor.selection = new Selection(0, 6, 0, 6); await expandEmmetAbbreviation({ language: 'html' }); From 71935eff90daf4b5d7423b01fc48b2f019150b41 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Mon, 10 Jul 2023 11:11:34 +0200 Subject: [PATCH 263/926] Fixes https://github.com/microsoft/monaco-editor/issues/4021 (#187318) --- .../browser/controller/textAreaHandler.ts | 2 + src/vs/editor/common/config/editorOptions.ts | 9 + .../common/standalone/standaloneEnums.ts | 281 ++++++++--------- src/vs/monaco.d.ts | 286 +++++++++--------- 4 files changed, 298 insertions(+), 280 deletions(-) diff --git a/src/vs/editor/browser/controller/textAreaHandler.ts b/src/vs/editor/browser/controller/textAreaHandler.ts index 0d5c3317051..bc95af4fe9e 100644 --- a/src/vs/editor/browser/controller/textAreaHandler.ts +++ b/src/vs/editor/browser/controller/textAreaHandler.ts @@ -177,6 +177,7 @@ export class TextAreaHandler extends ViewPart { this.textArea.setAttribute('autocomplete', 'off'); this.textArea.setAttribute('spellcheck', 'false'); this.textArea.setAttribute('aria-label', this._getAriaLabel(options)); + this.textArea.setAttribute('aria-required', options.get(EditorOption.ariaRequired) ? 'true' : 'false'); this.textArea.setAttribute('tabindex', String(options.get(EditorOption.tabIndex))); this.textArea.setAttribute('role', 'textbox'); this.textArea.setAttribute('aria-roledescription', nls.localize('editor', "editor")); @@ -601,6 +602,7 @@ export class TextAreaHandler extends ViewPart { const { tabSize } = this._context.viewModel.model.getOptions(); this.textArea.domNode.style.tabSize = `${tabSize * this._fontInfo.spaceWidth}px`; this.textArea.setAttribute('aria-label', this._getAriaLabel(options)); + this.textArea.setAttribute('aria-required', options.get(EditorOption.ariaRequired) ? 'true' : 'false'); this.textArea.setAttribute('tabindex', String(options.get(EditorOption.tabIndex))); if (e.hasChanged(EditorOption.domReadOnly) || e.hasChanged(EditorOption.readOnly)) { diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index b2f09749f2f..96dbb05c275 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -58,6 +58,11 @@ export interface IEditorOptions { * The aria label for the editor's textarea (when it is focused). */ ariaLabel?: string; + + /** + * Whether the aria-required attribute should be set on the editors textarea. + */ + ariaRequired?: boolean; /** * Control whether a screen reader announces inline suggestion content immediately. */ @@ -4972,6 +4977,7 @@ export const enum EditorOption { accessibilitySupport, accessibilityPageSize, ariaLabel, + ariaRequired, autoClosingBrackets, screenReaderAnnounceInlineSuggestion, autoClosingDelete, @@ -5142,6 +5148,9 @@ export const EditorOptions = { ariaLabel: register(new EditorStringOption( EditorOption.ariaLabel, 'ariaLabel', nls.localize('editorViewAccessibleLabel', "Editor content") )), + ariaRequired: register(new EditorBooleanOption( + EditorOption.ariaRequired, 'ariaRequired', false, undefined + )), screenReaderAnnounceInlineSuggestion: register(new EditorBooleanOption( EditorOption.screenReaderAnnounceInlineSuggestion, 'screenReaderAnnounceInlineSuggestion', true, { diff --git a/src/vs/editor/common/standalone/standaloneEnums.ts b/src/vs/editor/common/standalone/standaloneEnums.ts index d48dc8f7aa1..d92f0c1272e 100644 --- a/src/vs/editor/common/standalone/standaloneEnums.ts +++ b/src/vs/editor/common/standalone/standaloneEnums.ts @@ -178,146 +178,147 @@ export enum EditorOption { accessibilitySupport = 2, accessibilityPageSize = 3, ariaLabel = 4, - autoClosingBrackets = 5, - screenReaderAnnounceInlineSuggestion = 6, - autoClosingDelete = 7, - autoClosingOvertype = 8, - autoClosingQuotes = 9, - autoIndent = 10, - automaticLayout = 11, - autoSurround = 12, - bracketPairColorization = 13, - guides = 14, - codeLens = 15, - codeLensFontFamily = 16, - codeLensFontSize = 17, - colorDecorators = 18, - colorDecoratorsLimit = 19, - columnSelection = 20, - comments = 21, - contextmenu = 22, - copyWithSyntaxHighlighting = 23, - cursorBlinking = 24, - cursorSmoothCaretAnimation = 25, - cursorStyle = 26, - cursorSurroundingLines = 27, - cursorSurroundingLinesStyle = 28, - cursorWidth = 29, - disableLayerHinting = 30, - disableMonospaceOptimizations = 31, - domReadOnly = 32, - dragAndDrop = 33, - dropIntoEditor = 34, - emptySelectionClipboard = 35, - experimentalWhitespaceRendering = 36, - extraEditorClassName = 37, - fastScrollSensitivity = 38, - find = 39, - fixedOverflowWidgets = 40, - folding = 41, - foldingStrategy = 42, - foldingHighlight = 43, - foldingImportsByDefault = 44, - foldingMaximumRegions = 45, - unfoldOnClickAfterEndOfLine = 46, - fontFamily = 47, - fontInfo = 48, - fontLigatures = 49, - fontSize = 50, - fontWeight = 51, - fontVariations = 52, - formatOnPaste = 53, - formatOnType = 54, - glyphMargin = 55, - gotoLocation = 56, - hideCursorInOverviewRuler = 57, - hover = 58, - inDiffEditor = 59, - inlineSuggest = 60, - letterSpacing = 61, - lightbulb = 62, - lineDecorationsWidth = 63, - lineHeight = 64, - lineNumbers = 65, - lineNumbersMinChars = 66, - linkedEditing = 67, - links = 68, - matchBrackets = 69, - minimap = 70, - mouseStyle = 71, - mouseWheelScrollSensitivity = 72, - mouseWheelZoom = 73, - multiCursorMergeOverlapping = 74, - multiCursorModifier = 75, - multiCursorPaste = 76, - multiCursorLimit = 77, - occurrencesHighlight = 78, - overviewRulerBorder = 79, - overviewRulerLanes = 80, - padding = 81, - pasteAs = 82, - parameterHints = 83, - peekWidgetDefaultFocus = 84, - definitionLinkOpensInPeek = 85, - quickSuggestions = 86, - quickSuggestionsDelay = 87, - readOnly = 88, - readOnlyMessage = 89, - renameOnType = 90, - renderControlCharacters = 91, - renderFinalNewline = 92, - renderLineHighlight = 93, - renderLineHighlightOnlyWhenFocus = 94, - renderValidationDecorations = 95, - renderWhitespace = 96, - revealHorizontalRightPadding = 97, - roundedSelection = 98, - rulers = 99, - scrollbar = 100, - scrollBeyondLastColumn = 101, - scrollBeyondLastLine = 102, - scrollPredominantAxis = 103, - selectionClipboard = 104, - selectionHighlight = 105, - selectOnLineNumbers = 106, - showFoldingControls = 107, - showUnused = 108, - snippetSuggestions = 109, - smartSelect = 110, - smoothScrolling = 111, - stickyScroll = 112, - stickyTabStops = 113, - stopRenderingLineAfter = 114, - suggest = 115, - suggestFontSize = 116, - suggestLineHeight = 117, - suggestOnTriggerCharacters = 118, - suggestSelection = 119, - tabCompletion = 120, - tabIndex = 121, - unicodeHighlighting = 122, - unusualLineTerminators = 123, - useShadowDOM = 124, - useTabStops = 125, - wordBreak = 126, - wordSeparators = 127, - wordWrap = 128, - wordWrapBreakAfterCharacters = 129, - wordWrapBreakBeforeCharacters = 130, - wordWrapColumn = 131, - wordWrapOverride1 = 132, - wordWrapOverride2 = 133, - wrappingIndent = 134, - wrappingStrategy = 135, - showDeprecated = 136, - inlayHints = 137, - editorClassName = 138, - pixelRatio = 139, - tabFocusMode = 140, - layoutInfo = 141, - wrappingInfo = 142, - defaultColorDecorators = 143, - colorDecoratorsActivatedOn = 144 + ariaRequired = 5, + autoClosingBrackets = 6, + screenReaderAnnounceInlineSuggestion = 7, + autoClosingDelete = 8, + autoClosingOvertype = 9, + autoClosingQuotes = 10, + autoIndent = 11, + automaticLayout = 12, + autoSurround = 13, + bracketPairColorization = 14, + guides = 15, + codeLens = 16, + codeLensFontFamily = 17, + codeLensFontSize = 18, + colorDecorators = 19, + colorDecoratorsLimit = 20, + columnSelection = 21, + comments = 22, + contextmenu = 23, + copyWithSyntaxHighlighting = 24, + cursorBlinking = 25, + cursorSmoothCaretAnimation = 26, + cursorStyle = 27, + cursorSurroundingLines = 28, + cursorSurroundingLinesStyle = 29, + cursorWidth = 30, + disableLayerHinting = 31, + disableMonospaceOptimizations = 32, + domReadOnly = 33, + dragAndDrop = 34, + dropIntoEditor = 35, + emptySelectionClipboard = 36, + experimentalWhitespaceRendering = 37, + extraEditorClassName = 38, + fastScrollSensitivity = 39, + find = 40, + fixedOverflowWidgets = 41, + folding = 42, + foldingStrategy = 43, + foldingHighlight = 44, + foldingImportsByDefault = 45, + foldingMaximumRegions = 46, + unfoldOnClickAfterEndOfLine = 47, + fontFamily = 48, + fontInfo = 49, + fontLigatures = 50, + fontSize = 51, + fontWeight = 52, + fontVariations = 53, + formatOnPaste = 54, + formatOnType = 55, + glyphMargin = 56, + gotoLocation = 57, + hideCursorInOverviewRuler = 58, + hover = 59, + inDiffEditor = 60, + inlineSuggest = 61, + letterSpacing = 62, + lightbulb = 63, + lineDecorationsWidth = 64, + lineHeight = 65, + lineNumbers = 66, + lineNumbersMinChars = 67, + linkedEditing = 68, + links = 69, + matchBrackets = 70, + minimap = 71, + mouseStyle = 72, + mouseWheelScrollSensitivity = 73, + mouseWheelZoom = 74, + multiCursorMergeOverlapping = 75, + multiCursorModifier = 76, + multiCursorPaste = 77, + multiCursorLimit = 78, + occurrencesHighlight = 79, + overviewRulerBorder = 80, + overviewRulerLanes = 81, + padding = 82, + pasteAs = 83, + parameterHints = 84, + peekWidgetDefaultFocus = 85, + definitionLinkOpensInPeek = 86, + quickSuggestions = 87, + quickSuggestionsDelay = 88, + readOnly = 89, + readOnlyMessage = 90, + renameOnType = 91, + renderControlCharacters = 92, + renderFinalNewline = 93, + renderLineHighlight = 94, + renderLineHighlightOnlyWhenFocus = 95, + renderValidationDecorations = 96, + renderWhitespace = 97, + revealHorizontalRightPadding = 98, + roundedSelection = 99, + rulers = 100, + scrollbar = 101, + scrollBeyondLastColumn = 102, + scrollBeyondLastLine = 103, + scrollPredominantAxis = 104, + selectionClipboard = 105, + selectionHighlight = 106, + selectOnLineNumbers = 107, + showFoldingControls = 108, + showUnused = 109, + snippetSuggestions = 110, + smartSelect = 111, + smoothScrolling = 112, + stickyScroll = 113, + stickyTabStops = 114, + stopRenderingLineAfter = 115, + suggest = 116, + suggestFontSize = 117, + suggestLineHeight = 118, + suggestOnTriggerCharacters = 119, + suggestSelection = 120, + tabCompletion = 121, + tabIndex = 122, + unicodeHighlighting = 123, + unusualLineTerminators = 124, + useShadowDOM = 125, + useTabStops = 126, + wordBreak = 127, + wordSeparators = 128, + wordWrap = 129, + wordWrapBreakAfterCharacters = 130, + wordWrapBreakBeforeCharacters = 131, + wordWrapColumn = 132, + wordWrapOverride1 = 133, + wordWrapOverride2 = 134, + wrappingIndent = 135, + wrappingStrategy = 136, + showDeprecated = 137, + inlayHints = 138, + editorClassName = 139, + pixelRatio = 140, + tabFocusMode = 141, + layoutInfo = 142, + wrappingInfo = 143, + defaultColorDecorators = 144, + colorDecoratorsActivatedOn = 145 } /** diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index d59db506ee1..9e2a863d592 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -3221,6 +3221,10 @@ declare namespace monaco.editor { * The aria label for the editor's textarea (when it is focused). */ ariaLabel?: string; + /** + * Whether the aria-required attribute should be set on the editors textarea. + */ + ariaRequired?: boolean; /** * Control whether a screen reader announces inline suggestion content immediately. */ @@ -4861,146 +4865,147 @@ declare namespace monaco.editor { accessibilitySupport = 2, accessibilityPageSize = 3, ariaLabel = 4, - autoClosingBrackets = 5, - screenReaderAnnounceInlineSuggestion = 6, - autoClosingDelete = 7, - autoClosingOvertype = 8, - autoClosingQuotes = 9, - autoIndent = 10, - automaticLayout = 11, - autoSurround = 12, - bracketPairColorization = 13, - guides = 14, - codeLens = 15, - codeLensFontFamily = 16, - codeLensFontSize = 17, - colorDecorators = 18, - colorDecoratorsLimit = 19, - columnSelection = 20, - comments = 21, - contextmenu = 22, - copyWithSyntaxHighlighting = 23, - cursorBlinking = 24, - cursorSmoothCaretAnimation = 25, - cursorStyle = 26, - cursorSurroundingLines = 27, - cursorSurroundingLinesStyle = 28, - cursorWidth = 29, - disableLayerHinting = 30, - disableMonospaceOptimizations = 31, - domReadOnly = 32, - dragAndDrop = 33, - dropIntoEditor = 34, - emptySelectionClipboard = 35, - experimentalWhitespaceRendering = 36, - extraEditorClassName = 37, - fastScrollSensitivity = 38, - find = 39, - fixedOverflowWidgets = 40, - folding = 41, - foldingStrategy = 42, - foldingHighlight = 43, - foldingImportsByDefault = 44, - foldingMaximumRegions = 45, - unfoldOnClickAfterEndOfLine = 46, - fontFamily = 47, - fontInfo = 48, - fontLigatures = 49, - fontSize = 50, - fontWeight = 51, - fontVariations = 52, - formatOnPaste = 53, - formatOnType = 54, - glyphMargin = 55, - gotoLocation = 56, - hideCursorInOverviewRuler = 57, - hover = 58, - inDiffEditor = 59, - inlineSuggest = 60, - letterSpacing = 61, - lightbulb = 62, - lineDecorationsWidth = 63, - lineHeight = 64, - lineNumbers = 65, - lineNumbersMinChars = 66, - linkedEditing = 67, - links = 68, - matchBrackets = 69, - minimap = 70, - mouseStyle = 71, - mouseWheelScrollSensitivity = 72, - mouseWheelZoom = 73, - multiCursorMergeOverlapping = 74, - multiCursorModifier = 75, - multiCursorPaste = 76, - multiCursorLimit = 77, - occurrencesHighlight = 78, - overviewRulerBorder = 79, - overviewRulerLanes = 80, - padding = 81, - pasteAs = 82, - parameterHints = 83, - peekWidgetDefaultFocus = 84, - definitionLinkOpensInPeek = 85, - quickSuggestions = 86, - quickSuggestionsDelay = 87, - readOnly = 88, - readOnlyMessage = 89, - renameOnType = 90, - renderControlCharacters = 91, - renderFinalNewline = 92, - renderLineHighlight = 93, - renderLineHighlightOnlyWhenFocus = 94, - renderValidationDecorations = 95, - renderWhitespace = 96, - revealHorizontalRightPadding = 97, - roundedSelection = 98, - rulers = 99, - scrollbar = 100, - scrollBeyondLastColumn = 101, - scrollBeyondLastLine = 102, - scrollPredominantAxis = 103, - selectionClipboard = 104, - selectionHighlight = 105, - selectOnLineNumbers = 106, - showFoldingControls = 107, - showUnused = 108, - snippetSuggestions = 109, - smartSelect = 110, - smoothScrolling = 111, - stickyScroll = 112, - stickyTabStops = 113, - stopRenderingLineAfter = 114, - suggest = 115, - suggestFontSize = 116, - suggestLineHeight = 117, - suggestOnTriggerCharacters = 118, - suggestSelection = 119, - tabCompletion = 120, - tabIndex = 121, - unicodeHighlighting = 122, - unusualLineTerminators = 123, - useShadowDOM = 124, - useTabStops = 125, - wordBreak = 126, - wordSeparators = 127, - wordWrap = 128, - wordWrapBreakAfterCharacters = 129, - wordWrapBreakBeforeCharacters = 130, - wordWrapColumn = 131, - wordWrapOverride1 = 132, - wordWrapOverride2 = 133, - wrappingIndent = 134, - wrappingStrategy = 135, - showDeprecated = 136, - inlayHints = 137, - editorClassName = 138, - pixelRatio = 139, - tabFocusMode = 140, - layoutInfo = 141, - wrappingInfo = 142, - defaultColorDecorators = 143, - colorDecoratorsActivatedOn = 144 + ariaRequired = 5, + autoClosingBrackets = 6, + screenReaderAnnounceInlineSuggestion = 7, + autoClosingDelete = 8, + autoClosingOvertype = 9, + autoClosingQuotes = 10, + autoIndent = 11, + automaticLayout = 12, + autoSurround = 13, + bracketPairColorization = 14, + guides = 15, + codeLens = 16, + codeLensFontFamily = 17, + codeLensFontSize = 18, + colorDecorators = 19, + colorDecoratorsLimit = 20, + columnSelection = 21, + comments = 22, + contextmenu = 23, + copyWithSyntaxHighlighting = 24, + cursorBlinking = 25, + cursorSmoothCaretAnimation = 26, + cursorStyle = 27, + cursorSurroundingLines = 28, + cursorSurroundingLinesStyle = 29, + cursorWidth = 30, + disableLayerHinting = 31, + disableMonospaceOptimizations = 32, + domReadOnly = 33, + dragAndDrop = 34, + dropIntoEditor = 35, + emptySelectionClipboard = 36, + experimentalWhitespaceRendering = 37, + extraEditorClassName = 38, + fastScrollSensitivity = 39, + find = 40, + fixedOverflowWidgets = 41, + folding = 42, + foldingStrategy = 43, + foldingHighlight = 44, + foldingImportsByDefault = 45, + foldingMaximumRegions = 46, + unfoldOnClickAfterEndOfLine = 47, + fontFamily = 48, + fontInfo = 49, + fontLigatures = 50, + fontSize = 51, + fontWeight = 52, + fontVariations = 53, + formatOnPaste = 54, + formatOnType = 55, + glyphMargin = 56, + gotoLocation = 57, + hideCursorInOverviewRuler = 58, + hover = 59, + inDiffEditor = 60, + inlineSuggest = 61, + letterSpacing = 62, + lightbulb = 63, + lineDecorationsWidth = 64, + lineHeight = 65, + lineNumbers = 66, + lineNumbersMinChars = 67, + linkedEditing = 68, + links = 69, + matchBrackets = 70, + minimap = 71, + mouseStyle = 72, + mouseWheelScrollSensitivity = 73, + mouseWheelZoom = 74, + multiCursorMergeOverlapping = 75, + multiCursorModifier = 76, + multiCursorPaste = 77, + multiCursorLimit = 78, + occurrencesHighlight = 79, + overviewRulerBorder = 80, + overviewRulerLanes = 81, + padding = 82, + pasteAs = 83, + parameterHints = 84, + peekWidgetDefaultFocus = 85, + definitionLinkOpensInPeek = 86, + quickSuggestions = 87, + quickSuggestionsDelay = 88, + readOnly = 89, + readOnlyMessage = 90, + renameOnType = 91, + renderControlCharacters = 92, + renderFinalNewline = 93, + renderLineHighlight = 94, + renderLineHighlightOnlyWhenFocus = 95, + renderValidationDecorations = 96, + renderWhitespace = 97, + revealHorizontalRightPadding = 98, + roundedSelection = 99, + rulers = 100, + scrollbar = 101, + scrollBeyondLastColumn = 102, + scrollBeyondLastLine = 103, + scrollPredominantAxis = 104, + selectionClipboard = 105, + selectionHighlight = 106, + selectOnLineNumbers = 107, + showFoldingControls = 108, + showUnused = 109, + snippetSuggestions = 110, + smartSelect = 111, + smoothScrolling = 112, + stickyScroll = 113, + stickyTabStops = 114, + stopRenderingLineAfter = 115, + suggest = 116, + suggestFontSize = 117, + suggestLineHeight = 118, + suggestOnTriggerCharacters = 119, + suggestSelection = 120, + tabCompletion = 121, + tabIndex = 122, + unicodeHighlighting = 123, + unusualLineTerminators = 124, + useShadowDOM = 125, + useTabStops = 126, + wordBreak = 127, + wordSeparators = 128, + wordWrap = 129, + wordWrapBreakAfterCharacters = 130, + wordWrapBreakBeforeCharacters = 131, + wordWrapColumn = 132, + wordWrapOverride1 = 133, + wordWrapOverride2 = 134, + wrappingIndent = 135, + wrappingStrategy = 136, + showDeprecated = 137, + inlayHints = 138, + editorClassName = 139, + pixelRatio = 140, + tabFocusMode = 141, + layoutInfo = 142, + wrappingInfo = 143, + defaultColorDecorators = 144, + colorDecoratorsActivatedOn = 145 } export const EditorOptions: { @@ -5009,6 +5014,7 @@ declare namespace monaco.editor { accessibilitySupport: IEditorOption; accessibilityPageSize: IEditorOption; ariaLabel: IEditorOption; + ariaRequired: IEditorOption; screenReaderAnnounceInlineSuggestion: IEditorOption; autoClosingBrackets: IEditorOption; autoClosingDelete: IEditorOption; From 293e46b36015307d159b3ea38ec8dbe534be9c76 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Mon, 10 Jul 2023 11:24:03 +0200 Subject: [PATCH 264/926] Fixes #187153 (#187289) --- .../diffEditorWidget2/diffEditorViewModel.ts | 102 ++++++++++-------- 1 file changed, 57 insertions(+), 45 deletions(-) diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts index bcb4c13371d..f9d77296cea 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts @@ -59,6 +59,50 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo const contentChangedSignal = observableSignal('contentChangedSignal'); const debouncer = this._register(new RunOnceScheduler(() => contentChangedSignal.trigger(undefined), 200)); + const updateUnchangedRegions = (result: IDocumentDiff, tx: ITransaction) => { + const newUnchangedRegions = UnchangedRegion.fromDiffs(result.changes, model.original.getLineCount(), model.modified.getLineCount()); + + // Transfer state from cur state + const lastUnchangedRegions = this._unchangedRegions.get(); + const lastUnchangedRegionsOrigRanges = lastUnchangedRegions.originalDecorationIds + .map(id => model.original.getDecorationRange(id)) + .filter(r => !!r) + .map(r => LineRange.fromRange(r!)); + const lastUnchangedRegionsModRanges = lastUnchangedRegions.modifiedDecorationIds + .map(id => model.modified.getDecorationRange(id)) + .filter(r => !!r) + .map(r => LineRange.fromRange(r!)); + + const originalDecorationIds = model.original.deltaDecorations( + lastUnchangedRegions.originalDecorationIds, + newUnchangedRegions.map(r => ({ range: r.originalRange.toInclusiveRange()!, options: { description: 'unchanged' } })) + ); + const modifiedDecorationIds = model.modified.deltaDecorations( + lastUnchangedRegions.modifiedDecorationIds, + newUnchangedRegions.map(r => ({ range: r.modifiedRange.toInclusiveRange()!, options: { description: 'unchanged' } })) + ); + + + for (const r of newUnchangedRegions) { + for (let i = 0; i < lastUnchangedRegions.regions.length; i++) { + if (r.originalRange.intersectsStrict(lastUnchangedRegionsOrigRanges[i]) + && r.modifiedRange.intersectsStrict(lastUnchangedRegionsModRanges[i])) { + r.setHiddenModifiedRange(lastUnchangedRegions.regions[i].getHiddenModifiedRange(undefined), tx); + break; + } + } + } + this._unchangedRegions.set( + { + regions: newUnchangedRegions, + originalDecorationIds, + modifiedDecorationIds + }, + tx + ); + }; + + this._register(model.modified.onDidChangeContent((e) => { const diff = this._diff.get(); if (!diff) { @@ -69,9 +113,12 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo const result = applyModifiedEdits(this._lastDiff!, textEdits, model.original, model.modified); if (result) { this._lastDiff = result; - this._diff.set(DiffState.fromDiffResult(this._lastDiff), undefined); - const currentSyncedMovedText = this.syncedMovedTexts.get(); - this.syncedMovedTexts.set(currentSyncedMovedText ? this._lastDiff.moves.find(m => m.lineRangeMapping.modifiedRange.intersect(currentSyncedMovedText.lineRangeMapping.modifiedRange)) : undefined, undefined); + transaction(tx => { + this._diff.set(DiffState.fromDiffResult(this._lastDiff!), tx); + updateUnchangedRegions(result, tx); + const currentSyncedMovedText = this.syncedMovedTexts.get(); + this.syncedMovedTexts.set(currentSyncedMovedText ? this._lastDiff!.moves.find(m => m.lineRangeMapping.modifiedRange.intersect(currentSyncedMovedText.lineRangeMapping.modifiedRange)) : undefined, tx); + }); } debouncer.schedule(); @@ -86,9 +133,12 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo const result = applyOriginalEdits(this._lastDiff!, textEdits, model.original, model.modified); if (result) { this._lastDiff = result; - this._diff.set(DiffState.fromDiffResult(this._lastDiff), undefined); - const currentSyncedMovedText = this.syncedMovedTexts.get(); - this.syncedMovedTexts.set(currentSyncedMovedText ? this._lastDiff.moves.find(m => m.lineRangeMapping.modifiedRange.intersect(currentSyncedMovedText.lineRangeMapping.modifiedRange)) : undefined, undefined); + transaction(tx => { + this._diff.set(DiffState.fromDiffResult(this._lastDiff!), tx); + updateUnchangedRegions(result, tx); + const currentSyncedMovedText = this.syncedMovedTexts.get(); + this.syncedMovedTexts.set(currentSyncedMovedText ? this._lastDiff!.moves.find(m => m.lineRangeMapping.modifiedRange.intersect(currentSyncedMovedText.lineRangeMapping.modifiedRange)) : undefined, tx); + }); } debouncer.schedule(); @@ -124,38 +174,9 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo result = applyOriginalEdits(result, originalTextEditInfos, model.original, model.modified) ?? result; result = applyModifiedEdits(result, modifiedTextEditInfos, model.original, model.modified) ?? result; - const newUnchangedRegions = UnchangedRegion.fromDiffs(result.changes, model.original.getLineCount(), model.modified.getLineCount()); - - // Transfer state from cur state - const lastUnchangedRegions = this._unchangedRegions.get(); - const lastUnchangedRegionsOrigRanges = lastUnchangedRegions.originalDecorationIds - .map(id => model.original.getDecorationRange(id)) - .filter(r => !!r) - .map(r => LineRange.fromRange(r!)); - const lastUnchangedRegionsModRanges = lastUnchangedRegions.modifiedDecorationIds - .map(id => model.modified.getDecorationRange(id)) - .filter(r => !!r) - .map(r => LineRange.fromRange(r!)); - - const originalDecorationIds = model.original.deltaDecorations( - lastUnchangedRegions.originalDecorationIds, - newUnchangedRegions.map(r => ({ range: r.originalRange.toInclusiveRange()!, options: { description: 'unchanged' } })) - ); - const modifiedDecorationIds = model.modified.deltaDecorations( - lastUnchangedRegions.modifiedDecorationIds, - newUnchangedRegions.map(r => ({ range: r.modifiedRange.toInclusiveRange()!, options: { description: 'unchanged' } })) - ); transaction(tx => { - for (const r of newUnchangedRegions) { - for (let i = 0; i < lastUnchangedRegions.regions.length; i++) { - if (r.originalRange.intersectsStrict(lastUnchangedRegionsOrigRanges[i]) - && r.modifiedRange.intersectsStrict(lastUnchangedRegionsModRanges[i])) { - r.setHiddenModifiedRange(lastUnchangedRegions.regions[i].getHiddenModifiedRange(undefined), tx); - break; - } - } - } + updateUnchangedRegions(result, tx); this._lastDiff = result; const state = DiffState.fromDiffResult(result); @@ -163,15 +184,6 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo this._isDiffUpToDate.set(true, tx); const currentSyncedMovedText = this.syncedMovedTexts.get(); this.syncedMovedTexts.set(currentSyncedMovedText ? this._lastDiff.moves.find(m => m.lineRangeMapping.modifiedRange.intersect(currentSyncedMovedText.lineRangeMapping.modifiedRange)) : undefined, tx); - - this._unchangedRegions.set( - { - regions: newUnchangedRegions, - originalDecorationIds, - modifiedDecorationIds - }, - tx - ); }); })); } From b7b88e4dbb934b06f227ec75ab1f72565a8648ae Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Mon, 10 Jul 2023 12:19:33 +0200 Subject: [PATCH 265/926] Opt the team into the new create PR view (#187434) --- .vscode/settings.json | 1 + 1 file changed, 1 insertion(+) diff --git a/.vscode/settings.json b/.vscode/settings.json index 85e608eb901..5d2930c5a92 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -135,6 +135,7 @@ "git", "sash" ], + "githubPullRequests.experimental.createView": true, "debug.javascript.terminalOptions": { "outFiles": [ "${workspaceFolder}/out/**/*.js", From 0ccb215397ced6315dc5851a5f01f566b0fcdb9b Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Mon, 10 Jul 2023 13:05:18 +0200 Subject: [PATCH 266/926] making stole focus also check whether the hover focused key is set to true --- src/vs/editor/contrib/hover/browser/contentHover.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/hover/browser/contentHover.ts b/src/vs/editor/contrib/hover/browser/contentHover.ts index 67b790508b2..170d097fe80 100644 --- a/src/vs/editor/contrib/hover/browser/contentHover.ts +++ b/src/vs/editor/contrib/hover/browser/contentHover.ts @@ -726,12 +726,15 @@ export class ContentHoverWidget extends ResizableContentWidget { if (!this._visibleData) { return; } + const stoleFocus = this._visibleData.stoleFocus || this._hoverFocusedKey.get(); this._setHoverData(undefined); this._resizableNode.maxSize = new dom.Dimension(Infinity, Infinity); this._resizableNode.clearSashHoverState(); this._hoverFocusedKey.set(false); this._editor.layoutContentWidget(this); - this._editor.focus(); + if (stoleFocus) { + this._editor.focus(); + } } private _removeConstraintsRenderNormally(): void { From 68a99e2f46d36f624ff0b1f57509411022785319 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Mon, 10 Jul 2023 14:27:07 +0200 Subject: [PATCH 267/926] Fixes inline completion/suggest widget issue. (#187458) --- .../inlineCompletions/browser/commands.ts | 5 ++++- .../browser/inlineCompletionsModel.ts | 16 +++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/vs/editor/contrib/inlineCompletions/browser/commands.ts b/src/vs/editor/contrib/inlineCompletions/browser/commands.ts index 2370e210bf3..799433568e0 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/commands.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/commands.ts @@ -11,6 +11,7 @@ import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { showNextInlineSuggestionActionId, showPreviousInlineSuggestionActionId, inlineSuggestCommitId } from 'vs/editor/contrib/inlineCompletions/browser/commandIds'; import { InlineCompletionContextKeys } from 'vs/editor/contrib/inlineCompletions/browser/inlineCompletionContextKeys'; import { InlineCompletionsController } from 'vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController'; +import { Context as SuggestContext } from 'vs/editor/contrib/suggest/browser/suggest'; import * as nls from 'vs/nls'; import { MenuId, Action2 } from 'vs/platform/actions/common/actions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -85,6 +86,7 @@ export class AcceptNextWordOfInlineCompletion extends EditorAction { kbOpts: { weight: KeybindingWeight.EditorContrib + 1, primary: KeyMod.CtrlCmd | KeyCode.RightArrow, + kbExpr: ContextKeyExpr.and(EditorContextKeys.writable, InlineCompletionContextKeys.inlineSuggestionVisible), }, menuOpts: [{ menuId: MenuId.InlineSuggestionToolbar, @@ -145,7 +147,8 @@ export class AcceptInlineCompletion extends EditorAction { kbExpr: ContextKeyExpr.and( InlineCompletionContextKeys.inlineSuggestionVisible, EditorContextKeys.tabMovesFocus.toNegated(), - InlineCompletionContextKeys.inlineSuggestionHasIndentationLessThanTabSize + InlineCompletionContextKeys.inlineSuggestionHasIndentationLessThanTabSize, + SuggestContext.Visible.toNegated() ), } }); diff --git a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel.ts b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel.ts index c66ec2a6d56..01946f09986 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel.ts @@ -278,11 +278,11 @@ export class InlineCompletionsModel extends Disposable { throw new BugIndicatingError(); } - const ghostText = this.ghostText.get(); - const completion = this.selectedInlineCompletion.get()?.toInlineCompletion(undefined); - if (!ghostText || !completion) { + const state = this.state.get(); + if (!state || state.ghostText.isEmpty() || !state.completion) { return; } + const completion = state.completion.toInlineCompletion(undefined); editor.pushUndoStop(); if (completion.snippetInfo) { @@ -370,11 +370,12 @@ export class InlineCompletionsModel extends Disposable { throw new BugIndicatingError(); } - const ghostText = this.ghostText.get(); - const completion = this.selectedInlineCompletion.get()?.toInlineCompletion(undefined); - if (!ghostText || !completion) { + const state = this.state.get(); + if (!state || state.ghostText.isEmpty() || !state.completion) { return; } + const ghostText = state.ghostText; + const completion = state.completion.toInlineCompletion(undefined); if (completion.snippetInfo || completion.filterText !== completion.insertText) { // not in WYSIWYG mode, partial commit might change completion, thus it is not supported @@ -382,9 +383,6 @@ export class InlineCompletionsModel extends Disposable { return; } - if (ghostText.parts.length === 0) { - return; - } const firstPart = ghostText.parts[0]; const position = new Position(ghostText.lineNumber, firstPart.column); const line = firstPart.lines.join('\n'); From 2ff3c9a7e7d77b801643685e6eaaa76074656462 Mon Sep 17 00:00:00 2001 From: "Rickey K. Liang" Date: Mon, 10 Jul 2023 13:48:51 +0100 Subject: [PATCH 268/926] Revert Python docstring color (#184938) Revert b4368691e928191228076c0c329d815972e07fe1 all color themes: treat comment docstrings as comments too (#182162) --- .../theme-abyss/themes/abyss-color-theme.json | 7 +- extensions/theme-defaults/themes/dark_vs.json | 6 +- .../theme-defaults/themes/hc_black.json | 6 +- .../theme-defaults/themes/hc_light.json | 6 +- .../theme-defaults/themes/light_vs.json | 6 +- .../themes/kimbie-dark-color-theme.json | 3 +- .../themes/dimmed-monokai-color-theme.json | 7 +- .../themes/monokai-color-theme.json | 7 +- .../themes/quietlight-color-theme.json | 3 +- .../theme-red/themes/Red-color-theme.json | 7 +- .../themes/solarized-dark-color-theme.json | 7 +- .../themes/solarized-light-color-theme.json | 7 +- .../tomorrow-night-blue-color-theme.json | 7 +- .../test/colorize-results/test_py.json | 288 +++++++++--------- 14 files changed, 164 insertions(+), 203 deletions(-) diff --git a/extensions/theme-abyss/themes/abyss-color-theme.json b/extensions/theme-abyss/themes/abyss-color-theme.json index 4202bdd7d2a..e81c3b9adca 100644 --- a/extensions/theme-abyss/themes/abyss-color-theme.json +++ b/extensions/theme-abyss/themes/abyss-color-theme.json @@ -17,11 +17,8 @@ } }, { - "name": "Comments", - "scope": [ - "comment", - "string.quoted.docstring" - ], + "name": "Comment", + "scope": "comment", "settings": { "foreground": "#384887" } diff --git a/extensions/theme-defaults/themes/dark_vs.json b/extensions/theme-defaults/themes/dark_vs.json index 04e33e07063..38af44cd2c1 100644 --- a/extensions/theme-defaults/themes/dark_vs.json +++ b/extensions/theme-defaults/themes/dark_vs.json @@ -57,11 +57,7 @@ } }, { - "name": "Comments", - "scope": [ - "comment", - "string.quoted.docstring" - ], + "scope": "comment", "settings": { "foreground": "#6A9955" } diff --git a/extensions/theme-defaults/themes/hc_black.json b/extensions/theme-defaults/themes/hc_black.json index dbc3d2808c2..b8eda7974b7 100644 --- a/extensions/theme-defaults/themes/hc_black.json +++ b/extensions/theme-defaults/themes/hc_black.json @@ -43,11 +43,7 @@ } }, { - "name": "Comments", - "scope": [ - "comment", - "string.quoted.docstring" - ], + "scope": "comment", "settings": { "foreground": "#7ca668" } diff --git a/extensions/theme-defaults/themes/hc_light.json b/extensions/theme-defaults/themes/hc_light.json index 5a06c116a1a..83a4083f90b 100644 --- a/extensions/theme-defaults/themes/hc_light.json +++ b/extensions/theme-defaults/themes/hc_light.json @@ -27,11 +27,7 @@ } }, { - "name": "Comments", - "scope": [ - "comment", - "string.quoted.docstring" - ], + "scope": "comment", "settings": { "foreground": "#515151" } diff --git a/extensions/theme-defaults/themes/light_vs.json b/extensions/theme-defaults/themes/light_vs.json index 2cfe0ec0448..89589bb0e74 100644 --- a/extensions/theme-defaults/themes/light_vs.json +++ b/extensions/theme-defaults/themes/light_vs.json @@ -62,11 +62,7 @@ } }, { - "name": "Comments", - "scope": [ - "comment", - "string.quoted.docstring" - ], + "scope": "comment", "settings": { "foreground": "#008000" } diff --git a/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json b/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json index b8f95bf86aa..eeb4eeb6b88 100644 --- a/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json +++ b/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json @@ -81,8 +81,7 @@ "name": "Comments", "scope": [ "comment", - "punctuation.definition.comment", - "string.quoted.docstring" + "punctuation.definition.comment" ], "settings": { "foreground": "#a57a4c" diff --git a/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json b/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json index 2ed1bcc96e0..ea84bededd5 100644 --- a/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json +++ b/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json @@ -78,11 +78,8 @@ } }, { - "name": "Comments", - "scope": [ - "comment", - "string.quoted.docstring" - ], + "name": "Comment", + "scope": "comment", "settings": { "fontStyle": "", "foreground": "#9A9B99" diff --git a/extensions/theme-monokai/themes/monokai-color-theme.json b/extensions/theme-monokai/themes/monokai-color-theme.json index 2fddaa5972c..6489b0dd39c 100644 --- a/extensions/theme-monokai/themes/monokai-color-theme.json +++ b/extensions/theme-monokai/themes/monokai-color-theme.json @@ -118,11 +118,8 @@ } }, { - "name": "Comments", - "scope": [ - "comment", - "string.quoted.docstring" - ], + "name": "Comment", + "scope": "comment", "settings": { "foreground": "#88846f" } diff --git a/extensions/theme-quietlight/themes/quietlight-color-theme.json b/extensions/theme-quietlight/themes/quietlight-color-theme.json index a01c73d3ec5..9d55f2e362b 100644 --- a/extensions/theme-quietlight/themes/quietlight-color-theme.json +++ b/extensions/theme-quietlight/themes/quietlight-color-theme.json @@ -20,8 +20,7 @@ "name": "Comments", "scope": [ "comment", - "punctuation.definition.comment", - "string.quoted.docstring" + "punctuation.definition.comment" ], "settings": { "fontStyle": "italic", diff --git a/extensions/theme-red/themes/Red-color-theme.json b/extensions/theme-red/themes/Red-color-theme.json index cf0f69316b2..c139400dc56 100644 --- a/extensions/theme-red/themes/Red-color-theme.json +++ b/extensions/theme-red/themes/Red-color-theme.json @@ -77,11 +77,8 @@ } }, { - "name": "Comments", - "scope": [ - "comment", - "string.quoted.docstring" - ], + "name": "Comment", + "scope": "comment", "settings": { "fontStyle": "italic", "foreground": "#e7c0c0ff" diff --git a/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json b/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json index 3abb94bd426..e10c6e67403 100644 --- a/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json +++ b/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json @@ -17,11 +17,8 @@ } }, { - "name": "Comments", - "scope": [ - "comment", - "string.quoted.docstring" - ], + "name": "Comment", + "scope": "comment", "settings": { "fontStyle": "italic", "foreground": "#586E75" diff --git a/extensions/theme-solarized-light/themes/solarized-light-color-theme.json b/extensions/theme-solarized-light/themes/solarized-light-color-theme.json index 19ccf4fc92e..8b4074c9a07 100644 --- a/extensions/theme-solarized-light/themes/solarized-light-color-theme.json +++ b/extensions/theme-solarized-light/themes/solarized-light-color-theme.json @@ -17,11 +17,8 @@ } }, { - "name": "Comments", - "scope": [ - "comment", - "string.quoted.docstring" - ], + "name": "Comment", + "scope": "comment", "settings": { "fontStyle": "italic", "foreground": "#93A1A1" diff --git a/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-color-theme.json b/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-color-theme.json index 5591d39f1a9..8e24e6fe4de 100644 --- a/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-color-theme.json +++ b/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-color-theme.json @@ -78,11 +78,8 @@ } }, { - "name": "Comments", - "scope": [ - "comment", - "string.quoted.docstring" - ], + "name": "Comment", + "scope": "comment", "settings": { "foreground": "#7285B7" } diff --git a/extensions/vscode-colorize-tests/test/colorize-results/test_py.json b/extensions/vscode-colorize-tests/test/colorize-results/test_py.json index 9d40766533d..e858cd5f201 100644 --- a/extensions/vscode-colorize-tests/test/colorize-results/test_py.json +++ b/extensions/vscode-colorize-tests/test/colorize-results/test_py.json @@ -395,42 +395,42 @@ "c": "'''", "t": "source.python string.quoted.docstring.multi.python punctuation.definition.string.begin.python", "r": { - "dark_plus": "string.quoted.docstring: #6A9955", - "light_plus": "string.quoted.docstring: #008000", - "dark_vs": "string.quoted.docstring: #6A9955", - "light_vs": "string.quoted.docstring: #008000", - "hc_black": "string.quoted.docstring: #7CA668", - "dark_modern": "string.quoted.docstring: #6A9955", - "hc_light": "string.quoted.docstring: #515151", - "light_modern": "string.quoted.docstring: #008000" + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" } }, { "c": "Make the monkey eat N bananas!", "t": "source.python string.quoted.docstring.multi.python", "r": { - "dark_plus": "string.quoted.docstring: #6A9955", - "light_plus": "string.quoted.docstring: #008000", - "dark_vs": "string.quoted.docstring: #6A9955", - "light_vs": "string.quoted.docstring: #008000", - "hc_black": "string.quoted.docstring: #7CA668", - "dark_modern": "string.quoted.docstring: #6A9955", - "hc_light": "string.quoted.docstring: #515151", - "light_modern": "string.quoted.docstring: #008000" + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" } }, { "c": "'''", "t": "source.python string.quoted.docstring.multi.python punctuation.definition.string.end.python", "r": { - "dark_plus": "string.quoted.docstring: #6A9955", - "light_plus": "string.quoted.docstring: #008000", - "dark_vs": "string.quoted.docstring: #6A9955", - "light_vs": "string.quoted.docstring: #008000", - "hc_black": "string.quoted.docstring: #7CA668", - "dark_modern": "string.quoted.docstring: #6A9955", - "hc_light": "string.quoted.docstring: #515151", - "light_modern": "string.quoted.docstring: #008000" + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" } }, { @@ -3181,42 +3181,42 @@ "c": "\"\"\"", "t": "source.python string.quoted.docstring.multi.python punctuation.definition.string.begin.python", "r": { - "dark_plus": "string.quoted.docstring: #6A9955", - "light_plus": "string.quoted.docstring: #008000", - "dark_vs": "string.quoted.docstring: #6A9955", - "light_vs": "string.quoted.docstring: #008000", - "hc_black": "string.quoted.docstring: #7CA668", - "dark_modern": "string.quoted.docstring: #6A9955", - "hc_light": "string.quoted.docstring: #515151", - "light_modern": "string.quoted.docstring: #008000" + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" } }, { "c": "Berechnung der zu zahlenden Steuern fuer ein zu versteuerndes Einkommen von x", "t": "source.python string.quoted.docstring.multi.python", "r": { - "dark_plus": "string.quoted.docstring: #6A9955", - "light_plus": "string.quoted.docstring: #008000", - "dark_vs": "string.quoted.docstring: #6A9955", - "light_vs": "string.quoted.docstring: #008000", - "hc_black": "string.quoted.docstring: #7CA668", - "dark_modern": "string.quoted.docstring: #6A9955", - "hc_light": "string.quoted.docstring: #515151", - "light_modern": "string.quoted.docstring: #008000" + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" } }, { "c": "\"\"\"", "t": "source.python string.quoted.docstring.multi.python punctuation.definition.string.end.python", "r": { - "dark_plus": "string.quoted.docstring: #6A9955", - "light_plus": "string.quoted.docstring: #008000", - "dark_vs": "string.quoted.docstring: #6A9955", - "light_vs": "string.quoted.docstring: #008000", - "hc_black": "string.quoted.docstring: #7CA668", - "dark_modern": "string.quoted.docstring: #6A9955", - "hc_light": "string.quoted.docstring: #515151", - "light_modern": "string.quoted.docstring: #008000" + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" } }, { @@ -7423,56 +7423,56 @@ "c": "'''", "t": "source.python string.quoted.docstring.raw.multi.python punctuation.definition.string.begin.python", "r": { - "dark_plus": "string.quoted.docstring: #6A9955", - "light_plus": "string.quoted.docstring: #008000", - "dark_vs": "string.quoted.docstring: #6A9955", - "light_vs": "string.quoted.docstring: #008000", - "hc_black": "string.quoted.docstring: #7CA668", - "dark_modern": "string.quoted.docstring: #6A9955", - "hc_light": "string.quoted.docstring: #515151", - "light_modern": "string.quoted.docstring: #008000" + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" } }, { "c": "Module docstring", "t": "source.python string.quoted.docstring.raw.multi.python", "r": { - "dark_plus": "string.quoted.docstring: #6A9955", - "light_plus": "string.quoted.docstring: #008000", - "dark_vs": "string.quoted.docstring: #6A9955", - "light_vs": "string.quoted.docstring: #008000", - "hc_black": "string.quoted.docstring: #7CA668", - "dark_modern": "string.quoted.docstring: #6A9955", - "hc_light": "string.quoted.docstring: #515151", - "light_modern": "string.quoted.docstring: #008000" + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" } }, { "c": " Some text followed by code sample:", "t": "source.python string.quoted.docstring.raw.multi.python", "r": { - "dark_plus": "string.quoted.docstring: #6A9955", - "light_plus": "string.quoted.docstring: #008000", - "dark_vs": "string.quoted.docstring: #6A9955", - "light_vs": "string.quoted.docstring: #008000", - "hc_black": "string.quoted.docstring: #7CA668", - "dark_modern": "string.quoted.docstring: #6A9955", - "hc_light": "string.quoted.docstring: #515151", - "light_modern": "string.quoted.docstring: #008000" + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" } }, { "c": " ", "t": "source.python string.quoted.docstring.raw.multi.python", "r": { - "dark_plus": "string.quoted.docstring: #6A9955", - "light_plus": "string.quoted.docstring: #008000", - "dark_vs": "string.quoted.docstring: #6A9955", - "light_vs": "string.quoted.docstring: #008000", - "hc_black": "string.quoted.docstring: #7CA668", - "dark_modern": "string.quoted.docstring: #6A9955", - "hc_light": "string.quoted.docstring: #515151", - "light_modern": "string.quoted.docstring: #008000" + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" } }, { @@ -7493,28 +7493,28 @@ "c": "for a in foo(2, b=1,", "t": "source.python string.quoted.docstring.raw.multi.python", "r": { - "dark_plus": "string.quoted.docstring: #6A9955", - "light_plus": "string.quoted.docstring: #008000", - "dark_vs": "string.quoted.docstring: #6A9955", - "light_vs": "string.quoted.docstring: #008000", - "hc_black": "string.quoted.docstring: #7CA668", - "dark_modern": "string.quoted.docstring: #6A9955", - "hc_light": "string.quoted.docstring: #515151", - "light_modern": "string.quoted.docstring: #008000" + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" } }, { "c": " ", "t": "source.python string.quoted.docstring.raw.multi.python", "r": { - "dark_plus": "string.quoted.docstring: #6A9955", - "light_plus": "string.quoted.docstring: #008000", - "dark_vs": "string.quoted.docstring: #6A9955", - "light_vs": "string.quoted.docstring: #008000", - "hc_black": "string.quoted.docstring: #7CA668", - "dark_modern": "string.quoted.docstring: #6A9955", - "hc_light": "string.quoted.docstring: #515151", - "light_modern": "string.quoted.docstring: #008000" + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" } }, { @@ -7535,28 +7535,28 @@ "c": " c=3):", "t": "source.python string.quoted.docstring.raw.multi.python", "r": { - "dark_plus": "string.quoted.docstring: #6A9955", - "light_plus": "string.quoted.docstring: #008000", - "dark_vs": "string.quoted.docstring: #6A9955", - "light_vs": "string.quoted.docstring: #008000", - "hc_black": "string.quoted.docstring: #7CA668", - "dark_modern": "string.quoted.docstring: #6A9955", - "hc_light": "string.quoted.docstring: #515151", - "light_modern": "string.quoted.docstring: #008000" + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" } }, { "c": " ", "t": "source.python string.quoted.docstring.raw.multi.python", "r": { - "dark_plus": "string.quoted.docstring: #6A9955", - "light_plus": "string.quoted.docstring: #008000", - "dark_vs": "string.quoted.docstring: #6A9955", - "light_vs": "string.quoted.docstring: #008000", - "hc_black": "string.quoted.docstring: #7CA668", - "dark_modern": "string.quoted.docstring: #6A9955", - "hc_light": "string.quoted.docstring: #515151", - "light_modern": "string.quoted.docstring: #008000" + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" } }, { @@ -7577,56 +7577,56 @@ "c": " print(a)", "t": "source.python string.quoted.docstring.raw.multi.python", "r": { - "dark_plus": "string.quoted.docstring: #6A9955", - "light_plus": "string.quoted.docstring: #008000", - "dark_vs": "string.quoted.docstring: #6A9955", - "light_vs": "string.quoted.docstring: #008000", - "hc_black": "string.quoted.docstring: #7CA668", - "dark_modern": "string.quoted.docstring: #6A9955", - "hc_light": "string.quoted.docstring: #515151", - "light_modern": "string.quoted.docstring: #008000" + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" } }, { "c": " 0", "t": "source.python string.quoted.docstring.raw.multi.python", "r": { - "dark_plus": "string.quoted.docstring: #6A9955", - "light_plus": "string.quoted.docstring: #008000", - "dark_vs": "string.quoted.docstring: #6A9955", - "light_vs": "string.quoted.docstring: #008000", - "hc_black": "string.quoted.docstring: #7CA668", - "dark_modern": "string.quoted.docstring: #6A9955", - "hc_light": "string.quoted.docstring: #515151", - "light_modern": "string.quoted.docstring: #008000" + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" } }, { "c": " 1", "t": "source.python string.quoted.docstring.raw.multi.python", "r": { - "dark_plus": "string.quoted.docstring: #6A9955", - "light_plus": "string.quoted.docstring: #008000", - "dark_vs": "string.quoted.docstring: #6A9955", - "light_vs": "string.quoted.docstring: #008000", - "hc_black": "string.quoted.docstring: #7CA668", - "dark_modern": "string.quoted.docstring: #6A9955", - "hc_light": "string.quoted.docstring: #515151", - "light_modern": "string.quoted.docstring: #008000" + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" } }, { "c": "'''", "t": "source.python string.quoted.docstring.raw.multi.python punctuation.definition.string.end.python", "r": { - "dark_plus": "string.quoted.docstring: #6A9955", - "light_plus": "string.quoted.docstring: #008000", - "dark_vs": "string.quoted.docstring: #6A9955", - "light_vs": "string.quoted.docstring: #008000", - "hc_black": "string.quoted.docstring: #7CA668", - "dark_modern": "string.quoted.docstring: #6A9955", - "hc_light": "string.quoted.docstring: #515151", - "light_modern": "string.quoted.docstring: #008000" + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string: #0F4A85", + "light_modern": "string: #A31515" } } ] \ No newline at end of file From 060dfba0dd0c6b103abd26c2c0fc873539735d9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Mon, 10 Jul 2023 15:15:06 +0200 Subject: [PATCH 269/926] simplify screencast keyboard options (#187469) fixes #179541 --- .../browser/actions/developerActions.ts | 80 +++++++++++-------- 1 file changed, 48 insertions(+), 32 deletions(-) diff --git a/src/vs/workbench/browser/actions/developerActions.ts b/src/vs/workbench/browser/actions/developerActions.ts index b8d2fd75363..91297a82197 100644 --- a/src/vs/workbench/browser/actions/developerActions.ts +++ b/src/vs/workbench/browser/actions/developerActions.ts @@ -92,6 +92,13 @@ class InspectContextKeysAction extends Action2 { } } +interface IScreencastKeyboardOptions { + readonly showKeys?: boolean; + readonly showCommands?: boolean; + readonly showCommandGroups?: boolean; + readonly showSingleEditorCursorMoves?: boolean; +} + class ToggleScreencastModeAction extends Action2 { static disposable: IDisposable | undefined; @@ -259,11 +266,12 @@ class ToggleScreencastModeAction extends Action2 { return; } + const options = configurationService.getValue('screencastMode.keyboardOptions'); const event = new StandardKeyboardEvent(e); const shortcut = keybindingService.softDispatch(event, event.target); // Hide the single arrow key pressed - if (shortcut.kind === ResultKind.KbFound && shortcut.commandId && configurationService.getValue('screencastMode.hideSingleEditorCursorMoves') && ( + if (shortcut.kind === ResultKind.KbFound && shortcut.commandId && !(options.showSingleEditorCursorMoves ?? true) && ( ['cursorLeft', 'cursorRight', 'cursorUp', 'cursorDown'].includes(shortcut.commandId)) ) { return; @@ -280,18 +288,17 @@ class ToggleScreencastModeAction extends Action2 { length = 0; } - const format = configurationService.getValue<'keys' | 'command' | 'commandWithGroup' | 'commandAndKeys' | 'commandWithGroupAndKeys'>('screencastMode.keyboardShortcutsFormat'); const keybinding = keybindingService.resolveKeyboardEvent(event); const command = (this._isKbFound(shortcut) && shortcut.commandId) ? MenuRegistry.getCommand(shortcut.commandId) : null; - let titleLabel = ''; + let commandAndGroupLabel = ''; let keyLabel: string | undefined | null = keybinding.getLabel(); if (command) { - titleLabel = typeof command.title === 'string' ? command.title : command.title.value; + commandAndGroupLabel = typeof command.title === 'string' ? command.title : command.title.value; - if ((format === 'commandWithGroup' || format === 'commandWithGroupAndKeys') && command.category) { - titleLabel = `${typeof command.category === 'string' ? command.category : command.category.value}: ${titleLabel} `; + if ((options.showCommandGroups ?? false) && command.category) { + commandAndGroupLabel = `${typeof command.category === 'string' ? command.category : command.category.value}: ${commandAndGroupLabel} `; } if (this._isKbFound(shortcut) && shortcut.commandId) { @@ -304,13 +311,11 @@ class ToggleScreencastModeAction extends Action2 { } } - const onlyKeyboardShortcuts = configurationService.getValue('screencastMode.onlyKeyboardShortcuts'); - - if (format !== 'keys' && titleLabel && !onlyKeyboardShortcuts) { - append(keyboardMarker, $('span.title', {}, `${titleLabel} `)); + if ((options.showCommands ?? true) && commandAndGroupLabel) { + append(keyboardMarker, $('span.title', {}, `${commandAndGroupLabel} `)); } - if (onlyKeyboardShortcuts || !titleLabel || (this._isKbFound(shortcut) && shortcut.commandId) && (format === 'keys' || format === 'commandAndKeys' || format === 'commandWithGroupAndKeys')) { + if (options.showKeys ?? true) { // Fix label for arrow keys keyLabel = keyLabel?.replace('UpArrow', '↑') ?.replace('DownArrow', '↓') @@ -421,27 +426,38 @@ configurationRegistry.registerConfiguration({ maximum: 100, description: localize('screencastMode.fontSize', "Controls the font size (in pixels) of the screencast mode keyboard.") }, - 'screencastMode.keyboardShortcutsFormat': { - enum: ['keys', 'command', 'commandWithGroup', 'commandAndKeys', 'commandWithGroupAndKeys'], - enumDescriptions: [ - localize('keyboardShortcutsFormat.keys', "Keys."), - localize('keyboardShortcutsFormat.command', "Command title."), - localize('keyboardShortcutsFormat.commandWithGroup', "Command title prefixed by its group."), - localize('keyboardShortcutsFormat.commandAndKeys', "Command title and keys."), - localize('keyboardShortcutsFormat.commandWithGroupAndKeys', "Command title and keys, with the command prefixed by its group.") - ], - description: localize('screencastMode.keyboardShortcutsFormat', "Controls what is displayed in the keyboard overlay when showing shortcuts."), - default: 'commandAndKeys' - }, - 'screencastMode.onlyKeyboardShortcuts': { - type: 'boolean', - description: localize('screencastMode.onlyKeyboardShortcuts', "Show only keyboard shortcuts in screencast mode (do not include action names)."), - default: false - }, - 'screencastMode.hideSingleEditorCursorMoves': { - type: 'boolean', - description: localize('screencastMode.hideSingleEditorCursorMoves', "Hide the single editor cursor move commands in screencast mode."), - default: false + 'screencastMode.keyboardOptions': { + type: 'object', + description: localize('screencastMode.keyboardOptions.description', "Options for customizing the keyboard overlay in screencast mode."), + properties: { + 'showKeys': { + type: 'boolean', + default: true, + description: localize('screencastMode.keyboardOptions.showKeys', "Show raw keys.") + }, + 'showCommands': { + type: 'boolean', + default: true, + description: localize('screencastMode.keyboardOptions.showCommands', "Show command names.") + }, + 'showCommandGroups': { + type: 'boolean', + default: false, + description: localize('screencastMode.keyboardOptions.showCommandGroups', "Show command group names, when commands are also shown.") + }, + 'showSingleEditorCursorMoves': { + type: 'boolean', + default: true, + description: localize('screencastMode.keyboardOptions.showSingleEditorCursorMoves', "Show single editor cursor move commands.") + } + }, + default: { + 'showKeys': true, + 'showCommands': true, + 'showCommandGroups': false, + 'showSingleEditorCursorMoves': true + }, + additionalProperties: false }, 'screencastMode.keyboardOverlayTimeout': { type: 'number', From ccea02b96dbc1351795de5ce66d8b52f10576fde Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Mon, 10 Jul 2023 15:16:02 +0200 Subject: [PATCH 270/926] Git - avoid opening a repository multiple times if symbolic links are used (#187435) --- extensions/git/src/model.ts | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/extensions/git/src/model.ts b/extensions/git/src/model.ts index e8a496822f0..cf22a284bbb 100644 --- a/extensions/git/src/model.ts +++ b/extensions/git/src/model.ts @@ -493,8 +493,9 @@ export class Model implements IBranchProtectionProviderRegistry, IRemoteSourcePu @sequentialize async openRepository(repoPath: string, openIfClosed = false): Promise { this.logger.trace(`Opening repository: ${repoPath}`); - if (this.getRepositoryExact(repoPath)) { - this.logger.trace(`Repository for path ${repoPath} already exists`); + const existingRepository = await this.getRepositoryExact(repoPath); + if (existingRepository) { + this.logger.trace(`Repository for path ${repoPath} already exists: ${existingRepository.root})`); return; } @@ -524,8 +525,9 @@ export class Model implements IBranchProtectionProviderRegistry, IRemoteSourcePu const { repositoryRoot, unsafeRepositoryMatch } = await this.getRepositoryRoot(repoPath); this.logger.trace(`Repository root for path ${repoPath} is: ${repositoryRoot}`); - if (this.getRepositoryExact(repositoryRoot)) { - this.logger.trace(`Repository for path ${repositoryRoot} already exists`); + const existingRepository = await this.getRepositoryExact(repositoryRoot); + if (existingRepository) { + this.logger.trace(`Repository for path ${repositoryRoot} already exists: ${existingRepository.root}`); return; } @@ -763,9 +765,12 @@ export class Model implements IBranchProtectionProviderRegistry, IRemoteSourcePu return liveRepository && liveRepository.repository; } - private getRepositoryExact(repoPath: string): Repository | undefined { - const openRepository = this.openRepositories - .find(r => pathEquals(r.repository.root, repoPath)); + private async getRepositoryExact(repoPath: string): Promise { + const repoPathCanonical = await fs.promises.realpath(repoPath, { encoding: 'utf8' }); + const openRepository = this.openRepositories.find(async r => { + const rootPathCanonical = await fs.promises.realpath(r.repository.root, { encoding: 'utf8' }); + return pathEquals(rootPathCanonical, repoPathCanonical); + }); return openRepository?.repository; } From 55613b5f13d009a2c0d32eb95b45b88024d4eecd Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Mon, 10 Jul 2023 15:31:45 +0200 Subject: [PATCH 271/926] adding some console logs, added todo --- .../browser/inlineChatController.ts | 50 ++++++++++++++++--- .../browser/inlineChatStrategies.ts | 5 +- 2 files changed, 46 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts index 7a9d3d2ee99..05cb6b8e8df 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -134,6 +134,7 @@ export class InlineChatController implements IEditorContribution { } this._log('session RESUMING', e); + console.log('before calling create session of the constructor'); await this._nextState(State.CREATE_SESSION, { existingSession }); this._log('session done or paused'); })); @@ -141,6 +142,7 @@ export class InlineChatController implements IEditorContribution { } dispose(): void { + console.log('inside of dispose'); this._stashedSession.clear(); this.finishExistingSession(); this._store.dispose(); @@ -174,10 +176,10 @@ export class InlineChatController implements IEditorContribution { } async run(options: InlineChatRunOptions | undefined = {}): Promise { - this._log('session starting'); + this._log('session starting inside of run'); await this.finishExistingSession(); this._stashedSession.clear(); - + console.log('before calling create session inside of run'); await this._nextState(State.CREATE_SESSION, options); this._log('session done or paused'); } @@ -214,6 +216,8 @@ export class InlineChatController implements IEditorContribution { } private async [State.CREATE_SESSION](options: InlineChatRunOptions): Promise { + console.log('inside of CREATE_SESSION'); + console.log('this._activeSession : ', this._activeSession); if (this._activeSession) { console.log('before clearing the session store'); this._sessionStore.clear(); @@ -235,6 +239,7 @@ export class InlineChatController implements IEditorContribution { if (!session) { const createSessionCts = new CancellationTokenSource(); const msgListener = Event.once(this._messages.event)(m => { + console.log('inside of the msgListener code of CREATE_SESSION'); this._log('state=_createSession) message received', m); if (m === Message.ACCEPT_INPUT) { // user accepted the input before having a session @@ -348,6 +353,7 @@ export class InlineChatController implements IEditorContribution { if (editIsOutsideOfWholeRange) { this._log('text changed outside of whole range, FINISH session'); + console.log('before the third finish existing session'); this.finishExistingSession(); } })); @@ -379,7 +385,7 @@ export class InlineChatController implements IEditorContribution { } - private async [State.WAIT_FOR_INPUT](options: InlineChatRunOptions): Promise { + private async [State.WAIT_FOR_INPUT](options: InlineChatRunOptions): Promise { assertType(this._activeSession); assertType(this._strategy); @@ -400,6 +406,7 @@ export class InlineChatController implements IEditorContribution { } else { const barrier = new Barrier(); const msgListener = Event.once(this._messages.event)(m => { + console.log('inside of msgListener of WAIT FOR INPUT'); this._log('state=_waitForInput) message received', m); message = m; barrier.open(); @@ -411,11 +418,19 @@ export class InlineChatController implements IEditorContribution { this._zone.value.widget.selectAll(); if (message & (Message.CANCEL_INPUT | Message.CANCEL_SESSION)) { - return State.CANCEL; + console.log('inside of wait for input'); + console.log('entered into the case when message cancel session'); + await this[State.CANCEL](); + return; + // return State.CANCEL; } if (message & Message.ACCEPT_SESSION) { - return State.ACCEPT; + console.log('inside of wait for input'); + console.log('entered into the case when message accept'); + await this[State.ACCEPT](); + return; + // return State.ACCEPT; } if (message & Message.PAUSE_SESSION) { @@ -467,6 +482,7 @@ export class InlineChatController implements IEditorContribution { let message = Message.NONE; const msgListener = Event.once(this._messages.event)(m => { + console.log('inside of msgListener of MAKE REQUEST'); this._log('state=_makeRequest) message received', m); message = m; requestCts.cancel(); @@ -521,10 +537,14 @@ export class InlineChatController implements IEditorContribution { this._activeSession.addExchange(new SessionExchange(this._activeSession.lastInput, response)); if (message & Message.CANCEL_SESSION) { + console.log('inside of make request'); + console.log('cancelling the session'); return State.CANCEL; } else if (message & Message.PAUSE_SESSION) { return State.PAUSE; } else if (message & Message.ACCEPT_SESSION) { + console.log('inside of make request'); + console.log('accepting'); return State.ACCEPT; } else { return State.APPLY_RESPONSE; @@ -665,21 +685,31 @@ export class InlineChatController implements IEditorContribution { assertType(this._activeSession); assertType(this._strategy); this._sessionStore.clear(); + console.log('after assert type'); try { + console.log('before strategy apply'); await this._strategy.apply(); + console.log('after strategy apply'); + // TODO: ASK WHY DESPITE AWAIT AFTER STRATEFY NOT PRINTED BEFORE CREATE SESSION } catch (err) { + console.log('when error obtained'); this._dialogService.error(localize('err.apply', "Failed to apply changes.", toErrorMessage(err))); this._log('FAILED to apply changes'); this._log(err); } - this._inlineChatSessionService.releaseSession(this._activeSession); + console.log('before release session'); - this[State.PAUSE](); + await this._inlineChatSessionService.releaseSession(this._activeSession); + + console.log('before state pause'); + await this[State.PAUSE](); } private async [State.CANCEL]() { + console.log('inside of cancel the session'); + assertType(this._activeSession); assertType(this._strategy); this._sessionStore.clear(); @@ -694,7 +724,7 @@ export class InlineChatController implements IEditorContribution { this._log(err); } - this[State.PAUSE](); + await this[State.PAUSE](); this._stashedSession.clear(); if (!mySession.isUnstashed && mySession.lastExchange) { @@ -780,10 +810,12 @@ export class InlineChatController implements IEditorContribution { } acceptSession(): void { + console.log('inside of acceptSession method'); this._messages.fire(Message.ACCEPT_SESSION); } cancelSession() { + console.log('inside of cancelSession'); let result: string | undefined; if (this._strategy && this._activeSession) { const changedText = this._activeSession.asChangedText(); @@ -798,6 +830,8 @@ export class InlineChatController implements IEditorContribution { } async finishExistingSession(): Promise { + console.log('inside of finish existing session'); + console.log(this._activeSession); if (this._activeSession) { if (this._activeSession.editMode === EditMode.Preview) { this._log('finishing existing session, using CANCEL', this._activeSession.editMode); diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts index 36e64ebf09a..019a4ecfd58 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts @@ -86,7 +86,7 @@ export class PreviewStrategy extends EditModeStrategy { } async apply() { - + console.log('at the beginning of the apply method of preview strategy'); if (!(this._session.lastExchange?.response instanceof EditResponse)) { return; } @@ -107,6 +107,7 @@ export class PreviewStrategy extends EditModeStrategy { modelN.pushStackElement(); } } + console.log('at the end of the apply method of preview strategy'); } async cancel(): Promise { @@ -279,6 +280,7 @@ export class LiveStrategy extends EditModeStrategy { } async apply() { + console.log('inside of apply of live strategy'); if (this._editCount > 0) { this._editor.pushUndoStop(); } @@ -286,6 +288,7 @@ export class LiveStrategy extends EditModeStrategy { await this._bulkEditService.apply(this._lastResponse.workspaceEdits); this._instaService.invokeFunction(showSingleCreateFile, this._lastResponse); } + console.log('at the end of apply for live strategy'); } async cancel() { From 6d1066875ea1fb9a1116419c5bb895d51846f4a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Mon, 10 Jul 2023 16:15:29 +0200 Subject: [PATCH 272/926] isolate artifacts from attempted runs (#187437) * isolate artifacts from attempted runs * set ARTIFACT_PREFIX to empty string first * cleaner --- .../alpine/product-build-alpine.yml | 12 ++++++---- .../darwin/product-build-darwin.yml | 14 +++++++---- .../linux/product-build-linux.yml | 23 ++++++++++--------- build/azure-pipelines/product-build.yml | 2 ++ .../azure-pipelines/web/product-build-web.yml | 6 ++++- .../win32/product-build-win32.yml | 18 +++++++++------ 6 files changed, 47 insertions(+), 28 deletions(-) diff --git a/build/azure-pipelines/alpine/product-build-alpine.yml b/build/azure-pipelines/alpine/product-build-alpine.yml index 704f4814ae9..eed3623addc 100644 --- a/build/azure-pipelines/alpine/product-build-alpine.yml +++ b/build/azure-pipelines/alpine/product-build-alpine.yml @@ -128,23 +128,27 @@ steps: GITHUB_TOKEN: "$(github-distro-mixin-password)" displayName: Build server (web) + - script: echo "##vso[task.setvariable variable=ARTIFACT_PREFIX]attempt$(System.JobAttempt)_" + condition: and(succeededOrFailed(), notIn(variables['Agent.JobStatus'], 'Succeeded', 'SucceededWithIssues')) + displayName: Generate artifact prefix + - publish: $(SERVER_PATH) - artifact: vscode_server_alpine_$(VSCODE_ARCH)_archive-unsigned + artifact: $(ARTIFACT_PREFIX)vscode_server_alpine_$(VSCODE_ARCH)_archive-unsigned displayName: Publish server archive condition: and(succeededOrFailed(), ne(variables['SERVER_PATH'], ''), ne(variables['VSCODE_ARCH'], 'x64')) - publish: $(WEB_PATH) - artifact: vscode_web_alpine_$(VSCODE_ARCH)_archive-unsigned + artifact: $(ARTIFACT_PREFIX)vscode_web_alpine_$(VSCODE_ARCH)_archive-unsigned displayName: Publish web server archive condition: and(succeededOrFailed(), ne(variables['WEB_PATH'], ''), ne(variables['VSCODE_ARCH'], 'x64')) # Legacy x64 artifact name - publish: $(SERVER_PATH) - artifact: vscode_server_linux_alpine_archive-unsigned + artifact: $(ARTIFACT_PREFIX)vscode_server_linux_alpine_archive-unsigned displayName: Publish x64 server archive condition: and(succeededOrFailed(), ne(variables['SERVER_PATH'], ''), eq(variables['VSCODE_ARCH'], 'x64')) - publish: $(WEB_PATH) - artifact: vscode_web_linux_alpine_archive-unsigned + artifact: $(ARTIFACT_PREFIX)vscode_web_linux_alpine_archive-unsigned displayName: Publish x64 web server archive condition: and(succeededOrFailed(), ne(variables['WEB_PATH'], ''), eq(variables['VSCODE_ARCH'], 'x64')) diff --git a/build/azure-pipelines/darwin/product-build-darwin.yml b/build/azure-pipelines/darwin/product-build-darwin.yml index 95d81431089..054a3caec37 100644 --- a/build/azure-pipelines/darwin/product-build-darwin.yml +++ b/build/azure-pipelines/darwin/product-build-darwin.yml @@ -202,6 +202,10 @@ steps: condition: and(succeededOrFailed(), eq(variables['BUILT_CLIENT'], 'true')) displayName: Package client + - script: echo "##vso[task.setvariable variable=ARTIFACT_PREFIX]attempt$(System.JobAttempt)_" + condition: and(succeededOrFailed(), notIn(variables['Agent.JobStatus'], 'Succeeded', 'SucceededWithIssues')) + displayName: Generate artifact prefix + - task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0 displayName: Generate SBOM (client) inputs: @@ -210,7 +214,7 @@ steps: - publish: $(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH)/_manifest displayName: Publish SBOM (client) - artifact: vscode_client_darwin_$(VSCODE_ARCH)_sbom + artifact: $(ARTIFACT_PREFIX)sbom_client_darwin_$(VSCODE_ARCH)_sbom - task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0 displayName: Generate SBOM (server) @@ -220,19 +224,19 @@ steps: - publish: $(agent.builddirectory)/vscode-server-darwin-$(VSCODE_ARCH)/_manifest displayName: Publish SBOM (server) - artifact: vscode_server_darwin_$(VSCODE_ARCH)_sbom + artifact: $(ARTIFACT_PREFIX)sbom_server_darwin_$(VSCODE_ARCH)_sbom - publish: $(CLIENT_PATH) - artifact: unsigned_vscode_client_darwin_$(VSCODE_ARCH)_archive + artifact: $(ARTIFACT_PREFIX)unsigned_vscode_client_darwin_$(VSCODE_ARCH)_archive condition: and(succeededOrFailed(), ne(variables['CLIENT_PATH'], '')) displayName: Publish client archive - publish: $(SERVER_PATH) - artifact: vscode_server_darwin_$(VSCODE_ARCH)_archive-unsigned + artifact: $(ARTIFACT_PREFIX)vscode_server_darwin_$(VSCODE_ARCH)_archive-unsigned condition: and(succeededOrFailed(), ne(variables['SERVER_PATH'], '')) displayName: Publish server archive - publish: $(WEB_PATH) - artifact: vscode_web_darwin_$(VSCODE_ARCH)_archive-unsigned + artifact: $(ARTIFACT_PREFIX)vscode_web_darwin_$(VSCODE_ARCH)_archive-unsigned condition: and(succeededOrFailed(), ne(variables['WEB_PATH'], '')) displayName: Publish web server archive diff --git a/build/azure-pipelines/linux/product-build-linux.yml b/build/azure-pipelines/linux/product-build-linux.yml index fa307a37628..87f4cd800da 100644 --- a/build/azure-pipelines/linux/product-build-linux.yml +++ b/build/azure-pipelines/linux/product-build-linux.yml @@ -235,7 +235,7 @@ steps: - ${{ if and(ne(parameters.VSCODE_CIBUILD, true), ne(parameters.VSCODE_QUALITY, 'oss')) }}: - task: DownloadPipelineArtifact@2 inputs: - artifact: vscode_cli_linux_$(VSCODE_ARCH)_cli + artifact: $(ARTIFACT_PREFIX)vscode_cli_linux_$(VSCODE_ARCH)_cli patterns: "**" path: $(Build.ArtifactStagingDirectory)/cli displayName: Download VS Code CLI @@ -280,8 +280,9 @@ steps: - script: node build/azure-pipelines/common/sign $(Agent.ToolsDirectory)/esrpclient/*/*/net6.0/esrpcli.dll rpm $(ESRP-PKI) $(esrp-aad-username) $(esrp-aad-password) .build/linux/rpm '*.rpm' displayName: Codesign rpm - # - script: ./build/azure-pipelines/linux/prepare-publish.sh - # displayName: Prepare for Publish + - script: echo "##vso[task.setvariable variable=ARTIFACT_PREFIX]attempt$(System.JobAttempt)_" + condition: and(succeededOrFailed(), notIn(variables['Agent.JobStatus'], 'Succeeded', 'SucceededWithIssues')) + displayName: Generate artifact prefix - task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0 displayName: Generate SBOM (client) @@ -291,7 +292,7 @@ steps: - publish: $(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH)/_manifest displayName: Publish SBOM (client) - artifact: vscode_client_linux_$(VSCODE_ARCH)_sbom + artifact: $(ARTIFACT_PREFIX)sbom_vscode_client_linux_$(VSCODE_ARCH) - task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0 displayName: Generate SBOM (server) @@ -301,35 +302,35 @@ steps: - publish: $(agent.builddirectory)/vscode-server-linux-$(VSCODE_ARCH)/_manifest displayName: Publish SBOM (server) - artifact: vscode_server_linux_$(VSCODE_ARCH)_sbom + artifact: $(ARTIFACT_PREFIX)sbom_vscode_server_linux_$(VSCODE_ARCH) - publish: $(CLIENT_PATH) - artifact: vscode_client_linux_$(VSCODE_ARCH)_archive-unsigned + artifact: $(ARTIFACT_PREFIX)vscode_client_linux_$(VSCODE_ARCH)_archive-unsigned condition: and(succeededOrFailed(), ne(variables['CLIENT_PATH'], '')) displayName: Publish client archive - publish: $(SERVER_PATH) - artifact: vscode_server_linux_$(VSCODE_ARCH)_archive-unsigned + artifact: $(ARTIFACT_PREFIX)vscode_server_linux_$(VSCODE_ARCH)_archive-unsigned condition: and(succeededOrFailed(), ne(variables['SERVER_PATH'], '')) displayName: Publish server archive - publish: $(WEB_PATH) - artifact: vscode_web_linux_$(VSCODE_ARCH)_archive-unsigned + artifact: $(ARTIFACT_PREFIX)vscode_web_linux_$(VSCODE_ARCH)_archive-unsigned condition: and(succeededOrFailed(), ne(variables['WEB_PATH'], '')) displayName: Publish web server archive - publish: $(DEB_PATH) - artifact: vscode_client_linux_$(VSCODE_ARCH)_deb-package + artifact: $(ARTIFACT_PREFIX)vscode_client_linux_$(VSCODE_ARCH)_deb-package condition: and(succeededOrFailed(), ne(variables['DEB_PATH'], '')) displayName: Publish deb package - publish: $(RPM_PATH) - artifact: vscode_client_linux_$(VSCODE_ARCH)_rpm-package + artifact: $(ARTIFACT_PREFIX)vscode_client_linux_$(VSCODE_ARCH)_rpm-package condition: and(succeededOrFailed(), ne(variables['RPM_PATH'], '')) displayName: Publish rpm package - publish: $(SNAP_PATH) - artifact: snap-$(VSCODE_ARCH) + artifact: $(ARTIFACT_PREFIX)snap-$(VSCODE_ARCH) condition: and(succeededOrFailed(), ne(variables['SNAP_PATH'], '')) displayName: Publish snap pre-package diff --git a/build/azure-pipelines/product-build.yml b/build/azure-pipelines/product-build.yml index d0a5f753431..7d809d6d11e 100644 --- a/build/azure-pipelines/product-build.yml +++ b/build/azure-pipelines/product-build.yml @@ -136,6 +136,8 @@ variables: value: true - name: Codeql.SkipTaskAutoInjection value: true + - name: ARTIFACT_PREFIX + value: '' name: "$(Date:yyyyMMdd).$(Rev:r) (${{ parameters.VSCODE_QUALITY }})" diff --git a/build/azure-pipelines/web/product-build-web.yml b/build/azure-pipelines/web/product-build-web.yml index 1b42ebaaaf9..a941ae9adfe 100644 --- a/build/azure-pipelines/web/product-build-web.yml +++ b/build/azure-pipelines/web/product-build-web.yml @@ -139,7 +139,11 @@ steps: node build/azure-pipelines/upload-nlsmetadata displayName: Upload NLS Metadata + - script: echo "##vso[task.setvariable variable=ARTIFACT_PREFIX]attempt$(System.JobAttempt)_" + condition: and(succeededOrFailed(), notIn(variables['Agent.JobStatus'], 'Succeeded', 'SucceededWithIssues')) + displayName: Generate artifact prefix + - publish: $(WEB_PATH) - artifact: vscode_web_linux_standalone_archive-unsigned + artifact: $(ARTIFACT_PREFIX)vscode_web_linux_standalone_archive-unsigned condition: and(succeededOrFailed(), ne(variables['WEB_PATH'], '')) displayName: Publish web archive diff --git a/build/azure-pipelines/win32/product-build-win32.yml b/build/azure-pipelines/win32/product-build-win32.yml index 7bca3e5ef8a..24957abbaa2 100644 --- a/build/azure-pipelines/win32/product-build-win32.yml +++ b/build/azure-pipelines/win32/product-build-win32.yml @@ -299,6 +299,10 @@ steps: echo "##vso[task.setvariable variable=USER_SETUP_PATH]$SetupPath" displayName: Build user setup + - powershell: echo "##vso[task.setvariable variable=ARTIFACT_PREFIX]attempt$(System.JobAttempt)_" + condition: and(succeededOrFailed(), notIn(variables['Agent.JobStatus'], 'Succeeded', 'SucceededWithIssues')) + displayName: Generate artifact prefix + - task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0 displayName: Generate SBOM (client) inputs: @@ -307,7 +311,7 @@ steps: - publish: $(agent.builddirectory)/VSCode-win32-$(VSCODE_ARCH)/_manifest displayName: Publish SBOM (client) - artifact: vscode_client_win32_$(VSCODE_ARCH)_sbom + artifact: $(ARTIFACT_PREFIX)sbom_client_win32_$(VSCODE_ARCH) - task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0 displayName: Generate SBOM (server) @@ -318,30 +322,30 @@ steps: - publish: $(agent.builddirectory)/vscode-server-win32-$(VSCODE_ARCH)/_manifest displayName: Publish SBOM (server) - artifact: vscode_server_win32_$(VSCODE_ARCH)_sbom + artifact: $(ARTIFACT_PREFIX)sbom_server_win32_$(VSCODE_ARCH) condition: and(succeeded(), ne(variables['VSCODE_ARCH'], 'arm64')) - publish: $(CLIENT_PATH) - artifact: vscode_client_win32_$(VSCODE_ARCH)_archive + artifact: $(ARTIFACT_PREFIX)vscode_client_win32_$(VSCODE_ARCH)_archive condition: and(succeededOrFailed(), ne(variables['CLIENT_PATH'], '')) displayName: Publish archive - publish: $(SERVER_PATH) - artifact: vscode_server_win32_$(VSCODE_ARCH)_archive + artifact: $(ARTIFACT_PREFIX)vscode_server_win32_$(VSCODE_ARCH)_archive condition: and(succeededOrFailed(), ne(variables['SERVER_PATH'], ''), ne(variables['VSCODE_ARCH'], 'arm64')) displayName: Publish server archive - publish: $(WEB_PATH) - artifact: vscode_web_win32_$(VSCODE_ARCH)_archive + artifact: $(ARTIFACT_PREFIX)vscode_web_win32_$(VSCODE_ARCH)_archive condition: and(succeededOrFailed(), ne(variables['WEB_PATH'], ''), ne(variables['VSCODE_ARCH'], 'arm64')) displayName: Publish web server archive - publish: $(SYSTEM_SETUP_PATH) - artifact: vscode_client_win32_$(VSCODE_ARCH)_setup + artifact: $(ARTIFACT_PREFIX)vscode_client_win32_$(VSCODE_ARCH)_setup condition: and(succeededOrFailed(), ne(variables['SYSTEM_SETUP_PATH'], '')) displayName: Publish system setup - publish: $(USER_SETUP_PATH) - artifact: vscode_client_win32_$(VSCODE_ARCH)_user-setup + artifact: $(ARTIFACT_PREFIX)vscode_client_win32_$(VSCODE_ARCH)_user-setup condition: and(succeededOrFailed(), ne(variables['USER_SETUP_PATH'], '')) displayName: Publish user setup From db16e86c983455a13469584702ab19f838565612 Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Mon, 10 Jul 2023 10:33:01 -0400 Subject: [PATCH 273/926] Restore sinon sandbox (#187476) Restore sinon stubs --- .../telemetry/test/browser/telemetryService.test.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/vs/platform/telemetry/test/browser/telemetryService.test.ts b/src/vs/platform/telemetry/test/browser/telemetryService.test.ts index 0c6711e8539..cbfd8554437 100644 --- a/src/vs/platform/telemetry/test/browser/telemetryService.test.ts +++ b/src/vs/platform/telemetry/test/browser/telemetryService.test.ts @@ -292,6 +292,7 @@ suite('TelemetryService', () => { errorTelemetry.dispose(); service.dispose(); + sinon.restore(); })); test('Error Telemetry removes PII from filename with spaces', sinonTestFn(function (this: any) { @@ -314,6 +315,7 @@ suite('TelemetryService', () => { errorTelemetry.dispose(); service.dispose(); + sinon.restore(); })); test('Uncaught Error Telemetry removes PII from filename', sinonTestFn(function (this: any) { @@ -342,6 +344,7 @@ suite('TelemetryService', () => { errorTelemetry.dispose(); service.dispose(); + sinon.restore(); })); test('Unexpected Error Telemetry removes PII', sinonTestFn(function (this: any) { @@ -398,6 +401,7 @@ suite('TelemetryService', () => { errorTelemetry.dispose(); service.dispose(); + sinon.restore(); })); test('Unexpected Error Telemetry removes PII but preserves Code file path', sinonTestFn(function (this: any) { @@ -465,6 +469,7 @@ suite('TelemetryService', () => { errorTelemetry.dispose(); service.dispose(); + sinon.restore(); })); test('Unexpected Error Telemetry removes PII but preserves Code file path with node modules', sinonTestFn(function (this: any) { @@ -559,6 +564,7 @@ suite('TelemetryService', () => { errorTelemetry.dispose(); service.dispose(); + sinon.restore(); })); test('Unexpected Error Telemetry removes PII but preserves Missing Model error message', sinonTestFn(function (this: any) { @@ -623,6 +629,7 @@ suite('TelemetryService', () => { errorTelemetry.dispose(); service.dispose(); + sinon.restore(); })); test('Unexpected Error Telemetry removes PII but preserves No Such File error message', sinonTestFn(function (this: any) { @@ -692,6 +699,7 @@ suite('TelemetryService', () => { errorTelemetry.dispose(); service.dispose(); + sinon.restore(); } finally { Errors.setUnexpectedErrorHandler(origErrorHandler); } From b0620485c20aea2911cd1ee79e74fb7b35c943e0 Mon Sep 17 00:00:00 2001 From: Zuo Zongyuan Date: Mon, 10 Jul 2023 22:58:32 +0800 Subject: [PATCH 274/926] Fix: Remote tunnel service running on VM with XGETBV disabled exit with SIGILL when being connected (#187345) fix: update cpufeatures from v0.2.5 to v0.2.8 (#187343) --- cli/Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/Cargo.lock b/cli/Cargo.lock index bed1af5cdd7..8e6e477991f 100644 --- a/cli/Cargo.lock +++ b/cli/Cargo.lock @@ -489,9 +489,9 @@ checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" [[package]] name = "cpufeatures" -version = "0.2.5" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +checksum = "03e69e28e9f7f77debdedbaafa2866e1de9ba56df55a8bd7cfc724c25a09987c" dependencies = [ "libc", ] From c61a08d4610712cd8c4e719a451cb1e4b6cd6e2a Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Mon, 10 Jul 2023 17:04:45 +0200 Subject: [PATCH 275/926] adding code in order be able to pass in a position for the inline chat --- src/vs/workbench/api/common/extHostInlineChat.ts | 6 +++++- .../inlineChat/browser/inlineChatActions.ts | 7 +++++-- .../inlineChat/browser/inlineChatController.ts | 15 ++++++++++----- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/api/common/extHostInlineChat.ts b/src/vs/workbench/api/common/extHostInlineChat.ts index 07eb77c0deb..1a5e5e9eba4 100644 --- a/src/vs/workbench/api/common/extHostInlineChat.ts +++ b/src/vs/workbench/api/common/extHostInlineChat.ts @@ -17,6 +17,7 @@ import * as extHostTypes from 'vs/workbench/api/common/extHostTypes'; import type * as vscode from 'vscode'; import { ApiCommand, ApiCommandArgument, ApiCommandResult, ExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; import { IRange } from 'vs/editor/common/core/range'; +import { IPosition } from 'vs/editor/common/core/position'; class ProviderWrapper { @@ -59,12 +60,14 @@ export class ExtHostInteractiveEditor implements ExtHostInlineChatShape { initialRange?: vscode.Range; message?: string; autoSend?: boolean; + position?: vscode.Position; }; type InteractiveEditorRunOptions = { initialRange?: IRange; message?: string; autoSend?: boolean; + position?: IPosition; }; extHostCommands.registerApiCommand(new ApiCommand( @@ -78,7 +81,8 @@ export class ExtHostInteractiveEditor implements ExtHostInlineChatShape { return { initialRange: v.initialRange ? typeConvert.Range.from(v.initialRange) : undefined, message: v.message, - autoSend: v.autoSend + autoSend: v.autoSend, + position: v.position ? typeConvert.Position.from(v.position) : undefined, }; })], ApiCommandResult.Void diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts index 83b481739a1..1f2d61380d8 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts @@ -29,6 +29,7 @@ import { CONTEXT_ACCESSIBILITY_MODE_ENABLED } from 'vs/platform/accessibility/co import { AccessibilityHelpAction } from 'vs/workbench/contrib/accessibility/browser/accessibilityContribution'; import { Disposable } from 'vs/base/common/lifecycle'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { Position } from 'vs/editor/common/core/position'; CommandsRegistry.registerCommandAlias('interactiveEditor.start', 'inlineChat.start'); @@ -50,11 +51,12 @@ export class StartSessionAction extends EditorAction2 { } private _isInteractivEditorOptions(options: any): options is InlineChatRunOptions { - const { initialRange, message, autoSend } = options; + const { initialRange, message, autoSend, position } = options; if ( typeof message !== 'undefined' && typeof message !== 'string' || typeof autoSend !== 'undefined' && typeof autoSend !== 'boolean' - || typeof initialRange !== 'undefined' && !Range.isIRange(initialRange)) { + || typeof initialRange !== 'undefined' && !Range.isIRange(initialRange) + || typeof position !== 'undefined' && typeof !Position.isIPosition(position)) { return false; } return true; @@ -66,6 +68,7 @@ export class StartSessionAction extends EditorAction2 { if (arg && this._isInteractivEditorOptions(arg)) { options = arg; } + console.log('options inside of run editor command : ', options); InlineChatController.get(editor)?.run(options); } } diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts index ba825ebb44a..c5f48e7963c 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -14,7 +14,7 @@ import { StopWatch } from 'vs/base/common/stopwatch'; import { assertType } from 'vs/base/common/types'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditOperation } from 'vs/editor/common/core/editOperation'; -import { Position } from 'vs/editor/common/core/position'; +import { IPosition, Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; import { IEditorContribution, ScrollType } from 'vs/editor/common/editorCommon'; import { ModelDecorationOptions, createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/textModel'; @@ -66,6 +66,7 @@ export interface InlineChatRunOptions { autoSend?: boolean; existingSession?: Session; isUnstashed?: boolean; + position?: IPosition; } export class InlineChatController implements IEditorContribution { @@ -184,12 +185,14 @@ export class InlineChatController implements IEditorContribution { // ---- state machine - private _showWidget(initialRender: boolean = false) { + // TODO: maybe should instead use the position inside of the line number? + // Makes the code more difficult but more coherent + private _showWidget(initialRender: boolean = false, position?: IPosition) { assertType(this._editor.hasModel()); let widgetPosition: Position; if (initialRender) { - widgetPosition = this._editor.getSelection().getEndPosition(); + widgetPosition = position ? Position.lift(position) : this._editor.getSelection().getEndPosition(); this._zone.value.setContainerMargins(); this._zone.value.setWidgetMargins(widgetPosition); } else { @@ -219,7 +222,8 @@ export class InlineChatController implements IEditorContribution { let session: Session | undefined = options.existingSession; - this._showWidget(true); + console.log('options : ', options); + this._showWidget(true, options.position); this._zone.value.widget.updateInfo(localize('welcome.1', "AI-generated code may be incorrect")); this._zone.value.widget.placeholder = this._getPlaceholderText(); @@ -307,7 +311,8 @@ export class InlineChatController implements IEditorContribution { } }); - this._showWidget(true); + console.log('options : ', options); + this._showWidget(true, options.position); this._sessionStore.add(this._editor.onDidChangeModel((e) => { const msg = this._activeSession?.lastExchange From ef4206956adc05e328ed48ff30fc326ae109bec1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Mon, 10 Jul 2023 17:12:32 +0200 Subject: [PATCH 276/926] always roll processed artifacts forward (#187465) --- build/azure-pipelines/product-publish.ps1 | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/build/azure-pipelines/product-publish.ps1 b/build/azure-pipelines/product-publish.ps1 index a9170d54f5c..fa4bf1aa31e 100644 --- a/build/azure-pipelines/product-publish.ps1 +++ b/build/azure-pipelines/product-publish.ps1 @@ -60,6 +60,7 @@ do { $artifacts | ForEach-Object { $artifactName = $_.name + if($set.Add($artifactName)) { Write-Host "Processing artifact: '$artifactName. Downloading from: $($_.resource.downloadUrl)" @@ -98,8 +99,11 @@ do { } | Format-Table exec { node build/azure-pipelines/common/createAsset.js $product $os $arch $type $asset.Name $asset.FullName } - $artifactName >> $ARTIFACT_PROCESSED_FILE_PATH } + + # Mark the artifact as processed. Make sure to keep the previously + # processed artifacts in the file as well, not just from this run. + $artifactName >> $ARTIFACT_PROCESSED_FILE_PATH } # Get the timeline and see if it says the other stage completed From ba4564b6335e2a6d02932b266d7ecc22dee7824e Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 10 Jul 2023 17:23:02 +0200 Subject: [PATCH 277/926] include error message when save fails (#187479) --- .../services/configuration/common/configurationEditing.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/services/configuration/common/configurationEditing.ts b/src/vs/workbench/services/configuration/common/configurationEditing.ts index 33919697053..699d388f340 100644 --- a/src/vs/workbench/services/configuration/common/configurationEditing.ts +++ b/src/vs/workbench/services/configuration/common/configurationEditing.ts @@ -210,7 +210,7 @@ export class ConfigurationEditing { if ((error).fileOperationResult === FileOperationResult.FILE_MODIFIED_SINCE) { throw this.toConfigurationEditingError(ConfigurationEditingErrorCode.ERROR_CONFIGURATION_FILE_MODIFIED_SINCE, operation.target, operation); } - throw this.toConfigurationEditingError(ConfigurationEditingErrorCode.ERROR_INTERNAL, operation.target, operation); + throw new ConfigurationEditingError(nls.localize('fsError', "Error while writing to {0}. {1}", this.stringifyTarget(operation.target), error.message), ConfigurationEditingErrorCode.ERROR_INTERNAL); } } From 8e1b0952af8c2902d4da0e87949f67e208377037 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Mon, 10 Jul 2023 17:25:54 +0200 Subject: [PATCH 278/926] cleaning the code --- .../workbench/contrib/inlineChat/browser/inlineChatActions.ts | 3 +-- .../contrib/inlineChat/browser/inlineChatController.ts | 4 ---- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts index 1f2d61380d8..b5cf33298e6 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts @@ -56,7 +56,7 @@ export class StartSessionAction extends EditorAction2 { typeof message !== 'undefined' && typeof message !== 'string' || typeof autoSend !== 'undefined' && typeof autoSend !== 'boolean' || typeof initialRange !== 'undefined' && !Range.isIRange(initialRange) - || typeof position !== 'undefined' && typeof !Position.isIPosition(position)) { + || typeof position !== 'undefined' && !Position.isIPosition(position)) { return false; } return true; @@ -68,7 +68,6 @@ export class StartSessionAction extends EditorAction2 { if (arg && this._isInteractivEditorOptions(arg)) { options = arg; } - console.log('options inside of run editor command : ', options); InlineChatController.get(editor)?.run(options); } } diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts index c5f48e7963c..d24cbbe0ab8 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -185,8 +185,6 @@ export class InlineChatController implements IEditorContribution { // ---- state machine - // TODO: maybe should instead use the position inside of the line number? - // Makes the code more difficult but more coherent private _showWidget(initialRender: boolean = false, position?: IPosition) { assertType(this._editor.hasModel()); @@ -222,7 +220,6 @@ export class InlineChatController implements IEditorContribution { let session: Session | undefined = options.existingSession; - console.log('options : ', options); this._showWidget(true, options.position); this._zone.value.widget.updateInfo(localize('welcome.1', "AI-generated code may be incorrect")); this._zone.value.widget.placeholder = this._getPlaceholderText(); @@ -311,7 +308,6 @@ export class InlineChatController implements IEditorContribution { } }); - console.log('options : ', options); this._showWidget(true, options.position); this._sessionStore.add(this._editor.onDidChangeModel((e) => { From 58969b44cd7196f8f596944280691d05300abed3 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 10 Jul 2023 09:28:26 -0700 Subject: [PATCH 279/926] increase time, rename --- .../contrib/tasks/browser/abstractTaskService.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index 0bb0287040a..5dc0f99ea57 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -223,7 +223,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer protected _outputChannel: IOutputChannel; protected readonly _onDidStateChange: Emitter; - private _waitForSupportedExecutions: Promise; + private _waitForOneSupportedExecution: Promise; private _waitForAllSupportedExecutions: Promise; private _onDidRegisterSupportedExecutions: Emitter = new Emitter(); private _onDidRegisterAllSupportedExecutions: Emitter = new Emitter(); @@ -335,7 +335,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer this._setPersistentTask(e.__task); } })); - this._waitForSupportedExecutions = new Promise(resolve => { + this._waitForOneSupportedExecution = new Promise(resolve => { once(this._onDidRegisterSupportedExecutions.event)(() => resolve()); }); this._waitForAllSupportedExecutions = new Promise(resolve => { @@ -2209,10 +2209,10 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer if (!(await this._trust())) { return new Map(); } - await this._waitForSupportedExecutions; + await this._waitForOneSupportedExecution; if (runSource === TaskRunSource.Reconnect) { - await raceTimeout(this._waitForAllSupportedExecutions, 500, () => { - console.warn('Timed out waiting for all supported executions for task reconnection'); + await raceTimeout(this._waitForAllSupportedExecutions, 2000, () => { + this._logService.warn('Timed out waiting for all supported executions for task reconnection'); }); } await this._whenTaskSystemReady; @@ -2891,7 +2891,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer /** * * @param tasks - The tasks which need to be filtered - * @param taskGlobsInList - This tells splitPerGroupType to filter out globbed tasks (into defaults) + * @param tasksInList - This tells splitPerGroupType to filter out globbed tasks (into defaults) * @returns */ private _getDefaultTasks(tasks: Task[], taskGlobsInList: boolean = false): Task[] { From 44d1d6a39c8d88c481ce48c83da198c5c41048d3 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Mon, 10 Jul 2023 10:19:55 -0700 Subject: [PATCH 280/926] build: check for cargo.lock changes in pr too (#187481) * build: check for cargo.lock changes in pr too * keep old name --- .github/workflows/no-yarn-lock-changes.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/no-yarn-lock-changes.yml b/.github/workflows/no-yarn-lock-changes.yml index ac32bc367dc..57082a28b1c 100644 --- a/.github/workflows/no-yarn-lock-changes.yml +++ b/.github/workflows/no-yarn-lock-changes.yml @@ -24,8 +24,8 @@ jobs: - name: Get file changes uses: trilom/file-changes-action@ce38c8ce2459ca3c303415eec8cb0409857b4272 if: ${{ steps.control.outputs.should_run == 'true' }} - - name: Check for yarn.lock changes + - name: Check for lockfile changes if: ${{ steps.control.outputs.should_run == 'true' }} run: | - cat $HOME/files.json | jq -e 'any(test("yarn\\.lock$")) | not' \ - || (echo "Changes to yarn.lock files aren't allowed in PRs." && exit 1) + cat $HOME/files.json | jq -e 'any(test("yarn\\.lock$|Cargo\\.lock$")) | not' \ + || (echo "Changes to yarn.lock/Cargo.lock files aren't allowed in PRs." && exit 1) From c6033eadec1aa29c958ab108ccd4fd62613ae1b7 Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Mon, 10 Jul 2023 10:35:24 -0700 Subject: [PATCH 281/926] Restore previous behavior of `onDidChangeSecret` (#187333) ref https://github.com/microsoft/vscode/issues/187247 --- src/vs/platform/secrets/common/secrets.ts | 27 +++++------------------ 1 file changed, 6 insertions(+), 21 deletions(-) diff --git a/src/vs/platform/secrets/common/secrets.ts b/src/vs/platform/secrets/common/secrets.ts index 47ed701bbf8..6c6b822ebc2 100644 --- a/src/vs/platform/secrets/common/secrets.ts +++ b/src/vs/platform/secrets/common/secrets.ts @@ -7,7 +7,7 @@ import { SequencerByKey } from 'vs/base/common/async'; import { IEncryptionService } from 'vs/platform/encryption/common/encryptionService'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IStorageService, InMemoryStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; -import { Event, PauseableEmitter } from 'vs/base/common/event'; +import { Emitter, Event } from 'vs/base/common/event'; import { ILogService } from 'vs/platform/log/common/log'; import { IDisposable } from 'vs/base/common/lifecycle'; @@ -30,7 +30,7 @@ export abstract class BaseSecretStorageService implements ISecretStorageService private _storagePrefix = 'secret://'; - private readonly _onDidChangeSecret = new PauseableEmitter(); + private readonly _onDidChangeSecret = new Emitter(); onDidChangeSecret: Event = this._onDidChangeSecret.event; protected readonly _sequencer = new SequencerByKey(); @@ -55,11 +55,6 @@ export abstract class BaseSecretStorageService implements ISecretStorageService return; } - if (this._onDidChangeSecret.isPaused) { - this._logService.trace(`[SecretStorageService] Skipping change event for secret: ${key} because it is paused`); - return; - } - const secretKey = key.slice(this._storagePrefix.length); this._logService.trace(`[SecretStorageService] Notifying change in value for secret: ${secretKey}`); @@ -104,13 +99,8 @@ export abstract class BaseSecretStorageService implements ISecretStorageService throw e; } const fullKey = this.getKey(key); - try { - this._onDidChangeSecret.pause(); - this._logService.trace('[secrets] storing encrypted secret for key:', fullKey); - storageService.store(fullKey, encrypted, StorageScope.APPLICATION, StorageTarget.MACHINE); - } finally { - this._onDidChangeSecret.resume(); - } + this._logService.trace('[secrets] storing encrypted secret for key:', fullKey); + storageService.store(fullKey, encrypted, StorageScope.APPLICATION, StorageTarget.MACHINE); this._logService.trace('[secrets] stored encrypted secret for key:', fullKey); }); } @@ -120,13 +110,8 @@ export abstract class BaseSecretStorageService implements ISecretStorageService const storageService = await this.resolvedStorageService; const fullKey = this.getKey(key); - try { - this._onDidChangeSecret.pause(); - this._logService.trace('[secrets] deleting secret for key:', fullKey); - storageService.remove(fullKey, StorageScope.APPLICATION); - } finally { - this._onDidChangeSecret.resume(); - } + this._logService.trace('[secrets] deleting secret for key:', fullKey); + storageService.remove(fullKey, StorageScope.APPLICATION); this._logService.trace('[secrets] deleted secret for key:', fullKey); }); } From de6f24cf773acd9785f7535b8d2a53b8d7c87eb2 Mon Sep 17 00:00:00 2001 From: FateLee Date: Tue, 11 Jul 2023 01:36:14 +0800 Subject: [PATCH 282/926] fix: typos (#179840) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 李京廷 --- src/vs/editor/browser/widget/codeEditorWidget.ts | 2 +- src/vs/editor/common/core/cursorColumns.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index e78be4bd6b7..e90726fe00c 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -1308,7 +1308,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE for (const decorationOption of decorationOptions) { let typeKey = decorationTypeKey; if (decorationOption.renderOptions) { - // identify custom reder options by a hash code over all keys and values + // identify custom render options by a hash code over all keys and values // For custom render options register a decoration type if necessary const subType = hash(decorationOption.renderOptions).toString(16); // The fact that `decorationTypeKey` appears in the typeKey has no influence diff --git a/src/vs/editor/common/core/cursorColumns.ts b/src/vs/editor/common/core/cursorColumns.ts index 6f8cd6fbc72..669c38b43f5 100644 --- a/src/vs/editor/common/core/cursorColumns.ts +++ b/src/vs/editor/common/core/cursorColumns.ts @@ -116,7 +116,7 @@ export class CursorColumns { } /** - * ATTENTION: This works with 0-based columns (as oposed to the regular 1-based columns) + * ATTENTION: This works with 0-based columns (as opposed to the regular 1-based columns) * @see {@link CursorColumns} */ public static nextRenderTabStop(visibleColumn: number, tabSize: number): number { @@ -124,7 +124,7 @@ export class CursorColumns { } /** - * ATTENTION: This works with 0-based columns (as oposed to the regular 1-based columns) + * ATTENTION: This works with 0-based columns (as opposed to the regular 1-based columns) * @see {@link CursorColumns} */ public static nextIndentTabStop(visibleColumn: number, indentSize: number): number { From 7dbc6ecb931127cd25513edf58bb29ed25fe8155 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Mon, 10 Jul 2023 11:11:13 -0700 Subject: [PATCH 283/926] Fix #187447. Avoid npe for reading notebook pane (#187491) --- src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index a58d4ebd9b0..2959ca1b13a 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -806,7 +806,7 @@ export function getNotebookEditorFromEditorPane(editorPane?: IEditorPane): INote const input = editorPane.input; if (input && isCompositeNotebookEditorInput(input)) { - return (editorPane.getControl() as { notebookEditor: INotebookEditor | undefined }).notebookEditor; + return (editorPane.getControl() as { notebookEditor: INotebookEditor | undefined } | undefined)?.notebookEditor; } return undefined; From babfe39bb73757136eeaa9c0ef1be5490124ee35 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 10 Jul 2023 11:21:15 -0700 Subject: [PATCH 284/926] Don't call fireSimulatedContinuedEvent if the debug adapter sent a Stopped event before the response of the next/stepIn/etc request (#187215) * Don't call fireSimulatedContinuedEvent if the debug adapter sent a Stopped event before the response of the next/stepIn/etc request Fix #185785 * Fix typo --- .../contrib/debug/browser/rawDebugSession.ts | 42 +++++++++++++++---- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts b/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts index db8cd73120e..c7201795adf 100644 --- a/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts @@ -76,6 +76,7 @@ export class RawDebugSession implements IDisposable { // DA events private readonly _onDidExitAdapter = new Emitter(); private debugAdapter: IDebugAdapter | null; + private stoppedSinceLastStep = false; private toDispose: IDisposable[] = []; @@ -122,6 +123,7 @@ export class RawDebugSession implements IDisposable { break; case 'stopped': this.didReceiveStoppedEvent = true; // telemetry: remember that debugger stopped successfully + this.stoppedSinceLastStep = true; this._onDidStop.fire(event); break; case 'continued': @@ -326,29 +328,41 @@ export class RawDebugSession implements IDisposable { } async next(args: DebugProtocol.NextArguments): Promise { + this.stoppedSinceLastStep = false; const response = await this.send('next', args); - this.fireSimulatedContinuedEvent(args.threadId); + if (!this.stoppedSinceLastStep) { + this.fireSimulatedContinuedEvent(args.threadId); + } return response; } async stepIn(args: DebugProtocol.StepInArguments): Promise { + this.stoppedSinceLastStep = false; const response = await this.send('stepIn', args); - this.fireSimulatedContinuedEvent(args.threadId); + if (!this.stoppedSinceLastStep) { + this.fireSimulatedContinuedEvent(args.threadId); + } return response; } async stepOut(args: DebugProtocol.StepOutArguments): Promise { + this.stoppedSinceLastStep = false; const response = await this.send('stepOut', args); - this.fireSimulatedContinuedEvent(args.threadId); + if (!this.stoppedSinceLastStep) { + this.fireSimulatedContinuedEvent(args.threadId); + } return response; } async continue(args: DebugProtocol.ContinueArguments): Promise { + this.stoppedSinceLastStep = false; const response = await this.send('continue', args); if (response && response.body && response.body.allThreadsContinued !== undefined) { this.allThreadsContinued = response.body.allThreadsContinued; } - this.fireSimulatedContinuedEvent(args.threadId, this.allThreadsContinued); + if (!this.stoppedSinceLastStep) { + this.fireSimulatedContinuedEvent(args.threadId, this.allThreadsContinued); + } return response; } @@ -380,8 +394,11 @@ export class RawDebugSession implements IDisposable { async restartFrame(args: DebugProtocol.RestartFrameArguments, threadId: number): Promise { if (this.capabilities.supportsRestartFrame) { + this.stoppedSinceLastStep = false; const response = await this.send('restartFrame', args); - this.fireSimulatedContinuedEvent(threadId); + if (!this.stoppedSinceLastStep) { + this.fireSimulatedContinuedEvent(threadId); + } return response; } return Promise.reject(new Error('restartFrame not supported')); @@ -484,8 +501,11 @@ export class RawDebugSession implements IDisposable { async stepBack(args: DebugProtocol.StepBackArguments): Promise { if (this.capabilities.supportsStepBack) { + this.stoppedSinceLastStep = false; const response = await this.send('stepBack', args); - this.fireSimulatedContinuedEvent(args.threadId); + if (!this.stoppedSinceLastStep) { + this.fireSimulatedContinuedEvent(args.threadId); + } return response; } return Promise.reject(new Error('stepBack not supported')); @@ -493,8 +513,11 @@ export class RawDebugSession implements IDisposable { async reverseContinue(args: DebugProtocol.ReverseContinueArguments): Promise { if (this.capabilities.supportsStepBack) { + this.stoppedSinceLastStep = false; const response = await this.send('reverseContinue', args); - this.fireSimulatedContinuedEvent(args.threadId); + if (!this.stoppedSinceLastStep) { + this.fireSimulatedContinuedEvent(args.threadId); + } return response; } return Promise.reject(new Error('reverseContinue not supported')); @@ -509,8 +532,11 @@ export class RawDebugSession implements IDisposable { async goto(args: DebugProtocol.GotoArguments): Promise { if (this.capabilities.supportsGotoTargetsRequest) { + this.stoppedSinceLastStep = false; const response = await this.send('goto', args); - this.fireSimulatedContinuedEvent(args.threadId); + if (!this.stoppedSinceLastStep) { + this.fireSimulatedContinuedEvent(args.threadId); + } return response; } From 5822d97cb362ff4d219bbb6231d6b0132fe31732 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 10 Jul 2023 11:37:19 -0700 Subject: [PATCH 285/926] Bump milestones in issue notebooks (#187494) --- .vscode/notebooks/api.github-issues | 2 +- .vscode/notebooks/endgame.github-issues | 2 +- .vscode/notebooks/my-endgame.github-issues | 2 +- .vscode/notebooks/verification.github-issues | 2 +- .vscode/notebooks/vscode-dev.github-issues | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.vscode/notebooks/api.github-issues b/.vscode/notebooks/api.github-issues index 682cd02911b..1d39b63dd9f 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:\"June 2023\"" + "value": "$repo=repo:microsoft/vscode\n$milestone=milestone:\"July 2023\"" }, { "kind": 1, diff --git a/.vscode/notebooks/endgame.github-issues b/.vscode/notebooks/endgame.github-issues index e0a1b766d76..60c8c5a6aec 100644 --- a/.vscode/notebooks/endgame.github-issues +++ b/.vscode/notebooks/endgame.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-unpkg repo:microsoft/vscode-references-view repo:microsoft/vscode-anycode repo:microsoft/vscode-hexeditor repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-livepreview repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remote-repositories-github repo:microsoft/monaco-editor repo:microsoft/vscode-vsce repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-livepreview repo:microsoft/vscode-livepreview repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-l10n repo:microsoft/vscode-remote-tunnels\n\n$MILESTONE=milestone:\"June 2023\"" + "value": "$REPOS=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-unpkg repo:microsoft/vscode-references-view repo:microsoft/vscode-anycode repo:microsoft/vscode-hexeditor repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-livepreview repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remote-repositories-github repo:microsoft/monaco-editor repo:microsoft/vscode-vsce repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-livepreview repo:microsoft/vscode-livepreview repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-l10n repo:microsoft/vscode-remote-tunnels\n\n$MILESTONE=milestone:\"July 2023\"" }, { "kind": 1, diff --git a/.vscode/notebooks/my-endgame.github-issues b/.vscode/notebooks/my-endgame.github-issues index 74316c9367e..92fddeef220 100644 --- a/.vscode/notebooks/my-endgame.github-issues +++ b/.vscode/notebooks/my-endgame.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-unpkg repo:microsoft/vscode-references-view repo:microsoft/vscode-anycode repo:microsoft/vscode-hexeditor repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-livepreview repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remote-repositories-github repo:microsoft/monaco-editor repo:microsoft/vscode-vsce repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-livepreview repo:microsoft/vscode-livepreview repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-l10n repo:microsoft/vscode-remote-tunnels\n\n$MILESTONE=milestone:\"June 2023\"\n\n$MINE=assignee:@me" + "value": "$REPOS=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-unpkg repo:microsoft/vscode-references-view repo:microsoft/vscode-anycode repo:microsoft/vscode-hexeditor repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-livepreview repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remote-repositories-github repo:microsoft/monaco-editor repo:microsoft/vscode-vsce repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-livepreview repo:microsoft/vscode-livepreview repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-l10n repo:microsoft/vscode-remote-tunnels\n\n$MILESTONE=milestone:\"July 2023\"\n\n$MINE=assignee:@me" }, { "kind": 1, diff --git a/.vscode/notebooks/verification.github-issues b/.vscode/notebooks/verification.github-issues index f3c54eab361..43ff3912c72 100644 --- a/.vscode/notebooks/verification.github-issues +++ b/.vscode/notebooks/verification.github-issues @@ -12,7 +12,7 @@ { "kind": 2, "language": "github-issues", - "value": "$repos=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-unpkg repo:microsoft/vscode-references-view repo:microsoft/vscode-anycode repo:microsoft/vscode-hexeditor repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-livepreview repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remote-repositories-github repo:microsoft/monaco-editor repo:microsoft/vscode-vsce repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-livepreview repo:microsoft/vscode-livepreview repo:microsoft/vscode-python repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-l10n repo:microsoft/vscode-remote-tunnels\n$milestone=milestone:\"June 2023\"" + "value": "$repos=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-unpkg repo:microsoft/vscode-references-view repo:microsoft/vscode-anycode repo:microsoft/vscode-hexeditor repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-livepreview repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remote-repositories-github repo:microsoft/monaco-editor repo:microsoft/vscode-vsce repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-livepreview repo:microsoft/vscode-livepreview repo:microsoft/vscode-python repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-l10n repo:microsoft/vscode-remote-tunnels\n$milestone=milestone:\"July 2023\"" }, { "kind": 1, diff --git a/.vscode/notebooks/vscode-dev.github-issues b/.vscode/notebooks/vscode-dev.github-issues index ffcd620be52..65cfed180a5 100644 --- a/.vscode/notebooks/vscode-dev.github-issues +++ b/.vscode/notebooks/vscode-dev.github-issues @@ -2,7 +2,7 @@ { "kind": 2, "language": "github-issues", - "value": "$milestone=milestone:\"June 2023\"" + "value": "$milestone=milestone:\"July 2023\"" }, { "kind": 1, From 84fc1ff4b3894aa9304b11632136c486a667dadd Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Mon, 10 Jul 2023 11:40:55 -0700 Subject: [PATCH 286/926] testing: fix memory leak during unit tests (#187488) For #187471 --- src/vs/workbench/api/test/browser/extHostTesting.test.ts | 1 + src/vs/workbench/contrib/debug/test/browser/callStack.test.ts | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/src/vs/workbench/api/test/browser/extHostTesting.test.ts b/src/vs/workbench/api/test/browser/extHostTesting.test.ts index f4e445ae444..92970a9a9cd 100644 --- a/src/vs/workbench/api/test/browser/extHostTesting.test.ts +++ b/src/vs/workbench/api/test/browser/extHostTesting.test.ts @@ -93,6 +93,7 @@ suite('ExtHost Testing', () => { teardown(() => { single.dispose(); + sinon.restore(); }); suite('OwnedTestCollection', () => { diff --git a/src/vs/workbench/contrib/debug/test/browser/callStack.test.ts b/src/vs/workbench/contrib/debug/test/browser/callStack.test.ts index 0cb9462b00c..c6158426173 100644 --- a/src/vs/workbench/contrib/debug/test/browser/callStack.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/callStack.test.ts @@ -76,6 +76,10 @@ suite('Debug - CallStack', () => { mockRawSession = new MockRawSession(); }); + teardown(() => { + sinon.restore(); + }); + // Threads test('threads simple', () => { From e5782fe70ae548fc9d589ea83bb7189b03263cf3 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 10 Jul 2023 12:31:39 -0700 Subject: [PATCH 287/926] fix #185960 --- .../tasks/browser/abstractTaskService.ts | 31 +++++++++++++------ 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index 12fc22bd477..e8ec458751d 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -2936,22 +2936,33 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer }); }); }; - + let globTasksDetected = false; // First check for globs before checking for the default tasks of the task group const absoluteURI = EditorResourceAccessor.getOriginalUri(this._editorService.activeEditor); if (absoluteURI) { const workspaceFolder = this._contextService.getWorkspaceFolder(absoluteURI); - // fallback to absolute path of the file if it is not in a workspace or relative path cannot be found - const relativePath = workspaceFolder?.uri ? (resources.relativePath(workspaceFolder.uri, absoluteURI) ?? absoluteURI.path) : absoluteURI.path; + if (workspaceFolder) { + const config = this._getConfiguration(workspaceFolder); + globTasksDetected = ((config?.config?.tasks) || []).filter(task => task.group && typeof task.group !== 'string' && typeof task.group.isDefault === 'string').length > 0; - taskGroupTasks = await this._findWorkspaceTasks((task) => { - const currentTaskGroup = task.configurationProperties.group; - if (currentTaskGroup && typeof currentTaskGroup !== 'string' && typeof currentTaskGroup.isDefault === 'string') { - return (currentTaskGroup._id === taskGroup._id && glob.match(currentTaskGroup.isDefault, relativePath)); + if (globTasksDetected) { + // fallback to absolute path of the file if it is not in a workspace or relative path cannot be found + const relativePath = workspaceFolder?.uri ? (resources.relativePath(workspaceFolder.uri, absoluteURI) ?? absoluteURI.path) : absoluteURI.path; + + taskGroupTasks = await this._findWorkspaceTasks((task) => { + const currentTaskGroup = task.configurationProperties.group; + if (currentTaskGroup && typeof currentTaskGroup !== 'string' && typeof currentTaskGroup.isDefault === 'string') { + return (currentTaskGroup._id === taskGroup._id && glob.match(currentTaskGroup.isDefault, relativePath)); + } + + return false; + }); } + } + } - return false; - }); + if (!globTasksDetected && taskGroupTasks.length === 0) { + taskGroupTasks = await this._findWorkspaceTasksInGroup(TaskGroup.Build, true); } const handleMultipleTasks = (areGlobTasks: boolean) => { @@ -2991,7 +3002,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer // If there's multiple globs that match we want to show the quick picker for those tasks // We will need to call splitPerGroupType putting globs in defaults and the remaining tasks in none. // We don't need to carry on after here - if (taskGroupTasks.length > 1) { + if (globTasksDetected && taskGroupTasks.length > 1) { return handleMultipleTasks(true); } From 199fd02fc1605ff46a0184d814caa827159e7633 Mon Sep 17 00:00:00 2001 From: Andrea Mah <31675041+andreamah@users.noreply.github.com> Date: Mon, 10 Jul 2023 12:55:02 -0700 Subject: [PATCH 288/926] add roaming changes to search history (#187330) --- src/vs/base/browser/ui/inputbox/inputBox.ts | 13 ++++++ .../search/browser/patternInputWidget.ts | 4 ++ .../contrib/search/browser/searchView.ts | 40 ++++++++++++++++--- .../contrib/search/browser/searchWidget.ts | 8 ++++ .../search/common/searchHistoryService.ts | 4 +- 5 files changed, 61 insertions(+), 8 deletions(-) diff --git a/src/vs/base/browser/ui/inputbox/inputBox.ts b/src/vs/base/browser/ui/inputbox/inputBox.ts index 1dcc1751ff8..af621b9047e 100644 --- a/src/vs/base/browser/ui/inputbox/inputBox.ts +++ b/src/vs/base/browser/ui/inputbox/inputBox.ts @@ -685,6 +685,19 @@ export class HistoryInputBox extends InputBox implements IHistoryNavigationWidge } } + public prependHistory(restoredHistory: string[]): void { + const newHistory = this.getHistory(); + this.clearHistory(); + + restoredHistory.forEach((item) => { + this.history.add(item); + }); + + newHistory.forEach(item => { + this.history.add(item); + }); + } + public getHistory(): string[] { return this.history.getHistory(); } diff --git a/src/vs/workbench/contrib/search/browser/patternInputWidget.ts b/src/vs/workbench/contrib/search/browser/patternInputWidget.ts index 92de68be38d..6d167046d37 100644 --- a/src/vs/workbench/contrib/search/browser/patternInputWidget.ts +++ b/src/vs/workbench/contrib/search/browser/patternInputWidget.ts @@ -116,6 +116,10 @@ export class PatternInputWidget extends Widget { this.inputBox.clearHistory(); } + prependHistory(history: string[]): void { + this.inputBox.prependHistory(history); + } + clear(): void { this.setValue(''); } diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index eeaed2cc943..44d8993bf19 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -70,7 +70,7 @@ import { SearchWidget } from 'vs/workbench/contrib/search/browser/searchWidget'; import * as Constants from 'vs/workbench/contrib/search/common/constants'; import { IReplaceService } from 'vs/workbench/contrib/search/browser/replace'; import { getOutOfWorkspaceEditorResources, SearchStateKey, SearchUIState } from 'vs/workbench/contrib/search/common/search'; -import { ISearchHistoryService, ISearchHistoryValues } from 'vs/workbench/contrib/search/common/searchHistoryService'; +import { ISearchHistoryService, ISearchHistoryValues, SearchHistoryService } from 'vs/workbench/contrib/search/common/searchHistoryService'; import { FileMatch, FileMatchOrMatch, FolderMatch, FolderMatchWithResource, IChangeEvent, ISearchWorkbenchService, Match, MatchInNotebook, RenderableMatch, searchMatchComparer, SearchModel, SearchResult } from 'vs/workbench/contrib/search/browser/searchModel'; import { createEditorFromSearchResult } from 'vs/workbench/contrib/searchEditor/browser/searchEditorActions'; import { ACTIVE_GROUP, IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; @@ -185,7 +185,7 @@ export class SearchView extends ViewPane { @IContextMenuService contextMenuService: IContextMenuService, @IAccessibilityService private readonly accessibilityService: IAccessibilityService, @IKeybindingService keybindingService: IKeybindingService, - @IStorageService storageService: IStorageService, + @IStorageService private readonly storageService: IStorageService, @IOpenerService openerService: IOpenerService, @ITelemetryService telemetryService: ITelemetryService, @INotebookService private readonly notebookService: INotebookService, @@ -257,6 +257,30 @@ export class SearchView extends ViewPane { this.isTreeLayoutViewVisible = this.viewletState['view.treeLayout'] ?? (this.searchConfig.defaultViewMode === ViewMode.Tree); this._refreshResultsScheduler = this._register(new RunOnceScheduler(this._updateResults.bind(this), 80)); + + // storage service listener for for roaming changes + this._register(this.storageService.onWillSaveState(() => { + this._saveSearchHistoryService(); + })); + + this._register(this.storageService.onDidChangeValue((v) => { + if (v.key === SearchHistoryService.SEARCH_HISTORY_KEY) { + const restoredHistory = this.searchHistoryService.load(); + + if (restoredHistory.include) { + this.inputPatternIncludes.prependHistory(restoredHistory.include); + } + if (restoredHistory.exclude) { + this.inputPatternExcludes.prependHistory(restoredHistory.exclude); + } + if (restoredHistory.search) { + this.searchWidget.prependSearchHistory(restoredHistory.search); + } + if (restoredHistory.replace) { + this.searchWidget.prependReplaceHistory(restoredHistory.replace); + } + } + })); } get isTreeLayoutViewVisible(): boolean { @@ -2038,6 +2062,14 @@ export class SearchView extends ViewPane { this.viewletState['view.treeLayout'] = this.isTreeLayoutViewVisible; this.viewletState['query.replaceText'] = isReplaceShown && this.searchWidget.getReplaceValue(); + this._saveSearchHistoryService(); + + this.memento.saveMemento(); + + super.saveState(); + } + + private _saveSearchHistoryService() { const history: ISearchHistoryValues = Object.create(null); const searchHistory = this.searchWidget.getSearchHistory(); @@ -2061,10 +2093,6 @@ export class SearchView extends ViewPane { } this.searchHistoryService.save(history); - - this.memento.saveMemento(); - - super.saveState(); } private async retrieveFileStats(): Promise { diff --git a/src/vs/workbench/contrib/search/browser/searchWidget.ts b/src/vs/workbench/contrib/search/browser/searchWidget.ts index ea705b2a2f8..6062d87937d 100644 --- a/src/vs/workbench/contrib/search/browser/searchWidget.ts +++ b/src/vs/workbench/contrib/search/browser/searchWidget.ts @@ -300,6 +300,14 @@ export class SearchWidget extends Widget { return this.replaceInput?.inputBox.getHistory() ?? []; } + prependSearchHistory(history: string[]): void { + this.searchInput?.inputBox.prependHistory(history); + } + + prependReplaceHistory(history: string[]): void { + this.replaceInput?.inputBox.prependHistory(history); + } + clearHistory(): void { this.searchInput?.inputBox.clearHistory(); this.replaceInput?.inputBox.clearHistory(); diff --git a/src/vs/workbench/contrib/search/common/searchHistoryService.ts b/src/vs/workbench/contrib/search/common/searchHistoryService.ts index 4e864e7c171..1c6640363af 100644 --- a/src/vs/workbench/contrib/search/common/searchHistoryService.ts +++ b/src/vs/workbench/contrib/search/common/searchHistoryService.ts @@ -28,7 +28,7 @@ export interface ISearchHistoryValues { export class SearchHistoryService implements ISearchHistoryService { declare readonly _serviceBrand: undefined; - private static readonly SEARCH_HISTORY_KEY = 'workbench.search.history'; + public static readonly SEARCH_HISTORY_KEY = 'workbench.search.history'; private readonly _onDidClearHistory = new Emitter(); readonly onDidClearHistory: Event = this._onDidClearHistory.event; @@ -61,7 +61,7 @@ export class SearchHistoryService implements ISearchHistoryService { if (isEmptyObject(history)) { this.storageService.remove(SearchHistoryService.SEARCH_HISTORY_KEY, StorageScope.WORKSPACE); } else { - this.storageService.store(SearchHistoryService.SEARCH_HISTORY_KEY, JSON.stringify(history), StorageScope.WORKSPACE, StorageTarget.MACHINE); + this.storageService.store(SearchHistoryService.SEARCH_HISTORY_KEY, JSON.stringify(history), StorageScope.WORKSPACE, StorageTarget.USER); } } } From a0208c89dfe806278dc116d2855018bea6e4de44 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 10 Jul 2023 12:56:28 -0700 Subject: [PATCH 289/926] Pick up latest TS nightly for building VS Code (#187500) --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 7f011f15bbd..4f04e9ddb1d 100644 --- a/package.json +++ b/package.json @@ -210,7 +210,7 @@ "ts-loader": "^9.4.2", "ts-node": "^10.9.1", "tsec": "0.2.7", - "typescript": "^5.2.0-dev.20230621", + "typescript": "^5.2.0-dev.20230710", "typescript-formatter": "7.1.0", "underscore": "^1.12.1", "util": "^0.12.4", diff --git a/yarn.lock b/yarn.lock index 13650bd8310..e8975eef7f6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10041,10 +10041,10 @@ typescript@^4.7.4: resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.4.tgz#c464abca159669597be5f96b8943500b238e60e6" integrity sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ== -typescript@^5.2.0-dev.20230621: - version "5.2.0-dev.20230621" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.2.0-dev.20230621.tgz#fc61be8c6448dce096aa5df1abda7966d730999d" - integrity sha512-8iIehsqwx8TrzBcPWAuXZ0Z4wF/+0CmwdbibftaivqXJlD3V4qWr26hn67r/UT/VNm67iO86XN45F8Viivvobw== +typescript@^5.2.0-dev.20230710: + version "5.2.0-dev.20230710" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.2.0-dev.20230710.tgz#6e42862d126c2c7b4ce14ce6124396561dd808c0" + integrity sha512-D4u4/pfdrIi94+F+iVzxzuzCk8bE/cbe4jng6Sce2Y1szj/QiBqN43X5lMrIbMR9Kb1G2AdWzYPrcNv/ButJGA== typical@^4.0.0: version "4.0.0" From 011ac2c103a032c754d33e5b75b29aa1613d5569 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 10 Jul 2023 13:03:41 -0700 Subject: [PATCH 290/926] Pick up TS 5.1.6 (#187495) For #187493 --- extensions/package.json | 2 +- extensions/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/extensions/package.json b/extensions/package.json index 8ab93875293..f5f0b7e6197 100644 --- a/extensions/package.json +++ b/extensions/package.json @@ -4,7 +4,7 @@ "license": "MIT", "description": "Dependencies shared by all extensions", "dependencies": { - "typescript": "5.1.3" + "typescript": "5.1.6" }, "scripts": { "postinstall": "node ./postinstall.mjs" diff --git a/extensions/yarn.lock b/extensions/yarn.lock index 50070eb668f..18507f58b59 100644 --- a/extensions/yarn.lock +++ b/extensions/yarn.lock @@ -228,10 +228,10 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -typescript@5.1.3: - version "5.1.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.3.tgz#8d84219244a6b40b6fb2b33cc1c062f715b9e826" - integrity sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw== +typescript@5.1.6: + version "5.1.6" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.6.tgz#02f8ac202b6dad2c0dd5e0913745b47a37998274" + integrity sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA== vscode-grammar-updater@^1.1.0: version "1.1.0" From 6aca75d9d09f7d9d32c18e58c836067f2d420495 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 10 Jul 2023 13:04:03 -0700 Subject: [PATCH 291/926] Fix markdown preview classes (#187501) For #187234 Got introduced through a bad PR change. Makes more sense to move this styling to the css file instead --- extensions/markdown-language-features/media/markdown.css | 1 + .../markdown-language-features/src/preview/documentRenderer.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/extensions/markdown-language-features/media/markdown.css b/extensions/markdown-language-features/media/markdown.css index 896531fdcc8..809a8bf0600 100644 --- a/extensions/markdown-language-features/media/markdown.css +++ b/extensions/markdown-language-features/media/markdown.css @@ -9,6 +9,7 @@ html, body { padding: 0 26px; line-height: var(--markdown-line-height, 22px); word-wrap: break-word; + overscroll-behavior-x: none; } body { diff --git a/extensions/markdown-language-features/src/preview/documentRenderer.ts b/extensions/markdown-language-features/src/preview/documentRenderer.ts index 57ec4ee0051..331fb5566a0 100644 --- a/extensions/markdown-language-features/src/preview/documentRenderer.ts +++ b/extensions/markdown-language-features/src/preview/documentRenderer.ts @@ -103,7 +103,7 @@ export class MdDocumentRenderer { ${this._getStyles(resourceProvider, sourceUri, config, imageInfo)} - + ${body.html} ${this._getScripts(resourceProvider, nonce)} From 13991f66032b18133c42e824dc2a4062ae781c32 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 10 Jul 2023 13:30:58 -0700 Subject: [PATCH 292/926] clean up and add comment --- .../tasks/browser/abstractTaskService.ts | 48 ++++++++++--------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index e8ec458751d..75bf1a9952d 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -2908,7 +2908,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer title: strings.fetching }; const promise = (async () => { - let taskGroupTasks: (Task | ConfiguringTask)[] = []; + let groupTasks: (Task | ConfiguringTask)[] = []; async function runSingleTask(task: Task | undefined, problemMatcherOptions: IProblemMatcherRunOptions | undefined, that: AbstractTaskService) { that.run(task, problemMatcherOptions, TaskRunSource.User).then(undefined, reason => { @@ -2942,27 +2942,29 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer if (absoluteURI) { const workspaceFolder = this._contextService.getWorkspaceFolder(absoluteURI); if (workspaceFolder) { - const config = this._getConfiguration(workspaceFolder); - globTasksDetected = ((config?.config?.tasks) || []).filter(task => task.group && typeof task.group !== 'string' && typeof task.group.isDefault === 'string').length > 0; + const configuredTasks = this._getConfiguration(workspaceFolder)?.config?.tasks; + if (configuredTasks) { + globTasksDetected = configuredTasks.filter(task => task.group && typeof task.group !== 'string' && typeof task.group.isDefault === 'string').length > 0; + // This will activate extensions, so only do so if necessary #185960 + if (globTasksDetected) { + // Fallback to absolute path of the file if it is not in a workspace or relative path cannot be found + const relativePath = workspaceFolder?.uri ? (resources.relativePath(workspaceFolder.uri, absoluteURI) ?? absoluteURI.path) : absoluteURI.path; - if (globTasksDetected) { - // fallback to absolute path of the file if it is not in a workspace or relative path cannot be found - const relativePath = workspaceFolder?.uri ? (resources.relativePath(workspaceFolder.uri, absoluteURI) ?? absoluteURI.path) : absoluteURI.path; + groupTasks = await this._findWorkspaceTasks((task) => { + const currentTaskGroup = task.configurationProperties.group; + if (currentTaskGroup && typeof currentTaskGroup !== 'string' && typeof currentTaskGroup.isDefault === 'string') { + return (currentTaskGroup._id === taskGroup._id && glob.match(currentTaskGroup.isDefault, relativePath)); + } - taskGroupTasks = await this._findWorkspaceTasks((task) => { - const currentTaskGroup = task.configurationProperties.group; - if (currentTaskGroup && typeof currentTaskGroup !== 'string' && typeof currentTaskGroup.isDefault === 'string') { - return (currentTaskGroup._id === taskGroup._id && glob.match(currentTaskGroup.isDefault, relativePath)); - } - - return false; - }); + return false; + }); + } } } } - if (!globTasksDetected && taskGroupTasks.length === 0) { - taskGroupTasks = await this._findWorkspaceTasksInGroup(TaskGroup.Build, true); + if (!globTasksDetected && groupTasks.length === 0) { + groupTasks = await this._findWorkspaceTasksInGroup(TaskGroup.Build, true); } const handleMultipleTasks = (areGlobTasks: boolean) => { @@ -2995,25 +2997,25 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer }; // A single default glob task was returned, just run it directly - if (taskGroupTasks.length === 1) { - return resolveTaskAndRun(taskGroupTasks[0]); + if (groupTasks.length === 1) { + return resolveTaskAndRun(groupTasks[0]); } // If there's multiple globs that match we want to show the quick picker for those tasks // We will need to call splitPerGroupType putting globs in defaults and the remaining tasks in none. // We don't need to carry on after here - if (globTasksDetected && taskGroupTasks.length > 1) { + if (globTasksDetected && groupTasks.length > 1) { return handleMultipleTasks(true); } // If no globs are found or matched fallback to checking for default tasks of the task group - if (!taskGroupTasks.length) { - taskGroupTasks = await this._findWorkspaceTasksInGroup(taskGroup, false); + if (!groupTasks.length) { + groupTasks = await this._findWorkspaceTasksInGroup(taskGroup, false); } // A single default task was returned, just run it directly - if (taskGroupTasks.length === 1) { - return resolveTaskAndRun(taskGroupTasks[0]); + if (groupTasks.length === 1) { + return resolveTaskAndRun(groupTasks[0]); } // Multiple default tasks returned, show the quickPicker From 261a75e3a4800d25ef6c2a03f52fb9a60c4ea65d Mon Sep 17 00:00:00 2001 From: Andrea Mah <31675041+andreamah@users.noreply.github.com> Date: Mon, 10 Jul 2023 13:41:50 -0700 Subject: [PATCH 293/926] add missing sinon restore calls in search (#187489) * add missing sinon restore calls * use `teardown(() => sinon.restore())` in search tests to restore stubs --- .../search/test/browser/searchModel.test.ts | 27 +++++-------------- .../search/test/browser/searchResult.test.ts | 5 ++-- 2 files changed, 9 insertions(+), 23 deletions(-) diff --git a/src/vs/workbench/contrib/search/test/browser/searchModel.test.ts b/src/vs/workbench/contrib/search/test/browser/searchModel.test.ts index e7254f5fb73..c6cfdd8c5ff 100644 --- a/src/vs/workbench/contrib/search/test/browser/searchModel.test.ts +++ b/src/vs/workbench/contrib/search/test/browser/searchModel.test.ts @@ -60,7 +60,6 @@ const lineOneRange = new OneLineRange(1, 0, 1); suite('SearchModel', () => { let instantiationService: TestInstantiationService; - let restoreStubs: sinon.SinonStub[]; const testSearchStats: IFileSearchStats = { fromCache: false, @@ -80,7 +79,6 @@ suite('SearchModel', () => { ]; setup(() => { - restoreStubs = []; instantiationService = new TestInstantiationService(); instantiationService.stub(ITelemetryService, NullTelemetryService); instantiationService.stub(ILabelService, { getUriBasenameLabel: (uri: URI) => '' }); @@ -92,11 +90,7 @@ suite('SearchModel', () => { instantiationService.stub(ILogService, new NullLogService()); }); - teardown(() => { - restoreStubs.forEach(element => { - element.restore(); - }); - }); + teardown(() => sinon.restore()); function searchServiceWithResults(results: IFileMatch[], complete: ISearchComplete | null = null): ISearchService { return { @@ -174,8 +168,7 @@ suite('SearchModel', () => { new TextSearchMatch('this is a test', new OneLineRange(1, 11, 15))), aRawMatch('/3', new TextSearchMatch('test', lineOneRange))]; const searchService = instantiationService.stub(ISearchService, searchServiceWithResults(results)); - const addContext = sinon.stub(CellMatch.prototype, 'addContext'); - restoreStubs.push(addContext); + sinon.stub(CellMatch.prototype, 'addContext'); const textSearch = sinon.spy(searchService, 'textSearch'); const mdInputCell = { @@ -264,8 +257,6 @@ suite('SearchModel', () => { scannedFiles: new ResourceSet([...localResults.keys()]), }); }); - restoreStubs.push(notebookSearch); - await model.search({ contentPattern: { pattern: 'test' }, type: QueryType.Text, folderQueries }); const actual = model.searchResult.matches(); @@ -329,7 +320,7 @@ suite('SearchModel', () => { test('Search Model: Search reports timed telemetry on search when progress is not called', () => { const target2 = sinon.spy(); - stub(nullEvent, 'stop', target2); + sinon.stub(nullEvent, 'stop').callsFake(target2); const target1 = sinon.stub().returns(nullEvent); instantiationService.stub(ITelemetryService, 'publicLog', target1); @@ -348,7 +339,7 @@ suite('SearchModel', () => { test('Search Model: Search reports timed telemetry on search when progress is called', () => { const target2 = sinon.spy(); - stub(nullEvent, 'stop', target2); + sinon.stub(nullEvent, 'stop').callsFake(target2); const target1 = sinon.stub().returns(nullEvent); instantiationService.stub(ITelemetryService, 'publicLog', target1); @@ -372,7 +363,7 @@ suite('SearchModel', () => { test('Search Model: Search reports timed telemetry on search when error is called', () => { const target2 = sinon.spy(); - stub(nullEvent, 'stop', target2); + sinon.stub(nullEvent, 'stop').callsFake(target2); const target1 = sinon.stub().returns(nullEvent); instantiationService.stub(ITelemetryService, 'publicLog', target1); @@ -392,7 +383,7 @@ suite('SearchModel', () => { test('Search Model: Search reports timed telemetry on search when error is cancelled error', () => { const target2 = sinon.spy(); - stub(nullEvent, 'stop', target2); + sinon.stub(nullEvent, 'stop').callsFake(target2); const target1 = sinon.stub().returns(nullEvent); instantiationService.stub(ITelemetryService, 'publicLog', target1); @@ -491,12 +482,6 @@ suite('SearchModel', () => { return { resource: createFileUriFromPathFromRoot(resource), cellResults: cells }; } - function stub(arg1: any, arg2: any, arg3: any): sinon.SinonStub { - const stub = sinon.stub(arg1, arg2).callsFake(arg3); - restoreStubs.push(stub); - return stub; - } - function stubModelService(instantiationService: TestInstantiationService): IModelService { instantiationService.stub(IThemeService, new TestThemeService()); const config = new TestConfigurationService(); diff --git a/src/vs/workbench/contrib/search/test/browser/searchResult.test.ts b/src/vs/workbench/contrib/search/test/browser/searchResult.test.ts index a65a4f7c43c..30e273f7096 100644 --- a/src/vs/workbench/contrib/search/test/browser/searchResult.test.ts +++ b/src/vs/workbench/contrib/search/test/browser/searchResult.test.ts @@ -51,6 +51,8 @@ suite('SearchResult', () => { instantiationService.stub(ILogService, new NullLogService()); }); + teardown(() => sinon.restore()); + test('Line Match', function () { const fileMatch = aFileMatch('folder/file.txt', null!); const lineMatch = new Match(fileMatch, ['0 foo bar'], new OneLineRange(0, 2, 5), new OneLineRange(1, 0, 5)); @@ -228,7 +230,7 @@ suite('SearchResult', () => { const cell1 = { cellKind: CellKind.Code } as ICellViewModel; const cell2 = { cellKind: CellKind.Code } as ICellViewModel; - const addContext = sinon.stub(CellMatch.prototype, 'addContext'); + sinon.stub(CellMatch.prototype, 'addContext'); const addFileMatch = sinon.spy(FolderMatch.prototype, "addFileMatch"); const fileMatch1 = aRawFileMatchWithCells('/1', @@ -263,7 +265,6 @@ suite('SearchResult', () => { assert.deepStrictEqual(fileMatch1.cellResults[0].webviewResults, (addFileMatch.getCall(0).args[0][0] as IFileMatchWithCells).cellResults[0].webviewResults); assert.deepStrictEqual(fileMatch2.cellResults[0].contentResults, (addFileMatch.getCall(0).args[0][1] as IFileMatchWithCells).cellResults[0].contentResults); assert.deepStrictEqual(fileMatch2.cellResults[0].webviewResults, (addFileMatch.getCall(0).args[0][1] as IFileMatchWithCells).cellResults[0].webviewResults); - addContext.restore(); }); test('Dispose disposes matches', function () { From f07abd224bf29609d00cb3b56cbbbf5ce0a409e4 Mon Sep 17 00:00:00 2001 From: Meghan Kulkarni Date: Mon, 10 Jul 2023 14:11:49 -0700 Subject: [PATCH 294/926] Make markdown link pasting feature smarter (#187170) * making markdown link pasting feature smarter * Update settings description Co-authored-by: Joyce Er * made checkPaste more concise * won't paste md link in fenced code or math --------- Co-authored-by: Joyce Er --- .../markdown-language-features/package.json | 14 ++++- .../package.nls.json | 5 +- .../src/commands/insertResource.ts | 4 +- .../copyFiles/copyPasteLinks.ts | 5 +- .../src/languageFeatures/copyFiles/shared.ts | 51 +++++++++++++++---- 5 files changed, 62 insertions(+), 17 deletions(-) diff --git a/extensions/markdown-language-features/package.json b/extensions/markdown-language-features/package.json index 1f4fcbc6034..1afabaf9dff 100644 --- a/extensions/markdown-language-features/package.json +++ b/extensions/markdown-language-features/package.json @@ -499,10 +499,20 @@ ] }, "markdown.editor.pasteUrlAsFormattedLink.enabled": { - "type": "boolean", + "type": "string", "scope": "resource", "markdownDescription": "%configuration.markdown.editor.pasteUrlAsFormattedLink.enabled%", - "default": true + "default":"smart", + "enum": [ + "always", + "smart", + "never" + ], + "markdownEnumDescriptions": [ + "%configuration.pasteUrlAsFormattedLink.always%", + "%configuration.pasteUrlAsFormattedLink.smart%", + "%configuration.pasteUrlAsFormattedLink.never%" + ] }, "markdown.validate.enabled": { "type": "boolean", diff --git a/extensions/markdown-language-features/package.nls.json b/extensions/markdown-language-features/package.nls.json index e39e597b02f..0468fbf1d79 100644 --- a/extensions/markdown-language-features/package.nls.json +++ b/extensions/markdown-language-features/package.nls.json @@ -41,9 +41,12 @@ "configuration.markdown.editor.drop.copyIntoWorkspace": "Controls if files outside of the workspace that are dropped into a Markdown editor should be copied into the workspace.\n\nUse `#markdown.copyFiles.destination#` to configure where copied dropped files should be created", "configuration.markdown.editor.filePaste.enabled": "Enable pasting files into a Markdown editor to create Markdown links. Requires enabling `#editor.pasteAs.enabled#`.", "configuration.markdown.editor.filePaste.copyIntoWorkspace": "Controls if files outside of the workspace that are pasted into a Markdown editor should be copied into the workspace.\n\nUse `#markdown.copyFiles.destination#` to configure where copied files should be created.", - "configuration.markdown.editor.pasteUrlAsFormattedLink.enabled": "Controls if a Markdown link is created when a URL is pasted into the Markdown editor. Requires enabling `#editor.pasteAs.enabled#`.", "configuration.copyIntoWorkspace.mediaFiles": "Try to copy external image and video files into the workspace.", "configuration.copyIntoWorkspace.never": "Do not copy external files into the workspace.", + "configuration.markdown.editor.pasteUrlAsFormattedLink.enabled": "Controls how a Markdown link is created when a URL is pasted into the Markdown editor. Requires enabling `#editor.pasteAs.enabled#`.", + "configuration.pasteUrlAsFormattedLink.always": "Always create a Markdown link when a URL is pasted into the Markdown editor.", + "configuration.pasteUrlAsFormattedLink.smart": "Does not create a Markdown link within a link snippet or code bracket.", + "configuration.pasteUrlAsFormattedLink.never": "Never create a Markdown link when a URL is pasted into the Markdown editor.", "configuration.markdown.validate.enabled.description": "Enable all error reporting in Markdown files.", "configuration.markdown.validate.referenceLinks.enabled.description": "Validate reference links in Markdown files, for example: `[link][ref]`. Requires enabling `#markdown.validate.enabled#`.", "configuration.markdown.validate.fragmentLinks.enabled.description": "Validate fragment links to headers in the current Markdown file, for example: `[link](#header)`. Requires enabling `#markdown.validate.enabled#`.", diff --git a/extensions/markdown-language-features/src/commands/insertResource.ts b/extensions/markdown-language-features/src/commands/insertResource.ts index 2e7b97c3048..da50c83c991 100644 --- a/extensions/markdown-language-features/src/commands/insertResource.ts +++ b/extensions/markdown-language-features/src/commands/insertResource.ts @@ -76,10 +76,10 @@ async function insertLink(activeEditor: vscode.TextEditor, selectedFiles: vscode await vscode.workspace.applyEdit(edit); } -function createInsertLinkEdit(activeEditor: vscode.TextEditor, selectedFiles: vscode.Uri[], insertAsMedia: boolean, title = '', placeholderValue = 0) { +function createInsertLinkEdit(activeEditor: vscode.TextEditor, selectedFiles: vscode.Uri[], insertAsMedia: boolean, title = '', placeholderValue = 0, smartPaste = false) { const snippetEdits = coalesce(activeEditor.selections.map((selection, i): vscode.SnippetTextEdit | undefined => { const selectionText = activeEditor.document.getText(selection); - const snippet = createUriListSnippet(activeEditor.document, selectedFiles, title, placeholderValue, { + const snippet = createUriListSnippet(activeEditor.document, selectedFiles, title, placeholderValue, smartPaste, { insertAsMedia, placeholderText: selectionText, placeholderStartIndex: (i + 1) * selectedFiles.length, diff --git a/extensions/markdown-language-features/src/languageFeatures/copyFiles/copyPasteLinks.ts b/extensions/markdown-language-features/src/languageFeatures/copyFiles/copyPasteLinks.ts index 313a3916688..90755d990fc 100644 --- a/extensions/markdown-language-features/src/languageFeatures/copyFiles/copyPasteLinks.ts +++ b/extensions/markdown-language-features/src/languageFeatures/copyFiles/copyPasteLinks.ts @@ -5,7 +5,6 @@ import * as vscode from 'vscode'; import { getMarkdownLink } from './shared'; - class PasteLinkEditProvider implements vscode.DocumentPasteEditProvider { readonly id = 'insertMarkdownLink'; @@ -15,8 +14,8 @@ class PasteLinkEditProvider implements vscode.DocumentPasteEditProvider { dataTransfer: vscode.DataTransfer, token: vscode.CancellationToken, ): Promise { - const enabled = vscode.workspace.getConfiguration('markdown', document).get('editor.pasteUrlAsFormattedLink.enabled', true); - if (!enabled) { + const enabled = vscode.workspace.getConfiguration('markdown', document).get<'always' | 'smart' | 'never'>('editor.pasteUrlAsFormattedLink.enabled', 'smart'); + if (enabled === 'never') { return; } diff --git a/extensions/markdown-language-features/src/languageFeatures/copyFiles/shared.ts b/extensions/markdown-language-features/src/languageFeatures/copyFiles/shared.ts index 28a86a22d70..f40fea3556f 100644 --- a/extensions/markdown-language-features/src/languageFeatures/copyFiles/shared.ts +++ b/extensions/markdown-language-features/src/languageFeatures/copyFiles/shared.ts @@ -65,15 +65,26 @@ export async function getMarkdownLink(document: vscode.TextDocument, ranges: rea if (ranges.length === 0) { return; } + const enabled = vscode.workspace.getConfiguration('markdown', document).get<'always' | 'smart' | 'never'>('editor.pasteUrlAsFormattedLink.enabled', 'always'); const edits: vscode.SnippetTextEdit[] = []; let placeHolderValue: number = ranges.length; let label: string = ''; + let smartPaste: boolean = false; for (let i = 0; i < ranges.length; i++) { - const snippet = await tryGetUriListSnippet(document, urlList, token, document.getText(ranges[i]), placeHolderValue); + if (enabled === 'smart') { + const inMarkdownLink = checkPaste(document, ranges, /\[([^\]]*)\]\(([^)]*)\)/g, i); + const inFencedCode = checkPaste(document, ranges, /^```[\s\S]*?```$/gm, i); + const inFencedMath = checkPaste(document, ranges, /^\$\$[\s\S]*?\$\$$/gm, i); + smartPaste = (inMarkdownLink || inFencedCode || inFencedMath); + } + + const snippet = await tryGetUriListSnippet(document, urlList, token, document.getText(ranges[i]), placeHolderValue, smartPaste); if (!snippet) { return; } + + smartPaste = false; placeHolderValue--; edits.push(new vscode.SnippetTextEdit(ranges[i], snippet.snippet)); label = snippet.label; @@ -85,7 +96,20 @@ export async function getMarkdownLink(document: vscode.TextDocument, ranges: rea return { additionalEdits, label }; } -export async function tryGetUriListSnippet(document: vscode.TextDocument, urlList: String, token: vscode.CancellationToken, title = '', placeHolderValue = 0): Promise<{ snippet: vscode.SnippetString; label: string } | undefined> { +function checkPaste(document: vscode.TextDocument, ranges: readonly vscode.Range[], regex: RegExp, index: number): boolean { + const rangeStartOffset = document.offsetAt(ranges[index].start); + const rangeEndOffset = document.offsetAt(ranges[index].end); + const matches = [...document.getText().matchAll(regex)]; + for (const match of matches) { + if (match.index !== undefined && rangeStartOffset > match.index && rangeEndOffset < match.index + match[0].length) { + return true; + } + } + + return false; +} + +export async function tryGetUriListSnippet(document: vscode.TextDocument, urlList: String, token: vscode.CancellationToken, title = '', placeHolderValue = 0, smartPaste = false): Promise<{ snippet: vscode.SnippetString; label: string } | undefined> { if (token.isCancellationRequested) { return undefined; } @@ -99,7 +123,7 @@ export async function tryGetUriListSnippet(document: vscode.TextDocument, urlLis } } - return createUriListSnippet(document, uris, title, placeHolderValue); + return createUriListSnippet(document, uris, title, placeHolderValue, smartPaste); } interface UriListSnippetOptions { @@ -122,6 +146,7 @@ export function createUriListSnippet( uris: readonly vscode.Uri[], title = '', placeholderValue = 0, + smartPaste = false, options?: UriListSnippetOptions, ): { snippet: vscode.SnippetString; label: string } | undefined { if (!uris.length) { @@ -164,13 +189,21 @@ export function createUriListSnippet( snippet.appendText(`](${escapeMarkdownLinkPath(mdPath)})`); } else { insertedLinkCount++; - snippet.appendText('['); - snippet.appendPlaceholder(escapeBrackets(title) || 'Title', placeholderValue); - if (externalUriSchemes.includes(uri.scheme)) { - const uriString = uri.toString(true); - snippet.appendText(`](${uriString})`); + if (smartPaste) { + if (externalUriSchemes.includes(uri.scheme)) { + snippet.appendText(uri.toString(true)); + } else { + snippet.appendText(escapeMarkdownLinkPath(mdPath)); + } } else { - snippet.appendText(`](${escapeMarkdownLinkPath(mdPath)})`); + snippet.appendText('['); + snippet.appendPlaceholder(escapeBrackets(title) || 'Title', placeholderValue); + if (externalUriSchemes.includes(uri.scheme)) { + const uriString = uri.toString(true); + snippet.appendText(`](${uriString})`); + } else { + snippet.appendText(`](${escapeMarkdownLinkPath(mdPath)})`); + } } } } From a2daa8d69e1925de3a65e3e2c6549d97d66787b1 Mon Sep 17 00:00:00 2001 From: rebornix Date: Mon, 10 Jul 2023 14:54:41 -0700 Subject: [PATCH 295/926] paddingTop/Bottom --- src/vs/base/browser/ui/list/listPaging.ts | 2 +- src/vs/base/browser/ui/list/listView.ts | 30 +++++++++---------- src/vs/base/browser/ui/list/listWidget.ts | 4 +-- src/vs/base/browser/ui/list/rangeMap.ts | 24 +++++++-------- .../browser/diff/notebookDiffEditor.ts | 2 +- .../notebook/browser/notebookEditorWidget.ts | 8 ++--- .../test/browser/notebookCellList.test.ts | 4 +-- .../terminal/browser/terminalTabsList.ts | 2 +- 8 files changed, 38 insertions(+), 38 deletions(-) diff --git a/src/vs/base/browser/ui/list/listPaging.ts b/src/vs/base/browser/ui/list/listPaging.ts index 3701207d187..a3d2eb16cfc 100644 --- a/src/vs/base/browser/ui/list/listPaging.ts +++ b/src/vs/base/browser/ui/list/listPaging.ts @@ -108,7 +108,7 @@ export interface IPagedListOptions { readonly mouseSupport?: boolean; readonly horizontalScrolling?: boolean; readonly scrollByPage?: boolean; - readonly additionalScrollHeight?: number; + readonly paddingBottom?: number; } function fromPagedListOptions(modelProvider: () => IPagedModel, options: IPagedListOptions): IListOptions { diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index d3ff8d806bd..312b1ec6fb5 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -56,13 +56,13 @@ export interface IListViewAccessibilityProvider { } export interface IListViewOptionsUpdate { - readonly additionalScrollHeight?: number; readonly smoothScrolling?: boolean; readonly horizontalScrolling?: boolean; readonly scrollByPage?: boolean; readonly mouseWheelScrollSensitivity?: number; readonly fastScrollSensitivity?: number; - readonly topPadding?: number; + readonly paddingTop?: number; + readonly paddingBottom?: number; } export interface IListViewOptions extends IListViewOptionsUpdate { @@ -281,7 +281,7 @@ export class ListView implements IListView { private items: IItem[]; private itemId: number; private rangeMap: RangeMap; - private topPadding: number; + private paddingTop: number; private cache: RowCache; private renderers = new Map>(); private lastRenderTop: number; @@ -300,7 +300,7 @@ export class ListView implements IListView { private setRowLineHeight: boolean; private setRowHeight: boolean; private supportDynamicHeights: boolean; - private additionalScrollHeight: number; + private paddingBottom: number; private accessibilityProvider: ListViewAccessibilityProvider; private scrollWidth: number | undefined; @@ -366,8 +366,8 @@ export class ListView implements IListView { this.items = []; this.itemId = 0; - this.topPadding = options.topPadding ?? 0; - this.rangeMap = new RangeMap(this.topPadding); + this.paddingTop = options.paddingTop ?? 0; + this.rangeMap = new RangeMap(this.paddingTop); for (const renderer of renderers) { this.renderers.set(renderer.templateId, renderer); @@ -389,7 +389,7 @@ export class ListView implements IListView { this._horizontalScrolling = options.horizontalScrolling ?? DefaultOptions.horizontalScrolling; this.domNode.classList.toggle('horizontal-scrolling', this._horizontalScrolling); - this.additionalScrollHeight = typeof options.additionalScrollHeight === 'undefined' ? 0 : options.additionalScrollHeight; + this.paddingBottom = typeof options.paddingBottom === 'undefined' ? 0 : options.paddingBottom; this.accessibilityProvider = new ListViewAccessibilityProvider(options.accessibilityProvider); @@ -444,8 +444,8 @@ export class ListView implements IListView { } updateOptions(options: IListViewOptionsUpdate) { - if (options.additionalScrollHeight !== undefined) { - this.additionalScrollHeight = options.additionalScrollHeight; + if (options.paddingBottom !== undefined) { + this.paddingBottom = options.paddingBottom; this.scrollableElement.setScrollDimensions({ scrollHeight: this.scrollHeight }); } @@ -475,12 +475,12 @@ export class ListView implements IListView { this.scrollableElement.updateOptions(scrollableOptions); } - if (options.topPadding !== undefined && options.topPadding !== this.topPadding) { + if (options.paddingTop !== undefined && options.paddingTop !== this.paddingTop) { // trigger a rerender - this.topPadding = options.topPadding; + this.paddingTop = options.paddingTop; const lastRenderRange = this.getRenderRange(this.lastRenderTop, this.lastRenderHeight); - const offset = options.topPadding - this.rangeMap.topPadding; - this.rangeMap.topPadding = options.topPadding; + const offset = options.paddingTop - this.rangeMap.paddingTop; + this.rangeMap.paddingTop = options.paddingTop; this.render(lastRenderRange, Math.max(0, this.lastRenderTop + offset), this.lastRenderHeight, undefined, undefined, true); this.setScrollTop(this.lastRenderTop); @@ -622,7 +622,7 @@ export class ListView implements IListView { // TODO@joao: improve this optimization to catch even more cases if (start === 0 && deleteCount >= this.items.length) { - this.rangeMap = new RangeMap(this.topPadding); + this.rangeMap = new RangeMap(this.paddingTop); this.rangeMap.splice(0, 0, inserted); deleted = this.items; this.items = inserted; @@ -1037,7 +1037,7 @@ export class ListView implements IListView { } get scrollHeight(): number { - return this._scrollHeight + (this.horizontalScrolling ? 10 : 0) + this.additionalScrollHeight; + return this._scrollHeight + (this.horizontalScrolling ? 10 : 0) + this.paddingBottom; } // Events diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 64bd1aa8e0f..75806511d49 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -991,13 +991,13 @@ export interface IListOptions extends IListOptionsUpdate { readonly mouseSupport?: boolean; readonly horizontalScrolling?: boolean; readonly scrollByPage?: boolean; - readonly additionalScrollHeight?: number; + readonly paddingBottom?: number; readonly transformOptimization?: boolean; readonly smoothScrolling?: boolean; readonly scrollableElementChangeOptions?: ScrollableElementChangeOptions; readonly alwaysConsumeMouseWheel?: boolean; readonly initialSize?: Dimension; - readonly topPadding?: number; + readonly paddingTop?: number; } export interface IListStyles { diff --git a/src/vs/base/browser/ui/list/rangeMap.ts b/src/vs/base/browser/ui/list/rangeMap.ts index bd77b9a24b2..5743bcad2c9 100644 --- a/src/vs/base/browser/ui/list/rangeMap.ts +++ b/src/vs/base/browser/ui/list/rangeMap.ts @@ -91,20 +91,20 @@ export class RangeMap { private groups: IRangedGroup[] = []; private _size = 0; - private _topPadding = 0; + private _paddingTop = 0; - get topPadding() { - return this._topPadding; + get paddingTop() { + return this._paddingTop; } - set topPadding(topPadding: number) { - this._topPadding = topPadding; - this._size = this._topPadding + this.groups.reduce((t, g) => t + (g.size * (g.range.end - g.range.start)), 0); + set paddingTop(topPadding: number) { + this._paddingTop = topPadding; + this._size = this._paddingTop + this.groups.reduce((t, g) => t + (g.size * (g.range.end - g.range.start)), 0); } constructor(topPadding?: number) { - this._topPadding = topPadding || 0; - this._size = this._topPadding; + this._paddingTop = topPadding || 0; + this._size = this._paddingTop; } splice(index: number, deleteCount: number, items: IItem[] = []): void { @@ -119,7 +119,7 @@ export class RangeMap { })); this.groups = concat(before, middle, after); - this._size = this._topPadding + this.groups.reduce((t, g) => t + (g.size * (g.range.end - g.range.start)), 0); + this._size = this._paddingTop + this.groups.reduce((t, g) => t + (g.size * (g.range.end - g.range.start)), 0); } /** @@ -150,12 +150,12 @@ export class RangeMap { return -1; } - if (position < this._topPadding) { + if (position < this._paddingTop) { return 0; } let index = 0; - let size = this._topPadding; + let size = this._paddingTop; for (const group of this.groups) { const count = group.range.end - group.range.start; @@ -196,7 +196,7 @@ export class RangeMap { const newCount = count + groupCount; if (index < newCount) { - return this._topPadding + position + ((index - count) * group.size); + return this._paddingTop + position + ((index - count) * group.size); } position += groupCount * group.size; diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditor.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditor.ts index cf7cdc955bc..e21aba1d2ec 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditor.ts @@ -268,7 +268,7 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD mouseSupport: true, multipleSelectionSupport: false, typeNavigationEnabled: true, - additionalScrollHeight: 0, + paddingBottom: 0, // transformOptimization: (isMacintosh && isNative) || getTitleBarStyle(this.configurationService, this.environmentService) === 'native', styleController: (_suffix: string) => { return this._list!; }, overrideStyles: { diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 32ad43d1b1c..ac28637eebb 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -869,10 +869,10 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD multipleSelectionSupport: true, selectionNavigation: true, typeNavigationEnabled: true, - additionalScrollHeight: 0, + paddingBottom: 0, transformOptimization: false, //(isMacintosh && isNative) || getTitleBarStyle(this.configurationService, this.environmentService) === 'native', initialSize: this._dimension, - topPadding: this._notebookOptions.computeTopInsertToolbarHeight(this.viewModel?.viewType), + paddingTop: this._notebookOptions.computeTopInsertToolbarHeight(this.viewModel?.viewType), styleController: (_suffix: string) => { return this._list; }, overrideStyles: { listBackground: notebookEditorBackground, @@ -1766,12 +1766,12 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD const newCellListHeight = newBodyHeight; if (this._list.getRenderHeight() < newCellListHeight) { // the new dimension is larger than the list viewport, update its additional height first, otherwise the list view will move down a bit (as the `scrollBottom` will move down) - this._list.updateOptions({ additionalScrollHeight: this._allowScrollBeyondLastLine() ? Math.max(0, (newCellListHeight - 50)) : 0, topPadding: topInserToolbarHeight }); + this._list.updateOptions({ paddingBottom: this._allowScrollBeyondLastLine() ? Math.max(0, (newCellListHeight - 50)) : 0, paddingTop: topInserToolbarHeight }); this._list.layout(newCellListHeight, dimension.width); } else { // the new dimension is smaller than the list viewport, if we update the additional height, the `scrollBottom` will move up, which moves the whole list view upwards a bit. So we run a layout first. this._list.layout(newCellListHeight, dimension.width); - this._list.updateOptions({ additionalScrollHeight: this._allowScrollBeyondLastLine() ? Math.max(0, (newCellListHeight - 50)) : 0, topPadding: topInserToolbarHeight }); + this._list.updateOptions({ paddingBottom: this._allowScrollBeyondLastLine() ? Math.max(0, (newCellListHeight - 50)) : 0, paddingTop: topInserToolbarHeight }); } this._overlayContainer.style.visibility = 'visible'; diff --git a/src/vs/workbench/contrib/notebook/test/browser/notebookCellList.test.ts b/src/vs/workbench/contrib/notebook/test/browser/notebookCellList.test.ts index 047e8baa9aa..b8f060ed210 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/notebookCellList.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/notebookCellList.test.ts @@ -139,8 +139,8 @@ suite('NotebookCellList', () => { }); const cellList = createNotebookCellList(instantiationService); - // without additionalscrollheight, the last 20 px will always be hidden due to `topInsertToolbarHeight` - cellList.updateOptions({ additionalScrollHeight: 100 }); + // without paddingBottom, the last 20 px will always be hidden due to `topInsertToolbarHeight` + cellList.updateOptions({ paddingBottom: 100 }); cellList.attachViewModel(viewModel); // render height 210, it can render 3 full cells and 1 partial cell diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts index 29ff72688d2..4a3fb064e99 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts @@ -97,7 +97,7 @@ export class TerminalTabList extends WorkbenchList { accessibilityProvider: instantiationService.createInstance(TerminalTabsAccessibilityProvider), smoothScrolling: _configurationService.getValue('workbench.list.smoothScrolling'), multipleSelectionSupport: true, - additionalScrollHeight: TerminalTabsListSizes.TabHeight, + paddingBottom: TerminalTabsListSizes.TabHeight, dnd: instantiationService.createInstance(TerminalTabsDragAndDrop), openOnSingleClick: true }, From ef62d8e8fc4dd2b6e9fb0fded25ef8feee308905 Mon Sep 17 00:00:00 2001 From: rebornix Date: Mon, 10 Jul 2023 14:56:24 -0700 Subject: [PATCH 296/926] Remove paddingTop from ListView --- src/vs/base/browser/ui/list/listView.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index 312b1ec6fb5..b73e5811121 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -281,7 +281,6 @@ export class ListView implements IListView { private items: IItem[]; private itemId: number; private rangeMap: RangeMap; - private paddingTop: number; private cache: RowCache; private renderers = new Map>(); private lastRenderTop: number; @@ -366,8 +365,7 @@ export class ListView implements IListView { this.items = []; this.itemId = 0; - this.paddingTop = options.paddingTop ?? 0; - this.rangeMap = new RangeMap(this.paddingTop); + this.rangeMap = new RangeMap(options.paddingTop ?? 0); for (const renderer of renderers) { this.renderers.set(renderer.templateId, renderer); @@ -475,9 +473,8 @@ export class ListView implements IListView { this.scrollableElement.updateOptions(scrollableOptions); } - if (options.paddingTop !== undefined && options.paddingTop !== this.paddingTop) { + if (options.paddingTop !== undefined && options.paddingTop !== this.rangeMap.paddingTop) { // trigger a rerender - this.paddingTop = options.paddingTop; const lastRenderRange = this.getRenderRange(this.lastRenderTop, this.lastRenderHeight); const offset = options.paddingTop - this.rangeMap.paddingTop; this.rangeMap.paddingTop = options.paddingTop; @@ -622,7 +619,7 @@ export class ListView implements IListView { // TODO@joao: improve this optimization to catch even more cases if (start === 0 && deleteCount >= this.items.length) { - this.rangeMap = new RangeMap(this.paddingTop); + this.rangeMap = new RangeMap(this.rangeMap.paddingTop); this.rangeMap.splice(0, 0, inserted); deleted = this.items; this.items = inserted; From 30d566800169c48652b6ca68f66b9d97c8ff0dd6 Mon Sep 17 00:00:00 2001 From: rebornix Date: Mon, 10 Jul 2023 14:58:41 -0700 Subject: [PATCH 297/926] Resolve comments --- src/vs/base/browser/ui/list/rangeMap.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/base/browser/ui/list/rangeMap.ts b/src/vs/base/browser/ui/list/rangeMap.ts index 5743bcad2c9..91f435cd822 100644 --- a/src/vs/base/browser/ui/list/rangeMap.ts +++ b/src/vs/base/browser/ui/list/rangeMap.ts @@ -97,13 +97,13 @@ export class RangeMap { return this._paddingTop; } - set paddingTop(topPadding: number) { - this._paddingTop = topPadding; - this._size = this._paddingTop + this.groups.reduce((t, g) => t + (g.size * (g.range.end - g.range.start)), 0); + set paddingTop(paddingTop: number) { + this._size = this._size + paddingTop - this._paddingTop; + this._paddingTop = paddingTop; } constructor(topPadding?: number) { - this._paddingTop = topPadding || 0; + this._paddingTop = topPadding ?? 0; this._size = this._paddingTop; } From fa823baa3cbd35c01b5c4a7e77d8e41dfe178c8c Mon Sep 17 00:00:00 2001 From: rebornix Date: Mon, 10 Jul 2023 15:01:33 -0700 Subject: [PATCH 298/926] :lipstick: --- src/vs/base/browser/ui/list/listWidget.ts | 2 +- .../workbench/contrib/notebook/browser/notebookEditorWidget.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 75806511d49..e8ec5372e18 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -991,13 +991,13 @@ export interface IListOptions extends IListOptionsUpdate { readonly mouseSupport?: boolean; readonly horizontalScrolling?: boolean; readonly scrollByPage?: boolean; - readonly paddingBottom?: number; readonly transformOptimization?: boolean; readonly smoothScrolling?: boolean; readonly scrollableElementChangeOptions?: ScrollableElementChangeOptions; readonly alwaysConsumeMouseWheel?: boolean; readonly initialSize?: Dimension; readonly paddingTop?: number; + readonly paddingBottom?: number; } export interface IListStyles { diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index ac28637eebb..801f7d93f61 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -869,10 +869,10 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD multipleSelectionSupport: true, selectionNavigation: true, typeNavigationEnabled: true, + paddingTop: this._notebookOptions.computeTopInsertToolbarHeight(this.viewModel?.viewType), paddingBottom: 0, transformOptimization: false, //(isMacintosh && isNative) || getTitleBarStyle(this.configurationService, this.environmentService) === 'native', initialSize: this._dimension, - paddingTop: this._notebookOptions.computeTopInsertToolbarHeight(this.viewModel?.viewType), styleController: (_suffix: string) => { return this._list; }, overrideStyles: { listBackground: notebookEditorBackground, From 1beeb2e113ddf014478124e1aae1c341cc55ee2e Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Mon, 10 Jul 2023 15:01:54 -0700 Subject: [PATCH 299/926] Repopulate slash command in chat input editor (#187207) * Repopulate slash command in chat input editor --- .../browser/contrib/chatInputEditorContrib.ts | 32 ++++++++++++++++++- .../contrib/chat/common/chatService.ts | 3 ++ .../contrib/chat/common/chatServiceImpl.ts | 9 ++++++ .../vscode.proposed.interactive.d.ts | 1 + 4 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts index 4dff6bbd72a..d869e5bed4f 100644 --- a/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts @@ -21,6 +21,7 @@ import { IChatWidget, IChatWidgetService } from 'vs/workbench/contrib/chat/brows import { ChatWidget } from 'vs/workbench/contrib/chat/browser/chatWidget'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { ChatInputPart } from 'vs/workbench/contrib/chat/browser/chatInputPart'; +import { IChatService } from 'vs/workbench/contrib/chat/common/chatService'; const decorationDescription = 'chat'; const slashCommandPlaceholderDecorationType = 'chat-session-detail'; @@ -131,7 +132,36 @@ class InputEditorDecorations extends Disposable { } } -ChatWidget.CONTRIBS.push(InputEditorDecorations); +class InputEditorSlashCommandFollowups extends Disposable { + constructor( + private readonly widget: IChatWidget, + @IChatService private readonly chatService: IChatService + ) { + super(); + this._register(this.chatService.onDidCompleteSlashCommand(({ slashCommand, sessionId }) => this.repopulateSlashCommand(slashCommand, sessionId))); + } + + private async repopulateSlashCommand(slashCommand: string, sessionId: string) { + if (this.widget.viewModel?.sessionId !== sessionId) { + return; + } + + const slashCommands = await this.widget.getSlashCommands(); + + if (this.widget.inputEditor.getValue().trim().length !== 0) { + return; + } + + if (slashCommands?.find(c => c.command === slashCommand)?.shouldRepopulate) { + const value = `/${slashCommand} `; + this.widget.inputEditor.setValue(value); + this.widget.inputEditor.setPosition({ lineNumber: 1, column: value.length + 1 }); + + } + } +} + +ChatWidget.CONTRIBS.push(InputEditorDecorations, InputEditorSlashCommandFollowups); class SlashCommandCompletions extends Disposable { constructor( diff --git a/src/vs/workbench/contrib/chat/common/chatService.ts b/src/vs/workbench/contrib/chat/common/chatService.ts index 477f7118062..8acfa91959c 100644 --- a/src/vs/workbench/contrib/chat/common/chatService.ts +++ b/src/vs/workbench/contrib/chat/common/chatService.ts @@ -67,6 +67,7 @@ export interface ISlashCommandProvider { export interface ISlashCommand { command: string; + shouldRepopulate?: boolean; provider?: ISlashCommandProvider; sortText?: string; detail?: string; @@ -176,6 +177,8 @@ export const IChatService = createDecorator('IChatService'); export interface IChatService { _serviceBrand: undefined; transferredSessionId: string | undefined; + + onDidCompleteSlashCommand: Event<{ slashCommand: string; sessionId: string }>; registerProvider(provider: IChatProvider): IDisposable; registerSlashCommandProvider(provider: ISlashCommandProvider): IDisposable; getProviderInfos(): IChatProviderInfo[]; diff --git a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts index c15379f06c6..3c20b21d168 100644 --- a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts +++ b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts @@ -135,6 +135,9 @@ export class ChatService extends Disposable implements IChatService { private readonly _onDidPerformUserAction = this._register(new Emitter()); public readonly onDidPerformUserAction: Event = this._onDidPerformUserAction.event; + private readonly _onDidCompleteSlashCommand = this._register(new Emitter<{ slashCommand: string; sessionId: string }>()); + public readonly onDidCompleteSlashCommand = this._onDidCompleteSlashCommand.event; + constructor( @IStorageService private readonly storageService: IStorageService, @ILogService private readonly logService: ILogService, @@ -472,9 +475,15 @@ export class ChatService extends Disposable implements IChatService { Promise.resolve(provider.provideFollowups(model.session!, CancellationToken.None)).then(followups => { model.setFollowups(request, withNullAsUndefined(followups)); model.completeResponse(request); + if (usedSlashCommand?.command) { + this._onDidCompleteSlashCommand.fire({ slashCommand: usedSlashCommand.command, sessionId: model.sessionId }); + } }); } else { model.completeResponse(request); + if (usedSlashCommand?.command) { + this._onDidCompleteSlashCommand.fire({ slashCommand: usedSlashCommand.command, sessionId: model.sessionId }); + } } } }); diff --git a/src/vscode-dts/vscode.proposed.interactive.d.ts b/src/vscode-dts/vscode.proposed.interactive.d.ts index 916e425189c..79d6db91573 100644 --- a/src/vscode-dts/vscode.proposed.interactive.d.ts +++ b/src/vscode-dts/vscode.proposed.interactive.d.ts @@ -128,6 +128,7 @@ declare module 'vscode' { export interface InteractiveSessionSlashCommand { command: string; + shouldRepopulate?: boolean; kind: CompletionItemKind; detail?: string; } From 35be9af88eb6757f7db17b1fc3de2648ebf8c43d Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 10 Jul 2023 15:03:31 -0700 Subject: [PATCH 300/926] clean up accessible view --- .../accessibility/browser/accessibleView.ts | 14 +++++--------- .../browser/accessibility/accessibility.css | 4 ++-- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index a9b03c3ed6f..534d08414ea 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -24,8 +24,7 @@ import { SelectionClipboardContributionID } from 'vs/workbench/contrib/codeEdito import { getSimpleEditorOptions } from 'vs/workbench/contrib/codeEditor/browser/simpleEditorOptions'; const enum DIMENSION_DEFAULT { - WIDTH = .6, - HEIGHT = 200 + WIDTH = 800 } export interface IAccessibleContentProvider { @@ -88,14 +87,15 @@ class AccessibleView extends Disposable { quickSuggestions: false, renderWhitespace: 'none', dropIntoEditor: { enabled: true }, - readOnly: true + readOnly: true, + fontFamily: 'var(--monaco-monospace-font)' }; this._editorWidget = this._register(this._instantiationService.createInstance(CodeEditorWidget, this._editorContainer, editorOptions, codeEditorWidgetOptions)); } show(provider: IAccessibleContentProvider): void { const delegate: IContextViewDelegate = { - getAnchor: () => this._editorContainer, + getAnchor: () => { return { x: (window.innerWidth / 2) - (DIMENSION_DEFAULT.WIDTH / 2), y: (window.innerHeight / 2) - (this.editorWidget.getContentHeight() / 2) }; }, render: (container) => { return this._render(provider, container); }, @@ -161,11 +161,7 @@ class AccessibleView extends Disposable { } private _layout(): void { - const windowWidth = window.innerWidth; - const width = windowWidth * DIMENSION_DEFAULT.WIDTH; - this._editorWidget.layout({ width, height: this._editorWidget.getContentHeight() }); - const left = Math.round((windowWidth - width) / 2); - this._editorContainer.style.left = `${left}px`; + this._editorWidget.layout({ width: DIMENSION_DEFAULT.WIDTH, height: this._editorWidget.getContentHeight() }); } private async _getTextModel(resource: URI): Promise { diff --git a/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.css b/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.css index d56086e674c..0a03582ca60 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.css +++ b/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.css @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ .accessible-view { - position: relative; + position: absolute; background-color: var(--vscode-editorWidget-background); color: var(--vscode-editorWidget-foreground); box-shadow: 0 2px 8px var(--vscode-widget-shadow); - border: 2px solid var(--vscode-contrastActiveBorder); + border: 2px solid var(--vscode-focusBorder); } From 54b7f4272e82fb691ebd5ae014c3f6f64318a1c3 Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Mon, 10 Jul 2023 15:49:50 -0700 Subject: [PATCH 301/926] Repopulate slash command when user submits query (#187514) --- .../chat/browser/contrib/chatInputEditorContrib.ts | 2 +- src/vs/workbench/contrib/chat/common/chatService.ts | 2 +- .../contrib/chat/common/chatServiceImpl.ts | 13 +++++-------- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts index d869e5bed4f..1bf461d1ae3 100644 --- a/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts @@ -138,7 +138,7 @@ class InputEditorSlashCommandFollowups extends Disposable { @IChatService private readonly chatService: IChatService ) { super(); - this._register(this.chatService.onDidCompleteSlashCommand(({ slashCommand, sessionId }) => this.repopulateSlashCommand(slashCommand, sessionId))); + this._register(this.chatService.onDidSubmitSlashCommand(({ slashCommand, sessionId }) => this.repopulateSlashCommand(slashCommand, sessionId))); } private async repopulateSlashCommand(slashCommand: string, sessionId: string) { diff --git a/src/vs/workbench/contrib/chat/common/chatService.ts b/src/vs/workbench/contrib/chat/common/chatService.ts index 8acfa91959c..95ebca45502 100644 --- a/src/vs/workbench/contrib/chat/common/chatService.ts +++ b/src/vs/workbench/contrib/chat/common/chatService.ts @@ -178,7 +178,7 @@ export interface IChatService { _serviceBrand: undefined; transferredSessionId: string | undefined; - onDidCompleteSlashCommand: Event<{ slashCommand: string; sessionId: string }>; + onDidSubmitSlashCommand: Event<{ slashCommand: string; sessionId: string }>; registerProvider(provider: IChatProvider): IDisposable; registerSlashCommandProvider(provider: ISlashCommandProvider): IDisposable; getProviderInfos(): IChatProviderInfo[]; diff --git a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts index 3c20b21d168..68a14aaf8b7 100644 --- a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts +++ b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts @@ -135,8 +135,8 @@ export class ChatService extends Disposable implements IChatService { private readonly _onDidPerformUserAction = this._register(new Emitter()); public readonly onDidPerformUserAction: Event = this._onDidPerformUserAction.event; - private readonly _onDidCompleteSlashCommand = this._register(new Emitter<{ slashCommand: string; sessionId: string }>()); - public readonly onDidCompleteSlashCommand = this._onDidCompleteSlashCommand.event; + private readonly _onDidSubmitSlashCommand = this._register(new Emitter<{ slashCommand: string; sessionId: string }>()); + public readonly onDidSubmitSlashCommand = this._onDidSubmitSlashCommand.event; constructor( @IStorageService private readonly storageService: IStorageService, @@ -446,6 +446,9 @@ export class ChatService extends Disposable implements IChatService { model.cancelRequest(request); }); + if (usedSlashCommand?.command) { + this._onDidSubmitSlashCommand.fire({ slashCommand: usedSlashCommand.command, sessionId: model.sessionId }); + } let rawResponse = await provider.provideReply({ session: model.session!, message: resolvedCommand }, progressCallback, token); if (token.isCancellationRequested) { return; @@ -475,15 +478,9 @@ export class ChatService extends Disposable implements IChatService { Promise.resolve(provider.provideFollowups(model.session!, CancellationToken.None)).then(followups => { model.setFollowups(request, withNullAsUndefined(followups)); model.completeResponse(request); - if (usedSlashCommand?.command) { - this._onDidCompleteSlashCommand.fire({ slashCommand: usedSlashCommand.command, sessionId: model.sessionId }); - } }); } else { model.completeResponse(request); - if (usedSlashCommand?.command) { - this._onDidCompleteSlashCommand.fire({ slashCommand: usedSlashCommand.command, sessionId: model.sessionId }); - } } } }); From ff81f6cb82c29590844e8d5fd07f498bbea7692e Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 10 Jul 2023 16:41:31 -0700 Subject: [PATCH 302/926] Remove un-needed use of native privates (#187518) For #186737 --- .../codeAction/browser/codeActionController.ts | 6 +++--- .../codeAction/browser/codeActionModel.ts | 10 +++++----- .../browser/webviewWorkbenchService.ts | 18 +++++++++--------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/vs/editor/contrib/codeAction/browser/codeActionController.ts b/src/vs/editor/contrib/codeAction/browser/codeActionController.ts index 384902ff004..5b8d6576b14 100644 --- a/src/vs/editor/contrib/codeAction/browser/codeActionController.ts +++ b/src/vs/editor/contrib/codeAction/browser/codeActionController.ts @@ -56,7 +56,7 @@ export class CodeActionController extends Disposable implements IEditorContribut private readonly _resolver: CodeActionKeybindingResolver; - #disposed = false; + private _disposed = false; constructor( editor: ICodeEditor, @@ -91,7 +91,7 @@ export class CodeActionController extends Disposable implements IEditorContribut } override dispose() { - this.#disposed = true; + this._disposed = true; super.dispose(); } @@ -150,7 +150,7 @@ export class CodeActionController extends Disposable implements IEditorContribut return; } - if (this.#disposed) { + if (this._disposed) { return; } diff --git a/src/vs/editor/contrib/codeAction/browser/codeActionModel.ts b/src/vs/editor/contrib/codeAction/browser/codeActionModel.ts index e36f94c275f..19963bfe57f 100644 --- a/src/vs/editor/contrib/codeAction/browser/codeActionModel.ts +++ b/src/vs/editor/contrib/codeAction/browser/codeActionModel.ts @@ -147,7 +147,7 @@ export class CodeActionModel extends Disposable { private readonly _onDidChangeState = this._register(new Emitter()); public readonly onDidChangeState = this._onDidChangeState.event; - #isDisposed = false; + private _disposed = false; constructor( private readonly _editor: ICodeEditor, @@ -167,17 +167,17 @@ export class CodeActionModel extends Disposable { } override dispose(): void { - if (this.#isDisposed) { + if (this._disposed) { return; } - this.#isDisposed = true; + this._disposed = true; super.dispose(); this.setState(CodeActionsState.Empty, true); } private _update(): void { - if (this.#isDisposed) { + if (this._disposed) { return; } @@ -228,7 +228,7 @@ export class CodeActionModel extends Disposable { this._state = newState; - if (!skipNotify && !this.#isDisposed) { + if (!skipNotify && !this._disposed) { this._onDidChangeState.fire(newState); } } diff --git a/src/vs/workbench/contrib/webviewPanel/browser/webviewWorkbenchService.ts b/src/vs/workbench/contrib/webviewPanel/browser/webviewWorkbenchService.ts index 263649a8b74..39750b798b2 100644 --- a/src/vs/workbench/contrib/webviewPanel/browser/webviewWorkbenchService.ts +++ b/src/vs/workbench/contrib/webviewPanel/browser/webviewWorkbenchService.ts @@ -120,8 +120,8 @@ function canRevive(reviver: WebviewResolver, webview: WebviewInput): boolean { export class LazilyResolvedWebviewEditorInput extends WebviewInput { - #resolved = false; - #resolvePromise?: CancelablePromise; + private _resolved = false; + private _resolvePromise?: CancelablePromise; constructor( init: WebviewInputInitInfo, @@ -133,17 +133,17 @@ export class LazilyResolvedWebviewEditorInput extends WebviewInput { override dispose() { super.dispose(); - this.#resolvePromise?.cancel(); - this.#resolvePromise = undefined; + this._resolvePromise?.cancel(); + this._resolvePromise = undefined; } @memoize public override async resolve() { - if (!this.#resolved) { - this.#resolved = true; - this.#resolvePromise = createCancelablePromise(token => this._webviewWorkbenchService.resolveWebview(this, token)); + if (!this._resolved) { + this._resolved = true; + this._resolvePromise = createCancelablePromise(token => this._webviewWorkbenchService.resolveWebview(this, token)); try { - await this.#resolvePromise; + await this._resolvePromise; } catch (e) { if (!isCancellationError(e)) { throw e; @@ -158,7 +158,7 @@ export class LazilyResolvedWebviewEditorInput extends WebviewInput { return; } - other.#resolved = this.#resolved; + other._resolved = this._resolved; return other; } } From b14044d28520339c787ad823fe0459fe35dcfac4 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 10 Jul 2023 17:07:10 -0700 Subject: [PATCH 303/926] part of #179684 --- .../contrib/tasks/browser/abstractTaskService.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index 0a5012a9c9a..9bb6a0deee6 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -1957,6 +1957,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } private async _getGroupedTasks(filter?: ITaskFilter): Promise { + await this._waitForAllSupportedExecutions; const type = filter?.type; const needsRecentTasksMigration = this._needsRecentTasksMigration(); await this._activateTaskProviders(filter?.type); @@ -2210,11 +2211,9 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return new Map(); } await this._waitForOneSupportedExecution; - if (runSource === TaskRunSource.Reconnect) { - await raceTimeout(this._waitForAllSupportedExecutions, 2000, () => { - this._logService.warn('Timed out waiting for all supported executions for task reconnection'); - }); - } + await raceTimeout(this._waitForAllSupportedExecutions, 2000, () => { + this._logService.warn('Timed out waiting for all supported executions'); + }); await this._whenTaskSystemReady; if (this._workspaceTasksPromise) { return this._workspaceTasksPromise; From b041b4edc4f1f7c191be7762703cff64be503d91 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 10 Jul 2023 17:07:51 -0700 Subject: [PATCH 304/926] tweak --- src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index 9bb6a0deee6..82abd3c3c0f 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -2210,7 +2210,6 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer if (!(await this._trust())) { return new Map(); } - await this._waitForOneSupportedExecution; await raceTimeout(this._waitForAllSupportedExecutions, 2000, () => { this._logService.warn('Timed out waiting for all supported executions'); }); From 492217cfd3d419f087d2a33fc40c8df66db5023c Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 10 Jul 2023 20:27:12 -0700 Subject: [PATCH 305/926] fix compile error --- src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index 82abd3c3c0f..04b21c59814 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -223,7 +223,6 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer protected _outputChannel: IOutputChannel; protected readonly _onDidStateChange: Emitter; - private _waitForOneSupportedExecution: Promise; private _waitForAllSupportedExecutions: Promise; private _onDidRegisterSupportedExecutions: Emitter = new Emitter(); private _onDidRegisterAllSupportedExecutions: Emitter = new Emitter(); @@ -335,9 +334,6 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer this._setPersistentTask(e.__task); } })); - this._waitForOneSupportedExecution = new Promise(resolve => { - once(this._onDidRegisterSupportedExecutions.event)(() => resolve()); - }); this._waitForAllSupportedExecutions = new Promise(resolve => { once(this._onDidRegisterAllSupportedExecutions.event)(() => resolve()); }); From 6243562032a9a168df33c1b4dc84d5b2abbcb22d Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Mon, 10 Jul 2023 21:48:30 -0700 Subject: [PATCH 306/926] Avoid restoring stale input value after clear (#187513) --- src/vs/workbench/contrib/chat/browser/chatViewPane.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/chat/browser/chatViewPane.ts b/src/vs/workbench/contrib/chat/browser/chatViewPane.ts index e3863e507b7..5f7a05375d9 100644 --- a/src/vs/workbench/contrib/chat/browser/chatViewPane.ts +++ b/src/vs/workbench/contrib/chat/browser/chatViewPane.ts @@ -119,6 +119,7 @@ export class ChatViewPane extends ViewPane implements IChatViewPane { if (this.widget.viewModel) { this.chatService.clearSession(this.widget.viewModel.sessionId); } + this.viewState.inputValue = ''; this.updateModel(); } From fee8338d67f8466e06f1c58781b487862195184c Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 11 Jul 2023 09:17:10 +0200 Subject: [PATCH 307/926] exclude config editing errors from telemetry --- .../services/configuration/common/configurationEditing.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/services/configuration/common/configurationEditing.ts b/src/vs/workbench/services/configuration/common/configurationEditing.ts index 699d388f340..04982ee6684 100644 --- a/src/vs/workbench/services/configuration/common/configurationEditing.ts +++ b/src/vs/workbench/services/configuration/common/configurationEditing.ts @@ -29,6 +29,7 @@ import { EditOperation } from 'vs/editor/common/core/editOperation'; import { Selection } from 'vs/editor/common/core/selection'; import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { ErrorNoTelemetry } from 'vs/base/common/errors'; export const enum ConfigurationEditingErrorCode { @@ -103,7 +104,7 @@ export const enum ConfigurationEditingErrorCode { ERROR_INTERNAL } -export class ConfigurationEditingError extends Error { +export class ConfigurationEditingError extends ErrorNoTelemetry { constructor(message: string, public code: ConfigurationEditingErrorCode) { super(message); } From e46d6c80790196293c3235cee5331414c2ee267d Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 11 Jul 2023 11:27:43 +0200 Subject: [PATCH 308/926] revert atomic write for user data (#187537) #187245 revert atomic write for user data --- .../common/extensionsProfileScannerService.ts | 5 ++--- .../extensionManagement/node/extensionManagementService.ts | 5 ++++- src/vs/platform/userData/common/fileUserDataProvider.ts | 6 +----- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/vs/platform/extensionManagement/common/extensionsProfileScannerService.ts b/src/vs/platform/extensionManagement/common/extensionsProfileScannerService.ts index 925b9dd905c..30e749ac312 100644 --- a/src/vs/platform/extensionManagement/common/extensionsProfileScannerService.ts +++ b/src/vs/platform/extensionManagement/common/extensionsProfileScannerService.ts @@ -12,7 +12,7 @@ import { URI, UriComponents } from 'vs/base/common/uri'; import { Metadata, isIExtensionIdentifier } from 'vs/platform/extensionManagement/common/extensionManagement'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IExtension, IExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; -import { FileOperationResult, IFileService, hasFileAtomicWriteCapability, toFileOperationResult } from 'vs/platform/files/common/files'; +import { FileOperationResult, IFileService, toFileOperationResult } from 'vs/platform/files/common/files'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; @@ -290,8 +290,7 @@ export abstract class AbstractExtensionsProfileScannerService extends Disposable relativeLocation: this.toRelativePath(e.location), metadata: e.metadata })); - const fsp = this.fileService.getProvider(file.scheme); - await this.fileService.writeFile(file, VSBuffer.fromString(JSON.stringify(storedProfileExtensions)), fsp && hasFileAtomicWriteCapability(fsp) ? { atomic: { postfix: '.vsctmp' } } : undefined); + await this.fileService.writeFile(file, VSBuffer.fromString(JSON.stringify(storedProfileExtensions))); } return extensions; diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index 49ebc362edf..9ad1f4c6759 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -425,6 +425,7 @@ export class ExtensionsScanner extends Disposable { @IFileService private readonly fileService: IFileService, @IExtensionsScannerService private readonly extensionsScannerService: IExtensionsScannerService, @IExtensionsProfileScannerService private readonly extensionsProfileScannerService: IExtensionsProfileScannerService, + @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, @ILogService private readonly logService: ILogService, ) { super(); @@ -582,7 +583,9 @@ export class ExtensionsScanner extends Disposable { private async deleteExtensionFromLocation(id: string, location: URI, type: string): Promise { this.logService.trace(`Deleting ${type} extension from disk`, id, location.fsPath); - await this.fileService.del(location, { recursive: true, atomic: { postfix: `.${hash(generateUuid()).toString(16)}${DELETED_FOLDER_POSTFIX}` } }); + const renamedLocation = this.uriIdentityService.extUri.joinPath(this.uriIdentityService.extUri.dirname(location), `${this.uriIdentityService.extUri.basename(location)}.${hash(generateUuid()).toString(16)}${DELETED_FOLDER_POSTFIX}`); + await this.rename({ id }, location.fsPath, renamedLocation.fsPath, Date.now() + (2 * 60 * 1000) /* Retry for 2 minutes */); + await this.fileService.del(renamedLocation, { recursive: true }); this.logService.info(`Deleted ${type} extension from disk`, id, location.fsPath); } diff --git a/src/vs/platform/userData/common/fileUserDataProvider.ts b/src/vs/platform/userData/common/fileUserDataProvider.ts index 9382a676acb..539c8011f3e 100644 --- a/src/vs/platform/userData/common/fileUserDataProvider.ts +++ b/src/vs/platform/userData/common/fileUserDataProvider.ts @@ -4,14 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import { Event, Emitter } from 'vs/base/common/event'; import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { IFileSystemProviderWithFileReadWriteCapability, IFileChange, IWatchOptions, IStat, IFileOverwriteOptions, FileType, IFileWriteOptions, IFileDeleteOptions, FileSystemProviderCapabilities, IFileSystemProviderWithFileReadStreamCapability, IFileReadStreamOptions, IFileSystemProviderWithFileAtomicReadCapability, IFileSystemProviderWithFileFolderCopyCapability, hasFileFolderCopyCapability, hasFileAtomicWriteCapability } from 'vs/platform/files/common/files'; +import { IFileSystemProviderWithFileReadWriteCapability, IFileChange, IWatchOptions, IStat, IFileOverwriteOptions, FileType, IFileWriteOptions, IFileDeleteOptions, FileSystemProviderCapabilities, IFileSystemProviderWithFileReadStreamCapability, IFileReadStreamOptions, IFileSystemProviderWithFileAtomicReadCapability, IFileSystemProviderWithFileFolderCopyCapability, hasFileFolderCopyCapability } from 'vs/platform/files/common/files'; import { URI } from 'vs/base/common/uri'; import { CancellationToken } from 'vs/base/common/cancellation'; import { newWriteableStream, ReadableStreamEvents } from 'vs/base/common/stream'; import { ILogService } from 'vs/platform/log/common/log'; import { TernarySearchTree } from 'vs/base/common/ternarySearchTree'; import { VSBuffer } from 'vs/base/common/buffer'; -import { isObject } from 'vs/base/common/types'; /** * This is a wrapper on top of the local filesystem provider which will @@ -86,9 +85,6 @@ export class FileUserDataProvider extends Disposable implements } writeFile(resource: URI, content: Uint8Array, opts: IFileWriteOptions): Promise { - if (!isObject(opts.atomic) && hasFileAtomicWriteCapability(this.fileSystemProvider)) { - opts = { ...opts, atomic: { postfix: '.vsctmp' } }; - } return this.fileSystemProvider.writeFile(this.toFileSystemResource(resource), content, opts); } From b457942f6eeb4a426cc32c8abff21f9f32c41cfa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Jul 2023 11:35:24 +0200 Subject: [PATCH 309/926] Bump semver from 5.7.1 to 5.7.2 in /build (#187548) Bumps [semver](https://github.com/npm/node-semver) from 5.7.1 to 5.7.2. - [Release notes](https://github.com/npm/node-semver/releases) - [Changelog](https://github.com/npm/node-semver/blob/v5.7.2/CHANGELOG.md) - [Commits](https://github.com/npm/node-semver/compare/v5.7.1...v5.7.2) --- updated-dependencies: - dependency-name: semver dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build/yarn.lock | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/build/yarn.lock b/build/yarn.lock index 3c3bce1fbc5..e5bc497260d 100644 --- a/build/yarn.lock +++ b/build/yarn.lock @@ -2559,19 +2559,19 @@ semver-compare@^1.0.0: integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w= semver@^5.1.0, semver@^5.4.1: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== semver@^6.2.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + version "6.3.1" + 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.5, semver@^7.3.8: - version "7.5.3" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.3.tgz#161ce8c2c6b4b3bdca6caadc9fa3317a4c4fe88e" - integrity sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ== + 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== dependencies: lru-cache "^6.0.0" From 14ef7c1eb27f473cdb0109597ec135d8a3bee1e0 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Tue, 11 Jul 2023 11:50:47 +0200 Subject: [PATCH 310/926] adding the persistance mechanism which lasts during vscode duration --- src/vs/editor/contrib/hover/browser/contentHover.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/vs/editor/contrib/hover/browser/contentHover.ts b/src/vs/editor/contrib/hover/browser/contentHover.ts index f03cf308b61..dc2e6c00983 100644 --- a/src/vs/editor/contrib/hover/browser/contentHover.ts +++ b/src/vs/editor/contrib/hover/browser/contentHover.ts @@ -459,6 +459,8 @@ const CONTAINER_HEIGHT_PADDING = 6; export class ContentHoverWidget extends ResizableContentWidget { public static ID = 'editor.contrib.resizableContentHoverWidget'; + private static _lastHeight: number = 0; + private static _lastWidth: number = 0; private _visibleData: ContentHoverVisibleData | undefined; private _positionPreference: ContentWidgetPositionPreference | undefined; @@ -582,6 +584,8 @@ export class ContentHoverWidget extends ResizableContentWidget { } protected override _resize(size: dom.Dimension): void { + ContentHoverWidget._lastHeight = size.height; + ContentHoverWidget._lastWidth = size.width; this._setAdjustedHoverWidgetDimensions(size); this._resizableNode.layout(size.height, size.width); this._setResizableNodeMaxDimensions(); @@ -654,12 +658,13 @@ export class ContentHoverWidget extends ResizableContentWidget { } private _layout(): void { - const height = Math.max(this._editor.getLayoutInfo().height / 4, 250); + const height = Math.max(this._editor.getLayoutInfo().height / 4, 250, ContentHoverWidget._lastHeight); + const width = Math.max(this._editor.getLayoutInfo().width * 0.66, 500, ContentHoverWidget._lastWidth); const { fontSize, lineHeight } = this._editor.getOption(EditorOption.fontInfo); const contentsDomNode = this._hover.contentsDomNode; contentsDomNode.style.fontSize = `${fontSize}px`; contentsDomNode.style.lineHeight = `${lineHeight / fontSize}`; - this._setContentsDomNodeMaxDimensions(Math.max(this._editor.getLayoutInfo().width * 0.66, 500), height); + this._setContentsDomNodeMaxDimensions(width, height); } private _updateFont(): void { @@ -680,8 +685,8 @@ export class ContentHoverWidget extends ResizableContentWidget { } private _updateContentsDomNodeMaxDimensions() { - const width = Math.max(this._editor.getLayoutInfo().width * 0.66, 500); - const height = Math.max(this._editor.getLayoutInfo().height / 4, 250); + const width = Math.max(this._editor.getLayoutInfo().width * 0.66, 500, ContentHoverWidget._lastWidth); + const height = Math.max(this._editor.getLayoutInfo().height / 4, 250, ContentHoverWidget._lastHeight); this._setContentsDomNodeMaxDimensions(width, height); } From 9b6849760b4c9ec1c30e32d9dc62cf58c770b6c4 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Tue, 11 Jul 2023 12:56:51 +0200 Subject: [PATCH 311/926] doing optimization --- .../stickyScroll/browser/stickyScrollWidget.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts index 7b4a946f5f3..0664148b786 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts @@ -21,6 +21,12 @@ export class StickyScrollWidgetState { readonly lineNumbers: number[], readonly lastLineRelativePosition: number ) { } + + public equals(other: StickyScrollWidgetState | undefined): boolean { + return !!other && this.lastLineRelativePosition === other.lastLineRelativePosition + && this.lineNumbers.length === other.lineNumbers.length + && this.lineNumbers.every((lineNumber, index) => lineNumber === other.lineNumbers[index]); + } } const _ttPolicy = createTrustedTypesPolicy('stickyScrollViewLayer', { createHTML: value => value }); @@ -35,6 +41,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { private _lastLineRelativePosition: number = 0; private _hoverOnLine: number = -1; private _hoverOnColumn: number = -1; + private _state: StickyScrollWidgetState | undefined; constructor( private readonly _editor: ICodeEditor @@ -68,6 +75,10 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { } setState(state: StickyScrollWidgetState): void { + if (state.equals(this._state)) { + return; + } + this._state = state; dom.clearNode(this._rootDomNode); this._disposableStore.clear(); this._lineNumbers.length = 0; From 79c022072e2ba07d189a1deed79226129caaa77a Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 11 Jul 2023 13:45:19 +0200 Subject: [PATCH 312/926] dispose properly (#187569) --- .../extensionsWorkbenchService.test.ts | 21 ++++++++++++------- .../test/browser/configurationEditing.test.ts | 3 ++- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/test/electron-sandbox/extensionsWorkbenchService.test.ts b/src/vs/workbench/contrib/extensions/test/electron-sandbox/extensionsWorkbenchService.test.ts index 8c4d8213d4e..700a07c66bf 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-sandbox/extensionsWorkbenchService.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-sandbox/extensionsWorkbenchService.test.ts @@ -48,11 +48,14 @@ import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKe import { platform } from 'vs/base/common/platform'; import { arch } from 'vs/base/common/process'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; suite('ExtensionsWorkbenchServiceTest', () => { let instantiationService: TestInstantiationService; let testObject: IExtensionsWorkbenchService; + const suiteDisposables = new DisposableStore(); + let testDisposables: DisposableStore = new DisposableStore(); let installEvent: Emitter, didInstallEvent: Emitter, @@ -60,10 +63,11 @@ suite('ExtensionsWorkbenchServiceTest', () => { didUninstallEvent: Emitter; suiteSetup(() => { - installEvent = new Emitter(); - didInstallEvent = new Emitter(); - uninstallEvent = new Emitter(); - didUninstallEvent = new Emitter(); + suiteDisposables.add(toDisposable(() => sinon.restore())); + installEvent = suiteDisposables.add(new Emitter()); + didInstallEvent = suiteDisposables.add(new Emitter()); + uninstallEvent = suiteDisposables.add(new Emitter()); + didUninstallEvent = suiteDisposables.add(new Emitter()); instantiationService = new TestInstantiationService(); instantiationService.stub(ITelemetryService, NullTelemetryService); @@ -126,7 +130,10 @@ suite('ExtensionsWorkbenchServiceTest', () => { }); }); + suiteTeardown(() => suiteDisposables.dispose()); + setup(async () => { + testDisposables = new DisposableStore(); instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', []); instantiationService.stub(IExtensionGalleryService, 'isEnabled', true); instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage()); @@ -135,9 +142,7 @@ suite('ExtensionsWorkbenchServiceTest', () => { (instantiationService.get(IWorkbenchExtensionEnablementService)).reset(); }); - teardown(() => { - (testObject).dispose(); - }); + teardown(() => testDisposables.dispose()); test('test gallery extension', async () => { const expected = aGalleryExtension('expectedName', { @@ -1412,7 +1417,7 @@ suite('ExtensionsWorkbenchServiceTest', () => { }); async function aWorkbenchService(): Promise { - const workbenchService: ExtensionsWorkbenchService = instantiationService.createInstance(ExtensionsWorkbenchService); + const workbenchService: ExtensionsWorkbenchService = testDisposables.add(instantiationService.createInstance(ExtensionsWorkbenchService)); await workbenchService.queryLocal(); return workbenchService; } diff --git a/src/vs/workbench/services/configuration/test/browser/configurationEditing.test.ts b/src/vs/workbench/services/configuration/test/browser/configurationEditing.test.ts index 6c8d92f9a0f..9a87d174025 100644 --- a/src/vs/workbench/services/configuration/test/browser/configurationEditing.test.ts +++ b/src/vs/workbench/services/configuration/test/browser/configurationEditing.test.ts @@ -33,7 +33,7 @@ import { IFileService } from 'vs/platform/files/common/files'; import { KeybindingsEditingService, IKeybindingEditingService } from 'vs/workbench/services/keybinding/common/keybindingEditing'; import { FileUserDataProvider } from 'vs/platform/userData/common/fileUserDataProvider'; import { UriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentityService'; -import { DisposableStore } from 'vs/base/common/lifecycle'; +import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider'; import { joinPath } from 'vs/base/common/resources'; import { VSBuffer } from 'vs/base/common/buffer'; @@ -98,6 +98,7 @@ suite('ConfigurationEditing', () => { }); setup(async () => { + disposables.add(toDisposable(() => sinon.restore())); const logService = new NullLogService(); fileService = disposables.add(new FileService(logService)); const fileSystemProvider = disposables.add(new InMemoryFileSystemProvider()); From f0db763ba94e8250c17c37b791b6b01bbb85a446 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Tue, 11 Jul 2023 14:23:55 +0200 Subject: [PATCH 313/926] Dispose instances of test instantiation service (#187482) * Dispose instances of test instantiation service This will call `sinon.restore()` and prevent a memory leak Part of #187471 * Fix interfering terminal tests * Remove `async` from terminal test suite * Fix `any` --- src/vs/editor/test/browser/testCodeEditor.ts | 2 +- .../textResourceConfigurationService.test.ts | 7 +++- .../test/browser/contextkey.test.ts | 5 ++- .../extensionsProfileScannerService.test.ts | 2 +- .../node/extensionsScannerService.test.ts | 2 +- .../test/common/instantiationServiceMock.ts | 10 +++-- .../test/common/telemetryLogAppender.test.ts | 2 + .../terminal/test/common/requestStore.test.ts | 4 ++ .../browser/extHostAuthentication.test.ts | 4 ++ .../browser/extHostLanguageFeatures.test.ts | 4 +- .../api/test/browser/extHostTreeViews.test.ts | 7 +++- .../browser/mainThreadConfiguration.test.ts | 4 ++ .../chat/test/common/chatModel.test.ts | 2 +- .../chat/test/common/chatService.test.ts | 4 ++ .../debug/test/browser/breakpoints.test.ts | 1 + .../browser/debugConfigurationManager.test.ts | 3 +- .../test/electron-sandbox/extension.test.ts | 4 ++ .../extensionRecommendationsService.test.ts | 4 ++ .../extensionsActions.test.ts | 2 +- .../electron-sandbox/extensionsViews.test.ts | 4 ++ .../common/externalUriOpenerService.test.ts | 4 ++ .../test/browser/testNotebookEditor.ts | 2 +- .../search/test/browser/searchActions.test.ts | 4 ++ .../browser/searchNotebookHelpers.test.ts | 5 +++ .../search/test/browser/searchResult.test.ts | 4 +- .../search/test/browser/searchViewlet.test.ts | 4 ++ .../test/browser/taskTerminalStatus.test.ts | 3 ++ .../test/common/taskConfiguration.test.ts | 3 ++ .../commandDetectionCapability.test.ts | 7 +++- .../test/browser/terminalInstance.test.ts | 4 ++ .../browser/terminalInstanceService.test.ts | 4 ++ .../terminalProfileService.integrationTest.ts | 3 ++ .../test/browser/terminalService.test.ts | 4 ++ .../browser/xterm/decorationAddon.test.ts | 38 ++++++++++--------- .../test/browser/xterm/xtermTerminal.test.ts | 4 ++ .../common/environmentVariableService.test.ts | 4 ++ .../terminal/test/common/history.test.ts | 20 ++++++++++ .../test/browser/bufferContentTracker.test.ts | 4 +- .../test/browser/terminalLinkManager.test.ts | 4 ++ .../test/browser/terminalLinkOpeners.test.ts | 4 ++ .../browser/terminalLocalLinkDetector.test.ts | 4 ++ .../terminalMultiLineLinkDetector.test.ts | 4 ++ .../browser/terminalUriLinkDetector.test.ts | 7 +++- .../browser/terminalWordLinkDetector.test.ts | 7 +++- .../test/browser/quickFixAddon.test.ts | 6 ++- .../fileDialogService.test.ts | 2 +- .../extensionEnablementService.test.ts | 1 + .../test/browser/extensionService.test.ts | 4 +- ...extensionManifestPropertiesService.test.ts | 5 ++- .../browser/keybindingsEditorModel.test.ts | 4 ++ .../search/test/browser/queryBuilder.test.ts | 4 ++ .../test/browser/domActivityTracker.test.ts | 5 ++- .../browser/viewDescriptorService.test.ts | 2 +- .../test/common/workspaceTrust.test.ts | 4 ++ .../parts/editor/editorGroupModel.test.ts | 7 ++++ .../browser/parts/editor/editorModel.test.ts | 4 ++ .../test/browser/workbenchTestServices.ts | 2 +- 57 files changed, 233 insertions(+), 45 deletions(-) diff --git a/src/vs/editor/test/browser/testCodeEditor.ts b/src/vs/editor/test/browser/testCodeEditor.ts index 957920ebe67..acca9e7cde3 100644 --- a/src/vs/editor/test/browser/testCodeEditor.ts +++ b/src/vs/editor/test/browser/testCodeEditor.ts @@ -201,7 +201,7 @@ export function createCodeEditorServices(disposables: DisposableStore, services: define(ILanguageFeatureDebounceService, LanguageFeatureDebounceService); define(ILanguageFeaturesService, LanguageFeaturesService); - const instantiationService = new TestInstantiationService(services, true); + const instantiationService = disposables.add(new TestInstantiationService(services, true)); disposables.add(toDisposable(() => { for (const id of serviceIdentifiers) { const instanceOrDescriptor = services.get(id); diff --git a/src/vs/editor/test/common/services/textResourceConfigurationService.test.ts b/src/vs/editor/test/common/services/textResourceConfigurationService.test.ts index 0e96cff2069..faaf6b520ec 100644 --- a/src/vs/editor/test/common/services/textResourceConfigurationService.test.ts +++ b/src/vs/editor/test/common/services/textResourceConfigurationService.test.ts @@ -15,6 +15,7 @@ import { URI } from 'vs/base/common/uri'; suite('TextResourceConfigurationService - Update', () => { + let instantiationService: TestInstantiationService; let configurationValue: IConfigurationValue = {}; let updateArgs: any[]; const configurationService = new class extends TestConfigurationService { @@ -30,13 +31,17 @@ suite('TextResourceConfigurationService - Update', () => { let testObject: TextResourceConfigurationService; setup(() => { - const instantiationService = new TestInstantiationService(); + instantiationService = new TestInstantiationService(); instantiationService.stub(IModelService, >{ getModel() { return null; } }); instantiationService.stub(ILanguageService, >{ guessLanguageIdByFilepathOrFirstLine() { return language; } }); instantiationService.stub(IConfigurationService, configurationService); testObject = instantiationService.createInstance(TextResourceConfigurationService); }); + teardown(() => { + instantiationService.dispose(); + }); + test('updateValue writes without target and overrides when no language is defined', async () => { const resource = URI.file('someFile'); await testObject.updateValue(resource, 'a', 'b'); diff --git a/src/vs/platform/contextkey/test/browser/contextkey.test.ts b/src/vs/platform/contextkey/test/browser/contextkey.test.ts index 12cc7cc065d..429f76d42c4 100644 --- a/src/vs/platform/contextkey/test/browser/contextkey.test.ts +++ b/src/vs/platform/contextkey/test/browser/contextkey.test.ts @@ -83,7 +83,7 @@ suite('ContextKeyService', () => { const disposables = new DisposableStore(); const configurationService: IConfigurationService = new TestConfigurationService(); const contextKeyService: IContextKeyService = disposables.add(new ContextKeyService(configurationService)); - const instantiationService = new TestInstantiationService(new ServiceCollection( + const instantiationService = disposables.add(new TestInstantiationService(new ServiceCollection( [IConfigurationService, configurationService], [IContextKeyService, contextKeyService], [ITelemetryService, new class extends mock() { @@ -91,7 +91,7 @@ suite('ContextKeyService', () => { // } }] - )); + ))); const uri = URI.parse('test://abc'); contextKeyService.createKey('notebookCellResource', undefined).set(uri.toString()); @@ -99,6 +99,7 @@ suite('ContextKeyService', () => { const expr = ContextKeyExpr.in('notebookCellResource', 'jupyter.runByLineCells'); assert.deepStrictEqual(contextKeyService.contextMatchesRules(expr), true); + disposables.dispose(); }); test('suppress update event from parent when one key is overridden by child', () => { diff --git a/src/vs/platform/extensionManagement/test/common/extensionsProfileScannerService.test.ts b/src/vs/platform/extensionManagement/test/common/extensionsProfileScannerService.test.ts index 4b7ec18e8f4..2dcd920d3e2 100644 --- a/src/vs/platform/extensionManagement/test/common/extensionsProfileScannerService.test.ts +++ b/src/vs/platform/extensionManagement/test/common/extensionsProfileScannerService.test.ts @@ -33,7 +33,7 @@ suite('ExtensionsProfileScannerService', () => { let instantiationService: TestInstantiationService; setup(async () => { - instantiationService = new TestInstantiationService(); + instantiationService = disposables.add(new TestInstantiationService()); const logService = new NullLogService(); const fileService = disposables.add(new FileService(logService)); const fileSystemProvider = disposables.add(new InMemoryFileSystemProvider()); diff --git a/src/vs/platform/extensionManagement/test/node/extensionsScannerService.test.ts b/src/vs/platform/extensionManagement/test/node/extensionsScannerService.test.ts index 2e5d07d3105..6834bb4c10f 100644 --- a/src/vs/platform/extensionManagement/test/node/extensionsScannerService.test.ts +++ b/src/vs/platform/extensionManagement/test/node/extensionsScannerService.test.ts @@ -60,7 +60,7 @@ suite('NativeExtensionsScanerService Test', () => { setup(async () => { translations = {}; - instantiationService = new TestInstantiationService(); + instantiationService = disposables.add(new TestInstantiationService()); const logService = new NullLogService(); const fileService = disposables.add(new FileService(logService)); const fileSystemProvider = disposables.add(new InMemoryFileSystemProvider()); diff --git a/src/vs/platform/instantiation/test/common/instantiationServiceMock.ts b/src/vs/platform/instantiation/test/common/instantiationServiceMock.ts index 78e41ad9a45..a639165802d 100644 --- a/src/vs/platform/instantiation/test/common/instantiationServiceMock.ts +++ b/src/vs/platform/instantiation/test/common/instantiationServiceMock.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as sinon from 'sinon'; -import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; import { InstantiationService, Trace } from 'vs/platform/instantiation/common/instantiationService'; @@ -17,7 +17,7 @@ interface IServiceMock { const isSinonSpyLike = (fn: Function): fn is sinon.SinonSpy => fn && 'callCount' in fn; -export class TestInstantiationService extends InstantiationService { +export class TestInstantiationService extends InstantiationService implements IDisposable { private _servciesMap: Map, any>; @@ -129,6 +129,10 @@ export class TestInstantiationService extends InstantiationService { override createChild(services: ServiceCollection): TestInstantiationService { return new TestInstantiationService(services, false, this); } + + dispose() { + sinon.restore(); + } } interface SinonOptions { @@ -157,7 +161,7 @@ export function createServices(disposables: DisposableStore, services: ServiceId define(id, ctor); } - const instantiationService = new TestInstantiationService(serviceCollection, true); + const instantiationService = disposables.add(new TestInstantiationService(serviceCollection, true)); disposables.add(toDisposable(() => { for (const id of serviceIdentifiers) { const instanceOrDescriptor = serviceCollection.get(id); diff --git a/src/vs/platform/telemetry/test/common/telemetryLogAppender.test.ts b/src/vs/platform/telemetry/test/common/telemetryLogAppender.test.ts index 55d65519cfb..70241f677dd 100644 --- a/src/vs/platform/telemetry/test/common/telemetryLogAppender.test.ts +++ b/src/vs/platform/telemetry/test/common/telemetryLogAppender.test.ts @@ -93,6 +93,7 @@ suite('TelemetryLogAdapter', () => { const testObject = new TelemetryLogAppender(new NullLogService(), testLoggerService, testInstantiationService.stub(IEnvironmentService, {}), testInstantiationService.stub(IProductService, {})); testObject.log('testEvent', { hello: 'world', isTrue: true, numberBetween1And3: 2 }); assert.strictEqual(testLoggerService.createLogger().logs.length, 2); + testInstantiationService.dispose(); }); test('Log Telemetry if log level is trace', async () => { @@ -108,5 +109,6 @@ suite('TelemetryLogAdapter', () => { isTrue: 1, numberBetween1And3: 2 } }])); + testInstantiationService.dispose(); }); }); diff --git a/src/vs/platform/terminal/test/common/requestStore.test.ts b/src/vs/platform/terminal/test/common/requestStore.test.ts index 13ff93b7076..377e878f955 100644 --- a/src/vs/platform/terminal/test/common/requestStore.test.ts +++ b/src/vs/platform/terminal/test/common/requestStore.test.ts @@ -17,6 +17,10 @@ suite('RequestStore', () => { instantiationService.stub(ILogService, new LogService(new ConsoleLogger())); }); + teardown(() => { + instantiationService.dispose(); + }); + test('should resolve requests', async () => { const store: RequestStore<{ data: string }, { arg: string }> = instantiationService.createInstance(RequestStore<{ data: string }, { arg: string }>, undefined); let eventArgs: { requestId: number; arg: string } | undefined; diff --git a/src/vs/workbench/api/test/browser/extHostAuthentication.test.ts b/src/vs/workbench/api/test/browser/extHostAuthentication.test.ts index 5cf3459d52e..87266ef8b62 100644 --- a/src/vs/workbench/api/test/browser/extHostAuthentication.test.ts +++ b/src/vs/workbench/api/test/browser/extHostAuthentication.test.ts @@ -127,6 +127,10 @@ suite('ExtHostAuthentication', () => { { supportsMultipleAccounts: true })); }); + suiteTeardown(() => { + instantiationService.dispose(); + }); + teardown(() => { disposables.dispose(); }); diff --git a/src/vs/workbench/api/test/browser/extHostLanguageFeatures.test.ts b/src/vs/workbench/api/test/browser/extHostLanguageFeatures.test.ts index 35cf0c1d087..c0f47b0bc85 100644 --- a/src/vs/workbench/api/test/browser/extHostLanguageFeatures.test.ts +++ b/src/vs/workbench/api/test/browser/extHostLanguageFeatures.test.ts @@ -67,6 +67,7 @@ suite('ExtHostLanguageFeatures', function () { let rpcProtocol: TestRPCProtocol; let languageFeaturesService: ILanguageFeaturesService; let originalErrorHandler: (e: any) => any; + let instantiationService: TestInstantiationService; suiteSetup(() => { @@ -87,7 +88,7 @@ suite('ExtHostLanguageFeatures', function () { // Use IInstantiationService to get typechecking when instantiating let inst: IInstantiationService; { - const instantiationService = new TestInstantiationService(); + instantiationService = new TestInstantiationService(); instantiationService.stub(IMarkerService, MarkerService); instantiationService.set(ILanguageFeaturesService, languageFeaturesService); instantiationService.set(IUriIdentityService, new class extends mock() { @@ -140,6 +141,7 @@ suite('ExtHostLanguageFeatures', function () { setUnexpectedErrorHandler(originalErrorHandler); model.dispose(); mainThread.dispose(); + instantiationService.dispose(); }); teardown(() => { diff --git a/src/vs/workbench/api/test/browser/extHostTreeViews.test.ts b/src/vs/workbench/api/test/browser/extHostTreeViews.test.ts index 853cf088729..df4903ee04a 100644 --- a/src/vs/workbench/api/test/browser/extHostTreeViews.test.ts +++ b/src/vs/workbench/api/test/browser/extHostTreeViews.test.ts @@ -50,6 +50,7 @@ suite('ExtHostTreeView', function () { let tree: { [key: string]: any }; let labels: { [key: string]: string }; let nodes: { [key: string]: { key: string } }; + let instantiationService: TestInstantiationService; setup(() => { tree = { @@ -70,7 +71,7 @@ suite('ExtHostTreeView', function () { // Use IInstantiationService to get typechecking when instantiating let inst: IInstantiationService; { - const instantiationService = new TestInstantiationService(); + instantiationService = new TestInstantiationService(); inst = instantiationService; } @@ -94,6 +95,10 @@ suite('ExtHostTreeView', function () { return loadCompleteTree('testNodeTreeProvider'); }); + teardown(() => { + instantiationService.dispose(); + }); + test('construct node tree', () => { return testObject.$getChildren('testNodeTreeProvider') .then(elements => { diff --git a/src/vs/workbench/api/test/browser/mainThreadConfiguration.test.ts b/src/vs/workbench/api/test/browser/mainThreadConfiguration.test.ts index 46462e2f5b0..a3a8e47755c 100644 --- a/src/vs/workbench/api/test/browser/mainThreadConfiguration.test.ts +++ b/src/vs/workbench/api/test/browser/mainThreadConfiguration.test.ts @@ -59,6 +59,10 @@ suite('MainThreadConfiguration', function () { }); }); + teardown(() => { + instantiationService.dispose(); + }); + test('update resource configuration without configuration target defaults to workspace in multi root workspace when no resource is provided', function () { instantiationService.stub(IWorkspaceContextService, { getWorkbenchState: () => WorkbenchState.WORKSPACE }); const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(proxy)); diff --git a/src/vs/workbench/contrib/chat/test/common/chatModel.test.ts b/src/vs/workbench/contrib/chat/test/common/chatModel.test.ts index 0213accd91d..ea7cd3617a3 100644 --- a/src/vs/workbench/contrib/chat/test/common/chatModel.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/chatModel.test.ts @@ -19,7 +19,7 @@ suite('ChatModel', () => { let instantiationService: TestInstantiationService; suiteSetup(async () => { - instantiationService = new TestInstantiationService(); + instantiationService = testDisposables.add(new TestInstantiationService()); instantiationService.stub(IStorageService, new TestStorageService()); instantiationService.stub(ILogService, new NullLogService()); instantiationService.stub(IExtensionService, new TestExtensionService()); diff --git a/src/vs/workbench/contrib/chat/test/common/chatService.test.ts b/src/vs/workbench/contrib/chat/test/common/chatService.test.ts index 9f0c92de41c..1960fbb4e63 100644 --- a/src/vs/workbench/contrib/chat/test/common/chatService.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/chatService.test.ts @@ -71,6 +71,10 @@ suite('Chat', () => { instantiationService.stub(IWorkspaceContextService, new TestContextService()); }); + suiteTeardown(() => { + instantiationService.dispose(); + }); + teardown(() => { testDisposables.clear(); }); diff --git a/src/vs/workbench/contrib/debug/test/browser/breakpoints.test.ts b/src/vs/workbench/contrib/debug/test/browser/breakpoints.test.ts index c3d4af019fa..e5db72a8eaa 100644 --- a/src/vs/workbench/contrib/debug/test/browser/breakpoints.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/breakpoints.test.ts @@ -430,5 +430,6 @@ suite('Debug - Breakpoints', () => { assert.strictEqual(decorations[0].options.overviewRuler, null); textModel.dispose(); + instantiationService.dispose(); }); }); diff --git a/src/vs/workbench/contrib/debug/test/browser/debugConfigurationManager.test.ts b/src/vs/workbench/contrib/debug/test/browser/debugConfigurationManager.test.ts index 20d39ead7cc..287c489a781 100644 --- a/src/vs/workbench/contrib/debug/test/browser/debugConfigurationManager.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/debugConfigurationManager.test.ts @@ -48,12 +48,13 @@ suite('debugConfigurationManager', () => { const configurationService = new TestConfigurationService(); setup(() => { const fileService = disposables.add(new FileService(new NullLogService())); + const instantiationService = disposables.add(new TestInstantiationService(new ServiceCollection([IPreferencesService, preferencesService], [IConfigurationService, configurationService]))); _debugConfigurationManager = new ConfigurationManager( adapterManager, new TestContextService(), configurationService, new TestQuickInputService(), - new TestInstantiationService(new ServiceCollection([IPreferencesService, preferencesService], [IConfigurationService, configurationService])), + instantiationService, new TestStorageService(), new TestExtensionService(), new TestHistoryService(), diff --git a/src/vs/workbench/contrib/extensions/test/electron-sandbox/extension.test.ts b/src/vs/workbench/contrib/extensions/test/electron-sandbox/extension.test.ts index ca40188231b..320aa5f551f 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-sandbox/extension.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-sandbox/extension.test.ts @@ -23,6 +23,10 @@ suite('Extension Test', () => { instantiationService.stub(IProductService, >{ quality: 'insiders' }); }); + teardown(() => { + instantiationService.dispose(); + }); + test('extension is not outdated when there is no local and gallery', () => { const extension = instantiationService.createInstance(Extension, () => ExtensionState.Installed, () => undefined, undefined, undefined, undefined); assert.strictEqual(extension.outdated, false); diff --git a/src/vs/workbench/contrib/extensions/test/electron-sandbox/extensionRecommendationsService.test.ts b/src/vs/workbench/contrib/extensions/test/electron-sandbox/extensionRecommendationsService.test.ts index 49f617e7019..d19b0fedbdd 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-sandbox/extensionRecommendationsService.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-sandbox/extensionRecommendationsService.test.ts @@ -309,6 +309,10 @@ suite('ExtensionRecommendationsService Test', () => { teardown(() => (testObject).dispose()); + suiteTeardown(() => { + instantiationService.dispose(); + }); + function setUpFolderWorkspace(folderName: string, recommendedExtensions: string[], ignoredRecommendations: string[] = []): Promise { return setUpFolder(folderName, recommendedExtensions, ignoredRecommendations); } diff --git a/src/vs/workbench/contrib/extensions/test/electron-sandbox/extensionsActions.test.ts b/src/vs/workbench/contrib/extensions/test/electron-sandbox/extensionsActions.test.ts index 69a69419c71..afbbf237cf9 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-sandbox/extensionsActions.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-sandbox/extensionsActions.test.ts @@ -72,7 +72,7 @@ function setupTest() { uninstallEvent = new Emitter(); didUninstallEvent = new Emitter(); - instantiationService = new TestInstantiationService(); + instantiationService = disposables.add(new TestInstantiationService()); instantiationService.stub(IEnvironmentService, TestEnvironmentService); instantiationService.stub(IWorkbenchEnvironmentService, TestEnvironmentService); diff --git a/src/vs/workbench/contrib/extensions/test/electron-sandbox/extensionsViews.test.ts b/src/vs/workbench/contrib/extensions/test/electron-sandbox/extensionsViews.test.ts index 048f19275d5..e5a665e70dd 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-sandbox/extensionsViews.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-sandbox/extensionsViews.test.ts @@ -204,6 +204,10 @@ suite('ExtensionsViews Tests', () => { testableView.dispose(); }); + suiteTeardown(() => { + instantiationService.dispose(); + }); + test('Test query types', () => { assert.strictEqual(ExtensionsListView.isBuiltInExtensionsQuery('@builtin'), true); assert.strictEqual(ExtensionsListView.isLocalExtensionsQuery('@installed'), true); diff --git a/src/vs/workbench/contrib/externalUriOpener/test/common/externalUriOpenerService.test.ts b/src/vs/workbench/contrib/externalUriOpener/test/common/externalUriOpenerService.test.ts index 36ba2979901..ec8685a9bdf 100644 --- a/src/vs/workbench/contrib/externalUriOpener/test/common/externalUriOpenerService.test.ts +++ b/src/vs/workbench/contrib/externalUriOpener/test/common/externalUriOpenerService.test.ts @@ -48,6 +48,10 @@ suite('ExternalUriOpenerService', () => { }); }); + teardown(() => { + instantiationService.dispose(); + }); + test('Should not open if there are no openers', async () => { const externalUriOpenerService: ExternalUriOpenerService = instantiationService.createInstance(ExternalUriOpenerService); diff --git a/src/vs/workbench/contrib/notebook/test/browser/testNotebookEditor.ts b/src/vs/workbench/contrib/notebook/test/browser/testNotebookEditor.ts index fe3a138aff2..c8ac2befe3a 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/testNotebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/testNotebookEditor.ts @@ -169,7 +169,7 @@ export class NotebookEditorTestModel extends EditorModel implements INotebookEdi } export function setupInstantiationService(disposables = new DisposableStore()) { - const instantiationService = new TestInstantiationService(); + const instantiationService = disposables.add(new TestInstantiationService()); instantiationService.stub(ILanguageService, disposables.add(new LanguageService())); instantiationService.stub(IUndoRedoService, instantiationService.createInstance(UndoRedoService)); instantiationService.stub(IConfigurationService, new TestConfigurationService()); diff --git a/src/vs/workbench/contrib/search/test/browser/searchActions.test.ts b/src/vs/workbench/contrib/search/test/browser/searchActions.test.ts index f3a58a4d9a0..fd518ce666c 100644 --- a/src/vs/workbench/contrib/search/test/browser/searchActions.test.ts +++ b/src/vs/workbench/contrib/search/test/browser/searchActions.test.ts @@ -36,6 +36,10 @@ suite('Search Actions', () => { counter = 0; }); + teardown(() => { + instantiationService.dispose(); + }); + test('get next element to focus after removing a match when it has next sibling file', function () { const fileMatch1 = aFileMatch(); const fileMatch2 = aFileMatch(); diff --git a/src/vs/workbench/contrib/search/test/browser/searchNotebookHelpers.test.ts b/src/vs/workbench/contrib/search/test/browser/searchNotebookHelpers.test.ts index 2df451b04d4..c8e419dd373 100644 --- a/src/vs/workbench/contrib/search/test/browser/searchNotebookHelpers.test.ts +++ b/src/vs/workbench/contrib/search/test/browser/searchNotebookHelpers.test.ts @@ -115,6 +115,11 @@ suite('searchNotebookHelpers', () => { ); }); + + teardown(() => { + instantiationService.dispose(); + }); + suite('notebookEditorMatchesToTextSearchResults', () => { function assertRangesEqual(actual: ISearchRange | ISearchRange[], expected: ISearchRange[]) { diff --git a/src/vs/workbench/contrib/search/test/browser/searchResult.test.ts b/src/vs/workbench/contrib/search/test/browser/searchResult.test.ts index 30e273f7096..49a7afe9c09 100644 --- a/src/vs/workbench/contrib/search/test/browser/searchResult.test.ts +++ b/src/vs/workbench/contrib/search/test/browser/searchResult.test.ts @@ -51,7 +51,9 @@ suite('SearchResult', () => { instantiationService.stub(ILogService, new NullLogService()); }); - teardown(() => sinon.restore()); + teardown(() => { + instantiationService.dispose(); + }); test('Line Match', function () { const fileMatch = aFileMatch('folder/file.txt', null!); diff --git a/src/vs/workbench/contrib/search/test/browser/searchViewlet.test.ts b/src/vs/workbench/contrib/search/test/browser/searchViewlet.test.ts index 1fcfc4055e3..1624ba20a46 100644 --- a/src/vs/workbench/contrib/search/test/browser/searchViewlet.test.ts +++ b/src/vs/workbench/contrib/search/test/browser/searchViewlet.test.ts @@ -45,6 +45,10 @@ suite('Search - Viewlet', () => { instantiation.stub(ILogService, new NullLogService()); }); + teardown(() => { + instantiation.dispose(); + }); + test('Data Source', function () { const result: SearchResult = aSearchResult(); result.query = { diff --git a/src/vs/workbench/contrib/tasks/test/browser/taskTerminalStatus.test.ts b/src/vs/workbench/contrib/tasks/test/browser/taskTerminalStatus.test.ts index 72193bb43a3..39da6960b72 100644 --- a/src/vs/workbench/contrib/tasks/test/browser/taskTerminalStatus.test.ts +++ b/src/vs/workbench/contrib/tasks/test/browser/taskTerminalStatus.test.ts @@ -76,6 +76,9 @@ suite('Task Terminal Status', () => { testTask = instantiationService.createInstance(TestTask) as unknown as Task; problemCollector = instantiationService.createInstance(TestProblemCollector) as any; }); + teardown(() => { + instantiationService.dispose(); + }); test('Should add failed status when there is an exit code on task end', async () => { taskTerminalStatus.addTerminal(testTask, testTerminal, problemCollector); taskService.triggerStateChange({ kind: TaskEventKind.ProcessStarted }); diff --git a/src/vs/workbench/contrib/tasks/test/common/taskConfiguration.test.ts b/src/vs/workbench/contrib/tasks/test/common/taskConfiguration.test.ts index 82d631fa95d..816894f6b32 100644 --- a/src/vs/workbench/contrib/tasks/test/common/taskConfiguration.test.ts +++ b/src/vs/workbench/contrib/tasks/test/common/taskConfiguration.test.ts @@ -1802,6 +1802,9 @@ suite('Task configuration conversions', () => { parseContext.namedProblemMatchers = { 'real': namedProblemMatcher }; parseContext.uuidMap = new UUIDMap(); }); + teardown(() => { + instantiationService.dispose(); + }); suite('ProblemMatcherConverter.from', () => { test('returns [] and an error for an unknown problem matcher', () => { const result = (ProblemMatcherConverter.from('$fake', parseContext)); diff --git a/src/vs/workbench/contrib/terminal/test/browser/capabilities/commandDetectionCapability.test.ts b/src/vs/workbench/contrib/terminal/test/browser/capabilities/commandDetectionCapability.test.ts index b23f6773cd7..8ce2b64a810 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/capabilities/commandDetectionCapability.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/capabilities/commandDetectionCapability.test.ts @@ -26,6 +26,7 @@ suite('CommandDetectionCapability', () => { let xterm: Terminal; let capability: TestCommandDetectionCapability; let addEvents: ITerminalCommand[]; + let instantiationService: TestInstantiationService; function assertCommands(expectedCommands: TestTerminalCommandMatch[]) { deepStrictEqual(capability.commands.map(e => e.command), expectedCommands.map(e => e.command)); @@ -59,7 +60,7 @@ suite('CommandDetectionCapability', () => { const TerminalCtor = (await importAMDNodeModule('xterm', 'lib/xterm.js')).Terminal; xterm = new TerminalCtor({ allowProposedApi: true, cols: 80 }); - const instantiationService = new TestInstantiationService(); + instantiationService = new TestInstantiationService(); instantiationService.stub(IContextMenuService, { showContextMenu(delegate: IContextMenuDelegate): void { } } as Partial); capability = new TestCommandDetectionCapability(xterm, new NullLogService()); addEvents = []; @@ -67,6 +68,10 @@ suite('CommandDetectionCapability', () => { assertCommands([]); }); + teardown(() => { + instantiationService.dispose(); + }); + test('should not add commands when no capability methods are triggered', async () => { await writeP(xterm, 'foo\r\nbar\r\n'); assertCommands([]); 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 6bff48ba6fb..9fd40901d9e 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/terminalInstance.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/terminalInstance.test.ts @@ -190,6 +190,10 @@ suite('Workbench - TerminalInstance', () => { emptyContextService.setWorkspace(emptyWorkspace); }); + teardown(() => { + instantiationService.dispose(); + }); + test('should resolve to "" when the template variables are empty', () => { configurationService = new TestConfigurationService({ terminal: { integrated: { tabs: { separator: ' - ', title: '', description: '' } } } }); configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!); diff --git a/src/vs/workbench/contrib/terminal/test/browser/terminalInstanceService.test.ts b/src/vs/workbench/contrib/terminal/test/browser/terminalInstanceService.test.ts index c86cd2bee4d..b36530cb82b 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/terminalInstanceService.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/terminalInstanceService.test.ts @@ -36,6 +36,10 @@ suite('Workbench - TerminalInstanceService', () => { terminalInstanceService = instantiationService.createInstance(TerminalInstanceService); }); + teardown(() => { + instantiationService.dispose(); + }); + suite('convertProfileToShellLaunchConfig', () => { test('should return an empty shell launch config when undefined is provided', () => { deepStrictEqual(terminalInstanceService.convertProfileToShellLaunchConfig(), {}); diff --git a/src/vs/workbench/contrib/terminal/test/browser/terminalProfileService.integrationTest.ts b/src/vs/workbench/contrib/terminal/test/browser/terminalProfileService.integrationTest.ts index c46497a5f24..b4b05504093 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/terminalProfileService.integrationTest.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/terminalProfileService.integrationTest.ts @@ -201,6 +201,9 @@ suite('TerminalProfileService', () => { } configurationService.setUserConfiguration('terminal', { integrated: defaultTerminalConfig }); }); + teardown(() => { + instantiationService.dispose(); + }); suite('Contributed Profiles', () => { test('should filter out contributed profiles set to null (Linux)', async () => { remoteAgentService.setEnvironment(OperatingSystem.Linux); diff --git a/src/vs/workbench/contrib/terminal/test/browser/terminalService.test.ts b/src/vs/workbench/contrib/terminal/test/browser/terminalService.test.ts index 10ffee5b9d0..4ba34944a97 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/terminalService.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/terminalService.test.ts @@ -57,6 +57,10 @@ suite('Workbench - TerminalService', () => { instantiationService.stub(ITerminalService, terminalService); }); + teardown(() => { + instantiationService.dispose(); + }); + suite('safeDisposeTerminal', () => { let onExitEmitter: Emitter; diff --git a/src/vs/workbench/contrib/terminal/test/browser/xterm/decorationAddon.test.ts b/src/vs/workbench/contrib/terminal/test/browser/xterm/decorationAddon.test.ts index 4126dc9640d..5eb71018668 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/xterm/decorationAddon.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/xterm/decorationAddon.test.ts @@ -10,7 +10,7 @@ import { ILogService, NullLogService } from 'vs/platform/log/common/log'; import { DecorationAddon } from 'vs/workbench/contrib/terminal/browser/xterm/decorationAddon'; import { TerminalCapabilityStore } from 'vs/platform/terminal/common/capabilities/terminalCapabilityStore'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; -import type { IDecoration, IDecorationOptions } from 'xterm'; +import type { IDecoration, IDecorationOptions, Terminal as RawXtermTerminal } from 'xterm'; import { ITerminalCommand, TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities'; import { CommandDetectionCapability } from 'vs/platform/terminal/common/capabilities/commandDetectionCapability'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; @@ -21,24 +21,24 @@ import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecy import { TestLifecycleService } from 'vs/workbench/test/browser/workbenchTestServices'; import { importAMDNodeModule } from 'vs/amdX'; -suite('DecorationAddon', async () => { - - const TerminalCtor = (await importAMDNodeModule('xterm', 'lib/xterm.js')).Terminal; - class TestTerminal extends TerminalCtor { - override registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined { - if (decorationOptions.marker.isDisposed) { - return undefined; - } - const element = document.createElement('div'); - return { marker: decorationOptions.marker, element, onDispose: () => { }, isDisposed: false, dispose: () => { }, onRender: (element: HTMLElement) => { return element; } } as unknown as IDecoration; - } - } - +suite('DecorationAddon', () => { let decorationAddon: DecorationAddon; - let xterm: TestTerminal; + let xterm: RawXtermTerminal; + let instantiationService: TestInstantiationService; - setup(() => { - const instantiationService = new TestInstantiationService(); + setup(async () => { + const TerminalCtor = (await importAMDNodeModule('xterm', 'lib/xterm.js')).Terminal; + class TestTerminal extends TerminalCtor { + override registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined { + if (decorationOptions.marker.isDisposed) { + return undefined; + } + const element = document.createElement('div'); + return { marker: decorationOptions.marker, element, onDispose: () => { }, isDisposed: false, dispose: () => { }, onRender: (element: HTMLElement) => { return element; } } as unknown as IDecoration; + } + } + + instantiationService = new TestInstantiationService(); const configurationService = new TestConfigurationService({ workbench: { hover: { delay: 5 }, @@ -67,6 +67,10 @@ suite('DecorationAddon', async () => { xterm.loadAddon(decorationAddon); }); + teardown(() => { + instantiationService.dispose(); + }); + suite('registerDecoration', async () => { test('should throw when command has no marker', async () => { throws(() => decorationAddon.registerCommandDecoration({ command: 'cd src', timestamp: Date.now(), hasOutput: () => false } as ITerminalCommand)); diff --git a/src/vs/workbench/contrib/terminal/test/browser/xterm/xtermTerminal.test.ts b/src/vs/workbench/contrib/terminal/test/browser/xterm/xtermTerminal.test.ts index aab94b18d5d..ab689e76e87 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/xterm/xtermTerminal.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/xterm/xtermTerminal.test.ts @@ -132,6 +132,10 @@ suite('XtermTerminal', () => { TestWebglAddon.isEnabled = false; }); + teardown(() => { + instantiationService.dispose(); + }); + test('should use fallback dimensions of 80x30', () => { strictEqual(xterm.raw.cols, 80); strictEqual(xterm.raw.rows, 30); diff --git a/src/vs/workbench/contrib/terminal/test/common/environmentVariableService.test.ts b/src/vs/workbench/contrib/terminal/test/common/environmentVariableService.test.ts index 4d3d7a840c3..e338f9d55fd 100644 --- a/src/vs/workbench/contrib/terminal/test/common/environmentVariableService.test.ts +++ b/src/vs/workbench/contrib/terminal/test/common/environmentVariableService.test.ts @@ -47,6 +47,10 @@ suite('EnvironmentVariable - EnvironmentVariableService', () => { environmentVariableService = instantiationService.createInstance(TestEnvironmentVariableService); }); + teardown(() => { + instantiationService.dispose(); + }); + test('should persist collections to the storage service and be able to restore from them', () => { const collection = new Map(); collection.set('A-key', { value: 'a', type: EnvironmentVariableMutatorType.Replace, variable: 'A' }); diff --git a/src/vs/workbench/contrib/terminal/test/common/history.test.ts b/src/vs/workbench/contrib/terminal/test/common/history.test.ts index 3ae72c0cafd..bfc90c11697 100644 --- a/src/vs/workbench/contrib/terminal/test/common/history.test.ts +++ b/src/vs/workbench/contrib/terminal/test/common/history.test.ts @@ -56,6 +56,10 @@ suite('Terminal history', () => { history = instantiationService.createInstance(TerminalPersistedHistory, 'test'); }); + teardown(() => { + instantiationService.dispose(); + }); + test('should support adding items to the cache and respect LRU', () => { history.add('foo', 1); deepStrictEqual(Array.from(history.entries), [ @@ -151,6 +155,10 @@ suite('Terminal history', () => { } as Pick); }); + teardown(() => { + instantiationService.dispose(); + }); + if (!isWindows) { suite('local', async () => { let originalEnvValues: { HOME: string | undefined }; @@ -239,6 +247,10 @@ suite('Terminal history', () => { } as Pick); }); + teardown(() => { + instantiationService.dispose(); + }); + if (!isWindows) { suite('local', () => { let originalEnvValues: { HOME: string | undefined }; @@ -328,6 +340,10 @@ suite('Terminal history', () => { } as Pick); }); + teardown(() => { + instantiationService.dispose(); + }); + suite('local', async () => { let originalEnvValues: { HOME: string | undefined; APPDATA: string | undefined }; setup(() => { @@ -433,6 +449,10 @@ suite('Terminal history', () => { } as Pick); }); + teardown(() => { + instantiationService.dispose(); + }); + if (!isWindows) { suite('local', () => { let originalEnvValues: { HOME: string | undefined }; diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/test/browser/bufferContentTracker.test.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/test/browser/bufferContentTracker.test.ts index cafcc7cd6c5..240c15c4c21 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/test/browser/bufferContentTracker.test.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/test/browser/bufferContentTracker.test.ts @@ -54,7 +54,6 @@ suite('Buffer Content Tracker', () => { configurationService = new TestConfigurationService({ terminal: { integrated: defaultTerminalConfig } }); instantiationService = new TestInstantiationService(); themeService = new TestThemeService(); - instantiationService = new TestInstantiationService(); instantiationService.stub(IConfigurationService, configurationService); instantiationService.stub(IThemeService, themeService); instantiationService.stub(ITerminalLogService, new NullLogService()); @@ -75,6 +74,9 @@ suite('Buffer Content Tracker', () => { configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!); bufferTracker = instantiationService.createInstance(BufferContentTracker, xterm); }); + teardown(() => { + instantiationService.dispose(); + }); test('should not clear the prompt line', async () => { assert.strictEqual(bufferTracker.lines.length, 0); await writeP(xterm.raw, prompt); diff --git a/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkManager.test.ts b/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkManager.test.ts index 6689f791380..2f4c06504e4 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkManager.test.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkManager.test.ts @@ -96,6 +96,10 @@ suite('TerminalLinkManager', () => { } as Partial as any, instantiationService.createInstance(TerminalLinkResolver)); }); + teardown(() => { + instantiationService.dispose(); + }); + suite('getLinks and open recent link', () => { test('should return no links', async () => { const links = await linkManager.getLinks(); diff --git a/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkOpeners.test.ts b/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkOpeners.test.ts index 34a2c7250b1..e9193758069 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkOpeners.test.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkOpeners.test.ts @@ -113,6 +113,10 @@ suite('Workbench - TerminalLinkOpeners', () => { xterm = new TerminalCtor({ allowProposedApi: true }); }); + teardown(() => { + instantiationService.dispose(); + }); + suite('TerminalSearchLinkOpener', () => { let opener: TestTerminalSearchLinkOpener; let capabilities: TerminalCapabilityStore; diff --git a/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLocalLinkDetector.test.ts b/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLocalLinkDetector.test.ts index b29d05beaf0..ba2e367c310 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLocalLinkDetector.test.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLocalLinkDetector.test.ts @@ -192,6 +192,10 @@ suite('Workbench - TerminalLocalLinkDetector', () => { xterm = new TerminalCtor({ allowProposedApi: true, cols: 80, rows: 30 }); }); + teardown(() => { + instantiationService.dispose(); + }); + suite('platform independent', () => { setup(() => { detector = instantiationService.createInstance(TerminalLocalLinkDetector, xterm, new TerminalCapabilityStore(), { diff --git a/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalMultiLineLinkDetector.test.ts b/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalMultiLineLinkDetector.test.ts index 0e7e686dc35..a0c84d878b2 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalMultiLineLinkDetector.test.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalMultiLineLinkDetector.test.ts @@ -151,6 +151,10 @@ suite('Workbench - TerminalMultiLineLinkDetector', () => { xterm = new TerminalCtor({ allowProposedApi: true, cols: 80, rows: 30 }); }); + teardown(() => { + instantiationService.dispose(); + }); + suite('macOS/Linux', () => { setup(() => { detector = instantiationService.createInstance(TerminalMultiLineLinkDetector, xterm, { diff --git a/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalUriLinkDetector.test.ts b/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalUriLinkDetector.test.ts index c1480524b43..f42e8619756 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalUriLinkDetector.test.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalUriLinkDetector.test.ts @@ -22,9 +22,10 @@ suite('Workbench - TerminalUriLinkDetector', () => { let detector: TerminalUriLinkDetector; let xterm: Terminal; let validResources: URI[] = []; + let instantiationService: TestInstantiationService; setup(async () => { - const instantiationService = new TestInstantiationService(); + instantiationService = new TestInstantiationService(); configurationService = new TestConfigurationService(); instantiationService.stub(IConfigurationService, configurationService); instantiationService.stub(IFileService, { @@ -48,6 +49,10 @@ suite('Workbench - TerminalUriLinkDetector', () => { }, instantiationService.createInstance(TerminalLinkResolver)); }); + teardown(() => { + instantiationService.dispose(); + }); + async function assertLink( type: TerminalBuiltinLinkType, text: string, diff --git a/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalWordLinkDetector.test.ts b/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalWordLinkDetector.test.ts index 1a103f07364..d274ff008a8 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalWordLinkDetector.test.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalWordLinkDetector.test.ts @@ -18,9 +18,10 @@ suite('Workbench - TerminalWordLinkDetector', () => { let configurationService: TestConfigurationService; let detector: TerminalWordLinkDetector; let xterm: Terminal; + let instantiationService: TestInstantiationService; setup(async () => { - const instantiationService = new TestInstantiationService(); + instantiationService = new TestInstantiationService(); configurationService = new TestConfigurationService(); await configurationService.setUserConfiguration('terminal', { integrated: { wordSeparators: '' } }); @@ -32,6 +33,10 @@ suite('Workbench - TerminalWordLinkDetector', () => { detector = instantiationService.createInstance(TerminalWordLinkDetector, xterm); }); + teardown(() => { + instantiationService.dispose(); + }); + async function assertLink( text: string, expected: (Pick & { range: [number, number][] })[] diff --git a/src/vs/workbench/contrib/terminalContrib/quickFix/test/browser/quickFixAddon.test.ts b/src/vs/workbench/contrib/terminalContrib/quickFix/test/browser/quickFixAddon.test.ts index b7f5b729fdf..cabbf4d1c21 100644 --- a/src/vs/workbench/contrib/terminalContrib/quickFix/test/browser/quickFixAddon.test.ts +++ b/src/vs/workbench/contrib/terminalContrib/quickFix/test/browser/quickFixAddon.test.ts @@ -38,8 +38,9 @@ suite('QuickFixAddon', () => { let openerService: OpenerService; let labelService: LabelService; let terminal: Terminal; + let instantiationService: TestInstantiationService; setup(async () => { - const instantiationService = new TestInstantiationService(); + instantiationService = new TestInstantiationService(); const TerminalCtor = (await importAMDNodeModule('xterm', 'lib/xterm.js')).Terminal; terminal = new TerminalCtor({ allowProposedApi: true, @@ -68,6 +69,9 @@ suite('QuickFixAddon', () => { quickFixAddon = instantiationService.createInstance(TerminalQuickFixAddon, [], capabilities); terminal.loadAddon(quickFixAddon); }); + teardown(() => { + instantiationService.dispose(); + }); suite('registerCommandFinishedListener & getMatchActions', () => { suite('gitSimilarCommand', async () => { const expectedMap = new Map(); diff --git a/src/vs/workbench/services/dialogs/test/electron-sandbox/fileDialogService.test.ts b/src/vs/workbench/services/dialogs/test/electron-sandbox/fileDialogService.test.ts index d14734c312d..f8866b4c923 100644 --- a/src/vs/workbench/services/dialogs/test/electron-sandbox/fileDialogService.test.ts +++ b/src/vs/workbench/services/dialogs/test/electron-sandbox/fileDialogService.test.ts @@ -78,7 +78,7 @@ suite('FileDialogService', function () { setup(async function () { disposables = new DisposableStore(); - instantiationService = workbenchInstantiationService(undefined, disposables); + disposables.add(instantiationService = workbenchInstantiationService(undefined, disposables)); const configurationService = new TestConfigurationService(); await configurationService.setUserConfiguration('files', { simpleDialog: { enable: true } }); instantiationService.stub(IConfigurationService, configurationService); diff --git a/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts b/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts index 39f06f3c822..2faa51fa86b 100644 --- a/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts +++ b/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts @@ -141,6 +141,7 @@ suite('ExtensionEnablementService Test', () => { teardown(() => { (testObject).dispose(); + instantiationService.dispose(); }); test('test disable an extension globally', async () => { diff --git a/src/vs/workbench/services/extensions/test/browser/extensionService.test.ts b/src/vs/workbench/services/extensions/test/browser/extensionService.test.ts index 36c3ed811d5..f506bbee811 100644 --- a/src/vs/workbench/services/extensions/test/browser/extensionService.test.ts +++ b/src/vs/workbench/services/extensions/test/browser/extensionService.test.ts @@ -222,7 +222,7 @@ suite('ExtensionService', () => { setup(() => { disposables = new DisposableStore(); - instantiationService = createServices(disposables, [ + disposables.add(instantiationService = createServices(disposables, [ // custom [IExtensionService, MyTestExtensionService], // default @@ -246,7 +246,7 @@ suite('ExtensionService', () => { [IUriIdentityService, UriIdentityService], [IRemoteExtensionsScannerService, TestRemoteExtensionsScannerService], [IRemoteAuthorityResolverService, RemoteAuthorityResolverService] - ]); + ])); extService = instantiationService.get(IExtensionService); }); diff --git a/src/vs/workbench/services/extensions/test/common/extensionManifestPropertiesService.test.ts b/src/vs/workbench/services/extensions/test/common/extensionManifestPropertiesService.test.ts index 9c890337351..07e5b50f154 100644 --- a/src/vs/workbench/services/extensions/test/common/extensionManifestPropertiesService.test.ts +++ b/src/vs/workbench/services/extensions/test/common/extensionManifestPropertiesService.test.ts @@ -106,7 +106,10 @@ if (!isWeb) { instantiationService.stub(IConfigurationService, testConfigurationService); }); - teardown(() => testObject.dispose()); + teardown(() => { + testObject.dispose(); + instantiationService.dispose(); + }); function assertUntrustedWorkspaceSupport(extensionManifest: IExtensionManifest, expected: ExtensionUntrustedWorkspaceSupportType): void { testObject = instantiationService.createInstance(ExtensionManifestPropertiesService); diff --git a/src/vs/workbench/services/preferences/test/browser/keybindingsEditorModel.test.ts b/src/vs/workbench/services/preferences/test/browser/keybindingsEditorModel.test.ts index 29ec76bb1df..1e5631e8a67 100644 --- a/src/vs/workbench/services/preferences/test/browser/keybindingsEditorModel.test.ts +++ b/src/vs/workbench/services/preferences/test/browser/keybindingsEditorModel.test.ts @@ -48,6 +48,10 @@ suite('KeybindingsEditorModel', () => { CommandsRegistry.registerCommand('command_without_keybinding', () => { }); }); + teardown(() => { + instantiationService.dispose(); + }); + test('fetch returns default keybindings', async () => { const expected = prepareKeybindingService( aResolvedKeybindingItem({ command: 'a' + uuid.generateUuid(), firstChord: { keyCode: KeyCode.Escape } }), diff --git a/src/vs/workbench/services/search/test/browser/queryBuilder.test.ts b/src/vs/workbench/services/search/test/browser/queryBuilder.test.ts index 0f4af8d156a..c112215cfe2 100644 --- a/src/vs/workbench/services/search/test/browser/queryBuilder.test.ts +++ b/src/vs/workbench/services/search/test/browser/queryBuilder.test.ts @@ -58,6 +58,10 @@ suite('QueryBuilder', () => { queryBuilder = instantiationService.createInstance(QueryBuilder); }); + teardown(() => { + instantiationService.dispose(); + }); + test('simple text pattern', () => { assertEqualTextQueries( queryBuilder.text(PATTERN_INFO), diff --git a/src/vs/workbench/services/userActivity/test/browser/domActivityTracker.test.ts b/src/vs/workbench/services/userActivity/test/browser/domActivityTracker.test.ts index 4c443c006a1..f97c4ab35ba 100644 --- a/src/vs/workbench/services/userActivity/test/browser/domActivityTracker.test.ts +++ b/src/vs/workbench/services/userActivity/test/browser/domActivityTracker.test.ts @@ -12,12 +12,14 @@ import * as assert from 'assert'; suite('DomActivityTracker', () => { let uas: UserActivityService; let dom: DomActivityTracker; + let insta: TestInstantiationService; let clock: sinon.SinonFakeTimers; const maxTimeToBecomeIdle = 3 * 30_000; // (MIN_INTERVALS_WITHOUT_ACTIVITY + 1) * CHECK_INTERVAL; setup(() => { clock = sinon.useFakeTimers(); - uas = new UserActivityService(new TestInstantiationService()); + insta = new TestInstantiationService(); + uas = new UserActivityService(insta); dom = new DomActivityTracker(uas); }); @@ -25,6 +27,7 @@ suite('DomActivityTracker', () => { dom.dispose(); uas.dispose(); clock.restore(); + insta.dispose(); }); diff --git a/src/vs/workbench/services/views/test/browser/viewDescriptorService.test.ts b/src/vs/workbench/services/views/test/browser/viewDescriptorService.test.ts index c873bc6800e..74b4b56185d 100644 --- a/src/vs/workbench/services/views/test/browser/viewDescriptorService.test.ts +++ b/src/vs/workbench/services/views/test/browser/viewDescriptorService.test.ts @@ -30,7 +30,7 @@ suite('ViewDescriptorService', () => { let instantiationService: TestInstantiationService; setup(() => { - instantiationService = workbenchInstantiationService(undefined, disposables); + disposables.add(instantiationService = workbenchInstantiationService(undefined, disposables)); instantiationService.stub(IContextKeyService, instantiationService.createInstance(ContextKeyService)); }); diff --git a/src/vs/workbench/services/workspaces/test/common/workspaceTrust.test.ts b/src/vs/workbench/services/workspaces/test/common/workspaceTrust.test.ts index ab6f9d78a7d..037330ae50e 100644 --- a/src/vs/workbench/services/workspaces/test/common/workspaceTrust.test.ts +++ b/src/vs/workbench/services/workspaces/test/common/workspaceTrust.test.ts @@ -42,6 +42,10 @@ suite('Workspace Trust', () => { instantiationService.stub(IRemoteAuthorityResolverService, new class extends mock() { }); }); + teardown(() => { + instantiationService.dispose(); + }); + suite('Enablement', () => { let testObject: WorkspaceTrustEnablementService; diff --git a/src/vs/workbench/test/browser/parts/editor/editorGroupModel.test.ts b/src/vs/workbench/test/browser/parts/editor/editorGroupModel.test.ts index d5b2f9fa085..4b790fac167 100644 --- a/src/vs/workbench/test/browser/parts/editor/editorGroupModel.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/editorGroupModel.test.ts @@ -1064,6 +1064,7 @@ suite('EditorGroupModel', () => { assert.strictEqual(events.closed.length, 3); assert.strictEqual(group.count, 0); + inst.dispose(); }); test('Multiple Editors - Pinned and Not Active', function () { @@ -1320,6 +1321,7 @@ suite('EditorGroupModel', () => { assert.ok(!group.activeEditor); assert.strictEqual(group.count, 0); + inst.dispose(); }); test('Multiple Editors - move editor', function () { @@ -1673,6 +1675,7 @@ suite('EditorGroupModel', () => { assert.strictEqual(group.activeEditor!.matches(input1), true); assert.strictEqual(group.previewEditor!.matches(input1), true); assert.strictEqual(group.isActive(input1), true); + inst.dispose(); }); test('Multiple Groups, Multiple editors - persist', function () { @@ -1743,6 +1746,7 @@ suite('EditorGroupModel', () => { assert.strictEqual(group2.getEditors(EditorsOrder.MOST_RECENTLY_ACTIVE)[0].matches(g2_input1), true); assert.strictEqual(group2.getEditors(EditorsOrder.MOST_RECENTLY_ACTIVE)[1].matches(g2_input3), true); assert.strictEqual(group2.getEditors(EditorsOrder.MOST_RECENTLY_ACTIVE)[2].matches(g2_input2), true); + inst.dispose(); }); test('Single group, multiple editors - persist (some not persistable)', function () { @@ -1787,6 +1791,7 @@ suite('EditorGroupModel', () => { assert.strictEqual(group.getEditors(EditorsOrder.MOST_RECENTLY_ACTIVE)[0].matches(serializableInput2), true); assert.strictEqual(group.getEditors(EditorsOrder.MOST_RECENTLY_ACTIVE)[1].matches(serializableInput1), true); + inst.dispose(); }); test('Single group, multiple editors - persist (some not persistable, sticky editors)', function () { @@ -1822,6 +1827,7 @@ suite('EditorGroupModel', () => { assert.strictEqual(group.count, 2); assert.strictEqual(group.stickyCount, 0); + inst.dispose(); }); test('Multiple groups, multiple editors - persist (some not persistable, causes empty group)', function () { @@ -1858,6 +1864,7 @@ suite('EditorGroupModel', () => { assert.strictEqual(group1.count, 2); assert.strictEqual(group1.getEditors(EditorsOrder.SEQUENTIAL)[0].matches(serializableInput1), true); assert.strictEqual(group1.getEditors(EditorsOrder.SEQUENTIAL)[1].matches(serializableInput2), true); + inst.dispose(); }); test('Multiple Editors - Editor Dispose', function () { diff --git a/src/vs/workbench/test/browser/parts/editor/editorModel.test.ts b/src/vs/workbench/test/browser/parts/editor/editorModel.test.ts index 07ccba391e9..2548c2db93e 100644 --- a/src/vs/workbench/test/browser/parts/editor/editorModel.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/editorModel.test.ts @@ -75,6 +75,10 @@ suite('EditorModel', () => { languageService = instantiationService.stub(ILanguageService, LanguageService); }); + teardown(() => { + instantiationService.dispose(); + }); + test('basics', async () => { let counter = 0; diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index 3994e6bb1e5..a333fabb14e 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -243,7 +243,7 @@ export function workbenchInstantiationService( }, disposables: DisposableStore = new DisposableStore() ): TestInstantiationService { - const instantiationService = new TestInstantiationService(new ServiceCollection([ILifecycleService, new TestLifecycleService()])); + const instantiationService = disposables.add(new TestInstantiationService(new ServiceCollection([ILifecycleService, new TestLifecycleService()]))); instantiationService.stub(IEditorWorkerService, new TestEditorWorkerService()); instantiationService.stub(IWorkingCopyService, disposables.add(new TestWorkingCopyService())); From f254ad1758a0d947d644fe6fd7a068980b93e27a Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Tue, 11 Jul 2023 14:24:23 +0200 Subject: [PATCH 314/926] Git - Add Commit (Amend) to the Commit Action button (#187568) --- extensions/git/package.json | 82 ++++++++++++++++++++---- extensions/git/package.nls.json | 4 ++ extensions/git/src/actionButton.ts | 2 +- extensions/git/src/commands.ts | 20 ++++++ extensions/git/src/postCommitCommands.ts | 11 ++-- 5 files changed, 103 insertions(+), 16 deletions(-) diff --git a/extensions/git/package.json b/extensions/git/package.json index 45a221730ca..f45447de7f1 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -254,6 +254,20 @@ "icon": "$(check)", "enablement": "!operationInProgress" }, + { + "command": "git.commitAmend", + "title": "%command.commitAmend%", + "category": "Git", + "icon": "$(check)", + "enablement": "!operationInProgress" + }, + { + "command": "git.commitSigned", + "title": "%command.commitSigned%", + "category": "Git", + "icon": "$(check)", + "enablement": "!operationInProgress" + }, { "command": "git.commitStaged", "title": "%command.commitStaged%", @@ -321,6 +335,18 @@ "category": "Git", "enablement": "!operationInProgress" }, + { + "command": "git.commitAmendNoVerify", + "title": "%command.commitAmendNoVerify%", + "category": "Git", + "enablement": "!operationInProgress" + }, + { + "command": "git.commitSignedNoVerify", + "title": "%command.commitSignedNoVerify%", + "category": "Git", + "enablement": "!operationInProgress" + }, { "command": "git.commitStagedAmendNoVerify", "title": "%command.commitStagedAmendNoVerify%", @@ -873,6 +899,14 @@ "command": "git.commit", "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0" }, + { + "command": "git.commitAmend", + "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0" + }, + { + "command": "git.commitSigned", + "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0" + }, { "command": "git.commitStaged", "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0" @@ -921,6 +955,14 @@ "command": "git.commitStagedSignedNoVerify", "when": "config.git.enabled && !git.missing && config.git.allowNoVerifyCommit && gitOpenRepositoryCount != 0" }, + { + "command": "git.commitAmendNoVerify", + "when": "config.git.enabled && !git.missing && config.git.allowNoVerifyCommit && gitOpenRepositoryCount != 0" + }, + { + "command": "git.commitSignedNoVerify", + "when": "config.git.enabled && !git.missing && config.git.allowNoVerifyCommit && gitOpenRepositoryCount != 0" + }, { "command": "git.commitStagedAmendNoVerify", "when": "config.git.enabled && !git.missing && config.git.allowNoVerifyCommit && gitOpenRepositoryCount != 0" @@ -1772,53 +1814,71 @@ }, { "command": "git.commitNoVerify", - "group": "1_commit@6", + "group": "2_commit_noverify@1", "when": "config.git.allowNoVerifyCommit" }, { "command": "git.commitStagedNoVerify", - "group": "1_commit@7", + "group": "2_commit_noverify@2", "when": "config.git.allowNoVerifyCommit" }, { "command": "git.commitAllNoVerify", - "group": "1_commit@8", + "group": "2_commit_noverify@3", "when": "config.git.allowNoVerifyCommit" }, + { + "command": "git.commitAmend", + "group": "3_amend@1" + }, { "command": "git.commitStagedAmend", - "group": "2_amend@1" + "group": "3_amend@2" }, { "command": "git.commitAllAmend", - "group": "2_amend@2" + "group": "3_amend@3" + }, + { + "command": "git.commitAmendNoVerify", + "group": "4_amend_noverify@1", + "when": "config.git.allowNoVerifyCommit" }, { "command": "git.commitStagedAmendNoVerify", - "group": "2_amend@3", + "group": "4_amend_noverify@2", "when": "config.git.allowNoVerifyCommit" }, { "command": "git.commitAllAmendNoVerify", - "group": "2_amend@4", + "group": "4_amend_noverify@3", "when": "config.git.allowNoVerifyCommit" }, + { + "command": "git.commitSigned", + "group": "5_signoff@1" + }, { "command": "git.commitStagedSigned", - "group": "3_signoff@1" + "group": "5_signoff@2" }, { "command": "git.commitAllSigned", - "group": "3_signoff@2" + "group": "5_signoff@3" + }, + { + "command": "git.commitSignedNoVerify", + "group": "6_signoff_noverify@1", + "when": "config.git.allowNoVerifyCommit" }, { "command": "git.commitStagedSignedNoVerify", - "group": "3_signoff@3", + "group": "6_signoff_noverify@2", "when": "config.git.allowNoVerifyCommit" }, { "command": "git.commitAllSignedNoVerify", - "group": "3_signoff@4", + "group": "6_signoff_noverify@3", "when": "config.git.allowNoVerifyCommit" } ], diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index 74386ba1464..24f03414ce6 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -33,6 +33,8 @@ "command.cleanAllUntracked": "Discard All Untracked Changes", "command.closeAllDiffEditors": "Close All Diff Editors", "command.commit": "Commit", + "command.commitAmend": "Commit (Amend)", + "command.commitSigned": "Commit (Signed Off)", "command.commitStaged": "Commit Staged", "command.commitEmpty": "Commit Empty", "command.commitStagedSigned": "Commit Staged (Signed Off)", @@ -44,6 +46,8 @@ "command.commitStagedNoVerify": "Commit Staged (No Verify)", "command.commitEmptyNoVerify": "Commit Empty (No Verify)", "command.commitStagedSignedNoVerify": "Commit Staged (Signed Off, No Verify)", + "command.commitAmendNoVerify": "Commit (Amend, No Verify)", + "command.commitSignedNoVerify": "Commit (Signed Off, No Verify)", "command.commitStagedAmendNoVerify": "Commit Staged (Amend, No Verify)", "command.commitAllNoVerify": "Commit All (No Verify)", "command.commitAllSignedNoVerify": "Commit All (Signed Off, No Verify)", diff --git a/extensions/git/src/actionButton.ts b/extensions/git/src/actionButton.ts index e0524d9dff6..6b99a2a967a 100644 --- a/extensions/git/src/actionButton.ts +++ b/extensions/git/src/actionButton.ts @@ -126,7 +126,7 @@ export class ActionButtonCommand { const commandGroups: Command[][] = []; for (const commands of this.postCommitCommandCenter.getSecondaryCommands()) { commandGroups.push(commands.map(c => { - return { command: 'git.commit', title: c.title, tooltip: c.tooltip, arguments: c.arguments }; + return { command: c.command, title: c.title, tooltip: c.tooltip, arguments: c.arguments }; })); } diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index ef9b0a56fad..8eba9cbac03 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -1973,6 +1973,16 @@ export class CommandCenter { await this.commitWithAnyInput(repository, { postCommitCommand }); } + @command('git.commitAmend', { repository: true }) + async commitAmend(repository: Repository): Promise { + await this.commitWithAnyInput(repository, { amend: true }); + } + + @command('git.commitSigned', { repository: true }) + async commitSigned(repository: Repository): Promise { + await this.commitWithAnyInput(repository, { signoff: true }); + } + @command('git.commitStaged', { repository: true }) async commitStaged(repository: Repository): Promise { await this.commitWithAnyInput(repository, { all: false }); @@ -2091,6 +2101,16 @@ export class CommandCenter { await this.commitWithAnyInput(repository, { all: false, signoff: true, noVerify: true }); } + @command('git.commitAmendNoVerify', { repository: true }) + async commitAmendNoVerify(repository: Repository): Promise { + await this.commitWithAnyInput(repository, { amend: true, noVerify: true }); + } + + @command('git.commitSignedNoVerify', { repository: true }) + async commitSignedNoVerify(repository: Repository): Promise { + await this.commitWithAnyInput(repository, { signoff: true, noVerify: true }); + } + @command('git.commitStagedAmendNoVerify', { repository: true }) async commitStagedAmendNoVerify(repository: Repository): Promise { await this.commitWithAnyInput(repository, { all: false, amend: true, noVerify: true }); diff --git a/extensions/git/src/postCommitCommands.ts b/extensions/git/src/postCommitCommands.ts index 1def1716a80..d4e227b6db7 100644 --- a/extensions/git/src/postCommitCommands.ts +++ b/extensions/git/src/postCommitCommands.ts @@ -117,7 +117,7 @@ export class CommitCommandsCenter { const commandFromStorage = allCommands.find(c => c.arguments?.length === 2 && c.arguments[1] === this.getPostCommitCommandStringFromStorage()); const commandFromSetting = allCommands.find(c => c.arguments?.length === 2 && c.arguments[1] === this.getPostCommitCommandStringFromSetting()); - return commandFromStorage ?? commandFromSetting ?? this.getCommitCommand(); + return commandFromStorage ?? commandFromSetting ?? this.getCommitCommands()[0]; } getSecondaryCommands(): Command[][] { @@ -131,7 +131,7 @@ export class CommitCommandsCenter { } if (commandGroups.length > 0) { - commandGroups[0].splice(0, 0, this.getCommitCommand()); + commandGroups.splice(0, 0, this.getCommitCommands()); } return commandGroups; @@ -169,7 +169,7 @@ export class CommitCommandsCenter { return `postCommitCommand:${this.repository.root}`; } - private getCommitCommand(): Command { + private getCommitCommands(): Command[] { const config = workspace.getConfiguration('git', Uri.file(this.repository.root)); // Branch protection @@ -196,7 +196,10 @@ export class CommitCommandsCenter { l10n.t('Committing Changes to New Branch...'); } - return { command: 'git.commit', title: l10n.t('{0} Commit', icon ?? '$(check)'), tooltip, arguments: [this.repository.sourceControl, null] }; + return [ + { command: 'git.commit', title: l10n.t('{0} Commit', icon ?? '$(check)'), tooltip, arguments: [this.repository.sourceControl, null] }, + { command: 'git.commitAmend', title: l10n.t('{0} Commit (Amend)', icon ?? '$(check)'), tooltip, arguments: [this.repository.sourceControl, null] }, + ]; } private getPostCommitCommandStringFromSetting(): string | undefined { From f0d0d1f27f72abe33d1826ec847b81e8bd847fe0 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Tue, 11 Jul 2023 14:24:30 +0200 Subject: [PATCH 315/926] using instead the equals utility from the arrays.ts file --- .../contrib/stickyScroll/browser/stickyScrollWidget.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts index 0664148b786..7c7260203bf 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts @@ -6,6 +6,7 @@ import * as dom from 'vs/base/browser/dom'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { createTrustedTypesPolicy } from 'vs/base/browser/trustedTypes'; +import { equals } from 'vs/base/common/arrays'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import 'vs/css!./stickyScroll'; import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition } from 'vs/editor/browser/editorBrowser'; @@ -23,9 +24,7 @@ export class StickyScrollWidgetState { ) { } public equals(other: StickyScrollWidgetState | undefined): boolean { - return !!other && this.lastLineRelativePosition === other.lastLineRelativePosition - && this.lineNumbers.length === other.lineNumbers.length - && this.lineNumbers.every((lineNumber, index) => lineNumber === other.lineNumbers[index]); + return !!other && this.lastLineRelativePosition === other.lastLineRelativePosition && equals(this.lineNumbers, other.lineNumbers); } } From a044e57b0760ed2c16b97db619267c823024dbae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Tue, 11 Jul 2023 14:29:02 +0200 Subject: [PATCH 316/926] still run tests on win32-ia32, but make them optional to pass (#187474) * still run tests on win32-ia32, but make them optional to pass * hm * ok * try this --- build/azure-pipelines/product-build-pr.yml | 3 +++ build/azure-pipelines/product-build.yml | 12 +++++++++--- .../win32/product-build-win32-test.yml | 11 +++++++++++ build/azure-pipelines/win32/product-build-win32.yml | 3 +++ 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/build/azure-pipelines/product-build-pr.yml b/build/azure-pipelines/product-build-pr.yml index 789996060ec..90f85c5817f 100644 --- a/build/azure-pipelines/product-build-pr.yml +++ b/build/azure-pipelines/product-build-pr.yml @@ -105,6 +105,7 @@ jobs: - template: win32/product-build-win32.yml parameters: VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_ARCH: x64 VSCODE_CIBUILD: ${{ variables.VSCODE_CIBUILD }} VSCODE_RUN_UNIT_TESTS: true VSCODE_RUN_INTEGRATION_TESTS: false @@ -121,6 +122,7 @@ jobs: - template: win32/product-build-win32.yml parameters: VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_ARCH: x64 VSCODE_CIBUILD: ${{ variables.VSCODE_CIBUILD }} VSCODE_RUN_UNIT_TESTS: false VSCODE_RUN_INTEGRATION_TESTS: true @@ -137,6 +139,7 @@ jobs: # - template: win32/product-build-win32.yml # parameters: # VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + # VSCODE_ARCH: x64 # VSCODE_RUN_UNIT_TESTS: false # VSCODE_RUN_INTEGRATION_TESTS: false # VSCODE_RUN_SMOKE_TESTS: true diff --git a/build/azure-pipelines/product-build.yml b/build/azure-pipelines/product-build.yml index 7d809d6d11e..1be2db3b5fa 100644 --- a/build/azure-pipelines/product-build.yml +++ b/build/azure-pipelines/product-build.yml @@ -284,6 +284,7 @@ stages: - template: win32/product-build-win32.yml parameters: VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_ARCH: x64 VSCODE_CIBUILD: ${{ variables.VSCODE_CIBUILD }} VSCODE_RUN_UNIT_TESTS: true VSCODE_RUN_INTEGRATION_TESTS: false @@ -297,6 +298,7 @@ stages: - template: win32/product-build-win32.yml parameters: VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_ARCH: x64 VSCODE_CIBUILD: ${{ variables.VSCODE_CIBUILD }} VSCODE_RUN_UNIT_TESTS: false VSCODE_RUN_INTEGRATION_TESTS: true @@ -310,6 +312,7 @@ stages: - template: win32/product-build-win32.yml parameters: VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_ARCH: x64 VSCODE_CIBUILD: ${{ variables.VSCODE_CIBUILD }} VSCODE_RUN_UNIT_TESTS: false VSCODE_RUN_INTEGRATION_TESTS: false @@ -324,6 +327,7 @@ stages: - template: win32/product-build-win32.yml parameters: VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_ARCH: x64 VSCODE_CIBUILD: ${{ variables.VSCODE_CIBUILD }} VSCODE_RUN_UNIT_TESTS: ${{ eq(parameters.VSCODE_STEP_ON_IT, false) }} VSCODE_RUN_INTEGRATION_TESTS: ${{ eq(parameters.VSCODE_STEP_ON_IT, false) }} @@ -347,10 +351,11 @@ stages: - template: win32/product-build-win32.yml parameters: VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_ARCH: ia32 VSCODE_CIBUILD: ${{ variables.VSCODE_CIBUILD }} - VSCODE_RUN_UNIT_TESTS: false - VSCODE_RUN_INTEGRATION_TESTS: false - VSCODE_RUN_SMOKE_TESTS: false + VSCODE_RUN_UNIT_TESTS: true + VSCODE_RUN_INTEGRATION_TESTS: true + VSCODE_RUN_SMOKE_TESTS: true - ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_WIN32_ARM64, true)) }}: - job: WindowsARM64 @@ -361,6 +366,7 @@ stages: - template: win32/product-build-win32.yml parameters: VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_ARCH: arm64 VSCODE_CIBUILD: ${{ variables.VSCODE_CIBUILD }} VSCODE_RUN_UNIT_TESTS: false VSCODE_RUN_INTEGRATION_TESTS: false diff --git a/build/azure-pipelines/win32/product-build-win32-test.yml b/build/azure-pipelines/win32/product-build-win32-test.yml index ab4240f689c..630e226a742 100644 --- a/build/azure-pipelines/win32/product-build-win32-test.yml +++ b/build/azure-pipelines/win32/product-build-win32-test.yml @@ -1,6 +1,8 @@ parameters: - name: VSCODE_QUALITY type: string + - name: VSCODE_ARCH + type: string - name: VSCODE_RUN_UNIT_TESTS type: boolean - name: VSCODE_RUN_INTEGRATION_TESTS @@ -32,14 +34,17 @@ steps: - powershell: .\scripts\test.bat --build --tfs "Unit Tests" displayName: Run unit tests (Electron) timeoutInMinutes: 15 + continueOnError: ${{ eq(parameters.VSCODE_ARCH, 'ia32') }} - powershell: yarn test-node --build displayName: Run unit tests (node.js) timeoutInMinutes: 15 + continueOnError: ${{ eq(parameters.VSCODE_ARCH, 'ia32') }} - powershell: yarn test-browser-no-install --sequential --build --browser chromium --tfs "Browser Unit Tests" displayName: Run unit tests (Browser, Chromium) timeoutInMinutes: 20 + continueOnError: ${{ eq(parameters.VSCODE_ARCH, 'ia32') }} - ${{ if eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true) }}: - powershell: | @@ -94,6 +99,7 @@ steps: exec { .\scripts\test-integration.bat --build --tfs "Integration Tests" } displayName: Run integration tests (Electron) timeoutInMinutes: 20 + continueOnError: ${{ eq(parameters.VSCODE_ARCH, 'ia32') }} - powershell: | . build/azure-pipelines/win32/exec.ps1 @@ -102,6 +108,7 @@ steps: exec { .\scripts\test-web-integration.bat --browser firefox } displayName: Run integration tests (Browser, Firefox) timeoutInMinutes: 20 + continueOnError: ${{ eq(parameters.VSCODE_ARCH, 'ia32') }} - powershell: | . build/azure-pipelines/win32/exec.ps1 @@ -114,6 +121,7 @@ steps: exec { .\scripts\test-remote-integration.bat } displayName: Run integration tests (Remote) timeoutInMinutes: 20 + continueOnError: ${{ eq(parameters.VSCODE_ARCH, 'ia32') }} - ${{ if eq(parameters.VSCODE_RUN_SMOKE_TESTS, true) }}: - powershell: .\build\azure-pipelines\win32\listprocesses.bat @@ -136,12 +144,14 @@ steps: - powershell: yarn smoketest-no-compile --tracing --build "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)" displayName: Run smoke tests (Electron) timeoutInMinutes: 20 + continueOnError: ${{ eq(parameters.VSCODE_ARCH, 'ia32') }} - powershell: yarn smoketest-no-compile --web --tracing --headless env: VSCODE_REMOTE_SERVER_PATH: $(agent.builddirectory)\vscode-server-win32-$(VSCODE_ARCH)-web displayName: Run smoke tests (Browser, Chromium) timeoutInMinutes: 20 + continueOnError: ${{ eq(parameters.VSCODE_ARCH, 'ia32') }} - powershell: yarn gulp compile-extension:vscode-test-resolver displayName: Compile test resolver extension @@ -152,6 +162,7 @@ steps: VSCODE_REMOTE_SERVER_PATH: $(agent.builddirectory)\vscode-server-win32-$(VSCODE_ARCH) displayName: Run smoke tests (Remote) timeoutInMinutes: 20 + continueOnError: ${{ eq(parameters.VSCODE_ARCH, 'ia32') }} - powershell: .\build\azure-pipelines\win32\listprocesses.bat displayName: Diagnostics after smoke test run diff --git a/build/azure-pipelines/win32/product-build-win32.yml b/build/azure-pipelines/win32/product-build-win32.yml index 24957abbaa2..a6eea8852e6 100644 --- a/build/azure-pipelines/win32/product-build-win32.yml +++ b/build/azure-pipelines/win32/product-build-win32.yml @@ -1,6 +1,8 @@ parameters: - name: VSCODE_QUALITY type: string + - name: VSCODE_ARCH + type: string - name: VSCODE_CIBUILD type: boolean - name: VSCODE_RUN_UNIT_TESTS @@ -188,6 +190,7 @@ steps: - template: product-build-win32-test.yml parameters: VSCODE_QUALITY: ${{ parameters.VSCODE_QUALITY }} + VSCODE_ARCH: ${{ parameters.VSCODE_ARCH }} VSCODE_RUN_UNIT_TESTS: ${{ parameters.VSCODE_RUN_UNIT_TESTS }} VSCODE_RUN_INTEGRATION_TESTS: ${{ parameters.VSCODE_RUN_INTEGRATION_TESTS }} VSCODE_RUN_SMOKE_TESTS: ${{ parameters.VSCODE_RUN_SMOKE_TESTS }} From 85fa846ddb3acd62eaa8125a860a20385c427b18 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 11 Jul 2023 14:29:12 +0200 Subject: [PATCH 317/926] dispose test instantiation service --- .../node/installGalleryExtensionTask.test.ts | 21 ++++++++++--------- .../extensionsWorkbenchService.test.ts | 2 +- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/vs/platform/extensionManagement/test/node/installGalleryExtensionTask.test.ts b/src/vs/platform/extensionManagement/test/node/installGalleryExtensionTask.test.ts index fec8c3ce3e7..a4ca27c91fc 100644 --- a/src/vs/platform/extensionManagement/test/node/installGalleryExtensionTask.test.ts +++ b/src/vs/platform/extensionManagement/test/node/installGalleryExtensionTask.test.ts @@ -70,8 +70,9 @@ class TestInstallGalleryExtensionTask extends InstallGalleryExtensionTask { constructor( extension: IGalleryExtension, extensionDownloader: ExtensionsDownloader, + disposables: DisposableStore, ) { - const instantiationService = new TestInstantiationService(); + const instantiationService = disposables.add(new TestInstantiationService()); const logService = instantiationService.stub(ILogService, new NullLogService()); const fileService = instantiationService.stub(IFileService, new FileService(logService)); const fileSystemProvider = new InMemoryFileSystemProvider(); @@ -131,7 +132,7 @@ suite('InstallGalleryExtensionTask Tests', () => { teardown(() => disposables.clear()); test('if verification is enabled by default, the task completes', async () => { - const testObject = new TestInstallGalleryExtensionTask(aGalleryExtension('a', { isSigned: true }), anExtensionsDownloader({ isSignatureVerificationEnabled: true, verificationResult: true, didExecute: true })); + const testObject = new TestInstallGalleryExtensionTask(aGalleryExtension('a', { isSigned: true }), anExtensionsDownloader({ isSignatureVerificationEnabled: true, verificationResult: true, didExecute: true }), disposables); await testObject.run(); @@ -140,7 +141,7 @@ suite('InstallGalleryExtensionTask Tests', () => { }); test('if verification is enabled in stable, the task completes', async () => { - const testObject = new TestInstallGalleryExtensionTask(aGalleryExtension('a', { isSigned: true }), anExtensionsDownloader({ isSignatureVerificationEnabled: true, verificationResult: true, didExecute: true, quality: 'stable' })); + const testObject = new TestInstallGalleryExtensionTask(aGalleryExtension('a', { isSigned: true }), anExtensionsDownloader({ isSignatureVerificationEnabled: true, verificationResult: true, didExecute: true, quality: 'stable' }), disposables); await testObject.run(); @@ -149,7 +150,7 @@ suite('InstallGalleryExtensionTask Tests', () => { }); test('if verification is disabled by setting set to false, the task skips verification', async () => { - const testObject = new TestInstallGalleryExtensionTask(aGalleryExtension('a', { isSigned: true }), anExtensionsDownloader({ isSignatureVerificationEnabled: false, verificationResult: 'error', didExecute: false })); + const testObject = new TestInstallGalleryExtensionTask(aGalleryExtension('a', { isSigned: true }), anExtensionsDownloader({ isSignatureVerificationEnabled: false, verificationResult: 'error', didExecute: false }), disposables); await testObject.run(); @@ -158,7 +159,7 @@ suite('InstallGalleryExtensionTask Tests', () => { }); test('if verification is disabled because the module is not loaded, the task skips verification', async () => { - const testObject = new TestInstallGalleryExtensionTask(aGalleryExtension('a', { isSigned: true }), anExtensionsDownloader({ isSignatureVerificationEnabled: true, verificationResult: false, didExecute: false })); + const testObject = new TestInstallGalleryExtensionTask(aGalleryExtension('a', { isSigned: true }), anExtensionsDownloader({ isSignatureVerificationEnabled: true, verificationResult: false, didExecute: false }), disposables); await testObject.run(); @@ -168,7 +169,7 @@ suite('InstallGalleryExtensionTask Tests', () => { test('if verification fails to execute, the task completes', async () => { const errorCode = 'ENOENT'; - const testObject = new TestInstallGalleryExtensionTask(aGalleryExtension('a', { isSigned: true }), anExtensionsDownloader({ isSignatureVerificationEnabled: true, verificationResult: errorCode, didExecute: false })); + const testObject = new TestInstallGalleryExtensionTask(aGalleryExtension('a', { isSigned: true }), anExtensionsDownloader({ isSignatureVerificationEnabled: true, verificationResult: errorCode, didExecute: false }), disposables); await testObject.run(); @@ -179,7 +180,7 @@ suite('InstallGalleryExtensionTask Tests', () => { test('if verification fails', async () => { const errorCode = 'IntegrityCheckFailed'; - const testObject = new TestInstallGalleryExtensionTask(aGalleryExtension('a', { isSigned: true }), anExtensionsDownloader({ isSignatureVerificationEnabled: true, verificationResult: errorCode, didExecute: true })); + const testObject = new TestInstallGalleryExtensionTask(aGalleryExtension('a', { isSigned: true }), anExtensionsDownloader({ isSignatureVerificationEnabled: true, verificationResult: errorCode, didExecute: true }), disposables); await testObject.run(); @@ -188,7 +189,7 @@ suite('InstallGalleryExtensionTask Tests', () => { }); test('if verification succeeds, the task completes', async () => { - const testObject = new TestInstallGalleryExtensionTask(aGalleryExtension('a', { isSigned: true }), anExtensionsDownloader({ isSignatureVerificationEnabled: true, verificationResult: true, didExecute: true })); + const testObject = new TestInstallGalleryExtensionTask(aGalleryExtension('a', { isSigned: true }), anExtensionsDownloader({ isSignatureVerificationEnabled: true, verificationResult: true, didExecute: true }), disposables); await testObject.run(); @@ -197,7 +198,7 @@ suite('InstallGalleryExtensionTask Tests', () => { }); test('task completes for unsigned extension', async () => { - const testObject = new TestInstallGalleryExtensionTask(aGalleryExtension('a', { isSigned: false }), anExtensionsDownloader({ isSignatureVerificationEnabled: true, verificationResult: true, didExecute: false })); + const testObject = new TestInstallGalleryExtensionTask(aGalleryExtension('a', { isSigned: false }), anExtensionsDownloader({ isSignatureVerificationEnabled: true, verificationResult: true, didExecute: false }), disposables); await testObject.run(); @@ -206,7 +207,7 @@ suite('InstallGalleryExtensionTask Tests', () => { }); test('task completes for an unsigned extension even when signature verification throws error', async () => { - const testObject = new TestInstallGalleryExtensionTask(aGalleryExtension('a', { isSigned: false }), anExtensionsDownloader({ isSignatureVerificationEnabled: true, verificationResult: 'error', didExecute: true })); + const testObject = new TestInstallGalleryExtensionTask(aGalleryExtension('a', { isSigned: false }), anExtensionsDownloader({ isSignatureVerificationEnabled: true, verificationResult: 'error', didExecute: true }), disposables); await testObject.run(); diff --git a/src/vs/workbench/contrib/extensions/test/electron-sandbox/extensionsWorkbenchService.test.ts b/src/vs/workbench/contrib/extensions/test/electron-sandbox/extensionsWorkbenchService.test.ts index 700a07c66bf..36450a6c665 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-sandbox/extensionsWorkbenchService.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-sandbox/extensionsWorkbenchService.test.ts @@ -69,7 +69,7 @@ suite('ExtensionsWorkbenchServiceTest', () => { uninstallEvent = suiteDisposables.add(new Emitter()); didUninstallEvent = suiteDisposables.add(new Emitter()); - instantiationService = new TestInstantiationService(); + instantiationService = suiteDisposables.add(new TestInstantiationService()); instantiationService.stub(ITelemetryService, NullTelemetryService); instantiationService.stub(ILogService, NullLogService); instantiationService.stub(IProgressService, ProgressService); From 846162c69627b8b313b79361252b20c1d19fbd21 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 11 Jul 2023 14:30:12 +0200 Subject: [PATCH 318/926] dispose test instantiation service --- src/vs/platform/userDataSync/test/common/userDataSyncClient.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts b/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts index 44b3b40774a..c9398a04e20 100644 --- a/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts +++ b/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts @@ -52,7 +52,7 @@ export class UserDataSyncClient extends Disposable { constructor(readonly testServer: UserDataSyncTestServer = new UserDataSyncTestServer()) { super(); - this.instantiationService = new TestInstantiationService(); + this.instantiationService = this._register(new TestInstantiationService()); } async setUp(empty: boolean = false): Promise { From 29f37cc240b394df6fd5494514a153e55686d81b Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Tue, 11 Jul 2023 14:54:06 +0200 Subject: [PATCH 319/926] adding code in order to make the picked color representation disappear when resizing if it overlaps with the icon --- .../colorPicker/browser/colorPickerWidget.ts | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/contrib/colorPicker/browser/colorPickerWidget.ts b/src/vs/editor/contrib/colorPicker/browser/colorPickerWidget.ts index 24b6e6408b3..6466b09430c 100644 --- a/src/vs/editor/contrib/colorPicker/browser/colorPickerWidget.ts +++ b/src/vs/editor/contrib/colorPicker/browser/colorPickerWidget.ts @@ -22,10 +22,22 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; const $ = dom.$; +function elementsOverlap(el1: HTMLElement, el2: HTMLElement) { + const domRect1 = el1.getBoundingClientRect(); + const domRect2 = el2.getBoundingClientRect(); + return !( + domRect1.top > domRect2.bottom || + domRect1.right < domRect2.left || + domRect1.bottom < domRect2.top || + domRect1.left > domRect2.right + ); +} + export class ColorPickerHeader extends Disposable { private readonly _domNode: HTMLElement; private readonly _pickedColorNode: HTMLElement; + private readonly _pickedColorRepresentation: HTMLElement; private readonly _originalColorNode: HTMLElement; private readonly _closeButton: CloseButton | null = null; private backgroundColor: Color; @@ -37,6 +49,8 @@ export class ColorPickerHeader extends Disposable { dom.append(container, this._domNode); this._pickedColorNode = dom.append(this._domNode, $('.picked-color')); + this._pickedColorRepresentation = dom.append(this._pickedColorNode, document.createElement('div')); + const icon = dom.append(this._pickedColorNode, $('.codicon.codicon-color-mode')); const tooltip = localize('clickToToggleColorOptions', "Click to toggle color options (rgb/hsl/hex)"); this._pickedColorNode.setAttribute('title', tooltip); @@ -66,6 +80,16 @@ export class ColorPickerHeader extends Disposable { this._domNode.classList.add('standalone-colorpicker'); this._closeButton = this._register(new CloseButton(this._domNode)); } + + const resizeObserver = new ResizeObserver(() => { + this._pickedColorRepresentation.style.display = 'block'; + if (elementsOverlap(this._pickedColorRepresentation, icon)) { + this._pickedColorRepresentation.style.display = 'none'; + } else { + this._pickedColorRepresentation.style.display = 'block'; + } + }); + resizeObserver.observe(this._domNode); } public get domNode(): HTMLElement { @@ -91,8 +115,7 @@ export class ColorPickerHeader extends Disposable { } private onDidChangePresentation(): void { - this._pickedColorNode.textContent = this.model.presentation ? this.model.presentation.label : ''; - this._pickedColorNode.prepend($('.codicon.codicon-color-mode')); + this._pickedColorRepresentation.textContent = this.model.presentation ? this.model.presentation.label : ''; } } From cac438f1bf0cfe56baa2b1fd2ff0f0f82cf85da0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Tue, 11 Jul 2023 14:58:06 +0200 Subject: [PATCH 320/926] fix windows build (#187575) --- build/azure-pipelines/win32/product-build-win32.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/azure-pipelines/win32/product-build-win32.yml b/build/azure-pipelines/win32/product-build-win32.yml index a6eea8852e6..d517793c4d6 100644 --- a/build/azure-pipelines/win32/product-build-win32.yml +++ b/build/azure-pipelines/win32/product-build-win32.yml @@ -297,7 +297,7 @@ steps: $env:ESRPAADUsername = "$(esrp-aad-username)" $env:ESRPAADPassword = "$(esrp-aad-password)" exec { yarn gulp "vscode-win32-$(VSCODE_ARCH)-user-setup" --sign } - $SetupPath = ".build\win32-$(VSCODE_ARCH)\user-setup\VSCodeSetup-$(VSCODE_ARCH)-$(VSCODE_VERSION).exe" + $SetupPath = ".build\win32-$(VSCODE_ARCH)\user-setup\VSCodeUserSetup-$(VSCODE_ARCH)-$(VSCODE_VERSION).exe" mv .build\win32-$(VSCODE_ARCH)\user-setup\VSCodeSetup.exe $SetupPath echo "##vso[task.setvariable variable=USER_SETUP_PATH]$SetupPath" displayName: Build user setup From 65b21886d07e23bc34aba2acdec46c7f43e3ffb2 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Tue, 11 Jul 2023 15:04:55 +0200 Subject: [PATCH 321/926] Removing the check on top and bottom which are not needed --- .../editor/contrib/colorPicker/browser/colorPickerWidget.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/vs/editor/contrib/colorPicker/browser/colorPickerWidget.ts b/src/vs/editor/contrib/colorPicker/browser/colorPickerWidget.ts index 6466b09430c..2f267fcaf0c 100644 --- a/src/vs/editor/contrib/colorPicker/browser/colorPickerWidget.ts +++ b/src/vs/editor/contrib/colorPicker/browser/colorPickerWidget.ts @@ -22,13 +22,11 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; const $ = dom.$; -function elementsOverlap(el1: HTMLElement, el2: HTMLElement) { +function elementsOverlapHorizontally(el1: HTMLElement, el2: HTMLElement) { const domRect1 = el1.getBoundingClientRect(); const domRect2 = el2.getBoundingClientRect(); return !( - domRect1.top > domRect2.bottom || domRect1.right < domRect2.left || - domRect1.bottom < domRect2.top || domRect1.left > domRect2.right ); } @@ -83,7 +81,7 @@ export class ColorPickerHeader extends Disposable { const resizeObserver = new ResizeObserver(() => { this._pickedColorRepresentation.style.display = 'block'; - if (elementsOverlap(this._pickedColorRepresentation, icon)) { + if (elementsOverlapHorizontally(this._pickedColorRepresentation, icon)) { this._pickedColorRepresentation.style.display = 'none'; } else { this._pickedColorRepresentation.style.display = 'block'; From 8baa7ed3e3c9d91579fa376031e2d8ef3620b610 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Tue, 11 Jul 2023 15:09:39 +0200 Subject: [PATCH 322/926] Git - fix repository detection regression (#187578) --- extensions/git/src/model.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/extensions/git/src/model.ts b/extensions/git/src/model.ts index cf22a284bbb..eacfca8f035 100644 --- a/extensions/git/src/model.ts +++ b/extensions/git/src/model.ts @@ -767,11 +767,14 @@ export class Model implements IBranchProtectionProviderRegistry, IRemoteSourcePu private async getRepositoryExact(repoPath: string): Promise { const repoPathCanonical = await fs.promises.realpath(repoPath, { encoding: 'utf8' }); - const openRepository = this.openRepositories.find(async r => { - const rootPathCanonical = await fs.promises.realpath(r.repository.root, { encoding: 'utf8' }); - return pathEquals(rootPathCanonical, repoPathCanonical); - }); - return openRepository?.repository; + + for (const openRepository of this.openRepositories) { + const rootPathCanonical = await fs.promises.realpath(openRepository.repository.root, { encoding: 'utf8' }); + if (pathEquals(rootPathCanonical, repoPathCanonical)) { + return openRepository.repository; + } + } + return undefined; } private getOpenRepository(repository: Repository): OpenRepository | undefined; From d363a2fd7666c5efcad397132ebe21bb7e4f35e4 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Tue, 11 Jul 2023 15:37:41 +0200 Subject: [PATCH 323/926] setting some minimum dimensions on the content hover. --- src/vs/editor/contrib/hover/browser/contentHover.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/vs/editor/contrib/hover/browser/contentHover.ts b/src/vs/editor/contrib/hover/browser/contentHover.ts index f03cf308b61..5cc8ab5028e 100644 --- a/src/vs/editor/contrib/hover/browser/contentHover.ts +++ b/src/vs/editor/contrib/hover/browser/contentHover.ts @@ -504,6 +504,7 @@ export class ContentHoverWidget extends ResizableContentWidget { this._hoverFocusedKey.set(false); })); this._setHoverData(undefined); + this._setMinimumDimensions(); this._layout(); this._editor.addContentWidget(this); } @@ -518,6 +519,15 @@ export class ContentHoverWidget extends ResizableContentWidget { return ContentHoverWidget.ID; } + private _setMinimumDimensions(): void { + const width = 50; + const height = this._editor.getOption(EditorOption.lineHeight) + 8; + const contentsDomNode = this._hover.contentsDomNode; + contentsDomNode.style.minWidth = width + 'px'; + contentsDomNode.style.minHeight = height + 'px'; + this._resizableNode.minSize = new dom.Dimension(width, height); + } + private static _applyDimensions(container: HTMLElement, width: number | string, height: number | string): void { const transformedWidth = typeof width === 'number' ? `${width}px` : width; const transformedHeight = typeof height === 'number' ? `${height}px` : height; From 4a7a02ab9f83c9abc97c0d21237884c75d113a90 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Tue, 11 Jul 2023 15:42:07 +0200 Subject: [PATCH 324/926] GitHub - update branchProtection setting default (#187583) --- extensions/github/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/github/package.json b/extensions/github/package.json index da9ccd0084e..6e655fca983 100644 --- a/extensions/github/package.json +++ b/extensions/github/package.json @@ -136,7 +136,7 @@ "github.branchProtection": { "type": "boolean", "scope": "resource", - "default": false, + "default": true, "description": "%config.branchProtection%" }, "github.gitAuthentication": { From b17ce63968bdc18ad7f9694119505b6030c5b702 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Tue, 11 Jul 2023 15:43:18 +0200 Subject: [PATCH 325/926] changing the code so as to use dom.Dimension instead of storing in two separate fields --- .../editor/contrib/hover/browser/contentHover.ts | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/vs/editor/contrib/hover/browser/contentHover.ts b/src/vs/editor/contrib/hover/browser/contentHover.ts index dc2e6c00983..b094de8a8d3 100644 --- a/src/vs/editor/contrib/hover/browser/contentHover.ts +++ b/src/vs/editor/contrib/hover/browser/contentHover.ts @@ -459,8 +459,7 @@ const CONTAINER_HEIGHT_PADDING = 6; export class ContentHoverWidget extends ResizableContentWidget { public static ID = 'editor.contrib.resizableContentHoverWidget'; - private static _lastHeight: number = 0; - private static _lastWidth: number = 0; + private static _lastDimensions: dom.Dimension = new dom.Dimension(0, 0); private _visibleData: ContentHoverVisibleData | undefined; private _positionPreference: ContentWidgetPositionPreference | undefined; @@ -584,8 +583,7 @@ export class ContentHoverWidget extends ResizableContentWidget { } protected override _resize(size: dom.Dimension): void { - ContentHoverWidget._lastHeight = size.height; - ContentHoverWidget._lastWidth = size.width; + ContentHoverWidget._lastDimensions = new dom.Dimension(size.width, size.height); this._setAdjustedHoverWidgetDimensions(size); this._resizableNode.layout(size.height, size.width); this._setResizableNodeMaxDimensions(); @@ -658,8 +656,8 @@ export class ContentHoverWidget extends ResizableContentWidget { } private _layout(): void { - const height = Math.max(this._editor.getLayoutInfo().height / 4, 250, ContentHoverWidget._lastHeight); - const width = Math.max(this._editor.getLayoutInfo().width * 0.66, 500, ContentHoverWidget._lastWidth); + const height = Math.max(this._editor.getLayoutInfo().height / 4, 250, ContentHoverWidget._lastDimensions.height); + const width = Math.max(this._editor.getLayoutInfo().width * 0.66, 500, ContentHoverWidget._lastDimensions.width); const { fontSize, lineHeight } = this._editor.getOption(EditorOption.fontInfo); const contentsDomNode = this._hover.contentsDomNode; contentsDomNode.style.fontSize = `${fontSize}px`; @@ -685,8 +683,8 @@ export class ContentHoverWidget extends ResizableContentWidget { } private _updateContentsDomNodeMaxDimensions() { - const width = Math.max(this._editor.getLayoutInfo().width * 0.66, 500, ContentHoverWidget._lastWidth); - const height = Math.max(this._editor.getLayoutInfo().height / 4, 250, ContentHoverWidget._lastHeight); + const height = Math.max(this._editor.getLayoutInfo().height / 4, 250, ContentHoverWidget._lastDimensions.height); + const width = Math.max(this._editor.getLayoutInfo().width * 0.66, 500, ContentHoverWidget._lastDimensions.width); this._setContentsDomNodeMaxDimensions(width, height); } From f8782370c90ec98467dc3590bcfa2eeb1f218107 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Tue, 11 Jul 2023 15:52:32 +0200 Subject: [PATCH 326/926] fix windows archive packages (#187585) --- build/azure-pipelines/win32/product-build-win32.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build/azure-pipelines/win32/product-build-win32.yml b/build/azure-pipelines/win32/product-build-win32.yml index d517793c4d6..9cadfab45a0 100644 --- a/build/azure-pipelines/win32/product-build-win32.yml +++ b/build/azure-pipelines/win32/product-build-win32.yml @@ -253,7 +253,7 @@ steps: $ErrorActionPreference = "Stop" $ArchivePath = ".build\win32-$(VSCODE_ARCH)\VSCode-win32-$(VSCODE_ARCH)-$(VSCODE_VERSION).zip" New-Item -ItemType Directory -Path .build\win32-$(VSCODE_ARCH) -Force - exec { 7z.exe a -tzip $ArchivePath -x!CodeSignSummary*.md ..\VSCode-win32-$(VSCODE_ARCH) -r } + exec { 7z.exe a -tzip $ArchivePath -x!CodeSignSummary*.md ..\VSCode-win32-$(VSCODE_ARCH)\* -r } echo "##vso[task.setvariable variable=CLIENT_PATH]$ArchivePath" condition: and(succeededOrFailed(), eq(variables['BUILT_CLIENT'], 'true')) displayName: Package client @@ -263,7 +263,7 @@ steps: $ErrorActionPreference = "Stop" $ArchivePath = ".build\win32-$(VSCODE_ARCH)\vscode-server-win32-$(VSCODE_ARCH).zip" New-Item -ItemType Directory -Path .build\win32-$(VSCODE_ARCH) -Force - exec { 7z.exe a -tzip $ArchivePath ..\vscode-server-win32-$(VSCODE_ARCH) -r } + exec { 7z.exe a -tzip $ArchivePath ..\vscode-server-win32-$(VSCODE_ARCH)\* -r } echo "##vso[task.setvariable variable=SERVER_PATH]$ArchivePath" condition: and(succeededOrFailed(), eq(variables['BUILT_SERVER'], 'true')) displayName: Package server @@ -273,7 +273,7 @@ steps: $ErrorActionPreference = "Stop" $ArchivePath = ".build\win32-$(VSCODE_ARCH)\vscode-server-win32-$(VSCODE_ARCH)-web.zip" New-Item -ItemType Directory -Path .build\win32-$(VSCODE_ARCH) -Force - exec { 7z.exe a -tzip $ArchivePath ..\vscode-server-win32-$(VSCODE_ARCH)-web -r } + exec { 7z.exe a -tzip $ArchivePath ..\vscode-server-win32-$(VSCODE_ARCH)-web\* -r } echo "##vso[task.setvariable variable=WEB_PATH]$ArchivePath" condition: and(succeededOrFailed(), eq(variables['BUILT_WEB'], 'true')) displayName: Package server (web) From dca0fc0fd75130b17837019cb951234390573518 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Tue, 11 Jul 2023 16:18:06 +0200 Subject: [PATCH 327/926] GitHub - do not clear branch protection if the user is not signed in (#187588) --- extensions/github/src/branchProtection.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/extensions/github/src/branchProtection.ts b/extensions/github/src/branchProtection.ts index 8966b41c155..0a26afdfe78 100644 --- a/extensions/github/src/branchProtection.ts +++ b/extensions/github/src/branchProtection.ts @@ -204,12 +204,14 @@ export class GithubBranchProtectionProvider implements BranchProtectionProvider if (err instanceof AuthenticationError) { // A GitHub authentication session could be missing if the user has not yet - // signed in with their GitHub account or they have signed out. In this case - // we have to clear the branch protection information. - this.branchProtection = branchProtection; - this._onDidChangeBranchProtection.fire(this.repository.rootUri); + // signed in with their GitHub account or they have signed out. If there is + // branch protection information we have to clear it. + if (this.branchProtection.length !== 0) { + this.branchProtection = branchProtection; + this._onDidChangeBranchProtection.fire(this.repository.rootUri); - await this.globalState.update(this.globalStateKey, undefined); + await this.globalState.update(this.globalStateKey, undefined); + } } } } From 9f5cd8e693811fdd9459b177a08896728708ab4c Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 11 Jul 2023 07:19:10 -0700 Subject: [PATCH 328/926] xterm@5.3.0-beta.7 Fixes #184894 --- package.json | 6 +++--- remote/package.json | 6 +++--- remote/web/package.json | 4 ++-- remote/web/yarn.lock | 16 ++++++++-------- remote/yarn.lock | 24 ++++++++++++------------ yarn.lock | 24 ++++++++++++------------ 6 files changed, 40 insertions(+), 40 deletions(-) diff --git a/package.json b/package.json index 4f04e9ddb1d..e77d3b770d6 100644 --- a/package.json +++ b/package.json @@ -94,14 +94,14 @@ "vscode-oniguruma": "1.7.0", "vscode-regexpp": "^3.1.0", "vscode-textmate": "9.0.0", - "xterm": "5.3.0-beta.3", + "xterm": "5.3.0-beta.7", "xterm-addon-canvas": "0.5.0-beta.2", - "xterm-addon-image": "0.4.1", + "xterm-addon-image": "0.4.2", "xterm-addon-search": "0.13.0-beta.2", "xterm-addon-serialize": "0.11.0-beta.2", "xterm-addon-unicode11": "0.5.0", "xterm-addon-webgl": "0.16.0-beta.2", - "xterm-headless": "5.3.0-beta.3", + "xterm-headless": "5.3.0-beta.7", "yauzl": "^2.9.2", "yazl": "^2.4.3" }, diff --git a/remote/package.json b/remote/package.json index 832b8d5dbd3..9b92d007a47 100644 --- a/remote/package.json +++ b/remote/package.json @@ -26,14 +26,14 @@ "vscode-oniguruma": "1.7.0", "vscode-regexpp": "^3.1.0", "vscode-textmate": "9.0.0", - "xterm": "5.3.0-beta.3", + "xterm": "5.3.0-beta.7", "xterm-addon-canvas": "0.5.0-beta.2", - "xterm-addon-image": "0.4.1", + "xterm-addon-image": "0.4.2", "xterm-addon-search": "0.13.0-beta.2", "xterm-addon-serialize": "0.11.0-beta.2", "xterm-addon-unicode11": "0.5.0", "xterm-addon-webgl": "0.16.0-beta.2", - "xterm-headless": "5.3.0-beta.3", + "xterm-headless": "5.3.0-beta.7", "yauzl": "^2.9.2", "yazl": "^2.4.3" } diff --git a/remote/web/package.json b/remote/web/package.json index db7baab82d3..64b5c929970 100644 --- a/remote/web/package.json +++ b/remote/web/package.json @@ -11,9 +11,9 @@ "tas-client-umd": "0.1.8", "vscode-oniguruma": "1.7.0", "vscode-textmate": "9.0.0", - "xterm": "5.3.0-beta.3", + "xterm": "5.3.0-beta.7", "xterm-addon-canvas": "0.5.0-beta.2", - "xterm-addon-image": "0.4.1", + "xterm-addon-image": "0.4.2", "xterm-addon-search": "0.13.0-beta.2", "xterm-addon-unicode11": "0.5.0", "xterm-addon-webgl": "0.16.0-beta.2" diff --git a/remote/web/yarn.lock b/remote/web/yarn.lock index 2f879c22bf6..e2a8a2d0fbe 100644 --- a/remote/web/yarn.lock +++ b/remote/web/yarn.lock @@ -73,10 +73,10 @@ xterm-addon-canvas@0.5.0-beta.2: resolved "https://registry.yarnpkg.com/xterm-addon-canvas/-/xterm-addon-canvas-0.5.0-beta.2.tgz#1b83c2a9a306766c47a4f80b8c65cc9ee5f5a5c4" integrity sha512-oTb/2krdbHYGxH2X6yiBZzAB/1WB+apUu4nXHdhBnht20bl8E+YVWqg95D4o0Gl+QJI+XOfB3mqmWaBx1x531A== -xterm-addon-image@0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/xterm-addon-image/-/xterm-addon-image-0.4.1.tgz#ec8f750af48005ad641c1128fa1f551ac198472a" - integrity sha512-iJpYyvtbHg4oXSv+D6J73ZfCjnboZpbZ567MLplXDBlYSUknv3kvPTfVMPJATV7Zsx7+bDgyXboCh9vsDf/m/w== +xterm-addon-image@0.4.2: + version "0.4.2" + resolved "https://registry.yarnpkg.com/xterm-addon-image/-/xterm-addon-image-0.4.2.tgz#d25bb3a8c543d014accbfbbc875927f54d5f43b9" + integrity sha512-QVuH5CGlValtFMVorb71TnMV7kQLqc9+V5oyOMNvFqW1NiOBNo/d0WVvMU8qOd2vsAdTbhjvUO8SPptZ5TJHYw== xterm-addon-search@0.13.0-beta.2: version "0.13.0-beta.2" @@ -93,7 +93,7 @@ xterm-addon-webgl@0.16.0-beta.2: resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.16.0-beta.2.tgz#30489ef235405255ee54077002c90553531870d8" integrity sha512-DAt4E/QI1w34ToBhcDj0vaZOAHOO+ffwMt2HGDAB7amPXRcMb0LBIjLpyZhB9sD4tbIgsE0vuqZi1R9vKxZwbg== -xterm@5.3.0-beta.3: - version "5.3.0-beta.3" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0-beta.3.tgz#1a1aaf9a57afe4dcf86e87d8dc85e80a41d68644" - integrity sha512-NGxpV25U2W/KKk6M5V2OXuLgrKY+w05ABi66ZEYuCTi7ux1Qv0z+jm7bkgzk1pGGiTVLG+90OGr2nrhbFr5Y4w== +xterm@5.3.0-beta.7: + version "5.3.0-beta.7" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0-beta.7.tgz#ee638fe60382e6b13f36dec2355c3530ff1c4c34" + integrity sha512-wd+QHHfCVSZMSH7ah8WSeCu7VAqZcAD5pEAOiQxCg4VfXu+L//QOycqlWecgr0SFui2X9LshfD+cI9J3FcHRNA== diff --git a/remote/yarn.lock b/remote/yarn.lock index 531ba983765..9b9acd2200d 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -841,10 +841,10 @@ xterm-addon-canvas@0.5.0-beta.2: resolved "https://registry.yarnpkg.com/xterm-addon-canvas/-/xterm-addon-canvas-0.5.0-beta.2.tgz#1b83c2a9a306766c47a4f80b8c65cc9ee5f5a5c4" integrity sha512-oTb/2krdbHYGxH2X6yiBZzAB/1WB+apUu4nXHdhBnht20bl8E+YVWqg95D4o0Gl+QJI+XOfB3mqmWaBx1x531A== -xterm-addon-image@0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/xterm-addon-image/-/xterm-addon-image-0.4.1.tgz#ec8f750af48005ad641c1128fa1f551ac198472a" - integrity sha512-iJpYyvtbHg4oXSv+D6J73ZfCjnboZpbZ567MLplXDBlYSUknv3kvPTfVMPJATV7Zsx7+bDgyXboCh9vsDf/m/w== +xterm-addon-image@0.4.2: + version "0.4.2" + resolved "https://registry.yarnpkg.com/xterm-addon-image/-/xterm-addon-image-0.4.2.tgz#d25bb3a8c543d014accbfbbc875927f54d5f43b9" + integrity sha512-QVuH5CGlValtFMVorb71TnMV7kQLqc9+V5oyOMNvFqW1NiOBNo/d0WVvMU8qOd2vsAdTbhjvUO8SPptZ5TJHYw== xterm-addon-search@0.13.0-beta.2: version "0.13.0-beta.2" @@ -866,15 +866,15 @@ xterm-addon-webgl@0.16.0-beta.2: resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.16.0-beta.2.tgz#30489ef235405255ee54077002c90553531870d8" integrity sha512-DAt4E/QI1w34ToBhcDj0vaZOAHOO+ffwMt2HGDAB7amPXRcMb0LBIjLpyZhB9sD4tbIgsE0vuqZi1R9vKxZwbg== -xterm-headless@5.3.0-beta.3: - version "5.3.0-beta.3" - resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-5.3.0-beta.3.tgz#153cf330082f4b2aae64ff736ef0b62d93c30da8" - integrity sha512-4i/bpFoAn4D4ZA4g8RKrJdhq2EcB1HN2E25yUg3omRbWCOZ2Gp9nAn+62LYzX5rvGqdNbpUTRJLX0lKwEFyLFw== +xterm-headless@5.3.0-beta.7: + version "5.3.0-beta.7" + resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-5.3.0-beta.7.tgz#92704cecf937a18f2223b16efcab8c4e9e8a9e62" + integrity sha512-8LDY/cId73q3w6jMmiGU6HtJlfIhtqu/oWlDj/9wUr8SQnCQJwPBzu6RizzQRz1lzMN+8WHdzrHjKcNtayZbdw== -xterm@5.3.0-beta.3: - version "5.3.0-beta.3" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0-beta.3.tgz#1a1aaf9a57afe4dcf86e87d8dc85e80a41d68644" - integrity sha512-NGxpV25U2W/KKk6M5V2OXuLgrKY+w05ABi66ZEYuCTi7ux1Qv0z+jm7bkgzk1pGGiTVLG+90OGr2nrhbFr5Y4w== +xterm@5.3.0-beta.7: + version "5.3.0-beta.7" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0-beta.7.tgz#ee638fe60382e6b13f36dec2355c3530ff1c4c34" + integrity sha512-wd+QHHfCVSZMSH7ah8WSeCu7VAqZcAD5pEAOiQxCg4VfXu+L//QOycqlWecgr0SFui2X9LshfD+cI9J3FcHRNA== yallist@^4.0.0: version "4.0.0" diff --git a/yarn.lock b/yarn.lock index e8975eef7f6..2c3a2fc5531 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10730,10 +10730,10 @@ xterm-addon-canvas@0.5.0-beta.2: resolved "https://registry.yarnpkg.com/xterm-addon-canvas/-/xterm-addon-canvas-0.5.0-beta.2.tgz#1b83c2a9a306766c47a4f80b8c65cc9ee5f5a5c4" integrity sha512-oTb/2krdbHYGxH2X6yiBZzAB/1WB+apUu4nXHdhBnht20bl8E+YVWqg95D4o0Gl+QJI+XOfB3mqmWaBx1x531A== -xterm-addon-image@0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/xterm-addon-image/-/xterm-addon-image-0.4.1.tgz#ec8f750af48005ad641c1128fa1f551ac198472a" - integrity sha512-iJpYyvtbHg4oXSv+D6J73ZfCjnboZpbZ567MLplXDBlYSUknv3kvPTfVMPJATV7Zsx7+bDgyXboCh9vsDf/m/w== +xterm-addon-image@0.4.2: + version "0.4.2" + resolved "https://registry.yarnpkg.com/xterm-addon-image/-/xterm-addon-image-0.4.2.tgz#d25bb3a8c543d014accbfbbc875927f54d5f43b9" + integrity sha512-QVuH5CGlValtFMVorb71TnMV7kQLqc9+V5oyOMNvFqW1NiOBNo/d0WVvMU8qOd2vsAdTbhjvUO8SPptZ5TJHYw== xterm-addon-search@0.13.0-beta.2: version "0.13.0-beta.2" @@ -10755,15 +10755,15 @@ xterm-addon-webgl@0.16.0-beta.2: resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.16.0-beta.2.tgz#30489ef235405255ee54077002c90553531870d8" integrity sha512-DAt4E/QI1w34ToBhcDj0vaZOAHOO+ffwMt2HGDAB7amPXRcMb0LBIjLpyZhB9sD4tbIgsE0vuqZi1R9vKxZwbg== -xterm-headless@5.3.0-beta.3: - version "5.3.0-beta.3" - resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-5.3.0-beta.3.tgz#153cf330082f4b2aae64ff736ef0b62d93c30da8" - integrity sha512-4i/bpFoAn4D4ZA4g8RKrJdhq2EcB1HN2E25yUg3omRbWCOZ2Gp9nAn+62LYzX5rvGqdNbpUTRJLX0lKwEFyLFw== +xterm-headless@5.3.0-beta.7: + version "5.3.0-beta.7" + resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-5.3.0-beta.7.tgz#92704cecf937a18f2223b16efcab8c4e9e8a9e62" + integrity sha512-8LDY/cId73q3w6jMmiGU6HtJlfIhtqu/oWlDj/9wUr8SQnCQJwPBzu6RizzQRz1lzMN+8WHdzrHjKcNtayZbdw== -xterm@5.3.0-beta.3: - version "5.3.0-beta.3" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0-beta.3.tgz#1a1aaf9a57afe4dcf86e87d8dc85e80a41d68644" - integrity sha512-NGxpV25U2W/KKk6M5V2OXuLgrKY+w05ABi66ZEYuCTi7ux1Qv0z+jm7bkgzk1pGGiTVLG+90OGr2nrhbFr5Y4w== +xterm@5.3.0-beta.7: + version "5.3.0-beta.7" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0-beta.7.tgz#ee638fe60382e6b13f36dec2355c3530ff1c4c34" + integrity sha512-wd+QHHfCVSZMSH7ah8WSeCu7VAqZcAD5pEAOiQxCg4VfXu+L//QOycqlWecgr0SFui2X9LshfD+cI9J3FcHRNA== y18n@^3.2.1: version "3.2.2" From 9b8e6edaf1280d0f698d363b3f238365ede2c02a Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Tue, 11 Jul 2023 16:28:24 +0200 Subject: [PATCH 329/926] using instead the CSS to set size --- .../colorPicker/browser/colorPicker.css | 6 +++++ .../colorPicker/browser/colorPickerWidget.ts | 22 ++----------------- 2 files changed, 8 insertions(+), 20 deletions(-) diff --git a/src/vs/editor/contrib/colorPicker/browser/colorPicker.css b/src/vs/editor/contrib/colorPicker/browser/colorPicker.css index 62f891080fe..9b588999dd7 100644 --- a/src/vs/editor/contrib/colorPicker/browser/colorPicker.css +++ b/src/vs/editor/contrib/colorPicker/browser/colorPicker.css @@ -50,6 +50,12 @@ flex: 1; } +.colorpicker-header .picked-color .picked-color-representation { + white-space: nowrap; + margin-left: 30px; + margin-right: 5px; +} + .colorpicker-header .picked-color .codicon { color: inherit; font-size: 14px; diff --git a/src/vs/editor/contrib/colorPicker/browser/colorPickerWidget.ts b/src/vs/editor/contrib/colorPicker/browser/colorPickerWidget.ts index 2f267fcaf0c..efc7053fe26 100644 --- a/src/vs/editor/contrib/colorPicker/browser/colorPickerWidget.ts +++ b/src/vs/editor/contrib/colorPicker/browser/colorPickerWidget.ts @@ -22,15 +22,6 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; const $ = dom.$; -function elementsOverlapHorizontally(el1: HTMLElement, el2: HTMLElement) { - const domRect1 = el1.getBoundingClientRect(); - const domRect2 = el2.getBoundingClientRect(); - return !( - domRect1.right < domRect2.left || - domRect1.left > domRect2.right - ); -} - export class ColorPickerHeader extends Disposable { private readonly _domNode: HTMLElement; @@ -48,7 +39,8 @@ export class ColorPickerHeader extends Disposable { this._pickedColorNode = dom.append(this._domNode, $('.picked-color')); this._pickedColorRepresentation = dom.append(this._pickedColorNode, document.createElement('div')); - const icon = dom.append(this._pickedColorNode, $('.codicon.codicon-color-mode')); + this._pickedColorRepresentation.classList.add('picked-color-representation'); + dom.append(this._pickedColorNode, $('.codicon.codicon-color-mode')); const tooltip = localize('clickToToggleColorOptions', "Click to toggle color options (rgb/hsl/hex)"); this._pickedColorNode.setAttribute('title', tooltip); @@ -78,16 +70,6 @@ export class ColorPickerHeader extends Disposable { this._domNode.classList.add('standalone-colorpicker'); this._closeButton = this._register(new CloseButton(this._domNode)); } - - const resizeObserver = new ResizeObserver(() => { - this._pickedColorRepresentation.style.display = 'block'; - if (elementsOverlapHorizontally(this._pickedColorRepresentation, icon)) { - this._pickedColorRepresentation.style.display = 'none'; - } else { - this._pickedColorRepresentation.style.display = 'block'; - } - }); - resizeObserver.observe(this._domNode); } public get domNode(): HTMLElement { From e4426ebf914be37c912b68b5e53d1536c22dddfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Tue, 11 Jul 2023 16:32:31 +0200 Subject: [PATCH 330/926] Add command to show update release notes (#187590) closes #187150 --- .../workbench/contrib/update/browser/update.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/vs/workbench/contrib/update/browser/update.ts b/src/vs/workbench/contrib/update/browser/update.ts index a43125b04a6..39daccab96f 100644 --- a/src/vs/workbench/contrib/update/browser/update.ts +++ b/src/vs/workbench/contrib/update/browser/update.ts @@ -461,6 +461,23 @@ export class UpdateContribution extends Disposable implements IWorkbenchContribu when: CONTEXT_UPDATE_STATE.isEqualTo(StateType.Ready) }); + CommandsRegistry.registerCommand('update.showUpdateReleaseNotes', () => { + if (this.updateService.state.type !== StateType.Ready) { + return; + } + + const version = this.updateService.state.update.version; + this.instantiationService.invokeFunction(accessor => showReleaseNotes(accessor, version)); + }); + MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { + group: '7_update', + command: { + id: 'update.showUpdateReleaseNotes', + title: nls.localize('showUpdateReleaseNotes', "Show Update Release Notes") + }, + when: CONTEXT_UPDATE_STATE.isEqualTo(StateType.Ready) + }); + CommandsRegistry.registerCommand('_update.state', () => { return this.state; }); From 0e34605600704b03450880c4cf8368f312049d6a Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Tue, 11 Jul 2023 16:36:41 +0200 Subject: [PATCH 331/926] changing the word representation to presentation --- src/vs/editor/contrib/colorPicker/browser/colorPicker.css | 2 +- .../contrib/colorPicker/browser/colorPickerWidget.ts | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vs/editor/contrib/colorPicker/browser/colorPicker.css b/src/vs/editor/contrib/colorPicker/browser/colorPicker.css index 9b588999dd7..f84c7ba6f2b 100644 --- a/src/vs/editor/contrib/colorPicker/browser/colorPicker.css +++ b/src/vs/editor/contrib/colorPicker/browser/colorPicker.css @@ -50,7 +50,7 @@ flex: 1; } -.colorpicker-header .picked-color .picked-color-representation { +.colorpicker-header .picked-color .picked-color-presentation { white-space: nowrap; margin-left: 30px; margin-right: 5px; diff --git a/src/vs/editor/contrib/colorPicker/browser/colorPickerWidget.ts b/src/vs/editor/contrib/colorPicker/browser/colorPickerWidget.ts index efc7053fe26..6c8cc3ab4e0 100644 --- a/src/vs/editor/contrib/colorPicker/browser/colorPickerWidget.ts +++ b/src/vs/editor/contrib/colorPicker/browser/colorPickerWidget.ts @@ -26,7 +26,7 @@ export class ColorPickerHeader extends Disposable { private readonly _domNode: HTMLElement; private readonly _pickedColorNode: HTMLElement; - private readonly _pickedColorRepresentation: HTMLElement; + private readonly _pickedColorPresentation: HTMLElement; private readonly _originalColorNode: HTMLElement; private readonly _closeButton: CloseButton | null = null; private backgroundColor: Color; @@ -38,8 +38,8 @@ export class ColorPickerHeader extends Disposable { dom.append(container, this._domNode); this._pickedColorNode = dom.append(this._domNode, $('.picked-color')); - this._pickedColorRepresentation = dom.append(this._pickedColorNode, document.createElement('div')); - this._pickedColorRepresentation.classList.add('picked-color-representation'); + this._pickedColorPresentation = dom.append(this._pickedColorNode, document.createElement('div')); + this._pickedColorPresentation.classList.add('picked-color-presentation'); dom.append(this._pickedColorNode, $('.codicon.codicon-color-mode')); const tooltip = localize('clickToToggleColorOptions', "Click to toggle color options (rgb/hsl/hex)"); @@ -95,7 +95,7 @@ export class ColorPickerHeader extends Disposable { } private onDidChangePresentation(): void { - this._pickedColorRepresentation.textContent = this.model.presentation ? this.model.presentation.label : ''; + this._pickedColorPresentation.textContent = this.model.presentation ? this.model.presentation.label : ''; } } From 42313dd3810a000e04b0e679327db7c1292b8ffe Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 11 Jul 2023 07:43:47 -0700 Subject: [PATCH 332/926] fix #186520 --- .../contrib/accessibility/browser/accessibleView.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index 534d08414ea..83c4e4c65f3 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -23,8 +23,9 @@ import { IOpenerService } from 'vs/platform/opener/common/opener'; import { SelectionClipboardContributionID } from 'vs/workbench/contrib/codeEditor/browser/selectionClipboard'; import { getSimpleEditorOptions } from 'vs/workbench/contrib/codeEditor/browser/simpleEditorOptions'; -const enum DIMENSION_DEFAULT { - WIDTH = 800 +const enum DEFAULT { + WIDTH = 800, + TOP = 25 } export interface IAccessibleContentProvider { @@ -95,7 +96,7 @@ class AccessibleView extends Disposable { show(provider: IAccessibleContentProvider): void { const delegate: IContextViewDelegate = { - getAnchor: () => { return { x: (window.innerWidth / 2) - (DIMENSION_DEFAULT.WIDTH / 2), y: (window.innerHeight / 2) - (this.editorWidget.getContentHeight() / 2) }; }, + getAnchor: () => { return { x: (window.innerWidth / 2) - (DEFAULT.WIDTH / 2), y: DEFAULT.TOP }; }, render: (container) => { return this._render(provider, container); }, @@ -161,7 +162,7 @@ class AccessibleView extends Disposable { } private _layout(): void { - this._editorWidget.layout({ width: DIMENSION_DEFAULT.WIDTH, height: this._editorWidget.getContentHeight() }); + this._editorWidget.layout({ width: DEFAULT.WIDTH, height: this._editorWidget.getContentHeight() }); } private async _getTextModel(resource: URI): Promise { From 00105d2ddbb156022a43183246efb3005b77e9d8 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 11 Jul 2023 08:45:12 -0700 Subject: [PATCH 333/926] Exclude `azurerepos:` files from JS/TS intellisense (#187521) Fixes #186385 --- .../src/configuration/fileSchemes.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/extensions/typescript-language-features/src/configuration/fileSchemes.ts b/extensions/typescript-language-features/src/configuration/fileSchemes.ts index 93417cde8e4..9fb572c7175 100644 --- a/extensions/typescript-language-features/src/configuration/fileSchemes.ts +++ b/extensions/typescript-language-features/src/configuration/fileSchemes.ts @@ -10,6 +10,7 @@ export const file = 'file'; export const untitled = 'untitled'; export const git = 'git'; export const github = 'github'; +export const azurerepos = 'azurerepos'; /** Live share scheme */ export const vsls = 'vsls'; @@ -39,4 +40,5 @@ export const disabledSchemes = new Set([ git, vsls, github, + azurerepos, ]); From 343270e7bbff02d7d30cf6732568d08b83650d52 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 11 Jul 2023 08:48:33 -0700 Subject: [PATCH 334/926] fix #187596 --- .../browser/accessibility.contribution.ts | 7 ++---- .../accessibility/browser/accessibleView.ts | 22 +++---------------- .../browser/actions/chatAccessibilityHelp.ts | 4 +--- .../contrib/chat/browser/chat.contribution.ts | 4 +--- .../browser/notebookAccessibilityHelp.ts | 4 +--- .../terminal.accessibility.contribution.ts | 3 +-- 6 files changed, 9 insertions(+), 35 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibility.contribution.ts b/src/vs/workbench/contrib/accessibility/browser/accessibility.contribution.ts index d3ddc03a653..a21370d2b7f 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibility.contribution.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibility.contribution.ts @@ -109,8 +109,7 @@ class EditorAccessibilityHelpContribution extends Disposable { await commandService.executeCommand(NEW_UNTITLED_FILE_COMMAND_ID); codeEditor = codeEditorService.getActiveCodeEditor()!; } - accessibleViewService.registerProvider(instantiationService.createInstance(AccessibilityHelpProvider, codeEditor)); - accessibleViewService.show('editor'); + accessibleViewService.show(instantiationService.createInstance(AccessibilityHelpProvider, codeEditor)); })); } } @@ -136,18 +135,16 @@ class HoverAccessibleViewContribution extends Disposable { if (!controller || !content) { return false; } - const provider = accessibleViewService.registerProvider({ + accessibleViewService.show({ id: 'hover', provideContent() { return content; }, onClose() { - provider.dispose(); controller.focus(); }, options: { ariaLabel: localize('hoverAccessibleView', "Hover Accessible View"), language: 'typescript', type: AccessibleViewType.View } }); - accessibleViewService.show('hover'); return true; })); } diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index 534d08414ea..c2c24ea5374 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -5,7 +5,7 @@ import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; -import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { IEditorConstructionOptions } from 'vs/editor/browser/config/editorConfiguration'; import { EditorExtensionsRegistry } from 'vs/editor/browser/editorExtensions'; @@ -39,8 +39,7 @@ export const IAccessibleViewService = createDecorator('a export interface IAccessibleViewService { readonly _serviceBrand: undefined; - show(providerId: string): AccessibleView; - registerProvider(provider: IAccessibleContentProvider): IDisposable; + show(provider: IAccessibleContentProvider): void; } export const enum AccessibleViewType { @@ -175,9 +174,6 @@ class AccessibleView extends Disposable { export class AccessibleViewService extends Disposable implements IAccessibleViewService { declare readonly _serviceBrand: undefined; - - private _providers: Map = new Map(); - private _accessibleView: AccessibleView | undefined; constructor( @@ -186,22 +182,10 @@ export class AccessibleViewService extends Disposable implements IAccessibleView super(); } - registerProvider(provider: IAccessibleContentProvider): IDisposable { - this._providers.set(provider.id, provider); - return toDisposable(() => { - this._providers.delete(provider.id); - }); - } - - show(providerId: string): AccessibleView { + show(provider: IAccessibleContentProvider): void { if (!this._accessibleView) { this._accessibleView = this._register(this._instantiationService.createInstance(AccessibleView)); } - const provider = this._providers.get(providerId); - if (!provider) { - throw new Error(`No accessible view provider with id: ${providerId}`); - } this._accessibleView.show(provider); - return this._accessibleView; } } diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatAccessibilityHelp.ts b/src/vs/workbench/contrib/chat/browser/actions/chatAccessibilityHelp.ts index 983e6b6f86b..c505c984639 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatAccessibilityHelp.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatAccessibilityHelp.ts @@ -71,7 +71,7 @@ export async function runAccessibilityHelpAction(accessor: ServicesAccessor, edi const cachedPosition = inputEditor.getPosition(); inputEditor.getSupportedActions(); const helpText = getAccessibilityHelpText(accessor, type); - const provider = accessibleViewService.registerProvider({ + accessibleViewService.show({ id: type, provideContent: () => helpText, onClose: () => { @@ -81,9 +81,7 @@ export async function runAccessibilityHelpAction(accessor: ServicesAccessor, edi } else if (type === 'inlineChat') { InlineChatController.get(editor)?.focus(); } - provider.dispose(); }, options: { type: AccessibleViewType.HelpMenu, ariaLabel: type === 'panelChat' ? localize('chat-help-label', "Chat accessibility help") : localize('inline-chat-label', "Inline chat accessibility help") } }); - accessibleViewService.show(type); } diff --git a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts index 757a933abe6..7c4aa2fde5c 100644 --- a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts +++ b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts @@ -135,16 +135,14 @@ class ChatAccessibleViewContribution extends Disposable { if (!responseContent) { return false; } - const provider = accessibleViewService.registerProvider({ + accessibleViewService.show({ id: 'panelChat', provideContent(): string { return responseContent; }, onClose() { widget.focus(focusedItem); - provider.dispose(); }, options: { ariaLabel: nls.localize('chatAccessibleView', "Chat Accessible View"), language: 'typescript', type: AccessibleViewType.View } }); - accessibleViewService.show('panelChat'); return true; }, CONTEXT_IN_CHAT_SESSION)); } diff --git a/src/vs/workbench/contrib/notebook/browser/notebookAccessibilityHelp.ts b/src/vs/workbench/contrib/notebook/browser/notebookAccessibilityHelp.ts index 0bd04764dd6..2a3bce2a6e2 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookAccessibilityHelp.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookAccessibilityHelp.ts @@ -45,14 +45,12 @@ function descriptionForCommand(commandId: string, msg: string, noKbMsg: string, export async function runAccessibilityHelpAction(accessor: ServicesAccessor, editor: ICodeEditor): Promise { const accessibleViewService = accessor.get(IAccessibleViewService); const helpText = getAccessibilityHelpText(accessor); - const provider = accessibleViewService.registerProvider({ + accessibleViewService.show({ id: 'notebook', provideContent: () => helpText, onClose: () => { editor.focus(); - provider.dispose(); }, options: { type: AccessibleViewType.HelpMenu, ariaLabel: 'Notebook accessibility help' } }); - accessibleViewService.show('notebook'); } diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts index b096c56526a..bdf148b2ef2 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts @@ -73,8 +73,7 @@ export class TerminalAccessibilityHelpContribution extends Disposable { if (!terminal) { return; } - accessibleViewService.registerProvider(instantiationService.createInstance(TerminalAccessibleContentProvider, instance, terminal)); - accessibleViewService.show('terminal'); + accessibleViewService.show(instantiationService.createInstance(TerminalAccessibleContentProvider, instance, terminal)); }, TerminalContextKeys.focus)); } } From aab7d04e5ec8b76fb69f2a03c15308f430737bfc Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 11 Jul 2023 09:20:35 -0700 Subject: [PATCH 335/926] fix #186679 --- .../workbench/contrib/accessibility/browser/accessibleView.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index 83c4e4c65f3..51b16af3dc5 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -22,6 +22,7 @@ import { IInstantiationService, createDecorator } from 'vs/platform/instantiatio import { IOpenerService } from 'vs/platform/opener/common/opener'; import { SelectionClipboardContributionID } from 'vs/workbench/contrib/codeEditor/browser/selectionClipboard'; import { getSimpleEditorOptions } from 'vs/workbench/contrib/codeEditor/browser/simpleEditorOptions'; +import { alert } from 'vs/base/browser/ui/aria/aria'; const enum DEFAULT { WIDTH = 800, @@ -142,6 +143,7 @@ class AccessibleView extends Disposable { // Delay to allow the context view to hide #186514 setTimeout(() => provider.onClose(), 100); } else if (e.keyCode === KeyCode.KeyD && this._configurationService.getValue(settingKey)) { + alert(localize('disableAccessibilityHelp', '{0} accessibility verbosity is now disabled', provider.id)); this._configurationService.updateValue(settingKey, false); } e.stopPropagation(); From ae50dc394e9f9a485c562b5e639abcbd4330ca1e Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Tue, 11 Jul 2023 18:43:56 +0200 Subject: [PATCH 336/926] Removes shadow from diff review --- .../browser/widget/diffEditorWidget2/diffEditorWidget2.ts | 1 - .../editor/browser/widget/diffEditorWidget2/diffReview.ts | 7 ------- 2 files changed, 8 deletions(-) diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts index 7930581e426..91f99d55c6b 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts @@ -159,7 +159,6 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { this._reviewPane = this._register(this._instantiationService.createInstance(DiffReview2, this)); this.elements.root.appendChild(this._reviewPane.domNode.domNode); - this.elements.root.appendChild(this._reviewPane.shadow.domNode); this.elements.root.appendChild(this._reviewPane.actionBarContainer.domNode); reviewPaneObservable.set(this._reviewPane, undefined); diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffReview.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffReview.ts index ed1c345f5e5..82359ffaef0 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffReview.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffReview.ts @@ -87,7 +87,6 @@ export class DiffReview2 extends Disposable { private readonly _diffEditor: DiffEditorWidget2; private get _isVisible() { return this._isVisibleObs.get(); } - public readonly shadow: FastDomNode; private readonly _actionBar: ActionBar; public readonly actionBarContainer: FastDomNode; public readonly domNode: FastDomNode; @@ -109,9 +108,6 @@ export class DiffReview2 extends Disposable { super(); this._diffEditor = diffEditor; - this.shadow = createFastDomNode(document.createElement('div')); - this.shadow.setClassName('diff-review-shadow'); - this.actionBarContainer = createFastDomNode(document.createElement('div')); this.actionBarContainer.setClassName('diff-review-actions'); this._actionBar = this._register(new ActionBar( @@ -343,9 +339,6 @@ export class DiffReview2 extends Disposable { this._top = top; this._height = height; - this.shadow.setTop(top - 6); - this.shadow.setWidth(width); - this.shadow.setHeight(this._isVisible ? 6 : 0); this.domNode.setTop(top); this.domNode.setWidth(width); this.domNode.setHeight(height); From 3661871d343bb5bdbe03a8585da8fa284327ccc5 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 11 Jul 2023 10:00:41 -0700 Subject: [PATCH 337/926] fix #188679 --- src/vs/editor/common/standaloneStrings.ts | 10 +++--- .../browser/accessibility.contribution.ts | 23 +------------ .../accessibility/browser/accessibleView.ts | 33 +++++++++++++++++-- .../browser/accessibility/accessibility.ts | 7 +++- 4 files changed, 44 insertions(+), 29 deletions(-) diff --git a/src/vs/editor/common/standaloneStrings.ts b/src/vs/editor/common/standaloneStrings.ts index 5bc2ddd91de..f2251207711 100644 --- a/src/vs/editor/common/standaloneStrings.ts +++ b/src/vs/editor/common/standaloneStrings.ts @@ -12,10 +12,12 @@ export namespace AccessibilityHelpNLS { export const editableDiffEditor = nls.localize("editableDiffEditor", "You are in a pane of a diff editor."); export const readonlyEditor = nls.localize("readonlyEditor", "You are in a read-only code editor"); export const editableEditor = nls.localize("editableEditor", "You are in a code editor"); - export const changeConfigToOnMac = nls.localize("changeConfigToOnMac", "To configure the editor to be optimized for usage with a Screen Reader press Command+E now."); - export const changeConfigToOnWinLinux = nls.localize("changeConfigToOnWinLinux", "To configure the editor to be optimized for usage with a Screen Reader press Control+E now."); - export const auto_on = nls.localize("auto_on", "The editor is configured to be optimized for usage with a Screen Reader."); - export const auto_off = nls.localize("auto_off", "The editor is configured to never be optimized for usage with a Screen Reader"); + export const changeConfigToOnMac = nls.localize("changeConfigToOnMac", "To configure the application to be optimized for usage with a Screen Reader press Command+E now."); + export const changeConfigToOnWinLinux = nls.localize("changeConfigToOnWinLinux", "To configure the application to be optimized for usage with a Screen Reader press Control+E now."); + export const auto_on = nls.localize("auto_on", "The application is configured to be optimized for usage with a Screen Reader."); + export const auto_off = nls.localize("auto_off", "The application is configured to never be optimized for usage with a Screen Reader"); + export const screenReaderModeEnabled = nls.localize("screenReaderModeEnabled", "Screen Reader Optimized Mode enabled."); + export const screenReaderModeDisabled = nls.localize("screenReaderModeDisabled", "Screen Reader Optimized Mode disabled."); export const tabFocusModeOnMsg = nls.localize("tabFocusModeOnMsg", "Pressing Tab in the current editor will move focus to the next focusable element. Toggle this behavior by pressing {0}."); export const tabFocusModeOnMsgNoKb = nls.localize("tabFocusModeOnMsgNoKb", "Pressing Tab in the current editor will move focus to the next focusable element. The command {0} is currently not triggerable by a keybinding."); export const tabFocusModeOffMsg = nls.localize("tabFocusModeOffMsg", "Pressing Tab in the current editor will insert the tab character. Toggle this behavior by pressing {0}."); diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibility.contribution.ts b/src/vs/workbench/contrib/accessibility/browser/accessibility.contribution.ts index d3ddc03a653..b0fca3289b2 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibility.contribution.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibility.contribution.ts @@ -10,13 +10,11 @@ import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { AccessibilityHelpNLS } from 'vs/editor/common/standaloneStrings'; import { ToggleTabFocusModeAction } from 'vs/editor/contrib/toggleTabFocusMode/browser/toggleTabFocusMode'; import { localize } from 'vs/nls'; -import { AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { AccessibilityHelpAction, AccessibleViewAction, registerAccessibilityConfiguration } from 'vs/workbench/contrib/accessibility/browser/accessibilityContribution'; import { AccessibleViewService, AccessibleViewType, IAccessibleContentProvider, IAccessibleViewOptions, IAccessibleViewService } from 'vs/workbench/contrib/accessibility/browser/accessibleView'; import * as strings from 'vs/base/common/strings'; -import * as platform from 'vs/base/common/platform'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; @@ -29,10 +27,9 @@ import { withNullAsUndefined } from 'vs/base/common/types'; registerAccessibilityConfiguration(); registerSingleton(IAccessibleViewService, AccessibleViewService, InstantiationType.Delayed); -class AccessibilityHelpProvider extends Disposable implements IAccessibleContentProvider { +class AccessibilityHelpProvider implements IAccessibleContentProvider { onClose() { this._editor.focus(); - this.dispose(); } options: IAccessibleViewOptions = { type: AccessibleViewType.HelpMenu, ariaLabel: localize('editor-help', "editor accessibility help"), readMoreUrl: 'https://go.microsoft.com/fwlink/?linkid=851010' }; id: string = 'editor'; @@ -40,7 +37,6 @@ class AccessibilityHelpProvider extends Disposable implements IAccessibleContent private readonly _editor: ICodeEditor, @IKeybindingService private readonly _keybindingService: IKeybindingService ) { - super(); } private _descriptionForCommand(commandId: string, msg: string, noKbMsg: string): string { @@ -69,23 +65,6 @@ class AccessibilityHelpProvider extends Disposable implements IAccessibleContent } } - const turnOnMessage = ( - platform.isMacintosh - ? AccessibilityHelpNLS.changeConfigToOnMac - : AccessibilityHelpNLS.changeConfigToOnWinLinux - ); - switch (options.get(EditorOption.accessibilitySupport)) { - case AccessibilitySupport.Unknown: - content.push(turnOnMessage); - break; - case AccessibilitySupport.Enabled: - content.push(AccessibilityHelpNLS.auto_on); - break; - case AccessibilitySupport.Disabled: - content.push(AccessibilityHelpNLS.auto_off, turnOnMessage); - break; - } - if (options.get(EditorOption.tabFocusMode)) { content.push(this._descriptionForCommand(ToggleTabFocusModeAction.ID, AccessibilityHelpNLS.tabFocusModeOnMsg, AccessibilityHelpNLS.tabFocusModeOnMsgNoKb)); } else { diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index 83c4e4c65f3..b613bfb3cc9 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -6,6 +6,7 @@ import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { isMacintosh } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; import { IEditorConstructionOptions } from 'vs/editor/browser/config/editorConfiguration'; import { EditorExtensionsRegistry } from 'vs/editor/browser/editorExtensions'; @@ -15,6 +16,7 @@ import { IModelService } from 'vs/editor/common/services/model'; import { AccessibilityHelpNLS } from 'vs/editor/common/standaloneStrings'; import { LinkDetector } from 'vs/editor/contrib/links/browser/links'; import { localize } from 'vs/nls'; +import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IContextViewDelegate, IContextViewService } from 'vs/platform/contextview/browser/contextView'; @@ -22,6 +24,7 @@ import { IInstantiationService, createDecorator } from 'vs/platform/instantiatio import { IOpenerService } from 'vs/platform/opener/common/opener'; import { SelectionClipboardContributionID } from 'vs/workbench/contrib/codeEditor/browser/selectionClipboard'; import { getSimpleEditorOptions } from 'vs/workbench/contrib/codeEditor/browser/simpleEditorOptions'; +import { alert } from 'vs/base/browser/ui/aria/aria'; const enum DEFAULT { WIDTH = 800, @@ -62,13 +65,16 @@ class AccessibleView extends Disposable { private _accessiblityHelpIsShown: IContextKey; get editorWidget() { return this._editorWidget; } private _editorContainer: HTMLElement; + private _currentProvider: IAccessibleContentProvider | undefined; + constructor( @IOpenerService private readonly _openerService: IOpenerService, @IInstantiationService private readonly _instantiationService: IInstantiationService, @IConfigurationService private readonly _configurationService: IConfigurationService, @IModelService private readonly _modelService: IModelService, @IContextViewService private readonly _contextViewService: IContextViewService, - @IContextKeyService private readonly _contextKeyService: IContextKeyService + @IContextKeyService private readonly _contextKeyService: IContextKeyService, + @IAccessibilityService private readonly _accessibilityService: IAccessibilityService ) { super(); this._accessiblityHelpIsShown = accessibilityHelpIsShown.bindTo(this._contextKeyService); @@ -92,6 +98,11 @@ class AccessibleView extends Disposable { fontFamily: 'var(--monaco-monospace-font)' }; this._editorWidget = this._register(this._instantiationService.createInstance(CodeEditorWidget, this._editorContainer, editorOptions, codeEditorWidgetOptions)); + this._register(this._accessibilityService.onDidChangeScreenReaderOptimized(() => { + if (this._currentProvider && this._accessiblityHelpIsShown.get()) { + this.show(this._currentProvider); + } + })); } show(provider: IAccessibleContentProvider): void { @@ -104,6 +115,7 @@ class AccessibleView extends Disposable { if (provider.options.type === AccessibleViewType.HelpMenu) { this._accessiblityHelpIsShown.reset(); } + this._currentProvider = undefined; } }; this._contextViewService.showContextView(delegate); @@ -113,11 +125,28 @@ class AccessibleView extends Disposable { } private _render(provider: IAccessibleContentProvider, container: HTMLElement): IDisposable { + this._currentProvider = provider; const settingKey = `accessibility.verbosity.${provider.id}`; const value = this._configurationService.getValue(settingKey); const readMoreLink = provider.options.readMoreUrl ? localize("openDoc", "\nPress H now to open a browser window with more information related to accessibility.\n") : ''; const disableHelpHint = provider.options.type === AccessibleViewType.HelpMenu && !!value ? localize('disable-help-hint', '\nTo disable the `accessibility.verbosity` hint for this feature, press D now.\n') : '\n'; - const fragment = provider.provideContent() + readMoreLink + disableHelpHint + localize('exit-tip', 'Exit this menu via the Escape key.'); + const accessibilitySupport = this._accessibilityService.isScreenReaderOptimized(); + let message = ''; + if (provider.options.type === AccessibleViewType.HelpMenu) { + const turnOnMessage = ( + isMacintosh + ? AccessibilityHelpNLS.changeConfigToOnMac + : AccessibilityHelpNLS.changeConfigToOnWinLinux + ); + if (accessibilitySupport) { + message = AccessibilityHelpNLS.auto_on; + } else { + message = AccessibilityHelpNLS.auto_off + '\n' + turnOnMessage; + } + message += '\n'; + } + + const fragment = message + provider.provideContent() + readMoreLink + disableHelpHint + localize('exit-tip', 'Exit this menu via the Escape key.'); this._getTextModel(URI.from({ path: `accessible-view-${provider.id}`, scheme: 'accessible-view', fragment })).then((model) => { if (!model) { diff --git a/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.ts b/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.ts index fd67c1689b1..903406a2947 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.ts @@ -12,6 +12,9 @@ import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; import { accessibilityHelpIsShown } from 'vs/workbench/contrib/accessibility/browser/accessibleView'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { alert } from 'vs/base/browser/ui/aria/aria'; +import { AccessibilityHelpNLS } from 'vs/editor/common/standaloneStrings'; + class ToggleScreenReaderMode extends Action2 { constructor() { @@ -30,7 +33,9 @@ class ToggleScreenReaderMode extends Action2 { async run(accessor: ServicesAccessor): Promise { const accessibiiltyService = accessor.get(IAccessibilityService); const configurationService = accessor.get(IConfigurationService); - configurationService.updateValue('editor.accessibilitySupport', accessibiiltyService.isScreenReaderOptimized() ? 'off' : 'on', ConfigurationTarget.USER); + const isScreenReaderOptimized = accessibiiltyService.isScreenReaderOptimized(); + configurationService.updateValue('editor.accessibilitySupport', isScreenReaderOptimized ? 'off' : 'on', ConfigurationTarget.USER); + alert(isScreenReaderOptimized ? AccessibilityHelpNLS.screenReaderModeDisabled : AccessibilityHelpNLS.screenReaderModeEnabled); } } From 974269ebfa103e9979c739e755ae961993a1fb7d Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 11 Jul 2023 10:08:41 -0700 Subject: [PATCH 338/926] update menu when verbosity setting is changed --- .../contrib/accessibility/browser/accessibleView.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index b613bfb3cc9..adc37344156 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -103,6 +103,11 @@ class AccessibleView extends Disposable { this.show(this._currentProvider); } })); + this._register(this._configurationService.onDidChangeConfiguration(e => { + if (this._currentProvider && this._accessiblityHelpIsShown.get() && e.affectsConfiguration(`accessibility.verbosity.${this._currentProvider.id}`)) { + this.show(this._currentProvider); + } + })); } show(provider: IAccessibleContentProvider): void { From 49ab813b200d6a3ba2b4df91f4dbc7ed783e8a46 Mon Sep 17 00:00:00 2001 From: Michael Lively Date: Tue, 11 Jul 2023 10:14:26 -0700 Subject: [PATCH 339/926] rename cell toolbar's stickyScroll (#187611) --- .../cellParts/{stickyScroll.ts => cellToolbarStickyScroll.ts} | 2 +- .../contrib/notebook/browser/view/cellParts/cellToolbars.ts | 4 ++-- .../notebook/browser/view/cellParts/codeCellRunToolbar.ts | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) rename src/vs/workbench/contrib/notebook/browser/view/cellParts/{stickyScroll.ts => cellToolbarStickyScroll.ts} (88%) diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/stickyScroll.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbarStickyScroll.ts similarity index 88% rename from src/vs/workbench/contrib/notebook/browser/view/cellParts/stickyScroll.ts rename to src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbarStickyScroll.ts index e70bddadbbd..227be9967a2 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/stickyScroll.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbarStickyScroll.ts @@ -7,7 +7,7 @@ import { IDisposable } from 'vs/base/common/lifecycle'; import { clamp } from 'vs/base/common/numbers'; import { ICellViewModel, INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; -export function registerStickyScroll(notebookEditor: INotebookEditor, cell: ICellViewModel, element: HTMLElement, opts?: { extraOffset?: number; min?: number }): IDisposable { +export function registerCellToolbarStickyScroll(notebookEditor: INotebookEditor, cell: ICellViewModel, element: HTMLElement, opts?: { extraOffset?: number; min?: number }): IDisposable { const extraOffset = opts?.extraOffset ?? 0; const min = opts?.min ?? 0; diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars.ts index 26f50e3b0f8..389a8ea3661 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars.ts @@ -20,7 +20,7 @@ import { INotebookCellActionContext } from 'vs/workbench/contrib/notebook/browse import { ICellViewModel, INotebookEditorDelegate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CodiconActionViewItem } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellActionView'; import { CellOverlayPart } from 'vs/workbench/contrib/notebook/browser/view/cellPart'; -import { registerStickyScroll } from 'vs/workbench/contrib/notebook/browser/view/cellParts/stickyScroll'; +import { registerCellToolbarStickyScroll } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbarStickyScroll'; import { WorkbenchToolBar } from 'vs/platform/actions/browser/toolbar'; export class BetweenCellToolbar extends CellOverlayPart { @@ -196,7 +196,7 @@ export class CellTitleToolbarPart extends CellOverlayPart { override didRenderCell(element: ICellViewModel): void { const model = this._initializeModel(); const view = this._initialize(model, element); - this.cellDisposables.add(registerStickyScroll(this._notebookEditor, element, this.toolbarContainer, { extraOffset: 4, min: -14 })); + this.cellDisposables.add(registerCellToolbarStickyScroll(this._notebookEditor, element, this.toolbarContainer, { extraOffset: 4, min: -14 })); this.updateContext(view, { ui: true, diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCellRunToolbar.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCellRunToolbar.ts index 549a1a228ca..48974ad10a5 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCellRunToolbar.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCellRunToolbar.ts @@ -20,7 +20,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { INotebookCellActionContext } from 'vs/workbench/contrib/notebook/browser/controller/coreActions'; import { ICellViewModel, INotebookEditorDelegate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellContentPart } from 'vs/workbench/contrib/notebook/browser/view/cellPart'; -import { registerStickyScroll } from 'vs/workbench/contrib/notebook/browser/view/cellParts/stickyScroll'; +import { registerCellToolbarStickyScroll } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbarStickyScroll'; import { NOTEBOOK_CELL_EXECUTION_STATE, NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_TYPE, NOTEBOOK_EDITOR_FOCUSED } from 'vs/workbench/contrib/notebook/common/notebookContextKeys'; export class RunToolbar extends CellContentPart { @@ -56,7 +56,7 @@ export class RunToolbar extends CellContentPart { } override didRenderCell(element: ICellViewModel): void { - this.cellDisposables.add(registerStickyScroll(this.notebookEditor, element, this.runButtonContainer)); + this.cellDisposables.add(registerCellToolbarStickyScroll(this.notebookEditor, element, this.runButtonContainer)); this.toolbar.context = { ui: true, From 1bbf7dd7a114d6f32fe8d5312df41569449551db Mon Sep 17 00:00:00 2001 From: Raymond Zhao <7199958+rzhao271@users.noreply.github.com> Date: Tue, 11 Jul 2023 10:30:16 -0700 Subject: [PATCH 340/926] Add revealSetting and focusSearch params (#187079) --- .../browser/preferences.contribution.ts | 34 ++++++++++++++++--- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts index ef83f66b598..174bb727550 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts @@ -6,7 +6,7 @@ import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { Disposable, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; -import { isObject } from 'vs/base/common/types'; +import { isBoolean, isObject, isString } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import 'vs/css!./media/preferences'; import { EditorContributionInstantiation, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; @@ -126,6 +126,19 @@ const category = { value: nls.localize('preferences', "Preferences"), original: interface IOpenSettingsActionOptions { openToSide?: boolean; query?: string; + revealSetting?: { + key: string; + edit?: boolean; + }; + focusSearch?: boolean; +} + +function sanitizeBoolean(arg: any): boolean | undefined { + return isBoolean(arg) ? arg : undefined; +} + +function sanitizeString(arg: any): string | undefined { + return isString(arg) ? arg : undefined; } function sanitizeOpenSettingsArgs(args: any): IOpenSettingsActionOptions { @@ -133,10 +146,23 @@ function sanitizeOpenSettingsArgs(args: any): IOpenSettingsActionOptions { args = {}; } - return { - openToSide: args.openToSide, - query: args.query + let sanitizedObject: IOpenSettingsActionOptions = { + focusSearch: sanitizeBoolean(args?.focusSearch), + openToSide: sanitizeBoolean(args?.openToSide), + query: sanitizeString(args?.query) }; + + if (isString(args?.revealSetting?.key)) { + sanitizedObject = { + ...sanitizedObject, + revealSetting: { + key: args.revealSetting.key, + edit: sanitizeBoolean(args.revealSetting?.edit) + } + }; + } + + return sanitizedObject; } class PreferencesActionsContribution extends Disposable implements IWorkbenchContribution { From 224ac6f56d0e29b0d9030af9c55e44549afc5581 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 11 Jul 2023 11:15:32 -0700 Subject: [PATCH 341/926] Require canvas/webgl use to enable image support Part of #187198 --- .../contrib/terminal/browser/xterm/xtermTerminal.ts | 11 ++++++++--- .../contrib/terminal/common/terminalConfiguration.ts | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts index ef2640b6490..0eb141ad11a 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts @@ -40,6 +40,7 @@ import { SuggestAddon } from 'vs/workbench/contrib/terminal/browser/xterm/sugges import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { debounce } from 'vs/base/common/decorators'; const enum RenderConstants { /** @@ -262,7 +263,6 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal, ID // Load addons this._updateUnicodeVersion(); - this._refreshImageAddon(); this._markNavigationAddon = this._instantiationService.createInstance(MarkNavigationAddon, _capabilities); this.raw.loadAddon(this._markNavigationAddon); this._decorationAddon = this._instantiationService.createInstance(DecorationAddon, this._capabilities); @@ -405,7 +405,6 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal, ID } } } - this._refreshImageAddon(); } private _shouldLoadWebgl(): boolean { @@ -640,6 +639,7 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal, ID this._logService.info(`Webgl lost context, disposing of webgl renderer`); this._disposeOfWebglRenderer(); }); + this._refreshImageAddon(); // Uncomment to add the texture atlas to the DOM // setTimeout(() => { // if (this._webglAddon?.textureAtlas) { @@ -679,6 +679,7 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal, ID XtermTerminal._suggestedRendererType = 'dom'; this._disposeOfCanvasRenderer(); } + this._refreshImageAddon(); } protected async _getCanvasAddonConstructor(): Promise { @@ -688,8 +689,10 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal, ID return CanvasAddon; } + @debounce(100) private async _refreshImageAddon(): Promise { - if (this._configHelper.config.enableImages) { + // Only allow the image addon when a canvas is being used to avoid possible GPU issues + if (this._configHelper.config.enableImages && (this._canvasAddon || this._webglAddon)) { if (!this._imageAddon) { const AddonCtor = await this._getImageAddonConstructor(); this._imageAddon = new AddonCtor(); @@ -747,6 +750,7 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal, ID // ignore } this._canvasAddon = undefined; + this._refreshImageAddon(); } private _disposeOfWebglRenderer(): void { @@ -756,6 +760,7 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal, ID // ignore } this._webglAddon = undefined; + this._refreshImageAddon(); } private async _measureRenderTime(): Promise { diff --git a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts index 6d05f5781d6..8a58ebac81a 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts @@ -587,7 +587,7 @@ const terminalConfiguration: IConfigurationNode = { }, [TerminalSettingId.EnableImages]: { restricted: true, - markdownDescription: localize('terminal.integrated.enableImages', "Enables image support in the terminal. Both sixel and iTerm's inline image protocol are supported on Linux and macOS, Windows support will light up automatically when ConPTY passes through the sequences. Images will currently not be restored between window reloads/reconnects."), + markdownDescription: localize('terminal.integrated.enableImages', "Enables image support in the terminal, this will only work when {0} is enabled. Both sixel and iTerm's inline image protocol are supported on Linux and macOS, Windows support will light up automatically when ConPTY passes through the sequences. Images will currently not be restored between window reloads/reconnects.", `\`#${TerminalSettingId.GpuAcceleration}#\``), type: 'boolean', default: true }, From 2c4cdea85f4e93937cfe5ee4cc21878d1883bd65 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 11 Jul 2023 11:20:57 -0700 Subject: [PATCH 342/926] Bump highlight js versions (#187619) Fixes #187605 --- extensions/markdown-language-features/package.json | 2 +- extensions/markdown-language-features/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/extensions/markdown-language-features/package.json b/extensions/markdown-language-features/package.json index 1afabaf9dff..0c5e79b6373 100644 --- a/extensions/markdown-language-features/package.json +++ b/extensions/markdown-language-features/package.json @@ -723,7 +723,7 @@ "dependencies": { "@vscode/extension-telemetry": "0.7.5", "dompurify": "^2.4.1", - "highlight.js": "^11.4.0", + "highlight.js": "^11.8.0", "markdown-it": "^12.3.2", "markdown-it-front-matter": "^0.2.1", "morphdom": "^2.6.1", diff --git a/extensions/markdown-language-features/yarn.lock b/extensions/markdown-language-features/yarn.lock index 46f769556aa..c5c67af15b9 100644 --- a/extensions/markdown-language-features/yarn.lock +++ b/extensions/markdown-language-features/yarn.lock @@ -378,10 +378,10 @@ form-data@^4.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" -highlight.js@^11.4.0: - version "11.4.0" - resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-11.4.0.tgz#34ceadd49e1596ee5aba3d99346cdfd4845ee05a" - integrity sha512-nawlpCBCSASs7EdvZOYOYVkJpGmAOKMYZgZtUqSRqodZE0GRVcFKwo1RcpeOemqh9hyttTdd5wDBwHkuSyUfnA== +highlight.js@^11.8.0: + version "11.8.0" + resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-11.8.0.tgz#966518ea83257bae2e7c9a48596231856555bb65" + integrity sha512-MedQhoqVdr0U6SSnWPzfiadUcDHfN/Wzq25AkXiQv9oiOO/sG0S7XkvpFIqWBl9Yq1UYyYOOVORs5UW2XlPyzg== http-proxy-agent@^5.0.0: version "5.0.0" From 26120d880b9998e45b254c68a5b8c02de38c9846 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 11 Jul 2023 11:51:33 -0700 Subject: [PATCH 343/926] Disable overscroll in all webviews (#187624) Fixes #182013 --- extensions/markdown-language-features/media/markdown.css | 1 - .../workbench/contrib/webview/browser/pre/index-no-csp.html | 3 ++- src/vs/workbench/contrib/webview/browser/pre/index.html | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/extensions/markdown-language-features/media/markdown.css b/extensions/markdown-language-features/media/markdown.css index 809a8bf0600..896531fdcc8 100644 --- a/extensions/markdown-language-features/media/markdown.css +++ b/extensions/markdown-language-features/media/markdown.css @@ -9,7 +9,6 @@ html, body { padding: 0 26px; line-height: var(--markdown-line-height, 22px); word-wrap: break-word; - overscroll-behavior-x: none; } body { diff --git a/src/vs/workbench/contrib/webview/browser/pre/index-no-csp.html b/src/vs/workbench/contrib/webview/browser/pre/index-no-csp.html index 34dbea981f1..b51db4053c9 100644 --- a/src/vs/workbench/contrib/webview/browser/pre/index-no-csp.html +++ b/src/vs/workbench/contrib/webview/browser/pre/index-no-csp.html @@ -9,7 +9,7 @@ content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no"> - +