Skip to content

Commit 3868642

Browse files
committed
initial implementation of context area handling
1 parent 854a4a8 commit 3868642

File tree

3 files changed

+84
-14
lines changed

3 files changed

+84
-14
lines changed

lua/eca/mediator.lua

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -201,12 +201,12 @@ function mediator:add_context(context)
201201
self.state:add_context(context)
202202
end
203203

204-
function mediator:remove_context(name)
204+
function mediator:remove_context(context)
205205
if not self.state then
206206
return
207207
end
208208

209-
self.state:remove_context(name)
209+
self.state:remove_context(context)
210210
end
211211

212212
function mediator:clear_contexts()

lua/eca/sidebar.lua

Lines changed: 81 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ local Split = require("nui.split")
2323
---@field private _response_start_time number Timestamp when streaming started
2424
---@field private _max_response_length number Maximum allowed response length
2525
---@field private _headers table Table of headers for the chat
26+
---@field private _welcome_message_applied boolean Whether the welcome message has been applied
27+
---@field private _contexts_placeholder_line string Placeholder line for contexts in input
2628

2729
local M = {}
2830
M.__index = M
@@ -60,6 +62,7 @@ function M.new(id, mediator)
6062
assistant = (Config.chat and Config.chat.headers and Config.chat.headers.assistant) or "",
6163
}
6264
instance._welcome_message_applied = false
65+
instance._contexts_placeholder_line = ""
6366

6467
require("eca.observer").subscribe("sidebar-" .. id, function(message)
6568
instance:handle_chat_content(message)
@@ -186,6 +189,7 @@ function M:reset()
186189
self._force_welcome = false
187190
self._current_status = ""
188191
self._welcome_message_applied = false
192+
self._contexts_placeholder_line = ""
189193
end
190194

191195
function M:new_chat()
@@ -360,6 +364,7 @@ end
360364
function M:_setup_container_events(container, name)
361365
-- Setup container-specific keymaps
362366
if name == "input" then
367+
self:_setup_input_events(container)
363368
self:_setup_input_keymaps(container)
364369
end
365370
end
@@ -373,6 +378,58 @@ function M:_handle_container_closed(name)
373378
end
374379
end
375380

381+
---@private
382+
---@param container NuiSplit
383+
function M:_setup_input_events(container)
384+
-- prevent contexts line or input prefix from being deleted
385+
vim.api.nvim_buf_attach(container.bufnr, false, {
386+
on_lines = function(_, buf, _changedtick, first, _last, _new_last, _bytecount)
387+
if first ~= 0 and first ~= 1 then
388+
return
389+
end
390+
391+
local lines = vim.api.nvim_buf_get_lines(buf, 0, -1, false)
392+
393+
-- restore input line if deleted
394+
if first == 1 and #lines < 2 then
395+
self:_update_input_display()
396+
return
397+
end
398+
399+
-- first line (contexts) was changed
400+
if first == 0 then
401+
local contexts_line = lines[1]
402+
403+
-- if contexts line is deleted, clear all contexts
404+
if not contexts_line or contexts_line == "" then
405+
self.mediator:clear_contexts()
406+
return
407+
end
408+
409+
-- contexts line was modified
410+
if contexts_line ~= self._contexts_placeholder_line then
411+
412+
-- if contexts line is shorter than placeholder, a context was removed
413+
if #contexts_line < #self._contexts_placeholder_line then
414+
local contexts = self.mediator:contexts()
415+
416+
local row, col = unpack(vim.api.nvim_win_get_cursor(container.winid))
417+
local context = contexts[col+1]
418+
419+
if row == 1 and context then
420+
self.mediator:remove_context(context)
421+
return
422+
end
423+
end
424+
425+
self:_update_input_display()
426+
return
427+
end
428+
end
429+
end
430+
})
431+
end
432+
376433
---@private
377434
---@param container NuiSplit
378435
function M:_setup_input_keymaps(container)
@@ -561,7 +618,7 @@ function M:_set_welcome_content()
561618
self:_update_welcome_content()
562619
end
563620

564-
function M:_update_input_display()
621+
function M:_update_input_display(opts)
565622
return vim.schedule(function()
566623
local input = self.containers.input
567624
if not input then
@@ -590,15 +647,22 @@ function M:_update_input_display()
590647
end
591648
end
592649

593-
local placeholder_line = "@"
650+
local old_contexts_placeholder_line = self._contexts_placeholder_line
651+
652+
self._contexts_placeholder_line = "@"
594653
for _ = 1, #contexts_name do
595-
placeholder_line = placeholder_line .. "@"
654+
self._contexts_placeholder_line = self._contexts_placeholder_line .. "@"
596655
end
597656

598657
-- Get existing lines to preserve user input (lines after the header)
599-
local existing_lines = vim.api.nvim_buf_get_lines(input.bufnr, 1, -1, false)
658+
local existing_lines = vim.api.nvim_buf_get_lines(input.bufnr, 0, -1, false)
659+
660+
-- If first line is the contexts placeholder, remove it from existing lines
661+
if existing_lines and #existing_lines > 1 and (existing_lines[1] == "" or existing_lines[1] == old_contexts_placeholder_line or existing_lines[1] == self._contexts_placeholder_line) then
662+
table.remove(existing_lines, 1)
663+
end
600664

601-
vim.api.nvim_buf_set_lines(input.bufnr, 0, -1, false, { placeholder_line, "" })
665+
vim.api.nvim_buf_set_lines(input.bufnr, 0, -1, false, { self._contexts_placeholder_line, "" })
602666

603667
if not self.extmarks.contexts then
604668
self.extmarks.contexts = {
@@ -628,7 +692,9 @@ function M:_update_input_display()
628692
}
629693
end
630694

631-
if #existing_lines > 0 then
695+
local clear = opts and opts.clear
696+
697+
if #existing_lines > 0 and not clear then
632698
vim.api.nvim_buf_set_lines(input.bufnr, 1, 1 + #existing_lines, false, existing_lines)
633699
end
634700

@@ -640,9 +706,12 @@ function M:_update_input_display()
640706
vim.tbl_extend("force", { virt_text = { { prefix, "Normal" } }, virt_text_pos = "inline", right_gravity = false }, { id = self.extmarks.prefix._id })
641707
)
642708

643-
-- Set cursor to end of prefix line
709+
-- Set cursor to end of input line
644710
if vim.api.nvim_win_is_valid(input.winid) then
645-
vim.api.nvim_win_set_cursor(input.winid, { 2, #prefix })
711+
local row = 1 + (not clear and existing_lines and #existing_lines > 0 and #existing_lines or 1)
712+
local col = #prefix + (not clear and existing_lines and #existing_lines > 0 and #existing_lines[#existing_lines] or 0)
713+
714+
vim.api.nvim_win_set_cursor(input.winid, { row, col })
646715
end
647716
end)
648717
end
@@ -715,7 +784,7 @@ function M:_handle_input()
715784
self:_send_message(message)
716785

717786
-- Add new input line and focus
718-
self:_update_input_display()
787+
self:_update_input_display({ clear = true })
719788
self:_focus_input()
720789
end
721790

@@ -832,7 +901,7 @@ function M:_update_usage_info()
832901
end
833902

834903
function M:_update_welcome_content()
835-
if self._welcome_applied then
904+
if self._welcome_message_applied then
836905
return
837906
end
838907

@@ -858,11 +927,11 @@ function M:_update_welcome_content()
858927
end
859928
end
860929

861-
self._welcome_applied = true
930+
self._welcome_message_applied = true
862931
end
863932

864933
table.insert(lines, "")
865-
Logger.debug("Setting welcome content for chat (welcome applied: " .. tostring(self._welcome_applied) .. ")")
934+
Logger.debug("Setting welcome content for chat (welcome applied: " .. tostring(self._welcome_message_applied) .. ")")
866935
vim.api.nvim_buf_set_lines(chat.bufnr, 0, -1, false, lines)
867936
end
868937

lua/eca/state.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,7 @@ function State:remove_context(context)
276276
if ctx.type == context.type and vim.deep_equal(ctx.data, context.data) then
277277
table.remove(self.contexts, i)
278278
self:_update_contexts()
279+
break
279280
end
280281
end
281282
end

0 commit comments

Comments
 (0)