Skip to content

Commit bcc93df

Browse files
committed
feat: chat macro support
- starting with @context_file (issue: #206)
1 parent 449052b commit bcc93df

File tree

3 files changed

+128
-2
lines changed

3 files changed

+128
-2
lines changed

after/ftplugin/gpchat.lua

+28
Original file line numberDiff line numberDiff line change
@@ -151,3 +151,31 @@ vim.api.nvim_create_autocmd({ "User" }, {
151151
M.helpers.save_buffer(buf, "gpchat User GpRefresh autocmd")
152152
end,
153153
})
154+
155+
local has_cmp, cmp = pcall(require, "cmp")
156+
if not has_cmp then
157+
M.logger.debug("gpchat: cmp not found, skipping cmp setup")
158+
return
159+
end
160+
161+
M.macro.build_cmp_source("gpchat", {
162+
require("gp.macros.context_file"),
163+
})
164+
165+
local sources = {
166+
{ name = "gpchat" },
167+
}
168+
for _, source in pairs(cmp.get_config().sources) do
169+
if source.name ~= "gpchat" and source.name ~= "buffer" then
170+
table.insert(sources, source)
171+
end
172+
end
173+
174+
M.logger.debug("gpchat: cmp sources " .. vim.inspect(sources))
175+
176+
cmp.setup.buffer({
177+
-- keyword_length = 1,
178+
max_item_count = 100,
179+
completion = { autocomplete = { require("cmp.types").cmp.TriggerEvent.TextChanged } },
180+
sources = sources,
181+
})

lua/gp/init.lua

+9-2
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,9 @@ M.setup = function(opts)
229229
require("gp.macros.context_file"),
230230
})
231231

232-
M.logger.debug("command_parser done")
232+
M.chat_parser = M.macro.build_parser({
233+
require("gp.macros.context_file"),
234+
})
233235

234236
local completions = {
235237
ChatNew = { "popup", "split", "vsplit", "tabnew" },
@@ -1035,8 +1037,13 @@ M.chat_respond = function(params)
10351037
table.insert(messages, 1, { role = "system", content = content })
10361038
end
10371039

1038-
-- strip whitespace from ends of content
1040+
local state = M.buffer_state.get(buf)
10391041
for _, message in ipairs(messages) do
1042+
local response = M.chat_parser(message.content, {}, state)
1043+
if response then
1044+
message.content = M.render.template(response.template, response.artifacts)
1045+
state = response.state
1046+
end
10401047
message.content = message.content:gsub("^%s*(.-)%s*$", "%1")
10411048
end
10421049

lua/gp/macro.lua

+91
Original file line numberDiff line numberDiff line change
@@ -153,4 +153,95 @@ M.build_completion = function(macros, raw)
153153
return completion
154154
end
155155

156+
local registered_cmp_sources = {}
157+
M.build_cmp_source = function(name, macros)
158+
if registered_cmp_sources[name] then
159+
logger.debug("cmp source " .. name .. " already registered")
160+
return nil
161+
end
162+
local source = {}
163+
164+
source.new = function()
165+
return setmetatable({}, { __index = source })
166+
end
167+
168+
source.get_trigger_characters = function()
169+
return { "@", " " }
170+
end
171+
172+
local completion = M.build_completion(macros, true)
173+
174+
source.complete = function(self, params, callback)
175+
local ctx = params.context
176+
local suggestions, triggered = completion(ctx.cursor_before_line:match("%S*$"), ctx.cursor_line, ctx.cursor.col)
177+
178+
if not triggered and not ctx.cursor_before_line:match("%s*@%S*$") then
179+
suggestions = {}
180+
end
181+
182+
logger.debug("macro completion suggestions: " .. vim.inspect(suggestions))
183+
184+
local items = {}
185+
for _, suggestion in ipairs(suggestions) do
186+
table.insert(items, {
187+
label = suggestion,
188+
kind = require("cmp").lsp.CompletionItemKind.Keyword,
189+
documentation = name,
190+
})
191+
end
192+
logger.debug("macro cmp complete output: " .. vim.inspect(items))
193+
194+
callback(items)
195+
end
196+
197+
local has_cmp, cmp = pcall(require, "cmp")
198+
if not has_cmp then
199+
logger.warning("cmp not found, skipping cmp source registration")
200+
return source
201+
end
202+
203+
cmp.register_source(name, source)
204+
registered_cmp_sources[name] = true
205+
206+
if true then
207+
return source
208+
end
209+
210+
cmp.event:on("complete_done", function(event)
211+
if not event or not event.entry or event.entry.source.name ~= name then
212+
return
213+
end
214+
local ctx = event.entry.source.context
215+
local suggestions, triggered = completion(ctx.cursor_before_line:match("%S*$"), ctx.cursor_line, ctx.cursor.col)
216+
logger.debug(
217+
"macro cmp complete_done suggestions: " .. vim.inspect(suggestions) .. " triggered: " .. vim.inspect(triggered)
218+
)
219+
if not suggestions or not triggered then
220+
return
221+
end
222+
223+
vim.schedule(function()
224+
-- insert a space if not already present at the cursor
225+
local cursor_col = vim.api.nvim_win_get_cursor(0)[2]
226+
local line = vim.api.nvim_get_current_line()
227+
logger.debug(
228+
"macro cmp complete_done cursor_col: "
229+
.. cursor_col
230+
.. " line: "
231+
.. line
232+
.. " char: "
233+
.. line:sub(cursor_col, cursor_col)
234+
)
235+
if line:sub(cursor_col, cursor_col) ~= " " then
236+
vim.api.nvim_put({ " " }, "c", false, true)
237+
end
238+
vim.schedule(function()
239+
cmp.complete(suggestions)
240+
end)
241+
end)
242+
end)
243+
244+
return source
245+
end
246+
156247
return M

0 commit comments

Comments
 (0)