From ea969b66966ff1e0a6f2d055abe0b2693d38dab3 Mon Sep 17 00:00:00 2001 From: Blink Date: Fri, 6 Jun 2025 16:55:54 +0000 Subject: [PATCH 01/15] feat: add nvim-tree and neotree integration --- README.md | 8 +++++-- lua/claudecode/init.lua | 41 +++++++++++++++++++++++++++++++++ lua/claudecode/integrations.lua | 29 +++++++++++++++++++++++ 3 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 lua/claudecode/integrations.lua diff --git a/README.md b/README.md index d398883..bf786b1 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,9 @@ Using [lazy.nvim](https://github.com/folke/lazy.nvim): config = true, keys = { { "ac", "ClaudeCode", desc = "Toggle Claude" }, - { "as", "ClaudeCodeSend", mode = "v", desc = "Send to Claude" }, + { "as", "ClaudeCodeSendSelection", mode = "v", desc = "Send to Claude" }, + -- Add file from nvim-tree or neo-tree + { "ca", "ClaudeCodeTreeAddFile", desc = "ClaudeCode: Add file from tree", ft = { "NvimTree", "neo-tree" } }, }, } ``` @@ -60,7 +62,9 @@ That's it! For more configuration options, see [Advanced Setup](#advanced-setup) ## Usage 1. **Launch Claude**: Run `:ClaudeCode` to open Claude in a split terminal -2. **Send context**: Select text and run `:'<,'>ClaudeCodeSend` to send it to Claude +2. **Send context**: + - Select text and run `:'<,'>ClaudeCodeSendSelection` to send it to Claude + - Use the keymap you configured to add a file from `nvim-tree` or `neo-tree` 3. **Let Claude work**: Claude can now: - See your current file and selections in real-time - Open files in your editor diff --git a/lua/claudecode/init.lua b/lua/claudecode/init.lua index 489785c..ef0b683 100644 --- a/lua/claudecode/init.lua +++ b/lua/claudecode/init.lua @@ -246,6 +246,47 @@ function M._create_commands() }) vim.api.nvim_create_user_command("ClaudeCodeSend", function(opts) + if not M.state.server then + logger.error("command", "ClaudeCodeSend: Claude Code integration is not running.") + return + end + + local terminal = require("claudecode.terminal") + local bufnr = terminal.get_active_terminal_bufnr() + if not bufnr then + logger.error("command", "ClaudeCodeSend: Could not find terminal buffer.") + return + end + + if opts.fargs and #opts.fargs > 0 then + local file_path = table.concat(opts.fargs, " ") + local formatted_path = "@file " .. file_path + local last_line = vim.api.nvim_buf_line_count(bufnr) + vim.api.nvim_buf_set_lines(bufnr, last_line, -1, false, { formatted_path }) + else + local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false) + local content = table.concat(lines, "\n") + M.state.server.send(content) + vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, { "" }) + end + end, { + desc = "Send the content of the input buffer to Claude Code. Optionally add a file path.", + nargs = "*", + }) + + vim.api.nvim_create_user_command("ClaudeCodeTreeAddFile", function() + local integrations = require("claudecode.integrations") + local file_path = integrations.get_path_from_tree() + if file_path then + vim.cmd("ClaudeCodeSend " .. file_path) + else + logger.warn("command", "ClaudeCodeTreeAddFile: Could not determine file path from the tree.") + end + end, { + desc = "Add the selected file from a tree explorer to the Claude Code input buffer.", + }) + + vim.api.nvim_create_user_command("ClaudeCodeSendSelection", function(opts) if not M.state.server then logger.error("command", "ClaudeCodeSend: Claude Code integration is not running.") vim.notify("Claude Code integration is not running", vim.log.levels.ERROR) diff --git a/lua/claudecode/integrations.lua b/lua/claudecode/integrations.lua new file mode 100644 index 0000000..666d9c9 --- /dev/null +++ b/lua/claudecode/integrations.lua @@ -0,0 +1,29 @@ +local M = {} + +function M.get_path_from_tree() + if vim.bo.filetype == "NvimTree" then + local success, nvim_tree_api = pcall(require, "nvim-tree.api") + if success and nvim_tree_api then + local node = nvim_tree_api.tree.get_node_under_cursor() + if node and node.absolute_path then + return node.absolute_path + end + end + elseif vim.bo.filetype == "neo-tree" then + local success, manager = pcall(require, "neo-tree.sources.manager") + if success and manager then + local state = manager.get_state() + if state and state.tree then + local node = state.tree:get_node() + if node and node.path then + return node.path + end + end + end + end + + vim.notify("Could not get file path from tree.", vim.log.levels.WARN, { title = "ClaudeCode" }) + return nil +end + +return M From d76c99f352c6936e7ef7f6ef17d2a029ed5a37be Mon Sep 17 00:00:00 2001 From: Blink Date: Fri, 6 Jun 2025 16:58:31 +0000 Subject: [PATCH 02/15] fix: correct neo-tree API call --- README.md | 10 +++++----- lua/claudecode/integrations.lua | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index bf786b1..95e7a84 100644 --- a/README.md +++ b/README.md @@ -50,9 +50,9 @@ Using [lazy.nvim](https://github.com/folke/lazy.nvim): config = true, keys = { { "ac", "ClaudeCode", desc = "Toggle Claude" }, - { "as", "ClaudeCodeSendSelection", mode = "v", desc = "Send to Claude" }, + { "av", "ClaudeCodeSendSelection", mode = "v", desc = "Send Visual Selection" }, -- Add file from nvim-tree or neo-tree - { "ca", "ClaudeCodeTreeAddFile", desc = "ClaudeCode: Add file from tree", ft = { "NvimTree", "neo-tree" } }, + { "as", "ClaudeCodeTreeAddFile", desc = "Send File From Tree", ft = { "NvimTree", "neo-tree" } }, }, } ``` @@ -62,9 +62,9 @@ That's it! For more configuration options, see [Advanced Setup](#advanced-setup) ## Usage 1. **Launch Claude**: Run `:ClaudeCode` to open Claude in a split terminal -2. **Send context**: - - Select text and run `:'<,'>ClaudeCodeSendSelection` to send it to Claude - - Use the keymap you configured to add a file from `nvim-tree` or `neo-tree` +2. **Send context**: + - Select text in visual mode and use `av` to send it to Claude + - In `nvim-tree` or `neo-tree`, press `as` on a file to add it to the context 3. **Let Claude work**: Claude can now: - See your current file and selections in real-time - Open files in your editor diff --git a/lua/claudecode/integrations.lua b/lua/claudecode/integrations.lua index 666d9c9..be92531 100644 --- a/lua/claudecode/integrations.lua +++ b/lua/claudecode/integrations.lua @@ -12,7 +12,7 @@ function M.get_path_from_tree() elseif vim.bo.filetype == "neo-tree" then local success, manager = pcall(require, "neo-tree.sources.manager") if success and manager then - local state = manager.get_state() + local state = manager.get_state("filesystem") if state and state.tree then local node = state.tree:get_node() if node and node.path then From f1d913ddc5da39136d9d8719c4a5df360a90d890 Mon Sep 17 00:00:00 2001 From: Blink Date: Fri, 6 Jun 2025 17:00:01 +0000 Subject: [PATCH 03/15] fix: correct neo-tree API call --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 95e7a84..e37827e 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,7 @@ Using [lazy.nvim](https://github.com/folke/lazy.nvim): "coder/claudecode.nvim", config = true, keys = { + { "a", nil, mode = { "n", "v", "NvimTree", "neo-tree" }, desc = "AI/Claude Code" }, { "ac", "ClaudeCode", desc = "Toggle Claude" }, { "av", "ClaudeCodeSendSelection", mode = "v", desc = "Send Visual Selection" }, -- Add file from nvim-tree or neo-tree @@ -62,9 +63,9 @@ That's it! For more configuration options, see [Advanced Setup](#advanced-setup) ## Usage 1. **Launch Claude**: Run `:ClaudeCode` to open Claude in a split terminal -2. **Send context**: - - Select text in visual mode and use `av` to send it to Claude - - In `nvim-tree` or `neo-tree`, press `as` on a file to add it to the context +2. **Send context**: + - Select text in visual mode and use `av` to send it to Claude + - In `nvim-tree` or `neo-tree`, press `as` on a file to add it to the context 3. **Let Claude work**: Claude can now: - See your current file and selections in real-time - Open files in your editor From 4808410e63297c264f441e71ff3d4bd7b6d7af51 Mon Sep 17 00:00:00 2001 From: "blink-so[bot]" <211532188+blink-so[bot]@users.noreply.github.com> Date: Fri, 6 Jun 2025 17:11:48 +0000 Subject: [PATCH 04/15] feat: make leader key group available in all modes Remove mode restriction from a key group to make it available in all modes instead of just n, v, NvimTree, and neo-tree modes. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e37827e..9615a13 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ Using [lazy.nvim](https://github.com/folke/lazy.nvim): "coder/claudecode.nvim", config = true, keys = { - { "a", nil, mode = { "n", "v", "NvimTree", "neo-tree" }, desc = "AI/Claude Code" }, + { "a", nil, desc = "AI/Claude Code" }, { "ac", "ClaudeCode", desc = "Toggle Claude" }, { "av", "ClaudeCodeSendSelection", mode = "v", desc = "Send Visual Selection" }, -- Add file from nvim-tree or neo-tree From ece7e05d69111c31df47dd53207b5bbf8129360f Mon Sep 17 00:00:00 2001 From: "blink-so[bot]" <211532188+blink-so[bot]@users.noreply.github.com> Date: Fri, 6 Jun 2025 17:44:42 +0000 Subject: [PATCH 05/15] revert: restore original ClaudeCodeSend command behavior - Revert ClaudeCodeSend to handle visual selections as originally intended - Remove ClaudeCodeTreeAddFile and ClaudeCodeSendSelection commands - Remove integrations.lua file - Restore original keybinding configuration in README Co-authored-by: ThomasK33 <2198487-ThomasK33@users.noreply.github.com> --- README.md | 9 ++----- lua/claudecode/init.lua | 42 +-------------------------------- lua/claudecode/integrations.lua | 29 ----------------------- 3 files changed, 3 insertions(+), 77 deletions(-) delete mode 100644 lua/claudecode/integrations.lua diff --git a/README.md b/README.md index 9615a13..d398883 100644 --- a/README.md +++ b/README.md @@ -49,11 +49,8 @@ Using [lazy.nvim](https://github.com/folke/lazy.nvim): "coder/claudecode.nvim", config = true, keys = { - { "a", nil, desc = "AI/Claude Code" }, { "ac", "ClaudeCode", desc = "Toggle Claude" }, - { "av", "ClaudeCodeSendSelection", mode = "v", desc = "Send Visual Selection" }, - -- Add file from nvim-tree or neo-tree - { "as", "ClaudeCodeTreeAddFile", desc = "Send File From Tree", ft = { "NvimTree", "neo-tree" } }, + { "as", "ClaudeCodeSend", mode = "v", desc = "Send to Claude" }, }, } ``` @@ -63,9 +60,7 @@ That's it! For more configuration options, see [Advanced Setup](#advanced-setup) ## Usage 1. **Launch Claude**: Run `:ClaudeCode` to open Claude in a split terminal -2. **Send context**: - - Select text in visual mode and use `av` to send it to Claude - - In `nvim-tree` or `neo-tree`, press `as` on a file to add it to the context +2. **Send context**: Select text and run `:'<,'>ClaudeCodeSend` to send it to Claude 3. **Let Claude work**: Claude can now: - See your current file and selections in real-time - Open files in your editor diff --git a/lua/claudecode/init.lua b/lua/claudecode/init.lua index ef0b683..9a4015f 100644 --- a/lua/claudecode/init.lua +++ b/lua/claudecode/init.lua @@ -246,47 +246,6 @@ function M._create_commands() }) vim.api.nvim_create_user_command("ClaudeCodeSend", function(opts) - if not M.state.server then - logger.error("command", "ClaudeCodeSend: Claude Code integration is not running.") - return - end - - local terminal = require("claudecode.terminal") - local bufnr = terminal.get_active_terminal_bufnr() - if not bufnr then - logger.error("command", "ClaudeCodeSend: Could not find terminal buffer.") - return - end - - if opts.fargs and #opts.fargs > 0 then - local file_path = table.concat(opts.fargs, " ") - local formatted_path = "@file " .. file_path - local last_line = vim.api.nvim_buf_line_count(bufnr) - vim.api.nvim_buf_set_lines(bufnr, last_line, -1, false, { formatted_path }) - else - local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false) - local content = table.concat(lines, "\n") - M.state.server.send(content) - vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, { "" }) - end - end, { - desc = "Send the content of the input buffer to Claude Code. Optionally add a file path.", - nargs = "*", - }) - - vim.api.nvim_create_user_command("ClaudeCodeTreeAddFile", function() - local integrations = require("claudecode.integrations") - local file_path = integrations.get_path_from_tree() - if file_path then - vim.cmd("ClaudeCodeSend " .. file_path) - else - logger.warn("command", "ClaudeCodeTreeAddFile: Could not determine file path from the tree.") - end - end, { - desc = "Add the selected file from a tree explorer to the Claude Code input buffer.", - }) - - vim.api.nvim_create_user_command("ClaudeCodeSendSelection", function(opts) if not M.state.server then logger.error("command", "ClaudeCodeSend: Claude Code integration is not running.") vim.notify("Claude Code integration is not running", vim.log.levels.ERROR) @@ -333,6 +292,7 @@ function M._create_commands() range = true, -- Important: This makes the command expect a range (visual selection) }) + local terminal_ok, terminal = pcall(require, "claudecode.terminal") if terminal_ok then vim.api.nvim_create_user_command("ClaudeCode", function(_opts) diff --git a/lua/claudecode/integrations.lua b/lua/claudecode/integrations.lua deleted file mode 100644 index be92531..0000000 --- a/lua/claudecode/integrations.lua +++ /dev/null @@ -1,29 +0,0 @@ -local M = {} - -function M.get_path_from_tree() - if vim.bo.filetype == "NvimTree" then - local success, nvim_tree_api = pcall(require, "nvim-tree.api") - if success and nvim_tree_api then - local node = nvim_tree_api.tree.get_node_under_cursor() - if node and node.absolute_path then - return node.absolute_path - end - end - elseif vim.bo.filetype == "neo-tree" then - local success, manager = pcall(require, "neo-tree.sources.manager") - if success and manager then - local state = manager.get_state("filesystem") - if state and state.tree then - local node = state.tree:get_node() - if node and node.path then - return node.path - end - end - end - end - - vim.notify("Could not get file path from tree.", vim.log.levels.WARN, { title = "ClaudeCode" }) - return nil -end - -return M From 64964406e318b24e2ed3e2772a6aed6812cc548c Mon Sep 17 00:00:00 2001 From: "blink-so[bot]" <211532188+blink-so[bot]@users.noreply.github.com> Date: Fri, 6 Jun 2025 18:06:16 +0000 Subject: [PATCH 06/15] 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 as keybinding behavior Co-authored-by: ThomasK33 <2198487-ThomasK33@users.noreply.github.com> --- README.md | 26 ++++++- lua/claudecode/init.lua | 51 ++++++++++++++ lua/claudecode/integrations.lua | 118 ++++++++++++++++++++++++++++++++ 3 files changed, 194 insertions(+), 1 deletion(-) create mode 100644 lua/claudecode/integrations.lua diff --git a/README.md b/README.md index d398883..923c429 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,8 @@ Using [lazy.nvim](https://github.com/folke/lazy.nvim): keys = { { "ac", "ClaudeCode", desc = "Toggle Claude" }, { "as", "ClaudeCodeSend", mode = "v", desc = "Send to Claude" }, + -- Add file from nvim-tree or neo-tree + { "as", "ClaudeCodeTreeAdd", desc = "Add file to Claude context", ft = { "NvimTree", "neo-tree" } }, }, } ``` @@ -60,13 +62,35 @@ That's it! For more configuration options, see [Advanced Setup](#advanced-setup) ## Usage 1. **Launch Claude**: Run `:ClaudeCode` to open Claude in a split terminal -2. **Send context**: Select text and run `:'<,'>ClaudeCodeSend` to send it to Claude +2. **Send context**: + - Select text in visual mode and use `as` to send it to Claude + - In `nvim-tree` or `neo-tree`, press `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 - Show diffs with proposed changes - Access diagnostics and workspace info +## Commands + +- `:ClaudeCode` - Toggle the Claude Code terminal window +- `:ClaudeCodeSend` - Send current visual selection to Claude +- `:ClaudeCodeTreeAdd` - Add selected file(s) from tree explorer to Claude context + +### Tree Integration + +The `as` keybinding has context-aware behavior: +- **In normal buffers (visual mode)**: Sends selected text to Claude +- **In nvim-tree/neo-tree buffers**: Adds the file under cursor (or selected files) to Claude's context + +This allows you to quickly add entire files to Claude's context for review, refactoring, or discussion. + +#### Features: +- **Single file**: Place cursor on any file and press `as` +- **Multiple files**: Select multiple files (using tree plugin's selection features) and press `as` +- **Smart detection**: Automatically detects whether you're in nvim-tree or neo-tree +- **Error handling**: Clear feedback if no files are selected or if tree plugins aren't available + ## How It Works 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. diff --git a/lua/claudecode/init.lua b/lua/claudecode/init.lua index 9a4015f..9a80da8 100644 --- a/lua/claudecode/init.lua +++ b/lua/claudecode/init.lua @@ -292,6 +292,57 @@ function M._create_commands() range = true, -- Important: This makes the command expect a range (visual selection) }) + vim.api.nvim_create_user_command("ClaudeCodeTreeAdd", function() + if not M.state.server then + logger.error("command", "ClaudeCodeTreeAdd: Claude Code integration is not running.") + vim.notify("Claude Code integration is not running", vim.log.levels.ERROR) + return + end + + local integrations = require("claudecode.integrations") + local files, error = integrations.get_selected_files_from_tree() + + if error then + logger.warn("command", "ClaudeCodeTreeAdd: " .. error) + vim.notify(error, vim.log.levels.WARN, { title = "ClaudeCode" }) + return + end + + if not files or #files == 0 then + logger.warn("command", "ClaudeCodeTreeAdd: No files selected") + vim.notify("No files selected", vim.log.levels.WARN, { title = "ClaudeCode" }) + return + end + + -- Send each file as an at_mention (full file, no line numbers) + local success_count = 0 + for _, file_path in ipairs(files) do + local params = { + filePath = file_path, + lineStart = nil, -- No line numbers for full file + lineEnd = nil -- No line numbers for full file + } + + local broadcast_success = M.state.server.broadcast("at_mentioned", params) + if broadcast_success then + success_count = success_count + 1 + logger.debug("command", "ClaudeCodeTreeAdd: Added file " .. file_path) + else + logger.error("command", "ClaudeCodeTreeAdd: Failed to add file " .. file_path) + end + end + + if success_count > 0 then + local message = success_count == 1 + and "Added 1 file to Claude context" + or string.format("Added %d files to Claude context", success_count) + vim.notify(message, vim.log.levels.INFO, { title = "ClaudeCode" }) + else + vim.notify("Failed to add any files", vim.log.levels.ERROR, { title = "ClaudeCode" }) + end + end, { + desc = "Add selected file(s) from tree explorer to Claude Code context", + }) local terminal_ok, terminal = pcall(require, "claudecode.terminal") if terminal_ok then diff --git a/lua/claudecode/integrations.lua b/lua/claudecode/integrations.lua new file mode 100644 index 0000000..154ae4c --- /dev/null +++ b/lua/claudecode/integrations.lua @@ -0,0 +1,118 @@ +--- +-- Tree integration module for ClaudeCode.nvim +-- Handles detection and selection of files from nvim-tree and neo-tree +-- @module claudecode.integrations +local M = {} + +local logger = require("claudecode.logger") + +--- Get selected files from the current tree explorer +--- @return table|nil files List of file paths, or nil if error +--- @return string|nil error Error message if operation failed +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() + else + return nil, "Not in a supported tree buffer (current filetype: " .. current_ft .. ")" + end +end + +--- Get selected files from nvim-tree +--- Supports both multi-selection (marks) and single file under cursor +--- @return table files List of file paths +--- @return string|nil error Error message if operation failed +function M._get_nvim_tree_selection() + local success, nvim_tree_api = pcall(require, "nvim-tree.api") + if not success then + logger.warn("integrations", "nvim-tree API not available") + return {}, "nvim-tree not available" + end + + local files = {} + + -- Check for multi-selection first (marked files) + local marks = nvim_tree_api.marks.list() + if marks and #marks > 0 then + logger.debug("integrations", "Found " .. #marks .. " marked files in nvim-tree") + for _, mark in ipairs(marks) do + if mark.type == "file" and mark.absolute_path then + table.insert(files, mark.absolute_path) + logger.debug("integrations", "Added marked file: " .. mark.absolute_path) + end + end + if #files > 0 then + return files, nil + end + end + + -- Fall back to node under cursor + local node = nvim_tree_api.tree.get_node_under_cursor() + if node then + if node.type == "file" and node.absolute_path then + logger.debug("integrations", "Found file under cursor: " .. node.absolute_path) + return { node.absolute_path }, nil + elseif node.type == "directory" then + return {}, "Cannot add directory to Claude context. Please select a file." + end + end + + return {}, "No file found under cursor" +end + +--- Get selected files from neo-tree +--- Supports both multi-selection and single file under cursor +--- @return table files List of file paths +--- @return string|nil error Error message if operation failed +function M._get_neotree_selection() + local success, manager = pcall(require, "neo-tree.sources.manager") + if not success then + logger.warn("integrations", "neo-tree manager not available") + return {}, "neo-tree not available" + end + + local state = manager.get_state("filesystem") + if not state then + logger.warn("integrations", "neo-tree filesystem state not available") + return {}, "neo-tree filesystem state not available" + end + + local files = {} + + -- Check for multi-selection first + if state.tree and state.tree.get_selection then + local selection = state.tree:get_selection() + if selection and #selection > 0 then + logger.debug("integrations", "Found " .. #selection .. " selected items in neo-tree") + for _, node in ipairs(selection) do + if node.type == "file" and node.path then + table.insert(files, node.path) + logger.debug("integrations", "Added selected file: " .. node.path) + end + end + if #files > 0 then + return files, nil + end + end + end + + -- Fall back to current node under cursor + if state.tree then + local node = state.tree:get_node() + if node then + if node.type == "file" and node.path then + logger.debug("integrations", "Found file under cursor: " .. node.path) + return { node.path }, nil + elseif node.type == "directory" then + return {}, "Cannot add directory to Claude context. Please select a file." + end + end + end + + return {}, "No file found under cursor" +end + +return M \ No newline at end of file From 782cd5696da704725e21dbef9eb7b129aa11886a Mon Sep 17 00:00:00 2001 From: "blink-so[bot]" <211532188+blink-so[bot]@users.noreply.github.com> Date: Fri, 6 Jun 2025 18:20:40 +0000 Subject: [PATCH 07/15] chore: apply nix fmt formatting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Format files with nix fmt as per project requirements - All checks pass: nix fmt ✓, make check ✓, make test ✓ Co-authored-by: ThomasK33 <2198487-ThomasK33@users.noreply.github.com> --- README.md | 4 +++- lua/claudecode/init.lua | 17 ++++++++--------- lua/claudecode/integrations.lua | 22 +++++++++++----------- 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 923c429..5f15b2f 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ That's it! For more configuration options, see [Advanced Setup](#advanced-setup) ## Usage 1. **Launch Claude**: Run `:ClaudeCode` to open Claude in a split terminal -2. **Send context**: +2. **Send context**: - Select text in visual mode and use `as` to send it to Claude - In `nvim-tree` or `neo-tree`, press `as` on a file to add it to Claude's context 3. **Let Claude work**: Claude can now: @@ -80,12 +80,14 @@ That's it! For more configuration options, see [Advanced Setup](#advanced-setup) ### Tree Integration The `as` keybinding has context-aware behavior: + - **In normal buffers (visual mode)**: Sends selected text to Claude - **In nvim-tree/neo-tree buffers**: Adds the file under cursor (or selected files) to Claude's context This allows you to quickly add entire files to Claude's context for review, refactoring, or discussion. #### Features: + - **Single file**: Place cursor on any file and press `as` - **Multiple files**: Select multiple files (using tree plugin's selection features) and press `as` - **Smart detection**: Automatically detects whether you're in nvim-tree or neo-tree diff --git a/lua/claudecode/init.lua b/lua/claudecode/init.lua index 9a80da8..a2eae10 100644 --- a/lua/claudecode/init.lua +++ b/lua/claudecode/init.lua @@ -301,28 +301,28 @@ function M._create_commands() local integrations = require("claudecode.integrations") local files, error = integrations.get_selected_files_from_tree() - + if error then logger.warn("command", "ClaudeCodeTreeAdd: " .. error) vim.notify(error, vim.log.levels.WARN, { title = "ClaudeCode" }) return end - + if not files or #files == 0 then logger.warn("command", "ClaudeCodeTreeAdd: No files selected") vim.notify("No files selected", vim.log.levels.WARN, { title = "ClaudeCode" }) return end - + -- Send each file as an at_mention (full file, no line numbers) local success_count = 0 for _, file_path in ipairs(files) do local params = { filePath = file_path, - lineStart = nil, -- No line numbers for full file - lineEnd = nil -- No line numbers for full file + lineStart = nil, -- No line numbers for full file + lineEnd = nil, -- No line numbers for full file } - + local broadcast_success = M.state.server.broadcast("at_mentioned", params) if broadcast_success then success_count = success_count + 1 @@ -331,10 +331,9 @@ function M._create_commands() logger.error("command", "ClaudeCodeTreeAdd: Failed to add file " .. file_path) end end - + if success_count > 0 then - local message = success_count == 1 - and "Added 1 file to Claude context" + local message = success_count == 1 and "Added 1 file to Claude context" or string.format("Added %d files to Claude context", success_count) vim.notify(message, vim.log.levels.INFO, { title = "ClaudeCode" }) else diff --git a/lua/claudecode/integrations.lua b/lua/claudecode/integrations.lua index 154ae4c..cc70b57 100644 --- a/lua/claudecode/integrations.lua +++ b/lua/claudecode/integrations.lua @@ -11,7 +11,7 @@ local logger = require("claudecode.logger") --- @return string|nil error Error message if operation failed 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 @@ -31,9 +31,9 @@ function M._get_nvim_tree_selection() logger.warn("integrations", "nvim-tree API not available") return {}, "nvim-tree not available" end - + local files = {} - + -- Check for multi-selection first (marked files) local marks = nvim_tree_api.marks.list() if marks and #marks > 0 then @@ -48,7 +48,7 @@ function M._get_nvim_tree_selection() return files, nil end end - + -- Fall back to node under cursor local node = nvim_tree_api.tree.get_node_under_cursor() if node then @@ -59,7 +59,7 @@ function M._get_nvim_tree_selection() return {}, "Cannot add directory to Claude context. Please select a file." end end - + return {}, "No file found under cursor" end @@ -73,15 +73,15 @@ function M._get_neotree_selection() logger.warn("integrations", "neo-tree manager not available") return {}, "neo-tree not available" end - + local state = manager.get_state("filesystem") if not state then logger.warn("integrations", "neo-tree filesystem state not available") return {}, "neo-tree filesystem state not available" end - + local files = {} - + -- Check for multi-selection first if state.tree and state.tree.get_selection then local selection = state.tree:get_selection() @@ -98,7 +98,7 @@ function M._get_neotree_selection() end end end - + -- Fall back to current node under cursor if state.tree then local node = state.tree:get_node() @@ -111,8 +111,8 @@ function M._get_neotree_selection() end end end - + return {}, "No file found under cursor" end -return M \ No newline at end of file +return M From cd6b4a9a0309dafb0f54d360c106874d62a78a46 Mon Sep 17 00:00:00 2001 From: "blink-so[bot]" <211532188+blink-so[bot]@users.noreply.github.com> Date: Fri, 6 Jun 2025 18:27:08 +0000 Subject: [PATCH 08/15] fix: change tree file add success message to debug level - Change success notification from INFO to DEBUG to avoid popup - User requested less verbose notifications for successful file additions - Error notifications remain at ERROR level for visibility Co-authored-by: ThomasK33 <2198487-ThomasK33@users.noreply.github.com> --- lua/claudecode/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/claudecode/init.lua b/lua/claudecode/init.lua index a2eae10..24591ef 100644 --- a/lua/claudecode/init.lua +++ b/lua/claudecode/init.lua @@ -335,7 +335,7 @@ function M._create_commands() if success_count > 0 then local message = success_count == 1 and "Added 1 file to Claude context" or string.format("Added %d files to Claude context", success_count) - vim.notify(message, vim.log.levels.INFO, { title = "ClaudeCode" }) + vim.notify(message, vim.log.levels.DEBUG, { title = "ClaudeCode" }) else vim.notify("Failed to add any files", vim.log.levels.ERROR, { title = "ClaudeCode" }) end From 66f18f96af341ece871d09e58b7b09304606f49a Mon Sep 17 00:00:00 2001 From: "blink-so[bot]" <211532188+blink-so[bot]@users.noreply.github.com> Date: Fri, 6 Jun 2025 18:33:11 +0000 Subject: [PATCH 09/15] fix: enhance ClaudeCodeSend to handle tree multi-selection properly - Add tree buffer detection to ClaudeCodeSend command - Delegate to tree integration when in nvim-tree or neo-tree buffers - Fixes issue where visual selection in tree sent line ranges instead of files - Now properly sends each selected file as individual at_mentions - Simplify keybinding configuration - single command handles both contexts - Update documentation to reflect enhanced functionality Resolves multi-file selection issue in neotree/nvim-tree Co-authored-by: ThomasK33 <2198487-ThomasK33@users.noreply.github.com> --- README.md | 8 +++---- lua/claudecode/init.lua | 52 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 5f15b2f..56a0db2 100644 --- a/README.md +++ b/README.md @@ -51,8 +51,8 @@ Using [lazy.nvim](https://github.com/folke/lazy.nvim): keys = { { "ac", "ClaudeCode", desc = "Toggle Claude" }, { "as", "ClaudeCodeSend", mode = "v", desc = "Send to Claude" }, - -- Add file from nvim-tree or neo-tree - { "as", "ClaudeCodeTreeAdd", desc = "Add file to Claude context", ft = { "NvimTree", "neo-tree" } }, + -- Tree integration works automatically with the same keybinding + { "as", "ClaudeCodeSend", desc = "Add file to Claude context", ft = { "NvimTree", "neo-tree" } }, }, } ``` @@ -74,8 +74,8 @@ That's it! For more configuration options, see [Advanced Setup](#advanced-setup) ## Commands - `:ClaudeCode` - Toggle the Claude Code terminal window -- `:ClaudeCodeSend` - Send current visual selection to Claude -- `:ClaudeCodeTreeAdd` - Add selected file(s) from tree explorer to Claude context +- `:ClaudeCodeSend` - Send current visual selection to Claude, or add files from tree explorer +- `:ClaudeCodeTreeAdd` - Add selected file(s) from tree explorer to Claude context (also available via ClaudeCodeSend) ### Tree Integration diff --git a/lua/claudecode/init.lua b/lua/claudecode/init.lua index 24591ef..d5cfe3b 100644 --- a/lua/claudecode/init.lua +++ b/lua/claudecode/init.lua @@ -251,6 +251,58 @@ function M._create_commands() vim.notify("Claude Code integration is not running", vim.log.levels.ERROR) return end + + -- Check if we're in a tree buffer - if so, delegate to tree integration + local current_ft = vim.bo.filetype + if current_ft == "NvimTree" or current_ft == "neo-tree" then + logger.debug("command", "ClaudeCodeSend: Detected tree buffer, delegating to tree integration") + + local integrations = require("claudecode.integrations") + local files, error = integrations.get_selected_files_from_tree() + + if error then + logger.warn("command", "ClaudeCodeSend->TreeAdd: " .. error) + vim.notify(error, vim.log.levels.WARN, { title = "ClaudeCode" }) + return + end + + if not files or #files == 0 then + logger.warn("command", "ClaudeCodeSend->TreeAdd: No files selected") + vim.notify("No files selected", vim.log.levels.WARN, { title = "ClaudeCode" }) + return + end + + -- Send each file as an at_mention (full file, no line numbers) + local success_count = 0 + for _, file_path in ipairs(files) do + local params = { + filePath = file_path, + lineStart = nil, -- No line numbers for full file + lineEnd = nil, -- No line numbers for full file + } + + local broadcast_success = M.state.server.broadcast("at_mentioned", params) + if broadcast_success then + success_count = success_count + 1 + logger.debug("command", "ClaudeCodeSend->TreeAdd: Added file " .. file_path) + else + logger.error("command", "ClaudeCodeSend->TreeAdd: Failed to add file " .. file_path) + end + end + + if success_count > 0 then + local message = success_count == 1 and "Added 1 file to Claude context" + or string.format("Added %d files to Claude context", success_count) + logger.debug("command", message) -- Use debug level to avoid popup + else + vim.notify("Failed to add any files", vim.log.levels.ERROR, { title = "ClaudeCode" }) + end + + -- Exit visual mode if we were in it + vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes("", true, false, true), "n", false) + return + end + logger.debug( "command", "ClaudeCodeSend (new logic) invoked. Mode: " From 8d667d87e3a3fc1db18d76728fdb32a307fb9fcb Mon Sep 17 00:00:00 2001 From: "blink-so[bot]" <211532188+blink-so[bot]@users.noreply.github.com> Date: Fri, 6 Jun 2025 18:40:46 +0000 Subject: [PATCH 10/15] refactor: unify logging by using logger module instead of vim.notify - Replace all vim.notify calls with appropriate logger.* calls for consistency - Remove redundant notifications where logger already handles the logging - Maintain unified logging throughout the project - Better adherence to project's logging standards Co-authored-by: ThomasK33 <2198487-ThomasK33@users.noreply.github.com> --- lua/claudecode/init.lua | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/lua/claudecode/init.lua b/lua/claudecode/init.lua index d5cfe3b..2df03d3 100644 --- a/lua/claudecode/init.lua +++ b/lua/claudecode/init.lua @@ -262,13 +262,11 @@ function M._create_commands() if error then logger.warn("command", "ClaudeCodeSend->TreeAdd: " .. error) - vim.notify(error, vim.log.levels.WARN, { title = "ClaudeCode" }) return end if not files or #files == 0 then logger.warn("command", "ClaudeCodeSend->TreeAdd: No files selected") - vim.notify("No files selected", vim.log.levels.WARN, { title = "ClaudeCode" }) return end @@ -295,7 +293,7 @@ function M._create_commands() or string.format("Added %d files to Claude context", success_count) logger.debug("command", message) -- Use debug level to avoid popup else - vim.notify("Failed to add any files", vim.log.levels.ERROR, { title = "ClaudeCode" }) + logger.error("command", "ClaudeCodeSend->TreeAdd: Failed to add any files") end -- Exit visual mode if we were in it @@ -315,7 +313,6 @@ function M._create_commands() if not M.state.server then logger.error("command", "ClaudeCodeSend: Claude Code integration is not running.") - vim.notify("Claude Code integration is not running", vim.log.levels.ERROR, { title = "ClaudeCode Error" }) return end @@ -356,13 +353,11 @@ function M._create_commands() if error then logger.warn("command", "ClaudeCodeTreeAdd: " .. error) - vim.notify(error, vim.log.levels.WARN, { title = "ClaudeCode" }) return end if not files or #files == 0 then logger.warn("command", "ClaudeCodeTreeAdd: No files selected") - vim.notify("No files selected", vim.log.levels.WARN, { title = "ClaudeCode" }) return end @@ -387,9 +382,9 @@ function M._create_commands() if success_count > 0 then local message = success_count == 1 and "Added 1 file to Claude context" or string.format("Added %d files to Claude context", success_count) - vim.notify(message, vim.log.levels.DEBUG, { title = "ClaudeCode" }) + logger.debug("command", message) else - vim.notify("Failed to add any files", vim.log.levels.ERROR, { title = "ClaudeCode" }) + logger.error("command", "ClaudeCodeTreeAdd: Failed to add any files") end end, { desc = "Add selected file(s) from tree explorer to Claude Code context", From 4a385506a3b647e78d1210cf0f28e8dd21a52e23 Mon Sep 17 00:00:00 2001 From: "blink-so[bot]" <211532188+blink-so[bot]@users.noreply.github.com> Date: Fri, 6 Jun 2025 18:45:13 +0000 Subject: [PATCH 11/15] debug: add logging to diagnose tree buffer detection issue - Add debug logging to show actual filetype and buffer name during visual selection - Improve tree buffer detection to include buffer name pattern matching - This will help identify why tree detection isn't working in neotree visual mode Co-authored-by: ThomasK33 <2198487-ThomasK33@users.noreply.github.com> --- lua/claudecode/init.lua | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/lua/claudecode/init.lua b/lua/claudecode/init.lua index 2df03d3..976e758 100644 --- a/lua/claudecode/init.lua +++ b/lua/claudecode/init.lua @@ -254,7 +254,19 @@ function M._create_commands() -- Check if we're in a tree buffer - if so, delegate to tree integration local current_ft = vim.bo.filetype - if current_ft == "NvimTree" or current_ft == "neo-tree" then + local current_bufname = vim.api.nvim_buf_get_name(0) + logger.debug( + "command", + "ClaudeCodeSend: Buffer detection - filetype: '" .. current_ft .. "', bufname: '" .. current_bufname .. "'" + ) + + -- Check both filetype and buffer name for tree detection + local is_tree_buffer = current_ft == "NvimTree" + or current_ft == "neo-tree" + or string.match(current_bufname, "neo%-tree") + or string.match(current_bufname, "NvimTree") + + if is_tree_buffer then logger.debug("command", "ClaudeCodeSend: Detected tree buffer, delegating to tree integration") local integrations = require("claudecode.integrations") From 24ff09a71438f90373065b724186f082d9d479ca Mon Sep 17 00:00:00 2001 From: "blink-so[bot]" <211532188+blink-so[bot]@users.noreply.github.com> Date: Fri, 6 Jun 2025 18:50:14 +0000 Subject: [PATCH 12/15] fix: improve neo-tree multi-selection detection and clean up logging - Remove redundant vim.notify call from ClaudeCodeTreeAdd - Enhance neo-tree multi-selection detection with multiple fallback methods - Add comprehensive debug logging to identify which selection method works - Try get_selection(), is_selected nodes, and state.selected_nodes approaches - Better error handling and debugging for neo-tree integration Co-authored-by: ThomasK33 <2198487-ThomasK33@users.noreply.github.com> --- lua/claudecode/init.lua | 1 - lua/claudecode/integrations.lua | 51 +++++++++++++++++++++++++++++++-- 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/lua/claudecode/init.lua b/lua/claudecode/init.lua index 976e758..079d633 100644 --- a/lua/claudecode/init.lua +++ b/lua/claudecode/init.lua @@ -356,7 +356,6 @@ function M._create_commands() vim.api.nvim_create_user_command("ClaudeCodeTreeAdd", function() if not M.state.server then logger.error("command", "ClaudeCodeTreeAdd: Claude Code integration is not running.") - vim.notify("Claude Code integration is not running", vim.log.levels.ERROR) return end diff --git a/lua/claudecode/integrations.lua b/lua/claudecode/integrations.lua index cc70b57..6c8770c 100644 --- a/lua/claudecode/integrations.lua +++ b/lua/claudecode/integrations.lua @@ -82,9 +82,52 @@ function M._get_neotree_selection() local files = {} - -- Check for multi-selection first - if state.tree and state.tree.get_selection then - local selection = state.tree:get_selection() + -- Debug: Check what methods are available + if state.tree then + logger.debug("integrations", "neo-tree state.tree available, checking for selection methods") + + -- Try different approaches for multi-selection + local selection = nil + + -- Method 1: get_selection + if state.tree.get_selection then + selection = state.tree:get_selection() + logger.debug( + "integrations", + "get_selection method available, selection count: " .. (selection and #selection or "nil") + ) + else + logger.debug("integrations", "get_selection method not available") + end + + -- Method 2: Check for selected nodes directly + if not selection or #selection == 0 then + if state.tree.get_nodes then + local nodes = state.tree:get_nodes() + if nodes then + for _, node in ipairs(nodes) do + if node.is_selected or (node.extra and node.extra.is_selected) then + selection = selection or {} + table.insert(selection, node) + logger.debug( + "integrations", + "Found selected node via is_selected: " .. (node.path or node.name or "unknown") + ) + end + end + end + end + end + + -- Method 3: Try to get selected items via neo-tree's selection state + if not selection or #selection == 0 then + if state.selected_nodes then + selection = state.selected_nodes + logger.debug("integrations", "Using state.selected_nodes, count: " .. #selection) + end + end + + -- Process the selection if found if selection and #selection > 0 then logger.debug("integrations", "Found " .. #selection .. " selected items in neo-tree") for _, node in ipairs(selection) do @@ -96,6 +139,8 @@ function M._get_neotree_selection() if #files > 0 then return files, nil end + else + logger.debug("integrations", "No multi-selection found, falling back to cursor node") end end From d3e1c1f955b078fbe63486d22a513020cbe0b087 Mon Sep 17 00:00:00 2001 From: "blink-so[bot]" <211532188+blink-so[bot]@users.noreply.github.com> Date: Fri, 6 Jun 2025 19:01:22 +0000 Subject: [PATCH 13/15] feat: implement visual selection detection for neo-tree multi-file selection - Rewrite neo-tree selection detection to handle visual mode properly - Map line numbers to nodes to get files from visual selection range - Try multiple approaches: visual mode detection, get_selection, state.selected_nodes - Add comprehensive debug logging to understand selection behavior - Fix luacheck warning about unused variable This should properly detect when multiple files are selected in neo-tree using visual mode and send each file as a separate at_mention. Co-authored-by: ThomasK33 <2198487-ThomasK33@users.noreply.github.com> --- lua/claudecode/integrations.lua | 97 +++++++++++++++++++++------------ 1 file changed, 61 insertions(+), 36 deletions(-) diff --git a/lua/claudecode/integrations.lua b/lua/claudecode/integrations.lua index 6c8770c..8386a11 100644 --- a/lua/claudecode/integrations.lua +++ b/lua/claudecode/integrations.lua @@ -81,55 +81,82 @@ function M._get_neotree_selection() end local files = {} + local selection = nil - -- Debug: Check what methods are available - if state.tree then - logger.debug("integrations", "neo-tree state.tree available, checking for selection methods") + -- Debug: Log available state structure + logger.debug("integrations", "neo-tree state available, checking for selection") - -- Try different approaches for multi-selection - local selection = nil + -- Method 1: Check for visual selection in neo-tree (when using V to select multiple lines) + -- This is likely what happens when you select multiple files with visual mode - -- Method 1: get_selection - if state.tree.get_selection then - selection = state.tree:get_selection() - logger.debug( - "integrations", - "get_selection method available, selection count: " .. (selection and #selection or "nil") - ) - else - logger.debug("integrations", "get_selection method not available") + -- Get visual selection range if in visual mode + local mode = vim.fn.mode() + if mode == "V" or mode == "v" or mode == "\22" then -- Visual modes + logger.debug("integrations", "Visual mode detected: " .. mode) + + -- Get the visual selection range + local start_line = vim.fn.line("v") + local end_line = vim.fn.line(".") + if start_line > end_line then + start_line, end_line = end_line, start_line end - -- Method 2: Check for selected nodes directly - if not selection or #selection == 0 then - if state.tree.get_nodes then - local nodes = state.tree:get_nodes() - if nodes then - for _, node in ipairs(nodes) do - if node.is_selected or (node.extra and node.extra.is_selected) then - selection = selection or {} - table.insert(selection, node) - logger.debug( - "integrations", - "Found selected node via is_selected: " .. (node.path or node.name or "unknown") - ) + logger.debug("integrations", "Visual selection from line " .. start_line .. " to " .. end_line) + + -- Get the rendered tree to map line numbers to file paths + if state.tree and state.tree.get_nodes then + local nodes = state.tree:get_nodes() + if nodes then + local line_to_node = {} + + -- Build a mapping of line numbers to nodes + local function map_nodes(node_list, depth) + depth = depth or 0 + for _, node in ipairs(node_list) do + if node.position and node.position.row then + line_to_node[node.position.row] = node + end + if node.children then + map_nodes(node.children, depth + 1) end end end + + map_nodes(nodes) + + -- Get files from selected lines + for line = start_line, end_line do + local node = line_to_node[line] + if node and node.type == "file" and node.path then + table.insert(files, node.path) + logger.debug("integrations", "Added file from line " .. line .. ": " .. node.path) + end + end + + if #files > 0 then + return files, nil + end end end + end - -- Method 3: Try to get selected items via neo-tree's selection state - if not selection or #selection == 0 then - if state.selected_nodes then - selection = state.selected_nodes - logger.debug("integrations", "Using state.selected_nodes, count: " .. #selection) + -- Method 2: Try neo-tree's built-in selection methods + if state.tree then + if state.tree.get_selection then + selection = state.tree:get_selection() + if selection and #selection > 0 then + logger.debug("integrations", "Found selection via get_selection: " .. #selection) end end - -- Process the selection if found + -- Method 3: Check state-level selection + if (not selection or #selection == 0) and state.selected_nodes then + selection = state.selected_nodes + logger.debug("integrations", "Found selection via state.selected_nodes: " .. #selection) + end + + -- Process selection if found if selection and #selection > 0 then - logger.debug("integrations", "Found " .. #selection .. " selected items in neo-tree") for _, node in ipairs(selection) do if node.type == "file" and node.path then table.insert(files, node.path) @@ -139,8 +166,6 @@ function M._get_neotree_selection() if #files > 0 then return files, nil end - else - logger.debug("integrations", "No multi-selection found, falling back to cursor node") end end From 647b8f934a125e6c4b0e87c3333b82e47bc6301d Mon Sep 17 00:00:00 2001 From: "blink-so[bot]" <211532188+blink-so[bot]@users.noreply.github.com> Date: Fri, 6 Jun 2025 19:07:33 +0000 Subject: [PATCH 14/15] debug: add comprehensive logging for neo-tree node structure analysis - Add detailed debug logging to understand neo-tree node structure - Log all nodes, their positions, types, and paths - Show line-to-node mapping details - Debug visual selection line mapping process - This will help identify why line-to-node mapping isn't working Co-authored-by: ThomasK33 <2198487-ThomasK33@users.noreply.github.com> --- lua/claudecode/integrations.lua | 46 +++++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/lua/claudecode/integrations.lua b/lua/claudecode/integrations.lua index 8386a11..fe61197 100644 --- a/lua/claudecode/integrations.lua +++ b/lua/claudecode/integrations.lua @@ -107,16 +107,37 @@ function M._get_neotree_selection() if state.tree and state.tree.get_nodes then local nodes = state.tree:get_nodes() if nodes then + logger.debug("integrations", "Found " .. #nodes .. " top-level nodes") local line_to_node = {} -- Build a mapping of line numbers to nodes local function map_nodes(node_list, depth) depth = depth or 0 - for _, node in ipairs(node_list) do + for i, node in ipairs(node_list) do + logger.debug( + "integrations", + "Node " + .. i + .. " at depth " + .. depth + .. ": type=" + .. (node.type or "nil") + .. ", path=" + .. (node.path or "nil") + .. ", position=" + .. (node.position and node.position.row or "nil") + ) + if node.position and node.position.row then line_to_node[node.position.row] = node + logger.debug( + "integrations", + "Mapped line " .. node.position.row .. " to node: " .. (node.path or node.name or "unknown") + ) end - if node.children then + + if node.children and #node.children > 0 then + logger.debug("integrations", "Node has " .. #node.children .. " children") map_nodes(node.children, depth + 1) end end @@ -124,6 +145,21 @@ function M._get_neotree_selection() map_nodes(nodes) + logger.debug("integrations", "Built line_to_node mapping with " .. vim.tbl_count(line_to_node) .. " entries") + + -- Debug: Show what's in the line mapping for our selection range + for line = start_line, end_line do + local node = line_to_node[line] + if node then + logger.debug( + "integrations", + "Line " .. line .. " maps to: type=" .. (node.type or "nil") .. ", path=" .. (node.path or "nil") + ) + else + logger.debug("integrations", "Line " .. line .. " has no mapping") + end + end + -- Get files from selected lines for line = start_line, end_line do local node = line_to_node[line] @@ -135,8 +171,14 @@ function M._get_neotree_selection() if #files > 0 then return files, nil + else + logger.debug("integrations", "No files found from visual selection lines " .. start_line .. "-" .. end_line) end + else + logger.debug("integrations", "get_nodes() returned nil") end + else + logger.debug("integrations", "state.tree.get_nodes not available") end end From 75b0dbb3f34131e3d526c33bb6fd297e2ae09ced Mon Sep 17 00:00:00 2001 From: "blink-so[bot]" <211532188+blink-so[bot]@users.noreply.github.com> Date: Fri, 6 Jun 2025 19:32:56 +0000 Subject: [PATCH 15/15] fix: implement proper neo-tree visual selection detection - Use neo-tree's internal visual selection method - Get visual selection range with getpos(<") and getpos(" --- lua/claudecode/integrations.lua | 126 +++++--------- lua/claudecode/integrations.lua.backup | 230 +++++++++++++++++++++++++ 2 files changed, 271 insertions(+), 85 deletions(-) create mode 100644 lua/claudecode/integrations.lua.backup diff --git a/lua/claudecode/integrations.lua b/lua/claudecode/integrations.lua index fe61197..0538396 100644 --- a/lua/claudecode/integrations.lua +++ b/lua/claudecode/integrations.lua @@ -64,7 +64,7 @@ function M._get_nvim_tree_selection() end --- Get selected files from neo-tree ---- Supports both multi-selection and single file under cursor +--- Uses neo-tree's own visual selection method when in visual mode --- @return table files List of file paths --- @return string|nil error Error message if operation failed function M._get_neotree_selection() @@ -81,109 +81,65 @@ function M._get_neotree_selection() end local files = {} - local selection = nil - -- Debug: Log available state structure - logger.debug("integrations", "neo-tree state available, checking for selection") - - -- Method 1: Check for visual selection in neo-tree (when using V to select multiple lines) - -- This is likely what happens when you select multiple files with visual mode - - -- Get visual selection range if in visual mode + -- Method 1: Use neo-tree's own visual selection method (like their copy/paste feature) + -- This matches how neo-tree internally handles visual selection local mode = vim.fn.mode() if mode == "V" or mode == "v" or mode == "\22" then -- Visual modes logger.debug("integrations", "Visual mode detected: " .. mode) - -- Get the visual selection range - local start_line = vim.fn.line("v") - local end_line = vim.fn.line(".") - if start_line > end_line then - start_line, end_line = end_line, start_line - end + -- Check if we're in the neo-tree window + if state.winid and state.winid == vim.api.nvim_get_current_win() then + logger.debug("integrations", "In neo-tree window, getting visual selection using neo-tree method") - logger.debug("integrations", "Visual selection from line " .. start_line .. " to " .. end_line) - - -- Get the rendered tree to map line numbers to file paths - if state.tree and state.tree.get_nodes then - local nodes = state.tree:get_nodes() - if nodes then - logger.debug("integrations", "Found " .. #nodes .. " top-level nodes") - local line_to_node = {} - - -- Build a mapping of line numbers to nodes - local function map_nodes(node_list, depth) - depth = depth or 0 - for i, node in ipairs(node_list) do - logger.debug( - "integrations", - "Node " - .. i - .. " at depth " - .. depth - .. ": type=" - .. (node.type or "nil") - .. ", path=" - .. (node.path or "nil") - .. ", position=" - .. (node.position and node.position.row or "nil") - ) - - if node.position and node.position.row then - line_to_node[node.position.row] = node - logger.debug( - "integrations", - "Mapped line " .. node.position.row .. " to node: " .. (node.path or node.name or "unknown") - ) - end - - if node.children and #node.children > 0 then - logger.debug("integrations", "Node has " .. #node.children .. " children") - map_nodes(node.children, depth + 1) - end - end - end + -- Use neo-tree's method to get selected nodes from visual range + local start_pos = vim.fn.getpos("'<")[2] + local end_pos = vim.fn.getpos("'>")[2] - map_nodes(nodes) - - logger.debug("integrations", "Built line_to_node mapping with " .. vim.tbl_count(line_to_node) .. " entries") - - -- Debug: Show what's in the line mapping for our selection range - for line = start_line, end_line do - local node = line_to_node[line] - if node then - logger.debug( - "integrations", - "Line " .. line .. " maps to: type=" .. (node.type or "nil") .. ", path=" .. (node.path or "nil") - ) - else - logger.debug("integrations", "Line " .. line .. " has no mapping") - end - end + if end_pos < start_pos then + start_pos, end_pos = end_pos, start_pos + end - -- Get files from selected lines - for line = start_line, end_line do - local node = line_to_node[line] - if node and node.type == "file" and node.path then - table.insert(files, node.path) - logger.debug("integrations", "Added file from line " .. line .. ": " .. node.path) - end + logger.debug("integrations", "Visual selection from line " .. start_pos .. " to " .. end_pos) + + local selected_nodes = {} + for line = start_pos, end_pos do + local node = state.tree:get_node(line) + if node then + table.insert(selected_nodes, node) + logger.debug( + "integrations", + "Found node at line " .. line .. ": type=" .. (node.type or "nil") .. ", path=" .. (node.path or "nil") + ) + else + logger.debug("integrations", "No node found at line " .. line) end + end - if #files > 0 then - return files, nil - else - logger.debug("integrations", "No files found from visual selection lines " .. start_line .. "-" .. end_line) + logger.debug("integrations", "Found " .. #selected_nodes .. " selected nodes from visual range") + + -- Extract file paths from selected nodes + for _, node in ipairs(selected_nodes) do + if node.type == "file" and node.path then + table.insert(files, node.path) + logger.debug("integrations", "Added file from visual selection: " .. node.path) end + end + + if #files > 0 then + return files, nil else - logger.debug("integrations", "get_nodes() returned nil") + logger.debug("integrations", "No files found in visual selection (" .. #selected_nodes .. " nodes total)") end else - logger.debug("integrations", "state.tree.get_nodes not available") + logger.debug("integrations", "Not in neo-tree window, visual selection method not applicable") end end -- Method 2: Try neo-tree's built-in selection methods if state.tree then + local selection = nil + if state.tree.get_selection then selection = state.tree:get_selection() if selection and #selection > 0 then diff --git a/lua/claudecode/integrations.lua.backup b/lua/claudecode/integrations.lua.backup new file mode 100644 index 0000000..fe61197 --- /dev/null +++ b/lua/claudecode/integrations.lua.backup @@ -0,0 +1,230 @@ +--- +-- Tree integration module for ClaudeCode.nvim +-- Handles detection and selection of files from nvim-tree and neo-tree +-- @module claudecode.integrations +local M = {} + +local logger = require("claudecode.logger") + +--- Get selected files from the current tree explorer +--- @return table|nil files List of file paths, or nil if error +--- @return string|nil error Error message if operation failed +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() + else + return nil, "Not in a supported tree buffer (current filetype: " .. current_ft .. ")" + end +end + +--- Get selected files from nvim-tree +--- Supports both multi-selection (marks) and single file under cursor +--- @return table files List of file paths +--- @return string|nil error Error message if operation failed +function M._get_nvim_tree_selection() + local success, nvim_tree_api = pcall(require, "nvim-tree.api") + if not success then + logger.warn("integrations", "nvim-tree API not available") + return {}, "nvim-tree not available" + end + + local files = {} + + -- Check for multi-selection first (marked files) + local marks = nvim_tree_api.marks.list() + if marks and #marks > 0 then + logger.debug("integrations", "Found " .. #marks .. " marked files in nvim-tree") + for _, mark in ipairs(marks) do + if mark.type == "file" and mark.absolute_path then + table.insert(files, mark.absolute_path) + logger.debug("integrations", "Added marked file: " .. mark.absolute_path) + end + end + if #files > 0 then + return files, nil + end + end + + -- Fall back to node under cursor + local node = nvim_tree_api.tree.get_node_under_cursor() + if node then + if node.type == "file" and node.absolute_path then + logger.debug("integrations", "Found file under cursor: " .. node.absolute_path) + return { node.absolute_path }, nil + elseif node.type == "directory" then + return {}, "Cannot add directory to Claude context. Please select a file." + end + end + + return {}, "No file found under cursor" +end + +--- Get selected files from neo-tree +--- Supports both multi-selection and single file under cursor +--- @return table files List of file paths +--- @return string|nil error Error message if operation failed +function M._get_neotree_selection() + local success, manager = pcall(require, "neo-tree.sources.manager") + if not success then + logger.warn("integrations", "neo-tree manager not available") + return {}, "neo-tree not available" + end + + local state = manager.get_state("filesystem") + if not state then + logger.warn("integrations", "neo-tree filesystem state not available") + return {}, "neo-tree filesystem state not available" + end + + local files = {} + local selection = nil + + -- Debug: Log available state structure + logger.debug("integrations", "neo-tree state available, checking for selection") + + -- Method 1: Check for visual selection in neo-tree (when using V to select multiple lines) + -- This is likely what happens when you select multiple files with visual mode + + -- Get visual selection range if in visual mode + local mode = vim.fn.mode() + if mode == "V" or mode == "v" or mode == "\22" then -- Visual modes + logger.debug("integrations", "Visual mode detected: " .. mode) + + -- Get the visual selection range + local start_line = vim.fn.line("v") + local end_line = vim.fn.line(".") + if start_line > end_line then + start_line, end_line = end_line, start_line + end + + logger.debug("integrations", "Visual selection from line " .. start_line .. " to " .. end_line) + + -- Get the rendered tree to map line numbers to file paths + if state.tree and state.tree.get_nodes then + local nodes = state.tree:get_nodes() + if nodes then + logger.debug("integrations", "Found " .. #nodes .. " top-level nodes") + local line_to_node = {} + + -- Build a mapping of line numbers to nodes + local function map_nodes(node_list, depth) + depth = depth or 0 + for i, node in ipairs(node_list) do + logger.debug( + "integrations", + "Node " + .. i + .. " at depth " + .. depth + .. ": type=" + .. (node.type or "nil") + .. ", path=" + .. (node.path or "nil") + .. ", position=" + .. (node.position and node.position.row or "nil") + ) + + if node.position and node.position.row then + line_to_node[node.position.row] = node + logger.debug( + "integrations", + "Mapped line " .. node.position.row .. " to node: " .. (node.path or node.name or "unknown") + ) + end + + if node.children and #node.children > 0 then + logger.debug("integrations", "Node has " .. #node.children .. " children") + map_nodes(node.children, depth + 1) + end + end + end + + map_nodes(nodes) + + logger.debug("integrations", "Built line_to_node mapping with " .. vim.tbl_count(line_to_node) .. " entries") + + -- Debug: Show what's in the line mapping for our selection range + for line = start_line, end_line do + local node = line_to_node[line] + if node then + logger.debug( + "integrations", + "Line " .. line .. " maps to: type=" .. (node.type or "nil") .. ", path=" .. (node.path or "nil") + ) + else + logger.debug("integrations", "Line " .. line .. " has no mapping") + end + end + + -- Get files from selected lines + for line = start_line, end_line do + local node = line_to_node[line] + if node and node.type == "file" and node.path then + table.insert(files, node.path) + logger.debug("integrations", "Added file from line " .. line .. ": " .. node.path) + end + end + + if #files > 0 then + return files, nil + else + logger.debug("integrations", "No files found from visual selection lines " .. start_line .. "-" .. end_line) + end + else + logger.debug("integrations", "get_nodes() returned nil") + end + else + logger.debug("integrations", "state.tree.get_nodes not available") + end + end + + -- Method 2: Try neo-tree's built-in selection methods + if state.tree then + if state.tree.get_selection then + selection = state.tree:get_selection() + if selection and #selection > 0 then + logger.debug("integrations", "Found selection via get_selection: " .. #selection) + end + end + + -- Method 3: Check state-level selection + if (not selection or #selection == 0) and state.selected_nodes then + selection = state.selected_nodes + logger.debug("integrations", "Found selection via state.selected_nodes: " .. #selection) + end + + -- Process selection if found + if selection and #selection > 0 then + for _, node in ipairs(selection) do + if node.type == "file" and node.path then + table.insert(files, node.path) + logger.debug("integrations", "Added selected file: " .. node.path) + end + end + if #files > 0 then + return files, nil + end + end + end + + -- Fall back to current node under cursor + if state.tree then + local node = state.tree:get_node() + if node then + if node.type == "file" and node.path then + logger.debug("integrations", "Found file under cursor: " .. node.path) + return { node.path }, nil + elseif node.type == "directory" then + return {}, "Cannot add directory to Claude context. Please select a file." + end + end + end + + return {}, "No file found under cursor" +end + +return M