feat: add project root option in commands Ask/Chat/ChatNew (#2659)
This commit is contained in:
@@ -31,7 +31,17 @@ local History = {}
|
|||||||
function History.get_history_dir(bufnr)
|
function History.get_history_dir(bufnr)
|
||||||
local dirname = generate_project_dirname_in_storage(bufnr)
|
local dirname = generate_project_dirname_in_storage(bufnr)
|
||||||
local history_dir = Path:new(Config.history.storage_path):joinpath(dirname):joinpath("history")
|
local history_dir = Path:new(Config.history.storage_path):joinpath(dirname):joinpath("history")
|
||||||
if not history_dir:exists() then history_dir:mkdir({ parents = true }) end
|
if not history_dir:exists() then
|
||||||
|
history_dir:mkdir({ parents = true })
|
||||||
|
|
||||||
|
local metadata_filepath = history_dir:joinpath("metadata.json")
|
||||||
|
local metadata = {
|
||||||
|
project_root = Utils.root.get({
|
||||||
|
buf = bufnr,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
metadata_filepath:write(vim.json.encode(metadata), "w")
|
||||||
|
end
|
||||||
return history_dir
|
return history_dir
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -105,13 +115,14 @@ end
|
|||||||
|
|
||||||
function History.save_latest_filename(bufnr, filename)
|
function History.save_latest_filename(bufnr, filename)
|
||||||
local metadata_filepath = History.get_metadata_filepath(bufnr)
|
local metadata_filepath = History.get_metadata_filepath(bufnr)
|
||||||
local metadata
|
local metadata = {}
|
||||||
if not metadata_filepath:exists() then
|
if metadata_filepath:exists() then
|
||||||
metadata = {}
|
|
||||||
else
|
|
||||||
local metadata_content = metadata_filepath:read()
|
local metadata_content = metadata_filepath:read()
|
||||||
metadata = vim.json.decode(metadata_content)
|
metadata = vim.json.decode(metadata_content)
|
||||||
end
|
end
|
||||||
|
if metadata.project_root == nil then metadata.project_root = Utils.root.get({
|
||||||
|
buf = bufnr,
|
||||||
|
}) end
|
||||||
metadata.latest_filename = filename
|
metadata.latest_filename = filename
|
||||||
metadata_filepath:write(vim.json.encode(metadata), "w")
|
metadata_filepath:write(vim.json.encode(metadata), "w")
|
||||||
end
|
end
|
||||||
@@ -187,6 +198,56 @@ end
|
|||||||
|
|
||||||
P.history = History
|
P.history = History
|
||||||
|
|
||||||
|
---@return table[] List of projects with their information
|
||||||
|
function P.list_projects()
|
||||||
|
local projects_dir = Path:new(Config.history.storage_path):joinpath("projects")
|
||||||
|
if not projects_dir:exists() then return {} end
|
||||||
|
|
||||||
|
local projects = {}
|
||||||
|
local dirs = Scan.scan_dir(tostring(projects_dir), { depth = 1, add_dirs = true, only_dirs = true })
|
||||||
|
|
||||||
|
for _, dir_path in ipairs(dirs) do
|
||||||
|
local project_dir = Path:new(dir_path)
|
||||||
|
local history_dir = project_dir:joinpath("history")
|
||||||
|
|
||||||
|
local metadata_file = history_dir:joinpath("metadata.json")
|
||||||
|
local project_root = ""
|
||||||
|
if metadata_file:exists() then
|
||||||
|
local content = metadata_file:read()
|
||||||
|
if content then
|
||||||
|
local metadata = vim.json.decode(content)
|
||||||
|
if metadata and metadata.project_root then project_root = metadata.project_root end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Skip if project_root is empty
|
||||||
|
if project_root == "" then goto continue end
|
||||||
|
|
||||||
|
-- Count history files
|
||||||
|
local history_count = 0
|
||||||
|
if history_dir:exists() then
|
||||||
|
local history_files = vim.fn.glob(tostring(history_dir:joinpath("*.json")), true, true)
|
||||||
|
for _, file in ipairs(history_files) do
|
||||||
|
if not file:match("metadata.json") then history_count = history_count + 1 end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(projects, {
|
||||||
|
name = filepath_to_filename(project_dir),
|
||||||
|
root = project_root,
|
||||||
|
history_count = history_count,
|
||||||
|
directory = tostring(project_dir),
|
||||||
|
})
|
||||||
|
|
||||||
|
::continue::
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Sort by history count (most active projects first)
|
||||||
|
table.sort(projects, function(a, b) return a.history_count > b.history_count end)
|
||||||
|
|
||||||
|
return projects
|
||||||
|
end
|
||||||
|
|
||||||
-- Prompt path
|
-- Prompt path
|
||||||
local Prompt = {}
|
local Prompt = {}
|
||||||
|
|
||||||
|
|||||||
@@ -1607,6 +1607,44 @@ function M.uuid()
|
|||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---Parse command arguments (fargs) into a structured format
|
||||||
|
---@param fargs string[] Command arguments
|
||||||
|
---@param options? {collect_remaining?: boolean, boolean_keys?: string[]} Options for parsing
|
||||||
|
---@return table parsed_args Key-value pairs from arguments
|
||||||
|
---@return string|nil remaining_text Concatenated remaining arguments (if collect_remaining is true)
|
||||||
|
function M.parse_args(fargs, options)
|
||||||
|
options = options or {}
|
||||||
|
local parsed_args = {}
|
||||||
|
local remaining_parts = {}
|
||||||
|
local boolean_keys = options.boolean_keys or {}
|
||||||
|
|
||||||
|
-- Create a lookup table for boolean keys for faster access
|
||||||
|
local boolean_keys_lookup = {}
|
||||||
|
for _, key in ipairs(boolean_keys) do
|
||||||
|
boolean_keys_lookup[key] = true
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, arg in ipairs(fargs) do
|
||||||
|
local key, value = arg:match("([%w_]+)=(.+)")
|
||||||
|
|
||||||
|
if key and value then
|
||||||
|
-- Convert "true"/"false" string values to boolean for specified keys
|
||||||
|
if boolean_keys_lookup[key] or value == "true" or value == "false" then
|
||||||
|
parsed_args[key] = (value == "true")
|
||||||
|
else
|
||||||
|
parsed_args[key] = value
|
||||||
|
end
|
||||||
|
elseif options.collect_remaining then
|
||||||
|
table.insert(remaining_parts, arg)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Return the parsed arguments and optionally the concatenated remaining text
|
||||||
|
if options.collect_remaining and #remaining_parts > 0 then return parsed_args, table.concat(remaining_parts, " ") end
|
||||||
|
|
||||||
|
return parsed_args
|
||||||
|
end
|
||||||
|
|
||||||
---@param tool_use AvanteLLMToolUse
|
---@param tool_use AvanteLLMToolUse
|
||||||
function M.tool_use_to_xml(tool_use)
|
function M.tool_use_to_xml(tool_use)
|
||||||
local tool_use_json = vim.json.encode({
|
local tool_use_json = vim.json.encode({
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ vim.g.avante = 1
|
|||||||
local Clipboard = require("avante.clipboard")
|
local Clipboard = require("avante.clipboard")
|
||||||
local Config = require("avante.config")
|
local Config = require("avante.config")
|
||||||
local Utils = require("avante.utils")
|
local Utils = require("avante.utils")
|
||||||
|
local P = require("avante.path")
|
||||||
local api = vim.api
|
local api = vim.api
|
||||||
|
|
||||||
if Config.support_paste_image() then
|
if Config.support_paste_image() then
|
||||||
@@ -51,52 +52,62 @@ local function cmd(n, c, o)
|
|||||||
api.nvim_create_user_command("Avante" .. n, c, o)
|
api.nvim_create_user_command("Avante" .. n, c, o)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function ask_complete(prefix, _, _)
|
||||||
|
local candidates = {} ---@type string[]
|
||||||
|
vim.list_extend(
|
||||||
|
candidates,
|
||||||
|
---@param x string
|
||||||
|
vim.tbl_map(function(x) return "position=" .. x end, { "left", "right", "top", "bottom" })
|
||||||
|
)
|
||||||
|
vim.list_extend(
|
||||||
|
candidates,
|
||||||
|
---@param x string
|
||||||
|
vim.tbl_map(function(x) return "project_root=" .. x.root end, P.list_projects())
|
||||||
|
)
|
||||||
|
return vim.tbl_filter(function(candidate) return vim.startswith(candidate, prefix) end, candidates)
|
||||||
|
end
|
||||||
|
|
||||||
cmd("Ask", function(opts)
|
cmd("Ask", function(opts)
|
||||||
---@type AskOptions
|
---@type AskOptions
|
||||||
local args = { question = nil, win = {} }
|
local args = { question = nil, win = {} }
|
||||||
local q_parts = {}
|
|
||||||
local q_ask = nil
|
local parsed_args, question = Utils.parse_args(opts.fargs, {
|
||||||
for _, arg in ipairs(opts.fargs) do
|
collect_remaining = true,
|
||||||
local value = arg:match("position=(%w+)")
|
boolean_keys = { "ask" },
|
||||||
local ask = arg:match("ask=(%w+)")
|
})
|
||||||
if ask ~= nil then
|
|
||||||
q_ask = ask == "true"
|
if parsed_args.position then args.win.position = parsed_args.position end
|
||||||
elseif value then
|
|
||||||
args.win.position = value
|
require("avante.api").ask(vim.tbl_deep_extend("force", args, {
|
||||||
else
|
ask = parsed_args.ask,
|
||||||
table.insert(q_parts, arg)
|
project_root = parsed_args.project_root,
|
||||||
end
|
question = question or nil,
|
||||||
end
|
}))
|
||||||
require("avante.api").ask(
|
|
||||||
vim.tbl_deep_extend("force", args, { ask = q_ask, question = #q_parts > 0 and table.concat(q_parts, " ") or nil })
|
|
||||||
)
|
|
||||||
end, {
|
end, {
|
||||||
desc = "avante: ask AI for code suggestions",
|
desc = "avante: ask AI for code suggestions",
|
||||||
nargs = "*",
|
nargs = "*",
|
||||||
complete = function(_, _, _)
|
complete = ask_complete,
|
||||||
local candidates = {} ---@type string[]
|
|
||||||
vim.list_extend(
|
|
||||||
candidates,
|
|
||||||
---@param x string
|
|
||||||
vim.tbl_map(function(x) return "position=" .. x end, { "left", "right", "top", "bottom" })
|
|
||||||
)
|
|
||||||
vim.list_extend(candidates, vim.tbl_map(function(x) return "ask=" .. x end, { "true", "false" }))
|
|
||||||
return candidates
|
|
||||||
end,
|
|
||||||
})
|
})
|
||||||
cmd("Chat", function() require("avante.api").ask({ ask = false }) end, { desc = "avante: chat with the codebase" })
|
cmd("Chat", function(opts)
|
||||||
cmd(
|
local args = Utils.parse_args(opts.fargs)
|
||||||
"ChatNew",
|
args.ask = false
|
||||||
function() require("avante.api").ask({ ask = false, new_chat = true }) end,
|
|
||||||
{ desc = "avante: create new chat" }
|
require("avante.api").ask(args)
|
||||||
)
|
end, {
|
||||||
|
desc = "avante: chat with the codebase",
|
||||||
|
nargs = "*",
|
||||||
|
complete = ask_complete,
|
||||||
|
})
|
||||||
|
cmd("ChatNew", function(opts)
|
||||||
|
local args = Utils.parse_args(opts.fargs)
|
||||||
|
args.ask = false
|
||||||
|
args.new_chat = true
|
||||||
|
require("avante.api").ask(args)
|
||||||
|
end, { desc = "avante: create new chat", nargs = "*", complete = ask_complete })
|
||||||
cmd("Toggle", function() require("avante").toggle() end, { desc = "avante: toggle AI panel" })
|
cmd("Toggle", function() require("avante").toggle() end, { desc = "avante: toggle AI panel" })
|
||||||
cmd("Build", function(opts)
|
cmd("Build", function(opts)
|
||||||
local args = {}
|
local args = Utils.parse_args(opts.fargs)
|
||||||
for _, arg in ipairs(opts.fargs) do
|
|
||||||
local key, value = arg:match("(%w+)=(%w+)")
|
|
||||||
if key and value then args[key] = value == "true" end
|
|
||||||
end
|
|
||||||
if args.source == nil then args.source = false end
|
if args.source == nil then args.source = false end
|
||||||
|
|
||||||
require("avante.api").build(args)
|
require("avante.api").build(args)
|
||||||
@@ -149,11 +160,10 @@ cmd("Clear", function(opts)
|
|||||||
end
|
end
|
||||||
sidebar:clear_history()
|
sidebar:clear_history()
|
||||||
elseif arg == "cache" then
|
elseif arg == "cache" then
|
||||||
local P = require("avante.path")
|
|
||||||
local history_path = P.history_path:absolute()
|
local history_path = P.history_path:absolute()
|
||||||
local cache_path = P.cache_path:absolute()
|
local cache_path = P.cache_path:absolute()
|
||||||
local prompt = string.format("Recursively delete %s and %s?", history_path, cache_path)
|
local prompt = string.format("Recursively delete %s and %s?", history_path, cache_path)
|
||||||
if vim.fn.confirm(prompt, "&Yes\n&No", 2) == 1 then require("avante.path").clear() end
|
if vim.fn.confirm(prompt, "&Yes\n&No", 2) == 1 then P.clear() end
|
||||||
else
|
else
|
||||||
Utils.error("Invalid argument. Valid arguments: 'history', 'memory', 'cache'")
|
Utils.error("Invalid argument. Valid arguments: 'history', 'memory', 'cache'")
|
||||||
return
|
return
|
||||||
|
|||||||
Reference in New Issue
Block a user