Skip to content

Commit 7617f59

Browse files
authored
Allow line numbers and ranges in autocomplete (#4238)
1 parent 7aecb43 commit 7617f59

File tree

1 file changed

+57
-11
lines changed

1 file changed

+57
-11
lines changed

packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx

Lines changed: 57 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,38 @@ import { useTerminalDimensions } from "@opentui/solid"
1212
import { Locale } from "@/util/locale"
1313
import type { PromptInfo } from "./history"
1414

15+
function removeLineRange(input: string) {
16+
const hashIndex = input.lastIndexOf("#")
17+
return hashIndex !== -1 ? input.substring(0, hashIndex) : input
18+
}
19+
20+
function extractLineRange(input: string) {
21+
const hashIndex = input.lastIndexOf("#")
22+
if (hashIndex === -1) {
23+
return { baseQuery: input }
24+
}
25+
26+
const baseName = input.substring(0, hashIndex)
27+
const linePart = input.substring(hashIndex + 1)
28+
const lineMatch = linePart.match(/^(\d+)(?:-(\d*))?$/)
29+
30+
if (!lineMatch) {
31+
return { baseQuery: baseName }
32+
}
33+
34+
const startLine = Number(lineMatch[1])
35+
const endLine = lineMatch[2] && startLine < Number(lineMatch[2]) ? Number(lineMatch[2]) : undefined
36+
37+
return {
38+
lineRange: {
39+
baseName,
40+
startLine,
41+
endLine,
42+
},
43+
baseQuery: baseName,
44+
}
45+
}
46+
1547
export type AutocompleteRef = {
1648
onInput: (value: string) => void
1749
onKeyDown: (e: KeyEvent) => void
@@ -142,9 +174,11 @@ export function Autocomplete(props: {
142174
async (query) => {
143175
if (!store.visible || store.visible === "/") return []
144176

177+
const { lineRange, baseQuery } = extractLineRange(query ?? "")
178+
145179
// Get files from SDK
146180
const result = await sdk.client.find.files({
147-
query: query ?? "",
181+
query: baseQuery,
148182
})
149183

150184
const options: AutocompleteOption[] = []
@@ -153,15 +187,27 @@ export function Autocomplete(props: {
153187
if (!result.error && result.data) {
154188
const width = props.anchor().width - 4
155189
options.push(
156-
...result.data.map(
157-
(item): AutocompleteOption => ({
158-
display: Locale.truncateMiddle(item, width),
190+
...result.data.map((item): AutocompleteOption => {
191+
let url = `file://${process.cwd()}/${item}`
192+
let filename = item
193+
if (lineRange && !item.endsWith("/")) {
194+
filename = `${item}#${lineRange.startLine}${lineRange.endLine ? `-${lineRange.endLine}` : ""}`
195+
const urlObj = new URL(url)
196+
urlObj.searchParams.set("start", String(lineRange.startLine))
197+
if (lineRange.endLine !== undefined) {
198+
urlObj.searchParams.set("end", String(lineRange.endLine))
199+
}
200+
url = urlObj.toString()
201+
}
202+
203+
return {
204+
display: Locale.truncateMiddle(filename, width),
159205
onSelect: () => {
160-
insertPart(item, {
206+
insertPart(filename, {
161207
type: "file",
162208
mime: "text/plain",
163-
filename: item,
164-
url: `file://${process.cwd()}/${item}`,
209+
filename,
210+
url,
165211
source: {
166212
type: "file",
167213
text: {
@@ -173,8 +219,8 @@ export function Autocomplete(props: {
173219
},
174220
})
175221
},
176-
}),
177-
),
222+
}
223+
}),
178224
)
179225
}
180226

@@ -383,8 +429,8 @@ export function Autocomplete(props: {
383429
return prev
384430
}
385431

386-
const result = fuzzysort.go(currentFilter, mixed, {
387-
keys: [(obj) => obj.display.trimEnd(), "description", (obj) => obj.aliases?.join(" ") ?? ""],
432+
const result = fuzzysort.go(removeLineRange(currentFilter), mixed, {
433+
keys: [(obj) => removeLineRange(obj.display.trimEnd()), "description", (obj) => obj.aliases?.join(" ") ?? ""],
388434
limit: 10,
389435
scoreFn: (objResults) => {
390436
const displayResult = objResults[0]

0 commit comments

Comments
 (0)