Question: *--------------------------------------------------------------------------------------------*/ import { Event } from 'vs/base/common/event'; <del> import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IDebugService, VIEWLET_ID, IDebugSession } from 'vs/workbench/contrib/debug/common/debug'; </del> <ins> import { IDisposable, dispose } from 'vs/base/common/lifecycle'; </ins> import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; <del> import { dispose, IDisposable } from 'vs/base/common/lifecycle'; </del> <ins> import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IDebugService, IDebugSession, VIEWLET_ID } from 'vs/workbench/contrib/debug/common/debug'; </ins> import { IViewsService } from 'vs/workbench/services/views/common/viewsService'; <del> import { NotificationPriority } from 'vs/platform/notification/common/notification'; </del> export class DebugProgressContribution implements IWorkbenchContribution {

I have tried the vscode-bisect, but it failed: PS C:gitBIOSDBGV4srcextensionnpx --yes --reset [build] deleting cache directory C:UserszhiqzhanAppDataLocalTempvscode-bisect √ Commit of released insiders build that reproduces the issue (leave empty to pick the latest build) ... √ Commit of released insiders build that does not reproduce the issue (leave empty to pick the oldest build) ... [build] fetching all builds from [build] total 171 builds with roughly 7 steps [build] downloading build from [build] unzipping build to C:UserszhiqzhanAppDataLocalTempvscode-bisect.buildsVSCode-win32-x64-1.85.0-insider... [build] starting desktop build ... √ Is good or bad? » Good [build] downloading build from [build] unzipping build to C:UserszhiqzhanAppDataLocalTempvscode-bisect.buildsVSCode-win32-x64-1.86.0-insider... [build] starting desktop build ... √ Is good or bad? » Bad [build] downloading build from [error] Error: read ECONNRESET You can run vscode-bisect --verbose for more detailed output and vscode-bisect --reset for a fresh start without caches.
At first, it is quick in downloading vscode-insider, but later it takes a long time to download it. I am not sure why. !
thanks a lot for trying it though, maybe some connection trouble. I can try it if you tell me how to reproduce.
I create a simple debugger for testing. code: vsix: I am not sure whether you know how to start a debugger or not: 1) install test-debugger- 2) open a empty folder and then create a ! 3) select Debugger For Test ! 4) Start Testing debugger ! 5) You will see progress information in status bar !
If run in 1.85.2, it is as below: !
thanks! Is it possible that you have snoozed notifications either globally or for the extension?


Question: *--------------------------------------------------------------------------------------------*/ import { Event } from 'vs/base/common/event'; <del> import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IDebugService, VIEWLET_ID, IDebugSession } from 'vs/workbench/contrib/debug/common/debug'; </del> <ins> import { IDisposable, dispose } from 'vs/base/common/lifecycle'; </ins> import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; <del> import { dispose, IDisposable } from 'vs/base/common/lifecycle'; </del> <ins> import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IDebugService, IDebugSession, VIEWLET_ID } from 'vs/workbench/contrib/debug/common/debug'; </ins> import { IViewsService } from 'vs/workbench/services/views/common/viewsService'; <del> import { NotificationPriority } from 'vs/platform/notification/common/notification'; </del> export class DebugProgressContribution implements IWorkbenchContribution {

You effectively see the same notification but when it appears in the status bar, it means the notification is hiding because of the "Do not disturb mode". When you click the status bar entry you can see how it changes to the notification toast.
Yes, you are right. But even in below setting (BIOSDBG V4 is my extension): ! The notification is hidden unless you click the right-bottom icon. ! I think it makes me confused and it is not easy for user to notice this. Thanks!
There is indeed a change that happened as part of our latest release, but I think for the better: debug had always decided to show notifications for debug progress in the status bar via this directive: A change I did last milestone actually made to support it now: So I am deferring to debug component to further triage, but I think this is actually expected behaviour.
I think we should revert to the old behavior for consistency as that's what users have historically seen


Question: } private _updateContentsDomNodeMaxDimensions() { <del> const width = Math.max(this._editor.getLayoutInfo().width * 0.66, 500); const height = Math.max(this._editor.getLayoutInfo().height / 4, 250); </del> <ins> 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); </ins> this._setContentsDomNodeMaxDimensions(width, height); }

Add the possibility to persist the content hover sizes
To test, please read the following. Currently the implementation of the persistance mechanism of the content hover works as follows: The content hover has a default maximum size which is defined as follows: const defaultMaxHeight = (().height / 4, 250); const defaultMaxWidth = (().width 0.66, 500); Whenever the content hover is resized, the last hover dimensions are stored. Next time the content hover is shown, the maximum dimensions are updated as follows: const currentMaxHeight = (().height / 4, 250, lastHeight); const currentMaxWidth = (().width 0.66, 500, lastWidth); The content hover is rendered naturally within these constraints. If the lastHeight and lastWidth are smaller than the default max dimensions, then the current max dimensions are unchanged. Therefore the next rendering of the content hover will be the same as the initial rendering. Test that the content hover works according to these constraints. There is some discussion going on about how to better implement a persistance mechanism. We could also persist the size for example on a per-token basis in the editor. Let me know what you think is best!
Do you have an example of how it should work? Regardless of how I resize it, it is not remembered.
You may see an example of how it works here. The content hover is resized to a bigger size in the gif. The current max width is increased to a higher value and at the next rendering of the content hover, the widget takes on a bigger size. To see this behavior you should choose a content hover where the content does not fit in the visible widget, where there is a horizontal scrollbar. I opened an issue for persisting the content hover on a per-token basis here: as an alternative idea.


Question: return ''; } <ins> private get selectedText(): string { const activeEditor = this.editorService.getActiveEditor(); if (activeEditor) { const editorControl = (<ICodeEditor>activeEditor.getControl()); if (editorControl) { const editorModel = editorControl.getModel(); const editorSelection = editorControl.getSelection(); if (editorModel && editorSelection) { return editorModel.getValueInRange(editorSelection); } } } return ''; } </ins> private getFilePath(): string { let input = this.editorService.getActiveEditorInput(); if (input instanceof DiffEditorInput) {

Variable substitutions that are available for (see ) do not include currently selected text. I am looking for something similar to $(CurText) that is available in Visual Studio Arguments for External Tools (see ). Please add a variable ${curtext} which would be replaced with the currently selected text in the editor window. If no selection is available, it could use the current line at cursor position.
Nice idea, but I think it's too late for implement that in the current milestone...
Fixed by in
Verifiers: what happens if all text in a 300 MB file is selected...
good point. Maybe we should put a limit on this. Or only to copy the howl line at most (even though the line could also be 300mb I am fine with that) thoughts?
Selecting lots of text, at some point I get . If there's a predictable limit where this will fail, we should just cut the string off there. Unless only sending part of the text could be dangerous or unexpected for the user. In any case I think that sending multiple lines of text could be useful.
there is not a predictable limit since the variables are used in different scenarios. If somebody has an idea for a limit I am open to PRs


Question: this.container.style.marginLeft = `${marginWithoutIndentation}px`; } <del> setWidgetMargins(position: Position, indentationWidth?: number): void { if (indentationWidth === undefined) { indentationWidth = this._calculateIndentationWidth(position); } </del> <ins> setWidgetMargins(position: Position): void { const indentationWidth = this._calculateIndentationWidth(position); </ins> if (this._indentationWidth === indentationWidth) { return; }

I think we used to indent inline chat to the indentation level of the line: ! Steps: have an indented line press enter to be on an empty line (still indented) press to bring up inline chat


Question: this._domNode.domNode.height = height * ratio; const ctx = this._domNode.domNode.getContext('2d')!; ctx.clearRect(0, 0, this.width * ratio, height * ratio); <del> this._renderCanvas(ctx, this.width * ratio, height * ratio, scrollHeight * ratio, ratio); </del> <ins> this._renderCanvas(ctx, this.width * ratio, height * ratio, contentHeight * ratio, ratio); </ins> this._renderOverviewViewport(); }

Testing Ideally there would be commands to navigate between changes. And they might deserve an action in the diff title bar. Same as the editor diff experience. !
Why is for these commands? I would expect them to be available from the command palette, since in a regular text editor Show Previous Change and Show Next Change both appear.


Question: this._domNode.domNode.height = height * ratio; const ctx = this._domNode.domNode.getContext('2d')!; ctx.clearRect(0, 0, this.width * ratio, height * ratio); <del> this._renderCanvas(ctx, this.width * ratio, height * ratio, scrollHeight * ratio, ratio); </del> <ins> this._renderCanvas(ctx, this.width * ratio, height * ratio, contentHeight * ratio, ratio); </ins> this._renderOverviewViewport(); }

Testing Notice that the scroll range is below the decorations, even though they're on screen <img width="717" alt="image" src="https://user-"Meanwhile if I scroll to where they're shown, they aren't in viewport <img width="713" alt="image" src="https://user-"This was on the following gif: <img width="401" alt="image" src="https://user-"


Question: private openAsBinary(input: FileEditorInput, options: ITextEditorOptions | undefined): void { <del> // Mark file input for forced binary opening input.setForceOpenAsBinary(); // Open in group (this.group ?? this.editorGroupService.activeGroup).openEditor(input, { </del> <ins> const defaultBinaryEditor = this.configurationService.getValue<string | undefined>('workbench.editor.defaultBinaryEditor'); const groupToOpen = this.group ?? this.editorGroupService.activeGroup; const editorOptions = { </ins> ...options, // Make sure to not steal away the currently active group // because we are triggering another openEditor() call // and do not control the initial intent that resulted // in us now opening as binary. activation: EditorActivation.PRESERVE <del> }); </del> <ins> }; // If we the user setting specifies a default binary editor we use that. if (defaultBinaryEditor && defaultBinaryEditor !== '') { this.editorService.replaceEditors([{ editor: input, replacement: { resource: input.resource, options: { ...editorOptions, override: defaultBinaryEditor } } }], groupToOpen); } else { // Mark file input for forced binary opening input.setForceOpenAsBinary(); // Open in group groupToOpen.openEditor(input, editorOptions); } </ins> } private async openAsFolder(input: FileEditorInput): Promise<void> {

We don't want to officially have the hex editor be recommended for non-textual files, but what about a setting like that can be used to open a binary file in a given editor instead of showing the "binary file" message? ! I would love it if I could have all non-text files automatically opened in the hex editor.
To verify ensure the setting works as expected. Setting it to a custom editor should automatically have that editor be opened when a file is detected as binary.


Question: } private get centeredEditorAvailableSize(): number { <del> return this.silosSize[Position.ONE] - EditorGroupsControl.CENTERED_EDITOR_MIN_MARGIN * 2; </del> <ins> return this.dimension.width - EditorGroupsControl.CENTERED_EDITOR_MIN_MARGIN * 2; </ins> } private get centeredEditorSize(): number { <del> return Math.min(this.centeredEditorAvailableSize, this.centeredEditorPreferedSize); </del> <ins> return Math.min(this.centeredEditorAvailableSize, this.centeredEditorPreferredSize); </ins> } private get centeredEditorPosition(): number {

<!-- Do you have a question? Please ask it on --<!-- Use Help Report Issue to prefill these. --- VSCode Version: 1.21 OS Version: Mac 10.12.16 Steps to Reproduce: editor mode should be in vertical mode on centered layout with one tab, you should see it is centered split editor mode to horizontal mode centered layout will no longer work until you switch back to vertical mode <!-- Launch with to check. --Does this issue occur when all extensions are disabled?: Yes
(Experimental duplicate detection) Thanks for submitting this issue. Please also check if it is already covered by an existing one, like: <!-- score: 0.458 --- <!-- score: 0.451 --- <!-- score: 0.451 --<!-- potentialduplicatescomment --
Thanks for figuring this out. I spent a while trying to figure out what the problem was. Thank to you, I changed splitting to vertical mode and now centered layout works. I can affirm that this big is happening to me too. I'm on a mac as well.
I would expect centered mode to work if I have horizontal layout and only one editor. I tried removing the check to simply also enable it for horizontal layout but somehow the layout gets broken in that case. I plan to fix this in the following week but just to let you know that a PR is more than welcome :)
I just let you know that money is more than welcome :smile: Just kidding... I run on fun xD you shall have your PR :) it has the number . The problem was, that the available size was calculated by using which holds the height and not the width of the editor when in horizontal mode. You have to play around with it a little... it seemed to work but I did no deep testing or anything As bonus I fixed two typos (one of whom I made myself :smile:)
thanks a lot for your PR, I just merged it in I tried it out and it works nicely, also thanks for fixing typos. For money feel free to apply for our internshipts if you are a student or to our open positions :)
:smile: I'll finish university in 4 month... so... I might very well do that xD


Question: // Default Filter return enableSeparateSubstringMatching ? fuzzySeparateFilter(word, wordToMatchAgainst) : fuzzyContiguousFilter(word, wordToMatchAgainst); } <ins> export function matchesFuzzy2(pattern: string, word: string): IMatch[] | undefined { pattern = pattern.toLowerCase(); word = word.toLowerCase(); let result: IMatch[] = []; let lastMatch: IMatch; let patternPos = 0; let wordPos = 0; while (patternPos < pattern.length && wordPos < word.length) { if (pattern.charAt(patternPos) === word.charAt(wordPos)) { patternPos += 1; if (lastMatch && lastMatch.end === wordPos) { lastMatch.end += 1; } else { lastMatch = { start: wordPos, end: wordPos + 1 }; result.push(lastMatch); } } wordPos += 1; } if (patternPos !== pattern.length) { return undefined; } return result; } export function matchesFuzzy3(pattern: string, word: string) { return _doMatchesFuzzy3( pattern, pattern.toLowerCase(), 0, word, word.toLowerCase(), 0, [], 0 ); } function _doMatchesFuzzy3( pattern: string, lowPattern: string, patternPos: number, word: string, lowWord: string, wordPos: number, positions: number[], score: number ): [IMatch[], number] { let retryPoints: number[] = []; let lastDidMatch = false; while (patternPos < lowPattern.length && wordPos < lowWord.length) { const charLowPattern = lowPattern.charAt(patternPos); const charLowWord = lowWord.charAt(wordPos); if (charLowPattern === charLowWord) { if (positions.length === 0) { score = -Math.min(wordPos, 3) * 3; // penalty -> gaps at start } else if (lastDidMatch) { score += 1; // bonus -> subsequent match } if (charLowWord !== word.charAt(wordPos)) { score += 10; // bonus -> upper-case } else if (wordPos > 0 && word.charAt(wordPos).match(_separator)) { score += 10; // bonus -> after a separator } else { // keep this as a retry point retryPoints.push(patternPos, wordPos + 1, positions.length, score); } patternPos += 1; positions.push(wordPos); lastDidMatch = true; } else { lastDidMatch = false; score -= 1; // penalty -> gaps in match } wordPos += 1; } if (patternPos !== lowPattern.length) { return undefined; } const matches: IMatch[] = []; let lastMatch: IMatch; for (const pos of positions) { if (lastMatch && lastMatch.end === pos) { lastMatch.end += 1; } else { lastMatch = { start: pos, end: pos + 1 }; matches.push(lastMatch); } } let result: [IMatch[], number] = [matches, score]; // try alternative matches for (let i = 0; i < retryPoints.length; i += 4) { const alt = _doMatchesFuzzy3( pattern, lowPattern, retryPoints[i], word, lowWord, retryPoints[i + 1], positions.slice(0, retryPoints[i + 2]), retryPoints[i + 3] ); if (alt && alt[1] > result[1]) { result = alt; } } return result; } const _separator = /[-_. ]/; export function matchesFuzzy4(pattern: string, word: string): [IMatch[], number] { const lowPattern = pattern.toLowerCase(); const lowWord = word.toLowerCase(); const [landmarkWord, landmarkPositions] = computeLandmarks(word, lowWord); let landmarkPos = 0; let wordPos = 0; let patternPos = 1; let charLowPattern = lowPattern.charAt(0); let result: IMatch[] = []; let lastMatch: IMatch; let score = 0; if (charLowPattern === lowWord.charAt(0)) { lastMatch = { start: wordPos, end: wordPos + 1 }; result.push(lastMatch); wordPos = 1; landmarkPos = 1; if (pattern.charAt(0) === word.charAt(0)) { score += 10; } } else if ((landmarkPos = landmarkWord.indexOf(charLowPattern)) >= 0) { wordPos = landmarkPositions[landmarkPos]; score += 10 - Math.min(9, wordPos * 3); lastMatch = { start: wordPos, end: wordPos + 1 }; result.push(lastMatch); wordPos += 1; landmarkPos += 1; } else { return undefined; } while (patternPos < lowPattern.length && wordPos < lowWord.length) { charLowPattern = lowPattern.charAt(patternPos); let match = false; if (landmarkPos < landmarkWord.length && charLowPattern === landmarkWord.charAt(landmarkPos)) { let newWordPos = landmarkPositions[landmarkPos]; match = true; score += 10 - (newWordPos - wordPos); wordPos = newWordPos; landmarkPos += 1; patternPos += 1; } else if (charLowPattern === lowWord.charAt(wordPos)) { match = true; patternPos += 1; } if (match) { if (lastMatch && lastMatch.end === wordPos) { lastMatch.end += 1; score += 1; } else { lastMatch = { start: wordPos, end: wordPos + 1 }; result.push(lastMatch); } } wordPos += 1; if (wordPos >= landmarkPositions[landmarkPos]) { landmarkPos += 1; } } if (patternPos !== lowPattern.length) { return undefined; } // substract uncovered remainder score -= lowWord.length - wordPos; return [result, score]; } function computeLandmarks(word: string, lowWord: string): [string, number[]] { let result: string = ''; let positions: number[] = []; let lastCh: string; for (let pos = 0; pos < word.length; pos++) { const ch = word.charAt(pos); if (!result // first character is a landmark || (lastCh === '_' || lastCh === '-' || lastCh === ' ') // last was separator || ch !== lowWord.charAt(pos) // upper-case ) { result += ch; positions.push(pos); } lastCh = ch; } result = result.toLowerCase(); return [result, positions]; } // function print(m: number[][]) { // 	for (const n of m) { // 		console.log(n.join('|')); // 	} // } export function matchesFuzzy5(pattern: string, word: string) { // create matrix const matrix: number[][] = [[0]]; for (let i = 1; i <= pattern.length; i++) { matrix.push([-i]); } for (let i = 1; i <= word.length; i++) { matrix[0].push(-i); } for (let i = 0; i < pattern.length; i++) { let match = false; for (let j = 0; j < word.length; j++) { let diagScore = 0; if (pattern[i] === word[j]) { diagScore = 1 + matrix[i][j]; match = true; } else { diagScore = -1 + matrix[i][j]; } let upScore = -1 + matrix[i][j + 1]; let leftScore = -1 + matrix[i + 1][j]; matrix[i + 1][j + 1] = Math.max(diagScore, upScore, leftScore); } if (!match) { return undefined; } } // print(matrix); return []; } export function createMatches(position: number[]): IMatch[] { let ret: IMatch[] = []; let last: IMatch; for (const pos of position) { if (last && last.end === pos) { last.end += 1; } else { last = { start: pos, end: pos + 1 }; ret.push(last); } } return ret; } export function fuzzyMatchAndScore(pattern: string, word: string): [number, number[]] { let matches: number[] = []; let score = _matchRecursive( pattern, pattern.toLowerCase(), pattern.toUpperCase(), 0, word, word.toLowerCase(), 0, matches ); if (score <= 0) { return undefined; } score -= Math.min(matches[0], 3) * 3; // penalty for first matching character score -= (1 + matches[matches.length - 1]) - (pattern.length); // penalty for all non matching characters between first and last return [score, matches]; } export function _matchRecursive( pattern: string, lowPattern: string, upPattern: string, patternPos: number, word: string, lowWord: string, wordPos: number, matches: number[] ): number { if (patternPos >= lowPattern.length) { return 0; } const lowPatternChar = lowPattern.charAt(patternPos); let idx = -1; let value = 0; if ((patternPos === wordPos && lowPatternChar === lowWord.charAt(wordPos)) && ((value = _matchRecursive(pattern, lowPattern, upPattern, patternPos + 1, word, lowWord, wordPos + 1, matches)) >= 0) ) { matches.unshift(wordPos); return (pattern.charAt(patternPos) === word.charAt(wordPos) ? 17 : 11) + value; } if ((idx = lowWord.indexOf(`_${lowPatternChar}`, wordPos)) >= 0 && ((value = _matchRecursive(pattern, lowPattern, upPattern, patternPos + 1, word, lowWord, idx + 2, matches)) >= 0) ) { matches.unshift(idx + 1); return (pattern.charAt(patternPos) === word.charAt(idx + 1) ? 17 : 11) + value; } if ((idx = word.indexOf(upPattern.charAt(patternPos), wordPos)) >= 0 && ((value = _matchRecursive(pattern, lowPattern, upPattern, patternPos + 1, word, lowWord, idx + 1, matches)) >= 0) ) { matches.unshift(idx); return (pattern.charAt(patternPos) === word.charAt(idx) ? 17 : 11) + value; } if (patternPos > 0 && (idx = lowWord.indexOf(lowPatternChar, wordPos)) >= 0 && ((value = _matchRecursive(pattern, lowPattern, upPattern, patternPos + 1, word, lowWord, idx + 1, matches)) >= 0) ) { matches.unshift(idx); return 1 + value; } return -1; } </ins>


Question: goMenuRegistration(); debugMenuRegistration(); tasksMenuRegistration(); <del> terminalMenuRegistration(); </del> if (isMacintosh) { windowMenuRegistration();

We should also take out the "Terminal" menu when using the custom menu: ! //cc
pushed a fix for this only to the release branch. What we do for master is up for discussion. please verify once the build is out in 30 mins. Thanks
Thanks I didn't know about this code.


Question: this._widget?.inlineChatWidget.updateInfo(''); this._widget?.inlineChatWidget.updateToolbar(true); <del> this._activeSession.addExchange(new SessionExchange(this._activeSession.lastInput, response)); </del> <ins> this._activeSession?.addExchange(new SessionExchange(this._activeSession.lastInput, response)); </ins> this._ctxLastResponseType.set(response instanceof ReplyResponse ? response.raw.type : undefined); }

use generate cell in notebooks make a request while the request is active press stop a bunch of errors are printed
It seems not wise at all to maintain my own *chatcontroller. I'd love to look into how do I ensure I reuse the most of Inline Chat Controller later.
Is the generate cell feature just with using ctrl+i from within a cell body? Not sure how I can trigger it.


Question: private layoutTree(): void { const scrollBarHeight = 10; <del> const treeHeight = Math.min(Math.max(266, this.editor.getLayoutInfo().height * 0.55), this.tree.contentHeight + scrollBarHeight); </del> <ins> let maxHeightToAvoidCursorOverlay = Infinity; if (this.showAtPosition) { const editorTop = this.editor.getDomNode()?.offsetTop || 0; const containerTop = this.treeContainer.offsetTop + editorTop; const hoveredCharTop = this.editor.getTopForLineNumber(this.showAtPosition.lineNumber, true) - this.editor.getScrollTop(); if (containerTop < hoveredCharTop) { maxHeightToAvoidCursorOverlay = hoveredCharTop + editorTop - 22; // 22 is monaco top padding https://github.com/microsoft/vscode/blob/a1df2d7319382d42f66ad7f411af01e4cc49c80a/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts#L364 } } const treeHeight = Math.min(Math.max(266, this.editor.getLayoutInfo().height * 0.55), this.tree.contentHeight + scrollBarHeight, maxHeightToAvoidCursorOverlay); </ins> const realTreeWidth = this.tree.contentWidth; const treeWidth = clamp(realTreeWidth, 400, 550);

see gif have the debug hover above the cursor expand some objects the hover grows over the current line/cursor !
Yeah, this is a consequence of the change to not make it jump. Since in this scenario it does not have enough space above to grow, it can only grow over the line and below. Alternative would be to show it down, which would cause a jump. Or to limit the size, that it does not grow over the current line. That I think makes sense. However will not tackle during endgame.
How about not growing and use the scrollbar sooner? Just measure the max size above the cursor and don't go over that
I think this makes sense. My current max height is 70% of the editor height. Idealy as you suggest the max height should be the size above the cursor if I am placed above, or max size below the cursor if I am placed below. However I am not aware of editor API that gives me height above or below the cursor. Are you already using something like this for the suggest?
I am using and some math:


Question: "target" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } } */ <del> telemetryService.publicLog('update:win32SetupTarget', { target: product.target }); </del> <ins> this.telemetryService.publicLog('update:win32SetupTarget', { target: product.target }); </ins> } }

run you'll notice that in the main process before creating the renderer the update service takes its time (~5-10ms). while that's not much it seems unnecessary and "block" the renderer for no good reason <img width="1101" alt="Screenshot 2020-01-31 at 14 45 07" src="https://user-"
This is only problematic in macOS where calls into the native which blocks.
Thanks


Question: "list.hoverBackground": "#F2F2F2", "list.focusAndSelectionOutline": "#005FB8", "menu.border": "#CECECE", <ins> "menu.selectionBackground": "#005FB8", "menu.selectionForeground": "#ffffff", </ins> "notebook.cellBorderColor": "#E5E5E5", "notebook.selectedCellBackground": "#C8DDF150", "notificationCenterHeader.background": "#FFFFFF",

<!-- Do Not Delete This! bugreporttemplate --<!-- Please read our Rules of Conduct: --<!-- Read our guide about submitting issues: --<!-- Search existing issues to avoid creating duplicates. --<!-- Test using the latest Insiders build to see if your issue has already been fixed: --<!-- Instead of creating your report here, use 'Report Issue' from the 'Help' menu in VS Code to pre-fill useful information. --<!-- Launch with to check. --Does this issue occur when all extensions are disabled?: Yes <!-- If you answered No above, use 'Help: Start Extension Bisect' from Command Palette to try to identify the cause. --<!-- Issues caused by an extension need to be reported directly to the extension publisher. The 'Help Report Issue' dialog can assist with this. --- VS Code Version: 1.85.2 OS Version: Windows 11 Enterprise (Windows_NT x64 10.0..3007) Description During an accessibility test, we found the contrast ratio between the highlight color of the focused button in "More Actions..." in notification popup and its background is not high enough under "Default Dark Modern" theme. Highlight Color: Background Color: Contrast Ratio: 1.346:1 Required Contrast Ratio: 3:1 !
I think this refers to the background color of the gear icon in the toolbar of a notification relative to the background color of the notification.
The bug reported on my side particularly specified the "Manage Extension" button highlight color and the background color of that actions popup under settings icon in the notification.
Looks like it's about the blue on black bg, in that case this applies to all the web menus in VS Code.
yes, it's about the blue highlight and black background.
A11yMAS; #A11yTCS; #A11ySev2; #DesktopApp; #Win32; #Visual Studio Code Client; #BM-VisualStudioCodeClient-Win32-Aug2023; #WCAG1.4.11; #Non-text Contrast;
Hi, is there any update on the plan of fixing this accessibility bug?
putting this on the March plan.
Hi, may I check if the fix is included in v1.88.0?
Hi, I have checked v1.88.0 but it seems the active color is still not the one you changed to in your PR. Did I miss something?


Question: "list.hoverBackground": "#F2F2F2", "list.focusAndSelectionOutline": "#005FB8", "menu.border": "#CECECE", <ins> "menu.selectionBackground": "#005FB8", "menu.selectionForeground": "#ffffff", </ins> "notebook.cellBorderColor": "#E5E5E5", "notebook.selectedCellBackground": "#C8DDF150", "notificationCenterHeader.background": "#FFFFFF",

Verified with the Dark Modern theme, but do we also have to fix this with the Dark+ or Dark (Visual Studio) themes?
Thanks, I think it's fine we only fix it in default dark theme for now. The test team doesn't require that in other themes so far.


Question: this.update(); } <ins> get toggles() { return this._toggles; } set toggles(toggles: IQuickInputToggle[]) { this._toggles = toggles ?? []; this.togglesUpdated = true; this.update(); } </ins> get validationMessage() { return this._validationMessage; }

Type: <bBug</bFuzzy search icon is visible in Go to Symbol view (Shift + CMD + O) which on press opens terminal recent search with fuzzy search. I think that button should be excluded from Go to Symbol view and be visible only in terminal recent search view. VS Code version: Code 1.73.1 (, 2022-11-09T02:22:48.959Z) OS version: Darwin arm64 22.1.0 <img width="607" alt="image" src="https://user-"<img width="597" alt="image" src="https://user-"


Question: if (typeof options?.focusEditorLine === 'number') { this._cursorNavMode.set(true); this.revealInView(cell); <del> } else if (options?.revealBehavior === ScrollToRevealBehavior.fullCell) { this.revealInView(cell); </del> } else if (options?.revealBehavior === ScrollToRevealBehavior.firstLine) { this.revealFirstLineIfOutsideViewport(cell); } else {

a notebook that has several cells, and put this code in the second to last cell the cell so that it has output, and shrink the window so that the single cell+output takes up the whole scrollable region the cell again with shift+enter :bug: the notebook ends up scrolling up, instead of showing the full output and next cell. !
The current behavior of the list view is to use the second visible element as an anchor which can cause some strange behavior. In this scenario: next cell is focused and revealed, making that cell the anchor as second visible, since the executing cell takes up the rest of the visible region. output is removed, moving more elements into view from above is and the initial anchor is no longer the second element, so it is scrolled out of view I'll try to play around with some options, but my initial thought would be to use the executing cell as the anchor. The output could expand and push the next cell out of view, but typing in that cell will bring it back into view. cc
I wanted to see the behavior locally. I really can't seem to repro this, my notebook stays pretty stable as the cell shrinks and grows. What am I missing?
I think I found it. I believe you have disabled, am I correct? Can you reproduce this when flipping that setting back to the default value of ?
It repro's with or without - you just need to scroll down enough so that the resized cell is only partially visible. The strange behavior is due to the notebook cell list setting different anchors depending on whether the element top is above the render top.
As I looked into the remaining open issue and PR , I was trying to grasp what the issue really is, what has been actually fixed and what's left to fix. Then, I pulled latest and ran out of sources and this is even worse than my last recording. Everything jumps around even when the cell is fully in view: Did just break this even more? I suggest reverting , pausing, simplifying the problem, narrowing in on a solution and consider fixing it in debt week so as to not cause regressions during this endgame week. cc
thanks for the testing, the two recordings you posted above were using different execution commands so I want to ensure we refer to the same behavior: in , it's re-executing the active cell (ctrl+enter), which should always stay visually stable.


Question: if (typeof options?.focusEditorLine === 'number') { this._cursorNavMode.set(true); this.revealInView(cell); <del> } else if (options?.revealBehavior === ScrollToRevealBehavior.fullCell) { this.revealInView(cell); </del> } else if (options?.revealBehavior === ScrollToRevealBehavior.firstLine) { this.revealFirstLineIfOutsideViewport(cell); } else {

I think it still works this way out from source in the latest , from what I saw, it's running shift+enter: run cell and move to next This command moves focus to the next cell, so the cell is now the focused cell Then the output of the previous cell (which is no longer focused) starts to grow The change introduced via 's last PR was to ensure that the focused cell stay visually stable, once they are revealed into view can you confirm if this is what you are seeing?
I captured a recording for what probably was comparing, right side is Stable and left side Insiders In both setup, the next cell is fully visible In Stable, shift+enter moves the next cell up, and then push it down, but the new focused cell is still visible in the viewport at the end In Insiders, shift+enter moves the next cell up, and when the previous starts to grow, it does not move the focused cell downwards Even though the new behavior is more self explanatory (from our perspective), but unluckily is not the same as Jupyter Lab. It means we are changing from one custom behavior (our previous heuristics) to another (always keep focused cell stable)
Thanks for clarifying this It was not clear to me that (1) shift+enter and clicking the run cell action did different things, nor that (2) this bug was only about one of these actions.
fixed by


Question: if (typeof options?.focusEditorLine === 'number') { this._cursorNavMode.set(true); this.revealInView(cell); <del> } else if (options?.revealBehavior === ScrollToRevealBehavior.fullCell) { this.revealInView(cell); </del> } else if (options?.revealBehavior === ScrollToRevealBehavior.firstLine) { this.revealFirstLineIfOutsideViewport(cell); } else {

Currently the scroll top position update is not context-aware, meaning when we have cell height changes, we don't know if it's because output height change, or users re-run a cell and outputs are updated/replaced, or users shift+enter to run cell and navigate to next cell. Users might have different expectations for each scenario but currently our heuristics for updating the scroll top didn't take the context into account. Let's use this issue to finalize the desired behavior of scroll top update. The polished/new design should cover [ ] Update element height API respects context (execution, execute+create, output update) [ ] Reveal API should handle out of sync view/content height Click the run button or press Ctrl+Enter to run focused cell If there is no enough room/space after the cell (e.g., the focused cell is the last cell) Users have disabled [ ] There is no output before execution, the view should scroll up to reveal the output fully [ ] Some users might want the bottom of the output to be fully revealed, when the output is larger than the viewport [ ] There is output before execution, the cell position should be persistent Users have enabled [ ] Reveal cell output If there is enough room after the cell [ ] Reveal cell output [ ] If the cell input is larger than the viewport, execute cell should reveal the output properly [ ] Collapse focused cell output should always keep the cell in viewport [ ] Type/Pasting content into focused cell should keep the cell in viewport [x] [ ] Notebook cell list reveal api might reveal a position on a cell whose view height is not synced with real editor height yet. [ ] Execute non-focused cell, should it keep the focused cell position persistent or keep the top of the target cell persistent? [ ] Non focused cell output height change should not change scroll top on scroll [ ] Shift+enter executes and moves focus to next cell, then new focused cell should always be in viewport [x] Scroll top position of inactive notebook editor should not change if the first partially visible cell's height change
I noticed an issue recently, is this related? Paste a large amount of text into a code cell The notebook scrolls down so far that the focused code cell is out of the viewport I can open a separate issue if you want
I guess what you were seeing is , I'll update the description to include this.
Couple cases discovered in the latest notebook sync.


Question: if (typeof options?.focusEditorLine === 'number') { this._cursorNavMode.set(true); this.revealInView(cell); <del> } else if (options?.revealBehavior === ScrollToRevealBehavior.fullCell) { this.revealInView(cell); </del> } else if (options?.revealBehavior === ScrollToRevealBehavior.firstLine) { this.revealFirstLineIfOutsideViewport(cell); } else {

both of these are likely tied to outputLineLimit being large @ 100 when the shift-enter behavior is set to "firstline" the events are as follows: first line of next cell is focused and shown at the bottom of the viewport output is cleared and the focused cell is pulled upwards against the currently executing cell output streams and pushes the focused cell out of the viewport. my ideal thoughts: anchor the focused cell (with setting), maintaining the first line visible. output will stream in, pushing the executing cell upwards out of the viewport shift-enter set to "fullCell" and next cell has a large output focus shifts to next cell, and the viewport exposes the bottom of the output for that cell. there is no cell editor in the viewport my thoughts: "fullCell" behavior should show the top of the next cell editor at the top of the viewport


Question: "displayName": "Markdown it ipynb Cell Attachment renderer", "entrypoint": { "extends": "vscode.markdown-it-renderer", <del> "path": "./out/cellAttachmentRenderer.js" </del> <ins> "path": "./notebook-out/cellAttachmentRenderer.js" </ins> } } ],

Type: <bBug</bipynb notebook bug In dev tools, see: VS Code version: Code - Insiders 1.70.0-insider (Universal) (, 2022-07-26T05:17:37.262Z) OS version: Darwin x64 21.5.0 Modes: <details<summarySystem Info</summary </details<!-- generated by issue reporter --
Looks like the directory is excluded from publish: You likely want to have the notebook code go to a different directory, such as instead of the dir
Just changed that inside esbuild, didn't realize that out wasn't included. I had been having an issue with needing to run while using OSS, could that be related? I'll make a PR to adjust and


Question: // Meta if (element.saneDetail) { <ins> data.detail.element.style.display = ''; </ins> data.detail.setLabel(element.saneDetail, undefined, { matches: detailHighlights, title: element.saneDetail }); <del> } /* else { // TODO investigate potential detail bleeding into next quickpicks data.detail.setLabel(''); } */ </del> <ins> } else { data.detail.element.style.display = 'none'; } </ins> // Separator if (element.item && element.separator && element.separator.label) {

<!-- Do Not Delete This! bugreporttemplate --<!-- Please read our Rules of Conduct: --<!-- Read our guide about submitting issues: --<!-- Search existing issues to avoid creating duplicates. --<!-- Test using the latest Insiders build to see if your issue has already been fixed: --<!-- Instead of creating your report here, use 'Report Issue' from the 'Help' menu in VS Code to pre-fill useful information. --<!-- Launch with to check. --Does this issue occur when all extensions are disabled?: Yes/No <!-- If you answered No above, use 'Help: Start Extension Bisect' from Command Palette to try to identify the cause. --<!-- Issues caused by an extension need to be reported directly to the extension publisher. The 'Help Report Issue' dialog can assist with this. --- VS Code Version: 1.73.1 OS Version: macOS Ventura 13.0.1 Steps to Reproduce: VoiceOver a command that uses a Quick Pick List where the items have a as well as a title or description (demoed with Azure Extension "Create Resource") list a command that uses a Quick Pick List where the items have no detail, e.g. "Select Display Language" or even command palette reader will read the title of the new item with the detail of the item in the previously displayed quick pick list https://user- Same bug with the command palette reading details for items: https://user-
This issue is caused by the item still containing the values from the previous list.
Verification steps: the issue steps are good. If you want a non-Azure extension command that shows a quick pick with details, use


Question: <ins> /*--------------------------------------------------------------------------------------------- *  Copyright (c) Microsoft Corporation. All rights reserved. *  Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; import * as sinon from 'sinon'; import { CancellationTokenSource, Disposable, EventEmitter, ExtensionContext, NotebookCellKind, NotebookDocumentChangeEvent, NotebookDocumentWillSaveEvent, NotebookEdit, NotebookRange, TextDocumentSaveReason, workspace, type CancellationToken, type NotebookCell, type NotebookDocument, type WorkspaceEdit, type WorkspaceEditMetadata } from 'vscode'; import { activate } from '../notebookModelStoreSync'; suite('Notebook Model Store Sync', () => { let disposables: Disposable[] = []; let onDidChangeNotebookDocument: EventEmitter<NotebookDocumentChangeEvent>; let onWillSaveNotebookDocument: AsyncEmitter<NotebookDocumentWillSaveEvent>; let notebook: NotebookDocument; let token: CancellationTokenSource; let editsApplied: WorkspaceEdit[] = []; let pendingPromises: Promise<void>[] = []; let cellMetadataUpdates: NotebookEdit[] = []; let applyEditStub: sinon.SinonStub<[edit: WorkspaceEdit, metadata?: WorkspaceEditMetadata | undefined], Thenable<boolean>>; setup(() => { disposables = []; notebook = { notebookType: '', metadata: {} } as NotebookDocument; token = new CancellationTokenSource(); disposables.push(token); sinon.stub(notebook, 'notebookType').get(() => 'jupyter-notebook'); applyEditStub = sinon.stub(workspace, 'applyEdit').callsFake((edit: WorkspaceEdit) => { editsApplied.push(edit); return Promise.resolve(true); }); const context = { subscriptions: [] as Disposable[] } as ExtensionContext; onDidChangeNotebookDocument = new EventEmitter<NotebookDocumentChangeEvent>(); disposables.push(onDidChangeNotebookDocument); onWillSaveNotebookDocument = new AsyncEmitter<NotebookDocumentWillSaveEvent>(); sinon.stub(NotebookEdit, 'updateCellMetadata').callsFake((index, metadata) => { const edit = (NotebookEdit.updateCellMetadata as any).wrappedMethod.call(NotebookEdit, index, metadata); cellMetadataUpdates.push(edit); return edit; } ); sinon.stub(workspace, 'onDidChangeNotebookDocument').callsFake(cb => onDidChangeNotebookDocument.event(cb) ); sinon.stub(workspace, 'onWillSaveNotebookDocument').callsFake(cb => onWillSaveNotebookDocument.event(cb) ); activate(context); }); teardown(async () => { await Promise.allSettled(pendingPromises); editsApplied = []; pendingPromises = []; cellMetadataUpdates = []; disposables.forEach(d => d.dispose()); disposables = []; sinon.restore(); }); test('Empty cell will not result in any updates', async () => { const e: NotebookDocumentChangeEvent = { notebook, metadata: undefined, contentChanges: [], cellChanges: [] }; onDidChangeNotebookDocument.fire(e); assert.strictEqual(editsApplied.length, 0); }); test('Adding cell for non Jupyter Notebook will not result in any updates', async () => { sinon.stub(notebook, 'notebookType').get(() => 'some-other-type'); const cell: NotebookCell = { document: {} as any, executionSummary: {}, index: 0, kind: NotebookCellKind.Code, metadata: {}, notebook, outputs: [] }; const e: NotebookDocumentChangeEvent = { notebook, metadata: undefined, contentChanges: [ { range: new NotebookRange(0, 0), removedCells: [], addedCells: [cell] } ], cellChanges: [] }; onDidChangeNotebookDocument.fire(e); assert.strictEqual(editsApplied.length, 0); assert.strictEqual(cellMetadataUpdates.length, 0); }); test('Adding cell will result in an update to the metadata', async () => { const cell: NotebookCell = { document: {} as any, executionSummary: {}, index: 0, kind: NotebookCellKind.Code, metadata: {}, notebook, outputs: [] }; const e: NotebookDocumentChangeEvent = { notebook, metadata: undefined, contentChanges: [ { range: new NotebookRange(0, 0), removedCells: [], addedCells: [cell] } ], cellChanges: [] }; onDidChangeNotebookDocument.fire(e); assert.strictEqual(editsApplied.length, 1); assert.strictEqual(cellMetadataUpdates.length, 1); const newMetadata = cellMetadataUpdates[0].newCellMetadata; assert.deepStrictEqual(newMetadata, { custom: { metadata: {} } }); }); test('Add cell id if nbformat is 4.5', async () => { sinon.stub(notebook, 'metadata').get(() => ({ custom: { nbformat: 4, nbformat_minor: 5 } })); const cell: NotebookCell = { document: {} as any, executionSummary: {}, index: 0, kind: NotebookCellKind.Code, metadata: {}, notebook, outputs: [] }; const e: NotebookDocumentChangeEvent = { notebook, metadata: undefined, contentChanges: [ { range: new NotebookRange(0, 0), removedCells: [], addedCells: [cell] } ], cellChanges: [] }; onDidChangeNotebookDocument.fire(e); assert.strictEqual(editsApplied.length, 1); assert.strictEqual(cellMetadataUpdates.length, 1); const newMetadata = cellMetadataUpdates[0].newCellMetadata || {}; assert.strictEqual(Object.keys(newMetadata).length, 1); assert.strictEqual(Object.keys(newMetadata.custom).length, 2); assert.deepStrictEqual(newMetadata.custom.metadata, {}); assert.ok(newMetadata.custom.id); }); test('Do not add cell id if one already exists', async () => { sinon.stub(notebook, 'metadata').get(() => ({ custom: { nbformat: 4, nbformat_minor: 5 } })); const cell: NotebookCell = { document: {} as any, executionSummary: {}, index: 0, kind: NotebookCellKind.Code, metadata: { custom: { id: '1234' } }, notebook, outputs: [] }; const e: NotebookDocumentChangeEvent = { notebook, metadata: undefined, contentChanges: [ { range: new NotebookRange(0, 0), removedCells: [], addedCells: [cell] } ], cellChanges: [] }; onDidChangeNotebookDocument.fire(e); assert.strictEqual(editsApplied.length, 1); assert.strictEqual(cellMetadataUpdates.length, 1); const newMetadata = cellMetadataUpdates[0].newCellMetadata || {}; assert.strictEqual(Object.keys(newMetadata).length, 1); assert.strictEqual(Object.keys(newMetadata.custom).length, 2); assert.deepStrictEqual(newMetadata.custom.metadata, {}); assert.strictEqual(newMetadata.custom.id, '1234'); }); test('Do not perform any updates if cell id and metadata exists', async () => { sinon.stub(notebook, 'metadata').get(() => ({ custom: { nbformat: 4, nbformat_minor: 5 } })); const cell: NotebookCell = { document: {} as any, executionSummary: {}, index: 0, kind: NotebookCellKind.Code, metadata: { custom: { id: '1234', metadata: {} } }, notebook, outputs: [] }; const e: NotebookDocumentChangeEvent = { notebook, metadata: undefined, contentChanges: [ { range: new NotebookRange(0, 0), removedCells: [], addedCells: [cell] } ], cellChanges: [] }; onDidChangeNotebookDocument.fire(e); assert.strictEqual(editsApplied.length, 0); assert.strictEqual(cellMetadataUpdates.length, 0); }); test('Store language id in custom metadata, whilst preserving existing metadata', async () => { sinon.stub(notebook, 'metadata').get(() => ({ custom: { nbformat: 4, nbformat_minor: 5, metadata: { language_info: { name: 'python' } } } })); const cell: NotebookCell = { document: { languageId: 'javascript' } as any, executionSummary: {}, index: 0, kind: NotebookCellKind.Code, metadata: { custom: { id: '1234', metadata: { collapsed: true, scrolled: true } } }, notebook, outputs: [] }; const e: NotebookDocumentChangeEvent = { notebook, metadata: undefined, contentChanges: [], cellChanges: [ { cell, document: undefined, metadata: undefined, outputs: undefined, executionSummary: undefined } ] }; onDidChangeNotebookDocument.fire(e); assert.strictEqual(editsApplied.length, 1); assert.strictEqual(cellMetadataUpdates.length, 1); const newMetadata = cellMetadataUpdates[0].newCellMetadata || {}; assert.strictEqual(Object.keys(newMetadata).length, 1); assert.strictEqual(Object.keys(newMetadata.custom).length, 2); assert.deepStrictEqual(newMetadata.custom.metadata, { collapsed: true, scrolled: true, vscode: { languageId: 'javascript' } }); assert.strictEqual(newMetadata.custom.id, '1234'); }); test('No changes when language is javascript', async () => { sinon.stub(notebook, 'metadata').get(() => ({ custom: { nbformat: 4, nbformat_minor: 5, metadata: { language_info: { name: 'javascript' } } } })); const cell: NotebookCell = { document: { languageId: 'javascript' } as any, executionSummary: {}, index: 0, kind: NotebookCellKind.Code, metadata: { custom: { id: '1234', metadata: { collapsed: true, scrolled: true } } }, notebook, outputs: [] }; const e: NotebookDocumentChangeEvent = { notebook, metadata: undefined, contentChanges: [], cellChanges: [ { cell, document: undefined, metadata: undefined, outputs: undefined, executionSummary: undefined } ] }; onDidChangeNotebookDocument.fire(e); assert.strictEqual(editsApplied.length, 0); assert.strictEqual(cellMetadataUpdates.length, 0); }); test('Remove language from metadata when cell language matches kernel language', async () => { sinon.stub(notebook, 'metadata').get(() => ({ custom: { nbformat: 4, nbformat_minor: 5, metadata: { language_info: { name: 'javascript' } } } })); const cell: NotebookCell = { document: { languageId: 'javascript' } as any, executionSummary: {}, index: 0, kind: NotebookCellKind.Code, metadata: { custom: { id: '1234', metadata: { vscode: { languageId: 'python' }, collapsed: true, scrolled: true } } }, notebook, outputs: [] }; const e: NotebookDocumentChangeEvent = { notebook, metadata: undefined, contentChanges: [], cellChanges: [ { cell, document: undefined, metadata: undefined, outputs: undefined, executionSummary: undefined } ] }; onDidChangeNotebookDocument.fire(e); assert.strictEqual(editsApplied.length, 1); assert.strictEqual(cellMetadataUpdates.length, 1); const newMetadata = cellMetadataUpdates[0].newCellMetadata || {}; assert.strictEqual(Object.keys(newMetadata).length, 1); assert.strictEqual(Object.keys(newMetadata.custom).length, 2); assert.deepStrictEqual(newMetadata.custom.metadata, { collapsed: true, scrolled: true }); assert.strictEqual(newMetadata.custom.id, '1234'); }); test('Update language in metadata', async () => { sinon.stub(notebook, 'metadata').get(() => ({ custom: { nbformat: 4, nbformat_minor: 5, metadata: { language_info: { name: 'javascript' } } } })); const cell: NotebookCell = { document: { languageId: 'powershell' } as any, executionSummary: {}, index: 0, kind: NotebookCellKind.Code, metadata: { custom: { id: '1234', metadata: { vscode: { languageId: 'python' }, collapsed: true, scrolled: true } } }, notebook, outputs: [] }; const e: NotebookDocumentChangeEvent = { notebook, metadata: undefined, contentChanges: [], cellChanges: [ { cell, document: undefined, metadata: undefined, outputs: undefined, executionSummary: undefined } ] }; onDidChangeNotebookDocument.fire(e); assert.strictEqual(editsApplied.length, 1); assert.strictEqual(cellMetadataUpdates.length, 1); const newMetadata = cellMetadataUpdates[0].newCellMetadata || {}; assert.strictEqual(Object.keys(newMetadata).length, 1); assert.strictEqual(Object.keys(newMetadata.custom).length, 2); assert.deepStrictEqual(newMetadata.custom.metadata, { collapsed: true, scrolled: true, vscode: { languageId: 'powershell' } }); assert.strictEqual(newMetadata.custom.id, '1234'); }); test('Will save event without any changes', async () => { await onWillSaveNotebookDocument.fireAsync({ notebook, reason: TextDocumentSaveReason.Manual }, token.token); }); test('Wait for pending updates to complete when saving', async () => { let resolveApplyEditPromise: (value: boolean) => void; const promise = new Promise<boolean>((resolve) => resolveApplyEditPromise = resolve); applyEditStub.restore(); sinon.stub(workspace, 'applyEdit').callsFake((edit: WorkspaceEdit) => { editsApplied.push(edit); return promise; }); const cell: NotebookCell = { document: {} as any, executionSummary: {}, index: 0, kind: NotebookCellKind.Code, metadata: {}, notebook, outputs: [] }; const e: NotebookDocumentChangeEvent = { notebook, metadata: undefined, contentChanges: [ { range: new NotebookRange(0, 0), removedCells: [], addedCells: [cell] } ], cellChanges: [] }; onDidChangeNotebookDocument.fire(e); assert.strictEqual(editsApplied.length, 1); assert.strictEqual(cellMetadataUpdates.length, 1); // Try to save. let saveCompleted = false; const saved = onWillSaveNotebookDocument.fireAsync({ notebook, reason: TextDocumentSaveReason.Manual }, token.token); saved.finally(() => saveCompleted = true); await new Promise((resolve) => setTimeout(resolve, 10)); // Verify we have not yet completed saving. assert.strictEqual(saveCompleted, false); resolveApplyEditPromise!(true); await new Promise((resolve) => setTimeout(resolve, 1)); // Should have completed saving. saved.finally(() => saveCompleted = true); }); interface IWaitUntil { token: CancellationToken; waitUntil(thenable: Promise<unknown>): void; } interface IWaitUntil { token: CancellationToken; waitUntil(thenable: Promise<unknown>): void; } type IWaitUntilData<T> = Omit<Omit<T, 'waitUntil'>, 'token'>; class AsyncEmitter<T extends IWaitUntil> { private listeners: ((d: T) => void)[] = []; get event(): (listener: (e: T) => any, thisArgs?: any, disposables?: Disposable[]) => Disposable { return (listener, thisArgs, _disposables) => { this.listeners.push(listener.bind(thisArgs)); return { dispose: () => { // } }; }; } dispose() { this.listeners = []; } async fireAsync(data: IWaitUntilData<T>, token: CancellationToken): Promise<void> { if (!this.listeners.length) { return; } const promises: Promise<unknown>[] = []; this.listeners.forEach(cb => { const event = { ...data, token, waitUntil: (thenable: Promise<WorkspaceEdit>) => { promises.push(thenable); } } as T; cb(event); }); await Promise.all(promises); } } }); </ins>

Create an ipynb file (with one Python cell) Save this Verify the entry exists in time line Change the language of the cell to Save this Verify we have an entry Click that item Bug On the left we need On the right there's no such entry in metadata This is incorrect. The ipynb has the above metadata. What seems to be happening is When the language changes, We serialize the ipynb and JSon contains the metadata However the model is not updated with corresponding changes to the ipynb file I.e. model change triggered =ipynb serialize to JSON =Json contains new metadtaa not in model =Missing step is to update the model again If we implement the last step, we could always end up in an infinite loop, Perhaps we should first update the model with metadata when ever we detect chagnes in the model before the save event. E.g. as part of pre-Save (onDidChangeNotebookDocument &/or onWillSaveNotebookDocument) This way, model will always be accurate and upto date when ipynb is serialized.
Easier way to explain the bug Create an ipynb file (with one Python cell) Save this Change the language of the cell to Save this The cell metadata will not contain the new metadata that exists in the ipynb JSON file. (model is not in sync with ipynb json)
Suggested Solution Perform model updates when we're about to save (or when we detect changes to model) When saving, ensure we save the metadata as is, without generating any new content I.e. JSON.stringify the metadata.
/cc
Considering that a serialize operation should not have side effects (ideally), enforcing a "save participant" from ipynb extension makes very good sense to me. We can leverage which support additional async operations, like how we handle attachment clearnup
For such critical metadata change, we might want to handle both auto save and manual save as we want the json and model to be always in sync.
Another simple repro step Create a notebook, and save this Add a new cell The latest entry in timeline will be However there's a difference, one has and the other (current model state) does not have any Again, model does not contain metadta (as this is a new cell) and ipynb JSON contains metadata when serialized.


Question: <ins> /*--------------------------------------------------------------------------------------------- *  Copyright (c) Microsoft Corporation. All rights reserved. *  Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; import * as sinon from 'sinon'; import { CancellationTokenSource, Disposable, EventEmitter, ExtensionContext, NotebookCellKind, NotebookDocumentChangeEvent, NotebookDocumentWillSaveEvent, NotebookEdit, NotebookRange, TextDocumentSaveReason, workspace, type CancellationToken, type NotebookCell, type NotebookDocument, type WorkspaceEdit, type WorkspaceEditMetadata } from 'vscode'; import { activate } from '../notebookModelStoreSync'; suite('Notebook Model Store Sync', () => { let disposables: Disposable[] = []; let onDidChangeNotebookDocument: EventEmitter<NotebookDocumentChangeEvent>; let onWillSaveNotebookDocument: AsyncEmitter<NotebookDocumentWillSaveEvent>; let notebook: NotebookDocument; let token: CancellationTokenSource; let editsApplied: WorkspaceEdit[] = []; let pendingPromises: Promise<void>[] = []; let cellMetadataUpdates: NotebookEdit[] = []; let applyEditStub: sinon.SinonStub<[edit: WorkspaceEdit, metadata?: WorkspaceEditMetadata | undefined], Thenable<boolean>>; setup(() => { disposables = []; notebook = { notebookType: '', metadata: {} } as NotebookDocument; token = new CancellationTokenSource(); disposables.push(token); sinon.stub(notebook, 'notebookType').get(() => 'jupyter-notebook'); applyEditStub = sinon.stub(workspace, 'applyEdit').callsFake((edit: WorkspaceEdit) => { editsApplied.push(edit); return Promise.resolve(true); }); const context = { subscriptions: [] as Disposable[] } as ExtensionContext; onDidChangeNotebookDocument = new EventEmitter<NotebookDocumentChangeEvent>(); disposables.push(onDidChangeNotebookDocument); onWillSaveNotebookDocument = new AsyncEmitter<NotebookDocumentWillSaveEvent>(); sinon.stub(NotebookEdit, 'updateCellMetadata').callsFake((index, metadata) => { const edit = (NotebookEdit.updateCellMetadata as any).wrappedMethod.call(NotebookEdit, index, metadata); cellMetadataUpdates.push(edit); return edit; } ); sinon.stub(workspace, 'onDidChangeNotebookDocument').callsFake(cb => onDidChangeNotebookDocument.event(cb) ); sinon.stub(workspace, 'onWillSaveNotebookDocument').callsFake(cb => onWillSaveNotebookDocument.event(cb) ); activate(context); }); teardown(async () => { await Promise.allSettled(pendingPromises); editsApplied = []; pendingPromises = []; cellMetadataUpdates = []; disposables.forEach(d => d.dispose()); disposables = []; sinon.restore(); }); test('Empty cell will not result in any updates', async () => { const e: NotebookDocumentChangeEvent = { notebook, metadata: undefined, contentChanges: [], cellChanges: [] }; onDidChangeNotebookDocument.fire(e); assert.strictEqual(editsApplied.length, 0); }); test('Adding cell for non Jupyter Notebook will not result in any updates', async () => { sinon.stub(notebook, 'notebookType').get(() => 'some-other-type'); const cell: NotebookCell = { document: {} as any, executionSummary: {}, index: 0, kind: NotebookCellKind.Code, metadata: {}, notebook, outputs: [] }; const e: NotebookDocumentChangeEvent = { notebook, metadata: undefined, contentChanges: [ { range: new NotebookRange(0, 0), removedCells: [], addedCells: [cell] } ], cellChanges: [] }; onDidChangeNotebookDocument.fire(e); assert.strictEqual(editsApplied.length, 0); assert.strictEqual(cellMetadataUpdates.length, 0); }); test('Adding cell will result in an update to the metadata', async () => { const cell: NotebookCell = { document: {} as any, executionSummary: {}, index: 0, kind: NotebookCellKind.Code, metadata: {}, notebook, outputs: [] }; const e: NotebookDocumentChangeEvent = { notebook, metadata: undefined, contentChanges: [ { range: new NotebookRange(0, 0), removedCells: [], addedCells: [cell] } ], cellChanges: [] }; onDidChangeNotebookDocument.fire(e); assert.strictEqual(editsApplied.length, 1); assert.strictEqual(cellMetadataUpdates.length, 1); const newMetadata = cellMetadataUpdates[0].newCellMetadata; assert.deepStrictEqual(newMetadata, { custom: { metadata: {} } }); }); test('Add cell id if nbformat is 4.5', async () => { sinon.stub(notebook, 'metadata').get(() => ({ custom: { nbformat: 4, nbformat_minor: 5 } })); const cell: NotebookCell = { document: {} as any, executionSummary: {}, index: 0, kind: NotebookCellKind.Code, metadata: {}, notebook, outputs: [] }; const e: NotebookDocumentChangeEvent = { notebook, metadata: undefined, contentChanges: [ { range: new NotebookRange(0, 0), removedCells: [], addedCells: [cell] } ], cellChanges: [] }; onDidChangeNotebookDocument.fire(e); assert.strictEqual(editsApplied.length, 1); assert.strictEqual(cellMetadataUpdates.length, 1); const newMetadata = cellMetadataUpdates[0].newCellMetadata || {}; assert.strictEqual(Object.keys(newMetadata).length, 1); assert.strictEqual(Object.keys(newMetadata.custom).length, 2); assert.deepStrictEqual(newMetadata.custom.metadata, {}); assert.ok(newMetadata.custom.id); }); test('Do not add cell id if one already exists', async () => { sinon.stub(notebook, 'metadata').get(() => ({ custom: { nbformat: 4, nbformat_minor: 5 } })); const cell: NotebookCell = { document: {} as any, executionSummary: {}, index: 0, kind: NotebookCellKind.Code, metadata: { custom: { id: '1234' } }, notebook, outputs: [] }; const e: NotebookDocumentChangeEvent = { notebook, metadata: undefined, contentChanges: [ { range: new NotebookRange(0, 0), removedCells: [], addedCells: [cell] } ], cellChanges: [] }; onDidChangeNotebookDocument.fire(e); assert.strictEqual(editsApplied.length, 1); assert.strictEqual(cellMetadataUpdates.length, 1); const newMetadata = cellMetadataUpdates[0].newCellMetadata || {}; assert.strictEqual(Object.keys(newMetadata).length, 1); assert.strictEqual(Object.keys(newMetadata.custom).length, 2); assert.deepStrictEqual(newMetadata.custom.metadata, {}); assert.strictEqual(newMetadata.custom.id, '1234'); }); test('Do not perform any updates if cell id and metadata exists', async () => { sinon.stub(notebook, 'metadata').get(() => ({ custom: { nbformat: 4, nbformat_minor: 5 } })); const cell: NotebookCell = { document: {} as any, executionSummary: {}, index: 0, kind: NotebookCellKind.Code, metadata: { custom: { id: '1234', metadata: {} } }, notebook, outputs: [] }; const e: NotebookDocumentChangeEvent = { notebook, metadata: undefined, contentChanges: [ { range: new NotebookRange(0, 0), removedCells: [], addedCells: [cell] } ], cellChanges: [] }; onDidChangeNotebookDocument.fire(e); assert.strictEqual(editsApplied.length, 0); assert.strictEqual(cellMetadataUpdates.length, 0); }); test('Store language id in custom metadata, whilst preserving existing metadata', async () => { sinon.stub(notebook, 'metadata').get(() => ({ custom: { nbformat: 4, nbformat_minor: 5, metadata: { language_info: { name: 'python' } } } })); const cell: NotebookCell = { document: { languageId: 'javascript' } as any, executionSummary: {}, index: 0, kind: NotebookCellKind.Code, metadata: { custom: { id: '1234', metadata: { collapsed: true, scrolled: true } } }, notebook, outputs: [] }; const e: NotebookDocumentChangeEvent = { notebook, metadata: undefined, contentChanges: [], cellChanges: [ { cell, document: undefined, metadata: undefined, outputs: undefined, executionSummary: undefined } ] }; onDidChangeNotebookDocument.fire(e); assert.strictEqual(editsApplied.length, 1); assert.strictEqual(cellMetadataUpdates.length, 1); const newMetadata = cellMetadataUpdates[0].newCellMetadata || {}; assert.strictEqual(Object.keys(newMetadata).length, 1); assert.strictEqual(Object.keys(newMetadata.custom).length, 2); assert.deepStrictEqual(newMetadata.custom.metadata, { collapsed: true, scrolled: true, vscode: { languageId: 'javascript' } }); assert.strictEqual(newMetadata.custom.id, '1234'); }); test('No changes when language is javascript', async () => { sinon.stub(notebook, 'metadata').get(() => ({ custom: { nbformat: 4, nbformat_minor: 5, metadata: { language_info: { name: 'javascript' } } } })); const cell: NotebookCell = { document: { languageId: 'javascript' } as any, executionSummary: {}, index: 0, kind: NotebookCellKind.Code, metadata: { custom: { id: '1234', metadata: { collapsed: true, scrolled: true } } }, notebook, outputs: [] }; const e: NotebookDocumentChangeEvent = { notebook, metadata: undefined, contentChanges: [], cellChanges: [ { cell, document: undefined, metadata: undefined, outputs: undefined, executionSummary: undefined } ] }; onDidChangeNotebookDocument.fire(e); assert.strictEqual(editsApplied.length, 0); assert.strictEqual(cellMetadataUpdates.length, 0); }); test('Remove language from metadata when cell language matches kernel language', async () => { sinon.stub(notebook, 'metadata').get(() => ({ custom: { nbformat: 4, nbformat_minor: 5, metadata: { language_info: { name: 'javascript' } } } })); const cell: NotebookCell = { document: { languageId: 'javascript' } as any, executionSummary: {}, index: 0, kind: NotebookCellKind.Code, metadata: { custom: { id: '1234', metadata: { vscode: { languageId: 'python' }, collapsed: true, scrolled: true } } }, notebook, outputs: [] }; const e: NotebookDocumentChangeEvent = { notebook, metadata: undefined, contentChanges: [], cellChanges: [ { cell, document: undefined, metadata: undefined, outputs: undefined, executionSummary: undefined } ] }; onDidChangeNotebookDocument.fire(e); assert.strictEqual(editsApplied.length, 1); assert.strictEqual(cellMetadataUpdates.length, 1); const newMetadata = cellMetadataUpdates[0].newCellMetadata || {}; assert.strictEqual(Object.keys(newMetadata).length, 1); assert.strictEqual(Object.keys(newMetadata.custom).length, 2); assert.deepStrictEqual(newMetadata.custom.metadata, { collapsed: true, scrolled: true }); assert.strictEqual(newMetadata.custom.id, '1234'); }); test('Update language in metadata', async () => { sinon.stub(notebook, 'metadata').get(() => ({ custom: { nbformat: 4, nbformat_minor: 5, metadata: { language_info: { name: 'javascript' } } } })); const cell: NotebookCell = { document: { languageId: 'powershell' } as any, executionSummary: {}, index: 0, kind: NotebookCellKind.Code, metadata: { custom: { id: '1234', metadata: { vscode: { languageId: 'python' }, collapsed: true, scrolled: true } } }, notebook, outputs: [] }; const e: NotebookDocumentChangeEvent = { notebook, metadata: undefined, contentChanges: [], cellChanges: [ { cell, document: undefined, metadata: undefined, outputs: undefined, executionSummary: undefined } ] }; onDidChangeNotebookDocument.fire(e); assert.strictEqual(editsApplied.length, 1); assert.strictEqual(cellMetadataUpdates.length, 1); const newMetadata = cellMetadataUpdates[0].newCellMetadata || {}; assert.strictEqual(Object.keys(newMetadata).length, 1); assert.strictEqual(Object.keys(newMetadata.custom).length, 2); assert.deepStrictEqual(newMetadata.custom.metadata, { collapsed: true, scrolled: true, vscode: { languageId: 'powershell' } }); assert.strictEqual(newMetadata.custom.id, '1234'); }); test('Will save event without any changes', async () => { await onWillSaveNotebookDocument.fireAsync({ notebook, reason: TextDocumentSaveReason.Manual }, token.token); }); test('Wait for pending updates to complete when saving', async () => { let resolveApplyEditPromise: (value: boolean) => void; const promise = new Promise<boolean>((resolve) => resolveApplyEditPromise = resolve); applyEditStub.restore(); sinon.stub(workspace, 'applyEdit').callsFake((edit: WorkspaceEdit) => { editsApplied.push(edit); return promise; }); const cell: NotebookCell = { document: {} as any, executionSummary: {}, index: 0, kind: NotebookCellKind.Code, metadata: {}, notebook, outputs: [] }; const e: NotebookDocumentChangeEvent = { notebook, metadata: undefined, contentChanges: [ { range: new NotebookRange(0, 0), removedCells: [], addedCells: [cell] } ], cellChanges: [] }; onDidChangeNotebookDocument.fire(e); assert.strictEqual(editsApplied.length, 1); assert.strictEqual(cellMetadataUpdates.length, 1); // Try to save. let saveCompleted = false; const saved = onWillSaveNotebookDocument.fireAsync({ notebook, reason: TextDocumentSaveReason.Manual }, token.token); saved.finally(() => saveCompleted = true); await new Promise((resolve) => setTimeout(resolve, 10)); // Verify we have not yet completed saving. assert.strictEqual(saveCompleted, false); resolveApplyEditPromise!(true); await new Promise((resolve) => setTimeout(resolve, 1)); // Should have completed saving. saved.finally(() => saveCompleted = true); }); interface IWaitUntil { token: CancellationToken; waitUntil(thenable: Promise<unknown>): void; } interface IWaitUntil { token: CancellationToken; waitUntil(thenable: Promise<unknown>): void; } type IWaitUntilData<T> = Omit<Omit<T, 'waitUntil'>, 'token'>; class AsyncEmitter<T extends IWaitUntil> { private listeners: ((d: T) => void)[] = []; get event(): (listener: (e: T) => any, thisArgs?: any, disposables?: Disposable[]) => Disposable { return (listener, thisArgs, _disposables) => { this.listeners.push(listener.bind(thisArgs)); return { dispose: () => { // } }; }; } dispose() { this.listeners = []; } async fireAsync(data: IWaitUntilData<T>, token: CancellationToken): Promise<void> { if (!this.listeners.length) { return; } const promises: Promise<unknown>[] = []; this.listeners.forEach(cb => { const event = { ...data, token, waitUntil: (thenable: Promise<WorkspaceEdit>) => { promises.push(thenable); } } as T; cb(event); }); await Promise.all(promises); } } }); </ins>

<details<summaryInternal Notes</summaryHere's the work flow Add a cell A change event is triggered, undo redo stack is updated Last undo redo is User hits save button Working copy starts to handle the save operation Save participants start We add a save participant and update the model via a work space edit Now a new change event is triggered This results in a whole new undo redo stack The latest (last) undo redo item is Save participants done Working copy keeps track of the last as the entry for Problems Time line does not include , time line shows only I guess this is correct, However from a users perspective, the labels are never going to be meaningful as we'd always say . </details


Question: } fs.writeFileSync(this.workspacesJsonPath, JSON.stringify(this.backups)); } catch (ex) { <del> console.error('Could not save workspaces.json', ex); </del> <ins> console.error(`Backup: Could not save workspaces.json: ${ex.toString()}`); </ins> } }

Moving this over from After the backups are not restored if the workspace directory doesn't exist anymore. The workspace is no longer tracked but the backups are kept around in the backup directory should they be needed later.
Tricky.... I would expect an empty window to open with those files dirty. This might require some work on the area of restoring on empty workspaces. Have not thought about this longer yet.
Marking as important because this means data loss if the folder is deleted on disk.
maybe this should be done on the main side on startup similarly to If a folder no longer exists, convert it into an empty workspace backup and let it open as empty window.
On test plan:


Question: import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; <del> import { ITerminalInstance, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, TERMINAL_PANEL_ID, IShellLaunchConfig, ITerminalProcessManager, ProcessState } from 'vs/workbench/parts/terminal/common/terminal'; </del> <ins> import { ITerminalInstance, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, TERMINAL_PANEL_ID, IShellLaunchConfig, ITerminalProcessManager, ProcessState, NEVER_MEASURE_RENDER_TIME_STORAGE_KEY } from 'vs/workbench/parts/terminal/common/terminal'; </ins> import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { TabFocus } from 'vs/editor/common/config/commonEditorConfig';

Upstream:
It's not true that it takes very long. It does not get visible at all. I have to downsize the terminal pane to a certain size to make it visible again. In my case it's about 5-10 lines. Seems to me that the canvas is not resized properly. There are no problems if I disable GPU rendering (code --disable-gpu). Also without any noticeable performance issues. Would be nice to have a flag in the preferences to disable GPU rendering as a quick workaround. One of the reasons for using code (instead of Atom or Sublime) was the integrated terminal which seems to be broken since the new canvas rendering was introduced. My System Config: OS X 10.11.6, GPU: NVidia 8800, 4GB RAM
that will probably get fixed as part of this as well, it's probably something to do with your GPU/Chromium not liking the texture sizes above a certain size.


Question: }); viewsRegistry.registerViewWelcomeContent(WelcomeView.ID, { <del> 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), </del> <ins> content: `[${localize('detectThenRunAndDebug', "Show all automatic debug configurations")}](command:${SELECT_AND_START_ID}).`, </ins> when: CONTEXT_DEBUGGERS_AVAILABLE, group: ViewContentGroups.Debug, order: 10

There's an unnecessary whitespace in "i18n/vscode-language-pack-". The whitespace after "command:" makes the link dead. !
you can add a comment to this string like so: to ensure that the command never has a problem like this. Notice that the example string has, in the Locked area, a at the start of it. This will make sure there is no whitespace between them.


Question: .suggest-input-container .monaco-editor, .suggest-input-container .mtk1 { /* allow the embedded monaco to be styled from the outer context */ <del> background-color: inherit; </del> <ins> background-color: transparent; </ins> color: inherit; }

Issue Type: <bBug</bCompared to input boxes in other viewlets, the search box in the extensions viewlets looks slightly different with a little padding of a different color inside the box. VS Code version: Code - Insiders 1.27.0-insider (, 2018-08-15T05:19:24.200Z) OS version: WindowsNT x64 10.0. <details<summarySystem Info</summary </details<!-- generated by issue reporter --
This appears to be because the sets the background color to a transparent color (), if that is changed to , the look is the same but without the border. This seems like incorrect behavior of the renderer, as the type of color shouldn't affect what gets colored.
Actually, looks like the transparency is showing up differently on the different backgrounds. If you increase the alpha, it starts to look like this: ! I'll look in to making the transparency affect both equally


Question: if (searchView.shouldShowAIResults()) { if (searchView.model.hasAIResults) { <del> viewer.expandAll(); </del> <ins> await forcedExpandRecursively(viewer, undefined); </ins> } else { <del> await viewer.expand(searchView.model.searchResult.plainTextSearchResult, true); </del> <ins> await forcedExpandRecursively(viewer, searchView.model.searchResult.plainTextSearchResult); </ins> } } else { <del> viewer.expandAll(); </del> <ins> await forcedExpandRecursively(viewer, undefined); </ins> } } } <ins> /** * Recursively expand all nodes in the search results tree that are a child of `element` * If `element` is not provided, it is the root node. */ export async function forcedExpandRecursively( viewer: WorkbenchCompressibleAsyncDataTree<SearchResult, RenderableMatch, void>, element: RenderableMatch | undefined ) { if (element) { if (!viewer.hasNode(element)) { return; } await viewer.expand(element, true); } const children = viewer.getNode(element)?.children; if (children) { for (const child of children) { if (child.element instanceof SearchResult) { throw Error('SearchResult should not be a child of a RenderableMatch'); } forcedExpandRecursively(viewer, child.element); } } } </ins> function clearSearchResults(accessor: ServicesAccessor) { const viewsService = accessor.get(IViewsService); const searchView = getSearchView(viewsService);

to search, then click the that expands the results. :bug: does not expand. Looking into this more, it is because the call for the node isn't working.
I've also seen the same thing with not working. Unsure if this is related, but the repro is the same, but between step 2 and 3, expand the AI results.
This seems to also happen in when in tree mode.
It could be very expensive if expand all would actually expand all tree nodes in an async data tree. It's possible to have infinite deep async trees so in those cases this method would crash. My recommendation would be to create your own implementation for this in the search view. If that does not work the way you would like it to, we could consider alternatives. I think the bulk edit pane has a similar behaviour here:


Question: private async _getCommandActions(command: ITerminalCommand): Promise<IAction[]> { const actions: IAction[] = []; <ins> if (command.command !== '') { const labelRun = localize("terminal.rerunCommand", 'Rerun Command'); actions.push({ class: undefined, tooltip: labelRun, dispose: () => { }, id: 'terminal.rerunCommand', label: labelRun, enabled: true, run: () => this._onDidRequestRunCommand.fire({ command }) }); const labelCopy = localize("terminal.copyCommand", 'Copy Command'); actions.push({ class: undefined, tooltip: labelCopy, dispose: () => { }, id: 'terminal.copyCommand', label: labelCopy, enabled: true, run: () => this._clipboardService.writeText(command.command) }); } </ins> if (command.hasOutput) { <ins> if (actions.length > 0) { actions.push(new Separator()); } const labelText = localize("terminal.copyOutput", 'Copy Output'); </ins> actions.push({ <del> class: 'copy-output', tooltip: 'Copy Output', dispose: () => { }, id: 'terminal.copyOutput', label: localize("terminal.copyOutput", 'Copy Output'), enabled: true, </del> <ins> class: undefined, tooltip: labelText, dispose: () => { }, id: 'terminal.copyOutput', label: labelText, enabled: true, </ins> run: () => this._clipboardService.writeText(command.getOutput()!) }); <ins> const labelHtml = localize("terminal.copyOutputAsHtml", 'Copy Output as HTML'); </ins> actions.push({ <del> class: 'copy-output', tooltip: 'Copy Output as HTML', dispose: () => { }, id: 'terminal.copyOutputAsHtml', label: localize("terminal.copyOutputAsHtml", 'Copy Output as HTML'), enabled: true, </del> <ins> class: undefined, tooltip: labelHtml, dispose: () => { }, id: 'terminal.copyOutputAsHtml', label: labelHtml, enabled: true, </ins> run: () => this._onDidRequestRunCommand.fire({ command, copyAsHtml: true }) }); } <del> if (command.command !== '') { actions.push({ class: 'rerun-command', tooltip: 'Rerun Command', dispose: () => { }, id: 'terminal.rerunCommand', label: localize("terminal.rerunCommand", 'Rerun Command'), enabled: true, run: () => this._onDidRequestRunCommand.fire({ command }) }); </del> <ins> if (actions.length > 0) { actions.push(new Separator()); </ins> } <ins> const label = localize("terminal.learnShellIntegration", 'Learn About Shell Integration'); </ins> actions.push({ <del> class: 'how-does-this-work', tooltip: 'How does this work?', dispose: () => { }, id: 'terminal.howDoesThisWork', label: localize("terminal.howDoesThisWork", 'How does this work?'), enabled: true, </del> <ins> class: undefined, tooltip: label, dispose: () => { }, id: 'terminal.learnShellIntegration', label, enabled: true, </ins> run: () => this._openerService.open('https://code.visualstudio.com/docs/editor/integrated-terminal#_shell-integration') }); return actions;

I wanted to copy this command to a diff't window and see how the selection didn't work so well https://user- Since we know where the prompt ends, could we do something about this? If not, should we have a copy command action?
Double click is all driven by the links system which works pretty well generally, adding prompt knowledge in there would be a bit of a mess and need some more APIs to override specific cell double-click selection handling since links live in but shell integration is all in vscode. The way I do this is to double click and drag it to the right a bit. I'd say this is out of scope but we can talk about it more if you want.
Yeah I thought abt the complications with the existing link/selection code and decided that would be too much. I wonder if we should have a copy command action though?
Sure we could do that, it would also be good to get some more order to this: ! Proposal: Rerun Command Copy Command - Copy Output Copy Output as HTML - How does this work?
Verified. Looks like the proposal to me. <img width="277" alt="image" src="https://user-"


Question: */ globalFindClipboard?: boolean; /** <del> * Controls whether the search automatically restarts from the beginning (or the end) when no further matches can be found </del> <ins> * Controls whether the search result and diff result automatically restarts from the beginning (or the end) when no further matches can be found </ins> */ loop?: boolean; }

<!-- Do Not Delete This! featurerequesttemplate --<!-- Please read our Rules of Conduct: --<!-- Please search existing issues to avoid creating duplicates. --<!-- Describe the feature you'd like. --Setting to stop at the bottom (or top) when navigating in the diff view through the changes with "next change" and reaching the last change. Like did in
Can I provide a PR for this feature
Go ahead.
Thanks a lot for implementing this. But what is the setting to enable this?
Looks like this behavior got to the "" setting
No, does not affect this unfortunately.


Question: abstract renderChanges(response: ReplyResponse): Promise<Position | undefined>; abstract hasFocus(): boolean; <del> abstract needsMargin(): boolean; </del> } export class PreviewStrategy extends EditModeStrategy {

I think we used to indent inline chat to the indentation level of the line: ! Steps: have an indented line press enter to be on an empty line (still indented) press to bring up inline chat


Question: resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== <ins> fs-extra@^11.2.0: version "11.2.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.2.0.tgz#e70e17dfad64232287d01929399e0ea7c86b0e5b" integrity sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw== dependencies: graceful-fs "^4.2.0" jsonfile "^6.0.1" universalify "^2.0.0" </ins> github-from-package@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce" integrity sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4= <del> graceful-fs@4.2.11: </del> <ins> graceful-fs@4.2.11, graceful-fs@^4.1.6, graceful-fs@^4.2.0: </ins> 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==

a folder in WSL and show logs the error log !
Are things broken in this case or just a bad stack trace. I'll take a look to see if I missed a module include somewhere
Just a bad stacktrace from what I see, though I didn't intentionally go test things thoroughly


Question: value: that.userDataProfileService.getShortName(profile), title: localize('change short name', "Change Short Name..."), validateInput: async (value: string) => { <del> if (profile.shortName !== value && !ThemeIcon.fromString(value) && charCount(value) > 2) { </del> <ins> if (profile.shortName === value) { return undefined; } const themeIcon = ThemeIcon.fromString(value); if (themeIcon) { if (Codicon.getAll().some(c => c.id === themeIcon.id)) { return undefined; } return localize('invalid codicon', "Invalid codicon. Please use a valid codicon id."); } if (charCount(value) > 2) { </ins> return localize('invalid short name', "Short name should be at most 2 characters long."); } return undefined;

I don't know the codicons by heart. So I went with . Here's what I see: ! ! Can you validate if is valid? Or somehow show a better default? I'd prefer validation though. Testing


Question: import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { MenuRegistry } from 'vs/platform/actions/common/actions'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; <ins> import { commandsExtensionPoint } from 'vs/workbench/api/common/menusExtensionPoint'; </ins> export class KeyboardMapperFactory { public static readonly INSTANCE = new KeyboardMapperFactory();

contribute command that uses enablement assign a keybinding, via :bug: enablement isn't honoured
When users configure keybindings we allow them to change preconditions as well, e.g rename requires an editable document so I don't plan to have a different strategy for contributed commands
I don't see the nice "Command 'Hello World' is not enabled in the current context." info toast when I try the keybinding though. I only see that message when I try to run it from the command palette. The command correctly doesn't run when not enabled though.
Yeah, that's the design. Keybindings don't show messages


Question: } /** <del> * Splits a recent label in name and parent path, supporting both '/' and '' and workspace suffixes </del> <ins> * Splits a recent label in name and parent path, supporting both '/' and '' and workspace suffixes. * If the location is remote, the remote name is included in the name part. </ins> */ <del> export function splitRecentLabel(recentLabel: string) { </del> <ins> export function splitRecentLabel(recentLabel: string): { name: string; parentPath: string } { </ins> if (recentLabel.endsWith(']')) { // label with workspace suffix const lastIndexOfSquareBracket = recentLabel.lastIndexOf(' [', recentLabel.length - 2); if (lastIndexOfSquareBracket !== -1) { const split = splitName(recentLabel.substring(0, lastIndexOfSquareBracket)); <del> return { name: split.name, parentPath: split.parentPath + recentLabel.substring(lastIndexOfSquareBracket) }; </del> <ins> const remoteNameWithSpace = recentLabel.substring(lastIndexOfSquareBracket); return { name: split.name + remoteNameWithSpace, parentPath: split.parentPath }; </ins> } } return splitName(recentLabel);

<!-- Do Not Delete This! bugreporttemplate --<!-- Please read our Rules of Conduct: --<!-- Read our guide about submitting issues: --<!-- Search existing issues to avoid creating duplicates. --<!-- Test using the latest Insiders build to see if your issue has already been fixed: --<!-- Instead of creating your report here, use 'Report Issue' from the 'Help' menu in VS Code to pre-fill useful information. --<!-- Launch with to check. --Does this issue occur when all extensions are disabled?: Yes <!-- If you answered No above, use 'Help: Start Extension Bisect' from Command Palette to try to identify the cause. --<!-- Issues caused by an extension need to be reported directly to the extension publisher. The 'Help Report Issue' dialog can assist with this. --- VS Code Version: 1.81.1 OS Version: win10 Steps to Reproduce: new version vscode after 1.78.2 remote connection before vscode 1.78.2 in win 10 right click win10 taskbar show remote connect like ! but recently I update my vscode to 1.81.1, the information for remote node disapear. ! This bug is quite inconvenience when I have many projects with the same name in different remote nodes.
Same Issue Here
Do you own this part of the label service Aware of any changes?


Question: protocol: true } : undefined; <del> this._proxy.$setTunnelProvider(tunnelFeatures); </del> <ins> this._proxy.$setTunnelProvider(tunnelFeatures, !!provider.tunnelFactory); </ins> } } else { this._forwardPortProvider = undefined;

Type: <bBug</bvscode with wsl: or similar tool to monitor CPU usage when vscode is idle or minimized to the taskbar. Expected: CPU usage for vscode's remote server should stay around 0% at idle conditions. Actual: CPU usage stays between 2-10 % VS Code version: Code 1.91.1 (, 2024-07-09T22:06:49.809Z) OS version: WindowsNT x64 10.0. Modes: Remote OS version: Linux x64 6.6.36.3-microsoft-standard-WSL2 <details<summarySystem Info</summary </details<details<summaryExtensions (10)</summaryExtensionVersion ------ remote-wsl0.88.2 material-icon-theme5.6.0 vim1.27.3 markdown-checkbox0.4.0 gitlens15.2.0 prettier-vscode10.4.0 hexeditor1.10.0 advanced-new-file1.2.2 vscode-fileutils3.10.3 even-better-toml0.19.2 </details<!-- generated by issue reporter --
I had earlier opened the issue at where I provided more in-depth issue description. I'm just going to put the same description here. I recently noticed high CPU usage and faster battery drain (intel cpu) when working with any project under WSL. To find out what's taking so much cpu time (even when I'm doing nothing, apart from staring at the screen), I used to list processes in the order of cpu usage: ! Looked like vscode's remote server running inside wsl is taking CPU. So, I went a little further to profile the running instance of the remote server and I found: ! ! from is being called at a regular interval with a small delay (~1000ms). : ! I was able to trace back to : ! And I finally found where it is being called repeatedly: ! Now, why is it a bug? Because I've disabled all related settings in my config, yet the tunnel monitoring service is running in the background. I expect the service to not be running when or related configs are disabled. The service is currently always running.
Wow, great job, thanks for tracking down the problem!
I'm having trouble understanding this: Is WSL going to end up being a ?


Question: protocol: true } : undefined; <del> this._proxy.$setTunnelProvider(tunnelFeatures); </del> <ins> this._proxy.$setTunnelProvider(tunnelFeatures, !!provider.tunnelFactory); </ins> } } else { this._forwardPortProvider = undefined;

I don't think we actually want to show the ports view in WSL even if the Tunnels extension isn't installed.
I agree. For non-managed remote authorities created just via the in the workbench, how did we determine whether or not to show the ports view? We should apply the same logic here...
The ports view is controlled by a context key getting set. It gets set in two ways: an extension that wants the ports view. This is what dev containers does. a tunnel factory getting . Since the tunnel factory is always now, we're always enabling it. I should be able to add a check if the tunnel factory is coming from an extension and only do 2 if that's the case.
Looks like we also show the ports view when there's an extension with an actiation event: Is this perhaps no longer needed with the work you did for tunnels I don't remember exactly why I it.
I've moved the check to happen when there's been a port forwarded.
Looks like this was actually a feature: You can set to disable this. I'm also going to try to improve the description of and .
<!-- AUTHORVERIFICATIONREQUEST --This bug has been fixed in the latest release of ! you can help us out by commenting if things are now working as expected. If things still don't seem right, please ensure you're on version of Insiders (today's or later - you can use in the command palette to check), and leave a comment letting us know what isn't working as expected. Happy Coding!
To verify: the descriptions of and . Verify that you can understand what the settings do and how they're related. (These settings may be a bit confusing. We should consider deprecating and only have ) . to or if it isn't already. the WSL extension, connect to a WSL remote. the log level to Trace for the Remote Extension Host. the output for the Remote Extension Host. Verify that you see a steady stream of log lines that say "candidate" coming in. This means that port discovery is on. to and to . Reload. sure that the log level of the Remote Extension Host is still set to Trace the output for the Remote Extension Host. Verify that you DO NOT see a steady stream of log lines that say "candidate" coming in.


Question: protocol: true } : undefined; <del> this._proxy.$setTunnelProvider(tunnelFeatures); </del> <ins> this._proxy.$setTunnelProvider(tunnelFeatures, !!provider.tunnelFactory); </ins> } } else { this._forwardPortProvider = undefined;

In the WSL workspace, I don't see any log lines that say "candidate" even after starting a Python HTTP server.
I can't repro this. Do you have an old version of WSL installed?
I verified that on my machine the work.
I haven't checked with Insider, but in stable I can confirm 'candidate' logs are not visible after Step 6 of the verification steps. Thanks. I also want to point out that it's not clear from the description that there's a process monitoring service running in the background, and that it can be disabled using . Should the default option be instead of ?
Thanks for trying it out If we set it to , then we might end up not showing any ports in the Ports view. In insiders, I updated the text of the setting: Sets the source from which ports are automatically forwarded when is true. When is false, will be used to find information about ports that have already been forwarded. On Windows and macOS remotes, the process and hybrid options have no effect and output will be used. It still doesn't explicitly say there will be process monitoring, but it should be clearer.


Question: this.focus(); } <del> getContextMenuActions(): IAction[] { </del> <ins> getContextMenuActions(viewDescriptor?: IViewDescriptor): IAction[] { </ins> const result: IAction[] = []; <ins> if (viewDescriptor) { result.push(<IAction>{ id: `${viewDescriptor.id}.removeView`, label: localize('hideView', "Hide"), enabled: viewDescriptor.canToggleVisibility, run: () => this.toggleViewVisibility(viewDescriptor.id) }); } </ins> const viewToggleActions = this.viewsModel.viewDescriptors.map(viewDescriptor => (<IAction>{ id: `${viewDescriptor.id}.toggleVisibility`, label: viewDescriptor.name,

insiders, install remote SSH extension the Remote view container all the views via context menu menu on the title -the views are not there and they can not be brought back -the views are back -I would expect the hidden state to be preserved fyi !
Assigning to as remote viewlet has special treatment.
Original decision was that none of the views in the filter-view should be hide-able. Looks like I missed a spot!


Question: import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { IMarkerData, IMarkerService } from 'vs/platform/markers/common/markers'; <del> import { IInlineChatService, IInlineChatSessionProvider } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; </del> <ins> import { ChatAgentLocation, IChatAgent, IChatAgentData, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; </ins> import { CellDiagnostics } from 'vs/workbench/contrib/notebook/browser/contrib/cellDiagnostics/cellDiagnosticEditorContrib'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; import { CellKind, NotebookSetting } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { ICellExecutionStateChangedEvent, IExecutionStateChangedEvent, INotebookCellExecution, INotebookExecutionStateService, NotebookExecutionType } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; import { setupInstantiationService, TestNotebookExecutionStateService, withTestNotebook } from 'vs/workbench/contrib/notebook/test/browser/testNotebookEditor'; <ins> import { nullExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; </ins> suite('notebookCellDiagnostics', () => {

There are some parts in notebook land that depend on . This service and its provider are deprecated and should be removed. As of today the following dependencies must be cleaned up first: , , The should be used instead, it will allow you to know if a agent per location exists or not