diff --git a/bootstrap.sh b/bootstrap.sh index bed56a8..61f3a61 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -38,6 +38,7 @@ echo "Setting up NeoVim" test -d $HOME/.config/nvim || mkdir -p $HOME/.config/nvim test -d $HOME/.local/share/nvim || mkdir -p $HOME/.local/share/nvim test -L $HOME/.config/nvim/init.lua || ln -f -s $BASEDIR/neovim/init.lua $HOME/.config/nvim/init.lua +test -L $HOME/.config/nvim/nvim-pack-lock.json || ln -f -s $BASEDIR/neovim/nvim-pack-lock.json $HOME/.config/nvim/nvim-pack-lock.json test -d $HOME/.config/nvim/lua || mkdir -p $HOME/.config/nvim/lua test -L $HOME/.config/nvim/lua/config || ln -f -s $BASEDIR/neovim/config $HOME/.config/nvim/lua/config test -d $HOME/.config/nvim/lsp || mkdir -p $HOME/.config/nvim/lsp diff --git a/brew.sh b/brew.sh index deee053..2b49611 100755 --- a/brew.sh +++ b/brew.sh @@ -69,7 +69,11 @@ brew install zoxide # Install some casks brew install --cask 1password-cli brew install --cask airbuddy -brew install --cask claude-code +# Deliberately no claude-code cask: brew tracks stable, we want newer +# releases, so it's installed via Claude's own installer into ~/.local/bin + +# Terminal font for ghostty; also supplies the glyphs nvim-web-devicons needs +brew install --cask font-hack-nerd-font brew install --cask ngrok brew install --cask silentknight brew install --cask sublime-text diff --git a/neovim/config/diagnostics.lua b/neovim/config/diagnostics.lua index d09ad3f..366a246 100644 --- a/neovim/config/diagnostics.lua +++ b/neovim/config/diagnostics.lua @@ -16,3 +16,8 @@ vim.diagnostic.config({ }, }, }) + +-- Nvim already binds ]d / [d to jump and d to show the diagnostic under +-- the cursor; these put the same actions on so which-key lists them. +vim.keymap.set('n', 'dd', vim.diagnostic.open_float, { desc = 'Show diagnostic' }) +vim.keymap.set('n', 'dq', vim.diagnostic.setqflist, { desc = 'Diagnostics to quickfix' }) diff --git a/neovim/config/init.lua b/neovim/config/init.lua index f9db727..379cf15 100644 --- a/neovim/config/init.lua +++ b/neovim/config/init.lua @@ -1,3 +1,4 @@ require('config.options') +require('config.plugins') require('config.diagnostics') require('config.lsp') diff --git a/neovim/config/options.lua b/neovim/config/options.lua index 5b0dff4..b66b348 100644 --- a/neovim/config/options.lua +++ b/neovim/config/options.lua @@ -1,8 +1,14 @@ +-- Set before any plugin defines a mapping +vim.g.mapleader = ' ' +vim.g.maplocalleader = '\\' + vim.o.autocomplete = true vim.o.pumborder = 'rounded' vim.o.pummaxwidth = 40 vim.o.completeopt = 'menu,menuone,noinsert,nearest' vim.o.number = true +-- Always shown, so git/diagnostic signs don't shift the text around +vim.o.signcolumn = 'yes' vim.o.spelllang = 'en_gb' vim.o.spell = true vim.o.termguicolors = true diff --git a/neovim/config/plugins/fzf-lua.lua b/neovim/config/plugins/fzf-lua.lua new file mode 100644 index 0000000..8b56161 --- /dev/null +++ b/neovim/config/plugins/fzf-lua.lua @@ -0,0 +1,15 @@ +vim.pack.add({ 'https://github.com/ibhagwan/fzf-lua' }) + +local fzf = require('fzf-lua') + +fzf.setup() + +local function map(lhs, rhs, desc) + vim.keymap.set('n', lhs, rhs, { desc = desc }) +end + +map('ff', fzf.files, 'Find files') +map('fg', fzf.live_grep, 'Grep project') +map('fb', fzf.buffers, 'Switch buffer') +map('fw', fzf.grep_cword, 'Grep word under cursor') +map('fh', fzf.helptags, 'Search help') diff --git a/neovim/config/plugins/gitsigns.lua b/neovim/config/plugins/gitsigns.lua new file mode 100644 index 0000000..f1f352e --- /dev/null +++ b/neovim/config/plugins/gitsigns.lua @@ -0,0 +1,58 @@ +vim.pack.add({ 'https://github.com/lewis6991/gitsigns.nvim' }) + +local gitsigns = require('gitsigns') + +gitsigns.setup() + +-- Mapped globally rather than from on_attach. Gitsigns attaches asynchronously, +-- after which-key has already built its keymap tree for the buffer, and +-- which-key only rebuilds that on BufReadPost/BufNew/LspAttach - so +-- buffer-local maps added later never show up in its popup. The gitsigns API +-- no-ops in buffers it hasn't attached to, so global maps are safe. +local function map(mode, lhs, rhs, desc) + vim.keymap.set(mode, lhs, rhs, { desc = desc }) +end + +-- Navigation, falling back to vim's own diff motions in diff mode +map('n', ']c', function() + if vim.wo.diff then + vim.cmd.normal({ ']c', bang = true }) + else + gitsigns.nav_hunk('next') + end +end, 'Next git hunk') + +map('n', '[c', function() + if vim.wo.diff then + vim.cmd.normal({ '[c', bang = true }) + else + gitsigns.nav_hunk('prev') + end +end, 'Previous git hunk') + +-- Actions +map('n', 'hs', gitsigns.stage_hunk, 'Stage hunk') +map('n', 'hr', gitsigns.reset_hunk, 'Reset hunk') + +map('v', 'hs', function() + gitsigns.stage_hunk({ vim.fn.line('.'), vim.fn.line('v') }) +end, 'Stage selected hunk') + +map('v', 'hr', function() + gitsigns.reset_hunk({ vim.fn.line('.'), vim.fn.line('v') }) +end, 'Reset selected hunk') + +map('n', 'hS', gitsigns.stage_buffer, 'Stage buffer') +map('n', 'hR', gitsigns.reset_buffer, 'Reset buffer') +map('n', 'hp', gitsigns.preview_hunk, 'Preview hunk') +map('n', 'hd', gitsigns.diffthis, 'Diff against index') + +map('n', 'hb', function() + gitsigns.blame_line({ full = true }) +end, 'Blame line') + +map('n', 'hq', gitsigns.setqflist, 'Hunks to quickfix') + +-- Toggles +map('n', 'tb', gitsigns.toggle_current_line_blame, 'Toggle line blame') +map('n', 'td', gitsigns.toggle_deleted, 'Toggle deleted lines') diff --git a/neovim/config/plugins/init.lua b/neovim/config/plugins/init.lua new file mode 100644 index 0000000..b9bcfe9 --- /dev/null +++ b/neovim/config/plugins/init.lua @@ -0,0 +1,4 @@ +require('config.plugins.fzf-lua') +require('config.plugins.gitsigns') +require('config.plugins.web-devicons') +require('config.plugins.which-key') diff --git a/neovim/config/plugins/web-devicons.lua b/neovim/config/plugins/web-devicons.lua new file mode 100644 index 0000000..e6428b1 --- /dev/null +++ b/neovim/config/plugins/web-devicons.lua @@ -0,0 +1,3 @@ +-- Filetype icons, used by file trees, pickers and statuslines. +-- Needs a Nerd Font in the terminal; setup() is optional so isn't called. +vim.pack.add({ 'https://github.com/nvim-tree/nvim-web-devicons' }) diff --git a/neovim/config/plugins/which-key.lua b/neovim/config/plugins/which-key.lua new file mode 100644 index 0000000..634bd35 --- /dev/null +++ b/neovim/config/plugins/which-key.lua @@ -0,0 +1,13 @@ +vim.pack.add({ 'https://github.com/folke/which-key.nvim' }) + +local wk = require('which-key') + +wk.setup() + +-- Names for the prefixes, so the popup groups them sensibly +wk.add({ + { 'd', group = 'diagnostics' }, + { 'f', group = 'find' }, + { 'h', group = 'hunks' }, + { 't', group = 'toggles' }, +}) diff --git a/neovim/nvim-pack-lock.json b/neovim/nvim-pack-lock.json new file mode 100644 index 0000000..4fdde6e --- /dev/null +++ b/neovim/nvim-pack-lock.json @@ -0,0 +1,20 @@ +{ + "plugins": { + "fzf-lua": { + "rev": "8671012a7b2a1648595952c655039d51fb301c14", + "src": "https://github.com/ibhagwan/fzf-lua" + }, + "gitsigns.nvim": { + "rev": "31d6fb2d618bca1482b9f274751ead5f03461408", + "src": "https://github.com/lewis6991/gitsigns.nvim" + }, + "nvim-web-devicons": { + "rev": "2ae6958df7ced50baac5035cec0c15799eedfbf7", + "src": "https://github.com/nvim-tree/nvim-web-devicons" + }, + "which-key.nvim": { + "rev": "3aab2147e74890957785941f0c1ad87d0a44c15a", + "src": "https://github.com/folke/which-key.nvim" + } + } +}