Skip to content

Instantly share code, notes, and snippets.

@lightstrike
Created April 22, 2026 01:40
Show Gist options
  • Select an option

  • Save lightstrike/9516434ed525b356e9aa7c5541a74047 to your computer and use it in GitHub Desktop.

Select an option

Save lightstrike/9516434ed525b356e9aa7c5541a74047 to your computer and use it in GitHub Desktop.
Lightstrike nvim bootstrap — single script that installs Neovim + lazy.nvim + full plugin config (LSP, Treesitter, Telescope, Catppuccin, conform, etc.)
#!/usr/bin/env bash
set -euo pipefail
# Lightstrike nvim bootstrap
# Installs Neovim (if needed) and writes the full config to ~/.config/nvim.
# Idempotent — safe to re-run. Backs up any existing config first.
#
# Usage:
# curl -fsSL <gist-raw-url> | bash
# # or
# bash nvim-bootstrap.sh
NVIM_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/nvim"
# ---------------------------------------------------------------------------
# 1. Install Neovim if missing
# ---------------------------------------------------------------------------
if ! command -v nvim &>/dev/null; then
echo ":: Installing Neovim..."
if command -v brew &>/dev/null; then
brew install neovim
else
echo "ERROR: Neovim not found and Homebrew not available. Install Neovim manually." >&2
exit 1
fi
fi
echo ":: Neovim $(nvim --version | head -1)"
# ---------------------------------------------------------------------------
# 2. Install external tool dependencies via Homebrew
# ---------------------------------------------------------------------------
BREW_DEPS=(ripgrep fd node prettierd stylua golangci-lint)
if command -v brew &>/dev/null; then
echo ":: Checking Homebrew dependencies..."
for dep in "${BREW_DEPS[@]}"; do
if ! brew list --formula "$dep" &>/dev/null; then
echo " Installing $dep..."
brew install "$dep"
fi
done
fi
# ---------------------------------------------------------------------------
# 3. Back up existing config
# ---------------------------------------------------------------------------
if [ -d "$NVIM_DIR" ]; then
BACKUP="${NVIM_DIR}.bak.$(date +%Y%m%d%H%M%S)"
echo ":: Backing up existing config to $BACKUP"
mv "$NVIM_DIR" "$BACKUP"
fi
mkdir -p "$NVIM_DIR/lua/config" "$NVIM_DIR/lua/plugins"
# ---------------------------------------------------------------------------
# 4. Write config files
# ---------------------------------------------------------------------------
echo ":: Writing config files..."
# -- init.lua ---------------------------------------------------------------
cat > "$NVIM_DIR/init.lua" << 'LUA'
-- Bootstrap lazy.nvim
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not 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 core settings before plugins
require("config.options")
require("config.keymaps")
-- Load plugins via lazy.nvim
require("lazy").setup("plugins", {
change_detection = { notify = false },
install = { colorscheme = { "catppuccin" } },
})
-- Load autocmds after plugins
require("config.autocmds")
LUA
# -- config/options.lua -----------------------------------------------------
cat > "$NVIM_DIR/lua/config/options.lua" << 'LUA'
local opt = vim.opt
-- Line numbers
opt.number = true
opt.relativenumber = true
opt.signcolumn = "yes"
-- Tabs / indentation
opt.tabstop = 2
opt.shiftwidth = 2
opt.expandtab = true
opt.smartindent = true
-- Search
opt.ignorecase = true
opt.smartcase = true
opt.hlsearch = true
opt.incsearch = true
-- Appearance
opt.termguicolors = true
opt.cursorline = true
opt.scrolloff = 8
opt.sidescrolloff = 8
opt.wrap = false
opt.showmode = false
-- Splits
opt.splitbelow = true
opt.splitright = true
-- Performance
opt.updatetime = 250
opt.timeoutlen = 300
-- Undo
opt.undofile = true
opt.swapfile = false
-- Clipboard
opt.clipboard = "unnamedplus"
-- Mouse
opt.mouse = "a"
-- Completion
opt.completeopt = "menu,menuone,noselect"
LUA
# -- config/keymaps.lua -----------------------------------------------------
cat > "$NVIM_DIR/lua/config/keymaps.lua" << 'LUA'
vim.g.mapleader = " "
vim.g.maplocalleader = " "
local map = vim.keymap.set
-- Better window navigation
map("n", "<C-h>", "<C-w>h", { desc = "Go to left window" })
map("n", "<C-j>", "<C-w>j", { desc = "Go to lower window" })
map("n", "<C-k>", "<C-w>k", { desc = "Go to upper window" })
map("n", "<C-l>", "<C-w>l", { desc = "Go to right window" })
-- Resize windows
map("n", "<C-Up>", "<cmd>resize +2<cr>", { desc = "Increase window height" })
map("n", "<C-Down>", "<cmd>resize -2<cr>", { desc = "Decrease window height" })
map("n", "<C-Left>", "<cmd>vertical resize -2<cr>", { desc = "Decrease window width" })
map("n", "<C-Right>", "<cmd>vertical resize +2<cr>", { desc = "Increase window width" })
-- Move lines
map("v", "J", ":m '>+1<cr>gv=gv", { desc = "Move line down" })
map("v", "K", ":m '<-2<cr>gv=gv", { desc = "Move line up" })
-- Better indenting (stay in visual mode)
map("v", "<", "<gv")
map("v", ">", ">gv")
-- Clear search highlight
map("n", "<Esc>", "<cmd>nohlsearch<cr>", { desc = "Clear search highlight" })
-- Diagnostic navigation
map("n", "[d", vim.diagnostic.goto_prev, { desc = "Previous diagnostic" })
map("n", "]d", vim.diagnostic.goto_next, { desc = "Next diagnostic" })
map("n", "<leader>e", vim.diagnostic.open_float, { desc = "Show diagnostic" })
-- Buffer navigation
map("n", "<S-h>", "<cmd>bprevious<cr>", { desc = "Previous buffer" })
map("n", "<S-l>", "<cmd>bnext<cr>", { desc = "Next buffer" })
map("n", "<leader>bd", "<cmd>bdelete<cr>", { desc = "Delete buffer" })
LUA
# -- config/autocmds.lua ----------------------------------------------------
cat > "$NVIM_DIR/lua/config/autocmds.lua" << 'LUA'
local autocmd = vim.api.nvim_create_autocmd
-- Highlight on yank
autocmd("TextYankPost", {
callback = function()
vim.highlight.on_yank({ timeout = 200 })
end,
})
-- Go: use tabs, 4-wide
autocmd("FileType", {
pattern = "go",
callback = function()
vim.opt_local.tabstop = 4
vim.opt_local.shiftwidth = 4
vim.opt_local.expandtab = false
end,
})
-- Python / Rust: 4-space indent
autocmd("FileType", {
pattern = { "python", "rust" },
callback = function()
vim.opt_local.tabstop = 4
vim.opt_local.shiftwidth = 4
end,
})
-- Auto-resize splits on terminal resize
autocmd("VimResized", {
callback = function()
vim.cmd("tabdo wincmd =")
end,
})
LUA
# -- plugins/colorscheme.lua ------------------------------------------------
cat > "$NVIM_DIR/lua/plugins/colorscheme.lua" << 'LUA'
return {
{
"catppuccin/nvim",
name = "catppuccin",
lazy = false,
priority = 1000,
opts = {
flavour = "mocha",
integrations = {
cmp = true,
gitsigns = true,
mason = true,
treesitter = true,
telescope = { enabled = true },
native_lsp = {
enabled = true,
inlay_hints = { background = true },
},
},
},
config = function(_, opts)
require("catppuccin").setup(opts)
vim.cmd.colorscheme("catppuccin")
end,
},
}
LUA
# -- plugins/completion.lua -------------------------------------------------
cat > "$NVIM_DIR/lua/plugins/completion.lua" << 'LUA'
return {
{
"hrsh7th/nvim-cmp",
event = "InsertEnter",
dependencies = {
"hrsh7th/cmp-nvim-lsp",
"hrsh7th/cmp-buffer",
"hrsh7th/cmp-path",
"L3MON4D3/LuaSnip",
"saadparwaiz1/cmp_luasnip",
"rafamadriz/friendly-snippets",
},
config = function()
local cmp = require("cmp")
local luasnip = require("luasnip")
require("luasnip.loaders.from_vscode").lazy_load()
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({
["<C-b>"] = cmp.mapping.scroll_docs(-4),
["<C-f>"] = cmp.mapping.scroll_docs(4),
["<C-Space>"] = cmp.mapping.complete(),
["<C-e>"] = cmp.mapping.abort(),
["<CR>"] = cmp.mapping.confirm({ select = true }),
["<Tab>"] = 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" }),
["<S-Tab>"] = 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" },
}),
})
end,
},
}
LUA
# -- plugins/editor.lua -----------------------------------------------------
cat > "$NVIM_DIR/lua/plugins/editor.lua" << 'LUA'
return {
-- Auto pairs
{
"windwp/nvim-autopairs",
event = "InsertEnter",
config = function()
require("nvim-autopairs").setup({})
-- Integrate with cmp
local cmp_autopairs = require("nvim-autopairs.completion.cmp")
require("cmp").event:on("confirm_done", cmp_autopairs.on_confirm_done())
end,
},
-- Comment toggling
{
"numToStr/Comment.nvim",
keys = {
{ "gcc", mode = "n", desc = "Comment line" },
{ "gc", mode = { "n", "v" }, desc = "Comment" },
},
opts = {},
},
-- Surround
{
"kylechui/nvim-surround",
version = "*",
event = "VeryLazy",
opts = {},
},
-- File explorer
{
"nvim-tree/nvim-tree.lua",
dependencies = { "nvim-tree/nvim-web-devicons" },
keys = {
{ "<leader>t", "<cmd>NvimTreeToggle<cr>", desc = "Toggle file tree" },
},
opts = {
filters = { dotfiles = false },
view = { width = 35 },
},
},
}
LUA
# -- plugins/formatting.lua -------------------------------------------------
cat > "$NVIM_DIR/lua/plugins/formatting.lua" << 'LUA'
return {
{
"stevearc/conform.nvim",
event = { "BufWritePre" },
cmd = { "ConformInfo" },
keys = {
{ "<leader>cf", function() require("conform").format({ async = true }) end, desc = "Format buffer" },
},
opts = {
formatters_by_ft = {
typescript = { "prettierd", "prettier", stop_after_first = true },
typescriptreact = { "prettierd", "prettier", stop_after_first = true },
javascript = { "prettierd", "prettier", stop_after_first = true },
javascriptreact = { "prettierd", "prettier", stop_after_first = true },
json = { "prettierd", "prettier", stop_after_first = true },
css = { "prettierd", "prettier", stop_after_first = true },
html = { "prettierd", "prettier", stop_after_first = true },
markdown = { "prettierd", "prettier", stop_after_first = true },
python = { "ruff_format" },
go = { "goimports", "gofumpt" },
rust = { "rustfmt" },
lua = { "stylua" },
},
format_on_save = {
timeout_ms = 3000,
lsp_format = "fallback",
},
},
},
}
LUA
# -- plugins/linting.lua ----------------------------------------------------
cat > "$NVIM_DIR/lua/plugins/linting.lua" << 'LUA'
return {
{
"mfussenegger/nvim-lint",
event = { "BufReadPre", "BufNewFile" },
config = function()
local lint = require("lint")
lint.linters_by_ft = {
-- ruff is handled via LSP, eslint via LSP
-- Add additional linters here if needed
go = { "golangcilint" },
}
vim.api.nvim_create_autocmd({ "BufEnter", "BufWritePost", "InsertLeave" }, {
callback = function()
lint.try_lint()
end,
})
end,
},
}
LUA
# -- plugins/lsp.lua --------------------------------------------------------
cat > "$NVIM_DIR/lua/plugins/lsp.lua" << 'LUA'
return {
-- Mason: auto-install language servers
{
"williamboman/mason.nvim",
cmd = "Mason",
opts = {},
},
{
"williamboman/mason-lspconfig.nvim",
dependencies = { "williamboman/mason.nvim" },
opts = {
ensure_installed = {
"basedpyright", -- Python
"vtsls", -- TypeScript
"gopls", -- Go
"rust_analyzer", -- Rust
"lua_ls", -- Lua (for editing nvim config)
"eslint", -- ESLint
"ruff", -- Python linting/formatting
},
automatic_installation = true,
},
},
-- LSP keymaps + inlay hints via native Neovim 0.11 API
{
"hrsh7th/cmp-nvim-lsp",
lazy = true,
},
{
"neovim/nvim-lspconfig",
event = { "BufReadPre", "BufNewFile" },
dependencies = {
"williamboman/mason-lspconfig.nvim",
"hrsh7th/cmp-nvim-lsp",
},
config = function()
local capabilities = require("cmp_nvim_lsp").default_capabilities()
-- Shared LspAttach autocmd for keymaps + inlay hints
vim.api.nvim_create_autocmd("LspAttach", {
callback = function(ev)
local bufnr = ev.buf
local client = vim.lsp.get_client_by_id(ev.data.client_id)
local map = function(keys, func, desc)
vim.keymap.set("n", keys, func, { buffer = bufnr, 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, "References")
map("gi", vim.lsp.buf.implementation, "Go to implementation")
map("gy", vim.lsp.buf.type_definition, "Type definition")
map("K", vim.lsp.buf.hover, "Hover docs")
map("<C-k>", vim.lsp.buf.signature_help, "Signature help")
map("<leader>rn", vim.lsp.buf.rename, "Rename")
map("<leader>ca", vim.lsp.buf.code_action, "Code action")
-- Enable inlay hints
if client and client.supports_method("textDocument/inlayHint") then
vim.lsp.inlay_hint.enable(true, { bufnr = bufnr })
end
end,
})
-- Configure servers using native vim.lsp.config (Neovim 0.11+)
vim.lsp.config("vtsls", {
capabilities = capabilities,
settings = {
typescript = {
inlayHints = {
parameterNames = { enabled = "all" },
parameterTypes = { enabled = true },
variableTypes = { enabled = true },
propertyDeclarationTypes = { enabled = true },
functionLikeReturnTypes = { enabled = true },
},
},
javascript = {
inlayHints = {
parameterNames = { enabled = "all" },
variableTypes = { enabled = true },
functionLikeReturnTypes = { enabled = true },
},
},
},
})
vim.lsp.config("basedpyright", {
capabilities = capabilities,
settings = {
basedpyright = {
analysis = {
typeCheckingMode = "standard",
autoImportCompletions = true,
inlayHints = {
variableTypes = true,
callArgumentNames = true,
functionReturnTypes = true,
},
},
},
},
})
vim.lsp.config("ruff", {
capabilities = capabilities,
on_attach = function(client)
-- Disable hover in favor of basedpyright
client.server_capabilities.hoverProvider = false
end,
})
vim.lsp.config("gopls", {
capabilities = capabilities,
settings = {
gopls = {
analyses = {
unusedparams = true,
shadow = true,
},
staticcheck = true,
hints = {
assignVariableTypes = true,
compositeLiteralFields = true,
compositeLiteralTypes = true,
constantValues = true,
functionTypeParameters = true,
parameterNames = true,
rangeVariableTypes = true,
},
},
},
})
vim.lsp.config("eslint", {
capabilities = capabilities,
})
vim.lsp.config("lua_ls", {
capabilities = capabilities,
settings = {
Lua = {
runtime = { version = "LuaJIT" },
workspace = {
checkThirdParty = false,
library = { vim.env.VIMRUNTIME },
},
telemetry = { enable = false },
},
},
})
-- Enable all configured servers
vim.lsp.enable({
"vtsls", "basedpyright", "ruff", "gopls", "eslint", "lua_ls",
})
-- Diagnostic appearance
vim.diagnostic.config({
virtual_text = { spacing = 4, prefix = "●" },
signs = true,
underline = true,
update_in_insert = false,
severity_sort = true,
float = { border = "rounded" },
})
end,
},
}
LUA
# -- plugins/treesitter.lua -------------------------------------------------
cat > "$NVIM_DIR/lua/plugins/treesitter.lua" << 'LUA'
return {
{
"nvim-treesitter/nvim-treesitter",
build = ":TSUpdate",
lazy = false,
config = function()
require("nvim-treesitter").setup()
-- Install grammars
local ensure_installed = {
-- Target languages
"typescript", "tsx", "javascript", "jsdoc",
"python",
"go", "gomod", "gosum", "gowork",
"rust", "toml",
-- General
"lua", "vim", "vimdoc", "query",
"json", "yaml", "html", "css",
"bash", "markdown", "markdown_inline",
"regex", "diff", "gitcommit", "gitignore",
}
-- Use vim.treesitter for highlighting (built-in in 0.11)
vim.api.nvim_create_autocmd("FileType", {
callback = function(ev)
pcall(vim.treesitter.start, ev.buf)
end,
})
-- Install missing parsers (only on first run)
local install = require("nvim-treesitter.install")
for _, lang in ipairs(ensure_installed) do
local ok = pcall(vim.treesitter.language.add, lang)
if not ok then
install.install(lang)
end
end
end,
},
{
"nvim-treesitter/nvim-treesitter-textobjects",
dependencies = "nvim-treesitter/nvim-treesitter",
event = { "BufReadPost", "BufNewFile" },
config = function()
-- textobjects works via queries, not configs in newer versions
local ts_repeat_move = require("nvim-treesitter-textobjects.repeatable_move") or nil
-- Selection keymaps
vim.keymap.set({ "x", "o" }, "af", function()
require("nvim-treesitter-textobjects.select").select_textobject("@function.outer", "textobjects")
end, { desc = "Select outer function" })
vim.keymap.set({ "x", "o" }, "if", function()
require("nvim-treesitter-textobjects.select").select_textobject("@function.inner", "textobjects")
end, { desc = "Select inner function" })
vim.keymap.set({ "x", "o" }, "ac", function()
require("nvim-treesitter-textobjects.select").select_textobject("@class.outer", "textobjects")
end, { desc = "Select outer class" })
vim.keymap.set({ "x", "o" }, "ic", function()
require("nvim-treesitter-textobjects.select").select_textobject("@class.inner", "textobjects")
end, { desc = "Select inner class" })
vim.keymap.set({ "x", "o" }, "aa", function()
require("nvim-treesitter-textobjects.select").select_textobject("@parameter.outer", "textobjects")
end, { desc = "Select outer parameter" })
vim.keymap.set({ "x", "o" }, "ia", function()
require("nvim-treesitter-textobjects.select").select_textobject("@parameter.inner", "textobjects")
end, { desc = "Select inner parameter" })
end,
},
}
LUA
# -- plugins/ui.lua ----------------------------------------------------------
cat > "$NVIM_DIR/lua/plugins/ui.lua" << 'LUA'
return {
-- Telescope (fuzzy finder)
{
"nvim-telescope/telescope.nvim",
branch = "0.1.x",
dependencies = {
"nvim-lua/plenary.nvim",
{ "nvim-telescope/telescope-fzf-native.nvim", build = "make" },
},
cmd = "Telescope",
keys = {
{ "<leader>ff", "<cmd>Telescope find_files<cr>", desc = "Find files" },
{ "<leader>fg", "<cmd>Telescope live_grep<cr>", desc = "Live grep" },
{ "<leader>fb", "<cmd>Telescope buffers<cr>", desc = "Buffers" },
{ "<leader>fh", "<cmd>Telescope help_tags<cr>", desc = "Help tags" },
{ "<leader>fd", "<cmd>Telescope diagnostics<cr>", desc = "Diagnostics" },
{ "<leader>fs", "<cmd>Telescope lsp_document_symbols<cr>", desc = "Document symbols" },
{ "<leader>fr", "<cmd>Telescope lsp_references<cr>", desc = "References" },
},
config = function()
local telescope = require("telescope")
telescope.setup({
defaults = {
file_ignore_patterns = { "node_modules", ".git/", "target/", "__pycache__" },
},
})
telescope.load_extension("fzf")
end,
},
-- Trouble (diagnostics panel)
{
"folke/trouble.nvim",
cmd = "Trouble",
keys = {
{ "<leader>xx", "<cmd>Trouble diagnostics toggle<cr>", desc = "Diagnostics (Trouble)" },
{ "<leader>xX", "<cmd>Trouble diagnostics toggle filter.buf=0<cr>", desc = "Buffer diagnostics" },
},
opts = {},
},
-- Gitsigns
{
"lewis6991/gitsigns.nvim",
event = { "BufReadPre", "BufNewFile" },
opts = {
signs = {
add = { text = "│" },
change = { text = "│" },
delete = { text = "━" },
topdelete = { text = "‾" },
changedelete = { text = "~" },
},
},
},
-- Lualine (status bar)
{
"nvim-lualine/lualine.nvim",
dependencies = { "nvim-tree/nvim-web-devicons" },
event = "VeryLazy",
opts = {
options = {
theme = "catppuccin",
component_separators = { left = "│", right = "│" },
section_separators = { left = "", right = "" },
},
sections = {
lualine_a = { "mode" },
lualine_b = { "branch", "diff", "diagnostics" },
lualine_c = { { "filename", path = 1 } },
lualine_x = { "filetype" },
lualine_y = { "progress" },
lualine_z = { "location" },
},
},
},
-- Indent guides
{
"lukas-reineke/indent-blankline.nvim",
main = "ibl",
event = { "BufReadPost", "BufNewFile" },
opts = {
indent = { char = "│" },
scope = { enabled = true },
},
},
-- Which-key (keybinding hints)
{
"folke/which-key.nvim",
event = "VeryLazy",
opts = {},
},
}
LUA
# -- plugins/go.lua ----------------------------------------------------------
cat > "$NVIM_DIR/lua/plugins/go.lua" << 'LUA'
return {
{
"ray-x/go.nvim",
dependencies = {
"ray-x/guihua.lua",
"neovim/nvim-lspconfig",
"nvim-treesitter/nvim-treesitter",
},
event = { "CmdlineEnter" },
ft = { "go", "gomod" },
build = ':lua require("go.install").update_all_sync()',
opts = {},
},
}
LUA
# -- plugins/python.lua ------------------------------------------------------
cat > "$NVIM_DIR/lua/plugins/python.lua" << 'LUA'
return {
{
"linux-cultist/venv-selector.nvim",
branch = "regexp",
dependencies = {
"neovim/nvim-lspconfig",
"nvim-telescope/telescope.nvim",
},
ft = "python",
keys = {
{ "<leader>vs", "<cmd>VenvSelect<cr>", desc = "Select Python venv" },
},
opts = {},
},
}
LUA
# -- plugins/rust.lua --------------------------------------------------------
cat > "$NVIM_DIR/lua/plugins/rust.lua" << 'LUA'
return {
{
"mrcjkb/rustaceanvim",
version = "^5",
lazy = false,
init = function()
vim.g.rustaceanvim = {
server = {
default_settings = {
["rust-analyzer"] = {
checkOnSave = { command = "clippy" },
inlayHints = {
bindingModeHints = { enable = true },
chainingHints = { enable = true },
closureReturnTypeHints = { enable = "always" },
lifetimeElisionHints = { enable = "always" },
parameterHints = { enable = true },
typeHints = { enable = true },
},
},
},
},
}
end,
},
{
"saecki/crates.nvim",
event = "BufRead Cargo.toml",
opts = {
completion = {
cmp = { enabled = true },
},
},
},
}
LUA
# ---------------------------------------------------------------------------
# 5. First launch — install plugins headlessly
# ---------------------------------------------------------------------------
echo ":: Installing plugins (headless)..."
nvim --headless "+Lazy! sync" +qa 2>/dev/null || true
echo ""
echo "Done. Launch nvim — Mason will auto-install LSP servers on first open."
echo "Run :checkhealth to verify everything is working."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment