Austin Allman
PortfolioBitbucketResumeContact


-- Enable automatic folding of this file.
-- vim:set foldmethod=marker:

local api = vim.api
local bo  = vim.bo
local cmd = vim.cmd
local fn  = vim.fn
local g   = vim.g
local o   = vim.o
local opt = vim.opt
local t   = vim.t
local wo  = vim.wo

-- Variables {{{

opt.autoindent     = true
opt.cursorline     = true
opt.foldcolumn     = "4"
opt.hlsearch       = false
opt.laststatus     = 2
opt.list           = true
opt.listchars      = { tab = "¬ ", trail = "·", nbsp = "␣" }
opt.number         = true
opt.relativenumber = true
opt.scrolloff      = 8
opt.shiftwidth     = 2
opt.showcmd        = true
opt.showtabline    = 2
opt.sidescroll     = 1
opt.sidescrolloff  = 16
opt.signcolumn     = "auto"
opt.softtabstop    = 2
opt.spelllang      = "en_us"
opt.splitbelow     = true
opt.splitright     = true
opt.switchbuf      = "useopen,usetab"
opt.tabpagemax     = 100
opt.tabstop        = 2
opt.termguicolors  = true
opt.updatetime     = 250
opt.wildmenu       = true
opt.wrap           = false

cmd("set tagfunc=v:lua.vim.lsp.tagfunc")

g.mapleader    = ' '
g.netrw_banner = 0
g.use_coc      = 0

-- }}}
-- Mappings {{{

-- Map Related Functions {{{
local map = function(mode, lhs, rhs, opts)
  local options = opts or { noremap = true }
  vim.keymap.set(mode, lhs, rhs, options)
end

-- Initialize the custom variable to an empty dictionary
local my_diff_buffers = {}

function ToggleGitGutterDiffOrig()
  for k, v in pairs(my_diff_buffers) do
    if fn.bufnr('%') == k and api.nvim_buf_is_valid(v) or fn.bufnr('%') == v then
      -- The buffer is open, so close the split
      api.nvim_buf_delete(v, { force = true })
      my_diff_buffers[k] = nil
      return
    end
  end
  -- The buffer is closed, so open the split and store the buffer number
  if not t.DiffviewFilePanel and not t.DiffviewFileHistoryPanel then
    local splitdir = opt.splitright
    opt.splitright = false
    cmd("GitGutterDiffOrig")
    my_diff_buffers[fn.bufnr('%')] = fn.bufnr('$')
    opt.splitright = splitdir
  end
end

function FindTabgepageWithVar(var_name)
  -- get the number of tabpages
  local num_tabpages = fn.tabpagenr('$')
  -- loop through all tabpages
  for i = 1, num_tabpages do
    -- check if the variable exists on this tabpage
    local success, value = pcall(vim.api.nvim_tabpage_get_var, api.nvim_list_tabpages()[i], var_name)
    if success and value ~= nil then
      -- return the tabpagenr of the first tabpage where the variable is found
      return i
    end
  end

  -- return nil if the variable is not found on any tabpage
  return nil
end

function OpenDiffview()
  local nr = FindTabgepageWithVar("DiffviewFilePanel")
  if nr ~= nil then
    if fn.tabpagenr() == nr then
      cmd('tabclose!')
    else
      api.nvim_set_current_tabpage(api.nvim_list_tabpages()[nr])
    end
  else
    cmd("DiffviewOpen")
  end
end

function OpenFileHistory()
  local nr = FindTabgepageWithVar("DiffviewFileHistoryPanel")
  if nr ~= nil then
    if fn.tabpagenr() == nr then
      vim.t["DiffviewFileHistoryPanel"] = nil
      cmd('tabclose!')
    else
      api.nvim_set_current_tabpage(api.nvim_list_tabpages()[nr])
    end
  else
    cmd("DiffviewFileHistory")
  end
end

-- }}}

-- Fix arrow keys breaking when remapped
map('i', '<Esc>[A', '<Up>')
map('i', '<Esc>[B', '<Down>')
map('i', '<Esc>[C', '<Right>')
map('i', '<Esc>[D', '<Left>')

-- Make redo more sensible
map('n', 'U', '<C-R>')

-- Copy to system clipboard
map('v', '<C-c>', '"+y')
map('n', '<C-c>', '"+yy')

-- Navigation
map('n', '<Up>',   'gk',           { silent = true })
map('n', '<Down>', 'gj',           { silent = true })
map('i', '<Up>',   '<C-o>gk',      { silent = true })
map('i', '<Down>', '<C-o>gj',      { silent = true })
map('n', '<Home>', 'g<Home>',      { silent = true })
map('n', '<End>',  'g<End>',       { silent = true })
map('i', '<Home>', '<C-o>g<Home>', { silent = true })
map('i', '<End>',  '<C-o>g<End>',  { silent = true })

-- Tab navigation
map('n', '<C-Left>',  ':tabprevious<CR>',          { silent = true, noremap = true })
map('n', '<C-Right>', ':tabnext<CR>',              { silent = true, noremap = true })
map('n', '<A-Left>',  ':silent! tabmove -1<CR>',   { silent = true, noremap = true })
map('n', '<A-Right>', ':silent! tabmove +1<CR>',   { silent = true, noremap = true })
map('i', '<C-Left>',  '<Esc><C-Left>',             { silent = true })
map('i', '<C-Right>', '<Esc><C-Right>',            { silent = true })
map('i', '<A-Left>',  '<Esc><A-Left>',             { silent = true })
map('i', '<A-Right>', '<Esc><A-Right>',            { silent = true })
map('t', '<C-Left>',  '<C-\><C-o><C-Left><Esc>',  { silent = true })
map('t', '<C-Right>', '<C-\><C-o><C-Right><Esc>', { silent = true })
map('t', '<A-Left>',  '<C-\><C-o><A-Left>',       { silent = true })
map('t', '<A-Right>', '<C-\><C-o><A-Right>',      { silent = true })

-- Fold navigation
map('n', '<C-Up>',   'zk', { silent = true })
map('n', '<C-Down>', 'zj', { silent = true })
map('n', '<A-Up>',   '[z', { silent = true })
map('n', '<A-Down>', ']z', { silent = true })

-- Split navigation
map('n', '<leader><Left>',  '<C-W>h', { silent = true })
map('n', '<leader><Right>', '<C-W>l', { silent = true })
map('n', '<leader><Up>',    '<C-W>k', { silent = true })
map('n', '<leader><Down>',  '<C-W>j', { silent = true })

-- Resize Splits
map('n', '<C-S-Left>',  '<C-W><', { silent = true })
map('n', '<C-S-Right>', '<C-W>>', { silent = true })
map('n', '<C-S-Up>',    '<C-W>+', { silent = true })
map('n', '<C-S-Down>',  '<C-W>-', { silent = true })

-- Navigate the wildmenu
api.nvim_command('cnoremap <expr> <Up> wildmenumode() ? "<C-p>" : "<Up>"')
api.nvim_command('cnoremap <expr> <Down> wildmenumode() ? "<C-n>" : "<Down>"')

-- Press Esc twice to go from terminal into normal mode
map('t', '<Esc><Esc>', '<C-\><C-N>',                                          { silent = true, noremap = true })
map('n', '<C-d>',      ':if &buftype == "terminal" <Bar> q <Bar> endif <CR>"', { silent = true })

-- Toggle folds
map('n', '<leader><leader>', 'za', { silent = true })
map('v', '<leader><leader>', 'za', { silent = true })

-- Define function text-object
map('o', 'if', ':normal Vif<CR>')
map('v', 'if', 'aBob^V')

-- Goto files in a new tab
map('n', 'gf', '<C-w>gf')

-- Open fzf :Files
map('n', '<leader>f', ':Files<CR>', { noremap = true })
map('n', '<leader>p', ':GFiles<CR>', { noremap = true })

-- Goto definition using ctags if ctags is installed
map('n', '<C-]>', '<C-w><C-]><C-w>T')

-- Open terminal
map('n', "<leader>t", ":tabnew +term<CR>", { silent = true })

-- Popup spellsuggest
map('n', "<leader>s", "a<C-x>s", { silent = true })

-- Open help in a tab rather than a split
cmd('cnoreabbrev <expr> h getcmdtype() == ":" && getcmdline() == "h" ? "tab help" : "h"')

-- Toggle Margins
map('n', '<F1>', ':Margin<CR>', { silent = true, noremap = true })

-- Map the function to the F2 key
map('n', '<F2>', ToggleGitGutterDiffOrig, { silent = true })

-- Open Diffview File Window
map('n', '<F3>', OpenDiffview, { silent = true, noremap = true })

-- Open Diffview File History
map('n', '<F4>', OpenFileHistory, { silent = true })

-- Toggle files panel in File History
map('n', '<F5>', '<cmd>silent! DiffviewToggleFiles<CR>', { silent = true })

-- Toggle syntax highlighting (doesn't work with treesitter)
map('n', '<F6>', ':if exists("g:syntax_on") <Bar> syntax off <Bar> else <Bar> syntax enable <Bar> endif <CR>',
  { silent = true, noremap = true })

-- Toggle visibility of invisible characters
map('n', '<F7>', ':if &list <Bar> set nolist <Bar> else <Bar> set list <Bar> endif <CR>',
  { silent = true, noremap = true })

-- }}}
-- Commnds {{{

-- Enable wrapping without wrapping in the middle of a word
cmd('command! Wrap setlocal wrap linebreak breakat&vim')

-- For renaming files with numbers when bulk renaming in vifm
cmd([[command! -range Renumber let i=1 | <line1>,<line2>g/d*/ s//=i/ | let i+=1]])
cmd([[command! -range -nargs=1 Refrom let i=<f-args> | <line1>,<line2>g/d*/ s//=i/ | let i+=1]])

-- Show the highlight group of the current cursor position in vim
function SynGroup()
  local s = fn.synID(fn.line('.'), fn.col('.'), 1)
  print(fn.synIDattr(s, 'name') .. ' -> ' .. fn.synIDattr(fn.synIDtrans(s), 'name'))
end

cmd('command! SynGroup lua SynGroup()')

cmd('command! Gadd Git add % | echo "File added"')

local margin_left_buffers = {}
local margin_right_buffers = {}

function CreateMargins()
  for k, v in pairs(margin_left_buffers) do
    if fn.bufnr('%') == k or fn.bufnr('%') == v then
      -- The buffer is open, so close the split
      api.nvim_buf_delete(margin_left_buffers[k], { force = true })
      api.nvim_buf_delete(margin_right_buffers[k], { force = true })
      margin_left_buffers[k] = nil
      margin_right_buffers[k] = nil
      return
    end
  end
  for k, v in pairs(margin_right_buffers) do
    if fn.bufnr('%') == v then
      -- The buffer is open, so close the split
      api.nvim_buf_delete(margin_left_buffers[k], { force = true })
      api.nvim_buf_delete(margin_right_buffers[k], { force = true })
      margin_left_buffers[k] = nil
      margin_right_buffers[k] = nil
      return
    end
  end
  local split_width = fn.winwidth(0)
  local document_width = 80 --math.floor(columns * 0.65)
  local marginwidth = math.floor((split_width - document_width) / 2)

  -- Create left margin buffer
  local bufnum = fn.bufnr('%')
  cmd('leftabove vertical new')
  margin_left_buffers[bufnum] = fn.bufnr('$')
  cmd('vertical resize ' .. marginwidth - 1)
  wo.fillchars = 'eob: '
  bo.buftype = 'nofile'
  bo.bufhidden = 'hide'
  -- bo.nobuflisted = true
  bo.swapfile = false
  wo.wrap = false
  wo.relativenumber = false
  wo.number = false
  bo.readonly = true
  wo.cursorline = false
  wo.signcolumn = 'no'
  wo.foldcolumn = '0'
  cmd('normal! ggVGd')

  -- Create right margin buffer
  vim.cmd('wincmd l')
  vim.cmd('rightbelow vertical new')
  margin_right_buffers[bufnum] = fn.bufnr('$')
  vim.cmd('vertical resize ' .. (split_width - document_width) - marginwidth - 1)
  wo.fillchars = 'eob: '
  bo.buftype = 'nofile'
  bo.bufhidden = 'hide'
  -- bo.nobuflisted = true
  bo.swapfile = false
  wo.wrap = false
  wo.relativenumber = false
  wo.number = false
  bo.readonly = true
  wo.cursorline = false
  wo.signcolumn = 'no'
  wo.foldcolumn = '0'
  vim.cmd('normal! ggVGd')

  vim.cmd('wincmd p')
end

cmd('command! Margin lua CreateMargins()')
-- }}}
-- Autocommands {{{

cmd('filetype plugin on')

-- FileType {{{
-- Set indentation.
api.nvim_create_autocmd('FileType', { pattern = 'css',        command = 'setlocal expandtab' })
api.nvim_create_autocmd('FileType', { pattern = 'html',       command = 'setlocal expandtab' })
api.nvim_create_autocmd('FileType', { pattern = 'javascript', command = 'setlocal expandtab' })
api.nvim_create_autocmd('FileType', { pattern = 'lua',        command = 'setlocal expandtab' })
api.nvim_create_autocmd('FileType', { pattern = 'markdown',   command = 'setlocal expandtab' })
api.nvim_create_autocmd('FileType', { pattern = 'php',        command = 'setlocal expandtab shiftwidth=4 softtabstop=4 tabstop=4' })
api.nvim_create_autocmd('FileType', { pattern = 'python',     command = 'setlocal expandtab' })

-- Set linewrap
api.nvim_create_autocmd('FileType', { pattern = 'text',     command = 'setlocal wrap linebreak breakat&vim spell nonumber norelativenumber signcolumn=no foldcolumn=0' })
api.nvim_create_autocmd('FileType', { pattern = 'markdown', command = 'setlocal wrap linebreak breakat&vim spell nonumber norelativenumber signcolumn=no foldcolumn=0' })
-- }}}

-- Automatically enter insert mode when opening or switching to
-- a terminal. Automatically bypass the terminal exit message.
api.nvim_create_autocmd('TermOpen',    { pattern = '*', command = 'setlocal nonumber norelativenumber foldcolumn=0 signcolumn=no | startinsert' })
api.nvim_create_autocmd('TabEnter',    { pattern = '*', command = 'if &buftype == "terminal" | startinsert | endif' })
api.nvim_create_autocmd('TermClose',   { pattern = '*', command = 'call feedkeys("i\<Esc>")' })
api.nvim_create_autocmd('BufWritePre', { pattern = '*', command = [[%s/s+$//e]] })

-- api.nvim_create_autocmd('BufEnter,BufRead', { pattern = "*", command =[[lcd %:p:h | let git_toplevel = system('git rev-parse --show-toplevel 2>/dev/null') | if v:shell_error == 0 && git_toplevel != "" | execute 'lcd '.git_toplevel | endif]]})

-- }}}
-- Custom Tab Line {{{

g.begintab = 1
g.endtab = 1
g.previoustab = 1
g.direction = -1
g.blinking = 1

-- Set cursor character to blink on a timer on :terminal tab
local timer = vim.loop.new_timer()
local cursorchar = '█'
local cursor = cursorchar
timer:start(1000, 1000, function()
  if cursor == ' ' then
    cursor = '█'
  else
    cursor = ' '
  end
  if g.blinking == 0 then
    cursor = cursorchar
  end
end)
fn.timer_start(1000, function() cmd [[silent redrawtabline]] end, { ["repeat"] = -1 })

-- Get the name of the selected buffer on a tab page.
function GetTabName(tabnr)
  local bufnr = fn.tabpagebuflist(tabnr)[fn.tabpagewinnr(tabnr)]
  local filename = fn.fnamemodify(fn.bufname(bufnr), ':t')
  if filename == '' then
    filename = '[No Name]'
  end
  local success, value = pcall(vim.api.nvim_tabpage_get_var, api.nvim_list_tabpages()[tabnr], 'DiffviewFilePanel')
  if success and value ~= nil then
    -- return the tabpagenr of the first tabpage where the variable is found
    filename = "[Diffview Files]"
  end
  success, value = pcall(vim.api.nvim_tabpage_get_var, api.nvim_list_tabpages()[tabnr], 'DiffviewFileHistoryPanel')
  if success and value ~= nil then
    -- return the tabpagenr of the first tabpage where the variable is found
    filename = "[Diffview History]"
  end

  local j = ' '
  if fn.getbufvar(bufnr, '&modified') ~= 0 then
    j = j .. '+ '
  end
  if filename == 'bash' then
    local cwd = fn.getcwd()
    local current_folder = string.match(cwd, "/([^/]+)$")
    filename = current_folder
    if vim.env.USER == "root" then
      filename = filename .. '# ' .. cursor
    else
      filename = filename .. '$ ' .. cursor
    end
  end
  j = j .. filename
  j = j .. ' '
  return j
end

-- Generate the tabline text
function MyTabLine()
  local maxwidth = o.columns
  local s = ''
  local active = fn.tabpagenr()
  -- If the active tab has changed figure out which
  -- direction you tabbed
  if g.previoustab ~= active then
    if g.previoustab > active then
      g.direction = -1
    else
      g.direction = 1
    end
    g.previoustab = active
  end
  -- If you tab left set the right-most tab to be displayed
  -- to the current active tab number. If you tab right set
  -- the left-most tab to be displayed to the current active
  -- tab number.
  if g.direction == 1 then
    g.begintab = active
  else
    g.endtab = active
  end
  -- If the starting tab is greater than the ending tab reset both
  if g.begintab > g.endtab then
    g.begintab = active
    g.endtab = active
  end
  local numtabs = fn.tabpagenr('$')
  if g.endtab > numtabs then
    g.endtab = numtabs
  end

  -- Get the size of each individual tab.
  local sizes = {}
  for i = 1, numtabs do
    sizes[i] = fn.strwidth(GetTabName(i))
  end

  -- Figure out how many tabs will fit on screen by adding tabs in the opposite
  -- direction of scroll until they don't fit.
  local currentwidth = 10
  for i = g.begintab, g.endtab do
    currentwidth = currentwidth + sizes[i]
  end
  local numshown = g.endtab - g.begintab + 1
  while currentwidth < maxwidth and numshown < numtabs do
    if g.direction == 1 then
      if g.begintab > 1 then
        currentwidth = currentwidth + sizes[g.begintab - 1]
        if currentwidth < maxwidth then
          g.begintab = g.begintab - 1
        end
      else
        g.direction = -1
      end
    else
      if g.endtab < numtabs then
        currentwidth = currentwidth + sizes[g.endtab + 1]
        if currentwidth < maxwidth then
          g.endtab = g.endtab + 1
        end
      else
        g.direction = 1
      end
    end
    numshown = g.endtab - g.begintab + 1
  end

  -- Add left-scroll button
  if g.begintab > 1 then
    s = s .. '%#TabLineFill#'
    s = s .. ' '
    s = s .. '%#TabLine#'
    s = s .. ' < '
    s = s .. '%#TabLineFill#'
    s = s .. ' '
  end

  for i = g.begintab, g.endtab do
    -- set the highlight group for the active tab
    if i == active then
      s = s .. '%#TabLineSel#'
    else
      s = s .. '%#TabLine#'
    end
    s = s .. GetTabName(i)
  end

  -- Add right-scroll button
  if g.endtab < fn.tabpagenr('$') then
    s = s .. '%#TabLineFill#'
    s = s .. ' '
    s = s .. '%#TabLine#'
    s = s .. ' > '
    s = s .. '%#TabLineFill#'
    s = s .. ' '
  end

  -- fill the rest with TabLineFill
  s = s .. '%#TabLineFill#'
  s = s .. ' '

  -- return the tabline string
  return s
end

o.tabline = [[%!luaeval('MyTabLine()')]]

-- }}}
-- Custom Status Line {{{

o.statusline = ""
o.statusline = o.statusline .. "%{luaeval('GetGitBranch()')}"
o.statusline = o.statusline .. "%f"
o.statusline = o.statusline .. " %m"
o.statusline = o.statusline .. "%="
o.statusline = o.statusline .. "%l,%c     %P"

function GetGitBranch()
  local gitbranch = ""
  if bo.modifiable then
    local dir = vim.fn.expand('%:p:h')
    local gitrevparse = vim.fn.system("git -C " .. dir .. " rev-parse --abbrev-ref HEAD")
    if vim.v.shell_error == 0 then
      gitbranch = "(" .. vim.fn.substitute(gitrevparse, "n", "", "g") .. ") "
    end
  end
  return gitbranch
end

api.nvim_create_autocmd({ 'TermOpen', 'WinEnter', 'BufEnter' }, { pattern = '*', command = "lua GetGitBranch()" })

-- }}}
-- Custom Fold Text {{{

-- Define a function to set the FoldMarker highlight
local function set_foldmarker_highlight()
  local gui_fg = vim.fn.synIDattr(vim.fn.synIDtrans(vim.fn.hlID('Folded')), 'fg', 'gui')
  local cterm_fg = vim.fn.synIDattr(vim.fn.synIDtrans(vim.fn.hlID('Folded')), 'fg', 'cterm')

  if gui_fg == '' or cterm_fg == '' then
    -- vim.notify('FoldMarker highlight could not be set: fg values are empty.', vim.log.levels.WARN)
    return
  end

  vim.cmd(string.format('hi FoldMarker gui=NONE cterm=NONE guibg=NONE ctermbg=NONE guifg=%s ctermfg=%s', gui_fg, cterm_fg))
end

-- Set the FoldMarker highlight initially
set_foldmarker_highlight()

-- Set up an autocommand to update the FoldMarker highlight when the colorscheme changes
vim.api.nvim_create_autocmd('ColorScheme', {
  callback = set_foldmarker_highlight
})

-- A prettier formatting for folds. Used primarily for folding functions
cmd([[
autocmd BufEnter,BufRead * match FoldMarker /([a-zA-Z]+(.*){{{.*|}}})/
set foldtext=MyFoldText()
function! MyFoldText()
  if &diff
    return repeat('=',&columns)
  endif
  let line = getline(v:foldstart)
  let line2 = getline(v:foldstart+1)
  let n = v:foldend - v:foldstart + 1
  let info = '  ' . n . " lines"
  if match( line, '^[ t]*(/*|//)[*/\]*[ t]*$' ) == 0
    let initial = substitute( line, '^([ t])*(/*|//)(.*)', '12', '' )
    let linenum = v:foldstart + 1
    while linenum < v:foldend
      let line = getline( linenum )
      let comment_content = substitute( line, '^([ t/*]*)(.*)$', '2', 'g' )
      if comment_content != ''
        break
      endif
      let linenum = linenum + 1
    endwhile
    let sub = initial . ' ' . comment_content
  else
    let indent_level = indent(v:foldstart)
    let indent = repeat(' ',indent_level)
    let sub = substitute( line, '^[ t]*(.*)$', indent.'1', 'g')
    let startbrace = substitute( line, '^.*{[ t]*$', '{', 'g')
    let startbrace2 = substitute( line2, '^[ t]*{[ t]*$', '{', 'g')
    if startbrace2 == '{'
      let startbrace = startbrace2
      let sub = sub . ' {'
    endif
    if startbrace == '{'
      let startbrace = '{'
      let line = getline(v:foldend)
      let endbrace = substitute( line, '^[ t]*}(.*)$', '}', 'g')
      if endbrace == '}'
        let sub = sub.substitute( line, '^[ t]*}(.*)$', '...}1', 'g')
      endif
    endif
  endif
  let sub = substitute(sub, '([ t]*)W*(.*){'.'{{.*', '1* 2', '')
  let editor_width = winwidth(0) - getwininfo(win_getid())[0].textoff
  let sub = sub . repeat(' ', winsaveview().leftcol + winwidth(0) )
  let sub = strpart( sub, winsaveview().leftcol, editor_width - strlen( info ) - 1)
  return sub . info . " "
endfunction
]])

-- }}}
-- Install Plugins {{{

local ensure_lazy = function()
  local lazypath = vim.fn.stdpath('data') .. '/lazy/lazy.nvim'
  if not vim.loop.fs_stat(lazypath) then
    vim.fn.system({
      'git',
      'clone',
      '--filter=blob:none',
      'https://github.com/folke/lazy.nvim.git',
      '--branch=stable', -- latest stable release
      lazypath,
    })
  end
  vim.opt.rtp:prepend(lazypath)
end

ensure_lazy()

-- Using Lazy.nvim
require('lazy').setup({
  -- Plugin manager itself
  { 'folke/lazy.nvim' },

  -- Theme
  { 'drazil100/dusklight.vim' },

  -- Treesitter
  {
    'nvim-treesitter/nvim-treesitter',
    build = ':TSUpdate',
  },
  { 'nvim-treesitter/playground' },

  -- Git related plugins
  { 'airblade/vim-gitgutter' },
  { 'sindrets/diffview.nvim',           dependencies = { 'nvim-lua/plenary.nvim', 'nvim-tree/nvim-web-devicons' } },
  { 'tpope/vim-fugitive' },

  -- Utility plugins
  { 'tpope/vim-surround' },
  { 'tpope/vim-commentary' },
  { 'tpope/vim-repeat' },
  { 'vim-scripts/guicolorscheme.vim' },
  { 'markonm/traces.vim' },
  { 'NvChad/nvim-colorizer.lua' },

  -- LSP Zero
  { 'VonHeikemen/lsp-zero.nvim',        branch = 'v4.x' },
  { 'williamboman/mason.nvim' },
  { 'williamboman/mason-lspconfig.nvim' },
  { 'neovim/nvim-lspconfig' },
  { 'hrsh7th/cmp-nvim-lsp' },
  { 'hrsh7th/nvim-cmp' },

  -- FZF
  { 'junegunn/fzf',                     build = ':call fzf#install()' },
  { 'junegunn/fzf.vim' },
})


-- }}}
-- Configure Plugins {{{

-- Configure Dusklight {{{
-- Use personal colorscheme
cmd('colorscheme dusklight')
-- }}}
-- Configure Colorizer {{{

require("colorizer").setup {
  filetypes = { "*" },
  user_default_options = {
    RGB = true,          -- #RGB hex codes
    RRGGBB = true,       -- #RRGGBB hex codes
    names = true,        -- "Name" codes like Blue or blue
    RRGGBBAA = true,     -- #RRGGBBAA hex codes
    AARRGGBB = false,    -- 0xAARRGGBB hex codes
    rgb_fn = true,       -- CSS rgb() and rgba() functions
    hsl_fn = true,       -- CSS hsl() and hsla() functions
    css = true,          -- Enable all CSS features: rgb_fn, hsl_fn, names, RGB, RRGGBB
    css_fn = false,      -- Enable all CSS *functions*: rgb_fn, hsl_fn
    -- Available modes for `mode`: foreground, background,  virtualtext
    mode = "background", -- Set the display mode.
    -- Available methods are false / true / "normal" / "lsp" / "both"
    -- True is same as normal
    tailwind = false,                                -- Enable tailwind colors
    -- parsers can contain values used in |user_default_options|
    sass = { enable = false, parsers = { "css" }, }, -- Enable sass colors
    virtualtext = "■",
    -- update color values even if buffer is not focused
    -- example use: cmp_menu, cmp_docs
    always_update = false
  },
  -- all the sub-options of filetypes apply to buftypes
  buftypes = {},
}

-- }}}
-- Configure Diffview {{{
local actions = require("diffview.actions")

require("diffview").setup({
  hooks = {
    view_opened = function(view)
      if view.class:name() == 'FileHistoryView' then
        t.DiffviewFileHistoryPanel = true
      end
      if view.class:name() == 'DiffView' then
        t.DiffviewFilePanel = true
      end
    end,
  }, -- See ':h diffview-config-hooks'
})
-- }}}
-- Configure Gitgutter {{{
g.gitgutter_sign_added = '|+'
g.gitgutter_sign_modified = '|±'
g.gitgutter_sign_removed = '|-'
g.gitgutter_sign_removed_first_line = '|⊼'
g.gitgutter_sign_modified_removed = '|≥'
-- }}}
-- Configure LSP Zero {{{
local lsp_zero = require('lsp-zero')

local lsp_attach = function(client, bufnr)
  local opts = { buffer = bufnr }

  vim.keymap.set('n', 'K', '<cmd>lua vim.lsp.buf.hover()<cr>', opts)
  vim.keymap.set('n', 'gd', '<cmd>lua vim.lsp.buf.definition()<cr>', opts)
  vim.keymap.set("n", "gD", ":split | lua vim.lsp.buf.definition()<CR><C-w>T", opts)
  vim.keymap.set('n', 'gi', '<cmd>lua vim.lsp.buf.implementation()<cr>', opts)
  vim.keymap.set('n', 'go', '<cmd>lua vim.lsp.buf.type_definition()<cr>', opts)
  vim.keymap.set('n', 'gr', '<cmd>lua vim.lsp.buf.references()<cr>', opts)
  vim.keymap.set('n', 'gs', '<cmd>lua vim.lsp.buf.signature_help()<cr>', opts)
end

lsp_zero.extend_lspconfig({
  sign_text = true,
  lsp_attach = lsp_attach,
  capabilities = require('cmp_nvim_lsp').default_capabilities(),
})

local lspconfig = require('lspconfig')

local servers = {
  'tsserver',
  'jedi_language_server',
  'lua_ls',
  'bashls',
  'vimls',
  'eslint',
}

lsp_zero.setup_servers(servers);

require('mason').setup({})
require('mason-lspconfig').setup({
  ensure_installed = servers,
  handlers = {
    function(server_name)
      lspconfig[server_name].setup({})
    end,
    lua_ls = function()
      lspconfig.lua_ls.setup({
        on_init = function(client)
          lsp_zero.nvim_lua_settings(client, {})
        end,
      })
    end,
  },
})

local cmp = require('cmp')

cmp.setup({
  sources = {
    {name = 'tsserver'},
    {name = 'jedi_language_server'},
    {name = 'lua_ls'},
    {name = 'bashls'},
    {name = 'vimls'},
    {name = 'eslint'},
    {name = 'nvim_lsp'},
  },
  mapping = {
    ['<CR>'] = cmp.mapping.confirm({ select = true }),
    ['<Tab>'] = cmp.mapping.confirm({ select = true }),
    ['<Up>'] = cmp.mapping(function(fallback)
      if cmp.visible() then
        cmp.select_prev_item({ behavior = cmp.SelectBehavior.Select })
        if cmp.get_selected_entry() == nil then
          cmp.select_prev_item({ behavior = cmp.SelectBehavior.Select })
        end
      else
        fallback()
      end
    end, { 'i', 's' }),
    ['<Down>'] = cmp.mapping(function(fallback)
      if cmp.visible() then
        local selected_entry = cmp.get_selected_entry()
        if selected_entry == nil then
          cmp.select_next_item({ behavior = cmp.SelectBehavior.Select })
          cmp.select_next_item({ behavior = cmp.SelectBehavior.Select })
        else
          cmp.select_next_item({ behavior = cmp.SelectBehavior.Select })
          selected_entry = cmp.get_selected_entry()
          if selected_entry == nil then
            cmp.select_next_item({ behavior = cmp.SelectBehavior.Select })
          end
        end
      else
        fallback()
      end
    end, { 'i', 's' }),
  },
  snippets = {
    expand = function(args)
    end
  }
})

-- }}}
-- Configure Treesitter {{{
require 'nvim-treesitter.configs'.setup {
  -- A list of parser names, or "all" (the five listed parsers should always be installed)
  ensure_installed = {
    "c",
    "cpp",
    "c_sharp",
    "css",
    "html",
    "javascript",
    "lua",
    "markdown",
    -- "php",
    "query",
    "vimdoc",
  },

  -- Install parsers synchronously (only applied to `ensure_installed`)
  sync_install = false,

  -- Automatically install missing parsers when entering buffer
  -- Recommendation: set to false if you don't have `tree-sitter` CLI installed locally
  auto_install = true,

  ignore_install = { "vim" },

  highlight = {
    enable = true,

    -- Setting this to true will run `:h syntax` and tree-sitter at the same time.
    -- Set this to `true` if you depend on 'syntax' being enabled (like for indentation).
    -- Using this option may slow down your editor, and you may see some duplicate highlights.
    -- Instead of true it can also be a list of languages
    additional_vim_regex_highlighting = false,
  },
  playground = {
    enable = true,
    disable = {},
    updatetime = 25,         -- Debounced time for highlighting nodes in the playground from source code
    persist_queries = false, -- Whether the query persists across vim sessions
    keybindings = {
      toggle_query_editor = 'o',
      toggle_hl_groups = 'i',
      toggle_injected_languages = 't',
      toggle_anonymous_nodes = 'a',
      toggle_language_display = 'I',
      focus_language = 'f',
      unfocus_language = 'F',
      update = 'R',
      goto_node = '<cr>',
      show_help = '?',
    },
  },
}

-- }}}
-- Configure FZF {{{
-- Open files in a new tab when using fzf
g.fzf_action = { enter = 'tab split' }
-- }}}

-- }}}