├── .gitignore ├── LICENSE ├── README.md ├── init.lua ├── lsp ├── gopls.lua ├── lua_ls.lua └── ts_ls.lua └── lua ├── core ├── lsp.lua ├── options.lua ├── statusline.lua └── treesitter.lua └── util.lua /.gitignore: -------------------------------------------------------------------------------- 1 | # ignore all personal configs I don't want to ship with this config: 2 | **/*personal.lua 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Seongmin Lee 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NativeVim 2 | 3 | ![preview](https://github.com/boltlessengineer/nativevim/assets/60088301/7d0c6841-6e4c-43e0-8982-dc58328f484c) 4 | 5 | > NativeVim is a Neovim config without plugins 6 | 7 | NativeVim is **not**: 8 | - a distro[^1] 9 | - a plugin/package 10 | - or something that tries to reinvent famous plugins 11 | 12 | NativeVim **doesn't** include: 13 | - a package manager (e.g. `lazy.nvim` or `rocks.nvim`) 14 | - famous plugins like `nvim-lspconfig`, `nvim-cmp` and `nvim-treesitter` 15 | - and any other vim/neovim plugins 16 | 17 | NativeVim has features like: 18 | 19 | - basic LSP functionalities 20 | - TreeSitter Highlighting 21 | - completions 22 | 23 | and more! 24 | 25 | > [!CAUTION] 26 | > Since this config was developed to demonstrate the most native method, it may be slower or lack features than other configs. 27 | 28 | ## Why? 29 | 30 | This is an academical project to see if I can write Neovim config from scratch without any external plugins. 31 | So people can understand the Neovim plugin ecosystem more and know what exact plugins they really need. 32 | 33 | > [!NOTE] 34 | > I'm not saying "you don't need any of those plugins". Most plugins out there have their own reasons to be used. 35 | > But if you're curious about the solution without them, this project is for you. 36 | 37 | ## Requirement 38 | 39 | - Neovim v0.11+ 40 | 41 | ## How to use 42 | 43 | NativeVim doesn't support any plugin manager by default. 44 | Just clone it to `~/.config/nativevim` and use with `NVIM_APPNAME` environment variable. 45 | 46 | ```sh 47 | # 1. clone it 48 | git clone https://github.com/boltlessengineer/nativevim.git ~/.config/nativevim 49 | 50 | # 2. use it with NVIM_APPNAME 51 | NVIM_APPNAME=nativevim nvim 52 | ``` 53 | 54 | > [!NOTE] 55 | > NativeVim is just a Neovim config template designed to be read. It's just ~200 lines of lua code. 56 | > Take some time and read through the entire codebase to see how it works. 57 | > Feel free to open an issue if you have a different opinion about default config. 58 | 59 | ## Lines of Code 60 | 61 | ``` 62 | -------------------------------------------------------------------------------- 63 | Language Files Lines Blank Comment Code 64 | -------------------------------------------------------------------------------- 65 | Lua 9 235 45 72 118 66 | Markdown 1 69 21 0 48 67 | -------------------------------------------------------------------------------- 68 | Total 10 304 66 72 166 69 | -------------------------------------------------------------------------------- 70 | ``` 71 | 72 | [^1]: unless you call [kickstart.nvim] a Neovim distro 73 | 74 | [kickstart.nvim]: https://github.com/nvim-lua/kickstart.nvim 75 | -------------------------------------------------------------------------------- /init.lua: -------------------------------------------------------------------------------- 1 | if vim.fn.has("nvim-0.11") == 0 then 2 | vim.notify("NativeVim only supports Neovim 0.11+", vim.log.levels.ERROR) 3 | return 4 | end 5 | 6 | require("core.options") 7 | require("core.treesitter") 8 | require("core.lsp") 9 | require("core.statusline") 10 | -------------------------------------------------------------------------------- /lsp/gopls.lua: -------------------------------------------------------------------------------- 1 | ---@type vim.lsp.Config 2 | return { 3 | cmd = { "gopls" }, 4 | root_markers = { "go.work", "go.mod", ".git" }, 5 | filetypes = { "go", "gomod", "gowork", "gotmpl" }, 6 | } 7 | -------------------------------------------------------------------------------- /lsp/lua_ls.lua: -------------------------------------------------------------------------------- 1 | ---@type vim.lsp.Config 2 | return { 3 | cmd = { "lua-language-server" }, 4 | root_markers = { ".luarc.json", ".luarc.jsonc", ".luacheckrc", ".stylua.toml", "stylua.toml", "selene.toml", "selene.yml", ".git" }, 5 | filetypes = { "lua" }, 6 | on_init = require("util").lua_ls_on_init, 7 | } 8 | -------------------------------------------------------------------------------- /lsp/ts_ls.lua: -------------------------------------------------------------------------------- 1 | ---@type vim.lsp.Config 2 | return { 3 | cmd = { "typescript-language-server", "--stdio" }, 4 | root_markers = { "tsconfig.json", "jsconfig.json", "package.json", ".git" }, 5 | filetypes = { "javascript", "javascriptreact", "javascript.jsx", "typescript", "typescriptreact", "typescript.tsx" }, 6 | init_options = { 7 | hostInfo = "neovim", 8 | }, 9 | } 10 | -------------------------------------------------------------------------------- /lua/core/lsp.lua: -------------------------------------------------------------------------------- 1 | -- :h lsp-config 2 | 3 | -- enable lsp completion 4 | vim.api.nvim_create_autocmd("LspAttach", { 5 | group = vim.api.nvim_create_augroup("UserLspAttach", { clear = true }), 6 | callback = function(ev) 7 | vim.lsp.completion.enable(true, ev.data.client_id, ev.buf) 8 | end, 9 | }) 10 | 11 | -- enable configured language servers 12 | -- you can find server configurations from lsp/*.lua files 13 | vim.lsp.enable('gopls') 14 | vim.lsp.enable('lua_ls') 15 | vim.lsp.enable('ts_ls') 16 | -------------------------------------------------------------------------------- /lua/core/options.lua: -------------------------------------------------------------------------------- 1 | -- general options 2 | vim.o.completeopt = "menu,menuone,popup,fuzzy" -- modern completion menu 3 | 4 | vim.o.foldenable = true -- enable fold 5 | vim.o.foldlevel = 99 -- start editing with all folds opened 6 | vim.o.foldmethod = "expr" -- use tree-sitter for folding method 7 | vim.o.foldexpr = "v:lua.vim.treesitter.foldexpr()" 8 | 9 | 10 | -- NOTE: Setting vim options can be opinionated. 11 | -- While options above are crucial to make this whole config work as expected, 12 | -- below are just list of options I think most users will satisfy. 13 | -- Feel free to modify as your preference. 14 | 15 | 16 | vim.o.termguicolors = true -- enable rgb colors 17 | 18 | vim.o.cursorline = true -- enable cursor line 19 | 20 | vim.o.number = true -- enable line number 21 | vim.o.relativenumber = true -- and relative line number 22 | 23 | vim.o.signcolumn = "yes" -- always show sign column 24 | 25 | vim.o.pumheight = 10 -- max height of completion menu 26 | 27 | vim.o.list = true -- use special characters to represent things like tabs or trailing spaces 28 | vim.opt.listchars = { -- NOTE: using `vim.opt` instead of `vim.o` to pass rich object 29 | tab = "▏ ", 30 | trail = "·", 31 | extends = "»", 32 | precedes = "«", 33 | } 34 | 35 | vim.opt.diffopt:append("linematch:60") -- second stage diff to align lines 36 | 37 | vim.o.confirm = true -- show dialog for unsaved file(s) before quit 38 | vim.o.updatetime = 200 -- save swap file with 200ms debouncing 39 | 40 | vim.o.ignorecase = true -- case-insensitive search 41 | vim.o.smartcase = true -- , until search pattern contains upper case characters 42 | 43 | vim.o.smartindent = true -- auto-indenting when starting a new line 44 | vim.o.shiftround = true -- round indent to multiple of 'shiftwidth' 45 | vim.o.shiftwidth = 0 -- 0 to follow the 'tabstop' value 46 | vim.o.tabstop = 4 -- tab width 47 | 48 | vim.o.undofile = true -- enable persistent undo 49 | vim.o.undolevels = 10000 -- 10x more undo levels 50 | 51 | 52 | -- define and keys 53 | -- you should use `vim.keycode` to translate keycodes or pass raw keycode values like `" "` instead of just `""` 54 | vim.g.mapleader = vim.keycode("") 55 | vim.g.maplocalleader = vim.keycode("") 56 | 57 | -- remove netrw banner for cleaner looking 58 | vim.g.netrw_banner = 0 59 | -------------------------------------------------------------------------------- /lua/core/statusline.lua: -------------------------------------------------------------------------------- 1 | --[[ :h 'statusline' 2 | This is default statusline value: 3 | 4 | ```lua 5 | vim.o.statusline = "%f %h%w%m%r%=%-14.(%l,%c%V%) %P" 6 | ``` 7 | 8 | below is simple example of custom statusline using neovim APIs 9 | 10 | See `:h 'statusline'` for more information about statusline. 11 | ]] 12 | 13 | ---Show attached LSP clients in `[name1, name2]` format. 14 | ---Long server names will be modified. For example, `lua-language-server` will be shorten to `lua-ls` 15 | ---Returns an empty string if there aren't any attached LSP clients. 16 | ---@return string 17 | local function lsp_status() 18 | local attached_clients = vim.lsp.get_clients({ bufnr = 0 }) 19 | if #attached_clients == 0 then 20 | return "" 21 | end 22 | local names = vim.iter(attached_clients) 23 | :map(function(client) 24 | local name = client.name:gsub("language.server", "ls") 25 | return name 26 | end) 27 | :totable() 28 | return "[" .. table.concat(names, ", ") .. "]" 29 | end 30 | 31 | function _G.statusline() 32 | return table.concat({ 33 | "%f", 34 | "%h%w%m%r", 35 | "%=", 36 | lsp_status(), 37 | " %-14(%l,%c%V%)", 38 | "%P", 39 | }, " ") 40 | end 41 | 42 | vim.o.statusline = "%{%v:lua._G.statusline()%}" 43 | -------------------------------------------------------------------------------- /lua/core/treesitter.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | # How to install tree-sitter parsers manually? 3 | 4 | There are two ways to install tree-sitter parsers 5 | 6 | - download from git repository 7 | 1. download parser to `'runtimepath'` from git repository (e.g. clone to `~/.local/share/nvim/site/pack/*/start/tree-sitter-rust`) 8 | 2. write needed queries manually (e.g. `highlights.scm`, `folds.scm`) 9 | - install with luarocks 10 | 1. install parser with luarocks (e.g. `tree-sitter-css`) 11 | 2. add installed path to `'runtimepath'` 12 | 13 | First way is how `nvim-treesitter`'s `:TSInstall rust` works. `nvim-treesitter` also includes all needed queries for supported languages at this point (09-Jul-2024.) 14 | 15 | If we do all manually, second way is more convenient because we don't need to manually write all needed queries for each languages. 16 | 17 | ## 1. install parser with luarocks 18 | 19 | ```console 20 | luarocks \ 21 | --lua-version=5.1 \ 22 | --tree=$HOME/.local/share/nvim/rocks \ 23 | install tree-sitter-rust 24 | ``` 25 | 26 | We need to specify local install path with `--tree` flag. Here, we are using `rocks` folder in `stdpath("data")` (see `:h $XDG_DATA_HOME`.) 27 | 28 | ## 2. add installed rock to `'packpath'` 29 | 30 | Installed parser will be in `~/.local/share/nvim/rocks/lib/luarocks/rocks-5.1/tree-sitter-rust/0.0.27-1` where `0.0.27-1` is the installed version. 31 | 32 | Add this path to `'packpath'` so that Neovim can recognize and register the parser and bundled queries. 33 | 34 | ```console 35 | # create target packpath directory (`treesitter` is arbitrary) 36 | mkdir -p $HOME/.local/share/nvim/site/pack/treesitter/start 37 | 38 | # symlink installed rock to packpath 39 | ln -sf $HOME/.local/share/nvim/rocks/lib/luarocks/rocks-5.1/tree-sitter-rust/0.0.27-1 $HOME/.local/share/nvim/site/pack/treesitter/start/tree-sitter-rust 40 | ``` 41 | 42 | ## 3. start the parser in `FileType` AutoCommand 43 | 44 | ```lua 45 | vim.api.nvim_create_autocmd("FileType", { 46 | callback = function(ev) 47 | pcall(vim.treesitter.start) 48 | end 49 | }) 50 | ``` 51 | 52 | Add this code in your config. Reopen the editor, and you are done. 53 | --]] 54 | 55 | vim.api.nvim_create_autocmd("FileType", { 56 | callback = function() 57 | pcall(vim.treesitter.start) 58 | end 59 | }) 60 | -------------------------------------------------------------------------------- /lua/util.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | function M.lua_ls_on_init(client) 4 | local path = vim.tbl_get(client, "workspace_folders", 1, "name") 5 | if not path then 6 | return 7 | end 8 | -- override the lua-language-server settings for Neovim config 9 | client.settings = vim.tbl_deep_extend('force', client.settings, { 10 | Lua = { 11 | runtime = { 12 | version = 'LuaJIT' 13 | }, 14 | -- Make the server aware of Neovim runtime files 15 | workspace = { 16 | checkThirdParty = false, 17 | library = { 18 | vim.env.VIMRUNTIME 19 | -- Depending on the usage, you might want to add additional paths here. 20 | -- "${3rd}/luv/library" 21 | -- "${3rd}/busted/library", 22 | } 23 | -- or pull in all of 'runtimepath'. NOTE: this is a lot slower 24 | -- library = vim.api.nvim_get_runtime_file("", true) 25 | } 26 | } 27 | }) 28 | end 29 | 30 | return M 31 | --------------------------------------------------------------------------------