Convert nvim submodule to regular directory

This commit is contained in:
Didictateur 2026-06-02 08:20:14 +02:00
parent e861999040
commit 785dc1b36e
95 changed files with 6611 additions and 1 deletions

1
nvim

@ -1 +0,0 @@
Subproject commit 652e95b4c17c4737ef6a214fbe4473e29342da9a

8
nvim/.gitignore vendored Normal file
View file

@ -0,0 +1,8 @@
tags
en.utf-8.add.spl
*.exe
.netrwhist
*.log
.DS_Store
lazy-lock.json
.env*

8
nvim/.stylua.toml Normal file
View file

@ -0,0 +1,8 @@
# config for stylua, ref: https://github.com/JohnnyMorganz/StyLua
syntax = "Lua51"
column_width = 100
line_endings = "Unix"
indent_type = "Spaces"
indent_width = 2
quote_style = "AutoPreferDouble"
call_parentheses= "NoSingleTable"

21
nvim/LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018-2023 jdhao
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

209
nvim/README.md Normal file
View file

@ -0,0 +1,209 @@
<div align="center">
<p>
<a>
<img alt="Linux" src="https://img.shields.io/badge/Linux-%23.svg?style=flat-square&logo=linux&color=FCC624&logoColor=black" />
</a>
<a>
<img alt="macOS" src="https://img.shields.io/badge/macOS-%23.svg?style=flat-square&logo=apple&color=000000&logoColor=white" />
</a>
<a>
<img alt="Windows" src="https://img.shields.io/badge/Windows-%23.svg?style=flat-square&logo=windows&color=0078D6&logoColor=white" />
</a>
<a href="https://github.com/neovim/neovim/releases/tag/stable">
<img src="https://img.shields.io/badge/Neovim-0.12.2-blueviolet.svg?style=flat-square&logo=Neovim&logoColor=green" alt="Neovim minimum version"/>
</a>
<a href="https://github.com/jdhao/nvim-config/search?l=vim-script">
<img src="https://img.shields.io/github/languages/top/jdhao/nvim-config" alt="Top languages"/>
</a>
<a href="https://github.com/jdhao/nvim-config/graphs/commit-activity">
<img src="https://img.shields.io/github/commit-activity/m/jdhao/nvim-config?style=flat-square" />
</a>
<a href="https://github.com/jdhao/nvim-config/graphs/contributors">
<img src="https://img.shields.io/github/contributors/jdhao/nvim-config?style=flat-square" />
</a>
<a>
<img src="https://img.shields.io/github/repo-size/jdhao/nvim-config?style=flat-square" />
</a>
<a href="https://github.com/jdhao/nvim-config/blob/master/LICENSE">
<img src="https://img.shields.io/github/license/jdhao/nvim-config?style=flat-square&logo=GNU&label=License" alt="License"/>
</a>
<a href="https://deepwiki.com/jdhao/nvim-config">
<img src="https://deepwiki.com/badge.svg" alt="deepwiki"/>
</a>
</p>
</div>
# Introduction
This repo hosts my Nvim configuration for Linux, macOS, and Windows.
`init.lua` is the config entry point for terminal Nvim,
and `ginit.vim` is the additional config file for [GUI client of Nvim](https://github.com/neovim/neovim/wiki/Related-projects#gui).
My configurations are heavily documented to make it as clear as possible.
While you can clone the whole repository and use it, it is not recommended though.
Good configurations are personal. Everyone should have his or her unique config file.
You are encouraged to copy from this repo the part you want and add it to your own config.
To reduce the possibility of breakage, **this config is only maintained for [the latest nvim stable release](https://github.com/neovim/neovim/releases/tag/stable).
No effort is spent on maintaining backward compatibility.**
# Install and setup
See [doc here](docs/README.md) on how to install Nvim's dependencies, Nvim itself,
and how to set up on different platforms (Linux, macOS, and Windows).
# Features #
+ Plugin management via [Lazy.nvim](https://github.com/folke/lazy.nvim).
+ Code, snippet, word auto-completion via [nvim-cmp](https://github.com/hrsh7th/nvim-cmp).
+ Language server protocol (LSP) support via [nvim-lspconfig](https://github.com/neovim/nvim-lspconfig).
+ Git integration via [vim-fugitive](https://github.com/tpope/vim-fugitive).
+ Better escaping from insert mode via [better-escape.vim](https://github.com/nvim-zh/better-escape.vim).
+ Ultra-fast project-wide fuzzy searching via [fzf-lua](https://github.com/ibhagwan/fzf-lua).
+ Faster code commenting via [vim-commentary](https://github.com/tpope/vim-commentary).
+ Faster matching pair insertion and jump via [nvim-autopairs](https://github.com/windwp/nvim-autopairs).
+ Smarter and faster matching pair management (add, replace or delete) via [vim-sandwich](https://github.com/machakann/vim-sandwich).
+ Fast buffer jump via [hop.nvim](https://github.com/smoka7/hop.nvim).
+ Powerful snippet insertion via [Ultisnips](https://github.com/SirVer/ultisnips).
+ Beautiful statusline via [lualine.nvim](https://github.com/nvim-lualine/lualine.nvim).
+ File tree explorer via [nvim-tree.lua](https://github.com/nvim-tree/nvim-tree.lua).
+ Better quickfix list with [nvim-bqf](https://github.com/kevinhwang91/nvim-bqf).
+ Show search index and count with [nvim-hlslens](https://github.com/kevinhwang91/nvim-hlslens).
+ User-defined mapping hint via [which-key.nvim](https://github.com/folke/which-key.nvim).
+ Asynchronous code execution via [asyncrun.vim](https://github.com/skywind3000/asyncrun.vim).
+ Code highlighting via [nvim-treesitter](https://github.com/nvim-treesitter/nvim-treesitter).
+ Code editing using true nvim inside browser via [firenvim](https://github.com/glacambre/firenvim).
+ Beautiful colorscheme via [sainnhe/gruvbox-material](https://github.com/sainnhe/gruvbox-material) and other colorschemes.
+ Markdown previewing via [render-markdown.nvim](https://github.com/MeanderingProgrammer/render-markdown.nvim)
+ LaTeX editing and previewing via [vimtex](https://github.com/lervag/vimtex)
+ Animated GUI style notification via [nvim-notify](https://github.com/rcarriga/nvim-notify).
+ Tags navigation via [vista](https://github.com/liuchengxu/vista.vim).
+ Code folding with [nvim-ufo](https://github.com/kevinhwang91/nvim-ufo) and [statuscol.nvim](https://github.com/luukvbaal/statuscol.nvim)
+ ......
# UI Demo
For more UI demos, check [here](https://github.com/jdhao/nvim-config/issues/15).
## Start screen with dashboard-nvim
<p align="center">
<img src="https://user-images.githubusercontent.com/16662357/183256752-fb23b215-a6b8-4646-beed-9999f52d53f1.png" width="800">
</p>
## File fuzzy finding using fzf-lua
<p align="center">
<img src="https://github.com/user-attachments/assets/3199e35d-121a-487b-bfd1-58eb69b0b48a" width="800">
</p>
## Code autocompletion with nvim-cmp
<p align="center">
<img src="https://user-images.githubusercontent.com/16662357/128590006-0fc1451f-fac1-49b2-bb95-8aba21bfa44e.gif" width="800">
</p>
## Git add, commit and push via fugitive.vim
<p align="center">
<img src="https://user-images.githubusercontent.com/16662357/128590833-aaa05d53-19ef-441d-a5a9-ba1bbd3936c1.gif" width="800">
</p>
## Tags
<p align="center">
<img src="https://user-images.githubusercontent.com/16662357/128589584-4036a1a2-2e0a-4bbe-8aaf-ff8b91644648.jpg" width="800">
</p>
## Cursor jump via hop.nvim
Go to a string starting with `se`
<p align="center">
<img src="https://user-images.githubusercontent.com/16662357/139459219-8a7e6ac4-1d24-4008-a370-b56773d7cb85.gif" width="800">
</p>
## GUI-style notification with nvim-notify
<p align="center">
<img src="https://user-images.githubusercontent.com/16662357/128589873-aadb8264-1098-4834-9876-fa66a309be05.gif" width="800">
</p>
## code folding with nvim-ufo and statuscol.nvim
<p align="center">
<img src="https://github.com/user-attachments/assets/a01a56b2-7c91-43de-b305-f2fbaa81dcec" width="800">
</p>
# Shortcuts
Some of the shortcuts I use frequently are listed here. In the following shortcuts, `<leader>` represents ASCII character `,`.
| Shortcut | Mode | platform | Description |
|-------------------|---------------|-----------------|--------------------------------------------------------------------------|
| `<leader>ff` | Normal | Linux/macOS/Win | Fuzzy file searching in a floating window |
| `<leader>fh` | Normal | Linux/macOS/Win | Fuzzy help file grepping in a floating window |
| `<leader>fg` | Normal | Linux/macOS/Win | Fuzzy project-wide grepping in a floating window |
| `<leader>ft` | Normal | Linux/macOS/Win | Fuzzy buffer tag searching in a floating window |
| `<leader>fb` | Normal | Linux/macOS/Win | Fuzzy buffer switching in a floating window |
| `<leader><Space>` | Normal | Linux/macOS/Win | Remove trailing white spaces |
| `<leader>v` | Normal | Linux/macOS/Win | Reselect last pasted text |
| `<leader>ev` | Normal | Linux/macOS/Win | Edit Nvim config in a new tabpage |
| `<leader>sv` | Normal | Linux/macOS/Win | Reload Nvim config |
| `<leader>q` | Normal | Linux/macOS/Win | Quit current window |
| `<leader>Q` | Normal | Linux/macOS/Win | Quit all window and close Nvim |
| `<leader>w` | Normal | Linux/macOS/Win | Save current buffer content |
| `<leader>y` | Normal | Linux/macOS/Win | Copy the content of entire buffer to default register |
| `<leader>cl` | Normal | Linux/macOS/Win | Toggle cursor column |
| `<leader>cd` | Normal | Linux/macOS/Win | Change current working directory to to the dir of current buffer |
| `<space>t` | Normal | Linux/macOS/Win | Toggle tag window (show project tags in the right window) |
| `<leader>gs` | Normal | Linux/macOS/Win | Show Git status result |
| `<leader>gw` | Normal | Linux/macOS/Win | Run Git add for current file |
| `<leader>gc` | Normal | Linux/macOS/Win | Run git commit |
| `<leader>gpl` | Normal | Linux/macOS/Win | Run git pull |
| `<leader>gpu` | Normal | Linux/macOS/Win | Run git push |
| `<leader>gbd` | Normal | Linux/macOS/Win | Delete a branch |
| `<leader>gbn` | Normal | Linux/macOS/Win | Create a new branch |
| `<leader>gl` | Normal/Visual | Linux/macOS/Win | Get perm link for current/visually-select lines |
| `<leader>gbr` | Normal | macOS | Browse current git repo in browser |
| `<leader>gb` | Visual | macOS | Blame current line |
| `<F9>` | Normal | Linux/macOS/Win | Compile&run current source file (for C++, LaTeX, Lua, Python) |
| `<F11>` | Normal | Linux/macOS/Win | Toggle spell checking |
| `<F12>` | Normal | Linux/macOS/Win | Toggle paste mode |
| `\x` | Normal | Linux/macOS/Win | Close location or quickfix window |
| `\d` | Normal | Linux/macOS/Win | Close current buffer and go to previous buffer |
| `{count}gb` | Normal | Linux/macOS/Win | Go to buffer `{count}` or next buffer in the buffer list. |
| `{operator}iB` | Normal | Linux/macOS/Win | Operate in the whole buffer, `{operator}` can be `v`, `y`, `c`, `d` etc. |
| `Alt-k` | Normal | Linux/macOS/Win | Move current line or selected lines up |
| `Alt-j` | Normal | Linux/macOS/Win | Move current line or selected lines down |
| `ctrl-u` | Insert | Linux/macOS/Win | Turn word under cursor to upper case |
| `ctrl-t` | Insert | Linux/macOS/Win | Turn word under cursor to title case |
| `jk` | Insert | Linux/macOS/Win | Return to Normal mode without lagging |
| `ZR` | Normal | Linux/macOS/Win | Restart nvim without quitting |
# Custom commands
In addition to commands provided by various plugins, I have also created some custom commands for personal use.
| command | description | example |
|--------------|-------------------------------------------------------------------------|--------------------------------|
| `Redir` | capture command output to a tabpage for easier inspection. | `Redir hi` |
| `Edit` | edit multiple files at the same time, supports globing | `Edit *.vim` |
| `Datetime` | print current date and time or convert Unix time stamp to date and time | `Datetime 12345` or `Datetime` |
| `JSONFormat` | format a JSON file | `JSONFormat` |
| `CopyPath` | copy current file path to clipboard | `CopyPath relative` |
# Contributing
If you find anything that needs improving, do not hesitate to point it out or create a PR.
If you come across an issue, you can first use `:checkhealth` command provided by `nvim` to trouble-shoot yourself.
Please read carefully the messages provided by health check.
If you still have an issue, [open a new issue](https://github.com/jdhao/nvim-config/issues).
# Further readings
Some of the resources that I find helpful in mastering Nvim is documented [here](docs/nvim_resources.md).
You may also be interested in my posts on configuring Nvim [here](https://jdhao.github.io/categories/Nvim/).

1
nvim/_config.yml Normal file
View file

@ -0,0 +1 @@
theme: jekyll-theme-dinky

View file

@ -0,0 +1,36 @@
set commentstring=//\ %s
" Disable inserting comment leader after hitting o or O or <Enter>
set formatoptions-=o
set formatoptions-=r
nnoremap <silent> <buffer> <F9> :call <SID>compile_run_cpp()<CR>
function! s:compile_run_cpp() abort
let src_path = expand('%:p:~')
let src_noext = expand('%:p:~:r')
" The building flags
let _flag = '-Wall -Wextra -std=c++11 -O2'
if executable('clang++')
let prog = 'clang++'
elseif executable('g++')
let prog = 'g++'
else
echoerr 'No C++ compiler found on the system!'
endif
call s:create_term_buf('h', 20)
execute printf('term %s %s %s -o %s && %s', prog, _flag, src_path, src_noext, src_noext)
startinsert
endfunction
function s:create_term_buf(_type, size) abort
set splitbelow
set splitright
if a:_type ==# 'v'
vnew
else
new
endif
execute 'resize ' . a:size
endfunction

View file

@ -0,0 +1 @@
set commentstring=#\ %s

View file

@ -0,0 +1,20 @@
local opt = vim.opt
-- General tab settings
opt.tabstop = 4 -- Number of visual spaces per TAB
opt.softtabstop = 4 -- Number of spaces in tab when editing
opt.shiftwidth = 4 -- Number of spaces to use for autoindent
opt.expandtab = false -- Expand tab to spaces so that tabs are spaces
-- gofumpt adds more rule to gofmt, and is compatible with gofmt,
-- so also https://github.com/mvdan/gofumpt
vim.keymap.set("n", "<Space>f", function()
vim.cmd([[silent !gofumpt -w %]])
end, { buffer = true, silent = true })
vim.keymap.set("n", "<F9>", function()
vim.cmd([[!go run %]])
end, {
buffer = true,
silent = true,
})

View file

@ -0,0 +1,9 @@
local win_width = vim.api.nvim_win_get_width(0)
-- do not change layout if the screen is not wide enough
if win_width < 200 then
return
end
-- L means to put window to leftmost
vim.cmd.wincmd([[L]])

View file

@ -0,0 +1,3 @@
" Disable inserting comment leader after hitting o or O or <Enter>
set formatoptions-=o
set formatoptions-=r

View file

@ -0,0 +1,4 @@
vim.keymap.set({ "n", "v" }, "<space>f", ":JSONFormat<cr>", {
buffer = true,
silent = true,
})

View file

@ -0,0 +1,11 @@
-- Disable inserting comment leader after hitting o/O/<Enter>
vim.opt_local.formatoptions:remove { "o", "r" }
vim.keymap.set("n", "<F9>", "<cmd>luafile %<CR>", {
buffer = true,
silent = true,
})
vim.keymap.set("n", "<Space>f", "<cmd>silent !stylua %<CR>", {
buffer = true,
silent = true,
})

View file

@ -0,0 +1,4 @@
-- q to quit quickly
vim.keymap.set("n", "q", "<cmd>q<CR>", {
desc = "quit nvim",
})

View file

@ -0,0 +1,107 @@
local function add_reference_at_end(label, url, title)
vim.schedule(function()
local bufnr = vim.api.nvim_get_current_buf()
local line_count = vim.api.nvim_buf_line_count(bufnr)
-- Prepare reference definition
local ref_def = "[" .. label .. "]: " .. url
if title and title ~= "" then
ref_def = ref_def .. ' "' .. title .. '"'
end
-- Check if references section exists
local buffer_lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
local has_ref_section = false
for _, line in ipairs(buffer_lines) do
if line:match("^%s*<!%-%-.*[Rr]eferences.*%-%->[%s]*$") then
has_ref_section = true
break
end
end
local lines_to_add = {}
-- Add references header if it doesn't exist
if not has_ref_section then
if #lines_to_add == 0 then
table.insert(lines_to_add, "")
end
table.insert(lines_to_add, "<!-- References -->")
end
table.insert(lines_to_add, ref_def)
-- Insert at buffer end
vim.api.nvim_buf_set_lines(bufnr, line_count, line_count, false, lines_to_add)
end)
end
local function get_ref_link_labels()
local labels = {}
local seen = {} -- To avoid duplicates
local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false)
for _, line in ipairs(lines) do
-- Pattern explanation:
-- %[.-%] matches [link text] (non-greedy)
-- %[(.-)%] matches [label] and captures the label content
local start_pos = 1
while start_pos <= #line do
local match_start, match_end, label = string.find(line, "%[.-%]%[(.-)%]", start_pos)
if not match_start then
break
end
-- Only add unique labels
if label and label ~= "" and not seen[label] then
table.insert(labels, label)
seen[label] = true
end
start_pos = match_end + 1
end
end
return labels
end
local function count_consecutive_spaces(str)
-- Remove leading spaces first
local trimmed = str:match("^%s*(.*)")
local count = 0
-- Count each sequence of one or more consecutive spaces
for spaces in trimmed:gmatch("%s+") do
count = count + 1
end
return count
end
vim.api.nvim_buf_create_user_command(0, "AddRef", function(opts)
local args = vim.split(opts.args, " ", { trimempty = true })
if #args < 2 then
vim.print("Usage: :AddRef <label> <url>")
return
end
local label = args[1]
local url = args[2]
add_reference_at_end(label, url, "")
end, {
desc = "Add reference link at buffer end",
nargs = "+",
complete = function(arg_lead, cmdline, curpos)
-- vim.print(string.format("arg_lead: '%s', cmdline: '%s', curpos: %d", arg_lead, cmdline, curpos))
-- only complete the first argument
if count_consecutive_spaces(cmdline) > 1 then
-- we are now starting the second argument, so no completion anymore
return {}
end
local ref_link_labels = get_ref_link_labels()
return ref_link_labels
end,
})

View file

@ -0,0 +1,69 @@
set concealcursor=c
set synmaxcol=3000 " For long Chinese paragraphs
set wrap
" Fix minor issue with footnote, see https://github.com/vim-pandoc/vim-markdownfootnotes/issues/22
if exists(':FootnoteNumber')
nnoremap <buffer><silent> ^^ :<C-U>call markdownfootnotes#VimFootnotes('i')<CR>
inoremap <buffer><silent> ^^ <C-O>:<C-U>call markdownfootnotes#VimFootnotes('i')<CR>
imap <buffer> <silent> @@ <Plug>ReturnFromFootnote
nmap <buffer> <silent> @@ <Plug>ReturnFromFootnote
endif
" Text objects for Markdown code blocks.
xnoremap <buffer><silent> ic :<C-U>call text_obj#MdCodeBlock('i')<CR>
xnoremap <buffer><silent> ac :<C-U>call text_obj#MdCodeBlock('a')<CR>
onoremap <buffer><silent> ic :<C-U>call text_obj#MdCodeBlock('i')<CR>
onoremap <buffer><silent> ac :<C-U>call text_obj#MdCodeBlock('a')<CR>
" Use + to turn several lines to an unordered list.
" Ref: https://vi.stackexchange.com/q/5495/15292 and https://stackoverflow.com/q/42438795/6064933.
nnoremap <buffer><silent> + :set operatorfunc=AddListSymbol<CR>g@
xnoremap <buffer><silent> + :<C-U> call AddListSymbol(visualmode(), 1)<CR>
function! AddListSymbol(type, ...) abort
if a:0
let line_start = line("'<")
let line_end = line("'>")
else
let line_start = line("'[")
let line_end = line("']")
endif
" add list symbol to each line
for line in range(line_start, line_end)
let text = getline(line)
let l:end = matchend(text, '^\s*')
if l:end == 0
let new_text = '+ ' . text
else
let new_text = text[0 : l:end-1] . ' + ' . text[l:end :]
endif
call setline(line, new_text)
endfor
endfunction
" Add hard line breaks for Markdown
" nnoremap <buffer><silent> \ :set operatorfunc=AddLineBreak<CR>g@
xnoremap <buffer><silent> \ :<C-U> call AddLineBreak(visualmode(), 1)<CR>
function! AddLineBreak(type, ...) abort
if a:0
let line_start = line("'<")
let line_end = line("'>")
else
let line_start = line("'[")
let line_end = line("']")
endif
for line in range(line_start, line_end)
let text = getline(line)
" add backslash to each line
let new_text = text . "\\"
call setline(line, new_text)
endfor
endfunction

View file

@ -0,0 +1,75 @@
local utils = require("utils")
local opt = vim.opt
opt.wrap = false
opt.sidescroll = 5
opt.sidescrolloff = 2
opt.colorcolumn = "100"
opt.tabstop = 4 -- Number of visual spaces per TAB
opt.softtabstop = 4 -- Number of spaces in tab when editing
opt.shiftwidth = 4 -- Number of spaces to use for autoindent
opt.expandtab = true -- Expand tab to spaces so that tabs are spaces
-- when we run `:compiler ruff`, then followed by `:make`,
-- Nvim will run ruff in the current directory. By default, `--preview` option is used.
-- The following option is used to customize the option passed to ruff.
vim.g.ruff_makeprg_params = ""
local get_proj_root = function()
local project_marker = { ".git", "pyproject.toml" }
local project_root = vim.fs.root(0, project_marker)
return project_root
end
local get_py_env = function()
local project_root = get_proj_root()
if project_root == nil then
return
end
local venv_name = utils.get_virtual_env()
if venv_name ~= "" then
return "plain_venv"
end
-- check if this is uv-managed project
local uv_lock_path = vim.fs.joinpath(project_root, "uv.lock")
if vim.fn.filereadable(uv_lock_path) == 1 then
return "uv"
end
return ""
end
local py_env = get_py_env()
if vim.fn.exists(":AsyncRun") == 2 then
local py_cmd = "python"
if py_env == "uv" then
py_cmd = "uv run python"
end
local rhs = string.format(":<C-U>AsyncRun %s -u %%<CR>", py_cmd)
vim.keymap.set("n", "<F9>", rhs, {
buffer = true,
silent = true,
})
end
-- format current file
local py_fmt_cmd = "!black"
if py_env == "uv" then
py_fmt_cmd = "!uv run black"
end
local rhs = string.format("<cmd>silent %s %%<CR>", py_fmt_cmd)
vim.keymap.set("n", "<space>f", rhs, {
buffer = true,
silent = true,
})

View file

@ -0,0 +1,6 @@
" Set quickfix window height, see also https://github.com/lervag/vimtex/issues/1127
function! AdjustWindowHeight(minheight, maxheight)
execute max([a:minheight, min([line('$'), a:maxheight])]) . 'wincmd _'
endfunction
call AdjustWindowHeight(5, 15)

View file

@ -0,0 +1 @@
set commentstring=--\ %s

View file

@ -0,0 +1,2 @@
vim.o.textwidth = 120
vim.o.wrap = true

View file

@ -0,0 +1 @@
set colorcolumn=

View file

View file

@ -0,0 +1,15 @@
" Disable inserting comment leader after hitting o or O or <Enter>
set formatoptions-=o
set formatoptions-=r
" Set the folding related options for vim script. Setting folding option in
" modeline is annoying in that the modeline get executed each time the window
" focus is lost (see
" https://github.com/tmux-plugins/vim-tmux-focus-events/issues/14)
set foldmethod=expr foldexpr=utils#VimFolds(v:lnum) foldtext=utils#MyFoldText()
" Use :help command for keyword when pressing `K` in vim file,
" see `:h K` and https://stackoverflow.com/q/15867323/6064933
set keywordprg=:help
nnoremap <buffer><silent> <F9> :source %<CR>

View file

@ -0,0 +1,4 @@
" Turn off syntax highlighting for large YAML files.
if line('$') > 500
setlocal syntax=OFF
endif

View file

@ -0,0 +1,4 @@
---@type vim.lsp.Config
return {
filetypes = { "c", "cpp", "cc" },
}

19
nvim/after/lsp/gopls.lua Normal file
View file

@ -0,0 +1,19 @@
-- settings for gopls can be found in https://go.dev/gopls/settings
---@type vim.lsp.Config
return {
settings = {
gopls = {
usePlaceholders = true,
analyses = {
unusedparams = true,
},
staticcheck = true,
gofumpt = true,
-- inlayHints settings, see https://go.dev/gopls/inlayHints
hints = {
compositeLiteralFields = true,
parameterNames = true,
},
},
},
}

21
nvim/after/lsp/lua_ls.lua Normal file
View file

@ -0,0 +1,21 @@
-- settings for lua-language-server can be found on https://luals.github.io/wiki/settings/
---@type vim.lsp.Config
return {
---@type lspconfig.settings.lua_ls
settings = {
Lua = {
runtime = {
-- Tell the language server which version of Lua you're using (most likely LuaJIT in the case of Neovim)
version = "LuaJIT",
},
hint = {
enable = true,
},
workspace = {
library = {
vim.env.VIMRUNTIME .. "/lua",
},
},
},
},
}

View file

@ -0,0 +1,53 @@
-- For what diagnostic is enabled in which type checking mode, check doc:
-- https://github.com/microsoft/pyright/blob/main/docs/configuration.md#diagnostic-settings-defaults
-- Currently, the pyright also has some issues displaying hover documentation:
-- https://www.reddit.com/r/neovim/comments/1gdv1rc/what_is_causeing_the_lsp_hover_docs_to_looks_like/
local new_capability = {
-- this will remove some of the diagnostics that duplicates those from ruff, idea taken and adapted from
-- here: https://github.com/astral-sh/ruff-lsp/issues/384#issuecomment-1989619482
textDocument = {
publishDiagnostics = {
tagSupport = {
valueSet = { 2 },
},
},
hover = {
contentFormat = { "plaintext" },
dynamicRegistration = true,
},
},
}
---@type vim.lsp.Config
return {
-- cmd = { "delance-langserver", "--stdio" },
---@type lspconfig.settings.pyright
settings = {
pyright = {
-- disable import sorting and use Ruff for this
disableOrganizeImports = true,
disableTaggedHints = false,
},
python = {
analysis = {
autoSearchPaths = true,
diagnosticMode = "workspace",
typeCheckingMode = "standard",
useLibraryCodeForTypes = true,
-- we can this setting below to redefine some diagnostics
diagnosticSeverityOverrides = {
deprecateTypingAliases = false,
},
-- inlay hint settings are provided by pylance?
inlayHints = {
callArgumentNames = "partial",
functionReturnTypes = true,
pytestParameters = true,
variableTypes = true,
},
},
},
},
capabilities = new_capability,
}

9
nvim/after/lsp/ruff.lua Normal file
View file

@ -0,0 +1,9 @@
---@type vim.lsp.Config
return {
init_options = {
-- the settings can be found here: https://docs.astral.sh/ruff/editors/settings/
settings = {
organizeImports = true,
},
},
}

View file

@ -0,0 +1,30 @@
function! buf_utils#GoToBuffer(count, direction) abort
if a:count == 0
if a:direction ==# 'forward'
bnext
elseif a:direction ==# 'backward'
bprevious
else
echoerr 'Bad argument ' a:direction
endif
return
endif
" Check the validity of buffer number.
if index(s:GetBufNums(), a:count) == -1
" Using `lua vim.notify('invalid bufnr: ' .. a:count)` won't work, because
" we are essentially mixing Lua and vim script. We need to make sure that
" args inside vim.notify() are valid vim values. The conversion from vim
" value to lua value will be done by Nvim. See also https://github.com/neovim/neovim/pull/11338.
call v:lua.vim.notify('Invalid bufnr: ' . a:count, 4, {'title': 'nvim-config'})
return
endif
" Do not use {count} for gB (it is less useful)
if a:direction ==# 'forward'
silent execute('buffer' . a:count)
endif
endfunction
function! s:GetBufNums() abort
return map(copy(getbufinfo({'buflisted':1})), 'v:val.bufnr')
endfunction

View file

@ -0,0 +1,86 @@
function! text_obj#URL() abort
if match(&runtimepath, 'vim-highlighturl') != -1
" Note that we use https://github.com/itchyny/vim-highlighturl to get the URL pattern.
let url_pattern = highlighturl#default_pattern()
else
let url_pattern = expand('<cfile>')
" Since expand('<cfile>') also works for normal words, we need to check if
" this is really URL using heuristics, e.g., URL length.
if len(url_pattern) <= 10
return
endif
endif
" We need to find all possible URL on this line and their start, end index.
" Then find where current cursor is, and decide if cursor is on one of the
" URLs.
let line_text = getline('.')
let url_infos = []
let [_url, _idx_start, _idx_end] = matchstrpos(line_text, url_pattern)
while _url !=# ''
let url_infos += [[_url, _idx_start+1, _idx_end]]
let [_url, _idx_start, _idx_end] = matchstrpos(line_text, url_pattern, _idx_end)
endwhile
" echo url_infos
" If no URL is found, do nothing.
if len(url_infos) == 0
return
endif
let [start_col, end_col] = [-1, -1]
" If URL is found, find if cursor is on it.
let [buf_num, cur_row, cur_col] = getcurpos()[0:2]
for url_info in url_infos
" echo url_info
let [_url, _idx_start, _idx_end] = url_info
if cur_col >= _idx_start && cur_col <= _idx_end
let start_col = _idx_start
let end_col = _idx_end
break
endif
endfor
" Cursor is not on a URL, do nothing.
if start_col == -1
return
endif
" Now set the '< and '> mark
call setpos("'<", [buf_num, cur_row, start_col, 0])
call setpos("'>", [buf_num, cur_row, end_col, 0])
normal! gv
endfunction
function! text_obj#MdCodeBlock(type) abort
" the parameter type specify whether it is inner text objects or around
" text objects.
" Move the cursor to the end of line in case that cursor is on the opening
" of a code block. Actually, there are still issues if the cursor is on the
" closing of a code block. In this case, the start row of code blocks would
" be wrong. Unless we can match code blocks, it not easy to fix this.
normal! $
let start_row = searchpos('\s*```', 'bnW')[0]
let end_row = searchpos('\s*```', 'nW')[0]
let buf_num = bufnr()
if a:type ==# 'i'
let start_row += 1
let end_row -= 1
endif
" echo a:type start_row end_row
call setpos("'<", [buf_num, start_row, 1, 0])
call setpos("'>", [buf_num, end_row, 1, 0])
execute 'normal! `<V`>'
endfunction
function! text_obj#Buffer() abort
let buf_num = bufnr()
call setpos("'<", [buf_num, 1, 1, 0])
call setpos("'>", [buf_num, line('$'), 1, 0])
execute 'normal! `<V`>'
endfunction

141
nvim/autoload/utils.vim Normal file
View file

@ -0,0 +1,141 @@
" Create command alias safely, see https://stackoverflow.com/q/3878692/6064933
" The following two functions are taken from answer below on SO:
" https://stackoverflow.com/a/10708687/6064933
function! utils#Cabbrev(key, value) abort
execute printf('cabbrev <expr> %s (getcmdtype() == ":" && getcmdpos() <= %d) ? %s : %s',
\ a:key, 1+len(a:key), <SID>Single_quote(a:value), <SID>Single_quote(a:key))
endfunction
function! s:Single_quote(str) abort
return "'" . substitute(copy(a:str), "'", "''", 'g') . "'"
endfunction
" Custom fold expr, adapted from https://vi.stackexchange.com/a/9094/15292
function! utils#VimFolds(lnum) abort
" get content of current line and the line below
let l:cur_line = getline(a:lnum)
let l:next_line = getline(a:lnum+1)
if l:cur_line =~# '^"{'
return '>' . (matchend(l:cur_line, '"{*') - 1)
endif
if l:cur_line ==# '' && (matchend(l:next_line, '"{*') - 1) == 1
return 0
endif
return '='
endfunction
" Custom fold text, adapted from https://vi.stackexchange.com/a/3818/15292
" and https://vi.stackexchange.com/a/6608/15292
function! utils#MyFoldText() abort
let l:line = getline(v:foldstart)
let l:fold_line_num = v:foldend - v:foldstart
let l:fold_text = substitute(l:line, '^"{\+', '', 'g')
let l:fill_char_num = &textwidth - len(l:fold_text) - len(l:fold_line_num) - 10
return printf('+%s%s %s (%s L)', repeat('-', 4), l:fold_text, repeat('-', l:fill_char_num), l:fold_line_num)
endfunction
" Toggle cursor column
function! utils#ToggleCursorCol() abort
if &cursorcolumn
set nocursorcolumn
echo 'cursorcolumn: OFF'
else
set cursorcolumn
echo 'cursorcolumn: ON'
endif
endfunction
function! utils#SwitchLine(src_line_idx, direction) abort
if a:direction ==# 'up'
if a:src_line_idx == 1
return
endif
move-2
elseif a:direction ==# 'down'
if a:src_line_idx == line('$')
return
endif
move+1
endif
endfunction
function! utils#MoveSelection(direction) abort
" only do this if previous mode is visual line mode. Once we press some keys in
" visual line mode, we will leave this mode. So the output of `mode()` will be
" `n` instead of `V`. We can use `visualmode()` instead to check the previous
" mode, see also https://stackoverflow.com/a/61486601/6064933
if visualmode() !=# 'V'
return
endif
let l:start_line = line("'<")
let l:end_line = line("'>")
let l:num_line = l:end_line - l:start_line + 1
if a:direction ==# 'up'
if l:start_line == 1
" we can also directly use `normal gv`, see https://stackoverflow.com/q/9724123/6064933
normal! gv
return
endif
silent execute printf('%s,%smove-2', l:start_line, l:end_line)
normal! gv
elseif a:direction ==# 'down'
if l:end_line == line('$')
normal! gv
return
endif
silent execute printf('%s,%smove+%s', l:start_line, l:end_line, l:num_line)
normal! gv
endif
endfunction
" Output current time or unix timestamp in human-readable format.
function! utils#iso_time(timestamp) abort
" Get current datetime
if !a:timestamp
return strftime('%Y-%m-%d %H:%M:%S%z')
endif
" this timestamp in expressed in milliseconds
if len(a:timestamp) == 13
let l:timestamp = a:timestamp[:-4]
" this timestamp in expressed in microseconds
elseif len(a:timestamp) == 16
let l:timestamp = a:timestamp[:-7]
else
let l:timestamp = a:timestamp
endif
return strftime('%Y-%m-%d %H:%M:%S%z', l:timestamp)
endfunction
" Redirect command output to a register for later processing.
" Ref: https://stackoverflow.com/q/2573021/6064933 and https://unix.stackexchange.com/q/8101/221410 .
function! utils#CaptureCommandOutput(command) abort
let l:tmp = @m
redir @m
silent! execute a:command
redir END
"create a scratch buffer for dumping the text, ref: https://vi.stackexchange.com/a/11311/15292.
tabnew | setlocal buftype=nofile bufhidden=wipe nobuflisted noswapfile
let l:lines = split(@m, '\n')
call nvim_buf_set_lines(0, 0, 0, 0, l:lines)
let @m = l:tmp
endfunction
" Edit all files matching the given patterns.
function! utils#MultiEdit(patterns) abort
for p in a:patterns
for f in glob(p, 0, 1)
execute 'edit ' . f
endfor
endfor
endfunction

192
nvim/docs/README.md Normal file
View file

@ -0,0 +1,192 @@
This doc summarizes how to install and use this configuration in detail.
# Pre-requisite
## Terminal emulators
Which [terminal emulator](https://en.wikipedia.org/wiki/Terminal_emulator) we choose to use greatly affects the appearance and features of Nvim.
Since Nvim supports true colors, terminals that support true colors are preferred.
For a list of terminals that support true colors, see [here](https://github.com/termstandard/colors).
For macOS, we can use [kitty](https://sw.kovidgoyal.net/kitty/), [iterm2](https://www.iterm2.com/), [wezterm](https://wezfurlong.org/wezterm/) or [Alacritty](https://github.com/jwilm/alacritty).
If you ssh to Linux server on Windows, I recommend [wsltty](https://github.com/mintty/wsltty) and [Cygwin](https://www.cygwin.com/),
both of them use [mintty](https://github.com/mintty/mintty) as the terminal emulator.
For the latest version of Windows 10, you can also try [Windows Terminal](https://github.com/microsoft/terminal).
## Patched Fonts
Since statusline or file explorer plugins often use Unicode symbols not available in normal font,
we need to install a patched font from the [nerd-fonts](https://github.com/ryanoasis/nerd-fonts) project.
# Automatic installation
## Automatic Installation for Linux
To set up a workable Nvim environment on Linux,
I use [a bash script](nvim_setup_linux.sh) to automatically install necessary dependencies, Nvim itself and configs.
Note that the variable `PYTHON_INSTALLED`, `SYSTEM_PYTHON` and `ADD_TO_SYSTEM_PATH` in the script
should be set properly based on your environment.
## Automatic installation for Windows
Run the script [nvim_setup_windows.ps1](nvim_setup_windows.ps1) in PowerShell **with Administrator rights**:
```
.\docs\nvim_setup_windows.ps1
```
# Manual install
There are a few dependencies if we want to use Nvim for efficient editing and development work.
## Dependencies
### Python
A lot of Nvim plugins are mainly written in Python, so we must install Python 3.
The easiest way to install is via [Anaconda](https://docs.anaconda.com/anaconda/install/index.html) or [Miniconda](https://docs.conda.io/en/latest/miniconda.html).
After installation, make sure that you can run `python --version`,
and that the output should be Python 3.x.
### Pynvim
Nvim relies on [pynvim](https://github.com/neovim/pynvim) to communicate with plugins that utilize its Python binding.
Pynvim is required by plugins such as [wilder.nvim](https://github.com/gelguy/wilder.nvim).
```
pip install -U pynvim
```
### python-lsp-server
[python-lsp-server (pylsp)](https://github.com/python-lsp/python-lsp-server) is a Python [Language Server](https://microsoft.github.io/language-server-protocol/) for completion, linting, go to definition, etc.
```
pip install 'python-lsp-server[all]' pylsp-mypy python-lsp-isort python-lsp-black
```
Note the executable for pylsp is also named `pylsp`. You need to set its PATH correctly.
If you use pip from Anaconda, the executable path may be something like `$CONDA_ROOT/bin/pylsp`.
For native python, the path for pylsp may be like `$HOME/.local/bin/pylsp`
### Node
We need to install node.js from [here](https://nodejs.org/en/download/).
For Linux, you can use the following script:
```bash
# Ref: https://johnpapa.net/node-and-npm-without-sudo/
wget https://nodejs.org/dist/v14.15.4/node-v14.15.4-linux-x64.tar.xz
mkdir -p $HOME/tools
# extract node to a custom directory, the directory should exist.
tar xvf node-v14.15.4-linux-x64.tar.xz --directory=$HOME/tools
```
Then add the following config to `.bash_profile` or `.zshrc`
```bash
export PATH="$HOME/tools/node-v14.15.4-linux-x64/bin:$PATH"
```
Source the file:
```bash
source ~/.bash_profile
# source ~/.zshrc
```
### vim-language-server
[vim-language-server](https://github.com/iamcco/vim-language-server) provides completion for vim script. We can install vim-language-server globally:
```bash
npm install -g vim-language-server
```
vim-language-server is installed in the same directory as the node executable.
### Git
Git is required by plugin manager [lazy.nvim](https://github.com/folke/lazy.nvim) and other git-related plugins.
For Linux and macOS, Git is usually pre-installed.
The version of Git on the Linux system may be too old so that plugins may break.
Check [here](https://jdhao.github.io/2021/03/27/upgrade_git_on_linux/) on how to install and set up the latest version of Git.
For Windows, install [Git for Windows](https://git-scm.com/download/win) and make sure you can run `git` from command line.
### universal-ctags
In order to use tags related plugins such as [vista.vim](https://github.com/liuchengxu/vista.vim), we need to install a ctags distribution.
Universal-ctags is preferred.
To install it on Linux, we need to build it from source. See [here](https://askubuntu.com/a/836521/768311) for the details.
To install ctags on macOS, use [Homebrew](https://formulae.brew.sh/formula/universal-ctags):
```bash
brew install universal-ctags
```
To install it Windows, use [chocolatey](https://chocolatey.org/) or [scoop](https://scoop.sh/)
```
choco install universal-ctags
# scoop bucket add extras
# scoop install universal-ctags
```
Set its PATH properly and make sure you can run `ctags` from command line.
### Ripgrep
[Ripgrep](https://github.com/BurntSushi/ripgrep), aka, `rg`, is a fast grepping tool available for both Linux, Windows and macOS.
It is used by several searching plugins.
For Windows and macOS, we can install it via chocolatey and homebrew respectively.
For Linux, we can download the [binary release](https://github.com/BurntSushi/ripgrep/releases) and install it.
Set its PATH properly and make sure you can run `rg` from command line.
### Linters
A linter is a tool to check the source code for possible style and syntax issues.
Based on the programming languages we use, we may need to install various linters.
+ Python: [pylint](https://github.com/PyCQA/pylint) and [flake8](https://github.com/PyCQA/flake8).
+ Vim script: [vint](https://github.com/Kuniwak/vint).
Set their PATH properly and make sure you can run `pylint`, `flake8` and `vint` from command line.
## Install Nvim
There are various ways to install Nvim depending on your system.
This config is only maintained for [the latest nvim stable release](https://github.com/neovim/neovim/releases/tag/stable).
Please check the official doc on how to install Neovim on different systems: https://github.com/neovim/neovim/blob/master/INSTALL.md
After installing Nvim, we need to set the PATH to nvim correctly.
**Make sure that you can run `nvim` from the command line after all these setups**.
## Setting up Nvim
After installing nvim and all the dependencies, we will install plugin managers and set up this config.
### How to install this configuration
On Linux and macOS, the directory is `~/.config/nvim`.
On Windows, the config directory is `$HOME/AppData/Local/nvim`[^1].
First, we need to remove all the files under the config directory (including dot files),
then go to this directory, and run the following command:
```
git clone --depth=1 https://github.com/jdhao/nvim-config.git .
```
After that, when we first open nvim, all the plugins will be installed automatically.
Since I use quite a lot of plugins (more than 60), it may take some time to install all of them.
[^1]: Use `echo %userprofile%` to see where your `$HOME` is.

View file

@ -0,0 +1,20 @@
# This script is used to update Nvim on macOS
#!/bin/bash
set -eux
wget https://github.com/neovim/neovim/releases/download/stable/nvim-macos.tar.gz
if [[ ! -d "$HOME/tools/" ]]; then
mkdir -p "$HOME/tools"
fi
# Delete existing nvim installation.
# For newer release, the directory name is nvim-macos
if [[ -d "$HOME/tools/nvim-macos" ]]; then
rm -rf "$HOME/tools/nvim-macos"
fi
# Extract the tar ball
tar zxvf nvim-macos.tar.gz -C "$HOME/tools"
rm nvim-macos.tar.gz

View file

@ -0,0 +1,18 @@
Below are a list of resources that inspire me. This list is non-exhaustive as I
can not remember the source of many settings.
- [From .vimrc to .vim](https://vimways.org/2018/from-vimrc-to-vim/)
- http://stevelosh.com/blog/2010/09/coming-home-to-vim/
- https://github.com/tamlok/tvim/blob/master/.vimrc
- https://nvie.com/posts/how-i-boosted-my-vim/
- https://blog.carbonfive.com/2011/10/17/vim-text-objects-the-definitive-guide/
- https://sanctum.geek.nz/arabesque/vim-anti-patterns/
- https://github.com/gkapfham/dotfiles/blob/master/.vimrc
- https://google.github.io/styleguide/vimscriptguide.xml
- https://github.com/romainl/idiomatic-vimrc
- https://github.com/mhinz/vim-galore
- https://learnvimscriptthehardway.stevelosh.com/
- https://vimways.org/2018/from-vimrc-to-vim/
- [vim script for Python developers](https://github.com/yegappan/VimScriptForPythonDevelopers)
- [Awesome neovim](https://github.com/rockerBOO/awesome-neovim): extensive list of neovim resources (plugins, mostly).
- [What is your favorite plugins for Nvim 0.5](https://www.reddit.com/r/neovim/comments/o20w3z/what_are_your_favorite_neovim_plugins_exclusive/)

258
nvim/docs/nvim_setup_linux.sh Executable file
View file

@ -0,0 +1,258 @@
#!/bin/bash
set -exu
set -o pipefail
# Whether python3 has been installed on the system
PYTHON_INSTALLED=true
# If Python has been installed, then we need to know whether Python is provided
# by the system, or you have already installed Python under your HOME.
SYSTEM_PYTHON=false
# If SYSTEM_PYTHON is false, we need to decide whether to install
# Anaconda (INSTALL_ANACONDA=true) or Miniconda (INSTALL_ANACONDA=false)
INSTALL_ANACONDA=false
# Whether to add the path of the installed executables to system PATH
ADD_TO_SYSTEM_PATH=true
# select which shell we are using
USE_ZSH_SHELL=true
USE_BASH_SHELL=false
if [[ ! -d "$HOME/packages/" ]]; then
mkdir -p "$HOME/packages/"
fi
if [[ ! -d "$HOME/tools/" ]]; then
mkdir -p "$HOME/tools/"
fi
#######################################################################
# Anaconda or miniconda install #
#######################################################################
if [[ "$INSTALL_ANACONDA" = true ]]; then
CONDA_DIR=$HOME/tools/anaconda
CONDA_NAME=Anaconda.sh
CONDA_LINK="https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/Anaconda3-2021.11-Linux-x86_64.sh"
else
CONDA_DIR=$HOME/tools/miniconda
CONDA_NAME=Miniconda.sh
CONDA_LINK="https://mirrors.tuna.tsinghua.edu.cn/anaconda/miniconda/Miniconda3-py39_4.10.3-Linux-x86_64.sh"
fi
if [[ ! "$PYTHON_INSTALLED" = true ]]; then
echo "Installing Python in user HOME"
SYSTEM_PYTHON=false
echo "Downloading and installing conda"
if [[ ! -f "$HOME/packages/$CONDA_NAME" ]]; then
curl -Lo "$HOME/packages/$CONDA_NAME" $CONDA_LINK
fi
# Install conda silently
if [[ -d $CONDA_DIR ]]; then
rm -rf "$CONDA_DIR"
fi
bash "$HOME/packages/$CONDA_NAME" -b -p "$CONDA_DIR"
# Setting up environment variables
if [[ "$ADD_TO_SYSTEM_PATH" = true ]] && [[ "$USE_BASH_SHELL" = true ]]; then
echo "export PATH=\"$CONDA_DIR/bin:\$PATH\"" >> "$HOME/.bash_profile"
fi
else
echo "Python is already installed. Skip installing it."
fi
# Install some Python packages used by Nvim plugins.
echo "Installing Python packages"
declare -a PY_PACKAGES=("pynvim" 'python-lsp-server[all]' "vim-vint" "python-lsp-isort" "pylsp-mypy" "python-lsp-black")
if [[ "$SYSTEM_PYTHON" = true ]]; then
echo "Using system Python to install $(PY_PACKAGES)"
# If we use system Python, we need to install these Python packages under
# user HOME, since we do not have permissions to install them under system
# directories.
for p in "${PY_PACKAGES[@]}"; do
pip install --user "$p"
done
else
echo "Using custom Python to install $(PY_PACKAGES)"
for p in "${PY_PACKAGES[@]}"; do
"$CONDA_DIR/bin/pip" install "$p"
done
fi
#######################################################################
# Install node and js-based language server #
#######################################################################
NODE_DIR=$HOME/tools/nodejs
NODE_SRC_NAME=$HOME/packages/nodejs.tar.gz
# when download speed is slow, we can also use its mirror site: https://mirrors.ustc.edu.cn/node/v15.0.0/
NODE_LINK="https://mirrors.ustc.edu.cn/node/v15.0.0/node-v15.0.0-linux-x64.tar.xz"
if [[ -z "$(command -v node)" ]]; then
echo "Install Node.js"
if [[ ! -f $NODE_SRC_NAME ]]; then
echo "Downloading Node.js and renaming"
wget $NODE_LINK -O "$NODE_SRC_NAME"
fi
if [[ ! -d "$NODE_DIR" ]]; then
echo "Creating Node.js directory under tools directory"
mkdir -p "$NODE_DIR"
echo "Extracting to $HOME/tools/nodejs directory"
tar xvf "$NODE_SRC_NAME" -C "$NODE_DIR" --strip-components 1
fi
if [[ "$ADD_TO_SYSTEM_PATH" = true ]] && [[ "$USE_BASH_SHELL" = true ]]; then
echo "export PATH=\"$NODE_DIR/bin:\$PATH\"" >> "$HOME/.bash_profile"
fi
else
echo "Node.js is already installed. Skip installing it."
NODE_DIR="$(realpath $(dirname $(which node))/..)"
fi
# Install vim-language-server
"$NODE_DIR/bin/npm" install -g vim-language-server
# Install bash-language-server
"$NODE_DIR/bin/npm" install -g bash-language-server
#######################################################################
# lua-language-server #
#######################################################################
LUA_LS_DIR=$HOME/tools/lua-language-server
LUA_LS_SRC=$HOME/packages/lua-language-server.tar.gz
LUA_LS_LINK="https://github.com/LuaLS/lua-language-server/releases/download/3.6.11/lua-language-server-3.6.11-linux-x64.tar.gz"
if [[ -z "$(command -v lua-language-server)" ]] && [[ ! -f "$LUA_LS_DIR/bin/lua-language-server" ]]; then
echo 'Install lua-language-server'
if [[ ! -f $LUA_LS_SRC ]]; then
echo "Downloading lua-language-server and renaming"
wget $LUA_LS_LINK -O "$LUA_LS_SRC"
fi
if [[ ! -d "$LUA_LS_DIR" ]]; then
echo "Creating lua-language-server directory under tools directory"
mkdir -p "$LUA_LS_DIR"
echo "Extracting to directory $LUA_LS_DIR"
tar zxvf "$LUA_LS_SRC" -C "$LUA_LS_DIR"
fi
if [[ "$ADD_TO_SYSTEM_PATH" = true ]] && [[ "$USE_BASH_SHELL" = true ]]; then
echo "export PATH=\"$LUA_LS_DIR/bin:\$PATH\"" >> "$HOME/.bash_profile"
fi
else
echo "lua-language-server is already installed. Skip installing it."
fi
#######################################################################
# Ripgrep part #
#######################################################################
RIPGREP_DIR=$HOME/tools/ripgrep
RIPGREP_SRC_NAME=$HOME/packages/ripgrep.tar.gz
RIPGREP_LINK="https://github.com/BurntSushi/ripgrep/releases/download/12.0.0/ripgrep-12.0.0-x86_64-unknown-linux-musl.tar.gz"
if [[ -z "$(command -v rg)" ]] && [[ ! -f "$RIPGREP_DIR/rg" ]]; then
echo "Install ripgrep"
if [[ ! -f $RIPGREP_SRC_NAME ]]; then
echo "Downloading ripgrep and renaming"
wget $RIPGREP_LINK -O "$RIPGREP_SRC_NAME"
fi
if [[ ! -d "$RIPGREP_DIR" ]]; then
echo "Creating ripgrep directory under tools directory"
mkdir -p "$RIPGREP_DIR"
echo "Extracting to $HOME/tools/ripgrep directory"
tar zxvf "$RIPGREP_SRC_NAME" -C "$RIPGREP_DIR" --strip-components 1
fi
if [[ "$ADD_TO_SYSTEM_PATH" = true ]] && [[ "$USE_BASH_SHELL" = true ]]; then
echo "export PATH=\"$RIPGREP_DIR:\$PATH\"" >> "$HOME/.bash_profile"
fi
# set up manpath and zsh completion for ripgrep
mkdir -p "$HOME/tools/ripgrep/doc/man/man1"
mv "$HOME/tools/ripgrep/doc/rg.1" "$HOME/tools/ripgrep/doc/man/man1"
if [[ "$USE_BASH_SHELL" = true ]]; then
echo 'export MANPATH=$HOME/tools/ripgrep/doc/man:$MANPATH' >> "$HOME/.bash_profile"
else
echo 'export MANPATH=$HOME/tools/ripgrep/doc/man:$MANPATH' >> "$HOME/.zshrc"
echo 'export FPATH=$HOME/tools/ripgrep/complete:$FPATH' >> "$HOME/.zshrc"
fi
else
echo "ripgrep is already installed. Skip installing it."
fi
#######################################################################
# Ctags install #
#######################################################################
CTAGS_SRC_DIR=$HOME/packages/ctags
CTAGS_DIR=$HOME/tools/ctags
CTAGS_LINK="https://github.com/universal-ctags/ctags.git"
if [[ ! -f "$CTAGS_DIR/bin/ctags" ]]; then
echo "Install ctags"
if [[ ! -d $CTAGS_SRC_DIR ]]; then
mkdir -p "$CTAGS_SRC_DIR"
else
# Prevent an incomplete download.
rm -rf "$CTAGS_SRC_DIR"
fi
git clone --depth=1 "$CTAGS_LINK" "$CTAGS_SRC_DIR" && cd "$CTAGS_SRC_DIR"
./autogen.sh && ./configure --prefix="$CTAGS_DIR"
make -j && make install
if [[ "$ADD_TO_SYSTEM_PATH" = true ]] && [[ "$USE_BASH_SHELL" = true ]]; then
echo "export PATH=\"$CTAGS_DIR/bin:\$PATH\"" >> "$HOME/.bash_profile"
fi
else
echo "ctags is already installed. Skip installing it."
fi
#######################################################################
# Nvim install #
#######################################################################
NVIM_DIR=$HOME/tools/nvim
NVIM_SRC_NAME=$HOME/packages/nvim-linux64.tar.gz
NVIM_CONFIG_DIR=$HOME/.config/nvim
NVIM_LINK="https://github.com/neovim/neovim/releases/download/stable/nvim-linux-x86_64.tar.gz"
if [[ ! -f "$NVIM_DIR/bin/nvim" ]]; then
echo "Installing Nvim"
echo "Creating nvim directory under tools directory"
if [[ ! -d "$NVIM_DIR" ]]; then
mkdir -p "$NVIM_DIR"
fi
if [[ ! -f $NVIM_SRC_NAME ]]; then
echo "Downloading Nvim"
wget "$NVIM_LINK" -O "$NVIM_SRC_NAME"
fi
echo "Extracting neovim"
tar zxvf "$NVIM_SRC_NAME" --strip-components 1 -C "$NVIM_DIR"
if [[ "$ADD_TO_SYSTEM_PATH" = true ]] && [[ "$USE_BASH_SHELL" = true ]]; then
echo "export PATH=\"$NVIM_DIR/bin:\$PATH\"" >> "$HOME/.bash_profile"
fi
else
echo "Nvim is already installed. Skip installing it."
fi
echo "Setting up config and installing plugins"
if [[ -d "$NVIM_CONFIG_DIR" ]]; then
rm -rf "$NVIM_CONFIG_DIR.backup"
mv "$NVIM_CONFIG_DIR" "$NVIM_CONFIG_DIR.backup"
fi
git clone --depth=1 https://github.com/jdhao/nvim-config.git "$NVIM_CONFIG_DIR"
echo "Installing nvim plugins, please wait"
"$NVIM_DIR/bin/nvim" -c "autocmd User LazyInstall quitall" -c "lua require('lazy').install()"
echo "Finished installing Nvim and its dependencies!"

View file

@ -0,0 +1,55 @@
# Set policy to avoid errors
Set-ExecutionPolicy RemoteSigned -scope CurrentUser
# Install scoop
Invoke-WebRequest -UseBasicParsing get.scoop.sh | Invoke-Expression
# Install node
scoop install nodejs
# Install ripgrep
scoop install ripgrep
# Install universal-ctags
scoop bucket add extras
scoop install universal-ctags
# Install vim-language-server
npm install -g vim-language-server
# Install bash-language-server
npm install -g bash-language-server
# Install miniconda3 (for Python)
scoop install miniconda3
# Install pynvim
pip install -U pynvim
# Install python-language-server
pip install 'python-lsp-server[all]' pylsp-mypy python-lsp-isort
# Install visual c++ redistribution
scoop install vcredist2022
# Install 7zip
scoop install 7zip
# Install lua-language-server
$lua_ls_link = "https://github.com/LuaLS/lua-language-server/releases/download/3.6.11/lua-language-server-3.6.11-win32-x64.zip"
$lua_ls_install_dir = "D:\portable_tools"
$lua_ls_src_path = "$lua_ls_install_dir\lua-language-server.zip"
$lua_ls_dir = "$lua_ls_install_dir\lua-language-server"
# Download file, ref: https://stackoverflow.com/a/51225744/6064933
Invoke-WebRequest $lua_ls_link -OutFile "$lua_ls_src_path"
# Extract the zip file using 7zip, ref: https://stackoverflow.com/a/41933215/6064933
7z x "$lua_ls_src_path" -o"$lua_ls_dir"
# Setup PATH env variable, ref: https://stackoverflow.com/q/714877/6064933
[Environment]::SetEnvironmentVariable("Path", $env:Path + ";$lua_ls_dir\bin", "Machine")
# Install neovim nightly
scoop bucket add versions
scoop install neovim

4
nvim/ftdetect/pdc.vim Normal file
View file

@ -0,0 +1,4 @@
augroup det_md
autocmd!
autocmd BufRead,BufNewFile *.pdc set filetype=markdown
augroup END

View file

@ -0,0 +1,4 @@
augroup det_snippet
autocmd!
autocmd BufRead,BufNewFile *.snippets set filetype=snippets
augroup END

74
nvim/ginit.vim Normal file
View file

@ -0,0 +1,74 @@
" Fix key mapping issues for GUI
inoremap <silent> <S-Insert> <C-R>+
cnoremap <S-Insert> <C-R>+
nnoremap <silent> <C-6> <C-^>
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" config for nvim-qt "
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" To check if neovim-qt is running, use `exists('g:GuiLoaded')`,
" see https://github.com/equalsraf/neovim-qt/issues/219
if exists('g:GuiLoaded')
" call GuiWindowMaximized(1)
GuiTabline 0
GuiPopupmenu 0
GuiLinespace 2
GuiFont! Hack\ NF:h10:l
endif
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" config for fvim "
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
if exists('g:fvim_loaded')
set termguicolors
colorscheme gruvbox8_hard
set guifont=Hack\ NF:h13
" Cursor tweaks
FVimCursorSmoothMove v:true
FVimCursorSmoothBlink v:true
" Background composition, can be 'none', 'blur' or 'acrylic'
FVimBackgroundComposition 'none'
FVimBackgroundOpacity 1.0
FVimBackgroundAltOpacity 1.0
" Title bar tweaks (themed with colorscheme)
FVimCustomTitleBar v:true
" Debug UI overlay
FVimDrawFPS v:false
" Font debugging -- draw bounds around each glyph
FVimFontDrawBounds v:false
" Font tweaks
FVimFontAntialias v:true
FVimFontAutohint v:true
FVimFontHintLevel 'full'
FVimFontSubpixel v:true
FVimFontLigature v:true
" can be 'default', '14.0', '-1.0' etc.
FVimFontLineHeight '+1'
" Try to snap the fonts to the pixels, reduces blur
" in some situations (e.g. 100% DPI).
FVimFontAutoSnap v:true
" Font weight tuning, possible values are 100..900
FVimFontNormalWeight 100
FVimFontBoldWeight 700
FVimUIPopupMenu v:false
endif
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" config for neovide "
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
if exists("g:neovide")
set background=dark
let g:neovide_transparency = 1.0
let g:neovide_cursor_animation_length = 0.1
let g:neovide_cursor_trail_size=0.3
let g:neovide_cursor_vfx_mode = ""
let g:neovide_cursor_vfx_particle_density=10.0
let g:neovide_cursor_vfx_opacity=150.0
endif

41
nvim/init.lua Normal file
View file

@ -0,0 +1,41 @@
-- This is my personal Nvim configuration supporting Mac, Linux and Windows, with various plugins configured.
-- This configuration evolves as I learn more about Nvim and become more proficient in using Nvim.
-- Since it is very long (more than 1000 lines!), you should read it carefully and take only the settings that suit you.
-- I would not recommend cloning this repo and replace your own config. Good configurations are personal,
-- built over time with a lot of polish.
--
-- Author: Jiedong Hao
-- Email: jdhao@hotmail.com
-- Blog: https://jdhao.github.io/
-- GitHub: https://github.com/jdhao
-- StackOverflow: https://stackoverflow.com/users/6064933/jdhao
local utils = require("utils")
vim.loader.enable()
local expected_version = "0.12.2"
utils.is_compatible_version(expected_version)
-- some global settings
require("globals")
-- setting options in nvim
require("options")
-- various autocommands
require("custom-autocmd")
-- all the user-defined mappings
require("mappings")
-- all the plugins installed and their configurations
require("plugin_specs")
-- This is done after plugin_specs, since lsp-config is loaded in that step
require("lsp_conf")
-- diagnostic related config
require("diagnostic-conf")
-- colorscheme settings
require("ui")

View file

@ -0,0 +1,100 @@
require("blink.cmp").setup {
-- 'default' (recommended) for mappings similar to built-in completions (C-y to accept)
-- 'super-tab' for mappings similar to vscode (tab to accept)
-- 'enter' for enter to accept
-- 'none' for no mappings
--
keymap = {
preset = "default",
["<Tab>"] = { "select_next", "fallback" },
["<S-Tab>"] = { "select_prev", "fallback" },
["<Enter>"] = { "select_and_accept", "fallback" },
["<C-U>"] = { "scroll_documentation_up", "fallback" },
["<C-D>"] = { "scroll_documentation_down", "fallback" },
},
appearance = {
-- 'mono' (default) for 'Nerd Font Mono' or 'normal' for 'Nerd Font'
-- Adjusts spacing to ensure icons are aligned
nerd_font_variant = "mono",
},
-- (Default) Only show the documentation popup when manually triggered
completion = {
documentation = {
auto_show = true,
},
},
-- Default list of enabled providers defined so that you can extend it
-- elsewhere in your config, without redefining it, due to `opts_extend`
sources = {
default = { "lsp", "path", "buffer", "omni" },
providers = {
-- Use the thesaurus source
thesaurus = {
name = "blink-cmp-words",
module = "blink-cmp-words.thesaurus",
-- All available options
opts = {
-- A score offset applied to returned items.
-- By default the highest score is 0 (item 1 has a score of -1, item 2 of -2 etc..).
score_offset = 0,
-- Default pointers define the lexical relations listed under each definition,
-- see Pointer Symbols below.
-- Default is as below ("antonyms", "similar to" and "also see").
definition_pointers = { "!", "&", "^" },
-- The pointers that are considered similar words when using the thesaurus,
-- see Pointer Symbols below.
-- Default is as below ("similar to", "also see" }
similarity_pointers = { "&", "^" },
-- The depth of similar words to recurse when collecting synonyms. 1 is similar words,
-- 2 is similar words of similar words, etc. Increasing this may slow results.
similarity_depth = 2,
},
},
-- Use the dictionary source
dictionary = {
name = "blink-cmp-words",
module = "blink-cmp-words.dictionary",
-- All available options
opts = {
-- The number of characters required to trigger completion.
-- Set this higher if completion is slow, 3 is default.
dictionary_search_threshold = 3,
-- See above
score_offset = 0,
-- See above
definition_pointers = { "!", "&", "^" },
},
},
},
per_filetype = {
text = { "dictionary" },
markdown = { "dictionary" },
},
},
-- (Default) Rust fuzzy matcher for typo resistance and significantly better performance
-- You may use a lua implementation instead by using `implementation = "lua"` or fallback to the lua implementation,
-- when the Rust fuzzy matcher is not available, by using `implementation = "prefer_rust"`
--
-- See the fuzzy documentation for more information
fuzzy = { implementation = "prefer_rust_with_warning" },
cmdline = {
completion = {
menu = {
auto_show = true,
},
},
keymap = {
["<Enter>"] = { "select_and_accept", "fallback" },
},
},
}

6
nvim/lua/config/bqf.lua Normal file
View file

@ -0,0 +1,6 @@
require("bqf").setup {
auto_resize_height = false,
preview = {
auto_preview = false,
},
}

View file

@ -0,0 +1,50 @@
require("bufferline").setup {
options = {
numbers = "none",
close_command = "bdelete! %d",
right_mouse_command = nil,
left_mouse_command = "buffer %d",
middle_mouse_command = nil,
indicator = {
icon = "", -- this should be omitted if indicator style is not 'icon'
style = "icon",
},
buffer_close_icon = "",
modified_icon = "",
close_icon = "",
left_trunc_marker = "",
right_trunc_marker = "",
max_name_length = 18,
max_prefix_length = 15,
tab_size = 10,
diagnostics = false,
custom_filter = function(bufnr)
-- if the result is false, this buffer will be shown, otherwise, this
-- buffer will be hidden.
-- filter out filetypes you don't want to see
local exclude_ft = { "qf", "fugitive", "git" }
local cur_ft = vim.bo[bufnr].filetype
local should_filter = vim.tbl_contains(exclude_ft, cur_ft)
if should_filter then
return false
end
return true
end,
show_buffer_icons = false,
show_buffer_close_icons = true,
show_close_icon = true,
show_tab_indicators = true,
persist_buffer_sort = true, -- whether or not custom sorted buffers should persist
separator_style = "bar",
enforce_regular_tabs = false,
always_show_bufferline = true,
sort_by = "id",
},
}
vim.keymap.set("n", "<space>bp", "<cmd>BufferLinePick<CR>", {
desc = "pick a buffer",
})

View file

@ -0,0 +1,51 @@
local ok, diffview = pcall(require, "diffview")
if not ok then
return
end
local actions = require("diffview.actions")
local prefix_conflicts = "<leader>gC"
diffview.setup {
enhanced_diff_hl = true,
view = {
default = {
disable_diagnostics = true,
},
merge_tool = {
layout = "diff3_mixed",
},
},
file_history_panel = {
win_config = {
type = "split",
position = "bottom",
height = 10,
},
},
keymaps = {
view = {
{
"n",
prefix_conflicts .. "t",
actions.conflict_choose("theirs"),
{ desc = "Conflict choose theirs" },
},
{
"n",
prefix_conflicts .. "o",
actions.conflict_choose("ours"),
{ desc = "Conflict choose ours" },
},
{
"n",
prefix_conflicts .. "a",
actions.conflict_choose("all"),
{ desc = "Conflict choose both" },
},
{ "n", "]C", actions.next_conflict, { desc = "Next conflict" } },
{ "n", "[C", actions.prev_conflict, { desc = "Previous conflict" } },
},
},
}

View file

@ -0,0 +1 @@
require("fidget").setup {}

View file

@ -0,0 +1,27 @@
local keymap = vim.keymap
keymap.set("n", "<leader>gs", "<cmd>Git<cr>", { desc = "Git: show status" })
keymap.set("n", "<leader>gw", "<cmd>Gwrite<cr>", { desc = "Git: add file" })
keymap.set("n", "<leader>gc", "<cmd>Git commit<cr>", { desc = "Git: commit changes" })
keymap.set("n", "<leader>gpl", "<cmd>Git pull<cr>", { desc = "Git: pull changes" })
keymap.set("n", "<leader>gpu", "<cmd>15 split|term git push<cr>", { desc = "Git: push changes" })
keymap.set("v", "<leader>gb", ":Git blame<cr>", { desc = "Git: blame selected line" })
-- convert git to Git in command line mode
vim.fn["utils#Cabbrev"]("git", "Git")
keymap.set("n", "<leader>gbn", function()
vim.ui.input({ prompt = "Enter a new branch name" }, function(user_input)
if user_input == nil or user_input == "" then
return
end
local cmd_str = string.format("G checkout -b %s", user_input)
vim.cmd(cmd_str)
end)
end, {
desc = "Git: create new branch",
})
keymap.set("n", "<leader>gf", ":Git fetch ", { desc = "Git: prune branches" })
keymap.set("n", "<leader>gbd", ":Git branch -D ", { desc = "Git: delete branch" })

View file

@ -0,0 +1,43 @@
require("fzf-lua").setup {
defaults = {
file_icons = "mini",
},
winopts = {
row = 0.5,
height = 0.7,
},
files = {
previewer = false,
git_icons = true,
-- using .gitignore is usually good, but still we may want to include some files,
-- you can create a file `.rgignore` to "unignore" those files, e.g., `.env` files.
-- see also https://github.com/BurntSushi/ripgrep/discussions/2512
-- and https://www.reddit.com/r/linuxquestions/comments/zycvud/ripgrep_respect_gitignore_but_show_env_files/
no_ignore = false,
},
grep = {
RIPGREP_CONFIG_PATH = vim.env.RIPGREP_CONFIG_PATH,
},
}
vim.keymap.set("n", "<leader>ff", "<cmd>FzfLua files<cr>", { desc = "Fuzzy find files" })
vim.keymap.set("n", "<leader>fg", "<cmd>FzfLua live_grep_native<cr>", { desc = "Fuzzy grep files" })
vim.keymap.set(
"n",
"<leader>fh",
"<cmd>FzfLua helptags<cr>",
{ desc = "Fuzzy grep tags in help files" }
)
vim.keymap.set("n", "<leader>ft", "<cmd>FzfLua btags<cr>", { desc = "Fuzzy search buffer tags" })
vim.keymap.set(
"n",
"<leader>fb",
"<cmd>FzfLua buffers<cr>",
{ desc = "Fuzzy search opened buffers" }
)
vim.keymap.set(
"n",
"<leader>fr",
"<cmd>FzfLua oldfiles<cr>",
{ desc = "Fuzzy search opened files history" }
)

View file

@ -0,0 +1,47 @@
local keymap = vim.keymap
local gitlinker = require("gitlinker")
gitlinker.setup {
callbacks = {
["dev.azure.com"] = function(url_data)
vim.print(url_data)
local url = require("gitlinker.hosts").get_base_https_url(url_data)
if url_data.lstart then
if url_data.lend == nil then
url_data.lend = url_data.lstart
end
url = url
.. "?path=/"
.. url_data.file
.. "&version=GC"
.. url_data.rev
.. "&line="
.. url_data.lstart
.. "&lineEnd="
.. url_data.lend
.. "&lineStartColumn=1"
.. "&lineEndColumn=120"
end
return url
end,
},
mappings = nil,
}
keymap.set({ "n", "v" }, "<leader>gl", function()
local mode = string.lower(vim.fn.mode())
gitlinker.get_buf_range_url(mode)
end, {
silent = true,
desc = "Git: get permlink",
})
keymap.set("n", "<leader>gbr", function()
gitlinker.get_repo_url {
action_callback = gitlinker.actions.open_in_browser,
}
end, {
silent = true,
desc = "Git: browse repo in browser",
})

View file

@ -0,0 +1,57 @@
local gs = require("gitsigns")
gs.setup {
signs = {
add = { text = "+" },
change = { text = "~" },
delete = { text = "_" },
topdelete = { text = "" },
changedelete = { text = "" },
},
word_diff = false,
on_attach = function(bufnr)
local function map(mode, l, r, opts)
opts = opts or {}
opts.buffer = bufnr
vim.keymap.set(mode, l, r, opts)
end
-- Navigation
map("n", "]c", function()
if vim.wo.diff then
return "]c"
end
vim.schedule(function()
gs.next_hunk()
end)
return "<Ignore>"
end, { expr = true, desc = "next hunk" })
map("n", "[c", function()
if vim.wo.diff then
return "[c"
end
vim.schedule(function()
gs.prev_hunk()
end)
return "<Ignore>"
end, { expr = true, desc = "previous hunk" })
-- Actions
map("n", "<leader>hp", gs.preview_hunk, { desc = "preview hunk" })
map("n", "<leader>hb", function()
gs.blame_line { full = true }
end, { desc = "blame hunk" })
end,
}
vim.api.nvim_create_autocmd("ColorScheme", {
pattern = "*",
callback = function()
vim.cmd([[
hi GitSignsChangeInline gui=reverse
hi GitSignsAddInline gui=reverse
hi GitSignsDeleteInline gui=reverse
]])
end,
})

View file

@ -0,0 +1,12 @@
local glance = require("glance")
glance.setup {
height = 25,
border = {
enable = true,
},
}
vim.keymap.set("n", "<space>gd", "<cmd>Glance definitions<cr>")
vim.keymap.set("n", "<space>gr", "<cmd>Glance references<cr>")
vim.keymap.set("n", "<space>gi", "<cmd>Glance implementations<cr>")

21
nvim/lua/config/gx.lua Normal file
View file

@ -0,0 +1,21 @@
---@diagnostic disable-next-line: missing-fields
require("gx").setup {
handlers = {
jira = {
name = "jira", -- set name of handler
handle = function(mode, line, _)
local ticket = require("gx.helper").find(line, mode, "(%a%a%a+%-%d+)")
local dotenv = require("dotenv")
local env_path = vim.fs.joinpath(vim.fn.stdpath("config"), ".env")
dotenv.load_dotenv(env_path)
local company_name = dotenv.get("COMPANY_NAME")
if ticket and #ticket < 20 then
local ticket_link = string.format("http://jira.%s/browse/%s", company_name, ticket)
return ticket_link
end
end,
},
},
}

View file

@ -0,0 +1,82 @@
local api = vim.api
local keymap = vim.keymap
local hlslens = require("hlslens")
hlslens.setup {
calm_down = true,
nearest_only = true,
}
local activate_hlslens = function(direction)
local cmd = string.format("normal! %s%szzzv", vim.v.count1, direction)
local status, msg = pcall(vim.cmd, cmd)
-- Deal with the case that there is no such pattern in current buffer.
if not status then
local start_idx, _ = string.find(msg, "E486", 1, true)
local msg_part = string.sub(msg, start_idx)
api.nvim_echo({ { msg_part } }, true, { err = true })
return
end
hlslens.start()
end
keymap.set("n", "n", "", {
callback = function()
activate_hlslens("n")
end,
})
keymap.set("n", "N", "", {
callback = function()
activate_hlslens("N")
end,
})
local check_cursor_word = function()
local cursor_word = vim.fn.expand("<cword>")
local result = cursor_word == ""
if result then
local msg = "E348: No string under cursor"
api.nvim_echo({ { msg } }, true, { err = true })
end
return result, cursor_word
end
keymap.set("n", "*", "", {
callback = function()
local cursor_word_empty, cursor_word = check_cursor_word()
if cursor_word_empty then
return
end
local cmd = string.format([[normal! /\v<%s>]], cursor_word)
-- In order to say that we are pressing Enter key, instead of typing literally the character,
-- we need to replace special notation with their internal representation.
local escaped_enter = vim.api.nvim_replace_termcodes("<CR>", true, false, true)
-- character `N` is used to keep the cursor when pressing `*`
local full_cmd = cmd .. escaped_enter .. "N"
vim.fn.execute(full_cmd)
hlslens.start()
end,
})
keymap.set("n", "#", "", {
callback = function()
local cursor_word_empty, cursor_word = check_cursor_word()
if cursor_word_empty then
return
end
local cmd = string.format([[normal! ?\v<%s>]], cursor_word)
local escaped_enter = vim.api.nvim_replace_termcodes("<CR>", true, false, true)
local full_cmd = cmd .. escaped_enter .. "N"
vim.fn.execute(full_cmd)
hlslens.start()
end,
})

7
nvim/lua/config/iron.lua Normal file
View file

@ -0,0 +1,7 @@
local iron = require("iron")
iron.core.set_config {
preferred = {
python = "ipython",
},
repl_open_cmd = "vertical 120 split",
}

View file

@ -0,0 +1,5 @@
require("iswap").setup {
move_cursor = true,
}
vim.keymap.set("n", "gs<", "<cmd>ISwapWithLeft<cr>")
vim.keymap.set("n", "gs>", "<cmd>ISwapWithRight<cr>")

View file

@ -0,0 +1,20 @@
---@diagnostic disable: missing-fields
require("nvim-lightbulb").setup {
autocmd = {
enabled = true,
updatetime = -1,
},
---@diagnostic disable-next-line: unused-local
filter = function(client_name, result)
-- Ruff always sends these two actions even if there are no action to take,
-- so it is better to just ignore this to avoid noise. See also discussion below:
-- https://github.com/astral-sh/ruff-lsp/issues/91
local ignored_kinds = { "source.fixAll.ruff", "source.organizeImports.ruff" }
if vim.tbl_contains(ignored_kinds, result.kind) then
return false
end
return true
end,
}

View file

@ -0,0 +1,9 @@
require("live-command").setup {
enable_highlighting = true,
inline_highlighting = true,
commands = {
Norm = { cmd = "norm" },
},
}
vim.cmd("cnoreabbrev norm Norm")

311
nvim/lua/config/lualine.lua Normal file
View file

@ -0,0 +1,311 @@
local utils = require("utils")
local fn = vim.fn
-- cache for git states
local git_status_cache = {
fetch_success = false,
behind_count = 0,
ahead_count = 0,
}
local on_exit_fetch = function(result)
if result.code == 0 then
git_status_cache.fetch_success = true
end
end
local function handle_numeric_result(cache_key)
return function(result)
if result.code == 0 then
git_status_cache[cache_key] = tonumber(result.stdout:match("(%d+)")) or 0
else
-- when the git command fails, it usually means there are some changes in your branch. For example, you
-- on branchA, for this one, you have upstream branch. Then you changed to branchB, and there is no upstream
-- branch, the git rev-list command will error out. In this case, we should clear the cache
-- vim.print("Error running git command", result)
git_status_cache[cache_key] = 0
end
end
end
local async_cmd = function(cmd_str, on_exit)
local cmd = vim.tbl_filter(function(element)
return element ~= ""
end, vim.split(cmd_str, " "))
vim.system(cmd, { text = true }, on_exit)
end
local async_git_status_update = function()
-- Fetch the latest changes from the remote repository (replace 'origin' if needed)
async_cmd("git fetch origin", on_exit_fetch)
if not git_status_cache.fetch_success then
return
end
-- Get the number of commits behind
-- the @{upstream} notation is inspired by post: https://www.reddit.com/r/neovim/comments/t48x5i/git_branch_aheadbehind_info_status_line_component/
-- note that here we should use double dots instead of triple dots
local behind_cmd_str = "git rev-list --count HEAD..@{upstream}"
async_cmd(behind_cmd_str, handle_numeric_result("behind_count"))
-- Get the number of commits ahead
local ahead_cmd_str = "git rev-list --count @{upstream}..HEAD"
async_cmd(ahead_cmd_str, handle_numeric_result("ahead_count"))
end
local function get_git_ahead_behind_info()
async_git_status_update()
local status = git_status_cache
if not status then
return ""
end
local msg = ""
if type(status.ahead_count) == "number" and status.ahead_count > 0 then
local ahead_str = string.format("↑[%d] ", status.ahead_count)
msg = msg .. ahead_str
end
if type(status.behind_count) == "number" and status.behind_count > 0 then
local behind_str = string.format("↓[%d] ", status.behind_count)
msg = msg .. behind_str
end
return msg
end
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()
local venv_name = utils.get_virtual_env()
if venv_name ~= "" then
return string.format(" (%s)", venv_name)
else
return ""
end
end
local get_active_lsp = function()
local msg = "🚫"
local buf_ft = vim.api.nvim_get_option_value("filetype", {})
local clients = vim.lsp.get_clients { bufnr = 0 }
if next(clients) == nil then
return msg
end
for _, client in ipairs(clients) do
---@diagnostic disable-next-line: undefined-field
local filetypes = client.config.filetypes
if filetypes and vim.fn.index(filetypes, buf_ft) ~= -1 then
return client.name
end
end
return msg
end
require("lualine").setup {
options = {
icons_enabled = true,
theme = "auto",
component_separators = { left = "|", right = "|" },
section_separators = "",
disabled_filetypes = {},
always_divide_middle = true,
refresh = {
statusline = 1000,
},
},
sections = {
lualine_a = {
{
"filename",
symbols = {
readonly = "[🔒]",
},
},
},
lualine_b = {
{
"branch",
fmt = function(name, _)
-- truncate branch name in case the name is too long
return string.sub(name, 1, 20)
end,
color = { gui = "italic,bold" },
},
{
get_git_ahead_behind_info,
color = { fg = "#E0C479" },
},
{
"diff",
source = diff,
},
{
virtual_env,
color = { fg = "black", bg = "#F1CA81" },
},
},
lualine_c = {
{
"%S",
color = { gui = "bold", fg = "cyan" },
},
{
spell,
color = { fg = "black", bg = "#a7c080" },
},
},
lualine_x = {
{
get_active_lsp,
icon = "📡",
},
{
"diagnostics",
sources = { "nvim_diagnostic" },
symbols = { error = "🆇 ", warn = "⚠️ ", info = " ", hint = "" },
},
{
trailing_space,
color = "WarningMsg",
},
{
mixed_indent,
color = "WarningMsg",
},
},
lualine_y = {
{
"encoding",
fmt = string.upper,
},
{
"fileformat",
symbols = {
unix = "unix",
dos = "win",
mac = "mac",
},
},
"filetype",
{
ime_state,
color = { fg = "black", bg = "#f46868" },
},
},
lualine_z = {
"location",
"progress",
},
},
inactive_sections = {
lualine_a = {},
lualine_b = {},
lualine_c = { "filename" },
lualine_x = { "location" },
lualine_y = {},
lualine_z = {},
},
tabline = {},
extensions = { "quickfix", "fugitive", "nvim-tree" },
}

View file

@ -0,0 +1,111 @@
-- Setup nvim-cmp.
local cmp = require("cmp")
-- The extentions needed by nvim-cmp should be loaded beforehand
require("cmp_nvim_lsp")
require("cmp_path")
require("cmp_buffer")
require("cmp_omni")
require("cmp_nvim_ultisnips")
require("cmp_cmdline")
local MiniIcons = require("mini.icons")
cmp.setup {
snippet = {
expand = function(args)
-- For `ultisnips` user.
vim.fn["UltiSnips#Anon"](args.body)
end,
},
mapping = cmp.mapping.preset.insert {
["<Tab>"] = function(fallback)
if cmp.visible() then
cmp.select_next_item()
else
fallback()
end
end,
["<S-Tab>"] = function(fallback)
if cmp.visible() then
cmp.select_prev_item()
else
fallback()
end
end,
["<CR>"] = cmp.mapping.confirm { select = true },
["<C-e>"] = cmp.mapping.abort(),
["<Esc>"] = cmp.mapping.close(),
["<C-d>"] = cmp.mapping.scroll_docs(-4),
["<C-f>"] = cmp.mapping.scroll_docs(4),
},
sources = {
{ name = "nvim_lsp" }, -- For nvim-lsp
{ name = "ultisnips" }, -- For ultisnips user.
{ name = "path" }, -- for path completion
{ name = "buffer", keyword_length = 2 }, -- for buffer word completion
},
completion = {
keyword_length = 1,
completeopt = "menu,noselect",
},
view = {
entries = "custom",
},
-- solution taken from https://github.com/echasnovski/mini.nvim/issues/1007#issuecomment-2258929830
formatting = {
format = function(_, vim_item)
local icon, hl = MiniIcons.get("lsp", vim_item.kind)
vim_item.kind = icon .. " " .. vim_item.kind
vim_item.kind_hl_group = hl
return vim_item
end,
},
}
cmp.setup.filetype("tex", {
sources = {
{ name = "omni" },
{ name = "ultisnips" }, -- For ultisnips user.
{ name = "buffer", keyword_length = 2 }, -- for buffer word completion
{ name = "path" }, -- for path completion
},
})
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" },
}),
matching = { disallow_symbol_nonprefix_matching = false },
})
-- see https://github.com/hrsh7th/nvim-cmp/wiki/Menu-Appearance#how-to-add-visual-studio-code-dark-theme-colors-to-the-menu
vim.cmd([[
highlight! link CmpItemMenu Comment
" gray
highlight! CmpItemAbbrDeprecated guibg=NONE gui=strikethrough guifg=#808080
" blue
highlight! CmpItemAbbrMatch guibg=NONE guifg=#569CD6
highlight! CmpItemAbbrMatchFuzzy guibg=NONE guifg=#569CD6
" light blue
highlight! CmpItemKindVariable guibg=NONE guifg=#9CDCFE
highlight! CmpItemKindInterface guibg=NONE guifg=#9CDCFE
highlight! CmpItemKindText guibg=NONE guifg=#9CDCFE
" pink
highlight! CmpItemKindFunction guibg=NONE guifg=#C586C0
highlight! CmpItemKindMethod guibg=NONE guifg=#C586C0
" front
highlight! CmpItemKindKeyword guibg=NONE guifg=#D4D4D4
highlight! CmpItemKindProperty guibg=NONE guifg=#D4D4D4
highlight! CmpItemKindUnit guibg=NONE guifg=#D4D4D4
]])

View file

@ -0,0 +1,16 @@
local nvim_lint = require("lint")
nvim_lint.linters_by_ft = {
-- to use vale, install it: `brew install vale`
-- configure it via: https://vale.sh/generator, put config in `.vale.ini` in project root.
-- to install cspell and typos: `brew install cspell typos-cli`
-- markdown = { "vale", "cspell", "typos" },
}
vim.api.nvim_create_autocmd({ "InsertLeave" }, {
callback = function()
-- try_lint without arguments runs the linters defined in `linters_by_ft`
-- for the current filetype
nvim_lint.try_lint()
end,
})

View file

@ -0,0 +1,10 @@
local nvim_notify = require("notify")
nvim_notify.setup {
-- Animation style
stages = "fade_in_slide_out",
-- Default timeout for notifications
timeout = 1500,
background_colour = "#2E3440",
}
vim.notify = nvim_notify

View file

@ -0,0 +1,23 @@
local builtin = require("statuscol.builtin")
local ffi = require("statuscol.ffidef")
local C = ffi.C
-- only show fold level up to this level
local fold_level_limit = 3
local function foldfunc(args)
local foldinfo = C.fold_info(args.wp, args.lnum)
if foldinfo.level > fold_level_limit then
return " "
end
return builtin.foldfunc(args)
end
require("statuscol").setup {
relculright = false,
segments = {
{ text = { "%s" }, click = "v:lua.ScSa" },
{ text = { builtin.lnumfunc, " " }, click = "v:lua.ScLa" },
{ text = { foldfunc, " " }, condition = { true, builtin.not_empty }, click = "v:lua.ScFa" },
},
}

View file

@ -0,0 +1,104 @@
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 = {},
},
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",
})

View file

@ -0,0 +1,28 @@
local keymap = vim.keymap
local hop = require("hop")
hop.setup {
case_insensitive = true,
char2_fallback_key = "<CR>",
quit_key = "<Esc>",
match_mappings = { "zh_sc" },
}
keymap.set({ "n", "v", "o" }, "f", "", {
silent = true,
noremap = true,
callback = function()
hop.hint_char2()
end,
desc = "nvim-hop char2",
})
vim.api.nvim_create_autocmd("ColorScheme", {
pattern = "*",
callback = function()
vim.cmd([[
hi HopNextKey cterm=bold ctermfg=176 gui=bold guibg=#ff00ff guifg=#ffffff
hi HopNextKey1 cterm=bold ctermfg=176 gui=bold guibg=#ff00ff guifg=#ffffff
hi HopNextKey2 cterm=bold ctermfg=176 gui=bold guibg=#ff00ff guifg=#ffffff
]])
end,
})

View file

@ -0,0 +1,45 @@
local handler = function(virtText, lnum, endLnum, width, truncate)
local newVirtText = {}
local foldedLines = endLnum - lnum
local suffix = (" 󰁂 %d"):format(foldedLines)
local sufWidth = vim.fn.strdisplaywidth(suffix)
local targetWidth = width - sufWidth
local curWidth = 0
for _, chunk in ipairs(virtText) do
local chunkText = chunk[1]
local chunkWidth = vim.fn.strdisplaywidth(chunkText)
if targetWidth > curWidth + chunkWidth then
table.insert(newVirtText, chunk)
else
chunkText = truncate(chunkText, targetWidth - curWidth)
local hlGroup = chunk[2]
table.insert(newVirtText, { chunkText, hlGroup })
chunkWidth = vim.fn.strdisplaywidth(chunkText)
-- str width returned from truncate() may less than 2nd argument, need padding
if curWidth + chunkWidth < targetWidth then
suffix = suffix .. (" "):rep(targetWidth - curWidth - chunkWidth)
end
break
end
curWidth = curWidth + chunkWidth
end
local rAlignAppndx =
math.max(math.min(vim.opt.textwidth["_value"], width - 1) - curWidth - sufWidth, 0)
suffix = (" "):rep(rAlignAppndx) .. suffix
table.insert(newVirtText, { suffix, "MoreMsg" })
return newVirtText
end
require("ufo").setup {
fold_virt_text_handler = handler,
}
vim.keymap.set("n", "zR", require("ufo").openAllFolds)
vim.keymap.set("n", "zM", require("ufo").closeAllFolds)
vim.keymap.set("n", "zr", require("ufo").openFoldsExceptKinds)
vim.keymap.set("n", "<leader>K", function()
local _ = require("ufo").peekFoldedLinesUnderCursor()
end, {
desc = "Preview folded maps",
})

View file

@ -0,0 +1,12 @@
require("snacks").setup {
-- more beautiful vim.ui.input
input = {
enabled = true,
win = {
relative = "cursor",
backdrop = true,
},
},
-- more beautiful vim.ui.select
picker = { enabled = true },
}

View file

@ -0,0 +1,50 @@
-- configuration
require("nvim-treesitter-textobjects").setup {
select = {
-- Automatically jump forward to textobj, similar to targets.vim
lookahead = true,
-- You can choose the select mode (default is charwise 'v')
--
-- Can also be a function which gets passed a table with the keys
-- * query_string: eg '@function.inner'
-- * method: eg 'v' or 'o'
-- and should return the mode ('v', 'V', or '<c-v>') or a table
-- mapping query_strings to modes.
selection_modes = {
["@function.inner"] = "V", -- linewise
["@function.outer"] = "V", -- linewise
["@class.outer"] = "V", -- linewise
["@class.inner"] = "V", -- linewise
["@parameter.outer"] = "v", -- charwise
},
-- If you set this to `true` (default is `false`) then any textobject is
-- extended to include preceding or succeeding whitespace. Succeeding
-- whitespace has priority in order to act similarly to eg the built-in
-- `ap`.
--
-- Can also be a function which gets passed a table with the keys
-- * query_string: eg '@function.inner'
-- * selection_mode: eg 'v'
-- and should return true of false
include_surrounding_whitespace = false,
},
}
-- keymaps
-- You can use the capture groups defined in `textobjects.scm`
vim.keymap.set({ "x", "o" }, "af", function()
require("nvim-treesitter-textobjects.select").select_textobject("@function.outer", "textobjects")
end)
vim.keymap.set({ "x", "o" }, "if", function()
require("nvim-treesitter-textobjects.select").select_textobject("@function.inner", "textobjects")
end)
vim.keymap.set({ "x", "o" }, "ac", function()
require("nvim-treesitter-textobjects.select").select_textobject("@class.outer", "textobjects")
end)
vim.keymap.set({ "x", "o" }, "ic", function()
require("nvim-treesitter-textobjects.select").select_textobject("@class.inner", "textobjects")
end)
-- You can also use captures from other query groups like `locals.scm`
vim.keymap.set({ "x", "o" }, "as", function()
require("nvim-treesitter-textobjects.select").select_textobject("@local.scope", "locals")
end)

View file

@ -0,0 +1,61 @@
-- a list of filetypes to install treesitter parsers and queries
local nvim_treesitter = require("nvim-treesitter")
local ensure_installed = {
"cpp",
"diff",
"go",
"gomod",
"gosum",
"javascript",
"json",
"lua",
"markdown",
"python",
"sh",
"toml",
"typescript",
"vim",
"yaml",
"zsh",
}
vim.api.nvim_create_autocmd("FileType", {
pattern = ensure_installed,
callback = function(args)
local ft = vim.bo[args.buf].filetype
local lang = vim.treesitter.language.get_lang(ft)
if lang == nil then
return
end
-- check if parser is available
local is_parser_available = vim.treesitter.language.add(lang)
if not is_parser_available then
local available_langs = vim.g.ts_available or nvim_treesitter.get_available()
if not vim.g.ts_available then
vim.g.ts_available = available_langs
end
if vim.tbl_contains(available_langs, lang) then
-- install treesitter parsers and queries
local install_msg = string.format("Installing parsers and queries for %s", lang)
vim.print(install_msg)
require("nvim-treesitter").install(lang)
end
end
if vim.treesitter.language.add(lang) then
-- start treesitter highlighting
vim.treesitter.start(args.buf, lang)
-- the following two statements will enable treesitter folding
-- vim.wo[0][0].foldexpr = "v:lua.vim.treesitter.foldexpr()"
-- vim.wo[0][0].foldmethod = "expr"
-- enable treesitter-based indentation
-- vim.bo.indentexpr = "v:lua.require'nvim-treesitter'.indentexpr()"
end
end,
})

View file

@ -0,0 +1,6 @@
require("which-key").setup {
preset = "modern",
icons = {
mappings = false,
},
}

17
nvim/lua/config/yanky.lua Normal file
View file

@ -0,0 +1,17 @@
require("yanky").setup {
preserve_cursor_position = {
enabled = false,
},
highlight = {
on_put = true,
on_yank = false,
timer = 300,
},
}
vim.keymap.set({ "n", "x" }, "p", "<Plug>(YankyPutAfter)")
vim.keymap.set({ "n", "x" }, "P", "<Plug>(YankyPutBefore)")
-- cycle through the yank history, only work after paste
vim.keymap.set("n", "[y", "<Plug>(YankyPreviousEntry)")
vim.keymap.set("n", "]y", "<Plug>(YankyNextEntry)")

308
nvim/lua/custom-autocmd.lua Normal file
View file

@ -0,0 +1,308 @@
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.hl.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,
---@diagnostic disable-next-line: unused-local
callback = function(context)
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
api.nvim_create_autocmd({ "VimEnter" }, { callback = open_nvim_tree })
-- Do not use smart case in command line mode, extracted from https://vi.stackexchange.com/a/16511/15292.
api.nvim_create_augroup("dynamic_smartcase", { clear = true })
api.nvim_create_autocmd("CmdLineEnter", {
group = "dynamic_smartcase",
pattern = ":",
callback = function()
vim.o.smartcase = false
end,
})
api.nvim_create_autocmd("CmdLineLeave", {
group = "dynamic_smartcase",
pattern = ":",
callback = function()
vim.o.smartcase = true
end,
})
api.nvim_create_autocmd("TermOpen", {
group = api.nvim_create_augroup("term_start", { clear = true }),
pattern = "*",
callback = function()
-- Do not use number and relative number for terminal inside nvim
vim.wo.relativenumber = false
vim.wo.number = false
-- Go to insert mode by default to start typing command
vim.cmd("startinsert")
end,
})
local number_toggle_group = api.nvim_create_augroup("numbertoggle", { clear = true })
api.nvim_create_autocmd({ "BufEnter", "FocusGained", "InsertLeave", "WinEnter" }, {
pattern = "*",
group = number_toggle_group,
desc = "togger line number",
callback = function()
if vim.wo.number then
vim.wo.relativenumber = true
end
end,
})
api.nvim_create_autocmd({ "BufLeave", "FocusLost", "InsertEnter", "WinLeave" }, {
group = number_toggle_group,
desc = "togger line number",
callback = function()
if vim.wo.number then
vim.wo.relativenumber = false
end
end,
})
api.nvim_create_autocmd("ColorScheme", {
group = api.nvim_create_augroup("custom_highlight", { clear = true }),
pattern = "*",
desc = "Define or overrride some highlight groups",
callback = function()
-- For yank highlight
vim.api.nvim_set_hl(
0,
"YankColor",
{ fg = "#34495E", bg = "#2ECC71", ctermfg = 59, ctermbg = 41 }
)
-- For cursor colors, see option guicursor for more info
vim.api.nvim_set_hl(0, "Cursor", { fg = "black", bg = "#00c918", update = true })
vim.api.nvim_set_hl(0, "Cursor2", { fg = "None", bg = "yellow", update = true })
-- For floating windows border highlight
vim.api.nvim_set_hl(0, "FloatBorder", { bg = "None", fg = "LightGreen", update = true })
-- change the background color of floating window to None, so it blenders better
vim.api.nvim_set_hl(0, "NormalFloat", { bg = "None", update = true })
-- this is the highlight used by nvim-cmp for cmdline completion window border
vim.api.nvim_set_hl(0, "Pmenu", { bg = "None", update = true })
-- highlight for matching parentheses
vim.api.nvim_set_hl(0, "MatchParen", { bold = true, underline = true, update = true })
vim.api.nvim_set_hl(0, "IlluminatedWordWrite", { reverse = true, update = true })
vim.api.nvim_set_hl(0, "IlluminatedWordRead", { reverse = true, update = true })
vim.api.nvim_set_hl(0, "IlluminatedWordText", { reverse = true, update = true })
end,
})
api.nvim_create_autocmd("BufEnter", {
pattern = "*",
group = api.nvim_create_augroup("auto_close_win", { clear = true }),
desc = "Quit Nvim if we have only one window, and its filetype match our pattern",
---@diagnostic disable-next-line: unused-local
callback = function(context)
local quit_filetypes = { "qf", "vista", "NvimTree" }
local should_quit = true
local tabwins = api.nvim_tabpage_list_wins(0)
for _, win in pairs(tabwins) do
local buf = api.nvim_win_get_buf(win)
local buf_type = vim.api.nvim_get_option_value("filetype", { buf = buf })
if not vim.tbl_contains(quit_filetypes, buf_type) then
should_quit = false
end
end
if should_quit then
vim.cmd("qall")
end
end,
})
api.nvim_create_autocmd({ "VimEnter", "DirChanged" }, {
group = api.nvim_create_augroup("git_repo_check", { clear = true }),
pattern = "*",
desc = "check if we are inside Git repo",
callback = function()
utils.inside_git_repo()
end,
})
-- ref: https://vi.stackexchange.com/a/169/15292
api.nvim_create_autocmd("BufReadPre", {
group = api.nvim_create_augroup("large_file", { clear = true }),
pattern = "*",
desc = "optimize for large file",
callback = function(ev)
local file_size_limit = 524288 -- 0.5MB
local f = ev.file
if fn.getfsize(f) > file_size_limit or fn.getfsize(f) == -2 then
vim.o.eventignore = "all"
-- show ruler
vim.o.ruler = true
-- turning off relative number helps a lot
vim.wo.relativenumber = false
vim.wo.number = false
vim.bo.swapfile = false
vim.bo.bufhidden = "unload"
vim.bo.undolevels = -1
end
end,
})
-- check if current file is formatted
local ft_to_command = {
python = { "black", "--check" },
lua = { "stylua", "--check" },
}
api.nvim_create_autocmd("BufWritePost", {
group = api.nvim_create_augroup("format check", { clear = true }),
pattern = { "*" },
desc = "Check if file needs reformat",
callback = function(ev)
local fpath = ev.file
local ft = vim.api.nvim_get_option_value("filetype", { buf = ev.buf })
local cmd_partial = ft_to_command[ft]
local cmd = nil
if cmd_partial ~= nil then
cmd = vim.deepcopy(cmd_partial)
table.insert(cmd, fpath)
else
return
end
local exe_path = cmd[1]
if not utils.executable(exe_path) then
vim.print(string.format("%s not found!", exe_path))
return
end
local result = vim.system(cmd, { text = true }):wait()
if result.code ~= 0 then
vim.notify("This file is not formatted!")
end
end,
})
api.nvim_create_autocmd({ "InsertLeave", "TextChanged" }, {
group = api.nvim_create_augroup("auto_save", { clear = true }),
pattern = { "*" },
desc = "Auto save current file",
callback = function(ev)
local is_readonly = vim.api.nvim_get_option_value("readonly", { buf = ev.buf })
local is_modifiable = vim.api.nvim_get_option_value("modifiable", { buf = ev.buf })
if not is_readonly and is_modifiable then
vim.cmd([[silent! update]])
end
end,
})

View file

@ -0,0 +1,69 @@
local diagnostic = vim.diagnostic
local api = vim.api
-- global config for diagnostic
diagnostic.config {
underline = false,
virtual_text = false,
virtual_lines = false,
signs = {
text = {
[diagnostic.severity.ERROR] = "🆇",
[diagnostic.severity.WARN] = "⚠️",
[diagnostic.severity.INFO] = "",
[diagnostic.severity.HINT] = "",
},
},
severity_sort = true,
float = {
source = true,
header = "Diagnostics:",
prefix = " ",
border = "single",
max_height = 10,
max_width = 130,
close_events = { "CursorMoved", "BufLeave", "WinLeave", "InsertEnter" },
},
}
-- set quickfix list from diagnostics in a certain buffer, not the whole workspace
local set_qflist = function(buf_num, severity)
local diagnostics = nil
diagnostics = diagnostic.get(buf_num, { severity = severity })
local qf_items = diagnostic.toqflist(diagnostics)
vim.fn.setqflist({}, " ", { title = "Diagnostics", items = qf_items })
-- open quickfix by default
vim.cmd([[copen]])
end
-- this puts diagnostics from opened files to quickfix
vim.keymap.set("n", "<space>qw", diagnostic.setqflist, { desc = "put window diagnostics to qf" })
-- this puts diagnostics from current buffer to quickfix
vim.keymap.set("n", "<space>qb", function()
set_qflist(0)
end, { desc = "put buffer diagnostics to qf" })
-- automatically show diagnostic in float win for current line
api.nvim_create_autocmd("CursorHold", {
pattern = "*",
callback = function()
if #vim.diagnostic.get(0) == 0 then
return
end
if not vim.b.diagnostics_pos then
vim.b.diagnostics_pos = { nil, nil }
end
local cursor_pos = api.nvim_win_get_cursor(0)
if not vim.deep_equal(cursor_pos, vim.b.diagnostics_pos) then
diagnostic.open_float {}
end
vim.b.diagnostics_pos = cursor_pos
end,
})

29
nvim/lua/dotenv.lua Normal file
View file

@ -0,0 +1,29 @@
-- code from https://github.com/mcjkula/lua-dotenv/blob/main/env.lua
local M = {}
local env_vars = {}
function M.get(key, default)
local value = env_vars[key] or os.getenv(key)
return value or default
end
function M.load_dotenv(file_path)
file_path = file_path or os.getenv("HOME") .. "/.config/.env"
local file, _ = io.open(file_path, "r")
if not file then
return
end
local content = file:read("*all")
file:close()
for line in content:gmatch("[^\r\n]+") do
local key, value = line:match("^([%w_]+)%s*=%s*(.+)$")
if key and value then
value = value:gsub("^[\"'](.-)[\"']$", "%1")
env_vars[key] = value
end
end
end
return M

59
nvim/lua/globals.lua Normal file
View file

@ -0,0 +1,59 @@
local utils = require("utils")
------------------------------------------------------------------------
-- custom variables --
------------------------------------------------------------------------
vim.g.is_win = (utils.has("win32") or utils.has("win64")) and true or false
vim.g.is_linux = (utils.has("unix") and (not utils.has("macunix"))) and true or false
vim.g.is_mac = utils.has("macunix") and true or false
vim.g.logging_level = vim.log.levels.INFO
------------------------------------------------------------------------
-- builtin variables --
------------------------------------------------------------------------
vim.g.loaded_perl_provider = 0 -- Disable perl provider
vim.g.loaded_ruby_provider = 0 -- Disable ruby provider
vim.g.loaded_node_provider = 0 -- Disable node provider
vim.g.did_install_default_menus = 1 -- do not load menu
-- Custom mapping <leader> (see `:h mapleader` for more info)
vim.g.mapleader = ","
-- Enable highlighting for lua HERE doc inside vim script
vim.g.vimsyn_embed = "l"
-- Use English as main language
vim.cmd([[language fr_FR.UTF-8]])
-- Disable loading certain plugins
-- Whether to load netrw by default, see https://github.com/bling/dotvim/issues/4
vim.g.loaded_netrw = 1
vim.g.loaded_netrwPlugin = 1
vim.g.netrw_liststyle = 3
if vim.g.is_win then
vim.g.netrw_http_cmd = "curl --ssl-no-revoke -Lo"
end
-- Do not load tohtml.vim
vim.g.loaded_2html_plugin = 1
-- Do not load zipPlugin.vim, gzip.vim and tarPlugin.vim (all these plugins are
-- related to checking files inside compressed files)
vim.g.loaded_zipPlugin = 1
vim.g.loaded_gzip = 1
vim.g.loaded_tarPlugin = 1
-- Do not load the tutor plugin
vim.g.loaded_tutor_mode_plugin = 1
-- Do not use builtin matchit.vim and matchparen.vim since we use vim-matchup
vim.g.loaded_matchit = 1
vim.g.loaded_matchparen = 1
-- Disable sql omni completion, it is broken.
vim.g.loaded_sql_completion = 1
-- control how to show health check window
vim.g.health = { style = nil }

210
nvim/lua/lsp_conf.lua Normal file
View file

@ -0,0 +1,210 @@
local utils = require("utils")
vim.api.nvim_create_autocmd("LspAttach", {
group = vim.api.nvim_create_augroup("lsp_buf_conf", { clear = true }),
callback = function(event_context)
local client = vim.lsp.get_client_by_id(event_context.data.client_id)
-- vim.print(client.name, client.server_capabilities)
if not client then
return
end
local bufnr = event_context.buf
-- Mappings.
local map = function(mode, l, r, opts)
opts = opts or {}
opts.silent = true
opts.buffer = bufnr
vim.keymap.set(mode, l, r, opts)
end
map("n", "gd", function()
vim.lsp.buf.definition {
on_list = function(options)
-- custom logic to avoid showing multiple definition when you use this style of code:
-- `local M.my_fn_name = function() ... end`.
-- See also post here: https://www.reddit.com/r/neovim/comments/19cvgtp/any_way_to_remove_redundant_definition_in_lua_file/
-- vim.print(options.items)
local unique_defs = {}
local def_loc_hash = {}
-- each item in options.items contain the location info for a definition provided by LSP server
for _, def_location in pairs(options.items) do
-- use filename and line number to uniquelly indentify a definition,
-- we do not expect/want multiple definition in single line!
local hash_key = def_location.filename .. def_location.lnum
if not def_loc_hash[hash_key] then
def_loc_hash[hash_key] = true
table.insert(unique_defs, def_location)
end
end
options.items = unique_defs
-- set the location list
---@diagnostic disable-next-line: param-type-mismatch
vim.fn.setloclist(0, {}, " ", options)
-- open the location list when we have more than 1 definitions found,
-- otherwise, jump directly to the definition
if #options.items > 1 then
vim.cmd.lopen()
else
vim.cmd([[silent! lfirst]])
end
end,
}
end, { desc = "go to definition" })
map("n", "<C-]>", vim.lsp.buf.definition)
map("n", "K", function()
vim.lsp.buf.hover {
border = "single",
max_height = 20,
max_width = 130,
close_events = { "CursorMoved", "BufLeave", "WinLeave", "LSPDetach" },
}
end)
map("n", "<C-k>", vim.lsp.buf.signature_help)
map("n", "<space>rn", vim.lsp.buf.rename, { desc = "varialbe rename" })
map("n", "<space>ca", vim.lsp.buf.code_action, { desc = "LSP code action" })
map("n", "<space>wa", vim.lsp.buf.add_workspace_folder, { desc = "add workspace folder" })
map("n", "<space>wr", vim.lsp.buf.remove_workspace_folder, { desc = "remove workspace folder" })
map("n", "<space>wl", function()
vim.print(vim.lsp.buf.list_workspace_folders())
end, { desc = "list workspace folder" })
-- Set some key bindings conditional on server capabilities
-- Disable ruff hover feature in favor of Pyright
if client.name == "ruff" then
client.server_capabilities.hoverProvider = false
end
end,
nested = true,
desc = "Configure buffer keymap and behavior based on LSP",
})
-- Enable lsp servers when they are available
local capabilities = require("lsp_utils").get_default_capabilities()
-- `*` will set default config for all lsp
vim.lsp.config("*", {
capabilities = capabilities,
flags = {
debounce_text_changes = 500,
},
})
vim.lsp.config("basedpyright", {
capabilities = capabilities,
offset_encoding = "utf-16",
settings = {
python = {
analysis = {
typeCheckingMode = "strict",
},
},
},
})
vim.lsp.config("ruff", {
capabilities = capabilities,
offset_encoding = "utf-8",
})
-- A mapping from lsp server name to the executable name
local enabled_lsp_servers = {
basedpyright = "basedpyright-langserver",
ruff = "ruff",
lua_ls = "lua-language-server",
-- clangd = "clangd",
vimls = "vim-language-server",
bashls = "bash-language-server",
yamlls = "yaml-language-server",
gopls = "gopls",
-- the server can be install via homebrew: brew install golangci-lint-langserver
-- golangci-lint also needs to be installed: https://github.com/golangci/golangci-lint
golangci_lint_ls = "golangci-lint-langserver",
-- to install codebook, run `brew install codebook-lsp`
-- codebook = "codebook-lsp"
}
for server_name, lsp_executable in pairs(enabled_lsp_servers) do
if utils.executable(lsp_executable) then
vim.lsp.enable(server_name)
else
local msg = string.format(
"Executable '%s' for server '%s' not found! Server will not be enabled",
lsp_executable,
server_name
)
vim.notify(msg, vim.log.levels.WARN, { title = "Nvim-config" })
end
end
-- LSP related command
vim.api.nvim_create_user_command("LspInfo", "checkhealth vim.lsp", {
desc = "Show LSP Info",
})
vim.api.nvim_create_user_command("LspLog", function(_)
local log_path = vim.lsp.log.get_filename()
vim.cmd(string.format("edit %s", log_path))
end, {
desc = "Show LSP log",
})
vim.api.nvim_create_user_command("LspRestart", "lsp restart", {
desc = "Restart LSP",
})
--- show LSP progress (works on Ghostty)
vim.api.nvim_create_autocmd("LspProgress", {
callback = function(ev)
local value = ev.data.params.value
vim.api.nvim_echo({ { value.message or "done" } }, false, {
id = "lsp." .. ev.data.client_id,
kind = "progress",
source = "vim.lsp",
title = value.title,
status = value.kind ~= "end" and "running" or "success",
percent = value.percentage,
})
end,
})
-- this controls the LSP inlayHints behavior
vim.g.lsp_inlay_hint_enabled = false
local update_inlayhint = function(enable)
-- Some LSP server supports inlay hint, but disable this feature by default, so you may need to
-- enable inlay hint in the LSP server config.
vim.lsp.inlay_hint.enable(enable)
end
vim.api.nvim_create_user_command("LspInlayHints", function(context)
-- vim.print("context", context)
if context["args"] == "enable" then
vim.g.lsp_inlay_hint_enabled = true
end
if context["args"] == "disable" then
vim.g.lsp_inlay_hint_enabled = false
end
update_inlayhint(vim.g.lsp_inlay_hint_enabled)
end, {
bang = false,
nargs = 1,
force = true,
desc = "Toggle LSP inlayHints",
complete = function()
return { "enable", "disable" }
end,
})

39
nvim/lua/lsp_utils.lua Normal file
View file

@ -0,0 +1,39 @@
local M = {}
M.get_default_capabilities = function()
local capabilities = vim.lsp.protocol.make_client_capabilities()
-- nvim-cmp integration (IMPORTANT)
local ok, cmp_nvim_lsp = pcall(require, "cmp_nvim_lsp")
if ok then
capabilities = cmp_nvim_lsp.default_capabilities(capabilities)
end
-- required by nvim-ufo (tu l'as déjà, on garde)
capabilities.textDocument.foldingRange = {
dynamicRegistration = false,
lineFoldingOnly = true,
}
-- améliore Pyright / Python completion
capabilities.textDocument.completion.completionItem = {
snippetSupport = true,
preselectSupport = true,
insertReplaceSupport = true,
labelDetailsSupport = true,
deprecatedSupport = true,
commitCharactersSupport = true,
tagSupport = { valueSet = { 1 } },
resolveSupport = {
properties = {
"documentation",
"detail",
"additionalTextEdits",
},
},
}
return capabilities
end
return M

276
nvim/lua/mappings.lua Normal file
View file

@ -0,0 +1,276 @@
local keymap = vim.keymap
local uv = vim.uv
-- 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" })
-- 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", [[\db]], "<cmd>bprevious <bar> bdelete #<cr>", {
silent = true,
desc = "Delete current buffer",
})
keymap.set("n", [[\dB]], function()
local buf_ids = vim.api.nvim_list_bufs()
local cur_buf = vim.api.nvim_win_get_buf(0)
for _, buf_id in pairs(buf_ids) do
-- do not Delete unlisted buffers, which may lead to unexpected errors
if vim.api.nvim_get_option_value("buflisted", { buf = buf_id }) and buf_id ~= cur_buf then
vim.api.nvim_buf_delete(buf_id, { force = true })
end
end
end, {
desc = "Delete other buffers",
})
keymap.set("n", [[\dt]], "<cmd>tabclose<CR>", {
silent = true,
desc = "Delete current tab",
})
keymap.set("n", [[\dT]], "<cmd>tabonly<CR>", {
silent = true,
desc = "Delete other tabs",
})
-- 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")
-- Restart nvim
keymap.set("n", "<leader>sv", function()
vim.print("Use ZR to restart nvim instead!")
end)
keymap.set("n", "ZR", function()
local current_buf_path = vim.fn.expand("%")
local restart_cmd = string.format("restart edit %s", current_buf_path)
vim.cmd(restart_cmd)
end, {
silent = true,
desc = "Restart nvim",
})
-- 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" }
)
-- Copy entire buffer.
keymap.set("n", "<leader>y", "<cmd>%yank<cr>", { desc = "yank entire buffer" })
-- Toggle terminal
keymap.set(
"n",
"<leader>t",
"<cmd>botright 10split | terminal<cr>",
{ desc = "open a terminal" }
)
-- 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()
if timer == nil then
return
end
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, { desc = "show cursor" })
keymap.set("n", "q", function()
vim.print("q is remapped to Q in Normal mode!")
end)
keymap.set("n", "Q", "q", {
desc = "Record macro",
})
keymap.set("n", "<Esc>", function()
vim.cmd("fclose!")
end, {
desc = "close floating win",
})

249
nvim/lua/options.lua Normal file
View file

@ -0,0 +1,249 @@
local utils = require("utils")
local fn = vim.fn
local opt = vim.opt
local o = vim.o
-- Change fillchars for folding, vertical split, end of buffer, and message separator
opt.fillchars = {
fold = " ",
foldsep = " ",
foldopen = "",
foldclose = "",
vert = "",
eob = " ",
msgsep = "",
diff = "",
}
-- Split window below/right when creating horizontal/vertical windows
opt.splitbelow = true
opt.splitright = true
-- Avoid the flickering when splitting window horizontal
opt.splitkeep = "screen"
-- Time in milliseconds to wait for a mapped sequence to complete,
-- see https://unix.stackexchange.com/q/36882/221410 for more info
opt.timeoutlen = 500
opt.updatetime = 500 -- For CursorHold events
-- Clipboard settings, always use clipboard for all delete, yank, change, put
-- operation, see https://stackoverflow.com/q/30691466/6064933
if fn["provider#clipboard#Executable"]() ~= "" then
opt.clipboard:append("unnamedplus")
end
-- Disable creating swapfiles, see https://stackoverflow.com/q/821902/6064933
opt.swapfile = false
-- Ignore certain files and folders when globbing
opt.wildignore:append {
"*.o",
"*.obj",
"*.dylib",
"*.bin",
"*.dll",
"*.exe",
"*/.git/*",
"*/.svn/*",
"*/__pycache__/*",
"*/build/**",
"*.jpg",
"*.png",
"*.jpeg",
"*.bmp",
"*.gif",
"*.tiff",
"*.svg",
"*.ico",
"*.pyc",
"*.pkl",
"*.DS_Store",
"*.aux",
"*.bbl",
"*.blg",
"*.brf",
"*.fls",
"*.fdb_latexmk",
"*.synctex.gz",
"*.xdv",
}
opt.wildignorecase = true -- Ignore file and dir name cases in cmd-completion
-- Set up backup directory
vim.g.backupdir = fn.stdpath("data") .. "/backup//"
opt.backupdir = vim.g.backupdir
-- Skip backup for patterns in option wildignore
opt.backupskip = o.wildignore
opt.backup = true -- Create backup for files
opt.backupcopy = "yes" -- Copy the original file to backupdir and overwrite it
-- General tab settings
opt.tabstop = 2 -- Number of visual spaces per TAB
opt.softtabstop = 2 -- Number of spaces in tab when editing
opt.shiftwidth = 2 -- Number of spaces to use for autoindent
opt.expandtab = true -- Expand tab to spaces so that tabs are spaces
-- Set matching pairs of characters and highlight matching brackets
opt.matchpairs:append {
"<:>",
"「:」",
"『:』",
"【:】",
'":"',
"':'",
"《:》",
}
-- Show line number and relative line number
opt.number = true
opt.relativenumber = true
-- Ignore case in general, but become case-sensitive when uppercase is present
opt.ignorecase = true
opt.smartcase = true
-- File and script encoding settings for vim
opt.fileencoding = "utf-8"
opt.fileencodings = { "ucs-bom", "utf-8", "cp936", "gb18030", "big5", "euc-jp", "euc-kr", "latin1" }
-- Break line at predefined characters
opt.linebreak = true
-- Character to show before the lines that have been soft-wrapped
opt.showbreak = ""
-- List all matches and complete till longest common string
opt.wildmode = "list:longest"
-- Minimum lines to keep above and below cursor when scrolling
opt.scrolloff = 999
-- Use mouse to select and resize windows, etc.
opt.mouse = "n"
opt.mousemodel = "popup" -- Set the behaviour of mouse
opt.mousescroll = { "ver:1", "hor:0" }
-- Disable showing current mode on command line since statusline plugins can show it.
opt.showmode = false
opt.fileformats = { "unix", "dos" } -- Fileformats to use for new files
-- Ask for confirmation when handling unsaved or read-only files
opt.confirm = true
opt.visualbell = true
opt.errorbells = false -- Do not use visual and errorbells
opt.history = 500 -- The number of command and search history to keep
-- Use list mode and customized listchars
opt.list = true
opt.listchars = {
tab = "",
extends = "",
precedes = "",
nbsp = "",
}
-- Auto-write the file based on some condition
opt.autowrite = true
opt.autowriteall = true
-- Auto reload file if changed outside nvim
opt.autoread = true
-- Show hostname, full path of file and last-mod time on the window title.
-- The meaning of the format str for strftime can be found in
-- http://man7.org/linux/man-pages/man3/strftime.3.html. The function to get
-- lastmod time is drawn from https://stackoverflow.com/q/8426736/6064933
opt.title = true
o.titlestring = "%{v:lua.require('utils').get_titlestr()}"
-- Persistent undo even after you close a file and re-open it
opt.undofile = true
-- Do not show "match xx of xx" and other messages during auto-completion
-- Do not show search match count on bottom right (seriously, I would strain my
-- neck looking at it). Using plugins like vim-anzu or nvim-hlslens is a better
-- choice, IMHO.
opt.shortmess:append("cS")
opt.messagesopt = "hit-enter,history:500"
-- Completion behaviour
opt.completeopt:append("menuone") -- Show menu even if there is only one item
opt.completeopt:remove("preview") -- Disable the preview window
opt.pumheight = 10 -- Maximum number of items to show in popup menu
opt.pumblend = 5 -- Pseudo transparency for completion menu
opt.pumborder = "single"
opt.winblend = 0 -- Pseudo transparency for floating window
opt.winborder = "single"
-- Insert mode key word completion setting
opt.complete:append("kspell")
opt.complete:remove { "w", "b", "u", "t" }
opt.spelllang = { "en", "cjk" } -- Spell languages
opt.spellsuggest:append("9") -- Show 9 spell suggestions at most
-- Align indent to next multiple value of shiftwidth. For its meaning,
-- see http://vim.1045645.n5.nabble.com/shiftround-option-td5712100.html
opt.shiftround = true
opt.virtualedit = "block" -- Virtual edit is useful for visual block edit
-- Correctly break multi-byte characters such as CJK,
-- see https://stackoverflow.com/q/32669814/6064933
opt.formatoptions:append("mM")
-- Tilde (~) is an operator, thus must be followed by motions like `e` or `w`.
opt.tildeop = true
opt.synmaxcol = 250 -- Text after this column number is not highlighted
opt.startofline = false
-- External program to use for grep command
if utils.executable("rg") then
opt.grepprg = "rg --vimgrep --no-heading --smart-case"
opt.grepformat = "%f:%l:%c:%m"
end
-- Enable true color support. Do not set this option if your terminal does not
-- support true colors! For a comprehensive list of terminals supporting true
-- colors, see https://github.com/termstandard/colors and https://gist.github.com/XVilka/8346728.
opt.termguicolors = true
-- Set up cursor color and shape in various mode, ref:
-- https://neovim.io/doc/user/faq/#_how-to-change-cursor-color-in-the-terminal%3f
opt.guicursor =
"n-v:block-Cursor/lCursor,i-c-ci-ve:ver50-blinkwait50-blinkoff100-blinkon175-Cursor2/lCursor2,r-cr:hor20,o:hor20"
opt.signcolumn = "yes:1"
opt.colorcolumn = "100"
-- Remove certain character from file name pattern matching
opt.isfname:remove { "=", "," }
-- diff options
opt.diffopt = {
"vertical", -- Show diff in vertical position
"filler", -- Show filler for deleted lines
"closeoff", -- Turn off diff when one file window is closed
"context:3", -- Context for diff
"internal",
"indent-heuristic",
"algorithm:histogram",
}
-- inline diff makes changes in a line more noticeable, the author suggests to
-- remove linematch option if you use inline option, see also
-- https://www.reddit.com/r/neovim/comments/1myfvla/comment/najy4s3/
opt.diffopt:append("inline:char")
opt.wrap = false -- Do not wrap
opt.ruler = false
opt.showcmdloc = "statusline"

842
nvim/lua/plugin_specs.lua Normal file
View file

@ -0,0 +1,842 @@
local utils = require("utils")
local plugin_dir = vim.fs.joinpath(vim.fn.stdpath("data"), "lazy")
local lazypath = vim.fs.joinpath(plugin_dir, "lazy.nvim")
if not vim.uv.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/cmp-nvim-lsp", lazy = true },
{ "hrsh7th/cmp-path", lazy = true },
{ "hrsh7th/cmp-buffer", lazy = true },
{ "hrsh7th/cmp-omni", lazy = true },
{ "hrsh7th/cmp-cmdline", lazy = true },
{ "quangnguyen30192/cmp-nvim-ultisnips", lazy = true },
{
"hrsh7th/nvim-cmp",
name = "nvim-cmp",
event = "VeryLazy",
config = function()
require("config.nvim-cmp")
end,
},
-- {
-- "saghen/blink.cmp",
-- -- optional: provides snippets for the snippet source
-- dependencies = {
-- "rafamadriz/friendly-snippets",
-- "archie-judd/blink-cmp-words",
-- },
-- -- use a release tag to download pre-built binaries
-- version = "1.*",
-- config = function()
-- require("config.blink-cmp")
-- end,
-- opts_extend = { "sources.default" },
-- },
{
"neovim/nvim-lspconfig",
},
{
"dnlhc/glance.nvim",
config = function()
require("config.glance")
end,
event = "VeryLazy",
},
{
"nvim-treesitter/nvim-treesitter",
lazy = false,
build = ":TSUpdate",
branch = "main",
config = function()
require("config.treesitter")
end,
},
{
"nvim-treesitter/nvim-treesitter-textobjects",
event = "VeryLazy",
branch = "main",
init = function()
-- Disable entire built-in ftplugin mappings to avoid conflicts.
-- See https://github.com/neovim/neovim/tree/master/runtime/ftplugin for built-in ftplugins.
vim.g.no_plugin_maps = true
-- Or, disable per filetype (add as you like)
-- vim.g.no_python_maps = true
-- vim.g.no_ruby_maps = true
-- vim.g.no_rust_maps = true
-- vim.g.no_go_maps = true
end,
config = function()
require("config.treesitter-textobjects")
end,
},
{
"mizlan/iswap.nvim",
event = "VeryLazy",
config = function()
require("config.iswap")
end,
},
-- Super fast buffer jump
{
"smoka7/hop.nvim",
keys = { "f" },
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,
},
{
"nvim-telescope/telescope.nvim",
cmd = "Telescope",
dependencies = {
"nvim-telescope/telescope-symbols.nvim",
},
},
{
"ibhagwan/fzf-lua",
config = function()
require("config.fzf-lua")
end,
event = "VeryLazy",
},
{
"MeanderingProgrammer/render-markdown.nvim",
main = "render-markdown",
opts = {},
ft = { "markdown" },
},
-- 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 = false,
priority = 1000,
name = "everforest",
config = function()
vim.g.everforest_enable_italic = true
vim.g.everforest_background = "hard"
vim.cmd("colorscheme everforest")
end,
},
{ "EdenEast/nightfox.nvim", lazy = true },
{ "catppuccin/nvim", name = "catppuccin", lazy = true },
{ "olimorris/onedarkpro.nvim", lazy = true },
{ "marko-cerovac/material.nvim", lazy = true },
{
"rockyzhang24/arctic.nvim",
dependencies = { "rktjmp/lush.nvim" },
name = "arctic",
branch = "v2",
},
{ "rebelot/kanagawa.nvim", lazy = true },
{ "miikanissi/modus-themes.nvim", priority = 1000 },
{ "wtfox/jellybeans.nvim", priority = 1000 },
{ "projekt0n/github-nvim-theme", name = "github-theme" },
{ "ficcdaf/ashen.nvim", priority = 1000 },
{ "savq/melange-nvim", priority = 1000 },
{ "Skardyy/makurai-nvim", priority = 1000 },
{ "vague2k/vague.nvim", priority = 1000 },
{ "webhooked/kanso.nvim", priority = 1000 },
{ "zootedb0t/citruszest.nvim", priority = 1000 },
{ "nyoom-engineering/oxocarbon.nvim", priority = 1000 },
{ "ember-theme/nvim", name = "ember", priority = 1000 },
{ "yonatanperel/lake-dweller.nvim", priority = 1000 },
{ "dchinmay2/alabaster.nvim", priority = 1000 },
{ "jpwol/thorn.nvim", priority = 1000 },
-- plugins to provide nerdfont icons
{
"nvim-mini/mini.icons",
version = false,
config = function()
-- this is the compatibility fix for plugins that only support nvim-web-devicons
require("mini.icons").mock_nvim_web_devicons()
require("mini.icons").tweak_lsp_kind()
end,
lazy = true,
},
{
"nvim-lualine/lualine.nvim",
event = "BufRead",
cond = firenvim_not_active,
config = function()
require("config.lualine")
end,
},
{
"akinsho/bufferline.nvim",
event = { "BufEnter" },
cond = firenvim_not_active,
config = function()
require("config.bufferline")
end,
},
{
"nvim-mini/mini.indentscope",
version = false,
config = function()
local mini_indent = require("mini.indentscope")
mini_indent.setup {
draw = {
animation = mini_indent.gen_animation.none(),
},
symbol = "",
}
end,
},
{
"luukvbaal/statuscol.nvim",
opts = {},
config = function()
require("config.nvim-statuscol")
end,
},
{
"kevinhwang91/nvim-ufo",
dependencies = "kevinhwang91/promise-async",
event = "VeryLazy",
opts = {},
init = function()
vim.o.foldcolumn = "1" -- '0' is not bad
vim.o.foldlevel = 99 -- Using ufo provider need a large value, feel free to decrease the value
vim.o.foldlevelstart = 99
vim.o.foldenable = true
end,
config = function()
require("config.nvim_ufo")
end,
},
-- Highlight URLs inside vim
{ "itchyny/vim-highlighturl", event = "BufReadPost" },
-- notification plugin
{
"rcarriga/nvim-notify",
event = "VeryLazy",
config = function()
require("config.nvim-notify")
end,
},
{ "nvim-lua/plenary.nvim", lazy = true },
-- 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.
{
"chrishrb/gx.nvim",
keys = { { "gx", "<cmd>Browse<cr>", mode = { "n", "x" } } },
cmd = { "Browse" },
init = function()
vim.g.netrw_nogx = 1 -- disable netrw gx
end,
enabled = function()
return vim.g.is_win or vim.g.is_mac
end,
config = function()
require("config.gx")
end,
submodules = false, -- not needed, submodules are required only for tests
},
-- Only install these plugins if ctags are installed on the system
-- show file tags in vim window
{
"liuchengxu/vista.vim",
init = function()
vim.cmd([[
let g:vista#renderer#icons = {
\ 'member': '',
\ }
" Do not echo message on command line
let g:vista_echo_cursor = 0
" Stay in current window when vista window is opened
let g:vista_stay_on_open = 0
nnoremap <silent> <Space>t :<C-U>Vista!!<CR>
]])
end,
enabled = function()
return utils.executable("ctags")
end,
cmd = "Vista",
},
-- Snippet engine and snippet template
{
"SirVer/ultisnips",
init = function()
vim.cmd([[
" Trigger configuration. Do not use <tab> if you use YouCompleteMe
let g:UltiSnipsExpandTrigger='<c-j>'
" Do not look for SnipMate snippets
let g:UltiSnipsEnableSnipMate = 0
" Shortcut to jump forward and backward in tabstop positions
let g:UltiSnipsJumpForwardTrigger='<c-j>'
let g:UltiSnipsJumpBackwardTrigger='<c-k>'
" Configuration for custom snippets directory, see
" https://jdhao.github.io/2019/04/17/neovim_snippet_s1/ for details.
let g:UltiSnipsSnippetDirectories=['UltiSnips', 'my_snippets']
]])
end,
event = "VeryLazy",
},
-- Automatic insertion and deletion of a pair of characters
{
"windwp/nvim-autopairs",
event = "InsertEnter",
config = true,
},
-- Comment plugin
{
"tpope/vim-commentary",
keys = {
{ "gc", mode = "n" },
{ "gc", mode = "v" },
},
},
-- Multiple cursor plugin like Sublime Text?
-- 'mg979/vim-visual-multi'
-- Manage your yank history
{
"gbprod/yanky.nvim",
config = function()
require("config.yanky")
end,
cmd = "YankyRingHistory",
},
-- 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",
init = function()
vim.cmd([[
let g:better_escape_interval = 200
]])
end,
event = { "InsertEnter" },
},
{
"lyokha/vim-xkbswitch",
init = function()
vim.cmd([[
let g:XkbSwitchEnabled = 1
]])
end,
enabled = function()
return vim.g.is_mac and utils.executable("xkbswitch")
end,
event = { "InsertEnter" },
},
{
"Neur1n/neuims",
enabled = function()
return vim.g.is_win
end,
event = { "InsertEnter" },
},
-- Git command inside vim
{
"tpope/vim-fugitive",
event = "User InGitRepo",
config = function()
require("config.fugitive")
end,
},
{
"NeogitOrg/neogit",
dependencies = {
"nvim-lua/plenary.nvim", -- required
"sindrets/diffview.nvim", -- optional - Diff integration
-- Only one of these is needed.
"ibhagwan/fzf-lua", -- optional
},
event = "User InGitRepo",
},
-- Better git log display
{ "rbong/vim-flog", cmd = { "Flog" } },
{
"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,
event = "BufRead",
version = "*",
},
{
"sindrets/diffview.nvim",
cmd = { "DiffviewOpen" },
config = function()
require("config.diffview")
end,
},
{
"barrettruth/diffs.nvim",
init = function()
vim.g.diffs = {
integrations = {
fugitive = true,
neogit = true,
gitsigns = true,
},
}
end,
},
{
"esmuellert/codediff.nvim",
cmd = "CodeDiff",
},
{
"kevinhwang91/nvim-bqf",
ft = "qf",
config = function()
require("config.bqf")
end,
},
-- Faster footnote generation
{ "vim-pandoc/vim-markdownfootnotes", ft = { "markdown" } },
-- Vim tabular plugin for manipulate tabular, required by markdown plugins
{ "godlygeek/tabular", ft = { "markdown" } },
{
"chrisbra/unicode.vim",
init = function()
vim.cmd([[
nmap ga <Plug>(UnicodeGA)
]])
end,
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",
init = function()
vim.cmd([[
" Map s to nop since s in used by vim-sandwich. Use cl instead of s.
nmap s <Nop>
omap s <Nop>
]])
end,
event = "VeryLazy",
},
-- Only use these plugin on Windows and Mac and when LaTeX is installed
{
"lervag/vimtex",
init = function()
vim.cmd([[
if executable('latex')
" Hacks for inverse search to work semi-automatically,
" see https://jdhao.github.io/2021/02/20/inverse_search_setup_neovim_vimtex/.
function! s:write_server_name() abort
let nvim_server_file = (has('win32') ? $TEMP : '/tmp') . '/vimtexserver.txt'
call writefile([v:servername], nvim_server_file)
endfunction
augroup vimtex_common
autocmd!
autocmd FileType tex call s:write_server_name()
autocmd FileType tex nmap <buffer> <F9> <plug>(vimtex-compile)
augroup END
let g:vimtex_compiler_latexmk = {
\ 'build_dir' : 'build',
\ }
" TOC settings
let g:vimtex_toc_config = {
\ 'name' : 'TOC',
\ 'layers' : ['content', 'todo', 'include'],
\ 'resize' : 1,
\ 'split_width' : 30,
\ 'todo_sorted' : 0,
\ 'show_help' : 1,
\ 'show_numbers' : 1,
\ 'mode' : 2,
\ }
" Viewer settings for different platforms
if g:is_win
let g:vimtex_view_general_viewer = 'SumatraPDF'
let g:vimtex_view_general_options = '-reuse-instance -forward-search @tex @line @pdf'
endif
if g:is_mac
" let g:vimtex_view_method = "skim"
let g:vimtex_view_general_viewer = '/Applications/Skim.app/Contents/SharedSupport/displayline'
let g:vimtex_view_general_options = '-r @line @pdf @tex'
augroup vimtex_mac
autocmd!
autocmd User VimtexEventCompileSuccess call UpdateSkim()
augroup END
" The following code is adapted from https://gist.github.com/skulumani/7ea00478c63193a832a6d3f2e661a536.
function! UpdateSkim() abort
let l:out = b:vimtex.out()
let l:src_file_path = expand('%:p')
let l:cmd = [g:vimtex_view_general_viewer, '-r']
if !empty(system('pgrep Skim'))
call extend(l:cmd, ['-g'])
endif
call jobstart(l:cmd + [line('.'), l:out, l:src_file_path])
endfunction
endif
endif
]])
end,
enabled = function()
return utils.executable("latex")
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()
return utils.executable("tmux")
end,
ft = { "tmux" },
},
-- Modern matchit implementation
{
"andymass/vim-matchup",
init = function()
vim.cmd([[
" Improve performance
let g:matchup_matchparen_deferred = 1
let g:matchup_matchparen_timeout = 100
let g:matchup_matchparen_insert_timeout = 30
" Enhanced matching with matchup plugin
let g:matchup_override_vimtex = 1
" Whether to enable matching inside comment or string
let g:matchup_delim_noskips = 0
" Show offscreen match pair in popup window
let g:matchup_matchparen_offscreen = {'method': 'popup'}
]])
end,
event = "BufRead",
},
{ "tpope/vim-scriptease", cmd = { "Scriptnames", "Messages", "Verbose" } },
-- Asynchronous command execution
{
"skywind3000/asyncrun.vim",
init = function()
vim.cmd([[
" Automatically open quickfix window of 6 line tall after asyncrun starts
let g:asyncrun_open = 6
if g:is_win
" Command output encoding for Windows
let g:asyncrun_encs = 'gbk'
endif
]])
end,
cmd = { "AsyncRun" },
},
{ "cespare/vim-toml", ft = { "toml" }, branch = "main" },
-- Edit text area in browser using nvim
{
"glacambre/firenvim",
init = function()
vim.cmd([[
""""""""""""""""""""""""""""""firenvim settings""""""""""""""""""""""""""""""
if exists('g:started_by_firenvim') && g:started_by_firenvim
if g:is_mac
set guifont=Iosevka\ Nerd\ Font:h18
else
set guifont=Consolas
endif
" general config for firenvim
let g:firenvim_config = {
\ 'globalSettings': {
\ 'alt': 'all',
\ },
\ 'localSettings': {
\ '.*': {
\ 'cmdline': 'neovim',
\ 'priority': 0,
\ 'selector': 'textarea',
\ 'takeover': 'never',
\ },
\ }
\ }
function s:setup_firenvim() abort
set signcolumn=no
set noruler
set noshowcmd
set laststatus=0
set showtabline=0
endfunction
augroup firenvim
autocmd!
autocmd BufEnter * call s:setup_firenvim()
autocmd BufEnter sqlzoo*.txt set filetype=sql
autocmd BufEnter github.com_*.txt set filetype=markdown
autocmd BufEnter stackoverflow.com_*.txt set filetype=markdown
augroup END
endif
]])
end,
enabled = function()
return vim.g.is_win or vim.g.is_mac
end,
-- it seems that we can only call the firenvim function directly.
-- Using vim.fn or vim.cmd to call this function will fail.
build = function()
local firenvim_path = vim.fs.joinpath(plugin_dir, "firenvim")
vim.opt.runtimepath:append(firenvim_path)
vim.cmd("runtime! firenvim.vim")
-- macOS will reset the PATH when firenvim starts a nvim process, causing the PATH variable to change unexpectedly.
-- Here we are trying to get the correct PATH and use it for firenvim.
-- See also https://github.com/glacambre/firenvim/blob/master/TROUBLESHOOTING.md#make-sure-firenvims-path-is-the-same-as-neovims
local path_env = vim.env.PATH
local prologue = string.format('export PATH="%s"', path_env)
-- local prologue = "echo"
local cmd_str = string.format(":call firenvim#install(0, '%s')", prologue)
vim.cmd(cmd_str)
end,
},
-- Session management plugin
{ "tpope/vim-obsession", cmd = "Obsession" },
{
"ojroques/vim-oscyank",
enabled = function()
return vim.g.is_linux
end,
cmd = { "OSCYank", "OSCYankReg" },
},
-- showing keybindings
{
"folke/which-key.nvim",
event = "VeryLazy",
config = function()
require("config.which-key")
end,
},
{
"folke/snacks.nvim",
priority = 1000,
lazy = false,
opts = {},
config = function()
require("config.snacks")
end,
},
-- show and trim trailing whitespaces
{ "nvim-zh/whitespace.nvim", event = "VeryLazy" },
-- file explorer
{
"nvim-tree/nvim-tree.lua",
keys = { "<space>s" },
config = function()
require("config.nvim-tree")
end,
},
{
"j-hui/fidget.nvim",
event = "BufRead",
config = function()
require("config.fidget-nvim")
end,
},
{
"folke/lazydev.nvim",
ft = "lua", -- only load on lua files
opts = {
library = {
-- See the configuration section for more details
-- Load luvit types when the `vim.uv` word is found
{ path = "${3rd}/luv/library", words = { "vim%.uv" } },
{ path = "nvim-lspconfig", words = { "lspconfig" } },
},
},
},
{
"smjonas/live-command.nvim",
-- live-command supports semantic versioning via Git tags
-- tag = "2.*",
event = "VeryLazy",
config = function()
require("config.live-command")
end,
},
{
-- show hint for code actions, the user can also implement code actions themselves,
-- see discussion here: https://github.com/neovim/neovim/issues/14869
"kosayoda/nvim-lightbulb",
config = function()
require("config.lightbulb")
end,
event = "LspAttach",
},
{
"Bekaboo/dropbar.nvim",
event = "VeryLazy",
},
{
"catgoose/nvim-colorizer.lua",
event = "BufReadPre",
opts = { -- set to setup table
},
},
{
"stevearc/quicker.nvim",
event = "FileType qf",
---@module "quicker"
---@type quicker.SetupOptions
opts = {
max_filename_width = function()
return math.floor(math.min(40, vim.o.columns / 2))
end,
},
},
{
"nickjvandyke/opencode.nvim",
config = function()
---@module "opencode"
---@type opencode.Opts
vim.g.opencode_opts = {
-- Your configuration, if any — see `lua/opencode/config.lua`, or "goto definition" on the type or field.
}
-- Required for `opts.events.reload`.
vim.o.autoread = true
end,
},
{
"RRethy/vim-illuminate",
event = "VeryLazy",
config = function()
require("illuminate").configure {
filetypes_denylist = {},
filetypes_allowlist = {
"lua",
"python",
"sh",
"yaml",
"json",
"toml",
},
min_count_to_highlight = 2,
}
end,
},
{
"mfussenegger/nvim-lint",
config = function()
require("config.nvim-lint")
end,
},
}
require("lazy").setup {
spec = plugin_specs,
ui = {
border = "rounded",
title = "Plugin Manager",
title_pos = "center",
},
rocks = {
enabled = false,
hererocks = false,
},
}
-- Use short names for common plugin manager commands to simplify typing.
-- To use these shortcuts: first activate command line with `:`, then input the
-- short alias, e.g., `pi`, then press <space>, the alias will be expanded to
-- the full command automatically.
vim.fn["utils#Cabbrev"]("pi", "Lazy install")
vim.fn["utils#Cabbrev"]("pud", "Lazy update")
vim.fn["utils#Cabbrev"]("pc", "Lazy clean")
vim.fn["utils#Cabbrev"]("ps", "Lazy sync")

162
nvim/lua/ui.lua Normal file
View file

@ -0,0 +1,162 @@
--- This module will load a random colorscheme on nvim startup process.
local utils = require("utils")
local M = {}
local use_theme = function(name)
local ok, err = pcall(vim.cmd.colorscheme, name)
if not ok then
vim.notify(
string.format("Failed to load colorscheme %s, err: %s", name, err),
vim.log.levels.WARN
)
vim.cmd.colorscheme("default")
end
end
-- 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()
-- Lua
require("onedark").setup {
style = "darker",
}
require("onedark").load()
end,
edge = function()
vim.g.edge_style = "default"
vim.g.edge_enable_italic = 1
vim.g.edge_better_performance = 1
use_theme("edge")
end,
sonokai = function()
vim.g.sonokai_enable_italic = 1
vim.g.sonokai_better_performance = 1
use_theme("sonokai")
end,
gruvbox_material = function()
-- foreground option can be material, mix, or original
vim.g.gruvbox_material_foreground = "original"
--background option can be hard, medium, soft
vim.g.gruvbox_material_background = "hard"
vim.g.gruvbox_material_enable_italic = 1
vim.g.gruvbox_material_better_performance = 1
use_theme("gruvbox-material")
end,
everforest = function()
vim.g.everforest_background = "hard"
vim.g.everforest_enable_italic = 1
vim.g.everforest_better_performance = 1
use_theme("everforest")
end,
nightfox = function()
use_theme("carbonfox")
end,
onedarkpro = function()
-- set colorscheme after options
-- onedark_vivid does not enough contrast
use_theme("onedark_dark")
end,
material = function()
vim.g.material_style = "darker"
use_theme("material")
end,
arctic = function()
use_theme("arctic")
end,
kanagawa = function()
use_theme("kanagawa-dragon")
end,
modus = function()
use_theme("modus")
end,
jellybeans = function()
use_theme("jellybeans")
end,
github = function()
use_theme("github_dark_default")
end,
ashen = function()
use_theme("ashen")
end,
melange = function()
use_theme("melange")
end,
makurai = function()
use_theme("makurai_dark")
end,
vague = function()
use_theme("vague")
end,
kanso = function()
use_theme("kanso")
end,
citruszest = function()
use_theme("citruszest")
end,
oxocarbon = function()
use_theme("oxocarbon")
end,
ember = function()
use_theme("ember")
end,
lake_dweller = function()
require("lake-dweller").setup {
-- "lake-dweller", "pond-dweller", or "ocean-dweller"
variant = "lake-dweller",
}
use_theme("lake-dweller")
end,
alabaster = function()
use_theme("alabaster")
end,
thorn = function()
use_theme("thorn")
end,
}
--- Fixe the colorscheme
vim.opt.termguicolors = true
vim.api.nvim_create_autocmd("VimEnter", {
callback = function()
vim.g.everforest_background = "medium"
vim.o.background = "light"
vim.g.everforest_light_background = true
vim.g.everforest_enable_italic = 1
vim.cmd("colorscheme everforest")
end,
})
-- enable the experiment UI
require("vim._core.ui2").enable {
enable = true,
msg = { -- Options related to the message module.
targets = {
[""] = "cmd",
empty = "msg",
},
cmd = { -- Options related to messages in the cmdline window.
height = 0.2, -- Maximum height while expanded for messages beyond 'cmdheight'.
},
dialog = { -- Options related to dialog window.
height = 0.2, -- Maximum height.
},
msg = { -- Options related to msg window.
height = 0.2, -- Maximum height.
timeout = 1000, -- Time a message is visible in the message window.
},
pager = { -- Options related to message window.
height = 0.3, -- Maximum height.
},
},
}
return M

134
nvim/lua/utils.lua Normal file
View file

@ -0,0 +1,134 @@
local fn = vim.fn
local version = vim.version
local M = {}
--- Check if an executable exists
--- @param name string An executable name/path
--- @return boolean
function M.executable(name)
return fn.executable(name) > 0
end
--- check whether a feature exists in Nvim
--- @param feat string the feature name, like `nvim-0.7` or `unix`.
--- @return boolean
function M.has(feat)
if fn.has(feat) == 1 then
return true
end
return false
end
--- Create a dir if it does not exist
function M.may_create_dir(dir)
local res = fn.isdirectory(dir)
if res == 0 then
fn.mkdir(dir, "p")
end
end
--- Generate random integers in the range [Low, High], inclusive,
--- adapted from https://stackoverflow.com/a/12739441/6064933
--- @param low integer the lower value for this range
--- @param high integer the upper value for this range
--- @return integer
function M.rand_int(low, high)
-- Use lua to generate random int, see also: https://stackoverflow.com/a/20157671/6064933
math.randomseed(os.time())
return math.random(low, high)
end
--- Select a random element from a sequence/list.
--- @param seq any[] the sequence to choose an element
function M.rand_element(seq)
local idx = M.rand_int(1, #seq)
return seq[idx]
end
--- check if the current nvim version is compatible with the allowed version
--- @param expected_version string
--- @return boolean
function M.is_compatible_version(expected_version)
-- check if we have the latest stable version of nvim
local expect_ver = version.parse(expected_version)
local actual_ver = vim.version()
if expect_ver == nil then
local msg = string.format("Unsupported version string: %s", expected_version)
vim.api.nvim_echo({ { msg } }, true, { err = true })
return false
end
local result = version.cmp(expect_ver, actual_ver)
if result ~= 0 then
local _ver = string.format("%s.%s.%s", actual_ver.major, actual_ver.minor, actual_ver.patch)
local msg = string.format(
"Expect nvim version %s, but your current nvim version is %s. Use at your own risk!",
expected_version,
_ver
)
vim.api.nvim_echo({ { msg } }, true, { err = true })
end
return true
end
--- check if we are inside a git repo
--- @return boolean
function M.inside_git_repo()
local result = vim.system({ "git", "rev-parse", "--is-inside-work-tree" }, { text = true }):wait()
if result.code ~= 0 then
return false
end
-- Manually trigger a special user autocmd InGitRepo (used lazyloading.
vim.cmd([[doautocmd User InGitRepo]])
return true
end
--- Get custom title string for the window title.
--- Shows hostname (on Linux), buffer path, and last modified time.
--- @return string
function M.get_titlestr()
local title_str = ""
if vim.g.is_linux then
title_str = vim.fn.hostname() .. " "
end
local buf_path = vim.fn.expand("%:p:~")
title_str = title_str .. buf_path .. " "
if vim.bo.buflisted and buf_path ~= "" then
local mod_time = vim.fn.strftime("%Y-%m-%d %H:%M:%S%z", vim.fn.getftime(vim.fn.expand("%")))
title_str = title_str .. mod_time
end
return title_str
end
--- Get the current virtual env
--- @return string
function M.get_virtual_env()
local conda_env = os.getenv("CONDA_DEFAULT_ENV")
-- venv_path is the complete path to the virtual env
local venv_path = os.getenv("VIRTUAL_ENV")
local venv_name = ""
if venv_path == nil then
if conda_env ~= nil then
venv_name = conda_env
end
else
venv_name = vim.fn.fnamemodify(venv_path, ":t")
end
return venv_name
end
return M

View file

@ -0,0 +1,7 @@
snippet "(?<!\w)ltx" "LaTeX symbol" r
LaTeX
endsnippet
snippet arw "Right-pointed arrow"
--> $1
endsnippet

View file

@ -0,0 +1,187 @@
snippet bare "barebone code template"
#include <iostream>
#include <vector>
#include <string>
#include <map>
#include <unordered_map>
#include <set>
#include <unordered_set>
#include <stack>
#include <queue>
#include <numeric>
using std::cout;
using std::endl;
using std::vector;
using std::string;
using std::map;
using std::unordered_map;
using std::set;
using std::unordered_set;
using std::stack;
using std::queue;
using std::pair;
using std::make_pair;
int main()
{
return 0;
}
endsnippet
snippet icd "#include directive" b
#include <$1>
$0
endsnippet
snippet plist "print vector" w
template <class T>
void printList(const T& arr, const string& desc){
std::cout << desc << ": [";
for (auto it = arr.begin(); it != arr.end(); it++){
std::cout << *it << ((std::next(it) != arr.end()) ? ", " : "");
}
std::cout << "]\n";
}
endsnippet
snippet pmat "print list of list" w
template <class T>
void printMat(const vector<vector<T>>& mat, const string& desc){
cout << desc << ": " << endl;
for (auto it1 = mat.begin(); it1 != mat.end(); it1++){
auto cur_vec = *it1;
cout << "[";
for (auto it2 = cur_vec.begin(); it2 != cur_vec.end(); it2++){
cout << *it2 << ((std::next(it2) != cur_vec.end()) ? ", " : "]\n");
}
}
}
endsnippet
snippet pqueue "print queue"
template <class T>
void printQueue(T q){
while(!q.empty()){
std::cout << q.top() << " ";
q.pop();
}
std::cout << '\n';
}
endsnippet
snippet cout "print a variable" w
cout << "$1: " << $2 << endl;
endsnippet
snippet random "Generate a random list" b
// Generate a random sequence of length len, in range(low, high) (inclusive).
// need to #include<random>
vector<int> genRandom(int low, int high, int len){
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<int> distribution(low, high);
vector<int> arr(len, 0);
for (int i = 0; i != len; ++i){
arr[i] = distribution(gen);
}
return arr;
}
endsnippet
snippet incset "Use set" b
#include <set>
using std::set;
endsnippet
snippet incmap "Use map" b
#include <map>
using std::map;
endsnippet
snippet incqueue "Use queue" b
#include <queue>
using std::queue;
endsnippet
snippet incstr "Use string" b
#include <string>
using std::string;
endsnippet
snippet incvec "Use vector" b
#include <vector>
using std::vector;
endsnippet
snippet incstack "Use stack" b
#include <stack>
using std::stack;
endsnippet
snippet vec "std::vector" w
vector<$1> ${2:vec}
endsnippet
snippet map "std::map" w
map<$1, $2> ${3:mymap}
endsnippet
snippet umap "std::unordered_map"
unordered_map<$1, $2> ${3:mymap}
endsnippet
snippet set "std::set" w
set<$1> ${2:myset}
endsnippet
snippet uset "std::unordered_set" w
unordered_set<$1> ${2:myset}
endsnippet
snippet queue "std::queue" w
queue<$1> ${2:q}
endsnippet
snippet stack "std::stack" w
stack<$1> ${2:mystack}
endsnippet
snippet sol "solution" w
auto solution = Solution();
$0
endsnippet
snippet for "for loop" w
for ($1; $2; $3){
$4
}
endsnippet
snippet if "if condition" w
if ($1){
$2
}
$0
endsnippet
snippet ifelse "if else condition"
if ($1){
$2
}else{
}
endsnippet

View file

@ -0,0 +1,11 @@
snippet main "func main" b
func main() {
$1
}
endsnippet
snippet func "create a function" b
func $1($2) $3 {
$4
}
endsnippet

View file

@ -0,0 +1,172 @@
global !p
def gen_header(snip):
placeholders_string = snip.buffer[snip.line].strip()
level = int(placeholders_string[0])
# erase current line
snip.buffer[snip.line] = ""
line_content = "#"*level + " ${1:Section Name}"
line_content += '\n$0'
snip.expand_anon(line_content)
endglobal
snippet "(k1|kbd)" "HTML kbd tag" rw
<kbd>${1:KEY}</kbd>$0
endsnippet
snippet k2 "Two key strokes shortcut"
<kbd>${1:KEY}</kbd> + <kbd>${2:KEY}</kbd>
endsnippet
snippet k3 "Three key strokes shortcut"
<kbd>${1:KEY}</kbd> + <kbd>${2:KEY}</kbd> + <kbd>${3:KEY}</kbd>
endsnippet
snippet meta "Markdown front matter (YAML format)" b
---
title: "$1"
date: `!p from datetime import datetime
if not snip.c:
snip.rv=datetime.now().astimezone().strftime("%Y-%m-%d %H:%M:%S%z")`
tags: [$2]
categories: [$3]
---
$0
endsnippet
snippet more "HTML more tag"
<!--more-->
endsnippet
snippet img "Aligned image using HTML tag"
<p align="center">
<img src="${1:URL}" width="${2:800}">
</p>
$0
endsnippet
snippet font "HTML font tag"
<font color="${1:blue}">${2:TEXT}</font>
endsnippet
snippet link "Markdown links"
[$1]($2)$0
endsnippet
snippet rlink "Markdown ref link"
[${1:link_text}][${2:label}]
endsnippet
post_jump "gen_header(snip)"
snippet "h([1-6])" "Markdown header" br
`!p snip.rv = match.group(1)`
endsnippet
snippet detail "Clickable details" b
<details>
<summary><font size="2" color="red">${1:Click to show the code.}</font></summary>
$2
</details>
endsnippet
snippet yh "直角引号" w
「$1」
endsnippet
snippet info "info box"
<style type="text/css">
@import url('//maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css');
.info-msg {
color: #059;
background-color: #BEF;
margin: 5px 0;
margin-bottom: 20px;
padding: 10px;
border-radius: 5px 5px 5px 5px;
border: 2px solid transparent;
border-color: transparent;
}
</style>
<div class="info-msg">
<i class="fa fa-info-circle"> Info</i></br>
${1:info text}
</div>
$0
endsnippet
snippet warn "warning box"
<style type="text/css">
@import url('//maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css');
.warning-msg {
color: #9F6000;
background-color: #FEEFB3;
margin: 5px 0;
margin-bottom: 20px;
padding: 10px;
border-radius: 5px 5px 5px 5px;
border: 2px solid transparent;
border-color: transparent;
}
</style>
<div class="warning-msg">
<i class="fa fa-warning"> Warning</i></br>
${1:warning text}
</div>
$0
endsnippet
snippet error "error box"
<style type="text/css">
@import url('//maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css');
.error-msg {
color: #D8000C;
background-color: #FFBABA;
margin: 5px 0;
margin-bottom: 20px;
padding: 10px;
border-radius: 5px 5px 5px 5px;
border: 2px solid transparent;
border-color: transparent;
}
</style>
<div class="error-msg">
<i class="fa fa-times-circle"> Error</i></br>
${1:error text}
</div>
$0
endsnippet
snippet success "success box"
<style type="text/css">
@import url('//maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css');
.success-msg {
color: #270;
background-color: #DFF2BF;
margin: 5px 0;
margin-bottom: 20px;
padding: 10px;
border-radius: 5px 5px 5px 5px;
border: 2px solid transparent;
border-color: transparent;
}
</style>
<div class="success-msg">
<i class="fa fa-check"></i>
${1:success text}
</div>
$0
endsnippet
snippet td "too long do not read" bw
tl;dr: $1
endsnippet

View file

@ -0,0 +1,30 @@
snippet head "Python source file header" b
"""
Description: $1
Author: Jie-dong Hao (jdhao@hotmail.com)
Created: `!v strftime("%Y-%m-%d %H:%M:%S%z")`
"""
$0
endsnippet
snippet print "Print value of some variable"
print("$1".format($2))
$0
endsnippet
snippet impa "import FOO as BAR" b
import ${1:FOO} as ${2:BAR}
endsnippet
snippet main "Main function boilerplate" b
def main():
$0
if __name__ == "__main__":
main()
endsnippet
snippet sol "solution" b
solution = Solution()
endsnippet

View file

@ -0,0 +1,6 @@
# copied from https://github.com/honza/vim-snippets/blob/master/UltiSnips/snippets.snippets
snippet snip "Ultisnips snippet definition" b
`!p snip.rv = "snippet"` ${1:Tab_trigger} "${2:Description}" ${3:b}
${0:${VISUAL}}
`!p snip.rv = "endsnippet"`
endsnippet

View file

@ -0,0 +1,9 @@
snippet use "usepackage" b
\usepackage{${1:package}}
endsnippet
snippet eqa "equation environment" b
\begin{equation}\label{$1}
$2
\end{equation}
endsnippet

View file

@ -0,0 +1,14 @@
snippet fun "vim function"
function! ${1:MyFunc}(${2}) abort
$3
endfunction
$0
endsnippet
snippet aug "vim augroup" b
augroup ${1:GROUP_NAME}
autocmd!
autocmd ${2:EVENT} ${3:PATTERN} $4
augroup END
$0
endsnippet

3
nvim/plugin/abbrev.vim Normal file
View file

@ -0,0 +1,3 @@
" some abbreviations
iabbrev reqire require
iabbrev serveral several

74
nvim/plugin/command.lua Normal file
View file

@ -0,0 +1,74 @@
local utils = require("utils")
-- Copy file path to clipboard
vim.api.nvim_create_user_command("CopyPath", function(context)
local full_path = vim.fn.glob("%:p")
local file_path = nil
if context["args"] == "nameonly" then
file_path = vim.fn.fnamemodify(full_path, ":t")
end
-- get the file path relative to project root
if context["args"] == "relative" then
local project_marker = { ".git", "pyproject.toml" }
local project_root = vim.fs.root(0, project_marker)
if project_root == nil then
vim.print("can not find project root")
return
end
file_path = vim.fn.substitute(full_path, project_root, "<project-root>", "g")
end
if context["args"] == "absolute" then
file_path = full_path
end
vim.fn.setreg("+", file_path)
vim.print("Filepath copied to clipboard!")
end, {
bang = false,
nargs = 1,
force = true,
desc = "Copy current file path to clipboard",
complete = function()
return { "nameonly", "relative", "absolute" }
end,
})
-- JSON format part of or the whole file
vim.api.nvim_create_user_command("JSONFormat", function(context)
local range = context["range"]
local line1 = context["line1"]
local line2 = context["line2"]
local python_name = nil
if utils.executable("python3") then
python_name = "python3"
elseif utils.executable("python") then
python_name = "python"
else
vim.print("No python command found")
return
end
if range == 0 then
-- the command is invoked without range, then we assume whole buffer
local cmd_str = string.format("%s,%s!%s -m json.tool", line1, line2, python_name)
vim.fn.execute(cmd_str)
elseif range == 2 then
-- the command is invoked with some range
-- for this to work, the mapping has to call this command with `:JSONFormat`,
-- <cmd>JSONFormat won't work, the range can not be passed with `<cmd>`.
-- See also: https://www.reddit.com/r/neovim/comments/17xxehz/how_to_correctly_get_line_ranges_from_command/
local cmd_str = string.format("%s,%s!%s -m json.tool", line1, line2, python_name)
vim.fn.execute(cmd_str)
else
local msg = string.format("unsupported range: %s", range)
vim.api.nvim_echo({ { msg } }, true, { err = true })
end
end, {
desc = "Format JSON string",
range = "%",
})

47
nvim/plugin/command.vim Normal file
View file

@ -0,0 +1,47 @@
" Capture output from a command to register @m, to paste, press "mp
command! -nargs=1 -complete=command Redir call utils#CaptureCommandOutput(<q-args>)
command! -bar -bang -nargs=+ -complete=file Edit call utils#MultiEdit([<f-args>])
call utils#Cabbrev('edit', 'Edit')
call utils#Cabbrev('man', 'Man')
" show current date and time in human readable format
command! -nargs=? Datetime echo utils#iso_time(<q-args>)
" Convert Markdown file to PDF
command! ToPDF call s:md_to_pdf()
function! s:md_to_pdf() abort
" check if pandoc is installed
if executable('pandoc') != 1
echoerr "pandoc not found"
return
endif
let l:md_path = expand("%:p")
let l:pdf_path = fnamemodify(l:md_path, ":r") .. ".pdf"
let l:header_path = stdpath('config') . '/resources/head.tex'
let l:cmd = "pandoc --pdf-engine=xelatex --highlight-style=zenburn --table-of-content " .
\ "--include-in-header=" . l:header_path . " -V fontsize=10pt -V colorlinks -V toccolor=NavyBlue " .
\ "-V linkcolor=red -V urlcolor=teal -V filecolor=magenta -s " .
\ l:md_path . " -o " . l:pdf_path
if g:is_mac
let l:cmd = l:cmd . '&& open ' . l:pdf_path
endif
if g:is_win
let l:cmd = l:cmd . '&& start ' . l:pdf_path
endif
" echomsg l:cmd
let l:id = jobstart(l:cmd)
if l:id == 0 || l:id == -1
echoerr "Error running command"
endif
endfunction

View file

@ -0,0 +1,119 @@
command! LogAutocmds call s:log_autocmds_toggle()
function! s:log_autocmds_toggle()
augroup LogAutocmd
autocmd!
augroup END
let l:date = strftime('%F', localtime())
let s:activate = get(s:, 'activate', 0) ? 0 : 1
if !s:activate
call s:log('Stopped autocmd log (' . l:date . ')')
return
endif
call s:log('Started autocmd log (' . l:date . ')')
augroup LogAutocmd
for l:au in s:aulist
silent execute 'autocmd' l:au '* call s:log(''' . l:au . ''')'
endfor
augroup END
endfunction
function! s:log(message)
silent execute '!echo "'
\ . strftime('%T', localtime()) . ' - ' . a:message . '"'
\ '>> /tmp/vim_log_autocommands'
endfunction
" These are deliberately left out due to side effects
" - SourceCmd
" - FileAppendCmd
" - FileWriteCmd
" - BufWriteCmd
" - FileReadCmd
" - BufReadCmd
" - FuncUndefined
let s:aulist = [
\ 'BufNewFile',
\ 'BufReadPre',
\ 'BufRead',
\ 'BufReadPost',
\ 'FileReadPre',
\ 'FileReadPost',
\ 'FilterReadPre',
\ 'FilterReadPost',
\ 'StdinReadPre',
\ 'StdinReadPost',
\ 'BufWrite',
\ 'BufWritePre',
\ 'BufWritePost',
\ 'FileWritePre',
\ 'FileWritePost',
\ 'FileAppendPre',
\ 'FileAppendPost',
\ 'FilterWritePre',
\ 'FilterWritePost',
\ 'BufAdd',
\ 'BufCreate',
\ 'BufDelete',
\ 'BufWipeout',
\ 'BufFilePre',
\ 'BufFilePost',
\ 'BufEnter',
\ 'BufLeave',
\ 'BufWinEnter',
\ 'BufWinLeave',
\ 'BufUnload',
\ 'BufHidden',
\ 'BufNew',
\ 'SwapExists',
\ 'FileType',
\ 'Syntax',
\ 'EncodingChanged',
\ 'TermChanged',
\ 'VimEnter',
\ 'GUIEnter',
\ 'GUIFailed',
\ 'TermResponse',
\ 'QuitPre',
\ 'VimLeavePre',
\ 'VimLeave',
\ 'FileChangedShell',
\ 'FileChangedShellPost',
\ 'FileChangedRO',
\ 'ShellCmdPost',
\ 'ShellFilterPost',
\ 'CmdUndefined',
\ 'SpellFileMissing',
\ 'SourcePre',
\ 'VimResized',
\ 'FocusGained',
\ 'FocusLost',
\ 'CursorHold',
\ 'CursorHoldI',
\ 'CursorMoved',
\ 'CursorMovedI',
\ 'WinEnter',
\ 'WinLeave',
\ 'TabEnter',
\ 'TabLeave',
\ 'CmdwinEnter',
\ 'CmdwinLeave',
\ 'InsertEnter',
\ 'InsertChange',
\ 'InsertLeave',
\ 'InsertCharPre',
\ 'TextChanged',
\ 'TextChangedI',
\ 'ColorScheme',
\ 'RemoteReply',
\ 'QuickFixCmdPre',
\ 'QuickFixCmdPost',
\ 'SessionLoadPost',
\ 'MenuPopup',
\ 'CompleteDone',
\ 'User',
\ ]

78
nvim/resources/head.tex Normal file
View file

@ -0,0 +1,78 @@
\usepackage{fancyvrb,newverbs}
\usepackage[top=2cm, bottom=1.5cm, left=2cm, right=2cm]{geometry}
\PassOptionsToPackage{dvipsnames,svgnames,x11names}{xcolor}
\usepackage{hyperref}
% begin a new page for each section (first level header),
% we need to combine this with `-V subparagraph` when invoking pandc
% \usepackage{titlesec}
% \newcommand{\sectionbreak}{\clearpage}
% change background color for inline code in markdown files.
% The following code does not work well for long text, because the text will exceed the page boundary.
\definecolor{bgcolor}{HTML}{DADADA}
\let\oldtexttt\texttt
\renewcommand{\texttt}[1]{
\colorbox{bgcolor}{\oldtexttt{#1}}
}
% change style of quote, see also https://tex.stackexchange.com/a/436253/114857
\usepackage[most]{tcolorbox}
\definecolor{linequote}{RGB}{224,215,188}
\definecolor{backquote}{RGB}{249,245,233}
\definecolor{bordercolor}{RGB}{221,221,221}
% change left border: https://tex.stackexchange.com/a/475716/114857
% change left margin: https://tex.stackexchange.com/a/457936/114857
\newtcolorbox{myquote}[1][]{%
enhanced,
breakable,
size=minimal,
left=10pt,
top=5pt,
bottom=5pt,
frame hidden,
boxrule=0pt,
sharp corners=all,
colback=backquote,
borderline west={2pt}{0pt}{bordercolor},
#1
}
% redefine quote environment to use the myquote environment, see https://tex.stackexchange.com/a/337587/114857
\renewenvironment{quote}{\begin{myquote}}{\end{myquote}}
% start a new page after toc, we need to save the old command before defining
% new one to avoid recursive command calls,
% see https://tex.stackexchange.com/questions/47351/can-i-redefine-a-command-to-contain-itself
\let\oldtoc\tableofcontents
\renewcommand{\tableofcontents}{\oldtoc\newpage}
% fix header level issue
\usepackage{enumitem}
\setlistdepth{9}
\setlist[itemize,1]{label=$\bullet$}
\setlist[itemize,2]{label=$\bullet$}
\setlist[itemize,3]{label=$\bullet$}
\setlist[itemize,4]{label=$\bullet$}
\setlist[itemize,5]{label=$\bullet$}
\setlist[itemize,6]{label=$\bullet$}
\setlist[itemize,7]{label=$\bullet$}
\setlist[itemize,8]{label=$\bullet$}
\setlist[itemize,9]{label=$\bullet$}
\renewlist{itemize}{itemize}{9}
\setlist[enumerate,1]{label=$\arabic*.$}
\setlist[enumerate,2]{label=$\alph*.$}
\setlist[enumerate,3]{label=$\roman*.$}
\setlist[enumerate,4]{label=$\arabic*.$}
\setlist[enumerate,5]{label=$\alpha*$}
\setlist[enumerate,6]{label=$\roman*.$}
\setlist[enumerate,7]{label=$\arabic*.$}
\setlist[enumerate,8]{label=$\alph*.$}
\setlist[enumerate,9]{label=$\roman*.$}
\renewlist{enumerate}{enumerate}{9}

501
nvim/spell/en.utf-8.add Normal file
View file

@ -0,0 +1,501 @@
AED
API
Autocommands
BufRead
Builtin
Bézier
CER
CMD
CentOS
Cmder
Ctrl
DLL
DT
Deoplete
Deteval
Esc
Exif
Firefox
Foxmail
GPU
GPUs
GUIs
GhostText
GitHub
HomeBrew
ICCF
ICDAR
IDE
IoU
Iterm2
JPEG
JSON
LSP
Linters
Listary
MPI
MXNet
MacOS
Matplotlib
Neomake
Nvim
Numpy
OpenCV
PIL
PNG
POSIX
Pandoc
Plugin
Plugins
PowerShell
PyTorch
Pynvim
RCTW
Ripgrep
TOC
TensorFlow
UI
URLs
Uber
Ubuntu
UltiSnips
Ultisnips
Unindent
VOC
VimConf
WSL
X11
YouCompleteMe
Zsh
ack
ag
appimage
autocommand
autocommands
autocompletion
autoload
backend
backtick
base64
bashrc
blog
bruteforce
builtin
cmder
colorscheme
conda
config
configs
css
css_intro/!
ctags
deoplete
dict
diff
dir
docstrings
emoji
env
epub
favicon
favicons
filetype
filetypes
filetypes/!
firenvim
fontconfig
fzf
gcc
grep
gutentags
homebrew
horovod
inline
iterm2
jedi
jpg
keras
linters
linting
lookaround
lookbehind
lua
macOS
maskrcnn
mergetools
metadata
mobi
modeline
modelines
mouseless
namespace
ndarray
neovim
nerdtree
nvim
opentype
pdflatex
perl
plugin
plugins
png
popup
pylint
quickfix
refractor
regex
repo
rg
ripgrep
runtimepath
smartphone
solarized
specicial/!
statusline
statuslines
tabline
tabstops
tagbar
terminfo
tex
textline
tmux
truetype
uWSGI
ultisnips
uninstall
usepackage
uwsgi
vimrc
vimscript
vimtex
warmup
wildmenu
workflow
wsltty
zplug
zsh
zinit
pycodestyle
pep8
pyflakes
pyls
flake8
sbcl
quicklisp
REPL
vlime
Matplotlib's
MiKTeX
FontManager
ttc
xnoremap
wiki
backticks
Reddit
LeaderF
tabpage
ccls
cmake
LLVM
llvm
subsampling
refactor
colorschemes
gruvbox8
gruvbox
Gruvbox
monokai
NeoSolarized
FiraCode
libc
printf
CMake
lsp
PPTX
libreoffice
PPT
pptx
pdf
unoconv
python3
imagemagick
ghostscript
poppler
Tqdm
tqdm
cmd
ThreadPoolExecutor
Cygwin
mintty
iTerm
stdin
unicode
ClashX
Qv2ray
socks5
v2ray
V2Ray
Dockerfile
pip3
dein
pynvim
iTerm2
neoformat
documentation/!
documentation
WSGI
ncurses
powerline
repos
hugo
ZOC
stackexchange
https
username
url
dev
Miniconda
Semshi
Alacritty
vim/!
Vim/!
Vim
vim
RPC
SumatraPDF/!
api
YAML
TOML
pandoc
textwidth
bytecode
CPython
nohup
stdout
stderr
UTC
CJK
numpy
LibreOffice
rebase
rebasing
glibc
Clangd
clangd
cheatsheet
redis
Disqus
submodules
stackoverflow
hostname
strftime
shiftwidth
whitespace
treesitter
linter
MySQL
advocator
Hexo
pybind11
OSC
minpac
ctrl
IME
emacs
Elisp
conf
MELPA
smartparens
Tpope
extmarks
extmark
CTAN
MikTex/!
MikTeX
softwares/!
Zong
cjk
backupcopy
nowritebackup
nvim's
linux
zshrc
pdftoppm
pdf2image
pdffonts
myers
difftool
vimdiff
softmax
lib64
toolset
sym
ffmpeg
PyAV
mp4
vint
npm
js
sudo
ps1
tpm
hacky
PCRE
teardown
github
fastgit
EOL
PyCharm
deduplication
STL
delimitMate
lspconfig
lualine
Keymap
iCloud
keyframe
maskRCNN
vundle
ycm
ftplugin
vim9
dotfile
magit
xelatex
subarray
fvim
CUDA
cuda
cpu
jdhao
whitelisted
BST
autocmd
init
miniconda
tsinghua
pylsp
mypy
isort
ustc
PyTorch
maxcdn
torchvision
FFmpeg
pyav
wezterm
frac
argmax
NeruIPS
InfoNCE
LSTM
linkedin
hotmail
datetime
csv
iterable
dataframe
CRNN
ICPR
matplotlib
scikit
NumPy
cudnn
SGD
ImageNet
ReLU
CIFAR
ResNet
XGBoost
Shenzhen
Tanh
Kaggle
minist
src
RGBA
yapf
Vimming
overfitting
PCA
SVM
KNN
leetcode
Logitech
WTF
tensorboard
Nvidia
GPG
manpage
metatable
open3d
pyntcloud
param
reddit
viml
PostgreSQL
CNY
wechat
ziroom
Postgres
plpgsql
postgres
PySpark
winbar
cmdline
shada
async
localhost
JetBrains
localhost
MariaDB
SQLite
Pgadmin
Datagrip
mysql
cwd
pyright
stubgen
pylance
variadic
Vimium
Databricks
Jira
databricks
FastAPI
pylintrc
PYTHONPATH
Uvicorn
qpdf
Elasticsearch
kibana
submodule
unix
GCP
Kubernetes
pubsub
Luarocks
luarocks
pytest
conftest
pytest
rootdir
pythonpath
py
#armin
Garmin/!
Garmin
garmin
MTP
openMTP
MacBook
Komoot
JVM
Lucene
replia
#hitespaces
whitespaces/!
venv
lockfile
uv
ref