From 3213bbc1147b57a2474975c9f7a29e3eaa55d9dc Mon Sep 17 00:00:00 2001 From: PlexSheep Date: Wed, 6 Sep 2023 00:26:45 +0200 Subject: [PATCH] shameless steal --- init.lua | 46 ++ lua/plex/config/autocmds.lua | 159 ++++++ lua/plex/config/init.lua | 318 +++++++++++ lua/plex/config/keymaps.lua | 359 ++++++++++++ lua/plex/config/options.lua | 253 +++++++++ lua/plex/init.lua | 8 + lua/plex/lib/badge.lua | 210 +++++++ lua/plex/lib/color.lua | 140 +++++ lua/plex/lib/contextmenu.lua | 94 ++++ lua/plex/lib/edit.lua | 141 +++++ lua/plex/lib/preview.lua | 108 ++++ lua/plex/lib/utils.lua | 122 +++++ lua/plex/plugins/coding.lua | 325 +++++++++++ lua/plex/plugins/colorscheme.lua | 73 +++ lua/plex/plugins/core.lua | 6 + lua/plex/plugins/editor.lua | 514 ++++++++++++++++++ lua/plex/plugins/extras/coding/autopairs.lua | 18 + lua/plex/plugins/extras/coding/cmp-git.lua | 26 + lua/plex/plugins/extras/coding/copilot.lua | 107 ++++ .../plugins/extras/coding/editorconfig.lua | 20 + lua/plex/plugins/extras/coding/emmet.lua | 31 ++ lua/plex/plugins/extras/coding/sandwich.lua | 19 + .../plugins/extras/diagnostics/proselint.lua | 22 + .../plugins/extras/diagnostics/write-good.lua | 22 + lua/plex/plugins/extras/editor/anyjump.lua | 22 + lua/plex/plugins/extras/editor/flybuf.lua | 11 + lua/plex/plugins/extras/editor/sidebar.lua | 14 + lua/plex/plugins/extras/editor/ufo.lua | 112 ++++ .../plugins/extras/formatting/prettier.lua | 36 ++ lua/plex/plugins/extras/git/fugitive.lua | 31 ++ lua/plex/plugins/extras/lang/ansible.lua | 72 +++ lua/plex/plugins/extras/lang/docker.lua | 50 ++ lua/plex/plugins/extras/lang/go.lua | 199 +++++++ lua/plex/plugins/extras/lang/helm.lua | 24 + lua/plex/plugins/extras/lang/json.lua | 40 ++ lua/plex/plugins/extras/lang/python.lua | 72 +++ lua/plex/plugins/extras/lang/terraform.lua | 38 ++ lua/plex/plugins/extras/lang/typescript.lua | 125 +++++ lua/plex/plugins/extras/lang/yaml.lua | 63 +++ lua/plex/plugins/extras/linting/ruff.lua | 50 ++ lua/plex/plugins/extras/lsp/gtd.lua | 33 ++ lua/plex/plugins/extras/lsp/inlayhints.lua | 22 + lua/plex/plugins/extras/lsp/lightbulb.lua | 20 + lua/plex/plugins/extras/lsp/null-ls.lua | 62 +++ .../plugins/extras/lsp/yaml-companion.lua | 33 ++ lua/plex/plugins/extras/org/vimwiki.lua | 44 ++ lua/plex/plugins/extras/treesitter/treesj.lua | 10 + lua/plex/plugins/extras/ui/barbecue.lua | 28 + lua/plex/plugins/extras/ui/bufferline.lua | 45 ++ lua/plex/plugins/extras/ui/cursorword.lua | 47 ++ lua/plex/plugins/extras/ui/cybu.lua | 21 + lua/plex/plugins/extras/ui/deadcolumn.lua | 9 + lua/plex/plugins/extras/ui/goto-preview.lua | 51 ++ lua/plex/plugins/extras/ui/incline.lua | 7 + lua/plex/plugins/extras/ui/minimap.lua | 19 + lua/plex/plugins/extras/ui/statuscol.lua | 24 + lua/plex/plugins/git.lua | 246 +++++++++ lua/plex/plugins/lsp/format.lua | 166 ++++++ lua/plex/plugins/lsp/highlight.lua | 47 ++ lua/plex/plugins/lsp/init.lua | 326 +++++++++++ lua/plex/plugins/lsp/keymaps.lua | 178 ++++++ lua/plex/plugins/lualine.lua | 285 ++++++++++ lua/plex/plugins/neo-tree.lua | 234 ++++++++ lua/plex/plugins/telescope.lua | 512 +++++++++++++++++ lua/plex/plugins/treesitter.lua | 190 +++++++ lua/plex/plugins/ui.lua | 343 ++++++++++++ 66 files changed, 7102 insertions(+) create mode 100644 init.lua create mode 100644 lua/plex/config/autocmds.lua create mode 100644 lua/plex/config/init.lua create mode 100644 lua/plex/config/keymaps.lua create mode 100644 lua/plex/config/options.lua create mode 100644 lua/plex/init.lua create mode 100644 lua/plex/lib/badge.lua create mode 100644 lua/plex/lib/color.lua create mode 100644 lua/plex/lib/contextmenu.lua create mode 100644 lua/plex/lib/edit.lua create mode 100644 lua/plex/lib/preview.lua create mode 100644 lua/plex/lib/utils.lua create mode 100644 lua/plex/plugins/coding.lua create mode 100644 lua/plex/plugins/colorscheme.lua create mode 100644 lua/plex/plugins/core.lua create mode 100644 lua/plex/plugins/editor.lua create mode 100644 lua/plex/plugins/extras/coding/autopairs.lua create mode 100644 lua/plex/plugins/extras/coding/cmp-git.lua create mode 100644 lua/plex/plugins/extras/coding/copilot.lua create mode 100644 lua/plex/plugins/extras/coding/editorconfig.lua create mode 100644 lua/plex/plugins/extras/coding/emmet.lua create mode 100644 lua/plex/plugins/extras/coding/sandwich.lua create mode 100644 lua/plex/plugins/extras/diagnostics/proselint.lua create mode 100644 lua/plex/plugins/extras/diagnostics/write-good.lua create mode 100644 lua/plex/plugins/extras/editor/anyjump.lua create mode 100644 lua/plex/plugins/extras/editor/flybuf.lua create mode 100644 lua/plex/plugins/extras/editor/sidebar.lua create mode 100644 lua/plex/plugins/extras/editor/ufo.lua create mode 100644 lua/plex/plugins/extras/formatting/prettier.lua create mode 100644 lua/plex/plugins/extras/git/fugitive.lua create mode 100644 lua/plex/plugins/extras/lang/ansible.lua create mode 100644 lua/plex/plugins/extras/lang/docker.lua create mode 100644 lua/plex/plugins/extras/lang/go.lua create mode 100644 lua/plex/plugins/extras/lang/helm.lua create mode 100644 lua/plex/plugins/extras/lang/json.lua create mode 100644 lua/plex/plugins/extras/lang/python.lua create mode 100644 lua/plex/plugins/extras/lang/terraform.lua create mode 100644 lua/plex/plugins/extras/lang/typescript.lua create mode 100644 lua/plex/plugins/extras/lang/yaml.lua create mode 100644 lua/plex/plugins/extras/linting/ruff.lua create mode 100644 lua/plex/plugins/extras/lsp/gtd.lua create mode 100644 lua/plex/plugins/extras/lsp/inlayhints.lua create mode 100644 lua/plex/plugins/extras/lsp/lightbulb.lua create mode 100644 lua/plex/plugins/extras/lsp/null-ls.lua create mode 100644 lua/plex/plugins/extras/lsp/yaml-companion.lua create mode 100644 lua/plex/plugins/extras/org/vimwiki.lua create mode 100644 lua/plex/plugins/extras/treesitter/treesj.lua create mode 100644 lua/plex/plugins/extras/ui/barbecue.lua create mode 100644 lua/plex/plugins/extras/ui/bufferline.lua create mode 100644 lua/plex/plugins/extras/ui/cursorword.lua create mode 100644 lua/plex/plugins/extras/ui/cybu.lua create mode 100644 lua/plex/plugins/extras/ui/deadcolumn.lua create mode 100644 lua/plex/plugins/extras/ui/goto-preview.lua create mode 100644 lua/plex/plugins/extras/ui/incline.lua create mode 100644 lua/plex/plugins/extras/ui/minimap.lua create mode 100644 lua/plex/plugins/extras/ui/statuscol.lua create mode 100644 lua/plex/plugins/git.lua create mode 100644 lua/plex/plugins/lsp/format.lua create mode 100644 lua/plex/plugins/lsp/highlight.lua create mode 100644 lua/plex/plugins/lsp/init.lua create mode 100644 lua/plex/plugins/lsp/keymaps.lua create mode 100644 lua/plex/plugins/lualine.lua create mode 100644 lua/plex/plugins/neo-tree.lua create mode 100644 lua/plex/plugins/telescope.lua create mode 100644 lua/plex/plugins/treesitter.lua create mode 100644 lua/plex/plugins/ui.lua diff --git a/init.lua b/init.lua new file mode 100644 index 0000000..65d9b7a --- /dev/null +++ b/init.lua @@ -0,0 +1,46 @@ +-- plex Neovim entry-point +-- https://git.cscherr.de/PlexSheep/neovim-conf + +local config = require('plex.config') +config.ensure_lazy() + +-- Start lazy.nvim plugin manager. +require('lazy').setup(vim.tbl_extend('keep', config.user_lazy_opts(), { + spec = { + { import = 'plex.plugins' }, + -- { import = 'plex.plugins.extras.lang.go' }, + -- { import = 'plex.plugins.extras.lang.json' }, + -- { import = 'plex.plugins.extras.lang.python' }, + -- { import = 'plex.plugins.extras.lang.yaml' }, + + -- This will load a custom user lua/plugins.lua or lua/plugins/* + config.has_user_plugins() and { import = 'plugins' } or nil, + }, + concurrency = vim.loop.available_parallelism() * 2, + defaults = { lazy = true, version = false }, + dev = { path = config.path_join(vim.fn.stdpath('config'), 'dev') }, + install = { missing = true, colorscheme = {} }, + checker = { enabled = true, notify = false }, + change_detection = { notify = false }, + ui = { border = 'rounded' }, + diff = { cmd = 'terminal_git' }, + performance = { + rtp = { + disabled_plugins = { + 'gzip', + 'vimballPlugin', + 'matchit', + 'matchparen', + '2html_plugin', + 'tarPlugin', + 'netrwPlugin', + 'tutor', + 'zipPlugin', + 'vim-lsp', + 'lua-ls', + }, + }, + }, +})) + +config.setup() diff --git a/lua/plex/config/autocmds.lua b/lua/plex/config/autocmds.lua new file mode 100644 index 0000000..bed3c6f --- /dev/null +++ b/lua/plex/config/autocmds.lua @@ -0,0 +1,159 @@ +-- Rafi's Neovim autocmds +-- github.com/plex/vim-config +-- === + +-- This file is automatically loaded by plex.config.init + +local function augroup(name) + return vim.api.nvim_create_augroup('plex_' .. name, {}) +end + +-- Check if we need to reload the file when it changed +vim.api.nvim_create_autocmd({ 'FocusGained', 'TermClose', 'TermLeave' }, { + group = augroup('checktime'), + command = 'checktime', +}) + +-- Go to last loc when opening a buffer, see ':h last-position-jump' +vim.api.nvim_create_autocmd('BufReadPost', { + group = augroup('last_loc'), + callback = function() + local exclude = { 'gitcommit', 'commit', 'gitrebase' } + local buf = vim.api.nvim_get_current_buf() + if vim.tbl_contains(exclude, vim.bo[buf].filetype) then + return + end + local mark = vim.api.nvim_buf_get_mark(buf, '"') + local lcount = vim.api.nvim_buf_line_count(buf) + if mark[1] > 0 and mark[1] <= lcount then + pcall(vim.api.nvim_win_set_cursor, 0, mark) + end + end, +}) + +-- Show cursor line only in active window +vim.api.nvim_create_autocmd({ 'InsertLeave', 'WinEnter' }, { + group = augroup('auto_cursorline_show'), + callback = function(event) + if vim.bo[event.buf].buftype == '' then + vim.opt_local.cursorline = true + end + end, +}) +vim.api.nvim_create_autocmd({ 'InsertEnter', 'WinLeave' }, { + group = augroup('auto_cursorline_hide'), + callback = function(_) + vim.opt_local.cursorline = false + end, +}) + +-- Highlight on yank +vim.api.nvim_create_autocmd('TextYankPost', { + group = augroup('highlight_yank'), + callback = function() + vim.highlight.on_yank() + end, +}) + +-- Automatically set read-only for files being edited elsewhere +vim.api.nvim_create_autocmd('SwapExists', { + group = augroup('open_swap'), + nested = true, + callback = function() + vim.v.swapchoice = 'o' + end, +}) + +-- Create directories when needed, when saving a file (except for URIs "://"). +vim.api.nvim_create_autocmd('BufWritePre', { + group = augroup('auto_create_dir'), + callback = function(event) + if event.match:match('^%w%w+://') then + return + end + local file = vim.loop.fs_realpath(event.match) or event.match + vim.fn.mkdir(vim.fn.fnamemodify(file, ':p:h'), 'p') + end, +}) + +-- Disable conceallevel for specific file-types. +vim.api.nvim_create_autocmd('FileType', { + group = augroup('fix_conceallevel'), + pattern = { 'markdown' }, + callback = function() + vim.opt_local.conceallevel = 0 + end, +}) + +-- Resize splits if window got resized +vim.api.nvim_create_autocmd('VimResized', { + group = augroup('resize_splits'), + callback = function() + vim.cmd('wincmd =') + end, +}) + +-- Wrap and enable spell-checker in text filetypes +vim.api.nvim_create_autocmd('FileType', { + group = augroup('spell_conceal'), + pattern = { 'gitcommit', 'markdown' }, + callback = function() + vim.opt_local.spell = true + vim.opt_local.conceallevel = 0 + end, +}) + +-- Close some filetypes with +vim.api.nvim_create_autocmd('FileType', { + group = augroup('close_with_q'), + pattern = { + 'blame', + 'checkhealth', + 'fugitive', + 'fugitiveblame', + 'help', + 'httpResult', + 'lspinfo', + 'notify', + 'PlenaryTestPopup', + 'qf', + 'spectre_panel', + 'startuptime', + 'tsplayground', + }, + callback = function(event) + vim.bo[event.buf].buflisted = false + -- stylua: ignore + vim.keymap.set('n', 'q', 'close', { buffer = event.buf, silent = true }) + end, +}) + +vim.api.nvim_create_autocmd('BufWritePre', { + group = augroup('undo_disable'), + pattern = { '/tmp/*', '*.tmp', '*.bak', 'COMMIT_EDITMSG', 'MERGE_MSG' }, + callback = function(event) + vim.opt_local.undofile = false + if event.file == 'COMMIT_EDITMSG' or event.file == 'MERGE_MSG' then + vim.opt_local.swapfile = false + end + end, +}) + +-- Disable swap/undo/backup files in temp directories or shm +vim.api.nvim_create_autocmd({ 'BufNewFile', 'BufReadPre' }, { + group = augroup('secure'), + pattern = { + '/tmp/*', + '$TMPDIR/*', + '$TMP/*', + '$TEMP/*', + '*/shm/*', + '/private/var/*', + }, + callback = function() + vim.opt_local.undofile = false + vim.opt_local.swapfile = false + vim.opt_global.backup = false + vim.opt_global.writebackup = false + end, +}) diff --git a/lua/plex/config/init.lua b/lua/plex/config/init.lua new file mode 100644 index 0000000..a52cb89 --- /dev/null +++ b/lua/plex/config/init.lua @@ -0,0 +1,318 @@ +-- Rafi's Neovim config loader +-- https://github.com/plex/vim-config + +-- This is part of LazyVim's code, with my modifications. +-- See: https://github.com/LazyVim/LazyVim/blob/main/lua/lazyvim/config/init.lua + +---@type RafiConfig +local M = {} + +M.lazy_version = '>=9.1.0' + +---@class RafiConfig +local defaults = { + -- Load the default settings + -- stylua: ignore + defaults = { + autocmds = true, -- plex.config.autocmds + keymaps = true, -- plex.config.keymaps + -- plex.config.options can't be configured here since it's loaded + -- prematurely. You can disable loading options with the following line at + -- the top of your lua/config/setup.lua or init.lua: + -- `package.loaded['plex.config.options'] = true` + }, + + -- String like `habamax` or a function that will load the colorscheme. + -- Disabled by default to allow theme-loader.nvim to manage the colorscheme. + ---@type string|fun() + colorscheme = '', + + features = { + elite_mode = false, + window_q_mapping = true, + }, + + -- stylua: ignore + icons = { + git = ' ', + diagnostics = { + Error = '✘', --   ✘ + Warn = '󰀪', -- 󰀪 󰳤 󱦄 󱗓  + Info = 'ⁱ', --    ⁱ 󰋼 󰋽 + Hint = '', --  󰌶  + }, + status = { + git = { + added = '₊', --  ₊ + modified = '∗', --  ∗ + removed = '₋', --  ₋ + }, + diagnostics = { + error = ' ', + warn = ' ', + info = ' ', + hint = '󰌶 ', + }, + filename = { + modified = '+', + readonly = '🔒', + zoomed = '🔎', + }, + }, + -- Default completion kind symbols. + kinds = { + Array = '󰅪 ', --  󰅪 󰅨 󱃶 + Boolean = '◩ ', --  ◩ 󰔡 󱃙 󰟡 󰨙 + Class = '󰌗 ', --  󰌗 󰠱 𝓒 + Color = '󰏘 ', -- 󰸌 󰏘 + Constant = '󰏿 ', --   󰏿 + Constructor = '󰆧 ', --  󰆧    + Copilot = ' ', --  + Enum = '󰕘 ', --  󰕘 ℰ  + EnumMember = ' ', --   + Event = ' ', --   + Field = ' ', -- 󰄶  󰆨  󰀻 󰃒 + File = ' ', --    󰈔 󰈙 + Folder = ' ', --   󰉋 + Function = '󰊕 ', --  󰊕  + Interface = ' ', --     + Key = ' ', --  + Keyword = ' ', --   󰌋  + Method = '󰆧 ', --  󰆧 ƒ + Module = ' ', --   󰅩 󰆧 󰏗 + Namespace = ' ', --   󰅩 + Null = ' ', --  󰟢 + Number = '󰎠 ', --  󰎠  + Object = ' ', --   󰅩 + Operator = '󰃬 ', --  󰃬 󰆕 + + Package = ' ', --   󰏖 󰏗 + Property = '󰖷 ', --  󰜢   + Reference = '󰈝 ', --  󰈝 󰈇 + Snippet = ' ', --  󰘌 ⮡    + String = '󰅳 ', --  󰅳 + Struct = ' ', --   𝓢 󰙅 󱏒 + Text = ' ', --   󰉿 𝓐 + TypeParameter = ' ', --  󰊄 𝙏 + Unit = ' ', --   󰑭  + Value = ' ', --   󰀬 󰎠 + Variable = ' ', --   󰀫  + }, + }, +} + +M.renames = {} + +M.did_init = false +function M.init() + if not M.did_init then + M.did_init = true + -- delay notifications till vim.notify was replaced or after 500ms + require('plex.config').lazy_notify() + + -- load options here, before lazy init while sourcing plugin modules + -- this is needed to make sure options will be correctly applied + -- after installing missing plugins + require('plex.config').load('options') + + -- carry over plugin options that their name has been changed. + local Plugin = require('lazy.core.plugin') + local add = Plugin.Spec.add + ---@diagnostic disable-next-line: duplicate-set-field + Plugin.Spec.add = function(self, plugin, ...) + if type(plugin) == 'table' and M.renames[plugin[1]] then + plugin[1] = M.renames[plugin[1]] + end + return add(self, plugin, ...) + end + end +end + +---@type RafiConfig +local options + +-- Load plex and user config files. +---@param user_opts table|nil +function M.setup(user_opts) + if not M.did_init then + M.init() + end + options = vim.tbl_deep_extend('force', defaults, user_opts or {}) + if not M.has_version() then + require('lazy.core.util').error( + string.format( + '**lazy.nvim** version %s is required.\n Please upgrade **lazy.nvim**', + M.lazy_version + ) + ) + error('Exiting') + end + + -- Override config with user config at lua/config/setup.lua + local ok, user_setup = pcall(require, 'config.setup') + if ok and user_setup.override then + options = vim.tbl_deep_extend('force', options, user_setup.override()) + end + for feat_name, feat_val in pairs(options.features) do + vim.g['plex_' .. feat_name] = feat_val + end + + M.load('autocmds') + M.load('keymaps') + + -- Set colorscheme + require('lazy.core.util').try(function() + if type(M.colorscheme) == 'function' then + M.colorscheme() + elseif #M.colorscheme > 0 then + vim.cmd.colorscheme(M.colorscheme) + end + end, { + msg = 'Could not load your colorscheme', + on_error = function(msg) + require('lazy.core.util').error(msg) + vim.cmd.colorscheme('habamax') + end, + }) +end + +---@return table +function M.user_lazy_opts() + local ok, user_setup = pcall(require, 'config.setup') + if ok and user_setup.lazy_opts then + return user_setup.lazy_opts() + end + return {} +end + +---@param range? string +---@return boolean +function M.has_version(range) + local Semver = require('lazy.manage.semver') + return Semver.range(range or M.lazy_version) + :matches(require('lazy.core.config').version or '0.0.0') +end + +---@param name "autocmds" | "options" | "keymaps" +function M.load(name) + local Util = require('lazy.core.util') + local function _load(mod) + Util.try(function() + require(mod) + end, { + msg = 'Failed loading ' .. mod, + on_error = function(msg) + local info = require('lazy.core.cache').find(mod) + if info == nil or (type(info) == 'table' and #info == 0) then + return + end + Util.error(msg) + end, + }) + end + -- always load plex's file, then user file + if M.defaults[name] or name == 'options' then + _load('plex.config.' .. name) + end + _load('config.' .. name) + if vim.bo.filetype == 'lazy' then + vim.cmd([[do VimResized]]) + end +end + +-- Ensure package manager (lazy.nvim) exists. +function M.ensure_lazy() + local lazypath = M.path_join(vim.fn.stdpath('data'), 'lazy', 'lazy.nvim') + if not vim.loop.fs_stat(lazypath) then + print('Installing lazy.nvim…') + vim.fn.system({ + 'git', + 'clone', + '--filter=blob:none', + '--branch=stable', + 'https://github.com/folke/lazy.nvim.git', + lazypath, + }) + end + vim.opt.rtp:prepend(lazypath) +end + +-- Validate if lua/plugins/ or lua/plugins.lua exist. +---@return boolean +function M.has_user_plugins() + local user_path = M.path_join(vim.fn.stdpath('config'), 'lua') + return vim.loop.fs_stat(M.path_join(user_path, 'plugins')) ~= nil + or vim.loop.fs_stat(M.path_join(user_path, 'plugins.lua')) ~= nil +end + +-- Delay notifications till vim.notify was replaced or after 500ms. +function M.lazy_notify() + local notifs = {} + local function temp(...) + table.insert(notifs, vim.F.pack_len(...)) + end + + local orig = vim.notify + vim.notify = temp + + local timer = vim.loop.new_timer() + local check = vim.loop.new_check() + if timer == nil or check == nil then + return + end + + local replay = function() + timer:stop() + check:stop() + if vim.notify == temp then + vim.notify = orig -- put back the original notify if needed + end + vim.schedule(function() + ---@diagnostic disable-next-line: no-unknown + for _, notif in ipairs(notifs) do + vim.notify(vim.F.unpack_len(notif)) + end + end) + end + + -- wait till vim.notify has been replaced + check:start(function() + if vim.notify ~= temp then + replay() + end + end) + -- or if it took more than 500ms, then something went wrong + timer:start(500, 0, replay) +end + +-- Join paths. +---@private +M.path_join = function(...) + return table.concat({ ... }, M.path_sep) +end + +-- Variable holds OS directory separator. +---@private +M.path_sep = (function() + if jit then + local os = string.lower(jit.os) + if os ~= 'windows' then + return '/' + else + return '\\' + end + else + return package.config:sub(1, 1) + end +end)() + +setmetatable(M, { + __index = function(_, key) + if options == nil then + return vim.deepcopy(defaults)[key] + end + ---@cast options RafiConfig + return options[key] + end, +}) + +return M diff --git a/lua/plex/config/keymaps.lua b/lua/plex/config/keymaps.lua new file mode 100644 index 0000000..faaa3f3 --- /dev/null +++ b/lua/plex/config/keymaps.lua @@ -0,0 +1,359 @@ +-- Rafi's Neovim keymaps +-- github.com/plex/vim-config +-- === + +-- This file is automatically loaded by plex.config.init + +local Util = require('plex.lib.utils') +local map = vim.keymap.set + +local function augroup(name) + return vim.api.nvim_create_augroup('plex_' .. name, {}) +end + +-- Elite-mode: Arrow-keys resize window +if vim.g.plex_elite_mode then + map('n', '', 'resize +1', { desc = 'Resize Window' }) + map('n', '', 'resize -1', { desc = 'Resize Window' }) + map('n', '', 'vertical resize +1', { desc = 'Resize Window' }) + map('n', '', 'vertical resize -1', { desc = 'Resize Window' }) +end + +-- Package-manager +map('n', 'l', 'Lazy', { desc = 'Open Lazy UI' }) + +-- stylua: ignore start + +-- Navigation +-- === + +-- Moves through display-lines, unless count is provided +map({ 'n', 'x' }, 'j', "v:count == 0 ? 'gj' : 'j'", { expr = true, silent = true }) +map({ 'n', 'x' }, 'k', "v:count == 0 ? 'gk' : 'k'", { expr = true, silent = true }) + +-- Easier line-wise movement +map('n', 'gh', 'g^') +map('n', 'gl', 'g$') + +map('n', '', 'V', { desc = 'Visual Mode' }) +map('x', '', '', { desc = 'Exit Visual Mode' }) + +-- Toggle fold or select option from popup menu +---@return string +map('n', '', function() + return vim.fn.pumvisible() == 1 and '' or 'za' +end, { expr = true, desc = 'Toggle Fold' }) + +-- Focus the current fold by closing all others +map('n', '', 'zMzv', { remap = true, desc = 'Focus Fold' }) + +-- Location/quickfix list movement +if not Util.has('mini.bracketed') and not Util.has('trouble.nvim') then + map('n', ']q', 'cnext', { desc = 'Next Quickfix Item' }) + map('n', '[q', 'cprev', { desc = 'Previous Quickfix Item' }) +end +map('n', ']a', 'lnext', { desc = 'Next Loclist Item' }) +map('n', '[a', 'lprev', { desc = 'Previous Loclist Item' }) + +-- Whitespace jump (see plugin/whitespace.vim) +map('n', ']s', function() + require('plex.lib.edit').whitespace_jump(1) +end, { desc = 'Next Whitespace' }) +map('n', '[s', function() + require('plex.lib.edit').whitespace_jump(-1) +end, { desc = 'Previous Whitespace' }) + +-- Navigation in command line +map('c', '', '') +map('c', '', '') +map('c', '', '') +map('c', '', '') + +-- Scroll step sideways +map('n', 'zl', 'z4l') +map('n', 'zh', 'z4h') + +-- Clipboard +-- === + +-- Yank buffer's relative path to clipboard +map('n', 'y', function() + local path = vim.fn.fnamemodify(vim.api.nvim_buf_get_name(0), ':~:.') + vim.fn.setreg('+', path) + vim.notify(path, vim.log.levels.INFO, { title = 'Yanked relative path' }) +end, { silent = true, desc = 'Yank relative path' }) + +-- Yank absolute path +map('n', 'Y', function() + local path = vim.fn.fnamemodify(vim.api.nvim_buf_get_name(0), ':p') + vim.fn.setreg('+', path) + vim.notify(path, vim.log.levels.INFO, { title = 'Yanked absolute path' }) +end, { silent = true, desc = 'Yank absolute path' }) + +-- Paste in visual-mode without pushing to register +map('x', 'p', 'p:let @+=@0:let @"=@0', { silent = true, desc = 'Paste' }) +map('x', 'P', 'P:let @+=@0:let @"=@0', { silent = true, desc = 'Paste In-place' }) + +-- Edit +-- === + +-- Macros +map('n', '', 'q', { desc = 'Macro Prefix' }) + +-- Start new line from any cursor position in insert-mode +map('i', '', 'o', { desc = 'Start Newline' }) + +-- Re-select blocks after indenting in visual/select mode +map('x', '<', '', '>gv|', { desc = 'Indent Left and Re-select' }) + +-- Use tab for indenting in visual/select mode +map('x', '', '>gv|', { desc = 'Indent Left' }) +map('x', '', 'k', 'move-2==', { desc = 'Move line up' }) +map('n', 'j', 'move+==', { desc = 'Move line down' }) +map('x', 'k', ":move'<-2gv=gv", { desc = 'Move selection up' }) +map('x', 'j', ":move'>+gv=gv", { desc = 'Move selection down' }) + +-- Duplicate lines without affecting PRIMARY and CLIPBOARD selections. +map('n', 'd', 'm`""Y""P``', { desc = 'Duplicate line' }) +map('x', 'd', '""Y""Pgv', { desc = 'Duplicate selection' }) + +-- Duplicate paragraph +map('n', 'cp', 'yapp', { desc = 'Duplicate Paragraph' }) + +-- Remove spaces at the end of lines +map('n', 'cw', 'lua MiniTrailspace.trim()', { desc = 'Erase Whitespace' }) + +-- Search & Replace +-- === + +-- Switch */g* and #/g# +map('n', '*', 'g*') +map('n', 'g*', '*') +map('n', '#', 'g#') +map('n', 'g#', '#') + +-- Clear search with +map('n', '', 'noh', { desc = 'Clear Search Highlight' }) + +-- Clear search, diff update and redraw taken from runtime/lua/_editor.lua +map( + 'n', + 'ur', + 'nohlsearchdiffupdatenormal! ', + { desc = 'Redraw / clear hlsearch / diff update' } +) + +-- Use backspace key for matching parens +map({ 'n', 'x' }, '', '%', { remap = true, desc = 'Jump to Paren' }) + +-- Select last paste +map('n', 'gpp', "'`['.strpart(getregtype(), 0, 1).'`]'", { expr = true, desc = 'Select Paste' }) + +-- Quick substitute within selected area +map('x', 'sg', ':s//gc', { desc = 'Substitute Within Selection' }) + +-- C-r: Easier search and replace visual/select mode +map( + 'x', + '', + ":%s/\\V=v:lua.require'plex.lib.edit'.get_visual_selection()" + .. '//gc', + { desc = 'Replace Selection' } +) + +-- Command & History +-- === + +-- Start an external command with a single bang +map('n', '!', ':!', { desc = 'Execute Shell Command' }) + +-- Put vim command output into buffer +map('n', 'g!', ":put=execute('')", { desc = 'Paste Command' }) + +-- Switch history search pairs, matching my bash shell +---@return string +map('c', '', function() + return vim.fn.pumvisible() == 1 and '' or '' +end, { expr = true }) + +map('c', '', function() + return vim.fn.pumvisible() == 1 and '' or '' +end, { expr = true }) + +map('c', '', '') +map('c', '', '') + +-- Allow misspellings +vim.cmd.cnoreabbrev('qw', 'wq') +vim.cmd.cnoreabbrev('Wq', 'wq') +vim.cmd.cnoreabbrev('WQ', 'wq') +vim.cmd.cnoreabbrev('Qa', 'qa') +vim.cmd.cnoreabbrev('Bd', 'bd') +vim.cmd.cnoreabbrev('bD', 'bd') + +-- File operations +-- === + +-- Switch (window) to the directory of the current opened buffer +map('n', 'cd', function() + local bufdir = vim.fn.fnamemodify(vim.api.nvim_buf_get_name(0), ':p:h') + if bufdir ~= nil and vim.loop.fs_stat(bufdir) then + vim.cmd.tcd(bufdir) + vim.notify(bufdir) + end +end, { desc = 'Change Local Directory' }) + +-- Fast saving from all modes +map('n', 'w', 'write', { desc = 'Save' }) +map({ 'n', 'i', 'v' }, '', 'write', { desc = 'Save' }) + +-- Editor UI +-- === + +-- Toggle editor's visual effects +map('n', 'uf', require('plex.plugins.lsp.format').toggle, { desc = 'Toggle format on Save' }) +map('n', 'us', 'setlocal spell!', { desc = 'Toggle Spellcheck' }) +map('n', 'ul', 'setlocal nonumber!', { desc = 'Toggle Line Numbers' }) +map('n', 'uo', 'setlocal nolist!', { desc = 'Toggle Whitespace Symbols' }) +map('n', 'uu', 'nohlsearch', { desc = 'Hide Search Highlight' }) + +if vim.lsp.inlay_hint then + map('n', 'uh', function() vim.lsp.inlay_hint(0, nil) end, { desc = 'Toggle Inlay Hints' }) +end + +-- Smart wrap toggle (breakindent and colorcolumn toggle as-well) +map('n', 'uw', function() + vim.opt_local.wrap = not vim.wo.wrap + vim.opt_local.breakindent = not vim.wo.breakindent + + if vim.wo.colorcolumn == '' then + vim.opt_local.colorcolumn = tostring(vim.bo.textwidth) + else + vim.opt_local.colorcolumn = '' + end +end, { desc = 'Toggle Wrap' }) + +-- Tabs: Many ways to navigate them +map('n', '', 'tabnext', { desc = 'Next Tab' }) +map('n', '', 'tabprevious', { desc = 'Previous Tab' }) +map('n', '', 'tabprevious', { desc = 'Previous Tab' }) +map('n', '', 'tabnext', { desc = 'Next Tab' }) +map('n', '', 'tabnext', { desc = 'Next Tab' }) +map('n', '', 'tabprevious', { desc = 'Previous Tab' }) + +-- Moving tabs +map('n', '', '-tabmove', { desc = 'Tab Move Backwards' }) +map('n', '', '+tabmove', { desc = 'Tab Move Forwards' }) + +-- Show treesitter nodes under cursor +-- highlights under cursor +if vim.fn.has('nvim-0.9') == 1 then + map('n', 'ui', vim.show_pos, { desc = 'Show Treesitter Node' }) +end + +-- Custom Tools +-- === + +-- Append mode-line to current buffer +map('n', 'ml', function() + require('plex.lib.edit').append_modeline() +end, { desc = 'Append Modeline' }) + +-- Jump entire buffers throughout jumplist +map('n', 'g', function() + require('plex.lib.edit').jump_buffer(1) +end, { desc = 'Jump to newer buffer' }) +map('n', 'g', function() + require('plex.lib.edit').jump_buffer(-1) +end, { desc = 'Jump to older buffer' }) + +-- Context aware menu. See lua/lib/contextmenu.lua +map('n', 'c', function() + require('plex.lib.contextmenu').show() +end, { desc = 'Content-aware menu' }) + +-- Lazygit +map('n', 'tg', function() Util.float_term({ 'lazygit' }, { cwd = Util.get_root(), esc_esc = false }) end, { desc = 'Lazygit (root dir)' }) +map('n', 'tG', function() Util.float_term({ 'lazygit' }, { esc_esc = false }) end, { desc = 'Lazygit (cwd)' }) + +-- Floating terminal +map('t', '', '', { desc = 'Enter Normal Mode' }) +map('n', 'tt', function() Util.float_term(nil, { cwd = Util.get_root() }) end, { desc = 'Terminal (root dir)' }) +map('n', 'tT', function() Util.float_term() end, { desc = 'Terminal (cwd)' }) + +if vim.fn.has('mac') then + -- Open the macOS dictionary on current word + map('n', '?', 'silent !open dict://', { desc = 'Dictionary' }) + + -- Use Marked for real-time Markdown preview + -- See: https://marked2app.com/ + if vim.fn.executable('/Applications/Marked 2.app') then + vim.api.nvim_create_autocmd('FileType', { + group = augroup('marked_preview'), + pattern = 'markdown', + callback = function() + local cmd = "silent !open -a Marked\\ 2.app '%:p'" + map('n', 'P', cmd, { desc = 'Markdown Preview' }) + end, + }) + end +end + +-- Windows, buffers and tabs +-- === + +-- Ultimatus Quitos +if vim.F.if_nil(vim.g.plex_window_q_mapping, true) then + vim.api.nvim_create_autocmd({ 'BufWinEnter', 'VimEnter' }, { + group = augroup('quit_mapping'), + callback = function(event) + if vim.bo.buftype == '' and vim.fn.maparg('q', 'n') == '' then + local args = { buffer = event.buf, desc = 'Quit' } + map('n', 'q', 'quit', args) + end + end, + }) +end + +-- Toggle quickfix window +map('n', 'q', function() + require('plex.lib.edit').toggle_list('quickfix') +end, { desc = 'Open Quickfix' }) + +-- Set locations with diagnostics and open the list. +map('n', 'a', function() + if vim.bo.filetype ~= 'qf' then + vim.diagnostic.setloclist({ open = false }) + end + require('plex.lib.edit').toggle_list('loclist') +end, { desc = 'Open Location List' }) + +-- Switch with adjacent window +map('n', '', 'x', { remap = true, desc = 'Swap windows' }) + +map('n', 'sb', 'buffer#', { desc = 'Alternate buffer' }) +map('n', 'sc', 'close', { desc = 'Close window' }) +map('n', 'sd', 'bdelete', { desc = 'Buffer delete' }) +map('n', 'sv', 'split', { desc = 'Split window horizontally' }) +map('n', 'sg', 'vsplit', { desc = 'Split window vertically' }) +map('n', 'st', 'tabnew', { desc = 'New tab' }) +map('n', 'so', 'only', { desc = 'Close other windows' }) +map('n', 'sq', 'quit', { desc = 'Quit' }) +map('n', 'sz', 'vertical resize | resize | normal! ze', { desc = 'Maximize' }) +map('n', 'sx', function() + require('mini.bufremove').delete(0, false) + vim.cmd.enew() +end, { desc = 'Delete buffer and open new' }) + +-- Background dark/light toggle +map('n', 'sh', function() + if vim.o.background == 'dark' then + vim.o.background = 'light' + else + vim.o.background = 'dark' + end +end, { desc = 'Toggle background dark/light' }) diff --git a/lua/plex/config/options.lua b/lua/plex/config/options.lua new file mode 100644 index 0000000..c532917 --- /dev/null +++ b/lua/plex/config/options.lua @@ -0,0 +1,253 @@ +-- Rafi's Neovim options +-- github.com/plex/vim-config +-- === + +-- This file is automatically loaded by config.init or plugins.core +-- stylua: ignore start + +-- Keyboard leaders +vim.g.mapleader = ' ' +vim.g.maplocalleader = ';' + +-- General +-- === + +local opt = vim.opt + +opt.mouse = 'nv' -- Disable mouse in command-line mode +opt.errorbells = true -- Trigger bell on error +opt.virtualedit = 'block' -- Position cursor anywhere in visual blockd +opt.confirm = true -- Confirm to save changes before exiting modified bufferd + +-- number +opt.number = true -- show line number +opt.relativenumber = false -- + +-- History and persistence +opt.history = 5000 +opt.shada = { "'1000", "<50", "s10", "h" } + +opt.conceallevel = 3 +opt.signcolumn = 'yes' +opt.spelloptions:append('camel') + +-- What to save for views and sessions +opt.viewoptions:remove('folds') +opt.sessionoptions:remove({ 'buffers', 'folds' }) + +-- Sync with system clipboard +opt.clipboard = 'unnamedplus' + +-- Undo +opt.undofile = true +opt.undolevels = 10000 +opt.writebackup = false + +-- If sudo, disable vim swap/backup/undo/shada writing +local USER = vim.env.USER or '' +local SUDO_USER = vim.env.SUDO_USER or '' +if + SUDO_USER ~= '' and USER ~= SUDO_USER + and vim.env.HOME ~= vim.fn.expand('~' .. USER, true) + and vim.env.HOME == vim.fn.expand('~' .. SUDO_USER, true) +then + vim.opt_global.modeline = false + vim.opt_global.undofile = false + vim.opt_global.swapfile = false + vim.opt_global.backup = false + vim.opt_global.writebackup = false + vim.opt_global.shadafile = 'NONE' +end + +-- Tabs and Indents +-- === + +opt.textwidth = 80 -- Text width maximum chars before wrapping +opt.tabstop = 2 -- The number of spaces a tab is +opt.shiftwidth = 2 -- Number of spaces to use in auto(indent) +opt.smarttab = true -- Tab insert blanks according to 'shiftwidth' +opt.autoindent = true -- Use same indenting on new lines +opt.smartindent = true -- Smart autoindenting on new lines +opt.shiftround = true -- Round indent to multiple of 'shiftwidth' + +-- Timing +-- === +opt.ttimeout = true +opt.timeoutlen = 500 -- Time out on mappings +opt.ttimeoutlen = 10 -- Time out on key codes +opt.updatetime = 500 -- Idle time to write swap and trigger CursorHold + +-- Searching +-- === +opt.ignorecase = true -- Search ignoring case +opt.smartcase = true -- Keep case when searching with * +opt.infercase = true -- Adjust case in insert completion mode +opt.incsearch = true -- Incremental search +opt.inccommand = 'nosplit' +opt.grepformat = '%f:%l:%c:%m' +opt.path:append('**') -- Find recursively + +if vim.fn.executable('rg') then + opt.grepprg = 'rg --vimgrep --no-heading' + .. (opt.smartcase and ' --smart-case' or '') .. ' --' +elseif vim.fn.executable('ag') then + opt.grepprg = 'ag --vimgrep' + .. (opt.smartcase and ' --smart-case' or '') .. ' --' +end + +-- Formatting +-- === + +opt.wrap = false -- No wrap by default +opt.linebreak = true -- Break long lines at 'breakat' +opt.breakat = '\\ \\ ;:,!?' -- Long lines break chars +opt.startofline = false -- Cursor in same column for few commands +opt.splitbelow = true -- Splits open bottom right +opt.splitright = true +opt.breakindentopt = { shift = 2, min = 20 } +opt.formatoptions = opt.formatoptions + - 'a' -- Auto formatting is BAD. + - 't' -- Don't auto format my code. I got linters for that. + + 'c' -- In general, I like it when comments respect textwidth + + 'q' -- Allow formatting comments w/ gq + - 'o' -- O and o, don't continue comments + + 'r' -- But do continue when pressing enter. + + 'n' -- Indent past the formatlistpat, not underneath it. + + 'j' -- Auto-remove comments if possible. + - '2' -- I'm not in gradeschool anymore + +-- Completion and Diff +-- === + +-- C-n completion +opt.complete:append('k') +opt.complete:remove('u') +opt.complete:remove('t') + +opt.completeopt = 'menu,menuone,noselect' + +opt.diffopt:append({ 'iwhite', 'indent-heuristic', 'algorithm:patience' }) + +opt.wildmode = 'longest:full,full' -- Command-line completion mode + +-- Editor UI +-- === + +opt.termguicolors = true +opt.shortmess:append({ W = true, I = true, c = true }) +opt.showmode = false -- Don't show mode in cmd window +opt.scrolloff = 2 -- Keep at least 2 lines above/below +opt.sidescrolloff = 5 -- Keep at least 5 lines left/right +opt.numberwidth = 2 -- Minimum number of columns to use for the line number +opt.number = false -- Don't show line numbers +opt.ruler = false -- Disable default status ruler +opt.list = true -- Show hidden characters + +opt.showtabline = 2 -- Always show the tabs line +opt.helpheight = 0 -- Disable help window resizing +opt.winwidth = 30 -- Minimum width for active window +opt.winminwidth = 1 -- Minimum width for inactive windows +opt.winheight = 1 -- Minimum height for active window +opt.winminheight = 1 -- Minimum height for inactive window + +opt.showcmd = false -- Don't show command in status line +opt.cmdheight = 0 +opt.cmdwinheight = 5 -- Command-line lines +opt.equalalways = true -- Resize windows on split or close +opt.colorcolumn = '+0' -- Column highlight at textwidth's max character-limit + +opt.cursorline = true +opt.cursorlineopt = { 'number', 'screenline' } + +opt.pumheight = 10 -- Maximum number of items to show in the popup menu +opt.pumwidth = 10 -- Minimum width for the popup menu +opt.pumblend = 10 -- Popup blend + +if vim.fn.has('nvim-0.9') == 1 then + opt.splitkeep = 'screen' + opt.shortmess:append({ C = true }) +end + +-- UI Symbols +-- === +-- icons: ▏│ ¦ ╎ ┆ ⋮ ⦙ ┊  + +opt.showbreak = '↳ ' +opt.listchars = { + tab = ' ', + extends = '⟫', + precedes = '⟪', + nbsp = '␣', + trail = '·' +} +opt.fillchars = { + foldopen = '󰅀', -- 󰅀  + foldclose = '󰅂', -- 󰅂  + fold = ' ', + foldsep = ' ', + diff = '╱', + eob = ' ', + horiz = '━', + horizup = '┻', + horizdown = '┳', + vert = '┃', + vertleft = '┫', + vertright = '┣', + verthoriz = '╋', +} + +-- Folds +-- === + +opt.foldlevel = 99 +opt.foldlevelstart = 99 +opt.foldcolumn = '0' +opt.foldenable = true + +-- Misc +-- === + +-- Disable python/perl/ruby/node providers +vim.g.loaded_python3_provider = 0 +vim.g.loaded_perl_provider = 0 +vim.g.loaded_ruby_provider = 0 +vim.g.loaded_node_provider = 0 + +vim.g.no_gitrebase_maps = 1 -- See share/nvim/runtime/ftplugin/gitrebase.vim +vim.g.no_man_maps = 1 -- See share/nvim/runtime/ftplugin/man.vim + +-- Filetype detection +-- === + +---@diagnostic disable-next-line: missing-fields +vim.filetype.add({ + filename = { + Brewfile = 'ruby', + justfile = 'just', + Justfile = 'just', + Tmuxfile = 'tmux', + ['yarn.lock'] = 'yaml', + ['.buckconfig'] = 'toml', + ['.flowconfig'] = 'ini', + ['.jsbeautifyrc'] = 'json', + ['.jscsrc'] = 'json', + ['.watchmanconfig'] = 'json', + ['dev-requirements.txt'] = 'requirements', + }, + pattern = { + ['.*%.js%.map'] = 'json', + ['.*%.postman_collection'] = 'json', + ['Jenkinsfile.*'] = 'groovy', + ['%.kube/config'] = 'yaml', + ['%.config/git/users/.*'] = 'gitconfig', + ['requirements-.*%.txt'] = 'requirements', + ['.*/templates/.*%.ya?ml'] = 'helm', + ['.*/templates/.*%.tpl'] = 'helm', + ['.*/playbooks/.*%.ya?ml'] = 'yaml.ansible', + ['.*/roles/.*/tasks/.*%.ya?ml'] = 'yaml.ansible', + ['.*/roles/.*/handlers/.*%.ya?ml'] = 'yaml.ansible', + ['.*/inventory/.*%.ini'] = 'ansible_hosts', + }, +}) + +-- vim: set ts=2 sw=0 tw=80 noet : diff --git a/lua/plex/init.lua b/lua/plex/init.lua new file mode 100644 index 0000000..7cf4116 --- /dev/null +++ b/lua/plex/init.lua @@ -0,0 +1,8 @@ +local M = {} + +---@param opts? RafiConfig +function M.setup(opts) + require('plex.config').setup(opts) +end + +return M diff --git a/lua/plex/lib/badge.lua b/lua/plex/lib/badge.lua new file mode 100644 index 0000000..38854c0 --- /dev/null +++ b/lua/plex/lib/badge.lua @@ -0,0 +1,210 @@ +-- Badge utilities +-- https://github.com/plex/vim-config + +local plugin_icons = { + DiffviewFiles = { '' }, + fugitive = { ' ' }, + fugitiveblame = { '󰊢', 'Blame' }, + lazy = { '󰒲 ', 'Lazy.nvim' }, + loclist = { '󰂖', 'Location List' }, + mason = { '󰈏 ', 'Mason' }, + NeogitStatus = { '󰉺' }, + ['neo-tree'] = { ' ', 'Neo-tree' }, + ['neo-tree-popup'] = { '󰋱', 'Neo-tree' }, + Outline = { ' ' }, + quickfix = { ' ', 'Quickfix List' }, -- 󰎟  + spectre_panel = { '󰥩 ', 'Spectre' }, + TelescopePrompt = { '󰋱', 'Telescope' }, + terminal = { ' ' }, + toggleterm = { ' ', 'Terminal' }, + Trouble = { '' }, --   + undotree = { '󰃢' }, +} + +local cache_keys = { + 'badge_cache_filepath', + 'badge_cache_filepath_tab', + 'badge_cache_icon', +} + +local augroup = vim.api.nvim_create_augroup('plex_badge', {}) + +-- Clear cached values that relate to buffer filename. +vim.api.nvim_create_autocmd( + { 'BufReadPost', 'BufFilePost', 'BufNewFile', 'BufWritePost' }, + { + group = augroup, + callback = function() + if vim.bo.buftype ~= '' then + return + end + for _, cache_key in ipairs(cache_keys) do + pcall(vim.api.nvim_buf_del_var, 0, cache_key) + end + end, + } +) +-- Clear cached values that relate to buffer content. +vim.api.nvim_create_autocmd( + { 'BufWritePre', 'FileChangedShellPost', 'TextChanged', 'InsertLeave' }, + { + group = augroup, + callback = function() + pcall(vim.api.nvim_buf_del_var, 0, 'badge_cache_trails') + end, + } +) + +local M = {} + +-- Try to guess the project's name +---@return string +function M.project() + return vim.fn.fnamemodify(require('plex.lib.utils').get_root(), ':t') or '' +end + +-- Provides relative path with limited characters in each directory name, and +-- limits number of total directories. Caches the result for current buffer. +---@param bufnr integer buffer number +---@param max_dirs integer max dirs to show +---@param dir_max_chars integer max chars in dir +---@param cache_suffix string? cache suffix +---@return string +function M.filepath(bufnr, max_dirs, dir_max_chars, cache_suffix) + local msg = '' + local cache_key = 'badge_cache_filepath' -- _'..ft + if cache_suffix then + cache_key = cache_key .. cache_suffix + end + local cache_ok, cache = pcall(vim.api.nvim_buf_get_var, bufnr, cache_key) + if cache_ok then + return cache + end + + local bufname = vim.api.nvim_buf_get_name(bufnr) + local buftype = vim.bo[bufnr].buftype + local filetype = vim.bo[bufnr].filetype + + -- Normalize bufname + if bufname:len() < 1 and buftype:len() < 1 then + return 'N/A' + end + bufname = vim.fn.fnamemodify(bufname, ':~:.') or '' + + -- Reduce directory count according to 'max_dirs' setting. + local formatter = string.format('([^%s]+)', M.path_sep) + local parts = {} + for str in string.gmatch(bufname, formatter) do + table.insert(parts, str) + end + + local short_parts = {} + for i = #parts, 1, -1 do + if #short_parts <= max_dirs then + table.insert(short_parts, 1, parts[i]) + end + end + bufname = table.concat(short_parts, M.path_sep) + + -- Reduce each directory character count according to setting. + bufname = vim.fn.pathshorten(bufname, dir_max_chars + 1) + + -- Override with plugin names. + local plugin_type = filetype == 'qf' and vim.fn.win_gettype() or filetype + if plugin_icons[plugin_type] ~= nil and #plugin_icons[plugin_type] > 1 then + msg = msg .. plugin_icons[plugin_type][2] + else + msg = msg .. bufname + end + + vim.api.nvim_buf_set_var(bufnr, cache_key, msg) + return msg +end + +function M.filemedia(separator) + local parts = {} + if vim.bo.fileformat ~= '' and vim.bo.fileformat ~= 'unix' then + table.insert(parts, vim.bo.fileformat) + end + if vim.bo.fileencoding ~= '' and vim.bo.fileencoding ~= 'utf-8' then + table.insert(parts, vim.bo.fileencoding) + end + if vim.bo.filetype ~= '' then + table.insert(parts, vim.bo.filetype) + end + return table.concat(parts, separator) +end + +function M.icon(bufnr) + bufnr = bufnr or 0 + local cache_key = 'badge_cache_icon' + local cache_ok, cache = pcall(vim.api.nvim_buf_get_var, bufnr, cache_key) + if cache_ok then + return cache + end + + local icon = '' + local ft = vim.bo[bufnr].filetype + local buftype = vim.bo[bufnr].buftype + local bufname = vim.api.nvim_buf_get_name(bufnr) + + local plugin_type = ft == 'qf' and vim.fn.win_gettype() or ft + if buftype ~= '' and plugin_icons[plugin_type] ~= nil then + icon = plugin_icons[plugin_type][1] + else + -- Try nvim-tree/nvim-web-devicons + local ok, devicons = pcall(require, 'nvim-web-devicons') + if ok then + if buftype == '' and bufname == '' then + return devicons.get_default_icon().icon + end + local f_name = vim.fn.fnamemodify(bufname, ':t') + local f_extension = vim.fn.fnamemodify(bufname, ':e') + icon, _ = devicons.get_icon(f_name, f_extension) + if icon == '' or icon == nil then + icon = devicons.get_default_icon().icon + end + end + end + vim.api.nvim_buf_set_var(bufnr, cache_key, icon) + return icon +end + +-- Detect trailing whitespace and cache result per buffer +---@param symbol string +---@return string +function M.trails(symbol) + local cache_key = 'badge_cache_trails' + local cache_ok, cache = pcall(vim.api.nvim_buf_get_var, 0, cache_key) + if cache_ok then + return cache + end + + local msg = '' + if not vim.bo.readonly and vim.bo.modifiable and vim.fn.line('$') < 9000 then + local trailing = vim.fn.search('\\s$', 'nw') + if trailing > 0 then + local label = symbol or 'WS:' + msg = msg .. label .. trailing + end + end + vim.api.nvim_buf_set_var(0, cache_key, msg) + + return msg +end + +-- Variable holds OS directory separator. +M.path_sep = (function() + if jit then + local os = string.lower(jit.os) + if os ~= 'windows' then + return '/' + else + return '\\' + end + else + return package.config:sub(1, 1) + end +end)() + +return M diff --git a/lua/plex/lib/color.lua b/lua/plex/lib/color.lua new file mode 100644 index 0000000..b9b1e89 --- /dev/null +++ b/lua/plex/lib/color.lua @@ -0,0 +1,140 @@ +-- Source: https://github.com/nvim-lualine/lualine.nvim/blob/master/lua/lualine/themes/auto.lua +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. +local M = {} + +-- Turns #rrggbb -> { red, green, blue } +function M.rgb_str2num(rgb_color_str) + if rgb_color_str:find('#') == 1 then + rgb_color_str = rgb_color_str:sub(2, #rgb_color_str) + end + local red = tonumber(rgb_color_str:sub(1, 2), 16) + local green = tonumber(rgb_color_str:sub(3, 4), 16) + local blue = tonumber(rgb_color_str:sub(5, 6), 16) + return { red = red, green = green, blue = blue } +end + +-- Turns { red, green, blue } -> #rrggbb +function M.rgb_num2str(rgb_color_num) + local rgb_color_str = string.format( + '#%02x%02x%02x', + rgb_color_num.red, + rgb_color_num.green, + rgb_color_num.blue + ) + return rgb_color_str +end + +-- Returns brightness level of color in range 0 to 1 +-- arbitrary value it's basically an weighted average +function M.get_color_brightness(rgb_color) + local color = M.rgb_str2num(rgb_color) + local brightness = (color.red * 2 + color.green * 3 + color.blue) / 6 + return brightness / 256 +end + +-- returns average of colors in range 0 to 1 +-- used to determine contrast level +function M.get_color_avg(rgb_color) + local color = M.rgb_str2num(rgb_color) + return (color.red + color.green + color.blue) / 3 / 256 +end + +-- Clamps the val between left and right +function M.clamp(val, left, right) + if val > right then + return right + end + if val < left then + return left + end + return val +end + +-- Changes brightness of rgb_color by percentage +function M.brightness_modifier(rgb_color, parcentage) + local color = M.rgb_str2num(rgb_color) + color.red = M.clamp(color.red + (color.red * parcentage / 100), 0, 255) + color.green = M.clamp(color.green + (color.green * parcentage / 100), 0, 255) + color.blue = M.clamp(color.blue + (color.blue * parcentage / 100), 0, 255) + return M.rgb_num2str(color) +end + +-- Changes contrast of rgb_color by amount +function M.contrast_modifier(rgb_color, amount) + local color = M.rgb_str2num(rgb_color) + color.red = M.clamp(color.red + amount, 0, 255) + color.green = M.clamp(color.green + amount, 0, 255) + color.blue = M.clamp(color.blue + amount, 0, 255) + return M.rgb_num2str(color) +end + +-- Changes brightness of foreground color to achieve contrast +-- without changing the color +function M.apply_contrast(highlight, threshold) + local hightlight_bg_avg = M.get_color_avg(highlight.bg) + local contrast_threshold_config = M.clamp(threshold, 0, 0.5) + local contranst_change_step = 5 + if hightlight_bg_avg > 0.5 then + contranst_change_step = -contranst_change_step + end + + -- Don't waste too much time here max 25 iteration should be more than enough + local iteration_count = 1 + while + math.abs(M.get_color_avg(highlight.fg) - hightlight_bg_avg) + < contrast_threshold_config + and iteration_count < 25 + do + highlight.fg = M.contrast_modifier(highlight.fg, contranst_change_step) + iteration_count = iteration_count + 1 + end +end + +-- Change brightness of colors +-- Darken if light theme (or) Lighten if dark theme +function M.apply_brightness(color, base_color, brightness_modifier_parameter) + if base_color ~= nil then + if M.get_color_brightness(base_color) > 0.5 then + brightness_modifier_parameter = -brightness_modifier_parameter + end + return M.brightness_modifier(color, brightness_modifier_parameter) + end +end + +-- Assorted highlight helpers +-- + +local has_nvim9 = vim.fn.has('nvim-0.9') == 1 + +-- Retrieves color value from highlight group names. +-- First present highlight is returned +---@param scope string +---@param highlights table +---@param default string? +---@return string|nil +function M.get_color(scope, highlights, default) + for _, hl_name in ipairs(highlights) do + local hl + if has_nvim9 then + hl = vim.api.nvim_get_hl(0, { name = hl_name }) + else + ---@diagnostic disable-next-line: deprecated + hl = vim.api.nvim_get_hl_by_name(hl_name, true) + hl.fg, hl.bg, hl.sp = hl.foreground, hl.background, hl.special + end + if hl.reverse then + if scope == 'bg' then + scope = 'fg' + elseif scope == 'fg' then + scope = 'bg' + end + end + if hl[scope] then + return string.format('#%06x', hl[scope]) + end + end + return default +end + +return M diff --git a/lua/plex/lib/contextmenu.lua b/lua/plex/lib/contextmenu.lua new file mode 100644 index 0000000..0fe9e47 --- /dev/null +++ b/lua/plex/lib/contextmenu.lua @@ -0,0 +1,94 @@ +-- Context-aware menu +-- https://github.com/plex/vim-config + +local M = {} + +---@param method string +---@param clients lsp.Client[] +---@return boolean +local function supports_method(method, clients) + for _, client in pairs(clients) do + if client.supports_method(method) then + return true + end + end + return false +end + +M.show = function() + if vim.fn.has('nvim-0.8') ~= 1 then + vim.notify( + 'You must be running Neovim ≥8.0', + vim.log.levels.WARN, + { title = 'Contextmenu' } + ) + return + end + + local cword = vim.fn.expand('') + local bufnr = vim.api.nvim_get_current_buf() + local clients + if vim.lsp.get_clients ~= nil then + clients = vim.lsp.get_clients({ bufnr = bufnr }) + else + ---@diagnostic disable-next-line: deprecated + clients = vim.lsp.get_active_clients({ bufnr = bufnr }) + end + + -- Remove all menu options + pcall(vim.cmd.aunmenu, 'Context') + + if cword == '' then + -- Cursor is on blank character. + vim.cmd([[ + nmenu Context.Select\ All ggVG + nmenu Context.-1- + ]]) + else + -- Add LSP methods, only if one of the servers support it. + if supports_method('textDocument/declaration', clients) then + vim.cmd( + 'nmenu Context.Declaration lua vim.lsp.buf.declaration()' + ) + end + if supports_method('textDocument/definition', clients) then + vim.cmd('nmenu Context.&Definition lua vim.lsp.buf.definition()') + end + + if supports_method('textDocument/references', clients) then + vim.cmd( + 'nmenu Context.&References… lua vim.lsp.buf.references()' + ) + end + if supports_method('textDocument/implementation', clients) then + vim.cmd( + 'nmenu Context.Implementation lua vim.lsp.buf.implementation()' + ) + end + + if #clients > 0 then + vim.cmd([[ + nmenu Context.-1- + nmenu Context.Find\ symbol… lua vim.schedule(function() require'telescope.builtin'.lsp_workspace_symbols({default_text = vim.fn.expand('')}) end) + ]]) + end + + vim.cmd([[ + nmenu Context.Grep… lua vim.schedule(function() require'telescope.builtin'.live_grep({default_text = vim.fn.expand('')}) end) + nmenu Context.-2- + ]]) + end + + vim.cmd([[ + nmenu Context.Diagnostics Trouble + nmenu Context.Bookmark m; + nmenu Context.TODOs TodoTrouble + nmenu Context.Git\ diff Gdiffsplit + nmenu Context.Unsaved\ diff DiffOrig + nmenu Context.Open\ in\ browser lua require('gitlinker').get_buf_range_url('n') + ]]) + + pcall(vim.cmd.popup, 'Context') +end + +return M diff --git a/lua/plex/lib/edit.lua b/lua/plex/lib/edit.lua new file mode 100644 index 0000000..8da9131 --- /dev/null +++ b/lua/plex/lib/edit.lua @@ -0,0 +1,141 @@ +-- Edit utilities +-- https://github.com/plex/vim-config + +local M = {} + +-- Get visually selected lines. +-- Source: https://github.com/ibhagwan/fzf-lua/blob/main/lua/fzf-lua/utils.lua +---@return string +function M.get_visual_selection() + -- this will exit visual mode + -- use 'gv' to reselect the text + local _, csrow, cscol, cerow, cecol + local mode = vim.fn.mode() + if mode == 'v' or mode == 'V' or mode == '' then + -- if we are in visual mode use the live position + _, csrow, cscol, _ = unpack(vim.fn.getpos('.')) + _, cerow, cecol, _ = unpack(vim.fn.getpos('v')) + if mode == 'V' then + -- visual line doesn't provide columns + cscol, cecol = 0, 999 + end + -- exit visual mode + vim.api.nvim_feedkeys( + vim.api.nvim_replace_termcodes('', true, false, true), + 'n', + true + ) + else + -- otherwise, use the last known visual position + _, csrow, cscol, _ = unpack(vim.fn.getpos("'<")) + _, cerow, cecol, _ = unpack(vim.fn.getpos("'>")) + end + -- swap vars if needed + if cerow < csrow then + csrow, cerow = cerow, csrow + end + if cecol < cscol then + cscol, cecol = cecol, cscol + end + local lines = vim.fn.getline(csrow, cerow) + -- local n = cerow-csrow+1 + local n = #lines + if n <= 0 or type(lines) ~= 'table' then + return '' + end + lines[n] = string.sub(lines[n], 1, cecol) + lines[1] = string.sub(lines[1], cscol) + return table.concat(lines, '\n') +end + +-- Append modeline at end of file. +function M.append_modeline() + local modeline = string.format( + 'vim: set ts=%d sw=%d tw=%d %set :', + vim.bo.tabstop, + vim.bo.shiftwidth, + vim.bo.textwidth, + vim.bo.expandtab and '' or 'no' + ) + modeline = string.gsub(vim.bo.commentstring, '%%s', modeline) + vim.api.nvim_buf_set_lines(0, -1, -1, false, { modeline }) +end + +-- Go to newer/older buffer through jumplist. +---@param direction 1 | -1 +function M.jump_buffer(direction) + local jumplist, curjump = unpack(vim.fn.getjumplist()) + if #jumplist == 0 then + return + end + local cur_buf = vim.api.nvim_get_current_buf() + local jumpcmd = direction > 0 and '' or '' + local searchrange = {} + curjump = curjump + 1 + if direction > 0 then + searchrange = vim.fn.range(curjump + 1, #jumplist) + else + searchrange = vim.fn.range(curjump - 1, 1, -1) + end + + for _, i in ipairs(searchrange) do + local nr = jumplist[i]['bufnr'] + if nr ~= cur_buf and vim.fn.bufname(nr):find('^%w+://') == nil then + local n = tostring(math.abs(i - curjump)) + vim.notify('Executing ' .. jumpcmd .. ' ' .. n .. ' times') + jumpcmd = vim.api.nvim_replace_termcodes(jumpcmd, true, true, true) + vim.cmd.normal({ n .. jumpcmd, bang = true }) + break + end + end +end + +-- Jump to next/previous whitespace error. +---@param direction 1 | -1 +function M.whitespace_jump(direction) + local opts = 'wz' + if direction < 1 then + opts = opts .. 'b' + end + + -- Whitespace pattern: Trailing whitespace or mixed tabs/spaces. + local pat = '\\s\\+$\\| \\+\\ze\\t' + vim.fn.search(pat, opts) +end + +-- Toggle list window +---@param name "quickfix" | "loclist" +M.toggle_list = function(name) + local win_bufs = M.get_tabpage_win_bufs(0) + for win, buf in pairs(win_bufs) do + if vim.bo[buf].filetype == 'qf' and vim.fn.win_gettype(win) == name then + vim.api.nvim_win_close(win, false) + return + end + end + + if name == 'loclist' then + vim.cmd([[ botright lopen ]]) + else + vim.cmd([[ botright copen ]]) + end +end + +-- Return a table with all window buffers from a tabpage. +---@private +---@param tabpage integer +---@return table +M.get_tabpage_win_bufs = function(tabpage) + local bufs = {} + for _, win in pairs(vim.api.nvim_tabpage_list_wins(tabpage)) do + if win ~= nil and vim.api.nvim_win_is_valid(win) then + local buf = vim.api.nvim_win_get_buf(win) + if buf ~= nil and vim.api.nvim_buf_is_valid(buf) then + bufs[win] = buf + end + end + end + return bufs +end + +return M diff --git a/lua/plex/lib/preview.lua b/lua/plex/lib/preview.lua new file mode 100644 index 0000000..ed8bce2 --- /dev/null +++ b/lua/plex/lib/preview.lua @@ -0,0 +1,108 @@ +-- plex preview functions +-- https://github.com/plex/vim-config +-- requires telescope + +local M = {} + +local opts = {} + +local default_opts = { + popup = { + enter = false, + -- moved = 'any', -- doesn't work. + focusable = true, + noautocmd = true, + relative = 'cursor', + line = 'cursor-3', + col = 'cursor+27', + minwidth = math.ceil(vim.o.columns / 2), + minheight = math.ceil(vim.o.lines / 1.5), + border = true, + borderchars = { '─', '│', '─', '│', '╭', '╮', '╯', '╰' }, + highlight = 'Normal', + borderhighlight = 'FloatBorder', + titlehighlight = 'Title', + zindex = 100, + }, +} + +opts = vim.deepcopy(default_opts) + +---@param popup_state table +---@param augroup integer +local function close(popup_state, augroup) + vim.schedule(function() + local utils = require('telescope.utils') + pcall(vim.api.nvim_del_augroup_by_id, augroup) + utils.win_delete('preview_border_win', popup_state.win_id, true, true) + if popup_state.border and popup_state.border.win_id then + utils.win_delete( + 'preview_border_win', + popup_state.border.win_id, + true, + true + ) + end + end) +end + +---@param user_opts table +function M.setup(user_opts) + user_opts = vim.F.if_nil(user_opts, {}) + opts = vim.tbl_deep_extend('keep', user_opts, default_opts) +end + +---@param path string +function M.open(path) + local bufnr = vim.api.nvim_get_current_buf() + local popup = require('plenary.popup') + opts.popup.title = path + local winid, popup_state = popup.create('', opts.popup) + local popup_bufnr = vim.api.nvim_win_get_buf(winid) + + -- Ensure best viewing options are toggled. + local scope = { scope = 'local', win = winid } + vim.api.nvim_set_option_value('number', true, scope) + vim.api.nvim_set_option_value('relativenumber', false, scope) + vim.api.nvim_set_option_value('wrap', false, scope) + vim.api.nvim_set_option_value('spell', false, scope) + vim.api.nvim_set_option_value('list', false, scope) + vim.api.nvim_set_option_value('foldenable', false, scope) + vim.api.nvim_set_option_value('cursorline', false, scope) + vim.api.nvim_set_option_value('signcolumn', 'no', scope) + vim.api.nvim_set_option_value('colorcolumn', '', scope) + vim.api.nvim_set_option_value('winhighlight', 'Normal:NormalFloat', scope) + + -- Run telescope preview. + local previewer = require('telescope.config').values.buffer_previewer_maker + previewer(path, popup_bufnr, {}) + + -- Setup close events + local augroup = vim.api.nvim_create_augroup('preview_window_' .. winid, {}) + + -- Close the preview window when entered a buffer that is not + -- the floating window buffer or the buffer that spawned it. + vim.api.nvim_create_autocmd('BufEnter', { + group = augroup, + callback = function() + -- close preview unless we're in original window or popup window + local bufnrs = { popup_bufnr, bufnr } + if not vim.tbl_contains(bufnrs, vim.api.nvim_get_current_buf()) then + close(popup_state, augroup) + end + end, + }) + + -- Create autocommands to close a preview window when events happen. + local events = { 'CursorMoved', 'BufUnload', 'InsertCharPre', 'ModeChanged' } + vim.api.nvim_create_autocmd(events, { + group = augroup, + buffer = bufnr, + once = true, + callback = function() + close(popup_state, augroup) + end, + }) +end + +return M diff --git a/lua/plex/lib/utils.lua b/lua/plex/lib/utils.lua new file mode 100644 index 0000000..2eb90c6 --- /dev/null +++ b/lua/plex/lib/utils.lua @@ -0,0 +1,122 @@ +-- General utilities +-- https://github.com/plex/vim-config + +local root_patterns = { '.git', '_darcs', '.hg', '.bzr', '.svn' } + +local M = {} + +local augroup_lsp_attach = vim.api.nvim_create_augroup('plex_lsp_attach', {}) + +---@param on_attach fun(client:lsp.Client, buffer:integer) +function M.on_attach(on_attach) + vim.api.nvim_create_autocmd('LspAttach', { + group = augroup_lsp_attach, + callback = function(args) + local buffer = args.buf + local client = vim.lsp.get_client_by_id(args.data.client_id) + if client ~= nil then + on_attach(client, buffer) + end + end, + }) +end + +---@param plugin string +---@return boolean +function M.has(plugin) + -- return require('lazy.core.config').plugins[plugin] ~= nil + return require('lazy.core.config').spec.plugins[plugin] ~= nil +end + +---@param fn fun() +function M.on_very_lazy(fn) + vim.api.nvim_create_autocmd('User', { + pattern = 'VeryLazy', + callback = function() + fn() + end, + }) +end + +---@param name string +---@return table +function M.opts(name) + local plugin = require('lazy.core.config').plugins[name] + if not plugin then + return {} + end + local Plugin = require('lazy.core.plugin') + return Plugin.values(plugin, 'opts', false) +end + +-- Find the root directory by searching for the version-control dir +---@return string +function M.get_root() + local cwd = vim.loop.cwd() + if cwd == '' or cwd == nil then + return '' + end + local ok, cache = pcall(vim.api.nvim_buf_get_var, 0, 'project_dir') + if ok and cache then + local _, last_cwd = + pcall(vim.api.nvim_buf_get_var, 0, 'project_dir_last_cwd') + if cwd == last_cwd then + return cache + end + end + + local root = vim.fs.find(root_patterns, { path = cwd, upward = true })[1] + root = root and vim.fs.dirname(root) or vim.loop.cwd() or '' + vim.api.nvim_buf_set_var(0, 'project_dir', root) + vim.api.nvim_buf_set_var(0, 'project_dir_last_cwd', cwd) + return root +end + +---@type table +local terminals = {} + +-- Opens a floating terminal (interactive by default) +---@param cmd? string[]|string +---@param opts? LazyCmdOptions|{interactive?:boolean, esc_esc?:false, ctrl_hjkl?:false} +function M.float_term(cmd, opts) + opts = vim.tbl_deep_extend('force', { + ft = 'lazyterm', + size = { width = 0.9, height = 0.9 }, + }, opts or {}, { persistent = true }) + ---@cast opts LazyCmdOptions|{interactive?:boolean, esc_esc?:false, ctrl_hjkl?:false} + + local termkey = vim.inspect({ + cmd = cmd or 'shell', + cwd = opts.cwd, + env = opts.env, + count = vim.v.count1, + }) + + if terminals[termkey] and terminals[termkey]:buf_valid() then + terminals[termkey]:toggle() + else + terminals[termkey] = require('lazy.util').float_term(cmd, opts) + local buf = terminals[termkey].buf + vim.b[buf].lazyterm_cmd = cmd + if opts.esc_esc == false then + vim.keymap.set('t', '', '', { buffer = buf, nowait = true }) + end + if opts.ctrl_hjkl == false then + vim.keymap.set('t', '', '', { buffer = buf, nowait = true }) + vim.keymap.set('t', '', '', { buffer = buf, nowait = true }) + vim.keymap.set('t', '', '', { buffer = buf, nowait = true }) + vim.keymap.set('t', '', '', { buffer = buf, nowait = true }) + end + + vim.api.nvim_create_autocmd('BufEnter', { + buffer = buf, + callback = function() + vim.cmd.startinsert() + end, + }) + end + + return terminals[termkey] +end + +return M diff --git a/lua/plex/plugins/coding.lua b/lua/plex/plugins/coding.lua new file mode 100644 index 0000000..241679f --- /dev/null +++ b/lua/plex/plugins/coding.lua @@ -0,0 +1,325 @@ +-- Plugins: Coding +-- https://github.com/plex/vim-config + +return { + + ----------------------------------------------------------------------------- + { + 'hrsh7th/nvim-cmp', + event = 'InsertEnter', + dependencies = { + 'hrsh7th/cmp-nvim-lsp', + 'hrsh7th/cmp-buffer', + 'hrsh7th/cmp-path', + 'hrsh7th/cmp-emoji', + { 'saadparwaiz1/cmp_luasnip', dependencies = 'L3MON4D3/LuaSnip' }, + 'andersevenrud/cmp-tmux', + }, + opts = function() + local cmp = require('cmp') + local defaults = require('cmp.config.default')() + local luasnip = require('luasnip') + + local function has_words_before() + if vim.bo.buftype == 'prompt' then + return false + end + local line, col = unpack(vim.api.nvim_win_get_cursor(0)) + -- stylua: ignore + return col ~= 0 and vim.api.nvim_buf_get_lines(0, line - 1, line, true)[1]:sub(col, col):match('%s') == nil + end + + return { + preselect = cmp.PreselectMode.None, + sorting = defaults.sorting, + experimental = { + ghost_text = { + hl_group = 'Comment', + }, + }, + snippet = { + expand = function(args) + require('luasnip').lsp_expand(args.body) + end, + }, + sources = cmp.config.sources({ + { name = 'nvim_lsp', priority = 50 }, + { name = 'path', priority = 40 }, + { name = 'luasnip', priority = 30 }, + }, { + { name = 'buffer', priority = 50, keyword_length = 3 }, + { name = 'emoji', insert = true, priority = 20 }, + { + name = 'tmux', + priority = 10, + keyword_length = 3, + option = { all_panes = true, label = 'tmux' }, + }, + }), + mapping = cmp.mapping.preset.insert({ + -- accepts currently selected item. + -- Set `select` to `false` to only confirm explicitly selected items. + [''] = cmp.mapping({ + i = function(fallback) + if cmp.visible() and cmp.get_active_entry() then + cmp.confirm({ select = false }) + else + fallback() + end + end, + s = cmp.mapping.confirm({ + select = true, + behavior = cmp.ConfirmBehavior.Replace, + }), + -- Do not set command mode, it will interfere with noice popmenu. + }), + [''] = cmp.mapping.confirm({ + behavior = cmp.ConfirmBehavior.Replace, + select = true, + }), + [''] = cmp.mapping.complete(), + [''] = cmp.mapping.select_next_item(), + [''] = cmp.mapping.select_prev_item(), + [''] = cmp.mapping.select_next_item({ count = 5 }), + [''] = cmp.mapping.select_prev_item({ count = 5 }), + [''] = cmp.mapping.scroll_docs(4), + [''] = cmp.mapping.scroll_docs(-4), + [''] = function(fallback) + cmp.close() + fallback() + end, + [''] = cmp.mapping(function(fallback) + if cmp.visible() then + cmp.select_next_item() + elseif luasnip.jumpable(1) then + luasnip.jump(1) + elseif has_words_before() then + cmp.complete() + else + fallback() + end + end, { 'i', 's' }), + [''] = cmp.mapping(function(fallback) + if cmp.visible() then + cmp.select_prev_item() + elseif luasnip.jumpable(-1) then + luasnip.jump(-1) + else + fallback() + end + end, { 'i', 's' }), + }), + formatting = { + format = function(entry, vim_item) + -- Prepend with a fancy icon from config lua/plex/config/init.lua + local icons = require('plex.config').icons + if entry.source.name == 'git' then + vim_item.kind = icons.git + else + local symbol = icons.kinds[vim_item.kind] + if symbol ~= nil then + vim_item.kind = symbol .. ' ' .. vim_item.kind + end + end + return vim_item + end, + }, + } + end, + }, + + ----------------------------------------------------------------------------- + { + 'L3MON4D3/LuaSnip', + event = 'InsertEnter', + dependencies = { 'rafamadriz/friendly-snippets' }, + build = (not jit.os:find('Windows')) + and "echo 'NOTE: jsregexp is optional, so not a big deal if it fails to build'; make install_jsregexp" + or nil, + -- stylua: ignore + keys = { + { + '', + function() require('luasnip').expand_or_jump() end, + mode = { 'i', 's' }, + }, + }, + opts = { + -- Don't store snippet history for less overhead + history = false, + -- Event on which to check for exiting a snippet's region + region_check_events = 'InsertEnter', + delete_check_events = 'InsertLeave', + ft_func = function() + return vim.split(vim.bo.filetype, '.', { plain = true }) + end, + }, + config = function(_, opts) + require('luasnip').setup(opts) + require('luasnip.loaders.from_vscode').lazy_load() + require('luasnip.loaders.from_lua').load({ paths = './snippets' }) + vim.api.nvim_create_user_command('LuaSnipEdit', function() + require('luasnip.loaders.from_lua').edit_snippet_files() + end, {}) + end, + }, + + ----------------------------------------------------------------------------- + { + 'ziontee113/SnippetGenie', + event = 'InsertEnter', + dependencies = 'L3MON4D3/LuaSnip', + opts = { + snippets_directory = vim.fn.stdpath('config') .. '/snippets', + }, + }, + + ----------------------------------------------------------------------------- + { + 'danymat/neogen', + -- stylua: ignore + keys = { + { + 'cc', + function() require('neogen').generate({}) end, + desc = 'Neogen Comment', + }, + }, + opts = { snippet_engine = 'luasnip' }, + }, + + ----------------------------------------------------------------------------- + { + 'echasnovski/mini.pairs', + event = 'VeryLazy', + opts = {}, + }, + + ----------------------------------------------------------------------------- + { + 'echasnovski/mini.surround', + -- stylua: ignore + keys = function(_, keys) + -- Populate the keys based on the user's options + local plugin = require('lazy.core.config').spec.plugins['mini.surround'] + local opts = require('lazy.core.plugin').values(plugin, 'opts', false) + local mappings = { + { opts.mappings.add, desc = 'Add surrounding', mode = { 'n', 'x' } }, + { opts.mappings.delete, desc = 'Delete surrounding' }, + { opts.mappings.find, desc = 'Find right surrounding' }, + { opts.mappings.find_left, desc = 'Find left surrounding' }, + { opts.mappings.highlight, desc = 'Highlight surrounding' }, + { opts.mappings.replace, desc = 'Replace surrounding' }, + { opts.mappings.update_n_lines, desc = 'Update `MiniSurround.config.n_lines`' }, + } + mappings = vim.tbl_filter(function(m) + return m[1] and #m[1] > 0 + end, mappings) + return vim.list_extend(mappings, keys) + end, + opts = { + mappings = { + add = 'sa', -- Add surrounding in Normal and Visual modes + delete = 'ds', -- Delete surrounding + find = 'gzf', -- Find surrounding (to the right) + find_left = 'gzF', -- Find surrounding (to the left) + highlight = 'gzh', -- Highlight surrounding + replace = 'cs', -- Replace surrounding + update_n_lines = 'gzn', -- Update `n_lines` + }, + }, + }, + + ----------------------------------------------------------------------------- + { + 'echasnovski/mini.comment', + event = 'VeryLazy', + dependencies = { 'JoosepAlviste/nvim-ts-context-commentstring' }, + keys = { + { 'v', 'gcc', remap = true, silent = true, mode = 'n' }, + { 'v', 'gc', remap = true, silent = true, mode = 'x' }, + }, + opts = { + options = { + custom_commentstring = function() + return require('ts_context_commentstring.internal').calculate_commentstring() + or vim.bo.commentstring + end, + }, + }, + }, + + ----------------------------------------------------------------------------- + { + 'echasnovski/mini.trailspace', + event = { 'BufReadPost', 'BufNewFile' }, + opts = {}, + }, + + ----------------------------------------------------------------------------- + { + 'echasnovski/mini.ai', + event = 'VeryLazy', + dependencies = { 'nvim-treesitter/nvim-treesitter-textobjects' }, + opts = function() + local ai = require('mini.ai') + return { + n_lines = 500, + -- stylua: ignore + custom_textobjects = { + o = ai.gen_spec.treesitter({ + a = { '@block.outer', '@conditional.outer', '@loop.outer' }, + i = { '@block.inner', '@conditional.inner', '@loop.inner' }, + }, {}), + }, + } + end, + }, + + ----------------------------------------------------------------------------- + { + 'echasnovski/mini.splitjoin', + keys = { + { + 'sj', + 'lua MiniSplitjoin.join()', + mode = { 'n', 'x' }, + desc = 'Join arguments', + }, + { + 'sk', + 'lua MiniSplitjoin.split()', + mode = { 'n', 'x' }, + desc = 'Split arguments', + }, + }, + opts = { + mappings = { toggle = '' }, + }, + }, + + ----------------------------------------------------------------------------- + { + 'AndrewRadev/linediff.vim', + cmd = { 'Linediff', 'LinediffAdd' }, + keys = { + { 'mdf', ':Linediff', mode = 'x', desc = 'Line diff' }, + { 'mda', ':LinediffAdd', mode = 'x', desc = 'Line diff add' }, + { 'mds', 'LinediffShow', desc = 'Line diff show' }, + { 'mdr', 'LinediffReset', desc = 'Line diff reset' }, + }, + }, + + ----------------------------------------------------------------------------- + { + 'AndrewRadev/dsf.vim', + -- stylua: ignore + keys = { + { 'dsf', 'DsfDelete', noremap = true, desc = 'Delete Surrounding Function' }, + { 'csf', 'DsfChange', noremap = true, desc = 'Change Surrounding Function' }, + }, + init = function() + vim.g.dsf_no_mappings = 1 + end, + }, +} diff --git a/lua/plex/plugins/colorscheme.lua b/lua/plex/plugins/colorscheme.lua new file mode 100644 index 0000000..d681225 --- /dev/null +++ b/lua/plex/plugins/colorscheme.lua @@ -0,0 +1,73 @@ +-- Plugins: Colorschemes +-- https://github.com/plex/vim-config + +return { + + { + 'plex/theme-loader.nvim', + lazy = false, + priority = 99, + opts = { initial_colorscheme = 'neohybrid' }, + }, + + { 'plex/neo-hybrid.vim', priority = 100, lazy = false }, + { 'plex/awesome-vim-colorschemes', lazy = false }, + { 'AlexvZyl/nordic.nvim' }, + { 'folke/tokyonight.nvim', opts = { style = 'night' } }, + { 'rebelot/kanagawa.nvim' }, + { 'olimorris/onedarkpro.nvim' }, + { 'EdenEast/nightfox.nvim' }, + { 'nyoom-engineering/oxocarbon.nvim' }, + + { + 'catppuccin/nvim', + lazy = true, + name = 'catppuccin', + opts = { + flavour = 'mocha', -- latte, frappe, macchiato, mocha + dim_inactive = { enabled = false }, + integrations = { + alpha = true, + cmp = true, + flash = true, + gitsigns = true, + illuminate = true, + indent_blankline = { enabled = true }, + lsp_trouble = true, + markdown = true, + mason = true, + mini = true, + native_lsp = { + enabled = true, + virtual_text = { + errors = { 'italic' }, + hints = { 'italic' }, + warnings = { 'italic' }, + information = { 'italic' }, + }, + underlines = { + errors = { 'undercurl' }, + hints = { 'undercurl' }, + warnings = { 'undercurl' }, + information = { 'undercurl' }, + }, + inlay_hints = { + background = true, + }, + }, + navic = { enabled = true }, + neogit = true, + neotest = true, + neotree = true, + noice = true, + notify = true, + semantic_tokens = true, + symbols_outline = true, + treesitter_context = true, + telescope = { enabled = true }, + treesitter = true, + which_key = true, + }, + }, + }, +} diff --git a/lua/plex/plugins/core.lua b/lua/plex/plugins/core.lua new file mode 100644 index 0000000..1bb456d --- /dev/null +++ b/lua/plex/plugins/core.lua @@ -0,0 +1,6 @@ +require('plex.config').init() + +return { + { 'folke/lazy.nvim', version = '*' }, + { 'nvim-lua/plenary.nvim', lazy = false }, +} diff --git a/lua/plex/plugins/editor.lua b/lua/plex/plugins/editor.lua new file mode 100644 index 0000000..d7dcbcd --- /dev/null +++ b/lua/plex/plugins/editor.lua @@ -0,0 +1,514 @@ +-- Plugins: Editor +-- https://github.com/plex/vim-config + +local is_windows = vim.loop.os_uname().sysname == 'Windows_NT' + +return { + + ----------------------------------------------------------------------------- + { 'nmac427/guess-indent.nvim', lazy = false, priority = 50, config = true }, + { 'tweekmonster/helpful.vim', cmd = 'HelpfulVersion' }, + { 'lambdalisue/suda.vim', event = 'BufRead' }, + + ----------------------------------------------------------------------------- + { + 'christoomey/vim-tmux-navigator', + lazy = false, + cond = not is_windows, + -- stylua: ignore + keys = { + { '', 'TmuxNavigateLeft', mode = { 'n', 't' }, silent = true, desc = 'Jump to left pane' }, + { '', 'TmuxNavigateDown', mode = { 'n', 't' }, silent = true, desc = 'Jump to lower pane' }, + { '', 'TmuxNavigateUp', mode = { 'n', 't' }, silent = true, desc = 'Jump to upper pane' }, + { '', 'TmuxNavigateRight', mode = { 'n', 't' }, silent = true, desc = 'Jump to right pane' }, + }, + init = function() + vim.g.tmux_navigator_no_mappings = true + end, + }, + + ----------------------------------------------------------------------------- + { + 'olimorris/persisted.nvim', + event = 'VimEnter', + priority = 1000, + opts = { + autoload = true, + follow_cwd = false, + ignored_dirs = { '/usr', '/opt', '~/.cache', vim.env.TMPDIR or '/tmp' }, + }, + config = function(_, opts) + if vim.g.in_pager_mode or vim.env.GIT_EXEC_PATH ~= nil then + -- Do not autoload if stdin has been provided, or git commit session. + opts.autoload = false + opts.autosave = false + end + require('persisted').setup(opts) + end, + init = function() + -- Detect if stdin has been provided. + vim.g.in_pager_mode = false + vim.api.nvim_create_autocmd('StdinReadPre', { + group = vim.api.nvim_create_augroup('plex_persisted', {}), + callback = function() + vim.g.in_pager_mode = true + end, + }) + -- Close all floats before loading a session. (e.g. Lazy.nvim) + vim.api.nvim_create_autocmd('User', { + group = 'plex_persisted', + pattern = 'PersistedLoadPre', + callback = function() + for _, win in pairs(vim.api.nvim_tabpage_list_wins(0)) do + if vim.api.nvim_win_get_config(win).zindex then + vim.api.nvim_win_close(win, false) + end + end + end, + }) + -- Close all plugin owned buffers before saving a session. + vim.api.nvim_create_autocmd('User', { + pattern = 'PersistedSavePre', + group = 'plex_persisted', + callback = function() + -- Detect if window is owned by plugin by checking buftype. + local current_buffer = vim.api.nvim_get_current_buf() + for _, win in ipairs(vim.fn.getwininfo()) do + local buftype = vim.bo[win.bufnr].buftype + if buftype ~= '' and buftype ~= 'help' then + -- Delete plugin owned window buffers. + if win.bufnr == current_buffer then + -- Jump to previous window if current window is not a real file + vim.cmd.wincmd('p') + end + vim.api.nvim_buf_delete(win.bufnr, {}) + end + end + end, + }) + -- Before switching to a different session using Telescope, save and stop + -- current session to avoid previous session to be overwritten. + vim.api.nvim_create_autocmd('User', { + pattern = 'PersistedTelescopeLoadPre', + group = 'plex_persisted', + callback = function() + require('persisted').save() + require('persisted').stop() + end, + }) + -- After switching to a different session using Telescope, start it so it + -- will be auto-saved. + vim.api.nvim_create_autocmd('User', { + pattern = 'PersistedTelescopeLoadPost', + group = 'plex_persisted', + callback = function(session) + require('persisted').start() + print('Started session ' .. session.data.name) + end, + }) + end, + }, + + ----------------------------------------------------------------------------- + { + 'RRethy/vim-illuminate', + event = { 'BufReadPost', 'BufNewFile' }, + opts = { + delay = 200, + under_cursor = false, + modes_allowlist = { 'n', 'no', 'nt' }, + filetypes_denylist = { + 'DiffviewFileHistory', + 'DiffviewFiles', + 'SidebarNvim', + 'fugitive', + 'git', + 'minifiles', + 'neo-tree', + }, + }, + keys = { + { ']]', desc = 'Next Reference' }, + { '[[', desc = 'Prev Reference' }, + }, + config = function(_, opts) + require('illuminate').configure(opts) + + local function map(key, dir, buffer) + vim.keymap.set('n', key, function() + require('illuminate')['goto_' .. dir .. '_reference'](false) + end, { + desc = dir:sub(1, 1):upper() .. dir:sub(2) .. ' Reference', + buffer = buffer, + }) + end + + map(']]', 'next') + map('[[', 'prev') + + -- also set it after loading ftplugins, since a lot overwrite [[ and ]] + vim.api.nvim_create_autocmd('FileType', { + group = vim.api.nvim_create_augroup('plex_illuminate', {}), + callback = function() + local buffer = vim.api.nvim_get_current_buf() + map(']]', 'next', buffer) + map('[[', 'prev', buffer) + end, + }) + end, + }, + + ----------------------------------------------------------------------------- + { + 'mbbill/undotree', + cmd = 'UndotreeToggle', + keys = { + { 'gu', 'UndotreeToggle', desc = 'Undo Tree' }, + }, + }, + + ----------------------------------------------------------------------------- + { + 'ggandor/flit.nvim', + keys = function() + ---@type LazyKeys[] + local ret = {} + for _, key in ipairs({ 'f', 'F', 't', 'T' }) do + ret[#ret + 1] = { key, mode = { 'n', 'x', 'o' }, desc = key } + end + return ret + end, + opts = { labeled_modes = 'nx' }, + }, + + ----------------------------------------------------------------------------- + { + 'ggandor/leap.nvim', + -- stylua: ignore + keys = { + { 'ss', '(leap-forward-to)', mode = { 'n', 'x', 'o' }, desc = 'Leap forward to' }, + { 'sS', '(leap-backward-to)', mode = { 'n', 'x', 'o' }, desc = 'Leap backward to' }, + { 'SS', '(leap-from-window)', mode = { 'n', 'x', 'o' }, desc = 'Leap from windows' }, + }, + config = true, + }, + + ----------------------------------------------------------------------------- + { + 'kana/vim-niceblock', + -- stylua: ignore + keys = { + { 'I', '(niceblock-I)', silent = true, mode = 'x', desc = 'Blockwise Insert' }, + { 'gI', '(niceblock-gI)', silent = true, mode = 'x', desc = 'Blockwise Insert' }, + { 'A', '(niceblock-A)', silent = true, mode = 'x', desc = 'Blockwise Append' }, + }, + init = function() + vim.g.niceblock_no_default_key_mappings = 0 + end, + }, + + ----------------------------------------------------------------------------- + { + 'haya14busa/vim-edgemotion', + -- stylua: ignore + keys = { + { 'gj', '(edgemotion-j)', mode = { 'n', 'x' }, desc = 'Move to bottom edge' }, + { 'gk', '(edgemotion-k)', mode = { 'n', 'x' }, desc = 'Move to top edge' }, + }, + }, + + ----------------------------------------------------------------------------- + { + 'folke/zen-mode.nvim', + cmd = 'ZenMode', + keys = { + { 'zz', 'ZenMode', noremap = true, desc = 'Zen Mode' }, + }, + opts = { + plugins = { + gitsigns = { enabled = true }, + tmux = { enabled = vim.env.TMUX ~= nil }, + }, + }, + }, + + ----------------------------------------------------------------------------- + { + 'folke/which-key.nvim', + event = 'VeryLazy', + opts = { + icons = { separator = ' 󰁔 ' }, + window = { winblend = 0 }, + defaults = { + mode = { 'n', 'v' }, + [';'] = { name = '+telescope' }, + [';d'] = { name = '+lsp/todo' }, + ['g'] = { name = '+goto' }, + ['gz'] = { name = '+surround' }, + [']'] = { name = '+next' }, + ['['] = { name = '+prev' }, + ['b'] = { name = '+buffer' }, + ['c'] = { name = '+code' }, + ['g'] = { name = '+git' }, + ['h'] = { name = '+hunks' }, + ['s'] = { name = '+search' }, + ['t'] = { name = '+toggle/tools' }, + ['u'] = { name = '+ui' }, + ['x'] = { name = '+diagnostics/quickfix' }, + ['z'] = { name = '+notes' }, + }, + }, + config = function(_, opts) + local wk = require('which-key') + wk.setup(opts) + wk.register(opts.defaults) + end, + }, + + ----------------------------------------------------------------------------- + { + 'folke/todo-comments.nvim', + dependencies = 'nvim-telescope/telescope.nvim', + -- stylua: ignore + keys = { + { ']t', function() require('todo-comments').jump_next() end, desc = 'Next todo comment' }, + { '[t', function() require('todo-comments').jump_prev() end, desc = 'Previous todo comment' }, + { 'dt', 'TodoTelescope', desc = 'todo' }, + { 'xt', 'TodoTrouble', desc = 'Todo (Trouble)' }, + { 'xT', 'TodoTrouble keywords=TODO,FIX,FIXME', desc = 'Todo/Fix/Fixme (Trouble)' }, + { 'st', 'TodoTelescope', desc = 'Todo' }, + { 'sT', 'TodoTelescope keywords=TODO,FIX,FIXME', desc = 'Todo/Fix/Fixme' }, + }, + opts = { signs = false }, + }, + + ----------------------------------------------------------------------------- + { + 'folke/trouble.nvim', + cmd = { 'Trouble', 'TroubleToggle' }, + opts = { use_diagnostic_signs = true }, + -- stylua: ignore + keys = { + { 'e', 'TroubleToggle document_diagnostics', noremap = true, desc = 'Document Diagnostics' }, + { 'r', 'TroubleToggle workspace_diagnostics', noremap = true, desc = 'Workspace Diagnostics' }, + { 'xx', 'TroubleToggle document_diagnostics', desc = 'Document Diagnostics (Trouble)' }, + { 'xX', 'TroubleToggle workspace_diagnostics', desc = 'Workspace Diagnostics (Trouble)' }, + { 'xQ', 'TroubleToggle quickfix', desc = 'Quickfix List (Trouble)' }, + { 'xL', 'TroubleToggle loclist', desc = 'Location List (Trouble)' }, + { + '[q', + function() + if require('trouble').is_open() then + require('trouble').previous({ skip_groups = true, jump = true }) + else + vim.cmd.cprev() + end + end, + desc = 'Previous trouble/quickfix item', + }, + { + ']q', + function() + if require('trouble').is_open() then + require('trouble').next({ skip_groups = true, jump = true }) + else + vim.cmd.cnext() + end + end, + desc = 'Next trouble/quickfix item', + }, + }, + }, + + ----------------------------------------------------------------------------- + { + 'akinsho/toggleterm.nvim', + cmd = 'ToggleTerm', + keys = { + { + '', + mode = { 'n', 't' }, + silent = true, + function() + local venv = vim.b['virtual_env'] + local term = require('toggleterm.terminal').Terminal:new({ + env = venv and { VIRTUAL_ENV = venv } or nil, + count = vim.v.count > 0 and vim.v.count or 1, + }) + term:toggle() + end, + desc = 'Toggle terminal', + }, + }, + opts = { + open_mapping = false, + float_opts = { + border = 'curved', + }, + }, + }, + + ----------------------------------------------------------------------------- + { + 'simrat39/symbols-outline.nvim', + cmd = { 'SymbolsOutline', 'SymbolsOutlineOpen' }, + keys = { + { 'o', 'SymbolsOutline', desc = 'Symbols Outline' }, + }, + opts = { + width = 30, + autofold_depth = 0, + keymaps = { + hover_symbol = 'K', + toggle_preview = 'p', + }, + }, + init = function() + vim.api.nvim_create_autocmd('FileType', { + group = vim.api.nvim_create_augroup('plex_outline', {}), + pattern = 'Outline', + callback = function() + vim.opt_local.winhighlight = 'CursorLine:WildMenu' + vim.opt_local.signcolumn = 'auto' + end, + }) + end, + }, + + ----------------------------------------------------------------------------- + { + 's1n7ax/nvim-window-picker', + event = 'VeryLazy', + keys = function(_, keys) + local pick_window = function() + local picked_window_id = require('window-picker').pick_window() + if picked_window_id ~= nil then + vim.api.nvim_set_current_win(picked_window_id) + end + end + + local swap_window = function() + local picked_window_id = require('window-picker').pick_window() + if picked_window_id ~= nil then + local current_winnr = vim.api.nvim_get_current_win() + local current_bufnr = vim.api.nvim_get_current_buf() + local other_bufnr = vim.api.nvim_win_get_buf(picked_window_id) + vim.api.nvim_win_set_buf(current_winnr, other_bufnr) + vim.api.nvim_win_set_buf(picked_window_id, current_bufnr) + end + end + + local mappings = { + { '-', pick_window, desc = 'Pick window' }, + { 'sp', pick_window, desc = 'Pick window' }, + { 'sw', swap_window, desc = 'Swap picked window' }, + } + return vim.list_extend(mappings, keys) + end, + opts = { + hint = 'floating-big-letter', + show_prompt = false, + filter_rules = { + include_current_win = true, + bo = { + filetype = { 'notify', 'noice' }, + buftype = {}, + }, + }, + }, + }, + + ----------------------------------------------------------------------------- + { + 'rest-nvim/rest.nvim', + ft = 'http', + keys = { + { 'mh', 'RestNvim', desc = 'Execute HTTP request' }, + }, + opts = { skip_ssl_verification = true }, + }, + + ----------------------------------------------------------------------------- + { + 'mickael-menu/zk-nvim', + name = 'zk', + ft = 'markdown', + cmd = { 'ZkNew', 'ZkNotes', 'ZkTags', 'ZkMatch' }, + -- stylua: ignore + keys = { + { 'zn', "ZkNew { title = vim.fn.input('Title: ') }", desc = 'Zk New' }, + { 'zo', "ZkNotes { sort = { 'modified' } }", desc = 'Zk Notes' }, + { 'zt', 'ZkTags', desc = 'Zk Tags' }, + { 'zf', "ZkNotes { sort = { 'modified' }, match = vim.fn.input('Search: ') }", desc = 'Zk Search' }, + { 'zf', ":'<,'>ZkMatch", mode = 'x', desc = 'Zk Match' }, + { 'zb', 'ZkBacklinks', desc = 'Zk Backlinks' }, + { 'zl', 'ZkLinks', desc = 'Zk Links' }, + }, + opts = { picker = 'telescope' }, + }, + + ----------------------------------------------------------------------------- + { + 'nvim-pack/nvim-spectre', + -- stylua: ignore + keys = { + { 'sp', function() require('spectre').toggle() end, desc = 'Spectre', }, + { 'sp', function() require('spectre').open_visual({ select_word = true }) end, mode = 'x', desc = 'Spectre Word' }, + }, + opts = { + mapping = { + ['toggle_gitignore'] = { + map = 'tg', + cmd = "lua require('spectre').change_options('gitignore')", + desc = 'toggle gitignore', + }, + }, + find_engine = { + ['rg'] = { + cmd = 'rg', + args = { + '--color=never', + '--no-heading', + '--with-filename', + '--line-number', + '--column', + '--ignore', + }, + options = { + ['gitignore'] = { + value = '--no-ignore', + icon = '[G]', + desc = 'gitignore', + }, + }, + }, + }, + default = { + find = { + cmd = 'rg', + options = { 'ignore-case', 'hidden', 'gitignore' }, + }, + }, + }, + }, + + ----------------------------------------------------------------------------- + { + 'echasnovski/mini.bufremove', + opts = {}, + -- stylua: ignore + keys = { + { 'bd', function() require('mini.bufremove').delete(0, false) end, desc = 'Delete Buffer', }, + }, + }, + + ----------------------------------------------------------------------------- + { + 'mzlogin/vim-markdown-toc', + cmd = { 'GenTocGFM', 'GenTocRedcarpet', 'GenTocGitLab', 'UpdateToc' }, + ft = 'markdown', + init = function() + vim.g.vmt_auto_update_on_save = 0 + end, + }, +} diff --git a/lua/plex/plugins/extras/coding/autopairs.lua b/lua/plex/plugins/extras/coding/autopairs.lua new file mode 100644 index 0000000..059a446 --- /dev/null +++ b/lua/plex/plugins/extras/coding/autopairs.lua @@ -0,0 +1,18 @@ +return { + { + 'windwp/nvim-autopairs', + dependencies = 'hrsh7th/nvim-cmp', + config = function() + local autopairs = require('nvim-autopairs') + autopairs.setup() + + -- Disable [ rule, it interferes with zk-nvim link completion. + local cond = require('nvim-autopairs.conds') + autopairs.get_rule('['):with_pair(cond.not_filetypes({ 'markdown' })) + + -- Insert `(` after function or method item selection. + local cmp_autopairs = require('nvim-autopairs.completion.cmp') + require('cmp').event:on('confirm_done', cmp_autopairs.on_confirm_done()) + end, + }, +} diff --git a/lua/plex/plugins/extras/coding/cmp-git.lua b/lua/plex/plugins/extras/coding/cmp-git.lua new file mode 100644 index 0000000..07ead15 --- /dev/null +++ b/lua/plex/plugins/extras/coding/cmp-git.lua @@ -0,0 +1,26 @@ +return { + + { + 'hrsh7th/nvim-cmp', + optional = true, + dependencies = { 'petertriho/cmp-git' }, + }, + + { + 'petertriho/cmp-git', + opts = {}, + config = function() + local cmp = require('cmp') + ---@diagnostic disable-next-line: missing-fields + cmp.setup.filetype('gitcommit', { + sources = cmp.config.sources({ + { name = 'git', priority = 50 }, + { name = 'path', priority = 40 }, + }, { + { name = 'buffer', priority = 50 }, + { name = 'emoji', insert = true, priority = 20 }, + }), + }) + end, + }, +} diff --git a/lua/plex/plugins/extras/coding/copilot.lua b/lua/plex/plugins/extras/coding/copilot.lua new file mode 100644 index 0000000..e9bff62 --- /dev/null +++ b/lua/plex/plugins/extras/coding/copilot.lua @@ -0,0 +1,107 @@ +-- This is part of LazyVim's code, with my modifications. +-- See: https://github.com/LazyVim/LazyVim/blob/main/lua/lazyvim/plugins/extras/coding/copilot.lua + +return { + + ----------------------------------------------------------------------------- + { + 'zbirenbaum/copilot.lua', + cmd = 'Copilot', + build = ':Copilot auth', + opts = { + suggestion = { enabled = false }, + panel = { enabled = false }, + filetypes = { + markdown = true, + help = true, + }, + }, + }, + + ----------------------------------------------------------------------------- + { + 'nvim-lualine/lualine.nvim', + optional = true, + event = 'VeryLazy', + opts = function(_, opts) + local get_color = require('plex.lib.color').get_color + local fg = function(...) + return { fg = get_color('fg', ...) } + end + + local colors = { + [''] = fg({ 'Comment' }), + ['Normal'] = fg({ 'Comment' }), + ['Warning'] = fg({ 'DiagnosticError' }), + ['InProgress'] = fg({ 'DiagnosticWarn' }), + } + -- Add copilot icon to lualine statusline + table.insert(opts.sections.lualine_x, { + function() + local icon = require('plex.config').icons.kinds.Copilot + local status = require('copilot.api').status.data + return icon .. (status.message or '') + end, + cond = function() + local clients + if vim.lsp.get_clients ~= nil then + clients = vim.lsp.get_clients({ name = 'copilot', bufnr = 0 }) + else + ---@diagnostic disable-next-line: deprecated + clients = vim.lsp.get_active_clients({ + name = 'copilot', + bufnr = 0, + }) + end + return #clients > 0 + end, + color = function() + if not package.loaded["copilot"] then + return + end + local status = require('copilot.api').status.data + return colors[status.status] or colors[''] + end, + }) + end, + }, + + ----------------------------------------------------------------------------- + { + 'nvim-cmp', + dependencies = { + { + 'zbirenbaum/copilot-cmp', + dependencies = 'zbirenbaum/copilot.lua', + opts = {}, + config = function(_, opts) + local copilot_cmp = require('copilot_cmp') + copilot_cmp.setup(opts) + -- attach cmp source whenever copilot attaches + -- fixes lazy-loading issues with the copilot cmp source + ---@param client lsp.Client + require('plex.lib.utils').on_attach(function(client) + if client.name == 'copilot' then + copilot_cmp._on_insert_enter({}) + end + end) + end, + }, + }, + ---@param opts cmp.ConfigSchema|{sources: table[]} + opts = function(_, opts) + -- Add copilot nvim-cmp source. + table.insert(opts.sources, 1, { + name = 'copilot', + group_index = 2, + priority = 60, + }) + opts.sorting = opts.sorting or require('cmp.config.default')().sorting + table.insert( + opts.sorting.comparators, + 1, + require('copilot_cmp.comparators').prioritize + ) + end, + }, +} diff --git a/lua/plex/plugins/extras/coding/editorconfig.lua b/lua/plex/plugins/extras/coding/editorconfig.lua new file mode 100644 index 0000000..8f9d30a --- /dev/null +++ b/lua/plex/plugins/extras/coding/editorconfig.lua @@ -0,0 +1,20 @@ +return { + { + 'sgur/vim-editorconfig', + lazy = false, + init = function() + vim.g.editorconfig_verbose = 1 + vim.g.editorconfig_blacklist = { + filetype = { + 'git.*', + 'fugitive', + 'help', + 'lsp-.*', + 'any-jump', + 'gina-.*', + }, + pattern = { '\\.un~$' }, + } + end, + }, +} diff --git a/lua/plex/plugins/extras/coding/emmet.lua b/lua/plex/plugins/extras/coding/emmet.lua new file mode 100644 index 0000000..4d4dc45 --- /dev/null +++ b/lua/plex/plugins/extras/coding/emmet.lua @@ -0,0 +1,31 @@ +return { + { + 'mattn/emmet-vim', + ft = { 'html', 'css', 'vue', 'javascript', 'javascriptreact', 'svelte' }, + init = function() + vim.g.user_emmet_mode = 'i' + vim.g.user_emmet_install_global = 0 + vim.g.user_emmet_install_command = 0 + vim.g.user_emmet_complete_tag = 0 + end, + config = function() + vim.api.nvim_create_autocmd('FileType', { + group = vim.api.nvim_create_augroup('plex_emmet', {}), + pattern = { + 'css', + 'html', + 'javascript', + 'javascriptreact', + 'svelte', + 'vue', + }, + callback = function() + vim.cmd([[ + EmmetInstall + imap (emmet-expand-abbr) + ]]) + end, + }) + end, + }, +} diff --git a/lua/plex/plugins/extras/coding/sandwich.lua b/lua/plex/plugins/extras/coding/sandwich.lua new file mode 100644 index 0000000..b29d003 --- /dev/null +++ b/lua/plex/plugins/extras/coding/sandwich.lua @@ -0,0 +1,19 @@ +return { + { + 'machakann/vim-sandwich', + -- stylua: ignore + keys = { + -- See https://github.com/machakann/vim-sandwich/blob/master/macros/sandwich/keymap/surround.vim + { 'ds', '(operator-sandwich-delete)(operator-sandwich-release-count)(textobj-sandwich-query-a)', silent = true }, + { 'dss', '(operator-sandwich-delete)(operator-sandwich-release-count)(textobj-sandwich-auto-a)', silent = true }, + { 'cs', '(operator-sandwich-replace)(operator-sandwich-release-count)(textobj-sandwich-query-a)', silent = true }, + { 'css', '(operator-sandwich-replace)(operator-sandwich-release-count)(textobj-sandwich-auto-a)', silent = true }, + { 'sa', '(operator-sandwich-add)', silent = true, mode = { 'n', 'x', 'o' }}, + { 'ir', '(textobj-sandwich-auto-i)', silent = true, mode = { 'x', 'o' }}, + { 'ab', '(textobj-sandwich-auto-a)', silent = true, mode = { 'x', 'o' }}, + }, + init = function() + vim.g.sandwich_no_default_key_mappings = 1 + end, + }, +} diff --git a/lua/plex/plugins/extras/diagnostics/proselint.lua b/lua/plex/plugins/extras/diagnostics/proselint.lua new file mode 100644 index 0000000..5dc2306 --- /dev/null +++ b/lua/plex/plugins/extras/diagnostics/proselint.lua @@ -0,0 +1,22 @@ +return { + { + 'williamboman/mason.nvim', + opts = function(_, opts) + table.insert(opts.ensure_installed, 'proselint') + end, + }, + { + 'jose-elias-alvarez/null-ls.nvim', + optional = true, + opts = function(_, opts) + local nls = require('null-ls') + local source = nls.builtins.diagnostics.proselint.with({ + diagnostics_postprocess = function(diagnostic) + diagnostic.severity = vim.diagnostic.severity.HINT + end, + }) + + table.insert(opts.sources, source) + end, + }, +} diff --git a/lua/plex/plugins/extras/diagnostics/write-good.lua b/lua/plex/plugins/extras/diagnostics/write-good.lua new file mode 100644 index 0000000..b75766e --- /dev/null +++ b/lua/plex/plugins/extras/diagnostics/write-good.lua @@ -0,0 +1,22 @@ +return { + { + 'williamboman/mason.nvim', + opts = function(_, opts) + table.insert(opts.ensure_installed, 'write-good') + end, + }, + { + 'jose-elias-alvarez/null-ls.nvim', + optional = true, + opts = function(_, opts) + local nls = require('null-ls') + local source = nls.builtins.diagnostics.write_good.with({ + diagnostics_postprocess = function(diagnostic) + diagnostic.severity = vim.diagnostic.severity.HINT + end, + }) + + table.insert(opts.sources, source) + end, + }, +} diff --git a/lua/plex/plugins/extras/editor/anyjump.lua b/lua/plex/plugins/extras/editor/anyjump.lua new file mode 100644 index 0000000..33134f5 --- /dev/null +++ b/lua/plex/plugins/extras/editor/anyjump.lua @@ -0,0 +1,22 @@ +return { + { + 'pechorin/any-jump.vim', + cmd = { 'AnyJump', 'AnyJumpVisual' }, + keys = { + { 'ii', 'AnyJump', desc = 'Any Jump' }, + { 'ii', 'AnyJumpVisual', mode = 'x', desc = 'Any Jump' }, + { 'ib', 'AnyJumpBack', desc = 'Any Jump Back' }, + { 'il', 'AnyJumpLastResults', desc = 'Any Jump Resume' }, + }, + init = function() + vim.g.any_jump_disable_default_keybindings = 1 + vim.api.nvim_create_autocmd('FileType', { + group = vim.api.nvim_create_augroup('plex_any-jump', {}), + pattern = 'any-jump', + callback = function() + vim.opt.cursorline = true + end, + }) + end, + }, +} diff --git a/lua/plex/plugins/extras/editor/flybuf.lua b/lua/plex/plugins/extras/editor/flybuf.lua new file mode 100644 index 0000000..e62afff --- /dev/null +++ b/lua/plex/plugins/extras/editor/flybuf.lua @@ -0,0 +1,11 @@ +return { + { + 'glepnir/flybuf.nvim', + cmd = 'FlyBuf', + keys = { + -- stylua: ignore + { '', function() require('flybuf').toggle() end, desc = 'Flybuf' }, + }, + opts = {}, + }, +} diff --git a/lua/plex/plugins/extras/editor/sidebar.lua b/lua/plex/plugins/extras/editor/sidebar.lua new file mode 100644 index 0000000..222a267 --- /dev/null +++ b/lua/plex/plugins/extras/editor/sidebar.lua @@ -0,0 +1,14 @@ +return { + { + 'sidebar-nvim/sidebar.nvim', + main = 'sidebar-nvim', + cmd = { 'SidebarNvimToggle', 'SidebarNvimOpen' }, + opts = { + open = true, + bindings = { + -- stylua: ignore + ['q'] = function() require('sidebar-nvim').close() end, + }, + }, + }, +} diff --git a/lua/plex/plugins/extras/editor/ufo.lua b/lua/plex/plugins/extras/editor/ufo.lua new file mode 100644 index 0000000..bb44411 --- /dev/null +++ b/lua/plex/plugins/extras/editor/ufo.lua @@ -0,0 +1,112 @@ +return { + + { + 'neovim/nvim-lspconfig', + opts = { + capabilities = { + textDocument = { + foldingRange = { + dynamicRegistration = false, + lineFoldingOnly = true, + }, + }, + }, + }, + }, + + { + 'kevinhwang91/nvim-ufo', + event = { 'BufReadPost', 'BufNewFile' }, + -- stylua: ignore + keys = { + { 'zR', function() require('ufo').openAllFolds() end }, + { 'zM', function() require('ufo').closeAllFolds() end }, + -- { 'zr', function() require('ufo').openFoldsExceptKinds() end }, + -- { 'zm', function() require('ufo').closeFoldsWith() end }, + }, + dependencies = { + 'kevinhwang91/promise-async', + 'nvim-treesitter/nvim-treesitter', + 'neovim/nvim-lspconfig', + }, + opts = function() + -- lsp->treesitter->indent + ---@param bufnr number + ---@return table + local function customizeSelector(bufnr) + local function handleFallbackException(err, providerName) + if type(err) == 'string' and err:match('UfoFallbackException') then + return require('ufo').getFolds(bufnr, providerName) + else + return require('promise').reject(err) + end + end + + return require('ufo') + .getFolds(bufnr, 'lsp') + :catch(function(err) + return handleFallbackException(err, 'treesitter') + end) + :catch(function(err) + return handleFallbackException(err, 'indent') + end) + end + + local ft_providers = { + vim = 'indent', + python = { 'indent' }, + git = '', + help = '', + qf = '', + fugitive = '', + fugitiveblame = '', + ['neo-tree'] = '', + } + + return { + open_fold_hl_timeout = 0, + preview = { + win_config = { + border = { '', '─', '', '', '', '─', '', '' }, + winhighlight = 'Normal:Folded', + winblend = 10, + }, + mappings = { + scrollU = '', + scrollD = '', + jumpTop = '[', + jumpBot = ']', + }, + }, + + -- Select the fold provider. + provider_selector = function(_, filetype, _) + return ft_providers[filetype] or customizeSelector + end, + + -- Display text for folded lines. + ---@param text table + ---@param lnum integer + ---@param endLnum integer + ---@param width integer + ---@return table + fold_virt_text_handler = function(text, lnum, endLnum, width) + local suffix = ' 󰇘 ' + local lines = (' 󰁂 %d '):format(endLnum - lnum) + + local cur_width = 0 + for _, section in ipairs(text) do + cur_width = cur_width + vim.fn.strdisplaywidth(section[1]) + end + + suffix = suffix + .. (' '):rep(width - cur_width - vim.fn.strdisplaywidth(lines) - 3) + + table.insert(text, { suffix, 'UfoFoldedEllipsis' }) + table.insert(text, { lines, 'Folded' }) + return text + end, + } + end, + }, +} diff --git a/lua/plex/plugins/extras/formatting/prettier.lua b/lua/plex/plugins/extras/formatting/prettier.lua new file mode 100644 index 0000000..dbbeba8 --- /dev/null +++ b/lua/plex/plugins/extras/formatting/prettier.lua @@ -0,0 +1,36 @@ +-- This is part of LazyVim's code, with my modifications. +-- See: https://github.com/LazyVim/LazyVim/blob/main/lua/lazyvim/plugins/extras/formatting/prettier.lua + +return { + + { + 'williamboman/mason.nvim', + opts = function(_, opts) + if type(opts.ensure_installed) == 'table' then + table.insert(opts.ensure_installed, 'prettierd') + end + end, + }, + + { + 'mhartington/formatter.nvim', + optional = true, + opts = function(_, opts) + opts = opts or {} + local filetypes = { + -- FIXME:add more filetypes + json = { require('formatter.defaults.prettierd') }, + } + opts.filetype = vim.tbl_extend('keep', opts.filetype or {}, filetypes) + end, + }, + + { + 'jose-elias-alvarez/null-ls.nvim', + optional = true, + opts = function(_, opts) + local nls = require('null-ls') + table.insert(opts.sources, nls.builtins.formatting.prettierd) + end, + }, +} diff --git a/lua/plex/plugins/extras/git/fugitive.lua b/lua/plex/plugins/extras/git/fugitive.lua new file mode 100644 index 0000000..30e43cc --- /dev/null +++ b/lua/plex/plugins/extras/git/fugitive.lua @@ -0,0 +1,31 @@ +return { + + ----------------------------------------------------------------------------- + { + 'tpope/vim-fugitive', + cmd = { 'G', 'Git', 'Gfetch', 'Gpush', 'Gclog', 'Gdiffsplit' }, + keys = { + { 'gd', 'Gdiffsplit', desc = 'Git diff' }, + { 'gb', 'Git blame', desc = 'Git blame' }, + }, + config = function() + vim.api.nvim_create_autocmd('FileType', { + group = vim.api.nvim_create_augroup('plex_fugitive', {}), + pattern = 'fugitiveblame', + callback = function() + vim.schedule(function() + vim.cmd.normal('A') + end) + end, + }) + end, + }, + + ----------------------------------------------------------------------------- + { + 'junegunn/gv.vim', + dependencies = { 'tpope/vim-fugitive' }, + cmd = 'GV', + }, + +} diff --git a/lua/plex/plugins/extras/lang/ansible.lua b/lua/plex/plugins/extras/lang/ansible.lua new file mode 100644 index 0000000..b3a4f49 --- /dev/null +++ b/lua/plex/plugins/extras/lang/ansible.lua @@ -0,0 +1,72 @@ +-- plex.plugins.extras.lang.ansible +-- + +return { + + { + 'nvim-treesitter/nvim-treesitter', + dependencies = { + 'pearofducks/ansible-vim', + ft = { 'ansible', 'ansible_hosts', 'jinja2' }, + }, + opts = function(_, opts) + if type(opts.ensure_installed) == 'table' then + vim.list_extend(opts.ensure_installed, { 'yaml' }) + end + + vim.g.ansible_extra_keywords_highlight = 1 + vim.g.ansible_template_syntaxes = { + ['*.json.j2'] = 'json', + ['*.(ba)?sh.j2'] = 'sh', + ['*.ya?ml.j2'] = 'yaml', + ['*.xml.j2'] = 'xml', + ['*.conf.j2'] = 'conf', + ['*.ini.j2'] = 'ini', + } + + -- Setup filetype settings + vim.api.nvim_create_autocmd('FileType', { + group = vim.api.nvim_create_augroup('plex_ftplugin_ansible', {}), + pattern = 'ansible', + callback = function() + -- Add '.' to iskeyword for ansible modules, e.g. ansible.builtin.copy + vim.opt_local.iskeyword:append('.') + vim.b.undo_ftplugin = (vim.b.undo_ftplugin or '') + .. (vim.b.undo_ftplugin ~= nil and ' | ' or '') + .. 'setlocal iskeyword<' + + if vim.fn.executable('ansible-doc') then + vim.b.undo_ftplugin = vim.b.undo_ftplugin + .. '| sil! nunmap gK' + vim.keymap.set('n', 'gK', function() + -- Open ansible-doc in a vertical split with word under cursor. + vim.cmd([[ + vertical split + | execute('terminal PAGER=cat ansible-doc ' .. shellescape(expand(''))) + | setf man + | wincmd p + ]]) + end, { buffer = 0 }) + end + end, + }) + end, + }, + + { + 'neovim/nvim-lspconfig', + opts = { + servers = { + ansiblels = {}, + }, + }, + }, + + { + 'mason.nvim', + opts = function(_, opts) + opts.ensure_installed = opts.ensure_installed or {} + vim.list_extend(opts.ensure_installed, { 'ansible-lint' }) + end, + }, +} diff --git a/lua/plex/plugins/extras/lang/docker.lua b/lua/plex/plugins/extras/lang/docker.lua new file mode 100644 index 0000000..ec07a14 --- /dev/null +++ b/lua/plex/plugins/extras/lang/docker.lua @@ -0,0 +1,50 @@ +-- This is part of LazyVim's code, with my modifications. +-- See: https://github.com/LazyVim/LazyVim/blob/main/lua/lazyvim/plugins/extras/lang/docker.lua + +return { + { + 'nvim-treesitter/nvim-treesitter', + opts = function(_, opts) + if type(opts.ensure_installed) == 'table' then + vim.list_extend(opts.ensure_installed, { 'dockerfile' }) + end + end, + }, + + { + 'neovim/nvim-lspconfig', + opts = { + servers = { + dockerls = {}, + -- docker_compose_language_service = {}, + }, + }, + }, + + { + 'mason.nvim', + opts = function(_, opts) + opts.ensure_installed = opts.ensure_installed or {} + vim.list_extend(opts.ensure_installed, { 'hadolint' }) + end, + }, + + { + 'jose-elias-alvarez/null-ls.nvim', + optional = true, + opts = function(_, opts) + local nls = require('null-ls') + opts.sources = opts.sources or {} + vim.list_extend(opts.sources, { + nls.builtins.diagnostics.hadolint, + }) + end, + dependencies = { + 'mason.nvim', + opts = function(_, opts) + opts.ensure_installed = opts.ensure_installed or {} + vim.list_extend(opts.ensure_installed, { 'hadolint' }) + end, + }, + }, +} diff --git a/lua/plex/plugins/extras/lang/go.lua b/lua/plex/plugins/extras/lang/go.lua new file mode 100644 index 0000000..a8c89fb --- /dev/null +++ b/lua/plex/plugins/extras/lang/go.lua @@ -0,0 +1,199 @@ +-- This is part of LazyVim's code, with my modifications. +-- See: https://github.com/LazyVim/LazyVim/blob/main/lua/lazyvim/plugins/extras/lang/go.lua + +return { + + { + 'nvim-treesitter/nvim-treesitter', + opts = function(_, opts) + if type(opts.ensure_installed) == 'table' then + vim.list_extend(opts.ensure_installed, { + 'go', + 'gomod', + 'gosum', + 'gowork', + }) + end + + -- Convert a JSON string to a Go struct. + vim.api.nvim_buf_create_user_command( + 0, + 'JsonToStruct', + ---@param args table + function(args) + local range = args.line1 .. ',' .. args.line2 + local fname = vim.api.nvim_buf_get_name(0) + local cmd = { '!json-to-struct' } + table.insert(cmd, '-name ' .. vim.fn.fnamemodify(fname, ':t:r')) + table.insert(cmd, '-pkg ' .. vim.fn.fnamemodify(fname, ':h:t:r')) + vim.cmd(range .. ' ' .. table.concat(cmd, ' ')) + end, + { bar = true, nargs = 0, range = true } + ) + end, + }, + + { + 'neovim/nvim-lspconfig', + opts = { + servers = { + gopls = { + settings = { + -- https://github.com/golang/tools/blob/master/gopls/doc/settings.md + gopls = { + gofumpt = true, + usePlaceholders = true, + completeUnimported = true, + staticcheck = true, + directoryFilters = { + '-.git', + '-.vscode', + '-.idea', + '-.vscode-test', + '-node_modules', + }, + semanticTokens = 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, + }, + -- https://github.com/golang/tools/blob/master/gopls/doc/analyzers.md + analyses = { + fieldalignment = true, + nilness = true, + unusedparams = true, + unusedwrite = true, + useany = true, + -- fillreturns = true, + -- nonewvars = true, + -- shadow = true, + -- undeclaredname = true, + -- unusedvariable = true, + -- ST1000 = false, + -- ST1005 = false, + }, + }, + }, + }, + }, + setup = { + gopls = function(_, _) + -- workaround for gopls not supporting semanticTokensProvider + -- https://github.com/golang/go/issues/54531#issuecomment-1464982242 + require('plex.lib.utils').on_attach(function(client, _) + if client.name == 'gopls' then + if not client.server_capabilities.semanticTokensProvider then + local semantic = + client.config.capabilities.textDocument.semanticTokens + if semantic ~= nil then + client.server_capabilities.semanticTokensProvider = { + full = true, + legend = { + tokenTypes = semantic.tokenTypes, + tokenModifiers = semantic.tokenModifiers, + }, + range = true, + } + end + end + end + end) + -- end workaround + end, + }, + }, + }, + + { + 'mason.nvim', + opts = function(_, opts) + opts.ensure_installed = opts.ensure_installed or {} + vim.list_extend(opts.ensure_installed, { + 'gofumpt', + 'goimports-reviser', + 'gomodifytags', + 'impl', + 'json-to-struct', + }) + end, + }, + + { + 'mhartington/formatter.nvim', + optional = true, + opts = function(_, opts) + opts = opts or {} + local filetypes = { + go = { + require('formatter.filetypes.go').gofumpt, + }, + } + opts.filetype = vim.tbl_extend('keep', opts.filetype or {}, filetypes) + end, + }, + + { + 'jose-elias-alvarez/null-ls.nvim', + optional = true, + opts = function(_, opts) + local nls = require('null-ls') + local sources = { + nls.builtins.code_actions.gomodifytags, + nls.builtins.code_actions.impl, + nls.builtins.formatting.gofumpt, + -- nls.builtins.formatting.goimports_reviser, + } + opts.sources = opts.sources or {} + for _, source in ipairs(sources) do + table.insert(opts.sources, source) + end + end, + }, + + { + 'mfussenegger/nvim-dap', + optional = true, + dependencies = { + { + 'mason.nvim', + opts = function(_, opts) + opts.ensure_installed = opts.ensure_installed or {} + vim.list_extend(opts.ensure_installed, { 'delve' }) + end, + }, + { + 'leoluz/nvim-dap-go', + config = true, + }, + }, + }, + + { + 'nvim-neotest/neotest', + optional = true, + dependencies = { 'nvim-neotest/neotest-go' }, + opts = { + adapters = { + ['neotest-go'] = { + -- Here we can set options for neotest-go, e.g. + -- args = { '-tags=integration' } + }, + }, + }, + }, +} diff --git a/lua/plex/plugins/extras/lang/helm.lua b/lua/plex/plugins/extras/lang/helm.lua new file mode 100644 index 0000000..4b70967 --- /dev/null +++ b/lua/plex/plugins/extras/lang/helm.lua @@ -0,0 +1,24 @@ +-- plex.plugins.extras.lang.helm +-- + +return { + + { 'towolf/vim-helm', ft = 'helm' }, + + { + 'nvim-treesitter/nvim-treesitter', + opts = function(_, opts) + if type(opts.ensure_installed) == 'table' then + vim.list_extend(opts.ensure_installed, { 'yaml' }) + end + end, + }, + + { + 'mason.nvim', + opts = function(_, opts) + opts.ensure_installed = opts.ensure_installed or {} + vim.list_extend(opts.ensure_installed, { 'helm-ls' }) + end, + }, +} diff --git a/lua/plex/plugins/extras/lang/json.lua b/lua/plex/plugins/extras/lang/json.lua new file mode 100644 index 0000000..a512f84 --- /dev/null +++ b/lua/plex/plugins/extras/lang/json.lua @@ -0,0 +1,40 @@ +-- This is part of LazyVim's code, with my modifications. +-- See: https://github.com/LazyVim/LazyVim/blob/main/lua/lazyvim/plugins/extras/lang/json.lua + +return { + + { + 'nvim-treesitter/nvim-treesitter', + opts = function(_, opts) + if type(opts.ensure_installed) == 'table' then + vim.list_extend(opts.ensure_installed, { 'json', 'json5', 'jsonc' }) + end + end, + }, + + { + 'neovim/nvim-lspconfig', + dependencies = { 'b0o/SchemaStore.nvim', version = false }, + opts = { + servers = { + jsonls = { + -- lazy-load schemastore when needed + on_new_config = function(new_config) + new_config.settings.json.schemas = new_config.settings.json.schemas + or {} + vim.list_extend( + new_config.settings.json.schemas, + require('schemastore').json.schemas() + ) + end, + settings = { + json = { + format = { enable = true }, + validate = { enable = true }, + }, + }, + }, + }, + }, + }, +} diff --git a/lua/plex/plugins/extras/lang/python.lua b/lua/plex/plugins/extras/lang/python.lua new file mode 100644 index 0000000..a4c5a21 --- /dev/null +++ b/lua/plex/plugins/extras/lang/python.lua @@ -0,0 +1,72 @@ +-- plex.plugins.extras.lang.python +-- + +-- This is part of LazyVim's code, with my modifications. +-- See: https://github.com/LazyVim/LazyVim/blob/main/lua/lazyvim/plugins/extras/lang/python.lua +return { + + { + 'nvim-treesitter/nvim-treesitter', + opts = function(_, opts) + if type(opts.ensure_installed) == 'table' then + vim.list_extend( + opts.ensure_installed, + { 'ninja', 'python', 'requirements', 'rst', 'toml' } + ) + end + end, + }, + + { + 'neovim/nvim-lspconfig', + dependencies = 'plex/neoconf-venom.nvim', + opts = { + servers = { + pyright = {}, + }, + }, + }, + + { + 'plex/neoconf-venom.nvim', + main = 'venom', + opts = {}, + -- stylua: ignore + keys = { + { 'cv', 'Telescope venom virtualenvs', desc = 'Select VirtualEnv' }, + }, + }, + + { + 'nvim-neotest/neotest', + optional = true, + dependencies = { 'nvim-neotest/neotest-python' }, + opts = { + adapters = { + ['neotest-python'] = { + -- Here you can specify the settings for the adapter, i.e. + -- runner = "pytest", + -- python = '.venv/bin/python', + }, + }, + }, + }, + + { + 'mfussenegger/nvim-dap', + optional = true, + dependencies = { + 'mfussenegger/nvim-dap-python', + -- stylua: ignore + keys = { + { 'dPt', function() require('dap-python').test_method() end, desc = 'Debug Method' }, + { 'dPc', function() require('dap-python').test_class() end, desc = 'Debug Class' }, + }, + config = function() + local path = + require('mason-registry').get_package('debugpy'):get_install_path() + require('dap-python').setup(path .. '/venv/bin/python') + end, + }, + }, +} diff --git a/lua/plex/plugins/extras/lang/terraform.lua b/lua/plex/plugins/extras/lang/terraform.lua new file mode 100644 index 0000000..242ad12 --- /dev/null +++ b/lua/plex/plugins/extras/lang/terraform.lua @@ -0,0 +1,38 @@ +-- This is part of LazyVim's code, with my modifications. +-- See: https://github.com/LazyVim/LazyVim/blob/main/lua/lazyvim/plugins/extras/lang/terraform.lua + +return { + + { + 'nvim-treesitter/nvim-treesitter', + opts = function(_, opts) + if type(opts.ensure_installed) == 'table' then + vim.list_extend(opts.ensure_installed, { + 'terraform', + 'hcl', + }) + end + end, + }, + + { + 'neovim/nvim-lspconfig', + opts = { + servers = { + terraformls = {}, + }, + }, + }, + + { + 'jose-elias-alvarez/null-ls.nvim', + optional = true, + opts = function(_, opts) + if type(opts.sources) == 'table' then + local builtins = require('null-ls.builtins') + table.insert(opts.sources, builtins.formatting.terraform_fmt) + table.insert(opts.sources, builtins.diagnostics.terraform_validate) + end + end, + }, +} diff --git a/lua/plex/plugins/extras/lang/typescript.lua b/lua/plex/plugins/extras/lang/typescript.lua new file mode 100644 index 0000000..8cdd7aa --- /dev/null +++ b/lua/plex/plugins/extras/lang/typescript.lua @@ -0,0 +1,125 @@ +-- This is part of LazyVim's code, with my modifications. +-- See: https://github.com/LazyVim/LazyVim/blob/main/lua/lazyvim/plugins/extras/lang/typescript.lua + +return { + + -- add typescript to treesitter + { + 'nvim-treesitter/nvim-treesitter', + opts = function(_, opts) + if type(opts.ensure_installed) == 'table' then + vim.list_extend(opts.ensure_installed, { 'typescript', 'tsx' }) + end + end, + }, + + -- correctly setup lspconfig + { + 'neovim/nvim-lspconfig', + dependencies = { 'jose-elias-alvarez/typescript.nvim' }, + opts = { + -- make sure mason installs the server + servers = { + ---@type lspconfig.options.tsserver + tsserver = { + -- stylua: ignore + keys = { + { 'co', 'TypescriptOrganizeImports', desc = 'Organize Imports' }, + { 'cR', 'TypescriptRenameFile', desc = 'Rename File' }, + }, + settings = { + ---@diagnostic disable: missing-fields + typescript = { + format = { + indentSize = vim.o.shiftwidth, + convertTabsToSpaces = vim.o.expandtab, + tabSize = vim.o.tabstop, + }, + }, + javascript = { + format = { + indentSize = vim.o.shiftwidth, + convertTabsToSpaces = vim.o.expandtab, + tabSize = vim.o.tabstop, + }, + }, + completions = { + completeFunctionCalls = true, + }, + }, + }, + }, + setup = { + tsserver = function(_, opts) + require('typescript').setup({ server = opts }) + return true + end, + }, + }, + }, + + { + 'jose-elias-alvarez/null-ls.nvim', + optional = true, + opts = function(_, opts) + table.insert( + opts.sources, + require('typescript.extensions.null-ls.code-actions') + ) + end, + }, + + { + 'mfussenegger/nvim-dap', + optional = true, + dependencies = { + { + 'williamboman/mason.nvim', + opts = function(_, opts) + opts.ensure_installed = opts.ensure_installed or {} + table.insert(opts.ensure_installed, 'js-debug-adapter') + end, + }, + }, + opts = function() + local dap = require('dap') + if not dap.adapters['pwa-node'] then + require('dap').adapters['pwa-node'] = { + type = 'server', + host = 'localhost', + port = '${port}', + executable = { + command = 'node', + -- 💀 Make sure to update this path to point to your installation + args = { + require('mason-registry') + .get_package('js-debug-adapter') + :get_install_path() .. '/js-debug/src/dapDebugServer.js', + '${port}', + }, + }, + } + end + for _, language in ipairs({ 'typescript', 'javascript' }) do + if not dap.configurations[language] then + dap.configurations[language] = { + { + type = 'pwa-node', + request = 'launch', + name = 'Launch file', + program = '${file}', + cwd = '${workspaceFolder}', + }, + { + type = 'pwa-node', + request = 'attach', + name = 'Attach', + processId = require('dap.utils').pick_process, + cwd = '${workspaceFolder}', + }, + } + end + end + end, + }, +} diff --git a/lua/plex/plugins/extras/lang/yaml.lua b/lua/plex/plugins/extras/lang/yaml.lua new file mode 100644 index 0000000..f9528be --- /dev/null +++ b/lua/plex/plugins/extras/lang/yaml.lua @@ -0,0 +1,63 @@ +-- This is part of LazyVim's code, with my modifications. +-- See: https://github.com/LazyVim/LazyVim/blob/main/lua/lazyvim/plugins/extras/lang/yaml.lua + +return { + + { + 'nvim-treesitter/nvim-treesitter', + opts = function(_, opts) + if type(opts.ensure_installed) == 'table' then + vim.list_extend(opts.ensure_installed, { 'yaml' }) + end + end, + }, + + { + 'neovim/nvim-lspconfig', + dependencies = { 'b0o/SchemaStore.nvim', version = false }, + opts = { + servers = { + yamlls = { + -- Have to add this for yamlls to understand that we support line folding + capabilities = { + textDocument = { + foldingRange = { + dynamicRegistration = false, + lineFoldingOnly = true, + }, + }, + }, + -- Lazy-load schemastore when needed + on_new_config = function(new_config) + new_config.settings.yaml.schemas = vim.tbl_extend( + 'force', + require('schemastore').yaml.schemas(), + new_config.settings.yaml.schemas or {} + ) + end, + settings = { + redhat = { telemetry = { enabled = false } }, + yaml = { + completion = true, + hover = true, + validate = true, + format = { + enable = true, + }, + schemaStore = { + -- Must disable built-in schemaStore support to use + -- schemas from SchemaStore.nvim plugin + enable = false, + -- Avoid TypeError: Cannot read properties of undefined (reading 'length') + url = '', + }, + schemas = { + kubernetes = { 'k8s**.yaml', 'kube*/*.yaml' }, + }, + }, + }, + }, + }, + }, + }, +} diff --git a/lua/plex/plugins/extras/linting/ruff.lua b/lua/plex/plugins/extras/linting/ruff.lua new file mode 100644 index 0000000..20592da --- /dev/null +++ b/lua/plex/plugins/extras/linting/ruff.lua @@ -0,0 +1,50 @@ +return { + + { + 'neovim/nvim-lspconfig', + opts = { + servers = { + ruff_lsp = {}, + }, + setup = { + ruff_lsp = function(_, opts) + local root_files = { + '.python-version', + 'pyproject.toml', + 'ruff.toml', + } + local util = require('lspconfig.util') + opts.root_dir = util.root_pattern(unpack(root_files)) + or util.find_git_ancestor() + + require('plex.lib.utils').on_attach(function(client, _) + if client.name == 'ruff_lsp' then + client.server_capabilities.hoverProvider = false + end + end) + end, + }, + }, + }, + + { + 'williamboman/mason.nvim', + opts = function(_, opts) + vim.list_extend(opts.ensure_installed, { + 'ruff', + 'ruff-lsp', + }) + end, + }, + + { + 'jose-elias-alvarez/null-ls.nvim', + optional = true, + opts = function(_, opts) + if type(opts.sources) == 'table' then + local nls = require('null-ls') + table.insert(opts.sources, nls.builtins.formatting.ruff) + end + end, + }, +} diff --git a/lua/plex/plugins/extras/lsp/gtd.lua b/lua/plex/plugins/extras/lsp/gtd.lua new file mode 100644 index 0000000..076afa7 --- /dev/null +++ b/lua/plex/plugins/extras/lsp/gtd.lua @@ -0,0 +1,33 @@ +return { + { + 'hrsh7th/nvim-gtd', + event = { 'BufReadPre', 'BufNewFile' }, + dependencies = 'neovim/nvim-lspconfig', + -- stylua: ignore + keys = { + { + 'gf', + function() require('gtd').exec({ command = 'split' }) end, + desc = 'Go to definition or file', + }, + }, + ---@type gtd.kit.App.Config.Schema + opts = { + sources = { + { name = 'findup' }, + { + name = 'walk', + root_markers = { + '.git', + '.neoconf.json', + 'Makefile', + 'package.json', + 'tsconfig.json', + }, + ignore_patterns = { '/node_modules', '/.git' }, + }, + { name = 'lsp' }, + }, + }, + }, +} diff --git a/lua/plex/plugins/extras/lsp/inlayhints.lua b/lua/plex/plugins/extras/lsp/inlayhints.lua new file mode 100644 index 0000000..d35d84d --- /dev/null +++ b/lua/plex/plugins/extras/lsp/inlayhints.lua @@ -0,0 +1,22 @@ +return { + -- Not needed anymore with nvim-0.10.0 + { + 'lvimuser/lsp-inlayhints.nvim', + branch = 'anticonceal', + event = 'LspAttach', + opts = {}, + config = function(_, opts) + require('lsp-inlayhints').setup(opts) + vim.api.nvim_create_autocmd('LspAttach', { + group = vim.api.nvim_create_augroup('LspAttach_inlayhints', {}), + callback = function(args) + if not (args.data and args.data.client_id) then + return + end + local client = vim.lsp.get_client_by_id(args.data.client_id) + require('lsp-inlayhints').on_attach(client, args.buf) + end, + }) + end, + }, +} diff --git a/lua/plex/plugins/extras/lsp/lightbulb.lua b/lua/plex/plugins/extras/lsp/lightbulb.lua new file mode 100644 index 0000000..9109007 --- /dev/null +++ b/lua/plex/plugins/extras/lsp/lightbulb.lua @@ -0,0 +1,20 @@ +return { + { + 'kosayoda/nvim-lightbulb', + event = { 'BufReadPre', 'BufNewFile' }, + opts = { + ignore = { + clients = { 'null-ls' }, + }, + }, + config = function(_, opts) + require('nvim-lightbulb').setup(opts) + vim.api.nvim_create_autocmd('CursorHold', { + group = vim.api.nvim_create_augroup('plex_lightbulb', {}), + callback = function() + require('nvim-lightbulb').update_lightbulb() + end, + }) + end, + }, +} diff --git a/lua/plex/plugins/extras/lsp/null-ls.lua b/lua/plex/plugins/extras/lsp/null-ls.lua new file mode 100644 index 0000000..15d250c --- /dev/null +++ b/lua/plex/plugins/extras/lsp/null-ls.lua @@ -0,0 +1,62 @@ +return { + + { + 'jose-elias-alvarez/null-ls.nvim', + event = { 'BufReadPre', 'BufNewFile' }, + dependencies = { 'williamboman/mason.nvim' }, + opts = function(_, opts) + -- https://github.com/jose-elias-alvarez/null-ls.nvim/blob/main/doc/BUILTINS.md + local builtins = require('null-ls').builtins + local sources = { + builtins.formatting.stylua, + builtins.formatting.shfmt, + } + for _, source in ipairs(sources) do + table.insert(opts.sources, source) + end + + opts.fallback_severity = vim.diagnostic.severity.INFO + opts.should_attach = function(bufnr) + return not vim.api.nvim_buf_get_name(bufnr):match('^[a-z]+://') + end + opts.root_dir = require('null-ls.utils').root_pattern( + '.git', + '_darcs', + '.hg', + '.bzr', + '.svn', + '.null-ls-root', + '.neoconf.json', + '.python-version', + 'Makefile' + ) + end, + }, + + -- You can add your own sources. Here's an example: + -- { + -- 'jose-elias-alvarez/null-ls.nvim', + -- optional = true, + -- opts = function(_, opts) + -- local builtins = require('null-ls.builtins') + -- local sources = { + -- builtins.formatting.black, + -- builtins.formatting.fixjson, + -- builtins.formatting.golines, + -- builtins.formatting.markdownlint, + -- builtins.formatting.shellharden, + -- builtins.formatting.sql_formatter, + -- builtins.formatting.taplo, + -- builtins.diagnostics.markdownlint, + -- builtins.diagnostics.mypy, + -- builtins.diagnostics.vint, + -- builtins.diagnostics.yamllint, + -- builtins.code_actions.shellcheck, + -- } + -- opts.sources = opts.sources or {} + -- for _, source in ipairs(sources) do + -- table.insert(opts.sources, source) + -- end + -- end, + -- }, +} diff --git a/lua/plex/plugins/extras/lsp/yaml-companion.lua b/lua/plex/plugins/extras/lsp/yaml-companion.lua new file mode 100644 index 0000000..378c822 --- /dev/null +++ b/lua/plex/plugins/extras/lsp/yaml-companion.lua @@ -0,0 +1,33 @@ +return { + + { + 'neovim/nvim-lspconfig', + opts = { + setup = { + yamlls = function(_, _) + local yamlls_opts = require('yaml-companion').setup( + require('plex.lib.utils').opts('yaml-companion.nvim') + ) + require('lspconfig')['yamlls'].setup(yamlls_opts) + return true + end, + }, + }, + }, + + { + 'someone-stole-my-name/yaml-companion.nvim', + dependencies = { + 'neovim/nvim-lspconfig', + 'nvim-telescope/telescope.nvim', + }, + keys = { + -- stylua: ignore + { 'y', 'Telescope yaml_schema', desc = 'YAML Schema' }, + }, + opts = {}, + config = function(_, _) + require('telescope').load_extension('yaml_schema') + end, + }, +} diff --git a/lua/plex/plugins/extras/org/vimwiki.lua b/lua/plex/plugins/extras/org/vimwiki.lua new file mode 100644 index 0000000..f96f55b --- /dev/null +++ b/lua/plex/plugins/extras/org/vimwiki.lua @@ -0,0 +1,44 @@ +return { + { + 'vimwiki/vimwiki', + cmd = { 'VimwikiIndex', 'VimwikiUISelect' }, + keys = { + { 'W', 'VimwikiIndex', { noremap = true } }, + }, + init = function() + vim.g.vimwiki_global_ext = 0 + vim.g.vimwiki_use_calendar = 1 + vim.g.vimwiki_hl_headers = 1 + vim.g.vimwiki_hl_cb_checked = 1 + vim.g.vimwiki_autowriteall = 0 + vim.g.vimwiki_listsym_rejected = '✗' + vim.g.vimwiki_listsyms = '✗○◐●✓' + end, + config = function() + vim.g.vimwiki_key_mappings = { + all_maps = 1, + global = 1, + headers = 1, + text_objs = 1, + table_format = 1, + table_mappings = 1, + lists = 1, + links = 1, + html = 1, + mouse = 0, + } + vim.g.vimwiki_list = { + { + diary_header = 'Diary', + diary_link_fmt = '%Y-%m/%d', + auto_toc = 1, + path = '~/docs/wiki/', + syntax = 'markdown', + ext = '.md', + }, + { path = '~/docs/books/', syntax = 'markdown', ext = '.md' }, + { path = '~/notes/', syntax = 'markdown', ext = '.md' }, + } + end, + }, +} diff --git a/lua/plex/plugins/extras/treesitter/treesj.lua b/lua/plex/plugins/extras/treesitter/treesj.lua new file mode 100644 index 0000000..52cdb0a --- /dev/null +++ b/lua/plex/plugins/extras/treesitter/treesj.lua @@ -0,0 +1,10 @@ +return { + { + 'Wansmer/treesj', + cmd = { 'TSJJoin', 'TSJSplit' }, + keys = { + { 'sj', 'TSJJoin' }, + { 'sk', 'TSJSplit' }, + }, + }, +} diff --git a/lua/plex/plugins/extras/ui/barbecue.lua b/lua/plex/plugins/extras/ui/barbecue.lua new file mode 100644 index 0000000..db3ef36 --- /dev/null +++ b/lua/plex/plugins/extras/ui/barbecue.lua @@ -0,0 +1,28 @@ +return { + { + 'utilyre/barbecue.nvim', + dependencies = { 'SmiteshP/nvim-navic', 'nvim-tree/nvim-web-devicons' }, + keys = { + { + 'ub', + function() + local off = vim.b['barbecue_entries'] == nil + require('barbecue.ui').toggle(off and true or nil) + end, + desc = 'Breadcrumbs toggle', + }, + }, + opts = function() + local kind_icons = vim.tbl_map(function(icon) + return vim.trim(icon) + end, require('plex.config').icons.kinds) + return { + attach_navic = false, + show_dirname = false, + show_modified = true, + kinds = kind_icons, + symbols = { separator = '' }, + } + end, + }, +} diff --git a/lua/plex/plugins/extras/ui/bufferline.lua b/lua/plex/plugins/extras/ui/bufferline.lua new file mode 100644 index 0000000..9fdeb9c --- /dev/null +++ b/lua/plex/plugins/extras/ui/bufferline.lua @@ -0,0 +1,45 @@ +return { + { + 'akinsho/bufferline.nvim', + event = 'VeryLazy', + opts = { + options = { + mode = 'tabs', + separator_style = 'slant', + show_close_icon = false, + show_buffer_close_icons = false, + diagnostics = false, + always_show_bufferline = true, + diagnostics_indicator = function(_, _, diag) + local icons = require('plex.config').icons.diagnostics + local ret = (diag.error and icons.Error .. diag.error .. ' ' or '') + .. (diag.warning and icons.Warn .. diag.warning or '') + return vim.trim(ret) + end, + custom_areas = { + right = function() + local project_root = require('plex.lib.badge').project() + local result = {} + local part = {} + part.text = '%#BufferLineTab# ' .. project_root + table.insert(result, part) + + -- Session indicator + if vim.v['this_session'] ~= '' then + table.insert(result, { text = '%#BufferLineTab#  ' }) + end + return result + end, + }, + offsets = { + { + filetype = 'neo-tree', + text = 'Neo-tree', + highlight = 'Directory', + text_align = 'center', + }, + }, + }, + }, + }, +} diff --git a/lua/plex/plugins/extras/ui/cursorword.lua b/lua/plex/plugins/extras/ui/cursorword.lua new file mode 100644 index 0000000..6a99446 --- /dev/null +++ b/lua/plex/plugins/extras/ui/cursorword.lua @@ -0,0 +1,47 @@ +return { + { + 'itchyny/vim-cursorword', + event = 'FileType', + init = function() + vim.g.cursorword = 0 + end, + config = function() + local augroup = vim.api.nvim_create_augroup('plex_cursorword', {}) + vim.api.nvim_create_autocmd('FileType', { + group = augroup, + pattern = { + 'conf', + 'dosini', + 'json', + 'markdown', + 'nginx', + 'text', + 'yaml', + }, + callback = function() + if vim.wo.diff or vim.wo.previewwindow then + vim.b.cursorword = 0 + else + vim.b.cursorword = 1 + end + end, + }) + vim.api.nvim_create_autocmd('InsertEnter', { + group = augroup, + callback = function() + if vim.b['cursorword'] == 1 then + vim.b['cursorword'] = 0 + end + end, + }) + vim.api.nvim_create_autocmd('InsertLeave', { + group = augroup, + callback = function() + if vim.b['cursorword'] == 0 then + vim.b['cursorword'] = 1 + end + end, + }) + end, + }, +} diff --git a/lua/plex/plugins/extras/ui/cybu.lua b/lua/plex/plugins/extras/ui/cybu.lua new file mode 100644 index 0000000..d733a55 --- /dev/null +++ b/lua/plex/plugins/extras/ui/cybu.lua @@ -0,0 +1,21 @@ +return { + { + 'ghillb/cybu.nvim', + dependencies = { 'nvim-tree/nvim-web-devicons', 'nvim-lua/plenary.nvim' }, + keys = { + { '[b', '(CybuPrev)' }, + { ']b', '(CybuNext)' }, + { '', '(CybuLastusedPrev)' }, + { '', '(CybuLastusedNext)' }, + }, + config = true, + }, + { + 'echasnovski/mini.bracketed', + optional = true, + opts = function(_, opts) + opts.buffer = { suffix = '' } + return opts + end, + }, +} diff --git a/lua/plex/plugins/extras/ui/deadcolumn.lua b/lua/plex/plugins/extras/ui/deadcolumn.lua new file mode 100644 index 0000000..e39b99f --- /dev/null +++ b/lua/plex/plugins/extras/ui/deadcolumn.lua @@ -0,0 +1,9 @@ +return { + { + 'Bekaboo/deadcolumn.nvim', + event = { 'BufReadPre', 'BufNewFile' }, + opts = { + scope = 'visible', + }, + }, +} diff --git a/lua/plex/plugins/extras/ui/goto-preview.lua b/lua/plex/plugins/extras/ui/goto-preview.lua new file mode 100644 index 0000000..25db925 --- /dev/null +++ b/lua/plex/plugins/extras/ui/goto-preview.lua @@ -0,0 +1,51 @@ +return { + { + 'rmagatti/goto-preview', + dependencies = 'nvim-telescope/telescope.nvim', + keys = { + { + 'gpd', + function() + require('goto-preview').goto_preview_definition({}) + end, + { noremap = true }, + }, + { + 'gpi', + function() + require('goto-preview').goto_preview_implementation({}) + end, + { noremap = true }, + }, + { + 'gpc', + function() + require('goto-preview').close_all_win() + end, + { noremap = true }, + }, + { + 'gpr', + function() + require('goto-preview').goto_preview_references({}) + end, + { noremap = true }, + }, + }, + opts = { + width = 78, + height = 15, + default_mappings = false, + opacity = 10, + post_open_hook = function(_, win) + vim.api.nvim_win_set_config(win, { + border = { '╭', '─', '╮', '│', '╯', '─', '╰', '│' }, + }) + local scope = { scope = 'local', win = win } + vim.api.nvim_set_option_value('spell', false, scope) + vim.api.nvim_set_option_value('signcolumn', 'no', scope) + vim.keymap.set('n', '', 'quit') + end, + }, + }, +} diff --git a/lua/plex/plugins/extras/ui/incline.lua b/lua/plex/plugins/extras/ui/incline.lua new file mode 100644 index 0000000..4da4417 --- /dev/null +++ b/lua/plex/plugins/extras/ui/incline.lua @@ -0,0 +1,7 @@ +return { + { + 'b0o/incline.nvim', + event = 'FileType', + opts = {}, + }, +} diff --git a/lua/plex/plugins/extras/ui/minimap.lua b/lua/plex/plugins/extras/ui/minimap.lua new file mode 100644 index 0000000..076c33f --- /dev/null +++ b/lua/plex/plugins/extras/ui/minimap.lua @@ -0,0 +1,19 @@ +return { + { + 'echasnovski/mini.map', + keys = { + { 'mn', 'lua MiniMap.toggle()', desc = 'Mini map' }, + }, + opts = function() + local minimap = require('mini.map') + return { + integrations = { + minimap.gen_integration.diagnostic(), + minimap.gen_integration.builtin_search(), + minimap.gen_integration.gitsigns(), + }, + window = { winblend = 50 }, + } + end, + }, +} diff --git a/lua/plex/plugins/extras/ui/statuscol.lua b/lua/plex/plugins/extras/ui/statuscol.lua new file mode 100644 index 0000000..b9e1212 --- /dev/null +++ b/lua/plex/plugins/extras/ui/statuscol.lua @@ -0,0 +1,24 @@ +return { + { + 'luukvbaal/statuscol.nvim', + event = 'BufReadPost', + init = function() + vim.opt_global.foldcolumn = '1' + end, + opts = function() + local builtin = require('statuscol.builtin') + return { + setopt = true, + relculright = true, + segments = { + { + sign = { name = { '.*' }, maxwidth = 2, colwidth = 1, auto = true }, + click = 'v:lua.ScSa', + }, + { text = { builtin.lnumfunc }, click = 'v:lua.ScLa' }, + { text = { builtin.foldfunc }, click = 'v:lua.ScFa' }, + }, + } + end, + }, +} diff --git a/lua/plex/plugins/git.lua b/lua/plex/plugins/git.lua new file mode 100644 index 0000000..15530f4 --- /dev/null +++ b/lua/plex/plugins/git.lua @@ -0,0 +1,246 @@ +-- Plugins: Git +-- https://github.com/rafi/vim-config + +return { + + ----------------------------------------------------------------------------- + { + 'lewis6991/gitsigns.nvim', + event = { 'BufReadPre', 'BufNewFile' }, + -- See: https://github.com/lewis6991/gitsigns.nvim#usage + -- stylua: ignore + opts = { + signcolumn = true, -- Toggle with `:Gitsigns toggle_signs` + numhl = false, -- Toggle with `:Gitsigns toggle_numhl` + linehl = false, -- Toggle with `:Gitsigns toggle_linehl` + word_diff = false, -- Toggle with `:Gitsigns toggle_word_diff` + current_line_blame = false, -- Toggle with `:Gitsigns toggle_current_line_blame` + attach_to_untracked = true, + watch_gitdir = { + interval = 1000, + follow_files = true, + }, + preview_config = { + border = 'rounded', + }, + on_attach = function(bufnr) + local gs = package.loaded.gitsigns + + local function map(mode, l, r, opts) + opts = opts or {} + opts.buffer = bufnr + vim.keymap.set(mode, l, r, opts) + end + + -- Navigation + ---@return string + map('n', ']g', function() + if vim.wo.diff then return ']c' end + vim.schedule(function() gs.next_hunk() end) + return '' + end, { expr = true, desc = 'Git hunk forward' }) + + map('n', '[g', function() + if vim.wo.diff then return '[c' end + vim.schedule(function() gs.prev_hunk() end) + return '' + end, { expr = true, desc = 'Git hunk last' }) + + -- Actions + -- + map('n', 'hs', gs.stage_hunk, { silent = true, desc = 'Stage hunk' }) + map('n', 'hr', gs.reset_hunk, { silent = true, desc = 'Reset hunk' }) + map('x', 'hs', function() gs.stage_hunk({vim.fn.line('.'), vim.fn.line('v')}) end) + map('x', 'hr', function() gs.reset_hunk({vim.fn.line('.'), vim.fn.line('v')}) end) + map('n', 'hS', gs.stage_buffer, { silent = true, desc = 'Stage buffer' }) + map('n', 'hu', gs.undo_stage_hunk, { desc = 'Undo staged hunk' }) + map('n', 'hR', gs.reset_buffer, { desc = 'Reset buffer' }) + map('n', 'gs', gs.preview_hunk, { desc = 'Preview hunk' }) + map('n', 'hp', gs.preview_hunk_inline, { desc = 'Preview hunk inline' }) + map('n', 'hb', function() gs.blame_line({ full=true }) end, { desc = 'Show blame commit' }) + map('n', 'tb', gs.toggle_current_line_blame, { desc = 'Toggle Git line blame' }) + -- map('n', 'tw', gs.toggle_word_diff) + map('n', 'hd', gs.diffthis, { desc = 'Diff against the index' }) + map('n', 'hD', function() gs.diffthis('~') end, { desc = 'Diff against the last commit' }) + map('n', 'td', gs.toggle_deleted, { desc = 'Toggle Git deleted' }) + map('n', 'hl', function() + if vim.bo.filetype ~= 'qf' then + require('gitsigns').setqflist(0, { use_location_list = true }) + end + end, { desc = 'Send to location list' }) + + -- Text object + map({'o', 'x'}, 'ih', ':Gitsigns select_hunk', { silent = true, desc = 'Select hunk'}) + + end, + }, + }, + + ----------------------------------------------------------------------------- + { + 'sindrets/diffview.nvim', + cmd = { 'DiffviewOpen', 'DiffviewFileHistory' }, + keys = { + { 'gd', 'DiffviewFileHistory', desc = 'Diff File' }, + { 'gv', 'DiffviewOpen', desc = 'Diff View' }, + }, + opts = function() + local actions = require('diffview.actions') + vim.api.nvim_create_autocmd({ 'WinEnter', 'BufEnter' }, { + group = vim.api.nvim_create_augroup('rafi_diffview', {}), + pattern = 'diffview:///panels/*', + callback = function() + vim.opt_local.cursorline = true + vim.opt_local.winhighlight = 'CursorLine:WildMenu' + end, + }) + + return { + enhanced_diff_hl = true, -- See ':h diffview-config-enhanced_diff_hl' + keymaps = { + view = { + { 'n', 'q', 'DiffviewClose' }, + { 'n', '', actions.select_next_entry }, + { 'n', '', actions.select_prev_entry }, + { 'n', 'a', actions.focus_files }, + { 'n', 'e', actions.toggle_files }, + }, + file_panel = { + { 'n', 'q', 'DiffviewClose' }, + { 'n', 'h', actions.prev_entry }, + { 'n', 'o', actions.focus_entry }, + { 'n', 'gf', actions.goto_file }, + { 'n', 'sg', actions.goto_file_split }, + { 'n', 'st', actions.goto_file_tab }, + { 'n', '', actions.refresh_files }, + { 'n', ';e', actions.toggle_files }, + }, + file_history_panel = { + { 'n', 'q', 'DiffviewClose' }, + { 'n', 'o', actions.focus_entry }, + { 'n', 'O', actions.options }, + }, + }, + } + end, + }, + + ----------------------------------------------------------------------------- + { + 'NeogitOrg/neogit', + dependencies = { + 'sindrets/diffview.nvim', + 'nvim-telescope/telescope.nvim' + }, + cmd = 'Neogit', + keys = { + { 'mg', 'Neogit', desc = 'Neogit' }, + }, + -- See: https://github.com/TimUntersberger/neogit#configuration + opts = { + disable_signs = false, + disable_context_highlighting = false, + disable_commit_confirmation = false, + signs = { + section = { '>', 'v' }, + item = { '>', 'v' }, + hunk = { '', '' }, + }, + integrations = { + diffview = true, + }, + }, + }, + + ----------------------------------------------------------------------------- + { + 'FabijanZulj/blame.nvim', + cmd = 'ToggleBlame', + -- stylua: ignore + keys = { + { 'gb', 'ToggleBlame virtual', desc = 'Git blame' }, + { 'gB', 'ToggleBlame window', desc = 'Git blame (window)' }, + }, + opts = { + date_format = '%Y-%m-%d %H:%M', + }, + }, + + ----------------------------------------------------------------------------- + { + 'rhysd/git-messenger.vim', + cmd = 'GitMessenger', + keys = { + { 'gm', '(git-messenger)', desc = 'Git messenger'} + }, + init = function() + vim.g.git_messenger_include_diff = 'current' + vim.g.git_messenger_no_default_mappings = false + vim.g.git_messenger_floating_win_opts = { border = 'rounded' } + end, + }, + + ----------------------------------------------------------------------------- + { + 'ruifm/gitlinker.nvim', + keys = { + { + 'go', + function() + require('gitlinker').get_buf_range_url('n') + end, + silent = true, + desc = 'Git open in browser', + }, + { + 'go', + function() + require('gitlinker').get_buf_range_url('v') + end, + mode = 'x', + desc = 'Git open in browser', + }, + }, + opts = { + mappings = nil, + opts = { + add_current_line_on_normal_mode = true, + print_url = false, + action_callback = function(...) + return require('gitlinker.actions').open_in_browser(...) + end, + }, + }, + }, + + ----------------------------------------------------------------------------- + { + 'rhysd/committia.vim', + event = 'BufReadPre COMMIT_EDITMSG', + init = function() + -- See: https://github.com/rhysd/committia.vim#variables + vim.g.committia_min_window_width = 30 + vim.g.committia_edit_window_width = 75 + end, + config = function() + vim.g.committia_hooks = { + edit_open = function() + vim.cmd.resize(10) + local opts = { + buffer = vim.api.nvim_get_current_buf(), + silent = true, + } + local function imap(lhs, rhs) + vim.keymap.set('i', lhs, rhs, opts) + end + imap('', '(committia-scroll-diff-down-half)') + imap('', '(committia-scroll-diff-up-half)') + imap('', '(committia-scroll-diff-down-page)') + imap('', '(committia-scroll-diff-up-page)') + imap('', '(committia-scroll-diff-down)') + imap('', '(committia-scroll-diff-up)') + end, + } + end, + }, +} diff --git a/lua/plex/plugins/lsp/format.lua b/lua/plex/plugins/lsp/format.lua new file mode 100644 index 0000000..01d896a --- /dev/null +++ b/lua/plex/plugins/lsp/format.lua @@ -0,0 +1,166 @@ +-- LSP: Auto-format on save +-- https://github.com/plex/vim-config + +-- This is part of LazyVim's code, with my modifications. +-- See: https://github.com/LazyVim/LazyVim/blob/main/lua/lazyvim/plugins/lsp/format.lua + +local M = {} + +---@type PluginLspOpts +M.opts = nil + +function M.enabled() + return M.opts.autoformat +end + +function M.toggle() + if vim.b.autoformat == false then + vim.b.autoformat = nil + M.opts.autoformat = true + else + M.opts.autoformat = not M.opts.autoformat + end + local msg = M.opts.autoformat and 'Enabled' or 'Disabled' + vim.notify( + msg .. ' format on save', + vim.log.levels.INFO, + { title = 'Format' } + ) +end + +---@param opts? {force?:boolean} +function M.format(opts) + local buf = vim.api.nvim_get_current_buf() + if vim.b.autoformat == false and not (opts and opts.force) then + return + end + + local formatters = M.get_formatters(buf) + local client_ids = vim.tbl_map(function(client) + return client.id + end, formatters.active) + + if #client_ids == 0 then + return + end + + if M.opts.format_notify then + M.notify(formatters) + end + + vim.lsp.buf.format(vim.tbl_deep_extend('force', { + bufnr = buf, + filter = function(client) + return vim.tbl_contains(client_ids, client.id) + end, + }, require('plex.lib.utils').opts('nvim-lspconfig').format or {})) +end + +---@param formatters LazyVimFormatters +function M.notify(formatters) + local lines = { '# Active:' } + + for _, client in ipairs(formatters.active) do + local line = '- **' .. client.name .. '**' + if client.name == 'null-ls' then + line = line + .. ' (' + .. table.concat( + vim.tbl_map(function(f) + return '`' .. f.name .. '`' + end, formatters.null_ls), + ', ' + ) + .. ')' + end + table.insert(lines, line) + end + + if #formatters.available > 0 then + table.insert(lines, '') + table.insert(lines, '# Disabled:') + for _, client in ipairs(formatters.available) do + table.insert(lines, '- **' .. client.name .. '**') + end + end + + vim.notify(table.concat(lines, '\n'), vim.log.levels.INFO, { + title = 'Formatting', + on_open = function(win) + local scope = { scope = 'local', win = win } + vim.api.nvim_set_option_value('conceallevel', 3, scope) + vim.api.nvim_set_option_value('spell', false, scope) + local buf = vim.api.nvim_win_get_buf(win) + vim.treesitter.start(buf, 'markdown') + end, + }) +end + +-- Gets all lsp clients that support formatting. +-- When a null-ls formatter is available for the current filetype, +-- only null-ls formatters are returned. +function M.get_formatters(bufnr) + local ft = vim.bo[bufnr].filetype + -- check if we have any null-ls formatters for the current filetype + local null_ls = package.loaded['null-ls'] + and require('null-ls.sources').get_available(ft, 'NULL_LS_FORMATTING') + or {} + + ---@class LazyVimFormatters + local ret = { + ---@type lsp.Client[] + active = {}, + ---@type lsp.Client[] + available = {}, + null_ls = null_ls, + } + + local clients + if vim.lsp.get_clients ~= nil then + clients = vim.lsp.get_clients({ bufnr = bufnr }) + else + ---@diagnostic disable-next-line: deprecated + clients = vim.lsp.get_active_clients({ bufnr = bufnr }) + end + for _, client in ipairs(clients) do + if M.supports_format(client) then + if (#null_ls > 0 and client.name == 'null-ls') or #null_ls == 0 then + table.insert(ret.active, client) + else + table.insert(ret.available, client) + end + end + end + + return ret +end + +-- Gets all lsp clients that support formatting +-- and have not disabled it in their client config +---@param client lsp.Client +function M.supports_format(client) + if + client.config + and client.config.capabilities + and client.config.capabilities['documentFormattingProvider'] == false + then + return false + end + return client.supports_method('textDocument/formatting') + or client.supports_method('textDocument/rangeFormatting') +end + +---@param opts PluginLspOpts +function M.setup(opts) + M.opts = opts + vim.api.nvim_create_autocmd('BufWritePre', { + group = vim.api.nvim_create_augroup('plex_format', {}), + callback = function() + if M.opts.autoformat then + M.format() + end + end, + }) +end + +return M diff --git a/lua/plex/plugins/lsp/highlight.lua b/lua/plex/plugins/lsp/highlight.lua new file mode 100644 index 0000000..ba9e368 --- /dev/null +++ b/lua/plex/plugins/lsp/highlight.lua @@ -0,0 +1,47 @@ +-- LSP: Highlights +-- https://github.com/plex/vim-config + +-- This is part of LunarVim's code, with my modifications. +-- Reference: https://github.com/LunarVim/LunarVim + +local M = {} + +---@param client lsp.Client +---@param bufnr integer +function M.on_attach(client, bufnr) + if require('plex.lib.utils').has('vim-illuminate') then + -- Skipped setup for document_highlight, illuminate is installed. + return + end + local status_ok, highlight_supported = pcall(function() + return client.supports_method('textDocument/documentHighlight') + end) + if not status_ok or not highlight_supported then + return + end + + local group_name = 'lsp_document_highlight' + local ok, hl_autocmds = pcall(vim.api.nvim_get_autocmds, { + group = group_name, + buffer = bufnr, + event = 'CursorHold', + }) + + if ok and #hl_autocmds > 0 then + return + end + + vim.api.nvim_create_augroup(group_name, { clear = false }) + vim.api.nvim_create_autocmd('CursorHold', { + group = group_name, + buffer = bufnr, + callback = vim.lsp.buf.document_highlight, + }) + vim.api.nvim_create_autocmd('CursorMoved', { + group = group_name, + buffer = bufnr, + callback = vim.lsp.buf.clear_references, + }) +end + +return M diff --git a/lua/plex/plugins/lsp/init.lua b/lua/plex/plugins/lsp/init.lua new file mode 100644 index 0000000..c5ad749 --- /dev/null +++ b/lua/plex/plugins/lsp/init.lua @@ -0,0 +1,326 @@ +-- LSP: Plugins +-- https://github.com/plex/vim-config + +-- This is part of LazyVim's code, with my modifications. +-- See: https://github.com/LazyVim/LazyVim/blob/main/lua/lazyvim/plugins/lsp/init.lua + +return { + + ----------------------------------------------------------------------------- + { + 'neovim/nvim-lspconfig', + event = { 'BufReadPre', 'BufNewFile' }, + dependencies = { + { 'folke/neoconf.nvim', cmd = 'Neoconf', config = false, dependencies = { 'nvim-lspconfig' } }, + { 'folke/neodev.nvim', opts = {} }, + 'williamboman/mason.nvim', + 'williamboman/mason-lspconfig.nvim', + { + 'hrsh7th/cmp-nvim-lsp', + cond = function() + return require('plex.lib.utils').has('nvim-cmp') + end, + }, + }, + ---@class PluginLspOpts + opts = { + -- Options for vim.diagnostic.config() + diagnostics = { + signs = true, + underline = true, + update_in_insert = false, + virtual_text = { + spacing = 4, + source = 'if_many', + prefix = '●', + }, + severity_sort = true, + float = { + show_header = true, + border = 'rounded', + source = 'always', + }, + }, + -- Enable this to enable the builtin LSP inlay hints on Neovim >= 0.10.0 + -- Be aware that you also will need to properly configure your LSP server to + -- provide the inlay hints. + inlay_hints = { + enabled = false, + }, + -- Add any global capabilities here + capabilities = {}, + -- Automatically format on save + autoformat = false, + -- Options for vim.lsp.buf.format + -- `bufnr` and `filter` is handled by the formatter, + -- but can be also overridden when specified + format = { + formatting_options = nil, + timeout_ms = nil, + }, + -- Enable this to show formatters used in a notification + -- Useful for debugging formatter issues + format_notify = false, + -- LSP Server Settings + ---@type lspconfig.options + ---@diagnostic disable: missing-fields + servers = { + -- jsonls = {}, + lua_ls = { + settings = { + Lua = { + workspace = { checkThirdParty = false }, + completion = { callSnippet = 'Replace' }, + }, + }, + }, + }, + -- you can do any additional lsp server setup here + -- return true if you don't want this server to be setup with lspconfig + ---@type table + setup = { + -- example to setup with typescript.nvim + -- tsserver = function(_, opts) + -- require('typescript').setup({ server = opts }) + -- return true + -- end, + -- Specify * to use this function as a fallback for any server + -- ['*'] = function(server, opts) end, + }, + }, + ---@param opts PluginLspOpts + config = function(_, opts) + if require('plex.lib.utils').has('neoconf.nvim') then + local plugin = require('lazy.core.config').spec.plugins['neoconf.nvim'] + require('neoconf').setup(require('lazy.core.plugin').values(plugin, 'opts', false)) + end + -- Setup autoformat + require('plex.plugins.lsp.format').setup(opts) + -- Setup formatting, keymaps and highlights. + local lsp_on_attach = require('plex.lib.utils').on_attach + ---@param client lsp.Client + ---@param buffer integer + lsp_on_attach(function(client, buffer) + require('plex.plugins.lsp.keymaps').on_attach(client, buffer) + require('plex.plugins.lsp.highlight').on_attach(client, buffer) + + if vim.diagnostic.is_disabled() or vim.bo[buffer].buftype ~= '' then + vim.diagnostic.disable(buffer) + return + end + end) + + local register_capability = vim.lsp.handlers['client/registerCapability'] + + ---@diagnostic disable-next-line: duplicate-set-field + vim.lsp.handlers['client/registerCapability'] = function(err, res, ctx) + local ret = register_capability(err, res, ctx) + local client_id = ctx.client_id + ---@type lsp.Client|nil + local client = vim.lsp.get_client_by_id(client_id) + local buffer = vim.api.nvim_get_current_buf() + if client ~= nil then + require('plex.plugins.lsp.keymaps').on_attach(client, buffer) + end + return ret + end + + -- Diagnostics signs and highlights + for type, icon in pairs(require('plex.config').icons.diagnostics) do + local hl = 'DiagnosticSign' .. type + vim.fn.sign_define(hl, { text = icon, texthl = hl, numhl = '' }) + end + + -- Setup inlay-hints + local inlay_hint = vim.lsp.buf.inlay_hint or vim.lsp.inlay_hint + if opts.inlay_hints.enabled and inlay_hint then + lsp_on_attach(function(client, buffer) + if client.supports_method('textDocument/inlayHint') then + inlay_hint(buffer, true) + end + end) + end + + if + type(opts.diagnostics.virtual_text) == 'table' + and opts.diagnostics.virtual_text.prefix == 'icons' + then + opts.diagnostics.virtual_text.prefix = vim.fn.has('nvim-0.10') == 0 + and '●' + or function(diagnostic) + local icons = require('plex.config').icons.diagnostics + for d, icon in pairs(icons) do + if diagnostic.severity == vim.diagnostic.severity[d:upper()] then + return icon + end + end + end + end + + vim.diagnostic.config(vim.deepcopy(opts.diagnostics)) + + -- Initialize LSP servers and ensure Mason packages + + -- Setup base config for all servers. + local servers = opts.servers + local has_cmp, cmp_nvim_lsp = pcall(require, 'cmp_nvim_lsp') + local capabilities = vim.tbl_deep_extend( + 'force', + {}, + vim.lsp.protocol.make_client_capabilities(), + has_cmp and cmp_nvim_lsp.default_capabilities() or {}, + opts.capabilities or {} + ) + + -- Combine base config for each server and merge user-defined settings. + -- These can be overridden locally by lua/lsp/.lua + ---@param server_name string + local function make_config(server_name) + local server_opts = vim.tbl_deep_extend('force', { + capabilities = vim.deepcopy(capabilities), + }, servers[server_name] or {}) + + if opts.setup[server_name] then + if opts.setup[server_name](server_name, server_opts) then + return + end + elseif opts.setup['*'] then + if opts.setup['*'](server_name, server_opts) then + return + end + end + + local exists, module = pcall(require, 'lsp.' .. server_name) + if exists and module ~= nil then + local user_config = module.config(server_opts) or {} + server_opts = vim.tbl_deep_extend('force', server_opts, user_config) + end + require('lspconfig')[server_name].setup(server_opts) + end + + -- Get all the servers that are available thourgh mason-lspconfig + local have_mason, mlsp = pcall(require, 'mason-lspconfig') + local all_mslp_servers = {} + if have_mason then + all_mslp_servers = vim.tbl_keys( + require('mason-lspconfig.mappings.server').lspconfig_to_package + ) + end + + local ensure_installed = {} ---@type string[] + for server, server_opts in pairs(servers) do + if server_opts then + server_opts = server_opts == true and {} or server_opts + -- run manual setup if mason=false or if this is a server that cannot + -- be installed with mason-lspconfig + if + server_opts.mason == false + or not vim.tbl_contains(all_mslp_servers, server) + then + make_config(server) + else + ensure_installed[#ensure_installed + 1] = server + end + end + end + + if have_mason then + mlsp.setup({ + ensure_installed = ensure_installed, + handlers = { make_config }, + }) + end + + -- Enable rounded borders in :LspInfo window. + require('lspconfig.ui.windows').default_options.border = 'rounded' + end, + }, + + ----------------------------------------------------------------------------- + { + 'williamboman/mason.nvim', + cmd = 'Mason', + build = ':MasonUpdate', + keys = { { 'mm', 'Mason', desc = 'Mason' } }, + opts = { + ensure_installed = {}, + ui = { + border = 'rounded', + }, + }, + ---@param opts MasonSettings | {ensure_installed: string[]} + config = function(_, opts) + require('mason').setup(opts) + local mr = require('mason-registry') + local function ensure_installed() + for _, tool in ipairs(opts.ensure_installed) do + local p = mr.get_package(tool) + if not p:is_installed() then + p:install() + end + end + end + if mr.refresh then + mr.refresh(ensure_installed) + else + ensure_installed() + end + end, + }, + + ----------------------------------------------------------------------------- + { + 'mhartington/formatter.nvim', + event = { 'BufReadPre', 'BufNewFile' }, + dependencies = { 'williamboman/mason.nvim', 'neovim/nvim-lspconfig' }, + opts = function(_, opts) + opts = opts or {} + local defaults = { + logging = true, + log_level = vim.log.levels.WARN, + filetype = { + lua = { require('formatter.filetypes.lua').stylua }, + }, + } + opts = vim.tbl_extend('keep', opts, defaults) + return opts + end, + config = true, + }, + + ----------------------------------------------------------------------------- + { + 'dnlhc/glance.nvim', + cmd = 'Glance', + keys = { + { 'gpd', 'Glance definitions' }, + { 'gpr', 'Glance references' }, + { 'gpy', 'Glance type_definitions' }, + { 'gpi', 'Glance implementations' }, + }, + opts = function() + local actions = require('glance').actions + return { + folds = { + fold_closed = '󰅂', -- 󰅂  + fold_open = '󰅀', -- 󰅀  + folded = true, + }, + mappings = { + list = { + [''] = actions.preview_scroll_win(5), + [''] = actions.preview_scroll_win(-5), + ['sg'] = actions.jump_vsplit, + ['sv'] = actions.jump_split, + ['st'] = actions.jump_tab, + ['p'] = actions.enter_win('preview'), + }, + preview = { + ['q'] = actions.close, + ['p'] = actions.enter_win('list'), + }, + }, + } + end, + }, +} diff --git a/lua/plex/plugins/lsp/keymaps.lua b/lua/plex/plugins/lsp/keymaps.lua new file mode 100644 index 0000000..f234ebe --- /dev/null +++ b/lua/plex/plugins/lsp/keymaps.lua @@ -0,0 +1,178 @@ +-- LSP: Key-maps +-- https://github.com/plex/vim-config + +local M = {} + +---@type PluginLspKeys +M._keys = nil + +---@return (LazyKeys|{has?:string})[] +function M.get() + if M._keys then + return M._keys + end + local format = function() + require('plex.plugins.lsp.format').format({ force = true }) + end + + ---@class PluginLspKeys + -- stylua: ignore + M._keys = { + { 'gD', vim.lsp.buf.declaration, desc = 'Goto Declaration', has = 'declaration' }, + { 'gd', vim.lsp.buf.definition, desc = 'Goto Definition', has = 'definition' }, + { 'gr', vim.lsp.buf.references, desc = 'References', has = 'references' }, + { 'gy', vim.lsp.buf.type_definition, desc = 'Goto Type Definition', has = 'typeDefinition' }, + { 'gi', vim.lsp.buf.implementation, desc = 'Goto Implementation', has = 'implementation' }, + { 'gK', vim.lsp.buf.signature_help, desc = 'Signature Help', has = 'signatureHelp' }, + { 'h', vim.lsp.buf.signature_help, mode = 'i', desc = 'Signature Help', has = 'signatureHelp' }, + { ']d', M.diagnostic_goto(true), desc = 'Next Diagnostic' }, + { '[d', M.diagnostic_goto(false), desc = 'Prev Diagnostic' }, + { ']e', M.diagnostic_goto(true, 'ERROR'), desc = 'Next Error' }, + { '[e', M.diagnostic_goto(false, 'ERROR'), desc = 'Prev Error' }, + + { ',wa', vim.lsp.buf.add_workspace_folder, desc = 'Show Workspace Folders' }, + { ',wr', vim.lsp.buf.remove_workspace_folder, desc = 'Remove Workspace Folder' }, + { ',wl', 'lua =vim.lsp.buf.list_workspace_folders()', desc = 'List Workspace Folders' }, + + { 'K', function() + -- Show hover documentation or folded lines. + local winid = require('plex.lib.utils').has('nvim-ufo') + and require('ufo').peekFoldedLinesUnderCursor() or nil + if not winid then + vim.lsp.buf.hover() + end + end }, + + { 'ud', function() M.diagnostic_toggle(false) end, desc = 'Disable Diagnostics' }, + { 'uD', function() M.diagnostic_toggle(true) end, desc = 'Disable All Diagnostics' }, + + { 'cl', 'LspInfo' }, + { 'cf', format, desc = 'Format Document', has = 'formatting' }, + { 'cf', format, mode = 'x', desc = 'Format Range' }, -- , has = 'rangeFormatting' + { 'cr', vim.lsp.buf.rename, desc = 'Rename', has = 'rename' }, + { 'ce', vim.diagnostic.open_float, desc = 'Open diagnostics' }, + { 'ca', vim.lsp.buf.code_action, mode = { 'n', 'x' }, has = 'codeAction', desc = 'Code Action' }, + { 'cA', function() + vim.lsp.buf.code_action({ + context = { + only = { 'source' }, + diagnostics = {}, + }, + }) + end, desc = 'Source Action', has = 'codeAction' }, + } + return M._keys +end + +---@param method string +function M.has(buffer, method) + method = method:find('/') and method or 'textDocument/' .. method + local clients + if vim.lsp.get_clients ~= nil then + clients = vim.lsp.get_clients({ bufnr = buffer }) + else + ---@diagnostic disable-next-line: deprecated + clients = vim.lsp.get_active_clients({ bufnr = buffer }) + end + for _, client in ipairs(clients) do + if client.supports_method(method) then + return true + end + end + return false +end + +---@param buffer integer +function M.resolve(buffer) + local Keys = require('lazy.core.handler.keys') + local keymaps = {} ---@type table + + local function add(keymap) + local keys = Keys.parse(keymap) + if keys[2] == false then + keymaps[keys.id] = nil + else + keymaps[keys.id] = keys + end + end + for _, keymap in ipairs(M.get()) do + add(keymap) + end + + local opts = require('plex.lib.utils').opts('nvim-lspconfig') + local clients + if vim.lsp.get_clients ~= nil then + clients = vim.lsp.get_clients({ bufnr = buffer }) + else + ---@diagnostic disable-next-line: deprecated + clients = vim.lsp.get_active_clients({ bufnr = buffer }) + end + for _, client in ipairs(clients) do + local maps = opts.servers[client.name] and opts.servers[client.name].keys + or {} + for _, keymap in ipairs(maps) do + add(keymap) + end + end + return keymaps +end + +---@param client lsp.Client +---@param buffer integer +function M.on_attach(client, buffer) + local Keys = require('lazy.core.handler.keys') + local keymaps = M.resolve(buffer) + + for _, keys in pairs(keymaps) do + if not keys.has or M.has(buffer, keys.has) then + local opts = Keys.opts(keys) + ---@diagnostic disable-next-line: no-unknown + opts.has = nil + opts.silent = opts.silent ~= false + opts.buffer = buffer + vim.keymap.set(keys.mode or 'n', keys[1], keys[2], opts) + end + end +end + +-- Toggle diagnostics locally (false) or globally (true). +---@param global boolean +function M.diagnostic_toggle(global) + local bufnr, cmd, msg, state + if global then + bufnr = nil + state = vim.g.diagnostics_disabled + vim.g.diagnostics_disabled = not state + else + bufnr = 0 + if vim.fn.has('nvim-0.9') == 1 then + state = vim.diagnostic.is_disabled(bufnr) + else + state = vim.b.diagnostics_disabled + vim.b.diagnostics_disabled = not state + end + end + + cmd = state and 'enable' or 'disable' + msg = cmd:gsub('^%l', string.upper) .. 'd diagnostics' + if global then + msg = msg .. ' globally' + end + vim.notify(msg) + vim.schedule(function() + vim.diagnostic[cmd](bufnr) + end) +end + +---@param next boolean +---@param severity string|nil +---@return fun() +function M.diagnostic_goto(next, severity) + local go = next and vim.diagnostic.goto_next or vim.diagnostic.goto_prev + local severity_int = severity and vim.diagnostic.severity[severity] or nil + return function() + go({ severity = severity_int }) + end +end + +return M diff --git a/lua/plex/plugins/lualine.lua b/lua/plex/plugins/lualine.lua new file mode 100644 index 0000000..eca8701 --- /dev/null +++ b/lua/plex/plugins/lualine.lua @@ -0,0 +1,285 @@ +-- Plugin: Lualine +-- https://github.com/plex/vim-config + +return { + + ----------------------------------------------------------------------------- + { + 'nvim-lualine/lualine.nvim', + dependencies = { + 'nvim-lua/plenary.nvim', + 'nvim-tree/nvim-web-devicons', + }, + event = 'VeryLazy', + init = function() + vim.g.qf_disable_statusline = true + end, + opts = function() + local icons = require('plex.config').icons + local get_color = require('plex.lib.color').get_color + local fg = function(...) return { fg = get_color('fg', ...) } end + + local function filepath() + local fpath = require('plex.lib.badge').filepath(0, 3, 5) + -- % char must be escaped in statusline. + return fpath:gsub('%%', '%%%%') + end + + local function is_file_window() + return vim.bo.buftype == '' + end + + local function is_min_width(min) + if vim.o.laststatus > 2 then + return vim.o.columns > min + end + return vim.fn.winwidth(0) > min + end + + local active = { + fg = get_color('fg', { 'StatusLine' }, '#000000'), + bg = get_color('bg', { 'StatusLine' }, '#000000'), + } + local inactive = { + fg = get_color('fg', { 'StatusLineNC' }, '#666656'), + bg = get_color('bg', { 'StatusLineNC' }, '#000000'), + } + + local theme = { + normal = { + a = active, + b = active, + c = active, + x = active, + y = { + fg = active.fg, + bg = require('plex.lib.color').brightness_modifier(active.bg, -20), + }, + z = { + fg = active.fg, + bg = require('plex.lib.color').brightness_modifier(active.bg, 63), + }, + }, + inactive = { + a = inactive, b = inactive, c = inactive, + x = inactive, y = inactive, z = inactive, + }, + } + + return { + options = { + theme = theme, + globalstatus = true, + always_divide_middle = false, + disabled_filetypes = { statusline = { 'dashboard', 'alpha' } }, + component_separators = '', + section_separators = '', + }, + extensions = { + 'man', + }, + sections = { + lualine_a = { + -- Left edge block. + { + function() return '▊' end, + color = fg({'Directory'}, '#51afef'), + padding = 0, + }, + + -- Readonly/zoomed/hash symbol. + { + padding = { left = 1, right = 0 }, + cond = is_file_window, + function() + if vim.bo.buftype == '' and vim.bo.readonly then + return icons.status.filename.readonly + elseif vim.t['zoomed'] then + return icons.status.filename.zoomed + end + return '%*#' + end, + }, + + -- Buffer number. + { function() return '%n' end, cond = is_file_window, padding = 0 }, + + -- Modified symbol. + { + function() + return vim.bo.modified and icons.status.filename.modified or '' + end, + cond = is_file_window, + padding = 0, + color = { fg = get_color('bg', {'DiffDelete'}, '#ec5f67') }, + }, + }, + lualine_b = { + { + function() return require('plex.lib.badge').icon() end, + padding = { left = 1, right = 0 }, + }, + { + filepath, + padding = 1, + color = { fg = '#D7D7BC' }, + separator = '', + }, + { + 'branch', + cond = is_file_window, + icon = '', + padding = 1, + }, + { + function() return '#' .. vim.b['toggle_number'] end, + cond = function() return vim.bo.buftype == 'terminal' end, + }, + { + function() + if vim.fn.win_gettype() == 'loclist' then + return vim.fn.getloclist(0, { title = 0 }).title + end + return vim.fn.getqflist({ title = 0 }).title + end, + cond = function() return vim.bo.filetype == 'qf' end, + padding = { left = 1, right = 0 }, + }, + }, + lualine_c = { + { + 'diagnostics', + padding = { left = 1, right = 0 }, + symbols = { + error = icons.status.diagnostics.error, + warn = icons.status.diagnostics.warn, + info = icons.status.diagnostics.info, + hint = icons.status.diagnostics.hint, + }, + }, + + -- Whitespace trails + { + function() return require('plex.lib.badge').trails('␣') end, + cond = is_file_window, + padding = { left = 1, right = 0 }, + color = { fg = get_color('bg', {'Identifier'}, '#b294bb') }, + }, + + -- Start truncating here + { function() return '%<' end, padding = 0 }, + + { + 'diff', + symbols = { + added = icons.status.git.added, + modified = icons.status.git.modified, + removed = icons.status.git.removed, + }, + padding = { left = 1, right = 0 }, + cond = function() + return is_file_window() and is_min_width(70) + end, + }, + -- { + -- function() return require('nvim-navic').get_location() end, + -- padding = { left = 1, right = 0 }, + -- cond = function() + -- return is_min_width(100) + -- and package.loaded['nvim-navic'] + -- and require('nvim-navic').is_available() + -- end, + -- }, + }, + lualine_x = { + -- showcmd + { + function() return require('noice').api.status.command.get() end, + cond = function() + return package.loaded['noice'] + and require('noice').api.status.command.has() + end, + color = fg({'Statement'}), + }, + -- showmode + { + function() return require('noice').api.status.mode.get() end, + cond = function() + return package.loaded['noice'] + and require('noice').api.status.mode.has() + end, + color = fg({'Constant'}), + }, + -- search count + { + function() require('noice').api.status.search.get() end, + cond = function() + return package.loaded['noice'] + and require('noice').api.status.search.has() + end, + color = { fg = "#ff9e64" }, + }, + -- lazy.nvim updates + { + require('lazy.status').updates, + cond = require('lazy.status').has_updates, + color = fg({'Comment'}), + separator = { left = '' }, + }, + }, + lualine_y = { + { + function() return require('plex.lib.badge').filemedia('  ') end, + cond = function() return is_min_width(70) end, + separator = { left = '' }, + padding = 1, + }, + }, + lualine_z = { + { + function() + if is_file_window() then + return '%l/%2c%4p%%' + end + return '%l/%L' + end, + cond = function() + return vim.bo.filetype ~= 'TelescopePrompt' + end, + separator = { left = '' }, + padding = 1, + }, + }, + }, + inactive_sections = { + lualine_a = { + { + function() return require('plex.lib.badge').icon() end, + padding = 1, + }, + { filepath, padding = { left = 1, right = 0 } }, + { + function() + return vim.bo.modified + and vim.bo.buftype == '' + and icons.status.filename.modified + or '' + end, + cond = is_file_window, + padding = 1, + color = { fg = get_color('bg', {'DiffDelete'}, '#ec5f67') }, + }, + }, + lualine_b = {}, + lualine_c = {}, + lualine_x = {}, + lualine_y = {}, + lualine_z = { + { function() return vim.bo.filetype end, cond = is_file_window }, + } + }, + } + end, + }, + +} diff --git a/lua/plex/plugins/neo-tree.lua b/lua/plex/plugins/neo-tree.lua new file mode 100644 index 0000000..a7fdf29 --- /dev/null +++ b/lua/plex/plugins/neo-tree.lua @@ -0,0 +1,234 @@ +-- Plugin: Neo-tree +-- https://github.com/rafi/vim-config + +local winwidth = 30 + +-- Toggle width. +local toggle_width = function() + local max = winwidth * 2 + local cur_width = vim.fn.winwidth(0) + local half = math.floor((winwidth + (max - winwidth) / 2) + 0.4) + local new_width = winwidth + if cur_width == winwidth then + new_width = half + elseif cur_width == half then + new_width = max + else + new_width = winwidth + end + vim.cmd(new_width .. ' wincmd |') +end + +-- Get current opened directory from state. +---@param state table +---@return string +local function get_current_directory(state) + local node = state.tree:get_node() + local path = node.path + if node.type ~= 'directory' or not node:is_expanded() then + local path_separator = package.config:sub(1, 1) + path = path:match('(.*)' .. path_separator) + end + return path +end + +return { + + ----------------------------------------------------------------------------- + 'nvim-neo-tree/neo-tree.nvim', + dependencies = { + 'MunifTanjim/nui.nvim', + 's1n7ax/nvim-window-picker', + }, + cmd = 'Neotree', + keys = { + { + 'e', + 'Neotree filesystem left toggle dir=./', + desc = 'Explorer NeoTree Toggle', + }, + }, + deactivate = function() + vim.cmd([[Neotree close]]) + end, + init = function() + vim.g.neo_tree_remove_legacy_commands = 1 + if vim.fn.argc() == 1 then + local stat = vim.loop.fs_stat(tostring(vim.fn.argv(0))) + if stat and stat.type == 'directory' then + require('neo-tree') + end + end + end, + -- See: https://github.com/nvim-neo-tree/neo-tree.nvim + opts = { + close_if_last_window = true, + + source_selector = { + winbar = false, + show_scrolled_off_parent_node = true, + padding = { left = 1, right = 0 }, + sources = { + { source = 'filesystem', display_name = '  Files' }, --        + { source = 'buffers', display_name = '  Buffers' }, --         + { source = 'git_status', display_name = ' 󰊢 Git' }, -- 󰊢       + }, + }, + + -- event_handlers = { + -- -- Close neo-tree when opening a file. + -- { + -- event = 'file_opened', + -- handler = function() + -- require('neo-tree').close_all() + -- end, + -- }, + -- }, + + default_component_configs = { + indent = { + padding = 0, + }, + icon = { + folder_closed = '', + folder_open = '', + folder_empty = '', + folder_empty_open = '', + default = '', + }, + modified = { + symbol = '•', + }, + name = { + trailing_slash = true, + highlight_opened_files = true, -- NeoTreeFileNameOpened + use_git_status_colors = false, + }, + git_status = { + symbols = { + -- Change type + added = 'A', + deleted = 'D', + modified = 'M', + renamed = 'R', + -- Status type + untracked = 'U', + ignored = 'I', + unstaged = '', + staged = 'S', + conflict = 'C', + }, + }, + }, + window = { + width = winwidth, + mappings = { + ['q'] = 'close_window', + ['?'] = 'show_help', + [''] = 'noop', + + ['g?'] = 'show_help', + ['<2-LeftMouse>'] = 'open', + [''] = 'open_with_window_picker', + ['l'] = 'open_drop', + ['h'] = 'change_root_to_node', + ['C'] = 'cd', + ['z'] = 'close_all_nodes', + [''] = 'refresh', + + ['s'] = 'noop', + ['sv'] = 'open_split', + ['sg'] = 'open_vsplit', + ['st'] = 'open_tabnew', + + ['c'] = { 'copy', config = { show_path = 'relative' } }, + ['m'] = { 'move', config = { show_path = 'relative' } }, + ['a'] = { 'add', nowait = true, config = { show_path = 'relative' } }, + ['N'] = { 'add_directory', config = { show_path = 'relative' } }, + ['d'] = 'noop', + ['dd'] = 'delete', + ['r'] = 'rename', + ['y'] = 'copy_to_clipboard', + ['x'] = 'cut_to_clipboard', + ['P'] = 'paste_from_clipboard', + [''] = 'prev_source', + [''] = 'next_source', + + ['p'] = { + 'toggle_preview', + nowait = true, + config = { use_float = true }, + }, + + ['w'] = toggle_width, + }, + }, + filesystem = { + window = { + mappings = { + ['H'] = 'toggle_hidden', + ['/'] = 'noop', + ['f'] = 'fuzzy_finder', + ['F'] = 'filter_on_submit', + [''] = 'clear_filter', + [''] = 'clear_filter', + [''] = 'navigate_up', + ['.'] = 'set_root', + ['[g'] = 'prev_git_modified', + [']g'] = 'next_git_modified', + + ['gf'] = function(state) + require('telescope.builtin').find_files({ + cwd = get_current_directory(state), + }) + end, + + ['gr'] = function(state) + require('telescope.builtin').live_grep({ + cwd = get_current_directory(state), + }) + end, + }, + }, + group_empty_dirs = true, + use_libuv_file_watcher = true, + bind_to_cwd = false, + cwd_target = { + sidebar = 'window', + current = 'window', + }, + + filtered_items = { + visible = false, + show_hidden_count = true, + hide_dotfiles = false, + hide_gitignored = false, + hide_by_name = { + '.git', + '.hg', + '.svc', + '.DS_Store', + 'thumbs.db', + '.sass-cache', + 'node_modules', + '.pytest_cache', + '.mypy_cache', + '__pycache__', + '.stfolder', + '.stversions', + }, + never_show = {}, + }, + }, + buffers = { + bind_to_cwd = false, + window = { + mappings = { + [''] = 'navigate_up', + ['.'] = 'set_root', + ['dd'] = 'buffer_delete', + }, + }, + }, + }, +} diff --git a/lua/plex/plugins/telescope.lua b/lua/plex/plugins/telescope.lua new file mode 100644 index 0000000..ba0413c --- /dev/null +++ b/lua/plex/plugins/telescope.lua @@ -0,0 +1,512 @@ +-- Plugin: telescope.nvim +-- https://github.com/rafi/vim-config + +-- Helpers + +-- Custom actions + +local myactions = {} + +function myactions.send_to_qflist(prompt_bufnr) + require('telescope.actions').send_to_qflist(prompt_bufnr) + vim.api.nvim_command([[ botright copen ]]) +end + +function myactions.smart_send_to_qflist(prompt_bufnr) + require('telescope.actions').smart_send_to_qflist(prompt_bufnr) + vim.api.nvim_command([[ botright copen ]]) +end + +--- Scroll the results window up +---@param prompt_bufnr number: The prompt bufnr +function myactions.results_scrolling_up(prompt_bufnr) + myactions.scroll_results(prompt_bufnr, -1) +end + +--- Scroll the results window down +---@param prompt_bufnr number: The prompt bufnr +function myactions.results_scrolling_down(prompt_bufnr) + myactions.scroll_results(prompt_bufnr, 1) +end + +---@param prompt_bufnr number: The prompt bufnr +---@param direction number: 1|-1 +function myactions.scroll_results(prompt_bufnr, direction) + local status = require('telescope.state').get_status(prompt_bufnr) + local default_speed = vim.api.nvim_win_get_height(status.results_win) / 2 + local speed = status.picker.layout_config.scroll_speed or default_speed + + require('telescope.actions.set').shift_selection( + prompt_bufnr, + math.floor(speed) * direction + ) +end + +-- Custom pickers + +local plugin_directories = function(opts) + local actions = require('telescope.actions') + local utils = require('telescope.utils') + local dir = vim.fn.stdpath('data') .. '/lazy' + + opts = opts or {} + opts.cmd = vim.F.if_nil(opts.cmd, { + vim.o.shell, + '-c', + 'find ' .. vim.fn.shellescape(dir) .. ' -mindepth 1 -maxdepth 1 -type d', + }) + + local dir_len = dir:len() + opts.entry_maker = function(line) + return { + value = line, + ordinal = line, + display = line:sub(dir_len + 2), + } + end + + require('telescope.pickers') + .new(opts, { + layout_config = { + width = 0.65, + height = 0.7, + }, + prompt_title = '[ Plugin directories ]', + finder = require('telescope.finders').new_table({ + results = utils.get_os_command_output(opts.cmd), + entry_maker = opts.entry_maker, + }), + sorter = require('telescope.sorters').get_fuzzy_file(), + previewer = require('telescope.previewers.term_previewer').cat.new(opts), + attach_mappings = function(prompt_bufnr) + actions.select_default:replace(function() + local entry = require('telescope.actions.state').get_selected_entry() + actions.close(prompt_bufnr) + vim.cmd.tcd(entry.value) + end) + return true + end, + }) + :find() +end + +-- Custom window-sizes +---@param dimensions table +---@param size integer +---@return float +local function get_matched_ratio(dimensions, size) + for min_cols, scale in pairs(dimensions) do + if min_cols == 'lower' or size >= min_cols then + return math.floor(size * scale) + end + end + return dimensions.lower +end + +local function width_tiny(_, cols, _) + return get_matched_ratio({ [180] = 0.27, lower = 0.37 }, cols) +end + +local function width_small(_, cols, _) + return get_matched_ratio({ [180] = 0.4, lower = 0.5 }, cols) +end + +local function width_medium(_, cols, _) + return get_matched_ratio({ [180] = 0.5, [110] = 0.6, lower = 0.75 }, cols) +end + +local function width_large(_, cols, _) + return get_matched_ratio({ [180] = 0.7, [110] = 0.8, lower = 0.85 }, cols) +end + +-- Enable indent-guides in telescope preview +vim.api.nvim_create_autocmd('User', { + pattern = 'TelescopePreviewerLoaded', + group = vim.api.nvim_create_augroup('rafi_telescope', {}), + callback = function(args) + if args.buf ~= vim.api.nvim_win_get_buf(0) then + return + end + vim.opt_local.listchars = vim.wo.listchars .. ',tab:▏\\ ' + vim.opt_local.conceallevel = 0 + vim.opt_local.wrap = true + vim.opt_local.list = true + vim.opt_local.number = true + end, +}) + +-- Setup Telescope +-- See telescope.nvim/lua/telescope/config.lua for defaults. +return { + + ----------------------------------------------------------------------------- + { + 'nvim-telescope/telescope.nvim', + cmd = 'Telescope', + commit = vim.fn.has('nvim-0.9') == 0 and '057ee0f8783' or nil, + dependencies = { + 'nvim-lua/plenary.nvim', + 'jvgrootveld/telescope-zoxide', + 'folke/todo-comments.nvim', + 'rafi/telescope-thesaurus.nvim', + }, + config = function(_, opts) + require('telescope').setup(opts) + require('telescope').load_extension('persisted') + end, + -- stylua: ignore + keys = { + -- General pickers + { 'r', 'Telescope resume initial_mode=normal', desc = 'Resume last' }, + { 'R', 'Telescope pickers', desc = 'Pickers' }, + { 'f', 'Telescope find_files', desc = 'Find files' }, + { 'g', 'Telescope live_grep', desc = 'Grep' }, + { 'b', 'Telescope buffers show_all_buffers=true', desc = 'Buffers' }, + { 'h', 'Telescope highlights', desc = 'Highlights' }, + { 'j', 'Telescope jumplist', desc = 'Jump list' }, + { 'm', 'Telescope marks', desc = 'Marks' }, + { 'o', 'Telescope vim_options', desc = 'Neovim options' }, + { 't', 'Telescope lsp_dynamic_workspace_symbols', desc = 'Workspace symbols' }, + { 'v', 'Telescope registers', desc = 'Registers' }, + { 'u', 'Telescope spell_suggest', desc = 'Spell suggest' }, + { 's', 'Telescope persisted', desc = 'Sessions' }, + { 'x', 'Telescope oldfiles', desc = 'Old files' }, + { ';', 'Telescope command_history', desc = 'Command history' }, + { ':', 'Telescope commands', desc = 'Commands' }, + { '/', 'Telescope search_history', desc = 'Search history' }, + { '/', 'Telescope current_buffer_fuzzy_find', desc = 'Buffer find' }, + + { 'sd', 'Telescope diagnostics bufnr=0', desc = 'Document diagnostics' }, + { 'sD', 'Telescope diagnostics', desc = 'Workspace diagnostics' }, + { 'sh', 'Telescope help_tags', desc = 'Help Pages' }, + { 'sk', 'Telescope keymaps', desc = 'Key Maps' }, + { 'sm', 'Telescope man_pages', desc = 'Man Pages' }, + { 'sw', 'Telescope grep_string', desc = 'Word' }, + { 'sc', 'Telescope colorscheme', desc = 'Colorscheme' }, + { 'uC', 'Telescope colorscheme', desc = 'Colorscheme' }, + + -- LSP related + { 'dd', 'Telescope lsp_definitions', desc = 'Definitions' }, + { 'di', 'Telescope lsp_implementations', desc = 'Implementations' }, + { 'dr', 'Telescope lsp_references', desc = 'References' }, + { 'da', 'Telescope lsp_code_actions', desc = 'Code actions' }, + { 'da', ':Telescope lsp_range_code_actions', mode = 'x', desc = 'Code actions' }, + { + 'ss', + function() + require('telescope.builtin').lsp_document_symbols({ + symbols = { + 'Class', + 'Function', + 'Method', + 'Constructor', + 'Interface', + 'Module', + 'Struct', + 'Trait', + 'Field', + 'Property', + }, + }) + end, + desc = 'Goto Symbol', + }, + { + 'sS', + function() + require('telescope.builtin').lsp_dynamic_workspace_symbols({ + symbols = { + 'Class', + 'Function', + 'Method', + 'Constructor', + 'Interface', + 'Module', + 'Struct', + 'Trait', + 'Field', + 'Property', + }, + }) + end, + desc = 'Goto Symbol (Workspace)', + }, + + -- Git + { 'gs', 'Telescope git_status', desc = 'Git status' }, + { 'gr', 'Telescope git_branches', desc = 'Git branches' }, + { 'gl', 'Telescope git_commits', desc = 'Git commits' }, + { 'gL', 'Telescope git_bcommits', desc = 'Git buffer commits' }, + { 'gh', 'Telescope git_stash', desc = 'Git stashes' }, + { 'gc', 'Telescope git_bcommits_range', mode = { 'x', 'n' }, desc = 'Git bcommits range' }, + + -- Plugins + { 'n', plugin_directories, desc = 'Plugins' }, + { 'k', 'Telescope thesaurus lookup', desc = 'Thesaurus' }, + { 'w', 'ZkNotes', desc = 'Zk notes' }, + + { + 'z', + function() + require('telescope').extensions.zoxide.list({ + layout_config = { width = 0.5, height = 0.6 }, + }) + end, + desc = 'Zoxide (MRU)', + }, + + -- Find by... + { + 'gt', + function() + require('telescope.builtin').lsp_workspace_symbols({ + default_text = vim.fn.expand(''), + }) + end, + desc = 'Find symbol', + }, + { + 'gf', + function() + require('telescope.builtin').find_files({ + default_text = vim.fn.expand(''), + }) + end, + desc = 'Find file', + }, + { + 'gg', function() + require('telescope.builtin').live_grep({ + default_text = vim.fn.expand(''), + }) + end, + desc = 'Grep cursor word', + }, + { + 'gg', + function() + require('telescope.builtin').live_grep({ + default_text = require('rafi.lib.edit').get_visual_selection(), + }) + end, + mode = 'x', + desc = 'Grep cursor word', + }, + + }, + opts = function() + local transform_mod = require('telescope.actions.mt').transform_mod + local actions = require('telescope.actions') + + -- Transform to Telescope proper actions. + myactions = transform_mod(myactions) + + -- Clone the default Telescope configuration and enable hidden files. + local has_ripgrep = vim.fn.executable('rg') == 1 + local vimgrep_args = { + unpack(require('telescope.config').values.vimgrep_arguments), + } + table.insert(vimgrep_args, '--hidden') + table.insert(vimgrep_args, '--follow') + table.insert(vimgrep_args, '--no-ignore-vcs') + table.insert(vimgrep_args, '--glob') + table.insert(vimgrep_args, '!**/.git/*') + + local find_args = { + 'rg', + '--vimgrep', + '--files', + '--follow', + '--hidden', + '--no-ignore-vcs', + '--smart-case', + '--glob', + '!**/.git/*', + } + + return { + defaults = { + sorting_strategy = 'ascending', + cache_picker = { num_pickers = 3 }, + + prompt_prefix = '  ', -- ❯   + selection_caret = '▍ ', + multi_icon = ' ', + + path_display = { 'truncate' }, + file_ignore_patterns = { 'node_modules' }, + set_env = { COLORTERM = 'truecolor' }, + vimgrep_arguments = has_ripgrep and vimgrep_args or nil, + + layout_strategy = 'horizontal', + layout_config = { + prompt_position = 'top', + horizontal = { + height = 0.85, + }, + }, + + -- stylua: ignore + mappings = { + + i = { + ['jj'] = { '', type = 'command' }, + + [''] = actions.move_selection_worse, + [''] = actions.move_selection_better, + [''] = actions.results_scrolling_up, + [''] = actions.results_scrolling_down, + + [''] = myactions.smart_send_to_qflist, + + [''] = actions.cycle_history_next, + [''] = actions.cycle_history_prev, + + [''] = actions.preview_scrolling_up, + [''] = actions.preview_scrolling_down, + }, + + n = { + ['q'] = actions.close, + [''] = actions.close, + + [''] = actions.move_selection_worse, + [''] = actions.move_selection_better, + [''] = myactions.results_scrolling_up, + [''] = myactions.results_scrolling_down, + + [''] = actions.preview_scrolling_up, + [''] = actions.preview_scrolling_down, + + [''] = actions.cycle_history_next, + [''] = actions.cycle_history_prev, + + ['*'] = actions.toggle_all, + ['u'] = actions.drop_all, + ['J'] = actions.toggle_selection + actions.move_selection_next, + ['K'] = actions.toggle_selection + actions.move_selection_previous, + [' '] = { + actions.toggle_selection + actions.move_selection_next, + type = 'action', + opts = { nowait = true }, + }, + + ['sv'] = actions.select_horizontal, + ['sg'] = actions.select_vertical, + ['st'] = actions.select_tab, + + ['w'] = myactions.smart_send_to_qflist, + ['e'] = myactions.send_to_qflist, + + ['!'] = actions.edit_command_line, + + ['t'] = function(...) + return require('trouble.providers.telescope').open_with_trouble(...) + end, + + ['p'] = function() + local entry = require('telescope.actions.state').get_selected_entry() + require('rafi.lib.preview').open(entry.path) + end, + }, + + }, + }, + pickers = { + buffers = { + sort_lastused = true, + sort_mru = true, + show_all_buffers = true, + ignore_current_buffer = true, + layout_config = { width = width_large, height = 0.7 }, + mappings = { + n = { + ['dd'] = actions.delete_buffer, + }, + }, + }, + find_files = { + find_command = has_ripgrep and find_args or nil, + }, + live_grep = { + dynamic_preview_title = true, + }, + colorscheme = { + enable_preview = true, + layout_config = { preview_width = 0.7 }, + }, + highlights = { + layout_config = { preview_width = 0.7 }, + }, + vim_options = { + theme = 'dropdown', + layout_config = { width = width_medium, height = 0.7 }, + }, + command_history = { + theme = 'dropdown', + layout_config = { width = width_medium, height = 0.7 }, + }, + search_history = { + theme = 'dropdown', + layout_config = { width = width_small, height = 0.6 }, + }, + spell_suggest = { + theme = 'cursor', + layout_config = { width = width_tiny, height = 0.45 }, + }, + registers = { + theme = 'cursor', + layout_config = { width = 0.35, height = 0.4 }, + }, + oldfiles = { + theme = 'dropdown', + previewer = false, + layout_config = { width = width_medium, height = 0.7 }, + }, + lsp_definitions = { + layout_config = { width = width_large, preview_width = 0.55 }, + }, + lsp_implementations = { + layout_config = { width = width_large, preview_width = 0.55 }, + }, + lsp_references = { + layout_config = { width = width_large, preview_width = 0.55 }, + }, + lsp_code_actions = { + theme = 'cursor', + previewer = false, + layout_config = { width = 0.3, height = 0.4 }, + }, + lsp_range_code_actions = { + theme = 'cursor', + previewer = false, + layout_config = { width = 0.3, height = 0.4 }, + }, + }, + extensions = { + persisted = { + layout_config = { width = 0.55, height = 0.55 }, + }, + zoxide = { + prompt_title = '[ Zoxide directories ]', + mappings = { + default = { + action = function(selection) + vim.cmd.tcd(selection.path) + end, + after_action = function(selection) + vim.notify( + "Current working directory set to '" + .. selection.path + .. "'", + vim.log.levels.INFO + ) + end, + }, + }, + }, + }, + } + end, + }, +} diff --git a/lua/plex/plugins/treesitter.lua b/lua/plex/plugins/treesitter.lua new file mode 100644 index 0000000..a422b5a --- /dev/null +++ b/lua/plex/plugins/treesitter.lua @@ -0,0 +1,190 @@ +-- Plugins: Tree-sitter and Syntax +-- https://github.com/rafi/vim-config + +return { + + ----------------------------------------------------------------------------- + -- Vimscript syntax/indent plugins + { 'iloginow/vim-stylus', ft = 'stylus' }, + { 'chrisbra/csv.vim', ft = 'csv' }, + { 'mustache/vim-mustache-handlebars', ft = { 'mustache', 'handlebars' } }, + { 'lifepillar/pgsql.vim', ft = 'pgsql' }, + { 'MTDL9/vim-log-highlighting', ft = 'log' }, + { 'reasonml-editor/vim-reason-plus', ft = { 'reason', 'merlin' } }, + { 'vmchale/just-vim', ft = 'just' }, + + ----------------------------------------------------------------------------- + { + 'andymass/vim-matchup', + init = function() + vim.g.matchup_matchparen_offscreen = {} + end, + }, + + ----------------------------------------------------------------------------- + { + 'nvim-treesitter/nvim-treesitter', + event = { 'BufReadPost', 'BufNewFile' }, + main = 'nvim-treesitter.configs', + build = ':TSUpdate', + dependencies = { + 'nvim-treesitter/nvim-treesitter-textobjects', + { 'nvim-treesitter/nvim-treesitter-context', opts = { enable = false } }, + 'JoosepAlviste/nvim-ts-context-commentstring', + 'RRethy/nvim-treesitter-endwise', + 'windwp/nvim-ts-autotag', + 'andymass/vim-matchup', + }, + cmd = { + 'TSUpdate', + 'TSInstall', + 'TSInstallInfo', + 'TSModuleInfo', + 'TSConfigInfo', + 'TSUpdateSync', + }, + keys = { + { 'v', desc = 'Increment selection', mode = 'x' }, + { 'V', desc = 'Shrink selection', mode = 'x' }, + }, + ---@type TSConfig + ---@diagnostic disable-next-line: missing-fields + opts = { + highlight = { enable = true }, + indent = { enable = true }, + refactor = { + highlight_definitions = { enable = true }, + highlight_current_scope = { enable = true }, + }, + + -- See: https://github.com/RRethy/nvim-treesitter-endwise + endwise = { enable = true }, + + -- See: https://github.com/andymass/vim-matchup + matchup = { + enable = true, + include_match_words = true, + }, + + -- See: https://github.com/windwp/nvim-ts-autotag + autotag = { + enable = true, + -- Removed markdown due to errors + filetypes = { + 'glimmer', + 'handlebars', + 'hbs', + 'html', + 'javascript', + 'javascriptreact', + 'jsx', + 'rescript', + 'svelte', + 'tsx', + 'typescript', + 'typescriptreact', + 'vue', + 'xml', + }, + }, + + -- See: https://github.com/JoosepAlviste/nvim-ts-context-commentstring + context_commentstring = { enable = true, enable_autocmd = false }, + + incremental_selection = { + enable = true, + keymaps = { + init_selection = false, + node_incremental = 'v', + scope_incremental = false, + node_decremental = 'V', + }, + }, + + -- See: https://github.com/nvim-treesitter/nvim-treesitter-textobjects + textobjects = { + select = { + enable = true, + lookahead = true, + keymaps = { + ['af'] = '@function.outer', + ['if'] = '@function.inner', + ['ac'] = '@class.outer', + ['ic'] = '@class.inner', + ['a,'] = '@parameter.outer', + ['i,'] = '@parameter.inner', + }, + }, + move = { + enable = true, + set_jumps = true, + goto_next_start = { + ['],'] = '@parameter.inner', + }, + goto_previous_start = { + ['[,'] = '@parameter.inner', + }, + }, + swap = { + enable = true, + swap_next = { + ['>,'] = '@parameter.inner', + }, + swap_previous = { + ['<,'] = '@parameter.inner', + }, + }, + }, + + -- https://github.com/nvim-treesitter/nvim-treesitter#supported-languages + ensure_installed = { + 'bash', + 'comment', + 'css', + 'cue', + 'diff', + 'fish', + 'fennel', + 'git_config', + 'git_rebase', + 'gitcommit', + 'gitignore', + 'gitattributes', + 'graphql', + 'hcl', + 'html', + 'http', + 'java', + 'javascript', + 'jsdoc', + 'kotlin', + 'lua', + 'luadoc', + 'luap', + 'make', + 'markdown', + 'markdown_inline', + 'nix', + 'perl', + 'php', + 'pug', + 'regex', + 'ruby', + 'rust', + 'scala', + 'scss', + 'sql', + 'svelte', + 'terraform', + 'todotxt', + 'toml', + 'tsx', + 'typescript', + 'vim', + 'vimdoc', + 'vue', + 'zig', + }, + }, + }, +} diff --git a/lua/plex/plugins/ui.lua b/lua/plex/plugins/ui.lua new file mode 100644 index 0000000..77e173f --- /dev/null +++ b/lua/plex/plugins/ui.lua @@ -0,0 +1,343 @@ +-- Plugins: UI +-- https://github.com/plex/vim-config + +return { + + ----------------------------------------------------------------------------- + { 'nvim-tree/nvim-web-devicons', lazy = false }, + { 'MunifTanjim/nui.nvim', lazy = false }, + { 'plex/tabstrip.nvim', lazy = false, priority = 98, opts = true }, + + ----------------------------------------------------------------------------- + { + 'folke/noice.nvim', + event = 'VeryLazy', + dependencies = { + 'MunifTanjim/nui.nvim', + 'rcarriga/nvim-notify', + 'nvim-treesitter/nvim-treesitter', + }, + -- stylua: ignore + keys = { + { '', function() require('noice').redirect(tostring(vim.fn.getcmdline())) end, mode = 'c', desc = 'Redirect Cmdline' }, + { 'snl', function() require('noice').cmd('last') end, desc = 'Noice Last Message' }, + { 'snh', function() require('noice').cmd('history') end, desc = 'Noice History' }, + { 'sna', function() require('noice').cmd('all') end, desc = 'Noice All' }, + { '', function() if not require('noice.lsp').scroll(4) then return '' end end, silent = true, expr = true, desc = 'Scroll forward', mode = {'i', 'n', 's'} }, + { '', function() if not require('noice.lsp').scroll(-4) then return '' end end, silent = true, expr = true, desc = 'Scroll backward', mode = {'i', 'n', 's'}}, + }, + ---@type NoiceConfig + opts = { + lsp = { + override = { + ['vim.lsp.util.convert_input_to_markdown_lines'] = true, + ['vim.lsp.util.stylize_markdown'] = true, + ['cmp.entry.get_documentation'] = true, + }, + }, + messages = { + view_search = false, + }, + routes = { + -- See :h ui-messages + { + filter = { event = 'msg_show', find = '%d+L, %d+B$' }, + view = 'mini', + }, + { + filter = { event = 'msg_show', find = '^Hunk %d+ of %d+$' }, + view = 'mini', + }, + { + filter = { event = 'notify', find = '^No code actions available$' }, + view = 'mini', + }, + { + filter = { event = 'notify', find = '^No information available$' }, + opts = { skip = true }, + }, + { + filter = { event = 'msg_show', find = '^%d+ change;' }, + opts = { skip = true }, + }, + { + filter = { event = 'msg_show', find = '^%d+ %a+ lines' }, + opts = { skip = true }, + }, + { + filter = { event = 'msg_show', find = '^%d+ lines yanked$' }, + opts = { skip = true }, + }, + { + filter = { event = 'msg_show', kind = 'emsg', find = 'E490' }, + opts = { skip = true }, + }, + { + filter = { event = 'msg_show', kind = 'quickfix' }, + view = 'mini', + }, + { + filter = { event = 'msg_show', kind = 'search_count' }, + view = 'mini', + }, + { + filter = { event = 'msg_show', kind = 'wmsg' }, + view = 'mini', + }, + }, + presets = { + bottom_search = true, + command_palette = true, + long_message_to_split = true, + lsp_doc_border = true, + }, + commands = { + all = { + view = 'split', + opts = { enter = true, format = 'details' }, + filter = {}, + }, + }, + ---@type NoiceConfigViews + views = { + mini = { + zindex = 100, + win_options = { winblend = 0 }, + }, + }, + }, + }, + + ----------------------------------------------------------------------------- + { + 'stevearc/dressing.nvim', + init = function() + ---@diagnostic disable-next-line: duplicate-set-field + vim.ui.select = function(...) + require('lazy').load({ plugins = { 'dressing.nvim' } }) + return vim.ui.select(...) + end + ---@diagnostic disable-next-line: duplicate-set-field + vim.ui.input = function(...) + require('lazy').load({ plugins = { 'dressing.nvim' } }) + return vim.ui.input(...) + end + end, + }, + + ----------------------------------------------------------------------------- + { + 'SmiteshP/nvim-navic', + keys = { + { + 'tf', + function() + if vim.b.navic_winbar then + vim.b.navic_winbar = false + vim.opt_local.winbar = '' + else + vim.b.navic_winbar = true + vim.opt_local.winbar = '%#NavicIconsFile# %t %* ' + .. "%{%v:lua.require'nvim-navic'.get_location()%}" + end + end, + desc = 'Toggle structure panel', + }, + }, + init = function() + vim.g.navic_silence = true + + ---@param client lsp.Client + ---@param buffer integer + require('plex.lib.utils').on_attach(function(client, buffer) + if client.server_capabilities.documentSymbolProvider then + require('nvim-navic').attach(client, buffer) + end + end) + end, + opts = function() + return { + separator = '  ', + highlight = true, + icons = require('plex.config').icons.kinds, + } + end, + }, + + ----------------------------------------------------------------------------- + { + 'rcarriga/nvim-notify', + event = 'VeryLazy', + keys = { + { + 'un', + function() + require('notify').dismiss({ silent = true, pending = true }) + end, + desc = 'Dismiss all Notifications', + }, + }, + opts = { + timeout = 3000, + max_height = function() + return math.floor(vim.o.lines * 0.75) + end, + max_width = function() + return math.floor(vim.o.columns * 0.75) + end, + }, + init = function() + -- When noice is not enabled, install notify on VeryLazy + local Util = require('plex.lib.utils') + if not Util.has('noice.nvim') then + Util.on_very_lazy(function() + vim.notify = require('notify') + end) + end + end, + }, + + ----------------------------------------------------------------------------- + { + 'chentoast/marks.nvim', + dependencies = 'lewis6991/gitsigns.nvim', + event = 'FileType', + keys = { + { 'm/', 'MarksListAll', desc = 'Marks from all opened buffers' }, + }, + opts = { + sign_priority = { lower = 10, upper = 15, builtin = 8, bookmark = 20 }, + bookmark_1 = { sign = '󰈼' }, -- ⚐ ⚑ 󰈻 󰈼 󰈽 󰈾 󰈿 󰉀 + mappings = { + annotate = 'm', + }, + }, + }, + + ----------------------------------------------------------------------------- + { + 'lukas-reineke/indent-blankline.nvim', + event = 'FileType', + keys = { + { 'ue', 'IndentBlanklineToggle' }, + }, + opts = { + show_trailing_blankline_indent = false, + disable_with_nolist = true, + show_foldtext = false, + char_priority = 100, + show_current_context = true, + show_current_context_start = false, + filetype_exclude = { + 'lspinfo', + 'checkhealth', + 'git', + 'gitcommit', + 'help', + 'man', + 'lazy', + 'alpha', + 'dashboard', + 'terminal', + 'TelescopePrompt', + 'TelescopeResults', + 'neo-tree', + 'Outline', + 'mason', + 'Trouble', + }, + }, + }, + + ----------------------------------------------------------------------------- + { + 'tenxsoydev/tabs-vs-spaces.nvim', + event = { 'BufReadPost', 'BufNewFile' }, + config = true, + }, + + ----------------------------------------------------------------------------- + { + 't9md/vim-quickhl', + keys = { + { + 'mt', + '(quickhl-manual-this)', + mode = { 'n', 'x' }, + desc = 'Highlight word', + }, + }, + }, + + ----------------------------------------------------------------------------- + { + 'kevinhwang91/nvim-bqf', + ft = 'qf', + cmd = 'BqfAutoToggle', + event = 'QuickFixCmdPost', + opts = { + auto_resize_height = false, + func_map = { + tab = 'st', + split = 'sv', + vsplit = 'sg', + + stoggleup = 'K', + stoggledown = 'J', + stogglevm = '', + + ptoggleitem = 'p', + ptoggleauto = 'P', + ptogglemode = 'zp', + + pscrollup = '', + pscrolldown = '', + + prevfile = 'gk', + nextfile = 'gj', + + prevhist = '', + nexthist = '', + }, + preview = { + auto_preview = true, + should_preview_cb = function(bufnr) + -- file size greater than 100kb can't be previewed automatically + local filename = vim.api.nvim_buf_get_name(bufnr) + local fsize = vim.fn.getfsize(filename) + if fsize > 100 * 1024 then + return false + end + return true + end, + }, + }, + }, + + ----------------------------------------------------------------------------- + { + 'uga-rosa/ccc.nvim', + event = 'FileType', + keys = { + { 'cp', 'CccPick', desc = 'Color-picker' }, + }, + opts = { + highlighter = { + auto_enable = true, + lsp = true, + excludes = { 'lazy', 'mason', 'help', 'neo-tree' }, + }, + }, + }, + + ----------------------------------------------------------------------------- + { + 'itchyny/calendar.vim', + cmd = 'Calendar', + init = function() + vim.g.calendar_google_calendar = 1 + vim.g.calendar_google_task = 1 + vim.g.calendar_cache_directory = vim.fn.stdpath('data') .. '/calendar' + end, + }, +}