Skip to content
Draft
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
12 changes: 8 additions & 4 deletions lua/claudecode/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -1094,9 +1094,10 @@ end

---Format file path for at mention (exposed for testing)
---@param file_path string The file path to format
---@param base_cwd string|nil Optional base working directory (defaults to Neovim's CWD)
---@return string formatted_path The formatted path
---@return boolean is_directory Whether the path is a directory
function M._format_path_for_at_mention(file_path)
function M._format_path_for_at_mention(file_path, base_cwd)
-- Input validation
if not file_path or type(file_path) ~= "string" or file_path == "" then
error("format_path_for_at_mention: file_path must be a non-empty string")
Expand All @@ -1112,9 +1113,9 @@ function M._format_path_for_at_mention(file_path)

local is_directory = vim.fn.isdirectory(file_path) == 1
local formatted_path = file_path
local cwd = base_cwd or vim.fn.getcwd()

if is_directory then
local cwd = vim.fn.getcwd()
if string.find(file_path, cwd, 1, true) == 1 then
local relative_path = string.sub(file_path, #cwd + 2)
if relative_path ~= "" then
Expand All @@ -1127,7 +1128,6 @@ function M._format_path_for_at_mention(file_path)
formatted_path = formatted_path .. "/"
end
else
local cwd = vim.fn.getcwd()
if string.find(file_path, cwd, 1, true) == 1 then
local relative_path = string.sub(file_path, #cwd + 2)
if relative_path ~= "" then
Expand All @@ -1145,9 +1145,13 @@ function M._broadcast_at_mention(file_path, start_line, end_line)
return false, "Claude Code integration is not running"
end

-- Get terminal CWD for path formatting (falls back to Neovim CWD if nil)
local terminal = require("claudecode.terminal")
local terminal_cwd = terminal.get_terminal_cwd()

-- Safely format the path and handle validation errors
local formatted_path, is_directory
local format_success, format_result, is_dir_result = pcall(M._format_path_for_at_mention, file_path)
local format_success, format_result, is_dir_result = pcall(M._format_path_for_at_mention, file_path, terminal_cwd)
if not format_success then
return false, format_result -- format_result contains the error message
end
Expand Down
13 changes: 13 additions & 0 deletions lua/claudecode/terminal.lua
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ local M = {}

local claudecode_server_module = require("claudecode.server.init")

-- Cache the terminal's working directory (set when terminal is opened)
local last_terminal_cwd = nil

---@type ClaudeCodeTerminalConfig
local defaults = {
split_side = "right",
Expand Down Expand Up @@ -498,6 +501,7 @@ end
---@param cmd_args string? Arguments to append to the claude command.
function M.open(opts_override, cmd_args)
local effective_config = build_config(opts_override)
last_terminal_cwd = effective_config.cwd
local cmd_string, claude_env_table = get_claude_command_and_env(cmd_args)

get_provider().open(cmd_string, claude_env_table, effective_config)
Expand All @@ -513,6 +517,7 @@ end
---@param cmd_args string? Arguments to append to the claude command.
function M.simple_toggle(opts_override, cmd_args)
local effective_config = build_config(opts_override)
last_terminal_cwd = effective_config.cwd
local cmd_string, claude_env_table = get_claude_command_and_env(cmd_args)

get_provider().simple_toggle(cmd_string, claude_env_table, effective_config)
Expand All @@ -523,6 +528,7 @@ end
---@param cmd_args string|nil (optional) Arguments to append to the claude command.
function M.focus_toggle(opts_override, cmd_args)
local effective_config = build_config(opts_override)
last_terminal_cwd = effective_config.cwd
local cmd_string, claude_env_table = get_claude_command_and_env(cmd_args)

get_provider().focus_toggle(cmd_string, claude_env_table, effective_config)
Expand Down Expand Up @@ -569,4 +575,11 @@ function M._get_managed_terminal_for_test()
return nil
end

---Gets the cached terminal working directory.
---This returns the CWD that was resolved when the terminal was opened.
---@return string|nil The terminal's working directory, or nil if no terminal has been opened.
function M.get_terminal_cwd()
return last_terminal_cwd
end

return M
52 changes: 52 additions & 0 deletions tests/unit/at_mention_edge_cases_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -326,4 +326,56 @@ describe("At Mention Edge Cases", function()
assert_contains(result, "file.lua")
end)
end)

describe("custom base_cwd parameter", function()
it("should use custom base_cwd when provided", function()
mock_vim.fn.filereadable = function(path)
return path == "/git/repo/src/main.lua" and 1 or 0
end

-- Default behavior uses vim.fn.getcwd() which returns /Users/test/project
local result_default = init_module._format_path_for_at_mention("/git/repo/src/main.lua")
expect(result_default).to_be("/git/repo/src/main.lua") -- Not relative to /Users/test/project

-- With custom base_cwd, should be relative to /git/repo
local result_custom = init_module._format_path_for_at_mention("/git/repo/src/main.lua", "/git/repo")
expect(result_custom).to_be("src/main.lua")
end)

it("should use custom base_cwd for directories", function()
mock_vim.fn.isdirectory = function(path)
return path == "/git/repo/src" and 1 or 0
end
mock_vim.fn.filereadable = function()
return 0
end

-- With custom base_cwd, directory should be relative
local result_custom = init_module._format_path_for_at_mention("/git/repo/src", "/git/repo")
expect(result_custom).to_be("src/")
end)

it("should fall back to vim.fn.getcwd() when base_cwd is nil", function()
mock_vim.fn.filereadable = function(path)
return path == "/Users/test/project/config.lua" and 1 or 0
end

-- When base_cwd is nil, should use vim.fn.getcwd()
local result = init_module._format_path_for_at_mention("/Users/test/project/config.lua", nil)
expect(result).to_be("config.lua") -- Relative to /Users/test/project (getcwd)
end)

it("should handle root directory with custom base_cwd", function()
mock_vim.fn.isdirectory = function(path)
return path == "/git/repo" and 1 or 0
end
mock_vim.fn.filereadable = function()
return 0
end

-- Path is exactly the base_cwd
local result = init_module._format_path_for_at_mention("/git/repo", "/git/repo")
expect(result).to_be("./")
end)
end)
end)
Loading