web: Remove extensions controller remnants from client/web (#61138)

Support for extensions, even legacy support, [had been removed](https://github.com/sourcegraph/sourcegraph/pull/47517)
for quite a while. Now it seems that none of the code actually needs the
extensions controller shim. At most they are registiring the current
user or document, but there seems to be no code that actually reads that
information.
This commit is contained in:
Felix Kling 2024-03-15 10:12:08 +01:00 committed by GitHub
parent 3fe06f6234
commit e067f9fa2d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
29 changed files with 19 additions and 557 deletions

View File

@ -143,7 +143,6 @@ ts_project(
"src/api/extension/util.ts",
"src/api/extension/utils/ReferenceCounter.ts",
"src/api/extension/worker.ts",
"src/api/features.ts",
"src/api/sharedEventLogger.ts",
"src/api/textDocumentTypes.ts",
"src/api/util.ts",
@ -214,7 +213,6 @@ ts_project(
"src/contributions/contributions.ts",
"src/deprecated-theme-utils.ts",
"src/extensions/controller.ts",
"src/extensions/createNoopLoadedController.ts",
"src/extensions/createSyncLoadedController.ts",
"src/extensions/extension.ts",
"src/extensions/extensionManifest.ts",

View File

@ -1,19 +0,0 @@
import type { Remote } from 'comlink'
import { from } from 'rxjs'
import { switchMap } from 'rxjs/operators'
import { memoizeObservable } from '@sourcegraph/common'
import { wrapRemoteObservable } from './client/api/common'
import type { FlatExtensionHostAPI } from './contract'
/**
* Typically used to display loading indicators re: ready state of extension features
*/
export const haveInitialExtensionsLoaded = memoizeObservable(
(extensionHostAPI: Promise<Remote<FlatExtensionHostAPI>>) =>
from(extensionHostAPI).pipe(
switchMap(extensionHost => wrapRemoteObservable(extensionHost.haveInitialExtensionsLoaded()))
),
() => 'haveInitialExtensionsLoaded' // only one instance
)

View File

@ -1,61 +0,0 @@
import type { Remote } from 'comlink'
import { type ExposedToClient, initMainThreadAPI } from '../api/client/mainthread-api'
import type { FlatExtensionHostAPI } from '../api/contract'
import { createExtensionHostAPI } from '../api/extension/extensionHostApi'
import { createExtensionHostState } from '../api/extension/extensionHostState'
import { pretendRemote, syncPromiseSubscription } from '../api/util'
import type { PlatformContext } from '../platform/context'
import { isSettingsValid } from '../settings/settings'
import type { Controller } from './controller'
export function createNoopController(platformContext: PlatformContext): Controller {
const api: Promise<{
remoteExtensionHostAPI: Remote<FlatExtensionHostAPI>
exposedToClient: ExposedToClient
}> = new Promise((resolve, reject) => {
platformContext.settings.subscribe(settingsCascade => {
;(async () => {
const [injectNewCodeintel, newSettingsGetter] = await Promise.all([
import('../codeintel/api').then(module => module.injectNewCodeintel),
import('../codeintel/legacy-extensions/api').then(module => module.newSettingsGetter),
])
if (!isSettingsValid(settingsCascade)) {
throw new Error('Settings are not valid')
}
const extensionHostState = createExtensionHostState(
{
clientApplication: 'sourcegraph',
initialSettings: settingsCascade,
},
null,
null
)
const extensionHostAPI = injectNewCodeintel(createExtensionHostAPI(extensionHostState), {
requestGraphQL: platformContext.requestGraphQL,
telemetryService: platformContext.telemetryService,
settings: newSettingsGetter(settingsCascade),
})
const remoteExtensionHostAPI = pretendRemote(extensionHostAPI)
const exposedToClient = initMainThreadAPI(remoteExtensionHostAPI, platformContext).exposedToClient
// We don't have to load any extensions so we are already done
extensionHostState.haveInitialExtensionsLoaded.next(true)
return { remoteExtensionHostAPI, exposedToClient }
})().then(resolve, reject)
})
})
return {
executeCommand: parameters => api.then(({ exposedToClient }) => exposedToClient.executeCommand(parameters)),
registerCommand: entryToRegister =>
syncPromiseSubscription(
api.then(({ exposedToClient }) => exposedToClient.registerCommand(entryToRegister))
),
extHostAPI: api.then(({ remoteExtensionHostAPI }) => remoteExtensionHostAPI),
unsubscribe: () => {},
}
}

View File

@ -173,7 +173,6 @@ ts_project(
"src/auth/icons.tsx",
"src/auth/withAuthenticatedUser.tsx",
"src/backend/diff.ts",
"src/backend/features.ts",
"src/backend/getPersistentCache.ts",
"src/backend/graphql.ts",
"src/backend/persistenceMapper.ts",
@ -1290,7 +1289,6 @@ ts_project(
"src/repo/blob/codemirror/react-interop.tsx",
"src/repo/blob/codemirror/scip-snapshot.ts",
"src/repo/blob/codemirror/search.tsx",
"src/repo/blob/codemirror/sourcegraph-extensions.ts",
"src/repo/blob/codemirror/static-highlights.tsx",
"src/repo/blob/codemirror/tooltips/CodyTooltip.tsx",
"src/repo/blob/codemirror/tooltips/HovercardView.tsx",
@ -1801,7 +1799,6 @@ ts_project(
"//:node_modules/apollo3-cache-persist",
"//:node_modules/bloomfilter",
"//:node_modules/classnames",
"//:node_modules/comlink",
"//:node_modules/copy-to-clipboard",
"//:node_modules/d3-time-format",
"//:node_modules/date-fns",

View File

@ -253,11 +253,7 @@ export const LegacyLayout: FC<LegacyLayoutProps> = props => {
)}
{needsSiteInit && !isSiteInit && <Navigate replace={true} to="/site-admin/init" />}
<ApplicationRoutes routes={props.routes} />
<GlobalContributions
key={3}
extensionsController={props.extensionsController}
platformContext={props.platformContext}
/>
<GlobalContributions key={3} />
{fuzzyFinder && (
<LazyFuzzyFinder
isVisible={isFuzzyFinderVisible}

View File

@ -10,8 +10,6 @@ import { logger } from '@sourcegraph/common'
import { type GraphQLClient, HTTPStatusError } from '@sourcegraph/http-client'
import { SharedSpanName, TraceSpanProvider } from '@sourcegraph/observability-client'
import { type FetchFileParameters, fetchHighlightedFileLineRanges } from '@sourcegraph/shared/src/backend/file'
import type { Controller as ExtensionsController } from '@sourcegraph/shared/src/extensions/controller'
import { createNoopController } from '@sourcegraph/shared/src/extensions/createNoopLoadedController'
import type { PlatformContext } from '@sourcegraph/shared/src/platform/context'
import { ShortcutProvider } from '@sourcegraph/shared/src/react-shortcuts'
import {
@ -91,7 +89,6 @@ const WILDCARD_THEME: WildcardTheme = {
*/
export class LegacySourcegraphWebApp extends React.Component<StaticAppConfig, LegacySourcegraphWebAppState> {
private readonly subscriptions = new Subscription()
private readonly extensionsController: ExtensionsController | null
constructor(props: StaticAppConfig) {
super(props)
@ -102,11 +99,6 @@ export class LegacySourcegraphWebApp extends React.Component<StaticAppConfig, Le
}),
})
this.extensionsController = createNoopController(basePlatformContext)
if (this.extensionsController !== null) {
this.subscriptions.add(this.extensionsController)
}
this.state = {
authenticatedUser: authenticatedUserValue,
settingsCascade: EMPTY_SETTINGS_CASCADE,
@ -189,10 +181,6 @@ export class LegacySourcegraphWebApp extends React.Component<StaticAppConfig, Le
// select the user's default search context.
this.setSelectedSearchContextSpecToDefault()
}
this.setWorkspaceSearchContext(this.state.selectedSearchContextSpec).catch(error => {
logger.error('Error sending search context to extensions!', error)
})
}
public componentWillUnmount(): void {
@ -223,7 +211,6 @@ export class LegacySourcegraphWebApp extends React.Component<StaticAppConfig, Le
authenticatedUser,
viewerSubject: this.state.viewerSubject,
settingsCascade: this.state.settingsCascade,
extensionsController: this.extensionsController,
}
const router = createBrowserRouter(
@ -287,9 +274,6 @@ export class LegacySourcegraphWebApp extends React.Component<StaticAppConfig, Le
private setSelectedSearchContextSpecWithNoChecks = (spec: string): void => {
this.setState({ selectedSearchContextSpec: spec })
this.setWorkspaceSearchContext(spec).catch(error => {
logger.error('Error sending search context to extensions', error)
})
}
private setSelectedSearchContextSpec = (spec: string): void => {
@ -334,14 +318,6 @@ export class LegacySourcegraphWebApp extends React.Component<StaticAppConfig, Le
)
}
private async setWorkspaceSearchContext(spec: string | undefined): Promise<void> {
if (this.extensionsController === null) {
return
}
const extensionHostAPI = await this.extensionsController.extHostAPI
await extensionHostAPI.setSearchContext(spec)
}
private fetchHighlightedFileLineRanges = (
parameters: FetchFileParameters,
force?: boolean | undefined

View File

@ -8,7 +8,6 @@ import { combineLatest, from, Subscription, fromEvent } from 'rxjs'
import { HTTPStatusError } from '@sourcegraph/http-client'
import { SharedSpanName, TraceSpanProvider } from '@sourcegraph/observability-client'
import type { ExtensionsControllerProps } from '@sourcegraph/shared/src/extensions/controller'
import type { PlatformContext } from '@sourcegraph/shared/src/platform/context'
import { ShortcutProvider } from '@sourcegraph/shared/src/react-shortcuts'
import {
@ -51,7 +50,6 @@ import { siteSubjectNoAdmin, viewerSubjectFromSettings } from './util/settings'
export interface StaticSourcegraphWebAppContext {
setSelectedSearchContextSpec: (spec: string) => void
platformContext: PlatformContext
extensionsController: ExtensionsControllerProps['extensionsController'] | null
}
export interface DynamicSourcegraphWebAppContext {
@ -235,7 +233,6 @@ export const SourcegraphWebApp: FC<SourcegraphWebAppProps> = props => {
const staticContext = {
setSelectedSearchContextSpec,
platformContext,
extensionsController: null,
} satisfies StaticSourcegraphWebAppContext
const dynamicContext = {

View File

@ -1,79 +0,0 @@
import { type Observable, from, concat } from 'rxjs'
import { switchMap } from 'rxjs/operators'
import type { HoverMerged } from '@sourcegraph/client-api'
import type { MaybeLoadingResult } from '@sourcegraph/codeintellify'
import { wrapRemoteObservable } from '@sourcegraph/shared/src/api/client/api/common'
import type { DocumentHighlight } from '@sourcegraph/shared/src/codeintel/legacy-extensions/api'
import type { ExtensionsControllerProps } from '@sourcegraph/shared/src/extensions/controller'
import {
type FileSpec,
type UIPositionSpec,
type RepoSpec,
type ResolvedRevisionSpec,
toURIWithPath,
} from '@sourcegraph/shared/src/util/url'
/**
* Fetches hover information for the given location.
*
* @param context the location
* @returns hover for the location
*/
export function getHover(
context: RepoSpec & ResolvedRevisionSpec & FileSpec & UIPositionSpec,
{ extensionsController }: ExtensionsControllerProps<'extHostAPI'>
): Observable<MaybeLoadingResult<HoverMerged | null>> {
return concat(
[{ isLoading: true, result: null }],
extensionsController !== null
? from(extensionsController.extHostAPI).pipe(
switchMap(extensionHost =>
wrapRemoteObservable(
extensionHost.getHover({
textDocument: {
uri: toURIWithPath(context),
},
position: {
character: context.position.character - 1,
line: context.position.line - 1,
},
})
)
)
)
: [{ isLoading: false, result: null }]
)
}
/**
* Fetches document highlight information for the given location.
*
* @param context the location
* @returns document highlights for the location
*/
export function getDocumentHighlights(
context: RepoSpec & ResolvedRevisionSpec & FileSpec & UIPositionSpec,
{ extensionsController }: ExtensionsControllerProps<'extHostAPI'>
): Observable<DocumentHighlight[]> {
return concat(
[[]],
extensionsController !== null
? from(extensionsController.extHostAPI).pipe(
switchMap(extensionHost =>
wrapRemoteObservable(
extensionHost.getDocumentHighlights({
textDocument: {
uri: toURIWithPath(context),
},
position: {
character: context.position.character - 1,
line: context.position.line - 1,
},
})
)
)
)
: [[]]
)
}

View File

@ -682,7 +682,6 @@ const HIGHLIGHTED_FILE_MOCK = {
}
export const defaultProps: ReferencesPanelProps = {
extensionsController: null,
telemetryService: NOOP_TELEMETRY_SERVICE,
telemetryRecorder: noOpTelemetryRecorder,
settingsCascade: {

View File

@ -13,7 +13,6 @@ import { type ErrorLike, logger, pluralize } from '@sourcegraph/common'
import { Position } from '@sourcegraph/extension-api-classes'
import type { FetchFileParameters } from '@sourcegraph/shared/src/backend/file'
import { displayRepoName } from '@sourcegraph/shared/src/components/RepoLink'
import type { ExtensionsControllerProps } from '@sourcegraph/shared/src/extensions/controller'
import { HighlightResponseFormat } from '@sourcegraph/shared/src/graphql-operations'
import type { PlatformContextProps } from '@sourcegraph/shared/src/platform/context'
import type { SettingsCascadeProps } from '@sourcegraph/shared/src/settings/settings'
@ -72,7 +71,6 @@ export interface ReferencesPanelProps
TelemetryProps,
TelemetryV2Props,
HoverThresholdProps,
ExtensionsControllerProps,
HighlightedFileLineRangesProps {
/** Whether to show the first loaded reference in mini code view */
jumpToFirst?: boolean

View File

@ -4,7 +4,6 @@ import classNames from 'classnames'
import { Position } from '@sourcegraph/extension-api-classes'
import { useQuery } from '@sourcegraph/http-client'
import { ExtensionsControllerProps } from '@sourcegraph/shared/src/extensions/controller'
import { PlatformContextProps } from '@sourcegraph/shared/src/platform/context'
import { SettingsCascadeProps } from '@sourcegraph/shared/src/settings/settings'
import { TelemetryV2Props } from '@sourcegraph/shared/src/telemetry'
@ -24,12 +23,7 @@ import { FETCH_HIGHLIGHTED_BLOB } from './ReferencesPanelQueries'
import styles from './ReferencesPanel.module.scss'
export interface SideBlobProps
extends TelemetryProps,
TelemetryV2Props,
SettingsCascadeProps,
PlatformContextProps,
ExtensionsControllerProps {
export interface SideBlobProps extends TelemetryProps, TelemetryV2Props, SettingsCascadeProps, PlatformContextProps {
repository: string
commitID: string
file: string
@ -53,7 +47,6 @@ export const SideBlob: FC<SideBlobProps> = props => {
wrapLines = true,
navigateToLineOnAnyClick = true,
searchPanelConfig,
extensionsController,
settingsCascade,
telemetryService,
telemetryRecorder,
@ -131,7 +124,6 @@ export const SideBlob: FC<SideBlobProps> = props => {
searchPanelConfig={searchPanelConfig}
className={classNames(className, styles.sideBlobCode)}
platformContext={platformContext}
extensionsController={extensionsController}
settingsCascade={settingsCascade}
telemetryService={telemetryService}
telemetryRecorder={telemetryRecorder}

View File

@ -4,7 +4,6 @@ import { subDays } from 'date-fns'
import { EMPTY, NEVER, type Observable, of } from 'rxjs'
import { subtypeOf } from '@sourcegraph/common'
import type { ActionItemComponentProps } from '@sourcegraph/shared/src/actions/ActionItem'
import type { SearchContextFields } from '@sourcegraph/shared/src/graphql-operations'
import { noOpTelemetryRecorder } from '@sourcegraph/shared/src/telemetry'
import {
@ -34,10 +33,6 @@ const config: Meta = {
export default config
const EXTENSIONS_CONTROLLER: ActionItemComponentProps['extensionsController'] = {
executeCommand: () => new Promise(resolve => setTimeout(resolve, 750)),
}
const PLATFORM_CONTEXT: CommunitySearchContextPageProps['platformContext'] = {
settings: NEVER,
sourcegraphURL: '',
@ -116,7 +111,6 @@ const commonProps = () =>
patternType: SearchPatternType.standard,
setPatternType: action('setPatternType'),
caseSensitive: false,
extensionsController: { ...EXTENSIONS_CONTROLLER },
platformContext: PLATFORM_CONTEXT,
setCaseSensitivity: action('setCaseSensitivity'),
activation: undefined,

View File

@ -8,7 +8,6 @@ import { catchError, startWith } from 'rxjs/operators'
import { SyntaxHighlightedSearchQuery } from '@sourcegraph/branded'
import { asError, isErrorLike } from '@sourcegraph/common'
import { displayRepoName } from '@sourcegraph/shared/src/components/RepoLink'
import type { ExtensionsControllerProps } from '@sourcegraph/shared/src/extensions/controller'
import type { PlatformContextProps } from '@sourcegraph/shared/src/platform/context'
import type { QueryState, SearchContextInputProps, SearchContextProps } from '@sourcegraph/shared/src/search'
import type { SettingsCascadeProps, Settings } from '@sourcegraph/shared/src/settings/settings'
@ -40,7 +39,6 @@ const specTypes: { [k in CommunitySearchContextSpecs]: number } = {
export interface CommunitySearchContextPageProps
extends SettingsCascadeProps<Settings>,
ExtensionsControllerProps<'executeCommand'>,
PlatformContextProps<'settings' | 'sourcegraphURL' | 'requestGraphQL' | 'telemetryRecorder'>,
SearchContextInputProps,
Pick<SearchContextProps, 'fetchSearchContextBySpec'> {

View File

@ -31,7 +31,6 @@ export const injectedAppConfig = {} as unknown as StaticInjectedAppConfig
export const staticWebAppConfig = {
setSelectedSearchContextSpec: () => {},
platformContext: NOOP_PLATFORM_CONTEXT as PlatformContext,
extensionsController: null,
} satisfies StaticSourcegraphWebAppContext
export const dynamicWebAppConfig = {

View File

@ -1,34 +1,10 @@
import { useEffect, useRef, useState } from 'react'
import { type NavigateFunction, useLocation, useNavigate } from 'react-router-dom'
import { Subscription } from 'rxjs'
import type { ExtensionsControllerProps } from '@sourcegraph/shared/src/extensions/controller'
import { registerHoverContributions } from '@sourcegraph/shared/src/hover/actions'
import type { PlatformContextProps } from '@sourcegraph/shared/src/platform/context'
interface Props extends ExtensionsControllerProps, PlatformContextProps {}
import { useEffect, useState } from 'react'
/**
* A component that registers global contributions. It is implemented as a React component so that its
* registrations use the React lifecycle.
*/
export function GlobalContributions(props: Props): null {
const { extensionsController, platformContext } = props
const location = useLocation()
const navigate = useNavigate()
// Location and navigate may be used by the hover contributions after they
// are initialized and closed over. To avoid stale data, we keep them in
// refs.
const locationRef = useRef(location)
const navigateRef = useRef(navigate)
useEffect(() => {
locationRef.current = location
navigateRef.current = navigate
}, [location, navigate])
export function GlobalContributions(): null {
const [error, setError] = useState<null | Error>(null)
useEffect(() => {
@ -38,24 +14,6 @@ export function GlobalContributions(props: Props): null {
.catch(setError)
}, [])
useEffect(() => {
const subscriptions = new Subscription()
if (extensionsController !== null) {
const historyOrNavigate: NavigateFunction = ((to: any, options: any): void =>
navigateRef.current?.(to, options)) as any
subscriptions.add(
registerHoverContributions({
platformContext,
historyOrNavigate,
getLocation: () => locationRef.current,
extensionsController,
locationAssign: globalThis.location.assign.bind(globalThis.location),
})
)
}
return () => subscriptions.unsubscribe()
}, [extensionsController, platformContext])
// Throw error to the <ErrorBoundary />
if (error) {
throw error

View File

@ -19,14 +19,13 @@ import { NEVER, of } from 'rxjs'
import { catchError, switchMap } from 'rxjs/operators'
import type { StreamingSearchResultsListProps } from '@sourcegraph/branded'
import { asError, type ErrorLike, isErrorLike, logger, repeatUntil } from '@sourcegraph/common'
import { asError, type ErrorLike, isErrorLike, repeatUntil } from '@sourcegraph/common'
import {
isCloneInProgressErrorLike,
isRepoSeeOtherErrorLike,
isRevisionNotFoundErrorLike,
} from '@sourcegraph/shared/src/backend/errors'
import { RepoQuestionIcon } from '@sourcegraph/shared/src/components/icons'
import type { ExtensionsControllerProps } from '@sourcegraph/shared/src/extensions/controller'
import { useKeyboardShortcut } from '@sourcegraph/shared/src/keyboardShortcuts/useKeyboardShortcut'
import type { PlatformContextProps } from '@sourcegraph/shared/src/platform/context'
import { Shortcut } from '@sourcegraph/shared/src/react-shortcuts'
@ -36,7 +35,6 @@ import { escapeSpaces } from '@sourcegraph/shared/src/search/query/filters'
import type { SettingsCascadeProps } from '@sourcegraph/shared/src/settings/settings'
import type { TelemetryProps } from '@sourcegraph/shared/src/telemetry/telemetryService'
import { lazyComponent } from '@sourcegraph/shared/src/util/lazyComponent'
import { makeRepoURI } from '@sourcegraph/shared/src/util/url'
import { LoadingSpinner, Panel, useObservable } from '@sourcegraph/wildcard'
import type { AuthenticatedUser } from '../auth'
@ -86,7 +84,6 @@ const RepoSettingsArea = lazyComponent(() => import('./settings/RepoSettingsArea
export interface RepoContainerContext
extends RepoHeaderContributionsLifecycleProps,
SettingsCascadeProps,
ExtensionsControllerProps,
PlatformContextProps,
HoverThresholdProps,
TelemetryProps,
@ -123,7 +120,6 @@ interface RepoContainerProps
extends SettingsCascadeProps<Settings>,
PlatformContextProps,
TelemetryProps,
ExtensionsControllerProps,
Pick<SearchContextProps, 'selectedSearchContextSpec' | 'searchContextsEnabled'>,
BreadcrumbSetters,
BreadcrumbsProps,
@ -330,7 +326,7 @@ const RepoUserContainer: FC<RepoUserContainerProps> = ({
repoHeaderContributionsLifecycleProps,
...props
}) => {
const { extensionsController, repoContainerRoutes, authenticatedUser, selectedSearchContextSpec } = props
const { repoContainerRoutes, authenticatedUser, selectedSearchContextSpec } = props
const location = useLocation()
@ -365,41 +361,6 @@ const RepoUserContainer: FC<RepoUserContainerProps> = ({
// The external links to show in the repository header, if any.
const [externalLinks, setExternalLinks] = useState<ExternalLinkFields[] | undefined>()
// Update the workspace roots service to reflect the current repo / resolved revision
useEffect(() => {
const workspaceRootUri =
resolvedRevisionOrError &&
!isErrorLike(resolvedRevisionOrError) &&
makeRepoURI({
repoName,
revision: resolvedRevisionOrError.commitID,
})
if (workspaceRootUri && extensionsController !== null) {
extensionsController.extHostAPI
.then(extensionHostAPI =>
extensionHostAPI.addWorkspaceRoot({
uri: workspaceRootUri,
inputRevision: revision || '',
})
)
.catch(error => {
logger.error('Error adding workspace root', error)
})
}
// Clear the Sourcegraph extensions model's roots when navigating away.
return () => {
if (workspaceRootUri && extensionsController !== null) {
extensionsController.extHostAPI
.then(extensionHostAPI => extensionHostAPI.removeWorkspaceRoot(workspaceRootUri))
.catch(error => {
logger.error('Error removing workspace root', error)
})
}
}
}, [extensionsController, repoName, resolvedRevisionOrError, revision])
// Update the navbar query to reflect the current repo / revision
const [enableV2QueryInput] = useV2QueryInput()
const queryPrefix = useMemo(

View File

@ -4,7 +4,6 @@ import { Route, Routes } from 'react-router-dom'
import type { StreamingSearchResultsListProps } from '@sourcegraph/branded'
import { isErrorLike } from '@sourcegraph/common'
import type { ExtensionsControllerProps } from '@sourcegraph/shared/src/extensions/controller'
import type { PlatformContextProps } from '@sourcegraph/shared/src/platform/context'
import type { SearchContextProps } from '@sourcegraph/shared/src/search'
import type { SettingsCascadeProps } from '@sourcegraph/shared/src/settings/settings'
@ -49,7 +48,6 @@ import styles from './RepoRevisionContainer.module.scss'
export interface RepoRevisionContainerContext
extends RepoHeaderContributionsLifecycleProps,
SettingsCascadeProps,
ExtensionsControllerProps,
PlatformContextProps,
TelemetryProps,
HoverThresholdProps,
@ -83,7 +81,6 @@ interface RepoRevisionContainerProps
PlatformContextProps,
TelemetryProps,
HoverThresholdProps,
ExtensionsControllerProps,
Pick<SearchContextProps, 'selectedSearchContextSpec' | 'searchContextsEnabled'>,
RevisionSpec,
BreadcrumbSetters,

View File

@ -21,7 +21,6 @@ import {
useCurrentSpan,
} from '@sourcegraph/observability-client'
import type { FetchFileParameters } from '@sourcegraph/shared/src/backend/file'
import type { ExtensionsControllerProps } from '@sourcegraph/shared/src/extensions/controller'
import { HighlightResponseFormat } from '@sourcegraph/shared/src/graphql-operations'
import type { PlatformContextProps } from '@sourcegraph/shared/src/platform/context'
import type { SearchContextProps } from '@sourcegraph/shared/src/search'
@ -100,7 +99,6 @@ interface BlobPageProps
PlatformContextProps,
TelemetryProps,
TelemetryV2Props,
ExtensionsControllerProps,
HoverThresholdProps,
BreadcrumbSetters,
SearchStreamingProps,
@ -609,7 +607,6 @@ export const BlobPage: React.FunctionComponent<BlobPageProps> = ({ className, co
blobInfo={{ ...blobInfoOrError, commitID }}
wrapCode={wrapCode}
platformContext={props.platformContext}
extensionsController={props.extensionsController}
settingsCascade={props.settingsCascade}
onHoverShown={props.onHoverShown}
telemetryService={props.telemetryService}

View File

@ -22,7 +22,6 @@ import {
} from '@sourcegraph/common'
import { getOrCreateCodeIntelAPI, type CodeIntelAPI } from '@sourcegraph/shared/src/codeintel/api'
import { editorHeight, useCodeMirror, useCompartment } from '@sourcegraph/shared/src/components/CodeMirrorEditor'
import type { ExtensionsControllerProps } from '@sourcegraph/shared/src/extensions/controller'
import { useKeyboardShortcut } from '@sourcegraph/shared/src/keyboardShortcuts/useKeyboardShortcut'
import type { PlatformContext, PlatformContextProps } from '@sourcegraph/shared/src/platform/context'
import { Shortcut } from '@sourcegraph/shared/src/react-shortcuts'
@ -65,7 +64,6 @@ import { navigateToLineOnAnyClickExtension } from './codemirror/navigate-to-any-
import { CodeMirrorContainer } from './codemirror/react-interop'
import { scipSnapshot } from './codemirror/scip-snapshot'
import { search, type SearchPanelConfig } from './codemirror/search'
import { sourcegraphExtensions } from './codemirror/sourcegraph-extensions'
import { staticHighlights, type Range } from './codemirror/static-highlights'
import { codyWidgetExtension } from './codemirror/tooltips/CodyTooltip'
import { HovercardView } from './codemirror/tooltips/HovercardView'
@ -98,7 +96,6 @@ export interface BlobProps
TelemetryProps,
TelemetryV2Props,
HoverThresholdProps,
ExtensionsControllerProps,
CodeMirrorBlobProps {
className: string
@ -217,7 +214,6 @@ export const CodeMirrorBlob: React.FunctionComponent<BlobProps> = props => {
wrapCode,
ariaLabel,
role,
extensionsController,
isBlameVisible,
blameHunks,
ocgVisibility,
@ -395,13 +391,6 @@ export const CodeMirrorBlob: React.FunctionComponent<BlobProps> = props => {
pinnedTooltip,
navigateToLineOnAnyClick ? navigateToLineOnAnyClickExtension(navigate) : codeIntelExtension,
syntaxHighlight.of(blobInfo),
extensionsController !== null && !navigateToLineOnAnyClick
? sourcegraphExtensions({
blobInfo,
initialSelection: position,
extensionsController,
})
: [],
blobProps,
blameDecorations,
wrapCodeSettings,
@ -426,7 +415,6 @@ export const CodeMirrorBlob: React.FunctionComponent<BlobProps> = props => {
staticHighlightRanges,
navigate,
blobInfo,
extensionsController,
isCodyEnabled,
openCodeGraphExtension,
codeIntelExtension,

View File

@ -1,165 +0,0 @@
/**
* This file contains CodeMirror extensions to integrate with Sourcegraph
* extensions.
*
* This integration is done in various ways, see the specific extensions for
* more information.
*/
import type { Extension } from '@codemirror/state'
import { type PluginValue, ViewPlugin, type ViewUpdate } from '@codemirror/view'
import type { Remote } from 'comlink'
import { combineLatest, EMPTY, from, type Observable, Subject, Subscription } from 'rxjs'
import { catchError, map, shareReplay, switchMap } from 'rxjs/operators'
import { type LineOrPositionOrRange, logger, lprToSelectionsZeroIndexed } from '@sourcegraph/common'
import type { FlatExtensionHostAPI } from '@sourcegraph/shared/src/api/contract'
import { haveInitialExtensionsLoaded } from '@sourcegraph/shared/src/api/features'
import type { ViewerId } from '@sourcegraph/shared/src/api/viewerTypes'
import type { RequiredExtensionsControllerProps } from '@sourcegraph/shared/src/extensions/controller'
import { toURIWithPath } from '@sourcegraph/shared/src/util/url'
import type { BlobInfo } from '../CodeMirrorBlob'
import { type SelectedLineRange, selectedLines } from './linenumbers'
/**
* Context holds all the information needed for CodeMirror extensions to
* communicate with the extensions host.
*/
interface Context {
viewerId: ViewerId
extensionsController: RequiredExtensionsControllerProps['extensionsController']
extensionHostAPI: Remote<FlatExtensionHostAPI>
blobInfo: BlobInfo
}
/**
* Enables integration with Sourcegraph extensions:
* - Document highlights
* - Hovercards (partially)
* - Selection updates
* - Reference panel warmup
*/
export function sourcegraphExtensions({
blobInfo,
initialSelection,
extensionsController,
}: {
blobInfo: BlobInfo
initialSelection: LineOrPositionOrRange
extensionsController: RequiredExtensionsControllerProps['extensionsController']
}): Extension {
const subscriptions = new Subscription()
// Initialize document and viewer as early as possible and make context
// available as observable
const contextObservable = from(
extensionsController.extHostAPI.then(async extensionHostAPI => {
const uri = toURIWithPath(blobInfo)
const [, viewerId] = await Promise.all([
// This call should be made before adding viewer, but since
// messages to web worker are handled in order, we can use Promise.all
extensionHostAPI.addTextDocumentIfNotExists({
uri,
languageId: blobInfo.mode,
text: blobInfo.content,
}),
extensionHostAPI.addViewerIfNotExists({
type: 'CodeEditor' as const,
resource: uri,
selections: lprToSelectionsZeroIndexed(initialSelection),
isActive: true,
}),
])
return [viewerId, extensionHostAPI] as [ViewerId, Remote<FlatExtensionHostAPI>]
})
).pipe(
catchError(() => {
logger.error('Unable to initialize extensions context')
return EMPTY
}),
map(([viewerId, extensionHostAPI]) => {
subscriptions.add(() => {
extensionHostAPI
.removeViewer(viewerId)
.catch(error => logger.error('Error removing viewer from extension host', error))
})
return {
blobInfo,
viewerId,
extensionHostAPI,
extensionsController,
}
}),
shareReplay(1)
)
return [
// This view plugin is used to have a way to cleanup any resources via the
// `destroy` method.
ViewPlugin.define(() => ({
destroy() {
subscriptions.unsubscribe()
},
})),
ViewPlugin.define(() => new SelectionManager(contextObservable)),
ViewPlugin.define(() => new WarmupReferencesManager(contextObservable)),
]
}
//
// Selection change notifier
//
/**
* The selection manager listens to CodeMirror selection changes and sends them
* to the extensions host.
*/
class SelectionManager implements PluginValue {
private nextSelection: Subject<SelectedLineRange> = new Subject()
private subscription = new Subscription()
constructor(context: Observable<Context>) {
this.subscription = combineLatest([context, this.nextSelection]).subscribe(([context, selection]) => {
// Used to convert SelectedLineRange type to a valid LineOrPositionOrRange type to keep TypeScript happy
let lprSelection: LineOrPositionOrRange = {}
if (selection) {
lprSelection =
selection.line !== selection.endLine
? { line: selection.line, endLine: selection.endLine }
: { line: selection.line, character: selection.character }
}
context.extensionHostAPI
.setEditorSelections(context.viewerId, lprToSelectionsZeroIndexed(lprSelection))
.catch(error => logger.error('Error updating editor selections on extension host', error))
})
}
public destroy(): void {
this.subscription.unsubscribe()
}
public update(update: ViewUpdate): void {
if (update.state.field(selectedLines) !== update.startState.field(selectedLines)) {
this.nextSelection.next(update.state.field(selectedLines))
}
}
}
class WarmupReferencesManager implements PluginValue {
private subscription: Subscription
constructor(context: Observable<Context>) {
this.subscription = context
.pipe(switchMap(context => haveInitialExtensionsLoaded(context.extensionsController.extHostAPI)))
.subscribe()
}
public destroy(): void {
this.subscription.unsubscribe()
}
}

View File

@ -19,7 +19,11 @@ type Unwrap<T> = T extends Observable<infer U> ? U : never
// WebHoverOverlay expects to be passed the overlay position. Since CodeMirror
// positions the element we always use the same value.
const dummyOverlayPosition = { left: 0, bottom: 0 }
const NOOP_OVERLAY_POSITION = { left: 0, bottom: 0 }
// WebHoverOverlay is shared witht the browser extension and expects to be passed
// an extension controller. This is a dummy value that is never used.
const NOOP_EXTENSION_CONTROLLER = { executeCommand: () => Promise.resolve(undefined) }
/**
* This class is responsible for rendering a WebHoverOverlay component as a
@ -126,14 +130,14 @@ export class HovercardView implements TooltipView {
platformContext={props.platformContext}
settingsCascade={props.settingsCascade}
telemetryService={props.telemetryService}
extensionsController={props.extensionsController}
extensionsController={NOOP_EXTENSION_CONTROLLER}
// Hover props
actionsOrError={actionsOrError}
hoverOrError={hoverOrError}
// CodeMirror handles the positioning but a
// non-nullable value must be passed for the
// hovercard to render
overlayPosition={dummyOverlayPosition}
overlayPosition={NOOP_OVERLAY_POSITION}
hoveredToken={hoveredToken}
pinOptions={{
showCloseButton: pinned,

View File

@ -7,7 +7,6 @@ import { type Panel, useBuiltinTabbedPanelViews } from '@sourcegraph/branded/src
import { PanelContent } from '@sourcegraph/branded/src/components/panel/views/PanelContent'
import { isDefined, isErrorLike } from '@sourcegraph/common'
import type { FetchFileParameters } from '@sourcegraph/shared/src/backend/file'
import type { ExtensionsControllerProps } from '@sourcegraph/shared/src/extensions/controller'
import type { Scalars } from '@sourcegraph/shared/src/graphql-operations'
import type { PlatformContextProps } from '@sourcegraph/shared/src/platform/context'
import type { Settings, SettingsCascadeOrError, SettingsCascadeProps } from '@sourcegraph/shared/src/settings/settings'
@ -27,7 +26,6 @@ interface Props
extends AbsoluteRepoFile,
ModeSpec,
SettingsCascadeProps,
ExtensionsControllerProps,
PlatformContextProps,
Pick<CodeIntelligenceProps, 'useCodeIntel'>,
OwnConfigProps,
@ -47,7 +45,6 @@ export type BlobPanelTabID = 'info' | 'def' | 'references' | 'impl' | 'typedef'
* A React hook that registers panel views for the blob.
*/
function useBlobPanelViews({
extensionsController,
revision,
filePath,
repoID,
@ -90,7 +87,6 @@ function useBlobPanelViews({
<ReferencesPanel
settingsCascade={settingsCascade}
platformContext={platformContext}
extensionsController={extensionsController}
telemetryService={telemetryService}
telemetryRecorder={telemetryRecorder}
key="references"
@ -154,7 +150,6 @@ function useBlobPanelViews({
position,
settingsCascade,
platformContext,
extensionsController,
telemetryService,
telemetryRecorder,
fetchHighlightedFileLineRanges,

View File

@ -48,7 +48,6 @@ describe('TreePage', () => {
subjects: null,
final: null,
},
extensionsController: null,
platformContext: {
settings: NEVER,
updateSettings: () => Promise.reject(new Error('updateSettings not implemented')),

View File

@ -18,18 +18,17 @@ import classNames from 'classnames'
import { Navigate } from 'react-router-dom'
import { catchError } from 'rxjs/operators'
import { asError, encodeURIPathComponent, type ErrorLike, isErrorLike, logger, basename } from '@sourcegraph/common'
import { asError, encodeURIPathComponent, type ErrorLike, isErrorLike, basename } from '@sourcegraph/common'
import { gql, useQuery } from '@sourcegraph/http-client'
import { fetchTreeEntries } from '@sourcegraph/shared/src/backend/repo'
import { displayRepoName } from '@sourcegraph/shared/src/components/RepoLink'
import type { ExtensionsControllerProps } from '@sourcegraph/shared/src/extensions/controller'
import type { PlatformContextProps } from '@sourcegraph/shared/src/platform/context'
import type { Settings } from '@sourcegraph/shared/src/schema/settings.schema'
import type { SearchContextProps } from '@sourcegraph/shared/src/search'
import type { SettingsCascadeProps } from '@sourcegraph/shared/src/settings/settings'
import { noOpTelemetryRecorder } from '@sourcegraph/shared/src/telemetry'
import type { TelemetryProps } from '@sourcegraph/shared/src/telemetry/telemetryService'
import { toPrettyBlobURL, toURIWithPath } from '@sourcegraph/shared/src/util/url'
import { toPrettyBlobURL } from '@sourcegraph/shared/src/util/url'
import {
Badge,
Button,
@ -81,7 +80,6 @@ const FILE_COMMITS_QUERY = gql`
`
export interface Props
extends SettingsCascadeProps<Settings>,
ExtensionsControllerProps,
PlatformContextProps,
TelemetryProps,
CodeIntelligenceProps,
@ -197,47 +195,8 @@ export const TreePage: FC<Props> = ({
})
const treeWithHistory = fileCommitData?.repository?.commit?.tree?.entries
const showCodeInsights =
!isErrorLike(settingsCascade.final) &&
!!settingsCascade.final?.experimentalFeatures?.codeInsights &&
settingsCascade.final['insights.displayLocation.directory'] === true
const showOwnership = ownEnabled && !isSourcegraphDotCom
// Add DirectoryViewer
const uri = toURIWithPath({ repoName, commitID, filePath })
const { extensionsController } = props
useEffect(() => {
if (!showCodeInsights || extensionsController === null) {
return
}
const viewerIdPromise = extensionsController.extHostAPI
.then(extensionHostAPI =>
extensionHostAPI.addViewerIfNotExists({
type: 'DirectoryViewer',
isActive: true,
resource: uri,
})
)
.catch(error => {
logger.error('Error adding viewer to extension host:', error)
return null
})
return () => {
Promise.all([extensionsController.extHostAPI, viewerIdPromise])
.then(([extensionHostAPI, viewerId]) => {
if (viewerId) {
return extensionHostAPI.removeViewer(viewerId)
}
return
})
.catch(error => logger.error('Error removing viewer from extension host:', error))
}
}, [uri, showCodeInsights, extensionsController])
const getPageTitle = (): string => {
const repoString = displayRepoName(repoName)
if (filePath) {

View File

@ -9,7 +9,6 @@ import { encodeURIPathComponent, numberWithCommas, pluralize } from '@sourcegrap
import { gql, useQuery } from '@sourcegraph/http-client'
import { TeamAvatar } from '@sourcegraph/shared/src/components/TeamAvatar'
import { UserAvatar } from '@sourcegraph/shared/src/components/UserAvatar'
import type { ExtensionsControllerProps } from '@sourcegraph/shared/src/extensions/controller'
import { SearchPatternType, type TreeFields } from '@sourcegraph/shared/src/graphql-operations'
import type { PlatformContextProps } from '@sourcegraph/shared/src/platform/context'
import type { TelemetryProps } from '@sourcegraph/shared/src/telemetry/telemetryService'
@ -146,7 +145,7 @@ const ExtraInfoSection: React.FC<{
)
}
interface TreePageContentProps extends ExtensionsControllerProps, TelemetryProps, PlatformContextProps {
interface TreePageContentProps extends TelemetryProps, PlatformContextProps {
filePath: string
tree: TreeFields
treeWithHistory?: TreeHistoryFields[]

View File

@ -55,7 +55,6 @@ const defaultProps: StreamingSearchResultsProps = {
searchAggregationEnabled: true,
codeMonitoringEnabled: true,
ownEnabled: true,
extensionsController: {} as any,
}
const decorator: Decorator = Story => {

View File

@ -58,7 +58,6 @@ describe('StreamingSearchResults', () => {
searchAggregationEnabled: false,
codeMonitoringEnabled: true,
ownEnabled: true,
extensionsController: {} as any,
}
const revisionsMockResponses = generateMockedResponses(GitRefType.GIT_BRANCH, 5, 'github.com/golang/oauth2')

View File

@ -5,7 +5,6 @@ import type { Observable } from 'rxjs'
import { limitHit, useUrlFilters } from '@sourcegraph/branded'
import type { FetchFileParameters } from '@sourcegraph/shared/src/backend/file'
import { ExtensionsControllerProps } from '@sourcegraph/shared/src/extensions/controller'
import { SearchPatternType } from '@sourcegraph/shared/src/graphql-operations'
import type { PlatformContextProps } from '@sourcegraph/shared/src/platform/context'
import type { QueryUpdate, SearchContextProps } from '@sourcegraph/shared/src/search'
@ -40,8 +39,7 @@ export interface StreamingSearchResultsProps
CodeInsightsProps,
SearchAggregationProps,
CodeMonitoringProps,
OwnConfigProps,
ExtensionsControllerProps {
OwnConfigProps {
authenticatedUser: AuthenticatedUser | null
isSourcegraphDotCom: boolean
fetchHighlightedFileLineRanges: (parameters: FetchFileParameters, force?: boolean) => Observable<string[][]>
@ -56,7 +54,6 @@ export const StreamingSearchResults: FC<StreamingSearchResultsProps> = props =>
searchAggregationEnabled,
codeMonitoringEnabled,
platformContext,
extensionsController,
} = props
const location = useLocation()
@ -273,7 +270,6 @@ export const StreamingSearchResults: FC<StreamingSearchResultsProps> = props =>
settingsCascade={props.settingsCascade}
telemetryService={telemetryService}
platformContext={platformContext}
extensionsController={extensionsController}
/>
)
}

View File

@ -17,7 +17,6 @@ import { Observable } from 'rxjs'
import { StreamingProgress, StreamingSearchResultsList, useSearchResultState } from '@sourcegraph/branded'
import { FetchFileParameters } from '@sourcegraph/shared/src/backend/file'
import { FilePrefetcher } from '@sourcegraph/shared/src/components/PrefetchableFile'
import { ExtensionsControllerProps } from '@sourcegraph/shared/src/extensions/controller'
import { HighlightResponseFormat, SearchPatternType } from '@sourcegraph/shared/src/graphql-operations'
import { PlatformContextProps } from '@sourcegraph/shared/src/platform/context'
import {
@ -73,7 +72,6 @@ interface NewSearchContentProps
extends TelemetryProps,
SettingsCascadeProps,
PlatformContextProps,
ExtensionsControllerProps,
SearchPatternTypeProps,
SearchPatternTypeMutationProps {
submittedURLQuery: string
@ -127,7 +125,6 @@ export const NewSearchContent: FC<NewSearchContentProps> = props => {
codeMonitoringEnabled,
options,
platformContext,
extensionsController,
onNavbarQueryChange,
onSearchSubmit,
onQuerySubmit,
@ -337,7 +334,6 @@ export const NewSearchContent: FC<NewSearchContentProps> = props => {
<FilePreviewPanel
blobInfo={previewBlob}
platformContext={platformContext}
extensionsController={extensionsController}
settingsCascade={settingsCascade}
telemetryService={telemetryService}
onClose={handleFilterPanelClose}
@ -381,17 +377,13 @@ const NewSearchSidebarWrapper: FC<PropsWithChildren<NewSearchSidebarWrapper>> =
)
}
interface FilePreviewPanelProps
extends PlatformContextProps,
SettingsCascadeProps,
ExtensionsControllerProps,
TelemetryProps {
interface FilePreviewPanelProps extends PlatformContextProps, SettingsCascadeProps, TelemetryProps {
blobInfo: SearchResultPreview
onClose: () => void
}
const FilePreviewPanel: FC<FilePreviewPanelProps> = props => {
const { blobInfo, onClose, platformContext, settingsCascade, extensionsController, telemetryService } = props
const { blobInfo, onClose, platformContext, settingsCascade, telemetryService } = props
const staticHighlights = useMemo(() => {
if (blobInfo.type === 'path') {
@ -440,7 +432,6 @@ const FilePreviewPanel: FC<FilePreviewPanelProps> = props => {
telemetryService={NOOP_TELEMETRY_SERVICE}
// TODO (dadlerj): update to use a real telemetry recorder
telemetryRecorder={noOpTelemetryRecorder}
extensionsController={extensionsController}
staticHighlightRanges={staticHighlights}
/>
</Suspense>