From e99c4145f74bab691ad60a65a4da2dcd3abf6f8a Mon Sep 17 00:00:00 2001 From: Quinn Slack Date: Sat, 25 Feb 2023 10:28:43 -0800 Subject: [PATCH] remove enableLegacyExtensions and all now-unreachable code (#47517) This is part of the extension API deprecation (https://docs.google.com/document/d/10vtoe-kpNvVZ8Etrx34bSCoTaCCHxX8o3ncCmuErPZo/edit). There are a lot more refactors to do after this, but this removes the admin-facing `enableLegacyExtensions` setting and some of the root-level instantiation and usage of the extensions controller API. ## Test plan Check that hovers still work on dotcom. --- CHANGELOG.md | 1 + client/browser/src/integration/github.test.ts | 28 +- client/browser/src/integration/gitlab.test.ts | 14 +- client/browser/src/shared/platform/context.ts | 40 +- .../platform/inlineExtensionsService.ts | 52 +-- client/shared/src/api/client/connection.ts | 8 +- .../src/api/client/enabledExtensions.ts | 90 ---- .../src/api/client/mainthread-api.test.ts | 40 +- .../shared/src/api/client/mainthread-api.ts | 16 +- client/shared/src/api/client/search.ts | 45 +- client/shared/src/api/contract.ts | 6 +- client/shared/src/api/extension/activation.ts | 67 +-- .../src/api/extension/test/activation.test.ts | 3 +- .../test/extensionHost.configuration.test.ts | 6 - .../extensionHost.documentHighlights.test.ts | 1 - .../test/extensionHost.hover.test.ts | 1 - .../test/extensionHost.logging.test.ts | 1 - .../test/extensionHost.search.test.ts | 1 - client/shared/src/backend/apolloCache.ts | 7 - .../extensions/createLazyLoadedController.ts | 66 --- .../extensions/createSyncLoadedController.ts | 1 - client/shared/src/extensions/extension.ts | 2 +- client/shared/src/extensions/helpers.test.ts | 32 -- client/shared/src/extensions/helpers.ts | 87 ---- client/shared/src/hover/actions.ts | 78 ++-- .../src/notifications/NotificationItem.tsx | 32 +- client/shared/src/platform/context.ts | 18 +- .../src/testing/integration/mockExtension.ts | 80 +--- client/shared/src/testing/testHelpers.ts | 2 - client/vscode/src/webview/platform/context.ts | 2 - client/vscode/tests/vsce.test.ts | 10 - client/web/dev/utils/create-js-context.ts | 1 - .../web/dev/utils/get-api-proxy-settings.ts | 3 - client/web/src/Layout.tsx | 1 - client/web/src/LegacyLayout.tsx | 1 - client/web/src/LegacySourcegraphWebApp.tsx | 25 +- .../web/src/backend/persistenceMapper.test.ts | 1 - client/web/src/backend/persistenceMapper.ts | 6 +- .../WebCommandListPopoverButton.module.scss | 21 - .../WebCommandListPopoverButton.tsx | 36 -- .../WebCommandListPopoverButton/index.ts | 1 - client/web/src/components/shared.tsx | 1 - client/web/src/components/useCarousel.ts | 129 ------ .../insights/CodeInsightsRouter.tsx | 2 +- .../components/ActionItemsBar.module.scss | 151 ------- .../components/ActionItemsBar.story.tsx | 101 ----- .../extensions/components/ActionItemsBar.tsx | 412 ------------------ .../web/src/integration/blob-viewer.test.ts | 367 ---------------- .../integration/codemirror-blob-view.test.ts | 184 -------- client/web/src/integration/jscontext.ts | 1 - client/web/src/jscontext.ts | 3 - client/web/src/nav/GlobalNavbar.story.tsx | 3 - client/web/src/nav/GlobalNavbar.test.tsx | 13 +- client/web/src/nav/GlobalNavbar.tsx | 19 +- client/web/src/platform/context.ts | 6 +- client/web/src/repo/RepoContainer.tsx | 79 ++-- client/web/src/repo/RepoHeader.story.tsx | 13 - client/web/src/repo/RepoHeader.tsx | 14 +- client/web/src/repo/RepoRevisionContainer.tsx | 3 - .../web/src/repo/RepositoryFileTreePage.tsx | 1 - client/web/src/repo/blob/BlobPage.tsx | 57 ++- client/web/src/repo/repoContainerRoutes.tsx | 24 - .../src/repo/repoRevisionContainerRoutes.tsx | 17 - client/web/src/repo/tree/TreePage.tsx | 3 - client/web/src/search/SearchConsolePage.tsx | 7 +- .../web/src/search/home/SearchPage.story.tsx | 2 - client/web/src/search/home/SearchPage.tsx | 2 - .../results/SearchResultsCacheProvider.tsx | 6 +- .../results/SearchResultsInfoBar.test.tsx | 8 - .../search/results/SearchResultsInfoBar.tsx | 55 +-- .../results/StreamingSearchResults.story.tsx | 2 - .../results/StreamingSearchResults.test.tsx | 5 - .../search/results/StreamingSearchResults.tsx | 7 +- .../SearchResultsInfoBar.test.tsx.snap | 20 - .../web/src/settings/MonacoSettingsEditor.tsx | 8 - client/web/src/settings/SettingsArea.tsx | 33 +- client/web/src/settings/configuration.test.ts | 66 --- client/web/src/settings/configuration.ts | 54 --- .../graphqlbackend/default_settings.go | 64 +-- .../graphqlbackend/extension_registry.go | 61 --- cmd/frontend/graphqlbackend/graphqlbackend.go | 3 - cmd/frontend/graphqlbackend/node.go | 7 - cmd/frontend/graphqlbackend/schema.graphql | 79 ---- cmd/frontend/graphqlbackend/site.go | 4 - .../internal/app/jscontext/jscontext.go | 4 - .../app/ui/legacy_extensions_redirects.go | 12 - .../ui/legacy_extensions_redirects_test.go | 38 -- cmd/frontend/internal/app/ui/router.go | 4 +- .../api/extension_connection_graphql.go | 98 ----- .../api/extension_connection_graphql_test.go | 53 --- .../registry/api/extension_graphql.go | 52 --- .../registry/api/extension_manifest.go | 48 -- .../registry/api/extension_manifest_test.go | 39 -- .../registry/api/extension_remote_graphql.go | 37 -- cmd/frontend/registry/api/extensions.go | 89 ---- cmd/frontend/registry/api/extensions_test.go | 14 - cmd/frontend/registry/api/gating.go | 72 --- cmd/frontend/registry/api/registry_graphql.go | 24 - internal/conf/platform.go | 57 --- internal/conf/platform_test.go | 76 ---- internal/search/keyword/file_score.go | 14 - schema/schema.go | 26 -- schema/settings.schema.json | 20 - schema/site.schema.json | 46 -- 104 files changed, 173 insertions(+), 3679 deletions(-) delete mode 100644 client/shared/src/api/client/enabledExtensions.ts delete mode 100644 client/shared/src/extensions/createLazyLoadedController.ts delete mode 100644 client/shared/src/extensions/helpers.test.ts delete mode 100644 client/shared/src/extensions/helpers.ts delete mode 100644 client/web/src/components/WebCommandListPopoverButton/WebCommandListPopoverButton.module.scss delete mode 100644 client/web/src/components/WebCommandListPopoverButton/WebCommandListPopoverButton.tsx delete mode 100644 client/web/src/components/WebCommandListPopoverButton/index.ts delete mode 100644 client/web/src/components/useCarousel.ts delete mode 100644 client/web/src/extensions/components/ActionItemsBar.module.scss delete mode 100644 client/web/src/extensions/components/ActionItemsBar.story.tsx delete mode 100644 client/web/src/extensions/components/ActionItemsBar.tsx delete mode 100644 client/web/src/settings/configuration.test.ts delete mode 100644 client/web/src/settings/configuration.ts delete mode 100644 cmd/frontend/graphqlbackend/extension_registry.go delete mode 100644 cmd/frontend/internal/app/ui/legacy_extensions_redirects.go delete mode 100644 cmd/frontend/registry/api/extension_connection_graphql.go delete mode 100644 cmd/frontend/registry/api/extension_connection_graphql_test.go delete mode 100644 cmd/frontend/registry/api/extension_graphql.go delete mode 100644 cmd/frontend/registry/api/extension_manifest.go delete mode 100644 cmd/frontend/registry/api/extension_manifest_test.go delete mode 100644 cmd/frontend/registry/api/extension_remote_graphql.go delete mode 100644 cmd/frontend/registry/api/extensions.go delete mode 100644 cmd/frontend/registry/api/extensions_test.go delete mode 100644 cmd/frontend/registry/api/registry_graphql.go delete mode 100644 internal/conf/platform.go delete mode 100644 internal/conf/platform_test.go delete mode 100644 internal/search/keyword/file_score.go diff --git a/CHANGELOG.md b/CHANGELOG.md index f0a54dd3d76..6ddbca73efb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -100,6 +100,7 @@ All notable changes to Sourcegraph are documented in this file. - The Code insights "run over all repositories" mode has been replaced with search-powered repositories filed syntax. [#45687](https://github.com/sourcegraph/sourcegraph/pull/45687) - The settings `search.repositoryGroups`, `codeInsightsGqlApi`, `codeInsightsAllRepos`, `experimentalFeatures.copyQueryButton`,, `experimentalFeatures.showRepogroupHomepage`, `experimentalFeatures.showOnboardingTour`, `experimentalFeatures.showSearchContextManagement` and `codeIntelligence.autoIndexRepositoryGroups` have been removed as they were deprecated and unsued. [#47481](https://github.com/sourcegraph/sourcegraph/pull/47481) +- The site config `enableLegacyExtensions` setting was removed. It is no longer possible to enable legacy Sourcegraph extension API functionality in this version. ## 4.4.2 diff --git a/client/browser/src/integration/github.test.ts b/client/browser/src/integration/github.test.ts index 5e410c506d9..8626c79741d 100644 --- a/client/browser/src/integration/github.test.ts +++ b/client/browser/src/integration/github.test.ts @@ -102,11 +102,6 @@ describe('GitHub', () => { siteAdmin: false, }, }), - EnableLegacyExtensions: () => ({ - site: { - enableLegacyExtensions: true, - }, - }), }) // Ensure that the same assets are requested in all environments. @@ -155,10 +150,7 @@ describe('GitHub', () => { // it('shows hover tooltips when hovering a token and respects "Enable single click to go to definition" setting', async () => { // mockUrls(['https://github.com/*path/find-definition']) - // const { mockExtension, Extensions, extensionSettings } = setupExtensionMocking({ - // pollyServer: testContext.server, - // sourcegraphBaseUrl: driver.sourcegraphBaseUrl, - // }) + // const { mockExtension, Extensions, extensionSettings } = setupExtensionMocking() // const userSettings: Settings = { // extensions: extensionSettings, @@ -316,16 +308,14 @@ describe('GitHub', () => { // }) describe('Pull request pages', () => { - describe('Files Changed view', () => { + // TODO(sqs): skipped because these have not been reimplemented after the extension API deprecation + describe.skip('Files Changed view', () => { // For each pull request test, set up a mock extension that verifies that the correct // file and revision info reach extensions. beforeEach(() => { mockUrls(['https://github.com/*path/find-definition']) - const { mockExtension, Extensions, extensionSettings } = setupExtensionMocking({ - pollyServer: testContext.server, - sourcegraphBaseUrl: driver.sourcegraphBaseUrl, - }) + const { mockExtension, extensionSettings } = setupExtensionMocking() const userSettings: Settings = { extensions: extensionSettings, @@ -350,7 +340,6 @@ describe('GitHub', () => { merged: { contents: JSON.stringify(userSettings), messages: [] }, }, }), - Extensions, ResolveRev: ({ revision }) => ({ repository: { mirrorInfo: { cloned: true }, @@ -639,7 +628,8 @@ describe('GitHub', () => { }) }) - describe('Commit view', () => { + // TODO(sqs): skipped because these have not been reimplemented after the extension API deprecation + describe.skip('Commit view', () => { beforeEach(() => { mockUrls([ 'https://github.com/*path/find-definition', @@ -647,10 +637,7 @@ describe('GitHub', () => { 'https://github.com/commits/badges', ]) - const { mockExtension, Extensions, extensionSettings } = setupExtensionMocking({ - pollyServer: testContext.server, - sourcegraphBaseUrl: driver.sourcegraphBaseUrl, - }) + const { mockExtension, extensionSettings } = setupExtensionMocking() const userSettings: Settings = { extensions: extensionSettings, @@ -691,7 +678,6 @@ describe('GitHub', () => { }, }, }), - Extensions, }) // Serve a mock extension with a simple hover provider diff --git a/client/browser/src/integration/gitlab.test.ts b/client/browser/src/integration/gitlab.test.ts index c08bcb2a657..b6bada1f4b5 100644 --- a/client/browser/src/integration/gitlab.test.ts +++ b/client/browser/src/integration/gitlab.test.ts @@ -91,11 +91,6 @@ describe('GitLab', () => { hasCodeIntelligence: true, }, }), - EnableLegacyExtensions: () => ({ - site: { - enableLegacyExtensions: true, - }, - }), }) // Ensure that the same assets are requested in all environments. @@ -146,11 +141,9 @@ describe('GitLab', () => { }) }) - it('shows hover tooltips when hovering a token', async () => { - const { mockExtension, Extensions, extensionSettings } = setupExtensionMocking({ - pollyServer: testContext.server, - sourcegraphBaseUrl: driver.sourcegraphBaseUrl, - }) + // TODO(sqs): skipped because these have not been reimplemented after the extension API deprecation + it.skip('shows hover tooltips when hovering a token', async () => { + const { mockExtension, extensionSettings } = setupExtensionMocking() const userSettings: Settings = { extensions: extensionSettings, @@ -175,7 +168,6 @@ describe('GitLab', () => { merged: { contents: JSON.stringify(userSettings), messages: [] }, }, }), - Extensions, }) // Serve a mock extension with a simple hover provider diff --git a/client/browser/src/shared/platform/context.ts b/client/browser/src/shared/platform/context.ts index 2562dce9c3c..504ff01ffa7 100644 --- a/client/browser/src/shared/platform/context.ts +++ b/client/browser/src/shared/platform/context.ts @@ -1,5 +1,5 @@ -import { combineLatest, ReplaySubject, of } from 'rxjs' -import { map, switchMap } from 'rxjs/operators' +import { combineLatest, ReplaySubject } from 'rxjs' +import { map } from 'rxjs/operators' import { asError } from '@sourcegraph/common' import { isHTTPAuthError } from '@sourcegraph/http-client' @@ -8,13 +8,11 @@ import { mutateSettings, updateSettings } from '@sourcegraph/shared/src/settings import { EMPTY_SETTINGS_CASCADE, gqlToCascade, SettingsSubject } from '@sourcegraph/shared/src/settings/settings' import { toPrettyBlobURL } from '@sourcegraph/shared/src/util/url' -import { background } from '../../browser-extension/web-extension-api/runtime' import { createGraphQLHelpers } from '../backend/requestGraphQl' import { CodeHost } from '../code-hosts/shared/codeHost' -import { isInPage } from '../context' import { createExtensionHost } from './extensionHost' -import { getInlineExtensions, shouldUseInlineExtensions } from './inlineExtensionsService' +import { getInlineExtensions } from './inlineExtensionsService' import { editClientSettings, fetchViewerSettings, mergeCascades, storageSettingsCascade } from './settings' export interface SourcegraphIntegrationURLs { @@ -57,8 +55,6 @@ export function createPlatformContext( }>(1) const { requestGraphQL, getBrowserGraphQLClient } = createGraphQLHelpers(sourcegraphURL, isExtension) - const shouldUseInlineExtensionsObservable = shouldUseInlineExtensions(requestGraphQL) - const context: BrowserPlatformContext = { /** * The active settings cascade. @@ -120,31 +116,6 @@ export function createPlatformContext( requestGraphQL, getGraphQLClient: getBrowserGraphQLClient, createExtensionHost: () => createExtensionHost({ assetsURL }), - getScriptURLForExtension: () => { - if (isInPage) { - return undefined - } - - return bundleURLs => - shouldUseInlineExtensionsObservable.toPromise().then(shouldUseInlineExtensions => { - if (shouldUseInlineExtensions) { - // inline extensions have fixed scriptURLs - return bundleURLs - } - - // We need to import the extension's JavaScript file (in importScripts in the Web Worker) from a blob: - // URI, not its original http:/https: URL, because Chrome extensions are not allowed to be published - // with a CSP that allowlists https://* in script-src (see - // https://developer.chrome.com/extensions/contentSecurityPolicy#relaxing-remote-script). (Firefox - // add-ons have an even stricter restriction.) - return Promise.allSettled(bundleURLs.map(bundleURL => background.createBlobURL(bundleURL))).then( - results => - results.map(result => - result.status === 'rejected' ? asError(result.reason) : result.value - ) - ) - }) - }, urlToFile: ({ rawRepoName, ...target }, context) => { // We don't always resolve the rawRepoName, e.g. if there are multiple definitions. // Construct URL to file on code host, if possible. @@ -156,10 +127,7 @@ export function createPlatformContext( }, sourcegraphURL, clientApplication: 'other', - getStaticExtensions: () => - shouldUseInlineExtensionsObservable.pipe( - switchMap(shouldUseInline => (shouldUseInline ? getInlineExtensions() : of(undefined))) - ), + getStaticExtensions: () => getInlineExtensions(), } return context } diff --git a/client/browser/src/shared/platform/inlineExtensionsService.ts b/client/browser/src/shared/platform/inlineExtensionsService.ts index 1f8b5b3dc6e..e058f99c61e 100644 --- a/client/browser/src/shared/platform/inlineExtensionsService.ts +++ b/client/browser/src/shared/platform/inlineExtensionsService.ts @@ -1,60 +1,10 @@ import { Observable, from } from 'rxjs' -import { map } from 'rxjs/operators' -import { checkOk, isErrorGraphQLResult, gql } from '@sourcegraph/http-client' +import { checkOk } from '@sourcegraph/http-client' import { ExecutableExtension } from '@sourcegraph/shared/src/api/extension/activation' import { ExtensionManifest } from '@sourcegraph/shared/src/extensions/extensionManifest' -import { PlatformContext } from '@sourcegraph/shared/src/platform/context' import extensions from '../../../code-intel-extensions.json' -import { EnableLegacyExtensionsResult } from '../../graphql-operations' - -const DEFAULT_ENABLE_LEGACY_EXTENSIONS = false - -/** - * Determine which extensions should be loaded: - * - inline (bundled with the browser extension) - * - or from the extensions registry (if `enableLegacyExtensions` experimental feature value is set to `true`). - */ -export const shouldUseInlineExtensions = (requestGraphQL: PlatformContext['requestGraphQL']): Observable => - requestGraphQL({ - request: gql` - query EnableLegacyExtensions { - site { - enableLegacyExtensions - } - } - `, - variables: {}, - mightContainPrivateInfo: false, - }).pipe( - map(result => { - if (isErrorGraphQLResult(result)) { - // EnableLegacyExtensions query resolver may not be implemented on older versions. - // Return `true` by default. - return true - } - - try { - const enableLegacyExtensions = result.data.site.enableLegacyExtensions - return typeof enableLegacyExtensions === 'undefined' - ? DEFAULT_ENABLE_LEGACY_EXTENSIONS - : enableLegacyExtensions - } catch { - return DEFAULT_ENABLE_LEGACY_EXTENSIONS - } - }), - map(enableLegacyExtensions => { - // TODO: The Phabricator native extension is currently the only runtime that runs the - // browser extension code but does not use bundled extensions yet. We will fix this - // when we update the browser extensions to use the new code intel APIs (#42104). - if (window.SOURCEGRAPH_PHABRICATOR_EXTENSION === true) { - return false - } - - return !enableLegacyExtensions - }) - ) /** * Get the manifest URL and script URL for a Sourcegraph extension which is inline (bundled with the browser add-on). diff --git a/client/shared/src/api/client/connection.ts b/client/shared/src/api/client/connection.ts index 2f97ec48a65..90d2a3e6398 100644 --- a/client/shared/src/api/client/connection.ts +++ b/client/shared/src/api/client/connection.ts @@ -44,13 +44,7 @@ export async function createExtensionHostClientConnection( initData: Omit, platformContext: Pick< PlatformContext, - | 'settings' - | 'updateSettings' - | 'getGraphQLClient' - | 'requestGraphQL' - | 'telemetryService' - | 'getScriptURLForExtension' - | 'clientApplication' + 'settings' | 'updateSettings' | 'getGraphQLClient' | 'requestGraphQL' | 'telemetryService' | 'clientApplication' > ): Promise<{ subscription: Unsubscribable diff --git a/client/shared/src/api/client/enabledExtensions.ts b/client/shared/src/api/client/enabledExtensions.ts deleted file mode 100644 index 2cd71f10148..00000000000 --- a/client/shared/src/api/client/enabledExtensions.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { isEqual, once } from 'lodash' -import { combineLatest, from, Observable, throwError } from 'rxjs' -import { catchError, distinctUntilChanged, map, publishReplay, refCount, shareReplay, switchMap } from 'rxjs/operators' - -import { asError } from '@sourcegraph/common' - -import { ConfiguredExtension, extensionIDsFromSettings, isExtensionEnabled } from '../../extensions/extension' -import { areExtensionsSame } from '../../extensions/extensions' -import { queryConfiguredRegistryExtensions } from '../../extensions/helpers' -import { PlatformContext } from '../../platform/context' -import { isSettingsValid } from '../../settings/settings' - -/** - * @returns An observable that emits the list of extensions configured in the viewer's final settings upon - * subscription and each time it changes. - */ -function viewerConfiguredExtensions({ - settings, - getGraphQLClient, -}: Pick): Observable { - return from(settings).pipe( - map(settings => extensionIDsFromSettings(settings)), - distinctUntilChanged((a, b) => isEqual(a, b)), - switchMap(extensionIDs => queryConfiguredRegistryExtensions({ getGraphQLClient }, extensionIDs)), - catchError(error => throwError(asError(error))), - // TODO: Restore reference counter after refactoring contributions service - // to not unsubscribe from existing entries when new entries are registered, - // in order to ensure that the source is unsubscribed from. - shareReplay(1) - ) -} - -/** - * List of extensions migrated to the core workflow. - */ -export const MIGRATED_TO_CORE_WORKFLOW_EXTENSION_IDS = new Set([ - 'sourcegraph/git-extras', - 'sourcegraph/search-export', - 'sourcegraph/open-in-editor', - 'sourcegraph/open-in-vscode', - 'dymka/open-in-webstorm', - 'sourcegraph/open-in-atom', -]) - -/** - * Returns an Observable of extensions enabled for the user. - * Wrapped with the `once` function from lodash. - */ -export const getEnabledExtensions = once( - ( - context: Pick< - PlatformContext, - 'settings' | 'getGraphQLClient' | 'getScriptURLForExtension' | 'clientApplication' - > - ): Observable => - combineLatest([viewerConfiguredExtensions(context), context.settings]).pipe( - map(([configuredExtensions, settings]) => { - const enableGoImportsSearchQueryTransform = - isSettingsValid(settings) && - settings.final.experimentalFeatures?.enableGoImportsSearchQueryTransform - - return configuredExtensions.filter(extension => { - const extensionsAsCoreFeatureMigratedExtension = MIGRATED_TO_CORE_WORKFLOW_EXTENSION_IDS.has( - extension.id - ) - // Ignore extensions migrated to the core workflow if the experimental feature is enabled - if (context.clientApplication === 'sourcegraph' && extensionsAsCoreFeatureMigratedExtension) { - return false - } - - // Go import search query transform is enabled by default but can be disabled by the setting - const enableGoImportsSearchQueryTransformMigratedExtension = - (enableGoImportsSearchQueryTransform === undefined || enableGoImportsSearchQueryTransform) && - extension.id === 'go-imports-search' - // Ignore loading the go-imports-search extension when the migrated go imports search is enabled - if ( - context.clientApplication === 'sourcegraph' && - enableGoImportsSearchQueryTransformMigratedExtension - ) { - return false - } - - return isExtensionEnabled(settings.final, extension.id) - }) - }), - distinctUntilChanged((a, b) => areExtensionsSame(a, b)), - publishReplay(1), - refCount() - ) -) diff --git a/client/shared/src/api/client/mainthread-api.test.ts b/client/shared/src/api/client/mainthread-api.test.ts index 15842987dbc..3537d8bd192 100644 --- a/client/shared/src/api/client/mainthread-api.test.ts +++ b/client/shared/src/api/client/mainthread-api.test.ts @@ -22,18 +22,12 @@ describe('MainThreadAPI', () => { const platformContext: Pick< PlatformContext, - | 'updateSettings' - | 'settings' - | 'getGraphQLClient' - | 'requestGraphQL' - | 'getScriptURLForExtension' - | 'clientApplication' + 'updateSettings' | 'settings' | 'getGraphQLClient' | 'requestGraphQL' | 'clientApplication' > = { settings: EMPTY, getGraphQLClient, updateSettings: () => Promise.resolve(), requestGraphQL, - getScriptURLForExtension: () => undefined, clientApplication: 'other', } @@ -61,18 +55,12 @@ describe('MainThreadAPI', () => { const platformContext: Pick< PlatformContext, - | 'updateSettings' - | 'settings' - | 'getGraphQLClient' - | 'requestGraphQL' - | 'getScriptURLForExtension' - | 'clientApplication' + 'updateSettings' | 'settings' | 'getGraphQLClient' | 'requestGraphQL' | 'clientApplication' > = { settings: EMPTY, getGraphQLClient, updateSettings: () => Promise.resolve(), requestGraphQL, - getScriptURLForExtension: () => undefined, clientApplication: 'other', } @@ -93,12 +81,7 @@ describe('MainThreadAPI', () => { } const platformContext: Pick< PlatformContext, - | 'updateSettings' - | 'settings' - | 'requestGraphQL' - | 'getGraphQLClient' - | 'getScriptURLForExtension' - | 'clientApplication' + 'updateSettings' | 'settings' | 'requestGraphQL' | 'getGraphQLClient' | 'clientApplication' > = { settings: of({ subjects: [ @@ -131,7 +114,6 @@ describe('MainThreadAPI', () => { updateSettings, getGraphQLClient, requestGraphQL: () => EMPTY, - getScriptURLForExtension: () => undefined, clientApplication: 'other', } @@ -161,18 +143,12 @@ describe('MainThreadAPI', () => { const platformContext: Pick< PlatformContext, - | 'updateSettings' - | 'settings' - | 'getGraphQLClient' - | 'requestGraphQL' - | 'getScriptURLForExtension' - | 'clientApplication' + 'updateSettings' | 'settings' | 'getGraphQLClient' | 'requestGraphQL' | 'clientApplication' > = { getGraphQLClient, settings: of(...values), updateSettings: () => Promise.resolve(), requestGraphQL: () => EMPTY, - getScriptURLForExtension: () => undefined, clientApplication: 'other', } @@ -193,18 +169,12 @@ describe('MainThreadAPI', () => { const values = new Subject>() const platformContext: Pick< PlatformContext, - | 'updateSettings' - | 'settings' - | 'getGraphQLClient' - | 'requestGraphQL' - | 'getScriptURLForExtension' - | 'clientApplication' + 'updateSettings' | 'settings' | 'getGraphQLClient' | 'requestGraphQL' | 'clientApplication' > = { settings: values.asObservable(), updateSettings: () => Promise.resolve(), getGraphQLClient, requestGraphQL: () => EMPTY, - getScriptURLForExtension: () => undefined, clientApplication: 'other', } const passedToExtensionHost: SettingsCascade[] = [] diff --git a/client/shared/src/api/client/mainthread-api.ts b/client/shared/src/api/client/mainthread-api.ts index f74451323b7..e88e3d19200 100644 --- a/client/shared/src/api/client/mainthread-api.ts +++ b/client/shared/src/api/client/mainthread-api.ts @@ -13,7 +13,6 @@ import { proxySubscribable } from '../extension/api/common' import { NotificationType, PlainNotification } from '../extension/extensionHostApi' import { ProxySubscription } from './api/common' -import { getEnabledExtensions } from './enabledExtensions' import { updateSettings } from './services/settings' /** A registered command in the command registry. */ @@ -65,7 +64,6 @@ export const initMainThreadAPI = ( | 'requestGraphQL' | 'showMessage' | 'showInputBox' - | 'getScriptURLForExtension' | 'getStaticExtensions' | 'telemetryService' | 'clientApplication' @@ -149,14 +147,6 @@ export const initMainThreadAPI = ( showInputBox: options => platformContext.showInputBox ? platformContext.showInputBox(options) : defaultShowInputBox(options), - getScriptURLForExtension: () => { - const getScriptURL = platformContext.getScriptURLForExtension() - if (!getScriptURL) { - return undefined - } - - return proxy(getScriptURL) - }, getEnabledExtensions: () => { if (platformContext.getStaticExtensions) { return proxySubscribable( @@ -164,15 +154,13 @@ export const initMainThreadAPI = ( .getStaticExtensions() .pipe( switchMap(staticExtensions => - staticExtensions - ? of(staticExtensions).pipe(publishReplay(1), refCount()) - : getEnabledExtensions(platformContext) + staticExtensions ? of(staticExtensions).pipe(publishReplay(1), refCount()) : of([]) ) ) ) } - return proxySubscribable(getEnabledExtensions(platformContext)) + return proxySubscribable(of([])) }, logEvent: (eventName, eventProperties) => platformContext.telemetryService?.log(eventName, eventProperties), logExtensionMessage: (...data) => logger.log(...data), diff --git a/client/shared/src/api/client/search.ts b/client/shared/src/api/client/search.ts index 598f75b4f1a..ce02b1fd8a4 100644 --- a/client/shared/src/api/client/search.ts +++ b/client/shared/src/api/client/search.ts @@ -1,29 +1,18 @@ // For search-related extension API features, such as query transformers -import { Remote } from 'comlink' -import { from, Observable, of, TimeoutError } from 'rxjs' -import { catchError, filter, first, switchMap, timeout } from 'rxjs/operators' +import { Observable, of } from 'rxjs' -import { logger } from '@sourcegraph/common' - -import { FlatExtensionHostAPI } from '../contract' import { SharedEventLogger } from '../sharedEventLogger' -import { wrapRemoteObservable } from './api/common' - -const TRANSFORM_QUERY_TIMEOUT = 3000 - /** * Executes search query transformers contributed by Sourcegraph extensions. */ export function transformSearchQuery({ query, - extensionHostAPIPromise, enableGoImportsSearchQueryTransform, eventLogger, }: { query: string - extensionHostAPIPromise: null | Promise> enableGoImportsSearchQueryTransform: undefined | boolean eventLogger: SharedEventLogger }): Observable { @@ -33,37 +22,7 @@ export function transformSearchQuery({ query = goImportsTransform(query, eventLogger) } - if (extensionHostAPIPromise === null) { - return of(query) - } - - return from(extensionHostAPIPromise).pipe( - switchMap(extensionHostAPI => - // Since we won't re-compute on subsequent extension activation, ensure that - // at least the initial set of extensions, which should include always-activated - // query-transforming extensions, have been loaded to ensure that the initial - // search query is transformed - wrapRemoteObservable(extensionHostAPI.haveInitialExtensionsLoaded()).pipe( - filter(haveLoaded => haveLoaded), - first(), // Ensure that it only emits once - switchMap(() => - wrapRemoteObservable(extensionHostAPI.transformSearchQuery(query)).pipe( - first() // Ensure that it only emits once - ) - ) - ) - ), - // Timeout: if this is hanging due to any sort of extension bug, it may not result in a thrown error, - // but will degrade search UX. - // Wait up to 5 seconds and log to console for users to debug slow query transformer extensions - timeout(TRANSFORM_QUERY_TIMEOUT), - catchError(error => { - if (error instanceof TimeoutError) { - logger.error(`Extension query transformers took more than ${TRANSFORM_QUERY_TIMEOUT}ms`) - } - return of(query) - }) - ) + return of(query) } function goImportsTransform(query: string, eventLogger: SharedEventLogger): string { diff --git a/client/shared/src/api/contract.ts b/client/shared/src/api/contract.ts index 3b61f124741..384a3a850d6 100644 --- a/client/shared/src/api/contract.ts +++ b/client/shared/src/api/contract.ts @@ -4,7 +4,7 @@ import { DocumentHighlight } from 'sourcegraph' import { Contributions, Evaluated, Raw, TextDocumentPositionParameters, HoverMerged } from '@sourcegraph/client-api' import { MaybeLoadingResult } from '@sourcegraph/codeintellify' -import { DeepReplace, ErrorLike } from '@sourcegraph/common' +import { DeepReplace } from '@sourcegraph/common' import * as clientType from '@sourcegraph/extension-api-types' import { GraphQLResult } from '@sourcegraph/http-client' @@ -196,10 +196,6 @@ export interface MainThreadAPI { showMessage: (message: string) => Promise showInputBox: (options?: InputBoxOptions) => Promise - getScriptURLForExtension: () => - | undefined - | (((bundleURLs: string[]) => Promise<(string | ErrorLike)[]>) & ProxyMarked) - getEnabledExtensions: () => ProxySubscribable<(ConfiguredExtension | ExecutableExtension)[]> /** diff --git a/client/shared/src/api/extension/activation.ts b/client/shared/src/api/extension/activation.ts index 181d682250b..21c7c5e8629 100644 --- a/client/shared/src/api/extension/activation.ts +++ b/client/shared/src/api/extension/activation.ts @@ -1,10 +1,10 @@ import { Remote } from 'comlink' -import { BehaviorSubject, combineLatest, from, Observable, Subscription } from 'rxjs' +import { BehaviorSubject, combineLatest, from, Observable, of, Subscription } from 'rxjs' import { catchError, concatMap, distinctUntilChanged, first, map, switchMap, tap } from 'rxjs/operators' import sourcegraph from 'sourcegraph' import { Contributions } from '@sourcegraph/client-api' -import { asError, ErrorLike, isErrorLike, hashCode, memoizeObservable, logger } from '@sourcegraph/common' +import { asError, isErrorLike, hashCode, logger } from '@sourcegraph/common' import { ConfiguredExtension, getScriptURLFromExtensionManifest, splitExtensionID } from '../../extensions/extension' import { areExtensionsSame, getEnabledExtensionsForSubject } from '../../extensions/extensions' @@ -62,7 +62,7 @@ const DEPRECATED_EXTENSION_IDS = new Set(['sourcegraph/code-stats-insights', 'so export function activateExtensions( state: Pick, - mainAPI: Remote>, + mainAPI: Remote>, createExtensionAPI: (extensionID: string) => typeof sourcegraph, mainThreadAPIInitializations: Observable, /** @@ -76,31 +76,12 @@ export function activateExtensions( * */ deactivate = deactivateExtension ): Subscription { - const getScriptURLs = memoizeObservable( - () => - mainThreadAPIInitializations.pipe( - first(initialized => initialized), - switchMap(() => - from(mainAPI.getScriptURLForExtension()).pipe( - map(getScriptURL => { - function getBundleURLs(urls: string[]): Promise<(string | ErrorLike)[]> { - return getScriptURL ? getScriptURL(urls) : Promise.resolve(urls) - } - - return getBundleURLs - }) - ) - ) - ), - () => 'getScriptURL' - ) - const previouslyActivatedExtensions = new Set() const extensionContributions = new Map() const contributionsToAdd = new Map() - const extensionsSubscription = combineLatest([state.activeExtensions, getScriptURLs(null)]) + const extensionsSubscription = combineLatest([state.activeExtensions]) .pipe( - concatMap(([activeExtensions, getScriptURLs]) => { + concatMap(([activeExtensions]) => { const toDeactivate = new Set() const toActivate = new Map() const activeExtensionIDs = new Set() @@ -126,32 +107,24 @@ export function activateExtensions( } } - return from( - getScriptURLs( - [...toActivate.values()].map(extension => { - if ('scriptURL' in extension) { - // This is already an executable extension (inline extension) - return extension.scriptURL - } + const scriptURLs = [...toActivate.values()].map(extension => { + if ('scriptURL' in extension) { + // This is already an executable extension (inline extension) + return extension.scriptURL + } - return getScriptURLFromExtensionManifest(extension) - }) - ).then(scriptURLs => { - // TODO: (not urgent) add scriptURL cache + return getScriptURLFromExtensionManifest(extension) + }) - const executableExtensionsToActivate: ExecutableExtension[] = [...toActivate.values()] - .map((extension, index) => ({ - id: extension.id, - manifest: extension.manifest, - scriptURL: scriptURLs[index], - })) - .filter( - (extension): extension is ExecutableExtension => typeof extension.scriptURL === 'string' - ) + const executableExtensionsToActivate: ExecutableExtension[] = [...toActivate.values()] + .map((extension, index) => ({ + id: extension.id, + manifest: extension.manifest, + scriptURL: scriptURLs[index], + })) + .filter((extension): extension is ExecutableExtension => typeof extension.scriptURL === 'string') - return { toActivate: executableExtensionsToActivate, toDeactivate } - }) - ).pipe( + return of({ toActivate: executableExtensionsToActivate, toDeactivate }).pipe( tap(({ toActivate }) => { for (const extension of toActivate) { if ( diff --git a/client/shared/src/api/extension/test/activation.test.ts b/client/shared/src/api/extension/test/activation.test.ts index 67ab0b7a949..3d78ad8a5d8 100644 --- a/client/shared/src/api/extension/test/activation.test.ts +++ b/client/shared/src/api/extension/test/activation.test.ts @@ -16,8 +16,7 @@ describe('Extension activation', () => { it('logs events for activated extensions', async () => { const logEvent = sinon.spy() - const mockMain = pretendRemote>({ - getScriptURLForExtension: () => undefined, + const mockMain = pretendRemote>({ logEvent, }) diff --git a/client/shared/src/api/extension/test/extensionHost.configuration.test.ts b/client/shared/src/api/extension/test/extensionHost.configuration.test.ts index 67960021c50..c3eaf9d4bc6 100644 --- a/client/shared/src/api/extension/test/extensionHost.configuration.test.ts +++ b/client/shared/src/api/extension/test/extensionHost.configuration.test.ts @@ -1,4 +1,3 @@ -import { proxy } from 'comlink' import { BehaviorSubject } from 'rxjs' import { SettingsCascade } from '../../../settings/settings' @@ -24,7 +23,6 @@ describe('ExtensionHost: Configuration', () => { sourcegraphURL: 'https://example.com/', }, pretendRemote({ - getScriptURLForExtension: proxy(() => undefined), getEnabledExtensions: () => proxySubscribable(new BehaviorSubject([])), }) ) @@ -44,7 +42,6 @@ describe('ExtensionHost: Configuration', () => { sourcegraphURL: 'https://example.com/', }, pretendRemote({ - getScriptURLForExtension: proxy(() => undefined), getEnabledExtensions: () => proxySubscribable(new BehaviorSubject([])), }) ) @@ -62,7 +59,6 @@ describe('ExtensionHost: Configuration', () => { sourcegraphURL: 'https://example.com/', }, pretendRemote({ - getScriptURLForExtension: proxy(() => undefined), getEnabledExtensions: () => proxySubscribable(new BehaviorSubject([])), }) ) @@ -82,7 +78,6 @@ describe('ExtensionHost: Configuration', () => { sourcegraphURL: 'https://example.com/', }, pretendRemote({ - getScriptURLForExtension: proxy(() => undefined), getEnabledExtensions: () => proxySubscribable(new BehaviorSubject([])), }) ) @@ -105,7 +100,6 @@ describe('ExtensionHost: Configuration', () => { sourcegraphURL: 'https://example.com/', }, pretendRemote({ - getScriptURLForExtension: proxy(() => undefined), getEnabledExtensions: () => proxySubscribable(new BehaviorSubject([])), applySettingsEdit: edit => Promise.resolve().then(() => { diff --git a/client/shared/src/api/extension/test/extensionHost.documentHighlights.test.ts b/client/shared/src/api/extension/test/extensionHost.documentHighlights.test.ts index 30918a2e092..11b4c07a620 100644 --- a/client/shared/src/api/extension/test/extensionHost.documentHighlights.test.ts +++ b/client/shared/src/api/extension/test/extensionHost.documentHighlights.test.ts @@ -15,7 +15,6 @@ describe('getDocumentHighlights from ExtensionHost API, it aims to have more e2e // integration(ish) tests for scenarios not covered by providers tests const noopMain = pretendRemote({ getEnabledExtensions: () => proxySubscribable(new BehaviorSubject([])), - getScriptURLForExtension: () => undefined, }) const initialSettings: SettingsCascade = { subjects: [], diff --git a/client/shared/src/api/extension/test/extensionHost.hover.test.ts b/client/shared/src/api/extension/test/extensionHost.hover.test.ts index fc02491c09f..983dc122d39 100644 --- a/client/shared/src/api/extension/test/extensionHost.hover.test.ts +++ b/client/shared/src/api/extension/test/extensionHost.hover.test.ts @@ -16,7 +16,6 @@ describe('getHover from ExtensionHost API, it aims to have more e2e feel', () => // integration(ish) tests for scenarios not covered by providers tests const noopMain = pretendRemote({ getEnabledExtensions: () => pretendProxySubscribable(of([])), - getScriptURLForExtension: () => undefined, }) const initialSettings: SettingsCascade = { subjects: [], diff --git a/client/shared/src/api/extension/test/extensionHost.logging.test.ts b/client/shared/src/api/extension/test/extensionHost.logging.test.ts index aff997aae8c..8e192787bb1 100644 --- a/client/shared/src/api/extension/test/extensionHost.logging.test.ts +++ b/client/shared/src/api/extension/test/extensionHost.logging.test.ts @@ -11,7 +11,6 @@ import { initializeExtensionHostTest } from './test-helpers' const noopMain = pretendRemote({ getEnabledExtensions: () => proxySubscribable(new BehaviorSubject([])), - getScriptURLForExtension: () => undefined, logExtensionMessage: (...data) => logger.log(...data), }) diff --git a/client/shared/src/api/extension/test/extensionHost.search.test.ts b/client/shared/src/api/extension/test/extensionHost.search.test.ts index 9291e8841fa..c754afffd33 100644 --- a/client/shared/src/api/extension/test/extensionHost.search.test.ts +++ b/client/shared/src/api/extension/test/extensionHost.search.test.ts @@ -10,7 +10,6 @@ import { initializeExtensionHostTest } from './test-helpers' const noopMain = pretendRemote({ getEnabledExtensions: () => proxySubscribable(new BehaviorSubject([])), - getScriptURLForExtension: () => undefined, }) const initialSettings: SettingsCascade = { subjects: [], final: {} } diff --git a/client/shared/src/backend/apolloCache.ts b/client/shared/src/backend/apolloCache.ts index 913c9169dc6..587dc477b63 100644 --- a/client/shared/src/backend/apolloCache.ts +++ b/client/shared/src/backend/apolloCache.ts @@ -5,13 +5,6 @@ import { TypedTypePolicies } from '../graphql-operations' // Defines how the Apollo cache interacts with our GraphQL schema. // See https://www.apollographql.com/docs/react/caching/cache-configuration/#typepolicy-fields const typePolicies: TypedTypePolicies = { - ExtensionRegistry: { - // Replace existing `ExtensionRegistry` with the incoming value. - // Required because of the missing `id` on the `ExtensionRegistry` field. - merge(existing, incoming) { - return incoming - }, - }, Person: { // Replace existing `Person` with the incoming value. // Required because of the missing `id` on the `Person` field. diff --git a/client/shared/src/extensions/createLazyLoadedController.ts b/client/shared/src/extensions/createLazyLoadedController.ts deleted file mode 100644 index 0f741d5b9c7..00000000000 --- a/client/shared/src/extensions/createLazyLoadedController.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { from, Subscription } from 'rxjs' -import { switchMap } from 'rxjs/operators' - -import type { InitData } from '../api/extension/extensionHost' -import { syncPromiseSubscription } from '../api/util' -import type { PlatformContext } from '../platform/context' - -import type { Controller } from './controller' - -/** - * Creates the controller, which handles all communication between the client application and extensions. - * - * There should only be a single controller for the entire client application. The controller's model represents - * all of the client application state that the client needs to know. - * - * The implementation (`createExtensionHostClientConnection`) is lazy loaded to avoid adding bytes when - * the extension system is disabled - */ -export function createController( - context: Pick< - PlatformContext, - | 'updateSettings' - | 'settings' - | 'getGraphQLClient' - | 'requestGraphQL' - | 'showMessage' - | 'showInputBox' - | 'getScriptURLForExtension' - | 'getStaticExtensions' - | 'telemetryService' - | 'clientApplication' - | 'sourcegraphURL' - | 'createExtensionHost' - > -): Controller { - const subscriptions = new Subscription() - const initData: Omit = { - sourcegraphURL: context.sourcegraphURL, - clientApplication: context.clientApplication, - } - const extensionHostClientPromise = import('../api/client/connection').then(module => - module.createExtensionHostClientConnection(context.createExtensionHost(), initData, context) - ) - - subscriptions.add(() => extensionHostClientPromise.then(({ subscription }) => subscription.unsubscribe())) - - // TODO: Debug helpers, logging - - return { - executeCommand: (parameters, suppressNotificationOnError) => - extensionHostClientPromise.then(({ exposedToClient }) => - exposedToClient.executeCommand(parameters, suppressNotificationOnError) - ), - commandErrors: from(extensionHostClientPromise).pipe( - switchMap(({ exposedToClient }) => exposedToClient.commandErrors) - ), - registerCommand: entryToRegister => - syncPromiseSubscription( - extensionHostClientPromise.then(({ exposedToClient }) => - exposedToClient.registerCommand(entryToRegister) - ) - ), - extHostAPI: extensionHostClientPromise.then(({ api }) => api), - unsubscribe: () => subscriptions.unsubscribe(), - } -} diff --git a/client/shared/src/extensions/createSyncLoadedController.ts b/client/shared/src/extensions/createSyncLoadedController.ts index 2782c90599c..ee8466853c2 100644 --- a/client/shared/src/extensions/createSyncLoadedController.ts +++ b/client/shared/src/extensions/createSyncLoadedController.ts @@ -26,7 +26,6 @@ export function createController( | 'requestGraphQL' | 'showMessage' | 'showInputBox' - | 'getScriptURLForExtension' | 'getStaticExtensions' | 'telemetryService' | 'clientApplication' diff --git a/client/shared/src/extensions/extension.ts b/client/shared/src/extensions/extension.ts index 20f2ea235c6..29689b3eaab 100644 --- a/client/shared/src/extensions/extension.ts +++ b/client/shared/src/extensions/extension.ts @@ -8,7 +8,7 @@ import { ExtensionManifest } from './extensionManifest' * The default fields in the {@link ConfiguredExtension} manifest (i.e., the default value of the * `K` type parameter). */ -export const CONFIGURED_EXTENSION_DEFAULT_MANIFEST_FIELDS = ['contributes', 'activationEvents', 'url'] as const +const CONFIGURED_EXTENSION_DEFAULT_MANIFEST_FIELDS = ['contributes', 'activationEvents', 'url'] as const export type ConfiguredExtensionManifestDefaultFields = typeof CONFIGURED_EXTENSION_DEFAULT_MANIFEST_FIELDS[number] /** diff --git a/client/shared/src/extensions/helpers.test.ts b/client/shared/src/extensions/helpers.test.ts deleted file mode 100644 index 5c7da8c6eb0..00000000000 --- a/client/shared/src/extensions/helpers.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { createGraphQLClientGetter } from '../testing/apollo/createGraphQLClientGetter' - -import { ConfiguredExtension, ConfiguredExtensionManifestDefaultFields } from './extension' -import { ExtensionManifest } from './extensionManifest' -import { queryConfiguredRegistryExtensions } from './helpers' - -const TEST_MANIFEST: Pick = { - publisher: 'a', - url: 'https://example.com', - activationEvents: [], -} - -describe('queryConfiguredRegistryExtensions', () => { - it('gets extensions from GraphQL servers supporting extensions(extensionIDs)', done => { - const extensionsMock = { - data: { - extensionRegistry: { - extensions: { - nodes: [{ extensionID: 'a/b', manifest: { jsonFields: TEST_MANIFEST } }], - }, - }, - }, - } - - const getGraphQLClient = createGraphQLClientGetter({ watchQueryMocks: [extensionsMock] }) - - queryConfiguredRegistryExtensions({ getGraphQLClient }, ['a/b']).subscribe(data => { - expect(data).toEqual([{ id: 'a/b', manifest: TEST_MANIFEST }] as ConfiguredExtension[]) - done() - }) - }) -}) diff --git a/client/shared/src/extensions/helpers.ts b/client/shared/src/extensions/helpers.ts deleted file mode 100644 index 47b29cff154..00000000000 --- a/client/shared/src/extensions/helpers.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { Observable, of } from 'rxjs' -import { map } from 'rxjs/operators' - -import { createAggregateError } from '@sourcegraph/common' -import { fromObservableQueryPromise, getDocumentNode, gql } from '@sourcegraph/http-client' - -import { ExtensionsResult, ExtensionsVariables } from '../graphql-operations' -import { PlatformContext } from '../platform/context' - -import { - ConfiguredExtension, - ConfiguredExtensionManifestDefaultFields, - CONFIGURED_EXTENSION_DEFAULT_MANIFEST_FIELDS, -} from './extension' -import { ExtensionManifest } from './extensionManifest' - -const ExtensionsQuery = gql` - query Extensions($first: Int!, $extensionIDs: [String!]!, $extensionManifestFields: [String!]!) { - extensionRegistry { - extensions(first: $first, extensionIDs: $extensionIDs) { - nodes { - id - extensionID - manifest { - jsonFields(fields: $extensionManifestFields) - } - } - } - } - } -` - -/** - * Query the GraphQL API for registry metadata about the extensions given in {@link extensionIDs}. - * - * @returns An observable that emits once with the results. - */ -export function queryConfiguredRegistryExtensions( - // TODO(tj): can copy this over to extension host, just replace platformContext.requestGraphQL - // with mainThreadAPI.requestGraphQL - { getGraphQLClient }: Pick, - extensionIDs: string[] -): Observable { - if (extensionIDs.length === 0) { - return of([]) - } - - const queryObservablePromise = getGraphQLClient().then(client => - client.watchQuery({ - query: getDocumentNode(ExtensionsQuery), - variables: { - first: extensionIDs.length, - extensionIDs, - // Spread operator is required to avoid Typescript type error - // because of `readonly` type of `CONFIGURED_EXTENSION_DEFAULT_MANIFEST_FIELDS`. - extensionManifestFields: [...CONFIGURED_EXTENSION_DEFAULT_MANIFEST_FIELDS], - }, - }) - ) - - return fromObservableQueryPromise(queryObservablePromise).pipe( - map(({ data, errors }) => { - if (!data?.extensionRegistry?.extensions?.nodes) { - throw createAggregateError(errors) - } - - const { nodes } = data.extensionRegistry.extensions - - return nodes - .filter(({ extensionID }) => extensionIDs.includes(extensionID)) - .map(({ extensionID, manifest }) => { - const getManifest = (value: typeof manifest): ConfiguredExtension['manifest'] => { - if (!value) { - return value - } - - return value.jsonFields as Pick - } - - return { - id: extensionID, - manifest: getManifest(manifest), - } - }) - }) - ) -} diff --git a/client/shared/src/hover/actions.ts b/client/shared/src/hover/actions.ts index b88d28c9d25..7b9462bbf46 100644 --- a/client/shared/src/hover/actions.ts +++ b/client/shared/src/hover/actions.ts @@ -488,47 +488,45 @@ export function registerHoverContributions({ subscriptions.add(syncRemoteSubscription(referencesContributionPromise)) let implementationsContributionPromise: Promise = Promise.resolve() - if (window.context?.enableLegacyExtensions === false) { - const promise = extensionHostAPI.registerContributions({ - actions: [ - ...languageSpecs.map(spec => ({ - actionItem: { label: 'Find implementations' }, - command: 'open', - commandArguments: [ - "${get(context, 'implementations_" + - spec.languageID + - "') && get(context, 'panel.url') && sub(get(context, 'panel.url'), 'panelID', 'implementations_" + - spec.languageID + - "') || 'noop'}", - ], - id: 'findImplementations_' + spec.languageID, - title: 'Find implementations', - })), - ], - menus: { - hover: languageSpecs.map(spec => ({ - action: 'findImplementations_' + spec.languageID, - when: - "resource.language == '" + + const promise = extensionHostAPI.registerContributions({ + actions: [ + ...languageSpecs.map(spec => ({ + actionItem: { label: 'Find implementations' }, + command: 'open', + commandArguments: [ + "${get(context, 'implementations_" + spec.languageID + - // eslint-disable-next-line no-template-curly-in-string - "' && get(context, `implementations_${resource.language}`) && (goToDefinition.showLoading || goToDefinition.url || goToDefinition.error)", - })), - }, - }) - implementationsContributionPromise = promise - subscriptions.add(syncRemoteSubscription(promise)) - for (const spec of languageSpecs) { - if (spec.textDocumentImplemenationSupport) { - extensionHostAPI - .updateContext({ - [`implementations_${spec.languageID}`]: true, - }) - .then( - () => {}, - () => {} - ) - } + "') && get(context, 'panel.url') && sub(get(context, 'panel.url'), 'panelID', 'implementations_" + + spec.languageID + + "') || 'noop'}", + ], + id: 'findImplementations_' + spec.languageID, + title: 'Find implementations', + })), + ], + menus: { + hover: languageSpecs.map(spec => ({ + action: 'findImplementations_' + spec.languageID, + when: + "resource.language == '" + + spec.languageID + + // eslint-disable-next-line no-template-curly-in-string + "' && get(context, `implementations_${resource.language}`) && (goToDefinition.showLoading || goToDefinition.url || goToDefinition.error)", + })), + }, + }) + implementationsContributionPromise = promise + subscriptions.add(syncRemoteSubscription(promise)) + for (const spec of languageSpecs) { + if (spec.textDocumentImplemenationSupport) { + extensionHostAPI + .updateContext({ + [`implementations_${spec.languageID}`]: true, + }) + .then( + () => {}, + () => {} + ) } } diff --git a/client/shared/src/notifications/NotificationItem.tsx b/client/shared/src/notifications/NotificationItem.tsx index 21ae1a094d1..8af3a6856cd 100644 --- a/client/shared/src/notifications/NotificationItem.tsx +++ b/client/shared/src/notifications/NotificationItem.tsx @@ -5,7 +5,7 @@ import { from, Subject, Subscription } from 'rxjs' import { catchError, distinctUntilChanged, map, scan, switchMap } from 'rxjs/operators' import { renderMarkdown } from '@sourcegraph/common' -import { Alert, AlertProps } from '@sourcegraph/wildcard' +import { Alert } from '@sourcegraph/wildcard' import type { NotificationType, Progress } from '../codeintel/legacy-extensions/api' @@ -17,21 +17,11 @@ export interface UnbrandedNotificationItemStyleProps { notificationItemClassNames: Record } -export interface BrandedNotificationItemStyleProps { - notificationItemVariants: Record -} - -/** - * Note, we do not export this type because it is not intended to be used directly. - * Consumers should use either `UnbrandedNotificationItemStyleProps` or `BrandedNotificationItemStyleProps` when configuring this component. - */ -type NotificationItemStyleProps = UnbrandedNotificationItemStyleProps | BrandedNotificationItemStyleProps - export interface NotificationItemProps { notification: Notification onDismiss: (notification: Notification) => void className?: string - notificationItemStyleProps: NotificationItemStyleProps + notificationItemStyleProps: UnbrandedNotificationItemStyleProps } interface NotificationItemState { @@ -93,18 +83,12 @@ export class NotificationItem extends React.PureComponent diff --git a/client/shared/src/platform/context.ts b/client/shared/src/platform/context.ts index a5d3e591acd..eb2f07bce5d 100644 --- a/client/shared/src/platform/context.ts +++ b/client/shared/src/platform/context.ts @@ -3,7 +3,7 @@ import { isObject } from 'lodash' import { Observable, Subscribable, Subscription } from 'rxjs' import { DiffPart } from '@sourcegraph/codeintellify' -import { ErrorLike, hasProperty } from '@sourcegraph/common' +import { hasProperty } from '@sourcegraph/common' import { GraphQLClient, GraphQLResult } from '@sourcegraph/http-client' import { SettingsEdit } from '../api/client/services/settings' @@ -130,22 +130,6 @@ export interface PlatformContext { */ createExtensionHost: () => Promise - /** - * Returns the script URL suitable for passing to importScripts for an extension's bundle. - * - * This is necessary because some platforms (such as Chrome extensions) use a script-src CSP - * that would prevent loading bundles from arbitrary URLs, which requires us to pass blob: URIs - * to importScripts. - * - * @param bundleURL The URL to the JavaScript bundle file specified in the extension manifest. - * @returns A script URL suitable for passing to importScripts, typically either the original - * https:// URL for the extension's bundle or a blob: URI for it. - * - * TODO(tj): If this doesn't return a getScriptURLForExtension function, the original bundleURL will be used. - * Also, make getScriptURL batched to minimize round trips between extension host and client application - */ - getScriptURLForExtension: () => undefined | ((bundleURL: string[]) => Promise<(string | ErrorLike)[]>) - /** * Constructs the URL (possibly relative or absolute) to the file with the specified options. * diff --git a/client/shared/src/testing/integration/mockExtension.ts b/client/shared/src/testing/integration/mockExtension.ts index 520bb72ac2c..5cdc80769e5 100644 --- a/client/shared/src/testing/integration/mockExtension.ts +++ b/client/shared/src/testing/integration/mockExtension.ts @@ -1,20 +1,4 @@ -import { PollyServer } from '@pollyjs/core' - import type { ExtensionContext } from '../../codeintel/legacy-extensions/api' -import { ExtensionManifest } from '../../extensions/extensionManifest' -import { ExtensionsResult, SharedGraphQlOperations } from '../../graphql-operations' -import { Settings } from '../../settings/settings' - -interface ExtensionMockingInit { - /** - * The polly server object, used to intercept extension bundle requests. - */ - pollyServer: PollyServer - /** - * The base Sourcegraph URL for the test instance, used to construst bundle URL. - */ - sourcegraphBaseUrl: string -} interface ExtensionMockingUtils { /** @@ -23,75 +7,17 @@ interface ExtensionMockingUtils { * and exports an `activate` function, just like any other Sourcegraph extension. */ mockExtension: ({ id, bundle }: { id: string; bundle: () => void }) => void - /** - * Use this as the `Extension` override for `TestContext#overrideGraphQL`. - */ - Extensions: SharedGraphQlOperations['Extensions'] /** * Merge/replace your mock settings `extensions` property with this object. */ - extensionSettings: Settings['extensions'] -} - -interface ExtensionsResultMock { - extensionRegistry: ExtensionsResult['extensionRegistry'] & { - __typename: 'ExtensionRegistry' - } + extensionSettings: {} } /** * Set up Sourcegraph extension mocking for an integration test. */ -export function setupExtensionMocking({ - pollyServer, - sourcegraphBaseUrl, -}: ExtensionMockingInit): ExtensionMockingUtils { - let internalID = 0 - - const extensionSettings: Settings['extensions'] = {} - const extensionsResult: ExtensionsResultMock = { - extensionRegistry: { - __typename: 'ExtensionRegistry', - extensions: { - nodes: [], - }, - }, - } - - return { - mockExtension: ({ id, bundle }) => { - internalID++ - - /** The URL at which the manifest says the extension bundle is served. We should intercept requests to this URL. */ - const bundleURL = new URL( - `/-/static/extension/00${internalID}-${id.replace(/\//g, '-')}.js?hash--${id.replace(/\//g, '-')}`, - sourcegraphBaseUrl - ).href - - const extensionManifest: ExtensionManifest = { - url: bundleURL, - activationEvents: ['*'], - } - - // Mutate mock data objects - extensionSettings[id] = true - extensionsResult.extensionRegistry.extensions.nodes.push({ - id, - extensionID: id, - manifest: { - jsonFields: extensionManifest, - }, - }) - - pollyServer.get(bundleURL).intercept((request, response) => { - // Create an immediately-invoked function expression for the extensionBundle function - const extensionBundleString = `(${bundle.toString()})()` - response.type('application/javascript; charset=utf-8').send(extensionBundleString) - }) - }, - Extensions: () => extensionsResult, - extensionSettings, - } +export function setupExtensionMocking(): ExtensionMockingUtils { + throw new Error('not yet reimplemented after the extension API deprecation') } // Commonly mocked extensions. diff --git a/client/shared/src/testing/testHelpers.ts b/client/shared/src/testing/testHelpers.ts index c5d5d24094d..c5bcb03f15c 100644 --- a/client/shared/src/testing/testHelpers.ts +++ b/client/shared/src/testing/testHelpers.ts @@ -44,7 +44,6 @@ interface Mocks | 'updateSettings' | 'getGraphQLClient' | 'requestGraphQL' - | 'getScriptURLForExtension' | 'clientApplication' | 'showMessage' | 'showInputBox' @@ -55,7 +54,6 @@ const NOOP_MOCKS: Mocks = { updateSettings: () => Promise.reject(new Error('Mocks#updateSettings not implemented')), getGraphQLClient: () => Promise.reject(new Error('Mocks#getGraphQLClient not implemented')), requestGraphQL: () => throwError(new Error('Mocks#queryGraphQL not implemented')), - getScriptURLForExtension: () => undefined, clientApplication: 'sourcegraph', } diff --git a/client/vscode/src/webview/platform/context.ts b/client/vscode/src/webview/platform/context.ts index edce6ef1d8e..73ee3c09ba4 100644 --- a/client/vscode/src/webview/platform/context.ts +++ b/client/vscode/src/webview/platform/context.ts @@ -26,7 +26,6 @@ export interface VSCodePlatformContext | 'getGraphQLClient' | 'showMessage' | 'showInputBox' - | 'getScriptURLForExtension' | 'getStaticExtensions' | 'telemetryService' | 'clientApplication' @@ -60,7 +59,6 @@ export function createPlatformContext(extensionCoreAPI: Comlink.Remote Promise.resolve(), telemetryService: new EventLogger(extensionCoreAPI), clientApplication: 'other', // TODO add 'vscode-extension' to `clientApplication`, - getScriptURLForExtension: () => undefined, // TODO showInputBox // TODO showMessage getStaticExtensions: () => getInlineExtensions(), diff --git a/client/vscode/tests/vsce.test.ts b/client/vscode/tests/vsce.test.ts index 888f39cd629..c9e03aca9cd 100644 --- a/client/vscode/tests/vsce.test.ts +++ b/client/vscode/tests/vsce.test.ts @@ -2,14 +2,11 @@ import { downloadAndUnzipVSCode } from '@vscode/test-electron' import { mixedSearchStreamEvents, highlightFileResult } from '@sourcegraph/shared/src/search/integration' import { Settings } from '@sourcegraph/shared/src/settings/settings' -import { setupExtensionMocking } from '@sourcegraph/shared/src/testing/integration/mockExtension' import { createVSCodeIntegrationTestContext, VSCodeIntegrationTestContext } from './context' import { getVSCodeWebviewFrames } from './getWebview' import { launchVsCode, VSCodeTestDriver } from './launch' -const sourcegraphBaseUrl = 'https://sourcegraph.com' - describe('VS Code extension', () => { let vsCodeDriver: VSCodeTestDriver before(async () => { @@ -39,18 +36,11 @@ describe('VS Code extension', () => { // fixing before we add more test cases to the suite. it('works', async () => { - const { Extensions } = setupExtensionMocking({ - pollyServer: testContext.server, - sourcegraphBaseUrl, - }) - const userSettings: Settings = { extensions: {}, } testContext.overrideGraphQL({ - Extensions, - ...highlightFileResult, ViewerSettings: () => ({ viewerSettings: { diff --git a/client/web/dev/utils/create-js-context.ts b/client/web/dev/utils/create-js-context.ts index 89a5b28916b..6e8aa0efe12 100644 --- a/client/web/dev/utils/create-js-context.ts +++ b/client/web/dev/utils/create-js-context.ts @@ -67,7 +67,6 @@ export const createJsContext = ({ sourcegraphBaseUrl }: { sourcegraphBaseUrl: st openTelemetry: { endpoint: ENVIRONMENT_CONFIG.CLIENT_OTEL_EXPORTER_OTLP_ENDPOINT, }, - enableLegacyExtensions: false, // Site-config overrides default JS context ...siteConfig, } diff --git a/client/web/dev/utils/get-api-proxy-settings.ts b/client/web/dev/utils/get-api-proxy-settings.ts index d4741e26aa5..f98ef9cc49a 100644 --- a/client/web/dev/utils/get-api-proxy-settings.ts +++ b/client/web/dev/utils/get-api-proxy-settings.ts @@ -85,9 +85,6 @@ const jsContextChanges = ` // Only username/password auth-provider provider is supported with the standalone server. authProviders: window.context.authProviders.filter(provider => provider.isBuiltin), - // For some reason, the standalone server crashes with legacy extensions enabled. - enableLegacyExtensions: false, - // Sync externalURL with the development environment config. externalURL: '${HTTPS_WEB_SERVER_URL}', diff --git a/client/web/src/Layout.tsx b/client/web/src/Layout.tsx index 79cbf5c8a1b..e6fbc79a3d3 100644 --- a/client/web/src/Layout.tsx +++ b/client/web/src/Layout.tsx @@ -197,7 +197,6 @@ export const Layout: React.FC = props => { isRepositoryRelatedPage={isRepositoryRelatedPage} showKeyboardShortcutsHelp={showKeyboardShortcutsHelp} showFeedbackModal={showFeedbackModal} - enableLegacyExtensions={window.context.enableLegacyExtensions} /> )} {needsSiteInit && !isSiteInit && } diff --git a/client/web/src/LegacyLayout.tsx b/client/web/src/LegacyLayout.tsx index 6fb8b7ba6ad..7e414a63640 100644 --- a/client/web/src/LegacyLayout.tsx +++ b/client/web/src/LegacyLayout.tsx @@ -189,7 +189,6 @@ export const LegacyLayout: FC = props => { isRepositoryRelatedPage={isRepositoryRelatedPage} showKeyboardShortcutsHelp={showKeyboardShortcutsHelp} showFeedbackModal={showFeedbackModal} - enableLegacyExtensions={window.context.enableLegacyExtensions} /> )} {needsSiteInit && !isSiteInit && } diff --git a/client/web/src/LegacySourcegraphWebApp.tsx b/client/web/src/LegacySourcegraphWebApp.tsx index 642e5f3fb18..9bc8a2e9f72 100644 --- a/client/web/src/LegacySourcegraphWebApp.tsx +++ b/client/web/src/LegacySourcegraphWebApp.tsx @@ -10,14 +10,10 @@ import { combineLatest, from, Subscription, fromEvent, Observable } from 'rxjs' import { logger } from '@sourcegraph/common' import { GraphQLClient, HTTPStatusError } from '@sourcegraph/http-client' import { SharedSpanName, TraceSpanProvider } from '@sourcegraph/observability-client' -import { NotificationType } from '@sourcegraph/shared/src/api/extension/extensionHostApi' import { FetchFileParameters, fetchHighlightedFileLineRanges } from '@sourcegraph/shared/src/backend/file' import { setCodeIntelSearchContext } from '@sourcegraph/shared/src/codeintel/searchContext' import { Controller as ExtensionsController } from '@sourcegraph/shared/src/extensions/controller' -import { createController as createExtensionsController } from '@sourcegraph/shared/src/extensions/createLazyLoadedController' import { createNoopController } from '@sourcegraph/shared/src/extensions/createNoopLoadedController' -import { BrandedNotificationItemStyleProps } from '@sourcegraph/shared/src/notifications/NotificationItem' -import { Notifications } from '@sourcegraph/shared/src/notifications/Notifications' import { PlatformContext } from '@sourcegraph/shared/src/platform/context' import { ShortcutProvider } from '@sourcegraph/shared/src/react-shortcuts' import { @@ -91,16 +87,6 @@ interface LegacySourcegraphWebAppState extends SettingsCascadeProps { globbing: boolean } -const notificationStyles: BrandedNotificationItemStyleProps = { - notificationItemVariants: { - [NotificationType.Log]: 'secondary', - [NotificationType.Success]: 'success', - [NotificationType.Info]: 'info', - [NotificationType.Warning]: 'warning', - [NotificationType.Error]: 'danger', - }, -} - const WILDCARD_THEME: WildcardTheme = { isBranded: true, } @@ -113,9 +99,7 @@ setLinkComponent(RouterLink) export class LegacySourcegraphWebApp extends React.Component { private readonly subscriptions = new Subscription() private readonly platformContext: PlatformContext = createPlatformContext() - private readonly extensionsController: ExtensionsController | null = window.context.enableLegacyExtensions - ? createExtensionsController(this.platformContext) - : createNoopController(this.platformContext) + private readonly extensionsController: ExtensionsController | null = createNoopController(this.platformContext) constructor(props: StaticAppConfig) { super(props) @@ -304,13 +288,6 @@ export class LegacySourcegraphWebApp extends React.Component - {this.extensionsController !== null && window.context.enableLegacyExtensions ? ( - - ) : null} ) diff --git a/client/web/src/backend/persistenceMapper.test.ts b/client/web/src/backend/persistenceMapper.test.ts index 22ee9b145bc..d35d890932d 100644 --- a/client/web/src/backend/persistenceMapper.test.ts +++ b/client/web/src/backend/persistenceMapper.test.ts @@ -25,7 +25,6 @@ describe('persistenceMapper', () => { const persistedString = await persistenceMapper( createStringifiedCache({ viewerSettings: { empty: null, data: true }, - extensionRegistry: { data: true }, shouldNotBePersisted: {}, }) ) diff --git a/client/web/src/backend/persistenceMapper.ts b/client/web/src/backend/persistenceMapper.ts index 3636be5883f..c45d2ec504c 100644 --- a/client/web/src/backend/persistenceMapper.ts +++ b/client/web/src/backend/persistenceMapper.ts @@ -5,11 +5,7 @@ import { QueryFieldPolicy } from '@sourcegraph/shared/src/graphql-operations' * After the implementation of the `persistLink` which will support `@persist` directive * hardcoded query names will be deprecated. */ -export const QUERIES_TO_PERSIST: (keyof QueryFieldPolicy)[] = [ - 'viewerSettings', - 'extensionRegistry', - 'temporarySettings', -] +export const QUERIES_TO_PERSIST: (keyof QueryFieldPolicy)[] = ['viewerSettings', 'temporarySettings'] export const ROOT_QUERY_KEY = 'ROOT_QUERY' export interface CacheReference { diff --git a/client/web/src/components/WebCommandListPopoverButton/WebCommandListPopoverButton.module.scss b/client/web/src/components/WebCommandListPopoverButton/WebCommandListPopoverButton.module.scss deleted file mode 100644 index b8eae38c7ee..00000000000 --- a/client/web/src/components/WebCommandListPopoverButton/WebCommandListPopoverButton.module.scss +++ /dev/null @@ -1,21 +0,0 @@ -.button { - color: var(--icon-color); - display: flex; - align-items: center; - - &:hover { - // Required to overwrite .btn:hover styles with greater specificity. - color: var(--body-color) !important; - } -} - -.action-item { - --link-color: var(--body-color); - --link-hover-color: var(--body-color); - - display: block; - text-align: left; - - /* Overwrite `ButtonLink` style */ - text-decoration: none !important; -} diff --git a/client/web/src/components/WebCommandListPopoverButton/WebCommandListPopoverButton.tsx b/client/web/src/components/WebCommandListPopoverButton/WebCommandListPopoverButton.tsx deleted file mode 100644 index 28ca18e43f3..00000000000 --- a/client/web/src/components/WebCommandListPopoverButton/WebCommandListPopoverButton.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import React from 'react' - -import classNames from 'classnames' - -import { - CommandListPopoverButton, - CommandListPopoverButtonProps, -} from '@sourcegraph/shared/src/commandPalette/CommandList' -import { useKeyboardShortcut } from '@sourcegraph/shared/src/keyboardShortcuts/useKeyboardShortcut' -import { Button } from '@sourcegraph/wildcard' - -import styles from './WebCommandListPopoverButton.module.scss' - -export const WebCommandListPopoverButton: React.FunctionComponent< - React.PropsWithChildren -> = props => { - const showCommandPaletteShortcut = useKeyboardShortcut('commandPalette') - - return ( - - ) -} - -WebCommandListPopoverButton.displayName = 'WebCommandListPopoverButton' diff --git a/client/web/src/components/WebCommandListPopoverButton/index.ts b/client/web/src/components/WebCommandListPopoverButton/index.ts deleted file mode 100644 index 22fc6728db4..00000000000 --- a/client/web/src/components/WebCommandListPopoverButton/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './WebCommandListPopoverButton' diff --git a/client/web/src/components/shared.tsx b/client/web/src/components/shared.tsx index 95b9486ec6d..8ccd94c327e 100644 --- a/client/web/src/components/shared.tsx +++ b/client/web/src/components/shared.tsx @@ -1,3 +1,2 @@ // Components from shared with web-styling class names applied export { WebHoverOverlay } from './WebHoverOverlay' -export { WebCommandListPopoverButton } from './WebCommandListPopoverButton' diff --git a/client/web/src/components/useCarousel.ts b/client/web/src/components/useCarousel.ts deleted file mode 100644 index 814f7433a0f..00000000000 --- a/client/web/src/components/useCarousel.ts +++ /dev/null @@ -1,129 +0,0 @@ -import { useCallback, useEffect, useRef, useState } from 'react' - -import { isEqual } from 'lodash' -import { Subscription } from 'rxjs' - -import { observeResize } from '@sourcegraph/common' - -interface CarouselOptions { - amountToScroll?: number - direction: CarouselDirection -} - -type CarouselDirection = 'leftToRight' | 'topToBottom' - -interface CarouselState { - canScrollNegative: boolean - canScrollPositive: boolean - onNegativeClicked: () => void - onPositiveClicked: () => void - carouselReference: React.RefCallback -} - -const defaultCarouselState = { canScrollNegative: false, canScrollPositive: false } - -const carouselScrollHandlers: Record< - CarouselDirection, - (carousel: HTMLElement) => Pick -> = { - leftToRight: carousel => ({ - canScrollNegative: carousel.scrollLeft > 0, - canScrollPositive: carousel.scrollLeft + carousel.clientWidth < carousel.scrollWidth, - }), - topToBottom: carousel => ({ - canScrollNegative: carousel.scrollTop > 0, - canScrollPositive: carousel.scrollTop + carousel.clientHeight < carousel.scrollHeight, - }), -} - -const carouselClickHandlers: Record< - CarouselDirection, - (options: { carousel: HTMLElement; amountToScroll: number; sign: 'positive' | 'negative' }) => void -> = { - leftToRight: ({ carousel, amountToScroll, sign }) => { - const width = carousel.clientWidth - carousel.scrollBy({ - top: 0, - left: sign === 'positive' ? width * amountToScroll : -(width * amountToScroll), - behavior: 'smooth', - }) - }, - topToBottom: ({ carousel, amountToScroll, sign }) => { - const height = carousel.clientHeight - carousel.scrollBy({ - top: sign === 'positive' ? height * amountToScroll : -(height * amountToScroll), - left: 0, - behavior: 'smooth', - }) - }, -} - -export function useCarousel({ amountToScroll = 0.9, direction }: CarouselOptions): CarouselState { - const [carousel, setCarousel] = useState() - const nextCarousel = useCallback((carousel: HTMLElement) => { - setCarousel(carousel) - }, []) - - const [scrollability, setScrollability] = useState(defaultCarouselState) - - const scrollabilityReference = useRef(scrollability) - scrollabilityReference.current = scrollability - - // Listen for UIEvents that can affect scrollability (e.g. scroll, resize) - useEffect(() => { - function onScroll(): void { - if (carousel) { - const newScrollability = carouselScrollHandlers[direction](carousel) - if (!isEqual(scrollabilityReference.current, newScrollability)) { - setScrollability(newScrollability) - } - } - } - - carousel?.addEventListener('scroll', onScroll) - - let subscription: Subscription | undefined - - if (carousel) { - subscription = observeResize(carousel).subscribe(() => { - const newScrollability = carouselScrollHandlers[direction](carousel) - - if (!isEqual(scrollabilityReference.current, newScrollability)) { - setScrollability(newScrollability) - } - }) - - // Check initial scroll state - const newScrollability = carouselScrollHandlers[direction](carousel) - if (!isEqual(scrollabilityReference.current, newScrollability)) { - setScrollability(newScrollability) - } - } - - return () => { - carousel?.removeEventListener('scroll', onScroll) - subscription?.unsubscribe() - } - }, [carousel, direction]) - - // Handle negative and positive click events - const onNegativeClicked = useCallback(() => { - if (carousel) { - carouselClickHandlers[direction]({ sign: 'negative', amountToScroll, carousel }) - } - }, [direction, amountToScroll, carousel]) - - const onPositiveClicked = useCallback(() => { - if (carousel) { - carouselClickHandlers[direction]({ sign: 'positive', amountToScroll, carousel }) - } - }, [direction, amountToScroll, carousel]) - - return { - canScrollNegative: scrollability.canScrollNegative, - canScrollPositive: scrollability.canScrollPositive, - onNegativeClicked, - onPositiveClicked, - carouselReference: nextCarousel, - } -} diff --git a/client/web/src/enterprise/insights/CodeInsightsRouter.tsx b/client/web/src/enterprise/insights/CodeInsightsRouter.tsx index dcd1c070892..b0b81c61309 100644 --- a/client/web/src/enterprise/insights/CodeInsightsRouter.tsx +++ b/client/web/src/enterprise/insights/CodeInsightsRouter.tsx @@ -20,7 +20,7 @@ export interface CodeInsightsRouterProps extends TelemetryProps { export const CodeInsightsRouter: FC = props => { const { authenticatedUser, telemetryService } = props - if (!window.context.codeInsightsEnabled) { + if (!window.context?.codeInsightsEnabled) { return ( ({ - action: id, - alt: id, - when: true, - })), - }, -} - -const mockExtensionsController = { - ...extensionsController, - extHostAPI: Promise.resolve( - pretendRemote({ - getContributions: () => pretendProxySubscribable(of(mockContributions)), - registerContributions: () => pretendProxySubscribable(EMPTY).subscribe(noop as any), - haveInitialExtensionsLoaded: () => pretendProxySubscribable(of(true)), - }) - ), -} - -const decorator: DecoratorFn = story => ( - - {() => ( - -
{story()}
-
- )} -
-) - -const config: Meta = { - title: 'web/extensions/ActionItemsBar', - decorators: [decorator], - component: ActionItemsBar, - parameters: { - chromatic: { - enableDarkMode: true, - disableSnapshot: false, - }, - }, -} - -export default config - -export const Default: React.FunctionComponent> = () => { - const { useActionItemsBar } = useWebActionItems() - - return ( - - ) -} diff --git a/client/web/src/extensions/components/ActionItemsBar.tsx b/client/web/src/extensions/components/ActionItemsBar.tsx deleted file mode 100644 index a8e4a1b82b5..00000000000 --- a/client/web/src/extensions/components/ActionItemsBar.tsx +++ /dev/null @@ -1,412 +0,0 @@ -import React, { useCallback, useEffect, useMemo, useState } from 'react' - -import { mdiChevronDoubleDown, mdiChevronDoubleUp, mdiMenuDown, mdiMenuUp, mdiPuzzleOutline } from '@mdi/js' -import VisuallyHidden from '@reach/visually-hidden' -import classNames from 'classnames' -import { head, last } from 'lodash' -import { useLocation } from 'react-router-dom' -import { BehaviorSubject, from, of } from 'rxjs' -import { distinctUntilChanged, map } from 'rxjs/operators' -import { focusable, FocusableElement } from 'tabbable' -import { Key } from 'ts-key-enum' - -import { ContributableMenu } from '@sourcegraph/client-api' -import { LocalStorageSubject } from '@sourcegraph/common' -import { ActionItem } from '@sourcegraph/shared/src/actions/ActionItem' -import { ActionsContainer } from '@sourcegraph/shared/src/actions/ActionsContainer' -import { haveInitialExtensionsLoaded } from '@sourcegraph/shared/src/api/features' -import { ExtensionsControllerProps } from '@sourcegraph/shared/src/extensions/controller' -import { PlatformContextProps } from '@sourcegraph/shared/src/platform/context' -import { isSettingsValid } from '@sourcegraph/shared/src/settings/settings' -import { TelemetryProps } from '@sourcegraph/shared/src/telemetry/telemetryService' -import { Button, ButtonLink, Icon, LoadingSpinner, Tooltip, useObservable } from '@sourcegraph/wildcard' - -import { ErrorBoundary } from '../../components/ErrorBoundary' -import { useCarousel } from '../../components/useCarousel' -import { RepositoryFields } from '../../graphql-operations' -import { OpenInEditorActionItem } from '../../open-in-editor/OpenInEditorActionItem' -import { GoToCodeHostAction } from '../../repo/actions/GoToCodeHostAction' -import { ToggleBlameAction } from '../../repo/actions/ToggleBlameAction' -import { fetchFileExternalLinks } from '../../repo/backend' -import { parseBrowserRepoURL } from '../../util/url' - -import styles from './ActionItemsBar.module.scss' - -const scrollButtonClassName = styles.scroll - -function getIconClassName(index: number): string | undefined { - return (styles as Record)[`icon${index % 5}`] -} - -function arrowable(element: HTMLElement): FocusableElement[] { - return focusable(element).filter( - elm => !elm.classList.contains('disabled') && !elm.classList.contains(scrollButtonClassName) - ) -} - -export function useWebActionItems(): Pick & - Pick { - const toggles = useMemo(() => new LocalStorageSubject('action-items-bar-expanded', true), []) - - const [toggleReference, setToggleReference] = useState(null) - const nextToggleReference = useCallback((toggle: HTMLElement) => { - setToggleReference(toggle) - }, []) - - const [barReference, setBarReference] = useState(null) - const nextBarReference = useCallback((bar: HTMLElement) => { - setBarReference(bar) - }, []) - - // Set up keyboard navigation for distant toggle and bar. Remove previous event - // listeners whenever references change. - useEffect(() => { - function onKeyDownToggle(event: KeyboardEvent): void { - if (event.key === Key.ArrowDown && barReference) { - const firstBarArrowable = head(arrowable(barReference)) - if (firstBarArrowable) { - firstBarArrowable.focus() - event.preventDefault() - } - } - - if (event.key === Key.ArrowUp && barReference) { - const lastBarArrowable = last(arrowable(barReference)) - if (lastBarArrowable) { - lastBarArrowable.focus() - event.preventDefault() - } - } - } - - function onKeyDownBar(event: KeyboardEvent): void { - if (event.target instanceof HTMLElement && toggleReference && barReference) { - const arrowableChildren = arrowable(barReference) - const indexOfTarget = arrowableChildren.indexOf(event.target) - - if (event.key === Key.ArrowDown) { - // If this is the last arrowable element, go back to the toggle - if (indexOfTarget === arrowableChildren.length - 1) { - toggleReference.focus() - event.preventDefault() - return - } - - const itemToFocus = arrowableChildren[indexOfTarget + 1] - if (itemToFocus instanceof HTMLElement) { - itemToFocus.focus() - event.preventDefault() - return - } - } - - if (event.key === Key.ArrowUp) { - // If this is the first arrowable element, go back to the toggle - if (indexOfTarget === 0) { - toggleReference.focus() - event.preventDefault() - return - } - - const itemToFocus = arrowableChildren[indexOfTarget - 1] - if (itemToFocus instanceof HTMLElement) { - itemToFocus.focus() - event.preventDefault() - return - } - } - } - } - - toggleReference?.addEventListener('keydown', onKeyDownToggle) - barReference?.addEventListener('keydown', onKeyDownBar) - - return () => { - toggleReference?.removeEventListener('keydown', onKeyDownToggle) - toggleReference?.removeEventListener('keydown', onKeyDownBar) - } - }, [toggleReference, barReference]) - - const barsReferenceCounts = useMemo(() => new BehaviorSubject(0), []) - - const useActionItemsBar = useCallback(() => { - // `useActionItemsBar` will be used as a hook - // eslint-disable-next-line react-hooks/rules-of-hooks - const isOpen = useObservable(toggles) - - // Let the toggle know it's on the page - // eslint-disable-next-line react-hooks/rules-of-hooks - useEffect(() => { - // Use reference counter so that effect order doesn't matter - barsReferenceCounts.next(barsReferenceCounts.value + 1) - - return () => barsReferenceCounts.next(barsReferenceCounts.value - 1) - }, []) - - return { isOpen, barReference: nextBarReference } - }, [toggles, nextBarReference, barsReferenceCounts]) - - const useActionItemsToggle = useCallback(() => { - // `useActionItemsToggle` will be used as a hook - // eslint-disable-next-line react-hooks/rules-of-hooks - const isOpen = useObservable(toggles) - - // eslint-disable-next-line react-hooks/rules-of-hooks - const toggle = useCallback(() => toggles.next(!isOpen), [isOpen]) - - // Only show the action items toggle when the component is on the page - // eslint-disable-next-line react-hooks/rules-of-hooks - const barInPage = !!useObservable( - // eslint-disable-next-line react-hooks/rules-of-hooks - useMemo( - () => - barsReferenceCounts.pipe( - map(count => count > 0), - distinctUntilChanged() - ), - [] - ) - ) - - return { isOpen, toggle, barInPage, toggleReference: nextToggleReference } - }, [toggles, nextToggleReference, barsReferenceCounts]) - - return { - useActionItemsBar, - useActionItemsToggle, - } -} - -export interface ActionItemsBarProps extends ExtensionsControllerProps, TelemetryProps, PlatformContextProps { - repo?: RepositoryFields - useActionItemsBar: () => { isOpen: boolean | undefined; barReference: React.RefCallback } - source?: 'compare' | 'commit' | 'blob' -} - -const actionItemClassName = classNames( - 'd-flex justify-content-center align-items-center text-decoration-none', - styles.action -) - -/** - * Renders extensions (both migrated to the core workflow and legacy) actions items in the sidebar. - */ -export const ActionItemsBar = React.memo(function ActionItemsBar(props) { - const { extensionsController, source } = props - - const location = useLocation() - const { isOpen, barReference } = props.useActionItemsBar() - const { repoName, rawRevision, filePath, commitRange, position, range } = parseBrowserRepoURL( - location.pathname + location.search + location.hash - ) - - const { carouselReference, canScrollNegative, canScrollPositive, onNegativeClicked, onPositiveClicked } = - useCarousel({ direction: 'topToBottom' }) - - const haveExtensionsLoaded = useObservable( - useMemo( - () => - extensionsController !== null ? haveInitialExtensionsLoaded(extensionsController.extHostAPI) : of(true), - [extensionsController] - ) - ) - - const settingsOrError = useObservable( - useMemo(() => from(props.platformContext.settings), [props.platformContext.settings]) - ) - const settings = - settingsOrError !== undefined && isSettingsValid(settingsOrError) ? settingsOrError.final : undefined - const perforceCodeHostUrlToSwarmUrlMap = - (settings?.['perforce.codeHostToSwarmMap'] as { [codeHost: string]: string } | undefined) || {} - - if (!isOpen) { - return
- } - - return ( -
- {/* To be clear to users that this isn't an error reported by extensions about e.g. the code they're viewing. */} - Component error: {error.message}}> - - {canScrollNegative && ( - - )} - - {source !== 'compare' && source !== 'commit' && ( - - )} - - {source === 'blob' && ( - <> - - {window.context.isAuthenticatedUser && ( - - )} - - )} - - {extensionsController !== null ? ( - - {items => ( -
    - {items.map((item, index) => { - const hasIconURL = !!item.action.actionItem?.iconURL - const className = classNames( - actionItemClassName, - !hasIconURL && - classNames(styles.actionNoIcon, getIconClassName(index), 'text-sm') - ) - const inactiveClassName = classNames( - styles.actionInactive, - !hasIconURL && styles.actionNoIconInactive - ) - const listItemClassName = classNames( - styles.listItem, - index !== items.length - 1 && 'mb-1' - ) - - const dataContent = !hasIconURL ? item.action.category?.slice(0, 1) : undefined - - return ( -
  • - -
  • - ) - })} -
- )} -
- ) : null} - {canScrollPositive && ( - - )} - {haveExtensionsLoaded && } -
-
- ) -}) - -export interface ActionItemsToggleProps extends ExtensionsControllerProps<'extHostAPI'> { - useActionItemsToggle: () => { - isOpen: boolean | undefined - toggle: () => void - toggleReference: React.RefCallback - barInPage: boolean - } - className?: string -} - -export const ActionItemsToggle: React.FunctionComponent> = ({ - useActionItemsToggle, - extensionsController, - className, -}) => { - const panelName = extensionsController !== null && window.context.enableLegacyExtensions ? 'extensions' : 'actions' - - const { isOpen, toggle, toggleReference, barInPage } = useActionItemsToggle() - - const haveExtensionsLoaded = useObservable( - useMemo( - () => - extensionsController !== null ? haveInitialExtensionsLoaded(extensionsController.extHostAPI) : of(true), - [extensionsController] - ) - ) - - return barInPage ? ( - <> -
  • -
  • -
    - - - {!haveExtensionsLoaded ? ( - - ) : isOpen ? ( - - ) : ( - - )} - {haveExtensionsLoaded && Down arrow to enter} - - -
    -
  • - - ) : null -} - -const ActionItemsDivider: React.FunctionComponent> = ({ - className, -}) =>
    diff --git a/client/web/src/integration/blob-viewer.test.ts b/client/web/src/integration/blob-viewer.test.ts index a7db675dd3a..f1b8790ecee 100644 --- a/client/web/src/integration/blob-viewer.test.ts +++ b/client/web/src/integration/blob-viewer.test.ts @@ -1,10 +1,6 @@ import assert from 'assert' -import type { ExtensionContext } from '@sourcegraph/shared/src/codeintel/legacy-extensions/api' import { SharedGraphQlOperations } from '@sourcegraph/shared/src/graphql-operations' -import { ExtensionManifest } from '@sourcegraph/shared/src/schema/extensionSchema' -import { Settings } from '@sourcegraph/shared/src/schema/settings.schema' -import { accessibilityAudit } from '@sourcegraph/shared/src/testing/accessibility' import { Driver, createDriverForTest } from '@sourcegraph/shared/src/testing/driver' import { afterEachSaveScreenshotIfFailed } from '@sourcegraph/shared/src/testing/screenshotReporter' @@ -18,7 +14,6 @@ import { createBlobContentResult, } from './graphQlResponseHelpers' import { commonWebGraphQlResults, createViewerSettingsGraphQLOverride } from './graphQlResults' -import { percySnapshotWithVariants } from './utils' describe('Blob viewer', () => { let driver: Driver @@ -161,366 +156,4 @@ describe('Blob viewer', () => { await driver.assertWindowLocation(`/${repositoryName}/-/blob/${fileName}?L1-3`) }) }) - - // Describes the ways the blob viewer can be extended through Sourcegraph extensions. - describe('extensibility', () => { - beforeEach(() => { - const userSettings: Settings = { - extensions: { - 'test/test': true, - }, - } - const extensionManifest: ExtensionManifest = { - url: new URL('/-/static/extension/0001-test-test.js?hash--test-test', driver.sourcegraphBaseUrl).href, - activationEvents: ['*'], - } - testContext.overrideGraphQL({ - ...commonBlobGraphQlResults, - ViewerSettings: () => ({ - viewerSettings: { - __typename: 'SettingsCascade', - final: JSON.stringify(userSettings), - subjects: [ - { - __typename: 'User', - displayName: 'Test User', - id: 'TestUserSettingsID', - latestSettings: { - id: 123, - contents: JSON.stringify(userSettings), - }, - username: 'test', - viewerCanAdminister: true, - settingsURL: '/users/test/settings', - }, - ], - }, - }), - Blob: () => ({ - repository: { - commit: { - blob: null, - file: { - __typename: 'VirtualFile', - content: '// Log to console\nconsole.log("Hello world")', - totalLines: 2, - richHTML: '', - highlight: { - aborted: false, - html: - // Note: whitespace in this string is significant. - '' + - '' + - '
    ' + - '' + - '// ' + - 'Log to console\n
    ' + - '' + - 'console' + - '.' + - 'log' + - '(' + - '' + - '"' + - 'Hello world' + - '"' + - ')\n
    ', - lsif: '', - }, - }, - }, - }, - }), - Extensions: () => ({ - extensionRegistry: { - __typename: 'ExtensionRegistry', - extensions: { - nodes: [ - { - id: 'test', - extensionID: 'test/test', - manifest: { - jsonFields: extensionManifest, - }, - }, - ], - }, - }, - }), - }) - - // Serve a mock extension bundle with a simple hover provider - testContext.server - .get(new URL(extensionManifest.url, driver.sourcegraphBaseUrl).href) - .intercept((request, response) => { - function extensionBundle(): void { - // eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires - const sourcegraph = require('sourcegraph') as typeof import('sourcegraph') - - function activate(context: ExtensionContext): void { - context.subscriptions.add( - sourcegraph.languages.registerHoverProvider([{ language: 'typescript' }], { - provideHover: () => ({ - contents: { - kind: sourcegraph.MarkupKind.Markdown, - value: 'Test hover content', - }, - }), - }) - ) - } - - exports.activate = activate - } - // Create an immediately-invoked function expression for the extensionBundle function - const extensionBundleString = `(${extensionBundle.toString()})()` - response.type('application/javascript; charset=utf-8').send(extensionBundleString) - }) - }) - it('truncates long file paths properly', async () => { - await driver.page.goto( - `${driver.sourcegraphBaseUrl}/${repositoryName}/-/blob/this_is_a_long_file_path/apps/rest-showcase/src/main/java/org/demo/rest/example/OrdersController.java` - ) - await driver.page.waitForSelector('[data-testid="repo-blob"]') - await driver.page.waitForSelector('.test-breadcrumb') - // Uncomment this snapshot once https://github.com/sourcegraph/sourcegraph/issues/15126 is resolved - // await percySnapshot(driver.page, this.test!.fullTitle()) - }) - - it.skip('shows a hover overlay from a hover provider when a token is hovered', async () => { - await driver.page.goto(`${driver.sourcegraphBaseUrl}/${repositoryName}/-/blob/${fileName}`) - await driver.page.waitForSelector('[data-testid="repo-blob"]') - // TODO - }) - - it.skip('gets displayed when navigating to a URL with a token position', async () => { - await driver.page.goto(`${driver.sourcegraphBaseUrl}/github.com/sourcegraph/test/-/test.ts#2:9`) - // TODO - }) - - // Disabled because it's flaky. See: https://github.com/sourcegraph/sourcegraph/issues/31806 - it.skip('properly displays reference panel for URIs with spaces', async () => { - const repositoryName = 'github.com/sourcegraph/test%20repo' - const files = ['test.ts', 'test spaces.ts'] - const commitID = '1234' - const userSettings: Settings = { - extensions: { - 'test/references': true, - }, - } - const extensionManifest: ExtensionManifest = { - url: new URL( - '/-/static/extension/0001-test-references.js?hash--test-references', - driver.sourcegraphBaseUrl - ).href, - activationEvents: ['*'], - } - testContext.overrideGraphQL({ - ...commonBlobGraphQlResults, - ViewerSettings: () => ({ - viewerSettings: { - __typename: 'SettingsCascade', - final: JSON.stringify(userSettings), - subjects: [ - { - __typename: 'User', - displayName: 'Test User', - id: 'TestUserSettingsID', - latestSettings: { - id: 123, - contents: JSON.stringify(userSettings), - }, - username: 'test', - viewerCanAdminister: true, - settingsURL: '/users/test/settings', - }, - ], - }, - }), - Extensions: () => ({ - extensionRegistry: { - __typename: 'ExtensionRegistry', - extensions: { - nodes: [ - { - id: 'test', - extensionID: 'test/references', - manifest: { - jsonFields: extensionManifest, - }, - }, - ], - }, - }, - }), - Blob: ({ filePath }) => ({ - repository: { - commit: { - blob: null, - file: { - __typename: 'VirtualFile', - content: `// file path: ${filePath}\nconsole.log("Hello world")`, - totalLines: 2, - richHTML: '', - highlight: { - aborted: false, - html: - '' + - '
    ' + - '' + - '//' + - ` file path: ${filePath}\n` + - '
    ' + - '' + - '' + - 'console' + - '.' + - 'log' + - '(' + - '' + - '"' + - 'Hello world' + - '"' + - ')' + - '\n
    ', - lsif: '', - lineRanges: [], - }, - }, - }, - }, - }), - TreeEntries: () => createTreeEntriesResult(repositorySourcegraphUrl, files), - ResolveRepoRev: () => createResolveRepoRevisionResult(repositorySourcegraphUrl, commitID), - HighlightedFile: ({ filePath }) => ({ - repository: { - commit: { - file: { - isDirectory: false, - richHTML: '', - highlight: { - aborted: false, - html: - '' + - '
    ' + - '' + - '//' + - ` file path: ${filePath}\n` + - '
    ' + - '' + - '' + - 'console' + - '.' + - 'log' + - '(' + - '' + - '"' + - 'Hello world' + - '"' + - ')' + - '\n
    ', - lineRanges: [], - }, - }, - }, - }, - }), - FetchCommits: () => ({ - node: { __typename: 'GitCommit' }, - }), - // Required for definition provider, - ResolveRawRepoName: () => ({ - repository: { - mirrorInfo: { - cloned: true, - }, - uri: repositoryName, - }, - }), - }) - - // Serve a mock extension bundle with a simple reference provider - testContext.server - .get(new URL(extensionManifest.url, driver.sourcegraphBaseUrl).href) - .intercept((request, response) => { - function extensionBundle(): void { - // eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires - const sourcegraph = require('sourcegraph') as typeof import('sourcegraph') - - function activate(context: ExtensionContext): void { - context.subscriptions.add( - sourcegraph.languages.registerReferenceProvider(['*'], { - provideReferences: () => [ - new sourcegraph.Location( - new URL('git://github.com/sourcegraph/test%20repo?1234#test%20spaces.ts'), - new sourcegraph.Range( - new sourcegraph.Position(0, 0), - new sourcegraph.Position(1, 0) - ) - ), - ], - }) - ) - - // We aren't testing definition providers in this test; we include a definition provider - // because the "Find references" action isn't displayed unless a definition is found - context.subscriptions.add( - sourcegraph.languages.registerDefinitionProvider(['*'], { - provideDefinition: () => - new sourcegraph.Location( - new URL('git://github.com/sourcegraph/test%20repo?1234#test%20spaces.ts'), - new sourcegraph.Range( - new sourcegraph.Position(0, 0), - new sourcegraph.Position(1, 0) - ) - ), - }) - ) - } - - exports.activate = activate - } - // Create an immediately-invoked function expression for the extensionBundle function - const extensionBundleString = `(${extensionBundle.toString()})()` - response.type('application/javascript; charset=utf-8').send(extensionBundleString) - }) - - // TEMPORARY: Mock `Date.now` to prevent temporary Firefox from rendering. - await driver.page.evaluateOnNewDocument(() => { - // Number of ms between Unix epoch and July 1, 2020 (outside of Firefox campaign range) - const mockMs = new Date('July 1, 2020 00:00:00 UTC').getTime() - Date.now = () => mockMs - }) - - await driver.page.goto(`${driver.sourcegraphBaseUrl}/${repositoryName}/-/blob/test.ts`) - - // Click on "log" in "console.log()" in line 2 - await driver.page.waitForSelector('.test-log-token', { visible: true }) - await driver.page.click('.test-log-token') - - // Click 'Find references' - await driver.page.waitForSelector('.test-tooltip-find-references', { visible: true }) - await driver.page.click('.test-tooltip-find-references') - - await driver.page.waitForSelector('.test-file-match-children-item', { visible: true }) - - await percySnapshotWithVariants(driver.page, 'Blob Reference Panel', { waitForCodeHighlighting: true }) - await accessibilityAudit(driver.page) - // Click on the first reference - await driver.page.click('.test-file-match-children-item') - - // Assert that the first line of code has text content which contains: 'file path: test spaces.ts' - try { - await driver.page.waitForFunction( - () => - document - .querySelector('[data-testid="repo-blob"] [data-line="1"]') - ?.nextElementSibling?.textContent?.includes('file path: test spaces.ts'), - { timeout: 5000 } - ) - } catch { - throw new Error('Expected to navigate to file after clicking on link in references panel') - } - }) - }) }) diff --git a/client/web/src/integration/codemirror-blob-view.test.ts b/client/web/src/integration/codemirror-blob-view.test.ts index 5120b5772c9..bd6ad319195 100644 --- a/client/web/src/integration/codemirror-blob-view.test.ts +++ b/client/web/src/integration/codemirror-blob-view.test.ts @@ -2,11 +2,8 @@ import assert from 'assert' import { ElementHandle, MouseButton } from 'puppeteer' -import type { ExtensionContext } from '@sourcegraph/shared/src/codeintel/legacy-extensions/api' import { JsonDocument, SyntaxKind } from '@sourcegraph/shared/src/codeintel/scip' import { SharedGraphQlOperations } from '@sourcegraph/shared/src/graphql-operations' -import { ExtensionManifest } from '@sourcegraph/shared/src/schema/extensionSchema' -import { Settings } from '@sourcegraph/shared/src/schema/settings.schema' import { Driver, createDriverForTest, percySnapshot } from '@sourcegraph/shared/src/testing/driver' import { afterEachSaveScreenshotIfFailed } from '@sourcegraph/shared/src/testing/screenshotReporter' @@ -284,136 +281,6 @@ describe('CodeMirror blob view', () => { }) }) - // Describes the ways the blob viewer can be extended through Sourcegraph extensions. - describe('extensibility', () => { - beforeEach(() => { - testContext.overrideJsContext({ enableLegacyExtensions: true }) - }) - - describe('hovercards', () => { - beforeEach(() => { - const { - graphqlResults: extensionGraphQlResult, - intercept, - userSettings, - } = createExtensionData([ - { - id: 'test', - extensionID: 'test/test', - extensionManifest: { - url: new URL( - '/-/static/extension/0001-test-test.js?hash--test-test', - driver.sourcegraphBaseUrl - ).href, - activationEvents: ['*'], - }, - bundle: function extensionBundle(): void { - // eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires - const sourcegraph = require('sourcegraph') as typeof import('sourcegraph') - - function activate(context: ExtensionContext): void { - context.subscriptions.add( - sourcegraph.languages.registerHoverProvider([{ language: 'typescript' }], { - provideHover: () => ({ - contents: { - kind: sourcegraph.MarkupKind.Markdown, - value: 'Test hover content', - }, - }), - }) - ) - } - - exports.activate = activate - }, - }, - ]) - testContext.overrideGraphQL({ - ...commonBlobGraphQlResults, - ...createViewerSettingsGraphQLOverride({ - user: { - ...userSettings, - experimentalFeatures: { - enableCodeMirrorFileView: true, - }, - }, - }), - ...extensionGraphQlResult, - }) - - // Serve a mock extension bundle with a simple hover provider - intercept(testContext, driver) - }) - - it('shows a hover overlay from a hover provider when a token is hovered', async () => { - await driver.page.goto(`${driver.sourcegraphBaseUrl}${filePaths['test.ts']}`) - await waitForView() - await driver.page.hover(wordSelector) - await driver.page.waitForSelector('.cm-code-intel-hovercard') - assert.strictEqual( - await driver.page.evaluate( - (): string => - document.querySelector('[data-testid="hover-overlay-contents"]')?.textContent?.trim() ?? '' - ), - 'Test hover content', - 'hovercard is visible with correct content' - ) - - await driver.page.hover(lineAt(5)) - try { - await driver.page.waitForSelector('.cm-code-intel-hovercard', { hidden: true }) - } catch { - throw new Error('Timeout waiting for hovercard to disappear') - } - }) - - it('pins a hovercard and unpins hovercards', async () => { - await driver.page.goto(`${driver.sourcegraphBaseUrl}${filePaths['test.ts']}`) - await waitForView() - await driver.page.hover(wordSelector) - await driver.page.waitForSelector('.cm-code-intel-hovercard [data-testid="hover-copy-link"]') - - await driver.page.click('.cm-code-intel-hovercard [data-testid="hover-copy-link"]') - - // URL gets updated - await driver.assertWindowLocation(`${filePaths['test.ts']}?L1:1&popover=pinned`) - - // Close button is visible - await driver.page.waitForSelector('.cm-code-intel-hovercard [aria-label="Close"]') - - // Hovercard stay open when moving the mouse away - await driver.page.hover(lineAt(5)) - await driver.page.waitForSelector('.cm-code-intel-hovercard') - - // Closes hovercard when clicking on another line - await driver.page.click(lineAt(5)) - try { - await driver.page.waitForSelector('.cm-code-intel-hovercard', { hidden: true }) - } catch { - throw new Error('Timeout waiting for hovercard to close after selecting another line') - } - - // Opens pinned hovecard when navigating back - await driver.page.goBack() - await driver.page.waitForSelector('.cm-code-intel-hovercard') - - // Closes hover card when clicking the close button - await driver.page.click('.cm-code-intel-hovercard [aria-label="Close"]') - try { - await driver.page.waitForSelector('.cm-code-intel-hovercard', { hidden: true }) - } catch { - throw new Error('Timeout waiting for hovercard to close after clicking close button') - } - }) - - it('opens a pinned hovercard on page load', async () => { - await driver.page.goto(`${driver.sourcegraphBaseUrl}${filePaths['test.ts']}?L1:1&popover=pinned`) - await waitForView() - await driver.page.waitForSelector('.cm-code-intel-hovercard') - }) - }) - }) - describe('in-document search', () => { const { graphqlResults: blobGraphqlResults, filePaths } = createBlobPageData({ repoName, @@ -578,54 +445,3 @@ function createBlobPageData({ }, } } - -interface MockExtension { - id: string - extensionID: string - extensionManifest: ExtensionManifest - /** - * A function whose body is a Sourcegraph extension. - * - * Bundle must import 'sourcegraph' (e.g. `const sourcegraph = require('sourcegraph')`) - * */ - bundle: () => void -} - -function createExtensionData(extensions: MockExtension[]): { - intercept: (testContext: WebIntegrationTestContext, driver: Driver) => void - graphqlResults: Pick - userSettings: Required> -} { - return { - intercept(testContext: WebIntegrationTestContext, driver: Driver) { - for (const extension of extensions) { - testContext.server - .get(new URL(extension.extensionManifest.url, driver.sourcegraphBaseUrl).href) - .intercept((_request, response) => { - // Create an immediately-invoked function expression for the extensionBundle function - const extensionBundleString = `(${extension.bundle.toString()})()` - response.type('application/javascript; charset=utf-8').send(extensionBundleString) - }) - } - }, - graphqlResults: { - Extensions: () => ({ - extensionRegistry: { - __typename: 'ExtensionRegistry', - extensions: { - nodes: extensions.map(extension => ({ - ...extension, - manifest: { jsonFields: extension.extensionManifest }, - })), - }, - }, - }), - }, - userSettings: { - extensions: extensions.reduce((extensionsSettings: Record, mockExtension) => { - extensionsSettings[mockExtension.extensionID] = true - return extensionsSettings - }, {}), - }, - } -} diff --git a/client/web/src/integration/jscontext.ts b/client/web/src/integration/jscontext.ts index dcf393bf779..96f530fc70a 100644 --- a/client/web/src/integration/jscontext.ts +++ b/client/web/src/integration/jscontext.ts @@ -49,5 +49,4 @@ export const createJsContext = ({ sourcegraphBaseUrl }: { sourcegraphBaseUrl: st xhrHeaders: {}, authProviders: [builtinAuthProvider], authMinPasswordLength: 12, - enableLegacyExtensions: false, }) diff --git a/client/web/src/jscontext.ts b/client/web/src/jscontext.ts index f311f871144..eb131330979 100644 --- a/client/web/src/jscontext.ts +++ b/client/web/src/jscontext.ts @@ -201,9 +201,6 @@ export interface SourcegraphContext extends Pick, 'e /** Whether the product research sign-up page is enabled on the site. */ productResearchPageEnabled: boolean - /** Whether the use of extensions are enabled. (Doesn't affect code intel and git extras.) */ - enableLegacyExtensions?: boolean - /** Contains information about the product license. */ licenseInfo?: { currentPlan: 'old-starter-0' | 'old-enterprise-0' | 'team-0' | 'enterprise-0' | 'business-0' | 'enterprise-1' diff --git a/client/web/src/nav/GlobalNavbar.story.tsx b/client/web/src/nav/GlobalNavbar.story.tsx index 9fa997b7613..63ab7384c2e 100644 --- a/client/web/src/nav/GlobalNavbar.story.tsx +++ b/client/web/src/nav/GlobalNavbar.story.tsx @@ -5,7 +5,6 @@ import { mockFetchSearchContexts, mockGetUserSearchContextNamespaces, } from '@sourcegraph/shared/src/testing/searchContexts/testHelpers' -import { extensionsController } from '@sourcegraph/shared/src/testing/searchTestHelpers' import { Grid, H3 } from '@sourcegraph/wildcard' import { AuthenticatedUser } from '../auth' @@ -21,7 +20,6 @@ const defaultProps: GlobalNavbarProps = { final: null, subjects: null, }, - extensionsController, telemetryService: NOOP_TELEMETRY_SERVICE, globbing: false, platformContext: {} as any, @@ -49,7 +47,6 @@ const allNavItemsProps: Partial = { batchChangesExecutionEnabled: true, batchChangesWebhookLogsEnabled: true, codeInsightsEnabled: true, - enableLegacyExtensions: true, } const allAuthenticatedNavItemsProps: Partial = { diff --git a/client/web/src/nav/GlobalNavbar.test.tsx b/client/web/src/nav/GlobalNavbar.test.tsx index dbdbab3c0a3..5a83fe522e7 100644 --- a/client/web/src/nav/GlobalNavbar.test.tsx +++ b/client/web/src/nav/GlobalNavbar.test.tsx @@ -5,7 +5,7 @@ import { mockFetchSearchContexts, mockGetUserSearchContextNamespaces, } from '@sourcegraph/shared/src/testing/searchContexts/testHelpers' -import { extensionsController, NOOP_SETTINGS_CASCADE } from '@sourcegraph/shared/src/testing/searchTestHelpers' +import { NOOP_SETTINGS_CASCADE } from '@sourcegraph/shared/src/testing/searchTestHelpers' import { renderWithBrandedContext } from '@sourcegraph/wildcard/src/testing' import { GlobalNavbar } from './GlobalNavbar' @@ -15,7 +15,6 @@ jest.mock('../components/branding/BrandLogo', () => ({ BrandLogo: 'BrandLogo' }) const PROPS: React.ComponentProps = { authenticatedUser: null, - extensionsController, isSourcegraphDotCom: false, isSourcegraphApp: false, platformContext: {} as any, @@ -41,16 +40,6 @@ const PROPS: React.ComponentProps = { } describe('GlobalNavbar', () => { - const origContext = window.context - beforeEach(() => { - window.context = { - enableLegacyExtensions: false, - } as any - }) - afterEach(() => { - window.context = origContext - }) - test('default', () => { const { asFragment } = renderWithBrandedContext( diff --git a/client/web/src/nav/GlobalNavbar.tsx b/client/web/src/nav/GlobalNavbar.tsx index 28407ebd257..cfc765f678e 100644 --- a/client/web/src/nav/GlobalNavbar.tsx +++ b/client/web/src/nav/GlobalNavbar.tsx @@ -6,9 +6,7 @@ import BookOutlineIcon from 'mdi-react/BookOutlineIcon' import MagnifyIcon from 'mdi-react/MagnifyIcon' import { useLocation } from 'react-router-dom' -import { ContributableMenu } from '@sourcegraph/client-api' import { isErrorLike, isMacPlatform } from '@sourcegraph/common' -import { ExtensionsControllerProps } from '@sourcegraph/shared/src/extensions/controller' import { shortcutDisplayName } from '@sourcegraph/shared/src/keyboardShortcuts' import { PlatformContextProps } from '@sourcegraph/shared/src/platform/context' import { Settings } from '@sourcegraph/shared/src/schema/settings.schema' @@ -27,7 +25,6 @@ import { CodeMonitoringProps } from '../codeMonitoring' import { CodyIcon } from '../cody/CodyIcon' import { BrandLogo } from '../components/branding/BrandLogo' import { getFuzzyFinderFeatureFlags } from '../components/fuzzyFinder/FuzzyFinderFeatureFlag' -import { WebCommandListPopoverButton } from '../components/shared' import { useFeatureFlag } from '../featureFlags/useFeatureFlag' import { useRoutesMatch } from '../hooks' import { CodeInsightsProps } from '../insights/types' @@ -50,7 +47,6 @@ import styles from './GlobalNavbar.module.scss' export interface GlobalNavbarProps extends SettingsCascadeProps, PlatformContextProps, - ExtensionsControllerProps, TelemetryProps, SearchContextInputProps, CodeInsightsProps, @@ -67,7 +63,6 @@ export interface GlobalNavbarProps globbing: boolean isSearchAutoFocusRequired?: boolean isRepositoryRelatedPage?: boolean - enableLegacyExtensions?: boolean branding?: typeof window.context.branding showKeyboardShortcutsHelp: () => void showFeedbackModal: () => void @@ -132,8 +127,6 @@ export const GlobalNavbar: React.FunctionComponent { @@ -293,16 +286,6 @@ export const GlobalNavbar: React.FunctionComponent )} {fuzzyFinderNavbar && FuzzyFinderNavItem(props.setFuzzyFinderIsVisible)} - {props.authenticatedUser && extensionsController !== null && enableLegacyExtensions && ( - - - - )} {props.authenticatedUser?.siteAdmin && ( @@ -325,7 +308,7 @@ export const GlobalNavbar: React.FunctionComponent Sign in - {!isSourcegraphDotCom && window.context.allowSignup && ( + {!isSourcegraphDotCom && window.context?.allowSignup && ( Sign up diff --git a/client/web/src/platform/context.ts b/client/web/src/platform/context.ts index 6f44db4ed27..4b0e49335a7 100644 --- a/client/web/src/platform/context.ts +++ b/client/web/src/platform/context.ts @@ -71,10 +71,10 @@ export function createPlatformContext(): PlatformContext { }, getGraphQLClient: getWebGraphQLClient, requestGraphQL: ({ request, variables }) => requestGraphQL(request, variables), - createExtensionHost: async () => - (await import('@sourcegraph/shared/src/api/extension/worker')).createExtensionHost(), + createExtensionHost: () => { + throw new Error('extensions are no longer supported in the web app') + }, urlToFile: toPrettyWebBlobURL, - getScriptURLForExtension: () => undefined, sourcegraphURL: window.context.externalURL, clientApplication: 'sourcegraph', telemetryService: eventLogger, diff --git a/client/web/src/repo/RepoContainer.tsx b/client/web/src/repo/RepoContainer.tsx index 9549e5a7152..3892f62ff10 100644 --- a/client/web/src/repo/RepoContainer.tsx +++ b/client/web/src/repo/RepoContainer.tsx @@ -3,7 +3,7 @@ import React, { FC, Suspense, useEffect, useMemo, useState } from 'react' import { mdiSourceRepository } from '@mdi/js' import classNames from 'classnames' import { escapeRegExp } from 'lodash' -import { matchPath, Location, useLocation, Route, Routes } from 'react-router-dom' +import { Location, useLocation, Route, Routes } from 'react-router-dom' import { NEVER, of } from 'rxjs' import { catchError, switchMap } from 'rxjs/operators' @@ -33,7 +33,6 @@ import { CodeIntelligenceProps } from '../codeintel' import { BreadcrumbSetters, BreadcrumbsProps } from '../components/Breadcrumbs' import { ErrorBoundary } from '../components/ErrorBoundary' import { HeroPage } from '../components/HeroPage' -import { ActionItemsBarProps, useWebActionItems } from '../extensions/components/ActionItemsBar' import { ExternalLinkFields, RepositoryFields } from '../graphql-operations' import { CodeInsightsProps } from '../insights/types' import { NotebookProps } from '../notebooks' @@ -45,7 +44,6 @@ import { parseBrowserRepoURL } from '../util/url' import { GoToCodeHostAction } from './actions/GoToCodeHostAction' import { fetchFileExternalLinks, ResolvedRevision, resolveRepoRevision } from './backend' import { RepoContainerError } from './RepoContainerError' -import { compareSpecPath } from './repoContainerRoutes' import { RepoHeader, RepoHeaderActionButton, RepoHeaderContributionsLifecycleProps } from './RepoHeader' import { RepoHeaderContributionPortal } from './RepoHeaderContributionPortal' import { @@ -53,7 +51,7 @@ import { RepoRevisionContainerContext, RepoRevisionContainerRoute, } from './RepoRevisionContainer' -import { commitsPath, repoSplat } from './repoRevisionContainerRoutes' +import { repoSplat } from './repoRevisionContainerRoutes' import { RepoSettingsAreaRoute } from './settings/RepoSettingsArea' import { RepoSettingsSideBarGroup } from './settings/RepoSettingsSidebar' import { repoSettingsAreaPath } from './settings/routes' @@ -74,7 +72,6 @@ export interface RepoContainerContext TelemetryProps, Pick, BreadcrumbSetters, - ActionItemsBarProps, SearchStreamingProps, Pick, CodeIntelligenceProps, @@ -273,23 +270,6 @@ export const RepoContainer: FC = props => { }) }, [revision, filePath, repoName, onNavbarQueryChange, globbing]) - const { useActionItemsBar, useActionItemsToggle } = useWebActionItems() - - // render go to the code host action on all the repo container routes and on all compare spec routes - const isGoToCodeHostActionVisible = useMemo(() => { - if (!window.context.enableLegacyExtensions) { - return true - } - const paths = [ - ...repoContainerRoutes.map(route => route.path), - compareSpecPath, - repoSettingsAreaPath, - commitsPath, - ] - - return paths.some(path => matchPath(path, location.pathname)) - }, [repoContainerRoutes, location.pathname]) - const isError = isErrorLike(repoOrError) || isErrorLike(resolvedRevisionOrError) // if revision for given repo does not resolve then we still proceed to render settings routes @@ -327,7 +307,6 @@ export const RepoContainer: FC = props => { repoName, revision: revision || '', resolvedRevision, - useActionItemsBar, } const perforceCodeHostUrlToSwarmUrlMap = @@ -347,7 +326,6 @@ export const RepoContainer: FC = props => {
    = props => { settingsCascade={props.settingsCascade} authenticatedUser={authenticatedUser} platformContext={props.platformContext} - extensionsController={extensionsController} telemetryService={props.telemetryService} /> - {isGoToCodeHostActionVisible && ( - - {({ actionType }) => ( - - )} - - )} + + {({ actionType }) => ( + + )} + {isCodeIntelRepositoryBadgeVisible && ( ( {() =>
    {story()}
    } @@ -70,12 +65,6 @@ export const Default: Story = () => (
    ) -const useActionItemsToggle = () => ({ - isOpen: false, - toggle: () => null, - toggleReference: () => null, - barInPage: false, -}) const onLifecyclePropsChange = (lifecycleProps: RepoHeaderContributionsLifecycleProps) => { lifecycleProps.repoHeaderContributionsLifecycleProps?.onRepoHeaderContributionAdd({ @@ -152,7 +141,6 @@ const createBreadcrumbs = (path: string) => [ const createProps = (path: string, forceWrap: boolean = false): React.ComponentProps => ({ actionButtons: [], - useActionItemsToggle, breadcrumbs: createBreadcrumbs(path), repoName: 'sourcegraph/sourcegraph', revision: 'main', @@ -160,7 +148,6 @@ const createProps = (path: string, forceWrap: boolean = false): React.ComponentP settingsCascade: EMPTY_SETTINGS_CASCADE, authenticatedUser: mockUser, platformContext: {} as any, - extensionsController: null, telemetryService: NOOP_TELEMETRY_SERVICE, forceWrap, }) diff --git a/client/web/src/repo/RepoHeader.tsx b/client/web/src/repo/RepoHeader.tsx index 6fe3a6527cd..ae833464c00 100644 --- a/client/web/src/repo/RepoHeader.tsx +++ b/client/web/src/repo/RepoHeader.tsx @@ -7,13 +7,11 @@ import { useLocation } from 'react-router-dom' import { PlatformContextProps } from '@sourcegraph/shared/src/platform/context' import { SettingsCascadeOrError } from '@sourcegraph/shared/src/settings/settings' import { TelemetryProps } from '@sourcegraph/shared/src/telemetry/telemetryService' -import { lazyComponent } from '@sourcegraph/shared/src/util/lazyComponent' import { Menu, MenuList, Position, Icon } from '@sourcegraph/wildcard' import { AuthenticatedUser } from '../auth' import { Breadcrumbs, BreadcrumbsProps } from '../components/Breadcrumbs' import { ErrorBoundary } from '../components/ErrorBoundary' -import type { ActionItemsToggleProps } from '../extensions/components/ActionItemsBar' import { ActionButtonDescriptor } from '../util/contributions' import { useBreakpoint } from '../util/dom' @@ -21,8 +19,6 @@ import { RepoHeaderActionDropdownToggle } from './components/RepoHeaderActions' import styles from './RepoHeader.module.scss' -const ActionItemsToggle = lazyComponent(() => import('../extensions/components/ActionItemsBar'), 'ActionItemsToggle') - /** * Stores the list of RepoHeaderContributions, manages addition/deletion, and ensures they are sorted. * @@ -121,7 +117,7 @@ export interface RepoHeaderContext { export interface RepoHeaderActionButton extends ActionButtonDescriptor {} -interface Props extends PlatformContextProps, TelemetryProps, BreadcrumbsProps, ActionItemsToggleProps { +interface Props extends PlatformContextProps, TelemetryProps, BreadcrumbsProps { /** * An array of render functions for action buttons that can be configured *in addition* to action buttons * contributed through {@link RepoHeaderContributionsLifecycleProps} and through extensions. @@ -251,14 +247,6 @@ export const RepoHeader: React.FunctionComponent> )} - {window.context.enableLegacyExtensions ? ( -
      - -
    - ) : null} ) diff --git a/client/web/src/repo/RepoRevisionContainer.tsx b/client/web/src/repo/RepoRevisionContainer.tsx index 9f2e45328f4..b5fb500dcac 100644 --- a/client/web/src/repo/RepoRevisionContainer.tsx +++ b/client/web/src/repo/RepoRevisionContainer.tsx @@ -16,7 +16,6 @@ import { AuthenticatedUser } from '../auth' import { BatchChangesProps } from '../batches' import { CodeIntelligenceProps } from '../codeintel' import { BreadcrumbSetters } from '../components/Breadcrumbs' -import { ActionItemsBarProps } from '../extensions/components/ActionItemsBar' import { RepositoryFields } from '../graphql-operations' import { CodeInsightsProps } from '../insights/types' import { NotebookProps } from '../notebooks' @@ -49,7 +48,6 @@ export interface RepoRevisionContainerContext Pick, RevisionSpec, BreadcrumbSetters, - ActionItemsBarProps, SearchStreamingProps, Pick, BatchChangesProps, @@ -81,7 +79,6 @@ interface RepoRevisionContainerProps Pick, RevisionSpec, BreadcrumbSetters, - ActionItemsBarProps, SearchStreamingProps, Pick, CodeIntelligenceProps, diff --git a/client/web/src/repo/RepositoryFileTreePage.tsx b/client/web/src/repo/RepositoryFileTreePage.tsx index a39b48af808..92c065c7fee 100644 --- a/client/web/src/repo/RepositoryFileTreePage.tsx +++ b/client/web/src/repo/RepositoryFileTreePage.tsx @@ -116,7 +116,6 @@ export const RepositoryFileTreePage: FC = props => globbing={globbing} repo={repo} repoName={repoName} - useActionItemsBar={context.useActionItemsBar} isSourcegraphDotCom={context.isSourcegraphDotCom} className={styles.pageContent} /> diff --git a/client/web/src/repo/blob/BlobPage.tsx b/client/web/src/repo/blob/BlobPage.tsx index ba4a7871472..4d76f8f29ba 100644 --- a/client/web/src/repo/blob/BlobPage.tsx +++ b/client/web/src/repo/blob/BlobPage.tsx @@ -363,38 +363,33 @@ export const BlobPage: React.FunctionComponent = ({ className, .. const alwaysRender = ( <> - {!window.context.enableLegacyExtensions ? ( - <> - {window.context.isAuthenticatedUser && ( - - {({ actionType }) => ( - - )} - + {window.context.isAuthenticatedUser && ( + + {({ actionType }) => ( + )} - - {({ actionType }) => ( - - )} - - - ) : null} - + + )} + + {({ actionType }) => ( + + )} + import('./compare/RepositoryCompareArea'), 'RepositoryCompareArea') const RepositoryStatsArea = lazyComponent(() => import('./stats/RepositoryStatsArea'), 'RepositoryStatsArea') -const ActionItemsBar = lazyComponent( - () => import('../extensions/components/ActionItemsBar'), - 'ActionItemsBar' -) export const compareSpecPath = '/-/compare/*' @@ -30,15 +24,6 @@ export const repoContainerRoutes: readonly RepoContainerRoute[] = [ render: context => ( - {window.context.enableLegacyExtensions && ( - - )} ), }, @@ -55,15 +40,6 @@ export const repoContainerRoutes: readonly RepoContainerRoute[] = [ render: context => ( - {window.context.enableLegacyExtensions && ( - - )} ), }, diff --git a/client/web/src/repo/repoRevisionContainerRoutes.tsx b/client/web/src/repo/repoRevisionContainerRoutes.tsx index 7ecb37fbe8e..4ae1e5a1d87 100644 --- a/client/web/src/repo/repoRevisionContainerRoutes.tsx +++ b/client/web/src/repo/repoRevisionContainerRoutes.tsx @@ -2,19 +2,12 @@ import { TraceSpanProvider } from '@sourcegraph/observability-client' import { lazyComponent } from '@sourcegraph/shared/src/util/lazyComponent' import { LoadingSpinner } from '@sourcegraph/wildcard' -import { ActionItemsBarProps } from '../extensions/components/ActionItemsBar' - import { RepoRevisionContainerRoute } from './RepoRevisionContainer' const RepositoryCommitsPage = lazyComponent(() => import('./commits/RepositoryCommitsPage'), 'RepositoryCommitsPage') const RepositoryFileTreePage = lazyComponent(() => import('./RepositoryFileTreePage'), 'RepositoryFileTreePage') -const ActionItemsBar = lazyComponent( - () => import('../extensions/components/ActionItemsBar'), - 'ActionItemsBar' -) - // Work around the issue that react router can not match nested splats when the URL contains spaces // by expanding the repo matcher to an optional path of up to 10 segments. // @@ -42,16 +35,6 @@ export const repoRevisionContainerRoutes: readonly RepoRevisionContainerRoute[] render: props => ( - {window.context.enableLegacyExtensions && ( - - )} ), })), diff --git a/client/web/src/repo/tree/TreePage.tsx b/client/web/src/repo/tree/TreePage.tsx index 5a093d9728f..2f6312ca6e4 100644 --- a/client/web/src/repo/tree/TreePage.tsx +++ b/client/web/src/repo/tree/TreePage.tsx @@ -35,7 +35,6 @@ import { RepoBatchChangesButton } from '../../batches/RepoBatchChangesButton' import { CodeIntelligenceProps } from '../../codeintel' import { BreadcrumbSetters } from '../../components/Breadcrumbs' import { PageTitle } from '../../components/PageTitle' -import { ActionItemsBarProps } from '../../extensions/components/ActionItemsBar' import { RepositoryFields } from '../../graphql-operations' import { basename } from '../../util/path' import { FilePathBreadcrumbs } from '../FilePathBreadcrumbs' @@ -60,7 +59,6 @@ interface Props commitID: string revision: string globbing: boolean - useActionItemsBar: ActionItemsBarProps['useActionItemsBar'] isSourcegraphDotCom: boolean className?: string } @@ -85,7 +83,6 @@ export const TreePage: FC = ({ useBreadcrumb, codeIntelligenceEnabled, batchChangesEnabled, - useActionItemsBar, isSourcegraphDotCom, className, ...props diff --git a/client/web/src/search/SearchConsolePage.tsx b/client/web/src/search/SearchConsolePage.tsx index 5b6e117b069..95f401395c1 100644 --- a/client/web/src/search/SearchConsolePage.tsx +++ b/client/web/src/search/SearchConsolePage.tsx @@ -43,9 +43,7 @@ interface SearchConsolePageProps export const SearchConsolePage: React.FunctionComponent> = props => { const location = useLocation() const navigate = useNavigate() - const { globbing, streamSearch, extensionsController, isSourcegraphDotCom } = props - const extensionHostAPI = - extensionsController !== null && window.context.enableLegacyExtensions ? extensionsController.extHostAPI : null + const { globbing, streamSearch, isSourcegraphDotCom } = props const enableGoImportsSearchQueryTransform = useExperimentalFeatures( features => features.enableGoImportsSearchQueryTransform ) @@ -72,11 +70,10 @@ export const SearchConsolePage: React.FunctionComponent diff --git a/client/web/src/search/home/SearchPage.story.tsx b/client/web/src/search/home/SearchPage.story.tsx index e13f2c90a7e..c7dfece42c8 100644 --- a/client/web/src/search/home/SearchPage.story.tsx +++ b/client/web/src/search/home/SearchPage.story.tsx @@ -5,7 +5,6 @@ import { mockFetchSearchContexts, mockGetUserSearchContextNamespaces, } from '@sourcegraph/shared/src/testing/searchContexts/testHelpers' -import { extensionsController } from '@sourcegraph/shared/src/testing/searchTestHelpers' import { WebStory } from '../../components/WebStory' import { MockedFeatureFlagsProvider } from '../../featureFlags/MockedFeatureFlagsProvider' @@ -18,7 +17,6 @@ const defaultProps: SearchPageProps = { final: null, subjects: null, }, - extensionsController, telemetryService: NOOP_TELEMETRY_SERVICE, authenticatedUser: null, globbing: false, diff --git a/client/web/src/search/home/SearchPage.tsx b/client/web/src/search/home/SearchPage.tsx index 3dc8d0cc23a..a1b2bb02b79 100644 --- a/client/web/src/search/home/SearchPage.tsx +++ b/client/web/src/search/home/SearchPage.tsx @@ -4,7 +4,6 @@ import { mdiArrowRight } from '@mdi/js' import classNames from 'classnames' import { QueryExamples } from '@sourcegraph/branded/src/search-ui/components/QueryExamples' -import { ExtensionsControllerProps } from '@sourcegraph/shared/src/extensions/controller' import { PlatformContextProps } from '@sourcegraph/shared/src/platform/context' import { Settings } from '@sourcegraph/shared/src/schema/settings.schema' import { QueryState, SearchContextInputProps } from '@sourcegraph/shared/src/search' @@ -30,7 +29,6 @@ import styles from './SearchPage.module.scss' export interface SearchPageProps extends SettingsCascadeProps, TelemetryProps, - ExtensionsControllerProps<'extHostAPI' | 'executeCommand'>, PlatformContextProps<'settings' | 'sourcegraphURL' | 'updateSettings' | 'requestGraphQL'>, SearchContextInputProps, CodeInsightsProps { diff --git a/client/web/src/search/results/SearchResultsCacheProvider.tsx b/client/web/src/search/results/SearchResultsCacheProvider.tsx index f06c788701f..e867f7b10e4 100644 --- a/client/web/src/search/results/SearchResultsCacheProvider.tsx +++ b/client/web/src/search/results/SearchResultsCacheProvider.tsx @@ -1,13 +1,11 @@ import React, { createContext, Dispatch, SetStateAction, useContext, useEffect, useMemo, useState } from 'react' -import { Remote } from 'comlink' import { isEqual } from 'lodash' import { useNavigationType, useLocation } from 'react-router-dom' import { merge, of } from 'rxjs' import { last, share, throttleTime } from 'rxjs/operators' import { transformSearchQuery } from '@sourcegraph/shared/src/api/client/search' -import { FlatExtensionHostAPI } from '@sourcegraph/shared/src/api/contract' import { AggregateStreamingSearchResults, StreamSearchOptions } from '@sourcegraph/shared/src/search/stream' import { TelemetryService } from '@sourcegraph/shared/src/telemetry/telemetryService' import { useObservable } from '@sourcegraph/wildcard' @@ -39,7 +37,6 @@ export function useCachedSearchResults( streamSearch: SearchStreamingProps['streamSearch'], query: string, options: StreamSearchOptions, - extensionHostAPI: Promise> | null, telemetryService: TelemetryService ): AggregateStreamingSearchResults | undefined { const [cachedResults, setCachedResults] = useContext(SearchResultsCacheContext) @@ -55,11 +52,10 @@ export function useCachedSearchResults( () => transformSearchQuery({ query, - extensionHostAPIPromise: extensionHostAPI, enableGoImportsSearchQueryTransform, eventLogger, }), - [query, extensionHostAPI, enableGoImportsSearchQueryTransform] + [query, enableGoImportsSearchQueryTransform] ) const results = useObservable( diff --git a/client/web/src/search/results/SearchResultsInfoBar.test.tsx b/client/web/src/search/results/SearchResultsInfoBar.test.tsx index 9201b1a8912..31d6e994a37 100644 --- a/client/web/src/search/results/SearchResultsInfoBar.test.tsx +++ b/client/web/src/search/results/SearchResultsInfoBar.test.tsx @@ -3,7 +3,6 @@ import { NEVER } from 'rxjs' import { NOOP_TELEMETRY_SERVICE } from '@sourcegraph/shared/src/telemetry/telemetryService' import { MockedTestProvider } from '@sourcegraph/shared/src/testing/apollo' -import { extensionsController } from '@sourcegraph/shared/src/testing/searchTestHelpers' import { renderWithBrandedContext } from '@sourcegraph/wildcard/src/testing' import { SearchPatternType } from '../../graphql-operations' @@ -11,7 +10,6 @@ import { SearchPatternType } from '../../graphql-operations' import { SearchResultsInfoBar, SearchResultsInfoBarProps } from './SearchResultsInfoBar' const COMMON_PROPS: Omit = { - extensionsController, platformContext: { settings: NEVER, sourcegraphURL: 'https://sourcegraph.com' }, authenticatedUser: { id: 'userID', @@ -40,12 +38,6 @@ const renderSearchResultsInfoBar = ( ) describe('SearchResultsInfoBar', () => { - beforeAll(() => { - window.context = { - enableLegacyExtensions: false, - } as any - }) - test('code monitoring feature flag disabled', () => { expect( renderSearchResultsInfoBar({ query: 'foo type:diff', enableCodeMonitoring: false }).asFragment() diff --git a/client/web/src/search/results/SearchResultsInfoBar.tsx b/client/web/src/search/results/SearchResultsInfoBar.tsx index ee70d4e7c11..7088c167aa6 100644 --- a/client/web/src/search/results/SearchResultsInfoBar.tsx +++ b/client/web/src/search/results/SearchResultsInfoBar.tsx @@ -2,12 +2,7 @@ import React, { useMemo, useState } from 'react' import { mdiChevronDoubleUp, mdiChevronDoubleDown } from '@mdi/js' import classNames from 'classnames' -import { useLocation } from 'react-router-dom' -import { ContributableMenu } from '@sourcegraph/client-api' -import { ActionItem } from '@sourcegraph/shared/src/actions/ActionItem' -import { ActionsContainer } from '@sourcegraph/shared/src/actions/ActionsContainer' -import { ExtensionsControllerProps } from '@sourcegraph/shared/src/extensions/controller' import { PlatformContextProps } from '@sourcegraph/shared/src/platform/context' import { SearchPatternTypeProps, CaseSensitivityProps } from '@sourcegraph/shared/src/search' import { FilterKind, findFilter } from '@sourcegraph/shared/src/search/query/query' @@ -29,8 +24,7 @@ import { SearchActionsMenu } from './SearchActionsMenu' import styles from './SearchResultsInfoBar.module.scss' export interface SearchResultsInfoBarProps - extends ExtensionsControllerProps<'executeCommand' | 'extHostAPI'>, - TelemetryProps, + extends TelemetryProps, PlatformContextProps<'settings' | 'sourcegraphURL'>, SearchPatternTypeProps, Pick { @@ -74,7 +68,6 @@ export interface SearchResultsInfoBarProps export const SearchResultsInfoBar: React.FunctionComponent< React.PropsWithChildren > = props => { - const location = useLocation() const globalTypeFilter = useMemo( () => (props.query ? findFilter(props.query, 'type', FilterKind.Global)?.value?.value : undefined), [props.query] @@ -106,7 +99,7 @@ export const SearchResultsInfoBar: React.FunctionComponent< ) ), getSearchContextCreateAction(props.query, props.authenticatedUser), - getInsightsCreateAction(props.query, props.patternType, window.context.codeInsightsEnabled), + getInsightsCreateAction(props.query, props.patternType, window.context?.codeInsightsEnabled), ].filter((button): button is CreateAction => button !== null), [ props.authenticatedUser, @@ -125,15 +118,6 @@ export const SearchResultsInfoBar: React.FunctionComponent< [props.enableCodeMonitoring, props.patternType, props.query] ) - const extraContext = useMemo( - () => ({ - searchQuery: props.query || null, - patternType: props.patternType, - caseSensitive: props.caseSensitive, - }), - [props.query, props.patternType, props.caseSensitive] - ) - // Show/hide mobile filters menu const [showMobileFilters, setShowMobileFilters] = useState(false) const onShowMobileFiltersClicked = (): void => { @@ -142,8 +126,6 @@ export const SearchResultsInfoBar: React.FunctionComponent< props.onShowMobileFiltersChanged?.(newShowFilters) } - const { extensionsController } = props - return (