index
disable completion temporarily
tabs vs spaces :)
Tabs Reasoning: Tabs are for indentation. The editor decides how a tab character is displayed. I find 3 spaces to be most visually apprpriate.
-- Insert tabs but they appear like 3 spaces
vim.opt.expandtab = false
vim.opt.tabstop = 3
vim.opt.shiftwidth = 3
vim.opt.softtabstop = 3
vim.opt.list = true
vim.opt.listchars = "tab:» ,eol:↲,nbsp:+,trail:-"
Spaces reasoning: What you see is what it is: if indentation has 3 spaces, its 3 spaces. If its 8 its a tab. We only use spaces. So when we press 'Tab' for indent, we want spaces to be inserted as opposed to a tab character.
-- The representation of a tab in spaces (or more precisely, columns)
vim.opt.tabstop = 8
vim.opt.shiftwidth = 2
-- When enabled uses spaces instead of tab characters
vim.opt.expandtab = true
vim.opt.softtabstop = 2
vim.optpt.list = true
vim.opt.listchars = "tab:» ,eol:↲,nbsp:+,trail:-"
LSP Request
local function test(err, result, ctx)
vim.api.nvim_buf_set_lines(5, 0, -1, true, vim.split(result.contents.value, '\n'))
end
vim.lsp.buf_request(0, "textDocument/hover", vim.lsp.util.make_position_params(0), test)
Installing packages without package manager
Place the package in ~/.config/nvim/pack
Eg: Treesitter installation
cd ~/.config/nvim/pack
mkdir -p treesitter/start
cd treesitter/start
git clone 'https://github.com/nvim-treesitter/nvim-treesitter.git'
When you want to install a package, say nvim-treesitter, and you place it pack/
set tw=80 for markdown files
file: ~/.config/nvim/lua/core/autocmds.lua
local group = vim.api.nvim_create_augroup("VectorClearAu", { clear = true })
vim.api.nvim_create_autocmd('FileType', {
pattern = "markdown",
callback = function()
vim.opt.tw = 80
end,
group = group,
})
Autocommands
source: TJ Devries YT
test.lua:
BufEnter
is an event. Autocommands are tied to events. To see more about events :h events
To check autocmds tied to a particular event: :au BufEnter
To clear autocmds attached to particular event: au! BufEnter
As it is, if test.lua
file is executed again, it greates another autocmd. So "Hello"
gets print twice with every BufEnter
event. This is why augroup
is used.
Creating augroup
:
local group = vim.api.nvim_ceate_augroup("SmashThatLikeButton", { clear = true })
-- if it was { clear = false } we'd get the old behavior
vim.api.nvim_create_autocmd('BufEnter', { command = "echo 'Hello 1'", group = group })
vim.api.nvim_create_autocmd('BufEnter', { command = "echo 'Hello 2'", group = group })
Now every time the file gets created, the autogroup gets cleared before its created, and with it the autocmds part of the group also gets cleared.
Now, this might be the feature that we're gonna use more often —lua functions triggered
by events
.
local group = vim.api.nvim_ceate_augroup("SmashThatLikeButton", { clear = true })
-- if it was { clear = false } we'd get the old behavior
vim.api.nvim_create_autocmd('BufEnter', { command = "echo 'Hello 1'", group = group })
vim.api.nvim_create_autocmd('BufEnter', { callback = function()
print("Hello")
end, group = group })
Screenshots to markdown
This script both gets the latest file in ~/Pictures where my screenshots are and then copies them to img/
folder
in the same folder where the file is being edited.
local M = {}
M.insert_screenshot_markdown = function()
local bufno = vim.api.nvim_get_current_buf()
local winid = vim.fn.bufwinid(bufno)
local path = vim.api.nvim_buf_get_name(bufno)
local pathtofile = vim.fn.fnamemodify(path, ':h')
local img_dir = pathtofile .. "/img"
local stat = vim.loop.fs_stat(img_dir)
local dir_exists = stat and stat.type == 'directory'
if not dir_exists then
local success, err = vim.loop.fs_mkdir(img_dir, 511)
if not success then
print("Unable to create dir")
return 1
end
end
local directory = vim.fn.expand('~/Pictures')
local cmd = "ls -t " .. directory .. " | head --lines 1"
local newest_file = vim.fn.system(cmd)
newest_file = newest_file:gsub("\n", "")
local cmd2 = "cp " .. directory .. "/" .. newest_file .. " " .. img_dir
vim.fn.system(cmd2)
if newest_file ~= "" then
local markdown_link = string.format("", newest_file, newest_file)
local row, col = unpack(vim.api.nvim_win_get_cursor(winid))
local lines = vim.api.nvim_buf_get_lines(bufno, row - 1, row, false)
-- Check if the 'lines' table is empty
local line = ""
if #lines ~= 0 then
line = lines[1]:gsub("\n", " ")
end
local new_line = line .. markdown_link
vim.api.nvim_buf_set_lines(bufno, row - 1, row, false, { new_line })
vim.api.nvim_win_set_cursor(winid, { row, col + #markdown_link })
end
end
return M
The above script is placed in ~/plugins and it gets picked up by neovim lazy.nvim because of this mention
in ~/.config/nvim/lua/plugins/vector.lua
:
{
dir = "/home/vector/plugins/markdown-screenshot.nvim",
config = function()
vim.keymap.set({ 'n', 'i' }, '<Leader>p',
function()
require("markdown-screenshot").insert_screenshot_markdown()
end,
{ desc = "Paste screenshot" })
end
},
Scripting workflow
:vnew ~/.config/nvim/lua/development/ss.lua
:echo nvim_get_current_buf()
1
local bufno = 1
local path = vim.api.nvim_buf_get_name(bufno)
Getting treesitter root
local parser = vim.treesitter.get_parser(5, "dart", {})
local tree = parser:parse()[1]
local root = tree:root()
Visual range
local cursorpos = vim.fn.getpos('.')
local vstart = vim.fn.getpos('v')
-- { 0, 31, 4, 0 jo}
-- row: cursorpos[2] row: vstart[2]
-- col: curspor[3], col: vstart[3]
Note
to
is visual selection in vim. Note the return value
local result = {} --string[]
local op1 = vim.fn.getpos('v')
local op2 = vim.fn.getpos('.')
table.insert(result, vim.inspect(op1))
table.insert(result, vim.inspect(op2))
vim.api.nvim_buf_set_text(19, 0, -1, result)
-- note 19 is output buffer
Dealing with visual ranges (incomplete)
local outputbuf = 10
local inputbuf = 1
local inputwin = 1000
local cursorpos = vim.api.nvim_win_get_cursor(1000)
local visualstart = vim.fn.getpos('v')
vim.api.nvim_buf_set_lines(outputbuf, 0, -1, false, {
string.format("Row: %s Column: %s", cursorpos[1], cursorpos[2]),
string.format("Row: %s Column: %s", visualstart[2], visualstart[3])
})
Complete init.lua
-- Essential keymaps
-- Rebind space in normal mode to leader
vim.g.mapleader = ' '
vim.g.maplocalleader = ' '
-- Make line numbers default
vim.opt.number = true
-- Save undo history
vim.opt.undofile = true
vim.opt.completeopt = "menu,menuone,noselect"
-- TABS
-- -- Insert tabs but they appear like 3 spaces
-- vim.opt.expandtab = false
-- vim.opt.tabstop = 3
-- vim.opt.shiftwidth = 3
-- vim.opt.softtabstop = 3
-- vim.opt.list = true
-- vim.opt.listchars = "tab:» ,eol:↲,nbsp:+,trail:-"
-- SPACES
-- The representation of a tab in spaces (or more precisely, columns)
vim.opt.tabstop = 8
vim.opt.shiftwidth = 2
-- When enabled uses spaces instead of tab characters
vim.opt.expandtab = true
vim.opt.softtabstop = 2
vim.opt.list = true
vim.opt.listchars = "tab:» ,eol:↲,nbsp:+,trail:-"
-- Case insensitive searching UNLESS /C or capital in search
vim.opt.ignorecase = true
vim.opt.smartcase = true
vim.opt.termguicolors = true
vim.opt.foldopen = ""
-- C-c to copy to system clipboard
vim.keymap.set('v', '<C-c>', '"+y')
-- [[ Highlight on yank ]]
-- See `:help vim.highlight.on_yank()`
local highlight_group = vim.api.nvim_create_augroup('YankHighlight', { clear = true })
vim.api.nvim_create_autocmd('TextYankPost', {
callback = function()
vim.highlight.on_yank()
end,
group = highlight_group,
pattern = '*',
})
-- Copy the path to current file so I can cd into it from another terminal
vim.keymap.set('n', '<leader>cd', function()
local cwd = vim.fn.expand('%:p:h')
vim.fn.setreg('+', cwd)
print("Copied " .. cwd .. " to clipboard")
end, { desc = "Copy the directory containing file" })
-- Run the current file (lua)
vim.keymap.set('n', '<leader>cc', function()
vim.cmd(":w")
vim.cmd("so " .. vim.fn.expand('%:p'))
end, { desc = "Source the current file" })
-- H,L to switch tabs in normal mode
vim.keymap.set('n', 'H', vim.cmd.tabp, { desc = 'Switch to previous tab' })
vim.keymap.set('n', 'L', vim.cmd.tabn, { desc = 'Switch to next tab' })
-- Inspect any variable v using P(v)
function P(v)
vim.notify(vim.inspect(v))
return v
end
local lspautocmds = vim.api.nvim_create_augroup('LSPAutoCmds', { clear = true, })
vim.api.nvim_create_autocmd("LspAttach", {
group = lspautocmds,
callback = function(args)
local bufnr = args.buf
local fzf_lua = require('fzf-lua')
local client_id = args.data.client_id
local client = vim.lsp.get_client_by_id(client_id)
vim.keymap.set('n', 'gd', vim.lsp.buf.definition, { buffer = bufnr, desc = '[G]o to [D]efinition' })
vim.keymap.set('n', 'gD', vim.lsp.buf.declaration, { buffer = bufnr, desc = '[G]o to [D]eclaration' })
-- vim.keymap.set('n', '<Leader>ca', vim.lsp.buf.code_action, { buffer = bufnr, desc = '[C]ode [A]ction' })
vim.keymap.set('n', '<Leader>rn', vim.lsp.buf.rename, { buffer = bufnr, desc = '[R]e[n]ame' })
vim.keymap.set('n', 'K', vim.lsp.buf.hover, { buffer = bufnr, desc = 'Hover Documentation' })
vim.api.nvim_buf_create_user_command(bufnr, 'Format', function(_)
vim.lsp.buf.format()
end, { desc = 'Format current buffer with LSP' })
vim.keymap.set('n', '[d', vim.diagnostic.goto_prev, { desc = 'Go to previous diagnostic message' })
vim.keymap.set('n', ']d', vim.diagnostic.goto_next, { desc = 'Go to next diagnostic message' })
vim.keymap.set('n', '<leader>e', vim.diagnostic.open_float, { desc = 'Open floating diagnostic message' })
vim.keymap.set('n', '<leader>q', vim.diagnostic.setloclist, { desc = 'Open diagnostics list' })
-- fzf-lua
vim.keymap.set('n', 'gr', fzf_lua.lsp_references, { buffer = bufnr, desc = '[G]o to [D]efinition' })
vim.keymap.set('n', '<Leader>ca', fzf_lua.lsp_code_actions, { buffer = bufnr, desc = '[C]ode [A]ction' })
-- vim.lsp.completion.enable(true, client_id, bufnr, { autotrigger = true })
end
})
vim.api.nvim_create_autocmd('FileType', {
group = lspautocmds,
pattern = 'lua',
callback = function(ev)
vim.lsp.start({
name = "lua_ls",
cmd = { 'lua-language-server' },
root_dir = vim.fs.root(ev.buf, { '.luarc.json', '.luarc.jsonc' }),
on_init = function(client)
client.settings.Lua = {
runtime = {
version = "LuaJIT",
},
workspace = {
library = {
"/usr/share/nvim/runtime",
"/usr/lib/lua-language-server/meta/3rd/luv/library"
},
},
}
end,
settings = {
Lua = {},
},
})
end,
})
-- handler for dart/textDocument/publishClosingLabels
local namespace = vim.api.nvim_create_namespace("flutter_closing_tags")
local function closing_tags(error, result, ctx, config)
vim.api.nvim_buf_clear_namespace(0, namespace, 0, -1)
for _, item in ipairs(result.labels) do
local line = tonumber(item.range["end"].line)
if line <= vim.api.nvim_buf_line_count(0) then
vim.api.nvim_buf_set_extmark(0, namespace, line, -1, {
virt_text = { {
"// " .. item.label,
"Comment",
} },
virt_text_pos = "eol",
hl_mode = "combine",
})
end
end
end
vim.api.nvim_create_autocmd('FileType', {
group = lspautocmds,
pattern = 'dart',
callback = function(ev)
vim.lsp.start({
name = "dartls",
cmd = { 'dart', 'language-server', '--protocol=lsp' },
root_dir = vim.fs.root(ev.buf, { 'pubspec.yaml' }),
init_options = {
closingLabels = true,
flutterOutline = true,
onlyAnalyzeProjectsWithOpenFiles = true,
outline = true,
suggestFromUnimportedLibraries = true
},
-- capabilities = require('cmp_nvim_lsp').default_capabilities(),
capabilities = vim.lsp.protocol.make_client_capabilities(),
settings = {
dart = {
completeFunctionCalls = true,
showTodos = true,
enableSnippets = true,
documentation = "full",
},
},
handlers = {
["dart/textDocument/publishClosingLabels"] = closing_tags
}
})
vim.opt.commentstring = "// %s"
end,
})
-- Todo: Make this better.
vim.keymap.set({ 'n', 'i' }, '<M-p>', function()
local dirpath = vim.fn.expand('%:p:h') .. '/img'
local imgname = os.time() .. '.png'
-- A shell script to paste image from clipboard into subdir img
local shell_script = string.format([[
DIRPATH=%s
IMGFNAME=%s
if ! [ -d $DIRPATH ]
then
mkdir $DIRPATH
fi
xclip -selection clipboard -t image/png -o > $DIRPATH/$IMGFNAME
]], dirpath, imgname)
vim.fn.system(shell_script)
-- All this is just to insert markdown image link
local row, col = unpack(vim.api.nvim_win_get_cursor(0))
local lines = vim.api.nvim_buf_get_lines(0, row - 1, row, false)
local markdown_link = string.format("", imgname, imgname)
-- Check if the 'lines' table is empty
local line = ""
if #lines ~= 0 then
line = lines[1]:gsub("\n", " ")
end
local new_line = line .. markdown_link
vim.api.nvim_buf_set_lines(0, row - 1, row, false, { new_line })
vim.api.nvim_win_set_cursor(0, { row, col + #markdown_link })
end, { desc = "Paste screenshot" })
vim.cmd('colorscheme vector')
require 'nvim-treesitter.parsers'.get_parser_configs().dart = {
install_info = {
files = { "src/parser.c", "src/scanner.c" },
url = "https://github.com/vectorspacexyz/tree-sitter-dart"
},
-- Optional: list of maintainers
maintainers = { "@vectorspacexyz" }
}
require 'nvim-treesitter.configs'.setup {
ensure_installed = { "c", "lua", "vim", "vimdoc", "query", "markdown", "markdown_inline" },
highlight = {
enable = true,
additional_vim_regex_highlighting = true,
},
incremental_selection = {
enable = true,
keymaps = {
init_selection = "gnn", -- set to `false` to disable one of the mappings
node_incremental = "grn",
scope_incremental = "grc",
node_decremental = "grm",
},
},
}
local cmp = require 'cmp'
cmp.setup {
sources = {
{ name = 'nvim_lsp' }
},
mapping = cmp.mapping.preset.insert({
["<C-n>"] = cmp.mapping.select_next_item({ behavior = cmp.SelectBehavior.Insert }),
["<C-p>"] = cmp.mapping.select_prev_item({ behavior = cmp.SelectBehavior.Insert }),
["<C-b>"] = cmp.mapping.scroll_docs(-4),
["<C-f>"] = cmp.mapping.scroll_docs(4),
["<C-Space>"] = cmp.mapping.complete(),
-- ["<C-e>"] = cmp.mapping.abort(),
-- ["<C-y>"] = cmp.mapping.confirm({ select = true }), -- Accept currently selected item. Set `select` to `false` to only confirm explicitly selected items.
["<C-Y>"] = cmp.mapping.confirm({
behavior = cmp.ConfirmBehavior.Replace,
select = true,
}), -- Accept currently selected item. Set `select` to `false` to only confirm explicitly selected items.
}),
}
local fold_named_arg_func = function()
local allowed_types = {
["selector"] = true,
["named_argument"] = true,
["list_literal"] = true
}
local ts_utils = require('nvim-treesitter.ts_utils')
local winid = vim.fn.win_getid()
local nuc = ts_utils.get_node_at_cursor(winid)
local cn = nuc
while cn ~= nil and allowed_types[cn:type()] == nil
do
cn = cn:parent()
end
local start_row
local end_row
if cn ~= nil then
start_row, _, end_row, _ = cn:range()
vim.cmd(start_row + 1 .. "," .. end_row + 1 .. "fold")
end
end
local fold_node = function()
local ts_utils = require('nvim-treesitter.ts_utils')
local cn = ts_utils.get_node_at_cursor(0)
while cn ~= nil
do
local node_start_row, _, node_end_row, _ = cn:range()
if node_end_row == node_start_row then
cn = cn:parent()
else
vim.cmd(node_start_row + 1 .. "," .. node_end_row + 1 .. "fold")
break
end
end
end
vim.keymap.set('n', '<C-f>', function()
local fold_level = vim.fn.foldlevel('.')
local fold_closed = vim.fn.foldclosed('.')
if fold_level > 0 then
if fold_closed == -1 then
-- fold_named_arg_func()
fold_node()
else
vim.cmd("foldopen")
end
else
-- fold_named_arg_func()
fold_node()
end
end)
local cursor_to_node_ends = function()
local ts_utils = require('nvim-treesitter.ts_utils')
local nuc = ts_utils.get_node_at_cursor(0)
local node_start_row, node_start_col, node_end_row, node_end_col = nuc:range()
local current_cursor = vim.api.nvim_win_get_cursor(0)
local current_cursor_row = current_cursor[1] - 1
local current_cursor_col = current_cursor[2]
-- print(current_cursor_row, current_cursor_col, node_start_row, node_start_col, node_end_row, node_end_col)
-- check if at beginning of node, if so go to end
if current_cursor_row == node_start_row and current_cursor_col == node_start_col then
vim.api.nvim_win_set_cursor(0, { node_end_row + 1, node_end_col - 1 })
elseif current_cursor_row == node_end_row and current_cursor_col == node_end_col then
vim.api.nvim_win_set_cursor(0, { node_start_row + 1, node_start_col })
else
vim.api.nvim_win_set_cursor(0, { node_start_row + 1, node_start_col })
end
end
vim.keymap.set('n', '<C-e>', cursor_to_node_ends)
require('mkdnflow').setup({
links = {
transform_explicit = false,
},
mappings = {
MkdnDecreaseHeading = false,
},
})
require("oil").setup()
vim.keymap.set("n", "-", ":Oil<CR>", { desc = "Open parent directory" })
vim.opt.fillchars = { eob = "-", fold = " " }
vim.opt.foldtext = 'v:lua.MyFoldText()'
function MyFoldText()
local start_line = vim.fn.getline(vim.v.foldstart)
local end_line = vim.fn.getline(vim.v.foldend)
end_line = end_line:gsub("^%s+", "")
return start_line .. " ... " .. end_line
end
vim.keymap.set("v", "<C-f>", ":fold<CR>", { desc = "fold" })
-- primeagen: https://youtu.be/w7i4amO_zaE?t=1543
-- https://github.com/ThePrimeagen/init.lua/blob/master/lua/theprimeagen/remap.lua
vim.keymap.set("v", "J", ":m '>+1<CR>gv=gv")
vim.keymap.set("v", "K", ":m '<-2<CR>gv=gv")
vim.keymap.set("n", "J", "mzJ`z")
vim.keymap.set("n", "<C-d>", "<C-d>zz")
vim.keymap.set("n", "<C-u>", "<C-u>zz")
vim.keymap.set("n", "n", "nzzzv")
vim.keymap.set("n", "N", "Nzzzv")
-- greatest remap ever
vim.keymap.set("x", "<leader>p", [["_dP]])
require("nvim-tree").setup({
update_focused_file = {
enable = true,
update_root = false,
ignore_list = {},
},
view = {
preserve_window_proportions = true,
},
actions = {
open_file = {
resize_window = false,
},
},
})
vim.keymap.set('n', '<C-n>', require("nvim-tree.api").tree.toggle,
{ desc = "Toggle nvim-tree" })
-- require("ibl").setup({
-- debounce = 100,
-- -- indent = { char = "|" },
-- -- indent = { char = "│" },
-- indent = {
-- char = "│",
-- smart_indent_cap = true,
-- },
-- scope = {
-- enabled = true,
-- show_start = true,
-- show_end = true,
-- },
-- })
-- -- https://github.com/nvim-telescope/telescope.nvim
-- local builtin = require('telescope.builtin')
-- vim.keymap.set('n', '<leader>ff', builtin.find_files, { desc = 'Telescope find files' })
-- vim.keymap.set('n', '<leader>fg', builtin.live_grep, { desc = 'Telescope live grep' })
-- vim.keymap.set('n', '<leader>fb', builtin.buffers, { desc = 'Telescope buffers' })
-- vim.keymap.set('n', '<leader>fh', builtin.help_tags, { desc = 'Telescope help tags' })
--
-- -- This is your opts table
-- require("telescope").setup {
-- defaults = {
-- mappings = {
-- i = {
-- ["<C-y>"] = "select_default",
-- }
-- }
-- },
-- extensions = {
-- ["ui-select"] = {
-- require("telescope.themes").get_dropdown {
-- -- even more opts
-- }
--
-- -- pseudo code / specification for writing custom displays, like the one
-- -- for "codeactions"
-- -- specific_opts = {
-- -- [kind] = {
-- -- make_indexed = function(items) -> indexed_items, width,
-- -- make_displayer = function(widths) -> displayer
-- -- make_display = function(displayer) -> function(e)
-- -- make_ordinal = function(e) -> string
-- -- },
-- -- -- for example to disable the custom builtin "codeactions" display
-- -- do the following
-- -- codeactions = false,
-- -- }
-- }
-- }
-- }
-- -- To get ui-select loaded and working with telescope, you need to call
-- -- load_extension, somewhere after setup function:
-- require("telescope").load_extension("ui-select")
-- fzf-lua bindings
local fzf_lua = require('fzf-lua')
vim.keymap.set('n', '<Leader>ff', fzf_lua.files, { desc = 'find files'})