feat: add nvim setting for textobjects

This commit is contained in:
Ray Andrew 2025-12-04 21:48:42 -06:00
parent dc3ee6130e
commit efdb64216f
Signed by: rayandrew
SSH key fingerprint: SHA256:XYrYrxF0Z3A72n8P/p6mqPRNQZT22F88XcLsG+kX4xw
8 changed files with 500 additions and 46 deletions

76
bin/gc
View file

@ -2,6 +2,7 @@
# Conventional commit helper
# Usage: gc [type] [message] [-s scope] [-b] [-B body]
# gc -e [type] (open in editor with template)
# gc (interactive mode)
set -o errexit
@ -10,14 +11,23 @@ set -o nounset
TYPES="feat fix docs style refactor test chore perf ci build"
usage() {
echo "Usage: gc [type] [message] [-s scope] [-b] [-B body]"
echo "Usage: gc [type] [message] [--scope scope] [-b] [-B body]"
echo ' gc -e [type] # open in $EDITOR'
echo " gc (interactive mode)"
echo ""
echo "Options:"
echo " -s, --scope Scope of the change"
echo ' -e, --edit Open commit message in $EDITOR'
echo " --scope Scope of the change"
echo " -b, --breaking Mark as breaking change (!)"
echo " -B, --body Commit body (longer description)"
echo ""
echo "Git passthrough flags:"
echo " -s, --signoff Add Signed-off-by trailer"
echo " -a, --all Stage all modified files"
echo " -S, --gpg-sign GPG sign the commit"
echo " -v, --verbose Show diff in editor"
echo " --amend Amend previous commit"
echo ""
echo "Types: $TYPES"
echo ""
echo "Examples:"
@ -26,9 +36,46 @@ usage() {
echo " gc feat 'new api' -b # feat!: new api"
echo " gc feat 'new api' -s auth -b # feat(auth)!: new api"
echo " gc feat 'big change' -B 'Details here'"
echo ' gc -e feat # edit feat commit in $EDITOR'
exit 1
}
editor_mode() {
local type="$1"
shift
local git_args=("$@")
local template
template=$(mktemp)
trap "rm -f $template" EXIT
cat >"$template" <<'EOF'
# Conventional Commit Format:
# <type>(<scope>)!: <description>
#
# [optional body]
#
# [optional footer(s)]
#
# Types: feat fix docs style refactor test chore perf ci build
# Add ! before : for breaking changes (e.g., feat!: or feat(api)!:)
#
# Examples:
# feat: add user authentication
# fix(api): resolve null pointer exception
# feat(auth)!: change token format
# docs: update API documentation
EOF
# Prepend type if provided
if [ -n "$type" ]; then
sed -i.bak "1s/^/$type: /" "$template" && rm -f "$template.bak"
fi
git commit -e -t "$template" "${git_args[@]}"
}
build_message() {
local type="$1"
local scope="$2"
@ -85,10 +132,16 @@ msg=""
scope=""
breaking="false"
body=""
use_editor="false"
git_args=()
while [ $# -gt 0 ]; do
case "$1" in
-s | --scope)
-e | --edit)
use_editor="true"
shift
;;
--scope)
scope="$2"
shift 2
;;
@ -103,6 +156,15 @@ while [ $# -gt 0 ]; do
-h | --help)
usage
;;
# Pass through git commit flags
-s | --signoff | -a | --all | -v | --verbose | -n | --no-verify | --amend | --no-edit)
git_args+=("$1")
shift
;;
-S | --gpg-sign)
git_args+=("$1")
shift
;;
*)
if [ -z "$type" ]; then
type="$1"
@ -114,6 +176,12 @@ while [ $# -gt 0 ]; do
esac
done
# Editor mode
if [ "$use_editor" = "true" ]; then
editor_mode "$type" "${git_args[@]}"
exit 0
fi
# Validate type
if ! echo "$TYPES" | grep -qw "$type"; then
echo "Invalid type: $type"
@ -127,4 +195,4 @@ if [ -z "$msg" ]; then
fi
commit_msg=$(build_message "$type" "$scope" "$breaking" "$msg" "$body")
git commit -m "$commit_msg"
git commit -m "$commit_msg" "${git_args[@]}"

39
config/git/config Normal file
View file

@ -0,0 +1,39 @@
[user]
name = Ray Andrew
email = rs@rs.ht
signingkey = ~/.ssh/id_ed25519.pub
[gpg]
format = ssh
[core]
editor = nvim
[credential]
helper = store --file ~/.git-credentials
[pull]
rebase = true
[github]
user = rayandrew
[commit]
gpgsign = true
[format]
signoff = true
[lfs]
repositoryformatversion = 0
[filter "lfs"]
clean = git-lfs clean -- %f
smudge = git-lfs smudge -- %f
process = git-lfs filter-process
required = true
[alias]
c = !~/dotfiles/bin/gc -s
cc = !~/dotfiles/bin/gc-check
cl = !~/dotfiles/bin/gc-changelog

3
config/git/ignore Normal file
View file

@ -0,0 +1,3 @@
.DS_Store
*~
*.swp

View file

@ -57,6 +57,9 @@ add 'aserowy/tmux.nvim'
add 'NMAC427/guess-indent.nvim'
add 'wakatime/vim-wakatime'
add 'OXY2DEV/markview.nvim'
add { source = 'R-nvim/R.nvim', depends = { 'nvim-treesitter/nvim-treesitter' } }
add 'nvim-treesitter/nvim-treesitter'
add 'nvim-treesitter/nvim-treesitter-textobjects'
-- color themes
add 'EdenEast/nightfox.nvim'
@ -279,6 +282,112 @@ later(function()
}
require('tmux').setup {}
-- Ensure treesitter parsers are installed
require('nvim-treesitter.configs').setup {
ensure_installed = {
'r',
'rnoweb',
'lua',
'python',
'rust',
'c',
'cpp',
'javascript',
'typescript',
'tsx',
'json',
'yaml',
'html',
'css',
'markdown',
'markdown_inline',
'nix',
'bash',
'vim',
'vimdoc',
'query',
'diff',
'git_rebase',
'gitcommit',
},
auto_install = true,
highlight = { enable = true },
incremental_selection = {
enable = true,
keymaps = {
init_selection = 'vv',
node_incremental = '<Tab>',
scope_incremental = '<C-Tab>',
node_decremental = '<S-Tab>',
},
},
textobjects = {
select = {
enable = true,
lookahead = true,
keymaps = {
['af'] = { query = '@function.outer', desc = 'Select outer function' },
['if'] = { query = '@function.inner', desc = 'Select inner function' },
['ac'] = { query = '@class.outer', desc = 'Select outer class' },
['ic'] = { query = '@class.inner', desc = 'Select inner class' },
['aa'] = { query = '@parameter.outer', desc = 'Select outer argument' },
['ia'] = { query = '@parameter.inner', desc = 'Select inner argument' },
['ai'] = { query = '@conditional.outer', desc = 'Select outer conditional' },
['ii'] = { query = '@conditional.inner', desc = 'Select inner conditional' },
['al'] = { query = '@loop.outer', desc = 'Select outer loop' },
['il'] = { query = '@loop.inner', desc = 'Select inner loop' },
['ab'] = { query = '@block.outer', desc = 'Select outer block' },
['ib'] = { query = '@block.inner', desc = 'Select inner block' },
},
},
move = {
enable = true,
set_jumps = true,
goto_next_start = {
[']f'] = { query = '@function.outer', desc = 'Next function start' },
[']c'] = { query = '@class.outer', desc = 'Next class start' },
[']a'] = { query = '@parameter.inner', desc = 'Next argument' },
},
goto_next_end = {
[']F'] = { query = '@function.outer', desc = 'Next function end' },
[']C'] = { query = '@class.outer', desc = 'Next class end' },
},
goto_previous_start = {
['[f'] = { query = '@function.outer', desc = 'Previous function start' },
['[c'] = { query = '@class.outer', desc = 'Previous class start' },
['[a'] = { query = '@parameter.inner', desc = 'Previous argument' },
},
goto_previous_end = {
['[F'] = { query = '@function.outer', desc = 'Previous function end' },
['[C'] = { query = '@class.outer', desc = 'Previous class end' },
},
},
swap = {
enable = true,
swap_next = {
['<leader>a'] = { query = '@parameter.inner', desc = 'Swap with next argument' },
},
swap_previous = {
['<leader>A'] = { query = '@parameter.inner', desc = 'Swap with previous argument' },
},
},
},
}
require('r').setup {
hook = {
on_filetype = function()
vim.keymap.set('n', '<leader>rs', '<Plug>RStart', { buffer = true, desc = 'Start R' })
vim.keymap.set('n', '<leader>rq', '<Plug>RClose', { buffer = true, desc = 'Close R' })
vim.keymap.set('n', '<leader>rl', '<Plug>RSendLine', { buffer = true, desc = 'Send line to R' })
vim.keymap.set('v', '<leader>rs', '<Plug>RSendSelection', { buffer = true, desc = 'Send selection to R' })
vim.keymap.set('n', '<leader>rf', '<Plug>RSendFile', { buffer = true, desc = 'Send file to R' })
vim.keymap.set('n', '<leader>ro', '<Plug>RShowArgs', { buffer = true, desc = 'Show function args' })
vim.keymap.set('n', '<leader>rh', '<Plug>RHelp', { buffer = true, desc = 'R help' })
end,
},
}
end)
later(function()
@ -530,8 +639,15 @@ later(function()
root_markers = { '.marksman.toml', '.git', '.editorconfig' },
}
-- R
vim.lsp.config.r_language_server = {
cmd = { 'R', '--slave', '-e', 'languageserver::run()' },
filetypes = { 'r', 'rmd' },
root_markers = { '.Rproj', 'DESCRIPTION', '.git' },
}
-- Enable language servers
vim.lsp.enable { 'lua_ls', 'ts_ls', 'pyright', 'rust_analyzer', 'clangd', 'copilot', 'nixd', 'marksman' }
vim.lsp.enable { 'lua_ls', 'ts_ls', 'pyright', 'rust_analyzer', 'clangd', 'copilot', 'nixd', 'marksman', 'r_language_server' }
end)
local map = function(mode, lhs, rhs, opts)
@ -650,3 +766,22 @@ vim.api.nvim_create_autocmd({ 'FocusGained', 'BufEnter' }, {
command = "if mode() != 'c' | checktime | endif",
pattern = '*',
})
-- Restore gitrebase keybindings (overridden by clipboard=unnamedplus)
vim.api.nvim_create_autocmd('FileType', {
pattern = 'gitrebase',
callback = function()
local opts = { buffer = true, silent = true }
vim.keymap.set('n', 'p', '<cmd>Pick<cr>', opts)
vim.keymap.set('n', 'r', '<cmd>Reword<cr>', opts)
vim.keymap.set('n', 'e', '<cmd>Edit<cr>', opts)
vim.keymap.set('n', 's', '<cmd>Squash<cr>', opts)
vim.keymap.set('n', 'f', '<cmd>Fixup<cr>', opts)
vim.keymap.set('n', 'd', '<cmd>Drop<cr>', opts)
vim.keymap.set('n', 'x', '<cmd>Exec<cr>', opts)
vim.keymap.set('n', '<C-n>', '<cmd>move +1<cr>', opts)
vim.keymap.set('n', '<C-p>', '<cmd>move -2<cr>', opts)
vim.keymap.set('n', '<C-j>', '<cmd>move +1<cr>', opts)
vim.keymap.set('n', '<C-k>', '<cmd>move -2<cr>', opts)
end,
})

View file

@ -439,7 +439,7 @@ local function apply_highlights(c, opts)
hi('MarkviewCode', { bg = c.bg_dark })
hi('MarkviewCodeInfo', { fg = c.comment })
hi('MarkviewCodeFg', { fg = c.fg })
hi('MarkviewInlineCode', { fg = c.string, bg = c.bg_dark })
hi('MarkviewInlineCode', { fg = c.string, bg = c.bg_float })
-- Lists
hi('MarkviewListItemStar', { fg = c.tag })

244
docs/nvim.md Normal file
View file

@ -0,0 +1,244 @@
# Neovim Configuration
Leader key: `<Space>`
## Files & Buffers
| Key | Action |
|-----|--------|
| `<leader>ex` | Toggle file tree |
| `<leader>fs` | Save file |
| `<leader>ff` | Find file |
| `<leader>fr` | Find recent file |
| `<leader>sf` | Search git files |
| `<leader>bb` | List buffers |
| `<leader><leader>` | List buffers |
| `<leader>bd` | Delete buffer |
| `<leader>.` | Scratch buffer |
## Search
| Key | Action |
|-----|--------|
| `<leader>sg` | Search grep |
| `<leader>sw` | Search grep word |
| `<leader>sb` | Buffer lines |
| `<leader>sB` | Grep open buffers |
| `<leader>:` | Command history |
## LSP
| Key | Action |
|-----|--------|
| `gd` | Goto definition |
| `gD` | Goto declaration |
| `gr` | References |
| `gI` | Goto implementation |
| `gy` | Goto type definition |
| `gai` | Incoming calls |
| `gao` | Outgoing calls |
| `K` | Hover documentation |
| `<leader>rn` | Rename symbol |
| `<leader>ca` | Code action |
| `<leader>d` | Show diagnostic |
| `[d` / `]d` | Previous / next diagnostic |
| `<leader>ls` | LSP symbols |
| `<leader>lS` | LSP workspace symbols |
## Git
| Key | Action |
|-----|--------|
| `<leader>gg` | Open Neogit |
| `<leader>gb` | Git branches |
| `<leader>gl` | Git log |
| `<leader>gL` | Git log (file) |
| `<leader>gs` | Git status |
| `<leader>gS` | Git stash |
| `<leader>gd` | Git diff (hunks) |
| `<leader>gi` | GitHub issues (open) |
| `<leader>gI` | GitHub issues (all) |
| `<leader>gp` | GitHub PRs (open) |
| `<leader>gP` | GitHub PRs (all) |
## Grapple (Harpoon-style marks)
| Key | Action |
|-----|--------|
| `<leader>ma` | Toggle tag |
| `<leader>mm` | Tags menu |
| `<leader>m1-4` | Select tag 1-4 |
| `<leader>mn` | Next tag |
| `<leader>mp` | Previous tag |
| `<leader>mga` | Add global tag |
| `<leader>mgm` | Global tags menu |
## Claude Code
| Key | Action |
|-----|--------|
| `<leader>ac` | Toggle Claude |
| `<leader>af` | Focus Claude |
| `<leader>ar` | Resume Claude |
| `<leader>aC` | Continue Claude |
| `<leader>am` | Select Claude model |
| `<leader>ab` | Add current buffer |
| `<leader>as` | Send to Claude (visual) |
| `<leader>aa` | Accept diff |
| `<leader>ad` | Deny diff |
## Code / Config
| Key | Action |
|-----|--------|
| `<leader>cc` | Compile |
| `<leader>cC` | Recompile |
| `<leader>cf` | Format buffer |
| `<leader>cr` | Reload config |
| `<leader>ce` | Edit config |
| `<leader>ch` | Check health |
## Windows
| Key | Action |
|-----|--------|
| `<leader>wh/j/k/l` | Navigate windows |
| `<leader>ws` | Split horizontal |
| `<leader>wv` | Split vertical |
| `<leader>wq` | Close window |
| `<C-h/j/k/l>` | Move to tmux pane |
## Quickfix
| Key | Action |
|-----|--------|
| `<leader>qq` | Open/focus quickfix |
| `<leader>ql` | Toggle loclist |
| `>` | Expand quickfix context |
| `<` | Collapse quickfix context |
## Utilities
| Key | Action |
|-----|--------|
| `<leader>ut` | Toggle undotree |
| `<leader>zz` | Toggle zen mode |
| `<leader>zm` | Toggle zoom |
## R (in R files)
| Key | Action |
|-----|--------|
| `<leader>rs` | Start R |
| `<leader>rq` | Close R |
| `<leader>rl` | Send line to R |
| `<leader>rs` | Send selection to R (visual) |
| `<leader>rf` | Send file to R |
| `<leader>ro` | Show function args |
| `<leader>rh` | R help |
## Treesitter
### Incremental Selection
Select code based on AST nodes - expand or shrink selection intelligently.
| Key | Action |
|-----|--------|
| `vv` | Start treesitter selection |
| `<Tab>` | Expand to larger node |
| `<S-Tab>` | Shrink to smaller node |
| `<C-Tab>` | Expand to full scope |
### Text Objects
Use with operators like `v` (visual), `d` (delete), `c` (change), `y` (yank).
| Key | Description |
|-----|-------------|
| `af` / `if` | Outer / inner function |
| `ac` / `ic` | Outer / inner class |
| `aa` / `ia` | Outer / inner argument |
| `ai` / `ii` | Outer / inner conditional |
| `al` / `il` | Outer / inner loop |
| `ab` / `ib` | Outer / inner block |
Examples:
- `vaf` - select entire function
- `dif` - delete function body
- `caa` - change an argument
- `yic` - yank inner class
### Movement
Jump between functions, classes, and arguments.
| Key | Action |
|-----|--------|
| `]f` / `[f` | Next / previous function start |
| `]F` / `[F` | Next / previous function end |
| `]c` / `[c` | Next / previous class start |
| `]C` / `[C` | Next / previous class end |
| `]a` / `[a` | Next / previous argument |
### Swap
| Key | Action |
|-----|--------|
| `<leader>a` | Swap argument with next |
| `<leader>A` | Swap argument with previous |
## Git Rebase (in gitrebase files)
| Key | Action |
|-----|--------|
| `p` | Pick |
| `r` | Reword |
| `e` | Edit |
| `s` | Squash |
| `f` | Fixup |
| `d` | Drop |
| `x` | Exec |
| `<C-n>` / `<C-j>` | Move line down |
| `<C-p>` / `<C-k>` | Move line up |
## Terminal
| Key | Action |
|-----|--------|
| `<C-q>` | Exit terminal mode |

View file

@ -148,6 +148,7 @@
treefmtEval.${system}.config.build.wrapper
];
packages = with pkgs; [
git
sops
age
ssh-to-age

View file

@ -1,52 +1,16 @@
{
pkgs,
config,
user,
dots,
...
}:
let
home = config.home.homeDirectory;
in
{
home.packages = with pkgs; [
gh
git-lfs
];
programs.git = {
enable = true;
lfs = {
enable = true;
xdg.configFile = {
"git".source = config.lib.file.mkOutOfStoreSymlink "${dots}/config/git";
};
settings = {
user = {
name = "Ray Andrew";
email = "rs@rs.ht";
};
gpg.format = "ssh";
core.editor = "emacs";
credential.helper = "store --file ${home}/.git-credentials";
pull.rebase = true;
github.user = user;
};
signing = {
key = "${home}/.ssh/id_ed25519.pub";
signByDefault = true;
signer = "";
};
ignores = [
".DS_Store"
"*~"
"*.swp"
];
};
# programs.gh = {
# enable = true;
# settings = {
# git_protocol = "ssh";
# };
# extensions = with pkgs; [
# gh-copilot
# ];
# };
}