Skip to content

Commit 6496440

Browse files
blink-so[bot]ThomasK33
andcommitted
feat: add ClaudeCodeTreeAdd command for tree integration
- Add integrations.lua module to handle nvim-tree and neo-tree file selection - Add ClaudeCodeTreeAdd command to send file at_mentions from tree explorers - Support both single file selection and multi-file selection - Update README with tree integration documentation and lazy.nvim keybinding setup - Implement context-aware <leader>as keybinding behavior Co-authored-by: ThomasK33 <[email protected]>
1 parent ece7e05 commit 6496440

File tree

3 files changed

+194
-1
lines changed

3 files changed

+194
-1
lines changed

README.md

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ Using [lazy.nvim](https://github.com/folke/lazy.nvim):
5151
keys = {
5252
{ "<leader>ac", "<cmd>ClaudeCode<cr>", desc = "Toggle Claude" },
5353
{ "<leader>as", "<cmd>ClaudeCodeSend<cr>", mode = "v", desc = "Send to Claude" },
54+
-- Add file from nvim-tree or neo-tree
55+
{ "<leader>as", "<cmd>ClaudeCodeTreeAdd<cr>", desc = "Add file to Claude context", ft = { "NvimTree", "neo-tree" } },
5456
},
5557
}
5658
```
@@ -60,13 +62,35 @@ That's it! For more configuration options, see [Advanced Setup](#advanced-setup)
6062
## Usage
6163

6264
1. **Launch Claude**: Run `:ClaudeCode` to open Claude in a split terminal
63-
2. **Send context**: Select text and run `:'<,'>ClaudeCodeSend` to send it to Claude
65+
2. **Send context**:
66+
- Select text in visual mode and use `<leader>as` to send it to Claude
67+
- In `nvim-tree` or `neo-tree`, press `<leader>as` on a file to add it to Claude's context
6468
3. **Let Claude work**: Claude can now:
6569
- See your current file and selections in real-time
6670
- Open files in your editor
6771
- Show diffs with proposed changes
6872
- Access diagnostics and workspace info
6973

74+
## Commands
75+
76+
- `:ClaudeCode` - Toggle the Claude Code terminal window
77+
- `:ClaudeCodeSend` - Send current visual selection to Claude
78+
- `:ClaudeCodeTreeAdd` - Add selected file(s) from tree explorer to Claude context
79+
80+
### Tree Integration
81+
82+
The `<leader>as` keybinding has context-aware behavior:
83+
- **In normal buffers (visual mode)**: Sends selected text to Claude
84+
- **In nvim-tree/neo-tree buffers**: Adds the file under cursor (or selected files) to Claude's context
85+
86+
This allows you to quickly add entire files to Claude's context for review, refactoring, or discussion.
87+
88+
#### Features:
89+
- **Single file**: Place cursor on any file and press `<leader>as`
90+
- **Multiple files**: Select multiple files (using tree plugin's selection features) and press `<leader>as`
91+
- **Smart detection**: Automatically detects whether you're in nvim-tree or neo-tree
92+
- **Error handling**: Clear feedback if no files are selected or if tree plugins aren't available
93+
7094
## How It Works
7195

7296
This plugin creates a WebSocket server that Claude Code CLI connects to, implementing the same protocol as the official VS Code extension. When you launch Claude, it automatically detects Neovim and gains full access to your editor.

lua/claudecode/init.lua

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,57 @@ function M._create_commands()
292292
range = true, -- Important: This makes the command expect a range (visual selection)
293293
})
294294

295+
vim.api.nvim_create_user_command("ClaudeCodeTreeAdd", function()
296+
if not M.state.server then
297+
logger.error("command", "ClaudeCodeTreeAdd: Claude Code integration is not running.")
298+
vim.notify("Claude Code integration is not running", vim.log.levels.ERROR)
299+
return
300+
end
301+
302+
local integrations = require("claudecode.integrations")
303+
local files, error = integrations.get_selected_files_from_tree()
304+
305+
if error then
306+
logger.warn("command", "ClaudeCodeTreeAdd: " .. error)
307+
vim.notify(error, vim.log.levels.WARN, { title = "ClaudeCode" })
308+
return
309+
end
310+
311+
if not files or #files == 0 then
312+
logger.warn("command", "ClaudeCodeTreeAdd: No files selected")
313+
vim.notify("No files selected", vim.log.levels.WARN, { title = "ClaudeCode" })
314+
return
315+
end
316+
317+
-- Send each file as an at_mention (full file, no line numbers)
318+
local success_count = 0
319+
for _, file_path in ipairs(files) do
320+
local params = {
321+
filePath = file_path,
322+
lineStart = nil, -- No line numbers for full file
323+
lineEnd = nil -- No line numbers for full file
324+
}
325+
326+
local broadcast_success = M.state.server.broadcast("at_mentioned", params)
327+
if broadcast_success then
328+
success_count = success_count + 1
329+
logger.debug("command", "ClaudeCodeTreeAdd: Added file " .. file_path)
330+
else
331+
logger.error("command", "ClaudeCodeTreeAdd: Failed to add file " .. file_path)
332+
end
333+
end
334+
335+
if success_count > 0 then
336+
local message = success_count == 1
337+
and "Added 1 file to Claude context"
338+
or string.format("Added %d files to Claude context", success_count)
339+
vim.notify(message, vim.log.levels.INFO, { title = "ClaudeCode" })
340+
else
341+
vim.notify("Failed to add any files", vim.log.levels.ERROR, { title = "ClaudeCode" })
342+
end
343+
end, {
344+
desc = "Add selected file(s) from tree explorer to Claude Code context",
345+
})
295346

296347
local terminal_ok, terminal = pcall(require, "claudecode.terminal")
297348
if terminal_ok then

lua/claudecode/integrations.lua

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
---
2+
-- Tree integration module for ClaudeCode.nvim
3+
-- Handles detection and selection of files from nvim-tree and neo-tree
4+
-- @module claudecode.integrations
5+
local M = {}
6+
7+
local logger = require("claudecode.logger")
8+
9+
--- Get selected files from the current tree explorer
10+
--- @return table|nil files List of file paths, or nil if error
11+
--- @return string|nil error Error message if operation failed
12+
function M.get_selected_files_from_tree()
13+
local current_ft = vim.bo.filetype
14+
15+
if current_ft == "NvimTree" then
16+
return M._get_nvim_tree_selection()
17+
elseif current_ft == "neo-tree" then
18+
return M._get_neotree_selection()
19+
else
20+
return nil, "Not in a supported tree buffer (current filetype: " .. current_ft .. ")"
21+
end
22+
end
23+
24+
--- Get selected files from nvim-tree
25+
--- Supports both multi-selection (marks) and single file under cursor
26+
--- @return table files List of file paths
27+
--- @return string|nil error Error message if operation failed
28+
function M._get_nvim_tree_selection()
29+
local success, nvim_tree_api = pcall(require, "nvim-tree.api")
30+
if not success then
31+
logger.warn("integrations", "nvim-tree API not available")
32+
return {}, "nvim-tree not available"
33+
end
34+
35+
local files = {}
36+
37+
-- Check for multi-selection first (marked files)
38+
local marks = nvim_tree_api.marks.list()
39+
if marks and #marks > 0 then
40+
logger.debug("integrations", "Found " .. #marks .. " marked files in nvim-tree")
41+
for _, mark in ipairs(marks) do
42+
if mark.type == "file" and mark.absolute_path then
43+
table.insert(files, mark.absolute_path)
44+
logger.debug("integrations", "Added marked file: " .. mark.absolute_path)
45+
end
46+
end
47+
if #files > 0 then
48+
return files, nil
49+
end
50+
end
51+
52+
-- Fall back to node under cursor
53+
local node = nvim_tree_api.tree.get_node_under_cursor()
54+
if node then
55+
if node.type == "file" and node.absolute_path then
56+
logger.debug("integrations", "Found file under cursor: " .. node.absolute_path)
57+
return { node.absolute_path }, nil
58+
elseif node.type == "directory" then
59+
return {}, "Cannot add directory to Claude context. Please select a file."
60+
end
61+
end
62+
63+
return {}, "No file found under cursor"
64+
end
65+
66+
--- Get selected files from neo-tree
67+
--- Supports both multi-selection and single file under cursor
68+
--- @return table files List of file paths
69+
--- @return string|nil error Error message if operation failed
70+
function M._get_neotree_selection()
71+
local success, manager = pcall(require, "neo-tree.sources.manager")
72+
if not success then
73+
logger.warn("integrations", "neo-tree manager not available")
74+
return {}, "neo-tree not available"
75+
end
76+
77+
local state = manager.get_state("filesystem")
78+
if not state then
79+
logger.warn("integrations", "neo-tree filesystem state not available")
80+
return {}, "neo-tree filesystem state not available"
81+
end
82+
83+
local files = {}
84+
85+
-- Check for multi-selection first
86+
if state.tree and state.tree.get_selection then
87+
local selection = state.tree:get_selection()
88+
if selection and #selection > 0 then
89+
logger.debug("integrations", "Found " .. #selection .. " selected items in neo-tree")
90+
for _, node in ipairs(selection) do
91+
if node.type == "file" and node.path then
92+
table.insert(files, node.path)
93+
logger.debug("integrations", "Added selected file: " .. node.path)
94+
end
95+
end
96+
if #files > 0 then
97+
return files, nil
98+
end
99+
end
100+
end
101+
102+
-- Fall back to current node under cursor
103+
if state.tree then
104+
local node = state.tree:get_node()
105+
if node then
106+
if node.type == "file" and node.path then
107+
logger.debug("integrations", "Found file under cursor: " .. node.path)
108+
return { node.path }, nil
109+
elseif node.type == "directory" then
110+
return {}, "Cannot add directory to Claude context. Please select a file."
111+
end
112+
end
113+
end
114+
115+
return {}, "No file found under cursor"
116+
end
117+
118+
return M

0 commit comments

Comments
 (0)