Skip to content

index

disable completion temporarily

vim.b.completion = false

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

source: https://vi.stackexchange.com/questions/36509/how-do-i-install-a-package-in-neovim-without-a-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:

vim.api.nvim_create_autocmd('BufEnter', { command = "echo 'Hello'", })

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("![%s](img/%s)", 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]
visual range: getpos('v') -- [2] and [3]
nvim_win_get_cursor(0) [1] and [2]

Note

vim.fn.getpos('v')

to

vim.fn.getpos('.')

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("![%s](img/%s)", 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'})

Comments