-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathinit.lua
More file actions
174 lines (163 loc) · 5.66 KB
/
Copy pathinit.lua
File metadata and controls
174 lines (163 loc) · 5.66 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
local M = {}
M.config = {
provider = "openrouter",
endpoint = nil,
model = nil,
api_key = nil,
claude_code_command = "claude",
system_prompt = table.concat({
"You are a code editor. The user will give you a file and an instruction.",
"Return ONLY the changed sections as SEARCH/REPLACE blocks.",
"",
"Format for each change:",
"<<<< SEARCH",
"exact existing lines from the file",
"====",
"replacement lines",
">>>> REPLACE",
"",
"Rules:",
"- SEARCH content must match the file EXACTLY (whitespace, indentation, etc.)",
"- Include 1-2 lines of surrounding context so the match is unique",
"- For deletions, leave the section between ==== and >>>> REPLACE empty",
"- Output NOTHING outside of SEARCH/REPLACE blocks",
"- Do NOT wrap in markdown code fences",
"- Do NOT explain",
}, "\n"),
system_prompt_multi_file = table.concat({
"When multiple files are provided, prefix SEARCH with the exact path from ",
"each --- FILE: ... --- header:",
"<<<< SEARCH src/foo.lua",
"exact existing lines from that file",
"====",
"replacement lines",
">>>> REPLACE",
"",
"The path after SEARCH must be copied VERBATIM from the --- FILE: ... --- header. Do NOT add any prefix.",
"You may edit any subset of the provided files. Every SEARCH block MUST include a path.",
}, "\n"),
keymaps = {
prompt = "<leader>j",
next_hunk = "]h",
prev_hunk = "[h",
accept = "<leader>a",
reject = "<leader>x",
accept_all = "<leader>A",
reject_all = "<leader>X",
reprompt = "<leader>r",
quickfix = "<leader>q",
sidebar = "<leader>s",
},
highlights = {
added = "JumpyAdded",
removed = "JumpyRemoved",
},
sidebar = {
position = "left", -- "left" or "right"
width = 42,
},
}
local provider_defaults = {
openrouter = {
endpoint = "https://openrouter.ai/api/v1/chat/completions",
model = "openai/gpt-4o",
env_key = "JUMPY_API_KEY",
},
openai = {
endpoint = "https://api.openai.com/v1/chat/completions",
model = "gpt-4o",
env_key = "OPENAI_API_KEY",
},
anthropic = {
endpoint = "https://api.anthropic.com/v1/messages",
model = "claude-sonnet-4-6",
env_key = "ANTHROPIC_API_KEY",
},
claude_code = {
model = "sonnet",
},
}
function M.setup(opts)
M.config = vim.tbl_deep_extend("force", M.config, opts or {})
local p = provider_defaults[M.config.provider]
if p then
M.config.endpoint = M.config.endpoint or p.endpoint
M.config.model = M.config.model or p.model
if p.env_key and not M.config.api_key then
M.config.api_key = vim.env[p.env_key] or vim.env.JUMPY_API_KEY
end
else
if not M.config.api_key then
M.config.api_key = vim.env.JUMPY_API_KEY
end
end
M._setup_highlights()
M._setup_keymaps()
end
function M.auto_setup()
M._setup_highlights()
vim.api.nvim_create_user_command("Jumpy", function()
require("jumpy.prompt").open()
end, { desc = "Open Jumpy prompt" })
vim.api.nvim_create_user_command("JumpyCancel", function()
require("jumpy.loading").cancel()
end, { desc = "Cancel all in-flight Jumpy requests" })
vim.api.nvim_create_user_command("JumpySidebar", function()
require("jumpy.sidebar").toggle()
end, { desc = "Toggle Jumpy session sidebar" })
vim.api.nvim_create_user_command("JumpySessions", function()
require("jumpy.sidebar").pick()
end, { desc = "Open a saved Jumpy session" })
vim.api.nvim_create_autocmd("VimLeavePre", {
callback = function()
pcall(function()
require("jumpy.persist").flush()
end)
end,
desc = "Flush pending Jumpy session to disk",
})
end
function M._setup_highlights()
local hl = vim.api.nvim_set_hl
hl(0, "JumpyAdded", { bg = "#1a3a1a", default = true })
hl(0, "JumpyRemoved", { bg = "#3a1a1a", strikethrough = true, default = true })
hl(0, "JumpyAddedSign", { fg = "#4ec94e", default = true })
hl(0, "JumpyRemovedSign", { fg = "#e05252", default = true })
hl(0, "JumpyInspect", { bg = "#3a3320", default = true })
hl(0, "JumpyInspectSign", { fg = "#d7b95a", default = true })
hl(0, "JumpyInspectOther", { fg = "#8a7a3a", italic = true, default = true })
hl(0, "JumpySessionBanner", { link = "Comment", default = true })
hl(0, "JumpySessionHint", { link = "Comment", default = true })
hl(0, "JumpySessionHeader", { link = "Title", default = true })
hl(0, "JumpySessionFile", { link = "Directory", default = true })
hl(0, "JumpySessionPending", { link = "WarningMsg", default = true })
hl(0, "JumpySessionAccepted", { link = "MoreMsg", default = true })
hl(0, "JumpySessionRejected", { link = "ErrorMsg", default = true })
hl(0, "JumpySessionSuperseded", { link = "Comment", default = true })
end
function M._setup_keymaps()
local map = vim.keymap.set
local opts = { silent = true }
local c = M.config.keymaps
-- The prompt is also bound in visual mode ("x") so you can select a region
-- and scope the edit to it; everything else is normal-mode only.
local keymaps = {
{ c.prompt, "jumpy.prompt", "open", { "n", "x" } },
{ c.next_hunk, "jumpy.navigate", "next_hunk" },
{ c.prev_hunk, "jumpy.navigate", "prev_hunk" },
{ c.accept, "jumpy.navigate", "accept" },
{ c.reject, "jumpy.navigate", "reject" },
{ c.accept_all, "jumpy.navigate", "accept_all" },
{ c.reject_all, "jumpy.navigate", "reject_all" },
{ c.reprompt, "jumpy.prompt", "reprompt" },
{ c.quickfix, "jumpy.navigate", "add_hunks_to_quickfix" },
{ c.sidebar, "jumpy.sidebar", "toggle" },
}
for _, km in ipairs(keymaps) do
local key, mod, fn, modes = km[1], km[2], km[3], km[4] or "n"
map(modes, key, function()
require(mod)[fn]()
end, opts)
end
end
return M