diff --git a/clients/tabby-chat-panel/src/index.ts b/clients/tabby-chat-panel/src/index.ts index 08f419e295f7..4cb7a4c89ac9 100644 --- a/clients/tabby-chat-panel/src/index.ts +++ b/clients/tabby-chat-panel/src/index.ts @@ -53,7 +53,10 @@ export type Location = number | LineRange | Position | PositionRange export interface FileContext { kind: 'file' - range: LineRange + /** + * If the range is undefined, it implies that the context is the entire file. + */ + range?: LineRange filepath: string content: string git_url: string diff --git a/clients/vscode/src/chat/fileContext.ts b/clients/vscode/src/chat/fileContext.ts index 8cf6dbb50fce..68bd74867b20 100644 --- a/clients/vscode/src/chat/fileContext.ts +++ b/clients/vscode/src/chat/fileContext.ts @@ -34,10 +34,7 @@ export async function getFileContext( start: editor.selection.start.line + 1, end: editor.selection.end.line + 1, } - : { - start: 1, - end: editor.document.lineCount, - }; + : undefined; const filePathParams = await buildFilePathParams(editor.document.uri, gitProvider); @@ -75,8 +72,8 @@ export async function showFileContext(fileContext: FileContext, gitProvider: Git }); // Move the cursor to the specified line - const start = new Position(Math.max(0, fileContext.range.start - 1), 0); - const end = new Position(fileContext.range.end, 0); + const start = fileContext.range ? new Position(Math.max(0, fileContext.range.start - 1), 0) : new Position(0, 0); + const end = fileContext.range ? new Position(fileContext.range.end, 0) : new Position(0, 0); editor.selection = new Selection(start, end); editor.revealRange(new Range(start, end), TextEditorRevealType.InCenter); } diff --git a/ee/tabby-ui/app/files/components/chat-side-bar.tsx b/ee/tabby-ui/app/files/components/chat-side-bar.tsx index f8f673dfae95..674bc25704c6 100644 --- a/ee/tabby-ui/app/files/components/chat-side-bar.tsx +++ b/ee/tabby-ui/app/files/components/chat-side-bar.tsx @@ -122,10 +122,12 @@ export const ChatSideBar: React.FC = ({ selectContext: { kind: 'file', content: code, - range: { - start: lineFrom, - end: lineTo ?? lineFrom - }, + range: lineFrom + ? { + start: lineFrom, + end: lineTo ?? lineFrom + } + : undefined, filepath: path, git_url: gitUrl } diff --git a/ee/tabby-ui/app/search/components/assistant-message-section.tsx b/ee/tabby-ui/app/search/components/assistant-message-section.tsx index d4af5ab3a092..c9bd57cf54df 100644 --- a/ee/tabby-ui/app/search/components/assistant-message-section.tsx +++ b/ee/tabby-ui/app/search/components/assistant-message-section.tsx @@ -153,14 +153,9 @@ export function AssistantMessageSection({ if (!clientCode?.length) return [] return ( clientCode.map(code => { - const { startLine, endLine } = getRangeFromAttachmentCode(code) - return { kind: 'file', - range: { - start: startLine, - end: endLine - }, + range: getRangeFromAttachmentCode(code), filepath: code.filepath || '', content: code.content, git_url: relevantCodeGitURL @@ -172,14 +167,9 @@ export function AssistantMessageSection({ const serverCodeContexts: RelevantCodeContext[] = useMemo(() => { return ( message?.attachment?.code?.map(code => { - const { startLine, endLine } = getRangeFromAttachmentCode(code) - return { kind: 'file', - range: { - start: startLine, - end: endLine - }, + range: getRangeFromAttachmentCode(code), filepath: code.filepath, content: code.content, git_url: code.gitUrl, @@ -217,10 +207,7 @@ export function AssistantMessageSection({ searchParams.append('redirect_git_url', ctx.git_url) url.search = searchParams.toString() - const lineHash = formatLineHashForCodeBrowser({ - start: ctx.range.start, - end: ctx.range.end - }) + const lineHash = formatLineHashForCodeBrowser(ctx.range) if (lineHash) { url.hash = lineHash } @@ -239,7 +226,7 @@ export function AssistantMessageSection({ } const openCodeBrowserTab = (code: MessageAttachmentCode) => { - const { startLine, endLine } = getRangeFromAttachmentCode(code) + const range = getRangeFromAttachmentCode(code) if (!code.filepath) return const url = new URL(`${window.location.origin}/files`) @@ -248,10 +235,7 @@ export function AssistantMessageSection({ searchParams.append('redirect_git_url', code.gitUrl) url.search = searchParams.toString() - const lineHash = formatLineHashForCodeBrowser({ - start: startLine, - end: endLine - }) + const lineHash = formatLineHashForCodeBrowser(range) if (lineHash) { url.hash = lineHash } diff --git a/ee/tabby-ui/components/chat/chat-panel.tsx b/ee/tabby-ui/components/chat/chat-panel.tsx index 92d5a7746e7e..d3ee3d3a0f0b 100644 --- a/ee/tabby-ui/components/chat/chat-panel.tsx +++ b/ee/tabby-ui/components/chat/chat-panel.tsx @@ -284,10 +284,11 @@ function ChatPanelRenderer( ) : null} {relevantContext.map((item, idx) => { + // `git_url + filepath + range` as unique key + const key = `${item.git_url}_${item.filepath}_${item.range?.start}_${item.range?.end}` return ( {fileName} - {line} + {!!context.range && {line}} ) } diff --git a/ee/tabby-ui/components/chat/chat.tsx b/ee/tabby-ui/components/chat/chat.tsx index 360afcec4bc7..16843cce0d36 100644 --- a/ee/tabby-ui/components/chat/chat.tsx +++ b/ee/tabby-ui/components/chat/chat.tsx @@ -413,7 +413,7 @@ function ChatRenderer( clientSideFileContexts.map(o => ({ content: o.content, filepath: o.filepath, - startLine: o.range.start + startLine: o.range?.start })) return [ diff --git a/ee/tabby-ui/components/chat/code-references.tsx b/ee/tabby-ui/components/chat/code-references.tsx index b9171f61b18e..f5010290ba6c 100644 --- a/ee/tabby-ui/components/chat/code-references.tsx +++ b/ee/tabby-ui/components/chat/code-references.tsx @@ -146,6 +146,7 @@ function ContextItem({ }) { const [tooltipOpen, setTooltipOpen] = useState(false) const isMultiLine = + context.range && !isNil(context.range?.start) && !isNil(context.range?.end) && context.range.start < context.range.end @@ -178,16 +179,20 @@ function ContextItem({
{fileName} - {context.range?.start && ( - - :{context.range.start} - - )} - {isMultiLine && ( - - -{context.range.end} - - )} + {context.range ? ( + <> + {context.range?.start && ( + + :{context.range.start} + + )} + {isMultiLine && ( + + -{context.range.end} + + )} + + ) : null} {path}
{showClientCodeIcon && ( diff --git a/ee/tabby-ui/components/chat/question-answer.tsx b/ee/tabby-ui/components/chat/question-answer.tsx index f9dd02821ae9..ab953f9032f1 100644 --- a/ee/tabby-ui/components/chat/question-answer.tsx +++ b/ee/tabby-ui/components/chat/question-answer.tsx @@ -124,7 +124,10 @@ function UserMessageCard(props: { message: UserMessage }) { selectCode = { filepath, isMultiLine: - !isNil(range?.start) && !isNil(range?.end) && range.start < range.end + !!range && + !isNil(range?.start) && + !isNil(range?.end) && + range.start < range.end } } return ( @@ -188,7 +191,7 @@ function UserMessageCard(props: { message: UserMessage }) { :{message.selectContext?.range.start} )} {selectCode.isMultiLine && ( - -{message.selectContext?.range.end} + -{message.selectContext?.range?.end} )}

@@ -269,20 +272,13 @@ function AssistantMessageCard(props: AssistantMessageCardProps) { React.useState(undefined) const serverCode: Array = React.useMemo(() => { return ( - message?.relevant_code?.map(code => { - const { startLine, endLine } = getRangeFromAttachmentCode(code) - - return { - kind: 'file', - range: { - start: startLine, - end: endLine - }, - filepath: code.filepath, - content: code.content, - git_url: code.gitUrl - } - }) ?? [] + message?.relevant_code?.map(code => ({ + kind: 'file', + range: getRangeFromAttachmentCode(code), + filepath: code.filepath, + content: code.content, + git_url: code.gitUrl + })) ?? [] ) }, [message?.relevant_code]) @@ -298,19 +294,22 @@ function AssistantMessageCard(props: AssistantMessageCardProps) { const attachmentDocsLen = 0 - const attachmentClientCode: Array> = - useMemo(() => { - const formatedAttachmentClientCode = - clientCode?.map(o => ({ - content: o.content, - filepath: o.filepath, - gitUrl: o.git_url, - startLine: o.range.start, - language: filename2prism(o.filepath ?? '')[0], - isClient: true - })) ?? [] - return formatedAttachmentClientCode - }, [clientCode]) + const attachmentClientCode: Array< + Omit & { + startLine: number | undefined + } + > = useMemo(() => { + const formatedAttachmentClientCode = + clientCode?.map(o => ({ + content: o.content, + filepath: o.filepath, + gitUrl: o.git_url, + startLine: o.range ? o.range.start : undefined, + language: filename2prism(o.filepath ?? '')[0], + isClient: true + })) ?? [] + return formatedAttachmentClientCode + }, [clientCode]) const attachmentCode: Array> = useMemo(() => { @@ -319,7 +318,8 @@ function AssistantMessageCard(props: AssistantMessageCardProps) { content: o.content, filepath: o.filepath, gitUrl: o.git_url, - startLine: o.range.start, + // for server attachment code, startLine will not be undefined + startLine: o.range?.start ?? 1, language: filename2prism(o.filepath ?? '')[0], isClient: false })) ?? [] @@ -335,16 +335,12 @@ function AssistantMessageCard(props: AssistantMessageCardProps) { } const onCodeCitationClick = (code: AttachmentCodeItem) => { - const { startLine, endLine } = getRangeFromAttachmentCode(code) const ctx: Context = { git_url: code.gitUrl, content: code.content, filepath: code.filepath, kind: 'file', - range: { - start: startLine, - end: endLine - } + range: getRangeFromAttachmentCode(code) } onNavigateToContext?.(ctx, { openInEditor: code.isClient diff --git a/ee/tabby-ui/components/message-markdown/index.tsx b/ee/tabby-ui/components/message-markdown/index.tsx index 935ed8bed849..9deebe99b0d8 100644 --- a/ee/tabby-ui/components/message-markdown/index.tsx +++ b/ee/tabby-ui/components/message-markdown/index.tsx @@ -175,7 +175,7 @@ export function MessageMarkdown({ setSymbolLocationMap(map => new Map(map.set(keyword, undefined))) const hints: LookupSymbolHint[] = [] - if (activeSelection) { + if (activeSelection && activeSelection?.range) { // FIXME(@icycodes): this is intended to convert the filepath to Filepath type // We should remove this after FileContext.filepath use type Filepath instead of string let filepath: Filepath diff --git a/ee/tabby-ui/lib/utils/index.ts b/ee/tabby-ui/lib/utils/index.ts index 218487694347..aab5b018659b 100644 --- a/ee/tabby-ui/lib/utils/index.ts +++ b/ee/tabby-ui/lib/utils/index.ts @@ -1,6 +1,7 @@ import { clsx, type ClassValue } from 'clsx' import { compact, isNil } from 'lodash-es' import { customAlphabet } from 'nanoid' +import { LineRange } from 'tabby-chat-panel/index' import { twMerge } from 'tailwind-merge' import { AttachmentCodeItem, AttachmentDocItem } from '@/lib/types' @@ -110,22 +111,22 @@ export function formatLineHashForCodeBrowser( export function getRangeFromAttachmentCode(code: { startLine?: Maybe content: string -}) { - const startLine = code?.startLine ?? 0 - const lineCount = code?.content.split('\n').length - const endLine = startLine + lineCount - 1 +}): LineRange | undefined { + if (!code?.startLine) return undefined + + const start = code.startLine + const lineCount = code.content.split('\n').length + const end = start + lineCount - 1 return { - startLine, - endLine, - isValid: !!startLine, - isMultiLine: !!startLine && startLine <= endLine + start, + end } } export function getRangeTextFromAttachmentCode(code: AttachmentCodeItem) { - const { startLine, endLine } = getRangeFromAttachmentCode(code) - return formatLineHashForCodeBrowser({ start: startLine, end: endLine }) + const range = getRangeFromAttachmentCode(code) + return formatLineHashForCodeBrowser(range) } export function getContent(item: AttachmentDocItem) {