add telescope_tasks, nvim lua modules, and doc updates
- add telescope_tasks.lua: vault-wide task pickers (<leader>tl / <leader>tL) - track mappings.lua and rename_term.lua in dotfiles - update CLAUDE.md: add vault definition, telescope_tasks entry, nvim lua file list - fix tdo/tdop aliases and ga/gx aliases in .bashrc - install_nvchad.sh: symlink personal nvim modules after clone Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
2f695def41
commit
1e4fa75dc7
@ -59,7 +59,10 @@ Symlink-based deployment. Editing `~/.<file>` directly edits the repo file. `git
|
||||
- `dotfiles/.bashrc` — main shell config
|
||||
- `dotfiles/.vimrc`, `.gitconfig`, `.tmux.conf`, `.psqlrc`, `.pspgconf`
|
||||
- `dotfiles/bin/td` — time-tracking CLI
|
||||
- `dotfiles/nvim/td.lua`, `td_mappings.lua` — nvim lua modules
|
||||
- `dotfiles/nvim/mappings.lua` — all nvim keybindings
|
||||
- `dotfiles/nvim/td.lua`, `td_mappings.lua` — nvim time-tracking integration
|
||||
- `dotfiles/nvim/rename_term.lua` — vault-wide find-and-replace with confirmation popup (`<leader>rn`)
|
||||
- `dotfiles/nvim/telescope_tasks.lua` — vault-wide task pickers (`<leader>tl` open tasks, `<leader>tL` priority tasks)
|
||||
|
||||
**Gitignored** (machine-specific, auto-bootstrapped from examples):
|
||||
- `dotfiles/.bashrc_local` — secrets and credentials
|
||||
|
||||
@ -129,14 +129,14 @@ xmspa() {
|
||||
}
|
||||
|
||||
alias gs='git status -s'
|
||||
alias ga='git status --untracked-files=all -s | fzf -m | awk "{print \$2}" | xargs git add '
|
||||
alias gx='git status --untracked-files=all -s | fzf -m | awk "{print \$2}" | xargs git checkout '
|
||||
alias ga='git status --untracked-files=all -s | fzf -m | while IFS= read -r line; do git add "${line:3}"; done'
|
||||
alias gx='git status --untracked-files=all -s | fzf -m | while IFS= read -r line; do git checkout "${line:3}"; done'
|
||||
alias td='rg "\- \[[^(x|~)]\]"'
|
||||
alias tdp='rg "\- \[[^(x|~)]\].*(🔼|⏫)"'
|
||||
alias tdtp='rg "\- \[[^(x|~)]\].*⏫"'
|
||||
# alias tdo='rg "\- \[[^x]\]" | fzf | xargs nvim'
|
||||
alias tdo='rg "\- \[[^(x|~)]\]" --line-number | fzf | awk -F: "{print \$1, \"+\"\$2}" | xargs -r nvim'
|
||||
alias tdop='rg "\- \[[^(x|~)]\].*(🔼|⏫)" --line-number | fzf | awk -F: "{print \$1, \"+\"\$2}" | xargs -r nvim'
|
||||
alias tdo='rg "\- \[[^(x|~)]\]" --line-number | fzf | { IFS=: read -r file line rest && nvim "$file" "+$line"; }'
|
||||
alias tdop='rg "\- \[[^(x|~)]\].*(🔼|⏫)" --line-number | fzf | { IFS=: read -r file line rest && nvim "$file" "+$line"; }'
|
||||
|
||||
# time-track markdown todos — see `command td --help`. Log at $TD_LOG (default ./time.csv)
|
||||
# `command td` bypasses the `td` alias (which is rg for finding todos).
|
||||
|
||||
78
dotfiles/nvim/mappings.lua
Normal file
78
dotfiles/nvim/mappings.lua
Normal file
@ -0,0 +1,78 @@
|
||||
require "nvchad.mappings"
|
||||
|
||||
-- add yours here
|
||||
|
||||
local map = vim.keymap.set
|
||||
|
||||
map("n", ";", ":", { desc = "CMD enter command mode" })
|
||||
map("i", "jk", "<ESC>")
|
||||
|
||||
-- map({ "n", "i", "v" }, "<C-s>", "<cmd> w <cr>")
|
||||
|
||||
-- open tasks / priority tasks vault-wide (telescope_tasks.lua)
|
||||
map('n', '<leader>tl', function() require('telescope_tasks').open_tasks() end, { desc = "telescope: open tasks" })
|
||||
map('n', '<leader>tL', function() require('telescope_tasks').priority_tasks() end, { desc = "telescope: priority tasks" })
|
||||
-- Add a keybinding for calling ObsidianTag
|
||||
vim.api.nvim_set_keymap('n', '<leader>tt', ':ObsidianTag<CR>', { noremap = true, silent = true })
|
||||
-- Add a keybinding for calling ObsidianBacklinks
|
||||
vim.api.nvim_set_keymap('n', '<leader>lb', ':ObsidianBacklinks<CR>', { noremap = true, silent = true })
|
||||
-- Add a keybinding for calling ObsidianLink
|
||||
vim.api.nvim_set_keymap('v', '<leader>fl', ':ObsidianLink<CR>', { noremap = true, silent = true })
|
||||
-- priority task
|
||||
vim.api.nvim_set_keymap("n", "<leader>p1", "A 🔼<Esc>", { noremap = true, silent = true })
|
||||
vim.api.nvim_set_keymap("n", "<leader>p2", "A ⏫<Esc>", { noremap = true, silent = true })
|
||||
|
||||
-- Live grep all files (including gitignored and hidden files)
|
||||
vim.keymap.set("n", "<leader>fW", function()
|
||||
require('telescope.builtin').live_grep({
|
||||
additional_args = function()
|
||||
return { "--hidden", "--no-ignore" }
|
||||
end
|
||||
})
|
||||
end, { desc = "telescope live grep all files" })
|
||||
|
||||
-- make leader-e toggle the tree view as opposed to just setting focus
|
||||
vim.api.nvim_set_keymap('n', '<leader>e', ':NvimTreeToggle<CR>', {noremap = true, silent = true})
|
||||
|
||||
-- move the whole page without moving the cursor
|
||||
vim.api.nvim_set_keymap('n', 'J', '<C-e>', { noremap = true })
|
||||
vim.keymap.set('n', '<leader>j', 'J', { remap = false, desc = "Join lines" })
|
||||
vim.api.nvim_set_keymap('n', 'K', '<C-y>', { noremap = true })
|
||||
|
||||
-- Resize windows
|
||||
vim.api.nvim_set_keymap('n', '<Up>', '5<C-w>+', { silent = true })
|
||||
vim.api.nvim_set_keymap('n', '<Down>', '5<C-w>-', { silent = true })
|
||||
vim.api.nvim_set_keymap('n', '<Right>', '10<C-w>>', { silent = true })
|
||||
vim.api.nvim_set_keymap('n', '<Left>', '10<C-w><', { silent = true })
|
||||
|
||||
-- wrap selected text in single quotes
|
||||
vim.keymap.set('x', "<leader>'", function()
|
||||
local text = vim.fn.getreg('"') -- Get the visually selected text
|
||||
vim.cmd("normal! c'" .. text .. "'")
|
||||
end, { desc = "Wrap selected text in single quotes" })
|
||||
|
||||
-- wrap selected text in double quotes
|
||||
vim.keymap.set('x', '<leader>"', function()
|
||||
local text = vim.fn.getreg('"') -- Get the visually selected text
|
||||
vim.cmd('normal! c"' .. text .. '"')
|
||||
end, { desc = "Wrap selected text in double quotes" })
|
||||
|
||||
-- Gitsigns blame current line
|
||||
vim.keymap.set("n", "<leader>gb", ":Gitsigns blame_line<CR>", { noremap = true, silent = true, desc = "Git blame line" })
|
||||
|
||||
-- Format current buffer (uses conform; for markdown this snaps tables via mdformat-gfm)
|
||||
vim.keymap.set({ "n", "v" }, "<leader>fm", function()
|
||||
require("conform").format({ async = true, lsp_fallback = true })
|
||||
end, { desc = "Format buffer / selection" })
|
||||
|
||||
-- Disable alt+h and alt+v terminal toggles
|
||||
vim.keymap.del({ "n", "t" }, "<A-h>")
|
||||
vim.keymap.del({ "n", "t" }, "<A-v>")
|
||||
|
||||
-- Vault-wide find-and-replace with confirmation popup
|
||||
vim.keymap.set("n", "<leader>rn", function()
|
||||
require("rename_term").run()
|
||||
end, { desc = "Rename term across vault (find → replace)" })
|
||||
|
||||
-- td: time-track markdown todos — all in ~/setup_env/dotfiles/nvim/{td,td_mappings}.lua
|
||||
require("td_mappings")
|
||||
169
dotfiles/nvim/rename_term.lua
Normal file
169
dotfiles/nvim/rename_term.lua
Normal file
@ -0,0 +1,169 @@
|
||||
-- rename_term.lua
|
||||
-- Vault-wide find-and-replace with a confirmation popup.
|
||||
-- Usage: require("rename_term").run()
|
||||
-- Keybinding: <leader>rn
|
||||
|
||||
local M = {}
|
||||
|
||||
local function get_vault_root()
|
||||
-- prefer obsidian vault root, fall back to cwd
|
||||
local ok, obs = pcall(require, "obsidian")
|
||||
if ok and obs.get_client then
|
||||
local client = obs.get_client()
|
||||
if client and client.dir then
|
||||
return tostring(client.dir)
|
||||
end
|
||||
end
|
||||
return vim.fn.getcwd()
|
||||
end
|
||||
|
||||
local function show_popup(lines, on_confirm)
|
||||
local width = math.min(80, vim.o.columns - 4)
|
||||
local height = math.min(#lines + 4, vim.o.lines - 6)
|
||||
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.api.nvim_buf_set_option(buf, "bufhidden", "wipe")
|
||||
|
||||
local display = { "Affected files — press y to apply, n/q to cancel:", "" }
|
||||
for _, l in ipairs(lines) do
|
||||
table.insert(display, " " .. l)
|
||||
end
|
||||
table.insert(display, "")
|
||||
table.insert(display, string.format(" %d file(s) will be modified.", #lines))
|
||||
|
||||
vim.api.nvim_buf_set_lines(buf, 0, -1, false, display)
|
||||
vim.api.nvim_buf_set_option(buf, "modifiable", false)
|
||||
|
||||
local win = vim.api.nvim_open_win(buf, true, {
|
||||
relative = "editor",
|
||||
width = width,
|
||||
height = height,
|
||||
row = row,
|
||||
col = col,
|
||||
style = "minimal",
|
||||
border = "rounded",
|
||||
})
|
||||
|
||||
local function close()
|
||||
if vim.api.nvim_win_is_valid(win) then
|
||||
vim.api.nvim_win_close(win, true)
|
||||
end
|
||||
end
|
||||
|
||||
local opts = { buffer = buf, nowait = true, silent = true }
|
||||
|
||||
vim.keymap.set("n", "y", function()
|
||||
close()
|
||||
on_confirm()
|
||||
end, opts)
|
||||
|
||||
vim.keymap.set("n", "n", close, opts)
|
||||
vim.keymap.set("n", "q", close, opts)
|
||||
vim.keymap.set("n", "<Esc>", close, opts)
|
||||
end
|
||||
|
||||
function M.run(search, replace)
|
||||
local vault = get_vault_root()
|
||||
|
||||
-- prompt if not passed as args
|
||||
if not search or search == "" then
|
||||
search = vim.fn.input("Search: ")
|
||||
if search == "" then
|
||||
print("rename_term: search term is empty, aborting.")
|
||||
return
|
||||
end
|
||||
end
|
||||
if not replace or replace == "" then
|
||||
replace = vim.fn.input("Replace: ")
|
||||
-- allow replacing with empty string intentionally
|
||||
end
|
||||
|
||||
-- find affected files (content)
|
||||
local raw = vim.fn.systemlist(
|
||||
string.format("grep -rl --include='*.md' -F %q %q", search, vault)
|
||||
)
|
||||
|
||||
-- find affected file names
|
||||
local named = vim.fn.systemlist(
|
||||
string.format("find %q -name %q", vault, "*" .. search .. "*")
|
||||
)
|
||||
|
||||
-- deduplicate
|
||||
local seen = {}
|
||||
local content_files = {}
|
||||
for _, f in ipairs(raw) do
|
||||
if not seen[f] then
|
||||
seen[f] = true
|
||||
table.insert(content_files, f)
|
||||
end
|
||||
end
|
||||
|
||||
if #content_files == 0 and #named == 0 then
|
||||
vim.notify('rename_term: no matches for "' .. search .. '"', vim.log.levels.WARN)
|
||||
return
|
||||
end
|
||||
|
||||
-- build display list
|
||||
local display = {}
|
||||
for _, f in ipairs(content_files) do
|
||||
table.insert(display, vim.fn.fnamemodify(f, ":~:.") .. " [content]")
|
||||
end
|
||||
for _, f in ipairs(named) do
|
||||
table.insert(display, vim.fn.fnamemodify(f, ":~:.") .. " [filename]")
|
||||
end
|
||||
|
||||
show_popup(display, function()
|
||||
local errors = {}
|
||||
|
||||
-- 1. replace content in all matched files
|
||||
for _, f in ipairs(content_files) do
|
||||
-- escape for sed: & / \ need escaping
|
||||
local function sed_escape(s)
|
||||
return s:gsub("([&/\\])", "\\%1")
|
||||
end
|
||||
local cmd = string.format(
|
||||
"sed -i 's/%s/%s/g' %q",
|
||||
sed_escape(search), sed_escape(replace), f
|
||||
)
|
||||
local result = vim.fn.system(cmd)
|
||||
if vim.v.shell_error ~= 0 then
|
||||
table.insert(errors, "content: " .. f .. " — " .. result)
|
||||
end
|
||||
end
|
||||
|
||||
-- 2. rename files whose names contain the search term
|
||||
for _, f in ipairs(named) do
|
||||
local dir = vim.fn.fnamemodify(f, ":h")
|
||||
local base = vim.fn.fnamemodify(f, ":t")
|
||||
local newbase = base:gsub(vim.pesc(search), replace)
|
||||
local newpath = dir .. "/" .. newbase
|
||||
if newbase ~= base then
|
||||
local ok, err = os.rename(f, newpath)
|
||||
if not ok then
|
||||
table.insert(errors, "rename: " .. f .. " — " .. (err or "unknown"))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- reload any open buffers whose files were touched
|
||||
for _, f in ipairs(content_files) do
|
||||
local bufnr = vim.fn.bufnr(f)
|
||||
if bufnr ~= -1 and vim.api.nvim_buf_is_loaded(bufnr) then
|
||||
vim.api.nvim_buf_call(bufnr, function() vim.cmd("edit!") end)
|
||||
end
|
||||
end
|
||||
|
||||
if #errors > 0 then
|
||||
vim.notify("rename_term errors:\n" .. table.concat(errors, "\n"), vim.log.levels.ERROR)
|
||||
else
|
||||
vim.notify(string.format(
|
||||
'rename_term: replaced "%s" → "%s" in %d file(s), renamed %d file(s).',
|
||||
search, replace, #content_files, #named
|
||||
), vim.log.levels.INFO)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
return M
|
||||
38
dotfiles/nvim/telescope_tasks.lua
Normal file
38
dotfiles/nvim/telescope_tasks.lua
Normal file
@ -0,0 +1,38 @@
|
||||
local telescope = require('telescope.builtin')
|
||||
|
||||
local M = {}
|
||||
|
||||
local function vault_root()
|
||||
local path = vim.fn.expand('%:p:h')
|
||||
local home = vim.fn.expand('~')
|
||||
while path ~= home and path ~= '/' do
|
||||
if vim.fn.filereadable(path .. '/time.csv') == 1
|
||||
or vim.fn.isdirectory(path .. '/.obsidian') == 1 then
|
||||
return path
|
||||
end
|
||||
path = vim.fn.fnamemodify(path, ':h')
|
||||
end
|
||||
return vim.fn.getcwd()
|
||||
end
|
||||
|
||||
-- all open tasks vault-wide (equivalent to tdo shell alias)
|
||||
function M.open_tasks()
|
||||
telescope.grep_string({
|
||||
prompt_title = "Open Tasks",
|
||||
search = "\\- \\[[^x~]\\]",
|
||||
use_regex = true,
|
||||
cwd = vault_root(),
|
||||
})
|
||||
end
|
||||
|
||||
-- priority open tasks vault-wide (equivalent to tdop shell alias)
|
||||
function M.priority_tasks()
|
||||
telescope.grep_string({
|
||||
prompt_title = "Priority Tasks",
|
||||
search = "\\- \\[[^x~]\\].*(🔼|⏫)",
|
||||
use_regex = true,
|
||||
cwd = vault_root(),
|
||||
})
|
||||
end
|
||||
|
||||
return M
|
||||
@ -82,6 +82,20 @@ if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Overlay personal lua modules from setup_env (symlinks override cloned files)
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
NVIM_DOTFILES="$SCRIPT_DIR/dotfiles/nvim"
|
||||
if [ -d "$NVIM_DOTFILES" ]; then
|
||||
echo "Symlinking personal nvim modules from $NVIM_DOTFILES ..."
|
||||
for mod in "$NVIM_DOTFILES"/*.lua; do
|
||||
[ -f "$mod" ] || continue
|
||||
target="$HOME/.config/nvim/lua/$(basename "$mod")"
|
||||
[ -L "$target" ] && rm "$target"
|
||||
ln -s "$mod" "$target"
|
||||
echo " linked: $(basename "$mod")"
|
||||
done
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Starting installation..."
|
||||
echo ""
|
||||
|
||||
Loading…
Reference in New Issue
Block a user