diff options
| author | Jackson Moore <jacksonmoore@tuta.io> | 2026-09-04 14:27:24 +0200 |
|---|---|---|
| committer | Jackson Moore <jacksonmoore@tuta.io> | 2026-09-04 14:27:24 +0200 |
| commit | 890cedb3003d45ded2327e483675c42fdfe024f1 (patch) | |
| tree | 0e7334ba15c6419bf7b7b4fda1ce145c6ee778c6 | |
| parent | ae03a565105416f52a09bc0a171a7fdbb9ced5a0 (diff) | |
Preserve cursor presentation state
| -rw-r--r-- | lua/clever_f/host_adapter.lua | 72 | ||||
| -rw-r--r-- | tests/run.lua | 54 |
2 files changed, 126 insertions, 0 deletions
diff --git a/lua/clever_f/host_adapter.lua b/lua/clever_f/host_adapter.lua index ab3467c..51591a9 100644 --- a/lua/clever_f/host_adapter.lua +++ b/lua/clever_f/host_adapter.lua @@ -45,6 +45,7 @@ function HostAdapter.new(options) next_identity = 1, highlights = {}, timers = {}, + cursor_presentations = {}, events = {}, event_order = {}, augroup = nil, @@ -417,6 +418,77 @@ function HostAdapter:commit_action_transition(token) return adapter_records[self].event_queue:commit_transition(token) end +local function terminal_cursor_option(runtime) + return runtime.fn.eval("&t_ve") +end + +local function set_terminal_cursor_option(runtime, value) + runtime.api.nvim_cmd({ + cmd = "let", + args = { "&t_ve", "=", runtime.fn.string(value) }, + }, {}) +end + +function HostAdapter:supports_cursor_presentation() + local runtime = self:runtime() + return type(runtime.api.nvim_get_option_value) == "function" + and type(runtime.api.nvim_set_option_value) == "function" + and type(runtime.api.nvim_cmd) == "function" + and type(runtime.fn) == "table" + and type(runtime.fn.exists) == "function" + and runtime.fn.exists("+t_ve") == 1 + and type(runtime.fn.eval) == "function" + and type(runtime.fn.string) == "function" +end + +function HostAdapter:suppress_cursor_presentation() + if not self:supports_cursor_presentation() then + return nil + end + local record = adapter_records[self] + local runtime = record.runtime + local identity = next_identity(self, "cursor-presentation") + local saved = { + guicursor = runtime.api.nvim_get_option_value( + "guicursor", + { scope = "global" } + ), + terminal_cursor = terminal_cursor_option(runtime), + } + runtime.api.nvim_set_option_value( + "guicursor", + "a:ver1", + { scope = "global" } + ) + local ok, failure = pcall(set_terminal_cursor_option, runtime, "") + if not ok then + runtime.api.nvim_set_option_value( + "guicursor", + saved.guicursor, + { scope = "global" } + ) + error(failure, 0) + end + record.cursor_presentations[identity] = saved + return identity +end + +function HostAdapter:restore_cursor_presentation(identity) + local record = adapter_records[self] + local saved = record.cursor_presentations[identity] + if saved == nil then + return false + end + record.cursor_presentations[identity] = nil + record.runtime.api.nvim_set_option_value( + "guicursor", + saved.guicursor, + { scope = "global" } + ) + set_terminal_cursor_option(record.runtime, saved.terminal_cursor) + return true +end + local function escape_key(runtime) if type(runtime.keycode) == "function" then return runtime.keycode("<Esc>") diff --git a/tests/run.lua b/tests/run.lua index e0c9b0c..5ca4f34 100644 --- a/tests/run.lua +++ b/tests/run.lua @@ -361,6 +361,60 @@ test("ActionOutcome and DotPayload retain resolved result data", function() same("problem", domain.ActionOutcome.error(destination, "problem").diagnostic) end) +test("Host adapter restores exact cursor presentation values", function() + local options = { + guicursor = "n:block,i:ver37-blinkon123", + t_ve = "terminal-visible-sequence", + } + local writes = {} + local runtime = { + api = { + nvim_get_option_value = function(name, request) + same("global", request.scope) + return options[name] + end, + nvim_set_option_value = function(name, value, request) + same("global", request.scope) + options[name] = value + writes[#writes + 1] = { name = name, value = value } + end, + nvim_cmd = function(command) + same("let", command.cmd) + same("&t_ve", command.args[1]) + same("=", command.args[2]) + options.t_ve = command.args[3] + writes[#writes + 1] = { name = "t_ve", value = command.args[3] } + end, + }, + fn = { + exists = function(name) + same("+t_ve", name) + return 1 + end, + eval = function(name) + same("&t_ve", name) + return options.t_ve + end, + string = function(value) + return value + end, + }, + } + local adapter = host_adapter.new({ runtime = runtime }) + truthy(adapter:supports_cursor_presentation()) + + local lease = adapter:suppress_cursor_presentation() + truthy(lease ~= nil) + same("a:ver1", options.guicursor) + same("", options.t_ve) + + truthy(adapter:restore_cursor_presentation(lease)) + same("n:block,i:ver37-blinkon123", options.guicursor) + same("terminal-visible-sequence", options.t_ve) + falsy(adapter:restore_cursor_presentation(lease)) + same(4, #writes) +end) + test("Host adapter owns Nvim timers and event registrations", function() local timer_callbacks = {} local stopped_timers = {} |
