Skip to content

Commit 85b39f9

Browse files
committed
refactor: simplify parseGhostResponse to return FillInAtCursorSuggestion directly
- Remove StreamingParseResult interface and related complexity - parseGhostResponse now returns FillInAtCursorSuggestion with text, prefix, suffix - Returns empty string for text when no completion found (instead of undefined) - Update GhostInlineCompletionProvider to handle new return type - Update all tests to work with simplified API - Maintain backward compatibility by creating GhostSuggestionsState internally when needed This change simplifies the API and reduces unnecessary abstraction layers while maintaining all existing functionality.
1 parent 4deaf0f commit 85b39f9

File tree

7 files changed

+82
-131
lines changed

7 files changed

+82
-131
lines changed

src/services/ghost/__tests__/GhostServiceManager.spec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ describe("GhostServiceManager", () => {
108108
const position = context.range?.start ?? context.document.positionAt(0)
109109
const { prefix, suffix } = extractPrefixSuffix(context.document, position)
110110
const result = parseGhostResponse("", prefix, suffix)
111-
expect(result.suggestions.hasSuggestions()).toBe(false)
111+
expect(result.text).toBe("")
112112
})
113113

114114
it("should handle invalid COMPLETION format", async () => {
@@ -119,7 +119,7 @@ describe("GhostServiceManager", () => {
119119
const position = context.range?.start ?? context.document.positionAt(0)
120120
const { prefix, suffix } = extractPrefixSuffix(context.document, position)
121121
const result = parseGhostResponse(invalidCOMPLETION, prefix, suffix)
122-
expect(result.suggestions.hasSuggestions()).toBe(false)
122+
expect(result.text).toBe("")
123123
})
124124

125125
it("should handle file not found in context", async () => {
@@ -138,7 +138,7 @@ console.log('test');</COMPLETION>`
138138
const position = context.range?.start ?? context.document.positionAt(0)
139139
const { prefix, suffix } = extractPrefixSuffix(context.document, position)
140140
const result = parseGhostResponse(completionResponse, prefix, suffix)
141-
expect(result.suggestions.hasSuggestions()).toBe(true)
141+
expect(result.text).toBe("// Added comment\nconsole.log('test');")
142142
})
143143
})
144144

src/services/ghost/classic-auto-complete/GhostInlineCompletionProvider.ts

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as vscode from "vscode"
2-
import { FillInAtCursorSuggestion, GhostSuggestionsState } from "./GhostSuggestions"
2+
import { FillInAtCursorSuggestion } from "./GhostSuggestions"
33
import { extractPrefixSuffix, GhostSuggestionContext, contextToAutocompleteInput } from "../types"
44
import { parseGhostResponse } from "./GhostStreamingParser"
55
import { AutoTriggerStrategy } from "./AutoTriggerStrategy"
@@ -62,7 +62,7 @@ export function findMatchingSuggestion(
6262
}
6363

6464
export interface LLMRetrievalResult {
65-
suggestions: GhostSuggestionsState
65+
suggestion: FillInAtCursorSuggestion
6666
cost: number
6767
inputTokens: number
6868
outputTokens: number
@@ -134,9 +134,9 @@ export class GhostInlineCompletionProvider implements vscode.InlineCompletionIte
134134
// Check cache before making API call (includes both successful and failed lookups)
135135
const cachedResult = findMatchingSuggestion(prefix, suffix, this.suggestionsHistory)
136136
if (cachedResult !== null) {
137-
// Return empty result if we have a cached entry (either success or failure)
137+
// Return cached result (either success with text or failure with empty string)
138138
return {
139-
suggestions: new GhostSuggestionsState(),
139+
suggestion: { text: cachedResult, prefix, suffix },
140140
cost: 0,
141141
inputTokens: 0,
142142
outputTokens: 0,
@@ -154,7 +154,7 @@ export class GhostInlineCompletionProvider implements vscode.InlineCompletionIte
154154

155155
if (this.isRequestCancelled) {
156156
return {
157-
suggestions: new GhostSuggestionsState(),
157+
suggestion: { text: "", prefix, suffix },
158158
cost: 0,
159159
inputTokens: 0,
160160
outputTokens: 0,
@@ -183,7 +183,7 @@ export class GhostInlineCompletionProvider implements vscode.InlineCompletionIte
183183

184184
if (this.isRequestCancelled) {
185185
return {
186-
suggestions: new GhostSuggestionsState(),
186+
suggestion: { text: "", prefix, suffix },
187187
cost: usageInfo.cost,
188188
inputTokens: usageInfo.inputTokens,
189189
outputTokens: usageInfo.outputTokens,
@@ -193,14 +193,15 @@ export class GhostInlineCompletionProvider implements vscode.InlineCompletionIte
193193
}
194194

195195
// Parse the response using the standalone function
196-
const finalParseResult = parseGhostResponse(response, prefix, suffix)
196+
const fillInAtCursorSuggestion = parseGhostResponse(response, prefix, suffix)
197197

198-
if (finalParseResult.suggestions.getFillInAtCursor()) {
199-
console.info("Final suggestion:", finalParseResult.suggestions.getFillInAtCursor())
198+
if (fillInAtCursorSuggestion.text) {
199+
console.info("Final suggestion:", fillInAtCursorSuggestion)
200200
}
201201

202+
// Always return a FillInAtCursorSuggestion, even if text is empty
202203
return {
203-
suggestions: finalParseResult.suggestions,
204+
suggestion: fillInAtCursorSuggestion,
204205
cost: usageInfo.cost,
205206
inputTokens: usageInfo.inputTokens,
206207
outputTokens: usageInfo.outputTokens,
@@ -281,19 +282,17 @@ export class GhostInlineCompletionProvider implements vscode.InlineCompletionIte
281282
)
282283
}
283284

284-
const fillInAtCursor = result.suggestions.getFillInAtCursor()
285+
// Always update suggestions, even if text is empty (for caching)
286+
this.updateSuggestions(result.suggestion)
285287

286-
if (fillInAtCursor) {
287-
this.updateSuggestions(fillInAtCursor)
288+
if (result.suggestion.text) {
288289
const item: vscode.InlineCompletionItem = {
289-
insertText: fillInAtCursor.text,
290+
insertText: result.suggestion.text,
290291
range: new vscode.Range(position, position),
291292
}
292293
return [item]
293294
} else {
294-
// Store empty suggestion to prevent re-requesting
295-
this.updateSuggestions({ text: "", prefix, suffix })
296-
295+
// Empty text means no suggestion to show
297296
return []
298297
}
299298
} catch (error) {
Lines changed: 7 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,26 @@
1-
import { GhostSuggestionsState } from "./GhostSuggestions"
2-
3-
export interface StreamingParseResult {
4-
suggestions: GhostSuggestionsState
5-
isComplete: boolean
6-
hasNewSuggestions: boolean
7-
}
1+
import { FillInAtCursorSuggestion } from "./GhostSuggestions"
82

93
/**
104
* Parse the response - only handles responses with <COMPLETION> tags
5+
* Returns a FillInAtCursorSuggestion with the extracted text, or an empty string if nothing found
116
*/
12-
export function parseGhostResponse(fullResponse: string, prefix: string, suffix: string): StreamingParseResult {
13-
const suggestions = new GhostSuggestionsState()
14-
let hasNewSuggestions = false
15-
7+
export function parseGhostResponse(fullResponse: string, prefix: string, suffix: string): FillInAtCursorSuggestion {
168
let fimText: string = ""
17-
let isComplete: boolean = true
189

1910
// Match content strictly between <COMPLETION> and </COMPLETION> tags
2011
const completionMatch = fullResponse.match(/<COMPLETION>([\s\S]*?)<\/COMPLETION>/i)
2112

2213
if (completionMatch) {
2314
// Extract the captured group (content between tags)
2415
fimText = completionMatch[1] || ""
25-
isComplete = true
2616
}
2717
// Remove any accidentally captured tag remnants
2818
fimText = fimText.replace(/<\/?COMPLETION>/gi, "")
2919

30-
// Create suggestion if there's actual content
31-
if (fimText.length > 0) {
32-
suggestions.setFillInAtCursor({
33-
text: fimText,
34-
prefix,
35-
suffix,
36-
})
37-
hasNewSuggestions = true
38-
}
39-
20+
// Return FillInAtCursorSuggestion with the text (empty string if nothing found)
4021
return {
41-
suggestions,
42-
isComplete,
43-
hasNewSuggestions,
22+
text: fimText,
23+
prefix,
24+
suffix,
4425
}
4526
}

src/services/ghost/classic-auto-complete/GhostSuggestions.ts

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,3 @@ export interface FillInAtCursorSuggestion {
33
prefix: string
44
suffix: string
55
}
6-
7-
export class GhostSuggestionsState {
8-
private fillinAtCursorSuggestion: FillInAtCursorSuggestion | undefined = undefined
9-
10-
constructor() {}
11-
12-
public setFillInAtCursor(suggestion: FillInAtCursorSuggestion) {
13-
this.fillinAtCursorSuggestion = suggestion
14-
}
15-
16-
public getFillInAtCursor(): FillInAtCursorSuggestion | undefined {
17-
return this.fillinAtCursorSuggestion
18-
}
19-
20-
public hasSuggestions(): boolean {
21-
return !!this.fillinAtCursorSuggestion
22-
}
23-
24-
public clear(): void {
25-
this.fillinAtCursorSuggestion = undefined
26-
}
27-
}

src/services/ghost/classic-auto-complete/__tests__/GhostInlineCompletionProvider.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {
44
findMatchingSuggestion,
55
CostTrackingCallback,
66
} from "../GhostInlineCompletionProvider"
7-
import { GhostSuggestionsState, FillInAtCursorSuggestion } from "../GhostSuggestions"
7+
import { FillInAtCursorSuggestion } from "../GhostSuggestions"
88
import { MockTextDocument } from "../../../mocking/MockTextDocument"
99
import { GhostModel } from "../../GhostModel"
1010
import { GhostContext } from "../../GhostContext"

0 commit comments

Comments
 (0)