diff --git a/lib/prompt-editor/src/mentions/mentionMenu/MentionMenuItem.module.css b/lib/prompt-editor/src/mentions/mentionMenu/MentionMenuItem.module.css index 0069cec37c78..d6877c4c58d8 100644 --- a/lib/prompt-editor/src/mentions/mentionMenu/MentionMenuItem.module.css +++ b/lib/prompt-editor/src/mentions/mentionMenu/MentionMenuItem.module.css @@ -22,3 +22,7 @@ .description { flex: 1; } + +.title:hover { + direction: rtl; +} diff --git a/lib/prompt-editor/src/mentions/mentionMenu/MentionMenuItem.test.tsx b/lib/prompt-editor/src/mentions/mentionMenu/MentionMenuItem.test.tsx new file mode 100644 index 000000000000..0653d414811d --- /dev/null +++ b/lib/prompt-editor/src/mentions/mentionMenu/MentionMenuItem.test.tsx @@ -0,0 +1,99 @@ +import type { ContextItem, ContextItemRepository, ContextItemSymbol } from '@sourcegraph/cody-shared' +import { describe, expect, it } from 'vitest' +import { URI } from 'vscode-uri' +import { getMentionItemTitleAndDisplayName } from './MentionMenuItem' + +describe('getMentionItemTitleAndDisplayName', () => { + it('should return correct title and displayName for a file', () => { + const item: ContextItem = { + type: 'file', + uri: URI.parse('file:///path/to/testfile.ts'), + } + const result = getMentionItemTitleAndDisplayName(item) + expect(result).toEqual({ title: 'testfile.ts', displayName: 'testfile.ts' }) + }) + + it('should use provided title if available', () => { + const item: ContextItem = { + type: 'file', + uri: URI.parse('file:///path/to/file.ts'), + title: 'Custom Title', + } + const result = getMentionItemTitleAndDisplayName(item) + expect(result).toEqual({ title: 'Custom Title', displayName: 'Custom Title' }) + }) + + it('should return correct title and displayName for a class symbol', () => { + const item: ContextItemSymbol = { + type: 'symbol', + symbolName: 'ClassSymbol', + uri: URI.parse('file:///path/to/file.ts'), + kind: 'class', + } + const result = getMentionItemTitleAndDisplayName(item) + expect(result).toEqual({ title: 'ClassSymbol', displayName: 'ClassSymbol' }) + }) + + it('should return correct title and displayName for a method symbol', () => { + const item: ContextItemSymbol = { + type: 'symbol', + symbolName: 'MethodSymbol', + uri: URI.parse('file:///path/to/file.ts'), + kind: 'method', + } + const result = getMentionItemTitleAndDisplayName(item) + expect(result).toEqual({ title: 'MethodSymbol', displayName: 'MethodSymbol' }) + }) + + it('should return correct title and displayName for a repository', () => { + const item: ContextItemRepository = { + type: 'repository', + title: 'host.com/org/repo', + repoName: 'host.com/org/repo', + repoID: 'host.com/org/repo', + uri: URI.parse('https://host.com/org/repo'), + content: null, + } + const result = getMentionItemTitleAndDisplayName(item) + expect(result).toEqual({ title: 'host.com/org/repo', displayName: 'org/repo' }) + }) + + it('should handle repository with multiple slashes in title', () => { + const item: ContextItemRepository = { + type: 'repository', + title: 'host.com/org/repo/sub/dir', + repoName: 'host.com/org/repo/sub/dir', + repoID: 'host.com/org/repo/sub/dir', + uri: URI.parse('https://host.com/org/repo/sub/dir'), + content: null, + } + const result = getMentionItemTitleAndDisplayName(item) + expect(result).toEqual({ title: 'host.com/org/repo/sub/dir', displayName: 'org/repo/sub/dir' }) + }) + + it('should handle repository with a single slash in title', () => { + const item: ContextItemRepository = { + type: 'repository', + title: 'org/repo', + repoName: 'org/repo', + repoID: 'repo', + uri: URI.parse('https://host.org/repo'), + content: null, + } + const result = getMentionItemTitleAndDisplayName(item) + expect(result).toEqual({ title: 'org/repo', displayName: 'repo' }) + }) + + it('should handle repository without slash in title', () => { + const item: ContextItemRepository = { + type: 'repository', + title: 'repo', + repoName: 'repo', + repoID: 'repo', + uri: URI.parse('https://host.org/repo'), + content: null, + } + const result = getMentionItemTitleAndDisplayName(item) + expect(result).toEqual({ title: 'repo', displayName: 'repo' }) + }) +}) diff --git a/lib/prompt-editor/src/mentions/mentionMenu/MentionMenuItem.tsx b/lib/prompt-editor/src/mentions/mentionMenu/MentionMenuItem.tsx index 148df7d46ba0..219e5a866129 100644 --- a/lib/prompt-editor/src/mentions/mentionMenu/MentionMenuItem.tsx +++ b/lib/prompt-editor/src/mentions/mentionMenu/MentionMenuItem.tsx @@ -61,6 +61,20 @@ function getDescription(item: ContextItem, query: MentionQuery): string { } } +export function getMentionItemTitleAndDisplayName(item: ContextItem): { + title: string + displayName: string +} { + const isSymbol = item.type === 'symbol' + const isRepo = item.type === 'repository' + const title = item.title ?? (isSymbol ? item.symbolName : displayPathBasename(item.uri)) + // For repository items, display only the org/repo part of the title + // to prevent long hostnames from causing repo names to be truncated in the UI. + const displayName = isRepo ? title?.split('/')?.slice(1)?.join('/') || title : title + + return { title, displayName } +} + export const MentionMenuContextItemContent: FunctionComponent<{ query: MentionQuery item: ContextItem @@ -68,9 +82,10 @@ export const MentionMenuContextItemContent: FunctionComponent<{ const isOpenCtx = item.type === 'openctx' const isFileType = item.type === 'file' const isSymbol = item.type === 'symbol' - const icon = - item.icon || (isSymbol ? (item.kind === 'class' ? 'symbol-structure' : 'symbol-method') : null) - const title = item.title ?? (isSymbol ? item.symbolName : displayPathBasename(item.uri)) + const isClassSymbol = isSymbol && item.kind === 'class' + + const icon = item.icon || (isSymbol ? (isClassSymbol ? 'symbol-structure' : 'symbol-method') : null) + const { title, displayName } = getMentionItemTitleAndDisplayName(item) const description = getDescription(item, query) const isIgnored = (isFileType || isOpenCtx) && item.isIgnored @@ -89,7 +104,7 @@ export const MentionMenuContextItemContent: FunctionComponent<{
{icon && } - {title} + {displayName} {description && ( diff --git a/vscode/CHANGELOG.md b/vscode/CHANGELOG.md index fa8dd288ce1a..c64552cb6185 100644 --- a/vscode/CHANGELOG.md +++ b/vscode/CHANGELOG.md @@ -12,6 +12,8 @@ Chat: Fixed feedback buttons not working in chat. [pull/5509](https://github.com ### Changed +Enterprise: Remote Repository items in the mention menu now display only the org/repo part of the title, omitting the code host name to prevent repository names from being truncated in the UI. [pull/5518](https://github.com/sourcegraph/cody/pull/5518) + ## 1.34.1 ### Added diff --git a/vscode/test/e2e/mention-repository.test.ts b/vscode/test/e2e/mention-repository.test.ts index ed3b6b9aaa0c..645bd44a5fda 100644 --- a/vscode/test/e2e/mention-repository.test.ts +++ b/vscode/test/e2e/mention-repository.test.ts @@ -12,7 +12,7 @@ import { import { mockEnterpriseRepoMapping, testWithGitRemote } from './helpers' testWithGitRemote('@-mention repository', async ({ page, sidebar, server }) => { - mockEnterpriseRepoMapping(server, 'host.example/user/myrepo') + mockEnterpriseRepoMapping(server, 'codehost.example/user/myrepo') await sidebarSignin(page, sidebar) const [chatFrame, lastChatInput] = await createEmptyChatPanel(page) @@ -23,16 +23,16 @@ testWithGitRemote('@-mention repository', async ({ page, sidebar, server }) => { results: { repositories: [ { - id: 'a/b', - name: 'a/b', + id: 'codehost.example/a/b', + name: 'codehost.example/a/b', stars: 10, - url: 'https://example.com/a/b', + url: 'https://codehost.example/a/b', }, { - id: 'c/d', - name: 'c/d', + id: 'codehost.example/c/d', + name: 'codehost.example/c/d', stars: 9, - url: 'https://example.com/c/d', + url: 'https://codehost.example/c/d', }, ], }, @@ -43,5 +43,8 @@ testWithGitRemote('@-mention repository', async ({ page, sidebar, server }) => { await openMentionsForProvider(chatFrame, lastChatInput, 'Remote Repositories') await expect(mentionMenuItems(chatFrame)).toHaveText(['a/b', 'c/d']) await selectMentionMenuItem(chatFrame, 'c/d') - await expect(chatInputMentions(lastChatInput)).toHaveText(['host.example/user/myrepo', 'c/d']) + await expect(chatInputMentions(lastChatInput)).toHaveText([ + 'codehost.example/user/myrepo', + 'codehost.example/c/d', + ]) })