Skip to content

Commit 39904e2

Browse files
committed
feat: MVP
1 parent 190b355 commit 39904e2

2 files changed

Lines changed: 61 additions & 109 deletions

File tree

src/geminiClient.ts

Lines changed: 14 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -78,67 +78,30 @@ export class GeminiApiClient implements GeminiClient {
7878
}
7979

8080
private buildExplanationPrompt(codeSnippet: string, context: CodeContext): string {
81-
let prompt = `You are a helpful programming assistant. Please analyze the following code and provide a comprehensive explanation.
81+
let prompt = `Analyze the selected code (marked with >>>) and provide a concise explanation.
8282
83-
**IMPORTANT**: The code marked with ">>> code <<< // [SELECTED BY USER]" is what the user specifically selected for explanation. Focus your explanation on this selected code, but use the surrounding context to provide better understanding.
84-
85-
**Code to analyze:**
83+
**Code with line numbers:**
8684
\`\`\`${context.language}
8785
${codeSnippet}
8886
\`\`\`
8987
90-
**Context:**`;
91-
92-
if (context.functionName) {
93-
prompt += `\n- This code is part of the function: ${context.functionName}`;
94-
}
95-
96-
if (context.className) {
97-
prompt += `\n- This code is part of the class: ${context.className}`;
98-
}
99-
100-
if (context.variables.length > 0) {
101-
prompt += `\n- Related variables: ${context.variables.join(', ')}`;
102-
}
103-
104-
if (context.imports.length > 0) {
105-
prompt += `\n- Relevant imports: ${context.imports.join(', ')}`;
106-
}
107-
108-
prompt += `\n- Programming language: ${context.language}`;
109-
110-
prompt += `\n\n**Please provide a well-formatted explanation with:**
111-
1. **Primary Focus**: A clear explanation of what the SELECTED code (marked with >>>) does
112-
2. **How it works**: Explain the mechanics and logic of the selected code
113-
3. **Context Integration**: How the selected code fits within the surrounding code/function/class
114-
4. **Related Lines**: Identify OTHER lines in the provided code that are directly related to or affected by the selected code (NOT the selected lines themselves)
115-
5. **Important concepts**: Any important patterns, algorithms, or best practices demonstrated
88+
**Instructions:**
89+
1. Focus ONLY on the selected code (marked with >>>)
90+
2. Be concise - 2-3 short paragraphs maximum
91+
3. If you identify related lines that help understand the selected code, mention their line numbers
92+
4. Use the surrounding code as context but don't explain it unless directly relevant
11693
117-
**CRITICAL**: When identifying "related lines", only mention line numbers or code snippets that are NOT part of the user's selection but are relevant to understanding the selected code.
118-
119-
**Formatting Guidelines:**
120-
- Use markdown formatting for better readability
121-
- Wrap any code snippets in triple backticks with language specification
122-
- Use **bold** for important terms
123-
- Use bullet points or numbered lists for clarity
124-
- If identifying related lines, format them as: "Related lines: Line X (purpose), Line Y (purpose)"
125-
- Keep the explanation comprehensive but focused on the selected code
126-
127-
Example format:
128-
## What the selected code does
129-
Brief overview of the selected code's purpose...
94+
**Response format:**
95+
## Purpose
96+
Brief explanation of what the selected code does.
13097
13198
## How it works
132-
Step-by-step breakdown of the selected code...
133-
134-
## Context and integration
135-
How this fits with the surrounding code...
99+
Concise explanation of the mechanism.
136100
137-
## Related code elements
138-
- Line X: [explanation of how this line relates]
139-
- Variable Y: [explanation of how this relates]
101+
## Related lines
102+
If applicable: "Lines X, Y: brief explanation of relevance"
140103
141-
Keep the explanation suitable for a developer trying to understand the selected code.`;
104+
Keep it focused and under 300 words.`;
142105

143106
return prompt;
144107
}

src/selectionHandler.ts

Lines changed: 47 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,7 @@ export class SelectionHandler {
241241

242242
console.log(`Sending full file context to Gemini API (${enhancedContext.length} characters)`);
243243
console.log(`Selected text: "${selectedText}" (lines ${startLine + 1}-${endLine + 1})`);
244+
console.log('Context preview (first 500 chars):', enhancedContext.substring(0, 500) + '...');
244245

245246
// Generate explanation using Gemini with retry logic
246247
const explanation = await this.errorHandler.retryWithBackoff(async () => {
@@ -353,104 +354,92 @@ export class SelectionHandler {
353354
const lines = fullFileContent.split('\n');
354355
const maxContextLength = 8000; // Reasonable limit for API calls
355356

356-
// If the file is small enough, send the whole thing with selection markers
357+
// If the file is small enough, send the whole thing with line numbers
357358
if (fullFileContent.length <= maxContextLength) {
358-
// Add markers to indicate the selected portion
359+
// Add line numbers and selection markers
359360
const markedLines = lines.map((line, index) => {
361+
const lineNumber = index + 1; // 1-based line numbers
360362
if (index >= startLine && index <= endLine) {
361-
return `>>> ${line} <<< // [SELECTED BY USER]`;
363+
return `${lineNumber}: >>> ${line} <<< // [SELECTED BY USER]`;
362364
}
363-
return line;
365+
return `${lineNumber}: ${line}`;
364366
});
365367

366368
return markedLines.join('\n');
367369
}
368370

369-
// For larger files, include context around the selection
371+
// For larger files, include context around the selection WITH ACTUAL LINE NUMBERS
370372
const contextRadius = 20; // Lines of context before and after selection
371373
const contextStart = Math.max(0, startLine - contextRadius);
372374
const contextEnd = Math.min(lines.length - 1, endLine + contextRadius);
373375

374376
let contextLines: string[] = [];
375377

376-
// Add file header info if available
377-
if (context.imports && context.imports.length > 0) {
378-
contextLines.push('// File imports:');
379-
context.imports.forEach((imp: string) => {
380-
contextLines.push(`// ${imp}`);
381-
});
382-
contextLines.push('');
383-
}
378+
// Add context info
379+
contextLines.push(`// Context: Lines ${contextStart + 1}-${contextEnd + 1} of ${lines.length} total lines`);
380+
contextLines.push(`// Selected: Lines ${startLine + 1}-${endLine + 1}`);
381+
contextLines.push('');
384382

385-
// Add context before selection
386-
if (contextStart > 0) {
387-
contextLines.push('// ... (earlier code omitted) ...');
388-
contextLines.push('');
389-
}
390-
391-
// Add the contextual code with selection markers
383+
// Add the contextual code with ACTUAL line numbers and selection markers
392384
for (let i = contextStart; i <= contextEnd; i++) {
385+
const lineNumber = i + 1; // 1-based line numbers
393386
if (i >= startLine && i <= endLine) {
394-
contextLines.push(`>>> ${lines[i]} <<< // [SELECTED BY USER]`);
387+
contextLines.push(`${lineNumber}: >>> ${lines[i]} <<< // [SELECTED BY USER]`);
395388
} else {
396-
contextLines.push(lines[i]);
389+
contextLines.push(`${lineNumber}: ${lines[i]}`);
397390
}
398391
}
399392

400-
// Add indicator if there's more code after
401-
if (contextEnd < lines.length - 1) {
402-
contextLines.push('');
403-
contextLines.push('// ... (later code omitted) ...');
404-
}
405-
406393
return contextLines.join('\n');
407394
}
408395

409396
private parseRelatedLinesFromExplanation(explanation: string): number[] {
410397
const relatedLines: number[] = [];
411398

412-
// Look for patterns like "Line 15", "line 23", "Lines 10-15", etc.
399+
// Look for "Lines X, Y" or "Line X" patterns (more precise matching)
413400
const linePatterns = [
414-
/\bline\s+(\d+)/gi, // "line 15"
415-
/\blines?\s+(\d+)(?:\s*[-]\s*(\d+))?/gi, // "line 15" or "lines 15-18"
416-
/\bLine\s+(\d+)/g, // "Line 15" (capital L)
417-
/\bLines\s+(\d+)(?:\s*[-]\s*(\d+))?/g // "Lines 15-18"
401+
/\bLines?\s+(\d+)(?:\s*[,\s]+(\d+))*(?:\s*[-]\s*(\d+))?/gi, // "Lines 15, 20" or "Line 15-18"
402+
/\bLine\s+(\d+)/gi, // "Line 15"
403+
/\b(\d+):\s*\w/g // Look for line numbers followed by colon (from our format)
418404
];
419405

420-
for (const pattern of linePatterns) {
421-
let match;
422-
while ((match = pattern.exec(explanation)) !== null) {
423-
const startLine = parseInt(match[1]) - 1; // Convert to 0-based indexing
424-
const endLine = match[2] ? parseInt(match[2]) - 1 : startLine;
425-
426-
// Add all lines in the range
427-
for (let line = startLine; line <= endLine; line++) {
428-
if (line >= 0 && !relatedLines.includes(line)) {
429-
relatedLines.push(line);
406+
// First, try to find explicit "Related lines" sections
407+
const relatedSectionMatch = explanation.match(/related\s+lines?[:\s]*([^\n]*)/gi);
408+
if (relatedSectionMatch) {
409+
for (const match of relatedSectionMatch) {
410+
const numbers = match.match(/\d+/g);
411+
if (numbers) {
412+
for (const num of numbers) {
413+
const lineNum = parseInt(num) - 1; // Convert to 0-based
414+
if (lineNum >= 0 && !relatedLines.includes(lineNum)) {
415+
relatedLines.push(lineNum);
416+
}
430417
}
431418
}
432419
}
433420
}
434421

435-
// Also look for code snippets in backticks that might reference line numbers
436-
const codeBlockPattern = /```[\s\S]*?```/g;
437-
explanation.replace(codeBlockPattern, ''); // Remove code blocks to avoid false positives
438-
439-
// Look for "related lines:" sections
440-
const relatedSectionPattern = /related\s+(?:lines?|code|elements?)[\s:]*([^\n]*)/gi;
441-
let match;
442-
while ((match = relatedSectionPattern.exec(explanation)) !== null) {
443-
const section = match[1];
444-
const numberPattern = /\d+/g;
445-
let numberMatch;
446-
while ((numberMatch = numberPattern.exec(section)) !== null) {
447-
const lineNum = parseInt(numberMatch[0]) - 1; // Convert to 0-based
448-
if (lineNum >= 0 && !relatedLines.includes(lineNum)) {
449-
relatedLines.push(lineNum);
422+
// If no explicit related lines section, look for line references in the text
423+
if (relatedLines.length === 0) {
424+
const lineReferences = explanation.match(/\blines?\s+\d+(?:[,\s]+\d+)*/gi);
425+
if (lineReferences) {
426+
for (const ref of lineReferences) {
427+
const numbers = ref.match(/\d+/g);
428+
if (numbers) {
429+
for (const num of numbers) {
430+
const lineNum = parseInt(num) - 1; // Convert to 0-based
431+
if (lineNum >= 0 && !relatedLines.includes(lineNum)) {
432+
relatedLines.push(lineNum);
433+
}
434+
}
435+
}
450436
}
451437
}
452438
}
453439

440+
// Remove any lines that are likely part of the user's selection
441+
// (we'll filter these out in the decoration manager anyway)
442+
454443
console.log(`Parsed related lines from explanation:`, relatedLines.map(l => l + 1)); // Log as 1-based for readability
455444
return relatedLines.sort((a, b) => a - b);
456445
}

0 commit comments

Comments
 (0)