Skip to content

Commit dda6dd3

Browse files
committed
improve: enhance mini.files selection and fix path extraction
- Add buffer ID to mini.files.get_fs_entry() for reliable selection - Fix path extraction from "minifiles://bufid/path" to filesystem paths - Improve visual mode multi-file selection with comprehensive test coverage - Enhance error handling and validation for visual selections - Ensure proper filetype and buffer name detection in tree logic - Add support for mini.files in is_tree_buffer detection - Resolve raw mini.files buffer issues with path extraction
1 parent 24556db commit dda6dd3

File tree

4 files changed

+204
-15
lines changed

4 files changed

+204
-15
lines changed

lua/claudecode/init.lua

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -611,8 +611,10 @@ function M._create_commands()
611611
local is_tree_buffer = current_ft == "NvimTree"
612612
or current_ft == "neo-tree"
613613
or current_ft == "oil"
614+
or current_ft == "minifiles"
614615
or string.match(current_bufname, "neo%-tree")
615616
or string.match(current_bufname, "NvimTree")
617+
or string.match(current_bufname, "minifiles://")
616618

617619
if is_tree_buffer then
618620
local integrations = require("claudecode.integrations")

lua/claudecode/integrations.lua

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,8 @@ function M._get_mini_files_selection()
276276

277277
local files = {}
278278

279+
local bufnr = vim.api.nvim_get_current_buf()
280+
279281
-- Check if we're in visual mode for multi-selection
280282
local mode = vim.fn.mode()
281283
if mode == "V" or mode == "v" or mode == "\22" then
@@ -285,11 +287,18 @@ function M._get_mini_files_selection()
285287

286288
-- Process each line in the visual selection
287289
for line = start_line, end_line do
288-
local entry_ok, entry = pcall(mini_files.get_fs_entry, nil, line)
289-
if entry_ok and entry and entry.path then
290+
local entry_ok, entry = pcall(mini_files.get_fs_entry, bufnr, line)
291+
if entry_ok and entry and entry.path and entry.path ~= "" then
292+
-- Extract real filesystem path from mini.files buffer path
293+
local real_path = entry.path
294+
-- Remove mini.files buffer protocol prefix if present
295+
if real_path:match("^minifiles://") then
296+
real_path = real_path:gsub("^minifiles://[^/]*/", "")
297+
end
298+
290299
-- Validate that the path exists
291-
if vim.fn.filereadable(entry.path) == 1 or vim.fn.isdirectory(entry.path) == 1 then
292-
table.insert(files, entry.path)
300+
if vim.fn.filereadable(real_path) == 1 or vim.fn.isdirectory(real_path) == 1 then
301+
table.insert(files, real_path)
293302
end
294303
end
295304
end
@@ -299,17 +308,24 @@ function M._get_mini_files_selection()
299308
end
300309
else
301310
-- Normal mode: get file under cursor
302-
local entry_ok, entry = pcall(mini_files.get_fs_entry)
311+
local entry_ok, entry = pcall(mini_files.get_fs_entry, bufnr)
303312
if not entry_ok or not entry then
304313
return {}, "Failed to get entry from mini.files"
305314
end
306315

307316
if entry.path and entry.path ~= "" then
317+
-- Extract real filesystem path from mini.files buffer path
318+
local real_path = entry.path
319+
-- Remove mini.files buffer protocol prefix if present
320+
if real_path:match("^minifiles://") then
321+
real_path = real_path:gsub("^minifiles://[^/]*/", "")
322+
end
323+
308324
-- Validate that the path exists
309-
if vim.fn.filereadable(entry.path) == 1 or vim.fn.isdirectory(entry.path) == 1 then
310-
return { entry.path }, nil
325+
if vim.fn.filereadable(real_path) == 1 or vim.fn.isdirectory(real_path) == 1 then
326+
return { real_path }, nil
311327
else
312-
return {}, "Invalid file or directory path: " .. entry.path
328+
return {}, "Invalid file or directory path: " .. real_path
313329
end
314330
end
315331
end

lua/claudecode/selection.lua

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -445,7 +445,13 @@ function M.get_visual_selection()
445445
local start_coords, end_coords = get_selection_coordinates()
446446

447447
local current_buf = vim.api.nvim_get_current_buf()
448-
local file_path = vim.api.nvim_buf_get_name(current_buf)
448+
local raw_file_path = vim.api.nvim_buf_get_name(current_buf)
449+
450+
-- Extract real filesystem path from mini.files buffer path if needed
451+
local file_path = raw_file_path
452+
if file_path:match("^minifiles://") then
453+
file_path = file_path:gsub("^minifiles://[^/]*/", "")
454+
end
449455

450456
local lines_content = vim.api.nvim_buf_get_lines(
451457
current_buf,
@@ -490,7 +496,13 @@ end
490496
function M.get_cursor_position()
491497
local cursor_pos = vim.api.nvim_win_get_cursor(0)
492498
local current_buf = vim.api.nvim_get_current_buf()
493-
local file_path = vim.api.nvim_buf_get_name(current_buf)
499+
local raw_file_path = vim.api.nvim_buf_get_name(current_buf)
500+
501+
-- Extract real filesystem path from mini.files buffer path if needed
502+
local file_path = raw_file_path
503+
if file_path:match("^minifiles://") then
504+
file_path = file_path:gsub("^minifiles://[^/]*/", "")
505+
end
494506

495507
return {
496508
text = "",
@@ -584,7 +596,13 @@ function M.get_range_selection(line1, line2)
584596
end
585597

586598
local current_buf = vim.api.nvim_get_current_buf()
587-
local file_path = vim.api.nvim_buf_get_name(current_buf)
599+
local raw_file_path = vim.api.nvim_buf_get_name(current_buf)
600+
601+
-- Extract real filesystem path from mini.files buffer path if needed
602+
local file_path = raw_file_path
603+
if file_path:match("^minifiles://") then
604+
file_path = file_path:gsub("^minifiles://[^/]*/", "")
605+
end
588606

589607
-- Get the total number of lines in the buffer
590608
local total_lines = vim.api.nvim_buf_line_count(current_buf)
@@ -668,7 +686,14 @@ function M.send_at_mention_for_visual_selection(line1, line2)
668686
end
669687

670688
-- Sanity check: ensure the selection is for the current buffer
671-
local current_buf_name = vim.api.nvim_buf_get_name(vim.api.nvim_get_current_buf())
689+
local raw_current_buf_name = vim.api.nvim_buf_get_name(vim.api.nvim_get_current_buf())
690+
691+
-- Extract real filesystem path from mini.files buffer path if needed
692+
local current_buf_name = raw_current_buf_name
693+
if current_buf_name:match("^minifiles://") then
694+
current_buf_name = current_buf_name:gsub("^minifiles://[^/]*/", "")
695+
end
696+
672697
if sel_to_send.filePath ~= current_buf_name then
673698
logger.warn(
674699
"selection",

tests/unit/mini_files_integration_spec.lua

Lines changed: 149 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ describe("mini.files integration", function()
4242
return 0
4343
end,
4444
},
45+
api = {
46+
nvim_get_current_buf = function()
47+
return 1 -- Mock buffer ID
48+
end
49+
},
4550
bo = { filetype = "minifiles" },
4651
}
4752

@@ -57,7 +62,11 @@ describe("mini.files integration", function()
5762
it("should get single file under cursor", function()
5863
-- Mock mini.files module
5964
local mock_mini_files = {
60-
get_fs_entry = function()
65+
get_fs_entry = function(buf_id)
66+
-- Verify buffer ID is passed correctly
67+
if buf_id ~= 1 then
68+
return nil
69+
end
6170
return { path = "/Users/test/project/main.lua" }
6271
end,
6372
}
@@ -74,7 +83,11 @@ describe("mini.files integration", function()
7483
it("should get directory under cursor", function()
7584
-- Mock mini.files module
7685
local mock_mini_files = {
77-
get_fs_entry = function()
86+
get_fs_entry = function(buf_id)
87+
-- Verify buffer ID is passed correctly
88+
if buf_id ~= 1 then
89+
return nil
90+
end
7891
return { path = "/Users/test/project/src" }
7992
end,
8093
}
@@ -88,6 +101,89 @@ describe("mini.files integration", function()
88101
expect(files[1]).to_be("/Users/test/project/src")
89102
end)
90103

104+
it("should handle mini.files buffer path format", function()
105+
-- Mock mini.files module that returns buffer-style paths
106+
local mock_mini_files = {
107+
get_fs_entry = function(buf_id)
108+
if buf_id ~= 1 then
109+
return nil
110+
end
111+
return { path = "minifiles://42//Users/test/project/buffer_file.lua" }
112+
end,
113+
}
114+
package.loaded["mini.files"] = mock_mini_files
115+
116+
local files, err = integrations._get_mini_files_selection()
117+
118+
expect(err).to_be_nil()
119+
expect(files).to_be_table()
120+
expect(#files).to_be(1)
121+
expect(files[1]).to_be("/Users/test/project/buffer_file.lua")
122+
end)
123+
124+
it("should handle various mini.files buffer path formats", function()
125+
-- Test different buffer path formats that could occur
126+
local test_cases = {
127+
{ input = "minifiles://42/Users/test/file.lua", expected = "Users/test/file.lua" },
128+
{ input = "minifiles://42//Users/test/file.lua", expected = "/Users/test/file.lua" },
129+
{ input = "minifiles://123///Users/test/file.lua", expected = "//Users/test/file.lua" },
130+
{ input = "/Users/test/normal_path.lua", expected = "/Users/test/normal_path.lua" },
131+
}
132+
133+
for i, test_case in ipairs(test_cases) do
134+
local mock_mini_files = {
135+
get_fs_entry = function(buf_id)
136+
if buf_id ~= 1 then
137+
return nil
138+
end
139+
return { path = test_case.input }
140+
end,
141+
}
142+
package.loaded["mini.files"] = mock_mini_files
143+
144+
local files, err = integrations._get_mini_files_selection()
145+
146+
expect(err).to_be_nil()
147+
expect(files).to_be_table()
148+
expect(#files).to_be(1)
149+
expect(files[1]).to_be(test_case.expected)
150+
end
151+
end)
152+
153+
it("should handle mini.files buffer paths in visual mode", function()
154+
mock_vim.fn.mode = function()
155+
return "V" -- Visual line mode
156+
end
157+
158+
-- Mock mini.files module that returns buffer-style paths
159+
local mock_mini_files = {
160+
get_fs_entry = function(buf_id, line)
161+
if buf_id ~= 1 then
162+
return nil
163+
end
164+
165+
if line == 1 then
166+
return { path = "minifiles://42//Users/test/project/visual1.lua" }
167+
elseif line == 2 then
168+
return { path = "minifiles://42//Users/test/project/visual2.txt" }
169+
elseif line == 3 then
170+
return { path = "minifiles://42//Users/test/project/src" }
171+
end
172+
return nil
173+
end,
174+
}
175+
package.loaded["mini.files"] = mock_mini_files
176+
177+
local files, err = integrations._get_mini_files_selection()
178+
179+
expect(err).to_be_nil()
180+
expect(files).to_be_table()
181+
expect(#files).to_be(3)
182+
expect(files[1]).to_be("/Users/test/project/visual1.lua")
183+
expect(files[2]).to_be("/Users/test/project/visual2.txt")
184+
expect(files[3]).to_be("/Users/test/project/src")
185+
end)
186+
91187
it("should get multiple files in visual mode", function()
92188
mock_vim.fn.mode = function()
93189
return "V" -- Visual line mode
@@ -96,6 +192,11 @@ describe("mini.files integration", function()
96192
-- Mock mini.files module
97193
local mock_mini_files = {
98194
get_fs_entry = function(buf_id, line)
195+
-- Verify buffer ID is passed correctly
196+
if buf_id ~= 1 then
197+
return nil
198+
end
199+
99200
if line == 1 then
100201
return { path = "/Users/test/project/file1.lua" }
101202
elseif line == 2 then
@@ -147,6 +248,51 @@ describe("mini.files integration", function()
147248
expect(files[2]).to_be("/Users/test/project/src")
148249
end)
149250

251+
it("should handle visual mode with buffer ID correctly", function()
252+
mock_vim.fn.mode = function()
253+
return "v" -- Visual character mode
254+
end
255+
256+
-- Mock different buffer ID to test parameter passing
257+
mock_vim.api.nvim_get_current_buf = function()
258+
return 42 -- Different buffer ID
259+
end
260+
261+
-- Mock mini.files module that validates buffer ID
262+
local mock_mini_files = {
263+
get_fs_entry = function(buf_id, line)
264+
-- Should receive buffer ID 42
265+
if buf_id ~= 42 then
266+
error("Expected buffer ID 42, got " .. tostring(buf_id))
267+
end
268+
269+
if line == 1 then
270+
return { path = "/Users/test/project/selected1.lua" }
271+
elseif line == 2 then
272+
return { path = "/Users/test/project/selected2.txt" }
273+
elseif line == 3 then
274+
return { path = "/Users/test/project/selected3.lua" }
275+
end
276+
return nil
277+
end,
278+
}
279+
package.loaded["mini.files"] = mock_mini_files
280+
281+
local files, err = integrations._get_mini_files_selection()
282+
283+
expect(err).to_be_nil()
284+
expect(files).to_be_table()
285+
expect(#files).to_be(3)
286+
expect(files[1]).to_be("/Users/test/project/selected1.lua")
287+
expect(files[2]).to_be("/Users/test/project/selected2.txt")
288+
expect(files[3]).to_be("/Users/test/project/selected3.lua")
289+
290+
-- Reset buffer ID for other tests
291+
mock_vim.api.nvim_get_current_buf = function()
292+
return 1
293+
end
294+
end)
295+
150296
it("should handle empty entry under cursor", function()
151297
-- Mock mini.files module
152298
local mock_mini_files = {
@@ -279,4 +425,4 @@ describe("mini.files integration", function()
279425
expect(files).to_be_nil()
280426
end)
281427
end)
282-
end)
428+
end)

0 commit comments

Comments
 (0)