mirror of
https://github.com/sourcebot-dev/sourcebot.git
synced 2025-12-12 04:15:30 +00:00
fix: Fix issue with ambiguous references (#393)
This commit is contained in:
parent
da8d49f8d9
commit
dbd8ef7fdb
10 changed files with 191 additions and 136 deletions
|
|
@ -200,19 +200,19 @@ When you have sufficient context, output your answer as a structured markdown re
|
||||||
**Required Response Format:**
|
**Required Response Format:**
|
||||||
- **CRITICAL**: You MUST always prefix your answer with a \`${ANSWER_TAG}\` tag at the very top of your response
|
- **CRITICAL**: You MUST always prefix your answer with a \`${ANSWER_TAG}\` tag at the very top of your response
|
||||||
- **CRITICAL**: You MUST provide your complete response in markdown format with embedded code references
|
- **CRITICAL**: You MUST provide your complete response in markdown format with embedded code references
|
||||||
- **CODE REFERENCE REQUIREMENT**: Whenever you mention, discuss, or refer to ANY specific part of the code (files, functions, variables, methods, classes, imports, etc.), you MUST immediately follow with a code reference using the format \`${fileReferenceToString({ fileName: 'filename'})}\` or \`${fileReferenceToString({ fileName: 'filename', range: { startLine: 1, endLine: 10 } })}\` (where the numbers are the start and end line numbers of the code snippet). This includes:
|
- **CODE REFERENCE REQUIREMENT**: Whenever you mention, discuss, or refer to ANY specific part of the code (files, functions, variables, methods, classes, imports, etc.), you MUST immediately follow with a code reference using the format \`${fileReferenceToString({ repo: 'repository', path: 'filename'})}\` or \`${fileReferenceToString({ repo: 'repository', path: 'filename', range: { startLine: 1, endLine: 10 } })}\` (where the numbers are the start and end line numbers of the code snippet). This includes:
|
||||||
- Files (e.g., "The \`auth.ts\` file" → must include \`${fileReferenceToString({ fileName: 'auth.ts' })}\`)
|
- Files (e.g., "The \`auth.ts\` file" → must include \`${fileReferenceToString({ repo: 'repository', path: 'auth.ts' })}\`)
|
||||||
- Function names (e.g., "The \`getRepos()\` function" → must include \`${fileReferenceToString({ fileName: 'auth.ts', range: { startLine: 15, endLine: 20 } })}\`)
|
- Function names (e.g., "The \`getRepos()\` function" → must include \`${fileReferenceToString({ repo: 'repository', path: 'auth.ts', range: { startLine: 15, endLine: 20 } })}\`)
|
||||||
- Variable names (e.g., "The \`suggestionQuery\` variable" → must include \`${fileReferenceToString({ fileName: 'search.ts', range: { startLine: 42, endLine: 42 } })}\`)
|
- Variable names (e.g., "The \`suggestionQuery\` variable" → must include \`${fileReferenceToString({ repo: 'repository', path: 'search.ts', range: { startLine: 42, endLine: 42 } })}\`)
|
||||||
- Code patterns (e.g., "using \`file:\${suggestionQuery}\` pattern" → must include \`${fileReferenceToString({ fileName: 'search.ts', range: { startLine: 10, endLine: 15 } })}\`)
|
|
||||||
- Any code snippet or line you're explaining
|
- Any code snippet or line you're explaining
|
||||||
- Class names, method calls, imports, etc.
|
- Class names, method calls, imports, etc.
|
||||||
- Some examples of both correct and incorrect code references:
|
- Some examples of both correct and incorrect code references:
|
||||||
- Correct: @file:{path/to/file.ts}
|
- Correct: @file:{repository::path/to/file.ts}
|
||||||
- Correct: @file:{path/to/file.ts:10-15}
|
- Correct: @file:{repository::path/to/file.ts:10-15}
|
||||||
- Incorrect: @file{path/to/file.ts} (missing colon)
|
- Incorrect: @file{repository::path/to/file.ts} (missing colon)
|
||||||
- Incorrect: @file:path/to/file.ts (missing curly braces)
|
- Incorrect: @file:repository::path/to/file.ts (missing curly braces)
|
||||||
- Incorrect: @file:{path/to/file.ts:10-25,30-35} (multiple ranges not supported)
|
- Incorrect: @file:{repository::path/to/file.ts:10-25,30-35} (multiple ranges not supported)
|
||||||
|
- Incorrect: @file:{path/to/file.ts} (missing repository)
|
||||||
- Be clear and very concise. Use bullet points where appropriate
|
- Be clear and very concise. Use bullet points where appropriate
|
||||||
- Do NOT explain code without providing the exact location reference. Every code mention requires a corresponding \`${FILE_REFERENCE_PREFIX}\` reference
|
- Do NOT explain code without providing the exact location reference. Every code mention requires a corresponding \`${FILE_REFERENCE_PREFIX}\` reference
|
||||||
- If you cannot provide a code reference for something you're discussing, do not mention that specific code element
|
- If you cannot provide a code reference for something you're discussing, do not mention that specific code element
|
||||||
|
|
@ -221,7 +221,7 @@ When you have sufficient context, output your answer as a structured markdown re
|
||||||
**Example answer structure:**
|
**Example answer structure:**
|
||||||
\`\`\`markdown
|
\`\`\`markdown
|
||||||
${ANSWER_TAG}
|
${ANSWER_TAG}
|
||||||
Authentication in Sourcebot is built on NextAuth.js with a session-based approach using JWT tokens and Prisma as the database adapter ${fileReferenceToString({ fileName: 'auth.ts', range: { startLine: 135, endLine: 140 } })}. The system supports multiple authentication providers and implements organization-based authorization with role-defined permissions.
|
Authentication in Sourcebot is built on NextAuth.js with a session-based approach using JWT tokens and Prisma as the database adapter ${fileReferenceToString({ repo: 'github.com/sourcebot-dev/sourcebot', path: 'auth.ts', range: { startLine: 135, endLine: 140 } })}. The system supports multiple authentication providers and implements organization-based authorization with role-defined permissions.
|
||||||
\`\`\`
|
\`\`\`
|
||||||
|
|
||||||
</answer_instructions>
|
</answer_instructions>
|
||||||
|
|
|
||||||
|
|
@ -17,13 +17,14 @@ describe('calculateVisibleRanges', () => {
|
||||||
test('applies padding to a single range', () => {
|
test('applies padding to a single range', () => {
|
||||||
const references: FileReference[] = [
|
const references: FileReference[] = [
|
||||||
{
|
{
|
||||||
fileName: 'test.ts',
|
path: 'test.ts',
|
||||||
id: '1',
|
id: '1',
|
||||||
type: 'file',
|
type: 'file',
|
||||||
range: {
|
range: {
|
||||||
startLine: 10,
|
startLine: 10,
|
||||||
endLine: 15
|
endLine: 15
|
||||||
}
|
},
|
||||||
|
repo: 'github.com/sourcebot-dev/sourcebot'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -38,16 +39,18 @@ describe('calculateVisibleRanges', () => {
|
||||||
test('merges overlapping ranges', () => {
|
test('merges overlapping ranges', () => {
|
||||||
const references: FileReference[] = [
|
const references: FileReference[] = [
|
||||||
{
|
{
|
||||||
fileName: 'test.ts',
|
path: 'test.ts',
|
||||||
id: '1',
|
id: '1',
|
||||||
type: 'file',
|
type: 'file',
|
||||||
range: { startLine: 10, endLine: 15 }
|
range: { startLine: 10, endLine: 15 },
|
||||||
|
repo: 'github.com/sourcebot-dev/sourcebot'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fileName: 'test.ts',
|
path: 'test.ts',
|
||||||
id: '2',
|
id: '2',
|
||||||
type: 'file',
|
type: 'file',
|
||||||
range: { startLine: 12, endLine: 20 }
|
range: { startLine: 12, endLine: 20 },
|
||||||
|
repo: 'github.com/sourcebot-dev/sourcebot'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -62,16 +65,18 @@ describe('calculateVisibleRanges', () => {
|
||||||
test('merges adjacent ranges (including padding)', () => {
|
test('merges adjacent ranges (including padding)', () => {
|
||||||
const references: FileReference[] = [
|
const references: FileReference[] = [
|
||||||
{
|
{
|
||||||
fileName: 'test.ts',
|
path: 'test.ts',
|
||||||
id: '1',
|
id: '1',
|
||||||
type: 'file',
|
type: 'file',
|
||||||
range: { startLine: 10, endLine: 15 }
|
range: { startLine: 10, endLine: 15 },
|
||||||
|
repo: 'github.com/sourcebot-dev/sourcebot'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fileName: 'test.ts',
|
path: 'test.ts',
|
||||||
id: '2',
|
id: '2',
|
||||||
type: 'file',
|
type: 'file',
|
||||||
range: { startLine: 19, endLine: 25 }
|
range: { startLine: 19, endLine: 25 },
|
||||||
|
repo: 'github.com/sourcebot-dev/sourcebot'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -88,16 +93,18 @@ describe('calculateVisibleRanges', () => {
|
||||||
test('keeps separate ranges when they dont overlap', () => {
|
test('keeps separate ranges when they dont overlap', () => {
|
||||||
const references: FileReference[] = [
|
const references: FileReference[] = [
|
||||||
{
|
{
|
||||||
fileName: 'test.ts',
|
path: 'test.ts',
|
||||||
id: '1',
|
id: '1',
|
||||||
type: 'file',
|
type: 'file',
|
||||||
range: { startLine: 10, endLine: 15 }
|
range: { startLine: 10, endLine: 15 },
|
||||||
|
repo: 'github.com/sourcebot-dev/sourcebot'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fileName: 'test.ts',
|
path: 'test.ts',
|
||||||
id: '2',
|
id: '2',
|
||||||
type: 'file',
|
type: 'file',
|
||||||
range: { startLine: 25, endLine: 30 }
|
range: { startLine: 25, endLine: 30 },
|
||||||
|
repo: 'github.com/sourcebot-dev/sourcebot'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -112,10 +119,11 @@ describe('calculateVisibleRanges', () => {
|
||||||
test('respects file boundaries - start of file', () => {
|
test('respects file boundaries - start of file', () => {
|
||||||
const references: FileReference[] = [
|
const references: FileReference[] = [
|
||||||
{
|
{
|
||||||
fileName: 'test.ts',
|
path: 'test.ts',
|
||||||
id: '1',
|
id: '1',
|
||||||
type: 'file',
|
type: 'file',
|
||||||
range: { startLine: 1, endLine: 5 }
|
range: { startLine: 1, endLine: 5 },
|
||||||
|
repo: 'github.com/sourcebot-dev/sourcebot'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -130,10 +138,11 @@ describe('calculateVisibleRanges', () => {
|
||||||
test('respects file boundaries - end of file', () => {
|
test('respects file boundaries - end of file', () => {
|
||||||
const references: FileReference[] = [
|
const references: FileReference[] = [
|
||||||
{
|
{
|
||||||
fileName: 'test.ts',
|
path: 'test.ts',
|
||||||
id: '1',
|
id: '1',
|
||||||
type: 'file',
|
type: 'file',
|
||||||
range: { startLine: 95, endLine: 100 }
|
range: { startLine: 95, endLine: 100 },
|
||||||
|
repo: 'github.com/sourcebot-dev/sourcebot'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -148,28 +157,32 @@ describe('calculateVisibleRanges', () => {
|
||||||
test('handles multiple ranges with complex overlaps', () => {
|
test('handles multiple ranges with complex overlaps', () => {
|
||||||
const references: FileReference[] = [
|
const references: FileReference[] = [
|
||||||
{
|
{
|
||||||
fileName: 'test.ts',
|
path: 'test.ts',
|
||||||
id: '1',
|
id: '1',
|
||||||
type: 'file',
|
type: 'file',
|
||||||
range: { startLine: 10, endLine: 15 }
|
range: { startLine: 10, endLine: 15 },
|
||||||
|
repo: 'github.com/sourcebot-dev/sourcebot'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fileName: 'test.ts',
|
path: 'test.ts',
|
||||||
id: '2',
|
id: '2',
|
||||||
type: 'file',
|
type: 'file',
|
||||||
range: { startLine: 20, endLine: 25 }
|
range: { startLine: 20, endLine: 25 },
|
||||||
|
repo: 'github.com/sourcebot-dev/sourcebot'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fileName: 'test.ts',
|
path: 'test.ts',
|
||||||
id: '3',
|
id: '3',
|
||||||
type: 'file',
|
type: 'file',
|
||||||
range: { startLine: 22, endLine: 30 }
|
range: { startLine: 22, endLine: 30 },
|
||||||
|
repo: 'github.com/sourcebot-dev/sourcebot'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fileName: 'test.ts',
|
path: 'test.ts',
|
||||||
id: '4',
|
id: '4',
|
||||||
type: 'file',
|
type: 'file',
|
||||||
range: { startLine: 50, endLine: 55 }
|
range: { startLine: 50, endLine: 55 },
|
||||||
|
repo: 'github.com/sourcebot-dev/sourcebot'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -195,16 +208,18 @@ describe('calculateVisibleRanges', () => {
|
||||||
test('ignores references without ranges', () => {
|
test('ignores references without ranges', () => {
|
||||||
const references: FileReference[] = [
|
const references: FileReference[] = [
|
||||||
{
|
{
|
||||||
fileName: 'test.ts',
|
path: 'test.ts',
|
||||||
id: '1',
|
id: '1',
|
||||||
type: 'file',
|
type: 'file',
|
||||||
// No range property
|
// No range property
|
||||||
|
repo: 'github.com/sourcebot-dev/sourcebot'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fileName: 'test.ts',
|
path: 'test.ts',
|
||||||
id: '2',
|
id: '2',
|
||||||
type: 'file',
|
type: 'file',
|
||||||
range: { startLine: 10, endLine: 15 }
|
range: { startLine: 10, endLine: 15 },
|
||||||
|
repo: 'github.com/sourcebot-dev/sourcebot'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -219,10 +234,11 @@ describe('calculateVisibleRanges', () => {
|
||||||
test('works with zero padding', () => {
|
test('works with zero padding', () => {
|
||||||
const references: FileReference[] = [
|
const references: FileReference[] = [
|
||||||
{
|
{
|
||||||
fileName: 'test.ts',
|
path: 'test.ts',
|
||||||
id: '1',
|
id: '1',
|
||||||
type: 'file',
|
type: 'file',
|
||||||
range: { startLine: 10, endLine: 15 }
|
range: { startLine: 10, endLine: 15 },
|
||||||
|
repo: 'github.com/sourcebot-dev/sourcebot'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -237,10 +253,11 @@ describe('calculateVisibleRanges', () => {
|
||||||
test('handles single line ranges', () => {
|
test('handles single line ranges', () => {
|
||||||
const references: FileReference[] = [
|
const references: FileReference[] = [
|
||||||
{
|
{
|
||||||
fileName: 'test.ts',
|
path: 'test.ts',
|
||||||
id: '1',
|
id: '1',
|
||||||
type: 'file',
|
type: 'file',
|
||||||
range: { startLine: 10, endLine: 10 }
|
range: { startLine: 10, endLine: 10 },
|
||||||
|
repo: 'github.com/sourcebot-dev/sourcebot'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -255,22 +272,25 @@ describe('calculateVisibleRanges', () => {
|
||||||
test('sorts ranges by start line', () => {
|
test('sorts ranges by start line', () => {
|
||||||
const references: FileReference[] = [
|
const references: FileReference[] = [
|
||||||
{
|
{
|
||||||
fileName: 'test.ts',
|
path: 'test.ts',
|
||||||
id: '1',
|
id: '1',
|
||||||
type: 'file',
|
type: 'file',
|
||||||
range: { startLine: 50, endLine: 55 }
|
range: { startLine: 50, endLine: 55 },
|
||||||
|
repo: 'github.com/sourcebot-dev/sourcebot'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fileName: 'test.ts',
|
path: 'test.ts',
|
||||||
id: '2',
|
id: '2',
|
||||||
type: 'file',
|
type: 'file',
|
||||||
range: { startLine: 10, endLine: 15 }
|
range: { startLine: 10, endLine: 15 },
|
||||||
|
repo: 'github.com/sourcebot-dev/sourcebot'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fileName: 'test.ts',
|
path: 'test.ts',
|
||||||
id: '3',
|
id: '3',
|
||||||
type: 'file',
|
type: 'file',
|
||||||
range: { startLine: 30, endLine: 35 }
|
range: { startLine: 30, endLine: 35 },
|
||||||
|
repo: 'github.com/sourcebot-dev/sourcebot'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -380,16 +400,18 @@ describe('StateField Integration', () => {
|
||||||
test('initial state calculation with references', () => {
|
test('initial state calculation with references', () => {
|
||||||
const references: FileReference[] = [
|
const references: FileReference[] = [
|
||||||
{
|
{
|
||||||
fileName: 'test.ts',
|
path: 'test.ts',
|
||||||
id: '1',
|
id: '1',
|
||||||
type: 'file',
|
type: 'file',
|
||||||
range: { startLine: 10, endLine: 15 }
|
range: { startLine: 10, endLine: 15 },
|
||||||
|
repo: 'github.com/sourcebot-dev/sourcebot'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fileName: 'test.ts',
|
path: 'test.ts',
|
||||||
id: '2',
|
id: '2',
|
||||||
type: 'file',
|
type: 'file',
|
||||||
range: { startLine: 25, endLine: 30 }
|
range: { startLine: 25, endLine: 30 },
|
||||||
|
repo: 'github.com/sourcebot-dev/sourcebot'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -457,7 +479,7 @@ describe('StateField Integration', () => {
|
||||||
// Update references
|
// Update references
|
||||||
const newReferences: FileReference[] = [
|
const newReferences: FileReference[] = [
|
||||||
{
|
{
|
||||||
fileName: 'test.ts',
|
path: 'test.ts',
|
||||||
id: '1',
|
id: '1',
|
||||||
type: 'file',
|
type: 'file',
|
||||||
range: { startLine: 10, endLine: 15 }
|
range: { startLine: 10, endLine: 15 }
|
||||||
|
|
@ -482,10 +504,11 @@ describe('StateField Integration', () => {
|
||||||
test('expandRegionEffect expands hidden region up', () => {
|
test('expandRegionEffect expands hidden region up', () => {
|
||||||
const references: FileReference[] = [
|
const references: FileReference[] = [
|
||||||
{
|
{
|
||||||
fileName: 'test.ts',
|
path: 'test.ts',
|
||||||
id: '1',
|
id: '1',
|
||||||
type: 'file',
|
type: 'file',
|
||||||
range: { startLine: 20, endLine: 25 }
|
range: { startLine: 20, endLine: 25 },
|
||||||
|
repo: 'github.com/sourcebot-dev/sourcebot'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -528,10 +551,11 @@ describe('StateField Integration', () => {
|
||||||
test('expandRegionEffect expands hidden region down', () => {
|
test('expandRegionEffect expands hidden region down', () => {
|
||||||
const references: FileReference[] = [
|
const references: FileReference[] = [
|
||||||
{
|
{
|
||||||
fileName: 'test.ts',
|
path: 'test.ts',
|
||||||
id: '1',
|
id: '1',
|
||||||
type: 'file',
|
type: 'file',
|
||||||
range: { startLine: 20, endLine: 25 }
|
range: { startLine: 20, endLine: 25 },
|
||||||
|
repo: 'github.com/sourcebot-dev/sourcebot'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -564,10 +588,11 @@ describe('StateField Integration', () => {
|
||||||
test('document changes recalculate state', () => {
|
test('document changes recalculate state', () => {
|
||||||
const references: FileReference[] = [
|
const references: FileReference[] = [
|
||||||
{
|
{
|
||||||
fileName: 'test.ts',
|
path: 'test.ts',
|
||||||
id: '1',
|
id: '1',
|
||||||
type: 'file',
|
type: 'file',
|
||||||
range: { startLine: 10, endLine: 15 }
|
range: { startLine: 10, endLine: 15 },
|
||||||
|
repo: 'github.com/sourcebot-dev/sourcebot'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -606,10 +631,11 @@ describe('StateField Integration', () => {
|
||||||
test('action creators work correctly', () => {
|
test('action creators work correctly', () => {
|
||||||
const references: FileReference[] = [
|
const references: FileReference[] = [
|
||||||
{
|
{
|
||||||
fileName: 'test.ts',
|
path: 'test.ts',
|
||||||
id: '1',
|
id: '1',
|
||||||
type: 'file',
|
type: 'file',
|
||||||
range: { startLine: 10, endLine: 15 }
|
range: { startLine: 10, endLine: 15 },
|
||||||
|
repo: 'github.com/sourcebot-dev/sourcebot'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -647,16 +673,18 @@ describe('StateField Integration', () => {
|
||||||
// Add references
|
// Add references
|
||||||
const references: FileReference[] = [
|
const references: FileReference[] = [
|
||||||
{
|
{
|
||||||
fileName: 'test.ts',
|
path: 'test.ts',
|
||||||
id: '1',
|
id: '1',
|
||||||
type: 'file',
|
type: 'file',
|
||||||
range: { startLine: 20, endLine: 25 }
|
range: { startLine: 20, endLine: 25 },
|
||||||
|
repo: 'github.com/sourcebot-dev/sourcebot'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fileName: 'test.ts',
|
path: 'test.ts',
|
||||||
id: '2',
|
id: '2',
|
||||||
type: 'file',
|
type: 'file',
|
||||||
range: { startLine: 60, endLine: 65 }
|
range: { startLine: 60, endLine: 65 },
|
||||||
|
repo: 'github.com/sourcebot-dev/sourcebot'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,12 +45,13 @@ function remarkReferencesPlugin() {
|
||||||
return function (tree: Nodes) {
|
return function (tree: Nodes) {
|
||||||
findAndReplace(tree, [
|
findAndReplace(tree, [
|
||||||
FILE_REFERENCE_REGEX,
|
FILE_REFERENCE_REGEX,
|
||||||
(_, fileName: string, startLine?: string, endLine?: string) => {
|
(_, repo: string, fileName: string, startLine?: string, endLine?: string) => {
|
||||||
// Create display text
|
// Create display text
|
||||||
let displayText = fileName.split('/').pop() ?? fileName;
|
let displayText = fileName.split('/').pop() ?? fileName;
|
||||||
|
|
||||||
const fileReference = createFileReference({
|
const fileReference = createFileReference({
|
||||||
fileName,
|
repo: repo,
|
||||||
|
path: fileName,
|
||||||
startLine,
|
startLine,
|
||||||
endLine,
|
endLine,
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,10 @@ interface ReferencedSourcesListViewProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
const resolveFileReference = (reference: FileReference, sources: FileSource[]): FileSource | undefined => {
|
const resolveFileReference = (reference: FileReference, sources: FileSource[]): FileSource | undefined => {
|
||||||
return sources.find((source) => source.path.endsWith(reference.fileName));
|
return sources.find(
|
||||||
|
(source) => source.repo.endsWith(reference.repo) &&
|
||||||
|
source.path.endsWith(reference.path)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const getFileId = (fileSource: FileSource) => {
|
const getFileId = (fileSource: FileSource) => {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
|
|
||||||
export const FILE_REFERENCE_PREFIX = '@file:';
|
export const FILE_REFERENCE_PREFIX = '@file:';
|
||||||
export const FILE_REFERENCE_REGEX = new RegExp(`${FILE_REFERENCE_PREFIX}\\{([^:}]+)(?::(\\d+)(?:-(\\d+))?)?\\}`, 'g');
|
export const FILE_REFERENCE_REGEX = new RegExp(
|
||||||
|
// @file:{repoName::fileName:startLine-endLine}
|
||||||
|
`${FILE_REFERENCE_PREFIX}\\{([^:}]+)::([^:}]+)(?::(\\d+)(?:-(\\d+))?)?\\}`,
|
||||||
|
'g'
|
||||||
|
);
|
||||||
|
|
||||||
export const ANSWER_TAG = '<!--answer-->';
|
export const ANSWER_TAG = '<!--answer-->';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,8 @@ export type Source = z.infer<typeof sourceSchema>;
|
||||||
const fileReferenceSchema = z.object({
|
const fileReferenceSchema = z.object({
|
||||||
type: z.literal('file'),
|
type: z.literal('file'),
|
||||||
id: z.string(),
|
id: z.string(),
|
||||||
fileName: z.string(),
|
repo: z.string(),
|
||||||
|
path: z.string(),
|
||||||
range: z.object({
|
range: z.object({
|
||||||
startLine: z.number(),
|
startLine: z.number(),
|
||||||
endLine: z.number(),
|
endLine: z.number(),
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ test('useExtractReferences extracts file references from text content', () => {
|
||||||
parts: [
|
parts: [
|
||||||
{
|
{
|
||||||
type: 'text',
|
type: 'text',
|
||||||
text: 'The auth flow is implemented in @file:{auth.ts} and uses sessions @file:{auth.ts:45-60}.'
|
text: 'The auth flow is implemented in @file:{github.com/sourcebot-dev/sourcebot::auth.ts} and uses sessions @file:{github.com/sourcebot-dev/sourcebot::auth.ts:45-60}.'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
@ -20,15 +20,18 @@ test('useExtractReferences extracts file references from text content', () => {
|
||||||
|
|
||||||
expect(result.current).toHaveLength(2);
|
expect(result.current).toHaveLength(2);
|
||||||
expect(result.current[0]).toMatchObject({
|
expect(result.current[0]).toMatchObject({
|
||||||
fileName: 'auth.ts',
|
repo: 'github.com/sourcebot-dev/sourcebot',
|
||||||
id: getFileReferenceId({ fileName: 'auth.ts' }),
|
path: 'auth.ts',
|
||||||
|
id: getFileReferenceId({ repo: 'github.com/sourcebot-dev/sourcebot', path: 'auth.ts' }),
|
||||||
type: 'file',
|
type: 'file',
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result.current[1]).toMatchObject({
|
expect(result.current[1]).toMatchObject({
|
||||||
fileName: 'auth.ts',
|
repo: 'github.com/sourcebot-dev/sourcebot',
|
||||||
|
path: 'auth.ts',
|
||||||
id: getFileReferenceId({
|
id: getFileReferenceId({
|
||||||
fileName: 'auth.ts',
|
repo: 'github.com/sourcebot-dev/sourcebot',
|
||||||
|
path: 'auth.ts',
|
||||||
range: {
|
range: {
|
||||||
startLine: 45,
|
startLine: 45,
|
||||||
endLine: 60,
|
endLine: 60,
|
||||||
|
|
@ -49,7 +52,7 @@ test('useExtractReferences extracts file references from reasoning content', ()
|
||||||
parts: [
|
parts: [
|
||||||
{
|
{
|
||||||
type: 'reasoning',
|
type: 'reasoning',
|
||||||
text: 'The auth flow is implemented in @file:{auth.ts} and uses sessions @file:{auth.ts:45-60}.'
|
text: 'The auth flow is implemented in @file:{github.com/sourcebot-dev/sourcebot::auth.ts} and uses sessions @file:{github.com/sourcebot-dev/sourcebot::auth.ts:45-60}.'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
@ -58,15 +61,18 @@ test('useExtractReferences extracts file references from reasoning content', ()
|
||||||
|
|
||||||
expect(result.current).toHaveLength(2);
|
expect(result.current).toHaveLength(2);
|
||||||
expect(result.current[0]).toMatchObject({
|
expect(result.current[0]).toMatchObject({
|
||||||
fileName: 'auth.ts',
|
repo: 'github.com/sourcebot-dev/sourcebot',
|
||||||
id: getFileReferenceId({ fileName: 'auth.ts' }),
|
path: 'auth.ts',
|
||||||
|
id: getFileReferenceId({ repo: 'github.com/sourcebot-dev/sourcebot', path: 'auth.ts' }),
|
||||||
type: 'file',
|
type: 'file',
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result.current[1]).toMatchObject({
|
expect(result.current[1]).toMatchObject({
|
||||||
fileName: 'auth.ts',
|
repo: 'github.com/sourcebot-dev/sourcebot',
|
||||||
|
path: 'auth.ts',
|
||||||
id: getFileReferenceId({
|
id: getFileReferenceId({
|
||||||
fileName: 'auth.ts',
|
repo: 'github.com/sourcebot-dev/sourcebot',
|
||||||
|
path: 'auth.ts',
|
||||||
range: {
|
range: {
|
||||||
startLine: 45,
|
startLine: 45,
|
||||||
endLine: 60,
|
endLine: 60,
|
||||||
|
|
@ -87,15 +93,15 @@ test('useExtractReferences extracts file references from multi-part', () => {
|
||||||
parts: [
|
parts: [
|
||||||
{
|
{
|
||||||
type: 'text',
|
type: 'text',
|
||||||
text: 'The auth flow is implemented in @file:{auth.ts}.'
|
text: 'The auth flow is implemented in @file:{github.com/sourcebot-dev/sourcebot::auth.ts}.'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'reasoning',
|
type: 'reasoning',
|
||||||
text: 'We need to check the session handling in @file:{session.ts:10-20}.'
|
text: 'We need to check the session handling in @file:{github.com/sourcebot-dev/sourcebot::session.ts:10-20}.'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'text',
|
type: 'text',
|
||||||
text: 'The configuration is stored in @file:{config.json} and @file:{utils.ts:5}.'
|
text: 'The configuration is stored in @file:{github.com/sourcebot-dev/sourcebot::config.json} and @file:{github.com/sourcebot-dev/sourcebot::utils.ts:5}.'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
@ -106,16 +112,19 @@ test('useExtractReferences extracts file references from multi-part', () => {
|
||||||
|
|
||||||
// From text part
|
// From text part
|
||||||
expect(result.current[0]).toMatchObject({
|
expect(result.current[0]).toMatchObject({
|
||||||
fileName: 'auth.ts',
|
repo: 'github.com/sourcebot-dev/sourcebot',
|
||||||
id: getFileReferenceId({ fileName: 'auth.ts' }),
|
path: 'auth.ts',
|
||||||
|
id: getFileReferenceId({ repo: 'github.com/sourcebot-dev/sourcebot', path: 'auth.ts' }),
|
||||||
type: 'file',
|
type: 'file',
|
||||||
});
|
});
|
||||||
|
|
||||||
// From reasoning part
|
// From reasoning part
|
||||||
expect(result.current[1]).toMatchObject({
|
expect(result.current[1]).toMatchObject({
|
||||||
fileName: 'session.ts',
|
repo: 'github.com/sourcebot-dev/sourcebot',
|
||||||
|
path: 'session.ts',
|
||||||
id: getFileReferenceId({
|
id: getFileReferenceId({
|
||||||
fileName: 'session.ts',
|
repo: 'github.com/sourcebot-dev/sourcebot',
|
||||||
|
path: 'session.ts',
|
||||||
range: {
|
range: {
|
||||||
startLine: 10,
|
startLine: 10,
|
||||||
endLine: 20,
|
endLine: 20,
|
||||||
|
|
@ -129,15 +138,18 @@ test('useExtractReferences extracts file references from multi-part', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result.current[2]).toMatchObject({
|
expect(result.current[2]).toMatchObject({
|
||||||
fileName: 'config.json',
|
repo: 'github.com/sourcebot-dev/sourcebot',
|
||||||
id: getFileReferenceId({ fileName: 'config.json' }),
|
path: 'config.json',
|
||||||
|
id: getFileReferenceId({ repo: 'github.com/sourcebot-dev/sourcebot', path: 'config.json' }),
|
||||||
type: 'file',
|
type: 'file',
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result.current[3]).toMatchObject({
|
expect(result.current[3]).toMatchObject({
|
||||||
fileName: 'utils.ts',
|
repo: 'github.com/sourcebot-dev/sourcebot',
|
||||||
|
path: 'utils.ts',
|
||||||
id: getFileReferenceId({
|
id: getFileReferenceId({
|
||||||
fileName: 'utils.ts',
|
repo: 'github.com/sourcebot-dev/sourcebot',
|
||||||
|
path: 'utils.ts',
|
||||||
range: {
|
range: {
|
||||||
startLine: 5,
|
startLine: 5,
|
||||||
endLine: 5,
|
endLine: 5,
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,11 @@ export const useExtractReferences = (message?: SBChatMessage) => {
|
||||||
|
|
||||||
let match;
|
let match;
|
||||||
while ((match = FILE_REFERENCE_REGEX.exec(content ?? '')) !== null && match !== null) {
|
while ((match = FILE_REFERENCE_REGEX.exec(content ?? '')) !== null && match !== null) {
|
||||||
const [_, fileName, startLine, endLine] = match;
|
const [_, repo, fileName, startLine, endLine] = match;
|
||||||
|
|
||||||
const fileReference = createFileReference({
|
const fileReference = createFileReference({
|
||||||
fileName,
|
repo: repo,
|
||||||
|
path: fileName,
|
||||||
startLine,
|
startLine,
|
||||||
endLine,
|
endLine,
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -13,26 +13,30 @@ vi.mock('@/env.mjs', () => ({
|
||||||
|
|
||||||
test('fileReferenceToString formats file references correctly', () => {
|
test('fileReferenceToString formats file references correctly', () => {
|
||||||
expect(fileReferenceToString({
|
expect(fileReferenceToString({
|
||||||
fileName: 'auth.ts'
|
repo: 'github.com/sourcebot-dev/sourcebot',
|
||||||
})).toBe('@file:{auth.ts}');
|
path: 'auth.ts'
|
||||||
|
})).toBe('@file:{github.com/sourcebot-dev/sourcebot::auth.ts}');
|
||||||
|
|
||||||
expect(fileReferenceToString({
|
expect(fileReferenceToString({
|
||||||
fileName: 'auth.ts',
|
repo: 'github.com/sourcebot-dev/sourcebot',
|
||||||
|
path: 'auth.ts',
|
||||||
range: {
|
range: {
|
||||||
startLine: 45,
|
startLine: 45,
|
||||||
endLine: 60,
|
endLine: 60,
|
||||||
}
|
}
|
||||||
})).toBe('@file:{auth.ts:45-60}');
|
})).toBe('@file:{github.com/sourcebot-dev/sourcebot::auth.ts:45-60}');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('fileReferenceToString matches FILE_REFERENCE_REGEX', () => {
|
test('fileReferenceToString matches FILE_REFERENCE_REGEX', () => {
|
||||||
expect(FILE_REFERENCE_REGEX.test(fileReferenceToString({
|
expect(FILE_REFERENCE_REGEX.test(fileReferenceToString({
|
||||||
fileName: 'auth.ts'
|
repo: 'github.com/sourcebot-dev/sourcebot',
|
||||||
|
path: 'auth.ts'
|
||||||
}))).toBe(true);
|
}))).toBe(true);
|
||||||
|
|
||||||
FILE_REFERENCE_REGEX.lastIndex = 0;
|
FILE_REFERENCE_REGEX.lastIndex = 0;
|
||||||
expect(FILE_REFERENCE_REGEX.test(fileReferenceToString({
|
expect(FILE_REFERENCE_REGEX.test(fileReferenceToString({
|
||||||
fileName: 'auth.ts',
|
repo: 'github.com/sourcebot-dev/sourcebot',
|
||||||
|
path: 'auth.ts',
|
||||||
range: {
|
range: {
|
||||||
startLine: 45,
|
startLine: 45,
|
||||||
endLine: 60,
|
endLine: 60,
|
||||||
|
|
@ -240,55 +244,55 @@ test('getAnswerPartFromAssistantMessage returns undefined when streaming and no
|
||||||
});
|
});
|
||||||
|
|
||||||
test('repairCitations fixes missing colon after @file', () => {
|
test('repairCitations fixes missing colon after @file', () => {
|
||||||
const input = 'See the function in @file{auth.ts} for details.';
|
const input = 'See the function in @file{github.com/sourcebot-dev/sourcebot::auth.ts} for details.';
|
||||||
const expected = 'See the function in @file:{auth.ts} for details.';
|
const expected = 'See the function in @file:{github.com/sourcebot-dev/sourcebot::auth.ts} for details.';
|
||||||
expect(repairCitations(input)).toBe(expected);
|
expect(repairCitations(input)).toBe(expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('repairCitations fixes missing colon with range', () => {
|
test('repairCitations fixes missing colon with range', () => {
|
||||||
const input = 'Check @file{config.ts:15-20} for the configuration.';
|
const input = 'Check @file{github.com/sourcebot-dev/sourcebot::config.ts:15-20} for the configuration.';
|
||||||
const expected = 'Check @file:{config.ts:15-20} for the configuration.';
|
const expected = 'Check @file:{github.com/sourcebot-dev/sourcebot::config.ts:15-20} for the configuration.';
|
||||||
expect(repairCitations(input)).toBe(expected);
|
expect(repairCitations(input)).toBe(expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('repairCitations fixes missing braces around filename', () => {
|
test('repairCitations fixes missing braces around filename', () => {
|
||||||
const input = 'The logic is in @file:utils.js and handles validation.';
|
const input = 'The logic is in @file:github.com/sourcebot-dev/sourcebot::utils.js and handles validation.';
|
||||||
const expected = 'The logic is in @file:{utils.js} and handles validation.';
|
const expected = 'The logic is in @file:{github.com/sourcebot-dev/sourcebot::utils.js} and handles validation.';
|
||||||
expect(repairCitations(input)).toBe(expected);
|
expect(repairCitations(input)).toBe(expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('repairCitations fixes missing braces with path', () => {
|
test('repairCitations fixes missing braces with path', () => {
|
||||||
const input = 'Look at @file:src/components/Button.tsx for the component.';
|
const input = 'Look at @file:github.com/sourcebot-dev/sourcebot::src/components/Button.tsx for the component.';
|
||||||
const expected = 'Look at @file:{src/components/Button.tsx} for the component.';
|
const expected = 'Look at @file:{github.com/sourcebot-dev/sourcebot::src/components/Button.tsx} for the component.';
|
||||||
expect(repairCitations(input)).toBe(expected);
|
expect(repairCitations(input)).toBe(expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('repairCitations removes multiple ranges keeping only first', () => {
|
test('repairCitations removes multiple ranges keeping only first', () => {
|
||||||
const input = 'See @file:{service.ts:10-15,20-25,30-35} for implementation.';
|
const input = 'See @file:{github.com/sourcebot-dev/sourcebot::service.ts:10-15,20-25,30-35} for implementation.';
|
||||||
const expected = 'See @file:{service.ts:10-15} for implementation.';
|
const expected = 'See @file:{github.com/sourcebot-dev/sourcebot::service.ts:10-15} for implementation.';
|
||||||
expect(repairCitations(input)).toBe(expected);
|
expect(repairCitations(input)).toBe(expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('repairCitations fixes malformed triple number ranges', () => {
|
test('repairCitations fixes malformed triple number ranges', () => {
|
||||||
const input = 'Check @file:{handler.ts:5-10-15} for the logic.';
|
const input = 'Check @file:{github.com/sourcebot-dev/sourcebot::handler.ts:5-10-15} for the logic.';
|
||||||
const expected = 'Check @file:{handler.ts:5-10} for the logic.';
|
const expected = 'Check @file:{github.com/sourcebot-dev/sourcebot::handler.ts:5-10} for the logic.';
|
||||||
expect(repairCitations(input)).toBe(expected);
|
expect(repairCitations(input)).toBe(expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('repairCitations handles multiple citations in same text', () => {
|
test('repairCitations handles multiple citations in same text', () => {
|
||||||
const input = 'See @file{auth.ts} and @file:config.js for setup details.';
|
const input = 'See @file{github.com/sourcebot-dev/sourcebot::auth.ts} and @file:github.com/sourcebot-dev/sourcebot::config.js for setup details.';
|
||||||
const expected = 'See @file:{auth.ts} and @file:{config.js} for setup details.';
|
const expected = 'See @file:{github.com/sourcebot-dev/sourcebot::auth.ts} and @file:{github.com/sourcebot-dev/sourcebot::config.js} for setup details.';
|
||||||
expect(repairCitations(input)).toBe(expected);
|
expect(repairCitations(input)).toBe(expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('repairCitations leaves correctly formatted citations unchanged', () => {
|
test('repairCitations leaves correctly formatted citations unchanged', () => {
|
||||||
const input = 'The function @file:{utils.ts:42-50} handles validation correctly.';
|
const input = 'The function @file:{github.com/sourcebot-dev/sourcebot::utils.ts:42-50} handles validation correctly.';
|
||||||
expect(repairCitations(input)).toBe(input);
|
expect(repairCitations(input)).toBe(input);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('repairCitations handles edge cases with spaces and punctuation', () => {
|
test('repairCitations handles edge cases with spaces and punctuation', () => {
|
||||||
const input = 'Functions like @file:helper.ts, @file{main.js}, and @file:{app.ts:1-5,10-15} work.';
|
const input = 'Functions like @file:github.com/sourcebot-dev/sourcebot::helper.ts, @file{github.com/sourcebot-dev/sourcebot::main.js}, and @file:{github.com/sourcebot-dev/sourcebot::app.ts:1-5,10-15} work.';
|
||||||
const expected = 'Functions like @file:{helper.ts}, @file:{main.js}, and @file:{app.ts:1-5} work.';
|
const expected = 'Functions like @file:{github.com/sourcebot-dev/sourcebot::helper.ts}, @file:{github.com/sourcebot-dev/sourcebot::main.js}, and @file:{github.com/sourcebot-dev/sourcebot::app.ts:1-5} work.';
|
||||||
expect(repairCitations(input)).toBe(expected);
|
expect(repairCitations(input)).toBe(expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -302,24 +306,24 @@ test('repairCitations returns text without citations unchanged', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('repairCitations handles complex file paths correctly', () => {
|
test('repairCitations handles complex file paths correctly', () => {
|
||||||
const input = 'Check @file:src/components/ui/Button/index.tsx for implementation.';
|
const input = 'Check @file:github.com/sourcebot-dev/sourcebot::src/components/ui/Button/index.tsx for implementation.';
|
||||||
const expected = 'Check @file:{src/components/ui/Button/index.tsx} for implementation.';
|
const expected = 'Check @file:{github.com/sourcebot-dev/sourcebot::src/components/ui/Button/index.tsx} for implementation.';
|
||||||
expect(repairCitations(input)).toBe(expected);
|
expect(repairCitations(input)).toBe(expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('repairCitations handles files with numbers and special characters', () => {
|
test('repairCitations handles files with numbers and special characters', () => {
|
||||||
const input = 'See @file{utils-v2.0.1.ts} and @file:config_2024.json for setup.';
|
const input = 'See @file{github.com/sourcebot-dev/sourcebot::utils-v2.0.1.ts} and @file:github.com/sourcebot-dev/sourcebot::config_2024.json for setup.';
|
||||||
const expected = 'See @file:{utils-v2.0.1.ts} and @file:{config_2024.json} for setup.';
|
const expected = 'See @file:{github.com/sourcebot-dev/sourcebot::utils-v2.0.1.ts} and @file:{github.com/sourcebot-dev/sourcebot::config_2024.json} for setup.';
|
||||||
expect(repairCitations(input)).toBe(expected);
|
expect(repairCitations(input)).toBe(expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('repairCitations handles citation at end of sentence', () => {
|
test('repairCitations handles citation at end of sentence', () => {
|
||||||
const input = 'The implementation is in @file:helper.ts.';
|
const input = 'The implementation is in @file:github.com/sourcebot-dev/sourcebot::helper.ts.';
|
||||||
const expected = 'The implementation is in @file:{helper.ts}.';
|
const expected = 'The implementation is in @file:{github.com/sourcebot-dev/sourcebot::helper.ts}.';
|
||||||
expect(repairCitations(input)).toBe(expected);
|
expect(repairCitations(input)).toBe(expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('repairCitations preserves already correct citations with ranges', () => {
|
test('repairCitations preserves already correct citations with ranges', () => {
|
||||||
const input = 'The function @file:{utils.ts:10-20} and variable @file:{config.js:5} work correctly.';
|
const input = 'The function @file:{github.com/sourcebot-dev/sourcebot::utils.ts:10-20} and variable @file:{github.com/sourcebot-dev/sourcebot::config.js:5} work correctly.';
|
||||||
expect(repairCitations(input)).toBe(input);
|
expect(repairCitations(input)).toBe(input);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -129,7 +129,7 @@ export const slateContentToString = (children: Descendant[]): string => {
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'file':
|
case 'file':
|
||||||
return `${fileReferenceToString({ fileName: child.data.name })} `;
|
return `${fileReferenceToString({ repo: child.data.repo, path: child.data.path })} `;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -210,15 +210,15 @@ export const createUIMessage = (text: string, mentions: MentionData[], selectedR
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getFileReferenceId = ({ fileName, range }: Omit<FileReference, 'type' | 'id'>) => {
|
export const getFileReferenceId = ({ repo, path, range }: Omit<FileReference, 'type' | 'id'>) => {
|
||||||
return `file-reference-${fileName}${range ? `-${range.startLine}-${range.endLine}` : ''}`;
|
return `file-reference-${repo}::${path}${range ? `-${range.startLine}-${range.endLine}` : ''}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const fileReferenceToString = ({ fileName, range }: Omit<FileReference, 'type' | 'id'>) => {
|
export const fileReferenceToString = ({ repo, path, range }: Omit<FileReference, 'type' | 'id'>) => {
|
||||||
return `${FILE_REFERENCE_PREFIX}{${fileName}${range ? `:${range.startLine}-${range.endLine}` : ''}}`;
|
return `${FILE_REFERENCE_PREFIX}{${repo}::${path}${range ? `:${range.startLine}-${range.endLine}` : ''}}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createFileReference = ({ fileName, startLine, endLine }: { fileName: string, startLine?: string, endLine?: string }): FileReference => {
|
export const createFileReference = ({ repo, path, startLine, endLine }: { repo: string, path: string, startLine?: string, endLine?: string }): FileReference => {
|
||||||
const range = startLine && endLine ? {
|
const range = startLine && endLine ? {
|
||||||
startLine: parseInt(startLine),
|
startLine: parseInt(startLine),
|
||||||
endLine: parseInt(endLine),
|
endLine: parseInt(endLine),
|
||||||
|
|
@ -229,8 +229,9 @@ export const createFileReference = ({ fileName, startLine, endLine }: { fileName
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: 'file',
|
type: 'file',
|
||||||
id: getFileReferenceId({ fileName, range }),
|
id: getFileReferenceId({ repo, path, range }),
|
||||||
fileName,
|
repo,
|
||||||
|
path,
|
||||||
range,
|
range,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -241,7 +242,7 @@ export const createFileReference = ({ fileName, startLine, endLine }: { fileName
|
||||||
* links.
|
* links.
|
||||||
*/
|
*/
|
||||||
export const convertLLMOutputToPortableMarkdown = (text: string): string => {
|
export const convertLLMOutputToPortableMarkdown = (text: string): string => {
|
||||||
return text.replace(FILE_REFERENCE_REGEX, (_, fileName, startLine, endLine) => {
|
return text.replace(FILE_REFERENCE_REGEX, (_, _repo, fileName, startLine, endLine) => {
|
||||||
const displayName = fileName.split('/').pop() || fileName;
|
const displayName = fileName.split('/').pop() || fileName;
|
||||||
|
|
||||||
let linkText = displayName;
|
let linkText = displayName;
|
||||||
|
|
@ -294,9 +295,9 @@ export const repairCitations = (text: string): string => {
|
||||||
// Fix missing braces: @file:filename -> @file:{filename}
|
// Fix missing braces: @file:filename -> @file:{filename}
|
||||||
.replace(/@file:([^\s{]\S*?)(\s|[,;!?](?:\s|$)|\.(?:\s|$)|$)/g, '@file:{$1}$2')
|
.replace(/@file:([^\s{]\S*?)(\s|[,;!?](?:\s|$)|\.(?:\s|$)|$)/g, '@file:{$1}$2')
|
||||||
// Fix multiple ranges: keep only first range
|
// Fix multiple ranges: keep only first range
|
||||||
.replace(/@file:\{([^:}]+):(\d+-\d+),[\d,-]+\}/g, '@file:{$1:$2}')
|
.replace(/@file:\{(.+?):(\d+-\d+),[\d,-]+\}/g, '@file:{$1:$2}')
|
||||||
// Fix malformed ranges
|
// Fix malformed ranges
|
||||||
.replace(/@file:\{([^:}]+):(\d+)-(\d+)-(\d+)\}/g, '@file:{$1:$2-$3}');
|
.replace(/@file:\{(.+?):(\d+)-(\d+)-(\d+)\}/g, '@file:{$1:$2-$3}');
|
||||||
};
|
};
|
||||||
|
|
||||||
// Attempts to find the part of the assistant's message
|
// Attempts to find the part of the assistant's message
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue