From 6f31a04b5d6962106a4646f7abbdf8778a102fd3 Mon Sep 17 00:00:00 2001 From: David Stosik Date: Tue, 3 Mar 2026 10:23:57 +0900 Subject: [PATCH 1/4] feat: add mitamae recipe for cross-platform package installation mitamae is a single static binary (mruby compiled in) that provides a Chef-like DSL for package management. Its `package` resource auto-detects the system package manager (Homebrew on macOS, apt on Linux), so one recipe works everywhere. bootstrap.sh downloads the right mitamae binary for the current platform (macOS/Linux, x86_64/aarch64) and runs the recipe. Packages installed: neovim, gh, tmux, ripgrep, fzf, mise. Ghostty (brew cask) is macOS-only. mitamae handles package installation; install.sh stays for symlinking. Clean separation of concerns. --- mitamae/.gitignore | 2 ++ mitamae/bootstrap.sh | 74 ++++++++++++++++++++++++++++++++++++++++++++ mitamae/recipe.rb | 55 ++++++++++++++++++++++++++++++++ 3 files changed, 131 insertions(+) create mode 100644 mitamae/.gitignore create mode 100755 mitamae/bootstrap.sh create mode 100644 mitamae/recipe.rb diff --git a/mitamae/.gitignore b/mitamae/.gitignore new file mode 100644 index 0000000..6c0c2d1 --- /dev/null +++ b/mitamae/.gitignore @@ -0,0 +1,2 @@ +# Downloaded mitamae binary +bin/ diff --git a/mitamae/bootstrap.sh b/mitamae/bootstrap.sh new file mode 100755 index 0000000..520d391 --- /dev/null +++ b/mitamae/bootstrap.sh @@ -0,0 +1,74 @@ +#!/usr/bin/env bash +# bootstrap.sh — download mitamae and run the package recipe +# +# Usage: ./mitamae/bootstrap.sh +# +# Downloads the mitamae binary for the current platform from GitHub releases, +# then runs recipe.rb to install packages. + +set -euo pipefail + +MITAMAE_VERSION="v1.14.4" +MITAMAE_DIR="$(cd "$(dirname "$0")" && pwd)" +MITAMAE_BIN="${MITAMAE_DIR}/bin/mitamae" + +# --- Detect platform --- +detect_platform() { + local os arch + + case "$(uname -s)" in + Darwin) os="darwin" ;; + Linux) os="linux" ;; + *) + echo "Unsupported OS: $(uname -s)" >&2 + exit 1 + ;; + esac + + case "$(uname -m)" in + x86_64|amd64) arch="x86_64" ;; + aarch64|arm64) arch="aarch64" ;; + *) + echo "Unsupported architecture: $(uname -m)" >&2 + exit 1 + ;; + esac + + echo "${arch}-${os}" +} + +# --- Download mitamae --- +download_mitamae() { + if [[ -x "${MITAMAE_BIN}" ]]; then + local current_version + current_version=$("${MITAMAE_BIN}" version 2>/dev/null || echo "unknown") + if [[ "${current_version}" == *"${MITAMAE_VERSION#v}"* ]]; then + echo "✓ mitamae ${MITAMAE_VERSION} already installed" + return + fi + fi + + local platform + platform="$(detect_platform)" + local url="https://github.com/itamae-kitchen/mitamae/releases/download/${MITAMAE_VERSION}/mitamae-${platform}" + + echo "→ Downloading mitamae ${MITAMAE_VERSION} for ${platform}..." + mkdir -p "${MITAMAE_DIR}/bin" + curl -fsSL -o "${MITAMAE_BIN}" "${url}" + chmod +x "${MITAMAE_BIN}" + echo "✓ mitamae installed to ${MITAMAE_BIN}" +} + +# --- Main --- +echo "" +echo " mitamae bootstrap" +echo " =================" +echo "" + +download_mitamae + +echo "" +echo "→ Running recipe..." +echo "" + +exec "${MITAMAE_BIN}" local "${MITAMAE_DIR}/recipe.rb" diff --git a/mitamae/recipe.rb b/mitamae/recipe.rb new file mode 100644 index 0000000..16046a7 --- /dev/null +++ b/mitamae/recipe.rb @@ -0,0 +1,55 @@ +# recipe.rb — cross-platform package installation +# +# Uses mitamae's `package` resource which auto-detects the package manager: +# - Homebrew on macOS +# - apt on Debian/Ubuntu Linux +# +# Run via: mitamae local recipe.rb + +# --- Helpers --- +def macos? + node[:platform] == "darwin" +end + +def linux? + node[:platform] == "linux" +end + +# --- Core packages --- +# These are needed on all platforms. + +package "neovim" do + not_if "which nvim" +end + +package "gh" do + not_if "which gh" +end + +package "tmux" do + not_if "which tmux" +end + +package "ripgrep" do + not_if "which rg" +end + +package "fzf" do + not_if "which fzf" +end + +# --- mise (polyglot version manager) --- +# mise isn't in most distro repos, so install via its official script. +execute "install mise" do + command "curl -fsSL https://mise.jdx.dev/install.sh | sh" + not_if "which mise" +end + +# --- macOS-only packages --- +if macos? + # Ghostty is distributed as a macOS app (brew cask) + execute "install ghostty" do + command "brew install --cask ghostty" + not_if "brew list --cask ghostty >/dev/null 2>&1" + end +end From aeb4522f667c22114d5c325e304fd62305ed577f Mon Sep 17 00:00:00 2001 From: David Stosik Date: Mon, 2 Mar 2026 15:49:30 +0900 Subject: [PATCH 2/4] =?UTF-8?q?feat:=20neovim=20=E2=80=94=20explicit=20laz?= =?UTF-8?q?y.nvim=20config=20(no=20LazyVim)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Complete Neovim configuration using lazy.nvim as plugin manager, with every plugin explicitly configured. No LazyVim framework. Structure: config/nvim/ ├── init.lua — entry point └── lua/ ├── options.lua — editor options ├── keymaps.lua — key mappings ├── autocmds.lua — autocommands └── plugins/ ├── init.lua — lazy.nvim bootstrap ├── colorscheme.lua — Tokyo Night ├── telescope.lua — fuzzy finder ├── treesitter.lua — syntax highlighting ├── lsp.lua — language servers (Mason + lspconfig) ├── completion.lua — nvim-cmp ├── ui.lua — lualine, oil.nvim, gitsigns, which-key, indent guides └── editor.lua — comments, pairs, surround, endwise, fugitive, trouble Plugin choices: - Tokyo Night colorscheme (consistent with tmux + ghostty theme) - Telescope with fzf-native (replaces fzf.vim from v3) - Treesitter for Ruby, Lua, JS/TS, Bash, YAML, Markdown, and more - LSP via Mason: ruby_lsp and lua_ls pre-configured - nvim-cmp with Tab completion (LSP, buffer, path, snippets) - Lualine statusline with Tokyo Night theme - Oil.nvim as file explorer (press - to open parent dir) - Gitsigns for git gutter decorations + hunk navigation - vim-fugitive for :Git commands (carried from v1-v3 vim config) - Comment.nvim (replaces vim-commentary from v3) - nvim-surround, nvim-autopairs (replaces auto-pairs from v3) - nvim-treesitter-endwise (replaces vim-endwise from v1-v3) - Trouble for diagnostics list - Todo-comments for TODO/FIXME highlighting - Which-key for keybinding discovery - vim-sleuth for auto-indentation detection (from v3) Key mappings: - Leader: , (comma, same as v1-v3 vim config) - C-p: find files (same as v1-v3 vim config) - Leader+f*: telescope pickers (files, grep, buffers, etc.) - gd/gr/gi/K: LSP navigation - Leader+ca: code action, Leader+rn: rename - gc/gcc: toggle comments - -: open file explorer (oil.nvim) - ]/[h: next/prev git hunk - Leader+xx: diagnostics list (Trouble) Design decisions: - No LazyVim — full control over every plugin and mapping - Lazy-load where sensible (events, commands, keys) but don't be extreme - Mason for LSP server management (auto-install, cross-platform) - Oil.nvim over nvim-tree — lighter, feels more like editing a buffer - Explicit over implicit — every option documented in its file --- config/nvim/init.lua | 10 +++ config/nvim/lua/autocmds.lua | 49 +++++++++++++ config/nvim/lua/keymaps.lua | 55 ++++++++++++++ config/nvim/lua/options.lua | 64 ++++++++++++++++ config/nvim/lua/plugins/colorscheme.lua | 23 ++++++ config/nvim/lua/plugins/completion.lua | 79 ++++++++++++++++++++ config/nvim/lua/plugins/editor.lua | 62 ++++++++++++++++ config/nvim/lua/plugins/init.lua | 39 ++++++++++ config/nvim/lua/plugins/lsp.lua | 98 +++++++++++++++++++++++++ config/nvim/lua/plugins/telescope.lua | 49 +++++++++++++ config/nvim/lua/plugins/treesitter.lua | 47 ++++++++++++ config/nvim/lua/plugins/ui.lua | 88 ++++++++++++++++++++++ 12 files changed, 663 insertions(+) create mode 100644 config/nvim/init.lua create mode 100644 config/nvim/lua/autocmds.lua create mode 100644 config/nvim/lua/keymaps.lua create mode 100644 config/nvim/lua/options.lua create mode 100644 config/nvim/lua/plugins/colorscheme.lua create mode 100644 config/nvim/lua/plugins/completion.lua create mode 100644 config/nvim/lua/plugins/editor.lua create mode 100644 config/nvim/lua/plugins/init.lua create mode 100644 config/nvim/lua/plugins/lsp.lua create mode 100644 config/nvim/lua/plugins/telescope.lua create mode 100644 config/nvim/lua/plugins/treesitter.lua create mode 100644 config/nvim/lua/plugins/ui.lua diff --git a/config/nvim/init.lua b/config/nvim/init.lua new file mode 100644 index 0000000..8fb3b49 --- /dev/null +++ b/config/nvim/init.lua @@ -0,0 +1,10 @@ +-- Neovim configuration — David Stosik +-- Plugin manager: lazy.nvim (NOT LazyVim) +-- Theme: Tokyo Night + +require("options") +require("keymaps") +require("autocmds") + +-- Bootstrap and load plugins +require("plugins") diff --git a/config/nvim/lua/autocmds.lua b/config/nvim/lua/autocmds.lua new file mode 100644 index 0000000..12952ed --- /dev/null +++ b/config/nvim/lua/autocmds.lua @@ -0,0 +1,49 @@ +-- Autocommands + +local augroup = vim.api.nvim_create_augroup +local autocmd = vim.api.nvim_create_autocmd + +-- Highlight on yank +autocmd("TextYankPost", { + group = augroup("YankHighlight", { clear = true }), + callback = function() + vim.highlight.on_yank({ higroup = "IncSearch", timeout = 200 }) + end, +}) + +-- Restore cursor position when reopening a file +autocmd("BufReadPost", { + group = augroup("RestoreCursor", { clear = true }), + callback = function() + local mark = vim.api.nvim_buf_get_mark(0, '"') + local lcount = vim.api.nvim_buf_line_count(0) + if mark[1] > 0 and mark[1] <= lcount then + pcall(vim.api.nvim_win_set_cursor, 0, mark) + end + end, +}) + +-- Remove trailing whitespace on save for certain filetypes +autocmd("BufWritePre", { + group = augroup("TrimWhitespace", { clear = true }), + pattern = { "*.rb", "*.lua", "*.sh", "*.zsh", "*.yml", "*.yaml", "*.json", "*.md" }, + callback = function() + local save = vim.fn.winsaveview() + vim.cmd([[%s/\s\+$//e]]) + vim.fn.winrestview(save) + end, +}) + +-- Auto-reload files changed outside of Neovim +autocmd({ "FocusGained", "TermClose", "TermLeave" }, { + group = augroup("AutoReload", { clear = true }), + command = "checktime", +}) + +-- Resize splits when terminal is resized +autocmd("VimResized", { + group = augroup("ResizeSplits", { clear = true }), + callback = function() + vim.cmd("tabdo wincmd =") + end, +}) diff --git a/config/nvim/lua/keymaps.lua b/config/nvim/lua/keymaps.lua new file mode 100644 index 0000000..973a10f --- /dev/null +++ b/config/nvim/lua/keymaps.lua @@ -0,0 +1,55 @@ +-- Keymaps + +local map = vim.keymap.set + +-- Better escape +map("i", "jk", "", { desc = "Escape insert mode" }) + +-- Clear search highlight +map("n", "", "nohlsearch", { desc = "Clear search highlight" }) + +-- Window navigation +map("n", "", "h", { desc = "Move to left window" }) +map("n", "", "j", { desc = "Move to lower window" }) +map("n", "", "k", { desc = "Move to upper window" }) +map("n", "", "l", { desc = "Move to right window" }) + +-- Window resizing +map("n", "", "resize +2", { desc = "Increase window height" }) +map("n", "", "resize -2", { desc = "Decrease window height" }) +map("n", "", "vertical resize -2", { desc = "Decrease window width" }) +map("n", "", "vertical resize +2", { desc = "Increase window width" }) + +-- Move lines up/down in visual mode +map("v", "J", ":m '>+1gv=gv", { desc = "Move selection down" }) +map("v", "K", ":m '<-2gv=gv", { desc = "Move selection up" }) + +-- Keep cursor centered when scrolling +map("n", "", "zz") +map("n", "", "zz") + +-- Keep search results centered +map("n", "n", "nzzzv") +map("n", "N", "Nzzzv") + +-- Don't lose selection when indenting +map("v", "<", "", ">gv") + +-- Buffer navigation +map("n", "", "bprevious", { desc = "Previous buffer" }) +map("n", "", "bnext", { desc = "Next buffer" }) +map("n", "bd", "bdelete", { desc = "Delete buffer" }) + +-- Vertical diff split +vim.opt.diffopt:append("vertical") + +-- Save with Ctrl-S (also in insert mode) +map({ "n", "i" }, "", "w", { desc = "Save file" }) + +-- Quit +map("n", "q", "q", { desc = "Quit" }) +map("n", "Q", "qa!", { desc = "Force quit all" }) + +-- Delete trailing whitespace +map("n", "", [[let _s=@/:%s/\s\+$//e:let @/=_s:nohl]], { desc = "Delete trailing whitespace" }) diff --git a/config/nvim/lua/options.lua b/config/nvim/lua/options.lua new file mode 100644 index 0000000..5b061d9 --- /dev/null +++ b/config/nvim/lua/options.lua @@ -0,0 +1,64 @@ +-- Options + +local opt = vim.opt + +-- Line numbers +opt.number = true +opt.relativenumber = true + +-- Indentation +opt.expandtab = true +opt.shiftwidth = 2 +opt.softtabstop = 2 +opt.tabstop = 2 +opt.smartindent = true + +-- Search +opt.ignorecase = true +opt.smartcase = true +opt.hlsearch = true +opt.incsearch = true +opt.gdefault = true -- Substitute globally by default + +-- Display +opt.termguicolors = true +opt.signcolumn = "yes" +opt.cursorline = true +opt.scrolloff = 8 +opt.sidescrolloff = 8 +opt.colorcolumn = "81" +opt.showmode = false -- Shown by lualine instead +opt.wrap = false +opt.list = true +opt.listchars = { tab = "» ", trail = "·", extends = ">", precedes = "<", nbsp = "%" } + +-- Splits +opt.splitbelow = true +opt.splitright = true + +-- Files +opt.undofile = true +opt.backup = false +opt.swapfile = false +opt.autoread = true + +-- Clipboard (sync with system) +opt.clipboard = "unnamedplus" + +-- Completion +opt.completeopt = { "menu", "menuone", "noselect" } +opt.pumheight = 10 + +-- Misc +opt.updatetime = 250 +opt.timeoutlen = 300 +opt.mouse = "a" +opt.wildmode = { "list", "longest" } + +-- Leader +vim.g.mapleader = "," +vim.g.maplocalleader = "," + +-- Disable netrw (we use telescope and oil.nvim) +vim.g.loaded_netrw = 1 +vim.g.loaded_netrwPlugin = 1 diff --git a/config/nvim/lua/plugins/colorscheme.lua b/config/nvim/lua/plugins/colorscheme.lua new file mode 100644 index 0000000..5dbaf19 --- /dev/null +++ b/config/nvim/lua/plugins/colorscheme.lua @@ -0,0 +1,23 @@ +-- Tokyo Night colorscheme +return { + { + "folke/tokyonight.nvim", + lazy = false, + priority = 1000, + opts = { + style = "night", + transparent = false, + terminal_colors = true, + styles = { + comments = { italic = true }, + keywords = { italic = true }, + sidebars = "dark", + floats = "dark", + }, + }, + config = function(_, opts) + require("tokyonight").setup(opts) + vim.cmd.colorscheme("tokyonight") + end, + }, +} diff --git a/config/nvim/lua/plugins/completion.lua b/config/nvim/lua/plugins/completion.lua new file mode 100644 index 0000000..5b4b9ff --- /dev/null +++ b/config/nvim/lua/plugins/completion.lua @@ -0,0 +1,79 @@ +-- Completion — nvim-cmp +return { + { + "hrsh7th/nvim-cmp", + event = "InsertEnter", + dependencies = { + "hrsh7th/cmp-nvim-lsp", -- LSP completions + "hrsh7th/cmp-buffer", -- Buffer words + "hrsh7th/cmp-path", -- File paths + "hrsh7th/cmp-cmdline", -- Command-line completions + "L3MON4D3/LuaSnip", -- Snippet engine + "saadparwaiz1/cmp_luasnip", -- Snippet completions + }, + config = function() + local cmp = require("cmp") + local luasnip = require("luasnip") + + cmp.setup({ + snippet = { + expand = function(args) + luasnip.lsp_expand(args.body) + end, + }, + window = { + completion = cmp.config.window.bordered(), + documentation = cmp.config.window.bordered(), + }, + mapping = cmp.mapping.preset.insert({ + [""] = cmp.mapping.scroll_docs(-4), + [""] = cmp.mapping.scroll_docs(4), + [""] = cmp.mapping.complete(), + [""] = cmp.mapping.abort(), + [""] = cmp.mapping.confirm({ select = false }), + -- Tab: select next item or jump in snippet + [""] = cmp.mapping(function(fallback) + if cmp.visible() then + cmp.select_next_item() + elseif luasnip.expand_or_jumpable() then + luasnip.expand_or_jump() + else + fallback() + end + end, { "i", "s" }), + [""] = cmp.mapping(function(fallback) + if cmp.visible() then + cmp.select_prev_item() + elseif luasnip.jumpable(-1) then + luasnip.jump(-1) + else + fallback() + end + end, { "i", "s" }), + }), + sources = cmp.config.sources({ + { name = "nvim_lsp" }, + { name = "luasnip" }, + { name = "path" }, + }, { + { name = "buffer" }, + }), + }) + + -- Command-line completion for / search + cmp.setup.cmdline("/", { + mapping = cmp.mapping.preset.cmdline(), + sources = { { name = "buffer" } }, + }) + + -- Command-line completion for : commands + cmp.setup.cmdline(":", { + mapping = cmp.mapping.preset.cmdline(), + sources = cmp.config.sources( + { { name = "path" } }, + { { name = "cmdline" } } + ), + }) + end, + }, +} diff --git a/config/nvim/lua/plugins/editor.lua b/config/nvim/lua/plugins/editor.lua new file mode 100644 index 0000000..bf9b1cb --- /dev/null +++ b/config/nvim/lua/plugins/editor.lua @@ -0,0 +1,62 @@ +-- Editor plugins — quality of life improvements +return { + -- Comment.nvim — toggle comments (gc/gcc) + { + "numToStr/Comment.nvim", + event = { "BufReadPre", "BufNewFile" }, + opts = {}, + }, + + -- Auto pairs — automatically close brackets, quotes, etc. + { + "windwp/nvim-autopairs", + event = "InsertEnter", + opts = {}, + }, + + -- Surround — add/change/delete surrounding characters (ys, cs, ds) + { + "kylechui/nvim-surround", + version = "*", + event = "VeryLazy", + opts = {}, + }, + + -- Sleuth — auto-detect indentation + { + "tpope/vim-sleuth", + }, + + -- Fugitive — Git commands (:Git, :Gblame, etc.) + { + "tpope/vim-fugitive", + cmd = { "Git", "G", "Gblame", "Gdiff", "Glog" }, + }, + + -- Endwise — automatically add `end` in Ruby, Lua, etc. + { + "RRethy/nvim-treesitter-endwise", + event = "InsertEnter", + dependencies = { "nvim-treesitter/nvim-treesitter" }, + }, + + -- Trouble — better diagnostics list + { + "folke/trouble.nvim", + dependencies = { "nvim-tree/nvim-web-devicons" }, + cmd = { "Trouble" }, + keys = { + { "xx", "Trouble diagnostics toggle", desc = "Diagnostics (Trouble)" }, + { "xX", "Trouble diagnostics toggle filter.buf=0", desc = "Buffer diagnostics (Trouble)" }, + }, + opts = {}, + }, + + -- Todo comments — highlight TODO, FIXME, etc. + { + "folke/todo-comments.nvim", + dependencies = { "nvim-lua/plenary.nvim" }, + event = { "BufReadPre", "BufNewFile" }, + opts = {}, + }, +} diff --git a/config/nvim/lua/plugins/init.lua b/config/nvim/lua/plugins/init.lua new file mode 100644 index 0000000..2fc9a41 --- /dev/null +++ b/config/nvim/lua/plugins/init.lua @@ -0,0 +1,39 @@ +-- Bootstrap lazy.nvim +local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim" +if not (vim.uv or vim.loop).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) + +-- Load all plugin specs from this directory +require("lazy").setup({ + spec = { + { import = "plugins.colorscheme" }, + { import = "plugins.telescope" }, + { import = "plugins.treesitter" }, + { import = "plugins.lsp" }, + { import = "plugins.completion" }, + { import = "plugins.ui" }, + { import = "plugins.editor" }, + }, + defaults = { lazy = false }, + install = { colorscheme = { "tokyonight", "habamax" } }, + checker = { enabled = false }, -- Don't auto-check for updates + change_detection = { notify = false }, + performance = { + rtp = { + disabled_plugins = { + "gzip", + "tarPlugin", + "tohtml", + "tutor", + "zipPlugin", + }, + }, + }, +}) diff --git a/config/nvim/lua/plugins/lsp.lua b/config/nvim/lua/plugins/lsp.lua new file mode 100644 index 0000000..680f255 --- /dev/null +++ b/config/nvim/lua/plugins/lsp.lua @@ -0,0 +1,98 @@ +-- LSP — Language Server Protocol configuration +return { + -- Mason: auto-install LSP servers, formatters, linters + { + "williamboman/mason.nvim", + cmd = "Mason", + opts = {}, + }, + + -- Bridge between Mason and lspconfig + { + "williamboman/mason-lspconfig.nvim", + dependencies = { "williamboman/mason.nvim" }, + opts = { + ensure_installed = { + "lua_ls", -- Lua + "ruby_lsp", -- Ruby (Shopify's ruby-lsp) + }, + automatic_installation = true, + }, + }, + + -- LSP configuration + { + "neovim/nvim-lspconfig", + event = { "BufReadPre", "BufNewFile" }, + dependencies = { + "williamboman/mason.nvim", + "williamboman/mason-lspconfig.nvim", + }, + config = function() + local lspconfig = require("lspconfig") + local capabilities = vim.lsp.protocol.make_client_capabilities() + + -- Merge with nvim-cmp capabilities if available + local ok, cmp_lsp = pcall(require, "cmp_nvim_lsp") + if ok then + capabilities = vim.tbl_deep_extend("force", capabilities, cmp_lsp.default_capabilities()) + end + + -- Keymaps (set on LspAttach so they only apply when LSP is active) + vim.api.nvim_create_autocmd("LspAttach", { + group = vim.api.nvim_create_augroup("LspKeymaps", { clear = true }), + callback = function(event) + local map = function(keys, func, desc) + vim.keymap.set("n", keys, func, { buffer = event.buf, desc = "LSP: " .. desc }) + end + + map("gd", vim.lsp.buf.definition, "Go to definition") + map("gD", vim.lsp.buf.declaration, "Go to declaration") + map("gr", vim.lsp.buf.references, "Go to references") + map("gi", vim.lsp.buf.implementation, "Go to implementation") + map("K", vim.lsp.buf.hover, "Hover documentation") + map("ca", vim.lsp.buf.code_action, "Code action") + map("rn", vim.lsp.buf.rename, "Rename symbol") + map("D", vim.lsp.buf.type_definition, "Type definition") + map("ds", vim.lsp.buf.document_symbol, "Document symbols") + map("[d", vim.diagnostic.goto_prev, "Previous diagnostic") + map("]d", vim.diagnostic.goto_next, "Next diagnostic") + map("e", vim.diagnostic.open_float, "Show diagnostic") + end, + }) + + -- Server configurations + lspconfig.lua_ls.setup({ + capabilities = capabilities, + settings = { + Lua = { + runtime = { version = "LuaJIT" }, + workspace = { + checkThirdParty = false, + library = { vim.env.VIMRUNTIME }, + }, + diagnostics = { globals = { "vim" } }, + telemetry = { enable = false }, + }, + }, + }) + + lspconfig.ruby_lsp.setup({ + capabilities = capabilities, + }) + + -- Diagnostic display configuration + vim.diagnostic.config({ + virtual_text = { spacing = 4, prefix = "●" }, + signs = true, + underline = true, + update_in_insert = false, + severity_sort = true, + float = { + border = "rounded", + source = true, + }, + }) + end, + }, +} diff --git a/config/nvim/lua/plugins/telescope.lua b/config/nvim/lua/plugins/telescope.lua new file mode 100644 index 0000000..3cd43f3 --- /dev/null +++ b/config/nvim/lua/plugins/telescope.lua @@ -0,0 +1,49 @@ +-- Telescope — fuzzy finder +return { + { + "nvim-telescope/telescope.nvim", + branch = "0.1.x", + dependencies = { + "nvim-lua/plenary.nvim", + { + "nvim-telescope/telescope-fzf-native.nvim", + build = "make", + cond = function() + return vim.fn.executable("make") == 1 + end, + }, + }, + keys = { + { "", "Telescope find_files", desc = "Find files" }, + { "ff", "Telescope find_files", desc = "Find files" }, + { "fg", "Telescope live_grep", desc = "Live grep" }, + { "fb", "Telescope buffers", desc = "Buffers" }, + { "fh", "Telescope help_tags", desc = "Help tags" }, + { "fr", "Telescope oldfiles", desc = "Recent files" }, + { "fw", "Telescope grep_string", desc = "Grep word under cursor" }, + { "fd", "Telescope diagnostics", desc = "Diagnostics" }, + { "fs", "Telescope lsp_document_symbols", desc = "Document symbols" }, + { "fp", function() + require("telescope.builtin").find_files({ cwd = require("lazy.core.config").options.root }) + end, desc = "Find plugin file" }, + }, + opts = { + defaults = { + layout_strategy = "horizontal", + layout_config = { prompt_position = "top" }, + sorting_strategy = "ascending", + file_ignore_patterns = { "node_modules", ".git/" }, + }, + pickers = { + find_files = { + hidden = true, + }, + }, + }, + config = function(_, opts) + local telescope = require("telescope") + telescope.setup(opts) + pcall(telescope.load_extension, "fzf") + end, + }, +} diff --git a/config/nvim/lua/plugins/treesitter.lua b/config/nvim/lua/plugins/treesitter.lua new file mode 100644 index 0000000..bcea5d4 --- /dev/null +++ b/config/nvim/lua/plugins/treesitter.lua @@ -0,0 +1,47 @@ +-- Treesitter — syntax highlighting & text objects +return { + { + "nvim-treesitter/nvim-treesitter", + build = ":TSUpdate", + event = { "BufReadPre", "BufNewFile" }, + opts = { + ensure_installed = { + "bash", + "css", + "diff", + "dockerfile", + "gitcommit", + "html", + "javascript", + "json", + "lua", + "markdown", + "markdown_inline", + "query", + "regex", + "ruby", + "toml", + "tsx", + "typescript", + "vim", + "vimdoc", + "yaml", + }, + auto_install = true, + highlight = { enable = true }, + indent = { enable = true }, + incremental_selection = { + enable = true, + keymaps = { + init_selection = "", + node_incremental = "", + scope_incremental = false, + node_decremental = "", + }, + }, + }, + config = function(_, opts) + require("nvim-treesitter.configs").setup(opts) + end, + }, +} diff --git a/config/nvim/lua/plugins/ui.lua b/config/nvim/lua/plugins/ui.lua new file mode 100644 index 0000000..1add00a --- /dev/null +++ b/config/nvim/lua/plugins/ui.lua @@ -0,0 +1,88 @@ +-- UI plugins — statusline, file explorer, indent guides, etc. +return { + -- Lualine — statusline + { + "nvim-lualine/lualine.nvim", + dependencies = { "nvim-tree/nvim-web-devicons" }, + event = "VeryLazy", + opts = { + options = { + theme = "tokyonight", + component_separators = { left = "", right = "" }, + section_separators = { left = "", right = "" }, + globalstatus = true, + }, + sections = { + lualine_a = { "mode" }, + lualine_b = { "branch", "diff", "diagnostics" }, + lualine_c = { { "filename", path = 1 } }, + lualine_x = { "encoding", "fileformat", "filetype" }, + lualine_y = { "progress" }, + lualine_z = { "location" }, + }, + }, + }, + + -- Oil.nvim — file explorer as a buffer (edit your filesystem like a buffer) + { + "stevearc/oil.nvim", + dependencies = { "nvim-tree/nvim-web-devicons" }, + keys = { + { "-", "Oil", desc = "Open parent directory" }, + }, + opts = { + view_options = { + show_hidden = true, + }, + }, + }, + + -- Indent blankline — show indent guides + { + "lukas-reineke/indent-blankline.nvim", + main = "ibl", + event = { "BufReadPre", "BufNewFile" }, + opts = { + indent = { char = "│" }, + scope = { enabled = true }, + }, + }, + + -- Which-key — show keybinding hints + { + "folke/which-key.nvim", + event = "VeryLazy", + opts = {}, + }, + + -- Gitsigns — git decorations in the gutter + { + "lewis6991/gitsigns.nvim", + event = { "BufReadPre", "BufNewFile" }, + opts = { + signs = { + add = { text = "│" }, + change = { text = "│" }, + delete = { text = "_" }, + topdelete = { text = "‾" }, + changedelete = { text = "~" }, + }, + on_attach = function(bufnr) + local gs = package.loaded.gitsigns + local map = function(mode, l, r, desc) + vim.keymap.set(mode, l, r, { buffer = bufnr, desc = desc }) + end + + map("n", "]h", gs.next_hunk, "Next hunk") + map("n", "[h", gs.prev_hunk, "Previous hunk") + map("n", "hs", gs.stage_hunk, "Stage hunk") + map("n", "hr", gs.reset_hunk, "Reset hunk") + map("n", "hp", gs.preview_hunk, "Preview hunk") + map("n", "hb", function() gs.blame_line({ full = true }) end, "Blame line") + end, + }, + }, + + -- nvim-web-devicons — file type icons + { "nvim-tree/nvim-web-devicons", lazy = true }, +} From a11d36586e1fcaf313daac22678e8cb8e8e3761b Mon Sep 17 00:00:00 2001 From: David Stosik Date: Mon, 2 Mar 2026 15:51:22 +0900 Subject: [PATCH 3/4] fix: enable treesitter-endwise in treesitter config The endwise plugin requires explicit activation in the treesitter setup opts, not just being listed as a dependency. --- config/nvim/lua/plugins/treesitter.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/config/nvim/lua/plugins/treesitter.lua b/config/nvim/lua/plugins/treesitter.lua index bcea5d4..5fb51a8 100644 --- a/config/nvim/lua/plugins/treesitter.lua +++ b/config/nvim/lua/plugins/treesitter.lua @@ -39,6 +39,7 @@ return { node_decremental = "", }, }, + endwise = { enable = true }, -- requires nvim-treesitter-endwise }, config = function(_, opts) require("nvim-treesitter.configs").setup(opts) From 672b0fe9f611b05576e195843b06c3255731a38f Mon Sep 17 00:00:00 2001 From: David Stosik Date: Tue, 3 Mar 2026 10:26:09 +0900 Subject: [PATCH 4/4] chore: add neovim to install.sh and README Adds the nvim symlink to install.sh and documents the neovim plugin setup in README.md. --- README.md | 45 +++++++++++++++++++++++++++++++++++++++++++-- install.sh | 5 +++++ 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d7d237f..8b138cc 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ My personal dotfiles. macOS primary, Linux compatible. | **zsh** | `zsh/` | Prompt with git info, history, aliases, functions | | **git** | `git/` | Modern defaults, useful aliases, pretty log | | **tmux** | `tmux/` | C-a prefix, vim-style nav, Tokyo Night theme, TPM | +| **Neovim** | `config/nvim/` | lazy.nvim, Tokyo Night, Telescope, Treesitter, LSP, nvim-cmp | | **Ghostty** | `config/ghostty/` | Tokyo Night, Monaspace font, transparency | ## Bootstrap @@ -32,6 +33,7 @@ cd ~/.dotfiles 1. **Restart your shell** (or `source ~/.zshrc`) 2. **tmux:** Press `C-a I` to install tmux plugins via TPM +3. **Neovim:** Open `nvim` — plugins install automatically on first launch ## Local overrides @@ -70,10 +72,48 @@ dotfiles/ │ ├── tmux.conf # tmux configuration │ └── tmux.mac.conf # macOS-specific (pbcopy integration) └── config/ - └── ghostty/ - └── config # Ghostty terminal config + ├── ghostty/ + │ └── config # Ghostty terminal config + └── nvim/ + ├── init.lua # Entry point + └── lua/ + ├── options.lua # Editor options + ├── keymaps.lua # Key mappings + ├── autocmds.lua # Autocommands + └── plugins/ + ├── init.lua # lazy.nvim bootstrap + ├── colorscheme.lua # Tokyo Night + ├── telescope.lua # Fuzzy finder + ├── treesitter.lua # Syntax highlighting + ├── lsp.lua # Language servers + ├── completion.lua # nvim-cmp + ├── ui.lua # Statusline, file explorer, git signs + └── editor.lua # Comments, pairs, surround, etc. ``` +## Neovim plugins + +Managed by [lazy.nvim](https://github.com/folke/lazy.nvim) (not LazyVim): + +- **Tokyo Night** — colorscheme +- **Telescope** — fuzzy finder (files, grep, buffers, symbols) +- **Treesitter** — syntax highlighting, text objects, incremental selection +- **LSP** — language servers via Mason (Ruby, Lua out of the box) +- **nvim-cmp** — autocompletion with LSP, buffer, path, and snippet sources +- **Lualine** — statusline +- **Oil.nvim** — file explorer as a buffer (`-` to open parent dir) +- **Gitsigns** — git decorations in the gutter +- **Fugitive** — Git commands in Neovim +- **Comment.nvim** — toggle comments (`gc` / `gcc`) +- **nvim-surround** — add/change/delete surrounding chars +- **nvim-autopairs** — auto-close brackets and quotes +- **nvim-treesitter-endwise** — auto-add `end` in Ruby, Lua, etc. +- **Trouble** — better diagnostics list +- **Todo-comments** — highlight TODO/FIXME comments +- **Which-key** — keybinding hints +- **Indent blankline** — indent guides +- **vim-sleuth** — auto-detect indentation + ## Design decisions - **mitamae for package installation** — single static binary (mruby compiled in), no Ruby/RubyGems needed. Chef-like DSL with `package` resource that auto-detects the package manager. One recipe works on macOS (Homebrew) and Linux (apt). Cross-platform from day one. @@ -81,4 +121,5 @@ dotfiles/ - **No dotfiles framework** — chezmoi, yadm, etc. are overkill for this setup. mitamae + install.sh covers packages and links with minimal complexity. - **mise over rbenv/nvm** — single tool for all language versions. - **Local override files** — machine-specific config stays out of the repo. +- **No LazyVim** — lazy.nvim as plugin manager, but every plugin is explicitly configured. Full control, no hidden magic. - **macOS + Linux** — platform conditionals where needed (ls colors, vpn-fix, tmux copy). diff --git a/install.sh b/install.sh index 5d6909b..26513e6 100755 --- a/install.sh +++ b/install.sh @@ -69,6 +69,10 @@ else fi link_file "${DOTFILES_DIR}/config/ghostty/config" "${ghostty_dir}/config" +# Neovim +info "Linking Neovim config..." +link_file "${DOTFILES_DIR}/config/nvim" "${HOME}/.config/nvim" + # TPM (tmux plugin manager) if [[ ! -d "${HOME}/.tmux/plugins/tpm" ]]; then info "Installing TPM (tmux plugin manager)..." @@ -84,6 +88,7 @@ echo "" echo " Next steps:" echo " 1. Restart your shell (or: source ~/.zshrc)" echo " 2. Open tmux and press ${YELLOW}prefix + I${NC} to install tmux plugins" +echo " 3. Open Neovim — plugins will install automatically on first launch" echo "" echo " Optional:" echo " • Create ${YELLOW}~/.zshrc.local${NC} for machine-specific shell config"