diff --git a/extensions/github1s/package.json b/extensions/github1s/package.json index 8abb79b35..2b5ee7396 100644 --- a/extensions/github1s/package.json +++ b/extensions/github1s/package.json @@ -609,7 +609,7 @@ "node-polyfill-webpack-plugin": "^1.1.4", "ts-loader": "^9.2.8", "typescript": "^4.6.3", - "webpack": "^5.72.0", + "webpack": "^5.76.0", "webpack-cli": "^4.9.2" } } diff --git a/extensions/github1s/yarn.lock b/extensions/github1s/yarn.lock index d871806df..d5edbb679 100644 --- a/extensions/github1s/yarn.lock +++ b/extensions/github1s/yarn.lock @@ -359,10 +359,10 @@ acorn-import-assertions@^1.7.6: resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz#ba2b5939ce62c238db6d93d81c9b111b29b855e9" integrity sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw== -acorn@^8.4.1, acorn@^8.5.0: - version "8.7.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.1.tgz#0197122c843d1bf6d0a5e83220a788f278f63c30" - integrity sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A== +acorn@^8.5.0, acorn@^8.7.1: + version "8.8.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" + integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== ajv-keywords@^3.5.2: version "3.5.2" @@ -738,10 +738,10 @@ elliptic@^6.5.3: minimalistic-assert "^1.0.1" minimalistic-crypto-utils "^1.0.1" -enhanced-resolve@^5.0.0, enhanced-resolve@^5.9.2: - version "5.9.3" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.9.3.tgz#44a342c012cbc473254af5cc6ae20ebd0aae5d88" - integrity sha512-Bq9VSor+kjvW3f9/MiiR4eE3XYgOl7/rS8lnSxbRbF3kS0B2r+Y9w5krBWxZgDxASVZbdYrn5wT4j/Wb0J9qow== +enhanced-resolve@^5.0.0, enhanced-resolve@^5.10.0: + version "5.12.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz#300e1c90228f5b570c4d35babf263f6da7155634" + integrity sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ== dependencies: graceful-fs "^4.2.4" tapable "^2.2.0" @@ -1251,10 +1251,10 @@ js-base64@^3.7.2: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -json-parse-better-errors@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" - integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== +json-parse-even-better-errors@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== json-schema-traverse@^0.4.1: version "0.4.1" @@ -2019,10 +2019,10 @@ vm-browserify@^1.1.2: resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== -watchpack@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.3.1.tgz#4200d9447b401156eeca7767ee610f8809bc9d25" - integrity sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA== +watchpack@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d" + integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg== dependencies: glob-to-regexp "^0.4.1" graceful-fs "^4.1.2" @@ -2063,34 +2063,34 @@ webpack-sources@^3.2.3: resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== -webpack@^5.72.0: - version "5.72.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.72.0.tgz#f8bc40d9c6bb489a4b7a8a685101d6022b8b6e28" - integrity sha512-qmSmbspI0Qo5ld49htys8GY9XhS9CGqFoHTsOVAnjBdg0Zn79y135R+k4IR4rKK6+eKaabMhJwiVB7xw0SJu5w== +webpack@^5.76.0: + version "5.76.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.76.0.tgz#f9fb9fb8c4a7dbdcd0d56a98e56b8a942ee2692c" + integrity sha512-l5sOdYBDunyf72HW8dF23rFtWq/7Zgvt/9ftMof71E/yUb1YLOBmTgA2K4vQthB3kotMrSj609txVE0dnr2fjA== dependencies: "@types/eslint-scope" "^3.7.3" "@types/estree" "^0.0.51" "@webassemblyjs/ast" "1.11.1" "@webassemblyjs/wasm-edit" "1.11.1" "@webassemblyjs/wasm-parser" "1.11.1" - acorn "^8.4.1" + acorn "^8.7.1" acorn-import-assertions "^1.7.6" browserslist "^4.14.5" chrome-trace-event "^1.0.2" - enhanced-resolve "^5.9.2" + enhanced-resolve "^5.10.0" es-module-lexer "^0.9.0" eslint-scope "5.1.1" events "^3.2.0" glob-to-regexp "^0.4.1" graceful-fs "^4.2.9" - json-parse-better-errors "^1.0.2" + json-parse-even-better-errors "^2.3.1" loader-runner "^4.2.0" mime-types "^2.1.27" neo-async "^2.6.2" schema-utils "^3.1.0" tapable "^2.1.1" terser-webpack-plugin "^5.1.3" - watchpack "^2.3.1" + watchpack "^2.4.0" webpack-sources "^3.2.3" whatwg-url@^5.0.0: diff --git a/package.json b/package.json index 0f0b76c94..6b3992725 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "lib": "lib" }, "devDependencies": { - "@github1s/vscode-web": "0.8.0", + "@github1s/vscode-web": "0.9.0", "@typescript-eslint/eslint-plugin": "^5.40.1", "@typescript-eslint/parser": "^5.40.1", "chokidar": "^3.5.3", diff --git a/src/index.ts b/src/index.ts index c2bda34bc..2e2d0c9ac 100644 --- a/src/index.ts +++ b/src/index.ts @@ -66,9 +66,10 @@ const resolvePlatformState = (): [Platform, string] => { ]; (window as any).vscodeWeb = { + commands: vscodeCommands, + allowEditorLabelOverride: true, additionalBuiltinExtensions: ['ms-vscode.anycode'], webviewEndpoint: staticAssetsPrefix + '/vscode/vs/workbench/contrib/webview/browser/pre', - commands: vscodeCommands, productConfiguration: createProductConfiguration(platform), initialColorTheme: { themeType: 'dark' as any }, builtinExtensions: window.github1sExtensions || [], diff --git a/vscode-web/.VERSION b/vscode-web/.VERSION index 84f09b651..e168f31cb 100644 --- a/vscode-web/.VERSION +++ b/vscode-web/.VERSION @@ -1 +1 @@ - 1.75.1 \ No newline at end of file +1.77.3 \ No newline at end of file diff --git a/vscode-web/package.json b/vscode-web/package.json index 2cf490132..fcb754c80 100644 --- a/vscode-web/package.json +++ b/vscode-web/package.json @@ -1,6 +1,6 @@ { "name": "@github1s/vscode-web", - "version": "0.8.0", + "version": "0.9.0", "description": "VS Code web for GitHub1s", "author": "github1s", "license": "MIT", @@ -33,12 +33,12 @@ "jschardet": "3.0.0", "tas-client-umd": "0.1.6", "vscode-oniguruma": "1.7.0", - "vscode-textmate": "8.0.0", - "xterm": "5.2.0-beta.21", + "vscode-textmate": "9.0.0", + "xterm": "5.2.0-beta.30", "xterm-addon-canvas": "0.4.0-beta.7", "xterm-addon-search": "0.11.0", "xterm-addon-unicode11": "0.5.0", - "xterm-addon-webgl": "0.15.0-beta.4" + "xterm-addon-webgl": "0.15.0-beta.7" }, "devDependencies": { "@types/trusted-types": "^2.0.0", diff --git a/vscode-web/src/vs/editor/common/config/editorOptions.ts b/vscode-web/src/vs/editor/common/config/editorOptions.ts index a1fbed4a3..ee80b6af7 100644 --- a/vscode-web/src/vs/editor/common/config/editorOptions.ts +++ b/vscode-web/src/vs/editor/common/config/editorOptions.ts @@ -57,6 +57,10 @@ export interface IEditorOptions { * The aria label for the editor's textarea (when it is focused). */ ariaLabel?: string; + /** + * Control whether a screen reader announces inline suggestion content immediately. + */ + screenReaderAnnounceInlineSuggestion?: boolean; /** * The `tabindex` property of the editor's textarea */ @@ -698,6 +702,11 @@ export interface IEditorOptions { * When enabled, this shows a preview of the drop location and triggers an `onDropIntoEditor` event. */ dropIntoEditor?: IDropIntoEditorOptions; + + /** + * Controls whether the editor receives tabs or defers them to the workbench for navigation. + */ + tabFocusMode?: boolean; } /** @@ -2649,7 +2658,10 @@ export interface IEditorStickyScrollOptions { * Maximum number of sticky lines to show */ maxLineCount?: number; - + /** + * Model to choose for sticky scroll by default + */ + defaultModel?: 'outlineModel' | 'foldingProviderModel' | 'indentationModel'; } /** @@ -2660,21 +2672,27 @@ export type EditorStickyScrollOptions = Readonly { constructor() { - const defaults: EditorStickyScrollOptions = { enabled: false, maxLineCount: 5 }; + const defaults: EditorStickyScrollOptions = { enabled: false, maxLineCount: 5, defaultModel: 'outlineModel' }; super( EditorOption.stickyScroll, 'stickyScroll', defaults, { 'editor.stickyScroll.enabled': { type: 'boolean', default: defaults.enabled, - description: nls.localize('editor.stickyScroll', "Shows the nested current scopes during the scroll at the top of the editor.") + description: nls.localize('editor.stickyScroll.enabled', "Shows the nested current scopes during the scroll at the top of the editor.") }, 'editor.stickyScroll.maxLineCount': { type: 'number', default: defaults.maxLineCount, minimum: 1, maximum: 10, - description: nls.localize('editor.stickyScroll.', "Defines the maximum number of sticky lines to show.") + description: nls.localize('editor.stickyScroll.maxLineCount', "Defines the maximum number of sticky lines to show.") + }, + 'editor.stickyScroll.defaultModel': { + type: 'string', + enum: ['outlineModel', 'foldingProviderModel', 'indentationModel'], + default: defaults.defaultModel, + description: nls.localize('editor.stickyScroll.defaultModel', "Defines the model to use for determining which lines to stick. If the outline model does not exist, it will fall back on the folding provider model which falls back on the indentation model. This order is respected in all three cases.") }, } ); @@ -2688,6 +2706,7 @@ class EditorStickyScroll extends BaseEditorOption(input.defaultModel, this.defaultValue.defaultModel, ['outlineModel', 'foldingProviderModel', 'indentationModel']), }; } } @@ -3786,6 +3805,8 @@ export interface IInlineSuggestOptions { mode?: 'prefix' | 'subword' | 'subwordSmart'; showToolbar?: 'always' | 'onHover'; + + suppressSuggestions?: boolean; } /** @@ -3802,6 +3823,7 @@ class InlineEditorSuggest extends BaseEditorOption { - - constructor() { - super(EditorOption.tabFocusMode); - } - - public compute(env: IEnvironmentalOptions, options: IComputedEditorOptions, _: boolean): boolean { - const readOnly = options.get(EditorOption.readOnly); - return (readOnly ? true : env.tabFocusMode); - } -} - -//#endregion - //#region wrappingIndent /** @@ -4765,6 +4777,7 @@ export const enum EditorOption { accessibilityPageSize, ariaLabel, autoClosingBrackets, + screenReaderAnnounceInlineSuggestion, autoClosingDelete, autoClosingOvertype, autoClosingQuotes, @@ -4929,6 +4942,13 @@ export const EditorOptions = { ariaLabel: register(new EditorStringOption( EditorOption.ariaLabel, 'ariaLabel', nls.localize('editorViewAccessibleLabel', "Editor content") )), + screenReaderAnnounceInlineSuggestion: register(new EditorBooleanOption( + EditorOption.screenReaderAnnounceInlineSuggestion, 'screenReaderAnnounceInlineSuggestion', false, + { + description: nls.localize('screenReaderAnnounceInlineSuggestion', "Control whether inline suggestions are announced by a screen reader. Note that this does not work on macOS with VoiceOver."), + tags: ['accessibility'] + } + )), autoClosingBrackets: register(new EditorStringEnumOption( EditorOption.autoClosingBrackets, 'autoClosingBrackets', 'languageDefined' as 'always' | 'languageDefined' | 'beforeWhitespace' | 'never', @@ -5621,7 +5641,9 @@ export const EditorOptions = { // Leave these at the end (because they have dependencies!) editorClassName: register(new EditorClassName()), pixelRatio: register(new EditorPixelRatio()), - tabFocusMode: register(new EditorTabFocusMode()), + tabFocusMode: register(new EditorBooleanOption(EditorOption.tabFocusMode, 'tabFocusMode', false, + { markdownDescription: nls.localize('tabFocusMode', "Controls whether the editor receives tabs or defers them to the workbench for navigation.") } + )), layoutInfo: register(new EditorLayoutInfoComputer()), wrappingInfo: register(new EditorWrappingInfoComputer()), wrappingIndent: register(new WrappingIndentOption()), diff --git a/vscode-web/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts b/vscode-web/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts index c568e7d1d..0324f348e 100644 --- a/vscode-web/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts +++ b/vscode-web/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts @@ -5,10 +5,11 @@ import 'vs/css!./media/activityaction'; import { localize } from 'vs/nls'; -import { EventType, addDisposableListener, EventHelper } from 'vs/base/browser/dom'; +import { EventType, addDisposableListener, EventHelper, append, $, clearNode, hide, show } from 'vs/base/browser/dom'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { EventType as TouchEventType, GestureEvent } from 'vs/base/browser/touch'; import { Action, IAction, Separator, SubmenuAction, toAction } from 'vs/base/common/actions'; +import { Event } from 'vs/base/common/event'; import { KeyCode } from 'vs/base/common/keyCodes'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { IMenuService, MenuId, IMenu, registerAction2, Action2, IAction2Options } from 'vs/platform/actions/common/actions'; @@ -24,7 +25,7 @@ import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/bro import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { getCurrentAuthenticationSessionInfo } from 'vs/workbench/services/authentication/browser/authenticationService'; -import { AuthenticationSession, IAuthenticationService } from 'vs/workbench/services/authentication/common/authentication'; +import { AuthenticationSessionAccount, IAuthenticationService } from 'vs/workbench/services/authentication/common/authentication'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IProductService } from 'vs/platform/product/common/productService'; @@ -39,6 +40,7 @@ import { IPaneCompositePart } from 'vs/workbench/browser/parts/paneCompositePart import { ICredentialsService } from 'vs/platform/credentials/common/credentials'; import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; +import { ILogService } from 'vs/platform/log/common/log'; export class ViewContainerActivityAction extends ActivityAction { @@ -271,6 +273,12 @@ export class AccountsActivityActionViewItem extends MenuActivityActionViewItem { static readonly ACCOUNTS_VISIBILITY_PREFERENCE_KEY = 'workbench.activity.showAccounts'; + private readonly groupedAccounts: Map = new Map(); + private readonly problematicProviders: Set = new Set(); + + private initialized = false; + private sessionFromEmbedder = getCurrentAuthenticationSessionInfo(this.credentialsService, this.productService); + constructor( action: ActivityAction, contextMenuActionsProvider: () => IAction[], @@ -288,65 +296,110 @@ export class AccountsActivityActionViewItem extends MenuActivityActionViewItem { @IStorageService private readonly storageService: IStorageService, @IKeybindingService keybindingService: IKeybindingService, @ICredentialsService private readonly credentialsService: ICredentialsService, + @ILogService private readonly logService: ILogService ) { super(MenuId.AccountsContext, action, contextMenuActionsProvider, true, colors, activityHoverOptions, themeService, hoverService, menuService, contextMenuService, contextKeyService, configurationService, environmentService, keybindingService); + this.registerListeners(); + this.initialize(); + } + + private registerListeners(): void { + this._register(this.authenticationService.onDidRegisterAuthenticationProvider(async (e) => { + await this.addAccountsFromProvider(e.id); + })); + + this._register(this.authenticationService.onDidUnregisterAuthenticationProvider((e) => { + this.groupedAccounts.delete(e.id); + this.problematicProviders.delete(e.id); + })); + + this._register(this.authenticationService.onDidChangeSessions(async e => { + const promises = []; + for (const changed of e.event.changed) { + promises.push(this.addOrUpdateAccount(e.providerId, changed.account)); + } + for (const added of e.event.added) { + promises.push(this.addOrUpdateAccount(e.providerId, added.account)); + } + const result = await Promise.allSettled(promises); + for (const r of result) { + if (r.status === 'rejected') { + this.logService.error(r.reason); + } + } + for (const removed of e.event.removed) { + this.removeAccount(e.providerId, removed.account); + } + })); } + // This function exists to ensure that the accounts are added for auth providers that had already been registered + // before the menu was created. + private async initialize(): Promise { + const providerIds = this.authenticationService.getProviderIds(); + const results = await Promise.allSettled(providerIds.map(providerId => this.addAccountsFromProvider(providerId))); + + // Log any errors that occurred while initializing. We try to be best effort here to show the most amount of accounts + for (const result of results) { + if (result.status === 'rejected') { + this.logService.error(result.reason); + } + } + + this.initialized = true; + } + + //#region overrides + protected override async resolveMainMenuActions(accountsMenu: IMenu, disposables: DisposableStore): Promise { await super.resolveMainMenuActions(accountsMenu, disposables); - const otherCommands = accountsMenu.getActions(); const providers = this.authenticationService.getProviderIds(); - const allSessions = providers.map(async providerId => { - try { - const sessions = await this.authenticationService.getSessions(providerId); - - const groupedSessions: { [label: string]: AuthenticationSession[] } = {}; - sessions.forEach(session => { - if (groupedSessions[session.account.label]) { - groupedSessions[session.account.label].push(session); - } else { - groupedSessions[session.account.label] = [session]; - } - }); + const otherCommands = accountsMenu.getActions(); + let menus: IAction[] = []; - return { providerId, sessions: groupedSessions }; - } catch { - return { providerId }; + for (const providerId of providers) { + if (!this.initialized) { + const noAccountsAvailableAction = disposables.add(new Action('noAccountsAvailable', localize('loading', "Loading..."), undefined, false)); + menus.push(noAccountsAvailableAction); + break; + } + const providerLabel = this.authenticationService.getLabel(providerId); + const accounts = this.groupedAccounts.get(providerId); + if (!accounts) { + if (this.problematicProviders.has(providerId)) { + const providerUnavailableAction = disposables.add(new Action('providerUnavailable', localize('authProviderUnavailable', '{0} is currently unavailable', providerLabel), undefined, false)); + menus.push(providerUnavailableAction); + // try again in the background so that if the failure was intermittent, we can resolve it on the next showing of the menu + try { + await this.addAccountsFromProvider(providerId); + } catch (e) { + this.logService.error(e); + } + } + continue; } - }); - - const result = await Promise.all(allSessions); - let menus: IAction[] = []; - const authenticationSession = await getCurrentAuthenticationSessionInfo(this.credentialsService, this.productService); - result.forEach(sessionInfo => { - const providerDisplayName = this.authenticationService.getLabel(sessionInfo.providerId); - - if (sessionInfo.sessions) { - Object.keys(sessionInfo.sessions).forEach(accountName => { - const manageExtensionsAction = disposables.add(new Action(`configureSessions${accountName}`, localize('manageTrustedExtensions', "Manage Trusted Extensions"), '', true, () => { - return this.authenticationService.manageTrustedExtensionsForAccount(sessionInfo.providerId, accountName); - })); - const signOutAction = disposables.add(new Action('signOut', localize('signOut', "Sign Out"), '', true, () => { - return this.authenticationService.removeAccountSessions(sessionInfo.providerId, accountName, sessionInfo.sessions[accountName]); - })); + for (const account of accounts) { + const manageExtensionsAction = disposables.add(new Action(`configureSessions${account.label}`, localize('manageTrustedExtensions', "Manage Trusted Extensions"), undefined, true, () => { + return this.authenticationService.manageTrustedExtensionsForAccount(providerId, account.label); + })); - const providerSubMenuActions = [manageExtensionsAction]; + const providerSubMenuActions: Action[] = [manageExtensionsAction]; - const hasEmbedderAccountSession = sessionInfo.sessions[accountName].some(session => session.id === (authenticationSession?.id)); - if (!hasEmbedderAccountSession || authenticationSession?.canSignOut) { - providerSubMenuActions.push(signOutAction); - } + if (account.canSignOut) { + const signOutAction = disposables.add(new Action('signOut', localize('signOut', "Sign Out"), undefined, true, async () => { + const allSessions = await this.authenticationService.getSessions(providerId); + const sessionsForAccount = allSessions.filter(s => s.account.id === account.id); + return await this.authenticationService.removeAccountSessions(providerId, account.label, sessionsForAccount); + })); + providerSubMenuActions.push(signOutAction); + } - const providerSubMenu = new SubmenuAction('activitybar.submenu', `${accountName} (${providerDisplayName})`, providerSubMenuActions); - menus.push(providerSubMenu); - }); - } else { - const providerUnavailableAction = disposables.add(new Action('providerUnavailable', localize('authProviderUnavailable', '{0} is currently unavailable', providerDisplayName))); - menus.push(providerUnavailableAction); + const providerSubMenu = new SubmenuAction('activitybar.submenu', `${account.label} (${providerLabel})`, providerSubMenuActions); + menus.push(providerSubMenu); } - }); + } if (providers.length && !menus.length) { const noAccountsAvailableAction = disposables.add(new Action('noAccountsAvailable', localize('noAccounts', "You are not signed in to any accounts"), undefined, false)); @@ -378,6 +431,77 @@ export class AccountsActivityActionViewItem extends MenuActivityActionViewItem { return actions; } + + //#endregion + + //#region groupedAccounts helpers + + private async addOrUpdateAccount(providerId: string, account: AuthenticationSessionAccount): Promise { + const accounts = this.groupedAccounts.get(providerId); + const existingAccount = accounts?.find(a => a.id === account.id); + if (existingAccount) { + if (existingAccount.label !== account.label) { + existingAccount.label = account.label; + } + return; + } + + const sessionFromEmbedder = await this.sessionFromEmbedder; + // If the session stored from the embedder allows sign out, then we can treat it and all others as sign out-able + let canSignOut = !!sessionFromEmbedder?.canSignOut; + if (!canSignOut) { + if (sessionFromEmbedder?.id) { + const sessions = (await this.authenticationService.getSessions(providerId)).filter(s => s.account.id === account.id); + canSignOut = !sessions.some(s => s.id === sessionFromEmbedder.id); + } else { + // The default if we don't have a session from the embedder is to allow sign out + canSignOut = true; + } + } + + if (!accounts) { + this.groupedAccounts.set(providerId, [{ ...account, canSignOut }]); + return; + } + + accounts.push({ ...account, canSignOut }); + } + + private removeAccount(providerId: string, account: AuthenticationSessionAccount): void { + const accounts = this.groupedAccounts.get(providerId); + if (!accounts) { + return; + } + + const index = accounts.findIndex(a => a.id === account.id); + if (index === -1) { + return; + } + + accounts.splice(index, 1); + if (accounts.length === 0) { + this.groupedAccounts.delete(providerId); + } + } + + private async addAccountsFromProvider(providerId: string): Promise { + try { + const sessions = await this.authenticationService.getSessions(providerId); + this.problematicProviders.delete(providerId); + + const result = await Promise.allSettled(sessions.map(s => this.addOrUpdateAccount(providerId, s.account))); + for (const r of result) { + if (r.status === 'rejected') { + this.logService.error(r.reason); + } + } + } catch (e) { + this.logService.error(e); + this.problematicProviders.add(providerId); + } + } + + //#endregion } export interface IProfileActivity extends IActivity { @@ -386,6 +510,9 @@ export interface IProfileActivity extends IActivity { export class GlobalActivityActionViewItem extends MenuActivityActionViewItem { + private profileBadge: HTMLElement | undefined; + private profileBadgeContent: HTMLElement | undefined; + constructor( action: ActivityAction, contextMenuActionsProvider: () => IAction[], @@ -402,6 +529,40 @@ export class GlobalActivityActionViewItem extends MenuActivityActionViewItem { @IKeybindingService keybindingService: IKeybindingService, ) { super(MenuId.GlobalActivity, action, contextMenuActionsProvider, true, colors, activityHoverOptions, themeService, hoverService, menuService, contextMenuService, contextKeyService, configurationService, environmentService, keybindingService); + this._register(Event.any(this.userDataProfileService.onDidUpdateCurrentProfile, this.userDataProfileService.onDidChangeCurrentProfile)(() => this.updateProfileBadge())); + } + + override render(container: HTMLElement): void { + super.render(container); + + this.profileBadge = append(container, $('.profile-badge')); + this.profileBadgeContent = append(this.profileBadge, $('.profile-badge-content')); + this.updateProfileBadge(); + } + + private updateProfileBadge(): void { + if (!this.profileBadge || !this.profileBadgeContent) { + return; + } + + clearNode(this.profileBadgeContent); + hide(this.profileBadge); + + if (this.userDataProfileService.currentProfile.isDefault) { + return; + } + + if ((this.action as ActivityAction).getBadge()) { + return; + } + + this.profileBadgeContent.textContent = this.userDataProfileService.currentProfile.name.substring(0, 2).toUpperCase(); + show(this.profileBadge); + } + + protected override updateBadge(): void { + super.updateBadge(); + this.updateProfileBadge(); } protected override computeTitle(): string { diff --git a/vscode-web/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/vscode-web/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts index d4a7a2869..3e7c6e1ff 100644 --- a/vscode-web/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts +++ b/vscode-web/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts @@ -58,7 +58,6 @@ interface IPlaceholderViewContainer { interface IPinnedViewContainer { readonly id: string; readonly pinned: boolean; - readonly badgeEnabled: boolean; readonly order?: number; readonly visible: boolean; } @@ -68,7 +67,6 @@ interface ICachedViewContainer { name?: string; icon?: URI | ThemeIcon; readonly pinned: boolean; - readonly badgeEnabled: boolean; readonly order?: number; visible: boolean; isBuiltin?: boolean; @@ -159,7 +157,6 @@ export class ActivitybarPart extends Part implements IPaneCompositeSelectorPart name: container.name, visible: container.visible, order: container.order, - badgeEnabled: container.badgeEnabled, pinned: container.pinned, })); @@ -761,7 +758,7 @@ export class ActivitybarPart extends Part implements IPaneCompositeSelectorPart cachedViewContainer = cachedViewContainer || this.cachedViewContainers.find(({ id }) => id === viewContainerId); // Show builtin ViewContainer if not registered yet - if (!viewContainer && cachedViewContainer?.isBuiltin) { + if (!viewContainer && cachedViewContainer?.isBuiltin && cachedViewContainer?.visible) { return false; } @@ -884,7 +881,6 @@ export class ActivitybarPart extends Part implements IPaneCompositeSelectorPart id: cachedViewContainer.id, name: cachedViewContainer.name, order: cachedViewContainer.order, - badgeEnabled: cachedViewContainer.badgeEnabled, pinned: cachedViewContainer.pinned, visible: !!compositeItems.find(({ id }) => id === cachedViewContainer.id) }); @@ -929,13 +925,12 @@ export class ActivitybarPart extends Part implements IPaneCompositeSelectorPart icon: URI.isUri(viewContainerModel.icon) && this.environmentService.remoteAuthority ? undefined : viewContainerModel.icon, /* Donot cache uri icons with remote connection */ views, pinned: compositeItem.pinned, - badgeEnabled: compositeItem.badgeEnabled, order: compositeItem.order, visible: compositeItem.visible, isBuiltin: !viewContainer.extensionId }); } else { - state.push({ id: compositeItem.id, pinned: compositeItem.pinned, badgeEnabled: compositeItem.badgeEnabled, order: compositeItem.order, visible: false, isBuiltin: false }); + state.push({ id: compositeItem.id, pinned: compositeItem.pinned, order: compositeItem.order, visible: false, isBuiltin: false }); } } diff --git a/vscode-web/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css b/vscode-web/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css index 14efd257f..1119b5a33 100644 --- a/vscode-web/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css +++ b/vscode-web/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css @@ -127,7 +127,6 @@ /* Window Title Menu */ .monaco-workbench .part.titlebar > .titlebar-container > .titlebar-center > .window-title > .command-center { z-index: 2500; - padding: 0 8px; } /* MacOS Desktop supports click event despite `drag` and therefore we don't need to clear it */ @@ -141,7 +140,7 @@ } .monaco-workbench .part.titlebar > .titlebar-container > .titlebar-center > .window-title > .command-center .action-item > .action-label { - color: var(--vscode-titleBar-foreground); + color: var(--vscode-titleBar-activeForeground); } .monaco-workbench .part.titlebar.inactive > .titlebar-container > .titlebar-center > .window-title > .command-center .action-item > .action-label { @@ -159,7 +158,7 @@ background-color: var(--vscode-commandCenter-background); border: 1px solid var(--vscode-commandCenter-border); overflow: hidden; - margin: 0 6px; + margin-left: 6px; border-top-left-radius: 6px; border-bottom-left-radius: 6px; border-top-right-radius: 6px; @@ -169,6 +168,10 @@ max-width: 600px; } +.monaco-workbench .part.titlebar > .titlebar-container > .titlebar-center > .window-title > .command-center .action-item.command-center:only-child { + margin-left: 0; /* no margin if there is only the command center, without nav buttons */ +} + .monaco-workbench .part.titlebar.inactive > .titlebar-container > .titlebar-center > .window-title > .command-center .action-item.command-center { color: var(--vscode-titleBar-inactiveForeground); border-color: var(--vscode-commandCenter-inactiveBorder) !important; @@ -379,7 +382,7 @@ } .monaco-workbench .part.titlebar .window-controls-container .window-icon { - color: var(--vscode-titleBarActiveForeground); + color: var(--vscode-titleBar-activeForeground); } .monaco-workbench .part.titlebar.inactive .window-controls-container .window-icon { diff --git a/vscode-web/src/vs/workbench/browser/web.main.ts b/vscode-web/src/vs/workbench/browser/web.main.ts index 850fbabf6..385285423 100644 --- a/vscode-web/src/vs/workbench/browser/web.main.ts +++ b/vscode-web/src/vs/workbench/browser/web.main.ts @@ -7,7 +7,7 @@ import { mark } from 'vs/base/common/performance'; import { domContentLoaded, detectFullscreen, getCookieValue } from 'vs/base/browser/dom'; import { assertIsDefined } from 'vs/base/common/types'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -import { ILogService, ConsoleLogger, getLogLevel, ILoggerService } from 'vs/platform/log/common/log'; +import { ILogService, ConsoleLogger, getLogLevel, ILoggerService, ILogger } from 'vs/platform/log/common/log'; import { ConsoleLogInAutomationLogger } from 'vs/platform/log/browser/log'; import { Disposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; import { BrowserWorkbenchEnvironmentService, IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService'; @@ -244,7 +244,7 @@ export class BrowserMain extends Disposable { // Log const logLevel = getLogLevel(environmentService); const bufferLogger = new BufferLogger(logLevel); - const otherLoggers = [new ConsoleLogger(logLevel)]; + const otherLoggers: ILogger[] = [new ConsoleLogger(logLevel)]; if (environmentService.isExtensionDevelopment && !!environmentService.extensionTestsLocationURI) { otherLoggers.push(new ConsoleLogInAutomationLogger(logLevel)); } @@ -271,20 +271,14 @@ export class BrowserMain extends Disposable { // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - // Remote Agent - const remoteAgentService = this._register(new RemoteAgentService(this.configuration.webSocketFactory, environmentService, productService, remoteAuthorityResolverService, signService, logService)); - serviceCollection.set(IRemoteAgentService, remoteAgentService); - // Files const fileService = this._register(new FileService(logService)); serviceCollection.set(IWorkbenchFileService, fileService); // Logger - const loggerService = new FileLoggerService(logLevel, fileService); + const loggerService = new FileLoggerService(logLevel, logsPath, fileService); serviceCollection.set(ILoggerService, loggerService); - await this.registerFileSystemProviders(environmentService, fileService, remoteAgentService, bufferLogger, logService, loggerService, logsPath); - // URI Identity const uriIdentityService = new UriIdentityService(fileService); serviceCollection.set(IUriIdentityService, uriIdentityService); @@ -292,15 +286,17 @@ export class BrowserMain extends Disposable { // User Data Profiles const userDataProfilesService = new BrowserUserDataProfilesService(environmentService, fileService, uriIdentityService, logService); serviceCollection.set(IUserDataProfilesService, userDataProfilesService); - if (environmentService.remoteAuthority) { - // Always Disabled in web with remote connection - userDataProfilesService.setEnablement(false); - } const currentProfile = userDataProfilesService.getProfileForWorkspace(workspace) ?? userDataProfilesService.defaultProfile; const userDataProfileService = new UserDataProfileService(currentProfile, userDataProfilesService); serviceCollection.set(IUserDataProfileService, userDataProfileService); + // Remote Agent + const remoteAgentService = this._register(new RemoteAgentService(this.configuration.webSocketFactory, userDataProfileService, environmentService, productService, remoteAuthorityResolverService, signService, logService)); + serviceCollection.set(IRemoteAgentService, remoteAgentService); + + await this.registerFileSystemProviders(environmentService, fileService, remoteAgentService, bufferLogger, logService, loggerService, logsPath); + // Long running services (workspace, config, storage) const [configurationService, storageService] = await Promise.all([ this.createWorkspaceService(workspace, environmentService, userDataProfileService, userDataProfilesService, fileService, remoteAgentService, uriIdentityService, logService).then(service => { @@ -345,7 +341,7 @@ export class BrowserMain extends Disposable { this._register(workspaceTrustManagementService.onDidChangeTrust(() => configurationService.updateWorkspaceTrust(workspaceTrustManagementService.isWorkspaceTrusted()))); // Request Service - const requestService = new BrowserRequestService(remoteAgentService, configurationService, logService); + const requestService = new BrowserRequestService(remoteAgentService, configurationService, loggerService); serviceCollection.set(IRequestService, requestService); // Userdata Sync Store Management Service diff --git a/vscode-web/src/vs/workbench/contrib/files/browser/editors/fileEditorInput.ts b/vscode-web/src/vs/workbench/contrib/files/browser/editors/fileEditorInput.ts index 68bf9160e..b486a7873 100644 --- a/vscode-web/src/vs/workbench/contrib/files/browser/editors/fileEditorInput.ts +++ b/vscode-web/src/vs/workbench/contrib/files/browser/editors/fileEditorInput.ts @@ -181,9 +181,9 @@ export class FileEditorInput extends AbstractTextResourceEditorInput implements } private allowLabelOverride(): boolean { - /* below codes are changed by github1s */ - if (window?.vscodeWeb?.allowEditorLabelOverride) return true; - /* above codes are changed by github1s */ + /* below codes are changed by github1s */ + if (window?.vscodeWeb?.allowEditorLabelOverride) return true; + /* above codes are changed by github1s */ return this.resource.scheme !== this.pathService.defaultUriScheme && this.resource.scheme !== Schemas.vscodeUserData && this.resource.scheme !== Schemas.file && diff --git a/vscode-web/src/vs/workbench/contrib/webview/browser/pre/index.html b/vscode-web/src/vs/workbench/contrib/webview/browser/pre/index.html index 09f720d50..27fc9c005 100644 --- a/vscode-web/src/vs/workbench/contrib/webview/browser/pre/index.html +++ b/vscode-web/src/vs/workbench/contrib/webview/browser/pre/index.html @@ -131,21 +131,16 @@ } kbd { - color: var(--vscode-editor-foreground); + background-color: var(--vscode-keybindingLabel-background); + color: var(--vscode-keybindingLabel-foreground); + border-style: solid; + border-width: 1px; border-radius: 3px; + border-color: var(--vscode-keybindingLabel-border); + border-bottom-color: var(--vscode-keybindingLabel-bottomBorder); + box-shadow: inset 0 -1px 0 var(--vscode-widget-shadow); vertical-align: middle; padding: 1px 3px; - - background-color: hsla(0,0%,50%,.17); - border: 1px solid rgba(71,71,71,.4); - border-bottom-color: rgba(88,88,88,.4); - box-shadow: inset 0 -1px 0 rgba(88,88,88,.4); - } - .vscode-light kbd { - background-color: hsla(0,0%,87%,.5); - border: 1px solid hsla(0,0%,80%,.7); - border-bottom-color: hsla(0,0%,73%,.7); - box-shadow: inset 0 -1px 0 hsla(0,0%,73%,.7); } ::-webkit-scrollbar { @@ -223,9 +218,8 @@ return reject(new Error('Service Workers are not enabled. Webviews will not work. Try disabling private/incognito mode.')); } - const swPath = `service-worker.js?v=${expectedWorkerVersion}&vscode-resource-base-authority=${searchParams.get('vscode-resource-base-authority')}&remoteAuthority=${searchParams.get('remoteAuthority') ?? ''}`; + const swPath = encodeURI(`service-worker.js?v=${expectedWorkerVersion}&vscode-resource-base-authority=${searchParams.get('vscode-resource-base-authority')}&remoteAuthority=${searchParams.get('remoteAuthority') ?? ''}`); navigator.serviceWorker.register(swPath) - .then(() => navigator.serviceWorker.ready) .then(async registration => { /** * @param {MessageEvent} event @@ -247,7 +241,6 @@ // `unregister` and `register` here. return registration.unregister() .then(() => navigator.serviceWorker.register(swPath)) - .then(() => navigator.serviceWorker.ready) .finally(() => { resolve(); }); } }; @@ -266,6 +259,8 @@ // service worker already loaded & ready to receive messages postVersionMessage(currentController); } else { + console.log(`Found unexpected service worker controller. Found: ${currentController?.scriptURL}. Expected: ${swPath}`); + // either there's no controlling service worker, or it's an old one: // wait for it to change before posting the message const onControllerChange = () => { @@ -275,7 +270,10 @@ navigator.serviceWorker.addEventListener('controllerchange', onControllerChange); } }).catch(error => { - reject(new Error(`Could not register service workers: ${error}.`)); + if (!onElectron && error instanceof Error && error.message.includes('user denied permission')) { + return reject(new Error(`Could not register service worker. Please make sure third party cookies are enabled: ${error}`)); + } + return reject(new Error(`Could not register service worker: ${error}.`)); }); }); @@ -409,13 +407,13 @@ }; hostMessaging.onMessage('did-load-resource', (_event, data) => { - navigator.serviceWorker.ready.then(registration => { + navigator.serviceWorker.getRegistration().then(registration => { assertIsDefined(registration.active).postMessage({ channel: 'did-load-resource', data }, data.data?.buffer ? [data.data.buffer] : []); }); }); hostMessaging.onMessage('did-load-localhost', (_event, data) => { - navigator.serviceWorker.ready.then(registration => { + navigator.serviceWorker.getRegistration().then(registration => { assertIsDefined(registration.active).postMessage({ channel: 'did-load-localhost', data }); }); }); @@ -573,7 +571,7 @@ /** * @param {KeyboardEvent} e */ - const handleInnerUp = (e) => { + const handleInnerKeyup = (e) => { hostMessaging.postMessage('did-keyup', { key: e.key, keyCode: e.keyCode, @@ -802,6 +800,12 @@ return '\n' + newDocument.documentElement.outerHTML; } + // Also forward events before the contents of the webview have loaded + window.addEventListener('keydown', handleInnerKeydown); + window.addEventListener('keyup', handleInnerKeyup); + window.addEventListener('dragenter', handleInnerDragStartEvent); + window.addEventListener('dragover', handleInnerDragStartEvent); + onDomReady(() => { if (!document.body) { return; @@ -932,6 +936,9 @@ newFrame.style.cssText = 'display: block; margin: 0; overflow: hidden; position: absolute; width: 100%; height: 100%; visibility: hidden'; document.body.appendChild(newFrame); + newFrame.contentWindow.addEventListener('keydown', handleInnerKeydown); + newFrame.contentWindow.addEventListener('keyup', handleInnerKeyup); + /** * @param {Document} contentDocument */ @@ -1039,7 +1046,7 @@ contentWindow.addEventListener('click', handleInnerClick); contentWindow.addEventListener('auxclick', handleAuxClick); contentWindow.addEventListener('keydown', handleInnerKeydown); - contentWindow.addEventListener('keyup', handleInnerUp); + contentWindow.addEventListener('keyup', handleInnerKeyup); contentWindow.addEventListener('contextmenu', e => { if (e.defaultPrevented) { // Extension code has already handled this event @@ -1180,11 +1187,6 @@ } }; - // Also forward events before the contents of the webview have loaded - window.addEventListener('keydown', handleInnerKeydown); - window.addEventListener('dragenter', handleInnerDragStartEvent); - window.addEventListener('dragover', handleInnerDragStartEvent); - hostMessaging.signalReady(); }); diff --git a/vscode-web/src/vs/workbench/services/extensionManagement/browser/builtinExtensionsScannerService.ts b/vscode-web/src/vs/workbench/services/extensionManagement/browser/builtinExtensionsScannerService.ts index 24bd71ec4..ef32ef478 100644 --- a/vscode-web/src/vs/workbench/services/extensionManagement/browser/builtinExtensionsScannerService.ts +++ b/vscode-web/src/vs/workbench/services/extensionManagement/browser/builtinExtensionsScannerService.ts @@ -15,13 +15,11 @@ import { IExtensionResourceLoaderService } from 'vs/platform/extensionResourceLo import { IProductService } from 'vs/platform/product/common/productService'; import { ITranslations, localizeManifest } from 'vs/platform/extensionManagement/common/extensionNls'; import { ILogService } from 'vs/platform/log/common/log'; -import { ImplicitActivationEvents } from 'vs/platform/extensionManagement/common/implicitActivationEvents'; interface IBundledExtension { extensionPath: string; packageJSON: IExtensionManifest; packageNLS?: any; - browserNlsMetadataPath?: string; readmePath?: string; changelogPath?: string; } @@ -76,20 +74,11 @@ export class BuiltinExtensionsScannerService implements IBuiltinExtensionsScanne this.builtinExtensionsPromises = bundledExtensions.map(async e => { const id = getGalleryExtensionId(e.packageJSON.publisher, e.packageJSON.name); - const browserNlsBundleUris: { [language: string]: URI } = {}; - if (e.browserNlsMetadataPath) { - if (this.nlsUrl) { - browserNlsBundleUris[Language.value()] = uriIdentityService.extUri.joinPath(this.nlsUrl, id, 'main'); - } - browserNlsBundleUris.en = uriIdentityService.extUri.resolvePath(builtinExtensionsServiceUrl!, e.browserNlsMetadataPath); - } - ImplicitActivationEvents.updateManifest(e.packageJSON); return { identifier: { id }, location: uriIdentityService.extUri.joinPath(builtinExtensionsServiceUrl!, e.extensionPath), type: ExtensionType.System, isBuiltin: true, - browserNlsBundleUris, manifest: e.packageNLS ? await this.localizeManifest(id, e.packageJSON, e.packageNLS) : e.packageJSON, readmeUrl: e.readmePath ? uriIdentityService.extUri.joinPath(builtinExtensionsServiceUrl!, e.readmePath) : undefined, changelogUrl: e.changelogPath ? uriIdentityService.extUri.joinPath(builtinExtensionsServiceUrl!, e.changelogPath) : undefined, diff --git a/vscode-web/src/vs/workbench/services/label/common/labelService.ts b/vscode-web/src/vs/workbench/services/label/common/labelService.ts index da19081c1..e176b0e25 100644 --- a/vscode-web/src/vs/workbench/services/label/common/labelService.ts +++ b/vscode-web/src/vs/workbench/services/label/common/labelService.ts @@ -5,7 +5,7 @@ import { localize } from 'vs/nls'; import { URI } from 'vs/base/common/uri'; -import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; +import { IDisposable, Disposable, dispose } from 'vs/base/common/lifecycle'; import { posix, sep, win32 } from 'vs/base/common/path'; import { Emitter } from 'vs/base/common/event'; import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry, IWorkbenchContribution } from 'vs/workbench/common/contributions'; @@ -89,30 +89,34 @@ class ResourceLabelFormattersHandler implements IWorkbenchContribution { constructor(@ILabelService labelService: ILabelService) { resourceLabelFormattersExtPoint.setHandler((extensions, delta) => { - delta.added.forEach(added => added.value.forEach(untrustedFormatter => { + for (const added of delta.added) { + for (const untrustedFormatter of added.value) { - // We cannot trust that the formatter as it comes from an extension - // adheres to our interface, so for the required properties we fill - // in some defaults if missing. + // We cannot trust that the formatter as it comes from an extension + // adheres to our interface, so for the required properties we fill + // in some defaults if missing. - const formatter = { ...untrustedFormatter }; - if (typeof formatter.formatting.label !== 'string') { - formatter.formatting.label = '${authority}${path}'; - } - if (typeof formatter.formatting.separator !== `string`) { - formatter.formatting.separator = sep; - } + const formatter = { ...untrustedFormatter }; + if (typeof formatter.formatting.label !== 'string') { + formatter.formatting.label = '${authority}${path}'; + } + if (typeof formatter.formatting.separator !== `string`) { + formatter.formatting.separator = sep; + } - if (!isProposedApiEnabled(added.description, 'contribLabelFormatterWorkspaceTooltip') && formatter.formatting.workspaceTooltip) { - formatter.formatting.workspaceTooltip = undefined; // workspaceTooltip is only proposed - } + if (!isProposedApiEnabled(added.description, 'contribLabelFormatterWorkspaceTooltip') && formatter.formatting.workspaceTooltip) { + formatter.formatting.workspaceTooltip = undefined; // workspaceTooltip is only proposed + } - this.formattersDisposables.set(formatter, labelService.registerFormatter(formatter)); - })); + this.formattersDisposables.set(formatter, labelService.registerFormatter(formatter)); + } + } - delta.removed.forEach(removed => removed.value.forEach(formatter => { - this.formattersDisposables.get(formatter)!.dispose(); - })); + for (const removed of delta.removed) { + for (const formatter of removed.value) { + dispose(this.formattersDisposables.get(formatter)); + } + } }); } } diff --git a/vscode-web/src/vs/workbench/services/textfile/browser/textFileService.ts b/vscode-web/src/vs/workbench/services/textfile/browser/textFileService.ts index 148211d95..a27edf1ac 100644 --- a/vscode-web/src/vs/workbench/services/textfile/browser/textFileService.ts +++ b/vscode-web/src/vs/workbench/services/textfile/browser/textFileService.ts @@ -20,7 +20,7 @@ import { Schemas } from 'vs/base/common/network'; import { createTextBufferFactoryFromSnapshot, createTextBufferFactoryFromStream } from 'vs/editor/common/model/textModel'; import { IModelService } from 'vs/editor/common/services/model'; import { joinPath, dirname, basename, toLocalResource, extname, isEqual } from 'vs/base/common/resources'; -import { IDialogService, IFileDialogService, IConfirmation } from 'vs/platform/dialogs/common/dialogs'; +import { IDialogService, IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; import { VSBuffer, VSBufferReadable, bufferToStream, VSBufferReadableStream } from 'vs/base/common/buffer'; import { ITextSnapshot, ITextModel } from 'vs/editor/common/model'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfiguration'; @@ -535,7 +535,7 @@ export abstract class AbstractTextFileService extends Disposable implements ITex const sourceLanguageId = sourceTextModel.getLanguageId(); const targetLanguageId = targetTextModel.getLanguageId(); if (sourceLanguageId !== PLAINTEXT_LANGUAGE_ID && targetLanguageId === PLAINTEXT_LANGUAGE_ID) { - targetTextModel.setMode(sourceLanguageId); // only use if more specific than plain/text + targetTextModel.setLanguage(sourceLanguageId); // only use if more specific than plain/text } // transient properties @@ -560,14 +560,14 @@ export abstract class AbstractTextFileService extends Disposable implements ITex } private async confirmOverwrite(resource: URI): Promise { - const confirm: IConfirmation = { + const { confirmed } = await this.dialogService.confirm({ + type: 'warning', message: localize('confirmOverwrite', "'{0}' already exists. Do you want to replace it?", basename(resource)), detail: localize('irreversible', "A file or folder with the name '{0}' already exists in the folder '{1}'. Replacing it will overwrite its current contents.", basename(resource), basename(dirname(resource))), primaryButton: localize({ key: 'replaceButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Replace"), - type: 'warning' - }; + }); - return (await this.dialogService.confirm(confirm)).confirmed; + return confirmed; } private async suggestSavePath(resource: URI): Promise { @@ -595,13 +595,18 @@ export abstract class AbstractTextFileService extends Disposable implements ITex // of untitled model if it is a valid path name and // figure out the file extension from the mode if any. + let nameCandidate: string; if (await this.pathService.hasValidBasename(joinPath(defaultFilePath, model.name), model.name)) { - const languageId = model.getLanguageId(); - if (languageId && languageId !== PLAINTEXT_LANGUAGE_ID) { - suggestedFilename = this.suggestFilename(languageId, model.name); - } else { - suggestedFilename = model.name; - } + nameCandidate = model.name; + } else { + nameCandidate = basename(resource); + } + + const languageId = model.getLanguageId(); + if (languageId && languageId !== PLAINTEXT_LANGUAGE_ID) { + suggestedFilename = this.suggestFilename(languageId, nameCandidate); + } else { + suggestedFilename = nameCandidate; } } } diff --git a/vscode-web/yarn.lock b/vscode-web/yarn.lock index 971cc9f5f..8e2bb31f2 100644 --- a/vscode-web/yarn.lock +++ b/vscode-web/yarn.lock @@ -494,10 +494,10 @@ vscode-oniguruma@1.7.0: resolved "https://registry.yarnpkg.com/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz#439bfad8fe71abd7798338d1cd3dc53a8beea94b" integrity sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA== -vscode-textmate@8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-8.0.0.tgz#2c7a3b1163ef0441097e0b5d6389cd5504b59e5d" - integrity sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg== +vscode-textmate@9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-9.0.0.tgz#313c6c8792b0507aef35aeb81b6b370b37c44d6c" + integrity sha512-Cl65diFGxz7gpwbav10HqiY/eVYTO1sjQpmRmV991Bj7wAoOAjGQ97PpQcXorDE2Uc4hnGWLY17xme+5t6MlSg== which-boxed-primitive@^1.0.2: version "1.0.2" @@ -532,12 +532,12 @@ xterm-addon-unicode11@0.5.0: resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.5.0.tgz#41c0d96acc1e3bb6c6596eee64e163b6bca74be7" integrity sha512-Jm4/g4QiTxiKiTbYICQgC791ubhIZyoIwxAIgOW8z8HWFNY+lwk+dwaKEaEeGBfM48Vk8fklsUW9u/PlenYEBg== -xterm-addon-webgl@0.15.0-beta.4: - version "0.15.0-beta.4" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.15.0-beta.4.tgz#d03a98e446372a5dbb7d92075d1f125eec23b030" - integrity sha512-W9N0+5i3trQhgBOHDsnNiBbBiJpleFenY668wWaZ9GlvWseCTnjnWis1kfnM9WASDh/0+7aOjWrD089o+QeHGQ== +xterm-addon-webgl@0.15.0-beta.7: + version "0.15.0-beta.7" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.15.0-beta.7.tgz#ab247b499f61e8eebff92e08ec5ca999d87e06af" + integrity sha512-7WCI/D6uFNp3y9TeTsbSo1h7gCy4h/yP2lWn8ZEjCaiGvO11DbKMq17fbiwaR3YmGWXoRKkcLaNIiqxFnjKO4w== -xterm@5.2.0-beta.21: - version "5.2.0-beta.21" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.2.0-beta.21.tgz#13462f37e7030897f5aacb3f27f524db530273dd" - integrity sha512-7KsDc+goSKIuV1s9mhU/o3iXN0f/PHfB0/11zsN2jADlGZGAO9Xe58pZXd35P8AgLsl4w7HctrMXW6t25l3ExQ== +xterm@5.2.0-beta.30: + version "5.2.0-beta.30" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.2.0-beta.30.tgz#6f50796d1652a61b30eeed7fa2bdd9c485a7d8ee" + integrity sha512-l1YBwMnakKXd638oxbzEg9Y1sWqxcrm/q7i5gBuWaK8N7Tq1NvF51FCamxXtfdL4dostgw8WoM+/6KRlL53t6A== diff --git a/yarn.lock b/yarn.lock index 94813303f..8aee93be8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -31,22 +31,22 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@github1s/vscode-web@0.8.0": - version "0.8.0" - resolved "https://registry.yarnpkg.com/@github1s/vscode-web/-/vscode-web-0.8.0.tgz#89cca96ba7870fe0fb487ed60fe779e07e0e5405" - integrity sha512-7VeH/Ay4lMR0zbrfn+GZ88dueAbPkXvv7bOD3FrE4wZVYQOyZVq1wjxRy9z0unCfAi3O4eR33oa35PnDwLkg/Q== +"@github1s/vscode-web@0.9.0": + version "0.9.0" + resolved "https://registry.yarnpkg.com/@github1s/vscode-web/-/vscode-web-0.9.0.tgz#4531745452dbd10be0fda8470818c5bf17fddc87" + integrity sha512-M/t0PExhgq09wgIYLnvcAlTdRJaR9m+gKP/sp7dmdkjTV9YQElfSCGMzEFfKN8fW3FASGunbZ2Hq79bpr0TCcA== dependencies: "@vscode/iconv-lite-umd" "0.7.0" "@vscode/vscode-languagedetection" "1.0.21" jschardet "3.0.0" tas-client-umd "0.1.6" vscode-oniguruma "1.7.0" - vscode-textmate "8.0.0" - xterm "5.2.0-beta.21" + vscode-textmate "9.0.0" + xterm "5.2.0-beta.30" xterm-addon-canvas "0.4.0-beta.7" xterm-addon-search "0.11.0" xterm-addon-unicode11 "0.5.0" - xterm-addon-webgl "0.15.0-beta.4" + xterm-addon-webgl "0.15.0-beta.7" "@hapi/hoek@^9.0.0": version "9.3.0" @@ -579,21 +579,11 @@ acorn-jsx@^5.3.2: resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== -acorn@^8.5.0: - version "8.7.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.1.tgz#0197122c843d1bf6d0a5e83220a788f278f63c30" - integrity sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A== - -acorn@^8.7.1: +acorn@^8.5.0, acorn@^8.7.1, acorn@^8.8.0: version "8.8.2" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== -acorn@^8.8.0: - version "8.8.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8" - integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w== - aggregate-error@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" @@ -4076,10 +4066,10 @@ vscode-oniguruma@1.7.0: resolved "https://registry.yarnpkg.com/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz#439bfad8fe71abd7798338d1cd3dc53a8beea94b" integrity sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA== -vscode-textmate@8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-8.0.0.tgz#2c7a3b1163ef0441097e0b5d6389cd5504b59e5d" - integrity sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg== +vscode-textmate@9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-9.0.0.tgz#313c6c8792b0507aef35aeb81b6b370b37c44d6c" + integrity sha512-Cl65diFGxz7gpwbav10HqiY/eVYTO1sjQpmRmV991Bj7wAoOAjGQ97PpQcXorDE2Uc4hnGWLY17xme+5t6MlSg== wait-on@6.0.0: version "6.0.0" @@ -4185,9 +4175,9 @@ webpack-sources@^3.2.3: integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== webpack@^5.75.0: - version "5.75.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.75.0.tgz#1e440468647b2505860e94c9ff3e44d5b582c152" - integrity sha512-piaIaoVJlqMsPtX/+3KTTO6jfvrSYgauFVdt8cr9LTHKmcq/AMd4mhzsiP7ZF/PGRNPGA8336jldh9l2Kt2ogQ== + version "5.76.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.76.0.tgz#f9fb9fb8c4a7dbdcd0d56a98e56b8a942ee2692c" + integrity sha512-l5sOdYBDunyf72HW8dF23rFtWq/7Zgvt/9ftMof71E/yUb1YLOBmTgA2K4vQthB3kotMrSj609txVE0dnr2fjA== dependencies: "@types/eslint-scope" "^3.7.3" "@types/estree" "^0.0.51" @@ -4306,15 +4296,15 @@ xterm-addon-unicode11@0.5.0: resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.5.0.tgz#41c0d96acc1e3bb6c6596eee64e163b6bca74be7" integrity sha512-Jm4/g4QiTxiKiTbYICQgC791ubhIZyoIwxAIgOW8z8HWFNY+lwk+dwaKEaEeGBfM48Vk8fklsUW9u/PlenYEBg== -xterm-addon-webgl@0.15.0-beta.4: - version "0.15.0-beta.4" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.15.0-beta.4.tgz#d03a98e446372a5dbb7d92075d1f125eec23b030" - integrity sha512-W9N0+5i3trQhgBOHDsnNiBbBiJpleFenY668wWaZ9GlvWseCTnjnWis1kfnM9WASDh/0+7aOjWrD089o+QeHGQ== +xterm-addon-webgl@0.15.0-beta.7: + version "0.15.0-beta.7" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.15.0-beta.7.tgz#ab247b499f61e8eebff92e08ec5ca999d87e06af" + integrity sha512-7WCI/D6uFNp3y9TeTsbSo1h7gCy4h/yP2lWn8ZEjCaiGvO11DbKMq17fbiwaR3YmGWXoRKkcLaNIiqxFnjKO4w== -xterm@5.2.0-beta.21: - version "5.2.0-beta.21" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.2.0-beta.21.tgz#13462f37e7030897f5aacb3f27f524db530273dd" - integrity sha512-7KsDc+goSKIuV1s9mhU/o3iXN0f/PHfB0/11zsN2jADlGZGAO9Xe58pZXd35P8AgLsl4w7HctrMXW6t25l3ExQ== +xterm@5.2.0-beta.30: + version "5.2.0-beta.30" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.2.0-beta.30.tgz#6f50796d1652a61b30eeed7fa2bdd9c485a7d8ee" + integrity sha512-l1YBwMnakKXd638oxbzEg9Y1sWqxcrm/q7i5gBuWaK8N7Tq1NvF51FCamxXtfdL4dostgw8WoM+/6KRlL53t6A== y18n@^5.0.5: version "5.0.8"