aboutsummaryrefslogtreecommitdiff
path: root/common
diff options
context:
space:
mode:
Diffstat (limited to 'common')
-rwxr-xr-xcommon/nvim/.gitignore4
-rwxr-xr-xcommon/nvim/.luacheckrc5
-rwxr-xr-xcommon/nvim/after/ftplugin/c.lua9
-rwxr-xr-xcommon/nvim/after/ftplugin/lua.lua.bak35
-rwxr-xr-xcommon/nvim/after/ftplugin/markdown.lua37
-rwxr-xr-xcommon/nvim/after/ftplugin/vim.lua18
-rwxr-xr-xcommon/nvim/autoload/statusline.vim267
-rwxr-xr-xcommon/nvim/autoload/utils.vim238
-rwxr-xr-xcommon/nvim/colors/colorscheme.vim247
-rwxr-xr-xcommon/nvim/init.lua152
-rw-r--r--common/nvim/lsp/bashls.lua4
-rw-r--r--common/nvim/lsp/clangd.lua5
-rw-r--r--common/nvim/lsp/cssls.lua4
-rw-r--r--common/nvim/lsp/gopls.lua41
-rw-r--r--common/nvim/lsp/html.lua4
-rw-r--r--common/nvim/lsp/jsonls.lua4
-rw-r--r--common/nvim/lsp/lua_ls.lua20
-rw-r--r--common/nvim/lsp/pyright.lua12
-rw-r--r--common/nvim/lsp/rust_analyzer.lua5
-rw-r--r--common/nvim/lsp/ts_ls.lua8
-rw-r--r--common/nvim/lsp/yamlls.lua4
-rwxr-xr-xcommon/nvim/lua/plugins/auto-session.lua39
-rwxr-xr-xcommon/nvim/lua/plugins/autopairs.lua99
-rwxr-xr-xcommon/nvim/lua/plugins/cmp-gh-source.lua70
-rwxr-xr-xcommon/nvim/lua/plugins/cmp.lua67
-rwxr-xr-xcommon/nvim/lua/plugins/colorizer.lua8
-rwxr-xr-xcommon/nvim/lua/plugins/colorscheme.lua24
-rwxr-xr-xcommon/nvim/lua/plugins/comment.lua125
-rwxr-xr-xcommon/nvim/lua/plugins/dap.lua265
-rwxr-xr-xcommon/nvim/lua/plugins/dashboard.lua126
-rwxr-xr-xcommon/nvim/lua/plugins/fidget.lua34
-rwxr-xr-xcommon/nvim/lua/plugins/friendly-snippets.lua3
-rwxr-xr-xcommon/nvim/lua/plugins/fugitive.lua8
-rwxr-xr-xcommon/nvim/lua/plugins/fzf.lua43
-rwxr-xr-xcommon/nvim/lua/plugins/git.lua8
-rwxr-xr-xcommon/nvim/lua/plugins/gitsigns.lua85
-rwxr-xr-xcommon/nvim/lua/plugins/goto-preview.lua31
-rwxr-xr-xcommon/nvim/lua/plugins/hardtime.lua29
-rwxr-xr-xcommon/nvim/lua/plugins/harpoon.lua50
-rwxr-xr-xcommon/nvim/lua/plugins/heirline.lua1497
-rwxr-xr-xcommon/nvim/lua/plugins/indent-blankline.lua73
-rwxr-xr-xcommon/nvim/lua/plugins/interestingwords.lua499
-rwxr-xr-xcommon/nvim/lua/plugins/leetcode.lua68
-rwxr-xr-xcommon/nvim/lua/plugins/loclist.lua18
-rwxr-xr-xcommon/nvim/lua/plugins/lsp.lua674
-rwxr-xr-xcommon/nvim/lua/plugins/lualine.lua22
-rwxr-xr-xcommon/nvim/lua/plugins/luasnip.lua13
-rwxr-xr-xcommon/nvim/lua/plugins/messages.lua85
-rwxr-xr-xcommon/nvim/lua/plugins/modify-blend.lua43
-rwxr-xr-xcommon/nvim/lua/plugins/navic.lua51
-rwxr-xr-xcommon/nvim/lua/plugins/neodev.lua45
-rwxr-xr-xcommon/nvim/lua/plugins/neoscroll.lua22
-rwxr-xr-xcommon/nvim/lua/plugins/neotest.lua38
-rwxr-xr-xcommon/nvim/lua/plugins/notify.lua36
-rwxr-xr-xcommon/nvim/lua/plugins/nvim-tree.lua479
-rwxr-xr-xcommon/nvim/lua/plugins/overseer.lua14
-rwxr-xr-xcommon/nvim/lua/plugins/plenary.lua3
-rwxr-xr-xcommon/nvim/lua/plugins/prettier.lua8
-rwxr-xr-xcommon/nvim/lua/plugins/quickfix.lua15
-rwxr-xr-xcommon/nvim/lua/plugins/snippets.lua33
-rwxr-xr-xcommon/nvim/lua/plugins/sniprun.lua57
-rwxr-xr-xcommon/nvim/lua/plugins/statuscol.lua37
-rwxr-xr-xcommon/nvim/lua/plugins/surround.lua35
-rwxr-xr-xcommon/nvim/lua/plugins/telescope.lua740
-rwxr-xr-xcommon/nvim/lua/plugins/toggleterm.lua294
-rwxr-xr-xcommon/nvim/lua/plugins/treesitter.lua54
-rwxr-xr-xcommon/nvim/lua/plugins/trouble.lua73
-rwxr-xr-xcommon/nvim/lua/plugins/vimtex.lua45
-rwxr-xr-xcommon/nvim/lua/plugins/web-devicons.lua125
-rwxr-xr-xcommon/nvim/lua/plugins/which-key.lua53
-rwxr-xr-xcommon/nvim/lua/plugins/zen-mode.lua7
-rwxr-xr-xcommon/nvim/lua/setup/compat.lua104
-rwxr-xr-xcommon/nvim/lua/setup/manager.lua811
-rwxr-xr-xcommon/nvim/lua/setup/plugins.lua609
-rwxr-xr-xcommon/nvim/lua/user/keys.lua928
-rwxr-xr-xcommon/nvim/lua/user/mods.lua1427
-rwxr-xr-xcommon/nvim/lua/user/opts.lua438
-rwxr-xr-xcommon/nvim/lua/user/view.lua180
-rwxr-xr-xcommon/nvim/neovim.ps1917
-rwxr-xr-xcommon/nvim/neovim.sh516
-rw-r--r--common/nvim/snippets/boilerplate.lua75
-rw-r--r--common/nvim/snippets/lua.lua264
-rw-r--r--common/nvim/snippets/markdown.lua58
83 files changed, 13820 insertions, 0 deletions
diff --git a/common/nvim/.gitignore b/common/nvim/.gitignore
new file mode 100755
index 0000000..f6f2fa2
--- /dev/null
+++ b/common/nvim/.gitignore
@@ -0,0 +1,4 @@
+plugin/packer_compiled.lua
+startup.log
+tmp
+*.log
diff --git a/common/nvim/.luacheckrc b/common/nvim/.luacheckrc
new file mode 100755
index 0000000..26f9f67
--- /dev/null
+++ b/common/nvim/.luacheckrc
@@ -0,0 +1,5 @@
+-- .luacheckrc
+globals = {
+ "vim",
+ -- Add other Neovim globals like 'require', 'rawset', etc., if needed
+}
diff --git a/common/nvim/after/ftplugin/c.lua b/common/nvim/after/ftplugin/c.lua
new file mode 100755
index 0000000..6af8a5c
--- /dev/null
+++ b/common/nvim/after/ftplugin/c.lua
@@ -0,0 +1,9 @@
+-- Fix C filetype comments
+vim.api.nvim_create_autocmd("Filetype", {
+ pattern = "c",
+ callback = function()
+ vim.bo.commentstring = "//%s"
+ end,
+ group = comment_augroup,
+})
+
diff --git a/common/nvim/after/ftplugin/lua.lua.bak b/common/nvim/after/ftplugin/lua.lua.bak
new file mode 100755
index 0000000..fe9587b
--- /dev/null
+++ b/common/nvim/after/ftplugin/lua.lua.bak
@@ -0,0 +1,35 @@
+local lspconfig = require("lspconfig")
+
+if lspconfig.lua_ls then
+ lspconfig.lua_ls.setup({
+ settings = {
+ Lua = {
+ diagnostics = {
+ -- This is the Lua table for diagnostics settings
+ globals = { "vim", "use", "_G", "packer_plugins", "P" },
+ disable = {
+ "undefined-global",
+ "lowercase-global",
+ "unused-local",
+ "unused-vararg",
+ "trailing-space"
+ },
+ },
+ workspace = {
+ -- Points the language server to Neovim's runtime files for auto-completion
+ library = {
+ --vim.api.nvim_get_runtime_path(),
+ --checkThirdParty = false,
+ vim.env.VIMRUNTIME,
+ -- Depending on the usage, you might want to add additional paths here.
+ "${3rd}/luv/library",
+ "${3rd}/busted/library",
+ },
+ },
+ telemetry = {
+ enable = false,
+ },
+ },
+ },
+ })
+end
diff --git a/common/nvim/after/ftplugin/markdown.lua b/common/nvim/after/ftplugin/markdown.lua
new file mode 100755
index 0000000..5941402
--- /dev/null
+++ b/common/nvim/after/ftplugin/markdown.lua
@@ -0,0 +1,37 @@
+vim.wo.spell = true
+vim.bo.spelllang = "en"
+vim.wo.wrap = true
+vim.wo.linebreak = true
+vim.wo.breakindent = true
+vim.wo.colorcolumn = "0"
+--vim.wo.conceallevel = 3
+vim.opt.softtabstop = 2 -- Tab key indents by 2 spaces.
+vim.opt.shiftwidth = 2 -- >> indents by 2 spaces.
+-- vim.g.markdown_recommended_style = 0 -- prevents markdown from changing tabs to 4 spaces
+
+vim.b[0].undo_ftplugin = "setlocal nospell nowrap nolinebreak nobreakindent conceallevel=0"
+
+vim.cmd([[
+ autocmd FileType markdown iabbrev <buffer> `` ``
+]])
+
+require("nvim-surround").buffer_setup({
+ surrounds = {
+ -- ["e"] = {
+ -- add = function()
+ -- local env = require("nvim-surround.config").get_input ("Environment: ")
+ -- return { { "\\begin{" .. env .. "}" }, { "\\end{" .. env .. "}" } }
+ -- end,
+ -- },
+ ["b"] = {
+ add = { "**", "**" },
+ find = "**.-**",
+ delete = "^(**)().-(**)()$",
+ },
+ ["i"] = {
+ add = { "_", "_" },
+ find = "_.-_",
+ delete = "^(_)().-(_)()$",
+ },
+ },
+})
diff --git a/common/nvim/after/ftplugin/vim.lua b/common/nvim/after/ftplugin/vim.lua
new file mode 100755
index 0000000..7823f73
--- /dev/null
+++ b/common/nvim/after/ftplugin/vim.lua
@@ -0,0 +1,18 @@
+vim.opt_local.tabstop = 4
+vim.opt_local.shiftwidth = 4
+vim.opt_local.softtabstop = 4
+vim.opt_local.expandtab = true
+vim.opt_local.autoindent = true
+vim.opt_local.smartindent = true
+
+--vim.api.nvim_create_autocmd("FileType", {
+-- pattern = "vim",
+-- callback = function()
+-- vim.opt_local.tabstop = 4
+-- vim.opt_local.shiftwidth = 4
+-- vim.opt_local.softtabstop = 4
+-- vim.opt_local.expandtab = true
+-- vim.opt_local.autoindent = true
+-- vim.opt_local.smartindent = true
+-- end,
+--})
diff --git a/common/nvim/autoload/statusline.vim b/common/nvim/autoload/statusline.vim
new file mode 100755
index 0000000..bf5f972
--- /dev/null
+++ b/common/nvim/autoload/statusline.vim
@@ -0,0 +1,267 @@
+" statusline.vim
+
+if exists('g:loaded_statusline') | finish | endif
+let g:loaded_statusline = 1
+
+" --- Detect Nerd Fonts ---
+function! s:HasNerdFonts()
+ if exists('g:statusline_nerd_fonts')
+ return g:statusline_nerd_fonts
+ endif
+
+ if executable('fc-list')
+ let l:output = system('fc-list | grep -i nerd')
+ if len(split(l:output, '\n')) > 0
+ return 1
+ endif
+ endif
+
+ return 0
+endfunction
+
+let g:statusline_has_nerd_fonts = s:HasNerdFonts()
+
+" --- Color Palette ---
+let g:StslineColorGreen = '#2BBB4F'
+let g:StslineColorBlue = '#4799EB'
+let g:StslineColorViolet = '#986FEC'
+let g:StslineColorYellow = '#D7A542'
+let g:StslineColorOrange = '#EB754D'
+let g:StslineColorLight = '#C0C0C0'
+let g:StslineColorDark = '#080808'
+let g:StslineColorDark1 = '#181818'
+let g:StslineColorDark2 = 'NONE'
+let g:StslineColorDark3 = '#303030'
+
+let g:StslineBackColor = g:StslineColorDark2
+let g:StslineOnBackColor = g:StslineColorLight
+let g:StslinePriColor = g:StslineColorGreen
+let g:StslineOnPriColor = g:StslineColorDark
+let g:StslineSecColor = g:StslineColorDark3
+let g:StslineOnSecColor = g:StslineColorLight
+
+" --- Highlight Groups ---
+" Initial setup of highlight groups (will be updated by UpdateStslineColors)
+execute 'highlight StslinePriColorBG guifg=' . g:StslineOnPriColor . ' guibg=' . g:StslinePriColor
+execute 'highlight StslineSecColorFG guifg=' . g:StslineSecColor . ' guibg=' . g:StslineBackColor
+execute 'highlight StslineSecColorBG guifg=' . g:StslineColorLight . ' guibg=' . g:StslineSecColor
+execute 'highlight StslineBackColorBG guifg=' . g:StslineColorLight . ' guibg=' . g:StslineBackColor
+execute 'highlight StslineBackColorFGSecColorBG guifg=' . g:StslineBackColor . ' guibg=' . g:StslineSecColor
+execute 'highlight StslineSecColorFGBackColorBG guifg=' . g:StslineSecColor . ' guibg=' . g:StslineBackColor
+execute 'highlight StslineModColorFG guifg=' . g:StslineColorYellow . ' guibg=' . g:StslineBackColor
+execute 'highlight StslinePriColorBG_SecColorBG guifg=' . g:StslinePriColor . ' guibg=' . g:StslineSecColor
+execute 'highlight StslineModeSep guifg=' . g:StslinePriColor . ' guibg=' . g:StslineSecColor
+execute 'highlight StslineGitSep guifg=' . g:StslineSecColor . ' guibg=' . g:StslineColorDark2
+
+" --- Statusline Settings ---
+if has('nvim')
+ set laststatus=3
+else
+ set laststatus=2
+endif
+
+"set noshowmode
+"set termguicolors
+
+let space = ''
+
+" Get Statusline mode & also set primary color for that mode
+function! autoload#statusline#StslineMode() abort
+ let l:CurrentMode = mode()
+
+ if l:CurrentMode ==# 'n'
+ let g:StslinePriColor = g:StslineColorGreen
+ let b:CurrentMode = 'NORMAL '
+ elseif l:CurrentMode ==# 'i'
+ let g:StslinePriColor = g:StslineColorViolet
+ let b:CurrentMode = 'INSERT '
+ elseif l:CurrentMode ==# 'c'
+ let g:StslinePriColor = g:StslineColorYellow
+ let b:CurrentMode = 'COMMAND'
+ elseif l:CurrentMode ==# 'v'
+ let g:StslinePriColor = g:StslineColorBlue
+ let b:CurrentMode = 'VISUAL '
+ elseif l:CurrentMode ==# '\<C-v>'
+ let g:StslinePriColor = g:StslineColorBlue
+ let b:CurrentMode = 'V-BLOCK'
+ elseif l:CurrentMode ==# 'V'
+ let g:StslinePriColor = g:StslineColorBlue
+ let b:CurrentMode = 'V-LINE '
+ elseif l:CurrentMode ==# 'R'
+ let g:StslinePriColor = g:StslineColorViolet
+ let b:CurrentMode = 'REPLACE'
+ elseif l:CurrentMode ==# 's'
+ let g:StslinePriColor = g:StslineColorBlue
+ let b:CurrentMode = 'SELECT '
+ elseif l:CurrentMode ==# 't'
+ let g:StslinePriColor = g:StslineColorYellow
+ let b:CurrentMode = 'TERM '
+ elseif l:CurrentMode ==# '!'
+ let g:StslinePriColor = g:StslineColorYellow
+ let b:CurrentMode = 'SHELL '
+ else
+ let g:StslinePriColor = g:StslineColorGreen
+ endif
+
+ call autoload#statusline#UpdateStslineColors()
+
+ return b:CurrentMode
+endfunction
+
+function! autoload#statusline#UpdateStslineColors() abort
+ execute 'highlight StslinePriColorBG guifg=' . g:StslineOnPriColor . ' guibg=' . g:StslinePriColor
+ execute 'highlight StslinePriColorBGBold guifg=' . g:StslineOnPriColor . ' guibg=' . g:StslinePriColor . ' gui=bold'
+ execute 'highlight StslinePriColorFG guifg=' . g:StslinePriColor . ' guibg=' . g:StslineBackColor
+ execute 'highlight StslinePriColorFGSecColorBG guifg=' . g:StslinePriColor . ' guibg=' . g:StslineSecColor
+ execute 'highlight StslineModeSep guifg=' . g:StslinePriColor . ' guibg=' . g:StslineSecColor
+ execute 'highlight StslineGitSep guifg=' . g:StslineSecColor . ' guibg=' . g:StslineColorDark2
+ execute 'highlight StslineSecColorBG guifg=' . g:StslineColorLight . ' guibg=' . g:StslineSecColor
+ execute 'highlight StslineBackColorBG guifg=' . g:StslineColorLight . ' guibg=' . g:StslineBackColor
+ execute 'highlight StslineBackColorFGSecColorBG guifg=' . g:StslineBackColor . ' guibg=' . g:StslineSecColor
+ execute 'highlight StslineSecColorFGBackColorBG guifg=' . g:StslineSecColor . ' guibg=' . g:StslineBackColor
+ execute 'highlight StslineModColorFG guifg=' . g:StslineColorYellow . ' guibg=' . g:StslineBackColor
+ execute 'highlight StslinePriColorBG_SecColorBG guifg=' . g:StslinePriColor . ' guibg=' . g:StslineSecColor
+ execute 'highlight StslineSecColorFG guifg=' . g:StslineSecColor . ' guibg=' . g:StslineBackColor
+endfunction
+
+function! autoload#statusline#GetGitBranch() abort
+ let b:GitBranch = ''
+ try
+ let l:dir = expand('%:p:h')
+ let l:gitrevparse = system("git -C ".l:dir." rev-parse --abbrev-ref HEAD")
+ if !v:shell_error
+ let icon = g:statusline_has_nerd_fonts ? '  ' : ' [git] '
+ let b:GitBranch = icon . substitute(l:gitrevparse, '\n', '', 'g') . ' '
+ endif
+ catch
+ endtry
+endfunction
+
+function! autoload#statusline#GetFileType() abort
+ if !g:statusline_has_nerd_fonts
+ let b:FiletypeIcon = ''
+ return
+ endif
+ if &filetype ==# 'typescript' | let b:FiletypeIcon = ' '
+ elseif &filetype ==# 'html' | let b:FiletypeIcon = ' '
+ elseif &filetype ==# 'scss' | let b:FiletypeIcon = ' '
+ elseif &filetype ==# 'css' | let b:FiletypeIcon = ' '
+ elseif &filetype ==# 'javascript' | let b:FiletypeIcon = ' '
+ elseif &filetype ==# 'javascriptreact' | let b:FiletypeIcon = ' '
+ elseif &filetype ==# 'markdown' | let b:FiletypeIcon = ' '
+ elseif &filetype ==# 'sh' || &filetype ==# 'zsh' | let b:FiletypeIcon = ' '
+ elseif &filetype ==# 'vim' | let b:FiletypeIcon = ' '
+ elseif &filetype ==# 'rust' | let b:FiletypeIcon = ' '
+ elseif &filetype ==# 'ruby' | let b:FiletypeIcon = ' '
+ elseif &filetype ==# 'cpp' | let b:FiletypeIcon = ' '
+ elseif &filetype ==# 'c' | let b:FiletypeIcon = ' '
+ elseif &filetype ==# 'go' | let b:FiletypeIcon = ' '
+ elseif &filetype ==# 'lua' | let b:FiletypeIcon = ' '
+ elseif &filetype ==# 'haskell' | let b:FiletypeIcon = ' '
+ else | let b:FiletypeIcon = ' '
+ endif
+endfunction
+
+function! autoload#statusline#ActivateStatusline() abort
+ call autoload#statusline#GetFileType()
+ call autoload#statusline#GetGitBranch() " Ensure git branch is updated
+
+ let current_mode_str = autoload#statusline#StslineMode()
+ call autoload#statusline#UpdateStslineColors()
+
+ let readonly_icon = g:statusline_has_nerd_fonts ? ' ' : '[RO] '
+ let modified_icon = g:statusline_has_nerd_fonts ? ' ' : '[+] '
+ let git_sep = g:statusline_has_nerd_fonts ? '' : ' '
+ let file_sep1 = g:statusline_has_nerd_fonts ? ' ' : ' '
+ let file_sep2 = g:statusline_has_nerd_fonts ? '' : ''
+
+ " Get dynamic parts as simple strings
+ let git_status_str = get(b:, "coc_git_status", get(b:, "GitBranch", ""))
+ let git_blame_str = get(b:, "coc_git_blame", "")
+ let filetype_icon_str = get(b:, "FiletypeIcon", "")
+ let file_encoding_str = ''
+ if &fenc != "utf-8"
+ let file_encoding_str = &fenc . ' '
+ endif
+
+ " Build the statusline as a static string
+ let l:statusline = ''
+
+ let l:statusline .= '%#StslinePriColorBG# ' . current_mode_str . ''
+ let l:statusline .= '%#StslineModeSep#' . git_sep
+ let l:statusline .= '%#StslineSecColorBG#' . git_status_str . git_blame_str
+ let l:statusline .= '%#StslineGitSep#' . git_sep
+
+ " File info (Readonly, Modified, Filename)
+ let l:statusline .= '%#StslinePriColorFG#'
+ if &readonly
+ let l:statusline .= readonly_icon
+ endif
+ let l:statusline .= ' %F '
+ if &modified
+ let l:statusline .= modified_icon
+ endif
+
+ " Right align everything after this
+ let l:statusline .= '%='
+
+ " Right side (Filetype, Encoding, Position)
+ let l:statusline .= '%#StslinePriColorFG# ' . filetype_icon_str . '%y'
+ let l:statusline .= '%#StslineSecColorFG#' . file_sep1
+ "let l:statusline .= '%#StslineSecColorBG# ' . file_encoding_str
+ let l:statusline .= '%#StslinePriColorFGSecColorBG#' . file_sep2
+ let l:statusline .= '%#StslinePriColorBG# %p%% %#StslinePriColorBGBold#%l%#StslinePriColorBG#/%L :%c '
+ let l:statusline .= '%#StslineBackColorBG#'
+
+ " Set the statusline for the current buffer
+ let &l:statusline = l:statusline
+endfunction
+
+function! autoload#statusline#DeactivateStatusline() abort
+ let git_sep = g:statusline_has_nerd_fonts ? '' : ''
+ let readonly_icon = g:statusline_has_nerd_fonts ? ' ' : '[RO] '
+ let modified_icon = g:statusline_has_nerd_fonts ? ' ' : '[+] '
+
+ " NOTE: This DeactivateStatusline function still uses %{} for dynamic parts.
+ " If you encounter general E518 or other issues related to %{} expressions,
+ " you will need to refactor this function to build a static string
+ " similar to how ActivateStatusline now does it.
+ if !exists("b:GitBranch") || b:GitBranch == ''
+ let statusline =
+ \ '%#StslineSecColorBG# INACTIVE ' .
+ \ '%{get(b:,"coc_git_statusline",b:GitBranch)}%{get(b:,"coc_git_blame","")}' .
+ \ '%#StslineBackColorFGSecColorBG#' . git_sep .
+ \ '%#StslineBackColorBG# %{&readonly?"' . readonly_icon . '":""}%F ' .
+ \ '%#StslineModColorFG#%{&modified?"' . modified_icon . '":""}' .
+ \ '%=%#StslineBackColorBG# %{b:FiletypeIcon}%{&filetype}' .
+ \ '%#StslineSecColorFGBackColorBG# | %p%% %l/%L :%c'
+ else
+ let statusline =
+ \ '%#StslineSecColorBG# %{get(b:,"coc_git_statusline",b:GitBranch)}%{get(b:,"coc_git_blame","")}' .
+ \ '%#StslineBackColorFGSecColorBG#' . git_sep .
+ \ '%#StslineBackColorBG# %{&readonly?"' . readonly_icon . '":""}%F ' .
+ \ '%#StslineModColorFG#%{&modified?"' . modified_icon . '":""}' .
+ \ '%=%#StslineBackColorBG# %{b:FiletypeIcon}%{&filetype}' .
+ \ '%#StslineSecColorFGBackColorBG# | %p%% %l/%L :%c'
+ endif
+
+ execute 'setlocal statusline=' . substitute(statusline, '"', '\\"', 'g')
+endfunction
+
+augroup StatuslineGit
+ autocmd!
+ autocmd BufEnter * call autoload#statusline#GetGitBranch()
+augroup END
+
+augroup SetStsline
+ autocmd!
+ autocmd BufEnter,WinEnter * call autoload#statusline#ActivateStatusline()
+ autocmd ModeChanged * call autoload#statusline#ActivateStatusline()
+augroup END
+
+augroup StatuslineAutoReload
+ autocmd!
+ autocmd BufWritePost statusline.vim source <afile> | call autoload#statusline#ActivateStatusline()
+augroup END
+
+"call autoload#statusline#ActivateStatusline()
diff --git a/common/nvim/autoload/utils.vim b/common/nvim/autoload/utils.vim
new file mode 100755
index 0000000..d92b771
--- /dev/null
+++ b/common/nvim/autoload/utils.vim
@@ -0,0 +1,238 @@
+" Toggle Zoom
+function! utils#ZoomToggle()
+ if exists('t:zoomed') && t:zoomed
+ execute t:zoom_winrestcmd
+ let t:zoomed = 0
+ else
+ let t:zoom_winrestcmd = winrestcmd()
+ resize
+ vertical resize
+ let t:zoomed = 1
+ endif
+endfunction
+"command! ZoomToggle call ZoomToggle()
+
+
+"-------------------------------------------------
+
+" Toggle DiagnosticsOpenFloat
+" Enable DiagnosticsOpenFloat by default
+"let g:DiagnosticsOpenFloat = 1
+"
+"" Define the autocmd group on startup
+"augroup OpenFloat
+" autocmd!
+" autocmd CursorHold * lua if vim.g.DiagnosticsOpenFloat then vim.diagnostic.open_float(nil, { focusable = false }) end
+"augroup END
+
+" Toggle function
+function! utils#ToggleDiagnosticsOpenFloat()
+ " Flip the toggle
+ let g:DiagnosticsOpenFloat = !get(g:, 'DiagnosticsOpenFloat', 1)
+endfunction
+
+" Command to toggle
+command! ToggleDiagnosticsOpenFloat call utils#ToggleDiagnosticsOpenFloat()
+
+"-------------------------------------------------
+
+" Toggle transparency
+let t:is_transparent = 0
+function! utils#Toggle_transparent_background()
+ if t:is_transparent == 0
+ hi Normal guibg=#111111 ctermbg=black
+ let t:is_transparent = 1
+ else
+ hi Normal guibg=NONE ctermbg=NONE
+ let t:is_transparent = 0
+ endif
+endfunction
+"nnoremap <leader>tb :call Toggle_transparent_background()<CR>
+
+
+"-------------------------------------------------
+
+" Toggle statusline
+let s:hidden_all = 0
+function! utils#ToggleHiddenAll()
+ if s:hidden_all == 0
+ let s:hidden_all = 1
+ set noshowmode
+ set noruler
+ set laststatus=0
+ set noshowcmd
+ else
+ let s:hidden_all = 0
+ set showmode
+ set ruler
+ set laststatus=2
+ set showcmd
+ endif
+endfunction
+"nnoremap <S-h> :call ToggleHiddenAll()<CR>
+
+
+"-------------------------------------------------
+
+" Open last closed buffer
+function! utils#OpenLastClosed()
+ let last_buf = bufname('#')
+ if empty(last_buf)
+ echo "No recently closed buffer found"
+ return
+ endif
+ let result = input("Open ". last_buf . " in (n)ormal (v)split, (t)ab or (s)plit ? (n/v/t/s) : ")
+ if empty(result) || (result !=# 'v' && result !=# 't' && result !=# 's' && result !=# 'n')
+ return
+ endif
+ if result ==# 't'
+ execute 'tabnew'
+ elseif result ==# 'v'
+ execute "vsplit"
+ elseif result ==# 's'
+ execute "split"
+ endif
+ execute 'b ' . last_buf
+endfunction
+
+
+"-------------------------------------------------
+
+" Toggle Diff
+let g:diff_is_open = 0
+
+function! utils#ToggleDiff()
+ if g:diff_is_open
+ windo diffoff
+ let g:diff_is_open = 0
+ else
+ windo diffthis
+ let g:diff_is_open = 1
+ endif
+endfunction
+
+
+"-------------------------------------------------
+
+" Verbose Toggle
+function! utils#VerboseToggle()
+ if !&verbose
+ set verbosefile=~/.config/nvim/verbose.log
+ set verbose=15
+ else
+ set verbose=0
+ set verbosefile=
+ endif
+endfunction
+
+
+"-------------------------------------------------
+
+" Jump List
+function! utils#GotoJump()
+ jumps
+ let j = input("Please select your jump: ")
+ if j != ''
+ let pattern = '\v\c^\+'
+ if j =~ pattern
+ let j = substitute(j, pattern, '', 'g')
+ execute "normal " . j . "\<c-i>"
+ else
+ execute "normal " . j . "\<c-o>"
+ endif
+ endif
+endfunction
+
+
+"-------------------------------------------------
+
+" Disable annoying auto line break
+fu! utils#DisableBr()
+ set wrap
+ set linebreak
+ set nolist " list disables linebreak
+ set textwidth=0
+ set wrapmargin=0
+ set formatoptions-=t
+endfu
+
+" Disable line breaks for all file types
+autocmd! BufNewFile,BufRead *.* call utils#DisableBr()
+
+
+"-------------------------------------------------
+
+" Annoying timestamp issue on write (The file has been changed since reading it...)
+"function! utils#ProcessFileChangedShell()
+" if v:fcs_reason == 'mode' || v:fcs_reason == 'time'
+" let v:fcs_choice = ''
+" else
+" let v:fcs_choice = 'ask'
+" endif
+"endfunction
+"autocmd FileChangedShell <buffer> call utils#ProcessFileChangedShell()
+"
+"let lastline = line('$')
+"let bufcontents = getline(1, lastline)
+"edit!
+"call setline(1, bufcontents)
+"if line('$') > lastline
+" execute lastline+1.',$:d _'
+"endif
+
+" Annoying timestamp issue on write (The file has been changed since reading it...)
+function! utils#ProcessFileChangedShell()
+ if v:fcs_reason == 'mode' || v:fcs_reason == 'time'
+ let v:fcs_choice = ''
+ else
+ let v:fcs_choice = 'ask'
+ endif
+endfunction
+
+" Triggered when the file is changed externally
+autocmd FileChangedShell <buffer> call utils#ProcessFileChangedShell()
+
+" Triggered before writing the buffer to the file
+autocmd BufWritePre <buffer> call utils#BeforeWrite()
+
+function! utils#BeforeWrite()
+ let lastline = line('$')
+ let bufcontents = getline(1, lastline)
+ edit!
+ call setline(1, bufcontents)
+ if line('$') > lastline
+ execute lastline+1.',$:d _'
+ endif
+endfunction
+
+
+"-------------------------------------------------
+
+" On The Fly Table mode
+function! s:isAtStartOfLine(mapping)
+ let text_before_cursor = getline('.')[0 : col('.')-1]
+ let mapping_pattern = '\V' . escape(a:mapping, '\')
+ let comment_pattern = '\V' . escape(substitute(&l:commentstring, '%s.*$', '', ''), '\')
+ return (text_before_cursor =~? '^' . ('\v(' . comment_pattern . '\v)?') . '\s*\v' . mapping_pattern . '\v$')
+endfunction
+
+
+"-------------------------------------------------
+
+" :Rename {newname}
+function! utils#RenameFile()
+ let old_name = expand('%')
+ let new_name = input('New file name: ', expand('%'), 'file')
+ if new_name != '' && new_name != old_name
+ exec ':saveas ' . new_name
+ exec ':silent !rm ' . old_name
+ redraw!
+ endif
+endfunction
+
+augroup obsidian
+ autocmd!
+ autocmd Filetype markdown set conceallevel=2
+augroup END
+
+"-------------------------------------------------
diff --git a/common/nvim/colors/colorscheme.vim b/common/nvim/colors/colorscheme.vim
new file mode 100755
index 0000000..ce0526e
--- /dev/null
+++ b/common/nvim/colors/colorscheme.vim
@@ -0,0 +1,247 @@
+" Vim Colorscheme
+" Name: cherryblossom.vim
+" Author: Luo Boming
+" Version: 0.3
+" License: The MIT Licence
+
+"{{{ Pre-setting
+let g:colors_name = expand('<sfile>:t:r')
+
+"hi clear
+"if exists("syntax_on")
+" syntax reset
+"endif
+
+if ! exists("g:terminal_italics")
+ let g:terminal_italics = 0
+endif
+
+"if ! exists("g:switch_statusline_bg_in_insert")
+" let g:switch_statusline_bg_in_insert = 0
+"endif
+
+if ! exists("g:spell_undercurl")
+ let g:spell_undercurl = 0
+endif
+
+"}}}
+"{{{ Color Palette
+" Color Entity
+let s:black = { "gui": "#171717", "cterm": "16" }
+let s:white = { "gui": "#EAE8E7", "cterm": "231" }
+
+let s:gray = { "gui": "#3a3f52", "cterm": "247" }
+
+let s:green = { "gui": "#30B536", "cterm": "34" }
+let s:pink = { "gui": "#D36DD3", "cterm": "170" }
+let s:orange = { "gui": "#FC923F", "cterm": "208" }
+let s:purple = { "gui": "#B586E7", "cterm": "141" }
+let s:light_cyan = { "gui": "#D7FFFF", "cterm": "195" }
+let s:dark_cyan = { "gui": "#00AF87", "cterm": "36" }
+let s:ultramarine = { "gui": "#229EC0", "cterm": "38" }
+let s:skyblue = { "gui": "#9BE7F8", "cterm": "195" }
+
+let s:white_pink = { "gui": "#FEF7FE", "cterm": "231" }
+let s:white_pink_deep = { "gui": "#FEF0FE", "cterm": "255" }
+let s:black_green = { "gui": "#053703", "cterm": "235" }
+let s:black_green_bright = { "gui": "#074005", "cterm": "239" }
+let s:middle_gray = { "gui": "#8a8a8a", "cterm": "245" }
+
+let s:light_gray = { "gui": "#E1DCDA", "cterm": "253" }
+let s:light_green = { "gui": "#B7EFA5", "cterm": "157" }
+let s:light_pink = { "gui": "#FEDCFE", "cterm": "225" }
+let s:light_yellow = { "gui": "#EDE682", "cterm": "228" }
+let s:light_red = { "gui": "#EB5A7C", "cterm": "204" }
+
+let s:dark_gray = { "gui": "#4D4A48", "cterm": "241" }
+let s:dark_green = { "gui": "#09570A", "cterm": "22" }
+let s:dark_yellow = { "gui": "#BC922B", "cterm": "3" }
+let s:dark_pink = { "gui": "#B365A2", "cterm": "133" }
+let s:dark_red = { "gui": "#D9372D", "cterm": "160" }
+let s:NONE = { "gui": "NONE", "cterm": "NONE" }
+
+" Color Alias
+if &background == "light"
+ let s:norm = s:black
+ let s:bg = s:NONE
+ let s:bg_subtle = s:white_pink_deep
+ let s:gray_fg = s:middle_gray
+ let s:green_fg = s:green
+ let s:yellow_fg = s:dark_yellow
+ let s:pink_fg = s:dark_pink
+ let s:cyan_fg = s:dark_cyan
+ let s:blue_fg = s:ultramarine
+ let s:red_fg = s:dark_red
+ let s:gray_bg = s:light_gray
+ let s:green_bg = s:light_green
+ let s:yellow_bg = s:light_yellow
+ let s:pink_bg = s:light_pink
+ let s:cyan_bg = s:light_cyan
+ let s:blue_bg = s:skyblue
+ let s:red_bg = s:light_red
+endif
+
+if &background == "dark"
+ let s:norm = s:white
+ let s:bg = s:NONE
+ let s:bg_subtle = s:gray
+ let s:gray_fg = s:middle_gray
+ let s:green_fg = s:light_green
+ let s:yellow_fg = s:light_yellow
+ let s:pink_fg = s:light_pink
+ let s:cyan_fg = s:light_cyan
+ let s:blue_fg = s:skyblue
+ let s:red_fg = s:light_red
+ let s:gray_bg = s:dark_gray
+ let s:green_bg = s:green
+ let s:yellow_bg = s:dark_yellow
+ let s:pink_bg = s:pink
+ let s:cyan_bg = s:dark_cyan
+ let s:blue_bg = s:ultramarine
+ let s:red_bg = s:dark_red
+endif
+"}}}
+"{{{ Highlight Function
+" shamelessly stolen from pencil: https://github.com/reedes/vim-colors-pencil
+function! s:hi(group, style)
+ if g:terminal_italics == 0
+ if has_key(a:style, "cterm") && a:style["cterm"] == "italic"
+ unlet a:style.cterm
+ endif
+ if has_key(a:style, "term") && a:style["term"] == "italic"
+ unlet a:style.term
+ endif
+ endif
+ execute "highlight" a:group
+ \ "guifg=" (has_key(a:style, "fg") ? a:style.fg.gui : "NONE")
+ \ "guibg=" (has_key(a:style, "bg") ? a:style.bg.gui : "NONE")
+ \ "guisp=" (has_key(a:style, "sp") ? a:style.sp.gui : "NONE")
+ \ "gui=" (has_key(a:style, "gui") ? a:style.gui : "NONE")
+ \ "ctermfg=" (has_key(a:style, "fg") ? a:style.fg.cterm : "NONE")
+ \ "ctermbg=" (has_key(a:style, "bg") ? a:style.bg.cterm : "NONE")
+ \ "cterm=" (has_key(a:style, "cterm") ? a:style.cterm : "NONE")
+ \ "term=" (has_key(a:style, "term") ? a:style.term : "NONE")
+endfunction
+
+if g:spell_undercurl == 1
+ let s:attr_un = 'undercurl'
+else
+ let s:attr_un = 'underline'
+endif
+
+"}}}
+"{{{ Common Highlighting
+call s:hi("Normal", {"fg": s:norm, "bg": s:bg})
+call s:hi("Cursor", {})
+call s:hi("Comment", {"fg": s:gray_fg, "gui": "italic", "cterm": "italic", "term": "italic"})
+
+call s:hi("Constant", {"fg": s:pink_fg})
+hi! link String Constant
+hi! link Character Constant
+hi! link Number Constant
+hi! link Boolean Constant
+hi! link Float Constant
+
+call s:hi("Identifier", {"fg": s:red_fg})
+hi! link Function Identifier
+
+call s:hi("Statement", {"fg": s:green_fg})
+hi! link Conditonal Statement
+hi! link Repeat Statement
+hi! link Label Statement
+hi! link Operator Statement
+hi! link Keyword Statement
+hi! link Exception Statement
+
+call s:hi("PreProc", {"fg": s:blue_fg})
+hi! link Include PreProc
+hi! link Define PreProc
+hi! link Macro PreProc
+hi! link PreCondit PreProc
+
+call s:hi("Type", {"fg": s:yellow_fg})
+hi! link StorageClass Type
+hi! link Structure Type
+hi! link Typedef Type
+
+call s:hi("Special", {"fg": s:orange})
+hi! link SpecialChar Special
+hi! link Tag Special
+hi! link Delimiter Special
+hi! link SpecialComment Special
+hi! link Debug Special
+
+call s:hi("Underlined", {"gui": "underline", "cterm": "underline"})
+call s:hi("Ignore", {"fg": s:bg_subtle})
+call s:hi("Error", {"fg": s:white, "bg": s:red_fg , "gui": "bold", "cterm": "bold"})
+call s:hi("Todo", {"bg": s:yellow_bg, "gui": "bold", "cterm": "bold"})
+
+"}}}
+"{{{ Semi-Common Highlighting
+call s:hi("SpecialKey", {"fg": s:purple, "gui": "bold", "cterm": "bold", "term": "bold"})
+call s:hi("NonText", {"fg": s:cyan_bg, "gui": "bold", "cterm": "bold", "term": "bold"})
+call s:hi("Directory", {"fg": s:blue_fg, "gui": "bold", "cterm": "bold", "term": "bold"})
+call s:hi("ErrorMsg", {"fg": s:red_fg, "gui": "bold", "cterm": "bold", "term": "bold"})
+call s:hi("IncSearch", {"gui": "reverse", "cterm": "reverse", "term": "reverse"})
+call s:hi("Search", {"fg": s:norm, "bg": s:pink_bg})
+call s:hi("MoreMsg", {"fg": s:pink_fg, "gui": "bold", "cterm": "bold", "term": "bold"})
+call s:hi("ModeMsg", {"fg": s:pink_fg, "gui": "bold", "cterm": "bold", "term": "bold"})
+call s:hi("LineNr", {"fg": s:gray})
+call s:hi("CursorLineNr", {"fg": s:pink_fg, "gui": "bold", "cterm": "bold", "term": "bold"})
+call s:hi("Question", {"fg": s:purple, "gui": "bold", "cterm": "bold", "term": "bold"})
+"call s:hi("StatusLine", {"fg": s:norm, "bg": s:green_bg, "gui": "bold", "cterm": "bold", "term": "bold"})
+"call s:hi("StatusLineNC", {"fg": s:norm, "bg": s:gray_bg})
+call s:hi("Conceal", {"fg": s:yellow_fg})
+call s:hi("VertSplit", {"gui": "reverse", "cterm": "reverse", "term": "reverse"})
+call s:hi("Title", {"fg": s:pink_fg, "gui": "bold", "cterm": "bold", "term": "bold"})
+call s:hi("Visual", {"gui": "reverse", "cterm": "reverse", "term": "reverse"})
+call s:hi("VisualNOS", {"gui": "bold,underline", "cterm": "bold,underline", "term": "bold,underline"})
+call s:hi("WarningMsg", {"fg": s:orange, "gui": "bold", "cterm": "bold", "term": "bold"})
+call s:hi("WildMenu", {"fg": s:norm, "bg": s:blue_bg})
+call s:hi("Folded", {"fg": s:green_fg, "bg": s:gray_bg})
+call s:hi("FoldColumn", {"fg": s:green_fg, "bg": s:gray_bg})
+call s:hi("DiffAdd", {"bg": s:green_bg})
+call s:hi("DiffChange", {"bg": s:yellow_bg})
+call s:hi("DiffDelete", {"bg": s:red_bg})
+call s:hi("DiffText", {"bg": s:blue_bg, "gui": "bold", "cterm": "bold", "term": "bold"})
+call s:hi("SignColumn", {"fg": s:green_fg, "bg": s:gray})
+if has("gui_running")
+ call s:hi("SpellBad", {"gui": s:attr_un, "sp": s:red_bg})
+ call s:hi("SpellCap", {"gui": s:attr_un, "sp": s:yellow_bg})
+ call s:hi("SpellRare", {"gui": s:attr_un, "sp": s:blue_bg})
+ call s:hi("SpellLocal", {"gui": s:attr_un, "sp": s:green_bg})
+else
+ call s:hi("SpellBad", {"cterm": s:attr_un, "fg": s:red_fg})
+ call s:hi("SpellCap", {"cterm": s:attr_un, "fg": s:yellow_fg})
+ call s:hi("SpellRare", {"cterm": s:attr_un, "fg": s:blue_fg})
+ call s:hi("SpellLocal", {"cterm": s:attr_un, "fg": s:green_fg})
+endif
+call s:hi("Pmenu", {"bg": s:gray_bg})
+call s:hi("PmenuSel", {"bg": s:pink_bg})
+call s:hi("PmenuSbar", {"bg": s:gray_bg})
+call s:hi("PmenuThumb", {"bg": s:gray_bg})
+call s:hi("TabLine", {"bg": s:bg_subtle})
+call s:hi("TabLineSel", {"bg": s:pink_bg})
+call s:hi("TabLineFill", {"bg": s:bg_subtle})
+call s:hi("CursorColumn", {"bg": s:yellow_fg})
+call s:hi("CursorLine", {"bg": s:bg_subtle})
+call s:hi("ColorColumn", {"bg": s:bg_subtle})
+call s:hi("MatchParen", {"fg": s:pink_fg, "gui": "underline", "cterm": "underline"})
+call s:hi("qfLineNr", {"fg": s:gray})
+
+"}}}
+""{{{ Switching StatusLine bg
+"function! s:changebg(group, color)
+" execute "highlight" a:group "guibg=" a:color.gui "ctermbg=" a:color.cterm
+"endfunction
+"
+"if g:switch_statusline_bg_in_insert == 1
+" "" Change Color when entering Insert Mode
+" autocmd InsertEnter * call s:changebg("StatusLine", s:pink_bg)
+" "" Revert Color to default when leaving Insert Mode
+" autocmd InsertLeave * call s:changebg("StatusLine", s:green_bg)
+"endif
+
+"}}}
+" vim: set foldmethod=marker:
+
diff --git a/common/nvim/init.lua b/common/nvim/init.lua
new file mode 100755
index 0000000..75ca825
--- /dev/null
+++ b/common/nvim/init.lua
@@ -0,0 +1,152 @@
+--[[
+ ███╗ ██╗███████╗ ██████╗ ██╗ ██╗██╗███╗ ███╗
+ ████╗ ██║██╔════╝██╔═══██╗██║ ██║██║████╗ ████║
+ ██╔██╗ ██║█████╗ ██║ ██║██║ ██║██║██╔████╔██║
+ ██║╚██╗██║██╔══╝ ██║ ██║╚██╗ ██╔╝██║██║╚██╔╝██║
+ ██║ ╚████║███████╗╚██████╔╝ ╚████╔╝ ██║██║ ╚═╝ ██║
+ ╚═╝ ╚═══╝╚══════╝ ╚═════╝ ╚═══╝ ╚═╝╚═╝ ╚═╝
+ ------------------------------------------------------------------------------
+ Author : srdusr
+ URL : https://github.com/srdusr/nvim.git
+ Description : System-agnostic, backwards-compatible config.
+ Bootstraps packer/lazy/builtin based on availability.
+ Use :PackerSync, :Lazy install, or built-in (v0.12+).
+ ------------------------------------------------------------------------------
+--]]
+
+-- Load impatient (Faster loading times)
+local impatient_ok, impatient = pcall(require, "impatient")
+if impatient_ok then
+ impatient.enable_profile()
+end
+
+-- Schedule reading shadafile to improve the startup time
+vim.opt.shadafile = "NONE"
+vim.schedule(function()
+ vim.opt.shadafile = ""
+ vim.cmd("silent! rsh")
+end)
+
+-- Improve speed by disabling some default plugins/modules
+local builtins = {
+ "gzip",
+ "zip",
+ "zipPlugin",
+ "tar",
+ "tarPlugin",
+ "getscript",
+ "getscriptPlugin",
+ "vimball",
+ "vimballPlugin",
+ "2html_plugin",
+ --"matchit",
+ --"matchparen",
+ "logiPat",
+ "rrhelper",
+ "tutor_mode_plugin",
+ "spellfile_plugin",
+ "sleuth",
+ "fzf",
+}
+
+local enable_netrw = true
+local ok, _ = pcall(require, "nvim-tree")
+if ok then
+ enable_netrw = false
+end
+
+if not enable_netrw then
+ vim.g.loaded_netrw = 1
+ vim.g.loaded_netrwPlugin = 1
+ vim.g.loaded_netrwSettings = 1
+ vim.g.loaded_netrwFileHandlers = 1
+end
+
+for _, plugin in ipairs(builtins) do
+ vim.g["loaded_" .. plugin] = 1
+end
+
+
+-- Load/reload modules
+local modules = {
+ -- SETUP/MANAGER --
+ "setup.compat", -- Backwards compatibility/future proofing
+ "setup.manager", -- Package Manager (builtin/packer/lazy)
+ "setup.plugins", -- Plugins list
+
+ -- USER/CORE --
+ "user.keys", -- Keymaps
+ "user.mods", -- Modules/functions
+ "user.opts", -- Options
+ "user.view", -- Colorscheme/UI
+
+ -- PLUGINS --
+ "plugins.auto-session",
+ "plugins.treesitter",
+ "plugins.web-devicons",
+ "plugins.telescope",
+ "plugins.fzf",
+ "plugins.nvim-tree",
+ "plugins.neodev",
+ "plugins.lsp",
+ "plugins.cmp",
+ "plugins.quickfix",
+ "plugins.colorizer",
+ "plugins.prettier",
+ "plugins.git",
+ "plugins.fugitive",
+ "plugins.snippets",
+ "plugins.gitsigns",
+ "plugins.sniprun",
+ "plugins.surround",
+ "plugins.neoscroll",
+ "plugins.statuscol",
+ "plugins.trouble",
+ "plugins.goto-preview",
+ "plugins.autopairs",
+ "plugins.navic",
+ "plugins.toggleterm",
+ "plugins.zen-mode",
+ --"plugins.fidget",
+ "plugins.dap",
+ "plugins.neotest",
+ "plugins.heirline",
+ "plugins.indent-blankline",
+ "plugins.dashboard",
+ "plugins.which-key",
+ "plugins.harpoon",
+ "plugins.leetcode",
+ --"plugins.hardtime",
+ "plugins.notify",
+ "plugins.overseer",
+ "plugins.vimtex",
+ "plugins.interestingwords",
+
+ --"plugins.nvim-tree",
+ --"plugins.telescope",
+ --"plugins.heirline",
+ --"plugins.fzf",
+ --"",
+
+}
+
+-- Refresh module cache
+--for _, mod in ipairs(modules) do
+-- package.loaded[mod] = nil
+-- pcall(require, mod)
+--end
+
+for _, mod in ipairs(modules) do
+ local ready, loaded = pcall(require, mod)
+ if ready and type(loaded) == "table" and loaded.setup then
+ local success, err = pcall(loaded.setup)
+ if not success then
+ vim.notify(string.format("Error setting up %s: %s", mod, err), vim.log.levels.ERROR)
+ end
+ elseif not ready then
+ vim.notify(string.format("Failed to load %s: %s", mod, loaded), vim.log.levels.WARN)
+ end
+end
+
+--require("setup.manager").setup() -- Setup all managers
+--require("user.view").setup() -- Colors/UI
diff --git a/common/nvim/lsp/bashls.lua b/common/nvim/lsp/bashls.lua
new file mode 100644
index 0000000..fc7d709
--- /dev/null
+++ b/common/nvim/lsp/bashls.lua
@@ -0,0 +1,4 @@
+return {
+ cmd = { "bash-language-server", "start" },
+ filetypes = { "sh", "bash" }
+} \ No newline at end of file
diff --git a/common/nvim/lsp/clangd.lua b/common/nvim/lsp/clangd.lua
new file mode 100644
index 0000000..4a19600
--- /dev/null
+++ b/common/nvim/lsp/clangd.lua
@@ -0,0 +1,5 @@
+return {
+ cmd = { "clangd", "--background-index", "--clang-tidy", "--header-insertion=iwyu" },
+ filetypes = { "c", "cpp", "objc", "objcpp", "cuda", "proto" },
+ root_markers = { ".clangd", ".clang-tidy", ".clang-format", "compile_commands.json", "compile_flags.txt", "configure.ac" }
+} \ No newline at end of file
diff --git a/common/nvim/lsp/cssls.lua b/common/nvim/lsp/cssls.lua
new file mode 100644
index 0000000..e734c19
--- /dev/null
+++ b/common/nvim/lsp/cssls.lua
@@ -0,0 +1,4 @@
+return {
+ cmd = { "vscode-css-language-server", "--stdio" },
+ filetypes = { "css", "scss", "less" }
+} \ No newline at end of file
diff --git a/common/nvim/lsp/gopls.lua b/common/nvim/lsp/gopls.lua
new file mode 100644
index 0000000..cf959c4
--- /dev/null
+++ b/common/nvim/lsp/gopls.lua
@@ -0,0 +1,41 @@
+return {
+ cmd = { "gopls" },
+ filetypes = { "go", "gomod", "gowork", "gotmpl" },
+ root_markers = { "go.work", "go.mod" },
+ settings = {
+ gopls = {
+ analyses = {
+ fieldalignment = true,
+ nilness = true,
+ unusedparams = true,
+ unusedwrite = true,
+ useany = true
+ },
+ codelenses = {
+ gc_details = false,
+ generate = true,
+ regenerate_cgo = true,
+ run_govulncheck = true,
+ test = true,
+ tidy = true,
+ upgrade_dependency = true,
+ vendor = true
+ },
+ completeUnimported = true,
+ directoryFilters = { "-.git", "-.vscode", "-.idea", "-.vscode-test", "-node_modules" },
+ gofumpt = true,
+ hints = {
+ assignVariableTypes = true,
+ compositeLiteralFields = true,
+ compositeLiteralTypes = true,
+ constantValues = true,
+ functionTypeParameters = true,
+ parameterNames = true,
+ rangeVariableTypes = true
+ },
+ semanticTokens = true,
+ staticcheck = true,
+ usePlaceholders = true
+ }
+ }
+} \ No newline at end of file
diff --git a/common/nvim/lsp/html.lua b/common/nvim/lsp/html.lua
new file mode 100644
index 0000000..5b322b1
--- /dev/null
+++ b/common/nvim/lsp/html.lua
@@ -0,0 +1,4 @@
+return {
+ cmd = { "vscode-html-language-server", "--stdio" },
+ filetypes = { "html" }
+} \ No newline at end of file
diff --git a/common/nvim/lsp/jsonls.lua b/common/nvim/lsp/jsonls.lua
new file mode 100644
index 0000000..6474e0a
--- /dev/null
+++ b/common/nvim/lsp/jsonls.lua
@@ -0,0 +1,4 @@
+return {
+ cmd = { "vscode-json-language-server", "--stdio" },
+ filetypes = { "json", "jsonc" }
+} \ No newline at end of file
diff --git a/common/nvim/lsp/lua_ls.lua b/common/nvim/lsp/lua_ls.lua
new file mode 100644
index 0000000..d248e2e
--- /dev/null
+++ b/common/nvim/lsp/lua_ls.lua
@@ -0,0 +1,20 @@
+return {
+ cmd = { "lua-language-server" },
+ filetypes = { "lua" },
+ root_markers = { ".luarc.json", ".luarc.jsonc", ".luacheckrc", ".stylua.toml", "stylua.toml", "selene.toml", "selene.yml" },
+ settings = {
+ Lua = {
+ diagnostics = {
+ disable = { "undefined-global", "lowercase-global", "unused-local", "unused-vararg", "trailing-space" },
+ globals = { "vim", "use", "_G", "packer_plugins", "P" }
+ },
+ telemetry = {
+ enable = false
+ },
+ workspace = {
+ checkThirdParty = false,
+ library = { "/tmp/.mount_nvimOIpamk/usr/share/nvim/runtime", "${3rd}/luv/library", "${3rd}/busted/library" }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/common/nvim/lsp/pyright.lua b/common/nvim/lsp/pyright.lua
new file mode 100644
index 0000000..f89d41f
--- /dev/null
+++ b/common/nvim/lsp/pyright.lua
@@ -0,0 +1,12 @@
+return {
+ cmd = { "pyright-langserver", "--stdio" },
+ filetypes = { "python" },
+ root_markers = { "pyproject.toml", "setup.py", "setup.cfg", "requirements.txt", "Pipfile", "pyrightconfig.json" },
+ settings = {
+ python = {
+ formatting = {
+ provider = "none"
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/common/nvim/lsp/rust_analyzer.lua b/common/nvim/lsp/rust_analyzer.lua
new file mode 100644
index 0000000..b4522f5
--- /dev/null
+++ b/common/nvim/lsp/rust_analyzer.lua
@@ -0,0 +1,5 @@
+return {
+ cmd = { "rust-analyzer" },
+ filetypes = { "rust" },
+ root_markers = { "Cargo.toml", "rust-project.json" }
+} \ No newline at end of file
diff --git a/common/nvim/lsp/ts_ls.lua b/common/nvim/lsp/ts_ls.lua
new file mode 100644
index 0000000..940fd3d
--- /dev/null
+++ b/common/nvim/lsp/ts_ls.lua
@@ -0,0 +1,8 @@
+return {
+ cmd = { "typescript-language-server", "--stdio" },
+ filetypes = { "javascript", "javascriptreact", "javascript.jsx", "typescript", "typescriptreact", "typescript.tsx" },
+ init_options = {
+ disableAutomaticTypeAcquisition = true
+ },
+ root_markers = { "tsconfig.json", "jsconfig.json", "package.json" }
+} \ No newline at end of file
diff --git a/common/nvim/lsp/yamlls.lua b/common/nvim/lsp/yamlls.lua
new file mode 100644
index 0000000..e52d322
--- /dev/null
+++ b/common/nvim/lsp/yamlls.lua
@@ -0,0 +1,4 @@
+return {
+ cmd = { "yaml-language-server", "--stdio" },
+ filetypes = { "yaml", "yml" }
+} \ No newline at end of file
diff --git a/common/nvim/lua/plugins/auto-session.lua b/common/nvim/lua/plugins/auto-session.lua
new file mode 100755
index 0000000..d982e08
--- /dev/null
+++ b/common/nvim/lua/plugins/auto-session.lua
@@ -0,0 +1,39 @@
+local M = {}
+
+function M.setup()
+ local auto = pcall(require, 'auto-session') and require('auto-session')
+ if not auto then
+ return false
+ end
+
+ local nvim_version = vim.version()
+ if nvim_version.major == 0 and nvim_version.minor < 5 then
+ return false
+ end
+
+ -- Configure session options
+ vim.opt.sessionoptions:append("localoptions") -- Add localoptions to sessionoptions
+
+ -- Set up auto-session
+ auto.setup({
+ log_level = 'info',
+ auto_session_suppress_dirs = { '~/', '~/Projects', '~/projects', '~/Downloads', '~/downloads' },
+ auto_session_use_git_branch = true,
+ bypass_save_filetypes = { "dashboard" },
+
+ -- Additional configuration to handle session options
+ pre_save_cmds = {
+ -- Ensure local options are saved with the session
+ function() vim.opt.sessionoptions:append("localoptions") end,
+ },
+
+ -- Post restore hook to ensure local options are properly set
+ post_restore = function()
+ vim.opt.sessionoptions:append("localoptions")
+ end,
+ })
+
+ return true
+end
+
+return M
diff --git a/common/nvim/lua/plugins/autopairs.lua b/common/nvim/lua/plugins/autopairs.lua
new file mode 100755
index 0000000..22dcf27
--- /dev/null
+++ b/common/nvim/lua/plugins/autopairs.lua
@@ -0,0 +1,99 @@
+local M = {}
+
+--- Setup and configure nvim-autopairs
+-- This function initializes and configures the autopairs plugin
+-- @return boolean True if setup was successful, false otherwise
+function M.setup()
+ local ok, autopairs = pcall(require, "nvim-autopairs")
+ if not ok then
+ return false
+ end
+
+ -- Configure autopairs
+ autopairs.setup({
+ check_ts = true,
+ ts_config = {
+ lua = { "string", "source" },
+ javascript = { "string", "template_string" },
+ java = false,
+ },
+ map = "<M-e>",
+ pairs_map = {
+ ["<"] = ">",
+ },
+ disable_filetype = { "TelescopePrompt", "spectre_panel" },
+ disable_in_macro = true,
+ disable_in_visualblock = true,
+ enable_moveright = true,
+ enable_afterquote = true, -- add bracket pairs after quote
+ enable_check_bracket_line = false, --- check bracket in same line
+ enable_bracket_in_quote = true, --
+ break_undo = true, -- switch for basic rule break undo sequence
+ --fast_wrap = {
+ -- chars = { "{", "[", "(", '"', "'" },
+ -- pattern = string.gsub([[ [%'%"%)%>%]%)%}%,] ]], "%s+", ""),
+ -- offset = 0, -- Offset from pattern match
+ -- end_key = "$",
+ -- keys = "qwertyuiopzxcvbnmasdfghjkl",
+ -- check_comma = true,
+ -- highlight = "PmenuSel",
+ -- highlight_grey = "LineNr",
+ --},
+})
+local Rule = require("nvim-autopairs.rule")
+
+local cond = require("nvim-autopairs.conds")
+
+autopairs.add_rules({
+ Rule("`", "'", "tex"),
+ Rule("$", "$", "tex"),
+ Rule(" ", " ")
+ :with_pair(function(opts)
+ local pair = opts.line:sub(opts.col, opts.col + 1)
+ return vim.tbl_contains({ "$$", "()", "{}", "[]", "<>" }, pair)
+ end)
+ :with_move(cond.none())
+ :with_cr(cond.none())
+ :with_del(function(opts)
+ local col = vim.api.nvim_win_get_cursor(0)[2]
+ local context = opts.line:sub(col - 1, col + 2)
+ return vim.tbl_contains({ "$ $", "( )", "{ }", "[ ]", "< >" }, context)
+ end),
+ Rule("$ ", " ", "tex"):with_pair(cond.not_after_regex(" ")):with_del(cond.none()),
+ Rule("[ ", " ", "tex"):with_pair(cond.not_after_regex(" ")):with_del(cond.none()),
+ Rule("{ ", " ", "tex"):with_pair(cond.not_after_regex(" ")):with_del(cond.none()),
+ Rule("( ", " ", "tex"):with_pair(cond.not_after_regex(" ")):with_del(cond.none()),
+ Rule("< ", " ", "tex"):with_pair(cond.not_after_regex(" ")):with_del(cond.none()),
+})
+
+autopairs.get_rule("$"):with_move(function(opts)
+ return opts.char == opts.next_char:sub(1, 1)
+end)
+
+-- import nvim-cmp plugin (completions plugin)
+local cmp = require("cmp")
+
+-- import nvim-autopairs completion functionality
+local cmp_autopairs = require("nvim-autopairs.completion.cmp")
+
+-- make autopairs and completion work together
+cmp.event:on(
+ "confirm_done",
+ cmp_autopairs.on_confirm_done({
+ filetypes = {
+ tex = false, -- Disable for tex
+ },
+ })
+)
+
+--local cmp_autopairs = require "nvim-autopairs.completion.cmp"
+--local cmp_status_ok, cmp = pcall(require, "cmp")
+--if not cmp_status_ok then
+-- return
+--end
+--cmp.event:on("confirm_done", cmp_autopairs.on_confirm_done { map_char = { tex = "" } })
+
+ return true
+end
+
+return M
diff --git a/common/nvim/lua/plugins/cmp-gh-source.lua b/common/nvim/lua/plugins/cmp-gh-source.lua
new file mode 100755
index 0000000..4990c35
--- /dev/null
+++ b/common/nvim/lua/plugins/cmp-gh-source.lua
@@ -0,0 +1,70 @@
+local ok, Job = pcall(require, 'plenary.job')
+if not ok then
+ return
+end
+
+local source = {}
+
+source.new = function()
+ local self = setmetatable({ cache = {} }, { __index = source })
+
+ return self
+end
+
+source.complete = function(self, _, callback)
+ local bufnr = vim.api.nvim_get_current_buf()
+
+ -- This just makes sure that we only hit the GH API once per session.
+ --
+ -- You could remove this if you wanted, but this just makes it so we're
+ -- good programming citizens.
+ if not self.cache[bufnr] then
+ Job:new({
+ -- Uses `gh` executable to request the issues from the remote repository.
+ 'gh',
+ 'issue',
+ 'list',
+ '--limit',
+ '1000',
+ '--json',
+ 'title,number,body',
+
+ on_exit = function(job)
+ local result = job:result()
+ local ok, parsed = pcall(vim.json.decode, table.concat(result, ''))
+ if not ok then
+ vim.notify('Failed to parse gh result')
+ return
+ end
+
+ local items = {}
+ for _, gh_item in ipairs(parsed) do
+ gh_item.body = string.gsub(gh_item.body or '', '\r', '')
+
+ table.insert(items, {
+ label = string.format('#%s', gh_item.number),
+ documentation = {
+ kind = 'markdown',
+ value = string.format('# %s\n\n%s', gh_item.title, gh_item.body),
+ },
+ })
+ end
+
+ callback({ items = items, isIncomplete = false })
+ self.cache[bufnr] = items
+ end,
+ }):start()
+ else
+ callback({ items = self.cache[bufnr], isIncomplete = false })
+ end
+end
+
+source.get_trigger_characters = function()
+ return { '#' }
+end
+
+source.is_available = function()
+ return vim.bo.filetype == 'gitcommit'
+end
+
+require('cmp').register_source('gh_issues', source.new())
diff --git a/common/nvim/lua/plugins/cmp.lua b/common/nvim/lua/plugins/cmp.lua
new file mode 100755
index 0000000..7de04ad
--- /dev/null
+++ b/common/nvim/lua/plugins/cmp.lua
@@ -0,0 +1,67 @@
+local M = {}
+
+--- Setup and configure nvim-cmp
+-- This function initializes and configures the completion plugin
+-- @return boolean True if setup was successful, false otherwise
+function M.setup()
+ -- Check Neovim version
+ local nvim_version = vim.version()
+ if nvim_version.major == 0 and nvim_version.minor < 5 then
+ return false
+ end
+
+ -- Try to load required modules
+ local cmp = pcall(require, 'cmp') and require('cmp')
+ if not cmp then
+ return false
+ end
+
+ local luasnip_ok, luasnip = pcall(require, 'luasnip')
+ if not luasnip_ok then
+ vim.notify("luasnip not found, some features may be limited", vim.log.levels.WARN)
+ end
+
+ -- Setup nvim-cmp
+ cmp.setup({
+ snippet = {
+ expand = function(args)
+ if luasnip_ok then luasnip.lsp_expand(args.body) end
+ end,
+ },
+ mapping = cmp.mapping.preset.insert({
+ ['<C-Space>'] = cmp.mapping.complete(),
+ ['<CR>'] = cmp.mapping.confirm({ select = true }),
+ ['<Tab>'] = cmp.mapping.select_next_item(),
+ ['<S-Tab>'] = cmp.mapping.select_prev_item(),
+ }),
+ sources = cmp.config.sources({
+ { name = 'nvim_lsp' },
+ { name = 'luasnip' },
+ { name = 'buffer' },
+ }),
+})
+
+vim.cmd([[
+ highlight! link CmpItemMenu Comment
+ " gray
+ highlight! CmpItemAbbrDeprecated guibg=NONE gui=strikethrough guifg=#808080
+ " blue
+ highlight! CmpItemAbbrMatch guibg=NONE guifg=#569CD6
+ highlight! CmpItemAbbrMatchFuzzy guibg=NONE guifg=#569CD6
+ " light blue
+ highlight! CmpItemKindVariable guibg=NONE guifg=#9CDCFE
+ highlight! CmpItemKindInterface guibg=NONE guifg=#9CDCFE
+ highlight! CmpItemKindText guibg=NONE guifg=#9CDCFE
+ " pink
+ highlight! CmpItemKindFunction guibg=NONE guifg=#C586C0
+ highlight! CmpItemKindMethod guibg=NONE guifg=#C586C0
+ " front
+ highlight! CmpItemKindKeyword guibg=NONE guifg=#D4D4D4
+ highlight! CmpItemKindProperty guibg=NONE guifg=#D4D4D4
+ highlight! CmpItemKindUnit guibg=NONE guifg=#D4D4D4
+ ]])
+
+ return true
+end
+
+return M
diff --git a/common/nvim/lua/plugins/colorizer.lua b/common/nvim/lua/plugins/colorizer.lua
new file mode 100755
index 0000000..6019bc5
--- /dev/null
+++ b/common/nvim/lua/plugins/colorizer.lua
@@ -0,0 +1,8 @@
+local M = {}
+
+function M.setup()
+ -- No-op if colorizer is not installed
+ return true
+end
+
+return M
diff --git a/common/nvim/lua/plugins/colorscheme.lua b/common/nvim/lua/plugins/colorscheme.lua
new file mode 100755
index 0000000..7fbabc1
--- /dev/null
+++ b/common/nvim/lua/plugins/colorscheme.lua
@@ -0,0 +1,24 @@
+local M = {}
+
+-- List of preferred colorschemes in order of preference
+local preferred_colorschemes = {
+ 'tokyonight',
+ 'desert',
+ 'default'
+}
+
+function M.setup()
+ -- Try each colorscheme in order of preference
+ for _, scheme in ipairs(preferred_colorschemes) do
+ local ok = pcall(vim.cmd, 'colorscheme ' .. scheme)
+ if ok then
+ return true
+ end
+ end
+
+ -- If all else fails, use the built-in default
+ vim.cmd('colorscheme default')
+ return true
+end
+
+return M
diff --git a/common/nvim/lua/plugins/comment.lua b/common/nvim/lua/plugins/comment.lua
new file mode 100755
index 0000000..392b279
--- /dev/null
+++ b/common/nvim/lua/plugins/comment.lua
@@ -0,0 +1,125 @@
+local M = {}
+
+--- Setup and configure comment.nvim
+-- This function initializes and configures the comment plugin
+-- @return boolean True if setup was successful, false otherwise
+function M.setup()
+ local ok, comment = pcall(require, 'Comment')
+ if not ok then
+ vim.notify("Comment.nvim not found", vim.log.levels.WARN)
+ return false
+ end
+
+ -- Configure comment.nvim
+ comment.setup({
+ -- Add a space b/w comment and the line
+ padding = true,
+
+ -- Whether the cursor should stay at its position
+ sticky = true,
+
+ -- Lines to be ignored while (un)commenting
+ ignore = '^$',
+
+ -- LHS of toggle mappings in NORMAL mode
+ toggler = {
+ -- Line-comment toggle keymap
+ line = 'gcc',
+ -- Block-comment toggle keymap
+ block = 'gbc',
+ },
+
+ -- LHS of operator-pending mappings in NORMAL and VISUAL mode
+ opleader = {
+ -- Line-comment keymap
+ line = 'gc',
+ -- Block-comment keymap
+ block = 'gb',
+ },
+
+ -- LHS of extra mappings
+ extra = {
+ -- Add comment on the line above
+ above = 'gcO',
+ -- Add comment on the line below
+ below = 'gco',
+ -- Add comment at the end of line
+ eol = 'gcA',
+ },
+
+ -- Enable keybindings
+ -- NOTE: If given `false` then the plugin won't create any mappings
+ mappings = {
+ -- Operator-pending mapping; `gcc` `gbc` `gc[count]{motion}` `gb[count]{motion}`
+ basic = true,
+ -- Extra mapping; `gco`, `gcO`, `gcA`
+ extra = true,
+ -- Extended mapping; `g>` `g<` `g>[count]{motion}` `g<[count]{motion}`
+ extended = false,
+ },
+
+ -- Function to call before (un)comment
+ pre_hook = nil,
+
+ -- Function to call after (un)comment
+ post_hook = nil,
+ })
+
+ -- Additional keymaps for better UX
+ local keymap = vim.keymap.set
+ local opts = { noremap = true, silent = true }
+
+ -- Toggle comment for current line or visual selection
+ keymap('n', '<leader>cc', '<Plug>(comment_toggle_linewise_current)', opts)
+ keymap('n', '<leader>bc', '<Plug>(comment_toggle_blockwise_current)', opts)
+
+ -- Toggle comment for current line or visual selection and add new line
+ keymap('n', '<leader>cO', '<Plug>(comment_toggle_linewise_above)', opts)
+ keymap('n', '<leader>co', '<Plug>(comment_toggle_linewise_below)', opts)
+
+ -- Toggle comment for visual selection
+ keymap('v', '<leader>cc', '<Plug>(comment_toggle_linewise_visual)', { noremap = false })
+ keymap('v', '<leader>bc', '<Plug>(comment_toggle_blockwise_visual)', { noremap = false })
+
+ -- Filetype specific settings
+ local ft = require('Comment.ft')
+
+ -- Set comment string for specific filetypes
+ ft.set('lua', { '--%s', '--[[%s]]' })
+ ft.set('vim', { '" %s' })
+ ft.set('python', { '# %s', '"""%s"""' })
+ ft.set('javascript', { '// %s', '/*%s*/' })
+ ft.set('typescript', { '// %s', '/*%s*/' })
+ ft.set('css', { '/* %s */' })
+ ft.set('html', { '<!-- %s -->' })
+
+ -- Set up autocommands for specific filetypes
+ local group = vim.api.nvim_create_augroup('CommentCustom', { clear = true })
+
+ -- Disable comment plugin for certain filetypes
+ vim.api.nvim_create_autocmd('FileType', {
+ group = group,
+ pattern = {
+ 'qf', 'help', 'man', 'notify', 'lspinfo', 'packer',
+ 'checkhealth', 'startuptime', 'Trouble', 'alpha', 'dashboard'
+ },
+ callback = function()
+ vim.b.comment_disable = true
+ end,
+ })
+
+ -- Re-enable comment plugin for normal files
+ vim.api.nvim_create_autocmd('FileType', {
+ group = group,
+ pattern = { '*' },
+ callback = function()
+ if vim.bo.buftype == '' then
+ vim.b.comment_disable = nil
+ end
+ end,
+ })
+
+ return true
+end
+
+return M
diff --git a/common/nvim/lua/plugins/dap.lua b/common/nvim/lua/plugins/dap.lua
new file mode 100755
index 0000000..7de032c
--- /dev/null
+++ b/common/nvim/lua/plugins/dap.lua
@@ -0,0 +1,265 @@
+local M = {}
+
+function M.setup()
+ local ok, dap = pcall(require, "dap")
+ if not ok or not dap then
+ return false
+ end
+
+-- options
+dap.defaults.fallback.switchbuf = "uselast"
+dap.defaults.fallback.focus_terminal = true
+dap.defaults.fallback.external_terminal = {
+ command = "/usr/bin/wezterm",
+ args = { "-e" },
+}
+
+-- Autocmds
+vim.api.nvim_create_autocmd("FileType", {
+ pattern = { "dap-float" },
+ callback = function(event)
+ vim.keymap.set("n", "<Tab>", "", { buffer = event.buf, silent = true })
+ vim.keymap.set("n", "<S-Tab>", "", { buffer = event.buf, silent = true })
+ end,
+})
+
+dap.adapters.cppdbg = {
+ id = "cppdbg",
+ type = "executable",
+ --command = vim.fn.stdpath('data') .. '/mason/bin/OpenDebugAD7',
+ command = os.getenv("HOME") .. "/apps/cpptools/extension/debugAdapters/bin/OpenDebugAD7",
+ --command = cpptools:get_install_path() .. '/extension/debugAdapters/bin/OpenDebugAD7'
+}
+
+dap.adapters.codelldb = {
+ type = "server",
+ port = "${port}",
+ --host = "localhost",
+ --host = '127.0.0.1',
+ --port = 13000, -- 💀 Use the port printed out or specified with `--port`
+ executable = {
+ --command = os.getenv("HOME") .. '/apps/codelldb/extension/adapter/codelldb',
+ command = os.getenv("HOME") .. "/.vscode-oss/extensions/vadimcn.vscode-lldb-1.9.0-universal/adapter/codelldb",
+ args = { "--port", "${port}" },
+ },
+ --detached = true,
+}
+
+dap.adapters.lldb = {
+ type = "executable",
+ command = "/usr/bin/lldb-vscode",
+ name = "lldb",
+}
+dap.configurations.cpp = {
+ {
+ name = "Debugger",
+ --type = "lldb",
+ --type = "cppdbg",
+ type = "codelldb",
+ request = "launch",
+ cwd = "${workspaceFolder}",
+ program = function()
+ return vim.fn.input("Path to executable: ", vim.fn.getcwd() .. "/", "file")
+ end,
+ --program = '${file}',
+ --program = function()
+ -- -- First, check if exists CMakeLists.txt
+ -- local cwd = vim.fn.getcwd()
+ -- if (file.exists(cwd, "CMakeLists.txt")) then
+ -- -- Todo. Then invoke cmake commands
+ -- -- Then ask user to provide execute file
+ -- return vim.fn.input("Path to executable: ", vim.fn.getcwd() .. "/", "file")
+ -- else
+ -- local fileName = vim.fn.expand("%:t:r")
+ -- if (not file.exists(cwd, "bin")) then
+ -- -- create this directory
+ -- os.execute("mkdir " .. "bin")
+ -- end
+ -- local cmd = "!gcc -g % -o bin/" .. fileName
+ -- -- First, compile it
+ -- vim.cmd(cmd)
+ -- -- Then, return it
+ -- return "${fileDirname}/bin/" .. fileName
+ -- end
+ --end,
+ stopAtEntry = true,
+ args = {},
+ --runInTerminal = true,
+ --runInTerminal = false,
+ --console = 'integratedTerminal',
+
+ --MIMode = 'gdb',
+ --miDebuggerServerAddress = 'localhost:1234',
+ --miDebuggerPath = 'gdb-oneapi',
+ --miDebuggerPath = '/usr/bin/gdb',
+ externalConsole = true,
+ --setupCommands = {
+ -- {
+ -- text = '-enable-pretty-printing',
+ -- description = 'enable pretty printing',
+ -- ignoreFailures = false
+ -- }
+ --},
+ },
+}
+
+-- If you want to use this for Rust and C, add something like this:
+dap.configurations.c = dap.configurations.cpp
+dap.configurations.rust = dap.configurations.cpp
+
+-- javascript
+--dap.adapters.node2 = {
+-- type = 'executable',
+-- command = 'node-debug2-adapter',
+-- args = {},
+--}
+
+--dap.configurations.javascript = {
+-- {
+-- name = 'Launch',
+-- type = 'node2',
+-- request = 'attach',
+-- program = '${file}',
+-- cwd = vim.fn.getcwd(),
+-- sourceMaps = true,
+-- protocol = 'inspector',
+-- console = 'integratedTerminal',
+-- },
+--}
+
+dap.adapters.python = {
+ type = "executable",
+ command = vim.trim(vim.fn.system("which python")),
+ args = { "-m", "debugpy.adapter" },
+}
+
+dap.configurations.python = {
+ {
+ -- The first three options are required by nvim-dap
+ type = "python", -- the type here established the link to the adapter definition: `dap.adapters.python`
+ request = "launch",
+ name = "Launch file",
+ -- Options below are for debugpy, see https://github.com/microsoft/debugpy/wiki/Debug-configuration-settings for supported options
+ program = "${file}", -- This configuration will launch the current file if used.
+ stopOnEntry = true,
+ },
+}
+
+local dapui = require("dapui")
+--local dap_ui_status_ok, dapui = pcall(require, "dapui")
+--if not dap_ui_status_ok then
+-- return
+--end
+
+-- setup repl
+--dap.repl.commands = vim.tbl_extend('force', dap.repl.commands, {
+-- exit = { 'q', 'exit' },
+-- custom_commands = {
+-- ['.run_to_cursor'] = dap.run_to_cursor,
+-- ['.restart'] = dap.run_last
+-- }
+--})
+
+-- Load dapui configuration only if it hasn't been loaded before
+ local dapui_ok, dapui = pcall(require, "dapui")
+ if dapui_ok and dapui then
+ dapui.setup({
+ mappings = {
+ expand = "<CR>",
+ open = "o",
+ remove = "D",
+ edit = "e",
+ repl = "r",
+ toggle = "t",
+ },
+ controls = {
+ enabled = true,
+ },
+ layouts = {
+ {
+ elements = {
+ -- Elements can be strings or table with id and size keys.
+ { id = "watches", size = 0.25 },
+ { id = "scopes", size = 0.25 },
+ { id = "breakpoints", size = 0.25 },
+ { id = "stacks", size = 0.25 },
+ },
+ size = 50, -- 40 columns
+ position = "left",
+ },
+ {
+ elements = {
+ { id = "console", size = 0.6 },
+ { id = "repl", size = 0.4 },
+ },
+ size = 0.3,
+ position = "bottom",
+ },
+ },
+ render = {
+ max_value_lines = 3,
+ },
+ floating = {
+ max_height = nil, -- These can be integers or a float between 0 and 1.
+ max_width = nil, -- Floats will be treated as percentage of your screen.
+ border = "single", -- Border style. Can be "single", "double" or "rounded"
+ mappings = {
+ close = { "q", "<Esc>" },
+ },
+ },
+ --icons = { expanded = "-", collapsed = "$" },
+ icons = {
+ expanded = "",
+ collapsed = "",
+ current_frame = "",
+ },
+ })
+ vim.g.loaded_dapui = true
+end
+
+-- Signs
+local sign = vim.fn.sign_define
+sign("DapBreakpoint", { text = "●", texthl = "DapBreakpoint", linehl = "", numhl = "" })
+sign("DapBreakpointCondition", { text = "◆", texthl = "DapBreakpointCondition", linehl = "", numhl = "" }) --
+sign("DapBreakpointRejected", { text = "R", texthl = "DiagnosticError", numhl = "DiagnosticError" })
+sign("DapLogPoint", { text = "L", texthl = "DapLogPoint", linehl = "", numhl = "" })
+sign("DapStopped", { text = "", texthl = "DiagnosticSignHint", numbhl = "", linehl = "" })
+
+--sign('DapBreakpoint', { text = '', texthl = 'DiagnosticSignError', numbhl = '', linehl = '' })
+--sign("DapLogPoint", { text = '.>', texthl = 'DiagnosticInfo', numhl = 'DiagnosticInfo' })
+--vim.fn.sign_define("DapBreakpointCondition", { text = '?>', texthl = 'DiagnosticInfo', numhl = 'DiagnosticInfo' })
+--vim.fn.sign_define("DapStopped", { text = '=>', texthl = 'DiagnosticWarn', numhl = 'DiagnosticWarn' })
+--vim.fn.sign_define("DapBreakpoint", { text = '<>', texthl = 'DiagnosticInfo', numhl = 'DiagnosticInfo' })
+
+dap.listeners.after.event_initialized["dapui_config"] = function()
+ dapui.open()
+end
+dap.listeners.before.event_terminated["dapui_config"] = function()
+ dapui.close()
+end
+dap.listeners.before.event_exited["dapui_config"] = function()
+ dapui.close()
+end
+dap.listeners.before.disconnect["dapui_config"] = function()
+ dapui.close()
+end
+
+require("nvim-dap-virtual-text").setup({
+ enabled = true,
+ enabled_commands = true,
+ highlight_changed_variables = true,
+ highlight_new_as_changed = false,
+ show_stop_reason = true,
+ commented = true,
+ only_first_definition = true,
+ all_references = false,
+ filter_references_pattern = "<module",
+ virt_text_pos = "eol",
+ all_frames = false,
+ virt_text_win_col = nil,
+ })
+
+ return true
+end
+
+return M
diff --git a/common/nvim/lua/plugins/dashboard.lua b/common/nvim/lua/plugins/dashboard.lua
new file mode 100755
index 0000000..43a3461
--- /dev/null
+++ b/common/nvim/lua/plugins/dashboard.lua
@@ -0,0 +1,126 @@
+local M = {}
+
+--- Setup and configure dashboard.nvim
+-- This function initializes and configures the dashboard plugin
+-- @return boolean True if setup was successful, false otherwise
+function M.setup()
+ local ok, db = pcall(require, 'dashboard')
+ if not ok then
+ return false
+ end
+
+ local messages = {
+ "The only way to do great work is to love what you do. - Steve Jobs",
+ "Code is like humor. When you have to explain it, it's bad. - Cory House",
+ "First, solve the problem. Then, write the code. - John Johnson",
+ "Any fool can write code that a computer can understand. Good programmers write code that humans can understand. - Martin Fowler",
+ "The most disastrous thing that you can ever learn is your first programming language. - Alan Kay",
+ "The most important property of a program is whether it accomplishes the intention of its user. - C.A.R. Hoare",
+ "The best error message is the one that never shows up. - Thomas Fuchs",
+ "The most important skill for a programmer is the ability to effectively communicate ideas. - Gastón Jorquera",
+ "The only way to learn a new programming language is by writing programs in it. - Dennis Ritchie",
+ "The most damaging phrase in the language is 'We've always done it this way!' - Grace Hopper"
+ }
+
+ local function get_random_message()
+ local random_index = math.random(1, #messages)
+ return messages[random_index]
+ end
+
+--vim.api.nvim_create_autocmd("VimEnter", {
+-- callback = function()
+-- -- disable line numbers
+-- vim.opt_local.number = false
+-- vim.opt_local.relativenumber = false
+-- -- always start in insert mode
+-- end,
+--})
+
+ -- Configure dashboard
+ db.setup({
+ theme = "hyper",
+ config = {
+ mru = { limit = 20, label = "" },
+ project = { limit = 10 },
+ header = {
+ [[ ███╗ ██╗ ███████╗ ██████╗ ██╗ ██╗ ██╗ ███╗ ███╗]],
+ [[ ████╗ ██║ ██╔════╝██╔═══██╗ ██║ ██║ ██║ ████╗ ████║]],
+ [[ ██╔██╗ ██║ █████╗ ██║ ██║ ██║ ██║ ██║ ██╔████╔██║]],
+ [[ ██║╚██╗██║ ██╔══╝ ██║ ██║ ╚██╗ ██╔╝ ██║ ██║╚██╔╝██║]],
+ [[ ██║ ╚████║ ███████╗╚██████╔╝ ╚████╔╝ ██║ ██║ ╚═╝ ██║]],
+ [[ ╚═╝ ╚═══╝ ╚══════╝ ╚═════╝ ╚═══╝ ╚═╝ ╚═╝ ╚═╝]],
+ },
+ disable_move = false,
+ shortcut = {
+ { desc = " Plugins", group = "Number", action = "PackerStatus", key = "p" },
+ {
+ desc = " Files",
+ group = "Number",
+ action = "Telescope find_files",
+ key = "f",
+ },
+ {
+ desc = " TODO",
+ group = "Number",
+ action = ":edit ~/documents/main/inbox/tasks/TODO.md",
+ key = "t",
+ },
+ {
+ desc = " New",
+ group = "Number",
+ action = "enew",
+ key = "e",
+ },
+ {
+ desc = " Grep",
+ group = "Number",
+ action = "Telescope live_grep",
+ key = "g",
+ },
+ {
+ desc = " Scheme",
+ group = "Number",
+ action = "Telescope colorscheme",
+ key = "s",
+ },
+ {
+ desc = " Config",
+ group = "Number",
+ action = ":edit ~/.config/nvim/init.lua",
+ key = "c",
+ },
+ },
+ footer = function()
+ return { "", "" }
+ --return { "", GetRandomMessage() }
+ end,
+ },
+ hide = {
+ statusline = false,
+ tabline = false,
+ winbar = false,
+ },
+})
+
+-- Set keymaps only when dashboard is active
+vim.api.nvim_create_autocmd("FileType", {
+ group = vim.api.nvim_create_augroup("DashboardMappings", { clear = true }),
+ pattern = "dashboard",
+ callback = function()
+ vim.keymap.set("n", "e", "<Cmd>DashboardNewFile<CR>", { buffer = true })
+ vim.keymap.set("n", "q", "<Cmd>q!<CR>", { buffer = true })
+ vim.keymap.set("n", "<C-o>", "<C-o><C-o>", { buffer = true }) -- Allow Ctrl + o to act normally
+ end,
+})
+---- General
+--DashboardHeader DashboardFooter
+---- Hyper theme
+--DashboardProjectTitle DashboardProjectTitleIcon DashboardProjectIcon
+--DashboardMruTitle DashboardMruIcon DashboardFiles DashboardShotCutIcon
+---- Doome theme
+--DashboardDesc DashboardKey DashboardIcon DashboardShotCut
+
+ return true
+end
+
+return M
diff --git a/common/nvim/lua/plugins/fidget.lua b/common/nvim/lua/plugins/fidget.lua
new file mode 100755
index 0000000..d401c5f
--- /dev/null
+++ b/common/nvim/lua/plugins/fidget.lua
@@ -0,0 +1,34 @@
+require("fidget").setup({
+ --event = "LspAttach",
+ text = {
+ --spinner = "pipe", -- (Default) animation shown when tasks are ongoing
+ --spinner = "hamburger", -- animation shown when tasks are ongoing
+ --spinner = "dots_pulse", -- animation shown when tasks are ongoing
+ spinner = "dots", -- animation shown when tasks are ongoing
+ done = "✔", -- character shown when all tasks are complete
+ commenced = "Started", -- message shown when task starts
+ completed = "Completed", -- message shown when task completes
+ },
+ fmt = {
+ task = function(task_name, message, percentage)
+ if task_name == "diagnostics" then
+ return false
+ end
+ return string.format(
+ "%s%s [%s]",
+ message,
+ percentage and string.format(" (%s%%)", percentage) or "",
+ task_name
+ )
+ end,
+ },
+ --sources = { -- Sources to configure
+ --["null-ls"] = { -- Name of source
+ --ignore = true, -- Ignore notifications from this source
+ --},
+ --},
+ debug = {
+ logging = false, -- whether to enable logging, for debugging
+ strict = false, -- whether to interpret LSP strictly
+ },
+})
diff --git a/common/nvim/lua/plugins/friendly-snippets.lua b/common/nvim/lua/plugins/friendly-snippets.lua
new file mode 100755
index 0000000..2a7695e
--- /dev/null
+++ b/common/nvim/lua/plugins/friendly-snippets.lua
@@ -0,0 +1,3 @@
+-- friendly-snippets plugin config (modular, robust)
+local ok, _ = pcall(require, 'friendly-snippets')
+-- No config needed, loaded by LuaSnip \ No newline at end of file
diff --git a/common/nvim/lua/plugins/fugitive.lua b/common/nvim/lua/plugins/fugitive.lua
new file mode 100755
index 0000000..22620e3
--- /dev/null
+++ b/common/nvim/lua/plugins/fugitive.lua
@@ -0,0 +1,8 @@
+local M = {}
+
+function M.setup()
+ -- No-op if fugitive is not installed
+ return true
+end
+
+return M
diff --git a/common/nvim/lua/plugins/fzf.lua b/common/nvim/lua/plugins/fzf.lua
new file mode 100755
index 0000000..9e62c48
--- /dev/null
+++ b/common/nvim/lua/plugins/fzf.lua
@@ -0,0 +1,43 @@
+local M = {}
+
+if not fzfLua then
+ return M
+end
+
+local ok_fzfLua, actions = pcall(require, "fzf-lua")
+if not ok_fzfLua then
+ return
+end
+
+local ok_fzfLua, actions = pcall(require, "fzf-lua.actions")
+if not ok_fzfLua then
+ return
+end
+
+
+local ok, fzfLua = pcall(require, "fzf-lua")
+if not ok then
+ vim.notify("fzf-lua not found", vim.log.levels.WARN)
+ return M
+end
+
+fzfLua.setup({
+ defaults = {
+ file_icons = "mini",
+ },
+ winopts = {
+ row = 0.5,
+ height = 0.7,
+ },
+ files = {
+ previewer = false,
+ },
+})
+
+vim.keymap.set("n", "<leader>fz", "<cmd>FzfLua files<cr>", { desc = "Fuzzy find files" })
+vim.keymap.set("n", "<leader>fzg", "<cmd>FzfLua live_grep<cr>", { desc = "Fuzzy grep files" })
+vim.keymap.set("n", "<leader>fzh", "<cmd>FzfLua helptags<cr>", { desc = "Fuzzy grep tags in help files" })
+vim.keymap.set("n", "<leader>fzt", "<cmd>FzfLua btags<cr>", { desc = "Fuzzy search buffer tags" })
+vim.keymap.set("n", "<leader>fzb", "<cmd>FzfLua buffers<cr>", { desc = "Fuzzy search opened buffers" })
+
+return M
diff --git a/common/nvim/lua/plugins/git.lua b/common/nvim/lua/plugins/git.lua
new file mode 100755
index 0000000..24a0871
--- /dev/null
+++ b/common/nvim/lua/plugins/git.lua
@@ -0,0 +1,8 @@
+local M = {}
+
+function M.setup()
+ -- No-op if git plugin is not installed
+ return true
+end
+
+return M
diff --git a/common/nvim/lua/plugins/gitsigns.lua b/common/nvim/lua/plugins/gitsigns.lua
new file mode 100755
index 0000000..7bbe637
--- /dev/null
+++ b/common/nvim/lua/plugins/gitsigns.lua
@@ -0,0 +1,85 @@
+local M = {}
+
+--- Setup and configure gitsigns
+-- This function initializes and configures the git signs in the gutter
+-- @return boolean True if setup was successful, false otherwise
+function M.setup()
+ local ok, gitsigns = pcall(require, 'gitsigns')
+ if not ok then
+ return false
+ end
+
+ gitsigns.setup({
+ signs = {
+ --add = {
+ -- hl = "GitSignsAdd",
+ -- text = "▍", --│
+ -- numhl = "GitSignsAddNr",
+ -- linehl = "GitSignsAddLn",
+ --},
+ --change = {
+ -- hl = "GitSignsChange",
+ -- text = "▍", --│
+ -- numhl = "GitSignsChangeNr",
+ -- linehl = "GitSignsChangeLn",
+ --},
+ delete = {
+ hl = "GitSignsDelete",
+ text = "▁", --_━─
+ numhl = "GitSignsDeleteNr",
+ linehl = "GitSignsDeleteLn",
+ },
+ topdelete = {
+ hl = "GitSignsDelete",
+ text = "▔", --‾
+ numhl = "GitSignsDeleteNr",
+ linehl = "GitSignsDeleteLn",
+ },
+ changedelete = {
+ hl = "GitSignsDelete",
+ text = "~",
+ numhl = "GitSignsChangeNr",
+ linehl = "GitSignsChangeLn",
+ },
+ },
+ current_line_blame = true,
+ })
+
+vim.api.nvim_command("highlight DiffAdd guibg=none guifg=#21c7a8")
+vim.api.nvim_command("highlight DiffModified guibg=none guifg=#82aaff")
+vim.api.nvim_command("highlight DiffDelete guibg=none guifg=#fc514e")
+vim.api.nvim_command("highlight DiffText guibg=none guifg=#fc514e")
+vim.cmd([[
+hi link GitSignsAdd DiffAdd
+hi link GitSignsChange DiffModified
+hi link GitSignsDelete DiffDelete
+hi link GitSignsTopDelete DiffDelete
+hi link GitSignsChangedDelete DiffDelete
+]])
+ -- Set up highlights
+ vim.cmd([[
+ highlight DiffAdd guibg=none guifg=#21c7a8
+ highlight DiffModified guibg=none guifg=#82aaff
+ highlight DiffDelete guibg=none guifg=#fc514e
+ highlight DiffText guibg=none guifg=#fc514e
+
+ hi link GitSignsAdd DiffAdd
+ hi link GitSignsChange DiffModified
+ hi link GitSignsDelete DiffDelete
+ hi link GitSignsTopDelete DiffDelete
+ hi link GitSignsChangedelete DiffDelete
+ hi link GitSignsChangedeleteLn DiffDelete
+ hi link GitSignsChangedeleteNr DiffDeleteNr
+ ]])
+
+ return true
+end
+
+return M
+--'signs.delete.hl' is now deprecated, please define highlight 'GitSignsDelete'
+--'signs.delete.linehl' is now deprecated, please define highlight 'GitSignsDeleteLn'
+--'signs.delete.numhl' is now deprecated, please define highlight 'GitSignsDeleteNr'
+--'signs.topdelete.hl' is now deprecated, please define highlight 'GitSignsTopdelete'
+--'signs.topdelete.linehl' is now deprecated, please define highlight 'GitSignsTopdeleteLn'
+--'signs.topdelete.numhl' is now deprecated, please define highlight 'GitSignsTopdeleteNr'
+
diff --git a/common/nvim/lua/plugins/goto-preview.lua b/common/nvim/lua/plugins/goto-preview.lua
new file mode 100755
index 0000000..eb54a8c
--- /dev/null
+++ b/common/nvim/lua/plugins/goto-preview.lua
@@ -0,0 +1,31 @@
+local M = {}
+
+function M.setup()
+ local ok, gp = pcall(require, 'goto-preview')
+ if not ok or not gp then
+ return false
+ end
+
+ gp.setup {
+ width = 120; -- Width of the floating window
+ height = 15; -- Height of the floating window
+ border = {"↖", "─" ,"┐", "│", "┘", "─", "└", "│"}; -- Border characters of the floating window
+ default_mappings = false; -- Bind default mappings
+ debug = false; -- Print debug information
+ opacity = nil; -- 0-100 opacity level of the floating window where 100 is fully transparent.
+ resizing_mappings = false; -- Binds arrow keys to resizing the floating window.
+ post_open_hook = nil; -- A function taking two arguments, a buffer and a window to be ran as a hook.
+ references = { -- Configure the telescope UI for slowing the references cycling window.
+ telescope = require("telescope.themes").get_dropdown({ hide_preview = false })
+ };
+ -- These two configs can also be passed down to the goto-preview definition and implementation calls for one off "peak" functionality.
+ focus_on_open = true; -- Focus the floating window when opening it.
+ dismiss_on_move = false; -- Dismiss the floating window when moving the cursor.
+ force_close = true, -- passed into vim.api.nvim_win_close's second argument. See :h nvim_win_close
+ bufhidden = "wipe", -- the bufhidden option to set on the floating window. See :h bufhidden
+ }
+
+ return true
+end
+
+return M
diff --git a/common/nvim/lua/plugins/hardtime.lua b/common/nvim/lua/plugins/hardtime.lua
new file mode 100755
index 0000000..b440334
--- /dev/null
+++ b/common/nvim/lua/plugins/hardtime.lua
@@ -0,0 +1,29 @@
+-- Function to toggle the hardtime state and echo a message
+local hardtime_enabled = true
+
+local hardtime = require("hardtime")
+
+hardtime.setup({
+ -- hardtime config here
+ enabled = true,
+ restriction_mode = "hint",
+ disabled_filetypes = { "qf", "netrw", "NvimTree", "NvimTree_1", "lazy", "mason", "oil", "dashboard" },
+ disable_mouse = false,
+ disabled_keys = {
+ ["<Up>"] = {},
+ ["<Down>"] = {},
+ ["<Left>"] = {},
+ ["<Right>"] = {},
+ },
+})
+
+function ToggleHardtime()
+ hardtime.toggle()
+ hardtime_enabled = not hardtime_enabled
+ local message = hardtime_enabled and "hardtime on" or "hardtime off"
+ vim.cmd('echo "' .. message .. '"')
+end
+
+return {
+ ToggleHardtime = ToggleHardtime,
+}
diff --git a/common/nvim/lua/plugins/harpoon.lua b/common/nvim/lua/plugins/harpoon.lua
new file mode 100755
index 0000000..8e842b3
--- /dev/null
+++ b/common/nvim/lua/plugins/harpoon.lua
@@ -0,0 +1,50 @@
+local M = {}
+
+function M.setup()
+ local ok, harpoon = pcall(require, "harpoon")
+ if not ok or not harpoon then
+ return false
+ end
+
+ harpoon.setup({
+ menu = {
+ width = vim.api.nvim_win_get_width(0) - 4,
+ },
+ --keys = {
+ -- { "mt", function() require("harpoon.mark").toggle_file() end, desc = "Toggle File" },
+ -- { "mm", function() require("harpoon.ui").toggle_quick_menu() end, desc = "Harpoon Menu" },
+ -- { "mc", function() require("harpoon.cmd-ui").toggle_quick_menu() end, desc = "Command Menu" },
+ -- --{ "<leader>1", function() require("harpoon.ui").nav_file(1) end, desc = "File 1" },
+ -- --{ "<leader>2", function() require("harpoon.ui").nav_file(2) end, desc = "File 2" },
+ -- --{ "<leader>3", function() require("harpoon.term").gotoTerminal(1) end, desc = "Terminal 1" },
+ -- --{ "<leader>4", function() require("harpoon.term").gotoTerminal(2) end, desc = "Terminal 2" },
+ -- --{ "<leader>5", function() require("harpoon.term").sendCommand(1,1) end, desc = "Command 1" },
+ -- --{ "<leader>6", function() require("harpoon.term").sendCommand(1,2) end, desc = "Command 2" },
+ })
+
+ -- Set up keymaps safely
+ local function safe_keymap(mode, lhs, rhs, opts)
+ local opts_with_noremap = vim.tbl_extend('force', {noremap = true, silent = true}, opts or {})
+ vim.keymap.set(mode, lhs, rhs, opts_with_noremap)
+ end
+
+ safe_keymap("n", "<leader>ma", function() require('harpoon.mark').add_file() end, { desc = "Harpoon: Add file" })
+ safe_keymap("n", "<leader>mt", function() require('harpoon.mark').toggle_file() end, { desc = "Harpoon: Toggle file" })
+ safe_keymap("n", "<leader>mq", function() require('harpoon.ui').toggle_quick_menu() end, { desc = "Harpoon: Toggle quick menu" })
+ safe_keymap("n", "<leader>mh", function() require('harpoon.ui').nav_file(1) end, { desc = "Harpoon: Navigate to file 1" })
+ safe_keymap("n", "<leader>mj", function() require('harpoon.ui').nav_file(2) end, { desc = "Harpoon: Navigate to file 2" })
+ safe_keymap("n", "<leader>mk", function() require('harpoon.ui').nav_file(3) end, { desc = "Harpoon: Navigate to file 3" })
+ safe_keymap("n", "<leader>ml", function() require('harpoon.ui').nav_file(4) end, { desc = "Harpoon: Navigate to file 4" })
+
+ return true
+end
+
+return M
+--
+--vim.keymap.set("n", "<leader>a", mark.add_file)
+--vim.keymap.set("n", "<C-e>", ui.toggle_quick_menu)
+--
+--vim.keymap.set("n", "<C-h>", function() ui.nav_file(1) end)
+--vim.keymap.set("n", "<C-t>", function() ui.nav_file(2) end)
+--vim.keymap.set("n", "<C-n>", function() ui.nav_file(3) end)
+--vim.keymap.set("n", "<C-s>", function() ui.nav_file(4) end)
diff --git a/common/nvim/lua/plugins/heirline.lua b/common/nvim/lua/plugins/heirline.lua
new file mode 100755
index 0000000..a4c2fc3
--- /dev/null
+++ b/common/nvim/lua/plugins/heirline.lua
@@ -0,0 +1,1497 @@
+local M = {}
+
+-- Safe require function to handle missing dependencies
+local function safe_require(module)
+ local ok, result = pcall(require, module)
+ return ok and result or nil
+end
+
+-- These will be initialized in M.setup()
+local heirline = nil
+local conditions = {}
+local utils = {}
+local colors = {}
+
+function M.setup()
+ heirline = safe_require("heirline")
+ if not heirline then
+ return
+ end
+
+ -- Initialize conditions and utils after heirline is loaded
+ conditions = require("heirline.conditions") or {}
+ utils = require("heirline.utils") or {}
+
+
+ -- Initialize colors after safe_fg is defined
+ colors = {
+ bg = "NONE",
+ nobg = "NONE",
+ white = "#f8f8f2",
+ black = "#000000",
+ darkgray = "#23232e",
+ gray = "#2d2b3a",
+ lightgray = "#d6d3ea",
+ pink = "#f92672",
+ green = "#50fa7b",
+ blue = "#39BAE6",
+ yellow = "#f1fa8c",
+ orange = "#ffb86c",
+ purple = "#BF40BF",
+ violet = "#7F00FF",
+ red = "#ff5555",
+ cyan = "#66d9eC",
+ --diag = {
+ -- warn = safe_fg("DiagnosticSignWarn", "#ffb86c"),
+ -- error = safe_fg("DiagnosticSignError", "#ff5555"),
+ -- hint = safe_fg("DiagnosticSignHint", "#50fa7b"),
+ -- info = safe_fg("DiagnosticSignInfo", "#66d9eC"),
+ --},
+ diag = {
+ warn = utils.get_highlight("DiagnosticSignWarn").fg,
+ error = utils.get_highlight("DiagnosticSignError").fg,
+ hint = utils.get_highlight("DiagnosticSignHint").fg,
+ info = utils.get_highlight("DiagnosticSignInfo").fg,
+ },
+ git = {
+ active = "#f34f29",
+ del = "#ff5555",
+ add = "#50fa7b",
+ change = "#ae81ff",
+ },
+ }
+
+ -- Only load colors if heirline is available
+ if heirline.load_colors then
+ local ok, err = pcall(heirline.load_colors, colors)
+ if not ok then
+ vim.notify("Failed to load Heirline colors: " .. tostring(err), vim.log.levels.ERROR)
+ end
+ end
+
+ local function get_icon(icon, fallback)
+ -- Check if we have Nerd Fonts available
+ local has_nerd_fonts = vim.g.statusline_has_nerd_fonts
+ if has_nerd_fonts == nil then
+ -- Cache the result to avoid repeated checks
+ if vim.fn.has('unix') == 1 and vim.fn.executable('fc-list') == 1 then
+ local handle = io.popen('fc-list | grep -i nerd')
+ local result = handle:read('*a')
+ handle:close()
+ has_nerd_fonts = result ~= ""
+ else
+ -- On non-Unix systems or if fc-list isn't available, assume no Nerd Fonts
+ has_nerd_fonts = false
+ end
+ vim.g.statusline_has_nerd_fonts = has_nerd_fonts
+ end
+
+ -- Return the appropriate string based on font availability
+ local result = has_nerd_fonts and icon or (fallback or '')
+ -- Trim any whitespace to prevent layout issues
+ return vim.trim(result)
+ end
+
+ -- Define all components after colors and utils are initialized
+
+ --local Signs = {
+ -- Error = "✘",
+ -- Warn = "",
+ -- Hint = "◉",
+ -- Info = "",
+ --}
+ local Icons = {
+ Signs = {
+ Error = "✘",
+ Warn = "",
+ Hint = "◉",
+ Info = "",
+ LSP = get_icon("⚙️", "LSP"),
+ },
+ ---- LSP/Debug
+ Error = get_icon("✘", "E"),
+ Warn = get_icon("", "W"),
+ Hint = get_icon("◉", "H"),--
+ Info = get_icon("ℹ", "I"),
+ --LSP = get_icon("⚙️", "LSP"),
+
+ -- Diagnostic
+ Diagnostic = {
+ error = get_icon("✘", "E"),
+ warn = get_icon("", "W"),
+ hint = get_icon("", "H"),
+ info = get_icon("ℹ", "I"),
+ },
+
+
+--local GitIcons = {
+-- added = "✚", -- plus in diff style
+-- modified = "", -- nf-oct-diff_modified
+-- removed = "", -- nf-oct-diff_removed
+--}
+--added = "", -- nf-oct-diff_added
+--modified = "", -- nf-oct-diff_modified
+--removed = "", -- nf-oct-diff_removed
+--local GitIcons = {
+-- added = "", -- nf-fa-plus_square
+-- modified = "", -- nf-fa-file_text_o
+-- removed = "", -- nf-fa-minus_square
+--}
+ -- Git
+ Git = {
+ branch = get_icon(" ", "⎇ "),
+ added = get_icon("+ ", "+"),
+ removed = get_icon("- ", "-"),
+ modified = get_icon("~ ", "~"),
+ renamed = get_icon("", "r"),
+ untracked = get_icon("", "?"),
+ ignored = get_icon("", "."),
+ },
+
+ -- UI Elements
+ UI = {
+ left_separator = get_icon("", ""),
+ right_separator = get_icon("", ""),
+ thin_separator = get_icon("▏", "|"),
+ ellipsis = get_icon("…", "..."),
+ arrow_left = get_icon("◀", "<"),
+ arrow_right = get_icon("▶", ">"),
+ close = get_icon("✕", "x"),
+ big_close = get_icon(" ", "x "),
+ modified = get_icon(" + ", "*"),
+ readonly = get_icon("", "RO"),
+ lock = get_icon("", "[L]"),
+ clock = get_icon("🕒", "[TIME]"),
+ buffer = get_icon("", "[BUF]"),
+ tab = get_icon("", "[TAB]"),
+ search = get_icon("🔍", "[SEARCH]"),
+ spell = get_icon("暈", "[SPELL]"),
+ whitespace = get_icon("␣", "[WS]"),
+ newline = get_icon("↵", "[NL]"),
+ indent = get_icon("▏", "|"),
+ fold = get_icon("", ">"),
+ fold_open = get_icon("", "v"),
+ fold_closed = get_icon("", ">"),
+ },
+
+ -- File types
+ File = {
+ default = get_icon("", "[F]"),
+ directory = get_icon("", "[D]"),
+ symlink = get_icon("", "[L]"),
+ executable = get_icon("", "[X]"),
+ image = get_icon("", "[IMG]"),
+ archive = get_icon("", "[ARC]"),
+ audio = get_icon("", "[AUD]"),
+ video = get_icon("", "[VID]"),
+ document = get_icon("", "[DOC]"),
+ config = get_icon("", "[CFG]"),
+ code = get_icon("", "[CODE]"),
+ terminal = get_icon("", "[TERM]"),
+ },
+
+ -- File format indicators
+ Format = {
+ unix = get_icon("", "[UNIX]"),
+ dos = get_icon("", "[DOS]"),
+ mac = get_icon("", "[MAC]"),
+ },
+
+ -- Version control
+ VCS = {
+ branch = get_icon("", "[BR]"),
+ git = get_icon("", "[GIT]"),
+ github = get_icon("", "[GH]"),
+ gitlab = get_icon("", "[GL]"),
+ bitbucket = get_icon("", "[BB]"),
+ },
+
+ -- Programming languages
+ Lang = {
+ lua = get_icon("", "[LUA]"),
+ python = get_icon("", "[PY]"),
+ javascript = get_icon("", "[JS]"),
+ typescript = get_icon("", "[TS]"),
+ html = get_icon("", "[HTML]"),
+ css = get_icon("", "[CSS]"),
+ json = get_icon("", "[JSON]"),
+ markdown = get_icon("", "[MD]"),
+ docker = get_icon("", "[DKR]"),
+ rust = get_icon("", "[RS]"),
+ go = get_icon("", "[GO]"),
+ java = get_icon("", "[JAVA]"),
+ c = get_icon("", "[C]"),
+ cpp = get_icon("", "[C++]"),
+ ruby = get_icon("", "[RB]"),
+ php = get_icon("", "[PHP]"),
+ haskell = get_icon("", "[HS]"),
+ scala = get_icon("", "[SCALA]"),
+ elixir = get_icon("", "[EXS]"),
+ clojure = get_icon("", "[CLJ]"),
+ },
+
+ -- UI Indicators
+ Indicator = {
+ error = get_icon("✘", "[E]"),
+ warning = get_icon("⚠", "[W]"),
+ info = get_icon("ℹ", "[I]"),
+ hint = get_icon("", "[H]"),
+ success = get_icon("✓", "[OK]"),
+ question = get_icon("?", "[?]"),
+ star = get_icon("★", "[*]"),
+ heart = get_icon("❤", "<3"),
+ lightning = get_icon("⚡", "[!]"),
+ check = get_icon("✓", "[√]"),
+ cross = get_icon("✗", "[x]"),
+ plus = get_icon("+", "[+]"),
+ recording = get_icon(" ", "q")
+ },
+
+ -- File operations
+ FileOp = {
+ new = get_icon("", "[NEW]"),
+ save = get_icon("💾", "[SAVE]"),
+ open = get_icon("📂", "[OPEN]"),
+ close = get_icon("✕", "[X]"),
+ undo = get_icon("↩", "[UNDO]"),
+ redo = get_icon("↪", "[REDO]"),
+ cut = get_icon("✂", "[CUT]"),
+ copy = get_icon("⎘", "[COPY]"),
+ paste = get_icon("📋", "[PASTE]"),
+ search = get_icon("🔍", "[FIND]"),
+ replace = get_icon("🔄", "[REPLACE]"),
+ },
+
+ -- Navigation
+ Nav = {
+ left = get_icon("←", "[<]"),
+ right = get_icon("→", "[>]"),
+ up = get_icon("↑", "[^]"),
+ down = get_icon("↓", "[v]"),
+ first = get_icon("⏮", "[<<]"),
+ last = get_icon("⏭", "[>>]"),
+ prev = get_icon("◀", "[<]"),
+ next = get_icon("▶", "[>]"),
+ back = get_icon("↩", "[B]"),
+ forward = get_icon("↪", "[F]"),
+ },
+
+ -- Editor states
+ State = {
+ insert = get_icon("", "[INS]"),
+ normal = get_icon("🚀", "[NOR]"),
+ visual = get_icon("👁", "[VIS]"),
+ replace = get_icon("🔄", "[REP]"),
+ command = get_icon("", "[CMD]"),
+ terminal = get_icon("", "[TERM]"),
+ select = get_icon("🔍", "[SEL]"),
+ },
+
+ -- Common symbols
+ Symbol = {
+ dot = get_icon("•", "•"),
+ bullet = get_icon("•", "•"),
+ middle_dot = get_icon("·", "·"),
+ ellipsis = get_icon("…", "..."),
+ check = get_icon("✓", "[OK]"),
+ cross = get_icon("✗", "[X]"),
+ arrow_right = get_icon(" ", "->"),
+ arrow_left = get_icon(" ", "<-"),
+ double_arrow_right = get_icon("»", ">>"),
+ double_arrow_left = get_icon("«", "<<"),
+ chevron_right = get_icon("›", ">"),
+ chevron_left = get_icon("‹", "<"),
+ },
+
+ -- Document symbols
+ DocSymbol = {
+ class = get_icon("", "[C]"),
+ function_icon = get_icon("", "[F]"),
+ method = get_icon("", "[M]"),
+ property = get_icon("", "[P]"),
+ field = get_icon("ﰠ", "[F]"),
+ constructor = get_icon("", "[C]"),
+ enum = get_icon("", "[E]"),
+ interface = get_icon("", "[I]"),
+ variable = get_icon("", "[V]"),
+ constant = get_icon("", "[C]"),
+ string = get_icon("", "[S]"),
+ number = get_icon("", "[N]"),
+ boolean = get_icon("◩", "[B]"),
+ array = get_icon("", "[A]"),
+ object = get_icon("⦿", "[O]"),
+ key = get_icon("🔑", "[K]"),
+ null = get_icon("NULL", "Ø"),
+ enum_member = get_icon("", "[E]"),
+ struct = get_icon("פּ", "[S]"),
+ event = get_icon("", "[E]"),
+ operator = get_icon("", "[O]"),
+ type_parameter = get_icon("", "[T]"),
+ },
+ }
+ local Align = { provider = "%=", hl = { bg = colors.bg } }
+ local Space = { provider = " ", hl = { bg = colors.bg } }
+ local Tab = { provider = " " }
+ local LeftSpace = { provider = "" }
+ local RightSpace = { provider = "" }
+
+ local ViMode = {
+ init = function(self)
+ self.mode = vim.fn.mode(1)
+ -- Store the initial mode
+ self.prev_mode = self.mode
+
+ -- Set up autocommand to force redraw on mode change
+ vim.api.nvim_create_autocmd("ModeChanged", {
+ pattern = "*:*",
+ callback = function()
+ -- Only redraw if the mode actually changed
+ local current_mode = vim.fn.mode(1)
+ if current_mode ~= self.prev_mode then
+ self.prev_mode = current_mode
+ vim.schedule(function()
+ vim.cmd("redrawstatus")
+ end)
+ end
+ end,
+ })
+ end,
+ static = {
+ mode_names = {
+ n = " NORMAL ",
+ no = "PENDING ",
+ nov = " N? ",
+ noV = " N? ",
+ ["no\22"] = " N? ",
+ niI = " Ni ",
+ niR = " Nr ",
+ niV = " Nv ",
+ nt = "TERMINAL",
+ v = " VISUAL ",
+ vs = " Vs ",
+ V = " V·LINE ",
+ ["\22"] = "V·BLOCK ",
+ ["\22s"] = "V·BLOCK ",
+ s = " SELECT ",
+ S = " S·LINE ",
+ ["\19"] = "S·BLOCK ",
+ i = " INSERT ",
+ ix = "insert x",
+ ic = "insert c",
+ R = "REPLACE ",
+ Rc = " Rc ",
+ Rx = " Rx ",
+ Rv = "V·REPLACE ",
+ Rvc = " Rv ",
+ Rvx = " Rv ",
+ c = "COMMAND ",
+ cv = " VIM EX ",
+ ce = " EX ",
+ r = " PROMPT ",
+ rm = " MORE ",
+ ["r?"] = "CONFIRM ",
+ ["!"] = " SHELL ",
+ t = "TERMINAL",
+ },
+ },
+ provider = function(self)
+ return " %2(" .. self.mode_names[self.mode] .. "%) "
+ end,
+ hl = function(self)
+ return { fg = "colors.black", bg = self.mode_color, bold = true }
+ end,
+ update = {
+ "ModeChanged",
+ "VimEnter",
+ "BufEnter",
+ "WinEnter",
+ "TabEnter",
+ pattern = "*:*",
+ callback = vim.schedule_wrap(function()
+ vim.cmd("redrawstatus")
+ end),
+ },
+ }
+
+ -- LSP
+ local LSPActive = {
+ condition = function()
+ local ok, _ = pcall(function()
+ local buf = vim.api.nvim_get_current_buf()
+ return #vim.lsp.get_clients({ bufnr = buf }) > 0
+ end)
+ return ok or false
+ end,
+ update = { "LspAttach", "LspDetach", "BufEnter" },
+ provider = function()
+ local ok, result = pcall(function()
+ local buf = vim.api.nvim_get_current_buf()
+ if not vim.api.nvim_buf_is_valid(buf) then return "" end
+
+ local clients = vim.lsp.get_clients({ bufnr = buf })
+ if not clients or #clients == 0 then return "" end
+
+ local client_names = {}
+ for _, client in ipairs(clients) do
+ if client and client.name and client.name ~= "null-ls" then
+ table.insert(client_names, client.name)
+ end
+ end
+
+ if #client_names > 0 then
+ return Icons.Signs.LSP .. " " .. table.concat(client_names, "/") .. " "
+ end
+
+ return ""
+ end)
+
+ if not ok then
+ vim.schedule(function()
+ vim.notify_once("Error in LSPActive provider: " .. tostring(result), vim.log.levels.DEBUG)
+ end)
+ return ""
+ end
+
+ return result or ""
+ end,
+ hl = { fg = "lightgray", bold = false },
+ }
+
+ local Navic = {
+ condition = function()
+ local ok, navic = pcall(require, "nvim-navic")
+ return ok and navic.is_available()
+ end,
+ static = {
+ type_hl = {
+ File = "Directory",
+ Module = "@include",
+ Namespace = "@namespace",
+ Package = "@include",
+ Class = "@structure",
+ Method = "@method",
+ Property = "@property",
+ Field = "@field",
+ Constructor = "@constructor",
+ Enum = "@field",
+ Interface = "@type",
+ Function = "@function",
+ Variable = "@variable",
+ Constant = "@constant",
+ String = "@string",
+ Number = "@number",
+ Boolean = "@boolean",
+ Array = "@field",
+ Object = "@type",
+ Key = "@keyword",
+ Null = "@comment",
+ EnumMember = "@field",
+ Struct = "@structure",
+ Event = "@keyword",
+ Operator = "@operator",
+ TypeParameter = "@type",
+ },
+ enc = function(line, col, winnr)
+ return bit.bor(bit.lshift(line, 16), bit.lshift(col, 6), winnr)
+ end,
+ dec = function(c)
+ local line = bit.rshift(c, 16)
+ local col = bit.band(bit.rshift(c, 6), 1023)
+ local winnr = bit.band(c, 63)
+ return line, col, winnr
+ end,
+ },
+ init = function(self)
+ local data = require("nvim-navic").get_data() or {}
+ local children = {}
+ for i, d in ipairs(data) do
+ local pos = self.enc(d.scope.start.line, d.scope.start.character, self.winnr)
+ local child = {
+ {
+ provider = d.icon,
+ hl = self.type_hl[d.type],
+ },
+ {
+ provider = d.name:gsub("%%", "%%%%"):gsub("%s*->%s*", ""),
+ on_click = {
+ minwid = pos,
+ callback = function(_, minwid)
+ local line, col, winnr = self.dec(minwid)
+ vim.api.nvim_win_set_cursor(vim.fn.win_getid(winnr), { line, col })
+ end,
+ name = "heirline_navic",
+ },
+ },
+ }
+ if #data > 1 and i < #data then
+ table.insert(child, {
+ provider = " > ",
+ hl = { fg = "bright_fg" },
+ })
+ end
+ table.insert(children, child)
+ end
+ self.child = self:new(children, 1)
+ end,
+ provider = function(self)
+ return self.child:eval()
+ end,
+ hl = { fg = "gray" },
+ update = "CursorMoved",
+ }
+
+ -- Diagnostics
+ local Diagnostics = {
+ condition = conditions.has_diagnostics,
+ static = {
+ error_icon = Icons.Error,
+ warn_icon = Icons.Warn,
+ info_icon = Icons.Info,
+ hint_icon = Icons.Hint,
+ },
+ init = function(self)
+ self.errors = #vim.diagnostic.get(0, { severity = vim.diagnostic.severity.ERROR })
+ self.warnings = #vim.diagnostic.get(0, { severity = vim.diagnostic.severity.WARN })
+ self.hints = #vim.diagnostic.get(0, { severity = vim.diagnostic.severity.HINT })
+ self.info = #vim.diagnostic.get(0, { severity = vim.diagnostic.severity.INFO })
+ end,
+ update = { "DiagnosticChanged", "BufEnter" },
+ {
+ provider = function(self)
+ return self.errors > 0 and (self.error_icon .. " " .. self.errors .. " ")
+ end,
+ hl = { fg = colors.diag.error, bg = colors.bg },
+ },
+ {
+ provider = function(self)
+ return self.warnings > 0 and (self.warn_icon .. " " .. self.warnings .. " ")
+ end,
+ hl = { fg = colors.diag.warn, bg = colors.bg },
+ },
+ {
+ provider = function(self)
+ return self.info > 0 and (self.info_icon .. " " .. self.info .. " ")
+ end,
+ hl = { fg = colors.diag.info, bg = colors.bg },
+ },
+ {
+ provider = function(self)
+ return self.hints > 0 and (self.hint_icon .. " " .. self.hints .. " ")
+ end,
+ hl = { fg = colors.diag.hint, bg = colors.bg },
+ },
+ on_click = {
+ callback = function()
+ local ok, _ = pcall(require, "trouble")
+ if ok then
+ require("trouble").toggle({ mode = "document_diagnostics" })
+ else
+ vim.diagnostic.setqflist()
+ end
+ end,
+ name = "heirline_diagnostics",
+ },
+ }
+
+ -- Git
+ local Git = {
+ condition = conditions.is_git_repo,
+ init = function(self)
+ self.status_dict = vim.b.gitsigns_status_dict or {}
+ self.has_changes = (self.status_dict.added or 0) ~= 0 or
+ (self.status_dict.removed or 0) ~= 0 or
+ (self.status_dict.changed or 0) ~= 0
+ end,
+ {
+ provider = function()
+ return " " .. Icons.Git.branch .. " "
+ end,
+ hl = { fg = colors.git.active, bg = colors.bg },
+ },
+ {
+ provider = function(self)
+ return self.status_dict.head or ""
+ end,
+ hl = { fg = colors.white, bg = colors.bg },
+ },
+ {
+ condition = function(self)
+ return self.has_changes
+ end,
+ provider = "",
+ },
+ {
+ provider = function(self)
+ local count = self.status_dict.added or 0
+ return count > 0 and (" " .. Icons.Git.added .. count) or ""
+ end,
+ hl = { fg = colors.git.add, bg = colors.bg },
+ },
+ {
+ provider = function(self)
+ local count = self.status_dict.removed or 0
+ return count > 0 and (" " .. Icons.Git.removed .. count) or ""
+ end,
+ hl = { fg = colors.git.del, bg = colors.bg },
+ },
+ {
+ provider = function(self)
+ local count = self.status_dict.changed or 0
+ return count > 0 and (" " .. Icons.Git.modified .. count) or ""
+ end,
+ hl = { fg = colors.git.change, bg = colors.bg },
+ },
+ on_click = {
+ callback = function()
+ vim.defer_fn(function()
+ vim.cmd("Lazygit")
+ end, 100)
+ end,
+ name = "heirline_git",
+ },
+ }
+
+ -- FileNameBlock: FileIcon, FileName and friends
+ local FileNameBlock = {
+ init = function(self)
+ self.filename = vim.api.nvim_buf_get_name(0)
+ end,
+ hl = { bg = colors.bg },
+ }
+
+ local FileIcon = {
+ init = function(self)
+ local filename = self.filename or vim.api.nvim_buf_get_name(0)
+ local extension = vim.fn.fnamemodify(filename, ":e")
+
+ local has_nerd_fonts = vim.g.statusline_has_nerd_fonts
+ if has_nerd_fonts == nil then
+ if vim.fn.has('unix') == 1 and vim.fn.executable('fc-list') == 1 then
+ local handle = io.popen('fc-list | grep -i nerd')
+ local result = handle:read('*a')
+ handle:close()
+ has_nerd_fonts = result ~= ""
+ else
+ has_nerd_fonts = false
+ end
+ vim.g.statusline_has_nerd_fonts = has_nerd_fonts
+ end
+
+ local icon, icon_color
+ if has_nerd_fonts then
+ icon, icon_color = require("nvim-web-devicons").get_icon_color(filename, extension, { default = true })
+ end
+
+ if vim.fn.isdirectory(filename) == 1 then
+ self.icon = has_nerd_fonts and Icons.File.directory or "[DIR]"
+ self.icon_color = colors.blue
+ else
+ if has_nerd_fonts and icon then
+ self.icon = icon .. " "
+ self.icon_color = icon_color or colors.blue
+ else
+ if extension == "" then
+ self.icon = Icons.File.default
+ else
+ local file_icon = Icons.File[extension:lower()] or Icons.File.default
+ if type(file_icon) == "table" then
+ self.icon = file_icon[1] or Icons.File.default
+ else
+ self.icon = file_icon
+ end
+ end
+ self.icon_color = colors.blue
+ end
+ end
+ end,
+ provider = function(self)
+ return self.icon
+ end,
+ hl = function(self)
+ return { fg = self.icon_color, bold = true }
+ end,
+ }
+
+ local FileName = {
+ provider = function(self)
+ local filename = vim.fn.fnamemodify(self.filename, ":.")
+ if filename == "" then
+ return "No Name"
+ end
+ if not conditions.width_percent_below(#filename, 0.25) then
+ filename = vim.fn.pathshorten(filename)
+ end
+ return filename
+ end,
+ hl = { fg = colors.white, bold = false, bg = colors.bg },
+ }
+
+ local FileFlags = {
+ {
+ provider = function()
+ if vim.bo.modified then
+ return " +"
+ end
+ end,
+ hl = { fg = colors.green, bg = colors.bg },
+ },
+ {
+ provider = function()
+ if not vim.bo.modifiable or vim.bo.readonly then
+ return " " .. Icons.UI.lock
+ end
+ end,
+ hl = { fg = colors.orange, bold = true, bg = colors.bg },
+ },
+ }
+
+ local FileNameModifier = {
+ hl = function()
+ if vim.bo.modified then
+ return { fg = colors.green, bold = false, force = true }
+ end
+ end,
+ }
+
+ -- FileType, FileEncoding and FileFormat
+ local FileType = {
+ provider = function()
+ return vim.bo.filetype
+ end,
+ hl = { fg = colors.white, bold = false, bg = colors.bg },
+ }
+
+ local FileEncoding = {
+ Space,
+ provider = function()
+ local enc = (vim.bo.fenc ~= "" and vim.bo.fenc) or vim.o.enc
+ return enc:lower()
+ end,
+ hl = { bg = colors.bg, bold = false },
+ }
+
+ local FileFormat = {
+ provider = function()
+ local fmt = vim.bo.fileformat
+ return fmt ~= "unix" and fmt:lower() or ""
+ end,
+ hl = { fg = utils.get_highlight("Statusline").fg, bold = true, bg = colors.bg },
+ }
+
+ local FileSize = {
+ provider = function()
+ local suffix = { "b", "k", "M", "G", "T", "P", "E" }
+ local filename = vim.api.nvim_buf_get_name(0)
+ local fsize = vim.fn.getfsize(filename)
+ fsize = (fsize < 0 and 0) or fsize
+ if fsize < 1024 then
+ return fsize .. suffix[1]
+ end
+ local i = math.floor((math.log(fsize) / math.log(1024)))
+ return string.format("%.2g%s", fsize / math.pow(1024, i), suffix[i + 1])
+ end,
+ hl = { fg = utils.get_highlight("Statusline").fg, bold = true, bg = colors.bg },
+ }
+
+ local FileLastModified = {
+ provider = function()
+ local filename = vim.api.nvim_buf_get_name(0)
+ local ftime = vim.fn.getftime(filename)
+ return (ftime > 0) and os.date("%c", ftime) or ""
+ end,
+ hl = { fg = utils.get_highlight("Statusline").fg, bold = true, bg = colors.bg },
+ }
+
+ local Spell = {
+ condition = function()
+ return vim.wo.spell
+ end,
+ provider = function()
+ return " " .. Icons.Indicator.spell .. " "
+ end,
+ hl = { bold = true, fg = colors.yellow },
+ }
+
+ local HelpFileName = {
+ condition = function()
+ return vim.bo.filetype == "help"
+ end,
+ provider = function()
+ local filename = vim.api.nvim_buf_get_name(0)
+ return vim.fn.fnamemodify(filename, ":t")
+ end,
+ hl = { fg = colors.blue },
+ }
+
+ local SearchCount = {
+ condition = function()
+ return vim.v.hlsearch ~= 0 and vim.o.cmdheight == 0
+ end,
+ init = function(self)
+ local ok, search = pcall(vim.fn.searchcount, { recompute = 1, maxcount = -1 })
+ if ok and search.total then
+ self.search = search
+ end
+ end,
+ provider = function(self)
+ local search = self.search or { current = 0, total = 0, maxcount = 0 }
+ return string.format("[%d/%d]", search.current, math.min(search.total, search.maxcount))
+ end,
+ update = { "CursorMoved", "CursorMovedI", "SearchWrapped" },
+ }
+
+ local MacroRec = {
+ condition = function()
+ return vim.fn.reg_recording() ~= "" and vim.o.cmdheight == 0
+ end,
+ provider = function()
+ return Icons.Indicator.recording .. " "
+ end,
+ hl = { fg = "orange", bold = true },
+ utils.surround({ "[", "]" }, nil, {
+ provider = function()
+ return vim.fn.reg_recording()
+ end,
+ hl = { fg = "green", bold = true },
+ }),
+ update = {
+ "RecordingEnter",
+ "RecordingLeave",
+ callback = vim.schedule_wrap(function()
+ vim.cmd("redrawstatus")
+ end),
+ },
+ }
+
+ local ShowCmd = {
+ condition = function()
+ return vim.o.cmdheight == 0
+ end,
+ provider = ":%3.5(%S%)",
+ update = { "CmdlineChanged" },
+ }
+
+ local cursor_location = {
+ { provider = "%1(%4l:%-3(%c%)%) %*", hl = { fg = colors.black, bold = true } },
+ }
+
+ local Ruler = { cursor_location }
+
+ local WordCount = {
+ condition = function()
+ return conditions.buffer_matches({
+ filetype = {
+ "markdown",
+ "txt",
+ "vimwiki",
+ },
+ })
+ end,
+ Space,
+ {
+ provider = function()
+ local ok, wordcount = pcall(vim.fn.wordcount)
+ return ok and wordcount.words and ("W:%d"):format(wordcount.words) or ""
+ end,
+ update = { "CursorMoved", "CursorMovedI", "InsertEnter", "TextChanged", "TextChangedI" },
+ },
+ }
+
+ local WorkDir = {
+ init = function(self)
+ local is_local = vim.fn.haslocaldir(0) == 1
+ self.icon = (is_local and "l" or "g") .. " " .. Icons.File.directory
+ local cwd = vim.fn.getcwd(0)
+ self.cwd = vim.fn.fnamemodify(cwd, ":~")
+ end,
+ hl = { fg = colors.blue, bold = true },
+ on_click = {
+ callback = function()
+ vim.cmd("Telescope find_files cwd=" .. vim.fn.getcwd(0))
+ end,
+ name = "heirline_workdir",
+ },
+ flexible = 1,
+ {
+ provider = function(self)
+ local trail = self.cwd:sub(-1) == "/" and "" or "/"
+ return self.icon .. " " .. self.cwd .. trail .. " "
+ end,
+ },
+ {
+ provider = function(self)
+ local cwd = vim.fn.pathshorten(self.cwd)
+ local trail = self.cwd:sub(-1) == "/" and "" or "/"
+ return self.icon .. " " .. cwd .. trail .. " "
+ end,
+ },
+ {
+ provider = function(self)
+ return self.icon .. " "
+ end,
+ },
+ }
+
+ -- Build FileNameBlock
+ FileNameBlock = utils.insert(
+ FileNameBlock,
+ FileIcon,
+ utils.insert(FileNameModifier, FileName),
+ unpack(FileFlags),
+ { provider = "%<" }
+ )
+
+ local FileInfoBlock = {
+ init = function(self)
+ self.filename = vim.api.nvim_buf_get_name(0)
+ end,
+ }
+
+ FileInfoBlock = utils.insert(
+ FileInfoBlock,
+ Space,
+ FileIcon,
+ FileType,
+ { provider = "%<" }
+ )
+
+ -- Create surrounded components with proper mode color functions
+ LeftSpace = utils.surround({ "", Icons.UI.right_separator }, function(self)
+ return self:mode_color()
+ end, { LeftSpace, hl = { fg = utils.get_highlight("statusline").bg, force = true } })
+
+ RightSpace = utils.surround({ Icons.UI.left_separator, "" }, function(self)
+ return self:mode_color()
+ end, { RightSpace, hl = { fg = utils.get_highlight("statusline").bg, force = true } })
+
+ LSPActive = utils.surround({ "", "" }, function(self)
+ return self:mode_color()
+ end, { Space, LSPActive, hl = { bg = colors.darkgray, force = true } })
+
+ FileInfoBlock = utils.surround({ "", "" }, function(self)
+ return self:mode_color()
+ end, { FileInfoBlock, Space, hl = { bg = colors.black, force = true } })
+
+ ViMode = utils.surround({ "", "" }, function(self)
+ return self:mode_color()
+ end, { ViMode, hl = { fg = colors.black, force = true } })
+
+ Ruler = utils.surround({ "", "" }, function(self)
+ return self:mode_color()
+ end, { Ruler, hl = { fg = colors.black, force = true } })
+
+ -- Statusline sections - FIXED: Removed duplicate LeftSpace from right section
+ local left = {
+ { RightSpace, hl = { bg = colors.nobg, force = true } },
+ { ViMode, hl = { bg = utils.get_highlight("statusline").bg, bold = false } },
+ { LeftSpace, hl = { bg = colors.nobg, force = true } },
+ { Space, hl = { bg = colors.nobg, force = true } },
+ { FileNameBlock, hl = { bg = colors.nobg, force = true } },
+ { Space, hl = { bg = colors.nobg, force = true } },
+ { Git, hl = { bg = colors.nobg, force = true } },
+ }
+
+ local middle = {
+ { Align, hl = { bg = colors.nobg, force = true } },
+ { Align, hl = { bg = colors.nobg, force = true } },
+ }
+
+ -- FIXED: Right section now has proper sequence without duplicate LeftSpace
+ local right = {
+ { Diagnostics, hl = { bg = colors.nobg, force = true } },
+ { Space, hl = { bg = colors.nobg, force = true } },
+ { LSPActive, hl = { bg = colors.nobg, force = true } },
+ { Space, hl = { bg = colors.nobg, force = true } },
+ { FileInfoBlock, hl = { bg = colors.nobg, force = true } },
+ { RightSpace, hl = { bg = colors.nobg, force = true } },
+ { Ruler, hl = { fg = utils.get_highlight("statusline").bg, bold = false } },
+ { LeftSpace, hl = { bg = colors.nobg, force = true } },
+ }
+
+ local sections = { left, middle, right }
+ local DefaultStatusline = { sections }
+
+ -- Special statuslines for inactive/special buffers
+ local specialleft = {
+ { RightSpace, hl = { bg = colors.nobg, force = true } },
+ { ViMode, hl = { bg = utils.get_highlight("statusline").bg, bold = false } },
+ { LeftSpace, hl = { bg = colors.nobg, force = true } },
+ }
+
+ local specialmiddle = {
+ { Align, hl = { bg = colors.nobg, force = true } },
+ { Align, hl = { bg = colors.nobg, force = true } },
+ }
+
+ local specialright = {
+ { RightSpace, hl = { bg = colors.nobg, force = true } },
+ { Ruler, hl = { fg = utils.get_highlight("statusline").bg, bold = false } },
+ { LeftSpace, hl = { bg = colors.nobg, force = true } },
+ }
+
+ local specialsections = { specialleft, specialmiddle, specialright }
+
+ local InactiveStatusline = {
+ condition = conditions.is_not_active,
+ specialsections,
+ }
+
+ local SpecialStatusline = {
+ condition = function()
+ return conditions.buffer_matches({
+ buftype = { "nofile", "prompt", "help", "quickfix" },
+ filetype = { "^git.*", "fugitive", "dashboard" },
+ })
+ end,
+ specialsections,
+ }
+
+ local TerminalStatusline = {
+ condition = function()
+ return conditions.buffer_matches({ buftype = { "terminal" } })
+ end,
+ specialsections,
+ }
+
+ -- FIXED: Main StatusLine with better mode handling
+ local StatusLine = {
+ static = {
+ mode_colors = {
+ n = colors.blue,
+ no = colors.blue,
+ nov = colors.blue,
+ noV = colors.blue,
+ ["no\22"] = colors.blue,
+ niI = colors.blue,
+ niR = colors.blue,
+ niV = colors.blue,
+ nt = colors.blue,
+ v = colors.purple,
+ vs = colors.purple,
+ V = colors.purple,
+ ["\22"] = colors.purple,
+ ["\22s"] = colors.purple,
+ s = colors.purple,
+ S = colors.purple,
+ ["\19"] = colors.purple,
+ i = colors.green,
+ ix = colors.green,
+ ic = colors.green,
+ R = colors.red,
+ Rc = colors.red,
+ Rx = colors.red,
+ Rv = colors.red,
+ Rvc = colors.red,
+ Rvx = colors.red,
+ c = colors.orange,
+ cv = colors.orange,
+ ce = colors.orange,
+ r = colors.red,
+ rm = colors.red,
+ ["r?"] = colors.red,
+ ["!"] = colors.orange,
+ t = colors.orange,
+ },
+ mode_color = function(self)
+ -- FIXED: Always get current mode to ensure updates
+ local mode = vim.fn.mode()
+ return self.mode_colors[mode] or colors.blue
+ end,
+ },
+ -- FIXED: Add update triggers to ensure statusline refreshes properly
+ update = {
+ "ModeChanged",
+ "BufEnter",
+ "WinEnter",
+ "WinLeave",
+ "BufWinEnter",
+ "CmdlineLeave",
+ callback = vim.schedule_wrap(function()
+ vim.cmd("redrawstatus")
+ end),
+ },
+ fallthrough = false,
+ SpecialStatusline,
+ TerminalStatusline,
+ InactiveStatusline,
+ DefaultStatusline,
+ }
+
+ -- WinBar components
+ local WinbarFileNameBlock = {
+ init = function(self)
+ self.filename = vim.api.nvim_buf_get_name(0)
+ end,
+ hl = { bg = colors.bg },
+ }
+
+ local WinbarFileName = {
+ provider = function(self)
+ local filename = vim.fn.fnamemodify(self.filename, ":.")
+ if filename == "" then
+ return "No Name"
+ end
+ if not conditions.width_percent_below(#filename, 0.25) then
+ filename = vim.fn.pathshorten(filename)
+ end
+ return filename
+ end,
+ hl = { fg = colors.gray, bold = false, bg = colors.bg },
+ }
+
+ WinbarFileNameBlock = utils.insert(
+ WinbarFileNameBlock,
+ FileIcon,
+ utils.insert(WinbarFileName),
+ unpack(FileFlags),
+ { provider = "%<" }
+ )
+
+ vim.api.nvim_create_autocmd("User", {
+ pattern = "HeirlineInitWinbar",
+ callback = function(args)
+ local buf = args.buf
+ local buftype = vim.tbl_contains({ "prompt", "nofile", "help", "quickfix" }, vim.bo[buf].buftype)
+ local filetype = vim.tbl_contains({ "gitcommit", "fugitive" }, vim.bo[buf].filetype)
+ if buftype or filetype then
+ vim.opt_local.winbar = nil
+ end
+ end,
+ })
+
+ local On_click = {
+ minwid = function()
+ return vim.api.nvim_get_current_win()
+ end,
+ callback = function(_, minwid)
+ local winid = minwid
+ local buf = vim.api.nvim_win_get_buf(winid)
+ end,
+ }
+
+ local CloseButton = {
+ condition = function(self)
+ return not vim.bo.modified
+ end,
+ update = { "WinNew", "WinClosed", "BufEnter" },
+ { provider = " " },
+ {
+ provider = Icons.UI.close,
+ hl = { fg = "gray" },
+ On_click = {
+ minwid = function()
+ return vim.api.nvim_get_current_win()
+ end,
+ callback = function(_, minwid)
+ vim.api.nvim_win_close(minwid, true)
+ end,
+ name = "heirline_winbar_close_button",
+ },
+ },
+ }
+
+ local Center = {
+ fallthrough = false,
+ {
+ condition = function()
+ return conditions.buffer_matches({
+ buftype = { "terminal", "nofile", "prompt", "help", "quickfix" },
+ filetype = { "dap-ui", "NvimTree", "^git.*", "fugitive", "dashboard" },
+ })
+ end,
+ init = function()
+ vim.opt_local.winbar = nil
+ end,
+ },
+ {
+ condition = function()
+ return conditions.buffer_matches({ buftype = { "terminal" } })
+ end,
+ FileType,
+ Space,
+ },
+ {
+ condition = function()
+ return not conditions.is_active()
+ end,
+ utils.surround({ "", "" }, colors.nobg, { WinbarFileNameBlock }),
+ },
+ utils.surround({ "", "" }, colors.nobg, { FileNameBlock }),
+ }
+
+ local WinBar = { Space, Center }
+
+ -- Tabline components
+ local TablineFileIcon = {
+ init = function(self)
+ local filename = self.filename
+ local extension = vim.fn.fnamemodify(filename, ":e")
+
+ local has_nerd_fonts = vim.g.statusline_has_nerd_fonts
+ if has_nerd_fonts == nil then
+ if vim.fn.has('unix') == 1 and vim.fn.executable('fc-list') == 1 then
+ local handle = io.popen('fc-list | grep -i nerd')
+ local result = handle:read('*a')
+ handle:close()
+ has_nerd_fonts = result ~= ""
+ else
+ has_nerd_fonts = false
+ end
+ vim.g.statusline_has_nerd_fonts = has_nerd_fonts
+ end
+
+ if has_nerd_fonts then
+ self.icon, self.icon_color = require("nvim-web-devicons").get_icon_color(filename, extension, { default = true })
+ else
+ self.icon = ""
+ self.icon_color = colors.blue
+
+ if vim.fn.isdirectory(filename) == 1 then
+ self.icon = "[DIR]"
+ else
+ local file_icon = Icons.File[extension:lower()] or Icons.File.default
+ if type(file_icon) == "table" then
+ self.icon = file_icon[1] or Icons.File.default
+ else
+ self.icon = file_icon
+ end
+ end
+ end
+
+ if self.icon ~= "" then
+ self.icon = self.icon .. " "
+ end
+ end,
+ provider = function(self)
+ return self.icon or ""
+ end,
+ hl = function(self)
+ return { fg = self.icon_color or colors.blue }
+ end,
+ }
+
+ local TablineFileName = {
+ provider = function(self)
+ local filename = vim.fn.fnamemodify(self.filename, ":t")
+ if filename == "" then
+ return "[No Name]"
+ end
+ return filename
+ end,
+ }
+
+ local TablineFileFlags = {
+ {
+ condition = function(self)
+ return vim.api.nvim_buf_get_option(self.bufnr, "modified")
+ end,
+ provider = "%X " .. Icons.Indicator.plus .. " %X",
+ hl = { fg = "green" },
+ },
+ {
+ condition = function(self)
+ return not vim.api.nvim_buf_get_option(self.bufnr, "modifiable") or vim.api.nvim_buf_get_option(self.bufnr, "readonly")
+ end,
+ provider = function()
+ if vim.bo.readonly then
+ return " " .. Icons.UI.lock
+ end
+ return ""
+ end,
+ hl = { fg = "orange" },
+ },
+ }
+
+ local TablineFileNameBlock = {
+ init = function(self)
+ self.filename = vim.api.nvim_buf_get_name(self.bufnr)
+ end,
+ hl = function(self)
+ if self.is_active then
+ return "TabLineSel"
+ else
+ return "TabLineFill"
+ end
+ end,
+ on_click = {
+ callback = function(_, minwid, _, button)
+ if button == "m" then
+ vim.api.nvim_buf_delete(minwid, { force = false })
+ else
+ vim.api.nvim_win_set_buf(0, minwid)
+ end
+ end,
+ minwid = function(self)
+ return self.bufnr
+ end,
+ name = "heirline_tabline_buffer_callback",
+ },
+ TablineFileIcon,
+ TablineFileName,
+ TablineFileFlags,
+ }
+
+ local TablineCloseButton = {
+ condition = function(self)
+ return not vim.api.nvim_buf_get_option(self.bufnr, "modified")
+ end,
+ { provider = " " },
+ {
+ provider = "" .. Icons.UI.close .. " %X",
+ hl = { fg = colors.red },
+ on_click = {
+ callback = function(_, minwid)
+ vim.api.nvim_buf_delete(minwid, { force = false })
+ end,
+ minwid = function(self)
+ return self.bufnr
+ end,
+ name = "heirline_tabline_close_buffer_callback",
+ },
+ },
+ }
+
+ local TablineBufferBlock = utils.surround({ "", "" }, function(self)
+ if self.is_active then
+ return utils.get_highlight("TabLineSel").bg
+ else
+ return utils.get_highlight("TabLineFill").bg
+ end
+ end, { Tab, TablineFileNameBlock, TablineCloseButton })
+
+ local BufferLine = utils.make_buflist(
+ TablineBufferBlock,
+ { provider = Icons.Symbol.arrow_left, hl = { fg = colors.gray } },
+ { provider = Icons.Symbol.arrow_right, hl = { fg = colors.gray } }
+ )
+
+ local Tabpage = {
+ provider = function(self)
+ return "%" .. self.tabnr .. "T " .. self.tabnr .. " %T"
+ end,
+ hl = function(self)
+ return self.is_active and "TabLineSel" or "TabLineFill"
+ end,
+ }
+
+ local TabpageClose = {
+ provider = "%999X " .. Icons.UI.close .. " %X",
+ hl = { fg = colors.red, bg = colors.bg },
+ }
+
+ local TabPages = {
+ condition = function()
+ return #vim.api.nvim_list_tabpages() >= 2
+ end,
+ { provider = "%=" },
+ utils.make_tablist(Tabpage),
+ TabpageClose,
+ }
+
+ local TabLineOffset = {
+ condition = function(self)
+ local win = vim.api.nvim_tabpage_list_wins(0)[1]
+ local bufnr = vim.api.nvim_win_get_buf(win)
+ self.winid = win
+
+ if vim.api.nvim_buf_get_option(bufnr, "filetype") == "NvimTree" then
+ self.title = "NvimTree"
+ return true
+ end
+ end,
+ provider = function(self)
+ local title = self.title
+ local width = vim.api.nvim_win_get_width(self.winid)
+ local pad = math.ceil((width - #title) / 2)
+ return string.rep(" ", pad) .. title .. string.rep(" ", pad)
+ end,
+ hl = { fg = colors.white, bg = "#333842", bold = true },
+ }
+
+ local TabLine = {
+ TabLineOffset,
+ BufferLine,
+ TabPages,
+ }
+
+ -- Buffer navigation functions
+ local function get_bufs()
+ return vim.tbl_filter(function(bufnr)
+ return vim.api.nvim_buf_is_loaded(bufnr) and vim.bo[bufnr].buflisted
+ end, vim.api.nvim_list_bufs())
+ end
+
+ local function goto_buf(index)
+ local bufs = get_bufs()
+ if index > #bufs then
+ index = #bufs
+ end
+ vim.api.nvim_win_set_buf(0, bufs[index])
+ end
+
+ local function add_key(key, index)
+ vim.keymap.set("n", "<A-" .. key .. ">", function()
+ goto_buf(index)
+ end, { noremap = true, silent = true })
+ end
+
+ for i = 1, 9 do
+ add_key(i, i)
+ end
+ add_key("0", 10)
+
+ vim.o.showtabline = 2
+ vim.cmd([[au FileType * if index(['wipe', 'delete', 'unload'], &bufhidden) >= 0 | set nobuflisted | endif]])
+
+ -- FIXED: Add proper autocmds for better statusline updates
+ local augroup = vim.api.nvim_create_augroup("HeirlineStatusline", { clear = true })
+
+ -- Force statusline refresh on mode changes and buffer events
+ vim.api.nvim_create_autocmd({
+ "ModeChanged",
+ "BufEnter",
+ "BufWinEnter",
+ "WinEnter",
+ "WinLeave",
+ "CmdlineLeave",
+ "TermEnter",
+ "TermLeave"
+ }, {
+ group = augroup,
+ callback = function()
+ vim.schedule(function()
+ if vim.o.laststatus > 0 then
+ vim.cmd("redrawstatus!")
+ end
+ end)
+ end,
+ })
+
+ -- Final heirline setup
+ heirline.setup({
+ statusline = StatusLine,
+ winbar = WinBar,
+ tabline = TabLine,
+ opts = {
+ disable_winbar_cb = function(args)
+ local buf = args.buf
+ if not vim.api.nvim_buf_is_valid(buf) then
+ return true
+ end
+
+ local buftype = vim.tbl_contains(
+ { "prompt", "nofile", "help", "quickfix" },
+ vim.bo[buf].buftype
+ )
+ local filetype = vim.tbl_contains(
+ { "gitcommit", "fugitive" },
+ vim.bo[buf].filetype
+ )
+ return buftype or filetype
+ end,
+ }
+ })
+
+end
+
+return M
diff --git a/common/nvim/lua/plugins/indent-blankline.lua b/common/nvim/lua/plugins/indent-blankline.lua
new file mode 100755
index 0000000..cbbcf27
--- /dev/null
+++ b/common/nvim/lua/plugins/indent-blankline.lua
@@ -0,0 +1,73 @@
+local M = {}
+
+--- Setup and configure indent-blankline.nvim
+-- This function initializes and configures the indent guides
+-- @return boolean True if setup was successful, false otherwise
+function M.setup()
+ local ok, ibl = pcall(require, 'ibl')
+ if not ok then
+ return false
+ end
+
+ local highlight = {
+ "RainbowRed",
+ "RainbowYellow",
+ "RainbowBlue",
+ "RainbowOrange",
+ "RainbowGreen",
+ "RainbowViolet",
+ "RainbowCyan",
+ }
+
+ local hooks = require("ibl.hooks")
+ -- create the highlight groups in the highlight setup hook, so they are reset
+ -- every time the colorscheme changes
+ hooks.register(hooks.type.HIGHLIGHT_SETUP, function()
+ vim.api.nvim_set_hl(0, "RainbowRed", { fg = "#E06C75" })
+ vim.api.nvim_set_hl(0, "RainbowYellow", { fg = "#E5C07B" })
+ vim.api.nvim_set_hl(0, "RainbowBlue", { fg = "#61AFEF" })
+ vim.api.nvim_set_hl(0, "RainbowOrange", { fg = "#D19A66" })
+ vim.api.nvim_set_hl(0, "RainbowGreen", { fg = "#98C379" })
+ vim.api.nvim_set_hl(0, "RainbowViolet", { fg = "#C678DD" })
+ vim.api.nvim_set_hl(0, "RainbowCyan", { fg = "#56B6C2" })
+ end)
+
+ ibl.setup({
+ indent = { highlight = highlight },
+ exclude = {
+ filetypes = {
+ "", -- for all buffers without a file type
+ "NvimTree",
+ "Trouble",
+ "TelescopePrompt",
+ "TelescopeResults",
+ "mason",
+ "help",
+ "dashboard",
+ "packer",
+ "neogitstatus",
+ "Trouble",
+ "text",
+ "terminal",
+ "lazy",
+ },
+ buftypes = {
+ "terminal",
+ "nofile",
+ "quickfix",
+ "prompt",
+ },
+ },
+ })
+
+ -- Toggle indent blankline with <leader>ti
+ vim.keymap.set('n', '<leader>ti', '<cmd>IBLToggle<CR>', {
+ noremap = true,
+ silent = true,
+ desc = 'Toggle indent guides'
+ })
+
+ return true
+end
+
+return M
diff --git a/common/nvim/lua/plugins/interestingwords.lua b/common/nvim/lua/plugins/interestingwords.lua
new file mode 100755
index 0000000..655ed42
--- /dev/null
+++ b/common/nvim/lua/plugins/interestingwords.lua
@@ -0,0 +1,499 @@
+local interestingwords = (function()
+ local api = vim.api
+ local fn = vim.fn
+ local uv = vim.loop
+
+ local m = {}
+
+ m.words = {}
+ m.colors = {}
+ m.limits = {}
+ m.capcity = 0
+ m.next = 1
+
+ local get_default_config = function()
+ return {
+ colors = { "#aeee00", "#ff0000", "#0000ff", "#b88823", "#ffa724", "#ff2c4b" },
+ search_count = true,
+ navigation = true,
+ scroll_center = true,
+ search_key = "<leader>hl",
+ cancel_search_key = "<leader>lh",
+ color_key = "<leader>ih",
+ cancel_color_key = "<leader>hi",
+ select_mode = "random", -- random or loop
+ }
+ end
+
+ local init_colors = function()
+ for i, v in pairs(m.config.colors) do
+ local color = "InterestingWord" .. i
+
+ api.nvim_set_hl(0, color, { bg = v, fg = "Black" })
+ m.colors[color] = 595129 + i
+ m.capcity = m.capcity + 1
+ end
+ m.limits.min = 595129 + 1
+ m.limits.max = 595129 + #m.config.colors
+ end
+
+ local get_reg_ex = function(word)
+ if vim.o.ignorecase and (not vim.o.smartcase or fn.match(word, "\\u") == -1) then
+ return "\\c\\V" .. word
+ else
+ return "\\C\\V" .. word
+ end
+ end
+
+ local get_visual_selection = function()
+ local lines
+ local start_row, start_col = fn.getpos("v")[2], fn.getpos("v")[3]
+ local end_row, end_col = fn.getpos(".")[2], fn.getpos(".")[3]
+ if end_row < start_row then
+ start_row, end_row = end_row, start_row
+ start_col, end_col = end_col, start_col
+ elseif end_row == start_row and end_col < start_col then
+ start_col, end_col = end_col, start_col
+ end
+ start_row = start_row - 1
+ start_col = start_col - 1
+ end_row = end_row - 1
+ if api.nvim_get_mode().mode == "V" then
+ lines = api.nvim_buf_get_text(0, start_row, 0, end_row, -1, {})
+ elseif api.nvim_get_mode().mode == "v" then
+ lines = api.nvim_buf_get_text(0, start_row, start_col, end_row, end_col, {})
+ end
+ vim.cmd("normal! ")
+ if lines == nil then
+ return ""
+ end
+
+ local line = ""
+ for i, v in ipairs(lines) do
+ if i == 1 then
+ line = line .. fn.escape(v, "\\")
+ else
+ line = line .. "\\n" .. fn.escape(v, "\\")
+ end
+ end
+
+ return line
+ end
+
+ local uncolor = function(word)
+ if m.words[word] then
+ local windows = api.nvim_list_wins()
+ for _, i in ipairs(windows) do
+ pcall(function()
+ fn.matchdelete(m.words[word].mid, i)
+ end)
+ end
+ m.colors[m.words[word].color] = m.words[word].mid
+ m.words[word] = nil
+ end
+ end
+
+ local get_rest_color_random = function()
+ local res = {}
+ for k, v in pairs(m.colors) do
+ if v ~= 0 then
+ table.insert(res, { color = k, mid = v })
+ end
+ end
+ if #res == 0 then
+ return nil
+ end
+
+ return res[math.random(#res)]
+ end
+
+ local find_who_use_this = function(target_color)
+ for word, color in pairs(m.words) do
+ if color.color == target_color then
+ return word
+ end
+ end
+ return nil
+ end
+
+ local get_rest_color_loop = function()
+ if m.next > m.capcity then
+ m.next = 1
+ end
+ local color = "InterestingWord" .. m.next
+ if m.colors[color] == 0 then
+ local word = find_who_use_this(color)
+ if word ~= nil then
+ uncolor(word)
+ else
+ return nil
+ end
+ end
+ m.next = m.next + 1
+ return { color = color, mid = m.colors[color] }
+ end
+
+ local get_rest_color = function()
+ local selector = {
+ ["random"] = get_rest_color_random,
+ ["loop"] = get_rest_color_loop,
+ }
+ return selector[m.config.select_mode]()
+ end
+
+ local color = function(word)
+ local color = get_rest_color()
+ if not color then
+ vim.notify("InterestingWords: max number of highlight groups reached")
+ return
+ end
+
+ m.words[word] = {}
+ m.words[word].color = color.color
+ m.words[word].mid = color.mid
+ m.colors[color.color] = 0
+
+ local windows = api.nvim_list_wins()
+ for _, i in ipairs(windows) do
+ pcall(function()
+ fn.matchadd(m.words[word].color, word, 1, m.words[word].mid, { window = i })
+ end)
+ end
+ end
+
+ local recolorAllWords = function()
+ for k, v in pairs(m.words) do
+ pcall(function()
+ fn.matchadd(v.color, k, 1, v.mid, { window = 0 })
+ end)
+ end
+ end
+
+ local nearest_word_at_cursor = function()
+ for _, match_item in pairs(fn.getmatches()) do
+ if match_item.id >= m.limits.min or match_item.id <= m.limits.max then
+ local buf_content = fn.join(api.nvim_buf_get_lines(0, 0, -1, {}), "\n")
+ local cur_pos = #fn.join(api.nvim_buf_get_lines(0, 0, fn.line(".") - 1, {}), "\n") +
+ ((fn.line(".") == 1) and 0 or 1) + fn.col(".") - 1
+ local lst_pos = 0
+ while true do
+ local mat_pos = fn.matchstrpos(buf_content, match_item.pattern, lst_pos, 1)
+ if mat_pos[1] == "" then
+ break
+ end
+ if cur_pos >= mat_pos[2] and cur_pos < mat_pos[3] then
+ return match_item.pattern
+ end
+ lst_pos = mat_pos[3]
+ end
+ end
+ end
+ end
+
+ local filter = function(word)
+ if #word <= 4 or (string.sub(word, 1, 4) ~= "\\c\\V" and string.sub(word, 1, 4) ~= "\\C\\V") then
+ return word
+ else
+ return string.sub(word, 5, -1)
+ end
+ end
+
+ local display_search_count = function(word, count)
+ local icon = ""
+ m.search_count_extmark_id = api.nvim_buf_set_extmark(0, m.search_count_namespace, fn.line(".") - 1, 0, {
+ virt_text_pos = "eol",
+ virt_text = {
+ { icon .. count, "NonText" },
+ },
+ hl_mode = "combine",
+ })
+ m.search_count_cache = icon .. " " .. filter(word) .. count
+ m.search_count_timer:again()
+ end
+
+ local hide_search_count = function(bufnr)
+ if m.search_count_namespace then
+ api.nvim_buf_del_extmark(bufnr, m.search_count_namespace, m.search_count_extmark_id)
+ end
+ end
+
+ local scroll_timer = vim.loop.new_timer()
+ local function scroll_up(cnt)
+ return vim.cmd("normal! " .. cnt .. "")
+ end
+
+ local function scroll_down(cnt)
+ return vim.cmd("normal! " .. cnt .. "")
+ end
+
+ local function stop_scrolling()
+ scroll_timer:stop()
+ end
+
+ local scroll_to_center = function()
+ local window_height = api.nvim_win_get_height(0)
+ local lines = fn.winline() - math.floor(window_height / 2)
+ if lines == 0 then
+ return
+ end
+ local up = lines > 0
+ lines = math.abs(lines)
+
+ local move_lines = function(n)
+ return math.floor(n / 5) + 1
+ end
+
+ local each_time = function()
+ local lines_bak = lines
+ local circles = 0
+ while lines_bak ~= 0 do
+ lines_bak = lines_bak - move_lines(lines_bak)
+ circles = circles + 1
+ end
+ local pseudo_total_time = 300 + 15 * math.min((lines - 11), 10) + lines
+ return math.floor(pseudo_total_time / circles)
+ end
+ local t = each_time()
+ local time_total = 0
+
+ local scroll_callback = function()
+ local cnt = move_lines(lines)
+ if lines == 0 then
+ stop_scrolling()
+ return
+ else
+ lines = lines - cnt
+ end
+
+ if up then
+ scroll_up(cnt)
+ else
+ scroll_down(cnt)
+ end
+ time_total = time_total + t
+ end
+
+ scroll_timer:start(t, t, vim.schedule_wrap(scroll_callback))
+ end
+
+ m.lualine_get = function()
+ return m.search_count_cache
+ end
+
+ m.lualine_has = function()
+ return m.search_count_cache ~= ""
+ end
+
+ m.init_search_count = function()
+ m.search_count_extmark_id = 0
+ m.search_count_namespace = api.nvim_create_namespace("custom/search_count")
+ m.search_count_timer = vim.loop.new_timer()
+ m.search_count_timer:start(0, 5000, function()
+ m.search_count_cache = ""
+ vim.defer_fn(function()
+ hide_search_count(0)
+ end, 100)
+ m.search_count_timer:stop()
+ end)
+
+ vim.api.nvim_create_autocmd({ "CmdlineLeave" }, {
+ pattern = { "*" },
+ callback = function(event)
+ if vim.v.event.abort then
+ return
+ end
+ if event.match == "/" or event.match == "?" then
+ vim.defer_fn(function()
+ local searched = m.search_count(fn.getreg("/"))
+ if searched and m.config.scroll_center then
+ scroll_to_center()
+ end
+ end, 100)
+ end
+ end,
+ })
+ end
+
+ m.search_count = function(word)
+ hide_search_count(0)
+ if word == "" then
+ return false
+ end
+
+ local cur_cnt = 0
+ local total_cnt = 0
+ local buf_content = fn.join(api.nvim_buf_get_lines(0, 0, -1, {}), "\n")
+ local cur_pos = #fn.join(api.nvim_buf_get_lines(0, 0, fn.line(".") - 1, {}), "\n") + ((fn.line(".") == 1) and 0 or 1) +
+ fn.col(".") - 1
+ local lst_pos = 0
+ while true do
+ local mat_pos = fn.matchstrpos(buf_content, word, lst_pos, 1)
+ if mat_pos[1] == "" then
+ break
+ end
+ total_cnt = total_cnt + 1
+ if cur_pos >= mat_pos[2] and cur_pos < mat_pos[3] then
+ cur_cnt = total_cnt
+ end
+ lst_pos = mat_pos[3]
+ end
+
+ if total_cnt == 0 or cur_cnt == 0 then
+ return false
+ end
+
+ local count = " [" .. cur_cnt .. "/" .. total_cnt .. "]"
+ display_search_count(word, count)
+
+ return true
+ end
+
+ m.NavigateToWord = function(forward)
+ local word = nearest_word_at_cursor()
+ if not word then
+ word = fn.getreg("/")
+ end
+ if word == "" then
+ return
+ end
+
+ local search_flag = ""
+ if not forward then
+ search_flag = "b"
+ end
+ local n = fn.search(word, search_flag)
+ if n ~= 0 then
+ if m.config.scroll_center then
+ scroll_to_center()
+ end
+ else
+ vim.notify("Pattern not found: " .. filter(word))
+ return
+ end
+
+ if m.config.search_count then
+ m.search_count(word)
+ end
+ end
+
+ m.InterestingWord = function(mode, search)
+ local word = ""
+ if mode == "v" then
+ word = get_visual_selection()
+ else
+ word = "\\<" .. fn.expand("<cword>") .. "\\>"
+ end
+ if #word == 0 then
+ return
+ end
+ word = get_reg_ex(word)
+
+ if search then
+ if word == fn.getreg("/") then
+ fn.setreg("/", "")
+ word = ""
+ else
+ fn.setreg("/", word)
+ vim.cmd("set hls")
+ end
+ else
+ if m.words[word] then
+ uncolor(word)
+ word = ""
+ else
+ color(word)
+ end
+ end
+
+ if m.config.search_count then
+ m.search_count(word)
+ end
+ end
+
+ m.UncolorAllWords = function(search)
+ m.search_count("")
+ if search then
+ fn.setreg("/", "")
+ else
+ local windows = api.nvim_list_wins()
+ for _, v in pairs(m.words) do
+ for _, i in ipairs(windows) do
+ pcall(function()
+ fn.matchdelete(v.mid, i)
+ end)
+ end
+ m.colors[v.color] = v.mid
+ end
+
+ m.words = {}
+ end
+ end
+
+ m.setup = function(opt)
+ opt = opt or {}
+ m.config = vim.tbl_deep_extend("force", get_default_config(), opt)
+
+ init_colors()
+ math.randomseed(uv.now())
+
+ local group = api.nvim_create_augroup("InterestingWordsGroup", { clear = true })
+ api.nvim_create_autocmd({ "WinEnter" }, {
+ callback = function()
+ recolorAllWords()
+ local windows = api.nvim_list_wins()
+ for _, i in ipairs(windows) do
+ hide_search_count(api.nvim_win_get_buf(fn.win_getid(i)))
+ end
+ end,
+ group = group,
+ })
+
+ if m.config.navigation then
+ vim.keymap.set("n", "n", function()
+ m.NavigateToWord(true)
+ end, { noremap = true, silent = true, desc = "InterestingWord Navigation Forward" })
+ vim.keymap.set("n", "N", m.NavigateToWord,
+ { noremap = true, silent = true, desc = "InterestingWord Navigation Backword" })
+ end
+
+ if m.config.search_key then
+ vim.keymap.set("n", m.config.search_key, function()
+ m.InterestingWord("n", true)
+ end, { noremap = true, silent = true, desc = "InterestingWord Toggle Search" })
+ vim.keymap.set("x", m.config.search_key, function()
+ m.InterestingWord("v", true)
+ end, { noremap = true, silent = true, desc = "InterestingWord Toggle Search" })
+ vim.keymap.set("n", m.config.cancel_search_key, function()
+ m.UncolorAllWords(true)
+ end, { noremap = true, silent = true, desc = "InterestingWord Unsearch" })
+ end
+
+ if m.config.color_key then
+ vim.keymap.set("n", m.config.color_key, function()
+ m.InterestingWord("n", false)
+ end, { noremap = true, silent = true, desc = "InterestingWord Toggle Color" })
+ vim.keymap.set("x", m.config.color_key, function()
+ m.InterestingWord("v", false)
+ end, { noremap = true, silent = true, desc = "InterestingWord Toggle Color" })
+ vim.keymap.set("n", m.config.cancel_color_key, function()
+ m.UncolorAllWords()
+ end, { noremap = true, silent = true, desc = "InterestingWord Uncolor" })
+ end
+
+ if m.config.search_count then
+ m.init_search_count()
+ end
+ end
+
+ return m
+end)()
+
+interestingwords.setup({
+ select_mode = "loop", -- or "random"
+ scroll_center = false,
+ search_key = "<leader>S",
+ cancel_search_key = "<leader>C",
+ color_key = "<leader>H",
+ cancel_color_key = "<leader>C",
+ colors = { "#ff5f5f", "#5fafff", "#afff5f", "#ffd75f" },
+})
diff --git a/common/nvim/lua/plugins/leetcode.lua b/common/nvim/lua/plugins/leetcode.lua
new file mode 100755
index 0000000..50369e1
--- /dev/null
+++ b/common/nvim/lua/plugins/leetcode.lua
@@ -0,0 +1,68 @@
+---@alias lc.lang
+---| "cpp"
+---| "java"
+---| "python"
+---| "python3"
+---| "c"
+---| "csharp"
+---| "javascript"
+---| "typescript"
+---| "php"
+---| "swift"
+---| "kotlin"
+---| "dart"
+---| "golang"
+---| "ruby"
+---| "scala"
+---| "rust"
+---| "racket"
+---| "erlang"
+---| "elixir"
+
+---@alias lc.sql_lang
+---| "pythondata"
+---| "mysql"
+---| "mssql"
+---| "oraclesql"
+
+---@alias lc.domain
+---| "com"
+---| "cn"
+
+---@class lc.UserConfig
+local M = {
+ ---@type lc.domain
+ domain = 'com', -- For now "com" is the only one supported
+
+ ---@type string
+ arg = 'leetcode.nvim',
+
+ ---@type lc.lang
+ lang = 'cpp',
+
+ ---@type lc.sql_lang
+ sql = 'mysql',
+
+ ---@type string
+ directory = vim.fn.stdpath('data') .. '/leetcode/',
+
+ ---@type boolean
+ logging = true,
+
+ console = {
+ ---@type boolean
+ open_on_runcode = false,
+
+ size = {
+ width = '75%', ---@type string | integer
+ height = '75%', ---@type string | integer
+ },
+ dir = 'row', ---@type "col" | "row"
+ },
+
+ description = {
+ width = '40%', ---@type string | integer
+ },
+}
+
+return M
diff --git a/common/nvim/lua/plugins/loclist.lua b/common/nvim/lua/plugins/loclist.lua
new file mode 100755
index 0000000..9b72a94
--- /dev/null
+++ b/common/nvim/lua/plugins/loclist.lua
@@ -0,0 +1,18 @@
+local M = {}
+
+function M.loclist_toggle()
+ for _, info in ipairs(vim.fn.getwininfo()) do
+ if info.loclist == 1 then
+ vim.cmd('lclose')
+ return
+ end
+ end
+
+ if next(vim.fn.getloclist(0)) == nil then
+ print('loc list empty')
+ return
+ end
+ vim.cmd('lopen')
+end
+
+return M
diff --git a/common/nvim/lua/plugins/lsp.lua b/common/nvim/lua/plugins/lsp.lua
new file mode 100755
index 0000000..5ed1152
--- /dev/null
+++ b/common/nvim/lua/plugins/lsp.lua
@@ -0,0 +1,674 @@
+local M = {}
+
+-- Safe require helper
+local function safe_require(name)
+ local ok, mod = pcall(require, name)
+ return ok and mod or nil
+end
+
+-- Autocmd groups for managing event listeners
+local augroup_format = vim.api.nvim_create_augroup("LspFormattingOnSave", { clear = true })
+local augroup_diag_float = vim.api.nvim_create_augroup("ShowLineDiagnostics", { clear = true })
+local augroup_diag_load = vim.api.nvim_create_augroup("OpenDiagnosticsOnLoad", { clear = true })
+local augroup_highlight = vim.api.nvim_create_augroup("LspDocumentHighlight", { clear = true })
+
+-- Border for floating windows
+local border = {
+ { "┌", "FloatBorder" }, { "─", "FloatBorder" }, { "┐", "FloatBorder" },
+ { "│", "FloatBorder" }, { "┘", "FloatBorder" }, { "─", "FloatBorder" },
+ { "└", "FloatBorder" }, { "│", "FloatBorder" }
+}
+
+-- Initialize LSP modules
+local function init_modules()
+ -- Silently try to load each module
+ M.lspconfig = safe_require("lspconfig")
+ M.mason = safe_require("mason")
+ M.mason_lspconfig = safe_require("mason-lspconfig")
+ M.mason_tool_installer = safe_require("mason-tool-installer")
+ M.null_ls = safe_require("null-ls")
+
+ if M.null_ls then
+ M.builtins = M.null_ls.builtins
+ end
+
+ return true
+end
+
+-- Check Neovim version compatibility and feature availability
+local function has_feature(feature)
+ if feature == "diagnostic_api" then
+ return vim.fn.has("nvim-0.6") == 1
+ elseif feature == "native_lsp_config" then
+ -- Check for both vim.lsp.enable AND vim.lsp.config
+ return vim.fn.has("nvim-0.11") == 1 and vim.lsp.enable ~= nil
+ elseif feature == "lsp_get_client_by_id" then
+ return vim.fn.has("nvim-0.10") == 1
+ elseif feature == "cmp_nvim_lsp" then
+ return pcall(require, "cmp_nvim_lsp")
+ elseif feature == "virtual_text_disabled_by_default" then
+ return vim.fn.has("nvim-0.11") == 1
+ elseif feature == "deprecated_lsp_handlers" then
+ -- vim.lsp.handlers.hover and signature_help deprecated in 0.12, removed in 0.13
+ return vim.fn.has("nvim-0.12") == 0
+ elseif feature == "new_lsp_config_api" then
+ -- New LSP config API available from 0.12+
+ return vim.fn.has("nvim-0.12") == 1 and vim.lsp.config ~= nil
+ end
+ return false
+end
+
+-- Backwards compatible capabilities setup
+local function setup_capabilities()
+ local capabilities
+
+ if has_feature("cmp_nvim_lsp") then
+ capabilities = require('cmp_nvim_lsp').default_capabilities()
+ elseif vim.lsp.protocol and vim.lsp.protocol.make_client_capabilities then
+ capabilities = vim.lsp.protocol.make_client_capabilities()
+ else
+ capabilities = {}
+ end
+
+ -- Add snippet support if available
+ if capabilities.textDocument then
+ capabilities.textDocument.completion = capabilities.textDocument.completion or {}
+ capabilities.textDocument.completion.completionItem =
+ capabilities.textDocument.completion.completionItem or {}
+ capabilities.textDocument.completion.completionItem.snippetSupport = true
+ end
+
+ -- Set offset encoding for newer versions (0.11+ supports utf-8 and utf-32)
+ if vim.fn.has("nvim-0.11") == 1 then
+ capabilities.offsetEncoding = { "utf-8", "utf-32", "utf-16" }
+ elseif vim.fn.has("nvim-0.9") == 1 then
+ capabilities.offsetEncoding = { "utf-8", "utf-16" }
+ end
+
+ return capabilities
+end
+
+-- Default LSP keymaps (fallback if external not available)
+local function setup_fallback_keymaps(bufnr)
+ -- Only set up minimal fallbacks, prefer external setup
+ local opts = { buffer = bufnr, silent = true, noremap = true }
+ vim.keymap.set('n', 'gd', vim.lsp.buf.definition, opts)
+ vim.keymap.set('n', 'K', vim.lsp.buf.hover, opts)
+ vim.keymap.set('n', '[d', vim.diagnostic.goto_prev, opts)
+ vim.keymap.set('n', ']d', vim.diagnostic.goto_next, opts)
+end
+
+-- Create LSP directory and config files for native LSP
+local function setup_native_lsp_configs()
+ local config_path = vim.fn.stdpath("config")
+ local lsp_dir = config_path .. "/lsp"
+
+ -- Create lsp directory if it doesn't exist
+ vim.fn.mkdir(lsp_dir, "p")
+
+ -- LSP server configurations for native config
+ local server_configs = {
+ lua_ls = {
+ cmd = { "lua-language-server" },
+ filetypes = { "lua" },
+ root_markers = { ".luarc.json", ".luarc.jsonc", ".luacheckrc", ".stylua.toml", "stylua.toml", "selene.toml", "selene.yml" },
+ settings = {
+ Lua = {
+ diagnostics = {
+ globals = { "vim", "use", "_G", "packer_plugins", "P" },
+ disable = {
+ "undefined-global",
+ "lowercase-global",
+ "unused-local",
+ "unused-vararg",
+ "trailing-space"
+ },
+ },
+ workspace = {
+ library = {
+ vim.env.VIMRUNTIME,
+ "${3rd}/luv/library",
+ "${3rd}/busted/library",
+ },
+ checkThirdParty = false,
+ },
+ telemetry = {
+ enable = false,
+ },
+ },
+ },
+ },
+
+ pyright = {
+ cmd = { "pyright-langserver", "--stdio" },
+ filetypes = { "python" },
+ root_markers = { "pyproject.toml", "setup.py", "setup.cfg", "requirements.txt", "Pipfile", "pyrightconfig.json" },
+ settings = {
+ python = {
+ formatting = {
+ provider = "none"
+ }
+ }
+ }
+ },
+
+ ts_ls = {
+ cmd = { "typescript-language-server", "--stdio" },
+ filetypes = { "javascript", "javascriptreact", "javascript.jsx", "typescript", "typescriptreact", "typescript.tsx" },
+ root_markers = { "tsconfig.json", "jsconfig.json", "package.json" },
+ init_options = {
+ disableAutomaticTypeAcquisition = true
+ },
+ },
+
+ rust_analyzer = {
+ cmd = { "rust-analyzer" },
+ filetypes = { "rust" },
+ root_markers = { "Cargo.toml", "rust-project.json" },
+ },
+
+ clangd = {
+ cmd = { "clangd", "--background-index", "--clang-tidy", "--header-insertion=iwyu" },
+ filetypes = { "c", "cpp", "objc", "objcpp", "cuda", "proto" },
+ root_markers = { ".clangd", ".clang-tidy", ".clang-format", "compile_commands.json", "compile_flags.txt", "configure.ac" },
+ },
+
+ gopls = {
+ cmd = { "gopls" },
+ filetypes = { "go", "gomod", "gowork", "gotmpl" },
+ root_markers = { "go.work", "go.mod" },
+ settings = {
+ gopls = {
+ gofumpt = true,
+ codelenses = {
+ gc_details = false,
+ generate = true,
+ regenerate_cgo = true,
+ run_govulncheck = true,
+ test = true,
+ tidy = true,
+ upgrade_dependency = true,
+ vendor = true,
+ },
+ hints = {
+ assignVariableTypes = true,
+ compositeLiteralFields = true,
+ compositeLiteralTypes = true,
+ constantValues = true,
+ functionTypeParameters = true,
+ parameterNames = true,
+ rangeVariableTypes = true,
+ },
+ analyses = {
+ fieldalignment = true,
+ nilness = true,
+ unusedparams = true,
+ unusedwrite = true,
+ useany = true,
+ },
+ usePlaceholders = true,
+ completeUnimported = true,
+ staticcheck = true,
+ directoryFilters = { "-.git", "-.vscode", "-.idea", "-.vscode-test", "-node_modules" },
+ semanticTokens = true,
+ },
+ },
+ },
+
+ -- Add more basic configs
+ bashls = {
+ cmd = { "bash-language-server", "start" },
+ filetypes = { "sh", "bash" },
+ },
+
+ --html = {
+ -- cmd = { "vscode-html-language-server", "--stdio" },
+ -- filetypes = { "html" },
+ --},
+
+ --cssls = {
+ -- cmd = { "vscode-css-language-server", "--stdio" },
+ -- filetypes = { "css", "scss", "less" },
+ --},
+
+ --jsonls = {
+ -- cmd = { "vscode-json-language-server", "--stdio" },
+ -- filetypes = { "json", "jsonc" },
+ --},
+
+ yamlls = {
+ cmd = { "yaml-language-server", "--stdio" },
+ filetypes = { "yaml", "yml" },
+ },
+ }
+
+ -- Write config files to lsp directory
+ for server_name, config in pairs(server_configs) do
+ local file_path = lsp_dir .. "/" .. server_name .. ".lua"
+ local file_content = "return " .. vim.inspect(config)
+
+ -- Only write if file doesn't exist to avoid overwriting user customizations
+ if vim.fn.filereadable(file_path) == 0 then
+ local file = io.open(file_path, "w")
+ if file then
+ file:write(file_content)
+ file:close()
+ vim.notify("Created LSP config: " .. file_path, vim.log.levels.DEBUG)
+ end
+ end
+ end
+
+ return vim.tbl_keys(server_configs)
+end
+
+-- Set up LSP on_attach function
+local function create_on_attach()
+ return function(client, bufnr)
+ -- Your existing keymap setup function from keys.lua
+ if _G.setup_lsp_keymaps then
+ _G.setup_lsp_keymaps(bufnr)
+ else
+ setup_fallback_keymaps(bufnr)
+ end
+
+ -- Disable LSP formatting in favor of null-ls (if null-ls is available)
+ if M.null_ls then
+ client.server_capabilities.documentFormattingProvider = false
+ client.server_capabilities.documentRangeFormattingProvider = false
+ end
+
+ -- Disable specific LSP capabilities to avoid conflicts
+ if client.name == "ruff" then
+ -- Disable ruff hover in favor of Pyright
+ client.server_capabilities.hoverProvider = false
+ elseif client.name == "ts_ls" then
+ -- Disable ts_ls formatting in favor of prettier via null-ls
+ client.server_capabilities.documentFormattingProvider = false
+ client.server_capabilities.documentRangeFormattingProvider = false
+ elseif client.name == "pyright" and M.null_ls then
+ -- Disable pyright formatting in favor of black/isort via null-ls
+ client.server_capabilities.documentFormattingProvider = false
+ client.server_capabilities.documentRangeFormattingProvider = false
+ end
+
+ -- Set log level (backwards compatible)
+ if vim.lsp.set_log_level then
+ vim.lsp.set_log_level("warn")
+ end
+
+ -- Document highlight on cursor hold
+ if client.server_capabilities and client.server_capabilities.documentHighlightProvider then
+ vim.api.nvim_create_autocmd("CursorHold", {
+ group = augroup_highlight,
+ buffer = bufnr,
+ callback = function()
+ if vim.lsp.buf.document_highlight then
+ vim.lsp.buf.document_highlight()
+ end
+ end,
+ })
+ vim.api.nvim_create_autocmd("CursorMoved", {
+ group = augroup_highlight,
+ buffer = bufnr,
+ callback = function()
+ if vim.lsp.buf.clear_references then
+ vim.lsp.buf.clear_references()
+ end
+ end,
+ })
+ end
+ end
+end
+
+-- Set up basic LSP configuration
+function M.setup()
+ -- Initialize all required modules
+ init_modules()
+
+ -- Enable virtual_text diagnostics by default for 0.11+ (since it's disabled by default)
+ if has_feature("virtual_text_disabled_by_default") then
+ vim.diagnostic.config({ virtual_text = true })
+ end
+
+ -- Set up Mason if available (useful for tool management)
+ if M.mason then
+ M.mason.setup({
+ ui = {
+ border = 'rounded',
+ icons = {
+ package_installed = '✓',
+ package_pending = '➜',
+ package_uninstalled = '✗'
+ }
+ }
+ })
+ end
+
+ -- Set up mason-tool-installer if available
+ if M.mason_tool_installer then
+ M.mason_tool_installer.setup({
+ ensure_installed = {
+ -- Language servers
+ "lua-language-server", "pyright", "typescript-language-server", "rust-analyzer",
+ "clangd", "bash-language-server", "yaml-language-server",
+ -- Formatters
+ "stylua", "clang-format", "prettier", "shfmt", "black", "isort", "goimports",
+ "sql-formatter", "shellharden",
+ -- Linters/Diagnostics
+ "eslint_d", "selene", "flake8", "dotenv-linter", "phpcs",
+ -- Utilities
+ "jq"
+ },
+ auto_update = false,
+ run_on_start = true,
+ start_delay = 3000,
+ })
+ end
+
+ -- Set up null-ls if available
+ if M.null_ls and M.builtins then
+ local sources = {
+ M.builtins.diagnostics.selene.with({
+ condition = function(utils)
+ return utils.root_has_file({"selene.toml"})
+ end,
+ }),
+ M.builtins.diagnostics.dotenv_linter,
+ M.builtins.diagnostics.tidy,
+ M.builtins.diagnostics.phpcs.with({
+ condition = function(utils)
+ return utils.root_has_file({"phpcs.xml", "phpcs.xml.dist", ".phpcs.xml", ".phpcs.xml.dist"})
+ end,
+ }),
+
+ -- Formatters (prioritized over LSP formatting)
+ M.builtins.formatting.stylua.with({
+ extra_args = { "--quote-style", "AutoPreferSingle", "--indent-width", "2", "--column-width", "160" },
+ condition = function(utils)
+ return utils.root_has_file({"stylua.toml", ".stylua.toml"})
+ end,
+ }),
+ M.builtins.formatting.prettier.with({
+ extra_args = { "--single-quote", "--tab-width", "4", "--print-width", "100" },
+ filetypes = { "javascript", "javascriptreact", "typescript", "typescriptreact", "vue", "css", "scss", "less", "html", "json", "jsonc", "yaml", "markdown", "graphql", "handlebars" },
+ prefer_local = "node_modules/.bin",
+ }),
+ M.builtins.formatting.black.with({
+ extra_args = { "--fast" },
+ prefer_local = ".venv/bin",
+ }),
+ M.builtins.formatting.isort.with({
+ extra_args = { "--profile", "black" },
+ prefer_local = ".venv/bin",
+ }),
+ M.builtins.formatting.goimports,
+ M.builtins.formatting.clang_format.with({
+ extra_args = { "--style", "{BasedOnStyle: Google, IndentWidth: 4}" }
+ }),
+ M.builtins.formatting.shfmt.with({
+ extra_args = { "-i", "2", "-ci" }
+ }),
+ M.builtins.formatting.shellharden,
+ M.builtins.formatting.sql_formatter,
+ M.builtins.formatting.dart_format,
+
+ -- Code actions
+ M.builtins.code_actions.gitsigns,
+ M.builtins.code_actions.gitrebase,
+ }
+
+ M.null_ls.setup({
+ sources = sources,
+ update_in_insert = false,
+ on_attach = function(client, bufnr)
+ -- Disable LSP formatting in favor of null-ls
+ client.server_capabilities.documentFormattingProvider = false
+ client.server_capabilities.documentRangeFormattingProvider = false
+
+ local function lsp_supports_method(client, method)
+ if client.supports_method then
+ return client:supports_method(method)
+ elseif client.server_capabilities then
+ local capability_map = {
+ ["textDocument/formatting"] = "documentFormattingProvider",
+ ["textDocument/rangeFormatting"] = "documentRangeFormattingProvider",
+ ["textDocument/hover"] = "hoverProvider",
+ ["textDocument/signatureHelp"] = "signatureHelpProvider",
+ ["textDocument/documentHighlight"] = "documentHighlightProvider",
+ }
+ local cap = capability_map[method]
+ return cap and client.server_capabilities[cap]
+ end
+ return false
+ end
+
+ if lsp_supports_method(client, "textDocument/formatting") then
+ vim.api.nvim_create_autocmd("BufWritePre", {
+ group = augroup_format,
+ buffer = bufnr,
+ callback = function()
+ if vim.fn.has("nvim-0.8") == 1 then
+ vim.lsp.buf.format({
+ async = false,
+ bufnr = bufnr,
+ filter = function(formatting_client)
+ return formatting_client.name == "null-ls"
+ end,
+ })
+ else
+ vim.lsp.buf.formatting_sync()
+ end
+ end,
+ })
+ end
+ end,
+ })
+ end
+
+ -- Set up LSP capabilities
+ local capabilities = setup_capabilities()
+ local on_attach = create_on_attach()
+
+ -- Set up LSP handlers with version compatibility (avoid deprecated APIs)
+ if has_feature("deprecated_lsp_handlers") then
+ -- Use old handler setup for versions before 0.12
+ if vim.lsp.handlers then
+ vim.lsp.handlers['textDocument/hover'] = vim.lsp.with(
+ vim.lsp.handlers.hover, { border = 'rounded' }
+ )
+
+ vim.lsp.handlers['textDocument/signatureHelp'] = vim.lsp.with(
+ vim.lsp.handlers.signature_help, { border = 'rounded' }
+ )
+ end
+ else
+ -- Use new handler setup for 0.12+ (when old handlers are deprecated/removed)
+ if vim.lsp.handlers then
+ vim.lsp.handlers['textDocument/hover'] = vim.lsp.with(
+ vim.lsp.handlers['textDocument/hover'], { border = 'rounded' }
+ )
+
+ vim.lsp.handlers['textDocument/signatureHelp'] = vim.lsp.with(
+ vim.lsp.handlers['textDocument/signatureHelp'], { border = 'rounded' }
+ )
+ end
+ end
+
+ -- Choose configuration method based on Neovim version and available features
+ if has_feature("native_lsp_config") then
+ -- Set up native LSP configuration
+ local servers = setup_native_lsp_configs()
+
+ -- Set default on_attach and capabilities for all LSP servers
+ vim.lsp.config('*', {
+ on_attach = on_attach,
+ capabilities = capabilities,
+ })
+
+ -- Enable the LSP servers
+ vim.lsp.enable(servers)
+
+ elseif M.mason_lspconfig and M.lspconfig then
+ -- Set up mason-lspconfig if available
+ if M.mason_lspconfig then
+ M.mason_lspconfig.setup({
+ ensure_installed = {
+ "lua_ls", "pyright", "ts_ls", "rust_analyzer", "clangd", "gopls",
+ "bashls", "html", "cssls", "jsonls", "yamlls"
+ },
+ automatic_installation = true,
+ })
+ end
+
+ -- Use traditional lspconfig with mason
+ local enabled_servers = {}
+
+ local server_configs = {
+ lua_ls = {
+ settings = {
+ Lua = {
+ diagnostics = {
+ globals = { "vim", "use", "_G", "packer_plugins", "P" },
+ },
+ workspace = {
+ library = {
+ vim.env.VIMRUNTIME,
+ "${3rd}/luv/library",
+ "${3rd}/busted/library",
+ },
+ checkThirdParty = false,
+ },
+ telemetry = { enable = false },
+ },
+ },
+ },
+ pyright = {
+ settings = {
+ python = {
+ formatting = { provider = "none" }
+ }
+ }
+ },
+ ts_ls = {
+ init_options = {
+ disableAutomaticTypeAcquisition = true
+ },
+ },
+ clangd = {
+ cmd = { "clangd", "--background-index", "--clang-tidy", "--header-insertion=iwyu" },
+ },
+ gopls = {
+ settings = {
+ gopls = {
+ gofumpt = true,
+ usePlaceholders = true,
+ completeUnimported = true,
+ staticcheck = true,
+ },
+ },
+ },
+ }
+
+ M.mason_lspconfig.setup_handlers({
+ function(server_name)
+ if not enabled_servers[server_name] then
+ local config = server_configs[server_name] or {}
+ config.on_attach = on_attach
+ config.capabilities = capabilities
+ M.lspconfig[server_name].setup(config)
+ enabled_servers[server_name] = true
+ end
+ end,
+ })
+
+ elseif M.lspconfig then
+ -- Fallback: Set up servers manually if mason-lspconfig is not available
+ local servers = { 'lua_ls', 'pyright', 'ts_ls', 'rust_analyzer', 'clangd', 'gopls', 'bashls', 'html', 'cssls', 'jsonls', 'yamlls' }
+ local enabled_servers = {}
+
+ for _, server in ipairs(servers) do
+ if not enabled_servers[server] and M.lspconfig[server] then
+ local config = {
+ on_attach = on_attach,
+ capabilities = capabilities,
+ }
+ M.lspconfig[server].setup(config)
+ enabled_servers[server] = true
+ end
+ end
+ end
+
+ return true
+end
+
+-- Global toggle for diagnostics (backwards compatible)
+vim.g.diagnostics_visible = true
+function _G.toggle_diagnostics()
+ if has_feature("diagnostic_api") then
+ if vim.g.diagnostics_visible then
+ vim.g.diagnostics_visible = false
+ vim.diagnostic.disable()
+ else
+ vim.g.diagnostics_visible = true
+ vim.diagnostic.enable()
+ end
+ else
+ -- Fallback for older versions
+ if vim.g.diagnostics_visible then
+ vim.g.diagnostics_visible = false
+ vim.lsp.handlers["textDocument/publishDiagnostics"] = function() end
+ else
+ vim.g.diagnostics_visible = true
+ vim.lsp.handlers["textDocument/publishDiagnostics"] = vim.lsp.with(
+ vim.lsp.diagnostic.on_publish_diagnostics, {}
+ )
+ end
+ end
+end
+
+-- Create Mason command if Mason is available
+if M.mason then
+ vim.api.nvim_create_user_command("Mason", function()
+ require("mason.ui").open()
+ end, {})
+end
+
+-- Automatically show diagnostics in a float window for the current line
+if has_feature("diagnostic_api") then
+ vim.api.nvim_create_autocmd("CursorHold", {
+ group = augroup_diag_float,
+ pattern = "*",
+ callback = function()
+ local opts = {
+ focusable = false,
+ close_events = { "BufLeave", "CursorMoved", "InsertEnter", "FocusLost" },
+ border = border,
+ source = "always",
+ prefix = " ",
+ scope = "cursor",
+ }
+ vim.diagnostic.open_float(nil, opts)
+ end,
+ })
+
+ -- Autocmd to open the diagnostic window when a file with errors is opened
+ vim.api.nvim_create_autocmd({ "LspAttach", "BufReadPost" }, {
+ group = augroup_diag_load,
+ callback = function()
+ local has_errors = #vim.diagnostic.get(0, { severity = vim.diagnostic.severity.ERROR }) > 0
+ if has_errors then
+ vim.diagnostic.setqflist({
+ open = true,
+ title = "Diagnostics",
+ })
+ end
+ end,
+ })
+end
+
+-- Create Toggle Diagnostic command
+vim.api.nvim_create_user_command("ToggleDiagnostics", _G.toggle_diagnostics, {
+ desc = "Toggle global diagnostics visibility"
+})
+
+return M
diff --git a/common/nvim/lua/plugins/lualine.lua b/common/nvim/lua/plugins/lualine.lua
new file mode 100755
index 0000000..9c1cc43
--- /dev/null
+++ b/common/nvim/lua/plugins/lualine.lua
@@ -0,0 +1,22 @@
+-- lualine.nvim plugin config (modular, robust)
+local ok, lualine = pcall(require, 'lualine')
+if not ok then return end
+local nvim_version = vim.version()
+if nvim_version.major == 0 and nvim_version.minor < 5 then return end
+lualine.setup({
+ options = {
+ theme = 'auto',
+ icons_enabled = true,
+ section_separators = '',
+ component_separators = '',
+ disabled_filetypes = {},
+ },
+ sections = {
+ lualine_a = {'mode'},
+ lualine_b = {'branch', 'diff', 'diagnostics'},
+ lualine_c = {'filename'},
+ lualine_x = {'encoding', 'fileformat', 'filetype'},
+ lualine_y = {'progress'},
+ lualine_z = {'location'},
+ },
+}) \ No newline at end of file
diff --git a/common/nvim/lua/plugins/luasnip.lua b/common/nvim/lua/plugins/luasnip.lua
new file mode 100755
index 0000000..75f4c28
--- /dev/null
+++ b/common/nvim/lua/plugins/luasnip.lua
@@ -0,0 +1,13 @@
+-- LuaSnip plugin config (modular, robust)
+local ok, luasnip = pcall(require, 'luasnip')
+if not ok then return end
+local nvim_version = vim.version()
+if nvim_version.major == 0 and nvim_version.minor < 5 then return end
+-- Load friendly-snippets if available
+pcall(function()
+ require('luasnip.loaders.from_vscode').lazy_load()
+end)
+luasnip.config.set_config({
+ history = true,
+ updateevents = "TextChanged,TextChangedI",
+}) \ No newline at end of file
diff --git a/common/nvim/lua/plugins/messages.lua b/common/nvim/lua/plugins/messages.lua
new file mode 100755
index 0000000..8e46c09
--- /dev/null
+++ b/common/nvim/lua/plugins/messages.lua
@@ -0,0 +1,85 @@
+local M = {
+ 'Why do programmers prefer dark mode? Because light attracts bugs!',
+ 'Why did the AI break up with its computer? It found someone with better algorithms!',
+ "Why do Python programmers prefer snakes? Because they can't stand Java!",
+ 'Why did the developer go to the beach? To catch some rays and debug JavaScript!',
+ "Why was the HTML document lonely? It didn't have any <body> to share its content with!",
+ "Why did the CSS file break up with the HTML file? It couldn't stand the layout!",
+ 'Why do programmers always mix up Christmas and Halloween? Because Oct 31 == Dec 25!',
+ 'Why did the computer take up gardening? It wanted to improve its root system!',
+ 'Why do programmers prefer dark chocolate? It has better byte-size!',
+ "Why did the developer get mad at their computer? It couldn't understand their emotional code!",
+ 'Why was the JavaScript developer so good at relationships? They knew how to handle callbacks!',
+ 'Why did the coder go broke? They lost all their cache!',
+ 'Why did the SQL query go to therapy? It had too many inner joins!',
+ 'Why did the programmer plant a light bulb? They wanted to grow a power plant!',
+ 'Why did the computer keep its drink on the windowsill? It wanted a byte!',
+ "Why don't programmers like nature? It has too many bugs!",
+ 'Why did the developer go broke? They spent all their money on keyboard shortcuts!',
+ 'Why did the computer cross the road? To get to the other website!',
+ 'Why was the code cold? It left its Windows open!',
+ 'Why did the coder go to therapy? They had too many issues!',
+ 'Why was the function sad? It returned null!',
+ "Why did the programmer quit their job? They didn't get arrays!",
+ 'Why was the loop so fast? It was in a hurry!',
+ 'Why was the computer cold? It left its Windows open!',
+ "Why did the developer stay calm during the crisis? Because they knew how to 'handle' exceptions!",
+ "Why did the JavaScript developer always smile? Because they had 'callbacks' for everything!",
+ "Why did the programmer break up with their keyboard? It had too many 'commitment' issues!",
+ "Why don't Neovim users ever get lost in their text files? Because they always 'find' their way!",
+ "Why don't Neovim users need a GPS? Because they're experts at 'mapping' their routes!",
+ 'Why did the Neovim user become a musician? Because they can play the keyboard like a pro!',
+ "Why don't Neovim users ever lose track of time? Because they have a 'status line' to keep them informed!",
+ "Why did the Neovim user open a detective agency? Because they have an 'eye' for spotting code errors!",
+ 'Why did the developer bring a ladder to the coding competition? To take their code to the next level!',
+ "When your code is running slowly: 'It's not a bug; it's a feature that takes its time.'",
+ "Why did the programmer go to therapy? Because their code had too many 'issues'!",
+ "Why was the JavaScript developer sad? Because they didn't 'console' their feelings!",
+ "Why did the developer get locked out of their own codebase? They forgot the 'key'!",
+ 'Welcome to Neovim, where plugins multiply faster than rabbits!',
+ "How many programmers does it take to change a lightbulb? None, that's a hardware problem!",
+ "When you're debugging and can't find the issue: 'I swear, it was working yesterday!'",
+ "Why don't programmers trust stairs? Because they're always up to 'something'!",
+ "When you fix a bug without even trying: 'I guess I'm just that good.'",
+ 'Why was the computer cold? It left its Windows open!',
+ "Why do Java developers wear glasses? Because they don't C#!",
+ "Why did the programmer quit their job? They didn't get arrays!",
+ "When you write a one-liner that solves a complex problem: 'I am a genius, yes, I am.'",
+ "When you refactor your code and it breaks everything: 'I've made a huge mistake.'",
+ "When you accidentally close your editor with unsaved changes: 'Goodbye, cruel world.'",
+ "When you discover a bug on a Friday afternoon: 'Looks like we're working late again.'",
+ "When you realize your code from last year: 'Who wrote this junk? Oh, wait...'",
+ "When you write a comment and six months later can't understand it: 'I speak my own language.'",
+ "When you join a new project with zero documentation: 'Here be dragons.'",
+ "When you add a 'TODO' comment and hope someone else will deal with it: 'Not my problem.'",
+ "Remember, coding is not just about writing code; it's about solving problems.",
+ 'Stay curious and never stop learning. Technology is always evolving.',
+ "When debugging, don't guess; use systematic troubleshooting techniques.",
+ "Keep your code DRY (Don't Repeat Yourself) to make it more maintainable.",
+ 'Use meaningful variable and function names. Your code should read like a story.',
+ 'Always test your code thoroughly before deploying it. Automated tests are your friends.',
+ 'Spend time designing your code before jumping into implementation. Good architecture pays off.',
+ 'Learn to break down complex problems into smaller, manageable tasks.',
+ "Code with the future in mind. Write code that's easy to understand and maintain.",
+ 'Version control is your safety net. Use Git or other VCS systems religiously.',
+ 'Document your code and processes. It will save you and your team countless hours.',
+ "Don't optimize prematurely. Measure first, then optimize where it matters.",
+ "Read other people's code. It's a great way to learn different coding styles and techniques.",
+ 'Stay organized with your project structure. Consistency makes collaboration smoother.',
+ 'Take regular breaks to prevent burnout. Your productivity will thank you.',
+ 'Use comments sparingly but effectively. Explain why, not just what.',
+ 'Consider pair programming or code reviews to catch issues early and learn from others.',
+ 'Know when to ask for help. Programming is a team effort.',
+ "Programming is not just about the code; it's about the problem-solving mindset.",
+ 'Keep your development environment clean and well-maintained for consistent productivity.',
+ 'Learn from your mistakes and failures; they are valuable lessons in programming.',
+ 'When faced with a bug, isolate and reproduce it before attempting to fix it.',
+ "Why did the developer stay calm during the crisis? Because they knew how to 'handle' exceptions.",
+ "Why was the JavaScript developer always smiling? Because they had 'callbacks' for everything!",
+ "Why did the programmer break up with their keyboard? It had too many 'commitment' issues!",
+ "Margaret Hamilton coined the term 'software engineer.'",
+ 'Why did the function go to therapy? It had too many issues!',
+ "Why don't programmers like nature? It has too many bugs!",
+}
+
+return M
diff --git a/common/nvim/lua/plugins/modify-blend.lua b/common/nvim/lua/plugins/modify-blend.lua
new file mode 100755
index 0000000..1b2c6d5
--- /dev/null
+++ b/common/nvim/lua/plugins/modify-blend.lua
@@ -0,0 +1,43 @@
+local ui = vim.api.nvim_list_uis()[1]
+
+local bufnr = vim.api.nvim_create_buf(true, true)
+local win = vim.api.nvim_open_win(bufnr, true, {
+ relative = "editor",
+ --relative = "cursor",
+ width = ui.width,
+ height = ui.height,
+ anchor = "NE",
+ row = 10,
+ col = 10,
+ style = "minimal",
+ zindex = 50,
+})
+
+vim.api.nvim_win_set_option(win, "winblend", 1)
+
+local blend_start = 15
+local offset = 1
+
+CANCEL = false
+local timer = vim.loop.new_timer()
+timer:start(
+ 0,
+ 50,
+ vim.schedule_wrap(function()
+ blend_start = blend_start + offset
+
+ if blend_start > 90 then
+ offset = -1
+ elseif blend_start < 10 then
+ offset = 1
+ end
+
+ if CANCEL or not vim.api.nvim_win_is_valid(win) then
+ timer:close()
+ timer:stop()
+ return
+ end
+
+ vim.cmd([[highlight NormalFloat blend=]] .. tostring(blend_start))
+ end)
+)
diff --git a/common/nvim/lua/plugins/navic.lua b/common/nvim/lua/plugins/navic.lua
new file mode 100755
index 0000000..a574d5c
--- /dev/null
+++ b/common/nvim/lua/plugins/navic.lua
@@ -0,0 +1,51 @@
+local M = {}
+
+function M.setup()
+ local ok, navic = pcall(require, "nvim-navic")
+ if not ok or not navic then
+ return false
+ end
+
+ navic.setup({
+ icons = {
+ File = " ",
+ Module = " ",
+ Namespace = " ",
+ Package = " ",
+ Class = " ",
+ Method = " ",
+ Property = " ",
+ Field = " ",
+ Constructor = " ",
+ Enum = "練",
+ Interface = "練",
+ Function = " ",
+ Variable = " ",
+ Constant = " ",
+ String = " ",
+ Number = " ",
+ Boolean = "◩ ",
+ Array = " ",
+ Object = " ",
+ Key = " ",
+ Null = "ﳠ ",
+ EnumMember = " ",
+ Struct = " ",
+ Event = " ",
+ Operator = " ",
+ TypeParameter = " "
+ },
+ highlight = false,
+ separator = " > ",
+ depth_limit = 0,
+ depth_limit_indicator = "..",
+ safe_output = true,
+ lsp = {
+ auto_attach = true
+ }
+ })
+
+ return true
+end
+
+return M
diff --git a/common/nvim/lua/plugins/neodev.lua b/common/nvim/lua/plugins/neodev.lua
new file mode 100755
index 0000000..07843e1
--- /dev/null
+++ b/common/nvim/lua/plugins/neodev.lua
@@ -0,0 +1,45 @@
+local M = {}
+
+--- Setup and configure neodev
+-- This function initializes neodev with configurations for better Lua development experience
+-- @return boolean True if setup was successful, false otherwise
+function M.setup()
+ local ok, neodev = pcall(require, 'neodev')
+ if not ok then
+ return false
+ end
+
+ neodev.setup({
+ --library = { plugins = { "nvim-dap-ui" }, types = true },
+ --library = { plugins = { "neotest" }, types = true },
+ library = {
+ enabled = true, -- when not enabled, neodev will not change any settings to the LSP server
+ -- these settings will be used for your Neovim config directory
+ runtime = true, -- runtime path
+ types = true, -- full signature, docs and completion of vim.api, vim.treesitter, vim.lsp and others
+ --plugins = { "neotest" },
+ --{ "nvim-dap-ui" },
+ --plugins = true, -- installed opt or start plugins in packpath
+ -- you can also specify the list of plugins to make available as a workspace library
+ -- plugins = { "nvim-treesitter", "plenary.nvim", "telescope.nvim" },
+ plugins = { "nvim-treesitter", "plenary.nvim", "telescope.nvim", "neotest", "nvim-dap-ui" },
+ },
+ setup_jsonls = true, -- configures jsonls to provide completion for project specific .luarc.json files
+ -- for your Neovim config directory, the config.library settings will be used as is
+ -- for plugin directories (root_dirs having a /lua directory), config.library.plugins will be disabled
+ -- for any other directory, config.library.enabled will be set to false
+ override = function(root_dir, options)
+ end,
+ -- With lspconfig, Neodev will automatically setup your lua-language-server
+ -- If you disable this, then you have to set {before_init=require("neodev.lsp").before_init}
+ -- in your lsp start options
+ lspconfig = true,
+ -- much faster, but needs a recent built of lua-language-server
+ -- needs lua-language-server >= 3.6.0
+ pathStrict = true,
+ })
+
+ return true
+end
+
+return M
diff --git a/common/nvim/lua/plugins/neoscroll.lua b/common/nvim/lua/plugins/neoscroll.lua
new file mode 100755
index 0000000..f2ecb04
--- /dev/null
+++ b/common/nvim/lua/plugins/neoscroll.lua
@@ -0,0 +1,22 @@
+local M = {}
+
+function M.setup()
+ local ok, neoscroll = pcall(require, 'neoscroll')
+ if not ok then
+ return false
+ end
+
+ -- Basic configuration
+ neoscroll.setup({
+ mappings = {'<C-u>', '<C-d>', '<C-b>', '<C-f>', '<C-y>', '<C-e>', 'zt', 'zz', 'zb'},
+ hide_cursor = true,
+ stop_eof = true,
+ respect_scrolloff = false,
+ cursor_scrolls_alone = true,
+ easing_function = 'quadratic',
+ })
+
+ return true
+end
+
+return M
diff --git a/common/nvim/lua/plugins/neotest.lua b/common/nvim/lua/plugins/neotest.lua
new file mode 100755
index 0000000..1034d33
--- /dev/null
+++ b/common/nvim/lua/plugins/neotest.lua
@@ -0,0 +1,38 @@
+local M = {}
+
+function M.setup()
+ local ok, neotest = pcall(require, "neotest")
+ if not ok or not neotest then
+ return false
+ end
+
+ -- Safely require adapters
+ local python_ok, python_adapter = pcall(require, "neotest-python")
+ local plenary_ok, plenary_adapter = pcall(require, "neotest-plenary")
+ local vim_test_ok, vim_test_adapter = pcall(require, "neotest-vim-test")
+
+ local adapters = {}
+ if python_ok and python_adapter then
+ table.insert(adapters, python_adapter({
+ dap = { justMyCode = false },
+ }))
+ end
+
+ if plenary_ok and plenary_adapter then
+ table.insert(adapters, plenary_adapter)
+ end
+
+ if vim_test_ok and vim_test_adapter then
+ table.insert(adapters, vim_test_adapter({
+ ignore_file_types = { "python", "vim", "lua" },
+ }))
+ end
+
+ neotest.setup({
+ adapters = adapters,
+ })
+
+ return true
+end
+
+return M
diff --git a/common/nvim/lua/plugins/notify.lua b/common/nvim/lua/plugins/notify.lua
new file mode 100755
index 0000000..62a8f47
--- /dev/null
+++ b/common/nvim/lua/plugins/notify.lua
@@ -0,0 +1,36 @@
+local M = {}
+
+function M.setup()
+ local ok, notify = pcall(require, 'notify')
+ if not ok or not notify then
+ return false
+ end
+
+ notify.setup({
+ background_colour = '#000000',
+ icons = {
+ ERROR = '',
+ WARN = '',
+ INFO = '',
+ DEBUG = '',
+ TRACE = '✎',
+ }
+ })
+
+ -- Set highlight groups safely
+ local function set_hl(group, link)
+ vim.cmd(('hi default link %s %s'):format(group, link))
+ end
+
+ set_hl('NotifyERRORBody', 'Normal')
+ set_hl('NotifyWARNBody', 'Normal')
+ set_hl('NotifyINFOBody', 'Normal')
+ set_hl('NotifyDEBUGBody', 'Normal')
+ set_hl('NotifyTRACEBody', 'Normal')
+ set_hl('NotifyLogTime', 'Comment')
+ set_hl('NotifyLogTitle', 'Special')
+
+ return true
+end
+
+return M
diff --git a/common/nvim/lua/plugins/nvim-tree.lua b/common/nvim/lua/plugins/nvim-tree.lua
new file mode 100755
index 0000000..a212eab
--- /dev/null
+++ b/common/nvim/lua/plugins/nvim-tree.lua
@@ -0,0 +1,479 @@
+local M = {}
+
+-- Safe require helper
+local function safe_require(name)
+ local ok, mod = pcall(require, name)
+ return ok and mod or nil
+end
+
+
+local ok, api = pcall(require, 'nvim-tree.api')
+if not ok then return end
+local function on_attach(bufnr)
+ local function opts(desc)
+ return { desc = "nvim-tree: " .. desc, buffer = bufnr, noremap = true, silent = true, nowait = true }
+ end
+
+ local mappings = {
+ ["<C-]>"] = { api.tree.change_root_to_node, "CD" },
+ ["<C-e>"] = { api.node.open.replace_tree_buffer, "Open: In Place" },
+ ["<C-k>"] = { api.node.show_info_popup, "Info" },
+ ["<C-r>"] = { api.fs.rename_sub, "Rename: Omit Filename" },
+ ["<C-t>"] = { api.node.open.tab, "Open: New Tab" },
+ ["<C-v>"] = { api.node.open.vertical, "Open: Vertical Split" },
+ ["<C-x>"] = { api.node.open.horizontal, "Open: Horizontal Split" },
+ ["<BS>"] = { api.node.navigate.parent_close, "Close Directory" },
+ -- ["<CR>"] = { api.node.open.edit, "Open" },
+ ["<Tab>"] = { api.node.open.preview, "Open Preview" },
+ [">"] = { api.node.navigate.sibling.next, "Next Sibling" },
+ ["<"] = { api.node.navigate.sibling.prev, "Previous Sibling" },
+ ["."] = { api.node.run.cmd, "Run Command" },
+ ["-"] = { api.tree.change_root_to_parent, "Up" },
+ ["a"] = { api.fs.create, "Create" },
+ ["bmv"] = { api.marks.bulk.move, "Move Bookmarked" },
+ ["B"] = { api.tree.toggle_no_buffer_filter, "Toggle No Buffer" },
+ ["c"] = { api.fs.copy.node, "Copy" },
+ -- ["C"] = { api.tree.toggle_git_clean_filter, "Toggle Git Clean" },
+ ["[c"] = { api.node.navigate.git.prev, "Prev Git" },
+ ["]c"] = { api.node.navigate.git.next, "Next Git" },
+ ["d"] = { api.fs.remove, "Delete" },
+ ["D"] = { api.fs.trash, "Trash" },
+ ["E"] = { api.tree.expand_all, "Expand All" },
+ ["e"] = { api.fs.rename_basename, "Rename: Basename" },
+ ["]e"] = { api.node.navigate.diagnostics.next, "Next Diagnostic" },
+ ["[e"] = { api.node.navigate.diagnostics.prev, "Prev Diagnostic" },
+ ["F"] = { api.live_filter.clear, "Clean Filter" },
+ ["f"] = { api.live_filter.start, "Filter" },
+ ["g?"] = { api.tree.toggle_help, "Help" },
+ ["gy"] = { api.fs.copy.absolute_path, "Copy Absolute Path" },
+ ["H"] = { api.tree.toggle_hidden_filter, "Toggle Dotfiles" },
+ ["I"] = { api.tree.toggle_gitignore_filter, "Toggle Git Ignore" },
+ ["J"] = { api.node.navigate.sibling.last, "Last Sibling" },
+ ["K"] = { api.node.navigate.sibling.first, "First Sibling" },
+ ["m"] = { api.marks.toggle, "Toggle Bookmark" },
+ -- ["o"] = { api.node.open.edit, "Open" },
+ ["O"] = { api.node.open.no_window_picker, "Open: No Window Picker" },
+ ["p"] = { api.fs.paste, "Paste" },
+ ["P"] = { api.node.navigate.parent, "Parent Directory" },
+ ["q"] = { api.tree.close, "Close" },
+ ["r"] = { api.fs.rename, "Rename" },
+ ["R"] = { api.tree.reload, "Refresh" },
+ ["s"] = { api.node.run.system, "Run System" },
+ ["S"] = { api.tree.search_node, "Search" },
+ ["U"] = { api.tree.toggle_custom_filter, "Toggle Hidden" },
+ ["W"] = { api.tree.collapse_all, "Collapse" },
+ ["x"] = { api.fs.cut, "Cut" },
+ ["y"] = { api.fs.copy.filename, "Copy Name" },
+ ["Y"] = { api.fs.copy.relative_path, "Copy Relative Path" },
+ ["<2-LeftMouse>"] = { api.node.open.edit, "Open" },
+ ["<2-RightMouse>"] = { api.tree.change_root_to_node, "CD" },
+
+ -- Mappings migrated from view.mappings.list
+ ["l"] = { api.node.open.edit, "Open" },
+ ["<CR>"] = { api.node.open.edit, "Open" },
+ ["o"] = { api.node.open.edit, "Open" },
+ ["h"] = { api.node.navigate.parent_close, "Close Directory" },
+ ["v"] = { api.node.open.vertical, "Open: Vertical Split" },
+ ["C"] = { api.tree.change_root_to_node, "CD" },
+ }
+ for keys, mapping in pairs(mappings) do
+ vim.keymap.set("n", keys, mapping[1], opts(mapping[2]))
+ end
+end
+
+---- Icons configuration for nvim-tree
+--local icons = {
+-- webdev_colors = true,
+-- git_placement = "before",
+-- modified_placement = "after",
+-- padding = " ",
+-- symlink_arrow = " ➛ ",
+-- show = {
+-- file = true,
+-- folder = true,
+-- folder_arrow = true,
+-- git = true,
+-- modified = true,
+-- },
+-- glyphs = {
+-- default = "",
+-- symlink = "",
+-- bookmark = "",
+-- modified = "●",
+-- folder = {
+-- arrow_closed = "",
+-- arrow_open = "",
+-- default = "",
+-- open = "",
+-- empty = "",
+-- empty_open = "",
+-- symlink = "",
+-- symlink_open = "",
+-- },
+-- git = {
+-- unstaged = "✗",
+-- staged = "✓",
+-- unmerged = "",
+-- renamed = "➜",
+-- untracked = "★",
+-- deleted = "",
+-- ignored = "◌",
+-- },
+-- },
+--}
+
+local icons = {
+ webdev_colors = true,
+ git_placement = "signcolumn",
+ modified_placement = "after",
+ padding = " ",
+ show = {
+ file = true,
+ folder = true,
+ folder_arrow = true,
+ git = true,
+ modified = true,
+ },
+
+ glyphs = {
+ default = "󰈔",
+ symlink = "",
+ folder = {
+ arrow_open = "",
+ arrow_closed = "",
+ default = " ",
+ open = " ",
+ empty = " ",
+ empty_open = " ",
+ symlink = "",
+ symlink_open = "",
+ },
+
+ git = {
+ deleted = "",
+ unmerged = "",
+ untracked = "",
+ unstaged = "",
+ staged = "",
+ renamed = "➜",
+ ignored = "◌",
+ },
+ },
+ web_devicons = {
+ folder = {
+ enable = true,
+ color = true,
+ },
+ },
+}
+
+local float = {
+ enable = false,
+ open_win_config = function()
+ local screen_w = vim.o.columns
+ local screen_h = vim.o.lines - vim.o.cmdheight
+ local window_w = screen_w * WIDTH_RATIO
+ local window_h = screen_h * HEIGHT_RATIO
+ local window_w_int = math.floor(window_w)
+ local window_h_int = math.floor(window_h)
+ local center_x = (screen_w - window_w) / 2
+ local center_y = ((vim.o.lines - window_h) / 2) - vim.o.cmdheight
+ return {
+ border = "rounded",
+ relative = "editor",
+ row = center_y,
+ col = center_x,
+ width = window_w_int,
+ height = window_h_int,
+ }
+ end,
+}
+
+local renderer = {
+ group_empty = true, -- default: true. Compact folders that only contain a single folder into one node in the file tree.
+ highlight_git = false,
+ full_name = false,
+ highlight_opened_files = "icon", -- "none" (default), "icon", "name" or "all"
+ highlight_modified = "icon", -- "none", "name" or "all". Nice and subtle, override the open icon
+ root_folder_label = ":~:s?$?/..?",
+ indent_width = 2,
+ indent_markers = {
+ enable = true,
+ inline_arrows = true,
+ icons = {
+ corner = "└",
+ edge = "│",
+ item = "│",
+ bottom = "─",
+ none = " ",
+ },
+ },
+ icons = icons,
+}
+
+local system_open = { cmd = "zathura" }
+
+local HEIGHT_RATIO = 0.8
+local WIDTH_RATIO = 0.15
+local view = {
+ cursorline = true,
+ float = float,
+ --signcolumn = 'no',
+ --width = function()
+ -- return math.floor(vim.opt.columns:get() * WIDTH_RATIO)
+ --end,
+ width = { max = 38, min = 38 },
+ side = "left",
+}
+
+-- Open nvim-tree when opening a directory
+local function open_nvim_tree(data)
+ -- buffer is a directory
+ local directory = vim.fn.isdirectory(data.file) == 1
+
+ if not directory then
+ return
+ end
+
+ -- change to the directory
+ vim.cmd.cd(data.file)
+
+ -- open the tree
+ require("nvim-tree.api").tree.open()
+end
+
+
+-- Setup function
+function M.setup()
+ -- Check if nvim-tree is installed
+ --local nvim_tree = safe_require('nvim-tree')
+ --if not nvim_tree then
+ -- return false
+ --end
+
+ local nvim_tree = safe_require('nvim-tree')
+ if type(nvim_tree) ~= "table" or not nvim_tree.setup then
+ --vim.notify("[nvim-tree] Plugin did not load correctly", vim.log.levels.ERROR)
+ return false
+ end
+
+ -- Setup nvim-tree
+ nvim_tree.setup({
+ sync_root_with_cwd = true,
+ respect_buf_cwd = true,
+ disable_netrw = true,
+ hijack_netrw = true,
+ open_on_tab = false,
+ hijack_cursor = false,
+ update_cwd = true,
+ hijack_directories = {
+ enable = true,
+ auto_open = true,
+ },
+ diagnostics = {
+ enable = true,
+ icons = {
+ error = "✘",
+ warning = "",
+ hint = "◉",
+ info = "",
+ },
+ },
+ filesystem_watchers = {
+ enable = true,
+ debounce_delay = 50,
+ ignore_dirs = { "node_modules", ".config/nvm" },
+ },
+ update_focused_file = {
+ enable = true,
+ update_cwd = true,
+ --update_root = true,
+ ignore_list = {},
+ },
+ --root_dirs = {},
+ --system_open = {
+ -- --cmd = nil,
+ -- --args = {},
+ --},
+ system_open = system_open,
+ filters = {
+ dotfiles = false,
+ custom = {},
+ },
+ --git = {
+ -- enable = true,
+ -- ignore = true,
+ -- timeout = 500,
+ --},
+ git = { ignore = false },
+ view = view,
+ renderer = renderer,
+ --renderer = {
+ -- indent_markers = {
+ -- enable = false,
+ -- icons = {
+ -- corner = "└ ",
+ -- edge = "│ ",
+ -- none = " ",
+ -- },
+ -- },
+ -- icons = icons,
+ --},
+ on_attach = on_attach,
+ notify = {
+ threshold = vim.log.levels.ERROR,
+ },
+ log = {
+ enable = true,
+ truncate = true,
+ types = {
+ diagnostics = true,
+ git = true,
+ profile = true,
+ watcher = true,
+ },
+ },
+ trash = {
+ cmd = "gio trash",
+ require_confirm = true,
+ },
+ modified = {
+ enable = true,
+ show_on_dirs = true,
+ show_on_open_dirs = true,
+ },
+ actions = {
+ use_system_clipboard = true,
+ change_dir = {
+ enable = true,
+ global = false,
+ restrict_above_cwd = false,
+ },
+ remove_file = {
+ close_window = true,
+ },
+ open_file = {
+ quit_on_open = false,
+ resize_window = true,
+ window_picker = {
+ enable = true,
+ chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890",
+ exclude = {
+ filetype = { "notify", "packer", "qf", "diff", "fugitive", "fugitiveblame" },
+ buftype = { "nofile", "terminal", "help" },
+ },
+ },
+ },
+ },
+ })
+
+
+ local api = require("nvim-tree.api")
+ --local event = api.events.Event
+ --api.events.subscribe(event.TreeOpen, function(_)
+ -- vim.cmd([[setlocal statuscolumn=\ ]])
+ -- vim.cmd([[setlocal cursorlineopt=number]])
+ -- vim.cmd([[setlocal fillchars+=vert:🮇]])
+ -- vim.cmd([[setlocal fillchars+=horizup:🮇]])
+ -- vim.cmd([[setlocal fillchars+=vertright:🮇]])
+ --end)
+
+ local function open_nvim_tree(data)
+ vim.cmd.cd(data.file:match("(.+)/[^/]*$"))
+ local directory = vim.fn.isdirectory(data.file) == 1
+ if not directory then
+ return
+ end
+ require("nvim-tree.api").tree.open()
+ end
+
+ -- Auto open nvim-tree when opening a directory
+ vim.api.nvim_create_autocmd({ "VimEnter" }, { pattern = { "*" }, callback = open_nvim_tree })
+
+ -- Change Root To Global Current Working Directory
+ local function change_root_to_global_cwd()
+ local api = require("nvim-tree.api")
+ local global_cwd = vim.fn.getcwd(-1, -1)
+ api.tree.change_root(global_cwd)
+ end
+
+ local function copy_file_to(node)
+ local file_src = node["absolute_path"]
+ -- The args of input are {prompt}, {default}, {completion}
+ -- Read in the new file path using the existing file's path as the baseline.
+ local file_out = vim.fn.input("COPY TO: ", file_src, "file")
+ -- Create any parent dirs as required
+ local dir = vim.fn.fnamemodify(file_out, ":h")
+ vim.fn.system({ "mkdir", "-p", dir })
+ -- Copy the file
+ vim.fn.system({ "cp", "-R", file_src, file_out })
+ end
+
+ local function edit_and_close(node)
+ api.node.open.edit(node, {})
+ api.tree.close()
+ end
+
+ --vim.api.nvim_create_augroup('NvimTreeRefresh', {})
+ --vim.api.nvim_create_autocmd('BufEnter', {
+ -- pattern = 'NvimTree_1',
+ -- command = 'NvimTreeRefresh',
+ -- group = 'NvimTreeRefresh',
+ --})
+
+ vim.api.nvim_create_autocmd({ "CursorHold" }, {
+ pattern = "NvimTree*",
+ callback = function()
+ local def = vim.api.nvim_get_hl_by_name("Cursor", true)
+ vim.api.nvim_set_hl(
+ 0,
+ "Cursor",
+ vim.tbl_extend("force", def, {
+ blend = 100,
+ })
+ )
+ vim.opt.guicursor = "n-v-c-sm:block,i-ci-ve:ver25,r-cr-o:hor20,a:Cursor/lCursor"
+ end,
+ })
+
+ vim.api.nvim_create_autocmd({ "BufLeave", "WinClosed", "WinLeave" }, {
+ pattern = "NvimTree*",
+ callback = function()
+ local def = vim.api.nvim_get_hl_by_name("Cursor", true)
+ vim.api.nvim_set_hl(
+ 0,
+ "Cursor",
+ vim.tbl_extend("force", def, {
+ blend = 0,
+ })
+ )
+ vim.opt.guicursor = "n-v-c-sm:block,i-ci-ve:ver25,r-cr-o:hor20"
+ end,
+ })
+
+ vim.api.nvim_command("highlight NvimTreeNormal guibg=NONE ctermbg=NONE")
+ vim.api.nvim_command("highlight NvimTreeNormalNC guibg=NONE ctermbg=NONE guifg=NONE")
+ vim.api.nvim_command("highlight NvimTreeNormalFloat guibg=NONE ctermbg=NONE")
+ vim.api.nvim_command("highlight NvimTreeEndOfBuffer guibg=NONE ctermbg=NONE") --(NonText)
+ vim.api.nvim_command("highlight NvimTreeCursorLine guibg=#50fa7b guifg=#000000")
+ vim.api.nvim_command("highlight NvimTreeSymlinkFolderName guifg=#f8f8f2 guibg=NONE ctermbg=NONE")
+ vim.api.nvim_command("highlight NvimTreeFolderName guifg=#f8f8f2 guibg=NONE ctermbg=NONE")
+ vim.api.nvim_command("highlight NvimTreeRootFolder guifg=#f8f8f2 guibg=NONE ctermbg=NONE")
+ vim.api.nvim_command("highlight NvimTreeEmptyFolderName guifg=#f8f8f2 guibg=NONE ctermbg=NONE") --(Directory)
+ vim.api.nvim_command("highlight NvimTreeOpenedFolderName guifg=#f8f8f2 guibg=NONE ctermbg=NONE") --(Directory)
+ vim.api.nvim_command("highlight NvimTreeOpenedFile guifg=#50fa7b guibg=NONE ctermbg=NONE")
+ vim.api.nvim_command("highlight NvimTreeExecFile guifg=#ff882a guibg=none gui=NONE")
+
+ return true
+end
+
+---- Set highlights
+--vim.cmd([[highlight NvimTreeNormal guibg=NONE ctermbg=NONE]])
+--vim.cmd([[highlight NvimTreeNormalNC guibg=NONE ctermbg=NONE guifg=NONE]])
+--vim.cmd([[highlight NvimTreeNormalFloat guibg=NONE ctermbg=NONE]])
+--vim.cmd([[highlight NvimTreeEndOfBuffer guibg=NONE ctermbg=NONE]])
+--vim.cmd([[highlight NvimTreeCursorLine guibg=#50fa7b guifg=#000000]])
+
+-- Highlight Groups
+
+return M
diff --git a/common/nvim/lua/plugins/overseer.lua b/common/nvim/lua/plugins/overseer.lua
new file mode 100755
index 0000000..593d094
--- /dev/null
+++ b/common/nvim/lua/plugins/overseer.lua
@@ -0,0 +1,14 @@
+local M = {}
+
+function M.setup()
+ local ok, overseer = pcall(require, 'overseer')
+ if not ok or not overseer then
+ return false
+ end
+
+ overseer.setup({})
+
+ return true
+end
+
+return M
diff --git a/common/nvim/lua/plugins/plenary.lua b/common/nvim/lua/plugins/plenary.lua
new file mode 100755
index 0000000..f572244
--- /dev/null
+++ b/common/nvim/lua/plugins/plenary.lua
@@ -0,0 +1,3 @@
+-- plenary.nvim plugin config (modular, robust)
+local ok, _ = pcall(require, 'plenary')
+-- No config needed \ No newline at end of file
diff --git a/common/nvim/lua/plugins/prettier.lua b/common/nvim/lua/plugins/prettier.lua
new file mode 100755
index 0000000..ca57ea9
--- /dev/null
+++ b/common/nvim/lua/plugins/prettier.lua
@@ -0,0 +1,8 @@
+local M = {}
+
+function M.setup()
+ -- No-op if prettier is not installed
+ return true
+end
+
+return M
diff --git a/common/nvim/lua/plugins/quickfix.lua b/common/nvim/lua/plugins/quickfix.lua
new file mode 100755
index 0000000..4a76da0
--- /dev/null
+++ b/common/nvim/lua/plugins/quickfix.lua
@@ -0,0 +1,15 @@
+local M = {}
+
+M.close = function()
+ vim.cmd.cclose()
+end
+
+M.open = function()
+ if vim.tbl_count(vim.fn.getqflist()) == 0 then
+ vim.notify('Nothing in quickfix list; not opening.', vim.log.levels.WARN)
+ else
+ vim.cmd.copen()
+ end
+end
+
+return M
diff --git a/common/nvim/lua/plugins/snippets.lua b/common/nvim/lua/plugins/snippets.lua
new file mode 100755
index 0000000..989ad8a
--- /dev/null
+++ b/common/nvim/lua/plugins/snippets.lua
@@ -0,0 +1,33 @@
+local M = {}
+
+function M.setup()
+ local ok, ls = pcall(require, "luasnip")
+ if not ok or not ls then
+ return false
+ end
+
+ -- Safely load snippets
+ pcall(function() require("luasnip.loaders.from_lua").load({ paths = "~/.config/nvim/snippets/" }) end)
+ pcall(function() require("luasnip.loaders.from_vscode").lazy_load() end)
+ pcall(function() require("luasnip.loaders.from_snipmate").lazy_load() end)
+
+ ls.config.set_config {
+ history = true,
+ updateevents = "TextChanged,TextChangedI",
+ enable_autosnippets = true,
+ region_check_events = "InsertEnter",
+ delete_check_events = "TextChanged",
+ store_selection_keys = "<Tab>",
+ ext_opts = {
+ [require("luasnip.util.types").choiceNode] = {
+ active = {
+ virt_text = { { "«", "GruvboxOrange" } },
+ },
+ },
+ },
+ }
+
+ return true
+end
+
+return M
diff --git a/common/nvim/lua/plugins/sniprun.lua b/common/nvim/lua/plugins/sniprun.lua
new file mode 100755
index 0000000..418e8cc
--- /dev/null
+++ b/common/nvim/lua/plugins/sniprun.lua
@@ -0,0 +1,57 @@
+local status_ok, sniprun = pcall(require, 'sniprun')
+if not status_ok then
+ return
+end
+
+sniprun.setup({
+ -- selected_interpreters = {}, --# use those instead of the default for the current filetype
+ -- repl_enable = { "Python3_original" }, --# enable REPL-like behavior for the given interpreters
+ -- repl_disable = {}, --# disable REPL-like behavior for the given interpreters
+
+ -- interpreter_options = { --# intepreter-specific options, see docs / :SnipInfo <name>
+ -- GFM_original = {
+ -- use_on_filetypes = { "markdown.pandoc" }, --# the 'use_on_filetypes' configuration key is
+ -- --# available for every interpreter
+ -- },
+ -- },
+
+ --# you can combo different display modes as desired
+ display = {
+ -- "Classic", --# display results in the command-line area
+ --'VirtualTextOk', --# display ok results as virtual text (multiline is shortened)
+ -- "VirtualTextErr", --# display error results as virtual text
+ -- "TempFloatingWindow", --# display results in a floating window
+ -- "LongTempFloatingWindow", --# same as above, but only long results. To use with VirtualText__
+ 'Terminal', --# display results in a vertical split
+ -- "TerminalWithCode", --# display results and code history in a vertical split
+ -- "NvimNotify", --# display with the nvim-notify plugin
+ -- "Api" --# return output to a programming interface
+ },
+
+ display_options = {
+ terminal_width = 45, --# change the terminal display option width
+ notification_timeout = 5, --# timeout for nvim_notify output
+ },
+
+ --# You can use the same keys to customize whether a sniprun producing
+ --# no output should display nothing or '(no output)'
+ show_no_output = {
+ 'Classic',
+ 'TempFloatingWindow', --# implies LongTempFloatingWindow, which has no effect on its own
+ },
+
+ --# customize highlight groups (setting this overrides colorscheme)
+ -- snipruncolors = {
+ -- SniprunVirtualTextOk = { bg = "NONE", fg = "#66eeff", ctermbg = "Black", cterfg = "Cyan" },
+ -- SniprunFloatingWinOk = { fg = "NONE", ctermfg = "Cyan" },
+ -- SniprunVirtualTextErr = { bg = "#881515", fg = "#000000", ctermbg = "DarkRed", cterfg = "Black" },
+ -- SniprunFloatingWinErr = { fg = "#881515", ctermfg = "DarkRed" },
+ -- },
+
+ --# miscellaneous compatibility/adjustement settings
+ inline_messages = 0, --# inline_message (0/1) is a one-line way to display messages
+ --# to workaround sniprun not being able to display anything
+
+ borders = 'single', --# display borders around floating windows
+ --# possible values are 'none', 'single', 'double', or 'shadow'
+})
diff --git a/common/nvim/lua/plugins/statuscol.lua b/common/nvim/lua/plugins/statuscol.lua
new file mode 100755
index 0000000..c538790
--- /dev/null
+++ b/common/nvim/lua/plugins/statuscol.lua
@@ -0,0 +1,37 @@
+local M = {}
+
+function M.setup()
+ local ok, statuscol = pcall(require, "statuscol")
+ if not ok or not statuscol then
+ return false
+ end
+
+ local builtin_ok, builtin = pcall(require, "statuscol.builtin")
+ if not builtin_ok or not builtin then
+ return false
+ end
+
+ statuscol.setup({
+ segments = {
+ { text = { builtin.lnumfunc }, click = "v:lua.ScLa" },
+ { text = { "%s" }, click = "v:lua.ScSa" },
+ { text = { builtin.foldfunc }, click = "v:lua.ScFa" },
+ },
+ ft_ignore = {
+ "NvimTree",
+ "packer",
+ "NeogitStatus",
+ "toggleterm",
+ "dapui_scopes",
+ "dapui_breakpoints",
+ "dapui_stacks",
+ "dapui_watches",
+ "dapui_console",
+ "dapui_repl",
+ },
+ })
+
+ return true
+end
+
+return M
diff --git a/common/nvim/lua/plugins/surround.lua b/common/nvim/lua/plugins/surround.lua
new file mode 100755
index 0000000..71023c7
--- /dev/null
+++ b/common/nvim/lua/plugins/surround.lua
@@ -0,0 +1,35 @@
+local M = {}
+
+function M.setup()
+ local ok, surround = pcall(require, 'nvim-surround')
+ if not ok or not surround then
+ return false
+ end
+
+ surround.setup({
+ keymaps = {
+ insert = false,
+ insert_line = false,
+ normal = false,
+ normal_cur = false,
+ normal_line = false,
+ normal_cur_line = false,
+ visual = "<S-s>",
+ visual_line = false,
+ delete = false,
+ change = false,
+ },
+ aliases = {
+ ["a"] = false,
+ ["b"] = false,
+ ["B"] = false,
+ ["r"] = false,
+ ["q"] = false,
+ ["s"] = false,
+ },
+ })
+
+ return true
+end
+
+return M
diff --git a/common/nvim/lua/plugins/telescope.lua b/common/nvim/lua/plugins/telescope.lua
new file mode 100755
index 0000000..5aca8ac
--- /dev/null
+++ b/common/nvim/lua/plugins/telescope.lua
@@ -0,0 +1,740 @@
+local M = {}
+
+-- Safely require a module
+-- @param name string The module name to require
+-- @return table|nil The loaded module or nil if failed
+local function safe_require(name)
+ local ok, mod = pcall(require, name)
+ return ok and mod or nil
+end
+
+--- Setup and configure Telescope
+-- This function initializes Telescope with default configurations and extensions
+-- @return boolean True if setup was successful, false otherwise
+function M.setup()
+ -- Check if Telescope is installed
+ local telescope = safe_require("telescope")
+ if not telescope then
+ return false
+ end
+ -- Require Telescope and fail early if missing
+ local telescope = safe_require("telescope")
+ if not telescope then
+ return false
+ end
+
+ local actions = safe_require("telescope.actions")
+ local actions_set = safe_require("telescope.actions.set")
+ local actions_state = safe_require("telescope.actions.state")
+ local finders = safe_require("telescope.finders")
+ local pickers = safe_require("telescope.pickers")
+ local config_mod = safe_require("telescope.config")
+ local utils = safe_require("telescope.utils")
+ local previewers = require("telescope.previewers")
+
+ local config = config_mod and config_mod.values or {}
+
+ -- 🛡 Safe previewer to avoid nil path error
+ local safe_previewer = function()
+ return require("telescope.previewers").new_buffer_previewer({
+ define_preview = function(self, entry)
+ if not entry or type(entry) ~= "table" then return end
+
+ local path = entry.path or entry.filename or entry.value
+ if type(path) ~= "string" or path == "" then return end
+
+ -- Avoid expanding things like " Recent Books" which aren't valid files
+ if path:match("^%s") then return end
+
+ -- Resolve tilde if present
+ path = path:gsub("^~", vim.env.HOME)
+
+ if vim.fn.filereadable(path) ~= 1 and vim.fn.isdirectory(path) ~= 1 then
+ return
+ end
+
+ -- Protect against nil path being passed further
+ if not self.state or not self.state.bufnr or not self.state.bufname then return end
+
+ local preview_utils = require("telescope.previewers.utils")
+ preview_utils.buffer_previewer_maker(path, self.state.bufnr, {
+ bufname = self.state.bufname,
+ callback = function(bufnr, success)
+ if not success then
+ vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, { "Failed to preview file." })
+ end
+ end,
+ })
+ end,
+ })
+ end
+
+ local function get_extension_actions(ext)
+ local ok, telescope_ext = pcall(require, "telescope._extensions." .. ext)
+ if not ok then return {} end
+ return telescope_ext.actions or {}
+ end
+
+ telescope.setup({
+ defaults = {
+ vimgrep_arguments = {
+ "rg",
+ "--color=never",
+ "--no-heading",
+ "--with-filename",
+ "--line-number",
+ "--column",
+ "--smart-case",
+ "--hidden",
+ "--fixed-strings",
+ "--trim",
+ },
+ previewer = safe_previewer(),
+ prompt_prefix = " ",
+ selection_caret = " ",
+ entry_prefix = " ",
+ path_display = { "tail" },
+ file_ignore_patterns = {
+ "packer_compiled.lua",
+ "~/.config/zsh/plugins",
+ "zcompdump",
+ "%.DS_Store",
+ "%.git/",
+ "%.spl",
+ "%[No Name%]",
+ "/$",
+ "node_modules",
+ "%.png",
+ "%.zip",
+ "%.pxd",
+ "^.local/",
+ "^.cache/",
+ "^downloads/",
+ "^music/",
+ },
+ mappings = {
+ i = {
+ ["<C-n>"] = actions.cycle_history_next,
+ ["<C-p>"] = actions.cycle_history_prev,
+ ["<C-j>"] = actions.move_selection_next,
+ ["<C-k>"] = actions.move_selection_previous,
+ ["<Esc>"] = actions.close,
+ ["<?>"] = actions.which_key,
+ ["<Down>"] = actions.move_selection_next,
+ ["<Up>"] = actions.move_selection_previous,
+ ["<CR>"] = actions.select_default,
+ ["<C-x>"] = actions.select_horizontal,
+ ["<C-y>"] = actions.select_vertical,
+ ["<C-t>"] = actions.select_tab,
+ ["<C-c>"] = actions.delete_buffer,
+ ["<C-u>"] = actions.preview_scrolling_up,
+ ["<C-d>"] = actions.preview_scrolling_down,
+ ["<PageUp>"] = actions.results_scrolling_up,
+ ["<PageDown>"] = actions.results_scrolling_down,
+ ["<Tab>"] = actions.toggle_selection + actions.move_selection_worse,
+ ["<S-Tab>"] = actions.toggle_selection + actions.move_selection_better,
+ ["<C-q>"] = actions.send_to_qflist + actions.open_qflist,
+ ["<M-q>"] = actions.send_selected_to_qflist + actions.open_qflist,
+ ["<C-l>"] = actions.complete_tag,
+ ["<C-_>"] = actions.which_key,
+ },
+ n = {
+ ["<esc>"] = actions.close,
+ ["<q>"] = actions.close,
+ ["<CR>"] = actions.select_default,
+ ["<C-x>"] = actions.select_horizontal,
+ ["<C-y>"] = actions.select_vertical,
+ ["<C-t>"] = actions.select_tab,
+ ["<C-c>"] = actions.delete_buffer,
+ ["<Tab>"] = actions.toggle_selection + actions.move_selection_worse,
+ ["<S-Tab>"] = actions.toggle_selection + actions.move_selection_better,
+ ["<C-q>"] = actions.send_to_qflist + actions.open_qflist,
+ ["<M-q>"] = actions.send_selected_to_qflist + actions.open_qflist,
+ ["j"] = actions.move_selection_next,
+ ["k"] = actions.move_selection_previous,
+ ["H"] = actions.move_to_top,
+ ["M"] = actions.move_to_middle,
+ ["L"] = actions.move_to_bottom,
+ ["<Down>"] = actions.move_selection_next,
+ ["<Up>"] = actions.move_selection_previous,
+ ["gg"] = actions.move_to_top,
+ ["G"] = actions.move_to_bottom,
+ ["<C-u>"] = actions.preview_scrolling_up,
+ ["<C-d>"] = actions.preview_scrolling_down,
+ ["<PageUp>"] = actions.results_scrolling_up,
+ ["<PageDown>"] = actions.results_scrolling_down,
+ ["cd"] = function(prompt_bufnr)
+ local selection = actions_state.get_selected_entry()
+ local dir = vim.fn.fnamemodify(selection.path, ":p:h")
+ actions.close(prompt_bufnr)
+ vim.cmd("silent lcd " .. dir)
+ end,
+ ["?"] = actions.which_key,
+ },
+ },
+ },
+ preview = {
+ filesize_limit = 3,
+ timeout = 250,
+ },
+ selection_strategy = "reset",
+ sorting_strategy = "ascending",
+ scroll_strategy = "limit",
+ color_devicons = true,
+ layout_strategy = "horizontal",
+ layout_config = {
+ horizontal = {
+ height = 0.95,
+ preview_cutoff = 70,
+ width = 0.92,
+ preview_width = { 0.55, max = 50 },
+ },
+ bottom_pane = {
+ height = 12,
+ preview_cutoff = 70,
+ prompt_position = "bottom",
+ },
+ },
+ find_files = {
+ cwd = vim.fn.getcwd(),
+ prompt_prefix = " ",
+ follow = true,
+ },
+ extensions = {
+ file_browser = {
+ theme = "dropdown",
+ hijack_netrw = false,
+ mappings = {
+ i = {
+ ["<C-w>"] = function() vim.cmd("normal vbd") end,
+ ["<C-h>"] = function()
+ local fb_actions = get_extension_actions("file_browser")
+ if fb_actions.goto_parent_dir then
+ fb_actions.goto_parent_dir()
+ end
+ end,
+ },
+ n = {
+ ["N"] = function()
+ local fb_actions = get_extension_actions("file_browser")
+ if fb_actions.create then
+ fb_actions.create()
+ end
+ end,
+ ["<C-h>"] = function()
+ local fb_actions = get_extension_actions("file_browser")
+ if fb_actions.goto_parent_dir then
+ fb_actions.goto_parent_dir()
+ end
+ end,
+ },
+ },
+ },
+ },
+ })
+
+ -- Load extensions
+ for _, ext in ipairs({
+ "fzf", "ui-select", "file_browser", "changed_files",
+ "media_files", "notify", "dap", "session-lens", "recent_files"
+ }) do
+ pcall(telescope.load_extension, ext)
+ end
+
+ -- Define the custom command findhere/startup
+ vim.cmd('command! Findhere lua require("plugins.telescope").findhere()')
+
+ return true
+end
+
+-- Find config files
+local function _sys_path(repo_path)
+ local home = os.getenv("HOME") or vim.fn.expand("~")
+
+ -- Case 1: Files in the OS-specific home folder (e.g., linux/home/.bashrc)
+ if repo_path:find("/home/", 1, true) then
+ local file = repo_path:match(".*/home/(.*)")
+ return home .. "/" .. file
+ -- Case 2: Files in the common folder (e.g., common/README.md)
+ elseif repo_path:find("common/", 1, true) then
+ local file = repo_path:match("common/(.*)")
+ return home .. "/" .. file
+ -- Case 3: Root-level files (e.g., profile/profile_script or README.md)
+ elseif repo_path:find("profile/", 1, true) or repo_path:find("README.md", 1, true) then
+ return home .. "/" .. repo_path
+ -- Case 4: System-level files (e.g., linux/etc/issue)
+ elseif repo_path:find("/etc/", 1, true) then
+ local file = repo_path:match(".*/etc/(.*)")
+ return "/etc/" .. file
+ -- Return nil for paths that don't match any known pattern
+ else
+ return nil
+ end
+end
+
+function M.find_configs()
+ local telescope_builtin = require("telescope.builtin")
+ local tracked_files = {}
+ local home = os.getenv("HOME") or "~"
+ local original_dir = vim.fn.getcwd()
+ vim.fn.chdir(home)
+
+ vim.api.nvim_create_autocmd("VimLeave", {
+ callback = function()
+ vim.fn.chdir(original_dir)
+ end,
+ })
+
+ -- Check if the bare repository exists
+ if vim.fn.isdirectory(home .. "/.cfg") == 1 then
+ -- Repository exists, use git to find tracked files
+ local handle = io.popen("git --git-dir=" .. home .. "/.cfg --work-tree=" .. home .. " ls-tree --name-only -r HEAD")
+ local cfg_files = ""
+ if handle then
+ cfg_files = handle:read("*a") or ""
+ handle:close()
+ end
+
+ -- Process the list of files
+ for file in string.gmatch(cfg_files, "[^\n]+") do
+ file = vim.trim(file)
+ if file ~= "" then
+ local fullpath = _sys_path(file)
+ if fullpath and (vim.fn.filereadable(fullpath) == 1 or vim.fn.isdirectory(fullpath) == 1) then
+ table.insert(tracked_files, fullpath)
+ end
+ end
+ end
+ end
+
+ -- If no files were found (either no repo or no tracked files), use fallback paths
+ if #tracked_files == 0 then
+ local fallback_dirs = {
+ home .. "/.config/nvim",
+ home .. "/.config/zsh",
+ home .. "/.config/tmux",
+ home .. "/.bashrc",
+ home .. "/.zshrc",
+ home .. "/.tmux.conf",
+ }
+ for _, path in ipairs(fallback_dirs) do
+ if vim.fn.filereadable(path) == 1 or vim.fn.isdirectory(path) == 1 then
+ table.insert(tracked_files, path)
+ end
+ end
+ end
+
+ if #tracked_files == 0 then
+ vim.notify("[find_configs] No configuration files found to search.", vim.log.levels.WARN)
+ return
+ end
+
+ -- Launch Telescope
+ telescope_builtin.find_files({
+ hidden = true,
+ no_ignore = false,
+ prompt_title = " Find Configs",
+ results_title = "Config Files",
+ path_display = { "smart" },
+ search_dirs = tracked_files,
+ layout_strategy = "horizontal",
+ layout_config = { preview_width = 0.65, width = 0.75 },
+ previewer = true,
+ })
+end
+
+function M.find_scripts()
+ require("telescope.builtin").find_files({
+ hidden = true,
+ no_ignore = true,
+ prompt_title = " Find Scripts",
+ path_display = { "smart" },
+ search_dirs = {
+ "~/.scripts",
+ },
+ layout_strategy = "horizontal",
+ layout_config = { preview_width = 0.65, width = 0.75 },
+ })
+end
+
+function M.find_projects()
+ local search_dir = "~/projects"
+ local actions = safe_require("telescope.actions")
+ local actions_set = safe_require("telescope.actions.set")
+ local actions_state = safe_require("telescope.actions.state")
+ local finders = safe_require("telescope.finders")
+ local pickers = safe_require("telescope.pickers")
+ local config_mod = safe_require("telescope.config")
+ local config = config_mod and config_mod.values or {}
+
+ pickers
+ .new({}, {
+ prompt_title = "Find Projects",
+ finder = finders.new_oneshot_job({
+ "find",
+ vim.fn.expand(search_dir),
+ "-type",
+ "d",
+ "-maxdepth",
+ "1",
+ }),
+ previewer = require("telescope.previewers").vim_buffer_cat.new({}),
+ sorter = config.generic_sorter({}),
+ attach_mappings = function(prompt_bufnr, map)
+ actions_set.select:replace(function()
+ local entry = actions_state.get_selected_entry()
+ if entry ~= nil then
+ local dir = entry.value
+ actions.close(prompt_bufnr, false)
+ vim.fn.chdir(dir)
+ vim.cmd("e .")
+ vim.cmd("echon ''")
+ print("cwd: " .. vim.fn.getcwd())
+ end
+ end)
+ return true
+ end,
+ })
+ :find()
+end
+
+function M.grep_notes()
+ local opts = {}
+ opts.hidden = false
+ opts.search_dirs = {
+ "~/documents/main/",
+ }
+ opts.prompt_prefix = " "
+ opts.prompt_title = " Grep Notes"
+ opts.path_display = { "smart" }
+ require("telescope.builtin").live_grep(opts)
+end
+
+function M.find_notes()
+ require("telescope.builtin").find_files({
+ hidden = true,
+ no_ignore = false,
+ prompt_title = " Find Notes",
+ path_display = { "smart" },
+ search_dirs = {
+ "~/documents/main",
+ },
+ layout_strategy = "horizontal",
+ layout_config = { preview_width = 0.65, width = 0.75 },
+ })
+end
+
+function M.find_private()
+ require("telescope.builtin").find_files({
+ hidden = true,
+ no_ignore = false,
+ prompt_title = " Find Notes",
+ path_display = { "smart" },
+ search_dirs = {
+ "~/notes/private",
+ "~/notes",
+ },
+ layout_strategy = "horizontal",
+ layout_config = { preview_width = 0.65, width = 0.75 },
+ })
+end
+
+function M.find_books()
+ local search_dir = "~/documents/books"
+ local actions = safe_require("telescope.actions")
+ local actions_set = safe_require("telescope.actions.set")
+ local actions_state = safe_require("telescope.actions.state")
+ local finders = safe_require("telescope.finders")
+ local pickers = safe_require("telescope.pickers")
+ local config_mod = safe_require("telescope.config")
+ local config = config_mod and config_mod.values or {}
+
+ vim.fn.jobstart("$HOME/.scripts/track-books.sh")
+ local recent_books_directory = vim.fn.stdpath("config") .. "/tmp/"
+ local recent_books_file = recent_books_directory .. "recent_books.txt"
+
+ -- Check if recent_books.txt exists, create it if not
+ if vim.fn.filereadable(recent_books_file) == 0 then
+ vim.fn.mkdir(recent_books_directory, "p") -- Ensure the directory exists
+ vim.fn.writefile({}, recent_books_file) -- Create an empty file
+ end
+
+ local search_cmd = "find " .. vim.fn.expand(search_dir) .. " -type d -o -type f -maxdepth 1"
+
+ local recent_books = vim.fn.readfile(recent_books_file)
+ local search_results = vim.fn.systemlist(search_cmd)
+
+ local results = {}
+
+ -- Section for Recent Books
+ table.insert(results, " Recent Books")
+ for _, recent_book_path in ipairs(recent_books) do
+ local formatted_path = vim.fn.fnameescape(recent_book_path)
+ table.insert(results, formatted_path)
+ end
+
+ -- Section for All Books
+ table.insert(results, " All Books")
+ local directories = {}
+ local files = {}
+
+ for _, search_result in ipairs(search_results) do
+ if vim.fn.isdirectory(search_result) == 1 then
+ table.insert(directories, search_result)
+ else
+ table.insert(files, search_result)
+ end
+ end
+
+ table.sort(directories)
+ table.sort(files)
+
+ for _, dir in ipairs(directories) do
+ table.insert(results, dir)
+ end
+
+ for _, file in ipairs(files) do
+ table.insert(results, file)
+ end
+
+ local picker = pickers.new({}, {
+ prompt_title = "Find Books",
+ finder = finders.new_table({
+ results = results,
+ }),
+ file_ignore_patterns = {
+ "%.git",
+ },
+ previewer = require("telescope.previewers").vim_buffer_cat.new({}),
+ sorter = config.generic_sorter({}),
+ attach_mappings = function(prompt_bufnr, map)
+ actions_set.select:replace(function()
+ local entry = actions_state.get_selected_entry()
+ if entry ~= nil then
+ local path = entry.value
+
+ actions.close(prompt_bufnr, false)
+
+ -- Check if it's under "Recent Books"
+ if path == " Recent Books" or path == " All Books" then
+ vim.notify("Cannot select 'All Books'/'Recent Books', please select a book or directory.",
+ vim.log.levels.WARN, { title = "Find Books" })
+ else
+ -- Determine whether it's a directory or a file
+ local is_directory = vim.fn.isdirectory(path)
+ if is_directory then
+ -- It's a directory, navigate to it in the current buffer
+ vim.cmd("e " .. path)
+ else
+ -- It's a file, open it
+ vim.cmd("e " .. path)
+ end
+ end
+ end
+ end)
+ return true
+ end,
+ })
+
+ picker:find()
+end
+
+function M.grep_current_dir()
+ local buffer_dir = require("telescope.utils").buffer_dir()
+ local opts = {
+ prompt_title = "Live Grep in " .. buffer_dir,
+ cwd = buffer_dir,
+ }
+ require("telescope.builtin").live_grep(opts)
+end
+
+-- Helper functions that depend on telescope availability
+local function get_dropdown_theme()
+ return require("telescope.themes").get_dropdown({
+ hidden = true,
+ no_ignore = true,
+ previewer = false,
+ prompt_title = "",
+ preview_title = "",
+ results_title = "",
+ layout_config = {
+ prompt_position = "top",
+ },
+ })
+end
+
+-- Set current folder as prompt title
+local function with_title(opts, extra)
+ extra = extra or {}
+ local path = opts.cwd or opts.path or extra.cwd or extra.path or nil
+ local title = ""
+ local buf_path = vim.fn.expand("%:p:h")
+ local cwd = vim.fn.getcwd()
+ if path ~= nil and buf_path ~= cwd then
+ title = require("plenary.path"):new(buf_path):make_relative(cwd)
+ else
+ title = vim.fn.fnamemodify(cwd, ":t")
+ end
+
+ return vim.tbl_extend("force", opts, {
+ prompt_title = title,
+ }, extra or {})
+end
+
+-- Find here
+function M.findhere()
+ -- Open file browser if argument is a folder
+ local arg = vim.api.nvim_eval("argv(0)")
+ if arg and (vim.fn.isdirectory(arg) ~= 0 or arg == "") then
+ vim.defer_fn(function()
+ require("telescope.builtin").find_files(with_title(get_dropdown_theme()))
+ end, 10)
+ end
+end
+
+-- Find dirs
+function M.find_dirs()
+ local root_dir = vim.fn.input("Enter the root directory: ")
+
+ -- Check if root_dir is empty
+ if root_dir == "" then
+ print("No directory entered. Aborting.")
+ return
+ end
+
+ local entries = {}
+
+ -- Use vim.fn.expand() to get an absolute path
+ local root_path = vim.fn.expand(root_dir)
+
+ local subentries = vim.fn.readdir(root_path)
+ if subentries then
+ for _, subentry in ipairs(subentries) do
+ local absolute_path = root_path .. "/" .. subentry
+ table.insert(entries, subentry)
+ end
+ end
+
+ local actions = safe_require("telescope.actions")
+ local actions_set = safe_require("telescope.actions.set")
+ local actions_state = safe_require("telescope.actions.state")
+ local finders = safe_require("telescope.finders")
+ local pickers = safe_require("telescope.pickers")
+ local config_mod = safe_require("telescope.config")
+ local config = config_mod and config_mod.values or {}
+
+ pickers
+ .new({}, {
+ prompt_title = "Change Directory or Open File",
+ finder = finders.new_table({
+ results = entries,
+ }),
+ previewer = config.file_previewer({}),
+ sorter = config.generic_sorter({}),
+ attach_mappings = function(prompt_bufnr, map)
+ actions_set.select:replace(function()
+ local entry = actions_state.get_selected_entry()
+ if entry ~= nil then
+ local selected_entry = entry.value
+ actions.close(prompt_bufnr, false)
+ local selected_path = root_path .. "/" .. selected_entry
+ if vim.fn.isdirectory(selected_path) == 1 then
+ vim.fn.chdir(selected_path)
+ vim.cmd("e .")
+ print("cwd: " .. vim.fn.getcwd())
+ else
+ vim.cmd("e " .. selected_path)
+ end
+ end
+ end)
+ return true
+ end,
+ })
+ :find()
+end
+
+-- Safe telescope function wrapper for keymaps
+local function safe_telescope_call(module_path, func_name, fallback_msg)
+ return function()
+ local ok, module = pcall(require, module_path)
+ if ok and module[func_name] then
+ module[func_name]()
+ else
+ vim.notify(fallback_msg or ("Telescope plugin not available for " .. func_name), vim.log.levels.WARN)
+ end
+ end
+end
+
+local function safe_telescope_builtin(func_name, fallback_msg)
+ return function(opts)
+ local ok, telescope_builtin = pcall(require, "telescope.builtin")
+ if not ok then
+ vim.notify(fallback_msg or ("Telescope builtin module (telescope.builtin) not found!"), vim.log.levels.ERROR)
+ vim.notify("Error details: " .. tostring(telescope_builtin), vim.log.levels.DEBUG) -- telescope_builtin will contain the error message here
+ return
+ end
+
+ if not telescope_builtin[func_name] then
+ vim.notify(fallback_msg or ("Telescope builtin function '" .. func_name .. "' not found!"), vim.log.levels.ERROR)
+ vim.notify("Available builtin functions: " .. vim.inspect(vim.tbl_keys(telescope_builtin)), vim.log.levels.DEBUG)
+ return
+ end
+
+ -- If both are ok, proceed
+ telescope_builtin[func_name](opts or {})
+ end
+end
+
+-- Safe builtin telescope functions
+local function safe_telescope_builtin(func_name, fallback_msg)
+ return function(opts)
+ local ok, telescope_builtin = pcall(require, "telescope.builtin")
+ if ok and telescope_builtin[func_name] then
+ telescope_builtin[func_name](opts or {})
+ else
+ vim.notify(fallback_msg or ("Telescope builtin not available: " .. func_name), vim.log.levels.WARN)
+ end
+ end
+end
+
+-- Safe extension calls with better checking
+local function safe_telescope_extension(ext_name, func_name, fallback_msg)
+ return function(opts)
+ local telescope_mod = package.loaded.telescope or require("telescope")
+ if not telescope_mod then
+ return
+ end
+
+ -- Check if extension is loaded
+ if not telescope_mod.extensions or not telescope_mod.extensions[ext_name] then
+ vim.notify(fallback_msg or ("Telescope extension '" .. ext_name .. "' not available (plugin may not be installed)"), vim.log.levels.WARN)
+ return
+ end
+
+ local ext_func = telescope_mod.extensions[ext_name][func_name]
+ if not ext_func then
+ vim.notify(fallback_msg or ("Function '" .. func_name .. "' not found in extension '" .. ext_name .. "'"), vim.log.levels.WARN)
+ return
+ end
+
+ ext_func(opts or {})
+ end
+end
+
+-- Fallback-safe `find_files`
+M.safe_find_files = function()
+ local builtin = safe_require("telescope.builtin")
+ if builtin and builtin.find_files then
+ builtin.find_files()
+ else
+ local file = vim.fn.input("Open file: ", "", "file")
+ if file ~= "" then vim.cmd("edit " .. file) end
+ end
+end
+
+-- Export safe wrapper functions for external use
+M.safe_telescope_call = safe_telescope_call
+M.safe_telescope_builtin = safe_telescope_builtin
+M.safe_telescope_extension = safe_telescope_extension
+
+return M
diff --git a/common/nvim/lua/plugins/toggleterm.lua b/common/nvim/lua/plugins/toggleterm.lua
new file mode 100755
index 0000000..6b7aad5
--- /dev/null
+++ b/common/nvim/lua/plugins/toggleterm.lua
@@ -0,0 +1,294 @@
+local M = {}
+
+--- Setup and configure toggleterm.nvim
+-- This function initializes and configures the toggleterm plugin for terminal management
+-- @return boolean True if setup was successful, false otherwise
+function M.setup()
+ local ok, toggleterm = pcall(require, 'toggleterm')
+ if not ok or not toggleterm then
+ return false
+ end
+
+ toggleterm.setup({
+ --open_mapping = [[<leader>tt]],
+ autochdir = true,
+ hide_numbers = true,
+ shade_filetypes = {},
+ shade_terminals = false,
+ --shading_factor = 1,
+ start_in_insert = true,
+ insert_mappings = true,
+ terminal_mappings = true,
+ persist_size = true,
+ direction = 'float',
+ --direction = "vertical",
+ --direction = "horizontal",
+ close_on_exit = true,
+ shell = vim.o.shell,
+ highlights = {
+ -- highlights which map to a highlight group name and a table of it's values
+ -- NOTE: this is only a subset of values, any group placed here will be set for the terminal window split
+ --Normal = {
+ -- background = "#000000",
+ --},
+ --Normal = { guibg = 'Black', guifg = 'White' },
+ --FloatBorder = { guibg = 'Black', guifg = 'DarkGray' },
+ --NormalFloat = { guibg = 'Black' },
+ float_opts = {
+ --winblend = 3,
+ },
+ },
+ size = function(term)
+ if term.direction == 'horizontal' then
+ return 7
+ elseif term.direction == 'vertical' then
+ return math.floor(vim.o.columns * 0.4)
+ end
+ end,
+ float_opts = {
+ width = 70,
+ height = 15,
+ border = 'curved',
+ highlights = {
+ border = 'Normal',
+ --background = 'Normal',
+ },
+ --winblend = 0,
+ },
+ })
+
+ -- Set up keymaps for toggleterm
+ local Terminal = require('toggleterm.terminal').Terminal
+
+ -- Custom terminal commands
+ local lazygit
+ if not Terminal then return end
+ local term = Terminal:new({
+ cmd = 'lazygit',
+ dir = 'git_dir',
+ direction = 'float',
+ float_opts = {
+ border = 'curved',
+ },
+ on_open = function(term)
+ vim.cmd('startinsert!')
+ vim.api.nvim_buf_set_keymap(term.bufnr, 'n', 'q', '<cmd>close<CR>', {noremap = true, silent = true})
+ end,
+ })
+ if term then
+ lazygit = term
+ end
+
+ -- Toggle functions
+ local function _lazygit_toggle()
+ if not Terminal then return end
+ if not lazygit then
+ init_lazygit()
+ end
+ if lazygit then
+ pcall(lazygit.toggle, lazygit)
+ end
+ end
+
+ -- Set up keymaps
+ vim.keymap.set('n', '<leader>tt', '<cmd>ToggleTerm<CR>', {noremap = true, silent = true, desc = 'Toggle Terminal'})
+ vim.keymap.set('n', '<leader>tf', '<cmd>ToggleTerm direction=float<CR>', {noremap = true, silent = true, desc = 'Toggle Float Terminal'})
+ vim.keymap.set('n', '<leader>th', '<cmd>ToggleTerm size=10 direction=horizontal<CR>', {noremap = true, silent = true, desc = 'Toggle Horizontal Terminal'})
+ vim.keymap.set('n', '<leader>tv', '<cmd>ToggleTerm size=80 direction=vertical<CR>', {noremap = true, silent = true, desc = 'Toggle Vertical Terminal'})
+ vim.keymap.set('n', '<leader>tl', _lazygit_toggle, {noremap = true, silent = true, desc = 'Toggle Lazygit'})
+
+ -- Terminal mode mappings
+ vim.keymap.set('t', '<esc>', [[<C-\><C-n>]], {noremap = true, silent = true})
+ vim.keymap.set('t', 'jk', [[<C-\><C-n>]], {noremap = true, silent = true})
+ vim.keymap.set('t', '<C-h>', [[<Cmd>wincmd h<CR>]], {noremap = true, silent = true})
+ vim.keymap.set('t', '<C-j>', [[<Cmd>wincmd j<CR>]], {noremap = true, silent = true})
+ vim.keymap.set('t', '<C-k>', [[<Cmd>wincmd k<CR>]], {noremap = true, silent = true})
+ vim.keymap.set('t', '<C-l>', [[<Cmd>wincmd l<CR>]], {noremap = true, silent = true})
+
+ return true
+end
+
+-- Terminal utility functions
+local mods = {}
+
+-- Simple empty check function if mods.empty is not available
+function mods.empty(v)
+ return v == nil or v == ''
+end
+local float_handler = function(term)
+ if not mods.empty(vim.fn.mapcheck('jk', 't')) then
+ vim.keymap.del('t', 'jk', { buffer = term.bufnr })
+ vim.keymap.del('t', '<esc>', { buffer = term.bufnr })
+ end
+end
+
+function _G.set_terminal_keymaps()
+ local opts = { noremap = true }
+ --local opts = {buffer = 0}
+ --vim.api.nvim_buf_set_keymap(0, "i", ";to", "[[<Esc>]]<cmd>Toggleterm", opts)
+ vim.api.nvim_buf_set_keymap(0, 't', '<C-c>', [[<Esc>]], opts)
+ vim.api.nvim_buf_set_keymap(0, 't', '<esc>', [[<C-\><C-n>]], opts)
+ vim.api.nvim_buf_set_keymap(0, 't', 'jk', [[<C-\><C-n>]], opts)
+ vim.api.nvim_buf_set_keymap(0, 't', '<C-h>', [[<C-\><C-n><C-W>h]], opts)
+ vim.api.nvim_buf_set_keymap(0, 't', '<C-j>', [[<C-\><C-n><C-W>j]], opts)
+ vim.api.nvim_buf_set_keymap(0, 't', '<C-k>', [[<C-\><C-n><C-W>k]], opts)
+ vim.api.nvim_buf_set_keymap(0, 't', '<C-l>', [[<C-\><C-n><C-W>l]], opts)
+end
+
+-- if you only want these mappings for toggle term use term://*toggleterm#* instead
+vim.cmd('autocmd! TermOpen term://* lua set_terminal_keymaps()')
+local Terminal
+local horizontal_term, vertical_term
+
+-- Safely require toggleterm.terminal
+local toggleterm_ok, toggleterm = pcall(require, 'toggleterm.terminal')
+if toggleterm_ok and toggleterm and toggleterm.Terminal then
+ Terminal = toggleterm.Terminal
+ -- Initialize terminals only if Terminal is available
+ if Terminal then
+ local ok1, hterm = pcall(Terminal.new, Terminal, { hidden = true, direction = 'horizontal' })
+ local ok2, vterm = pcall(Terminal.new, Terminal, { hidden = true, direction = 'vertical' })
+ if ok1 then horizontal_term = hterm end
+ if ok2 then vertical_term = vterm end
+ end
+end
+
+function Horizontal_term_toggle()
+ if horizontal_term then
+ pcall(horizontal_term.toggle, horizontal_term, 8, 'horizontal')
+ end
+end
+
+function Vertical_term_toggle()
+ if vertical_term then
+ pcall(vertical_term.toggle, vertical_term, math.floor(vim.o.columns * 0.5), 'vertical')
+ end
+end
+
+-- Initialize lazygit terminal instance
+local lazygit = nil
+local Cur_cwd = vim.fn.getcwd()
+
+-- Function to initialize lazygit terminal
+local function init_lazygit()
+ if not Terminal then return nil end
+ if not lazygit then
+ local ok, term = pcall(function()
+ return Terminal:new({
+ cmd = 'lazygit',
+ count = 5,
+ id = 1000,
+ dir = 'git_dir',
+ direction = 'float',
+ on_open = float_handler,
+ hidden = true,
+ float_opts = {
+ border = { '╒', '═', '╕', '│', '╛', '═', '╘', '│' },
+ width = 150,
+ height = 40,
+ },
+ })
+ end)
+ if ok and term then
+ lazygit = term
+ end
+ end
+ return lazygit
+end
+
+-- Initialize lazygit on first use
+function Lazygit_toggle()
+ -- Initialize lazygit if not already done
+ if not init_lazygit() then return end
+
+ -- cwd is the root of project. if cwd is changed, change the git.
+ local cwd = vim.fn.getcwd()
+ if cwd ~= Cur_cwd then
+ Cur_cwd = cwd
+ if lazygit then
+ lazygit:close()
+ end
+ lazygit = Terminal:new({
+ cmd = "zsh --login -c 'lazygit'",
+ dir = 'git_dir',
+ direction = 'float',
+ hidden = true,
+ on_open = float_handler,
+ float_opts = {
+ border = { '╒', '═', '╕', '│', '╛', '═', '╘', '│' },
+ width = 150,
+ height = 40,
+ },
+ })
+ end
+ if lazygit then
+ lazygit:toggle()
+ else
+ vim.notify("Failed to initialize lazygit terminal", vim.log.levels.ERROR)
+ end
+end
+
+local node = nil
+local ncdu = nil
+
+-- Initialize node terminal if Terminal is available
+if Terminal then
+ local ok1, nterm = pcall(function() return Terminal:new({ cmd = 'node', hidden = true }) end)
+ local ok2, ncduterm = pcall(function() return Terminal:new({ cmd = 'ncdu', hidden = true }) end)
+ if ok1 then node = nterm end
+ if ok2 then ncdu = ncduterm end
+end
+
+function _NODE_TOGGLE()
+ if not node then return end
+ pcall(node.toggle, node)
+end
+
+function _NCDU_TOGGLE()
+ if not ncdu then return end
+ pcall(ncdu.toggle, ncdu)
+end
+
+local htop = nil
+
+function _HTOP_TOGGLE()
+ if not Terminal then return end
+ if not htop then
+ local ok, term = pcall(function() return Terminal:new({ cmd = 'htop', hidden = true }) end)
+ if ok then htop = term end
+ end
+ if htop then
+ pcall(htop.toggle, htop)
+ end
+end
+
+local python = nil
+
+function _PYTHON_TOGGLE()
+ if not Terminal then return end
+ if not python then
+ local ok, term = pcall(function() return Terminal:new({ cmd = 'python', hidden = true }) end)
+ if ok then python = term end
+ end
+ if python then
+ pcall(python.toggle, python)
+ end
+end
+
+function Gh_dash()
+ Terminal:new({
+ cmd = 'gh dash',
+ hidden = true,
+ direction = 'float',
+ on_open = float_handler,
+ float_opts = {
+ height = function()
+ return math.floor(vim.o.lines * 0.8)
+ end,
+ width = function()
+ return math.floor(vim.o.columns * 0.95)
+ end,
+ },
+ })
+ Gh_dash:toggle()
+end
diff --git a/common/nvim/lua/plugins/treesitter.lua b/common/nvim/lua/plugins/treesitter.lua
new file mode 100755
index 0000000..9df99b8
--- /dev/null
+++ b/common/nvim/lua/plugins/treesitter.lua
@@ -0,0 +1,54 @@
+local M = {}
+
+function M.setup()
+ local ok, treesitter = pcall(require, "nvim-treesitter.configs")
+ if not ok or not treesitter then
+ return false
+ end
+
+ -- Add custom parser directory to runtime path
+ vim.opt.runtimepath:append("$HOME/.local/share/treesitter")
+
+ -- Configure treesitter
+ treesitter.setup({
+ -- Install parsers in custom directory
+ parser_install_dir = "$HOME/.local/share/treesitter",
+
+ -- Enable syntax highlighting
+ highlight = {
+ enable = true,
+ -- Disable additional regex-based highlighting to improve performance
+ additional_vim_regex_highlighting = false,
+ },
+
+ -- Enable indentation
+ indent = {
+ enable = true,
+ },
+
+ -- Additional modules to enable
+ incremental_selection = {
+ enable = true,
+ keymaps = {
+ init_selection = "gnn",
+ node_incremental = "grn",
+ scope_incremental = "grc",
+ node_decremental = "grm",
+ },
+ },
+
+ -- Ensure parsers are installed automatically
+ ensure_installed = {
+ "bash", "c", "cpp", "css", "dockerfile", "go", "html",
+ "javascript", "json", "lua", "markdown", "python", "rust",
+ "toml", "typescript", "vim", "yaml"
+ },
+
+ -- Auto-install parsers
+ auto_install = true,
+ })
+
+ return true
+end
+
+return M
diff --git a/common/nvim/lua/plugins/trouble.lua b/common/nvim/lua/plugins/trouble.lua
new file mode 100755
index 0000000..4a07e3b
--- /dev/null
+++ b/common/nvim/lua/plugins/trouble.lua
@@ -0,0 +1,73 @@
+local M = {}
+
+--- Setup and configure trouble.nvim
+-- This function initializes and configures the trouble plugin for diagnostics and references
+-- @return boolean True if setup was successful, false otherwise
+function M.setup()
+ local ok, trouble = pcall(require, 'trouble')
+ if not ok then
+ return false
+ end
+
+ trouble.setup({
+ position = "bottom", -- bottom, top, left, right
+ height = 10,
+ width = 50,
+ icons = {
+ indent = {
+ fold = {
+ open = "",
+ closed = "",
+ },
+ },
+ kinds = {
+ -- you can use LSP kind symbols or devicons here
+ -- remove if you want default
+ },
+ },
+ modes = {
+ diagnostics = {
+ groups = { "filename", "kind" },
+ },
+ symbols = {
+ format = "{kind_icon} {symbol.name} {symbol.kind} [{symbol.scope}]",
+ },
+ },
+ action_keys = {
+ close = "q",
+ cancel = "<esc>",
+ refresh = "r",
+ jump = { "<cr>", "<tab>" },
+ open_split = { "<c-x>" },
+ open_vsplit = { "<c-v>" },
+ open_tab = { "<c-t>" },
+ jump_close = { "o" },
+ toggle_preview = "P",
+ hover = "K",
+ preview = "p",
+ close_folds = { "zM", "zm" },
+ open_folds = { "zR", "zr" },
+ toggle_fold = { "zA", "za" },
+ previous = "k",
+ next = "j",
+ },
+ indent_lines = true,
+ auto_open = false,
+ auto_close = false,
+ auto_preview = true,
+ auto_fold = false,
+ auto_jump = { "lsp_definitions" },
+ signs = {
+ error = "",
+ warning = "▲",
+ info = "󰋼",
+ hint = "⚑",
+ other = "•",
+ },
+ use_diagnostic_signs = true,
+ })
+
+ return true
+end
+
+return M
diff --git a/common/nvim/lua/plugins/vimtex.lua b/common/nvim/lua/plugins/vimtex.lua
new file mode 100755
index 0000000..732e6ed
--- /dev/null
+++ b/common/nvim/lua/plugins/vimtex.lua
@@ -0,0 +1,45 @@
+--ft = { "latex", "tex" },
+--if vim.loop.os_uname().sysname == "Linux" then
+-- vim.g.vimtex_view_method = "zathura"
+--end
+--vim.g["vimtex_view_method"] = "zathura" -- main variant with xdotool (requires X11; not compatible with wayland)
+--vim.g.vimtex_compiler_method = "pdflatex"
+-- compilation configuration
+vim.g["vimtex_compiler_method"] = "latexmk"
+--vim.g["vimtex_compiler_method"] = "xelatex"
+--vim.g["vimtex_compiler_method"] = "lualatex"
+vim.g["vimtex_compiler_latexmk"] = {
+ callback = 1,
+ continuous = 1,
+ executable = "latexmk",
+ options = {
+ "-shell-escape",
+ "-verbose",
+ "-file-line-error",
+ "-synctex=1",
+ "-interaction=nonstopmode",
+ },
+}
+vim.g["vimtex_view_enabled"] = 1
+vim.g["vimtex_view_zathura_check_libsynctex"] = 0
+--vim.g["vimtex_view_method"] = "zathura" -- main variant with xdotool (requires X11; not compatible with wayland)
+if vim.loop.os_uname().sysname == "Linux" then
+ vim.g.vimtex_view_method = "zathura"
+end
+--vim.g.vimtex_view_method = "sioyek"
+--vim.g["vimtex_view_method"] = "zathura_simple" -- for variant without xdotool to avoid errors in wayland
+vim.g["vimtex_quickfix_mode"] = 0 -- suppress error reporting on save and build
+vim.g["vimtex_mappings_enabled"] = 0 -- Ignore mappings
+vim.g["vimtex_indent_enabled"] = 0 -- Auto Indent
+vim.g["tex_flavor"] = "latex" -- how to read tex files
+vim.g["tex_indent_items"] = 0 -- turn off enumerate indent
+vim.g["tex_indent_brace"] = 0 -- turn off brace indent
+--vim.g.vimtex_view_forward_search_on_start = 0
+--vim.g["vimtex_context_pdf_viewer"] = "zathura" -- external PDF viewer run from vimtex menu command
+--vim.g["latex_view_general_viewer"] = "zathura"
+vim.g["vimtex_log_ignore"] = { -- Error suppression:
+ "Underfull",
+ "Overfull",
+ "specifier changed to",
+ "Token not allowed in a PDF string",
+}
diff --git a/common/nvim/lua/plugins/web-devicons.lua b/common/nvim/lua/plugins/web-devicons.lua
new file mode 100755
index 0000000..a565a31
--- /dev/null
+++ b/common/nvim/lua/plugins/web-devicons.lua
@@ -0,0 +1,125 @@
+local M = {}
+
+-- Cache the nerd fonts check with better error handling
+local function get_nerd_fonts_available()
+ if vim.g.nerd_fonts_available ~= nil then
+ return vim.g.nerd_fonts_available
+ end
+
+ local has_nerd_fonts = false
+ local ok, result = pcall(function()
+ if vim.fn.has('unix') == 1 and vim.fn.executable('fc-list') == 1 then
+ local handle = io.popen('fc-list | grep -i nerd 2>/dev/null')
+ if handle then
+ local result = handle:read('*a')
+ handle:close()
+ return result ~= ""
+ end
+ end
+ return false
+ end)
+
+ has_nerd_fonts = ok and result or false
+ vim.g.nerd_fonts_available = has_nerd_fonts
+ return has_nerd_fonts
+end
+
+-- Helper function to get icon with fallback and validation
+local function get_icon(nerd_icon, fallback, color, cterm_color, name)
+ local has_nerd = get_nerd_fonts_available()
+
+ -- Validate colors
+ if not color or color == '' then
+ color = '#6d8086' -- Default gray color
+ end
+ if not cterm_color or cterm_color == '' then
+ cterm_color = '102' -- Default gray for terminal
+ end
+
+ -- Pick icon
+ local icon = has_nerd and nerd_icon or fallback
+ if not icon or icon == '' then
+ icon = has_nerd and '󰈔' or '[F]'
+ end
+
+ return {
+ icon = icon,
+ color = color,
+ cterm_color = cterm_color,
+ name = name or 'File',
+ }
+end
+
+function M.setup()
+ local ok, devicons = pcall(require, 'nvim-web-devicons')
+ if not ok or not devicons then
+ return false
+ end
+
+ devicons.setup({
+ color_icons = true,
+ override = {
+ -- Languages
+ js = get_icon('󰌞', '[JS]', '#f5c06f', '179', 'Js'),
+ jsx = get_icon('', '[JSX]', '#689fb6', '67', 'Jsx'),
+ ts = get_icon('󰛦', '[TS]', '#4377c1', '67', 'Ts'),
+ tsx = get_icon('', '[TSX]', '#4377c1', '67', 'Tsx'),
+ lua = get_icon('', '[LUA]', '#51a0cf', '74', 'Lua'),
+ py = get_icon('', '[PY]', '#3572A5', '67', 'Python'),
+ rb = get_icon('', '[RB]', '#701516', '124', 'Ruby'),
+ go = get_icon('', '[GO]', '#519aba', '74', 'Go'),
+ rs = get_icon('', '[RS]', '#dea584', '173', 'Rust'),
+
+ -- Images
+ png = get_icon('󰋩', '[PNG]', '#d4843e', '173', 'Png'),
+ jpg = get_icon('󰋩', '[JPG]', '#16a085', '36', 'Jpg'),
+ jpeg = get_icon('󰋩', '[JPG]', '#16a085', '36', 'Jpeg'),
+ webp = get_icon('󰋩', '[WEBP]', '#3498db', '32', 'Webp'),
+ svg = get_icon('󰋩', '[SVG]', '#3affdb', '80', 'Svg'),
+
+ -- Archives
+ zip = get_icon('', '[ZIP]', '#e6b422', '178', 'Zip'),
+ rar = get_icon('', '[RAR]', '#e6b422', '178', 'Rar'),
+ ['7z'] = get_icon('', '[7Z]', '#e6b422', '178', '7z'),
+ tar = get_icon('', '[TAR]', '#e6b422', '178', 'Tar'),
+ gz = get_icon('', '[GZ]', '#e6b422', '178', 'GZip'),
+ bz2 = get_icon('', '[BZ2]', '#e6b422', '178', 'BZip2'),
+
+ -- Docs
+ md = get_icon('', '[MD]', '#519aba', '67', 'Markdown'),
+ txt = get_icon('', '[TXT]', '#6d8086', '102', 'Text'),
+ pdf = get_icon('', '[PDF]', '#e74c3c', '160', 'PDF'),
+ doc = get_icon('', '[DOC]', '#2c6ecb', '27', 'Word'),
+ docx = get_icon('', '[DOC]', '#2c6ecb', '27', 'Word'),
+ xls = get_icon('', '[XLS]', '#1d6f42', '29', 'Excel'),
+ xlsx = get_icon('', '[XLS]', '#1d6f42', '29', 'Excel'),
+
+ -- Config
+ json = get_icon('', '[JSON]', '#f5c06f', '179', 'Json'),
+ yaml = get_icon('', '[YAML]', '#6d8086', '102', 'Yaml'),
+ toml = get_icon('', '[TOML]', '#6d8086', '102', 'Toml'),
+ conf = get_icon('', '[CFG]', '#6d8086', '102', 'Config'),
+ ini = get_icon('', '[INI]', '#6d8086', '102', 'Ini'),
+
+ -- Shell
+ sh = get_icon('', '[SH]', '#4d5a5e', '59', 'Shell'),
+ zsh = get_icon('', '[ZSH]', '#89e051', '113', 'Zsh'),
+ bash = get_icon('', '[BASH]', '#89e051', '113', 'Bash'),
+
+ -- Git
+ ['.gitignore'] = get_icon('', '[GIT]', '#e24329', '166', 'GitIgnore'),
+ ['.gitattributes'] = get_icon('', '[GIT]', '#e24329', '166', 'GitAttributes'),
+ ['.gitconfig'] = get_icon('', '[GIT]', '#e24329', '166', 'GitConfig'),
+ },
+ default = {
+ icon = get_nerd_fonts_available() and '󰈔' or '[F]',
+ name = 'File',
+ color = '#6d8086',
+ cterm_color = '102',
+ },
+ })
+
+ return true
+end
+
+return M
diff --git a/common/nvim/lua/plugins/which-key.lua b/common/nvim/lua/plugins/which-key.lua
new file mode 100755
index 0000000..10015aa
--- /dev/null
+++ b/common/nvim/lua/plugins/which-key.lua
@@ -0,0 +1,53 @@
+local M = {}
+
+function M.setup()
+ local ok, wk = pcall(require, 'which-key')
+ if not ok then
+ return false
+ end
+
+ -- Basic configuration
+ wk.setup({
+ plugins = {
+ marks = true,
+ registers = true,
+ spelling = { enabled = true, suggestions = 20 },
+ presets = {
+ operators = true,
+ motions = true,
+ text_objects = true,
+ windows = true,
+ nav = true,
+ z = true,
+ g = true,
+ },
+ },
+ --window = {
+ -- border = "none",
+ -- position = "bottom",
+ -- margin = { 1, 0, 1, 0 },
+ -- padding = { 2, 2, 2, 2 },
+ -- winblend = 0
+ --},
+ --layout = {
+ -- height = { min = 4, max = 25 },
+ -- width = { min = 20, max = 50 },
+ -- spacing = 3,
+ -- align = "left"
+ --},
+ --ignore_missing = false,
+ --hidden = { "<silent>", "<cmd>", "<Cmd>", "<CR>", "call", "lua", "^:", "^ " },
+ --show_help = true,
+ --triggers = "<leader>",
+ --triggers_blacklist = {
+ -- i = { "j", "k" },
+ -- v = { "j", "k" },
+ --}
+ })
+
+
+
+ return true
+end
+
+return M
diff --git a/common/nvim/lua/plugins/zen-mode.lua b/common/nvim/lua/plugins/zen-mode.lua
new file mode 100755
index 0000000..7e52854
--- /dev/null
+++ b/common/nvim/lua/plugins/zen-mode.lua
@@ -0,0 +1,7 @@
+local status, zenMode = pcall(require, "zen-mode")
+if (not status) then return end
+
+zenMode.setup {
+}
+
+vim.keymap.set('n', '<C-w>o', '<cmd>ZenMode<cr>', { silent = true })
diff --git a/common/nvim/lua/setup/compat.lua b/common/nvim/lua/setup/compat.lua
new file mode 100755
index 0000000..ef90444
--- /dev/null
+++ b/common/nvim/lua/setup/compat.lua
@@ -0,0 +1,104 @@
+-- setup/compat.lua
+-- Automatically patches deprecated APIs based on Neovim version
+
+-- Version check helper
+local function has_version(major, minor, patch)
+ local v = vim.version()
+ patch = patch or 0
+ return v.major > major
+ or (v.major == major and v.minor > minor)
+ or (v.major == major and v.minor == minor and v.patch >= patch)
+end
+
+-- === GLOBAL PATCHES === --
+
+-- Neovim 0.10+: vim.islist replaces deprecated vim.tbl_islist
+if has_version(0, 10) then
+ if vim.tbl_islist == nil then
+ vim.tbl_islist = vim.islist
+ end
+end
+
+-- Neovim 0.12+: vim.tbl_flatten removed → shim using vim.iter
+if has_version(0, 12) then
+ vim.tbl_flatten = function(t)
+ return vim.iter(t):flatten():totable()
+ end
+end
+
+-- === DEPRECATION SHIMS (0.13 / 1.0) === --
+
+-- client.is_stopped → client:is_stopped()
+if has_version(0, 13) then
+ local mt = getmetatable(vim.lsp._client or {})
+ if mt and mt.__index and mt.__index.is_stopped then
+ mt.__index.is_stopped = function(client, ...)
+ return client:is_stopped(...)
+ end
+ end
+end
+
+-- client.request → client:request()
+if has_version(0, 13) then
+ local mt = getmetatable(vim.lsp._client or {})
+ if mt and mt.__index and mt.__index.request then
+ mt.__index.request = function(client, ...)
+ return client:request(...)
+ end
+ end
+end
+
+-- vim.validate{tbl} → vim.validate(tbl)
+if has_version(1, 0) then
+ if type(vim.validate) == "function" then
+ local old_validate = vim.validate
+ vim.validate = function(arg)
+ -- Handle both forms for backward compatibility
+ if type(arg) == "table" then
+ return old_validate(arg)
+ else
+ return old_validate{ arg }
+ end
+ end
+ end
+end
+
+-- Deprecated: vim.lsp.get_active_clients (moved in 0.11+)
+if has_version(0, 11) then
+ if vim.lsp.get_active_clients == nil then
+ vim.lsp.get_active_clients = function(...)
+ return vim.lsp.get_clients(...)
+ end
+ end
+end
+
+-- Deprecated: vim.diagnostic.setqflist / setloclist (moved in 0.11+)
+if has_version(0, 11) then
+ if vim.diagnostic.setqflist == nil then
+ vim.diagnostic.setqflist = function(diags, opts)
+ return vim.diagnostic.toqflist(diags, opts)
+ end
+ end
+ if vim.diagnostic.setloclist == nil then
+ vim.diagnostic.setloclist = function(diags, opts)
+ return vim.diagnostic.toloclist(diags, opts)
+ end
+ end
+end
+
+-- Deprecated: vim.lsp.buf.formatting/formatting_sync (removed in 0.8+)
+if has_version(0, 8) then
+ if vim.lsp.buf.formatting == nil then
+ vim.lsp.buf.formatting = function(opts)
+ return vim.lsp.buf.format(opts)
+ end
+ end
+ if vim.lsp.buf.formatting_sync == nil then
+ vim.lsp.buf.formatting_sync = function(opts, timeout_ms)
+ return vim.lsp.buf.format(vim.tbl_extend("force", opts or {}, { timeout_ms = timeout_ms }))
+ end
+ end
+end
+
+-- Return something to satisfy require()
+return true
diff --git a/common/nvim/lua/setup/manager.lua b/common/nvim/lua/setup/manager.lua
new file mode 100755
index 0000000..9cf1d14
--- /dev/null
+++ b/common/nvim/lua/setup/manager.lua
@@ -0,0 +1,811 @@
+-- manager.lua
+
+local M = {}
+
+-- State tracking
+local state = {
+ manager_invoked = nil,
+ initialized = false,
+ bootstrap_completed = {},
+}
+
+-- Path constants
+local PATHS = {
+ lazy = vim.fn.stdpath("data") .. "/lazy/lazy.nvim",
+ packer = vim.fn.stdpath("data") .. "/site/pack/packer/start/packer.nvim",
+ packer_dir = vim.fn.stdpath("data") .. "/site/pack/packer/start",
+ builtin_dir = vim.fn.stdpath("data") .. "/nvim/site/pack/core/opt",
+}
+
+-- Utility functions
+local function safe_require(module)
+ local ok, result = pcall(require, module)
+ return ok and result or nil
+end
+
+local function notify(msg, level)
+ vim.notify("[Manager] " .. msg, level or vim.log.levels.INFO)
+end
+
+local function execute_git_command(cmd, _)
+ -- Use vim.fn.system instead of os.execute for better cross-platform support and error handling
+ local result = vim.fn.system(cmd)
+ return vim.v.shell_error == 0
+end
+
+local function get_nvim_version()
+ local version = vim.version()
+ if version then
+ return version.major, version.minor, version.patch
+ end
+
+ -- Fallback for older versions
+ local version_str = vim.fn.execute("version"):match("NVIM v(%d+%.%d+%.%d+)")
+ if version_str then
+ local major, minor, patch = version_str:match("(%d+)%.(%d+)%.(%d+)")
+ return tonumber(major), tonumber(minor), tonumber(patch)
+ end
+ return 0, 0, 0
+end
+
+local function has_builtin_manager()
+ local major, minor = get_nvim_version()
+ return major > 0 or (major == 0 and minor >= 12)
+end
+
+-- CRITICAL FIX: This function is essential to prevent runtime conflicts.
+-- It removes the specified manager's directory from the runtimepath.
+local function cleanup_manager(manager_name)
+ if manager_name == "packer" then
+ -- Reset packer state and remove from rtp
+ local packer = safe_require("packer")
+ if packer then
+ pcall(packer.reset)
+ end
+ -- Remove the entire packer directory from rtp
+ local packer_rtp = vim.fn.glob(PATHS.packer_dir)
+ if packer_rtp then
+ local rtp_items = vim.split(vim.o.rtp, ",")
+ local new_rtp_items = {}
+ for _, item in ipairs(rtp_items) do
+ if item ~= packer_rtp then
+ table.insert(new_rtp_items, item)
+ end
+ end
+ vim.o.rtp = table.concat(new_rtp_items, ",")
+ end
+ elseif manager_name == "lazy" then
+ -- Lazy.nvim clears its state on each run, but we can remove it from rtp for good measure
+ local lazy_rtp = vim.fn.glob(PATHS.lazy)
+ if lazy_rtp then
+ local rtp_items = vim.split(vim.o.rtp, ",")
+ local new_rtp_items = {}
+ for _, item in ipairs(rtp_items) do
+ if item ~= lazy_rtp then
+ table.insert(new_rtp_items, item)
+ end
+ end
+ vim.o.rtp = table.concat(new_rtp_items, ",")
+ end
+ elseif manager_name == "builtin" then
+ -- Built-in manager is handled by vim.opt.packpath and doesn't need manual cleanup from rtp
+ -- unless we want to disable its packages, which isn't the goal here.
+ end
+end
+
+-- IMPROVED: Use vim.g for persistence instead of file system
+local function save_manager_choice(manager_name)
+ vim.g.nvim_manager_choice = manager_name
+ -- Also save to data directory as a simple text file for true persistence across sessions
+ local data_dir = vim.fn.stdpath("data")
+ local choice_file = data_dir .. "/.manager_choice"
+ local file = io.open(choice_file, "w")
+ if file then
+ file:write(manager_name)
+ file:close()
+ end
+end
+
+local function load_manager_choice()
+ -- First check vim.g (current session)
+ if vim.g.nvim_manager_choice then
+ return vim.g.nvim_manager_choice
+ end
+
+ -- Then check persistent file
+ local data_dir = vim.fn.stdpath("data")
+ local choice_file = data_dir .. "/.manager_choice"
+ local file = io.open(choice_file, "r")
+ if file then
+ local choice = file:read("*a"):gsub("%s+", "") -- trim whitespace
+ file:close()
+ if choice and choice ~= "" then
+ vim.g.nvim_manager_choice = choice -- cache in session
+ return choice
+ end
+ end
+
+ return nil
+end
+
+--- Packer Manager Implementation
+--
+-- Handles cloning, setup, and configuration of Packer.nvim.
+local Packer = {}
+
+function Packer.bootstrap()
+ if state.bootstrap_completed.packer then
+ return true
+ end
+
+ local fn = vim.fn
+ if fn.isdirectory(PATHS.packer_dir) == 0 then
+ fn.mkdir(PATHS.packer_dir, "p")
+ end
+
+ if fn.empty(fn.glob(PATHS.packer)) > 0 then
+ local is_windows = vim.loop.os_uname().version:match("Windows")
+ local git_cmd
+
+ if is_windows then
+ git_cmd = string.format(
+ 'git clone --depth=1 https://github.com/wbthomason/packer.nvim "%s" >nul 2>&1',
+ PATHS.packer
+ )
+ else
+ git_cmd = string.format(
+ 'env -i PATH="%s" HOME="%s" git clone --depth=1 --quiet https://github.com/wbthomason/packer.nvim %q >/dev/null 2>&1',
+ os.getenv("PATH") or "/usr/bin:/bin",
+ os.getenv("HOME") or "/tmp",
+ PATHS.packer
+ )
+ end
+
+ if not execute_git_command(git_cmd, "Failed to clone packer.nvim") then
+ return false
+ end
+ end
+
+ state.bootstrap_completed.packer = true
+ return true
+end
+
+function Packer.setup()
+ if not Packer.bootstrap() then
+ return false
+ end
+
+ -- Ensure packer.nvim is in the runtime path
+ vim.cmd("packadd packer.nvim")
+
+ local packer = safe_require("packer")
+ if not packer then
+ notify("Failed to load packer.nvim", vim.log.levels.ERROR)
+ return false
+ end
+
+ -- Reset any existing configuration from a previous run
+ pcall(packer.reset)
+
+ packer.init({
+ auto_reload_compiled = true,
+ display = {
+ open_fn = function()
+ return require("packer.util").float({ border = "rounded" })
+ end,
+ },
+ luarocks = {
+ python_cmd = 'python3'
+ },
+ })
+
+ local plugins = safe_require("setup.plugins")
+ if not plugins then
+ notify("Failed to load plugins configuration", vim.log.levels.ERROR)
+ return false
+ end
+
+ packer.startup(function(use)
+ use "wbthomason/packer.nvim"
+ for _, plugin in ipairs(plugins) do
+ -- CHECK FOR EXCLUDE HERE - Packer support for exclude option
+ if plugin.exclude and vim.tbl_contains(plugin.exclude, "packer") then
+ --notify("Excluding plugin for packer: " .. (plugin.name or plugin.as or plugin[1] or "unknown"), vim.log.levels.INFO)
+ goto continue
+ end
+
+ -- Packer doesn't have a lazy option, so we ensure all plugins are loaded eagerly
+ -- by clearing any lazy-loading keys from the plugins table.
+ local packer_plugin = vim.deepcopy(plugin)
+ packer_plugin.event = nil
+ packer_plugin.keys = nil
+ packer_plugin.cmd = nil
+ packer_plugin.ft = nil
+ packer_plugin.lazy = nil
+ packer_plugin.exclude = nil -- Remove exclude from the actual plugin spec
+ use(packer_plugin)
+ ::continue::
+ end
+ end)
+
+ return true
+end
+
+function Packer.is_available()
+ return vim.fn.isdirectory(PATHS.packer) == 1
+end
+
+--- Lazy.nvim Manager Implementation
+--
+local Lazy = {}
+
+function Lazy.bootstrap()
+ if state.bootstrap_completed.lazy then
+ return true
+ end
+
+ -- Check if lazy.nvim is already cloned
+ if not vim.loop.fs_stat(PATHS.lazy) then
+ local is_windows = vim.loop.os_uname().version:match("Windows")
+ local git_cmd
+
+ if is_windows then
+ git_cmd = string.format(
+ 'git clone --filter=blob:none --branch=stable https://github.com/folke/lazy.nvim.git "%s" >nul 2>&1',
+ PATHS.lazy
+ )
+ else
+ git_cmd = string.format(
+ 'env -i PATH="%s" HOME="%s" git clone --filter=blob:none --branch=stable --quiet https://github.com/folke/lazy.nvim.git %q >/dev/null 2>&1',
+ os.getenv("PATH") or "/usr/bin:/bin",
+ os.getenv("HOME") or "/tmp",
+ PATHS.lazy
+ )
+ end
+
+ if not execute_git_command(git_cmd, "Failed to clone lazy.nvim") then
+ return false
+ end
+ end
+
+ state.bootstrap_completed.lazy = true
+ return true
+end
+
+function Lazy.setup()
+ if not Lazy.bootstrap() then
+ return false
+ end
+
+ -- Ensure lazy.nvim is in the runtime path before requiring it
+ vim.opt.rtp:prepend(PATHS.lazy)
+
+ local lazy = safe_require("lazy")
+ if not lazy then
+ notify("Failed to load lazy.nvim", vim.log.levels.ERROR)
+ return false
+ end
+
+ -- FIX: Correctly require plugins and set up lazy.nvim
+ local plugins = safe_require("setup.plugins")
+ if not plugins then
+ notify("Failed to load plugins configuration", vim.log.levels.ERROR)
+ return false
+ end
+
+ -- Filter out excluded plugins for Lazy
+ local filtered_plugins = {}
+ for _, plugin in ipairs(plugins) do
+ -- CHECK FOR EXCLUDE HERE - Lazy support for exclude option
+ if plugin.exclude and vim.tbl_contains(plugin.exclude, "lazy") then
+ --notify("Excluding plugin for lazy: " .. (plugin.name or plugin[1] or "unknown"), vim.log.levels.INFO)
+ else
+ local lazy_plugin = vim.deepcopy(plugin)
+ lazy_plugin.exclude = nil -- Remove exclude from the actual plugin spec
+ table.insert(filtered_plugins, lazy_plugin)
+ end
+ end
+
+ -- Setup Lazy.nvim with the correct options
+ lazy.setup(filtered_plugins, {
+ {
+ import = "plugins",
+ },
+ defaults = { lazy = false }, -- Set plugins to be lazy-loaded by default
+ install = { missing = true }, -- CRITICAL FIX: This ensures missing plugins are installed
+ ui = {
+ border = "rounded",
+ },
+ performance = {
+ rtp = {
+ disabled_plugins = {
+ "gzip", "matchit", "matchparen", "netrwPlugin",
+ "tarPlugin", "tohtml", "tutor", "zipPlugin",
+ },
+ },
+ },
+ })
+
+ return true
+end
+
+function Lazy.is_available()
+ return vim.loop.fs_stat(PATHS.lazy) ~= nil
+end
+
+--- Built-in manager implementation (Neovim 0.12+)
+--
+local Builtin = {}
+
+function Builtin.bootstrap()
+ if not has_builtin_manager() then
+ --notify("Built-in package manager not available in this Neovim version", vim.log.levels.WARN)
+ return false
+ end
+
+ state.bootstrap_completed.builtin = true
+ return true
+end
+
+function Builtin.setup()
+ if not has_builtin_manager() then
+ --notify("Built-in package manager not available in this Neovim version", vim.log.levels.WARN)
+ return false
+ end
+
+ local plugins = safe_require("setup.plugins")
+ if not plugins then
+ notify("Failed to load plugins configuration", vim.log.levels.ERROR)
+ return false
+ end
+
+ -- Convert plugins to builtin manager format
+ local builtin_specs = {}
+ for _, plugin in ipairs(plugins) do
+ -- CHECK FOR EXCLUDE HERE
+ if plugin.exclude and vim.tbl_contains(plugin.exclude, "builtin") then
+ --notify("Excluding plugin for builtin: " .. (plugin.name or plugin[1] or "unknown"), vim.log.levels.INFO)
+ goto continue
+ end
+ local spec = {}
+
+ if type(plugin) == "string" then
+ -- Handle string format like "user/repo"
+ if plugin:match("^[%w%-_%.]+/[%w%-_%.]+$") then
+ -- It's a GitHub shorthand
+ spec.src = "https://github.com/" .. plugin
+ spec.name = plugin:match("/([%w%-_%.]+)$") -- Extract repo name
+ else
+ -- It's already a full URL
+ spec.src = plugin
+ end
+ elseif type(plugin) == "table" then
+ -- Handle table format
+ if plugin[1] and type(plugin[1]) == "string" then
+ -- Format like {"user/repo", ...}
+ if plugin[1]:match("^[%w%-_%.]+/[%w%-_%.]+$") then
+ spec.src = "https://github.com/" .. plugin[1]
+ spec.name = plugin[1]:match("/([%w%-_%.]+)$")
+ else
+ spec.src = plugin[1]
+ end
+
+ -- Copy other properties
+ for k, v in pairs(plugin) do
+ if type(k) == "string" then
+ spec[k] = v
+ end
+ end
+ elseif plugin.src then
+ spec.src = plugin.src
+ for k, v in pairs(plugin) do
+ if k ~= "src" then
+ spec[k] = v
+ end
+ end
+ elseif plugin.url then
+ spec.src = plugin.url
+ for k, v in pairs(plugin) do
+ if k ~= "url" then
+ spec[k] = v
+ end
+ end
+ else
+ notify("Invalid plugin specification for built-in manager: " .. vim.inspect(plugin), vim.log.levels.WARN)
+ goto continue
+ end
+
+ -- Handle name override
+ if plugin.name then
+ spec.name = plugin.name
+ elseif plugin.as then
+ spec.name = plugin.as
+ elseif not spec.name and spec.src then
+ -- Extract name from URL if not specified
+ spec.name = spec.src:match("/([%w%-_%.]+)%.git$") or spec.src:match("/([%w%-_%.]+)$") or spec.src
+ end
+
+ -- Handle version
+ if plugin.version then
+ spec.version = plugin.version
+ end
+
+ -- Remove keys that builtin manager doesn't understand
+ spec.lazy = nil
+ spec.event = nil
+ spec.keys = nil
+ spec.cmd = nil
+ spec.ft = nil
+ spec.dependencies = nil
+ spec.config = nil
+ spec.build = nil
+ spec.run = nil
+ spec.priority = nil
+ spec.as = nil
+ spec.url = nil
+ spec.exclude = nil
+ spec[1] = nil -- Remove positional argument
+ end
+
+ if spec.src then
+ table.insert(builtin_specs, spec)
+ end
+ ::continue::
+ end
+
+ -- Debug: Show what we're about to install
+ --notify(string.format("Installing %d plugins with built-in manager", #builtin_specs), vim.log.levels.INFO)
+
+ -- CRITICAL FIX: Call vim.pack.add with the specs directly, not wrapped in array
+ if #builtin_specs > 0 then
+ local ok, err = pcall(vim.pack.add, builtin_specs)
+ if not ok then
+ notify("Failed to add plugins: " .. tostring(err), vim.log.levels.ERROR)
+ return false
+ end
+
+ --notify("Plugins added successfully. Use :Pack to install/update them.", vim.log.levels.INFO)
+ else
+ notify("No valid plugins found for built-in manager", vim.log.levels.WARN)
+ end
+
+ -- Create user commands for convenience - FIXED COMMAND NAMES
+ vim.api.nvim_create_user_command("Package", function(opts)
+ local subcommand = opts.fargs[1] or "update"
+ local names = vim.list_slice(opts.fargs, 2)
+
+ if subcommand == "add" then
+ -- For add, we need to re-run setup to add new plugins
+ --notify("Re-running builtin manager setup to add new plugins...")
+ Builtin.setup()
+ elseif subcommand == "update" then
+ if #names == 0 then
+ names = nil -- Update all plugins
+ end
+ vim.pack.update(names)
+ elseif subcommand == "status" then
+ local plugins = vim.pack.get()
+ print(string.format("Built-in manager: %d plugins managed", #plugins))
+ for _, plugin in ipairs(plugins) do
+ local status = plugin.active and "active" or "inactive"
+ print(string.format(" %s (%s): %s", plugin.spec.name, status, plugin.path))
+ end
+ else
+ -- Default behavior - treat as update
+ if subcommand then
+ table.insert(names, 1, subcommand)
+ end
+ if #names == 0 then
+ names = nil
+ end
+ vim.pack.update(names)
+ end
+ end, {
+ nargs = "*",
+ complete = function(arglead, cmdline, cursorpos)
+ local args = vim.split(cmdline, "%s+")
+ if #args <= 2 then
+ -- Complete subcommands
+ local subcommands = { "add", "update", "status" }
+ local matches = {}
+ for _, cmd in ipairs(subcommands) do
+ if cmd:find("^" .. arglead) then
+ table.insert(matches, cmd)
+ end
+ end
+ return matches
+ else
+ -- Complete plugin names
+ local plugins = vim.pack.get()
+ local names = {}
+ for _, plugin in ipairs(plugins) do
+ if plugin.spec.name:find("^" .. arglead) then
+ table.insert(names, plugin.spec.name)
+ end
+ end
+ return names
+ end
+ end,
+ desc = "Manage plugins with built-in manager. Usage: :Pack [add|update|status] [plugin_names...]"
+ })
+
+ ---- Keep the old command for backwards compatibility
+ --vim.api.nvim_create_user_command("PackageStatus", function()
+ -- vim.cmd("Pack status")
+ --end, {
+ -- nargs = 0,
+ -- desc = "Show status of plugins managed by built-in manager (deprecated, use :Pack status)"
+ --})
+
+ return true
+end
+
+function Builtin.is_available()
+ return has_builtin_manager()
+end
+
+--- Manager registry
+--
+local MANAGERS = {
+ packer = Packer,
+ lazy = Lazy,
+ builtin = Builtin,
+}
+
+--- Core management functions
+--
+local function activate_manager(manager_name)
+ local manager = MANAGERS[manager_name]
+ if not manager then
+ notify("Unknown manager: " .. manager_name, vim.log.levels.ERROR)
+ return false
+ end
+
+ -- Cleanup the old manager before activating the new one to prevent runtime conflicts.
+ if state.manager_invoked and state.manager_invoked ~= manager_name then
+ cleanup_manager(state.manager_invoked)
+ end
+
+ if not manager.bootstrap() then
+ return false
+ end
+
+ local ok = manager.setup()
+ if ok then
+ state.manager_invoked = manager_name
+ -- CRITICAL FIX: Persist the manager choice after successful setup
+ save_manager_choice(manager_name)
+ end
+ return ok
+end
+
+--- Auto-detection and command setup
+--
+local function setup_auto_detection()
+ -- Autocmd to activate Packer when Packer commands are used
+ vim.api.nvim_create_autocmd("CmdUndefined", {
+ pattern = "Packer*",
+ callback = function(event)
+ if state.manager_invoked ~= "packer" then
+ local ok = activate_manager("packer")
+ if ok then
+ -- Re-execute the original command after setup
+ vim.cmd(event.match)
+ end
+ end
+ end,
+ desc = "Auto-activate Packer when Packer commands are used"
+ })
+
+ -- Autocmd to activate Lazy when Lazy commands are used
+ vim.api.nvim_create_autocmd("CmdUndefined", {
+ pattern = "Lazy*",
+ callback = function(event)
+ if state.manager_invoked ~= "lazy" then
+ local ok = activate_manager("lazy")
+ if ok then
+ -- CRITICAL FIX: Use vim.schedule to defer the command execution
+ -- This ensures Lazy's setup is complete before running the command.
+ vim.schedule(function()
+ pcall(vim.cmd, event.match)
+ end)
+ end
+ end
+ end,
+ desc = "Auto-activate Lazy and re-execute command"
+ })
+
+ vim.api.nvim_create_autocmd("CmdUndefined", {
+ pattern = "Package*",
+ callback = function(event)
+ if state.manager_invoked ~= "builtin" and has_builtin_manager() then
+ local ok = activate_manager("builtin")
+ if ok then
+ vim.cmd(event.match)
+ end
+ end
+ end,
+ desc = "Auto-activate built-in manager when Pack commands are used"
+ })
+end
+
+--- Public API
+--
+function M.setup()
+ if state.initialized then
+ return
+ end
+
+ -- Initial bootstrap attempt for all managers to see what's available
+ for name, manager in pairs(MANAGERS) do
+ -- CRITICAL FIX: Always bootstrap, but don't set up yet
+ pcall(manager.bootstrap)
+ end
+
+ -- CRITICAL FIX: Check for a previously saved choice
+ local persistent_choice = load_manager_choice()
+ if persistent_choice and MANAGERS[persistent_choice] then
+ -- If a choice exists, immediately activate that manager for this session
+ activate_manager(persistent_choice)
+ else
+ -- If no choice exists, set up the autocmds to wait for a command
+ setup_auto_detection()
+ end
+
+ state.initialized = true
+end
+
+function M.use_manager(manager_name)
+ if not state.initialized then
+ M.setup()
+ end
+
+ local available = M.available_managers()
+ if not vim.tbl_contains(available, manager_name) then
+ notify(string.format("Manager '%s' is not available. Available: %s",
+ manager_name, table.concat(available, ", ")), vim.log.levels.WARN)
+ return false
+ end
+
+ return activate_manager(manager_name)
+end
+
+function M.available_managers()
+ local managers = {}
+ for name, manager in pairs(MANAGERS) do
+ if manager.is_available() then
+ table.insert(managers, name)
+ end
+ end
+ return managers
+end
+
+function M.current_manager()
+ return state.manager_invoked
+end
+
+function M.status()
+ local info = {
+ initialized = state.initialized,
+ current_manager = state.manager_invoked,
+ available_managers = M.available_managers(),
+ bootstrap_completed = state.bootstrap_completed,
+ }
+
+ print("=== Neovim Plugin Manager Status ===")
+ print(string.format("Initialized: %s", tostring(info.initialized)))
+ print(string.format("Current Manager: %s", info.current_manager or "None"))
+ print(string.format("Available Managers: %s", table.concat(info.available_managers, ", ")))
+
+ -- FIX: Properly format the Neovim version
+ local major, minor, patch = get_nvim_version()
+ print(string.format("Neovim Version: %d.%d.%d", major, minor, patch))
+ print(string.format("Built-in Support: %s", tostring(has_builtin_manager())))
+
+ return info
+end
+
+-- FIX: Added M.get_nvim_version function to the public API
+function M.get_nvim_version()
+ local major, minor, patch = get_nvim_version()
+ return { major = major, minor = minor, patch = patch }
+end
+
+function M.reset_nvim()
+ vim.ui.input({
+ prompt = "Are you sure you want to reset Neovim? This will delete all data, state, cache, and plugins. (y/N): "
+ }, function(input)
+ if input and input:lower() == "y" then
+ local fn = vim.fn
+ local is_windows = vim.loop.os_uname().version:match("Windows")
+
+ local paths_to_remove = {
+ fn.stdpath("data"),
+ fn.stdpath("state"),
+ fn.stdpath("cache"),
+ fn.stdpath("config") .. "/plugin",
+ }
+
+ local cmd = ""
+ if is_windows then
+ local paths_quoted = {}
+ for _, path in ipairs(paths_to_remove) do
+ table.insert(paths_quoted, string.format('"%s"', path))
+ end
+ cmd = "powershell -Command \"Remove-Item " ..
+ table.concat(paths_quoted, ", ") .. " -Recurse -Force -ErrorAction SilentlyContinue\""
+ else
+ local paths_quoted = {}
+ for _, path in ipairs(paths_to_remove) do
+ table.insert(paths_quoted, vim.fn.shellescape(path))
+ end
+ cmd = "rm -rf " .. table.concat(paths_quoted, " ")
+ end
+
+ notify("Resetting Neovim... Please restart after this operation.")
+
+ vim.defer_fn(function()
+ local result = os.execute(cmd)
+ if result ~= 0 then
+ notify("Reset command may have failed. You might need to delete directories manually.", vim.log.levels.WARN)
+ else
+ notify("Reset completed successfully. Please restart Neovim.")
+ end
+ end, 100)
+ else
+ notify("Reset cancelled.")
+ end
+ end)
+end
+
+-- Clear manager choice function
+function M.clear_choice()
+ vim.g.nvim_manager_choice = nil
+ local data_dir = vim.fn.stdpath("data")
+ local choice_file = data_dir .. "/.manager_choice"
+ os.remove(choice_file)
+ notify("Manager choice cleared. Next command will determine the manager.")
+end
+
+vim.api.nvim_create_user_command("Reset", function()
+ M.reset_nvim()
+end, {
+ nargs = 0,
+ desc = "Reset Neovim's data, state, cache, and plugin directories"
+})
+
+local function manager_command(opts)
+ local subcommand = opts.fargs[1]
+
+ if subcommand == "status" then
+ M.status()
+ elseif subcommand == "packer" or subcommand == "Packer" then
+ M.use_manager("packer")
+ elseif subcommand == "lazy" or subcommand == "Lazy" then
+ M.use_manager("lazy")
+ elseif subcommand == "builtin" or subcommand == "built-in" or subcommand == "Builtin" or subcommand == "Built-in" then
+ M.use_manager("builtin")
+ elseif subcommand == "clear" then
+ M.clear_choice()
+ else
+ print("Unknown subcommand. Try 'status', 'packer', 'lazy', 'builtin' or 'clear'.")
+ end
+end
+
+vim.api.nvim_create_user_command("Manager", manager_command, {
+ nargs = "+",
+ complete = function(arglead)
+ local subcommands = { "status", "packer", "Packer", "lazy", "Lazy", "builtin", "built-in", "Builtin", "Built-in",
+ "clear" }
+ local result = {}
+ for _, subcommand in ipairs(subcommands) do
+ if subcommand:find("^" .. arglead, 1) then
+ table.insert(result, subcommand)
+ end
+ end
+ return result
+ end,
+ desc = "Manage plugins. Subcommands: status, packer, lazy, builtin, clear"
+})
+
+return M
diff --git a/common/nvim/lua/setup/plugins.lua b/common/nvim/lua/setup/plugins.lua
new file mode 100755
index 0000000..0fb0886
--- /dev/null
+++ b/common/nvim/lua/setup/plugins.lua
@@ -0,0 +1,609 @@
+-- plugins.lua
+
+-- Helper to compare current Neovim version
+local function version_at_least(minor, major)
+ local v = vim.version()
+ major = major or 0
+ return v.major > major or (v.major == major and v.minor >= minor)
+end
+
+local function version_below(minor, major)
+ local v = vim.version()
+ major = major or 0
+ return v.major < major or (v.major == major and v.minor < minor)
+end
+
+-- Normalize version input: number -> {0, number}, table -> itself
+local function parse_version(ver)
+ if type(ver) == "number" then return 0, ver end
+ if type(ver) == "table" then return ver[1] or 0, ver[2] or 0 end
+ return 0, 0
+end
+
+-- Determine if plugin should be loaded based on version
+local function should_load_plugin(min_version, max_version)
+ local min_major, min_minor = parse_version(min_version)
+ local max_major, max_minor = parse_version(max_version)
+
+ local ok_min = not min_version or version_at_least(min_minor, min_major)
+ local ok_max = not max_version or version_below(max_minor, max_major)
+
+ return ok_min and ok_max
+end
+
+-- Helper to check if a table contains a specific value
+local function contains(table, val)
+ for _, v in ipairs(table) do
+ if v == val then
+ return true
+ end
+ end
+ return false
+end
+
+-- The master list of plugins with all potential options.
+-- Keys like 'lazy', 'event', 'keys', 'dependencies' are for Lazy.nvim.
+-- Keys like 'config', 'run', 'build' are for all managers.
+local universal_plugins = {
+ -- Core
+ { "nvim-lua/plenary.nvim", lazy = true },
+ { "lewis6991/impatient.nvim" },
+
+ {
+ "nvim-treesitter/nvim-treesitter",
+ min_version = 9,
+ event = "BufReadPre",
+ },
+ { "nvim-treesitter/nvim-treesitter-textobjects", dependencies = { "nvim-treesitter/nvim-treesitter" } },
+ { "nvim-treesitter/playground", cmd = "TSPlaygroundToggle" },
+
+ -- LSP
+ { "nvimtools/none-ls.nvim", event = "BufReadPre" },
+ { "neovim/nvim-lspconfig", min_version = { 0, 9 }, event = "BufReadPre" },
+ {
+ "mason-org/mason.nvim",
+ min_version = 10,
+ cmd = "Mason",
+ event = "BufReadPre",
+ },
+ { "mason-org/mason-lspconfig.nvim", dependencies = { "mason-org/mason.nvim" } },
+ {
+ "whoissethdaniel/mason-tool-installer.nvim",
+ dependencies = { "mason-org/mason.nvim" },
+ event = "BufReadPre",
+ },
+ {
+ "https://git.sr.ht/~whynothugo/lsp_lines.nvim",
+ name = 'lsp_lines.nvim',
+ config = function()
+ require("lsp_lines").setup()
+ vim.diagnostic.config({
+ virtual_text = false,
+ })
+ end,
+ event = "LspAttach",
+ },
+ { "rmagatti/goto-preview", event = "LspAttach" },
+
+ -- Linters/Formatters
+ { "mhartington/formatter.nvim", event = "BufReadPre" },
+ {
+ "jay-babu/mason-null-ls.nvim",
+ event = "BufReadPre",
+ },
+
+ -- Completion
+ { "hrsh7th/nvim-cmp", event = "InsertEnter", exclude = { "builtin" } },
+ { "hrsh7th/cmp-nvim-lsp", dependencies = { "hrsh7th/nvim-cmp" }, exclude = { "builtin" } },
+ { "hrsh7th/cmp-buffer", dependencies = { "hrsh7th/nvim-cmp" }, exclude = { "builtin" } },
+ { "hrsh7th/cmp-path", dependencies = { "hrsh7th/nvim-cmp" }, exclude = { "builtin" } },
+ { "hrsh7th/cmp-cmdline", dependencies = { "hrsh7th/nvim-cmp" }, exclude = { "builtin" } },
+ { "petertriho/cmp-git", dependencies = { "hrsh7th/nvim-cmp" }, exclude = { "builtin" } },
+ { "tamago324/cmp-zsh", dependencies = { "hrsh7th/nvim-cmp" }, exclude = { "builtin" } },
+ { "f3fora/cmp-spell", dependencies = { "hrsh7th/nvim-cmp" }, exclude = { "builtin" } },
+ { "hrsh7th/cmp-calc", dependencies = { "hrsh7th/nvim-cmp" }, exclude = { "builtin" } },
+ { "saadparwaiz1/cmp_luasnip", dependencies = { "hrsh7th/nvim-cmp" }, exclude = { "builtin" } },
+ { "hrsh7th/cmp-nvim-lsp-signature-help", dependencies = { "hrsh7th/nvim-cmp" }, exclude = { "builtin" } },
+ { "rcarriga/cmp-dap", dependencies = { "hrsh7th/nvim-cmp" }, exclude = { "builtin" } },
+ { "micangl/cmp-vimtex", dependencies = { "hrsh7th/nvim-cmp" }, exclude = { "builtin" } },
+
+ -- Snippets
+ { "L3MON4D3/LuaSnip", event = "InsertEnter" },
+ { "rafamadriz/friendly-snippets", dependencies = { "L3MON4D3/LuaSnip" } },
+
+ -- Git
+ { "tpope/vim-fugitive", cmd = { "G", "Git", "Gdiffsplit" }, event = "VeryLazy" },
+ { "kdheepak/lazygit.nvim", cmd = "LazyGit", keys = "<leader>gg" },
+ { "lewis6991/gitsigns.nvim", min_version = 11, dependencies = { "nvim-lua/plenary.nvim" }, event = "BufReadPre" },
+
+ -- UI/UX & Enhancements
+ { "rcarriga/nvim-notify", lazy = false },
+ {
+ "nvim-tree/nvim-tree.lua",
+ --cmd = { "NvimTreeToggle", "NvimTreeFocus", "NvimTreeFindFile" },
+ --keys = { "<C-n>", "<leader>e" },
+ lazy = false,
+ dependencies = { "nvim-tree/nvim-web-devicons"},
+ config = function()
+ require("plugins.nvim-tree").setup()
+ end,
+ },
+ { "ThePrimeagen/harpoon", keys = { "<leader>h" } },
+ { "airblade/vim-rooter", event = "BufEnter" },
+ { "ibhagwan/fzf-lua", cmd = "FzfLua" },
+
+ --- **Telescope** ---
+ {
+ "nvim-telescope/telescope.nvim",
+ dependencies = { "nvim-lua/plenary.nvim" },
+ config = function()
+ require("plugins.telescope").setup()
+ end,
+ },
+ {
+ "nvim-telescope/telescope-fzf-native.nvim",
+ build = "make",
+ cond = function()
+ return vim.fn.executable("make") == 1
+ end,
+ dependencies = { "nvim-telescope/telescope.nvim" },
+ },
+ { "nvim-telescope/telescope-live-grep-args.nvim", dependencies = { "nvim-telescope/telescope.nvim" } },
+ { "nvim-telescope/telescope-ui-select.nvim", dependencies = { "nvim-telescope/telescope.nvim" } },
+ { "nvim-telescope/telescope-project.nvim", dependencies = { "nvim-telescope/telescope.nvim" } },
+ { "nvim-telescope/telescope-media-files.nvim", dependencies = { "nvim-telescope/telescope.nvim" } },
+ { "nvim-telescope/telescope-file-browser.nvim", dependencies = { "nvim-telescope/telescope.nvim" } },
+ { "nvim-telescope/telescope-symbols.nvim", dependencies = { "nvim-telescope/telescope.nvim" } },
+ { "nvim-telescope/telescope-dap.nvim", dependencies = { "nvim-telescope/telescope.nvim" } },
+ { "axkirillov/telescope-changed-files", dependencies = { "nvim-telescope/telescope.nvim" } },
+ { "smartpde/telescope-recent-files", dependencies = { "nvim-telescope/telescope.nvim" } },
+ --- End Telescope ---
+
+ -- Neovim UX
+ { "folke/neodev.nvim", ft = "lua" },
+ {
+ "numToStr/Navigator.nvim",
+ lazy = false,
+ config = function()
+ require("Navigator").setup()
+ end,
+ },
+ { "tpope/vim-eunuch", cmd = { "Rename", "Delete", "Mkdir" } },
+ { "tpope/vim-unimpaired", lazy = true, event = "VeryLazy" },
+ { "kylechui/nvim-surround", event = "VeryLazy" },
+ {
+ "mbbill/undotree",
+ cmd = "UndotreeToggle",
+ keys = "<leader>u",
+ event = "BufReadPre"
+ },
+ {
+ "myusuf3/numbers.vim",
+ event = "BufReadPost",
+ config = function()
+ vim.cmd("let g:numbers_exclude = ['dashboard']")
+ end,
+ },
+ { "windwp/nvim-autopairs", event = "InsertEnter" },
+ { "numToStr/Comment.nvim", keys = { "gc", "gb" }, event = "VeryLazy" },
+ { "akinsho/toggleterm.nvim", cmd = { "ToggleTerm", "TermExec" } },
+ { "tweekmonster/startuptime.vim", cmd = "StartupTime" },
+ { "qpkorr/vim-bufkill", cmd = { "BD", "BUN" } },
+ {
+ "ggandor/leap.nvim",
+ keys = { "s", "S" },
+ event = "VeryLazy",
+ config = function()
+ require("leap").add_default_mappings()
+ end,
+ },
+ {
+ "ggandor/flit.nvim",
+ keys = { "f", "F", "t", "T" },
+ event = "VeryLazy",
+ config = function()
+ require("flit").setup()
+ end,
+ },
+ {
+ "folke/which-key.nvim",
+ min_version = { 0, 10 },
+ event = "VeryLazy",
+ --keys = "<leader>",
+ config = function()
+ require("which-key").setup()
+ end,
+ },
+ { "folke/zen-mode.nvim", cmd = "ZenMode" },
+ { "romainl/vim-cool", event = "VeryLazy" },
+ { "antoinemadec/FixCursorHold.nvim", lazy = true },
+ { "folke/trouble.nvim", cmd = { "Trouble", "TroubleToggle" } },
+
+ -- Colorschemes & Visuals (load immediately)
+ { "nyoom-engineering/oxocarbon.nvim", lazy = false, priority = 1000 },
+ { "bluz71/vim-nightfly-guicolors", lazy = false, priority = 1000 },
+ { "ayu-theme/ayu-vim", lazy = false, priority = 1000 },
+ { "joshdick/onedark.vim", lazy = false, priority = 1000 },
+ { "NTBBloodbath/doom-one.nvim", lazy = false, priority = 1000 },
+ { "nyngwang/nvimgelion", lazy = false, priority = 1000 },
+ { "projekt0n/github-nvim-theme", lazy = false, priority = 1000 },
+ { "folke/tokyonight.nvim", lazy = false, priority = 1000 },
+ { "ribru17/bamboo.nvim", lazy = false, priority = 1000 },
+
+ -- UI Enhancements
+ --{ "kyazdani42/nvim-web-devicons", lazy = true },
+ { "nvim-tree/nvim-web-devicons", lazy = true },
+ { "onsails/lspkind-nvim", dependencies = { "hrsh7th/nvim-cmp" }, exclude = { "builtin" } },
+ { "kevinhwang91/nvim-ufo", dependencies = { "kevinhwang91/promise-async" }, event = "BufReadPre" },
+ {
+ "luukvbaal/statuscol.nvim",
+ event = "WinNew",
+ config = function()
+ local builtin = require("statuscol.builtin")
+ require("statuscol").setup({
+ relculright = true,
+ segments = {
+ { text = { builtin.foldfunc }, click = "v:lua.ScFa" },
+ { text = { "%s" }, click = "v:lua.ScSa" },
+ { text = { builtin.lnumfunc, " " }, click = "v:lua.ScLa" },
+ },
+ })
+ end,
+ },
+ { "lukas-reineke/indent-blankline.nvim", event = "BufReadPre" },
+ {
+ "glepnir/dashboard-nvim",
+ dependencies = { "nvim-tree/nvim-web-devicons" },
+ cmd = "Dashboard",
+ },
+ { "karb94/neoscroll.nvim", event = "BufReadPre" },
+ { "MunifTanjim/prettier.nvim", event = "BufWritePre" },
+ {
+ "norcalli/nvim-colorizer.lua",
+ cmd = { "ColorizerToggle", "ColorizerAttachToBuffer" },
+ config = function()
+ require("colorizer").setup({
+ user_default_options = {
+ RGB = true,
+ RRGGBB = true,
+ names = false,
+ RRGGBBAA = false,
+ css = false,
+ css_fn = true,
+ mode = "foreground",
+ },
+ })
+ end,
+ },
+ { "MunifTanjim/nui.nvim" },
+ { "metakirby5/codi.vim", cmd = "Codi" },
+ {
+ "kosayoda/nvim-lightbulb",
+ dependencies = { "antoinemadec/FixCursorHold.nvim" },
+ event = "LspAttach",
+ },
+ { "SmiteshP/nvim-navic", event = "LspAttach" },
+ {
+ "rebelot/heirline.nvim",
+ event = "VeryLazy",
+ dependencies = {
+ "nvim-tree/nvim-web-devicons", -- For file icons
+ "lewis6991/gitsigns.nvim", -- For git status
+ },
+ config = function()
+ -- Ensure gitsigns is loaded before Heirline
+ if package.loaded["gitsigns"] == nil then
+ require("gitsigns").setup()
+ end
+ local ok, heirline = pcall(require, "plugins.heirline")
+ if ok and heirline then
+ heirline.setup()
+ else
+ vim.notify("Failed to load Heirline configuration", vim.log.levels.ERROR)
+ end
+ end,
+ init = function()
+ -- Set up the statusline to use Heirline once it's loaded
+ vim.opt.statusline = "%{%v:lua.require'heirline'.eval_statusline()%}"
+ vim.opt.winbar = "%{%v:lua.require'heirline'.eval_winbar()%}"
+ vim.opt.tabline = "%{%v:lua.require'heirline'.eval_tabline()%}"
+ end,
+ },
+ {
+ "samodostal/image.nvim",
+ config = function()
+ require("image").setup({})
+ end,
+ ft = { "markdown" },
+ },
+
+ -- Language Specific
+ { "simrat39/rust-tools.nvim", ft = "rust" },
+ {
+ "saecki/crates.nvim",
+ dependencies = { "nvim-lua/plenary.nvim" },
+ config = function()
+ require("crates").setup()
+ end,
+ ft = "rust",
+ },
+ {
+ "akinsho/flutter-tools.nvim",
+ dependencies = {
+ "nvim-lua/plenary.nvim",
+ "stevearc/dressing.nvim",
+ },
+ config = function()
+ require("flutter-tools").setup({
+ debugger = {
+ enabled = true,
+ run_via_dap = true,
+ },
+ })
+ end,
+ ft = "dart",
+ },
+ {
+ "iamcco/markdown-preview.nvim",
+ build = "cd app && npm install",
+ ft = "markdown",
+ config = function()
+ vim.g.mkdp_filetypes = { "markdown" }
+ vim.cmd("let g:mkdp_auto_close = 0")
+ end,
+ cmd = "MarkdownPreview",
+ },
+ {
+ "ellisonleao/glow.nvim",
+ config = function()
+ local glow_path = vim.fn.executable("~/.local/bin/glow") == 1 and "~/.local/bin/glow" or "/usr/bin/glow"
+ require("glow").setup({
+ style = "dark",
+ glow_path = glow_path,
+ })
+ end,
+ ft = "markdown",
+ },
+
+ -- Debugging
+ { "mfussenegger/nvim-dap", event = "VeryLazy" },
+ { "rcarriga/nvim-dap-ui", dependencies = { "mfussenegger/nvim-dap" }, cmd = "DapUI" },
+ { "theHamsta/nvim-dap-virtual-text", dependencies = { "mfussenegger/nvim-dap" } },
+ { "gabrielpoca/replacer.nvim", cmd = "Replacer" },
+ { "jayp0521/mason-nvim-dap.nvim", dependencies = { "mason-org/mason.nvim" } },
+
+ -- Misc
+ { "rmagatti/auto-session", event = "VimEnter" },
+ { "tpope/vim-sleuth", lazy = true },
+ { "michaelb/sniprun", build = "bash ./install.sh", cmd = "SnipRun" },
+ { "stevearc/overseer.nvim", cmd = "Overseer" },
+ {
+ "nvim-neotest/neotest",
+ dependencies = {
+ "nvim-neotest/neotest-python",
+ "nvim-neotest/neotest-plenary",
+ "nvim-neotest/neotest-vim-test",
+ "nvim-neotest/nvim-nio",
+ },
+ cmd = "Neotest",
+ },
+ { "kawre/leetcode.nvim", cmd = "Leetcode" },
+ { "m4xshen/hardtime.nvim", lazy = true },
+
+ -- LaTeX
+ { "lervag/vimtex", ft = "tex" },
+}
+
+-- Helper function to detect current manager
+local function detect_current_manager()
+ -- Check if we're currently using lazy (by checking if lazy module exists)
+ if package.loaded["lazy"] or package.loaded["lazy.core.util"] then
+ return "lazy"
+ end
+
+ -- Check if we're currently using packer
+ if package.loaded["packer"] then
+ return "packer"
+ end
+
+ -- Check for builtin manager
+ if vim.plugins and vim.plugins.spec then
+ return "builtin"
+ end
+
+ return "unknown"
+end
+
+local function format_for_lazy(plugin)
+ -- Lazy.nvim's format is the closest to our universal format, so we can
+ -- largely just copy the table, with some specific adjustments.
+ local new_plugin = vim.deepcopy(plugin)
+
+ -- Lazy.nvim uses `dependencies` key for dependencies, not `requires`
+ if new_plugin.requires then
+ new_plugin.dependencies = new_plugin.requires
+ new_plugin.requires = nil
+ end
+
+ -- Change 'as' to 'name' for lazy
+ if new_plugin.as then
+ new_plugin.name = new_plugin.as
+ new_plugin.as = nil
+ end
+
+ -- Change 'run' to 'build' for lazy
+ if new_plugin.run then
+ new_plugin.build = new_plugin.run
+ new_plugin.run = nil
+ end
+
+ return new_plugin
+end
+
+local function format_for_packer(plugin)
+ -- For Packer, we need to remove lazy-loading keys to force eager loading
+ local new_plugin = vim.deepcopy(plugin)
+
+ -- Convert dependencies back to requires for packer
+ if new_plugin.dependencies then
+ new_plugin.requires = new_plugin.dependencies
+ new_plugin.dependencies = nil
+ end
+
+ -- Convert name back to as for packer
+ if new_plugin.name then
+ new_plugin.as = new_plugin.name
+ new_plugin.name = nil
+ end
+
+ -- Convert build back to run for packer
+ if new_plugin.build then
+ new_plugin.run = new_plugin.build
+ new_plugin.build = nil
+ end
+
+ -- Remove lazy-loading keys to force eager loading in packer
+ new_plugin.event = nil
+ new_plugin.keys = nil
+ new_plugin.cmd = nil
+ new_plugin.ft = nil
+ new_plugin.lazy = nil
+
+ return new_plugin
+end
+
+local function format_for_builtin(plugin)
+ -- This function is now simplified, as the main loop handles flattening
+ local new_plugin = vim.deepcopy(plugin)
+
+ -- Convert GitHub shorthand to full URL if needed
+ if type(new_plugin) == "string" then
+ if new_plugin:match("^[%w%-_%.]+/[%w%-_%.]+$") then
+ return {
+ src = "https://github.com/" .. new_plugin,
+ name = new_plugin:match("/([%w%-_%.]+)$")
+ }
+ else
+ return { src = new_plugin }
+ end
+ end
+
+ -- Handle table format
+ if new_plugin[1] and type(new_plugin[1]) == "string" then
+ local repo = new_plugin[1]
+ if repo:match("^[%w%-_%.]+/[%w%-_%.]+$") then
+ new_plugin.src = "https://github.com/" .. repo
+ new_plugin.name = new_plugin.name or new_plugin.as or repo:match("/([%w%-_%.]+)$")
+ else
+ new_plugin.src = repo
+ end
+ new_plugin[1] = nil -- Remove positional argument
+ end
+
+ -- Convert url to src if present
+ if new_plugin.url then
+ new_plugin.src = new_plugin.url
+ new_plugin.url = nil
+ end
+
+ -- Convert 'as' to 'name'
+ if new_plugin.as then
+ new_plugin.name = new_plugin.as
+ new_plugin.as = nil
+ end
+
+ -- Only keep the keys that vim.pack uses: src, name, and version
+ new_plugin.dependencies = nil
+ new_plugin.config = nil
+ new_plugin.build = nil
+ new_plugin.run = nil
+ new_plugin.cond = nil
+ new_plugin.min_version = nil
+ new_plugin.max_version = nil
+ new_plugin.lazy = nil
+ new_plugin.priority = nil
+ new_plugin.event = nil
+ new_plugin.keys = nil
+ new_plugin.cmd = nil
+ new_plugin.ft = nil
+ new_plugin.requires = nil
+
+ return new_plugin
+end
+
+-- Detect which manager is currently active and format plugins accordingly
+local current_manager = detect_current_manager()
+local plugins_to_process = {}
+local processed_plugins = {} -- Use a set to avoid duplicates
+
+-- Flatten the plugin list for the builtin manager
+if current_manager == "builtin" then
+ local function get_plugin_name(plugin)
+ if type(plugin) == "string" then
+ return plugin:match("/([%w%-_%.]+)$") or plugin
+ elseif type(plugin) == "table" then
+ -- Get name from 'name', 'as', or from the src/url
+ return plugin.name or plugin.as or (type(plugin[1]) == "string" and plugin[1]:match("/([%w%-_%.]+)$")) or
+ plugin.url:match("/([%w%-_%.]+)$")
+ end
+ end
+
+ local function add_to_process(plugin)
+ local name = get_plugin_name(plugin)
+ if name and not processed_plugins[name] then
+ table.insert(plugins_to_process, plugin)
+ processed_plugins[name] = true
+ end
+ end
+
+ for _, plugin in ipairs(universal_plugins) do
+ add_to_process(plugin)
+ if plugin.dependencies then
+ for _, dep in ipairs(plugin.dependencies) do
+ add_to_process(dep)
+ end
+ end
+ if plugin.requires then
+ for _, req in ipairs(plugin.requires) do
+ add_to_process(req)
+ end
+ end
+ end
+else
+ plugins_to_process = universal_plugins
+end
+
+local finalized_plugins = {}
+
+for _, plugin in ipairs(plugins_to_process) do
+ local cond_ok = true
+
+ -- Check for the new 'exclude' option first
+ if plugin.exclude and contains(plugin.exclude, current_manager) then
+ cond_ok = false
+ end
+
+ if cond_ok and (plugin.min_version or plugin.max_version) then
+ cond_ok = should_load_plugin(plugin.min_version, plugin.max_version)
+ end
+ if cond_ok and plugin.cond then
+ cond_ok = plugin.cond()
+ end
+
+ if cond_ok then
+ local new_plugin
+ if current_manager == "lazy" then
+ new_plugin = format_for_lazy(plugin)
+ elseif current_manager == "packer" then
+ new_plugin = format_for_packer(plugin)
+ elseif current_manager == "builtin" then
+ new_plugin = format_for_builtin(plugin)
+ else
+ -- Default to lazy format if manager is unknown
+ new_plugin = format_for_lazy(plugin)
+ end
+ table.insert(finalized_plugins, new_plugin)
+ end
+end
+
+return finalized_plugins
diff --git a/common/nvim/lua/user/keys.lua b/common/nvim/lua/user/keys.lua
new file mode 100755
index 0000000..63b64fa
--- /dev/null
+++ b/common/nvim/lua/user/keys.lua
@@ -0,0 +1,928 @@
+-- ============================================================================
+-- Key Mappings
+-- ============================================================================
+
+local map = function(mode, l, r, opts)
+ if r == nil then
+ vim.notify("Attempted to map key '" .. l .. "' but RHS is nil", vim.log.levels.WARN)
+ return
+ end
+ opts = vim.tbl_extend('force', {
+ silent = true,
+ noremap = true
+ }, opts or {})
+ vim.keymap.set(mode, l, r, opts)
+end
+
+-- Leader key
+vim.g.mapleader = ";"
+vim.g.maplocalleader = "\\"
+
+-- Tmux/Vim navigation
+local function smart_move(direction, tmux_cmd)
+ local curwin = vim.api.nvim_get_current_win()
+ vim.cmd('wincmd ' .. direction)
+ if curwin == vim.api.nvim_get_current_win() then
+ vim.fn.system('tmux select-pane ' .. tmux_cmd)
+ end
+end
+
+-- Window Navigation
+map('n', '<C-h>', function() smart_move('h', '-L') end)
+map('n', '<C-j>', function() smart_move('j', '-D') end)
+map('n', '<C-k>', function() smart_move('k', '-U') end)
+map('n', '<C-l>', function() smart_move('l', '-R') end)
+
+-- Buffer Navigation
+map('n', '<leader>bn', '<cmd>bnext<CR>')
+map('n', '<leader>bp', '<cmd>bprevious<CR>')
+--map('n', '<leader>bd', '<cmd>bdelete<CR>')
+map('n', '<leader>ba', '<cmd>%bdelete<CR>')
+
+
+
+-- Get list of loaded buffers in order
+local function get_buffers()
+ local bufs = {}
+ for _, buf in ipairs(vim.api.nvim_list_bufs()) do
+ if vim.api.nvim_buf_is_loaded(buf) then
+ table.insert(bufs, buf)
+ end
+ end
+ return bufs
+end
+
+-- Swap two buffers by index in the buffer list
+local function swap_buffers(idx1, idx2)
+ local bufs = get_buffers()
+ local buf1 = bufs[idx1]
+ local buf2 = bufs[idx2]
+ if not buf1 or not buf2 then return end
+ local name1 = vim.api.nvim_buf_get_name(buf1)
+ local name2 = vim.api.nvim_buf_get_name(buf2)
+ vim.cmd("b " .. buf1)
+ vim.cmd("file " .. name2)
+ vim.cmd("b " .. buf2)
+ vim.cmd("file " .. name1)
+end
+
+-- Move current buffer left
+vim.keymap.set("n", "<leader>bh", function()
+ local bufs = get_buffers()
+ local curr = vim.api.nvim_get_current_buf()
+ local idx
+ for i, b in ipairs(bufs) do if b == curr then idx = i break end end
+ if idx and idx > 1 then
+ swap_buffers(idx, idx-1)
+ end
+end, { noremap = true, silent = true })
+
+-- Move current buffer right
+vim.keymap.set("n", "<leader>bl", function()
+ local bufs = get_buffers()
+ local curr = vim.api.nvim_get_current_buf()
+ local idx
+ for i, b in ipairs(bufs) do if b == curr then idx = i break end end
+ if idx and idx < #bufs then
+ swap_buffers(idx, idx+1)
+ end
+end, { noremap = true, silent = true })
+-- Save and Quit
+map('n', '<leader>w', '<cmd>w<CR>')
+map('n', '<leader>q', '<cmd>q<CR>')
+map('n', '<leader>wq', '<cmd>wq<CR>')
+map('n', '<leader>Q', '<cmd>qa!<CR>')
+
+-- Resize Windows
+map('n', '<M-Up>', '<cmd>resize -2<CR>')
+map('n', '<M-Down>', '<cmd>resize +2<CR>')
+map('n', '<M-Left>', '<cmd>vertical resize -2<CR>')
+map('n', '<M-Right>', '<cmd>vertical resize +2<CR>')
+
+-- Quickfix and Location List
+map('n', ']q', '<cmd>cnext<CR>zz')
+map('n', '[q', '<cmd>cprev<CR>zz')
+map('n', ']l', '<cmd>lnext<CR>zz')
+map('n', '[l', '<cmd>lprev<CR>zz')
+
+-- Terminal Mode
+map('t', '<Esc>', '<C-\\><C-n>')
+map('t', '<C-h>', '<C-\\><C-n><C-w>h')
+map('t', '<C-j>', '<C-\\><C-n><C-w>j')
+map('t', '<C-k>', '<C-\\><C-n><C-w>k')
+map('t', '<C-l>', '<C-\\><C-n><C-w>l')
+
+-- Insert mode escape
+map('i', 'jk', '<ESC>')
+
+-- Tmux/(n)vim navigation
+local function smart_move(direction, tmux_cmd)
+ local curwin = vim.api.nvim_get_current_win()
+ vim.cmd('wincmd ' .. direction)
+ if curwin == vim.api.nvim_get_current_win() then
+ vim.fn.system('tmux select-pane ' .. tmux_cmd)
+ end
+end
+
+map('n', '<C-h>', function() smart_move('h', '-L') end, {silent = true})
+map('n', '<C-j>', function() smart_move('j', '-D') end, {silent = true})
+map('n', '<C-k>', function() smart_move('k', '-U') end, {silent = true})
+map('n', '<C-l>', function() smart_move('l', '-R') end, {silent = true})
+
+
+-- Jump to next match on line using `.` instead of `;` NOTE: commented out in favour of "ggandor/flit.nvim"
+--map("n", ".", ";")
+
+-- Repeat last command using `<Space>` instead of `.` NOTE: commented out in favour of "ggandor/flit.nvim"
+--map("n", "<Space>", ".")
+
+-- Reload nvim config
+map("n", "<leader><CR>",
+"<cmd>luafile ~/.config/nvim/init.lua<CR> | :echom ('Nvim config loading...') | :sl! | echo ('')<CR>")
+
+vim.keymap.set("t", "<Esc>", "<C-\\><C-n>", { desc = "Exit terminal mode" })
+vim.keymap.set("t", "<C-b>", "<C-\\><C-n>", { desc = "Exit terminal mode" })
+
+--------------- Extended Operations ---------------
+-- Conditional 'q' to quit on floating/quickfix/help windows otherwise still use it for macros
+-- TODO: Have a list of if available on system/packages, example "Zen Mode" to not work on it (quit Zen Mode)
+map("n", "q", function()
+ local config = vim.api.nvim_win_get_config(0)
+ if config.relative ~= "" then -- is_floating_window?
+ return ":silent! close!<CR>"
+ elseif vim.o.buftype == "quickfix" then
+ return ":quit<CR>"
+ elseif vim.o.buftype == "help" then
+ return ":close<CR>"
+ else
+ return "q"
+ end
+end, { expr = true, replace_keycodes = true })
+
+-- Minimalist Tab Completion
+map("i", "<Tab>", function()
+ local col = vim.fn.col('.') - 1
+ local line = vim.fn.getline('.')
+ local prev_char = line:sub(col, col)
+ if vim.fn.pumvisible() == 1 or prev_char:match("%w") then
+ return vim.api.nvim_replace_termcodes("<C-n>", true, true, true)
+ else
+ return vim.api.nvim_replace_termcodes("<Tab>", true, true, true)
+ end
+end, { expr = true })
+
+-- Shift-Tab for reverse completion
+map("i", "<S-Tab>", function()
+ if vim.fn.pumvisible() == 1 then
+ return vim.api.nvim_replace_termcodes("<C-p>", true, true, true)
+ else
+ return vim.api.nvim_replace_termcodes("<S-Tab>", true, true, true)
+ end
+end, { expr = true })
+
+
+-- Toggle completion
+map("n", "<Leader>tc", ':lua require("user.mods").toggle_completion()<CR>')
+
+-- Minimalist Auto Completion
+map("i", "<CR>", function()
+ -- Exit this keymap if nvim-cmp is present
+ local cmp_is_present, _ = pcall(require, "cmp")
+ if cmp_is_present and require("cmp").visible() then
+ return vim.api.nvim_replace_termcodes("<C-y>", true, true, true)
+ elseif cmp_is_present then
+ return vim.api.nvim_replace_termcodes("<CR>", true, true, true)
+ end
+
+ -- when cmp is NOT present
+ if vim.fn.pumvisible() == 1 then
+ return vim.api.nvim_replace_termcodes("<C-y>", true, true, true)
+ else
+ return vim.api.nvim_replace_termcodes("<CR>", true, true, true)
+ end
+end, { expr = true })
+
+-- Closing compaction in insert mode
+map("i", "[", "[]<Left>")
+map("i", "(", "()<Left>")
+map("i", "{", "{}<Left>")
+map("i", "/*", "/**/<Left><Left>")
+
+-- Edit new file
+map("n", "<leader>e", [[:e <C-R>=expand("%:h")..'/'<CR>]], { noremap = true, silent = true, desc = "New file" })
+
+-- Write as sudo
+map("c", "W!", "exe 'w !sudo tee >/dev/null %:p:S' | setl nomod", { silent = true, desc = "Write as Sudo" })
+
+-- Don't format on save
+map("c", "F!", ":noautocmd w<CR>")
+
+-- Combine buffers list with buffer name
+map("n", "<Leader>b", ":buffers<CR>:buffer<Space>")
+
+-- Buffer confirmation
+map("n", "<leader>y", ":BufferPick<CR>")
+
+-- Map buffer next, prev and delete to <leader>+(n/p/d) respectively and tab/s-tab
+map("n", "<leader>n", ":bn<cr>")
+map("n", "<leader>p", ":bp<cr>")
+map("n", "<leader>d", ":bd<cr>")
+map("n", "<TAB>", ":bnext<CR>")
+map("n", "<S-TAB>", ":bprevious<CR>")
+
+-- Close all buffers and reopen last one
+map("n", "<leader>D", ":update | %bdelete | edit # | normal `<CR>")
+
+-- Delete file of current buffer
+map("n", "<leader>rm", "<CMD>call delete(expand('%')) | bdelete!<CR>")
+
+-- List marks
+map("n", "<Leader>M", ":marks<CR>")
+
+-- Messages
+map("n", "<Leader>m", ":messages<CR>")
+
+--- Clear messages or just refresh/redraw the screen
+map("n", "<leader>i", function()
+ local ok, notify = pcall(require, "notify")
+ if ok then
+ notify.dismiss()
+ end
+end)
+
+-- Toggle set number
+map("n", "<leader>$", ":NumbersToggle<CR>")
+map("n", "<leader>%", ":NumbersOnOff<CR>")
+
+-- Easier split navigations, just ctrl-j instead of ctrl-w then j
+map("t", "<C-[>", "<C-\\><C-N>")
+map("t", "<C-h>", "<C-\\><C-N><C-h>")
+map("t", "<C-j>", "<C-\\><C-N><C-j>")
+map("t", "<C-k>", "<C-\\><C-N><C-k>")
+map("t", "<C-l>", "<C-\\><C-N><C-l>")
+
+-- Split window
+map("n", "<leader>-", ":split<CR>")
+map("n", "<leader>\\", ":vsplit<CR>")
+
+-- Close window
+--map("n", "<leader>c", "<C-w>c")
+map({ "n", "t", "c" }, "<leader>c", function()
+ local winid = vim.api.nvim_get_current_win()
+ local config = vim.api.nvim_win_get_config(winid)
+
+ if config.relative ~= "" then
+ -- This is a floating window
+ vim.cmd("CloseFloatingWindows")
+ else
+ -- Not a float/close window
+ vim.cmd("close")
+ end
+end, { desc = "Close current float or all floating windows" })
+
+-- Resize Panes
+map("n", "<Leader><", ":vertical resize +5<CR>")
+map("n", "<Leader>>", ":vertical resize -5<CR>")
+map("n", "<Leader>=", "<C-w>=")
+
+-- Mapping for left and right arrow keys in command-line mode
+vim.api.nvim_set_keymap("c", "<A-h>", "<Left>", { noremap = true, silent = false }) -- Left Arrow
+vim.api.nvim_set_keymap("c", "<A-l>", "<Right>", { noremap = true, silent = false }) -- Right Arrow
+
+-- Map Alt+(h/j/k/l) in insert(include terminal/command) mode to move directional
+map({ "i", "t" }, "<A-h>", "<Left>")
+map({ "i", "t" }, "<A-j>", "<Down>")
+map({ "i", "t" }, "<A-k>", "<Up>")
+map({ "i", "t" }, "<A-l>", "<Right>")
+
+-- Create tab, edit and move between them
+map("n", "<C-T>n", ":tabnew<CR>")
+map("n", "<C-T>e", ":tabedit")
+map("n", "<leader>[", ":tabprev<CR>")
+map("n", "<leader>]", ":tabnext<CR>")
+
+-- Vim TABs
+map("n", "<leader>1", "1gt<CR>")
+map("n", "<leader>2", "2gt<CR>")
+map("n", "<leader>3", "3gt<CR>")
+map("n", "<leader>4", "4gt<CR>")
+map("n", "<leader>5", "5gt<CR>")
+map("n", "<leader>6", "6gt<CR>")
+map("n", "<leader>7", "7gt<CR>")
+map("n", "<leader>8", "8gt<CR>")
+map("n", "<leader>9", "9gt<CR>")
+map("n", "<leader>0", "10gt<CR>")
+
+-- Hitting ESC when inside a terminal to get into normal mode
+--map("t", "<Esc>", [[<C-\><C-N>]])
+
+-- Move block (indentation) easily
+--map("n", "<", "<<", term_opts)
+--map("n", ">", ">>", term_opts)
+--map("x", "<", "<gv", term_opts)
+--map("x", ">", ">gv", term_opts)
+--map("v", "<", "<gv")
+--map("v", ">", ">gv")
+--map("n", "<", "<S-v><<esc>change mode to normal")
+--map("n", ">", "<S-v>><esc>change mode to normal")
+
+-- Visual mode: Indent and reselect the visual area, like default behavior but explicit
+map("v", "<", "<gv", { desc = "Indent left and reselect" })
+map("v", ">", ">gv", { desc = "Indent right and reselect" })
+
+-- Normal mode: Indent current line and enter Visual Line mode to repeat easily
+map("n", "<", "v<<", { desc = "Indent left and select" })
+map("n", ">", "v>>", { desc = "Indent right and select" })
+
+---- Visual mode: Indent and reselect the visual area, like default behavior but explicit
+--map("v", "<", "<", { desc = "Indent left" })
+--map("v", ">", ">", { desc = "Indent right" })
+--
+---- Normal mode: Indent current line and enter Visual Line mode to repeat easily
+--map("n", "<", "v<<", { desc = "Indent left and select" })
+--map("n", ">", "v>>", { desc = "Indent right and select" })
+
+-- Set alt+(j/k) to switch lines of texts or simply move them
+map("n", "<A-k>", ':let save_a=@a<Cr><Up>"add"ap<Up>:let @a=save_a<Cr>')
+map("n", "<A-j>", ':let save_a=@a<Cr>"add"ap:let @a=save_a<Cr>')
+
+-- Toggle Diff
+map("n", "<leader>df", "<Cmd>call utils#ToggleDiff()<CR>")
+
+-- Toggle Verbose
+map("n", "<leader>uvt", "<Cmd>call utils#VerboseToggle()<CR>")
+
+-- Jump List
+map("n", "<leader>j", "<Cmd>call utils#GotoJump()<CR>")
+
+-- Rename file
+map("n", "<leader>rf", "<Cmd>call utils#RenameFile()<CR>")
+
+-- Map delete to Ctrl+l
+map("i", "<C-l>", "<Del>")
+
+-- Clear screen
+map("n", "<leader><C-l>", "<Cmd>!clear<CR>")
+
+-- Change file to an executable
+map("n", "<Leader>x",
+":lua require('user.mods').Toggle_executable()<CR> | :echom ('Toggle executable')<CR> | :sl! | echo ('')<CR>")
+-- map("n", "<leader>x", ":!chmod +x %<CR>")
+
+vim.keymap.set("n", "<leader>cm", function()
+ vim.cmd("redir @+")
+ vim.cmd("silent messages")
+ vim.cmd("redir END")
+ vim.notify("Copied :messages to clipboard")
+end, { desc = "Copy :messages to clipboard" })
+
+-- Paste without replace clipboard
+map("v", "p", '"_dP')
+
+map("n", "]p", 'm`o<Esc>"+p``', opts)
+
+map("n", "[p", 'm`O<Esc>"+p``', opts)
+
+-- Bind Ctrl-V to paste in insert/normal/command mode
+map("i", "<C-v>", "<C-G>u<C-R><C-P>+", opts)
+map("n", "<C-v>", '"+p', { noremap = true, silent = true })
+vim.api.nvim_set_keymap("c", "<C-v>", "<C-R>=getreg('+')<CR><BS>", { noremap = true, silent = false })
+
+-- Change Working Directory to current project
+map("n", "<leader>cd", ":cd %:p:h<CR>:pwd<CR>")
+
+-- Search and replace
+map("v", "<leader>sr", 'y:%s/<C-r><C-r>"//g<Left><Left>c')
+
+-- Substitute globally and locally in the selected region.
+map("n", "<leader>s", ":%s//g<Left><Left>")
+map("v", "<leader>s", ":s//g<Left><Left>")
+
+-- Set line wrap
+map("n", "<M-z>", function()
+ local wrap_status = vim.api.nvim_exec("set wrap ?", true)
+
+ if wrap_status == "nowrap" then
+ vim.api.nvim_command("set wrap linebreak")
+ print("Wrap enabled")
+ else
+ vim.api.nvim_command("set wrap nowrap")
+ print("Wrap disabled")
+ end
+end, { silent = true })
+
+-- Toggle between folds
+map("n", "<Space>", "&foldlevel ? 'zM' : 'zR'", { expr = true })
+
+-- Use space to toggle fold
+--map("n", "<Space>", "za")
+
+map("n", "<leader>.b", ":!cp % %.backup<CR>")
+
+-- Go to next window
+map("n", "<leader>wn", "<C-w>w", { desc = "Next window" })
+
+-- Go to previous window
+map("n", "<leader>wp", "<C-w>W", { desc = "Previous window" })
+
+-- Toggle transparency
+map("n", "<leader>tb", ":call utils#Toggle_transparent_background()<CR>")
+
+-- Toggle zoom
+map("n", "<leader>z", ":call utils#ZoomToggle()<CR>")
+map("n", "<C-w>z", "<C-w>|<C-w>_")
+
+-- Toggle statusline
+map("n", "<leader>sl", ":call utils#ToggleHiddenAll()<CR>")
+
+-- Open last closed buffer
+map("n", "<C-t>", ":call utils#OpenLastClosed()<CR>")
+
+
+-- Automatically set LSP keymaps when LSP attaches to a buffer
+--vim.api.nvim_create_autocmd("LspAttach", {
+-- callback = function(args)
+-- local bufnr = args.buf
+-- local opts = { buffer = bufnr }
+-- map("n", "K", vim.lsp.buf.hover)
+-- map("n", "gd", "<cmd>lua require('goto-preview').goto_preview_definition()<CR>")
+-- map("n", "gi", "<cmd>lua require('goto-preview').goto_preview_implementation()<CR>")
+-- map("n", "gr", "<cmd>lua require('goto-preview').goto_preview_references()<CR>")
+-- map("n", "gD", vim.lsp.buf.declaration)
+-- map("n", "<leader>k", vim.lsp.buf.signature_help)
+-- map("n", "gt", "<cmd>lua require('goto-preview').goto_preview_type_definition()<CR>")
+-- map("n", "gn", vim.lsp.buf.rename)
+-- map("n", "ga", vim.lsp.buf.code_action)
+-- map("n", "gf", function() vim.lsp.buf.format({ async = true }) end)
+-- map("n", "go", vim.diagnostic.open_float)
+-- map("n", "<leader>go", ":call utils#ToggleDiagnosticsOpenFloat()<CR> | :echom ('Toggle Diagnostics Float open/close...')<CR> | :sl! | echo ('')<CR>")
+-- map("n", "gq", vim.diagnostic.setloclist)
+-- map("n", "[d", vim.diagnostic.goto_prev)
+-- map("n", "]d", vim.diagnostic.goto_next)
+-- map("n", "gs", vim.lsp.buf.document_symbol)
+-- map("n", "gw", vim.lsp.buf.workspace_symbol)
+-- map("n", "<leader>wa", vim.lsp.buf.add_workspace_folder)
+-- map("n", "<leader>wr", vim.lsp.buf.remove_workspace_folder)
+-- map("n", "<leader>wl", function()
+-- print(vim.inspect(vim.lsp.buf.list_workspace_folders()))
+-- end)
+-- end,
+--})
+
+---- LSP Global Keymaps (available in all buffers)
+--map("n", "[d", vim.diagnostic.goto_prev, { desc = "LSP: Previous Diagnostic" })
+--map("n", "]d", vim.diagnostic.goto_next, { desc = "LSP: Next Diagnostic" })
+--map("n", "go", vim.diagnostic.open_float, { desc = "LSP: Open Diagnostic Float" })
+--
+---- LSP Buffer-local keymaps function (to be called from LSP on_attach)
+--_G.setup_lsp_keymaps = function(bufnr)
+-- local bmap = function(mode, l, r, opts)
+-- opts = opts or {}
+-- opts.silent = true
+-- opts.noremap = true
+-- opts.buffer = bufnr
+-- vim.keymap.set(mode, l, r, opts)
+-- end
+--
+-- bmap("n", "K", vim.lsp.buf.hover, { desc = "LSP: Hover Documentation" })
+-- bmap("n", "gd", vim.lsp.buf.definition, { desc = "LSP: Go to Definition" })
+-- bmap("n", "gD", vim.lsp.buf.declaration, { desc = "LSP: Go to Declaration" })
+-- bmap("n", "gi", vim.lsp.buf.implementation, { desc = "LSP: Go to Implementation" })
+-- bmap("n", "gt", vim.lsp.buf.type_definition, { desc = "LSP: Go to Type Definition" })
+-- bmap("n", "gr", vim.lsp.buf.references, { desc = "LSP: Go to References" })
+-- bmap("n", "gn", vim.lsp.buf.rename, { desc = "LSP: Rename" })
+-- bmap("n", "ga", vim.lsp.buf.code_action, { desc = "LSP: Code Action" })
+-- bmap("n", "<leader>k", vim.lsp.buf.signature_help, { desc = "LSP: Signature Help" })
+-- bmap("n", "gs", vim.lsp.buf.document_symbol, { desc = "LSP: Document Symbols" })
+--end
+
+-- LSP Global Keymaps (available in all buffers)
+map("n", "[d", vim.diagnostic.goto_prev, { desc = "LSP: Previous Diagnostic" })
+map("n", "]d", vim.diagnostic.goto_next, { desc = "LSP: Next Diagnostic" })
+map("n", "go", vim.diagnostic.open_float, { desc = "LSP: Open Diagnostic Float" })
+map("n", "<leader>go", ":call utils#ToggleDiagnosticsOpenFloat()<CR> | :echom ('Toggle Diagnostics Float open/close...')<CR> | :sl! | echo ('')<CR>")
+
+-- LSP Buffer-local keymaps function (to be called from LSP on_attach)
+_G.setup_lsp_keymaps = function(bufnr)
+ local bmap = function(mode, l, r, opts)
+ opts = opts or {}
+ opts.silent = true
+ opts.noremap = true
+ opts.buffer = bufnr
+ vim.keymap.set(mode, l, r, opts)
+ end
+
+ -- Your preferred keybindings
+ bmap("n", "K", function()
+ vim.lsp.buf.hover { border = "single", max_height = 25, max_width = 120 }
+ end, { desc = "LSP: Hover Documentation" })
+
+ bmap("n", "gd", function()
+ vim.lsp.buf.definition {
+ on_list = function(options)
+ -- Custom logic to avoid showing multiple definitions for Lua patterns like:
+ -- `local M.my_fn_name = function() ... end`
+ local unique_defs = {}
+ local def_loc_hash = {}
+
+ for _, def_location in pairs(options.items) do
+ local hash_key = def_location.filename .. def_location.lnum
+ if not def_loc_hash[hash_key] then
+ def_loc_hash[hash_key] = true
+ table.insert(unique_defs, def_location)
+ end
+ end
+
+ options.items = unique_defs
+ vim.fn.setloclist(0, {}, " ", options)
+
+ -- Open location list if multiple definitions, otherwise jump directly
+ if #options.items > 1 then
+ vim.cmd.lopen()
+ else
+ vim.cmd([[silent! lfirst]])
+ end
+ end,
+ }
+ end, { desc = "LSP: Go to Definition" })
+
+ bmap("n", "<C-]>", vim.lsp.buf.definition, { desc = "LSP: Go to Definition (Alt)" })
+ bmap("n", "gD", vim.lsp.buf.declaration, { desc = "LSP: Go to Declaration" })
+ bmap("n", "gi", vim.lsp.buf.implementation, { desc = "LSP: Go to Implementation" })
+ bmap("n", "gt", vim.lsp.buf.type_definition, { desc = "LSP: Go to Type Definition" })
+ bmap("n", "gr", vim.lsp.buf.references, { desc = "LSP: Go to References" })
+ bmap("n", "gn", vim.lsp.buf.rename, { desc = "LSP: Rename" })
+ bmap("n", "<leader>rn", vim.lsp.buf.rename, { desc = "LSP: Rename (Alt)" })
+ bmap("n", "ga", vim.lsp.buf.code_action, { desc = "LSP: Code Action" })
+ bmap("n", "<leader>ca", vim.lsp.buf.code_action, { desc = "LSP: Code Action (Alt)" })
+ bmap("n", "<leader>k", vim.lsp.buf.signature_help, { desc = "LSP: Signature Help" })
+ bmap("n", "<C-k>", vim.lsp.buf.signature_help, { desc = "LSP: Signature Help (Alt)" })
+ bmap("n", "gs", vim.lsp.buf.document_symbol, { desc = "LSP: Document Symbols" })
+
+ -- Workspace folder management
+ bmap("n", "<leader>wa", vim.lsp.buf.add_workspace_folder, { desc = "LSP: Add Workspace Folder" })
+ bmap("n", "<leader>wr", vim.lsp.buf.remove_workspace_folder, { desc = "LSP: Remove Workspace Folder" })
+ bmap("n", "<leader>wl", function()
+ vim.print(vim.lsp.buf.list_workspace_folders())
+ end, { desc = "LSP: List Workspace Folders" })
+end
+
+---------------- Plugin Operations ----------------
+-- Packer
+map("n", "<leader>Pc", "<cmd>PackerCompile<cr>")
+map("n", "<leader>Pi", "<cmd>PackerInstall<cr>")
+map("n", "<leader>Ps", "<cmd>PackerSync<cr>")
+map("n", "<leader>PS", "<cmd>PackerStatus<cr>")
+map("n", "<leader>Pu", "<cmd>PackerUpdate<cr>")
+
+-- ToggleTerm
+map({ "n", "t" }, "<leader>tt", "<cmd>ToggleTerm<CR>")
+map({ "n", "t" }, "<leader>th", "<cmd>lua Horizontal_term_toggle()<CR>")
+map({ "n", "t" }, "<leader>tv", "<cmd>lua Vertical_term_toggle()<CR>")
+
+-- LazyGit
+map({ "n", "t" }, "<leader>gg", "<cmd>lua Lazygit_toggle()<CR>")
+
+map("n", "<leader>tg", "<cmd>lua Gh_dash()<CR>")
+
+-- Fugitive git bindings
+map("n", "<leader>gs", vim.cmd.Git)
+map("n", "<leader>ga", ":Git add %:p<CR><CR>")
+--map("n", "<leader>gs", ":Gstatus<CR>")
+--map("n", "<leader>gc", ":Gcommit -v -q<CR>")
+map("n", "<leader>gt", ":Gcommit -v -q %:p<CR>")
+map("n", "<leader>gd", ":Gdiff<CR>")
+map("n", "<leader>ge", ":Gedit<CR>")
+--map("n", "<leader>gr", ":Gread<Cj>")
+map("n", "<leader>gw", ":Gwrite<CR><CR>")
+map("n", "<leader>gl", ":silent! Glog<CR>:bot copen<CR>")
+--map("n", "<leader>gp", ":Ggrep<Space>")
+--map("n", "<Leader>gp", ":Git push<CR>")
+--map("n", "<Leader>gb", ":Gblame<CR>")
+map("n", "<leader>gm", ":Gmove<Space>")
+--map("n", "<leader>gb", ":Git branch<Space>")
+--map("n", "<leader>go", ":Git checkout<Space>")
+--map("n", "<leader>gps", ":Dispatch! git push<CR>")
+--map("n", "<leader>gpl", ":Dispatch! git pull<CR>")
+
+-- Telescope
+-- Safe load of your custom Telescope module
+-- This initial pcall for "plugins.telescope" is fine because it just checks if YOUR module is there.
+-- The actual checks for Telescope's core modules happen *inside* your wrapper functions when called.
+local telescope_ok, telescope_module = pcall(require, "plugins.telescope")
+
+if telescope_ok and telescope_module then
+
+ -- Direct function calls from your plugins.telescope module
+ -- M.safe_find_files handles its own internal `builtin` check
+ map("n", "<leader>ff", telescope_module.safe_find_files, { desc = "Find files" })
+
+ -- For `find all files`, use your `safe_telescope_builtin` wrapper
+ -- You need to wrap it in a function to pass the options correctly.
+ map("n", "<leader>f.", function()
+ telescope_module.safe_telescope_builtin("find_files")({ hidden = true, no_ignore = true })
+ end, { desc = "Find all files" })
+
+
+ ---
+ --- Built-in Telescope functions
+ --- Note: safe_telescope_builtin returns a function, so you map directly to it.
+ ---
+ map("n", "<leader>fg", function() telescope_module.safe_telescope_builtin("live_grep")() end, { desc = "Live grep" })
+ map("n", "<leader>fb", function() telescope_module.safe_telescope_builtin("buffers")() end, { desc = "Find buffers" })
+ map("n", "<leader>fh", function() telescope_module.safe_telescope_builtin("help_tags")() end, { desc = "Help tags" })
+ map("n", "<leader>fc", function() telescope_module.safe_telescope_builtin("commands")() end, { desc = "Commands" })
+ map("n", "<leader>fd", function() telescope_module.safe_telescope_builtin("diagnostics")() end, { desc = "Diagnostics" })
+ map("n", "<leader>fk", function() telescope_module.safe_telescope_builtin("keymaps")() end, { desc = "Keymaps" })
+ map("n", "<leader>fr", function() telescope_module.safe_telescope_builtin("registers")() end, { desc = "Registers" })
+ map("n", "<leader>ffc", function() telescope_module.safe_telescope_builtin("current_buffer_fuzzy_find")() end, { desc = "Current buffer fuzzy find" })
+ -- Corrected the previous `fp` mapping that pointed to `pickers`
+ map("n", "<leader>fp", function() telescope_module.safe_telescope_builtin("oldfiles")() end, { desc = "Recently opened files" })
+
+
+ ---
+ --- Telescope Extension functions
+ --- Note: safe_telescope_extension returns a function, so you map directly to it.
+ ---
+ map("n", "<leader>cf", function() telescope_module.safe_telescope_extension("changed_files", "changed_files")() end, { desc = "Changed files" })
+ map("n", "<leader>fm", function() telescope_module.safe_telescope_extension("media_files", "media_files")() end, { desc = "Media files" })
+ map("n", "<leader>fi", function() telescope_module.safe_telescope_extension("notify", "notify")() end, { desc = "Notifications" })
+ map("n", "<Leader>fs", function() telescope_module.safe_telescope_extension("session-lens", "search_session")() end, { desc = "Search sessions" })
+ map("n", "<Leader>frf", function() telescope_module.safe_telescope_extension("recent_files", "pick")() end, { desc = "Recent files" })
+ map("n", "<Leader>f/", function() telescope_module.safe_telescope_extension("file_browser", "file_browser")() end, { desc = "File browser" })
+
+
+ ---
+ --- Custom functions defined in plugins.telescope.lua
+ --- Note: safe_telescope_call returns a function, so you map directly to it.
+ --- (These were already correct as safe_telescope_call returns a callable function)
+ ---
+ map("n", "<leader>ffd", telescope_module.safe_telescope_call("plugins.telescope", "find_dirs"), { desc = "Find directories" })
+ map("n", "<leader>ff.", telescope_module.safe_telescope_call("plugins.telescope", "find_configs"), { desc = "Find configs" })
+ map("n", "<leader>ffs", telescope_module.safe_telescope_call("plugins.telescope", "find_scripts"), { desc = "Find scripts" })
+ map("n", "<leader>ffw", telescope_module.safe_telescope_call("plugins.telescope", "find_projects"), { desc = "Find projects" })
+ map("n", "<leader>ffB", telescope_module.safe_telescope_call("plugins.telescope", "find_books"), { desc = "Find books" })
+ map("n", "<leader>ffn", telescope_module.safe_telescope_call("plugins.telescope", "find_notes"), { desc = "Find notes" })
+ map("n", "<leader>fgn", telescope_module.safe_telescope_call("plugins.telescope", "grep_notes"), { desc = "Grep notes" })
+ map("n", "<leader>fpp", telescope_module.safe_telescope_call("plugins.telescope", "find_private"), { desc = "Find private notes" })
+ map("n", "<leader>fgc", telescope_module.safe_telescope_call("plugins.telescope", "grep_current_dir"), { desc = "Grep current directory" })
+
+end
+---- Fallback keymaps when telescope is not available
+--map("n", "<leader>ff", function()
+-- local file = vim.fn.input("Open file: ", "", "file")
+-- if file ~= "" then
+-- vim.cmd("edit " .. file)
+-- end
+--end, { desc = "Find files (fallback)" })
+
+---- You can add other basic fallbacks here
+--map("n", "<leader>fg", function()
+-- vim.notify("Live grep requires telescope plugin", vim.log.levels.WARN)
+--end, { desc = "Live grep (unavailable)" })
+----end
+
+
+map("n", "<leader>fF", ":cd %:p:h<CR>:pwd<CR><cmd>lua require('user.mods').findFilesInCwd()<CR>",
+{ noremap = true, silent = true, desc = "Find files in cwd" })
+
+-- FZF
+map("n", "<leader>fz", function()
+ local ok, fzf_lua = pcall(require, "fzf-lua")
+ if ok then
+ fzf_lua.files() -- no config, just open
+ else
+ local handle = io.popen("find . -type f | fzf")
+ if handle then
+ local result = handle:read("*a")
+ handle:close()
+ result = result:gsub("\n", "")
+ if result ~= "" then
+ vim.cmd("edit " .. vim.fn.fnameescape(result))
+ end
+ else
+ vim.notify("fzf not found or failed to run", vim.log.levels.ERROR)
+ end
+ end
+end, { desc = "FZF file picker (fzf-lua or fallback)" })
+
+map("n", "gA", ":FzfLua lsp_code_actions<CR>")
+
+-- Nvim-tree
+local function safe_nvim_tree_toggle()
+ local ok_tree, tree_api = pcall(require, "nvim-tree.api")
+ if ok_tree then
+ pcall(vim.cmd, "Rooter") -- silently run Rooter if available
+ tree_api.tree.toggle()
+ else
+ -- Fallback to netrw
+ local cur_buf = vim.api.nvim_get_current_buf()
+ local ft = vim.api.nvim_get_option_value("filetype", { buf = cur_buf })
+
+ if ft == "netrw" then
+ vim.cmd("close")
+ else
+ vim.cmd("Lexplore")
+ end
+ end
+end
+
+map("n", "<leader>f", safe_nvim_tree_toggle, { desc = "Toggle file explorer" })
+
+-- Undotree
+map("n", "<leader>u", vim.cmd.UndotreeToggle)
+
+-- Markdown-preview
+map("n", "<leader>md", "<Plug>MarkdownPreviewToggle")
+map("n", "<leader>mg", "<CMD>Glow<CR>")
+
+-- Autopairs
+map("n", "<leader>ww", "<cmd>lua require('user.mods').Toggle_autopairs()<CR>")
+
+-- Zen-mode toggle
+map("n", "<leader>zm", "<CMD>ZenMode<CR> | :echom ('Zen Mode')<CR> | :sl! | echo ('')<CR>")
+
+-- Vim-rooter
+local function safe_project_root()
+ if vim.fn.exists(":Rooter") == 2 then
+ vim.cmd("Rooter")
+ else
+ vim.cmd("cd %:p:h")
+ end
+end
+vim.keymap.set("n", "<leader>ro", safe_project_root, { desc = "Project root" })
+
+-- Trouble (UI to show diagnostics)
+local function safe_trouble_toggle(view, opts)
+ local ok, _ = pcall(require, "trouble")
+ if ok then
+ local cmd = "Trouble"
+ if view then
+ cmd = cmd .. " " .. view .. " toggle"
+ if opts then
+ cmd = cmd .. " " .. opts
+ end
+ else
+ cmd = cmd .. " diagnostics toggle"
+ end
+ vim.cmd(cmd)
+ else
+ vim.cmd("copen")
+ end
+end
+
+-- Replace 'map' with 'vim.keymap.set' if not already a global alias
+vim.keymap.set("n", "<leader>t", function()
+ safe_trouble_toggle()
+end, { desc = "Diagnostics (Workspace)" })
+
+vim.keymap.set("n", "<leader>tw", function()
+ vim.cmd("cd %:p:h | pwd")
+ safe_trouble_toggle("diagnostics")
+end, { desc = "Diagnostics (Workspace)" })
+
+vim.keymap.set("n", "<leader>td", function()
+ vim.cmd("cd %:p:h | pwd")
+ safe_trouble_toggle("diagnostics", "filter.buf=0")
+end, { desc = "Diagnostics (Buffer)" })
+
+vim.keymap.set("n", "<leader>tq", function()
+ vim.cmd("cd %:p:h | pwd")
+ safe_trouble_toggle("qflist")
+end, { desc = "Quickfix List" })
+
+vim.keymap.set("n", "<leader>tl", function()
+ vim.cmd("cd %:p:h | pwd")
+ safe_trouble_toggle("loclist")
+end, { desc = "Location List" })
+
+vim.keymap.set("n", "gR", function()
+ safe_trouble_toggle("lsp")
+end, { desc = "LSP References/Definitions" })
+
+-- Null-ls
+map("n", "<leader>ls", ':lua require("null-ls").toggle({})<CR>')
+
+
+-- Replacer
+map("n", "<Leader>qr", ':lua require("replacer").run()<CR>')
+
+-- Quickfix
+map("n", "<leader>q", function()
+ if vim.fn.getqflist({ winid = 0 }).winid ~= 0 then
+ require("plugins.quickfix").close()
+ else
+ require("plugins.quickfix").open()
+ end
+end, { desc = "Toggle quickfix window" })
+
+-- Move to the next and previous item in the quickfixlist
+map("n", "]c", "<Cmd>cnext<CR>")
+map("n", "[c", "<Cmd>cprevious<CR>")
+
+-- Location list
+map("n", "<leader>l", '<cmd>lua require("plugins.loclist").loclist_toggle()<CR>')
+
+-- Dap (debugging)
+local dap_ok, dap = pcall(require, "dap")
+local dap_ui_ok, ui = pcall(require, "dapui")
+
+if not (dap_ok and dap_ui_ok) then
+ --require("notify")("nvim-dap or dap-ui not installed!", "warning")
+ return
+end
+
+vim.fn.sign_define("DapBreakpoint", { text = "🐞" })
+
+-- Start debugging session
+map("n", "<leader>ds", function()
+ dap.continue()
+ ui.toggle({})
+ vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes("<C-w>=", false, true, true), "n", false) -- Spaces buffers evenly
+end)
+
+-- Set breakpoints, get variable values, step into/out of functions, etc.
+map("n", "<leader>dC", dap.continue)
+-- map("n", "<leader>dC", dap.close)
+-- map("n", "<leader>dt", dap.terminate)
+map("n", "<leader>dt", ui.toggle)
+map("n", "<leader>dd", function()
+ dap.disconnect({ terminateDebuggee = true })
+end)
+map("n", "<leader>dn", dap.step_over)
+map("n", "<leader>di", dap.step_into)
+map("n", "<leader>do", dap.step_out)
+map("n", "<leader>db", dap.toggle_breakpoint)
+map("n", "<leader>dB", function()
+ dap.clear_breakpoints()
+ require("notify")("Breakpoints cleared", "warn")
+end)
+map("n", "<leader>dl", function()
+ local ok, dap_widgets = pcall(require, "dap.ui.widgets")
+ if ok then dap_widgets.hover() end
+end)
+map("n", "<leader>de", function()
+ require("dapui").float_element()
+end, { desc = "Open Element" })
+map("n", "<leader>dq", function()
+ require("dapui").close()
+ require("dap").repl.close()
+ local session = require("dap").session()
+ if session then
+ require("dap").terminate()
+ end
+ require("nvim-dap-virtual-text").refresh()
+end, { desc = "Terminate Debug" })
+map("n", "<leader>dc", function()
+ require("telescope").extensions.dap.commands()
+end, { desc = "DAP-Telescope: Commands" })
+--vim.keymap.set("n", "<leader>B", ":lua require'dap'.set_breakpoint(vim.fn.input('Breakpoint condition: '))<CR>")
+--vim.keymap.set("v", "<leader>B", ":lua require'dap'.set_breakpoint(vim.fn.input('Breakpoint condition: '))<CR>")
+--vim.keymap.set("n", "<leader>lp", ":lua require'dap'.set_breakpoint(nil, nil, vim.fn.input('Log point message: '))<CR>")
+--vim.keymap.set("n", "<leader>dr", ":lua require'dap'.repl.open()<CR>")
+
+-- Close debugger and clear breakpoints
+--map("n", "<leader>de", function()
+-- dap.clear_breakpoints()
+-- ui.toggle({})
+-- dap.terminate()
+-- vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes("<C-w>=", false, true, true), "n", false)
+-- require("notify")("Debugger session ended", "warn")
+--end)
+
+-- Toggle Dashboard
+map("n", "<leader><Space>", '<CMD>lua require("user.mods").toggle_dashboard()<CR>')
+
+-- Lsp Lines toggle
+map("", "<Leader>ll", require("lsp_lines").toggle, { desc = "Toggle lsp_lines" })
+
+-- SnipRun
+map({ "n", "v" }, "<leader>r", "<Plug>SnipRun<CR>")
+
+-- Codi
+map("n", "<leader>co", '<CMD>lua require("user.mods").toggleCodi()<CR>')
+
+-- Scratch buffer
+map("n", "<leader>ss", '<CMD>lua require("user.mods").Scratch("float")<CR>')
+map("n", "<leader>sh", '<CMD>lua require("user.mods").Scratch("horizontal")<CR>')
+map("n", "<leader>sv", '<CMD>lua require("user.mods").Scratch("vertical")<CR>')
+
+-- Hardtime
+map("n", "<leader>H", '<CMD>lua require("plugins.hardtime").ToggleHardtime()<CR>')
+
+-- Code Run
+map("n", "<leader>rr", '<CMD>lua require("user.mods").toggleCodeRunner()<CR>')
+
+-- Run executable file
+map("n", "<leader>rx",
+":lua require('user.mods').RunCurrentFile()<CR>:echom 'Running executable file...'<CR>:sl!<CR>:echo ''<CR>")
+
+-- Set Files to current location as dir
+map({ "n" }, "<leader>cf", "<CMD>e %:h<CR>")
+
+-- Vimtex
+map("n", "<Leader>lc", ":VimtexCompile<cr>")
+map("v", "<Leader>ls", ":VimtexCompileSelected<cr>")
+map("n", "<Leader>li", ":VimtexInfo<cr>")
+map("n", "<Leader>lt", ":VimtexTocToggle<cr>")
+map("n", "<Leader>lv", ":VimtexView<cr>")
diff --git a/common/nvim/lua/user/mods.lua b/common/nvim/lua/user/mods.lua
new file mode 100755
index 0000000..b4e1579
--- /dev/null
+++ b/common/nvim/lua/user/mods.lua
@@ -0,0 +1,1427 @@
+-- ============================================================================
+-- Modules/Utility functions
+-- ============================================================================
+
+local M = {}
+
+-- Shorten Function Names
+local fn = vim.fn
+local api = vim.api
+
+--- Check if an executable exists
+---@param name string The name of the executable to check
+---@return boolean
+function M.executable(name)
+ return fn.executable(name) > 0
+end
+
+--- Check if a feature is available in Neovim
+---@param feat string The feature to check (e.g., 'nvim-0.7')
+---@return boolean
+function M.has(feat)
+ return fn.has(feat) == 1
+end
+
+--- Setup command aliases
+---@param from string The alias
+---@param to string The command to alias to
+function M.setup_command_alias(from, to)
+ local cmd = string.format('cnoreabbrev <expr> %s (getcmdtype() == ":" && getcmdline() == "%s") ? "%s" : "%s"',
+ from, from, to, from)
+ api.nvim_command(cmd)
+end
+
+--- Preserve cursor position while formatting
+---@param cmd string The command to run
+function M.preserve_cursor(cmd)
+ local cursor = api.nvim_win_get_cursor(0)
+ vim.cmd(cmd)
+ api.nvim_win_set_cursor(0, cursor)
+end
+
+--- Toggle quickfix window
+function M.toggle_quickfix()
+ local qf_exists = false
+ for _, win in pairs(fn.getwininfo()) do
+ if win.quickfix == 1 then
+ qf_exists = true
+ break
+ end
+ end
+ if qf_exists then
+ vim.cmd('cclose')
+ else
+ vim.cmd('copen')
+ end
+end
+
+--- Toggle location list
+function M.toggle_location()
+ local loc_exists = false
+ for _, win in pairs(fn.getwininfo()) do
+ if win.loclist == 1 then
+ loc_exists = true
+ break
+ end
+ end
+ if loc_exists then
+ vim.cmd('lclose')
+ else
+ vim.cmd('lopen')
+ end
+end
+
+-- Setup command aliases
+M.setup_command_alias('W', 'w')
+M.setup_command_alias('Wq', 'wq')
+M.setup_command_alias('WQ', 'wq')
+M.setup_command_alias('Q', 'q')
+M.setup_command_alias('Qa', 'qa')
+M.setup_command_alias('QA', 'qa')
+
+--------------------------------------------------
+
+--- Check whether a feature exists in Nvim
+--- @feat: string
+--- the feature name, like `nvim-0.7` or `unix`.
+--- return: bool
+M.has = function(feat)
+ if fn.has(feat) == 1 then
+ return true
+ end
+
+ return false
+end
+
+--------------------------------------------------
+
+-- Format on save
+local format_augroup = vim.api.nvim_create_augroup("LspFormatting", {})
+
+local ok, null_ls = pcall(require, "null-ls")
+if ok then
+ null_ls.setup({
+ on_attach = function(client, bufnr)
+ if client.supports_method("textDocument/formatting") then
+ vim.api.nvim_clear_autocmds({ group = format_augroup, buffer = bufnr })
+ vim.api.nvim_create_autocmd("BufWritePre", {
+ group = format_augroup,
+ buffer = bufnr,
+ callback = function()
+ if vim.lsp.buf.format then
+ vim.lsp.buf.format({ bufnr = bufnr })
+ else
+ vim.lsp.buf.formatting_seq_sync()
+ end
+ end,
+ })
+ end
+ end,
+ })
+end
+
+vim.cmd([[autocmd BufWritePre <buffer> lua vim.lsp.buf.format()]])
+
+
+--------------------------------------------------
+
+---Determine if a value of any type is empty
+---@param item any
+---@return boolean?
+
+--- Checks if an item is considered "empty".
+--
+-- An item is considered empty if:
+-- - It is nil.
+-- - It is an empty string.
+-- - It is an empty table.
+-- - It is a number equal to 0 (you might want to adjust this based on your definition of "empty" for numbers).
+--
+-- @param item any The item to check.
+-- @return boolean True if the item is empty, false otherwise.
+function M.empty(item)
+ -- Case 1: item is nil
+ if item == nil then
+ return true
+ end
+
+ local item_type = type(item)
+
+ -- Case 2: empty string
+ if item_type == "string" then
+ return item == ""
+ end
+
+ if item_type == "table" then
+ return vim.tbl_isempty(item)
+ end
+ if item_type == "number" then
+ return item == 0 -- Changed from item <= 0 for a stricter "empty" definition for numbers
+ end
+
+ if item_type == "boolean" then
+ return not item -- Returns true if item is false, false if item is true
+ end
+
+ return false
+end
+
+
+--------------------------------------------------
+
+--- Create a dir if it does not exist
+function M.may_create_dir(dir)
+ local res = fn.isdirectory(dir)
+
+ if res == 0 then
+ fn.mkdir(dir, "p")
+ end
+end
+
+--------------------------------------------------
+
+--- Toggle cmp completion
+vim.g.cmp_toggle_flag = false -- initialize
+local normal_buftype = function()
+ return vim.api.nvim_buf_get_option(0, "buftype") ~= "prompt"
+end
+M.toggle_completion = function()
+ local ok, cmp = pcall(require, "cmp")
+ if ok then
+ local next_cmp_toggle_flag = not vim.g.cmp_toggle_flag
+ if next_cmp_toggle_flag then
+ print("completion on")
+ else
+ print("completion off")
+ end
+ cmp.setup({
+ enabled = function()
+ vim.g.cmp_toggle_flag = next_cmp_toggle_flag
+ if next_cmp_toggle_flag then
+ return normal_buftype
+ else
+ return next_cmp_toggle_flag
+ end
+ end,
+ })
+ else
+ print("completion not available")
+ end
+end
+
+--------------------------------------------------
+
+--- Make sure using latest neovim version
+function M.get_nvim_version()
+ local actual_ver = vim.version()
+
+ local nvim_ver_str = string.format("%d.%d.%d", actual_ver.major, actual_ver.minor, actual_ver.patch)
+ return nvim_ver_str
+end
+
+function M.add_pack(name)
+ local status, error = pcall(vim.cmd, "packadd " .. name)
+
+ return status
+end
+
+--------------------------------------------------
+
+-- Define a global function to retrieve LSP clients based on Neovim version
+function M.get_lsp_clients(bufnr)
+ local mods = require("user.mods")
+ --local expected_ver = '0.10.0'
+ local nvim_ver = mods.get_nvim_version()
+
+ local version_major, version_minor = string.match(nvim_ver, "(%d+)%.(%d+)")
+ version_major = tonumber(version_major)
+ version_minor = tonumber(version_minor)
+
+ if version_major > 0 or (version_major == 0 and version_minor >= 10) then
+ return vim.lsp.get_clients({ buffer = bufnr })
+ else
+ return vim.lsp.buf_get_clients()
+ end
+end
+
+--------------------------------------------------
+
+--- Toggle autopairs on/off (requires "windwp/nvim-autopairs")
+function M.Toggle_autopairs()
+ local ok, autopairs = pcall(require, "nvim-autopairs")
+ if ok then
+ if autopairs.state.disabled then
+ autopairs.enable()
+ print("autopairs on")
+ else
+ autopairs.disable()
+ print("autopairs off")
+ end
+ else
+ print("autopairs not available")
+ end
+end
+
+--------------------------------------------------
+
+--- Make vim-rooter message disappear after making it's changes
+--vim.cmd([[
+--let timer = timer_start(1000, 'LogTrigger', {})
+--func! LogTrigger(timer)
+-- silent!
+--endfunc
+--]])
+--
+--vim.cmd([[
+--function! ConfigureChDir()
+-- echo ('')
+--endfunction
+--" Call after vim-rooter changes the root dir
+--autocmd User RooterChDir :sleep! | call LogTrigger(timer) | call ConfigureChDir()
+--]])
+
+function M.findFilesInCwd()
+ vim.cmd("let g:rooter_manual_only = 1") -- Toggle the rooter plugin
+ require("plugins.telescope").findhere()
+ vim.defer_fn(function()
+ vim.cmd("let g:rooter_manual_only = 0") -- Change back to automatic rooter
+ end, 100)
+end
+
+--function M.findFilesInCwd()
+-- vim.cmd("let g:rooter_manual_only = 1") -- Toggle the rooter plugin
+-- require("plugins.telescope").findhere()
+-- --vim.cmd("let g:rooter_manual_only = 0") -- Change back to automatic rooter
+--end
+
+--------------------------------------------------
+
+-- Toggle the executable permission
+function M.Toggle_executable()
+ local current_file = vim.fn.expand("%:p")
+ local executable = vim.fn.executable(current_file) == 1
+
+ if executable then
+ -- File is executable, unset the executable permission
+ vim.fn.system("chmod -x " .. current_file)
+ --print(current_file .. ' is no longer executable.')
+ print("No longer executable")
+ else
+ -- File is not executable, set the executable permission
+ vim.fn.system("chmod +x " .. current_file)
+ --print(current_file .. ' is now executable.')
+ print("Now executable")
+ end
+end
+
+--------------------------------------------------
+
+-- Set bare dotfiles repository git environment variables dynamically
+
+-- Set git enviornment variables
+--function M.Set_git_env_vars()
+-- local git_dir_job = vim.fn.jobstart({ "git", "rev-parse", "--git-dir" })
+-- local command_status = vim.fn.jobwait({ git_dir_job })[1]
+-- if command_status > 0 then
+-- vim.env.GIT_DIR = vim.fn.expand("$HOME/.cfg")
+-- vim.env.GIT_WORK_TREE = vim.fn.expand("~")
+-- else
+-- vim.env.GIT_DIR = nil
+-- vim.env.GIT_WORK_TREE = nil
+-- end
+-- -- Launch terminal emulator with Git environment variables set
+-- --require("toggleterm").exec(string.format([[%s %s]], os.getenv("SHELL"), "-i"))
+--end
+
+------
+
+local prev_cwd = ""
+
+function M.Set_git_env_vars()
+ local cwd = vim.fn.getcwd()
+ if prev_cwd == "" then
+ -- First buffer being opened, set prev_cwd to cwd
+ prev_cwd = cwd
+ elseif cwd ~= prev_cwd then
+ -- Working directory has changed since last buffer was opened
+ prev_cwd = cwd
+ local git_dir_job = vim.fn.jobstart({ "git", "rev-parse", "--git-dir" })
+ local command_status = vim.fn.jobwait({ git_dir_job })[1]
+ if command_status > 0 then
+ vim.env.GIT_DIR = vim.fn.expand("$HOME/.cfg")
+ vim.env.GIT_WORK_TREE = vim.fn.expand("~")
+ else
+ vim.env.GIT_DIR = nil
+ vim.env.GIT_WORK_TREE = nil
+ end
+ end
+end
+
+vim.cmd([[augroup my_git_env_vars]])
+vim.cmd([[ autocmd!]])
+vim.cmd([[ autocmd BufEnter * lua require('user.mods').Set_git_env_vars()]])
+vim.cmd([[ autocmd VimEnter * lua require('user.mods').Set_git_env_vars()]])
+vim.cmd([[augroup END]])
+
+--------------------------------------------------
+
+--- Update Tmux Status Vi-mode
+function M.update_tmux_status()
+ -- Check if the current buffer has a man filetype
+ if vim.bo.filetype == "man" then
+ return
+ end
+ local mode = vim.api.nvim_eval("mode()")
+ -- Determine the mode name based on the mode value
+ local mode_name
+ if mode == "n" then
+ mode_name = "-- NORMAL --"
+ elseif mode == "i" or mode == "ic" then
+ mode_name = "-- INSERT --"
+ else
+ mode_name = "-- NORMAL --" --'-- COMMAND --'
+ end
+
+ -- Write the mode name to the file
+ local file = io.open(os.getenv("HOME") .. "/.vi-mode", "w")
+ file:write(mode_name)
+ file:close()
+ if nvim_running then
+ -- Neovim is running, update the mode file and refresh tmux
+ VI_MODE = "" -- Clear VI_MODE to show Neovim mode
+ vim.cmd("silent !tmux refresh-client -S")
+ end
+ ---- Force tmux to update the status
+ vim.cmd("silent !tmux refresh-client -S")
+end
+
+vim.cmd([[
+ augroup TmuxStatus
+ autocmd!
+ autocmd InsertLeave,InsertEnter * lua require("user.mods").update_tmux_status()
+ autocmd VimEnter * lua require("user.mods").update_tmux_status()
+ autocmd BufEnter * lua require("user.mods").update_tmux_status()
+ autocmd ModeChanged * lua require("user.mods").update_tmux_status()
+ autocmd WinEnter,WinLeave * lua require("user.mods").update_tmux_status()
+ augroup END
+]])
+
+-- Add autocmd for <esc>
+-- Add autocmd to check when tmux switches panes/windows
+--autocmd InsertLeave,InsertEnter * lua require("user.mods").update_tmux_status()
+--autocmd BufEnter * lua require("user.mods").update_tmux_status()
+--autocmd WinEnter,WinLeave * lua require("user.mods").update_tmux_status()
+
+--autocmd WinEnter,WinLeave * lua require("user.mods").update_tmux_status()
+--autocmd VimResized * lua require("user.mods").update_tmux_status()
+--autocmd FocusGained * lua require("user.mods").update_tmux_status()
+--autocmd FocusLost * lua require("user.mods").update_tmux_status()
+--autocmd CmdwinEnter,CmdwinLeave * lua require("user.mods").update_tmux_status()
+
+--------------------------------------------------
+
+-- function OpenEmulatorList()
+-- local emulatorsBuffer = vim.api.nvim_create_buf(false, true)
+-- vim.api.nvim_buf_set_lines(emulatorsBuffer, 0, 0, true, {"Some text"})
+-- vim.api.nvim_open_win(
+-- emulatorsBuffer,
+-- false,
+-- {
+-- relative='win', row=3, col=3, width=12, height=3
+-- }
+-- )
+-- end
+--
+-- vim.api.nvim_create_user_command('OpenEmulators', OpenEmulatorList, {})
+
+--local api = vim.api
+--local fn = vim.fn
+--local cmd = vim.cmd
+--
+--local function bufremove(opts)
+-- local target_buf_id = api.nvim_get_current_buf()
+--
+-- -- Do nothing if buffer is in modified state.
+-- if not opts.force and api.nvim_buf_get_option(target_buf_id, 'modified') then
+-- return false
+-- end
+--
+-- -- Hide target buffer from all windows.
+-- vim.tbl_map(function(win_id)
+-- win_id = win_id or 0
+--
+-- local current_buf_id = api.nvim_win_get_buf(win_id)
+--
+-- api.nvim_win_call(win_id, function()
+-- -- Try using alternate buffer
+-- local alt_buf_id = fn.bufnr('#')
+-- if alt_buf_id ~= current_buf_id and fn.buflisted(alt_buf_id) == 1 then
+-- api.nvim_win_set_buf(win_id, alt_buf_id)
+-- return
+-- end
+--
+-- -- Try using previous buffer
+-- cmd('bprevious')
+-- if current_buf_id ~= api.nvim_win_get_buf(win_id) then
+-- return
+-- end
+--
+-- -- Create new listed scratch buffer
+-- local new_buf = api.nvim_create_buf(true, true)
+-- api.nvim_win_set_buf(win_id, new_buf)
+-- end)
+--
+-- return true
+-- end, fn.win_findbuf(target_buf_id))
+--
+-- cmd(string.format('bdelete%s %d', opts.force and '!' or '', target_buf_id))
+--end
+--
+---- Assign bufremove to a global variable
+--_G.bufremove = bufremove
+
+--vim.cmd([[
+-- augroup NvimTreeDelete
+-- autocmd!
+-- autocmd FileType NvimTree lua require('user.mods').enew_on_delete()
+-- augroup END
+--]])
+--
+--function M.enew_on_delete()
+-- if vim.bo.buftype == 'nofile' then
+-- vim.cmd('enew')
+-- end
+--end
+
+-- Update Neovim
+--function M.Update_neovim()
+-- -- Run the commands to download and extract the latest version
+-- os.execute("curl -L -o nvim-linux64.tar.gz https://github.com/neovim/neovim/releases/latest/download/nvim-linux64.tar.gz")
+-- os.execute("tar xzvf nvim-linux64.tar.gz")
+-- -- Replace the existing Neovim installation with the new version
+-- os.execute("rm -rf $HOME/.local/bin/nvim")
+-- os.execute("mv nvim-linux64 $HOME/.local/bin/nvim")
+--
+-- -- Clean up the downloaded file
+-- os.execute("rm nvim-linux64.tar.gz")
+--
+-- -- Print a message to indicate the update is complete
+-- print("Neovim has been updated to the latest version.")
+--end
+--
+---- Bind a keymap to the update_neovim function (optional)
+--vim.api.nvim_set_keymap('n', '<leader>u', '<cmd> lua require("user.mods").Update_neovim()<CR>', { noremap = true, silent = true })
+
+-- Define a function to create a floating window and run the update process inside it
+function M.Update_neovim()
+ -- Create a new floating window
+ local bufnr, winid = vim.api.nvim_create_buf(false, true)
+ vim.api.nvim_open_win(bufnr, true, {
+ relative = "editor",
+ width = 80,
+ height = 20,
+ row = 2,
+ col = 2,
+ style = "minimal",
+ border = "single",
+ })
+
+ -- Function to append a line to the buffer in the floating window
+ local function append_line(line)
+ vim.api.nvim_buf_set_option(bufnr, "modifiable", true)
+ vim.api.nvim_buf_set_lines(bufnr, -1, -1, false, { line })
+ vim.api.nvim_buf_set_option(bufnr, "modifiable", false)
+ end
+
+ -- Download the latest version of Neovim
+ append_line("Downloading the latest version of Neovim...")
+ os.execute(
+ "curl -L -o nvim-linux64.tar.gz https://github.com/neovim/neovim/releases/latest/download/nvim-linux64.tar.gz")
+ append_line("Download complete.")
+
+ -- Extract the downloaded archive
+ append_line("Extracting the downloaded archive...")
+ os.execute("tar xzvf nvim-linux64.tar.gz")
+ append_line("Extraction complete.")
+
+ -- Replace the existing Neovim installation with the new version
+ append_line("Replacing the existing Neovim installation...")
+ os.execute("rm -rf $HOME/nvim")
+ os.execute("mv nvim-linux64 $HOME/nvim")
+ append_line("Update complete.")
+
+ -- Clean up the downloaded file
+ append_line("Cleaning up the downloaded file...")
+ os.execute("rm nvim-linux64.tar.gz")
+ append_line("Cleanup complete.")
+
+ -- Close the floating window after a delay
+ vim.defer_fn(function()
+ vim.api.nvim_win_close(winid, true)
+ end, 5000) -- Adjust the delay as needed
+end
+
+-- Bind a keymap to the update_neovim function (optional)
+vim.api.nvim_set_keymap("n", "<leader>U", '<cmd> lua require("user.mods").Update_neovim()<CR>',
+ { noremap = true, silent = true })
+
+--------------------------------------------------
+
+-- Fix or suppress closing nvim error message (/src/unix/core.c:147: uv_close: Assertion `!uv__is_closing(handle)' failed.)
+vim.api.nvim_create_autocmd({ "VimLeave" }, {
+ callback = function()
+ vim.fn.jobstart("!notify-send 2>/dev/null &", { detach = true })
+ end,
+})
+
+--------------------------------------------------
+
+-- Rooter
+--vim.cmd([[autocmd BufEnter * lua vim.cmd('Rooter')]])
+
+--------------------------------------------------
+
+-- Nvim-tree
+local modifiedBufs = function(bufs) -- nvim-tree is also there in modified buffers so this function filter it out
+ local t = 0
+ for k, v in pairs(bufs) do
+ if v.name:match("NvimTree_", "NvimTree1") == nil then
+ t = t + 1
+ end
+ end
+ return t
+end
+
+-- Deleting current file opened behaviour
+function M.DeleteCurrentBuffer()
+ local cbn = vim.api.nvim_get_current_buf()
+ local buffers = vim.fn.getbufinfo({ buflisted = true })
+ local size = #buffers
+ local idx = 0
+
+ for n, e in ipairs(buffers) do
+ if e.bufnr == cbn then
+ idx = n
+ break -- Exit loop as soon as we find the buffer
+ end
+ end
+
+ if idx == 0 then
+ return
+ end
+
+ if idx == size then
+ vim.cmd("bprevious")
+ else
+ vim.cmd("bnext")
+ end
+
+ vim.cmd("silent! bdelete " .. cbn)
+
+ -- Open a new blank window
+ vim.cmd("silent! enew") -- Opens a new vertical split
+ -- OR
+ -- vim.cmd("new") -- Opens a new horizontal split
+ -- Delay before opening a new split
+ --vim.defer_fn(function()
+ -- vim.cmd("enew") -- Opens a new vertical split
+ --end, 100) -- Adjust the delay as needed (in milliseconds)
+ -- Delay before closing the nvim-tree window
+end
+
+
+-- On :bd nvim-tree should behave as if it wasn't opened
+-- Only run DeleteCurrentBuffer if NvimTree is loaded
+vim.api.nvim_create_autocmd("FileType", {
+ pattern = "NvimTree",
+ callback = function()
+ local ok, mods = pcall(require, "user.mods")
+ if ok and type(mods.DeleteCurrentBuffer) == "function" then
+ mods.DeleteCurrentBuffer()
+ end
+ end,
+})
+
+-- Handle NvimTree window closure safely
+vim.api.nvim_create_autocmd("BufEnter", {
+ nested = true,
+ callback = function()
+ local ok_utils, utils = pcall(require, "nvim-tree.utils")
+ if not ok_utils then return end
+
+ if #vim.api.nvim_list_wins() == 1 and utils.is_nvim_tree_buf() then
+ local ok_api, api = pcall(require, "nvim-tree.api")
+ if not ok_api then return end
+
+ vim.defer_fn(function()
+ -- Safely toggle tree off and on
+ pcall(api.tree.toggle, { find_file = true, focus = true })
+ pcall(api.tree.toggle, { find_file = true, focus = true })
+ vim.cmd("wincmd p")
+ end, 0)
+ end
+ end,
+})
+
+-- Dismiss notifications when opening nvim-tree window
+local function isNvimTreeOpen()
+ local win = vim.fn.win_findbuf(vim.fn.bufnr("NvimTree"))
+ return vim.fn.empty(win) == 0
+end
+
+function M.DisableNotify()
+ if isNvimTreeOpen() then
+ require("notify").dismiss()
+ end
+end
+
+vim.cmd([[
+ autocmd! WinEnter,WinLeave * lua require('user.mods').DisableNotify()
+]])
+
+--------------------------------------------------
+
+-- Toggle Dashboard
+function M.toggle_dashboard()
+ if vim.bo.filetype == "dashboard" then
+ vim.cmd("bdelete")
+ else
+ vim.cmd("Dashboard")
+ end
+end
+
+--------------------------------------------------
+
+-- Helper function to suppress errors
+local function silent_execute(cmd)
+ vim.fn["serverlist"]() -- Required to prevent 'Press ENTER' prompt
+ local result = vim.fn.system(cmd .. " 2>/dev/null")
+ vim.fn["serverlist"]()
+ return result
+end
+
+--------------------------------------------------
+
+-- Toggle Codi
+-- Define a global variable to track Codi's state
+local is_codi_open = false
+
+function M.toggleCodi()
+ if is_codi_open then
+ -- Close Codi
+ vim.cmd("Codi!")
+ is_codi_open = false
+ print("Codi off")
+ else
+ -- Open Codi
+ vim.cmd("Codi")
+ is_codi_open = true
+ print("Codi on")
+ end
+end
+
+--------------------------------------------------
+
+---- Function to create or toggle a scratch buffer
+-- Define global variables to store the scratch buffer and window
+local scratch_buf = nil
+local scratch_win = nil
+
+-- Other global variables
+local scratch_date = os.date("%Y-%m-%d")
+local scratch_dir = vim.fn.expand("~/notes/private")
+local scratch_file = "scratch-" .. scratch_date .. ".md"
+
+-- Function to close and delete a buffer
+function CloseAndDeleteBuffer(bufnr)
+ if bufnr and vim.api.nvim_buf_is_valid(bufnr) then
+ vim.api.nvim_command("silent! bwipe " .. bufnr)
+ end
+end
+
+function M.Scratch(Split_direction)
+ -- Check if the directory exists, and create it if it doesn't
+ if vim.fn.isdirectory(scratch_dir) == 0 then
+ vim.fn.mkdir(scratch_dir, "p")
+ end
+
+ -- Determine the window type based on Split_direction
+ local current_window_type = "float"
+ if Split_direction == "float" then
+ current_window_type = "float"
+ elseif Split_direction == "vertical" then
+ current_window_type = "vertical"
+ elseif Split_direction == "horizontal" then
+ current_window_type = "horizontal"
+ end
+
+ local file_path = scratch_dir .. "/" .. scratch_file
+
+ if scratch_win and vim.api.nvim_win_is_valid(scratch_win) then
+ -- Window exists, save buffer to file and close it
+ WriteScratchBufferToFile(scratch_buf, file_path)
+ vim.cmd(":w!")
+ vim.api.nvim_win_close(scratch_win, true)
+ CloseAndDeleteBuffer(scratch_buf)
+ scratch_win = nil
+ scratch_buf = nil
+ else
+ if scratch_buf and vim.api.nvim_buf_is_valid(scratch_buf) then
+ -- Buffer exists, reuse it and open a new window
+ OpenScratchWindow(scratch_buf, current_window_type)
+ else
+ -- Buffer doesn't exist, create it and load the file if it exists
+ scratch_buf = OpenScratchBuffer(file_path)
+ OpenScratchWindow(scratch_buf, current_window_type)
+ end
+ end
+end
+
+-- Function to write buffer contents to a file
+function WriteScratchBufferToFile(buf, file_path)
+ if buf and vim.api.nvim_buf_is_valid(buf) then
+ local lines = vim.api.nvim_buf_get_lines(buf, 0, -1, false)
+ local content = table.concat(lines, "\n")
+ local escaped_file_path = vim.fn.fnameescape(file_path)
+
+ -- Write the buffer content to the file
+ local file = io.open(escaped_file_path, "w")
+ if file then
+ file:write(content)
+ file:close()
+ end
+ end
+end
+
+-- Function to create or open the scratch buffer
+function OpenScratchBuffer(file_path)
+ local buf = vim.api.nvim_create_buf(true, false)
+
+ -- Set the file name for the buffer
+ local escaped_file_path = vim.fn.fnameescape(file_path)
+ vim.api.nvim_buf_set_name(buf, escaped_file_path)
+
+ -- Check if the file exists and load it if it does
+ if vim.fn.filereadable(file_path) == 1 then
+ local file_contents = vim.fn.readfile(file_path)
+ vim.api.nvim_buf_set_lines(buf, 0, -1, true, file_contents)
+ else
+ -- Insert initial content
+ vim.api.nvim_buf_set_lines(buf, 0, -1, true, {
+ "# Quick Notes - " .. scratch_date,
+ "--------------------------",
+ "",
+ })
+
+ -- Save the initial content to the file
+ vim.cmd(":w")
+ end
+
+ return buf
+end
+
+-- Function to open the scratch buffer in a window
+function OpenScratchWindow(buf, current_window_type)
+ if buf and vim.api.nvim_buf_is_valid(buf) then
+ if current_window_type == "float" then
+ local opts = {
+ relative = "win",
+ width = 120,
+ height = 10,
+ border = "single",
+ row = 20,
+ col = 20,
+ }
+ scratch_win = vim.api.nvim_open_win(buf, true, opts)
+ -- Go to the last line of the buffer
+ vim.api.nvim_win_set_cursor(scratch_win, { vim.api.nvim_buf_line_count(buf), 1 })
+ elseif current_window_type == "vertical" then
+ vim.cmd("vsplit")
+ vim.api.nvim_win_set_buf(0, buf)
+ scratch_win = 0
+ elseif current_window_type == "horizontal" then
+ vim.cmd("split")
+ vim.api.nvim_win_set_buf(0, buf)
+ scratch_win = 0
+ end
+ end
+end
+
+--------------------------------------------------
+
+---- Intercept file open
+--local augroup = vim.api.nvim_create_augroup("user-autocmds", { clear = true })
+--local intercept_file_open = true
+--vim.api.nvim_create_user_command("InterceptToggle", function()
+-- intercept_file_open = not intercept_file_open
+-- local intercept_state = "`Enabled`"
+-- if not intercept_file_open then
+-- intercept_state = "`Disabled`"
+-- end
+-- vim.notify("Intercept file open set to " .. intercept_state, vim.log.levels.INFO, {
+-- title = "Intercept File Open",
+-- ---@param win integer The window handle
+-- on_open = function(win)
+-- vim.api.nvim_buf_set_option(vim.api.nvim_win_get_buf(win), "filetype", "markdown")
+-- end,
+-- })
+--end, { desc = "Toggles intercepting BufNew to open files in custom programs" })
+
+---- NOTE: Add "BufReadPre" to the autocmd events to also intercept files given on the command line, e.g.
+---- `nvim myfile.txt`
+--vim.api.nvim_create_autocmd({ "BufNew" }, {
+-- group = augroup,
+-- callback = function(args)
+-- ---@type string
+-- local path = args.match
+-- ---@type integer
+-- local bufnr = args.buf
+--
+-- ---@type string? The file extension if detected
+-- local extension = vim.fn.fnamemodify(path, ":e")
+-- ---@type string? The filename if detected
+-- local filename = vim.fn.fnamemodify(path, ":t")
+--
+-- ---Open a given file path in a given program and remove the buffer for the file.
+-- ---@param buf integer The buffer handle for the opening buffer
+-- ---@param fpath string The file path given to the program
+-- ---@param fname string The file name used in notifications
+-- ---@param prog string The program to execute against the file path
+-- local function open_in_prog(buf, fpath, fname, prog)
+-- vim.notify(string.format("Opening `%s` in `%s`", fname, prog), vim.log.levels.INFO, {
+-- title = "Open File in External Program",
+-- ---@param win integer The window handle
+-- on_open = function(win)
+-- vim.api.nvim_buf_set_option(vim.api.nvim_win_get_buf(win), "filetype", "markdown")
+-- end,
+-- })
+-- local mods = require("user.mods")
+-- local nvim_ver = mods.get_nvim_version()
+--
+-- local version_major, version_minor = string.match(nvim_ver, "(%d+)%.(%d+)")
+-- version_major = tonumber(version_major)
+-- version_minor = tonumber(version_minor)
+--
+-- if version_major > 0 or (version_major == 0 and version_minor >= 10) then
+-- vim.system({ prog, fpath }, { detach = true })
+-- else
+-- vim.fn.jobstart({ prog, fpath }, { detach = true })
+-- end
+-- vim.api.nvim_buf_delete(buf, { force = true })
+-- end
+--
+-- local extension_callbacks = {
+-- ["pdf"] = function(buf, fpath, fname)
+-- open_in_prog(buf, fpath, fname, "zathura")
+-- end,
+-- ["epub"] = function(buf, fpath, fname)
+-- open_in_prog(buf, fpath, fname, "zathura")
+-- end,
+-- ["mobi"] = "pdf",
+-- ["png"] = function(buf, fpath, fname)
+-- open_in_prog(buf, fpath, fname, "vimiv")
+-- end,
+-- ["jpg"] = "png",
+-- ["mp4"] = function(buf, fpath, fname)
+-- open_in_prog(buf, fpath, fname, "vlc")
+-- end,
+-- ["gif"] = "mp4",
+-- }
+--
+-- ---Get the extension callback for a given extension. Will do a recursive lookup if an extension callback is actually
+-- ---of type string to get the correct extension
+-- ---@param ext string A file extension. Example: `png`.
+-- ---@return fun(bufnr: integer, path: string, filename: string?) extension_callback The extension callback to invoke, expects a buffer handle, file path, and filename.
+-- local function extension_lookup(ext)
+-- local callback = extension_callbacks[ext]
+-- if type(callback) == "string" then
+-- callback = extension_lookup(callback)
+-- end
+-- return callback
+-- end
+--
+-- if extension ~= nil and not extension:match("^%s*$") and intercept_file_open then
+-- local callback = extension_lookup(extension)
+-- if type(callback) == "function" then
+-- callback(bufnr, path, filename)
+-- end
+-- end
+-- end,
+--})
+
+--------------------------------------------------
+
+-- Delete [No Name] buffers
+vim.api.nvim_create_autocmd("BufHidden", {
+ desc = "Delete [No Name] buffers",
+ callback = function(event)
+ if event.file == "" and vim.bo[event.buf].buftype == "" and not vim.bo[event.buf].modified then
+ vim.schedule(function()
+ pcall(vim.api.nvim_buf_delete, event.buf, {})
+ end)
+ end
+ end,
+})
+
+--------------------------------------------------
+
+local codeRunnerEnabled = false
+
+function M.toggleCodeRunner()
+ codeRunnerEnabled = not codeRunnerEnabled
+ if codeRunnerEnabled then
+ print("Code Runner enabled")
+ M.RunCode() -- Execute when enabled
+ else
+ print("Code Runner disabled")
+ -- Close the terminal window when disabled
+ local buffers = vim.fn.getbufinfo()
+
+ for _, buf in ipairs(buffers) do
+ local type = vim.api.nvim_buf_get_option(buf.bufnr, "buftype")
+ if type == "terminal" then
+ vim.api.nvim_command("silent! bdelete " .. buf.bufnr)
+ end
+ end
+ end
+end
+
+local function substitute(cmd)
+ cmd = cmd:gsub("%%", vim.fn.expand("%"))
+ cmd = cmd:gsub("$fileBase", vim.fn.expand("%:r"))
+ cmd = cmd:gsub("$filePath", vim.fn.expand("%:p"))
+ cmd = cmd:gsub("$file", vim.fn.expand("%"))
+ cmd = cmd:gsub("$dir", vim.fn.expand("%:p:h"))
+ cmd = cmd:gsub("#", vim.fn.expand("#"))
+ cmd = cmd:gsub("$altFile", vim.fn.expand("#"))
+
+ return cmd
+end
+
+function M.RunCode()
+ if not codeRunnerEnabled then
+ print("Code Runner is currently disabled. Toggle it on to execute code.")
+ return
+ end
+ local file_extension = vim.fn.expand("%:e")
+ local selected_cmd = ""
+ local supported_filetypes = {
+ html = {
+ default = "%",
+ },
+ c = {
+ default = "gcc % -o $fileBase && ./$fileBase",
+ debug = "gcc -g % -o $fileBase && ./$fileBase",
+ },
+ cs = {
+ default = "dotnet run",
+ },
+ cpp = {
+ default = "g++ % -o $fileBase && ./$fileBase",
+ debug = "g++ -g % -o ./$fileBase",
+ competitive = "g++ -std=c++17 -Wall -DAL -O2 % -o $fileBase && $fileBase<input.txt",
+ },
+ py = {
+ default = "python %",
+ },
+ go = {
+ default = "go run %",
+ },
+ java = {
+ default = "java %",
+ },
+ js = {
+ default = "node %",
+ debug = "node --inspect %",
+ },
+ lua = {
+ default = "lua %",
+ },
+ ts = {
+ default = "tsc % && node $fileBase",
+ },
+ rs = {
+ default = "rustc % && $fileBase",
+ },
+ php = {
+ default = "php %",
+ },
+ r = {
+ default = "Rscript %",
+ },
+ jl = {
+ default = "julia %",
+ },
+ rb = {
+ default = "ruby %",
+ },
+ pl = {
+ default = "perl %",
+ },
+ }
+
+ local term_cmd = "bot 10 new | term "
+ local choices = {}
+
+ -- Add 'default' as the first option if available
+ if supported_filetypes[file_extension]["default"] then
+ table.insert(choices, "default")
+ end
+
+ -- Add 'debug' as the second option if available
+ if supported_filetypes[file_extension]["debug"] then
+ table.insert(choices, "debug")
+ end
+
+ -- Add other available options
+ for key, _ in pairs(supported_filetypes[file_extension]) do
+ if key ~= "default" and key ~= "debug" then
+ table.insert(choices, key)
+ end
+ end
+ if #choices == 0 then
+ vim.notify("It doesn't contain any command", vim.log.levels.WARN, { title = "Code Runner" })
+ elseif #choices == 1 then
+ selected_cmd = supported_filetypes[file_extension][choices[1]]
+ vim.cmd(term_cmd .. substitute(selected_cmd))
+ else
+ vim.ui.select(choices, {
+ prompt = "Choose a command: ",
+ layout_config = {
+ height = 10,
+ width = 40,
+ prompt_position = "top",
+ -- other options as required
+ },
+ }, function(choice)
+ selected_cmd = supported_filetypes[file_extension][choice]
+ if selected_cmd then
+ vim.cmd(term_cmd .. substitute(selected_cmd))
+ end
+ end)
+ end
+
+ if not supported_filetypes[file_extension] then
+ vim.notify("The filetype isn't included in the list", vim.log.levels.WARN, { title = "Code Runner" })
+ end
+end
+
+--------------------------------------------------
+
+-- Run executable file
+local interpreters = {
+ python = "python",
+ lua = "lua",
+ bash = "bash",
+ zsh = "zsh",
+ perl = "perl",
+ ruby = "ruby",
+ node = "node",
+ rust = "rust",
+ php = "php",
+}
+
+function M.RunCurrentFile()
+ local file_path = vim.fn.expand("%:p")
+ local file = io.open(file_path, "r")
+
+ if not file then
+ print("Error: Unable to open the file")
+ return
+ end
+
+ local shebang = file:read()
+ file:close()
+
+ local interpreter = shebang:match("#!%s*(.-)$")
+ if not interpreter then
+ print("Error: No shebang line found in the file")
+ return
+ end
+
+ -- Remove leading spaces and any arguments, extracting the interpreter name
+ interpreter = interpreter:gsub("^%s*([^%s]+).*", "%1")
+
+ local cmd = interpreters[interpreter]
+
+ if not cmd then
+ cmd = interpreter -- Set the command to the interpreter directly
+ end
+
+ -- Run the file using the determined interpreter
+ vim.fn.jobstart(cmd .. " " .. file_path, {
+ cwd = vim.fn.expand("%:p:h"),
+ })
+end
+
+--------------------------------------------------
+
+-- Close all floating windows
+vim.api.nvim_create_user_command("CloseFloatingWindows", function(opts)
+ for _, window_id in ipairs(vim.api.nvim_list_wins()) do
+ -- If window is floating
+ if vim.api.nvim_win_get_config(window_id).relative ~= "" then
+ -- Force close if called with !
+ vim.api.nvim_win_close(window_id, opts.bang)
+ end
+ end
+end, { bang = true, nargs = 0 })
+
+--------------------------------------------------
+
+
+-- Platform detection
+local uname = vim.loop.os_uname().sysname
+local has = vim.fn.has
+
+local is_mac = has("mac") == 1
+local is_linux = uname == "Linux"
+local is_windows = has("win32") == 1 or uname:find("Windows")
+local is_wsl = has("wsl") == 1 or (uname:find("Linux") and (os.getenv("WSL_DISTRO_NAME") ~= nil))
+local is_termux = has("termux") == 1 or (os.getenv("PREFIX") and os.getenv("PREFIX"):find("com.termux"))
+local os_name = (is_mac and "mac") or (is_linux and "linux") or (is_windows and "windows") or (is_wsl and "wsl") or (is_termux and "termux") or uname:lower()
+
+-- Check if a command exists
+local function command_exists(cmd)
+ local handle = io.popen(cmd .. " --version 2>/dev/null")
+ if handle then
+ local result = handle:read("*a")
+ handle:close()
+ return result ~= ""
+ end
+ return false
+end
+
+-- Detect clipboard tool on Linux
+local function detect_clipboard_tool()
+ if command_exists("xclip") then return "xclip" end
+ if command_exists("xsel") then return "xsel" end
+ if command_exists("wl-copy") and command_exists("wl-paste") then return "wl-clipboard" end
+ return nil
+end
+
+-- OSC52 clipboard copy fallback
+local function osc52_copy(text)
+ local encoded = vim.fn.system("base64 | tr -d '\n'", text)
+ io.write(string.format("\027]52;c;%s\007", encoded))
+end
+
+---- Set clipboard
+--function set_clipboard(text)
+-- if not text or text == "" then return end
+--
+-- if is_mac then
+-- local handle = io.popen("pbcopy", "w")
+-- if handle then
+-- handle:write(text)
+-- handle:close()
+-- end
+-- elseif is_linux then
+-- local tool = detect_clipboard_tool()
+-- if tool == "xclip" then
+-- local handle = io.popen("xclip -selection clipboard", "w")
+-- if handle then handle:write(text) handle:close() end
+-- elseif tool == "xsel" then
+-- local handle = io.popen("xsel --clipboard --input", "w")
+-- if handle then handle:write(text) handle:close() end
+-- elseif tool == "wl-clipboard" then
+-- local handle = io.popen("wl-copy", "w")
+-- if handle then handle:write(text) handle:close() end
+-- else
+-- osc52_copy(text)
+-- vim.notify("Using OSC52 for clipboard (install xclip, xsel, or wl-clipboard for better support)", vim.log.levels.INFO)
+-- end
+-- elseif is_wsl or is_windows then
+-- local handle = io.popen("clip", "w")
+-- if handle then handle:write(text) handle:close() end
+-- elseif is_termux then
+-- local handle = io.popen("termux-clipboard-set", "w")
+-- if handle then handle:write(text) handle:close() end
+-- else
+-- vim.notify("No clipboard support for OS: " .. os_name, vim.log.levels.WARN)
+-- end
+--end
+--
+---- Clipboard sync autocmd setup
+--local function setup_clipboard_sync()
+-- local ok, Job = pcall(require, "plenary.job")
+-- if not ok then
+-- -- plenary not available, skip
+-- return
+-- end
+--
+-- vim.api.nvim_create_augroup("clipboard_sync", { clear = true })
+-- vim.api.nvim_create_autocmd("TextYankPost", {
+-- group = "clipboard_sync",
+-- desc = "Sync yanked text to system clipboard",
+-- pattern = "*",
+-- callback = function()
+-- local text = vim.fn.getreg("\"")
+-- if text ~= nil and text ~= "" then
+-- set_clipboard(text)
+-- end
+-- end,
+-- })
+--end
+--setup_clipboard_sync()
+--
+---- Terminal clear function (optional)
+--function clear_terminal()
+-- vim.opt.scrollback = 1
+-- vim.api.nvim_feedkeys("i", "n", false)
+-- vim.api.nvim_feedkeys("clear\r", "n", false)
+-- vim.api.nvim_feedkeys("\x1b", "n", false)
+-- vim.api.nvim_feedkeys("i", "n", false)
+-- vim.defer_fn(function()
+-- vim.opt.scrollback = 10000
+-- end, 100)
+--end
+--
+---- Get clipboard content (optional)
+--function GetClipboard()
+-- local handle
+--
+-- if is_mac then
+-- handle = io.popen("pbpaste", "r")
+-- elseif is_linux then
+-- local tool = detect_clipboard_tool()
+-- if tool == "xclip" then
+-- handle = io.popen("xclip -selection clipboard -o", "r")
+-- elseif tool == "xsel" then
+-- handle = io.popen("xsel --clipboard --output", "r")
+-- elseif tool == "wl-clipboard" then
+-- handle = io.popen("wl-paste", "r")
+-- end
+-- elseif is_wsl or is_windows then
+-- handle = io.popen("powershell.exe Get-Clipboard", "r")
+-- elseif is_termux then
+-- handle = io.popen("termux-clipboard-get", "r")
+-- end
+--
+-- if handle then
+-- local result = handle:read("*a")
+-- handle:close()
+-- return result or ""
+-- end
+--
+-- return ""
+--end
+
+--------------------------------------------------
+
+-- Cross-platform file/URL opener
+function M.open_file_or_url(path)
+ local commands = {
+ mac = string.format('open "%s"', path),
+ linux = string.format('xdg-open "%s" &', path),
+ wsl = string.format('wslview "%s" &', path),
+ windows = string.format('start "" "%s"', path),
+ termux = string.format('am start -a android.intent.action.VIEW -d "%s"', path),
+ }
+
+ local cmd = commands[M.os_name]
+ if cmd then
+ os.execute(cmd)
+ else
+ vim.notify("No supported file opener for this OS: " .. tostring(M.os_name), vim.log.levels.WARN)
+ end
+end
+
+--------------------------------------------------
+
+-- Automcmd to close netrw buffer when file is opened
+vim.api.nvim_create_autocmd("FileType", {
+ pattern = "netrw",
+ callback = function()
+ vim.api.nvim_create_autocmd("BufEnter", {
+ once = true,
+ callback = function()
+ if vim.bo.filetype ~= "netrw" then
+ for _, buf in ipairs(vim.api.nvim_list_bufs()) do
+ if vim.bo[buf].filetype == "netrw" then
+ vim.api.nvim_buf_delete(buf, { force = true })
+ end
+ end
+ end
+ end,
+ })
+ end,
+})
+
+--------------------------------------------------
+
+-- Autocomplete
+vim.api.nvim_create_autocmd("InsertCharPre", {
+ callback = function()
+ -- Exit the autocmd if nvim-cmp is present
+ local cmp_is_present, _ = pcall(require, "cmp")
+ if cmp_is_present then
+ return
+ end
+
+ -- Skip unwanted buffer types (Telescope, NvimTree, etc.)
+ local ft = vim.bo.filetype
+ local bt = vim.bo.buftype
+ local ignore_ft = {
+ "TelescopePrompt",
+ "prompt",
+ "nofile",
+ "terminal",
+ "help",
+ "quickfix",
+ "lazy",
+ "neo-tree",
+ "NvimTree",
+ "starter",
+ "packer",
+ }
+
+ if bt ~= "" or vim.tbl_contains(ignore_ft, ft) then
+ return
+ end
+
+ local col = vim.fn.col(".")
+ local line = vim.fn.getline(".")
+ local function safe_sub(i)
+ return line:sub(i, i)
+ end
+
+ local prev3 = safe_sub(col - 3)
+ local prev2 = safe_sub(col - 2)
+ local prev1 = safe_sub(col - 1)
+ local curr = vim.v.char
+
+ if curr:match("%w") and prev3:match("%W") and prev2:match("%w") and prev1:match("%w") then
+ vim.api.nvim_feedkeys(
+ vim.api.nvim_replace_termcodes("<C-n>", true, true, true),
+ "n",
+ true
+ )
+ end
+ end,
+})
+--------------------------------------------------
+
+M.has_treesitter = function ( bufnr )
+ if not bufnr then
+ bufnr = vim.api.nvim_get_current_buf()
+ end
+
+ local highlighter = require( "vim.treesitter.highlighter" )
+
+ if highlighter.active[ bufnr ] then
+ return true
+ else
+ return false
+ end
+end
+
+M.parse_treesitter = function ( bufnr, range )
+ local parser = vim.treesitter.get_parser( bufnr )
+
+ -- XXX https://neovim.io/doc/user/treesitter.html#LanguageTree%3Aparse()
+ parser:parse( range )
+end
+
+-- ...
+return M
diff --git a/common/nvim/lua/user/opts.lua b/common/nvim/lua/user/opts.lua
new file mode 100755
index 0000000..bac80c3
--- /dev/null
+++ b/common/nvim/lua/user/opts.lua
@@ -0,0 +1,438 @@
+-- ============================================================================
+-- Options
+-- ============================================================================
+
+local uname = vim.loop.os_uname()
+local system = uname.sysname
+local shell = nil
+
+if system == "Windows_NT" then
+ -- Windows options
+ if vim.fn.executable("pwsh") == 1 then
+ shell = "pwsh"
+ elseif vim.fn.executable("powershell") == 1 then
+ shell = "powershell"
+ elseif vim.fn.executable("bash") == 1 then
+ shell = "bash"
+ end
+else
+ -- Unix-like systems: use the user's default shell
+ local env_shell = os.getenv("SHELL")
+ if env_shell and vim.fn.executable(env_shell) == 1 then
+ shell = env_shell
+ else
+ -- fallback logic
+ if vim.fn.executable("zsh") == 1 then
+ shell = vim.fn.exepath("zsh")
+ elseif vim.fn.executable("bash") == 1 then
+ shell = vim.fn.exepath("bash")
+ end
+ end
+end
+
+-- Finally set the shell if we found one
+if shell then
+ vim.o.shell = shell
+end
+
+-- Core Settings
+vim.opt.encoding = 'utf-8'
+vim.opt.fileencoding = 'utf-8'
+vim.scriptencoding = 'utf-8'
+vim.opt.termguicolors = true
+vim.opt.mouse = 'a'
+vim.opt.clipboard = 'unnamedplus'
+vim.opt.hidden = true
+vim.opt.updatetime = 300
+vim.opt.timeoutlen = 500
+vim.opt.ttimeoutlen = 10
+
+-- Display
+vim.opt.number = true
+vim.opt.relativenumber = true
+vim.opt.cursorline = true
+vim.opt.signcolumn = 'yes'
+vim.opt.showcmd = true
+vim.opt.showmode = true
+vim.opt.showmatch = true
+vim.opt.laststatus = 2
+vim.opt.cmdheight = 1
+vim.opt.scrolloff = 5
+vim.opt.sidescrolloff = 5
+vim.opt.display = 'lastline'
+
+-- Indentation
+vim.opt.autoindent = true
+vim.opt.smartindent = true
+vim.opt.expandtab = true
+vim.opt.tabstop = 2
+vim.opt.shiftwidth = 2
+vim.opt.softtabstop = 2
+vim.opt.shiftround = true
+
+-- Search
+vim.opt.hlsearch = true
+vim.opt.incsearch = true
+vim.opt.ignorecase = true
+vim.opt.smartcase = true
+vim.opt.inccommand = 'split'
+
+-- Window Management
+vim.opt.splitright = true
+vim.opt.splitbelow = true
+vim.opt.winminwidth = 1
+vim.opt.winwidth = 5
+
+-- File Handling
+vim.opt.autoread = true
+--vim.opt.autowrite = true
+vim.opt.backup = true
+vim.opt.backupdir = vim.fn.stdpath('cache') .. '/backup//'
+vim.opt.directory = vim.fn.stdpath('cache') .. '/swap//'
+vim.opt.undofile = true
+vim.opt.undodir = vim.fn.stdpath('cache') .. '/undo//'
+vim.opt.swapfile = false
+
+-- Wildmenu
+vim.opt.wildmenu = true
+vim.opt.wildmode = 'longest:full,full'
+vim.opt.wildignorecase = true
+vim.opt.wildignore = '*.o,*.obj,.git,*.rbc,*.pyc,__pycache__'
+
+
+vim.scriptencoding = "utf-8" --
+vim.opt.encoding = "utf-8" --
+vim.opt.fileencoding = "utf-8" --
+vim.g.python3_host_prog = "/usr/bin/python3" --
+vim.g.loaded_python3_provider = 1 --
+vim.g.sh_noisk = 1 -- iskeyword word boundaries when editing a 'sh' file
+vim.o.autochdir = true
+--vim.o.writeany= true
+
+-- Clipboard
+vim.opt.clipboard:append({ "unnamedplus" }) -- Install xclip or this will slowdown startup
+
+-- Behaviour
+vim.opt.backspace = { "start", "eol", "indent" } -- Make backspace work as you would expect.
+vim.opt.hidden = true -- Switch between buffers without having to save first.
+vim.opt.conceallevel = 2
+vim.opt.splitbelow = true -- make split put the new buffer below the current buffer
+vim.opt.splitright = true -- make vsplit put the new buffer on the right of the current buffer
+vim.opt.scrolloff = 8 --
+vim.opt.sidescrolloff = 8 -- how many lines to scroll when using the scrollbar
+vim.opt.autoread = true -- reload files if changed externally
+vim.opt.display = "lastline" -- Show as much as possible of the last line.
+vim.opt.inccommand = "split" --
+vim.opt.ttyfast = true -- Faster redrawing.
+vim.opt.lazyredraw = false -- Only redraw when necessary
+vim.opt.keywordprg = ":help" -- :help options
+vim.opt.ruler = true --
+vim.opt.errorbells = false --
+vim.opt.list = true -- Show non-printable characters.
+vim.opt.showmatch = true --
+vim.opt.matchtime = 3 --
+vim.opt.showbreak = "↪ " --
+vim.opt.linebreak = true --
+vim.opt.exrc = true --
+--vim.opt.autochdir = true -- or use this to use <:e> to create a file in current directory
+vim.opt.autoread = true -- if a file is changed outside of vim, automatically reload it without asking
+--vim.opt.notimeout = true -- Timeout on keycodes and not mappings
+vim.opt.ttimeout = true -- Makes terminal vim work sanely
+vim.opt.ttimeoutlen = 10 --
+--vim.opt.timeoutlen = 100 -- time to wait for a mapped sequence to complete (in milliseconds)
+--vim.cmd([[set diffopt = vertical = true]]) -- diffs are shown side-by-side not above/below
+
+-- Indent/tab
+vim.opt.breakindent = true --
+vim.opt.autoindent = true -- Indent according to previous line.
+vim.opt.copyindent = true -- Copy indent from the previous line
+vim.opt.smarttab = false --
+vim.opt.tabstop = 2 --
+vim.opt.expandtab = true -- Indent according to previous line.
+--vim.opt.expandtab = true -- Use spaces instead of tabs.
+vim.opt.softtabstop = 2 -- Tab key indents by 2 spaces.
+vim.opt.shiftwidth = 2 -- >> indents by 2 spaces.
+vim.opt.shiftround = true -- >> indents to next multiple of 'shiftwidth'.
+vim.opt.smartindent = true -- smart indent
+
+-- Column/statusline/Cl
+
+-- Enable number and relativenumber by default
+vim.opt.number = true
+vim.opt.relativenumber = true
+
+-- Entering insert mode: disable relativenumber
+vim.api.nvim_create_autocmd("InsertEnter", {
+ callback = function()
+ vim.opt.relativenumber = false
+ end,
+})
+
+-- Leaving insert mode: enable relativenumber
+vim.api.nvim_create_autocmd("InsertLeave", {
+ callback = function()
+ vim.opt.relativenumber = true
+ end,
+})
+
+vim.opt.title = true --
+--vim.opt.colorcolumn = "+1" --
+vim.opt.signcolumn = "yes:1" -- always show the sign column
+--vim.opt.signcolumn = "yes:" .. vim.o.numberwidth
+--vim.opt.signcolumn = "number"
+--vim.opt.signcolumn = "no" --
+vim.opt.laststatus = 3 -- " Always show statusline.
+vim.opt.showmode = true -- Show current mode in command-line, example: -- INSERT -- mode
+vim.opt.showcmd = true -- Show the command in the status bar
+vim.opt.cmdheight = 1 --
+--vim.opt.cmdheight = 0 --
+vim.opt.report = 0 -- Always report changed lines.
+--local autocmd = vim.api.nvim_create_autocmd
+--autocmd("bufenter", {
+-- pattern = "*",
+-- callback = function()
+-- if vim.bo.ft ~= "terminal" then
+-- vim.opt.statusline = "%!v:lua.require'ui.statusline'.run()"
+-- else
+-- vim.opt.statusline = "%#normal# "
+-- end
+-- end,
+--})
+---- With vertical splits, the statusline would still show up at the
+---- bottom of the split. A quick fix is to just set the statusline
+---- to empty whitespace (it can't be an empty string because then
+---- it'll get replaced by the default stline).
+--vim.opt.stl = " "
+
+-- Backup/undo/swap
+local prefix = vim.env.XDG_CONFIG_HOME or vim.fn.expand("~/.config")
+--vim.opt.undodir = os.getenv("HOME") .. "/.vim/undodir"
+--vim.opt.undodir = { prefix .. "/nvim/tmp/.undo//" }
+vim.opt.undodir = os.getenv("HOME") .. "/.vim/undodir"
+vim.opt.directory = { prefix .. "/nvim/tmp/.swp//" }
+vim.opt.backupdir = { prefix .. "/nvim/tmp/.backup//" }
+vim.opt.undofile = true --
+vim.opt.swapfile = true --
+vim.opt.backup = true --
+--vim.opt.backupcopy =
+-- Add timestamp as extension for backup files
+vim.api.nvim_create_autocmd("BufWritePre", {
+ group = vim.api.nvim_create_augroup("timestamp_backupext", { clear = true }),
+ desc = "Add timestamp to backup extension",
+ pattern = "*",
+ callback = function()
+ vim.opt.backupext = "-" .. vim.fn.strftime("%Y%m%d%H%M")
+ end,
+})
+
+-- Format
+--vim.opt.textwidth = 80 --
+vim.opt.isfname:append("@-@")
+vim.cmd([[let &t_Cs = "\e[4:3m"]]) -- Undercurl
+vim.cmd([[let &t_Ce = "\e[4:0m"]]) --
+vim.opt.path:append({ "**" }) -- Finding files - Search down into subfolder
+vim.cmd("set whichwrap+=<,>,[,],h,l") --
+vim.cmd([[set iskeyword+=-]]) --
+--vim.cmd([[set formatoptions-=cro]]) -- TODO: this doesn't seem to work
+vim.opt.formatoptions = vim.opt.formatoptions
+ - "t" -- wrap with text width
+ + "c" -- wrap comments
+ + "r" -- insert comment after enter
+ - "o" -- insert comment after o/O
+ - "q" -- allow formatting of comments with gq
+ - "a" -- format paragraphs
+ + "n" -- recognized numbered lists
+ - "2" -- use indent of second line for paragraph
+ + "l" -- long lines are not broken
+ + "j" -- remove comment when joining lines
+vim.opt.wrapscan = true -- " Searches wrap around end-of-file.
+--vim.wo.number = true --
+--vim.opt.wrap = false -- No Wrap lines
+--vim.opt.foldmethod = 'manual' --
+--vim.opt.foldmethod = "expr" --
+vim.opt.foldmethod = "manual"
+vim.opt.foldlevel = 3
+vim.opt.confirm = false
+--vim.opt.shortmess:append("sI")
+--vim.opt.shortmess = "a"
+--vim.opt.shortmess = "sI"
+--vim.o.shortmess = vim.o.shortmess:gsub('s', '')
+vim.opt.shortmess = table.concat({ -- Use abbreviations and short messages in command menu line.
+ "f", -- Use "(3 of 5)" instead of "(file 3 of 5)".
+ "i", -- Use "[noeol]" instead of "[Incomplete last line]".
+ "l", -- Use "999L, 888C" instead of "999 lines, 888 characters".
+ "m", -- Use "[+]" instead of "[Modified]".
+ "n", -- Use "[New]" instead of "[New File]".
+ "r", -- Use "[RO]" instead of "[readonly]".
+ "w", -- Use "[w]", "[a]" instead of "written", "appended".
+ "x", -- Use "[dos]", "[unix]", "[mac]" instead of "[dos format]", "[unix format]", "[mac format]".
+ "o", -- Overwrite message for writing a file with subsequent message.
+ "O", -- Message for reading a file overwrites any previous message.
+ "s", -- Disable "search hit BOTTOM, continuing at TOP" such messages.
+ "t", -- Truncate file message at the start if it is too long.
+ "T", -- Truncate other messages in the middle if they are too long.
+ "I", -- Don't give the :intro message when starting.
+ "c", -- Don't give ins-completion-menu messages.
+ "F", -- Don't give the file info when editing a file.
+})
+vim.opt.fillchars = {
+ horiz = "─",
+ horizup = "┴",
+ horizdown = "┬",
+ vert = "│",
+ vertleft = "┤",
+ vertright = "├",
+ verthoriz = "┼",
+ foldopen = "",
+ foldsep = "│",
+ foldclose = "",
+ fold = "─",
+ eob = " ",
+ --diff = "┃",
+ diff = "░",
+ msgsep = "━",
+ --msgsep = "‾",
+}
+vim.opt.listchars = { tab = "▸ ", trail = "·" } --
+--vim.opt.fillchars:append({ eob = " " }) -- remove the ~ from end of buffer
+vim.opt.modeline = true --
+vim.opt.modelines = 3 -- modelines (comments that set vim options on a per-file basis)
+--vim.opt.modelineexpr = true
+--vim.opt.nofoldenable = true -- turn folding off
+--vim.opt.foldenable = false -- turn folding off
+vim.o.showtabline = 2
+
+-- Highlights
+vim.opt.incsearch = true -- Highlight while searching with / or ?.
+vim.opt.hlsearch = true -- Keep matches highlighted.
+vim.opt.ignorecase = true -- ignore case in search patterns UNLESS /C or capital in search
+vim.opt.smartcase = true -- smart case
+vim.opt.synmaxcol = 200 -- Only highlight the first 200 columns.
+--vim.opt.winblend = 30
+--vim.opt.winblend = 5
+vim.opt.wildoptions = "pum" --
+--vim.opt.pumblend = 5 --
+vim.opt.pumblend = 12 --
+--vim.opt.pumblend=15
+vim.opt.pumheight = 10 -- pop up menu height
+
+-- Better Completion
+vim.opt.complete = { ".", "w", "b", "u", "t" } --
+--vim.opt.completeopt = { "longest,menuone,preview" } --
+vim.opt.completeopt = { "menu", "menuone", "noselect" }
+--vim.opt.completeopt = { "menuone", "noselect" } -- mostly just for cmp
+--vim.opt.completeopt = { "menu", "menuone", "noselect" } --
+
+-- Spellcheck
+vim.opt.spelllang = { "en_gb", "en_us" } -- Set a list of preferred dictionaries
+vim.opt.spell = true
+--vim.opt.spellfile = "~/.config/nvim/spell/en.utf-8.add" -- Specify a personal dictionary file
+
+-- Wildmenu completion --
+vim.opt.wildmenu = true --
+vim.opt.wildmode = { "list:longest" } --
+vim.opt.wildignore:append({ ".hg", ".git", ".svn" }) -- Version control
+vim.opt.wildignore:append({ "*.aux", "*.out", "*.toc" }) -- LaTeX intermediate files
+vim.opt.wildignore:append({ "*.jpg", "*.bmp", "*.gif", "*.png", "*.jpeg" }) -- binary images
+vim.opt.wildignore:append({ "*.o", "*.obj", "*.exe", "*.dll", "*.manifest" }) -- compiled object files
+vim.opt.wildignore:append({ "*.spl" }) -- compiled spelling word lists
+vim.opt.wildignore:append({ "*.sw?" }) -- Vim swap files
+vim.opt.wildignore:append({ "*.DS_Store" }) -- OSX bullshit
+vim.opt.wildignore:append({ "*.luac" }) -- Lua byte code
+vim.opt.wildignore:append({ "migrations" }) -- Django migrations
+vim.opt.wildignore:append({ "*.pyc" }) -- Python byte code
+vim.opt.wildignore:append({ "*.orig" }) -- Merge resolution files
+vim.opt.wildignore:append({ "*/node_modules/*" }) --
+
+-- Shada
+vim.opt.shada = "!,'1000,f1,<1000,s100,:1000,/1000,h"
+
+-- Sessions
+vim.opt.sessionoptions = "blank,buffers,curdir,folds,help,tabpages,winsize,winpos,terminal"
+--vim.opt.sessionoptions = "curdir,folds,help,options,tabpages,winsize,winpos,terminal,globals" --
+--vim.opt.sessionoptions = "buffers,curdir,folds,help,tabpages,winsize,winpos,terminal"
+--vim.opt.sessionoptions:remove({ "blank", "buffers", "globals" })
+
+-- Netrw file tree
+vim.g.netrw_browse_split = 0
+vim.g.netrw_banner = 0
+vim.g.netrw_winsize = 25
+
+-- " Load indent files, to automatically do language-dependent indenting.
+--vim.cmd([[
+-- "filetype plugin indent on
+--]])
+vim.cmd("filetype plugin on")
+vim.cmd("filetype indent off")
+
+--vim.cmd([[
+-- "autocmd BufEnter * :syntax sync fromstart
+-- "syntax enable
+-- "set nocompatible
+-- "autocmd FileType lua set comments=s1:---,m:--,ex:--
+--]])
+
+
+-- Fast macros without lazyredraw
+vim.cmd([[
+ set re=0
+ nnoremap @ <cmd>execute "noautocmd norm! " . v:count1 . "@" . getcharstr()<cr>
+ xnoremap @ :<C-U>execute "noautocmd '<,'>norm! " . v:count1 . "@" . getcharstr()<cr>
+]])
+
+-- Stop annoying auto commenting on new lines
+vim.cmd([[
+ augroup annoying
+ au!
+ au BufEnter * set fo-=c fo-=r fo-=o
+ augroup end
+]])
+
+-- Cursorline
+vim.cmd([[ " Only show cursorline in the current window and in normal mode
+ augroup cline
+ au!
+ au WinLeave,InsertEnter * set nocursorline
+ au WinEnter,InsertLeave * set cursorline
+ augroup END
+]])
+vim.opt.cursorline = true --
+vim.opt.guicursor = "i:ver100,r:hor100" --
+
+-- Trailing whitespace
+vim.cmd([[ " Only show in insert mode
+ augroup trailing
+ au!
+ au InsertEnter * :set listchars-=trail:⌴
+ au InsertLeave * :set listchars+=trail:⌴
+ augroup END
+]])
+
+-- Line Return
+vim.cmd([[ " Return to the same line when we reopen a file
+ augroup line_return
+ au!
+ au BufReadPost *
+ \ if line("'\"") > 0 && line("'\"") <= line("$") |
+ \ execute 'normal! g`"zvzz' |
+ \ endif
+ augroup END
+]])
+
+-- Enable mouse scrollback
+vim.cmd([[
+ set mouse=a
+ tnoremap <Esc> <C-\><C-n>
+ tnoremap <c-b> <c-\><c-n>
+ function! ClearTerminal()
+ set scrollback=1
+ let &g:scrollback=1
+ echo &scrollback
+ call feedkeys("\i")
+ call feedkeys("clear\<CR>")
+ call feedkeys("\<C-\>\<C-n>")
+ call feedkeys("\i")
+ sleep 100m
+ let &scrollback=s:scroll_value
+ endfunction
+]])
diff --git a/common/nvim/lua/user/view.lua b/common/nvim/lua/user/view.lua
new file mode 100755
index 0000000..f243194
--- /dev/null
+++ b/common/nvim/lua/user/view.lua
@@ -0,0 +1,180 @@
+-- ============================================================================
+-- View/UI
+-- ============================================================================
+
+local M = {}
+
+-- List of available themes (for reference or user selection UI)
+M.available_themes = {
+ "nightfly", "ayu", "onedark", "doom-one", "nvimgelion", "github_dark", "tokyonight", "bamboo", "oxocarbon"
+}
+
+-- Configuration
+local default_colorscheme = "tokyonight"
+local fallback_colorscheme = "default"
+
+-- Diagnostic icons
+local Signs = {
+ Error = "✘",
+ Warn = "",
+ Hint = "◉",
+ Info = "",
+}
+
+-- Setup Function
+function M.setup()
+ -- Truecolor & syntax
+ vim.opt.termguicolors = true
+ vim.cmd("syntax on")
+
+ -- Colorscheme setup with fallback
+ local ok = pcall(vim.cmd, "colorscheme " .. default_colorscheme)
+ if not ok then
+ vim.cmd("colorscheme " .. fallback_colorscheme)
+ end
+
+ -- Optional: Tokyonight configuration
+ pcall(function()
+ require("tokyonight").setup({
+ style = "night",
+ transparent = true,
+ transparent_sidebar = true,
+ dim_inactive = false,
+ styles = {
+ sidebars = "transparent",
+ floats = "transparent",
+ },
+ })
+ end)
+
+ -- Highlight groups
+ local highlights = {
+ -- Core UI
+ { group = "Normal", options = { bg = "none" } },
+ { group = "NormalNC", options = { bg = "none" } },
+ { group = "NormalFloat", options = { bg = "none" } },
+ { group = "Float", options = { bg = "none" } },
+ { group = "FloatBorder", options = { bg = "none", fg = "#7f8493" } },
+ { group = "StatusLine", options = { bg = "none" } },
+ { group = "TabLine", options = { bg = "#333842", bold = true } },
+ { group = "TabLineSel", options = { bg = "#333842", bold = true } },
+ { group = "TabLineFill", options = { bg = "none", bold = true } },
+ { group = "WinBar", options = { bg = "none", bold = true } },
+ { group = "WinBarNC", options = { bg = "none" } },
+ { group = "WinSeparator", options = { bg = "none", fg = "#444b62", bold = true } },
+ { group = "EndOfBuffer", options = { bg = "none", fg = "#7f8493" } },
+ { group = "NonText", options = { bg = "none", fg = "#555b71" } },
+ { group = "LineNr", options = { bg = "none", fg = "#555b71" } },
+ { group = "SignColumn", options = { bg = "none" } },
+ { group = "FoldColumn", options = { bg = "none" } },
+ { group = "CursorLine", options = { bg = "#3a3f52" } },
+ { group = "CursorLineNr", options = { bg = "#3a3f52", fg = "#cdd6f4" } },
+ { group = "CursorLineSign", options = { bg = "none" } },
+ { group = "Title", options = { bg = "none", bold = true } },
+ { group = "Comment", options = { bg = "none", fg = "#6b7089" } },
+ { group = "MsgSeparator", options = { bg = "none" } },
+ { group = "WarningMsg", options = { bg = "none", fg = "#e6c384" } },
+ { group = "MoreMsg", options = { bg = "none", fg = "#7f8493" } },
+
+ -- Pop-up / menu
+ { group = "Pmenu", options = { bg = "none" } },
+ { group = "PmenuSel", options = { fg = "black", bg = "white" } },
+ { group = "PmenuThumb", options = { bg = "none" } },
+ { group = "PmenuSbar", options = { bg = "none" } },
+ { group = "PmenuExtra", options = { bg = "none" } },
+ { group = "PmenuExtraSel", options = { bg = "none" } },
+ { group = "WildMenu", options = { link = "PmenuSel" } },
+
+ -- Telescope
+ { group = "TelescopeNormal", options = { bg = "none" } },
+ { group = "TelescopePromptNormal", options = { bg = "none" } },
+ { group = "TelescopeResultsNormal", options = { bg = "none" } },
+ { group = "TelescopePreviewNormal", options = { bg = "none" } },
+ { group = "TelescopeBorder", options = { bg = "none", fg = "#7f8493" } },
+ { group = "TelescopeMatching", options = { fg = "#cba6f7", bold = true } },
+
+ -- Blending
+ { group = "Winblend", options = { bg = "none" } },
+ { group = "Pumblend", options = { bg = "none" } },
+
+ ---- NvimTree
+ --{ group = "NvimTreeNormal", options = { bg = "none", fg = "NONE" } },
+ --{ group = "NvimTreeNormalNC", options = { bg = "none", fg = "NONE" } },
+ --{ group = "NvimTreeNormalFloat", options = { bg = "none" } },
+ --{ group = "NvimTreeEndOfBuffer", options = { bg = "none" } },
+ --{ group = "NvimTreeCursorLine", options = { bg = "#50fa7b", fg = "#000000" } },
+ --{ group = "NvimTreeSymlinkFolderName", options = { fg = "#f8f8f2", bg = "none" } },
+ --{ group = "NvimTreeFolderName", options = { fg = "#f8f8f2", bg = "none" } },
+ --{ group = "NvimTreeRootFolder", options = { fg = "#f8f8f2", bg = "none" } },
+ --{ group = "NvimTreeEmptyFolderName", options = { fg = "#f8f8f2", bg = "none" } },
+ --{ group = "NvimTreeOpenedFolderName", options = { fg = "#f8f8f2", bg = "none" } },
+ --{ group = "NvimTreeOpenedFile", options = { fg = "#50fa7b", bg = "none" } },
+ --{ group = "NvimTreeExecFile", options = { fg = "#ff882a", bg = "none" } },
+ }
+
+ for _, hl in ipairs(highlights) do
+ vim.api.nvim_set_hl(0, hl.group, hl.options)
+ end
+
+ -- Reapply highlights on ColorScheme change
+ vim.api.nvim_create_autocmd("ColorScheme", {
+ group = vim.api.nvim_create_augroup("CustomHighlights", { clear = true }),
+ pattern = "*",
+ callback = function()
+ for _, hl in ipairs(highlights) do
+ vim.api.nvim_set_hl(0, hl.group, hl.options)
+ end
+ end,
+ })
+
+ -- Optional window separator styling
+ vim.cmd([[
+ augroup CustomWinSeparator
+ autocmd!
+ autocmd WinEnter * setlocal winhl=WinSeparator:WinSeparatorA
+ autocmd WinLeave * setlocal winhl=WinSeparator:WinSeparator
+ augroup END
+ ]])
+
+ -- Diagnostics configuration
+ local border = "rounded"
+ vim.diagnostic.config({
+ signs = {
+ text = {
+ [vim.diagnostic.severity.ERROR] = Signs.Error,
+ [vim.diagnostic.severity.WARN] = Signs.Warn,
+ [vim.diagnostic.severity.HINT] = Signs.Hint,
+ [vim.diagnostic.severity.INFO] = Signs.Info,
+ },
+ },
+ underline = true,
+ virtual_text = false,
+ virtual_lines = false,
+ float = {
+ show_header = true,
+ source = "always",
+ border = border,
+ focusable = true,
+ },
+ update_in_insert = false,
+ severity_sort = true,
+ })
+
+ -- Fallback statusline if heirline is missing
+ local heirline_ok, _ = pcall(require, "heirline")
+ if not heirline_ok then
+ local statusline_path = vim.fn.stdpath("config") .. "/autoload/statusline.vim"
+ if vim.fn.filereadable(statusline_path) == 1 then
+ vim.cmd.source(statusline_path)
+ vim.api.nvim_create_autocmd("VimEnter", {
+ callback = function()
+ vim.cmd("call autoload#statusline#ActivateStatusline()")
+ end,
+ })
+ else
+ vim.notify("Fallback statusline script not found:\n" .. statusline_path, vim.log.levels.ERROR)
+ end
+ end
+end
+
+return M
diff --git a/common/nvim/neovim.ps1 b/common/nvim/neovim.ps1
new file mode 100755
index 0000000..a63965e
--- /dev/null
+++ b/common/nvim/neovim.ps1
@@ -0,0 +1,917 @@
+# Neovim Installation Script for Windows (PowerShell)
+# Created By: srdusr (PowerShell port)
+# Project: Install/update/uninstall/change version Neovim script for Windows
+
+#Requires -Version 5.1
+
+param(
+ [switch]$Force,
+ [switch]$NoPrompt
+)
+
+# Color definitions
+$Colors = @{
+ Red = "Red"
+ Green = "Green"
+ Yellow = "Yellow"
+ Cyan = "Cyan"
+}
+
+# Global variables
+$Script:DownloadCommand = $null
+$Script:IsAdmin = $false
+$Script:ShowPrompt = $true
+$Script:NeovimPath = "$env:LOCALAPPDATA\nvim"
+$Script:NeovimBin = "$Script:NeovimPath\bin"
+$Script:NeovimExe = "$Script:NeovimBin\nvim.exe"
+
+# Handle errors
+function Write-ErrorMessage {
+ param([string]$Message)
+ Write-Host "Error: $Message" -ForegroundColor $Colors.Red
+}
+
+# Handle success messages
+function Write-SuccessMessage {
+ param([string]$Message)
+ Write-Host $Message -ForegroundColor $Colors.Green
+}
+
+# Handle info messages
+function Write-InfoMessage {
+ param([string]$Message)
+ Write-Host $Message -ForegroundColor $Colors.Cyan
+}
+
+# Check if running as administrator
+function Test-Administrator {
+ $currentUser = [Security.Principal.WindowsIdentity]::GetCurrent()
+ $principal = New-Object Security.Principal.WindowsPrincipal($currentUser)
+ return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
+}
+
+# Check if necessary dependencies are installed
+function Test-Dependencies {
+ Write-InfoMessage "Checking dependencies..."
+
+ # Check for download tools
+ if (Get-Command curl -ErrorAction SilentlyContinue) {
+ $Script:DownloadCommand = "curl"
+ Write-InfoMessage "Found curl"
+ } elseif (Get-Command wget -ErrorAction SilentlyContinue) {
+ $Script:DownloadCommand = "wget"
+ Write-InfoMessage "Found wget"
+ } else {
+ Write-InfoMessage "Neither curl nor wget found. Will use PowerShell's Invoke-WebRequest"
+ $Script:DownloadCommand = "powershell"
+ }
+
+ # Check for admin privileges
+ $Script:IsAdmin = Test-Administrator
+ if (-not $Script:IsAdmin) {
+ Write-Host "Warning: Not running as administrator. Some operations may fail." -ForegroundColor $Colors.Yellow
+ if (-not $NoPrompt) {
+ $continue = Read-Host "Continue anyway? (y/n)"
+ if ($continue -notin @('y', 'yes', 'Y', 'Yes')) {
+ exit 1
+ }
+ }
+ }
+
+ return $true
+}
+
+# Find all Neovim installations
+function Find-AllNeovimInstallations {
+ $installations = @()
+
+ # Check common installation paths
+ $commonPaths = @(
+ "$env:LOCALAPPDATA\nvim",
+ "$env:ProgramFiles\Neovim",
+ "$env:ProgramFiles(x86)\Neovim",
+ "$env:APPDATA\nvim",
+ "$env:USERPROFILE\nvim",
+ "C:\tools\neovim",
+ "C:\neovim"
+ )
+
+ foreach ($path in $commonPaths) {
+ # Check for nvim.exe in bin subdirectory
+ if (Test-Path "$path\bin\nvim.exe") {
+ $installations += @{
+ Path = $path
+ BinPath = "$path\bin"
+ Type = "Portable"
+ Version = Get-NeovimVersion -Path "$path\bin\nvim.exe"
+ }
+ }
+ # Check for nvim.exe directly in path
+ elseif (Test-Path "$path\nvim.exe") {
+ $installations += @{
+ Path = $path
+ BinPath = $path
+ Type = "Portable"
+ Version = Get-NeovimVersion -Path "$path\nvim.exe"
+ }
+ }
+ # Check for nvim-win64 subdirectory (common structure)
+ elseif (Test-Path "$path\nvim-win64\bin\nvim.exe") {
+ $installations += @{
+ Path = "$path\nvim-win64"
+ BinPath = "$path\nvim-win64\bin"
+ Type = "Portable"
+ Version = Get-NeovimVersion -Path "$path\nvim-win64\bin\nvim.exe"
+ }
+ }
+ }
+
+ # Check for MSI installations in registry
+ try {
+ $uninstallKeys = @(
+ "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*",
+ "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*"
+ )
+
+ foreach ($keyPath in $uninstallKeys) {
+ Get-ItemProperty $keyPath -ErrorAction SilentlyContinue | Where-Object {
+ $_.DisplayName -like "*Neovim*" -or $_.DisplayName -like "*nvim*"
+ } | ForEach-Object {
+ $installations += @{
+ Path = $_.InstallLocation
+ BinPath = "$($_.InstallLocation)\bin"
+ Type = "MSI"
+ Version = $_.DisplayVersion
+ UninstallString = $_.UninstallString
+ ProductCode = $_.PSChildName
+ }
+ }
+ }
+ }
+ catch {
+ Write-InfoMessage "Could not check registry for MSI installations"
+ }
+
+ # Check PATH for nvim.exe - this is crucial for detecting installations
+ $pathDirs = $env:PATH -split ';' | Where-Object { $_ -ne "" }
+ foreach ($dir in $pathDirs) {
+ $dir = $dir.Trim()
+ if (Test-Path "$dir\nvim.exe") {
+ # Determine the installation root
+ $installRoot = $dir
+ if ($dir -like "*\bin") {
+ $installRoot = Split-Path -Parent $dir
+ }
+
+ # Check if we already found this installation
+ $alreadyFound = $false
+ foreach ($existing in $installations) {
+ if ($existing.Path -eq $installRoot -or $existing.BinPath -eq $dir) {
+ $alreadyFound = $true
+ break
+ }
+ }
+
+ if (-not $alreadyFound) {
+ $installations += @{
+ Path = $installRoot
+ BinPath = $dir
+ Type = "PATH"
+ Version = Get-NeovimVersion -Path "$dir\nvim.exe"
+ }
+ }
+ }
+ }
+
+ return $installations
+}
+
+# Get Neovim version from executable
+function Get-NeovimVersion {
+ param([string]$Path)
+
+ try {
+ $versionOutput = & $Path --version 2>$null | Select-String "NVIM v(\d+\.\d+\.\d+)" | ForEach-Object { $_.Matches[0].Groups[1].Value }
+ return $versionOutput
+ }
+ catch {
+ return "Unknown"
+ }
+}
+
+# Complete uninstall of all Neovim instances
+function Uninstall-AllNeovim {
+ param([switch]$Silent)
+
+ Write-InfoMessage "Searching for all Neovim installations..."
+ $installations = Find-AllNeovimInstallations
+
+ if ($installations.Count -eq 0) {
+ Write-InfoMessage "No Neovim installations found."
+ return $true
+ }
+
+ if (-not $Silent) {
+ Write-Host "Found $($installations.Count) Neovim installation(s):" -ForegroundColor $Colors.Yellow
+ for ($i = 0; $i -lt $installations.Count; $i++) {
+ Write-Host " $($i + 1). $($installations[$i].Type) - $($installations[$i].Path) (v$($installations[$i].Version))"
+ }
+
+ if (-not $NoPrompt) {
+ $confirm = Read-Host "Remove all installations? (y/n)"
+ if ($confirm -notin @('y', 'yes', 'Y', 'Yes')) {
+ Write-InfoMessage "Uninstall cancelled."
+ return $false
+ }
+ }
+ }
+
+ $success = $true
+ foreach ($installation in $installations) {
+ Write-InfoMessage "Removing $($installation.Type) installation: $($installation.Path)"
+
+ try {
+ if ($installation.Type -eq "MSI") {
+ # Uninstall MSI package
+ if ($installation.UninstallString) {
+ if ($installation.UninstallString -like "*msiexec*") {
+ # Extract the product code from the uninstall string
+ $productCode = $installation.ProductCode
+ if ($productCode -and $productCode -match "^\{.*\}$") {
+ $uninstallArgs = "/x `"$productCode`" /quiet /norestart"
+ Write-InfoMessage "Uninstalling MSI with product code: $productCode"
+ Start-Process -FilePath "msiexec.exe" -ArgumentList $uninstallArgs -Wait -NoNewWindow
+ } else {
+ # Fallback to parsing the uninstall string
+ $uninstallArgs = $installation.UninstallString -replace "MsiExec.exe", "" -replace "/I\{", "/x{"
+ $uninstallArgs += " /quiet /norestart"
+ Write-InfoMessage "Uninstalling MSI with args: $uninstallArgs"
+ Start-Process -FilePath "msiexec.exe" -ArgumentList $uninstallArgs -Wait -NoNewWindow
+ }
+ } else {
+ # Direct uninstall command
+ Write-InfoMessage "Running uninstall command: $($installation.UninstallString)"
+ Invoke-Expression $installation.UninstallString
+ }
+
+ if ($LASTEXITCODE -eq 0 -or $LASTEXITCODE -eq $null) {
+ Write-SuccessMessage "Successfully uninstalled MSI package"
+ } else {
+ Write-ErrorMessage "MSI uninstall failed with exit code: $LASTEXITCODE"
+ $success = $false
+ }
+ } else {
+ Write-ErrorMessage "No uninstall string found for MSI package"
+ $success = $false
+ }
+ } else {
+ # Remove portable installation
+ if (Test-Path $installation.Path) {
+ Write-InfoMessage "Removing directory: $($installation.Path)"
+ # Force kill any processes that might be using files in the directory
+ Get-Process -Name "nvim*" -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue
+
+ # Try to remove the directory
+ Remove-Item -Path $installation.Path -Recurse -Force -ErrorAction Stop
+ Write-SuccessMessage "Successfully removed directory: $($installation.Path)"
+ } else {
+ Write-InfoMessage "Directory already removed: $($installation.Path)"
+ }
+ }
+ }
+ catch {
+ Write-ErrorMessage "Failed to remove $($installation.Path): $_"
+ # Try alternative removal methods
+ try {
+ Write-InfoMessage "Attempting alternative removal method..."
+ if (Test-Path $installation.Path) {
+ # Use robocopy to move and delete (works around file locks)
+ $tempDir = "$env:TEMP\nvim_removal_$(Get-Random)"
+ robocopy "$($installation.Path)" "$tempDir" /E /MOVE /NFL /NDL /NJH /NJS /NC /NS /NP
+ Remove-Item -Path $tempDir -Recurse -Force -ErrorAction SilentlyContinue
+ Write-SuccessMessage "Successfully removed using alternative method: $($installation.Path)"
+ }
+ }
+ catch {
+ Write-ErrorMessage "Alternative removal also failed: $_"
+ $success = $false
+ }
+ }
+ }
+
+ # Clean up PATH environment variable
+ Write-InfoMessage "Cleaning up PATH environment variable..."
+
+ # Get both user and system PATH
+ $userPath = [Environment]::GetEnvironmentVariable("PATH", "User")
+ $systemPath = ""
+
+ if ($Script:IsAdmin) {
+ try {
+ $systemPath = [Environment]::GetEnvironmentVariable("PATH", "Machine")
+ }
+ catch {
+ Write-InfoMessage "Could not access system PATH"
+ }
+ }
+
+ # Create patterns for all found installations
+ $nvimPathPatterns = @()
+ foreach ($installation in $installations) {
+ $nvimPathPatterns += [regex]::Escape($installation.BinPath)
+ $nvimPathPatterns += [regex]::Escape($installation.Path)
+ }
+
+ # Add common patterns
+ $nvimPathPatterns += @(
+ [regex]::Escape($Script:NeovimBin),
+ [regex]::Escape("$env:ProgramFiles\Neovim\bin"),
+ [regex]::Escape("$env:ProgramFiles(x86)\Neovim\bin"),
+ [regex]::Escape("C:\tools\neovim\nvim-win64\bin"),
+ [regex]::Escape("C:\tools\neovim\nvim-win64")
+ )
+
+ # Clean user PATH
+ $originalUserPath = $userPath
+ foreach ($pattern in $nvimPathPatterns) {
+ $userPath = $userPath -replace ";$pattern;", ";"
+ $userPath = $userPath -replace "^$pattern;", ""
+ $userPath = $userPath -replace ";$pattern$", ""
+ $userPath = $userPath -replace "^$pattern$", ""
+ }
+
+ if ($userPath -ne $originalUserPath) {
+ try {
+ [Environment]::SetEnvironmentVariable("PATH", $userPath, "User")
+ Write-InfoMessage "Cleaned up user PATH"
+ }
+ catch {
+ Write-ErrorMessage "Failed to clean up user PATH: $_"
+ }
+ }
+
+ # Clean system PATH if running as admin
+ if ($Script:IsAdmin -and $systemPath) {
+ $originalSystemPath = $systemPath
+ foreach ($pattern in $nvimPathPatterns) {
+ $systemPath = $systemPath -replace ";$pattern;", ";"
+ $systemPath = $systemPath -replace "^$pattern;", ""
+ $systemPath = $systemPath -replace ";$pattern$", ""
+ $systemPath = $systemPath -replace "^$pattern$", ""
+ }
+
+ if ($systemPath -ne $originalSystemPath) {
+ try {
+ [Environment]::SetEnvironmentVariable("PATH", $systemPath, "Machine")
+ Write-InfoMessage "Cleaned up system PATH"
+ }
+ catch {
+ Write-ErrorMessage "Failed to clean up system PATH: $_"
+ }
+ }
+ }
+
+ if ($success) {
+ Write-SuccessMessage "All Neovim installations have been removed successfully!"
+ } else {
+ Write-Host "Some installations could not be removed completely." -ForegroundColor $Colors.Yellow
+ }
+
+ Write-InfoMessage "You may need to restart your shell for PATH changes to take effect."
+ return $success
+}
+
+# Check if Neovim is already installed
+function Test-NeovimInstalled {
+ if (Test-Path $Script:NeovimExe) {
+ return $true
+ }
+
+ # Check if nvim is in PATH
+ if (Get-Command nvim -ErrorAction SilentlyContinue) {
+ return $true
+ }
+
+ return $false
+}
+
+# Download a file - FIXED VERSION
+function Get-FileDownload {
+ param(
+ [string]$Url,
+ [string]$OutputPath
+ )
+
+ Write-InfoMessage "Downloading from: $Url"
+ Write-InfoMessage "Saving to: $OutputPath"
+
+ try {
+ switch ($Script:DownloadCommand) {
+ "curl" {
+ # Use Start-Process instead of cmd /c for better compatibility
+ $curlArgs = @("-L", "--progress-bar", "-o", $OutputPath, $Url)
+ $process = Start-Process -FilePath "curl" -ArgumentList $curlArgs -Wait -NoNewWindow -PassThru
+ if ($process.ExitCode -ne 0) {
+ throw "Curl download failed with exit code: $($process.ExitCode)"
+ }
+ }
+ "wget" {
+ # Use Start-Process instead of cmd /c for better compatibility
+ $wgetArgs = @("--progress=bar", "--show-progress", "-O", $OutputPath, $Url)
+ $process = Start-Process -FilePath "wget" -ArgumentList $wgetArgs -Wait -NoNewWindow -PassThru
+ if ($process.ExitCode -ne 0) {
+ throw "Wget download failed with exit code: $($process.ExitCode)"
+ }
+ }
+ "powershell" {
+ # Enhanced PowerShell download with progress
+ Write-InfoMessage "Using PowerShell's Invoke-WebRequest..."
+ $ProgressPreference = 'Continue'
+
+ # Create a WebClient for better progress reporting
+ $webClient = New-Object System.Net.WebClient
+ $webClient.Headers.Add("User-Agent", "PowerShell Neovim Installer")
+
+ # Register progress event
+ Register-ObjectEvent -InputObject $webClient -EventName DownloadProgressChanged -Action {
+ $Global:DownloadProgress = $Event.SourceEventArgs.ProgressPercentage
+ Write-Progress -Activity "Downloading Neovim" -Status "Progress: $($Event.SourceEventArgs.ProgressPercentage)%" -PercentComplete $Event.SourceEventArgs.ProgressPercentage
+ } | Out-Null
+
+ # Download the file
+ try {
+ $webClient.DownloadFile($Url, $OutputPath)
+ Write-Progress -Activity "Downloading Neovim" -Completed
+ }
+ finally {
+ $webClient.Dispose()
+ # Clean up event handlers
+ Get-EventSubscriber | Where-Object { $_.SourceObject -eq $webClient } | Unregister-Event
+ }
+ }
+ }
+
+ # Verify the file was downloaded
+ if (-not (Test-Path $OutputPath)) {
+ throw "Downloaded file not found at: $OutputPath"
+ }
+
+ $fileSize = (Get-Item $OutputPath).Length
+ if ($fileSize -eq 0) {
+ throw "Downloaded file is empty"
+ }
+
+ Write-InfoMessage "Download completed successfully. File size: $($fileSize / 1MB) MB"
+ return $true
+ }
+ catch {
+ Write-ErrorMessage "Download failed: $_"
+ # Clean up partial download
+ if (Test-Path $OutputPath) {
+ Remove-Item -Path $OutputPath -Force -ErrorAction SilentlyContinue
+ }
+ return $false
+ }
+}
+
+# Get available versions from GitHub API
+function Get-AvailableVersions {
+ try {
+ $apiUrl = "https://api.github.com/repos/neovim/neovim/releases"
+ $response = Invoke-RestMethod -Uri $apiUrl -UseBasicParsing
+ return $response | ForEach-Object { $_.tag_name }
+ }
+ catch {
+ Write-ErrorMessage "Failed to fetch available versions: $_"
+ return @()
+ }
+}
+
+# Check if a specific version exists
+function Test-VersionExists {
+ param([string]$Version)
+
+ if ($Version -notmatch "^v") {
+ $Version = "v$Version"
+ }
+
+ $versions = Get-AvailableVersions
+ return $versions -contains $Version
+}
+
+# Get the latest stable version
+function Get-LatestStableVersion {
+ try {
+ $apiUrl = "https://api.github.com/repos/neovim/neovim/releases/latest"
+ $response = Invoke-RestMethod -Uri $apiUrl -UseBasicParsing
+ return $response.tag_name
+ }
+ catch {
+ Write-ErrorMessage "Failed to fetch latest version: $_"
+ return $null
+ }
+}
+
+# Download specific version
+function Get-SpecificVersion {
+ param([string]$Version)
+
+ if ($Version -notmatch "^v") {
+ $Version = "v$Version"
+ }
+
+ try {
+ $apiUrl = "https://api.github.com/repos/neovim/neovim/releases/tags/$Version"
+ $response = Invoke-RestMethod -Uri $apiUrl -UseBasicParsing
+
+ # Look for Windows assets
+ $asset = $response.assets | Where-Object {
+ $_.name -match "nvim-win64\.zip$" -or
+ $_.name -match "nvim-win64\.msi$" -or
+ $_.name -match "nvim-windows\.zip$"
+ } | Select-Object -First 1
+
+ if (-not $asset) {
+ Write-ErrorMessage "No Windows asset found for version ${Version}"
+ return $null
+ }
+
+ $fileName = $asset.name
+ $downloadUrl = $asset.browser_download_url
+
+ Write-InfoMessage "Found asset: $fileName"
+
+ if (Get-FileDownload -Url $downloadUrl -OutputPath $fileName) {
+ return $fileName
+ }
+ }
+ catch {
+ Write-ErrorMessage "Failed to download version ${Version}: $_"
+ }
+
+ return $null
+}
+
+# Install Neovim from downloaded file
+function Install-NeovimFromFile {
+ param(
+ [string]$FilePath,
+ [string]$Version = "Unknown",
+ [switch]$CleanInstall
+ )
+
+ try {
+ if ($CleanInstall) {
+ Write-InfoMessage "Performing clean installation - removing existing installations..."
+ Uninstall-AllNeovim -Silent
+ }
+
+ Write-InfoMessage "Installing Neovim ${Version}..."
+
+ $fileExtension = [System.IO.Path]::GetExtension($FilePath).ToLower()
+
+ if ($fileExtension -eq ".msi") {
+ # Handle MSI installation
+ Write-InfoMessage "Installing from MSI package..."
+ $installArgs = @(
+ "/i", "`"$FilePath`""
+ "/quiet"
+ "/norestart"
+ )
+
+ Start-Process -FilePath "msiexec.exe" -ArgumentList $installArgs -Wait -NoNewWindow
+
+ if ($LASTEXITCODE -eq 0) {
+ Write-SuccessMessage "Neovim ${Version} installed successfully via MSI!"
+
+ # MSI typically installs to Program Files, add to PATH if needed
+ $programFiles = "${env:ProgramFiles}\Neovim\bin"
+ $programFilesX86 = "${env:ProgramFiles(x86)}\Neovim\bin"
+
+ $nvimPath = ""
+ if (Test-Path "$programFiles\nvim.exe") {
+ $nvimPath = $programFiles
+ } elseif (Test-Path "$programFilesX86\nvim.exe") {
+ $nvimPath = $programFilesX86
+ }
+
+ if ($nvimPath) {
+ $currentPath = [Environment]::GetEnvironmentVariable("PATH", "User")
+ if ($currentPath -notlike "*$nvimPath*") {
+ [Environment]::SetEnvironmentVariable("PATH", "$currentPath;$nvimPath", "User")
+ Write-InfoMessage "Added Neovim to PATH: $nvimPath"
+ }
+ }
+ } else {
+ Write-ErrorMessage "MSI installation failed with exit code: $LASTEXITCODE"
+ return $false
+ }
+ }
+ elseif ($fileExtension -eq ".zip") {
+ # Handle ZIP installation
+ Write-InfoMessage "Installing from ZIP archive..."
+
+ # Create installation directory
+ if (Test-Path $Script:NeovimPath) {
+ Remove-Item -Path $Script:NeovimPath -Recurse -Force
+ }
+ New-Item -Path $Script:NeovimPath -ItemType Directory -Force | Out-Null
+
+ # Extract zip file
+ Write-InfoMessage "Extracting archive..."
+ Add-Type -AssemblyName System.IO.Compression.FileSystem
+ [System.IO.Compression.ZipFile]::ExtractToDirectory($FilePath, $Script:NeovimPath)
+
+ # Find the nvim.exe in the extracted files
+ $nvimExe = Get-ChildItem -Path $Script:NeovimPath -Name "nvim.exe" -Recurse | Select-Object -First 1
+ if (-not $nvimExe) {
+ Write-ErrorMessage "Could not find nvim.exe in extracted files"
+ return $false
+ }
+
+ $nvimDir = Split-Path -Path (Get-ChildItem -Path $Script:NeovimPath -Name "nvim.exe" -Recurse | Select-Object -First 1).FullName
+
+ # Move files to proper location if needed
+ if ($nvimDir -ne $Script:NeovimBin) {
+ if (Test-Path $Script:NeovimBin) {
+ Remove-Item -Path $Script:NeovimBin -Recurse -Force
+ }
+ Move-Item -Path $nvimDir -Destination $Script:NeovimBin -Force
+ }
+
+ # Add to PATH if not already there
+ $currentPath = [Environment]::GetEnvironmentVariable("PATH", "User")
+ if ($currentPath -notlike "*$Script:NeovimBin*") {
+ [Environment]::SetEnvironmentVariable("PATH", "$currentPath;$Script:NeovimBin", "User")
+ Write-InfoMessage "Added Neovim to PATH"
+ }
+
+ Write-SuccessMessage "Neovim ${Version} installed successfully!"
+ Write-InfoMessage "Location: $Script:NeovimBin"
+ }
+ else {
+ Write-ErrorMessage "Unsupported file type: $fileExtension"
+ return $false
+ }
+
+ # Clean up downloaded file
+ Remove-Item -Path $FilePath -Force -ErrorAction SilentlyContinue
+
+ Write-InfoMessage "You may need to restart your shell for PATH changes to take effect."
+ return $true
+ }
+ catch {
+ Write-ErrorMessage "Installation failed: $_"
+ return $false
+ }
+}
+
+# Install nightly version
+function Install-NightlyVersion {
+ param([switch]$CleanInstall)
+
+ Write-InfoMessage "Installing Neovim Nightly..."
+ $url = "https://github.com/neovim/neovim/releases/download/nightly/nvim-win64.zip"
+ $fileName = "nvim-nightly.zip"
+
+ if (Get-FileDownload -Url $url -OutputPath $fileName) {
+ return Install-NeovimFromFile -FilePath $fileName -Version "Nightly" -CleanInstall:$CleanInstall
+ }
+ return $false
+}
+
+# Install stable version
+function Install-StableVersion {
+ param([switch]$CleanInstall)
+
+ Write-InfoMessage "Installing Neovim Stable..."
+ $latestVersion = Get-LatestStableVersion
+ if (-not $latestVersion) {
+ Write-ErrorMessage "Could not determine latest stable version"
+ return $false
+ }
+
+ $fileName = Get-SpecificVersion -Version $latestVersion
+ if ($fileName) {
+ return Install-NeovimFromFile -FilePath $fileName -Version "Stable ($latestVersion)" -CleanInstall:$CleanInstall
+ }
+ return $false
+}
+
+# Install specific version
+function Install-SpecificVersionWrapper {
+ param(
+ [string]$Version,
+ [switch]$CleanInstall
+ )
+
+ Write-InfoMessage "Installing Neovim ${Version}..."
+
+ if (-not (Test-VersionExists -Version $Version)) {
+ Write-ErrorMessage "Version ${Version} does not exist"
+ return $false
+ }
+
+ $fileName = Get-SpecificVersion -Version $Version
+ if ($fileName) {
+ return Install-NeovimFromFile -FilePath $fileName -Version $Version -CleanInstall:$CleanInstall
+ }
+ return $false
+}
+
+# Update/Install version menu
+function Show-UpdateMenu {
+ # Check if there are existing installations
+ $installations = Find-AllNeovimInstallations
+ $hasExistingInstalls = $installations.Count -gt 0
+
+ if ($hasExistingInstalls) {
+ Write-Host "Existing Neovim installations found:" -ForegroundColor $Colors.Yellow
+ for ($i = 0; $i -lt $installations.Count; $i++) {
+ Write-Host " - $($installations[$i].Type) - $($installations[$i].Path) (v$($installations[$i].Version))"
+ }
+ Write-Host ""
+
+ if (-not $NoPrompt) {
+ $cleanInstall = Read-Host "Perform clean installation (remove existing installations)? (y/n)"
+ $shouldClean = $cleanInstall -in @('y', 'yes', 'Y', 'Yes')
+ } else {
+ $shouldClean = $true
+ }
+ } else {
+ $shouldClean = $false
+ }
+
+ $validChoice = $false
+ while (-not $validChoice) {
+ Write-Host ""
+ Write-Host "Select version to install/update to:"
+ Write-Host " 1. Nightly"
+ Write-Host " 2. Stable"
+ Write-Host " 3. Choose specific version by tag"
+
+ $choice = Read-Host "Enter the number corresponding to your choice (1/2/3)"
+
+ switch ($choice) {
+ "1" {
+ $result = Install-NightlyVersion -CleanInstall:$shouldClean
+ $validChoice = $true
+ $env:PATH = [System.Environment]::GetEnvironmentVariable("PATH","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("PATH","User")
+ }
+ "2" {
+ $result = Install-StableVersion -CleanInstall:$shouldClean
+ $validChoice = $true
+ $env:PATH = [System.Environment]::GetEnvironmentVariable("PATH","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("PATH","User")
+ }
+ "3" {
+ $version = Read-Host "Enter the specific version (e.g., v0.9.0)"
+ $result = Install-SpecificVersionWrapper -Version $version -CleanInstall:$shouldClean
+ $validChoice = $true
+ $env:PATH = [System.Environment]::GetEnvironmentVariable("PATH","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("PATH","User")
+ }
+ default {
+ Write-ErrorMessage "Invalid choice. Please enter 1, 2, or 3."
+ }
+ }
+ }
+
+ return $result
+}
+
+# Uninstall Neovim (wrapper for backward compatibility)
+function Uninstall-Neovim {
+ return Uninstall-AllNeovim
+}
+
+# Check if Neovim is running
+function Test-NeovimRunning {
+ $nvimProcesses = Get-Process -Name "nvim" -ErrorAction SilentlyContinue
+ if ($nvimProcesses) {
+ Write-Host "Error: Neovim is currently running. Please close Neovim before proceeding." -ForegroundColor $Colors.Red
+
+ if (-not $NoPrompt) {
+ $choice = Read-Host "Do you want to forcefully terminate Neovim and continue? (y/n)"
+ if ($choice -in @('y', 'yes', 'Y', 'Yes')) {
+ $nvimProcesses | Stop-Process -Force
+ Write-InfoMessage "Neovim processes terminated"
+ } else {
+ Write-InfoMessage "Exiting..."
+ exit 1
+ }
+ } else {
+ $nvimProcesses | Stop-Process -Force
+ Write-InfoMessage "Neovim processes terminated"
+ }
+ }
+}
+
+# Check for updates
+function Test-Updates {
+ Write-InfoMessage "Checking for updates..."
+
+ try {
+ $latestVersion = Get-LatestStableVersion
+ if (-not $latestVersion) {
+ Write-ErrorMessage "Could not fetch latest version information"
+ return
+ }
+
+ Write-InfoMessage "Latest stable version: $latestVersion"
+
+ $installations = Find-AllNeovimInstallations
+ if ($hasExistingInstalls) {
+ Write-InfoMessage "Found $($installations.Count) installation(s):"
+ foreach ($installation in $installations) {
+ Write-InfoMessage " - $($installation.Type): v$($installation.Version) at $($installation.Path)"
+ if ($installation.Version -ne "Unknown" -and "v$($installation.Version)" -ne $latestVersion) {
+ Write-SuccessMessage " Update available: v$($installation.Version) → $latestVersion"
+ } elseif ("v$($installation.Version)" -eq $latestVersion) {
+ Write-InfoMessage " Up to date"
+ }
+ }
+ } else {
+ Write-InfoMessage "Neovim is not installed"
+ }
+ }
+ catch {
+ Write-ErrorMessage "Failed to check for updates: $_"
+ }
+}
+
+# Main function
+function Main {
+ Write-Host "Neovim Installation Script for Windows" -ForegroundColor $Colors.Cyan
+ Write-Host "=======================================" -ForegroundColor $Colors.Cyan
+
+ # Check dependencies
+ if (-not (Test-Dependencies)) {
+ exit 1
+ }
+
+ # Check if Neovim is running
+ Test-NeovimRunning
+
+ # Check if Neovim is installed
+ $installations = Find-AllNeovimInstallations
+ if ($installations.Count -gt 0) {
+ Write-SuccessMessage "Found $($installations.Count) Neovim installation(s):"
+ foreach ($installation in $installations) {
+ Write-InfoMessage " - $($installation.Type): v$($installation.Version) at $($installation.Path)"
+ }
+ } else {
+ Write-Host "Neovim is not installed." -ForegroundColor $Colors.Red
+ if (-not $NoPrompt) {
+ $choice = Read-Host "Install Neovim? (y/n)"
+ if ($choice -in @('y', 'yes', 'Y', 'Yes')) {
+ Show-UpdateMenu
+ return
+ } else {
+ Write-InfoMessage "Exiting..."
+ return
+ }
+ }
+ }
+
+ # Main menu loop
+ while ($Script:ShowPrompt) {
+ Write-Host ""
+ Write-Host "Select an option:"
+ Write-Host " 1. Install/update Neovim"
+ Write-Host " 2. Check for updates"
+ Write-Host " 3. Uninstall all Neovim installations"
+ Write-Host " 4. Run Neovim"
+ Write-Host " 5. Quit"
+
+ $choice = Read-Host "Enter a number or press 'q' to quit"
+
+ switch ($choice) {
+ "1" {
+ Show-UpdateMenu
+ }
+ "2" {
+ Test-Updates
+ }
+ "3" {
+ Uninstall-AllNeovim
+ }
+ "4" {
+ if ($installations.Count -gt 0 -or (Get-Command nvim -ErrorAction SilentlyContinue)) {
+ Write-InfoMessage "Starting Neovim..."
+ & nvim
+ } else {
+ Write-ErrorMessage "Neovim is not installed"
+ }
+ }
+ { $_ -in @("5", "q", "Q", "quit", "exit") } {
+ Write-InfoMessage "Exiting..."
+ $Script:ShowPrompt = $false
+ }
+ default {
+ Write-ErrorMessage "Invalid choice. Please choose a valid option by entering the corresponding number or press 'q' to quit."
+ }
+ }
+ }
+}
+
+# Run the main function
+Main
diff --git a/common/nvim/neovim.sh b/common/nvim/neovim.sh
new file mode 100755
index 0000000..842abed
--- /dev/null
+++ b/common/nvim/neovim.sh
@@ -0,0 +1,516 @@
+#!/bin/bash
+
+# Created By: srdusr
+# Created On: Sat 12 Aug 2023 13:11:39 CAT
+# Project: Install/update/uninstall/change version Neovim script, primarily for Linux but may work in other platforms
+
+# Dependencies: wget/curl, fuse, jq
+
+# Color definitions
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+NC='\033[0m' # No Color
+
+# Handle errors
+handle_error() {
+ local message="$1"
+ printf "${RED}Error: $message${NC}\n"
+}
+
+# Check if necessary dependencies are installed
+check_dependencies() {
+ if [ -x "$(command -v wget)" ]; then
+ DOWNLOAD_COMMAND="wget"
+ elif [ -x "$(command -v curl)" ]; then
+ DOWNLOAD_COMMAND="curl"
+ else
+ printf "${RED}Error: Neither wget nor curl found. Please install one of them to continue!${NC}\n"
+ exit 1
+ fi
+ if ! command -v jq >/dev/null 2>&1; then
+ printf "${RED}Error: jq is required for specific version downloads. Please install jq!${NC}\n"
+ exit 1
+ fi
+}
+
+# Check for privilege escalation tools
+check_privilege_tools() {
+ if [ -x "$(command -v sudo)" ]; then
+ PRIVILEGE_TOOL="sudo"
+ elif [ -x "$(command -v doas)" ]; then
+ PRIVILEGE_TOOL="doas"
+ elif [ -x "$(command -v pkexec)" ]; then
+ PRIVILEGE_TOOL="pkexec"
+ elif [ -x "$(command -v dzdo)" ]; then
+ PRIVILEGE_TOOL="dzdo"
+ elif [ "$(id -u)" -eq 0 ]; then
+ PRIVILEGE_TOOL="" # root
+ else
+ PRIVILEGE_TOOL="" # No privilege escalation mechanism found
+ printf "\n${RED}Error: No privilege escalation tool (sudo, doas, pkexec, dzdo, or root privileges) found. You may not have sufficient permissions to run this script.${NC}\n"
+ printf "\nAttempt to continue Installation (might fail without a privilege escalation tool)? [yes/no] "
+ read continue_choice
+ case $continue_choice in
+ [Yy] | [Yy][Ee][Ss]) ;;
+ [Nn] | [Nn][Oo]) exit ;;
+ *) handle_error "Invalid choice. Exiting..." && exit ;;
+ esac
+ fi
+}
+
+# Check if Neovim is already installed
+check_neovim_installed() {
+ if [ -x "$(command -v nvim)" ]; then
+ return 0 # Neovim is installed
+ else
+ return 1 # Neovim is not installed
+ fi
+}
+
+# Nightly version
+nightly_version() {
+ local url="https://github.com/neovim/neovim/releases/download/nightly/nvim-linux-x86_64.appimage"
+ install_neovim "$url"
+ local version_output=$(nvim --version)
+ version_id="Nightly $(echo "$version_output" | grep -oP 'NVIM \d+\.\d+\.\d+')"
+}
+
+# Stable version
+stable_version() {
+ #local url="https://github.com/neovim/neovim/releases/download/stable/nvim.appimage"
+ local url="https://github.com/neovim/neovim/releases/latest/download/nvim-linux-x86_64.appimage"
+ install_neovim "$url"
+ local version_output=$(nvim --version)
+ version_id="Stable $(echo "$version_output" | grep -oP 'NVIM \d+\.\d+')"
+}
+
+# Specific version
+specific_version() {
+ local version="$1"
+ filename=$(download_specific_version "$version")
+ echo "Installing Neovim $version..."
+ if [ -x "$(command -v fusermount)" ]; then
+ chmod u+x "$filename"
+ $PRIVILEGE_TOOL cp "$filename" /usr/local/bin/nvim
+ $PRIVILEGE_TOOL chmod +x /usr/local/bin/nvim
+ echo "Installed Neovim to /usr/local/bin/nvim"
+ else
+ chmod u+x "$filename"
+ ./$filename --appimage-extract
+ $PRIVILEGE_TOOL cp squashfs-root/usr/bin/nvim /usr/local/bin
+ $PRIVILEGE_TOOL chmod +x /usr/local/bin/nvim
+ echo "Installed Neovim to /usr/local/bin/nvim"
+ fi
+}
+
+# Download a file using wget or curl
+download_file() {
+ local url="$1"
+ local output="$2"
+
+ if [ "$DOWNLOAD_COMMAND" = "wget" ]; then
+ if ! "$DOWNLOAD_COMMAND" -q --show-progress -O "$output" "$url"; then
+ handle_error "Download failed. Exiting..."
+ exit 1
+ fi
+ elif [ "$DOWNLOAD_COMMAND" = "curl" ]; then
+ if ! "$DOWNLOAD_COMMAND" --progress-bar -# -o "$output" "$url"; then
+ handle_error "Download failed. Exiting..."
+ exit 1
+ fi
+ else
+ echo "Unsupported download command: $DOWNLOAD_COMMAND"
+ exit 1
+ fi
+}
+
+# Download the correct asset for a specific version using GitHub API and jq
+download_specific_version() {
+ local version="$1"
+ if [[ $version != v* ]]; then
+ version="v$version"
+ fi
+ local api_url="https://api.github.com/repos/neovim/neovim/releases/tags/$version"
+ local json=$(curl -sSL "$api_url")
+ local os_name=$(uname -s)
+ local arch=$(uname -m)
+ local asset=""
+ local asset_name=""
+ local asset_candidates=()
+
+ # Build candidate asset names based on platform/arch
+ if [[ "$os_name" == "Linux" ]]; then
+ if [[ "$arch" == "aarch64" || "$arch" == "arm64" ]]; then
+ asset_candidates=("nvim-linux-arm64.tar.gz" "nvim-linux64.tar.gz" "nvim.appimage")
+ elif [[ "$arch" == "armv7l" ]]; then
+ asset_candidates=("nvim-linux-arm.tar.gz" "nvim.appimage")
+ else
+ asset_candidates=("nvim-linux-x86_64.appimage" "nvim-linux64.appimage" "nvim.appimage" "nvim-linux64.tar.gz")
+ fi
+ elif [[ "$os_name" == "Darwin" ]]; then
+ if [[ "$arch" == "arm64" ]]; then
+ asset_candidates=("nvim-macos-arm64.tar.gz" "nvim-macos.tar.gz")
+ else
+ asset_candidates=("nvim-macos.tar.gz" "nvim-macos-x86_64.tar.gz")
+ fi
+ elif [[ "$os_name" =~ MINGW|MSYS|CYGWIN ]]; then
+ asset_candidates=("nvim-win64.zip" "nvim-win64.msi")
+ fi
+
+ # Find the first matching asset
+ for name in "${asset_candidates[@]}"; do
+ asset=$(echo "$json" | jq -r ".assets[] | select(.name==\"$name\") | .browser_download_url")
+ [[ -n "$asset" && "$asset" != "null" ]] && { asset_name="$name"; break; }
+ done
+
+ if [[ -z "$asset" || "$asset" == "null" ]]; then
+ echo "No suitable asset found for your platform/arch in this release." >&2
+ echo "Available assets:" >&2
+ echo "$json" | jq -r '.assets[].name' >&2
+ exit 1
+ fi
+
+ echo "DEBUG: Downloading asset: $asset_name from $asset" >&2
+ download_file "$asset" "$asset_name"
+
+ # Download checksum if available
+ checksum_url=$(echo "$json" | jq -r ".assets[] | select(.name==\"$asset_name.sha256sum\" or .name==\"$asset_name.sha256\") | .browser_download_url")
+ if [[ -n "$checksum_url" && "$checksum_url" != "null" ]]; then
+ checksum_file="${asset_name}.sha256sum"
+ echo "DEBUG: Downloading checksum: $checksum_file from $checksum_url" >&2
+ download_file "$checksum_url" "$checksum_file"
+ echo "DEBUG: Contents of $checksum_file:" >&2
+ cat "$checksum_file" >&2
+ echo "Verifying checksum..." >&2
+ # Try to handle both formats: with and without filename
+ if grep -q "$asset_name" "$checksum_file"; then
+ if command -v sha256sum >/dev/null 2>&1; then
+ sha256sum -c "$checksum_file" --ignore-missing >&2
+ elif command -v shasum >/dev/null 2>&1; then
+ shasum -a 256 -c "$checksum_file" >&2
+ else
+ echo "Warning: No sha256sum or shasum found, cannot verify checksum." >&2
+ fi
+ else
+ # If the checksum file contains only the hash, not the filename
+ hash=$(head -n1 "$checksum_file" | awk '{print $1}')
+ if command -v sha256sum >/dev/null 2>&1; then
+ echo "$hash $asset_name" | sha256sum -c - >&2
+ elif command -v shasum >/dev/null 2>&1; then
+ echo "$hash $asset_name" | shasum -a 256 -c - >&2
+ else
+ echo "Warning: No sha256sum or shasum found, cannot verify checksum." >&2
+ fi
+ fi
+ else
+ echo "Warning: No checksum file found for $asset_name." >&2
+ fi
+
+ echo "$asset_name"
+}
+
+# Check if a specific version of Neovim exists
+version_exists() {
+ local version="$1"
+
+ # Add 'v' prefix if not present
+ if [[ $version != v* ]]; then
+ version="v$version"
+ fi
+
+ # Fetch all the release tags from GitHub
+ ALL_TAGS=$(curl -s "https://api.github.com/repos/neovim/neovim/tags" | grep '"name":' | cut -d '"' -f 4)
+
+ # Check if the desired version is in the list of release tags
+ if echo "$ALL_TAGS" | grep -q "$version"; then
+ return 0 # Version exists
+ else
+ return 1 # Version does not exist
+ fi
+}
+
+# Update Neovim to the latest version (nightly/stable)
+update_version() {
+ valid_choice=false
+ while [ "$valid_choice" = false ]; do
+ # Determine which version to update to (nightly/stable)
+ printf "Select version to install/update to:\n"
+ printf " 1. Nightly\n"
+ printf " 2. Stable\n"
+ printf " 3. Choose specific version by tag\n"
+ printf "Enter the number corresponding to your choice (1/2/3): "
+ read update_choice
+
+ case $update_choice in
+ 1)
+ version="Nightly"
+ nightly_version
+ valid_choice=true
+ ;;
+ 2)
+ version="Stable"
+ stable_version
+ valid_choice=true
+ ;;
+ 3)
+ # Ask user for specific version
+ read -p "Enter the specific version (e.g., v0.1.0): " version
+ filename=$(download_specific_version "$version")
+ echo "Installing Neovim $version..."
+ if [ -x "$(command -v fusermount)" ]; then
+ chmod u+x "$filename"
+ $PRIVILEGE_TOOL cp "$filename" /usr/local/bin/nvim
+ $PRIVILEGE_TOOL chmod +x /usr/local/bin/nvim
+ echo "Installed Neovim to /usr/local/bin/nvim"
+ else
+ chmod u+x "$filename"
+ ./$filename --appimage-extract
+ $PRIVILEGE_TOOL cp squashfs-root/usr/bin/nvim /usr/local/bin
+ $PRIVILEGE_TOOL chmod +x /usr/local/bin/nvim
+ echo "Installed Neovim to /usr/local/bin/nvim"
+ fi
+ valid_choice=true
+ ;;
+ *)
+ handle_error "Invalid choice. Please enter a valid option (1, 2 or 3)."
+ ;;
+ esac
+ done
+}
+
+# Install Neovim
+install_neovim() {
+ local url="$1"
+ local install_action="$3"
+
+ if [ "$install_action" = "installed" ]; then
+ printf "Downloading and installing Neovim $version...\n"
+ else
+ printf "${GREEN}Updating Neovim to the latest version ($version)...${NC}\n"
+ fi
+
+ # Determine the platform-specific installation steps
+ case "$(uname -s)" in
+ Linux)
+ printf "Detected Linux OS.\n"
+ if [ -x "$(command -v fusermount)" ]; then
+ printf "FUSE is available. Downloading and running the AppImage...\n"
+ download_file "$url" "nvim.appimage"
+ chmod u+x nvim.appimage
+ "$PRIVILEGE_TOOL" cp nvim.appimage /usr/local/bin/nvim
+ "$PRIVILEGE_TOOL" mv nvim.appimage /usr/bin/nvim
+ else
+ printf "FUSE is not available. Downloading and extracting the AppImage...\n"
+ download_file "$url" "nvim.appimage"
+ chmod u+x nvim.appimage
+ ./nvim.appimage --appimage-extract
+ "$PRIVILEGE_TOOL" cp squashfs-root/usr/bin/nvim /usr/local/bin
+ "$PRIVILEGE_TOOL" mv squashfs-root/usr/bin/nvim /usr/bin
+ fi
+ ;;
+
+ Darwin)
+ printf "Detected macOS.\n"
+ download_file "$url" "nvim-macos.tar.gz"
+ xattr -c ./nvim-macos.tar.gz
+ tar xzvf nvim-macos.tar.gz
+ "$PRIVILEGE_TOOL" cp nvim-macos/bin/nvim /usr/local/bin
+ "$PRIVILEGE_TOOL" mv nvim-macos/bin/nvim /usr/bin/nvim
+ ;;
+
+ MINGW*)
+ printf "Detected Windows.\n"
+ download_file "$url" "nvim.appimage"
+ chmod +x nvim.appimage
+ if [ "$PRIVILEGE_TOOL" = "sudo" ]; then
+ "$PRIVILEGE_TOOL" cp nvim.appimage /usr/local/bin/nvim
+ "$PRIVILEGE_TOOL" mv /usr/local/bin/nvim /usr/bin
+ elif [ "$PRIVILEGE_TOOL" = "" ]; then
+ cp nvim.appimage /usr/local/bin/nvim
+ mv /usr/local/bin/nvim /usr/bin
+ else
+ printf "No privilege escalation tool found. Cannot install Neovim on Windows.\n"
+ fi
+ ;;
+
+ *)
+ printf "Unsupported operating system.\n"
+ exit 1
+ ;;
+ esac
+ # Check if the installation was successful
+ if [ $? -eq 0 ]; then
+ if [ "$install_action" = "installed" ]; then
+ printf "${GREEN}Neovim $version has been installed successfully!${NC}\n"
+ else
+ printf "${GREEN}Neovim has been updated successfully to $version!${NC}\n"
+ fi
+ else
+ printf "${RED}Error: Neovim installation/update failed.${NC}\n"
+ exit 1
+ fi
+}
+
+# Uninstall Neovim
+uninstall_neovim() {
+ printf "${RED}Uninstalling Neovim...${NC}\n"
+
+ # Detect the operating system to determine the appropriate uninstallation method
+ case "$(uname -s)" in
+ Linux)
+ printf "Detected Linux OS.\n"
+ "$PRIVILEGE_TOOL" rm /usr/local/bin/nvim
+ "$PRIVILEGE_TOOL" rm /usr/bin/nvim
+ ;;
+
+ Darwin)
+ printf "Detected macOS.\n"
+ "$PRIVILEGE_TOOL" rm /usr/local/bin/nvim
+ "$PRIVILEGE_TOOL" rm /usr/bin/nvim
+ ;;
+
+ MINGW*)
+ printf "Detected Windows.\n"
+ if [ "$PRIVILEGE_TOOL" = "sudo" ]; then
+ "$PRIVILEGE_TOOL" rm /usr/local/bin/nvim
+ "$PRIVILEGE_TOOL" rm /usr/bin/nvim
+ else
+ [ "$PRIVILEGE_TOOL" = "" ]
+ rm /usr/local/bin/nvim
+ rm /usr/bin/nvim
+ fi
+ ;;
+ *)
+ printf "Unsupported operating system.\n"
+ ;;
+ esac
+
+ printf "${GREEN}Neovim has been uninstalled successfully!${NC}\n"
+}
+
+# Check if Neovim is running
+check_neovim_running() {
+ if pgrep nvim >/dev/null; then
+ printf "${RED}Error: Neovim is currently running. Please close Neovim before proceeding.${NC}\n"
+ read -p "Do you want to forcefully terminate Neovim and continue? [yes/no] " terminate_choice
+
+ case $terminate_choice in
+ [Yy] | [Yy][Ee][Ss])
+ pkill nvim # Forcefully terminate Neovim
+ ;;
+ [Nn] | [Nn][Oo])
+ echo "Exiting..."
+ exit 1
+ ;;
+ *)
+ handle_error "Invalid choice."
+ ;;
+ esac
+ fi
+}
+
+check_neovim_running
+
+# Define the variable to control the prompt
+SHOW_PROMPT=1
+
+# Check if necessary dependencies are installed
+check_dependencies
+
+# Check for privilege escalation tools
+check_privilege_tools
+
+# Check if Neovim is already installed and ask the user if want to install it
+if check_neovim_installed; then
+ printf "${GREEN}Neovim is already installed!${NC}\n"
+else
+ printf "${RED}Neovim is not installed.${NC}\n"
+ read -p "Install Neovim? (y/n): " install_choice
+
+ case $install_choice in
+ [Yy])
+ update_version
+ ;;
+ [Nn])
+ echo "Exiting..."
+ exit
+ ;;
+ *)
+ handle_error "Invalid choice. Please enter 'y' for yes or 'n' for no."
+ ;;
+ esac
+fi
+
+# Check for updates and display breaking changes
+check_version_updates() {
+ local latest_version_url="https://api.github.com/repos/neovim/neovim/releases/latest"
+ local latest_version=""
+
+ if [ -x "$(command -v curl)" ]; then
+ latest_version=$(curl -sSL "$latest_version_url" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
+ elif [ -x "$(command -v wget)" ]; then
+ latest_version=$(wget -qO - "$latest_version_url" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
+ else
+ printf "${RED}Error: Neither curl nor wget found. Please install one of them to continue!${NC}\n"
+ exit 1
+ fi
+
+ if version_exists "$latest_version"; then
+ printf "${GREEN}An update is available!${NC}\n"
+ display_breaking_changes "$latest_version"
+ else
+ printf "You have the latest version of Neovim.\n"
+ fi
+}
+
+# To display breaking changes for a specific version
+display_breaking_changes() {
+ local version="$1"
+ local changelog_url="https://github.com/neovim/neovim/releases/tag/$version"
+ local changelog=""
+
+ if [ -x "$(command -v curl)" ]; then
+ changelog=$(curl -sSL "$changelog_url" | grep -oE '<h1>Breaking Changes.*?</ul>' | sed 's/<[^>]*>//g')
+ elif [ -x "$(command -v wget)" ]; then
+ changelog=$(wget -qO - "$changelog_url" | grep -oE '<h1>Breaking Changes.*?</ul>' | sed 's/<[^>]*>//g')
+ else
+ printf "${RED}Error: Neither curl nor wget found. Please install one of them to continue!${NC}\n"
+ exit 1
+ fi
+
+ printf "\nBreaking Changes in Neovim $version:\n"
+ printf "$changelog\n"
+}
+
+# Main loop
+while [ "$SHOW_PROMPT" -gt 0 ]; do
+ printf "Select an option:\n"
+ printf " 1. Install/update Neovim\n"
+ printf " 2. Check for updates\n"
+ printf " 3. Uninstall Neovim\n"
+ printf " 4. Run Neovim\n"
+ printf " 5. Quit\n"
+ read -p "Enter a number or press 'q' to quit: " choice
+
+ case $choice in
+ 1)
+ update_version
+ ;;
+ 2)
+ check_version_updates
+ ;;
+ 3)
+ uninstall_neovim
+ ;;
+ 4)
+ nvim
+ ;;
+ 5 | [Qq])
+ echo "Exiting..."
+ exit
+ ;;
+ *)
+ handle_error "Invalid choice. Please choose a valid option by entering the corresponding number or press 'q' to 'quit'."
+ ;;
+ esac
+done
diff --git a/common/nvim/snippets/boilerplate.lua b/common/nvim/snippets/boilerplate.lua
new file mode 100644
index 0000000..04e973a
--- /dev/null
+++ b/common/nvim/snippets/boilerplate.lua
@@ -0,0 +1,75 @@
+local ls = require("luasnip") --{{{
+local s = ls.s
+local i = ls.i
+local t = ls.t
+
+local d = ls.dynamic_node
+local c = ls.choice_node
+local f = ls.function_node
+local sn = ls.snippet_node
+
+local fmt = require("luasnip.extras.fmt").fmt
+local rep = require("luasnip.extras").rep
+
+local snippets, autosnippets = {}, {} --}}}
+
+local group = vim.api.nvim_create_augroup("Lua Snippets", { clear = true })
+local file_pattern = "*.lua"
+
+local function cs(trigger, nodes, opts) --{{{
+ local snippet = s(trigger, nodes)
+ local target_table = snippets
+
+ local pattern = file_pattern
+ local keymaps = {}
+
+ if opts ~= nil then
+ -- check for custom pattern
+ if opts.pattern then
+ pattern = opts.pattern
+ end
+
+ -- if opts is a string
+ if type(opts) == "string" then
+ if opts == "auto" then
+ target_table = autosnippets
+ else
+ table.insert(keymaps, { "i", opts })
+ end
+ end
+
+ -- if opts is a table
+ if opts ~= nil and type(opts) == "table" then
+ for _, keymap in ipairs(opts) do
+ if type(keymap) == "string" then
+ table.insert(keymaps, { "i", keymap })
+ else
+ table.insert(keymaps, keymap)
+ end
+ end
+ end
+
+ -- set autocmd for each keymap
+ if opts ~= "auto" then
+ for _, keymap in ipairs(keymaps) do
+ vim.api.nvim_create_autocmd("BufEnter", {
+ pattern = pattern,
+ group = group,
+ callback = function()
+ vim.keymap.set(keymap[1], keymap[2], function()
+ ls.snip_expand(snippet)
+ end, { noremap = true, silent = true, buffer = true })
+ end,
+ })
+ end
+ end
+ end
+
+ table.insert(target_table, snippet) -- insert snippet into appropriate table
+end --}}}
+
+-- Start Refactoring --
+
+-- End Refactoring --
+
+return snippets, autosnippets
diff --git a/common/nvim/snippets/lua.lua b/common/nvim/snippets/lua.lua
new file mode 100644
index 0000000..eb46b67
--- /dev/null
+++ b/common/nvim/snippets/lua.lua
@@ -0,0 +1,264 @@
+
+local ls = require("luasnip") --{{{
+local s = ls.s --> snippet
+local i = ls.i --> insert node
+local t = ls.t --> text node
+
+local d = ls.dynamic_node
+local c = ls.choice_node --> takes in a pos as first arg and a table of nodes
+local f = ls.function_node
+local sn = ls.snippet_node
+
+local fmt = require("luasnip.extras.fmt").fmt
+local rep = require("luasnip.extras").rep
+
+local snippets, autosnippets = {}, {} --}}}
+
+local group = vim.api.nvim_create_augroup("Lua Snippets", { clear = true })
+local file_pattern = "*.lua"
+
+local function cs(trigger, nodes, opts) --{{{
+ local snippet = s(trigger, nodes)
+ local target_table = snippets
+
+ local pattern = file_pattern
+ local keymaps = {}
+
+ if opts ~= nil then
+ -- check for custom pattern
+ if opts.pattern then
+ pattern = opts.pattern
+ end
+
+ -- if opts is a string
+ if type(opts) == "string" then
+ if opts == "auto" then
+ target_table = autosnippets
+ else
+ table.insert(keymaps, { "i", opts })
+ end
+ end
+
+ -- if opts is a table
+ if opts ~= nil and type(opts) == "table" then
+ for _, keymap in ipairs(opts) do
+ if type(keymap) == "string" then
+ table.insert(keymaps, { "i", keymap })
+ else
+ table.insert(keymaps, keymap)
+ end
+ end
+ end
+
+ -- set autocmd for each keymap
+ if opts ~= "auto" then
+ for _, keymap in ipairs(keymaps) do
+ vim.api.nvim_create_autocmd("BufEnter", {
+ pattern = pattern,
+ group = group,
+ callback = function()
+ vim.keymap.set(keymap[1], keymap[2], function()
+ ls.snip_expand(snippet)
+ end, { noremap = true, silent = true, buffer = true })
+ end,
+ })
+ end
+ end
+ end
+
+ table.insert(target_table, snippet) -- insert snippet into appropriate table
+end --}}}
+
+-- Start Refactoring --
+
+cs("CMD", { -- [CMD] multiline vim.cmd{{{
+ t({ "vim.cmd[[", " " }),
+ i(1, ""),
+ t({ "", "]]" }),
+}) --}}}
+cs("cmd", fmt("vim.cmd[[{}]]", { i(1, "") })) -- single line vim.cmd
+cs({ -- github import for packer{{{
+ trig = "https://github%.com/([%w-%._]+)/([%w-%._]+)!",
+ regTrig = true,
+ hidden = true,
+}, {
+ t([[use "]]),
+ f(function(_, snip)
+ return snip.captures[1]
+ end),
+ t("/"),
+ f(function(_, snip)
+ return snip.captures[2]
+ end),
+ t({ [["]], "" }),
+ i(1, ""),
+}, "auto") --}}}
+
+cs( -- {regexSnippet} LuaSnippet{{{
+ "regexSnippet",
+ fmt(
+ [=[
+cs( -- {}
+{{ trig = "{}", regTrig = true, hidden = true }}, fmt([[
+{}
+]], {{
+ {}
+}}))
+ ]=],
+ {
+ i(1, "Description"),
+ i(2, ""),
+ i(3, ""),
+ i(4, ""),
+ }
+ ),
+ { pattern = "*/snippets/*.lua", "<C-d>" }
+) --}}}
+cs( -- [luaSnippet] LuaSnippet{{{
+ "luaSnippet",
+ fmt(
+ [=[
+cs("{}", fmt( -- {}
+[[
+{}
+]], {{
+ {}
+ }}){})
+ ]=],
+ {
+ i(1, ""),
+ i(2, "Description"),
+ i(3, ""),
+ i(4, ""),
+ c(5, {
+ t(""),
+ fmt([[, "{}"]], { i(1, "keymap") }),
+ fmt([[, {{ pattern = "{}", {} }}]], { i(1, "*/snippets/*.lua"), i(2, "keymap") }),
+ }),
+ }
+ ),
+ { pattern = "*/snippets/*.lua", "jcs" }
+) --}}}
+
+cs( -- choice_node_snippet luaSnip choice node{{{
+ "choice_node_snippet",
+ fmt(
+ [[
+c({}, {{ {} }}),
+]],
+ {
+ i(1, ""),
+ i(2, ""),
+ }
+ ),
+ { pattern = "*/snippets/*.lua", "jcn" }
+) --}}}
+
+cs( -- [function] Lua function snippet{{{
+ "function",
+ fmt(
+ [[
+function {}({})
+ {}
+end
+]],
+ {
+ i(1, ""),
+ i(2, ""),
+ i(3, ""),
+ }
+ ),
+ "jff"
+) --}}}
+cs( -- [local_function] Lua function snippet{{{
+ "local_function",
+ fmt(
+ [[
+local function {}({})
+ {}
+end
+]],
+ {
+ i(1, ""),
+ i(2, ""),
+ i(3, ""),
+ }
+ ),
+ "jlf"
+) --}}}
+cs( -- [local] Lua local variable snippet{{{
+ "local",
+ fmt(
+ [[
+local {} = {}
+ ]],
+ { i(1, ""), i(2, "") }
+ ),
+ "jj"
+) --}}}
+-- Tutorial Snippets go here --
+local myFirstSnippet = s("myFirstSnippet", {
+ t("Hi! This is my first snippet in Luasnip "),
+ i(1, "placeholder"),
+ t({"", "this is another text node", ""}),
+ i(2, "put here"),
+})
+table.insert(snippets, myFirstSnippet)
+
+
+
+local mySecondSnippet = s(
+ "mySecondSnippet",
+ fmt(
+ [[
+ local {} = function({})
+ {} {{ im in a curly braces }}
+ end {}
+ ]],
+ {
+ i(1, "myVar"),
+ c(2, { t(""), i(1, "myArg") }),
+ i(3, "-- TODO: something"),
+ i(4, "-- nice")
+ }
+ )
+)
+table.insert(snippets, mySecondSnippet)
+
+local myFirstAutoSnippet = s("automatic", { t("This was auto triggered") })
+table.insert(autosnippets, myFirstAutoSnippet)
+
+local mySecondAutoSnippet = s({ trig = "digit(%d)(%d)", regTrig = true }, {
+ f(function(_, snip)
+ return snip.captures[1] .. " + "
+ end),
+ f(function(_, snip)
+ return snip.captures[2]
+ end),
+})
+table.insert(autosnippets, mySecondAutoSnippet)
+
+
+
+
+
+
+
+
+-- End Refactoring --
+
+return snippets, autosnippets
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/common/nvim/snippets/markdown.lua b/common/nvim/snippets/markdown.lua
new file mode 100644
index 0000000..d0d1487
--- /dev/null
+++ b/common/nvim/snippets/markdown.lua
@@ -0,0 +1,58 @@
+local ls = require("luasnip")
+local s = ls.s
+local i = ls.i
+local t = ls.t
+
+local d = ls.dynamic_node
+local c = ls.choice_node
+local f = ls.function_node
+local sn = ls.snippet_node
+
+local fmt = require("luasnip.extras.fmt").fmt
+local rep = require("luasnip.extras").rep
+
+-- --
+
+local snippets = {}
+local autosnippets = {}
+
+local autocmd = vim.api.nvim_create_autocmd
+local augroup = vim.api.nvim_create_augroup
+local map = vim.keymap.set
+local opts = { noremap = true, silent = true, buffer = true }
+local group = augroup("Markdown Snippets", { clear = true })
+
+local function cs(trigger, nodes, keymap) --> cs stands for create snippet
+ local snippet = s(trigger, nodes)
+ table.insert(snippets, snippet)
+
+ if keymap ~= nil then
+ local pattern = "*.md"
+ if type(keymap) == "table" then
+ pattern = keymap[1]
+ keymap = keymap[2]
+ end
+ autocmd("BufEnter", {
+ pattern = pattern,
+ group = group,
+ callback = function()
+ map({ "i" }, keymap, function()
+ ls.snip_expand(snippet)
+ end, opts)
+ end,
+ })
+ end
+end
+
+local function lp(package_name) -- Load Package Function
+ package.loaded[package_name] = nil
+ return require(package_name)
+end
+
+-- Utility Functions --
+
+-- Start Refactoring --
+
+-- Start Refactoring --
+
+return snippets, autosnippets