Skip to content

Commit ec6f364

Browse files
committed
wip
1 parent 7ee8557 commit ec6f364

3 files changed

Lines changed: 76 additions & 35 deletions

File tree

lua/codecompanion/acp/init.lua

Lines changed: 49 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
local METHODS = require("codecompanion.acp.methods")
2222
local PromptBuilder = require("codecompanion.acp.prompt_builder")
2323
local adapter_utils = require("codecompanion.utils.adapters")
24+
local async = require("codecompanion.utils.async")
2425
local config = require("codecompanion.config")
2526
local jsonrpc = require("codecompanion.utils.jsonrpc")
2627
local log = require("codecompanion.utils.log")
@@ -53,6 +54,7 @@ local uv = vim.uv
5354
---@field _modes {currentModeId: string, availableModes: table[]}|nil
5455
---@field _models {currentModelId: string, availableModels: table[]}|nil
5556
---@field _model_config_id string|nil The configOptions ID for the model selector
57+
---@field _pending_callbacks table<number, function> Async callbacks keyed by request ID
5658
---@field methods table
5759
local Connection = {}
5860

@@ -86,12 +88,13 @@ function Connection.new(args)
8688
local self = setmetatable({
8789
adapter = args.adapter,
8890
adapter_modified = {},
91+
methods = methods,
8992
pending_responses = {},
9093
session_id = args.session_id,
91-
methods = methods,
92-
_initialized = false,
9394
_authenticated = false,
95+
_initialized = false,
9496
_modes = nil,
97+
_pending_callbacks = {},
9598
_state = { handle = nil, id_gen = jsonrpc.IdGenerator.new(), line_buffer = jsonrpc.LineBuffer.new() },
9699
}, { __index = Connection }) ---@cast self CodeCompanion.ACP.Connection
97100

@@ -544,7 +547,7 @@ function Connection:start_agent_process()
544547
return true
545548
end
546549

547-
---Send a synchronous request and wait for response
550+
---If called inside an async coroutine, yields until the response arrives, or fallsback to sync
548551
---@param method string
549552
---@param params table
550553
---@return table|nil
@@ -560,6 +563,14 @@ function Connection:send_rpc_request(method, params)
560563
return nil
561564
end
562565

566+
-- Async path: yield and let store_rpc_response resume us
567+
if coroutine.running() then
568+
return async.wait(function(callback)
569+
self._pending_callbacks[id] = callback
570+
end)
571+
end
572+
573+
-- Sync fallback
563574
return self:wait_for_rpc_response(id)
564575
end
565576

@@ -664,24 +675,36 @@ function Connection:handle_rpc_message(line)
664675
end
665676
end
666677

667-
---Handle response to our request
678+
---Handles the response to the request
668679
---@param response table
669680
function Connection:store_rpc_response(response)
670-
if response.error then
671-
self.pending_responses[response.id] = { nil, response.error }
681+
local function forward_error_to_prompt()
682+
if not response.error or not self._active_prompt or not self._active_prompt.handle_error then
683+
return
684+
end
685+
self.methods.schedule(function()
686+
local error_msg = response.error.message or "Unknown error"
687+
if response.error.data and response.error.data.error then
688+
error_msg = response.error.data.error
689+
end
690+
self._active_prompt:handle_error(error_msg)
691+
end)
692+
end
672693

673-
-- Sometimes errors are passed as part of the response so we need to handle them
674-
if self._active_prompt and self._active_prompt.handle_error then
675-
self.methods.schedule(function()
676-
local error_msg = response.error.message or "Unknown error"
677-
if response.error.data and response.error.data.error then
678-
error_msg = response.error.data.error
679-
end
694+
-- Async path: resume the waiting coroutine via its callback
695+
local cb = self._pending_callbacks[response.id]
696+
if cb then
697+
self._pending_callbacks[response.id] = nil
698+
self.methods.schedule(function()
699+
cb((not response.error) and response.result or nil)
700+
end)
701+
return forward_error_to_prompt()
702+
end
680703

681-
self._active_prompt:handle_error(error_msg)
682-
end)
683-
end
684-
return
704+
-- Sync path: store for polling
705+
if response.error then
706+
self.pending_responses[response.id] = { nil, response.error }
707+
return forward_error_to_prompt()
685708
end
686709
self.pending_responses[response.id] = { response.result, nil }
687710
end
@@ -951,12 +974,19 @@ function Connection:handle_process_exit(code, signal)
951974
self.adapter_modified.handlers.on_exit(self.adapter_modified, code)
952975
end
953976

977+
-- Fire any pending async callbacks so coroutines don't hang
978+
for id, cb in pairs(self._pending_callbacks) do
979+
self._pending_callbacks[id] = nil
980+
pcall(cb, nil)
981+
end
982+
954983
-- Always clean up state
955984
self.adapter_modified = nil
956-
self._initialized = false
957985
self._authenticated = false
958-
self.session_id = nil
986+
self._initialized = false
987+
self._pending_callbacks = {}
959988
self.pending_responses = {}
989+
self.session_id = nil
960990

961991
if self._active_prompt and self._active_prompt.handle_done then
962992
pcall(function()

lua/codecompanion/interactions/chat/acp/handler.lua

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -57,23 +57,29 @@ function ACPHandler:submit(payload)
5757
end
5858

5959
---Ensure the ACP connection is authenticated
60-
---@return boolean success
60+
---@return boolean
6161
function ACPHandler:ensure_connection()
62+
-- If the async init already created the connection, check if it's ready
63+
if self.chat.acp_connection and self.chat.acp_connection:is_ready() then
64+
return true
65+
end
66+
6267
if not self.chat.acp_connection then
6368
self.chat.acp_connection = require("codecompanion.acp").new({
6469
adapter = self.chat.adapter, ---@type CodeCompanion.ACPAdapter
6570
})
71+
end
6672

67-
local connected = self.chat.acp_connection:connect_and_authenticate()
73+
local connected = self.chat.acp_connection:connect_and_authenticate()
6874

69-
if not connected then
70-
return false
71-
end
75+
if not connected then
76+
return false
77+
end
7278

73-
self.chat:update_metadata()
79+
self.chat:update_metadata()
80+
81+
watch.enable()
7482

75-
watch.enable()
76-
end
7783
return true
7884
end
7985

lua/codecompanion/interactions/chat/helpers/init.lua

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,23 @@ local M = {}
99
local api = vim.api
1010
local fmt = string.format
1111

12-
---Establishes the connection, authenticates, creates a session and links the buffer
12+
---Establishes the connection, authenticates, creates a session and links the buffer.
13+
---Runs asynchronously so the UI is not blocked during ACP initialization.
1314
---@param chat CodeCompanion.Chat The chat instance
14-
---@return boolean
15+
---@return nil
1516
function M.create_acp_connection(chat)
16-
local ACPHandler = require("codecompanion.interactions.chat.acp.handler")
17-
local handler = ACPHandler.new(chat)
17+
local async_utils = require("codecompanion.utils.async")
1818

19-
if not handler:ensure_connection() then
20-
return false
21-
end
19+
async_utils.sync(function()
20+
local ACPHandler = require("codecompanion.interactions.chat.acp.handler")
21+
local handler = ACPHandler.new(chat)
22+
23+
if not handler:ensure_connection() then
24+
return
25+
end
2226

23-
return handler:ensure_session()
27+
handler:ensure_session()
28+
end)()
2429
end
2530

2631
---Format the given role without any separator

0 commit comments

Comments
 (0)