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<{