├── .editorconfig
├── .luarc.jsonc
├── .stylua.toml
├── README.md
├── after
├── ftplugin
│ ├── c.lua
│ ├── config.lua
│ ├── cpp.lua
│ ├── gitconfig.lua
│ ├── go.lua
│ ├── java.lua
│ ├── javascript.lua
│ ├── json.lua
│ ├── jsonc.lua
│ ├── lua.lua
│ ├── markdown.lua
│ ├── python.lua
│ ├── qf.lua
│ ├── rust.lua
│ ├── solidity.lua
│ ├── text.lua
│ └── vim.lua
└── lsp
│ ├── ccls.lua
│ ├── lua_ls.lua
│ └── rust_analyzer.lua
├── css
├── highlight-gh-dark.css
└── previm-gh-dark.css
├── init.lua
├── lazy-lock.json
├── lua
├── autocmd.lua
├── keymaps.lua
├── lazyplug.lua
├── lib
│ └── jsonc.lua
├── lsp
│ ├── diag.lua
│ ├── icons.lua
│ └── keymaps.lua
├── options.lua
├── plugins
│ ├── blink.lua
│ ├── cmp.lua
│ ├── conform.lua
│ ├── dap
│ │ ├── cpp_rust.lua
│ │ ├── go.lua
│ │ ├── init.lua
│ │ ├── lua.lua
│ │ ├── python.lua
│ │ └── ui.lua
│ ├── devicons
│ │ ├── init.lua
│ │ └── setup.lua
│ ├── diffview.lua
│ ├── fugitive.lua
│ ├── fzf-lua
│ │ ├── cmds.lua
│ │ ├── init.lua
│ │ ├── mappings.lua
│ │ └── setup.lua
│ ├── gitsigns.lua
│ ├── init.lua
│ ├── lsp.lua
│ ├── mini
│ │ ├── indentscope.lua
│ │ ├── init.lua
│ │ ├── statusline.lua
│ │ └── surround.lua
│ ├── oil.lua
│ ├── snacks
│ │ ├── init.lua
│ │ └── mappings.lua
│ ├── treesitter.lua
│ ├── ts-vimdoc.lua
│ └── which_key.lua
├── term.lua
└── utils.lua
└── screenshot.png
/.editorconfig:
--------------------------------------------------------------------------------
1 | # https://github.com/CppCXY/EmmyLuaCodeStyle/blob/master/lua.template.editorconfig
2 | # see https://github.com/CppCXY/EmmyLuaCodeStyle
3 | [*.lua]
4 | # [basic]
5 |
6 | # optional space/tab
7 | indent_style = space
8 | # if indent_style is space, this is valid
9 | indent_size = 2
10 | # if indent_style is tab, this is valid
11 | tab_width = 2
12 | # none/single/double
13 | quote_style = double
14 |
15 | continuation_indent = 4
16 |
17 | # this mean utf8 length , if this is 'unset' then the line width is no longer checked
18 | # this option decides when to chopdown the code
19 | max_line_length = 100
20 |
21 | # optional crlf/lf/cr/auto, if it is 'auto', in windows it is crlf other platforms are lf
22 | # in neovim the value 'auto' is not a valid option, please use 'unset'
23 | end_of_line = unset
24 |
25 | #optional keep/never/always/smart
26 | trailing_table_separator = keep
27 |
28 | # keep/remove/remove_table_only/remove_string_only
29 | call_arg_parentheses = keep
30 |
31 | detect_end_of_line = false
32 |
33 | # this will check text end with new line
34 | insert_final_newline = true
35 |
36 | # [space]
37 | space_around_table_field_list = true
38 |
39 | space_before_attribute = true
40 |
41 | space_before_function_open_parenthesis = false
42 |
43 | space_before_function_call_open_parenthesis = false
44 |
45 | space_before_closure_open_parenthesis = false
46 |
47 | # optional always/only_string/only_table/none
48 | # or true/false
49 | space_before_function_call_single_arg = true
50 |
51 | space_before_open_square_bracket = false
52 |
53 | space_inside_function_call_parentheses = false
54 |
55 | space_inside_function_param_list_parentheses = false
56 |
57 | space_inside_square_brackets = false
58 |
59 | # like t[#t+1] = 1
60 | space_around_table_append_operator = false
61 |
62 | ignore_spaces_inside_function_call = false
63 |
64 | space_before_inline_comment = 1
65 |
66 | # [operator space]
67 | space_around_math_operator = true
68 |
69 | space_after_comma = true
70 |
71 | space_after_comma_in_for_statement = true
72 |
73 | space_around_concat_operator = true
74 |
75 | # [align]
76 |
77 | align_call_args = false
78 |
79 | align_function_params = true
80 |
81 | align_continuous_inline_comment = true
82 |
83 | align_continuous_assign_statement = true
84 |
85 | align_continuous_rect_table_field = true
86 |
87 | align_if_branch = false
88 |
89 | align_array_table = true
90 |
91 | # [indent]
92 |
93 | never_indent_before_if_condition = false
94 |
95 | never_indent_comment_on_if_branch = false
96 |
97 | # [line space]
98 |
99 | # The following configuration supports four expressions
100 | # keep
101 | # fixed(n)
102 | # min(n)
103 | # max(n)
104 | # for eg. min(2)
105 |
106 | line_space_after_if_statement = keep
107 |
108 | line_space_after_do_statement = keep
109 |
110 | line_space_after_while_statement = keep
111 |
112 | line_space_after_repeat_statement = keep
113 |
114 | line_space_after_for_statement = keep
115 |
116 | line_space_after_local_or_assign_statement = keep
117 |
118 | line_space_after_function_statement = fixed(2)
119 |
120 | line_space_after_expression_statement = keep
121 |
122 | line_space_after_comment = keep
123 |
124 | # [line break]
125 | break_all_list_when_line_exceed = false
126 |
127 | auto_collapse_lines = false
128 |
129 | # [preference]
130 | ignore_space_after_colon = false
131 |
132 | remove_call_expression_list_finish_comma = false
133 |
--------------------------------------------------------------------------------
/.luarc.jsonc:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://raw.githubusercontent.com/LuaLS/vscode-lua/master/setting/schema.json",
3 | "runtime.version": "LuaJIT",
4 | "diagnostics": {
5 | "enable": true,
6 | "globals": [
7 | "vim",
8 | "describe",
9 | "pending",
10 | "it",
11 | "before_each",
12 | "after_each",
13 | ],
14 | "neededFileStatus": {
15 | "codestyle-check": "Any",
16 | },
17 | },
18 | "workspace": {
19 | "library": [
20 | "lua",
21 | "$VIMRUNTIME/lua",
22 | "${3rd}/luv/library",
23 | "$HOME/Sources/nvim/fzf-lua/lua",
24 | "$HOME/Sources/nvim/smartyank.nvim/lua",
25 | "$HOME/Sources/nvim/ts-vimdoc.nvim/lua/",
26 | "$XDG_DATA_HOME/nvim/lazy/fzf-lua/lua",
27 | "$XDG_DATA_HOME/nvim/lazy/smartyank.nvim/lua",
28 | "$XDG_DATA_HOME/nvim/lazy/ts-vimdoc.nvim/lua",
29 | "$XDG_DATA_HOME/nvim/lazy/mini.nvim/lua",
30 | "$XDG_DATA_HOME/nvim/lazy/nvim-web-devicons/lua",
31 | "$XDG_DATA_HOME/nvim/lazy/telescope.nvim/lua",
32 | ],
33 | "checkThirdParty": false,
34 | "maxPreload": 2000,
35 | "preloadFileSize": 1000,
36 | },
37 | "type": {
38 | "weakNilCheck": true,
39 | "weakUnionCheck": true,
40 | "castNumberToInteger": true,
41 | },
42 | "telemetry.enable": false,
43 | }
44 |
--------------------------------------------------------------------------------
/.stylua.toml:
--------------------------------------------------------------------------------
1 | column_width = 100
2 | line_endings = "Unix"
3 | indent_type = "Spaces"
4 | indent_width = 2
5 | quote_style = "AutoPreferDouble"
6 | call_parentheses = "Always"
7 | collapse_simple_statement = "Never"
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | 
4 |
5 |
6 |
7 | 
8 |
9 | ## What's in this repo?
10 |
11 | **My personal neovim lua config (requires neovim >= `0.11`)**
12 |
13 | - Minimum changes to default key mapping
14 | - A good selection of carefully hand-picked plugins
15 | - Lazy load plugins where possible
16 | - Which-key to rule them all
17 | - Misc utilities and goodies
18 |
19 | ## Plugins & Packages
20 |
21 | - [lazy.nvim](https://github.com/folke/lazy.nvim): lua plugin
22 | manager to auto-install and update our plugins
23 |
24 | - [mason.nvim](https://github.com/williamboman/mason.nvim):
25 | automatic installation of LSP servers using the `:Mason` command
26 |
27 | ### Basics
28 |
29 | - [smartyank.nvim](https://github.com/ibhagwan/smartyank.nvim): only pollute
30 | the clipboard when you really mean it (written by me)!
31 |
32 | - [mini.surround](https://github.com/echasnovski/mini.nvim): adds the missing
33 | operators (`ds`, `cs`, `ys`) for dealing with pairs of "surroundings"
34 | (quotes, tags, etc)
35 |
36 | ### Git
37 |
38 | - [vim-fugitive](https://github.com/tpope/vim-fugitive): git porcelain and
39 | plumbing in one by tpope, the Swiss army knife of git
40 |
41 | - [gitsigns](https://github.com/lewis6991/gitsigns.nvim): git gutter indicators
42 | and hunk management
43 |
44 | - [diffview.nvim](https://github.com/sindrets/diffview.nvim): powerful git diff/merge
45 | tool (`gd|yd` to invoke for project|dotfiles)
46 |
47 | ### Coding, completion & LSP
48 |
49 | - [blink.cmp](https://github.com/Saghen/blink.cmp): autocompletion framework
50 |
51 | - [treesitter](https://github.com/nvim-treesitter/nvim-treesitter): text
52 | parsing library, provides better syntax highlighting and text-objects for
53 | different coding languages (e.g. `yaf` yank-a-function), see
54 | [treesitter.lua](https://github.com/ibhagwan/nvim-lua/blob/main/lua/plugins/treesitter.lua)
55 | for defined text objects
56 |
57 | - [conform.nvim](https://github.com/stevearc/conform.nvim): formatter
58 | plugin where LSP formatting isn't what you need, format `js|json|html`
59 | with `prettier` or lua with `stylua` using the `gQ` mapping.
60 |
61 | - [nvim-dap](https://github.com/mfussenegger/nvim-dap):
62 | set breakpoints and debug applications using Debug Adapter Protocol (DAP)
63 |
64 | - [fidget.nvim](https://github.com/j-hui/fidget.nvim): Eye candy LSP progress
65 | indicator above the status line (top right)
66 |
67 | ### Fuzzy search & file exploration
68 |
69 | - [fzf-lua](https://github.com/ibhagwan/fzf-lua): the original, tried and
70 | tested fuzzy finder, lua plugin that does pretty much everything, written by
71 | yours truly
72 |
73 | - [snacks.nvim](https://github.com/folke/snacks.nvim): amazing set of plugins
74 | from the great @folke, image previews using the kitty protocol, picker to rival
75 | no less than our own fzf-lua :-)
76 |
77 | - [oil.nvim](https://github.com/stevearc/oil.nvim): file explorer as a neovim
78 | buffer which also replaces netrw
79 |
80 | ### Misc
81 |
82 | - [mini.nvim](https://github.com/echasnovski/mini.nvim): a must have plugin from
83 | the great @echasnovski, used for statusline, indent lines, surround and much more
84 |
85 | - [which-key](https://github.com/folke/which-key.nvim): a must plugin in every
86 | setup, when leader key (and some built-ins) sequence is pressed and times out
87 | which-key will generate a help window with your keybinds and also let you
88 | continue the sequence at your own pace
89 |
90 | - [previm](https://github.com/previm/previm): live preview markdown files in
91 | the browser with `r`
92 |
93 | - [render-markdown.nvim](https://github.com/MeanderingProgrammer/render-markdown.nvim):
94 | magical plugin rendering markdown files inside neovim
95 |
96 |
--------------------------------------------------------------------------------
/after/ftplugin/c.lua:
--------------------------------------------------------------------------------
1 | vim.bo.tabstop = 4
2 | vim.cmd [[setlocal path+=/usr/include/**,/usr/local/include/**]]
3 | -- remove {o|O} newline auto-comments
4 | vim.opt_local.formatoptions:remove("o")
5 |
--------------------------------------------------------------------------------
/after/ftplugin/config.lua:
--------------------------------------------------------------------------------
1 | vim.bo.tabstop = 4
2 | vim.bo.textwidth = 0
3 | vim.bo.wrapmargin = 0
4 |
--------------------------------------------------------------------------------
/after/ftplugin/cpp.lua:
--------------------------------------------------------------------------------
1 | vim.bo.tabstop = 4
2 | vim.cmd [[setlocal path+=/usr/include/**,/usr/local/include/**]]
3 | -- remove {o|O} newline auto-comments
4 | vim.opt_local.formatoptions:remove("o")
5 |
--------------------------------------------------------------------------------
/after/ftplugin/gitconfig.lua:
--------------------------------------------------------------------------------
1 | vim.bo.tabstop = 2
2 |
--------------------------------------------------------------------------------
/after/ftplugin/go.lua:
--------------------------------------------------------------------------------
1 | vim.bo.tabstop = 4
2 | vim.bo.expandtab = true
3 | vim.bo.copyindent = true
4 | vim.bo.preserveindent = true
5 | -- remove {o|O} newline auto-comments
6 | vim.opt_local.formatoptions:remove("o")
7 |
--------------------------------------------------------------------------------
/after/ftplugin/java.lua:
--------------------------------------------------------------------------------
1 | vim.bo.tabstop = 4
2 |
3 | if not pcall(require, "jdtls") then
4 | return
5 | end
6 |
7 | local root_dir = require("jdtls.setup").find_root({ ".git", "mvnw", "gradlew" })
8 |
9 | local config = {
10 | -- https://github.com/eclipse/eclipse.jdt.ls#running-from-the-command-line
11 | cmd = {
12 | "java",
13 | "-Declipse.application=org.eclipse.jdt.ls.core.id1",
14 | "-Dosgi.bundles.defaultStartLevel=4",
15 | "-Declipse.product=org.eclipse.jdt.ls.core.product",
16 | "-Dlog.protocol=true",
17 | "-Dlog.level=ALL",
18 | "-Xms1g",
19 | "--add-modules=ALL-SYSTEM",
20 | "--add-opens", "java.base/java.util=ALL-UNNAMED",
21 | "--add-opens", "java.base/java.lang=ALL-UNNAMED",
22 | "-jar", vim.fn.glob(vim.fn.stdpath("data") ..
23 | "/mason/packages/jdtls/plugins/org.eclipse.equinox.launcher_*.jar"),
24 | "-configuration", vim.fn.stdpath("data") .. "/mason/packages/jdtls/config_linux",
25 | "-data", (root_dir or vim.uv.cwd()) .. "/.jdtls",
26 | },
27 | root_dir = root_dir
28 | }
29 |
30 | -- `:help vim.lsp.start_client`
31 | require("jdtls").start_or_attach(config)
32 |
--------------------------------------------------------------------------------
/after/ftplugin/javascript.lua:
--------------------------------------------------------------------------------
1 | vim.bo.tabstop = 2
2 | -- remove {o|O} newline auto-comments
3 | vim.opt_local.formatoptions:remove("o")
4 |
--------------------------------------------------------------------------------
/after/ftplugin/json.lua:
--------------------------------------------------------------------------------
1 | vim.bo.tabstop = 2
2 |
--------------------------------------------------------------------------------
/after/ftplugin/jsonc.lua:
--------------------------------------------------------------------------------
1 | vim.bo.tabstop = 2
2 |
--------------------------------------------------------------------------------
/after/ftplugin/lua.lua:
--------------------------------------------------------------------------------
1 | vim.bo.tabstop = 2
2 | vim.bo.textwidth = 120
3 | -- remove {o|O} newline auto-comments
4 | vim.opt_local.formatoptions:remove("o")
5 | -- remove auto-indent after 'end|until'
6 | -- set by '/usr/share/nvim/runtime/indent/lua.vim'
7 | -- this gets overwritten, moved to autocmd
8 | -- vim.cmd[[setlocal indentkeys-=0=end,0=until]]
9 |
--------------------------------------------------------------------------------
/after/ftplugin/markdown.lua:
--------------------------------------------------------------------------------
1 | vim.bo.tabstop = 4
2 | vim.wo.spell = true
3 |
4 | -- Previm plugin
5 | vim.keymap.set({ "n", "v" }, "r", function()
6 | vim.cmd [[call previm#open(previm#make_preview_file_path("index.html"))]]
7 | end, { buffer = true, silent = true, desc = "open markdown preview (previm)" })
8 |
9 | vim.api.nvim_create_autocmd(vim.g.previm_enable_realtime == 1
10 | and { "CursorHold", "CursorHoldI", "InsertLeave", "BufWritePost" }
11 | or { "BufWritePost" },
12 | {
13 | group = vim.api.nvim_create_augroup("ibhagwan/PrevimRefresh", { clear = true }),
14 | buffer = 0,
15 | desc = "Previm refresh",
16 | callback = function()
17 | vim.cmd [[call previm#refresh()]]
18 | end,
19 | })
20 |
--------------------------------------------------------------------------------
/after/ftplugin/python.lua:
--------------------------------------------------------------------------------
1 | vim.bo.tabstop = 4
2 | vim.bo.textwidth = 0
3 | vim.bo.expandtab = true
4 | -- remove {o|O} newline auto-comments
5 | vim.opt_local.formatoptions:remove("o")
6 |
--------------------------------------------------------------------------------
/after/ftplugin/qf.lua:
--------------------------------------------------------------------------------
1 | -- Always open QF window at the bottom
2 | -- vim.cmd[[wincmd J]]
3 |
4 | -- Quit vim if the last window is qf
5 | vim.cmd [[autocmd! BufEnter if winnr('$') < 2| q | endif]]
6 |
7 | vim.wo.scrolloff = 0
8 | vim.wo.wrap = false
9 | vim.wo.number = true
10 | vim.wo.relativenumber = false
11 | vim.wo.linebreak = true
12 | vim.wo.list = false
13 | vim.wo.cursorline = true
14 | vim.wo.spell = false
15 | vim.bo.buflisted = false
16 |
17 | vim.keymap.set("n", "[-", ":colder", { buffer = true })
18 | vim.keymap.set("n", "]+", ":cnewer", { buffer = true })
19 |
--------------------------------------------------------------------------------
/after/ftplugin/rust.lua:
--------------------------------------------------------------------------------
1 | vim.bo.tabstop = 4
2 | -- remove {o|O} newline auto-comments
3 | vim.opt_local.formatoptions:remove("o")
4 |
--------------------------------------------------------------------------------
/after/ftplugin/solidity.lua:
--------------------------------------------------------------------------------
1 | vim.bo.tabstop = 4
2 |
--------------------------------------------------------------------------------
/after/ftplugin/text.lua:
--------------------------------------------------------------------------------
1 | vim.wo.spell = true
2 | vim.bo.tabstop = 4
3 | vim.bo.textwidth = 78
4 |
--------------------------------------------------------------------------------
/after/ftplugin/vim.lua:
--------------------------------------------------------------------------------
1 | vim.bo.tabstop = 2
2 | -- remove {o|O} newline auto-comments
3 | vim.opt_local.formatoptions:remove("o")
4 |
--------------------------------------------------------------------------------
/after/lsp/ccls.lua:
--------------------------------------------------------------------------------
1 | return {
2 | on_attach = function() print("ccls attached") end,
3 | init_options = {
4 | codeLens = {
5 | enabled = false,
6 | renderInline = false,
7 | localVariables = false,
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/after/lsp/lua_ls.lua:
--------------------------------------------------------------------------------
1 | -- Custom root dir function that ignores "$HOME/.git" for lua files in $HOME
2 | -- which will then run the LSP in single file mdoe, otherwise will err with:
3 | -- LSP[lua_ls] Your workspace is set to `$HOME`.
4 | -- Lua language server refused to load this directory.
5 | -- Please check your configuration.
6 | -- [learn more here](https://luals.github.io/wiki/faq#why-is-the-server-scanning-the-wrong-folder)
7 | -- Reuse ".../nvim-lspconfig/lua/lspconfig/configs/lua_ls"
8 | local root_dir = function(...)
9 | local lua_ls = require "lspconfig.configs.lua_ls".default_config
10 | local root = lua_ls.root_dir(...)
11 | -- NOTE: although returning `nil` here does nullify the "rootUri" property lua_ls still
12 | -- displays the error, I'm not sure if returning an empty string is the correct move as
13 | -- it generates "rootUri = "file://" but it does seem to quiet lua_ls and make it work
14 | -- as if it was started in single file mode
15 | return root and root ~= vim.env.HOME and root or ""
16 | end
17 |
18 | return {
19 | -- on_attach = function() print("lua_ls attached") end,
20 | root_dir = function(bufnr, cb_root_dir)
21 | local bname = vim.api.nvim_buf_get_name(bufnr)
22 | local root = root_dir(#bname > 0 and bname or vim.uv.cwd())
23 | cb_root_dir(root)
24 | end,
25 | -- uncomment to enable trace logging into:
26 | -- "~/.local/share/nvim/mason/packages/lua-language-server/libexec/log/service.log"
27 | -- cmd = { "lua-language-server", "--loglevel=trace" },
28 | settings = {
29 | Lua = {
30 | telemetry = { enable = false },
31 | runtime = { version = "LuaJIT" },
32 | -- removes the annoying "Do you need to configure your work environment as"
33 | -- when opening a lua project that doesn't have a '.luarc.json'
34 | workspace = { checkThirdParty = false },
35 | diagnostics = {
36 | globals = {
37 | "vim",
38 | "require",
39 | },
40 | },
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/after/lsp/rust_analyzer.lua:
--------------------------------------------------------------------------------
1 | return {
2 | -- on_attach = function() print("rust_analyzer attached") end,
3 | -- use nightly rustfmt if exists
4 | -- https://github.com/rust-lang/rust-analyzer/issues/3627
5 | -- https://github.com/rust-lang/rust-analyzer/blob/master/docs/user/generated_config.adoc
6 | settings = {
7 | ["rust-analyzer"] = {
8 | -- check = { command = "clippy" },
9 | -- Enable all features of a crate
10 | cargo = { features = "all" },
11 | interpret = { tests = true },
12 | rustfmt = {
13 | extraArgs = { "+nightly", },
14 | -- overrideCommand = {
15 | -- "rustup",
16 | -- "run",
17 | -- "nightly",
18 | -- "--",
19 | -- "rustfmt",
20 | -- "--edition",
21 | -- "2021",
22 | -- "--",
23 | -- },
24 | },
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/css/highlight-gh-dark.css:
--------------------------------------------------------------------------------
1 | /*!
2 | highlight.js
3 | https://highlightjs.readthedocs.io/en/latest/css-classes-reference.html
4 | NOTE: we don't use '@import' as the css needs to be copied next to
5 | the generated HTML, this css is already copied by previm
6 | */
7 |
8 | /*!
9 | Theme: Default
10 | Description: Original highlight.js style
11 | Author: (c) Ivan Sagalaev
12 | Maintainer: @highlightjs/core-team
13 | Website: https://highlightjs.org/
14 | License: see project LICENSE
15 | Touched: 2021
16 | */
17 |
18 | pre code.hljs {
19 | display: block;
20 | overflow-x: auto;
21 | padding: 0.5em;
22 | -webkit-text-size-adjust: none;
23 | }
24 |
25 | code.hljs {
26 | padding: 3px 5px;
27 | }
28 |
29 | .hljs {
30 | background: #2D333B;
31 | color: #ADBAC7;
32 | }
33 |
34 | .hljs-comment,
35 | .hljs-quote,
36 | .diff .hljs-header,
37 | .hljs-javadoc {
38 | color: #768390;
39 | font-style: italic;
40 | }
41 |
42 | .hljs-list,
43 | .hljs-subst,
44 | .hljs-operator,
45 | .hljs-keyword {
46 | color: #F47067;
47 | }
48 |
49 | .hljs-number,
50 | .hljs-literal,
51 | .hljs-variable,
52 | .hljs-template-variable,
53 | .hljs-tag .hljs-attr {
54 | color: #6CB6FF;
55 | /* color: #008080; */
56 | }
57 |
58 | .hljs-hexcolor,
59 | .hljs-number,
60 | .hljs-literal,
61 | .hljs-variable,
62 | .hljs-template-variable,
63 | .hljs-tag .hljs-attr {
64 | color: #6CB6FF;
65 | /* color: #008080; */
66 | }
67 |
68 | .hljs-tag .hljs-value,
69 | .hljs-phpdoc,
70 | .hljs-dartdoc,
71 | .tex .hljs-formula {
72 | color: #F48FB1;
73 | }
74 | .hljs-string,
75 | .hljs-doctag {
76 | color: #96D0FF;
77 | /* color: #7fdbca; */
78 | }
79 |
80 | .hljs-title,
81 | .hljs-section,
82 | .hljs-selector-id {
83 | color: #fc514e;
84 | font-weight: bold;
85 | }
86 |
87 | .hljs-subst {
88 | font-weight: normal;
89 | }
90 |
91 | .hljs-type,
92 | .hljs-class .hljs-title {
93 | color: #458;
94 | font-weight: bold;
95 | }
96 |
97 | .hljs-tag,
98 | .hljs-name,
99 | .hljs-attribute {
100 | color: #000080;
101 | font-weight: normal;
102 | }
103 |
104 | .hljs-regexp,
105 | .hljs-link {
106 | color: #009926;
107 | }
108 |
109 | .hljs-symbol,
110 | .hljs-bullet {
111 | color: #990073;
112 | }
113 |
114 | .hljs-built_in,
115 | .hljs-builtin-name {
116 | color: #6CB6FF;
117 | }
118 |
119 | .hljs-preprocessor,
120 | .hljs-pragma,
121 | .hljs-pi,
122 | .hljs-doctype,
123 | .hljs-shebang,
124 | .hljs-cdata {
125 | color: #999;
126 | font-weight: bold;
127 | }
128 |
129 | .hljs-meta {
130 | color: #999;
131 | font-weight: bold;
132 | }
133 |
134 | .hljs-deletion {
135 | background: #fdd;
136 | }
137 |
138 | .hljs-addition {
139 | background: #dfd;
140 | }
141 |
142 | .hljs-emphasis {
143 | font-style: italic;
144 | }
145 |
146 | .hljs-strong {
147 | font-weight: bold;
148 |
149 | .hljs-chunk {
150 | color: #aaa;
151 | }
152 |
--------------------------------------------------------------------------------
/css/previm-gh-dark.css:
--------------------------------------------------------------------------------
1 | #header {
2 | border-bottom: 1px solid #444C56;
3 | }
4 |
5 | #last-modified {
6 | padding-left: 10px;
7 | }
8 |
9 | html {
10 | background: #22272E;
11 | margin: 0;
12 | padding: 0;
13 | }
14 |
15 | body {
16 | font: 14px helvetica,arial,freesans,clean,sans-serif;
17 | line-height: 1.6;
18 | margin: 0 auto;
19 | padding: 20px;
20 | text-align: left;
21 | color: #ADBAC7;
22 | width: auto;
23 | max-width: 920px;
24 | }
25 |
26 | body > *:first-child {
27 | margin-top: 0 !important;
28 | }
29 | body > *:last-child {
30 | margin-bottom: 0 !important;
31 | }
32 |
33 | h1 {
34 | font-size: 28px;
35 | margin-bottom: 10px;
36 | color: #ADBAC7;
37 | }
38 |
39 | h2 {
40 | font-size: 24px;
41 | margin: 20px 0 10px;
42 | color: #ADBAC7;
43 | border-bottom: 1px solid #444C56;
44 | }
45 |
46 | h3 {
47 | font-size: 18px;
48 | margin: 20px 0 10px;
49 | }
50 |
51 | h4 {
52 | font-size: 16px;
53 | font-weight: bold;
54 | margin: 20px 0 10px;
55 | }
56 |
57 | h5 {
58 | font-size: 14px;
59 | font-weight: bold;
60 | margin: 20px 0 10px;
61 | }
62 |
63 | h6 {
64 | color: #9b9b9b;
65 | font-size: 14px;
66 | font-weight: bold;
67 | margin: 20px 0 10px;
68 | }
69 |
70 | hr {
71 | }
72 |
73 | p {
74 | margin: 15px 0;
75 | }
76 |
77 | pre, code {
78 | font: 12px 'Bitstream Vera Sans Mono','Courier',monospace;
79 | }
80 |
81 | .highlight pre, pre {
82 | background-color: #2D333B;
83 | border: 1px solid #2D333B;
84 | font-size: 13px;
85 | line-height: 19px;
86 | overflow: auto;
87 | border-radius: 3px;
88 | -moz-border-radius: 3px;
89 | -webkit-border-radius: 3px;
90 | padding: 6px 10px;
91 | }
92 |
93 | code {
94 | white-space: nowrap;
95 | border: 1px solid #2D333B;
96 | background-color: #3c434d;
97 | border-radius: 3px;
98 | -moz-border-radius: 3px;
99 | -webkit-border-radius: 3px;
100 | margin: 0 2px;
101 | padding: 0 5px;
102 | }
103 |
104 | pre code
105 | {
106 | white-space: pre;
107 | border: none;
108 | background: transparent;
109 | margin: 0;
110 | padding: 0;
111 | display: block;
112 | }
113 |
114 | a, a code {
115 | color: #539BF5;
116 | text-decoration: none;
117 | }
118 |
119 | blockquote
120 | {
121 | border-left: 4px solid #444C56;
122 | padding: 0 15px;
123 | color: #768390;
124 | margin: 15px 0;
125 | }
126 | blockquote > :first-child {
127 | margin-top: 0;
128 | }
129 | blockquote > :last-child {
130 | margin-bottom: 0;
131 | }
132 |
133 | table
134 | {
135 | font-size: 14px;
136 | border-collapse: collapse;
137 | margin: 20px 0 0;
138 | padding: 0;
139 | }
140 |
141 | table tr
142 | {
143 | border-top: 1px solid #444C56;
144 | background-color: #22272E;
145 | margin: 0;
146 | padding: 0;
147 | }
148 |
149 | table tr:nth-child(2n)
150 | {
151 | background-color: #2D333B;
152 | }
153 | table tr th[align="center"], table tr td[align="center"] {
154 | text-align: center;
155 | }
156 | table tr th[align="right"], table tr td[align="right"] {
157 | text-align: right;
158 | }
159 | table tr th, table tr td
160 | {
161 | border: 1px solid #444C56;
162 | text-align: left;
163 | margin: 0;
164 | padding: 6px 13px;
165 | }
166 |
167 | ul, ol
168 | {
169 | margin: 15px 0;
170 | }
171 |
172 | ul li, ol li
173 | {
174 | margin-top: 7px;
175 | margin-bottom: 7px;
176 | }
177 |
178 | dl {
179 | padding: 0;
180 | }
181 | dl dt {
182 | font-size: 14px;
183 | font-weight: bold;
184 | font-style: italic;
185 | padding: 0;
186 | margin: 15px 0 5px;
187 | }
188 | dl dt:first-child {
189 | padding: 0;
190 | }
191 | dl dt > :first-child {
192 | margin-top: 0;
193 | }
194 | dl dt > :last-child {
195 | margin-bottom: 0;
196 | }
197 | dl dd {
198 | margin: 0 0 15px;
199 | padding: 0 15px;
200 | }
201 | dl dd > :first-child {
202 | margin-top: 0;
203 | }
204 | dl dd > :last-child {
205 | margin-bottom: 0;
206 | }
207 | img {
208 | max-width: 100%;
209 | }
210 |
211 | .shadow {
212 | -webkit-box-shadow: 0 5px 15px #000;
213 | -moz-box-shadow: 0 5px 15px #000;
214 | box-shadow: 0 5px 15px #000;
215 | }
216 |
217 | input[type="checkbox"] + label {
218 | margin-left: 0.5ex;
219 | }
220 |
221 | @media screen and (min-width: 914px) {
222 | body {
223 | width: 854px;
224 | margin:0 auto;
225 | }
226 | }
227 | @media print {
228 | table, pre {
229 | page-break-inside: avoid;
230 | }
231 | }
232 |
233 | .code-lang {
234 | color: #eee;
235 | display: inline-block;
236 | background-color: #777;
237 | padding: 2px 4px;
238 | transform: translateY(-1em);
239 | word-break: break-all;
240 | margin-top: 6px;
241 | }
242 |
--------------------------------------------------------------------------------
/init.lua:
--------------------------------------------------------------------------------
1 | local utils = require("utils")
2 |
3 | if vim.fn.has("nvim-0.10") ~= 1 then
4 | utils.warn("This config requires neovim 0.10 and above")
5 | vim.o.loadplugins = false
6 | vim.o.termguicolors = true
7 | return
8 | end
9 |
10 | require("options")
11 | require("autocmd")
12 | require("keymaps")
13 | require("term")
14 |
15 | -- Don't load plugins as root and use a different colorscheme
16 | if not utils.is_root() then
17 | require("lazyplug")
18 | end
19 |
--------------------------------------------------------------------------------
/lazy-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "blink.cmp": { "branch": "main", "commit": "196711b89a97c953877d6c257c62f18920a970f3" },
3 | "conform.nvim": { "branch": "master", "commit": "6feb2f28f9a9385e401857b21eeac3c1b66dd628" },
4 | "diffview.nvim": { "branch": "main", "commit": "4516612fe98ff56ae0415a259ff6361a89419b0a" },
5 | "fidget.nvim": { "branch": "main", "commit": "d9ba6b7bfe29b3119a610892af67602641da778e" },
6 | "gitsigns.nvim": { "branch": "main", "commit": "8b729e489f1475615dc6c9737da917b3bc163605" },
7 | "lazy.nvim": { "branch": "main", "commit": "6c3bda4aca61a13a9c63f1c1d1b16b9d3be90d7a" },
8 | "lazydev.nvim": { "branch": "main", "commit": "2367a6c0a01eb9edb0464731cc0fb61ed9ab9d2c" },
9 | "mason-lspconfig.nvim": { "branch": "main", "commit": "d24b3f1612e53f9d54d866b16bedab51813f2bf1" },
10 | "mason.nvim": { "branch": "main", "commit": "8024d64e1330b86044fed4c8494ef3dcd483a67c" },
11 | "mini.nvim": { "branch": "main", "commit": "9688298860d8ac7d334139c5fd599edbedeff3c0" },
12 | "nvim-cmp": { "branch": "main", "commit": "b555203ce4bd7ff6192e759af3362f9d217e8c89" },
13 | "nvim-dap": { "branch": "master", "commit": "b0f983507e3702f073bfe1516846e58b56d4e42f" },
14 | "nvim-dap-python": { "branch": "master", "commit": "261ce649d05bc455a29f9636dc03f8cdaa7e0e2c" },
15 | "nvim-dap-ui": { "branch": "master", "commit": "73a26abf4941aa27da59820fd6b028ebcdbcf932" },
16 | "nvim-dap-virtual-text": { "branch": "master", "commit": "fbdb48c2ed45f4a8293d0d483f7730d24467ccb6" },
17 | "nvim-jdtls": { "branch": "master", "commit": "c23f200fee469a415c77265ca55b496feb646992" },
18 | "nvim-lspconfig": { "branch": "master", "commit": "3ea99227e316c5028f57a4d86a1a7fd01dd876d0" },
19 | "nvim-nio": { "branch": "master", "commit": "21f5324bfac14e22ba26553caf69ec76ae8a7662" },
20 | "nvim-treesitter": { "branch": "master", "commit": "42fc28ba918343ebfd5565147a42a26580579482" },
21 | "nvim-treesitter-context": { "branch": "master", "commit": "404502e607c3b309e405be9112c438c721153372" },
22 | "nvim-treesitter-textobjects": { "branch": "master", "commit": "0f051e9813a36481f48ca1f833897210dbcfffde" },
23 | "nvim-web-devicons": { "branch": "master", "commit": "1fb58cca9aebbc4fd32b086cb413548ce132c127" },
24 | "oil.nvim": { "branch": "master", "commit": "685cdb4ffa74473d75a1b97451f8654ceeab0f4a" },
25 | "one-small-step-for-vimkind": { "branch": "main", "commit": "a2adadde3bcdb56a57dbba2a1f3eb39e24771f1c" },
26 | "previm": { "branch": "master", "commit": "8d414bf9b38d2a7c65a313775e26c03a0169f67f" },
27 | "render-markdown.nvim": { "branch": "main", "commit": "df64d5d5432e13026a79384ec4e2bab185fd4eb5" },
28 | "snacks.nvim": { "branch": "main", "commit": "bc0630e43be5699bb94dadc302c0d21615421d93" },
29 | "tokyonight.nvim": { "branch": "main", "commit": "c3ab53c3f544e4a04f2a05d43451fd9bedff51b4" },
30 | "vim-fugitive": { "branch": "master", "commit": "4a745ea72fa93bb15dd077109afbb3d1809383f2" },
31 | "vim-moonfly-colors": { "branch": "master", "commit": "e356d55cedb24a6c4251d83ce28e0e2010e99d2f" },
32 | "vim-startuptime": { "branch": "master", "commit": "b6f0d93f6b8cf6eee0b4c94450198ba2d6a05ff6" },
33 | "which-key.nvim": { "branch": "main", "commit": "370ec46f710e058c9c1646273e6b225acf47cbed" }
34 | }
35 |
--------------------------------------------------------------------------------
/lua/autocmd.lua:
--------------------------------------------------------------------------------
1 | local utils = require("utils")
2 | local aucmd = vim.api.nvim_create_autocmd
3 |
4 | local function augroup(name, fnc)
5 | fnc(vim.api.nvim_create_augroup(name, { clear = true }))
6 | end
7 |
8 | if utils.is_root() then
9 | augroup("ibhagwan/SmartTextYankPost", function(g)
10 | -- highlight yanked text and copy to system clipboard
11 | -- TextYankPost is also called on deletion, limit to
12 | -- yanks via v:operator
13 | -- if we are connected over ssh also copy using OSC52
14 | aucmd("TextYankPost", {
15 | group = g,
16 | pattern = "*",
17 | -- command = "if has('clipboard') && v:operator=='y' && len(@0)>0 | "
18 | -- .. "let @+=@0 | endif | "
19 | -- .. "lua vim.highlight.on_yank{higroup='IncSearch', timeout=2000}"
20 | desc = "Copy to clipboard/tmux/OSC52",
21 | callback = function()
22 | local ok, yank_data = pcall(vim.fn.getreg, "0")
23 | local valid_yank = ok and #yank_data > 0 and vim.v.operator == "y"
24 | if valid_yank and vim.fn.has("clipboard") == 1 then
25 | pcall(vim.fn.setreg, "+", yank_data)
26 | end
27 | -- $SSH_CONNECTION doesn't pass over to
28 | -- root when using `su -`, copy indiscriminately
29 | if valid_yank and (vim.env.SSH_CONNECTION or utils.is_root()) then
30 | utils.osc52printf(yank_data)
31 | end
32 | if valid_yank and vim.env.TMUX then
33 | -- we use `-w` to also copy to client's clipboard
34 | vim.fn.system({ "tmux", "set-buffer", "-w", yank_data })
35 | end
36 | vim.highlight.on_yank({ higroup = "IncSearch", timeout = 1000 })
37 | end
38 | })
39 | end)
40 | end
41 |
42 | augroup("ibhagwan/ColorScheme", function(g)
43 | aucmd("ColorSchemePre", {
44 | group = g,
45 | callback = function(_)
46 | vim.g.fzf_colors = nil
47 | end
48 | })
49 | aucmd("ColorScheme", {
50 | group = g,
51 | callback = function(_)
52 | -- workaround for messed up block cursor on blink.sh
53 | vim.api.nvim_set_hl(0, "TermCursor", { default = false, reverse = true })
54 | -- fix 'listchars' highlight on nightfly
55 | if vim.g.colors_name == "nightfly" or "moonfly" then
56 | vim.api.nvim_set_hl(0, "Whitespace", { default = false, link = "NonText" })
57 | vim.api.nvim_set_hl(0, "FloatBorder", { default = false, link = "LineNr" })
58 | vim.api.nvim_set_hl(0, "WinSeparator", { default = false, link = "FloatBorder" })
59 | vim.api.nvim_set_hl(0, "ColorColumn", { default = false, link = "PmenuSbar" })
60 | elseif vim.g.colors_name:match("tokyonight") then
61 | vim.api.nvim_set_hl(0, "WinSeparator", { default = false, link = "FloatBorder" })
62 | end
63 | -- fzf-lua
64 | if type(vim.g.fzf_colors) == "table" then
65 | vim.g.fzf_colors = vim.tbl_deep_extend("keep",
66 | { ["bg+"] = { "bg", "Visual" } }, vim.g.fzf_colors)
67 | end
68 | vim.api.nvim_set_hl(0, "FzfLuaCursorLine", { default = false, link = "Visual" })
69 | vim.api.nvim_set_hl(0, "FzfLuaFzfCursorLine", { default = false, link = "Visual" })
70 | -- treesitter context
71 | vim.api.nvim_set_hl(0, "TreesitterContext", { default = false, link = "Visual" })
72 | vim.api.nvim_set_hl(0, "TreesitterContextBottom", { default = false, underline = true })
73 | -- render-markdown
74 | vim.api.nvim_set_hl(0, "RenderMarkdownCode", { default = false, link = "DiffChange" })
75 | vim.api.nvim_set_hl(0, "RenderMarkdownH1Bg", { default = false, link = "Visual" })
76 | vim.api.nvim_set_hl(0, "RenderMarkdownH2Bg", { default = false, link = "DiffText" })
77 | vim.api.nvim_set_hl(0, "RenderMarkdownH3Bg", { default = false, link = "DiffAdd" })
78 | vim.api.nvim_set_hl(0, "RenderMarkdownH4Bg", { default = false, link = "DiffAdd" })
79 | vim.api.nvim_set_hl(0, "RenderMarkdownH5Bg", { default = false, link = "DiffAdd" })
80 | vim.api.nvim_set_hl(0, "RenderMarkdownH6Bg", { default = false, link = "DiffAdd" })
81 | end,
82 | })
83 | end)
84 |
85 | -- disable mini.indentscope for certain filetype|buftype
86 | augroup("ibhagwan/MiniIndentscopeDisable", function(g)
87 | aucmd("BufEnter", {
88 | group = g,
89 | callback = function(_)
90 | if vim.bo.filetype == "fzf"
91 | or vim.bo.filetype == "help"
92 | or vim.bo.buftype == "nofile"
93 | or vim.bo.buftype == "terminal"
94 | then
95 | vim.b.miniindentscope_disable = true
96 | end
97 | end,
98 | })
99 | end)
100 |
101 | augroup("ibhagwan/TermOptions", function(g)
102 | aucmd("TermOpen",
103 | {
104 | group = g,
105 | command = "setlocal listchars= nonumber norelativenumber"
106 | })
107 | end)
108 |
109 | augroup("ibhagwan/ResizeWindows", function(g)
110 | aucmd("VimResized",
111 | {
112 | group = g,
113 | command = "tabdo wincmd ="
114 | })
115 | end)
116 |
117 | augroup("ibhagwan/ToggleSearchHL", function(g)
118 | aucmd("InsertEnter", {
119 | group = g,
120 | callback = function()
121 | vim.schedule(function() vim.cmd("nohlsearch") end)
122 | end
123 | })
124 | aucmd("CursorMoved", {
125 | group = g,
126 | callback = function()
127 | --[[ -- No bloat lua adpatation of: https://github.com/romainl/vim-cool
128 | local view, rpos = vim.fn.winsaveview(), vim.fn.getpos(".")
129 | assert(view.lnum == rpos[2])
130 | assert(view.col + 1 == rpos[3])
131 | -- Move the cursor to a position where (whereas in active search) pressing `n`
132 | -- brings us to the original cursor position, in a forward search / that means
133 | -- one column before the match, in a backward search ? we move one col forward
134 | vim.cmd(string.format("silent! keepjumps go%s",
135 | (vim.fn.line2byte(view.lnum) + view.col + 1 - (vim.v.searchforward == 1 and 2 or 0))))
136 | -- Attempt to goto next match, if we're in an active search cursor position
137 | -- should be equal to original cursor position
138 | local ok, _ = pcall(vim.cmd, "silent! keepjumps norm! n")
139 | local insearch = ok and (function()
140 | local npos = vim.fn.getpos(".")
141 | return npos[2] == rpos[2] and npos[3] == rpos[3]
142 | end)()
143 | -- restore original view and position
144 | vim.fn.winrestview(view)
145 | if not insearch then
146 | vim.schedule(function() vim.cmd("nohlsearch") end)
147 | end ]]
148 | if vim.v.hlsearch == 1 and vim.fn.searchcount().exact_match == 0 then
149 | vim.schedule(function() vim.cmd.nohlsearch() end)
150 | end
151 | end
152 | })
153 | end)
154 |
155 | augroup("ibhagwan/ActiveWinCursorLine", function(g)
156 | -- Highlight current line only on focused window
157 | local callback = function()
158 | local curwin = vim.api.nvim_get_current_win()
159 | for _, w in ipairs(vim.api.nvim_list_wins()) do
160 | vim.wo[w].cursorline = w == curwin
161 | end
162 | end
163 | aucmd({ "WinEnter", "BufEnter", "InsertLeave" }, { group = g, callback = callback })
164 | aucmd({ "WinLeave", "BufLeave", "InsertEnter" }, { group = g, callback = callback })
165 | end)
166 |
167 | -- goto last location when opening a buffer
168 | augroup("ibhagwan/BufLastLocation", function(g)
169 | aucmd("BufReadPost", {
170 | group = g,
171 | callback = function(e)
172 | -- skip fugitive commit message buffers
173 | local bufname = vim.api.nvim_buf_get_name(e.buf)
174 | if bufname:match("COMMIT_EDITMSG$") then return end
175 | local mark = vim.api.nvim_buf_get_mark(e.buf, '"')
176 | local line_count = vim.api.nvim_buf_line_count(e.buf)
177 | if mark[1] > 0 and mark[1] <= line_count then
178 | vim.cmd 'normal! g`"zz'
179 | end
180 | end,
181 | })
182 | end)
183 |
184 | -- auto-delete fugitive buffers
185 | augroup("ibhagwan/Fugitive", function(g)
186 | aucmd("BufReadPost", {
187 | group = g,
188 | pattern = "fugitive:*",
189 | command = "set bufhidden=delete"
190 | })
191 | end)
192 |
193 | -- Solidity abi JSON
194 | augroup("ibhagwan/SolidityABI", function(g)
195 | aucmd({ "BufRead", "BufNewFile" }, {
196 | group = g,
197 | pattern = "*.abi",
198 | command = "set filetype=jsonc"
199 | })
200 | end)
201 |
202 | -- Display help|man in vertical splits and map 'q' to quit
203 | augroup("ibhagwan/Help", function(g)
204 | local function open_vert()
205 | -- do nothing for floating windows or if this is
206 | -- the fzf-lua minimized help window (height=1)
207 | local cfg = vim.api.nvim_win_get_config(0)
208 | if cfg and (cfg.external or cfg.relative and #cfg.relative > 0)
209 | or vim.api.nvim_win_get_height(0) == 1 then
210 | return
211 | end
212 | -- do not run if Diffview is open
213 | if vim.g.diffview_nvim_loaded and
214 | require "diffview.lib".get_current_view() then
215 | return
216 | end
217 | local width = math.floor(vim.o.columns * 0.75)
218 | vim.cmd("wincmd L")
219 | vim.cmd("vertical resize " .. width)
220 | vim.keymap.set("n", "q", "q", { buffer = true })
221 | end
222 |
223 | aucmd("FileType", {
224 | group = g,
225 | pattern = "help,man",
226 | callback = open_vert,
227 | })
228 | -- we also need this auto command or help
229 | -- still opens in a split on subsequent opens
230 | aucmd("BufEnter", {
231 | group = g,
232 | pattern = "*.txt",
233 | callback = function()
234 | if vim.bo.buftype == "help" then
235 | open_vert()
236 | end
237 | end
238 | })
239 | aucmd("BufHidden", {
240 | group = g,
241 | pattern = "man://*",
242 | callback = function()
243 | if vim.bo.filetype == "man" then
244 | local bufnr = vim.api.nvim_get_current_buf()
245 | vim.defer_fn(function()
246 | if vim.api.nvim_buf_is_valid(bufnr) then
247 | vim.api.nvim_buf_delete(bufnr, { force = true })
248 | end
249 | end, 0)
250 | end
251 | end
252 | })
253 | end)
254 |
255 | -- https://vim.fandom.com/wiki/Avoid_scrolling_when_switch_buffers
256 | augroup("ibhagwan/DoNotAutoScroll", function(g)
257 | aucmd("BufLeave", {
258 | group = g,
259 | desc = "Avoid autoscroll when switching buffers",
260 | callback = function()
261 | -- at this stage, current buffer is the buffer we leave
262 | -- but the current window already changed, verify neither
263 | -- source nor destination are floating windows
264 | local from_buf = vim.api.nvim_get_current_buf()
265 | local from_win = vim.fn.bufwinid(from_buf)
266 | local to_win = vim.api.nvim_get_current_win()
267 | if not utils.win_is_float(to_win) and not utils.win_is_float(from_win) then
268 | vim.b.__VIEWSTATE = vim.fn.winsaveview()
269 | end
270 | end
271 | })
272 | aucmd("BufEnter", {
273 | group = g,
274 | desc = "Avoid autoscroll when switching buffers",
275 | callback = function()
276 | if vim.b.__VIEWSTATE then
277 | local to_win = vim.api.nvim_get_current_win()
278 | if not utils.win_is_float(to_win) then
279 | vim.fn.winrestview(vim.b.__VIEWSTATE)
280 | end
281 | vim.b.__VIEWSTATE = nil
282 | end
283 | end
284 | })
285 | end)
286 |
287 | augroup("ibhagwan/GQFormatter", function(g)
288 | aucmd({ "FileType", "LspAttach" },
289 | {
290 | group = g,
291 | callback = function(e)
292 | -- execlude diffview and vim-fugitive
293 | if vim.bo.filetype == "fugitive"
294 | or e.file:match("^fugitive:")
295 | or require("plugins.diffview")._is_open() then
296 | return
297 | end
298 | require("plugins.conform")._set_gq_keymap(e)
299 | end,
300 | })
301 | end)
302 |
303 | augroup("ibhagwan/LspAttach", function(g)
304 | aucmd({ "LspAttach" },
305 | {
306 | group = g,
307 | callback = function(_)
308 | require("lsp.keymaps").setup()
309 | end,
310 | })
311 | end)
312 |
--------------------------------------------------------------------------------
/lua/keymaps.lua:
--------------------------------------------------------------------------------
1 | local map = vim.keymap.set
2 | local utils = require("utils")
3 |
4 | map("", "R", utils.reload_config, { silent = true, desc = "reload nvim configuration" })
5 |
6 | map("", "r", function()
7 | vim.api.nvim_exec2("update", {})
8 | vim.api.nvim_exec2("so %", {})
9 | utils.info(string.format("Sourced '%s'", vim.fn.expand("%")))
10 | end, { silent = true, desc = "source current file" })
11 |
12 | -- Use ':Grep' or ':LGrep' to grep into quickfix|loclist
13 | -- without output or jumping to first match
14 | -- Use ':Grep %' to search only current file
15 | -- Use ':Grep %:h' to search the current file dir
16 | vim.cmd("command! -nargs=+ -complete=file Grep noautocmd grep! | redraw! | copen")
17 | vim.cmd("command! -nargs=+ -complete=file LGrep noautocmd lgrep! | redraw! | lopen")
18 |
19 | -- Fix common typos
20 | vim.cmd([[
21 | cnoreabbrev W! w!
22 | cnoreabbrev W1 w!
23 | cnoreabbrev w1 w!
24 | cnoreabbrev Q! q!
25 | cnoreabbrev Q1 q!
26 | cnoreabbrev q1 q!
27 | cnoreabbrev Qa! qa!
28 | cnoreabbrev Qall! qall!
29 | cnoreabbrev Wa wa
30 | cnoreabbrev Wq wq
31 | cnoreabbrev wQ wq
32 | cnoreabbrev WQ wq
33 | cnoreabbrev wq1 wq!
34 | cnoreabbrev Wq1 wq!
35 | cnoreabbrev wQ1 wq!
36 | cnoreabbrev WQ1 wq!
37 | cnoreabbrev W w
38 | cnoreabbrev Q q
39 | cnoreabbrev Qa qa
40 | cnoreabbrev Qall qall
41 | ]])
42 |
43 | -- root doesn't use plugins, use builtin FZF
44 | if require "utils".is_root() then
45 | -- vim.g.fzf_layout = { window = { ["width"] = 0.9, height = 0.6 } }
46 | map("n", "", "FZF", { desc = "FZF" })
47 | end
48 |
49 | map({ "n", "v", "i" }, "",
50 | function() require("fzf-lua").complete_path({ file_icons = true }) end,
51 | { silent = true, desc = "Fuzzy complete path" })
52 |
53 | map({ "n", "v", "i" }, "",
54 | function() require("fzf-lua").complete_line() end,
55 | { silent = true, desc = "Fuzzy complete line" })
56 |
57 | map({ "n", "v", "i" }, "",
58 | function() require("fzf-lua").spell_suggest() end,
59 | { silent = true, desc = "Fuzzy complete path" })
60 |
61 | -- to Save
62 | map({ "n", "v", "i" }, "", ":update", { silent = true, desc = "Save" })
63 |
64 | -- w!! to save with sudo
65 | map("c", "w!!", require("utils").sudo_write, { silent = true })
66 |
67 | -- Beginning and end of line in `:` command mode
68 | map("c", "", "", {})
69 | map("c", "", "", {})
70 |
71 | -- Terminal mappings
72 | map("t", "", [[]], {})
73 | map("t", "", [['"'.nr2char(getchar()).'pi']], { expr = true })
74 |
75 | -- TMUX aware navigation
76 | for _, k in ipairs({ "h", "j", "k", "l", "o" }) do
77 | map({ "n", "x", "t" }, string.format("", k), function()
78 | require("utils").tmux_aware_navigate(k, true)
79 | end, { silent = true })
80 | end
81 |
82 | -- tmux like directional window resizes
83 | map("n", "=", "=",
84 | { silent = true, desc = "normalize split layout" })
85 | map("n", "", "lua require'utils'.resize(false, -5)",
86 | { silent = true, desc = "horizontal split increase" })
87 | map("n", "", "lua require'utils'.resize(false, 5)",
88 | { silent = true, desc = "horizontal split decrease" })
89 | map("n", "", "lua require'utils'.resize(true, -5)",
90 | { silent = true, desc = "vertical split decrease" })
91 | map("n", "", "lua require'utils'.resize(true, 5)",
92 | { silent = true, desc = "vertical split increase" })
93 |
94 | -- Navigate buffers|tabs|quickfix|loclist
95 | for k, v in pairs({
96 | t = { cmd = "tab", desc = "tab" },
97 | b = not utils.__HAS_NVIM_011 and { cmd = "b", desc = "buffer" } or nil,
98 | q = not utils.__HAS_NVIM_011 and { cmd = "c", desc = "quickfix" } or nil,
99 | l = not utils.__HAS_NVIM_011 and { cmd = "l", desc = "location" } or nil,
100 | }) do
101 | map("n", "[" .. k:lower(), "" .. v.cmd .. "previous", { desc = "Previous " .. v.desc })
102 | map("n", "]" .. k:lower(), "" .. v.cmd .. "next", { desc = "Next " .. v.desc })
103 | map("n", "[" .. k:upper(), "" .. v.cmd .. "first", { desc = "First " .. v.desc })
104 | map("n", "]" .. k:upper(), "" .. v.cmd .. "last", { desc = "Last " .. v.desc })
105 | end
106 |
107 | -- Tab split acts similar to tmux
108 | map({ "n", "v" }, "ts", [[tab split]], { desc = "tab split" })
109 | map({ "n", "v" }, "tz", [[tab split]], { desc = "tab split" })
110 |
111 | -- Quickfix|loclist toggles
112 | map("n", "q", "lua require'utils'.toggle_qf('q')",
113 | { desc = "toggle quickfix list" })
114 | map("n", "Q", "lua require'utils'.toggle_qf('l')",
115 | { desc = "toggle location list" })
116 |
117 | -- shortcut to view :messages
118 | map({ "n", "v" }, "m", "messages", { desc = "open :messages" })
119 | map({ "n", "v" }, "M", [[mes clear|echo "cleared :messages"]],
120 | { desc = "clear :messages" })
121 |
122 | -- v|s act as |
123 | -- p|P paste from yank register (0)
124 | map({ "n", "v" }, "v", [["+p]], { desc = "paste AFTER from clipboard" })
125 | map({ "n", "v" }, "V", [["+P]], { desc = "paste BEFORE from clipboard" })
126 | map({ "n", "v" }, "s", [["*p]], { desc = "paste AFTER from primary" })
127 | map({ "n", "v" }, "S", [["*P]], { desc = "paste BEFORE from primary" })
128 | map({ "n", "v" }, "p", [["0p]], { desc = "paste AFTER from yank (reg:0)" })
129 | map({ "n", "v" }, "P", [["0P]], { desc = "paste BEFORE from yank (reg:0)" })
130 |
131 | -- Overloads for 'd|c' that don't pollute the unnamed registers
132 | -- map("n", "D", [["_D]], { desc = "blackhole 'D'" })
133 | -- map("n", "C", [["_C]], { desc = "blackhole 'C'" })
134 | -- map({ "n", "v" }, "c", [["_c]], { desc = "blackhole 'c'" })
135 |
136 | -- keep visual selection when (de)indenting
137 | map("v", "<", "", ">gv", {})
139 |
140 | -- Move selected lines up/down in visual mode
141 | map("x", "K", ":move '<-2gv=gv", {})
142 | map("x", "J", ":move '>+1gv=gv", {})
143 |
144 | -- Select last pasted/yanked text
145 | map("n", "g", "`[v`]", { desc = "visual select last yank/paste" })
146 |
147 | -- Keep matches center screen when cycling with n|N
148 | map("n", "n", "nzzzv", { desc = "Fwd search '/' or '?'" })
149 | map("n", "N", "Nzzzv", { desc = "Back search '/' or '?'" })
150 |
151 | -- any jump over 5 modifies the jumplist
152 | -- so we can use to jump back and forth
153 | for _, c in ipairs({
154 | { "k", "Line up" },
155 | { "j", "Line down" },
156 | }) do
157 | map("n", c[1], ([[(v:count > 5 ? "m'" . v:count : "") . '%s']]):format(c[1]),
158 | { expr = true, silent = true, desc = c[2] })
159 | end
160 |
161 | -- move along visual lines, not numbered ones
162 | -- without interferring with {count}
163 | for _, m in ipairs({ "n", "v" }) do
164 | for _, c in ipairs({
165 | { "", "k", "Visual line up" },
166 | { "", "j", "Visual line down" }
167 | }) do
168 | map(m, c[1], ([[v:count == 0 ? 'g%s' : '%s']]):format(c[2], c[2]),
169 | { expr = true, silent = true, desc = c[3] })
170 | end
171 | end
172 |
173 | -- Search and Replace
174 | -- 'c.' for word, 'c>' for WORD
175 | -- 'c.' in visual mode for selection
176 | map("n", "c.", [[:%s/\<\>//g]],
177 | { desc = "search and replace word under cursor" })
178 | map("n", "c>", [[:%s/\V//g]],
179 | { desc = "search and replace WORD under cursor" })
180 | map("x", "c.",
181 | [[:%s/\V=luaeval("require'utils'.get_visual_selection(true)")//g]], {})
182 |
183 | -- Turn off search matches with double-
184 | map("n", "", ":nohlsearch", { silent = true })
185 |
186 | -- Toggle display of richer `listchars`
187 | map("n", "'", function()
188 | if not _G._listchars then
189 | _G._listchars = vim.o.listchars
190 | vim.opt.listchars = {
191 | tab = "→ ",
192 | eol = "↲",
193 | nbsp = "␣",
194 | lead = "␣",
195 | space = "␣",
196 | trail = "•",
197 | extends = "⟩", -- »
198 | precedes = "⟨", -- «
199 | }
200 | else
201 | vim.o.listchars = _G._listchars
202 | _G._listchars = nil
203 | end
204 | end, { silent = true, desc = "toggle rich 'listchars' on/off" })
205 |
206 | -- Toggle colored column at 81
207 | map("n", "|", function()
208 | if tonumber(vim.wo.colorcolumn) then
209 | vim.g._colorcolumn = vim.wo.colorcolumn
210 | vim.wo.colorcolumn = ""
211 | else
212 | assert(tonumber(vim.g._colorcolumn))
213 | vim.wo.colorcolumn = vim.g._colorcolumn
214 | end
215 | end, { silent = true, desc = "toggle color column on/off" })
216 |
217 | -- Change current working dir (:pwd) to curent file's folder
218 | map("n", "%", [[:lua require"utils".set_cwd()]],
219 | { silent = true, desc = "smart set cwd (git|file parent)" })
220 |
221 | -- Map o & O to newline without insert mode
222 | map("n", "o",
223 | [[:call append(line("."), repeat([""], v:count1))]],
224 | { silent = true, desc = "newline below (no insert-mode)" })
225 | map("n", "O",
226 | [[:call append(line(".")-1, repeat([""], v:count1))]],
227 | { silent = true, desc = "newline above (no insert-mode)" })
228 |
--------------------------------------------------------------------------------
/lua/lazyplug.lua:
--------------------------------------------------------------------------------
1 | -- bootstrap lazy.nvim
2 | local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
3 | if not vim.uv.fs_stat(lazypath) then
4 | print("Downloading folke/lazy.nvim...")
5 | vim.fn.system({
6 | "git",
7 | "clone",
8 | "--filter=blob:none",
9 | "--single-branch",
10 | "https://github.com/folke/lazy.nvim.git",
11 | lazypath,
12 | })
13 | print("Succesfully downloaded lazy.nvim.")
14 | end
15 | vim.opt.runtimepath:prepend(lazypath)
16 |
17 | local ok, lazy = pcall(require, "lazy")
18 | if not ok then
19 | require "utils".error("Error downloading lazy.nvim")
20 | return
21 | end
22 |
23 | lazy.setup("plugins", {
24 | defaults = { lazy = true },
25 | dev = {
26 | path = "~/Sources/nvim",
27 | -- this is a blanket dev for all matching plugins since it
28 | -- doesn't check for the existence of the directory we now
29 | -- use the 'dev' property individually instead
30 | -- patterns = { "ibhagwan" },
31 | },
32 | install = { colorscheme = { "default" } },
33 | checker = { enabled = false }, -- don't auto-check for plugin updates
34 | change_detection = { enabled = false }, -- don't auto-check for config updates
35 | ui = {
36 | backdrop = 101,
37 | border = "rounded",
38 | custom_keys = {
39 | ["l"] = false,
40 | ["t"] = false,
41 | },
42 | },
43 | debug = false,
44 | })
45 |
--------------------------------------------------------------------------------
/lua/lib/jsonc.lua:
--------------------------------------------------------------------------------
1 | -- https://github.com/actboy168/json.lua
2 | -- slightly modified to only decode jsonc
3 | local type = type
4 | local next = next
5 | local error = error
6 | local tonumber = tonumber
7 | local string_char = string.char
8 | local string_byte = string.byte
9 | local string_find = string.find
10 | local string_match = string.match
11 | local string_gsub = string.gsub
12 | local string_sub = string.sub
13 | local string_format = string.format
14 |
15 | local utf8_char
16 |
17 | if _VERSION == "Lua 5.1" or _VERSION == "Lua 5.2" then
18 | local math_floor = math.floor
19 | function utf8_char(c)
20 | if c <= 0x7f then
21 | return string_char(c)
22 | elseif c <= 0x7ff then
23 | return string_char(math_floor(c / 64) + 192, c % 64 + 128)
24 | elseif c <= 0xffff then
25 | return string_char(
26 | math_floor(c / 4096) + 224,
27 | math_floor(c % 4096 / 64) + 128,
28 | c % 64 + 128
29 | )
30 | elseif c <= 0x10ffff then
31 | return string_char(
32 | math_floor(c / 262144) + 240,
33 | math_floor(c % 262144 / 4096) + 128,
34 | math_floor(c % 4096 / 64) + 128,
35 | c % 64 + 128
36 | )
37 | end
38 | error(string_format("invalid UTF-8 code '%x'", c))
39 | end
40 | else
41 | utf8_char = utf8.char
42 | end
43 |
44 | local json = {}
45 |
46 | local objectMt = {}
47 |
48 | function json.createEmptyObject()
49 | return setmetatable({}, objectMt)
50 | end
51 |
52 | function json.isObject(t)
53 | if t[1] ~= nil then
54 | return false
55 | end
56 | return next(t) ~= nil or getmetatable(t) == objectMt
57 | end
58 |
59 | if debug and debug.upvalueid then
60 | -- Generate a lightuserdata
61 | json.null = debug.upvalueid(json.createEmptyObject, 1)
62 | else
63 | json.null = function() end
64 | end
65 |
66 | local encode_escape_map = {
67 | ["\""] = "\\\"",
68 | ["\\"] = "\\\\",
69 | ["/"] = "\\/",
70 | ["\b"] = "\\b",
71 | ["\f"] = "\\f",
72 | ["\n"] = "\\n",
73 | ["\r"] = "\\r",
74 | ["\t"] = "\\t",
75 | }
76 |
77 | local decode_escape_set = {}
78 | local decode_escape_map = {}
79 | for k, v in next, encode_escape_map do
80 | decode_escape_map[v] = k
81 | decode_escape_set[string_byte(v, 2)] = true
82 | end
83 |
84 | local statusBuf
85 | local statusPos
86 | local statusTop
87 | local statusAry = {}
88 | local statusRef = {}
89 |
90 | local function find_line()
91 | local line = 1
92 | local pos = 1
93 | while true do
94 | local f, _, nl1, nl2 = string_find(statusBuf, "([\n\r])([\n\r]?)", pos)
95 | if not f then
96 | return line, statusPos - pos + 1
97 | end
98 | local newpos = f + ((nl1 == nl2 or nl2 == "") and 1 or 2)
99 | if newpos > statusPos then
100 | return line, statusPos - pos + 1
101 | end
102 | pos = newpos
103 | line = line + 1
104 | end
105 | end
106 |
107 | local function decode_error(msg)
108 | error(string_format("ERROR: %s at line %d col %d", msg, find_line()), 2)
109 | end
110 |
111 | local function get_word()
112 | return string_match(statusBuf, "^[^ \t\r\n%]},]*", statusPos)
113 | end
114 |
115 | local function skip_comment(b)
116 | if b ~= 47 --[[ '/' ]] then
117 | return
118 | end
119 | local c = string_byte(statusBuf, statusPos + 1)
120 | if c == 42 --[[ '*' ]] then
121 | -- block comment
122 | local pos = string_find(statusBuf, "*/", statusPos)
123 | if pos then
124 | statusPos = pos + 2
125 | else
126 | statusPos = #statusBuf + 1
127 | end
128 | return true
129 | elseif c == 47 --[[ '/' ]] then
130 | -- line comment
131 | local pos = string_find(statusBuf, "[\r\n]", statusPos)
132 | if pos then
133 | statusPos = pos
134 | else
135 | statusPos = #statusBuf + 1
136 | end
137 | return true
138 | end
139 | end
140 |
141 | local function next_byte()
142 | local pos = string_find(statusBuf, "[^ \t\r\n]", statusPos)
143 | if pos then
144 | statusPos = pos
145 | local b = string_byte(statusBuf, pos)
146 | if not skip_comment(b) then
147 | return b
148 | end
149 | return next_byte()
150 | end
151 | return -1
152 | end
153 |
154 | local function decode_unicode_surrogate(s1, s2)
155 | return utf8_char(0x10000 + (tonumber(s1, 16) - 0xd800) * 0x400 + (tonumber(s2, 16) - 0xdc00))
156 | end
157 |
158 | local function decode_unicode_escape(s)
159 | return utf8_char(tonumber(s, 16))
160 | end
161 |
162 | local function decode_string()
163 | local has_unicode_escape = false
164 | local has_escape = false
165 | local i = statusPos + 1
166 | while true do
167 | i = string_find(statusBuf, '[%z\1-\31\\"]', i)
168 | if not i then
169 | decode_error "expected closing quote for string"
170 | end
171 | local x = string_byte(statusBuf, i)
172 | if x < 32 then
173 | statusPos = i
174 | decode_error "control character in string"
175 | end
176 | if x == 34 --[[ '"' ]] then
177 | local s = string_sub(statusBuf, statusPos + 1, i - 1)
178 | if has_unicode_escape then
179 | s = string_gsub(string_gsub(s
180 | , "\\u([dD][89aAbB]%x%x)\\u([dD][c-fC-F]%x%x)", decode_unicode_surrogate)
181 | , "\\u(%x%x%x%x)", decode_unicode_escape)
182 | end
183 | if has_escape then
184 | s = string_gsub(s, "\\.", decode_escape_map)
185 | end
186 | statusPos = i + 1
187 | return s
188 | end
189 | --assert(x == 92 --[[ "\\" ]])
190 | local nx = string_byte(statusBuf, i + 1)
191 | if nx == 117 --[[ "u" ]] then
192 | if not string_match(statusBuf, "^%x%x%x%x", i + 2) then
193 | statusPos = i
194 | decode_error "invalid unicode escape in string"
195 | end
196 | has_unicode_escape = true
197 | i = i + 6
198 | else
199 | if not decode_escape_set[nx] then
200 | statusPos = i
201 | decode_error("invalid escape char '" .. (nx and string_char(nx) or "") .. "' in string")
202 | end
203 | has_escape = true
204 | i = i + 2
205 | end
206 | end
207 | end
208 |
209 | local function decode_number()
210 | local num, c = string_match(statusBuf, "^([0-9]+%.?[0-9]*)([eE]?)", statusPos)
211 | if not num or string_byte(num, -1) == 0x2E --[[ "." ]] then
212 | decode_error("invalid number '" .. get_word() .. "'")
213 | end
214 | if c ~= "" then
215 | num = string_match(statusBuf, "^([^eE]*[eE][-+]?[0-9]+)[ \t\r\n%]},/]", statusPos)
216 | if not num then
217 | decode_error("invalid number '" .. get_word() .. "'")
218 | end
219 | end
220 | statusPos = statusPos + #num
221 | return tonumber(num)
222 | end
223 |
224 | local function decode_number_zero()
225 | local num, c = string_match(statusBuf, "^(.%.?[0-9]*)([eE]?)", statusPos)
226 | if not num or string_byte(num, -1) == 0x2E --[[ "." ]] or string_match(statusBuf, "^.[0-9]+", statusPos) then
227 | decode_error("invalid number '" .. get_word() .. "'")
228 | end
229 | if c ~= "" then
230 | num = string_match(statusBuf, "^([^eE]*[eE][-+]?[0-9]+)[ \t\r\n%]},/]", statusPos)
231 | if not num then
232 | decode_error("invalid number '" .. get_word() .. "'")
233 | end
234 | end
235 | statusPos = statusPos + #num
236 | return tonumber(num)
237 | end
238 |
239 | local function decode_number_negative()
240 | statusPos = statusPos + 1
241 | local c = string_byte(statusBuf, statusPos)
242 | if c then
243 | if c == 0x30 then
244 | return -decode_number_zero()
245 | elseif c > 0x30 and c < 0x3A then
246 | return -decode_number()
247 | end
248 | end
249 | decode_error("invalid number '" .. get_word() .. "'")
250 | end
251 |
252 | local function decode_true()
253 | if string_sub(statusBuf, statusPos, statusPos + 3) ~= "true" then
254 | decode_error("invalid literal '" .. get_word() .. "'")
255 | end
256 | statusPos = statusPos + 4
257 | return true
258 | end
259 |
260 | local function decode_false()
261 | if string_sub(statusBuf, statusPos, statusPos + 4) ~= "false" then
262 | decode_error("invalid literal '" .. get_word() .. "'")
263 | end
264 | statusPos = statusPos + 5
265 | return false
266 | end
267 |
268 | local function decode_null()
269 | if string_sub(statusBuf, statusPos, statusPos + 3) ~= "null" then
270 | decode_error("invalid literal '" .. get_word() .. "'")
271 | end
272 | statusPos = statusPos + 4
273 | return json.null
274 | end
275 |
276 | local function decode_array()
277 | statusPos = statusPos + 1
278 | local res = {}
279 | local chr = next_byte()
280 | if chr == 93 --[[ ']' ]] then
281 | statusPos = statusPos + 1
282 | return res
283 | end
284 | statusTop = statusTop + 1
285 | statusAry[statusTop] = true
286 | statusRef[statusTop] = res
287 | return res
288 | end
289 |
290 | local function decode_object()
291 | statusPos = statusPos + 1
292 | local res = {}
293 | local chr = next_byte()
294 | if chr == 125 --[[ ']' ]] then
295 | statusPos = statusPos + 1
296 | return json.createEmptyObject()
297 | end
298 | statusTop = statusTop + 1
299 | statusAry[statusTop] = false
300 | statusRef[statusTop] = res
301 | return res
302 | end
303 |
304 | local decode_uncompleted_map = {
305 | [string_byte '"'] = decode_string,
306 | [string_byte "0"] = decode_number_zero,
307 | [string_byte "1"] = decode_number,
308 | [string_byte "2"] = decode_number,
309 | [string_byte "3"] = decode_number,
310 | [string_byte "4"] = decode_number,
311 | [string_byte "5"] = decode_number,
312 | [string_byte "6"] = decode_number,
313 | [string_byte "7"] = decode_number,
314 | [string_byte "8"] = decode_number,
315 | [string_byte "9"] = decode_number,
316 | [string_byte "-"] = decode_number_negative,
317 | [string_byte "t"] = decode_true,
318 | [string_byte "f"] = decode_false,
319 | [string_byte "n"] = decode_null,
320 | [string_byte "["] = decode_array,
321 | [string_byte "{"] = decode_object,
322 | }
323 | local function unexpected_character()
324 | decode_error("unexpected character '" .. string_sub(statusBuf, statusPos, statusPos) .. "'")
325 | end
326 | local function unexpected_eol()
327 | decode_error("unexpected character ''")
328 | end
329 |
330 | local decode_map = {}
331 | for i = 0, 255 do
332 | decode_map[i] = decode_uncompleted_map[i] or unexpected_character
333 | end
334 | decode_map[-1] = unexpected_eol
335 |
336 | local function decode()
337 | return decode_map[next_byte()]()
338 | end
339 |
340 | local function decode_item()
341 | local top = statusTop
342 | local ref = statusRef[top]
343 | if statusAry[top] then
344 | ref[#ref + 1] = decode()
345 | else
346 | local key = decode_string()
347 | if next_byte() ~= 58 --[[ ':' ]] then
348 | decode_error "expected ':'"
349 | end
350 | statusPos = statusPos + 1
351 | ref[key] = decode()
352 | end
353 | if top == statusTop then
354 | repeat
355 | local chr = next_byte()
356 | statusPos = statusPos + 1
357 | if chr == 44 --[[ "," ]] then
358 | local c = next_byte()
359 | if statusAry[statusTop] then
360 | if c ~= 93 --[[ "]" ]] then return end
361 | else
362 | if c ~= 125 --[[ "}" ]] then return end
363 | end
364 | statusPos = statusPos + 1
365 | else
366 | if statusAry[statusTop] then
367 | if chr ~= 93 --[[ "]" ]] then decode_error "expected ']' or ','" end
368 | else
369 | if chr ~= 125 --[[ "}" ]] then decode_error "expected '}' or ','" end
370 | end
371 | end
372 | statusTop = statusTop - 1
373 | until statusTop == 0
374 | end
375 | end
376 |
377 | function json.decode(str)
378 | if type(str) ~= "string" then
379 | error("expected argument of type string, got " .. type(str))
380 | end
381 | statusBuf = str
382 | statusPos = 1
383 | statusTop = 0
384 | if next_byte() == -1 then
385 | return json.null
386 | end
387 | local res = decode()
388 | while statusTop > 0 do
389 | decode_item()
390 | end
391 | if string_find(statusBuf, "[^ \t\r\n]", statusPos) then
392 | decode_error "trailing garbage"
393 | end
394 | return res
395 | end
396 |
397 | return json
398 |
--------------------------------------------------------------------------------
/lua/lsp/diag.lua:
--------------------------------------------------------------------------------
1 | -- Diag config
2 | vim.diagnostic.config({
3 | underline = true,
4 | update_in_insert = false,
5 | -- virtual_lines = require("utils").__HAS_NVIM_011 and { current_line = true } or nil,
6 | virtual_text = {
7 | spacing = 4,
8 | source = "if_many",
9 | severity = {
10 | min = vim.diagnostic.severity.HINT,
11 | },
12 | -- format = function(diagnostic)
13 | -- if diagnostic.severity == vim.diagnostic.severity.ERROR then
14 | -- return string.format('E: %s', diagnostic.message)
15 | -- end
16 | -- return ("%s"):format(diagnostic.message)
17 | -- end,
18 | },
19 | signs = {
20 | -- nvim 0.10.0 uses `nvim_buf_set_extmark`
21 | text = {
22 | [vim.diagnostic.severity.ERROR] = "", -- index:0
23 | [vim.diagnostic.severity.WARN] = "", -- index:1
24 | [vim.diagnostic.severity.INFO] = "", -- index:2
25 | [vim.diagnostic.severity.HINT] = "", -- index:3
26 | },
27 | },
28 | severity_sort = true,
29 | float = {
30 | show_header = false,
31 | source = "if_many",
32 | border = "rounded",
33 | },
34 | })
35 |
--------------------------------------------------------------------------------
/lua/lsp/icons.lua:
--------------------------------------------------------------------------------
1 | -- LSP icons
2 | local icons = {
3 | Text = "", --
4 | Method = "",
5 | Function = "",
6 | Constructor = "", --
7 | Field = "", --
8 | Variable = "", --
9 | Class = "", --
10 | Interface = "", --
11 | Module = "",
12 | Property = "",
13 | Unit = "", --
14 | Value = "",
15 | Enum = "", --
16 | EnumMember = "",
17 | Keyword = "", --
18 | Snippet = "", --
19 | Color = "", --
20 | File = "",
21 | Folder = "",
22 | Reference = "", --
23 | Constant = "", -- π
24 | Struct = "", --
25 | Event = "",
26 | Operator = "", --
27 | TypeParameter = "",
28 | }
29 |
30 | for kind, symbol in pairs(icons) do
31 | local kinds = vim.lsp.protocol.CompletionItemKind
32 | local index = kinds[kind]
33 |
34 | if index ~= nil then
35 | kinds[index] = symbol
36 | end
37 | end
38 |
--------------------------------------------------------------------------------
/lua/lsp/keymaps.lua:
--------------------------------------------------------------------------------
1 | local utils = require("utils")
2 |
3 | local map = function(mode, lhs, rhs, opts)
4 | opts = vim.tbl_extend("keep", opts, { silent = true, buffer = 0 })
5 | vim.keymap.set(mode, lhs, rhs, opts)
6 | end
7 |
8 | local setup = function()
9 | for _, k in ipairs({ "l?", "k" }) do
10 | map("n", k, function()
11 | vim.diagnostic.open_float({ buffer = 0, scope = "line", border = "rounded" })
12 | end, { desc = "show line diagnostic [LSP]" })
13 | end
14 |
15 | map("n", "lh", function()
16 | local enabled = not vim.lsp.inlay_hint.is_enabled({})
17 | vim.lsp.inlay_hint.enable(enabled)
18 | utils.info(string.format("LSP inlay hints %s.", enabled and "enabled" or "disabled"))
19 | end, { desc = "toggle inlay hints [LSP]" })
20 |
21 | map("n", "lv", function()
22 | if not vim.b._diag_is_hidden then
23 | utils.info("Diagnostic virtual text is now hidden.")
24 | vim.diagnostic.hide()
25 | else
26 | utils.info("Diagnostic virtual text is now visible.")
27 | vim.diagnostic.show()
28 | end
29 | vim.b._diag_is_hidden = not vim.b._diag_is_hidden
30 | end, { desc = "toggle virtual text [LSP]" })
31 |
32 | local wk = package.loaded["which-key"]
33 | if wk then
34 | wk.add({
35 | "lh",
36 | desc = function()
37 | local enabled = vim.lsp.inlay_hint.is_enabled({})
38 | return string.format("%s inlay hints [LSP]", enabled and "hide" or "show")
39 | end,
40 | buffer = 0,
41 | nowait = false,
42 | remap = false
43 | })
44 | wk.add({
45 | "lv",
46 | desc = function()
47 | return string.format("%s virtual text [LSP]", vim.b._diag_is_hidden and "show" or "hide")
48 | end,
49 | buffer = 0,
50 | nowait = false,
51 | remap = false
52 | })
53 | end
54 | end
55 |
56 | return { setup = setup }
57 |
--------------------------------------------------------------------------------
/lua/options.lua:
--------------------------------------------------------------------------------
1 | local utils = require("utils")
2 |
3 | vim.o.mouse = "" -- disable the mouse
4 | vim.o.termguicolors = true -- enable 24bit colors
5 |
6 | vim.o.updatetime = 250 -- decrease update time for CursorHold
7 | vim.o.fileformat = "unix" -- for EOL
8 | vim.o.switchbuf = "useopen,uselast" -- jump to already open buffers on `:cn|:cp`
9 | vim.o.fileencoding = "utf-8"
10 |
11 | vim.opt.matchpairs:append("<:>") -- add "<>" to '%'
12 |
13 | -- recursive :find in current dir
14 | vim.cmd([[set path=.,,,$PWD/**]])
15 |
16 | -- DO NOT NEED ANY OF THIS, CRUTCH THAT POULLUTES REGISTERS
17 | -- vim clipboard copies to system clipboard
18 | -- unnamed = use the * register (cmd-s paste in our term)
19 | -- unnamedplus = use the + register (cmd-v paste in our term)
20 | -- vim.o.clipboard = 'unnamedplus'
21 |
22 | -- This featire isn't fully baked yet, `wait:0` causes
23 | -- even the `:messaeges` output to not show up at all
24 | -- if utils.__HAS_NVIM_011 then
25 | -- vim.o.mopt = "wait:0,history:1000"
26 | -- end
27 |
28 | vim.o.cmdheight = 2 -- cmdline heirequire('gitsigns.config').config.baseght
29 | vim.o.cmdwinheight = math.floor(vim.o.lines / 2) -- 'q:' window height
30 | vim.o.scrolloff = 3 -- min number of lines to keep between cursor and screen edge
31 | vim.o.sidescrolloff = 5 -- min number of cols to keep between cursor and screen edge
32 | vim.o.textwidth = 99 -- max inserted text width for paste operations
33 | vim.o.number = true -- show absolute line nvim.o. at the cursor pos
34 | vim.o.relativenumber = true -- otherwise, show relative numbers in the ruler
35 | vim.o.cursorline = true -- Show a line where the current cursor is
36 | vim.o.signcolumn = "yes" -- Show sign column as first column
37 | vim.o.colorcolumn = "100" -- mark column 100
38 | vim.o.breakindent = true -- start wrapped lines indented
39 | vim.o.linebreak = true -- do not break words on line wrap
40 |
41 | -- Characters to display on ':set list',explore glyphs using:
42 | -- `xfd -fa "InputMonoNerdFont:style:Regular"` or
43 | -- `xfd -fn "-misc-fixed-medium-r-semicondensed-*-13-*-*-*-*-*-iso10646-1"`
44 | -- input special chars with the sequence followed by the hex code
45 | vim.opt.listchars = {
46 | tab = "▏ ",
47 | trail = "·",
48 | extends = "»",
49 | precedes = "«",
50 | }
51 | vim.o.list = true
52 | vim.o.showbreak = "↪ "
53 |
54 | vim.opt.diffopt:append("linematch:60") -- As suggested by `:help diffopt`
55 |
56 | -- show menu even for one item do not auto select/insert
57 | vim.opt.completeopt = {
58 | "noselect",
59 | "menu",
60 | "menuone",
61 | "popup",
62 | utils.__HAS_NVIM_011 and "fuzzy" or nil,
63 | }
64 |
65 | vim.o.pumheight = 10 -- completion menu max height
66 |
67 | vim.o.joinspaces = true -- insert spaces after '.?!' when joining lines
68 | vim.o.smartindent = true -- add depending on syntax (C/C++)
69 |
70 | vim.o.tabstop = 4 -- Tab indentation levels every two columns
71 | vim.o.shiftwidth = 0 -- Use `tabstop` value for auto-indent
72 | vim.o.shiftround = true -- Always indent/outdent to nearest tabstop
73 | vim.o.expandtab = true -- Convert all tabs that are typed into spaces
74 |
75 | vim.opt.formatoptions = vim.opt.formatoptions
76 | - "a" -- auto-formatting
77 | - "t" -- auto-wrap text using 'textwidth'
78 | + "c" -- auto-wrap comments using 'textwidth'
79 | + "q" -- allow formatting comments w/ `gq`
80 | - "o" -- auto-continue comments on pressing `o|O`
81 | + "r" -- auto-continue comments on pressing `enter`
82 | + "n" -- recognize 'formatlistpat' while formatting
83 | + "j" -- auto-remove comments when joining lines
84 | - "2" -- disable heuristics in paragraph formatting
85 |
86 | vim.o.splitbelow = true -- ':new' ':split' below current
87 | vim.o.splitright = true -- ':vnew' ':vsplit' right of current
88 |
89 | vim.o.foldenable = true -- enable folding
90 | vim.o.foldlevelstart = 99 -- open all folds by default
91 | vim.o.foldmethod = "expr" -- use treesitter for folding
92 | vim.o.foldexpr = "v:lua.vim.treesitter.foldexpr()"
93 |
94 | vim.o.undofile = false -- no undo file
95 | vim.o.hidden = true -- do not unload buffer when abandoned
96 | vim.o.confirm = true -- confirm before loss of data with `:q`
97 |
98 | vim.o.ignorecase = true -- ignore case on search
99 | vim.o.smartcase = true -- case sensitive when search includes uppercase
100 | vim.o.showmatch = true -- highlight matching [{()}]
101 | vim.o.cpoptions = vim.o.cpoptions .. "x" -- stay on search item when
102 |
103 | vim.o.writebackup = false -- do not backup file before write
104 | vim.o.swapfile = false -- no swap file
105 |
106 | --[[
107 | ShDa (viminfo for vim): session data history
108 | --------------------------------------------
109 | ! - Save and restore global variables (their names should be without lowercase letter).
110 | ' - Specify the maximum number of marked files remembered. It also saves the jump list and the change list.
111 | < - Maximum of lines saved for each register. All the lines are saved if this is not included, <0 to disable pessistent registers.
112 | % - Save and restore the buffer list. You can specify the maximum number of buffer stored with a number.
113 | / or : - Number of search patterns and entries from the command-line history saved. vim.o.history is used if it’s not specified.
114 | f - Store file (uppercase) marks, use 'f0' to disable.
115 | s - Specify the maximum size of an item’s content in KiB (kilobyte).
116 | For the viminfo file, it only applies to register.
117 | For the shada file, it applies to all items except for the buffer list and header.
118 | h - Disable the effect of 'hlsearch' when loading the shada file.
119 |
120 | :oldfiles - all files with a mark in the shada file
121 | :rshada - read the shada file (:rviminfo for vim)
122 | :wshada - write the shada file (:wrviminfo for vim)
123 | ]]
124 | vim.o.shada = [[!,'100,<0,s100,h]]
125 | vim.o.sessionoptions = "blank,buffers,curdir,folds,help,tabpages,winsize"
126 |
127 | -- use ':grep' to send resulsts to quickfix
128 | -- use ':lgrep' to send resulsts to loclist
129 | if vim.fn.executable("rg") == 1 then
130 | vim.o.grepprg = "rg --vimgrep --no-heading --smart-case --hidden"
131 | vim.o.grepformat = "%f:%l:%c:%m"
132 | end
133 |
134 | -- Disable providers we do not care a about
135 | vim.g.loaded_python_provider = 0
136 | vim.g.loaded_ruby_provider = 0
137 | vim.g.loaded_perl_provider = 0
138 | vim.g.loaded_node_provider = 0
139 |
140 | -- Disable some in built plugins completely
141 | local disabled_built_ins = {
142 | "netrw",
143 | "netrwPlugin",
144 | "netrwSettings",
145 | "netrwFileHandlers",
146 | "gzip",
147 | "zip",
148 | "zipPlugin",
149 | "tar",
150 | "tarPlugin",
151 | "getscript",
152 | "getscriptPlugin",
153 | "vimball",
154 | "vimballPlugin",
155 | "2html_plugin",
156 | "logipat",
157 | "rrhelper",
158 | "spellfile_plugin",
159 | -- 'matchit',
160 | -- 'matchparen',
161 | }
162 | -- disable default fzf plugin if not
163 | -- root since we will be using fzf-lua
164 | if utils.is_root() and vim.uv.fs_stat("/usr/share/nvim/runtime/plugin/fzf.vim") then
165 | vim.opt.runtimepath:append("/usr/share/nvim/runtime")
166 | else
167 | -- table.insert(disabled_built_ins, "fzf")
168 | end
169 | for _, plugin in pairs(disabled_built_ins) do
170 | vim.g["loaded_" .. plugin] = 1
171 | end
172 |
173 | vim.g.markdown_fenced_languages = {
174 | "vim",
175 | "lua",
176 | "cpp",
177 | "sql",
178 | "python",
179 | "bash=sh",
180 | "console=sh",
181 | "javascript",
182 | "typescript",
183 | "js=javascript",
184 | "ts=typescript",
185 | "yaml",
186 | "json",
187 | }
188 |
189 | -- Map leader to
190 | vim.g.mapleader = " "
191 | vim.g.maplocalleader = " "
192 |
193 | -- MacOS clipboard
194 | if utils.is_darwin() then
195 | vim.g.clipboard = {
196 | name = "macOS-clipboard",
197 | copy = {
198 | ["+"] = "pbcopy",
199 | ["*"] = "pbcopy",
200 | },
201 | paste = {
202 | ["+"] = "pbpaste",
203 | ["*"] = "pbpaste",
204 | },
205 | }
206 | end
207 |
208 | -- OSC52 clipboard over ssh
209 | if vim.env.SSH_TTY then
210 | vim.g.clipboard = {
211 | name = "OSC 52",
212 | copy = {
213 | ["+"] = require("vim.ui.clipboard.osc52").copy("+"),
214 | ["*"] = require("vim.ui.clipboard.osc52").copy("*"),
215 | },
216 | paste = {
217 | ["+"] = require("vim.ui.clipboard.osc52").paste("+"),
218 | ["*"] = require("vim.ui.clipboard.osc52").paste("*"),
219 | },
220 | }
221 | end
222 |
--------------------------------------------------------------------------------
/lua/plugins/blink.lua:
--------------------------------------------------------------------------------
1 | local M = {
2 | "saghen/blink.cmp",
3 | enabled = require("utils").USE_BLINK_CMP,
4 | build = "cargo +nightly build --release",
5 | event = { "InsertEnter", "CmdLineEnter" },
6 | opts = {
7 | sources = {
8 | default = { "lazydev", "lsp", "path", "snippets", "buffer" },
9 | providers = {
10 | lazydev = {
11 | name = "LazyDev",
12 | module = "lazydev.integrations.blink",
13 | -- make lazydev completions top priority (see `:h blink.cmp`)
14 | score_offset = 100,
15 | },
16 | },
17 | },
18 | keymap = {
19 | [""] = { "accept", "fallback" },
20 | -- [""] = { "hide", "fallback" },
21 | -- [""] = { "cancel", "fallback" },
22 | [""] = { "select_prev", "fallback" },
23 | [""] = { "select_next", "fallback" },
24 | [""] = { "cancel", "show", "fallback" },
25 | [""] = { "select_prev", "fallback" },
26 | [""] = { "select_next", "fallback" },
27 | [""] = { "select_and_accept" },
28 | [""] = { "show", "show_documentation", "hide_documentation" },
29 | [""] = { "select_next", "snippet_forward", "fallback" },
30 | [""] = { "select_prev", "snippet_backward", "fallback" },
31 | [""] = { "scroll_documentation_up", "fallback" },
32 | [""] = { "scroll_documentation_down", "fallback" },
33 | },
34 | cmdline = {
35 | enabled = true,
36 | completion = {
37 | menu = { auto_show = true },
38 | list = { selection = { preselect = false, auto_insert = true } },
39 | },
40 | keymap = {
41 | [""] = { "accept", "fallback" },
42 | [""] = { "hide", "fallback" },
43 | [""] = { "select_next", "fallback" },
44 | [""] = { "select_prev", "fallback" },
45 | [""] = { "cancel", "fallback" },
46 | [""] = { "select_and_accept" },
47 | }
48 | },
49 | completion = {
50 | list = { selection = { preselect = false, auto_insert = true } },
51 | trigger = { show_in_snippet = false },
52 | accept = {
53 | create_undo_point = true,
54 | auto_brackets = { enabled = true },
55 | },
56 | menu = {
57 | draw = {
58 | treesitter = { "lsp" },
59 | columns = function(ctx)
60 | local ret = { { "kind_icon" }, { "label", "label_description", gap = 1 } } -- default
61 | -- Add kind, source to INSERT mode
62 | if ctx.mode ~= "cmdline" then
63 | table.insert(ret, { "kind", "source_name", gap = 1 })
64 | end
65 | return ret
66 | end,
67 | }
68 | },
69 | documentation = {
70 | auto_show = true,
71 | auto_show_delay_ms = 100,
72 | },
73 | ghost_text = { enabled = true },
74 | },
75 | signature = { enabled = true }
76 | },
77 | }
78 |
79 | return M
80 |
--------------------------------------------------------------------------------
/lua/plugins/cmp.lua:
--------------------------------------------------------------------------------
1 | local M = {
2 | "hrsh7th/nvim-cmp",
3 | enabled = not require("utils").USE_BLINK_CMP,
4 | event = { "InsertEnter", "CmdLineEnter" },
5 | dependencies = {
6 | "hrsh7th/cmp-path",
7 | "hrsh7th/cmp-buffer",
8 | "hrsh7th/cmp-cmdline",
9 | "hrsh7th/cmp-nvim-lsp",
10 | "hrsh7th/cmp-nvim-lua",
11 | },
12 | }
13 |
14 | local winopts = {
15 | -- NOT REQUIRED
16 | -- Set left|right border chars to invisible spaces for scollbar
17 | -- border = { "", "", "", "\xc2\xa0", "", "", "", "\xc2\xa0" },
18 | winhighlight = "Normal:Pmenu,FloatBorder:Pmenu,CursorLine:PmenuSel,Search:None",
19 | }
20 |
21 | M.config = function()
22 | local cmp = require("cmp")
23 |
24 | cmp.setup {
25 | snippet = {
26 | -- must use a snippet engine
27 | expand = function(args)
28 | vim.snippet.expand(args.body)
29 | end,
30 | },
31 |
32 | window = {
33 | completion = winopts,
34 | documentation = winopts,
35 | -- completion = cmp.config.window.bordered(),
36 | -- documentation = cmp.config.window.bordered(),
37 | },
38 |
39 | completion = {
40 | -- start completion immediately
41 | keyword_length = 1,
42 | },
43 |
44 | sources = {
45 | { name = "nvim_lsp" },
46 | { name = "nvim_lua" },
47 | { name = "path" },
48 | { name = "buffer" },
49 | {
50 | name = "lazydev",
51 | group_index = 0, -- set group index to 0 to skip loading LuaLS completions
52 | },
53 | },
54 |
55 | ---@diagnostic disable-next-line: missing-fields
56 | view = { entries = { follow_cursor = true } },
57 |
58 | -- we use 'comleteopt=...,noselect' but we still want cmp to autoselect
59 | -- an item if recommended by the LSP server (try with gopls, rust_analyzer)
60 | -- uncomment to disable
61 | -- preselect = cmp.PreselectMode.None,
62 |
63 | mapping = {
64 | [""] = cmp.mapping(cmp.mapping.select_prev_item(), { "i" }),
65 | [""] = cmp.mapping(cmp.mapping.select_next_item(), { "i" }),
66 | [""] = cmp.mapping(cmp.mapping.select_prev_item(), { "i", "c" }),
67 | [""] = cmp.mapping(cmp.mapping.select_next_item(), { "i", "c" }),
68 | [""] = cmp.mapping(cmp.mapping.select_prev_item(), { "i", "c" }),
69 | [""] = cmp.mapping(cmp.mapping.select_next_item(), { "i", "c" }),
70 | [""] = cmp.mapping(cmp.mapping.select_prev_item(), { "i", "c" }),
71 | [""] = cmp.mapping(cmp.mapping.select_next_item(), { "i", "c" }),
72 | [""] = cmp.mapping(cmp.mapping.scroll_docs(-4), { "i", "c" }),
73 | [""] = cmp.mapping(cmp.mapping.scroll_docs(4), { "i", "c" }),
74 | [""] = cmp.mapping(cmp.mapping.complete(), { "i" }),
75 | [""] = cmp.mapping(
76 | cmp.mapping.select_prev_item({ behavior = cmp.SelectBehavior.Select, count = 20 }),
77 | { "i", "c" }
78 | ),
79 | [""] = cmp.mapping(
80 | cmp.mapping.select_next_item({ behavior = cmp.SelectBehavior.Select, count = 20 }),
81 | { "i", "c" }
82 | ),
83 | [""] = cmp.mapping({
84 | i = cmp.mapping.abort(),
85 | c = cmp.mapping.close(),
86 | }),
87 | [""] = cmp.mapping.confirm({ select = true, behavior = cmp.ConfirmBehavior.Replace }),
88 | -- [''] = cmp.mapping.confirm({ select = false, behavior = cmp.ConfirmBehavior.Insert })
89 | -- close the cmp interface if no item is selected, I find it more
90 | -- intuitive when using LSP autoselect (instead of sending )
91 | [""] = cmp.mapping(function(fallback)
92 | if cmp.visible() then
93 | if cmp.get_selected_entry() then
94 | cmp.confirm({ select = false, cmp.ConfirmBehavior.Insert })
95 | else
96 | cmp.close()
97 | end
98 | else
99 | fallback()
100 | end
101 | end),
102 | },
103 |
104 | formatting = {
105 | expandable_indicator = true,
106 | fields = { "kind", "abbr", "menu" },
107 | format = function(entry, vim_item)
108 | local source_names = {
109 | path = "Path",
110 | buffer = "Buffer",
111 | cmdline = "Cmdline",
112 | nvim_lua = "Lua",
113 | nvim_lsp = "LSP",
114 | lazydev = "LazyDev",
115 | }
116 |
117 | vim_item.menu = ("%-10s [%s]"):format(
118 | vim_item.kind,
119 | source_names[entry.source.name] or entry.source.name)
120 |
121 | -- get the item kind icon from our LSP settings
122 | local kind_idx = vim.lsp.protocol.CompletionItemKind[vim_item.kind]
123 | if tonumber(kind_idx) > 0 then
124 | vim_item.kind = vim.lsp.protocol.CompletionItemKind[kind_idx]
125 | end
126 |
127 | -- set max width of the LSP item or we can't see the docs
128 | local max_width = math.floor(vim.o.columns * 0.40)
129 | vim_item.abbr = vim_item.abbr:sub(1, max_width)
130 |
131 | return vim_item
132 | end,
133 | },
134 |
135 | -- DO NOT ENABLE
136 | -- just for testing with nvim native completion menu
137 | experimental = {
138 | native_menu = false,
139 | ghost_text = true,
140 | },
141 | }
142 |
143 | -- Use buffer source for `/` (if you enabled `native_menu`, this won't work anymore).
144 | cmp.setup.cmdline("/", {
145 | sources = {
146 | { name = "buffer" }
147 | }
148 | })
149 |
150 | -- Use cmdline & path source for ':' (if you enabled `native_menu`, this won't work anymore).
151 | cmp.setup.cmdline(":", {
152 | sources = cmp.config.sources({
153 | { name = "path" }
154 | }, {
155 | { name = "cmdline" }
156 | })
157 | })
158 | end
159 |
160 | return M
161 |
--------------------------------------------------------------------------------
/lua/plugins/conform.lua:
--------------------------------------------------------------------------------
1 | return {
2 | "stevearc/conform.nvim",
3 | enabled = vim.fn.has("nvim-0.10") == 1,
4 | event = "BufReadPost",
5 | config = function()
6 | require("conform").setup({
7 | formatters_by_ft = {
8 | lua = { "stylua" },
9 | python = { "black" },
10 | css = { "prettier", "prettierd", stop_after_first = true },
11 | html = { "prettier", "prettierd", stop_after_first = true },
12 | yaml = { "prettier", "prettierd", stop_after_first = true },
13 | jsonc = { "prettier", "prettierd", stop_after_first = true },
14 | javascript = { "prettier", "prettierd", stop_after_first = true },
15 | },
16 | })
17 | end,
18 | _set_gq_keymap = function(e)
19 | -- priortize LSP formatting as `gq`
20 | local lsp_has_formatting = false
21 | local lsp_clients = require("utils").lsp_get_clients({ bufnr = e.buf })
22 | local lsp_keymap_set = function(m, c)
23 | vim.keymap.set(m, "gq", function()
24 | vim.lsp.buf.format({ async = true, bufnr = e.buf })
25 | end, {
26 | silent = true,
27 | buffer = e.buf,
28 | desc = string.format("format document [LSP:%s]", c.name)
29 | })
30 | end
31 | vim.tbl_map(function(c)
32 | if c:supports_method("textDocument/rangeFormatting", { bufnr = e.buf }) then
33 | lsp_keymap_set("x", c)
34 | lsp_has_formatting = true
35 | end
36 | if c:supports_method("textDocument/formatting", { bufnr = e.buf }) then
37 | lsp_keymap_set("n", c)
38 | lsp_has_formatting = true
39 | end
40 | end, lsp_clients)
41 | -- check conform.nvim for formatters:
42 | -- (1) if we have no LSP formatter map as `gq`
43 | -- (2) if LSP formatter exists, map as `gQ`
44 | local ok, conform = pcall(require, "conform")
45 | local formatters = ok and conform.list_formatters(e.buf) or {}
46 | if #formatters > 0 then
47 | vim.keymap.set("n", lsp_has_formatting and "gQ" or "gq", function()
48 | require("conform").format({ async = true, buffer = e.buf, lsp_fallback = false })
49 | end, {
50 | silent = true,
51 | buffer = e.buf,
52 | desc = string.format("format document [%s]", formatters[1].name)
53 | })
54 | end
55 | end
56 | }
57 |
--------------------------------------------------------------------------------
/lua/plugins/dap/cpp_rust.lua:
--------------------------------------------------------------------------------
1 | local res, dap = pcall(require, "dap")
2 | if not res then
3 | return
4 | end
5 |
6 | local utils = require("utils")
7 |
8 | dap.adapters.gdb = {
9 | type = "executable",
10 | command = "gdb",
11 | args = { "-i", "dap" }
12 | }
13 |
14 | -- https://github.com/mfussenegger/nvim-dap/wiki/Debug-Adapter-installation#ccrust-via-lldb-vscode
15 | dap.adapters.lldb = {
16 | type = "executable",
17 | command = "/usr/bin/lldb-vscode",
18 | name = "lldb"
19 | }
20 |
21 | -- https://github.com/mfussenegger/nvim-dap/wiki/C-C---Rust-(gdb-via--vscode-cpptools)
22 | dap.adapters.cppdbg = {
23 | id = "cppdbg",
24 | type = "executable",
25 | command = vim.fn.stdpath("data") .. "/mason/bin/OpenDebugAD7",
26 | }
27 |
28 | dap.configurations.c = {
29 | {
30 | name = "[CPPDBG] Launch Executable",
31 | type = "cppdbg",
32 | request = "launch",
33 | program = utils.dap_pick_exec,
34 | cwd = "${workspaceFolder}",
35 | stopAtEntry = false,
36 | },
37 | {
38 | name = "[CPPDBG] Launch Executable (External console)",
39 | type = "cppdbg",
40 | request = "launch",
41 | program = utils.dap_pick_exec,
42 | cwd = "${workspaceFolder}",
43 | stopAtEntry = false,
44 | externalConsole = true,
45 | },
46 | {
47 | name = "[CPPDBG] Attach to process",
48 | type = "cppdbg",
49 | request = "attach",
50 | processId = utils.dap_pick_process,
51 | program = utils.dap_pick_exec,
52 | args = {},
53 | },
54 | {
55 | name = "[CPPDBG] Launch Neovim (Development build)",
56 | type = "cppdbg",
57 | request = "launch",
58 | program = function()
59 | local nvim_bin = vim.fn.expand("$HOME/Sources/nvim/neovim/build/bin/nvim")
60 | if not vim.uv.fs_stat(nvim_bin) then
61 | utils.warn(string.format("'%s' is not executable, aborting.", nvim_bin))
62 | return dap.ABORT
63 | end
64 | -- The idea for the below is taken from:
65 | -- https://zignar.net/2023/02/17/debugging-neovim-with-neovim-and-nvim-dap/
66 | -- Neovim sepaprated the TUI from the main process, launching neovim in fact
67 | -- spawns another process `nvim --embed`, if we want to debug nvim itself we
68 | -- need to attach to the subprocess, we can do so by adding a oneshot listener
69 | local key = "nvim-debug-subprocess"
70 | dap.listeners.after.initialize[key] = function(session)
71 | -- This is a oneshot listener, clear immediately
72 | dap.listeners.after.initialize[key] = nil
73 | -- Ensure our listeners are cleaned up after close
74 | session.on_close[key] = function()
75 | for _, handler in pairs(dap.listeners.after) do
76 | handler[key] = nil
77 | end
78 | end
79 | end
80 | -- Listen to event `process` to get the pid
81 | dap.listeners.after.event_process[key] = function(_, body)
82 | dap.listeners.after.event_process[key] = nil
83 | -- Wait for the child pid for 1 second and if valid launch 2nd "attach" session
84 | -- this event also gets called a second time for the child process but the pid
85 | -- will be nil the second time and will therefore do nothing
86 | local ppid = body.systemProcessId
87 | utils.info(string.format("Launched nvim process (ppid=%s)", ppid))
88 | vim.wait(1000, function()
89 | return tonumber(vim.fn.system("ps -o pid= --ppid " .. tostring(ppid))) ~= nil
90 | end)
91 | local pid = tonumber(vim.fn.system("ps -o pid= --ppid " .. tostring(ppid)))
92 | utils.info(
93 | string.format("Attaching to nvim (ppid=%s) child process (pid=%s)", ppid, pid))
94 | if pid then
95 | dap.run({
96 | name = "Neovim embedded",
97 | type = "cppdbg",
98 | request = "attach",
99 | processId = pid,
100 | program = vim.fn.expand("$HOME/Sources/nvim/neovim/build/bin/nvim"),
101 | cwd = "${workspaceFolder}",
102 | externalConsole = false,
103 | })
104 | end
105 | end
106 | return nvim_bin
107 | end,
108 | environment = function()
109 | -- https://code.visualstudio.com/docs/cpp/launch-json-reference
110 | return {
111 | -- Neovim needs it's source directory `runtimepath`
112 | { name = "VIMRUNTIME", value = vim.fn.expand("$HOME/Sources/nvim/neovim/runtime") }
113 | }
114 | end,
115 | cwd = "${workspaceFolder}",
116 | stopAtEntry = false,
117 | externalConsole = true,
118 | },
119 | {
120 | name = "[CPPDBG] Attach to Neovim (Development build)",
121 | type = "cppdbg",
122 | request = "attach",
123 | program = function()
124 | local nvim_bin = vim.fn.expand("$HOME/Sources/nvim/neovim/build/bin/nvim")
125 | if not vim.uv.fs_stat(nvim_bin) then
126 | utils.warn(string.format("'%s' is not executable, aborting.", nvim_bin))
127 | return dap.ABORT
128 | end
129 | return nvim_bin
130 | end,
131 | processId = function()
132 | -- attach to the `nvim --embed` process
133 | return utils.dap_pick_process(
134 | { winopts = { height = 0.30 } },
135 | { filter = function(proc) return proc.name:match("nvim.*%-%-embed") end })
136 | end,
137 | cwd = "${workspaceFolder}",
138 | stopAtEntry = false,
139 | },
140 | {
141 | name = "[LLDB] Launch Executable",
142 | type = "lldb",
143 | request = "launch",
144 | program = utils.dap_pick_exec,
145 | cwd = "${workspaceFolder}",
146 | stopOnEntry = false,
147 | args = {},
148 | },
149 | {
150 | name = "[LLDB] Attach to process",
151 | type = "lldb",
152 | request = "attach",
153 | pid = utils.dap_pick_process,
154 | args = {},
155 | },
156 | {
157 | name = "[GDB] Launch Executable",
158 | type = "gdb",
159 | request = "launch",
160 | program = utils.dap_pick_exec,
161 | -- program = function()
162 | -- local bin
163 | -- vim.ui.input({ prompt = "Path to executable: " },
164 | -- function(input)
165 | -- bin = vim.fn.expand(input)
166 | -- end)
167 | -- if type(bin) == "string" and vim.uv.fs_stat(bin) then
168 | -- return bin
169 | -- else
170 | -- -- ctrl-c'ing `vin.ui.input` returns "v:null"
171 | -- if bin ~= "v:null" and bin ~= "" then
172 | -- utils.warn(string.format("'%s' is not executable, aborting.", bin))
173 | -- end
174 | -- return dap.ABORT
175 | -- end
176 | -- end,
177 | cwd = "${workspaceFolder}",
178 | stopAtBeginningOfMainSubprogram = false,
179 | },
180 | {
181 | name = "[GDB] Attach to process",
182 | type = "gdb",
183 | request = "attach",
184 | pid = utils.dap_pick_process,
185 | args = {},
186 | },
187 | }
188 |
189 | dap.configurations.cpp = dap.configurations.c
190 | dap.configurations.rust = vim.deepcopy(dap.configurations.c)
191 |
192 | -- https://github.com/mfussenegger/nvim-dap/wiki/Debug-Adapter-installation#ccrust-via-lldb-vscode
193 | local build_env = function()
194 | local variables = {}
195 | for k, v in pairs(vim.fn.environ()) do
196 | table.insert(variables, string.format("%s=%s", k, v))
197 | end
198 | return variables
199 | end
200 |
201 | local rustInitCommands = function()
202 | -- Find out where to look for the pretty printer Python module
203 | local rustc_sysroot = vim.fn.trim(vim.fn.system("rustc --print sysroot"))
204 | local script_import = 'command script import "' ..
205 | rustc_sysroot .. '/lib/rustlib/etc/lldb_lookup.py"'
206 | local commands_file = rustc_sysroot .. "/lib/rustlib/etc/lldb_commands"
207 |
208 | local commands = {}
209 | local file = io.open(commands_file, "r")
210 | if file then
211 | for line in file:lines() do
212 | table.insert(commands, line)
213 | end
214 | file:close()
215 | end
216 | table.insert(commands, 1, script_import)
217 |
218 | return commands
219 | end
220 |
221 | for i, c in ipairs(dap.configurations.rust) do
222 | if c.type == "lldb" then
223 | dap.configurations.rust[i] = vim.tbl_extend("force", dap.configurations.rust[i], {
224 | env = build_env,
225 | initCommands = rustInitCommands,
226 | })
227 | end
228 | end
229 |
--------------------------------------------------------------------------------
/lua/plugins/dap/go.lua:
--------------------------------------------------------------------------------
1 | local res, dap = pcall(require, "dap")
2 | if not res then
3 | return
4 | end
5 |
6 | dap.adapters.go = function(callback, config)
7 | local stdout = vim.uv.new_pipe(false)
8 | local handle
9 | local pid_or_err
10 | local host = config.host or "127.0.0.1"
11 | local port = config.port or "38697"
12 | local addr = string.format("%s:%s", host, port)
13 | local opts = {
14 | stdio = { nil, stdout },
15 | args = { "dap", "-l", addr },
16 | detached = true
17 | }
18 | handle, pid_or_err = vim.uv.spawn("dlv", opts, function(code)
19 | stdout:close()
20 | handle:close()
21 | if code ~= 0 then
22 | print("dlv exited with code", code)
23 | end
24 | end)
25 | assert(handle, "Error running dlv: " .. tostring(pid_or_err))
26 | stdout:read_start(function(err, chunk)
27 | assert(not err, err)
28 | if chunk then
29 | vim.schedule(function()
30 | require("dap.repl").append(chunk)
31 | end)
32 | end
33 | end)
34 | -- Wait for delve to start
35 | vim.defer_fn(
36 | function()
37 | callback({ type = "server", host = "127.0.0.1", port = port })
38 | end,
39 | 100)
40 | end
41 |
42 | dap.configurations.go = {
43 | {
44 | type = "go",
45 | name = "Debug",
46 | request = "launch",
47 | program = "${file}",
48 | },
49 | {
50 | type = "go",
51 | name = "Debug Package",
52 | request = "launch",
53 | program = "${fileDirname}",
54 | },
55 | {
56 | type = "go",
57 | name = "Attach",
58 | mode = "local",
59 | request = "attach",
60 | processId = require("dap.utils").pick_process,
61 | },
62 | {
63 | type = "go",
64 | name = "Debug test",
65 | request = "launch",
66 | mode = "test",
67 | program = "${file}",
68 | },
69 | {
70 | type = "go",
71 | name = "Debug test (go.mod)",
72 | request = "launch",
73 | mode = "test",
74 | program = "./${relativeFileDirname}",
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/lua/plugins/dap/init.lua:
--------------------------------------------------------------------------------
1 | local M = {
2 | "mfussenegger/nvim-dap",
3 | dependencies = {
4 | {
5 | "rcarriga/nvim-dap-ui",
6 | dependencies = { "nvim-neotest/nvim-nio" },
7 | },
8 | { "theHamsta/nvim-dap-virtual-text" },
9 | { "jbyuki/one-small-step-for-vimkind" },
10 | { "mfussenegger/nvim-dap-python" },
11 | },
12 | }
13 |
14 | local utils = require "utils"
15 | local BP_DB_PATH = vim.fn.stdpath("data") .. "/dap_bps.json"
16 |
17 | M._load_bps = function()
18 | local fp = io.open(BP_DB_PATH, "r")
19 | if not fp then
20 | utils.info("No breakpoint json-db present.")
21 | return
22 | end
23 | local json = fp:read("*a")
24 | local ok, bps = pcall(vim.json.decode, json)
25 | if not ok or type(bps) ~= "table" then
26 | utils.warn(string.format("Error parsing breakpoint json-db: %s", bps))
27 | return
28 | end
29 | local path2bufnr = {}
30 | for _, bufnr in ipairs(vim.api.nvim_list_bufs()) do
31 | local path = vim.api.nvim_buf_get_name(bufnr)
32 | if type(bps[path]) == "table" and not vim.tbl_isempty(bps[path]) then
33 | path2bufnr[path] = bufnr
34 | end
35 | end
36 | -- no breakpoints in current buflist
37 | if vim.tbl_isempty(path2bufnr) then return end
38 | local bp_count = 0
39 | for path, buf_bps in pairs(bps) do
40 | local bufnr = tonumber(path2bufnr[path])
41 | if bufnr then
42 | for _, bp in pairs(buf_bps) do
43 | bp_count = bp_count + 1
44 | local line = bp.line
45 | local opts = {
46 | condition = bp.condition,
47 | log_message = bp.logMessage,
48 | hit_condition = bp.hitCondition,
49 | }
50 | require("dap.breakpoints").set(opts, bufnr, line)
51 | end
52 | end
53 | end
54 | -- Load bps into active session (not just the UI)
55 | local session = require("dap").session()
56 | if session and bp_count > 0 then
57 | session:set_breakpoints(require("dap.breakpoints").get())
58 | end
59 | utils.info(string.format("Loaded %d breakpoints in %d bufers.",
60 | bp_count, vim.tbl_count(path2bufnr)))
61 | end
62 |
63 | M._store_bps = function()
64 | local fp = io.open(BP_DB_PATH, "r")
65 | local json = fp and fp:read("*a") or "{}"
66 | local ok, bps = pcall(vim.json.decode, json)
67 | if not ok or type(bps) ~= "table" then
68 | bps = {}
69 | end
70 | local bp_count = 0
71 | local breakpoints_by_buf = require("dap.breakpoints").get()
72 | for bufnr, buf_bps in pairs(breakpoints_by_buf) do
73 | bp_count = bp_count + #buf_bps
74 | bps[vim.api.nvim_buf_get_name(bufnr)] = buf_bps
75 | end
76 | -- If buffer has no breakpoints, remove from the db
77 | for _, bufnr in ipairs(vim.api.nvim_list_bufs()) do
78 | if not breakpoints_by_buf[bufnr] then
79 | local path = vim.api.nvim_buf_get_name(bufnr)
80 | bps[path] = nil
81 | end
82 | end
83 | fp = io.open(BP_DB_PATH, "w")
84 | if fp then
85 | fp:write(vim.json.encode(bps))
86 | fp:close()
87 | utils.info(string.format("Stored %d breakpoints in %d bufers.",
88 | bp_count, vim.tbl_count(breakpoints_by_buf)))
89 | end
90 | end
91 |
92 | M.init = function()
93 | vim.keymap.set({ "n", "v" },
94 | "",
95 | function() require "dap".continue() end,
96 | { silent = true, desc = "DAP launch or continue" })
97 | vim.keymap.set({ "n", "v" },
98 | -- ``, test by pressing `` in INSERT mode
99 | "",
100 | function() require "osv".launch({ port = 8086 }) end,
101 | { silent = true, desc = "Start OSV Lua Debug Server" })
102 | vim.keymap.set({ "n", "v" },
103 | "",
104 | function()
105 | -- lazy load nvim-dap so `dapui.setup()` is called
106 | require "dap"; require "plugins.dap.ui".toggle()
107 | end,
108 | { silent = true, desc = "DAP toggle UI" })
109 | vim.keymap.set({ "n", "v" },
110 | -- Test by pressing `` in INSERT mode
111 | -- "",
112 | "",
113 | function()
114 | require "dap"; require "plugins.dap.ui".toggle(true, true)
115 | end,
116 | { silent = true, desc = "DAP toggle UI" })
117 | vim.keymap.set({ "n", "v" },
118 | "",
119 | function() require "dap".toggle_breakpoint() end,
120 | { silent = true, desc = "DAP toggle breakpoint" })
121 | vim.keymap.set({ "n", "v" },
122 | "",
123 | function() require "dap".step_over() end,
124 | { silent = true, desc = "DAP step over" })
125 | vim.keymap.set({ "n", "v" },
126 | "",
127 | function() require "dap".step_into() end,
128 | { silent = true, desc = "DAP step into" })
129 | vim.keymap.set({ "n", "v" },
130 | "",
131 | function() require "dap".step_out() end,
132 | { silent = true, desc = "DAP step out" })
133 | vim.keymap.set({ "n", "v" },
134 | "",
135 | function() require "dap".terminate() end,
136 | { silent = true, desc = "DAP Terminate" })
137 | vim.keymap.set({ "n", "v" },
138 | "dt",
139 | function() require "dap".terminate() end,
140 | { silent = true, desc = "DAP terminate" })
141 |
142 | -- Conditional breakpoints
143 | vim.keymap.set({ "n", "v" },
144 | "dc",
145 | function() require "dap".set_breakpoint(utils.input("Breakpoint condition: ")) end,
146 | { silent = true, desc = "DAP: set breakpoint with condition" })
147 | vim.keymap.set({ "n", "v" },
148 | "dl",
149 | function() require "dap".set_breakpoint(nil, nil, utils.input("Log point message: ")) end,
150 | { silent = true, desc = "DAP: set breakpoint with log point message" })
151 |
152 | -- Load/Store breakpoint in a json-db
153 | vim.keymap.set({ "n", "v" },
154 | "d-",
155 | M._load_bps,
156 | { silent = true, desc = "DAP load breakpoints" })
157 | vim.keymap.set({ "n", "v" },
158 | "d+",
159 | M._store_bps,
160 | { silent = true, desc = "DAP store breakpoints" })
161 |
162 | vim.keymap.set({ "n", "v" },
163 | "dr",
164 | function() require "dap".repl.toggle() end,
165 | { silent = true, desc = "DAP toggle debugger REPL" })
166 |
167 | -- DAP-UI widgets
168 | vim.keymap.set({ "n", "v" },
169 | "dk",
170 | function() require("dap.ui.widgets").hover() end,
171 | { silent = true, desc = "DAP Hover" })
172 | vim.keymap.set({ "n", "v" },
173 | "dp",
174 | function() require("dap.ui.widgets").preview() end,
175 | { silent = true, desc = "DAP Preview" })
176 | vim.keymap.set({ "n", "v" },
177 | "df",
178 | function()
179 | local widgets = require("dap.ui.widgets")
180 | widgets.centered_float(widgets.frames)
181 | end,
182 | { silent = true, desc = "DAP Frames" })
183 | vim.keymap.set({ "n", "v" },
184 | "ds",
185 | function()
186 | local widgets = require("dap.ui.widgets")
187 | widgets.centered_float(widgets.scopes)
188 | end,
189 | { silent = true, desc = "DAP Scopes" })
190 |
191 | -- fzf-lua
192 | vim.keymap.set({ "n", "v" },
193 | "d?",
194 | function() require "fzf-lua".dap_commands() end,
195 | { silent = true, desc = "DAP: fzf nvim-dap builtin commands" })
196 | vim.keymap.set({ "n", "v" },
197 | "db",
198 | function() require "fzf-lua".dap_breakpoints() end,
199 | { silent = true, desc = "DAP: fzf breakpoint list" })
200 | vim.keymap.set({ "n", "v" },
201 | "dF",
202 | function() require "fzf-lua".dap_frames() end,
203 | { silent = true, desc = "DAP: fzf frames" })
204 | vim.keymap.set({ "n", "v" },
205 | "dv",
206 | function() require "fzf-lua".dap_variables() end,
207 | { silent = true, desc = "DAP: fzf variables" })
208 | vim.keymap.set({ "n", "v" },
209 | "dx",
210 | function() require "fzf-lua".dap_configurations() end,
211 | { silent = true, desc = "DAP: fzf debugger configurations" }
212 | )
213 | end
214 |
215 |
216 | M.config = function()
217 | local dap = require "dap"
218 |
219 | -- Lazy load fzf-lua to register_ui_select
220 | require("fzf-lua")
221 |
222 | -- Set logging level
223 | require("dap").set_log_level("DEBUG")
224 |
225 | -- configure dap-ui and language adapaters
226 | require "plugins.dap.ui".setup()
227 | require "plugins.dap.go"
228 | require "plugins.dap.lua"
229 | require "plugins.dap.python"
230 | require "plugins.dap.cpp_rust"
231 |
232 | -- Override the json decoder so we can support jsonc (comments, trailing commas)
233 | require("dap.ext.vscode").json_decode = require("lib.jsonc").decode
234 |
235 | -- Load configurations from `.launch.json`
236 | -- Example `.launch.json`:
237 | -- {
238 | -- "version": "0.2.0",
239 | -- "configurations": [
240 | -- {
241 | -- "type": "lldb",
242 | -- "request": "launch",
243 | -- "name": "[LLDB] Launch '${workspaceFolder}/a.out'",
244 | -- "program": "a.out",
245 | -- "cwd": "${workspaceFolder}",
246 | -- "stopOnEntry": false
247 | -- }
248 | -- ]
249 | -- }
250 | require("dap.ext.vscode").load_launchjs(".launch.jsonc", {
251 | go = { "go" },
252 | python = { "py" },
253 | gdb = { "c", "cpp", "rust" },
254 | lldb = { "c", "cpp", "rust" },
255 | cppdbg = { "c", "cpp", "rust" },
256 | })
257 |
258 | -- Controls how stepping switches buffers
259 | dap.defaults.fallback.switchbuf = "useopen,uselast"
260 |
261 | -- Which terminal should be launched when `externalConsole = true`
262 | dap.defaults.fallback.external_terminal = {
263 | command = "/usr/bin/alacritty",
264 | args = { "-e" },
265 | }
266 |
267 | -- links by default to DiagnosticVirtualTextXXX which linkx to Comment in nightgly
268 | vim.api.nvim_set_hl(0, "NvimDapVirtualText", { link = "Comment" })
269 | vim.api.nvim_set_hl(0, "NvimDapVirtualTextInfo", { link = "DiagnosticInfo" })
270 | vim.api.nvim_set_hl(0, "NvimDapVirtualTextError", { link = "DiagnosticError" })
271 | vim.api.nvim_set_hl(0, "NvimDapVirtualTextChanged", { link = "DiagnosticWarn" })
272 |
273 | -- configure nvim-dap-virtual-text
274 | local ok, dapvt = pcall(require, "nvim-dap-virtual-text")
275 | if ok and dapvt then
276 | dapvt.setup({
277 | -- "inline" is also possible with nvim-0.10, IMHO is confusing
278 | virt_text_pos = "eol",
279 | })
280 | end
281 | end
282 |
283 | return M
284 |
--------------------------------------------------------------------------------
/lua/plugins/dap/lua.lua:
--------------------------------------------------------------------------------
1 | local res, dap = pcall(require, "dap")
2 | if not res then
3 | return
4 | end
5 |
6 | local nvim_server
7 | local nvim_chanID
8 |
9 | -- both deugging and execution is done on external headless instances
10 | -- we start a headless instance and then call ("osv").launch() which
11 | -- in turn starts another headless instance which will be the instance
12 | -- we connect to
13 | -- once the instance is running we can call `:luafile ` in order
14 | -- to start debugging
15 | local function dap_server(opts)
16 | assert(dap.adapters.nlua,
17 | "nvim-dap adapter configuration for nlua not found. " ..
18 | "Please refer to the README.md or :help osv.txt")
19 |
20 | -- server already started?
21 | if nvim_chanID then
22 | local pid = vim.fn.jobpid(nvim_chanID)
23 | vim.fn.rpcnotify(nvim_chanID, "nvim_exec_lua", [[return require"osv".stop()]])
24 | vim.fn.jobstop(nvim_chanID)
25 | if type(vim.uv.os_getpriority(pid)) == "number" then
26 | vim.uv.kill(pid, 9)
27 | end
28 | nvim_chanID = nil
29 | end
30 |
31 | nvim_chanID = vim.fn.jobstart({ vim.v.progpath, "--embed", "--headless" }, { rpc = true })
32 | assert(nvim_chanID, "Could not create neovim instance with jobstart!")
33 |
34 | local mode = vim.fn.rpcrequest(nvim_chanID, "nvim_get_mode")
35 | assert(not mode.blocking, "Neovim is waiting for input at startup. Aborting.")
36 |
37 | -- create the symlink from lazy
38 | local plugin_name = "one-small-step-for-vimkind"
39 | local plugin_dir = vim.fn.stdpath("data") .. "/site/pack/dap"
40 | assert(vim.fn.mkdir(plugin_dir, "p"), "Unable to create plugin dir")
41 | vim.uv.fs_symlink(vim.fn.stdpath("data") .. "/lazy", plugin_dir .. "/opt", { dir = true })
42 |
43 | -- make sure OSV is loaded
44 | vim.fn.rpcrequest(nvim_chanID, "nvim_exec_lua",
45 | [[vim.opt.packpath:append({ vim.fn.stdpath("data") .. "/site" })]], {})
46 | vim.fn.rpcrequest(nvim_chanID, "nvim_command", "packadd " .. plugin_name)
47 |
48 | nvim_server = vim.fn.rpcrequest(nvim_chanID,
49 | "nvim_exec_lua",
50 | [[return require"osv".launch(...)]],
51 | { opts })
52 |
53 | vim.wait(100)
54 |
55 | -- print(("Server started on port %d, channel-id %d"):format(nvim_server.port, nvim_chanID))
56 | return nvim_server
57 | end
58 |
59 | dap.adapters.nlua = function(callback, config)
60 | if not config.port then
61 | local server = dap_server()
62 | config.host = server.host
63 | config.port = server.port
64 | end
65 | callback({ type = "server", host = config.host, port = config.port })
66 | if type(config.post) == "function" then
67 | config.post()
68 | end
69 | end
70 |
71 |
72 | dap.configurations.lua = {
73 | {
74 | type = "nlua",
75 | request = "attach",
76 | name = "Attach to running Neovim instance (localhost:8086)",
77 | host = "127.0.0.1",
78 | port = 8086,
79 | },
80 | {
81 | type = "nlua",
82 | request = "attach",
83 | name = "Attach to running Neovim instance (prompt)",
84 | host = function()
85 | local val = vim.fn.input("Host [127.0.0.1]: ")
86 | return #val > 0 and val or "127.0.0.1"
87 | end,
88 | port = function()
89 | local val = vim.fn.input("Port [8086]: ")
90 | return #val > 0 and tonumber(val) or 8086
91 | end,
92 | },
93 | {
94 | type = "nlua",
95 | name = "Debug current file",
96 | request = "attach",
97 | -- we acquire host/port in the adapters function above
98 | -- host = function() end,
99 | -- port = function() end,
100 | post = function()
101 | dap.listeners.after["setBreakpoints"]["osv"] = function(session, body)
102 | assert(nvim_chanID, "Fatal: neovim RPC channel is nil!")
103 | vim.fn.rpcnotify(nvim_chanID, "nvim_command", "luafile " .. vim.fn.expand("%:p"))
104 | -- clear the lisener or we get called in any dap-config run
105 | dap.listeners.after["setBreakpoints"]["osv"] = nil
106 | end
107 | -- for k, v in pairs(dap.listeners.after) do
108 | -- v["test"] = function()
109 | -- print(k, "called")
110 | -- end
111 | -- end
112 | end
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/lua/plugins/dap/python.lua:
--------------------------------------------------------------------------------
1 | local res, dap_python = pcall(require, "dap-python")
2 | if not res then
3 | return
4 | end
5 |
6 | local prefix = ""
7 | local bin_python = "/bin/python"
8 |
9 | if vim.fn.executable("pyenv") == 1 then
10 | local out = vim.fn.systemlist({ "pyenv", "prefix" })
11 | if vim.v.shell_error == 0 and type(out[1]) == "string" then
12 | -- nvim-dap-python uses `os.getenv("VIRTUAL_ENV")`
13 | prefix = out[1]
14 | vim.env.VIRTUAL_ENV = out[1]
15 | end
16 | else
17 | -- check mason registry
18 | local ok, pkg = pcall(function()
19 | return require("mason-registry").get_package("debugpy")
20 | end)
21 | if ok and pkg and pkg:is_installed() then
22 | prefix = pkg:get_install_path() .. "/venv"
23 | end
24 | end
25 |
26 | dap_python.setup(prefix .. bin_python)
27 | dap_python.test_runner = prefix .. "/bin/pytest"
28 |
29 | local function getpid()
30 | local pid = require("dap.utils").pick_process({ filter = "python" })
31 | if type(pid) == "thread" then
32 | -- returns a coroutine.create due to it being run from fzf-lua ui.select
33 | -- start the coroutine and wait for `coroutine.resume` (user selection)
34 | coroutine.resume(pid)
35 | pid = coroutine.yield(pid)
36 | end
37 | return pid
38 | end
39 |
40 | table.insert(require("dap").configurations.python, 4, {
41 | type = "python",
42 | request = "attach",
43 | name = "Attach to process",
44 | connect = function()
45 | -- https://github.com/microsoft/debugpy/#attaching-to-a-running-process-by-id
46 | local port = 5678
47 | local pid = getpid()
48 | local out = vim.fn.systemlist({ prefix .. bin_python, "-m", "debugpy",
49 | "--listen", "localhost:" .. tostring(port), "--pid", tostring(pid) })
50 | assert(vim.v.shell_error == 0, table.concat(out, "\n"))
51 | return { port = port }
52 | end,
53 | })
54 |
--------------------------------------------------------------------------------
/lua/plugins/dap/ui.lua:
--------------------------------------------------------------------------------
1 | local res, dapui = pcall(require, "dapui")
2 | if not res then
3 | return
4 | end
5 |
6 | local utils = require("utils")
7 | local M = {}
8 |
9 | M.setup = function()
10 | ---@diagnostic disable-next-line: missing-fields
11 | dapui.setup({
12 | layouts = {
13 | {
14 | position = "right",
15 | size = 0.40,
16 | elements = {
17 | { id = "scopes", size = 0.38, },
18 | { id = "watches", size = 0.16 },
19 | { id = "stacks", size = 0.28 },
20 | { id = "breakpoints", size = 0.18 },
21 | },
22 | },
23 | {
24 | position = "bottom",
25 | size = 0.30,
26 | elements = {
27 | { id = "repl", size = 0.60, },
28 | { id = "console", size = 0.40 },
29 | },
30 | },
31 | },
32 | })
33 | local dap = require("dap")
34 | dap.listeners.before.attach.dapui_config = function()
35 | M.open()
36 | end
37 | dap.listeners.before.launch.dapui_config = function()
38 | M.open()
39 | end
40 | dap.listeners.after.event_initialized.dapui_config = function()
41 | M.open()
42 | end
43 | dap.listeners.before.event_terminated.dapui_config = function(e)
44 | require("utils").info(
45 | string.format("program '%s' was terminated.", vim.fn.fnamemodify(e.config.program, ":t")))
46 | end
47 | -- dap.listeners.before.event_exited.dapui_config = function(e)
48 | -- dapui.close()
49 | -- end
50 | end
51 |
52 | M.open = function(reset, tmux_zoom)
53 | if not M._is_open and tmux_zoom and not M._tmux_was_unzoomed then
54 | M._tmux_was_unzoomed = utils.tmux_zoom()
55 | if M._tmux_was_unzoomed then
56 | vim.cmd("sleep! 20m")
57 | end
58 | end
59 | dapui.open({ reset = reset == nil and true or reset })
60 | M._is_open = true
61 | end
62 |
63 | M.close = function(tmux_zoom)
64 | dapui.close()
65 | if tmux_zoom and M._tmux_was_unzoomed then
66 | utils.tmux_unzoom()
67 | M._tmux_was_unzoomed = nil
68 | end
69 | M._is_open = nil
70 | end
71 |
72 | M.toggle = function(reset, tmux_zoom)
73 | if M._is_open then
74 | M.close(tmux_zoom)
75 | else
76 | M.open(reset, tmux_zoom)
77 | end
78 | end
79 |
80 | return M
81 |
--------------------------------------------------------------------------------
/lua/plugins/devicons/init.lua:
--------------------------------------------------------------------------------
1 | return {
2 | "nvim-tree/nvim-web-devicons",
3 | config = function()
4 | require("plugins.devicons.setup")
5 | end
6 | }
7 |
--------------------------------------------------------------------------------
/lua/plugins/devicons/setup.lua:
--------------------------------------------------------------------------------
1 | require("nvim-web-devicons").setup({
2 | override_by_extension = {
3 | sol = {
4 | -- icon = "♦",
5 | icon = "",
6 | color = "#a074c4",
7 | name = "Sol"
8 | },
9 | sh = {
10 | icon = "",
11 | color = "#89e051",
12 | cterm_color = "113",
13 | name = "Sh",
14 | },
15 | md = {
16 | icon = "",
17 | color = "#dddddd",
18 | cterm_color = "239",
19 | name = "Md",
20 | },
21 | norg = {
22 | icon = "",
23 | color = "#97eefc",
24 | name = "Neorg",
25 | },
26 | },
27 | })
28 |
--------------------------------------------------------------------------------
/lua/plugins/diffview.lua:
--------------------------------------------------------------------------------
1 | local utils = require("utils")
2 |
3 | local M = {
4 | "sindrets/diffview.nvim",
5 | cmd = { "DiffviewOpen", "DiffviewFileHistory" },
6 | }
7 |
8 | M._is_open = function()
9 | return package.loaded.diffview and require("diffview.lib").get_current_view()
10 | end
11 |
12 | M._close = function()
13 | if M._is_open() then
14 | vim.cmd("DiffviewClose")
15 | return 0
16 | end
17 | end
18 |
19 | M._toggle = function(git_args)
20 | if M._is_open() then
21 | return M._close()
22 | else
23 | git_args = git_args or {}
24 | local git_cmd = { "git" }
25 | for _, arg in ipairs(git_args) do
26 | table.insert(git_cmd, arg:match("%$") and vim.fn.expand(arg) or arg)
27 | end
28 | table.insert(git_cmd, "status")
29 | local ret = vim.system(git_cmd):wait()
30 | if #ret.stderr > 0 then
31 | utils.warn(ret.stderr)
32 | return
33 | end
34 | local no_changes = ret.stdout:match("nothing to commit")
35 | local diffview_cmd = { "DiffviewOpen" }
36 | -- DiffviewOpen needs the args to be in the format of key=val
37 | for i = 1, #git_args, 2 do
38 | table.insert(diffview_cmd, string.format("%s=%s", git_args[i], git_args[i + 1]))
39 | end
40 | table.insert(diffview_cmd, no_changes and "HEAD~" or "HEAD")
41 | vim.cmd(table.concat(diffview_cmd, " "))
42 | return 1
43 | end
44 | end
45 |
46 | M.init = function()
47 | vim.keymap.set({ "n", "x" }, "gd",
48 | function()
49 | if M._toggle() == 1 then
50 | M._tmux_was_unzoomed = utils.tmux_zoom()
51 | end
52 | end,
53 | { silent = true, desc = "Git diff (project)" })
54 |
55 | vim.keymap.set({ "n", "x" }, "yd",
56 | function()
57 | if
58 | M._toggle({
59 | "-c", "status.showUntrackedFiles=no",
60 | "--git-dir", "$HOME/dots/.git",
61 | "-C", "$HOME",
62 | }) == 1
63 | then
64 | M._tmux_was_unzoomed = utils.tmux_zoom()
65 | end
66 | end,
67 | { silent = true, desc = "Git diff (yadm)" })
68 | end
69 |
70 | M.config = function()
71 | local gq_keymap_set_opts = {
72 | { "n", "v" }, "gq", M._close, { silent = true, desc = "Close Diffview" }
73 | }
74 | require("diffview").setup({
75 | keymaps = {
76 | view = { gq_keymap_set_opts },
77 | file_panel = { gq_keymap_set_opts },
78 | file_history_panel = { gq_keymap_set_opts },
79 | option_panel = { gq_keymap_set_opts },
80 | },
81 | hooks = {
82 | -- view_opened = function(_) end
83 | view_closed = function()
84 | if M._tmux_was_unzoomed then
85 | utils.tmux_unzoom()
86 | end
87 | -- remap `gq` to conform since we hijacked it to `DiffviewClose`
88 | for _, w in ipairs(vim.api.nvim_tabpage_list_wins(0)) do
89 | local b = vim.api.nvim_win_get_buf(w)
90 | require("plugins.conform")._set_gq_keymap({ buf = b })
91 | end
92 | end
93 | }
94 | })
95 | end
96 |
97 | return M
98 |
--------------------------------------------------------------------------------
/lua/plugins/fugitive.lua:
--------------------------------------------------------------------------------
1 | local M = {
2 | "tpope/vim-fugitive",
3 | cmd = { "Git", "Dot", "Yit", "Gread", "Gwrite", "Gvdiffsplit", "Gdiffsplit" },
4 | }
5 |
6 | M.init = function()
7 | local map = vim.keymap.set
8 |
9 | -- :Gedit will always send us back to the working copy
10 | -- and thus serves as a quasi back button
11 | map("n", "gg", ":Git", { silent = true, desc = "Git" })
12 | map("n", "gr", ":Gread", { silent = true, desc = "Gread (reset)" })
13 | map("n", "gw", ":Gwrite", { silent = true, desc = "Gwrite (stage)" })
14 | -- map("n", "gb", ":Git blame", { silent = true, desc = "git blame" })
15 | -- map('n', 'gc', ':Git commit', { silent = true })
16 | -- map("n", "gD", ":Git diff", { silent = true, desc = "Git diff (project)" })
17 | map("n", "gD", ":Gvdiffsplit!", { silent = true, desc = "Git diff (buffer)" })
18 | map("n", "gp", ":Git push", { silent = true, desc = "Git push" })
19 | map("n", "gP", ":Git pull