sudo apt install vim
Getting started with Vim: The basics | Opensource.com
Give Me 20 Minutes and I’ll Make You a Vim Motions Expert - YouTube
vim <file>
esc normal modei (insert before) a (append after) insert modetab to select the suggestions
r (replace one character) R (write over) replace modehjkl3j jumps down 3 lines at once3b jumps back 3 words at once
w start of next word
W start of next consecutive non-blank worde end of current/next wordb start of previous word
B start of previous consecutive non-blank word0 go jump to the first non-blank character of the line^ jump to the first non-blank character of the line$ go to the end of the linef<char> / F<char> jump onto next/last <char>t<char> / T<char> jump right to next/last <char>; / , repeat the last f command forward/backward% jump to the matching bracketggjump to the first lineG go to the last line:<line> or <line>G go to line numberctrl + i/o jump to next/previous jump location:set ic to ignore case in search:set noic to make search case-sensitive
:/<keyword> / :?<keyword> search keyword below/above* / # search current word forward/backwardn/N next/previous occurrencectrl + u/d page up/down<verb><adv><object>. For example, di" means delete inside "", caw means change around word, yib means yank inside bracket, etc.
x delete one characterd cut selecteddw delete worddd delete current lineD delete till the end of lineciw change inside wordciq change inside quotes (can be "/'/`)ci" change inside ""cib change inside bracket (can be (), [], {})ci( change inside ()ci[ change inside []ci{ change inside {}i to a, the command becomes change around
"": ciw""<escape>hp(): ciw()<escape>hp[]: ciw[]<escape>hp": `di"vhpC change till the end of linecc change whole line+ before the command
y copy selectedyw copy wordyy copy whole linep / P paste selected before/afterddp swaps two linesd c will override system clipboard. To paste the last explicitly copyed/yanked content, use register 0: "0p:s/<old>/<new> replace the first occurrence in this line:s/<old>/<new>/g replace all occurrences in this line:s/<old>/<new>/gc replace all occurrences in this line with confirmation:%s/<old>/<new> replace the first occurrence:%s/<old>/<new>/g replace all occurrences:%s/<old>/<new>/gc replace all occurrences with confirmation:#,#s/<old>/<new>/g replace all occurrences within line range #,#z= Spell checks suggestionsu undoctrl + r redo. repeat the last editing operationqa start recording macro into register aq to end recording@a to relay the macro in register a:q quit. If there is unsaved editing, it will raise a warning:q! quite without saving:w save:wq save and quit:r <filename> read file content and insert after current line!<cmd> can execute shell command. It can be combined with :r like: :r !ls:e <file> open file:bn next buffer:bp previous buffer:split / :sp split windows horizontally:vsplit / :vsp split windows verticallycurl -lo https://github.com/neovim/neovim/releases/latest/download/nvim-linux-x86_64.tar.gz
sudo rm -rf /opt/nvim-linux-x86_64
sudo tar -c /opt -xzf nvim-linux-x86_64.tar.gz
Then add this to ~/.zshrc:
export PATH="$PATH:/opt/nvim-linux-x86_64/bin"
🚀 Getting Started | LazyVim
Zero to IDE with LazyVim - YouTube
# required
mv ~/.config/nvim{,.bak}
# optional but recommended
mv ~/.local/share/nvim{,.bak}
mv ~/.local/state/nvim{,.bak}
mv ~/.cache/nvim{,.bak}
# clone the starter
git clone https://github.com/LazyVim/starter ~/.config/nvim
rm -rf ~/.config/nvim/.git
Enter Neovim:
nvim
LazyVim From Scratch To BEAST MODE - YouTube
Tmux From Scratch To BEAST MODE - YouTube
LazyExtras are pre-made bundles of configuration. You can use :LazyExtras to install LSP (language server protocol) for mainstream languages easily. For example, use :LazyExtras python to enable Python LSP.
To force all plugins to match the versions in lazy-lock.json, use :Lazy restore.
Enable lang.tex in :LazyExtras and create ~/.config/nvim/lua/plugins/vimtex.lua:
return {
{
"lervag/vimtex",
init = function()
-- Display literal LaTeX source.
vim.g.vimtex_syntax_conceal_disable = 1
-- Use Skim for viewing and SyncTeX forward search.
vim.g.vimtex_view_method = "skim"
-- Do not automatically open quickfix.
vim.g.vimtex_quickfix_mode = 0
-- One-off compilation with \ll
vim.g.vimtex_compiler_latexmk = {
continuous = 0,
callback = 1,
options = {
"-verbose",
"-file-line-error",
"-synctex=1",
"-interaction=nonstopmode",
},
}
-- TOC setup
vim.g.vimtex_toc_config = {
split_pos = "vert botright",
layers = { "content", "todo" },
}
-- open .tex files in plain text mode to avoid conceal issues
vim.api.nvim_create_autocmd("FileType", {
pattern = { "tex", "plaintex" },
callback = function()
vim.opt_local.conceallevel = 0
vim.opt_local.concealcursor = ""
end,
})
end,
},
{
"neovim/nvim-lspconfig",
opts = {
servers = {
texlab = {
settings = {
texlab = {
inlayHints = {
labelDefinitions = false,
labelReferences = false,
},
},
},
},
},
},
},
}
Then you can use \ll to compile the file, \lv to view compiled PDF, and \lt to toggle the table of contents.
Also configure Neovim to use the Skim PDF viewer, which supports TeX-PDF synchronization, in ~/.config/nvim/lua/config/options.lua:
-- LaTeX pdf viewer setup
vim.g.vimtex_view_method = "skim"
vim.g.vimtex_compiler_latexmk = {
options = {
"-pdf",
"-interaction=nonstopmode",
"-synctex=1",
},
}
In Skim: Settings 👉 Sync 👉 Check for file changes 👉 Reload automatically.
To stop auto-format for .bib files, add this to ~/.config/nvim/lua/config/autocmds.lua:
vim.api.nvim_create_autocmd({ "FileType" }, {
pattern = { "bib" },
callback = function()
vim.b.autoformat = false
end,
})
Beancount is a command-line double-entry accounting language. It requires cargo to compile Rust code. To install cargo:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
Also add $HOME/.cargo/bin to the path in ~/.zshrc. Then:
source ~/.zshrc
cargo --version
Then create ~/.config/nvim/lua/plugins/beancount.lua:
return {
{
"neovim/nvim-lspconfig",
opts = {
servers = {
beancount = {},
},
},
},
}
Create ~/.config/nvim/after/ftplugin/beancount.lua:
vim.bo.commentstring = "; %s" -- set comment string for beancount
vim.b.autoformat = false -- disable autoformating
To further enable org-mode-style headings, create ~/.config/nvim/lua/outline/providers/beancount.lua:
local M = {
name = "beancount",
}
function M.supports_buffer(bufnr)
return vim.bo[bufnr].filetype == "beancount"
end
local function parse_heading(line)
-- Org-style:
-- * Heading
-- ** Subheading
local stars, title = line:match("^(%*+)%s+(.+)$")
if stars then
return #stars, title
end
-- Beancount comment-style:
-- ;;; Heading
-- ;;;; Subheading
local semis, comment_title = line:match("^(;;;+)%s+(.+)$")
if semis then
return #semis - 2, comment_title
end
end
function M.request_symbols(callback, opts)
local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false)
local root = { children = {} }
local stack = {}
local max_level = 0
for i, line in ipairs(lines) do
local level, title = parse_heading(line)
if level and title then
-- Close sections at this level or deeper.
for j = level, max_level do
local symbol = stack[j]
if symbol then
symbol.range["end"].line = i - 2
stack[j] = nil
end
end
-- Find nearest active parent.
local parent = root.children
for j = level - 1, 1, -1 do
if stack[j] then
parent = stack[j].children
break
end
end
local line_nr = i - 1 -- LSP/outline positions are 0-indexed
local symbol = {
name = title,
kind = "Module",
selectionRange = {
start = { line = line_nr, character = 0 },
["end"] = { line = line_nr, character = #line },
},
range = {
start = { line = line_nr, character = 0 },
["end"] = { line = line_nr, character = #line },
},
children = {},
}
table.insert(parent, symbol)
stack[level] = symbol
max_level = math.max(max_level, level)
end
end
-- Extend remaining sections to EOF.
local last_line = math.max(#lines - 1, 0)
for _, symbol in pairs(stack) do
symbol.range["end"].line = last_line
end
callback(root.children, opts)
end
return M
Edit outline.lua and make sure that "beancount" is before "lsp" in the provider field:
-- ...
-- Your setup opts here (leave empty to use defaults)
providers = {
-- Prefer outline.nvim's native Markdown parser over obsidian-ls.
priority = { "markdown", "beancount", "lsp", "coc", "norg", "man" },
},
-- ...
GitHub theme: create ~/.config/nvim/lua/plugins/github-nvim.lua:
return {
"projekt0n/github-nvim-theme",
name = "github-theme",
lazy = false, -- make sure we load this during startup if it is your main colorscheme
priority = 1000, -- make sure to load this before all the other start plugins
opts = {
transparent = false, -- use transparent background
},
config = function()
require("github-theme").setup({
options = {
transparent = true, -- use transparent background
},
})
vim.cmd("colorscheme github_dark_default")
end,
}
Rose Pine theme: create ~/.config/nvim/lua/plugins/rose-pine.lua:
return {
{
"rose-pine/neovim",
name = "rose-pine",
opts = {
variant = "auto", -- auto, main, moon, or dawn
dark_variant = "main", -- used when background=dark
styles = {
bold = true,
italic = true,
transparency = true, -- set true for transparent bg
},
},
},
-- Tell LazyVim to use this colorscheme
{ "LazyVim/LazyVim", opts = { colorscheme = "rose-pine" } },
}
Create ~/.config/lua/plugins/snack.lua:
return {
"folke/snacks.nvim",
opts = {
picker = {
actions = {
copy_file_name = function(picker, item)
item = item or picker:current()
if not item then
return
end
local path = item.file or item.path or item.text
if not path then
return
end
vim.fn.setreg("+", vim.fn.fnamemodify(path, ":t"))
vim.notify("Copied file name: " .. vim.fn.fnamemodify(path, ":t"))
end,
copy_relative_path = function(picker, item)
item = item or picker:current()
if not item then
return
end
local path = item.file or item.path or item.text
if not path then
return
end
local rel = vim.fn.fnamemodify(path, ":.")
vim.fn.setreg("+", rel)
vim.notify("Copied relative path: " .. rel)
end,
copy_absolute_path = function(picker, item)
item = item or picker:current()
if not item then
return
end
local path = item.file or item.path or item.text
if not path then
return
end
local abs = vim.fn.fnamemodify(path, ":p")
vim.fn.setreg("+", abs)
vim.notify("Copied absolute path: " .. abs)
end,
},
sources = {
explorer = {
win = {
list = {
keys = {
["gy"] = "copy_relative_path",
["gY"] = "copy_absolute_path",
["gf"] = "copy_file_name",
},
},
},
},
},
},
},
}
This augment the file explorer with the following keybindings:
gy to copy relative pathgY to copy absolute pathgf to copy file nameAdd these to ~/.config/nvim/lua/config/keymaps.lua:
local opts = { noremap = true, silent = true }
-- Opt+← / Opt+→ -> jump by word
vim.keymap.set("i", "<M-b>", "<C-Left>", opts)
vim.keymap.set("i", "<M-f>", "<C-Right>", opts)
-- Opt+Delete / Opt+Backspace -> delete word
vim.keymap.set("i", "<M-BS>", "<C-w>", opts) -- delete word before cursor
vim.keymap.set("i", "<M-Delete>", "<C-o>dw", opts) -- delete word after cursor
-- jj -> escape insert mode
vim.keymap.set("i", "jj", "<Esc>", opts)
-- buffer movement
vim.keymap.set("n", "<leader>b<Left>", "<cmd>BufferLineMovePrev<CR>", { desc = "Move buffer left" })
vim.keymap.set("n", "<leader>b<Right>", "<cmd>BufferLineMoveNext<CR>", { desc = "Move buffer right" })
Add these to ~/.config/nvim/lua/config/options.lua:
vim.g.ai_cmp = false
vim.opt.wrap = true
Create ~/.config/nvim/lua/plugins/outline.lua:
return {
"hedyhli/outline.nvim",
config = function()
-- Example mapping to toggle outline
vim.keymap.set("n", "<leader>o", "<cmd>Outline<CR>", { desc = "Toggle Outline" })
require("outline").setup({
-- Your setup opts here (leave empty to use defaults)
providers = {
-- Prefer outline.nvim's native Markdown parser over obsidian-ls.
priority = { "markdown", "lsp", "coc", "norg", "man" },
},
symbols = {
filter = {
-- Default: show everything except String
default = { "String", exclude = true },
-- Python: only show these kinds
python = { "Class", "Function", "Method" },
},
},
})
end,
}
Create ~/.config/nvim/lua/plugins/indent.lua:
return {
"NMAC427/guess-indent.nvim",
opts = {},
}
It will guess your indentation type by the first few hunders of lines of current file. To check its guess, run :GuessIndent.
VS Code-like multi-cursor could be reliazed via mg979/vim-viual-multi. Create ~/.config/nvim/lua/plugins/multi-cursor.lua:
return {
{
"mg979/vim-visual-multi",
branch = "master",
lazy = false, -- must NOT be lazy-loaded or keymaps won't work
init = function()
-- vim.g.* settings must go in `init`, not `config`
vim.g.VM_maps = {
["Find Under"] = "<C-n>", -- default: start multicursor on word
["Find Subword Under"] = "<C-n>",
["Select Cursor Down"] = "<M-Down>",
["Select Cursor Up"] = "<M-Up>",
}
end,
},
}
ctrl + n / N to select next/previous occurrence
q to skip current and get next occurrenceQ to skip current cursoropt + arrows to move up/downctrl + arrows has been used for window resizing. But strangely, this setup makes both opt/ctrl + arrows used for vertical multi-cursorIf you ssh into another machine, y to the clipboard might not work. Add this to ~/.config/nvim/init.lua to enable osc52:
-- disable system clipboard override
vim.opt.clipboard = ""
-- Use OSC 52 for clipboard if we're in an SSH session
if vim.env.SSH_TTY ~= nil then
vim.g.clipboard = {
name = "OSC 52",
copy = {
["+"] = require("vim.ui.clipboard.osc52").copy("+"),
["*"] = require("vim.ui.clipboard.osc52").copy("*"),
},
paste = {
["+"] = function()
return { vim.fn.split(vim.fn.getreg('"'), "\n"), vim.fn.getregtype('"') }
end,
["*"] = function()
return { vim.fn.split(vim.fn.getreg('"'), "\n"), vim.fn.getregtype('"') }
end,
},
}
end
P.S. Tmux -> SSH -> Neovim still doesn't wrok; but SSH -> Tux -> Neovim works.
Install LazyGit:
brew install lazygit
Then you can trigger lazygit in Neovim via leader + gg.
For VS Code-like diff view, install codediff.nvim by creating ~/.config/nvim/lua/plugins/codediff.lua:
return {
"esmuellert/codediff.nvim",
lazy = true,
cmd = "CodeDiff",
keys = {
{ "<leader>gd", "<cmd>CodeDiff<cr>", desc = "Code Diff" },
},
}
Add ~/.config/nvim/lua/plugins/blink-cmp.lua:
return {
"saghen/blink.cmp",
dependencies = {
"fang2hou/blink-copilot",
"L3MON4D3/LuaSnip",
},
opts = {
sources = {
default = { "copilot" },
providers = {
copilot = {
name = "copilot",
module = "blink-copilot",
score_offset = 100,
async = true,
},
},
},
},
}
Create ~/.config/nvim/lua/plugins/luasnip.lua:
return {
"L3MON4D3/LuaSnip",
version = "v2.*",
dependencies = {
{
"rafamadriz/friendly-snippets",
config = function()
require("luasnip.loaders.from_vscode").lazy_load()
end,
},
},
}
Create customized snippet folder:
cd ~/.config/nvim
mkdir snippets
touch package.json
echo "{}" > python.json
echo "{}" > lua.json
echo "{}" > latex.json
echo "{}" > md.json
Modify package.json:
{
"name": "my-snippets",
"engines": {
"vscode": "^1.11.0"
},
"contributes": {
"snippets": [
{
"language": "python",
"path": "./python.json"
},
{
"language": "lua",
"path": "./lua.json"
},
{
"language": "tex",
"path": "./latex.json"
},
{
"language": "markdown",
"path": "./md.json"
}
]
}
}
Copilot | LazyVim
GitHub - zbirenbaum/copilot.lua: Fully featured & enhanced replacement for copilot.vim complete with API for interacting with Github Copilot · GitHub
Copilot and Neovim
blink-copilot
P.S. Make sure the node.js version is >= 22.
Add ~/.config/nvim/lua/plugins/copilot.lua:
return {
"zbirenbaum/copilot.lua",
cmd = "Copilot",
event = "InsertEnter",
opts = {
suggestion = { enabled = false },
panel = { enabled = false },
filetypes = {
markdown = true,
help = true,
},
},
}
Re-enter nvim, then :Copilot auth, open the link for authentication.
P.S. GitHub - github/copilot.vim: Neovim plugin for GitHub Copilot · GitHub seem to be the official nvim plugin
supermaven is dead, what would be a good replace for it? | r/neovim
P.S. Remember to adapt the workspace option!
return {
"obsidian-nvim/obsidian.nvim",
version = "*",
ft = "markdown",
dependencies = {
"saghen/blink.cmp", -- declare blink as dependency
},
opts = {
workspaces = {
{
name = "DeepSpace",
path = "/Users/knpob/Library/Mobile Documents/iCloud~md~obsidian/Documents/DeepSpace",
overrides = {
frontmatter = { enabled = false },
},
},
},
completion = {
nvim_cmp = false,
blink = true,
min_chars = 2,
},
-- rest of your config...
},
}
Also, ripgrep needs to be installed in advance:
brew install ripgrep
P.S. The default leader key is space.
leader + " view all registriess
leader + qs restore last session for the current directoryleader + ql restore last used sessionleader + qS select session to restoreleader + qd don't save current sessionleader + qq quit allleader + e file explorer
h collapse current folderH show hidden folders/filesalt + i show git ignored folders/filesr rename filea add file/folder (to add a folder, end with /)d delete file/folderc duplicate file/foldery copy file/folderx cut file/folderp paste file/foldergy copy relative pathgY copy absolute pathgf copy file nameopt + h to show hidden files
leader + ff find fileleader + fr find recent fileleader + leader fuzzy search fileleader + fc configuration filesleader + sg grep on root directoryleader + sG grep on current working directoryleader + sr search and replcaedd to delete the matches you don't want to replace and use \s to sync the replacement for all matches; or \l to sync the replacement for current line. Use \r with caution -- it runs rg --replace on all matches regardless of the deletion you madeleader + , navigate between buffersshift + h/<- move to left buffershift + l/-> move to right bufferleader + bd delete current bufferleader + b <- move current buffer leftleader + b -> move current buffer rightctrl + h/<- move to left windowctrl + r/-> move to right windowleader + bd close current bufferleader + wq close current window:Mason/<keyword> to search for LSP, etc.i to installenter to exapndgc comment outgcc comment out current lineK hover for documentationgO symbol of current file<leader> + o outline of current file
Tab expand/collapse current itemE expand all itemsW collapse all itemsgd go to definitiongf go to filegI go to implementationgr go to references[d go to previous diagnostic]d go to next diagnostic<leader> cr rename symboltab accept suggestionctrl + right accept one wordcmd+right. I change it as ctrl+right to make my muscle memory consistentctrl + down accept one lineopt + ] cycle to next suggestionopt + [ cycle to previous suggestionctrl + ] dismiss suggestionleader + gs git statusleader + gd codediff
t change between side-by-side and inline modes[/] + c jump to previous/next changeleader + e jump to explorer panel- stage/unstage/discard change
leader + hs stage hunk under cursorleader + hu unstage hunk under cursorleader + hr discard hunk under cursor- stage/unstage current fileX discard current fileq quit<leader> + uw to toggle on/off word warping as a workaroundleader + gg lazygit
tab / <number> jump between panels<- / -> jump between hunks` to toggle between folder view or flat view in folder panel
space stage/unstage filed discard file changesa stage all changesc commitp pullP pushs stashleader + ft terminalgf go to file[[ / ]] jump to previous/next heading\ll for compilation\lt to toggle table of contentsenter jumps to the selection and closes the ToCspace jumps to the selection and keeps the ToC open\lv to view the compiled PDF and jump to the current line in the PDF
[!tip]
This will opened the PDF in Skim.
- If you are using macOS and have stage manager enabled, drag the Skim window to the same stage of Neovim's and press
fn+ctrl+shift+<-/->to arrange the two windows side by side. Alternatively, you can holdoptand drag the windows to tile to the left/right side of the screen.- Useful Skim shortcuts:
cmd+shift+-fit page size,cmd+shift+tsidebar
\le stop compilationObsidian:
For a comprehensive guide on using Neovim with VS Code, including LazyVim-inspired keybindings, tmux-style terminal management, and integrated git workflow, see [[vscode-neovim-setup]].