Neovim :: M Λ C R O is a collection of neovim configuration files inspired by Emacs / N Λ N O.
The goal of macro-neovim is to provide a clean and elegant user interface while remaining practical for daily tasks, striking a balance between a streamlined design and effective functionality. See showcases to get a glimpse of the basic usage and what this configuration looks like.
This is a highly personalized and opinionated neovim configuration, not a distribution. While it's not meant for direct use, you're welcome to fork, experiment, and adapt it to your liking. Feel free to use it as a starting point for your configuration or borrow elements you find useful. Issues and PRs are welcome.
Currently only tested on Linux (X11/Wayland/TTY) and Android (Termux).
- Features
- Requirements and Dependencies
- Installation
- Troubleshooting
- Performance
- Uninstallation
- Config Structure
- Tweaking this Configuration
- Appendix
- Modular design
- Install and manage packages in groups
- Make it easy to use different set of configuration for different use cases
- Clean and uncluttered UI, including customized versions of:
- VSCode-Neovim integration, makes you feel at home in VSCode when you occasionally need it
- Massive TeX math snippets
- Jupyter Notebook integration: edit notebooks like markdown files, run code in cells with simple commands and shortcuts
- Optimization for large files, open any file larger than 100 MB and edit like butter (see big files)
- Fast startup around ~25 ms
- Neovim 0.10, for exact version see nvim-version.txt
- Git
- GCC or Clang for building treesitter parsers and some libs
- Fd, Ripgrep, and Fzf for fuzzy search
- Pandoc, custom scripts and TexLive (for ArchLinux users, it is
texlive-core
andtexlive-extra
) for markdown → PDF conversion (:MarkdownToPDF
) - Node.js for installing dependencies for markdown-preview.nvim
- Pynvim, Jupyter Client, and IPython Kernel for Python support
- Jupytext for editing Jupyter notebooks
- A decent terminal emulator
- A nerd font, e.g. JetbrainsMono Nerd Font.
This is optional as nerd icons are disabled by default, to enable it, set the
environment variable
$NVIM_NF
, see environment variables
Tree-sitter installation and configuration are handled by nvim-treesitter.
Requires a C compiler, e.g. GCC or Clang, for building parsers.
To add or remove support for a language, install or uninstall the corresponding
parser using :TSInstall
or :TSUninstall
.
To make the change permanent, add or remove corresponding parsers in the
ensure_installed
field in the call to nvim-treesitter's setup()
function,
see lua/configs/nvim-treesitter.lua.
For LSP support, install the following language servers manually using your favorite package manager:
-
Bash: BashLS
Example for ArchLinux users:
sudo pacman -S bash-language-server
-
C/C++: Clang
-
Lua: LuaLS
-
Python: one of
-
Rust: Rust Analyzer
-
LaTeX: TexLab
-
VimL: VimLS
-
Markdown: Marksman
-
Go: Gopls
-
Typescript: Typescript Language Server and Biome
-
General-purpose language server: EFM Language Server
- Already configured for
- Black (formatter)
- Shfmt (formatter)
- Fish-indent (formatter)
- StyLua (formatter)
- Gofmt (formatter)
- Golangcli-lint (linter)
- Prettier (formatter)
- Eslint (linter)
- ...
- Already configured for
To add support for other languages, install corresponding language servers
manually then add lsp.lua
files under after/ftplugin to automatically launch
them for different filetypes.
Some examples of lsp.lua
files:
- after/ftplugin/lua/lsp.lua
- after/ftplugin/python/lsp.lua
- after/ftplugin/rust/lsp.lua
- after/ftplugin/sh/lsp.lua
- after/ftplugin/go/lsp.lua
- after/ftplugin/typescript/lsp.lua
Install the following debug adapters manually:
-
Bash:
Go to vscode-bash-debug release page, download the latest release (
bash-debug-x.x.x.vsix
), extract (change the extension from.vsix
to.zip
then unzip it) the contents to a new directoryvscode-bash-debug/
and put it under stdpathdata
(see:h stdpath
).Make sure
node
is executable. -
C/C++: install CodeLLDB.
Example for ArchLinux users:
yay -S codelldb # Install from AUR
-
Python: install DebugPy
Example for ArchLinux users:
sudo pacman -S python-debugpy
or
pip install --local debugpy # Install to user's home directory
or in a virtual env:
pip install debugpy
-
Go: install Delve
For more information on DAP installation, see Debug Adapter Installation.
- Bash: install Shfmt*
- C/C++: install Clang to use
clang-format
- Lua: install StyLua*
- Rust: install Rust to use
rustfmt
- Python: install Black*
- LaTeX: install texlive-core to use
latexindent
*Need EFM Language Server to work with vim.lsp.buf.format()
-
Make sure you have required dependencies installed.
-
Clone this repo to your config directory
git clone https://github.com/Bekaboo/dot.git bkb_dot && cp -r bkb_dot/.config/nvim ~/.config/nvim.macro
-
Open neovim using
NVIM_APPNAME=nvim.macro nvim
On first installation, neovim will prompt you to decide whether to install third-party plugins, press
y
to install,n
to skip,never
to skip and disable the prompt in the future (aka "do not ask again").The suggestion is to use
n
to skip installing plugins on first launch, and see if everything works OK under a bare minimum setup. Depending on your needs, you can choose whether to install third-party plugins later usingy
/yes
ornever
on the second launch.Some notes about third-party plugins
Installing third-party plugins is known to cause issues in some cases, including:
- Partially cloned plugins and missing dependencies due to slow network connection
- Building failure especially for plugins like telescope-fzf-native.nvim and markdown-preview.nvim due to missing building dependencies or slow installation process
- Treesitter plugins can easily cause issues if you are on a different nvim version, check nvim-version.txt for the version of nvim targeted by this config
To avoid these issues,
- Ensure you have a fast network before installing third-party plugins
- If the building process failed, go to corresponding project directory
under
g:package_path
and manually run the build command from there. The build commands are declared in module specification files under lua/modules - Ensure you are on the same version of nvim as specified in nvim-version.txt if you encounter any issue related to treesitter
-
After entering neovim, Run
:checkhealth
to check potential dependency issues. -
Enjoy!
If you encounter any issue, please try the following steps:
- Run
:Lazy restore
once to ensure that all packages are properly installed - Run
:checkhealth
to check potential dependency issues - Check
:version
to make sure you are on the same (of above) version of neovim as specified in nvim-version.txt - Try removing the following paths then restart neovim:
:echo stdpath('cache')
:echo stdpath('state')
:echo stdpath('data')
- If still not working, please open an issue and I will be happy to help
Use the following steps to generate a flamegraph to troubleshoot performance issues, e.g. laggy when typing or scrolling (requires FlameGraphs to be installed):
- Inside neovim, run
:lua require('jit.p').start('10,i1,s,m0,G', '/tmp/nvim-profile.log')
- Reproduce the performance issue
:lua require('jit.p').stop()
:qa!
flamegraph.pl /tmp/nvim-profile.log > /tmp/nvim-profile-flamegraph.svg && firefox /tmp/nvim-profile-flamegraph.svg
Customize how neovim determines large files by adjusting these settings:
vim.g.bigfile_max_lines
- Default:
32768
- Buffers with number of lines exceeding this will be flagged as big file
- Default:
vim.g.bigfile_max_size
- Default:
1048576
(bytes) - Buffers with corresponding file size exceeding this will be flagged as big file
- Default:
When a file is flagged as a big file (vim.b.bigfile
is set), certain features
will be disabled to improve performance.
You can uninstall this config completely by simply removing the following paths:
:echo stdpath('config')
:echo stdpath('cache')
:echo stdpath('state')
:echo stdpath('data')
.
├── colors # colorschemes
├── plugin # custom plugins
├── ftplugin # custom filetype plugins
├── init.lua # entry of config
├── lua
│ ├── core # files under this folder is required by 'init.lua'
│ │ ├── autocmds.lua
│ │ ├── opts.lua # options and general settings
│ │ ├── keymaps.lua
│ │ └── modules.lua # bootstraps plugin manager and specifies which modules to include
│ ├── modules # all plugin specifications and configs go here
│ │ ├── ui.lua # ui elements, e.g. icons
│ │ ├── completion.lua # auto-completion
│ │ ├── debug.lua # debug adapter (DAP) support
│ │ ├── edit.lua # general editing enhancements, e.g. auto-pair, surround, align, etc.
│ │ ├── langs.lua # language-specific plugins
│ │ ├── llm.lua # completion and code generators using LLMs
│ │ ├── markup.lua # enhancement for markdown and tex editing
│ │ ├── tools.lua # tools like fuzzy finder, git integration, etc.
│ │ ├── treesitter.lua # treesitter related plugins
│ │ └── colorschemes.lua # third-party themes
│ ├── configs # configs for each plugin
│ ├── snippets # snippets
│ ├── plugin # the actual implementation of custom plugins
│ └── utils
└── syntax # syntax files
In order to enable or disable a module, one need to change the table in
lua/core/modules.lua passed to enable_modules()
, for example
enable_modules({
'treesitter',
'edit',
-- ...
})
To install plugin foo
under module bar
, just insert the corresponding
specification to the big table lua/modules/bar.lua
returns, for instance,
lua/modules/bar.lua
:
return {
-- ...
{
'foo/foo',
dependencies = 'foo_dep',
},
}
To install plugin foo
under module bar
, one should first
create module bar
under lua/modules:
.
└── lua
└── modules
└── bar.lua
a module should return a big table containing all specifications of plugins under that module, for instance:
return {
{
'goolord/alpha-nvim',
cond = function()
return vim.fn.argc() == 0 and
vim.o.lines >= 36 and vim.o.columns >= 80
end,
dependencies = 'nvim-web-devicons',
},
{
'romgrk/barbar.nvim',
dependencies = 'nvim-web-devicons',
config = function() require('bufferline').setup() end,
},
}
After creating the new module bar
, enable it in lua/core/modules.lua:
enable_modules({
-- ...
'bar',
-- ...
})
See lua/core/opts.lua.
$NVIM_NO3RD
: disable third-party plugins if set$NVIM_NF
: enable nerd font icons if set
See lua/core/keymaps.lua, or see module config files for corresponding plugin keymaps.
cockatoo
, nano
, macro
, and sonokai
are three builtin custom
colorschemes, with separate palettes for dark and light background.
Neovim is configured to restore the previous background and colorscheme settings on startup, so there is no need to set them up in the config file explicitly.
To disable the auto-restore feature, remove the ColorSchemeRestore
augroup
in lua/core/autocmds.lua.
To tweak a colorscheme, edit corresponding colorscheme files under colors.
See lua/utils/lsp.lua and lsp.lua
files under after/ftplugin.
See lua/configs/dap-configs, lua/configs/nvim-dap.lua, and lua/configs/nvim-dap-ui.lua.
This configuration use LuaSnip as the snippet engine, custom snippets for different filetypes are defined under lua/snippets.
VSCode integration takes advantages of the modular design, allowing to use a different set of modules when neovim is launched by VSCode, relevant code is in autoload/plugin/vscode.vim and lua/core/modules.lua.
To make VSCode integration work, please install VSCode-Neovim in VSCode and configure it correctly.
After setting up VSCode-Neovim, re-enter VSCode, open a random file and it should work out of the box.
-
File manager using oil.nvim
-
DAP support powered by nvim-dap and nvim-dap-ui
-
Jupyter Notebook integration using jupytext and molten-nvim
-
Winbar with IDE-like drop-down menus using dropbar.nvim
-
LSP hover & completion thanks to neovim builtin LSP client and nvim-cmp
-
Git integration: fugitive and gitsigns.nvim
- UI
- Completion
- LLM
- Markup
- Edit
- Tools
- fzf-lua
- gitsigns.nvim
- plenary.nvim (dependency)
- nvim-colorizer.lua
- vim-fugitive
- vim-rhubarb (dependency)
- fugitive-gitlab.vim (dependency)
- oil.nvim
- quicker.nvim
- which-key.nvim
- LSP
- Treesitter
- Debug
- nvim-dap
- nvim-dap-ui
- nvim-nio (dependency)
- one-small-step-for-vimkind
- Colorschemes
- colorcolumn
- Shows color column dynamically based on current line width
- Released as deadcolumn.nvim
- expandtab
- Always use spaces for alignment, even if
'expandtab'
is not set, see:h 'tabstop'
point 5
- Always use spaces for alignment, even if
- fcitx5
- Switches and restores fcitx5 state in each buffer asynchronously
- jupytext
- Edits jupyter notebook like markdown files
- Writes into jupyter notebook asynchronously, which gives a smoother experience than jupytext.vim
- intro
- Shows a custom intro message on startup
- lsp
- Sets up LSP and diagnostic options and commands on
LspAttach
orDiagnosticChanged
- Sets up LSP and diagnostic options and commands on
- readline
- Readline-like keybindings in insert and command mode
- statuscolumn
- Custom statuscolumn, with git signs on the right of line numbers
- statusline
- Custom statusline inspired by nano-emacs
- tabline
- Simple tabline that shows the current working directory of each tab
- Use
:[count]TabRename [name]
to rename tabs
- tabout
- Tab out and in with
<Tab>
and<S-Tab>
- Tab out and in with
- term
- Some nice setup for terminal buffers
- tmux
- Integration with tmux, provides unified keymaps for navigation, resizing, and many other window operations
- vscode
- Integration with VSCode-Neovim
- winbar
- A winbar with drop-down menus and multiple backends
- Released as dropbar.nvim
- markdown-title
- Automatically capitalize the first letter of each word in markdown titles
- Use
:MarkdownAutoFormatTitle enable/disable
to enable or disable this feature
- markdown-codeblock
- Add shadings to markdown code blocks
- z
Like many vim builtin plugins, these plugins can be disabled by setting the
g:loaded_...
flag before loading them.
-
Neovim Version:
NVIM v0.10.4 Build type: RelWithDebInfo LuaJIT 2.1.1731601260 Run "nvim -V1 -v" for more info
-
Config Commit:
4ba45170
-
System: Arch Linux 6.12.10
-
Machine: ThinkPad X1 Nano Gen 1
-
Startup time with
--clean
:hyperfine -Nw10 'nvim --clean +q'
Benchmark 1: nvim --clean +q Time (mean ± σ): 7.2 ms ± 1.0 ms [User: 5.0 ms, System: 2.1 ms] Range (min … max): 6.0 ms … 12.5 ms 440 runs
-
Startup time with this config:
hyperfine -Nw10 'nvim +q'
Benchmark 1: nvim +q Time (mean ± σ): 8.8 ms ± 1.1 ms [User: 6.2 ms, System: 2.4 ms] Range (min … max): 7.4 ms … 14.2 ms 362 runs
startuptime log
--- Startup times for process: Primary/TUI --- times in msec clock self+sourced self: sourced script clock elapsed: other lines 000.000 000.000: --- NVIM STARTING --- 000.088 000.087: event init 000.150 000.062: early init 000.182 000.033: locale set 000.217 000.034: init first window 000.435 000.218: inits 1 000.439 000.004: window checked 000.441 000.002: parsing arguments 000.812 000.028 000.028: require('vim.shared') 000.869 000.029 000.029: require('vim.inspect') 000.925 000.048 000.048: require('vim._options') 000.927 000.113 000.036: require('vim._editor') 000.928 000.162 000.022: require('vim._init_packages') 000.930 000.327: init lua interpreter 001.322 000.392: --- NVIM STARTED --- --- Startup times for process: Embedded --- times in msec clock self+sourced self: sourced script clock elapsed: other lines 000.000 000.000: --- NVIM STARTING --- 000.076 000.075: event init 000.129 000.053: early init 000.156 000.027: locale set 000.186 000.030: init first window 000.386 000.200: inits 1 000.393 000.007: window checked 000.394 000.001: parsing arguments 000.734 000.025 000.025: require('vim.shared') 000.792 000.028 000.028: require('vim.inspect') 000.831 000.030 000.030: require('vim._options') 000.832 000.096 000.038: require('vim._editor') 000.833 000.140 000.019: require('vim._init_packages') 000.834 000.300: init lua interpreter 000.877 000.042: expanding arguments 000.889 000.012: inits 2 001.106 000.217: init highlight 001.107 000.001: waiting for UI 001.221 000.114: done waiting for UI 001.233 000.012: clear screen 001.282 000.005 000.005: require('vim.keymap') 001.704 000.469 000.464: require('vim._defaults') 001.706 000.004: init default mappings & autocommands 001.968 000.034 000.034: sourcing /usr/share/nvim/runtime/ftplugin.vim 002.021 000.032 000.032: sourcing /usr/share/nvim/runtime/indent.vim 002.100 000.047 000.047: sourcing /usr/share/nvim/archlinux.lua 002.103 000.061 000.013: sourcing /etc/xdg/nvim/sysinit.vim 002.192 000.032 000.032: require('vim.fs') 002.455 000.258 000.258: require('vim.uri') 002.465 000.326 000.036: require('vim.loader') 002.879 000.404 000.404: require('core.opts') 002.993 000.112 000.112: require('core.keymaps') 003.184 000.189 000.189: require('core.autocmds') 004.016 000.116 000.116: require('vim.ui') 004.036 000.851 000.735: require('core.modules') 004.037 001.910 000.028: sourcing /home/zeng/.config/nvim/init.lua 004.042 000.299: sourcing vimrc file(s) 004.268 000.032 000.032: sourcing /home/zeng/.config/nvim/filetype.lua 004.436 000.021 000.021: sourcing /usr/share/vim/vimfiles/ftdetect/meson.vim 004.456 000.172 000.152: sourcing /usr/share/nvim/runtime/filetype.lua 004.567 000.051 000.051: sourcing /usr/share/nvim/runtime/syntax/synload.vim 004.648 000.160 000.109: sourcing /usr/share/nvim/runtime/syntax/syntax.vim 005.039 000.268 000.268: sourcing /home/zeng/.config/nvim/plugin/_load.lua 005.161 000.108 000.108: sourcing /home/zeng/.config/nvim/plugin/intro.lua 005.373 000.016 000.016: sourcing /usr/share/nvim/runtime/plugin/gzip.vim 005.390 000.007 000.007: sourcing /usr/share/nvim/runtime/plugin/matchit.vim 005.495 000.087 000.087: sourcing /usr/share/nvim/runtime/plugin/matchparen.vim 005.513 000.008 000.008: sourcing /usr/share/nvim/runtime/plugin/netrwPlugin.vim 005.525 000.003 000.003: sourcing /usr/share/nvim/runtime/plugin/rplugin.vim 005.583 000.038 000.038: sourcing /usr/share/nvim/runtime/plugin/shada.vim 005.615 000.006 000.006: sourcing /usr/share/nvim/runtime/plugin/spellfile.vim 005.633 000.007 000.007: sourcing /usr/share/nvim/runtime/plugin/tarPlugin.vim 005.648 000.005 000.005: sourcing /usr/share/nvim/runtime/plugin/tutor.vim 005.666 000.009 000.009: sourcing /usr/share/nvim/runtime/plugin/zipPlugin.vim 005.716 000.041 000.041: sourcing /usr/share/nvim/runtime/plugin/editorconfig.lua 005.788 000.061 000.061: sourcing /usr/share/nvim/runtime/plugin/man.lua 005.875 000.075 000.075: sourcing /usr/share/nvim/runtime/plugin/osc52.lua 005.915 000.027 000.027: sourcing /usr/share/nvim/runtime/plugin/tohtml.lua 005.993 000.023 000.023: sourcing /usr/share/vim/vimfiles/plugin/black.vim 006.010 000.008 000.008: sourcing /usr/share/vim/vimfiles/plugin/fzf.vim 006.024 000.821: loading rtp plugins 006.096 000.071: loading packages 006.101 000.005: loading after plugins 006.109 000.008: inits 3 006.158 000.049: opening buffers 006.186 000.005 000.005: require('vim.F') 006.189 000.026: BufEnter autocommands 006.190 000.001: editing files in windows 006.277 000.024 000.024: require('utils') 006.278 000.071 000.047: require('utils.json') 006.311 000.027 000.027: require('utils.fs') 008.283 000.898 000.898: sourcing /home/zeng/.config/nvim/colors/sonokai.lua 008.389 000.085 000.085: require('utils.hl') 008.439 001.169: VimEnter autocommands 008.853 000.017 000.017: require('ffi') 008.861 000.263 000.246: require('lazy.stats') 009.132 000.256 000.256: require('lazy.core.util') 009.318 000.061 000.061: require('vim.highlight') 009.427 000.408: UIEnter autocommands 009.429 000.002: before starting main loop 009.610 000.057 000.057: require('plugin.statuscolumn') 009.713 000.028 000.028: require('utils.stl') 010.012 000.242 000.242: require('plugin.statusline') 010.113 000.065 000.065: require('utils.git') 010.158 000.037 000.037: require('vim._system') 010.985 001.127: first screen update 010.988 000.003: --- NVIM STARTED ---