feat: improve portability and add Grug-far QoL improvements
- Refactor terminal title logic in lua/options.lua with Kitty detection and OSC 2 fallback. - Set mapleader and maplocalleader explicitly at the top of lua/options.lua. - Add <leader>sr mapping for Grug-far search and replace in lua/keymaps.lua. - Add autocommand to close Grug-far window with 'q'. - Update README.md with system dependencies (Debian/Arch) and new keymaps. - Highlight portability features (terminal titles, root detection) in README.md.
This commit is contained in:
@@ -13,6 +13,7 @@ A modern, modular, and high-performance Neovim configuration built from scratch
|
||||
- **High Performance**: Featuring **blink.cmp** (Rust-based completion) and optimized **Snacks.nvim** components.
|
||||
- **AI-Powered**: Native integration with the **GitHub Copilot Language Server**.
|
||||
- **User-Centric QoL**: Hybrid line numbers, autosave-on-edit, and seamless system clipboard integration.
|
||||
- **Resilient & Portable**: Intelligent terminal title management (Kitty + Fallback) and automatic project root detection.
|
||||
|
||||
## 📁 Architecture
|
||||
|
||||
@@ -20,20 +21,43 @@ The configuration is strictly modular:
|
||||
|
||||
- `init.lua`: Entry point and global state initialization.
|
||||
- `lua/lazyload.lua`: Logic for async and phased plugin loading.
|
||||
- `lua/options.lua`: Global Vim settings and non-plugin autocommands.
|
||||
- `lua/options.lua`: Global Vim settings, root management, and terminal title logic.
|
||||
- `lua/plugins.lua`: Plugin declarations and detailed registry-based configurations.
|
||||
- `lua/keymaps.lua`: Centralized user-facing keybindings.
|
||||
- `lua/const.lua`: Stores constant values for reference in the configuration.
|
||||
- `lua/const.lua`: Stores constant values (like dashboard headers) for the configuration.
|
||||
|
||||
## 🛠️ System Dependencies
|
||||
|
||||
To ensure all features (pickers, formatters, and LSPs) work correctly, the following packages are required:
|
||||
|
||||
### Essential Tools
|
||||
- `git`, `curl`, `unzip`, `build-essential` (or `base-devel`)
|
||||
- `ripgrep` (Grep support)
|
||||
- `fd` (Fast file finding)
|
||||
- `fzf` (Fuzzy finder fallback)
|
||||
- `lazygit` (Git TUI)
|
||||
- `gh` (GitHub CLI integration)
|
||||
- `xclip` / `xsel` (X11) or `wl-copy` (Wayland) for clipboard sync.
|
||||
|
||||
### Runtime Environments
|
||||
- `Node.js` & `npm` (Copilot and various LSPs)
|
||||
- `Python3` & `pip` (Python LSPs)
|
||||
- `Cargo` (Rust toolchain, required for building `blink.cmp`)
|
||||
|
||||
### Installation Commands
|
||||
|
||||
**Debian / Ubuntu:**
|
||||
```bash
|
||||
sudo apt install git ripgrep fd-find fzf lazygit gh xclip nodejs npm build-essential curl unzip
|
||||
```
|
||||
|
||||
**Arch Linux:**
|
||||
```bash
|
||||
sudo pacman -S git ripgrep fd fzf lazygit github-cli xclip nodejs npm base-devel curl unzip
|
||||
```
|
||||
|
||||
## 🚀 Getting Started
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- **Neovim 0.10+**
|
||||
- **Nerd Font** (Mono variant recommended for optimal UI alignment)
|
||||
- **Cargo** (Required for building `blink.cmp` fuzzy matching library)
|
||||
- **Node.js** (Required for various LSPs and Copilot)
|
||||
|
||||
### Installation
|
||||
|
||||
```bash
|
||||
@@ -44,24 +68,27 @@ nvim
|
||||
### Post-Install
|
||||
|
||||
1. **Build Blink**: If completion isn't working, run `cargo build --release` inside `~/.local/share/nvim/site/pack/core/opt/blink.cmp`.
|
||||
2. **LSP Servers**: Run `:Mason` to monitor the installation of Language Servers (Lua, C, Rust, Python, etc.).
|
||||
3. **Copilot**: Run `:LspCopilotSignIn` to authenticate with GitHub.
|
||||
2. **LSP Servers**: Run `:Mason` to monitor the installation of Language Servers.
|
||||
3. **Copilot**: Run `:LspCopilotSignIn` to authenticate.
|
||||
|
||||
## ⌨️ Key Features & Mappings
|
||||
|
||||
| Key | Description |
|
||||
| :--- | :--- |
|
||||
| `<leader><space>` | Smart Find Files (Snacks) |
|
||||
| `<leader>e` | File Explorer |
|
||||
| `<leader>ff` | Find Files |
|
||||
| `<leader>sg` | Project-wide Grep |
|
||||
| `<leader>e` | File Explorer (Snacks) |
|
||||
| `<leader>sr` | Search and Replace (Grug-far) |
|
||||
| `<leader>gg` | Open Lazygit |
|
||||
| `<leader>qs` | Restore Last Session (Persistence) |
|
||||
| `<leader>cf` | Format Buffer (Conform) |
|
||||
| `<leader>uu` | Toggle Undo Tree |
|
||||
| `<leader>z` | Toggle Zen Mode |
|
||||
| `gd` / `gr` | Goto Definition / References |
|
||||
| `K` | Hover Documentation |
|
||||
| `s` / `S` | Leap Motion (Normal/Window) |
|
||||
| `ys` / `ds` | Mini.surround (Add/Delete) |
|
||||
| `ys` / `ds` / `cs` | Surround (Add/Delete/Change) |
|
||||
| `:Q` | Forced Write-All and Quit |
|
||||
|
||||
## 📜 License
|
||||
|
||||
Distributed under the **GPLv3 or later** License. See `LICENSE` for more information.
|
||||
|
||||
|
||||
+237
-80
@@ -22,15 +22,15 @@
|
||||
-- System Clipboard Integration
|
||||
-- This makes 'y' and 'p' (and all other yank/paste operations)
|
||||
-- use the system clipboard by default.
|
||||
vim.opt.clipboard = 'unnamedplus'
|
||||
vim.opt.clipboard = "unnamedplus"
|
||||
|
||||
----------------------------------------------------------
|
||||
|
||||
-- Redirect 'delete' and 'change' operations to the 'd' register
|
||||
-- This keeps your system clipboard (unnamedplus) clean
|
||||
local delete_keys = { 'd', 'D', 'c', 'C', 'x', 'X' }
|
||||
local delete_keys = { "d", "D", "c", "C", "x", "X" }
|
||||
for _, key in ipairs(delete_keys) do
|
||||
vim.keymap.set({ 'n', 'v' }, key, '"d' .. key, { noremap = true, silent = true })
|
||||
vim.keymap.set({ "n", "v" }, key, '"d' .. key, { noremap = true, silent = true })
|
||||
end
|
||||
|
||||
-- If you ever need to paste what you just deleted:
|
||||
@@ -40,80 +40,201 @@ end
|
||||
|
||||
-- Quit and Save All with :Q
|
||||
-- Replaces safety-checked :xa with a forced write-all-and-quit.
|
||||
vim.api.nvim_create_user_command('Q', 'xa!', { desc = 'Write all and quit (forced)' })
|
||||
vim.api.nvim_create_user_command("Q", "xa!", { desc = "Write all and quit (forced)" })
|
||||
|
||||
----------------------------------------------------------
|
||||
|
||||
-- Search and Replace (Grug-far)
|
||||
vim.keymap.set("n", "<leader>sr", function()
|
||||
require("grug-far").open({
|
||||
prefills = {
|
||||
search = vim.fn.expand("<cword>"),
|
||||
},
|
||||
})
|
||||
end, { desc = "Search and Replace" })
|
||||
|
||||
-- Snacks Keymaps
|
||||
-- Top Pickers & Explorer
|
||||
vim.keymap.set("n", "<leader><space>", function() Snacks.picker.smart() end, { desc = "Smart Find Files" })
|
||||
vim.keymap.set("n", "<leader>,", function() Snacks.picker.buffers() end, { desc = "Buffers" })
|
||||
vim.keymap.set("n", "<leader>/", function() Snacks.picker.grep() end, { desc = "Grep" })
|
||||
vim.keymap.set("n", "<leader>:", function() Snacks.picker.command_history() end, { desc = "Command History" })
|
||||
vim.keymap.set("n", "<leader>n", function() Snacks.picker.notifications() end, { desc = "Notification History" })
|
||||
vim.keymap.set("n", "<leader>e", function() Snacks.explorer() end, { desc = "File Explorer" })
|
||||
vim.keymap.set("n", "<leader><space>", function()
|
||||
Snacks.picker.smart()
|
||||
end, { desc = "Smart Find Files" })
|
||||
vim.keymap.set("n", "<leader>,", function()
|
||||
Snacks.picker.buffers()
|
||||
end, { desc = "Buffers" })
|
||||
vim.keymap.set("n", "<leader>/", function()
|
||||
Snacks.picker.grep()
|
||||
end, { desc = "Grep" })
|
||||
vim.keymap.set("n", "<leader>:", function()
|
||||
Snacks.picker.command_history()
|
||||
end, { desc = "Command History" })
|
||||
vim.keymap.set("n", "<leader>n", function()
|
||||
Snacks.picker.notifications()
|
||||
end, { desc = "Notification History" })
|
||||
vim.keymap.set("n", "<leader>e", function()
|
||||
Snacks.explorer()
|
||||
end, { desc = "File Explorer" })
|
||||
|
||||
-- Find
|
||||
vim.keymap.set("n", "<leader>fb", function() Snacks.picker.buffers() end, { desc = "Buffers" })
|
||||
vim.keymap.set("n", "<leader>fc", function() Snacks.picker.files({ cwd = vim.fn.stdpath("config") }) end, { desc = "Find Config File" })
|
||||
vim.keymap.set("n", "<leader>ff", function() Snacks.picker.files() end, { desc = "Find Files" })
|
||||
vim.keymap.set("n", "<leader>fg", function() Snacks.picker.git_files() end, { desc = "Find Git Files" })
|
||||
vim.keymap.set("n", "<leader>fp", function() Snacks.picker.projects() end, { desc = "Projects" })
|
||||
vim.keymap.set("n", "<leader>fr", function() Snacks.picker.recent() end, { desc = "Recent" })
|
||||
vim.keymap.set("n", "<leader>fb", function()
|
||||
Snacks.picker.buffers()
|
||||
end, { desc = "Buffers" })
|
||||
vim.keymap.set("n", "<leader>fc", function()
|
||||
Snacks.picker.files({ cwd = vim.fn.stdpath("config") })
|
||||
end, { desc = "Find Config File" })
|
||||
vim.keymap.set("n", "<leader>ff", function()
|
||||
Snacks.picker.files()
|
||||
end, { desc = "Find Files" })
|
||||
vim.keymap.set("n", "<leader>fg", function()
|
||||
Snacks.picker.git_files()
|
||||
end, { desc = "Find Git Files" })
|
||||
vim.keymap.set("n", "<leader>fp", function()
|
||||
Snacks.picker.projects()
|
||||
end, { desc = "Projects" })
|
||||
vim.keymap.set("n", "<leader>fr", function()
|
||||
Snacks.picker.recent()
|
||||
end, { desc = "Recent" })
|
||||
|
||||
-- Git
|
||||
vim.keymap.set("n", "<leader>gb", function() Snacks.picker.git_branches() end, { desc = "Git Branches" })
|
||||
vim.keymap.set("n", "<leader>gl", function() Snacks.picker.git_log() end, { desc = "Git Log" })
|
||||
vim.keymap.set("n", "<leader>gL", function() Snacks.picker.git_log_line() end, { desc = "Git Log Line" })
|
||||
vim.keymap.set("n", "<leader>gs", function() Snacks.picker.git_status() end, { desc = "Git Status" })
|
||||
vim.keymap.set("n", "<leader>gS", function() Snacks.picker.git_stash() end, { desc = "Git Stash" })
|
||||
vim.keymap.set("n", "<leader>gd", function() Snacks.picker.git_diff() end, { desc = "Git Diff (Hunks)" })
|
||||
vim.keymap.set("n", "<leader>gf", function() Snacks.picker.git_log_file() end, { desc = "Git Log File" })
|
||||
vim.keymap.set("n", "<leader>gb", function()
|
||||
Snacks.picker.git_branches()
|
||||
end, { desc = "Git Branches" })
|
||||
vim.keymap.set("n", "<leader>gl", function()
|
||||
Snacks.picker.git_log()
|
||||
end, { desc = "Git Log" })
|
||||
vim.keymap.set("n", "<leader>gL", function()
|
||||
Snacks.picker.git_log_line()
|
||||
end, { desc = "Git Log Line" })
|
||||
vim.keymap.set("n", "<leader>gs", function()
|
||||
Snacks.picker.git_status()
|
||||
end, { desc = "Git Status" })
|
||||
vim.keymap.set("n", "<leader>gS", function()
|
||||
Snacks.picker.git_stash()
|
||||
end, { desc = "Git Stash" })
|
||||
vim.keymap.set("n", "<leader>gd", function()
|
||||
Snacks.picker.git_diff()
|
||||
end, { desc = "Git Diff (Hunks)" })
|
||||
vim.keymap.set("n", "<leader>gf", function()
|
||||
Snacks.picker.git_log_file()
|
||||
end, { desc = "Git Log File" })
|
||||
|
||||
-- GH
|
||||
vim.keymap.set("n", "<leader>gi", function() Snacks.picker.gh_issue() end, { desc = "GitHub Issues (open)" })
|
||||
vim.keymap.set("n", "<leader>gI", function() Snacks.picker.gh_issue({ state = "all" }) end, { desc = "GitHub Issues (all)" })
|
||||
vim.keymap.set("n", "<leader>gp", function() Snacks.picker.gh_pr() end, { desc = "GitHub Pull Requests (open)" })
|
||||
vim.keymap.set("n", "<leader>gP", function() Snacks.picker.gh_pr({ state = "all" }) end, { desc = "GitHub Pull Requests (all)" })
|
||||
vim.keymap.set("n", "<leader>gi", function()
|
||||
Snacks.picker.gh_issue()
|
||||
end, { desc = "GitHub Issues (open)" })
|
||||
vim.keymap.set("n", "<leader>gI", function()
|
||||
Snacks.picker.gh_issue({ state = "all" })
|
||||
end, { desc = "GitHub Issues (all)" })
|
||||
vim.keymap.set("n", "<leader>gp", function()
|
||||
Snacks.picker.gh_pr()
|
||||
end, { desc = "GitHub Pull Requests (open)" })
|
||||
vim.keymap.set("n", "<leader>gP", function()
|
||||
Snacks.picker.gh_pr({ state = "all" })
|
||||
end, { desc = "GitHub Pull Requests (all)" })
|
||||
|
||||
-- Grep
|
||||
vim.keymap.set("n", "<leader>sb", function() Snacks.picker.lines() end, { desc = "Buffer Lines" })
|
||||
vim.keymap.set("n", "<leader>sB", function() Snacks.picker.grep_buffers() end, { desc = "Grep Open Buffers" })
|
||||
vim.keymap.set("n", "<leader>sg", function() Snacks.picker.grep() end, { desc = "Grep" })
|
||||
vim.keymap.set({ "n", "x" }, "<leader>sw", function() Snacks.picker.grep_word() end, { desc = "Visual selection or word" })
|
||||
vim.keymap.set("n", "<leader>sb", function()
|
||||
Snacks.picker.lines()
|
||||
end, { desc = "Buffer Lines" })
|
||||
vim.keymap.set("n", "<leader>sB", function()
|
||||
Snacks.picker.grep_buffers()
|
||||
end, { desc = "Grep Open Buffers" })
|
||||
vim.keymap.set("n", "<leader>sg", function()
|
||||
Snacks.picker.grep()
|
||||
end, { desc = "Grep" })
|
||||
vim.keymap.set({ "n", "x" }, "<leader>sw", function()
|
||||
Snacks.picker.grep_word()
|
||||
end, { desc = "Visual selection or word" })
|
||||
|
||||
-- Search
|
||||
vim.keymap.set("n", '<leader>s"', function() Snacks.picker.registers() end, { desc = "Registers" })
|
||||
vim.keymap.set("n", '<leader>s/', function() Snacks.picker.search_history() end, { desc = "Search History" })
|
||||
vim.keymap.set("n", "<leader>sa", function() Snacks.picker.autocmds() end, { desc = "Autocmds" })
|
||||
vim.keymap.set("n", "<leader>sc", function() Snacks.picker.command_history() end, { desc = "Command History" })
|
||||
vim.keymap.set("n", "<leader>sC", function() Snacks.picker.commands() end, { desc = "Commands" })
|
||||
vim.keymap.set("n", "<leader>sd", function() Snacks.picker.diagnostics() end, { desc = "Diagnostics" })
|
||||
vim.keymap.set("n", "<leader>sD", function() Snacks.picker.diagnostics_buffer() end, { desc = "Buffer Diagnostics" })
|
||||
vim.keymap.set("n", "<leader>sh", function() Snacks.picker.help() end, { desc = "Help Pages" })
|
||||
vim.keymap.set("n", "<leader>sH", function() Snacks.picker.highlights() end, { desc = "Highlights" })
|
||||
vim.keymap.set("n", "<leader>si", function() Snacks.picker.icons() end, { desc = "Icons" })
|
||||
vim.keymap.set("n", "<leader>sj", function() Snacks.picker.jumps() end, { desc = "Jumps" })
|
||||
vim.keymap.set("n", "<leader>sk", function() Snacks.picker.keymaps() end, { desc = "Keymaps" })
|
||||
vim.keymap.set("n", "<leader>sl", function() Snacks.picker.loclist() end, { desc = "Location List" })
|
||||
vim.keymap.set("n", "<leader>sm", function() Snacks.picker.marks() end, { desc = "Marks" })
|
||||
vim.keymap.set("n", "<leader>sM", function() Snacks.picker.man() end, { desc = "Man Pages" })
|
||||
vim.keymap.set("n", "<leader>sp", function() Snacks.picker.lazy() end, { desc = "Search for Plugin Spec" })
|
||||
vim.keymap.set("n", "<leader>sq", function() Snacks.picker.qflist() end, { desc = "Quickfix List" })
|
||||
vim.keymap.set("n", "<leader>sR", function() Snacks.picker.resume() end, { desc = "Resume" })
|
||||
vim.keymap.set("n", "<leader>su", function() Snacks.picker.undo() end, { desc = "Undo History" })
|
||||
vim.keymap.set("n", "<leader>uC", function() Snacks.picker.colorschemes() end, { desc = "Colorschemes" })
|
||||
vim.keymap.set("n", '<leader>s"', function()
|
||||
Snacks.picker.registers()
|
||||
end, { desc = "Registers" })
|
||||
vim.keymap.set("n", "<leader>s/", function()
|
||||
Snacks.picker.search_history()
|
||||
end, { desc = "Search History" })
|
||||
vim.keymap.set("n", "<leader>sa", function()
|
||||
Snacks.picker.autocmds()
|
||||
end, { desc = "Autocmds" })
|
||||
vim.keymap.set("n", "<leader>sc", function()
|
||||
Snacks.picker.command_history()
|
||||
end, { desc = "Command History" })
|
||||
vim.keymap.set("n", "<leader>sC", function()
|
||||
Snacks.picker.commands()
|
||||
end, { desc = "Commands" })
|
||||
vim.keymap.set("n", "<leader>sd", function()
|
||||
Snacks.picker.diagnostics()
|
||||
end, { desc = "Diagnostics" })
|
||||
vim.keymap.set("n", "<leader>sD", function()
|
||||
Snacks.picker.diagnostics_buffer()
|
||||
end, { desc = "Buffer Diagnostics" })
|
||||
vim.keymap.set("n", "<leader>sh", function()
|
||||
Snacks.picker.help()
|
||||
end, { desc = "Help Pages" })
|
||||
vim.keymap.set("n", "<leader>sH", function()
|
||||
Snacks.picker.highlights()
|
||||
end, { desc = "Highlights" })
|
||||
vim.keymap.set("n", "<leader>si", function()
|
||||
Snacks.picker.icons()
|
||||
end, { desc = "Icons" })
|
||||
vim.keymap.set("n", "<leader>sj", function()
|
||||
Snacks.picker.jumps()
|
||||
end, { desc = "Jumps" })
|
||||
vim.keymap.set("n", "<leader>sk", function()
|
||||
Snacks.picker.keymaps()
|
||||
end, { desc = "Keymaps" })
|
||||
vim.keymap.set("n", "<leader>sl", function()
|
||||
Snacks.picker.loclist()
|
||||
end, { desc = "Location List" })
|
||||
vim.keymap.set("n", "<leader>sm", function()
|
||||
Snacks.picker.marks()
|
||||
end, { desc = "Marks" })
|
||||
vim.keymap.set("n", "<leader>sM", function()
|
||||
Snacks.picker.man()
|
||||
end, { desc = "Man Pages" })
|
||||
vim.keymap.set("n", "<leader>sp", function()
|
||||
Snacks.picker.lazy()
|
||||
end, { desc = "Search for Plugin Spec" })
|
||||
vim.keymap.set("n", "<leader>sq", function()
|
||||
Snacks.picker.qflist()
|
||||
end, { desc = "Quickfix List" })
|
||||
vim.keymap.set("n", "<leader>sR", function()
|
||||
Snacks.picker.resume()
|
||||
end, { desc = "Resume" })
|
||||
vim.keymap.set("n", "<leader>su", function()
|
||||
Snacks.picker.undo()
|
||||
end, { desc = "Undo History" })
|
||||
vim.keymap.set("n", "<leader>uC", function()
|
||||
Snacks.picker.colorschemes()
|
||||
end, { desc = "Colorschemes" })
|
||||
|
||||
-- LSP
|
||||
vim.keymap.set("n", "gd", function() Snacks.picker.lsp_definitions() end, { desc = "Goto Definition" })
|
||||
vim.keymap.set("n", "gD", function() Snacks.picker.lsp_declarations() end, { desc = "Goto Declaration" })
|
||||
vim.keymap.set("n", "gr", function() Snacks.picker.lsp_references() end, { nowait = true, desc = "References" })
|
||||
vim.keymap.set("n", "gI", function() Snacks.picker.lsp_implementations() end, { desc = "Goto Implementation" })
|
||||
vim.keymap.set("n", "gy", function() Snacks.picker.lsp_type_definitions() end, { desc = "Goto T[y]pe Definition" })
|
||||
vim.keymap.set("n", "gai", function() Snacks.picker.lsp_incoming_calls() end, { desc = "C[a]lls Incoming" })
|
||||
vim.keymap.set("n", "gao", function() Snacks.picker.lsp_outgoing_calls() end, { desc = "C[a]lls Outgoing" })
|
||||
vim.keymap.set("n", "<leader>ss", function() Snacks.picker.lsp_symbols() end, { desc = "LSP Symbols" })
|
||||
vim.keymap.set("n", "<leader>sS", function() Snacks.picker.lsp_workspace_symbols() end, { desc = "LSP Workspace Symbols" })
|
||||
vim.keymap.set("n", "gd", function()
|
||||
Snacks.picker.lsp_definitions()
|
||||
end, { desc = "Goto Definition" })
|
||||
vim.keymap.set("n", "gD", function()
|
||||
Snacks.picker.lsp_declarations()
|
||||
end, { desc = "Goto Declaration" })
|
||||
vim.keymap.set("n", "gr", function()
|
||||
Snacks.picker.lsp_references()
|
||||
end, { nowait = true, desc = "References" })
|
||||
vim.keymap.set("n", "gI", function()
|
||||
Snacks.picker.lsp_implementations()
|
||||
end, { desc = "Goto Implementation" })
|
||||
vim.keymap.set("n", "gy", function()
|
||||
Snacks.picker.lsp_type_definitions()
|
||||
end, { desc = "Goto T[y]pe Definition" })
|
||||
vim.keymap.set("n", "gai", function()
|
||||
Snacks.picker.lsp_incoming_calls()
|
||||
end, { desc = "C[a]lls Incoming" })
|
||||
vim.keymap.set("n", "gao", function()
|
||||
Snacks.picker.lsp_outgoing_calls()
|
||||
end, { desc = "C[a]lls Outgoing" })
|
||||
vim.keymap.set("n", "<leader>ss", function()
|
||||
Snacks.picker.lsp_symbols()
|
||||
end, { desc = "LSP Symbols" })
|
||||
vim.keymap.set("n", "<leader>sS", function()
|
||||
Snacks.picker.lsp_workspace_symbols()
|
||||
end, { desc = "LSP Workspace Symbols" })
|
||||
|
||||
-- Standard LSP
|
||||
vim.keymap.set("n", "K", vim.lsp.buf.hover, { desc = "Hover Documentation" })
|
||||
@@ -124,20 +245,48 @@ end, { expr = true, desc = "Rename Symbol" })
|
||||
|
||||
-- Other
|
||||
vim.keymap.set("n", "<leader>sq", "<cmd>nohlsearch<cr>", { desc = "Clear Search Highlights" })
|
||||
vim.keymap.set("n", "<leader>z", function() Snacks.zen() end, { desc = "Toggle Zen Mode" })
|
||||
vim.keymap.set("n", "<leader>Z", function() Snacks.zen.zoom() end, { desc = "Toggle Zoom" })
|
||||
vim.keymap.set("n", "<leader>.", function() Snacks.scratch() end, { desc = "Toggle Scratch Buffer" })
|
||||
vim.keymap.set("n", "<leader>S", function() Snacks.scratch.select() end, { desc = "Select Scratch Buffer" })
|
||||
vim.keymap.set("n", "<leader>n", function() Snacks.notifier.show_history() end, { desc = "Notification History" })
|
||||
vim.keymap.set("n", "<leader>bd", function() Snacks.bufdelete() end, { desc = "Delete Buffer" })
|
||||
vim.keymap.set("n", "<leader>cR", function() Snacks.rename.rename_file() end, { desc = "Rename File" })
|
||||
vim.keymap.set({ "n", "v" }, "<leader>gB", function() Snacks.gitbrowse() end, { desc = "Git Browse" })
|
||||
vim.keymap.set("n", "<leader>gg", function() Snacks.lazygit() end, { desc = "Lazygit" })
|
||||
vim.keymap.set("n", "<leader>un", function() Snacks.notifier.hide() end, { desc = "Dismiss All Notifications" })
|
||||
vim.keymap.set({ "n", "t" }, "<c-/>", function() Snacks.terminal() end, { desc = "Toggle Terminal" })
|
||||
vim.keymap.set({ "n", "t" }, "<c-_>", function() Snacks.terminal() end, { desc = "which_key_ignore" })
|
||||
vim.keymap.set({ "n", "t" }, "]]", function() Snacks.words.jump(vim.v.count1) end, { desc = "Next Reference" })
|
||||
vim.keymap.set({ "n", "t" }, "[[", function() Snacks.words.jump(-vim.v.count1) end, { desc = "Prev Reference" })
|
||||
vim.keymap.set("n", "<leader>z", function()
|
||||
Snacks.zen()
|
||||
end, { desc = "Toggle Zen Mode" })
|
||||
vim.keymap.set("n", "<leader>Z", function()
|
||||
Snacks.zen.zoom()
|
||||
end, { desc = "Toggle Zoom" })
|
||||
vim.keymap.set("n", "<leader>.", function()
|
||||
Snacks.scratch()
|
||||
end, { desc = "Toggle Scratch Buffer" })
|
||||
vim.keymap.set("n", "<leader>S", function()
|
||||
Snacks.scratch.select()
|
||||
end, { desc = "Select Scratch Buffer" })
|
||||
vim.keymap.set("n", "<leader>n", function()
|
||||
Snacks.notifier.show_history()
|
||||
end, { desc = "Notification History" })
|
||||
vim.keymap.set("n", "<leader>bd", function()
|
||||
Snacks.bufdelete()
|
||||
end, { desc = "Delete Buffer" })
|
||||
vim.keymap.set("n", "<leader>cR", function()
|
||||
Snacks.rename.rename_file()
|
||||
end, { desc = "Rename File" })
|
||||
vim.keymap.set({ "n", "v" }, "<leader>gB", function()
|
||||
Snacks.gitbrowse()
|
||||
end, { desc = "Git Browse" })
|
||||
vim.keymap.set("n", "<leader>gg", function()
|
||||
Snacks.lazygit()
|
||||
end, { desc = "Lazygit" })
|
||||
vim.keymap.set("n", "<leader>un", function()
|
||||
Snacks.notifier.hide()
|
||||
end, { desc = "Dismiss All Notifications" })
|
||||
vim.keymap.set({ "n", "t" }, "<c-/>", function()
|
||||
Snacks.terminal()
|
||||
end, { desc = "Toggle Terminal" })
|
||||
vim.keymap.set({ "n", "t" }, "<c-_>", function()
|
||||
Snacks.terminal()
|
||||
end, { desc = "which_key_ignore" })
|
||||
vim.keymap.set({ "n", "t" }, "]]", function()
|
||||
Snacks.words.jump(vim.v.count1)
|
||||
end, { desc = "Next Reference" })
|
||||
vim.keymap.set({ "n", "t" }, "[[", function()
|
||||
Snacks.words.jump(-vim.v.count1)
|
||||
end, { desc = "Prev Reference" })
|
||||
vim.keymap.set("n", "<leader>N", function()
|
||||
Snacks.win({
|
||||
file = vim.api.nvim_get_runtime_file("doc/news.txt", false)[1],
|
||||
@@ -156,8 +305,8 @@ end, { desc = "Neovim News" })
|
||||
----------------------------------------------------------
|
||||
|
||||
-- Leap Keymaps
|
||||
vim.keymap.set({ 'n', 'x', 'o' }, '<CR>', '<Plug>(leap)')
|
||||
vim.keymap.set('n', 'S', '<Plug>(leap-from-window)')
|
||||
vim.keymap.set({ "n", "x", "o" }, "<CR>", "<Plug>(leap)")
|
||||
vim.keymap.set("n", "S", "<Plug>(leap-from-window)")
|
||||
|
||||
----------------------------------------------------------
|
||||
|
||||
@@ -174,7 +323,15 @@ vim.keymap.set("n", "<leader>uu", "<cmd>UndotreeToggle<cr>", { desc = "Toggle Un
|
||||
----------------------------------------------------------
|
||||
|
||||
-- Persistence Keymaps
|
||||
vim.keymap.set("n", "<leader>qs", function() require("persistence").load() end, { desc = "Restore Session" })
|
||||
vim.keymap.set("n", "<leader>qS", function() require("persistence").select() end, { desc = "Select Session" })
|
||||
vim.keymap.set("n", "<leader>ql", function() require("persistence").load({ last = true }) end, { desc = "Restore Last Session" })
|
||||
vim.keymap.set("n", "<leader>qd", function() require("persistence").stop() end, { desc = "Don't Save Current Session" })
|
||||
vim.keymap.set("n", "<leader>qs", function()
|
||||
require("persistence").load()
|
||||
end, { desc = "Restore Session" })
|
||||
vim.keymap.set("n", "<leader>qS", function()
|
||||
require("persistence").select()
|
||||
end, { desc = "Select Session" })
|
||||
vim.keymap.set("n", "<leader>ql", function()
|
||||
require("persistence").load({ last = true })
|
||||
end, { desc = "Restore Last Session" })
|
||||
vim.keymap.set("n", "<leader>qd", function()
|
||||
require("persistence").stop()
|
||||
end, { desc = "Don't Save Current Session" })
|
||||
|
||||
+108
@@ -4,6 +4,10 @@
|
||||
└────────────────────────────────────────────────────────────────┘
|
||||
--]]
|
||||
|
||||
-- Set leader keys before any other configuration
|
||||
vim.g.mapleader = " "
|
||||
vim.g.maplocalleader = "\\"
|
||||
|
||||
-- Copyright (C) 2026 rootiest
|
||||
--
|
||||
-- This program is free software: you can redistribute it and/or modify
|
||||
@@ -66,3 +70,107 @@ vim.api.nvim_create_user_command("Format", function(args)
|
||||
end
|
||||
require("conform").format({ async = true, lsp_format = "fallback", range = range })
|
||||
end, { range = true })
|
||||
|
||||
--[[
|
||||
┌────────────────────────────────────────────────────────────────┐
|
||||
│ Root Management │
|
||||
└────────────────────────────────────────────────────────────────┘
|
||||
--]]
|
||||
|
||||
-- Automatically change the working directory to the project root.
|
||||
-- This ensures that plugins like Snacks.picker and GrugFar work
|
||||
-- relative to the file or project you are currently editing.
|
||||
vim.api.nvim_create_autocmd("BufEnter", {
|
||||
group = vim.api.nvim_create_augroup("rootiest_auto_cd", { clear = true }),
|
||||
callback = function()
|
||||
local path = vim.api.nvim_buf_get_name(0)
|
||||
if path == "" then
|
||||
return
|
||||
end
|
||||
|
||||
-- Skip special buffers, but allow directory buffers (for explorer support)
|
||||
if vim.bo.buftype ~= "" and vim.bo.filetype ~= "snacks_explorer_tree" then
|
||||
return
|
||||
end
|
||||
|
||||
-- Get the directory (handle both files and directory paths)
|
||||
local dir = vim.fn.isdirectory(path) == 1 and path or vim.fn.fnamemodify(path, ":p:h")
|
||||
|
||||
-- Find the project root using common markers, fallback to the directory itself
|
||||
local root = vim.fs.root(dir, { ".git", "lua", "package.json", "go.mod", "Cargo.toml", "Makefile" }) or dir
|
||||
|
||||
-- Change directory if it's different and valid
|
||||
if root and vim.fn.isdirectory(root) == 1 and root ~= vim.fn.getcwd() then
|
||||
vim.fn.chdir(root)
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
-- Grug-far QoL: Close with 'q'
|
||||
vim.api.nvim_create_autocmd("FileType", {
|
||||
group = vim.api.nvim_create_augroup("grug_far_q_to_close", { clear = true }),
|
||||
pattern = "grug-far",
|
||||
callback = function(args)
|
||||
vim.keymap.set("n", "q", "<cmd>close<cr>", { buffer = args.buf, silent = true, desc = "Close Grug-far" })
|
||||
end,
|
||||
})
|
||||
|
||||
--[[
|
||||
┌────────────────────────────────────────────────────────────────┐
|
||||
│ Shell Interaction │
|
||||
└────────────────────────────────────────────────────────────────┘
|
||||
--]]
|
||||
|
||||
-- Detect terminal environment
|
||||
local is_kitty = os.getenv("KITTY_PID") ~= nil
|
||||
local current_shell = os.getenv("SHELL") or "/bin/sh"
|
||||
local shell_name = current_shell:match("([^/]+)$") or "sh"
|
||||
|
||||
-- Helper function to update the terminal title
|
||||
local function set_terminal_title(title)
|
||||
-- If in Kitty, we use the direct escape sequence as it's reliable
|
||||
if is_kitty then
|
||||
io.stdout:write("\27]2;" .. title .. "\7")
|
||||
-- Fallback: Use Neovim's built-in title management for other terminals
|
||||
-- (Standard OSC 2 sequences are supported by most, but opt.title is safer)
|
||||
elseif os.getenv("TERM") ~= "linux" then
|
||||
vim.opt.title = true
|
||||
vim.opt.titlestring = title
|
||||
end
|
||||
end
|
||||
|
||||
-- Create the Autocommand Group
|
||||
local title_group = vim.api.nvim_create_augroup("TerminalTitle", { clear = true })
|
||||
|
||||
vim.api.nvim_create_autocmd({ "BufEnter", "BufWinEnter" }, {
|
||||
group = title_group,
|
||||
callback = function()
|
||||
local buftype = vim.bo.buftype
|
||||
local filetype = vim.bo.filetype
|
||||
local filename = vim.fn.expand("%:t")
|
||||
|
||||
-- 1. Specifically catch Snacks Terminal or standard Terminals
|
||||
if filetype == "snacks_terminal" or buftype == "terminal" then
|
||||
set_terminal_title("NVIM: Terminal")
|
||||
|
||||
-- 2. Handle normal files (buftype is empty)
|
||||
elseif buftype == "" and filename ~= "" then
|
||||
set_terminal_title("NVIM: " .. filename)
|
||||
|
||||
-- 3. Handle the empty start screen
|
||||
elseif filename == "" and buftype == "" then
|
||||
set_terminal_title("Neovim")
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
-- Reset the title back to the shell name when you quit Neovim
|
||||
vim.api.nvim_create_autocmd("VimLeave", {
|
||||
group = title_group,
|
||||
callback = function()
|
||||
-- On leave, direct stdout is more reliable than setting an option
|
||||
if is_kitty or os.getenv("TERM") ~= "linux" then
|
||||
io.stdout:write("\27]2;" .. shell_name .. "\7")
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user