diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 7cd980db1..8bb3051f5 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -2,7 +2,7 @@ # [Choice] Node.js version: 14, 12, 10 ARG VARIANT="14-buster" -FROM mcr.microsoft.com/vscode/devcontainers/typescript-node:0-${VARIANT} +FROM mcr.microsoft.com/devcontainers/typescript-node:${VARIANT} # same package list from github1s/scripts/preinstall.sh RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ @@ -13,7 +13,7 @@ RUN mkdir -p /opt/dev \ && cd /opt/dev \ && git clone https://github.com/emscripten-core/emsdk.git \ && cd /opt/dev/emsdk \ - && ./emsdk install 2.0.6 \ - && ./emsdk activate 2.0.6 + && ./emsdk install 3.1.21 \ + && ./emsdk activate 3.1.21 -ENV PATH="/opt/dev/emsdk:/opt/dev/emsdk/node/12.9.1_64bit/bin:/opt/dev/emsdk/upstream/emscripten:${PATH}" +ENV PATH="/opt/dev/emsdk:/opt/dev/emsdk/node/14.18.2_64bit/bin:/opt/dev/emsdk/upstream/emscripten:${PATH}" diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index b81e96bee..bdc4b626d 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -6,7 +6,7 @@ "dockerfile": "Dockerfile", // Update 'VARIANT' to pick a Node version: 10, 12, 14 "args": { - "VARIANT": "12" + "VARIANT": "20-bullseye" } }, diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index dd3422594..bab593222 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,7 +13,7 @@ jobs: strategy: matrix: os: [ubuntu-20.04] - node-version: [16.x, 18.x] + node-version: [18.x] runs-on: ${{ matrix.os }} diff --git a/.github/workflows/test-wtih-vscode-build.yml b/.github/workflows/test-wtih-vscode-build.yml index 1e547aa13..80a0efeed 100644 --- a/.github/workflows/test-wtih-vscode-build.yml +++ b/.github/workflows/test-wtih-vscode-build.yml @@ -13,7 +13,7 @@ jobs: strategy: matrix: os: [ubuntu-20.04] - node-version: [16.x] + node-version: [18.x] runs-on: ${{ matrix.os }} diff --git a/extensions/github1s/package.json b/extensions/github1s/package.json index 2b5ee7396..0d2ee1391 100644 --- a/extensions/github1s/package.json +++ b/extensions/github1s/package.json @@ -595,7 +595,7 @@ "@apollo/client": "^3.5.10", "@octokit/core": "^3.6.0", "dayjs": "^1.11.1", - "graphql": "^16.3.0", + "graphql": "^16.8.1", "history": "^5.3.0", "js-base64": "^3.7.2", "json-stable-stringify": "^1.0.1", diff --git a/extensions/github1s/src/adapters/ossinsight/data-source.ts b/extensions/github1s/src/adapters/ossinsight/data-source.ts index 2bc017933..3f03ebcd4 100644 --- a/extensions/github1s/src/adapters/ossinsight/data-source.ts +++ b/extensions/github1s/src/adapters/ossinsight/data-source.ts @@ -33,24 +33,6 @@ export class OSSInsightDataSource extends DataSource { type: FileType.Directory, name: '', children: [ - { - type: FileType.Directory, - name: '.vscode', - children: [ - { - type: FileType.File, - name: 'settings.json', - content: () => - JSON.stringify({ - 'files.exclude': { '.vscode': true }, - 'markdown.preview.doubleClickToSwitchToEditor': false, - 'workbench.editorAssociations': { - '*.md': 'vscode.markdown.preview.editor', - }, - }), - }, - ], - }, { type: FileType.Directory, name: 'collections', diff --git a/extensions/github1s/yarn.lock b/extensions/github1s/yarn.lock index 8989d5aca..118e60dcd 100644 --- a/extensions/github1s/yarn.lock +++ b/extensions/github1s/yarn.lock @@ -961,10 +961,10 @@ graphql-tag@^2.12.6: dependencies: tslib "^2.1.0" -graphql@^16.3.0: - version "16.4.0" - resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.4.0.tgz#bb10b1b4683045dedcb67000eb4ad134a36c59e6" - integrity sha512-tYDNcRvKCcfHREZYje3v33NSrSD/ZpbWWdPtBtUUuXx9NCo/2QDxYzNqCnMvfsrnbwRpEHMovVrPu/ERoLrIRg== +graphql@^16.8.1: + version "16.8.1" + resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.8.1.tgz#1930a965bef1170603702acdb68aedd3f3cf6f07" + integrity sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw== has-bigints@^1.0.1, has-bigints@^1.0.2: version "1.0.2" diff --git a/package.json b/package.json index f8c441d17..04eb19fb2 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "lib": "lib" }, "devDependencies": { - "@github1s/vscode-web": "0.12.0", + "@github1s/vscode-web": "0.13.0", "@typescript-eslint/eslint-plugin": "^5.40.1", "@typescript-eslint/parser": "^5.40.1", "chokidar": "^3.5.3", @@ -16,7 +16,7 @@ "css-loader": "^6.7.3", "eslint": "^8.25.0", "eslint-config-prettier": "^8.5.0", - "eslint-plugin-jsdoc": "^39.3.14", + "eslint-plugin-jsdoc": "^46.8.2", "eslint-plugin-prettier": "^4.2.1", "file-loader": "^6.2.0", "fs-extra": "^10.1.0", diff --git a/src/config.ts b/src/config.ts index 2ef79eb65..5ea1325f1 100644 --- a/src/config.ts +++ b/src/config.ts @@ -32,6 +32,8 @@ const createConfigurationDefaults = (disableSomeAnyCodeFeatures: boolean) => { 'workbench.colorTheme': 'Default Dark+', 'telemetry.telemetryLevel': 'off', 'workbench.startupEditor': 'readme', + 'workbench.editorAssociations': { '*.md': 'vscode.markdown.preview.editor' }, + 'markdown.preview.doubleClickToSwitchToEditor': false, } as Record; // disable some anycode features when we can use sourcegraph instead diff --git a/tests/__tests__/__image_snapshots__/index-test-ts-should-load-successfully-1-snap.png b/tests/__tests__/__image_snapshots__/index-test-ts-should-load-successfully-1-snap.png index 81f6d450e..eb2358883 100644 Binary files a/tests/__tests__/__image_snapshots__/index-test-ts-should-load-successfully-1-snap.png and b/tests/__tests__/__image_snapshots__/index-test-ts-should-load-successfully-1-snap.png differ diff --git a/tests/__tests__/__image_snapshots__/index-test-ts-should-open-file-correctly-1-snap.png b/tests/__tests__/__image_snapshots__/index-test-ts-should-open-file-correctly-1-snap.png index 1b2f170ac..4b239f696 100644 Binary files a/tests/__tests__/__image_snapshots__/index-test-ts-should-open-file-correctly-1-snap.png and b/tests/__tests__/__image_snapshots__/index-test-ts-should-open-file-correctly-1-snap.png differ diff --git a/tests/__tests__/__image_snapshots__/index-test-ts-should-show-commit-files-1-snap.png b/tests/__tests__/__image_snapshots__/index-test-ts-should-show-commit-files-1-snap.png index 7d32fbc17..44aeb1ffa 100644 Binary files a/tests/__tests__/__image_snapshots__/index-test-ts-should-show-commit-files-1-snap.png and b/tests/__tests__/__image_snapshots__/index-test-ts-should-show-commit-files-1-snap.png differ diff --git a/tests/__tests__/index.test.ts b/tests/__tests__/index.test.ts index afe70a842..522ad8721 100644 --- a/tests/__tests__/index.test.ts +++ b/tests/__tests__/index.test.ts @@ -64,6 +64,7 @@ it('should load successfully', async () => { it('should open file correctly', async () => { await page.goto(`${BASE_URL}/conwnet/github1s`); + await page.waitForTimeout(3000); await page.click('[title="~/tsconfig.json"]'); await page.click('[data-resource-name="tsconfig.json"]'); await page.waitForTimeout(3000); @@ -76,7 +77,7 @@ it('should show Commit files', async () => { await page.goto(`${BASE_URL}/conwnet/github1s/commit/ecd252fa54de41b1cb622ff5a1f8a1b715d3b621`); await page.waitForSelector('.monaco-action-bar.vertical ul.actions-container[aria-label="Active View Switcher"]'); await page.press('body', 'Control+Shift+G'); - await page.waitForTimeout(3000); + await page.waitForTimeout(6000); const container = await page.$('[id="workbench.parts.sidebar"]'); let image = await container?.screenshot(); diff --git a/vscode-web/.VERSION b/vscode-web/.VERSION index b4924f78a..c6e4b57f5 100644 --- a/vscode-web/.VERSION +++ b/vscode-web/.VERSION @@ -1 +1 @@ -1.81.1 \ No newline at end of file +1.82.2 \ No newline at end of file diff --git a/vscode-web/package.json b/vscode-web/package.json index 5182f8df8..58ac743b7 100644 --- a/vscode-web/package.json +++ b/vscode-web/package.json @@ -1,6 +1,6 @@ { "name": "@github1s/vscode-web", - "version": "0.12.0", + "version": "0.13.0", "description": "VS Code web for GitHub1s", "author": "github1s", "license": "MIT", @@ -31,14 +31,16 @@ "@vscode/iconv-lite-umd": "0.7.0", "@vscode/vscode-languagedetection": "1.0.21", "jschardet": "3.0.0", - "tas-client-umd": "0.1.6", + "tas-client-umd": "0.1.8", "vscode-oniguruma": "1.7.0", "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.7" + "xterm-addon-canvas": "0.5.0-beta.22", + "xterm-addon-image": "0.6.0-beta.14", + "xterm-addon-search": "0.13.0-beta.20", + "xterm-addon-serialize": "0.11.0-beta.20", + "xterm-addon-unicode11": "0.6.0-beta.12", + "xterm-addon-webgl": "0.16.0-beta.30" }, "devDependencies": { "@types/trusted-types": "^2.0.0", diff --git a/vscode-web/scripts/.patch b/vscode-web/scripts/.patch index a1be642d6..a0ccd5c85 100644 --- a/vscode-web/scripts/.patch +++ b/vscode-web/scripts/.patch @@ -1,13 +1,14 @@ { - "vs/code/browser/workbench/workbench.ts": "be140606a59a329584a67af7a8ec3d624ab71028ed3717a3d41bf368412beb4a", - "vs/editor/common/config/editorOptions.ts": "648903f0306c733cb2df5c0b7c241a8b1423a46ea7f2b746ef879cd43992117a", - "vs/workbench/browser/parts/activitybar/activitybarActions.ts": "a4813477aac947fc94d84f563143ef67370a9cf9c34a054741e051ec88f2eca2", - "vs/workbench/browser/parts/activitybar/activitybarPart.ts": "0a9ff35eaa2bf4ffaa22ecf362b070cf47d8a63fc9e7055a280b456150677a06", - "vs/workbench/browser/parts/titlebar/media/titlebarpart.css": "715607e6f7330e1137a4ddf7892c4846b40fe0d6c04a164d2b684eefa608846f", - "vs/workbench/browser/web.main.ts": "707d64b8fc7436e704fb1e6e06cddc6b48224c75c2617cc2175d7763906f1895", + "vs/code/browser/workbench/workbench.ts": "a954327b52b6ed462b3b7fbd2301c6ba11bf135ca7ba70c5f552214309492946", + "vs/editor/common/config/editorOptions.ts": "5dee522e65874effeb1d1a0a3637d8dedcfceb929892cf0c51c9cf29162f44da", + "vs/workbench/browser/parts/activitybar/activitybarActions.ts": "51c169706a121b1008f9173812cbc77d864255e24bfe78dfac53fec5dd6263da", + "vs/workbench/browser/parts/activitybar/activitybarPart.ts": "e202c8406f9c4c8dc998ecd8763a2753191d2a325d6fe28d6ab951966dc84d0e", + "vs/workbench/browser/parts/titlebar/media/titlebarpart.css": "e9a29c2d1a9ed11262667ce3433e03ca98e87e4a1d9cb591383333eb4988e50b", + "vs/workbench/browser/web.main.ts": "6faa17247fc0370a4262ebd38d92922eb199c75aa1c92bbd72bbdcfc3e93dac8", "vs/workbench/contrib/files/browser/editors/fileEditorInput.ts": "d52aa1f962f1a0bdfc6d186762f6fa51c9aa35bb98667aead8d8e9ad75e45215", - "vs/workbench/contrib/webview/browser/pre/index.html": "110da74034afd7843b0939ed0e2aa8a8ce8f4288d0382f9b651913659d3eceaf", + "vs/workbench/contrib/webview/browser/pre/index.html": "6dc11aefde8587732dba85116dac2718cef9287075c8d3a306663a7741e6910f", "vs/workbench/services/extensionManagement/browser/builtinExtensionsScannerService.ts": "b2c52e36c3d4122aa21b8f9a14fc9b14d45550f5a2251617e6d0e60685dde83c", "vs/workbench/services/label/common/labelService.ts": "73237cce0f9496151ea8ae5079676bbdfa15ce49de9b05621d39fcd04e9c1f0d", - "vs/workbench/services/textfile/browser/textFileService.ts": "c9e7439cf0356c327c791e1c3ea911cf13399212e84d27f49e6c2bc8a983b861" + "vs/workbench/services/textfile/browser/textFileService.ts": "514ae0a7922c9e0e63c1b50ef08936d0b900e4970c19ba219b73759d62bac1a4", + "vs/workbench/services/editor/browser/editorResolverService.ts": "43cd774a4715e98afaf85b3d44f0381d0b83dc1b9cd75fc090553f84fa0f7e80" } \ No newline at end of file diff --git a/vscode-web/src/vs/editor/common/config/editorOptions.ts b/vscode-web/src/vs/editor/common/config/editorOptions.ts index a38403f06..62547f280 100644 --- a/vscode-web/src/vs/editor/common/config/editorOptions.ts +++ b/vscode-web/src/vs/editor/common/config/editorOptions.ts @@ -730,6 +730,11 @@ export interface IEditorOptions { * Controls whether the editor receives tabs or defers them to the workbench for navigation. */ tabFocusMode?: boolean; + + /** + * Controls whether the accessibility hint should be provided to screen reader users when an inline completion is shown. + */ + inlineCompletionsAccessibilityVerbose?: boolean; } /** @@ -755,6 +760,16 @@ export interface IDiffEditorBaseOptions { * Defaults to true. */ renderSideBySide?: boolean; + /** + * When `renderSideBySide` is enabled, `useInlineViewWhenSpaceIsLimited` is set, + * and the diff editor has a width less than `renderSideBySideInlineBreakpoint`, the inline view is used. + */ + renderSideBySideInlineBreakpoint?: number | undefined; + /** + * When `renderSideBySide` is enabled, `useInlineViewWhenSpaceIsLimited` is set, + * and the diff editor has a width less than `renderSideBySideInlineBreakpoint`, the inline view is used. + */ + useInlineViewWhenSpaceIsLimited?: boolean; /** * Timeout in milliseconds after which diff computation is cancelled. * Defaults to 5000. @@ -810,10 +825,6 @@ export interface IDiffEditorBaseOptions { accessibilityVerbose?: boolean; experimental?: { - /** - * Defaults to false. - */ - collapseUnchangedRegions?: boolean; /** * Defaults to false. */ @@ -832,6 +843,13 @@ export interface IDiffEditorBaseOptions { * If the diff editor should only show the difference review mode. */ onlyShowAccessibleDiffViewer?: boolean; + + hideUnchangedRegions?: { + enabled?: boolean; + revealLineCount?: number; + minimumLineCount?: number; + contextLineCount?: number; + }; } /** @@ -2742,6 +2760,10 @@ export interface IEditorStickyScrollOptions { * Model to choose for sticky scroll by default */ defaultModel?: 'outlineModel' | 'foldingProviderModel' | 'indentationModel'; + /** + * Define whether to scroll sticky scroll with editor horizontal scrollbae + */ + scrollWithEditor?: boolean; } /** @@ -2752,7 +2774,7 @@ export type EditorStickyScrollOptions = Readonly { constructor() { - const defaults: EditorStickyScrollOptions = { enabled: false, maxLineCount: 5, defaultModel: 'outlineModel' }; + const defaults: EditorStickyScrollOptions = { enabled: false, maxLineCount: 5, defaultModel: 'outlineModel', scrollWithEditor: true }; super( EditorOption.stickyScroll, 'stickyScroll', defaults, { @@ -2774,6 +2796,11 @@ class EditorStickyScroll extends BaseEditorOption(input.defaultModel, this.defaultValue.defaultModel, ['outlineModel', 'foldingProviderModel', 'indentationModel']), + scrollWithEditor: boolean(input.scrollWithEditor, this.defaultValue.scrollWithEditor) }; } } @@ -5123,7 +5151,8 @@ export const enum EditorOption { layoutInfo, wrappingInfo, defaultColorDecorators, - colorDecoratorsActivatedOn + colorDecoratorsActivatedOn, + inlineCompletionsAccessibilityVerbose } export const EditorOptions = { @@ -5716,6 +5745,8 @@ export const EditorOptions = { )), suggest: register(new EditorSuggest()), inlineSuggest: register(new InlineEditorSuggest()), + inlineCompletionsAccessibilityVerbose: register(new EditorBooleanOption(EditorOption.inlineCompletionsAccessibilityVerbose, 'inlineCompletionsAccessibilityVerbose', false, + { description: nls.localize('inlineCompletionsAccessibilityVerbose', "Controls whether the accessibility hint should be provided to screen reader users when an inline completion is shown.") })), suggestFontSize: register(new EditorIntOption( EditorOption.suggestFontSize, 'suggestFontSize', 0, 0, 1000, 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 e45fc8e2c..eda1eb451 100644 --- a/vscode-web/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts +++ b/vscode-web/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts @@ -23,7 +23,7 @@ import { ACTIVITY_BAR_ACTIVE_FOCUS_BORDER, ACTIVITY_BAR_ACTIVE_BACKGROUND, ACTIV import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService'; 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 { AuthenticationSessionInfo, getCurrentAuthenticationSessionInfo } from 'vs/workbench/services/authentication/browser/authenticationService'; 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'; @@ -41,6 +41,9 @@ import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/c import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { ILogService } from 'vs/platform/log/common/log'; import { ISecretStorageService } from 'vs/platform/secrets/common/secrets'; +import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; +import { runWhenIdle } from 'vs/base/common/async'; +import { Lazy } from 'vs/base/common/lazy'; export class ViewContainerActivityAction extends ActivityAction { @@ -273,7 +276,7 @@ export class AccountsActivityActionViewItem extends MenuActivityActionViewItem { private readonly problematicProviders: Set = new Set(); private initialized = false; - private sessionFromEmbedder = getCurrentAuthenticationSessionInfo(this.credentialsService, this.secretStorageService, this.productService); + private sessionFromEmbedder = new Lazy>(() => getCurrentAuthenticationSessionInfo(this.credentialsService, this.secretStorageService, this.productService)); constructor( action: ActivityAction, @@ -281,6 +284,7 @@ export class AccountsActivityActionViewItem extends MenuActivityActionViewItem { colors: (theme: IColorTheme) => ICompositeBarColors, activityHoverOptions: IActivityHoverOptions, @IThemeService themeService: IThemeService, + @ILifecycleService private readonly lifecycleService: ILifecycleService, @IHoverService hoverService: IHoverService, @IContextMenuService contextMenuService: IContextMenuService, @IMenuService menuService: IMenuService, @@ -327,6 +331,16 @@ export class AccountsActivityActionViewItem extends MenuActivityActionViewItem { // 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 { + // Resolving the menu doesn't need to happen immediately, so we can wait until after the workbench has been restored + // and only run this when the system is idle. + await this.lifecycleService.when(LifecyclePhase.Restored); + const disposable = this._register(runWhenIdle(async () => { + await this.doInitialize(); + disposable.dispose(); + })); + } + + private async doInitialize(): Promise { const providerIds = this.authenticationService.getProviderIds(); const results = await Promise.allSettled(providerIds.map(providerId => this.addAccountsFromProvider(providerId))); @@ -443,7 +457,7 @@ export class AccountsActivityActionViewItem extends MenuActivityActionViewItem { this.groupedAccounts.set(providerId, accounts); } - const sessionFromEmbedder = await this.sessionFromEmbedder; + const sessionFromEmbedder = await this.sessionFromEmbedder.value; // 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) { 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 b41bca013..1d503790f 100644 --- a/vscode-web/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts +++ b/vscode-web/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts @@ -21,7 +21,7 @@ import { ACTIVITY_BAR_BACKGROUND, ACTIVITY_BAR_BORDER, ACTIVITY_BAR_FOREGROUND, import { contrastBorder } from 'vs/platform/theme/common/colorRegistry'; import { CompositeBar, ICompositeBarItem, CompositeDragAndDrop } from 'vs/workbench/browser/parts/compositeBar'; import { Dimension, createCSSRule, asCSSUrl, addDisposableListener, EventType, isAncestor } from 'vs/base/browser/dom'; -import { IStorageService, StorageScope, IStorageValueChangeEvent, StorageTarget } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget, IProfileStorageValueChangeEvent } from 'vs/platform/storage/common/storage'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { URI, UriComponents } from 'vs/base/common/uri'; import { ToggleCompositePinnedAction, ICompositeBarColors, ActivityAction, ICompositeActivity, IActivityHoverOptions, ToggleCompositeBadgeAction } from 'vs/workbench/browser/parts/compositeBarActions'; @@ -253,7 +253,8 @@ export class ActivitybarPart extends Part implements IPaneCompositeSelectorPart disposables.clear(); this.onDidRegisterExtensions(); this.compositeBar.onDidChange(() => this.saveCachedViewContainers(), this, disposables); - this.storageService.onDidChangeValue(e => this.onDidStorageValueChange(e), this, disposables); + this.storageService.onDidChangeValue(StorageScope.PROFILE, ActivitybarPart.PINNED_VIEW_CONTAINERS, disposables)(e => this.onDidPinnedViewContainersStorageValueChange(e), this, disposables); + this.storageService.onDidChangeValue(StorageScope.PROFILE, AccountsActivityActionViewItem.ACCOUNTS_VISIBILITY_PREFERENCE_KEY, disposables)(() => this.toggleAccountsActivity(), this, disposables); })); // Register for configuration changes @@ -866,9 +867,8 @@ export class ActivitybarPart extends Part implements IPaneCompositeSelectorPart return this.viewDescriptorService.getViewContainersByLocation(this.location); } - private onDidStorageValueChange(e: IStorageValueChangeEvent): void { - if (e.key === ActivitybarPart.PINNED_VIEW_CONTAINERS && e.scope === StorageScope.PROFILE - && this.pinnedViewContainersValue !== this.getStoredPinnedViewContainersValue() /* This checks if current window changed the value or not */) { + private onDidPinnedViewContainersStorageValueChange(e: IProfileStorageValueChangeEvent): void { + if (this.pinnedViewContainersValue !== this.getStoredPinnedViewContainersValue() /* This checks if current window changed the value or not */) { this._pinnedViewContainersValue = undefined; this._cachedViewContainers = undefined; @@ -900,10 +900,6 @@ export class ActivitybarPart extends Part implements IPaneCompositeSelectorPart this.compositeBar.setCompositeBarItems(newCompositeItems); } - - if (e.key === AccountsActivityActionViewItem.ACCOUNTS_VISIBILITY_PREFERENCE_KEY && e.scope === StorageScope.PROFILE) { - this.toggleAccountsActivity(); - } } private saveCachedViewContainers(): void { 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 1119b5a33..8f0bb6381 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,11 +127,6 @@ /* Window Title Menu */ .monaco-workbench .part.titlebar > .titlebar-container > .titlebar-center > .window-title > .command-center { z-index: 2500; -} - -/* MacOS Desktop supports click event despite `drag` and therefore we don't need to clear it */ -.monaco-workbench:not(.mac) .part.titlebar > .titlebar-container > .titlebar-center > .window-title > .command-center, -.monaco-workbench.mac.web .part.titlebar > .titlebar-container > .titlebar-center > .window-title > .command-center { -webkit-app-region: no-drag; } diff --git a/vscode-web/src/vs/workbench/browser/web.main.ts b/vscode-web/src/vs/workbench/browser/web.main.ts index 98d91f065..7d88d86bb 100644 --- a/vscode-web/src/vs/workbench/browser/web.main.ts +++ b/vscode-web/src/vs/workbench/browser/web.main.ts @@ -73,7 +73,7 @@ import { DelayedLogChannel } from 'vs/workbench/services/output/common/delayedLo import { dirname, joinPath } from 'vs/base/common/resources'; import { IUserDataProfile, IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { NullPolicyService } from 'vs/platform/policy/common/policy'; -import { IRemoteExplorerService, TunnelSource } from 'vs/workbench/services/remote/common/remoteExplorerService'; +import { IRemoteExplorerService } from 'vs/workbench/services/remote/common/remoteExplorerService'; import { DisposableTunnel, TunnelProtocol } from 'vs/platform/tunnel/common/tunnel'; import { ILabelService } from 'vs/platform/label/common/label'; import { UserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfileService'; @@ -96,6 +96,7 @@ import { BrowserSecretStorageService } from 'vs/workbench/services/secrets/brows import { EncryptionService } from 'vs/workbench/services/encryption/browser/encryptionService'; import { IEncryptionService } from 'vs/platform/encryption/common/encryptionService'; import { ISecretStorageService } from 'vs/platform/secrets/common/secrets'; +import { TunnelSource } from 'vs/workbench/services/remote/common/tunnelModel'; export class BrowserMain extends Disposable { @@ -210,6 +211,10 @@ export class BrowserMain extends Disposable { protocol: tunnelOptions.protocol === TunnelProtocol.Https ? tunnelOptions.protocol : TunnelProtocol.Http })); + if (typeof tunnel === 'string') { + throw new Error(tunnel); + } + return new class extends DisposableTunnel implements ITunnel { declare localAddress: string; }({ 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 7f687d31c..7fd5ba482 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 @@ -4,8 +4,10 @@ + + ; + +export class EditorResolverService extends Disposable implements IEditorResolverService { + readonly _serviceBrand: undefined; + + // Events + private readonly _onDidChangeEditorRegistrations = this._register(new PauseableEmitter()); + readonly onDidChangeEditorRegistrations = this._onDidChangeEditorRegistrations.event; + + // Constants + private static readonly configureDefaultID = 'promptOpenWith.configureDefault'; + private static readonly cacheStorageID = 'editorOverrideService.cache'; + private static readonly conflictingDefaultsStorageID = 'editorOverrideService.conflictingDefaults'; + + // Data Stores + private _editors: Map> = new Map>(); + private _flattenedEditors: Map = new Map(); + private _shouldReFlattenEditors: boolean = true; + private cache: Set | undefined; + + constructor( + @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService, + @IInstantiationService private readonly instantiationService: IInstantiationService, + @IConfigurationService private readonly configurationService: IConfigurationService, + @IQuickInputService private readonly quickInputService: IQuickInputService, + @INotificationService private readonly notificationService: INotificationService, + @ITelemetryService private readonly telemetryService: ITelemetryService, + @IStorageService private readonly storageService: IStorageService, + @IExtensionService private readonly extensionService: IExtensionService, + @ILogService private readonly logService: ILogService + ) { + super(); + // Read in the cache on statup + this.cache = new Set(JSON.parse(this.storageService.get(EditorResolverService.cacheStorageID, StorageScope.PROFILE, JSON.stringify([])))); + this.storageService.remove(EditorResolverService.cacheStorageID, StorageScope.PROFILE); + + this._register(this.storageService.onWillSaveState(() => { + // We want to store the glob patterns we would activate on, this allows us to know if we need to await the ext host on startup for opening a resource + this.cacheEditors(); + })); + + // When extensions have registered we no longer need the cache + this.extensionService.onDidRegisterExtensions(() => { + this.cache = undefined; + }); + } + + private resolveUntypedInputAndGroup(editor: IUntypedEditorInput, preferredGroup: PreferredGroup | undefined): [IUntypedEditorInput, IEditorGroup, EditorActivation | undefined] | undefined { + const untypedEditor = editor; + + // Use the untyped editor to find a group + const [group, activation] = this.instantiationService.invokeFunction(findGroup, untypedEditor, preferredGroup); + + return [untypedEditor, group, activation]; + } + + async resolveEditor(editor: IUntypedEditorInput, preferredGroup: PreferredGroup | undefined): Promise { + // Update the flattened editors + this._flattenedEditors = this._flattenEditorsMap(); + + // Special case: side by side editors requires us to + // independently resolve both sides and then build + // a side by side editor with the result + if (isResourceSideBySideEditorInput(editor)) { + return this.doResolveSideBySideEditor(editor, preferredGroup); + } + + const resolvedUntypedAndGroup = this.resolveUntypedInputAndGroup(editor, preferredGroup); + if (!resolvedUntypedAndGroup) { + return ResolvedStatus.NONE; + } + // Get the resolved untyped editor, group, and activation + const [untypedEditor, group, activation] = resolvedUntypedAndGroup; + if (activation) { + untypedEditor.options = { ...untypedEditor.options, activation }; + } + + let resource = EditorResourceAccessor.getCanonicalUri(untypedEditor, { supportSideBySide: SideBySideEditor.PRIMARY }); + + // If it was resolved before we await for the extensions to activate and then proceed with resolution or else the backing extensions won't be registered + if (this.cache && resource && this.resourceMatchesCache(resource)) { + await this.extensionService.whenInstalledExtensionsRegistered(); + } + + // Undefined resource -> untilted. Other malformed URI's are unresolvable + if (resource === undefined) { + resource = URI.from({ scheme: Schemas.untitled }); + } else if (resource.scheme === undefined || resource === null) { + return ResolvedStatus.NONE; + } + + if (untypedEditor.options?.override === EditorResolution.PICK) { + const picked = await this.doPickEditor(untypedEditor); + // If the picker was cancelled we will stop resolving the editor + if (!picked) { + return ResolvedStatus.ABORT; + } + // Populate the options with the new ones + untypedEditor.options = picked; + } + + // Resolved the editor ID as much as possible, now find a given editor (cast here is ok because we resolve down to a string above) + let { editor: selectedEditor, conflictingDefault } = this.getEditor(resource, untypedEditor.options?.override as (string | EditorResolution.EXCLUSIVE_ONLY | undefined)); + // If no editor was found and this was a typed editor or an editor with an explicit override we could not resolve it + if (!selectedEditor && (untypedEditor.options?.override || isEditorInputWithOptions(editor))) { + return ResolvedStatus.NONE; + } else if (!selectedEditor) { + // Simple untyped editors that we could not resolve will be resolved to the default editor + const resolvedEditor = this.getEditor(resource, DEFAULT_EDITOR_ASSOCIATION.id); + selectedEditor = resolvedEditor?.editor; + conflictingDefault = resolvedEditor?.conflictingDefault; + if (!selectedEditor) { + return ResolvedStatus.NONE; + } + } + + // In the special case of diff editors we do some more work to determine the correct editor for both sides + if (isResourceDiffEditorInput(untypedEditor) && untypedEditor.options?.override === undefined) { + let resource2 = EditorResourceAccessor.getCanonicalUri(untypedEditor, { supportSideBySide: SideBySideEditor.SECONDARY }); + if (!resource2) { + resource2 = URI.from({ scheme: Schemas.untitled }); + } + const { editor: selectedEditor2 } = this.getEditor(resource2, undefined); + if (!selectedEditor2 || selectedEditor.editorInfo.id !== selectedEditor2.editorInfo.id) { + const { editor: selectedDiff, conflictingDefault: conflictingDefaultDiff } = this.getEditor(resource, DEFAULT_EDITOR_ASSOCIATION.id); + selectedEditor = selectedDiff; + conflictingDefault = conflictingDefaultDiff; + } + if (!selectedEditor) { + return ResolvedStatus.NONE; + } + } + + // If no override we take the selected editor id so that matches works with the isActive check + untypedEditor.options = { override: selectedEditor.editorInfo.id, ...untypedEditor.options }; + + // Check if diff can be created based on prescene of factory function + if (selectedEditor.editorFactoryObject.createDiffEditorInput === undefined && isResourceDiffEditorInput(untypedEditor)) { + return ResolvedStatus.NONE; + } + + const input = await this.doResolveEditor(untypedEditor, group, selectedEditor); + if (conflictingDefault && input) { + // Show the conflicting default dialog + await this.doHandleConflictingDefaults(resource, selectedEditor.editorInfo.label, untypedEditor, input.editor, group); + } + + if (input) { + this.sendEditorResolutionTelemetry(input.editor); + if (input.editor.editorId !== selectedEditor.editorInfo.id) { + this.logService.warn(`Editor ID Mismatch: ${input.editor.editorId} !== ${selectedEditor.editorInfo.id}. This will cause bugs. Please ensure editorInput.editorId matches the registered id`); + } + return { ...input, group }; + } + return ResolvedStatus.ABORT; + } + + private async doResolveSideBySideEditor(editor: IResourceSideBySideEditorInput, preferredGroup: PreferredGroup | undefined): Promise { + const primaryResolvedEditor = await this.resolveEditor(editor.primary, preferredGroup); + if (!isEditorInputWithOptionsAndGroup(primaryResolvedEditor)) { + return ResolvedStatus.NONE; + } + const secondaryResolvedEditor = await this.resolveEditor(editor.secondary, primaryResolvedEditor.group ?? preferredGroup); + if (!isEditorInputWithOptionsAndGroup(secondaryResolvedEditor)) { + return ResolvedStatus.NONE; + } + return { + group: primaryResolvedEditor.group ?? secondaryResolvedEditor.group, + editor: this.instantiationService.createInstance(SideBySideEditorInput, editor.label, editor.description, secondaryResolvedEditor.editor, primaryResolvedEditor.editor), + options: editor.options + }; + } + + bufferChangeEvents(callback: Function): void { + this._onDidChangeEditorRegistrations.pause(); + try { + callback(); + } finally { + this._onDidChangeEditorRegistrations.resume(); + } + } + + registerEditor( + globPattern: string | glob.IRelativePattern, + editorInfo: RegisteredEditorInfo, + options: RegisteredEditorOptions, + editorFactoryObject: EditorInputFactoryObject + ): IDisposable { + let registeredEditor = this._editors.get(globPattern); + if (registeredEditor === undefined) { + registeredEditor = new Map(); + this._editors.set(globPattern, registeredEditor); + } + + let editorsWithId = registeredEditor.get(editorInfo.id); + if (editorsWithId === undefined) { + editorsWithId = []; + } + const remove = insert(editorsWithId, { + globPattern, + editorInfo, + options, + editorFactoryObject + }); + registeredEditor.set(editorInfo.id, editorsWithId); + this._shouldReFlattenEditors = true; + this._onDidChangeEditorRegistrations.fire(); + return toDisposable(() => { + remove(); + if (editorsWithId && editorsWithId.length === 0) { + registeredEditor?.delete(editorInfo.id); + } + this._shouldReFlattenEditors = true; + this._onDidChangeEditorRegistrations.fire(); + }); + } + + getAssociationsForResource(resource: URI): EditorAssociations { + const associations = this.getAllUserAssociations(); + let matchingAssociations = associations.filter(association => association.filenamePattern && globMatchesResource(association.filenamePattern, resource)); + // Sort matching associations based on glob length as a longer glob will be more specific + matchingAssociations = matchingAssociations.sort((a, b) => (b.filenamePattern?.length ?? 0) - (a.filenamePattern?.length ?? 0)); + const allEditors: RegisteredEditors = this._registeredEditors; + // Ensure that the settings are valid editors + return matchingAssociations.filter(association => allEditors.find(c => c.editorInfo.id === association.viewType)); + } + + private getAllUserAssociations(): EditorAssociations { + const inspectedEditorAssociations = this.configurationService.inspect<{ [fileNamePattern: string]: string }>(editorsAssociationsSettingId) || {}; + /* below codes are changed by github1s */ + const defaultAssociations = inspectedEditorAssociations.defaultValue ?? {}; + const workspaceAssociations = inspectedEditorAssociations.workspaceValue ?? {}; + const userAssociations = inspectedEditorAssociations.userValue ?? {}; + const rawAssociations: { [fileNamePattern: string]: string } = { ...workspaceAssociations }; + // We want to apply the default associations and user associations on top of the workspace associations but ignore duplicate keys. + /* above codes are changed by github1s */ + for (const [key, value] of Object.entries({ ...defaultAssociations, ...userAssociations })) { + if (rawAssociations[key] === undefined) { + rawAssociations[key] = value; + } + } + const associations = []; + for (const [key, value] of Object.entries(rawAssociations)) { + const association: EditorAssociation = { + filenamePattern: key, + viewType: value + }; + associations.push(association); + } + return associations; + } + + /** + * Given the nested nature of the editors map, we merge factories of the same glob and id to make it flat + * and easier to work with + */ + private _flattenEditorsMap() { + // If we shouldn't be re-flattening (due to lack of update) then return early + if (!this._shouldReFlattenEditors) { + return this._flattenedEditors; + } + this._shouldReFlattenEditors = false; + const editors = new Map(); + for (const [glob, value] of this._editors) { + const registeredEditors: RegisteredEditors = []; + for (const editors of value.values()) { + let registeredEditor: RegisteredEditor | undefined = undefined; + // Merge all editors with the same id and glob pattern together + for (const editor of editors) { + if (!registeredEditor) { + registeredEditor = { + editorInfo: editor.editorInfo, + globPattern: editor.globPattern, + options: {}, + editorFactoryObject: {} + }; + } + // Merge options and factories + registeredEditor.options = { ...registeredEditor.options, ...editor.options }; + registeredEditor.editorFactoryObject = { ...registeredEditor.editorFactoryObject, ...editor.editorFactoryObject }; + } + if (registeredEditor) { + registeredEditors.push(registeredEditor); + } + } + editors.set(glob, registeredEditors); + } + return editors; + } + + /** + * Returns all editors as an array. Possible to contain duplicates + */ + private get _registeredEditors(): RegisteredEditors { + return flatten(Array.from(this._flattenedEditors.values())); + } + + updateUserAssociations(globPattern: string, editorID: string): void { + const newAssociation: EditorAssociation = { viewType: editorID, filenamePattern: globPattern }; + const currentAssociations = this.getAllUserAssociations(); + const newSettingObject = Object.create(null); + // Form the new setting object including the newest associations + for (const association of [...currentAssociations, newAssociation]) { + if (association.filenamePattern) { + newSettingObject[association.filenamePattern] = association.viewType; + } + } + this.configurationService.updateValue(editorsAssociationsSettingId, newSettingObject); + } + + private findMatchingEditors(resource: URI): RegisteredEditor[] { + // The user setting should be respected even if the editor doesn't specify that resource in package.json + const userSettings = this.getAssociationsForResource(resource); + const matchingEditors: RegisteredEditor[] = []; + // Then all glob patterns + for (const [key, editors] of this._flattenedEditors) { + for (const editor of editors) { + const foundInSettings = userSettings.find(setting => setting.viewType === editor.editorInfo.id); + if ((foundInSettings && editor.editorInfo.priority !== RegisteredEditorPriority.exclusive) || globMatchesResource(key, resource)) { + matchingEditors.push(editor); + } + } + } + // Return the editors sorted by their priority + return matchingEditors.sort((a, b) => { + // Very crude if priorities match longer glob wins as longer globs are normally more specific + if (priorityToRank(b.editorInfo.priority) === priorityToRank(a.editorInfo.priority) && typeof b.globPattern === 'string' && typeof a.globPattern === 'string') { + return b.globPattern.length - a.globPattern.length; + } + return priorityToRank(b.editorInfo.priority) - priorityToRank(a.editorInfo.priority); + }); + } + + public getEditors(resource?: URI): RegisteredEditorInfo[] { + this._flattenedEditors = this._flattenEditorsMap(); + + // By resource + if (URI.isUri(resource)) { + const editors = this.findMatchingEditors(resource); + if (editors.find(e => e.editorInfo.priority === RegisteredEditorPriority.exclusive)) { + return []; + } + return editors.map(editor => editor.editorInfo); + } + + // All + return distinct(this._registeredEditors.map(editor => editor.editorInfo), editor => editor.id); + } + + /** + * Given a resource and an editorId selects the best possible editor + * @returns The editor and whether there was another default which conflicted with it + */ + private getEditor(resource: URI, editorId: string | EditorResolution.EXCLUSIVE_ONLY | undefined): { editor: RegisteredEditor | undefined; conflictingDefault: boolean } { + + const findMatchingEditor = (editors: RegisteredEditors, viewType: string) => { + return editors.find((editor) => { + if (editor.options && editor.options.canSupportResource !== undefined) { + return editor.editorInfo.id === viewType && editor.options.canSupportResource(resource); + } + return editor.editorInfo.id === viewType; + }); + }; + + if (editorId && editorId !== EditorResolution.EXCLUSIVE_ONLY) { + // Specific id passed in doesn't have to match the resource, it can be anything + const registeredEditors = this._registeredEditors; + return { + editor: findMatchingEditor(registeredEditors, editorId), + conflictingDefault: false + }; + } + + const editors = this.findMatchingEditors(resource); + + const associationsFromSetting = this.getAssociationsForResource(resource); + // We only want minPriority+ if no user defined setting is found, else we won't resolve an editor + const minPriority = editorId === EditorResolution.EXCLUSIVE_ONLY ? RegisteredEditorPriority.exclusive : RegisteredEditorPriority.builtin; + let possibleEditors = editors.filter(editor => priorityToRank(editor.editorInfo.priority) >= priorityToRank(minPriority) && editor.editorInfo.id !== DEFAULT_EDITOR_ASSOCIATION.id); + if (possibleEditors.length === 0) { + return { + editor: associationsFromSetting[0] && minPriority !== RegisteredEditorPriority.exclusive ? findMatchingEditor(editors, associationsFromSetting[0].viewType) : undefined, + conflictingDefault: false + }; + } + // If the editor is exclusive we use that, else use the user setting, else use the built-in+ editor + const selectedViewType = possibleEditors[0].editorInfo.priority === RegisteredEditorPriority.exclusive ? + possibleEditors[0].editorInfo.id : + associationsFromSetting[0]?.viewType || possibleEditors[0].editorInfo.id; + + let conflictingDefault = false; + + // Filter out exclusive before we check for conflicts as exclusive editors cannot be manually chosen + possibleEditors = possibleEditors.filter(editor => editor.editorInfo.priority !== RegisteredEditorPriority.exclusive); + if (associationsFromSetting.length === 0 && possibleEditors.length > 1) { + conflictingDefault = true; + } + + return { + editor: findMatchingEditor(editors, selectedViewType), + conflictingDefault + }; + } + + private async doResolveEditor(editor: IUntypedEditorInput, group: IEditorGroup, selectedEditor: RegisteredEditor): Promise { + let options = editor.options; + const resource = EditorResourceAccessor.getCanonicalUri(editor, { supportSideBySide: SideBySideEditor.PRIMARY }); + // If no activation option is provided, populate it. + if (options && typeof options.activation === 'undefined') { + options = { ...options, activation: options.preserveFocus ? EditorActivation.RESTORE : undefined }; + } + + // If it's a merge editor we trigger the create merge editor input + if (isResourceMergeEditorInput(editor)) { + if (!selectedEditor.editorFactoryObject.createMergeEditorInput) { + return; + } + const inputWithOptions = await selectedEditor.editorFactoryObject.createMergeEditorInput(editor, group); + return { editor: inputWithOptions.editor, options: inputWithOptions.options ?? options }; + } + + // If it's a diff editor we trigger the create diff editor input + if (isResourceDiffEditorInput(editor)) { + if (!selectedEditor.editorFactoryObject.createDiffEditorInput) { + return; + } + const inputWithOptions = await selectedEditor.editorFactoryObject.createDiffEditorInput(editor, group); + return { editor: inputWithOptions.editor, options: inputWithOptions.options ?? options }; + } + + if (isResourceSideBySideEditorInput(editor)) { + throw new Error(`Untyped side by side editor input not supported here.`); + } + + if (isUntitledResourceEditorInput(editor)) { + if (!selectedEditor.editorFactoryObject.createUntitledEditorInput) { + return; + } + const inputWithOptions = await selectedEditor.editorFactoryObject.createUntitledEditorInput(editor, group); + return { editor: inputWithOptions.editor, options: inputWithOptions.options ?? options }; + } + + // Should no longer have an undefined resource so lets throw an error if that's somehow the case + if (resource === undefined) { + throw new Error(`Undefined resource on non untitled editor input.`); + } + + // If the editor states it can only be opened once per resource we must close all existing ones except one and move the new one into the group + const singleEditorPerResource = typeof selectedEditor.options?.singlePerResource === 'function' ? selectedEditor.options.singlePerResource() : selectedEditor.options?.singlePerResource; + if (singleEditorPerResource) { + const foundInput = await this.moveExistingEditorForResource(resource, selectedEditor.editorInfo.id, group); + if (foundInput) { + return { editor: foundInput, options }; + } + } + + // If no factory is above, return flow back to caller letting them know we could not resolve it + if (!selectedEditor.editorFactoryObject.createEditorInput) { + return; + } + + // Respect options passed back + const inputWithOptions = await selectedEditor.editorFactoryObject.createEditorInput(editor, group); + options = inputWithOptions.options ?? options; + const input = inputWithOptions.editor; + + return { editor: input, options }; + } + + /** + * Moves an editor with the resource and viewtype to target group if one exists + * Additionally will close any other editors that are open for that resource and viewtype besides the first one found + * @param resource The resource of the editor + * @param viewType the viewtype of the editor + * @param targetGroup The group to move it to + * @returns An editor input if one exists, else undefined + */ + private async moveExistingEditorForResource( + resource: URI, + viewType: string, + targetGroup: IEditorGroup, + ): Promise { + const editorInfoForResource = this.findExistingEditorsForResource(resource, viewType); + if (!editorInfoForResource.length) { + return; + } + + const editorToUse = editorInfoForResource[0]; + + // We should only have one editor but if there are multiple we close the others + for (const { editor, group } of editorInfoForResource) { + if (editor !== editorToUse.editor) { + const closed = await group.closeEditor(editor); + if (!closed) { + return; + } + } + } + + // Move the editor already opened to the target group + if (targetGroup.id !== editorToUse.group.id) { + editorToUse.group.moveEditor(editorToUse.editor, targetGroup); + return editorToUse.editor; + } + return; + } + + /** + * Given a resource and an editorId, returns all editors open for that resource and editorId. + * @param resource The resource specified + * @param editorId The editorID + * @returns A list of editors + */ + private findExistingEditorsForResource( + resource: URI, + editorId: string, + ): Array<{ editor: EditorInput; group: IEditorGroup }> { + const out: Array<{ editor: EditorInput; group: IEditorGroup }> = []; + const orderedGroups = distinct([ + ...this.editorGroupService.groups, + ]); + + for (const group of orderedGroups) { + for (const editor of group.editors) { + if (isEqual(editor.resource, resource) && editor.editorId === editorId) { + out.push({ editor, group }); + } + } + } + return out; + } + + private async doHandleConflictingDefaults(resource: URI, editorName: string, untypedInput: IUntypedEditorInput, currentEditor: EditorInput, group: IEditorGroup) { + type StoredChoice = { + [key: string]: string[]; + }; + const editors = this.findMatchingEditors(resource); + const storedChoices: StoredChoice = JSON.parse(this.storageService.get(EditorResolverService.conflictingDefaultsStorageID, StorageScope.PROFILE, '{}')); + const globForResource = `*${extname(resource)}`; + // Writes to the storage service that a choice has been made for the currently installed editors + const writeCurrentEditorsToStorage = () => { + storedChoices[globForResource] = []; + editors.forEach(editor => storedChoices[globForResource].push(editor.editorInfo.id)); + this.storageService.store(EditorResolverService.conflictingDefaultsStorageID, JSON.stringify(storedChoices), StorageScope.PROFILE, StorageTarget.MACHINE); + }; + + // If the user has already made a choice for this editor we don't want to ask them again + if (storedChoices[globForResource] && storedChoices[globForResource].find(editorID => editorID === currentEditor.editorId)) { + return; + } + + const handle = this.notificationService.prompt(Severity.Warning, + localize('editorResolver.conflictingDefaults', 'There are multiple default editors available for the resource.'), + [{ + label: localize('editorResolver.configureDefault', 'Configure Default'), + run: async () => { + // Show the picker and tell it to update the setting to whatever the user selected + const picked = await this.doPickEditor(untypedInput, true); + if (!picked) { + return; + } + untypedInput.options = picked; + const replacementEditor = await this.resolveEditor(untypedInput, group); + if (replacementEditor === ResolvedStatus.ABORT || replacementEditor === ResolvedStatus.NONE) { + return; + } + // Replace the current editor with the picked one + group.replaceEditors([ + { + editor: currentEditor, + replacement: replacementEditor.editor, + options: replacementEditor.options ?? picked, + } + ]); + } + }, + { + label: localize('editorResolver.keepDefault', 'Keep {0}', editorName), + run: writeCurrentEditorsToStorage + } + ]); + // If the user pressed X we assume they want to keep the current editor as default + const onCloseListener = handle.onDidClose(() => { + writeCurrentEditorsToStorage(); + onCloseListener.dispose(); + }); + } + + private mapEditorsToQuickPickEntry(resource: URI, showDefaultPicker?: boolean) { + const currentEditor = firstOrDefault(this.editorGroupService.activeGroup.findEditors(resource)); + // If untitled, we want all registered editors + let registeredEditors = resource.scheme === Schemas.untitled ? this._registeredEditors.filter(e => e.editorInfo.priority !== RegisteredEditorPriority.exclusive) : this.findMatchingEditors(resource); + // We don't want duplicate Id entries + registeredEditors = distinct(registeredEditors, c => c.editorInfo.id); + const defaultSetting = this.getAssociationsForResource(resource)[0]?.viewType; + // Not the most efficient way to do this, but we want to ensure the text editor is at the top of the quickpick + registeredEditors = registeredEditors.sort((a, b) => { + if (a.editorInfo.id === DEFAULT_EDITOR_ASSOCIATION.id) { + return -1; + } else if (b.editorInfo.id === DEFAULT_EDITOR_ASSOCIATION.id) { + return 1; + } else { + return priorityToRank(b.editorInfo.priority) - priorityToRank(a.editorInfo.priority); + } + }); + const quickPickEntries: Array = []; + const currentlyActiveLabel = localize('promptOpenWith.currentlyActive', "Active"); + const currentDefaultLabel = localize('promptOpenWith.currentDefault', "Default"); + const currentDefaultAndActiveLabel = localize('promptOpenWith.currentDefaultAndActive', "Active and Default"); + // Default order = setting -> highest priority -> text + let defaultViewType = defaultSetting; + if (!defaultViewType && registeredEditors.length > 2 && registeredEditors[1]?.editorInfo.priority !== RegisteredEditorPriority.option) { + defaultViewType = registeredEditors[1]?.editorInfo.id; + } + if (!defaultViewType) { + defaultViewType = DEFAULT_EDITOR_ASSOCIATION.id; + } + // Map the editors to quickpick entries + registeredEditors.forEach(editor => { + const currentViewType = currentEditor?.editorId ?? DEFAULT_EDITOR_ASSOCIATION.id; + const isActive = currentEditor ? editor.editorInfo.id === currentViewType : false; + const isDefault = editor.editorInfo.id === defaultViewType; + const quickPickEntry: IQuickPickItem = { + id: editor.editorInfo.id, + label: editor.editorInfo.label, + description: isActive && isDefault ? currentDefaultAndActiveLabel : isActive ? currentlyActiveLabel : isDefault ? currentDefaultLabel : undefined, + detail: editor.editorInfo.detail ?? editor.editorInfo.priority, + }; + quickPickEntries.push(quickPickEntry); + }); + if (!showDefaultPicker && extname(resource) !== '') { + const separator: IQuickPickSeparator = { type: 'separator' }; + quickPickEntries.push(separator); + const configureDefaultEntry = { + id: EditorResolverService.configureDefaultID, + label: localize('promptOpenWith.configureDefault', "Configure default editor for '{0}'...", `*${extname(resource)}`), + }; + quickPickEntries.push(configureDefaultEntry); + } + return quickPickEntries; + } + + private async doPickEditor(editor: IUntypedEditorInput, showDefaultPicker?: boolean): Promise { + + type EditorPick = { + readonly item: IQuickPickItem; + readonly keyMods?: IKeyMods; + readonly openInBackground: boolean; + }; + + let resource = EditorResourceAccessor.getOriginalUri(editor, { supportSideBySide: SideBySideEditor.PRIMARY }); + + if (resource === undefined) { + resource = URI.from({ scheme: Schemas.untitled }); + } + + // Get all the editors for the resource as quickpick entries + const editorPicks = this.mapEditorsToQuickPickEntry(resource, showDefaultPicker); + + // Create the editor picker + const editorPicker = this.quickInputService.createQuickPick(); + const placeHolderMessage = showDefaultPicker ? + localize('promptOpenWith.updateDefaultPlaceHolder', "Select new default editor for '{0}'", `*${extname(resource)}`) : + localize('promptOpenWith.placeHolder', "Select editor for '{0}'", basename(resource)); + editorPicker.placeholder = placeHolderMessage; + editorPicker.canAcceptInBackground = true; + editorPicker.items = editorPicks; + const firstItem = editorPicker.items.find(item => item.type === 'item') as IQuickPickItem | undefined; + if (firstItem) { + editorPicker.selectedItems = [firstItem]; + } + + // Prompt the user to select an editor + const picked: EditorPick | undefined = await new Promise(resolve => { + editorPicker.onDidAccept(e => { + let result: EditorPick | undefined = undefined; + + if (editorPicker.selectedItems.length === 1) { + result = { + item: editorPicker.selectedItems[0], + keyMods: editorPicker.keyMods, + openInBackground: e.inBackground + }; + } + + // If asked to always update the setting then update it even if the gear isn't clicked + if (resource && showDefaultPicker && result?.item.id) { + this.updateUserAssociations(`*${extname(resource)}`, result.item.id,); + } + + resolve(result); + }); + + editorPicker.onDidHide(() => resolve(undefined)); + + editorPicker.onDidTriggerItemButton(e => { + + // Trigger opening and close picker + resolve({ item: e.item, openInBackground: false }); + + // Persist setting + if (resource && e.item && e.item.id) { + this.updateUserAssociations(`*${extname(resource)}`, e.item.id,); + } + }); + + editorPicker.show(); + }); + + // Close picker + editorPicker.dispose(); + + // If the user picked an editor, look at how the picker was + // used (e.g. modifier keys, open in background) and create the + // options and group to use accordingly + if (picked) { + + // If the user selected to configure default we trigger this picker again and tell it to show the default picker + if (picked.item.id === EditorResolverService.configureDefaultID) { + return this.doPickEditor(editor, true); + } + + // Figure out options + const targetOptions: IEditorOptions = { + ...editor.options, + override: picked.item.id, + preserveFocus: picked.openInBackground || editor.options?.preserveFocus, + }; + + return targetOptions; + } + + return undefined; + } + + private sendEditorResolutionTelemetry(chosenInput: EditorInput): void { + type editorResolutionClassification = { + viewType: { classification: 'PublicNonPersonalData'; purpose: 'FeatureInsight'; comment: 'The id of the editor opened. Used to gain an understanding of what editors are most popular' }; + owner: 'lramos15'; + comment: 'An event that fires when an editor type is picked'; + }; + type editorResolutionEvent = { + viewType: string; + }; + if (chosenInput.editorId) { + this.telemetryService.publicLog2('override.viewType', { viewType: chosenInput.editorId }); + } + } + + private cacheEditors() { + // Create a set to store glob patterns + const cacheStorage: Set = new Set(); + + // Store just the relative pattern pieces without any path info + for (const [globPattern, contribPoint] of this._flattenedEditors) { + const nonOptional = !!contribPoint.find(c => c.editorInfo.priority !== RegisteredEditorPriority.option && c.editorInfo.id !== DEFAULT_EDITOR_ASSOCIATION.id); + // Don't keep a cache of the optional ones as those wouldn't be opened on start anyways + if (!nonOptional) { + continue; + } + if (glob.isRelativePattern(globPattern)) { + cacheStorage.add(`${globPattern.pattern}`); + } else { + cacheStorage.add(globPattern); + } + } + + // Also store the users settings as those would have to activate on startup as well + const userAssociations = this.getAllUserAssociations(); + for (const association of userAssociations) { + if (association.filenamePattern) { + cacheStorage.add(association.filenamePattern); + } + } + this.storageService.store(EditorResolverService.cacheStorageID, JSON.stringify(Array.from(cacheStorage)), StorageScope.PROFILE, StorageTarget.MACHINE); + } + + private resourceMatchesCache(resource: URI): boolean { + if (!this.cache) { + return false; + } + + for (const cacheEntry of this.cache) { + if (globMatchesResource(cacheEntry, resource)) { + return true; + } + } + return false; + } +} + +registerSingleton(IEditorResolverService, EditorResolverService, InstantiationType.Eager); 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 518e65dc3..41b05e651 100644 --- a/vscode-web/src/vs/workbench/services/textfile/browser/textFileService.ts +++ b/vscode-web/src/vs/workbench/services/textfile/browser/textFileService.ts @@ -43,7 +43,6 @@ import { IDecorationData, IDecorationsProvider, IDecorationsService } from 'vs/w import { Emitter } from 'vs/base/common/event'; import { Codicon } from 'vs/base/common/codicons'; import { listErrorForeground } from 'vs/platform/theme/common/colorRegistry'; -import { withNullAsUndefined } from 'vs/base/common/types'; import { firstOrDefault } from 'vs/base/common/arrays'; /** @@ -94,7 +93,7 @@ export abstract class AbstractTextFileService extends Disposable implements ITex private provideDecorations(): void { // Text file model decorations - this.decorationsService.registerDecorationsProvider(new class extends Disposable implements IDecorationsProvider { + const provider = this._register(new class extends Disposable implements IDecorationsProvider { readonly label = localize('textFileModelDecorations', "Text File Model Decorations"); @@ -166,6 +165,8 @@ export abstract class AbstractTextFileService extends Disposable implements ITex return undefined; } }(this.files)); + + this._register(this.decorationsService.registerDecorationsProvider(provider)); } //#endregin @@ -312,7 +313,7 @@ export abstract class AbstractTextFileService extends Disposable implements ITex acceptTextOnly: options?.acceptTextOnly ?? false, guessEncoding: options?.autoGuessEncoding || this.textResourceConfigurationService.getValue(resource, 'files.autoGuessEncoding'), overwriteEncoding: async detectedEncoding => { - const { encoding } = await this.encoding.getPreferredReadEncoding(resource, options, withNullAsUndefined(detectedEncoding)); + const { encoding } = await this.encoding.getPreferredReadEncoding(resource, options, detectedEncoding ?? undefined); return encoding; } @@ -511,7 +512,7 @@ export abstract class AbstractTextFileService extends Disposable implements ITex let sourceTextModel: ITextModel | undefined = undefined; if (sourceModel instanceof BaseTextEditorModel) { if (sourceModel.isResolved()) { - sourceTextModel = withNullAsUndefined(sourceModel.textEditorModel); + sourceTextModel = sourceModel.textEditorModel ?? undefined; } } else { sourceTextModel = sourceModel as ITextModel; diff --git a/vscode-web/yarn.lock b/vscode-web/yarn.lock index f2393b6ff..a0dee5ff4 100644 --- a/vscode-web/yarn.lock +++ b/vscode-web/yarn.lock @@ -466,10 +466,10 @@ supports-color@^5.3.0: dependencies: has-flag "^3.0.0" -tas-client-umd@0.1.6: - version "0.1.6" - resolved "https://registry.yarnpkg.com/tas-client-umd/-/tas-client-umd-0.1.6.tgz#a0cf70a68f50d406773457630666224f0eb545a6" - integrity sha512-eOz5IK4cuNmSZI9QlqlT0FdvgfnnHDB6rjqleFaYAbzYE4RdJzYNiM28zFIXgmOVEgESvfabMFxG8WX5M4z3HA== +tas-client-umd@0.1.8: + version "0.1.8" + resolved "https://registry.yarnpkg.com/tas-client-umd/-/tas-client-umd-0.1.8.tgz#38bd32d49545417a0ea67fb618e646298e1b67cc" + integrity sha512-0jAAujLmjjGXf9PzrNpjOrr/6CTpSOp8jX80NOHK5nlOTWWpwaZ16EOyrPdHnm2bVfPHvT0/RAD0xyiQHGQvCQ== unbox-primitive@^1.0.0: version "1.0.1" @@ -517,25 +517,35 @@ which@^1.2.9: dependencies: isexe "^2.0.0" -xterm-addon-canvas@0.4.0-beta.7: - version "0.4.0-beta.7" - resolved "https://registry.yarnpkg.com/xterm-addon-canvas/-/xterm-addon-canvas-0.4.0-beta.7.tgz#ae365d8e10c900292186529f70f7f275ac94b3d5" - integrity sha512-r1hbQTsulI49orR5G3qWrJCwn2dKsEUCrgj6xsmgXuTeoUcGfed6lly+MvYlL3P8aPrxS2fC2TEzSM0Au4SX+w== - -xterm-addon-search@0.11.0: - version "0.11.0" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.11.0.tgz#2a00ff7f9848f6140e7c4d1782486b0b18b06e0d" - integrity sha512-6U4uHXcQ7G5igsdaGqrJ9ehm7vep24bXqWxuy3AnIosXF2Z5uy2MvmYRyTGNembIqPV/x1YhBQ7uShtuqBHhOQ== - -xterm-addon-unicode11@0.5.0: - version "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.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-addon-canvas@0.5.0-beta.22: + version "0.5.0-beta.22" + resolved "https://registry.yarnpkg.com/xterm-addon-canvas/-/xterm-addon-canvas-0.5.0-beta.22.tgz#513f0c2b7cf96073f47627b27e8965c1b1a22431" + integrity sha512-9F6ZI0DMRgffVfHkLkDwl5n8VscvCaV10tWI3skXOX7Y7Aws6OEeglkOPoU3IllofCU792kHKM4pPoToUxTltg== + +xterm-addon-image@0.6.0-beta.14: + version "0.6.0-beta.14" + resolved "https://registry.yarnpkg.com/xterm-addon-image/-/xterm-addon-image-0.6.0-beta.14.tgz#75fc3f824123183a4bbb5306e22f8b2c6966b0a6" + integrity sha512-D5Gh5JTKhHaPt1KwQNf6diF37KA4eToJw3XId1wy62tWmSqfq+QflhOGTfd+SnSQYCktU05ETzM+0tncIU62pQ== + +xterm-addon-search@0.13.0-beta.20: + version "0.13.0-beta.20" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.13.0-beta.20.tgz#8ddd0513e2a70fcefa325722100d2e1bfaf3b9cb" + integrity sha512-wrx6187cJ1UenGL6ZeYv3jFvRPhhENTfbC+Hv1Fnww8LmsKhcj+0+Pm6yInNjX/9hNVsNzdqKyqNeEMoykyoyA== + +xterm-addon-serialize@0.11.0-beta.20: + version "0.11.0-beta.20" + resolved "https://registry.yarnpkg.com/xterm-addon-serialize/-/xterm-addon-serialize-0.11.0-beta.20.tgz#e879b34d214761403f1081833f9221c6903bf0c3" + integrity sha512-OXnC1SATaz7kEFjFWhyv9MJaXi8yHdPjazpGLNi11h33CRTKtCQiqqPBHU87dztnXmpEX6Jw0/jr3zlyXuAmnw== + +xterm-addon-unicode11@0.6.0-beta.12: + version "0.6.0-beta.12" + resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.6.0-beta.12.tgz#ac6df9d635325dc692e4c602e74a2fc27a09405c" + integrity sha512-9wWWf/5nFafYgq0pn9EgAWnXaXGleVxfjNOqavpLRYFv0nw42QbaYyGvnGcxyYHM5Aqx/8rYE/DDVWZBqQZdYA== + +xterm-addon-webgl@0.16.0-beta.30: + version "0.16.0-beta.30" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.16.0-beta.30.tgz#820d5c65f868b14ec4177bfb8a294931a53616bf" + integrity sha512-39qPHPFmNENxcHf8/CzGHS6wzKMMegoRkHB1+scqtBhSxFaD8tX5Ye33HZIEdQ9nXe9xtr4FWVp77T+n9hdrew== xterm@5.2.0-beta.30: version "5.2.0-beta.30" diff --git a/webpack.config.js b/webpack.config.js index 91d86ca95..e585845f0 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -22,7 +22,9 @@ const VSCODE_NODE_MODULES = [ 'vscode-oniguruma', 'vscode-textmate', 'xterm', + 'xterm-addon-image', 'xterm-addon-search', + 'xterm-addon-serialize', 'xterm-addon-unicode11', 'xterm-addon-webgl', ].map((pkg) => ({ diff --git a/yarn.lock b/yarn.lock index 25b67ee82..c948ac9ec 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7,14 +7,14 @@ resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== -"@es-joy/jsdoccomment@~0.33.0": - version "0.33.0" - resolved "https://registry.yarnpkg.com/@es-joy/jsdoccomment/-/jsdoccomment-0.33.0.tgz#59416d08442d0fdbb316fc1cb491e750db80b9ad" - integrity sha512-bkxMGTlHPE4vfarXt1L1fOm81O18jTRFNgh3Fm4iPKctfWxcpJw4cpth5BhLkGZy4HFzGn/KfD/zGks/J+ZIIw== +"@es-joy/jsdoccomment@~0.40.1": + version "0.40.1" + resolved "https://registry.yarnpkg.com/@es-joy/jsdoccomment/-/jsdoccomment-0.40.1.tgz#13acd77fb372ed1c83b7355edd865a3b370c9ec4" + integrity sha512-YORCdZSusAlBrFpZ77pJjc5r1bQs5caPWtAu+WWmiSo+8XaUzseapVrfAtiRFbQWnrBxxLLEwF6f6ZG/UgCQCg== dependencies: - comment-parser "1.3.1" - esquery "^1.4.0" - jsdoc-type-pratt-parser "~3.1.0" + comment-parser "1.4.0" + esquery "^1.5.0" + jsdoc-type-pratt-parser "~4.0.0" "@eslint/eslintrc@^1.3.3": version "1.3.3" @@ -31,22 +31,24 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@github1s/vscode-web@0.12.0": - version "0.12.0" - resolved "https://registry.yarnpkg.com/@github1s/vscode-web/-/vscode-web-0.12.0.tgz#5de3d9c47c8ab4eb6120189dbb786177eadf1e1d" - integrity sha512-pi28LdqI043zk3+IcAUgZKghXu9qGFCbcaACrDV61wE7y0vQuZTgCa7bEV9I5xUPeg7RqvS33Xpn4QGfc4kF7Q== +"@github1s/vscode-web@0.13.0": + version "0.13.0" + resolved "https://registry.yarnpkg.com/@github1s/vscode-web/-/vscode-web-0.13.0.tgz#5a34f3adf44a91f0f897458cb162fe15e747c995" + integrity sha512-HC3/Aw8t3F/mTFUysVJVP+dDJCYxX+NKgLl6LAmrUteZCA9Bhl9HTRdJVwV3CX05Ksh2VFKGOCEMx8g+Nrn1Dg== dependencies: "@vscode/iconv-lite-umd" "0.7.0" "@vscode/vscode-languagedetection" "1.0.21" jschardet "3.0.0" - tas-client-umd "0.1.6" + tas-client-umd "0.1.8" vscode-oniguruma "1.7.0" 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.7" + xterm-addon-canvas "0.5.0-beta.22" + xterm-addon-image "0.6.0-beta.14" + xterm-addon-search "0.13.0-beta.20" + xterm-addon-serialize "0.11.0-beta.20" + xterm-addon-unicode11 "0.6.0-beta.12" + xterm-addon-webgl "0.16.0-beta.30" "@hapi/hoek@^9.0.0": version "9.3.0" @@ -680,6 +682,11 @@ anymatch@~3.1.2: normalize-path "^3.0.0" picomatch "^2.0.4" +are-docs-informative@^0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/are-docs-informative/-/are-docs-informative-0.0.2.tgz#387f0e93f5d45280373d387a59d34c96db321963" + integrity sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig== + argparse@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" @@ -800,6 +807,11 @@ buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== +builtin-modules@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6" + integrity sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw== + bytes@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" @@ -987,10 +999,10 @@ commander@^9.3.0: resolved "https://registry.yarnpkg.com/commander/-/commander-9.3.0.tgz#f619114a5a2d2054e0d9ff1b31d5ccf89255e26b" integrity sha512-hv95iU5uXPbK83mjrJKuZyFM/LBAoCV/XhVGkS5Je6tl7sxr6A0ITMw5WoRV46/UaJ46Nllm3Xt7IaJhXTIkzw== -comment-parser@1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/comment-parser/-/comment-parser-1.3.1.tgz#3d7ea3adaf9345594aedee6563f422348f165c1b" - integrity sha512-B52sN2VNghyq5ofvUsqZjmk6YkihBX5vMSChmSK9v4ShjKf3Vk5Xcmgpw4o+iIgtrnM/u5FiMpz9VKb8lpBveA== +comment-parser@1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/comment-parser/-/comment-parser-1.4.0.tgz#0f8c560f59698193854f12884c20c0e39a26d32c" + integrity sha512-QLyTNiZ2KDOibvFPlZ6ZngVsZ/0gYnE6uTXi5aoDg8ed3AkJAz4sEje3Y8a29hQ1s6A99MZXe47fLAXQ1rTqaw== compressible@~2.0.16: version "2.0.18" @@ -1396,17 +1408,19 @@ eslint-config-prettier@^8.5.0: resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz#5a81680ec934beca02c7b1a61cf8ca34b66feab1" integrity sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q== -eslint-plugin-jsdoc@^39.3.14: - version "39.3.14" - resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-39.3.14.tgz#2591bf48907a7527a6e689fd721634364d08fe4e" - integrity sha512-kle7ot5xvzXwWzg7ElzTPM/y1IWUo0kfa5X+ZwOC/7Jw81OJaqIaNEk+2ZH+HcKkbwRUQ3RTdK9qsm4p5vbXAQ== +eslint-plugin-jsdoc@^46.8.2: + version "46.8.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-46.8.2.tgz#3e6b1c93e91e38fe01874d45da121b56393c54a5" + integrity sha512-5TSnD018f3tUJNne4s4gDWQflbsgOycIKEUBoCLn6XtBMgNHxQFmV8vVxUtiPxAQq8lrX85OaSG/2gnctxw9uQ== dependencies: - "@es-joy/jsdoccomment" "~0.33.0" - comment-parser "1.3.1" + "@es-joy/jsdoccomment" "~0.40.1" + are-docs-informative "^0.0.2" + comment-parser "1.4.0" debug "^4.3.4" escape-string-regexp "^4.0.0" - esquery "^1.4.0" - semver "^7.3.8" + esquery "^1.5.0" + is-builtin-module "^3.2.1" + semver "^7.5.4" spdx-expression-parse "^3.0.1" eslint-plugin-prettier@^4.2.1: @@ -1509,6 +1523,13 @@ esquery@^1.4.0: dependencies: estraverse "^5.1.0" +esquery@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" + integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== + dependencies: + estraverse "^5.1.0" + esrecurse@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" @@ -2218,6 +2239,13 @@ is-boolean-object@^1.1.0: call-bind "^1.0.2" has-tostringtag "^1.0.0" +is-builtin-module@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-3.2.1.tgz#f03271717d8654cfcaf07ab0463faa3571581169" + integrity sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A== + dependencies: + builtin-modules "^3.3.0" + is-callable@^1.1.4, is-callable@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945" @@ -2398,10 +2426,10 @@ jschardet@3.0.0: resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-3.0.0.tgz#898d2332e45ebabbdb6bf2feece9feea9a99e882" integrity sha512-lJH6tJ77V8Nzd5QWRkFYCLc13a3vADkh3r/Fi8HupZGWk2OVVDfnZP8V/VgQgZ+lzW0kG2UGb5hFgt3V3ndotQ== -jsdoc-type-pratt-parser@~3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-3.1.0.tgz#a4a56bdc6e82e5865ffd9febc5b1a227ff28e67e" - integrity sha512-MgtD0ZiCDk9B+eI73BextfRrVQl0oyzRG8B2BjORts6jbunj4ScKPcyXGTbB6eXL4y9TzxCm6hyeLq/2ASzNdw== +jsdoc-type-pratt-parser@~4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.0.0.tgz#136f0571a99c184d84ec84662c45c29ceff71114" + integrity sha512-YtOli5Cmzy3q4dP26GraSOeAhqecewG04hoO8DY56CH4KJ9Fvv5qKWUCCo3HZob7esJQHCv6/+bnTy72xZZaVQ== json-parse-better-errors@^1.0.1: version "1.0.2" @@ -3443,7 +3471,7 @@ selfsigned@^2.0.1: resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== -semver@^7.3.4, semver@^7.3.7, semver@^7.3.8: +semver@^7.3.4, semver@^7.3.7, semver@^7.3.8, semver@^7.5.4: version "7.5.4" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== @@ -3856,10 +3884,10 @@ tapable@^2.0.0, tapable@^2.1.1, tapable@^2.2.0: resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== -tas-client-umd@0.1.6: - version "0.1.6" - resolved "https://registry.yarnpkg.com/tas-client-umd/-/tas-client-umd-0.1.6.tgz#a0cf70a68f50d406773457630666224f0eb545a6" - integrity sha512-eOz5IK4cuNmSZI9QlqlT0FdvgfnnHDB6rjqleFaYAbzYE4RdJzYNiM28zFIXgmOVEgESvfabMFxG8WX5M4z3HA== +tas-client-umd@0.1.8: + version "0.1.8" + resolved "https://registry.yarnpkg.com/tas-client-umd/-/tas-client-umd-0.1.8.tgz#38bd32d49545417a0ea67fb618e646298e1b67cc" + integrity sha512-0jAAujLmjjGXf9PzrNpjOrr/6CTpSOp8jX80NOHK5nlOTWWpwaZ16EOyrPdHnm2bVfPHvT0/RAD0xyiQHGQvCQ== terser-webpack-plugin@^5.1.3: version "5.3.6" @@ -4274,25 +4302,35 @@ ws@^8.4.2: resolved "https://registry.yarnpkg.com/ws/-/ws-8.8.0.tgz#8e71c75e2f6348dbf8d78005107297056cb77769" integrity sha512-JDAgSYQ1ksuwqfChJusw1LSJ8BizJ2e/vVu5Lxjq3YvNJNlROv1ui4i+c/kUUrPheBvQl4c5UbERhTwKa6QBJQ== -xterm-addon-canvas@0.4.0-beta.7: - version "0.4.0-beta.7" - resolved "https://registry.yarnpkg.com/xterm-addon-canvas/-/xterm-addon-canvas-0.4.0-beta.7.tgz#ae365d8e10c900292186529f70f7f275ac94b3d5" - integrity sha512-r1hbQTsulI49orR5G3qWrJCwn2dKsEUCrgj6xsmgXuTeoUcGfed6lly+MvYlL3P8aPrxS2fC2TEzSM0Au4SX+w== - -xterm-addon-search@0.11.0: - version "0.11.0" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.11.0.tgz#2a00ff7f9848f6140e7c4d1782486b0b18b06e0d" - integrity sha512-6U4uHXcQ7G5igsdaGqrJ9ehm7vep24bXqWxuy3AnIosXF2Z5uy2MvmYRyTGNembIqPV/x1YhBQ7uShtuqBHhOQ== - -xterm-addon-unicode11@0.5.0: - version "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.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-addon-canvas@0.5.0-beta.22: + version "0.5.0-beta.22" + resolved "https://registry.yarnpkg.com/xterm-addon-canvas/-/xterm-addon-canvas-0.5.0-beta.22.tgz#513f0c2b7cf96073f47627b27e8965c1b1a22431" + integrity sha512-9F6ZI0DMRgffVfHkLkDwl5n8VscvCaV10tWI3skXOX7Y7Aws6OEeglkOPoU3IllofCU792kHKM4pPoToUxTltg== + +xterm-addon-image@0.6.0-beta.14: + version "0.6.0-beta.14" + resolved "https://registry.yarnpkg.com/xterm-addon-image/-/xterm-addon-image-0.6.0-beta.14.tgz#75fc3f824123183a4bbb5306e22f8b2c6966b0a6" + integrity sha512-D5Gh5JTKhHaPt1KwQNf6diF37KA4eToJw3XId1wy62tWmSqfq+QflhOGTfd+SnSQYCktU05ETzM+0tncIU62pQ== + +xterm-addon-search@0.13.0-beta.20: + version "0.13.0-beta.20" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.13.0-beta.20.tgz#8ddd0513e2a70fcefa325722100d2e1bfaf3b9cb" + integrity sha512-wrx6187cJ1UenGL6ZeYv3jFvRPhhENTfbC+Hv1Fnww8LmsKhcj+0+Pm6yInNjX/9hNVsNzdqKyqNeEMoykyoyA== + +xterm-addon-serialize@0.11.0-beta.20: + version "0.11.0-beta.20" + resolved "https://registry.yarnpkg.com/xterm-addon-serialize/-/xterm-addon-serialize-0.11.0-beta.20.tgz#e879b34d214761403f1081833f9221c6903bf0c3" + integrity sha512-OXnC1SATaz7kEFjFWhyv9MJaXi8yHdPjazpGLNi11h33CRTKtCQiqqPBHU87dztnXmpEX6Jw0/jr3zlyXuAmnw== + +xterm-addon-unicode11@0.6.0-beta.12: + version "0.6.0-beta.12" + resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.6.0-beta.12.tgz#ac6df9d635325dc692e4c602e74a2fc27a09405c" + integrity sha512-9wWWf/5nFafYgq0pn9EgAWnXaXGleVxfjNOqavpLRYFv0nw42QbaYyGvnGcxyYHM5Aqx/8rYE/DDVWZBqQZdYA== + +xterm-addon-webgl@0.16.0-beta.30: + version "0.16.0-beta.30" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.16.0-beta.30.tgz#820d5c65f868b14ec4177bfb8a294931a53616bf" + integrity sha512-39qPHPFmNENxcHf8/CzGHS6wzKMMegoRkHB1+scqtBhSxFaD8tX5Ye33HZIEdQ9nXe9xtr4FWVp77T+n9hdrew== xterm@5.2.0-beta.30: version "5.2.0-beta.30"