Skip to content

feat: add mini.files support #89

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

Merged
merged 5 commits into from
Jul 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ source fixtures/nvim-aliases.sh
vv nvim-tree # Test with nvim-tree integration
vv oil # Test with oil.nvim integration
vv netrw # Test with built-in netrw
vv mini-files # Test with built-in mini.files

# Each fixture provides:
# - Complete Neovim configuration
Expand Down
1 change: 1 addition & 0 deletions DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ claudecode.nvim/
├── .github/workflows/ # CI workflow definitions
├── fixtures/ # Test Neovim configurations for integration testing
│ ├── bin/ # Helper scripts (vv, vve, list-configs)
│ ├── mini-files/ # Neovim config testing with mini.files
│ ├── netrw/ # Neovim config testing with built-in file explorer
│ ├── nvim-tree/ # Neovim config testing with nvim-tree.lua
│ ├── oil/ # Neovim config testing with oil.nvim
Expand Down
1 change: 1 addition & 0 deletions fixtures/mini-files/init.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
require("config.lazy")
5 changes: 5 additions & 0 deletions fixtures/mini-files/lazy-lock.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"lazy.nvim": { "branch": "main", "commit": "6c3bda4aca61a13a9c63f1c1d1b16b9d3be90d7a" },
"mini.files": { "branch": "main", "commit": "5b9431cf5c69b8e69e5a67d2d12338a3ac2e1541" },
"tokyonight.nvim": { "branch": "main", "commit": "057ef5d260c1931f1dffd0f052c685dcd14100a3" }
}
41 changes: 41 additions & 0 deletions fixtures/mini-files/lua/config/lazy.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
-- Bootstrap lazy.nvim
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not (vim.uv or vim.loop).fs_stat(lazypath) then
local lazyrepo = "https://github.com/folke/lazy.nvim.git"
local out = vim.fn.system({ "git", "clone", "--filter=blob:none", "--branch=stable", lazyrepo, lazypath })
if vim.v.shell_error ~= 0 then
vim.api.nvim_echo({
{ "Failed to clone lazy.nvim:\n", "ErrorMsg" },
{ out, "WarningMsg" },
{ "\nPress any key to exit..." },
}, true, {})
vim.fn.getchar()
os.exit(1)
end
end
vim.opt.rtp:prepend(lazypath)

-- Make sure to setup `mapleader` and `maplocalleader` before
-- loading lazy.nvim so that mappings are correct.
-- This is also a good place to setup other settings (vim.opt)
vim.g.mapleader = " "
vim.g.maplocalleader = "\\"

-- Setup lazy.nvim
require("lazy").setup({
spec = {
-- import your plugins
{ import = "plugins" },
},
-- Configure any other settings here. See the documentation for more details.
-- colorscheme that will be used when installing plugins.
install = { colorscheme = { "habamax" } },
-- automatically check for plugin updates
checker = { enabled = true },
})

-- Add keybind for Lazy plugin manager
vim.keymap.set("n", "<leader>l", "<cmd>Lazy<cr>", { desc = "Lazy Plugin Manager" })

-- Terminal keybindings
vim.keymap.set("t", "<Esc><Esc>", "<C-\\><C-n>", { desc = "Exit terminal mode (double esc)" })
1 change: 1 addition & 0 deletions fixtures/mini-files/lua/plugins/dev-claudecode.lua
12 changes: 12 additions & 0 deletions fixtures/mini-files/lua/plugins/init.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
-- Basic plugin configuration
return {
-- Example: add a colorscheme
{
"folke/tokyonight.nvim",
lazy = false,
priority = 1000,
config = function()
vim.cmd([[colorscheme tokyonight]])
end,
},
}
176 changes: 176 additions & 0 deletions fixtures/mini-files/lua/plugins/mini-files.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
return {
"echasnovski/mini.files",
version = false,
config = function()
require("mini.files").setup({
-- Customization of shown content
content = {
-- Predicate for which file system entries to show
filter = nil,
-- What prefix to show to the left of file system entry
prefix = nil,
-- In which order to show file system entries
sort = nil,
},

-- Module mappings created only inside explorer.
-- Use `''` (empty string) to not create one.
mappings = {
close = "q",
go_in = "l",
go_in_plus = "L",
go_out = "h",
go_out_plus = "H",
reset = "<BS>",
reveal_cwd = "@",
show_help = "g?",
synchronize = "=",
trim_left = "<",
trim_right = ">",
},

-- General options
options = {
-- Whether to delete permanently or move into module-specific trash
permanent_delete = true,
-- Whether to use for editing directories
use_as_default_explorer = true,
},

-- Customization of explorer windows
windows = {
-- Maximum number of windows to show side by side
max_number = math.huge,
-- Whether to show preview of file/directory under cursor
preview = false,
-- Width of focused window
width_focus = 50,
-- Width of non-focused window
width_nofocus = 15,
-- Width of preview window
width_preview = 25,
},
})

-- Global keybindings for mini.files
vim.keymap.set("n", "<leader>e", function()
require("mini.files").open()
end, { desc = "Open mini.files (current dir)" })

vim.keymap.set("n", "<leader>E", function()
require("mini.files").open(vim.api.nvim_buf_get_name(0))
end, { desc = "Open mini.files (current file)" })

vim.keymap.set("n", "-", function()
require("mini.files").open()
end, { desc = "Open parent directory" })

-- Mini.files specific keybindings and autocommands
vim.api.nvim_create_autocmd("User", {
pattern = "MiniFilesBufferCreate",
callback = function(args)
local buf_id = args.data.buf_id

-- Add buffer-local keybindings
vim.keymap.set("n", "<C-s>", function()
-- Split window and open file
local cur_target = require("mini.files").get_fs_entry()
if cur_target and cur_target.fs_type == "file" then
require("mini.files").close()
vim.cmd("split " .. cur_target.path)
end
end, { buffer = buf_id, desc = "Split and open file" })

vim.keymap.set("n", "<C-v>", function()
-- Vertical split and open file
local cur_target = require("mini.files").get_fs_entry()
if cur_target and cur_target.fs_type == "file" then
require("mini.files").close()
vim.cmd("vsplit " .. cur_target.path)
end
end, { buffer = buf_id, desc = "Vertical split and open file" })

vim.keymap.set("n", "<C-t>", function()
-- Open in new tab
local cur_target = require("mini.files").get_fs_entry()
if cur_target and cur_target.fs_type == "file" then
require("mini.files").close()
vim.cmd("tabnew " .. cur_target.path)
end
end, { buffer = buf_id, desc = "Open in new tab" })

-- Create new file/directory
vim.keymap.set("n", "a", function()
local cur_target = require("mini.files").get_fs_entry()
local path = cur_target and cur_target.path or require("mini.files").get_explorer_state().cwd
local new_name = vim.fn.input("Create: " .. path .. "/")
if new_name and new_name ~= "" then
if new_name:sub(-1) == "/" then
-- Create directory
vim.fn.mkdir(path .. "/" .. new_name, "p")
else
-- Create file
local new_file = io.open(path .. "/" .. new_name, "w")
if new_file then
new_file:close()
end
end
require("mini.files").refresh()
end
end, { buffer = buf_id, desc = "Create new file/directory" })

-- Rename file/directory
vim.keymap.set("n", "r", function()
local cur_target = require("mini.files").get_fs_entry()
if cur_target then
local old_name = vim.fn.fnamemodify(cur_target.path, ":t")
local new_name = vim.fn.input("Rename to: ", old_name)
if new_name and new_name ~= "" and new_name ~= old_name then
local new_path = vim.fn.fnamemodify(cur_target.path, ":h") .. "/" .. new_name
os.rename(cur_target.path, new_path)
require("mini.files").refresh()
end
end
end, { buffer = buf_id, desc = "Rename file/directory" })

-- Delete file/directory
vim.keymap.set("n", "d", function()
local cur_target = require("mini.files").get_fs_entry()
if cur_target then
local confirm = vim.fn.confirm("Delete " .. cur_target.path .. "?", "&Yes\n&No", 2)
if confirm == 1 then
if cur_target.fs_type == "directory" then
vim.fn.delete(cur_target.path, "rf")
else
vim.fn.delete(cur_target.path)
end
require("mini.files").refresh()
end
end
end, { buffer = buf_id, desc = "Delete file/directory" })
end,
})

-- Auto-close mini.files when it's the last window
vim.api.nvim_create_autocmd("User", {
pattern = "MiniFilesBufferUpdate",
callback = function()
if vim.bo.filetype == "minifiles" then
-- Check if this is the only window left
local windows = vim.api.nvim_list_wins()
local minifiles_windows = 0
for _, win in ipairs(windows) do
local buf = vim.api.nvim_win_get_buf(win)
if vim.api.nvim_buf_get_option(buf, "filetype") == "minifiles" then
minifiles_windows = minifiles_windows + 1
end
end

if #windows == minifiles_windows then
vim.cmd("quit")
end
end
end,
})
end,
}
4 changes: 2 additions & 2 deletions lua/claudecode/diff.lua
Original file line number Diff line number Diff line change
Expand Up @@ -770,8 +770,8 @@ function M.open_diff_blocking(old_file_path, new_file_path, new_file_contents, t
end

-- Set up blocking diff operation
local co = coroutine.running()
if not co then
local co, is_main = coroutine.running()
if not co or is_main then
error({
code = -32000,
message = "Internal server error",
Expand Down
50 changes: 49 additions & 1 deletion lua/claudecode/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -614,8 +614,10 @@ function M._create_commands()
local is_tree_buffer = current_ft == "NvimTree"
or current_ft == "neo-tree"
or current_ft == "oil"
or current_ft == "minifiles"
or string.match(current_bufname, "neo%-tree")
or string.match(current_bufname, "NvimTree")
or string.match(current_bufname, "minifiles://")

if is_tree_buffer then
local integrations = require("claudecode.integrations")
Expand Down Expand Up @@ -659,7 +661,53 @@ function M._create_commands()
end

local function handle_send_visual(visual_data, _opts)
-- Try tree file selection first
-- Check if we're in a tree buffer first
local current_ft = (vim.bo and vim.bo.filetype) or ""
local current_bufname = (vim.api and vim.api.nvim_buf_get_name and vim.api.nvim_buf_get_name(0)) or ""

local is_tree_buffer = current_ft == "NvimTree"
or current_ft == "neo-tree"
or current_ft == "oil"
or current_ft == "minifiles"
or string.match(current_bufname, "neo%-tree")
or string.match(current_bufname, "NvimTree")
or string.match(current_bufname, "minifiles://")

if is_tree_buffer then
local integrations = require("claudecode.integrations")
local files, error

-- For mini.files, try to get the range from visual marks
if current_ft == "minifiles" or string.match(current_bufname, "minifiles://") then
local start_line = vim.fn.line("'<")
local end_line = vim.fn.line("'>")

if start_line > 0 and end_line > 0 and start_line <= end_line then
-- Use range-based selection for mini.files
files, error = integrations._get_mini_files_selection_with_range(start_line, end_line)
else
-- Fall back to regular method
files, error = integrations.get_selected_files_from_tree()
end
else
files, error = integrations.get_selected_files_from_tree()
end

if error then
logger.error("command", "ClaudeCodeSend_visual->TreeAdd: " .. error)
return
end

if not files or #files == 0 then
logger.warn("command", "ClaudeCodeSend_visual->TreeAdd: No files selected")
return
end

add_paths_to_claude(files, { context = "ClaudeCodeSend_visual->TreeAdd" })
return
end

-- Fall back to old visual selection logic for non-tree buffers
if visual_data then
local visual_commands = require("claudecode.visual_commands")
local files, error = visual_commands.get_files_from_visual_selection(visual_data)
Expand Down
Loading