aboutsummaryrefslogtreecommitdiff
path: root/.config/nvim/lua
diff options
context:
space:
mode:
Diffstat (limited to '.config/nvim/lua')
-rw-r--r--.config/nvim/lua/colorschemes.lua83
-rw-r--r--.config/nvim/lua/config/indent-blankline.lua36
-rw-r--r--.config/nvim/lua/config/nvim-tree.lua108
-rw-r--r--.config/nvim/lua/config/statusline.lua197
-rw-r--r--.config/nvim/lua/custom-autocmd.lua105
-rw-r--r--.config/nvim/lua/mappings.lua231
-rw-r--r--.config/nvim/lua/plugin_specs.lua503
7 files changed, 1263 insertions, 0 deletions
diff --git a/.config/nvim/lua/colorschemes.lua b/.config/nvim/lua/colorschemes.lua
new file mode 100644
index 0000000..9618b4d
--- /dev/null
+++ b/.config/nvim/lua/colorschemes.lua
@@ -0,0 +1,83 @@
+--- This module will load a random colorscheme on nvim startup process.
+
+local utils = require("utils")
+
+local M = {}
+
+-- Colorscheme to its directory name mapping, because colorscheme repo name is not necessarily
+-- the same as the colorscheme name itself.
+M.colorscheme_conf = {
+ onedark = function()
+ vim.cmd([[colorscheme onedark]])
+ end,
+ edge = function()
+ vim.g.edge_enable_italic = 1
+ vim.g.edge_better_performance = 1
+
+ vim.cmd([[colorscheme edge]])
+ end,
+ sonokai = function()
+ vim.g.sonokai_enable_italic = 1
+ vim.g.sonokai_better_performance = 1
+
+ vim.cmd([[colorscheme sonokai]])
+ end,
+ gruvbox_material = function()
+ -- foreground option can be material, mix, or original
+ vim.g.gruvbox_material_foreground = "material"
+ --background option can be hard, medium, soft
+ vim.g.gruvbox_material_background = "soft"
+ vim.g.gruvbox_material_enable_italic = 1
+ vim.g.gruvbox_material_better_performance = 1
+
+ vim.cmd([[colorscheme gruvbox-material]])
+ end,
+ everforest = function()
+ vim.g.everforest_enable_italic = 1
+ vim.g.everforest_better_performance = 1
+
+ vim.cmd([[colorscheme everforest]])
+ end,
+ nightfox = function()
+ vim.cmd([[colorscheme nordfox]])
+ end,
+ catppuccin = function()
+ -- available option: latte, frappe, macchiato, mocha
+ vim.g.catppuccin_flavour = "frappe"
+ require("catppuccin").setup()
+
+ vim.cmd([[colorscheme catppuccin]])
+ end,
+ onedarkpro = function()
+ -- set colorscheme after options
+ vim.cmd('colorscheme onedark_vivid')
+ end,
+ material = function()
+ vim.g.material_style = "oceanic"
+ vim.cmd('colorscheme material')
+ end,
+}
+
+--- Use a random colorscheme from the pre-defined list of colorschemes.
+M.rand_colorscheme = function()
+ local colorscheme = utils.rand_element(vim.tbl_keys(M.colorscheme_conf))
+
+ if not vim.tbl_contains(vim.tbl_keys(M.colorscheme_conf), colorscheme) then
+ local msg = "Invalid colorscheme: " .. colorscheme
+ vim.notify(msg, vim.log.levels.ERROR, { title = "nvim-config" })
+
+ return
+ end
+
+ -- Load the colorscheme and its settings
+ M.colorscheme_conf[colorscheme]()
+
+ if vim.g.logging_level == "debug" then
+ local msg = "Colorscheme: " .. colorscheme
+
+ vim.notify(msg, vim.log.levels.DEBUG, { title = "nvim-config" })
+ end
+end
+
+-- Load a random colorscheme
+M.rand_colorscheme()
diff --git a/.config/nvim/lua/config/indent-blankline.lua b/.config/nvim/lua/config/indent-blankline.lua
new file mode 100644
index 0000000..9b210d7
--- /dev/null
+++ b/.config/nvim/lua/config/indent-blankline.lua
@@ -0,0 +1,36 @@
+local api = vim.api
+
+local exclude_ft = { "help", "git", "markdown", "snippets", "text", "gitconfig", "alpha", "dashboard" }
+
+require("ibl").setup {
+ indent = {
+ -- -- U+2502 may also be a good choice, it will be on the middle of cursor.
+ -- -- U+250A is also a good choice
+ char = "▏",
+ },
+ scope = {
+ show_start = false,
+ show_end = false,
+ },
+ exclude = {
+ filetypes = exclude_ft,
+ buftypes = { "terminal" },
+ },
+}
+
+local gid = api.nvim_create_augroup("indent_blankline", { clear = true })
+api.nvim_create_autocmd("InsertEnter", {
+ pattern = "*",
+ group = gid,
+ command = "IBLDisable",
+})
+
+api.nvim_create_autocmd("InsertLeave", {
+ pattern = "*",
+ group = gid,
+ callback = function()
+ if not vim.tbl_contains(exclude_ft, vim.bo.filetype) then
+ vim.cmd([[IBLEnable]])
+ end
+ end,
+})
diff --git a/.config/nvim/lua/config/nvim-tree.lua b/.config/nvim/lua/config/nvim-tree.lua
new file mode 100644
index 0000000..6bf653d
--- /dev/null
+++ b/.config/nvim/lua/config/nvim-tree.lua
@@ -0,0 +1,108 @@
+local keymap = vim.keymap
+local nvim_tree = require("nvim-tree")
+
+nvim_tree.setup {
+ auto_reload_on_write = true,
+ disable_netrw = false,
+ hijack_netrw = true,
+ hijack_cursor = false,
+ hijack_unnamed_buffer_when_opening = false,
+ open_on_tab = false,
+ sort_by = "name",
+ update_cwd = false,
+ view = {
+ width = 30,
+ side = "left",
+ preserve_window_proportions = false,
+ number = false,
+ relativenumber = false,
+ signcolumn = "yes",
+ },
+ renderer = {
+ indent_markers = {
+ enable = false,
+ icons = {
+ corner = "└ ",
+ edge = "│ ",
+ none = " ",
+ },
+ },
+ icons = {
+ webdev_colors = true,
+ },
+ },
+ hijack_directories = {
+ enable = true,
+ auto_open = true,
+ },
+ update_focused_file = {
+ enable = false,
+ update_cwd = false,
+ ignore_list = {},
+ },
+ system_open = {
+ cmd = "",
+ args = {},
+ },
+ diagnostics = {
+ enable = false,
+ show_on_dirs = false,
+ icons = {
+ hint = "",
+ info = "",
+ warning = "",
+ error = "",
+ },
+ },
+ filters = {
+ dotfiles = false,
+ custom = {},
+ exclude = {},
+ },
+ git = {
+ enable = true,
+ ignore = true,
+ timeout = 400,
+ },
+ actions = {
+ use_system_clipboard = true,
+ change_dir = {
+ enable = true,
+ global = false,
+ restrict_above_cwd = false,
+ },
+ open_file = {
+ quit_on_open = false,
+ resize_window = false,
+ window_picker = {
+ enable = true,
+ chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890",
+ exclude = {
+ filetype = { "notify", "qf", "diff", "fugitive", "fugitiveblame" },
+ buftype = { "nofile", "terminal", "help" },
+ },
+ },
+ },
+ },
+ trash = {
+ cmd = "trash",
+ require_confirm = true,
+ },
+ log = {
+ enable = false,
+ truncate = false,
+ types = {
+ all = false,
+ config = false,
+ copy_paste = false,
+ diagnostics = false,
+ git = false,
+ profile = false,
+ },
+ },
+}
+
+keymap.set("n", "<space>s", require("nvim-tree.api").tree.toggle, {
+ silent = true,
+ desc = "toggle nvim-tree",
+})
diff --git a/.config/nvim/lua/config/statusline.lua b/.config/nvim/lua/config/statusline.lua
new file mode 100644
index 0000000..0563ba6
--- /dev/null
+++ b/.config/nvim/lua/config/statusline.lua
@@ -0,0 +1,197 @@
+local fn = vim.fn
+
+local function spell()
+ if vim.o.spell then
+ return string.format("[SPELL]")
+ end
+
+ return ""
+end
+
+--- show indicator for Chinese IME
+local function ime_state()
+ if vim.g.is_mac then
+ -- ref: https://github.com/vim-airline/vim-airline/blob/master/autoload/airline/extensions/xkblayout.vim#L11
+ local layout = fn.libcall(vim.g.XkbSwitchLib, "Xkb_Switch_getXkbLayout", "")
+
+ -- We can use `xkbswitch -g` on the command line to get current mode.
+ -- mode for macOS builtin pinyin IME: com.apple.inputmethod.SCIM.ITABC
+ -- mode for Rime: im.rime.inputmethod.Squirrel.Rime
+ local res = fn.match(layout, [[\v(Squirrel\.Rime|SCIM.ITABC)]])
+ if res ~= -1 then
+ return "[CN]"
+ end
+ end
+
+ return ""
+end
+
+local function trailing_space()
+ if not vim.o.modifiable then
+ return ""
+ end
+
+ local line_num = nil
+
+ for i = 1, fn.line("$") do
+ local linetext = fn.getline(i)
+ -- To prevent invalid escape error, we wrap the regex string with `[[]]`.
+ local idx = fn.match(linetext, [[\v\s+$]])
+
+ if idx ~= -1 then
+ line_num = i
+ break
+ end
+ end
+
+ local msg = ""
+ if line_num ~= nil then
+ msg = string.format("[%d]trailing", line_num)
+ end
+
+ return msg
+end
+
+local function mixed_indent()
+ if not vim.o.modifiable then
+ return ""
+ end
+
+ local space_pat = [[\v^ +]]
+ local tab_pat = [[\v^\t+]]
+ local space_indent = fn.search(space_pat, "nwc")
+ local tab_indent = fn.search(tab_pat, "nwc")
+ local mixed = (space_indent > 0 and tab_indent > 0)
+ local mixed_same_line
+ if not mixed then
+ mixed_same_line = fn.search([[\v^(\t+ | +\t)]], "nwc")
+ mixed = mixed_same_line > 0
+ end
+ if not mixed then
+ return ""
+ end
+ if mixed_same_line ~= nil and mixed_same_line > 0 then
+ return "MI:" .. mixed_same_line
+ end
+ local space_indent_cnt = fn.searchcount({ pattern = space_pat, max_count = 1e3 }).total
+ local tab_indent_cnt = fn.searchcount({ pattern = tab_pat, max_count = 1e3 }).total
+ if space_indent_cnt > tab_indent_cnt then
+ return "MI:" .. tab_indent
+ else
+ return "MI:" .. space_indent
+ end
+end
+
+local diff = function()
+ local git_status = vim.b.gitsigns_status_dict
+ if git_status == nil then
+ return
+ end
+
+ local modify_num = git_status.changed
+ local remove_num = git_status.removed
+ local add_num = git_status.added
+
+ local info = { added = add_num, modified = modify_num, removed = remove_num }
+ -- vim.print(info)
+ return info
+end
+
+local virtual_env = function()
+ -- only show virtual env for Python
+ if vim.bo.filetype ~= 'python' then
+ return ""
+ end
+
+ local conda_env = os.getenv('CONDA_DEFAULT_ENV')
+ local venv_path = os.getenv('VIRTUAL_ENV')
+
+ if venv_path == nil then
+ if conda_env == nil then
+ return ""
+ else
+ return string.format(" %s (conda)", conda_env)
+ end
+ else
+ local venv_name = vim.fn.fnamemodify(venv_path, ':t')
+ return string.format(" %s (venv)", venv_name)
+ end
+end
+
+require("lualine").setup {
+ options = {
+ icons_enabled = true,
+ theme = "auto",
+ -- component_separators = { left = "", right = "" },
+ -- section_separators = { left = "", right = "" },
+ section_separators = "",
+ component_separators = "",
+ disabled_filetypes = {},
+ always_divide_middle = true,
+ },
+ sections = {
+ lualine_a = { "mode" },
+ lualine_b = {
+ "branch",
+ {
+ "diff",
+ source = diff,
+ },
+ {
+ virtual_env,
+ color = { fg = 'black', bg = "#F1CA81" }
+ }
+ },
+ lualine_c = {
+ "filename",
+ {
+ ime_state,
+ color = { fg = "black", bg = "#f46868" },
+ },
+ {
+ spell,
+ color = { fg = "black", bg = "#a7c080" },
+ },
+ {
+ "diagnostics",
+ sources = { "nvim_diagnostic" },
+ symbols = {error = '🆇 ', warn = '⚠️ ', info = 'ℹ️ ', hint = ' '},
+ },
+ },
+ lualine_x = {
+ "encoding",
+ {
+ "fileformat",
+ symbols = {
+ unix = "unix",
+ dos = "win",
+ mac = "mac",
+ },
+ },
+ "filetype",
+ },
+ lualine_y = {
+ "location",
+ },
+ lualine_z = {
+ {
+ trailing_space,
+ color = "WarningMsg",
+ },
+ {
+ mixed_indent,
+ color = "WarningMsg",
+ },
+ },
+ },
+ inactive_sections = {
+ lualine_a = {},
+ lualine_b = {},
+ lualine_c = { "filename" },
+ lualine_x = { "location" },
+ lualine_y = {},
+ lualine_z = {},
+ },
+ tabline = {},
+ extensions = { "quickfix", "fugitive", "nvim-tree" },
+}
diff --git a/.config/nvim/lua/custom-autocmd.lua b/.config/nvim/lua/custom-autocmd.lua
new file mode 100644
index 0000000..21fa15c
--- /dev/null
+++ b/.config/nvim/lua/custom-autocmd.lua
@@ -0,0 +1,105 @@
+local fn = vim.fn
+local api = vim.api
+
+local utils = require("utils")
+
+-- Display a message when the current file is not in utf-8 format.
+-- Note that we need to use `unsilent` command here because of this issue:
+-- https://github.com/vim/vim/issues/4379
+api.nvim_create_autocmd({ "BufRead" }, {
+ pattern = "*",
+ group = api.nvim_create_augroup("non_utf8_file", { clear = true }),
+ callback = function()
+ if vim.bo.fileencoding ~= "utf-8" then
+ vim.notify("File not in UTF-8 format!", vim.log.levels.WARN, { title = "nvim-config" })
+ end
+ end,
+})
+
+-- highlight yanked region, see `:h lua-highlight`
+local yank_group = api.nvim_create_augroup("highlight_yank", { clear = true })
+api.nvim_create_autocmd({ "TextYankPost" }, {
+ pattern = "*",
+ group = yank_group,
+ callback = function()
+ vim.highlight.on_yank { higroup = "YankColor", timeout = 300 }
+ end,
+})
+
+api.nvim_create_autocmd({ "CursorMoved" }, {
+ pattern = "*",
+ group = yank_group,
+ callback = function()
+ vim.g.current_cursor_pos = vim.fn.getcurpos()
+ end,
+})
+
+api.nvim_create_autocmd("TextYankPost", {
+ pattern = "*",
+ group = yank_group,
+ callback = function(ev)
+ if vim.v.event.operator == 'y' then
+ vim.fn.setpos('.', vim.g.current_cursor_pos)
+ end
+ end,
+})
+
+-- Auto-create dir when saving a file, in case some intermediate directory does not exist
+api.nvim_create_autocmd({ "BufWritePre" }, {
+ pattern = "*",
+ group = api.nvim_create_augroup("auto_create_dir", { clear = true }),
+ callback = function(ctx)
+ local dir = fn.fnamemodify(ctx.file, ":p:h")
+ utils.may_create_dir(dir)
+ end,
+})
+
+-- Automatically reload the file if it is changed outside of Nvim, see https://unix.stackexchange.com/a/383044/221410.
+-- It seems that `checktime` does not work in command line. We need to check if we are in command
+-- line before executing this command, see also https://vi.stackexchange.com/a/20397/15292 .
+api.nvim_create_augroup("auto_read", { clear = true })
+
+api.nvim_create_autocmd({ "FileChangedShellPost" }, {
+ pattern = "*",
+ group = "auto_read",
+ callback = function()
+ vim.notify("File changed on disk. Buffer reloaded!", vim.log.levels.WARN, { title = "nvim-config" })
+ end,
+})
+
+api.nvim_create_autocmd({ "FocusGained", "CursorHold" }, {
+ pattern = "*",
+ group = "auto_read",
+ callback = function()
+ if fn.getcmdwintype() == "" then
+ vim.cmd("checktime")
+ end
+ end,
+})
+
+-- Resize all windows when we resize the terminal
+api.nvim_create_autocmd("VimResized", {
+ group = api.nvim_create_augroup("win_autoresize", { clear = true }),
+ desc = "autoresize windows on resizing operation",
+ command = "wincmd =",
+})
+
+local function open_nvim_tree(data)
+ -- check if buffer is a directory
+ local directory = vim.fn.isdirectory(data.file) == 1
+
+ if not directory then
+ return
+ end
+
+ -- create a new, empty buffer
+ vim.cmd.enew()
+
+ -- wipe the directory buffer
+ vim.cmd.bw(data.buf)
+
+ -- open the tree
+ require("nvim-tree.api").tree.open()
+end
+
+vim.api.nvim_create_autocmd({ "VimEnter" }, { callback = open_nvim_tree })
diff --git a/.config/nvim/lua/mappings.lua b/.config/nvim/lua/mappings.lua
new file mode 100644
index 0000000..2837650
--- /dev/null
+++ b/.config/nvim/lua/mappings.lua
@@ -0,0 +1,231 @@
+local keymap = vim.keymap
+local api = vim.api
+local uv = vim.loop
+
+-- Save key strokes (now we do not need to press shift to enter command mode).
+keymap.set({ "n", "x" }, ";", ":")
+
+-- Turn the word under cursor to upper case
+keymap.set("i", "<c-u>", "<Esc>viwUea")
+
+-- Turn the current word into title case
+keymap.set("i", "<c-t>", "<Esc>b~lea")
+
+-- Paste non-linewise text above or below current line, see https://stackoverflow.com/a/1346777/6064933
+keymap.set("n", "<leader>p", "m`o<ESC>p``", { desc = "paste below current line" })
+keymap.set("n", "<leader>P", "m`O<ESC>p``", { desc = "paste above current line" })
+
+-- Shortcut for faster save and quit
+keymap.set("n", "<leader>w", "<cmd>update<cr>", { silent = true, desc = "save buffer" })
+
+-- Saves the file if modified and quit
+keymap.set("n", "<leader>q", "<cmd>x<cr>", { silent = true, desc = "quit current window" })
+
+-- Quit all opened buffers
+keymap.set("n", "<leader>Q", "<cmd>qa!<cr>", { silent = true, desc = "quit nvim" })
+
+-- Navigation in the location and quickfix list
+keymap.set("n", "[l", "<cmd>lprevious<cr>zv", { silent = true, desc = "previous location item" })
+keymap.set("n", "]l", "<cmd>lnext<cr>zv", { silent = true, desc = "next location item" })
+
+keymap.set("n", "[L", "<cmd>lfirst<cr>zv", { silent = true, desc = "first location item" })
+keymap.set("n", "]L", "<cmd>llast<cr>zv", { silent = true, desc = "last location item" })
+
+keymap.set("n", "[q", "<cmd>cprevious<cr>zv", { silent = true, desc = "previous qf item" })
+keymap.set("n", "]q", "<cmd>cnext<cr>zv", { silent = true, desc = "next qf item" })
+
+keymap.set("n", "[Q", "<cmd>cfirst<cr>zv", { silent = true, desc = "first qf item" })
+keymap.set("n", "]Q", "<cmd>clast<cr>zv", { silent = true, desc = "last qf item" })
+
+-- Close location list or quickfix list if they are present, see https://superuser.com/q/355325/736190
+keymap.set("n", [[\x]], "<cmd>windo lclose <bar> cclose <cr>", {
+ silent = true,
+ desc = "close qf and location list",
+})
+
+-- Delete a buffer, without closing the window, see https://stackoverflow.com/q/4465095/6064933
+keymap.set("n", [[\d]], "<cmd>bprevious <bar> bdelete #<cr>", {
+ silent = true,
+ desc = "delete buffer",
+})
+
+-- Insert a blank line below or above current line (do not move the cursor),
+-- see https://stackoverflow.com/a/16136133/6064933
+keymap.set("n", "<space>o", "printf('m`%so<ESC>``', v:count1)", {
+ expr = true,
+ desc = "insert line below",
+})
+
+keymap.set("n", "<space>O", "printf('m`%sO<ESC>``', v:count1)", {
+ expr = true,
+ desc = "insert line above",
+})
+
+-- Move the cursor based on physical lines, not the actual lines.
+keymap.set("n", "j", "v:count == 0 ? 'gj' : 'j'", { expr = true })
+keymap.set("n", "k", "v:count == 0 ? 'gk' : 'k'", { expr = true })
+keymap.set("n", "^", "g^")
+keymap.set("n", "0", "g0")
+
+-- Do not include white space characters when using $ in visual mode,
+-- see https://vi.stackexchange.com/q/12607/15292
+keymap.set("x", "$", "g_")
+
+-- Go to start or end of line easier
+keymap.set({ "n", "x" }, "H", "^")
+keymap.set({ "n", "x" }, "L", "g_")
+
+-- Continuous visual shifting (does not exit Visual mode), `gv` means
+-- to reselect previous visual area, see https://superuser.com/q/310417/736190
+keymap.set("x", "<", "<gv")
+keymap.set("x", ">", ">gv")
+
+-- Edit and reload nvim config file quickly
+keymap.set("n", "<leader>ev", "<cmd>tabnew $MYVIMRC <bar> tcd %:h<cr>", {
+ silent = true,
+ desc = "open init.lua",
+})
+
+keymap.set("n", "<leader>sv", function()
+ vim.cmd([[
+ update $MYVIMRC
+ source $MYVIMRC
+ ]])
+ vim.notify("Nvim config successfully reloaded!", vim.log.levels.INFO, { title = "nvim-config" })
+end, {
+ silent = true,
+ desc = "reload init.lua",
+})
+
+-- Reselect the text that has just been pasted, see also https://stackoverflow.com/a/4317090/6064933.
+keymap.set("n", "<leader>v", "printf('`[%s`]', getregtype()[0])", {
+ expr = true,
+ desc = "reselect last pasted area",
+})
+
+-- Always use very magic mode for searching
+keymap.set("n", "/", [[/\v]])
+
+-- Search in selected region
+-- xnoremap / :<C-U>call feedkeys('/\%>'.(line("'<")-1).'l\%<'.(line("'>")+1)."l")<CR>
+
+-- Change current working directory locally and print cwd after that,
+-- see https://vim.fandom.com/wiki/Set_working_directory_to_the_current_file
+keymap.set("n", "<leader>cd", "<cmd>lcd %:p:h<cr><cmd>pwd<cr>", { desc = "change cwd" })
+
+-- Use Esc to quit builtin terminal
+keymap.set("t", "<Esc>", [[<c-\><c-n>]])
+
+-- Toggle spell checking
+keymap.set("n", "<F11>", "<cmd>set spell!<cr>", { desc = "toggle spell" })
+keymap.set("i", "<F11>", "<c-o><cmd>set spell!<cr>", { desc = "toggle spell" })
+
+-- Change text without putting it into the vim register,
+-- see https://stackoverflow.com/q/54255/6064933
+keymap.set("n", "c", '"_c')
+keymap.set("n", "C", '"_C')
+keymap.set("n", "cc", '"_cc')
+keymap.set("x", "c", '"_c')
+
+-- Remove trailing whitespace characters
+keymap.set("n", "<leader><space>", "<cmd>StripTrailingWhitespace<cr>", { desc = "remove trailing space" })
+
+-- check the syntax group of current cursor position
+keymap.set("n", "<leader>st", "<cmd>call utils#SynGroup()<cr>", { desc = "check syntax group" })
+
+-- Copy entire buffer.
+keymap.set("n", "<leader>y", "<cmd>%yank<cr>", { desc = "yank entire buffer" })
+
+-- Toggle cursor column
+keymap.set("n", "<leader>cl", "<cmd>call utils#ToggleCursorCol()<cr>", { desc = "toggle cursor column" })
+
+-- Move current line up and down
+keymap.set("n", "<A-k>", '<cmd>call utils#SwitchLine(line("."), "up")<cr>', { desc = "move line up" })
+keymap.set("n", "<A-j>", '<cmd>call utils#SwitchLine(line("."), "down")<cr>', { desc = "move line down" })
+
+-- Move current visual-line selection up and down
+keymap.set("x", "<A-k>", '<cmd>call utils#MoveSelection("up")<cr>', { desc = "move selection up" })
+
+keymap.set("x", "<A-j>", '<cmd>call utils#MoveSelection("down")<cr>', { desc = "move selection down" })
+
+-- Replace visual selection with text in register, but not contaminate the register,
+-- see also https://stackoverflow.com/q/10723700/6064933.
+keymap.set("x", "p", '"_c<Esc>p')
+
+-- Go to a certain buffer
+keymap.set("n", "gb", '<cmd>call buf_utils#GoToBuffer(v:count, "forward")<cr>', {
+ desc = "go to buffer (forward)",
+})
+keymap.set("n", "gB", '<cmd>call buf_utils#GoToBuffer(v:count, "backward")<cr>', {
+ desc = "go to buffer (backward)",
+})
+
+-- Switch windows
+keymap.set("n", "<left>", "<c-w>h")
+keymap.set("n", "<Right>", "<C-W>l")
+keymap.set("n", "<Up>", "<C-W>k")
+keymap.set("n", "<Down>", "<C-W>j")
+
+-- Text objects for URL
+keymap.set({ "x", "o" }, "iu", "<cmd>call text_obj#URL()<cr>", { desc = "URL text object" })
+
+-- Text objects for entire buffer
+keymap.set({ "x", "o" }, "iB", ":<C-U>call text_obj#Buffer()<cr>", { desc = "buffer text object" })
+
+-- Do not move my cursor when joining lines.
+keymap.set("n", "J", function()
+ vim.cmd([[
+ normal! mzJ`z
+ delmarks z
+ ]])
+end, {
+ desc = "join lines without moving cursor",
+})
+
+keymap.set("n", "gJ", function()
+ -- we must use `normal!`, otherwise it will trigger recursive mapping
+ vim.cmd([[
+ normal! mzgJ`z
+ delmarks z
+ ]])
+end, {
+ desc = "join lines without moving cursor",
+})
+
+-- Break inserted text into smaller undo units when we insert some punctuation chars.
+local undo_ch = { ",", ".", "!", "?", ";", ":" }
+for _, ch in ipairs(undo_ch) do
+ keymap.set("i", ch, ch .. "<c-g>u")
+end
+
+-- insert semicolon in the end
+keymap.set("i", "<A-;>", "<Esc>miA;<Esc>`ii")
+
+-- Go to the beginning and end of current line in insert mode quickly
+keymap.set("i", "<C-A>", "<HOME>")
+keymap.set("i", "<C-E>", "<END>")
+
+-- Go to beginning of command in command-line mode
+keymap.set("c", "<C-A>", "<HOME>")
+
+-- Delete the character to the right of the cursor
+keymap.set("i", "<C-D>", "<DEL>")
+
+keymap.set("n", "<leader>cb", function()
+ local cnt = 0
+ local blink_times = 7
+ local timer = uv.new_timer()
+
+ timer:start(0, 100, vim.schedule_wrap(function()
+ vim.cmd[[
+ set cursorcolumn!
+ set cursorline!
+ ]]
+
+ if cnt == blink_times then
+ timer:close()
+ end
+
+ cnt = cnt + 1
+ end))
+end)
diff --git a/.config/nvim/lua/plugin_specs.lua b/.config/nvim/lua/plugin_specs.lua
new file mode 100644
index 0000000..a585ee1
--- /dev/null
+++ b/.config/nvim/lua/plugin_specs.lua
@@ -0,0 +1,503 @@
+local utils = require("utils")
+
+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", -- latest stable release
+ lazypath,
+ }
+end
+vim.opt.rtp:prepend(lazypath)
+
+-- check if firenvim is active
+local firenvim_not_active = function()
+ return not vim.g.started_by_firenvim
+end
+
+local plugin_specs = {
+ -- auto-completion engine
+ {
+ "hrsh7th/nvim-cmp",
+ -- event = 'InsertEnter',
+ event = "VeryLazy",
+ dependencies = {
+ "hrsh7th/cmp-nvim-lsp",
+ "onsails/lspkind-nvim",
+ "hrsh7th/cmp-path",
+ "hrsh7th/cmp-buffer",
+ "hrsh7th/cmp-omni",
+ "hrsh7th/cmp-emoji",
+ "quangnguyen30192/cmp-nvim-ultisnips",
+ },
+ config = function()
+ require("config.nvim-cmp")
+ end,
+ },
+
+ {
+ "neovim/nvim-lspconfig",
+ event = { "BufRead", "BufNewFile" },
+ config = function()
+ require("config.lsp")
+ end,
+ },
+
+ {
+ "nvim-treesitter/nvim-treesitter",
+ enabled = function()
+ if vim.g.is_mac then
+ return true
+ end
+ return false
+ end,
+ event = "VeryLazy",
+ build = ":TSUpdate",
+ config = function()
+ require("config.treesitter")
+ end,
+ },
+
+ -- Python indent (follows the PEP8 style)
+ { "Vimjas/vim-python-pep8-indent", ft = { "python" } },
+
+ -- Python-related text object
+ { "jeetsukumaran/vim-pythonsense", ft = { "python" } },
+
+ { "machakann/vim-swap", event = "VeryLazy" },
+
+ -- IDE for Lisp
+ -- 'kovisoft/slimv'
+ {
+ "vlime/vlime",
+ enabled = function()
+ if utils.executable("sbcl") then
+ return true
+ end
+ return false
+ end,
+ config = function(plugin)
+ vim.opt.rtp:append(plugin.dir .. "/vim")
+ end,
+ ft = { "lisp" },
+ },
+
+ -- Super fast buffer jump
+ {
+ "smoka7/hop.nvim",
+ event = "VeryLazy",
+ config = function()
+ require("config.nvim_hop")
+ end,
+ },
+
+ -- Show match number and index for searching
+ {
+ "kevinhwang91/nvim-hlslens",
+ branch = "main",
+ keys = { "*", "#", "n", "N" },
+ config = function()
+ require("config.hlslens")
+ end,
+ },
+ {
+ "Yggdroot/LeaderF",
+ cmd = "Leaderf",
+ build = function()
+ if not vim.g.is_win then
+ vim.cmd(":LeaderfInstallCExtension")
+ end
+ end,
+ },
+ "nvim-lua/plenary.nvim",
+ {
+ "nvim-telescope/telescope.nvim",
+ cmd = "Telescope",
+ dependencies = {
+ "nvim-telescope/telescope-symbols.nvim",
+ },
+ },
+ {
+ "lukas-reineke/headlines.nvim",
+ dependencies = "nvim-treesitter/nvim-treesitter",
+ config = true, -- or `opts = {}`
+ },
+ -- A list of colorscheme plugin you may want to try. Find what suits you.
+ { "navarasu/onedark.nvim", lazy = true },
+ { "sainnhe/edge", lazy = true },
+ { "sainnhe/sonokai", lazy = true },
+ { "sainnhe/gruvbox-material", lazy = true },
+ { "sainnhe/everforest", lazy = true },
+ { "EdenEast/nightfox.nvim", lazy = true },
+ { "catppuccin/nvim", name = "catppuccin", lazy = true },
+ { "olimorris/onedarkpro.nvim", lazy = true },
+ { "marko-cerovac/material.nvim", lazy = true },
+
+ { "nvim-tree/nvim-web-devicons", event = "VeryLazy" },
+
+ {
+ "nvim-lualine/lualine.nvim",
+ event = "VeryLazy",
+ cond = firenvim_not_active,
+ config = function()
+ require("config.statusline")
+ end,
+ },
+
+ {
+ "akinsho/bufferline.nvim",
+ event = { "BufEnter" },
+ cond = firenvim_not_active,
+ config = function()
+ require("config.bufferline")
+ end,
+ },
+
+ -- fancy start screen
+ {
+ "nvimdev/dashboard-nvim",
+ cond = firenvim_not_active,
+ config = function()
+ require("config.dashboard-nvim")
+ end,
+ },
+
+ {
+ "lukas-reineke/indent-blankline.nvim",
+ event = "VeryLazy",
+ main = 'ibl',
+ config = function()
+ require("config.indent-blankline")
+ end,
+ },
+
+ -- Highlight URLs inside vim
+ { "itchyny/vim-highlighturl", event = "VeryLazy" },
+
+ -- notification plugin
+ {
+ "rcarriga/nvim-notify",
+ event = "VeryLazy",
+ config = function()
+ require("config.nvim-notify")
+ end,
+ },
+
+ -- For Windows and Mac, we can open an URL in the browser. For Linux, it may
+ -- not be possible since we maybe in a server which disables GUI.
+ {
+ "tyru/open-browser.vim",
+ enabled = function()
+ if vim.g.is_win or vim.g.is_mac then
+ return true
+ else
+ return false
+ end
+ end,
+ event = "VeryLazy",
+ },
+
+ -- Only install these plugins if ctags are installed on the system
+ -- show file tags in vim window
+ {
+ "liuchengxu/vista.vim",
+ enabled = function()
+ if utils.executable("ctags") then
+ return true
+ else
+ return false
+ end
+ end,
+ cmd = "Vista",
+ },
+
+ -- Snippet engine and snippet template
+ { "SirVer/ultisnips", dependencies = {
+ "honza/vim-snippets",
+ }, event = "InsertEnter" },
+
+ -- Automatic insertion and deletion of a pair of characters
+ { "Raimondi/delimitMate", event = "InsertEnter" },
+
+ -- Comment plugin
+ { "tpope/vim-commentary", event = "VeryLazy" },
+
+ -- Multiple cursor plugin like Sublime Text?
+ -- 'mg979/vim-visual-multi'
+
+ -- Autosave files on certain events
+ { "907th/vim-auto-save", event = "InsertEnter" },
+
+ -- Show undo history visually
+ { "simnalamburt/vim-mundo", cmd = { "MundoToggle", "MundoShow" } },
+
+ -- better UI for some nvim actions
+ { "stevearc/dressing.nvim" },
+
+ -- Manage your yank history
+ {
+ "gbprod/yanky.nvim",
+ cmd = { "YankyRingHistory" },
+ config = function()
+ require("config.yanky")
+ end,
+ },
+
+ -- Handy unix command inside Vim (Rename, Move etc.)
+ { "tpope/vim-eunuch", cmd = { "Rename", "Delete" } },
+
+ -- Repeat vim motions
+ { "tpope/vim-repeat", event = "VeryLazy" },
+
+ { "nvim-zh/better-escape.vim", event = { "InsertEnter" } },
+
+ {
+ "lyokha/vim-xkbswitch",
+ enabled = function()
+ if vim.g.is_mac and utils.executable("xkbswitch") then
+ return true
+ end
+ return false
+ end,
+ event = { "InsertEnter" },
+ },
+
+ {
+ "Neur1n/neuims",
+ enabled = function()
+ if vim.g.is_win then
+ return true
+ end
+ return false
+ end,
+ event = { "InsertEnter" },
+ },
+
+ -- Auto format tools
+ { "sbdchd/neoformat", cmd = { "Neoformat" } },
+
+ -- Git command inside vim
+ {
+ "tpope/vim-fugitive",
+ event = "User InGitRepo",
+ config = function()
+ require("config.fugitive")
+ end,
+ },
+
+ -- Better git log display
+ { "rbong/vim-flog", cmd = { "Flog" } },
+ { "akinsho/git-conflict.nvim", version = "*", config = true },
+ {
+ "ruifm/gitlinker.nvim",
+ event = "User InGitRepo",
+ config = function()
+ require("config.git-linker")
+ end,
+ },
+
+ -- Show git change (change, delete, add) signs in vim sign column
+ {
+ "lewis6991/gitsigns.nvim",
+ config = function()
+ require("config.gitsigns")
+ end,
+ },
+
+ -- Better git commit experience
+ { "rhysd/committia.vim", lazy = true },
+
+ {
+ "sindrets/diffview.nvim"
+ },
+
+ {
+ "kevinhwang91/nvim-bqf",
+ ft = "qf",
+ config = function()
+ require("config.bqf")
+ end,
+ },
+
+ -- Another markdown plugin
+ { "preservim/vim-markdown", ft = { "markdown" } },
+
+ -- Faster footnote generation
+ { "vim-pandoc/vim-markdownfootnotes", ft = { "markdown" } },
+
+ -- Vim tabular plugin for manipulate tabular, required by markdown plugins
+ { "godlygeek/tabular", cmd = { "Tabularize" } },
+
+ -- Markdown previewing (only for Mac and Windows)
+ {
+ "iamcco/markdown-preview.nvim",
+ enabled = function()
+ if vim.g.is_win or vim.g.is_mac then
+ return true
+ end
+ return false
+ end,
+ build = "cd app && npm install",
+ ft = { "markdown" },
+ },
+
+ {
+ "folke/zen-mode.nvim",
+ cmd = "ZenMode",
+ config = function()
+ require("config.zen-mode")
+ end,
+ },
+
+ {
+ "rhysd/vim-grammarous",
+ enabled = function()
+ if vim.g.is_mac then
+ return true
+ end
+ return false
+ end,
+ ft = { "markdown" },
+ },
+
+ { "chrisbra/unicode.vim", event = "VeryLazy" },
+
+ -- Additional powerful text object for vim, this plugin should be studied
+ -- carefully to use its full power
+ { "wellle/targets.vim", event = "VeryLazy" },
+
+ -- Plugin to manipulate character pairs quickly
+ { "machakann/vim-sandwich", event = "VeryLazy" },
+
+ -- Add indent object for vim (useful for languages like Python)
+ { "michaeljsmith/vim-indent-object", event = "VeryLazy" },
+
+ -- Only use these plugin on Windows and Mac and when LaTeX is installed
+ {
+ "lervag/vimtex",
+ enabled = function()
+ if utils.executable("latex") then
+ return true
+ end
+ return false
+ end,
+ ft = { "tex" },
+ },
+
+ -- Since tmux is only available on Linux and Mac, we only enable these plugins
+ -- for Linux and Mac
+ -- .tmux.conf syntax highlighting and setting check
+ {
+ "tmux-plugins/vim-tmux",
+ enabled = function()
+ if utils.executable("tmux") then
+ return true
+ end
+ return false
+ end,
+ ft = { "tmux" },
+ },
+
+ -- Modern matchit implementation
+ { "andymass/vim-matchup", event = "BufRead" },
+ { "tpope/vim-scriptease", cmd = { "Scriptnames", "Message", "Verbose" } },
+
+ -- Asynchronous command execution
+ { "skywind3000/asyncrun.vim", lazy = true, cmd = { "AsyncRun" } },
+ { "cespare/vim-toml", ft = { "toml" }, branch = "main" },
+
+ -- Edit text area in browser using nvim
+ {
+ "glacambre/firenvim",
+ enabled = function()
+ if vim.g.is_win or vim.g.is_mac then
+ return true
+ end
+ return false
+ end,
+ build = function()
+ vim.fn["firenvim#install"](0)
+ end,
+ lazy = true,
+ },
+
+ -- Debugger plugin
+ {
+ "sakhnik/nvim-gdb",
+ enabled = function()
+ if vim.g.is_win or vim.g.is_linux then
+ return true
+ end
+ return false
+ end,
+ build = { "bash install.sh" },
+ lazy = true,
+ },
+
+ -- Session management plugin
+ { "tpope/vim-obsession", cmd = "Obsession" },
+
+ {
+ "ojroques/vim-oscyank",
+ enabled = function()
+ if vim.g.is_linux then
+ return true
+ end
+ return false
+ end,
+ cmd = { "OSCYank", "OSCYankReg" },
+ },
+
+ -- The missing auto-completion for cmdline!
+ {
+ "gelguy/wilder.nvim",
+ build = ":UpdateRemotePlugins",
+ },
+
+ -- showing keybindings
+ {
+ "folke/which-key.nvim",
+ event = "VeryLazy",
+ config = function()
+ require("config.which-key")
+ end,
+ },
+
+ -- show and trim trailing whitespaces
+ { "jdhao/whitespace.nvim", event = "VeryLazy" },
+
+ -- file explorer
+ {
+ "nvim-tree/nvim-tree.lua",
+ keys = { "<space>s" },
+ dependencies = { "nvim-tree/nvim-web-devicons" },
+ config = function()
+ require("config.nvim-tree")
+ end,
+ },
+
+ { "ii14/emmylua-nvim", ft = "lua" },
+ {
+ "j-hui/fidget.nvim",
+ event = "VeryLazy",
+ tag = "legacy",
+ config = function()
+ require("config.fidget-nvim")
+ end,
+ },
+}
+
+-- configuration for lazy itself.
+local lazy_opts = {
+ ui = {
+ border = "rounded",
+ title = "Plugin Manager",
+ title_pos = "center",
+ },
+}
+
+require("lazy").setup(plugin_specs, lazy_opts)