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