diff --git a/.vscode/notebooks/endgame.github-issues b/.vscode/notebooks/endgame.github-issues index 9152bc8f163..9a7842a3570 100644 --- a/.vscode/notebooks/endgame.github-issues +++ b/.vscode/notebooks/endgame.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS=repo:microsoft/vscode repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-js-debug repo:microsoft/vscode-remote-release repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-remotehub repo:microsoft/vscode-jupyter repo:microsoft/vscode-python\r\n\r\n$MILESTONE=milestone:\"July 2021\"" + "value": "$REPOS=repo:microsoft/vscode repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-js-debug repo:microsoft/vscode-remote-release repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-remotehub\n\n$MILESTONE=milestone:\"July 2021\"" }, { "kind": 1, diff --git a/.vscode/notebooks/my-endgame.github-issues b/.vscode/notebooks/my-endgame.github-issues index 5f705ed164a..fe93406d483 100644 --- a/.vscode/notebooks/my-endgame.github-issues +++ b/.vscode/notebooks/my-endgame.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS=repo:microsoft/vscode repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-js-debug repo:microsoft/vscode-remote-release repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remotehub repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-jupyter repo:microsoft/vscode-python\n\n$MILESTONE=milestone:\"July 2021\"\n\n$MINE=assignee:@me" + "value": "$REPOS=repo:microsoft/vscode repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-js-debug repo:microsoft/vscode-remote-release repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remotehub repo:microsoft/vscode-emmet-helper\n\n$MILESTONE=milestone:\"July 2021\"\n\n$MINE=assignee:@me" }, { "kind": 1, diff --git a/extensions/ipynb/src/helpers.ts b/extensions/ipynb/src/helpers.ts index 31fa9193355..db51e14a37b 100644 --- a/extensions/ipynb/src/helpers.ts +++ b/extensions/ipynb/src/helpers.ts @@ -46,8 +46,8 @@ const orderOfMimeTypes = [ 'image/gif', 'text/latex', 'text/markdown', - 'image/svg+xml', 'image/png', + 'image/svg+xml', 'image/jpeg', 'application/json', 'text/plain' diff --git a/package.json b/package.json index 1f80f8ebbc3..d354f1c1c37 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,6 @@ "@vscode/vscode-languagedetection": "1.0.15", "applicationinsights": "1.0.8", "chokidar": "3.5.1", - "eslint-plugin-header": "3.1.1", "graceful-fs": "4.2.6", "http-proxy-agent": "^2.1.0", "https-proxy-agent": "^2.2.3", @@ -132,6 +131,7 @@ "deemon": "^1.4.0", "electron": "13.1.7", "eslint": "6.8.0", + "eslint-plugin-header": "3.1.1", "eslint-plugin-jsdoc": "^19.1.0", "event-stream": "3.3.4", "fancy-log": "^1.3.3", diff --git a/src/vs/editor/contrib/inlineCompletions/ghostText.ts b/src/vs/editor/contrib/inlineCompletions/ghostText.ts index 6817c1d24b7..077a85b1718 100644 --- a/src/vs/editor/contrib/inlineCompletions/ghostText.ts +++ b/src/vs/editor/contrib/inlineCompletions/ghostText.ts @@ -28,9 +28,9 @@ export class GhostText { this.parts.every((part, index) => part.equals(other.parts[index])); } - render(text: string, debug: boolean = false): string { + render(documentText: string, debug: boolean = false): string { const l = this.lineNumber; - return applyEdits(text, + return applyEdits(documentText, [ ...this.parts.map(p => ({ range: { startLineNumber: l, endLineNumber: l, startColumn: p.column, endColumn: p.column }, @@ -39,6 +39,23 @@ export class GhostText { ] ); } + + renderForScreenReader(lineText: string): string { + if (this.parts.length === 0) { + return ''; + } + const lastPart = this.parts[this.parts.length - 1]; + + const cappedLineText = lineText.substr(0, lastPart.column - 1); + const text = applyEdits(cappedLineText, + this.parts.map(p => ({ + range: { startLineNumber: 1, endLineNumber: 1, startColumn: p.column, endColumn: p.column }, + text: p.lines.join('\n') + })) + ); + + return text.substring(this.parts[0].column - 1); + } } class PositionOffsetTransformer { diff --git a/src/vs/editor/contrib/inlineCompletions/ghostTextController.ts b/src/vs/editor/contrib/inlineCompletions/ghostTextController.ts index 8e76f4a8a25..c42f11b7e76 100644 --- a/src/vs/editor/contrib/inlineCompletions/ghostTextController.ts +++ b/src/vs/editor/contrib/inlineCompletions/ghostTextController.ts @@ -15,6 +15,7 @@ import * as nls from 'vs/nls'; import { ContextKeyExpr, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { GhostTextModel } from 'vs/editor/contrib/inlineCompletions/ghostTextModel'; +import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; export class GhostTextController extends Disposable { public static readonly inlineSuggestionVisible = new RawContextKey('inlineSuggestionVisible', false, nls.localize('inlineSuggestionVisible', "Whether an inline suggestion is visible")); @@ -28,7 +29,7 @@ export class GhostTextController extends Disposable { private triggeredExplicitly = false; protected readonly activeController = this._register(new MutableDisposable()); - private get activeModel(): GhostTextModel | undefined { + public get activeModel(): GhostTextModel | undefined { return this.activeController.value?.model; } @@ -168,21 +169,23 @@ const GhostTextCommand = EditorCommand.bindToContribution(GhostTextController.ge export const commitInlineSuggestionAction = new GhostTextCommand({ id: 'editor.action.inlineSuggest.commit', - precondition: ContextKeyExpr.and( - GhostTextController.inlineSuggestionVisible, - GhostTextController.inlineSuggestionHasIndentation.toNegated(), - EditorContextKeys.tabMovesFocus.toNegated() - ), - kbOpts: { - weight: 200, - primary: KeyCode.Tab, - }, + precondition: GhostTextController.inlineSuggestionVisible, handler(x) { x.commit(); x.editor.focus(); } }); registerEditorCommand(commitInlineSuggestionAction); +KeybindingsRegistry.registerKeybindingRule({ + primary: KeyCode.Tab, + weight: 200, + id: commitInlineSuggestionAction.id, + when: ContextKeyExpr.and( + commitInlineSuggestionAction.precondition, + EditorContextKeys.tabMovesFocus.toNegated(), + GhostTextController.inlineSuggestionHasIndentation.toNegated() + ), +}); registerEditorCommand(new GhostTextCommand({ id: 'editor.action.inlineSuggest.hide', diff --git a/src/vs/editor/contrib/inlineCompletions/inlineCompletionsHoverParticipant.ts b/src/vs/editor/contrib/inlineCompletions/inlineCompletionsHoverParticipant.ts index 90a42c6510c..84b9ce8f9d1 100644 --- a/src/vs/editor/contrib/inlineCompletions/inlineCompletionsHoverParticipant.ts +++ b/src/vs/editor/contrib/inlineCompletions/inlineCompletionsHoverParticipant.ts @@ -14,6 +14,12 @@ import { ICommandService } from 'vs/platform/commands/common/commands'; import { IMenuService, MenuId, MenuItemAction } from 'vs/platform/actions/common/actions'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ITextContentData, IViewZoneData } from 'vs/editor/browser/controller/mouseTarget'; +import * as dom from 'vs/base/browser/dom'; +import { MarkdownRenderer } from 'vs/editor/browser/core/markdownRenderer'; +import { MarkdownString } from 'vs/base/common/htmlContent'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; export class InlineCompletionsHover implements IHoverPart { constructor( @@ -38,10 +44,13 @@ export class InlineCompletionsHover implements IHoverPart { export class InlineCompletionsHoverParticipant implements IEditorHoverParticipant { constructor( private readonly _editor: ICodeEditor, - hover: IEditorHover, + private readonly _hover: IEditorHover, @ICommandService private readonly _commandService: ICommandService, @IMenuService private readonly _menuService: IMenuService, @IContextKeyService private readonly _contextKeyService: IContextKeyService, + @IModeService private readonly _modeService: IModeService, + @IOpenerService private readonly _openerService: IOpenerService, + @IAccessibilityService private readonly accessibilityService: IAccessibilityService, ) { } suggestHoverAnchor(mouseEvent: IEditorMouseEvent): HoverAnchor | null { @@ -82,6 +91,11 @@ export class InlineCompletionsHoverParticipant implements IEditorHoverParticipan renderHoverParts(hoverParts: InlineCompletionsHover[], fragment: DocumentFragment, statusBar: IEditorHoverStatusBar): IDisposable { const disposableStore = new DisposableStore(); + const part = hoverParts[0]; + + if (this.accessibilityService.isScreenReaderOptimized()) { + this.renderScreenReaderText(part, fragment, disposableStore); + } const menu = disposableStore.add(this._menuService.createMenu( MenuId.InlineCompletionsActions, @@ -108,7 +122,7 @@ export class InlineCompletionsHoverParticipant implements IEditorHoverParticipan for (const action of actions) { action.setEnabled(false); } - hoverParts[0].hasMultipleSuggestions().then(hasMore => { + part.hasMultipleSuggestions().then(hasMore => { for (const action of actions) { action.setEnabled(hasMore); } @@ -128,4 +142,28 @@ export class InlineCompletionsHoverParticipant implements IEditorHoverParticipan return disposableStore; } + + private renderScreenReaderText(part: InlineCompletionsHover, fragment: DocumentFragment, disposableStore: DisposableStore) { + const $ = dom.$; + const markdownHoverElement = $('div.hover-row.markdown-hover'); + const hoverContentsElement = dom.append(markdownHoverElement, $('div.hover-contents')); + const renderer = disposableStore.add(new MarkdownRenderer({ editor: this._editor }, this._modeService, this._openerService)); + const render = (code: string) => { + disposableStore.add(renderer.onDidRenderAsync(() => { + hoverContentsElement.className = 'hover-contents code-hover-contents'; + this._hover.onContentsChanged(); + })); + + const inlineSuggestionAvailable = nls.localize('inlineSuggestionFollows', "Inline Suggestion:"); + const renderedContents = disposableStore.add(renderer.render(new MarkdownString().appendText(inlineSuggestionAvailable).appendCodeblock('text', code))); + hoverContentsElement.replaceChildren(renderedContents.element); + }; + + const ghostText = part.controller.activeModel?.inlineCompletionsModel?.ghostText; + if (ghostText) { + const lineText = this._editor.getModel()!.getLineContent(ghostText.lineNumber); + render(ghostText.renderForScreenReader(lineText)); + } + fragment.appendChild(markdownHoverElement); + } } diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index cb4aa52694e..b3dde9d781c 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -13903,9 +13903,9 @@ declare module 'vscode' { } /** - * Entry point to discover and execute tests. It contains {@link items} which + * Entry point to discover and execute tests. It contains {@link TestController.items} which * are used to populate the editor UI, and is associated with - * {@link createRunProfile run profiles} to allow + * {@link TestController.createRunProfile run profiles} to allow * for tests to be executed. */ export interface TestController { @@ -13921,16 +13921,16 @@ declare module 'vscode' { label: string; /** - * Available test items. Tests in the workspace should be added in this - * collection. The extension controls when to add these, although the - * editor may request children using the {@link resolveHandler}, - * and the extension should add tests for a file when - * {@link vscode.workspace.onDidOpenTextDocument} fires in order for - * decorations for tests within a file to be visible. + * A collection of "top-level" {@link TestItem} instances, which can in + * turn have their own {@link TestItem.children | children} to form the + * "test tree." * - * Tests in this collection should be watched and updated by the extension - * as files change. See {@link resolveHandler} for details around - * for the lifecycle of watches. + * The extension controls when to add tests. For example, extensions should + * add tests for a file when {@link vscode.workspace.onDidOpenTextDocument} + * fires in order for decorations for tests within a file to be visible. + * + * However, the editor may sometimes explicitly request children using the + * {@link resolveHandler} See the documentation on that method for more details. */ readonly items: TestItemCollection; diff --git a/src/vs/workbench/api/common/extHostTerminalService.ts b/src/vs/workbench/api/common/extHostTerminalService.ts index c35f7594626..6f6da4761c6 100644 --- a/src/vs/workbench/api/common/extHostTerminalService.ts +++ b/src/vs/workbench/api/common/extHostTerminalService.ts @@ -147,7 +147,7 @@ export class ExtHostTerminal { }); } - public async createExtensionTerminal(isSplitTerminal?: boolean, target?: TerminalLocation, iconPath?: TerminalIcon): Promise { + public async createExtensionTerminal(isSplitTerminal?: boolean, target?: TerminalLocation, iconPath?: TerminalIcon, color?: ThemeColor): Promise { if (typeof this._id !== 'string') { throw new Error('Terminal has already been created'); } @@ -155,6 +155,7 @@ export class ExtHostTerminal { name: this._name, isExtensionCustomPtyTerminal: true, icon: iconPath, + color: ThemeColor.isThemeColor(color) ? color.id : undefined, isSplitTerminal, target }); @@ -373,7 +374,7 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I public createExtensionTerminal(options: vscode.ExtensionTerminalOptions, internalOptions?: ITerminalInternalOptions): vscode.Terminal { const terminal = new ExtHostTerminal(this._proxy, generateUuid(), options, options.name); const p = new ExtHostPseudoterminal(options.pty); - terminal.createExtensionTerminal(internalOptions?.isSplitTerminal, internalOptions?.target, asTerminalIcon(options.iconPath)).then(id => { + terminal.createExtensionTerminal(internalOptions?.isSplitTerminal, internalOptions?.target, asTerminalIcon(options.iconPath), asTerminalColor(options.color)).then(id => { const disposable = this._setupExtHostProcessListeners(id, p); this._terminalProcessDisposables[id] = disposable; }); @@ -856,3 +857,7 @@ function asTerminalIcon(iconPath?: vscode.Uri | { light: vscode.Uri; dark: vscod color: iconPath.color as ThemeColor }; } + +function asTerminalColor(color?: vscode.ThemeColor): ThemeColor | undefined { + return ThemeColor.isThemeColor(color) ? color as ThemeColor : undefined; +} diff --git a/src/vs/workbench/contrib/comments/browser/reactionsAction.ts b/src/vs/workbench/contrib/comments/browser/reactionsAction.ts index 28215b94592..a3e86b9d645 100644 --- a/src/vs/workbench/contrib/comments/browser/reactionsAction.ts +++ b/src/vs/workbench/contrib/comments/browser/reactionsAction.ts @@ -49,7 +49,7 @@ export class ReactionActionViewItem extends ActionViewItem { let reactionIcon = dom.append(this.label, dom.$('.reaction-icon')); reactionIcon.style.display = ''; let uri = URI.revive(action.icon); - reactionIcon.style.backgroundImage = `url('${uri}')`; + reactionIcon.style.backgroundImage = dom.asCSSUrl(uri); reactionIcon.title = action.label; } if (action.count) { diff --git a/src/vs/workbench/contrib/debug/browser/debugSession.ts b/src/vs/workbench/contrib/debug/browser/debugSession.ts index 0ca0de841ed..38527591297 100644 --- a/src/vs/workbench/contrib/debug/browser/debugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/debugSession.ts @@ -47,6 +47,7 @@ export class DebugSession implements IDebugSession { private sources = new Map(); private threads = new Map(); + private threadIds: number[] = []; private cancellationMap = new Map(); private rawListeners: IDisposable[] = []; private fetchThreadsScheduler: RunOnceScheduler | undefined; @@ -738,7 +739,12 @@ export class DebugSession implements IDebugSession { getAllThreads(): IThread[] { const result: IThread[] = []; - this.threads.forEach(t => result.push(t)); + this.threadIds.forEach((threadId) => { + const thread = this.threads.get(threadId); + if (thread) { + result.push(thread); + } + }); return result; } @@ -763,6 +769,7 @@ export class DebugSession implements IDebugSession { if (removeThreads) { this.threads.clear(); + this.threadIds = []; ExpressionContainer.allValues.clear(); } } @@ -773,9 +780,9 @@ export class DebugSession implements IDebugSession { } rawUpdate(data: IRawModelUpdate): void { - const threadIds: number[] = []; + this.threadIds = []; data.threads.forEach(thread => { - threadIds.push(thread.id); + this.threadIds.push(thread.id); if (!this.threads.has(thread.id)) { // A new thread came in, initialize it. this.threads.set(thread.id, new Thread(this, thread.name, thread.id)); @@ -789,7 +796,7 @@ export class DebugSession implements IDebugSession { }); this.threads.forEach(t => { // Remove all old threads which are no longer part of the update #75980 - if (threadIds.indexOf(t.threadId) === -1) { + if (this.threadIds.indexOf(t.threadId) === -1) { this.threads.delete(t.threadId); } }); @@ -971,6 +978,27 @@ export class DebugSession implements IDebugSession { const outputQueue = new Queue(); this.rawListeners.push(this.raw.onDidOutput(async event => { + // When a variables event is received, execute immediately to obtain the variables value #126967 + if (event.body.variablesReference) { + const source = event.body.source && event.body.line ? { + lineNumber: event.body.line, + column: event.body.column ? event.body.column : 1, + source: this.getSource(event.body.source) + } : undefined; + const container = new ExpressionContainer(this, undefined, event.body.variablesReference, generateUuid()); + const children = container.getChildren(); + // we should put appendToRepl into queue to make sure the logs to be displayed in correct order + // see https://github.com/microsoft/vscode/issues/126967#issuecomment-874954269 + outputQueue.queue(async () => { + const resolved = await children; + resolved.forEach((child) => { + // Since we can not display multiple trees in a row, we are displaying these variables one after the other (ignoring their names) + (child).name = null; + this.appendToRepl(child, severity.Info, source); + }); + }); + return; + } outputQueue.queue(async () => { if (!event.body || !this.raw) { return; @@ -1014,16 +1042,7 @@ export class DebugSession implements IDebugSession { } } - if (event.body.variablesReference) { - const container = new ExpressionContainer(this, undefined, event.body.variablesReference, generateUuid()); - await container.getChildren().then(children => { - children.forEach(child => { - // Since we can not display multiple trees in a row, we are displaying these variables one after the other (ignoring their names) - (child).name = null; - this.appendToRepl(child, outputSeverity, source); - }); - }); - } else if (typeof event.body.output === 'string') { + if (typeof event.body.output === 'string') { this.appendToRepl(event.body.output, outputSeverity, source); } }); diff --git a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts index 6f3468ae5ec..d0d1cd1a8e8 100644 --- a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts +++ b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts @@ -369,9 +369,9 @@ export class DisassemblyView extends EditorPane { } interface IBreakpointColumnTemplateData { - container: HTMLElement, - icon: HTMLElement, - disposables: IDisposable[] + currentElement: { element?: IDisassembledInstructionEntry }; + icon: HTMLElement; + disposables: IDisposable[]; } class BreakpointRenderer implements ITableRenderer { @@ -399,63 +399,67 @@ class BreakpointRenderer implements ITableRenderer this.rerenderDebugStackframe(icon, currentElement.element)), + addStandardDisposableListener(container, 'mouseover', () => { + if (currentElement.element?.allowBreakpoint) { + icon.classList.add(this._breakpointHintIcon); + } + }), + addStandardDisposableListener(container, 'mouseout', () => { + if (currentElement.element?.allowBreakpoint) { + icon.classList.remove(this._breakpointHintIcon); + } + }), + addStandardDisposableListener(container, 'click', () => { + if (currentElement.element?.allowBreakpoint) { + // click show hint while waiting for BP to resolve. + icon.classList.add(this._breakpointHintIcon); + if (currentElement.element.isBreakpointSet) { + this._debugService.removeInstructionBreakpoints(currentElement.element.instruction.address); + + } else if (currentElement.element.allowBreakpoint && !currentElement.element.isBreakpointSet) { + this._debugService.addInstructionBreakpoint(currentElement.element.instruction.address, 0); + } + } + }) + ]; + + return { currentElement, icon, disposables }; } renderElement(element: IDisassembledInstructionEntry, index: number, templateData: IBreakpointColumnTemplateData, height: number | undefined): void { - const rerenderDebugStackframe = () => { - if (element.instruction.address === this._disassemblyView.currentInstructionAddress) { - templateData.icon.classList.add(this._debugStackframe); - } else { - templateData.icon.classList.remove(this._debugStackframe); - } - - templateData.icon.classList.remove(this._breakpointHintIcon); - - if (element.isBreakpointSet) { - templateData.icon.classList.add(this._breakpointIcon); - } else { - templateData.icon.classList.remove(this._breakpointIcon); - } - }; - - rerenderDebugStackframe(); - templateData.disposables.push(this._disassemblyView.onDidChangeStackFrame(rerenderDebugStackframe)); - - // TODO: see getBreakpointMessageAndIcon in vs\workbench\contrib\debug\browser\breakpointEditorContribution.ts - // for more types of breakpoint icons - if (element.allowBreakpoint) { - templateData.disposables.push(addStandardDisposableListener(templateData.container, 'mouseover', () => { - templateData.icon.classList.add(this._breakpointHintIcon); - })); - - templateData.disposables.push(addStandardDisposableListener(templateData.container, 'mouseout', () => { - templateData.icon.classList.remove(this._breakpointHintIcon); - })); - - templateData.disposables.push(addStandardDisposableListener(templateData.container, 'click', () => { - // click show hint while waiting for BP to resolve. - templateData.icon.classList.add(this._breakpointHintIcon); - if (element.isBreakpointSet) { - this._debugService.removeInstructionBreakpoints(element.instruction.address); - - } else if (element.allowBreakpoint && !element.isBreakpointSet) { - this._debugService.addInstructionBreakpoint(element.instruction.address, 0); - } - })); - } + templateData.currentElement.element = element; + this.rerenderDebugStackframe(templateData.icon, element); } - disposeElement(element: IDisassembledInstructionEntry, index: number, templateData: IBreakpointColumnTemplateData, height: number | undefined): void { + disposeTemplate(templateData: IBreakpointColumnTemplateData): void { dispose(templateData.disposables); templateData.disposables = []; } - disposeTemplate(templateData: IBreakpointColumnTemplateData): void { } + private rerenderDebugStackframe(icon: HTMLElement, element?: IDisassembledInstructionEntry) { + if (element?.instruction.address === this._disassemblyView.currentInstructionAddress) { + icon.classList.add(this._debugStackframe); + } else { + icon.classList.remove(this._debugStackframe); + } + + icon.classList.remove(this._breakpointHintIcon); + + if (element?.isBreakpointSet) { + icon.classList.add(this._breakpointIcon); + } else { + icon.classList.remove(this._breakpointIcon); + } + } } interface IInstructionColumnTemplateData { + currentElement: { element?: IDisassembledInstructionEntry }; // TODO: hover widget? instruction: HTMLElement; disposables: IDisposable[]; @@ -491,10 +495,19 @@ class InstructionRenderer extends Disposable implements ITableRenderer this.rerenderBackground(instruction, currentElement.element)) + ]; + + return { currentElement, instruction, disposables }; } renderElement(element: IDisassembledInstructionEntry, index: number, templateData: IInstructionColumnTemplateData, height: number | undefined): void { + templateData.currentElement.element = element; + const instruction = element.instruction; const sb = createStringBuilder(10000); @@ -523,24 +536,21 @@ class InstructionRenderer extends Disposable implements ITableRenderer { - if (element.instruction.address === this._disassemblyView.currentInstructionAddress) { - templateData.instruction.style.background = this._topStackFrameColor?.toString() || 'transparent'; - } else { - templateData.instruction.style.background = 'transparent'; - } - }; - - rerenderBackground(); - templateData.disposables.push(this._disassemblyView.onDidChangeStackFrame(rerenderBackground)); + this.rerenderBackground(templateData.instruction, element); } - disposeElement(element: IDisassembledInstructionEntry, index: number, templateData: IInstructionColumnTemplateData, height: number | undefined): void { + disposeTemplate(templateData: IInstructionColumnTemplateData): void { dispose(templateData.disposables); templateData.disposables = []; } - disposeTemplate(templateData: IInstructionColumnTemplateData): void { } + private rerenderBackground(instruction: HTMLElement, element?: IDisassembledInstructionEntry) { + if (element?.instruction.address === this._disassemblyView.currentInstructionAddress) { + instruction.style.background = this._topStackFrameColor?.toString() || 'transparent'; + } else { + instruction.style.background = 'transparent'; + } + } private applyFontInfo(element: HTMLElement) { const fontInfo = this._disassemblyView.fontInfo; @@ -562,16 +572,10 @@ class AccessibilityProvider implements IListAccessibilityProvider match.index >= oldCurrMatchCellIndex); + const matchAfterSelection = findFirstInSorted(findMatches, match => match.index >= oldCurrMatchCellIndex) % findMatches.length; if (findMatches[matchAfterSelection].index > oldCurrMatchCellIndex) { // there is no search result in curr cell anymore this._updateCurrentMatch(findMatches, this._matchesCountBeforeIndex(findMatches, matchAfterSelection)); @@ -215,6 +220,12 @@ export class FindModel extends Disposable { this.constructFindMatchesStarts(); this._currentMatch = -1; this.clearCurrentFindMatchDecoration(); + + this._state.changeMatchInfo( + this._currentMatch, + this._findMatches.reduce((p, c) => p + c.matches.length, 0), + undefined + ); return; } diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/find/test/find.test.ts b/src/vs/workbench/contrib/notebook/browser/contrib/find/test/find.test.ts index 1ab397bcdc9..0853cc1f462 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/find/test/find.test.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/find/test/find.test.ts @@ -192,4 +192,34 @@ suite('Notebook Find', () => { assert.strictEqual(model.currentMatch, 1); }); }); + + test('Reset when match not found, #127198', async function () { + await withTestNotebook( + [ + ['# header 1', 'markdown', CellKind.Markup, [], {}], + ['paragraph 1', 'markdown', CellKind.Markup, [], {}], + ['paragraph 2', 'markdown', CellKind.Markup, [], {}], + ], + async (editor, viewModel, accessor) => { + accessor.stub(IConfigurationService, configurationService); + const state = new FindReplaceState(); + const model = new FindModel(editor, state, accessor.get(IConfigurationService)); + state.change({ isRevealed: true }, true); + state.change({ searchString: '1' }, true); + assert.strictEqual(model.findMatches.length, 2); + assert.strictEqual(model.currentMatch, -1); + model.find(false); + assert.strictEqual(model.currentMatch, 0); + model.find(false); + assert.strictEqual(model.currentMatch, 1); + model.find(false); + assert.strictEqual(model.currentMatch, 0); + + assert.strictEqual(editor.textModel.length, 3); + + state.change({ searchString: '3' }, true); + assert.strictEqual(model.currentMatch, -1); + assert.strictEqual(model.findMatches.length, 0); + }); + }); }); diff --git a/src/vs/workbench/contrib/notebook/browser/media/notebook.css b/src/vs/workbench/contrib/notebook/browser/media/notebook.css index 53321b66dfd..7c55495c5ec 100644 --- a/src/vs/workbench/contrib/notebook/browser/media/notebook.css +++ b/src/vs/workbench/contrib/notebook/browser/media/notebook.css @@ -730,6 +730,8 @@ opacity: 1; } */ +.monaco-workbench .notebookOverlay .cell-list-top-cell-toolbar-container:focus-within, +.monaco-workbench .notebookOverlay .cell-list-top-cell-toolbar-container:hover, .monaco-workbench .notebookOverlay.notebook-editor-editable > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container:hover, .monaco-workbench .notebookOverlay.notebook-editor-editable > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container:focus-within { opacity: 1; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index bc947dde481..0f03a2cdfd6 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -1556,6 +1556,11 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor }; } + if (this._shadowElementViewInfo && this._shadowElementViewInfo.width <= 0 && this._shadowElementViewInfo.height <= 0) { + this.onWillHide(); + return; + } + const topInserToolbarHeight = this._notebookOptions.computeTopInserToolbarHeight(this.viewModel?.viewType); this._dimension = new DOM.Dimension(dimension.width, dimension.height); diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts index 582446c518f..25a651df040 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts @@ -861,6 +861,15 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel } private _spliceNotebookCellOutputs2(cell: NotebookCellTextModel, outputs: ICellOutput[], computeUndoRedo: boolean): void { + if (outputs.length === 0 && cell.outputs.length === 0) { + return; + } + + if (outputs.length <= 1) { + this._spliceNotebookCellOutputs(cell, { start: 0, deleteCount: cell.outputs.length, newOutputs: outputs }, false, computeUndoRedo); + return; + } + const diff = new LcsDiff(new OutputSequence(cell.outputs), new OutputSequence(outputs)); const diffResult = diff.ComputeDiff(false); const splices: NotebookCellOutputsSplice[] = diffResult.changes.map(change => ({ start: change.originalStart, deleteCount: change.originalLength, newOutputs: outputs.slice(change.modifiedStart, change.modifiedStart + change.modifiedLength) })); diff --git a/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts b/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts index e59bbccf82d..5d8fa7582d8 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts @@ -1201,17 +1201,17 @@ export class ObjectSettingDropdownWidget extends AbstractListSettingWidget update( - originalKeyOrValue.type === 'boolean' - ? { ...originalKeyOrValue, data: selected === 'true' ? true : false } - : { ...originalKeyOrValue, data: selected }, + changedKeyOrValue.type === 'boolean' + ? { ...changedKeyOrValue, data: selected === 'true' ? true : false } + : { ...changedKeyOrValue, data: selected }, ) ) ); @@ -1227,10 +1227,13 @@ export class ObjectSettingDropdownWidget extends AbstractListSettingWidget keyOrValue.data === option.value); if (selected === -1 && keyOrValue.options.length) { update( - originalKeyOrValue.type === 'boolean' - ? { ...originalKeyOrValue, data: true } - : { ...originalKeyOrValue, data: keyOrValue.options[0].value } + changedKeyOrValue.type === 'boolean' + ? { ...changedKeyOrValue, data: true } + : { ...changedKeyOrValue, data: keyOrValue.options[0].value } ); + } else if (changedKeyOrValue.type === 'boolean') { + // https://github.com/microsoft/vscode/issues/129581 + update({ ...changedKeyOrValue, data: keyOrValue.data === 'true' }); } return { widget: selectBox, element: wrapper }; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalGroupService.ts b/src/vs/workbench/contrib/terminal/browser/terminalGroupService.ts index 0a127312646..4c5ad332e8a 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalGroupService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalGroupService.ts @@ -299,10 +299,8 @@ export class TerminalGroupService extends Disposable implements ITerminalGroupSe const activeInstanceIndex = instanceLocation.instanceIndex; - if (this.activeGroupIndex !== instanceLocation.groupIndex) { - this.activeGroupIndex = instanceLocation.groupIndex; - this._onDidChangeActiveGroup.fire(this.activeGroup); - } + this.activeGroupIndex = instanceLocation.groupIndex; + this._onDidChangeActiveGroup.fire(this.activeGroup); instanceLocation.group.setActiveInstanceByIndex(activeInstanceIndex, true); this.groups.forEach((g, i) => g.setVisible(i === instanceLocation.groupIndex)); diff --git a/src/vs/workbench/contrib/testing/browser/explorerProjections/index.ts b/src/vs/workbench/contrib/testing/browser/explorerProjections/index.ts index de6f154b00c..aa8eac49e8c 100644 --- a/src/vs/workbench/contrib/testing/browser/explorerProjections/index.ts +++ b/src/vs/workbench/contrib/testing/browser/explorerProjections/index.ts @@ -162,7 +162,7 @@ export class TestItemTreeElement implements IActionableTestTreeElement { tests: [this.test], }; - for (let p = this.parent; p; p = p.parent) { + for (let p = this.parent; p && p.depth > 0; p = p.parent) { context.tests.unshift(p.test); } diff --git a/src/vs/workbench/contrib/testing/browser/testingDecorations.ts b/src/vs/workbench/contrib/testing/browser/testingDecorations.ts index 0ca88cf4f62..b60751d308e 100644 --- a/src/vs/workbench/contrib/testing/browser/testingDecorations.ts +++ b/src/vs/workbench/contrib/testing/browser/testingDecorations.ts @@ -39,7 +39,7 @@ import { ITestProfileService } from 'vs/workbench/contrib/testing/common/testCon import { isFailedState, maxPriority } from 'vs/workbench/contrib/testing/common/testingStates'; import { buildTestUri, TestUriType } from 'vs/workbench/contrib/testing/common/testingUri'; import { ITestResultService } from 'vs/workbench/contrib/testing/common/testResultService'; -import { ITestService, testsInFile } from 'vs/workbench/contrib/testing/common/testService'; +import { getContextForTestItem, ITestService, testsInFile } from 'vs/workbench/contrib/testing/common/testService'; function isOriginalInDiffEditor(codeEditorService: ICodeEditorService, codeEditor: ICodeEditor): boolean { const diffEditors = codeEditorService.listDiffEditors(); @@ -464,7 +464,8 @@ abstract class RunTestDecoration extends Disposable { try { const target: IAction[] = []; - const actionsDisposable = createAndFillInContextMenuActions(menu, { shouldForwardArgs: true }, target); + const arg = getContextForTestItem(this.testService.collection, test.item.extId); + const actionsDisposable = createAndFillInContextMenuActions(menu, { shouldForwardArgs: true, arg }, target); return { object: target, dispose: () => actionsDisposable.dispose }; } finally { menu.dispose(); diff --git a/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts b/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts index 9e8d5c80023..52000033939 100644 --- a/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts +++ b/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts @@ -566,7 +566,7 @@ export class TestingExplorerViewModel extends Disposable { let expandToLevel = 0; const idPath = [...TestId.fromString(id).idsFromRoot()]; for (let i = idPath.length - 1; i >= expandToLevel; i--) { - const element = projection.getElementByTestId(idPath[i]); + const element = projection.getElementByTestId(idPath[i].toString()); // Skip all elements that aren't in the tree. if (!element || !this.tree.hasElement(element)) { continue; @@ -737,8 +737,8 @@ const enum FilterResult { Include, } -const hasNodeInOrParentOfUri = (collection: IMainThreadTestCollection, testUri: URI | string, fromNode?: string) => { - testUri = testUri.toString(); +const hasNodeInOrParentOfUri = (collection: IMainThreadTestCollection, testUri: URI, fromNode?: string) => { + const fsPath = testUri.fsPath; const queue: Iterable[] = [fromNode ? [fromNode] : collection.rootIds]; while (queue.length) { @@ -753,7 +753,7 @@ const hasNodeInOrParentOfUri = (collection: IMainThreadTestCollection, testUri: continue; } - if (extpath.isEqualOrParent(testUri, node.item.uri.toString())) { + if (extpath.isEqualOrParent(fsPath, node.item.uri.fsPath)) { return true; } } @@ -765,7 +765,7 @@ const hasNodeInOrParentOfUri = (collection: IMainThreadTestCollection, testUri: class TestsFilter implements ITreeFilter { private lastText?: string; private filters: [include: boolean, value: string][] | undefined; - private documentUri: string | undefined; + private documentUri: URI | undefined; constructor( private readonly collection: IMainThreadTestCollection, @@ -826,7 +826,7 @@ class TestsFilter implements ITreeFilter { } public filterToDocumentUri(uri: URI | undefined) { - this.documentUri = uri?.toString(); + this.documentUri = uri; } private testState(element: TestItemTreeElement): FilterResult { diff --git a/src/vs/workbench/contrib/testing/common/testId.ts b/src/vs/workbench/contrib/testing/common/testId.ts index c3194fb16a3..4e43e965779 100644 --- a/src/vs/workbench/contrib/testing/common/testId.ts +++ b/src/vs/workbench/contrib/testing/common/testId.ts @@ -130,13 +130,8 @@ export class TestId { * including the current item. */ public *idsFromRoot() { - let built = this.path[0]; - yield built; - - for (let i = 1; i < this.viewEnd; i++) { - built += TestIdPathParts.Delimiter; - built += this.path[i]; - yield built; + for (let i = 1; i <= this.viewEnd; i++) { + yield new TestId(this.path, i); } } diff --git a/src/vs/workbench/contrib/testing/common/testService.ts b/src/vs/workbench/contrib/testing/common/testService.ts index 0d4f944def8..e339ac05aa4 100644 --- a/src/vs/workbench/contrib/testing/common/testService.ts +++ b/src/vs/workbench/contrib/testing/common/testService.ts @@ -8,10 +8,11 @@ import { Event } from 'vs/base/common/event'; import * as extpath from 'vs/base/common/extpath'; import { Iterable } from 'vs/base/common/iterator'; import { IDisposable } from 'vs/base/common/lifecycle'; +import { MarshalledId } from 'vs/base/common/marshalling'; import { URI } from 'vs/base/common/uri'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IObservableValue } from 'vs/workbench/contrib/testing/common/observableValue'; -import { AbstractIncrementalTestCollection, IncrementalTestCollectionItem, InternalTestItem, ITestIdWithSrc, ResolvedTestRunRequest, RunTestForControllerRequest, TestItemExpandState, TestRunProfileBitset, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection'; +import { AbstractIncrementalTestCollection, IncrementalTestCollectionItem, InternalTestItem, ITestIdWithSrc, ITestItemContext, ResolvedTestRunRequest, RunTestForControllerRequest, TestItemExpandState, TestRunProfileBitset, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection'; import { TestExclusions } from 'vs/workbench/contrib/testing/common/testExclusions'; import { TestId } from 'vs/workbench/contrib/testing/common/testId'; import { ITestResult } from 'vs/workbench/contrib/testing/common/testResult'; @@ -84,6 +85,28 @@ export const getCollectionItemParents = function* (collection: IMainThreadTestCo export const testCollectionIsEmpty = (collection: IMainThreadTestCollection) => !Iterable.some(collection.rootItems, r => r.children.size > 0); +export const getContextForTestItem = (collection: IMainThreadTestCollection, id: string | TestId) => { + if (typeof id === 'string') { + id = TestId.fromString(id); + } + + if (id.isRoot) { + return { controller: id.toString() }; + } + + const context: ITestItemContext = { $mid: MarshalledId.TestItemContext, tests: [] }; + for (const i of id.idsFromRoot()) { + if (!i.isRoot) { + const test = collection.getNodeById(i.toString()); + if (test) { + context.tests.push(test); + } + } + } + + return context; +}; + /** * Ensures the test with the given ID exists in the collection, if possible. * If cancellation is requested, or the test cannot be found, it will return @@ -94,7 +117,7 @@ export const expandAndGetTestById = async (collection: IMainThreadTestCollection let expandToLevel = 0; for (let i = idPath.length - 1; !ct.isCancellationRequested && i >= expandToLevel;) { - const id = idPath[i]; + const id = idPath[i].toString(); const existing = collection.getNodeById(id); if (!existing) { i--; @@ -106,7 +129,7 @@ export const expandAndGetTestById = async (collection: IMainThreadTestCollection } // expand children only if it looks like it's necessary - if (!existing.children.has(idPath[i + 1])) { + if (!existing.children.has(idPath[i + 1].toString())) { await collection.expand(id, 0); } @@ -138,18 +161,18 @@ export const getAllTestsInHierarchy = async (collection: IMainThreadTestCollecti * in strictly descending order. */ export const testsInFile = async function* (collection: IMainThreadTestCollection, uri: URI): AsyncIterable { - const demandUriStr = uri.toString(); + const demandFsPath = uri.fsPath; for (const test of collection.all) { if (!test.item.uri) { continue; } - const itemUriStr = test.item.uri.toString(); - if (itemUriStr === demandUriStr) { + const itemFsPath = test.item.uri.fsPath; + if (itemFsPath === demandFsPath) { yield test; } - if (extpath.isEqualOrParent(demandUriStr, itemUriStr) && test.expand === TestItemExpandState.Expandable) { + if (extpath.isEqualOrParent(demandFsPath, itemFsPath) && test.expand === TestItemExpandState.Expandable) { await collection.expand(test.item.extId, 1); } } diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index 898f3ad9621..5079140047c 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -57,6 +57,7 @@ import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneCont import { EditorResolution } from 'vs/platform/editor/common/editor'; import { CATEGORIES } from 'vs/workbench/common/actions'; import { IUserDataInitializationService } from 'vs/workbench/services/userData/browser/userDataInit'; +import { MarkdownString } from 'vs/base/common/htmlContent'; const CONTEXT_CONFLICTS_SOURCES = new RawContextKey('conflictsSources', ''); @@ -488,13 +489,20 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo this.updateSyncAfterInitializationContext(false); const result = await this.dialogService.show( Severity.Info, - localize('turnon sync after initialization message', "Your settings, keybindings, extensions, snippets and UI State were initialized but are not getting synced. Do you want to turn on Settings Sync?"), + localize('settings sync is off', "Settings Sync is Off"), [ localize('turn on settings sync', "Turn On Settings Sync"), localize('cancel', "Cancel"), ], { - cancelId: 1 + cancelId: 1, + custom: { + markdownDetails: [{ + markdown: new MarkdownString(`${localize('turnon sync after initialization message', "Your settings, keybindings, extensions, snippets and UI State were initialized but are not getting synced. Do you want to turn on Settings Sync?")}`, { isTrusted: true }) + }, { + markdown: new MarkdownString(`${localize({ key: 'change later', comment: ['Context here is that user can change (turn on/off) settings sync later.'] }, "You can always change this later.")} [${localize('learn more', "Learn More")}](https://aka.ms/vscode-settings-sync-help).`, { isTrusted: true }) + }] + } } ); if (result.choice === 0) { diff --git a/src/vs/workbench/contrib/webview/browser/webviewElement.ts b/src/vs/workbench/contrib/webview/browser/webviewElement.ts index 7360c6577a2..56cc3957919 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewElement.ts @@ -246,13 +246,7 @@ export class IFrameWebview extends Disposable implements Webview { const uri = URI.from({ scheme: entry.scheme, authority: entry.authority, - // This gets re-encoded - path: decodeURIComponent( - entry.path - // Make sure we don't transform encoded slash or percent into a real slash/percent - .replace(/%2f/ig, '%252f') - .replace(/%25/ig, '%2525') - ), + path: decodeURIComponent(entry.path), // This gets re-encoded query: entry.query ? decodeURIComponent(entry.query) : entry.query, }); this.loadResource(entry.id, uri, entry.ifNoneMatch); diff --git a/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStartedExtensionPoint.ts b/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStartedExtensionPoint.ts index aae5231afa9..c72b0ec1af3 100644 --- a/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStartedExtensionPoint.ts +++ b/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStartedExtensionPoint.ts @@ -159,14 +159,14 @@ export const walkthroughsExtensionPoint = ExtensionsRegistry.registerExtensionPo body: 'onContext:${2:key}' }, { - label: 'extensionInstalled', + label: 'onExtensionInstalled', description: localize('walkthroughs.steps.completionEvents.extensionInstalled', 'Check off step when an extension with the given id is installed. If the extension is already installed, the step will start off checked.'), - body: 'extensionInstalled:${3:extensionId}' + body: 'onExtensionInstalled:${3:extensionId}' }, { - label: 'stepSelected', + label: 'onStepSelected', description: localize('walkthroughs.steps.completionEvents.stepSelected', 'Check off step as soon as it is selected.'), - body: 'stepSelected' + body: 'onStepSelected' }, ] } diff --git a/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStartedService.ts b/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStartedService.ts index cb262745c78..4b44b9b44cb 100644 --- a/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStartedService.ts +++ b/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStartedService.ts @@ -609,14 +609,14 @@ export class GettingStartedService extends Disposable implements IGettingStarted } break; } - case 'stepSelected': - event = eventType + ':' + step.id; + case 'onStepSelected': case 'stepSelected': + event = 'stepSelected:' + step.id; break; case 'onCommand': event = eventType + ':' + argument.replace(/^toSide:/, ''); break; - case 'extensionInstalled': - event = eventType + ':' + argument.toLowerCase(); + case 'onExtensionInstalled': case 'extensionInstalled': + event = 'extensionInstalled:' + argument.toLowerCase(); break; default: console.error(`Unknown completionEvent ${event} when registering step ${step.id}`); diff --git a/src/vs/workbench/services/extensionResourceLoader/browser/extensionResourceLoaderService.ts b/src/vs/workbench/services/extensionResourceLoader/browser/extensionResourceLoaderService.ts index 5ca94588cff..0178c4a175f 100644 --- a/src/vs/workbench/services/extensionResourceLoader/browser/extensionResourceLoaderService.ts +++ b/src/vs/workbench/services/extensionResourceLoader/browser/extensionResourceLoaderService.ts @@ -13,6 +13,7 @@ import { IStorageService } from 'vs/platform/storage/common/storage'; import { getServiceMachineId } from 'vs/platform/serviceMachineId/common/serviceMachineId'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { isWeb } from 'vs/base/common/platform'; +import { ILogService } from 'vs/platform/log/common/log'; class ExtensionResourceLoaderService implements IExtensionResourceLoaderService { @@ -25,6 +26,7 @@ class ExtensionResourceLoaderService implements IExtensionResourceLoaderService @IProductService private readonly _productService: IProductService, @IStorageService private readonly _storageService: IStorageService, @IEnvironmentService private readonly _environmentService: IEnvironmentService, + @ILogService private readonly _logService: ILogService, ) { if (_productService.extensionsGallery) { this._extensionGalleryResourceAuthority = this._getExtensionResourceAuthority(URI.parse(_productService.extensionsGallery.resourceUrlTemplate)); @@ -55,6 +57,7 @@ class ExtensionResourceLoaderService implements IExtensionResourceLoaderService const response = await fetch(uri.toString(true), requestInit); if (response.status !== 200) { + this._logService.info(`Request to '${uri.toString(true)}' failed with status code ${response.status}`); throw new Error(response.statusText); } return response.text();