Skip to content

Instantly share code, notes, and snippets.

@grantvanhorn
Last active July 28, 2026 15:32
Show Gist options
  • Select an option

  • Save grantvanhorn/e2d0457a31ca52f4ea1140c765fe1f52 to your computer and use it in GitHub Desktop.

Select an option

Save grantvanhorn/e2d0457a31ca52f4ea1140c765fe1f52 to your computer and use it in GitHub Desktop.
Neovim setup — LazyVim plugin overrides (conform, eslint, typescript-tools, cmp, onedarkpro, etc.)

Neovim setup — macOS (LazyVim plugin overrides)

These are my personal Neovim customizations. They are plugin spec overrides that drop into an existing LazyVim config — they are not a standalone Neovim distribution. Install LazyVim first, then copy these on top. (No LLM/AI plugins — this is the macOS "no LLM setup" version.)

Requirements

Install with Homebrew:

brew install neovim node stylua git        # editor + JS/TS runtime + lua formatter
brew install --cask font-roboto-mono-nerd-font   # or any Nerd Font
npm install -g eslint_d prettier           # formatters used by conform.lua
xcode-select --install                     # C compiler + make (LuaSnip/treesitter)
  • Neovim 0.10+ and a working LazyVim base config (these files extend it). Fresh install: git clone https://github.com/LazyVim/starter ~/.config/nvim && rm -rf ~/.config/nvim/.git
  • A Nerd Font in your terminal — required for the completion-menu icons (nvim-cmp + lspkind) and Neo-tree/LazyVim UI glyphs.
  • Node.js + npm — the JS/TS toolchain Mason installs (typescript-language-server, eslint-lsp) plus the eslint_d/prettier formatters.
  • A C compiler + make (via xcode-select --install) — LuaSnip builds jsregexp and treesitter parsers compile on install.
  • Formatters on PATH (used by conform.lua): eslint_d/eslint, prettier (npm), and stylua (brew).

Mason auto-installs the LSP servers on first launch; run :Mason to check, and :Lazy sync if plugins don't install automatically.

Install (no stow — normal config paths)

Each file has a header comment telling you exactly where it goes. In short:

File Destination
conform.lua ~/.config/nvim/lua/plugins/conform.lua
editor-options.lua ~/.config/nvim/lua/plugins/editor-options.lua
eslint-lsp.lua ~/.config/nvim/lua/plugins/eslint-lsp.lua
neo-tree-show-hidden.lua ~/.config/nvim/lua/plugins/neo-tree-show-hidden.lua
nvim-cmp.lua ~/.config/nvim/lua/plugins/nvim-cmp.lua
onedarkpro.lua ~/.config/nvim/lua/plugins/onedarkpro.lua
typescript-tools.lua ~/.config/nvim/lua/plugins/typescript-tools.lua
search-highlights.lua ~/.config/nvim/plugin/after/search-highlights.lua
mkdir -p ~/.config/nvim/lua/plugins ~/.config/nvim/plugin/after
# then drop each file at the path shown in its header / the table above

Anything under lua/plugins/ is auto-loaded by LazyVim. Restart Neovim; LazyVim will sync the new plugins (:Lazy sync if it doesn't happen automatically).

What's in here

  • conform.lua — format-on-save via eslint_d/eslint for JS/TS, prettier for json/yaml/markdown, stylua for lua.
  • eslint-lsp.lua — ESLint LSP for diagnostics + fix-on-save; Mason ensures the TS and ESLint language servers are installed.
  • typescript-tools.lua — TypeScript/JS LSP: auto-imports, inlay hints, go-to-definition, references, rename, code actions.
  • nvim-cmp.lua — autocomplete UI (LSP, snippets, buffer, path, cmdline) with LuaSnip and lspkind icons.
  • onedarkpro.luaonedark_dark colorscheme with a custom cursorline.
  • search-highlights.lua — custom Search/IncSearch/Flash highlight colors, reapplied on ColorScheme.
  • neo-tree-show-hidden.lua — show dotfiles and gitignored files in Neo-tree.
  • editor-options.lua — relative line numbers + scrolloff=16.
-- Install to: ~/.config/nvim/lua/plugins/conform.lua
-- (No stow — copy this file into your existing LazyVim config at that path.)
return {
{
"stevearc/conform.nvim",
event = { "BufWritePre" },
cmd = { "ConformInfo" },
keys = {
{
"<leader>f",
function()
require("conform").format({ async = true, lsp_fallback = true })
end,
mode = "",
desc = "Format buffer",
},
},
opts = {
-- Define your formatters
formatters_by_ft = {
-- Use eslint_d if available (much faster), fallback to eslint
javascript = { "eslint_d", "eslint" },
javascriptreact = { "eslint_d", "eslint" },
typescript = { "eslint_d", "eslint" },
typescriptreact = { "eslint_d", "eslint" },
-- Fallback to prettier if eslint_d is not available
-- You can also use both: { "eslint_d", "prettier" }
json = { "prettier" },
yaml = { "prettier" },
markdown = { "prettier" },
lua = { "stylua" },
-- Add more file types as needed
},
-- Note: format_on_save is handled by LazyVim automatically
-- Configure formatters
formatters = {
-- ESLint formatter configuration (using eslint_d for performance)
eslint_d = {
condition = function(ctx)
-- Only run if eslint is available
return vim.fs.find({ "eslint.config.js", "eslint.config.mjs", ".eslintrc.js", ".eslintrc.json", ".eslintrc.yml", ".eslintrc.yaml" }, { path = ctx.filename, upward = true })[1]
end,
},
prettier = {
prepend_args = { "--print-width", "100" },
},
},
},
},
}
-- Install to: ~/.config/nvim/lua/plugins/editor-options.lua
-- (No stow — copy this file into your existing LazyVim config at that path.)
-- Line-number and scroll behavior on top of Omarchy's LazyVim defaults.
--
-- Omarchy's lua/config/options.lua sets `relativenumber = false`. This file is
-- imported by lazy.nvim *after* that runs, so setting the options here at
-- import time overrides the base config without editing any Omarchy-owned file.
vim.opt.number = true -- absolute number on the cursor line
vim.opt.relativenumber = true -- relative numbers on every other line (fast j/k jumps)
vim.opt.scrolloff = 16 -- keep 16 lines of context above/below the cursor
return {}
-- Install to: ~/.config/nvim/lua/plugins/eslint-lsp.lua
-- (No stow — copy this file into your existing LazyVim config at that path.)
-- ESLint LSP configuration
-- TypeScript support is handled by typescript-tools.nvim (see typescript-tools.lua)
-- Fix all function using native LSP API (no dependencies on lspconfig.util)
local function fix_all(opts)
opts = opts or {}
local bufnr = opts.bufnr or vim.api.nvim_get_current_buf()
vim.validate({ bufnr = { bufnr, "number" } })
local client = opts.client or vim.lsp.get_clients({ bufnr = bufnr, name = "eslint" })[1]
if not client then
return
end
local request
if opts.sync then
request = function(buf, method, params)
return client:request_sync(method, params, nil, buf)
end
else
request = function(buf, method, params)
client:request(method, params, nil, buf)
end
end
request(bufnr, "workspace/executeCommand", {
command = "eslint.applyAllFixes",
arguments = {
{
uri = vim.uri_from_bufnr(bufnr),
version = vim.lsp.util.buf_versions[bufnr],
},
},
})
end
return {
{
"neovim/nvim-lspconfig",
dependencies = {
"mason-org/mason.nvim",
"mason-org/mason-lspconfig.nvim",
},
opts = {
servers = {
eslint = {
settings = {
-- Helps eslint find the eslintrc when not in cwd
workingDirectories = { mode = "auto" },
-- Enable code actions
codeAction = {
disableRuleComment = {
enable = true,
location = "separateLine",
},
showDocumentation = {
enable = true,
},
},
},
on_init = function(client)
-- Create EslintFixAll command using the fix_all function
vim.api.nvim_create_user_command("EslintFixAll", function()
fix_all({ client = client, sync = true })
end, {})
end,
on_attach = function(client, bufnr)
-- Disable formatting via LSP - conform.nvim handles it faster with eslint_d
-- ESLint LSP is used for diagnostics (error highlighting) and auto-fixing
if client.name == "eslint" then
client.server_capabilities.documentFormattingProvider = false
-- Set up autocommand to run ESLint Fix All on save
-- This runs before conform.nvim's formatting
local eslint_fix_group = vim.api.nvim_create_augroup("eslint_fix", { clear = true })
vim.api.nvim_create_autocmd("BufWritePre", {
group = eslint_fix_group,
buffer = bufnr,
callback = function()
fix_all({ client = client, bufnr = bufnr, sync = true })
end,
})
end
end,
},
},
},
},
-- Mason for managing LSP servers
{
"mason-org/mason.nvim",
cmd = "Mason",
opts = {
ensure_installed = {
"typescript-language-server", -- TypeScript LSP (used by typescript-tools.nvim)
"eslint-lsp", -- ESLint LSP
},
},
},
{
"mason-org/mason-lspconfig.nvim",
opts = {
automatic_installation = true, -- Auto-install LSP servers
},
},
}
-- Install to: ~/.config/nvim/lua/plugins/neo-tree-show-hidden.lua
-- (No stow — copy this file into your existing LazyVim config at that path.)
return {
{
"nvim-neo-tree/neo-tree.nvim",
opts = {
filesystem = {
filtered_items = {
visible = true, -- This is what you want: If true, then all files are visible by default
hide_dotfiles = false, -- This is the key setting: show dotfiles
hide_gitignored = false, -- Also show gitignored files
},
},
},
},
}
-- Install to: ~/.config/nvim/lua/plugins/nvim-cmp.lua
-- (No stow — copy this file into your existing LazyVim config at that path.)
-- nvim-cmp: Autocomplete plugin for Neovim
-- Provides autocomplete UI for LSP, snippets, buffers, etc.
return {
{
"hrsh7th/nvim-cmp",
event = "InsertEnter",
dependencies = {
"hrsh7th/cmp-nvim-lsp", -- LSP source
"hrsh7th/cmp-buffer", -- Buffer source (completions from open files)
"hrsh7th/cmp-path", -- Path source (file paths)
"hrsh7th/cmp-cmdline", -- Command line source
"L3MON4D3/LuaSnip", -- Snippet engine
"saadparwaiz1/cmp_luasnip", -- Snippet source
"onsails/lspkind.nvim", -- Icons for completion menu
},
config = function()
local cmp = require("cmp")
local luasnip = require("luasnip")
local lspkind = require("lspkind")
cmp.setup({
snippet = {
expand = function(args)
luasnip.lsp_expand(args.body)
end,
},
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 }), -- Accept currently selected item
["<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", priority = 1000 }, -- LSP completions (highest priority)
{ name = "luasnip", priority = 750 }, -- Snippets
}, {
{ name = "buffer", priority = 500 }, -- Buffer completions
{ name = "path", priority = 250 }, -- Path completions
}),
formatting = {
format = lspkind.cmp_format({
mode = "symbol_text",
maxwidth = 50,
ellipsis_char = "...",
before = function(entry, vim_item)
return vim_item
end,
}),
},
-- Show completion menu even with single result
completion = {
completeopt = "menu,menuone,noinsert",
},
})
-- Set up cmdline completions
cmp.setup.cmdline({ "/", "?" }, {
mapping = cmp.mapping.preset.cmdline(),
sources = {
{ name = "buffer" },
},
})
cmp.setup.cmdline(":", {
mapping = cmp.mapping.preset.cmdline(),
sources = cmp.config.sources({
{ name = "path" },
}, {
{ name = "cmdline" },
}),
})
end,
},
-- LuaSnip snippet engine
{
"L3MON4D3/LuaSnip",
build = "make install_jsregexp",
config = function()
require("luasnip.loaders.from_vscode").lazy_load()
end,
},
}
-- Install to: ~/.config/nvim/lua/plugins/onedarkpro.lua
-- (No stow — copy this file into your existing LazyVim config at that path.)
return {
{
"olimorris/onedarkpro.nvim",
priority = 1000, -- Load early to ensure colors are available
lazy = false, -- Load immediately, not on demand
init = function()
-- Set background to dark before theme loads
vim.o.background = "dark"
end,
config = function()
require("onedarkpro").setup({
options = {
transparency = false, -- Ensure background is not transparent
terminal_colors = true, -- Use theme colors for terminal
cursorline = true, -- Enable cursorline highlighting
},
})
vim.cmd("colorscheme onedark_dark")
-- Enable cursorline
vim.opt.cursorline = true
-- Override the transparency script that sets bg = "none"
-- Create an autocmd that runs after VimEnter to restore theme background
vim.api.nvim_create_autocmd("VimEnter", {
once = true,
callback = function()
-- Wait a bit for all scripts to load, then restore background
vim.defer_fn(function()
-- Re-apply colorscheme to override transparency settings
vim.cmd("colorscheme onedark_dark")
-- Customize cursorline to be a different color than background
-- Get the theme's background color
local normal_bg = vim.api.nvim_get_hl(0, { name = "Normal" }).bg
if normal_bg then
-- Set cursorline to a slightly lighter/darker shade
-- For onedark_dark, we'll use a slightly lighter gray
-- You can adjust this color to your preference
vim.api.nvim_set_hl(0, "CursorLine", {
bg = "#2c313c", -- Slightly lighter than typical onedark_dark background
ctermbg = 236, -- Dark gray in terminal
})
else
-- Fallback: use a subtle highlight
vim.api.nvim_set_hl(0, "CursorLine", {
bg = "#2c313c",
ctermbg = 236,
})
end
end, 50)
end,
})
end,
},
}
-- Install to: ~/.config/nvim/plugin/after/search-highlights.lua
-- (No stow — note this goes under plugin/after/, NOT lua/plugins/.)
local function set_search_highlights()
vim.api.nvim_set_hl(0, "Search", { bg = "#414858", fg = "#e5c07b" })
vim.api.nvim_set_hl(0, "IncSearch", { bg = "#e5c07b", fg = "#1e2030" })
vim.api.nvim_set_hl(0, "CurSearch", { bg = "#e5c07b", fg = "#1e2030" })
vim.api.nvim_set_hl(0, "FlashMatch", { bg = "#414858", fg = "#e5c07b" })
vim.api.nvim_set_hl(0, "FlashCurrent", { bg = "#e5c07b", fg = "#1e2030" })
end
set_search_highlights()
vim.api.nvim_create_autocmd("ColorScheme", {
pattern = "*",
callback = set_search_highlights,
})
-- Install to: ~/.config/nvim/lua/plugins/typescript-tools.lua
-- (No stow — copy this file into your existing LazyVim config at that path.)
-- TypeScript/JavaScript LSP configuration
-- Provides autocomplete, auto-imports, go-to-definition, and project-wide code intelligence
return {
{
"neovim/nvim-lspconfig",
opts = {
servers = {
tsserver = {
root_dir = function(fname)
return require("lspconfig.util").root_pattern("tsconfig.json", "jsconfig.json", "package.json")(fname)
or require("lspconfig.util").root_pattern(".git")(fname)
end,
settings = {
typescript = {
-- Enable auto-imports (this is the key feature you wanted!)
suggest = {
autoImports = true,
},
-- Improve project-wide code intelligence
inlayHints = {
parameterNames = { enabled = "all" },
variableTypes = { enabled = false },
propertyDeclarationTypes = { enabled = true },
functionLikeReturnTypes = { enabled = true },
},
-- Better file discovery
preferences = {
includePackageJsonAutoImports = "on",
importModuleSpecifierPreference = "relative",
},
},
javascript = {
suggest = {
autoImports = true,
},
inlayHints = {
parameterNames = { enabled = "all" },
variableTypes = { enabled = false },
propertyDeclarationTypes = { enabled = true },
functionLikeReturnTypes = { enabled = true },
},
preferences = {
includePackageJsonAutoImports = "on",
importModuleSpecifierPreference = "relative",
},
},
},
-- Disable formatting (let conform.nvim handle it)
on_attach = function(client, bufnr)
client.server_capabilities.documentFormattingProvider = false
client.server_capabilities.documentRangeFormattingProvider = false
-- Keybindings for LSP features
local opts = { noremap = true, silent = true, buffer = bufnr }
-- Go to definition
vim.keymap.set("n", "gd", vim.lsp.buf.definition, vim.tbl_extend("force", opts, { desc = "Go to definition" }))
-- Go to references
vim.keymap.set("n", "gr", vim.lsp.buf.references, vim.tbl_extend("force", opts, { desc = "Go to references" }))
-- Hover documentation
vim.keymap.set("n", "K", vim.lsp.buf.hover, vim.tbl_extend("force", opts, { desc = "Hover documentation" }))
-- Code actions (including auto-import)
vim.keymap.set("n", "<leader>ca", vim.lsp.buf.code_action, vim.tbl_extend("force", opts, { desc = "Code actions" }))
-- Rename symbol
vim.keymap.set("n", "<leader>rn", vim.lsp.buf.rename, vim.tbl_extend("force", opts, { desc = "Rename symbol" }))
end,
},
},
},
},
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment