Skip to content

feat: support send-to-claude for snacks.explorer #68

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

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
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
30 changes: 30 additions & 0 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,34 @@ vim.api.nvim_create_autocmd("VimLeavePre", {
})
```

### 7. File Explorer Integrations (`integrations.lua`)

Unified interface for popular file explorers:

```lua
-- Supports nvim-tree, neo-tree, oil.nvim, and snacks.explorer
function M.get_selected_files_from_tree()
local current_ft = vim.bo.filetype

if current_ft == "NvimTree" then
return M._get_nvim_tree_selection()
elseif current_ft == "neo-tree" then
return M._get_neotree_selection()
elseif current_ft == "oil" then
return M._get_oil_selection()
elseif current_ft == "snacks_picker_list" then
return M._get_snacks_explorer_selection()
end
end
```

Key features across all integrations:

- **Visual mode support**: Select multiple files using vim visual mode
- **Security protection**: Filters out root-level files (`/etc/passwd`, `/usr/bin/vim`)
- **Directory handling**: Adds trailing slashes to directories for consistency
- **Fallback behavior**: Selected items → current item → error

## Module Structure

```
Expand All @@ -197,6 +225,8 @@ lua/claudecode/
│ ├── client.lua # Connection management
│ └── utils.lua # Pure Lua SHA-1, base64
├── tools/init.lua # MCP tool registry
├── integrations.lua # File explorer integrations
├── visual_commands.lua # Visual mode handling
├── diff.lua # Native diff support
├── selection.lua # Selection tracking
├── terminal.lua # Terminal management
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ When Anthropic released Claude Code, they only supported VS Code and JetBrains.
"<leader>as",
"<cmd>ClaudeCodeTreeAdd<cr>",
desc = "Add file",
ft = { "NvimTree", "neo-tree", "oil" },
ft = { "NvimTree", "neo-tree", "oil", "snacks_picker_list" },
},
-- Diff management
{ "<leader>aa", "<cmd>ClaudeCodeDiffAccept<cr>", desc = "Accept diff" },
Expand Down Expand Up @@ -76,7 +76,7 @@ That's it! The plugin will auto-configure everything else.
1. **Launch Claude**: Run `:ClaudeCode` to open Claude in a split terminal
2. **Send context**:
- Select text in visual mode and use `<leader>as` to send it to Claude
- In `nvim-tree`/`neo-tree`/`oil.nvim`, press `<leader>as` on a file to add it to Claude's context
- In `nvim-tree`/`neo-tree`/`oil.nvim`/`snacks.explorer`, press `<leader>as` on a file to add it to Claude's context
3. **Let Claude work**: Claude can now:
- See your current file and selections in real-time
- Open files in your editor
Expand Down
2 changes: 1 addition & 1 deletion dev-config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ return {
"<leader>as",
"<cmd>ClaudeCodeTreeAdd<cr>",
desc = "Add file from tree",
ft = { "NvimTree", "neo-tree", "oil" },
ft = { "NvimTree", "neo-tree", "oil", "snacks_picker_list" }, -- snacks.explorer uses "snacks_picker_list" filetype
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool, but this comment doesn't add any context here.

},

-- Development helpers
Expand Down
1 change: 1 addition & 0 deletions fixtures/snacks-explorer/init.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
require("config.lazy")
6 changes: 6 additions & 0 deletions fixtures/snacks-explorer/lazy-lock.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"lazy.nvim": { "branch": "main", "commit": "6c3bda4aca61a13a9c63f1c1d1b16b9d3be90d7a" },
"mini.icons": { "branch": "main", "commit": "b8f6fa6f5a3fd0c56936252edcd691184e5aac0c" },
"snacks.nvim": { "branch": "main", "commit": "bc0630e43be5699bb94dadc302c0d21615421d93" },
"which-key.nvim": { "branch": "main", "commit": "370ec46f710e058c9c1646273e6b225acf47cbed" }
}
41 changes: 41 additions & 0 deletions fixtures/snacks-explorer/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/snacks-explorer/lua/plugins/dev-claudecode.lua
33 changes: 33 additions & 0 deletions fixtures/snacks-explorer/lua/plugins/init.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
return {
-- Essential plugins for basic functionality
{
"folke/which-key.nvim",
event = "VeryLazy",
opts = {},
keys = {
{
"<leader>?",
function()
require("which-key").show({ global = false })
end,
desc = "Buffer Local Keymaps (which-key)",
},
},
},

-- Icon support for file explorers
{
"echasnovski/mini.icons",
opts = {},
lazy = true,
specs = {
{ "nvim-tree/nvim-web-devicons", enabled = false, optional = true },
},
init = function()
package.preload["nvim-web-devicons"] = function()
require("mini.icons").mock_nvim_web_devicons()
return package.loaded["nvim-web-devicons"]
end
end,
},
}
183 changes: 183 additions & 0 deletions fixtures/snacks-explorer/lua/plugins/snacks.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
return {
"folke/snacks.nvim",
priority = 1000,
lazy = false,
opts = {
-- Enable the explorer module
explorer = {
enabled = true,
replace_netrw = true, -- Replace netrw with snacks explorer
},
-- Enable other useful modules for testing
bigfile = { enabled = true },
notifier = { enabled = true },
quickfile = { enabled = true },
statuscolumn = { enabled = true },
words = { enabled = true },
},
keys = {
-- Main explorer keybindings
{
"<leader>e",
function()
require("snacks").explorer()
end,
desc = "Explorer",
},
{
"<leader>E",
function()
require("snacks").explorer.open()
end,
desc = "Explorer (open)",
},
{
"<leader>fe",
function()
require("snacks").explorer.reveal()
end,
desc = "Explorer (reveal current file)",
},

-- Alternative keybindings for testing
{
"-",
function()
require("snacks").explorer()
end,
desc = "Open parent directory",
},
{
"<C-n>",
function()
require("snacks").explorer()
end,
desc = "File Explorer",
},

-- Snacks utility keybindings for testing
{
"<leader>un",
function()
require("snacks").notifier.dismiss()
end,
desc = "Dismiss All Notifications",
},
{
"<leader>bd",
function()
require("snacks").bufdelete()
end,
desc = "Delete Buffer",
},
{
"<leader>gg",
function()
require("snacks").lazygit()
end,
desc = "Lazygit",
},
{
"<leader>gb",
function()
require("snacks").git.blame_line()
end,
desc = "Git Blame Line",
},
{
"<leader>gB",
function()
require("snacks").gitbrowse()
end,
desc = "Git Browse",
},
{
"<leader>gf",
function()
require("snacks").lazygit.log_file()
end,
desc = "Lazygit Current File History",
},
{
"<leader>gl",
function()
require("snacks").lazygit.log()
end,
desc = "Lazygit Log (cwd)",
},
{
"<leader>cR",
function()
require("snacks").rename.rename_file()
end,
desc = "Rename File",
},
{
"<c-/>",
function()
require("snacks").terminal()
end,
desc = "Toggle Terminal",
},
{
"<c-_>",
function()
require("snacks").terminal()
end,
desc = "which_key_ignore",
},
},
init = function()
vim.api.nvim_create_autocmd("User", {
pattern = "VeryLazy",
callback = function()
-- Setup some globals for easier testing
_G.Snacks = require("snacks")
_G.lazygit = _G.Snacks.lazygit
_G.explorer = _G.Snacks.explorer
end,
})
end,
config = function(_, opts)
require("snacks").setup(opts)

-- Additional explorer-specific keybindings that activate after setup
vim.api.nvim_create_autocmd("FileType", {
pattern = "snacks_picker_list", -- This is the filetype for snacks explorer
callback = function(event)
local buf = event.buf
-- Custom keybindings specifically for snacks explorer buffers
vim.keymap.set("n", "<C-v>", function()
-- Toggle visual mode for multi-selection (this is what the PR adds support for)
vim.cmd("normal! V")
end, { buffer = buf, desc = "Toggle visual selection" })

vim.keymap.set("n", "v", function()
vim.cmd("normal! v")
end, { buffer = buf, desc = "Visual mode" })

vim.keymap.set("n", "V", function()
vim.cmd("normal! V")
end, { buffer = buf, desc = "Visual line mode" })

-- Additional testing keybindings
vim.keymap.set("n", "?", function()
require("which-key").show({ buffer = buf })
end, { buffer = buf, desc = "Show keybindings" })
end,
})

-- Set up some helpful defaults for testing
vim.opt.number = true
vim.opt.relativenumber = true
vim.opt.signcolumn = "yes"
vim.opt.wrap = false

-- Print helpful message when starting
vim.defer_fn(function()
print("🍿 Snacks Explorer fixture loaded!")
print("Press <leader>e to open explorer, <leader>? for help")
print("Use visual modes (v/V/<C-v>) in explorer for multi-file selection")
end, 500)
end,
}
1 change: 1 addition & 0 deletions lua/claudecode/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,7 @@ function M._create_commands()
local is_tree_buffer = current_ft == "NvimTree"
or current_ft == "neo-tree"
or current_ft == "oil"
or current_ft == "snacks_picker_list"
or string.match(current_bufname, "neo%-tree")
or string.match(current_bufname, "NvimTree")

Expand Down
Loading