From ea689de4d74f2c9f5647a20bb31ec011aa69b8e0 Mon Sep 17 00:00:00 2001 From: Felix Kling Date: Thu, 25 Apr 2024 13:59:24 +0200 Subject: [PATCH] svelte: Add minimal reference panel (#62168) This PR adds a *very early* version of the reference panel. Things that work: - Fetch precise code intel information via GraphQL API - Opening the reference panel when clicking "find references" - "No references found" message - Open/close file preview - Error handling Things that don't work yet (to be iterated on): - Support for search-based code intel - Grouping of references, or repo indicator in general - Restore opened/selected preview on back/forward navigation I want to emphasize that this is by no means a "new" reference panel. In the interest of time it's an approximation of the reference panel in the React app. Some things have been simplified, such as omitting grouping of references. Worth noting is using the actual file page as file preview component. This is possible because pages are normal components. I'm still a bit unsure whether this is a good approach or not. - Pro: - Works nicely together with `preloadData` (except for data type casting). There is no need to duplicate the data loading logic or pluck out the right values from the preloaded data. That also means that data will be properly cached. - Ensures some consistency between the file page itself and wherever we show a file preview. - Con: - Might make working on the page itself more difficult since the "embedded" use case has to be kept in mind. - Additional props are required to adjust UI for the preview case. I'd like to give this a try and see how it works, possible also make the search results page use this for the preview panel. --- .../src/lib/CodeExcerpt.stories.svelte | 57 ++++++ .../src/lib/{search => }/CodeExcerpt.svelte | 34 +++- .../src/lib/CodeMirrorBlob.svelte | 57 +++--- client/web-sveltekit/src/lib/Scroller.svelte | 11 +- client/web-sveltekit/src/lib/TabPanel.svelte | 1 + client/web-sveltekit/src/lib/Tabs.svelte | 1 + .../DismissibleAlert.stories.svelte | 35 ++++ .../DismissibleAlert.svelte | 34 ++-- .../src/lib/repo/FileHeader.svelte | 28 ++- client/web-sveltekit/src/lib/repo/blob.ts | 16 +- client/web-sveltekit/src/lib/repo/utils.ts | 28 --- client/web-sveltekit/src/lib/shared.ts | 12 +- .../src/lib/wildcard/Alert.svelte | 3 - .../(validrev)/(code)/+layout.svelte | 66 ++++--- .../(validrev)/(code)/+layout.ts | 75 +++++++- .../(code)/-/blob/[...path]/+page.svelte | 34 ++-- .../(code)/-/blob/[...path]/+page.ts | 3 + .../(code)/-/tree/[...path]/+page.svelte | 2 +- .../(validrev)/(code)/FilePreview.svelte | 61 +++++++ .../(validrev)/(code)/ReferencePanel.gql | 27 +++ .../(validrev)/(code)/ReferencePanel.svelte | 162 ++++++++++++++++++ .../(code)/ReferencePanelCodeExcerpt.gql | 23 +++ .../(code)/ReferencePanelCodeExcerpt.svelte | 62 +++++++ .../(validrev)/(code)/layout.gql | 33 ++++ .../search/FileContentSearchResult.svelte | 4 +- .../routes/search/SymbolSearchResult.svelte | 4 +- 26 files changed, 742 insertions(+), 131 deletions(-) create mode 100644 client/web-sveltekit/src/lib/CodeExcerpt.stories.svelte rename client/web-sveltekit/src/lib/{search => }/CodeExcerpt.svelte (74%) create mode 100644 client/web-sveltekit/src/lib/global-notifications/DismissibleAlert.stories.svelte create mode 100644 client/web-sveltekit/src/routes/[...repo=reporev]/(validrev)/(code)/FilePreview.svelte create mode 100644 client/web-sveltekit/src/routes/[...repo=reporev]/(validrev)/(code)/ReferencePanel.gql create mode 100644 client/web-sveltekit/src/routes/[...repo=reporev]/(validrev)/(code)/ReferencePanel.svelte create mode 100644 client/web-sveltekit/src/routes/[...repo=reporev]/(validrev)/(code)/ReferencePanelCodeExcerpt.gql create mode 100644 client/web-sveltekit/src/routes/[...repo=reporev]/(validrev)/(code)/ReferencePanelCodeExcerpt.svelte diff --git a/client/web-sveltekit/src/lib/CodeExcerpt.stories.svelte b/client/web-sveltekit/src/lib/CodeExcerpt.stories.svelte new file mode 100644 index 00000000000..95713b15d0d --- /dev/null +++ b/client/web-sveltekit/src/lib/CodeExcerpt.stories.svelte @@ -0,0 +1,57 @@ + + + + + +

Default

+
+ +
+ +

Different start line

+
+ +
+ +

Hidden line numbers

+
+ +
+ +

Collapsed whitespace

+
+ +
+ +

With highlighted code

+
+ +
+
+ + diff --git a/client/web-sveltekit/src/lib/search/CodeExcerpt.svelte b/client/web-sveltekit/src/lib/CodeExcerpt.svelte similarity index 74% rename from client/web-sveltekit/src/lib/search/CodeExcerpt.svelte rename to client/web-sveltekit/src/lib/CodeExcerpt.svelte index c445281d6a2..2c36e716c1c 100644 --- a/client/web-sveltekit/src/lib/search/CodeExcerpt.svelte +++ b/client/web-sveltekit/src/lib/CodeExcerpt.svelte @@ -6,10 +6,20 @@ import { highlightNodeMultiline } from '$lib/common' import type { MatchGroupMatch } from '$lib/shared' + /** + * Number of the first line in the code excerpt. This is 1-indexed. + * Doesn't have any effect when `highlightedHTMLRows` or `hideLineNumbers` are set. + */ export let startLine: number - export let plaintextLines: string[] - export let highlightedHTMLRows: string[] | undefined = undefined + export let plaintextLines: readonly string[] + export let highlightedHTMLRows: readonly string[] | undefined = undefined export let matches: MatchGroupMatch[] = [] + /** + * Causes whitespace to *not* be preserved. Can be useful to ignore the leading whitespace in a code block, + * but will also remove any intentional whitespace formatting. + */ + export let collapseWhitespace = false + export let hideLineNumbers = false function highlightMatches(node: HTMLElement, matches: MatchGroupMatch[]) { const visibleRows = node.querySelectorAll('tr') @@ -39,14 +49,14 @@ } - + {#key matches} - {#if highlightedHTMLRows === undefined} + {#if highlightedHTMLRows === undefined || highlightedHTMLRows.length === 0} {#each plaintextLines as line, index} - {/each} @@ -77,11 +87,21 @@ :global(td.line::before) { content: attr(data-line); color: var(--text-muted); + padding-right: 1rem; } :global(td.code) { - white-space: pre; - padding-left: 1rem; + white-space: inherit; + } + + &.collapseWhitespace { + white-space: normal; + } + + &.hideLineNumbers { + :global(td.line::before) { + display: none; + } } } diff --git a/client/web-sveltekit/src/lib/CodeMirrorBlob.svelte b/client/web-sveltekit/src/lib/CodeMirrorBlob.svelte index 9a4bb6cad61..6896785b01b 100644 --- a/client/web-sveltekit/src/lib/CodeMirrorBlob.svelte +++ b/client/web-sveltekit/src/lib/CodeMirrorBlob.svelte @@ -149,7 +149,7 @@ export let highlights: string export let wrapLines: boolean = false export let selectedLines: LineOrPositionOrRange | null = null - export let codeIntelAPI: CodeIntelAPI + export let codeIntelAPI: CodeIntelAPI | null export let staticHighlightRanges: Range[] = [] /** * The initial scroll position when the editor is first mounted. @@ -187,31 +187,34 @@ filePath: blobInfo.filePath, languages: blobInfo.languages, } - $: codeIntelExtension = createCodeIntelExtension({ - api: { - api: codeIntelAPI, - documentInfo: documentInfo, - goToDefinition: (view, definition, options) => goToDefinition(documentInfo, view, definition, options), - openReferences, - openImplementations, - createTooltipView: options => new HovercardView(options.view, options.token, options.hovercardData), - }, - // TODO(fkling): Support tooltip pinning - pin: {}, - navigate: to => { - if (typeof to === 'number') { - if (to > 0) { - history.forward() - } else { - history.back() - } - } else { - goto(to.toString()) - } - }, - }) - $: lineWrapping = wrapLines ? EditorView.lineWrapping : [] - $: syntaxHighlighting = highlights ? syntaxHighlight.of({ content: blobInfo.content, lsif: highlights }) : [] + $: codeIntelExtension = codeIntelAPI + ? createCodeIntelExtension({ + api: { + api: codeIntelAPI, + documentInfo: documentInfo, + goToDefinition: (view, definition, options) => + goToDefinition(documentInfo, view, definition, options), + openReferences, + openImplementations, + createTooltipView: options => new HovercardView(options.view, options.token, options.hovercardData), + }, + // TODO(fkling): Support tooltip pinning + pin: {}, + navigate: to => { + if (typeof to === 'number') { + if (to > 0) { + history.forward() + } else { + history.back() + } + } else { + goto(to.toString()) + } + }, + }) + : null + $: lineWrapping = wrapLines ? EditorView.lineWrapping : null + $: syntaxHighlighting = highlights ? syntaxHighlight.of({ content: blobInfo.content, lsif: highlights }) : null $: staticHighlightExtension = staticHighlights(staticHighlightRanges) $: blameColumnExtension = showBlame @@ -225,7 +228,7 @@ } }, }) - : [] + : null $: blameDataExtension = blameDataFacet(blameData) // Reinitialize the editor when its content changes. Update only the extensions when they change. diff --git a/client/web-sveltekit/src/lib/Scroller.svelte b/client/web-sveltekit/src/lib/Scroller.svelte index 571c5ef7fd6..eef804e29c8 100644 --- a/client/web-sveltekit/src/lib/Scroller.svelte +++ b/client/web-sveltekit/src/lib/Scroller.svelte @@ -5,7 +5,7 @@
diff --git a/client/web-sveltekit/src/lib/TabPanel.svelte b/client/web-sveltekit/src/lib/TabPanel.svelte index f653f446941..2b474b10b05 100644 --- a/client/web-sveltekit/src/lib/TabPanel.svelte +++ b/client/web-sveltekit/src/lib/TabPanel.svelte @@ -35,5 +35,6 @@ div { flex: 1; min-height: 0; + overflow: hidden; } diff --git a/client/web-sveltekit/src/lib/Tabs.svelte b/client/web-sveltekit/src/lib/Tabs.svelte index 377dadac86c..21b363cb468 100644 --- a/client/web-sveltekit/src/lib/Tabs.svelte +++ b/client/web-sveltekit/src/lib/Tabs.svelte @@ -82,6 +82,7 @@ .tabs { display: flex; flex-direction: column; + height: 100%; } .tabs-header { diff --git a/client/web-sveltekit/src/lib/global-notifications/DismissibleAlert.stories.svelte b/client/web-sveltekit/src/lib/global-notifications/DismissibleAlert.stories.svelte new file mode 100644 index 00000000000..13f651ec85a --- /dev/null +++ b/client/web-sveltekit/src/lib/global-notifications/DismissibleAlert.stories.svelte @@ -0,0 +1,35 @@ + + + + + + {#key reset} + This is an info alert + This is a danger alert + This is a warning alert + {/key} + +
+ + +
+ + diff --git a/client/web-sveltekit/src/lib/global-notifications/DismissibleAlert.svelte b/client/web-sveltekit/src/lib/global-notifications/DismissibleAlert.svelte index 3f7f54bc32f..1036ed25608 100644 --- a/client/web-sveltekit/src/lib/global-notifications/DismissibleAlert.svelte +++ b/client/web-sveltekit/src/lib/global-notifications/DismissibleAlert.svelte @@ -10,6 +10,12 @@ function storageKeyForPartial(partialStorageKey: string): string { return `DismissibleAlert/${partialStorageKey}/dismissed` } + + export function clearDismissedAlertsState_TEST_ONLY(...partialStorageKeys: string[]): void { + for (const partialStorageKey of partialStorageKeys) { + localStorage.removeItem(storageKeyForPartial(partialStorageKey)) + } + }
-
+

{#each breadcrumbs as [name, path], index} + {@const last = index === breadcrumbs.length - 1} {' '} - + {#if index > 0} / {/if} + {#if last} + + {/if} {#if path} {name} {:else} - {name} {/if} diff --git a/client/web-sveltekit/src/lib/repo/blob.ts b/client/web-sveltekit/src/lib/repo/blob.ts index ff5fcd26c48..f03ea0d4ff1 100644 --- a/client/web-sveltekit/src/lib/repo/blob.ts +++ b/client/web-sveltekit/src/lib/repo/blob.ts @@ -5,6 +5,7 @@ import { get, type Readable, readable } from 'svelte/store' import { goto as svelteGoto } from '$app/navigation' import { page } from '$app/stores' +import { toPrettyBlobURL } from '$lib/shared' import { positionToOffset, type Definition, @@ -94,13 +95,18 @@ export async function goToDefinition( export function openReferences( view: EditorView, - _documentInfo: DocumentInfo, + documentInfo: DocumentInfo, occurrence: Definition['occurrence'] ): void { - const offset = positionToOffset(view.state.doc, occurrence.range.start) - if (offset) { - showTemporaryTooltip(view, 'Not supported yet: Find references', offset, 2000) - } + const url = toPrettyBlobURL({ + repoName: documentInfo.repoName, + revision: documentInfo.revision, + commitID: documentInfo.commitID, + filePath: documentInfo.filePath, + range: occurrence.range.withIncrementedValues(), + viewState: 'references', + }) + svelteGoto(url) } export function openImplementations( diff --git a/client/web-sveltekit/src/lib/repo/utils.ts b/client/web-sveltekit/src/lib/repo/utils.ts index 99b66c196bd..153321db622 100644 --- a/client/web-sveltekit/src/lib/repo/utils.ts +++ b/client/web-sveltekit/src/lib/repo/utils.ts @@ -1,33 +1,5 @@ -import { resolveRoute } from '$app/paths' - import type { ResolvedRevision } from '../../routes/[...repo=reporev]/+layout' -const TREE_ROUTE_ID = '/[...repo=reporev]/(validrev)/(code)/-/tree/[...path]' - -/** - * Returns a [segment, url] mapping for every segement in `path`. - * The URL for the last segment is empty. - * - * Example: - * 'foo/bar/baz' converts to - * [ - * ['foo', '//-/tree/foo'], - * ['bar', '//-/tree/foo/bar'], - * ['baz', '//-/tree/foo/bar/baz'], - * ] - * - */ -export function navFromPath(path: string, repo: string): [string, string][] { - const parts = path.split('/') - return parts - .slice(0, -1) - .map((part, index, all): [string, string] => [ - part, - resolveRoute(TREE_ROUTE_ID, { repo, path: all.slice(0, index + 1).join('/') }), - ]) - .concat([[parts.at(-1) ?? '', '']]) -} - export function getRevisionLabel( urlRevision: string | undefined, resolvedRevision: ResolvedRevision | null diff --git a/client/web-sveltekit/src/lib/shared.ts b/client/web-sveltekit/src/lib/shared.ts index 89876f8a2d2..67303affa0a 100644 --- a/client/web-sveltekit/src/lib/shared.ts +++ b/client/web-sveltekit/src/lib/shared.ts @@ -1,9 +1,13 @@ // We want to limit the number of imported modules as much as possible -export type { AbsoluteRepoFile } from '@sourcegraph/shared/src/util/url' - -export { parseRepoRevision, buildSearchURLQuery, makeRepoGitURI } from '@sourcegraph/shared/src/util/url' - +export { + parseRepoRevision, + buildSearchURLQuery, + makeRepoGitURI, + toPrettyBlobURL, + toRepoURL, + type AbsoluteRepoFile, +} from '@sourcegraph/shared/src/util/url' export { isCloneInProgressErrorLike, isRepoSeeOtherErrorLike, diff --git a/client/web-sveltekit/src/lib/wildcard/Alert.svelte b/client/web-sveltekit/src/lib/wildcard/Alert.svelte index 2bbf9d5efbf..7b2d9b9d166 100644 --- a/client/web-sveltekit/src/lib/wildcard/Alert.svelte +++ b/client/web-sveltekit/src/lib/wildcard/Alert.svelte @@ -21,9 +21,6 @@ --alert-content-padding: 0.5rem; --alert-background-color: var(--color-bg-1); - display: flex; - align-items: center; - overflow: hidden; position: relative; margin-bottom: 1rem; color: var(--body-color); diff --git a/client/web-sveltekit/src/routes/[...repo=reporev]/(validrev)/(code)/+layout.svelte b/client/web-sveltekit/src/routes/[...repo=reporev]/(validrev)/(code)/+layout.svelte index 147b201ef05..7673071a2e3 100644 --- a/client/web-sveltekit/src/routes/[...repo=reporev]/(validrev)/(code)/+layout.svelte +++ b/client/web-sveltekit/src/routes/[...repo=reporev]/(validrev)/(code)/+layout.svelte @@ -1,7 +1,7 @@
@@ -140,9 +151,18 @@ /> {/key} + + + - {#if lastCommit && selectedTab === null} - + {#if lastCommit} +
+ +
{/if}

@@ -201,29 +221,29 @@ } .bottom-panel { - background-color: var(--code-bg); --align-tabs: flex-start; - box-shadow: var(--bottom-panel-shadow); - max-height: 50vh; - overflow: hidden; + display: flex; + align-items: center; flex-flow: row nowrap; justify-content: space-between; - padding-right: 0.5rem; - max-width: 100%; + overflow: hidden; - :global(.tabs) { - flex-grow: 1; - height: 100%; - max-height: 100%; - } + box-shadow: var(--bottom-panel-shadow); + background-color: var(--code-bg); - :global(.tabs-header) { + :global([data-tab-header]) { border-bottom: 1px solid var(--border-color); } &.open { height: 30vh; + // Disable flex layout so that tabs simply fill the available space + display: block; + + .last-commit { + display: none; + } } } diff --git a/client/web-sveltekit/src/routes/[...repo=reporev]/(validrev)/(code)/+layout.ts b/client/web-sveltekit/src/routes/[...repo=reporev]/(validrev)/(code)/+layout.ts index c12f056e3d7..f1be454d3f5 100644 --- a/client/web-sveltekit/src/routes/[...repo=reporev]/(validrev)/(code)/+layout.ts +++ b/client/web-sveltekit/src/routes/[...repo=reporev]/(validrev)/(code)/+layout.ts @@ -2,6 +2,7 @@ import { dirname } from 'path' import { from } from 'rxjs' +import { SourcegraphURL } from '$lib/common' import { getGraphQLClient, infinityQuery, mapOrThrow } from '$lib/graphql' import { GitRefType } from '$lib/graphql-types' import { fetchSidebarFileTree } from '$lib/repo/api/tree' @@ -9,15 +10,25 @@ import { resolveRevision } from '$lib/repo/utils' import { parseRepoRevision } from '$lib/shared' import type { LayoutLoad } from './$types' -import { GitHistoryQuery, LastCommitQuery, RepositoryGitCommits, RepositoryGitRefs } from './layout.gql' +import { + GitHistoryQuery, + LastCommitQuery, + RepositoryGitCommits, + RepositoryGitRefs, + RepoPage_PreciseCodeIntel, +} from './layout.gql' const HISTORY_COMMITS_PER_PAGE = 20 +const REFERENCES_PER_PAGE = 20 -export const load: LayoutLoad = async ({ parent, params }) => { +export const load: LayoutLoad = async ({ parent, params, url }) => { const client = getGraphQLClient() const { repoName, revision = '' } = parseRepoRevision(params.repo) const parentPath = params.path ? dirname(params.path) : '' const resolvedRevision = resolveRevision(parent, revision) + const sgURL = SourcegraphURL.from(url) + const lineOrPosition = sgURL.lineRange + const view = sgURL.viewState // Prefetch the sidebar file tree for the parent path. // (we don't want to wait for the file tree to execute the query) @@ -85,6 +96,66 @@ export const load: LayoutLoad = async ({ parent, params }) => { }, }), + references: + view === 'references' && lineOrPosition?.line && lineOrPosition?.character + ? infinityQuery({ + client, + query: RepoPage_PreciseCodeIntel, + variables: from( + resolvedRevision.then(revspec => ({ + repoName, + revspec, + filePath: params.path ?? '', + first: REFERENCES_PER_PAGE, + // Line and character are 1-indexed, but the API expects 0-indexed + line: lineOrPosition.line - 1, + character: lineOrPosition.character! - 1, + afterCursor: null as string | null, + })) + ), + nextVariables: previousResult => { + if (previousResult?.data?.repository?.commit?.blob?.lsif?.references.pageInfo.hasNextPage) { + return { + afterCursor: + previousResult.data.repository.commit.blob.lsif.references.pageInfo.endCursor, + } + } + return undefined + }, + combine: (previousResult, nextResult) => { + if (!nextResult.data?.repository?.commit?.blob?.lsif) { + return nextResult + } + + const previousNodes = + previousResult.data?.repository?.commit?.blob?.lsif?.references?.nodes ?? [] + const nextNodes = nextResult.data?.repository?.commit?.blob?.lsif?.references?.nodes ?? [] + + return { + ...nextResult, + data: { + repository: { + ...nextResult.data.repository, + commit: { + ...nextResult.data.repository.commit, + blob: { + ...nextResult.data.repository.commit.blob, + lsif: { + ...nextResult.data.repository.commit.blob.lsif, + references: { + ...nextResult.data.repository.commit.blob.lsif.references, + nodes: [...previousNodes, ...nextNodes], + }, + }, + }, + }, + }, + }, + } + }, + }) + : null, + // Repository pickers queries (branch, tags and commits) getRepoBranches: (searchTerm: string) => getGraphQLClient() diff --git a/client/web-sveltekit/src/routes/[...repo=reporev]/(validrev)/(code)/-/blob/[...path]/+page.svelte b/client/web-sveltekit/src/routes/[...repo=reporev]/(validrev)/(code)/-/blob/[...path]/+page.svelte index 554757412e6..cb4aab1b1a4 100644 --- a/client/web-sveltekit/src/routes/[...repo=reporev]/(validrev)/(code)/-/blob/[...path]/+page.svelte +++ b/client/web-sveltekit/src/routes/[...repo=reporev]/(validrev)/(code)/-/blob/[...path]/+page.svelte @@ -32,6 +32,11 @@ export let data: PageData + // The following props control the look and file of the file page when used + // in a preview context. + export let embedded = false + export let disableCodeIntel = embedded + export const snapshot: Snapshot = { capture() { return cmblob?.getScrollSnapshot() ?? null @@ -64,7 +69,7 @@ $: if (!$combinedBlobData.blobPending) { blob = $combinedBlobData.blob highlights = $combinedBlobData.highlights - selectedPosition = SourcegraphURL.from($page.url).lineRange + selectedPosition = data.lineOrPosition } $: fileNotFound = $combinedBlobData.blobPending ? null : !$combinedBlobData.blob $: fileLoadingError = $combinedBlobData.blobPending ? null : !$combinedBlobData.blob && $combinedBlobData.blobError @@ -74,12 +79,14 @@ $: showBlame = viewMode === ViewMode.Blame $: showFormatted = isFormatted && viewMode === ViewMode.Default && !showBlame - $: codeIntelAPI = createCodeIntelAPI({ - settings: setting => (isErrorLike(settings?.final) ? undefined : settings?.final?.[setting]), - requestGraphQL(options) { - return from(graphQLClient.query(options.request, options.variables).then(toGraphQLResult)) - }, - }) + $: codeIntelAPI = disableCodeIntel + ? null + : createCodeIntelAPI({ + settings: setting => (isErrorLike(settings?.final) ? undefined : settings?.final?.[setting]), + requestGraphQL(options) { + return from(graphQLClient.query(options.request, options.variables).then(toGraphQLResult)) + }, + }) afterNavigate(event => { // Only restore scroll position when the user used the browser history to navigate back @@ -118,14 +125,21 @@ {#if data.compare} - + {data.compare.revisionToCompare} +{:else if embedded} + + + + + + {:else} - + {#await data.externalServiceType then externalServiceType} @@ -153,7 +167,7 @@ {/if} -{#if blob && !blob.binary && !data.compare} +{#if blob && !blob.binary && !data.compare && !embedded}
{ const { repoName, revision = '' } = parseRepoRevision(params.repo) const resolvedRevision = resolveRevision(parent, revision) const isBlame = url.searchParams.get('view') === 'blame' + const lineOrPosition = SourcegraphURL.from(url).lineRange // Create a BehaviorSubject so preloading does not create a subscriberless observable const blameData = new BehaviorSubject({ current: undefined, externalURLs: undefined }) @@ -41,6 +43,7 @@ export const load: PageLoad = ({ parent, params, url }) => { return { graphQLClient: client, + lineOrPosition, filePath: params.path, blob: resolvedRevision .then(resolvedRevision => diff --git a/client/web-sveltekit/src/routes/[...repo=reporev]/(validrev)/(code)/-/tree/[...path]/+page.svelte b/client/web-sveltekit/src/routes/[...repo=reporev]/(validrev)/(code)/-/tree/[...path]/+page.svelte index 5420f5893f0..0f7c7329ec5 100644 --- a/client/web-sveltekit/src/routes/[...repo=reporev]/(validrev)/(code)/-/tree/[...path]/+page.svelte +++ b/client/web-sveltekit/src/routes/[...repo=reporev]/(validrev)/(code)/-/tree/[...path]/+page.svelte @@ -24,7 +24,7 @@ {data.filePath} - {data.displayRepoName} - Sourcegraph - + diff --git a/client/web-sveltekit/src/routes/[...repo=reporev]/(validrev)/(code)/FilePreview.svelte b/client/web-sveltekit/src/routes/[...repo=reporev]/(validrev)/(code)/FilePreview.svelte new file mode 100644 index 00000000000..fb9c7a376f5 --- /dev/null +++ b/client/web-sveltekit/src/routes/[...repo=reporev]/(validrev)/(code)/FilePreview.svelte @@ -0,0 +1,61 @@ + + +
+ {#if $filePageData.pending} + + {:else if $filePageData.error} + + {$filePageData.error.message} +
+ Open file directly +
+ {:else if $filePageData.value} + + + + + + {/if} +
+ + diff --git a/client/web-sveltekit/src/routes/[...repo=reporev]/(validrev)/(code)/ReferencePanel.gql b/client/web-sveltekit/src/routes/[...repo=reporev]/(validrev)/(code)/ReferencePanel.gql new file mode 100644 index 00000000000..d26a928cc1f --- /dev/null +++ b/client/web-sveltekit/src/routes/[...repo=reporev]/(validrev)/(code)/ReferencePanel.gql @@ -0,0 +1,27 @@ +fragment ReferencePanel_LocationConnection on LocationConnection { + nodes { + ...ReferencePanel_Location + } +} + +fragment ReferencePanel_Location on Location { + ...ReferencePanelCodeExcerpt_Location + canonicalURL + resource { + name + repository { + name + id + } + } + range { + start { + line + character + } + end { + line + character + } + } +} diff --git a/client/web-sveltekit/src/routes/[...repo=reporev]/(validrev)/(code)/ReferencePanel.svelte b/client/web-sveltekit/src/routes/[...repo=reporev]/(validrev)/(code)/ReferencePanel.svelte new file mode 100644 index 00000000000..82ac7b1282b --- /dev/null +++ b/client/web-sveltekit/src/routes/[...repo=reporev]/(validrev)/(code)/ReferencePanel.svelte @@ -0,0 +1,162 @@ + + +
+ {#if showUsageInfo} + Hover over a symbol and click "Find references" to find references to the symbol. + {:else if showNoReferencesInfo} + No references found. + {:else} + + + +
    + {#each locations as location (location.canonicalURL)} + {@const selected = selectedLocation?.canonicalURL === location.canonicalURL} + +
  • selectedLocation = selected ? null : location} + > + + + + + + + {location.resource.name} + + + + {#if location.range} + :{location.range.start.line + 1}:{location.range.start.character + 1} + {/if} +
  • + {/each} +
+ {#if loading} + + {/if} +
+
+ {#if previewURL} + + + (selectedLocation = null)} /> + + {/if} +
+ {/if} +
+ + diff --git a/client/web-sveltekit/src/routes/[...repo=reporev]/(validrev)/(code)/ReferencePanelCodeExcerpt.gql b/client/web-sveltekit/src/routes/[...repo=reporev]/(validrev)/(code)/ReferencePanelCodeExcerpt.gql new file mode 100644 index 00000000000..b761e1094a2 --- /dev/null +++ b/client/web-sveltekit/src/routes/[...repo=reporev]/(validrev)/(code)/ReferencePanelCodeExcerpt.gql @@ -0,0 +1,23 @@ +fragment ReferencePanelCodeExcerpt_Location on Location { + resource { + content + path + commit { + oid + } + repository { + id + name + } + } + range { + start { + line + character + } + end { + line + character + } + } +} diff --git a/client/web-sveltekit/src/routes/[...repo=reporev]/(validrev)/(code)/ReferencePanelCodeExcerpt.svelte b/client/web-sveltekit/src/routes/[...repo=reporev]/(validrev)/(code)/ReferencePanelCodeExcerpt.svelte new file mode 100644 index 00000000000..df161ceea7b --- /dev/null +++ b/client/web-sveltekit/src/routes/[...repo=reporev]/(validrev)/(code)/ReferencePanelCodeExcerpt.svelte @@ -0,0 +1,62 @@ + + + + +{#if location.range && plaintextLines.length > 0} +
(visible = visible || event.detail)}> + +
+{:else} +
(no content information)
+{/if} diff --git a/client/web-sveltekit/src/routes/[...repo=reporev]/(validrev)/(code)/layout.gql b/client/web-sveltekit/src/routes/[...repo=reporev]/(validrev)/(code)/layout.gql index 2e42e8b571b..33906db8e3f 100644 --- a/client/web-sveltekit/src/routes/[...repo=reporev]/(validrev)/(code)/layout.gql +++ b/client/web-sveltekit/src/routes/[...repo=reporev]/(validrev)/(code)/layout.gql @@ -51,3 +51,36 @@ fragment GitHistory_HistoryConnection on GitCommitConnection { endCursor } } + +query RepoPage_PreciseCodeIntel( + $repoName: String! + $revspec: String! + $filePath: String! + $line: Int! + $character: Int! + $first: Int + $afterCursor: String +) { + repository(name: $repoName) { + id + commit(rev: $revspec) { + id + blob(path: $filePath) { + canonicalURL + lsif { + references(line: $line, character: $character, first: $first, after: $afterCursor) { + ...RepoPage_ReferencesLocationConnection + } + } + } + } + } +} + +fragment RepoPage_ReferencesLocationConnection on LocationConnection { + ...ReferencePanel_LocationConnection + pageInfo { + hasNextPage + endCursor + } +} diff --git a/client/web-sveltekit/src/routes/search/FileContentSearchResult.svelte b/client/web-sveltekit/src/routes/search/FileContentSearchResult.svelte index f3128701c37..23718c27dc9 100644 --- a/client/web-sveltekit/src/routes/search/FileContentSearchResult.svelte +++ b/client/web-sveltekit/src/routes/search/FileContentSearchResult.svelte @@ -14,7 +14,7 @@ import { observeIntersection } from '$lib/intersection-observer' import RepoStars from '$lib/repo/RepoStars.svelte' import { fetchFileRangeMatches } from '$lib/search/api/highlighting' - import CodeExcerpt from '$lib/search/CodeExcerpt.svelte' + import CodeExcerpt from '$lib/CodeExcerpt.svelte' import { rankContentMatch } from '$lib/search/results' import { getFileMatchUrl, type ContentMatch, rankByLine, rankPassthrough } from '$lib/shared' import { settings } from '$lib/stores' @@ -96,7 +96,7 @@ --> {#await highlightedHTMLRows} import { observeIntersection } from '$lib/intersection-observer' import { fetchFileRangeMatches } from '$lib/search/api/highlighting' - import CodeExcerpt from '$lib/search/CodeExcerpt.svelte' + import CodeExcerpt from '$lib/CodeExcerpt.svelte' import SymbolKind from '$lib/search/SymbolKind.svelte' import type { SymbolMatch } from '$lib/shared' @@ -46,7 +46,7 @@
{#await highlightedHTMLRows then result}
+ {line}