From 975a723ffb2ae8ce1a939e120ba4d9d98cf95b0f Mon Sep 17 00:00:00 2001 From: ewired <37567272+ewired@users.noreply.github.com> Date: Thu, 6 Nov 2025 21:33:30 -0500 Subject: [PATCH 1/3] allow line numbers in autocomplete --- .../cmd/tui/component/prompt/autocomplete.tsx | 72 ++++++++++++++++--- 1 file changed, 61 insertions(+), 11 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx index cef083ad734..7f2e0d4353e 100644 --- a/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx @@ -12,6 +12,42 @@ import { useTerminalDimensions } from "@opentui/solid" import { Locale } from "@/util/locale" import type { PromptInfo } from "./history" +function removeLineRange(input: string) { + const colonIndex = input.lastIndexOf(":") + return colonIndex !== -1 ? input.substring(0, colonIndex) : input +} + +function extractLineRange(input: string) { + const colonIndex = input.lastIndexOf(":") + if (colonIndex === -1) { + return { baseQuery: input } + } + + const baseName = input.substring(0, colonIndex) + const linePart = input.substring(colonIndex + 1) + const lineMatch = linePart.match(/^(\d+)(?:-(\d+))?$/) + + if (!lineMatch) { + return { baseQuery: baseName } + } + + const startLine = Number(lineMatch[1]) + const endLine = lineMatch[2] ? Number(lineMatch[2]) : undefined + + if (endLine !== undefined && startLine > endLine) { + return { baseQuery: baseName } + } + + return { + lineRange: { + baseName, + startLine, + endLine, + }, + baseQuery: baseName, + } +} + export type AutocompleteRef = { onInput: (value: string) => void onKeyDown: (e: KeyEvent) => void @@ -142,9 +178,11 @@ export function Autocomplete(props: { async (query) => { if (!store.visible || store.visible === "/") return [] + const { lineRange, baseQuery } = extractLineRange(query ?? "") + // Get files from SDK const result = await sdk.client.find.files({ - query: query ?? "", + query: baseQuery, }) const options: AutocompleteOption[] = [] @@ -153,15 +191,27 @@ export function Autocomplete(props: { if (!result.error && result.data) { const width = props.anchor().width - 4 options.push( - ...result.data.map( - (item): AutocompleteOption => ({ - display: Locale.truncateMiddle(item, width), + ...result.data.map((item): AutocompleteOption => { + let url = `file://${process.cwd()}/${item}` + let filename = item + if (lineRange && !item.endsWith("/")) { + filename = `${item}:${lineRange.startLine}${lineRange.endLine ? `-${lineRange.endLine}` : ""}` + const urlObj = new URL(url) + urlObj.searchParams.set("start", String(lineRange.startLine)) + if (lineRange.endLine !== undefined) { + urlObj.searchParams.set("end", String(lineRange.endLine)) + } + url = urlObj.toString() + } + + return { + display: Locale.truncateMiddle(filename, width), onSelect: () => { - insertPart(item, { + insertPart(filename, { type: "file", mime: "text/plain", - filename: item, - url: `file://${process.cwd()}/${item}`, + filename, + url, source: { type: "file", text: { @@ -173,8 +223,8 @@ export function Autocomplete(props: { }, }) }, - }), - ), + } + }), ) } @@ -383,8 +433,8 @@ export function Autocomplete(props: { return prev } - const result = fuzzysort.go(currentFilter, mixed, { - keys: [(obj) => obj.display.trimEnd(), "description", (obj) => obj.aliases?.join(" ") ?? ""], + const result = fuzzysort.go(removeLineRange(currentFilter), mixed, { + keys: [(obj) => removeLineRange(obj.display.trimEnd()), "description", (obj) => obj.aliases?.join(" ") ?? ""], limit: 10, scoreFn: (objResults) => { const displayResult = objResults[0] From a25e93c4fb772292faa0c23d8b85d04386c23b8d Mon Sep 17 00:00:00 2001 From: ewired <37567272+ewired@users.noreply.github.com> Date: Tue, 23 Dec 2025 14:58:26 -0500 Subject: [PATCH 2/3] use hash instead of colon for line number syntax in tui --- .../cli/cmd/tui/component/prompt/autocomplete.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx index 7f2e0d4353e..6533e1ceb0a 100644 --- a/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx @@ -13,18 +13,18 @@ import { Locale } from "@/util/locale" import type { PromptInfo } from "./history" function removeLineRange(input: string) { - const colonIndex = input.lastIndexOf(":") - return colonIndex !== -1 ? input.substring(0, colonIndex) : input + const hashIndex = input.lastIndexOf("#") + return hashIndex !== -1 ? input.substring(0, hashIndex) : input } function extractLineRange(input: string) { - const colonIndex = input.lastIndexOf(":") - if (colonIndex === -1) { + const hashIndex = input.lastIndexOf("#") + if (hashIndex === -1) { return { baseQuery: input } } - const baseName = input.substring(0, colonIndex) - const linePart = input.substring(colonIndex + 1) + const baseName = input.substring(0, hashIndex) + const linePart = input.substring(hashIndex + 1) const lineMatch = linePart.match(/^(\d+)(?:-(\d+))?$/) if (!lineMatch) { @@ -195,7 +195,7 @@ export function Autocomplete(props: { let url = `file://${process.cwd()}/${item}` let filename = item if (lineRange && !item.endsWith("/")) { - filename = `${item}:${lineRange.startLine}${lineRange.endLine ? `-${lineRange.endLine}` : ""}` + filename = `${item}#${lineRange.startLine}${lineRange.endLine ? `-${lineRange.endLine}` : ""}` const urlObj = new URL(url) urlObj.searchParams.set("start", String(lineRange.startLine)) if (lineRange.endLine !== undefined) { From c731378b88e52e3139a690cdb50c7ac005d7a4cc Mon Sep 17 00:00:00 2001 From: ewired <37567272+ewired@users.noreply.github.com> Date: Sat, 27 Dec 2025 00:18:56 -0500 Subject: [PATCH 3/3] respect the start value of invalid line ranges --- .../src/cli/cmd/tui/component/prompt/autocomplete.tsx | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx index 6533e1ceb0a..a5823289505 100644 --- a/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx @@ -25,18 +25,14 @@ function extractLineRange(input: string) { const baseName = input.substring(0, hashIndex) const linePart = input.substring(hashIndex + 1) - const lineMatch = linePart.match(/^(\d+)(?:-(\d+))?$/) + const lineMatch = linePart.match(/^(\d+)(?:-(\d*))?$/) if (!lineMatch) { return { baseQuery: baseName } } const startLine = Number(lineMatch[1]) - const endLine = lineMatch[2] ? Number(lineMatch[2]) : undefined - - if (endLine !== undefined && startLine > endLine) { - return { baseQuery: baseName } - } + const endLine = lineMatch[2] && startLine < Number(lineMatch[2]) ? Number(lineMatch[2]) : undefined return { lineRange: {