Skip to content

Commit 6eb14dc

Browse files
committed
refactor: modernize vim API usage and enhance type annotations for test compatibility
Change-Id: I5b4fbe37dc8b2a9141bd91042af73d4260efe5e1 Signed-off-by: Thomas Kosiewski <[email protected]>
1 parent b6d3d44 commit 6eb14dc

File tree

14 files changed

+419
-92
lines changed

14 files changed

+419
-92
lines changed

lua/claudecode/diff.lua

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ function M._create_temp_file(content, filename)
7575
if mkdir_ok_cache then
7676
final_base_dir = base_dir_cache
7777
else
78-
local base_dir_temp = vim.fn.stdpath("temp") .. "/claudecode_diffs"
78+
local base_dir_temp = vim.fn.stdpath("cache") .. "/claudecode_diffs_fallback"
7979
local mkdir_ok_temp, mkdir_err_temp = pcall(vim.fn.mkdir, base_dir_temp, "p")
8080
if not mkdir_ok_temp then
8181
local err_to_report = mkdir_err_temp or mkdir_err_cache or "unknown error creating base temp dir"
@@ -117,8 +117,32 @@ end
117117
function M._cleanup_temp_file(tmp_file)
118118
if tmp_file and vim.fn.filereadable(tmp_file) == 1 then
119119
local tmp_dir = vim.fn.fnamemodify(tmp_file, ":h")
120-
vim.fn.delete(tmp_file)
121-
vim.fn.delete(tmp_dir, "d")
120+
if vim.fs and type(vim.fs.remove) == "function" then
121+
local ok_file, err_file = pcall(vim.fs.remove, tmp_file)
122+
if not ok_file then
123+
vim.notify("ClaudeCode: Error removing temp file " .. tmp_file .. ": " .. tostring(err_file), vim.log.levels.WARN)
124+
end
125+
126+
local ok_dir, err_dir = pcall(vim.fs.remove, tmp_dir)
127+
if not ok_dir then
128+
vim.notify("ClaudeCode: Error removing temp directory " .. tmp_dir .. ": " .. tostring(err_dir), vim.log.levels.INFO)
129+
end
130+
else
131+
local reason = "vim.fs.remove is not a function"
132+
if not vim.fs then
133+
reason = "vim.fs is nil"
134+
end
135+
vim.notify(
136+
"ClaudeCode: Cannot perform standard cleanup: " .. reason .. ". Affected file: " .. tmp_file ..
137+
". Please check your Neovim setup or report this issue.",
138+
vim.log.levels.ERROR
139+
)
140+
-- Fallback to os.remove for the file.
141+
local os_ok, os_err = pcall(os.remove, tmp_file)
142+
if not os_ok then
143+
vim.notify("ClaudeCode: Fallback os.remove also failed for file " .. tmp_file .. ": " .. tostring(os_err), vim.log.levels.ERROR)
144+
end
145+
end
122146
end
123147
end
124148

lua/claudecode/init.lua

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ M.version = {
3636
--- @field terminal_cmd string|nil Custom terminal command to use when launching Claude.
3737
--- @field log_level "trace"|"debug"|"info"|"warn"|"error" Log level.
3838
--- @field track_selection boolean Enable sending selection updates to Claude.
39+
--- @field visual_demotion_delay_ms number Milliseconds to wait before demoting a visual selection.
40+
--- @field diff_provider "auto"|"diffview"|"native" The diff provider to use.
41+
--- @field diff_opts { auto_close_on_accept: boolean, show_diff_stats: boolean, vertical_split: boolean, open_in_current_tab: boolean } Options for the diff provider.
3942

4043
--- @type ClaudeCode.Config
4144
local default_config = {
@@ -44,6 +47,14 @@ local default_config = {
4447
terminal_cmd = nil,
4548
log_level = "info",
4649
track_selection = true,
50+
visual_demotion_delay_ms = 200,
51+
diff_provider = "auto",
52+
diff_opts = {
53+
auto_close_on_accept = true,
54+
show_diff_stats = true,
55+
vertical_split = true,
56+
open_in_current_tab = false,
57+
},
4758
}
4859

4960
--- @class ClaudeCode.State
@@ -137,7 +148,7 @@ function M.start(show_startup_notification)
137148
return false, "Already running"
138149
end
139150

140-
local server = require("claudecode.server")
151+
local server = require("claudecode.server.init")
141152
local success, result = server.start(M.state.config)
142153

143154
if not success then
@@ -162,7 +173,7 @@ function M.start(show_startup_notification)
162173

163174
if M.state.config.track_selection then
164175
local selection = require("claudecode.selection")
165-
selection.enable(server, M.state.config.visual_demotion_delay_ms)
176+
selection.enable(M.state.server, M.state.config.visual_demotion_delay_ms)
166177
end
167178

168179
if show_startup_notification then
@@ -245,7 +256,7 @@ function M._create_commands()
245256
logger.debug(
246257
"command",
247258
"ClaudeCodeSend (new logic) invoked. Mode: "
248-
.. vim.fn.mode(1)
259+
.. vim.fn.mode(true)
249260
.. ", Neovim's reported range: "
250261
.. tostring(opts and opts.range)
251262
)

lua/claudecode/meta/vim.lua

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,14 @@
2626
---@field on_close_timeout number|nil
2727

2828
---@class vim_options_table: table<string, any>
29+
---@field columns number Global option: width of the screen
30+
---@field lines number Global option: height of the screen
31+
-- Add other commonly used vim.o options as needed
2932

3033
---@class vim_buffer_options_table: table<string, any>
3134

3235
---@class vim_bo_proxy: vim_buffer_options_table
33-
---@operator #index(bufnr: number): vim_buffer_options_table Allows vim.bo[bufnr]
36+
---@field __index fun(self: vim_bo_proxy, bufnr: number): vim_buffer_options_table Allows vim.bo[bufnr]
3437

3538
---@class vim_diagnostic_info
3639
---@field bufnr number
@@ -47,6 +50,35 @@
4750
---@field get fun(bufnr?: number, ns_id?: number): vim_diagnostic_info[]
4851
-- Add other vim.diagnostic functions as needed, e.g., get_namespace, set, etc.
4952

53+
---@class vim_fs_module
54+
---@field remove fun(path: string, opts?: {force?: boolean, recursive?: boolean}):boolean|nil
55+
56+
---@class vim_fn_table
57+
---@field mode fun(mode_str?: string, full?: boolean|number):string
58+
---@field delete fun(name: string, flags?: string):integer For file deletion
59+
---@field filereadable fun(file: string):integer
60+
---@field fnamemodify fun(fname: string, mods: string):string
61+
---@field expand fun(str: string, ...):string|table
62+
---@field getcwd fun(winid?: number, tabnr?: number):string
63+
---@field mkdir fun(name: string, path?: string, prot?: number):integer
64+
---@field buflisted fun(bufnr: number|string):integer
65+
---@field bufname fun(expr?: number|string):string
66+
---@field bufnr fun(expr?: string|number, create?: boolean):number
67+
---@field win_getid fun(win?: number, tab?: number):number
68+
---@field win_gotoid fun(winid: number):boolean
69+
---@field line fun(expr: string, winid?: number):number
70+
---@field col fun(expr: string, winid?: number):number
71+
---@field virtcol fun(expr: string|string[], winid?: number):number|number[]
72+
---@field getpos fun(expr: string, winid?: number):number[]
73+
---@field setpos fun(expr: string, pos: number[], winid?: number):boolean
74+
---@field tempname fun():string
75+
---@field globpath fun(path: string, expr: string, ...):string
76+
---@field stdpath fun(type: "cache"|"config"|"data"|"log"|"run"|"state"|"config_dirs"|"data_dirs"):string|string[]
77+
---@field json_encode fun(expr: any):string
78+
---@field json_decode fun(string: string, opts?: {null_value?: any}):any
79+
---@field termopen fun(cmd: string|string[], opts?: table):number For vim.fn.termopen()
80+
-- Add other vim.fn functions as needed
81+
5082
---@class vim_global_api
5183
---@field notify fun(msg: string | string[], level?: number, opts?: vim_notify_opts):nil
5284
---@field log vim_log
@@ -57,8 +89,26 @@
5789
---@field diagnostic vim_diagnostic_module For vim.diagnostic.*
5890
---@field empty_dict fun(): table For vim.empty_dict()
5991
---@field schedule_wrap fun(fn: function): function For vim.schedule_wrap()
92+
---@field deepcopy fun(val: any): any For vim.deepcopy() -- Added based on test mocks
93+
---@field _current_mode string? For mocks in tests
94+
---@class vim_api_table
95+
---@field nvim_create_augroup fun(name: string, opts: {clear: boolean}):integer
96+
---@field nvim_create_autocmd fun(event: string|string[], opts: {group?: string|integer, pattern?: string|string[], buffer?: number, callback?: function|string, once?: boolean, desc?: string}):integer
97+
---@field nvim_clear_autocmds fun(opts: {group?: string|integer, event?: string|string[], pattern?: string|string[], buffer?: number}):nil
98+
---@field nvim_get_current_buf fun():integer
99+
---@field nvim_get_mode fun():{mode: string, blocking: boolean}
100+
---@field nvim_win_get_cursor fun(window: integer):integer[] Returns [row, col] (1-based for row, 0-based for col)
101+
---@field nvim_buf_get_name fun(buffer: integer):string
102+
---@field nvim_buf_get_lines fun(buffer: integer, start: integer, end_line: integer, strict_indexing: boolean):string[]
103+
-- Add other nvim_api functions as needed
104+
---@field cmd fun(command: string):nil For vim.cmd() -- Added based on test mocks
105+
---@field api vim_api_table For vim.api.*
106+
---@field fn vim_fn_table For vim.fn.*
107+
---@field fs vim_fs_module For vim.fs.*
108+
---@field test vim_test_utils? For test utility mocks
109+
---@field split fun(str: string, pat?: string, opts?: {plain?: boolean, trimempty?: boolean}):string[] For vim.split()
60110
-- Add other vim object definitions here if they cause linting issues
61-
-- e.g. vim.fn, vim.api, vim.loop, vim.deepcopy, etc.
111+
-- e.g. vim.api, vim.loop, vim.deepcopy, etc.
62112

63113
---@class SpyCall
64114
---@field vals table[] table of arguments passed to the call
@@ -83,6 +133,11 @@
83133
-- or on an object returned by `spy()`. Adjust as per your spy library's specifics.
84134
-- For busted's default spy, `calls` is often directly on the spied function.
85135

136+
---@class vim_test_utils
137+
---@field add_buffer fun(bufnr: number, filename: string, content: string|string[]):nil
138+
---@field set_cursor fun(bufnr: number, row: number, col: number):nil
139+
-- Add other test utility functions as needed
140+
86141
-- This section helps LuaLS understand that 'vim' is a global variable
87142
-- with the structure defined above. It's for type hinting only and
88143
-- does not execute or overwrite the actual 'vim' global provided by Neovim.

lua/claudecode/server/mock.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
---@brief ]]
77

88
local M = {}
9+
local tools = require("claudecode.tools.init")
910

1011
--- Mock server state
1112
M.state = {
@@ -91,7 +92,6 @@ function M.register_handlers()
9192

9293
["mcp.tool.invoke"] = function(client, params)
9394
-- Handle tool invocation by dispatching to tools implementation
94-
local tools = require("claudecode.tools")
9595
return tools.handle_invoke(client, params)
9696
end,
9797
}

lua/claudecode/terminal.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ if not snacks_available then
1414
)
1515
end
1616

17-
local claudecode_server_module = require("claudecode.server")
17+
local claudecode_server_module = require("claudecode.server.init")
1818

1919
local term_module_config = {
2020
split_side = "right",

tests/config_test.lua

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
-- Simple config module tests that don't rely on the vim API
22

33
_G.vim = { ---@type vim_global_api
4+
schedule_wrap = function(fn)
5+
return fn
6+
end,
47
deepcopy = function(t)
58
-- Basic deepcopy implementation for testing purposes
69
local copy = {}
@@ -24,7 +27,10 @@ _G.vim = { ---@type vim_global_api
2427
TRACE = 5,
2528
},
2629
},
27-
o = {}, ---@type vim_options_table
30+
o = { ---@type vim_options_table
31+
columns = 80,
32+
lines = 24,
33+
},
2834
bo = setmetatable({}, { -- Mock for vim.bo and vim.bo[bufnr]
2935
__index = function(t, k)
3036
if type(k) == "number" then
@@ -73,6 +79,34 @@ _G.vim = { ---@type vim_global_api
7379

7480
return result
7581
end,
82+
cmd = function() end, ---@type fun(command: string):nil
83+
api = {}, ---@type table
84+
fn = { ---@type vim_fn_table
85+
mode = function() return "n" end,
86+
delete = function(_, _) return 0 end,
87+
filereadable = function(_) return 1 end,
88+
fnamemodify = function(fname, _) return fname end,
89+
expand = function(s, _) return s end,
90+
getcwd = function() return "/mock/cwd" end,
91+
mkdir = function(_, _, _) return 1 end,
92+
buflisted = function(_) return 1 end,
93+
bufname = function(_) return "mockbuffer" end,
94+
bufnr = function(_) return 1 end,
95+
win_getid = function() return 1 end,
96+
win_gotoid = function(_) return true end,
97+
line = function(_) return 1 end,
98+
col = function(_) return 1 end,
99+
virtcol = function(_) return 1 end,
100+
getpos = function(_) return {0,1,1,0} end,
101+
setpos = function(_,_) return true end,
102+
tempname = function() return "/tmp/mocktemp" end,
103+
globpath = function(_,_) return "" end,
104+
termopen = function(_, _) return 0 end,
105+
stdpath = function(_) return "/mock/stdpath" end,
106+
json_encode = function(_) return "{}" end,
107+
json_decode = function(_) return {} end,
108+
},
109+
fs = { remove = function() end }, ---@type vim_fs_module
76110
}
77111

78112
describe("Config module", function()

0 commit comments

Comments
 (0)