Skip to content
This repository was archived by the owner on Aug 1, 2025. It is now read-only.
Open
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
bf241e0
feat(context): Display branch name in mention menu help text
ichim-david Jul 7, 2025
a71078a
feat(context): Enhance remote directory search and item retrieval
ichim-david Jul 8, 2025
064ccf8
feat(context): Improve remote directory item retrieval with context s…
ichim-david Jul 20, 2025
7e375b5
fix(context): Improve remote directory search tests and URL generation
ichim-david Jul 20, 2025
76143f9
feat(context): Enhance remote directory search with branch support an…
ichim-david Jul 20, 2025
e164488
feat(context): Enhance OpenCtx with branch mentions and remote file b…
ichim-david Jul 20, 2025
49af345
Fixed tests by using the exported functions and general cleanup
ichim-david Jul 21, 2025
0052d3f
pnpm biome fix
ichim-david Jul 21, 2025
3473b19
fixed remote file filtering once we choose the branch
ichim-david Jul 22, 2025
0314735
feat(context): Improve remote directory search using getDirectoryCont…
ichim-david Jul 27, 2025
4ffcf86
Removed experimental flag from remote directoy mention menu
ichim-david Jul 28, 2025
0f98b3a
WIP implementing repo -> branch -> directory selection for remote dir…
ichim-david Jul 28, 2025
e198229
repository branch searching beyond the first 10 results
ichim-david Jul 29, 2025
56730e2
fix regex for starting directories mentions on searches branches
ichim-david Jul 30, 2025
ca80b62
skip . folders from initial folder listing
ichim-david Jul 30, 2025
f32012f
simplify branch mentions after proper branch searching
ichim-david Jul 30, 2025
8fe7cec
simplify check for remote directories branch or directory listing
ichim-david Jul 30, 2025
1841de6
allow remote file to search for branches
ichim-david Jul 30, 2025
2b43819
test fixes
ichim-david Jul 30, 2025
9be17a4
fix mention menu tooltip height when branch name is long
ichim-david Jul 30, 2025
57bffd7
removed `getFileBranchMentions` fallback to file search
ichim-david Jul 31, 2025
29ce80a
simplify code by getting rid of need for extractBranchAndRepo
ichim-david Jul 31, 2025
c927aaa
simplify and bring mentions in sync between remoteFile and remoteDire…
ichim-david Jul 31, 2025
4d9016f
remove needless try catch and clarify why we have empty description
ichim-david Jul 31, 2025
75ef576
removed `parsedRemoteQuery` logic that ended up not being used
ichim-david Jul 31, 2025
8d5a4cf
parallel fetch of directory items
ichim-david Jul 31, 2025
996e08e
fix tooltips for dirname
ichim-david Jul 31, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
173 changes: 173 additions & 0 deletions lib/prompt-editor/src/mentions/mentionMenu/MentionMenu.branch.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
import {
ContextItemSource,
type MentionQuery,
REMOTE_DIRECTORY_PROVIDER_URI,
} from '@sourcegraph/cody-shared'
import type { MentionMenuData } from '@sourcegraph/cody-shared'
import { describe, expect, test } from 'vitest'
import { URI } from 'vscode-uri'
import { getBranchHelpText } from './MentionMenu'

describe('MentionMenu branch selection', () => {
test('should show default branch text when no branch is specified', () => {
const items: MentionMenuData['items'] = [
{
type: 'openctx',
provider: 'openctx',
title: 'src/components',
uri: URI.parse('https://example.com/repo/-/tree/src/components'),
providerUri: REMOTE_DIRECTORY_PROVIDER_URI,
source: ContextItemSource.User,
mention: {
uri: 'https://example.com/repo/-/tree/src/components',
data: {
repoName: 'test-repo',
directoryPath: 'src/components',
},
},
},
]

const mentionQuery: MentionQuery = {
text: 'test-repo:src',
provider: REMOTE_DIRECTORY_PROVIDER_URI,
}

const result = getBranchHelpText(items!, mentionQuery)
expect(result).toBe('* Sourced from the remote default branch')
})

test('should show branch name when branch is specified in query', () => {
const items: MentionMenuData['items'] = [
{
type: 'openctx',
provider: 'openctx',
title: 'src/components',
uri: URI.parse('https://example.com/repo/-/tree/src/components'),
providerUri: REMOTE_DIRECTORY_PROVIDER_URI,
source: ContextItemSource.User,
mention: {
uri: 'https://example.com/repo/-/tree/src/components',
data: {
repoName: 'test-repo',
directoryPath: 'src/components',
},
},
},
]

const mentionQuery = {
text: 'test-repo@feature-branch:src',
provider: REMOTE_DIRECTORY_PROVIDER_URI,
}

const result = getBranchHelpText(items!, mentionQuery)
expect(result).toBe("* Sourced from the 'feature-branch' branch")
})

test('should show branch name from mention data when available', () => {
const items: MentionMenuData['items'] = [
{
type: 'openctx',
provider: 'openctx',
title: 'src/components',
uri: URI.parse('https://example.com/repo/-/tree/src/components'),
providerUri: REMOTE_DIRECTORY_PROVIDER_URI,
source: ContextItemSource.User,
mention: {
uri: 'https://example.com/repo/-/tree/src/components',
data: {
repoName: 'test-repo',
directoryPath: 'src/components',
branch: 'main',
},
},
},
]

const mentionQuery = { text: 'test-repo:src', provider: REMOTE_DIRECTORY_PROVIDER_URI }

const result = getBranchHelpText(items!, mentionQuery)
expect(result).toBe("* Sourced from the 'main' branch")
})

test('should prefer mention data branch over query branch', () => {
const items: MentionMenuData['items'] = [
{
type: 'openctx',
provider: 'openctx',
title: 'src/components',
uri: URI.parse('https://example.com/repo/-/tree/src/components'),
providerUri: REMOTE_DIRECTORY_PROVIDER_URI,
source: ContextItemSource.User,
mention: {
uri: 'https://example.com/repo/-/tree/src/components',
data: {
repoName: 'test-repo',
directoryPath: 'src/components',
branch: 'actual-branch',
},
},
},
]

const mentionQuery = {
text: 'test-repo@query-branch:src',
provider: REMOTE_DIRECTORY_PROVIDER_URI,
}

const result = getBranchHelpText(items!, mentionQuery)
expect(result).toBe("* Sourced from the 'actual-branch' branch")
})

test('should handle empty items array', () => {
const items: MentionMenuData['items'] = []
const mentionQuery = { text: 'test-repo@main:src', provider: REMOTE_DIRECTORY_PROVIDER_URI }

const result = getBranchHelpText(items!, mentionQuery)
expect(result).toBe("* Sourced from the 'main' branch")
})

test('should handle non-openctx items', () => {
const items: MentionMenuData['items'] = [
{
type: 'file',
provider: 'file',
title: 'test.ts',
uri: URI.parse('file:///test.ts'),
source: ContextItemSource.User,
},
]

const mentionQuery = { text: 'test-repo@feature:src', provider: REMOTE_DIRECTORY_PROVIDER_URI }

const result = getBranchHelpText(items!, mentionQuery)
expect(result).toBe("* Sourced from the 'feature' branch")
})

test('should show branch selection help when showing branch options', () => {
const items: MentionMenuData['items'] = [
{
type: 'openctx',
provider: 'openctx',
title: '@main',
uri: URI.parse('https://example.com/repo@main'),
providerUri: REMOTE_DIRECTORY_PROVIDER_URI,
source: ContextItemSource.User,
mention: {
uri: 'https://example.com/repo@main',
data: {
repoName: 'test-repo',
branch: 'main',
// No directoryPath - this indicates we're in branch selection mode
},
},
},
]

const mentionQuery = { text: 'test-repo:', provider: REMOTE_DIRECTORY_PROVIDER_URI }

const result = getBranchHelpText(items!, mentionQuery)
expect(result).toBe('* Select or search for a specific branch')
})
})
101 changes: 80 additions & 21 deletions lib/prompt-editor/src/mentions/mentionMenu/MentionMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -165,20 +165,24 @@ export const MentionMenu: FunctionComponent<
// Rather keep the provider in place and update the query with repo name so that the provider can
// start showing the files instead.

const branch = item.mention?.data?.branch
const repoName = item?.mention?.data.repoName
const repoWithBranch = branch ? `${repoName}@${branch}` : repoName

updateMentionMenuParams({
parentItem:
item.providerUri === REMOTE_DIRECTORY_PROVIDER_URI
? {
id: REMOTE_DIRECTORY_PROVIDER_URI,
title: 'Remote Directories',
queryLabel: 'Enter directory path to search',
emptyLabel: `No matching directories found in ${item?.mention?.data.repoName} repository`,
queryLabel: `Enter directory path to search in ${repoWithBranch}`,
emptyLabel: `No matching directories found in ${repoWithBranch} repository`,
}
: {
id: REMOTE_FILE_PROVIDER_URI,
title: 'Remote Files',
queryLabel: 'Enter file path to search',
emptyLabel: `No matching files found in ${item?.mention?.data.repoName} repository`,
queryLabel: `Enter file path to search in ${repoWithBranch}`,
emptyLabel: `No matching files found in ${repoWithBranch} repository`,
},
})

Expand All @@ -192,7 +196,11 @@ export const MentionMenu: FunctionComponent<
const cursorPosition = selection.anchorOffset
const mentionStart = cursorPosition - mentionQuery.text.length
const mentionEndIndex = cursorPosition
const textToInsert = `${item.mention?.data?.repoName}:`
// If a branch is selected, include it in the query
const branch = item.mention?.data?.branch
const textToInsert = branch
? `${item.mention?.data?.repoName}@${branch}:`
: `${item.mention?.data?.repoName}:`

return [
currentText.slice(0, mentionStart) +
Expand Down Expand Up @@ -265,7 +273,7 @@ export const MentionMenu: FunctionComponent<
ref={ref}
data-testid="mention-menu"
>
<CommandList className="!tw-max-h-[unset]">
<CommandList className="!tw-max-h-[unset] tw-min-h-[90px]">
{providers.length > 0 && (
<CommandGroup className={COMMAND_GROUP_CLASS_NAME}>{providers}</CommandGroup>
)}
Expand Down Expand Up @@ -302,10 +310,11 @@ export const MentionMenu: FunctionComponent<
className={clsx(
COMMAND_ROW_CLASS_NAME,
COMMAND_ROW_TEXT_CLASS_NAME,
'tw-bg-accent'
'tw-bg-accent',
'!tw-h-auto'
)}
>
* Sourced from the remote default branch
{getBranchHelpText(data.items, mentionQuery)}
</CommandLoading>
)}

Expand Down Expand Up @@ -341,6 +350,55 @@ function commandRowValue(
return contextItemID(row)
}

export function getBranchHelpText(
items: NonNullable<MentionMenuData['items']>,
mentionQuery: MentionQuery
): string {
const firstItem = items[0]

// Type guard for openctx items
const isOpenCtxItem = (
item: ContextItem
): item is ContextItem & {
type: 'openctx'
mention?: { data?: { branch?: string; repoName?: string; directoryPath?: string } }
} => item.type === 'openctx'

if (firstItem && isOpenCtxItem(firstItem)) {
const openCtxItem = firstItem
const repoName = openCtxItem.mention?.data?.repoName
const branch = openCtxItem.mention?.data?.branch
const directoryPath = openCtxItem.mention?.data?.directoryPath

// If we're showing branch options (no directoryPath), show branch selection help
if (repoName && !directoryPath) {
// Check if this is a branch mention (title starts with @)
if (firstItem.title?.startsWith('@')) {
return '* Select or search for a specific branch'
}
}

// If we're browsing directories and have branch info, show current branch
if (branch) {
return `* Sourced from the '${branch}' branch`
}
}

// Check if user has specified a branch in the query
const atIndex = mentionQuery.text.indexOf('@')
if (atIndex !== -1 && atIndex < mentionQuery.text.length - 1) {
const afterAt = mentionQuery.text.slice(atIndex + 1)
const colonIndex = afterAt.indexOf(':')
const branchName = colonIndex !== -1 ? afterAt.slice(0, colonIndex) : afterAt

if (branchName.trim()) {
return `* Sourced from the '${branchName}' branch`
}
}

return '* Sourced from the remote default branch'
}

function getEmptyLabel(
parentItem: ContextMentionProviderMetadata | null,
mentionQuery: MentionQuery
Expand All @@ -359,6 +417,19 @@ function getEmptyLabel(
return parentItem.emptyLabel ?? 'No results'
}

function getRemoteDirectoryHeading(queryText: string): string {
if (!queryText.includes(':')) {
return 'Directory - Select a repository*'
}

// If path includes @, we're in directory selection/filtering mode
if (queryText.includes('@')) {
return 'Directory - Select or search for a directory*'
}

return 'Directory - Select or search for a branch*'
}

function getItemsHeading(
parentItem: ContextMentionProviderMetadata | null,
mentionQuery: MentionQuery
Expand All @@ -383,19 +454,7 @@ function getItemsHeading(
if (parentItem.id === REMOTE_DIRECTORY_PROVIDER_URI) {
return (
<div className="tw-flex tw-flex-gap-2 tw-items-center tw-justify-between">
<div>
{mentionQuery.text.includes(':')
? 'Directory - Select or search for a directory*'
: 'Directory - Select a repository*'}
</div>
<div
className={clsx(
'tw-text-xs tw-rounded tw-px-2 tw-text-foreground',
styles.experimental
)}
>
Experimental
</div>
<div>{getRemoteDirectoryHeading(mentionQuery.text)}</div>
</div>
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
direction: rtl;
text-align: left;
}

Expand All @@ -29,6 +28,4 @@
flex: 1;
}

.title:hover {
direction: rtl;
}

5 changes: 4 additions & 1 deletion lib/shared/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,10 @@ export {
promise,
type ReadonlyDeep,
} from './utils'
export type { CurrentUserCodySubscription } from './sourcegraph-api/graphql/client'
export type {
CurrentUserCodySubscription,
DirectoryContentsResponse,
} from './sourcegraph-api/graphql/client'
export * from './auth/types'
export * from './auth/tokens'
export * from './auth/referral'
Expand Down
Loading
Loading