Skip to content

Commit 73b4241

Browse files
committed
refactor: extract directory change handler to core module
1 parent 09b6f37 commit 73b4241

3 files changed

Lines changed: 156 additions & 17 deletions

File tree

lua/opencode/core.lua

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,33 @@ function M.paste_image_from_clipboard()
543543
return image_handler.paste_image_from_clipboard()
544544
end
545545

546+
--- Handle working directory changes by restarting the server and loading the appropriate session.
547+
--- This function performs the following steps:
548+
--- 1. Shuts down the existing opencode server
549+
--- 2. Starts a new server instance
550+
--- 3. Clears the active session and context
551+
--- 4. Loads the last workspace session for the new directory, or creates a new one if none exists
552+
--- @return Promise<void>
553+
M.handle_directory_change = Promise.async(function()
554+
local log = require('opencode.log')
555+
556+
if state.opencode_server then
557+
vim.notify('Directory changed, restarting Opencode server...', vim.log.levels.INFO)
558+
log.info('Shutting down Opencode server due to directory change...')
559+
state.opencode_server:shutdown():await()
560+
server_job.ensure_server():await()
561+
state.active_session = nil
562+
vim.notify('Loading last session for new working dir', vim.log.levels.INFO)
563+
state.last_sent_context = nil
564+
context.unload_attachments()
565+
566+
state.active_session = session.get_last_workspace_session():await()
567+
if not state.active_session then
568+
state.active_session = M.create_new_session():await()
569+
end
570+
end
571+
end)
572+
546573
function M.setup()
547574
state.subscribe('opencode_server', on_opencode_server)
548575
state.subscribe('user_message_count', M._on_user_message_count_change)

lua/opencode/ui/autocmds.lua

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -52,24 +52,8 @@ function M.setup_autocmds(windows)
5252
vim.api.nvim_create_autocmd('DirChanged', {
5353
group = group,
5454
callback = Promise.async(function(event)
55-
local log = require('opencode.log')
56-
local state = require('opencode.state')
57-
local server_job = require('opencode.server_job')
58-
local session = require('opencode.session')
5955
local core = require('opencode.core')
60-
61-
if state.opencode_server then
62-
vim.notify('Directory changed, restarting Opencode server...', vim.log.levels.INFO)
63-
log.info('Shutting down Opencode server due to directory change...')
64-
state.opencode_server:shutdown():await()
65-
server_job.ensure_server():await()
66-
state.active_session = nil
67-
vim.notify('Loading last session for new working dir', vim.log.levels.INFO)
68-
state.active_session = session.get_last_workspace_session():await()
69-
if not state.active_session then
70-
state.active_session = core.create_new_session():await()
71-
end
72-
end
56+
core.handle_directory_change():await()
7357
end),
7458
})
7559

tests/unit/core_spec.lua

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,134 @@ describe('opencode.core', function()
469469
end)
470470
end)
471471

472+
describe('handle_directory_change', function()
473+
local server_job
474+
local context
475+
476+
before_each(function()
477+
server_job = require('opencode.server_job')
478+
context = require('opencode.context')
479+
stub(server_job, 'ensure_server').invokes(function()
480+
local p = Promise.new()
481+
p:resolve({
482+
is_running = function()
483+
return true
484+
end,
485+
shutdown = function()
486+
return Promise.new():resolve()
487+
end,
488+
url = 'http://127.0.0.1:4000',
489+
})
490+
return p
491+
end)
492+
stub(context, 'unload_attachments')
493+
end)
494+
495+
after_each(function()
496+
if server_job.ensure_server.revert then
497+
server_job.ensure_server:revert()
498+
end
499+
if context.unload_attachments.revert then
500+
context.unload_attachments:revert()
501+
end
502+
end)
503+
504+
it('does nothing when no server is running', function()
505+
state.opencode_server = nil
506+
state.active_session = { id = 'sess1' }
507+
508+
core.handle_directory_change():wait()
509+
510+
assert.is_nil(state.opencode_server)
511+
assert.equal('sess1', state.active_session.id)
512+
assert.stub(server_job.ensure_server).was_not_called()
513+
end)
514+
515+
it('shuts down existing server and starts new one', function()
516+
local shutdown_called = false
517+
state.opencode_server = {
518+
is_running = function()
519+
return true
520+
end,
521+
shutdown = function()
522+
shutdown_called = true
523+
return Promise.new():resolve()
524+
end,
525+
url = 'http://127.0.0.1:4000',
526+
}
527+
528+
core.handle_directory_change():wait()
529+
530+
assert.is_true(shutdown_called)
531+
assert.stub(server_job.ensure_server).was_called()
532+
end)
533+
534+
it('clears active session and context', function()
535+
state.opencode_server = {
536+
is_running = function()
537+
return true
538+
end,
539+
shutdown = function()
540+
return Promise.new():resolve()
541+
end,
542+
url = 'http://127.0.0.1:4000',
543+
}
544+
state.active_session = { id = 'old-session' }
545+
state.last_sent_context = { some = 'context' }
546+
547+
core.handle_directory_change():wait()
548+
549+
-- Should be set to the new session from get_last_workspace_session stub
550+
assert.truthy(state.active_session)
551+
assert.equal('test-session', state.active_session.id)
552+
assert.is_nil(state.last_sent_context)
553+
assert.stub(context.unload_attachments).was_called()
554+
end)
555+
556+
it('loads last workspace session for new directory', function()
557+
state.opencode_server = {
558+
is_running = function()
559+
return true
560+
end,
561+
shutdown = function()
562+
return Promise.new():resolve()
563+
end,
564+
url = 'http://127.0.0.1:4000',
565+
}
566+
567+
core.handle_directory_change():wait()
568+
569+
assert.truthy(state.active_session)
570+
assert.equal('test-session', state.active_session.id)
571+
assert.stub(session.get_last_workspace_session).was_called()
572+
end)
573+
574+
it('creates new session when no last session exists', function()
575+
state.opencode_server = {
576+
is_running = function()
577+
return true
578+
end,
579+
shutdown = function()
580+
return Promise.new():resolve()
581+
end,
582+
url = 'http://127.0.0.1:4000',
583+
}
584+
585+
-- Override stub to return nil (no last session)
586+
session.get_last_workspace_session:revert()
587+
stub(session, 'get_last_workspace_session').invokes(function()
588+
local p = Promise.new()
589+
p:resolve(nil)
590+
return p
591+
end)
592+
593+
core.handle_directory_change():wait()
594+
595+
assert.truthy(state.active_session)
596+
assert.truthy(state.active_session.id)
597+
end)
598+
end)
599+
472600
describe('switch_to_mode', function()
473601
it('sets current model from config file when mode has a model configured', function()
474602
local Promise = require('opencode.promise')

0 commit comments

Comments
 (0)