Skip to content

Astro Vim¤

[2022-03-20 18:58]

This weekend I tested Astro Vim, which is a pretty well tuned (and good looking) LSP configuration bundle for nvim.

I like

  • the idea of having

  • LSP

  • treesitter
  • telescope
  • floatterm
  • (...)

...out of the box - and out of the way from my local conf - also to throw it also on servers with colleagues and point them to an "official" repo with docs. Those are not easy to get right with so many variants.

  • the full lua approach (but I'll have to have vim lang stuff, see below)
  • Also, while mine became a bit laggy on big files, this one is still pretty fast, with all the LSP goodies.

Here my notes about the conversion:

Cleaning Up¤

  • Backup and Remove:

  • $HOME/.config/nvim

  • $HOME/.cache/nvim
  • $HOME/.local/share/nvim

Install¤

git clone https://github.com/kabinspace/AstroVim ~/.config/nvim
nvim +PackerSync # nice

then e.g.

:LspInstall pyright
:TSInstall python # treesitter

Note

All user config is in .config/nvim/lua/user, from init.lua there.

Conventional Folders: ftplugin and spell: Symlinked¤

Put my old ones into lua/user/<ftplugin|spell> and symlinked 2 directories up. Worked.

Own Lua and Own Vimscript¤

Besides a few declarative config possibilities, for own plugins, in init.lua there is a lua polish function, loaded at the end.

There you hook in your stuff like this:

    require('user.mymodule')
    -- and vimscript like this:
    vim.cmd('source $HOME/.config/nvim/lua/user/polish.vim')

and in vimscript then back to lua in user folder like this: nmap ,g viW"ay:lua require('user.utils').smart_open([[<C-R>a]])<CR>

Colors¤

The declarative approach with colorscheme via lua failed, loading it in my .vim file at the end.

Spell¤

It did not ask me to download german at set spelllang=de, so:

mkdir .config/nvim/spell && cd $_` && wget 'http://ftp.vim.org/pub/vim/runtime/spell/de.utf-8.spl'

Telescope¤

LSP¤

Pretty straight forward, via their null_ls LSP "hub", where nvim is the direct server for all languages and calls the actual programs behind the scenes.

That way one can pick from a huge list of features and just install the binaries if required (command), done.

LSP log as always in $HOME/.cache/nvim/, file null_ls.log.

axblack¤

null_ls calls black with their --stdin-filename parameter. Had to add that to axblack so that single quote formatting works, as a no op (not in use anyway for stdin/stdout based formatting).

Edit: Found only later, that one can specify CLI args....

Special Plugins¤

Completion¤

I use "uga-rosa/cmp-dictionary", with 10k words.

Caution

For async the loading order is important (after cmp) - then the mpack import works.

Edit: Nope. After a packer sync it failed again, same error (mpack.so missing - loads too eaerly again)

Debugging¤

  • lsp: ~ ❯ tail -f .cache/nvim/null-ls.log (set lsp.debug = true)
  • tables: lua U.dump(vim.lsp)

Gotchas¤

  • When trying cmp or lsp, always try on files which exist at vi start. Do NOT create new files, it will not work.

  • The async flag at cmp_dictionary was behaving totally different after PackerSync calls, failed with mpack not found, allthough working before... Had to switch it off.

Config Dumps¤

My init.lua
$ cat /home/gk/.config/nvim.personal/init.lua
-- https://github.com/hrsh7th/nvim-cmp/wiki/Example-mappings#super-tab-like-mapping
--
local cmp_setup = function()
	local _, cmp = pcall(require, "cmp")
	local _, luasnip = pcall(require, "luasnip")
	local has_words_before = function()
		local line, col = unpack(vim.api.nvim_win_get_cursor(0))
		local lines = vim.api.nvim_buf_get_lines
		return col ~= 0 and lines(0, line - 1, line, true)[1]:sub(col, col):match("%s") == nil
	end
	return {
		mapping = {
			["<Tab>"] = cmp.mapping(function(fallback)
				if cmp.visible() then
					cmp.select_next_item()
				elseif luasnip.expand_or_jumpable() then
					luasnip.expand_or_jump()
				elseif has_words_before() then
					cmp.complete()
				else
					fallback()
				end
			end, { "i", "s" }),

			["<S-Tab>"] = cmp.mapping(function(fallback)
				if cmp.visible() then
					cmp.select_prev_item()
				elseif luasnip.jumpable(-1) then
					luasnip.jump(-1)
				else
					fallback()
				end
			end, { "i", "s" }),
		},
	}
end

local config = {

	-- Set colorscheme
	-- colorscheme = "default_theme",
	colorscheme = "kanagawa",

	-- Default theme configuration
	default_theme = {
		diagnostics_style = "none",
		-- Modify the color table
		colors = { fg = "#abb2bf" },
		-- Modify the highlight groups
		highlights = function(highlights)
			local C = require("default_theme.colors")

			highlights.Normal = { fg = C.fg, bg = C.bg }
			return highlights
		end,
	},

	-- Disable default plugins
	enabled = {
		bufferline = true,
		nvim_tree = true,
		lualine = true,
		lspsaga = true,
		gitsigns = true,
		colorizer = true,
		toggle_term = true,
		comment = true,
		symbols_outline = true,
		indent_blankline = true,
		dashboard = true,
		which_key = true,
		neoscroll = true,
		ts_rainbow = true,
		ts_autotag = true,
	},
	plugins = {
		cmp = cmp_setup(),
		init = {
			"ThePrimeagen/refactoring.nvim",
			"arcticicestudio/nord-vim",
			"godlygeek/tabular",
			"iamcco/markdown-preview.nvim",
			"kdheepak/lazygit.nvim",
			"matsuuu/pinkmare",
			"rebelot/kanagawa.nvim",
			"tpope/vim-repeat",
			"tpope/vim-surround",
			"voldikss/vim-floaterm",
			"rafamadriz/friendly-snippets",
			{
				"uga-rosa/cmp-dictionary",
				after = "nvim-cmp",
				config = function()
					local cmp = require("cmp")
					local config = cmp.get_config()
					table.insert(config.sources, { name = "dictionary", keyword_length = 2 })
					cmp.setup(config)
				end,
		require("cmp_dictionary").setup({
			dic = {
				["markdown"] = { "/home/gk/.config/nvim.personal/10k.txt" },
				--["markdown"] = { "/usr/share/dict/words" },
				-- ["lua"] = "path/to/lua.dic",
				-- ["javascript,typescript"] = { "path/to/js.dic", "path/to/js2.dic" },
				-- filename = {
				-- 	["xmake.lua"] = { "path/to/xmake.dic", "path/to/lua.dic" },
				-- },
				-- filepath = {
				-- 	["%.tmux.*%.conf"] = "path/to/tmux.dic",
				-- },
			},
			-- The following are default values, so you don't need to write them if you don't want to change them
			-- exact = 2,
			-- first_case_insensitive = false,
			-- document = false,
			-- document_command = "wn %s -over",
			async = false,
			-- capacity = 5,
			-- debug = false,
		})

			},

			-- { "andweeb/presence.nvim" },
			-- {
			--   "ray-x/lsp_signature.nvim",
			--   event = "BufRead",
			--   config = function()
			--     require("lsp_signature").setup()
			--   end,
			-- },
		},
		-- All other entries override the setup() call for default plugins
		treesitter = { ensure_installed = { "lua" } },
		packer = {
			compile_path = vim.fn.stdpath("config") .. "/lua/packer_compiled.lua",
		},
	},

	-- Add paths for including more VS Code style snippets in luasnip
	luasnip = {
		vscode_snippet_paths = {},
	},

	-- Modify which-key registration
	["which-key"] = {
		-- Add bindings to the normal mode <leader> mappings
		-- register_n_leader = {["N"] = {"<cmd>tabnew<cr>", "New Buffer"}}
	},

	-- Extend LSP configuration
	lsp = {
		-- add to the server on_attach function
		-- on_attach = function(client, bufnr)
		-- end,

		-- override the lsp installer server-registration function
		-- server_registration = function(server, opts)
		--   server:setup(opts)
		-- end

		-- Add overrides for LSP server settings, the keys are the name of the server
		["server-settings"] = {
			-- example for addings schemas to yamlls
			-- yamlls = {
			--   settings = {
			--     yaml = {
			--       schemas = {
			--         ["http://json.schemastore.org/github-workflow"] = ".github/workflows/*.{yml,yaml}",
			--         ["http://json.schemastore.org/github-action"] = ".github/action.{yml,yaml}",
			--         ["http://json.schemastore.org/ansible-stable-2.9"] = "roles/tasks/*.{yml,yaml}",
			--       },
			--     },
			--   },
			-- },
		},
	},

	-- Diagnostics configuration (for vim.diagnostics.config({}))
	diagnostics = { virtual_text = true, underline = true },

	-- null-ls configuration
	["null-ls"] = function()
		-- Formatting and linting
		-- https://github.com/jose-elias-alvarez/null-ls.nvim
		local status_ok, null_ls = pcall(require, "null-ls")
		if not status_ok then
			return
		end

		-- Check supported formatters
		-- https://github.com/jose-elias-alvarez/null-ls.nvim/tree/main/lua/null-ls/builtins/formatting

		-- Check supported linters
		-- https://github.com/jose-elias-alvarez/null-ls.nvim/tree/main/lua/null-ls/builtins/diagnostics
		local b = null_ls.builtins
		local txt = { filetypes = { "markdown", "text" } }

		null_ls.setup({
			debug = true,
			sources = {
				-- https://github.com/jose-elias-alvarez/null-ls.nvim/blob/main/doc/BUILTINS.md
				b.code_actions.refactoring,
				b.code_actions.shellcheck,
				b.completion.luasnip,
				b.completion.spell.with(txt),
				b.diagnostics.misspell.with(txt),
				b.diagnostics.shellcheck,
				b.diagnostics.write_good,
				b.formatting.black.with({ extra_args = { "--fast" } }),
				b.formatting.prettier.with({ filetypes = { "html", "json", "yaml" } }),
				b.formatting.shfmt,
				b.formatting.stylua,
				b.hover.dictionary.with(txt), -- shift-k is hover
			},
			-- NOTE: You can remove this on attach function to disable format on save
			on_attach = function(client)
				if client.resolved_capabilities.document_formatting then
					vim.cmd("autocmd BufWritePre <buffer> lua vim.lsp.buf.formatting()")
				end
			end,
		})
	end,
	polish = function()
		local opts = { noremap = true, silent = true }
		local map = vim.api.nvim_set_keymap
		-- Set options
		local set = vim.opt
		require("luasnip.loaders.from_snipmate").lazy_load()
		-- cmp.formatting.fields = {"kind"}
		U = require("user.utils") -- allows :lua U.dump(vim.lsp)
		require("luasnip.loaders.from_vscode").lazy_load()

		set.shiftwidth = 4 -- Number of space inserted for indentation
		set.tabstop = 4 -- Number of spaces in a tab
		set.foldmethod = "indent"
		set.foldlevel = 99 -- open all
		set.relativenumber = true
		--set.dict = "/usr/share/dict/words" -- much more
		set.dict = "~/.config/nvim.personal/10k.txt"
		-- map("n", "<C-s>", ":w!<CR>", opts)
		vim.api.nvim_set_keymap(
			"v",
			"<leader>rr",
			"<Esc><cmd>lua require('telescope').extensions.refactoring.refactors()<CR>",
			{ noremap = true }
		)
		map("n", ",4", ":ToggleTerm size=100 <CR>", opts)
		map("n", ",D", ":lua vim.diagnostic.config({virtual_text = false})<CR>", opts)
		-- all viml:
		vim.cmd("source $HOME/.config/nvim.personal/polish.vim")
		-- do this only here so that require mpack works for async:
	end,
}

return config

My polish.vim
$ cat /home/gk/.config/nvim.personal/polish.vim
" this is simply ctrl-i:
"nnoremap <Tab>   za
nnoremap <C-i>   za
"cnoremap <silent> x<CR>  :call ConfirmQuit(1)<CR>
"  "Yank constent with D and C:
nnoremap Y         y$
nnoremap ,l        :LazyGit<CR>
nnoremap ,q        :q!<CR>
nnoremap ,Q        :Q!<cr>
nnoremap ,d        :wq!<CR>
nnoremap ,w        :w<cr>
nnoremap ,1        :source ~/.config/nvim/init.lua<CR>
nnoremap ,2        :edit ~/.config/nvim/lua/user/init.lua<CR>
nnoremap ,c        :close<CR> " close just a split or a tab
nmap     ,f        za "folds


nnoremap <silent> ,3  :FloatermNew! --autoclose=2 --wintype=vsplit cd %:p:h<CR>

"" Line join better, position cursor at join point:
" (J is 5 lines jumps)
nnoremap gj $mx<cmd>join<CR>0$[`dmx

" Universal python scriptable file or browser opener over word:
"nmap ,g viW"ay:lua require('utils').smart_open([[<C-R>a]])<CR><CR>
nmap ,g viW"ay:lua require('user.utils').smart_open([[<C-R>a]])<CR>
vmap ,g :lua require('user.utils').smart_open([[visualsel]])<CR><CR>

" tabularize:
nmap ga   :Tabularize/
xmap ga   :Tabularize/
nmap tt  vip:s:,,:\|:ge<CR>vip:Tabularize/\|<CR>
" markdown table
nnoremap ,ta       vip:s/$/\|/ge<CR>vip:s:,,:\|:ge<CR>vip:s:^:\|:ge<CR>vip:s:\|\|:\|:ge<CR>vip:Tabularize/\|<CR> 

nnoremap S :%s//gI<Left><Left><Left>
" move between splits with alt-jk
nnoremap <M-j> <C-W><C-W>
nnoremap <M-k> <C-W><C-W>
inoremap <M-j> <ESC><C-W><C-W>
inoremap <M-k> <ESC><C-W><C-W>
nnoremap <C-L> <C-W><C-J>
nnoremap <C-H> <C-W><C-K>
" we often have old stuff at end of files:
nnoremap  G      G?begin_archive<CR>


nmap <silent> <Leader><Leader> <Leader>ff
nnoremap <silent> <Leader>j  :Telescope buffers<cr>
nnoremap <silent> <Leader>g  :Telescope live_grep<cr>


" go to the position I was when last editing the file
au BufReadPost * if line("'\"") > 0 && line("'\"") <= line("$") | exe "normal g'\"" | endif

"colorscheme pinkmare"colorscheme kanagawa

"" AutoSave
nmap ,s  :AutoSave<CR>
function! s:autosave(enable)
  augroup autosave
    autocmd!
    " at $IDE we call this at ANY BufEnter, with enable=2
    if a:enable == 2
        if $IDE != 'true'
            return
        endif
    endif
    if a:enable
      autocmd TextChanged,InsertLeave <buffer>
            \  if empty(&buftype) && !empty(bufname(''))
            \|   silent! update
            \| endif
    endif
  augroup END
endfunction
command! -bang AutoSave call s:autosave(<bang>1)
autocmd BufEnter *.* :call s:autosave(2) " $IDE -> always

"" Yank hilite
augroup highlight_yank
    autocmd!
    au TextYankPost * silent! lua vim.highlight.on_yank({higroup="IncSearch", timeout=400})
augroup END

"" Packer
augroup packer_conf
  autocmd!
  autocmd BufWritePost plugins.lua source <afile> | PackerSync
augroup end

Back to top