summaryrefslogtreecommitdiff
path: root/alpine/nvim/init.lua
diff options
context:
space:
mode:
Diffstat (limited to 'alpine/nvim/init.lua')
-rw-r--r--alpine/nvim/init.lua386
1 files changed, 386 insertions, 0 deletions
diff --git a/alpine/nvim/init.lua b/alpine/nvim/init.lua
new file mode 100644
index 0000000..db30dc7
--- /dev/null
+++ b/alpine/nvim/init.lua
@@ -0,0 +1,386 @@
+-- Bootstrap lazy.nvim
+local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
+if not vim.uv.fs_stat(lazypath) then
+ vim.fn.system({
+ "git", "clone", "--filter=blob:none",
+ "https://github.com/folke/lazy.nvim.git",
+ "--branch=stable", lazypath,
+ })
+end
+vim.opt.rtp:prepend(lazypath)
+
+-- Leader
+vim.g.mapleader = " "
+vim.g.maplocalleader = " "
+
+-- ============================================================
+-- Options (adjusted for a raw TTY)
+-- ============================================================
+
+local opt = vim.opt
+
+-- Line numbers
+opt.number = true
+opt.relativenumber = true
+
+-- Indentation
+opt.expandtab = true
+opt.tabstop = 4
+opt.shiftwidth = 4
+opt.softtabstop = 4
+
+-- Visual
+opt.cursorline = true
+opt.scrolloff = 20
+opt.signcolumn = "yes"
+opt.list = true
+opt.listchars = { tab = " ", trail = ".", nbsp = "_" }
+opt.fillchars:append({ fold = "-" })
+opt.termguicolors = false
+opt.colorcolumn = ""
+
+-- Behaviour
+opt.swapfile = false
+opt.undofile = true
+opt.updatetime = 250
+opt.timeoutlen = 300
+opt.splitbelow = true
+opt.splitright = true
+opt.ignorecase = true
+opt.smartcase = true
+opt.inccommand = "split"
+opt.hlsearch = true
+opt.breakindent = true
+
+-- Folding (nvim-ufo, see plugin list below)
+opt.foldcolumn = "0"
+opt.foldlevel = 99
+opt.foldlevelstart = 99
+opt.foldenable = true
+
+local map = vim.keymap.set
+
+-- ============================================================
+-- Keymaps
+-- ============================================================
+
+-- Disable Space in normal/visual (used as leader)
+map({ "n", "v" }, "<Space>", "<Nop>", { silent = true })
+
+-- Save
+map({ "n", "i" }, "<C-s>", "<cmd>w<CR>", { desc = "Save" })
+
+-- Clear search highlight
+map("n", "<Esc>", "<cmd>nohlsearch<CR>")
+
+-- Exit terminal mode
+map("t", "<Esc><Esc>", "<C-\\><C-n>", { desc = "Exit terminal" })
+
+-- Better up/down on wrapped lines
+map({ "n", "v" }, "j", "gj")
+map({ "n", "v" }, "k", "gk")
+
+-- Buffer navigation
+map("n", "<S-h>", "<cmd>bprevious<CR>", { desc = "Prev buffer" })
+map("n", "<S-l>", "<cmd>bnext<CR>", { desc = "Next buffer" })
+map("n", "<leader>bd", "<cmd>bdelete<CR>", { desc = "Delete buffer" })
+
+-- Splits
+map("n", "<leader>sv", "<C-w>v", { desc = "Split vertical" })
+map("n", "<leader>sh", "<C-w>s", { desc = "Split horizontal" })
+map("n", "<leader>se", "<C-w>=", { desc = "Equal splits" })
+map("n", "<leader>sx", "<cmd>close<CR>", { desc = "Close split" })
+
+-- Move lines
+map("n", "<C-S-Down>", "<cmd>m .+1<CR>==", { desc = "Move line down" })
+map("n", "<C-S-Up>", "<cmd>m .-2<CR>==", { desc = "Move line up" })
+map("v", "<C-S-Down>", ":m '>+1<CR>gv=gv", { desc = "Move selection down" })
+map("v", "<C-S-Up>", ":m '<-2<CR>gv=gv", { desc = "Move selection up" })
+
+-- Stay in visual after indent
+map("v", "<", "<gv")
+map("v", ">", ">gv")
+
+-- ============================================================
+-- Completion helper
+-- ============================================================
+
+local function expand_menu()
+ local cfg = require("blink.cmp.config").completion
+ cfg.menu.max_height = 10
+ cfg.documentation.auto_show = true
+ pcall(function()
+ require("blink.cmp.completion.windows.menu").update_position()
+ end)
+end
+
+vim.api.nvim_create_autocmd("User", {
+ pattern = "BlinkCmpMenuClose",
+ callback = function()
+ local ok, cfg = pcall(function() return require("blink.cmp.config").completion end)
+ if not ok then return end
+ cfg.menu.max_height = 5
+ cfg.documentation.auto_show = false
+ end,
+})
+
+-- ============================================================
+-- Fold text helper
+-- ============================================================
+
+local function ufo_ascii_foldtext(virtText, lnum, endLnum, width, truncate)
+ local newVirtText = {}
+ local suffix = (" ... %d lines "):format(endLnum - lnum)
+ local sufWidth = vim.fn.strdisplaywidth(suffix)
+ local targetWidth = width - sufWidth
+ local curWidth = 0
+ for _, chunk in ipairs(virtText) do
+ local chunkText = chunk[1]
+ local chunkWidth = vim.fn.strdisplaywidth(chunkText)
+ if targetWidth > curWidth + chunkWidth then
+ table.insert(newVirtText, chunk)
+ else
+ chunkText = truncate(chunkText, targetWidth - curWidth)
+ local hlGroup = chunk[2]
+ table.insert(newVirtText, { chunkText, hlGroup })
+ chunkWidth = vim.fn.strdisplaywidth(chunkText)
+ if curWidth + chunkWidth < targetWidth then
+ suffix = suffix .. (" "):rep(targetWidth - curWidth - chunkWidth)
+ end
+ break
+ end
+ curWidth = curWidth + chunkWidth
+ end
+ table.insert(newVirtText, { suffix, "Comment" })
+ return newVirtText
+end
+
+-- ============================================================
+-- Plugins: leap, oil, treesitter, nvim-ufo, clangd, blink.cmp
+-- ============================================================
+
+require("lazy").setup({
+
+ -- Motion: s + two chars to jump anywhere
+ {
+ url = "https://codeberg.org/andyg/leap.nvim",
+ keys = {
+ { "s", "<Plug>(leap)", mode = { "n", "x", "o" }, desc = "Leap" },
+ { "S", "<Plug>(leap-from-window)", mode = { "n", "o" }, desc = "Leap from window" },
+ },
+ },
+
+ -- Buffer-based file explorer
+ {
+ "stevearc/oil.nvim",
+ lazy = false,
+ keys = {
+ { "<leader>e", "<cmd>Oil<CR>", desc = "File explorer" },
+ { "-", "<cmd>Oil<CR>", desc = "Open parent dir" },
+ },
+ opts = {
+ default_file_explorer = true,
+ view_options = { show_hidden = true },
+ constrain_cursor = false,
+ },
+ },
+
+ -- Treesitter (syntax highlighting + textobjects)
+ {
+ "nvim-treesitter/nvim-treesitter",
+ branch = "main",
+ lazy = false,
+ build = ":TSUpdate",
+ dependencies = {
+ { "nvim-treesitter/nvim-treesitter-textobjects", branch = "main" },
+ },
+ config = function()
+ require("nvim-treesitter").setup({})
+ require("nvim-treesitter").install({ "c" })
+
+ vim.api.nvim_create_autocmd("FileType", {
+ callback = function(args)
+ local lang = vim.treesitter.language.get_lang(vim.bo[args.buf].filetype)
+ if lang and vim.treesitter.language.add(lang) then
+ pcall(vim.treesitter.start, args.buf, lang)
+ vim.bo[args.buf].indentexpr = "v:lua.require'nvim-treesitter'.indentexpr()"
+ end
+ end,
+ })
+
+ local ok_to, to = pcall(require, "nvim-treesitter-textobjects")
+ if not ok_to then return end
+ to.setup({
+ select = { lookahead = true },
+ move = { set_jumps = true },
+ })
+ local sel = require("nvim-treesitter-textobjects.select")
+ local move = require("nvim-treesitter-textobjects.move")
+ local swap = require("nvim-treesitter-textobjects.swap")
+ local tmap = vim.keymap.set
+
+ local objs = {
+ { "af", "@function.outer", "around function" },
+ { "if", "@function.inner", "inside function" },
+ { "ac", "@class.outer", "around class" },
+ { "ic", "@class.inner", "inside class" },
+ { "aa", "@parameter.outer", "around argument" },
+ { "ia", "@parameter.inner", "inside argument" },
+ { "ab", "@block.outer", "around block" },
+ { "ib", "@block.inner", "inside block" },
+ { "ai", "@conditional.outer", "around conditional" },
+ { "ii", "@conditional.inner", "inside conditional" },
+ { "al", "@loop.outer", "around loop" },
+ { "il", "@loop.inner", "inside loop" },
+ }
+ for _, o in ipairs(objs) do
+ local lhs, q, d = o[1], o[2], o[3]
+ tmap({ "x", "o" }, lhs, function() sel.select_textobject(q, "textobjects") end, { desc = d })
+ end
+
+ local nmo = { "n", "x", "o" }
+ tmap(nmo, "]m", function() move.goto_next_start("@function.outer", "textobjects") end, { desc = "next func start" })
+ tmap(nmo, "[m", function() move.goto_previous_start("@function.outer", "textobjects") end, { desc = "prev func start" })
+ tmap(nmo, "]M", function() move.goto_next_end("@function.outer", "textobjects") end, { desc = "next func end" })
+ tmap(nmo, "[M", function() move.goto_previous_end("@function.outer", "textobjects") end, { desc = "prev func end" })
+ tmap(nmo, "]]", function() move.goto_next_start("@class.outer", "textobjects") end, { desc = "next class start" })
+ tmap(nmo, "[[", function() move.goto_previous_start("@class.outer", "textobjects") end, { desc = "prev class start" })
+ tmap(nmo, "][", function() move.goto_next_end("@class.outer", "textobjects") end, { desc = "next class end" })
+ tmap(nmo, "[]", function() move.goto_previous_end("@class.outer", "textobjects") end, { desc = "prev class end" })
+
+ tmap("n", "<leader>a", function() swap.swap_next("@parameter.inner") end, { desc = "Swap arg with next" })
+ tmap("n", "<leader>A", function() swap.swap_previous("@parameter.inner") end, { desc = "Swap arg with prev" })
+ end,
+ },
+
+ -- Folding
+ {
+ "kevinhwang91/nvim-ufo",
+ dependencies = { "kevinhwang91/promise-async" },
+ event = "BufReadPost",
+ keys = {
+ {
+ "zR",
+ function()
+ require("ufo").openAllFolds()
+ end,
+ desc = "Open all folds",
+ },
+ {
+ "zM",
+ function()
+ require("ufo").closeAllFolds()
+ end,
+ desc = "Close all folds",
+ },
+ },
+ opts = {
+ provider_selector = function()
+ return { "treesitter", "indent" }
+ end,
+ fold_virt_text_handler = ufo_ascii_foldtext,
+ },
+ },
+
+ -- Completion engine
+ {
+ "saghen/blink.cmp",
+ version = "*",
+ dependencies = { "L3MON4D3/LuaSnip", "rafamadriz/friendly-snippets" },
+ opts = {
+ keymap = {
+ preset = "none",
+ ["<C-Space>"] = { "show", "show_documentation", "hide_documentation" },
+ ["<C-e>"] = { "hide" },
+ ["<CR>"] = { "accept", "fallback" },
+ ["<Tab>"] = { expand_menu, "select_next", "fallback" },
+ ["<S-Tab>"] = { "select_prev", "fallback" },
+ ["<C-b>"] = { "scroll_documentation_up", "fallback" },
+ ["<C-f>"] = { "scroll_documentation_down", "fallback" },
+ ["<C-n>"] = { "select_next", "fallback" },
+ ["<C-p>"] = { "select_prev", "fallback" },
+ ["<C-k>"] = { "snippet_forward", "fallback" },
+ ["<C-j>"] = { "snippet_backward", "fallback" },
+ },
+ sources = {
+ default = { "lsp", "path", "snippets", "buffer" },
+ },
+ snippets = { preset = "luasnip" },
+ completion = {
+ documentation = { auto_show = false, auto_show_delay_ms = 200 },
+ ghost_text = { enabled = false },
+ menu = {
+ auto_show = true,
+ max_height = 5,
+ draw = {
+ -- no kind_icon column -- plain text only, no nerd font glyphs
+ columns = { { "label", "label_description", gap = 1 } },
+ },
+ },
+ },
+ },
+ },
+ {
+ "L3MON4D3/LuaSnip",
+ event = "InsertEnter",
+ dependencies = { "rafamadriz/friendly-snippets" },
+ config = function()
+ require("luasnip.loaders.from_vscode").lazy_load()
+ end,
+ },
+
+ -- LSP
+ {
+ "neovim/nvim-lspconfig",
+ event = { "BufReadPre", "BufNewFile" },
+ dependencies = { "saghen/blink.cmp" },
+ config = function()
+ vim.lsp.config("*", {
+ capabilities = require("blink.cmp").get_lsp_capabilities(),
+ })
+
+ vim.lsp.enable({ "clangd" })
+
+ vim.api.nvim_create_autocmd("LspAttach", {
+ callback = function(ev)
+ local m = function(keys, func, desc)
+ vim.keymap.set("n", keys, func, { buffer = ev.buf, desc = "LSP: " .. desc })
+ end
+ if vim.lsp.inlay_hint then
+ vim.lsp.inlay_hint.enable(true, { bufnr = ev.buf })
+ end
+ m("gd", vim.lsp.buf.definition, "Goto definition")
+ m("gD", vim.lsp.buf.declaration, "Goto declaration")
+ m("gr", vim.lsp.buf.references, "Goto references")
+ m("gI", vim.lsp.buf.implementation, "Goto implementation")
+ m("K", vim.lsp.buf.hover, "Hover docs")
+ m("<leader>rn", vim.lsp.buf.rename, "Rename")
+ m("[d", vim.diagnostic.goto_prev, "Prev diagnostic")
+ m("]d", vim.diagnostic.goto_next, "Next diagnostic")
+ m("<leader>e", vim.diagnostic.open_float, "Show diagnostic")
+ end,
+ })
+
+ vim.diagnostic.config({
+ virtual_text = true,
+ signs = {
+ text = {
+ [vim.diagnostic.severity.ERROR] = "E",
+ [vim.diagnostic.severity.WARN] = "W",
+ [vim.diagnostic.severity.INFO] = "I",
+ [vim.diagnostic.severity.HINT] = "H",
+ },
+ },
+ underline = true,
+ update_in_insert = false,
+ })
+ end,
+ },
+
+}, {
+ install = { colorscheme = { "vim" } },
+ checker = { enabled = false },
+ change_detection = { notify = false },
+})
+
+vim.cmd.colorscheme("vim")