From 43d5d76aa029f5dc0c592c77e10cd3d4ddfe3884 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 10 Jan 2023 10:29:52 -0800 Subject: [PATCH 01/12] Add 3 terminal commands as hidden by default Part of #170991 --- .../contrib/terminal/browser/terminalMenus.ts | 41 ++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalMenus.ts b/src/vs/workbench/contrib/terminal/browser/terminalMenus.ts index b2db81c3c0c..292502d4e1e 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalMenus.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalMenus.ts @@ -517,7 +517,46 @@ export function setupTerminalMenus(): void { ContextKeyExpr.or(TerminalContextKeys.webExtensionContributedProfile, TerminalContextKeys.processSupported) ) } - } + }, + { + id: MenuId.ViewTitle, + item: { + command: { + id: TerminalCommandId.Clear, + title: localize('workbench.action.terminal.clearLong', "Clear Terminal"), + icon: Codicon.clearAll + }, + group: 'navigation', + order: 4, + isHiddenByDefault: true + } + }, + { + id: MenuId.ViewTitle, + item: { + command: { + id: TerminalCommandId.RunRecentCommand, + title: localize('workbench.action.terminal.runRecentCommand', "Run Recent Command"), + icon: Codicon.history + }, + group: 'navigation', + order: 5, + isHiddenByDefault: true + } + }, + { + id: MenuId.ViewTitle, + item: { + command: { + id: TerminalCommandId.GoToRecentDirectory, + title: localize('workbench.action.terminal.goToRecentDirectory', "Go To Recent Directory"), + icon: Codicon.folder + }, + group: 'navigation', + order: 6, + isHiddenByDefault: true + } + }, ] ); From 41fd92ef73aebe492cf59e5461428008c8bdbad1 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 11 Jan 2023 10:36:52 -0800 Subject: [PATCH 02/12] Support running vscode-userdata schema Fixes #171083 --- src/vs/workbench/contrib/terminal/browser/terminalActions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index 29945941ca7..9ef07eae736 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -625,7 +625,7 @@ export function registerTerminalActions() { const isRemote = instance ? instance.isRemote : (workbenchEnvironmentService.remoteAuthority ? true : false); const uri = editor.getModel().uri; - if ((!isRemote && uri.scheme !== Schemas.file) || (isRemote && uri.scheme !== Schemas.vscodeRemote)) { + if ((!isRemote && uri.scheme !== Schemas.file && uri.scheme !== Schemas.vscodeUserData) || (isRemote && uri.scheme !== Schemas.vscodeRemote)) { notificationService.warn(localize('workbench.action.terminal.runActiveFile.noFile', 'Only files on disk can be run in the terminal')); return; } From 1d342c908061dd7e3b4e8ac8c5a9aab327ca42e7 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 11 Jan 2023 10:39:20 -0800 Subject: [PATCH 03/12] Change to run active file and run selected text --- .../contrib/terminal/browser/terminalMenus.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalMenus.ts b/src/vs/workbench/contrib/terminal/browser/terminalMenus.ts index 292502d4e1e..ce8fbb3dd3e 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalMenus.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalMenus.ts @@ -535,9 +535,9 @@ export function setupTerminalMenus(): void { id: MenuId.ViewTitle, item: { command: { - id: TerminalCommandId.RunRecentCommand, - title: localize('workbench.action.terminal.runRecentCommand', "Run Recent Command"), - icon: Codicon.history + id: TerminalCommandId.RunActiveFile, + title: localize('workbench.action.terminal.runActiveFile', "Run Active File"), + icon: Codicon.run }, group: 'navigation', order: 5, @@ -548,9 +548,9 @@ export function setupTerminalMenus(): void { id: MenuId.ViewTitle, item: { command: { - id: TerminalCommandId.GoToRecentDirectory, - title: localize('workbench.action.terminal.goToRecentDirectory', "Go To Recent Directory"), - icon: Codicon.folder + id: TerminalCommandId.RunSelectedText, + title: localize('workbench.action.terminal.runSelectedText', "Run Selected Text"), + icon: Codicon.selection }, group: 'navigation', order: 6, From 12515569ae440cf7ccc02d4649a25f2c77909557 Mon Sep 17 00:00:00 2001 From: SteVen Batten <6561887+sbatten@users.noreply.github.com> Date: Wed, 11 Jan 2023 11:29:24 -0800 Subject: [PATCH 04/12] fix slow command palette (#171090) --- src/vs/platform/actions/common/menuService.ts | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/vs/platform/actions/common/menuService.ts b/src/vs/platform/actions/common/menuService.ts index 77d4405e31f..ce2aa05c8ee 100644 --- a/src/vs/platform/actions/common/menuService.ts +++ b/src/vs/platform/actions/common/menuService.ts @@ -48,6 +48,8 @@ class PersistedMenuHideState { private _ignoreChangeEvent: boolean = false; private _data: Record; + private _hiddenByDefaultCache = new Map(); + constructor(@IStorageService private readonly _storageService: IStorageService) { try { const raw = _storageService.get(PersistedMenuHideState._key, StorageScope.PROFILE, '{}'); @@ -78,14 +80,11 @@ class PersistedMenuHideState { } private _isHiddenByDefault(menu: MenuId, commandId: string) { - let hiddenByDefault = false; - for (const item of MenuRegistry.getMenuItems(menu)) { - if (isIMenuItem(item) && item.command.id === commandId) { - hiddenByDefault = Boolean(item.isHiddenByDefault); - break; - } - } - return hiddenByDefault; + return this._hiddenByDefaultCache.get(`${menu.id}/${commandId}`) ?? false; + } + + setDefaultState(menu: MenuId, commandId: string, hidden: boolean): void { + this._hiddenByDefaultCache.set(`${menu.id}/${commandId}`, hidden); } isHidden(menu: MenuId, commandId: string): boolean { @@ -239,6 +238,10 @@ class MenuInfo { for (const item of items) { if (this._contextKeyService.contextMatchesRules(item.when)) { const isMenuItem = isIMenuItem(item); + if (isMenuItem) { + this._hiddenStates.setDefaultState(this._id, item.command.id, !!item.isHiddenByDefault); + } + const menuHide = createMenuHide(this._id, isMenuItem ? item.command : item, this._hiddenStates); if (isMenuItem) { // MenuItemAction From b2b0642c45da081deadc311653555ffab091d5c7 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Wed, 11 Jan 2023 21:07:17 +0100 Subject: [PATCH 05/12] Git - Fix bug related to Stash & Checkout (#171099) --- extensions/git/src/commands.ts | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 9aa37188de0..94417d16fa7 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -2070,11 +2070,12 @@ export class CommandCenter { await this.cleanAll(repository); await item.run(opts); } else if (choice === stash || choice === migrate) { - await this.stash(repository); - await item.run(opts); + if (await this._stash(repository)) { + await item.run(opts); - if (choice === migrate) { - await this.stashPopLatest(repository); + if (choice === migrate) { + await this.stashPopLatest(repository); + } } } } @@ -2982,7 +2983,7 @@ export class CommandCenter { await commands.executeCommand('revealFileInOS', resourceState.resourceUri); } - private async _stash(repository: Repository, includeUntracked = false, staged = false): Promise { + private async _stash(repository: Repository, includeUntracked = false, staged = false): Promise { const noUnstagedChanges = repository.workingTreeGroup.resourceStates.length === 0 && (!includeUntracked || repository.untrackedGroup.resourceStates.length === 0); const noStagedChanges = repository.indexGroup.resourceStates.length === 0; @@ -2990,12 +2991,12 @@ export class CommandCenter { if (staged) { if (noStagedChanges) { window.showInformationMessage(l10n.t('There are no staged changes to stash.')); - return; + return false; } } else { if (noUnstagedChanges && noStagedChanges) { window.showInformationMessage(l10n.t('There are no changes to stash.')); - return; + return false; } } @@ -3022,7 +3023,7 @@ export class CommandCenter { if (pick === saveAndStash) { await Promise.all(documents.map(d => d.save())); } else if (pick !== stash) { - return; // do not stash on cancel + return false; // do not stash on cancel } } } @@ -3040,15 +3041,16 @@ export class CommandCenter { }); if (typeof message === 'undefined') { - return; + return false; } try { await repository.createStash(message, includeUntracked, staged); + return true; } catch (err) { if (/You do not have the initial commit yet/.test(err.stderr || '')) { window.showInformationMessage(l10n.t('The repository does not have any commits. Please make an initial commit before creating a stash.')); - return; + return false; } throw err; @@ -3056,18 +3058,18 @@ export class CommandCenter { } @command('git.stash', { repository: true }) - stash(repository: Repository): Promise { - return this._stash(repository); + async stash(repository: Repository): Promise { + await this._stash(repository); } @command('git.stashStaged', { repository: true }) - stashStaged(repository: Repository): Promise { - return this._stash(repository, false, true); + async stashStaged(repository: Repository): Promise { + await this._stash(repository, false, true); } @command('git.stashIncludeUntracked', { repository: true }) - stashIncludeUntracked(repository: Repository): Promise { - return this._stash(repository, true); + async stashIncludeUntracked(repository: Repository): Promise { + await this._stash(repository, true); } @command('git.stashPop', { repository: true }) From 95f9599d160be2021dcd13f57bf899a2d5f58948 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 11 Jan 2023 12:09:32 -0800 Subject: [PATCH 06/12] Remove `typescript.suggest.includeCompletionsWithSnippetText` setting (#171098) Fixes #171091 This setting controls TS Server's behavior and should not be exposed directly. Instead you can use the `includeCompletionsForImportStatements` and `suggest.classMemberSnippets.enabled` settings to enable/disable individual types of completions --- extensions/typescript-language-features/package.json | 6 ------ extensions/typescript-language-features/package.nls.json | 1 - .../src/languageFeatures/fileConfigurationManager.ts | 2 +- 3 files changed, 1 insertion(+), 8 deletions(-) diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index 3412f6d5939..4a8da3e37f8 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -400,12 +400,6 @@ "description": "%configuration.suggest.includeCompletionsForImportStatements%", "scope": "resource" }, - "typescript.suggest.includeCompletionsWithSnippetText": { - "type": "boolean", - "default": true, - "description": "%configuration.suggest.includeCompletionsWithSnippetText%", - "scope": "resource" - }, "typescript.reportStyleChecksAsWarnings": { "type": "boolean", "default": true, diff --git a/extensions/typescript-language-features/package.nls.json b/extensions/typescript-language-features/package.nls.json index 64addd50e1f..0b3cf1da598 100644 --- a/extensions/typescript-language-features/package.nls.json +++ b/extensions/typescript-language-features/package.nls.json @@ -8,7 +8,6 @@ "configuration.suggest.completeFunctionCalls": "Complete functions with their parameter signature.", "configuration.suggest.includeAutomaticOptionalChainCompletions": "Enable/disable showing completions on potentially undefined values that insert an optional chain call. Requires strict null checks to be enabled.", "configuration.suggest.includeCompletionsForImportStatements": "Enable/disable auto-import-style completions on partially-typed import statements.", - "configuration.suggest.includeCompletionsWithSnippetText": "Enable/disable snippet completions from TS Server.", "typescript.tsdk.desc": "Specifies the folder path to the tsserver and `lib*.d.ts` files under a TypeScript install to use for IntelliSense, for example: `./node_modules/typescript/lib`.\n\n- When specified as a user setting, the TypeScript version from `typescript.tsdk` automatically replaces the built-in TypeScript version.\n- When specified as a workspace setting, `typescript.tsdk` allows you to switch to use that workspace version of TypeScript for IntelliSense with the `TypeScript: Select TypeScript version` command.\n\nSee the [TypeScript documentation](https://code.visualstudio.com/docs/typescript/typescript-compiling#_using-newer-typescript-versions) for more detail about managing TypeScript versions.", "typescript.disableAutomaticTypeAcquisition": "Disables [automatic type acquisition](https://code.visualstudio.com/docs/nodejs/working-with-javascript#_typings-and-automatic-type-acquisition). Automatic type acquisition fetches `@types` packages from npm to improve IntelliSense for external libraries.", "typescript.enablePromptUseWorkspaceTsdk": "Enables prompting of users to use the TypeScript version configured in the workspace for Intellisense.", diff --git a/extensions/typescript-language-features/src/languageFeatures/fileConfigurationManager.ts b/extensions/typescript-language-features/src/languageFeatures/fileConfigurationManager.ts index 3cdc3ede1cd..6cfa599df41 100644 --- a/extensions/typescript-language-features/src/languageFeatures/fileConfigurationManager.ts +++ b/extensions/typescript-language-features/src/languageFeatures/fileConfigurationManager.ts @@ -186,7 +186,7 @@ export default class FileConfigurationManager extends Disposable { provideRefactorNotApplicableReason: true, generateReturnInDocTemplate: config.get('suggest.jsdoc.generateReturns', true), includeCompletionsForImportStatements: config.get('suggest.includeCompletionsForImportStatements', true), - includeCompletionsWithSnippetText: config.get('suggest.includeCompletionsWithSnippetText', true), + includeCompletionsWithSnippetText: true, includeCompletionsWithClassMemberSnippets: config.get('suggest.classMemberSnippets.enabled', true), includeCompletionsWithObjectLiteralMethodSnippets: config.get('suggest.objectLiteralMethodSnippets.enabled', true), autoImportFileExcludePatterns: this.getAutoImportFileExcludePatternsPreference(preferencesConfig, vscode.workspace.getWorkspaceFolder(document.uri)?.uri), From 6b08b84134d23d4c133bb260a02230ed3c45365e Mon Sep 17 00:00:00 2001 From: Raymond Zhao <7199958+rzhao271@users.noreply.github.com> Date: Wed, 11 Jan 2023 12:20:22 -0800 Subject: [PATCH 07/12] Fix links to help with website API gen (#171103) --- src/vscode-dts/vscode.d.ts | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/vscode-dts/vscode.d.ts b/src/vscode-dts/vscode.d.ts index 45cb13df56e..1a16dca4c98 100644 --- a/src/vscode-dts/vscode.d.ts +++ b/src/vscode-dts/vscode.d.ts @@ -206,7 +206,7 @@ declare module 'vscode' { /** * Get a word-range at the given position. By default words are defined by * common separators, like space, -, _, etc. In addition, per language custom - * [word definitions} can be defined. It + * [word definitions] can be defined. It * is also possible to provide a custom regular expression. * * * *Note 1:* A custom regular expression must not match the empty string and @@ -9030,7 +9030,7 @@ declare module 'vscode' { } /** - * Additional information used to implement {@linkcode CustomEditableDocument.backup}. + * Additional information used to implement {@linkcode CustomDocumentBackup}. */ interface CustomDocumentBackupContext { /** @@ -12435,11 +12435,11 @@ declare module 'vscode' { export const notebookDocuments: readonly NotebookDocument[]; /** - * Open a notebook. Will return early if this notebook is already {@link notebook.notebookDocuments loaded}. Otherwise - * the notebook is loaded and the {@linkcode notebook.onDidOpenNotebookDocument onDidOpenNotebookDocument}-event fires. + * Open a notebook. Will return early if this notebook is already {@link notebookDocuments loaded}. Otherwise + * the notebook is loaded and the {@linkcode onDidOpenNotebookDocument}-event fires. * * *Note* that the lifecycle of the returned notebook is owned by the editor and not by the extension. That means an - * {@linkcode notebook.onDidCloseNotebookDocument onDidCloseNotebookDocument}-event can occur at any time after. + * {@linkcode onDidCloseNotebookDocument}-event can occur at any time after. * * *Note* that opening a notebook does not show a notebook editor. This function only returns a notebook document which * can be shown in a notebook editor but it can also be used for other things. @@ -15068,7 +15068,7 @@ declare module 'vscode' { * * @param debugType The debug type for which the provider is registered. * @param provider The {@link DebugConfigurationProvider debug configuration provider} to register. - * @param triggerKind The {@link DebugConfigurationProviderTrigger trigger} for which the 'provideDebugConfiguration' method of the provider is registered. If `triggerKind` is missing, the value `DebugConfigurationProviderTriggerKind.Initial` is assumed. + * @param triggerKind The {@link DebugConfigurationProviderTriggerKind trigger} for which the 'provideDebugConfiguration' method of the provider is registered. If `triggerKind` is missing, the value `DebugConfigurationProviderTriggerKind.Initial` is assumed. * @return A {@link Disposable} that unregisters this provider when being disposed. */ export function registerDebugConfigurationProvider(debugType: string, provider: DebugConfigurationProvider, triggerKind?: DebugConfigurationProviderTriggerKind): Disposable; @@ -15816,8 +15816,8 @@ declare module 'vscode' { */ export function t(options: { /** - * The message to localize. If {@link args} is an array, this message supports index templating where strings like - * `{0}` and `{1}` are replaced by the item at that index in the {@link args} array. If `args` is a `Record`, + * The message to localize. If {@link options.args args} is an array, this message supports index templating where strings like + * `{0}` and `{1}` are replaced by the item at that index in the {@link options.args args} array. If `args` is a `Record`, * this supports named templating where strings like `{foo}` and `{bar}` are replaced by the value in * the Record for that key (foo, bar, etc). */ @@ -15964,7 +15964,7 @@ declare module 'vscode' { */ export interface TestController { /** - * The id of the controller passed in {@link vscode.tests.createTestController}. + * The id of the controller passed in {@link tests.createTestController}. * This must be globally unique. */ readonly id: string; @@ -15980,7 +15980,7 @@ declare module 'vscode' { * "test tree." * * The extension controls when to add tests. For example, extensions should - * add tests for a file when {@link vscode.workspace.onDidOpenTextDocument} + * add tests for a file when {@link workspace.onDidOpenTextDocument} * fires in order for decorations for tests within a file to be visible. * * However, the editor may sometimes explicitly request children using the @@ -16005,7 +16005,7 @@ declare module 'vscode' { * A function provided by the extension that the editor may call to request * children of a test item, if the {@link TestItem.canResolveChildren} is * `true`. When called, the item should discover children and call - * {@link vscode.tests.createTestItem} as children are discovered. + * {@link TestController.createTestItem} as children are discovered. * * Generally the extension manages the lifecycle of test items, but under * certain conditions the editor may request the children of a specific @@ -16289,7 +16289,7 @@ declare module 'vscode' { /** * Tags associated with this test item. May be used in combination with - * {@link TestRunProfile.tags}, or simply as an organizational feature. + * {@link TestRunProfile.tag tags}, or simply as an organizational feature. */ tags: readonly TestTag[]; @@ -16602,7 +16602,7 @@ declare module 'vscode' { * Whether or not the group is currently active. * * *Note* that only one tab group is active at a time, but that multiple tab - * groups can have an {@link TabGroup.aciveTab active tab}. + * groups can have an {@link activeTab active tab}. * * @see {@link Tab.isActive} */ From e3d247b542dd609ec993a86cf9feafdbc42e3a2a Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Wed, 11 Jan 2023 15:20:29 -0800 Subject: [PATCH 08/12] Notebook cell list reveal api polish (#171104) * CellReveal Sync. * RevealCellAsync and RevealCellLineAsync * RevealCellRangeAsync * :polish: name * Call reveal api directly on CellList * Simplify cell reveal api in nb editor. * Minimal ListViewInfoAccessor * Replace RevealLine with RevealRange * Group reveal cell methods. * inline revealCellAsync * drop revealElementInView --- .../notebook/browser/notebookBrowser.ts | 13 + .../notebook/browser/notebookEditorWidget.ts | 50 +- .../notebook/browser/view/notebookCellList.ts | 709 +++++++----------- .../browser/view/notebookRenderingCommon.ts | 25 +- .../test/browser/notebookCellList.test.ts | 12 +- 5 files changed, 320 insertions(+), 489 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index 80dbe23f2a0..3b775272c09 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -226,6 +226,7 @@ export interface ICellViewModel extends IGenericCellViewModel { readonly onDidChangeCellStatusBarItems: Event; readonly onCellDecorationsChanged: Event<{ added: INotebookCellDecorationOptions[]; removed: INotebookCellDecorationOptions[] }>; readonly onDidChangeState: Event; + readonly onDidChangeEditorAttachState: Event; readonly editStateSource: string; readonly editorAttached: boolean; isInputCollapsed: boolean; @@ -313,6 +314,18 @@ export interface INotebookDeltaCellStatusBarItems { readonly items: readonly INotebookCellStatusBarItem[]; } +export const enum CellRevealSyncType { + Default = 1, + Top = 2, + Center = 3, + CenterIfOutsideViewport = 4 +} + +export enum CellRevealRangeType { + Default = 1, + Center = 2, + CenterIfOutsideViewport = 3, +} export enum CellRevealType { NearTopIfOutsideViewport, diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index a81de7a47b9..0a2adf6e82c 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -48,7 +48,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { contrastBorder, errorForeground, focusBorder, foreground, listInactiveSelectionBackground, registerColor, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, transparent } from 'vs/platform/theme/common/colorRegistry'; import { EDITOR_PANE_BACKGROUND, PANEL_BORDER, SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; import { debugIconStartForeground } from 'vs/workbench/contrib/debug/browser/debugColors'; -import { CellEditState, CellFindMatchWithIndex, CellFocusMode, CellLayoutContext, CellRevealType, IActiveNotebookEditorDelegate, IBaseCellEditorOptions, ICellOutputViewModel, ICellViewModel, ICommonCellInfo, IDisplayOutputLayoutUpdateRequest, IFocusNotebookCellOptions, IInsetRenderOutput, IModelDecorationsChangeAccessor, INotebookDeltaDecoration, INotebookEditor, INotebookEditorContribution, INotebookEditorContributionDescription, INotebookEditorCreationOptions, INotebookEditorDelegate, INotebookEditorMouseEvent, INotebookEditorOptions, INotebookEditorViewState, INotebookViewCellsUpdateEvent, INotebookWebviewMessage, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CellEditState, CellFindMatchWithIndex, CellFocusMode, CellLayoutContext, CellRevealRangeType, CellRevealSyncType, CellRevealType, IActiveNotebookEditorDelegate, IBaseCellEditorOptions, ICellOutputViewModel, ICellViewModel, ICommonCellInfo, IDisplayOutputLayoutUpdateRequest, IFocusNotebookCellOptions, IInsetRenderOutput, IModelDecorationsChangeAccessor, INotebookDeltaDecoration, INotebookEditor, INotebookEditorContribution, INotebookEditorContributionDescription, INotebookEditorCreationOptions, INotebookEditorDelegate, INotebookEditorMouseEvent, INotebookEditorOptions, INotebookEditorViewState, INotebookViewCellsUpdateEvent, INotebookWebviewMessage, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NotebookEditorExtensionsRegistry } from 'vs/workbench/contrib/notebook/browser/notebookEditorExtensions'; import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/services/notebookEditorService'; import { notebookDebug } from 'vs/workbench/contrib/notebook/browser/notebookLogger'; @@ -1161,9 +1161,9 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD cell.focusMode = CellFocusMode.Editor; await this.revealLineInCenterIfOutsideViewportAsync(cell, selection.startLineNumber); } else if (options?.cellRevealType === CellRevealType.NearTopIfOutsideViewport) { - await this.revealNearTopIfOutsideViewportAync(cell); + await this._list.revealCellAsync(cell, CellRevealType.NearTopIfOutsideViewport); } else { - await this.revealInCenterIfOutsideViewportAsync(cell); + await this._list.revealCellAsync(cell, CellRevealType.CenterIfOutsideViewport); } const editor = this._renderedEditors.get(cell)!; @@ -1306,8 +1306,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD this._webview = this.instantiationService.createInstance(BackLayerWebView, { get creationOptions() { return that.creationOptions; }, - setScrollTop(scrollTop: number) { that._listViewInfoAccessor.setScrollTop(scrollTop); }, - triggerScroll(event: IMouseWheelEvent) { that._listViewInfoAccessor.triggerScroll(event); }, + setScrollTop(scrollTop: number) { that._list.scrollTop = scrollTop; }, + triggerScroll(event: IMouseWheelEvent) { that._list.triggerScrollFromMouseWheelEvent(event); }, getCellByInfo: that.getCellByInfo.bind(that), getCellById: that._getCellById.bind(that), toggleNotebookCellSelection: that._toggleNotebookCellSelection.bind(that), @@ -1950,63 +1950,55 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD } scrollToBottom() { - this._listViewInfoAccessor.scrollToBottom(); + this._list.scrollToBottom(); } revealCellRangeInView(range: ICellRange) { - return this._listViewInfoAccessor.revealCellRangeInView(range); + return this._list.revealCellsInView(range); } revealInView(cell: ICellViewModel) { - this._listViewInfoAccessor.revealInView(cell); + this._list.revealCell(cell, CellRevealSyncType.Default); } revealInViewAtTop(cell: ICellViewModel) { - this._listViewInfoAccessor.revealInViewAtTop(cell); - } - - revealInCenterIfOutsideViewport(cell: ICellViewModel) { - this._listViewInfoAccessor.revealInCenterIfOutsideViewport(cell); - } - - async revealInCenterIfOutsideViewportAsync(cell: ICellViewModel) { - return this._listViewInfoAccessor.revealInCenterIfOutsideViewportAsync(cell); + this._list.revealCell(cell, CellRevealSyncType.Top); } revealInCenter(cell: ICellViewModel) { - this._listViewInfoAccessor.revealInCenter(cell); + this._list.revealCell(cell, CellRevealSyncType.Center); } - revealNearTopIfOutsideViewportAync(cell: ICellViewModel) { - return this._listViewInfoAccessor.revealNearTopIfOutsideViewportAync(cell); + revealInCenterIfOutsideViewport(cell: ICellViewModel) { + this._list.revealCell(cell, CellRevealSyncType.CenterIfOutsideViewport); } async revealLineInViewAsync(cell: ICellViewModel, line: number): Promise { - return this._listViewInfoAccessor.revealLineInViewAsync(cell, line); + return this._list.revealCellRangeAsync(cell, new Range(line, 1, line, 1), CellRevealRangeType.Default); } async revealLineInCenterAsync(cell: ICellViewModel, line: number): Promise { - return this._listViewInfoAccessor.revealLineInCenterAsync(cell, line); + return this._list.revealCellRangeAsync(cell, new Range(line, 1, line, 1), CellRevealRangeType.Center); } async revealLineInCenterIfOutsideViewportAsync(cell: ICellViewModel, line: number): Promise { - return this._listViewInfoAccessor.revealLineInCenterIfOutsideViewportAsync(cell, line); + return this._list.revealCellRangeAsync(cell, new Range(line, 1, line, 1), CellRevealRangeType.CenterIfOutsideViewport); } async revealRangeInViewAsync(cell: ICellViewModel, range: Range): Promise { - return this._listViewInfoAccessor.revealRangeInViewAsync(cell, range); + return this._list.revealCellRangeAsync(cell, range, CellRevealRangeType.Default); } async revealRangeInCenterAsync(cell: ICellViewModel, range: Range): Promise { - return this._listViewInfoAccessor.revealRangeInCenterAsync(cell, range); + return this._list.revealCellRangeAsync(cell, range, CellRevealRangeType.Center); } async revealRangeInCenterIfOutsideViewportAsync(cell: ICellViewModel, range: Range): Promise { - return this._listViewInfoAccessor.revealRangeInCenterIfOutsideViewportAsync(cell, range); + return this._list.revealCellRangeAsync(cell, range, CellRevealRangeType.CenterIfOutsideViewport); } async revealCellOffsetInCenterAsync(cell: ICellViewModel, offset: number): Promise { - return this._listViewInfoAccessor.revealCellOffsetInCenterAsync(cell, offset); + return this._list.revealCellOffsetInCenterAsync(cell, offset); } getViewIndexByModelIndex(index: number): number { @@ -2038,11 +2030,11 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD } setCellEditorSelection(cell: ICellViewModel, range: Range): void { - this._listViewInfoAccessor.setCellEditorSelection(cell, range); + this._list.setCellEditorSelection(cell, range); } setHiddenAreas(_ranges: ICellRange[]): boolean { - return this._listViewInfoAccessor.setHiddenAreas(_ranges); + return this._list.setHiddenAreas(_ranges, true); } getVisibleRangesPlusViewportAboveAndBelow(): ICellRange[] { diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts index 91f7c9d6f53..9a29a9e54ce 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts @@ -18,7 +18,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IListService, IWorkbenchListOptions, WorkbenchList } from 'vs/platform/list/browser/listService'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { CursorAtBoundary, ICellViewModel, CellEditState, CellFocusMode, ICellOutputViewModel } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CursorAtBoundary, ICellViewModel, CellEditState, CellFocusMode, ICellOutputViewModel, CellRevealType, CellRevealSyncType, CellRevealRangeType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellViewModel, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModelImpl'; import { diff, NOTEBOOK_EDITOR_CURSOR_BOUNDARY, CellKind, SelectionStateType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { ICellRange, cellRangesToIndexes, reduceCellRanges, cellRangesEqual } from 'vs/workbench/contrib/notebook/common/notebookRange'; @@ -31,7 +31,7 @@ import { FastDomNode } from 'vs/base/browser/fastDomNode'; import { MarkupCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markupCellViewModel'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -const enum CellRevealType { +const enum CellEditorRevealType { Line, Range } @@ -222,7 +222,11 @@ export class NotebookCellList extends WorkbenchList implements ID if (focus && focus.cellKind === CellKind.Markup && !focus.isInputCollapsed && !this._viewModel?.options.isReadOnly) { // scroll the cell into view if out of viewport - this.revealElementInView(focus); + const focusedCellIndex = this._getViewIndexUpperBound(focus); + + if (focusedCellIndex >= 0) { + this._revealInViewWithMinimalScrolling(focusedCellIndex); + } focus.updateEditState(CellEditState.Editing, 'dbclick'); focus.focusMode = CellFocusMode.Editor; } @@ -735,7 +739,7 @@ export class NotebookCellList extends WorkbenchList implements ID /** * The range will be revealed with as little scrolling as possible. */ - revealElementsInView(range: ICellRange) { + revealCellsInView(range: ICellRange) { const startIndex = this._getViewIndexUpperBound2(range.start); if (startIndex < 0) { @@ -776,24 +780,16 @@ export class NotebookCellList extends WorkbenchList implements ID } } - - this._revealInView(startIndex); + this._revealInViewWithMinimalScrolling(startIndex); } - isScrolledToBottom() { - if (this.length === 0) { - return true; + private _revealInViewWithMinimalScrolling(viewIndex: number) { + const firstIndex = this.view.firstVisibleIndex; + if (viewIndex <= firstIndex) { + this._revealInternal(viewIndex, true, CellRevealPosition.Top); + } else { + this._revealInternal(viewIndex, true, CellRevealPosition.Bottom); } - - const last = this.length - 1; - const bottom = this.view.elementHeight(last) + this.view.elementTop(last); - const wrapperBottom = this.getViewScrollTop() + this.view.renderHeight; - - if (bottom <= wrapperBottom) { - return true; - } - - return false; } scrollToBottom() { @@ -805,109 +801,292 @@ export class NotebookCellList extends WorkbenchList implements ID this.view.setScrollTop(scrollHeight - (wrapperBottom - scrollTop) - topInsertToolbarHeight); } - revealElementInView(cell: ICellViewModel) { + //#region Reveal Cell synchronously + revealCell(cell: ICellViewModel, revealType: CellRevealSyncType) { const index = this._getViewIndexUpperBound(cell); - if (index >= 0) { - this._revealInView(index); + if (index < 0) { + return; + } + + switch (revealType) { + case CellRevealSyncType.Top: + this._revealInternal(index, false, CellRevealPosition.Top); + break; + case CellRevealSyncType.Center: + this._revealInternal(index, false, CellRevealPosition.Center); + break; + case CellRevealSyncType.CenterIfOutsideViewport: + this._revealInternal(index, true, CellRevealPosition.Center); + break; + case CellRevealSyncType.Default: + this._revealInViewWithMinimalScrolling(index); + break; } } - revealElementInViewAtTop(cell: ICellViewModel) { - const index = this._getViewIndexUpperBound(cell); + private _revealInternal(viewIndex: number, ignoreIfInsideViewport: boolean, revealPosition: CellRevealPosition) { + if (viewIndex >= this.view.length) { + return; + } - if (index >= 0) { - this._revealInternal(index, false, CellRevealPosition.Top); + const scrollTop = this.getViewScrollTop(); + const wrapperBottom = this.getViewScrollBottom(); + const elementTop = this.view.elementTop(viewIndex); + const elementBottom = this.view.elementHeight(viewIndex) + elementTop; + + if (ignoreIfInsideViewport + && elementTop >= scrollTop + && elementBottom < wrapperBottom) { + + if (revealPosition === CellRevealPosition.Center + && elementBottom > wrapperBottom + && elementTop > (scrollTop + wrapperBottom) / 2) { + // the element is partially visible and it's below the center of the viewport + } else { + return; + } + } + + switch (revealPosition) { + case CellRevealPosition.Top: + this.view.setScrollTop(elementTop); + this.view.setScrollTop(this.view.elementTop(viewIndex)); + break; + case CellRevealPosition.Center: + case CellRevealPosition.NearTop: + { + // reveal the cell top in the viewport center initially + this.view.setScrollTop(elementTop - this.view.renderHeight / 2); + // cell rendered already, we now have a more accurate cell height + const newElementTop = this.view.elementTop(viewIndex); + const newElementHeight = this.view.elementHeight(viewIndex); + const renderHeight = this.getViewScrollBottom() - this.getViewScrollTop(); + if (newElementHeight >= renderHeight) { + // cell is larger than viewport, reveal top + this.view.setScrollTop(newElementTop); + } else if (revealPosition === CellRevealPosition.Center) { + this.view.setScrollTop(newElementTop + (newElementHeight / 2) - (renderHeight / 2)); + } else if (revealPosition === CellRevealPosition.NearTop) { + this.view.setScrollTop(newElementTop - (renderHeight / 5)); + } + } + break; + case CellRevealPosition.Bottom: + this.view.setScrollTop(this.scrollTop + (elementBottom - wrapperBottom)); + this.view.setScrollTop(this.scrollTop + (this.view.elementTop(viewIndex) + this.view.elementHeight(viewIndex) - this.getViewScrollBottom())); + break; + default: + break; } } - revealElementInCenterIfOutsideViewport(cell: ICellViewModel) { + //#endregion + + //#region Reveal Cell asynchronously + async revealCellAsync(cell: ICellViewModel, revealType: CellRevealType) { + const viewIndex = this._getViewIndexUpperBound(cell); + + if (viewIndex < 0) { + return; + } + + const revealPosition = revealType === CellRevealType.NearTopIfOutsideViewport ? CellRevealPosition.NearTop : CellRevealPosition.Center; + this._revealInternal(viewIndex, true, revealPosition); + + // wait for the editor to be created only if the cell is in editing mode (meaning it has an editor and will focus the editor) + if (cell.getEditState() === CellEditState.Editing && !cell.editorAttached) { + return getEditorAttachedPromise(cell); + } + + return; + } + + //#endregion + + //#region Reveal Cell Editor Range asynchronously + async revealCellRangeAsync(cell: ICellViewModel, range: Range, revealType: CellRevealRangeType): Promise { const index = this._getViewIndexUpperBound(cell); - if (index >= 0) { - this._revealInCenterIfOutsideViewport(index); + if (index < 0) { + return; + } + + switch (revealType) { + case CellRevealRangeType.Default: + return this._revealRangeInternalAsync(index, range, CellEditorRevealType.Range); + case CellRevealRangeType.Center: + return this._revealRangeInCenterInternalAsync(index, range, CellEditorRevealType.Range); + case CellRevealRangeType.CenterIfOutsideViewport: + return this._revealRangeInCenterIfOutsideViewportInternalAsync(index, range, CellEditorRevealType.Range); } } - revealElementInCenter(cell: ICellViewModel) { - const index = this._getViewIndexUpperBound(cell); + // List items have real dynamic heights, which means after we set `scrollTop` based on the `elementTop(index)`, the element at `index` might still be removed from the view once all relayouting tasks are done. + // For example, we scroll item 10 into the view upwards, in the first round, items 7, 8, 9, 10 are all in the viewport. Then item 7 and 8 resize themselves to be larger and finally item 10 is removed from the view. + // To ensure that item 10 is always there, we need to scroll item 10 to the top edge of the viewport. + private async _revealRangeInternalAsync(viewIndex: number, range: Range, revealType: CellEditorRevealType): Promise { + const scrollTop = this.getViewScrollTop(); + const wrapperBottom = this.getViewScrollBottom(); + const elementTop = this.view.elementTop(viewIndex); + const element = this.view.element(viewIndex); - if (index >= 0) { - this._revealInCenter(index); + if (element.editorAttached) { + this._revealRangeCommon(viewIndex, range, revealType, false, false); + } else { + const elementHeight = this.view.elementHeight(viewIndex); + let upwards = false; + + if (elementTop + elementHeight < scrollTop) { + // scroll downwards + this.view.setScrollTop(elementTop); + upwards = false; + } else if (elementTop > wrapperBottom) { + // scroll upwards + this.view.setScrollTop(elementTop - this.view.renderHeight / 2); + upwards = true; + } + + const editorAttachedPromise = new Promise((resolve, reject) => { + element.onDidChangeEditorAttachState(() => { + element.editorAttached ? resolve() : reject(); + }); + }); + + return editorAttachedPromise.then(() => { + this._revealRangeCommon(viewIndex, range, revealType, true, upwards); + }); } } - async revealElementInCenterIfOutsideViewportAsync(cell: ICellViewModel): Promise { - const index = this._getViewIndexUpperBound(cell); + private async _revealRangeInCenterInternalAsync(viewIndex: number, range: Range, revealType: CellEditorRevealType): Promise { + const reveal = (viewIndex: number, range: Range, revealType: CellEditorRevealType) => { + const element = this.view.element(viewIndex); + const positionOffset = element.getPositionScrollTopOffset(range.startLineNumber, range.startColumn); + const positionOffsetInView = this.view.elementTop(viewIndex) + positionOffset; + this.view.setScrollTop(positionOffsetInView - this.view.renderHeight / 2); - if (index >= 0) { - return this._revealIfOutsideViewportAsync(index, CellRevealPosition.Center); + if (revealType === CellEditorRevealType.Range) { + element.revealRangeInCenter(range); + } + }; + + const elementTop = this.view.elementTop(viewIndex); + const viewItemOffset = elementTop; + this.view.setScrollTop(viewItemOffset - this.view.renderHeight / 2); + const element = this.view.element(viewIndex); + + if (!element.editorAttached) { + return getEditorAttachedPromise(element).then(() => reveal(viewIndex, range, revealType)); + } else { + reveal(viewIndex, range, revealType); } } - async revealNearTopIfOutsideViewportAync(cell: ICellViewModel): Promise { - const index = this._getViewIndexUpperBound(cell); + private async _revealRangeInCenterIfOutsideViewportInternalAsync(viewIndex: number, range: Range, revealType: CellEditorRevealType): Promise { + const reveal = (viewIndex: number, range: Range, revealType: CellEditorRevealType) => { + const element = this.view.element(viewIndex); + const positionOffset = element.getPositionScrollTopOffset(range.startLineNumber, range.startColumn); + const positionOffsetInView = this.view.elementTop(viewIndex) + positionOffset; + this.view.setScrollTop(positionOffsetInView - this.view.renderHeight / 2); - if (index >= 0) { - return this._revealIfOutsideViewportAsync(index, CellRevealPosition.NearTop); + if (revealType === CellEditorRevealType.Range) { + element.revealRangeInCenter(range); + } + }; + + const scrollTop = this.getViewScrollTop(); + const wrapperBottom = this.getViewScrollBottom(); + const elementTop = this.view.elementTop(viewIndex); + const viewItemOffset = elementTop; + const element = this.view.element(viewIndex); + const positionOffset = viewItemOffset + element.getPositionScrollTopOffset(range.startLineNumber, range.startColumn); + + if (positionOffset < scrollTop || positionOffset > wrapperBottom) { + // let it render + this.view.setScrollTop(positionOffset - this.view.renderHeight / 2); + + // after rendering, it might be pushed down due to markdown cell dynamic height + const newPositionOffset = this.view.elementTop(viewIndex) + element.getPositionScrollTopOffset(range.startLineNumber, range.startColumn); + this.view.setScrollTop(newPositionOffset - this.view.renderHeight / 2); + + // reveal editor + if (!element.editorAttached) { + return getEditorAttachedPromise(element).then(() => reveal(viewIndex, range, revealType)); + } else { + // for example markdown + } + } else { + if (element.editorAttached) { + element.revealRangeInCenter(range); + } else { + // for example, markdown cell in preview mode + return getEditorAttachedPromise(element).then(() => reveal(viewIndex, range, revealType)); + } } } - async revealElementLineInViewAsync(cell: ICellViewModel, line: number): Promise { - const index = this._getViewIndexUpperBound(cell); + private _revealRangeCommon(viewIndex: number, range: Range, revealType: CellEditorRevealType, newlyCreated: boolean, alignToBottom: boolean) { + const element = this.view.element(viewIndex); + const scrollTop = this.getViewScrollTop(); + const wrapperBottom = this.getViewScrollBottom(); + const positionOffset = element.getPositionScrollTopOffset(range.startLineNumber, range.startColumn); + const elementOriginalHeight = this.view.elementHeight(viewIndex); + if (positionOffset >= elementOriginalHeight) { + // we are revealing a range that is beyond current element height + // if we don't update the element height now, and directly `setTop` to reveal the range + // the element might be scrolled out of view + // next frame, when we update the element height, the element will never be scrolled back into view + const newTotalHeight = element.layoutInfo.totalHeight; + this.updateElementHeight(viewIndex, newTotalHeight); + } + const elementTop = this.view.elementTop(viewIndex); + const positionTop = elementTop + positionOffset; - if (index >= 0) { - return this._revealLineInViewAsync(index, line); + // TODO@rebornix 30 ---> line height * 1.5 + if (positionTop < scrollTop) { + this.view.setScrollTop(positionTop - 30); + } else if (positionTop > wrapperBottom) { + this.view.setScrollTop(scrollTop + positionTop - wrapperBottom + 30); + } else if (newlyCreated) { + // newly scrolled into view + if (alignToBottom) { + // align to the bottom + this.view.setScrollTop(scrollTop + positionTop - wrapperBottom + 30); + } else { + // align to to top + this.view.setScrollTop(positionTop - 30); + } + } + + if (revealType === CellEditorRevealType.Range) { + element.revealRangeInCenter(range); + } + } + //#endregion + + //#region Reveal Cell offset + async revealCellOffsetInCenterAsync(cell: ICellViewModel, offset: number): Promise { + const viewIndex = this._getViewIndexUpperBound(cell); + + if (viewIndex >= 0) { + const element = this.view.element(viewIndex); + const elementTop = this.view.elementTop(viewIndex); + if (element instanceof MarkupCellViewModel) { + return this._revealInCenterIfOutsideViewport(viewIndex); + } else { + const rangeOffset = element.layoutInfo.outputContainerOffset + offset; + this.view.setScrollTop(elementTop - this.view.renderHeight / 2); + this.view.setScrollTop(elementTop + rangeOffset - this.view.renderHeight / 2); + } } } - async revealElementLineInCenterAsync(cell: ICellViewModel, line: number): Promise { - const index = this._getViewIndexUpperBound(cell); - - if (index >= 0) { - return this._revealLineInCenterAsync(index, line); - } + private _revealInCenterIfOutsideViewport(viewIndex: number) { + this._revealInternal(viewIndex, true, CellRevealPosition.Center); } - async revealElementLineInCenterIfOutsideViewportAsync(cell: ICellViewModel, line: number): Promise { - const index = this._getViewIndexUpperBound(cell); - - if (index >= 0) { - return this._revealLineInCenterIfOutsideViewportAsync(index, line); - } - } - - async revealElementRangeInViewAsync(cell: ICellViewModel, range: Range): Promise { - const index = this._getViewIndexUpperBound(cell); - - if (index >= 0) { - return this._revealRangeInView(index, range); - } - } - - async revealElementRangeInCenterAsync(cell: ICellViewModel, range: Range): Promise { - const index = this._getViewIndexUpperBound(cell); - - if (index >= 0) { - return this._revealRangeInCenterAsync(index, range); - } - } - - async revealElementRangeInCenterIfOutsideViewportAsync(cell: ICellViewModel, range: Range): Promise { - const index = this._getViewIndexUpperBound(cell); - - if (index >= 0) { - return this._revealRangeInCenterIfOutsideViewportAsync(index, range); - } - } - - async revealElementOffsetInCenterAsync(cell: ICellViewModel, offset: number): Promise { - const index = this._getViewIndexUpperBound(cell); - - if (index >= 0) { - return this._revealOffset(index, offset); - } - } + //#endregion domElementOfElement(element: ICellViewModel): HTMLElement | null { const index = this._getViewIndexUpperBound(element); @@ -940,7 +1119,7 @@ export class NotebookCellList extends WorkbenchList implements ID this.view.delegateVerticalScrollbarPointerDown(browserEvent); } - isElementAboveViewport(index: number) { + private isElementAboveViewport(index: number) { const elementTop = this.view.elementTop(index); const elementBottom = elementTop + this.view.elementHeight(index); @@ -1031,270 +1210,7 @@ export class NotebookCellList extends WorkbenchList implements ID return this.getViewScrollTop() + this.view.renderHeight - topInsertToolbarHeight; } - private _revealOffset(viewIndex: number, offset: number) { - const element = this.view.element(viewIndex); - const elementTop = this.view.elementTop(viewIndex); - if (element instanceof MarkupCellViewModel) { - return this._revealInCenterIfOutsideViewport(viewIndex); - } else { - const rangeOffset = element.layoutInfo.outputContainerOffset + offset; - this.view.setScrollTop(elementTop - this.view.renderHeight / 2); - this.view.setScrollTop(elementTop + rangeOffset - this.view.renderHeight / 2); - } - } - - private _revealRange(viewIndex: number, range: Range, revealType: CellRevealType, newlyCreated: boolean, alignToBottom: boolean) { - const element = this.view.element(viewIndex); - const scrollTop = this.getViewScrollTop(); - const wrapperBottom = this.getViewScrollBottom(); - const positionOffset = element.getPositionScrollTopOffset(range.startLineNumber, range.startColumn); - const elementOriginalHeight = this.view.elementHeight(viewIndex); - if (positionOffset >= elementOriginalHeight) { - // we are revealing a range that is beyond current element height - // if we don't update the element height now, and directly `setTop` to reveal the range - // the element might be scrolled out of view - // next frame, when we update the element height, the element will never be scrolled back into view - const newTotalHeight = element.layoutInfo.totalHeight; - this.updateElementHeight(viewIndex, newTotalHeight); - } - const elementTop = this.view.elementTop(viewIndex); - const positionTop = elementTop + positionOffset; - - // TODO@rebornix 30 ---> line height * 1.5 - if (positionTop < scrollTop) { - this.view.setScrollTop(positionTop - 30); - } else if (positionTop > wrapperBottom) { - this.view.setScrollTop(scrollTop + positionTop - wrapperBottom + 30); - } else if (newlyCreated) { - // newly scrolled into view - if (alignToBottom) { - // align to the bottom - this.view.setScrollTop(scrollTop + positionTop - wrapperBottom + 30); - } else { - // align to to top - this.view.setScrollTop(positionTop - 30); - } - } - - if (revealType === CellRevealType.Range) { - element.revealRangeInCenter(range); - } - } - - // List items have real dynamic heights, which means after we set `scrollTop` based on the `elementTop(index)`, the element at `index` might still be removed from the view once all relayouting tasks are done. - // For example, we scroll item 10 into the view upwards, in the first round, items 7, 8, 9, 10 are all in the viewport. Then item 7 and 8 resize themselves to be larger and finally item 10 is removed from the view. - // To ensure that item 10 is always there, we need to scroll item 10 to the top edge of the viewport. - private async _revealRangeInternalAsync(viewIndex: number, range: Range, revealType: CellRevealType): Promise { - const scrollTop = this.getViewScrollTop(); - const wrapperBottom = this.getViewScrollBottom(); - const elementTop = this.view.elementTop(viewIndex); - const element = this.view.element(viewIndex); - - if (element.editorAttached) { - this._revealRange(viewIndex, range, revealType, false, false); - } else { - const elementHeight = this.view.elementHeight(viewIndex); - let upwards = false; - - if (elementTop + elementHeight < scrollTop) { - // scroll downwards - this.view.setScrollTop(elementTop); - upwards = false; - } else if (elementTop > wrapperBottom) { - // scroll upwards - this.view.setScrollTop(elementTop - this.view.renderHeight / 2); - upwards = true; - } - - const editorAttachedPromise = new Promise((resolve, reject) => { - element.onDidChangeEditorAttachState(() => { - element.editorAttached ? resolve() : reject(); - }); - }); - - return editorAttachedPromise.then(() => { - this._revealRange(viewIndex, range, revealType, true, upwards); - }); - } - } - - private async _revealLineInViewAsync(viewIndex: number, line: number): Promise { - return this._revealRangeInternalAsync(viewIndex, new Range(line, 1, line, 1), CellRevealType.Line); - } - - private async _revealRangeInView(viewIndex: number, range: Range): Promise { - return this._revealRangeInternalAsync(viewIndex, range, CellRevealType.Range); - } - - private async _revealRangeInCenterInternalAsync(viewIndex: number, range: Range, revealType: CellRevealType): Promise { - const reveal = (viewIndex: number, range: Range, revealType: CellRevealType) => { - const element = this.view.element(viewIndex); - const positionOffset = element.getPositionScrollTopOffset(range.startLineNumber, range.startColumn); - const positionOffsetInView = this.view.elementTop(viewIndex) + positionOffset; - this.view.setScrollTop(positionOffsetInView - this.view.renderHeight / 2); - - if (revealType === CellRevealType.Range) { - element.revealRangeInCenter(range); - } - }; - - const elementTop = this.view.elementTop(viewIndex); - const viewItemOffset = elementTop; - this.view.setScrollTop(viewItemOffset - this.view.renderHeight / 2); - const element = this.view.element(viewIndex); - - if (!element.editorAttached) { - return getEditorAttachedPromise(element).then(() => reveal(viewIndex, range, revealType)); - } else { - reveal(viewIndex, range, revealType); - } - } - - private async _revealLineInCenterAsync(viewIndex: number, line: number): Promise { - return this._revealRangeInCenterInternalAsync(viewIndex, new Range(line, 1, line, 1), CellRevealType.Line); - } - - private _revealRangeInCenterAsync(viewIndex: number, range: Range): Promise { - return this._revealRangeInCenterInternalAsync(viewIndex, range, CellRevealType.Range); - } - - private async _revealRangeInCenterIfOutsideViewportInternalAsync(viewIndex: number, range: Range, revealType: CellRevealType): Promise { - const reveal = (viewIndex: number, range: Range, revealType: CellRevealType) => { - const element = this.view.element(viewIndex); - const positionOffset = element.getPositionScrollTopOffset(range.startLineNumber, range.startColumn); - const positionOffsetInView = this.view.elementTop(viewIndex) + positionOffset; - this.view.setScrollTop(positionOffsetInView - this.view.renderHeight / 2); - - if (revealType === CellRevealType.Range) { - element.revealRangeInCenter(range); - } - }; - - const scrollTop = this.getViewScrollTop(); - const wrapperBottom = this.getViewScrollBottom(); - const elementTop = this.view.elementTop(viewIndex); - const viewItemOffset = elementTop; - const element = this.view.element(viewIndex); - const positionOffset = viewItemOffset + element.getPositionScrollTopOffset(range.startLineNumber, range.startColumn); - - if (positionOffset < scrollTop || positionOffset > wrapperBottom) { - // let it render - this.view.setScrollTop(positionOffset - this.view.renderHeight / 2); - - // after rendering, it might be pushed down due to markdown cell dynamic height - const newPositionOffset = this.view.elementTop(viewIndex) + element.getPositionScrollTopOffset(range.startLineNumber, range.startColumn); - this.view.setScrollTop(newPositionOffset - this.view.renderHeight / 2); - - // reveal editor - if (!element.editorAttached) { - return getEditorAttachedPromise(element).then(() => reveal(viewIndex, range, revealType)); - } else { - // for example markdown - } - } else { - if (element.editorAttached) { - element.revealRangeInCenter(range); - } else { - // for example, markdown cell in preview mode - return getEditorAttachedPromise(element).then(() => reveal(viewIndex, range, revealType)); - } - } - } - - private async _revealIfOutsideViewportAsync(viewIndex: number, revealPosition: CellRevealPosition): Promise { - this._revealInternal(viewIndex, true, revealPosition); - const element = this.view.element(viewIndex); - - // wait for the editor to be created only if the cell is in editing mode (meaning it has an editor and will focus the editor) - if (element.getEditState() === CellEditState.Editing && !element.editorAttached) { - return getEditorAttachedPromise(element); - } - - return; - } - - private async _revealLineInCenterIfOutsideViewportAsync(viewIndex: number, line: number): Promise { - return this._revealRangeInCenterIfOutsideViewportInternalAsync(viewIndex, new Range(line, 1, line, 1), CellRevealType.Line); - } - - private async _revealRangeInCenterIfOutsideViewportAsync(viewIndex: number, range: Range): Promise { - return this._revealRangeInCenterIfOutsideViewportInternalAsync(viewIndex, range, CellRevealType.Range); - } - - private _revealInternal(viewIndex: number, ignoreIfInsideViewport: boolean, revealPosition: CellRevealPosition) { - if (viewIndex >= this.view.length) { - return; - } - - const scrollTop = this.getViewScrollTop(); - const wrapperBottom = this.getViewScrollBottom(); - const elementTop = this.view.elementTop(viewIndex); - const elementBottom = this.view.elementHeight(viewIndex) + elementTop; - - if (ignoreIfInsideViewport - && elementTop >= scrollTop - && elementBottom < wrapperBottom) { - - if (revealPosition === CellRevealPosition.Center - && elementBottom > wrapperBottom - && elementTop > (scrollTop + wrapperBottom) / 2) { - // the element is partially visible and it's below the center of the viewport - } else { - return; - } - } - - switch (revealPosition) { - case CellRevealPosition.Top: - this.view.setScrollTop(elementTop); - this.view.setScrollTop(this.view.elementTop(viewIndex)); - break; - case CellRevealPosition.Center: - case CellRevealPosition.NearTop: - { - // reveal the cell top in the viewport center initially - this.view.setScrollTop(elementTop - this.view.renderHeight / 2); - // cell rendered already, we now have a more accurate cell height - const newElementTop = this.view.elementTop(viewIndex); - const newElementHeight = this.view.elementHeight(viewIndex); - const renderHeight = this.getViewScrollBottom() - this.getViewScrollTop(); - if (newElementHeight >= renderHeight) { - // cell is larger than viewport, reveal top - this.view.setScrollTop(newElementTop); - } else if (revealPosition === CellRevealPosition.Center) { - this.view.setScrollTop(newElementTop + (newElementHeight / 2) - (renderHeight / 2)); - } else if (revealPosition === CellRevealPosition.NearTop) { - this.view.setScrollTop(newElementTop - (renderHeight / 5)); - } - } - break; - case CellRevealPosition.Bottom: - this.view.setScrollTop(this.scrollTop + (elementBottom - wrapperBottom)); - this.view.setScrollTop(this.scrollTop + (this.view.elementTop(viewIndex) + this.view.elementHeight(viewIndex) - this.getViewScrollBottom())); - break; - default: - break; - } - } - - private _revealInView(viewIndex: number) { - const firstIndex = this.view.firstVisibleIndex; - if (viewIndex <= firstIndex) { - this._revealInternal(viewIndex, true, CellRevealPosition.Top); - } else { - this._revealInternal(viewIndex, true, CellRevealPosition.Bottom); - } - } - - private _revealInCenter(viewIndex: number) { - this._revealInternal(viewIndex, false, CellRevealPosition.Center); - } - - private _revealInCenterIfOutsideViewport(viewIndex: number) { - this._revealInternal(viewIndex, true, CellRevealPosition.Center); - } - - setCellSelection(cell: ICellViewModel, range: Range) { + setCellEditorSelection(cell: ICellViewModel, range: Range) { const element = cell as CellViewModel; if (element.editorAttached) { element.setSelection(range); @@ -1303,7 +1219,6 @@ export class NotebookCellList extends WorkbenchList implements ID } } - override style(styles: IListStyles) { const selectorSuffix = this.view.domId; if (!this.styleElement) { @@ -1449,74 +1364,6 @@ export class ListViewInfoAccessor extends Disposable { super(); } - setScrollTop(scrollTop: number) { - this.list.scrollTop = scrollTop; - } - - isScrolledToBottom() { - return this.list.isScrolledToBottom(); - } - - scrollToBottom() { - this.list.scrollToBottom(); - } - - revealCellRangeInView(range: ICellRange) { - return this.list.revealElementsInView(range); - } - - revealInView(cell: ICellViewModel) { - this.list.revealElementInView(cell); - } - - revealInViewAtTop(cell: ICellViewModel) { - this.list.revealElementInViewAtTop(cell); - } - - revealInCenterIfOutsideViewport(cell: ICellViewModel) { - this.list.revealElementInCenterIfOutsideViewport(cell); - } - - async revealInCenterIfOutsideViewportAsync(cell: ICellViewModel) { - return this.list.revealElementInCenterIfOutsideViewportAsync(cell); - } - - revealInCenter(cell: ICellViewModel) { - this.list.revealElementInCenter(cell); - } - - async revealNearTopIfOutsideViewportAync(cell: ICellViewModel) { - return this.list.revealNearTopIfOutsideViewportAync(cell); - } - - async revealLineInViewAsync(cell: ICellViewModel, line: number): Promise { - return this.list.revealElementLineInViewAsync(cell, line); - } - - async revealLineInCenterAsync(cell: ICellViewModel, line: number): Promise { - return this.list.revealElementLineInCenterAsync(cell, line); - } - - async revealLineInCenterIfOutsideViewportAsync(cell: ICellViewModel, line: number): Promise { - return this.list.revealElementLineInCenterIfOutsideViewportAsync(cell, line); - } - - async revealRangeInViewAsync(cell: ICellViewModel, range: Range): Promise { - return this.list.revealElementRangeInViewAsync(cell, range); - } - - async revealRangeInCenterAsync(cell: ICellViewModel, range: Range): Promise { - return this.list.revealElementRangeInCenterAsync(cell, range); - } - - async revealRangeInCenterIfOutsideViewportAsync(cell: ICellViewModel, range: Range): Promise { - return this.list.revealElementRangeInCenterIfOutsideViewportAsync(cell, range); - } - - async revealCellOffsetInCenterAsync(cell: ICellViewModel, offset: number): Promise { - return this.list.revealElementOffsetInCenterAsync(cell, offset); - } - getViewIndex(cell: ICellViewModel): number { return this.list.getViewIndex(cell) ?? -1; } @@ -1569,24 +1416,12 @@ export class ListViewInfoAccessor extends Disposable { return this.list.viewModel?.getCellsInRange(range) ?? []; } - setCellEditorSelection(cell: ICellViewModel, range: Range): void { - this.list.setCellSelection(cell, range); - } - - setHiddenAreas(_ranges: ICellRange[]): boolean { - return this.list.setHiddenAreas(_ranges, true); - } - getVisibleRangesPlusViewportAboveAndBelow(): ICellRange[] { return this.list?.getVisibleRangesPlusViewportAboveAndBelow() ?? []; } - - triggerScroll(event: IMouseWheelEvent) { - this.list.triggerScrollFromMouseWheelEvent(event); - } } -function getEditorAttachedPromise(element: CellViewModel) { +function getEditorAttachedPromise(element: ICellViewModel) { return new Promise((resolve, reject) => { Event.once(element.onDidChangeEditorAttachState)(() => element.editorAttached ? resolve() : reject()); }); diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts index 2279b887144..e89d352e419 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts @@ -14,11 +14,12 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { Range } from 'vs/editor/common/core/range'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { ICellOutputViewModel, ICellViewModel } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CellRevealRangeType, CellRevealSyncType, CellRevealType, ICellOutputViewModel, ICellViewModel } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellPartsCollection } from 'vs/workbench/contrib/notebook/browser/view/cellPart'; import { CellViewModel, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModelImpl'; import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; + export interface INotebookCellList { isDisposed: boolean; viewModel: NotebookViewModel | null; @@ -58,22 +59,12 @@ export interface INotebookCellList { selectElements(elements: ICellViewModel[]): void; getFocusedElements(): ICellViewModel[]; getSelectedElements(): ICellViewModel[]; - revealElementsInView(range: ICellRange): void; - isScrolledToBottom(): boolean; + revealCellsInView(range: ICellRange): void; scrollToBottom(): void; - revealElementInView(element: ICellViewModel): void; - revealElementInViewAtTop(element: ICellViewModel): void; - revealElementInCenterIfOutsideViewport(element: ICellViewModel): void; - revealElementInCenter(element: ICellViewModel): void; - revealElementInCenterIfOutsideViewportAsync(element: ICellViewModel): Promise; - revealNearTopIfOutsideViewportAync(element: ICellViewModel): Promise; - revealElementLineInViewAsync(element: ICellViewModel, line: number): Promise; - revealElementLineInCenterAsync(element: ICellViewModel, line: number): Promise; - revealElementLineInCenterIfOutsideViewportAsync(element: ICellViewModel, line: number): Promise; - revealElementRangeInViewAsync(element: ICellViewModel, range: Range): Promise; - revealElementRangeInCenterAsync(element: ICellViewModel, range: Range): Promise; - revealElementRangeInCenterIfOutsideViewportAsync(element: ICellViewModel, range: Range): Promise; - revealElementOffsetInCenterAsync(element: ICellViewModel, offset: number): Promise; + revealCell(cell: ICellViewModel, revealType: CellRevealSyncType): void; + revealCellAsync(cell: ICellViewModel, revealType: CellRevealType): Promise; + revealCellRangeAsync(cell: ICellViewModel, range: Range, revealType: CellRevealRangeType): Promise; + revealCellOffsetInCenterAsync(element: ICellViewModel, offset: number): Promise; setHiddenAreas(_ranges: ICellRange[], triggerViewUpdate: boolean): boolean; domElementOfElement(element: ICellViewModel): HTMLElement | null; focusView(): void; @@ -82,7 +73,7 @@ export interface INotebookCellList { updateElementHeight2(element: ICellViewModel, size: number, anchorElementIndex?: number | null): void; domFocus(): void; focusContainer(): void; - setCellSelection(element: ICellViewModel, range: Range): void; + setCellEditorSelection(element: ICellViewModel, range: Range): void; style(styles: IListStyles): void; getRenderHeight(): number; getScrollHeight(): number; diff --git a/src/vs/workbench/contrib/notebook/test/browser/notebookCellList.test.ts b/src/vs/workbench/contrib/notebook/test/browser/notebookCellList.test.ts index 5fd27dee8c2..f12d76a8af1 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/notebookCellList.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/notebookCellList.test.ts @@ -60,17 +60,17 @@ suite('NotebookCellList', () => { assert.deepStrictEqual(cellList.getViewScrollBottom(), 215); // reveal cell 1, top 50, bottom 150, which is fully visible in the viewport - cellList.revealElementsInView({ start: 1, end: 2 }); + cellList.revealCellsInView({ start: 1, end: 2 }); assert.deepStrictEqual(cellList.scrollTop, 5); assert.deepStrictEqual(cellList.getViewScrollBottom(), 215); // reveal cell 2, top 150, bottom 200, which is fully visible in the viewport - cellList.revealElementsInView({ start: 2, end: 3 }); + cellList.revealCellsInView({ start: 2, end: 3 }); assert.deepStrictEqual(cellList.scrollTop, 5); assert.deepStrictEqual(cellList.getViewScrollBottom(), 215); // reveal cell 3, top 200, bottom 300, which is partially visible in the viewport - cellList.revealElementsInView({ start: 3, end: 4 }); + cellList.revealCellsInView({ start: 3, end: 4 }); assert.deepStrictEqual(cellList.scrollTop, 90); }); }); @@ -105,7 +105,7 @@ suite('NotebookCellList', () => { assert.deepStrictEqual(cellList.getViewScrollBottom(), 210); // reveal cell 3, top 200, bottom 300, which is partially visible in the viewport - cellList.revealElementsInView({ start: 3, end: 4 }); + cellList.revealCellsInView({ start: 3, end: 4 }); assert.deepStrictEqual(cellList.scrollTop, 90); // scroll to 5 @@ -114,7 +114,7 @@ suite('NotebookCellList', () => { assert.deepStrictEqual(cellList.getViewScrollBottom(), 215); // reveal cell 0, top 0, bottom 50 - cellList.revealElementsInView({ start: 0, end: 1 }); + cellList.revealCellsInView({ start: 0, end: 1 }); assert.deepStrictEqual(cellList.scrollTop, 0); }); }); @@ -150,7 +150,7 @@ suite('NotebookCellList', () => { assert.deepStrictEqual(cellList.scrollTop, 0); assert.deepStrictEqual(cellList.getViewScrollBottom(), 210); - cellList.revealElementsInView({ start: 4, end: 5 }); + cellList.revealCellsInView({ start: 4, end: 5 }); assert.deepStrictEqual(cellList.scrollTop, 140); // assert.deepStrictEqual(cellList.getViewScrollBottom(), 330); }); From 0ad14b0010d90857680c13152eb4b9da5a21dd0d Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 11 Jan 2023 15:38:07 -0800 Subject: [PATCH 09/12] Remove extra space in setting description (#171120) --- src/vs/editor/common/config/editorOptions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 6756ab32d88..c261c3de8b0 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -4891,7 +4891,7 @@ export const enum EditorOption { export const EditorOptions = { acceptSuggestionOnCommitCharacter: register(new EditorBooleanOption( EditorOption.acceptSuggestionOnCommitCharacter, 'acceptSuggestionOnCommitCharacter', true, - { markdownDescription: nls.localize('acceptSuggestionOnCommitCharacter', "Controls whether suggestions should be accepted on commit characters. For example, in JavaScript, the semi-colon (`; `) can be a commit character that accepts a suggestion and types that character.") } + { markdownDescription: nls.localize('acceptSuggestionOnCommitCharacter', "Controls whether suggestions should be accepted on commit characters. For example, in JavaScript, the semi-colon (`;`) can be a commit character that accepts a suggestion and types that character.") } )), acceptSuggestionOnEnter: register(new EditorStringEnumOption( EditorOption.acceptSuggestionOnEnter, 'acceptSuggestionOnEnter', From 5070034f6dbbf48a1c171263050e7d9fec8fd291 Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Wed, 11 Jan 2023 15:47:00 -0800 Subject: [PATCH 10/12] clean up onDidInstallExtensions (#171121) --- .../localization.contribution.ts | 50 ++++++++++--------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/src/vs/workbench/contrib/localization/electron-sandbox/localization.contribution.ts b/src/vs/workbench/contrib/localization/electron-sandbox/localization.contribution.ts index 6a944aa9b15..c67e6684377 100644 --- a/src/vs/workbench/contrib/localization/electron-sandbox/localization.contribution.ts +++ b/src/vs/workbench/contrib/localization/electron-sandbox/localization.contribution.ts @@ -57,31 +57,33 @@ export class LocalizationWorkbenchContribution extends Disposable implements IWo private onDidInstallExtensions(results: readonly InstallExtensionResult[]): void { for (const e of results) { - if (e.local && e.operation === InstallOperation.Install && e.local.manifest.contributes && e.local.manifest.contributes.localizations && e.local.manifest.contributes.localizations.length) { - const locale = e.local.manifest.contributes.localizations[0].languageId; - if (platform.language !== locale) { - const updateAndRestart = platform.locale !== locale; - this.notificationService.prompt( - Severity.Info, - updateAndRestart ? localize('updateLocale', "Would you like to change VS Code's UI language to {0} and restart?", e.local.manifest.contributes.localizations[0].languageName || e.local.manifest.contributes.localizations[0].languageId) - : localize('activateLanguagePack', "In order to use VS Code in {0}, VS Code needs to restart.", e.local.manifest.contributes.localizations[0].languageName || e.local.manifest.contributes.localizations[0].languageId), - [{ - label: updateAndRestart ? localize('changeAndRestart', "Change Language and Restart") : localize('restart', "Restart"), - run: () => { - const updatePromise = updateAndRestart ? this.jsonEditingService.write(this.environmentService.argvResource, [{ path: ['locale'], value: locale }], true) : Promise.resolve(undefined); - updatePromise.then(() => this.hostService.restart(), e => this.notificationService.error(e)); - } - }, { - label: updateAndRestart ? localize('doNotChangeAndRestart', "Don't Change Language") : localize('doNotRestart', "Don't Restart"), - run: () => { } - }], - { - sticky: true, - neverShowAgain: { id: 'langugage.update.donotask', isSecondary: true, scope: NeverShowAgainScope.APPLICATION } - } - ); - } + if (e.operation !== InstallOperation.Install || !e.local?.manifest?.contributes?.localizations?.length) { + continue; } + const languageId = e.local.manifest.contributes.localizations[0].languageId; + if (platform.language === languageId) { + continue; + } + + this.notificationService.prompt( + Severity.Info, + localize('updateLocale', "Would you like to change VS Code's UI language to {0} and restart?", e.local.manifest.contributes.localizations[0].languageName || e.local.manifest.contributes.localizations[0].languageId), + [{ + label: localize('changeAndRestart', "Change Language and Restart"), + run: async () => { + try { + await this.jsonEditingService.write(this.environmentService.argvResource, [{ path: ['locale'], value: languageId }], true); + await this.hostService.restart(); + } catch (e) { + this.notificationService.error(e); + } + } + }], + { + sticky: true, + neverShowAgain: { id: 'langugage.update.donotask', isSecondary: true, scope: NeverShowAgainScope.APPLICATION } + } + ); } } From 87d1de6a45a10ea44d930943245af99d48ec14cc Mon Sep 17 00:00:00 2001 From: David Dossett Date: Wed, 11 Jan 2023 16:44:39 -0800 Subject: [PATCH 11/12] Update various dark+ v2 colors (#171123) Update dark+ v2 accent and button text colors --- .../themes/dark_plus_experimental.json | 43 ++++++++++--------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/extensions/theme-defaults/themes/dark_plus_experimental.json b/extensions/theme-defaults/themes/dark_plus_experimental.json index cb9f3f02dbb..1b215c1025a 100644 --- a/extensions/theme-defaults/themes/dark_plus_experimental.json +++ b/extensions/theme-defaults/themes/dark_plus_experimental.json @@ -3,19 +3,19 @@ "name": "Dark+ (Experimental)", "include": "./dark_plus.json", "colors": { - "activityBar.activeBorder": "#2488d8", + "activityBar.activeBorder": "#0078d4", "activityBar.background": "#181818", "activityBar.border": "#ffffff15", "activityBar.foreground": "#ffffffc5", "activityBar.inactiveForeground": "#ffffff80", - "activityBarBadge.background": "#2488d8", - "activityBarBadge.foreground": "#000000", - "badge.background": "#2488d8", + "activityBarBadge.background": "#0078d4", + "activityBarBadge.foreground": "#ffffff", + "badge.background": "#0078d4", "badge.foreground": "#000000", - "button.background": "#2488d8", + "button.background": "#0078d4", "button.border": "#ffffff12", - "button.foreground": "#000000", - "button.hoverBackground": "#2488d8e6", + "button.foreground": "#ffffff", + "button.hoverBackground": "#0078d4e6", "button.secondaryBackground": "#FFFFFF0F", "button.secondaryForeground": "#ffffffc5", "button.secondaryHoverBackground": "#ffffff15", @@ -34,13 +34,13 @@ "editor.background": "#1f1f1f", "editor.findMatchBackground": "#9e6a03", "editor.foreground": "#ffffffd3", - "editorCursor.foreground": "#2488d8", + "editorCursor.foreground": "#0078d4", "editorGroup.border": "#ffffff17", "editorGroupHeader.tabsBackground": "#181818", "editorGroupHeader.tabsBorder": "#ffffff15", "editorGutter.addedBackground": "#2ea043", "editorGutter.deletedBackground": "#f85149", - "editorGutter.modifiedBackground": "#2488d8", + "editorGutter.modifiedBackground": "#0078d4", "editorInlayHint.background": "#8b949e33", "editorInlayHint.foreground": "#8b949e", "editorInlayHint.typeBackground": "#8b949e33", @@ -50,7 +50,7 @@ "editorOverviewRuler.border": "#010409", "editorWidget.background": "#1f1f1f", "errorForeground": "#f85149", - "focusBorder": "#2488d8", + "focusBorder": "#0078d4", "foreground": "#ffffffc5", "icon.foreground": "#ffffffc5", "input.background": "#ffffff0f", @@ -72,7 +72,7 @@ "panel.background": "#181818", "panel.border": "#ffffff15", "panelInput.border": "#ffffff15", - "panelTitle.activeBorder": "#2488d8", + "panelTitle.activeBorder": "#0078d4", "panelTitle.activeForeground": "#ffffffc5", "panelTitle.inactiveForeground": "#8b949e", "peekViewEditor.background": "#1f1f1f", @@ -81,7 +81,7 @@ "peekViewResult.matchHighlightBackground": "#bb800966", "pickerGroup.border": "#ffffff15", "pickerGroup.foreground": "#8b949e", - "progressBar.background": "#2488d8", + "progressBar.background": "#0078d4", "quickInput.background": "#1f1f1f", "quickInput.foreground": "#ffffffc5", "scrollbar.shadow": "#484f5833", @@ -101,17 +101,17 @@ "sideBarTitle.foreground": "#ffffffc5", "statusBar.background": "#181818", "statusBar.border": "#ffffff15", - "statusBar.debuggingBackground": "#fd716c", - "statusBar.debuggingForeground": "#000000", + "statusBar.debuggingBackground": "#0078d4", + "statusBar.debuggingForeground": "#ffffff", "statusBar.foreground": "#ffffffc5", "statusBar.noFolderBackground": "#1f1f1f", - "statusBarItem.focusBorder": "#2488d8", + "statusBarItem.focusBorder": "#0078d4", "statusBarItem.prominentBackground": "#6e768166", - "statusBarItem.remoteBackground": "#2488d8", - "statusBarItem.remoteForeground": "#000000", + "statusBarItem.remoteBackground": "#0078d4", + "statusBarItem.remoteForeground": "#ffffff", "tab.activeBackground": "#1f1f1f", "tab.activeBorder": "#1f1f1f", - "tab.activeBorderTop": "#2488d8", + "tab.activeBorderTop": "#0078d4", "tab.activeForeground": "#ffffffc5", "tab.border": "#ffffff15", "tab.hoverBackground": "#1f1f1f", @@ -121,12 +121,12 @@ "tab.unfocusedActiveBorderTop": "#ffffff15", "tab.unfocusedHoverBackground": "#6e76811a", "terminal.foreground": "#ffffffc5", - "terminal.tab.activeBorder": "#2488d8", + "terminal.tab.activeBorder": "#0078d4", "textBlockQuote.background": "#010409", "textBlockQuote.border": "#ffffff14", "textCodeBlock.background": "#6e768166", - "textLink.activeForeground": "#8bb6f7", - "textLink.foreground": "#8bb6f7", + "textLink.activeForeground": "#40A6FF", + "textLink.foreground": "#40A6FF", "textPreformat.foreground": "#8b949e", "textSeparator.foreground": "#21262d", "titleBar.activeBackground": "#181818", @@ -135,6 +135,7 @@ "titleBar.inactiveBackground": "#1f1f1f", "titleBar.inactiveForeground": "#8b949e", "welcomePage.tileBackground": "#ffffff0f", + "welcomePage.progress.foreground": "#0078d4", "widgetBorder": "#ffffff15", }, } From 6d40104789d03d41b8866a1e57847dae14c5cf0d Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Wed, 11 Jan 2023 17:25:51 -0800 Subject: [PATCH 12/12] Join with empty string to mirror what l10n-dev does (#171125) This should have been the case all along, but I missed it and probably thought not passing anything joins with empty string. fixes https://github.com/microsoft/vscode-l10n/issues/77 --- src/vs/workbench/api/common/extHostLocalizationService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/api/common/extHostLocalizationService.ts b/src/vs/workbench/api/common/extHostLocalizationService.ts index f3e4d7554b9..7acfa075c65 100644 --- a/src/vs/workbench/api/common/extHostLocalizationService.ts +++ b/src/vs/workbench/api/common/extHostLocalizationService.ts @@ -40,7 +40,7 @@ export class ExtHostLocalizationService implements ExtHostLocalizationShape { let key = message; if (comment && comment.length > 0) { - key += `/${Array.isArray(comment) ? comment.join() : comment}`; + key += `/${Array.isArray(comment) ? comment.join('') : comment}`; } const str = this.bundleCache.get(extensionId)?.contents[key]; if (!str) {