From 041afd2afd2fce3a09ea9fb27b2c0cfae1b1ed9d Mon Sep 17 00:00:00 2001 From: Ole Date: Fri, 3 Feb 2023 20:47:48 +0100 Subject: [PATCH 01/43] Layout when switching from welcome to terminal. Before this change, VSCode assumed in multiple places that we only ever either have terminals open (on desktop) or a "welcome" screen (on web, explaining that terminals are not supported). Switching between the two causes a broken layout. This is currently not visible in VSCode because VSCode opens a new terminal as soon as the terminal panel is opened, and closes the terminal panel when the last terminal is closed. With this change, that assumption is removed and the question whether a welcome screen is available is centralized in one place. This makes it easier for downstream forks to have a welcome screen when first opening the panel, and when the last terminal is closed. In VSCode itself, this still works as before. This change also removes relying on the side-effect of the `shouldShowWelcome` getter to write a member variable, and removes superfluously setting that member variable when is it already set. --- .../contrib/terminal/browser/terminalView.ts | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalView.ts b/src/vs/workbench/contrib/terminal/browser/terminalView.ts index 51831b92435..4ee887dd44d 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalView.ts @@ -51,7 +51,6 @@ export class TerminalViewPane extends ViewPane { private _parentDomElement: HTMLElement | undefined; private _terminalTabbedView?: TerminalTabbedView; get terminalTabbedView(): TerminalTabbedView | undefined { return this._terminalTabbedView; } - private _isWelcomeShowing: boolean = false; private _newDropdown: DropdownWithPrimaryActionViewItem | undefined; private readonly _dropdownMenu: IMenu; private readonly _singleTabMenu: IMenu; @@ -83,13 +82,18 @@ export class TerminalViewPane extends ViewPane { })); this._register(this._terminalService.onDidChangeInstances(() => { - if (!this._isWelcomeShowing) { - return; + // If the first terminal is opened, hide the welcome view + // and if the last one is closed, show it again + if (this.hasWelcomeScreen() && this._terminalService.instances.length <= 1) { + this._onDidChangeViewWelcomeState.fire(); } - this._isWelcomeShowing = true; - this._onDidChangeViewWelcomeState.fire(); - if (!this._terminalTabbedView && this._parentDomElement) { + if (!this._parentDomElement) { return; } + // If we do not have the tab view yet, create it now. + if (!this._terminalTabbedView) { this._createTabsView(); + } + // If we just opened our first terminal, layout + if (this._terminalService.instances.length === 1) { this.layoutBody(this._parentDomElement.offsetHeight, this._parentDomElement.offsetWidth); } })); @@ -164,7 +168,7 @@ export class TerminalViewPane extends ViewPane { this._register(this.onDidChangeBodyVisibility(async visible => { this._viewShowing.set(visible); if (visible) { - if (!this._terminalService.isProcessSupportRegistered) { + if (this.hasWelcomeScreen()) { this._onDidChangeViewWelcomeState.fire(); } this._initializeTerminal(); @@ -281,9 +285,12 @@ export class TerminalViewPane extends ViewPane { this._terminalGroupService.showPanel(true); } + private hasWelcomeScreen(): boolean { + return !this._terminalService.isProcessSupportRegistered; + } + override shouldShowWelcome(): boolean { - this._isWelcomeShowing = !this._terminalService.isProcessSupportRegistered && this._terminalService.instances.length === 0; - return this._isWelcomeShowing; + return this.hasWelcomeScreen() && this._terminalService.instances.length === 0; } } From 04d75b3f196bd0a357b75ce292372771c9b9fbfb Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 30 Oct 2023 11:57:02 -0700 Subject: [PATCH 02/43] Add last instances of test leak check Part of #190503 --- .../terminal/test/common/requestStore.test.ts | 22 ++++++------------- .../terminalCapabilityStore.test.ts | 2 ++ 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/src/vs/platform/terminal/test/common/requestStore.test.ts b/src/vs/platform/terminal/test/common/requestStore.test.ts index 3d0301d55b0..a2026e91c65 100644 --- a/src/vs/platform/terminal/test/common/requestStore.test.ts +++ b/src/vs/platform/terminal/test/common/requestStore.test.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { fail, strictEqual } from 'assert'; -import { DisposableStore } from 'vs/base/common/lifecycle'; import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { ConsoleLogger, ILogService } from 'vs/platform/log/common/log'; @@ -12,37 +11,30 @@ import { LogService } from 'vs/platform/log/common/logService'; import { RequestStore } from 'vs/platform/terminal/common/requestStore'; suite('RequestStore', () => { - let disposables: DisposableStore; let instantiationService: TestInstantiationService; setup(() => { - disposables = new DisposableStore(); instantiationService = new TestInstantiationService(); instantiationService.stub(ILogService, new LogService(new ConsoleLogger())); }); - teardown(() => { - instantiationService.dispose(); - disposables.dispose(); - }); - - ensureNoDisposablesAreLeakedInTestSuite(); + const store = ensureNoDisposablesAreLeakedInTestSuite(); test('should resolve requests', async () => { - const store: RequestStore<{ data: string }, { arg: string }> = disposables.add(instantiationService.createInstance(RequestStore<{ data: string }, { arg: string }>, undefined)); + const requestStore: RequestStore<{ data: string }, { arg: string }> = store.add(instantiationService.createInstance(RequestStore<{ data: string }, { arg: string }>, undefined)); let eventArgs: { requestId: number; arg: string } | undefined; - disposables.add(store.onCreateRequest(e => eventArgs = e)); - const request = store.createRequest({ arg: 'foo' }); + store.add(requestStore.onCreateRequest(e => eventArgs = e)); + const request = requestStore.createRequest({ arg: 'foo' }); strictEqual(typeof eventArgs?.requestId, 'number'); strictEqual(eventArgs?.arg, 'foo'); - store.acceptReply(eventArgs!.requestId, { data: 'bar' }); + requestStore.acceptReply(eventArgs!.requestId, { data: 'bar' }); const result = await request; strictEqual(result.data, 'bar'); }); test('should reject the promise when the request times out', async () => { - const store: RequestStore<{ data: string }, { arg: string }> = disposables.add(instantiationService.createInstance(RequestStore<{ data: string }, { arg: string }>, 1)); - const request = store.createRequest({ arg: 'foo' }); + const requestStore: RequestStore<{ data: string }, { arg: string }> = store.add(instantiationService.createInstance(RequestStore<{ data: string }, { arg: string }>, 1)); + const request = requestStore.createRequest({ arg: 'foo' }); let threw = false; try { await request; diff --git a/src/vs/workbench/contrib/terminal/test/browser/capabilities/terminalCapabilityStore.test.ts b/src/vs/workbench/contrib/terminal/test/browser/capabilities/terminalCapabilityStore.test.ts index 07ec5631f32..6b1fb0e5467 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/capabilities/terminalCapabilityStore.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/capabilities/terminalCapabilityStore.test.ts @@ -22,6 +22,8 @@ suite('TerminalCapabilityStore', () => { removeEvents = []; }); + ensureNoDisposablesAreLeakedInTestSuite(); + teardown(() => store.dispose()); test('should fire events when capabilities are added', () => { From d0cdbac8205f5742889f6bb321b5d29c13fe1616 Mon Sep 17 00:00:00 2001 From: Andrea Mah <31675041+andreamah@users.noreply.github.com> Date: Mon, 30 Oct 2023 12:20:30 -0700 Subject: [PATCH 03/43] Quick search should select the term when it's auto filled (#197020) Fixes #195180 --- .../search/browser/quickTextSearch/textSearchQuickAccess.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts b/src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts index c14c5fedbea..471bb4dedc5 100644 --- a/src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts +++ b/src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts @@ -83,6 +83,9 @@ export class TextSearchQuickAccess extends PickerQuickAccessProvider, token: CancellationToken, runOptions?: IQuickAccessProviderRunOptions): IDisposable { const disposables = new DisposableStore(); + if (TEXT_SEARCH_QUICK_ACCESS_PREFIX.length < picker.value.length) { + picker.valueSelection = [TEXT_SEARCH_QUICK_ACCESS_PREFIX.length, picker.value.length]; + } disposables.add(super.provide(picker, token, runOptions)); disposables.add(picker.onDidHide(() => this.searchModel.searchResult.toggleHighlights(false))); disposables.add(picker.onDidAccept(() => this.searchModel.searchResult.toggleHighlights(false))); From a805b109d24e1d2f71ae18aa864cb3db9b8ec7c8 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 30 Oct 2023 12:52:00 -0700 Subject: [PATCH 04/43] Fix textarea sync edge cases - PowerShell is now handled with a specific regex which has an optional space at the end, if matched a space will be added to the end. This is needed because conpty may not 'render' the final space in the prompt. - Return true in _cursorOnNextLine check if there is a single command as this means it's the first command, so any match should be correct. - Increase polling to 1000ms (50x20ms) as it was being hit on my machine. Fixes #196758 --- .../commandDetectionCapability.ts | 34 +++++++++++++++---- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts b/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts index 37e32c47bb7..71231274d90 100644 --- a/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts +++ b/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts @@ -377,10 +377,10 @@ export class CommandDetectionCapability extends Disposable implements ICommandDe // Conpty could have the wrong cursor position at this point. if (!this._cursorOnNextLine() || !prompt) { this._windowsPromptPollingInProcess = true; - // Poll for 200ms until the cursor position is correct. + // Poll for 1000ms until the cursor position is correct. let i = 0; - for (; i < 20; i++) { - await timeout(10); + for (; i < 50; i++) { + await timeout(20); prompt = this._getWindowsPrompt(); if (this._store.isDisposed || !this._windowsPromptPollingInProcess || this._cursorOnNextLine() && prompt) { if (!this._windowsPromptPollingInProcess) { @@ -390,7 +390,7 @@ export class CommandDetectionCapability extends Disposable implements ICommandDe } } this._windowsPromptPollingInProcess = false; - if (i === 20) { + if (i >= 50) { this._logService.debug('CommandDetectionCapability#_handleCommandStartWindows reached max attempts, ', this._cursorOnNextLine(), this._getWindowsPrompt()); } else if (prompt) { // use the regex to set the position as it's possible input has occurred @@ -428,9 +428,11 @@ export class CommandDetectionCapability extends Disposable implements ICommandDe private _cursorOnNextLine(): boolean { const lastCommand = this.commands.at(-1); + // There is only a single command, so this check is unnecessary if (!lastCommand) { - return false; + return true; } + const cursorYAbsolute = this._terminal.buffer.active.baseY + this._terminal.buffer.active.cursorY; // If the cursor position is within the last command, we should poll. const lastCommandYAbsolute = (lastCommand.endMarker ? lastCommand.endMarker.line : lastCommand.marker?.line) ?? -1; @@ -442,8 +444,26 @@ export class CommandDetectionCapability extends Disposable implements ICommandDe if (!line) { return; } - // TODO: fine tune prompt regex to accomodate for unique configurtions. - return line.translateToString(true)?.match(/^(?(\(.+\)\s)?(?:PS.+>\s)|(?:[A-Z]:\\.*>))/)?.groups?.prompt; + // TODO: fine tune prompt regex to accomodate for unique configurations. + const lineText = line.translateToString(true); + if (!lineText) { + return; + } + + // PowerShell + const pwshMatch = lineText.match(/(?(\(.+\)\s)?(?:PS.+>\s?))/); + if (pwshMatch) { + let prompt = pwshMatch?.groups?.prompt; + if (lineText === prompt && prompt.endsWith('>')) { + // Conpty may not 'render' the space at the end of the prompt + prompt += ' '; + } + return prompt; + } + + // Command Prompt + const cmdMatch = lineText.match(/^(?(\(.+\)\s)?(?:[A-Z]:\\.*>))/); + return cmdMatch?.groups?.prompt; } handleGenericCommand(options?: IHandleCommandOptions): void { From 8ace0045b0e76cb5bd7ddded4bed6bbcd9a4979d Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 30 Oct 2023 12:56:34 -0700 Subject: [PATCH 05/43] Enable TextAreaSyncAddon when in devMode Fixes #197024 --- .../accessibility/browser/textAreaSyncAddon.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts index fb5b757d2b8..28929a64036 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts @@ -6,10 +6,11 @@ import { Disposable, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { ITerminalCapabilityStore, TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities'; -import { ITerminalLogService } from 'vs/platform/terminal/common/terminal'; +import { ITerminalLogService, TerminalSettingId } from 'vs/platform/terminal/common/terminal'; import type { Terminal, ITerminalAddon } from 'xterm'; import { debounce } from 'vs/base/common/decorators'; import { addDisposableListener } from 'vs/base/browser/dom'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; export interface ITextAreaData { content: string; @@ -24,7 +25,7 @@ export class TextAreaSyncAddon extends Disposable implements ITerminalAddon { activate(terminal: Terminal): void { this._terminal = terminal; - if (this._accessibilityService.isScreenReaderOptimized()) { + if (this._shouldBeActive()) { this._registerSyncListeners(); } } @@ -32,11 +33,12 @@ export class TextAreaSyncAddon extends Disposable implements ITerminalAddon { constructor( private readonly _capabilities: ITerminalCapabilityStore, @IAccessibilityService private readonly _accessibilityService: IAccessibilityService, + @IConfigurationService private readonly _configurationService: IConfigurationService, @ITerminalLogService private readonly _logService: ITerminalLogService ) { super(); this._register(this._accessibilityService.onDidChangeScreenReaderOptimized(() => { - if (this._accessibilityService.isScreenReaderOptimized()) { + if (this._shouldBeActive()) { this._syncTextArea(); this._registerSyncListeners(); } else { @@ -46,7 +48,7 @@ export class TextAreaSyncAddon extends Disposable implements ITerminalAddon { } private _registerSyncListeners(): void { - if (this._accessibilityService.isScreenReaderOptimized() && this._terminal?.textarea) { + if (this._shouldBeActive() && this._terminal?.textarea) { this._listeners.value = new DisposableStore(); this._listeners.value.add(this._terminal.onCursorMove(() => this._syncTextArea())); this._listeners.value.add(this._terminal.onData(() => this._syncTextArea())); @@ -54,6 +56,10 @@ export class TextAreaSyncAddon extends Disposable implements ITerminalAddon { } } + private _shouldBeActive(): boolean { + return this._accessibilityService.isScreenReaderOptimized() || this._configurationService.getValue(TerminalSettingId.DevMode); + } + @debounce(50) private _syncTextArea(): void { this._logService.debug('TextAreaSyncAddon#syncTextArea'); From 85f0a2e10a7597091d7faa8abf3b98a7296d42fc Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 30 Oct 2023 22:52:21 +0100 Subject: [PATCH 06/43] Clear Recently Opened function doesn't display pop-up prompt when no windows are open - macOS (fix #196971) (#197015) --- .../platform/menubar/electron-main/menubar.ts | 2 +- .../workspacesHistoryMainService.ts | 25 ++++++++++++++++--- .../parts/editor/editor.contribution.ts | 2 +- .../browser/parts/editor/editorActions.ts | 2 +- 4 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/vs/platform/menubar/electron-main/menubar.ts b/src/vs/platform/menubar/electron-main/menubar.ts index ed5141b1d0a..a8c58cb79e5 100644 --- a/src/vs/platform/menubar/electron-main/menubar.ts +++ b/src/vs/platform/menubar/electron-main/menubar.ts @@ -128,7 +128,7 @@ export class Menubar { this.fallbackMenuHandlers['workbench.action.openWorkspace'] = (menuItem, win, event) => this.nativeHostMainService.pickWorkspaceAndOpen(undefined, { forceNewWindow: this.isOptionClick(event), telemetryExtraData: { from: telemetryFrom } }); // Recent Menu Items - this.fallbackMenuHandlers['workbench.action.clearRecentFiles'] = () => this.workspacesHistoryMainService.clearRecentlyOpened(); + this.fallbackMenuHandlers['workbench.action.clearRecentFiles'] = () => this.workspacesHistoryMainService.clearRecentlyOpened({ confirm: true /* ask for confirmation */ }); // Help Menu Items const youTubeUrl = this.productService.youTubeUrl; diff --git a/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts b/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts index 971096dad97..25a017e19a9 100644 --- a/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts +++ b/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts @@ -24,6 +24,7 @@ import { IRecent, IRecentFile, IRecentFolder, IRecentlyOpened, IRecentWorkspace, import { IWorkspaceIdentifier, WORKSPACE_EXTENSION } from 'vs/platform/workspace/common/workspace'; import { IWorkspacesManagementMainService } from 'vs/platform/workspaces/electron-main/workspacesManagementMainService'; import { ResourceMap } from 'vs/base/common/map'; +import { IDialogMainService } from 'vs/platform/dialogs/electron-main/dialogMainService'; export const IWorkspacesHistoryMainService = createDecorator('workspacesHistoryMainService'); @@ -36,7 +37,7 @@ export interface IWorkspacesHistoryMainService { addRecentlyOpened(recents: IRecent[]): Promise; getRecentlyOpened(): Promise; removeRecentlyOpened(paths: URI[]): Promise; - clearRecentlyOpened(): Promise; + clearRecentlyOpened(options?: { confirm?: boolean }): Promise; } export class WorkspacesHistoryMainService extends Disposable implements IWorkspacesHistoryMainService { @@ -54,7 +55,8 @@ export class WorkspacesHistoryMainService extends Disposable implements IWorkspa @ILogService private readonly logService: ILogService, @IWorkspacesManagementMainService private readonly workspacesManagementMainService: IWorkspacesManagementMainService, @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService, - @IApplicationStorageMainService private readonly applicationStorageMainService: IApplicationStorageMainService + @IApplicationStorageMainService private readonly applicationStorageMainService: IApplicationStorageMainService, + @IDialogMainService private readonly dialogMainService: IDialogMainService ) { super(); @@ -157,7 +159,24 @@ export class WorkspacesHistoryMainService extends Disposable implements IWorkspa } } - async clearRecentlyOpened(): Promise { + async clearRecentlyOpened(options?: { confirm?: boolean }): Promise { + if (options?.confirm) { + const { response } = await this.dialogMainService.showMessageBox({ + type: 'warning', + buttons: [ + localize({ key: 'clearButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Clear"), + localize({ key: 'cancel', comment: ['&& denotes a mnemonic'] }, "&&Cancel") + ], + message: localize('confirmClearRecentsMessage', "Do you want to clear all recently opened files and workspaces?"), + detail: localize('confirmClearDetail', "This action is irreversible!"), + cancelId: 1 + }); + + if (response !== 0) { + return; + } + } + await this.saveRecentlyOpened({ workspaces: [], files: [] }); app.clearRecentDocuments(); diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index b6ee8991056..959089c51ee 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -601,7 +601,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarRecentMenu, { group: 'z_clear', command: { id: ClearRecentFilesAction.ID, - title: localize({ key: 'miClearRecentOpen', comment: ['&& denotes a mnemonic'] }, "&&Clear Recently Opened") + title: localize({ key: 'miClearRecentOpen', comment: ['&& denotes a mnemonic'] }, "&&Clear Recently Opened...") }, order: 1 }); diff --git a/src/vs/workbench/browser/parts/editor/editorActions.ts b/src/vs/workbench/browser/parts/editor/editorActions.ts index 1016fcc0e19..f0341acd5b5 100644 --- a/src/vs/workbench/browser/parts/editor/editorActions.ts +++ b/src/vs/workbench/browser/parts/editor/editorActions.ts @@ -1600,7 +1600,7 @@ export class ClearRecentFilesAction extends Action2 { constructor() { super({ id: ClearRecentFilesAction.ID, - title: { value: localize('clearRecentFiles', "Clear Recently Opened"), original: 'Clear Recently Opened' }, + title: { value: localize('clearRecentFiles', "Clear Recently Opened..."), original: 'Clear Recently Opened...' }, f1: true, category: Categories.File }); From 602125c14ac7ce40a421604c4bb5b52a7c51a1e5 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 30 Oct 2023 15:42:35 -0700 Subject: [PATCH 07/43] Port candidate fixes to main (#197027) * Don't show "ask a question" while chat provider is activating Fix microsoft/vscode-copilot#2506 * Fix chat agent codicon avatar color Override aggressive list codicon styles #196870 * Remove "Ask a question" text while extension is loading Better fix for microsoft/vscode-copilot#2506 --- .../chat/browser/contrib/chatInputEditorContrib.ts | 8 ++------ src/vs/workbench/contrib/chat/browser/media/chat.css | 2 +- src/vs/workbench/contrib/chat/common/chatViewModel.ts | 11 +++++++++-- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts index 7334337b118..6db8aa47a21 100644 --- a/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts @@ -89,7 +89,7 @@ class InputEditorDecorations extends Disposable { private registerViewModelListeners(): void { this.viewModelDisposables.value = this.widget.viewModel?.onDidChange(e => { - if (e?.kind === 'changePlaceholder') { + if (e?.kind === 'changePlaceholder' || e?.kind === 'initialize') { this.updateInputEditorDecorations(); } }); @@ -127,7 +127,6 @@ class InputEditorDecorations extends Disposable { private async updateInputEditorDecorations() { const inputValue = this.widget.inputEditor.getValue(); - const slashCommands = await this.widget.getSlashCommands(); // TODO this async call can lead to a flicker of the placeholder text when switching editor tabs const viewModel = this.widget.viewModel; if (!viewModel) { @@ -136,10 +135,7 @@ class InputEditorDecorations extends Disposable { if (!inputValue) { const viewModelPlaceholder = this.widget.viewModel?.inputPlaceholder; - const defaultPlaceholder = slashCommands?.length ? - localize('interactive.input.placeholderWithCommands', "Ask a question or type '@' or '/'") : - localize('interactive.input.placeholderNoCommands', "Ask a question"); - const placeholder = viewModelPlaceholder ?? defaultPlaceholder; + const placeholder = viewModelPlaceholder ?? ''; const decoration: IDecorationOptions[] = [ { range: { diff --git a/src/vs/workbench/contrib/chat/browser/media/chat.css b/src/vs/workbench/contrib/chat/browser/media/chat.css index 2d524d8172d..057ad815146 100644 --- a/src/vs/workbench/contrib/chat/browser/media/chat.css +++ b/src/vs/workbench/contrib/chat/browser/media/chat.css @@ -109,7 +109,7 @@ } .interactive-item-container .header .avatar .codicon { - color: var(--vscode-chat-avatarForeground); + color: var(--vscode-chat-avatarForeground) !important; font-size: 14px; } diff --git a/src/vs/workbench/contrib/chat/common/chatViewModel.ts b/src/vs/workbench/contrib/chat/common/chatViewModel.ts index dcaf0343508..d6337756df3 100644 --- a/src/vs/workbench/contrib/chat/common/chatViewModel.ts +++ b/src/vs/workbench/contrib/chat/common/chatViewModel.ts @@ -26,7 +26,7 @@ export function isWelcomeVM(item: unknown): item is IChatWelcomeMessageViewModel return !!item && typeof item === 'object' && 'content' in item; } -export type IChatViewModelChangeEvent = IChatAddRequestEvent | IChangePlaceholderEvent | null; +export type IChatViewModelChangeEvent = IChatAddRequestEvent | IChangePlaceholderEvent | IChatSessionInitEvent | null; export interface IChatAddRequestEvent { kind: 'addRequest'; @@ -36,6 +36,10 @@ export interface IChangePlaceholderEvent { kind: 'changePlaceholder'; } +export interface IChatSessionInitEvent { + kind: 'initialize'; +} + export interface IChatViewModel { readonly initState: ChatModelInitState; readonly providerId: string; @@ -184,7 +188,10 @@ export class ChatViewModel extends Disposable implements IChatViewModel { } } - this._onDidChange.fire(e.kind === 'addRequest' ? { kind: 'addRequest' } : null); + const modelEventToVmEvent: IChatViewModelChangeEvent = e.kind === 'addRequest' ? { kind: 'addRequest' } : + e.kind === 'initialize' ? { kind: 'initialize' } : + null; + this._onDidChange.fire(modelEventToVmEvent); })); } From c1ec0edf787367f34fb36d1b6cbc9319be141779 Mon Sep 17 00:00:00 2001 From: Andrea Mah <31675041+andreamah@users.noreply.github.com> Date: Mon, 30 Oct 2023 15:55:57 -0700 Subject: [PATCH 08/43] Removing the space after `%` for quick text search (#197028) Quick Search - consider removing the space after `%` for text search Fixes #191503 --- .../search/browser/quickTextSearch/textSearchQuickAccess.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts b/src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts index 471bb4dedc5..4ff988f0ad4 100644 --- a/src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts +++ b/src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts @@ -29,7 +29,7 @@ import { ACTIVE_GROUP, IEditorService, SIDE_GROUP } from 'vs/workbench/services/ import { ITextQueryBuilderOptions, QueryBuilder } from 'vs/workbench/services/search/common/queryBuilder'; import { IPatternInfo, ITextQuery, VIEW_ID } from 'vs/workbench/services/search/common/search'; -export const TEXT_SEARCH_QUICK_ACCESS_PREFIX = '% '; +export const TEXT_SEARCH_QUICK_ACCESS_PREFIX = '%'; const DEFAULT_TEXT_QUERY_BUILDER_OPTIONS: ITextQueryBuilderOptions = { _reason: 'quickAccessSearch', From 51528d8faa1d2104ba2b188059224a44f934e765 Mon Sep 17 00:00:00 2001 From: Harald Kirschner Date: Mon, 30 Oct 2023 16:34:40 -0700 Subject: [PATCH 09/43] Remove unnecessary watermark definition --- src/vs/workbench/browser/parts/editor/editorGroupWatermark.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorGroupWatermark.ts b/src/vs/workbench/browser/parts/editor/editorGroupWatermark.ts index 6a6fca85b64..3c4ac7d594b 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupWatermark.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupWatermark.ts @@ -28,8 +28,7 @@ const openFileNonMacOnly: WatermarkEntry = { text: localize('watermark.openFile' const openFolderNonMacOnly: WatermarkEntry = { text: localize('watermark.openFolder', "Open Folder"), id: 'workbench.action.files.openFolder', mac: false }; const openFileOrFolderMacOnly: WatermarkEntry = { text: localize('watermark.openFileFolder', "Open File or Folder"), id: 'workbench.action.files.openFileFolder', mac: true }; const openRecent: WatermarkEntry = { text: localize('watermark.openRecent', "Open Recent"), id: 'workbench.action.openRecent' }; -const newUntitledFile: WatermarkEntry = { text: localize('watermark.newUntitledFile', "New Untitled Text File"), id: 'workbench.action.files.newUntitledFile' }; -const newUntitledFileMacOnly: WatermarkEntry = Object.assign({ mac: true }, newUntitledFile); +const newUntitledFileMacOnly: WatermarkEntry = { text: localize('watermark.newUntitledFile', "New Untitled Text File"), id: 'workbench.action.files.newUntitledFile', mac: true }; const findInFiles: WatermarkEntry = { text: localize('watermark.findInFiles', "Find in Files"), id: 'workbench.action.findInFiles' }; const toggleTerminal: WatermarkEntry = { text: localize({ key: 'watermark.toggleTerminal', comment: ['toggle is a verb here'] }, "Toggle Terminal"), id: 'workbench.action.terminal.toggleTerminal', when: ContextKeyExpr.equals('terminalProcessSupported', true) }; const startDebugging: WatermarkEntry = { text: localize('watermark.startDebugging', "Start Debugging"), id: 'workbench.action.debug.start', when: ContextKeyExpr.equals('terminalProcessSupported', true) }; From 0279c52a021e0da5a4c87a74c7be6a20ec23a270 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 30 Oct 2023 16:40:44 -0700 Subject: [PATCH 10/43] Switch back to `node` as default for `moduleResolution` (#197031) Fixes #196554 Looks like bundler breaks `require()` intellisense --- extensions/typescript-language-features/src/tsconfig.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/typescript-language-features/src/tsconfig.ts b/extensions/typescript-language-features/src/tsconfig.ts index f5f91b23620..196cf185170 100644 --- a/extensions/typescript-language-features/src/tsconfig.ts +++ b/extensions/typescript-language-features/src/tsconfig.ts @@ -27,7 +27,7 @@ export function inferredProjectCompilerOptions( ): Proto.ExternalProjectCompilerOptions { const projectConfig: Proto.ExternalProjectCompilerOptions = { module: 'ESNext' as Proto.ModuleKind, - moduleResolution: (version.gte(API.v500) ? 'Bundler' : 'Node') as Proto.ModuleResolutionKind, + moduleResolution: 'Node' as Proto.ModuleResolutionKind, target: 'ES2022' as Proto.ScriptTarget, jsx: 'react' as Proto.JsxEmit, }; From f5e777073e7dedd0a9a27f36eb6733b794e8ecd5 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 31 Oct 2023 08:14:29 +0100 Subject: [PATCH 11/43] support debug toolbar on title bar --- src/vs/workbench/browser/layout.ts | 2 +- .../browser/parts/titlebar/titlebarPart.ts | 10 ++-- .../browser/workbench.contribution.ts | 14 ++--- .../contrib/debug/browser/debugToolBar.ts | 57 ++++++++++++++----- .../debug/browser/media/debugToolBar.css | 4 +- .../services/layout/browser/layoutService.ts | 8 +++ 6 files changed, 66 insertions(+), 29 deletions(-) diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 19553fffaa9..d602ee381da 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -1185,7 +1185,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } // with the command center enabled, we should always show - if (this.configurationService.getValue('window.commandCenter')) { + if (this.configurationService.getValue(LayoutSettings.COMMAND_CENTER)) { return true; } diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index 501b72806f4..07533e0e039 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -24,7 +24,7 @@ import { CustomMenubarControl } from 'vs/workbench/browser/parts/titlebar/menuba import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { Emitter, Event } from 'vs/base/common/event'; import { IStorageService } from 'vs/platform/storage/common/storage'; -import { Parts, IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; +import { Parts, IWorkbenchLayoutService, LayoutSettings } from 'vs/workbench/services/layout/browser/layoutService'; import { createActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; @@ -43,8 +43,6 @@ import { HoverPosition } from 'vs/base/browser/ui/hover/hoverWidget'; export class TitlebarPart extends Part implements ITitleService { - private static readonly configCommandCenter = 'window.commandCenter'; - declare readonly _serviceBrand: undefined; //#region IView @@ -134,7 +132,7 @@ export class TitlebarPart extends Part implements ITitleService { } get isCommandCenterVisible() { - return this.configurationService.getValue(TitlebarPart.configCommandCenter); + return this.configurationService.getValue(LayoutSettings.COMMAND_CENTER); } private registerListeners(): void { @@ -169,7 +167,7 @@ export class TitlebarPart extends Part implements ITitleService { this._onDidChange.fire(undefined); } - if (event.affectsConfiguration(TitlebarPart.configCommandCenter)) { + if (event.affectsConfiguration(LayoutSettings.COMMAND_CENTER)) { this.updateTitle(); this._onDidChangeCommandCenterVisibility.fire(); this._onDidChange.fire(undefined); @@ -475,7 +473,7 @@ class ToogleConfigAction extends Action2 { registerAction2(class ToogleCommandCenter extends ToogleConfigAction { constructor() { - super('window.commandCenter', localize('toggle.commandCenter', 'Command Center'), 1); + super(LayoutSettings.COMMAND_CENTER, localize('toggle.commandCenter', 'Command Center'), 1); } }); diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index 8e1e052f18c..bc5cc382e16 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -11,7 +11,7 @@ import { ConfigurationMigrationWorkbenchContribution, DynamicWorkbenchConfigurat import { isStandalone } from 'vs/base/browser/browser'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; -import { ActivityBarPosition, LayoutSettings } from 'vs/workbench/services/layout/browser/layoutService'; +import { ActivityBarPosition, EditorTabsMode, LayoutSettings } from 'vs/workbench/services/layout/browser/layoutService'; const registry = Registry.as(ConfigurationExtensions.Configuration); @@ -38,9 +38,9 @@ const registry = Registry.as(ConfigurationExtensions.Con description: localize('tabScrollbarHeight', "Controls the height of the scrollbars used for tabs and breadcrumbs in the editor title area."), default: 'default', }, - 'workbench.editor.showTabs': { + [LayoutSettings.EDITOR_TABS_MODE]: { 'type': 'string', - 'enum': ['multiple', 'single', 'none'], + 'enum': [EditorTabsMode.MULTIPLE, EditorTabsMode.SINGLE, EditorTabsMode.NONE], 'enumDescriptions': [ localize('workbench.editor.showTabs.multiple', "Each editor is displayed as a tab in the editor title area."), localize('workbench.editor.showTabs.single', "The active editor is displayed as a single large tab in the editor title area."), @@ -622,7 +622,7 @@ const registry = Registry.as(ConfigurationExtensions.Con 'default': isMacintosh ? ' \u2014 ' : ' - ', 'markdownDescription': localize("window.titleSeparator", "Separator used by {0}.", '`#window.title#`') }, - 'window.commandCenter': { + [LayoutSettings.COMMAND_CENTER]: { type: 'boolean', default: true, markdownDescription: isWeb ? @@ -796,11 +796,11 @@ Registry.as(Extensions.ConfigurationMigration) return [['workbench.editor.doubleClickTabToToggleEditorGroupSizes', { value: value }]]; } }, { - key: 'workbench.editor.showTabs', migrateFn: (value: any) => { + key: LayoutSettings.EDITOR_TABS_MODE, migrateFn: (value: any) => { if (typeof value === 'boolean') { - value = value ? 'multiple' : 'single'; + value = value ? EditorTabsMode.MULTIPLE : EditorTabsMode.SINGLE; } - return [['workbench.editor.showTabs', { value: value }]]; + return [[LayoutSettings.EDITOR_TABS_MODE, { value }]]; } }, { key: 'zenMode.hideTabs', migrateFn: (value: any) => { diff --git a/src/vs/workbench/contrib/debug/browser/debugToolBar.ts b/src/vs/workbench/contrib/debug/browser/debugToolBar.ts index 83372ce9637..f19c54d885e 100644 --- a/src/vs/workbench/contrib/debug/browser/debugToolBar.ts +++ b/src/vs/workbench/contrib/debug/browser/debugToolBar.ts @@ -36,7 +36,7 @@ import { debugToolBarBackground, debugToolBarBorder } from 'vs/workbench/contrib import { CONTINUE_ID, CONTINUE_LABEL, DISCONNECT_AND_SUSPEND_ID, DISCONNECT_AND_SUSPEND_LABEL, DISCONNECT_ID, DISCONNECT_LABEL, FOCUS_SESSION_ID, FOCUS_SESSION_LABEL, PAUSE_ID, PAUSE_LABEL, RESTART_LABEL, RESTART_SESSION_ID, REVERSE_CONTINUE_ID, STEP_BACK_ID, STEP_INTO_ID, STEP_INTO_LABEL, STEP_OUT_ID, STEP_OUT_LABEL, STEP_OVER_ID, STEP_OVER_LABEL, STOP_ID, STOP_LABEL } from 'vs/workbench/contrib/debug/browser/debugCommands'; import * as icons from 'vs/workbench/contrib/debug/browser/debugIcons'; import { CONTEXT_DEBUG_STATE, CONTEXT_FOCUSED_SESSION_IS_ATTACH, CONTEXT_IN_DEBUG_MODE, CONTEXT_MULTI_SESSION_DEBUG, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_SUSPEND_DEBUGGEE_SUPPORTED, CONTEXT_TERMINATE_DEBUGGEE_SUPPORTED, IDebugConfiguration, IDebugService, State, VIEWLET_ID } from 'vs/workbench/contrib/debug/common/debug'; -import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; +import { EditorTabsMode, IWorkbenchLayoutService, LayoutSettings, Parts } from 'vs/workbench/services/layout/browser/layoutService'; import { Codicon } from 'vs/base/common/codicons'; const DEBUG_TOOLBAR_POSITION_KEY = 'debug.actionswidgetposition'; @@ -73,6 +73,7 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution { this.$el = dom.$('div.debug-toolbar'); this.$el.style.top = `${layoutService.mainContainerOffset.top}px`; + this.setHeight(); this.dragArea = dom.append(this.$el, dom.$('div.drag-area' + ThemeIcon.asCSSSelector(icons.debugGripper))); @@ -132,6 +133,11 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution { if (e.affectsConfiguration('debug.toolBarLocation')) { this.updateScheduler.schedule(); } + if (e.affectsConfiguration(LayoutSettings.EDITOR_TABS_MODE) || e.affectsConfiguration(LayoutSettings.COMMAND_CENTER)) { + this._yRange = undefined; + this.setHeight(); + this.setYCoordinate(); + } })); this._register(this.debugToolBarMenu.onDidChange(() => this.updateScheduler.schedule())); this._register(this.actionBar.actionRunner.onDidRun((e: IRunEvent) => { @@ -163,7 +169,7 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution { // Prevent default to stop editor selecting text #8524 mouseMoveEvent.preventDefault(); // Reduce x by width of drag handle to reduce jarring #16604 - this.setCoordinates(mouseMoveEvent.posx - 14, mouseMoveEvent.posy - (this.layoutService.mainContainerOffset.top)); + this.setCoordinates(mouseMoveEvent.posx - 14, mouseMoveEvent.posy); }); const mouseUpListener = dom.addDisposableGenericMouseUpListener(window, (e: MouseEvent) => { @@ -208,12 +214,6 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution { } } - private setYCoordinate(y = this.yCoordinate): void { - const titlebarOffset = this.layoutService.mainContainerOffset.top; - this.$el.style.top = `${titlebarOffset + y}px`; - this.yCoordinate = y; - } - private setCoordinates(x?: number, y?: number): void { if (!this.isVisible) { return; @@ -230,12 +230,43 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution { if (y === undefined) { y = this.storageService.getNumber(DEBUG_TOOLBAR_Y_KEY, StorageScope.PROFILE, 0); } - const titleAreaHeight = 35; - if ((y < titleAreaHeight / 2) || (y > titleAreaHeight + titleAreaHeight / 2)) { - const moveToTop = y < titleAreaHeight; - this.setYCoordinate(moveToTop ? 0 : titleAreaHeight); - this.storageService.store(DEBUG_TOOLBAR_Y_KEY, moveToTop ? 0 : 2 * titleAreaHeight, StorageScope.PROFILE, StorageTarget.MACHINE); + + this.setYCoordinate(y); + } + + private setYCoordinate(y = this.yCoordinate): void { + const [yMin, yMax] = this.yRange; + y = Math.max(yMin, Math.min(y, yMax)); + this.$el.style.top = `${y}px`; + this.yCoordinate = y; + this.storageService.store(DEBUG_TOOLBAR_Y_KEY, y, StorageScope.PROFILE, StorageTarget.MACHINE); + } + + private _yRange: [number, number] | undefined; + private get yRange(): [number, number] { + if (!this._yRange) { + const isTitleBarVisible = this.layoutService.isVisible(Parts.TITLEBAR_PART); + const yMin = isTitleBarVisible ? 0 : this.layoutService.mainContainerOffset.top; + let yMax = 0; + + if (isTitleBarVisible) { + if (this.configurationService.getValue(LayoutSettings.COMMAND_CENTER) === true) { + yMax += 35; + } else { + yMax += 28; + } + } + + if (this.configurationService.getValue(LayoutSettings.EDITOR_TABS_MODE) !== EditorTabsMode.NONE) { + yMax += 35; + } + this._yRange = [yMin, yMax]; } + return this._yRange; + } + + private setHeight(): void { + this.$el.style.height = `${this.configurationService.getValue(LayoutSettings.EDITOR_TABS_MODE) === EditorTabsMode.NONE && this.configurationService.getValue(LayoutSettings.COMMAND_CENTER) !== true ? 26 : 32}px`; } private show(): void { diff --git a/src/vs/workbench/contrib/debug/browser/media/debugToolBar.css b/src/vs/workbench/contrib/debug/browser/media/debugToolBar.css index f32eaab62f8..64260286436 100644 --- a/src/vs/workbench/contrib/debug/browser/media/debugToolBar.css +++ b/src/vs/workbench/contrib/debug/browser/media/debugToolBar.css @@ -5,11 +5,11 @@ .monaco-workbench .debug-toolbar { position: absolute; - z-index: 39; - height: 32px; + z-index: 3000; display: flex; padding-left: 7px; border-radius: 4px; + -webkit-app-region: no-drag; } .monaco-workbench .debug-toolbar .monaco-action-bar .action-item { diff --git a/src/vs/workbench/services/layout/browser/layoutService.ts b/src/vs/workbench/services/layout/browser/layoutService.ts index 7c44a06d34a..5c05a72e79f 100644 --- a/src/vs/workbench/services/layout/browser/layoutService.ts +++ b/src/vs/workbench/services/layout/browser/layoutService.ts @@ -25,6 +25,8 @@ export const enum Parts { export const enum LayoutSettings { ACTIVITY_BAR_LOCATION = 'workbench.activityBar.location', + EDITOR_TABS_MODE = 'workbench.editor.showTabs', + COMMAND_CENTER = 'window.commandCenter', } export const enum ActivityBarPosition { @@ -33,6 +35,12 @@ export const enum ActivityBarPosition { HIDDEN = 'hidden' } +export const enum EditorTabsMode { + MULTIPLE = 'multiple', + SINGLE = 'single', + NONE = 'none' +} + export const enum Position { LEFT, RIGHT, From bee0ea18bd50319bca5a3374b3e6d9285d82718b Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 31 Oct 2023 09:00:40 +0100 Subject: [PATCH 12/43] Move wait for workbench restoration to Application (#197053) #195850: Move wait for workbench restoration to Application#checkWindowReady --- test/automation/src/application.ts | 3 +++ test/automation/src/quickaccess.ts | 4 ---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/test/automation/src/application.ts b/test/automation/src/application.ts index 83e7a76ace7..f969e5b0834 100644 --- a/test/automation/src/application.ts +++ b/test/automation/src/application.ts @@ -120,6 +120,9 @@ export class Application { await measureAndLog(() => code.didFinishLoad(), 'Application#checkWindowReady: wait for navigation to be committed', this.logger); await measureAndLog(() => code.waitForElement('.monaco-workbench'), 'Application#checkWindowReady: wait for .monaco-workbench element', this.logger); + // Wait for workbench to be restored + await this.code.whenWorkbenchRestored(); + // Remote but not web: wait for a remote connection state change if (this.remote) { await measureAndLog(() => code.waitForTextContent('.monaco-workbench .statusbar-item[id="status.host"]', undefined, statusHostLabel => { diff --git a/test/automation/src/quickaccess.ts b/test/automation/src/quickaccess.ts index b14197b4f4b..5d94a96f1c4 100644 --- a/test/automation/src/quickaccess.ts +++ b/test/automation/src/quickaccess.ts @@ -198,9 +198,6 @@ export class QuickAccess { this.code.logger.log(`QuickAccess: No matching commands, will retry...`); await this.quickInput.closeQuickInput(); - // Wait for workbench to be restored - await this.code.whenWorkbenchRestored(); - let retries = 0; while (++retries < 5) { hasCommandFound = await openCommandPalletteAndTypeCommand(); @@ -218,7 +215,6 @@ export class QuickAccess { } } - // wait and click on best choice await this.quickInput.selectQuickInputElement(0, keepOpen); } From 5240f7230a43711b1075d9ecdc5c436eaeb38589 Mon Sep 17 00:00:00 2001 From: n-gist <58081918+n-gist@users.noreply.github.com> Date: Tue, 31 Oct 2023 14:09:53 +0600 Subject: [PATCH 13/43] Add pinned tab button (icon) control setting (#196896) * add pinned tab button (icon) control setting - workbench.editor.pinnedTabButton * Update editor tab unpin action * Update editor tab action buttons location and visibility settings mechanics * Mount tab action button visibility into intermediate TabAction class * Refactor editor tab actions and settings --------- Co-authored-by: BeniBenj --- .../workbench/browser/parts/editor/editor.ts | 8 +++-- .../editor/media/multieditortabscontrol.css | 33 +++++++++---------- .../parts/editor/multiEditorTabsControl.ts | 32 +++++++++++++----- .../browser/workbench.contribution.ts | 30 ++++++++++++++--- src/vs/workbench/common/editor.ts | 4 ++- 5 files changed, 74 insertions(+), 33 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editor.ts b/src/vs/workbench/browser/parts/editor/editor.ts index b3486140a6d..2a6571abd4a 100644 --- a/src/vs/workbench/browser/parts/editor/editor.ts +++ b/src/vs/workbench/browser/parts/editor/editor.ts @@ -28,7 +28,9 @@ export const DEFAULT_EDITOR_MAX_DIMENSIONS = new Dimension(Number.POSITIVE_INFIN export const DEFAULT_EDITOR_PART_OPTIONS: IEditorPartOptions = { showTabs: 'multiple', highlightModifiedTabs: false, - tabCloseButton: 'right', + tabActionLocation: 'right', + tabActionCloseVisibility: true, + tabActionUnpinVisibility: true, tabSizing: 'fit', tabSizingFixedMinWidth: 50, tabSizingFixedMaxWidth: 160, @@ -103,6 +105,8 @@ function validateEditorPartOptions(options: IEditorPartOptions): void { 'wrapTabs', 'scrollToSwitchTabs', 'highlightModifiedTabs', + 'tabActionCloseVisibility', + 'tabActionUnpinVisibility', 'pinnedTabsOnSeparateRow', 'focusRecentEditorAfterClose', 'showIcons', @@ -137,7 +141,7 @@ function validateEditorPartOptions(options: IEditorPartOptions): void { // String options const stringOptions: Array<[OptionalStringKey, Array]> = [ ['showTabs', ['multiple', 'single', 'none']], - ['tabCloseButton', ['left', 'right', 'off']], + ['tabActionLocation', ['left', 'right']], ['tabSizing', ['fit', 'shrink', 'fixed']], ['pinnedTabSizing', ['normal', 'compact', 'shrink']], ['tabHeight', ['default', 'compact']], diff --git a/src/vs/workbench/browser/parts/editor/media/multieditortabscontrol.css b/src/vs/workbench/browser/parts/editor/media/multieditortabscontrol.css index 5fde102f436..3c892652a73 100644 --- a/src/vs/workbench/browser/parts/editor/media/multieditortabscontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/multieditortabscontrol.css @@ -115,9 +115,9 @@ } .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.has-icon.tab-actions-right, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.has-icon.tab-actions-off:not(.sticky-compact), +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.has-icon.close-action-off:not(.sticky-compact), .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed.has-icon.tab-actions-right, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed.has-icon.tab-actions-off:not(.sticky-compact) { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed.has-icon.close-action-off:not(.sticky-compact) { padding-left: 5px; /* reduce padding when we show icons and are in shrinking mode and tab actions is not left (unless sticky-compact) */ } @@ -205,9 +205,9 @@ } .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.tab-actions-left::after, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.tab-actions-off::after, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.close-action-off::after, .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed.tab-actions-left::after, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed.tab-actions-off::after { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed.close-action-off::after { content: ''; display: flex; flex: 0; @@ -310,7 +310,7 @@ padding-right: 5px; /* with tab sizing shrink/fixed and badges, we want a right-padding because the close button is hidden */ } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink:not(.tab-actions-left):not(.tab-actions-off) .tab-label { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink:not(.tab-actions-left):not(.close-action-off) .tab-label { padding-right: 5px; /* ensure that the gradient does not show when tab actions show https://github.com/microsoft/vscode/issues/189625*/ } @@ -365,16 +365,16 @@ overflow: visible; /* ...but still show the tab actions on hover, focus and when dirty or sticky */ } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-off:not(.dirty):not(.sticky) > .tab-actions, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-off.sticky-compact > .tab-actions { - display: none; /* hide the tab actions when we are configured to hide it (unless dirty or sticky, but always when sticky-compact) */ +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-action-off:not(.dirty) > .tab-actions, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-action-off.sticky-compact > .tab-actions { + display: none; /* hide the tab actions when we are configured to hide it (unless dirty, but always when sticky-compact) */ } .monaco-workbench .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab.active > .tab-actions .action-label, /* always show tab actions for active tab */ .monaco-workbench .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab > .tab-actions .action-label:focus, /* always show tab actions on focus */ .monaco-workbench .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab:hover > .tab-actions .action-label, /* always show tab actions on hover */ .monaco-workbench .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab.active:hover > .tab-actions .action-label, /* always show tab actions on hover */ -.monaco-workbench .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab.sticky > .tab-actions .action-label, /* always show tab actions for sticky tabs */ +.monaco-workbench .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab.sticky:not(.pinned-action-off) > .tab-actions .action-label, /* always show tab actions for sticky tabs */ .monaco-workbench .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab.dirty > .tab-actions .action-label { /* always show tab actions for dirty tabs */ opacity: 1; } @@ -404,7 +404,7 @@ .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active > .tab-actions .action-label, .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active:hover > .tab-actions .action-label, .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty > .tab-actions .action-label, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sticky > .tab-actions .action-label, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sticky:not(.pinned-action-off) > .tab-actions .action-label, .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab:hover > .tab-actions .action-label { opacity: 0.5; /* show tab actions dimmed for inactive group */ } @@ -415,25 +415,24 @@ /* Tab Actions: Off */ -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-off { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-action-off { padding-right: 10px; /* give a little bit more room if tab actions is off */ } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.tab-actions-off:not(.sticky-compact), -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed.tab-actions-off:not(.sticky-compact) { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.close-action-off:not(.sticky-compact), +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed.close-action-off:not(.sticky-compact) { padding-right: 5px; /* we need less room when sizing is shrink/fixed (unless tab is sticky-compact) */ } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-off.dirty-border-top > .tab-actions { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-action-off.dirty-border-top > .tab-actions { display: none; /* hide dirty state when highlightModifiedTabs is enabled and when running without tab actions */ } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-off.dirty:not(.dirty-border-top):not(.sticky-compact), -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-off.sticky:not(.sticky-compact) { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-action-off.dirty:not(.dirty-border-top):not(.sticky-compact) { padding-right: 0; /* remove extra padding when we are running without tab actions (unless tab is sticky-compact) */ } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-off > .tab-actions { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-action-off > .tab-actions { pointer-events: none; /* don't allow tab actions to be clicked when running without tab actions */ } diff --git a/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts b/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts index 834e2d66644..0acabfac24d 100644 --- a/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts +++ b/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts @@ -746,7 +746,9 @@ export class MultiEditorTabsControl extends EditorTabsControl { // Redraw tabs when other options change if ( oldOptions.labelFormat !== newOptions.labelFormat || - oldOptions.tabCloseButton !== newOptions.tabCloseButton || + oldOptions.tabActionLocation !== newOptions.tabActionLocation || + oldOptions.tabActionCloseVisibility !== newOptions.tabActionCloseVisibility || + oldOptions.tabActionUnpinVisibility !== newOptions.tabActionUnpinVisibility || oldOptions.tabSizing !== newOptions.tabSizing || oldOptions.pinnedTabSizing !== newOptions.pinnedTabSizing || oldOptions.showIcons !== newOptions.showIcons || @@ -1322,19 +1324,33 @@ export class MultiEditorTabsControl extends EditorTabsControl { this.redrawTabLabel(editor, tabIndex, tabContainer, tabLabelWidget, tabLabel); // Action - const tabAction = isTabSticky ? this.unpinEditorAction : this.closeEditorAction; - if (!tabActionBar.hasAction(tabAction)) { + const hasUnpinAction = isTabSticky && options.tabActionUnpinVisibility; + const hasCloseAction = !hasUnpinAction && options.tabActionCloseVisibility; + const hasAction = hasUnpinAction || hasCloseAction; + + // If no action is visible, ensure to clear it + if (!hasAction) { if (!tabActionBar.isEmpty()) { tabActionBar.clear(); } + } + // If action is visible, ensure it is showing + else { + const tabAction = hasUnpinAction ? this.unpinEditorAction : this.closeEditorAction; + if (!tabActionBar.hasAction(tabAction)) { + if (!tabActionBar.isEmpty()) { + tabActionBar.clear(); + } - tabActionBar.push(tabAction, { icon: true, label: false, keybinding: this.getKeybindingLabel(tabAction) }); + tabActionBar.push(tabAction, { icon: true, label: false, keybinding: this.getKeybindingLabel(tabAction) }); + } } - // Settings - const tabActionsVisibility = isTabSticky && options.pinnedTabSizing === 'compact' ? 'off' /* treat sticky compact tabs as tabCloseButton: 'off' */ : options.tabCloseButton; - for (const option of ['off', 'left', 'right']) { - tabContainer.classList.toggle(`tab-actions-${option}`, tabActionsVisibility === option); + tabContainer.classList.toggle(`pinned-action-off`, isTabSticky && !hasUnpinAction); + tabContainer.classList.toggle(`close-action-off`, !hasUnpinAction && !hasCloseAction); + + for (const option of ['left', 'right']) { + tabContainer.classList.toggle(`tab-actions-${option}`, hasAction && options.tabActionLocation === option); } const tabSizing = isTabSticky && options.pinnedTabSizing === 'shrink' ? 'shrink' /* treat sticky shrink tabs as tabSizing: 'shrink' */ : options.tabSizing; diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index 8e1e052f18c..ef76a3f524a 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -143,11 +143,21 @@ const registry = Registry.as(ConfigurationExtensions.Con } } }, - 'workbench.editor.tabCloseButton': { - 'type': 'string', - 'enum': ['left', 'right', 'off'], - 'default': 'right', - 'markdownDescription': localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'editorTabCloseButton' }, "Controls the position of the editor's tabs close buttons, or disables them when set to 'off'. This value is ignored when `#workbench.editor.showTabs#` is not set to `multiple`.") + 'workbench.editor.tabActionLocation': { + type: 'string', + enum: ['left', 'right'], + default: 'right', + markdownDescription: localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'tabActionLocation' }, "Controls the position of the editor's tabs action buttons (close, unpin). This value is ignored when `#workbench.editor.showTabs#` is not set to `multiple`.") + }, + 'workbench.editor.tabActionCloseVisibility': { + type: 'boolean', + default: true, + description: localize('workbench.editor.tabActionCloseVisibility', "Controls the visibility of the tab close action button.") + }, + 'workbench.editor.tabActionUnpinVisibility': { + type: 'boolean', + default: true, + description: localize('workbench.editor.tabActionUnpinVisibility', "Controls the visibility of the tab unpin action button.") }, 'workbench.editor.tabSizing': { 'type': 'string', @@ -802,6 +812,16 @@ Registry.as(Extensions.ConfigurationMigration) } return [['workbench.editor.showTabs', { value: value }]]; } + }, { + key: 'workbench.editor.tabCloseButton', migrateFn: (value: any) => { + const result: ConfigurationKeyValuePairs = []; + if (value === 'left' || value === 'right') { + result.push(['workbench.editor.tabActionLocation', { value }]); + } else if (value === 'off') { + result.push(['workbench.editor.tabActionCloseVisibility', { value: false }]); + } + return result; + } }, { key: 'zenMode.hideTabs', migrateFn: (value: any) => { const result: ConfigurationKeyValuePairs = [['zenMode.hideTabs', { value: undefined }]]; diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index e80c28f88a6..0c35a488fe3 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -1113,7 +1113,9 @@ interface IEditorPartConfiguration { wrapTabs?: boolean; scrollToSwitchTabs?: boolean; highlightModifiedTabs?: boolean; - tabCloseButton?: 'left' | 'right' | 'off'; + tabActionLocation?: 'left' | 'right'; + tabActionCloseVisibility?: boolean; + tabActionUnpinVisibility?: boolean; tabSizing?: 'fit' | 'shrink' | 'fixed'; tabSizingFixedMinWidth?: number; tabSizingFixedMaxWidth?: number; From 8e991e784cbc2d7edc133fcbc41de22f89dff62e Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 31 Oct 2023 09:10:23 +0100 Subject: [PATCH 14/43] Aux window: Enable screencast mode support (fix #196087) (#197047) --- src/vs/base/browser/dom.ts | 61 +++++++++++-------- .../quickInput/standaloneQuickInputService.ts | 1 + .../browser/standaloneLayoutService.ts | 4 ++ .../platform/layout/browser/layoutService.ts | 5 ++ .../window/electron-sandbox/window.ts | 2 +- .../browser/actions/developerActions.ts | 57 +++++++++++++---- src/vs/workbench/browser/layout.ts | 25 +++++--- .../electron-sandbox/actions/windowActions.ts | 2 +- .../electron-sandbox/nativeHostService.ts | 8 +++ .../services/layout/browser/layoutService.ts | 1 + .../test/browser/workbenchTestServices.ts | 2 +- 11 files changed, 116 insertions(+), 52 deletions(-) diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index 13ff65cfb4e..1949053395a 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -18,23 +18,38 @@ import * as platform from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; import { hash } from 'vs/base/common/hash'; +type WindowGlobal = Window & typeof globalThis; + +interface IWindow { + readonly window: WindowGlobal; + readonly disposables: DisposableStore; +} + export const { registerWindow, getWindows, getWindowsCount, onDidRegisterWindow, onWillUnregisterWindow, onDidUnregisterWindow } = (function () { - const windows = new Set([window]); - const onDidRegisterWindow = new event.Emitter<{ window: Window & typeof globalThis; disposables: DisposableStore }>(); - const onDidUnregisterWindow = new event.Emitter(); - const onWillUnregisterWindow = new event.Emitter(); + const windows = new Map(); + windows.set(window, { window, disposables: new DisposableStore() }); + + const onDidRegisterWindow = new event.Emitter(); + const onDidUnregisterWindow = new event.Emitter(); + const onWillUnregisterWindow = new event.Emitter(); + return { onDidRegisterWindow: onDidRegisterWindow.event, onWillUnregisterWindow: onWillUnregisterWindow.event, onDidUnregisterWindow: onDidUnregisterWindow.event, - registerWindow(window: Window & typeof globalThis): IDisposable { + registerWindow(window: WindowGlobal): IDisposable { if (windows.has(window)) { return Disposable.None; } - windows.add(window); - const disposables = new DisposableStore(); + + const registeredWindow = { + window, + disposables: disposables.add(new DisposableStore()) + }; + windows.set(window, registeredWindow); + disposables.add(toDisposable(() => { windows.delete(window); onDidUnregisterWindow.fire(window); @@ -44,14 +59,12 @@ export const { registerWindow, getWindows, getWindowsCount, onDidRegisterWindow, onWillUnregisterWindow.fire(window); })); - const eventDisposables = new DisposableStore(); - disposables.add(eventDisposables); - onDidRegisterWindow.fire({ window, disposables: eventDisposables }); + onDidRegisterWindow.fire(registeredWindow); return disposables; }, - getWindows(): Iterable { - return windows; + getWindows(): Iterable { + return windows.values(); }, getWindowsCount(): number { return windows.size; @@ -758,19 +771,19 @@ export function getActiveDocument(): Document { return document; } - const documents = Array.from(getWindows()).map(window => window.document); - return documents.find(doc => doc.hasFocus()) ?? document; + const documents = Array.from(getWindows()).map(({ window }) => window.document); + return documents.find(document => document.hasFocus()) ?? document; } -export function getActiveWindow(): Window & typeof globalThis { +export function getActiveWindow(): WindowGlobal { const document = getActiveDocument(); return document.defaultView?.window ?? window; } -export function getWindow(element: Node): Window & typeof globalThis; -export function getWindow(event: UIEvent): Window & typeof globalThis; -export function getWindow(obj: unknown): Window & typeof globalThis; -export function getWindow(e: unknown): Window & typeof globalThis { +export function getWindow(element: Node): WindowGlobal; +export function getWindow(event: UIEvent): WindowGlobal; +export function getWindow(obj: unknown): WindowGlobal; +export function getWindow(e: unknown): WindowGlobal { const candidateNode = e as Node | undefined; if (candidateNode?.ownerDocument?.defaultView) { return candidateNode.ownerDocument.defaultView.window; @@ -805,19 +818,13 @@ export function createStyleSheet(container: HTMLElement = document.head, beforeA // With as container, the stylesheet becomes global and is tracked // to support auxiliary windows to clone the stylesheet. if (container === document.head) { - for (const targetWindow of getWindows()) { + for (const { window: targetWindow, disposables } of getWindows()) { if (targetWindow === window) { continue; // main window is already tracked } - const cloneDisposable = cloneGlobalStyleSheet(style, targetWindow); + const cloneDisposable = disposables.add(cloneGlobalStyleSheet(style, targetWindow)); disposableStore?.add(cloneDisposable); - - disposableStore?.add(event.Event.once(onDidUnregisterWindow)(unregisteredWindow => { - if (unregisteredWindow === targetWindow) { - cloneDisposable.dispose(); - } - })); } } diff --git a/src/vs/editor/standalone/browser/quickInput/standaloneQuickInputService.ts b/src/vs/editor/standalone/browser/quickInput/standaloneQuickInputService.ts index 2c45263ca24..28cde30f584 100644 --- a/src/vs/editor/standalone/browser/quickInput/standaloneQuickInputService.ts +++ b/src/vs/editor/standalone/browser/quickInput/standaloneQuickInputService.ts @@ -41,6 +41,7 @@ class EditorScopedQuickInputService extends QuickInputService { _serviceBrand: undefined, get hasContainer() { return true; }, get container() { return widget.getDomNode(); }, + getContainer() { return widget.getDomNode(); }, get containers() { return [widget.getDomNode()]; }, get activeContainer() { return widget.getDomNode(); }, get mainContainerDimension() { return editor.getLayoutInfo(); }, diff --git a/src/vs/editor/standalone/browser/standaloneLayoutService.ts b/src/vs/editor/standalone/browser/standaloneLayoutService.ts index 86aeb14e2ab..ae901c68b41 100644 --- a/src/vs/editor/standalone/browser/standaloneLayoutService.ts +++ b/src/vs/editor/standalone/browser/standaloneLayoutService.ts @@ -54,6 +54,10 @@ class StandaloneLayoutService implements ILayoutService { return activeCodeEditor?.getContainerDomNode() ?? this.container; } + getContainer() { + return this.activeContainer; + } + focus(): void { this._codeEditorService.getFocusedCodeEditor()?.focus(); } diff --git a/src/vs/platform/layout/browser/layoutService.ts b/src/vs/platform/layout/browser/layoutService.ts index 8ba84c5d9e5..7ebad2e5fa4 100644 --- a/src/vs/platform/layout/browser/layoutService.ts +++ b/src/vs/platform/layout/browser/layoutService.ts @@ -81,6 +81,11 @@ export interface ILayoutService { */ readonly containers: Iterable; + /** + * Get the container for the given window. + */ + getContainer(window: Window): HTMLElement; + /** * An offset to use for positioning elements inside the main container. */ diff --git a/src/vs/platform/window/electron-sandbox/window.ts b/src/vs/platform/window/electron-sandbox/window.ts index 28968b4f9cc..692dbb9e44c 100644 --- a/src/vs/platform/window/electron-sandbox/window.ts +++ b/src/vs/platform/window/electron-sandbox/window.ts @@ -13,7 +13,7 @@ import { zoomLevelToZoomFactor } from 'vs/platform/window/common/window'; * browser helper so that it can be accessed in non-electron layers. */ export function applyZoom(zoomLevel: number): void { - for (const window of getWindows()) { + for (const { window } of getWindows()) { getGlobals(window)?.webFrame?.setZoomLevel(zoomLevel); } setZoomFactor(zoomLevelToZoomFactor(zoomLevel)); diff --git a/src/vs/workbench/browser/actions/developerActions.ts b/src/vs/workbench/browser/actions/developerActions.ts index 18d160704a6..14430b7c668 100644 --- a/src/vs/workbench/browser/actions/developerActions.ts +++ b/src/vs/workbench/browser/actions/developerActions.ts @@ -9,9 +9,9 @@ import { localize } from 'vs/nls'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { DomEmitter } from 'vs/base/browser/event'; import { Color } from 'vs/base/common/color'; -import { Event } from 'vs/base/common/event'; +import { Emitter, Event } from 'vs/base/common/event'; import { IDisposable, toDisposable, dispose, DisposableStore, setDisposableTracker, DisposableTracker, DisposableInfo } from 'vs/base/common/lifecycle'; -import { getDomNodePagePosition, createStyleSheet, createCSSRule, append, $, getActiveDocument } from 'vs/base/browser/dom'; +import { getDomNodePagePosition, createStyleSheet, createCSSRule, append, $, getActiveDocument, onDidRegisterWindow, getWindows } from 'vs/base/browser/dom'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ContextKeyExpr, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { Context } from 'vs/platform/contextkey/browser/contextKeyService'; @@ -130,13 +130,34 @@ class ToggleScreencastModeAction extends Action2 { const disposables = new DisposableStore(); - const container = layoutService.container; + const container = layoutService.activeContainer; + const mouseMarker = append(container, $('.screencast-mouse')); disposables.add(toDisposable(() => mouseMarker.remove())); - const onMouseDown = disposables.add(new DomEmitter(container, 'mousedown', true)); - const onMouseUp = disposables.add(new DomEmitter(container, 'mouseup', true)); - const onMouseMove = disposables.add(new DomEmitter(container, 'mousemove', true)); + const keyboardMarker = append(container, $('.screencast-keyboard')); + disposables.add(toDisposable(() => keyboardMarker.remove())); + + const onMouseDown = disposables.add(new Emitter()); + const onMouseUp = disposables.add(new Emitter()); + const onMouseMove = disposables.add(new Emitter()); + + function registerContainerListeners(container: HTMLElement, disposables: DisposableStore): void { + disposables.add(disposables.add(new DomEmitter(container, 'mousedown', true)).event(e => onMouseDown.fire(e))); + disposables.add(disposables.add(new DomEmitter(container, 'mouseup', true)).event(e => onMouseUp.fire(e))); + disposables.add(disposables.add(new DomEmitter(container, 'mousemove', true)).event(e => onMouseMove.fire(e))); + } + + for (const { window, disposables } of getWindows()) { + registerContainerListeners(layoutService.getContainer(window), disposables); + } + + disposables.add(onDidRegisterWindow(({ window, disposables }) => registerContainerListeners(layoutService.getContainer(window), disposables))); + + disposables.add(layoutService.onDidChangeActiveContainer(() => { + layoutService.activeContainer.appendChild(mouseMarker); + layoutService.activeContainer.appendChild(keyboardMarker); + })); const updateMouseIndicatorColor = () => { mouseMarker.style.borderColor = Color.fromHex(configurationService.getValue('screencastMode.mouseIndicatorColor')).toString(); @@ -172,9 +193,6 @@ class ToggleScreencastModeAction extends Action2 { }); })); - const keyboardMarker = append(container, $('.screencast-keyboard')); - disposables.add(toDisposable(() => keyboardMarker.remove())); - const updateKeyboardFontSize = () => { keyboardMarker.style.fontSize = `${clamp(configurationService.getValue('screencastMode.fontSize') || 56, 20, 100)}px`; }; @@ -214,10 +232,23 @@ class ToggleScreencastModeAction extends Action2 { } })); - const onKeyDown = disposables.add(new DomEmitter(window, 'keydown', true)); - const onCompositionStart = disposables.add(new DomEmitter(window, 'compositionstart', true)); - const onCompositionUpdate = disposables.add(new DomEmitter(window, 'compositionupdate', true)); - const onCompositionEnd = disposables.add(new DomEmitter(window, 'compositionend', true)); + const onKeyDown = disposables.add(new Emitter()); + const onCompositionStart = disposables.add(new Emitter()); + const onCompositionUpdate = disposables.add(new Emitter()); + const onCompositionEnd = disposables.add(new Emitter()); + + function registerWindowListeners(window: Window, disposables: DisposableStore): void { + disposables.add(disposables.add(new DomEmitter(window, 'keydown', true)).event(e => onKeyDown.fire(e))); + disposables.add(disposables.add(new DomEmitter(window, 'compositionstart', true)).event(e => onCompositionStart.fire(e))); + disposables.add(disposables.add(new DomEmitter(window, 'compositionupdate', true)).event(e => onCompositionUpdate.fire(e))); + disposables.add(disposables.add(new DomEmitter(window, 'compositionend', true)).event(e => onCompositionEnd.fire(e))); + } + + for (const { window, disposables } of getWindows()) { + registerWindowListeners(window, disposables); + } + + disposables.add(onDidRegisterWindow(({ window, disposables }) => registerWindowListeners(window, disposables))); let length = 0; let composing: Element | undefined = undefined; diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 19553fffaa9..e0b999dc53d 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -52,7 +52,7 @@ import { IAuxiliaryWindowService, isAuxiliaryWindow } from 'vs/workbench/service //#region Layout Implementation interface ILayoutRuntimeState { - activeContainer: 'main' | number /* window ID */; + activeContainerId: 'main' | number /* window ID */; fullscreen: boolean; maximized: boolean; hasFocus: boolean; @@ -170,7 +170,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi get activeContainer() { return this.getContainerFromDocument(getActiveDocument()); } get containers(): Iterable { const containers: HTMLElement[] = []; - for (const window of getWindows()) { + for (const { window } of getWindows()) { containers.push(this.getContainerFromDocument(window.document)); } @@ -453,8 +453,8 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi private onWindowFocusChanged(hasFocus: boolean): void { if (hasFocus) { const activeContainerId = this.getActiveContainerId(); - if (this.state.runtime.activeContainer !== activeContainerId) { - this.state.runtime.activeContainer = activeContainerId; + if (this.state.runtime.activeContainerId !== activeContainerId) { + this.state.runtime.activeContainerId = activeContainerId; this._onDidChangeActiveContainer.fire(); } } @@ -615,7 +615,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi // Layout Runtime State const layoutRuntimeState: ILayoutRuntimeState = { - activeContainer: this.getActiveContainerId(), + activeContainerId: this.getActiveContainerId(), fullscreen: isFullscreen(), hasFocus: this.hostService.hasFocus, maximized: false, @@ -1125,12 +1125,19 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } } - getContainer(part: Parts): HTMLElement | undefined { - if (!this.parts.get(part)) { - return undefined; + getContainer(window: Window): HTMLElement; + getContainer(part: Parts): HTMLElement | undefined; + getContainer(windowOrPart: Window | Parts): HTMLElement | undefined { + if (typeof windowOrPart === 'string') { + const part = windowOrPart; + if (!this.parts.get(part)) { + return undefined; + } + + return this.getPart(part).getContainer(); } - return this.getPart(part).getContainer(); + return this.getContainerFromDocument(windowOrPart.document); } isVisible(part: Parts): boolean { diff --git a/src/vs/workbench/electron-sandbox/actions/windowActions.ts b/src/vs/workbench/electron-sandbox/actions/windowActions.ts index 2e1902cd787..71208a301b0 100644 --- a/src/vs/workbench/electron-sandbox/actions/windowActions.ts +++ b/src/vs/workbench/electron-sandbox/actions/windowActions.ts @@ -250,7 +250,7 @@ abstract class BaseSwitchWindow extends Action2 { for (const window of mainWindows) { const auxiliaryWindows = mapMainWindowToAuxiliaryWindows.get(window.id); if (mapMainWindowToAuxiliaryWindows.size > 0) { - picks.push({ type: 'separator', payload: -1, label: auxiliaryWindows ? localize('windowGroup', "Window Group") : undefined } as unknown as IWindowPickItem); + picks.push({ type: 'separator', payload: -1, label: auxiliaryWindows ? localize('windowGroup', "window group") : undefined } as unknown as IWindowPickItem); } const resource = window.filename ? URI.file(window.filename) : isSingleFolderWorkspaceIdentifier(window.workspace) ? window.workspace.uri : isWorkspaceIdentifier(window.workspace) ? window.workspace.configPath : undefined; diff --git a/src/vs/workbench/services/host/electron-sandbox/nativeHostService.ts b/src/vs/workbench/services/host/electron-sandbox/nativeHostService.ts index d33a776105a..a3b261ccaa1 100644 --- a/src/vs/workbench/services/host/electron-sandbox/nativeHostService.ts +++ b/src/vs/workbench/services/host/electron-sandbox/nativeHostService.ts @@ -59,6 +59,14 @@ class WorkbenchHostService extends Disposable implements IHostService { disposables.add(focusTracker.onDidFocus(() => emitter.fire(this.hasFocus))); disposables.add(focusTracker.onDidBlur(() => emitter.fire(this.hasFocus))); disposables.add(onVisibilityChange.event(() => emitter.fire(this.hasFocus))); + + // Workaround: the window does not immediately seem to have focus when + // opening, so we schedule a check for focus on the next animation frame + window.requestAnimationFrame(() => { + if (window.document.hasFocus()) { + emitter.fire(true); + } + }); })); return emitter.event; diff --git a/src/vs/workbench/services/layout/browser/layoutService.ts b/src/vs/workbench/services/layout/browser/layoutService.ts index 7c44a06d34a..541432be739 100644 --- a/src/vs/workbench/services/layout/browser/layoutService.ts +++ b/src/vs/workbench/services/layout/browser/layoutService.ts @@ -175,6 +175,7 @@ export interface IWorkbenchLayoutService extends ILayoutService { /** * Returns the parts HTML element, if there is one. */ + getContainer(window: Window): HTMLElement; getContainer(part: Parts): HTMLElement | undefined; /** diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index 527aac54ce5..383ebaa90ae 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -623,7 +623,7 @@ export class TestLayoutService implements IWorkbenchLayoutService { getWindowBorderRadius(): string | undefined { return undefined; } isVisible(_part: Parts): boolean { return true; } getDimension(_part: Parts): Dimension { return new Dimension(0, 0); } - getContainer(_part: Parts): HTMLElement { return null!; } + getContainer(): HTMLElement { return null!; } isTitleBarHidden(): boolean { return false; } isStatusBarHidden(): boolean { return false; } isActivityBarHidden(): boolean { return false; } From 7df50fe380bb35a2043073b10bbe7d8957b6f6da Mon Sep 17 00:00:00 2001 From: Benjamin Simmonds <44439583+benibenj@users.noreply.github.com> Date: Tue, 31 Oct 2023 09:53:15 +0100 Subject: [PATCH 15/43] Scoped context keys for editor part (#197010) * scoped context key service for editor part * Refactor editor part context keys * Remove conext keys changes for none maximize * safer access to potential `undefined` context keys * Fix MockScopableContextKeyService --------- Co-authored-by: Benjamin Pasero --- .../test/common/mockKeybindingService.ts | 2 +- src/vs/workbench/browser/contextkeys.ts | 6 +- .../parts/editor/editor.contribution.ts | 8 +-- .../browser/parts/editor/editorActions.ts | 12 ++-- .../browser/parts/editor/editorPart.ts | 63 ++++++++++++++++--- src/vs/workbench/common/contextkeys.ts | 6 +- 6 files changed, 70 insertions(+), 27 deletions(-) diff --git a/src/vs/platform/keybinding/test/common/mockKeybindingService.ts b/src/vs/platform/keybinding/test/common/mockKeybindingService.ts index 9e47434c125..c655b9f9b7a 100644 --- a/src/vs/platform/keybinding/test/common/mockKeybindingService.ts +++ b/src/vs/platform/keybinding/test/common/mockKeybindingService.ts @@ -79,7 +79,7 @@ export class MockScopableContextKeyService extends MockContextKeyService { * Don't implement this for all tests since we rarely depend on this behavior and it isn't implemented fully */ public override createScoped(domNote: HTMLElement): IScopedContextKeyService { - return new MockContextKeyService(); + return new MockScopableContextKeyService(); } } diff --git a/src/vs/workbench/browser/contextkeys.ts b/src/vs/workbench/browser/contextkeys.ts index 174a0ac7a8c..94284bed195 100644 --- a/src/vs/workbench/browser/contextkeys.ts +++ b/src/vs/workbench/browser/contextkeys.ts @@ -7,7 +7,7 @@ import { Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { IContextKeyService, IContextKey, setConstant as setConstantContextKey } from 'vs/platform/contextkey/common/contextkey'; import { InputFocusedContext, IsMacContext, IsLinuxContext, IsWindowsContext, IsWebContext, IsMacNativeContext, IsDevelopmentContext, IsIOSContext, ProductQualityContext, IsMobileContext } from 'vs/platform/contextkey/common/contextkeys'; -import { SplitEditorsVertically, InEditorZenModeContext, ActiveEditorCanRevertContext, ActiveEditorGroupLockedContext, ActiveEditorCanSplitInGroupContext, SideBySideEditorActiveContext, AuxiliaryBarVisibleContext, SideBarVisibleContext, PanelAlignmentContext, PanelMaximizedContext, PanelVisibleContext, ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, EmbedderIdentifierContext, EditorTabsVisibleContext, IsCenteredLayoutContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorReadonlyContext, EditorAreaVisibleContext, ActiveEditorAvailableEditorIdsContext, DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, IsFullscreenContext, OpenFolderWorkspaceSupportContext, RemoteNameContext, VirtualWorkspaceContext, WorkbenchStateContext, WorkspaceFolderCountContext, PanelPositionContext, TemporaryWorkspaceContext, ActiveEditorCanToggleReadonlyContext, applyAvailableEditorIds, MaximizedEditorGroupContext, TitleBarVisibleContext, TitleBarStyleContext } from 'vs/workbench/common/contextkeys'; +import { SplitEditorsVertically, InEditorZenModeContext, ActiveEditorCanRevertContext, ActiveEditorGroupLockedContext, ActiveEditorCanSplitInGroupContext, SideBySideEditorActiveContext, AuxiliaryBarVisibleContext, SideBarVisibleContext, PanelAlignmentContext, PanelMaximizedContext, PanelVisibleContext, ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, EmbedderIdentifierContext, EditorTabsVisibleContext, IsCenteredLayoutContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorReadonlyContext, EditorAreaVisibleContext, ActiveEditorAvailableEditorIdsContext, DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, IsFullscreenContext, OpenFolderWorkspaceSupportContext, RemoteNameContext, VirtualWorkspaceContext, WorkbenchStateContext, WorkspaceFolderCountContext, PanelPositionContext, TemporaryWorkspaceContext, ActiveEditorCanToggleReadonlyContext, applyAvailableEditorIds, TitleBarVisibleContext, TitleBarStyleContext, MultipleEditorGroupsContext } from 'vs/workbench/common/contextkeys'; import { TEXT_DIFF_EDITOR_ID, EditorInputCapabilities, SIDE_BY_SIDE_EDITOR_ID, EditorResourceAccessor, SideBySideEditor } from 'vs/workbench/common/editor'; import { trackFocus, addDisposableListener, EventType, onDidRegisterWindow } from 'vs/base/browser/dom'; import { preferredSideBySideGroupDirection, GroupDirection, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; @@ -45,7 +45,6 @@ export class WorkbenchContextKeysHandler extends Disposable { private activeEditorGroupLast: IContextKey; private activeEditorGroupLocked: IContextKey; private multipleEditorGroupsContext: IContextKey; - private maximizedEditorGroupContext: IContextKey; private editorsVisibleContext: IContextKey; @@ -139,7 +138,6 @@ export class WorkbenchContextKeysHandler extends Disposable { this.activeEditorGroupLast = ActiveEditorGroupLastContext.bindTo(this.contextKeyService); this.activeEditorGroupLocked = ActiveEditorGroupLockedContext.bindTo(this.contextKeyService); this.multipleEditorGroupsContext = MultipleEditorGroupsContext.bindTo(this.contextKeyService); - this.maximizedEditorGroupContext = MaximizedEditorGroupContext.bindTo(this.contextKeyService); // Working Copies this.dirtyWorkingCopiesContext = DirtyWorkingCopiesContext.bindTo(this.contextKeyService); @@ -238,8 +236,6 @@ export class WorkbenchContextKeysHandler extends Disposable { this._register(this.editorGroupService.onDidChangeActiveGroup(() => this.updateEditorGroupContextKeys())); this._register(this.editorGroupService.onDidChangeGroupLocked(() => this.updateEditorGroupContextKeys())); - this._register(this.editorGroupService.onDidChangeGroupMaximized((maximized) => this.maximizedEditorGroupContext.set(maximized))); - this._register(this.editorGroupService.onDidChangeEditorPartOptions(() => this.updateEditorAreaContextKeys())); this._register(Event.runAndSubscribe(onDidRegisterWindow, ({ window, disposables }) => disposables.add(addDisposableListener(window, EventType.FOCUS_IN, () => this.updateInputContextKeys(window.document), true)), { window, disposables: this._store })); diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index 959089c51ee..875a02c676f 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -9,8 +9,8 @@ import { IEditorPaneRegistry, EditorPaneDescriptor } from 'vs/workbench/browser/ import { IEditorFactoryRegistry, EditorExtensions } from 'vs/workbench/common/editor'; import { TextCompareEditorActiveContext, ActiveEditorPinnedContext, EditorGroupEditorsCountContext, ActiveEditorStickyContext, ActiveEditorAvailableEditorIdsContext, - MultipleEditorGroupsContext, ActiveEditorDirtyContext, ActiveEditorGroupLockedContext, ActiveEditorCanSplitInGroupContext, SideBySideEditorActiveContext, - EditorTabsVisibleContext, ActiveEditorLastInGroupContext, MaximizedEditorGroupContext + EditorPartMultipleEditorGroupsContext, ActiveEditorDirtyContext, ActiveEditorGroupLockedContext, ActiveEditorCanSplitInGroupContext, SideBySideEditorActiveContext, + EditorTabsVisibleContext, ActiveEditorLastInGroupContext, EditorPartMaximizedEditorGroupContext, MultipleEditorGroupsContext } from 'vs/workbench/common/contextkeys'; import { SideBySideEditorInput, SideBySideEditorInputSerializer } from 'vs/workbench/common/editor/sideBySideEditorInput'; import { TextResourceEditor } from 'vs/workbench/browser/parts/editor/textResourceEditor'; @@ -384,8 +384,8 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: SHOW_EDITORS_IN MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: CLOSE_EDITORS_IN_GROUP_COMMAND_ID, title: localize('closeAll', "Close All") }, group: '5_close', order: 10 }); MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: CLOSE_SAVED_EDITORS_COMMAND_ID, title: localize('closeAllSaved', "Close Saved") }, group: '5_close', order: 20 }); MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: TOGGLE_KEEP_EDITORS_COMMAND_ID, title: localize('togglePreviewMode', "Enable Preview Editors"), toggled: ContextKeyExpr.has('config.workbench.editor.enablePreview') }, group: '7_settings', order: 10 }); -MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: TOGGLE_MAXIMIZE_EDITOR_GROUP, title: localize('maximizeGroup', "Maximize Group") }, group: '8_group_operations', order: 5, when: ContextKeyExpr.and(MaximizedEditorGroupContext.negate(), MultipleEditorGroupsContext) }); -MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: TOGGLE_MAXIMIZE_EDITOR_GROUP, title: localize('unmaximizeGroup', "Unmaximize Group") }, group: '8_group_operations', order: 5, when: MaximizedEditorGroupContext }); +MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: TOGGLE_MAXIMIZE_EDITOR_GROUP, title: localize('maximizeGroup', "Maximize Group") }, group: '8_group_operations', order: 5, when: ContextKeyExpr.and(EditorPartMaximizedEditorGroupContext.negate(), EditorPartMultipleEditorGroupsContext) }); +MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: TOGGLE_MAXIMIZE_EDITOR_GROUP, title: localize('unmaximizeGroup', "Unmaximize Group") }, group: '8_group_operations', order: 5, when: EditorPartMaximizedEditorGroupContext }); MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: TOGGLE_LOCK_GROUP_COMMAND_ID, title: localize('lockGroup', "Lock Group"), toggled: ActiveEditorGroupLockedContext }, group: '8_group_operations', order: 10, when: MultipleEditorGroupsContext }); function appendEditorToolItem(primary: ICommandAction, when: ContextKeyExpression | undefined, order: number, alternative?: ICommandAction, precondition?: ContextKeyExpression | undefined): void { diff --git a/src/vs/workbench/browser/parts/editor/editorActions.ts b/src/vs/workbench/browser/parts/editor/editorActions.ts index f0341acd5b5..846821dd7ca 100644 --- a/src/vs/workbench/browser/parts/editor/editorActions.ts +++ b/src/vs/workbench/browser/parts/editor/editorActions.ts @@ -33,7 +33,7 @@ import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ILogService } from 'vs/platform/log/common/log'; import { Categories } from 'vs/platform/action/common/actionCommonCategories'; -import { ActiveEditorAvailableEditorIdsContext, ActiveEditorContext, ActiveEditorGroupEmptyContext, MaximizedEditorGroupContext, MultipleEditorGroupsContext } from 'vs/workbench/common/contextkeys'; +import { ActiveEditorAvailableEditorIdsContext, ActiveEditorContext, ActiveEditorGroupEmptyContext, EditorPartMaximizedEditorGroupContext, EditorPartMultipleEditorGroupsContext } from 'vs/workbench/common/contextkeys'; import { URI } from 'vs/base/common/uri'; import { getActiveDocument } from 'vs/base/browser/dom'; @@ -1073,7 +1073,7 @@ export class MaximizeGroupHideSidebarAction extends Action2 { title: { value: localize('maximizeEditorHideSidebar', "Maximize Editor Group and Hide Side Bars"), original: 'Maximize Editor Group and Hide Side Bars' }, f1: true, category: Categories.View, - precondition: ContextKeyExpr.and(MaximizedEditorGroupContext.negate(), MultipleEditorGroupsContext) + precondition: ContextKeyExpr.and(EditorPartMaximizedEditorGroupContext.negate(), EditorPartMultipleEditorGroupsContext) }); } @@ -1098,7 +1098,7 @@ export class ToggleMaximizeEditorGroupAction extends Action2 { title: { value: localize('toggleMaximizeEditorGroup', "Toggle Maximize Editor Group"), original: 'Toggle Maximize Editor Group' }, f1: true, category: Categories.View, - precondition: ContextKeyExpr.or(MultipleEditorGroupsContext, MaximizedEditorGroupContext), + precondition: ContextKeyExpr.or(EditorPartMultipleEditorGroupsContext, EditorPartMaximizedEditorGroupContext), keybinding: { weight: KeybindingWeight.WorkbenchContrib, primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyCode.KeyM), @@ -1107,16 +1107,16 @@ export class ToggleMaximizeEditorGroupAction extends Action2 { id: MenuId.EditorTitle, order: -10000, // towards the front group: 'navigation', - when: MaximizedEditorGroupContext + when: EditorPartMaximizedEditorGroupContext }, { id: MenuId.EmptyEditorGroup, order: -10000, // towards the front group: 'navigation', - when: MaximizedEditorGroupContext + when: EditorPartMaximizedEditorGroupContext }], icon: Codicon.screenFull, - toggled: MaximizedEditorGroupContext, + toggled: EditorPartMaximizedEditorGroupContext, }); } diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index 6cffd8296b4..1a9679abf56 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -32,6 +32,9 @@ import { findGroup } from 'vs/workbench/services/editor/common/editorGroupFinder import { SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { IBoundarySashes } from 'vs/base/browser/ui/sash/sash'; import { IHostService } from 'vs/workbench/services/host/browser/host'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { EditorPartMaximizedEditorGroupContext, EditorPartMultipleEditorGroupsContext } from 'vs/workbench/common/contextkeys'; interface IEditorPartUIState { readonly serializedGrid: ISerializedGrid; @@ -140,6 +143,8 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupsView { private container: HTMLElement | undefined; + private scopedInstantiationService!: IInstantiationService; + private centeredLayoutWidget!: CenteredViewLayout; private gridWidget!: SerializableGrid; @@ -156,7 +161,8 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupsView { @IConfigurationService private readonly configurationService: IConfigurationService, @IStorageService storageService: IStorageService, @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService, - @IHostService private readonly hostService: IHostService + @IHostService private readonly hostService: IHostService, + @IContextKeyService private readonly contextKeyService: IContextKeyService ) { super(id, { hasTitle: false }, themeService, storageService, layoutService); @@ -214,7 +220,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupsView { readonly sideGroup: IEditorSideGroup = { openEditor: (editor, options) => { - const [group] = this.instantiationService.invokeFunction(accessor => findGroup(accessor, { editor, options }, SIDE_GROUP)); + const [group] = this.scopedInstantiationService.invokeFunction(accessor => findGroup(accessor, { editor, options }, SIDE_GROUP)); return group.openEditor(editor, options); } @@ -624,11 +630,11 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupsView { // Create group view let groupView: IEditorGroupView; if (from instanceof EditorGroupView) { - groupView = EditorGroupView.createCopy(from, this.editorPartsView, this, this.groupsLabel, this.count, this.instantiationService); + groupView = EditorGroupView.createCopy(from, this.editorPartsView, this, this.groupsLabel, this.count, this.scopedInstantiationService,); } else if (isSerializedEditorGroupModel(from)) { - groupView = EditorGroupView.createFromSerialized(from, this.editorPartsView, this, this.groupsLabel, this.count, this.instantiationService); + groupView = EditorGroupView.createFromSerialized(from, this.editorPartsView, this, this.groupsLabel, this.count, this.scopedInstantiationService); } else { - groupView = EditorGroupView.createNew(this.editorPartsView, this, this.groupsLabel, this.count, this.instantiationService); + groupView = EditorGroupView.createNew(this.editorPartsView, this, this.groupsLabel, this.count, this.scopedInstantiationService); } // Keep in map @@ -930,7 +936,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupsView { createEditorDropTarget(container: unknown, delegate: IEditorDropTargetDelegate): IDisposable { assertType(container instanceof HTMLElement); - return this.instantiationService.createInstance(EditorDropTarget, container, delegate); + return this.scopedInstantiationService.createInstance(EditorDropTarget, container, delegate); } //#region Part @@ -967,6 +973,12 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupsView { this.container.classList.add('content'); parent.appendChild(this.container); + // Scoped instantiation service + const scopedContextKeyService = this._register(this.contextKeyService.createScoped(this.container)); + this.scopedInstantiationService = this.instantiationService.createChild(new ServiceCollection( + [IContextKeyService, scopedContextKeyService] + )); + // Grid control this.doCreateGridControl(options); @@ -977,6 +989,9 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupsView { // Drag & Drop support this.setupDragAndDropSupport(parent, this.container); + // Context keys + this.handleContextKeys(scopedContextKeyService); + // Signal ready this.whenReadyPromise.complete(); this._isReady = true; @@ -989,6 +1004,32 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupsView { return this.container; } + private handleContextKeys(contextKeyService: IContextKeyService): void { + const multipleEditorGroupsContext = EditorPartMultipleEditorGroupsContext.bindTo(contextKeyService); + const maximizedEditorGroupContext = EditorPartMaximizedEditorGroupContext.bindTo(contextKeyService); + + const updateContextKeys = () => { + const groupCount = this.count; + if (groupCount > 1) { + multipleEditorGroupsContext.set(true); + } else { + multipleEditorGroupsContext.reset(); + } + + if (this.hasMaximizedGroup()) { + maximizedEditorGroupContext.set(true); + } else { + maximizedEditorGroupContext.reset(); + } + }; + + updateContextKeys(); + + this._register(this.onDidAddGroup(() => updateContextKeys())); + this._register(this.onDidRemoveGroup(() => updateContextKeys())); + this._register(this.onDidChangeGroupMaximized(() => updateContextKeys())); + } + private setupDragAndDropSupport(parent: HTMLElement, container: HTMLElement): void { // Editor drop target @@ -1335,9 +1376,10 @@ export class MainEditorPart extends EditorPart { @IConfigurationService configurationService: IConfigurationService, @IStorageService storageService: IStorageService, @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService, - @IHostService hostService: IHostService + @IHostService hostService: IHostService, + @IContextKeyService contextKeyService: IContextKeyService ) { - super(editorPartsView, Parts.EDITOR_PART, '', false, instantiationService, themeService, configurationService, storageService, layoutService, hostService); + super(editorPartsView, Parts.EDITOR_PART, '', false, instantiationService, themeService, configurationService, storageService, layoutService, hostService, contextKeyService); } } @@ -1356,10 +1398,11 @@ export class AuxiliaryEditorPart extends EditorPart implements IAuxiliaryEditorP @IConfigurationService configurationService: IConfigurationService, @IStorageService storageService: IStorageService, @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService, - @IHostService hostService: IHostService + @IHostService hostService: IHostService, + @IContextKeyService contextKeyService: IContextKeyService ) { const id = AuxiliaryEditorPart.COUNTER++; - super(editorPartsView, `workbench.parts.auxiliaryEditor.${id}`, groupsLabel, true, instantiationService, themeService, configurationService, storageService, layoutService, hostService); + super(editorPartsView, `workbench.parts.auxiliaryEditor.${id}`, groupsLabel, true, instantiationService, themeService, configurationService, storageService, layoutService, hostService, contextKeyService); } protected override saveState(): void { diff --git a/src/vs/workbench/common/contextkeys.ts b/src/vs/workbench/common/contextkeys.ts index d58629b85f7..d6844289a43 100644 --- a/src/vs/workbench/common/contextkeys.ts +++ b/src/vs/workbench/common/contextkeys.ts @@ -71,6 +71,11 @@ export const ActiveEditorGroupLockedContext = new RawContextKey('active export const MultipleEditorGroupsContext = new RawContextKey('multipleEditorGroups', false, localize('multipleEditorGroups', "Whether there are multiple editor groups opened")); export const SingleEditorGroupsContext = MultipleEditorGroupsContext.toNegated(); +// Editor Part Context Keys +export const EditorPartMultipleEditorGroupsContext = new RawContextKey('editorPartMultipleEditorGroups', false, localize('editorPartMultipleEditorGroups', "Whether there are multiple editor groups opened in an editor part")); +export const EditorPartSingleEditorGroupsContext = EditorPartMultipleEditorGroupsContext.toNegated(); +export const EditorPartMaximizedEditorGroupContext = new RawContextKey('editorPartMaximizedEditorGroup', false, localize('editorPartEditorGroupMaximized', "Editor Part has a maximized group")); + // Editor Layout Context Keys export const EditorsVisibleContext = new RawContextKey('editorIsOpen', false, localize('editorIsOpen', "Whether an editor is open")); export const InEditorZenModeContext = new RawContextKey('inZenMode', false, localize('inZenMode', "Whether Zen mode is enabled")); @@ -79,7 +84,6 @@ export const SplitEditorsVertically = new RawContextKey('splitEditorsVe export const EditorAreaVisibleContext = new RawContextKey('editorAreaVisible', true, localize('editorAreaVisible', "Whether the editor area is visible")); export const EditorTabsVisibleContext = new RawContextKey('editorTabsVisible', true, localize('editorTabsVisible', "Whether editor tabs are visible")); export const EditorPinnedAndUnpinnedTabsContext = new RawContextKey('editorPinnedAndUnpinnedTabsVisible', false, true); -export const MaximizedEditorGroupContext = new RawContextKey('maximizedEditorGroup', false, localize('editorGroupMaximized', "Editor group is maximized")); //#endregion From 491c1f17c0d23e6c2d9bbafa8c1ce3adb1db443e Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 31 Oct 2023 10:32:12 +0100 Subject: [PATCH 16/43] Fix drag handle coordinates in debugToolBar --- src/vs/workbench/contrib/debug/browser/debugToolBar.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugToolBar.ts b/src/vs/workbench/contrib/debug/browser/debugToolBar.ts index f19c54d885e..8822f3b8e1f 100644 --- a/src/vs/workbench/contrib/debug/browser/debugToolBar.ts +++ b/src/vs/workbench/contrib/debug/browser/debugToolBar.ts @@ -169,7 +169,7 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution { // Prevent default to stop editor selecting text #8524 mouseMoveEvent.preventDefault(); // Reduce x by width of drag handle to reduce jarring #16604 - this.setCoordinates(mouseMoveEvent.posx - 14, mouseMoveEvent.posy); + this.setCoordinates(mouseMoveEvent.posx - 14, mouseMoveEvent.posy - 14); }); const mouseUpListener = dom.addDisposableGenericMouseUpListener(window, (e: MouseEvent) => { From 519c8072f38520095d1636fa9cc1d70b18287002 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 31 Oct 2023 11:23:50 +0100 Subject: [PATCH 17/43] poll to wait for workbench to be restored (#197059) --- test/automation/src/application.ts | 4 +--- test/automation/src/code.ts | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/test/automation/src/application.ts b/test/automation/src/application.ts index f969e5b0834..e06eaaf5be4 100644 --- a/test/automation/src/application.ts +++ b/test/automation/src/application.ts @@ -119,9 +119,7 @@ export class Application { // We need a rendered workbench await measureAndLog(() => code.didFinishLoad(), 'Application#checkWindowReady: wait for navigation to be committed', this.logger); await measureAndLog(() => code.waitForElement('.monaco-workbench'), 'Application#checkWindowReady: wait for .monaco-workbench element', this.logger); - - // Wait for workbench to be restored - await this.code.whenWorkbenchRestored(); + await measureAndLog(() => code.whenWorkbenchRestored(), 'Application#checkWorkbenchRestored', this.logger); // Remote but not web: wait for a remote connection state change if (this.remote) { diff --git a/test/automation/src/code.ts b/test/automation/src/code.ts index 48cf1be0c53..5cbe5649d07 100644 --- a/test/automation/src/code.ts +++ b/test/automation/src/code.ts @@ -243,7 +243,7 @@ export class Code { } whenWorkbenchRestored(): Promise { - return this.driver.whenWorkbenchRestored(); + return this.poll(() => this.driver.whenWorkbenchRestored(), () => true, `when workbench restored`); } getLocaleInfo(): Promise { From 8a2bfc6384fcfd53ae2451af1340094edb72b8e1 Mon Sep 17 00:00:00 2001 From: Benjamin Simmonds <44439583+benibenj@users.noreply.github.com> Date: Tue, 31 Oct 2023 12:12:29 +0100 Subject: [PATCH 18/43] Allow breadcrumbs with no tab bar (#197061) --- .../parts/editor/editorTitleControl.ts | 4 ++-- .../parts/editor/noEditorTabsControl.ts | 20 ++++++++++++++++++- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorTitleControl.ts b/src/vs/workbench/browser/parts/editor/editorTitleControl.ts index 6330c3e814a..3f14abf5b9e 100644 --- a/src/vs/workbench/browser/parts/editor/editorTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/editorTitleControl.ts @@ -77,7 +77,7 @@ export class EditorTitleControl extends Themable { } private createBreadcrumbsControl(): BreadcrumbsControlFactory | undefined { - if (this.groupsView.partOptions.showTabs !== 'multiple') { + if (this.groupsView.partOptions.showTabs === 'single') { return undefined; // Single tabs have breadcrumbs inlined. No tabs have no breadcrumbs. } @@ -175,7 +175,7 @@ export class EditorTitleControl extends Themable { // Update editor tabs control if options changed if ( oldOptions.showTabs !== newOptions.showTabs || - (newOptions.showTabs === 'multiple' && oldOptions.pinnedTabsOnSeparateRow !== newOptions.pinnedTabsOnSeparateRow) + (newOptions.showTabs !== 'single' && oldOptions.pinnedTabsOnSeparateRow !== newOptions.pinnedTabsOnSeparateRow) ) { // Clear old this.editorTabsControlDisposable.clear(); diff --git a/src/vs/workbench/browser/parts/editor/noEditorTabsControl.ts b/src/vs/workbench/browser/parts/editor/noEditorTabsControl.ts index f265b196212..384365bd5c4 100644 --- a/src/vs/workbench/browser/parts/editor/noEditorTabsControl.ts +++ b/src/vs/workbench/browser/parts/editor/noEditorTabsControl.ts @@ -10,6 +10,7 @@ import { Dimension } from 'vs/base/browser/dom'; import { IEditorTitleControlDimensions } from 'vs/workbench/browser/parts/editor/editorTitleControl'; export class NoEditorTabsControl extends EditorTabsControl { + private activeEditor: EditorInput | null = null; protected prepareEditorActions(editorActions: IToolbarActions): IToolbarActions { return { @@ -19,10 +20,27 @@ export class NoEditorTabsControl extends EditorTabsControl { } openEditor(editor: EditorInput): boolean { - return false; + return this.handleOpenedEditors(); } openEditors(editors: EditorInput[]): boolean { + return this.handleOpenedEditors(); + } + + private handleOpenedEditors(): boolean { + const didChange = this.activeEditorChanged(); + this.activeEditor = this.tabsModel.activeEditor; + return didChange; + } + + private activeEditorChanged(): boolean { + if ( + !this.activeEditor && this.tabsModel.activeEditor || // active editor changed from null => editor + this.activeEditor && !this.tabsModel.activeEditor || // active editor changed from editor => null + (!this.activeEditor || !this.tabsModel.isActive(this.activeEditor)) // active editor changed from editorA => editorB + ) { + return true; + } return false; } From f8f9f06401694978abb27e3fbcb4b1371b18c9ad Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 31 Oct 2023 13:43:02 +0100 Subject: [PATCH 19/43] whenWorkbenchRestored was not implemented in the driver in previous releases (#197068) --- test/automation/src/code.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/test/automation/src/code.ts b/test/automation/src/code.ts index 5cbe5649d07..ce4f14aa889 100644 --- a/test/automation/src/code.ts +++ b/test/automation/src/code.ts @@ -242,8 +242,14 @@ export class Code { await this.poll(() => this.driver.writeInTerminal(selector, value), () => true, `writeInTerminal '${selector}'`); } - whenWorkbenchRestored(): Promise { - return this.poll(() => this.driver.whenWorkbenchRestored(), () => true, `when workbench restored`); + async whenWorkbenchRestored(): Promise { + try { + await this.poll(() => this.driver.whenWorkbenchRestored(), () => true, `when workbench restored`); + } catch (error) { + // TODO: @sandy081 Remove this when 1.84.0 is out + // whenWorkbenchRestored was not implemented in the driver before 1.84.0 + this.logger.log('whenWorkbenchRestored() timed out'); + } } getLocaleInfo(): Promise { From 9ed940067190600827438d6a6f70ceef51a89dfa Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 31 Oct 2023 06:28:38 -0700 Subject: [PATCH 20/43] Set font for terminal dev mode --- .../browser/terminal.developer.contribution.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/developer/browser/terminal.developer.contribution.ts b/src/vs/workbench/contrib/terminalContrib/developer/browser/terminal.developer.contribution.ts index 4d828a105ce..58699fc7b6d 100644 --- a/src/vs/workbench/contrib/terminalContrib/developer/browser/terminal.developer.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/developer/browser/terminal.developer.contribution.ts @@ -15,7 +15,7 @@ import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { ITerminalLogService, TerminalSettingId } from 'vs/platform/terminal/common/terminal'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { IInternalXtermTerminal, ITerminalContribution, ITerminalInstance, IXtermTerminal } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { IInternalXtermTerminal, ITerminalContribution, ITerminalInstance, ITerminalService, IXtermTerminal } from 'vs/workbench/contrib/terminal/browser/terminal'; import { registerTerminalAction } from 'vs/workbench/contrib/terminal/browser/terminalActions'; import { registerTerminalContribution } from 'vs/workbench/contrib/terminal/browser/terminalExtensions'; import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/widgets/widgetManager'; @@ -118,7 +118,8 @@ class DevModeContribution extends DisposableStore implements ITerminalContributi instance: ITerminalInstance, processManager: ITerminalProcessManager, widgetManager: TerminalWidgetManager, - @IConfigurationService private readonly _configurationService: IConfigurationService) { + @IConfigurationService private readonly _configurationService: IConfigurationService, + @ITerminalService private readonly _terminalService: ITerminalService) { super(); this.add(this._configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration(TerminalSettingId.DevMode)) { @@ -134,6 +135,11 @@ class DevModeContribution extends DisposableStore implements ITerminalContributi private _updateDevMode() { const devMode: boolean = this._configurationService.getValue(TerminalSettingId.DevMode) || false; this._xterm?.raw.element?.classList.toggle('dev-mode', devMode); + if (this._xterm?.raw.textarea) { + const font = this._terminalService.configHelper.getFont(); + this._xterm.raw.textarea.style.fontFamily = font.fontFamily; + this._xterm.raw.textarea.style.fontSize = `${font.fontSize}px`; + } } } registerTerminalContribution(DevModeContribution.ID, DevModeContribution); From c953142b5b24a9e18ea4fe8973805ec0650e7613 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 31 Oct 2023 07:23:10 -0700 Subject: [PATCH 21/43] Remove simple find width history placeholder The history hint causes it to double up. Part of #197071 --- .../contrib/codeEditor/browser/find/simpleFindWidget.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.ts b/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.ts index 2659700d67d..98342adaf6d 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.ts @@ -27,7 +27,7 @@ import { ISashEvent, IVerticalSashLayoutProvider, Orientation, Sash } from 'vs/b import { registerColor } from 'vs/platform/theme/common/colorRegistry'; const NLS_FIND_INPUT_LABEL = nls.localize('label.find', "Find"); -const NLS_FIND_INPUT_PLACEHOLDER = nls.localize('placeholder.find', "Find (\u21C5 for history)"); +const NLS_FIND_INPUT_PLACEHOLDER = nls.localize('placeholder.find', "Find"); const NLS_PREVIOUS_MATCH_BTN_LABEL = nls.localize('label.previousMatchButton', "Previous Match"); const NLS_NEXT_MATCH_BTN_LABEL = nls.localize('label.nextMatchButton', "Next Match"); const NLS_CLOSE_BTN_LABEL = nls.localize('label.closeButton', "Close"); From 6b5f84506687a3022f592feafaf7d3df10e67728 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 31 Oct 2023 07:23:46 -0700 Subject: [PATCH 22/43] Improve translatable strings in history hint Fixes #197071 --- src/vs/base/browser/ui/inputbox/inputBox.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/vs/base/browser/ui/inputbox/inputBox.ts b/src/vs/base/browser/ui/inputbox/inputBox.ts index da375f17187..e6c8f4987be 100644 --- a/src/vs/base/browser/ui/inputbox/inputBox.ts +++ b/src/vs/base/browser/ui/inputbox/inputBox.ts @@ -617,16 +617,22 @@ export class HistoryInputBox extends InputBox implements IHistoryNavigationWidge readonly onDidBlur = this._onDidBlur.event; constructor(container: HTMLElement, contextViewProvider: IContextViewProvider | undefined, options: IHistoryInputOptions) { - const NLS_PLACEHOLDER_HISTORY_HINT = nls.localize({ key: 'history.inputbox.hint', comment: ['Text will be prefixed with \u21C5 plus a single space, then used as a hint where input field keeps history'] }, "for history"); - const NLS_PLACEHOLDER_HISTORY_HINT_SUFFIX = ` or \u21C5 ${NLS_PLACEHOLDER_HISTORY_HINT}`; - const NLS_PLACEHOLDER_HISTORY_HINT_SUFFIX_IN_PARENS = ` (\u21C5 ${NLS_PLACEHOLDER_HISTORY_HINT})`; + const NLS_PLACEHOLDER_HISTORY_HINT_SUFFIX_NO_PARENS = nls.localize({ + key: 'history.inputbox.hint.suffix.noparens', + comment: ['Text is the suffix of an input field placeholder coming after the action the input field performs, this will be used when the input field ends in a closing parenthesis ")", for example "Filter (e.g. text, !exclude)". The character inserted into the final string is \u21C5 to represent the up and down arrow keys.'] + }, ' or {0} for history', `\u21C5`); + const NLS_PLACEHOLDER_HISTORY_HINT_SUFFIX_IN_PARENS = nls.localize({ + key: 'history.inputbox.hint.suffix.inparens', + comment: ['Text is the suffix of an input field placeholder coming after the action the input field performs, this will be used when the input field does NOT end in a closing parenthesis (eg. "Find"). The character inserted into the final string is \u21C5 to represent the up and down arrow keys.'] + }, ' ({0} for history)', `\u21C5`); + super(container, contextViewProvider, options); this.history = new HistoryNavigator(options.history, 100); // Function to append the history suffix to the placeholder if necessary const addSuffix = () => { - if (options.showHistoryHint && options.showHistoryHint() && !this.placeholder.endsWith(NLS_PLACEHOLDER_HISTORY_HINT_SUFFIX) && !this.placeholder.endsWith(NLS_PLACEHOLDER_HISTORY_HINT_SUFFIX_IN_PARENS) && this.history.getHistory().length) { - const suffix = this.placeholder.endsWith(')') ? NLS_PLACEHOLDER_HISTORY_HINT_SUFFIX : NLS_PLACEHOLDER_HISTORY_HINT_SUFFIX_IN_PARENS; + if (options.showHistoryHint && options.showHistoryHint() && !this.placeholder.endsWith(NLS_PLACEHOLDER_HISTORY_HINT_SUFFIX_NO_PARENS) && !this.placeholder.endsWith(NLS_PLACEHOLDER_HISTORY_HINT_SUFFIX_IN_PARENS) && this.history.getHistory().length) { + const suffix = this.placeholder.endsWith(')') ? NLS_PLACEHOLDER_HISTORY_HINT_SUFFIX_NO_PARENS : NLS_PLACEHOLDER_HISTORY_HINT_SUFFIX_IN_PARENS; const suffixedPlaceholder = this.placeholder + suffix; if (options.showPlaceholderOnFocus && !dom.isActiveElement(this.input)) { this.placeholder = suffixedPlaceholder; @@ -666,7 +672,7 @@ export class HistoryInputBox extends InputBox implements IHistoryNavigationWidge } }; if (!resetPlaceholder(NLS_PLACEHOLDER_HISTORY_HINT_SUFFIX_IN_PARENS)) { - resetPlaceholder(NLS_PLACEHOLDER_HISTORY_HINT_SUFFIX); + resetPlaceholder(NLS_PLACEHOLDER_HISTORY_HINT_SUFFIX_NO_PARENS); } }); } From 0a06579a7f1ff6be0a687d77c852b2666ccdfe44 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 31 Oct 2023 07:39:09 -0700 Subject: [PATCH 23/43] Update xterm.js Fixes #151181 --- package.json | 14 +++++------ remote/package.json | 14 +++++------ remote/web/package.json | 10 ++++---- remote/web/yarn.lock | 40 ++++++++++++++--------------- remote/yarn.lock | 56 ++++++++++++++++++++--------------------- yarn.lock | 56 ++++++++++++++++++++--------------------- 6 files changed, 95 insertions(+), 95 deletions(-) diff --git a/package.json b/package.json index d850ee9300e..4736c4dbca5 100644 --- a/package.json +++ b/package.json @@ -96,14 +96,14 @@ "vscode-oniguruma": "1.7.0", "vscode-regexpp": "^3.1.0", "vscode-textmate": "9.0.0", - "xterm": "5.4.0-beta.32", - "xterm-addon-canvas": "0.6.0-beta.32", + "xterm": "5.4.0-beta.37", + "xterm-addon-canvas": "0.6.0-beta.37", "xterm-addon-image": "0.6.0-beta.21", - "xterm-addon-search": "0.14.0-beta.31", - "xterm-addon-serialize": "0.12.0-beta.31", - "xterm-addon-unicode11": "0.7.0-beta.31", - "xterm-addon-webgl": "0.17.0-beta.31", - "xterm-headless": "5.4.0-beta.32", + "xterm-addon-search": "0.14.0-beta.36", + "xterm-addon-serialize": "0.12.0-beta.36", + "xterm-addon-unicode11": "0.7.0-beta.36", + "xterm-addon-webgl": "0.17.0-beta.36", + "xterm-headless": "5.4.0-beta.37", "yauzl": "^2.9.2", "yazl": "^2.4.3" }, diff --git a/remote/package.json b/remote/package.json index 8c2e24e4fac..50e021d504e 100644 --- a/remote/package.json +++ b/remote/package.json @@ -26,14 +26,14 @@ "vscode-oniguruma": "1.7.0", "vscode-regexpp": "^3.1.0", "vscode-textmate": "9.0.0", - "xterm": "5.4.0-beta.32", - "xterm-addon-canvas": "0.6.0-beta.32", + "xterm": "5.4.0-beta.37", + "xterm-addon-canvas": "0.6.0-beta.37", "xterm-addon-image": "0.6.0-beta.21", - "xterm-addon-search": "0.14.0-beta.31", - "xterm-addon-serialize": "0.12.0-beta.31", - "xterm-addon-unicode11": "0.7.0-beta.31", - "xterm-addon-webgl": "0.17.0-beta.31", - "xterm-headless": "5.4.0-beta.32", + "xterm-addon-search": "0.14.0-beta.36", + "xterm-addon-serialize": "0.12.0-beta.36", + "xterm-addon-unicode11": "0.7.0-beta.36", + "xterm-addon-webgl": "0.17.0-beta.36", + "xterm-headless": "5.4.0-beta.37", "yauzl": "^2.9.2", "yazl": "^2.4.3" } diff --git a/remote/web/package.json b/remote/web/package.json index 2634c6ff3b8..6570ba0ff0a 100644 --- a/remote/web/package.json +++ b/remote/web/package.json @@ -11,11 +11,11 @@ "tas-client-umd": "0.1.8", "vscode-oniguruma": "1.7.0", "vscode-textmate": "9.0.0", - "xterm": "5.4.0-beta.32", - "xterm-addon-canvas": "0.6.0-beta.32", + "xterm": "5.4.0-beta.37", + "xterm-addon-canvas": "0.6.0-beta.37", "xterm-addon-image": "0.6.0-beta.21", - "xterm-addon-search": "0.14.0-beta.31", - "xterm-addon-unicode11": "0.7.0-beta.31", - "xterm-addon-webgl": "0.17.0-beta.31" + "xterm-addon-search": "0.14.0-beta.36", + "xterm-addon-unicode11": "0.7.0-beta.36", + "xterm-addon-webgl": "0.17.0-beta.36" } } diff --git a/remote/web/yarn.lock b/remote/web/yarn.lock index 6eb7304ba80..4d7cf110e03 100644 --- a/remote/web/yarn.lock +++ b/remote/web/yarn.lock @@ -68,32 +68,32 @@ vscode-textmate@9.0.0: resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-9.0.0.tgz#313c6c8792b0507aef35aeb81b6b370b37c44d6c" integrity sha512-Cl65diFGxz7gpwbav10HqiY/eVYTO1sjQpmRmV991Bj7wAoOAjGQ97PpQcXorDE2Uc4hnGWLY17xme+5t6MlSg== -xterm-addon-canvas@0.6.0-beta.32: - version "0.6.0-beta.32" - resolved "https://registry.yarnpkg.com/xterm-addon-canvas/-/xterm-addon-canvas-0.6.0-beta.32.tgz#c9e74dd72fcc981a2e0cbd0b82827676bc5c74b9" - integrity sha512-Xw7oE4dbS+x+pu6cGW1bDSXcVviuorLz1OLaYw46jjmDezIqQIIEMhSMOprExFEWgeRQ9AEN4lPqw6aH87V74w== +xterm-addon-canvas@0.6.0-beta.37: + version "0.6.0-beta.37" + resolved "https://registry.yarnpkg.com/xterm-addon-canvas/-/xterm-addon-canvas-0.6.0-beta.37.tgz#4f7692fc87f3500a5a6e7f822ec74796e6a36db2" + integrity sha512-eFN7iPxqiQosUO+rbXKwPz/AR3TLJcLCVWjvT5EaewqNpw+E9rWVwRRPGqYSw+qkSKM/vcNlySIkq4hmpA/ohg== xterm-addon-image@0.6.0-beta.21: version "0.6.0-beta.21" resolved "https://registry.yarnpkg.com/xterm-addon-image/-/xterm-addon-image-0.6.0-beta.21.tgz#e3708bc504c56a23ff31f12a2eeb335331a92aac" integrity sha512-8/PTaXVPa4kQ0xzVeuZZk10OpbZBj2cgfwhM2B0ChSPvwrk0lX+ksnXdtDKH3tg+JYvo7fIhNXtkr4NwWt7VJQ== -xterm-addon-search@0.14.0-beta.31: - version "0.14.0-beta.31" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.14.0-beta.31.tgz#933ca5d2d642dacad29f2cfbd50830cff83bc274" - integrity sha512-JRY1ukhoh32D0AMz78xpumQkLgkcP9d3GXj6gzVHZZsjLAMDaJYEubYq1bUhM7IGHUyg+x0sdRJyx7d6fJpiQg== +xterm-addon-search@0.14.0-beta.36: + version "0.14.0-beta.36" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.14.0-beta.36.tgz#4bb161130c39090d506f73468ef65054aedcb9f0" + integrity sha512-2ydZV1Nt8d0vOjZ/y2RXiw3QLgabK+PCA7yETqZNivnmhSfcxtYPyI4aRfG/lc3hoMKiPppSSjJF0MlV/qo8ww== -xterm-addon-unicode11@0.7.0-beta.31: - version "0.7.0-beta.31" - resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.7.0-beta.31.tgz#abcba752172323f31312bd8a3f9b6a049dbca6e3" - integrity sha512-vvBKJbBoLbeIf2++6D16VnOOwevZE3nyO/PDZ7cyTJK1eYR73rr8ZbjUrH92YoTu4Z8MpZFepGQOgK/vlAQMwQ== +xterm-addon-unicode11@0.7.0-beta.36: + version "0.7.0-beta.36" + resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.7.0-beta.36.tgz#bde0b8c98211b74b0288cd5bad09107384b870ca" + integrity sha512-vV/f7dTP/46JjoJLYDCxolCWPoUn0cF7S5upYvY0ZA1VLQ9UnXmaNjzrg+9f+QBoXEQ2eQDLQcRFWOc6BAbF+A== -xterm-addon-webgl@0.17.0-beta.31: - version "0.17.0-beta.31" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.17.0-beta.31.tgz#3cd29b4858e3f4f6dd5a8dd969454e85e1f43baa" - integrity sha512-vYHj+HlTcqUlFFVuoCTjlgh89/lIoSkZ7Nc87cwSFTrJsl07qoKutmpupqFXyjhbEA1fQY2SuQLx08Gmf2jWkQ== +xterm-addon-webgl@0.17.0-beta.36: + version "0.17.0-beta.36" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.17.0-beta.36.tgz#6ed16a83cd4bed3cbddd631e7974375aa8f7e023" + integrity sha512-5PNQZcNH2UTaqSf7Exgh/fvsNeTOs+CSEuIaTafzKuMIrWH14+cRdyJdzyo8OKTNzhJOjwWDOTQTD/sur4lrLg== -xterm@5.4.0-beta.32: - version "5.4.0-beta.32" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.4.0-beta.32.tgz#1b4242cf1c0c1a5a1070da58d3f11956b537130a" - integrity sha512-mWTwEiNBFMF89oqVfi6qTM2Py5gC1Mwvslx1KxmI2Ukgh9v3CrqKDhj29eY1ZeAo0uuYknFWKyuexqp+3SHJCA== +xterm@5.4.0-beta.37: + version "5.4.0-beta.37" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.4.0-beta.37.tgz#bae127be8c939f096232d9858e77a457d6b77ddf" + integrity sha512-ys+mXqLFrJc7khmYN/MgBnfLv38NgXfkwkEXsCZKHGqn3h2xUBvTvsrSEWO3NQeDPLj4zMr1RwqTblMK9St3BA== diff --git a/remote/yarn.lock b/remote/yarn.lock index b0446180fb5..d4ffc6f9d28 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -589,45 +589,45 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= -xterm-addon-canvas@0.6.0-beta.32: - version "0.6.0-beta.32" - resolved "https://registry.yarnpkg.com/xterm-addon-canvas/-/xterm-addon-canvas-0.6.0-beta.32.tgz#c9e74dd72fcc981a2e0cbd0b82827676bc5c74b9" - integrity sha512-Xw7oE4dbS+x+pu6cGW1bDSXcVviuorLz1OLaYw46jjmDezIqQIIEMhSMOprExFEWgeRQ9AEN4lPqw6aH87V74w== +xterm-addon-canvas@0.6.0-beta.37: + version "0.6.0-beta.37" + resolved "https://registry.yarnpkg.com/xterm-addon-canvas/-/xterm-addon-canvas-0.6.0-beta.37.tgz#4f7692fc87f3500a5a6e7f822ec74796e6a36db2" + integrity sha512-eFN7iPxqiQosUO+rbXKwPz/AR3TLJcLCVWjvT5EaewqNpw+E9rWVwRRPGqYSw+qkSKM/vcNlySIkq4hmpA/ohg== xterm-addon-image@0.6.0-beta.21: version "0.6.0-beta.21" resolved "https://registry.yarnpkg.com/xterm-addon-image/-/xterm-addon-image-0.6.0-beta.21.tgz#e3708bc504c56a23ff31f12a2eeb335331a92aac" integrity sha512-8/PTaXVPa4kQ0xzVeuZZk10OpbZBj2cgfwhM2B0ChSPvwrk0lX+ksnXdtDKH3tg+JYvo7fIhNXtkr4NwWt7VJQ== -xterm-addon-search@0.14.0-beta.31: - version "0.14.0-beta.31" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.14.0-beta.31.tgz#933ca5d2d642dacad29f2cfbd50830cff83bc274" - integrity sha512-JRY1ukhoh32D0AMz78xpumQkLgkcP9d3GXj6gzVHZZsjLAMDaJYEubYq1bUhM7IGHUyg+x0sdRJyx7d6fJpiQg== +xterm-addon-search@0.14.0-beta.36: + version "0.14.0-beta.36" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.14.0-beta.36.tgz#4bb161130c39090d506f73468ef65054aedcb9f0" + integrity sha512-2ydZV1Nt8d0vOjZ/y2RXiw3QLgabK+PCA7yETqZNivnmhSfcxtYPyI4aRfG/lc3hoMKiPppSSjJF0MlV/qo8ww== -xterm-addon-serialize@0.12.0-beta.31: - version "0.12.0-beta.31" - resolved "https://registry.yarnpkg.com/xterm-addon-serialize/-/xterm-addon-serialize-0.12.0-beta.31.tgz#2a95dc1e12f4097e2894b04c9cb8fff0bc0b858c" - integrity sha512-h2rWR+Lfi1Iv4VkLUlrBMYh5Mdq8vux2BKyCJe6a1ZnEu5Dzb0VuiNxfTKXTCT5M83nMn7TCB9TX0E8z6bs7xw== +xterm-addon-serialize@0.12.0-beta.36: + version "0.12.0-beta.36" + resolved "https://registry.yarnpkg.com/xterm-addon-serialize/-/xterm-addon-serialize-0.12.0-beta.36.tgz#8e74159317e2c1e5f2724fd92b85df2b87d2c9c9" + integrity sha512-J2mr3Wuxzvxke14IpfJBhwCHlWoB428FE06tOfbGi5lCBm0AnO2cIyRymvgVZG4QVXVxzzWroE8wP/WQQVe45w== -xterm-addon-unicode11@0.7.0-beta.31: - version "0.7.0-beta.31" - resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.7.0-beta.31.tgz#abcba752172323f31312bd8a3f9b6a049dbca6e3" - integrity sha512-vvBKJbBoLbeIf2++6D16VnOOwevZE3nyO/PDZ7cyTJK1eYR73rr8ZbjUrH92YoTu4Z8MpZFepGQOgK/vlAQMwQ== +xterm-addon-unicode11@0.7.0-beta.36: + version "0.7.0-beta.36" + resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.7.0-beta.36.tgz#bde0b8c98211b74b0288cd5bad09107384b870ca" + integrity sha512-vV/f7dTP/46JjoJLYDCxolCWPoUn0cF7S5upYvY0ZA1VLQ9UnXmaNjzrg+9f+QBoXEQ2eQDLQcRFWOc6BAbF+A== -xterm-addon-webgl@0.17.0-beta.31: - version "0.17.0-beta.31" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.17.0-beta.31.tgz#3cd29b4858e3f4f6dd5a8dd969454e85e1f43baa" - integrity sha512-vYHj+HlTcqUlFFVuoCTjlgh89/lIoSkZ7Nc87cwSFTrJsl07qoKutmpupqFXyjhbEA1fQY2SuQLx08Gmf2jWkQ== +xterm-addon-webgl@0.17.0-beta.36: + version "0.17.0-beta.36" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.17.0-beta.36.tgz#6ed16a83cd4bed3cbddd631e7974375aa8f7e023" + integrity sha512-5PNQZcNH2UTaqSf7Exgh/fvsNeTOs+CSEuIaTafzKuMIrWH14+cRdyJdzyo8OKTNzhJOjwWDOTQTD/sur4lrLg== -xterm-headless@5.4.0-beta.32: - version "5.4.0-beta.32" - resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-5.4.0-beta.32.tgz#0d5cd35e1a0372888055ff0b06dfe17457979a6c" - integrity sha512-DQduq8KSoQZyRrQAFB+FkcY2UMxCW39P1/duOpksebc6PT9pbGkyPe5s+AdUQGiYzriEpzVtKUzDcquoVmpPhA== +xterm-headless@5.4.0-beta.37: + version "5.4.0-beta.37" + resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-5.4.0-beta.37.tgz#1e89dfa7e667a5951974a9e9b5e1b0875198ca65" + integrity sha512-iP28Z419p7anaNetLhmPOpt/DUdeA45jVLqoTn8bRpwFj8kM/gI76tUO9y/TyMfv79yWwPv2MrkALfS6ZyTi7g== -xterm@5.4.0-beta.32: - version "5.4.0-beta.32" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.4.0-beta.32.tgz#1b4242cf1c0c1a5a1070da58d3f11956b537130a" - integrity sha512-mWTwEiNBFMF89oqVfi6qTM2Py5gC1Mwvslx1KxmI2Ukgh9v3CrqKDhj29eY1ZeAo0uuYknFWKyuexqp+3SHJCA== +xterm@5.4.0-beta.37: + version "5.4.0-beta.37" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.4.0-beta.37.tgz#bae127be8c939f096232d9858e77a457d6b77ddf" + integrity sha512-ys+mXqLFrJc7khmYN/MgBnfLv38NgXfkwkEXsCZKHGqn3h2xUBvTvsrSEWO3NQeDPLj4zMr1RwqTblMK9St3BA== yallist@^4.0.0: version "4.0.0" diff --git a/yarn.lock b/yarn.lock index ec86215b388..32b1df8ced5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10368,45 +10368,45 @@ xtend@~4.0.0, xtend@~4.0.1: resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== -xterm-addon-canvas@0.6.0-beta.32: - version "0.6.0-beta.32" - resolved "https://registry.yarnpkg.com/xterm-addon-canvas/-/xterm-addon-canvas-0.6.0-beta.32.tgz#c9e74dd72fcc981a2e0cbd0b82827676bc5c74b9" - integrity sha512-Xw7oE4dbS+x+pu6cGW1bDSXcVviuorLz1OLaYw46jjmDezIqQIIEMhSMOprExFEWgeRQ9AEN4lPqw6aH87V74w== +xterm-addon-canvas@0.6.0-beta.37: + version "0.6.0-beta.37" + resolved "https://registry.yarnpkg.com/xterm-addon-canvas/-/xterm-addon-canvas-0.6.0-beta.37.tgz#4f7692fc87f3500a5a6e7f822ec74796e6a36db2" + integrity sha512-eFN7iPxqiQosUO+rbXKwPz/AR3TLJcLCVWjvT5EaewqNpw+E9rWVwRRPGqYSw+qkSKM/vcNlySIkq4hmpA/ohg== xterm-addon-image@0.6.0-beta.21: version "0.6.0-beta.21" resolved "https://registry.yarnpkg.com/xterm-addon-image/-/xterm-addon-image-0.6.0-beta.21.tgz#e3708bc504c56a23ff31f12a2eeb335331a92aac" integrity sha512-8/PTaXVPa4kQ0xzVeuZZk10OpbZBj2cgfwhM2B0ChSPvwrk0lX+ksnXdtDKH3tg+JYvo7fIhNXtkr4NwWt7VJQ== -xterm-addon-search@0.14.0-beta.31: - version "0.14.0-beta.31" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.14.0-beta.31.tgz#933ca5d2d642dacad29f2cfbd50830cff83bc274" - integrity sha512-JRY1ukhoh32D0AMz78xpumQkLgkcP9d3GXj6gzVHZZsjLAMDaJYEubYq1bUhM7IGHUyg+x0sdRJyx7d6fJpiQg== +xterm-addon-search@0.14.0-beta.36: + version "0.14.0-beta.36" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.14.0-beta.36.tgz#4bb161130c39090d506f73468ef65054aedcb9f0" + integrity sha512-2ydZV1Nt8d0vOjZ/y2RXiw3QLgabK+PCA7yETqZNivnmhSfcxtYPyI4aRfG/lc3hoMKiPppSSjJF0MlV/qo8ww== -xterm-addon-serialize@0.12.0-beta.31: - version "0.12.0-beta.31" - resolved "https://registry.yarnpkg.com/xterm-addon-serialize/-/xterm-addon-serialize-0.12.0-beta.31.tgz#2a95dc1e12f4097e2894b04c9cb8fff0bc0b858c" - integrity sha512-h2rWR+Lfi1Iv4VkLUlrBMYh5Mdq8vux2BKyCJe6a1ZnEu5Dzb0VuiNxfTKXTCT5M83nMn7TCB9TX0E8z6bs7xw== +xterm-addon-serialize@0.12.0-beta.36: + version "0.12.0-beta.36" + resolved "https://registry.yarnpkg.com/xterm-addon-serialize/-/xterm-addon-serialize-0.12.0-beta.36.tgz#8e74159317e2c1e5f2724fd92b85df2b87d2c9c9" + integrity sha512-J2mr3Wuxzvxke14IpfJBhwCHlWoB428FE06tOfbGi5lCBm0AnO2cIyRymvgVZG4QVXVxzzWroE8wP/WQQVe45w== -xterm-addon-unicode11@0.7.0-beta.31: - version "0.7.0-beta.31" - resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.7.0-beta.31.tgz#abcba752172323f31312bd8a3f9b6a049dbca6e3" - integrity sha512-vvBKJbBoLbeIf2++6D16VnOOwevZE3nyO/PDZ7cyTJK1eYR73rr8ZbjUrH92YoTu4Z8MpZFepGQOgK/vlAQMwQ== +xterm-addon-unicode11@0.7.0-beta.36: + version "0.7.0-beta.36" + resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.7.0-beta.36.tgz#bde0b8c98211b74b0288cd5bad09107384b870ca" + integrity sha512-vV/f7dTP/46JjoJLYDCxolCWPoUn0cF7S5upYvY0ZA1VLQ9UnXmaNjzrg+9f+QBoXEQ2eQDLQcRFWOc6BAbF+A== -xterm-addon-webgl@0.17.0-beta.31: - version "0.17.0-beta.31" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.17.0-beta.31.tgz#3cd29b4858e3f4f6dd5a8dd969454e85e1f43baa" - integrity sha512-vYHj+HlTcqUlFFVuoCTjlgh89/lIoSkZ7Nc87cwSFTrJsl07qoKutmpupqFXyjhbEA1fQY2SuQLx08Gmf2jWkQ== +xterm-addon-webgl@0.17.0-beta.36: + version "0.17.0-beta.36" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.17.0-beta.36.tgz#6ed16a83cd4bed3cbddd631e7974375aa8f7e023" + integrity sha512-5PNQZcNH2UTaqSf7Exgh/fvsNeTOs+CSEuIaTafzKuMIrWH14+cRdyJdzyo8OKTNzhJOjwWDOTQTD/sur4lrLg== -xterm-headless@5.4.0-beta.32: - version "5.4.0-beta.32" - resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-5.4.0-beta.32.tgz#0d5cd35e1a0372888055ff0b06dfe17457979a6c" - integrity sha512-DQduq8KSoQZyRrQAFB+FkcY2UMxCW39P1/duOpksebc6PT9pbGkyPe5s+AdUQGiYzriEpzVtKUzDcquoVmpPhA== +xterm-headless@5.4.0-beta.37: + version "5.4.0-beta.37" + resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-5.4.0-beta.37.tgz#1e89dfa7e667a5951974a9e9b5e1b0875198ca65" + integrity sha512-iP28Z419p7anaNetLhmPOpt/DUdeA45jVLqoTn8bRpwFj8kM/gI76tUO9y/TyMfv79yWwPv2MrkALfS6ZyTi7g== -xterm@5.4.0-beta.32: - version "5.4.0-beta.32" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.4.0-beta.32.tgz#1b4242cf1c0c1a5a1070da58d3f11956b537130a" - integrity sha512-mWTwEiNBFMF89oqVfi6qTM2Py5gC1Mwvslx1KxmI2Ukgh9v3CrqKDhj29eY1ZeAo0uuYknFWKyuexqp+3SHJCA== +xterm@5.4.0-beta.37: + version "5.4.0-beta.37" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.4.0-beta.37.tgz#bae127be8c939f096232d9858e77a457d6b77ddf" + integrity sha512-ys+mXqLFrJc7khmYN/MgBnfLv38NgXfkwkEXsCZKHGqn3h2xUBvTvsrSEWO3NQeDPLj4zMr1RwqTblMK9St3BA== y18n@^3.2.1: version "3.2.2" From 5f7ea174d6db222ba39fdc3a392333661e8d279d Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 31 Oct 2023 08:07:02 -0700 Subject: [PATCH 24/43] Add _ prefix --- src/vs/workbench/contrib/terminal/browser/terminalView.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalView.ts b/src/vs/workbench/contrib/terminal/browser/terminalView.ts index 1ff7eb0b9ee..0d24663b7f0 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalView.ts @@ -204,7 +204,7 @@ export class TerminalViewPane extends ViewPane { this._register(this.onDidChangeBodyVisibility(async visible => { this._viewShowing.set(visible); if (visible) { - if (this.hasWelcomeScreen()) { + if (this._hasWelcomeScreen()) { this._onDidChangeViewWelcomeState.fire(); } this._initializeTerminal(false); @@ -323,12 +323,12 @@ export class TerminalViewPane extends ViewPane { } } - private hasWelcomeScreen(): boolean { + private _hasWelcomeScreen(): boolean { return !this._terminalService.isProcessSupportRegistered; } override shouldShowWelcome(): boolean { - return this.hasWelcomeScreen() && this._terminalService.instances.length === 0; + return this._hasWelcomeScreen() && this._terminalService.instances.length === 0; } } From d69cf9c8c06d5f6260993a2fa4d96444e326f488 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 31 Oct 2023 08:10:18 -0700 Subject: [PATCH 25/43] Send quick chat toggle text when already visible Fixes #197076 --- src/vs/workbench/contrib/chat/browser/chatQuick.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/vs/workbench/contrib/chat/browser/chatQuick.ts b/src/vs/workbench/contrib/chat/browser/chatQuick.ts index 170c2ae89e6..c4e06196df2 100644 --- a/src/vs/workbench/contrib/chat/browser/chatQuick.ts +++ b/src/vs/workbench/contrib/chat/browser/chatQuick.ts @@ -74,6 +74,12 @@ export class QuickChatService extends Disposable implements IQuickChatService { open(providerId?: string, options?: IQuickChatOpenOptions): void { if (this._input) { + if (this._currentChat && options?.query) { + this._currentChat.setValue(options.query, options.selection); + if (!options.isPartialQuery) { + this._currentChat.acceptInput(); + } + } return this.focus(); } From 14d7452f0c9ec00d07178c23d4dfeb30b7b921be Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 31 Oct 2023 16:29:43 +0100 Subject: [PATCH 26/43] feedback --- .../workbench/contrib/debug/browser/debugToolBar.ts | 12 +++++------- .../contrib/debug/browser/media/debugToolBar.css | 1 + 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugToolBar.ts b/src/vs/workbench/contrib/debug/browser/debugToolBar.ts index 8822f3b8e1f..78536836e73 100644 --- a/src/vs/workbench/contrib/debug/browser/debugToolBar.ts +++ b/src/vs/workbench/contrib/debug/browser/debugToolBar.ts @@ -73,7 +73,6 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution { this.$el = dom.$('div.debug-toolbar'); this.$el.style.top = `${layoutService.mainContainerOffset.top}px`; - this.setHeight(); this.dragArea = dom.append(this.$el, dom.$('div.drag-area' + ThemeIcon.asCSSSelector(icons.debugGripper))); @@ -135,7 +134,6 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution { } if (e.affectsConfiguration(LayoutSettings.EDITOR_TABS_MODE) || e.affectsConfiguration(LayoutSettings.COMMAND_CENTER)) { this._yRange = undefined; - this.setHeight(); this.setYCoordinate(); } })); @@ -191,6 +189,11 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution { const position = parseFloat(left) / window.innerWidth; this.storageService.store(DEBUG_TOOLBAR_POSITION_KEY, position, StorageScope.PROFILE, StorageTarget.MACHINE); } + if (this.yCoordinate) { + this.storageService.store(DEBUG_TOOLBAR_Y_KEY, this.yCoordinate, StorageScope.PROFILE, StorageTarget.MACHINE); + } else { + this.storageService.remove(DEBUG_TOOLBAR_Y_KEY, StorageScope.PROFILE); + } } override updateStyles(): void { @@ -239,7 +242,6 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution { y = Math.max(yMin, Math.min(y, yMax)); this.$el.style.top = `${y}px`; this.yCoordinate = y; - this.storageService.store(DEBUG_TOOLBAR_Y_KEY, y, StorageScope.PROFILE, StorageTarget.MACHINE); } private _yRange: [number, number] | undefined; @@ -265,10 +267,6 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution { return this._yRange; } - private setHeight(): void { - this.$el.style.height = `${this.configurationService.getValue(LayoutSettings.EDITOR_TABS_MODE) === EditorTabsMode.NONE && this.configurationService.getValue(LayoutSettings.COMMAND_CENTER) !== true ? 26 : 32}px`; - } - private show(): void { if (this.isVisible) { this.setCoordinates(); diff --git a/src/vs/workbench/contrib/debug/browser/media/debugToolBar.css b/src/vs/workbench/contrib/debug/browser/media/debugToolBar.css index 64260286436..fbe36b38c1a 100644 --- a/src/vs/workbench/contrib/debug/browser/media/debugToolBar.css +++ b/src/vs/workbench/contrib/debug/browser/media/debugToolBar.css @@ -6,6 +6,7 @@ .monaco-workbench .debug-toolbar { position: absolute; z-index: 3000; + height: 26px; display: flex; padding-left: 7px; border-radius: 4px; From 96d48e8481afc0c937850c908bcd28031aa4368a Mon Sep 17 00:00:00 2001 From: Andrea Mah <31675041+andreamah@users.noreply.github.com> Date: Tue, 31 Oct 2023 08:58:36 -0700 Subject: [PATCH 27/43] Duplicate Lines In Search Editor (#197038) Fixes #192113 --- .../contrib/search/browser/searchModel.ts | 13 ++++---- .../services/search/common/searchHelpers.ts | 6 ++-- .../services/search/common/searchService.ts | 4 +-- .../search/test/common/searchHelpers.test.ts | 32 +++++++++---------- 4 files changed, 27 insertions(+), 28 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/searchModel.ts b/src/vs/workbench/contrib/search/browser/searchModel.ts index 68f64157179..a8bec5eff24 100644 --- a/src/vs/workbench/contrib/search/browser/searchModel.ts +++ b/src/vs/workbench/contrib/search/browser/searchModel.ts @@ -41,7 +41,7 @@ import { CellSearchModel, ICellMatch, contentMatchesToTextSearchMatches, isIFile import { INotebookSearchService } from 'vs/workbench/contrib/search/common/notebookSearch'; import { ReplacePattern } from 'vs/workbench/services/search/common/replace'; import { IFileMatch, IPatternInfo, ISearchComplete, ISearchConfigurationProperties, ISearchProgressItem, ISearchRange, ISearchService, ITextQuery, ITextSearchContext, ITextSearchMatch, ITextSearchPreviewOptions, ITextSearchResult, ITextSearchStats, OneLineRange, resultIsMatch, SearchCompletionExitCode, SearchSortOrder } from 'vs/workbench/services/search/common/search'; -import { addContextToEditorMatches, editorMatchesToTextSearchResults } from 'vs/workbench/services/search/common/searchHelpers'; +import { getTextSearchMatchWithModelContext, editorMatchesToTextSearchResults } from 'vs/workbench/services/search/common/searchHelpers'; export class Match { @@ -245,7 +245,7 @@ export class CellMatch { return; } this.cell.resolveTextModel().then((textModel) => { - const textResultsWithContext = addContextToEditorMatches(textSearchMatches, textModel, this.parent.parent().query!); + const textResultsWithContext = getTextSearchMatchWithModelContext(textSearchMatches, textModel, this.parent.parent().query!); const contexts = textResultsWithContext.filter((result => !resultIsMatch(result)) as ((a: any) => a is ITextSearchContext)); contexts.map(context => ({ ...context, lineNumber: context.lineNumber + 1 })) .forEach((context) => { this._context.set(context.lineNumber, context.text); }); @@ -567,10 +567,7 @@ export class FileMatch extends Disposable implements IFileMatch { }); }); - this.addContext( - addContextToEditorMatches(textSearchResults, model, this.parent().parent().query!) - .filter((result => !resultIsMatch(result)) as ((a: any) => a is ITextSearchContext)) - .map(context => ({ ...context, lineNumber: context.lineNumber + 1 }))); + this.addContext(getTextSearchMatchWithModelContext(textSearchResults, model, this.parent().parent().query!)); this._onChange.fire({ forceUpdateModel: modelChange }); this.updateHighlights(); @@ -1176,7 +1173,9 @@ export class FolderMatch extends Disposable { updated.push(existingFileMatch); - existingFileMatch.addContext(rawFileMatch.results); + if (rawFileMatch.results && rawFileMatch.results.length > 0) { + existingFileMatch.addContext(rawFileMatch.results); + } } else { if (this instanceof FolderMatchWorkspaceRoot || this instanceof FolderMatchNoRoot) { const fileMatch = this.createAndConfigureFileMatch(rawFileMatch, searchInstanceID); diff --git a/src/vs/workbench/services/search/common/searchHelpers.ts b/src/vs/workbench/services/search/common/searchHelpers.ts index 50e8d7a43ca..3f63134b5bf 100644 --- a/src/vs/workbench/services/search/common/searchHelpers.ts +++ b/src/vs/workbench/services/search/common/searchHelpers.ts @@ -44,7 +44,7 @@ export function editorMatchesToTextSearchResults(matches: FindMatch[], model: IT }); } -export function addContextToEditorMatches(matches: ITextSearchMatch[], model: ITextModel, query: ITextQuery): ITextSearchResult[] { +export function getTextSearchMatchWithModelContext(matches: ITextSearchMatch[], model: ITextModel, query: ITextQuery): ITextSearchResult[] { const results: ITextSearchResult[] = []; let prevLine = -1; @@ -55,7 +55,7 @@ export function addContextToEditorMatches(matches: ITextSearchMatch[], model: IT for (let b = beforeContextStartLine; b < matchStartLine; b++) { results.push({ text: model.getLineContent(b + 1), - lineNumber: b + lineNumber: b + 1 }); } } @@ -69,7 +69,7 @@ export function addContextToEditorMatches(matches: ITextSearchMatch[], model: IT for (let a = matchEndLine + 1; a <= afterContextToLine; a++) { results.push({ text: model.getLineContent(a + 1), - lineNumber: a + lineNumber: a + 1 }); } } diff --git a/src/vs/workbench/services/search/common/searchService.ts b/src/vs/workbench/services/search/common/searchService.ts index 4e8fb9a5805..ac934456627 100644 --- a/src/vs/workbench/services/search/common/searchService.ts +++ b/src/vs/workbench/services/search/common/searchService.ts @@ -22,7 +22,7 @@ import { EditorResourceAccessor, SideBySideEditor } from 'vs/workbench/common/ed import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { deserializeSearchError, FileMatch, ICachedSearchStats, IFileMatch, IFileQuery, IFileSearchStats, IFolderQuery, IProgressMessage, ISearchComplete, ISearchEngineStats, ISearchProgressItem, ISearchQuery, ISearchResultProvider, ISearchService, isFileMatch, isProgressMessage, ITextQuery, pathIncludedInQuery, QueryType, SEARCH_RESULT_LANGUAGE_ID, SearchError, SearchErrorCode, SearchProviderType } from 'vs/workbench/services/search/common/search'; -import { addContextToEditorMatches, editorMatchesToTextSearchResults } from 'vs/workbench/services/search/common/searchHelpers'; +import { getTextSearchMatchWithModelContext, editorMatchesToTextSearchResults } from 'vs/workbench/services/search/common/searchHelpers'; export class SearchService extends Disposable implements ISearchService { @@ -494,7 +494,7 @@ export class SearchService extends Disposable implements ISearchService { openEditorResults.set(originalResource, fileMatch); const textSearchResults = editorMatchesToTextSearchResults(matches, model, query.previewOptions); - fileMatch.results = addContextToEditorMatches(textSearchResults, model, query); + fileMatch.results = getTextSearchMatchWithModelContext(textSearchResults, model, query); } else { openEditorResults.set(originalResource, null); } diff --git a/src/vs/workbench/services/search/test/common/searchHelpers.test.ts b/src/vs/workbench/services/search/test/common/searchHelpers.test.ts index f22e14e9c76..2e09c9c6aea 100644 --- a/src/vs/workbench/services/search/test/common/searchHelpers.test.ts +++ b/src/vs/workbench/services/search/test/common/searchHelpers.test.ts @@ -7,7 +7,7 @@ import * as assert from 'assert'; import { Range } from 'vs/editor/common/core/range'; import { FindMatch, ITextModel } from 'vs/editor/common/model'; import { ISearchRange, ITextQuery, ITextSearchContext, QueryType } from 'vs/workbench/services/search/common/search'; -import { addContextToEditorMatches, editorMatchesToTextSearchResults } from 'vs/workbench/services/search/common/searchHelpers'; +import { getTextSearchMatchWithModelContext, editorMatchesToTextSearchResults } from 'vs/workbench/services/search/common/searchHelpers'; suite('SearchHelpers', () => { suite('editorMatchesToTextSearchResults', () => { @@ -107,7 +107,7 @@ suite('SearchHelpers', () => { ranges: new Range(0, 0, 0, 10) }]; - assert.deepStrictEqual(addContextToEditorMatches(matches, mockTextModel, getQuery()), matches); + assert.deepStrictEqual(getTextSearchMatchWithModelContext(matches, mockTextModel, getQuery()), matches); }); test('simple', () => { @@ -119,19 +119,19 @@ suite('SearchHelpers', () => { ranges: new Range(1, 0, 1, 10) }]; - assert.deepStrictEqual(addContextToEditorMatches(matches, mockTextModel, getQuery(1, 2)), [ + assert.deepStrictEqual(getTextSearchMatchWithModelContext(matches, mockTextModel, getQuery(1, 2)), [ { text: '1', - lineNumber: 0 + lineNumber: 1 }, ...matches, { text: '3', - lineNumber: 2 + lineNumber: 3 }, { text: '4', - lineNumber: 3 + lineNumber: 4 }, ]); }); @@ -153,19 +153,19 @@ suite('SearchHelpers', () => { ranges: new Range(2, 0, 2, 10) }]; - assert.deepStrictEqual(addContextToEditorMatches(matches, mockTextModel, getQuery(1, 2)), [ + assert.deepStrictEqual(getTextSearchMatchWithModelContext(matches, mockTextModel, getQuery(1, 2)), [ { text: '1', - lineNumber: 0 + lineNumber: 1 }, ...matches, { text: '4', - lineNumber: 3 + lineNumber: 4 }, { text: '5', - lineNumber: 4 + lineNumber: 5 }, ]); }); @@ -187,19 +187,19 @@ suite('SearchHelpers', () => { ranges: new Range(MOCK_LINE_COUNT - 1, 0, MOCK_LINE_COUNT - 1, 10) }]; - assert.deepStrictEqual(addContextToEditorMatches(matches, mockTextModel, getQuery(1, 2)), [ + assert.deepStrictEqual(getTextSearchMatchWithModelContext(matches, mockTextModel, getQuery(1, 2)), [ matches[0], { text: '2', - lineNumber: 1 - }, - { - text: '3', lineNumber: 2 }, + { + text: '3', + lineNumber: 3 + }, { text: '' + (MOCK_LINE_COUNT - 1), - lineNumber: MOCK_LINE_COUNT - 2 + lineNumber: MOCK_LINE_COUNT - 1 }, matches[1] ]); From 13df0753e38273dc8e42ca010496c7edccec81a1 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 31 Oct 2023 10:13:31 -0700 Subject: [PATCH 28/43] Fix compile --- src/vs/workbench/contrib/terminal/browser/terminalView.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalView.ts b/src/vs/workbench/contrib/terminal/browser/terminalView.ts index 0d24663b7f0..cedb103f398 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalView.ts @@ -88,7 +88,7 @@ export class TerminalViewPane extends ViewPane { this._register(this._terminalService.onDidChangeInstances(() => { // If the first terminal is opened, hide the welcome view // and if the last one is closed, show it again - if (this.hasWelcomeScreen() && this._terminalService.instances.length <= 1) { + if (this._hasWelcomeScreen() && this._terminalService.instances.length <= 1) { this._onDidChangeViewWelcomeState.fire(); } if (!this._parentDomElement) { return; } From 6384066be76810096fe045c46fb46e82911a7d95 Mon Sep 17 00:00:00 2001 From: Andrea Mah <31675041+andreamah@users.noreply.github.com> Date: Tue, 31 Oct 2023 10:39:03 -0700 Subject: [PATCH 29/43] adopt ensureNoDisposablesAreLeakedInTestSuite in search (#197014) * adopt ensureNoDisposablesAreLeakedInTestSuite in search * linux tests --- .../contrib/search/browser/searchModel.ts | 48 ++++++++++------- .../search/test/browser/searchActions.test.ts | 12 +++-- .../search/test/browser/searchModel.test.ts | 47 +++++++++++++--- .../browser/searchNotebookHelpers.test.ts | 16 ++++-- .../search/test/browser/searchResult.test.ts | 43 ++++++++++----- .../search/test/browser/searchTestCommon.ts | 13 +++-- .../search/test/browser/searchViewlet.test.ts | 53 +++++++------------ 7 files changed, 148 insertions(+), 84 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/searchModel.ts b/src/vs/workbench/contrib/search/browser/searchModel.ts index a8bec5eff24..ec6a10068f7 100644 --- a/src/vs/workbench/contrib/search/browser/searchModel.ts +++ b/src/vs/workbench/contrib/search/browser/searchModel.ts @@ -1038,7 +1038,7 @@ export class FolderMatch extends Disposable { } public createIntermediateFolderMatch(resource: URI, id: string, index: number, query: ITextQuery, baseWorkspaceFolder: FolderMatchWorkspaceRoot): FolderMatchWithResource { - const folderMatch = this.instantiationService.createInstance(FolderMatchWithResource, resource, id, index, query, this, this._searchResult, baseWorkspaceFolder); + const folderMatch = this._register(this.instantiationService.createInstance(FolderMatchWithResource, resource, id, index, query, this, this._searchResult, baseWorkspaceFolder)); this.configureIntermediateMatch(folderMatch); this.doAddFolder(folderMatch); return folderMatch; @@ -1046,7 +1046,7 @@ export class FolderMatch extends Disposable { public configureIntermediateMatch(folderMatch: FolderMatchWithResource) { const disposable = folderMatch.onChange((event) => this.onFolderChange(folderMatch, event)); - folderMatch.onDispose(() => disposable.dispose()); + this._register(folderMatch.onDispose(() => disposable.dispose())); } clear(clearingAll = false): void { @@ -1378,7 +1378,7 @@ export class FolderMatchWorkspaceRoot extends FolderMatchWithResource { ); parent.doAddFile(fileMatch); const disposable = fileMatch.onChange(({ didRemove }) => parent.onFileChange(fileMatch, didRemove)); - fileMatch.onDispose(() => disposable.dispose()); + this._register(fileMatch.onDispose(() => disposable.dispose())); return fileMatch; } @@ -1430,17 +1430,17 @@ export class FolderMatchNoRoot extends FolderMatch { } createAndConfigureFileMatch(rawFileMatch: IFileMatch, searchInstanceID: string): FileMatch { - const fileMatch = this.instantiationService.createInstance( + const fileMatch = this._register(this.instantiationService.createInstance( FileMatch, this._query.contentPattern, this._query.previewOptions, this._query.maxResults, this, rawFileMatch, null, - searchInstanceID); + searchInstanceID)); this.doAddFile(fileMatch); const disposable = fileMatch.onChange(({ didRemove }) => this.onFileChange(fileMatch, didRemove)); - fileMatch.onDispose(() => disposable.dispose()); + this._register(fileMatch.onDispose(() => disposable.dispose())); return fileMatch; } } @@ -1589,7 +1589,7 @@ export class SearchResult extends Disposable { private _showHighlights: boolean = false; private _query: ITextQuery | null = null; private _rangeHighlightDecorations: RangeHighlightDecorations; - private disposePastResults: () => void = () => { }; + private disposePastResults: () => Promise = () => Promise.resolve(); private _isDirty = false; private _onWillChangeModelListener: IDisposable | undefined; private _onDidChangeModelListener: IDisposable | undefined; @@ -1673,10 +1673,11 @@ export class SearchResult extends Disposable { set query(query: ITextQuery | null) { // When updating the query we could change the roots, so keep a reference to them to clean up when we trigger `disposePastResults` const oldFolderMatches = this.folderMatches(); - new Promise(resolve => this.disposePastResults = resolve) - .then(() => oldFolderMatches.forEach(match => match.clear())) - .then(() => oldFolderMatches.forEach(match => match.dispose())) - .then(() => this._isDirty = false); + this.disposePastResults = async () => { + oldFolderMatches.forEach(match => match.clear()); + oldFolderMatches.forEach(match => match.dispose()); + this._isDirty = false; + }; this._rangeHighlightDecorations.removeHighlightRange(); this._folderMatchesMap = TernarySearchTree.forUris(key => this.uriIdentityService.extUri.ignorePathCasing(key)); @@ -1735,12 +1736,12 @@ export class SearchResult extends Disposable { private _createBaseFolderMatch(resource: URI | null, id: string, index: number, query: ITextQuery): FolderMatch { let folderMatch: FolderMatch; if (resource) { - folderMatch = this.instantiationService.createInstance(FolderMatchWorkspaceRoot, resource, id, index, query, this); + folderMatch = this._register(this.instantiationService.createInstance(FolderMatchWorkspaceRoot, resource, id, index, query, this)); } else { - folderMatch = this.instantiationService.createInstance(FolderMatchNoRoot, id, index, query, this); + folderMatch = this._register(this.instantiationService.createInstance(FolderMatchNoRoot, id, index, query, this)); } const disposable = folderMatch.onChange((event) => this._onChange.fire(event)); - folderMatch.onDispose(() => disposable.dispose()); + this._register(folderMatch.onDispose(() => disposable.dispose())); return folderMatch; } @@ -1921,13 +1922,13 @@ export class SearchResult extends Disposable { this._rangeHighlightDecorations.removeHighlightRange(); } - override dispose(): void { + override async dispose(): Promise { this._onWillChangeModelListener?.dispose(); this._onDidChangeModelListener?.dispose(); - this.disposePastResults(); - this.disposeMatches(); this._rangeHighlightDecorations.dispose(); + this.disposeMatches(); super.dispose(); + await this.disposePastResults(); } } @@ -2079,7 +2080,7 @@ export class SearchModel extends Disposable { this._searchResult.query = this._searchQuery; - const progressEmitter = new Emitter(); + const progressEmitter = this._register(new Emitter()); this._replacePattern = new ReplacePattern(this.replaceString, this._searchQuery.contentPattern); // In search on type case, delay the streaming of results just a bit, so that we don't flash the only "local results" fast path @@ -2098,14 +2099,21 @@ export class SearchModel extends Disposable { } const start = Date.now(); + let event: IDisposable | undefined; - Promise.race([asyncResults, Event.toPromise(progressEmitter.event)]).finally(() => { + const progressEmitterPromise = new Promise(resolve => { + event = Event.once(progressEmitter.event)(resolve); + return event; + }); + + Promise.race([asyncResults, progressEmitterPromise]).finally(() => { /* __GDPR__ "searchResultsFirstRender" : { "owner": "roblourens", "duration" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true } } */ + event?.dispose(); this.telemetryService.publicLog('searchResultsFirstRender', { duration: Date.now() - start }); }); @@ -2330,9 +2338,9 @@ export class RangeHighlightDecorations implements IDisposable { dispose() { if (this._model) { this.removeHighlightRange(); - this._modelDisposables.dispose(); this._model = null; } + this._modelDisposables.dispose(); } private static readonly _RANGE_HIGHLIGHT_DECORATION = ModelDecorationOptions.register({ diff --git a/src/vs/workbench/contrib/search/test/browser/searchActions.test.ts b/src/vs/workbench/contrib/search/test/browser/searchActions.test.ts index ddaf7dcb2d5..5e39773a0cc 100644 --- a/src/vs/workbench/contrib/search/test/browser/searchActions.test.ts +++ b/src/vs/workbench/contrib/search/test/browser/searchActions.test.ts @@ -18,16 +18,18 @@ import { MockObjectTree } from 'vs/workbench/contrib/search/test/browser/mockSea import { ILabelService } from 'vs/platform/label/common/label'; import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/services/notebookEditorService'; import { createFileUriFromPathFromRoot, stubModelService, stubNotebookEditorService } from 'vs/workbench/contrib/search/test/browser/searchTestCommon'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('Search Actions', () => { let instantiationService: TestInstantiationService; let counter: number; + const store = ensureNoDisposablesAreLeakedInTestSuite(); setup(() => { instantiationService = new TestInstantiationService(); - instantiationService.stub(IModelService, stubModelService(instantiationService)); - instantiationService.stub(INotebookEditorService, stubNotebookEditorService(instantiationService)); + instantiationService.stub(IModelService, stubModelService(instantiationService, (e) => store.add(e))); + instantiationService.stub(INotebookEditorService, stubNotebookEditorService(instantiationService, (e) => store.add(e))); instantiationService.stub(IKeybindingService, {}); instantiationService.stub(ILabelService, { getUriBasenameLabel: (uri: URI) => '' }); instantiationService.stub(IKeybindingService, 'resolveKeybinding', (keybinding: Keybinding) => USLayoutResolvedKeybinding.resolveKeybinding(keybinding, OS)); @@ -113,14 +115,18 @@ suite('Search Actions', () => { }; const searchModel = instantiationService.createInstance(SearchModel); + store.add(searchModel); const folderMatch = instantiationService.createInstance(FolderMatch, URI.file('somepath'), '', 0, { type: QueryType.Text, folderQueries: [{ folder: createFileUriFromPathFromRoot() }], contentPattern: { pattern: '' } }, searchModel.searchResult, searchModel.searchResult, null); - return instantiationService.createInstance(FileMatch, { + store.add(folderMatch); + const fileMatch = instantiationService.createInstance(FileMatch, { pattern: '' }, undefined, undefined, folderMatch, rawMatch, null, ''); + store.add(fileMatch); + return fileMatch; } function aMatch(fileMatch: FileMatch): Match { diff --git a/src/vs/workbench/contrib/search/test/browser/searchModel.test.ts b/src/vs/workbench/contrib/search/test/browser/searchModel.test.ts index 715554ed96a..140f3ec64d1 100644 --- a/src/vs/workbench/contrib/search/test/browser/searchModel.test.ts +++ b/src/vs/workbench/contrib/search/test/browser/searchModel.test.ts @@ -40,6 +40,7 @@ import { INotebookSearchService } from 'vs/workbench/contrib/search/common/noteb import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; const nullEvent = new class { id: number = -1; @@ -65,6 +66,7 @@ const lineOneRange = new OneLineRange(1, 0, 1); suite('SearchModel', () => { let instantiationService: TestInstantiationService; + const store = ensureNoDisposablesAreLeakedInTestSuite(); const testSearchStats: IFileSearchStats = { fromCache: false, @@ -92,7 +94,11 @@ suite('SearchModel', () => { instantiationService.stub(INotebookEditorService, stubNotebookEditorService(instantiationService)); instantiationService.stub(ISearchService, {}); instantiationService.stub(ISearchService, 'textSearch', Promise.resolve({ results: [] })); - instantiationService.stub(IUriIdentityService, new UriIdentityService(new FileService(new NullLogService()))); + const fileService = new FileService(new NullLogService()); + store.add(fileService); + const uriIdentityService = new UriIdentityService(fileService); + store.add(uriIdentityService); + instantiationService.stub(IUriIdentityService, uriIdentityService); instantiationService.stub(ILogService, new NullLogService()); }); @@ -164,12 +170,18 @@ suite('SearchModel', () => { function canceleableSearchService(tokenSource: CancellationTokenSource): ISearchService { return { textSearch(query: ITextQuery, token?: CancellationToken, onProgress?: (result: ISearchProgressItem) => void): Promise { - token?.onCancellationRequested(() => tokenSource.cancel()); + const disposable = token?.onCancellationRequested(() => tokenSource.cancel()); + if (disposable) { + store.add(disposable); + } return this.textSearchSplitSyncAsync(query, token, onProgress).asyncResults; }, fileSearch(query: IFileQuery, token?: CancellationToken): Promise { - token?.onCancellationRequested(() => tokenSource.cancel()); + const disposable = token?.onCancellationRequested(() => tokenSource.cancel()); + if (disposable) { + store.add(disposable); + } return new Promise(resolve => { queueMicrotask(() => { resolve({}); @@ -177,7 +189,10 @@ suite('SearchModel', () => { }); }, textSearchSplitSyncAsync(query: ITextQuery, token?: CancellationToken | undefined, onProgress?: ((result: ISearchProgressItem) => void) | undefined): { syncResults: ISearchComplete; asyncResults: Promise } { - token?.onCancellationRequested(() => tokenSource.cancel()); + const disposable = token?.onCancellationRequested(() => tokenSource.cancel()); + if (disposable) { + store.add(disposable); + } return { syncResults: { results: [], @@ -219,7 +234,10 @@ suite('SearchModel', () => { completeData: Promise; allScannedFiles: Promise; } { - token?.onCancellationRequested(() => tokenSource?.cancel()); + const disposable = token?.onCancellationRequested(() => tokenSource?.cancel()); + if (disposable) { + store.add(disposable); + } const localResults = new ResourceMap(uri => uri.path); results.forEach(r => { @@ -252,6 +270,7 @@ suite('SearchModel', () => { instantiationService.stub(INotebookSearchService, notebookSearchServiceWithInfo([], undefined)); const testObject: SearchModel = instantiationService.createInstance(SearchModel); + store.add(testObject); await testObject.search({ contentPattern: { pattern: 'somestring' }, type: QueryType.Text, folderQueries }).asyncResults; const actual = testObject.searchResult.matches(); @@ -352,6 +371,7 @@ suite('SearchModel', () => { const notebookSearchService = instantiationService.stub(INotebookSearchService, notebookSearchServiceWithInfo([aRawMatchWithCells('/1', cellMatchMd, cellMatchCode)], undefined)); const notebookSearch = sinon.spy(notebookSearchService, "notebookSearch"); const model: SearchModel = instantiationService.createInstance(SearchModel); + store.add(model); await model.search({ contentPattern: { pattern: 'test' }, type: QueryType.Text, folderQueries }).asyncResults; const actual = model.searchResult.matches(); @@ -404,6 +424,7 @@ suite('SearchModel', () => { instantiationService.stub(INotebookSearchService, notebookSearchServiceWithInfo([], undefined)); const testObject: SearchModel = instantiationService.createInstance(SearchModel); + store.add(testObject); await testObject.search({ contentPattern: { pattern: 'somestring' }, type: QueryType.Text, folderQueries }).asyncResults; assert.ok(target.calledThrice); @@ -421,6 +442,7 @@ suite('SearchModel', () => { instantiationService.stub(INotebookSearchService, notebookSearchServiceWithInfo([], undefined)); const testObject = instantiationService.createInstance(SearchModel); + store.add(testObject); const result = testObject.search({ contentPattern: { pattern: 'somestring' }, type: QueryType.Text, folderQueries }).asyncResults; return result.then(() => { @@ -443,6 +465,7 @@ suite('SearchModel', () => { instantiationService.stub(INotebookSearchService, notebookSearchServiceWithInfo([], undefined)); const testObject = instantiationService.createInstance(SearchModel); + store.add(testObject); const result = testObject.search({ contentPattern: { pattern: 'somestring' }, type: QueryType.Text, folderQueries }).asyncResults; return result.then(() => { @@ -466,6 +489,7 @@ suite('SearchModel', () => { instantiationService.stub(INotebookSearchService, notebookSearchServiceWithInfo([], undefined)); const testObject = instantiationService.createInstance(SearchModel); + store.add(testObject); const result = testObject.search({ contentPattern: { pattern: 'somestring' }, type: QueryType.Text, folderQueries }).asyncResults; return result.then(() => { }, () => { @@ -488,6 +512,7 @@ suite('SearchModel', () => { instantiationService.stub(INotebookSearchService, notebookSearchServiceWithInfo([], undefined)); const testObject = instantiationService.createInstance(SearchModel); + store.add(testObject); const result = testObject.search({ contentPattern: { pattern: 'somestring' }, type: QueryType.Text, folderQueries }).asyncResults; deferredPromise.cancel(); @@ -511,6 +536,7 @@ suite('SearchModel', () => { instantiationService.stub(ISearchService, searchServiceWithResults(results, { limitHit: false, messages: [], results: [] })); instantiationService.stub(INotebookSearchService, notebookSearchServiceWithInfo([], undefined)); const testObject: SearchModel = instantiationService.createInstance(SearchModel); + store.add(testObject); await testObject.search({ contentPattern: { pattern: 'somestring' }, type: QueryType.Text, folderQueries }).asyncResults; assert.ok(!testObject.searchResult.isEmpty()); @@ -522,9 +548,11 @@ suite('SearchModel', () => { test('Search Model: Previous search is cancelled when new search is called', async () => { const tokenSource = new CancellationTokenSource(); + store.add(tokenSource); instantiationService.stub(ISearchService, canceleableSearchService(tokenSource)); instantiationService.stub(INotebookSearchService, notebookSearchServiceWithInfo([], tokenSource)); const testObject: SearchModel = instantiationService.createInstance(SearchModel); + store.add(testObject); testObject.search({ contentPattern: { pattern: 'somestring' }, type: QueryType.Text, folderQueries }); instantiationService.stub(ISearchService, searchServiceWithResults([])); instantiationService.stub(INotebookSearchService, notebookSearchServiceWithInfo([], undefined)); @@ -542,6 +570,7 @@ suite('SearchModel', () => { instantiationService.stub(INotebookSearchService, notebookSearchServiceWithInfo([], undefined)); const testObject: SearchModel = instantiationService.createInstance(SearchModel); + store.add(testObject); await testObject.search({ contentPattern: { pattern: 're' }, type: QueryType.Text, folderQueries }).asyncResults; testObject.replaceString = 'hello'; let match = testObject.searchResult.matches()[0].matches()[0]; @@ -578,14 +607,18 @@ suite('SearchModel', () => { const config = new TestConfigurationService(); config.setUserConfiguration('search', { searchOnType: true }); instantiationService.stub(IConfigurationService, config); - return instantiationService.createInstance(ModelService); + const modelService = instantiationService.createInstance(ModelService); + store.add(modelService); + return modelService; } function stubNotebookEditorService(instantiationService: TestInstantiationService): INotebookEditorService { instantiationService.stub(IEditorGroupsService, new TestEditorGroupsService()); instantiationService.stub(IContextKeyService, new MockContextKeyService()); instantiationService.stub(IEditorService, new TestEditorService()); - return instantiationService.createInstance(NotebookEditorWidgetService); + const notebookEditorWidgetService = instantiationService.createInstance(NotebookEditorWidgetService); + store.add(notebookEditorWidgetService); + return notebookEditorWidgetService; } }); diff --git a/src/vs/workbench/contrib/search/test/browser/searchNotebookHelpers.test.ts b/src/vs/workbench/contrib/search/test/browser/searchNotebookHelpers.test.ts index 4eae2669c1c..ff2c6a9039e 100644 --- a/src/vs/workbench/contrib/search/test/browser/searchNotebookHelpers.test.ts +++ b/src/vs/workbench/contrib/search/test/browser/searchNotebookHelpers.test.ts @@ -17,6 +17,7 @@ import { TestInstantiationService } from 'vs/platform/instantiation/test/common/ import { createFileUriFromPathFromRoot, stubModelService, stubNotebookEditorService } from 'vs/workbench/contrib/search/test/browser/searchTestCommon'; import { IModelService } from 'vs/editor/common/services/model'; import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/services/notebookEditorService'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('searchNotebookHelpers', () => { let instantiationService: TestInstantiationService; @@ -28,12 +29,16 @@ suite('searchNotebookHelpers', () => { let markdownContentResults: ITextSearchMatch[]; let codeContentResults: ITextSearchMatch[]; let codeWebviewResults: ITextSearchMatch[]; + const store = ensureNoDisposablesAreLeakedInTestSuite(); let counter: number = 0; setup(() => { instantiationService = new TestInstantiationService(); - instantiationService.stub(IModelService, stubModelService(instantiationService)); - instantiationService.stub(INotebookEditorService, stubNotebookEditorService(instantiationService)); + store.add(instantiationService); + const modelService = stubModelService(instantiationService, (e) => store.add(e)); + const notebookEditorService = stubNotebookEditorService(instantiationService, (e) => store.add(e)); + instantiationService.stub(IModelService, modelService); + instantiationService.stub(INotebookEditorService, notebookEditorService); mdInputCell = { cellKind: CellKind.Markup, textBuffer: { getLineContent(lineNumber: number): string { @@ -201,14 +206,19 @@ suite('searchNotebookHelpers', () => { }; const searchModel = instantiationService.createInstance(SearchModel); + store.add(searchModel); const folderMatch = instantiationService.createInstance(FolderMatch, URI.file('somepath'), '', 0, { type: QueryType.Text, folderQueries: [{ folder: createFileUriFromPathFromRoot() }], contentPattern: { pattern: '' } }, searchModel.searchResult, searchModel.searchResult, null); - return instantiationService.createInstance(FileMatch, { + const fileMatch = instantiationService.createInstance(FileMatch, { pattern: '' }, undefined, undefined, folderMatch, rawMatch, null, ''); + store.add(folderMatch); + store.add(fileMatch); + + return fileMatch; } }); }); diff --git a/src/vs/workbench/contrib/search/test/browser/searchResult.test.ts b/src/vs/workbench/contrib/search/test/browser/searchResult.test.ts index 923d74b550d..ad53f73d11c 100644 --- a/src/vs/workbench/contrib/search/test/browser/searchResult.test.ts +++ b/src/vs/workbench/contrib/search/test/browser/searchResult.test.ts @@ -35,19 +35,25 @@ import { addToSearchResult, createFileUriFromPathFromRoot, getRootName } from 'v import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; const lineOneRange = new OneLineRange(1, 0, 1); suite('SearchResult', () => { let instantiationService: TestInstantiationService; + const store = ensureNoDisposablesAreLeakedInTestSuite(); setup(() => { instantiationService = new TestInstantiationService(); instantiationService.stub(ITelemetryService, NullTelemetryService); instantiationService.stub(IModelService, stubModelService(instantiationService)); instantiationService.stub(INotebookEditorService, stubNotebookEditorService(instantiationService)); - instantiationService.stub(IUriIdentityService, new UriIdentityService(new FileService(new NullLogService()))); + const fileService = new FileService(new NullLogService()); + store.add(fileService); + const uriIdentityService = new UriIdentityService(fileService); + store.add(uriIdentityService); + instantiationService.stub(IUriIdentityService, uriIdentityService); instantiationService.stubPromise(IReplaceService, {}); instantiationService.stub(IReplaceService, 'replace', () => Promise.resolve(null)); instantiationService.stub(ILabelService, new MockLabelService()); @@ -164,7 +170,9 @@ suite('SearchResult', () => { test('Match -> FileMatch -> SearchResult hierarchy exists', function () { const searchModel = instantiationService.createInstance(SearchModel); + store.add(searchModel); const searchResult = instantiationService.createInstance(SearchResult, searchModel); + store.add(searchResult); const fileMatch = aFileMatch('far/boo', searchResult); const lineMatch = new Match(fileMatch, ['foo bar'], new OneLineRange(0, 0, 3), new OneLineRange(1, 0, 3)); @@ -283,8 +291,8 @@ suite('SearchResult', () => { aRawMatch('/2', new TextSearchMatch('preview 2', lineOneRange))]); - testObject.matches()[0].onDispose(target1); - testObject.matches()[1].onDispose(target2); + store.add(testObject.matches()[0].onDispose(target1)); + store.add(testObject.matches()[1].onDispose(target2)); testObject.dispose(); @@ -300,7 +308,7 @@ suite('SearchResult', () => { aRawMatch('/1', new TextSearchMatch('preview 1', lineOneRange))]); const objectToRemove = testObject.matches()[0]; - testObject.onChange(target); + store.add(testObject.onChange(target)); testObject.remove(objectToRemove); @@ -317,7 +325,7 @@ suite('SearchResult', () => { aRawMatch('/2', new TextSearchMatch('preview 2', lineOneRange))]); const arrayToRemove = testObject.matches(); - testObject.onChange(target); + store.add(testObject.onChange(target)); testObject.remove(arrayToRemove); @@ -362,7 +370,8 @@ suite('SearchResult', () => { addToSearchResult(testObject, [ aRawMatch('/1', new TextSearchMatch('preview 1', lineOneRange))]); - testObject.onChange(target); + + store.add(testObject.onChange(target)); const objectToRemove = testObject.matches()[0]; testObject.replace(objectToRemove); @@ -399,7 +408,7 @@ suite('SearchResult', () => { const arrayToRemove = [folderMatch, fileMatch, match]; const expectedArrayResult = folderMatch.allDownstreamFileMatches().concat([fileMatch, match.parent()]); - testObject.onChange(target); + store.add(testObject.onChange(target)); testObject.batchRemove(arrayToRemove); assert.ok(target.calledOnce); @@ -428,7 +437,7 @@ suite('SearchResult', () => { const arrayToRemove = [folderMatch, fileMatch, match]; - testObject.onChange(target); + store.add(testObject.onChange(target)); await testObject.batchReplace(arrayToRemove); assert.ok(target.calledOnce); @@ -491,7 +500,7 @@ suite('SearchResult', () => { const expectedArrayResult = folderMatch.allDownstreamFileMatches(); - testObject.onChange(target); + store.add(testObject.onChange(target)); testObject.remove(folderMatch); assert.ok(target.calledOnce); assert.deepStrictEqual([{ elements: expectedArrayResult, removed: true, added: false, clearingAll: false }], target.args[0]); @@ -505,7 +514,7 @@ suite('SearchResult', () => { const expectedArrayResult = folderMatch.allDownstreamFileMatches(); - testObject.onChange(target); + store.add(testObject.onChange(target)); await testObject.batchReplace([folderMatch]); assert.deepStrictEqual([{ elements: expectedArrayResult, removed: true, added: false }], target.args[0]); @@ -520,13 +529,17 @@ suite('SearchResult', () => { results: lineMatches }; const root = searchResult?.folderMatches()[0]; - return instantiationService.createInstance(FileMatch, { + const fileMatch = instantiationService.createInstance(FileMatch, { pattern: '' }, undefined, undefined, root, rawMatch, null, ''); + + store.add(fileMatch); + return fileMatch; } function aSearchResult(): SearchResult { const searchModel = instantiationService.createInstance(SearchModel); + store.add(searchModel); searchModel.searchResult.query = { type: QueryType.Text, folderQueries: [{ folder: createFileUriFromPathFromRoot() }], contentPattern: { pattern: '' @@ -551,14 +564,18 @@ suite('SearchResult', () => { const config = new TestConfigurationService(); config.setUserConfiguration('search', { searchOnType: true }); instantiationService.stub(IConfigurationService, config); - return instantiationService.createInstance(ModelService); + const modelService = instantiationService.createInstance(ModelService); + store.add(modelService); + return modelService; } function stubNotebookEditorService(instantiationService: TestInstantiationService): INotebookEditorService { instantiationService.stub(IEditorGroupsService, new TestEditorGroupsService()); instantiationService.stub(IContextKeyService, new MockContextKeyService()); instantiationService.stub(IEditorService, new TestEditorService()); - return instantiationService.createInstance(NotebookEditorWidgetService); + const notebookEditorWidgetService = instantiationService.createInstance(NotebookEditorWidgetService); + store.add(notebookEditorWidgetService); + return notebookEditorWidgetService; } function getPopulatedSearchResult() { diff --git a/src/vs/workbench/contrib/search/test/browser/searchTestCommon.ts b/src/vs/workbench/contrib/search/test/browser/searchTestCommon.ts index 71e860b9f75..54ace56ea38 100644 --- a/src/vs/workbench/contrib/search/test/browser/searchTestCommon.ts +++ b/src/vs/workbench/contrib/search/test/browser/searchTestCommon.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { IDisposable } from 'vs/base/common/lifecycle'; import { isWindows } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; import { IModelService } from 'vs/editor/common/services/model'; @@ -43,19 +44,23 @@ export function getRootName(): string { } } -export function stubModelService(instantiationService: TestInstantiationService): IModelService { +export function stubModelService(instantiationService: TestInstantiationService, addDisposable: (e: IDisposable) => void): IModelService { instantiationService.stub(IThemeService, new TestThemeService()); const config = new TestConfigurationService(); config.setUserConfiguration('search', { searchOnType: true }); instantiationService.stub(IConfigurationService, config); - return instantiationService.createInstance(ModelService); + const modelService = instantiationService.createInstance(ModelService); + addDisposable(modelService); + return modelService; } -export function stubNotebookEditorService(instantiationService: TestInstantiationService): INotebookEditorService { +export function stubNotebookEditorService(instantiationService: TestInstantiationService, addDisposable: (e: IDisposable) => void): INotebookEditorService { instantiationService.stub(IEditorGroupsService, new TestEditorGroupsService()); instantiationService.stub(IContextKeyService, new MockContextKeyService()); instantiationService.stub(IEditorService, new TestEditorService()); - return instantiationService.createInstance(NotebookEditorWidgetService); + const notebookEditorWidgetService = instantiationService.createInstance(NotebookEditorWidgetService); + addDisposable(notebookEditorWidgetService); + return notebookEditorWidgetService; } export function addToSearchResult(searchResult: SearchResult, allRaw: IFileMatch[], searchInstanceID = '') { diff --git a/src/vs/workbench/contrib/search/test/browser/searchViewlet.test.ts b/src/vs/workbench/contrib/search/test/browser/searchViewlet.test.ts index f73f00799f6..76b49f5f2bf 100644 --- a/src/vs/workbench/contrib/search/test/browser/searchViewlet.test.ts +++ b/src/vs/workbench/contrib/search/test/browser/searchViewlet.test.ts @@ -6,16 +6,11 @@ import * as assert from 'assert'; import { URI } from 'vs/base/common/uri'; import { ILanguageConfigurationService } from 'vs/editor/common/languages/languageConfigurationRegistry'; import { IModelService } from 'vs/editor/common/services/model'; -import { ModelService } from 'vs/editor/common/services/modelService'; import { TestLanguageConfigurationService } from 'vs/editor/test/common/modes/testLanguageConfigurationService'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { FileService } from 'vs/platform/files/common/fileService'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { ILabelService } from 'vs/platform/label/common/label'; import { ILogService, NullLogService } from 'vs/platform/log/common/log'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; import { UriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentityService'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; @@ -25,25 +20,25 @@ import { MockLabelService } from 'vs/workbench/services/label/test/common/mockLa import { IFileMatch, ITextSearchMatch, OneLineRange, QueryType, SearchSortOrder } from 'vs/workbench/services/search/common/search'; import { TestContextService } from 'vs/workbench/test/common/workbenchTestServices'; import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/services/notebookEditorService'; -import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { TestEditorGroupsService, TestEditorService } from 'vs/workbench/test/browser/workbenchTestServices'; -import { NotebookEditorWidgetService } from 'vs/workbench/contrib/notebook/browser/services/notebookEditorServiceImpl'; -import { createFileUriFromPathFromRoot, getRootName } from 'vs/workbench/contrib/search/test/browser/searchTestCommon'; -import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { createFileUriFromPathFromRoot, getRootName, stubModelService, stubNotebookEditorService } from 'vs/workbench/contrib/search/test/browser/searchTestCommon'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('Search - Viewlet', () => { let instantiation: TestInstantiationService; + const store = ensureNoDisposablesAreLeakedInTestSuite(); setup(() => { instantiation = new TestInstantiationService(); instantiation.stub(ILanguageConfigurationService, TestLanguageConfigurationService); - instantiation.stub(IModelService, stubModelService(instantiation)); - instantiation.stub(INotebookEditorService, stubNotebookEditorService(instantiation)); + instantiation.stub(IModelService, stubModelService(instantiation, (e) => store.add(e))); + instantiation.stub(INotebookEditorService, stubNotebookEditorService(instantiation, (e) => store.add(e))); instantiation.set(IWorkspaceContextService, new TestContextService(TestWorkspace)); - instantiation.stub(IUriIdentityService, new UriIdentityService(new FileService(new NullLogService()))); + const fileService = new FileService(new NullLogService()); + store.add(fileService); + const uriIdentityService = new UriIdentityService(fileService); + store.add(uriIdentityService); + instantiation.stub(IUriIdentityService, uriIdentityService); instantiation.stub(ILabelService, new MockLabelService()); instantiation.stub(ILogService, new NullLogService()); }); @@ -182,22 +177,29 @@ suite('Search - Viewlet', () => { resource: URI.file('/' + path), results: lineMatches }; - return instantiation.createInstance(FileMatch, { + const fileMatch = instantiation.createInstance(FileMatch, { pattern: '' }, undefined, undefined, parentFolder ?? aFolderMatch('', 0), rawMatch, null, ''); + store.add(fileMatch); + return fileMatch; } function aFolderMatch(path: string, index: number, parent?: SearchResult): FolderMatch { const searchModel = instantiation.createInstance(SearchModel); - return instantiation.createInstance(FolderMatch, createFileUriFromPathFromRoot(path), path, index, { + store.add(searchModel); + const folderMatch = instantiation.createInstance(FolderMatch, createFileUriFromPathFromRoot(path), path, index, { type: QueryType.Text, folderQueries: [{ folder: createFileUriFromPathFromRoot() }], contentPattern: { pattern: '' } }, parent ?? aSearchResult().folderMatches()[0], searchModel.searchResult, null); + store.add(folderMatch); + return folderMatch; } function aSearchResult(): SearchResult { const searchModel = instantiation.createInstance(SearchModel); + store.add(searchModel); + searchModel.searchResult.query = { type: QueryType.Text, folderQueries: [{ folder: createFileUriFromPathFromRoot() }], contentPattern: { pattern: '' @@ -205,21 +207,4 @@ suite('Search - Viewlet', () => { }; return searchModel.searchResult; } - - function stubModelService(instantiationService: TestInstantiationService): IModelService { - instantiationService.stub(IThemeService, new TestThemeService()); - - const config = new TestConfigurationService(); - config.setUserConfiguration('search', { searchOnType: true }); - instantiationService.stub(IConfigurationService, config); - - return instantiationService.createInstance(ModelService); - } - - function stubNotebookEditorService(instantiationService: TestInstantiationService): INotebookEditorService { - instantiationService.stub(IEditorGroupsService, new TestEditorGroupsService()); - instantiationService.stub(IContextKeyService, new MockContextKeyService()); - instantiationService.stub(IEditorService, new TestEditorService()); - return instantiationService.createInstance(NotebookEditorWidgetService); - } }); From 99f30942240658a4ea0dd405c1fdacaa01ddb9d7 Mon Sep 17 00:00:00 2001 From: Andrea Mah <31675041+andreamah@users.noreply.github.com> Date: Tue, 31 Oct 2023 12:10:03 -0700 Subject: [PATCH 30/43] Opening quick text search in a diff editor doesn't put selected text in input (#197096) Fixes #194675 --- .../contrib/search/browser/searchView.ts | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 593545994a1..f37b2f4e202 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -1281,13 +1281,6 @@ export class SearchView extends ViewPane { } editor = editor ?? this.editorService.activeTextEditorControl; - if (isDiffEditor(editor)) { - if (editor.getOriginalEditor().hasTextFocus()) { - editor = editor.getOriginalEditor(); - } else { - editor = editor.getModifiedEditor(); - } - } if (!editor) { return null; @@ -2147,7 +2140,17 @@ export function getEditorSelectionFromMatch(element: FileMatchOrMatch, viewModel return undefined; } -export function getSelectionTextFromEditor(allowUnselectedWord: boolean, editor: IEditor): string | null { +export function getSelectionTextFromEditor(allowUnselectedWord: boolean, activeEditor: IEditor): string | null { + + let editor = activeEditor; + + if (isDiffEditor(editor)) { + if (editor.getOriginalEditor().hasTextFocus()) { + editor = editor.getOriginalEditor(); + } else { + editor = editor.getModifiedEditor(); + } + } if (!isCodeEditor(editor) || !editor.hasModel()) { return null; From 4c6ab441a062e90672086183496cf9e193ea4cde Mon Sep 17 00:00:00 2001 From: David Dossett Date: Tue, 31 Oct 2023 15:50:31 -0700 Subject: [PATCH 31/43] Update pill background and foreground --- src/vs/workbench/contrib/chat/common/chatColors.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/chat/common/chatColors.ts b/src/vs/workbench/contrib/chat/common/chatColors.ts index c5a226fa28d..97c8bc85f5a 100644 --- a/src/vs/workbench/contrib/chat/common/chatColors.ts +++ b/src/vs/workbench/contrib/chat/common/chatColors.ts @@ -15,13 +15,13 @@ export const chatRequestBorder = registerColor( export const chatSlashCommandBackground = registerColor( 'chat.slashCommandBackground', - { dark: badgeBackground, light: badgeBackground, hcDark: Color.white, hcLight: badgeBackground }, + { dark: '#34414B', light: '#D2ECFF', hcDark: Color.white, hcLight: badgeBackground }, localize('chat.slashCommandBackground', 'The background color of a chat slash command.') ); export const chatSlashCommandForeground = registerColor( 'chat.slashCommandForeground', - { dark: badgeForeground, light: badgeForeground, hcDark: Color.black, hcLight: badgeForeground }, + { dark: '#40A6FF', light: '#306CA2', hcDark: Color.black, hcLight: badgeForeground }, localize('chat.slashCommandForeground', 'The foreground color of a chat slash command.') ); From fe40f853df7c75e3678c925467a400b76e538d7f Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 31 Oct 2023 16:13:59 -0700 Subject: [PATCH 32/43] Bump github notebooks to November (#197104) Bump github notebooks to november --- .vscode/notebooks/api.github-issues | 2 +- .vscode/notebooks/endgame.github-issues | 2 +- .vscode/notebooks/my-endgame.github-issues | 2 +- .vscode/notebooks/my-work.github-issues | 2 +- .vscode/notebooks/verification.github-issues | 2 +- .vscode/notebooks/vscode-dev.github-issues | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.vscode/notebooks/api.github-issues b/.vscode/notebooks/api.github-issues index be9908640bf..e2d8331cc77 100644 --- a/.vscode/notebooks/api.github-issues +++ b/.vscode/notebooks/api.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "$repo=repo:microsoft/vscode\n$milestone=milestone:\"October 2023\"\n" + "value": "$repo=repo:microsoft/vscode\n$milestone=milestone:\"November 2023\"\n" }, { "kind": 1, diff --git a/.vscode/notebooks/endgame.github-issues b/.vscode/notebooks/endgame.github-issues index 2e1c21bdb08..1b17b312fea 100644 --- a/.vscode/notebooks/endgame.github-issues +++ b/.vscode/notebooks/endgame.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-unpkg repo:microsoft/vscode-references-view repo:microsoft/vscode-anycode repo:microsoft/vscode-hexeditor repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-livepreview repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remote-repositories-github repo:microsoft/monaco-editor repo:microsoft/vscode-vsce repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-livepreview repo:microsoft/vscode-livepreview repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-l10n repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-pylint repo:microsoft/vscode-black-formatter repo:microsoft/vscode-isort repo:microsoft/lsprotocol repo:microsoft/vscode-flake8 repo:microsoft/vscode-autopep8 repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-mypy repo:microsoft/vscode-python-debugger\n\n$MILESTONE=milestone:\"October 2023\"\n" + "value": "$REPOS=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-unpkg repo:microsoft/vscode-references-view repo:microsoft/vscode-anycode repo:microsoft/vscode-hexeditor repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-livepreview repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remote-repositories-github repo:microsoft/monaco-editor repo:microsoft/vscode-vsce repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-livepreview repo:microsoft/vscode-livepreview repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-l10n repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-pylint repo:microsoft/vscode-black-formatter repo:microsoft/vscode-isort repo:microsoft/lsprotocol repo:microsoft/vscode-flake8 repo:microsoft/vscode-autopep8 repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-mypy repo:microsoft/vscode-python-debugger\n\n$MILESTONE=milestone:\"November 2023\"\n" }, { "kind": 1, diff --git a/.vscode/notebooks/my-endgame.github-issues b/.vscode/notebooks/my-endgame.github-issues index 0ae6c217798..0b3a721b43f 100644 --- a/.vscode/notebooks/my-endgame.github-issues +++ b/.vscode/notebooks/my-endgame.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-unpkg repo:microsoft/vscode-references-view repo:microsoft/vscode-anycode repo:microsoft/vscode-hexeditor repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-livepreview repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remote-repositories-github repo:microsoft/monaco-editor repo:microsoft/vscode-vsce repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-livepreview repo:microsoft/vscode-livepreview repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-l10n repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-pylint repo:microsoft/vscode-black-formatter repo:microsoft/vscode-isort repo:microsoft/lsprotocol repo:microsoft/vscode-flake8 repo:microsoft/vscode-autopep8 repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-mypy repo:microsoft/vscode-python-debugger\n\n$MILESTONE=milestone:\"October 2023\"\n\n$MINE=assignee:@me\n" + "value": "$REPOS=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-unpkg repo:microsoft/vscode-references-view repo:microsoft/vscode-anycode repo:microsoft/vscode-hexeditor repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-livepreview repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remote-repositories-github repo:microsoft/monaco-editor repo:microsoft/vscode-vsce repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-livepreview repo:microsoft/vscode-livepreview repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-l10n repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-pylint repo:microsoft/vscode-black-formatter repo:microsoft/vscode-isort repo:microsoft/lsprotocol repo:microsoft/vscode-flake8 repo:microsoft/vscode-autopep8 repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-mypy repo:microsoft/vscode-python-debugger\n\n$MILESTONE=milestone:\"November 2023\"\n\n$MINE=assignee:@me\n" }, { "kind": 1, diff --git a/.vscode/notebooks/my-work.github-issues b/.vscode/notebooks/my-work.github-issues index f1e618d194f..68aa2f090e7 100644 --- a/.vscode/notebooks/my-work.github-issues +++ b/.vscode/notebooks/my-work.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "// list of repos we work in\n$repos=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-unpkg repo:microsoft/vscode-references-view repo:microsoft/vscode-anycode repo:microsoft/vscode-hexeditor repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-livepreview repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remote-repositories-github repo:microsoft/monaco-editor repo:microsoft/vscode-vsce repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-python repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-l10n repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release\n\n// current milestone name\n$milestone=milestone:\"October 2023\"\n" + "value": "// list of repos we work in\n$repos=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-unpkg repo:microsoft/vscode-references-view repo:microsoft/vscode-anycode repo:microsoft/vscode-hexeditor repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-livepreview repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remote-repositories-github repo:microsoft/monaco-editor repo:microsoft/vscode-vsce repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-python repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-l10n repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release\n\n// current milestone name\n$milestone=milestone:\"November 2023\"\n" }, { "kind": 1, diff --git a/.vscode/notebooks/verification.github-issues b/.vscode/notebooks/verification.github-issues index f0b318a28d1..6e19a79afb9 100644 --- a/.vscode/notebooks/verification.github-issues +++ b/.vscode/notebooks/verification.github-issues @@ -12,7 +12,7 @@ { "kind": 2, "language": "github-issues", - "value": "$repos=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-unpkg repo:microsoft/vscode-references-view repo:microsoft/vscode-anycode repo:microsoft/vscode-hexeditor repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-livepreview repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remote-repositories-github repo:microsoft/monaco-editor repo:microsoft/vscode-vsce repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-livepreview repo:microsoft/vscode-livepreview repo:microsoft/vscode-python repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-l10n repo:microsoft/vscode-remote-tunnels\n$milestone=milestone:\"October 2023\"\n$closedRecently=closed:>2023-09-29\n" + "value": "$repos=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-unpkg repo:microsoft/vscode-references-view repo:microsoft/vscode-anycode repo:microsoft/vscode-hexeditor repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-livepreview repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remote-repositories-github repo:microsoft/monaco-editor repo:microsoft/vscode-vsce repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-livepreview repo:microsoft/vscode-livepreview repo:microsoft/vscode-python repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-l10n repo:microsoft/vscode-remote-tunnels\n$milestone=milestone:\"November 2023\"\n$closedRecently=closed:>2023-09-29\n" }, { "kind": 1, diff --git a/.vscode/notebooks/vscode-dev.github-issues b/.vscode/notebooks/vscode-dev.github-issues index 57c7b73bb91..314a541823f 100644 --- a/.vscode/notebooks/vscode-dev.github-issues +++ b/.vscode/notebooks/vscode-dev.github-issues @@ -2,7 +2,7 @@ { "kind": 2, "language": "github-issues", - "value": "$milestone=milestone:\"October 2023\"\n" + "value": "$milestone=milestone:\"November 2023\"\n" }, { "kind": 1, From 8a450b31e3f5c21707832176afe61eba546c3455 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 31 Oct 2023 16:25:27 -0700 Subject: [PATCH 33/43] Pull in latest markdown language server (#197105) --- extensions/markdown-language-features/server/package.json | 4 ++-- extensions/markdown-language-features/server/yarn.lock | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/extensions/markdown-language-features/server/package.json b/extensions/markdown-language-features/server/package.json index 0c8914b74e8..8d641861217 100644 --- a/extensions/markdown-language-features/server/package.json +++ b/extensions/markdown-language-features/server/package.json @@ -1,7 +1,7 @@ { "name": "vscode-markdown-languageserver", "description": "Markdown language server", - "version": "0.4.0-alpha.7", + "version": "0.4.0-alpha.8", "author": "Microsoft Corporation", "license": "MIT", "engines": { @@ -18,7 +18,7 @@ "vscode-languageserver": "^8.1.0", "vscode-languageserver-textdocument": "^1.0.8", "vscode-languageserver-types": "^3.17.3", - "vscode-markdown-languageservice": "^0.4.0-alpha.7", + "vscode-markdown-languageservice": "^0.4.0-alpha.8", "vscode-uri": "^3.0.7" }, "devDependencies": { diff --git a/extensions/markdown-language-features/server/yarn.lock b/extensions/markdown-language-features/server/yarn.lock index c3b96b3ec10..fcb85f9a94b 100644 --- a/extensions/markdown-language-features/server/yarn.lock +++ b/extensions/markdown-language-features/server/yarn.lock @@ -128,10 +128,10 @@ vscode-languageserver@^8.1.0: dependencies: vscode-languageserver-protocol "3.17.3" -vscode-markdown-languageservice@^0.4.0-alpha.7: - version "0.4.0-alpha.7" - resolved "https://registry.yarnpkg.com/vscode-markdown-languageservice/-/vscode-markdown-languageservice-0.4.0-alpha.7.tgz#485e3394ff792a6fb225e5323ba9b5170f6fa7e7" - integrity sha512-GZ8e1ewU989nLSRyQp3wXjIBFWZnu/rfo4gJxazujU2nTkR+VhPGh/ar/5pX7iHLRbESvcqgVqRQNU7zzVo/hw== +vscode-markdown-languageservice@^0.4.0-alpha.8: + version "0.4.0-alpha.8" + resolved "https://registry.yarnpkg.com/vscode-markdown-languageservice/-/vscode-markdown-languageservice-0.4.0-alpha.8.tgz#5c3aaf3b3cd3f6b309f8dfcf93cbfb1397041adc" + integrity sha512-6S6RE5s+4biWg2xk9bpwNi6GihUYQIVxdO3I+jb/XDyvfmqYVxrN86cKLF8QSbaQvX3fMuBAxBLFfX93FdJi3w== dependencies: "@vscode/l10n" "^0.0.10" node-html-parser "^6.1.5" From c43d9c433fe4214f2c4be8406abdc5f7d99c144f Mon Sep 17 00:00:00 2001 From: Andrea Mah <31675041+andreamah@users.noreply.github.com> Date: Tue, 31 Oct 2023 16:32:43 -0700 Subject: [PATCH 34/43] Quick search: No feedback when a search results in 0 results (#197117) Fixes #191799 --- .../search/browser/quickTextSearch/textSearchQuickAccess.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts b/src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts index 4ff988f0ad4..146eff9fad7 100644 --- a/src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts +++ b/src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts @@ -275,7 +275,9 @@ export class TextSearchQuickAccess extends PickerQuickAccessProvider this._getPicksFromMatches(asyncResults, MAX_FILES_SHOWN - matches.length)) + .then(asyncResults => (asyncResults.length + syncResult.length === 0) ? [{ + label: localize('noAnythingResults', "No matching results") + }] : this._getPicksFromMatches(asyncResults, MAX_FILES_SHOWN - matches.length)) .then(picks => { if (picks.length > 0) { this.searchModel.searchResult.toggleHighlights(true); From 1a3461f764c5ff26eba9447eec49ff2db4971a0d Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 1 Nov 2023 07:01:11 +0100 Subject: [PATCH 35/43] aux window - require a target window for `requestAnimationFrame` (#197126) --- src/vs/base/browser/dom.ts | 43 +++++++++---------- src/vs/base/browser/touch.ts | 24 +++++------ .../ui/breadcrumbs/breadcrumbsWidget.ts | 6 +-- src/vs/base/browser/ui/list/listView.ts | 8 ++-- src/vs/base/browser/ui/menu/menubar.ts | 2 +- .../browser/ui/scrollbar/scrollableElement.ts | 4 +- src/vs/base/browser/ui/splitview/splitview.ts | 4 +- .../browser/config/elementSizeObserver.ts | 5 ++- .../editor/browser/controller/mouseHandler.ts | 4 +- src/vs/editor/browser/view.ts | 2 +- .../viewParts/viewCursors/viewCursor.ts | 2 +- .../editor/browser/widget/codeEditorWidget.ts | 2 +- .../widget/diffEditor/diffEditorWidget.ts | 3 +- .../widget/diffEditor/lineAlignment.ts | 5 ++- .../editor/browser/widget/diffEditor/utils.ts | 4 +- .../contrib/suggest/browser/suggestWidget.ts | 4 +- .../browser/parts/editor/editorStatus.ts | 4 +- .../parts/editor/multiEditorTabsControl.ts | 23 +++++++--- .../notifications/notificationsToasts.ts | 4 +- .../contrib/chat/browser/chatListRenderer.ts | 4 +- .../contrib/chat/browser/chatWidget.ts | 4 +- .../contrib/comments/browser/commentNode.ts | 4 +- .../notebook/browser/diff/diffComponents.ts | 6 +-- .../browser/diff/notebookDiffEditor.ts | 6 +-- .../browser/diff/notebookDiffOverviewRuler.ts | 2 +- .../notebook/browser/notebookEditorWidget.ts | 6 +-- .../contrib/notebook/browser/view/cellPart.ts | 15 ++++--- .../browser/view/cellParts/codeCell.ts | 4 +- .../notebook/browser/view/notebookCellList.ts | 6 +-- .../browser/view/renderers/cellRenderer.ts | 4 +- .../notebookEditorWidgetContextKeys.ts | 2 +- .../testing/browser/testingOutputPeek.ts | 2 +- .../electron-sandbox/nativeHostService.ts | 6 +-- .../suggest/browser/simpleSuggestWidget.ts | 2 +- 34 files changed, 119 insertions(+), 107 deletions(-) diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index 1949053395a..0fd241f3313 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -176,14 +176,14 @@ export function addDisposableGenericMouseUpListener(node: EventTarget, handler: * If currently in an animation frame, `runner` will be executed immediately. * @return token that can be used to cancel the scheduled runner (only if `runner` was not executed immediately). */ -export let runAtThisOrScheduleAtNextAnimationFrame: (runner: () => void, priority?: number) => IDisposable; +export let runAtThisOrScheduleAtNextAnimationFrame: (runner: () => void, targetWindow: Window, priority?: number) => IDisposable; /** * Schedule a callback to be run at the next animation frame. * This allows multiple parties to register callbacks that should run at the next animation frame. * If currently in an animation frame, `runner` will be executed at the next animation frame. * @return token that can be used to cancel the scheduled runner. */ -export let scheduleAtNextAnimationFrame: (runner: () => void, priority?: number) => IDisposable; +export let scheduleAtNextAnimationFrame: (runner: () => void, targetWindow: Window, priority?: number) => IDisposable; class AnimationFrameQueueItem implements IDisposable { @@ -252,35 +252,35 @@ class AnimationFrameQueueItem implements IDisposable { inAnimationFrameRunner = false; }; - scheduleAtNextAnimationFrame = (runner: () => void, priority: number = 0) => { + scheduleAtNextAnimationFrame = (runner: () => void, targetWindow: Window, priority: number = 0) => { const item = new AnimationFrameQueueItem(runner, priority); NEXT_QUEUE.push(item); if (!animFrameRequested) { animFrameRequested = true; - requestAnimationFrame(animationFrameRunner); + targetWindow.requestAnimationFrame(animationFrameRunner); } return item; }; - runAtThisOrScheduleAtNextAnimationFrame = (runner: () => void, priority?: number) => { + runAtThisOrScheduleAtNextAnimationFrame = (runner: () => void, targetWindow: Window, priority?: number) => { if (inAnimationFrameRunner) { const item = new AnimationFrameQueueItem(runner, priority); CURRENT_QUEUE!.push(item); return item; } else { - return scheduleAtNextAnimationFrame(runner, priority); + return scheduleAtNextAnimationFrame(runner, targetWindow, priority); } }; })(); -export function measure(callback: () => void): IDisposable { - return scheduleAtNextAnimationFrame(callback, 10000 /* must be early */); +export function measure(callback: () => void, targetWindow: Window): IDisposable { + return scheduleAtNextAnimationFrame(callback, targetWindow, 10000 /* must be early */); } -export function modify(callback: () => void): IDisposable { - return scheduleAtNextAnimationFrame(callback, -10000 /* must be late */); +export function modify(callback: () => void, targetWindow: Window): IDisposable { + return scheduleAtNextAnimationFrame(callback, targetWindow, -10000 /* must be late */); } /** @@ -780,16 +780,15 @@ export function getActiveWindow(): WindowGlobal { return document.defaultView?.window ?? window; } -export function getWindow(element: Node): WindowGlobal; -export function getWindow(event: UIEvent): WindowGlobal; -export function getWindow(obj: unknown): WindowGlobal; +export function getWindow(element: Node | undefined | null): WindowGlobal; +export function getWindow(event: UIEvent | undefined | null): WindowGlobal; export function getWindow(e: unknown): WindowGlobal { - const candidateNode = e as Node | undefined; + const candidateNode = e as Node | undefined | null; if (candidateNode?.ownerDocument?.defaultView) { return candidateNode.ownerDocument.defaultView.window; } - const candidateEvent = e as UIEvent | undefined; + const candidateEvent = e as UIEvent | undefined | null; if (candidateEvent?.view) { return candidateEvent.view.window; } @@ -996,22 +995,22 @@ function isCSSStyleRule(rule: CSSRule): rule is CSSStyleRule { export function isMouseEvent(e: unknown): e is MouseEvent { // eslint-disable-next-line no-restricted-syntax - return e instanceof MouseEvent || e instanceof getWindow(e).MouseEvent; + return e instanceof MouseEvent || e instanceof getWindow(e as UIEvent).MouseEvent; } export function isKeyboardEvent(e: unknown): e is KeyboardEvent { // eslint-disable-next-line no-restricted-syntax - return e instanceof KeyboardEvent || e instanceof getWindow(e).KeyboardEvent; + return e instanceof KeyboardEvent || e instanceof getWindow(e as UIEvent).KeyboardEvent; } export function isPointerEvent(e: unknown): e is PointerEvent { // eslint-disable-next-line no-restricted-syntax - return e instanceof PointerEvent || e instanceof getWindow(e).PointerEvent; + return e instanceof PointerEvent || e instanceof getWindow(e as UIEvent).PointerEvent; } export function isDragEvent(e: unknown): e is DragEvent { // eslint-disable-next-line no-restricted-syntax - return e instanceof DragEvent || e instanceof getWindow(e).DragEvent; + return e instanceof DragEvent || e instanceof getWindow(e as UIEvent).DragEvent; } export const EventType = { @@ -1463,13 +1462,13 @@ export function windowOpenWithSuccess(url: string, noOpener = true): boolean { return false; } -export function animate(fn: () => void): IDisposable { +export function animate(fn: () => void, targetWindow: Window): IDisposable { const step = () => { fn(); - stepDisposable = scheduleAtNextAnimationFrame(step); + stepDisposable = scheduleAtNextAnimationFrame(step, targetWindow); }; - let stepDisposable = scheduleAtNextAnimationFrame(step); + let stepDisposable = scheduleAtNextAnimationFrame(step, targetWindow); return toDisposable(() => stepDisposable.dispose()); } diff --git a/src/vs/base/browser/touch.ts b/src/vs/base/browser/touch.ts index dd74dca3eb4..23607237cd2 100644 --- a/src/vs/base/browser/touch.ts +++ b/src/vs/base/browser/touch.ts @@ -93,7 +93,7 @@ export class Gesture extends Disposable { this._register(EventUtils.runAndSubscribe(DomUtils.onDidRegisterWindow, ({ window, disposables }) => { disposables.add(DomUtils.addDisposableListener(window.document, 'touchstart', (e: TouchEvent) => this.onTouchStart(e), { passive: false })); - disposables.add(DomUtils.addDisposableListener(window.document, 'touchend', (e: TouchEvent) => this.onTouchEnd(e))); + disposables.add(DomUtils.addDisposableListener(window.document, 'touchend', (e: TouchEvent) => this.onTouchEnd(window, e))); disposables.add(DomUtils.addDisposableListener(window.document, 'touchmove', (e: TouchEvent) => this.onTouchMove(e), { passive: false })); }, { window, disposables: this._store })); } @@ -173,7 +173,7 @@ export class Gesture extends Disposable { } } - private onTouchEnd(e: TouchEvent): void { + private onTouchEnd(targetWindow: Window, e: TouchEvent): void { const timestamp = Date.now(); // use Date.now() because on FF e.timeStamp is not epoch based. const activeTouchCount = Object.keys(this.activeTouches).length; @@ -218,13 +218,13 @@ export class Gesture extends Disposable { // We need to get all the dispatch targets on the start of the inertia event const dispatchTo = [...this.targets].filter(t => data.initialTarget instanceof Node && t.contains(data.initialTarget)); - this.inertia(dispatchTo, timestamp, // time now - Math.abs(deltaX) / deltaT, // speed - deltaX > 0 ? 1 : -1, // x direction - finalX, // x now - Math.abs(deltaY) / deltaT, // y speed - deltaY > 0 ? 1 : -1, // y direction - finalY // y now + this.inertia(targetWindow, dispatchTo, timestamp, // time now + Math.abs(deltaX) / deltaT, // speed + deltaX > 0 ? 1 : -1, // x direction + finalX, // x now + Math.abs(deltaY) / deltaT, // y speed + deltaY > 0 ? 1 : -1, // y direction + finalY // y now ); } @@ -282,7 +282,7 @@ export class Gesture extends Disposable { } } - private inertia(dispatchTo: readonly EventTarget[], t1: number, vX: number, dirX: number, x: number, vY: number, dirY: number, y: number): void { + private inertia(targetWindow: Window, dispatchTo: readonly EventTarget[], t1: number, vX: number, dirX: number, x: number, vY: number, dirY: number, y: number): void { this.handle = DomUtils.scheduleAtNextAnimationFrame(() => { const now = Date.now(); @@ -311,9 +311,9 @@ export class Gesture extends Disposable { dispatchTo.forEach(d => d.dispatchEvent(evt)); if (!stopped) { - this.inertia(dispatchTo, now, vX, dirX, x + delta_pos_x, vY, dirY, y + delta_pos_y); + this.inertia(targetWindow, dispatchTo, now, vX, dirX, x + delta_pos_x, vY, dirY, y + delta_pos_y); } - }); + }, targetWindow); } private onTouchMove(e: TouchEvent): void { diff --git a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts index 3775611c73a..6c4edfef56d 100644 --- a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts +++ b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts @@ -128,7 +128,7 @@ export class BreadcrumbsWidget { this._domNode.style.width = `${dim.width}px`; this._domNode.style.height = `${dim.height}px`; disposables.add(this._updateScrollbar()); - })); + }, dom.getWindow(this._domNode))); return disposables; } @@ -138,8 +138,8 @@ export class BreadcrumbsWidget { this._scrollable.setRevealOnScroll(false); this._scrollable.scanDomNode(); this._scrollable.setRevealOnScroll(true); - }); - }); + }, dom.getWindow(this._domNode)); + }, dom.getWindow(this._domNode)); } private _style(styleElement: HTMLStyleElement, style: IBreadcrumbsWidgetStyles): void { diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index 9090f95fa79..24af99d7e50 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { DataTransfers, IDragAndDropData } from 'vs/base/browser/dnd'; -import { $, addDisposableListener, animate, Dimension, getContentHeight, getContentWidth, getTopLeftOffset, scheduleAtNextAnimationFrame } from 'vs/base/browser/dom'; +import { $, addDisposableListener, animate, Dimension, getContentHeight, getContentWidth, getTopLeftOffset, getWindow, scheduleAtNextAnimationFrame } from 'vs/base/browser/dom'; import { DomEmitter } from 'vs/base/browser/event'; import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; import { EventType as TouchEventType, Gesture, GestureEvent } from 'vs/base/browser/touch'; @@ -407,7 +407,7 @@ export class ListView implements IListView { this.scrollable = this.disposables.add(new Scrollable({ forceIntegerValues: true, smoothScrollDuration: (options.smoothScrolling ?? false) ? 125 : 0, - scheduleAtNextAnimationFrame: cb => scheduleAtNextAnimationFrame(cb) + scheduleAtNextAnimationFrame: cb => scheduleAtNextAnimationFrame(cb, getWindow(this.domNode)) })); this.scrollableElement = this.disposables.add(new SmoothScrollableElement(this.rowsContainer, { alwaysConsumeMouseWheel: options.alwaysConsumeMouseWheel ?? DefaultOptions.alwaysConsumeMouseWheel, @@ -684,7 +684,7 @@ export class ListView implements IListView { this.scrollableElement.setScrollDimensions({ scrollHeight: this.scrollHeight }); this.updateScrollWidth(); this.scrollableElementUpdateDisposable = null; - }); + }, getWindow(this.domNode)); } } @@ -1291,7 +1291,7 @@ export class ListView implements IListView { private setupDragAndDropScrollTopAnimation(event: DragEvent): void { if (!this.dragOverAnimationDisposable) { const viewTop = getTopLeftOffset(this.domNode).top; - this.dragOverAnimationDisposable = animate(this.animateDragAndDropScrollTop.bind(this, viewTop)); + this.dragOverAnimationDisposable = animate(this.animateDragAndDropScrollTop.bind(this, viewTop), getWindow(this.domNode)); } this.dragOverAnimationStopDisposable.dispose(); diff --git a/src/vs/base/browser/ui/menu/menubar.ts b/src/vs/base/browser/ui/menu/menubar.ts index ce52caea6f1..58b3f29af0f 100644 --- a/src/vs/base/browser/ui/menu/menubar.ts +++ b/src/vs/base/browser/ui/menu/menubar.ts @@ -630,7 +630,7 @@ export class MenuBar extends Disposable { this.overflowLayoutScheduled = DOM.scheduleAtNextAnimationFrame(() => { this.updateOverflowAction(); this.overflowLayoutScheduled = undefined; - }); + }, DOM.getWindow(this.container)); } this.setUnfocusedState(); diff --git a/src/vs/base/browser/ui/scrollbar/scrollableElement.ts b/src/vs/base/browser/ui/scrollbar/scrollableElement.ts index 6cb673170c8..ac02cd0c567 100644 --- a/src/vs/base/browser/ui/scrollbar/scrollableElement.ts +++ b/src/vs/base/browser/ui/scrollbar/scrollableElement.ts @@ -576,7 +576,7 @@ export class ScrollableElement extends AbstractScrollableElement { const scrollable = new Scrollable({ forceIntegerValues: true, smoothScrollDuration: 0, - scheduleAtNextAnimationFrame: (callback) => dom.scheduleAtNextAnimationFrame(callback) + scheduleAtNextAnimationFrame: (callback) => dom.scheduleAtNextAnimationFrame(callback, dom.getWindow(element)) }); super(element, options, scrollable); this._register(scrollable); @@ -621,7 +621,7 @@ export class DomScrollableElement extends AbstractScrollableElement { const scrollable = new Scrollable({ forceIntegerValues: false, // See https://github.com/microsoft/vscode/issues/139877 smoothScrollDuration: 0, - scheduleAtNextAnimationFrame: (callback) => dom.scheduleAtNextAnimationFrame(callback) + scheduleAtNextAnimationFrame: (callback) => dom.scheduleAtNextAnimationFrame(callback, dom.getWindow(element)) }); super(element, options, scrollable); this._register(scrollable); diff --git a/src/vs/base/browser/ui/splitview/splitview.ts b/src/vs/base/browser/ui/splitview/splitview.ts index 8f63b6039a1..e88947b044e 100644 --- a/src/vs/base/browser/ui/splitview/splitview.ts +++ b/src/vs/base/browser/ui/splitview/splitview.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { $, addDisposableListener, append, scheduleAtNextAnimationFrame } from 'vs/base/browser/dom'; +import { $, addDisposableListener, append, getWindow, scheduleAtNextAnimationFrame } from 'vs/base/browser/dom'; import { DomEmitter } from 'vs/base/browser/event'; import { ISashEvent as IBaseSashEvent, Orientation, Sash, SashState } from 'vs/base/browser/ui/sash/sash'; import { SmoothScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; @@ -584,7 +584,7 @@ export class SplitView scheduleAtNextAnimationFrame(callback, getWindow(this.el)), })); this.scrollableElement = this._register(new SmoothScrollableElement(this.viewContainer, { vertical: this.orientation === Orientation.VERTICAL ? (options.scrollbarVisibility ?? ScrollbarVisibility.Auto) : ScrollbarVisibility.Hidden, diff --git a/src/vs/editor/browser/config/elementSizeObserver.ts b/src/vs/editor/browser/config/elementSizeObserver.ts index f6b344f1872..a7da5f289ed 100644 --- a/src/vs/editor/browser/config/elementSizeObserver.ts +++ b/src/vs/editor/browser/config/elementSizeObserver.ts @@ -6,6 +6,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { IDimension } from 'vs/editor/common/core/dimension'; import { Emitter, Event } from 'vs/base/common/event'; +import { getWindow, scheduleAtNextAnimationFrame } from 'vs/base/browser/dom'; export class ElementSizeObserver extends Disposable { @@ -65,10 +66,10 @@ export class ElementSizeObserver extends Disposable { alreadyObservedThisAnimationFrame = true; observeNow(); } finally { - requestAnimationFrame(() => { + scheduleAtNextAnimationFrame(() => { alreadyObservedThisAnimationFrame = false; update(); - }); + }, getWindow(this._referenceDomElement)); } } }; diff --git a/src/vs/editor/browser/controller/mouseHandler.ts b/src/vs/editor/browser/controller/mouseHandler.ts index cc23b026471..6796378114d 100644 --- a/src/vs/editor/browser/controller/mouseHandler.ts +++ b/src/vs/editor/browser/controller/mouseHandler.ts @@ -682,7 +682,7 @@ class TopBottomDragScrollingOperation extends Disposable { this._position = position; this._mouseEvent = mouseEvent; this._lastTime = Date.now(); - this._animationFrameDisposable = dom.scheduleAtNextAnimationFrame(() => this._execute()); + this._animationFrameDisposable = dom.scheduleAtNextAnimationFrame(() => this._execute(), dom.getWindow(mouseEvent.browserEvent)); } public override dispose(): void { @@ -752,7 +752,7 @@ class TopBottomDragScrollingOperation extends Disposable { } this._dispatchMouse(mouseTarget, true, NavigationCommandRevealType.None); - this._animationFrameDisposable = dom.scheduleAtNextAnimationFrame(() => this._execute()); + this._animationFrameDisposable = dom.scheduleAtNextAnimationFrame(() => this._execute(), dom.getWindow(mouseTarget.element)); } } diff --git a/src/vs/editor/browser/view.ts b/src/vs/editor/browser/view.ts index ff4b7514f27..700b1d55b1d 100644 --- a/src/vs/editor/browser/view.ts +++ b/src/vs/editor/browser/view.ts @@ -432,7 +432,7 @@ export class View extends ViewEventHandler { private _scheduleRender(): void { if (this._renderAnimationFrame === null) { - this._renderAnimationFrame = dom.runAtThisOrScheduleAtNextAnimationFrame(this._onRenderScheduled.bind(this), 100); + this._renderAnimationFrame = dom.runAtThisOrScheduleAtNextAnimationFrame(this._onRenderScheduled.bind(this), dom.getWindow(this.domNode.domNode), 100); } } diff --git a/src/vs/editor/browser/viewParts/viewCursors/viewCursor.ts b/src/vs/editor/browser/viewParts/viewCursors/viewCursor.ts index a09df64b633..36506e9fed0 100644 --- a/src/vs/editor/browser/viewParts/viewCursors/viewCursor.ts +++ b/src/vs/editor/browser/viewParts/viewCursors/viewCursor.ts @@ -146,7 +146,7 @@ export class ViewCursor { return null; } - const window = dom.getWindow(this._domNode); + const window = dom.getWindow(this._domNode.domNode); let width: number; if (this._cursorStyle === TextEditorCursorStyle.Line) { width = dom.computeScreenAwareSize(window, this._lineCursorWidth > 0 ? this._lineCursorWidth : 2); diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index c1ea768376a..93407998bc0 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -1648,7 +1648,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE model, DOMLineBreaksComputerFactory.create(), MonospaceLineBreaksComputerFactory.create(this._configuration.options), - (callback) => dom.scheduleAtNextAnimationFrame(callback), + (callback) => dom.scheduleAtNextAnimationFrame(callback, dom.getWindow(this._domElement)), this.languageConfigurationService, this._themeService, attachedView, diff --git a/src/vs/editor/browser/widget/diffEditor/diffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditor/diffEditorWidget.ts index b125fb00c01..1a4a07758c5 100644 --- a/src/vs/editor/browser/widget/diffEditor/diffEditorWidget.ts +++ b/src/vs/editor/browser/widget/diffEditor/diffEditorWidget.ts @@ -2,7 +2,7 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { $, h } from 'vs/base/browser/dom'; +import { $, getWindow, h } from 'vs/base/browser/dom'; import { IBoundarySashes } from 'vs/base/browser/ui/sash/sash'; import { findLast } from 'vs/base/common/arraysFind'; import { onUnexpectedError } from 'vs/base/common/errors'; @@ -178,6 +178,7 @@ export class DiffEditorWidget extends DelegatingEditor implements IDiffEditor { /** @description ViewZoneManager */ store.add(this._instantiationService.createInstance( readHotReloadableExport(ViewZoneManager, reader), + getWindow(this._domElement), this._editors, this._diffModel, this._options, diff --git a/src/vs/editor/browser/widget/diffEditor/lineAlignment.ts b/src/vs/editor/browser/widget/diffEditor/lineAlignment.ts index 893e4000c93..698734aeba0 100644 --- a/src/vs/editor/browser/widget/diffEditor/lineAlignment.ts +++ b/src/vs/editor/browser/widget/diffEditor/lineAlignment.ts @@ -42,14 +42,15 @@ export class ViewZoneManager extends Disposable { private readonly _originalTopPadding = observableValue(this, 0); private readonly _originalScrollTop: IObservable; private readonly _originalScrollOffset = observableValue(this, 0); - private readonly _originalScrollOffsetAnimated = animatedObservable(this._originalScrollOffset, this._store); + private readonly _originalScrollOffsetAnimated = animatedObservable(this._targetWindow, this._originalScrollOffset, this._store); private readonly _modifiedTopPadding = observableValue(this, 0); private readonly _modifiedScrollTop: IObservable; private readonly _modifiedScrollOffset = observableValue(this, 0); - private readonly _modifiedScrollOffsetAnimated = animatedObservable(this._modifiedScrollOffset, this._store); + private readonly _modifiedScrollOffsetAnimated = animatedObservable(this._targetWindow, this._modifiedScrollOffset, this._store); constructor( + private readonly _targetWindow: Window, private readonly _editors: DiffEditorEditors, private readonly _diffModel: IObservable, private readonly _options: DiffEditorOptions, diff --git a/src/vs/editor/browser/widget/diffEditor/utils.ts b/src/vs/editor/browser/widget/diffEditor/utils.ts index 352ff672edc..81b00083ac7 100644 --- a/src/vs/editor/browser/widget/diffEditor/utils.ts +++ b/src/vs/editor/browser/widget/diffEditor/utils.ts @@ -123,7 +123,7 @@ export class ObservableElementSizeObserver extends Disposable { } } -export function animatedObservable(base: IObservable, store: DisposableStore): IObservable { +export function animatedObservable(targetWindow: Window, base: IObservable, store: DisposableStore): IObservable { let targetVal = base.get(); let startVal = targetVal; let curVal = targetVal; @@ -160,7 +160,7 @@ export function animatedObservable(base: IObservable, store: Di curVal = Math.floor(easeOutExpo(passedMs, startVal, targetVal - startVal, durationMs)); if (passedMs < durationMs) { - animationFrame = requestAnimationFrame(update); + animationFrame = targetWindow.requestAnimationFrame(update); } else { curVal = targetVal; } diff --git a/src/vs/editor/contrib/suggest/browser/suggestWidget.ts b/src/vs/editor/contrib/suggest/browser/suggestWidget.ts index b86e2fd94ef..aa78d74936a 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestWidget.ts @@ -571,7 +571,7 @@ export class SuggestWidget implements IDisposable { this._layout(this.element.size); // Reset focus border this._details.widget.domNode.classList.remove('focused'); - }); + }, dom.getWindow(this.element.domNode)); } focusSelected(): void { @@ -721,7 +721,7 @@ export class SuggestWidget implements IDisposable { this._positionDetails(); this.editor.focus(); this.element.domNode.classList.add('shows-details'); - }); + }, dom.getWindow(this.element.domNode)); } toggleExplainMode(): void { diff --git a/src/vs/workbench/browser/parts/editor/editorStatus.ts b/src/vs/workbench/browser/parts/editor/editorStatus.ts index 4a1b22ae99f..d019690429b 100644 --- a/src/vs/workbench/browser/parts/editor/editorStatus.ts +++ b/src/vs/workbench/browser/parts/editor/editorStatus.ts @@ -315,7 +315,6 @@ const nlsMultiSelection = localize('multiSelection', "{0} selections"); const nlsEOLLF = localize('endOfLineLineFeed', "LF"); const nlsEOLCRLF = localize('endOfLineCarriageReturnLineFeed', "CRLF"); - export class EditorStatus extends Disposable implements IWorkbenchContribution { private readonly tabFocusModeElement = this._register(new MutableDisposable()); @@ -335,6 +334,7 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution { private toRender: StateChange | undefined = undefined; private editorService: IEditorService; + private targetWindow = window; constructor( @IEditorService editorService: IEditorService, @@ -570,7 +570,7 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution { if (toRender) { this.doRenderNow(); } - }); + }, this.targetWindow); } else { this.toRender.combine(changed); } diff --git a/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts b/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts index 0acabfac24d..5e40698cef7 100644 --- a/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts +++ b/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts @@ -1039,7 +1039,7 @@ export class MultiEditorTabsControl extends EditorTabsControl { // Fixes https://github.com/microsoft/vscode/issues/18733 tab.classList.add('dragged'); - scheduleAtNextAnimationFrame(() => tab.classList.remove('dragged')); + scheduleAtNextAnimationFrame(() => tab.classList.remove('dragged'), getWindow(tab)); })); // Drop support @@ -1610,16 +1610,25 @@ export class MultiEditorTabsControl extends EditorTabsControl { Object.assign(this.dimensions, dimensions); if (this.visible) { - // The layout of tabs can be an expensive operation because we access DOM properties - // that can result in the browser doing a full page layout to validate them. To buffer - // this a little bit we try at least to schedule this work on the next animation frame - // when we have restored or when idle otherwise. if (!this.layoutScheduler.value) { - const scheduledLayout = (this.lifecycleService.phase >= LifecyclePhase.Restored ? scheduleAtNextAnimationFrame : runWhenIdle)(() => { + + // The layout of tabs can be an expensive operation because we access DOM properties + // that can result in the browser doing a full page layout to validate them. To buffer + // this a little bit we try at least to schedule this work on the next animation frame + // when we have restored or when idle otherwise. + + const layoutFunction = () => { this.doLayout(this.dimensions, this.layoutScheduler.value?.options /* ensure to pick up latest options */); this.layoutScheduler.clear(); - }); + }; + + let scheduledLayout: IDisposable; + if (this.lifecycleService.phase >= LifecyclePhase.Restored) { + scheduledLayout = scheduleAtNextAnimationFrame(layoutFunction, getWindow(this.tabsContainer)); + } else { + scheduledLayout = runWhenIdle(layoutFunction); + } this.layoutScheduler.value = { options, dispose: () => scheduledLayout.dispose() }; } diff --git a/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts b/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts index c3f0490bedd..fb0f7f5d318 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts @@ -7,7 +7,7 @@ import 'vs/css!./media/notificationsToasts'; import { localize } from 'vs/nls'; import { INotificationsModel, NotificationChangeType, INotificationChangeEvent, INotificationViewItem, NotificationViewItemContentChangeKind } from 'vs/workbench/common/notifications'; import { IDisposable, dispose, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { addDisposableListener, EventType, Dimension, scheduleAtNextAnimationFrame, isAncestorOfActiveElement } from 'vs/base/browser/dom'; +import { addDisposableListener, EventType, Dimension, scheduleAtNextAnimationFrame, isAncestorOfActiveElement, getWindow } from 'vs/base/browser/dom'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { NotificationsList } from 'vs/workbench/browser/parts/notifications/notificationsList'; import { Event, Emitter } from 'vs/base/common/event'; @@ -150,7 +150,7 @@ export class NotificationsToasts extends Themable implements INotificationsToast // (see also https://github.com/microsoft/vscode/issues/107935) const itemDisposables = new DisposableStore(); this.mapNotificationToDisposable.set(item, itemDisposables); - itemDisposables.add(scheduleAtNextAnimationFrame(() => this.doAddToast(item, itemDisposables))); + itemDisposables.add(scheduleAtNextAnimationFrame(() => this.doAddToast(item, itemDisposables), getWindow(this.container))); } private doAddToast(item: INotificationViewItem, itemDisposables: DisposableStore): void { diff --git a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts index 82c067d6b1a..c7f1c97fd14 100644 --- a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts @@ -458,7 +458,7 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer { disposable.dispose(); this._onDidChangeItemHeight.fire({ element, height: newHeight }); - })); + }, dom.getWindow(templateData.value))); } } @@ -496,7 +496,7 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer { disposable.dispose(); this._onDidChangeItemHeight.fire({ element, height: newHeight }); - })); + }, dom.getWindow(templateData.value))); } } diff --git a/src/vs/workbench/contrib/chat/browser/chatWidget.ts b/src/vs/workbench/contrib/chat/browser/chatWidget.ts index d7526a49c87..c6f79e430df 100644 --- a/src/vs/workbench/contrib/chat/browser/chatWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/chatWidget.ts @@ -407,7 +407,7 @@ export class ChatWidget extends Disposable implements IChatWidget { dom.scheduleAtNextAnimationFrame(() => { // Can't set scrollTop during this event listener, the list might overwrite the change revealLastElement(this.tree); - }, 0); + }, dom.getWindow(this.listContainer), 0); } } @@ -640,7 +640,7 @@ export class ChatWidget extends Disposable implements IChatWidget { const inputPartHeight = this.inputPart.layout(possibleMaxHeight, width); const newHeight = Math.min(renderHeight + diff, possibleMaxHeight - inputPartHeight); this.layout(newHeight + inputPartHeight, width); - }); + }, dom.getWindow(this.listContainer)); })); } diff --git a/src/vs/workbench/contrib/comments/browser/commentNode.ts b/src/vs/workbench/contrib/comments/browser/commentNode.ts index d015ba6761b..65ad313fff7 100644 --- a/src/vs/workbench/contrib/comments/browser/commentNode.ts +++ b/src/vs/workbench/contrib/comments/browser/commentNode.ts @@ -170,7 +170,7 @@ export class CommentNode extends Disposable { this._scrollable = new Scrollable({ forceIntegerValues: true, smoothScrollDuration: 125, - scheduleAtNextAnimationFrame: cb => dom.scheduleAtNextAnimationFrame(cb) + scheduleAtNextAnimationFrame: cb => dom.scheduleAtNextAnimationFrame(cb, dom.getWindow(container)) }); this._scrollableElement = this._register(new SmoothScrollableElement(body, { horizontal: ScrollbarVisibility.Visible, @@ -489,7 +489,7 @@ export class CommentNode extends Disposable { dom.scheduleAtNextAnimationFrame(() => { this._commentEditor!.layout({ width: container.clientWidth - 14, height: this._editorHeight }); this._commentEditor!.focus(); - }); + }, dom.getWindow(editContainer)); const lastLine = this._commentEditorModel.getLineCount(); const lastColumn = this._commentEditorModel.getLineLength(lastLine) + 1; diff --git a/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts index f405b53b5e0..92327e752c6 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts @@ -998,7 +998,7 @@ export class DeletedElement extends SingleSideDiffElement { } this.layoutNotebookCell(); - }); + }, DOM.getWindow(this._diffEditorContainer)); } _buildOutputRendererContainer() { @@ -1201,7 +1201,7 @@ export class InsertElement extends SingleSideDiffElement { if (this._diagonalFill) { this._diagonalFill.style.height = `${this.cell.layoutInfo.editorHeight + this.cell.layoutInfo.editorMargin + this.cell.layoutInfo.metadataStatusHeight + this.cell.layoutInfo.metadataHeight + this.cell.layoutInfo.outputTotalHeight + this.cell.layoutInfo.outputStatusHeight}px`; } - }); + }, DOM.getWindow(this._diffEditorContainer)); } override dispose() { @@ -1626,7 +1626,7 @@ export class ModifiedElement extends AbstractElementRenderer { } this.layoutNotebookCell(); - }); + }, DOM.getWindow(this._diffEditorContainer)); } override dispose() { diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditor.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditor.ts index 2d6eb9ea124..35c1e3cf692 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditor.ts @@ -463,7 +463,7 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD return diffElement.original; }, DiffSide.Original); } - }); + }, DOM.getWindow(this._listViewContainer)); }; this._localStore.add(this._list.onDidChangeContentHeight(() => { @@ -763,7 +763,7 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD DOM.scheduleAtNextAnimationFrame(() => { webview?.ackHeight([{ cellId: cellInfo.cellId, outputId, height }]); - }, 10); + }, DOM.getWindow(this._listViewContainer), 10); } private pendingLayouts = new WeakMap(); @@ -784,7 +784,7 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD relayout(cell, height); r(); - }); + }, DOM.getWindow(this._listViewContainer)); this.pendingLayouts.set(cell, toDisposable(() => { layoutDisposable.dispose(); diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffOverviewRuler.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffOverviewRuler.ts index 1d39891624f..57938e24695 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffOverviewRuler.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffOverviewRuler.ts @@ -114,7 +114,7 @@ export class NotebookDiffOverviewRuler extends Themable { private _scheduleRender(): void { if (this._renderAnimationFrame === null) { - this._renderAnimationFrame = DOM.runAtThisOrScheduleAtNextAnimationFrame(this._onRenderScheduled.bind(this), 16); + this._renderAnimationFrame = DOM.runAtThisOrScheduleAtNextAnimationFrame(this._onRenderScheduled.bind(this), DOM.getWindow(this._domNode.domNode), 16); } } diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index e2cceb4b1ec..e391c3c2d56 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -1454,7 +1454,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD this._localStore.add(DOM.scheduleAtNextAnimationFrame(() => { hasPendingChangeContentHeight = false; this._updateScrollHeight(); - }, 100)); + }, DOM.getWindow(this._body), 100)); })); this._localStore.add(this._list.onDidRemoveOutputs(outputs => { @@ -2259,7 +2259,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD }; if (this._list.inRenderingTransaction) { - const layoutDisposable = DOM.scheduleAtNextAnimationFrame(doLayout); + const layoutDisposable = DOM.scheduleAtNextAnimationFrame(doLayout, DOM.getWindow(this._body)); this._pendingLayouts?.set(cell, toDisposable(() => { layoutDisposable.dispose(); @@ -2973,7 +2973,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD this._webview?.ackHeight([...this._pendingOutputHeightAcks.values()]); this._pendingOutputHeightAcks.clear(); - }, -1); // -1 priority because this depends on calls to layoutNotebookCell, and that may be called multiple times before this runs + }, DOM.getWindow(this._body), -1); // -1 priority because this depends on calls to layoutNotebookCell, and that may be called multiple times before this runs } } diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellPart.ts b/src/vs/workbench/contrib/notebook/browser/view/cellPart.ts index ac1396b310f..32dc2032748 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellPart.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellPart.ts @@ -128,18 +128,19 @@ export class CellPartsCollection extends Disposable { private _scheduledOverlayUpdateExecutionState = this._register(new MutableDisposable()); constructor( + private readonly targetWindow: Window, private readonly contentParts: readonly CellContentPart[], private readonly overlayParts: readonly CellOverlayPart[] ) { super(); } - concatContentPart(other: readonly CellContentPart[]): CellPartsCollection { - return new CellPartsCollection(this.contentParts.concat(other), this.overlayParts); + concatContentPart(other: readonly CellContentPart[], targetWindow: Window): CellPartsCollection { + return new CellPartsCollection(targetWindow, this.contentParts.concat(other), this.overlayParts); } - concatOverlayPart(other: readonly CellOverlayPart[]): CellPartsCollection { - return new CellPartsCollection(this.contentParts, this.overlayParts.concat(other)); + concatOverlayPart(other: readonly CellOverlayPart[], targetWindow: Window): CellPartsCollection { + return new CellPartsCollection(targetWindow, this.contentParts, this.overlayParts.concat(other)); } scheduleRenderCell(element: ICellViewModel): void { @@ -161,7 +162,7 @@ export class CellPartsCollection extends Disposable { for (const part of this.overlayParts) { part.renderCell(element); } - }); + }, this.targetWindow); } unrenderCell(element: ICellViewModel): void { @@ -203,7 +204,7 @@ export class CellPartsCollection extends Disposable { for (const part of this.overlayParts) { part.updateState(viewCell, e); } - }); + }, this.targetWindow); } updateForExecutionState(viewCell: ICellViewModel, e: ICellExecutionStateChangedEvent) { @@ -215,6 +216,6 @@ export class CellPartsCollection extends Disposable { for (const part of this.overlayParts) { part.updateForExecutionState(viewCell, e); } - }); + }, this.targetWindow); } } diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCell.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCell.ts index 501569e9984..9f727ebfe88 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCell.ts @@ -58,7 +58,7 @@ export class CodeCell extends Disposable { const cellEditorOptions = this._register(new CellEditorOptions(this.notebookEditor.getBaseCellEditorOptions(viewCell.language), this.notebookEditor.notebookOptions, this.configurationService)); this._outputContainerRenderer = this.instantiationService.createInstance(CellOutputContainer, notebookEditor, viewCell, templateData, { limit: outputDisplayLimit }); - this.cellParts = this._register(templateData.cellParts.concatContentPart([cellEditorOptions, this._outputContainerRenderer])); + this.cellParts = this._register(templateData.cellParts.concatContentPart([cellEditorOptions, this._outputContainerRenderer], DOM.getWindow(notebookEditor.getDomNode()))); // this.viewCell.layoutInfo.editorHeight or estimation when this.viewCell.layoutInfo.editorHeight === 0 const editorHeight = this.calculateInitEditorHeight(); @@ -146,7 +146,7 @@ export class CodeCell extends Disposable { this._pendingLayout?.dispose(); this._pendingLayout = DOM.modify(() => { this.cellParts.updateInternalLayoutNow(this.viewCell); - }); + }, DOM.getWindow(this.templateData.container)); } private updateForOutputHover() { diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts index f54d78ac851..04f3c12567f 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts @@ -289,7 +289,7 @@ export class NotebookCellList extends WorkbenchList implements ID if (this._isInLayout) { DOM.scheduleAtNextAnimationFrame(() => { updateVisibleRanges(); - }); + }, DOM.getWindow(container)); } updateVisibleRanges(); })); @@ -297,7 +297,7 @@ export class NotebookCellList extends WorkbenchList implements ID if (this._isInLayout) { DOM.scheduleAtNextAnimationFrame(() => { updateVisibleRanges(); - }); + }, DOM.getWindow(container)); } updateVisibleRanges(); })); @@ -369,7 +369,7 @@ export class NotebookCellList extends WorkbenchList implements ID } this._updateElementsInWebview(viewDiffs); - })); + }, DOM.getWindow(this.rowsContainer))); } })); diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index d9c13b6094e..f6f8aa92455 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -162,7 +162,7 @@ export class MarkupCellRenderer extends AbstractCellRenderer implements IListRen this.notebookEditor)); const focusIndicatorBottom = new FastDomNode(DOM.append(container, $('.cell-focus-indicator.cell-focus-indicator-bottom'))); - const cellParts = new CellPartsCollection([ + const cellParts = new CellPartsCollection(DOM.getWindow(rootContainer), [ templateDisposables.add(scopedInstaService.createInstance(CellEditorStatusBar, this.notebookEditor, container, editorPart, undefined)), templateDisposables.add(new CellFocusIndicator(this.notebookEditor, titleToolbar, focusIndicatorTop, focusIndicatorLeft, focusIndicatorRight, focusIndicatorBottom)), templateDisposables.add(new FoldedCellHint(this.notebookEditor, DOM.append(container, $('.notebook-folded-hint')))), @@ -306,7 +306,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende this.notebookEditor)); const focusIndicatorPart = templateDisposables.add(new CellFocusIndicator(this.notebookEditor, titleToolbar, focusIndicatorTop, focusIndicatorLeft, focusIndicatorRight, focusIndicatorBottom)); - const cellParts = new CellPartsCollection([ + const cellParts = new CellPartsCollection(DOM.getWindow(rootContainer), [ focusIndicatorPart, templateDisposables.add(scopedInstaService.createInstance(CellEditorStatusBar, this.notebookEditor, container, editorPart, editor)), templateDisposables.add(scopedInstaService.createInstance(CellProgressBar, editorPart, cellInputCollapsedContainer)), diff --git a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorWidgetContextKeys.ts b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorWidgetContextKeys.ts index c7b0471d676..390ed06f677 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorWidgetContextKeys.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorWidgetContextKeys.ts @@ -115,7 +115,7 @@ export class NotebookEditorContextKeys { layoutDisposable.add(DOM.scheduleAtNextAnimationFrame(() => { recomputeOutputsExistence(); - })); + }, DOM.getWindow(this._editor.getDomNode()))); }); }; diff --git a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts index ade1e1091e3..072fa071e36 100644 --- a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts +++ b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts @@ -1534,7 +1534,7 @@ class TerminalMessagePeek extends Disposable implements IPeekOutputRenderer { private attachTerminalToDom(terminal: IDetachedTerminalInstance) { terminal.xterm.write('\x1b[?25l'); // hide cursor - requestAnimationFrame(() => this.layoutTerminal(terminal)); + dom.scheduleAtNextAnimationFrame(() => this.layoutTerminal(terminal), dom.getWindow(this.container)); terminal.attachToElement(this.container, { enableGpu: false }); } diff --git a/src/vs/workbench/services/host/electron-sandbox/nativeHostService.ts b/src/vs/workbench/services/host/electron-sandbox/nativeHostService.ts index a3b261ccaa1..aeec64376e7 100644 --- a/src/vs/workbench/services/host/electron-sandbox/nativeHostService.ts +++ b/src/vs/workbench/services/host/electron-sandbox/nativeHostService.ts @@ -15,7 +15,7 @@ import { NativeHostService } from 'vs/platform/native/electron-sandbox/nativeHos import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; import { IMainProcessService } from 'vs/platform/ipc/common/mainProcessService'; import { isAuxiliaryWindow } from 'vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService'; -import { getActiveDocument, getWindowsCount, onDidRegisterWindow, trackFocus } from 'vs/base/browser/dom'; +import { getActiveDocument, getWindowsCount, onDidRegisterWindow, scheduleAtNextAnimationFrame, trackFocus } from 'vs/base/browser/dom'; import { DomEmitter } from 'vs/base/browser/event'; import { memoize } from 'vs/base/common/decorators'; @@ -62,11 +62,11 @@ class WorkbenchHostService extends Disposable implements IHostService { // Workaround: the window does not immediately seem to have focus when // opening, so we schedule a check for focus on the next animation frame - window.requestAnimationFrame(() => { + scheduleAtNextAnimationFrame(() => { if (window.document.hasFocus()) { emitter.fire(true); } - }); + }, window); })); return emitter.event; diff --git a/src/vs/workbench/services/suggest/browser/simpleSuggestWidget.ts b/src/vs/workbench/services/suggest/browser/simpleSuggestWidget.ts index 7df145abc38..f3e8d1c542c 100644 --- a/src/vs/workbench/services/suggest/browser/simpleSuggestWidget.ts +++ b/src/vs/workbench/services/suggest/browser/simpleSuggestWidget.ts @@ -256,7 +256,7 @@ export class SimpleSuggestWidget implements IDisposable { this._layout(this.element.size); // Reset focus border // this._details.widget.domNode.classList.remove('focused'); - }); + }, dom.getWindow(this.element.domNode)); } setLineContext(lineContext: LineContext): void { From 9581516b7c6a612882a0ec264df7637080c9afda Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 1 Nov 2023 11:09:08 +0100 Subject: [PATCH 36/43] fix #195532 (#197135) --- .../userDataSync/browser/userDataSyncWorkbenchService.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts b/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts index 9e10ec35371..aba2a967b4b 100644 --- a/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts +++ b/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts @@ -672,12 +672,14 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat } else { sessionId = (await this.authenticationService.createSession(accountOrAuthProvider.id, accountOrAuthProvider.scopes)).id; } + this.currentAuthenticationProviderId = accountOrAuthProvider.id; } else { if (this.environmentService.options?.settingsSyncOptions?.authenticationProvider?.id === accountOrAuthProvider.authenticationProviderId) { sessionId = await this.environmentService.options?.settingsSyncOptions?.authenticationProvider?.signIn(); } else { sessionId = accountOrAuthProvider.sessionId; } + this.currentAuthenticationProviderId = accountOrAuthProvider.authenticationProviderId; } this.currentSessionId = sessionId; await this.update(); From fba54a670c0f4dbe0a555544a017d260daa1aced Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Wed, 1 Nov 2023 12:21:46 +0100 Subject: [PATCH 37/43] SCM - correctly set the context of the action bar of the view pane container (#196802) --- .../workbench/contrib/scm/browser/scmViewPaneContainer.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPaneContainer.ts b/src/vs/workbench/contrib/scm/browser/scmViewPaneContainer.ts index 09c35778b5a..c603d810d2b 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPaneContainer.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPaneContainer.ts @@ -6,7 +6,7 @@ import 'vs/css!./media/scm'; import { localize } from 'vs/nls'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { VIEWLET_ID } from 'vs/workbench/contrib/scm/common/scm'; +import { ISCMViewService, VIEWLET_ID } from 'vs/workbench/contrib/scm/common/scm'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IThemeService } from 'vs/platform/theme/common/themeService'; @@ -21,6 +21,7 @@ import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneCont export class SCMViewPaneContainer extends ViewPaneContainer { constructor( + @ISCMViewService private readonly scmViewService: ISCMViewService, @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService, @ITelemetryService telemetryService: ITelemetryService, @IInstantiationService instantiationService: IInstantiationService, @@ -47,4 +48,9 @@ export class SCMViewPaneContainer extends ViewPaneContainer { override getTitle(): string { return localize('source control', "Source Control"); } + + override getActionsContext(): unknown { + return this.scmViewService.visibleRepositories.length === 1 ? this.scmViewService.visibleRepositories[0].provider : undefined; + } + } From 11b3aa8566d2b17891c5b2c0bd588f859960489d Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Wed, 1 Nov 2023 12:30:01 +0100 Subject: [PATCH 38/43] SCM - input box polish (#196988) --- .../contrib/scm/browser/media/scm.css | 48 +++++------ .../contrib/scm/browser/scm.contribution.ts | 5 ++ .../contrib/scm/browser/scmViewPane.ts | 84 ++++++++++++------- 3 files changed, 79 insertions(+), 58 deletions(-) diff --git a/src/vs/workbench/contrib/scm/browser/media/scm.css b/src/vs/workbench/contrib/scm/browser/media/scm.css index 3b6417cc287..0d13de2bb2f 100644 --- a/src/vs/workbench/contrib/scm/browser/media/scm.css +++ b/src/vs/workbench/contrib/scm/browser/media/scm.css @@ -239,28 +239,24 @@ .scm-view .scm-input { height: 100%; + display: flex; + align-items: center; padding-left: 11px; - border-radius: 2px; } -.scm-view .scm-input .actions { - position: absolute; - top: 7px; - right: 20px; - z-index: 2; +.scm-view .scm-input .scm-editor .scm-editor-toolbar { + padding: 1px 3px 1px 1px; } -.scm-view .scm-input .actions .actions-container { - background-color: var(--vscode-input-background); +.scm-view .scm-input .scm-editor .scm-editor-toolbar.hidden { + display: none; } -.scm-view .scm-input .actions .action-label { - border-radius: 5px; - outline: 1px dashed var(--vscode-toolbar-hoverOutline); - outline-offset: -1px; +.scm-view .scm-input .scm-editor .scm-editor-toolbar.scroll-decoration { + box-shadow: var(--vscode-scrollbar-shadow) 0 6px 6px -6px inset; } -.scm-view .scm-input .actions .action-label.codicon.codicon-debug-stop { +.scm-view .scm-input .scm-editor .scm-editor-toolbar .action-label.codicon.codicon-debug-stop { color: var(--vscode-icon-foreground) !important; } @@ -275,10 +271,12 @@ .scm-view .scm-editor { box-sizing: border-box; width: 100%; - height: 100%; display: flex; - flex-direction: column; - justify-content: center; + align-items: flex-start; + box-sizing: border-box; + border: 1px solid var(--vscode-input-border, transparent); + background-color: var(--vscode-input-background); + border-radius: 2px; } .scm-view .button-container { @@ -329,31 +327,23 @@ outline: 1px solid var(--vscode-panelInput-border); } -.scm-view .scm-editor-container { - position: relative; - box-sizing: border-box; - background-color: var(--vscode-input-background); - border: 1px solid var(--vscode-input-border, transparent); - border-radius: 2px; -} - -.scm-view .scm-editor-container.synthetic-focus, -.monaco-workbench .part.panel .scm-view .scm-editor-container.synthetic-focus { +.scm-view .scm-editor.synthetic-focus, +.monaco-workbench .part.panel .scm-view .scm-editor.synthetic-focus { outline: 1px solid var(--vscode-focusBorder); outline-offset: -1px; } -.scm-view .scm-editor-container.validation-info { +.scm-view .scm-editor.validation-info { outline: 1px solid var(--vscode-inputValidation-infoBorder) !important; outline-offset: -1px; } -.scm-view .scm-editor-container.validation-warning { +.scm-view .scm-editor.validation-warning { outline: 1px solid var(--vscode-inputValidation-warningBorder) !important; outline-offset: -1px; } -.scm-view .scm-editor-container.validation-error { +.scm-view .scm-editor.validation-error { outline: 1px solid var(--vscode-inputValidation-errorBorder) !important; outline-offset: -1px; } diff --git a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts index 924f9f477a1..5df3f7ae41d 100644 --- a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts +++ b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts @@ -267,6 +267,11 @@ Registry.as(ConfigurationExtensions.Configuration).regis markdownDescription: localize('inputFontSize', "Controls the font size for the input message in pixels."), default: 13 }, + 'scm.inputMaxLines': { + type: 'number', + markdownDescription: localize('inputMaxLines', "Controls the maximum number of lines that the input will auto-grow to."), + default: 10 + }, 'scm.alwaysShowRepositories': { type: 'boolean', markdownDescription: localize('alwaysShowRepository', "Controls whether repositories should always be visible in the Source Control view."), diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index 492d2e20979..23a0f32b75e 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -96,6 +96,7 @@ import { fillEditorsDragData } from 'vs/workbench/browser/dnd'; import { ElementsDragAndDropData } from 'vs/base/browser/ui/list/listView'; import { CodeDataTransfers } from 'vs/platform/dnd/browser/dnd'; import { FormatOnType } from 'vs/editor/contrib/format/browser/formatActions'; +import { EditorOption } from 'vs/editor/common/config/editorOptions'; type TreeElement = ISCMRepository | ISCMInput | ISCMActionButton | ISCMResourceGroup | IResourceNode | ISCMResource; @@ -1808,6 +1809,7 @@ class SCMInputWidget { private editorContainer: HTMLElement; private placeholderTextContainer: HTMLElement; private inputEditor: CodeEditorWidget; + private toolbarContainer: HTMLElement; private actionBar: ActionBar; private readonly disposables = new DisposableStore(); @@ -1837,7 +1839,7 @@ class SCMInputWidget { } this.clearValidation(); - this.editorContainer.classList.remove('synthetic-focus'); + this.element.classList.remove('synthetic-focus'); this.repositoryDisposables.clear(); this.repositoryIdContextKey.set(input?.repository.id); @@ -1958,11 +1960,8 @@ class SCMInputWidget { this.repositoryDisposables.add(input.onDidChangeEnablement(enabled => updateEnablement(enabled))); updateEnablement(input.enabled); - // ActionBar + // Toolbar const onDidChangeActionButton = () => { - // Update placeholder width to accommodate for the action bar - this.placeholderTextContainer.style.width = input.actionButton ? 'calc(100% - 26px)' : '100%'; - this.actionBar.clear(); if (!input.actionButton) { return; @@ -1976,6 +1975,7 @@ class SCMInputWidget { () => this.commandService.executeCommand(input.actionButton!.command.id, ...(input.actionButton!.command.arguments || []))); this.actionBar.push(action, { icon: true, label: false }); + this.layout(); }; this.repositoryDisposables.add(input.onDidChangeActionButton(onDidChangeActionButton, this)); @@ -2027,6 +2027,7 @@ class SCMInputWidget { this.element = append(container, $('.scm-editor')); this.editorContainer = append(this.element, $('.scm-editor-container')); this.placeholderTextContainer = append(this.editorContainer, $('.scm-editor-placeholder')); + this.toolbarContainer = append(this.element, $('.scm-editor-toolbar')); const fontFamily = this.getInputEditorFontFamily(); const fontSize = this.getInputEditorFontSize(); @@ -2049,7 +2050,10 @@ class SCMInputWidget { wrappingIndent: 'none', padding: { top: 2, bottom: 2 }, quickSuggestions: false, - scrollbar: { alwaysConsumeMouseWheel: false }, + scrollbar: { + alwaysConsumeMouseWheel: false, + vertical: 'hidden' + }, overflowWidgetsDomNode, formatOnType: true, renderWhitespace: 'none', @@ -2076,9 +2080,6 @@ class SCMInputWidget { ]) }; - this.actionBar = new ActionBar(append(this.element, $('.actions'))); - this.disposables.add(this.actionBar); - const services = new ServiceCollection([IContextKeyService, contextKeyService2]); const instantiationService2 = instantiationService.createChild(services); this.inputEditor = instantiationService2.createInstance(CodeEditorWidget, this.editorContainer, editorOptions, codeEditorWidgetOptions); @@ -2089,11 +2090,11 @@ class SCMInputWidget { this.scmViewService.focus(this.input.repository); } - this.editorContainer.classList.add('synthetic-focus'); + this.element.classList.add('synthetic-focus'); this.renderValidation(); })); this.disposables.add(this.inputEditor.onDidBlurEditorText(() => { - this.editorContainer.classList.remove('synthetic-focus'); + this.element.classList.remove('synthetic-focus'); setTimeout(() => { if (!this.validation || !this.validationHasFocus) { @@ -2113,6 +2114,9 @@ class SCMInputWidget { firstLineKey.set(viewPosition.lineNumber === 1 && viewPosition.column === 1); lastLineKey.set(viewPosition.lineNumber === lastLineNumber && viewPosition.column === lastLineCol); })); + this.disposables.add(this.inputEditor.onDidScrollChange(e => { + this.toolbarContainer.classList.toggle('scroll-decoration', e.scrollTop > 0); + })); const relevantSettings = [ 'scm.inputFontFamily', @@ -2153,16 +2157,23 @@ class SCMInputWidget { })); this.onDidChangeContentHeight = Event.signal(Event.filter(this.inputEditor.onDidContentSizeChange, e => e.contentHeightChanged, this.disposables)); + + // Toolbar + this.actionBar = new ActionBar(this.toolbarContainer); + this.disposables.add(this.actionBar); } getContentHeight(): number { const editorContentHeight = this.inputEditor.getContentHeight(); - return Math.min(editorContentHeight, 134); + const editorContextHeightMax = this.getInputEditorMaxHeight(); + + return Math.min(editorContentHeight, editorContextHeightMax); } layout(): void { const editorHeight = this.getContentHeight(); - const dimension = new Dimension(this.element.clientWidth - 2, editorHeight); + const toolbarWidth = this.toolbarContainer.clientWidth; + const dimension = new Dimension(this.element.clientWidth - toolbarWidth, editorHeight); if (dimension.width < 0) { this.lastLayoutWasTrash = true; @@ -2171,6 +2182,8 @@ class SCMInputWidget { this.lastLayoutWasTrash = false; this.inputEditor.layout(dimension); + this.placeholderTextContainer.style.width = `${dimension.width}px`; + this.toolbarContainer.classList.toggle('hidden', this.input?.actionButton === undefined); this.renderValidation(); if (this.shouldFocusAfterLayout) { @@ -2187,7 +2200,7 @@ class SCMInputWidget { } this.inputEditor.focus(); - this.editorContainer.classList.add('synthetic-focus'); + this.element.classList.add('synthetic-focus'); } hasFocus(): boolean { @@ -2197,9 +2210,9 @@ class SCMInputWidget { private renderValidation(): void { this.clearValidation(); - this.editorContainer.classList.toggle('validation-info', this.validation?.type === InputValidationType.Information); - this.editorContainer.classList.toggle('validation-warning', this.validation?.type === InputValidationType.Warning); - this.editorContainer.classList.toggle('validation-error', this.validation?.type === InputValidationType.Error); + this.element.classList.toggle('validation-info', this.validation?.type === InputValidationType.Information); + this.element.classList.toggle('validation-warning', this.validation?.type === InputValidationType.Warning); + this.element.classList.toggle('validation-error', this.validation?.type === InputValidationType.Error); if (!this.validation || !this.inputEditor.hasTextFocus()) { return; @@ -2208,16 +2221,16 @@ class SCMInputWidget { const disposables = new DisposableStore(); this.validationDisposable = this.contextViewService.showContextView({ - getAnchor: () => this.editorContainer, + getAnchor: () => this.element, render: container => { - this.editorContainer.style.borderBottomLeftRadius = '0'; - this.editorContainer.style.borderBottomRightRadius = '0'; + this.element.style.borderBottomLeftRadius = '0'; + this.element.style.borderBottomRightRadius = '0'; const validationContainer = append(container, $('.scm-editor-validation-container')); validationContainer.classList.toggle('validation-info', this.validation!.type === InputValidationType.Information); validationContainer.classList.toggle('validation-warning', this.validation!.type === InputValidationType.Warning); validationContainer.classList.toggle('validation-error', this.validation!.type === InputValidationType.Error); - validationContainer.style.width = `${this.editorContainer.clientWidth + 2}px`; + validationContainer.style.width = `${this.element.clientWidth + 2}px`; const element = append(validationContainer, $('.scm-editor-validation')); const message = this.validation!.message; @@ -2229,8 +2242,8 @@ class SCMInputWidget { disposables.add(tracker.onDidFocus(() => (this.validationHasFocus = true))); disposables.add(tracker.onDidBlur(() => { this.validationHasFocus = false; - this.editorContainer.style.borderBottomLeftRadius = '2px'; - this.editorContainer.style.borderBottomRightRadius = '2px'; + this.element.style.borderBottomLeftRadius = '2px'; + this.element.style.borderBottomRightRadius = '2px'; this.contextViewService.hideContextView(); })); @@ -2239,8 +2252,8 @@ class SCMInputWidget { actionHandler: { callback: (link) => { openLinkFromMarkdown(this.openerService, link, message.isTrusted); - this.editorContainer.style.borderBottomLeftRadius = '2px'; - this.editorContainer.style.borderBottomRightRadius = '2px'; + this.element.style.borderBottomLeftRadius = '2px'; + this.element.style.borderBottomRightRadius = '2px'; this.contextViewService.hideContextView(); }, disposables: disposables @@ -2253,8 +2266,8 @@ class SCMInputWidget { const actionbar = new ActionBar(actionsContainer); const action = new Action('scmInputWidget.validationMessage.close', localize('label.close', "Close"), ThemeIcon.asClassName(Codicon.close), true, () => { this.contextViewService.hideContextView(); - this.editorContainer.style.borderBottomLeftRadius = '2px'; - this.editorContainer.style.borderBottomRightRadius = '2px'; + this.element.style.borderBottomLeftRadius = '2px'; + this.element.style.borderBottomRightRadius = '2px'; }); disposables.add(actionbar); actionbar.push(action, { icon: true, label: false }); @@ -2263,8 +2276,8 @@ class SCMInputWidget { }, onHide: () => { this.validationHasFocus = false; - this.editorContainer.style.borderBottomLeftRadius = '2px'; - this.editorContainer.style.borderBottomRightRadius = '2px'; + this.element.style.borderBottomLeftRadius = '2px'; + this.element.style.borderBottomRightRadius = '2px'; disposables.dispose(); }, anchorAlignment: AnchorAlignment.LEFT @@ -2289,6 +2302,19 @@ class SCMInputWidget { return this.configurationService.getValue('scm.inputFontSize'); } + private getInputEditorMaxLines(): number { + return this.configurationService.getValue('scm.inputMaxLines'); + } + + private getInputEditorMaxHeight(): number { + const maxLines = this.getInputEditorMaxLines(); + const fontSize = this.getInputEditorFontSize(); + const lineHeight = this.computeLineHeight(fontSize); + const { top, bottom } = this.inputEditor.getOption(EditorOption.padding); + + return maxLines * lineHeight + top + bottom; + } + private computeLineHeight(fontSize: number): number { return Math.round(fontSize * 1.5); } From be84baa54307a7d697f5b889a7bbef580688eb64 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 1 Nov 2023 13:07:48 +0100 Subject: [PATCH 39/43] eng - recommend pr pinger (#197136) --- .vscode/extensions.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 2dd8a08255d..3d58135095b 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -7,6 +7,7 @@ "github.vscode-pull-request-github", "ms-vscode.vscode-github-issue-notebooks", "ms-vscode.vscode-selfhost-test-provider", - "ms-vscode.extension-test-runner" + "ms-vscode.extension-test-runner", + "jrieken.vscode-pr-pinger" ] } From 04992395df5852076bb7a726d18e40126737dd92 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Wed, 1 Nov 2023 15:35:55 +0100 Subject: [PATCH 40/43] Reconcile pending comments in comment threads (#197146) Add comment thread pending comment reconciliation Fixes #196574 --- .../comments/browser/commentsController.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/comments/browser/commentsController.ts b/src/vs/workbench/contrib/comments/browser/commentsController.ts index 2eb6c4c38bb..4e7ca0f1050 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsController.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsController.ts @@ -869,8 +869,20 @@ export class CommentController implements IEditorContribution { const matchedZones = this._commentWidgets.filter(zoneWidget => zoneWidget.owner === thread.owner && Range.lift(zoneWidget.commentThread.range)?.equalsRange(thread.range)); if (thread.isReply && matchedZones.length) { this.commentService.removeContinueOnComment({ owner: thread.owner, uri: editorURI, range: thread.range, isReply: true }); - const matchedZone = matchedZones[0]; - matchedZone.setPendingComment(thread.body); + matchedZones[0].setPendingComment(thread.body); + } else if (matchedZones.length) { + this.commentService.removeContinueOnComment({ owner: thread.owner, uri: editorURI, range: thread.range, isReply: false }); + const existingPendingComment = matchedZones[0].getPendingComments().newComment; + // We need to try to reconcile the existing pending comment with the incoming pending comment + let pendingComment: string; + if (!existingPendingComment || thread.body.includes(existingPendingComment)) { + pendingComment = thread.body; + } else if (existingPendingComment.includes(thread.body)) { + pendingComment = existingPendingComment; + } else { + pendingComment = `${existingPendingComment}\n${thread.body}`; + } + matchedZones[0].setPendingComment(pendingComment); } else if (!thread.isReply) { const threadStillAvailable = this.commentService.removeContinueOnComment({ owner: thread.owner, uri: editorURI, range: thread.range, isReply: false }); if (!threadStillAvailable) { From ca063373aaefd6f4e2464fecb7bd00389e5244a8 Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Fri, 27 Oct 2023 14:47:18 -0700 Subject: [PATCH 41/43] get the cononical URI when creating the notebookEditorInput --- .../browser/interactiveEditorInput.ts | 2 +- .../notebook/browser/notebook.contribution.ts | 4 +-- .../browser/services/notebookServiceImpl.ts | 22 +++++++++++----- .../common/notebookDiffEditorInput.ts | 4 +-- .../notebook/common/notebookEditorInput.ts | 25 ++++++++----------- .../test/browser/notebookServiceImpl.test.ts | 4 ++- 6 files changed, 35 insertions(+), 26 deletions(-) diff --git a/src/vs/workbench/contrib/interactive/browser/interactiveEditorInput.ts b/src/vs/workbench/contrib/interactive/browser/interactiveEditorInput.ts index 534443553ba..f7db01de920 100644 --- a/src/vs/workbench/contrib/interactive/browser/interactiveEditorInput.ts +++ b/src/vs/workbench/contrib/interactive/browser/interactiveEditorInput.ts @@ -95,7 +95,7 @@ export class InteractiveEditorInput extends EditorInput implements ICompositeNot @INotebookService private readonly _notebookService: INotebookService, @IFileDialogService private readonly _fileDialogService: IFileDialogService ) { - const input = NotebookEditorInput.create(instantiationService, resource, 'interactive', {}); + const input = NotebookEditorInput.create(instantiationService, resource, undefined, 'interactive', {}); super(); this._notebookEditorInput = input; this._register(this._notebookEditorInput); diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index c1dcf42d739..307677480ae 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -204,7 +204,7 @@ class NotebookEditorSerializer implements IEditorSerializer { return undefined; } - const input = NotebookEditorInput.create(instantiationService, resource, viewType, options); + const input = NotebookEditorInput.create(instantiationService, resource, undefined, viewType, options); return input; } } @@ -639,7 +639,7 @@ class SimpleNotebookWorkingCopyEditorHandler extends Disposable implements IWork } createEditor(workingCopy: IWorkingCopyIdentifier): EditorInput { - return NotebookEditorInput.create(this._instantiationService, workingCopy.resource, this._getViewType(workingCopy)!); + return NotebookEditorInput.create(this._instantiationService, workingCopy.resource, undefined, this._getViewType(workingCopy)!); } private async _installHandler(): Promise { diff --git a/src/vs/workbench/contrib/notebook/browser/services/notebookServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/services/notebookServiceImpl.ts index c01228ddc92..d25e02d446d 100644 --- a/src/vs/workbench/contrib/notebook/browser/services/notebookServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/services/notebookServiceImpl.ts @@ -42,6 +42,7 @@ import { DiffEditorInputFactoryFunction, EditorInputFactoryFunction, EditorInput import { IExtensionService, isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; import { IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { InstallRecommendedExtensionAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; +import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; export class NotebookProviderInfoStore extends Disposable { @@ -62,7 +63,8 @@ export class NotebookProviderInfoStore extends Disposable { @IAccessibilityService private readonly _accessibilityService: IAccessibilityService, @IInstantiationService private readonly _instantiationService: IInstantiationService, @IFileService private readonly _fileService: IFileService, - @INotebookEditorModelResolverService private readonly _notebookEditorModelResolverService: INotebookEditorModelResolverService + @INotebookEditorModelResolverService private readonly _notebookEditorModelResolverService: INotebookEditorModelResolverService, + @IUriIdentityService private readonly uriIdentService: IUriIdentityService, ) { super(); @@ -178,12 +180,18 @@ export class NotebookProviderInfoStore extends Disposable { }; const notebookEditorInputFactory: EditorInputFactoryFunction = ({ resource, options }) => { const data = CellUri.parse(resource); - let notebookUri: URI = resource; + let notebookUri: URI; + let cellOptions: IResourceEditorInput | undefined; + let preferredResource = resource; if (data) { - notebookUri = data.notebook; - cellOptions = { resource, options }; + // resource is a notebook cell + notebookUri = this.uriIdentService.asCanonicalUri(data.notebook); + preferredResource = data.notebook; + cellOptions = { resource: notebookUri, options }; + } else { + notebookUri = this.uriIdentService.asCanonicalUri(resource); } if (!cellOptions) { @@ -191,8 +199,10 @@ export class NotebookProviderInfoStore extends Disposable { } const notebookOptions = { ...options, cellOptions } as INotebookEditorOptions; - return { editor: NotebookEditorInput.create(this._instantiationService, notebookUri, notebookProviderInfo.id), options: notebookOptions }; + const editor = NotebookEditorInput.create(this._instantiationService, notebookUri, preferredResource, notebookProviderInfo.id); + return { editor, options: notebookOptions }; }; + const notebookUntitledEditorFactory: UntitledEditorInputFactoryFunction = async ({ resource, options }) => { const ref = await this._notebookEditorModelResolverService.resolve({ untitledResource: resource }, notebookProviderInfo.id); @@ -202,7 +212,7 @@ export class NotebookProviderInfoStore extends Disposable { ref!.dispose(); }); - return { editor: NotebookEditorInput.create(this._instantiationService, ref.object.resource, notebookProviderInfo.id), options }; + return { editor: NotebookEditorInput.create(this._instantiationService, ref.object.resource, undefined, notebookProviderInfo.id), options }; }; const notebookDiffEditorInputFactory: DiffEditorInputFactoryFunction = ({ modified, original, label, description }) => { return { editor: NotebookDiffEditorInput.create(this._instantiationService, modified.resource!, label, description, original.resource!, notebookProviderInfo.id) }; diff --git a/src/vs/workbench/contrib/notebook/common/notebookDiffEditorInput.ts b/src/vs/workbench/contrib/notebook/common/notebookDiffEditorInput.ts index 1b6a2db5743..cced57f69cc 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookDiffEditorInput.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookDiffEditorInput.ts @@ -24,8 +24,8 @@ class NotebookDiffEditorModel extends EditorModel implements INotebookDiffEditor export class NotebookDiffEditorInput extends DiffEditorInput { static create(instantiationService: IInstantiationService, resource: URI, name: string | undefined, description: string | undefined, originalResource: URI, viewType: string) { - const original = NotebookEditorInput.create(instantiationService, originalResource, viewType); - const modified = NotebookEditorInput.create(instantiationService, resource, viewType); + const original = NotebookEditorInput.create(instantiationService, originalResource, undefined, viewType); + const modified = NotebookEditorInput.create(instantiationService, resource, undefined, viewType); return instantiationService.createInstance(NotebookDiffEditorInput, name, description, original, modified, viewType); } diff --git a/src/vs/workbench/contrib/notebook/common/notebookEditorInput.ts b/src/vs/workbench/contrib/notebook/common/notebookEditorInput.ts index 1ac7010369c..8edeea2be9a 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookEditorInput.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookEditorInput.ts @@ -41,8 +41,8 @@ export interface NotebookEditorInputOptions { export class NotebookEditorInput extends AbstractResourceEditorInput { - static create(instantiationService: IInstantiationService, resource: URI, viewType: string, options: NotebookEditorInputOptions = {}) { - return instantiationService.createInstance(NotebookEditorInput, resource, viewType, options); + static create(instantiationService: IInstantiationService, resource: URI, preferredResource: URI | undefined, viewType: string, options: NotebookEditorInputOptions = {}) { + return instantiationService.createInstance(NotebookEditorInput, resource, preferredResource, viewType, options); } static readonly ID: string = 'workbench.input.notebook'; @@ -51,23 +51,27 @@ export class NotebookEditorInput extends AbstractResourceEditorInput { private _sideLoadedListener: IDisposable; private _defaultDirtyState: boolean = false; + static counter = 1; + private debugId = NotebookEditorInput.counter++; + constructor( resource: URI, + preferredResource: URI | undefined, public readonly viewType: string, public readonly options: NotebookEditorInputOptions, @INotebookService private readonly _notebookService: INotebookService, @INotebookEditorModelResolverService private readonly _notebookModelResolverService: INotebookEditorModelResolverService, @IFileDialogService private readonly _fileDialogService: IFileDialogService, - @IInstantiationService private readonly _instantiationService: IInstantiationService, @ILabelService labelService: ILabelService, @IFileService fileService: IFileService, @IFilesConfigurationService filesConfigurationService: IFilesConfigurationService, @IExtensionService extensionService: IExtensionService, @IEditorService editorService: IEditorService ) { - super(resource, undefined, labelService, fileService, filesConfigurationService); + super(resource, preferredResource, labelService, fileService, filesConfigurationService); this._defaultDirtyState = !!options.startDirty; + console.log(`Creating notebookEditorInput ${this.debugId}`); // Automatically resolve this input when the "wanted" model comes to life via // some other way. This happens only once per input and resolve disposes // this listener @@ -96,6 +100,7 @@ export class NotebookEditorInput extends AbstractResourceEditorInput { } override dispose() { + console.log(`Disposing notebokEditorInput ${this.debugId}`); this._sideLoadedListener.dispose(); this._editorModelReference?.dispose(); this._editorModelReference = null; @@ -253,20 +258,12 @@ export class NotebookEditorInput extends AbstractResourceEditorInput { // called when users rename a notebook document override async rename(group: GroupIdentifier, target: URI): Promise { if (this._editorModelReference) { - const contributedNotebookProviders = this._notebookService.getContributedNotebookTypes(target); + return { editor: { resource: target }, options: { override: this.viewType } }; - if (contributedNotebookProviders.find(provider => provider.id === this._editorModelReference!.object.viewType)) { - return this._move(group, target); - } } return undefined; } - private _move(_group: GroupIdentifier, newResource: URI): { editor: EditorInput } { - const editorInput = NotebookEditorInput.create(this._instantiationService, newResource, this.viewType); - return { editor: editorInput }; - } - override async revert(_group: GroupIdentifier, options?: IRevertOptions): Promise { if (this._editorModelReference && this._editorModelReference.object.isDirty()) { await this._editorModelReference.object.revert(options); @@ -336,7 +333,7 @@ export class NotebookEditorInput extends AbstractResourceEditorInput { override toUntyped(): IResourceEditorInput { return { - resource: this.preferredResource, + resource: this.resource, options: { override: this.viewType } diff --git a/src/vs/workbench/contrib/notebook/test/browser/notebookServiceImpl.test.ts b/src/vs/workbench/contrib/notebook/test/browser/notebookServiceImpl.test.ts index f8e70728176..c204630f49d 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/notebookServiceImpl.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/notebookServiceImpl.test.ts @@ -13,6 +13,7 @@ import { IAccessibilityService } from 'vs/platform/accessibility/common/accessib import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { IFileService } from 'vs/platform/files/common/files'; import { IStorageService } from 'vs/platform/storage/common/storage'; +import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; import { NotebookProviderInfoStore } from 'vs/workbench/contrib/notebook/browser/services/notebookServiceImpl'; import { INotebookEditorModelResolverService } from 'vs/workbench/contrib/notebook/common/notebookEditorModelResolverService'; import { NotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookProvider'; @@ -43,7 +44,8 @@ suite('NotebookProviderInfoStore', function () { new class extends mock() { override hasProvider() { return true; } }, - new class extends mock() { } + new class extends mock() { }, + new class extends mock() { } ); disposables.add(store); From 08f1c3d7924958f50fa144f0361e877029c4d99b Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Mon, 30 Oct 2023 15:55:30 -0700 Subject: [PATCH 42/43] reuse existing editorInputs, backup preferred resource URI --- .../notebook/browser/notebook.contribution.ts | 7 ++++--- .../notebook/common/notebookEditorInput.ts | 18 +++++++++++++++++- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index 307677480ae..267b43ccd45 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -180,7 +180,7 @@ class NotebookDiffEditorSerializer implements IEditorSerializer { } } -type SerializedNotebookEditorData = { resource: URI; viewType: string; options?: NotebookEditorInputOptions }; +type SerializedNotebookEditorData = { resource: URI; preferredResource: URI; viewType: string; options?: NotebookEditorInputOptions }; class NotebookEditorSerializer implements IEditorSerializer { canSerialize(): boolean { return true; @@ -189,6 +189,7 @@ class NotebookEditorSerializer implements IEditorSerializer { assertType(input instanceof NotebookEditorInput); const data: SerializedNotebookEditorData = { resource: input.resource, + preferredResource: input.preferredResource, viewType: input.viewType, options: input.options }; @@ -199,12 +200,12 @@ class NotebookEditorSerializer implements IEditorSerializer { if (!data) { return undefined; } - const { resource, viewType, options } = data; + const { resource, preferredResource, viewType, options } = data; if (!data || !URI.isUri(resource) || typeof viewType !== 'string') { return undefined; } - const input = NotebookEditorInput.create(instantiationService, resource, undefined, viewType, options); + const input = NotebookEditorInput.create(instantiationService, resource, preferredResource, viewType, options); return input; } } diff --git a/src/vs/workbench/contrib/notebook/common/notebookEditorInput.ts b/src/vs/workbench/contrib/notebook/common/notebookEditorInput.ts index 8edeea2be9a..31f9de83221 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookEditorInput.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookEditorInput.ts @@ -41,8 +41,24 @@ export interface NotebookEditorInputOptions { export class NotebookEditorInput extends AbstractResourceEditorInput { + private static EditorCache: Record = {}; + static create(instantiationService: IInstantiationService, resource: URI, preferredResource: URI | undefined, viewType: string, options: NotebookEditorInputOptions = {}) { - return instantiationService.createInstance(NotebookEditorInput, resource, preferredResource, viewType, options); + const cacheId = `${resource.toString()}|${viewType}|${options._workingCopy?.typeId}`; + let editor = NotebookEditorInput.EditorCache[cacheId]; + + if (!editor) { + editor = instantiationService.createInstance(NotebookEditorInput, resource, preferredResource, viewType, options); + NotebookEditorInput.EditorCache[resource.toString()] = editor; + + editor.onWillDispose(() => { + delete NotebookEditorInput.EditorCache[cacheId]; + }); + } else if (preferredResource) { + editor.setPreferredResource(preferredResource); + } + + return editor; } static readonly ID: string = 'workbench.input.notebook'; From b45119ef4d670977cdaafdbc9e8f1440593d7e0c Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Tue, 31 Oct 2023 10:31:19 -0700 Subject: [PATCH 43/43] fix cell link regression, remove diagnostic log --- .../interactive/browser/interactiveEditorInput.ts | 2 +- .../contrib/notebook/browser/notebook.contribution.ts | 4 ++-- .../notebook/browser/services/notebookServiceImpl.ts | 6 +++--- .../contrib/notebook/common/notebookDiffEditorInput.ts | 4 ++-- .../contrib/notebook/common/notebookEditorInput.ts | 9 ++------- 5 files changed, 10 insertions(+), 15 deletions(-) diff --git a/src/vs/workbench/contrib/interactive/browser/interactiveEditorInput.ts b/src/vs/workbench/contrib/interactive/browser/interactiveEditorInput.ts index f7db01de920..ecc3caa3815 100644 --- a/src/vs/workbench/contrib/interactive/browser/interactiveEditorInput.ts +++ b/src/vs/workbench/contrib/interactive/browser/interactiveEditorInput.ts @@ -95,7 +95,7 @@ export class InteractiveEditorInput extends EditorInput implements ICompositeNot @INotebookService private readonly _notebookService: INotebookService, @IFileDialogService private readonly _fileDialogService: IFileDialogService ) { - const input = NotebookEditorInput.create(instantiationService, resource, undefined, 'interactive', {}); + const input = NotebookEditorInput.getOrCreate(instantiationService, resource, undefined, 'interactive', {}); super(); this._notebookEditorInput = input; this._register(this._notebookEditorInput); diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index 267b43ccd45..96edb0787a4 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -205,7 +205,7 @@ class NotebookEditorSerializer implements IEditorSerializer { return undefined; } - const input = NotebookEditorInput.create(instantiationService, resource, preferredResource, viewType, options); + const input = NotebookEditorInput.getOrCreate(instantiationService, resource, preferredResource, viewType, options); return input; } } @@ -640,7 +640,7 @@ class SimpleNotebookWorkingCopyEditorHandler extends Disposable implements IWork } createEditor(workingCopy: IWorkingCopyIdentifier): EditorInput { - return NotebookEditorInput.create(this._instantiationService, workingCopy.resource, undefined, this._getViewType(workingCopy)!); + return NotebookEditorInput.getOrCreate(this._instantiationService, workingCopy.resource, undefined, this._getViewType(workingCopy)!); } private async _installHandler(): Promise { diff --git a/src/vs/workbench/contrib/notebook/browser/services/notebookServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/services/notebookServiceImpl.ts index d25e02d446d..1e207678d7d 100644 --- a/src/vs/workbench/contrib/notebook/browser/services/notebookServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/services/notebookServiceImpl.ts @@ -189,7 +189,7 @@ export class NotebookProviderInfoStore extends Disposable { // resource is a notebook cell notebookUri = this.uriIdentService.asCanonicalUri(data.notebook); preferredResource = data.notebook; - cellOptions = { resource: notebookUri, options }; + cellOptions = { resource, options }; } else { notebookUri = this.uriIdentService.asCanonicalUri(resource); } @@ -199,7 +199,7 @@ export class NotebookProviderInfoStore extends Disposable { } const notebookOptions = { ...options, cellOptions } as INotebookEditorOptions; - const editor = NotebookEditorInput.create(this._instantiationService, notebookUri, preferredResource, notebookProviderInfo.id); + const editor = NotebookEditorInput.getOrCreate(this._instantiationService, notebookUri, preferredResource, notebookProviderInfo.id); return { editor, options: notebookOptions }; }; @@ -212,7 +212,7 @@ export class NotebookProviderInfoStore extends Disposable { ref!.dispose(); }); - return { editor: NotebookEditorInput.create(this._instantiationService, ref.object.resource, undefined, notebookProviderInfo.id), options }; + return { editor: NotebookEditorInput.getOrCreate(this._instantiationService, ref.object.resource, undefined, notebookProviderInfo.id), options }; }; const notebookDiffEditorInputFactory: DiffEditorInputFactoryFunction = ({ modified, original, label, description }) => { return { editor: NotebookDiffEditorInput.create(this._instantiationService, modified.resource!, label, description, original.resource!, notebookProviderInfo.id) }; diff --git a/src/vs/workbench/contrib/notebook/common/notebookDiffEditorInput.ts b/src/vs/workbench/contrib/notebook/common/notebookDiffEditorInput.ts index cced57f69cc..5884848448e 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookDiffEditorInput.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookDiffEditorInput.ts @@ -24,8 +24,8 @@ class NotebookDiffEditorModel extends EditorModel implements INotebookDiffEditor export class NotebookDiffEditorInput extends DiffEditorInput { static create(instantiationService: IInstantiationService, resource: URI, name: string | undefined, description: string | undefined, originalResource: URI, viewType: string) { - const original = NotebookEditorInput.create(instantiationService, originalResource, undefined, viewType); - const modified = NotebookEditorInput.create(instantiationService, resource, undefined, viewType); + const original = NotebookEditorInput.getOrCreate(instantiationService, originalResource, undefined, viewType); + const modified = NotebookEditorInput.getOrCreate(instantiationService, resource, undefined, viewType); return instantiationService.createInstance(NotebookDiffEditorInput, name, description, original, modified, viewType); } diff --git a/src/vs/workbench/contrib/notebook/common/notebookEditorInput.ts b/src/vs/workbench/contrib/notebook/common/notebookEditorInput.ts index 31f9de83221..1d6bba54100 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookEditorInput.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookEditorInput.ts @@ -43,13 +43,13 @@ export class NotebookEditorInput extends AbstractResourceEditorInput { private static EditorCache: Record = {}; - static create(instantiationService: IInstantiationService, resource: URI, preferredResource: URI | undefined, viewType: string, options: NotebookEditorInputOptions = {}) { + static getOrCreate(instantiationService: IInstantiationService, resource: URI, preferredResource: URI | undefined, viewType: string, options: NotebookEditorInputOptions = {}) { const cacheId = `${resource.toString()}|${viewType}|${options._workingCopy?.typeId}`; let editor = NotebookEditorInput.EditorCache[cacheId]; if (!editor) { editor = instantiationService.createInstance(NotebookEditorInput, resource, preferredResource, viewType, options); - NotebookEditorInput.EditorCache[resource.toString()] = editor; + NotebookEditorInput.EditorCache[cacheId] = editor; editor.onWillDispose(() => { delete NotebookEditorInput.EditorCache[cacheId]; @@ -67,9 +67,6 @@ export class NotebookEditorInput extends AbstractResourceEditorInput { private _sideLoadedListener: IDisposable; private _defaultDirtyState: boolean = false; - static counter = 1; - private debugId = NotebookEditorInput.counter++; - constructor( resource: URI, preferredResource: URI | undefined, @@ -87,7 +84,6 @@ export class NotebookEditorInput extends AbstractResourceEditorInput { super(resource, preferredResource, labelService, fileService, filesConfigurationService); this._defaultDirtyState = !!options.startDirty; - console.log(`Creating notebookEditorInput ${this.debugId}`); // Automatically resolve this input when the "wanted" model comes to life via // some other way. This happens only once per input and resolve disposes // this listener @@ -116,7 +112,6 @@ export class NotebookEditorInput extends AbstractResourceEditorInput { } override dispose() { - console.log(`Disposing notebokEditorInput ${this.debugId}`); this._sideLoadedListener.dispose(); this._editorModelReference?.dispose(); this._editorModelReference = null;