diff --git a/clients/tabby-chat-panel/src/index.ts b/clients/tabby-chat-panel/src/index.ts index cfab2ece63f4..b7cac29b90f4 100644 --- a/clients/tabby-chat-panel/src/index.ts +++ b/clients/tabby-chat-panel/src/index.ts @@ -160,8 +160,9 @@ export interface FileLocation { /** * The location in the file. * It could be a 1-based line number, a line range, a position or a position range. + * If the location is not provided, the whole file is considered. */ - location: Location + location?: Location } /** diff --git a/clients/vscode/src/chat/fileContext.ts b/clients/vscode/src/chat/fileContext.ts index 584151430ef4..27810f249538 100644 --- a/clients/vscode/src/chat/fileContext.ts +++ b/clients/vscode/src/chat/fileContext.ts @@ -25,10 +25,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 filepath = localUriToChatPanelFilepath(editor.document.uri, gitProvider); diff --git a/clients/vscode/src/chat/utils.ts b/clients/vscode/src/chat/utils.ts index 1721277345a9..227bee6c028b 100644 --- a/clients/vscode/src/chat/utils.ts +++ b/clients/vscode/src/chat/utils.ts @@ -83,7 +83,10 @@ export function chatPanelLineRangeToVSCodeRange(lineRange: LineRange): VSCodeRan return new VSCodeRange(Math.max(0, lineRange.start - 1), 0, lineRange.end, 0); } -export function chatPanelLocationToVSCodeRange(location: Location): VSCodeRange | null { +export function chatPanelLocationToVSCodeRange(location: Location | undefined): VSCodeRange | null { + if (!location) { + return null; + } if (typeof location === "number") { const position = new VSCodePosition(Math.max(0, location - 1), 0); return new VSCodeRange(position, position); 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 50ee5b31c05f..920f21ad8ad4 100644 --- a/ee/tabby-ui/app/search/components/assistant-message-section.tsx +++ b/ee/tabby-ui/app/search/components/assistant-message-section.tsx @@ -152,14 +152,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 @@ -171,14 +166,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, @@ -216,10 +206,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 } @@ -238,7 +225,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`) @@ -247,10 +234,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 0fe704e4a174..0bfd130a43ab 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 67c53c4fefa9..ca95e67504e5 100644 --- a/ee/tabby-ui/components/chat/chat.tsx +++ b/ee/tabby-ui/components/chat/chat.tsx @@ -417,7 +417,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 bab877145ac2..1b695e3e409d 100644 --- a/ee/tabby-ui/components/chat/question-answer.tsx +++ b/ee/tabby-ui/components/chat/question-answer.tsx @@ -126,7 +126,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 ( @@ -189,7 +192,7 @@ function UserMessageCard(props: { message: UserMessage }) { :{message.selectContext?.range.start} )} {selectCode.isMultiLine && ( - -{message.selectContext?.range.end} + -{message.selectContext?.range?.end} )}

@@ -270,20 +273,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]) @@ -299,19 +295,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(() => { @@ -320,7 +319,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 })) ?? [] @@ -352,16 +352,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) } onContextClick(ctx, code.isClient) } diff --git a/ee/tabby-ui/components/message-markdown/index.tsx b/ee/tabby-ui/components/message-markdown/index.tsx index 3d55875d50e1..1ba2c7aa7f85 100644 --- a/ee/tabby-ui/components/message-markdown/index.tsx +++ b/ee/tabby-ui/components/message-markdown/index.tsx @@ -174,7 +174,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/types/chat.ts b/ee/tabby-ui/lib/types/chat.ts index 645b0246be85..515c923da74c 100644 --- a/ee/tabby-ui/lib/types/chat.ts +++ b/ee/tabby-ui/lib/types/chat.ts @@ -12,7 +12,11 @@ import { ArrayElementType } from './common' export interface FileContext { kind: 'file' filepath: string - range: { start: number; end: number } + /** + * The range of the selected content in the file. + * If the range is not provided, the whole file is considered. + */ + range?: { start: number; end: number } content: string git_url: string } diff --git a/ee/tabby-ui/lib/utils/index.ts b/ee/tabby-ui/lib/utils/index.ts index 96b8af4a0ac0..27b850442794 100644 --- a/ee/tabby-ui/lib/utils/index.ts +++ b/ee/tabby-ui/lib/utils/index.ts @@ -135,7 +135,7 @@ export function formatLineHashForLocation(location: Location | undefined) { const start = location.start if (typeof start === 'number') { const end = location.end as number - return `L${start}-${end}` + return `L${start}-L${end}` } if ( typeof start === 'object' && @@ -143,7 +143,7 @@ export function formatLineHashForLocation(location: Location | undefined) { typeof start.line === 'number' ) { const end = location.end as Position - return `L${start.line}-${end.line}` + return `L${start.line}-L${end.line}` } } return '' @@ -152,22 +152,22 @@ export function formatLineHashForLocation(location: Location | undefined) { 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) { @@ -199,9 +199,9 @@ export function convertEditorContext( editorContext: EditorContext ): FileContext { const convertRange = (range: LineRange | PositionRange | undefined) => { - // FIXME: If the range is not provided, the whole file is considered. + // If the range is not provided, the whole file is considered. if (!range) { - return { start: 0, end: 0 } + return undefined } if (typeof range.start === 'number') {