- Add preferences.lua module for managing per-project preferences
- Stores preferences in .coder/preferences.json
- Shows floating dialog to ask user on first /@ @/ tag
- Supports toggle between auto/manual modes
- Update autocmds.lua with preference-aware wrapper functions
- check_for_closed_prompt_with_preference()
- check_all_prompts_with_preference()
- Only auto-process when user chose automatic mode
- Add CoderAutoToggle and CoderAutoSet commands
- Toggle between automatic and manual modes
- Set mode directly with :CoderAutoSet auto|manual
- Fix completion.lua to work in directories outside project
- Use current file's directory as base when editing files
outside cwd (e.g., ~/.config/* files)
- Search in both current dir and cwd for completions
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
215 lines
5.7 KiB
Lua
215 lines
5.7 KiB
Lua
---@mod codetyper.preferences User preferences management
|
|
---@brief [[
|
|
--- Manages user preferences stored in .coder/preferences.json
|
|
--- Allows per-project configuration of plugin behavior.
|
|
---@brief ]]
|
|
|
|
local M = {}
|
|
|
|
local utils = require("codetyper.utils")
|
|
|
|
---@class CoderPreferences
|
|
---@field auto_process boolean Whether to auto-process /@ @/ tags (default: nil = ask)
|
|
---@field asked_auto_process boolean Whether we've asked the user about auto_process
|
|
|
|
--- Default preferences
|
|
local defaults = {
|
|
auto_process = nil, -- nil means "not yet decided"
|
|
asked_auto_process = false,
|
|
}
|
|
|
|
--- Cached preferences per project
|
|
---@type table<string, CoderPreferences>
|
|
local cache = {}
|
|
|
|
--- Get the preferences file path for current project
|
|
---@return string
|
|
local function get_preferences_path()
|
|
local cwd = vim.fn.getcwd()
|
|
return cwd .. "/.coder/preferences.json"
|
|
end
|
|
|
|
--- Ensure .coder directory exists
|
|
local function ensure_coder_dir()
|
|
local cwd = vim.fn.getcwd()
|
|
local coder_dir = cwd .. "/.coder"
|
|
if vim.fn.isdirectory(coder_dir) == 0 then
|
|
vim.fn.mkdir(coder_dir, "p")
|
|
end
|
|
end
|
|
|
|
--- Load preferences from file
|
|
---@return CoderPreferences
|
|
function M.load()
|
|
local cwd = vim.fn.getcwd()
|
|
|
|
-- Check cache first
|
|
if cache[cwd] then
|
|
return cache[cwd]
|
|
end
|
|
|
|
local path = get_preferences_path()
|
|
local prefs = vim.deepcopy(defaults)
|
|
|
|
if utils.file_exists(path) then
|
|
local content = utils.read_file(path)
|
|
if content then
|
|
local ok, decoded = pcall(vim.json.decode, content)
|
|
if ok and decoded then
|
|
-- Merge with defaults
|
|
for k, v in pairs(decoded) do
|
|
prefs[k] = v
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Cache it
|
|
cache[cwd] = prefs
|
|
return prefs
|
|
end
|
|
|
|
--- Save preferences to file
|
|
---@param prefs CoderPreferences
|
|
function M.save(prefs)
|
|
local cwd = vim.fn.getcwd()
|
|
ensure_coder_dir()
|
|
|
|
local path = get_preferences_path()
|
|
local ok, encoded = pcall(vim.json.encode, prefs)
|
|
if ok then
|
|
utils.write_file(path, encoded)
|
|
-- Update cache
|
|
cache[cwd] = prefs
|
|
end
|
|
end
|
|
|
|
--- Get a specific preference
|
|
---@param key string
|
|
---@return any
|
|
function M.get(key)
|
|
local prefs = M.load()
|
|
return prefs[key]
|
|
end
|
|
|
|
--- Set a specific preference
|
|
---@param key string
|
|
---@param value any
|
|
function M.set(key, value)
|
|
local prefs = M.load()
|
|
prefs[key] = value
|
|
M.save(prefs)
|
|
end
|
|
|
|
--- Check if auto-process is enabled
|
|
---@return boolean|nil Returns true/false if set, nil if not yet decided
|
|
function M.is_auto_process_enabled()
|
|
return M.get("auto_process")
|
|
end
|
|
|
|
--- Set auto-process preference
|
|
---@param enabled boolean
|
|
function M.set_auto_process(enabled)
|
|
M.set("auto_process", enabled)
|
|
M.set("asked_auto_process", true)
|
|
end
|
|
|
|
--- Check if we've already asked the user about auto-process
|
|
---@return boolean
|
|
function M.has_asked_auto_process()
|
|
return M.get("asked_auto_process") == true
|
|
end
|
|
|
|
--- Ask user about auto-process preference (shows floating window)
|
|
---@param callback function(enabled: boolean) Called with user's choice
|
|
function M.ask_auto_process_preference(callback)
|
|
-- Check if already asked
|
|
if M.has_asked_auto_process() then
|
|
local enabled = M.is_auto_process_enabled()
|
|
if enabled ~= nil then
|
|
callback(enabled)
|
|
return
|
|
end
|
|
end
|
|
|
|
-- Create floating window to ask
|
|
local width = 60
|
|
local height = 7
|
|
local row = math.floor((vim.o.lines - height) / 2)
|
|
local col = math.floor((vim.o.columns - width) / 2)
|
|
|
|
local buf = vim.api.nvim_create_buf(false, true)
|
|
vim.bo[buf].buftype = "nofile"
|
|
vim.bo[buf].bufhidden = "wipe"
|
|
|
|
local win = vim.api.nvim_open_win(buf, true, {
|
|
relative = "editor",
|
|
row = row,
|
|
col = col,
|
|
width = width,
|
|
height = height,
|
|
style = "minimal",
|
|
border = "rounded",
|
|
title = " Codetyper Preferences ",
|
|
title_pos = "center",
|
|
})
|
|
|
|
local lines = {
|
|
"",
|
|
" How would you like to process /@ @/ prompt tags?",
|
|
"",
|
|
" [a] Automatic - Process when you close the tag",
|
|
" [m] Manual - Only process with :CoderProcess",
|
|
"",
|
|
" Press 'a' or 'm' to choose (Esc to cancel)",
|
|
}
|
|
|
|
vim.api.nvim_buf_set_lines(buf, 0, -1, false, lines)
|
|
vim.bo[buf].modifiable = false
|
|
|
|
-- Highlight
|
|
local ns = vim.api.nvim_create_namespace("codetyper_prefs")
|
|
vim.api.nvim_buf_add_highlight(buf, ns, "Title", 1, 0, -1)
|
|
vim.api.nvim_buf_add_highlight(buf, ns, "String", 3, 2, 5)
|
|
vim.api.nvim_buf_add_highlight(buf, ns, "String", 4, 2, 5)
|
|
|
|
local function close_and_callback(enabled)
|
|
if vim.api.nvim_win_is_valid(win) then
|
|
vim.api.nvim_win_close(win, true)
|
|
end
|
|
if enabled ~= nil then
|
|
M.set_auto_process(enabled)
|
|
local mode = enabled and "automatic" or "manual"
|
|
vim.notify("Codetyper: Set to " .. mode .. " mode (saved to .coder/preferences.json)", vim.log.levels.INFO)
|
|
end
|
|
if callback then
|
|
callback(enabled)
|
|
end
|
|
end
|
|
|
|
-- Keymaps
|
|
local opts = { buffer = buf, noremap = true, silent = true }
|
|
vim.keymap.set("n", "a", function() close_and_callback(true) end, opts)
|
|
vim.keymap.set("n", "A", function() close_and_callback(true) end, opts)
|
|
vim.keymap.set("n", "m", function() close_and_callback(false) end, opts)
|
|
vim.keymap.set("n", "M", function() close_and_callback(false) end, opts)
|
|
vim.keymap.set("n", "<Esc>", function() close_and_callback(nil) end, opts)
|
|
vim.keymap.set("n", "q", function() close_and_callback(nil) end, opts)
|
|
end
|
|
|
|
--- Clear cached preferences (useful when changing projects)
|
|
function M.clear_cache()
|
|
cache = {}
|
|
end
|
|
|
|
--- Toggle auto-process mode
|
|
function M.toggle_auto_process()
|
|
local current = M.is_auto_process_enabled()
|
|
local new_value = not current
|
|
M.set_auto_process(new_value)
|
|
local mode = new_value and "automatic" or "manual"
|
|
vim.notify("Codetyper: Switched to " .. mode .. " mode", vim.log.levels.INFO)
|
|
end
|
|
|
|
return M
|