diff --git a/.gitignore b/.gitignore index a58273c..cbdc659 100644 --- a/.gitignore +++ b/.gitignore @@ -13,5 +13,8 @@ tags contribute/doom-nvim-contrib contribute/local-share-nvim contribute/workspace + +# User modules +user/modules/* # Editor files .luarc.json diff --git a/README.md b/README.md index 768ba47..d62fab8 100644 --- a/README.md +++ b/README.md @@ -24,11 +24,11 @@ * [What is Doom Nvim?](#what-is-doom-nvim-) * [Install](#install) * [Configuring](#configuring) - + [`modules.lua`](#-moduleslua-) + + [Enabling features: `modules.lua`](#-moduleslua-) - [What is a module?](#what-is-a-module-) - [Enabing/disabling modules](#enabing-disabling-modules) - [All modules](#all-modules) - + [`config.lua`](#-configlua-) + + [Configuring and personalising: `config.lua`](#-configlua-) - [Modifying neovim and doom options](#modifying-neovim-and-doom-options) * [Adding plugins](#adding-plugins) * [Adding Keybinds](#adding-keybinds) @@ -44,15 +44,16 @@ Doom Nvim is a Neovim interpretation of the [doom-emacs](https://github.com/hlissner/doom-emacs) framework, adapted to Vim philosophy. -Its goal is to provide a configurable, extensible, performant and stable basis for any neovim configuration. +Our goal is to provide a configurable, extensible, performant and stable basis for any neovim configuration. Some of the defining features that make this project unique are: -- **Fast** Rapid startup time without defer_fn, packages are lazy loaded and languages are only configured when opening the file type. +- **Fast** Rapid startup time without defer_fn, packages are lazy loaded and languages are only configured when opening its relevent file type. - **Stable** Plugins are pinned to commit shas to avoid breaking between updates. +- **Scalable** Because of modular architecture you can disable any features you don't use. Your config is as simple or complex as you want it to be. - **Configurable** All modules are 100% overridable and configurable, use a logical structure and have LSP completions. +- **Extensible** With a simple api you can easily add, and or contribute, your own modules. - **Integrated** Desgined to handle and setup integrations between plugins for you. For example, whichkey will only show keybinds for modules you have enabled (and will automatically handle your custom bindings). -- **Extensible** Take advantage of the modular architecture, enable only the features you need, customise or add your own modules. ## Install @@ -62,7 +63,7 @@ TODO: Add install docs here Doom nvim is configured by enabling modules in the `modules.lua` file and then tweaking, overriding or adding new packages, keybinds and more within the `config.lua` module. -### `modules.lua` +### Enabling features: `modules.lua` #### What is a module? A module is a collection of packages, autocommands, keybinds and functions that add new capabilities or functionality to Doom Nvim. @@ -76,6 +77,7 @@ We organise modules into 3 categories: You can enable or disable a module by going to `modules.lua` (`Dm`) and commenting or uncommenting the entry. ```lua -- modules.lua + return { features = { 'lsp' @@ -92,22 +94,18 @@ return { #### All modules -Doom-nvim currently has 39 `features` modules and 14 `langs` modules. Some standout features are: -- `lsp` Code completions provided by `nvim-cmp` -- `linter` Formatting and linting provided by `null-ls.nvim` -- `whichkey` Interactive cheatsheet that integrates with our keybind management to **only show keybinds for modules you have active** -- `fidget` Shows LSP loading/indexing progress status - -You can find a full list of modules (here)[#TODO: ]. +Doom-nvim currently has 39 `features` modules and 14 `langs` modules. +You can find a full list of modules (here)[./docs/modules.md#all-modules] -### `config.lua` +### Configuring and personalising: `config.lua` #### Modifying neovim and doom options -Doom nvim provides a number of config options, including wrapping some of vim's own options. See all available config options (here)[TODO:]. +Doom nvim provides a number of config options, including wrapping some of vim's own options. See all available config options (in the API Reference)[./docs/api.md]. ```lua -- config.lua + doom.freeze_dependencies = false -- Don't use pinned packer dependencies doom.logging = 'trace' -- Debug doom internal issues doom.indent = 2 -- Sets vim.opt.shiftwith, vim.opt.softtabstop, vim.opt.tabstop to 2 @@ -123,6 +121,8 @@ Additional packages can be imported with the `doom.use()` function. This is a wrapper around `packer.use()` and provides the same API. [DOCS](https://github.com/wbthomason/packer.nvim#quickstart) ```lua +-- config.lua + -- Simple config doom.use('sainnhe/sonokai', 'EdenEast/nightfox.nvim') @@ -140,6 +140,8 @@ Additional keybinds can be defined with the `doom.bind()` function. This is a wrapper around a custom `nest.nvim` implementation and provides the same API. [DOCS](https://github.com/connorgmeehan/nest.nvim/tree/integrations-api#quickstart-guide) ```lua +-- config.lua + doom.bind({ { 'u', name = '+user', { -- Names this group in whichkey "+user" { 's', 'Telescope git_status', name = 'Git status' } -- Adds `us` keybind to trigger `Telescope git_status` @@ -154,6 +156,8 @@ doom.bind({ Additional autocommands can be defined with the `doom.autocmd()` function. ```lua +-- config.lua + doom.autcmd({ -- { "", "", ""} { "FileType", "javascript", function() print('Yuck!') end} @@ -168,6 +172,7 @@ Here you can override the plugin git sources, pre-defined settings, keybinds or ```lua -- modules.lua + return { features = { 'whichkey' -- Whichkey module is enabled @@ -212,10 +217,6 @@ whichkey.configs["which-key.nvim"] = function () end ``` -## FAQ - -TODO: Add FAQ - ## Contributing For for information please see our [contributing docs](./docs/contributing.md). diff --git a/docs/api.md b/docs/api.md new file mode 100644 index 0000000..3ce854a --- /dev/null +++ b/docs/api.md @@ -0,0 +1,3 @@ +# Doom API Reference + +TODO: Autogenerate from config object. diff --git a/docs/modules.md b/docs/modules.md index 0666fbb..194c7c5 100644 --- a/docs/modules.md +++ b/docs/modules.md @@ -64,7 +64,7 @@ Modules are grouped into 3 categories: - [`illuminate`](../lua/doom/modules/features/illuminate) Highlight other occurances of the hovered word - [`indentlines`](../lua/doom/modules/features/indentlines) Explicitly show indentation - [`range_highlight`](../lua/doom/modules/features/range_highlight) Highlight selected range as you type commands - - [`todo_comments`](../lua/doom/modules/features/todo_comments) Highlights TODO: comments and more + - [`todo_comments`](../lua/doom/modules/features/todo_comments) Highlights TODO comments and more - **UI modules** - [`fidget`](../lua/doom/modules/features/fidget) Shows LSP loading status - [`tabline`](../lua/doom/modules/features/tabline) Tabbed buffer switcher @@ -241,7 +241,7 @@ module.autocmds = function() end ``` -## Implementing your own doom module +## Building your own module I will use an example of implementing a module that counts the number of chars that you've typed. This module will: @@ -253,21 +253,15 @@ This module will: ### 1. Setting up -> Modules are loaded from the `lua/doom/modules/` folder. Within this folder there is a `features/`, `langs/` and `core/` directory. -> If you look at [`modules.lua`](../modules.lua) you'll see that this maps 1:1 with the returned data structure (except for `core` modules which can't be disabled). -> > Because modules are implemented as folders with an `init.lua` inside, they must be named after valid folder names. > Best practices are: > - Seperate words with an underscore, this is so the plugin can be represented as a lua variable > - Name the module after the functionality rather than the plugin it uses. -> -> If you're adding language support, add a new folder module to `lua/doom/modules/langs/`, else if -> it's a new feature add a directory to `lua/doom/module/features/`. -For our example of adding char counting plugin I will add a folder called `lua/doom/modules/langs/char_counter/` and create a new `init.lua` -inside of it. +For our example of adding char counting plugin I will create a folder called `lua/user/modules/char_counter/` +and create a new `init.lua` inside of it. ```lua --- lua/doom/modules/features/char_counter/init.lua +-- lua/user/modules/char_counter/init.lua local char_counter = {} return char_counter @@ -286,7 +280,7 @@ For our example we need to hook into the [InsertEnter](https://neovim.io/doc/use and [InsertLeave](https://neovim.io/doc/user/autocmd.html#InsertLeave) auto commands. ```lua --- lua/doom/modules/features/char_counter/init.lua +-- lua/user/modules/char_counter/init.lua char_counter.autocmds = { { "InsertEnter", "*", function () print('Entered insert mode') @@ -299,6 +293,8 @@ char_counter.autocmds = { ### 3. Enabling and testing your module + + Now you can enable the module in `modules.lua`! Once enabled, restart your doom-nvim instance and check `:messages` to see if it's printing correctly. @@ -306,8 +302,15 @@ Now you can enable the module in `modules.lua`! Once enabled, restart your doom -- modules.lua return { features = { - "char_counter" -- Must match the name of the folder i.e. `lua/doom/modules/features/char_counter` - } + ... + }, + langs = { + ... + }, + -- user field is optional and will read from the `lua/user/modules` folder in your `.nvim/` folder. + user = { + "char_counter" -- Must match the name of the folder i.e. `lua/user/modules/char_counter/init.lua` + }, } ``` @@ -324,7 +327,7 @@ We will also check if the [`buftype`](https://neovim.io/doc/user/options.html#'b means we wont count other interactive buffers like terminals, prompts or quick fix lists. ```lua --- lua/doom/modules/features/char_counter/init.lua +-- lua/user/modules/char_counter/init.lua local char_counter = {} @@ -371,7 +374,7 @@ Using the `module.cmds` property we can define and expose vim commands to the us `:CountPrint` and `:CountReset` command. ```lua --- lua/doom/modules/features/char_counter/init.lua +-- lua/user/modules/char_counter/init.lua char_counter.cmds = { { "CountPrint", function () @@ -390,12 +393,10 @@ Now restart doom nvim and run `:CountPrint` and `:CountReset` to test it out. ### 6. Adding keybinds -Keybinds are provided using the `module.binds` field. We use a modified [nest.nvim]() config that integrates with whichkey and nvim-mapper. -You can read more about it [here](https://github.com/connorgmeehan/nest.nvim/tree/integrations-api#quickstart-guide) but generally you should -provide the `name` field for all entries so it displays in whichkey. +Keybinds are provided using the `module.binds` field. We use a modified [nest.nvim]() config that integrates with whichkey and nvim-mapper. You can read more about it [here](https://github.com/connorgmeehan/nest.nvim/tree/integrations-api#quickstart-guide) but generally you should provide the `name` field for all entries so it displays in whichkey. ```lua --- lua/doom/modules/features/char_counter/init.lua +-- lua/user/modules/char_counter/init.lua char_counter.binds = { { 'i', name = '+info', { -- Adds a new `whichkey` folder called `+info` @@ -416,7 +417,7 @@ In this example I will add [nui.nvim](https://github.com/MunifTanjim/nui.nvim) t the user uses the `CountPrint` command. ```lua --- lua/doom/modules/features/char_counter/init.lua +-- lua/user/modules/char_counter/init.lua -- Add these two fields to `char_counter` at the top of the file. char_counter.uses = { @@ -486,7 +487,7 @@ object. This will allow users to tweak the config in their `config.lua` file wi ```lua --- lua/doom/modules/features/char_counter/init.lua +-- lua/user/modules/char_counter/init.lua -- Copy the settings that are passed to the `Popup` function, place them in `char_counter.settings.popup` char_counter.settings = { @@ -533,12 +534,30 @@ char_counter.cmds = { } ``` -### 9. You're done! Final output +### 9. Contributing your module upstream + +> Builtin modules are loaded from the `lua/doom/modules/` folder. Within this folder there is a `features/`, `langs/` and `core/` directory. +> If you look at [`modules.lua`](../modules.lua) you'll see that the table fields are used to lookup the subfolder. +```lua +return { + features = { + "lsp" -- Maps to `lua/doom/modules/features/lsp/`, + }, + langs = { + "lua" -- Maps to `lua/doom/modules/langs/lua/` + } +} +``` + +If you would like to contribute your module, just move it from `lua/user/modules/` to +`lua/user/modules//` and create a PR in accordance with our [Contributing Guidelines](./contributing.md). + +### 10. You're done! Final output If you'd just like to look at the end result, or if you're comparing why your implementation didn't work, here is the final working output. ```lua --- lua/doom/modules/features/char_counter/init.lua +-- lua/user/modules/char_counter/init.lua local char_counter = {} char_counter.settings = { diff --git a/lua/doom/core/config/init.lua b/lua/doom/core/config/init.lua index 6a573e3..117a458 100644 --- a/lua/doom/core/config/init.lua +++ b/lua/doom/core/config/init.lua @@ -239,7 +239,12 @@ config.load = function() for section_name, section_modules in pairs(all_modules) do for _, module_name in pairs(section_modules) do - local ok, result = xpcall(require, debug.traceback, ("doom.modules.%s.%s"):format(section_name, module_name)) + -- Special case for user folder, resolves to `lua/user/modules` + local root_folder = section_name == "user" + and "user.modules" + or ("doom.modules.%s"):format(section_name) + + local ok, result = xpcall(require, debug.traceback, ("%s.%s"):format(root_folder, module_name)) if ok then doom.modules[module_name] = result else diff --git a/lua/user/modules/char_counter/init.lua b/lua/user/modules/char_counter/init.lua new file mode 100644 index 0000000..9d9eebb --- /dev/null +++ b/lua/user/modules/char_counter/init.lua @@ -0,0 +1,97 @@ +local char_counter = {} + +char_counter.settings = { + popup = { + position = '50%', + size = { + width = 80, + height = 40, + }, + border = { + padding = { + top = 2, + bottom = 2, + left = 3, + right = 3, + }, + }, + style = "rounded", + enter = true, + buf_options = { + modifiable = true, + readonly = true, + } + } +} + +char_counter.uses = { + ["nui.nvim"] = { + "MunifTanjim/nui.nvim", + cmd = { "CountPrint" }, + } +} + +char_counter.configs = { + ["nui.nvim"] = function() + vim.notify("char_counter: nui.nvim loaded", "info") + end +} + +char_counter._insert_enter_char_count = nil +char_counter._accumulated_difference = 0 +char_counter._get_current_buffer_char_count = function() + local lines = vim.api.nvim_buf_line_count(0) + local chars = 0 + for _, line in ipairs(vim.api.nvim_buf_get_lines(0, 0, lines, false)) do + chars = chars + #line + end + return chars +end + +char_counter.autocmds = { + { "InsertEnter", "*", function () + -- Only operate on normal file buffers + print(("buftype: %s"):format(vim.bo.buftype)) + if vim.bo.buftype == "" then + -- Store current char count + char_counter._insert_enter_char_count = char_counter._get_current_buffer_char_count() + end + end}, + { "InsertLeave", "*", function () + -- Only operate on normal file buffers + if vim.bo.buftype == "" and char_counter._insert_enter_char_count then + -- Find the amount of chars added or removed + local new_count = char_counter._get_current_buffer_char_count() + local diff = new_count - char_counter._insert_enter_char_count + print(new_count, diff) + -- Add the difference to the accumulated total + char_counter._accumulated_difference = char_counter._accumulated_difference + diff + print(('Accumulated difference %s'):format(char_counter._accumulated_difference)) + end + end}, +} + +char_counter.cmds = { + { "CountPrint", function () + local Popup = require('nui.popup') + local popup = Popup(char_counter.settings.popup) + popup:mount() + popup:map("n", "", function() popup:unmount() end) + + local msg = ("char_counter: You have typed %s characters since I started counting."):format(char_counter._accumulated_difference) + vim.api.nvim_buf_set_lines(popup.bufnr, 0, 1, false, { msg }) + end}, + { "CountReset", function () + char_counter._accumulated_difference = 0 + vim.notify("char_counter: Reset count!", "info") + end} +} + +char_counter.binds = { + { 'i', name = '+info', { -- Adds a new `whichkey` folder called `+info` + { 'c', ':CountPrint', name = 'Print new chars' }, -- Binds `:CountPrint` to `ic` + { 'r', ':CountReset', name = 'Reset char count' } -- Binds `:CountPrint` to `ic` + } } +} + +return char_counter