-
Notifications
You must be signed in to change notification settings - Fork 35
perf: open the panel without waiting on session #134
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
There is still some issues, like test and context not loading properly, but it's a start |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR improves performance by making the panel opening process asynchronous. Instead of blocking while initializing the session and server, the UI is created immediately and displays "Loading..." while initialization happens in the background via vim.schedule(). This is achieved by introducing an is_opening state flag and returning promises from the core.open() function to allow proper async flow control.
Key changes:
- Refactored
core.open()to be asynchronous usingvim.schedule()and promises - Added
is_openingstate flag to track initialization status and display "Loading..." in the UI - Updated API functions to chain promises with
:and_then()for proper async orchestration
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
| lua/opencode/state.lua | Adds is_opening boolean field to track panel opening state |
| lua/opencode/ui/topbar.lua | Displays "Loading..." when is_opening is true and subscribes to state changes |
| lua/opencode/core.lua | Refactors M.open() to be async with promises, moves session initialization into vim.schedule(), and adds error logging |
| lua/opencode/api.lua | Updates API functions to return promises, chains core.open() with :and_then(), removes redundant local config declarations, and changes default focus from 'output' to 'input' in M.run() functions |
Comments suppressed due to low confidence (1)
lua/opencode/core.lua:186
- The
send_messagefunction doesn't return the promise fromapi_client:create_message(). This breaks the promise chain in API functions likeM.run()andM.run_new_session()which expect to chain this with:and_then().
Add return before the state.api_client:create_message() call to properly propagate the promise:
return state.api_client
:create_message(session_id, params)
:and_then(function(response)
-- ...
end)
:catch(function(err)
-- ...
end) state.api_client
:create_message(session_id, params)
:and_then(function(response)
if not response or not response.info or not response.parts then
-- fall back to full render. incremental render is handled
-- event manager
vim.notify('Invalid response from session: ' .. vim.inspect(response), vim.log.levels.ERROR)
ui.render_output()
return
end
local received_message_count = vim.deepcopy(state.user_message_count)
received_message_count[response.info.sessionID] = (received_message_count[response.info.sessionID] ~= nil)
and (received_message_count[response.info.sessionID] - 1)
or 0
state.user_message_count = received_message_count
M.after_run(prompt)
end)
:catch(function(err)
vim.notify('Error sending message to session: ' .. vim.inspect(err), vim.log.levels.ERROR)
M.cancel()
end)
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.
|
Hmm, the UI panels open faster but then I still get a noticeable blocking pause after the panels appear: I think the underlying issues is the opencode.nvim/lua/opencode/server_job.lua Lines 134 to 155 in 9223b98
Instead of doing |
I will see what I can do. But even then until the server is loaded you cannot display most of the UI elements. |
e36bb46 to
2bfcb8f
Compare
|
I pushed a couple of improvements and removed the :wait() It improved a little bit, there is no jumping of the cursor, but we still have to wait for the session to be loaded. Which in itself is un-avoidable. Screen.Recording.2025-12-05.091554.mp4 |
|
It's improving but I still get a UI freeze. I think the core problem is that I added a To fully fix it, we'll have to update those places to not use wait in the common path so the nvim UI is never blocked. |
|
Good point, I think the "real" way of doing it would be to have a promise based on coroutines.. but it would require a bigger refactor. I will check the usages of :wait and see if it's possible to continue without blocking the ui or to manage the promise. |
|
@cameronr I had a look at eliminating the :wait on the critical path. The isssue I have is that promises are spreading and need to be changed everywhere. I gave up after a couple of hours. If you want to have a jab at it don't hesitate. I will try to find another solution to avoid calling :wait. |
Yes, I think you're right. I'll think about it a bit and see if I can come up with anything but I suspect it would involve refactoring most of the api to take call backs rather than executing synchronously. |
49dd82c to
bc85505
Compare
|
Just so you know I'm in the middle of the process of converting every promise into a coroutine. It will take a moment, but I think it will improve and cleanup a lot of stuff. |
|
oh great! switching to coroutines was what i was going to explore so that sounds good! |
9a67f66 to
4c8299a
Compare
This uses coroutines for managing promises. The sync :wait method is still used in some not critical parts where it's not obvious to change to the await method
4c8299a to
96e9f2d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 32 out of 32 changed files in this pull request and generated 11 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
0771d43 to
9a207ac
Compare
9a207ac to
e092046
Compare
|
Ooh, exciting! I'll take a look today! |
|
I've started looking into this and you really have made great progress! I did notice that there's still a small delay when opening the panels and I traced it down to ❯ time opencode --version
1.0.138
opencode --version 0.26s user 0.03s system 50% cpu 0.577 totalRather than using -- this could be in Promise or util
local function system_async(cmd, opts)
local p = Promise.new()
vim.system(cmd, opts or {}, function(result)
if result.code == 0 then
p:resolve(result)
else
p:reject(result)
end
end)
return p
end
M.opencode_ok = Promise.async(function()
if vim.fn.executable('opencode') == 0 then
vim.notify(
'opencode command not found - please install and configure opencode before using this plugin',
vim.log.levels.ERROR
)
return false
end
vim.notify('alive')
if not state.opencode_cli_version or state.opencode_cli_version == '' then
local result = system_async({ 'opencode', '--version' }):await()
-- can test with something like:
-- local result = system_async({ 'sleep', '3' }):await()
local out = (result and result.stdout or ''):gsub('%s+$', '')
state.opencode_cli_version = out:match('(%d+%%.%d+%%.%d+)') or out
vim.notify('done: ' .. tostring(state.opencode_cli_version))
end
...
end)Let me know if it's ok to make that change. I'll also keep looking at the changes. |
|
Great catch. Yes I am totally fine with this change Can you run 'time opencode --version' on your system. For me I don't see any significant delay But the change should help anyway |
|
I had already included the time results in my previous comment :) Takes about 270ms:
I'll make that change. |
Caused by vim.system():wait() delay when checking opencode version. Fixed by adding Promise.system() which wraps vim.system in a promise (using vim.system's callback mechanism)
Also fix type in Promise
I was on mobile and didn't see it 😂 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file is not used yet,
It ended up here by error, but I plan to use it in a near feature for the completion bug.
cameronr
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great, only noticed some minor things.
Also, make commands_list async


This PR is removing the :wait on the critical paths in favor of a coroutine similar to how promises work in js.
This should resolve #137 and #133