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