From 37d60e93f4a893c36ed9d9436a3df2e15f3392ea Mon Sep 17 00:00:00 2001 From: joaopluigi Date: Fri, 3 Oct 2025 14:56:27 -0300 Subject: [PATCH 1/7] add EcaChatSelectModel and EcaChatSelectBehavior commands --- lua/eca/commands.lua | 30 ++++++++++++++++++++++++++++++ lua/eca/sidebar.lua | 2 ++ lua/eca/state.lua | 26 +++++++++++++++++++++++++- 3 files changed, 57 insertions(+), 1 deletion(-) diff --git a/lua/eca/commands.lua b/lua/eca/commands.lua index 6a245e6..9c85360 100644 --- a/lua/eca/commands.lua +++ b/lua/eca/commands.lua @@ -346,4 +346,34 @@ function M.setup() Logger.debug("ECA commands registered") end +vim.api.nvim_create_user_command("EcaChatSelectModel", function() + local state = require("eca.state"):new() + local models = state.config.models.list + + vim.ui.select(models, { + prompt = "Select ECA Chat Model:", + }, function(choice) + if choice then + state:update_selected_model(choice) + end + end) +end, { + desc = "Select Current ECA Chat model", +}) + +vim.api.nvim_create_user_command("EcaChatSelectBehavior", function() + local state = require("eca.state"):new() + local behaviors = state.config.behaviors.list + + vim.ui.select(behaviors, { + prompt = "Select ECA Chat Behavior:", + }, function(choice) + if choice then + state:update_selected_behavior(choice) + end + end) +end, { + desc = "Select Current ECA Chat behavior", +}) + return M diff --git a/lua/eca/sidebar.lua b/lua/eca/sidebar.lua index 5786c45..e83d79f 100644 --- a/lua/eca/sidebar.lua +++ b/lua/eca/sidebar.lua @@ -1279,6 +1279,8 @@ function M:_send_message(message) requestId = tostring(os.time()), message = message, contexts = contexts or {}, + model = self.mediator:selected_model(), + behavior = self.mediator:selected_behavior(), }, function(err, result) if err then print("err is " .. err) diff --git a/lua/eca/state.lua b/lua/eca/state.lua index 2da885c..89677bc 100644 --- a/lua/eca/state.lua +++ b/lua/eca/state.lua @@ -168,7 +168,7 @@ end function State:_update_usage(usage) self.usage = { tokens = { - limit = (usage.limit and usage.limit.output) or self.usage.tokens.limit, + limit = (usage.limit and usage.limit.context) or self.usage.tokens.limit, session = usage.sessionTokens or self.usage.tokens.session, }, costs = { @@ -198,4 +198,28 @@ function State:_update_tools(tool) end) end +function State:update_selected_model(model) + if not model or type(model) ~= "string" then + return + end + + self.config.models.selected = model + + vim.schedule(function() + require("eca.observer").notify({ type = "state/updated", content = { config = vim.deepcopy(self.config) } }) + end) +end + +function State:update_selected_behavior(behavior) + if not behavior or type(behavior) ~= "string" then + return + end + + self.config.behaviors.selected = behavior + + vim.schedule(function() + require("eca.observer").notify({ type = "state/updated", content = { config = vim.deepcopy(self.config) } }) + end) +end + return State From 4f223a6281ee9b0d9c4dc49f538b86ea44710338 Mon Sep 17 00:00:00 2001 From: Eric Dallo Date: Fri, 3 Oct 2025 15:25:54 -0300 Subject: [PATCH 2/7] Add tests --- tests/test_select_commands.lua | 237 +++++++++++++++++++++++++++++++++ 1 file changed, 237 insertions(+) create mode 100644 tests/test_select_commands.lua diff --git a/tests/test_select_commands.lua b/tests/test_select_commands.lua new file mode 100644 index 0000000..5e30965 --- /dev/null +++ b/tests/test_select_commands.lua @@ -0,0 +1,237 @@ +local MiniTest = require("mini.test") +local eq = MiniTest.expect.equality +local child = MiniTest.new_child_neovim() + +local T = MiniTest.new_set({ + hooks = { + pre_case = function() + child.restart({ "-u", "scripts/minimal_init.lua" }) + child.lua([[ + -- Setup commands + require('eca.commands').setup() + + -- Instantiate state singleton + _G.State = require('eca.state').new() + + -- Mock vim.ui.select for testing + _G.selected_choice = nil + _G.shown_items = nil + _G.shown_prompt = nil + _G.original_select = vim.ui.select + + _G.mock_select = function(choice) + _G.selected_choice = choice + vim.ui.select = function(items, opts, on_choice) + _G.shown_items = items + _G.shown_prompt = opts.prompt + on_choice(choice) + end + end + + _G.restore_select = function() + vim.ui.select = _G.original_select + end + ]]) + end, + post_case = function() + child.lua([[_G.restore_select()]]) + end, + post_once = child.stop, + }, +}) + +-- Test EcaChatSelectModel command +T["EcaChatSelectModel"] = MiniTest.new_set() + +T["EcaChatSelectModel"]["command is registered"] = function() + local commands = child.lua_get("vim.api.nvim_get_commands({})") + eq(type(commands.EcaChatSelectModel), "table") + eq(commands.EcaChatSelectModel.name, "EcaChatSelectModel") +end + +T["EcaChatSelectModel"]["updates state when model selected"] = function() + -- Setup initial state with models + child.lua([[ + _G.State.config.models.list = { "model1", "model2", "model3" } + _G.State.config.models.selected = "model1" + + -- Mock vim.ui.select to auto-select model2 + _G.mock_select("model2") + ]]) + + -- Execute command + child.cmd("EcaChatSelectModel") + + -- Check that state was updated + eq(child.lua_get("_G.State.config.models.selected"), "model2") +end + +T["EcaChatSelectModel"]["handles nil selection"] = function() + -- Setup initial state + child.lua([[ + _G.State.config.models.list = { "model1", "model2" } + _G.State.config.models.selected = "model1" + + -- Mock vim.ui.select to return nil (user cancelled) + _G.mock_select(nil) + ]]) + + -- Execute command + child.cmd("EcaChatSelectModel") + + -- Check that state was NOT updated (still model1) + eq(child.lua_get("_G.State.config.models.selected"), "model1") +end + +T["EcaChatSelectModel"]["displays all available models"] = function() + -- Setup models list + child.lua([[ + _G.State.config.models.list = { "gpt-4", "gpt-3.5-turbo", "claude-3" } + + -- Mock vim.ui.select to capture the items shown + _G.mock_select(nil) + ]]) + + -- Execute command + child.cmd("EcaChatSelectModel") + + -- Verify all models were shown + local shown_items = child.lua_get("_G.shown_items") + eq(shown_items[1], "gpt-4") + eq(shown_items[2], "gpt-3.5-turbo") + eq(shown_items[3], "claude-3") +end + +-- Test EcaChatSelectBehavior command +T["EcaChatSelectBehavior"] = MiniTest.new_set() + +T["EcaChatSelectBehavior"]["command is registered"] = function() + local commands = child.lua_get("vim.api.nvim_get_commands({})") + eq(type(commands.EcaChatSelectBehavior), "table") + eq(commands.EcaChatSelectBehavior.name, "EcaChatSelectBehavior") +end + +T["EcaChatSelectBehavior"]["updates state when behavior selected"] = function() + -- Setup initial state with behaviors + child.lua([[ + _G.State.config.behaviors.list = { "helpful", "creative", "concise" } + _G.State.config.behaviors.selected = "helpful" + + -- Mock vim.ui.select to auto-select creative + _G.mock_select("creative") + ]]) + + -- Execute command + child.cmd("EcaChatSelectBehavior") + + -- Check that state was updated + eq(child.lua_get("_G.State.config.behaviors.selected"), "creative") +end + +T["EcaChatSelectBehavior"]["handles nil selection"] = function() + -- Setup initial state + child.lua([[ + _G.State.config.behaviors.list = { "helpful", "creative" } + _G.State.config.behaviors.selected = "helpful" + + -- Mock vim.ui.select to return nil (user cancelled) + _G.mock_select(nil) + ]]) + + -- Execute command + child.cmd("EcaChatSelectBehavior") + + -- Check that state was NOT updated (still helpful) + eq(child.lua_get("_G.State.config.behaviors.selected"), "helpful") +end + +T["EcaChatSelectBehavior"]["displays all available behaviors"] = function() + -- Setup behaviors list + child.lua([[ + _G.State.config.behaviors.list = { "helpful", "creative", "concise", "technical" } + + -- Mock vim.ui.select to capture the items shown + _G.mock_select(nil) + ]]) + + -- Execute command + child.cmd("EcaChatSelectBehavior") + + -- Verify all behaviors were shown + local shown_items = child.lua_get("_G.shown_items") + eq(shown_items[1], "helpful") + eq(shown_items[2], "creative") + eq(shown_items[3], "concise") + eq(shown_items[4], "technical") +end + +-- Test State update methods +T["State"] = MiniTest.new_set() + +T["State"]["update_selected_model updates config"] = function() + child.lua([[ + _G.State.config.models.list = { "model1", "model2" } + _G.State.config.models.selected = "model1" + + _G.State:update_selected_model("model2") + ]]) + + eq(child.lua_get("_G.State.config.models.selected"), "model2") +end + +T["State"]["update_selected_model handles nil"] = function() + child.lua([[ + _G.State.config.models.selected = "model1" + + -- Should not update if nil is passed + _G.State:update_selected_model(nil) + ]]) + + eq(child.lua_get("_G.State.config.models.selected"), "model1") +end + +T["State"]["update_selected_model handles non-string"] = function() + child.lua([[ + _G.State.config.models.selected = "model1" + + -- Should not update if non-string is passed + _G.State:update_selected_model(123) + ]]) + + eq(child.lua_get("_G.State.config.models.selected"), "model1") +end + +T["State"]["update_selected_behavior updates config"] = function() + child.lua([[ + _G.State.config.behaviors.list = { "helpful", "creative" } + _G.State.config.behaviors.selected = "helpful" + + _G.State:update_selected_behavior("creative") + ]]) + + eq(child.lua_get("_G.State.config.behaviors.selected"), "creative") +end + +T["State"]["update_selected_behavior handles nil"] = function() + child.lua([[ + _G.State.config.behaviors.selected = "helpful" + + -- Should not update if nil is passed + _G.State:update_selected_behavior(nil) + ]]) + + eq(child.lua_get("_G.State.config.behaviors.selected"), "helpful") +end + +T["State"]["update_selected_behavior handles non-string"] = function() + child.lua([[ + _G.State.config.behaviors.selected = "helpful" + + -- Should not update if non-string is passed + _G.State:update_selected_behavior(123) + ]]) + + eq(child.lua_get("_G.State.config.behaviors.selected"), "helpful") +end + +return T From 50b5c90979654e6b63046f72604ef153deb8c4f7 Mon Sep 17 00:00:00 2001 From: joaopluigi Date: Mon, 6 Oct 2025 14:27:23 -0300 Subject: [PATCH 3/7] fix tests --- tests/test_select_commands.lua | 113 +++++++-------------------------- tests/test_state.lua | 70 +++++++++++++++++++- 2 files changed, 91 insertions(+), 92 deletions(-) diff --git a/tests/test_select_commands.lua b/tests/test_select_commands.lua index 5e30965..997db52 100644 --- a/tests/test_select_commands.lua +++ b/tests/test_select_commands.lua @@ -9,16 +9,16 @@ local T = MiniTest.new_set({ child.lua([[ -- Setup commands require('eca.commands').setup() - + -- Instantiate state singleton _G.State = require('eca.state').new() - + -- Mock vim.ui.select for testing _G.selected_choice = nil _G.shown_items = nil _G.shown_prompt = nil _G.original_select = vim.ui.select - + _G.mock_select = function(choice) _G.selected_choice = choice vim.ui.select = function(items, opts, on_choice) @@ -27,7 +27,7 @@ local T = MiniTest.new_set({ on_choice(choice) end end - + _G.restore_select = function() vim.ui.select = _G.original_select end @@ -54,14 +54,14 @@ T["EcaChatSelectModel"]["updates state when model selected"] = function() child.lua([[ _G.State.config.models.list = { "model1", "model2", "model3" } _G.State.config.models.selected = "model1" - + -- Mock vim.ui.select to auto-select model2 _G.mock_select("model2") ]]) - + -- Execute command child.cmd("EcaChatSelectModel") - + -- Check that state was updated eq(child.lua_get("_G.State.config.models.selected"), "model2") end @@ -71,14 +71,14 @@ T["EcaChatSelectModel"]["handles nil selection"] = function() child.lua([[ _G.State.config.models.list = { "model1", "model2" } _G.State.config.models.selected = "model1" - + -- Mock vim.ui.select to return nil (user cancelled) _G.mock_select(nil) ]]) - + -- Execute command child.cmd("EcaChatSelectModel") - + -- Check that state was NOT updated (still model1) eq(child.lua_get("_G.State.config.models.selected"), "model1") end @@ -87,14 +87,14 @@ T["EcaChatSelectModel"]["displays all available models"] = function() -- Setup models list child.lua([[ _G.State.config.models.list = { "gpt-4", "gpt-3.5-turbo", "claude-3" } - + -- Mock vim.ui.select to capture the items shown _G.mock_select(nil) ]]) - + -- Execute command child.cmd("EcaChatSelectModel") - + -- Verify all models were shown local shown_items = child.lua_get("_G.shown_items") eq(shown_items[1], "gpt-4") @@ -116,14 +116,14 @@ T["EcaChatSelectBehavior"]["updates state when behavior selected"] = function() child.lua([[ _G.State.config.behaviors.list = { "helpful", "creative", "concise" } _G.State.config.behaviors.selected = "helpful" - + -- Mock vim.ui.select to auto-select creative _G.mock_select("creative") ]]) - + -- Execute command child.cmd("EcaChatSelectBehavior") - + -- Check that state was updated eq(child.lua_get("_G.State.config.behaviors.selected"), "creative") end @@ -133,14 +133,14 @@ T["EcaChatSelectBehavior"]["handles nil selection"] = function() child.lua([[ _G.State.config.behaviors.list = { "helpful", "creative" } _G.State.config.behaviors.selected = "helpful" - + -- Mock vim.ui.select to return nil (user cancelled) _G.mock_select(nil) ]]) - + -- Execute command child.cmd("EcaChatSelectBehavior") - + -- Check that state was NOT updated (still helpful) eq(child.lua_get("_G.State.config.behaviors.selected"), "helpful") end @@ -149,14 +149,14 @@ T["EcaChatSelectBehavior"]["displays all available behaviors"] = function() -- Setup behaviors list child.lua([[ _G.State.config.behaviors.list = { "helpful", "creative", "concise", "technical" } - + -- Mock vim.ui.select to capture the items shown _G.mock_select(nil) ]]) - + -- Execute command child.cmd("EcaChatSelectBehavior") - + -- Verify all behaviors were shown local shown_items = child.lua_get("_G.shown_items") eq(shown_items[1], "helpful") @@ -165,73 +165,4 @@ T["EcaChatSelectBehavior"]["displays all available behaviors"] = function() eq(shown_items[4], "technical") end --- Test State update methods -T["State"] = MiniTest.new_set() - -T["State"]["update_selected_model updates config"] = function() - child.lua([[ - _G.State.config.models.list = { "model1", "model2" } - _G.State.config.models.selected = "model1" - - _G.State:update_selected_model("model2") - ]]) - - eq(child.lua_get("_G.State.config.models.selected"), "model2") -end - -T["State"]["update_selected_model handles nil"] = function() - child.lua([[ - _G.State.config.models.selected = "model1" - - -- Should not update if nil is passed - _G.State:update_selected_model(nil) - ]]) - - eq(child.lua_get("_G.State.config.models.selected"), "model1") -end - -T["State"]["update_selected_model handles non-string"] = function() - child.lua([[ - _G.State.config.models.selected = "model1" - - -- Should not update if non-string is passed - _G.State:update_selected_model(123) - ]]) - - eq(child.lua_get("_G.State.config.models.selected"), "model1") -end - -T["State"]["update_selected_behavior updates config"] = function() - child.lua([[ - _G.State.config.behaviors.list = { "helpful", "creative" } - _G.State.config.behaviors.selected = "helpful" - - _G.State:update_selected_behavior("creative") - ]]) - - eq(child.lua_get("_G.State.config.behaviors.selected"), "creative") -end - -T["State"]["update_selected_behavior handles nil"] = function() - child.lua([[ - _G.State.config.behaviors.selected = "helpful" - - -- Should not update if nil is passed - _G.State:update_selected_behavior(nil) - ]]) - - eq(child.lua_get("_G.State.config.behaviors.selected"), "helpful") -end - -T["State"]["update_selected_behavior handles non-string"] = function() - child.lua([[ - _G.State.config.behaviors.selected = "helpful" - - -- Should not update if non-string is passed - _G.State:update_selected_behavior(123) - ]]) - - eq(child.lua_get("_G.State.config.behaviors.selected"), "helpful") -end - return T diff --git a/tests/test_state.lua b/tests/test_state.lua index 66a6d11..9f89a78 100644 --- a/tests/test_state.lua +++ b/tests/test_state.lua @@ -97,7 +97,7 @@ T["updates via observer notifications"]["updates usage on usage content"] = func method = 'chat/contentReceived', params = { content = { type = 'usage', - limit = { output = 1024 }, + limit = { context = 1024 }, sessionTokens = 256, lastMessageCost = '0.42', sessionCost = '3.14', @@ -184,4 +184,72 @@ T["updates via observer notifications"]["updates tools on tool/serverUpdated"] = eq(#updates >= 1, true) end +T["update selected model and behavior"] = MiniTest.new_set() + +T["update selected model and behavior"]["update_selected_model updates config"] = function() + child.lua([[ + _G.State.config.models.list = { "model1", "model2" } + _G.State.config.models.selected = "model1" + + _G.State:update_selected_model("model2") + ]]) + + eq(child.lua_get("_G.State.config.models.selected"), "model2") +end + +T["update selected model and behavior"]["update_selected_model handles nil"] = function() + child.lua([[ + _G.State.config.models.selected = "model1" + + -- Should not update if nil is passed + _G.State:update_selected_model(nil) + ]]) + + eq(child.lua_get("_G.State.config.models.selected"), "model1") +end + +T["update selected model and behavior"]["update_selected_model handles non-string"] = function() + child.lua([[ + _G.State.config.models.selected = "model1" + + -- Should not update if non-string is passed + _G.State:update_selected_model(123) + ]]) + + eq(child.lua_get("_G.State.config.models.selected"), "model1") +end + +T["update selected model and behavior"]["update_selected_behavior updates config"] = function() + child.lua([[ + _G.State.config.behaviors.list = { "helpful", "creative" } + _G.State.config.behaviors.selected = "helpful" + + _G.State:update_selected_behavior("creative") + ]]) + + eq(child.lua_get("_G.State.config.behaviors.selected"), "creative") +end + +T["update selected model and behavior"]["update_selected_behavior handles nil"] = function() + child.lua([[ + _G.State.config.behaviors.selected = "helpful" + + -- Should not update if nil is passed + _G.State:update_selected_behavior(nil) + ]]) + + eq(child.lua_get("_G.State.config.behaviors.selected"), "helpful") +end + +T["update selected model and behavior"]["update_selected_behavior handles non-string"] = function() + child.lua([[ + _G.State.config.behaviors.selected = "helpful" + + -- Should not update if non-string is passed + _G.State:update_selected_behavior(123) + ]]) + + eq(child.lua_get("_G.State.config.behaviors.selected"), "helpful") +end + return T From 87e3231b68bbfd320aa6f0862add3cc58646e00b Mon Sep 17 00:00:00 2001 From: joaopluigi Date: Wed, 8 Oct 2025 08:37:38 -0300 Subject: [PATCH 4/7] address code review changes --- lua/eca/commands.lua | 60 ++++++++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/lua/eca/commands.lua b/lua/eca/commands.lua index 9c85360..9976d5b 100644 --- a/lua/eca/commands.lua +++ b/lua/eca/commands.lua @@ -343,37 +343,37 @@ function M.setup() desc = "Emergency fix for treesitter issues in ECA chat", }) - Logger.debug("ECA commands registered") -end + vim.api.nvim_create_user_command("EcaChatSelectModel", function() + local state = require("eca.state"):new() + local models = state.config.models.list + + vim.ui.select(models, { + prompt = "Select ECA Chat Model:", + }, function(choice) + if choice then + state:update_selected_model(choice) + end + end) + end, { + desc = "Select current ECA Chat model", + }) -vim.api.nvim_create_user_command("EcaChatSelectModel", function() - local state = require("eca.state"):new() - local models = state.config.models.list + vim.api.nvim_create_user_command("EcaChatSelectBehavior", function() + local state = require("eca.state"):new() + local behaviors = state.config.behaviors.list - vim.ui.select(models, { - prompt = "Select ECA Chat Model:", - }, function(choice) - if choice then - state:update_selected_model(choice) - end - end) -end, { - desc = "Select Current ECA Chat model", -}) - -vim.api.nvim_create_user_command("EcaChatSelectBehavior", function() - local state = require("eca.state"):new() - local behaviors = state.config.behaviors.list - - vim.ui.select(behaviors, { - prompt = "Select ECA Chat Behavior:", - }, function(choice) - if choice then - state:update_selected_behavior(choice) - end - end) -end, { - desc = "Select Current ECA Chat behavior", -}) + vim.ui.select(behaviors, { + prompt = "Select ECA Chat Behavior:", + }, function(choice) + if choice then + state:update_selected_behavior(choice) + end + end) + end, { + desc = "Select current ECA Chat behavior", + }) + + Logger.debug("ECA commands registered") +end return M From db32ed94991510262dd7cbb93eeb285b9da104bf Mon Sep 17 00:00:00 2001 From: joaopluigi Date: Wed, 8 Oct 2025 08:50:56 -0300 Subject: [PATCH 5/7] make server start to accept clean option and use it on tests --- lua/eca/server.lua | 4 ++-- tests/test_server_integration.lua | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lua/eca/server.lua b/lua/eca/server.lua index 54b94e4..5ac3a23 100644 --- a/lua/eca/server.lua +++ b/lua/eca/server.lua @@ -98,7 +98,7 @@ end ---in testing ---@param opts? eca.ServerStartOpts function M:start(opts) - opts = opts or { initialize = true } + opts = vim.tbl_deep_extend("force", { initialize = true }, opts or {}) local this_file = debug.getinfo(1, "S").source:sub(2) local proj_root = vim.fn.fnamemodify(this_file, ":p:h:h:h") @@ -112,7 +112,7 @@ function M:start(opts) local lua_cmd = string.format("lua ServerPath.run(%s)", Utils.lua_quote(Config.server_path or "")) - local cmd = { nvim_exe, "--headless", "--noplugin", "-u", script_path, "-c", lua_cmd } + local cmd = { nvim_exe, "--headless", "--noplugin", (opts.clean and " --clean" or ""), "-u", script_path, "-c", lua_cmd } vim.system(cmd, { text = true }, function(out) if out.code ~= 0 then diff --git a/tests/test_server_integration.lua b/tests/test_server_integration.lua index e9a73db..94d3e8d 100644 --- a/tests/test_server_integration.lua +++ b/tests/test_server_integration.lua @@ -40,7 +40,7 @@ end T["server"] = MiniTest.new_set() T["server"]["start"] = function() - child.lua("_G.server:start()") + child.lua("_G.server:start({ clean = true })") child.lua([[ _G.server_started = vim.wait(10000, function() return _G.server and _G.server:is_running() @@ -52,7 +52,7 @@ T["server"]["start"] = function() end T["server"]["start without initialize"] = function() - child.lua("_G.server:start({ initialize = false })") + child.lua("_G.server:start({ clean = true, initialize = false })") child.lua([[ _G.server_started = vim.wait(10000, function() return _G.server and _G.server:is_running() @@ -67,7 +67,7 @@ T["server"]["start with inexistent path"] = function() child.lua([[ Config = require("eca.config") Config.setup({ server_path = "non-existing-path" } ) - _G.server:start() + _G.server:start({ clean = true }) ]]) child.lua([[ _G.server_started = vim.wait(1000, function() From a21fac37453abb4709da3ff1306f99465b6afa36 Mon Sep 17 00:00:00 2001 From: joaopluigi Date: Wed, 8 Oct 2025 08:56:23 -0300 Subject: [PATCH 6/7] inject eca dependencies when using --clean mode --- scripts/server_path.lua | 10 ++++++++++ tests/test_server_path.lua | 1 + 2 files changed, 11 insertions(+) diff --git a/scripts/server_path.lua b/scripts/server_path.lua index 8e0317b..f7b0570 100644 --- a/scripts/server_path.lua +++ b/scripts/server_path.lua @@ -3,9 +3,19 @@ local ServerPath = {} -- Setup if headless if #vim.api.nvim_list_uis() == 0 then _G.ServerPath = ServerPath + + -- hijack to make server tests work on CI using --clean mode + local eca_available = pcall(require, "eca") + if not eca_available then + vim.cmd([[let &rtp.=','.getcwd()]]) + vim.cmd('set rtp+=deps/nui.nvim') + vim.cmd('set rtp+=deps/eca-nvim') + end + vim.o.swapfile = false vim.o.backup = false vim.o.writebackup = false + require("eca").setup({}) end diff --git a/tests/test_server_path.lua b/tests/test_server_path.lua index b0880f5..a285171 100644 --- a/tests/test_server_path.lua +++ b/tests/test_server_path.lua @@ -9,6 +9,7 @@ local function setup_test_environment() "nvim", "--headless", "--noplugin", + "--clean", "--cmd", [[lua package.preload["eca.path_finder"] = function() local M = {} From 9b3d6bd10dc72a6ae6953af1f98b81325bb09a4f Mon Sep 17 00:00:00 2001 From: joaopluigi Date: Wed, 8 Oct 2025 09:21:08 -0300 Subject: [PATCH 7/7] make commands use current chat instead of state directly --- lua/eca/commands.lua | 26 +++++++++++---- lua/eca/mediator.lua | 16 +++++++++ tests/test_select_commands.lua | 59 +++++++++++++++++++--------------- 3 files changed, 69 insertions(+), 32 deletions(-) diff --git a/lua/eca/commands.lua b/lua/eca/commands.lua index 9976d5b..24d26cc 100644 --- a/lua/eca/commands.lua +++ b/lua/eca/commands.lua @@ -344,14 +344,21 @@ function M.setup() }) vim.api.nvim_create_user_command("EcaChatSelectModel", function() - local state = require("eca.state"):new() - local models = state.config.models.list + local eca = require("eca") + + if not eca or not eca.current or not eca.current.sidebar then + Logger.notify("No active ECA sidebar found", vim.log.levels.WARN) + return + end + + local chat = eca.current.sidebar + local models = chat.mediator:models() vim.ui.select(models, { prompt = "Select ECA Chat Model:", }, function(choice) if choice then - state:update_selected_model(choice) + chat.mediator:update_selected_model(choice) end end) end, { @@ -359,14 +366,21 @@ function M.setup() }) vim.api.nvim_create_user_command("EcaChatSelectBehavior", function() - local state = require("eca.state"):new() - local behaviors = state.config.behaviors.list + local eca = require("eca") + + if not eca or not eca.current or not eca.current.sidebar then + Logger.notify("No active ECA sidebar found", vim.log.levels.WARN) + return + end + + local chat = eca.current.sidebar + local behaviors = chat.mediator:behaviors() vim.ui.select(behaviors, { prompt = "Select ECA Chat Behavior:", }, function(choice) if choice then - state:update_selected_behavior(choice) + chat.mediator:update_selected_behavior(choice) end end) end, { diff --git a/lua/eca/mediator.lua b/lua/eca/mediator.lua index 7bdc8e6..9298891 100644 --- a/lua/eca/mediator.lua +++ b/lua/eca/mediator.lua @@ -26,14 +26,30 @@ function mediator:send(method, params, callback) self.server:send_request(method, params, callback) end +function mediator:behaviors() + return self.state.config.behaviors.list +end + function mediator:selected_behavior() return self.state.config.behaviors.selected end +function mediator:update_selected_behavior(behavior) + self.state:update_selected_behavior(behavior) +end + +function mediator:models() + return self.state.config.models.list +end + function mediator:selected_model() return self.state.config.models.selected end +function mediator:update_selected_model(model) + self.state:update_selected_model(model) +end + function mediator:tokens_session() return self.state.usage.tokens.session end diff --git a/tests/test_select_commands.lua b/tests/test_select_commands.lua index 997db52..23c028d 100644 --- a/tests/test_select_commands.lua +++ b/tests/test_select_commands.lua @@ -2,36 +2,43 @@ local MiniTest = require("mini.test") local eq = MiniTest.expect.equality local child = MiniTest.new_child_neovim() +local function setup_test_environment() + -- Setup commands + require('eca.commands').setup() + + -- Initialize everything + _G.Server = require('eca.server').new() + _G.State = require('eca.state').new() + _G.Mediator = require('eca.mediator').new(_G.Server, _G.State) + _G.Sidebar = require('eca.sidebar').new(1, _G.Mediator) + _G.Eca = require('eca') + _G.Eca.current = { sidebar = _G.Sidebar } + + -- Mock vim.ui.select for testing + _G.selected_choice = nil + _G.shown_items = nil + _G.shown_prompt = nil + _G.original_select = vim.ui.select + + _G.mock_select = function(choice) + _G.selected_choice = choice + vim.ui.select = function(items, opts, on_choice) + _G.shown_items = items + _G.shown_prompt = opts.prompt + on_choice(choice) + end + end + + _G.restore_select = function() + vim.ui.select = _G.original_select + end +end + local T = MiniTest.new_set({ hooks = { pre_case = function() child.restart({ "-u", "scripts/minimal_init.lua" }) - child.lua([[ - -- Setup commands - require('eca.commands').setup() - - -- Instantiate state singleton - _G.State = require('eca.state').new() - - -- Mock vim.ui.select for testing - _G.selected_choice = nil - _G.shown_items = nil - _G.shown_prompt = nil - _G.original_select = vim.ui.select - - _G.mock_select = function(choice) - _G.selected_choice = choice - vim.ui.select = function(items, opts, on_choice) - _G.shown_items = items - _G.shown_prompt = opts.prompt - on_choice(choice) - end - end - - _G.restore_select = function() - vim.ui.select = _G.original_select - end - ]]) + child.lua_func(setup_test_environment) end, post_case = function() child.lua([[_G.restore_select()]])