Skip to content

Commit 248208f

Browse files
committed
feat(context): return inline text for buffers not backed by a file
because then OpenCode has no file to read. primarily benefits terminal buffers. also improves the rare case when a buffer's backing file has been deleted. closes #247
1 parent db9b870 commit 248208f

1 file changed

Lines changed: 63 additions & 31 deletions

File tree

lua/opencode/context.lua

Lines changed: 63 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
---@module 'snacks.picker'
22

3+
local NS_ID = vim.api.nvim_create_namespace("OpencodeContext")
4+
35
---The context a prompt is being made in.
46
---Particularly useful when inputting or selecting a prompt
57
---because that changes the active mode, window, etc.
@@ -12,8 +14,6 @@
1214
local Context = {}
1315
Context.__index = Context
1416

15-
local ns_id = vim.api.nvim_create_namespace("OpencodeContext")
16-
1717
---@class opencode.context.Range
1818
---@field from integer[] { line, col } (1,0-based)
1919
---@field to integer[] { line, col } (1,0-based)
@@ -64,7 +64,7 @@ local function highlight(buf, range)
6464
local line = vim.api.nvim_buf_get_lines(buf, row, row + 1, false)[1] or ""
6565
local clamp_col = math.min(end_col + 1, #line)
6666
if clamp_col > start_col then
67-
vim.api.nvim_buf_set_extmark(buf, ns_id, row, start_col, {
67+
vim.api.nvim_buf_set_extmark(buf, NS_ID, row, start_col, {
6868
end_col = clamp_col,
6969
hl_group = "Visual",
7070
})
@@ -77,7 +77,7 @@ local function highlight(buf, range)
7777
local line = vim.api.nvim_buf_get_lines(buf, end_row, end_row + 1, false)[1] or ""
7878
end_col = math.min(range.to[2] + 1, #line)
7979
end
80-
vim.api.nvim_buf_set_extmark(buf, ns_id, from_row, from_col, {
80+
vim.api.nvim_buf_set_extmark(buf, NS_ID, from_row, from_col, {
8181
end_row = end_row,
8282
end_col = end_col,
8383
hl_group = "Visual",
@@ -108,7 +108,7 @@ function Context.new(range)
108108
end
109109

110110
function Context:clear()
111-
vim.api.nvim_buf_clear_namespace(self.buf, ns_id, 0, -1)
111+
vim.api.nvim_buf_clear_namespace(self.buf, NS_ID, 0, -1)
112112
end
113113

114114
function Context:resume()
@@ -257,45 +257,77 @@ function Context.input_highlight(rendered)
257257
end, Context.extmarks(rendered))
258258
end
259259

260+
---Get the literal text of a buffer range, trimmed to columns.
261+
---@param bufnr integer
262+
---@param start_line integer 1-indexed
263+
---@param start_col? integer 1-indexed
264+
---@param end_line integer 1-indexed
265+
---@param end_col? integer 1-indexed
266+
---@return string
267+
local function get_buffer_range_text(bufnr, start_line, start_col, end_line, end_col)
268+
end_line = end_line or start_line
269+
local lines = vim.api.nvim_buf_get_lines(bufnr, start_line - 1, end_line, false)
270+
if #lines == 0 then
271+
return ""
272+
end
273+
if start_col then
274+
lines[1] = lines[1]:sub(start_col)
275+
end
276+
if end_col then
277+
lines[#lines] = lines[#lines]:sub(1, end_col)
278+
end
279+
return table.concat(lines, "\n")
280+
end
281+
260282
---Format a location for `opencode`.
283+
---e.g. `opencode.lua:L21:C10-L65:C11` when backed by a file.
284+
---When not backed by a file it returns the literal text of the range (if present) or the entire buffer.
261285
---@param loc string|integer A buffer number or filepath.
262-
---@param args? { start_line?: integer, start_col?: integer, end_line?: integer, end_col?: integer } Location is a buffer number or filepath. Lines and cols are 1-based
263-
---@return string? location e.g. `opencode.lua:L21:C10-L65:C11`
286+
---@param args? { start_line?: integer, start_col?: integer, end_line?: integer, end_col?: integer } 1-based
287+
---@return string?
264288
function Context.format(loc, args)
265-
assert(type(loc) ~= "string" or #loc > 0, "Filepath cannot be an empty string")
266-
267289
local filepath = (type(loc) == "string" and loc) or (type(loc) == "number" and vim.api.nvim_buf_get_name(loc)) or nil
268290
if not filepath or filepath == "" then
269291
return nil
270292
end
271293

272-
local result = ""
294+
local start_line = args and args.start_line
295+
local start_col = args and args.start_col
296+
local end_line = args and args.end_line
297+
local end_col = args and args.end_col
273298

274-
local absolute_path = vim.fn.fnamemodify(filepath, ":p")
275-
result = result .. absolute_path
299+
-- Normalize start/end (for reversed visual selections)
300+
if start_line and end_line and start_line > end_line then
301+
start_line, end_line = end_line, start_line
302+
if start_col and end_col then
303+
start_col, end_col = end_col, start_col
304+
end
305+
end
276306

277-
if args and args.start_line then
278-
if args.end_line and args.start_line > args.end_line then
279-
-- Ensure start is before end (for reversed visual selections)
280-
args.start_line, args.end_line = args.end_line, args.start_line
281-
if args.start_col and args.end_col then
282-
-- Can happen in visual block mode
283-
args.start_col, args.end_col = args.end_col, args.start_col
284-
end
307+
-- For buffers not backed by a real file, return inline text
308+
if type(loc) == "number" then
309+
local filestat = vim.uv.fs_stat(filepath)
310+
if not filestat or filestat.type ~= "file" then
311+
return get_buffer_range_text(
312+
loc,
313+
start_line or 1,
314+
start_col,
315+
end_line or vim.api.nvim_buf_line_count(loc),
316+
end_col
317+
)
285318
end
319+
end
286320

287-
-- Previously we'd need a space here so `opencode` would recognize the file reference and insert the context,
288-
-- but that has regressed with the v1.0.0 `opentui` update :/
289-
-- There's a GH issue somewhere.
290-
result = result .. ":"
291-
result = result .. string.format("L%d", args.start_line)
292-
if args.start_col then
293-
result = result .. string.format(":C%d", args.start_col)
321+
local result = vim.fn.fnamemodify(filepath, ":p")
322+
if start_line then
323+
result = result .. ":" .. string.format("L%d", start_line)
324+
if start_col then
325+
result = result .. string.format(":C%d", start_col)
294326
end
295-
if args.end_line then
296-
result = result .. string.format("-L%d", args.end_line)
297-
if args.end_col then
298-
result = result .. string.format(":C%d", args.end_col)
327+
if end_line then
328+
result = result .. string.format("-L%d", end_line)
329+
if end_col then
330+
result = result .. string.format(":C%d", end_col)
299331
end
300332
end
301333
end

0 commit comments

Comments
 (0)