mirror of
https://github.com/sourcegraph/sourcegraph.git
synced 2026-02-06 18:51:59 +00:00
Merge 2bfc068f5e into f403bfc1ff
This commit is contained in:
commit
ff9cb2b73e
@ -244,10 +244,10 @@
|
||||
import { SymbolUsageKind } from '$lib/graphql-types'
|
||||
import Icon from '$lib/Icon.svelte'
|
||||
import LoadingSpinner from '$lib/LoadingSpinner.svelte'
|
||||
import DisplayRepoName from '$lib/repo/DisplayRepoName.svelte'
|
||||
import Scroller from '$lib/Scroller.svelte'
|
||||
import CodeHostIcon from '$lib/search/CodeHostIcon.svelte'
|
||||
import LoadingSkeleton from '$lib/search/dynamicFilters/LoadingSkeleton.svelte'
|
||||
import { displayRepoName, Occurrence } from '$lib/shared'
|
||||
import { Occurrence } from '$lib/shared'
|
||||
import { type SingleSelectTreeState, type TreeProvider } from '$lib/TreeView'
|
||||
import TreeView, { setTreeContext } from '$lib/TreeView.svelte'
|
||||
import type { DocumentInfo } from '$lib/web'
|
||||
@ -321,8 +321,7 @@
|
||||
<svelte:fragment let:entry>
|
||||
{#if entry.type === 'repo'}
|
||||
<span class="repo-entry" data-repo-name={entry.name}>
|
||||
<CodeHostIcon repository={entry.name} />
|
||||
{displayRepoName(entry.name)}
|
||||
<DisplayRepoName repoName={entry.name} kind={undefined} />
|
||||
</span>
|
||||
{:else}
|
||||
<span class="path-entry" data-repo-name={entry.repo} data-path={entry.path}>
|
||||
|
||||
@ -5,9 +5,8 @@
|
||||
import { observeIntersection } from '$lib/intersection-observer'
|
||||
import { pathHrefFactory } from '$lib/path'
|
||||
import DisplayPath from '$lib/path/DisplayPath.svelte'
|
||||
import DisplayRepoName from '$lib/repo/DisplayRepoName.svelte'
|
||||
import { fetchFileRangeMatches } from '$lib/search/api/highlighting'
|
||||
import CodeHostIcon from '$lib/search/CodeHostIcon.svelte'
|
||||
import { displayRepoName } from '$lib/shared'
|
||||
|
||||
import type { ExplorePanel_Usage } from './ExplorePanel.gql'
|
||||
|
||||
@ -73,8 +72,7 @@
|
||||
on:intersecting={event => (visible = visible || event.detail)}
|
||||
>
|
||||
<div class="header">
|
||||
<CodeHostIcon {repository} />
|
||||
<span class="repo-name"><DisplayPath path={displayRepoName(repository)} /></span>
|
||||
<span class="repo-name"><DisplayRepoName repoName={repository} kind={undefined} /></span>
|
||||
<span class="interpunct">⋅</span>
|
||||
<span class="file-name">
|
||||
<DisplayPath
|
||||
@ -134,14 +132,7 @@
|
||||
|
||||
.repo-name {
|
||||
:global([data-path-container]) {
|
||||
font-family: var(--font-family-base);
|
||||
font-weight: 500;
|
||||
font-size: var(--font-size-small);
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
:global([data-path-item]) {
|
||||
color: var(--text-title);
|
||||
}
|
||||
}
|
||||
|
||||
@ -150,6 +141,7 @@
|
||||
}
|
||||
|
||||
.file-name {
|
||||
font-size: var(--code-font-size);
|
||||
:global([data-path-container]) {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
@ -18,18 +18,18 @@
|
||||
import { nextSibling, onClickOutside, previousSibling } from '$lib/dom'
|
||||
import { getGraphQLClient } from '$lib/graphql'
|
||||
import Icon from '$lib/Icon.svelte'
|
||||
import { inferSplitCodeHost } from '$lib/repo/codehost'
|
||||
import CodeHostIcon from '$lib/repo/codehost/CodeHostIcon.svelte'
|
||||
import FileIcon from '$lib/repo/FileIcon.svelte'
|
||||
import CodeHostIcon from '$lib/search/CodeHostIcon.svelte'
|
||||
import EmphasizedLabel from '$lib/search/EmphasizedLabel.svelte'
|
||||
import SymbolKindIcon from '$lib/search/SymbolKindIcon.svelte'
|
||||
import { displayRepoName } from '$lib/shared'
|
||||
import { isViewportMobile } from '$lib/stores'
|
||||
import TabsHeader, { type Tab } from '$lib/TabsHeader.svelte'
|
||||
import { Alert, Input } from '$lib/wildcard'
|
||||
import Button from '$lib/wildcard/Button.svelte'
|
||||
|
||||
import { allHotkey, filesHotkey, reposHotkey, symbolsHotkey } from './keys'
|
||||
import { type CompletionSource, createFuzzyFinderSource } from './sources'
|
||||
import { isViewportMobile } from '$lib/stores'
|
||||
|
||||
export let open = false
|
||||
export let scope = ''
|
||||
@ -281,14 +281,14 @@
|
||||
{:else if $source.value?.results}
|
||||
{#each $source.value.results as item, index (item)}
|
||||
{@const repo = item.repository.name}
|
||||
{@const displayRepo = displayRepoName(repo)}
|
||||
{@const { kind, displayName } = inferSplitCodeHost(repo, undefined)}
|
||||
<li role="option" aria-selected={selectedOption === index} data-index={index}>
|
||||
{#if item.type === 'repo'}
|
||||
{@const matchOffset = repo.length - displayRepo.length}
|
||||
{@const matchOffset = repo.length - displayName.length}
|
||||
<a href="/{item.repository.name}" on:click={handleClick}>
|
||||
<span class="icon"><CodeHostIcon repository={item.repository.name} /></span>
|
||||
<span class="icon"><CodeHostIcon {kind} /></span>
|
||||
<span class="label"
|
||||
><EmphasizedLabel label={displayRepo} offset={matchOffset} /></span
|
||||
><EmphasizedLabel label={displayName} offset={matchOffset} /></span
|
||||
>
|
||||
<span class="info">{repo}</span>
|
||||
</a>
|
||||
@ -297,7 +297,7 @@
|
||||
<span class="icon"><SymbolKindIcon symbolKind={item.symbol.kind} /></span>
|
||||
<span class="label"><EmphasizedLabel label={item.symbol.name} /></span>
|
||||
<span class="info mono"
|
||||
>{#if !useScope}{displayRepo} · {/if}{item.file.path}</span
|
||||
>{#if !useScope}{displayName} · {/if}{item.file.path}</span
|
||||
>
|
||||
</a>
|
||||
{:else if item.type == 'file'}
|
||||
@ -309,7 +309,7 @@
|
||||
><EmphasizedLabel label={fileName} offset={folderName.length + 1} /></span
|
||||
>
|
||||
<span class="info mono">
|
||||
{#if !useScope}{displayRepo} · {/if}
|
||||
{#if !useScope}{displayName} · {/if}
|
||||
<EmphasizedLabel label={folderName} />
|
||||
</span>
|
||||
</a>
|
||||
|
||||
@ -79,15 +79,14 @@
|
||||
</span>
|
||||
|
||||
<style lang="scss">
|
||||
[data-path-container] {
|
||||
:where([data-path-container]) {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
align-items: baseline;
|
||||
gap: 0.125em;
|
||||
|
||||
white-space: pre-wrap;
|
||||
|
||||
font-weight: 400;
|
||||
font-size: var(--code-font-size);
|
||||
font-family: var(--code-font-family);
|
||||
|
||||
// Global so data-slash can be slotted in with the prefix
|
||||
@ -98,7 +97,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
[data-path-item] {
|
||||
:where([data-path-item]) {
|
||||
display: inline;
|
||||
white-space: nowrap;
|
||||
color: var(--text-body);
|
||||
|
||||
@ -0,0 +1,68 @@
|
||||
<script lang="ts" context="module">
|
||||
import { Story } from '@storybook/addon-svelte-csf'
|
||||
import type { ComponentProps } from 'svelte'
|
||||
|
||||
import { highlightRanges } from '$lib/dom'
|
||||
import { ExternalServiceKind } from '$lib/graphql-types'
|
||||
|
||||
import DisplayRepoName from './DisplayRepoName.svelte'
|
||||
|
||||
export const meta = {
|
||||
component: DisplayRepoName,
|
||||
}
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
const cases: ComponentProps<DisplayRepoName>[] = [
|
||||
{ repoName: 'github.com/sourcegraph/sourcegraph', kind: undefined },
|
||||
{ repoName: 'github.com/sourcegraph/sourcegraph', kind: ExternalServiceKind.GITHUB },
|
||||
{ repoName: 'github.com/sourcegraph/sourcegraph', kind: ExternalServiceKind.GITLAB },
|
||||
{ repoName: 'bitbucket.com/sourcegraph/sourcegraph', kind: ExternalServiceKind.BITBUCKETCLOUD },
|
||||
{ repoName: 'bitbucket.com/sourcegraph/sourcegraph', kind: ExternalServiceKind.BITBUCKETSERVER },
|
||||
{ repoName: 'bitbucket.com/sourcegraph/sourcegraph', kind: undefined },
|
||||
{ repoName: 'gitlab.com/sourcegraph/sourcegraph', kind: undefined },
|
||||
{ repoName: 'gitlab.com/sourcegraph/sourcegraph', kind: ExternalServiceKind.GITLAB },
|
||||
{ repoName: 'mytestrepo', kind: undefined },
|
||||
{ repoName: 'mytestrepo', kind: ExternalServiceKind.GITHUB },
|
||||
{ repoName: 'ghe.sgdev.org/sourcegraph/sourcegraph', kind: undefined },
|
||||
|
||||
// TODO(camdencheek): This is a scenario where, if we knew the external URL,
|
||||
// we could trim the hostname and put it in the tooltip. We can't safely do
|
||||
// this right now because an admin can name their repos whatever they want,
|
||||
// so we can't guarantee that the first element of a repo name is the host name
|
||||
// of the code host.
|
||||
{ repoName: 'ghe.sgdev.org/sourcegraph/sourcegraph', kind: ExternalServiceKind.GITHUB },
|
||||
]
|
||||
</script>
|
||||
|
||||
<Story name="Default">
|
||||
<h2>DisplayRepoName props</h2>
|
||||
<table>
|
||||
{#each cases as testCase}
|
||||
<tr>
|
||||
<th>{testCase.repoName}</th>
|
||||
<th>{testCase.kind}</th>
|
||||
<td><div><DisplayRepoName {...testCase} /><div /></div></td>
|
||||
</tr>
|
||||
{/each}
|
||||
</table>
|
||||
</Story>
|
||||
|
||||
<Story name="Highlighting works">
|
||||
<h2>"Graph" should be highlighted</h2>
|
||||
<span use:highlightRanges={{ ranges: [[17, 22]] }}
|
||||
><DisplayRepoName repoName="github.com/sourcegraph/conc" kind={undefined} /></span
|
||||
>
|
||||
</Story>
|
||||
|
||||
<style lang="scss">
|
||||
td,
|
||||
th {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
td > div {
|
||||
width: min-content;
|
||||
border: 1px solid black;
|
||||
}
|
||||
</style>
|
||||
59
client/web-sveltekit/src/lib/repo/DisplayRepoName.svelte
Normal file
59
client/web-sveltekit/src/lib/repo/DisplayRepoName.svelte
Normal file
@ -0,0 +1,59 @@
|
||||
<script context="module" lang="ts">
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { ExternalServiceKind } from '$lib/graphql-types'
|
||||
import DisplayPath from '$lib/path/DisplayPath.svelte'
|
||||
import Tooltip from '$lib/Tooltip.svelte'
|
||||
|
||||
import { inferSplitCodeHost } from './codehost'
|
||||
import CodeHostIcon from './codehost/CodeHostIcon.svelte'
|
||||
|
||||
export let repoName: string
|
||||
export let kind: ExternalServiceKind | undefined // No default to discourage inferring kind
|
||||
|
||||
$: ({ kind: inferredKind, codeHost, displayName } = inferSplitCodeHost(repoName, kind))
|
||||
</script>
|
||||
|
||||
<!--
|
||||
NOTE: the awkward formatting is intentional to ensure there is no extra
|
||||
whitespace in the DOM. This is necessary for correctly highlighting
|
||||
matches inside a repo name by offset.
|
||||
-->
|
||||
<span aria-label={repoName}
|
||||
><!--
|
||||
--><Tooltip tooltip={codeHost}
|
||||
><!--
|
||||
--><CodeHostIcon kind={inferredKind} inline /><!--
|
||||
--></Tooltip
|
||||
><!--
|
||||
--><span
|
||||
><!-- Include the tooltip text hidden in the DOM for the purpose of highlighting
|
||||
--><span class="hidden"
|
||||
>{codeHost ? codeHost + '/' : ''}</span
|
||||
><!--
|
||||
--><DisplayPath path={displayName} /><!--
|
||||
--></span
|
||||
><!--
|
||||
--></span
|
||||
>
|
||||
|
||||
<style lang="scss">
|
||||
span {
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
gap: 0.5em;
|
||||
|
||||
:global([data-icon]) {
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
:global([data-path-container]) {
|
||||
font-family: var(--font-family-base);
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -5,6 +5,7 @@
|
||||
import ShrinkablePath from '$lib/path/ShrinkablePath.svelte'
|
||||
import { DropdownMenu } from '$lib/wildcard'
|
||||
import { getButtonClassName } from '$lib/wildcard/Button'
|
||||
|
||||
import MobileFileSidePanelOpenButton from './MobileFileSidePanelOpenButton.svelte'
|
||||
|
||||
export let repoName: string
|
||||
@ -89,6 +90,7 @@
|
||||
gap: 0.5rem;
|
||||
align-items: center;
|
||||
margin: 0;
|
||||
font-size: var(--code-font-size);
|
||||
|
||||
:global([data-path-container]) {
|
||||
gap: 0.375em !important;
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
import Avatar from '$lib/Avatar.svelte'
|
||||
import { numberWithCommas } from '$lib/common'
|
||||
import type { GitRefType } from '$lib/graphql-types'
|
||||
import CodeHostIcon from '$lib/search/CodeHostIcon.svelte'
|
||||
import CodeHostIcon from '$lib/repo/codehost/CodeHostIcon.svelte'
|
||||
import Timestamp from '$lib/Timestamp.svelte'
|
||||
import Tooltip from '$lib/Tooltip.svelte'
|
||||
import { Badge } from '$lib/wildcard'
|
||||
@ -103,7 +103,7 @@
|
||||
<a href={url}>
|
||||
View on
|
||||
{#if serviceKind}
|
||||
<CodeHostIcon repository={serviceKind} disableTooltip />
|
||||
<CodeHostIcon kind={serviceKind} inline />
|
||||
{getHumanNameForCodeHost(serviceKind)}
|
||||
{:else}
|
||||
code host
|
||||
|
||||
@ -14,13 +14,11 @@
|
||||
|
||||
<script lang="ts">
|
||||
import Avatar from '$lib/Avatar.svelte'
|
||||
import Icon from '$lib/Icon.svelte'
|
||||
import { displayRepoName } from '$lib/shared'
|
||||
import Timestamp from '$lib/Timestamp.svelte'
|
||||
import Badge from '$lib/wildcard/Badge.svelte'
|
||||
|
||||
import DisplayRepoName from '../DisplayRepoName.svelte'
|
||||
import RepoStars from '../RepoStars.svelte'
|
||||
import { getHumanNameForCodeHost, getIconForCodeHost } from '../shared/codehost'
|
||||
|
||||
import type { RepoPopoverFragment } from './RepoPopover.gql'
|
||||
|
||||
@ -34,21 +32,11 @@
|
||||
<div class="root">
|
||||
{#if withHeader}
|
||||
<div class="header">
|
||||
<div class="left">
|
||||
<Icon icon={ILucideGitMerge} aria-hidden --icon-color="var(--primary)" inline />
|
||||
<h4>{displayRepoName(data.name)}</h4>
|
||||
<Badge variant="outlineSecondary" small pill>
|
||||
{data.isPrivate ? 'Private' : 'Public'}
|
||||
</Badge>
|
||||
</div>
|
||||
<div class="right">
|
||||
<Icon
|
||||
icon={getIconForCodeHost(data.externalRepository.serviceType)}
|
||||
--icon-color="var(--text-body)"
|
||||
inline
|
||||
/>
|
||||
<small>{getHumanNameForCodeHost(data.externalRepository.serviceType)}</small>
|
||||
</div>
|
||||
<!-- TODO: we should be able to get the service kind (not the service type) from the backend -->
|
||||
<h4><DisplayRepoName repoName={data.name} kind={undefined} /></h4>
|
||||
<Badge variant="outlineSecondary" small pill>
|
||||
{data.isPrivate ? 'Private' : 'Public'}
|
||||
</Badge>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@ -103,37 +91,13 @@
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
|
||||
.left {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
|
||||
h4 {
|
||||
color: var(--text-title);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
small {
|
||||
border: 1px solid var(--text-muted);
|
||||
color: var(--text-muted);
|
||||
padding: 0rem 0.5rem;
|
||||
border-radius: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.right {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
|
||||
small {
|
||||
color: var(--text-muted);
|
||||
}
|
||||
h4 {
|
||||
color: var(--text-title);
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,33 @@
|
||||
<script lang="ts">
|
||||
import type { SvelteHTMLElements } from 'svelte/elements'
|
||||
|
||||
import type { ExternalServiceKind } from '$lib/graphql-types'
|
||||
import Icon from '$lib/Icon.svelte'
|
||||
|
||||
import { getIconForServiceKind, getExternalServiceHumanName } from './index'
|
||||
|
||||
type $$Props = SvelteHTMLElements['svg'] & {
|
||||
/**
|
||||
* The service kind to render an icon for
|
||||
*/
|
||||
kind: ExternalServiceKind
|
||||
/**
|
||||
* Render the icon inline next to text.
|
||||
*/
|
||||
inline?: boolean
|
||||
}
|
||||
|
||||
export let kind: $$Props['kind']
|
||||
export let inline: $$Props['inline'] = false
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<Icon aria-label={getExternalServiceHumanName(kind)} icon={getIconForServiceKind(kind)} {inline} {...$$restProps} />
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
div {
|
||||
display: contents;
|
||||
--icon-color: currentColor;
|
||||
}
|
||||
</style>
|
||||
80
client/web-sveltekit/src/lib/repo/codehost/index.ts
Normal file
80
client/web-sveltekit/src/lib/repo/codehost/index.ts
Normal file
@ -0,0 +1,80 @@
|
||||
import { ExternalServiceKind } from '$lib/graphql-types'
|
||||
import type { IconComponent } from '$lib/Icon.svelte'
|
||||
|
||||
const icons: Partial<Record<ExternalServiceKind, IconComponent>> = {
|
||||
GITHUB: ISimpleIconsGithub,
|
||||
GITLAB: ISimpleIconsGitlab,
|
||||
BITBUCKETSERVER: ISimpleIconsBitbucket,
|
||||
BITBUCKETCLOUD: ISimpleIconsBitbucket,
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the SVG icon component for the given code host.
|
||||
*/
|
||||
export function getIconForServiceKind(kind: ExternalServiceKind): IconComponent {
|
||||
return icons[kind] ?? ILucideFolderGit2
|
||||
}
|
||||
|
||||
const humanNames: Record<ExternalServiceKind, string> = {
|
||||
AWSCODECOMMIT: 'AWS CodeCommit',
|
||||
AZUREDEVOPS: 'Azure DevOps',
|
||||
BITBUCKETCLOUD: 'Bitbucket',
|
||||
BITBUCKETSERVER: 'Bitbucket',
|
||||
GERRIT: 'Gerrit',
|
||||
GITHUB: 'GitHub',
|
||||
GITLAB: 'GitLab',
|
||||
GITOLITE: 'Gitolite',
|
||||
GOMODULES: 'Go Module',
|
||||
JVMPACKAGES: 'JVM Packages',
|
||||
NPMPACKAGES: 'NPM Packages',
|
||||
PAGURE: 'Pagure',
|
||||
PERFORCE: 'Perforce',
|
||||
PHABRICATOR: 'Phabricator',
|
||||
PYTHONPACKAGES: 'Python Packages',
|
||||
RUBYPACKAGES: 'Ruby Packages',
|
||||
RUSTPACKAGES: 'Rust Packages',
|
||||
OTHER: 'Unknown',
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the human-readable name for the given external service kind.
|
||||
*/
|
||||
export function getExternalServiceHumanName(kind: ExternalServiceKind): string {
|
||||
return humanNames[kind]
|
||||
}
|
||||
|
||||
const knownPrefixes: Partial<Record<string, ExternalServiceKind>> = {
|
||||
'github.com': ExternalServiceKind.GITHUB,
|
||||
'gitlab.com': ExternalServiceKind.GITLAB,
|
||||
'bitbucket.com': ExternalServiceKind.BITBUCKETCLOUD,
|
||||
}
|
||||
|
||||
const splitRepoNameRegex = /^(?<codehost>[^\.\/]+\.[^\.\/]+)\/(?<repo>.*)$/
|
||||
|
||||
/**
|
||||
* Attempts to infer the code host kind from the repo name and split the repo
|
||||
* name into a code host part and a display name part.
|
||||
*
|
||||
* If provided, the external service kind will be used instead of inferring the
|
||||
* kind from the repo name. Over time, we should attempt to provide a kind in
|
||||
* every place we display a repo name since guessing will not work for any
|
||||
* non-well-known repos.
|
||||
*
|
||||
* NOTE: there is no guarantee that the first element of a repo name is the
|
||||
* code host. Admins can name their repos whatever they want, so it's possible
|
||||
* we will incorrectly split if the chosen name matches the regex pattern.
|
||||
* We could theoretically use the external URLs to check this, but we do not
|
||||
* always have that information handy so we don't do that now.
|
||||
*/
|
||||
export function inferSplitCodeHost(
|
||||
repoName: string,
|
||||
kind: ExternalServiceKind | undefined
|
||||
): { kind: ExternalServiceKind; codeHost: string; displayName: string } {
|
||||
const matched = splitRepoNameRegex.exec(repoName)
|
||||
const codeHost = matched?.groups?.codehost ?? ''
|
||||
return {
|
||||
kind: kind ?? knownPrefixes[codeHost] ?? ExternalServiceKind.OTHER,
|
||||
codeHost,
|
||||
displayName: matched?.groups?.repo ?? repoName,
|
||||
}
|
||||
}
|
||||
@ -1,15 +0,0 @@
|
||||
<script lang="ts">
|
||||
import Icon from '$lib/Icon.svelte'
|
||||
import { getIconForCodeHost } from '$lib/repo/shared/codehost'
|
||||
import Tooltip from '$lib/Tooltip.svelte'
|
||||
|
||||
export let repository: string
|
||||
export let codeHost: string | undefined = undefined
|
||||
export let disableTooltip: boolean = false
|
||||
|
||||
$: hostName = repository.split('/')[0]
|
||||
</script>
|
||||
|
||||
<Tooltip tooltip={disableTooltip ? '' : hostName}>
|
||||
<Icon aria-label={hostName} icon={getIconForCodeHost(codeHost ?? hostName)} inline />
|
||||
</Tooltip>
|
||||
@ -121,10 +121,11 @@
|
||||
import KeyboardShortcut from '$lib/KeyboardShortcut.svelte'
|
||||
import LanguageIcon from '$lib/LanguageIcon.svelte'
|
||||
import Popover from '$lib/Popover.svelte'
|
||||
import { inferSplitCodeHost } from '$lib/repo/codehost'
|
||||
import CodeHostIcon from '$lib/repo/codehost/CodeHostIcon.svelte'
|
||||
import RepoPopover, { fetchRepoPopoverData } from '$lib/repo/RepoPopover/RepoPopover.svelte'
|
||||
import CodeHostIcon from '$lib/search/CodeHostIcon.svelte'
|
||||
import SymbolKindIcon from '$lib/search/SymbolKindIcon.svelte'
|
||||
import { TELEMETRY_FILTER_TYPES, displayRepoName, scanSearchQuery, type Filter } from '$lib/shared'
|
||||
import { TELEMETRY_FILTER_TYPES, scanSearchQuery, type Filter } from '$lib/shared'
|
||||
import { settings } from '$lib/stores'
|
||||
import { TELEMETRY_RECORDER } from '$lib/telemetry'
|
||||
import { delay } from '$lib/utils'
|
||||
@ -232,11 +233,12 @@
|
||||
|
||||
<Section items={sectionItems.repo} title="By repository" filterPlaceholder="Filter repositories">
|
||||
<svelte:fragment slot="item" let:item>
|
||||
{@const { kind: inferredKind, displayName } = inferSplitCodeHost(item.label, undefined)}
|
||||
<Popover showOnHover let:registerTrigger placement="right-start">
|
||||
<div use:registerTrigger>
|
||||
<SectionItem {...item} on:select={() => handleFilterSelect('repo')}>
|
||||
<CodeHostIcon slot="icon" disableTooltip repository={item.label} />
|
||||
<span slot="label">{displayRepoName(item.label)}</span>
|
||||
<CodeHostIcon slot="icon" inline kind={inferredKind} />
|
||||
<span slot="label">{displayName}</span>
|
||||
</SectionItem>
|
||||
</div>
|
||||
<svelte:fragment slot="content">
|
||||
|
||||
@ -90,18 +90,3 @@ export { getModeFromPath } from '@sourcegraph/shared/src/languages'
|
||||
export type { ActionItemAction } from '@sourcegraph/shared/src/actions/ActionItem'
|
||||
export { repositoryInsertText } from '@sourcegraph/shared/src/search/query/completion-utils'
|
||||
export { ThemeSetting, Theme } from '@sourcegraph/shared/src/theme-types'
|
||||
|
||||
// Copies of non-reusable code
|
||||
|
||||
// Currently defined in client/shared/src/components/RepoLink.tsx
|
||||
|
||||
/**
|
||||
* Returns the friendly display form of the repository name (e.g., removing "github.com/").
|
||||
*/
|
||||
export function displayRepoName(repoName: string): string {
|
||||
let parts = repoName.split('/')
|
||||
if (parts.length > 0 && parts[0].includes('.')) {
|
||||
parts = parts.slice(1) // remove hostname from repo name (reduce visual noise)
|
||||
}
|
||||
return parts.join('/')
|
||||
}
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
<script lang="ts">
|
||||
import type { LineOrPositionOrRange } from '$lib/common'
|
||||
import CodeHostIcon from '$lib/repo/codehost/CodeHostIcon.svelte'
|
||||
import { getHumanNameForCodeHost } from '$lib/repo/shared/codehost'
|
||||
import { getExternalURL } from '$lib/repo/url'
|
||||
import CodeHostIcon from '$lib/search/CodeHostIcon.svelte'
|
||||
import { TELEMETRY_RECORDER } from '$lib/telemetry'
|
||||
import Tooltip from '$lib/Tooltip.svelte'
|
||||
|
||||
@ -25,7 +25,7 @@
|
||||
on:click={handleOpenCodeHostClick}
|
||||
>
|
||||
{#if externalLink.serviceKind}
|
||||
<CodeHostIcon repository={externalLink.serviceKind} disableTooltip />
|
||||
<CodeHostIcon kind={externalLink.serviceKind} inline />
|
||||
<span data-action-label>
|
||||
{getHumanNameForCodeHost(externalLink.serviceKind)}
|
||||
</span>
|
||||
|
||||
@ -251,7 +251,7 @@ test.describe('file header', () => {
|
||||
// We specifically check the textContent here because this is what is
|
||||
// used to apply highlights. It must exactly equal the path (no additional
|
||||
// whitespace) or the highlights will be incorrectly offset.
|
||||
const pathContainer = page.locator('css=[data-path-container]').first()
|
||||
const pathContainer = page.getByText('src/readme.md')
|
||||
await expect(pathContainer).toHaveText(/^src\/readme.md$/)
|
||||
})
|
||||
|
||||
@ -271,7 +271,7 @@ test.describe('repo menu', () => {
|
||||
const url = `/${repoName}/-/blob/src/large-file-1.js`
|
||||
await page.goto(url)
|
||||
|
||||
await page.getByRole('heading', { name: 'sourcegraph/sourcegraph' }).click()
|
||||
await page.getByRole('button', { name: 'github.com/sourcegraph/sourcegraph' }).click()
|
||||
await page.getByRole('menuitem', { name: 'Go to repository root' }).click()
|
||||
await page.waitForURL(`/${repoName}`)
|
||||
})
|
||||
|
||||
@ -5,13 +5,12 @@
|
||||
import { navigating } from '$app/stores'
|
||||
import Commit from '$lib/Commit.svelte'
|
||||
import LoadingSpinner from '$lib/LoadingSpinner.svelte'
|
||||
import CodeHostIcon from '$lib/repo/codehost/CodeHostIcon.svelte'
|
||||
import RepositoryRevPicker from '$lib/repo/RepositoryRevPicker.svelte'
|
||||
import { getHumanNameForCodeHost } from '$lib/repo/shared/codehost'
|
||||
import Scroller, { type Capture as ScrollerCapture } from '$lib/Scroller.svelte'
|
||||
import CodeHostIcon from '$lib/search/CodeHostIcon.svelte'
|
||||
import { Alert, Badge } from '$lib/wildcard'
|
||||
|
||||
import RepositoryRevPicker from '$lib/repo/RepositoryRevPicker.svelte'
|
||||
|
||||
import type { PageData, Snapshot } from './$types'
|
||||
|
||||
export let data: PageData
|
||||
@ -100,7 +99,7 @@
|
||||
<a href={url}>
|
||||
View on
|
||||
{#if serviceKind}
|
||||
<CodeHostIcon repository={serviceKind} disableTooltip />
|
||||
<CodeHostIcon kind={serviceKind} inline />
|
||||
{getHumanNameForCodeHost(serviceKind)}
|
||||
{:else}
|
||||
code host
|
||||
|
||||
@ -174,7 +174,6 @@
|
||||
<nav aria-label="repository" use:sizeToFit={{ grow, shrink }}>
|
||||
<RepoMenu
|
||||
repoName={data.repoName}
|
||||
displayRepoName={data.displayRepoName}
|
||||
repoURL={data.repoURL}
|
||||
externalURL={data.resolvedRepository.externalURLs[0]?.url}
|
||||
externalServiceKind={data.resolvedRepository.externalURLs[0]?.serviceKind ?? undefined}
|
||||
|
||||
@ -3,7 +3,8 @@ import { error, redirect } from '@sveltejs/kit'
|
||||
import { loadMarkdownSyntaxHighlighting } from '$lib/common'
|
||||
import { getGraphQLClient, mapOrThrow, type GraphQLClient } from '$lib/graphql'
|
||||
import { GitRefType } from '$lib/graphql-types'
|
||||
import { CloneInProgressError, RepoNotFoundError, displayRepoName, parseRepoRevision } from '$lib/shared'
|
||||
import { inferSplitCodeHost } from '$lib/repo/codehost'
|
||||
import { CloneInProgressError, RepoNotFoundError, parseRepoRevision } from '$lib/shared'
|
||||
|
||||
import type { LayoutLoad } from './$types'
|
||||
import {
|
||||
@ -38,7 +39,7 @@ export const load: LayoutLoad = async ({ params, url, depends }) => {
|
||||
repoURL: '/' + params.repo,
|
||||
repoURLWithoutRevision: '/' + repoName,
|
||||
repoName,
|
||||
displayRepoName: displayRepoName(repoName),
|
||||
displayRepoName: inferSplitCodeHost(repoName, undefined).displayName,
|
||||
/**
|
||||
* Revision from URL
|
||||
*/
|
||||
|
||||
@ -7,10 +7,10 @@
|
||||
import Commit from '$lib/Commit.svelte'
|
||||
import { pluralize } from '$lib/common'
|
||||
import LoadingSpinner from '$lib/LoadingSpinner.svelte'
|
||||
import CodeHostIcon from '$lib/repo/codehost/CodeHostIcon.svelte'
|
||||
import FileDiff from '$lib/repo/FileDiff.svelte'
|
||||
import { getHumanNameForCodeHost } from '$lib/repo/shared/codehost'
|
||||
import Scroller, { type Capture as ScrollerCapture } from '$lib/Scroller.svelte'
|
||||
import CodeHostIcon from '$lib/search/CodeHostIcon.svelte'
|
||||
import { isViewportMobile } from '$lib/stores'
|
||||
import Alert from '$lib/wildcard/Alert.svelte'
|
||||
import Badge from '$lib/wildcard/Badge.svelte'
|
||||
@ -95,7 +95,7 @@
|
||||
<a href={url}>
|
||||
View on
|
||||
{#if serviceKind}
|
||||
<CodeHostIcon repository={serviceKind} disableTooltip />
|
||||
<CodeHostIcon kind={serviceKind} inline />
|
||||
{getHumanNameForCodeHost(serviceKind)}
|
||||
{:else}
|
||||
code host
|
||||
|
||||
@ -3,7 +3,8 @@
|
||||
|
||||
import { invalidate } from '$app/navigation'
|
||||
import HeroPage from '$lib/HeroPage.svelte'
|
||||
import { displayRepoName, type CloneInProgressError } from '$lib/shared'
|
||||
import { inferSplitCodeHost } from '$lib/repo/codehost'
|
||||
import { type CloneInProgressError } from '$lib/shared'
|
||||
|
||||
// TODO: Find a way to make this type stricter
|
||||
export let error: App.Error | null
|
||||
@ -19,7 +20,7 @@
|
||||
})
|
||||
</script>
|
||||
|
||||
<HeroPage title={displayRepoName(repoName)} icon={ILucideFolderGit2}>
|
||||
<HeroPage title={inferSplitCodeHost(repoName, undefined).displayName} icon={ILucideFolderGit2}>
|
||||
<code><pre>{progress}</pre></code>
|
||||
<!--TODO add DirectImportRepoAlert -->
|
||||
</HeroPage>
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
<script lang="ts">
|
||||
import { openFuzzyFinder } from '$lib/fuzzyfinder/FuzzyFinderContainer.svelte'
|
||||
import { reposHotkey } from '$lib/fuzzyfinder/keys'
|
||||
import type { ExternalServiceKind } from '$lib/graphql-types'
|
||||
import Icon from '$lib/Icon.svelte'
|
||||
import KeyboardShortcut from '$lib/KeyboardShortcut.svelte'
|
||||
import DisplayRepoName from '$lib/repo/DisplayRepoName.svelte'
|
||||
import { getHumanNameForCodeHost } from '$lib/repo/shared/codehost'
|
||||
import CodeHostIcon from '$lib/search/CodeHostIcon.svelte'
|
||||
import { getButtonClassName } from '$lib/wildcard/Button'
|
||||
import DropdownMenu from '$lib/wildcard/menu/DropdownMenu.svelte'
|
||||
import MenuButton from '$lib/wildcard/menu/MenuButton.svelte'
|
||||
@ -12,24 +13,16 @@
|
||||
import MenuSeparator from '$lib/wildcard/menu/MenuSeparator.svelte'
|
||||
|
||||
export let repoName: string
|
||||
export let displayRepoName: string
|
||||
export let repoURL: string
|
||||
|
||||
export let externalURL: string | undefined
|
||||
export let externalServiceKind: string | undefined
|
||||
export let externalServiceKind: ExternalServiceKind | undefined
|
||||
</script>
|
||||
|
||||
<DropdownMenu triggerButtonClass="{getButtonClassName({ variant: 'text' })} triggerButton">
|
||||
<svelte:fragment slot="trigger">
|
||||
<div class="trigger">
|
||||
<CodeHostIcon repository={repoName} codeHost={externalServiceKind} />
|
||||
<h2>
|
||||
{#each displayRepoName.split('/') as segment, i}
|
||||
{#if i > 0}<span class="slash">/</span>{/if}{segment}
|
||||
{/each}
|
||||
</h2>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
<h2 slot="trigger">
|
||||
<DisplayRepoName {repoName} kind={externalServiceKind} />
|
||||
</h2>
|
||||
|
||||
<MenuLink href={repoURL}>
|
||||
<div class="menu-item">
|
||||
@ -63,8 +56,7 @@
|
||||
{/if}
|
||||
</small>
|
||||
<div class="repo-name">
|
||||
<CodeHostIcon repository={repoName} codeHost={externalServiceKind} />
|
||||
<span>{displayRepoName}</span>
|
||||
<DisplayRepoName {repoName} kind={externalServiceKind} />
|
||||
</div>
|
||||
<div class="external-link-icon">
|
||||
<Icon icon={ILucideExternalLink} aria-hidden />
|
||||
@ -78,25 +70,14 @@
|
||||
:global(.triggerButton) {
|
||||
border-radius: 0;
|
||||
}
|
||||
.trigger {
|
||||
--icon-color: currentColor;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
white-space: nowrap;
|
||||
|
||||
h2 {
|
||||
font-size: var(--font-size-large);
|
||||
h2 {
|
||||
margin: 0;
|
||||
font-size: var(--font-size-base);
|
||||
:global([data-path-container]) {
|
||||
font-weight: 500;
|
||||
margin: 0;
|
||||
|
||||
.slash {
|
||||
font-weight: 400;
|
||||
color: var(--text-muted);
|
||||
margin: 0.25em;
|
||||
letter-spacing: -0.25px;
|
||||
}
|
||||
}
|
||||
:global([data-slash]) {
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -49,10 +49,14 @@ fragment ResolvedRepository on Repository {
|
||||
abbrevName
|
||||
}
|
||||
externalURLs {
|
||||
url
|
||||
serviceKind
|
||||
...RepositoryExternalLink
|
||||
}
|
||||
...RepoPage_ResolvedRevision
|
||||
...BlobPage_ResolvedRevision
|
||||
...CodySidebar_ResolvedRevision
|
||||
}
|
||||
|
||||
fragment RepositoryExternalLink on ExternalLink {
|
||||
url
|
||||
serviceKind
|
||||
}
|
||||
|
||||
@ -22,12 +22,13 @@
|
||||
|
||||
import { buildSearchURLQuery } from '@sourcegraph/shared/src/util/url'
|
||||
|
||||
import { ExternalServiceKind } from '$lib/graphql-types'
|
||||
import LoadingSpinner from '$lib/LoadingSpinner.svelte'
|
||||
import CodeHostIcon from '$lib/search/CodeHostIcon.svelte'
|
||||
import { inferSplitCodeHost } from '$lib/repo/codehost'
|
||||
import CodeHostIcon from '$lib/repo/codehost/CodeHostIcon.svelte'
|
||||
import SearchInput from '$lib/search/input/SearchInput.svelte'
|
||||
import { queryStateStore } from '$lib/search/state'
|
||||
import SyntaxHighlightedQuery from '$lib/search/SyntaxHighlightedQuery.svelte'
|
||||
import { displayRepoName } from '$lib/shared'
|
||||
import { settings } from '$lib/stores'
|
||||
import { TELEMETRY_RECORDER } from '$lib/telemetry'
|
||||
import { Alert, Button, Markdown } from '$lib/wildcard'
|
||||
@ -123,11 +124,13 @@
|
||||
rel="noopener noreferrer"
|
||||
on:click={handleExternalRepoLinkClick}
|
||||
>
|
||||
<CodeHostIcon repository={repo.name} />
|
||||
<CodeHostIcon
|
||||
kind={repo.externalURLs[0].serviceKind ?? ExternalServiceKind.OTHER}
|
||||
/>
|
||||
</a>
|
||||
{/if}
|
||||
<a href="/{repo.name}" on:click={handleRepoLinkClick}>
|
||||
{displayRepoName(repo.name)}
|
||||
{inferSplitCodeHost(repo.name, undefined).displayName}
|
||||
</a>
|
||||
</li>
|
||||
{/each}
|
||||
|
||||
@ -5,6 +5,7 @@ query CommunitySearchPage_SearchContext($spec: String!) {
|
||||
name
|
||||
externalURLs {
|
||||
url
|
||||
serviceKind
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -47,6 +47,7 @@
|
||||
|
||||
.path {
|
||||
display: contents;
|
||||
font-size: var(--code-font-size);
|
||||
}
|
||||
|
||||
.interpunct {
|
||||
|
||||
@ -2,9 +2,8 @@
|
||||
import { highlightRanges } from '$lib/dom'
|
||||
import { getGraphQLClient } from '$lib/graphql'
|
||||
import Popover from '$lib/Popover.svelte'
|
||||
import DisplayRepoName from '$lib/repo/DisplayRepoName.svelte'
|
||||
import { default as RepoPopover, fetchRepoPopoverData } from '$lib/repo/RepoPopover/RepoPopover.svelte'
|
||||
import CodeHostIcon from '$lib/search/CodeHostIcon.svelte'
|
||||
import { displayRepoName } from '$lib/shared'
|
||||
import { delay } from '$lib/utils'
|
||||
import Alert from '$lib/wildcard/Alert.svelte'
|
||||
|
||||
@ -15,52 +14,38 @@
|
||||
const client = getGraphQLClient()
|
||||
|
||||
$: href = `/${repoName}${rev ? `@${rev}` : ''}`
|
||||
$: displayName = displayRepoName(repoName)
|
||||
$: if (displayName !== repoName) {
|
||||
// We only display part of the repository name, therefore we have to
|
||||
// adjust the match ranges for highlighting
|
||||
const delta = repoName.length - displayName.length
|
||||
highlights = highlights.map(([start, end]) => [start - delta, end - delta])
|
||||
}
|
||||
</script>
|
||||
|
||||
<span class="root">
|
||||
<CodeHostIcon repository={repoName} />
|
||||
<!-- #key is needed here to recreate the link because use:highlightRanges changes the DOM -->
|
||||
{#key highlights}
|
||||
<Popover showOnHover let:registerTrigger placement="bottom-start">
|
||||
<a class="repo-link" {href} use:highlightRanges={{ ranges: highlights }} use:registerTrigger>
|
||||
{displayRepoName(repoName)}
|
||||
{#if rev}
|
||||
<small class="rev"> @ {rev}</small>
|
||||
{/if}
|
||||
</a>
|
||||
<svelte:fragment slot="content">
|
||||
{#await delay(fetchRepoPopoverData(client, repoName), 200) then data}
|
||||
<RepoPopover {data} withHeader />
|
||||
{:catch error}
|
||||
<Alert variant="danger" size="slim">{error}</Alert>
|
||||
{/await}
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
{/key}
|
||||
</span>
|
||||
<!-- #key is needed here to recreate the link because use:highlightRanges changes the DOM -->
|
||||
{#key highlights}
|
||||
<Popover showOnHover let:registerTrigger placement="bottom-start">
|
||||
<a class="repo-link" {href} use:highlightRanges={{ ranges: highlights }} use:registerTrigger>
|
||||
<DisplayRepoName {repoName} kind={undefined} />
|
||||
{#if rev}
|
||||
<small class="rev"> @ {rev}</small>
|
||||
{/if}
|
||||
</a>
|
||||
<svelte:fragment slot="content">
|
||||
{#await delay(fetchRepoPopoverData(client, repoName), 200) then data}
|
||||
<RepoPopover {data} withHeader />
|
||||
{:catch error}
|
||||
<Alert variant="danger" size="slim">{error}</Alert>
|
||||
{/await}
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
{/key}
|
||||
|
||||
<style lang="scss">
|
||||
.root {
|
||||
--icon-color: currentColor;
|
||||
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.375rem;
|
||||
|
||||
.repo-link {
|
||||
align-self: baseline;
|
||||
color: var(--body-color);
|
||||
.repo-link {
|
||||
display: flex;
|
||||
color: var(--body-color);
|
||||
font-weight: 500;
|
||||
align-items: baseline;
|
||||
:global([data-path-container]) {
|
||||
font-weight: 500;
|
||||
.rev {
|
||||
color: var(--text-muted);
|
||||
}
|
||||
}
|
||||
.rev {
|
||||
color: var(--text-muted);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -337,7 +337,7 @@ test.describe('search results', async () => {
|
||||
await expect(page.locator('*:focus')).toContainText(pathMatch.path.split('/').at(-1) ?? '')
|
||||
await page.keyboard.press('ArrowDown') // Check a down arrow too
|
||||
|
||||
await expect(page.locator('*:focus')).toContainText(repoMatch.repository.split('/').slice(1).join('/'))
|
||||
await expect(page.getByRole('link', { name: repoMatch.repository })).toBeFocused()
|
||||
await page.keyboard.press('j')
|
||||
|
||||
for (const symbol of symbolMatch.symbols) {
|
||||
@ -360,9 +360,7 @@ test.describe('search results', async () => {
|
||||
}
|
||||
|
||||
await page.keyboard.press('k')
|
||||
await expect(page.locator('*:focus')).toContainText(repoMatch.repository.split('/').slice(1).join('/'), {
|
||||
useInnerText: true,
|
||||
})
|
||||
await expect(page.getByRole('link', { name: repoMatch.repository })).toBeFocused()
|
||||
|
||||
await page.keyboard.press('ArrowUp') // Check an up arrow too
|
||||
await expect(page.locator('*:focus')).toContainText(pathMatch.path.split('/').at(-1) ?? '')
|
||||
|
||||
Loading…
Reference in New Issue
Block a user