From e78c5ce3f58d6413b66107af2bb0499d33168f71 Mon Sep 17 00:00:00 2001 From: Yingjie Wang Date: Sun, 21 Jul 2024 02:51:17 -0400 Subject: [PATCH] feat: add nvimdots --- nvim/.github/ISSUE_TEMPLATE/bug_report.yml | 121 ++++ nvim/.github/ISSUE_TEMPLATE/config.yml | 8 + nvim/.github/ISSUE_TEMPLATE/custom_config.yml | 111 ++++ .../ISSUE_TEMPLATE/feature_request.yml | 22 + .../ISSUE_TEMPLATE/lsp_issue_report.yml | 148 +++++ nvim/.github/workflows/lint_code.yml | 11 + nvim/.github/workflows/style_check.yml | 13 + nvim/.github/workflows/update_flake.yml | 26 + nvim/.github/workflows/update_lockfile.yml | 41 ++ nvim/.gitignore | 4 + nvim/.nixd.json | 7 + nvim/LICENSE | 30 + nvim/README.md | 216 +++++++ nvim/flake.lock | 148 +++++ nvim/flake.nix | 105 ++++ nvim/init.lua | 3 + nvim/init.vim | 43 -- nvim/lazy-lock.json | 100 +++ nvim/lua/core/event.lua | 152 +++++ nvim/lua/core/global.lua | 18 + nvim/lua/core/init.lua | 169 +++++ nvim/lua/core/mapping.lua | 65 ++ nvim/lua/core/options.lua | 128 ++++ nvim/lua/core/pack.lua | 138 +++++ nvim/lua/core/settings.lua | 225 +++++++ nvim/lua/keymap/bind.lua | 169 +++++ nvim/lua/keymap/completion.lua | 74 +++ nvim/lua/keymap/editor.lua | 109 ++++ nvim/lua/keymap/helpers.lua | 67 ++ nvim/lua/keymap/init.lua | 35 ++ nvim/lua/keymap/lang.lua | 12 + nvim/lua/keymap/tool.lua | 191 ++++++ nvim/lua/keymap/ui.lua | 105 ++++ .../lua/modules/configs/completion/aerial.lua | 80 +++ nvim/lua/modules/configs/completion/cmp.lua | 199 ++++++ .../modules/configs/completion/codeium.lua | 3 + .../configs/completion/copilot-cmp.lua | 3 + .../modules/configs/completion/copilot.lua | 28 + .../completion/formatters/clang_format.lua | 1 + .../modules/configs/completion/formatting.lua | 198 ++++++ .../lua/modules/configs/completion/glance.lua | 83 +++ .../configs/completion/lsp-signature.lua | 15 + nvim/lua/modules/configs/completion/lsp.lua | 23 + .../modules/configs/completion/lspsaga.lua | 183 ++++++ .../modules/configs/completion/luasnip.lua | 15 + .../configs/completion/mason-lspconfig.lua | 86 +++ .../configs/completion/mason-null-ls.lua | 12 + nvim/lua/modules/configs/completion/mason.lua | 94 +++ .../modules/configs/completion/neoconf.lua | 20 + .../modules/configs/completion/null-ls.lua | 77 +++ .../configs/completion/servers/bashls.lua | 5 + .../configs/completion/servers/clangd.lua | 79 +++ .../configs/completion/servers/dartls.lua | 12 + .../configs/completion/servers/gopls.lua | 50 ++ .../configs/completion/servers/html.lua | 12 + .../configs/completion/servers/jsonls.lua | 55 ++ .../configs/completion/servers/lua_ls.lua | 25 + .../configs/completion/servers/pylsp.lua | 46 ++ .../modules/configs/completion/tabnine.lua | 3 + nvim/lua/modules/configs/editor/align.lua | 11 + nvim/lua/modules/configs/editor/autoclose.lua | 39 ++ nvim/lua/modules/configs/editor/autotag.lua | 9 + nvim/lua/modules/configs/editor/bigfile.lua | 31 + nvim/lua/modules/configs/editor/comment.lua | 44 ++ nvim/lua/modules/configs/editor/diffview.lua | 11 + nvim/lua/modules/configs/editor/flash.lua | 37 ++ .../configs/editor/highlight-colors.lua | 31 + nvim/lua/modules/configs/editor/hop.lua | 3 + .../configs/editor/local-highlight.lua | 6 + nvim/lua/modules/configs/editor/persisted.lua | 23 + .../modules/configs/editor/rainbow_delims.lua | 53 ++ nvim/lua/modules/configs/editor/splits.lua | 14 + nvim/lua/modules/configs/editor/suda.lua | 5 + .../lua/modules/configs/editor/treesitter.lua | 66 ++ .../editor/ts-context-commentstring.lua | 7 + .../lua/modules/configs/editor/ts-context.lua | 12 + nvim/lua/modules/configs/lang/bqf.lua | 9 + .../modules/configs/lang/crates-keymap.lua | 126 ++++ nvim/lua/modules/configs/lang/crates.lua | 89 +++ nvim/lua/modules/configs/lang/go.lua | 21 + nvim/lua/modules/configs/lang/rust.lua | 12 + .../configs/tool/dap/clients/codelldb.lua | 47 ++ .../configs/tool/dap/clients/delve.lua | 100 +++ .../modules/configs/tool/dap/clients/lldb.lua | 36 ++ .../configs/tool/dap/clients/python.lua | 74 +++ .../modules/configs/tool/dap/dap-keymap.lua | 21 + nvim/lua/modules/configs/tool/dap/dapui.lua | 72 +++ nvim/lua/modules/configs/tool/dap/init.lua | 84 +++ nvim/lua/modules/configs/tool/dropbar.lua | 109 ++++ nvim/lua/modules/configs/tool/fcitx5.lua | 19 + nvim/lua/modules/configs/tool/nvim-tree.lua | 201 ++++++ nvim/lua/modules/configs/tool/project.lua | 13 + nvim/lua/modules/configs/tool/search.lua | 141 +++++ nvim/lua/modules/configs/tool/smartyank.lua | 24 + nvim/lua/modules/configs/tool/sniprun.lua | 27 + nvim/lua/modules/configs/tool/telescope.lua | 102 ++++ nvim/lua/modules/configs/tool/toggleterm.lua | 51 ++ nvim/lua/modules/configs/tool/trouble.lua | 38 ++ nvim/lua/modules/configs/tool/which-key.lua | 71 +++ nvim/lua/modules/configs/tool/wilder.lua | 65 ++ nvim/lua/modules/configs/ui/alpha.lua | 138 +++++ nvim/lua/modules/configs/ui/bufferline.lua | 73 +++ nvim/lua/modules/configs/ui/catppuccin.lua | 182 ++++++ nvim/lua/modules/configs/ui/fidget.lua | 25 + nvim/lua/modules/configs/ui/gitsigns.lua | 23 + .../modules/configs/ui/indent-blankline.lua | 578 ++++++++++++++++++ nvim/lua/modules/configs/ui/lualine.lua | 347 +++++++++++ nvim/lua/modules/configs/ui/neodim.lua | 37 ++ nvim/lua/modules/configs/ui/neoscroll.lua | 24 + nvim/lua/modules/configs/ui/notify.lua | 41 ++ nvim/lua/modules/configs/ui/paint.lua | 20 + nvim/lua/modules/configs/ui/scrollview.lua | 24 + nvim/lua/modules/configs/ui/todo.lua | 58 ++ nvim/lua/modules/plugins/completion.lua | 94 +++ nvim/lua/modules/plugins/editor.lua | 132 ++++ nvim/lua/modules/plugins/lang.lua | 40 ++ nvim/lua/modules/plugins/tool.lua | 140 +++++ nvim/lua/modules/plugins/ui.lua | 71 +++ nvim/lua/modules/utils/dap.lua | 32 + nvim/lua/modules/utils/icons.lua | 240 ++++++++ nvim/lua/modules/utils/init.lua | 370 +++++++++++ nvim/lua/modules/utils/keymap.lua | 205 +++++++ nvim/lua/user_template/configs/.gitkeep | 0 .../configs/dap-clients/.gitkeep | 0 .../user_template/configs/formatters/.gitkeep | 0 .../configs/lsp-servers/.gitkeep | 0 nvim/lua/user_template/event.lua | 8 + nvim/lua/user_template/keymap/completion.lua | 16 + nvim/lua/user_template/keymap/core.lua | 1 + nvim/lua/user_template/keymap/editor.lua | 1 + nvim/lua/user_template/keymap/init.lua | 9 + nvim/lua/user_template/keymap/lang.lua | 1 + nvim/lua/user_template/keymap/tool.lua | 1 + nvim/lua/user_template/keymap/ui.lua | 1 + nvim/lua/user_template/options.lua | 6 + nvim/lua/user_template/plugins/.gitkeep | 0 nvim/lua/user_template/settings.lua | 9 + nvim/nixos/default.nix | 7 + nvim/nixos/dotnet/default.nix | 43 ++ nvim/nixos/neovim/default.nix | 232 +++++++ nvim/nixos/testEnv.nix | 34 ++ nvim/scripts/install.ps1 | 416 +++++++++++++ nvim/scripts/install.sh | 304 +++++++++ nvim/snips/package.json | 270 ++++++++ nvim/snips/snippets/c.json | 11 + nvim/snips/snippets/cpp.json | 47 ++ nvim/snips/snippets/go.json | 17 + nvim/stylua.toml | 6 + nvim/tutor/dots.tutor | 157 +++++ 149 files changed, 10623 insertions(+), 43 deletions(-) create mode 100644 nvim/.github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 nvim/.github/ISSUE_TEMPLATE/config.yml create mode 100644 nvim/.github/ISSUE_TEMPLATE/custom_config.yml create mode 100644 nvim/.github/ISSUE_TEMPLATE/feature_request.yml create mode 100644 nvim/.github/ISSUE_TEMPLATE/lsp_issue_report.yml create mode 100644 nvim/.github/workflows/lint_code.yml create mode 100644 nvim/.github/workflows/style_check.yml create mode 100644 nvim/.github/workflows/update_flake.yml create mode 100644 nvim/.github/workflows/update_lockfile.yml create mode 100644 nvim/.gitignore create mode 100644 nvim/.nixd.json create mode 100644 nvim/LICENSE create mode 100644 nvim/README.md create mode 100644 nvim/flake.lock create mode 100644 nvim/flake.nix create mode 100644 nvim/init.lua delete mode 100644 nvim/init.vim create mode 100644 nvim/lazy-lock.json create mode 100644 nvim/lua/core/event.lua create mode 100644 nvim/lua/core/global.lua create mode 100644 nvim/lua/core/init.lua create mode 100644 nvim/lua/core/mapping.lua create mode 100644 nvim/lua/core/options.lua create mode 100644 nvim/lua/core/pack.lua create mode 100644 nvim/lua/core/settings.lua create mode 100644 nvim/lua/keymap/bind.lua create mode 100644 nvim/lua/keymap/completion.lua create mode 100644 nvim/lua/keymap/editor.lua create mode 100644 nvim/lua/keymap/helpers.lua create mode 100644 nvim/lua/keymap/init.lua create mode 100644 nvim/lua/keymap/lang.lua create mode 100644 nvim/lua/keymap/tool.lua create mode 100644 nvim/lua/keymap/ui.lua create mode 100644 nvim/lua/modules/configs/completion/aerial.lua create mode 100644 nvim/lua/modules/configs/completion/cmp.lua create mode 100644 nvim/lua/modules/configs/completion/codeium.lua create mode 100644 nvim/lua/modules/configs/completion/copilot-cmp.lua create mode 100644 nvim/lua/modules/configs/completion/copilot.lua create mode 100644 nvim/lua/modules/configs/completion/formatters/clang_format.lua create mode 100644 nvim/lua/modules/configs/completion/formatting.lua create mode 100644 nvim/lua/modules/configs/completion/glance.lua create mode 100644 nvim/lua/modules/configs/completion/lsp-signature.lua create mode 100644 nvim/lua/modules/configs/completion/lsp.lua create mode 100644 nvim/lua/modules/configs/completion/lspsaga.lua create mode 100644 nvim/lua/modules/configs/completion/luasnip.lua create mode 100644 nvim/lua/modules/configs/completion/mason-lspconfig.lua create mode 100644 nvim/lua/modules/configs/completion/mason-null-ls.lua create mode 100644 nvim/lua/modules/configs/completion/mason.lua create mode 100644 nvim/lua/modules/configs/completion/neoconf.lua create mode 100644 nvim/lua/modules/configs/completion/null-ls.lua create mode 100644 nvim/lua/modules/configs/completion/servers/bashls.lua create mode 100644 nvim/lua/modules/configs/completion/servers/clangd.lua create mode 100644 nvim/lua/modules/configs/completion/servers/dartls.lua create mode 100644 nvim/lua/modules/configs/completion/servers/gopls.lua create mode 100644 nvim/lua/modules/configs/completion/servers/html.lua create mode 100644 nvim/lua/modules/configs/completion/servers/jsonls.lua create mode 100644 nvim/lua/modules/configs/completion/servers/lua_ls.lua create mode 100644 nvim/lua/modules/configs/completion/servers/pylsp.lua create mode 100644 nvim/lua/modules/configs/completion/tabnine.lua create mode 100644 nvim/lua/modules/configs/editor/align.lua create mode 100644 nvim/lua/modules/configs/editor/autoclose.lua create mode 100644 nvim/lua/modules/configs/editor/autotag.lua create mode 100644 nvim/lua/modules/configs/editor/bigfile.lua create mode 100644 nvim/lua/modules/configs/editor/comment.lua create mode 100644 nvim/lua/modules/configs/editor/diffview.lua create mode 100644 nvim/lua/modules/configs/editor/flash.lua create mode 100644 nvim/lua/modules/configs/editor/highlight-colors.lua create mode 100644 nvim/lua/modules/configs/editor/hop.lua create mode 100644 nvim/lua/modules/configs/editor/local-highlight.lua create mode 100644 nvim/lua/modules/configs/editor/persisted.lua create mode 100644 nvim/lua/modules/configs/editor/rainbow_delims.lua create mode 100644 nvim/lua/modules/configs/editor/splits.lua create mode 100644 nvim/lua/modules/configs/editor/suda.lua create mode 100644 nvim/lua/modules/configs/editor/treesitter.lua create mode 100644 nvim/lua/modules/configs/editor/ts-context-commentstring.lua create mode 100644 nvim/lua/modules/configs/editor/ts-context.lua create mode 100644 nvim/lua/modules/configs/lang/bqf.lua create mode 100644 nvim/lua/modules/configs/lang/crates-keymap.lua create mode 100644 nvim/lua/modules/configs/lang/crates.lua create mode 100644 nvim/lua/modules/configs/lang/go.lua create mode 100644 nvim/lua/modules/configs/lang/rust.lua create mode 100644 nvim/lua/modules/configs/tool/dap/clients/codelldb.lua create mode 100644 nvim/lua/modules/configs/tool/dap/clients/delve.lua create mode 100644 nvim/lua/modules/configs/tool/dap/clients/lldb.lua create mode 100644 nvim/lua/modules/configs/tool/dap/clients/python.lua create mode 100644 nvim/lua/modules/configs/tool/dap/dap-keymap.lua create mode 100644 nvim/lua/modules/configs/tool/dap/dapui.lua create mode 100644 nvim/lua/modules/configs/tool/dap/init.lua create mode 100644 nvim/lua/modules/configs/tool/dropbar.lua create mode 100644 nvim/lua/modules/configs/tool/fcitx5.lua create mode 100644 nvim/lua/modules/configs/tool/nvim-tree.lua create mode 100644 nvim/lua/modules/configs/tool/project.lua create mode 100644 nvim/lua/modules/configs/tool/search.lua create mode 100644 nvim/lua/modules/configs/tool/smartyank.lua create mode 100644 nvim/lua/modules/configs/tool/sniprun.lua create mode 100644 nvim/lua/modules/configs/tool/telescope.lua create mode 100644 nvim/lua/modules/configs/tool/toggleterm.lua create mode 100644 nvim/lua/modules/configs/tool/trouble.lua create mode 100644 nvim/lua/modules/configs/tool/which-key.lua create mode 100644 nvim/lua/modules/configs/tool/wilder.lua create mode 100644 nvim/lua/modules/configs/ui/alpha.lua create mode 100644 nvim/lua/modules/configs/ui/bufferline.lua create mode 100644 nvim/lua/modules/configs/ui/catppuccin.lua create mode 100644 nvim/lua/modules/configs/ui/fidget.lua create mode 100644 nvim/lua/modules/configs/ui/gitsigns.lua create mode 100644 nvim/lua/modules/configs/ui/indent-blankline.lua create mode 100644 nvim/lua/modules/configs/ui/lualine.lua create mode 100644 nvim/lua/modules/configs/ui/neodim.lua create mode 100644 nvim/lua/modules/configs/ui/neoscroll.lua create mode 100644 nvim/lua/modules/configs/ui/notify.lua create mode 100644 nvim/lua/modules/configs/ui/paint.lua create mode 100644 nvim/lua/modules/configs/ui/scrollview.lua create mode 100644 nvim/lua/modules/configs/ui/todo.lua create mode 100644 nvim/lua/modules/plugins/completion.lua create mode 100644 nvim/lua/modules/plugins/editor.lua create mode 100644 nvim/lua/modules/plugins/lang.lua create mode 100644 nvim/lua/modules/plugins/tool.lua create mode 100644 nvim/lua/modules/plugins/ui.lua create mode 100644 nvim/lua/modules/utils/dap.lua create mode 100644 nvim/lua/modules/utils/icons.lua create mode 100644 nvim/lua/modules/utils/init.lua create mode 100644 nvim/lua/modules/utils/keymap.lua create mode 100644 nvim/lua/user_template/configs/.gitkeep create mode 100644 nvim/lua/user_template/configs/dap-clients/.gitkeep create mode 100644 nvim/lua/user_template/configs/formatters/.gitkeep create mode 100644 nvim/lua/user_template/configs/lsp-servers/.gitkeep create mode 100644 nvim/lua/user_template/event.lua create mode 100644 nvim/lua/user_template/keymap/completion.lua create mode 100644 nvim/lua/user_template/keymap/core.lua create mode 100644 nvim/lua/user_template/keymap/editor.lua create mode 100644 nvim/lua/user_template/keymap/init.lua create mode 100644 nvim/lua/user_template/keymap/lang.lua create mode 100644 nvim/lua/user_template/keymap/tool.lua create mode 100644 nvim/lua/user_template/keymap/ui.lua create mode 100644 nvim/lua/user_template/options.lua create mode 100644 nvim/lua/user_template/plugins/.gitkeep create mode 100644 nvim/lua/user_template/settings.lua create mode 100644 nvim/nixos/default.nix create mode 100644 nvim/nixos/dotnet/default.nix create mode 100644 nvim/nixos/neovim/default.nix create mode 100644 nvim/nixos/testEnv.nix create mode 100644 nvim/scripts/install.ps1 create mode 100755 nvim/scripts/install.sh create mode 100644 nvim/snips/package.json create mode 100644 nvim/snips/snippets/c.json create mode 100644 nvim/snips/snippets/cpp.json create mode 100644 nvim/snips/snippets/go.json create mode 100644 nvim/stylua.toml create mode 100644 nvim/tutor/dots.tutor diff --git a/nvim/.github/ISSUE_TEMPLATE/bug_report.yml b/nvim/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..4906f93 --- /dev/null +++ b/nvim/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,121 @@ +name: Bug Report +description: File a bug report +labels: [bug] +body: + - type: markdown + attributes: + value: | + _Before reporting:_ Search [existing issues](https://github.com/ayamir/nvimdots/issues) and check the [FAQ](https://github.com/ayamir/nvimdots/wiki/Issues). Thank you for helping us improve! + > [!IMPORTANT] + > The `0.11` branch is intended for nightly Neovim builds and is **not** stable. It typically harbors subtle issues scattered throughout. Therefore, refrain from submitting issues if you happen to encounter them. They will be closed directly unless a viable solution is proposed or included. + - type: checkboxes + id: is-latest-commit + attributes: + label: "Version confirmation" + description: "The local configuration is up-to-date in the current branch and this issue _persists_." + options: + - label: "Confirm" + required: true + - type: checkboxes + id: prerequisites-done + attributes: + label: "Following prerequisites" + description: "I've checked everything mentioned in [Wiki: Prerequisites](https://github.com/ayamir/nvimdots/wiki/Prerequisites)." + options: + - label: "Confirm" + required: true + - type: checkboxes + id: not-user-config-issue + attributes: + label: "Not a user config issue" + description: "This issue _persists_ after removing ALL user configs. If this is not the case, you should open a [Custom (User) Config Issue](https://github.com/ayamir/nvimdots/issues/new?assignees=&labels=usage&projects=&template=custom_config.yml) instead." + options: + - label: "Confirm" + required: true + + - type: input + id: nvim-version + attributes: + label: "Neovim version" + description: "Paste the output of `nvim --version` here" + placeholder: "NVIM v0.11.0-dev-194+g6c7677e5d" + validations: + required: true + - type: input + id: system-version + attributes: + label: "Operating system/version" + placeholder: "macOS 12.6.1" + validations: + required: true + - type: input + id: terminal-version + attributes: + label: "Terminal name/version" + placeholder: "iTerm2 Build 3.4.17" + validations: + required: true + - type: input + id: term-env + attributes: + label: "$TERM environment variable" + placeholder: "xterm-256color" + validations: + required: false + - type: dropdown + id: branch + attributes: + label: "Branch info" + description: "Which branch are you currently on? If you are not sure, check the output of `git rev-parse --abbrev-ref HEAD`" + options: + - main (Default/Latest) + - 0.9 (Legacy) + - 0.8 (Deprecated) + - 0.11 (Nightly) + validations: + required: true + - type: dropdown + id: fetch-pref + attributes: + label: "Fetch Preferences" + description: "In what way do you fetch resources? If you are not sure, check the value of `use_ssh` in `lua/user/settings.lua`" + options: + - SSH (use_ssh = true) + - HTTPS (use_ssh = false) + validations: + required: true + + - type: textarea + id: repro-steps + attributes: + label: "How to reproduce the issue" + description: "Note: Issues without any information will be closed directly" + placeholder: | + Steps to reproduce the behavior: + 1. Go to '...' + 2. Click on '....' + 3. Scroll down to '....' + 4. See error + validations: + required: true + + - type: textarea + id: expected-behavior + attributes: + label: "Expected behavior" + description: "Describe the behavior you expect" + validations: + required: true + - type: textarea + id: actual-behavior + attributes: + label: "Actual behavior" + validations: + required: false + - type: textarea + id: extras + attributes: + label: Additional information + description: If applicable, you may include logs, images, or videos to help explain your problem + validations: + required: false diff --git a/nvim/.github/ISSUE_TEMPLATE/config.yml b/nvim/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..1c7e924 --- /dev/null +++ b/nvim/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: true +contact_links: + - name: GitHub Discussions + url: https://github.com/ayamir/nvimdots/discussions/new/choose + about: Any issue that does not fall into the above categories shall go here + - name: GitHub Wiki + url: https://github.com/ayamir/nvimdots/wiki + about: Make sure you have checked our documentation first. To be explicit, the "Issues" section \ No newline at end of file diff --git a/nvim/.github/ISSUE_TEMPLATE/custom_config.yml b/nvim/.github/ISSUE_TEMPLATE/custom_config.yml new file mode 100644 index 0000000..6a95d39 --- /dev/null +++ b/nvim/.github/ISSUE_TEMPLATE/custom_config.yml @@ -0,0 +1,111 @@ +name: Custom (User) Config Issue +description: Problems when trying to implement your custom config +labels: [usage] +body: + - type: markdown + attributes: + value: | + _Before requesting:_ Make sure you've read through our [Wiki: Usage](https://github.com/ayamir/nvimdots/wiki/Usage) before you start to add things to nvimdots! + > [!IMPORTANT] + > The `0.11` branch is intended for nightly Neovim builds and is **not** stable. It typically harbors subtle issues scattered throughout. Therefore, refrain from submitting issues if you happen to encounter them. They will be closed directly unless a viable solution is proposed or included. + - type: checkboxes + id: is-latest-commit + attributes: + label: "Version confirmation" + description: "The local configuration is up-to-date in the current branch and this issue _persists_." + options: + - label: "Confirm" + required: true + - type: checkboxes + id: prerequisites-done + attributes: + label: "Following prerequisites" + description: "I've checked everything mentioned in [Wiki: Prerequisites](https://github.com/ayamir/nvimdots/wiki/Prerequisites)." + options: + - label: "Confirm" + required: true + + - type: input + id: nvim-version + attributes: + label: "Neovim version" + description: "Paste the output of `nvim --version` here" + placeholder: "NVIM v0.11.0-dev-194+g6c7677e5d" + validations: + required: true + - type: dropdown + id: branch-info + attributes: + label: "Branch info" + description: "This issue template mainly targets `main` branch. Check the output of `git rev-parse --abbrev-ref HEAD` if you're not sure." + options: + - main (Default/Latest) + - 0.9 (Legacy) + - 0.8 (Deprecated) + - 0.11 (Nightly) + validations: + required: true + + - type: textarea + id: folder-structure-ta + attributes: + label: "Minimal (user) folder structure required to reproduce the issue" + description: "Note: You only need to describe where the *new/modified files* are. This section will be automatically formatted." + render: console + placeholder: | + └── lua/ + ├── core/ + ├── keymap/ default keymaps + ├── modules/ default plugins and plugin configs + └── user custom configs root directory + ├── configs/ custom plugin config folder + │ ├── dap-clients/ custom dap client config folder + │ ├── lsp-servers/ custom lsp server config folder + │ └── your-config.lua your plugin configs (if applicable) + ├── keymap/ custom keymap folder + │ └── your-config.lua your keymap overrides (if applicable) + ├── plugins/ custom plugin folder + │ └── your-config.lua your plugins (if applicable) + ├── event.lua custom `core/events.lua` overrides + ├── options.lua custom `core/options.lua` overrides + └── settings.lua custom `core/settings.lua` overrides + validations: + required: true + + - type: textarea + id: repro-steps + attributes: + label: "Minimal config with steps on how to reproduce the issue" + description: "Note: Issues without any information will be closed directly" + placeholder: | + This is my custom config (`specs.lua`): + ```lua + return { + popup = { + delay_ms = 20, + } + } + ``` + + Steps to reproduce the behavior: + 1. Go to '...' + 2. Click on '....' + 3. Scroll down to '....' + 4. See error + validations: + required: true + + - type: textarea + id: expected-behavior + attributes: + label: "Expected behavior" + description: "Describe the behavior you expect" + validations: + required: true + - type: textarea + id: extras + attributes: + label: Additional information + description: If applicable, you may include logs, images, or videos to help explain your problem + validations: + required: false diff --git a/nvim/.github/ISSUE_TEMPLATE/feature_request.yml b/nvim/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..47fc1ae --- /dev/null +++ b/nvim/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,22 @@ +name: Feature Request +description: Request an enhancement for this project +labels: [enhancement] +body: + - type: markdown + attributes: + value: | + _Before requesting:_ Search [existing issues](https://github.com/ayamir/nvimdots/labels/enhancement) and check the [FAQ](https://github.com/ayamir/nvimdots/wiki/Issues). Thank you for helping us improve! + + - type: textarea + id: feat-desc + attributes: + label: "Feature description" + validations: + required: true + - type: textarea + id: extras + attributes: + label: "Additional information" + description: "If applicable, you may include images or videos to help explain your request" + validations: + required: false diff --git a/nvim/.github/ISSUE_TEMPLATE/lsp_issue_report.yml b/nvim/.github/ISSUE_TEMPLATE/lsp_issue_report.yml new file mode 100644 index 0000000..0997861 --- /dev/null +++ b/nvim/.github/ISSUE_TEMPLATE/lsp_issue_report.yml @@ -0,0 +1,148 @@ +name: LSP Issue +description: File a bug report related to LSPs +labels: [bug, lsp] +body: + - type: markdown + attributes: + value: | + _Before reporting:_ Search [existing issues](https://github.com/ayamir/nvimdots/issues) and check the [FAQ](https://github.com/ayamir/nvimdots/wiki/Issues). Thank you for helping us improve! + > [!IMPORTANT] + > The `0.11` branch is intended for nightly Neovim builds and is **not** stable. It typically harbors subtle issues scattered throughout. Therefore, refrain from submitting issues if you happen to encounter them. They will be closed directly unless a viable solution is proposed or included. + - type: checkboxes + id: is-latest-commit + attributes: + label: "Version confirmation" + description: "The local configuration is up-to-date in the current branch and this issue _persists_." + options: + - label: "Confirm" + required: true + - type: checkboxes + id: prerequisites-done + attributes: + label: "Following prerequisites" + description: "I've checked everything mentioned in [Wiki: Prerequisites](https://github.com/ayamir/nvimdots/wiki/Prerequisites)." + options: + - label: "Confirm" + required: true + - type: checkboxes + id: not-user-config-issue + attributes: + label: "Not a user config issue" + description: "This issue _persists_ after removing ALL user configs. If this is not the case, you should open a [Custom (User) Config Issue](https://github.com/ayamir/nvimdots/issues/new?assignees=&labels=usage&projects=&template=custom_config.yml) instead." + options: + - label: "Confirm" + required: true + + - type: input + id: nvim-version + attributes: + label: "Neovim version" + description: "Paste the output of `nvim --version` here" + placeholder: "NVIM v0.11.0-dev-194+g6c7677e5d" + validations: + required: true + - type: input + id: system-version + attributes: + label: "Operating system/version" + placeholder: "macOS 12.6.1" + validations: + required: true + - type: input + id: terminal-version + attributes: + label: "Terminal name/version" + placeholder: "iTerm2 Build 3.4.17" + validations: + required: true + - type: input + id: term-env + attributes: + label: "$TERM environment variable" + placeholder: "xterm-256color" + validations: + required: false + - type: dropdown + id: branch + attributes: + label: "Branch info" + description: "Which branch are you currently on? If you are not sure, check the output of `git rev-parse --abbrev-ref HEAD`" + options: + - main (Default/Latest) + - 0.9 (Legacy) + - 0.8 (Deprecated) + - 0.11 (Nightly) + validations: + required: true + - type: dropdown + id: fetch-pref + attributes: + label: "Fetch Preferences" + description: "In what way do you fetch resources? If you are not sure, check the value of `use_ssh` in `lua/user/settings.lua`" + options: + - SSH (use_ssh = true) + - HTTPS (use_ssh = false) + validations: + required: true + - type: input + id: servers + attributes: + label: "Affected language servers" + description: "If this issue is specific to one or more language servers, list them here. If not, write `any`" + placeholder: "lua-language-server, clangd" + validations: + required: true + + - type: textarea + id: repro-steps + attributes: + label: "How to reproduce the issue" + description: "Note: Issues without any information will be closed directly" + placeholder: | + Steps to reproduce the behavior: + 1. Go to '...' + 2. Click on '....' + 3. Scroll down to '....' + 4. See error + validations: + required: true + + - type: textarea + id: actual-behavior + attributes: + label: "Actual behavior" + description: "Briefly describe what you've observed" + validations: + required: true + - type: textarea + id: expected-behavior + attributes: + label: "Expected behavior" + description: "Describe the behavior you expect" + validations: + required: false + - type: textarea + id: support-info + attributes: + label: "Support info" + description: "Plase include information (screenshots) from `:LspInfo` and `:Mason`" + placeholder: | + :LspInfo + :Mason + validations: + required: true + - type: textarea + id: lsp-logs + attributes: + label: "Logs" + description: "Please copy and paste any relevant log output (e.g., `${HOME}/.local/state/nvim/lsp.log` and `${HOME}/.cache/nvim/lsp.log`). This section will be automatically formatted" + render: console + validations: + required: false + - type: textarea + id: extras + attributes: + label: "Additional information" + description: "If applicable, you may include images or videos to help explain your problem" + validations: + required: false diff --git a/nvim/.github/workflows/lint_code.yml b/nvim/.github/workflows/lint_code.yml new file mode 100644 index 0000000..71d11c5 --- /dev/null +++ b/nvim/.github/workflows/lint_code.yml @@ -0,0 +1,11 @@ +name: lint code +on: [push, pull_request] + +jobs: + luacheck: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: lunarmodules/luacheck@v1 + with: + args: . --std luajit --globals vim _debugging _command_panel _flash_esc_or_noh _telescope_collections _toggle_lazygit --max-line-length 150 --no-config diff --git a/nvim/.github/workflows/style_check.yml b/nvim/.github/workflows/style_check.yml new file mode 100644 index 0000000..3c7936d --- /dev/null +++ b/nvim/.github/workflows/style_check.yml @@ -0,0 +1,13 @@ +name: style check +on: [push, pull_request] + +jobs: + stylua: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: JohnnyMorganz/stylua-action@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + version: latest + args: --check --config-path=stylua.toml . diff --git a/nvim/.github/workflows/update_flake.yml b/nvim/.github/workflows/update_flake.yml new file mode 100644 index 0000000..9d9e31c --- /dev/null +++ b/nvim/.github/workflows/update_flake.yml @@ -0,0 +1,26 @@ +name: update flake.lock +on: + # Scheduled update (1st of every month) + schedule: [{ cron: "30 02 1 * *" }] + +jobs: + update-lockfile: + if: github.repository_owner == 'ayamir' + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + - uses: cachix/install-nix-action@v26 + with: + nix_path: nixpkgs=channel:nixos-unstable + - name: Run flake-update + run: | + nix flake update + - uses: stefanzweifel/git-auto-commit-action@v5 + with: + commit_message: "chore(lockfile): auto update flake.lock" + commit_user_name: "github-actions[bot]" + commit_user_email: "41898282+github-actions[bot]@users.noreply.github.com" + commit_author: "github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>" + file_pattern: "flake.lock" diff --git a/nvim/.github/workflows/update_lockfile.yml b/nvim/.github/workflows/update_lockfile.yml new file mode 100644 index 0000000..200e019 --- /dev/null +++ b/nvim/.github/workflows/update_lockfile.yml @@ -0,0 +1,41 @@ +name: update lockfile +on: + # Scheduled update (each day) + schedule: [{ cron: "30 01 * * *" }] + +jobs: + update-lockfile: + if: github.repository_owner == 'ayamir' + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # Required to count the commits + - uses: andstor/file-existence-action@v3 + id: check_lockfile + with: + files: "lazy-lock.json" + - name: Run count-new-commits + run: | + echo "NEW_COMMIT_COUNT=$(git log --oneline --since '24 hours ago' --perl-regexp --author='^((?!github-actions).*)$' | wc -l)" >> "$GITHUB_ENV" + - uses: rhysd/action-setup-vim@v1 + if: ${{ steps.check_lockfile.outputs.files_exists == 'true' && env.NEW_COMMIT_COUNT > 0 }} + with: + neovim: true + - name: Run lockfile-autoupdate + if: ${{ steps.check_lockfile.outputs.files_exists == 'true' && env.NEW_COMMIT_COUNT > 0 }} + timeout-minutes: 5 + run: | + ./scripts/install.sh + nvim --headless "+Lazy! update" +qa + cp -pv "${HOME}/.config/nvim/lazy-lock.json" . + - uses: stefanzweifel/git-auto-commit-action@v5 + if: ${{ steps.check_lockfile.outputs.files_exists == 'true' && env.NEW_COMMIT_COUNT > 0 }} + with: + commit_message: "chore(lockfile): auto update lazy-lock.json" + commit_user_name: "github-actions[bot]" + commit_user_email: "41898282+github-actions[bot]@users.noreply.github.com" + commit_author: "github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>" + file_pattern: "lazy-lock.json" diff --git a/nvim/.gitignore b/nvim/.gitignore new file mode 100644 index 0000000..51c6018 --- /dev/null +++ b/nvim/.gitignore @@ -0,0 +1,4 @@ +lua/modules/plugins/custom.lua +lua/modules/configs/custom +lua/user +result diff --git a/nvim/.nixd.json b/nvim/.nixd.json new file mode 100644 index 0000000..f8bee3e --- /dev/null +++ b/nvim/.nixd.json @@ -0,0 +1,7 @@ +{ + "formatting": { + "command": [ + "nixpkgs-fmt" + ] + } +} diff --git a/nvim/LICENSE b/nvim/LICENSE new file mode 100644 index 0000000..33895b6 --- /dev/null +++ b/nvim/LICENSE @@ -0,0 +1,30 @@ +BSD 3-Clause License + +Copyright (c) 2021 ayamir +Copyright (c) 2022 Jint-lzxy, CharlesChiuGit +Copyright (c) 2023 aarnphm, misumisumi + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/nvim/README.md b/nvim/README.md new file mode 100644 index 0000000..f4af58f --- /dev/null +++ b/nvim/README.md @@ -0,0 +1,216 @@ +

+ nvimdots +
+ + Neovim Version Capability + + + Release + +

+ +

+ + Stars + + + Issues + + + Contributors + + Code Size +

+ +## 🪷 Introduction + +This repo hosts our [Neovim](https://neovim.io/) configuration for Linux [(with NixOS support)](#nixos-support), macOS, and Windows. `init.lua` is the config entry point. + +Branch info: + +
+ +| Branch | Supported Neovim version | +| :----: | :----------------------: | +| main | nvim 0.10 stable | +| 0.11 | nvim 0.11 nightly | +| 0.9 | nvim 0.9 | +| 0.8 | nvim 0.8 | + +
+ +> [!IMPORTANT] +> The `0.11` branch is intended for nightly Neovim builds and is **not** stable. It typically harbors subtle issues scattered throughout. Therefore, refrain from submitting issues if you happen to encounter them. They will be closed directly unless a viable solution is proposed or included. + +We currently manage plugins using [lazy.nvim](https://github.com/folke/lazy.nvim). + +Chinese introduction is [here](https://zhuanlan.zhihu.com/p/382092667). + +### 🎐 Features + +- **Fast.** Less than **50ms** to start (Depends on SSD and CPU, tested on Zephyrus G14 2022 version). +- **Simple.** Runs out of the box. +- **Modern.** Pure `lua` config. +- **Modular.** Easy to customize. +- **Powerful.** Full functionality to code. + +## 🏗 How to Install + +Simply run the following interactive bootstrap command, and you should be all set 👍 + +- **Windows** _(Note: This script REQUIRES `pwsh` > `v7.1`)_ + +```pwsh +Set-ExecutionPolicy Bypass -Scope Process -Force; Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/ayamir/nvimdots/HEAD/scripts/install.ps1')) +``` + +- **\*nix** + +```sh +if command -v curl >/dev/null 2>&1; then + bash -c "$(curl -fsSL https://raw.githubusercontent.com/ayamir/nvimdots/HEAD/scripts/install.sh)" +else + bash -c "$(wget -O- https://raw.githubusercontent.com/ayamir/nvimdots/HEAD/scripts/install.sh)" +fi +``` + +It's strongly recommended to read [Wiki: Prerequisites](https://github.com/ayamir/nvimdots/wiki/Prerequisites) before starting, especially for \*nix users. + +## ⚙️ Configuration & Usage + +

+ 🗺️ Keybindings +

+

See Wiki: Keybindings for details

+
+ +

+ 🔌 Plugins & Deps +

+

See Wiki: Plugins for details
(You can also find a deps diagram there!)

+
+ +

+ 🔧 Usage & Customization +

+

See Wiki: Usage for details

+
+ +

+ ❄️ NixOS Support +

+

See Wiki: NixOS Support for details

+
+ +

+ 🤔 FAQ +

+

See Wiki: FAQ for details

+ +## ✨ Features + +

+ ⏱️ Startup Time +

+ +

+ StartupTime +

+ +

+ Vim-StartupTime +

+ +> Tested with [rhysd/vim-startuptime](https://github.com/rhysd/vim-startuptime) + +

+ 📸 Screenshots +

+ +

+ Dashboard + Dashboard +

+
+ +

+ Telescope + Telescope +

+
+ +

+ Coding + Coding +

+
+ +

+ Code Action + Code Action +

+
+ +

+ Debugging + Debugging +

+
+ +

+ Lazygit + Lazygit with built-in Terminal +

+
+ +

+ Command quickref + Command quickref +

+ +## 👐 Contributing + +- If you find anything that needs improving, do not hesitate to point it out or create a PR. +- If you come across an issue, you can first use `:checkhealth` command provided by nvim to trouble-shoot yourself. + - If you still have such problems, feel free to open a new issue! + +## ❤️ Thanks to + +- [ayamir](https://github.com/ayamir) +- [Jint-lzxy](https://github.com/Jint-lzxy) +- [CharlesChiuGit](https://github.com/CharlesChiuGit) +- [aarnphm](https://github.com/aarnphm) +- [misumisumi](https://github.com/misumisumi) + +## 🎉 Acknowledgement + +- [glepnir/nvim](https://github.com/glepnir/nvim) + +## 📜 License + +This Neovim configuration is released under the BSD 3-Clause license, which grants the following permissions: + +- Commercial use +- Distribution +- Modification +- Private use + +For more convoluted language, see the [LICENSE](https://github.com/ayamir/nvimdots/blob/main/LICENSE). diff --git a/nvim/flake.lock b/nvim/flake.lock new file mode 100644 index 0000000..c62dd92 --- /dev/null +++ b/nvim/flake.lock @@ -0,0 +1,148 @@ +{ + "nodes": { + "devshell": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + }, + "locked": { + "lastModified": 1717408969, + "narHash": "sha256-Q0OEFqe35fZbbRPPRdrjTUUChKVhhWXz3T9ZSKmaoVY=", + "owner": "numtide", + "repo": "devshell", + "rev": "1ebbe68d57457c8cae98145410b164b5477761f4", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "devshell", + "type": "github" + } + }, + "flake-parts": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "lastModified": 1719745305, + "narHash": "sha256-xwgjVUpqSviudEkpQnioeez1Uo2wzrsMaJKJClh+Bls=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "c3c5ecc05edc7dafba779c6c1a61cd08ac6583e9", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1701680307, + "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "4022d587cbbfd70fe950c1e2083a02621806a725", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "home-manager": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1719677234, + "narHash": "sha256-qO9WZsj/0E6zcK4Ht1y/iJ8XfwbBzq7xdqhBh44OP/M=", + "owner": "nix-community", + "repo": "home-manager", + "rev": "36317d4d38887f7629876b0e43c8d9593c5cc48d", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "home-manager", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1704161960, + "narHash": "sha256-QGua89Pmq+FBAro8NriTuoO/wNaUtugt29/qqA8zeeM=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "63143ac2c9186be6d9da6035fa22620018c85932", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-lib": { + "locked": { + "lastModified": 1717284937, + "narHash": "sha256-lIbdfCsf8LMFloheeE6N31+BMIeixqyQWbSr2vk79EQ=", + "type": "tarball", + "url": "https://github.com/NixOS/nixpkgs/archive/eb9ceca17df2ea50a250b6b27f7bf6ab0186f198.tar.gz" + }, + "original": { + "type": "tarball", + "url": "https://github.com/NixOS/nixpkgs/archive/eb9ceca17df2ea50a250b6b27f7bf6ab0186f198.tar.gz" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1719468428, + "narHash": "sha256-vN5xJAZ4UGREEglh3lfbbkIj+MPEYMuqewMn4atZFaQ=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "1e3deb3d8a86a870d925760db1a5adecc64d329d", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "devshell": "devshell", + "flake-parts": "flake-parts", + "home-manager": "home-manager", + "nixpkgs": "nixpkgs_2" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/nvim/flake.nix b/nvim/flake.nix new file mode 100644 index 0000000..9dd3840 --- /dev/null +++ b/nvim/flake.nix @@ -0,0 +1,105 @@ +{ + # This provides only NixOS module + # As of 2023/08/19, you need to depend on nixpkgs-unstable. + # because "doq" is not included in the stable version. + description = "Provide nixosModules for ayamir/nvimdots"; + + inputs = { + flake-parts.url = "github:hercules-ci/flake-parts"; + devshell.url = "github:numtide/devshell"; + nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; + home-manager = { + url = "github:nix-community/home-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = inputs @ { self, flake-parts, ... }: + flake-parts.lib.mkFlake { inherit inputs; } + { + imports = [ + inputs.devshell.flakeModule + ]; + flake = { + homeManagerModules = { + nvimdots = ./nixos; + }; + }; + systems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ]; + perSystem = { pkgs, system, ... }: { + packages = { + testEnv = (import ./nixos/testEnv.nix { inherit inputs pkgs; }).activationPackage; + check-linker = pkgs.writeShellApplication { + name = "check-linker"; + text = + let + ldd_cmd = if pkgs.stdenv.isDarwin then "xcrun otool -L" else "${pkgs.glibc.bin}/bin/ldd"; + in + '' + #shellcheck disable=SC1090 + source <(sed -ne :1 -e 'N;1,1b1' -e 'P;D' "${self.packages.${system}.testEnv}/home-path/bin/nvim") + echo "Checking files under ''${XDG_DATA_HOME}/''${NVIM_APPNAME:-nvim}/mason/bin..." + find "''${XDG_DATA_HOME}/''${NVIM_APPNAME:-nvim}/mason/bin" -type l | while read -r link; do + "${ldd_cmd}" "$(readlink -f "$link")" > /dev/zero 2>&1 || continue + linkers=$("${ldd_cmd}" "$(readlink -f "$link")" | tail -n+2) + echo "$linkers" | while read -r line; do + [ -z "$line" ] && continue + echo "$line" | grep -q "/nix/store" || printf '%s: %s does not link to /nix/store \n' "$(basename "$link")" "$line" + done + done + echo "*** Done ***" + ''; + }; + }; + devshells.default = { + commands = [ + { + help = "neovim linked to testEnv."; + name = "nvim"; + command = '' + ${self.packages.${system}.testEnv}/home-path/bin/nvim + ''; + } + { + help = "check-linker"; + package = self.packages.${system}.check-linker; + } + ]; + devshell = { + motd = '' + {202}🔨 Welcome to devshell{reset} + Symlink configs to "''${XDG_CONFIG_HOME}"/nvimdots! + And NVIM_APPNAME=nvimdots is already configured, so neovim will put files under "\$XDG_xxx_HOME"/nvimdots. + To uninstall, remove "\$XDG_xxx_HOME"/nvimdots. + + $(type -p menu &>/dev/null && menu) + ''; + startup = { + mkNvimDir = { + text = '' + mkdir -p "''${XDG_CONFIG_HOME}"/nvimdots + for path in lazy-lock.json init.lua lua snips tutor; do + ln -sf "''${PWD}/''${path}" "''${XDG_CONFIG_HOME}"/nvimdots/ + done + ''; + }; + }; + }; + env = [ + { + name = "NVIM_APPNAME"; + value = "nvimdots"; + } + { + name = "PATH"; + prefix = "${self.packages.${system}.testEnv}/home-path/bin"; + } + ]; + packages = with pkgs; [ + nixd + nixpkgs-fmt + ]; + }; + }; + }; +} diff --git a/nvim/init.lua b/nvim/init.lua new file mode 100644 index 0000000..1544509 --- /dev/null +++ b/nvim/init.lua @@ -0,0 +1,3 @@ +if not vim.g.vscode then + require("core") +end diff --git a/nvim/init.vim b/nvim/init.vim deleted file mode 100644 index 7aeaed9..0000000 --- a/nvim/init.vim +++ /dev/null @@ -1,43 +0,0 @@ -" _ _ _ -" | \ | | ___ _____ _(_)_ __ ___ -" | \| |/ _ \/ _ \ \ / / | '_ ` _ \ -" | |\ | __/ (_) \ V /| | | | | | | -" |_| \_|\___|\___/ \_/ |_|_| |_| |_| -" -" by Stephan Raabe (2023) -" ----------------------------------------------------- - -set nocompatible " disable compatibility to old-time vi -set showmatch " show matching -set ignorecase " case insensitive -set mouse=v " middle-click paste with -set hlsearch " highlight search -set incsearch " incremental search -set tabstop=4 " number of columns occupied by a tab -set softtabstop=4 " see multiple spaces as tabstops so does the right thing -set expandtab " converts tabs to white space -set shiftwidth=4 " width for autoindents -set autoindent " indent a new line the same amount as the line just typed -set number " add line numbers -set wildmode=longest,list " get bash-like tab completions -" set cc=80 " set an 80 column border for good coding style -filetype plugin indent on "allow auto-indenting depending on file type -syntax on " syntax highlighting -set mouse=a " enable mouse click -set clipboard=unnamedplus " using system clipboard -filetype plugin on -" set cursorline " highlight current cursorline -set ttyfast " Speed up scrolling in Vim -" set spell " enable spell check (may need to download language package) -" set noswapfile " disable creating swap file -" set backupdir=~/.cache/vim " Directory to store backup files. - -hi NonText ctermbg=none guibg=NONE -hi Normal guibg=NONE ctermbg=NONE -hi NormalNC guibg=NONE ctermbg=NONE -hi SignColumn ctermbg=NONE ctermfg=NONE guibg=NONE - -hi Pmenu ctermbg=NONE ctermfg=NONE guibg=NONE -hi FloatBorder ctermbg=NONE ctermfg=NONE guibg=NONE -hi NormalFloat ctermbg=NONE ctermfg=NONE guibg=NONE -hi TabLine ctermbg=None ctermfg=None guibg=None diff --git a/nvim/lazy-lock.json b/nvim/lazy-lock.json new file mode 100644 index 0000000..861dfdb --- /dev/null +++ b/nvim/lazy-lock.json @@ -0,0 +1,100 @@ +{ + "Comment.nvim": { "branch": "master", "commit": "e30b7f2008e52442154b66f7c519bfd2f1e32acb" }, + "LuaSnip": { "branch": "master", "commit": "ce0a05ab4e2839e1c48d072c5236cce846a387bc" }, + "advanced-git-search.nvim": { "branch": "main", "commit": "198cc402af1790ab26830fdbf24a28c336a20ba6" }, + "aerial.nvim": { "branch": "master", "commit": "4e77964569ef47a70f9bb76c668dcfea2d089d5a" }, + "alpha-nvim": { "branch": "main", "commit": "41283fb402713fc8b327e60907f74e46166f4cfd" }, + "autoclose.nvim": { "branch": "main", "commit": "dc42806540dcf448ecb2bad6b67204410cfbe629" }, + "bigfile.nvim": { "branch": "main", "commit": "33eb067e3d7029ac77e081cfe7c45361887a311a" }, + "bufferline.nvim": { "branch": "main", "commit": "0b2fd861eee7595015b6561dade52fb060be10c4" }, + "catppuccin": { "branch": "refactor/syntax-highlighting", "commit": "87ef3315b10ddee9e82e5ec7d21bf5f24db1a20f" }, + "cmp-buffer": { "branch": "main", "commit": "3022dbc9166796b644a841a02de8dd1cc1d311fa" }, + "cmp-latex-symbols": { "branch": "main", "commit": "165fb66afdbd016eaa1570e41672c4c557b57124" }, + "cmp-nvim-lsp": { "branch": "main", "commit": "39e2eda76828d88b773cc27a3f61d2ad782c922d" }, + "cmp-nvim-lua": { "branch": "main", "commit": "f12408bdb54c39c23e67cab726264c10db33ada8" }, + "cmp-path": { "branch": "main", "commit": "91ff86cd9c29299a64f968ebb45846c485725f23" }, + "cmp-spell": { "branch": "master", "commit": "694a4e50809d6d645c1ea29015dad0c293f019d6" }, + "cmp-tmux": { "branch": "main", "commit": "95b1b921802e6f60627b3e76afb9380fddd87f9a" }, + "cmp-treesitter": { "branch": "master", "commit": "c8e3a74b51597d69d240085a258636972ce98e15" }, + "cmp-under-comparator": { "branch": "master", "commit": "6857f10272c3cfe930cece2afa2406e1385bfef8" }, + "cmp_luasnip": { "branch": "master", "commit": "05a9ab28b53f71d1aece421ef32fee2cb857a843" }, + "copilot-cmp": { "branch": "master", "commit": "b6e5286b3d74b04256d0a7e3bd2908eabec34b44" }, + "copilot.lua": { "branch": "master", "commit": "86537b286f18783f8b67bccd78a4ef4345679625" }, + "crates.nvim": { "branch": "main", "commit": "c3fd47391de6999f4c939af89494d08443f71916" }, + "csv.vim": { "branch": "master", "commit": "4d5255829afe3b6badb0c8a040116704c0d3213c" }, + "diffview.nvim": { "branch": "main", "commit": "4516612fe98ff56ae0415a259ff6361a89419b0a" }, + "dropbar.nvim": { "branch": "master", "commit": "cab5e0226bc4f90a28cadcb0dc7341d550f1bdf0" }, + "fidget.nvim": { "branch": "main", "commit": "d855eed8a06531a7e8fd0684889b2943f373c469" }, + "flash.nvim": { "branch": "main", "commit": "25ba3f4d1e0b080213b39518ebcfbb693a23c957" }, + "friendly-snippets": { "branch": "main", "commit": "00ebcaa159e817150bd83bfe2d51fa3b3377d5c4" }, + "fzf": { "branch": "master", "commit": "50fa90dfb8eab7f03a7602ea9c098b01ff7236e8" }, + "fzy-lua-native": { "branch": "master", "commit": "820f745b7c442176bcc243e8f38ef4b985febfaf" }, + "gitsigns.nvim": { "branch": "main", "commit": "f4928ba14eb6c667786ac7d69927f6aee6719f1e" }, + "glance.nvim": { "branch": "master", "commit": "51059bcf21016387b6233c89eed220cf47fca752" }, + "go.nvim": { "branch": "master", "commit": "033344ddfa3cd5cfd55037903264b2bb86691619" }, + "guihua.lua": { "branch": "master", "commit": "225db770e36aae6a1e9e3a65578095c8eb4038d3" }, + "hop.nvim": { "branch": "master", "commit": "036462a345792279c58f2f6445756efab706f04a" }, + "indent-blankline.nvim": { "branch": "master", "commit": "65e20ab94a26d0e14acac5049b8641336819dfc7" }, + "lazy.nvim": { "branch": "main", "commit": "9a374a0fb4d3ac42dac4a129d4bead7252473c77" }, + "local-highlight.nvim": { "branch": "master", "commit": "ae3ada3a332128b1036c84c8587b9069891c63da" }, + "lsp-format-modifications.nvim": { "branch": "main", "commit": "006d4cd88f4f09fdc4375fcb75dd5b7d981a723b" }, + "lsp_signature.nvim": { "branch": "master", "commit": "e6d225e27770db81346c2f2aa6c30d0e249f9e7f" }, + "lspsaga.nvim": { "branch": "main", "commit": "6f920cfabddb9b7de5a3a4d0b7cd4f0774ae23e2" }, + "lualine.nvim": { "branch": "master", "commit": "544dd1583f9bb27b393f598475c89809c4d5e86b" }, + "markdown-preview.nvim": { "branch": "master", "commit": "a923f5fc5ba36a3b17e289dc35dc17f66d0548ee" }, + "mason-lspconfig.nvim": { "branch": "main", "commit": "58bc9119ca273c0ce5a66fad1927ef0f617bd81b" }, + "mason-null-ls.nvim": { "branch": "main", "commit": "de19726de7260c68d94691afb057fa73d3cc53e7" }, + "mason-nvim-dap.nvim": { "branch": "main", "commit": "4ba55f9755ebe8297d92c419b90a946123292ae6" }, + "mason.nvim": { "branch": "main", "commit": "e2f7f9044ec30067bc11800a9e266664b88cda22" }, + "mini.align": { "branch": "main", "commit": "b54d88b16bfac62ca6b3d699566135639a3ca4fa" }, + "neoconf.nvim": { "branch": "main", "commit": "57422353819051a67582d268d1557ed56d03d3a3" }, + "neodim": { "branch": "master", "commit": "0543414f1205d8937d8e703a73ae1f9948940b14" }, + "neoscroll.nvim": { "branch": "master", "commit": "a7f5953dbfbe7069568f2d0ed23a9709a56725ab" }, + "none-ls.nvim": { "branch": "main", "commit": "2cde745aadc2c36f6860a77a556494870675771a" }, + "nvim-bqf": { "branch": "main", "commit": "1b24dc6050c34e8cd377b6b4cd6abe40509e0187" }, + "nvim-bufdel": { "branch": "main", "commit": "523d58e94e7212fff3e05c247b962dc8f93bcfde" }, + "nvim-cmp": { "branch": "main", "commit": "d818fd0624205b34e14888358037fb6f5dc51234" }, + "nvim-dap": { "branch": "master", "commit": "bc03b83c94d0375145ff5ac6a6dcf28c1241e06f" }, + "nvim-dap-ui": { "branch": "master", "commit": "a5606bc5958db86f8d92803bea7400ee26a8d7e4" }, + "nvim-highlight-colors": { "branch": "main", "commit": "a8f6952cb1ff7bde864a34c502f1a42c360a6662" }, + "nvim-lspconfig": { "branch": "master", "commit": "e26da408cf955afa8e9ddbadd510e84ea8976cd7" }, + "nvim-nio": { "branch": "master", "commit": "a428f309119086dc78dd4b19306d2d67be884eee" }, + "nvim-notify": { "branch": "master", "commit": "d333b6f167900f6d9d42a59005d82919830626bf" }, + "nvim-scrollview": { "branch": "main", "commit": "afc67708a5a5585553dee132ffbc4761bb135faf" }, + "nvim-spectre": { "branch": "master", "commit": "9a28f926d3371b7ef02243cbbb653a0478d06e31" }, + "nvim-tree.lua": { "branch": "master", "commit": "f9ff00bc06d7cb70548a3847d7a2a05e928bc988" }, + "nvim-treehopper": { "branch": "master", "commit": "13559079e33665a310d9ccf0e43f4e9bb9f337e2" }, + "nvim-treesitter": { "branch": "master", "commit": "b3c54c31887b05d91be41a41392c811876807fd8" }, + "nvim-treesitter-context": { "branch": "master", "commit": "2aba92ceb1479485953007f4d5adf34d0b66917e" }, + "nvim-treesitter-textobjects": { "branch": "master", "commit": "34867c69838078df7d6919b130c0541c0b400c47" }, + "nvim-ts-autotag": { "branch": "main", "commit": "1624866a1379fc1861797f0ed05899a9c1d2ff61" }, + "nvim-ts-context-commentstring": { "branch": "main", "commit": "6b5f95aa4d24f2c629a74f2c935c702b08dbde62" }, + "nvim-web-devicons": { "branch": "master", "commit": "e612de3d3a41a6b7be47f51e956dddabcbf419d9" }, + "paint.nvim": { "branch": "main", "commit": "d738db9211ef73182ffa9b92a2a5661411267123" }, + "persisted.nvim": { "branch": "main", "commit": "4df3f8a03690add1bc2f9a79b31e1c43f5e0dd05" }, + "plenary.nvim": { "branch": "master", "commit": "a3e3bc82a3f95c5ed0d7201546d5d2c19b20d683" }, + "project.nvim": { "branch": "main", "commit": "8c6bad7d22eef1b71144b401c9f74ed01526a4fb" }, + "rainbow-delimiters.nvim": { "branch": "master", "commit": "b29da4a6061a88270e875b38367d82c04c856128" }, + "rustaceanvim": { "branch": "master", "commit": "047f9c9d8cd2861745eb9de6c1570ee0875aa795" }, + "search.nvim": { "branch": "main", "commit": "7b8f2315d031be73e14bc2d82386dfac15952614" }, + "smart-splits.nvim": { "branch": "master", "commit": "44145a4b37217348293fe457992cc38cf3b19f33" }, + "smartyank.nvim": { "branch": "master", "commit": "d9e078fe08d6466e37ea45ac446a9f60e6866789" }, + "sniprun": { "branch": "master", "commit": "f2302d4081914c3312456c91dc6965dfeec266b1" }, + "suda.vim": { "branch": "master", "commit": "b97fab52f9cdeabe2bbb5eb98d82356899f30829" }, + "telescope-frecency.nvim": { "branch": "master", "commit": "bb2c63ee5375ede81c1945b58638412af42b2d62" }, + "telescope-fzf-native.nvim": { "branch": "main", "commit": "cf48d4dfce44e0b9a2e19a008d6ec6ea6f01a83b" }, + "telescope-live-grep-args.nvim": { "branch": "master", "commit": "8ad632f793fd437865f99af5684f78300dac93fb" }, + "telescope-undo.nvim": { "branch": "main", "commit": "51be9ae7c42fc27c0b05505e3a0162e0f05fbb6a" }, + "telescope-zoxide": { "branch": "main", "commit": "68966349aa1b8e9ade403e18479ecf79447389a7" }, + "telescope.nvim": { "branch": "master", "commit": "bfcc7d5c6f12209139f175e6123a7b7de6d9c18a" }, + "todo-comments.nvim": { "branch": "main", "commit": "96fee098a90e7c09c9811aa7df71d773ba8b9b53" }, + "toggleterm.nvim": { "branch": "main", "commit": "8ed0f52006d3207ec6c94de7db62da840937ef2a" }, + "trouble.nvim": { "branch": "main", "commit": "ad8ef38666834e9e438f84bd446657445bc2a71e" }, + "vim-cool": { "branch": "master", "commit": "662e7b11064cbeedad17c45d2fe926e78d3cd0b6" }, + "vim-fugitive": { "branch": "master", "commit": "0444df68cd1cdabc7453d6bd84099458327e5513" }, + "vim-matchup": { "branch": "master", "commit": "1535a769d5dca851fe7d41b0be95f7c7203a4bef" }, + "vim-rhubarb": { "branch": "master", "commit": "ee69335de176d9325267b0fd2597a22901d927b1" }, + "vim-sleuth": { "branch": "master", "commit": "1cc4557420f215d02c4d2645a748a816c220e99b" }, + "which-key.nvim": { "branch": "main", "commit": "6e61b0904e9c038b6c511c43591ae2d811b4975e" }, + "wilder.nvim": { "branch": "master", "commit": "679f348dc90d80ff9ba0e7c470c40a4d038dcecf" } +} \ No newline at end of file diff --git a/nvim/lua/core/event.lua b/nvim/lua/core/event.lua new file mode 100644 index 0000000..b7ba778 --- /dev/null +++ b/nvim/lua/core/event.lua @@ -0,0 +1,152 @@ +-- Now use `` or `` to back to the `dotstutor`. +local autocmd = {} + +function autocmd.nvim_create_augroups(definitions) + for group_name, definition in pairs(definitions) do + -- Prepend an underscore to avoid name clashes + vim.api.nvim_command("augroup _" .. group_name) + vim.api.nvim_command("autocmd!") + for _, def in ipairs(definition) do + local command = table.concat(vim.iter({ "autocmd", def }):flatten(math.huge):totable(), " ") + vim.api.nvim_command(command) + end + vim.api.nvim_command("augroup END") + end +end + +-- Hold off on configuring anything related to the LSP until LspAttach +local mapping = require("keymap.completion") +vim.api.nvim_create_autocmd("LspAttach", { + group = vim.api.nvim_create_augroup("LspKeymapLoader", { clear = true }), + callback = function(event) + if not _G._debugging then + -- LSP Keymaps + mapping.lsp(event.buf) + + -- LSP Inlay Hints + local inlayhints_enabled = require("core.settings").lsp_inlayhints + local client = vim.lsp.get_client_by_id(event.data.client_id) + if client and client.server_capabilities.inlayHintProvider ~= nil then + vim.lsp.inlay_hint.enable(inlayhints_enabled == true, { bufnr = event.buf }) + end + end + end, +}) + +-- auto close NvimTree +vim.api.nvim_create_autocmd("BufEnter", { + group = vim.api.nvim_create_augroup("NvimTreeClose", { clear = true }), + pattern = "NvimTree_*", + callback = function() + local layout = vim.api.nvim_call_function("winlayout", {}) + if + layout[1] == "leaf" + and vim.bo[vim.api.nvim_win_get_buf(layout[2])].filetype == "NvimTree" + and layout[3] == nil + then + vim.api.nvim_command([[confirm quit]]) + end + end, +}) + +-- auto close some filetype with +vim.api.nvim_create_autocmd("FileType", { + pattern = { + "qf", + "help", + "man", + "notify", + "nofile", + "lspinfo", + "terminal", + "prompt", + "toggleterm", + "copilot", + "startuptime", + "tsplayground", + "PlenaryTestPopup", + }, + callback = function(event) + vim.bo[event.buf].buflisted = false + vim.api.nvim_buf_set_keymap(event.buf, "n", "q", "close", { silent = true }) + end, +}) + +function autocmd.load_autocmds() + local definitions = { + lazy = {}, + bufs = { + -- Reload vim config automatically + { + "BufWritePost", + [[$VIM_PATH/{*.vim,*.yaml,vimrc} nested source $MYVIMRC | redraw]], + }, + -- Reload Vim script automatically if setlocal autoread + { + "BufWritePost,FileWritePost", + "*.vim", + [[nested if &l:autoread > 0 | source | echo 'source ' . bufname('%') | endif]], + }, + { "BufWritePre", "/tmp/*", "setlocal noundofile" }, + { "BufWritePre", "COMMIT_EDITMSG", "setlocal noundofile" }, + { "BufWritePre", "MERGE_MSG", "setlocal noundofile" }, + { "BufWritePre", "*.tmp", "setlocal noundofile" }, + { "BufWritePre", "*.bak", "setlocal noundofile" }, + -- auto place to last edit + { + "BufReadPost", + "*", + [[if line("'\"") > 1 && line("'\"") <= line("$") | execute "normal! g'\"" | endif]], + }, + -- Auto toggle fcitx5 + -- {"InsertLeave", "* :silent", "!fcitx5-remote -c"}, + -- {"BufCreate", "*", ":silent !fcitx5-remote -c"}, + -- {"BufEnter", "*", ":silent !fcitx5-remote -c "}, + -- {"BufLeave", "*", ":silent !fcitx5-remote -c "} + }, + wins = { + -- Highlight current line only on focused window + { + "WinEnter,BufEnter,InsertLeave", + "*", + [[if ! &cursorline && &filetype !~# '^\(dashboard\|clap_\)' && ! &pvw | setlocal cursorline | endif]], + }, + { + "WinLeave,BufLeave,InsertEnter", + "*", + [[if &cursorline && &filetype !~# '^\(dashboard\|clap_\)' && ! &pvw | setlocal nocursorline | endif]], + }, + -- Attempt to write shada when leaving nvim + { + "VimLeave", + "*", + [[if has('nvim') | wshada | else | wviminfo! | endif]], + }, + -- Check if file changed when its window is focus, more eager than 'autoread' + { "FocusGained", "* checktime" }, + -- Equalize window dimensions when resizing vim window + { "VimResized", "*", [[tabdo wincmd =]] }, + }, + ft = { + { "FileType", "*", "setlocal formatoptions-=cro" }, + { "FileType", "alpha", "setlocal showtabline=0" }, + { "FileType", "markdown", "setlocal wrap" }, + { "FileType", "dap-repl", "lua require('dap.ext.autocompl').attach()" }, + { + "FileType", + "c,cpp", + "nnoremap h :ClangdSwitchSourceHeaderVSplit", + }, + }, + yank = { + { + "TextYankPost", + "*", + [[silent! lua vim.highlight.on_yank({higroup="IncSearch", timeout=300})]], + }, + }, + } + autocmd.nvim_create_augroups(require("modules.utils").extend_config(definitions, "user.event")) +end + +autocmd.load_autocmds() diff --git a/nvim/lua/core/global.lua b/nvim/lua/core/global.lua new file mode 100644 index 0000000..0f01edc --- /dev/null +++ b/nvim/lua/core/global.lua @@ -0,0 +1,18 @@ +local global = {} +local os_name = vim.uv.os_uname().sysname + +function global:load_variables() + self.is_mac = os_name == "Darwin" + self.is_linux = os_name == "Linux" + self.is_windows = os_name == "Windows_NT" + self.is_wsl = vim.fn.has("wsl") == 1 + self.vim_path = vim.fn.stdpath("config") + self.cache_dir = vim.fn.stdpath("cache") + self.data_dir = string.format("%s/site/", vim.fn.stdpath("data")) + self.modules_dir = self.vim_path .. "/modules" + self.home = self.is_windows and os.getenv("USERPROFILE") or os.getenv("HOME") +end + +global:load_variables() + +return global diff --git a/nvim/lua/core/init.lua b/nvim/lua/core/init.lua new file mode 100644 index 0000000..a5fa328 --- /dev/null +++ b/nvim/lua/core/init.lua @@ -0,0 +1,169 @@ +local settings = require("core.settings") +local global = require("core.global") + +-- Create cache dir and data dirs +local createdir = function() + local data_dirs = { + global.cache_dir .. "/backup", + global.cache_dir .. "/session", + global.cache_dir .. "/swap", + global.cache_dir .. "/tags", + global.cache_dir .. "/undo", + } + -- Only check whether cache_dir exists, this would be enough. + if vim.fn.isdirectory(global.cache_dir) == 0 then + ---@diagnostic disable-next-line: param-type-mismatch + vim.fn.mkdir(global.cache_dir, "p") + for _, dir in pairs(data_dirs) do + if vim.fn.isdirectory(dir) == 0 then + vim.fn.mkdir(dir, "p") + end + end + end +end + +local disable_distribution_plugins = function() + -- Disable menu loading + vim.g.did_install_default_menus = 1 + vim.g.did_install_syntax_menu = 1 + + -- Comment this if you define your own filetypes in `after/ftplugin` + -- vim.g.did_load_filetypes = 1 + + -- Do not load native syntax completion + vim.g.loaded_syntax_completion = 1 + + -- Do not load spell files + vim.g.loaded_spellfile_plugin = 1 + + -- Whether to load netrw by default + -- vim.g.loaded_netrw = 1 + -- vim.g.loaded_netrwFileHandlers = 1 + -- vim.g.loaded_netrwPlugin = 1 + -- vim.g.loaded_netrwSettings = 1 + -- newtrw liststyle: https://medium.com/usevim/the-netrw-style-options-3ebe91d42456 + vim.g.netrw_liststyle = 3 + + -- Do not load tohtml.vim + vim.g.loaded_2html_plugin = 1 + + -- Do not load zipPlugin.vim, gzip.vim and tarPlugin.vim (all of these plugins are + -- related to reading files inside compressed containers) + vim.g.loaded_gzip = 1 + vim.g.loaded_tar = 1 + vim.g.loaded_tarPlugin = 1 + vim.g.loaded_vimball = 1 + vim.g.loaded_vimballPlugin = 1 + vim.g.loaded_zip = 1 + vim.g.loaded_zipPlugin = 1 + + -- Do not use builtin matchit.vim and matchparen.vim because we're using vim-matchup + vim.g.loaded_matchit = 1 + vim.g.loaded_matchparen = 1 + + -- Disable sql omni completion + vim.g.loaded_sql_completion = 1 + + -- Set this to 0 in order to disable native EditorConfig support + vim.g.editorconfig = 1 + + -- Disable remote plugins + -- NOTE: + -- > Disabling rplugin.vim will make `wilder.nvim` complain about missing rplugins during :checkhealth, + -- > but since it's config doesn't require python rtp (strictly), it's fine to ignore that for now. + -- vim.g.loaded_remote_plugins = 1 +end + +local leader_map = function() + vim.g.mapleader = " " + -- NOTE: + -- > Uncomment the following if you're using a other than , and you wish + -- > to disable advancing one character by pressing in normal/visual mode. + -- vim.api.nvim_set_keymap("n", " ", "", { noremap = true }) + -- vim.api.nvim_set_keymap("x", " ", "", { noremap = true }) +end + +local gui_config = function() + vim.api.nvim_set_option_value("guifont", settings.gui_config.font_name .. ":h" .. settings.gui_config.font_size, {}) +end + +local neovide_config = function() + for name, config in pairs(settings.neovide_config) do + vim.g["neovide_" .. name] = config + end +end + +local clipboard_config = function() + if global.is_mac then + vim.g.clipboard = { + name = "macOS-clipboard", + copy = { ["+"] = "pbcopy", ["*"] = "pbcopy" }, + paste = { ["+"] = "pbpaste", ["*"] = "pbpaste" }, + cache_enabled = 0, + } + elseif global.is_wsl then + vim.g.clipboard = { + name = "win32yank-wsl", + copy = { + ["+"] = "win32yank.exe -i --crlf", + ["*"] = "win32yank.exe -i --crlf", + }, + paste = { + ["+"] = "win32yank.exe -o --lf", + ["*"] = "win32yank.exe -o --lf", + }, + cache_enabled = 0, + } + end +end + +local shell_config = function() + if global.is_windows then + if not (vim.fn.executable("pwsh") == 1 or vim.fn.executable("powershell") == 1) then + vim.notify( + [[ +Failed to setup terminal config + +PowerShell is either not installed, missing from PATH, or not executable; +cmd.exe will be used instead for `:!` (shell bang) and toggleterm.nvim. + +You're recommended to install PowerShell for better experience.]], + vim.log.levels.WARN, + { title = "[core] Runtime Warning" } + ) + return + end + + local basecmd = "-NoLogo -MTA -ExecutionPolicy RemoteSigned" + local ctrlcmd = "-Command [console]::InputEncoding = [console]::OutputEncoding = [System.Text.Encoding]::UTF8" + local set_opts = vim.api.nvim_set_option_value + set_opts("shell", vim.fn.executable("pwsh") == 1 and "pwsh" or "powershell", {}) + set_opts("shellcmdflag", string.format("%s %s;", basecmd, ctrlcmd), {}) + set_opts("shellredir", "-RedirectStandardOutput %s -NoNewWindow -Wait", {}) + set_opts("shellpipe", "2>&1 | Out-File -Encoding UTF8 %s; exit $LastExitCode", {}) + set_opts("shellquote", "", {}) + set_opts("shellxquote", "", {}) + end +end + +local load_core = function() + createdir() + disable_distribution_plugins() + leader_map() + + gui_config() + neovide_config() + clipboard_config() + shell_config() + + require("core.options") + require("core.mapping") + require("core.event") + require("core.pack") + require("keymap") + + vim.api.nvim_set_option_value("background", settings.background, {}) + vim.cmd.colorscheme(settings.colorscheme) +end + +load_core() diff --git a/nvim/lua/core/mapping.lua b/nvim/lua/core/mapping.lua new file mode 100644 index 0000000..3512169 --- /dev/null +++ b/nvim/lua/core/mapping.lua @@ -0,0 +1,65 @@ +local bind = require("keymap.bind") +local map_cr = bind.map_cr +local map_cu = bind.map_cu +local map_cmd = bind.map_cmd +local map_callback = bind.map_callback + +local core_map = { + -- Suckless + ["n|"] = map_cr("normal za"):with_noremap():with_silent():with_desc("edit: Toggle code fold"), + ["n|"] = map_cu("write"):with_noremap():with_silent():with_desc("edit: Save file"), + ["n|Y"] = map_cmd("y$"):with_desc("edit: Yank text to EOL"), + ["n|D"] = map_cmd("d$"):with_desc("edit: Delete text to EOL"), + ["n|n"] = map_cmd("nzzzv"):with_noremap():with_desc("edit: Next search result"), + ["n|N"] = map_cmd("Nzzzv"):with_noremap():with_desc("edit: Prev search result"), + ["n|J"] = map_cmd("mzJ`z"):with_noremap():with_desc("edit: Join next line"), + ["n|"] = map_callback(function() + _flash_esc_or_noh() + end) + :with_noremap() + :with_silent() + :with_desc("edit: Clear search highlight"), + ["n|"] = map_cmd("h"):with_noremap():with_desc("window: Focus left"), + ["n|"] = map_cmd("l"):with_noremap():with_desc("window: Focus right"), + ["n|"] = map_cmd("j"):with_noremap():with_desc("window: Focus down"), + ["n|"] = map_cmd("k"):with_noremap():with_desc("window: Focus up"), + ["t|h"] = map_cmd("wincmd h"):with_silent():with_noremap():with_desc("window: Focus left"), + ["t|l"] = map_cmd("wincmd l"):with_silent():with_noremap():with_desc("window: Focus right"), + ["t|j"] = map_cmd("wincmd j"):with_silent():with_noremap():with_desc("window: Focus down"), + ["t|k"] = map_cmd("wincmd k"):with_silent():with_noremap():with_desc("window: Focus up"), + ["n|"] = map_cr("vertical resize -3"):with_silent():with_desc("window: Resize -3 vertically"), + ["n|"] = map_cr("vertical resize +3"):with_silent():with_desc("window: Resize +3 vertically"), + ["n|"] = map_cr("resize -3"):with_silent():with_desc("window: Resize -3 horizontally"), + ["n|"] = map_cr("resize +3"):with_silent():with_desc("window: Resize +3 horizontally"), + ["n|"] = map_cr("wq"):with_desc("edit: Save file and quit"), + ["n|"] = map_cr("q!"):with_desc("edit: Force quit"), + ["n|o"] = map_cr("setlocal spell! spelllang=en_us"):with_desc("edit: Toggle spell check"), + ["n|bn"] = map_cu("enew"):with_noremap():with_silent():with_desc("buffer: New"), + ["n|tn"] = map_cr("tabnew"):with_noremap():with_silent():with_desc("tab: Create a new tab"), + ["n|tk"] = map_cr("tabnext"):with_noremap():with_silent():with_desc("tab: Move to next tab"), + ["n|tj"] = map_cr("tabprevious"):with_noremap():with_silent():with_desc("tab: Move to previous tab"), + ["n|to"] = map_cr("tabonly"):with_noremap():with_silent():with_desc("tab: Only keep current tab"), + -- Insert mode + ["i|"] = map_cmd("u"):with_noremap():with_desc("edit: Delete previous block"), + ["i|"] = map_cmd(""):with_noremap():with_desc("edit: Move cursor to left"), + ["i|"] = map_cmd("^i"):with_noremap():with_desc("edit: Move cursor to line start"), + ["i|"] = map_cmd(":w"):with_desc("edit: Save file"), + ["i|"] = map_cmd(":wq"):with_desc("edit: Save file and quit"), + -- Command mode + ["c|"] = map_cmd(""):with_noremap():with_desc("edit: Left"), + ["c|"] = map_cmd(""):with_noremap():with_desc("edit: Right"), + ["c|"] = map_cmd(""):with_noremap():with_desc("edit: Home"), + ["c|"] = map_cmd(""):with_noremap():with_desc("edit: End"), + ["c|"] = map_cmd(""):with_noremap():with_desc("edit: Delete"), + ["c|"] = map_cmd(""):with_noremap():with_desc("edit: Backspace"), + ["c|"] = map_cmd([[=expand("%:p:h") . "/" ]]) + :with_noremap() + :with_desc("edit: Complete path of current file"), + -- Visual mode + ["v|J"] = map_cmd(":m '>+1gv=gv"):with_desc("edit: Move this line down"), + ["v|K"] = map_cmd(":m '<-2gv=gv"):with_desc("edit: Move this line up"), + ["v|<"] = map_cmd(""] = map_cmd(">gv"):with_desc("edit: Increase indent"), +} + +bind.nvim_load_mapping(core_map) diff --git a/nvim/lua/core/options.lua b/nvim/lua/core/options.lua new file mode 100644 index 0000000..d025abf --- /dev/null +++ b/nvim/lua/core/options.lua @@ -0,0 +1,128 @@ +local global = require("core.global") + +local function load_options() + local global_local = { + -- backupdir = global.cache_dir .. "/backup/", + -- directory = global.cache_dir .. "/swap/", + -- spellfile = global.cache_dir .. "/spell/en.uft-8.add", + -- viewdir = global.cache_dir .. "/view/", + autoindent = true, + autoread = true, + autowrite = true, + backspace = "indent,eol,start", + backup = false, + backupskip = "/tmp/*,$TMPDIR/*,$TMP/*,$TEMP/*,*/shm/*,/private/var/*,.vault.vim", + breakat = [[\ \ ;:,!?]], + breakindentopt = "shift:2,min:20", + clipboard = "unnamedplus", + cmdheight = 1, -- 0, 1, 2 + cmdwinheight = 5, + complete = ".,w,b,k,kspell", + completeopt = "menuone,noselect,popup", + concealcursor = "niv", + conceallevel = 0, + cursorcolumn = true, + cursorline = true, + diffopt = "filler,iwhite,internal,linematch:60,algorithm:patience", + display = "lastline", + encoding = "utf-8", + equalalways = false, + errorbells = true, + fileformats = "unix,mac,dos", + foldenable = true, + foldlevelstart = 99, + formatoptions = "1jcroql", + grepformat = "%f:%l:%c:%m", + grepprg = "rg --hidden --vimgrep --smart-case --", + helpheight = 12, + hidden = true, + history = 2000, + ignorecase = true, + inccommand = "nosplit", + incsearch = true, + infercase = true, + jumpoptions = "stack", + laststatus = 3, + linebreak = true, + list = true, + listchars = "tab:»·,nbsp:+,trail:·,extends:→,precedes:←", + magic = true, + mousescroll = "ver:3,hor:6", + number = true, + previewheight = 12, + -- Do NOT adjust the following option (pumblend) if you're using transparent background + pumblend = 0, + pumheight = 15, + redrawtime = 1500, + relativenumber = true, + ruler = true, + scrolloff = 2, + sessionoptions = "buffers,curdir,folds,help,tabpages,winpos,winsize", + shada = "!,'500,<50,@100,s10,h", + shiftround = true, + shiftwidth = 4, + shortmess = "aoOTIcF", + showbreak = "↳ ", + showcmd = false, + showmode = false, + showtabline = 2, + sidescrolloff = 5, + signcolumn = "yes", + smartcase = true, + smarttab = true, + smoothscroll = true, + splitbelow = true, + splitkeep = "screen", + splitright = true, + startofline = false, + swapfile = false, + switchbuf = "usetab,uselast", + synmaxcol = 2500, + tabstop = 4, + termguicolors = true, + timeout = true, + timeoutlen = 300, + ttimeout = true, + ttimeoutlen = 0, + undodir = global.cache_dir .. "/undo/", + undofile = true, + -- Please do NOT set `updatetime` to above 500, otherwise most plugins may not function correctly + updatetime = 200, + viewoptions = "folds,cursor,curdir,slash,unix", + virtualedit = "block", + visualbell = true, + whichwrap = "h,l,<,>,[,],~", + wildignore = ".git,.hg,.svn,*.pyc,*.o,*.out,*.jpg,*.jpeg,*.png,*.gif,*.zip,**/tmp/**,*.DS_Store,**/node_modules/**,**/bower_modules/**", + wildignorecase = true, + -- Do NOT adjust the following option (winblend) if you're using transparent background + winblend = 0, + winminwidth = 10, + winwidth = 30, + wrap = false, + wrapscan = true, + writebackup = false, + } + + local function isempty(s) + return s == nil or s == "" + end + local function use_if_defined(val, fallback) + return val ~= nil and val or fallback + end + + -- custom python provider + local conda_prefix = os.getenv("CONDA_PREFIX") + if not isempty(conda_prefix) then + vim.g.python_host_prog = use_if_defined(vim.g.python_host_prog, conda_prefix .. "/bin/python") + vim.g.python3_host_prog = use_if_defined(vim.g.python3_host_prog, conda_prefix .. "/bin/python") + else + vim.g.python_host_prog = use_if_defined(vim.g.python_host_prog, "python") + vim.g.python3_host_prog = use_if_defined(vim.g.python3_host_prog, "python3") + end + + for name, value in pairs(require("modules.utils").extend_config(global_local, "user.options")) do + vim.api.nvim_set_option_value(name, value, {}) + end +end + +load_options() diff --git a/nvim/lua/core/pack.lua b/nvim/lua/core/pack.lua new file mode 100644 index 0000000..326bf3d --- /dev/null +++ b/nvim/lua/core/pack.lua @@ -0,0 +1,138 @@ +local fn, api = vim.fn, vim.api +local global = require("core.global") +local is_mac = global.is_mac +local vim_path = global.vim_path +local data_dir = global.data_dir +local lazy_path = data_dir .. "lazy/lazy.nvim" +local modules_dir = vim_path .. "/lua/modules" +local user_config_dir = vim_path .. "/lua/user" + +local settings = require("core.settings") +local use_ssh = settings.use_ssh + +local icons = { + kind = require("modules.utils.icons").get("kind"), + documents = require("modules.utils.icons").get("documents"), + ui = require("modules.utils.icons").get("ui"), + ui_sep = require("modules.utils.icons").get("ui", true), + misc = require("modules.utils.icons").get("misc"), +} + +local Lazy = {} + +function Lazy:load_plugins() + self.modules = {} + + local append_nativertp = function() + package.path = package.path + .. string.format( + ";%s;%s;%s", + modules_dir .. "/configs/?.lua", + modules_dir .. "/configs/?/init.lua", + user_config_dir .. "/?.lua" + ) + end + + local get_plugins_list = function() + local list = {} + local plugins_list = vim.split(fn.glob(modules_dir .. "/plugins/*.lua"), "\n") + local user_plugins_list = vim.split(fn.glob(user_config_dir .. "/plugins/*.lua"), "\n", { trimempty = true }) + vim.list_extend(plugins_list, user_plugins_list) + for _, f in ipairs(plugins_list) do + -- aggregate the plugins from `/plugins/*.lua` and `/user/plugins/*.lua` to a plugin list of a certain field for later `require` action. + -- current fields contains: completion, editor, lang, tool, ui + list[#list + 1] = f:find(modules_dir) and f:sub(#modules_dir - 6, -1) or f:sub(#user_config_dir - 3, -1) + end + return list + end + + append_nativertp() + + for _, m in ipairs(get_plugins_list()) do + -- require modules returned from `get_plugins_list()` function. + local modules = require(m:sub(0, #m - 4)) + if type(modules) == "table" then + for name, conf in pairs(modules) do + self.modules[#self.modules + 1] = vim.tbl_extend("force", { name }, conf) + end + end + end + for _, name in ipairs(settings.disabled_plugins) do + self.modules[#self.modules + 1] = { name, enabled = false } + end +end + +function Lazy:load_lazy() + if not vim.uv.fs_stat(lazy_path) then + local lazy_repo = use_ssh and "git@github.com:folke/lazy.nvim.git " or "https://github.com/folke/lazy.nvim.git " + api.nvim_command("!git clone --filter=blob:none --branch=stable " .. lazy_repo .. lazy_path) + end + self:load_plugins() + + local clone_prefix = use_ssh and "git@github.com:%s.git" or "https://github.com/%s.git" + local lazy_settings = { + root = data_dir .. "lazy", -- directory where plugins will be installed + git = { + -- log = { "-10" }, -- show the last 10 commits + timeout = 300, + url_format = clone_prefix, + }, + install = { + -- install missing plugins on startup. This doesn't increase startup time. + missing = true, + colorscheme = { settings.colorscheme }, + }, + ui = { + -- a number <1 is a percentage., >1 is a fixed size + size = { width = 0.88, height = 0.8 }, + wrap = true, -- wrap the lines in the ui + -- The border to use for the UI window. Accepts same border values as |nvim_open_win()|. + border = "rounded", + icons = { + cmd = icons.misc.Code, + config = icons.ui.Gear, + event = icons.kind.Event, + ft = icons.documents.Files, + init = icons.misc.ManUp, + import = icons.documents.Import, + keys = icons.ui.Keyboard, + loaded = icons.ui.Check, + not_loaded = icons.misc.Ghost, + plugin = icons.ui.Package, + runtime = icons.misc.Vim, + source = icons.kind.StaticMethod, + start = icons.ui.Play, + list = { + icons.ui_sep.BigCircle, + icons.ui_sep.BigUnfilledCircle, + icons.ui_sep.Square, + icons.ui_sep.ChevronRight, + }, + }, + }, + performance = { + cache = { + enabled = true, + path = vim.fn.stdpath("cache") .. "/lazy/cache", + -- Once one of the following events triggers, caching will be disabled. + -- To cache all modules, set this to `{}`, but that is not recommended. + disable_events = { "UIEnter", "BufReadPre" }, + ttl = 3600 * 24 * 2, -- keep unused modules for up to 2 days + }, + reset_packpath = true, -- reset the package path to improve startup time + rtp = { + reset = true, -- reset the runtime path to $VIMRUNTIME and the config directory + ---@type string[] + paths = {}, -- add any custom paths here that you want to include in the rtp + }, + }, + } + if is_mac then + lazy_settings.concurrency = 20 + end + + vim.opt.rtp:prepend(lazy_path) + require("lazy").setup(self.modules, lazy_settings) +end + +Lazy:load_lazy() diff --git a/nvim/lua/core/settings.lua b/nvim/lua/core/settings.lua new file mode 100644 index 0000000..1254160 --- /dev/null +++ b/nvim/lua/core/settings.lua @@ -0,0 +1,225 @@ +local settings = {} + +-- Set it to false if you want to use https to update plugins and treesitter parsers. +---@type boolean +settings["use_ssh"] = true + +-- Set it to false if you don't use copilot +---@type boolean +settings["use_copilot"] = true + +-- Set it to false if there is no need to format on save. +---@type boolean +settings["format_on_save"] = true + +-- Set format timeout here (in ms). +---@type number +settings["format_timeout"] = 1000 + +-- Set it to false if the notification after formatting is annoying. +---@type boolean +settings["format_notify"] = true + +-- Set it to true if you prefer formatting ONLY the *changed lines* as defined by your version control system. +-- NOTE: This entry will only be respected if: +-- > The buffer to be formatted is under version control (Git or Mercurial); +-- > Any of the server attached to that buffer supports |DocumentRangeFormattingProvider| server capability. +-- Otherwise Neovim would fall back to format the whole buffer, and a warning will be issued. +---@type boolean +settings["format_modifications_only"] = false + +-- Set the format disabled directories here, files under these dirs won't be formatted on save. +--- NOTE: Directories may contain regular expressions (grammar: vim). |regexp| +--- NOTE: Directories are automatically normalized. |vim.fs.normalize()| +---@type string[] +settings["format_disabled_dirs"] = { + -- Example + "~/format_disabled_dir", +} + +-- Filetypes in this list will skip lsp formatting if rhs is true. +---@type table +settings["formatter_block_list"] = { + lua = false, -- example +} + +-- Servers in this list will skip setting formatting capabilities if rhs is true. +---@type table +settings["server_formatting_block_list"] = { + lua_ls = true, + tsserver = true, + clangd = true, +} + +-- Set it to false if you want to turn off LSP Inlay Hints +---@type boolean +settings["lsp_inlayhints"] = true + +-- Set it to false if diagnostics virtual text is annoying. +-- If disabled, you may browse lsp diagnostics using trouble.nvim (press `gt` to toggle it). +---@type boolean +settings["diagnostics_virtual_text"] = true + +-- Set it to one of the values below if you want to change the visible severity level of lsp diagnostics. +-- Priority: `Error` > `Warning` > `Information` > `Hint`. +-- > e.g. if you set this option to `Warning`, only lsp warnings and errors will be shown. +-- NOTE: This entry only works when `diagnostics_virtual_text` is true. +---@type "ERROR"|"WARN"|"INFO"|"HINT" +settings["diagnostics_level"] = "HINT" + +-- Set the plugins to disable here. +-- Example: "Some-User/A-Repo" +---@type string[] +settings["disabled_plugins"] = {} + +-- Set it to false if you don't use nvim to open big files. +---@type boolean +settings["load_big_files_faster"] = true + +-- Change the colors of the global palette here. +-- Settings will complete their replacement at initialization. +-- Parameters will be automatically completed as you type. +-- Example: { sky = "#04A5E5" } +---@type palette[] +settings["palette_overwrite"] = {} + +-- Set the colorscheme to use here. +-- Available values are: `catppuccin`, `catppuccin-latte`, `catppucin-mocha`, `catppuccin-frappe`, `catppuccin-macchiato`. +---@type string +settings["colorscheme"] = "catppuccin" + +-- Set it to true if your terminal has transparent background. +---@type boolean +settings["transparent_background"] = false + +-- Set background color to use here. +-- Useful if you would like to use a colorscheme that has a light and dark variant like `edge`. +-- Valid values are: `dark`, `light`. +---@type "dark"|"light" +settings["background"] = "dark" + +-- Set the command for handling external URLs here. The executable must be available on your $PATH. +-- This entry is IGNORED on Windows and macOS, which have their default handlers builtin. +---@type string +settings["external_browser"] = "chrome-cli open" + +-- Set the language servers that will be installed during bootstrap here. +-- check the below link for all the supported LSPs: +-- https://github.com/neovim/nvim-lspconfig/tree/master/lua/lspconfig/server_configurations +---@type string[] +settings["lsp_deps"] = { + "bashls", + "clangd", + "html", + "jsonls", + "lua_ls", + "pylsp", + "gopls", +} + +-- Set the general-purpose servers that will be installed during bootstrap here. +-- Check the below link for all supported sources. +-- in `code_actions`, `completion`, `diagnostics`, `formatting`, `hover` folders: +-- https://github.com/nvimtools/none-ls.nvim/tree/main/lua/null-ls/builtins +---@type string[] +settings["null_ls_deps"] = { + "clang_format", + "gofumpt", + "goimports", + "prettier", + "shfmt", + "stylua", + "vint", +} + +-- Set the Debug Adapter Protocol (DAP) clients that will be installed and configured during bootstrap here. +-- Check the below link for all supported DAPs: +-- https://github.com/jay-babu/mason-nvim-dap.nvim/blob/main/lua/mason-nvim-dap/mappings/source.lua +---@type string[] +settings["dap_deps"] = { + "codelldb", -- C-Family + "delve", -- Go + "python", -- Python (debugpy) +} + +-- Set the Treesitter parsers that will be installed during bootstrap here. +-- Check the below link for all supported languages: +-- https://github.com/nvim-treesitter/nvim-treesitter#supported-languages +---@type string[] +settings["treesitter_deps"] = { + "bash", + "c", + "cpp", + "css", + "go", + "gomod", + "html", + "javascript", + "json", + "jsonc", + "latex", + "lua", + "make", + "markdown", + "markdown_inline", + "python", + "rust", + "typescript", + "vimdoc", + "vue", + "yaml", +} + +-- Set the options for neovim's gui clients like `neovide` and `neovim-qt` here. +-- NOTE: Currently, only the following options related to the GUI are supported. Other entries will be IGNORED. +---@type { font_name: string, font_size: number } +settings["gui_config"] = { + font_name = "JetBrainsMono Nerd Font", + font_size = 12, +} + +-- Set the options specific to `neovide` here. +-- NOTE: You should remove the `neovide_` prefix (with trailing underscore) from all your entries below. +-- Check the below link for all supported entries: +-- https://neovide.dev/configuration.html +---@type table +settings["neovide_config"] = { + no_idle = true, + refresh_rate = 120, + cursor_vfx_mode = "railgun", + cursor_vfx_opacity = 200.0, + cursor_antialiasing = true, + cursor_trail_length = 0.05, + cursor_animation_length = 0.03, + cursor_vfx_particle_speed = 20.0, + cursor_vfx_particle_density = 5.0, + cursor_vfx_particle_lifetime = 1.2, +} + +-- Set the dashboard startup image here +-- You can generate the ascii image using: https://github.com/TheZoraiz/ascii-image-converter +-- More info: https://github.com/ayamir/nvimdots/wiki/Issues#change-dashboard-startup-image +---@type string[] +settings["dashboard_image"] = { + [[⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿]], + [[⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠋⣠⣶⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿]], + [[⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣡⣾⣿⣿⣿⣿⣿⢿⣿⣿⣿⣿⣿⣿⣟⠻⣿⣿⣿⣿⣿⣿⣿⣿]], + [[⣿⣿⣿⣿⣿⣿⣿⣿⡿⢫⣷⣿⣿⣿⣿⣿⣿⣿⣾⣯⣿⡿⢧⡚⢷⣌⣽⣿⣿⣿⣿⣿⣶⡌⣿⣿⣿⣿⣿⣿]], + [[⣿⣿⣿⣿⣿⣿⣿⣿⠇⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣮⣇⣘⠿⢹⣿⣿⣿⣿⣿⣻⢿⣿⣿⣿⣿⣿]], + [[⣿⣿⣿⣿⣿⣿⣿⣿⠀⢸⣿⣿⡇⣿⣿⣿⣿⣿⣿⣿⣿⡟⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣦⣻⣿⣿⣿⣿]], + [[⣿⣿⣿⣿⣿⣿⣿⡇⠀⣬⠏⣿⡇⢻⣿⣿⣿⣿⣿⣿⣿⣷⣼⣿⣿⣸⣿⣿⣿⣿⣿⣿⣿⣿⣿⢻⣿⣿⣿⣿]], + [[⣿⣿⣿⣿⣿⣿⣿⠀⠈⠁⠀⣿⡇⠘⡟⣿⣿⣿⣿⣿⣿⣿⣿⡏⠿⣿⣟⣿⣿⣿⣿⣿⣿⣿⣿⣇⣿⣿⣿⣿]], + [[⣿⣿⣿⣿⣿⣿⡏⠀⠀⠐⠀⢻⣇⠀⠀⠹⣿⣿⣿⣿⣿⣿⣩⡶⠼⠟⠻⠞⣿⡈⠻⣟⢻⣿⣿⣿⣿⣿⣿⣿]], + [[⣿⣿⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⢿⠀⡆⠀⠘⢿⢻⡿⣿⣧⣷⢣⣶⡃⢀⣾⡆⡋⣧⠙⢿⣿⣿⣟⣿⣿⣿⣿]], + [[⣿⣿⣿⣿⣿⣿⡿⠀⠀⠀⠀⠀⠀⠀⡥⠂⡐⠀⠁⠑⣾⣿⣿⣾⣿⣿⣿⡿⣷⣷⣿⣧⣾⣿⣿⣿⣿⣿⣿⣿]], + [[⣿⣿⡿⣿⣍⡴⠆⠀⠀⠀⠀⠀⠀⠀⠀⣼⣄⣀⣷⡄⣙⢿⣿⣿⣿⣿⣯⣶⣿⣿⢟⣾⣿⣿⢡⣿⣿⣿⣿⣿]], + [[⣿⡏⣾⣿⣿⣿⣷⣦⠀⠀⠀⢀⡀⠀⠀⠠⣭⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠟⣡⣾⣿⣿⢏⣾⣿⣿⣿⣿⣿]], + [[⣿⣿⣿⣿⣿⣿⣿⣿⡴⠀⠀⠀⠀⠀⠠⠀⠰⣿⣿⣿⣷⣿⠿⠿⣿⣿⣭⡶⣫⠔⢻⢿⢇⣾⣿⣿⣿⣿⣿⣿]], + [[⣿⣿⣿⡿⢫⣽⠟⣋⠀⠀⠀⠀⣶⣦⠀⠀⠀⠈⠻⣿⣿⣿⣾⣿⣿⣿⣿⡿⣣⣿⣿⢸⣾⣿⣿⣿⣿⣿⣿⣿]], + [[⡿⠛⣹⣶⣶⣶⣾⣿⣷⣦⣤⣤⣀⣀⠀⠀⠀⠀⠀⠀⠉⠛⠻⢿⣿⡿⠫⠾⠿⠋⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿]], + [[⢀⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣀⡆⣠⢀⣴⣏⡀⠀⠀⠀⠉⠀⠀⢀⣠⣰⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿]], + [[⠿⠛⠛⠛⠛⠛⠛⠻⢿⣿⣿⣿⣿⣯⣟⠷⢷⣿⡿⠋⠀⠀⠀⠀⣵⡀⢠⡿⠋⢻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿]], + [[⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠉⠛⢿⣿⣿⠂⠀⠀⠀⠀⠀⢀⣽⣿⣿⣿⣿⣿⣿⣿⣍⠛⠿⣿⣿⣿⣿⣿⣿]], +} + +return require("modules.utils").extend_config(settings, "user.settings") diff --git a/nvim/lua/keymap/bind.lua b/nvim/lua/keymap/bind.lua new file mode 100644 index 0000000..a5c4b8d --- /dev/null +++ b/nvim/lua/keymap/bind.lua @@ -0,0 +1,169 @@ +---@class map_rhs +---@field cmd string +---@field options table +---@field options.noremap boolean +---@field options.silent boolean +---@field options.expr boolean +---@field options.nowait boolean +---@field options.callback function +---@field options.desc string +---@field buffer boolean|number +local rhs_options = {} + +function rhs_options:new() + local instance = { + cmd = "", + options = { + noremap = false, + silent = false, + expr = false, + nowait = false, + callback = nil, + desc = "", + }, + buffer = false, + } + setmetatable(instance, self) + self.__index = self + return instance +end + +---@param cmd_string string +---@return map_rhs +function rhs_options:map_cmd(cmd_string) + self.cmd = cmd_string + return self +end + +---@param cmd_string string +---@return map_rhs +function rhs_options:map_cr(cmd_string) + self.cmd = (":%s"):format(cmd_string) + return self +end + +---@param cmd_string string +---@return map_rhs +function rhs_options:map_args(cmd_string) + self.cmd = (":%s"):format(cmd_string) + return self +end + +---@param cmd_string string +---@return map_rhs +function rhs_options:map_cu(cmd_string) + -- to eliminate the automatically inserted range in visual mode + self.cmd = (":%s"):format(cmd_string) + return self +end + +---@param callback fun():nil +--- Takes a callback that will be called when the key is pressed +---@return map_rhs +function rhs_options:map_callback(callback) + self.cmd = "" + self.options.callback = callback + return self +end + +---@return map_rhs +function rhs_options:with_silent() + self.options.silent = true + return self +end + +---@param description_string string +---@return map_rhs +function rhs_options:with_desc(description_string) + self.options.desc = description_string + return self +end + +---@return map_rhs +function rhs_options:with_noremap() + self.options.noremap = true + return self +end + +---@return map_rhs +function rhs_options:with_expr() + self.options.expr = true + return self +end + +---@return map_rhs +function rhs_options:with_nowait() + self.options.nowait = true + return self +end + +---@param num number +---@return map_rhs +function rhs_options:with_buffer(num) + self.buffer = num + return self +end + +local bind = {} + +---@param cmd_string string +---@return map_rhs +function bind.map_cr(cmd_string) + local ro = rhs_options:new() + return ro:map_cr(cmd_string) +end + +---@param cmd_string string +---@return map_rhs +function bind.map_cmd(cmd_string) + local ro = rhs_options:new() + return ro:map_cmd(cmd_string) +end + +---@param cmd_string string +---@return map_rhs +function bind.map_cu(cmd_string) + local ro = rhs_options:new() + return ro:map_cu(cmd_string) +end + +---@param cmd_string string +---@return map_rhs +function bind.map_args(cmd_string) + local ro = rhs_options:new() + return ro:map_args(cmd_string) +end + +---@param callback fun():nil +---@return map_rhs +function bind.map_callback(callback) + local ro = rhs_options:new() + return ro:map_callback(callback) +end + +---@param cmd_string string +---@return string escaped_string +function bind.escape_termcode(cmd_string) + return vim.api.nvim_replace_termcodes(cmd_string, true, true, true) +end + +---@param mapping table +function bind.nvim_load_mapping(mapping) + for key, value in pairs(mapping) do + local modes, keymap = key:match("([^|]*)|?(.*)") + if type(value) == "table" then + for _, mode in ipairs(vim.split(modes, "")) do + local rhs = value.cmd + local options = value.options + local buf = value.buffer + if buf and type(buf) == "number" then + vim.api.nvim_buf_set_keymap(buf, mode, keymap, rhs, options) + else + vim.api.nvim_set_keymap(mode, keymap, rhs, options) + end + end + end + end +end + +return bind diff --git a/nvim/lua/keymap/completion.lua b/nvim/lua/keymap/completion.lua new file mode 100644 index 0000000..ae67e2a --- /dev/null +++ b/nvim/lua/keymap/completion.lua @@ -0,0 +1,74 @@ +local bind = require("keymap.bind") +local map_cr = bind.map_cr +local map_cmd = bind.map_cmd +local map_callback = bind.map_callback + +local plug_map = { + ["n|"] = map_cmd("FormatToggle"):with_noremap():with_desc("formatter: Toggle format on save"), +} +bind.nvim_load_mapping(plug_map) + +local mapping = {} + +function mapping.lsp(buf) + local map = { + -- LSP-related keymaps, ONLY effective in buffers with LSP(s) attached + ["n|li"] = map_cr("LspInfo"):with_silent():with_buffer(buf):with_desc("lsp: Info"), + ["n|lr"] = map_cr("LspRestart"):with_silent():with_buffer(buf):with_nowait():with_desc("lsp: Restart"), + ["n|go"] = map_cr("AerialToggle!"):with_silent():with_buffer(buf):with_desc("lsp: Toggle outline"), + ["n|gto"] = map_callback(function() + require("telescope").extensions.aerial.aerial() + end) + :with_silent() + :with_buffer(buf) + :with_desc("lsp: Toggle outline in Telescope"), + ["n|g["] = map_cr("Lspsaga diagnostic_jump_prev") + :with_silent() + :with_buffer(buf) + :with_desc("lsp: Prev diagnostic"), + ["n|g]"] = map_cr("Lspsaga diagnostic_jump_next") + :with_silent() + :with_buffer(buf) + :with_desc("lsp: Next diagnostic"), + ["n|lx"] = map_cr("Lspsaga show_line_diagnostics ++unfocus") + :with_silent() + :with_buffer(buf) + :with_desc("lsp: Line diagnostic"), + ["n|gs"] = map_callback(function() + vim.lsp.buf.signature_help() + end):with_desc("lsp: Signature help"), + ["n|gr"] = map_cr("Lspsaga rename"):with_silent():with_buffer(buf):with_desc("lsp: Rename in file range"), + ["n|gR"] = map_cr("Lspsaga rename ++project") + :with_silent() + :with_buffer(buf) + :with_desc("lsp: Rename in project range"), + ["n|K"] = map_cr("Lspsaga hover_doc"):with_silent():with_buffer(buf):with_desc("lsp: Show doc"), + ["nv|ga"] = map_cr("Lspsaga code_action") + :with_silent() + :with_buffer(buf) + :with_desc("lsp: Code action for cursor"), + ["n|gd"] = map_cr("Glance definitions"):with_silent():with_buffer(buf):with_desc("lsp: Preview definition"), + ["n|gD"] = map_cr("Lspsaga goto_definition"):with_silent():with_buffer(buf):with_desc("lsp: Goto definition"), + ["n|gh"] = map_cr("Glance references"):with_silent():with_buffer(buf):with_desc("lsp: Show reference"), + ["n|gm"] = map_cr("Glance implementations") + :with_silent() + :with_buffer(buf) + :with_desc("lsp: Show implementation"), + ["n|gci"] = map_cr("Lspsaga incoming_calls") + :with_silent() + :with_buffer(buf) + :with_desc("lsp: Show incoming calls"), + ["n|gco"] = map_cr("Lspsaga outgoing_calls") + :with_silent() + :with_buffer(buf) + :with_desc("lsp: Show outgoing calls"), + } + bind.nvim_load_mapping(map) + + local ok, user_mappings = pcall(require, "user.keymap.completion") + if ok and type(user_mappings.lsp) == "function" then + require("modules.utils.keymap").replace(user_mappings.lsp(buf)) + end +end + +return mapping diff --git a/nvim/lua/keymap/editor.lua b/nvim/lua/keymap/editor.lua new file mode 100644 index 0000000..9b0271c --- /dev/null +++ b/nvim/lua/keymap/editor.lua @@ -0,0 +1,109 @@ +local bind = require("keymap.bind") +local map_cr = bind.map_cr +local map_cu = bind.map_cu +local map_cmd = bind.map_cmd +local map_callback = bind.map_callback +local et = bind.escape_termcode + +local plug_map = { + -- Plugin persisted.nvim + ["n|ss"] = map_cu("SessionSave"):with_noremap():with_silent():with_desc("session: Save"), + ["n|sl"] = map_cu("SessionLoad"):with_noremap():with_silent():with_desc("session: Load current"), + ["n|sd"] = map_cu("SessionDelete"):with_noremap():with_silent():with_desc("session: Delete"), + + -- Plugin: nvim-bufdel + ["n|"] = map_cr("BufDel"):with_noremap():with_silent():with_desc("buffer: Close current"), + + -- Plugin: comment.nvim + ["n|gcc"] = map_callback(function() + return vim.v.count == 0 and et("(comment_toggle_linewise_current)") + or et("(comment_toggle_linewise_count)") + end) + :with_silent() + :with_noremap() + :with_expr() + :with_desc("edit: Toggle comment for line"), + ["n|gbc"] = map_callback(function() + return vim.v.count == 0 and et("(comment_toggle_blockwise_current)") + or et("(comment_toggle_blockwise_count)") + end) + :with_silent() + :with_noremap() + :with_expr() + :with_desc("edit: Toggle comment for block"), + ["n|gc"] = map_cmd("(comment_toggle_linewise)") + :with_silent() + :with_noremap() + :with_desc("edit: Toggle comment for line with operator"), + ["n|gb"] = map_cmd("(comment_toggle_blockwise)") + :with_silent() + :with_noremap() + :with_desc("edit: Toggle comment for block with operator"), + ["x|gc"] = map_cmd("(comment_toggle_linewise_visual)") + :with_silent() + :with_noremap() + :with_desc("edit: Toggle comment for line with selection"), + ["x|gb"] = map_cmd("(comment_toggle_blockwise_visual)") + :with_silent() + :with_noremap() + :with_desc("edit: Toggle comment for block with selection"), + + -- Plugin: diffview.nvim + ["n|gd"] = map_cr("DiffviewOpen"):with_silent():with_noremap():with_desc("git: Show diff"), + ["n|gD"] = map_cr("DiffviewClose"):with_silent():with_noremap():with_desc("git: Close diff"), + + -- Plugin: hop.nvim + ["nv|w"] = map_cmd("HopWordMW"):with_noremap():with_desc("jump: Goto word"), + ["nv|j"] = map_cmd("HopLineMW"):with_noremap():with_desc("jump: Goto line"), + ["nv|k"] = map_cmd("HopLineMW"):with_noremap():with_desc("jump: Goto line"), + ["nv|c"] = map_cmd("HopChar1MW"):with_noremap():with_desc("jump: Goto one char"), + ["nv|C"] = map_cmd("HopChar2MW"):with_noremap():with_desc("jump: Goto two chars"), + + -- Plugin: smart-splits.nvim + ["n|"] = map_cu("SmartResizeLeft"):with_silent():with_noremap():with_desc("window: Resize -3 horizontally"), + ["n|"] = map_cu("SmartResizeDown"):with_silent():with_noremap():with_desc("window: Resize -3 vertically"), + ["n|"] = map_cu("SmartResizeUp"):with_silent():with_noremap():with_desc("window: Resize +3 vertically"), + ["n|"] = map_cu("SmartResizeRight"):with_silent():with_noremap():with_desc("window: Resize +3 horizontally"), + ["n|"] = map_cu("SmartCursorMoveLeft"):with_silent():with_noremap():with_desc("window: Focus left"), + ["n|"] = map_cu("SmartCursorMoveDown"):with_silent():with_noremap():with_desc("window: Focus down"), + ["n|"] = map_cu("SmartCursorMoveUp"):with_silent():with_noremap():with_desc("window: Focus up"), + ["n|"] = map_cu("SmartCursorMoveRight"):with_silent():with_noremap():with_desc("window: Focus right"), + ["n|Wh"] = map_cu("SmartSwapLeft"):with_silent():with_noremap():with_desc("window: Move window leftward"), + ["n|Wj"] = map_cu("SmartSwapDown"):with_silent():with_noremap():with_desc("window: Move window downward"), + ["n|Wk"] = map_cu("SmartSwapUp"):with_silent():with_noremap():with_desc("window: Move window upward"), + ["n|Wl"] = map_cu("SmartSwapRight"):with_silent():with_noremap():with_desc("window: Move window rightward"), + + -- Plugin: nvim-spectre + ["n|Ss"] = map_callback(function() + require("spectre").toggle() + end) + :with_silent() + :with_noremap() + :with_desc("editn: Toggle search & replace panel"), + ["n|Sp"] = map_callback(function() + require("spectre").open_visual({ select_word = true }) + end) + :with_silent() + :with_noremap() + :with_desc("editn: search&replace current word (project)"), + ["v|Sp"] = map_callback(function() + require("spectre").open_visual() + end) + :with_silent() + :with_noremap() + :with_desc("edit: search & replace current word (project)"), + ["n|Sf"] = map_callback(function() + require("spectre").open_file_search({ select_word = true }) + end) + :with_silent() + :with_noremap() + :with_desc("editn: search & replace current word (file)"), + + -- Plugin: nvim-treehopper + ["o|m"] = map_cu("lua require('tsht').nodes()"):with_silent():with_desc("jump: Operate across syntax tree"), + + -- Plugin suda.vim + ["n|"] = map_cu("SudaWrite"):with_silent():with_noremap():with_desc("editn: Save file using sudo"), +} + +bind.nvim_load_mapping(plug_map) diff --git a/nvim/lua/keymap/helpers.lua b/nvim/lua/keymap/helpers.lua new file mode 100644 index 0000000..60d99e4 --- /dev/null +++ b/nvim/lua/keymap/helpers.lua @@ -0,0 +1,67 @@ +_G._command_panel = function() + require("telescope.builtin").keymaps({ + lhs_filter = function(lhs) + return not string.find(lhs, "Þ") + end, + layout_config = { + width = 0.6, + height = 0.6, + prompt_position = "top", + }, + }) +end + +_G._telescope_collections = function(picker_type) + local actions = require("telescope.actions") + local action_state = require("telescope.actions.state") + local conf = require("telescope.config").values + local finder = require("telescope.finders") + local pickers = require("telescope.pickers") + picker_type = picker_type or {} + + local collections = vim.tbl_keys(require("search.tabs").collections) + pickers + .new(picker_type, { + prompt_title = "Telescope Collections", + finder = finder.new_table({ results = collections }), + sorter = conf.generic_sorter(picker_type), + attach_mappings = function(bufnr) + actions.select_default:replace(function() + actions.close(bufnr) + local selection = action_state.get_selected_entry() + require("search").open({ collection = selection[1] }) + end) + + return true + end, + }) + :find() +end + +_G._flash_esc_or_noh = function() + local flash_active, state = pcall(function() + return require("flash.plugins.char").state + end) + if flash_active and state then + state:hide() + else + pcall(vim.cmd.noh) + end +end + +local _lazygit = nil +_G._toggle_lazygit = function() + if vim.fn.executable("lazygit") == 1 then + if not _lazygit then + _lazygit = require("toggleterm.terminal").Terminal:new({ + cmd = "lazygit", + direction = "float", + close_on_exit = true, + hidden = true, + }) + end + _lazygit:toggle() + else + vim.notify("Command [lazygit] not found!", vim.log.levels.ERROR, { title = "toggleterm.nvim" }) + end +end diff --git a/nvim/lua/keymap/init.lua b/nvim/lua/keymap/init.lua new file mode 100644 index 0000000..dfef620 --- /dev/null +++ b/nvim/lua/keymap/init.lua @@ -0,0 +1,35 @@ +require("keymap.helpers") +local bind = require("keymap.bind") +local map_cr = bind.map_cr +-- local map_cu = bind.map_cu +-- local map_cmd = bind.map_cmd +-- local map_callback = bind.map_callback + +local plug_map = { + -- Package manager: lazy.nvim + ["n|ph"] = map_cr("Lazy"):with_silent():with_noremap():with_nowait():with_desc("package: Show"), + ["n|ps"] = map_cr("Lazy sync"):with_silent():with_noremap():with_nowait():with_desc("package: Sync"), + ["n|pu"] = map_cr("Lazy update"):with_silent():with_noremap():with_nowait():with_desc("package: Update"), + ["n|pi"] = map_cr("Lazy install"):with_silent():with_noremap():with_nowait():with_desc("package: Install"), + ["n|pl"] = map_cr("Lazy log"):with_silent():with_noremap():with_nowait():with_desc("package: Log"), + ["n|pc"] = map_cr("Lazy check"):with_silent():with_noremap():with_nowait():with_desc("package: Check"), + ["n|pd"] = map_cr("Lazy debug"):with_silent():with_noremap():with_nowait():with_desc("package: Debug"), + ["n|pp"] = map_cr("Lazy profile"):with_silent():with_noremap():with_nowait():with_desc("package: Profile"), + ["n|pr"] = map_cr("Lazy restore"):with_silent():with_noremap():with_nowait():with_desc("package: Restore"), + ["n|px"] = map_cr("Lazy clean"):with_silent():with_noremap():with_nowait():with_desc("package: Clean"), +} + +bind.nvim_load_mapping(plug_map) + +-- Plugin keymaps +require("keymap.completion") +require("keymap.editor") +require("keymap.lang") +require("keymap.tool") +require("keymap.ui") + +-- User keymaps +local ok, mappings = pcall(require, "user.keymap.init") +if ok then + require("modules.utils.keymap").replace(mappings) +end diff --git a/nvim/lua/keymap/lang.lua b/nvim/lua/keymap/lang.lua new file mode 100644 index 0000000..46ec703 --- /dev/null +++ b/nvim/lua/keymap/lang.lua @@ -0,0 +1,12 @@ +local bind = require("keymap.bind") +local map_cr = bind.map_cr +-- local map_cu = bind.map_cu +-- local map_cmd = bind.map_cmd +-- local map_callback = bind.map_callback + +local plug_map = { + -- Plugin MarkdownPreview + ["n|"] = map_cr("MarkdownPreviewToggle"):with_noremap():with_silent():with_desc("tool: Preview markdown"), +} + +bind.nvim_load_mapping(plug_map) diff --git a/nvim/lua/keymap/tool.lua b/nvim/lua/keymap/tool.lua new file mode 100644 index 0000000..617a2d0 --- /dev/null +++ b/nvim/lua/keymap/tool.lua @@ -0,0 +1,191 @@ +local bind = require("keymap.bind") +local map_cr = bind.map_cr +local map_cu = bind.map_cu +local map_cmd = bind.map_cmd +local map_callback = bind.map_callback +require("keymap.helpers") + +local plug_map = { + -- Plugin: vim-fugitive + ["n|gps"] = map_cr("G push"):with_noremap():with_silent():with_desc("git: Push"), + ["n|gpl"] = map_cr("G pull"):with_noremap():with_silent():with_desc("git: Pull"), + ["n|gG"] = map_cu("Git"):with_noremap():with_silent():with_desc("git: Open git-fugitive"), + + -- Plugin: nvim-tree + ["n|"] = map_cr("NvimTreeToggle"):with_noremap():with_silent():with_desc("filetree: Toggle"), + ["n|nf"] = map_cr("NvimTreeFindFile"):with_noremap():with_silent():with_desc("filetree: Find file"), + ["n|nr"] = map_cr("NvimTreeRefresh"):with_noremap():with_silent():with_desc("filetree: Refresh"), + + -- Plugin: sniprun + ["v|r"] = map_cr("SnipRun"):with_noremap():with_silent():with_desc("tool: Run code by range"), + ["n|r"] = map_cu([[%SnipRun]]):with_noremap():with_silent():with_desc("tool: Run code by file"), + + -- Plugin: toggleterm + ["t|"] = map_cmd([[]]):with_noremap():with_silent(), -- switch to normal mode in terminal. + ["n|"] = map_cr("ToggleTerm direction=horizontal") + :with_noremap() + :with_silent() + :with_desc("terminal: Toggle horizontal"), + ["i|"] = map_cmd("ToggleTerm direction=horizontal") + :with_noremap() + :with_silent() + :with_desc("terminal: Toggle horizontal"), + ["t|"] = map_cmd("ToggleTerm"):with_noremap():with_silent():with_desc("terminal: Toggle horizontal"), + ["n|"] = map_cr("ToggleTerm direction=vertical") + :with_noremap() + :with_silent() + :with_desc("terminal: Toggle vertical"), + ["i|"] = map_cmd("ToggleTerm direction=vertical") + :with_noremap() + :with_silent() + :with_desc("terminal: Toggle vertical"), + ["t|"] = map_cmd("ToggleTerm"):with_noremap():with_silent():with_desc("terminal: Toggle vertical"), + ["n|"] = map_cr("ToggleTerm direction=vertical") + :with_noremap() + :with_silent() + :with_desc("terminal: Toggle vertical"), + ["i|"] = map_cmd("ToggleTerm direction=vertical") + :with_noremap() + :with_silent() + :with_desc("terminal: Toggle vertical"), + ["t|"] = map_cmd("ToggleTerm"):with_noremap():with_silent():with_desc("terminal: Toggle vertical"), + ["n|"] = map_cr("ToggleTerm direction=float"):with_noremap():with_silent():with_desc("terminal: Toggle float"), + ["i|"] = map_cmd("ToggleTerm direction=float") + :with_noremap() + :with_silent() + :with_desc("terminal: Toggle float"), + ["t|"] = map_cmd("ToggleTerm"):with_noremap():with_silent():with_desc("terminal: Toggle float"), + ["n|gg"] = map_callback(function() + _toggle_lazygit() + end) + :with_noremap() + :with_silent() + :with_desc("git: Toggle lazygit"), + + -- Plugin: trouble + ["n|gt"] = map_cr("Trouble diagnostics toggle"):with_noremap():with_silent():with_desc("lsp: Toggle trouble list"), + ["n|lw"] = map_cr("Trouble diagnostics toggle") + :with_noremap() + :with_silent() + :with_desc("lsp: Show workspace diagnostics"), + ["n|lp"] = map_cr("Trouble project_diagnostics toggle") + :with_noremap() + :with_silent() + :with_desc("lsp: Show project diagnostics"), + ["n|ld"] = map_cr("Trouble diagnostics toggle filter.buf=0") + :with_noremap() + :with_silent() + :with_desc("lsp: Show document diagnostics"), + + -- Plugin: telescope + ["n|"] = map_callback(function() + _command_panel() + end) + :with_noremap() + :with_silent() + :with_desc("tool: Toggle command panel"), + ["n|fc"] = map_callback(function() + _telescope_collections(require("telescope.themes").get_dropdown()) + end) + :with_noremap() + :with_silent() + :with_desc("tool: Open Telescope collections"), + ["n|ff"] = map_callback(function() + require("search").open({ collection = "file" }) + end) + :with_noremap() + :with_silent() + :with_desc("tool: Find files"), + ["n|fp"] = map_callback(function() + require("search").open({ collection = "pattern" }) + end) + :with_noremap() + :with_silent() + :with_desc("tool: Find patterns"), + ["v|fs"] = map_cu("Telescope grep_string") + :with_noremap() + :with_silent() + :with_desc("tool: Find word under cursor"), + ["n|fg"] = map_callback(function() + require("search").open({ collection = "git" }) + end) + :with_noremap() + :with_silent() + :with_desc("tool: Locate Git objects"), + ["n|fd"] = map_callback(function() + require("search").open({ collection = "dossier" }) + end) + :with_noremap() + :with_silent() + :with_desc("tool: Retrieve dossiers"), + ["n|fm"] = map_callback(function() + require("search").open({ collection = "misc" }) + end) + :with_noremap() + :with_silent() + :with_desc("tool: Miscellaneous"), + + -- Plugin: dap + ["n|"] = map_callback(function() + require("dap").continue() + end) + :with_noremap() + :with_silent() + :with_desc("debug: Run/Continue"), + ["n|"] = map_callback(function() + require("dap").terminate() + end) + :with_noremap() + :with_silent() + :with_desc("debug: Stop"), + ["n|"] = map_callback(function() + require("dap").toggle_breakpoint() + end) + :with_noremap() + :with_silent() + :with_desc("debug: Toggle breakpoint"), + ["n|"] = map_callback(function() + require("dap").step_into() + end) + :with_noremap() + :with_silent() + :with_desc("debug: Step into"), + ["n|"] = map_callback(function() + require("dap").step_out() + end) + :with_noremap() + :with_silent() + :with_desc("debug: Step out"), + ["n|"] = map_callback(function() + require("dap").step_over() + end) + :with_noremap() + :with_silent() + :with_desc("debug: Step over"), + ["n|db"] = map_callback(function() + require("dap").set_breakpoint(vim.fn.input("Breakpoint condition: ")) + end) + :with_noremap() + :with_silent() + :with_desc("debug: Set breakpoint with condition"), + ["n|dc"] = map_callback(function() + require("dap").run_to_cursor() + end) + :with_noremap() + :with_silent() + :with_desc("debug: Run to cursor"), + ["n|dl"] = map_callback(function() + require("dap").run_last() + end) + :with_noremap() + :with_silent() + :with_desc("debug: Run last"), + ["n|do"] = map_callback(function() + require("dap").repl.open() + end) + :with_noremap() + :with_silent() + :with_desc("debug: Open REPL"), +} + +bind.nvim_load_mapping(plug_map) diff --git a/nvim/lua/keymap/ui.lua b/nvim/lua/keymap/ui.lua new file mode 100644 index 0000000..77df22d --- /dev/null +++ b/nvim/lua/keymap/ui.lua @@ -0,0 +1,105 @@ +local bind = require("keymap.bind") +local map_cr = bind.map_cr +-- local map_cu = bind.map_cu +-- local map_cmd = bind.map_cmd +-- local map_callback = bind.map_callback + +local plug_map = { + -- Plugin: bufferline.nvim + ["n|"] = map_cr("BufferLineCycleNext"):with_noremap():with_silent():with_desc("buffer: Switch to next"), + ["n|"] = map_cr("BufferLineCyclePrev"):with_noremap():with_silent():with_desc("buffer: Switch to prev"), + ["n|"] = map_cr("BufferLineMoveNext"):with_noremap():with_silent():with_desc("buffer: Move current to next"), + ["n|"] = map_cr("BufferLineMovePrev"):with_noremap():with_silent():with_desc("buffer: Move current to prev"), + ["n|be"] = map_cr("BufferLineSortByExtension"):with_noremap():with_desc("buffer: Sort by extension"), + ["n|bd"] = map_cr("BufferLineSortByDirectory"):with_noremap():with_desc("buffer: Sort by directory"), + ["n|"] = map_cr("BufferLineGoToBuffer 1"):with_noremap():with_silent():with_desc("buffer: Goto buffer 1"), + ["n|"] = map_cr("BufferLineGoToBuffer 2"):with_noremap():with_silent():with_desc("buffer: Goto buffer 2"), + ["n|"] = map_cr("BufferLineGoToBuffer 3"):with_noremap():with_silent():with_desc("buffer: Goto buffer 3"), + ["n|"] = map_cr("BufferLineGoToBuffer 4"):with_noremap():with_silent():with_desc("buffer: Goto buffer 4"), + ["n|"] = map_cr("BufferLineGoToBuffer 5"):with_noremap():with_silent():with_desc("buffer: Goto buffer 5"), + ["n|"] = map_cr("BufferLineGoToBuffer 6"):with_noremap():with_silent():with_desc("buffer: Goto buffer 6"), + ["n|"] = map_cr("BufferLineGoToBuffer 7"):with_noremap():with_silent():with_desc("buffer: Goto buffer 7"), + ["n|"] = map_cr("BufferLineGoToBuffer 8"):with_noremap():with_silent():with_desc("buffer: Goto buffer 8"), + ["n|"] = map_cr("BufferLineGoToBuffer 9"):with_noremap():with_silent():with_desc("buffer: Goto buffer 9"), +} + +bind.nvim_load_mapping(plug_map) + +local mapping = {} + +function mapping.gitsigns(buf) + local actions = require("gitsigns.actions") + local map = { + ["n|]g"] = bind.map_callback(function() + if vim.wo.diff then + return "]g" + end + vim.schedule(function() + actions.next_hunk() + end) + return "" + end) + :with_buffer(buf) + :with_expr() + :with_desc("git: Goto next hunk"), + ["n|[g"] = bind.map_callback(function() + if vim.wo.diff then + return "[g" + end + vim.schedule(function() + actions.prev_hunk() + end) + return "" + end) + :with_buffer(buf) + :with_expr() + :with_desc("git: Goto prev hunk"), + ["n|gs"] = bind.map_callback(function() + actions.stage_hunk() + end) + :with_buffer(buf) + :with_desc("git: Stage hunk"), + ["v|gs"] = bind.map_callback(function() + actions.stage_hunk({ vim.fn.line("."), vim.fn.line("v") }) + end) + :with_buffer(buf) + :with_desc("git: Stage hunk"), + ["n|gu"] = bind.map_callback(function() + actions.undo_stage_hunk() + end) + :with_buffer(buf) + :with_desc("git: Undo stage hunk"), + ["n|gr"] = bind.map_callback(function() + actions.reset_hunk() + end) + :with_buffer(buf) + :with_desc("git: Reset hunk"), + ["v|gr"] = bind.map_callback(function() + actions.reset_hunk({ vim.fn.line("."), vim.fn.line("v") }) + end) + :with_buffer(buf) + :with_desc("git: Reset hunk"), + ["n|gR"] = bind.map_callback(function() + actions.reset_buffer() + end) + :with_buffer(buf) + :with_desc("git: Reset buffer"), + ["n|gp"] = bind.map_callback(function() + actions.preview_hunk() + end) + :with_buffer(buf) + :with_desc("git: Preview hunk"), + ["n|gb"] = bind.map_callback(function() + actions.blame_line({ full = true }) + end) + :with_buffer(buf) + :with_desc("git: Blame line"), + -- Text objects + ["ox|ih"] = bind.map_callback(function() + actions.text_object() + end):with_buffer(buf), + } + bind.nvim_load_mapping(map) +end + +return mapping diff --git a/nvim/lua/modules/configs/completion/aerial.lua b/nvim/lua/modules/configs/completion/aerial.lua new file mode 100644 index 0000000..4c89afd --- /dev/null +++ b/nvim/lua/modules/configs/completion/aerial.lua @@ -0,0 +1,80 @@ +return function() + require("modules.utils").load_plugin("aerial", { + lazy_load = false, + close_on_select = true, + highlight_on_jump = false, + disable_max_lines = 8500, + disable_max_size = 1000000, + ignore = { filetypes = { "NvimTree", "terminal", "nofile" } }, + -- Use symbol tree for folding. Set to true or false to enable/disable + -- Set to "auto" to manage folds if your previous foldmethod was 'manual' + -- This can be a filetype map (see :help aerial-filetype-map) + manage_folds = "auto", + layout = { + -- Determines the default direction to open the aerial window. The 'prefer' + -- options will open the window in the other direction *if* there is a + -- different buffer in the way of the preferred direction + -- Enum: prefer_right, prefer_left, right, left, float + default_direction = "prefer_right", + }, + -- Keymaps in aerial window. Can be any value that `vim.keymap.set` accepts OR a table of keymap + -- options with a `callback` (e.g. { callback = function() ... end, desc = "", nowait = true }) + -- Additionally, if it is a string that matches "actions.", + -- it will use the mapping at require("aerial.actions"). + -- Set to `false` to remove a keymap + keymaps = { + ["?"] = "actions.show_help", + ["g?"] = "actions.show_help", + [""] = "actions.jump", + ["<2-LeftMouse>"] = "actions.jump", + [""] = "actions.jump_vsplit", + [""] = "actions.jump_split", + [""] = "actions.down_and_scroll", + [""] = "actions.up_and_scroll", + ["{"] = "actions.prev", + ["}"] = "actions.next", + ["[["] = "actions.prev_up", + ["]]"] = "actions.next_up", + ["q"] = "actions.close", + ["o"] = "actions.tree_toggle", + ["O"] = "actions.tree_toggle_recursive", + ["zr"] = "actions.tree_increase_fold_level", + ["zR"] = "actions.tree_open_all", + ["zm"] = "actions.tree_decrease_fold_level", + ["zM"] = "actions.tree_close_all", + ["zx"] = "actions.tree_sync_folds", + ["zX"] = "actions.tree_sync_folds", + }, + -- A list of all symbols to display. Set to false to display all symbols. + -- This can be a filetype map (see :help aerial-filetype-map) + -- To see all available values, see :help SymbolKind + filter_kind = { + "Array", + "Boolean", + "Class", + "Constant", + "Constructor", + "Enum", + "EnumMember", + "Event", + "Field", + "File", + "Function", + "Interface", + "Key", + "Method", + "Module", + "Namespace", + "Null", + -- "Number", + "Object", + "Operator", + "Package", + -- "Property", + -- "String", + "Struct", + -- "TypeParameter", + -- "Variable", + }, + }) +end diff --git a/nvim/lua/modules/configs/completion/cmp.lua b/nvim/lua/modules/configs/completion/cmp.lua new file mode 100644 index 0000000..f20e7b6 --- /dev/null +++ b/nvim/lua/modules/configs/completion/cmp.lua @@ -0,0 +1,199 @@ +return function() + local icons = { + kind = require("modules.utils.icons").get("kind"), + type = require("modules.utils.icons").get("type"), + cmp = require("modules.utils.icons").get("cmp"), + } + + local border = function(hl) + return { + { "┌", hl }, + { "─", hl }, + { "┐", hl }, + { "│", hl }, + { "┘", hl }, + { "─", hl }, + { "└", hl }, + { "│", hl }, + } + end + + local compare = require("cmp.config.compare") + compare.lsp_scores = function(entry1, entry2) + local diff + if entry1.completion_item.score and entry2.completion_item.score then + diff = (entry2.completion_item.score * entry2.score) - (entry1.completion_item.score * entry1.score) + else + diff = entry2.score - entry1.score + end + return (diff < 0) + end + + local use_copilot = require("core.settings").use_copilot + local comparators = use_copilot == true + and { + require("copilot_cmp.comparators").prioritize, + require("copilot_cmp.comparators").score, + -- require("cmp_tabnine.compare"), + compare.offset, -- Items closer to cursor will have lower priority + compare.exact, + -- compare.scopes, + compare.lsp_scores, + compare.sort_text, + compare.score, + compare.recently_used, + -- compare.locality, -- Items closer to cursor will have higher priority, conflicts with `offset` + require("cmp-under-comparator").under, + compare.kind, + compare.length, + compare.order, + } + or { + -- require("cmp_tabnine.compare"), + compare.offset, -- Items closer to cursor will have lower priority + compare.exact, + -- compare.scopes, + compare.lsp_scores, + compare.sort_text, + compare.score, + compare.recently_used, + -- compare.locality, -- Items closer to cursor will have higher priority, conflicts with `offset` + require("cmp-under-comparator").under, + compare.kind, + compare.length, + compare.order, + } + + local cmp = require("cmp") + require("modules.utils").load_plugin("cmp", { + preselect = cmp.PreselectMode.None, + window = { + completion = { + border = border("PmenuBorder"), + winhighlight = "Normal:Pmenu,CursorLine:PmenuSel,Search:PmenuSel", + scrollbar = false, + }, + documentation = { + border = border("CmpDocBorder"), + winhighlight = "Normal:CmpDoc", + }, + }, + sorting = { + priority_weight = 2, + comparators = comparators, + }, + formatting = { + fields = { "abbr", "kind", "menu" }, + format = function(entry, vim_item) + local lspkind_icons = vim.tbl_deep_extend("force", icons.kind, icons.type, icons.cmp) + -- load lspkind icons + vim_item.kind = + string.format(" %s %s", lspkind_icons[vim_item.kind] or icons.cmp.undefined, vim_item.kind or "") + + vim_item.menu = setmetatable({ + cmp_tabnine = "[TN]", + copilot = "[CPLT]", + buffer = "[BUF]", + orgmode = "[ORG]", + nvim_lsp = "[LSP]", + nvim_lua = "[LUA]", + path = "[PATH]", + tmux = "[TMUX]", + treesitter = "[TS]", + latex_symbols = "[LTEX]", + luasnip = "[SNIP]", + spell = "[SPELL]", + }, { + __index = function() + return "[BTN]" -- builtin/unknown source names + end, + })[entry.source.name] + + local label = vim_item.abbr + local truncated_label = vim.fn.strcharpart(label, 0, 80) + if truncated_label ~= label then + vim_item.abbr = truncated_label .. "..." + end + + return vim_item + end, + }, + matching = { + disallow_partial_fuzzy_matching = false, + }, + performance = { + async_budget = 1, + max_view_entries = 120, + }, + -- You can set mappings if you want + mapping = cmp.mapping.preset.insert({ + [""] = cmp.mapping.select_prev_item({ behavior = cmp.SelectBehavior.Select }), + [""] = cmp.mapping.select_next_item({ behavior = cmp.SelectBehavior.Select }), + [""] = cmp.mapping.scroll_docs(-4), + [""] = cmp.mapping.scroll_docs(4), + [""] = cmp.mapping.abort(), + [""] = cmp.mapping(function(fallback) + if cmp.visible() then + cmp.select_next_item({ behavior = cmp.SelectBehavior.Select }) + elseif require("luasnip").expand_or_locally_jumpable() then + require("luasnip").expand_or_jump() + else + fallback() + end + end, { "i", "s" }), + [""] = cmp.mapping(function(fallback) + if cmp.visible() then + cmp.select_prev_item({ behavior = cmp.SelectBehavior.Select }) + elseif require("luasnip").jumpable(-1) then + require("luasnip").jump(-1) + else + fallback() + end + end, { "i", "s" }), + [""] = cmp.mapping({ + i = function(fallback) + if cmp.visible() and cmp.get_active_entry() then + cmp.confirm({ behavior = cmp.ConfirmBehavior.Insert, select = false }) + else + fallback() + end + end, + s = cmp.mapping.confirm({ select = true }), + c = cmp.mapping.confirm({ behavior = cmp.ConfirmBehavior.Insert, select = true }), + }), + }), + snippet = { + expand = function(args) + require("luasnip").lsp_expand(args.body) + end, + }, + -- You should specify your *installed* sources. + sources = { + { name = "nvim_lsp", max_item_count = 350 }, + { name = "nvim_lua" }, + { name = "luasnip" }, + { name = "path" }, + { name = "treesitter" }, + { name = "spell" }, + { name = "tmux" }, + { name = "orgmode" }, + { + name = "buffer", + option = { + get_bufnrs = function() + return vim.api.nvim_buf_line_count(0) < 7500 and vim.api.nvim_list_bufs() or {} + end, + }, + }, + { name = "latex_symbols" }, + { name = "copilot" }, + -- { name = "codeium" }, + -- { name = "cmp_tabnine" }, + }, + experimental = { + ghost_text = { + hl_group = "Whitespace", + }, + }, + }) +end diff --git a/nvim/lua/modules/configs/completion/codeium.lua b/nvim/lua/modules/configs/completion/codeium.lua new file mode 100644 index 0000000..229eab6 --- /dev/null +++ b/nvim/lua/modules/configs/completion/codeium.lua @@ -0,0 +1,3 @@ +return function() + require("modules.utils").load_plugin("codeium", {}) +end diff --git a/nvim/lua/modules/configs/completion/copilot-cmp.lua b/nvim/lua/modules/configs/completion/copilot-cmp.lua new file mode 100644 index 0000000..26d1dad --- /dev/null +++ b/nvim/lua/modules/configs/completion/copilot-cmp.lua @@ -0,0 +1,3 @@ +return function() + require("modules.utils").load_plugin("copilot_cmp", {}) +end diff --git a/nvim/lua/modules/configs/completion/copilot.lua b/nvim/lua/modules/configs/completion/copilot.lua new file mode 100644 index 0000000..a6ea78c --- /dev/null +++ b/nvim/lua/modules/configs/completion/copilot.lua @@ -0,0 +1,28 @@ +return function() + vim.defer_fn(function() + require("modules.utils").load_plugin("copilot", { + cmp = { + enabled = true, + method = "getCompletionsCycling", + }, + panel = { + -- if true, it can interfere with completions in copilot-cmp + enabled = false, + }, + suggestion = { + -- if true, it can interfere with completions in copilot-cmp + enabled = false, + }, + filetypes = { + ["bigfile"] = false, + ["dap-repl"] = false, + ["fugitive"] = false, + ["fugitiveblame"] = false, + ["git"] = false, + ["gitcommit"] = false, + ["log"] = false, + ["toggleterm"] = false, + }, + }) + end, 100) +end diff --git a/nvim/lua/modules/configs/completion/formatters/clang_format.lua b/nvim/lua/modules/configs/completion/formatters/clang_format.lua new file mode 100644 index 0000000..a0f81bf --- /dev/null +++ b/nvim/lua/modules/configs/completion/formatters/clang_format.lua @@ -0,0 +1 @@ +return { "-style={BasedOnStyle: LLVM, IndentWidth: 4}" } diff --git a/nvim/lua/modules/configs/completion/formatting.lua b/nvim/lua/modules/configs/completion/formatting.lua new file mode 100644 index 0000000..0368efe --- /dev/null +++ b/nvim/lua/modules/configs/completion/formatting.lua @@ -0,0 +1,198 @@ +local M = {} + +local settings = require("core.settings") +local disabled_workspaces = settings.format_disabled_dirs +local format_on_save = settings.format_on_save +local format_notify = settings.format_notify +local format_modifications_only = settings.format_modifications_only +local server_formatting_block_list = settings.server_formatting_block_list +local format_timeout = settings.format_timeout + +vim.api.nvim_create_user_command("FormatToggle", function() + M.toggle_format_on_save() +end, {}) + +local block_list = settings.formatter_block_list +vim.api.nvim_create_user_command("FormatterToggleFt", function(opts) + if block_list[opts.args] == nil then + vim.notify( + string.format("[LSP] Formatter for [%s] has been recorded in list and disabled.", opts.args), + vim.log.levels.WARN, + { title = "LSP Formatter Warning" } + ) + block_list[opts.args] = true + else + block_list[opts.args] = not block_list[opts.args] + vim.notify( + string.format( + "[LSP] Formatter for [%s] has been %s.", + opts.args, + not block_list[opts.args] and "enabled" or "disabled" + ), + not block_list[opts.args] and vim.log.levels.INFO or vim.log.levels.WARN, + { title = string.format("LSP Formatter %s", not block_list[opts.args] and "Info" or "Warning") } + ) + end +end, { nargs = 1, complete = "filetype" }) + +function M.enable_format_on_save(is_configured) + local opts = { pattern = "*", timeout = format_timeout } + vim.api.nvim_create_augroup("format_on_save", { clear = true }) + vim.api.nvim_create_autocmd("BufWritePre", { + group = "format_on_save", + pattern = opts.pattern, + callback = function() + require("completion.formatting").format({ + timeout_ms = opts.timeout, + filter = M.format_filter, + }) + end, + }) + if not is_configured then + vim.notify( + "Successfully enabled format-on-save", + vim.log.levels.INFO, + { title = "Settings modification success" } + ) + end +end + +function M.disable_format_on_save(is_configured) + pcall(vim.api.nvim_del_augroup_by_name, "format_on_save") + if not is_configured then + vim.notify( + "Successfully disabled format-on-save", + vim.log.levels.INFO, + { title = "Settings modification success" } + ) + end +end + +function M.configure_format_on_save() + if format_on_save then + M.enable_format_on_save(true) + else + M.disable_format_on_save(true) + end +end + +function M.toggle_format_on_save() + local status = pcall(vim.api.nvim_get_autocmds, { + group = "format_on_save", + event = "BufWritePre", + }) + if not status then + M.enable_format_on_save(false) + else + M.disable_format_on_save(false) + end +end + +function M.format_filter(clients) + return vim.tbl_filter(function(client) + local status_ok, formatting_supported = pcall(function() + return client.supports_method("textDocument/formatting") + end) + if status_ok and formatting_supported and client.name == "null-ls" then + return "null-ls" + elseif not server_formatting_block_list[client.name] and status_ok and formatting_supported then + return client.name + end + end, clients) +end + +function M.format(opts) + local filedir = vim.fn.expand("%:p:h") + for i = 1, #disabled_workspaces do + if vim.regex(vim.fs.normalize(disabled_workspaces[i])):match_str(filedir) ~= nil then + vim.notify( + string.format( + "[LSP] Formatting for all files under [%s] has been disabled.", + vim.fs.normalize(disabled_workspaces[i]) + ), + vim.log.levels.WARN, + { title = "LSP Formatter Warning" } + ) + return + end + end + + local bufnr = opts.bufnr or vim.api.nvim_get_current_buf() + local clients = vim.lsp.get_clients({ buffer = bufnr }) + + if opts.filter then + clients = opts.filter(clients) + elseif opts.id then + clients = vim.tbl_filter(function(client) + return client.id == opts.id + end, clients) + elseif opts.name then + clients = vim.tbl_filter(function(client) + return client.name == opts.name + end, clients) + end + + clients = vim.tbl_filter(function(client) + return client.supports_method("textDocument/formatting") + end, clients) + + if #clients == 0 then + vim.notify( + "[LSP] Format request failed, no matching language servers.", + vim.log.levels.WARN, + { title = "Formatting Failed" } + ) + end + + local timeout_ms = opts.timeout_ms + for _, client in pairs(clients) do + if block_list[vim.bo.filetype] == true then + vim.notify( + string.format( + "[LSP][%s] Formatting for [%s] has been disabled. This file is not being processed.", + client.name, + vim.bo.filetype + ), + vim.log.levels.WARN, + { title = "LSP Formatter Warning" } + ) + return + end + + if + format_modifications_only + and require("lsp-format-modifications").format_modifications(client, bufnr).success + then + if format_notify then + vim.notify( + string.format("[LSP] Format changed lines successfully with %s!", client.name), + vim.log.levels.INFO, + { title = "LSP Range Format Success" } + ) + end + return + end + + -- Fall back to format the whole buffer (even if partial formatting failed) + local params = vim.lsp.util.make_formatting_params(opts.formatting_options) + local result, err = client.request_sync("textDocument/formatting", params, timeout_ms, bufnr) + if result and result.result then + vim.lsp.util.apply_text_edits(result.result, bufnr, client.offset_encoding) + if format_notify then + vim.notify( + string.format("[LSP] Format successfully with %s!", client.name), + vim.log.levels.INFO, + { title = "LSP Format Success" } + ) + end + elseif err then + vim.notify( + string.format("[LSP][%s] %s", client.name, err), + vim.log.levels.ERROR, + { title = "LSP Format Error" } + ) + end + end +end + +return M diff --git a/nvim/lua/modules/configs/completion/glance.lua b/nvim/lua/modules/configs/completion/glance.lua new file mode 100644 index 0000000..f2950de --- /dev/null +++ b/nvim/lua/modules/configs/completion/glance.lua @@ -0,0 +1,83 @@ +return function() + local icons = { ui = require("modules.utils.icons").get("ui", true) } + local actions = require("glance").actions + + require("modules.utils").load_plugin("glance", { + height = 20, + zindex = 50, + preview_win_opts = { + cursorline = true, + number = true, + wrap = true, + }, + border = { + enable = require("core.settings").transparent_background, + top_char = "―", + bottom_char = "―", + }, + list = { + position = "right", + width = 0.33, -- 33% width relative to the active window, min 0.1, max 0.5 + }, + folds = { + folded = true, -- Automatically fold list on startup + fold_closed = icons.ui.ArrowClosed, + fold_open = icons.ui.ArrowOpen, + }, + indent_lines = { enable = true }, + winbar = { enable = true }, + mappings = { + list = { + ["k"] = actions.previous, + ["j"] = actions.next, + [""] = actions.previous, + [""] = actions.next, + [""] = actions.previous_location, -- Bring the cursor to the previous location skipping groups in the list + [""] = actions.next_location, -- Bring the cursor to the next location skipping groups in the list + [""] = actions.preview_scroll_win(8), + [""] = actions.preview_scroll_win(-8), + [""] = actions.jump, + ["v"] = actions.jump_vsplit, + ["s"] = actions.jump_split, + ["t"] = actions.jump_tab, + ["c"] = actions.close_fold, + ["o"] = actions.open_fold, + ["[]"] = actions.enter_win("preview"), -- Focus preview window + ["q"] = actions.close, + ["Q"] = actions.close, + [""] = actions.close, + ["gq"] = actions.quickfix, + }, + preview = { + ["Q"] = actions.close, + ["q"] = actions.close, + ["o"] = actions.jump, + ["v"] = actions.jump_vsplit, + ["s"] = actions.jump_split, + ["t"] = actions.jump_tab, + [""] = actions.previous_location, + [""] = actions.next_location, + ["[]"] = actions.enter_win("list"), -- Focus list window + }, + }, + hooks = { + before_open = function(results, open, _, method) + if #results == 0 then + vim.notify( + "This method is not supported by any of the servers registered for the current buffer", + vim.log.levels.WARN, + { title = "Glance" } + ) + elseif #results == 1 and method == "references" then + vim.notify( + "The identifier under cursor is the only one found", + vim.log.levels.INFO, + { title = "Glance" } + ) + else + open(results) + end + end, + }, + }) +end diff --git a/nvim/lua/modules/configs/completion/lsp-signature.lua b/nvim/lua/modules/configs/completion/lsp-signature.lua new file mode 100644 index 0000000..aacc20c --- /dev/null +++ b/nvim/lua/modules/configs/completion/lsp-signature.lua @@ -0,0 +1,15 @@ +return function() + require("modules.utils").load_plugin("lsp_signature", { + bind = true, + -- TODO: Remove the following line when nvim-cmp#1613 gets resolved + check_completion_visible = false, + floating_window = true, + floating_window_above_cur_line = true, + hi_parameter = "Search", + hint_enable = true, + transparency = nil, -- disabled by default, allow floating win transparent value 1~100 + wrap = true, + zindex = 45, -- avoid overlap with nvim.cmp + handler_opts = { border = "single" }, + }) +end diff --git a/nvim/lua/modules/configs/completion/lsp.lua b/nvim/lua/modules/configs/completion/lsp.lua new file mode 100644 index 0000000..ba3c5a8 --- /dev/null +++ b/nvim/lua/modules/configs/completion/lsp.lua @@ -0,0 +1,23 @@ +return function() + local nvim_lsp = require("lspconfig") + require("completion.neoconf").setup() + require("completion.mason").setup() + require("completion.mason-lspconfig").setup() + + local opts = { + capabilities = require("cmp_nvim_lsp").default_capabilities(vim.lsp.protocol.make_client_capabilities()), + } + -- Setup lsps that are not supported by `mason.nvim` but supported by `nvim-lspconfig` here. + if vim.fn.executable("dart") == 1 then + local ok, _opts = pcall(require, "user.configs.lsp-servers.dartls") + if not ok then + _opts = require("completion.servers.dartls") + end + local final_opts = vim.tbl_deep_extend("keep", _opts, opts) + nvim_lsp.dartls.setup(final_opts) + end + + pcall(require, "user.configs.lsp") + + pcall(vim.cmd.LspStart) -- Start LSPs +end diff --git a/nvim/lua/modules/configs/completion/lspsaga.lua b/nvim/lua/modules/configs/completion/lspsaga.lua new file mode 100644 index 0000000..162cdda --- /dev/null +++ b/nvim/lua/modules/configs/completion/lspsaga.lua @@ -0,0 +1,183 @@ +return function() + require("modules.utils").gen_lspkind_hl() + + local icons = { + cmp = require("modules.utils.icons").get("cmp", true), + diagnostics = require("modules.utils.icons").get("diagnostics", true), + kind = require("modules.utils.icons").get("kind", true), + type = require("modules.utils.icons").get("type", true), + ui = require("modules.utils.icons").get("ui", true), + } + + local function set_sidebar_icons() + -- Set icons for sidebar + vim.diagnostic.config({ + signs = { + text = { + [vim.diagnostic.severity.ERROR] = icons.diagnostics.Error_alt, + [vim.diagnostic.severity.WARN] = icons.diagnostics.Warning_alt, + [vim.diagnostic.severity.INFO] = icons.diagnostics.Information_alt, + [vim.diagnostic.severity.HINT] = icons.diagnostics.Hint_alt, + }, + }, + }) + end + + set_sidebar_icons() + + require("modules.utils").load_plugin("lspsaga", { + -- Breadcrumbs: https://nvimdev.github.io/lspsaga/breadcrumbs/ + symbol_in_winbar = { + enable = false, + separator = " " .. icons.ui.Separator, + hide_keyword = false, + show_file = false, + folder_level = 1, + color_mode = true, + delay = 100, + }, + -- Callhierarchy: https://nvimdev.github.io/lspsaga/callhierarchy/ + callhierarchy = { + layout = "float", + keys = { + edit = "e", + vsplit = "v", + split = "s", + tabe = "t", + quit = "q", + shuttle = "[]", + toggle_or_req = "u", + close = "", + }, + }, + -- Code Action: https://nvimdev.github.io/lspsaga/codeaction/ + code_action = { + num_shortcut = true, + only_in_cursor = false, + show_server_name = true, + extend_gitsigns = false, + keys = { + quit = "q", + exec = "", + }, + }, + -- Diagnostics: https://nvimdev.github.io/lspsaga/diagnostic/ + diagnostic = { + show_code_action = true, + jump_num_shortcut = true, + max_width = 0.5, + max_height = 0.6, + text_hl_follow = true, + border_follow = true, + extend_relatedInformation = true, + show_layout = "float", + show_normal_height = 10, + max_show_width = 0.9, + max_show_height = 0.6, + diagnostic_only_current = false, + keys = { + exec_action = "r", + quit = "q", + toggle_or_jump = "", + quit_in_show = { "q", "" }, + }, + }, + -- Hover: https://nvimdev.github.io/lspsaga/hover/ + hover = { + max_width = 0.45, + max_height = 0.7, + open_link = "gl", + open_cmd = "silent !" .. require("core.settings").external_browser, + }, + -- Impl: https://nvimdev.github.io/lspsaga/implement/ + implement = { + enable = true, + sign = true, + virtual_text = false, + priority = 100, + }, + -- LightBulb: https://nvimdev.github.io/lspsaga/lightbulb/ + lightbulb = { + enable = false, + sign = true, + virtual_text = false, + debounce = 10, + sign_priority = 20, + }, + -- Rename: https://nvimdev.github.io/lspsaga/rename/ + rename = { + in_select = false, + auto_save = false, + project_max_width = 0.5, + project_max_height = 0.5, + keys = { + quit = "", + exec = "", + select = "x", + }, + }, + -- Beacon: https://nvimdev.github.io/lspsaga/misc/#beacon + beacon = { + enable = true, + frequency = 12, + }, + -- Generic UI Options: https://nvimdev.github.io/lspsaga/misc/#generic-ui-options + ui = { + border = "single", -- Can be single, double, rounded, solid, shadow. + devicon = true, + title = true, + expand = icons.ui.ArrowClosed, + collapse = icons.ui.ArrowOpen, + code_action = icons.ui.CodeAction, + actionfix = icons.ui.Spell, + lines = { "┗", "┣", "┃", "━", "┏" }, + imp_sign = icons.kind.Implementation, + kind = { + -- Kind + Class = { icons.kind.Class, "LspKindClass" }, + Constant = { icons.kind.Constant, "LspKindConstant" }, + Constructor = { icons.kind.Constructor, "LspKindConstructor" }, + Enum = { icons.kind.Enum, "LspKindEnum" }, + EnumMember = { icons.kind.EnumMember, "LspKindEnumMember" }, + Event = { icons.kind.Event, "LspKindEvent" }, + Field = { icons.kind.Field, "LspKindField" }, + File = { icons.kind.File, "LspKindFile" }, + Function = { icons.kind.Function, "LspKindFunction" }, + Interface = { icons.kind.Interface, "LspKindInterface" }, + Key = { icons.kind.Keyword, "LspKindKey" }, + Method = { icons.kind.Method, "LspKindMethod" }, + Module = { icons.kind.Module, "LspKindModule" }, + Namespace = { icons.kind.Namespace, "LspKindNamespace" }, + Operator = { icons.kind.Operator, "LspKindOperator" }, + Package = { icons.kind.Package, "LspKindPackage" }, + Property = { icons.kind.Property, "LspKindProperty" }, + Struct = { icons.kind.Struct, "LspKindStruct" }, + TypeParameter = { icons.kind.TypeParameter, "LspKindTypeParameter" }, + Variable = { icons.kind.Variable, "LspKindVariable" }, + -- Type + Array = { icons.type.Array, "LspKindArray" }, + Boolean = { icons.type.Boolean, "LspKindBoolean" }, + Null = { icons.type.Null, "LspKindNull" }, + Number = { icons.type.Number, "LspKindNumber" }, + Object = { icons.type.Object, "LspKindObject" }, + String = { icons.type.String, "LspKindString" }, + -- ccls-specific icons. + TypeAlias = { icons.kind.TypeAlias, "LspKindTypeAlias" }, + Parameter = { icons.kind.Parameter, "LspKindParameter" }, + StaticMethod = { icons.kind.StaticMethod, "LspKindStaticMethod" }, + -- Microsoft-specific icons. + Text = { icons.kind.Text, "LspKindText" }, + Snippet = { icons.kind.Snippet, "LspKindSnippet" }, + Folder = { icons.kind.Folder, "LspKindFolder" }, + Unit = { icons.kind.Unit, "LspKindUnit" }, + Value = { icons.kind.Value, "LspKindValue" }, + }, + }, + -- Scrolling Keymaps: https://nvimdev.github.io/lspsaga/misc/#scrolling-keymaps + scroll_preview = { + scroll_down = "", + scroll_up = "", + }, + request_timeout = 3000, + }) +end diff --git a/nvim/lua/modules/configs/completion/luasnip.lua b/nvim/lua/modules/configs/completion/luasnip.lua new file mode 100644 index 0000000..d4ecd2b --- /dev/null +++ b/nvim/lua/modules/configs/completion/luasnip.lua @@ -0,0 +1,15 @@ +return function() + local snippet_path = vim.fn.stdpath("config") .. "/snips/" + if not vim.tbl_contains(vim.opt.rtp:get(), snippet_path) then + vim.opt.rtp:append(snippet_path) + end + + require("modules.utils").load_plugin("luasnip", { + history = true, + update_events = "TextChanged,TextChangedI", + delete_check_events = "TextChanged,InsertLeave", + }, false, require("luasnip").config.set_config) + require("luasnip.loaders.from_lua").lazy_load() + require("luasnip.loaders.from_vscode").lazy_load() + require("luasnip.loaders.from_snipmate").lazy_load() +end diff --git a/nvim/lua/modules/configs/completion/mason-lspconfig.lua b/nvim/lua/modules/configs/completion/mason-lspconfig.lua new file mode 100644 index 0000000..567ff9f --- /dev/null +++ b/nvim/lua/modules/configs/completion/mason-lspconfig.lua @@ -0,0 +1,86 @@ +local M = {} + +M.setup = function() + local diagnostics_virtual_text = require("core.settings").diagnostics_virtual_text + local diagnostics_level = require("core.settings").diagnostics_level + + local nvim_lsp = require("lspconfig") + local mason_lspconfig = require("mason-lspconfig") + require("lspconfig.ui.windows").default_options.border = "rounded" + + require("modules.utils").load_plugin("mason-lspconfig", { + ensure_installed = require("core.settings").lsp_deps, + }) + + vim.lsp.handlers["textDocument/publishDiagnostics"] = vim.lsp.with(vim.lsp.diagnostic.on_publish_diagnostics, { + signs = true, + underline = true, + virtual_text = diagnostics_virtual_text and { + severity = { + min = vim.diagnostic.severity[diagnostics_level], + }, + } or false, + -- set update_in_insert to false because it was enabled by lspsaga + update_in_insert = false, + }) + + local opts = { + capabilities = vim.tbl_deep_extend( + "force", + vim.lsp.protocol.make_client_capabilities(), + require("cmp_nvim_lsp").default_capabilities() + ), + } + ---A handler to setup all servers defined under `completion/servers/*.lua` + ---@param lsp_name string + local function mason_lsp_handler(lsp_name) + -- rust_analyzer is configured using mrcjkb/rustaceanvim + -- warn users if they have set it up manually + if lsp_name == "rust_analyzer" then + local config_exist = pcall(require, "completion.servers." .. lsp_name) + if config_exist then + vim.notify( + [[ +`rust_analyzer` is configured independently via `mrcjkb/rustaceanvim`. To get rid of this warning, +please REMOVE your LSP configuration (rust_analyzer.lua) from the `servers` directory and configure +`rust_analyzer` using the appropriate init options provided by `rustaceanvim` instead.]], + vim.log.levels.WARN, + { title = "nvim-lspconfig" } + ) + end + return + end + + local ok, custom_handler = pcall(require, "user.configs.lsp-servers." .. lsp_name) + -- Use preset if there is no user definition + if not ok then + ok, custom_handler = pcall(require, "completion.servers." .. lsp_name) + end + if not ok then + -- Default to use factory config for server(s) that doesn't include a spec + nvim_lsp[lsp_name].setup(opts) + return + elseif type(custom_handler) == "function" then + --- Case where language server requires its own setup + --- Make sure to call require("lspconfig")[lsp_name].setup() in the function + --- See `clangd.lua` for example. + custom_handler(opts) + elseif type(custom_handler) == "table" then + nvim_lsp[lsp_name].setup(vim.tbl_deep_extend("force", opts, custom_handler)) + else + vim.notify( + string.format( + "Failed to setup [%s].\n\nServer definition under `completion/servers` must return\neither a fun(opts) or a table (got '%s' instead)", + lsp_name, + type(custom_handler) + ), + vim.log.levels.ERROR, + { title = "nvim-lspconfig" } + ) + end + end + + mason_lspconfig.setup_handlers({ mason_lsp_handler }) +end + +return M diff --git a/nvim/lua/modules/configs/completion/mason-null-ls.lua b/nvim/lua/modules/configs/completion/mason-null-ls.lua new file mode 100644 index 0000000..d077fec --- /dev/null +++ b/nvim/lua/modules/configs/completion/mason-null-ls.lua @@ -0,0 +1,12 @@ +local M = {} + +M.setup = function() + require("modules.utils").load_plugin("mason-null-ls", { + ensure_installed = require("core.settings").null_ls_deps, + automatic_installation = false, + automatic_setup = true, + handlers = {}, + }) +end + +return M diff --git a/nvim/lua/modules/configs/completion/mason.lua b/nvim/lua/modules/configs/completion/mason.lua new file mode 100644 index 0000000..4773532 --- /dev/null +++ b/nvim/lua/modules/configs/completion/mason.lua @@ -0,0 +1,94 @@ +local M = {} + +M.setup = function() + local is_windows = require("core.global").is_windows + + local mason_registry = require("mason-registry") + require("lspconfig.ui.windows").default_options.border = "rounded" + + local icons = { + ui = require("modules.utils.icons").get("ui", true), + misc = require("modules.utils.icons").get("misc", true), + } + + require("modules.utils").load_plugin("mason", { + ui = { + border = "single", + icons = { + package_pending = icons.ui.Modified_alt, + package_installed = icons.ui.Check, + package_uninstalled = icons.misc.Ghost, + }, + keymaps = { + toggle_server_expand = "", + install_server = "i", + update_server = "u", + check_server_version = "c", + update_all_servers = "U", + check_outdated_servers = "C", + uninstall_server = "X", + cancel_installation = "", + }, + }, + }) + + -- Additional plugins for pylsp + mason_registry:on( + "package:install:success", + vim.schedule_wrap(function(pkg) + if pkg.name ~= "python-lsp-server" then + return + end + + local venv = vim.fn.stdpath("data") .. "/mason/packages/python-lsp-server/venv" + local python = is_windows and venv .. "/Scripts/python.exe" or venv .. "/bin/python" + local black = is_windows and venv .. "/Scripts/black.exe" or venv .. "/bin/black" + local ruff = is_windows and venv .. "/Scripts/ruff.exe" or venv .. "/bin/ruff" + + require("plenary.job") + :new({ + command = python, + args = { + "-m", + "pip", + "install", + "-U", + "--disable-pip-version-check", + "python-lsp-black", + "python-lsp-ruff", + "pylsp-rope", + }, + cwd = venv, + env = { VIRTUAL_ENV = venv }, + on_exit = function() + if vim.fn.executable(black) == 1 and vim.fn.executable(ruff) == 1 then + vim.notify( + "Finished installing pylsp plugins", + vim.log.levels.INFO, + { title = "[lsp] Install Status" } + ) + else + vim.notify( + "Failed to install pylsp plugins. [Executable not found]", + vim.log.levels.ERROR, + { title = "[lsp] Install Failure" } + ) + end + end, + on_start = function() + vim.notify( + "Now installing pylsp plugins...", + vim.log.levels.INFO, + { title = "[lsp] Install Status", timeout = 6000 } + ) + end, + on_stderr = function(_, msg_stream) + vim.notify(msg_stream, vim.log.levels.ERROR, { title = "[lsp] Install Failure" }) + end, + }) + :start() + end) + ) +end + +return M diff --git a/nvim/lua/modules/configs/completion/neoconf.lua b/nvim/lua/modules/configs/completion/neoconf.lua new file mode 100644 index 0000000..4db9cc7 --- /dev/null +++ b/nvim/lua/modules/configs/completion/neoconf.lua @@ -0,0 +1,20 @@ +local M = {} + +M.setup = function() + require("modules.utils").load_plugin("neoconf", { + -- send new configuration to lsp clients when changing json settings + live_reload = true, + -- name of the local settings files + local_settings = ".neoconf.json", + -- name of the global settings file in your Neovim config directory + global_settings = "neoconf.json", + -- import existing settings from other plugins + import = { + vscode = true, -- local .vscode/settings.json + coc = true, -- global/local coc-settings.json + nlsp = true, -- global/local nlsp-settings.nvim json settings + }, + }) +end + +return M diff --git a/nvim/lua/modules/configs/completion/null-ls.lua b/nvim/lua/modules/configs/completion/null-ls.lua new file mode 100644 index 0000000..72982d6 --- /dev/null +++ b/nvim/lua/modules/configs/completion/null-ls.lua @@ -0,0 +1,77 @@ +return function() + local null_ls = require("null-ls") + local btns = null_ls.builtins + + ---Return formatter args required by `extra_args` + ---@param formatter_name string + ---@return table|nil + local function formatter_args(formatter_name) + local ok, args = pcall(require, "user.configs.formatters." .. formatter_name) + if not ok then + args = require("completion.formatters." .. formatter_name) + end + return args + end + + -- Please set additional flags for the supported servers here + -- Don't specify any config here if you are using the default one. + local sources = { + btns.formatting.clang_format.with({ + filetypes = { "c", "cpp", "objc", "objcpp", "cs", "java", "cuda", "proto" }, + extra_args = formatter_args("clang_format"), + }), + btns.formatting.prettier.with({ + filetypes = { + "vue", + "typescript", + "javascript", + "typescriptreact", + "javascriptreact", + "yaml", + "html", + "css", + "scss", + "sh", + "markdown", + }, + }), + } + require("modules.utils").load_plugin("null-ls", { + border = "rounded", + debug = false, + log_level = "warn", + update_in_insert = false, + sources = sources, + default_timeout = require("core.settings").format_timeout, + }) + + require("completion.mason-null-ls").setup() + + -- Setup usercmd to register/deregister available source(s) + local function _gen_completion() + local sources_cont = null_ls.get_source({ + filetype = vim.bo.filetype, + }) + local completion_items = {} + for _, server in pairs(sources_cont) do + table.insert(completion_items, server.name) + end + return completion_items + end + vim.api.nvim_create_user_command("NullLsToggle", function(opts) + if vim.tbl_contains(_gen_completion(), opts.args) then + null_ls.toggle({ name = opts.args }) + else + vim.notify( + string.format("[Null-ls] Unable to find any registered source named [%s].", opts.args), + vim.log.levels.ERROR, + { title = "Null-ls Internal Error" } + ) + end + end, { + nargs = 1, + complete = _gen_completion, + }) + + require("completion.formatting").configure_format_on_save() +end diff --git a/nvim/lua/modules/configs/completion/servers/bashls.lua b/nvim/lua/modules/configs/completion/servers/bashls.lua new file mode 100644 index 0000000..d5e470f --- /dev/null +++ b/nvim/lua/modules/configs/completion/servers/bashls.lua @@ -0,0 +1,5 @@ +-- https://github.com/neovim/nvim-lspconfig/blob/master/lua/lspconfig/server_configurations/bashls.lua +return { + cmd = { "bash-language-server", "start" }, + filetypes = { "bash", "sh" }, +} diff --git a/nvim/lua/modules/configs/completion/servers/clangd.lua b/nvim/lua/modules/configs/completion/servers/clangd.lua new file mode 100644 index 0000000..7661177 --- /dev/null +++ b/nvim/lua/modules/configs/completion/servers/clangd.lua @@ -0,0 +1,79 @@ +local function switch_source_header_splitcmd(bufnr, splitcmd) + bufnr = require("lspconfig").util.validate_bufnr(bufnr) + local clangd_client = require("lspconfig").util.get_active_client_by_name(bufnr, "clangd") + local params = { uri = vim.uri_from_bufnr(bufnr) } + if clangd_client then + clangd_client.request("textDocument/switchSourceHeader", params, function(err, result) + if err then + error(tostring(err)) + end + if not result then + vim.notify("Corresponding file can’t be determined", vim.log.levels.ERROR, { title = "LSP Error!" }) + return + end + vim.api.nvim_command(splitcmd .. " " .. vim.uri_to_fname(result)) + end) + else + vim.notify( + "Method textDocument/switchSourceHeader is not supported by any active server on this buffer", + vim.log.levels.ERROR, + { title = "LSP Error!" } + ) + end +end + +local function get_binary_path_list(binaries) + local path_list = {} + for _, binary in ipairs(binaries) do + local path = vim.fn.exepath(binary) + if path ~= "" then + table.insert(path_list, path) + end + end + return table.concat(path_list, ",") +end + +-- https://github.com/neovim/nvim-lspconfig/blob/master/lua/lspconfig/server_configurations/clangd.lua +return function(options) + require("lspconfig").clangd.setup({ + on_attach = options.on_attach, + capabilities = vim.tbl_deep_extend("keep", { offsetEncoding = { "utf-16", "utf-8" } }, options.capabilities), + single_file_support = true, + cmd = { + "clangd", + "-j=12", + "--enable-config", + "--background-index", + "--pch-storage=memory", + -- You MUST set this arg ↓ to your c/cpp compiler location (if not included)! + "--query-driver=" .. get_binary_path_list({ "clang++", "clang", "gcc", "g++" }), + "--clang-tidy", + "--all-scopes-completion", + "--completion-style=detailed", + "--header-insertion-decorators", + "--header-insertion=iwyu", + "--limit-references=3000", + "--limit-results=350", + }, + commands = { + ClangdSwitchSourceHeader = { + function() + switch_source_header_splitcmd(0, "edit") + end, + description = "Open source/header in current buffer", + }, + ClangdSwitchSourceHeaderVSplit = { + function() + switch_source_header_splitcmd(0, "vsplit") + end, + description = "Open source/header in a new vsplit", + }, + ClangdSwitchSourceHeaderSplit = { + function() + switch_source_header_splitcmd(0, "split") + end, + description = "Open source/header in a new split", + }, + }, + }) +end diff --git a/nvim/lua/modules/configs/completion/servers/dartls.lua b/nvim/lua/modules/configs/completion/servers/dartls.lua new file mode 100644 index 0000000..5240aef --- /dev/null +++ b/nvim/lua/modules/configs/completion/servers/dartls.lua @@ -0,0 +1,12 @@ +-- https://github.com/neovim/nvim-lspconfig/blob/master/lua/lspconfig/server_configurations/dartls.lua +return { + cmd = { "dart", "language-server", "--protocol=lsp" }, + filetypes = { "dart" }, + init_options = { + closingLabels = true, + flutterOutline = true, + onlyAnalyzeProjectsWithOpenFiles = true, + outline = true, + suggestFromUnimportedLibraries = true, + }, +} diff --git a/nvim/lua/modules/configs/completion/servers/gopls.lua b/nvim/lua/modules/configs/completion/servers/gopls.lua new file mode 100644 index 0000000..c05d162 --- /dev/null +++ b/nvim/lua/modules/configs/completion/servers/gopls.lua @@ -0,0 +1,50 @@ +-- https://github.com/neovim/nvim-lspconfig/blob/master/lua/lspconfig/server_configurations/gopls.lua +return { + cmd = { "gopls", "-remote.debug=:0", "-remote=auto" }, + filetypes = { "go", "gomod", "gosum", "gotmpl", "gohtmltmpl", "gotexttmpl" }, + flags = { allow_incremental_sync = true, debounce_text_changes = 500 }, + capabilities = { + textDocument = { + completion = { + contextSupport = true, + dynamicRegistration = true, + completionItem = { + commitCharactersSupport = true, + deprecatedSupport = true, + preselectSupport = true, + insertReplaceSupport = true, + labelDetailsSupport = true, + snippetSupport = true, + documentationFormat = { "markdown", "plaintext" }, + resolveSupport = { + properties = { + "documentation", + "details", + "additionalTextEdits", + }, + }, + }, + }, + }, + }, + settings = { + gopls = { + staticcheck = true, + semanticTokens = true, + noSemanticString = true, + usePlaceholders = true, + completeUnimported = true, + symbolMatcher = "Fuzzy", + buildFlags = { "-tags", "integration" }, + codelenses = { + generate = true, + gc_details = true, + test = true, + tidy = true, + vendor = true, + regenerate_cgo = true, + upgrade_dependency = true, + }, + }, + }, +} diff --git a/nvim/lua/modules/configs/completion/servers/html.lua b/nvim/lua/modules/configs/completion/servers/html.lua new file mode 100644 index 0000000..fa8f9bd --- /dev/null +++ b/nvim/lua/modules/configs/completion/servers/html.lua @@ -0,0 +1,12 @@ +-- https://github.com/vscode-langservers/vscode-html-languageserver-bin +return { + cmd = { "html-languageserver", "--stdio" }, + filetypes = { "html" }, + init_options = { + configurationSection = { "html", "css", "javascript" }, + embeddedLanguages = { css = true, javascript = true }, + }, + settings = {}, + single_file_support = true, + flags = { debounce_text_changes = 500 }, +} diff --git a/nvim/lua/modules/configs/completion/servers/jsonls.lua b/nvim/lua/modules/configs/completion/servers/jsonls.lua new file mode 100644 index 0000000..d83198d --- /dev/null +++ b/nvim/lua/modules/configs/completion/servers/jsonls.lua @@ -0,0 +1,55 @@ +-- https://github.com/neovim/nvim-lspconfig/blob/master/lua/lspconfig/server_configurations/jsonls.lua +return { + flags = { debounce_text_changes = 500 }, + settings = { + json = { + -- Schemas https://www.schemastore.org + schemas = { + { + fileMatch = { "package.json" }, + url = "https://json.schemastore.org/package.json", + }, + { + fileMatch = { "tsconfig*.json" }, + url = "https://json.schemastore.org/tsconfig.json", + }, + { + fileMatch = { + ".prettierrc", + ".prettierrc.json", + "prettier.config.json", + }, + url = "https://json.schemastore.org/prettierrc.json", + }, + { + fileMatch = { ".eslintrc", ".eslintrc.json" }, + url = "https://json.schemastore.org/eslintrc.json", + }, + { + fileMatch = { + ".babelrc", + ".babelrc.json", + "babel.config.json", + }, + url = "https://json.schemastore.org/babelrc.json", + }, + { + fileMatch = { "lerna.json" }, + url = "https://json.schemastore.org/lerna.json", + }, + { + fileMatch = { + ".stylelintrc", + ".stylelintrc.json", + "stylelint.config.json", + }, + url = "http://json.schemastore.org/stylelintrc.json", + }, + { + fileMatch = { "/.github/workflows/*" }, + url = "https://json.schemastore.org/github-workflow.json", + }, + }, + }, + }, +} diff --git a/nvim/lua/modules/configs/completion/servers/lua_ls.lua b/nvim/lua/modules/configs/completion/servers/lua_ls.lua new file mode 100644 index 0000000..cd68c9e --- /dev/null +++ b/nvim/lua/modules/configs/completion/servers/lua_ls.lua @@ -0,0 +1,25 @@ +-- https://github.com/neovim/nvim-lspconfig/blob/master/lua/lspconfig/server_configurations/lua_ls.lua +return { + settings = { + Lua = { + runtime = { version = "LuaJIT" }, + diagnostics = { + globals = { "vim" }, + disable = { "different-requires", "undefined-field" }, + }, + workspace = { + library = { + vim.fn.expand("$VIMRUNTIME/lua"), + vim.fn.expand("$VIMRUNTIME/lua/vim/lsp"), + }, + maxPreload = 100000, + preloadFileSize = 10000, + }, + hint = { enable = true, setType = true }, + format = { enable = false }, + telemetry = { enable = false }, + -- Do not override treesitter lua highlighting with lua_ls's highlighting + semantic = { enable = false }, + }, + }, +} diff --git a/nvim/lua/modules/configs/completion/servers/pylsp.lua b/nvim/lua/modules/configs/completion/servers/pylsp.lua new file mode 100644 index 0000000..eb83ccd --- /dev/null +++ b/nvim/lua/modules/configs/completion/servers/pylsp.lua @@ -0,0 +1,46 @@ +-- https://github.com/neovim/nvim-lspconfig/blob/master/lua/lspconfig/server_configurations/pylsp.lua +return { + cmd = { "pylsp" }, + filetypes = { "python" }, + settings = { + pylsp = { + plugins = { + -- Lint + ruff = { + enabled = true, + select = { + -- enable pycodestyle + "E", + -- enable pyflakes + "F", + }, + ignore = { + -- ignore E501 (line too long) + -- "E501", + -- ignore F401 (imported but unused) + -- "F401", + }, + extendSelect = { "I" }, + severities = { + -- Hint, Information, Warning, Error + F401 = "I", + E501 = "I", + }, + }, + flake8 = { enabled = false }, + pyflakes = { enabled = false }, + pycodestyle = { enabled = false }, + mccabe = { enabled = false }, + + -- Code refactor + rope = { enabled = true }, + + -- Formatting + black = { enabled = true }, + pyls_isort = { enabled = false }, + autopep8 = { enabled = false }, + yapf = { enabled = false }, + }, + }, + }, +} diff --git a/nvim/lua/modules/configs/completion/tabnine.lua b/nvim/lua/modules/configs/completion/tabnine.lua new file mode 100644 index 0000000..0509616 --- /dev/null +++ b/nvim/lua/modules/configs/completion/tabnine.lua @@ -0,0 +1,3 @@ +return function() + require("cmp_tabnine.config"):setup({ max_line = 1000, max_num_results = 20, sort = true }) +end diff --git a/nvim/lua/modules/configs/editor/align.lua b/nvim/lua/modules/configs/editor/align.lua new file mode 100644 index 0000000..43c50d8 --- /dev/null +++ b/nvim/lua/modules/configs/editor/align.lua @@ -0,0 +1,11 @@ +return function() + require("modules.utils").load_plugin("mini.align", { + -- Whether to disable showing non-error feedback + silent = false, + -- Module mappings. Use `''` (empty string) to disable one. + mappings = { + start = "gea", + start_with_preview = "geA", + }, + }) +end diff --git a/nvim/lua/modules/configs/editor/autoclose.lua b/nvim/lua/modules/configs/editor/autoclose.lua new file mode 100644 index 0000000..a43888f --- /dev/null +++ b/nvim/lua/modules/configs/editor/autoclose.lua @@ -0,0 +1,39 @@ +return function() + require("modules.utils").load_plugin("autoclose", { + keys = { + ["("] = { escape = false, close = true, pair = "()" }, + ["["] = { escape = false, close = true, pair = "[]" }, + ["{"] = { escape = false, close = true, pair = "{}" }, + + ["<"] = { escape = true, close = true, pair = "<>", enabled_filetypes = { "rust" } }, + [">"] = { escape = true, close = false, pair = "<>" }, + [")"] = { escape = true, close = false, pair = "()" }, + ["]"] = { escape = true, close = false, pair = "[]" }, + ["}"] = { escape = true, close = false, pair = "{}" }, + + ['"'] = { escape = true, close = true, pair = '""' }, + ["`"] = { escape = true, close = true, pair = "``" }, + ["'"] = { escape = true, close = true, pair = "''", disabled_filetypes = { "rust" } }, + }, + options = { + disable_when_touch = false, + disabled_filetypes = { + "alpha", + "bigfile", + "checkhealth", + "dap-repl", + "diff", + "help", + "log", + "notify", + "NvimTree", + "Outline", + "qf", + "TelescopePrompt", + "toggleterm", + "undotree", + "vimwiki", + }, + }, + }) +end diff --git a/nvim/lua/modules/configs/editor/autotag.lua b/nvim/lua/modules/configs/editor/autotag.lua new file mode 100644 index 0000000..42dcd97 --- /dev/null +++ b/nvim/lua/modules/configs/editor/autotag.lua @@ -0,0 +1,9 @@ +return function() + require("modules.utils").load_plugin("nvim-ts-autotag", { + opts = { + enable_close = true, -- Auto close tags + enable_rename = true, -- Auto rename pairs of tags + enable_close_on_slash = false, -- Auto close on trailing ` + current = true, + -- for the current window, label targets closer to the cursor first + distance = true, + }, + modes = { + search = { enabled = false }, + -- options used when flash is activated through + -- `f`, `F`, `t`, `T`, `;` and `,` motions + char = { + enabled = true, + -- hide after jump when not using jump labels + autohide = false, + -- show jump labels + jump_labels = false, + -- set to `false` to use the current line only + multi_line = true, + -- When using jump labels, don't use these keys + -- This allows using those keys directly after the motion + label = { exclude = "hjkliardc" }, + }, + }, + }) +end diff --git a/nvim/lua/modules/configs/editor/highlight-colors.lua b/nvim/lua/modules/configs/editor/highlight-colors.lua new file mode 100644 index 0000000..06a0f6a --- /dev/null +++ b/nvim/lua/modules/configs/editor/highlight-colors.lua @@ -0,0 +1,31 @@ +return function() + require("modules.utils").load_plugin("nvim-highlight-colors", { + render = "background", + enable_hex = true, + enable_short_hex = true, + enable_rgb = true, + enable_hsl = true, + enable_var_usage = true, + enable_named_colors = false, + enable_tailwind = false, + -- Exclude filetypes or buftypes from highlighting + exclude_filetypes = { + "alpha", + "bigfile", + "dap-repl", + "fugitive", + "git", + "notify", + "NvimTree", + "Outline", + "TelescopePrompt", + "toggleterm", + "undotree", + }, + exclude_buftypes = { + "nofile", + "prompt", + "terminal", + }, + }) +end diff --git a/nvim/lua/modules/configs/editor/hop.lua b/nvim/lua/modules/configs/editor/hop.lua new file mode 100644 index 0000000..d52fa5b --- /dev/null +++ b/nvim/lua/modules/configs/editor/hop.lua @@ -0,0 +1,3 @@ +return function() + require("modules.utils").load_plugin("hop", { keys = "etovxqpdygfblzhckisuran" }) +end diff --git a/nvim/lua/modules/configs/editor/local-highlight.lua b/nvim/lua/modules/configs/editor/local-highlight.lua new file mode 100644 index 0000000..f8c001f --- /dev/null +++ b/nvim/lua/modules/configs/editor/local-highlight.lua @@ -0,0 +1,6 @@ +return function() + require("modules.utils").load_plugin("local-highlight", { + hlgroup = "IlluminatedWordText", + insert_mode = false, + }) +end diff --git a/nvim/lua/modules/configs/editor/persisted.lua b/nvim/lua/modules/configs/editor/persisted.lua new file mode 100644 index 0000000..003d8cb --- /dev/null +++ b/nvim/lua/modules/configs/editor/persisted.lua @@ -0,0 +1,23 @@ +return function() + require("modules.utils").load_plugin("persisted", { + save_dir = vim.fn.expand(vim.fn.stdpath("data") .. "/sessions/"), -- directory where session files are saved + silent = false, -- silent nvim message when sourcing session file + use_git_branch = true, -- create session files based on the branch of the git enabled repository + autosave = true, -- automatically save session files when exiting Neovim + should_autosave = function() + if vim.bo.filetype == "alpha" then + return false + end + return true + end, -- function to determine if a session should be autosaved + -- Set `lazy = false` in `plugins/editor.lua` to enable this + autoload = false, -- automatically load the session for the cwd on Neovim startup + on_autoload_no_session = nil, -- function to run when `autoload = true` but there is no session to load + follow_cwd = true, -- change session file name to match current working directory if it changes + allowed_dirs = nil, -- table of dirs that the plugin will auto-save and auto-load from + ignored_dirs = nil, -- table of dirs that are ignored when auto-saving and auto-loading + telescope = { -- options for the telescope extension + reset_prompt_after_deletion = true, -- whether to reset prompt after session deleted + }, + }) +end diff --git a/nvim/lua/modules/configs/editor/rainbow_delims.lua b/nvim/lua/modules/configs/editor/rainbow_delims.lua new file mode 100644 index 0000000..d2e681d --- /dev/null +++ b/nvim/lua/modules/configs/editor/rainbow_delims.lua @@ -0,0 +1,53 @@ +return function() + ---@param threshold number @Use global strategy if nr of lines exceeds this value + local function init_strategy(threshold) + return function() + -- Disable on very large files + local line_count = vim.api.nvim_buf_line_count(0) + if line_count > 7500 then + return nil + end + + -- Disable on parser error + local errors = 200 + vim.treesitter.get_parser():for_each_tree(function(lt) + if lt:root():has_error() and errors >= 0 then + errors = errors - 1 + end + end) + if errors < 0 then + return nil + end + + return line_count > threshold and require("rainbow-delimiters").strategy["global"] + or require("rainbow-delimiters").strategy["local"] + end + end + + vim.g.rainbow_delimiters = { + strategy = { + [""] = init_strategy(500), + c = init_strategy(300), + cpp = init_strategy(300), + lua = init_strategy(500), + vimdoc = init_strategy(300), + vim = init_strategy(300), + }, + query = { + [""] = "rainbow-delimiters", + latex = "rainbow-blocks", + javascript = "rainbow-delimiters-react", + }, + highlight = { + "RainbowDelimiterRed", + "RainbowDelimiterOrange", + "RainbowDelimiterYellow", + "RainbowDelimiterGreen", + "RainbowDelimiterBlue", + "RainbowDelimiterCyan", + "RainbowDelimiterViolet", + }, + } + + require("modules.utils").load_plugin("rainbow_delimiters", nil, true) +end diff --git a/nvim/lua/modules/configs/editor/splits.lua b/nvim/lua/modules/configs/editor/splits.lua new file mode 100644 index 0000000..891d01e --- /dev/null +++ b/nvim/lua/modules/configs/editor/splits.lua @@ -0,0 +1,14 @@ +return function() + require("modules.utils").load_plugin("smart-splits", { + -- Ignored buffer types (only while resizing) + ignored_buftypes = { + "nofile", + "quickfix", + "prompt", + }, + -- Ignored filetypes (only while resizing) + ignored_filetypes = { "NvimTree" }, + -- the default number of lines/columns to resize by at a time + default_amount = 3, + }) +end diff --git a/nvim/lua/modules/configs/editor/suda.lua b/nvim/lua/modules/configs/editor/suda.lua new file mode 100644 index 0000000..d70aa61 --- /dev/null +++ b/nvim/lua/modules/configs/editor/suda.lua @@ -0,0 +1,5 @@ +return function() + vim.g["suda#prompt"] = "Enter administrator password: " + + require("modules.utils").load_plugin("suda", nil, true) +end diff --git a/nvim/lua/modules/configs/editor/treesitter.lua b/nvim/lua/modules/configs/editor/treesitter.lua new file mode 100644 index 0000000..5b25ebe --- /dev/null +++ b/nvim/lua/modules/configs/editor/treesitter.lua @@ -0,0 +1,66 @@ +return vim.schedule_wrap(function() + local use_ssh = require("core.settings").use_ssh + + vim.api.nvim_set_option_value("foldmethod", "expr", {}) + vim.api.nvim_set_option_value("foldexpr", "nvim_treesitter#foldexpr()", {}) + + require("modules.utils").load_plugin("nvim-treesitter", { + ensure_installed = require("core.settings").treesitter_deps, + highlight = { + enable = true, + disable = function(ft, bufnr) + if + vim.tbl_contains({ "gitcommit" }, ft) + or (vim.api.nvim_buf_line_count(bufnr) > 7500 and ft ~= "vimdoc") + then + return true + end + + local ok, is_large_file = pcall(vim.api.nvim_buf_get_var, bufnr, "bigfile_disable_treesitter") + return ok and is_large_file + end, + additional_vim_regex_highlighting = false, + }, + textobjects = { + select = { + enable = true, + lookahead = true, + keymaps = { + ["af"] = "@function.outer", + ["if"] = "@function.inner", + ["ac"] = "@class.outer", + ["ic"] = "@class.inner", + }, + }, + move = { + enable = true, + set_jumps = true, + goto_next_start = { + ["]["] = "@function.outer", + ["]m"] = "@class.outer", + }, + goto_next_end = { + ["]]"] = "@function.outer", + ["]M"] = "@class.outer", + }, + goto_previous_start = { + ["[["] = "@function.outer", + ["[m"] = "@class.outer", + }, + goto_previous_end = { + ["[]"] = "@function.outer", + ["[M"] = "@class.outer", + }, + }, + }, + indent = { enable = true }, + matchup = { enable = true }, + }, false, require("nvim-treesitter.configs").setup) + require("nvim-treesitter.install").prefer_git = true + if use_ssh then + local parsers = require("nvim-treesitter.parsers").get_parser_configs() + for _, parser in pairs(parsers) do + parser.install_info.url = parser.install_info.url:gsub("https://github.com/", "git@github.com:") + end + end +end) diff --git a/nvim/lua/modules/configs/editor/ts-context-commentstring.lua b/nvim/lua/modules/configs/editor/ts-context-commentstring.lua new file mode 100644 index 0000000..ebefadc --- /dev/null +++ b/nvim/lua/modules/configs/editor/ts-context-commentstring.lua @@ -0,0 +1,7 @@ +return function() + vim.g.skip_ts_context_commentstring_module = true + require("modules.utils").load_plugin("ts_context_commentstring", { + -- Whether to update the `commentstring` on the `CursorHold` autocmd + enable_autocmd = false, + }) +end diff --git a/nvim/lua/modules/configs/editor/ts-context.lua b/nvim/lua/modules/configs/editor/ts-context.lua new file mode 100644 index 0000000..ba96e03 --- /dev/null +++ b/nvim/lua/modules/configs/editor/ts-context.lua @@ -0,0 +1,12 @@ +return function() + require("modules.utils").load_plugin("treesitter-context", { + enable = true, + max_lines = 3, -- How many lines the window should span. Values <= 0 mean no limit. + min_window_height = 0, -- Minimum editor window height to enable context. Values <= 0 mean no limit. + line_numbers = true, + multiline_threshold = 20, -- Maximum number of lines to collapse for a single context line + trim_scope = "outer", -- Which context lines to discard if `max_lines` is exceeded. Choices: 'inner', 'outer' + mode = "cursor", -- Line used to calculate context. Choices: 'cursor', 'topline' + zindex = 50, -- Ensure compatibility with Glance's preview window + }) +end diff --git a/nvim/lua/modules/configs/lang/bqf.lua b/nvim/lua/modules/configs/lang/bqf.lua new file mode 100644 index 0000000..9c80bd2 --- /dev/null +++ b/nvim/lua/modules/configs/lang/bqf.lua @@ -0,0 +1,9 @@ +return function() + require("modules.utils").load_plugin("bqf", { + preview = { + border = "single", + wrap = true, + winblend = 0, + }, + }) +end diff --git a/nvim/lua/modules/configs/lang/crates-keymap.lua b/nvim/lua/modules/configs/lang/crates-keymap.lua new file mode 100644 index 0000000..cf1584c --- /dev/null +++ b/nvim/lua/modules/configs/lang/crates-keymap.lua @@ -0,0 +1,126 @@ +local bind = require("keymap.bind") +local map_callback = bind.map_callback + +local crates = require("crates") +local crates_keymap = { + ["n|ct"] = map_callback(function() + crates.toggle() + end) + :with_noremap() + :with_silent() + :with_buffer(0) + :with_desc("crates: Toggle spec activities"), + ["n|cr"] = map_callback(function() + crates.reload() + end) + :with_noremap() + :with_silent() + :with_buffer(0) + :with_desc("crates: Reload crate specs"), + + ["n|cs"] = map_callback(function() + crates.show_popup() + end) + :with_noremap() + :with_silent() + :with_buffer(0) + :with_desc("crates: Toggle pop-up window"), + ["n|cv"] = map_callback(function() + crates.show_versions_popup() + crates.show_popup() + end) + :with_noremap() + :with_silent() + :with_buffer(0) + :with_desc("crates: Select spec versions"), + ["n|cf"] = map_callback(function() + crates.show_features_popup() + crates.show_popup() + end) + :with_noremap() + :with_silent() + :with_buffer(0) + :with_desc("crates: Select spec features"), + ["n|cd"] = map_callback(function() + crates.show_dependencies_popup() + crates.show_popup() + end) + :with_noremap() + :with_silent() + :with_buffer(0) + :with_desc("crates: Show project dependencies"), + + ["n|cu"] = map_callback(function() + crates.update_crate() + end) + :with_noremap() + :with_silent() + :with_buffer(0) + :with_desc("crates: Update current crate's spec"), + ["v|cu"] = map_callback(function() + crates.update_crates() + end) + :with_noremap() + :with_silent() + :with_buffer(0) + :with_desc("crates: Update selected crate's spec"), + ["n|ca"] = map_callback(function() + crates.update_all_crates() + end) + :with_noremap() + :with_silent() + :with_buffer(0) + :with_desc("crates: Update all crates' specs"), + ["n|cU"] = map_callback(function() + crates.upgrade_crate() + end) + :with_noremap() + :with_silent() + :with_buffer(0) + :with_desc("crates: Upgrade current crate"), + ["v|cU"] = map_callback(function() + crates.upgrade_crates() + end) + :with_noremap() + :with_silent() + :with_buffer(0) + :with_desc("crates: Upgrade selected crates"), + ["n|cA"] = map_callback(function() + crates.upgrade_all_crates() + end) + :with_noremap() + :with_silent() + :with_buffer(0) + :with_desc("crates: Upgrade all crates"), + + ["n|cH"] = map_callback(function() + crates.open_homepage() + end) + :with_noremap() + :with_silent() + :with_buffer(0) + :with_desc("crates: Open current crate's homepage"), + ["n|cR"] = map_callback(function() + crates.open_repository() + end) + :with_noremap() + :with_silent() + :with_buffer(0) + :with_desc("crates: Open current crate's repository"), + ["n|cD"] = map_callback(function() + crates.open_documentation() + end) + :with_noremap() + :with_silent() + :with_buffer(0) + :with_desc("crates: Open current crate's documentation"), + ["n|cC"] = map_callback(function() + crates.open_crates_io() + end) + :with_noremap() + :with_silent() + :with_buffer(0) + :with_desc("crates: Browse current crate on crates.io"), +} + +bind.nvim_load_mapping(crates_keymap) diff --git a/nvim/lua/modules/configs/lang/crates.lua b/nvim/lua/modules/configs/lang/crates.lua new file mode 100644 index 0000000..ee1d880 --- /dev/null +++ b/nvim/lua/modules/configs/lang/crates.lua @@ -0,0 +1,89 @@ +return function() + local icons = { + diagnostics = require("modules.utils.icons").get("diagnostics", true), + git = require("modules.utils.icons").get("git", true), + misc = require("modules.utils.icons").get("misc", true), + ui = require("modules.utils.icons").get("ui", true), + kind = require("modules.utils.icons").get("kind", true), + } + + require("modules.utils").load_plugin("crates", { + smart_insert = true, + insert_closing_quote = true, + avoid_prerelease = true, + autoload = true, + autoupdate = true, + autoupdate_throttle = 250, + loading_indicator = true, + date_format = "%Y-%m-%d", + thousands_separator = ",", + notification_title = "Crates", + curl_args = { "-sL", "--retry", "1" }, + disable_invalid_feature_diagnostic = false, + text = { + loading = " " .. icons.misc.Watch .. "Loading", + version = " " .. icons.ui.Check .. "%s", + prerelease = " " .. icons.diagnostics.Warning_alt .. "%s", + yanked = " " .. icons.diagnostics.Error .. "%s", + nomatch = " " .. icons.diagnostics.Question .. "No match", + upgrade = " " .. icons.diagnostics.Hint_alt .. "%s", + error = " " .. icons.diagnostics.Error .. "Error fetching crate", + }, + popup = { + autofocus = false, + hide_on_select = true, + copy_register = '"', + style = "minimal", + border = "rounded", + show_version_date = true, + show_dependency_version = true, + max_height = 30, + min_width = 20, + padding = 1, + text = { + title = icons.ui.Package .. "%s", + description = "%s", + created_label = icons.misc.Added .. "created" .. " ", + created = "%s", + updated_label = icons.misc.ManUp .. "updated" .. " ", + updated = "%s", + downloads_label = icons.ui.CloudDownload .. "downloads ", + downloads = "%s", + homepage_label = icons.misc.Campass .. "homepage ", + homepage = "%s", + repository_label = icons.git.Repo .. "repository ", + repository = "%s", + documentation_label = icons.diagnostics.Information_alt .. "documentation ", + documentation = "%s", + crates_io_label = icons.ui.Package .. "crates.io ", + crates_io = "%s", + categories_label = icons.kind.Class .. "categories ", + keywords_label = icons.kind.Keyword .. "keywords ", + version = " %s", + prerelease = icons.diagnostics.Warning_alt .. "%s prerelease", + yanked = icons.diagnostics.Error .. "%s yanked", + version_date = " %s", + feature = " %s", + enabled = icons.ui.Play .. "%s", + transitive = icons.ui.List .. "%s", + normal_dependencies_title = icons.kind.Interface .. "Dependencies", + build_dependencies_title = icons.misc.Gavel .. "Build dependencies", + dev_dependencies_title = icons.misc.Glass .. "Dev dependencies", + dependency = " %s", + optional = icons.ui.BigUnfilledCircle .. "%s", + dependency_version = " %s", + loading = " " .. icons.misc.Watch, + }, + }, + completion = { + insert_closing_quote = true, + text = { + prerelease = " " .. icons.diagnostics.Warning_alt .. "pre-release ", + yanked = " " .. icons.diagnostics.Error_alt .. "yanked ", + }, + }, + }) + + -- Set buffer-local keymaps + require("lang.crates-keymap") +end diff --git a/nvim/lua/modules/configs/lang/go.lua b/nvim/lua/modules/configs/lang/go.lua new file mode 100644 index 0000000..8ca7b03 --- /dev/null +++ b/nvim/lua/modules/configs/lang/go.lua @@ -0,0 +1,21 @@ +return function() + require("modules.utils").load_plugin("go", { + -- By default, we've turned off these options to prevent clashes with our gopls config + icons = false, + diagnostic = false, + lsp_cfg = false, + lsp_gofumpt = false, + lsp_keymaps = false, + lsp_codelens = false, + lsp_document_formatting = false, + lsp_inlay_hints = { enable = false }, + -- DAP-related settings are also turned off here for the same reason + dap_debug = false, + dap_debug_keymap = false, + textobjects = false, + -- Miscellaneous options to seamlessly integrate with other plugins + trouble = true, + luasnip = false, + run_in_floaterm = false, + }) +end diff --git a/nvim/lua/modules/configs/lang/rust.lua b/nvim/lua/modules/configs/lang/rust.lua new file mode 100644 index 0000000..cdcc1c7 --- /dev/null +++ b/nvim/lua/modules/configs/lang/rust.lua @@ -0,0 +1,12 @@ +return function() + vim.g.rustaceanvim = { + -- Disable automatic DAP configuration to avoid conflicts with previous user configs + dap = { + adapter = false, + configuration = false, + autoload_configurations = false, + }, + } + + require("modules.utils").load_plugin("rustaceanvim", nil, true) +end diff --git a/nvim/lua/modules/configs/tool/dap/clients/codelldb.lua b/nvim/lua/modules/configs/tool/dap/clients/codelldb.lua new file mode 100644 index 0000000..64124c6 --- /dev/null +++ b/nvim/lua/modules/configs/tool/dap/clients/codelldb.lua @@ -0,0 +1,47 @@ +-- https://github.com/mfussenegger/nvim-dap/wiki/C-C---Rust-(via--codelldb) +return function() + local dap = require("dap") + local utils = require("modules.utils.dap") + local is_windows = require("core.global").is_windows + + dap.adapters.codelldb = { + type = "server", + port = "${port}", + executable = { + command = vim.fn.exepath("codelldb"), -- Find codelldb on $PATH + args = { "--port", "${port}" }, + detached = is_windows and false or true, + }, + } + dap.configurations.c = { + { + name = "Debug", + type = "codelldb", + request = "launch", + program = utils.input_exec_path(), + cwd = "${workspaceFolder}", + stopOnEntry = false, + terminal = "integrated", + }, + { + name = "Debug (with args)", + type = "codelldb", + request = "launch", + program = utils.input_exec_path(), + args = utils.input_args(), + cwd = "${workspaceFolder}", + stopOnEntry = false, + terminal = "integrated", + }, + { + name = "Attach to a running process", + type = "codelldb", + request = "attach", + program = utils.input_exec_path(), + stopOnEntry = false, + waitFor = true, + }, + } + dap.configurations.cpp = dap.configurations.c + dap.configurations.rust = dap.configurations.c +end diff --git a/nvim/lua/modules/configs/tool/dap/clients/delve.lua b/nvim/lua/modules/configs/tool/dap/clients/delve.lua new file mode 100644 index 0000000..6f02344 --- /dev/null +++ b/nvim/lua/modules/configs/tool/dap/clients/delve.lua @@ -0,0 +1,100 @@ +-- https://github.com/mfussenegger/nvim-dap/wiki/Debug-Adapter-installation#go +-- https://github.com/golang/vscode-go/blob/master/docs/debugging.md +return function() + local dap = require("dap") + local utils = require("modules.utils.dap") + + if not require("mason-registry").is_installed("go-debug-adapter") then + vim.notify( + "Automatically installing `go-debug-adapter` for go debugging", + vim.log.levels.INFO, + { title = "nvim-dap" } + ) + + local go_dbg = require("mason-registry").get_package("go-debug-adapter") + go_dbg:install():once( + "closed", + vim.schedule_wrap(function() + if go_dbg:is_installed() then + vim.notify("Successfully installed `go-debug-adapter`", vim.log.levels.INFO, { title = "nvim-dap" }) + end + end) + ) + end + + dap.adapters.go = { + type = "executable", + command = "node", + args = { + require("mason-registry").get_package("go-debug-adapter"):get_install_path() + .. "/extension/dist/debugAdapter.js", + }, + } + dap.configurations.go = { + { + type = "go", + name = "Debug (file)", + request = "launch", + cwd = "${workspaceFolder}", + program = utils.input_file_path(), + console = "integratedTerminal", + dlvToolPath = vim.fn.exepath("dlv"), + showLog = true, + showRegisters = true, + stopOnEntry = false, + }, + { + type = "go", + name = "Debug (file with args)", + request = "launch", + cwd = "${workspaceFolder}", + program = utils.input_file_path(), + args = utils.input_args(), + console = "integratedTerminal", + dlvToolPath = vim.fn.exepath("dlv"), + showLog = true, + showRegisters = true, + stopOnEntry = false, + }, + { + type = "go", + name = "Debug (executable)", + request = "launch", + cwd = "${workspaceFolder}", + program = utils.input_exec_path(), + args = utils.input_args(), + console = "integratedTerminal", + dlvToolPath = vim.fn.exepath("dlv"), + mode = "exec", + showLog = true, + showRegisters = true, + stopOnEntry = false, + }, + { + type = "go", + name = "Debug (test file)", + request = "launch", + cwd = "${workspaceFolder}", + program = utils.input_file_path(), + console = "integratedTerminal", + dlvToolPath = vim.fn.exepath("dlv"), + mode = "test", + showLog = true, + showRegisters = true, + stopOnEntry = false, + }, + { + type = "go", + name = "Debug (using go.mod)", + request = "launch", + cwd = "${workspaceFolder}", + program = "./${relativeFileDirname}", + console = "integratedTerminal", + dlvToolPath = vim.fn.exepath("dlv"), + mode = "test", + showLog = true, + showRegisters = true, + stopOnEntry = false, + }, + } +end diff --git a/nvim/lua/modules/configs/tool/dap/clients/lldb.lua b/nvim/lua/modules/configs/tool/dap/clients/lldb.lua new file mode 100644 index 0000000..8461c77 --- /dev/null +++ b/nvim/lua/modules/configs/tool/dap/clients/lldb.lua @@ -0,0 +1,36 @@ +-- https://github.com/mfussenegger/nvim-dap/wiki/Debug-Adapter-installation#ccrust-via-lldb-vscode +return function() + local dap = require("dap") + local utils = require("modules.utils.dap") + + dap.adapters.lldb = { + type = "executable", + command = vim.fn.exepath("lldb-vscode"), -- Find lldb-vscode on $PATH + } + dap.configurations.c = { + { + name = "Launch", + type = "lldb", + request = "launch", + program = utils.input_exec_path(), + cwd = "${workspaceFolder}", + args = utils.input_args(), + env = utils.get_env(), + + -- if you change `runInTerminal` to true, you might need to change the yama/ptrace_scope setting: + -- + -- echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope + -- + -- Otherwise you might get the following error: + -- + -- Error on launch: Failed to attach to the target process + -- + -- But you should be aware of the implications: + -- https://www.kernel.org/doc/html/latest/admin-guide/LSM/Yama.html + runInTerminal = false, + }, + } + + dap.configurations.cpp = dap.configurations.c + dap.configurations.rust = dap.configurations.c +end diff --git a/nvim/lua/modules/configs/tool/dap/clients/python.lua b/nvim/lua/modules/configs/tool/dap/clients/python.lua new file mode 100644 index 0000000..3ff35e5 --- /dev/null +++ b/nvim/lua/modules/configs/tool/dap/clients/python.lua @@ -0,0 +1,74 @@ +-- https://github.com/mfussenegger/nvim-dap/wiki/Debug-Adapter-installation#python +-- https://github.com/microsoft/debugpy/wiki/Debug-configuration-settings +return function() + local dap = require("dap") + local utils = require("modules.utils.dap") + local is_windows = require("core.global").is_windows + local debugpy_root = require("mason-registry").get_package("debugpy"):get_install_path() + + dap.adapters.python = function(callback, config) + if config.request == "attach" then + local port = (config.connect or config).port + local host = (config.connect or config).host or "127.0.0.1" + callback({ + type = "server", + port = assert(port, "`connect.port` is required for a python `attach` configuration"), + host = host, + options = { source_filetype = "python" }, + }) + else + callback({ + type = "executable", + command = is_windows and debugpy_root .. "/venv/Scripts/pythonw.exe" + or debugpy_root .. "/venv/bin/python", + args = { "-m", "debugpy.adapter" }, + options = { source_filetype = "python" }, + }) + end + end + dap.configurations.python = { + { + -- The first three options are required by nvim-dap + type = "python", -- the type here established the link to the adapter definition: `dap.adapters.python` + request = "launch", + name = "Debug", + -- Options below are for debugpy, see https://github.com/microsoft/debugpy/wiki/Debug-configuration-settings for supported options + console = "integratedTerminal", + program = utils.input_file_path(), + pythonPath = function() + local venv = vim.env.CONDA_PREFIX + if venv then + return is_windows and venv .. "/Scripts/pythonw.exe" or venv .. "/bin/python" + else + return is_windows and "pythonw.exe" or "python3" + end + end, + }, + { + -- NOTE: This setting is for people using venv + type = "python", + request = "launch", + name = "Debug (using venv)", + -- Options below are for debugpy, see https://github.com/microsoft/debugpy/wiki/Debug-configuration-settings for supported options + console = "integratedTerminal", + program = utils.input_file_path(), + pythonPath = function() + -- Prefer the venv that is defined by the designated environment variable. + local cwd, venv = vim.fn.getcwd(), os.getenv("VIRTUAL_ENV") + local python = venv and (is_windows and venv .. "/Scripts/pythonw.exe" or venv .. "/bin/python") or "" + if vim.fn.executable(python) == 1 then + return python + end + + -- Otherwise, fall back to check if there are any local venvs available. + venv = vim.fn.isdirectory(cwd .. "/venv") == 1 and cwd .. "/venv" or cwd .. "/.venv" + python = is_windows and venv .. "/Scripts/pythonw.exe" or venv .. "/bin/python" + if vim.fn.executable(python) == 1 then + return python + else + return is_windows and "pythonw.exe" or "python3" + end + end, + }, + } +end diff --git a/nvim/lua/modules/configs/tool/dap/dap-keymap.lua b/nvim/lua/modules/configs/tool/dap/dap-keymap.lua new file mode 100644 index 0000000..bdaf08d --- /dev/null +++ b/nvim/lua/modules/configs/tool/dap/dap-keymap.lua @@ -0,0 +1,21 @@ +local M = {} + +local bind = require("keymap.bind") +local map_cmd = bind.map_cmd + +local did_load_debug_mappings = false +local debug_keymap = { + ["nv|K"] = map_cmd("lua require('dapui').eval()") + :with_noremap() + :with_nowait() + :with_desc("Evaluate expression under cursor"), +} + +function M.load_extras() + if not did_load_debug_mappings then + require("modules.utils.keymap").amend("Debugging", "_debugging", debug_keymap) + did_load_debug_mappings = true + end +end + +return M diff --git a/nvim/lua/modules/configs/tool/dap/dapui.lua b/nvim/lua/modules/configs/tool/dap/dapui.lua new file mode 100644 index 0000000..ab14dbc --- /dev/null +++ b/nvim/lua/modules/configs/tool/dap/dapui.lua @@ -0,0 +1,72 @@ +return function() + local icons = { + ui = require("modules.utils.icons").get("ui"), + dap = require("modules.utils.icons").get("dap"), + } + + require("modules.utils").load_plugin("dapui", { + force_buffers = true, + icons = { + expanded = icons.ui.ArrowOpen, + collapsed = icons.ui.ArrowClosed, + current_frame = icons.ui.Indicator, + }, + mappings = { + -- Use a table to apply multiple mappings + edit = "e", + expand = { "", "<2-LeftMouse>" }, + open = "o", + remove = "d", + repl = "r", + toggle = "t", + }, + layouts = { + { + elements = { + -- Provide as ID strings or tables with "id" and "size" keys + { + id = "scopes", + size = 0.3, -- Can be float or integer > 1 + }, + { id = "watches", size = 0.3 }, + { id = "stacks", size = 0.3 }, + { id = "breakpoints", size = 0.1 }, + }, + size = 0.3, + position = "right", + }, + { + elements = { + { id = "console", size = 0.55 }, + { id = "repl", size = 0.45 }, + }, + position = "bottom", + size = 0.25, + }, + }, + controls = { + enabled = true, + -- Display controls in this session + element = "repl", + icons = { + pause = icons.dap.Pause, + play = icons.dap.Play, + step_into = icons.dap.StepInto, + step_over = icons.dap.StepOver, + step_out = icons.dap.StepOut, + step_back = icons.dap.StepBack, + run_last = icons.dap.RunLast, + terminate = icons.dap.Terminate, + }, + }, + floating = { + max_height = nil, -- These can be integers or a float between 0 and 1. + max_width = nil, -- Floats will be treated as percentage of your screen. + border = "single", -- Border style. Can be "single", "double" or "rounded" + mappings = { + close = { "q", "" }, + }, + }, + render = { indent = 1, max_value_lines = 85 }, + }) +end diff --git a/nvim/lua/modules/configs/tool/dap/init.lua b/nvim/lua/modules/configs/tool/dap/init.lua new file mode 100644 index 0000000..f2fa42a --- /dev/null +++ b/nvim/lua/modules/configs/tool/dap/init.lua @@ -0,0 +1,84 @@ +return function() + local dap = require("dap") + local dapui = require("dapui") + local mason_dap = require("mason-nvim-dap") + + local icons = { dap = require("modules.utils.icons").get("dap") } + local colors = require("modules.utils").get_palette() + local mappings = require("tool.dap.dap-keymap") + + -- Initialize debug hooks + _G._debugging = false + local function debug_init_cb() + _G._debugging = true + mappings.load_extras() + dapui.open({ reset = true }) + end + local function debug_terminate_cb() + if _debugging then + _G._debugging = false + dapui.close() + end + end + dap.listeners.after.event_initialized["dapui_config"] = debug_init_cb + dap.listeners.before.event_terminated["dapui_config"] = debug_terminate_cb + dap.listeners.before.event_exited["dapui_config"] = debug_terminate_cb + dap.listeners.before.disconnect["dapui_config"] = debug_terminate_cb + + -- We need to override nvim-dap's default highlight groups, AFTER requiring nvim-dap for catppuccin. + vim.api.nvim_set_hl(0, "DapStopped", { fg = colors.green }) + + vim.fn.sign_define( + "DapBreakpoint", + { text = icons.dap.Breakpoint, texthl = "DapBreakpoint", linehl = "", numhl = "" } + ) + vim.fn.sign_define( + "DapBreakpointCondition", + { text = icons.dap.BreakpointCondition, texthl = "DapBreakpoint", linehl = "", numhl = "" } + ) + vim.fn.sign_define("DapStopped", { text = icons.dap.Stopped, texthl = "DapStopped", linehl = "", numhl = "" }) + vim.fn.sign_define( + "DapBreakpointRejected", + { text = icons.dap.BreakpointRejected, texthl = "DapBreakpoint", linehl = "", numhl = "" } + ) + vim.fn.sign_define("DapLogPoint", { text = icons.dap.LogPoint, texthl = "DapLogPoint", linehl = "", numhl = "" }) + + ---A handler to setup all clients defined under `tool/dap/clients/*.lua` + ---@param config table + local function mason_dap_handler(config) + local dap_name = config.name + local ok, custom_handler = pcall(require, "user.configs.dap-clients." .. dap_name) + if not ok then + -- Use preset if there is no user definition + ok, custom_handler = pcall(require, "tool.dap.clients." .. dap_name) + end + if not ok then + -- Default to use factory config for clients(s) that doesn't include a spec + mason_dap.default_setup(config) + return + elseif type(custom_handler) == "function" then + -- Case where the protocol requires its own setup + -- Make sure to set + -- * dap.adpaters. = { your config } + -- * dap.configurations. = { your config } + -- See `codelldb.lua` for a concrete example. + custom_handler(config) + else + vim.notify( + string.format( + "Failed to setup [%s].\n\nClient definition under `tool/dap/clients` must return\na fun(opts) (got '%s' instead)", + config.name, + type(custom_handler) + ), + vim.log.levels.ERROR, + { title = "nvim-dap" } + ) + end + end + + require("modules.utils").load_plugin("mason-nvim-dap", { + ensure_installed = require("core.settings").dap_deps, + automatic_installation = true, + handlers = { mason_dap_handler }, + }) +end diff --git a/nvim/lua/modules/configs/tool/dropbar.lua b/nvim/lua/modules/configs/tool/dropbar.lua new file mode 100644 index 0000000..a02dcaf --- /dev/null +++ b/nvim/lua/modules/configs/tool/dropbar.lua @@ -0,0 +1,109 @@ +return function() + local icons = { + kind = require("modules.utils.icons").get("kind", true), + type = require("modules.utils.icons").get("type", true), + misc = require("modules.utils.icons").get("misc", true), + ui = require("modules.utils.icons").get("ui", true), + } + + require("modules.utils").load_plugin("dropbar", { + bar = { + hover = false, + truncate = true, + pick = { pivots = "etovxqpdygfblzhckisuran" }, + }, + sources = { + path = { + relative_to = function() + -- Only show the leaf filename in dropbar + return vim.fn.expand("%:p:h") + end, + }, + terminal = { + name = function(buf) + local name = vim.api.nvim_buf_get_name(buf) + local term = select(2, require("toggleterm.terminal").identify(name)) + -- Trying to "snag" a display name from toggleterm + if term then + return term.display_name or term.name + else + return name + end + end, + }, + }, + icons = { + enable = true, + kinds = { + use_devicons = true, + symbols = { + -- Type + Array = icons.type.Array, + Boolean = icons.type.Boolean, + Null = icons.type.Null, + Number = icons.type.Number, + Object = icons.type.Object, + String = icons.type.String, + Text = icons.type.String, + + -- Kind + BreakStatement = icons.kind.Break, + Call = icons.kind.Call, + CaseStatement = icons.kind.Case, + Class = icons.kind.Class, + Color = icons.kind.Color, + Constant = icons.kind.Constant, + Constructor = icons.kind.Constructor, + ContinueStatement = icons.kind.Continue, + Declaration = icons.kind.Declaration, + Delete = icons.kind.Delete, + DoStatement = icons.kind.Loop, + Enum = icons.kind.Enum, + EnumMember = icons.kind.EnumMember, + Event = icons.kind.Event, + Field = icons.kind.Field, + File = icons.kind.File, + ForStatement = icons.kind.Loop, + Function = icons.kind.Function, + Identifier = icons.kind.Variable, + Interface = icons.kind.Interface, + Keyword = icons.kind.Keyword, + List = icons.kind.List, + Lsp = icons.misc.LspAvailable, + Method = icons.kind.Method, + Module = icons.kind.Module, + Namespace = icons.kind.Namespace, + Operator = icons.kind.Operator, + Package = icons.kind.Package, + Pair = icons.kind.List, + Property = icons.kind.Property, + Reference = icons.kind.Reference, + Regex = icons.kind.Regex, + Repeat = icons.kind.Loop, + Scope = icons.kind.Statement, + Snippet = icons.kind.Snippet, + Statement = icons.kind.Statement, + Struct = icons.kind.Struct, + SwitchStatement = icons.kind.Switch, + Type = icons.kind.Interface, + TypeParameter = icons.kind.TypeParameter, + Unit = icons.kind.Unit, + Value = icons.kind.Value, + Variable = icons.kind.Variable, + WhileStatement = icons.kind.Loop, + + -- Microsoft-specific icons + Folder = icons.kind.Folder, + + -- ccls-specific icons + Macro = icons.kind.Macro, + Terminal = icons.kind.Terminal, + }, + }, + ui = { + bar = { separator = "  " }, + menu = { indicator = icons.ui.ArrowClosed }, + }, + }, + }) +end diff --git a/nvim/lua/modules/configs/tool/fcitx5.lua b/nvim/lua/modules/configs/tool/fcitx5.lua new file mode 100644 index 0000000..c8b10e1 --- /dev/null +++ b/nvim/lua/modules/configs/tool/fcitx5.lua @@ -0,0 +1,19 @@ +return function() + require("modules.utils").load_plugin("fcitx5", { + msg = nil, -- string | nil: printed when startup is completed + imname = { -- fcitx5.Imname | nil: imnames on each mode set as prior. See `:h map-table` for more in-depth information. + norm = nil, -- string | nil: imname to set in normal mode. if nil, will restore the mode on exit. + ins = nil, + cmd = nil, + vis = nil, + sel = nil, + opr = nil, + term = nil, + lang = nil, + }, + remember_prior = true, -- boolean: if true, it remembers the mode on exit and restore it when entering the mode again. + -- if false, uses what was set in config. + define_autocmd = true, -- boolean: if true, defines autocmd at `ModeChanged` to switch fcitx5 mode. + log = "warn", -- string: log level (default: warn) + }) +end diff --git a/nvim/lua/modules/configs/tool/nvim-tree.lua b/nvim/lua/modules/configs/tool/nvim-tree.lua new file mode 100644 index 0000000..fcf5288 --- /dev/null +++ b/nvim/lua/modules/configs/tool/nvim-tree.lua @@ -0,0 +1,201 @@ +return function() + local icons = { + diagnostics = require("modules.utils.icons").get("diagnostics"), + documents = require("modules.utils.icons").get("documents"), + git = require("modules.utils.icons").get("git"), + ui = require("modules.utils.icons").get("ui"), + } + + require("modules.utils").load_plugin("nvim-tree", { + auto_reload_on_write = true, + create_in_closed_folder = false, + disable_netrw = false, + hijack_cursor = true, + hijack_netrw = true, + hijack_unnamed_buffer_when_opening = true, + open_on_tab = false, + respect_buf_cwd = false, + sort_by = "name", + sync_root_with_cwd = true, + on_attach = function(bufnr) + require("nvim-tree.api").config.mappings.default_on_attach(bufnr) + vim.keymap.del("n", "", { buffer = bufnr }) + end, + view = { + adaptive_size = false, + centralize_selection = false, + width = 30, + side = "left", + preserve_window_proportions = false, + number = false, + relativenumber = false, + signcolumn = "yes", + float = { + enable = false, + open_win_config = { + relative = "editor", + border = "rounded", + width = 30, + height = 30, + row = 1, + col = 1, + }, + }, + }, + renderer = { + add_trailing = false, + group_empty = true, + highlight_git = true, + full_name = false, + highlight_opened_files = "none", + special_files = { "Cargo.toml", "Makefile", "README.md", "readme.md", "CMakeLists.txt" }, + symlink_destination = true, + indent_markers = { + enable = true, + icons = { + corner = "└ ", + edge = "│ ", + item = "│ ", + none = " ", + }, + }, + root_folder_label = ":.:s?.*?/..?", + icons = { + webdev_colors = true, + git_placement = "after", + show = { + file = true, + folder = true, + folder_arrow = true, + git = true, + }, + padding = " ", + symlink_arrow = " 󰁔 ", + glyphs = { + default = icons.documents.Default, -- + symlink = icons.documents.Symlink, -- + bookmark = icons.ui.Bookmark, + git = { + unstaged = icons.git.Mod_alt, + staged = icons.git.Add, --󰄬 + unmerged = icons.git.Unmerged, + renamed = icons.git.Rename, --󰁔 + untracked = icons.git.Untracked, -- "󰞋" + deleted = icons.git.Remove, -- + ignored = icons.git.Ignore, --◌ + }, + folder = { + arrow_open = icons.ui.ArrowOpen, + arrow_closed = icons.ui.ArrowClosed, + -- arrow_open = "", + -- arrow_closed = "", + default = icons.ui.Folder, + open = icons.ui.FolderOpen, + empty = icons.ui.EmptyFolder, + empty_open = icons.ui.EmptyFolderOpen, + symlink = icons.ui.SymlinkFolder, + symlink_open = icons.ui.FolderOpen, + }, + }, + }, + }, + hijack_directories = { + enable = true, + auto_open = true, + }, + update_focused_file = { + enable = true, + update_root = true, + ignore_list = {}, + }, + filters = { + dotfiles = false, + custom = { ".DS_Store" }, + exclude = {}, + }, + actions = { + use_system_clipboard = true, + change_dir = { + enable = true, + global = false, + }, + open_file = { + quit_on_open = false, + resize_window = false, + window_picker = { + enable = true, + chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890", + exclude = { + buftype = { + "help", + "nofile", + "prompt", + "quickfix", + "terminal", + }, + filetype = { + "dap-repl", + "diff", + "fugitive", + "fugitiveblame", + "git", + "notify", + "NvimTree", + "Outline", + "qf", + "TelescopePrompt", + "toggleterm", + "undotree", + }, + }, + }, + }, + remove_file = { + close_window = true, + }, + }, + diagnostics = { + enable = false, + show_on_dirs = false, + debounce_delay = 50, + icons = { + hint = icons.diagnostics.Hint_alt, + info = icons.diagnostics.Information_alt, + warning = icons.diagnostics.Warning_alt, + error = icons.diagnostics.Error_alt, + }, + }, + filesystem_watchers = { + enable = true, + debounce_delay = 50, + }, + git = { + enable = true, + ignore = false, + show_on_dirs = true, + timeout = 400, + }, + trash = { + cmd = "gio trash", + require_confirm = true, + }, + live_filter = { + prefix = "[FILTER]: ", + always_show_folders = true, + }, + log = { + enable = false, + truncate = false, + types = { + all = false, + config = false, + copy_paste = false, + dev = false, + diagnostics = false, + git = false, + profile = false, + watcher = false, + }, + }, + }) +end diff --git a/nvim/lua/modules/configs/tool/project.lua b/nvim/lua/modules/configs/tool/project.lua new file mode 100644 index 0000000..3d77ab7 --- /dev/null +++ b/nvim/lua/modules/configs/tool/project.lua @@ -0,0 +1,13 @@ +return function() + require("modules.utils").load_plugin("project_nvim", { + manual_mode = false, + detection_methods = { "lsp", "pattern" }, + patterns = { ".git", "_darcs", ".hg", ".bzr", ".svn", "Makefile", "package.json" }, + ignore_lsp = { "null-ls", "copilot" }, + exclude_dirs = {}, + show_hidden = false, + silent_chdir = true, + scope_chdir = "global", + datapath = vim.fn.stdpath("data"), + }) +end diff --git a/nvim/lua/modules/configs/tool/search.lua b/nvim/lua/modules/configs/tool/search.lua new file mode 100644 index 0000000..ffad69c --- /dev/null +++ b/nvim/lua/modules/configs/tool/search.lua @@ -0,0 +1,141 @@ +return function() + local builtin = require("telescope.builtin") + local extensions = require("telescope").extensions + + require("modules.utils").load_plugin("search", { + collections = { + -- Search using filenames + file = { + initial_tab = 1, + tabs = { + { + name = "Files", + tele_func = function(opts) + opts = opts or {} + if vim.fn.isdirectory(".git") == 1 then + builtin.git_files(opts) + else + builtin.find_files(opts) + end + end, + }, + { + name = "Frecency", + tele_func = function() + extensions.frecency.frecency() + end, + }, + { + name = "Oldfiles", + tele_func = function() + builtin.oldfiles() + end, + }, + { + name = "Buffers", + tele_func = function() + builtin.buffers() + end, + }, + }, + }, + -- Search using patterns + pattern = { + initial_tab = 1, + tabs = { + { + name = "Word in project", + tele_func = function() + extensions.live_grep_args.live_grep_args() + end, + }, + { + name = "Word under cursor", + tele_func = function(opts) + opts = opts or {} + builtin.grep_string(opts) + end, + }, + }, + }, + -- Search Git objects (branches, commits) + git = { + initial_tab = 1, + tabs = { + { + name = "Branches", + tele_func = function() + builtin.git_branches() + end, + }, + { + name = "Commits", + tele_func = function() + builtin.git_commits() + end, + }, + { + name = "Commit content", + tele_func = function() + extensions.advanced_git_search.search_log_content() + end, + }, + { + name = "Diff current file with commit", + tele_func = function() + extensions.advanced_git_search.diff_commit_file() + end, + }, + }, + }, + -- Retrieve dossiers + dossier = { + initial_tab = 1, + tabs = { + { + name = "Sessions", + tele_func = function() + extensions.persisted.persisted() + end, + }, + { + name = "Projects", + tele_func = function() + extensions.projects.projects({}) + end, + }, + { + name = "Zoxide", + tele_func = function() + extensions.zoxide.list() + end, + }, + }, + }, + -- Miscellaneous + misc = { + initial_tab = 1, + tabs = { + { + name = "Colorschemes", + tele_func = function() + builtin.colorscheme({ enable_preview = true }) + end, + }, + { + name = "Notify", + tele_func = function() + extensions.notify.notify() + end, + }, + { + name = "Undo History", + tele_func = function() + extensions.undo.undo() + end, + }, + }, + }, + }, + }) +end diff --git a/nvim/lua/modules/configs/tool/smartyank.lua b/nvim/lua/modules/configs/tool/smartyank.lua new file mode 100644 index 0000000..551ae6f --- /dev/null +++ b/nvim/lua/modules/configs/tool/smartyank.lua @@ -0,0 +1,24 @@ +return function() + require("modules.utils").load_plugin("smartyank", { + highlight = { + enabled = false, -- highlight yanked text + higroup = "IncSearch", -- highlight group of yanked text + timeout = 2000, -- timeout for clearing the highlight + }, + clipboard = { + enabled = true, + }, + tmux = { + enabled = true, + -- remove `-w` to disable copy to host client's clipboard + cmd = { "tmux", "set-buffer", "-w" }, + }, + osc52 = { + enabled = true, + escseq = "tmux", -- use tmux escape sequence, only enable if you're using remote tmux and have issues (see #4) + ssh_only = true, -- false to OSC52 yank also in local sessions + silent = false, -- true to disable the "n chars copied" echo + echo_hl = "Directory", -- highlight group of the OSC52 echo message + }, + }) +end diff --git a/nvim/lua/modules/configs/tool/sniprun.lua b/nvim/lua/modules/configs/tool/sniprun.lua new file mode 100644 index 0000000..78cb979 --- /dev/null +++ b/nvim/lua/modules/configs/tool/sniprun.lua @@ -0,0 +1,27 @@ +return function() + require("modules.utils").load_plugin("sniprun", { + selected_interpreters = {}, -- " use those instead of the default for the current filetype + repl_enable = {}, -- " enable REPL-like behavior for the given interpreters + repl_disable = {}, -- " disable REPL-like behavior for the given interpreters + interpreter_options = {}, -- " intepreter-specific options, consult docs / :SnipInfo + -- " you can combo different display modes as desired + display = { + "TempFloatingWindowOk", -- display ok results in the floating window + "NvimNotifyErr", -- display err results with the nvim-notify plugin + -- "Classic", -- display results in the command line" + -- "VirtualText", -- display results in virtual text" + -- "LongTempFloatingWindow", -- display results in the long floating window + -- "Terminal" -- display results in a vertical split + -- "TerminalWithCode" -- display results and code history in a vertical split + }, + display_options = { + terminal_width = 45, + notification_timeout = 5000, + }, + -- " miscellaneous compatibility/adjustement settings + inline_messages = 0, -- " inline_message (0/1) is a one-line way to display messages + -- " to workaround sniprun not being able to display anything + borders = "single", -- " display borders around floating windows + -- " possible values are 'none', 'single', 'double', or 'shadow' + }) +end diff --git a/nvim/lua/modules/configs/tool/telescope.lua b/nvim/lua/modules/configs/tool/telescope.lua new file mode 100644 index 0000000..71884d0 --- /dev/null +++ b/nvim/lua/modules/configs/tool/telescope.lua @@ -0,0 +1,102 @@ +return function() + local icons = { ui = require("modules.utils.icons").get("ui", true) } + local lga_actions = require("telescope-live-grep-args.actions") + + require("modules.utils").load_plugin("telescope", { + defaults = { + vimgrep_arguments = { + "rg", + "--no-heading", + "--with-filename", + "--line-number", + "--column", + "--smart-case", + }, + initial_mode = "insert", + prompt_prefix = " " .. icons.ui.Telescope .. " ", + selection_caret = icons.ui.ChevronRight, + scroll_strategy = "limit", + results_title = false, + layout_strategy = "horizontal", + path_display = { "absolute" }, + selection_strategy = "reset", + sorting_strategy = "ascending", + color_devicons = true, + file_ignore_patterns = { ".git/", ".cache", "build/", "%.class", "%.pdf", "%.mkv", "%.mp4", "%.zip" }, + layout_config = { + horizontal = { + prompt_position = "top", + preview_width = 0.55, + results_width = 0.8, + }, + vertical = { + mirror = false, + }, + width = 0.85, + height = 0.92, + preview_cutoff = 120, + }, + file_previewer = require("telescope.previewers").vim_buffer_cat.new, + grep_previewer = require("telescope.previewers").vim_buffer_vimgrep.new, + qflist_previewer = require("telescope.previewers").vim_buffer_qflist.new, + file_sorter = require("telescope.sorters").get_fuzzy_file, + generic_sorter = require("telescope.sorters").get_generic_fuzzy_sorter, + buffer_previewer_maker = require("telescope.previewers").buffer_previewer_maker, + }, + extensions = { + aerial = { + show_lines = false, + show_nesting = { + ["_"] = false, -- This key will be the default + lua = true, -- You can set the option for specific filetypes + }, + }, + fzf = { + fuzzy = false, + override_generic_sorter = true, + override_file_sorter = true, + case_mode = "smart_case", + }, + frecency = { + show_scores = true, + show_unindexed = true, + ignore_patterns = { "*.git/*", "*/tmp/*" }, + }, + live_grep_args = { + auto_quoting = true, -- enable/disable auto-quoting + mappings = { -- extend mappings + i = { + [""] = lga_actions.quote_prompt(), + [""] = lga_actions.quote_prompt({ postfix = " --iglob " }), + }, + }, + }, + undo = { + side_by_side = true, + mappings = { + i = { + [""] = require("telescope-undo.actions").yank_additions, + [""] = require("telescope-undo.actions").yank_deletions, + [""] = require("telescope-undo.actions").restore, + }, + }, + }, + advanced_git_search = { + diff_plugin = "diffview", + git_flags = { "-c", "delta.side-by-side=true" }, + entry_default_author_or_date = "author", -- one of "author" or "date" + }, + }, + }) + + require("telescope").load_extension("frecency") + require("telescope").load_extension("fzf") + require("telescope").load_extension("live_grep_args") + require("telescope").load_extension("notify") + require("telescope").load_extension("projects") + require("telescope").load_extension("undo") + require("telescope").load_extension("zoxide") + require("telescope").load_extension("persisted") + require("telescope").load_extension("aerial") + require("telescope").load_extension("advanced_git_search") +end diff --git a/nvim/lua/modules/configs/tool/toggleterm.lua b/nvim/lua/modules/configs/tool/toggleterm.lua new file mode 100644 index 0000000..8e12a29 --- /dev/null +++ b/nvim/lua/modules/configs/tool/toggleterm.lua @@ -0,0 +1,51 @@ +return function() + require("modules.utils").load_plugin("toggleterm", { + -- size can be a number or function which is passed the current terminal + size = function(term) + if term.direction == "horizontal" then + return vim.o.lines * 0.30 + elseif term.direction == "vertical" then + return vim.o.columns * 0.40 + end + end, + on_open = function(term) + -- Prevent infinite calls from freezing neovim. + -- Only set these options specific to this terminal buffer. + vim.api.nvim_set_option_value("foldmethod", "manual", { scope = "local" }) + vim.api.nvim_set_option_value("foldexpr", "0", { scope = "local" }) + + -- Prevent horizontal terminal from obscuring `nvim-tree`. + local api = require("nvim-tree.api") + local tree = require("nvim-tree.view") + if tree.is_visible() and term.direction == "horizontal" then + local width = vim.fn.winwidth(tree.get_winnr()) + api.tree.toggle() + tree.View.width = width + api.tree.toggle(false, true) + end + end, + highlights = { + Normal = { + link = "Normal", + }, + NormalFloat = { + link = "NormalFloat", + }, + FloatBorder = { + link = "FloatBorder", + }, + }, + open_mapping = false, -- [[]], + hide_numbers = true, -- hide the number column in toggleterm buffers + shade_filetypes = {}, + shade_terminals = false, + shading_factor = "1", -- the degree by which to darken to terminal colour, default: 1 for dark backgrounds, 3 for light + start_in_insert = true, + persist_mode = false, + insert_mappings = true, -- whether or not the open mapping applies in insert mode + persist_size = true, + direction = "horizontal", + close_on_exit = true, -- close the terminal window when the process exits + shell = vim.o.shell, -- change the default shell + }) +end diff --git a/nvim/lua/modules/configs/tool/trouble.lua b/nvim/lua/modules/configs/tool/trouble.lua new file mode 100644 index 0000000..6a9bbfc --- /dev/null +++ b/nvim/lua/modules/configs/tool/trouble.lua @@ -0,0 +1,38 @@ +return function() + local icons = { + ui = require("modules.utils.icons").get("ui", true), + } + + require("modules.utils").load_plugin("trouble", { + auto_open = false, + auto_close = false, + auto_jump = false, + auto_preview = true, + auto_refresh = true, + focus = false, -- do not focus the window when opened + follow = true, + restore = true, + icons = { + indent = { + fold_open = icons.ui.ArrowOpen, + fold_closed = icons.ui.ArrowClosed, + }, + folder_closed = icons.ui.Folder, + folder_open = icons.ui.FolderOpen, + }, + modes = { + project_diagnostics = { + mode = "diagnostics", + filter = { + any = { + { + function(item) + return item.filename:find(vim.fn.getcwd(), 1, true) + end, + }, + }, + }, + }, + }, + }) +end diff --git a/nvim/lua/modules/configs/tool/which-key.lua b/nvim/lua/modules/configs/tool/which-key.lua new file mode 100644 index 0000000..c9d687e --- /dev/null +++ b/nvim/lua/modules/configs/tool/which-key.lua @@ -0,0 +1,71 @@ +return function() + local icons = { + ui = require("modules.utils.icons").get("ui"), + misc = require("modules.utils.icons").get("misc"), + git = require("modules.utils.icons").get("git", true), + cmp = require("modules.utils.icons").get("cmp", true), + } + + require("which-key").register({ + [""] = { + b = { + name = icons.ui.Buffer .. " Buffer", + }, + d = { + name = icons.ui.Bug .. " Debug", + }, + f = { + name = icons.ui.Telescope .. " Fuzzy Find", + }, + g = { + name = icons.git.Git .. "Git", + }, + l = { + name = icons.misc.LspAvailable .. " Lsp", + }, + n = { + name = icons.ui.FolderOpen .. " Nvim Tree", + }, + p = { + name = icons.ui.Package .. " Package", + }, + s = { + name = icons.cmp.tmux .. "Session", + }, + S = { + name = icons.ui.Search .. " Search", + }, + W = { + name = icons.ui.Window .. " Window", + }, + }, + }) + + require("modules.utils").load_plugin("which-key", { + plugins = { + presets = { + operators = false, + motions = false, + text_objects = false, + windows = false, + nav = false, + z = true, + g = true, + }, + }, + + icons = { + breadcrumb = icons.ui.Separator, + separator = icons.misc.Vbar, + group = "", + }, + + window = { + border = "none", + position = "bottom", + margin = { 1, 0, 1, 0 }, + padding = { 1, 1, 1, 1 }, + winblend = 0, + }, + }) +end diff --git a/nvim/lua/modules/configs/tool/wilder.lua b/nvim/lua/modules/configs/tool/wilder.lua new file mode 100644 index 0000000..45105b3 --- /dev/null +++ b/nvim/lua/modules/configs/tool/wilder.lua @@ -0,0 +1,65 @@ +return function() + local wilder = require("wilder") + local icons = { ui = require("modules.utils.icons").get("ui") } + + wilder.set_option("use_python_remote_plugin", 0) + wilder.set_option("pipeline", { + wilder.branch( + wilder.cmdline_pipeline({ use_python = 0, fuzzy = 1, fuzzy_filter = wilder.lua_fzy_filter() }), + wilder.vim_search_pipeline(), + { + wilder.check(function(_, x) + return x == "" + end), + wilder.history(), + wilder.result({ + draw = { + function(_, x) + return icons.ui.Calendar .. " " .. x + end, + }, + }), + } + ), + }) + + local popupmenu_renderer = wilder.popupmenu_renderer(wilder.popupmenu_border_theme({ + border = "rounded", + highlights = { + default = "Pmenu", + border = "PmenuBorder", -- highlight to use for the border + accent = wilder.make_hl("WilderAccent", "CmpItemAbbr", "CmpItemAbbrMatch"), + }, + empty_message = wilder.popupmenu_empty_message_with_spinner(), + highlighter = wilder.lua_fzy_highlighter(), + left = { + " ", + wilder.popupmenu_devicons(), + wilder.popupmenu_buffer_flags({ + flags = " a + ", + icons = { ["+"] = icons.ui.Pencil, a = icons.ui.Indicator, h = icons.ui.File }, + }), + }, + right = { + " ", + wilder.popupmenu_scrollbar(), + }, + })) + local wildmenu_renderer = wilder.wildmenu_renderer({ + apply_incsearch_fix = false, + highlighter = wilder.lua_fzy_highlighter(), + separator = " | ", + left = { " ", wilder.wildmenu_spinner(), " " }, + right = { " ", wilder.wildmenu_index() }, + }) + wilder.set_option( + "renderer", + wilder.renderer_mux({ + [":"] = popupmenu_renderer, + ["/"] = wildmenu_renderer, + substitute = wildmenu_renderer, + }) + ) + + require("modules.utils").load_plugin("wilder", { modes = { ":", "/", "?" } }) +end diff --git a/nvim/lua/modules/configs/ui/alpha.lua b/nvim/lua/modules/configs/ui/alpha.lua new file mode 100644 index 0000000..befcbd2 --- /dev/null +++ b/nvim/lua/modules/configs/ui/alpha.lua @@ -0,0 +1,138 @@ +return function() + local dashboard = require("alpha.themes.dashboard") + require("modules.utils").gen_alpha_hl() + + dashboard.section.header.val = require("core.settings").dashboard_image + dashboard.section.header.opts.hl = "AlphaHeader" + + local function button(sc, txt, leader_txt, keybind, keybind_opts) + local sc_after = sc:gsub("%s", ""):gsub(leader_txt, "") + + local opts = { + position = "center", + shortcut = sc, + cursor = 5, + width = 50, + align_shortcut = "right", + hl = "AlphaButtons", + hl_shortcut = "AlphaShortcut", + } + + if nil == keybind then + keybind = sc_after + end + keybind_opts = vim.F.if_nil(keybind_opts, { noremap = true, silent = true, nowait = true }) + opts.keymap = { "n", sc_after, keybind, keybind_opts } + + local function on_press() + -- local key = vim.api.nvim_replace_termcodes(keybind .. '', true, false, true) + local key = vim.api.nvim_replace_termcodes(sc_after .. "", true, false, true) + vim.api.nvim_feedkeys(key, "t", false) + end + + return { + type = "button", + val = txt, + on_press = on_press, + opts = opts, + } + end + + local leader = " " + local icons = { + documents = require("modules.utils.icons").get("documents", true), + git = require("modules.utils.icons").get("git", true), + ui = require("modules.utils.icons").get("ui", true), + misc = require("modules.utils.icons").get("misc", true), + } + + dashboard.section.buttons.val = { + button( + "space f c", + icons.misc.Neovim .. "Telescope collections", + leader, + nil, + { noremap = true, silent = true, nowait = true } + ), + button( + "space f f", + icons.documents.FileFind .. "Find files", + leader, + nil, + { noremap = true, silent = true, nowait = true } + ), + button( + "space f d", + icons.ui.FolderWithHeart .. "Retrieve dossiers", + leader, + nil, + { noremap = true, silent = true, nowait = true } + ), + button( + "space f p", + icons.documents.Word .. "Find patterns", + leader, + nil, + { noremap = true, silent = true, nowait = true } + ), + button( + "space f g", + icons.git.Git .. "Locate Git objects", + leader, + nil, + { noremap = true, silent = true, nowait = true } + ), + button( + "space f m", + icons.misc.Ghost .. "Miscellaneous artifacts", + leader, + nil, + { noremap = true, silent = true, nowait = true } + ), + } + dashboard.section.buttons.opts.hl = "AlphaButtons" + + local function footer() + local stats = require("lazy").stats() + local ms = (math.floor(stats.startuptime * 100 + 0.5) / 100) + return "  Have Fun with neovim" + .. " 󰀨 v" + .. vim.version().major + .. "." + .. vim.version().minor + .. "." + .. vim.version().patch + .. " 󰂖 " + .. stats.count + .. " plugins in " + .. ms + .. "ms" + end + + dashboard.section.footer.val = footer() + dashboard.section.footer.opts.hl = "AlphaFooter" + + local head_butt_padding = 2 + local occu_height = #dashboard.section.header.val + 2 * #dashboard.section.buttons.val + head_butt_padding + local header_padding = math.max(0, math.ceil((vim.fn.winheight(0) - occu_height) * 0.25)) + local foot_butt_padding = 1 + + dashboard.config.layout = { + { type = "padding", val = header_padding }, + dashboard.section.header, + { type = "padding", val = head_butt_padding }, + dashboard.section.buttons, + { type = "padding", val = foot_butt_padding }, + dashboard.section.footer, + } + + require("modules.utils").load_plugin("alpha", dashboard.opts) + + vim.api.nvim_create_autocmd("User", { + pattern = "LazyVimStarted", + callback = function() + dashboard.section.footer.val = footer() + pcall(vim.cmd.AlphaRedraw) + end, + }) +end diff --git a/nvim/lua/modules/configs/ui/bufferline.lua b/nvim/lua/modules/configs/ui/bufferline.lua new file mode 100644 index 0000000..b01200d --- /dev/null +++ b/nvim/lua/modules/configs/ui/bufferline.lua @@ -0,0 +1,73 @@ +return function() + local icons = { ui = require("modules.utils.icons").get("ui") } + + local opts = { + options = { + number = nil, + close_command = "BufDel! %d", + right_mouse_command = "BufDel! %d", + modified_icon = icons.ui.Modified, + buffer_close_icon = icons.ui.Close, + left_trunc_marker = icons.ui.Left, + right_trunc_marker = icons.ui.Right, + max_name_length = 20, + max_prefix_length = 13, + tab_size = 20, + color_icons = true, + show_buffer_icons = true, + show_buffer_close_icons = true, + show_close_icon = true, + show_tab_indicators = true, + enforce_regular_tabs = false, + persist_buffer_sort = true, + always_show_bufferline = true, + separator_style = "thin", + diagnostics = "nvim_lsp", + diagnostics_indicator = function(count) + return "(" .. count .. ")" + end, + offsets = { + { + filetype = "NvimTree", + text = "File Explorer", + text_align = "center", + padding = 0, + }, + { + filetype = "aerial", + text = "Symbol Outline", + text_align = "center", + padding = 0, + }, + }, + }, + -- Change bufferline's highlights here! See `:h bufferline-highlights` for detailed explanation. + -- Note: If you use catppuccin then modify the colors below! + highlights = {}, + } + + if vim.g.colors_name:find("catppuccin") then + local cp = require("modules.utils").get_palette() -- Get the palette. + + local catppuccin_hl_overwrite = { + highlights = require("catppuccin.groups.integrations.bufferline").get({ + styles = { "italic", "bold" }, + custom = { + all = { + -- Hint + hint = { fg = cp.rosewater }, + hint_visible = { fg = cp.rosewater }, + hint_selected = { fg = cp.rosewater }, + hint_diagnostic = { fg = cp.rosewater }, + hint_diagnostic_visible = { fg = cp.rosewater }, + hint_diagnostic_selected = { fg = cp.rosewater }, + }, + }, + }), + } + + opts = vim.tbl_deep_extend("force", opts, catppuccin_hl_overwrite) + end + + require("modules.utils").load_plugin("bufferline", opts) +end diff --git a/nvim/lua/modules/configs/ui/catppuccin.lua b/nvim/lua/modules/configs/ui/catppuccin.lua new file mode 100644 index 0000000..c0a9048 --- /dev/null +++ b/nvim/lua/modules/configs/ui/catppuccin.lua @@ -0,0 +1,182 @@ +return function() + local transparent_background = require("core.settings").transparent_background + local clear = {} + + require("modules.utils").load_plugin("catppuccin", { + background = { light = "latte", dark = "mocha" }, -- latte, frappe, macchiato, mocha + dim_inactive = { + enabled = false, + -- Dim inactive splits/windows/buffers. + -- NOT recommended if you use old palette (a.k.a., mocha). + shade = "dark", + percentage = 0.15, + }, + transparent_background = transparent_background, + show_end_of_buffer = false, -- show the '~' characters after the end of buffers + term_colors = true, + compile_path = vim.fn.stdpath("cache") .. "/catppuccin", + styles = { + comments = { "italic" }, + functions = { "bold" }, + keywords = { "italic" }, + operators = { "bold" }, + conditionals = { "bold" }, + loops = { "bold" }, + booleans = { "bold", "italic" }, + numbers = {}, + types = {}, + strings = {}, + variables = {}, + properties = {}, + }, + integrations = { + treesitter = true, + native_lsp = { + enabled = true, + virtual_text = { + errors = { "italic" }, + hints = { "italic" }, + warnings = { "italic" }, + information = { "italic" }, + }, + underlines = { + errors = { "underline" }, + hints = { "underline" }, + warnings = { "underline" }, + information = { "underline" }, + }, + }, + aerial = true, + alpha = false, + barbar = false, + beacon = false, + cmp = true, + coc_nvim = false, + dap = true, + dap_ui = true, + dashboard = false, + dropbar = { enabled = true, color_mode = true }, + fern = false, + fidget = true, + flash = true, + gitgutter = false, + gitsigns = true, + harpoon = false, + headlines = false, + hop = true, + illuminate = true, + indent_blankline = { enabled = true, colored_indent_levels = false }, + leap = false, + lightspeed = false, + lsp_saga = true, + lsp_trouble = true, + markdown = true, + mason = true, + mini = false, + navic = { enabled = false }, + neogit = false, + neotest = false, + neotree = { enabled = false, show_root = true, transparent_panel = false }, + noice = false, + notify = true, + nvimtree = true, + overseer = false, + pounce = false, + rainbow_delimiters = true, + sandwich = false, + semantic_tokens = true, + symbols_outline = false, + telekasten = false, + telescope = { enabled = true, style = "nvchad" }, + treesitter_context = true, + ts_rainbow = false, + vim_sneak = false, + vimwiki = false, + which_key = true, + }, + color_overrides = {}, + highlight_overrides = { + ---@param cp palette + all = function(cp) + return { + -- For base configs + NormalFloat = { fg = cp.text, bg = transparent_background and cp.none or cp.mantle }, + FloatBorder = { + fg = transparent_background and cp.blue or cp.mantle, + bg = transparent_background and cp.none or cp.mantle, + }, + CursorLineNr = { fg = cp.green }, + + -- For native lsp configs + DiagnosticVirtualTextError = { bg = cp.none }, + DiagnosticVirtualTextWarn = { bg = cp.none }, + DiagnosticVirtualTextInfo = { bg = cp.none }, + DiagnosticVirtualTextHint = { bg = cp.none }, + LspInfoBorder = { link = "FloatBorder" }, + + -- For mason.nvim + MasonNormal = { link = "NormalFloat" }, + + -- For indent-blankline + IblIndent = { fg = cp.surface0 }, + IblScope = { fg = cp.surface2, style = { "bold" } }, + + -- For nvim-cmp and wilder.nvim + Pmenu = { fg = cp.overlay2, bg = transparent_background and cp.none or cp.base }, + PmenuBorder = { fg = cp.surface1, bg = transparent_background and cp.none or cp.base }, + PmenuSel = { bg = cp.green, fg = cp.base }, + CmpItemAbbr = { fg = cp.overlay2 }, + CmpItemAbbrMatch = { fg = cp.blue, style = { "bold" } }, + CmpDoc = { link = "NormalFloat" }, + CmpDocBorder = { + fg = transparent_background and cp.surface1 or cp.mantle, + bg = transparent_background and cp.none or cp.mantle, + }, + + -- For fidget + FidgetTask = { bg = cp.none, fg = cp.surface2 }, + FidgetTitle = { fg = cp.blue, style = { "bold" } }, + + -- For nvim-notify + NotifyBackground = { bg = cp.base }, + + -- For nvim-tree + NvimTreeRootFolder = { fg = cp.pink }, + NvimTreeIndentMarker = { fg = cp.surface2 }, + + -- For trouble.nvim + TroubleNormal = { bg = transparent_background and cp.none or cp.base }, + TroubleNormalNC = { bg = transparent_background and cp.none or cp.base }, + + -- For telescope.nvim + TelescopeMatching = { fg = cp.lavender }, + TelescopeResultsDiffAdd = { fg = cp.green }, + TelescopeResultsDiffChange = { fg = cp.yellow }, + TelescopeResultsDiffDelete = { fg = cp.red }, + + -- For glance.nvim + GlanceWinBarFilename = { fg = cp.subtext1, style = { "bold" } }, + GlanceWinBarFilepath = { fg = cp.subtext0, style = { "italic" } }, + GlanceWinBarTitle = { fg = cp.teal, style = { "bold" } }, + GlanceListCount = { fg = cp.lavender }, + GlanceListFilepath = { link = "Comment" }, + GlanceListFilename = { fg = cp.blue }, + GlanceListMatch = { fg = cp.lavender, style = { "bold" } }, + GlanceFoldIcon = { fg = cp.green }, + + -- For nvim-treehopper + TSNodeKey = { + fg = cp.peach, + bg = transparent_background and cp.none or cp.base, + style = { "bold", "underline" }, + }, + + -- For treesitter + ["@keyword.return"] = { fg = cp.pink, style = clear }, + ["@error.c"] = { fg = cp.none, style = clear }, + ["@error.cpp"] = { fg = cp.none, style = clear }, + } + end, + }, + }) +end diff --git a/nvim/lua/modules/configs/ui/fidget.lua b/nvim/lua/modules/configs/ui/fidget.lua new file mode 100644 index 0000000..ee920a3 --- /dev/null +++ b/nvim/lua/modules/configs/ui/fidget.lua @@ -0,0 +1,25 @@ +return function() + local icons = { + ui = require("modules.utils.icons").get("ui"), + } + + require("modules.utils").load_plugin("fidget", { + progress = { + suppress_on_insert = false, -- Suppress new messages while in insert mode + ignore_done_already = false, -- Ignore new tasks that are already complete + ignore = { "null-ls" }, -- List of LSP servers to ignore + display = { + render_limit = 5, -- How many LSP messages to show at once + done_ttl = 2, -- How long a message should persist after completion + done_icon = icons.ui.Accepted, -- Icon shown when all LSP progress tasks are complete + }, + }, + notification = { + override_vim_notify = false, -- Automatically override vim.notify() with Fidget + window = { + winblend = 0, -- Background color opacity in the notification window + zindex = 75, -- Stacking priority of the notification window + }, + }, + }) +end diff --git a/nvim/lua/modules/configs/ui/gitsigns.lua b/nvim/lua/modules/configs/ui/gitsigns.lua new file mode 100644 index 0000000..de14cd5 --- /dev/null +++ b/nvim/lua/modules/configs/ui/gitsigns.lua @@ -0,0 +1,23 @@ +return function() + local mapping = require("keymap.ui") + require("modules.utils").load_plugin("gitsigns", { + signs = { + add = { text = "┃" }, + change = { text = "┃" }, + delete = { text = "_" }, + topdelete = { text = "‾" }, + changedelete = { text = "~" }, + untracked = { text = "┆" }, + }, + auto_attach = true, + on_attach = mapping.gitsigns, + signcolumn = true, + sign_priority = 6, + update_debounce = 100, + word_diff = false, + current_line_blame = true, + diff_opts = { internal = true }, + watch_gitdir = { follow_files = true }, + current_line_blame_opts = { delay = 1000, virt_text = true, virtual_text_pos = "eol" }, + }) +end diff --git a/nvim/lua/modules/configs/ui/indent-blankline.lua b/nvim/lua/modules/configs/ui/indent-blankline.lua new file mode 100644 index 0000000..169a56f --- /dev/null +++ b/nvim/lua/modules/configs/ui/indent-blankline.lua @@ -0,0 +1,578 @@ +return function() + -- This list delineates the per-language nodes used for guiding |ibl| in highlighting the current scope + -- It is extracted from each language's `indents.scm` file + -- NOTE: Only a subset of the supported programming languages is included + -- If your preferred language isn't listed, you can add it to the user config + local nodes = { + bibtex = { + "entry", + }, + c = { + "case_statement", + "compound_literal_expression", + "enumerator_list", + "field_declaration_list", + "initializer_list", + "init_declarator", + }, + cmake = { + "block_def", + "foreach_loop", + "function_def", + "if_condition", + "macro_def", + "normal_command", + "while_loop", + }, + cpp = { + "case_statement", + "compound_literal_expression", + "condition_clause", + "enumerator_list", + "field_declaration_list", + "field_initializer_list", + "init_declarator", + "initializer_list", + "namespace_definition", + }, + css = { + "block", + "declaration", + }, + d = { + "aggregate_body", + "block_statement", + "case_statement", + "expression_statement", + "function_body", + "parameters", + "scope_statement", + "template_parameters", + }, + dart = { + "arguments", + "class_body", + "formal_parameter", + "formal_parameter_list", + "function_body", + "function_expression_body", + "initializers", + "list_literal", + "return_statement", + "switch_block", + }, + dot = { + "block", + "attr_list", + }, + ecma = { + "arguments", + "array", + "binary_expression", + "call_expression", + "class_body", + "export_clause", + "formal_parameters", + "named_imports", + "object", + "object_pattern", + "parenthesized_expression", + "return_statement", + "switch_case", + "switch_default", + "switch_statement", + "template_substitution", + "ternary_expression", + }, + elixir = { + "arguments", + "block", + "do_block", + "list", + "map", + "tuple", + }, + firrtl = { + "memory", + }, + fortran = { + "derived_type_definition", + "do_loop_statement", + "enum", + "function", + "if_statement", + "module", + "program", + "subroutine", + "where_statement", + }, + gleam = { + "anonymous_function", + "assert", + "case", + "constant", + "external_function", + "function", + "import", + "let", + "list", + "constant", + "function", + "type_definition", + "type_alias", + "todo", + "tuple", + }, + go = { + "call_expression", + "communication_case", + "const_declaration", + "default_case", + "expression_case", + "import_declaration", + "literal_value", + "parameter_list", + "struct_type", + "type_case", + "type_declaration", + "var_declaration", + }, + html = { + "start_tag", + "self_closing_tag", + }, + java = { + "annotation_argument_list", + "annotation_type_body", + "argument_list", + "array_initializer", + "class_body", + "constructor_body", + "element_value_array_initializer", + "enum_body", + "formal_parameters", + "interface_body", + "method_invocation", + "switch_block", + }, + javascript = { + "arguments", + "array", + "binary_expression", + "call_expression", + "class_body", + "export_clause", + "formal_parameters", + "jsx_expression", + "jsx_self_closing_element", + "named_imports", + "object", + "object_pattern", + "parenthesized_expression", + "return_statement", + "switch_case", + "switch_default", + "switch_statement", + "template_substitution", + "ternary_expression", + }, + julia = { + "assignment", + "call_expression", + "compound_statement", + "comprehension_expression", + "for_binding", + "if_statement", + "matrix_expression", + "parenthesized_expression", + "struct_definition", + "tuple_expression", + "vector_expression", + }, + just = { + "external_command", + "recipe", + "string", + }, + linkerscript = { + "memory_command", + "output_section", + "phdrs_command", + "sections_command", + }, + lua = { + "arguments", + "field", + "method_index_expression", + "return_statement", + "table_constructor", + }, + matlab = { + "class_definition", + "enumeration", + "events", + "for_statement", + "if_statement", + "methods", + "properties", + "switch_statement", + "try_statement", + "while_statement", + }, + ninja = { + "build", + "pool", + "rule", + }, + ocaml = { + "application_expression", + "do_clause", + "external", + "field_expression", + "if_expression", + "list_expression", + "parenthesized_expression", + "record_declaration", + "record_expression", + "try_expression", + "type_binding", + "value_specification", + }, + pascal = { + "arrInitializer", + "block", + "declArgs", + "declClass", + "declConsts", + "declProc", + "declTypes", + "declUses", + "declVars", + "defaultValue", + "exprArgs", + "exprBrackets", + "exprParens", + "exprSubscript", + "recInitializer", + "statement", + }, + php = { + "arguments", + "array_creation_expression", + "binary_expression", + "case_statement", + "compound_statement", + "declaration_list", + "default_statement", + "enum_declaration_list", + "formal_parameters", + "match_block", + "member_call_expression", + "parenthesized_expression", + "return_statement", + "switch_block", + }, + python = { + "binary_operator", + "case_clause", + "concatenated_string", + "for_statement", + "generator_expression", + "if_statement", + "import_from_statement", + "lambda", + "list_pattern", + "match_statement", + "parenthesized_expression", + "try_statement", + "tuple_pattern", + "while_statement", + "with_statement", + }, + query = { + "list", + "predicate", + }, + r = { + "brace_list", + "call", + "paren_list", + "pipe", + "special", + }, + readline = { + "conditional_construct", + }, + ruby = { + "argument_list", + "array", + "assignment", + "begin", + "call", + "case", + "for", + "hash", + "if", + "module", + "parenthesized_statements", + "singleton_class", + "singleton_method", + "unless", + "until", + "while", + }, + rust = { + "arguments", + "array_expression", + "assignment_expression", + "call_expression", + "enum_variant_list", + "field_declaration_list", + "macro_definition", + "match_block", + "mod_item", + "ordered_field_declaration_list", + "parameters", + "struct_expression", + "struct_pattern", + "token_repetition", + "token_tree", + "trait_item", + "tuple_expression", + "tuple_pattern", + "tuple_struct_pattern", + "tuple_type", + "use_list", + "where_clause", + }, + scss = { + "block", + "declaration", + "each_statement", + "mixin_statement", + "while_statement", + }, + sql = { + "case", + "column_definitions", + "cte", + "insert", + "select", + "subquery", + "when_clause", + }, + ssh_config = { + "host_declaration", + "match_declaration", + }, + swift = { + "array_literal", + "array_type", + "assignment", + "call_expression", + "class_body", + "computed_getter", + "computed_property", + "computed_setter", + "control_transfer_statement", + "deinit_declaration", + "dictionary_literal", + "dictionary_type", + "didset_clause", + "enum_class_body", + "init_declaration", + "lambda_literal", + "protocol_body", + "subscript_declaration", + "tuple_expression", + "tuple_type", + "type_parameters", + "willset_clause", + "willset_didset_block", + }, + tablegen = { + "assert", + "value_suffix", + }, + tcl = { + "braced_word_simple", + "command", + "command_substitution", + "conditional", + "foreach", + "namespace", + "procedure", + "try", + "while", + }, + teal = { + "record_declaration", + "function_body", + "table_constructor", + "return_statement", + "while_statement", + }, + terraform = { + "block", + "function_call", + "object", + "tuple", + }, + textproto = { + "message_list", + "message_value", + "scalar_list", + }, + toml = { + "array", + "inline_table", + }, + typescript = { + "arguments", + "array", + "binary_expression", + "call_expression", + "class_body", + "enum_declaration", + "export_clause", + "formal_parameters", + "interface_declaration", + "named_imports", + "object", + "object_pattern", + "object_type", + "parenthesized_expression", + "return_statement", + "switch_case", + "switch_default", + "switch_statement", + "template_substitution", + "ternary_expression", + }, + vue = { + "start_tag", + }, + xml = { + "element", + }, + zig = { + "Block", + "ContainerDecl", + "InitList", + "SwitchExpr", + }, + } + + require("modules.utils").load_plugin("ibl", { + enabled = true, + debounce = 200, + indent = { + char = "│", + tab_char = "│", + smart_indent_cap = true, + priority = 2, + }, + whitespace = { remove_blankline_trail = true }, + -- Note: The `scope` field requires treesitter to be set up + scope = { + enabled = true, + char = "┃", + show_start = false, + show_end = false, + injected_languages = true, + priority = 1000, + include = { + node_type = { + angular = nodes.html, + arduino = nodes.cpp, + astro = nodes.html, + bibtex = nodes.bibtex, + c = nodes.c, + cmake = nodes.cmake, + cpp = nodes.cpp, + css = nodes.css, + cuda = nodes.cpp, + d = nodes.d, + dart = nodes.dart, + dot = nodes.dot, + ecma = nodes.ecma, + elixir = nodes.elixir, + firrtl = nodes.firrtl, + fortran = nodes.fortran, + glsl = nodes.c, + gleam = nodes.gleam, + go = nodes.go, + hlsl = nodes.cpp, + html = nodes.html, + java = nodes.java, + javascript = nodes.javascript, + julia = nodes.julia, + just = nodes.just, + linkerscript = nodes.linkerscript, + lua = nodes.lua, + luau = nodes.lua, + matlab = nodes.matlab, + ninja = nodes.ninja, + objc = nodes.c, + ocaml = nodes.ocaml, + ocaml_interface = nodes.ocaml, + pascal = nodes.pascal, + php = nodes.php, + python = nodes.python, + query = nodes.query, + r = nodes.r, + readline = nodes.readline, + ruby = nodes.ruby, + rust = nodes.rust, + scss = nodes.scss, + sql = nodes.sql, + ssh_config = nodes.ssh_config, + swift = nodes.swift, + tablegen = nodes.tablegen, + tcl = nodes.tcl, + teal = nodes.teal, + terraform = nodes.terraform, + textproto = nodes.textproto, + toml = nodes.toml, + typescript = nodes.typescript, + vue = nodes.vue, + xml = nodes.xml, + zig = nodes.zig, + }, + }, + }, + exclude = { + buftypes = { + "help", + "nofile", + "prompt", + "quickfix", + "terminal", + }, + filetypes = { + "", -- for all buffers without a file type + "alpha", + "bigfile", + "checkhealth", + "dap-repl", + "diff", + "fugitive", + "fugitiveblame", + "git", + "gitcommit", + "help", + "log", + "markdown", + "notify", + "NvimTree", + "Outline", + "qf", + "TelescopePrompt", + "text", + "toggleterm", + "undotree", + "vimwiki", + }, + }, + }) +end diff --git a/nvim/lua/modules/configs/ui/lualine.lua b/nvim/lua/modules/configs/ui/lualine.lua new file mode 100644 index 0000000..39e4b89 --- /dev/null +++ b/nvim/lua/modules/configs/ui/lualine.lua @@ -0,0 +1,347 @@ +return function() + local has_catppuccin = vim.g.colors_name:find("catppuccin") ~= nil + local colors = require("modules.utils").get_palette() + local icons = { + diagnostics = require("modules.utils.icons").get("diagnostics", true), + git = require("modules.utils.icons").get("git", true), + git_nosep = require("modules.utils.icons").get("git"), + misc = require("modules.utils.icons").get("misc", true), + ui = require("modules.utils.icons").get("ui", true), + } + + local function custom_theme() + vim.api.nvim_create_autocmd("ColorScheme", { + group = vim.api.nvim_create_augroup("LualineColorScheme", { clear = true }), + pattern = "*", + callback = function() + has_catppuccin = vim.g.colors_name:find("catppuccin") ~= nil + require("lualine").setup({ options = { theme = custom_theme() } }) + end, + }) + + if has_catppuccin then + colors = require("modules.utils").get_palette() + local universal_bg = require("core.settings").transparent_background and "NONE" or colors.mantle + return { + normal = { + a = { fg = colors.lavender, bg = colors.surface0, gui = "bold" }, + b = { fg = colors.text, bg = universal_bg }, + c = { fg = colors.text, bg = universal_bg }, + }, + command = { + a = { fg = colors.peach, bg = colors.surface0, gui = "bold" }, + }, + insert = { + a = { fg = colors.green, bg = colors.surface0, gui = "bold" }, + }, + visual = { + a = { fg = colors.flamingo, bg = colors.surface0, gui = "bold" }, + }, + terminal = { + a = { fg = colors.teal, bg = colors.surface0, gui = "bold" }, + }, + replace = { + a = { fg = colors.red, bg = colors.surface0, gui = "bold" }, + }, + inactive = { + a = { fg = colors.subtext0, bg = universal_bg, gui = "bold" }, + b = { fg = colors.subtext0, bg = universal_bg }, + c = { fg = colors.subtext0, bg = universal_bg }, + }, + } + else + return "auto" + end + end + + local conditionals = { + has_enough_room = function() + return vim.o.columns > 100 + end, + has_comp_before = function() + return vim.bo.filetype ~= "" + end, + has_git = function() + local gitdir = vim.fs.find(".git", { + limit = 1, + upward = true, + type = "directory", + path = vim.fn.expand("%:p:h"), + }) + return #gitdir > 0 + end, + } + + ---@class lualine_hlgrp + ---@field fg string + ---@field bg string + ---@field gui string? + local utils = { + force_centering = function() + return "%=" + end, + abbreviate_path = function(path) + local home = require("core.global").home + if path:find(home, 1, true) == 1 then + path = "~" .. path:sub(#home + 1) + end + return path + end, + ---Generate `color` for any component + ---@param fg string @Foreground hl group + ---@param gen_bg boolean @Generate guibg from hl group |StatusLine|? + ---@param special_nobg boolean @Disable guibg for transparent backgrounds? + ---@param bg string? @Background hl group + ---@param gui string? @GUI highlight arguments + ---@return nil|fun():lualine_hlgrp + gen_hl = function(fg, gen_bg, special_nobg, bg, gui) + if has_catppuccin then + return function() + local guifg = colors[fg] + local guibg = gen_bg and require("modules.utils").hl_to_rgb("StatusLine", true, colors.mantle) + or colors[bg] + local nobg = special_nobg and require("core.settings").transparent_background + return { + fg = guifg and guifg or colors.none, + bg = (guibg and not nobg) and guibg or colors.none, + gui = gui and gui or nil, + } + end + else + -- Return `nil` if the theme is user-defined + return nil + end + end, + } + + local function diff_source() + local gitsigns = vim.b.gitsigns_status_dict + if gitsigns then + return { + added = gitsigns.added, + modified = gitsigns.changed, + removed = gitsigns.removed, + } + end + end + + local components = { + separator = { -- use as section separators + function() + return "│" + end, + padding = 0, + color = utils.gen_hl("surface1", true, true), + separator = { left = "", right = "" }, + }, + + file_status = { + function() + local function is_new_file() + local filename = vim.fn.expand("%") + return filename ~= "" and vim.bo.buftype == "" and vim.fn.filereadable(filename) == 0 + end + + local symbols = {} + if vim.bo.modified then + table.insert(symbols, "[+]") + end + if vim.bo.modifiable == false then + table.insert(symbols, "[-]") + end + if vim.bo.readonly == true then + table.insert(symbols, "[RO]") + end + if is_new_file() then + table.insert(symbols, "[New]") + end + return #symbols > 0 and table.concat(symbols, "") or "" + end, + padding = { left = -1, right = 1 }, + cond = conditionals.has_comp_before, + }, + + lsp = { + function() + local buf_ft = vim.bo.filetype + local clients = vim.lsp.get_clients({ buffer = vim.api.nvim_get_current_buf() }) + local lsp_lists = {} + local available_servers = {} + if next(clients) == nil then + return icons.misc.NoActiveLsp -- No server available + end + for _, client in ipairs(clients) do + local filetypes = client.config.filetypes + local client_name = client.name + if filetypes and vim.fn.index(filetypes, buf_ft) ~= -1 then + -- Avoid adding servers that already exists. + if not lsp_lists[client_name] then + lsp_lists[client_name] = true + table.insert(available_servers, client_name) + end + end + end + return next(available_servers) == nil and icons.misc.NoActiveLsp + or string.format("%s[%s]", icons.misc.LspAvailable, table.concat(available_servers, ", ")) + end, + color = utils.gen_hl("blue", true, true, nil, "bold"), + cond = conditionals.has_enough_room, + }, + + python_venv = { + function() + local function env_cleanup(venv) + if string.find(venv, "/") then + local final_venv = venv + for w in venv:gmatch("([^/]+)") do + final_venv = w + end + venv = final_venv + end + return venv + end + + if vim.bo.filetype == "python" then + local venv = os.getenv("CONDA_DEFAULT_ENV") + if venv then + return icons.misc.PyEnv .. env_cleanup(venv) + end + venv = os.getenv("VIRTUAL_ENV") + if venv then + return icons.misc.PyEnv .. env_cleanup(venv) + end + end + return "" + end, + color = utils.gen_hl("green", true, true), + cond = conditionals.has_enough_room, + }, + + tabwidth = { + function() + return icons.ui.Tab .. vim.bo.tabstop + end, + padding = 1, + }, + + cwd = { + function() + return icons.ui.FolderWithHeart .. utils.abbreviate_path(vim.fs.normalize(vim.fn.getcwd())) + end, + color = utils.gen_hl("subtext0", true, true, nil, "bold"), + }, + + file_location = { + function() + local cursorline = vim.fn.line(".") + local cursorcol = vim.fn.virtcol(".") + local filelines = vim.fn.line("$") + local position + if cursorline == 1 then + position = "Top" + elseif cursorline == filelines then + position = "Bot" + else + position = string.format("%2d%%%%", math.floor(cursorline / filelines * 100)) + end + return string.format("%s · %3d:%-2d", position, cursorline, cursorcol) + end, + }, + } + + require("modules.utils").load_plugin("lualine", { + options = { + icons_enabled = true, + theme = custom_theme(), + disabled_filetypes = { statusline = { "alpha" } }, + component_separators = "", + section_separators = { left = "", right = "" }, + }, + sections = { + lualine_a = { "mode" }, + lualine_b = { + { + "filetype", + colored = true, + icon_only = false, + icon = { align = "left" }, + }, + components.file_status, + vim.tbl_extend("force", components.separator, { + cond = function() + return conditionals.has_git() and conditionals.has_comp_before() + end, + }), + }, + lualine_c = { + { + "branch", + icon = icons.git_nosep.Branch, + color = utils.gen_hl("subtext0", true, true, nil, "bold"), + cond = conditionals.has_git, + }, + { + "diff", + symbols = { + added = icons.git.Add, + modified = icons.git.Mod_alt, + removed = icons.git.Remove, + }, + source = diff_source, + colored = false, + color = utils.gen_hl("subtext0", true, true), + cond = conditionals.has_git, + padding = { right = 1 }, + }, + + { utils.force_centering }, + { + "diagnostics", + sources = { "nvim_diagnostic" }, + sections = { "error", "warn", "info", "hint" }, + symbols = { + error = icons.diagnostics.Error, + warn = icons.diagnostics.Warning, + info = icons.diagnostics.Information, + hint = icons.diagnostics.Hint_alt, + }, + }, + components.lsp, + }, + lualine_x = { + { + "encoding", + fmt = string.upper, + padding = { left = 1 }, + cond = conditionals.has_enough_room, + }, + { + "fileformat", + symbols = { + unix = "LF", + dos = "CRLF", + mac = "CR", -- Legacy macOS + }, + padding = { left = 1 }, + }, + components.tabwidth, + }, + lualine_y = { + components.separator, + components.python_venv, + components.cwd, + }, + lualine_z = { components.file_location }, + }, + inactive_sections = { + lualine_a = {}, + lualine_b = {}, + lualine_c = { "filename" }, + lualine_x = { "location" }, + lualine_y = {}, + lualine_z = {}, + }, + tabline = {}, + extensions = {}, + }) +end diff --git a/nvim/lua/modules/configs/ui/neodim.lua b/nvim/lua/modules/configs/ui/neodim.lua new file mode 100644 index 0000000..08ee94d --- /dev/null +++ b/nvim/lua/modules/configs/ui/neodim.lua @@ -0,0 +1,37 @@ +return function() + local blend_color = require("modules.utils").gen_neodim_blend_attr() + + require("modules.utils").load_plugin("neodim", { + alpha = 0.45, + blend_color = blend_color, + refresh_delay = 75, -- time in ms to wait after typing before refreshing diagnostics + hide = { + virtual_text = true, + signs = false, + underline = false, + }, + priority = 80, + disable = { + "alpha", + "bigfile", + "checkhealth", + "dap-repl", + "diff", + "fugitive", + "fugitiveblame", + "git", + "gitcommit", + "help", + "log", + "notify", + "NvimTree", + "Outline", + "qf", + "TelescopePrompt", + "text", + "toggleterm", + "undotree", + "vimwiki", + }, + }) +end diff --git a/nvim/lua/modules/configs/ui/neoscroll.lua b/nvim/lua/modules/configs/ui/neoscroll.lua new file mode 100644 index 0000000..9b1ae90 --- /dev/null +++ b/nvim/lua/modules/configs/ui/neoscroll.lua @@ -0,0 +1,24 @@ +return function() + require("modules.utils").load_plugin("neoscroll", { + -- All these keys will be mapped to their corresponding default scrolling animation + mappings = { + "", + "", + "", + "", + "", + "", + "zt", + "zz", + "zb", + }, + hide_cursor = true, -- Hide cursor while scrolling + stop_eof = true, -- Stop at when scrolling downwards + use_local_scrolloff = false, -- Use the local scope of scrolloff instead of the global scope + respect_scrolloff = false, -- Stop scrolling when the cursor reaches the scrolloff margin of the file + cursor_scrolls_alone = true, -- The cursor will keep on scrolling even if the window cannot scroll further + easing_function = nil, -- Default easing function + pre_hook = nil, -- Function to run before the scrolling animation starts + post_hook = nil, -- Function to run after the scrolling animation ends + }) +end diff --git a/nvim/lua/modules/configs/ui/notify.lua b/nvim/lua/modules/configs/ui/notify.lua new file mode 100644 index 0000000..a3dd3b6 --- /dev/null +++ b/nvim/lua/modules/configs/ui/notify.lua @@ -0,0 +1,41 @@ +return function() + local notify = require("notify") + local icons = { + diagnostics = require("modules.utils.icons").get("diagnostics"), + ui = require("modules.utils.icons").get("ui"), + } + + require("modules.utils").load_plugin("notify", { + ---@usage Animation style one of { "fade", "slide", "fade_in_slide_out", "static" } + stages = "fade", + ---@usage Function called when a new window is opened, use for changing win settings/config + on_open = function(win) + vim.api.nvim_set_option_value("winblend", 0, { scope = "local", win = win }) + vim.api.nvim_win_set_config(win, { zindex = 90 }) + end, + ---@usage Function called when a window is closed + on_close = nil, + ---@usage timeout for notifications in ms, default 5000 + timeout = 2000, + -- @usage User render fps value + fps = 20, + -- Render function for notifications. See notify-render() + render = "default", + ---@usage highlight behind the window for stages that change opacity + background_colour = "NotifyBackground", + ---@usage minimum width for notification windows + minimum_width = 50, + ---@usage notifications with level lower than this would be ignored. [ERROR > WARN > INFO > DEBUG > TRACE] + level = "INFO", + ---@usage Icons for the different levels + icons = { + ERROR = icons.diagnostics.Error, + WARN = icons.diagnostics.Warning, + INFO = icons.diagnostics.Information, + DEBUG = icons.ui.Bug, + TRACE = icons.ui.Pencil, + }, + }) + + vim.notify = notify +end diff --git a/nvim/lua/modules/configs/ui/paint.lua b/nvim/lua/modules/configs/ui/paint.lua new file mode 100644 index 0000000..5747cc2 --- /dev/null +++ b/nvim/lua/modules/configs/ui/paint.lua @@ -0,0 +1,20 @@ +return function() + require("modules.utils").load_plugin("paint", { + ---type PaintHighlight[] + highlights = { + { + -- filter can be a table of buffer options that should match, + -- or a function called with buf as param that should return true. + -- The example below will paint @something in comments with Constant + filter = { filetype = "lua" }, + pattern = "%s*%-%-%-%s*(@%w+)", + hl = "Constant", + }, + { + filter = { filetype = "python" }, + pattern = "%s*([_%w]+:)", + hl = "Constant", + }, + }, + }) +end diff --git a/nvim/lua/modules/configs/ui/scrollview.lua b/nvim/lua/modules/configs/ui/scrollview.lua new file mode 100644 index 0000000..ef11b59 --- /dev/null +++ b/nvim/lua/modules/configs/ui/scrollview.lua @@ -0,0 +1,24 @@ +return function() + local icons = { diagnostics = require("modules.utils.icons").get("diagnostics", true) } + + require("modules.utils").load_plugin("scrollview", { + mode = "virtual", + winblend = 0, + signs_on_startup = { "diagnostics", "folds", "marks", "search", "spell" }, + diagnostics_error_symbol = icons.diagnostics.Error, + diagnostics_warn_symbol = icons.diagnostics.Warning, + diagnostics_info_symbol = icons.diagnostics.Information, + diagnostics_hint_symbol = icons.diagnostics.Hint, + excluded_filetypes = { + "alpha", + "fugitive", + "git", + "notify", + "NvimTree", + "Outline", + "TelescopePrompt", + "toggleterm", + "undotree", + }, + }) +end diff --git a/nvim/lua/modules/configs/ui/todo.lua b/nvim/lua/modules/configs/ui/todo.lua new file mode 100644 index 0000000..ebdef8e --- /dev/null +++ b/nvim/lua/modules/configs/ui/todo.lua @@ -0,0 +1,58 @@ +return function() + local icons = { + diagnostics = require("modules.utils.icons").get("diagnostics"), + ui = require("modules.utils.icons").get("ui"), + } + + require("modules.utils").load_plugin("todo-comments", { + signs = false, -- show icons in the signs column + keywords = { + FIX = { + icon = icons.ui.Bug, + color = "error", + alt = { "FIXME", "BUG", "FIXIT", "ISSUE" }, + }, + TODO = { icon = icons.ui.Accepted, color = "info" }, + -- HACK = { icon = icons.ui.Fire, color = "warning" }, + WARN = { icon = icons.diagnostics.Warning, color = "warning", alt = { "WARNING", "XXX" } }, + PERF = { icon = icons.ui.Perf, alt = { "OPTIM", "PERFORMANCE", "OPTIMIZE" } }, + NOTE = { icon = icons.ui.Note, color = "hint", alt = { "INFO" } }, + TEST = { icon = icons.ui.Lock, color = "test", alt = { "TESTING", "PASSED", "FAILED" } }, + }, + gui_style = { + fg = "NONE", + bg = "BOLD", + }, + merge_keywords = true, + highlight = { + multiline = false, + keyword = "wide", -- "fg", "bg", "wide", "wide_bg", "wide_fg" or empty. + after = "", + comments_only = true, + max_line_len = 500, + exclude = { + "alpha", + "bigfile", + "checkhealth", + "dap-repl", + "diff", + "help", + "log", + "notify", + "NvimTree", + "Outline", + "qf", + "TelescopePrompt", + "toggleterm", + "undotree", + }, + }, + colors = { + error = { "DiagnosticError", "ErrorMsg", "#DC2626" }, + warning = { "DiagnosticWarn", "WarningMsg", "#FBBF24" }, + info = { "DiagnosticInfo", "#2563EB" }, + hint = { "DiagnosticHint", "#F5C2E7" }, + default = { "Conditional", "#7C3AED" }, + }, + }) +end diff --git a/nvim/lua/modules/plugins/completion.lua b/nvim/lua/modules/plugins/completion.lua new file mode 100644 index 0000000..af694f7 --- /dev/null +++ b/nvim/lua/modules/plugins/completion.lua @@ -0,0 +1,94 @@ +local completion = {} +local use_copilot = require("core.settings").use_copilot + +completion["neovim/nvim-lspconfig"] = { + lazy = true, + event = { "CursorHold", "CursorHoldI" }, + config = require("completion.lsp"), + dependencies = { + { "williamboman/mason.nvim" }, + { "williamboman/mason-lspconfig.nvim" }, + { "folke/neoconf.nvim" }, + { + "Jint-lzxy/lsp_signature.nvim", + config = require("completion.lsp-signature"), + }, + }, +} +completion["nvimdev/lspsaga.nvim"] = { + lazy = true, + event = "LspAttach", + config = require("completion.lspsaga"), + dependencies = { "nvim-tree/nvim-web-devicons" }, +} +completion["stevearc/aerial.nvim"] = { + lazy = true, + event = "LspAttach", + config = require("completion.aerial"), +} +completion["DNLHC/glance.nvim"] = { + lazy = true, + event = "LspAttach", + config = require("completion.glance"), +} +completion["joechrisellis/lsp-format-modifications.nvim"] = { + lazy = true, + event = "LspAttach", +} +completion["nvimtools/none-ls.nvim"] = { + lazy = true, + event = { "CursorHold", "CursorHoldI" }, + config = require("completion.null-ls"), + dependencies = { + "nvim-lua/plenary.nvim", + "jay-babu/mason-null-ls.nvim", + }, +} +completion["hrsh7th/nvim-cmp"] = { + lazy = true, + event = "InsertEnter", + config = require("completion.cmp"), + dependencies = { + { + "L3MON4D3/LuaSnip", + build = "make install_jsregexp", + config = require("completion.luasnip"), + dependencies = { "rafamadriz/friendly-snippets" }, + }, + { "lukas-reineke/cmp-under-comparator" }, + { "saadparwaiz1/cmp_luasnip" }, + { "hrsh7th/cmp-nvim-lsp" }, + { "hrsh7th/cmp-nvim-lua" }, + { "andersevenrud/cmp-tmux" }, + { "hrsh7th/cmp-path" }, + { "f3fora/cmp-spell" }, + { "hrsh7th/cmp-buffer" }, + { "kdheepak/cmp-latex-symbols" }, + { "ray-x/cmp-treesitter", commit = "c8e3a74" }, + -- { "tzachar/cmp-tabnine", build = "./install.sh", config = require("completion.tabnine") }, + -- { + -- "jcdickinson/codeium.nvim", + -- dependencies = { + -- "nvim-lua/plenary.nvim", + -- "MunifTanjim/nui.nvim", + -- }, + -- config = require("completion.codeium"), + -- }, + }, +} +if use_copilot then + completion["zbirenbaum/copilot.lua"] = { + lazy = true, + cmd = "Copilot", + event = "InsertEnter", + config = require("completion.copilot"), + dependencies = { + { + "zbirenbaum/copilot-cmp", + config = require("completion.copilot-cmp"), + }, + }, + } +end + +return completion diff --git a/nvim/lua/modules/plugins/editor.lua b/nvim/lua/modules/plugins/editor.lua new file mode 100644 index 0000000..f396fc0 --- /dev/null +++ b/nvim/lua/modules/plugins/editor.lua @@ -0,0 +1,132 @@ +local editor = {} + +editor["olimorris/persisted.nvim"] = { + lazy = true, + cmd = { + "SessionToggle", + "SessionStart", + "SessionStop", + "SessionSave", + "SessionLoad", + "SessionLoadLast", + "SessionLoadFromFile", + "SessionDelete", + }, + config = require("editor.persisted"), +} +editor["m4xshen/autoclose.nvim"] = { + lazy = true, + event = "InsertEnter", + config = require("editor.autoclose"), +} +editor["LunarVim/bigfile.nvim"] = { + lazy = false, + config = require("editor.bigfile"), + cond = require("core.settings").load_big_files_faster, +} +editor["ojroques/nvim-bufdel"] = { + lazy = true, + cmd = { "BufDel", "BufDelAll", "BufDelOthers" }, +} +-- NOTE: `flash.nvim` is a powerful plugin that can be used as partial or complete replacements for: +-- > `hop.nvim`, +-- > `wilder.nvim` +-- > `nvim-treehopper` +-- Considering its steep learning curve as well as backward compatibility issues... +-- > We have no plan to remove the above plugins for the time being. +-- But as usual, you can always tweak the plugin to your liking. +editor["folke/flash.nvim"] = { + lazy = true, + event = { "CursorHold", "CursorHoldI" }, + config = require("editor.flash"), +} +editor["numToStr/Comment.nvim"] = { + lazy = true, + event = { "CursorHold", "CursorHoldI" }, + config = require("editor.comment"), +} +editor["sindrets/diffview.nvim"] = { + lazy = true, + cmd = { "DiffviewOpen", "DiffviewClose" }, + config = require("editor.diffview"), +} +editor["echasnovski/mini.align"] = { + lazy = true, + event = { "CursorHold", "CursorHoldI" }, + config = require("editor.align"), +} +editor["smoka7/hop.nvim"] = { + lazy = true, + version = "*", + event = { "CursorHold", "CursorHoldI" }, + config = require("editor.hop"), +} +editor["tzachar/local-highlight.nvim"] = { + lazy = true, + event = { "BufReadPost", "BufAdd", "BufNewFile" }, + config = require("editor.local-highlight"), +} +editor["brenoprata10/nvim-highlight-colors"] = { + lazy = true, + event = { "CursorHold", "CursorHoldI" }, + config = require("editor.highlight-colors"), +} +editor["romainl/vim-cool"] = { + lazy = true, + event = { "CursorMoved", "InsertEnter" }, +} +editor["lambdalisue/suda.vim"] = { + lazy = true, + cmd = { "SudaRead", "SudaWrite" }, + init = require("editor.suda"), +} +editor["tpope/vim-sleuth"] = { + lazy = true, + event = { "BufNewFile", "BufReadPost", "BufFilePost" }, +} +editor["nvim-pack/nvim-spectre"] = { + lazy = true, + cmd = "Spectre", +} +editor["mrjones2014/smart-splits.nvim"] = { + lazy = true, + event = { "CursorHoldI", "CursorHold" }, + config = require("editor.splits"), +} + +---------------------------------------------------------------------- +--  :treesitter related plugins -- +---------------------------------------------------------------------- +editor["nvim-treesitter/nvim-treesitter"] = { + lazy = true, + build = function() + if vim.fn.has("gui_running") == 1 then + vim.api.nvim_command([[TSUpdate]]) + end + end, + event = "BufReadPre", + config = require("editor.treesitter"), + dependencies = { + { "andymass/vim-matchup" }, + { "mfussenegger/nvim-treehopper" }, + { "nvim-treesitter/nvim-treesitter-textobjects" }, + { + "windwp/nvim-ts-autotag", + config = require("editor.autotag"), + }, + { + "hiphish/rainbow-delimiters.nvim", + config = require("editor.rainbow_delims"), + }, + { + "nvim-treesitter/nvim-treesitter-context", + config = require("editor.ts-context"), + }, + { + "JoosepAlviste/nvim-ts-context-commentstring", + config = require("editor.ts-context-commentstring"), + }, + }, +} + +return editor diff --git a/nvim/lua/modules/plugins/lang.lua b/nvim/lua/modules/plugins/lang.lua new file mode 100644 index 0000000..2cfc397 --- /dev/null +++ b/nvim/lua/modules/plugins/lang.lua @@ -0,0 +1,40 @@ +local lang = {} + +lang["kevinhwang91/nvim-bqf"] = { + lazy = true, + ft = "qf", + config = require("lang.bqf"), + dependencies = { + { "junegunn/fzf", build = ":call fzf#install()" }, + }, +} +lang["ray-x/go.nvim"] = { + lazy = true, + ft = { "go", "gomod", "gosum" }, + build = ":GoInstallBinaries", + config = require("lang.go"), + dependencies = { "ray-x/guihua.lua" }, +} +lang["mrcjkb/rustaceanvim"] = { + lazy = true, + ft = "rust", + version = "*", + init = require("lang.rust"), + dependencies = { "nvim-lua/plenary.nvim" }, +} +lang["Saecki/crates.nvim"] = { + lazy = true, + event = "BufReadPost Cargo.toml", + config = require("lang.crates"), + dependencies = { "nvim-lua/plenary.nvim" }, +} +lang["iamcco/markdown-preview.nvim"] = { + lazy = true, + ft = "markdown", + build = ":call mkdp#util#install()", +} +lang["chrisbra/csv.vim"] = { + lazy = true, + ft = "csv", +} +return lang diff --git a/nvim/lua/modules/plugins/tool.lua b/nvim/lua/modules/plugins/tool.lua new file mode 100644 index 0000000..37be1ed --- /dev/null +++ b/nvim/lua/modules/plugins/tool.lua @@ -0,0 +1,140 @@ +local tool = {} + +tool["tpope/vim-fugitive"] = { + lazy = true, + cmd = { "Git", "G" }, +} +-- This is specifically for fcitx5 users who code in languages other than English +-- tool["pysan3/fcitx5.nvim"] = { +-- lazy = true, +-- event = "BufReadPost", +-- cond = vim.fn.executable("fcitx5-remote") == 1, +-- config = require("tool.fcitx5"), +-- } +tool["Bekaboo/dropbar.nvim"] = { + lazy = false, + config = require("tool.dropbar"), + dependencies = { + "nvim-tree/nvim-web-devicons", + "nvim-telescope/telescope-fzf-native.nvim", + }, +} +tool["nvim-tree/nvim-tree.lua"] = { + lazy = true, + cmd = { + "NvimTreeToggle", + "NvimTreeOpen", + "NvimTreeFindFile", + "NvimTreeFindFileToggle", + "NvimTreeRefresh", + }, + config = require("tool.nvim-tree"), +} +tool["ibhagwan/smartyank.nvim"] = { + lazy = true, + event = "BufReadPost", + config = require("tool.smartyank"), +} +tool["michaelb/sniprun"] = { + lazy = true, + -- You need to cd to `~/.local/share/nvim/site/lazy/sniprun/` and execute `bash ./install.sh`, + -- if you encountered error about no executable sniprun found. + build = "bash ./install.sh", + cmd = { "SnipRun", "SnipReset", "SnipInfo" }, + config = require("tool.sniprun"), +} +tool["akinsho/toggleterm.nvim"] = { + lazy = true, + cmd = { + "ToggleTerm", + "ToggleTermSetName", + "ToggleTermToggleAll", + "ToggleTermSendVisualLines", + "ToggleTermSendCurrentLine", + "ToggleTermSendVisualSelection", + }, + config = require("tool.toggleterm"), +} +tool["folke/trouble.nvim"] = { + lazy = true, + cmd = { "Trouble", "TroubleToggle", "TroubleRefresh" }, + config = require("tool.trouble"), +} +tool["folke/which-key.nvim"] = { + lazy = true, + event = { "CursorHold", "CursorHoldI" }, + config = require("tool.which-key"), +} +tool["gelguy/wilder.nvim"] = { + lazy = true, + event = "CmdlineEnter", + config = require("tool.wilder"), + dependencies = { "romgrk/fzy-lua-native" }, +} + +---------------------------------------------------------------------- +-- Telescope Plugins -- +---------------------------------------------------------------------- +tool["nvim-telescope/telescope.nvim"] = { + lazy = true, + cmd = "Telescope", + config = require("tool.telescope"), + dependencies = { + { "nvim-lua/plenary.nvim" }, + { "nvim-tree/nvim-web-devicons" }, + { "jvgrootveld/telescope-zoxide" }, + { "debugloop/telescope-undo.nvim" }, + { "nvim-telescope/telescope-frecency.nvim" }, + { "nvim-telescope/telescope-live-grep-args.nvim" }, + { "nvim-telescope/telescope-fzf-native.nvim", build = "make" }, + { + "FabianWirth/search.nvim", + config = require("tool.search"), + }, + { + "ahmedkhalf/project.nvim", + event = { "CursorHold", "CursorHoldI" }, + config = require("tool.project"), + }, + { + "aaronhallaert/advanced-git-search.nvim", + cmd = { "AdvancedGitSearch" }, + dependencies = { + "tpope/vim-rhubarb", + "tpope/vim-fugitive", + "sindrets/diffview.nvim", + }, + }, + }, +} + +---------------------------------------------------------------------- +-- DAP Plugins -- +---------------------------------------------------------------------- +tool["mfussenegger/nvim-dap"] = { + lazy = true, + cmd = { + "DapSetLogLevel", + "DapShowLog", + "DapContinue", + "DapToggleBreakpoint", + "DapToggleRepl", + "DapStepOver", + "DapStepInto", + "DapStepOut", + "DapTerminate", + }, + config = require("tool.dap"), + dependencies = { + { + "rcarriga/nvim-dap-ui", + config = require("tool.dap.dapui"), + dependencies = { + "nvim-neotest/nvim-nio", + }, + }, + { "jay-babu/mason-nvim-dap.nvim" }, + }, +} + +return tool diff --git a/nvim/lua/modules/plugins/ui.lua b/nvim/lua/modules/plugins/ui.lua new file mode 100644 index 0000000..1a5aab4 --- /dev/null +++ b/nvim/lua/modules/plugins/ui.lua @@ -0,0 +1,71 @@ +local ui = {} + +ui["goolord/alpha-nvim"] = { + lazy = true, + event = "BufWinEnter", + config = require("ui.alpha"), +} +ui["akinsho/bufferline.nvim"] = { + lazy = true, + event = { "BufReadPost", "BufAdd", "BufNewFile" }, + config = require("ui.bufferline"), +} +ui["Jint-lzxy/nvim"] = { + lazy = false, + branch = "refactor/syntax-highlighting", + name = "catppuccin", + config = require("ui.catppuccin"), +} +ui["j-hui/fidget.nvim"] = { + lazy = true, + event = "LspAttach", + config = require("ui.fidget"), +} +ui["lewis6991/gitsigns.nvim"] = { + lazy = true, + event = { "CursorHold", "CursorHoldI" }, + config = require("ui.gitsigns"), +} +ui["lukas-reineke/indent-blankline.nvim"] = { + lazy = true, + event = { "CursorHold", "CursorHoldI" }, + config = require("ui.indent-blankline"), +} +ui["nvim-lualine/lualine.nvim"] = { + lazy = true, + event = { "BufReadPost", "BufAdd", "BufNewFile" }, + config = require("ui.lualine"), +} +ui["zbirenbaum/neodim"] = { + lazy = true, + event = "LspAttach", + config = require("ui.neodim"), +} +ui["karb94/neoscroll.nvim"] = { + lazy = true, + event = { "CursorHold", "CursorHoldI" }, + config = require("ui.neoscroll"), +} +ui["rcarriga/nvim-notify"] = { + lazy = true, + event = "VeryLazy", + config = require("ui.notify"), +} +ui["folke/paint.nvim"] = { + lazy = true, + event = { "CursorHold", "CursorHoldI" }, + config = require("ui.paint"), +} +ui["folke/todo-comments.nvim"] = { + lazy = true, + event = { "CursorHold", "CursorHoldI" }, + config = require("ui.todo"), + dependencies = { "nvim-lua/plenary.nvim" }, +} +ui["dstein64/nvim-scrollview"] = { + lazy = true, + event = { "BufReadPost", "BufAdd", "BufNewFile" }, + config = require("ui.scrollview"), +} + +return ui diff --git a/nvim/lua/modules/utils/dap.lua b/nvim/lua/modules/utils/dap.lua new file mode 100644 index 0000000..0ea276c --- /dev/null +++ b/nvim/lua/modules/utils/dap.lua @@ -0,0 +1,32 @@ +local M = {} + +function M.input_args() + local argument_string = vim.fn.input("Program arg(s) (enter nothing to leave it null): ") + return vim.fn.split(argument_string, " ", true) +end + +function M.input_exec_path() + return vim.fn.input('Path to executable (default to "a.out"): ', vim.fn.expand("%:p:h") .. "/a.out", "file") +end + +function M.input_file_path() + return vim.fn.input("Path to debuggee (default to the current file): ", vim.fn.expand("%:p"), "file") +end + +function M.get_env() + local variables = {} + for k, v in pairs(vim.fn.environ()) do + table.insert(variables, string.format("%s=%s", k, v)) + end + return variables +end + +return setmetatable({}, { + __index = function(_, key) + return function() + return function() + return M[key]() + end + end + end, +}) diff --git a/nvim/lua/modules/utils/icons.lua b/nvim/lua/modules/utils/icons.lua new file mode 100644 index 0000000..a050401 --- /dev/null +++ b/nvim/lua/modules/utils/icons.lua @@ -0,0 +1,240 @@ +local icons = {} + +local data = { + kind = { + Break = "󰙧", + Call = "󰃷", + Case = "󰬶", + Class = "󰠱", + Color = "󰏘", + Constant = "󰏿", + Constructor = "", + Continue = "󰞘", + Declaration = "󰙠", + Delete = "󱟁", + Enum = "", + EnumMember = "", + Event = "", + Field = "󰇽", + File = "󰈙", + Folder = "󰉋", + Fragment = "", + Function = "󰊕", + Implementation = "", + Interface = "", + Keyword = "󰌋", + List = "󰅪", + Loop = "󰑖", + Method = "󰆧", + Module = "", + Namespace = "󰌗", + Operator = "󰆕", + Package = "", + Property = "󰜢", + Reference = "", + Regex = "", + Snippet = "", + Statement = "󰅩", + Struct = "", + Switch = "", + Text = "󰉿", + TypeParameter = "󰅲", + Undefined = "", + Unit = "", + Value = "󰎠", + Variable = "", + -- ccls-specific icons + Macro = "", + Parameter = "", + StaticMethod = "", + Terminal = "", + TypeAlias = "", + }, + type = { + Array = "󰅪", + Boolean = "", + Null = "󰟢", + Number = "", + Object = "󰅩", + String = "󰉿", + }, + documents = { + Default = "", + File = "", + Files = "", + FileFind = "󰈞", + FileTree = "󰙅", + Import = "", + Symlink = "", + Word = "", + }, + git = { + Add = "", + Branch = "", + Diff = "", + Git = "󰊢", + Ignore = "", + Mod = "M", + Mod_alt = "", + Remove = "", + Rename = "", + Repo = "", + Unmerged = "󰘬", + Untracked = "󰞋", + Unstaged = "", + Staged = "", + Conflict = "", + }, + ui = { + Accepted = "", + ArrowClosed = "", + ArrowOpen = "", + BigCircle = "", + BigUnfilledCircle = "", + BookMark = "󰃃", + Buffer = "󰓩", + Bug = "", + Calendar = "", + Character = "", + Check = "󰄳", + ChevronRight = "", + Circle = "", + Close = "󰅖", + Close_alt = "", + CloudDownload = "", + CodeAction = "󰌵", + Comment = "󰅺", + Dashboard = "", + DoubleSeparator = "󰄾", + Emoji = "󰱫", + EmptyFolder = "", + EmptyFolderOpen = "", + File = "󰈤", + Fire = "", + Folder = "", + FolderOpen = "", + FolderWithHeart = "󱃪", + Gear = "", + History = "󰄉", + Incoming = "󰏷", + Indicator = "", + Keyboard = "", + Left = "", + List = "", + Lock = "󰍁", + Modified = "✥", + Modified_alt = "", + NewFile = "", + Newspaper = "", + Note = "󰍨", + Outgoing = "󰏻", + Package = "", + Pencil = "󰏫", + Perf = "󰅒", + Play = "", + Project = "", + Right = "", + RootFolderOpened = "", + Search = "󰍉", + Separator = "", + SignIn = "", + SignOut = "", + Sort = "", + Spell = "󰓆", + Square = "", + Symlink = "", + SymlinkFolder = "", + Tab = "", + Table = "", + Telescope = "", + Window = "", + }, + diagnostics = { + Error = "", + Warning = "", + Information = "", + Question = "", + Hint = "󰌵", + -- Hollow version + Error_alt = "󰅚", + Warning_alt = "󰀪", + Information_alt = "", + Question_alt = "", + Hint_alt = "󰌶", + }, + misc = { + Add = "+", + Added = "", + Campass = "󰀹", + Code = "", + Gavel = "", + Ghost = "󰊠", + Glass = "󰂖", + Lego = "", + LspAvailable = "󱜙", + ManUp = "", + Neovim = "", + NoActiveLsp = "󱚧", + PyEnv = "󰢩", + Squirrel = "", + Tag = "", + Tree = "", + Vbar = "│", + Vim = "", + Watch = "", + }, + cmp = { + buffer = "󰉿", + latex_symbols = "", + luasnip = "󰃐", + nvim_lsp = "", + nvim_lua = "", + orgmode = "", + path = "", + spell = "󰓆", + tmux = "", + treesitter = "", + undefined = "", + -- Add source-specific icons here + codeium = "", + Codeium = "", + copilot = "", + copilot_alt = "", + Copilot = "", + Copilot_alt = "", + TabNine = "", + cmp_tabnine = "", + }, + dap = { + Breakpoint = "󰝥", + BreakpointCondition = "󰟃", + BreakpointRejected = "", + LogPoint = "", + Pause = "", + Play = "", + RunLast = "↻", + StepBack = "", + StepInto = "󰆹", + StepOut = "󰆸", + StepOver = "󰆷", + Stopped = "", + Terminate = "󰝤", + }, +} + +---Get a specific icon set. +---@param category "kind"|"type"|"documents"|"git"|"ui"|"diagnostics"|"misc"|"cmp"|"dap" +---@param add_space? boolean @Add trailing whitespace after the icon. +function icons.get(category, add_space) + if add_space then + return setmetatable({}, { + __index = function(_, key) + return data[category][key] .. " " + end, + }) + else + return data[category] + end +end + +return icons diff --git a/nvim/lua/modules/utils/init.lua b/nvim/lua/modules/utils/init.lua new file mode 100644 index 0000000..c2c9fd5 --- /dev/null +++ b/nvim/lua/modules/utils/init.lua @@ -0,0 +1,370 @@ +local M = {} + +---@class palette +---@field rosewater string +---@field flamingo string +---@field mauve string +---@field pink string +---@field red string +---@field maroon string +---@field peach string +---@field yellow string +---@field green string +---@field sapphire string +---@field blue string +---@field sky string +---@field teal string +---@field lavender string +---@field text string +---@field subtext1 string +---@field subtext0 string +---@field overlay2 string +---@field overlay1 string +---@field overlay0 string +---@field surface2 string +---@field surface1 string +---@field surface0 string +---@field base string +---@field mantle string +---@field crust string +---@field none "NONE" + +---@type nil|palette +local palette = nil + +-- Indicates if autocmd for refreshing the builtin palette has already been registered +---@type boolean +local _has_autocmd = false + +---Initialize the palette +---@return palette +local function init_palette() + -- Reinitialize the palette on event `ColorScheme` + if not _has_autocmd then + _has_autocmd = true + vim.api.nvim_create_autocmd("ColorScheme", { + group = vim.api.nvim_create_augroup("__builtin_palette", { clear = true }), + pattern = "*", + callback = function() + palette = nil + init_palette() + -- Also refresh hard-coded hl groups + M.gen_alpha_hl() + M.gen_lspkind_hl() + pcall(vim.cmd.AlphaRedraw) + end, + }) + end + + if not palette then + palette = vim.g.colors_name:find("catppuccin") and require("catppuccin.palettes").get_palette() + or { + rosewater = "#DC8A78", + flamingo = "#DD7878", + mauve = "#CBA6F7", + pink = "#F5C2E7", + red = "#E95678", + maroon = "#B33076", + peach = "#FF8700", + yellow = "#F7BB3B", + green = "#AFD700", + sapphire = "#36D0E0", + blue = "#61AFEF", + sky = "#04A5E5", + teal = "#B5E8E0", + lavender = "#7287FD", + + text = "#F2F2BF", + subtext1 = "#BAC2DE", + subtext0 = "#A6ADC8", + overlay2 = "#C3BAC6", + overlay1 = "#988BA2", + overlay0 = "#6E6B6B", + surface2 = "#6E6C7E", + surface1 = "#575268", + surface0 = "#302D41", + + base = "#1D1536", + mantle = "#1C1C19", + crust = "#161320", + } + + palette = vim.tbl_extend("force", { none = "NONE" }, palette, require("core.settings").palette_overwrite) + end + + return palette +end + +---@param c string @The color in hexadecimal. +local function hex_to_rgb(c) + c = string.lower(c) + return { tonumber(c:sub(2, 3), 16), tonumber(c:sub(4, 5), 16), tonumber(c:sub(6, 7), 16) } +end + +-- NOTE: If the active colorscheme isn't `catppuccin`, this function won't overwrite existing definitions +---Sets a global highlight group. +---@param name string @Highlight group name, e.g. "ErrorMsg" +---@param foreground string @The foreground color +---@param background? string @The background color +---@param italic? boolean +local function set_global_hl(name, foreground, background, italic) + vim.api.nvim_set_hl(0, name, { + fg = foreground, + bg = background, + italic = italic == true, + default = not vim.g.colors_name:find("catppuccin"), + }) +end + +---Blend foreground with background +---@param foreground string @The foreground color +---@param background string @The background color to blend with +---@param alpha number|string @Number between 0 and 1 for blending amount. +function M.blend(foreground, background, alpha) + alpha = type(alpha) == "string" and (tonumber(alpha, 16) / 0xff) or alpha + local bg = hex_to_rgb(background) + local fg = hex_to_rgb(foreground) + + local blend_channel = function(i) + local ret = (alpha * fg[i] + ((1 - alpha) * bg[i])) + return math.floor(math.min(math.max(0, ret), 255) + 0.5) + end + + return string.format("#%02x%02x%02x", blend_channel(1), blend_channel(2), blend_channel(3)) +end + +---Get RGB highlight by highlight group +---@param hl_group string @Highlight group name +---@param use_bg boolean @Returns background or not +---@param fallback_hl? string @Fallback value if the hl group is not defined +---@return string +function M.hl_to_rgb(hl_group, use_bg, fallback_hl) + local hex = fallback_hl or "#000000" + local hlexists = pcall(vim.api.nvim_get_hl, 0, { name = hl_group, link = false }) + + if hlexists then + local result = vim.api.nvim_get_hl(0, { name = hl_group, link = false }) + if use_bg then + hex = result.bg and string.format("#%06x", result.bg) or "NONE" + else + hex = result.fg and string.format("#%06x", result.fg) or "NONE" + end + end + + return hex +end + +---Extend a highlight group +---@param name string @Target highlight group name +---@param def table @Attributes to be extended +function M.extend_hl(name, def) + local hlexists = pcall(vim.api.nvim_get_hl, 0, { name = name, link = false }) + if not hlexists then + -- Do nothing if highlight group not found + return + end + local current_def = vim.api.nvim_get_hl(0, { name = name, link = false }) + local combined_def = vim.tbl_deep_extend("force", current_def, def) + + vim.api.nvim_set_hl(0, name, combined_def) +end + +---Generate universal highlight groups +---@param overwrite palette? @The color to be overwritten | highest priority +---@return palette +function M.get_palette(overwrite) + if not overwrite then + return vim.deepcopy(init_palette()) + else + return vim.tbl_extend("force", init_palette(), overwrite) + end +end + +-- Generate highlight groups for lspsaga. Existing attributes will NOT be overwritten +function M.gen_lspkind_hl() + local colors = M.get_palette() + local dat = { + Class = colors.yellow, + Constant = colors.peach, + Constructor = colors.sapphire, + Enum = colors.yellow, + EnumMember = colors.teal, + Event = colors.yellow, + Field = colors.teal, + File = colors.rosewater, + Function = colors.blue, + Interface = colors.yellow, + Key = colors.red, + Method = colors.blue, + Module = colors.blue, + Namespace = colors.blue, + Number = colors.peach, + Operator = colors.sky, + Package = colors.blue, + Property = colors.teal, + Struct = colors.yellow, + TypeParameter = colors.blue, + Variable = colors.peach, + Array = colors.peach, + Boolean = colors.peach, + Null = colors.yellow, + Object = colors.yellow, + String = colors.green, + TypeAlias = colors.green, + Parameter = colors.blue, + StaticMethod = colors.peach, + Text = colors.green, + Snippet = colors.mauve, + Folder = colors.blue, + Unit = colors.green, + Value = colors.peach, + } + + for kind, color in pairs(dat) do + set_global_hl("LspKind" .. kind, color) + end +end + +-- Generate highlight groups for alpha. Existing attributes will NOT be overwritten +function M.gen_alpha_hl() + local colors = M.get_palette() + + set_global_hl("AlphaHeader", colors.blue) + set_global_hl("AlphaButtons", colors.green) + set_global_hl("AlphaShortcut", colors.pink, nil, true) + set_global_hl("AlphaFooter", colors.yellow) +end + +-- Generate blend_color for neodim. +function M.gen_neodim_blend_attr() + local trans_bg = require("core.settings").transparent_background + local appearance = require("core.settings").background + + if trans_bg and appearance == "dark" then + return "#000000" + elseif trans_bg and appearance == "light" then + return "#FFFFFF" + else + return M.hl_to_rgb("Normal", true) + end +end + +---Convert number (0/1) to boolean +---@param value number @The value to check +---@return boolean|nil @Returns nil if failed +function M.tobool(value) + if value == 0 then + return false + elseif value == 1 then + return true + else + vim.notify( + "Attempting to convert data of type '" .. type(value) .. "' [other than 0 or 1] to boolean", + vim.log.levels.ERROR, + { title = "[utils] Runtime Error" } + ) + return nil + end +end + +--- Function to recursively merge src into dst +--- Unlike vim.tbl_deep_extend(), this function extends if the original value is a list +---@paramm dst table @Table which will be modified and appended to +---@paramm src table @Table from which values will be inserted +---@return table @Modified table +local function tbl_recursive_merge(dst, src) + for key, value in pairs(src) do + if type(dst[key]) == "table" and type(value) == "function" then + dst[key] = value(dst[key]) + elseif type(dst[key]) == "table" and vim.islist(dst[key]) and key ~= "dashboard_image" then + vim.list_extend(dst[key], value) + elseif type(dst[key]) == "table" and type(value) == "table" and not vim.islist(dst[key]) then + tbl_recursive_merge(dst[key], value) + else + dst[key] = value + end + end + return dst +end + +-- Function to extend existing core configs (settings, events, etc.) +---@param config table @The default config to be merged with +---@param user_config string @The module name used to require user config +---@return table @Extended config +function M.extend_config(config, user_config) + local ok, extras = pcall(require, user_config) + if ok and type(extras) == "table" then + config = tbl_recursive_merge(config, extras) + end + return config +end + +---@param plugin_name string @Module name of the plugin (used to setup itself) +---@param opts nil|table @The default config to be merged with +---@param vim_plugin? boolean @If this plugin is written in vimscript or not +---@param setup_callback? function @Add new callback if the plugin needs unusual setup function +function M.load_plugin(plugin_name, opts, vim_plugin, setup_callback) + vim_plugin = vim_plugin or false + + -- Get the file name of the default config + local fname = debug.getinfo(2, "S").source:match("[^@/\\]*.lua$") + local ok, user_config = pcall(require, "user.configs." .. fname:sub(0, #fname - 4)) + if ok and vim_plugin then + if user_config == false then + -- Return early if the user explicitly requires disabling plugin setup + return + elseif type(user_config) == "function" then + -- OK, setup as instructed by the user + user_config() + else + vim.notify( + string.format( + "<%s> is not a typical Lua plugin, please return a function with\nthe corresponding options defined instead (usually via `vim.g.*`)", + plugin_name + ), + vim.log.levels.ERROR, + { title = "[utils] Runtime Error (User Config)" } + ) + end + elseif not vim_plugin then + if user_config == false then + -- Return early if the user explicitly requires disabling plugin setup + return + else + setup_callback = setup_callback or require(plugin_name).setup + -- User config exists? + if ok then + -- Extend base config if the returned user config is a table + if type(user_config) == "table" then + opts = tbl_recursive_merge(opts, user_config) + setup_callback(opts) + -- Replace base config if the returned user config is a function + elseif type(user_config) == "function" then + local user_opts = user_config(opts) + if type(user_opts) == "table" then + setup_callback(user_opts) + end + else + vim.notify( + string.format( + [[ +Please return a `table` if you want to override some of the default options OR a +`function` returning a `table` if you want to replace the default options completely. + +We received a `%s` for plugin <%s>.]], + type(user_config), + plugin_name + ), + vim.log.levels.ERROR, + { title = "[utils] Runtime Error (User Config)" } + ) + end + else + -- Nothing provided... Fallback as default setup of the plugin + setup_callback(opts) + end + end + end +end + +return M diff --git a/nvim/lua/modules/utils/keymap.lua b/nvim/lua/modules/utils/keymap.lua new file mode 100644 index 0000000..e249250 --- /dev/null +++ b/nvim/lua/modules/utils/keymap.lua @@ -0,0 +1,205 @@ +local M = {} + +---Shortcut for `nvim_replace_termcodes`. +---@param keys string +---@return string +local function termcodes(keys) + return vim.api.nvim_replace_termcodes(keys, true, true, true) +end + +---Returns if two key sequence are equal or not. +---@param a string +---@param b string +---@return boolean +local function keymap_equals(a, b) + return termcodes(a) == termcodes(b) +end + +---Get map +---@param mode string +---@param lhs string +---@return table +local function get_map(mode, lhs) + for _, map in ipairs(vim.api.nvim_buf_get_keymap(0, mode)) do + if keymap_equals(map.lhs, lhs) then + vim.api.nvim_buf_del_keymap(0, mode, lhs) + return { + lhs = map.lhs, + rhs = map.rhs or "", + expr = map.expr == 1, + callback = map.callback, + noremap = map.noremap == 1, + script = map.script == 1, + silent = map.silent == 1, + nowait = map.nowait == 1, + buffer = true, + } + end + end + + for _, map in ipairs(vim.api.nvim_get_keymap(mode)) do + if keymap_equals(map.lhs, lhs) then + vim.api.nvim_del_keymap(mode, lhs) + return { + lhs = map.lhs, + rhs = map.rhs or "", + expr = map.expr == 1, + callback = map.callback, + noremap = map.noremap == 1, + script = map.script == 1, + silent = map.silent == 1, + nowait = map.nowait == 1, + buffer = false, + } + end + end + + return { + lhs = lhs, + rhs = lhs, + expr = false, + callback = nil, + noremap = true, + script = false, + silent = true, + nowait = false, + buffer = false, + } +end + +---Returns the function constructed from the passed keymap object on call of +---which the original keymapping will be executed. +---@param map table keymap object +---@return function +local function get_fallback(map) + return function() + local keys, fmode + if map.expr then + if map.callback then + keys = map.callback() + else + keys = vim.api.nvim_eval(map.rhs) + end + elseif map.callback then + map.callback() + return + else + keys = map.rhs + end + keys = termcodes(keys) + fmode = map.noremap and "in" or "im" + vim.api.nvim_feedkeys(keys, fmode, false) + end +end + +-- Amends a mapping (i.e., allows fallback when certain conditions are met) +---@param cond string +---@param mode string +---@param lhs string +---@param rhs function +---@param opts? table +local function amend(cond, mode, lhs, rhs, opts) + local map = get_map(mode, lhs) + local fallback = get_fallback(map) + local options = vim.deepcopy(opts) or {} + options.desc = table.concat({ + "[" .. cond, + (options.desc and ": " .. options.desc or ""), + "]", + (map.desc and " / " .. map.desc or ""), + }) + vim.keymap.set(mode, lhs, function() + rhs(fallback) + end, options) +end + +-- Completely replace a mapping +---@param mode string +---@param lhs string +---@param rhs string +---@param opts? table +---@param buf? boolean|number +local function replace(mode, lhs, rhs, opts, buf) + get_map(mode, lhs) + + local options = vim.deepcopy(opts) or {} + if buf and type(buf) == "number" then + vim.api.nvim_buf_set_keymap(buf, mode, lhs, rhs, options) + else + vim.api.nvim_set_keymap(mode, lhs, rhs, options) + end +end + +---Amend the existing keymap. +---@param cond string +---@param mode string | string[] +---@param lhs string +---@param rhs function +---@param opts? table +local function modes_amend(cond, mode, lhs, rhs, opts) + if type(mode) == "table" then + for _, m in ipairs(mode) do + amend(cond, m, lhs, rhs, opts) + end + else + amend(cond, mode, lhs, rhs, opts) + end +end + +---Replace the existing keymap. +---@param mode string | string[] +---@param lhs string +---@param rhs string +---@param opts? table +---@param buf? boolean|number +local function modes_replace(mode, lhs, rhs, opts, buf) + if type(mode) == "table" then + for _, m in ipairs(mode) do + replace(m, lhs, rhs, opts, buf) + end + else + replace(mode, lhs, rhs, opts, buf) + end +end + +---Amend the existing keymap. +---@param cond string +---@param global_flag string +---@param mapping table +function M.amend(cond, global_flag, mapping) + for key, value in pairs(mapping) do + local modes, keymap = key:match("([^|]*)|?(.*)") + if type(value) == "table" then + local rhs = value.cmd + local options = value.options + modes_amend(cond, vim.split(modes, ""), keymap, function(fallback) + if _G[global_flag] then + local fmode = options.noremap and "in" or "im" + vim.api.nvim_feedkeys(termcodes(rhs), fmode, false) + else + fallback() + end + end, options) + end + end +end + +---Replace the existing keymap. +---@param mapping table +function M.replace(mapping) + for key, value in pairs(mapping) do + local modes, keymap = key:match("([^|]*)|?(.*)") + if type(value) == "table" then + local rhs = value.cmd + local options = value.options + local buffer = value.buffer + modes_replace(vim.split(modes, ""), keymap, rhs, options, buffer) + elseif value == "" or value == false then + for _, m in ipairs(vim.split(modes, "")) do + get_map(m, keymap) + end + end + end +end + +return M diff --git a/nvim/lua/user_template/configs/.gitkeep b/nvim/lua/user_template/configs/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/nvim/lua/user_template/configs/dap-clients/.gitkeep b/nvim/lua/user_template/configs/dap-clients/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/nvim/lua/user_template/configs/formatters/.gitkeep b/nvim/lua/user_template/configs/formatters/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/nvim/lua/user_template/configs/lsp-servers/.gitkeep b/nvim/lua/user_template/configs/lsp-servers/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/nvim/lua/user_template/event.lua b/nvim/lua/user_template/event.lua new file mode 100644 index 0000000..44bee88 --- /dev/null +++ b/nvim/lua/user_template/event.lua @@ -0,0 +1,8 @@ +local definitions = { + -- Example + bufs = { + { "BufWritePre", "COMMIT_EDITMSG", "setlocal noundofile" }, + }, +} + +return definitions diff --git a/nvim/lua/user_template/keymap/completion.lua b/nvim/lua/user_template/keymap/completion.lua new file mode 100644 index 0000000..1000f15 --- /dev/null +++ b/nvim/lua/user_template/keymap/completion.lua @@ -0,0 +1,16 @@ +local mappings = {} + +-- Place global keymaps here. +mappings["plug_map"] = {} + +-- NOTE: This function is special! Keymaps defined here are ONLY effective in buffers with LSP(s) attached +-- NOTE: Make sure to include `:with_buffer(buf)` to limit the scope of your mappings. +---@param buf number @The effective bufnr +mappings["lsp"] = function(buf) + return { + -- Example + ["n|K"] = require("keymap.bind").map_cr("Lspsaga hover_doc"):with_buffer(buf):with_desc("lsp: Show doc"), + } +end + +return mappings diff --git a/nvim/lua/user_template/keymap/core.lua b/nvim/lua/user_template/keymap/core.lua new file mode 100644 index 0000000..a564707 --- /dev/null +++ b/nvim/lua/user_template/keymap/core.lua @@ -0,0 +1 @@ +return {} diff --git a/nvim/lua/user_template/keymap/editor.lua b/nvim/lua/user_template/keymap/editor.lua new file mode 100644 index 0000000..a564707 --- /dev/null +++ b/nvim/lua/user_template/keymap/editor.lua @@ -0,0 +1 @@ +return {} diff --git a/nvim/lua/user_template/keymap/init.lua b/nvim/lua/user_template/keymap/init.lua new file mode 100644 index 0000000..f4a08e2 --- /dev/null +++ b/nvim/lua/user_template/keymap/init.lua @@ -0,0 +1,9 @@ +return vim.tbl_extend( + "force", + require("user.keymap.core"), + require("user.keymap.completion").plug_map, + require("user.keymap.editor"), + require("user.keymap.lang"), + require("user.keymap.tool"), + require("user.keymap.ui") +) diff --git a/nvim/lua/user_template/keymap/lang.lua b/nvim/lua/user_template/keymap/lang.lua new file mode 100644 index 0000000..a564707 --- /dev/null +++ b/nvim/lua/user_template/keymap/lang.lua @@ -0,0 +1 @@ +return {} diff --git a/nvim/lua/user_template/keymap/tool.lua b/nvim/lua/user_template/keymap/tool.lua new file mode 100644 index 0000000..a564707 --- /dev/null +++ b/nvim/lua/user_template/keymap/tool.lua @@ -0,0 +1 @@ +return {} diff --git a/nvim/lua/user_template/keymap/ui.lua b/nvim/lua/user_template/keymap/ui.lua new file mode 100644 index 0000000..a564707 --- /dev/null +++ b/nvim/lua/user_template/keymap/ui.lua @@ -0,0 +1 @@ +return {} diff --git a/nvim/lua/user_template/options.lua b/nvim/lua/user_template/options.lua new file mode 100644 index 0000000..df03ac5 --- /dev/null +++ b/nvim/lua/user_template/options.lua @@ -0,0 +1,6 @@ +local options = { + -- Example + autoindent = true, +} + +return options diff --git a/nvim/lua/user_template/plugins/.gitkeep b/nvim/lua/user_template/plugins/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/nvim/lua/user_template/settings.lua b/nvim/lua/user_template/settings.lua new file mode 100644 index 0000000..3e11eb9 --- /dev/null +++ b/nvim/lua/user_template/settings.lua @@ -0,0 +1,9 @@ +-- Please check `lua/core/settings.lua` to view the full list of configurable settings +local settings = {} + +-- Examples +settings["use_ssh"] = true + +settings["colorscheme"] = "catppuccin" + +return settings diff --git a/nvim/nixos/default.nix b/nvim/nixos/default.nix new file mode 100644 index 0000000..c0e5e05 --- /dev/null +++ b/nvim/nixos/default.nix @@ -0,0 +1,7 @@ +# NOTE: to add more language support, make a directory under `nixos`, followed by the language name and `default.nix`. See `dotnet/default.nix` for example. +{ + imports = [ + ./dotnet + ./neovim + ]; +} diff --git a/nvim/nixos/dotnet/default.nix b/nvim/nixos/dotnet/default.nix new file mode 100644 index 0000000..af671e1 --- /dev/null +++ b/nvim/nixos/dotnet/default.nix @@ -0,0 +1,43 @@ +# This module provides DOTNET_ROOT, with a different way to install dotnet locally. +# This module is modified from the NixOS module `programs.dotnet` + +{ config, lib, pkgs, ... }: +with lib; +let + cfg = config.programs.dotnet.dev; +in +{ + options = { + programs.dotnet.dev = { + enable = mkEnableOption "" // { + description = '' + Install the DotNet runtime and set the + {env}`DOTNET_ROOT` variable. + ''; + }; + environmentVariables = mkOption { + type = with types; lazyAttrsOf (oneOf [ str path int float ]); + default = { }; + example = { DOTNET_SYSTEM_GLOBALIZATION_INVARIANT = "0"; }; + description = '' + An attribute set an environment variable for DotNET. + ''; + }; + package = mkOption { + type = types.package; + default = pkgs.dotnet-sdk_7; + defaultText = literalExpression "pkgs.dotnet-sdk_7"; + description = "DotNET package to install."; + }; + }; + }; + + config = mkIf cfg.enable { + home.packages = [ cfg.package ]; + + # Please see https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-environment-variables#dotnet_root-dotnet_rootx86 + home.sessionVariables = { + DOTNET_ROOT = "${cfg.package}"; + } // cfg.environmentVariables; + }; +} diff --git a/nvim/nixos/neovim/default.nix b/nvim/nixos/neovim/default.nix new file mode 100644 index 0000000..420e7fa --- /dev/null +++ b/nvim/nixos/neovim/default.nix @@ -0,0 +1,232 @@ +# home-manager module of neovim setup +{ config +, lib +, pkgs +, ... +}: +let + cfg = config.programs.neovim.nvimdots; + inherit (lib) flip warn const; + inherit (lib.attrsets) optionalAttrs; + inherit (lib.lists) optionals; + inherit (lib.modules) mkIf; + inherit (lib.options) mkEnableOption mkOption literalExpression; + inherit (lib.strings) concatStringsSep versionOlder versionAtLeast; + inherit (lib.types) listOf coercedTo package functionTo; +in +{ + options = { + programs.neovim = { + nvimdots = { + enable = mkEnableOption '' + Activate "ayamir/nvimdots". + Have a look at https://github.com/ayamir/nvimdots for details + ''; + bindLazyLock = mkEnableOption '' + Bind lazy-lock.json in your repository to $XDG_CONFIG_HOME/nvim. + Very powerful in terms of keeping the environment consistent, but has the following side effects. + You cannot update it even if you run the Lazy command, because it binds read-only. + You need to remove lazy-lock.json before enabling this option if `mergeLazyLock` is set. + ''; + mergeLazyLock = mkEnableOption '' + Merges the managed lazy-lock.json with the existing one under $XDG_CONFIG_HOME/nvim if its hash has changed on activation. + Upstream package version changes have high priority. + This means changes to lazy-lock.json in the config directory (likely due to installing package) will be preserved. + In other words, it achieves environment consistency while remaining adaptable to changes. + You need to unlink lazy-lock.json before enabling this option if `bindLazyLock` is set. + Please refer to the wiki for details on the behavior. + ''; + setBuildEnv = mkEnableOption '' + Sets environment variables that resolve build dependencies as required by `mason.nvim` and `nvim-treesitter` + Environment variables are only visible to `nvim` and have no effect on any parent sessions. + Required for NixOS. + ''; + withBuildTools = mkEnableOption '' + Include basic build tools like `gcc` and `pkg-config`. + Required for NixOS. + ''; + withHaskell = mkEnableOption '' + Enable the Haskell compiler. Set to `true` to + use Haskell plugins. + ''; + extraHaskellPackages = mkOption { + type = + let + fromType = listOf package; + in + coercedTo fromType + (flip warn const '' + Assigning a plain list to extraHaskellPackages is deprecated. + Please assign a function taking a package set as argument, so + extraHaskellPackages = [ pkgs.haskellPackages.xxx ]; + should become + extraHaskellPackages = ps: [ ps.xxx ]; + '') + (functionTo fromType); + default = _: [ ]; + defaultText = literalExpression "ps: [ ]"; + example = literalExpression "hsPkgs: with hsPkgs; [ mtl ]"; + description = '' + The extra Haskell packages required for your plugins to work. + This option accepts a function that takes a Haskell package set as an argument, + and selects the required Haskell packages from this package set. + See the example for more info. + ''; + }; + extraDependentPackages = mkOption { + type = listOf package; + default = [ ]; + example = literalExpression "[ pkgs.openssl ]"; + description = "Extra build depends to add `LIBRARY_PATH` and `CPATH`."; + }; + }; + }; + }; + config = + let + # Inspired from https://github.com/NixOS/nixpkgs/blob/nixos-unstable/nixos/modules/programs/nix-ld.nix + build-dependent-pkgs = builtins.filter (package: !package.meta.unsupported) [ + # manylinux + pkgs.acl + pkgs.attr + pkgs.bzip2 + pkgs.curl + pkgs.glibc + pkgs.libsodium + pkgs.libssh + pkgs.libxml2 + pkgs.openssl + pkgs.stdenv.cc.cc + pkgs.stdenv.cc.cc.lib + pkgs.systemd + pkgs.util-linux + pkgs.xz + pkgs.zlib + pkgs.zstd + # Packages not included in `nix-ld`'s NixOSModule + pkgs.glib + pkgs.libcxx + ] + ++ cfg.extraDependentPackages; + + neovim-build-deps = pkgs.buildEnv { + name = "neovim-build-deps"; + paths = build-dependent-pkgs; + extraOutputsToInstall = [ "dev" ]; + pathsToLink = [ "/lib" "/include" ]; + ignoreCollisions = true; + }; + + buildEnv = [ + ''CPATH=''${CPATH:+''${CPATH}:}${neovim-build-deps}/include'' + ''CPLUS_INCLUDE_PATH=''${CPLUS_INCLUDE_PATH:+''${CPLUS_INCLUDE_PATH}:}:${neovim-build-deps}/include/c++/v1'' + ''LD_LIBRARY_PATH=''${LD_LIBRARY_PATH:+''${LD_LIBRARY_PATH}:}${neovim-build-deps}/lib'' + ''LIBRARY_PATH=''${LIBRARY_PATH:+''${LIBRARY_PATH}:}${neovim-build-deps}/lib'' + ''NIX_LD_LIBRARY_PATH=''${NIX_LD_LIBRARY_PATH:+''${NIX_LD_LIBRARY_PATH}:}${neovim-build-deps}/lib'' + ''PKG_CONFIG_PATH=''${PKG_CONFIG_PATH:+''${PKG_CONFIG_PATH}:}${neovim-build-deps}/include/pkgconfig'' + ]; + in + mkIf cfg.enable { + assertions = [ + { + assertion = ! (cfg.bindLazyLock && cfg.mergeLazyLock); + message = "bindLazyLock and mergeLazyLock cannot be enabled at the same time."; + } + ]; + xdg.configFile = { + "nvim/init.lua".source = ../../init.lua; + "nvim/lua".source = ../../lua; + "nvim/snips".source = ../../snips; + "nvim/tutor".source = ../../tutor; + } // optionalAttrs cfg.bindLazyLock { + "nvim/lazy-lock.json".source = ../../lazy-lock.json; + } // optionalAttrs cfg.mergeLazyLock { + "nvim/lazy-lock.fixed.json" = { + source = ../../lazy-lock.json; + onChange = '' + if [ -f ${config.xdg.configHome}/nvim/lazy-lock.json ]; then + tmp=$(mktemp) + ${pkgs.jq}/bin/jq -r -s '.[0] * .[1]' ${config.xdg.configHome}/nvim/lazy-lock.json ${config.xdg.configFile."nvim/lazy-lock.fixed.json".source} > "''${tmp}" && mv "''${tmp}" ${config.xdg.configHome}/nvim/lazy-lock.json + else + ${pkgs.rsync}/bin/rsync --chmod 644 ${config.xdg.configFile."nvim/lazy-lock.fixed.json".source} ${config.xdg.configHome}/nvim/lazy-lock.json + fi + ''; + }; + }; + home = { + packages = [ + pkgs.ripgrep + ]; + shellAliases = optionalAttrs (cfg.setBuildEnv && (versionOlder config.home.stateVersion "24.05")) { + nvim = concatStringsSep " " buildEnv + " nvim"; + }; + }; + programs.neovim = { + enable = true; + + withNodeJs = true; + withPython3 = true; + + extraPackages = [ + # Dependent packages used by default plugins + pkgs.doq + pkgs.tree-sitter + ] + ++ optionals cfg.withBuildTools [ + pkgs.cargo + pkgs.clang + pkgs.cmake + pkgs.gcc + pkgs.gnumake + pkgs.go + pkgs.lua51Packages.luarocks + pkgs.ninja + pkgs.pkg-config + pkgs.yarn + ] + ++ optionals cfg.withHaskell [ + (pkgs.writeShellApplication { + name = "stack"; + text = '' + exec "${pkgs.stack}/bin/stack" "--extra-include-dirs=${config.home.profileDirectory}/lib/nvim-depends/include" "--extra-lib-dirs=${config.home.profileDirectory}/lib/nvim-depends/lib" "$@" + ''; + }) + (pkgs.haskellPackages.ghcWithPackages (ps: cfg.extraHaskellPackages ps)) + ]; + + extraPython3Packages = ps: with ps; [ + docformatter + isort + pynvim + ]; + } + // optionalAttrs (versionAtLeast config.home.stateVersion "24.05") { + extraWrapperArgs = optionals cfg.setBuildEnv [ + "--suffix" + "CPATH" + ":" + "${neovim-build-deps}/include" + "--suffix" + "CPLUS_INCLUDE_PATH" + ":" + "${neovim-build-deps}/include/c++/v1" + "--suffix" + "LD_LIBRARY_PATH" + ":" + "${neovim-build-deps}/lib" + "--suffix" + "LIBRARY_PATH" + ":" + "${neovim-build-deps}/lib" + "--suffix" + "PKG_CONFIG_PATH" + ":" + "${neovim-build-deps}/include/pkgconfig" + "--suffix" + "NIX_LD_LIBRARY_PATH" + ":" + "${neovim-build-deps}/lib" + ]; + }; + }; +} diff --git a/nvim/nixos/testEnv.nix b/nvim/nixos/testEnv.nix new file mode 100644 index 0000000..3fed9cf --- /dev/null +++ b/nvim/nixos/testEnv.nix @@ -0,0 +1,34 @@ +{ inputs, pkgs, ... }: +let + testSettings = { config, ... }: { + warnings = [ + "home-manager version: ${config.home.version.release}" + ]; + home = { + username = "hm-user"; + homeDirectory = "/home/hm-user"; + stateVersion = config.home.version.release; + }; + xdg.enable = true; + programs = { + home-manager.enable = true; + git.enable = true; + neovim = { + enable = true; + nvimdots = { + enable = true; + setBuildEnv = true; + withBuildTools = true; + withHaskell = true; + }; + }; + }; + }; +in +inputs.home-manager.lib.homeManagerConfiguration { + inherit pkgs; + modules = [ + ./default.nix + testSettings + ]; +} diff --git a/nvim/scripts/install.ps1 b/nvim/scripts/install.ps1 new file mode 100644 index 0000000..910abc8 --- /dev/null +++ b/nvim/scripts/install.ps1 @@ -0,0 +1,416 @@ +#Requires -Version 7.1 + +# We don't need return codes for "$(command)", only stdout is needed. +# Allow `func "$(command)"`, pipes, etc. + +Set-StrictMode -Version 3.0 + +$ErrorActionPreference = "Stop" # Exit when command fails + +# global-scope vars +$USE_SSH = $True +$REQUIRED_NVIM_VERSION = [version]'0.10.0' +$REQUIRED_NVIM_VERSION_LEGACY = [version]'0.9.0' + +# package mgr vars +$choco_package_matrix = @{ "gcc" = "mingw"; "git" = "git"; "nvim" = "neovim"; "make" = "make"; "sudo" = "psutils"; "node" = "nodejs"; "pip" = "python3"; "fzf" = "fzf"; "rg" = "ripgrep"; "go" = "go"; "curl" = "curl"; "wget" = "wget"; "tree-sitter" = "tree-sitter"; "ruby" = "ruby"; "rustc" = "rust-ms" } +$scoop_package_matrix = @{ "gcc" = "mingw"; "git" = "git"; "nvim" = "neovim"; "make" = "make"; "sudo" = "psutils"; "node" = "nodejs"; "pip" = "python"; "fzf" = "fzf"; "rg" = "ripgrep"; "go" = "go"; "curl" = "curl"; "wget" = "wget"; "tree-sitter" = "tree-sitter"; "ruby" = "ruby"; "rustc" = "rust" } +$installer_pkg_matrix = @{ "NodeJS" = "npm"; "Python" = "pip"; "Ruby" = "gem" } + +# env vars +$env:XDG_CONFIG_HOME ??= $env:LOCALAPPDATA +$env:CCPACK_MGR ??= 'unknown' +$env:CCLONE_ATTR ??= 'undef' +$env:CCLONE_BRANCH ??= 'main' +$env:CCLONE_BRANCH_LEGACY ??= '0.9' +$env:CCDEST_DIR ??= "$env:XDG_CONFIG_HOME\nvim" +$env:CCBACKUP_DIR = "$env:CCDEST_DIR" + "_backup-" + (Get-Date).ToUniversalTime().ToString("yyyyMMddTHHmmss") + +function _abort ([Parameter(Mandatory = $True)] [string]$Msg,[Parameter(Mandatory = $True)] [string]$Type,[Parameter(Mandatory = $False)] [string]$ExtMsg) { + if ($ExtMsg -ne $null) { + Write-Host $ExtMsg + } + Write-Error -Message "Error: $Msg" -Category $Type + exit 1 +} + +function _chomp ([Parameter(Mandatory = $True)] [string]$Str) { + return [string]::Join("\n",([string]::Join("\r",($Str.Split("`r"))).Split("`n"))) +} + +# Check if script is run with non-interactive mode, this is not allowed +# Returns $True if validation failed (i.e., in non-interactive mode) +function test_host { + $NonInteractive = [System.Environment]::GetCommandLineArgs() | Where-Object { $_ -like '-NonI*' } + if ([System.Environment]::UserInteractive -and -not $NonInteractive) { + return $False + } else { + return $True + } +} + +function info ([Parameter(Mandatory = $True)][ValidateNotNullOrEmpty()] [string]$Msg) { + Write-Host "==> " -ForegroundColor Blue -NoNewline; Write-Host $(_chomp -Str $Msg); +} + +function info_ext ([Parameter(Mandatory = $True)][ValidateNotNullOrEmpty()] [string]$Msg) { + Write-Host " $(_chomp -Str $Msg)" +} + +function warn ([Parameter(Mandatory = $True)][ValidateNotNullOrEmpty()] [string]$Msg) { + Write-Host "Warning:" -ForegroundColor Yellow -NoNewline; Write-Host " $(_chomp -Str $Msg)"; +} + +function warn_ext ([Parameter(Mandatory = $True)][ValidateNotNullOrEmpty()] [string]$Msg) { + Write-Host " $(_chomp -Str $Msg)" +} + +function safe_execute ([Parameter(Mandatory = $True)][ValidateNotNullOrEmpty()] [scriptblock]$WithCmd) { + try { + Invoke-Command -ErrorAction Stop -ScriptBlock $WithCmd + if (-not $?) { + throw # Also stop the script if cmd failed + } + } + catch { + _abort -Msg "Failed during: $WithCmd" -Type "InvalidResult" + } +} + +function wait_for_user { + Write-Host "" + Write-Host "Press " -NoNewline; Write-Host "RETURN" -ForegroundColor White -BackgroundColor DarkGray -NoNewline; Write-Host "/" -NoNewline; Write-Host "ENTER" -ForegroundColor White -BackgroundColor DarkGray -NoNewline; Write-Host " to continue or any other key to abort..."; + $ks = [System.Console]::ReadKey() + if ($ks.Key -ne 'Enter') { + Write-Host "" + _abort -Msg "Aborted." -Type "OperationStopped" + } +} + +function check_ssh { + info -Msg "Validating SSH connection..." + Invoke-Command -ErrorAction SilentlyContinue -ScriptBlock { ssh -T git@github.com *> $null } + if ($LastExitCode -ne 1) { + info -Msg "We'll use HTTPS to fetch and update plugins." + return $True + } else { + $_title = "Fetch Preferences" + $_message = "Do you prefer to use SSH to fetch and update plugins? (otherwise HTTPS)" + + $_opt_ssh = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes","Will use SSH to fetch and update plugins in the future" + $_opt_https = New-Object System.Management.Automation.Host.ChoiceDescription "&No","Will use HTTPS to fetch and update plugins in the future" + + $USR_CHOICE = $Host.ui.PromptForChoice($_title,$_message,[System.Management.Automation.Host.ChoiceDescription[]]($_opt_ssh,$_opt_https),0) + if ($USR_CHOICE -eq 0) { + return $False + } else { + return $True + } + } +} + +function check_clone_pref { + info -Msg "Checking 'git clone' preferences..." + + $_title = "'git clone' Preferences" + $_message = "Would you like to perform a shallow clone ('--depth=1')?" + + $_opt_yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes","Will append '--depth=1' to 'git clone' options" + $_opt_no = New-Object System.Management.Automation.Host.ChoiceDescription "&No","Do nothing" + + $USR_CHOICE = $Host.ui.PromptForChoice($_title,$_message,[System.Management.Automation.Host.ChoiceDescription[]]($_opt_yes,$_opt_no),0) + if ($USR_CHOICE -eq 0) { + $env:CCLONE_ATTR = '--depth=1' + } else { + $env:CCLONE_ATTR = '--progress' + } +} + +function check_in_path ([Parameter(Mandatory = $True)][ValidateNotNullOrEmpty()] [string]$WithName) { + if ((Get-Command $WithName -ErrorAction SilentlyContinue)) { + return $True + } else { + return $False + } +} + +function query_pack { + if ((check_in_path -WithName "scoop") -and (check_in_path -WithName "choco")) { + info -Msg " [Detected] Multiple package managers detected." + + $_title = "Package manager Preferences" + $_message = "Pick your favorite package manager" + + $_opt_scoop = New-Object System.Management.Automation.Host.ChoiceDescription "&Scoop","Will use 'scoop' to install dependencies" + $_opt_choco = New-Object System.Management.Automation.Host.ChoiceDescription "&Chocolatey","Will use 'choco' to install dependencies" + + $USR_CHOICE = $Host.ui.PromptForChoice($_title,$_message,[System.Management.Automation.Host.ChoiceDescription[]]($_opt_scoop,$_opt_choco),0) + if ($USR_CHOICE -eq 0) { + $env:CCPACK_MGR = 'scoop' + } else { + $env:CCPACK_MGR = 'choco' + } + } elseif ((check_in_path -WithName "scoop")) { + info -Msg " [Detected] We'll use 'Scoop' as the default package mgr." + $env:CCPACK_MGR = 'scoop' + } elseif ((check_in_path -WithName "choco")) { + info -Msg " [Detected] We'll use 'Chocolatey' as the default package mgr." + $env:CCPACK_MGR = 'choco' + } else { + _abort -Msg "Required executable not found." -Type "NotInstalled" -ExtMsg @' +You must install a modern package manager before installing this Nvim config. +Available choices are: + - Chocolatey + https://chocolatey.org/install#individual + ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ + - Scoop + https://scoop.sh/ + ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ +[INFO] "scoop" and "choco" are both either not installed, missing from PATH, or not executable. + +'@ + } +} + +function init_pack { + info -Msg "Initializing package manager preferences..." + if ($env:CCPACK_MGR -ne 'unknown') { + info -Msg '$env:CCPACK_MGR already defined. Validating...' + if (($env:CCPACK_MGR -eq 'choco') -and (check_in_path -WithName $env:CCPACK_MGR)) { + info -Msg "We'll use 'Chocolatey' as the default package mgr." + } elseif (($env:CCPACK_MGR -eq 'scoop') -and (check_in_path -WithName $env:CCPACK_MGR)) { + info -Msg "We'll use 'Scoop' as the default package mgr." + } else { + info -Msg "Validation failed. Fallback to query." + query_pack + } + } else { + query_pack + } +} + +function _install_exe ([Parameter(Mandatory = $True)][ValidateNotNullOrEmpty()] [string]$WithName) { + if ($env:CCPACK_MGR -eq 'choco') { + Write-Host "Attempting to install dependency [" -NoNewline; Write-Host $WithName -ForegroundColor Green -NoNewline; Write-Host "] with Chocolatey" + $_inst_name = $choco_package_matrix[$WithName] + safe_execute -WithCmd { choco install "$_inst_name" -y } + } + elseif ($env:CCPACK_MGR -eq 'scoop') { + Write-Host "Attempting to install dependency [" -NoNewline; Write-Host $WithName -ForegroundColor Green -NoNewline; Write-Host "] with Scoop" + $_inst_name = $scoop_package_matrix[$WithName] + safe_execute -WithCmd { scoop install "$_inst_name" } + } else { + _abort -Msg 'This function is invoked incorrectly - Invalid data: $env:CCPACK_MGR' -Type "InvalidOperation" + } +} + +function _install_nodejs_deps { + safe_execute -WithCmd { npm install --global neovim tree-sitter-cli } +} + +function _install_python_deps { + safe_execute -WithCmd { python -m pip install --user wheel } + safe_execute -WithCmd { python -m pip install --user pynvim } +} + +function _install_ruby_deps { + safe_execute -WithCmd { gem install neovim } + safe_execute -WithCmd { ridk install } +} + +function check_and_fetch_exec ([Parameter(Mandatory = $True)][ValidateNotNullOrEmpty()] [string]$PkgName) { + $_str = "Checking dependency: '$PkgName'" + " " * 15 + Write-Host $_str.substring(0,[System.Math]::Min(40,$_str.Length)) -NoNewline + + if (-not (check_in_path -WithName $PkgName)) { + Start-Sleep -Milliseconds 350 + Write-Host "Failed" -ForegroundColor Red + _install_exe -WithName $PkgName + } else { + Start-Sleep -Milliseconds 350 + Write-Host "Success" -ForegroundColor Green + } +} + +function confirm_dep_inst ([Parameter(Mandatory = $True)][ValidateNotNullOrEmpty()] [string]$PkgName) { + $_inst_name = $installer_pkg_matrix[$PkgName] + if (-not (check_in_path -WithName "$_inst_name")) { + _abort -Msg "This function is invoked incorrectly - The '$_inst_name' executable not found" -Type "InvalidOperation" + } else { + $_title = "Dependencies Installation" + $_message = "Would you like to check & install $PkgName dependencies?" + + $_opt_yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes","Will install $PkgName dependencies" + $_opt_no = New-Object System.Management.Automation.Host.ChoiceDescription "&No","Will SKIP installing $PkgName dependencies" + + $USR_CHOICE = $Host.ui.PromptForChoice($_title,$_message,[System.Management.Automation.Host.ChoiceDescription[]]($_opt_yes,$_opt_no),1) + if ($USR_CHOICE -eq 0) { + return $True + } else { + return $False + } + } +} + +function fetch_deps { + check_and_fetch_exec -PkgName "gcc" + check_and_fetch_exec -PkgName "git" + check_and_fetch_exec -PkgName "nvim" + check_and_fetch_exec -PkgName "make" + check_and_fetch_exec -PkgName "sudo" + check_and_fetch_exec -PkgName "node" + check_and_fetch_exec -PkgName "pip" + check_and_fetch_exec -PkgName "fzf" + check_and_fetch_exec -PkgName "rg" + check_and_fetch_exec -PkgName "ruby" + check_and_fetch_exec -PkgName "go" + check_and_fetch_exec -PkgName "curl" + check_and_fetch_exec -PkgName "wget" + check_and_fetch_exec -PkgName "rustc" + check_and_fetch_exec -PkgName "tree-sitter" + + # Reload PATH for future use + $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") +} + +function check_nvim_version ([Parameter(Mandatory = $True)][ValidateNotNullOrEmpty()] [version]$RequiredVersionMin) { + $nvim_version = Invoke-Command -ErrorAction SilentlyContinue -ScriptBlock { nvim --version } # First get neovim version + $nvim_version = $nvim_version.Split([System.Environment]::NewLine) | Select-Object -First 1 # Then do head -n1 + $nvim_version = $nvim_version.Split('-') | Select-Object -First 1 # Special for dev branches + $nvim_version = $nvim_version -replace '[^(\d+(\.\d+)*)]','' # Then do regex replacement similar to sed + + $nvim_version = [version]$nvim_version + return ($nvim_version -ge $RequiredVersionMin) +} + +function clone_repo ([Parameter(Mandatory = $True)][ValidateNotNullOrEmpty()] [string]$WithURL) { + if ((check_nvim_version -RequiredVersionMin $REQUIRED_NVIM_VERSION)) { + safe_execute -WithCmd { git clone --progress -b "$env:CCLONE_BRANCH" "$env:CCLONE_ATTR" $WithURL "$env:CCDEST_DIR" } + } elseif ((check_nvim_version -RequiredVersionMin $REQUIRED_NVIM_VERSION_LEGACY)) { + warn -Msg "You have outdated Nvim installed (< $REQUIRED_NVIM_VERSION)." + info -Msg "Automatically redirecting you to the latest compatible version..." + safe_execute -WithCmd { git clone --progress -b "$env:CCLONE_BRANCH_LEGACY" "$env:CCLONE_ATTR" $WithURL "$env:CCDEST_DIR" } + } else { + warn -Msg "You have outdated Nvim installed (< $REQUIRED_NVIM_VERSION_LEGACY)." + _abort -Msg "This Neovim distribution is no longer supported." -Type "NotImplemented" -ExtMsg @" +You have a legacy Neovim distribution installed. +Please make sure you have nvim v$REQUIRED_NVIM_VERSION_LEGACY installed at the very least. + +"@ + } +} + +function ring_bell { + [System.Console]::beep() +} + +function _main { + if (-not $IsWindows) { + _abort -Msg "This install script can only execute on Windows." -Type "DeviceError" + } + + if ((test_host)) { + _abort -Msg "This script cannot proceed in non-interactive mode." -Type "NotImplemented" + } + + info -Msg "Checking dependencies..." + + init_pack + fetch_deps + + if ((confirm_dep_inst -PkgName "NodeJS")) { + _install_nodejs_deps + } + if ((confirm_dep_inst -PkgName "Python")) { + _install_python_deps + } + if ((confirm_dep_inst -PkgName "Ruby")) { + _install_ruby_deps + } + + # Check dependencies + if (-not (check_in_path -WithName "nvim")) { + _abort -Msg "Required executable not found." -Type "NotInstalled" -ExtMsg @' +You must install Neovim before installing this Nvim config. See: + https://github.com/neovim/neovim/wiki/Installing-Neovim + ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ +[INFO] "nvim" is either not installed, missing from PATH, or not executable. + +'@ + } + + if (-not (check_in_path -WithName "git")) { + _abort -Msg "Required executable not found." -Type "NotInstalled" -ExtMsg @' +You must install Git before installing this Nvim config. See: + https://git-scm.com/ + ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ +[INFO] "git" is either not installed, missing from PATH, or not executable. + +'@ + } + + info -Msg "This script will install ayamir/nvimdots to:" + Write-Host $env:CCDEST_DIR + + if ((Test-Path $env:CCDEST_DIR)) { + warn -Msg "The destination folder: `"$env:CCDEST_DIR`" already exists." + warn_ext -Msg "We will make a backup for you at `"$env:CCBACKUP_DIR`"." + } + + ring_bell + wait_for_user + if ((check_ssh)) { + $USE_SSH = $False + } + check_clone_pref + + if ((Test-Path $env:CCDEST_DIR)) { + safe_execute -WithCmd { Move-Item -Path "$env:CCDEST_DIR" -Destination "$env:CCBACKUP_DIR" -Force } + } + + info -Msg "Fetching in progress..." + + if ($USE_SSH) { + clone_repo -WithURL 'git@github.com:ayamir/nvimdots.git' + } else { + clone_repo -WithURL 'https://github.com/ayamir/nvimdots.git' + } + + safe_execute -WithCmd { Set-Location -Path "$env:CCDEST_DIR" } + safe_execute -WithCmd { Copy-Item -Path "$env:CCDEST_DIR\lua\user_template\" -Destination "$env:CCDEST_DIR\lua\user" -Recurse -Force } + + if (-not $USE_SSH) { + info -Msg "Changing default fetching method to HTTPS..." + safe_execute -WithCmd { + (Get-Content "$env:CCDEST_DIR\lua\user\settings.lua") | + ForEach-Object { $_ -replace '\["use_ssh"\] = true','["use_ssh"] = false' } | + Set-Content "$env:CCDEST_DIR\lua\user\settings.lua" + } + } + + info -Msg "Spawning Neovim and fetching plugins... (You'll be redirected shortly)" + info -Msg 'Please make sure you have a Rust Toolchain installed via `rustup`! Otherwise, unexpected things may' + info_ext -Msg 'happen. See: https://www.rust-lang.org/tools/install.¯¯¯¯¯¯¯¯¯¯¯¯' + info_ext -Msg ' ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯' + info -Msg 'If lazy.nvim failed to fetch any plugin(s), maunally execute `:Lazy sync` until everything is up-to-date.' + Write-Host @' + +Thank you for using this set of configuration! +- Project Homepage: + https://github.com/ayamir/nvimdots + ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ +- Further documentation (including executables you |must| install for full functionality): + https://github.com/ayamir/nvimdots/wiki/Prerequisites + ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ +'@ + + ring_bell + wait_for_user + + safe_execute -WithCmd { nvim } + + # Exit the script + exit +} + +_main diff --git a/nvim/scripts/install.sh b/nvim/scripts/install.sh new file mode 100755 index 0000000..0d1e113 --- /dev/null +++ b/nvim/scripts/install.sh @@ -0,0 +1,304 @@ +#!/usr/bin/env bash + +# We don't need return codes for "$(command)", only stdout is needed. +# Allow `[[ -n "$(command)" ]]`, `func "$(command)"`, pipes, etc. +# shellcheck disable=SC2312 + +set -uo pipefail + +# global-scope vars +REQUIRED_NVIM_VERSION=0.10.0 +REQUIRED_NVIM_VERSION_LEGACY=0.9.0 +USE_SSH=1 +CLONE_ATTR=("--progress") +DEST_DIR="${HOME}/.config/nvim" +BACKUP_DIR="${DEST_DIR}_backup-$(date +%Y%m%dT%H%M%S)" + +abort() { + printf "%s\n" "$@" >&2 + exit 1 +} + +# Fail fast with a concise message when not using bash +# Single brackets are needed here for POSIX compatibility +# shellcheck disable=SC2292 +if [ -z "${BASH_VERSION:-}" ]; then + abort "Bash is required to interpret this script." +fi + +# Check if script is run with force-interactive mode in CI +if [[ -n "${CI-}" && -n "${INTERACTIVE-}" ]]; then + abort "Cannot run force-interactive mode in CI." +fi + +# String formatters +if [[ -t 1 ]]; then + tty_escape() { printf "\033[%sm" "$1"; } +else + tty_escape() { :; } +fi + +tty_mkbold() { tty_escape "1;$1"; } +tty_underline="$(tty_escape "4;39")" +tty_yellow="$(tty_escape "0;33")" +tty_blue="$(tty_mkbold 34)" +tty_red="$(tty_mkbold 31)" +tty_bold="$(tty_mkbold 39)" +tty_reset="$(tty_escape 0)" + +shell_join() { + local arg + printf "%s" "$1" + shift + for arg in "$@"; do + printf " " + printf "%s" "${arg// /\ }" + done +} + +execute() { + if ! "$@"; then + abort "$(printf "Failed during: %s" "$(shell_join "$@")")" + fi +} + +major_minor() { + echo "${1%%.*}.$( + x="${1#*.}" + echo "${x%%.*}" + )" +} + +chomp() { + printf "%s" "${1/"$'\n'"/}" +} + +info() { + printf "${tty_blue}==>${tty_bold} %s${tty_reset}\n" "$(shell_join "$@")" +} + +info_ext() { + printf "${tty_bold} %s${tty_reset}\n" "$(shell_join "$@")" +} + +warn() { + printf "${tty_yellow}Warning:${tty_reset} %s\n" "$(chomp "$1")" +} + +warn_ext() { + printf " %s\n" "$(chomp "$1")" +} + +getc() { + local save_state + save_state="$(/bin/stty -g)" + /bin/stty raw -echo + IFS='' read -r -n 1 -d '' "$@" + /bin/stty "${save_state}" +} + +ring_bell() { + # Use the shell's audible bell + if [[ -t 1 ]]; then + printf "\a" + fi +} + +wait_for_user() { + local c + printf "\n" + echo "Press ${tty_bold}RETURN${tty_reset}/${tty_bold}ENTER${tty_reset} to continue or any other key to abort..." + getc c + # we test for \r and \n because some stuff does \r instead + if ! [[ "$c" == $'\r' || "$c" == $'\n' ]]; then + abort "${tty_red}Aborted.${tty_reset}" + fi +} + +version_ge() { + [[ "${1%.*}" -gt "${2%.*}" ]] || [[ "${1%.*}" -eq "${2%.*}" && "${1#*.}" -ge "${2#*.}" ]] +} + +prompt_confirm() { + local choice + while true; do + read -r -p "$1 [Y/n]: " choice + case "$choice" in + [yY][eE][sS] | [yY]) + return 1 + ;; + [nN][oO] | [nN]) + return 0 + ;; + *) + if [[ -z "$choice" ]]; then + return 1 + fi + printf "${tty_red}%s\n\n${tty_reset}" "Input invalid! Please enter one of the following: '[y/yes]' or '[n/no]'." + ;; + esac + done +} + +check_ssh() { + info "Validating SSH connection..." + ssh -T git@github.com &>/dev/null + if ! [ $? -eq 1 ]; then + info "We'll use HTTPS to fetch and update plugins." + return 0 + else + prompt_confirm "Do you prefer to use SSH to fetch and update plugins? (otherwise HTTPS)" + return $? + fi +} + +clone_pref() { + info "Checking 'git clone' preferences..." + if ! prompt_confirm "Would you like to perform a shallow clone ('--depth=1')?"; then + CLONE_ATTR+=("--depth=1") + fi +} + +check_nvim_version() { + local nvim_version + nvim_version="$(nvim --version | head -n1 | sed -e 's|^[^0-9]*||' -e 's| .*||')" + if version_ge "$(major_minor "${nvim_version##* }")" "$(major_minor "$1")"; then + return 0 + else + return 1 + fi +} + +clone_repo() { + if check_nvim_version "${REQUIRED_NVIM_VERSION}"; then + execute "git" "clone" "-b" "main" "${CLONE_ATTR[@]}" "$1" "${DEST_DIR}" + elif check_nvim_version "${REQUIRED_NVIM_VERSION_LEGACY}"; then + warn "You have outdated Nvim installed (< ${REQUIRED_NVIM_VERSION})." + info "Automatically redirecting you to the latest compatible version..." + execute "git" "clone" "-b" "0.9" "${CLONE_ATTR[@]}" "$1" "${DEST_DIR}" + else + warn "You have outdated Nvim installed (< ${REQUIRED_NVIM_VERSION_LEGACY})." + abort "$( + cat </dev/null; then + abort "$( + cat </dev/null; then + abort "$( + cat </dev/null; then + abort "$( + cat <", + "", + "using namespace std;", + "", + "class Solution", + "{", + "public:", + "\t$1 $2($3) {", + "\t\t$4", + "\t}", + "};", + "", + "int main(int argc, char *argv[]) {", + "\tSolution s;", + "\treturn 0;", + "}" + ], + "description": "leetcode template" + }, + "hdef": { + "prefix": "hdef", + "body": [ + "#ifndef $1", + "#define $1", + "", + "#endif // $1" + ], + "description": "header file definitions" + } +} diff --git a/nvim/snips/snippets/go.json b/nvim/snips/snippets/go.json new file mode 100644 index 0000000..ec8bd55 --- /dev/null +++ b/nvim/snips/snippets/go.json @@ -0,0 +1,17 @@ +{ + "map declaration": { + "prefix": "map", + "body": "map[${1:type}]${2:type}", + "description": "Snippet for a map" + }, + "goroutine anonymous function": { + "prefix": "go", + "body": "go func($1) {\n\t$0\n}($2)", + "description": "Snippet for anonymous goroutine declaration" + }, + "goroutine function": { + "prefix": "gf", + "body": "go ${1:func}($0)", + "description": "Snippet for goroutine declaration" + } +} diff --git a/nvim/stylua.toml b/nvim/stylua.toml new file mode 100644 index 0000000..b1aeb38 --- /dev/null +++ b/nvim/stylua.toml @@ -0,0 +1,6 @@ +column_width = 120 +line_endings = "Unix" +indent_type = "Tabs" +indent_width = 4 +quote_style = "AutoPreferDouble" +call_parentheses = "Always" diff --git a/nvim/tutor/dots.tutor b/nvim/tutor/dots.tutor new file mode 100644 index 0000000..c4232b1 --- /dev/null +++ b/nvim/tutor/dots.tutor @@ -0,0 +1,157 @@ +# Welcome to the nvimdots Tutor + +Nvimdots is a [Neovim](https://neovim.io/) config suite designed for extensibility, performance, and ease +of use. It provides the ability to work with text files yet feels like you are +working within an IDE environment. + +This tutor will walk you through how to utilize our configuration to use neovim as a +powerful code editor. + +As a modern neovim config, it provides all the features you need: code completion, +(partial) code testing, tree-sitter, DAP, fuzzy find, and more. It comes with state- +of-the-art (SOTA) neovim plugins from the community to provide the best user +experience. + +The default []() key is ``{normal}. + +The approximate time required to complete the tutorial is 5 minutes, although the +exact duration may vary depending on the amount of time spent experimenting. + +# Lesson 1.1: EXPLANATION OF THE UI + +The opened buffers are shown at the top; you can use ``{normal} and ``{normal} to switch +between them. Also, ``{normal} (with `n`{normal} being any number between 1-9) can be used to +switch to the desired buffer directly. + +NOTE: macOS users may need to remap their `