Skip to content

feat: add oil.nvim support for file selection #27

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 4 commits into from
Jun 17, 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
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,15 +88,15 @@ That's it! For more configuration options, see [Advanced Setup](#advanced-setup)
The `<leader>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
- **In nvim-tree/neo-tree/oil.nvim 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 `<leader>as`
- **Multiple files**: Select multiple files (using tree plugin's selection features) and press `<leader>as`
- **Smart detection**: Automatically detects whether you're in nvim-tree or neo-tree
- **Multiple files**: Select multiple files (using tree plugin's selection features or visual selection in oil.nvim) and press `<leader>as`
- **Smart detection**: Automatically detects whether you're in nvim-tree, neo-tree, or oil.nvim
- **Error handling**: Clear feedback if no files are selected or if tree plugins aren't available

### Direct File Addition
Expand Down
1 change: 1 addition & 0 deletions lua/claudecode/diff.lua
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ function M._find_main_editor_window()
or filetype == "neo-tree-popup"
or filetype == "ClaudeCode"
or filetype == "NvimTree"
or filetype == "oil"
or filetype == "aerial"
or filetype == "tagbar"
)
Expand Down
1 change: 1 addition & 0 deletions lua/claudecode/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,7 @@ function M._create_commands()

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

Expand Down
85 changes: 84 additions & 1 deletion lua/claudecode/integrations.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
-- Tree integration module for ClaudeCode.nvim
-- Handles detection and selection of files from nvim-tree and neo-tree
-- Handles detection and selection of files from nvim-tree, neo-tree, and oil.nvim
-- @module claudecode.integrations
local M = {}

Expand All @@ -14,6 +14,8 @@ function M.get_selected_files_from_tree()
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()
else
return nil, "Not in a supported tree buffer (current filetype: " .. current_ft .. ")"
end
Expand Down Expand Up @@ -178,4 +180,85 @@ function M._get_neotree_selection()
return {}, "No file found under cursor"
end

--- Get selected files from oil.nvim
--- Supports both visual 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_oil_selection()
local success, oil = pcall(require, "oil")
if not success then
return {}, "oil.nvim not available"
end

local bufnr = vim.api.nvim_get_current_buf() --[[@as number]]
local files = {}

-- Check if we're in visual mode
local mode = vim.fn.mode()
if mode == "V" or mode == "v" or mode == "\22" then
-- Visual mode: use the common visual range function
local visual_commands = require("claudecode.visual_commands")
local start_line, end_line = visual_commands.get_visual_range()

-- Get current directory once
local dir_ok, current_dir = pcall(oil.get_current_dir, bufnr)
if not dir_ok or not current_dir then
return {}, "Failed to get current directory"
end

-- Process each line in the visual selection
for line = start_line, end_line do
local entry_ok, entry = pcall(oil.get_entry_on_line, bufnr, line)
if entry_ok and entry and entry.name then
-- Skip parent directory entries
if entry.name ~= ".." and entry.name ~= "." then
local full_path = current_dir .. entry.name
-- Handle various entry types
if entry.type == "file" or entry.type == "link" then
table.insert(files, full_path)
elseif entry.type == "directory" then
-- Ensure directory paths end with /
table.insert(files, full_path:match("/$") and full_path or full_path .. "/")
else
-- For unknown types, return the path anyway
table.insert(files, full_path)
end
end
end
end

if #files > 0 then
return files, nil
end
else
-- Normal mode: get file under cursor with error handling
local ok, entry = pcall(oil.get_cursor_entry)
if not ok or not entry then
return {}, "Failed to get cursor entry"
end

local dir_ok, current_dir = pcall(oil.get_current_dir, bufnr)
if not dir_ok or not current_dir then
return {}, "Failed to get current directory"
end

-- Process the entry
if entry.name and entry.name ~= ".." and entry.name ~= "." then
local full_path = current_dir .. entry.name
-- Handle various entry types
if entry.type == "file" or entry.type == "link" then
return { full_path }, nil
elseif entry.type == "directory" then
-- Ensure directory paths end with /
return { full_path:match("/$") and full_path or full_path .. "/" }, nil
else
-- For unknown types, return the path anyway
return { full_path }, nil
end
end
end

return {}, "No file found under cursor"
end

return M
1 change: 1 addition & 0 deletions lua/claudecode/tools/open_file.lua
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ local function find_main_editor_window()
or filetype == "neo-tree-popup"
or filetype == "ClaudeCode"
or filetype == "NvimTree"
or filetype == "oil"
or filetype == "aerial"
or filetype == "tagbar"
)
Expand Down
35 changes: 35 additions & 0 deletions lua/claudecode/visual_commands.lua
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,13 @@ function M.get_tree_state()
end

return nvim_tree_api, "nvim-tree"
elseif current_ft == "oil" then
local oil_success, oil = pcall(require, "oil")
if not oil_success then
return nil, nil
end

return oil, "oil"
else
return nil, nil
end
Expand Down Expand Up @@ -338,6 +345,34 @@ function M.get_files_from_visual_selection(visual_data)
end
end
files = unique_files
elseif tree_type == "oil" then
local oil = tree_state
local bufnr = vim.api.nvim_get_current_buf()

-- Get current directory once
local dir_ok, current_dir = pcall(oil.get_current_dir, bufnr)
if dir_ok and current_dir then
-- Access the process_oil_entry function through a module method
for line = start_pos, end_pos do
local entry_ok, entry = pcall(oil.get_entry_on_line, bufnr, line)
if entry_ok and entry and entry.name then
-- Skip parent directory entries
if entry.name ~= ".." and entry.name ~= "." then
local full_path = current_dir .. entry.name
-- Handle various entry types
if entry.type == "file" or entry.type == "link" then
table.insert(files, full_path)
elseif entry.type == "directory" then
-- Ensure directory paths end with /
table.insert(files, full_path:match("/$") and full_path or full_path .. "/")
else
-- For unknown types, return the path anyway
table.insert(files, full_path)
end
end
end
end
end
end

return files, nil
Expand Down
Loading