Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: CI

on:
push:
branches: [master]
pull_request:
branches: [master]

jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install luacheck
run: sudo apt-get update && sudo apt-get install -y lua-check

- name: Run luacheck
run: luacheck lua/ tests/

format:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Check formatting with stylua
uses: JohnnyMorganz/stylua-action@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
version: latest
args: --check lua/ tests/

test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Setup Lua
uses: leafo/gh-actions-lua@v10
with:
luaVersion: "5.1"

- name: Setup LuaRocks
uses: leafo/gh-actions-luarocks@v4

- name: Cache LuaRocks packages
uses: actions/cache@v4
with:
path: ~/.luarocks
key: ${{ runner.os }}-luarocks-${{ hashFiles('Makefile') }}
restore-keys: |
${{ runner.os }}-luarocks-

- name: Install busted
run: luarocks install busted

- name: Run tests
run: busted tests/
7 changes: 7 additions & 0 deletions .luacheckrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
std = "luajit"
globals = { "vim" }
max_line_length = 120

ignore = {
"212", -- unused argument
}
12 changes: 12 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.PHONY: test lint format check

test:
busted tests/

lint:
luacheck lua/ tests/

format:
stylua lua/ tests/

check: lint test
10 changes: 7 additions & 3 deletions lua/jumpy/diff.lua
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,13 @@ local function myers_diff(old_lines, new_lines)
local v = {}
v[1] = 0
local trace = {}
local done = false

for d = 0, max do
if done then
break
end

local snapshot = {}
for k, val in pairs(v) do
snapshot[k] = val
Expand All @@ -40,13 +45,12 @@ local function myers_diff(old_lines, new_lines)
v[k] = x

if x >= n and y >= m then
goto backtrack
done = true
break
end
end
end

::backtrack::

local edits = {}
local x, y = n, m

Expand Down
35 changes: 26 additions & 9 deletions lua/jumpy/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ M.config = {
endpoint = nil,
model = nil,
api_key = nil,
system_prompt = [[You are a code editor. The user will give you a file and an instruction. Return ONLY the complete modified file contents. Do not wrap in markdown code fences. Do not explain.]],
system_prompt = [[You are a code editor. The user will give you a file and an instruction. ]]
.. [[Return ONLY the complete modified file contents. Do not wrap in markdown code fences. Do not explain.]],
keymaps = {
prompt = "<leader>j",
next_hunk = "]h",
Expand Down Expand Up @@ -80,14 +81,30 @@ function M._setup_keymaps()
local opts = { silent = true }
local c = M.config.keymaps

map("n", c.prompt, function() require("jumpy.prompt").open() end, opts)
map("n", c.next_hunk, function() require("jumpy.navigate").next_hunk() end, opts)
map("n", c.prev_hunk, function() require("jumpy.navigate").prev_hunk() end, opts)
map("n", c.accept, function() require("jumpy.navigate").accept() end, opts)
map("n", c.reject, function() require("jumpy.navigate").reject() end, opts)
map("n", c.accept_all, function() require("jumpy.navigate").accept_all() end, opts)
map("n", c.reject_all, function() require("jumpy.navigate").reject_all() end, opts)
map("n", c.reprompt, function() require("jumpy.prompt").reprompt() end, opts)
map("n", c.prompt, function()
require("jumpy.prompt").open()
end, opts)
map("n", c.next_hunk, function()
require("jumpy.navigate").next_hunk()
end, opts)
map("n", c.prev_hunk, function()
require("jumpy.navigate").prev_hunk()
end, opts)
map("n", c.accept, function()
require("jumpy.navigate").accept()
end, opts)
map("n", c.reject, function()
require("jumpy.navigate").reject()
end, opts)
map("n", c.accept_all, function()
require("jumpy.navigate").accept_all()
end, opts)
map("n", c.reject_all, function()
require("jumpy.navigate").reject_all()
end, opts)
map("n", c.reprompt, function()
require("jumpy.prompt").reprompt()
end, opts)
end

return M
38 changes: 24 additions & 14 deletions lua/jumpy/llm.lua
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,12 @@ end

local function build_reprompt_messages(context)
local config = get_config()
local template = "File type: %s\n\n"
.. "--- ORIGINAL LINES ---\n%s\n--- END ORIGINAL ---\n\n"
.. "--- PREVIOUSLY PROPOSED (rejected) ---\n%s\n--- END PROPOSED ---\n\n"
.. "New instruction: %s\n\nReturn ONLY the replacement lines. No explanation, no fences."
local user_content = string.format(
"File type: %s\n\n--- ORIGINAL LINES ---\n%s\n--- END ORIGINAL ---\n\n--- PREVIOUSLY PROPOSED (rejected) ---\n%s\n--- END PROPOSED ---\n\nNew instruction: %s\n\nReturn ONLY the replacement lines. No explanation, no fences.",
template,
context.filetype or "text",
table.concat(context.original_lines, "\n"),
table.concat(context.proposed_lines, "\n"),
Expand All @@ -42,30 +46,36 @@ end

local function build_curl_cmd_openai(body_json, config)
return {
"curl", "-s",
"-H", "Content-Type: application/json",
"-H", string.format("Authorization: Bearer %s", config.api_key),
"-d", body_json,
"curl",
"-s",
"-H",
"Content-Type: application/json",
"-H",
string.format("Authorization: Bearer %s", config.api_key),
"-d",
body_json,
config.endpoint,
}
end

local function build_curl_cmd_anthropic(body_json, config)
return {
"curl", "-s",
"-H", "Content-Type: application/json",
"-H", string.format("x-api-key: %s", config.api_key),
"-H", "anthropic-version: 2023-06-01",
"-d", body_json,
"curl",
"-s",
"-H",
"Content-Type: application/json",
"-H",
string.format("x-api-key: %s", config.api_key),
"-H",
"anthropic-version: 2023-06-01",
"-d",
body_json,
config.endpoint,
}
end

local function extract_content_openai(parsed)
return parsed.choices
and parsed.choices[1]
and parsed.choices[1].message
and parsed.choices[1].message.content
return parsed.choices and parsed.choices[1] and parsed.choices[1].message and parsed.choices[1].message.content
end

local function extract_content_anthropic(parsed)
Expand Down
32 changes: 20 additions & 12 deletions lua/jumpy/loading.lua
Original file line number Diff line number Diff line change
Expand Up @@ -86,14 +86,18 @@ function M.start()
open_float(text)

timer = vim.loop.new_timer()
timer:start(80, 80, vim.schedule_wrap(function()
if not active then
return
end
frame_idx = frame_idx % #FRAMES + 1
local line = string.format(" %s waiting for model…%s ", FRAMES[frame_idx], format_elapsed())
update_text(line)
end))
timer:start(
80,
80,
vim.schedule_wrap(function()
if not active then
return
end
frame_idx = frame_idx % #FRAMES + 1
local line = string.format(" %s waiting for model…%s ", FRAMES[frame_idx], format_elapsed())
update_text(line)
end)
)
end

function M.stop()
Expand Down Expand Up @@ -130,10 +134,14 @@ function M.error(msg)
open_float(text)

dismiss_timer = vim.loop.new_timer()
dismiss_timer:start(4000, 0, vim.schedule_wrap(function()
cancel_dismiss()
close_ui()
end))
dismiss_timer:start(
4000,
0,
vim.schedule_wrap(function()
cancel_dismiss()
close_ui()
end)
)
end

return M
8 changes: 6 additions & 2 deletions lua/jumpy/navigate.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ local render = require("jumpy.render")

local function get_active_hunks(bufnr)
local state = render.get_state(bufnr)
if not state then return {} end
if not state then
return {}
end

local active = {}
for idx, hunk in pairs(state.hunks) do
Expand Down Expand Up @@ -167,7 +169,9 @@ function M._apply_offset(bufnr, accepted_idx, delta)
end

local state = render.get_state(bufnr)
if not state then return end
if not state then
return
end

for idx, hunk in pairs(state.hunks) do
if hunk and idx > accepted_idx then
Expand Down
26 changes: 14 additions & 12 deletions lua/jumpy/render.lua
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,7 @@ function M.show(bufnr, hunks, original_lines, proposed_lines)
table.insert(virt_lines, { { added_line, "JumpyAdded" } })
end

local anchor_line = math.min(
hunk.old_start - 1 + hunk.old_count - 1,
vim.api.nvim_buf_line_count(bufnr) - 1
)
local anchor_line = math.min(hunk.old_start - 1 + hunk.old_count - 1, vim.api.nvim_buf_line_count(bufnr) - 1)
anchor_line = math.max(0, anchor_line)

local id = vim.api.nvim_buf_set_extmark(bufnr, ns, anchor_line, 0, {
Expand Down Expand Up @@ -103,10 +100,14 @@ end

function M.clear_hunk(bufnr, hunk_idx)
local state = buf_states[bufnr]
if not state then return end
if not state then
return
end

local hunk = state.hunks[hunk_idx]
if not hunk then return end
if not hunk then
return
end

for _, eid in ipairs(hunk.extmarks) do
vim.api.nvim_buf_del_extmark(bufnr, ns, eid)
Expand All @@ -130,10 +131,14 @@ end

function M.update_hunk_lines(bufnr, hunk_idx, new_added_lines)
local state = buf_states[bufnr]
if not state then return end
if not state then
return
end

local hunk = state.hunks[hunk_idx]
if not hunk then return end
if not hunk then
return
end

for _, eid in ipairs(hunk.extmarks) do
vim.api.nvim_buf_del_extmark(bufnr, ns, eid)
Expand Down Expand Up @@ -162,10 +167,7 @@ function M.update_hunk_lines(bufnr, hunk_idx, new_added_lines)

local anchor_line
if hunk.old_count > 0 then
anchor_line = math.min(
hunk.old_start - 1 + hunk.old_count - 1,
vim.api.nvim_buf_line_count(bufnr) - 1
)
anchor_line = math.min(hunk.old_start - 1 + hunk.old_count - 1, vim.api.nvim_buf_line_count(bufnr) - 1)
anchor_line = math.max(0, anchor_line)
else
anchor_line = math.max(0, hunk.old_start - 1)
Expand Down
6 changes: 6 additions & 0 deletions stylua.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
column_width = 120
line_endings = "Unix"
indent_type = "Spaces"
indent_width = 2
quote_style = "AutoPreferDouble"
call_parentheses = "Always"
Loading
Loading