From b88d7990ea80ad666c01ead88f2bc28a623bfb52 Mon Sep 17 00:00:00 2001 From: Himanshu Sardana Date: Thu, 21 May 2026 08:57:28 +0530 Subject: [PATCH 1/5] chore: move get_workspace_symbols to context-tools.lua fix: formatting fix: replace buf_request_sync with buf_request --- lua/jumpy/context-tools.lua | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 lua/jumpy/context-tools.lua diff --git a/lua/jumpy/context-tools.lua b/lua/jumpy/context-tools.lua new file mode 100644 index 0000000..3be803c --- /dev/null +++ b/lua/jumpy/context-tools.lua @@ -0,0 +1,22 @@ +local M = {} + +function M.get_workspace_symbols(bufnr, callback) + local MAX_SYMBOLS = 200 + vim.lsp.buf_request(bufnr, "workspace/symbol", { query = "" }, function(_, result) + local out = {} + local kinds = vim.lsp.protocol.SymbolKind + + for i, s in ipairs(result or {}) do + if i > MAX_SYMBOLS then + break + end + local path = vim.uri_to_fname(s.location.uri) + path = vim.fn.fnamemodify(path, ":.") + out[#out + 1] = string.format("- %s [%s] > %s (%s)", s.name, kinds[s.kind] or "?", s.containerName, path) + end + + callback(table.concat(out, "\n")) + end) +end + +return M From 0451354b033fcedcf07c63273e36950d458588e1 Mon Sep 17 00:00:00 2001 From: Himanshu Sardana Date: Thu, 21 May 2026 09:21:56 +0530 Subject: [PATCH 2/5] fix: make buf_request async --- lua/jumpy/prompt.lua | 159 +++++++++++++++++++++++++++++-------------- 1 file changed, 107 insertions(+), 52 deletions(-) diff --git a/lua/jumpy/prompt.lua b/lua/jumpy/prompt.lua index d16e770..35b0475 100644 --- a/lua/jumpy/prompt.lua +++ b/lua/jumpy/prompt.lua @@ -1,5 +1,7 @@ local M = {} +local context_tools = require("jumpy.context-tools") + local state = { win = nil, buf = nil, @@ -50,6 +52,34 @@ function M.open() state.buf, state.win = create_float(" jumpy ") M._set_submit_keymap() + M._setup_completions(state.buf) +end + +function M._setup_completions(buf) + vim.bo[buf].completeopt = "menu,menuone,noselect" -- QoL option so vim opens a completion dropdown instead of directly appending the first option + + local completionItems = { + { word = "@lsp", menu = "[jumpy]" }, + } + + vim.api.nvim_create_autocmd("TextChangedI", { + buffer = buf, + callback = function() + local line = vim.api.nvim_get_current_line() + local col = vim.fn.col(".") + local before = line:sub(1, col - 1) + + local match = before:match("@%w*$") + + if vim.fn.mode() == "i" then + if match then + vim.schedule(function() + vim.fn.complete(col - #match, completionItems) + end) + end + end + end, + }) end function M.reprompt() @@ -90,8 +120,6 @@ function M._submit() return end - M._close() - local source_buf = state.source_buf local source_lines = vim.api.nvim_buf_get_lines(source_buf, 0, -1, false) local filetype = vim.bo[source_buf].filetype @@ -99,65 +127,92 @@ function M._submit() local llm = require("jumpy.llm") - if reprompt_idx then - local render = require("jumpy.render") - local hunk_state = render.get_state(source_buf) - if not hunk_state or not hunk_state.hunks[reprompt_idx] then - vim.notify("jumpy: hunk no longer exists", vim.log.levels.WARN) - return + local function send_request(symbols) + symbols = symbols or "" + + if symbols ~= "" then + symbols = "\n\n--- WORKSPACE SYMBOLS ---\n" .. symbols .. "\n--- END SYMBOLS ---" end - local hunk = hunk_state.hunks[reprompt_idx] - local context = { - original_lines = hunk.removed_lines, - proposed_lines = hunk.added_lines, - prompt = prompt_text, - filetype = filetype, - } - - llm.reprompt(context, function(new_lines) - vim.schedule(function() - local nav = require("jumpy.navigate") - nav.replace_hunk(source_buf, reprompt_idx, new_lines) - vim.notify("jumpy: hunk updated", vim.log.levels.INFO) + M._close() + + if reprompt_idx then + local render = require("jumpy.render") + local hunk_state = render.get_state(source_buf) + + if not hunk_state or not hunk_state.hunks[reprompt_idx] then + vim.notify("jumpy: hunk no longer exists", vim.log.levels.WARN) + return + end + + local hunk = hunk_state.hunks[reprompt_idx] + + local context = { + original_lines = hunk.removed_lines, + proposed_lines = hunk.added_lines, + prompt = prompt_text, + symbols = symbols, + filetype = filetype, + } + + llm.reprompt(context, function(new_lines) + vim.schedule(function() + local nav = require("jumpy.navigate") + + nav.replace_hunk(source_buf, reprompt_idx, new_lines) + + vim.notify("jumpy: hunk updated", vim.log.levels.INFO) + end) end) - end) - else - local context = { - file_contents = table.concat(source_lines, "\n"), - prompt = prompt_text, - filetype = filetype, - } - - llm.request(context, function(response_text) - vim.schedule(function() - if not vim.api.nvim_buf_is_valid(source_buf) then - vim.notify("jumpy: buffer closed, discarding response", vim.log.levels.WARN) - return - end + else + local context = { + file_contents = table.concat(source_lines, "\n"), + prompt = prompt_text, + symbols = symbols, + filetype = filetype, + } - local diff = require("jumpy.diff") - local render = require("jumpy.render") - local patch = require("jumpy.patch") + llm.request(context, function(response_text) + vim.schedule(function() + if not vim.api.nvim_buf_is_valid(source_buf) then + vim.notify("jumpy: buffer closed, discarding response", vim.log.levels.WARN) + return + end - local proposed_lines, unmatched = patch.apply(source_lines, response_text) - if unmatched > 0 then - vim.notify(string.format("jumpy: %d block(s) could not be matched", unmatched), vim.log.levels.WARN) - end - local hunks = diff.compute(source_lines, proposed_lines) + local diff = require("jumpy.diff") + local render = require("jumpy.render") + local patch = require("jumpy.patch") - if #hunks == 0 then - vim.notify("jumpy: no changes proposed", vim.log.levels.INFO) - return - end + local proposed_lines, unmatched = patch.apply(source_lines, response_text) + + if unmatched > 0 then + vim.notify(string.format("jumpy: %d block(s) could not be matched", unmatched), vim.log.levels.WARN) + end - render.show(source_buf, hunks, source_lines, proposed_lines) - vim.notify(string.format("jumpy: %d hunk(s) proposed", #hunks), vim.log.levels.INFO) + local hunks = diff.compute(source_lines, proposed_lines) - local nav = require("jumpy.navigate") - nav.next_hunk() + if #hunks == 0 then + vim.notify("jumpy: no changes proposed", vim.log.levels.INFO) + return + end + + render.show(source_buf, hunks, source_lines, proposed_lines) + + vim.notify(string.format("jumpy: %d hunk(s) proposed", #hunks), vim.log.levels.INFO) + + local nav = require("jumpy.navigate") + nav.next_hunk() + end) end) - end) + end + end + + if prompt_text:find("@lsp") then + prompt_text = vim.trim(prompt_text:gsub("%f[%w@]@lsp%f[%W]", "")) + + context_tools.get_workspace_symbols(tonumber(source_buf) or 0, send_request) + else + send_request("") end end From bd3eff25df3e586a38284a7592a09aba06a831aa Mon Sep 17 00:00:00 2001 From: Himanshu Sardana Date: Thu, 21 May 2026 09:22:21 +0530 Subject: [PATCH 3/5] chore: pass workspace symbols via context --- lua/jumpy/llm.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lua/jumpy/llm.lua b/lua/jumpy/llm.lua index cee6328..3b0c186 100644 --- a/lua/jumpy/llm.lua +++ b/lua/jumpy/llm.lua @@ -6,10 +6,12 @@ end local function build_messages(context) local config = get_config() + local user_content = string.format( - "File type: %s\n\n--- FILE CONTENTS ---\n%s\n--- END FILE ---\n\nInstruction: %s", + "File type: %s\n\n--- FILE CONTENTS ---\n%s\n--- END FILE ---%s\n\nInstruction: %s", context.filetype or "text", context.file_contents, + context.symbols, context.prompt ) From 54bb3710148ad983da51e9644a40203b05d2134e Mon Sep 17 00:00:00 2001 From: Himanshu Sardana Date: Thu, 21 May 2026 09:26:25 +0530 Subject: [PATCH 4/5] fix: notify user if LSP doesnt support workspace_symbols --- lua/jumpy/context-tools.lua | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/lua/jumpy/context-tools.lua b/lua/jumpy/context-tools.lua index 3be803c..85a0fb5 100644 --- a/lua/jumpy/context-tools.lua +++ b/lua/jumpy/context-tools.lua @@ -2,7 +2,27 @@ local M = {} function M.get_workspace_symbols(bufnr, callback) local MAX_SYMBOLS = 200 - vim.lsp.buf_request(bufnr, "workspace/symbol", { query = "" }, function(_, result) + + local clients = vim.lsp.get_clients({ bufnr = bufnr }) + local supports_workspace_symbols = vim.tbl_any(function(client) + return client:supports_method("workspace/symbol") + end, clients) + + if not supports_workspace_symbols then + vim.notify("No attached LSP supports workspace/symbol", vim.log.levels.WARN) + + callback("") + return + end + + vim.lsp.buf_request(bufnr, "workspace/symbol", { query = "" }, function(err, result) + if err then + vim.notify("workspace/symbol request failed: " .. err.message, vim.log.levels.WARN) + + callback("") + return + end + local out = {} local kinds = vim.lsp.protocol.SymbolKind @@ -10,9 +30,12 @@ function M.get_workspace_symbols(bufnr, callback) if i > MAX_SYMBOLS then break end + local path = vim.uri_to_fname(s.location.uri) path = vim.fn.fnamemodify(path, ":.") - out[#out + 1] = string.format("- %s [%s] > %s (%s)", s.name, kinds[s.kind] or "?", s.containerName, path) + + out[#out + 1] = + string.format("- %s [%s] > %s (%s)", s.name, kinds[s.kind] or "?", s.containerName or "global", path) end callback(table.concat(out, "\n")) From 232cb8b5d0548dd493b1df3285375511e60329ec Mon Sep 17 00:00:00 2001 From: Himanshu Sardana Date: Thu, 21 May 2026 09:29:03 +0530 Subject: [PATCH 5/5] fix: enable @lsp completion in reprompts --- lua/jumpy/prompt.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/lua/jumpy/prompt.lua b/lua/jumpy/prompt.lua index 35b0475..0168291 100644 --- a/lua/jumpy/prompt.lua +++ b/lua/jumpy/prompt.lua @@ -95,6 +95,7 @@ function M.reprompt() state.buf, state.win = create_float(" jumpy: reprompt this hunk ") M._set_submit_keymap() + M._setup_completions(state.buf) end function M._set_submit_keymap()