Browse Source

feat: Finished module.md docs overhaul

my-config
connorgmeean 3 years ago
parent
commit
adccda6649
  1. 468
      docs/modules.md

468
docs/modules.md

@ -1,5 +1,30 @@
# Doom Nvim Modules
- [Doom Nvim Modules](#doom-nvim-modules)
* [Introduction](#introduction)
* [All modules](#all-modules)
+ [`core` modules](#-core--modules)
+ [`features` modules](#-features--modules)
+ [`langs` modules](#-langs--modules)
* [Configuring modules](#configuring-modules)
+ [Quick Guide](#quick-guide)
+ [Advanced guide](#advanced-guide)
- [Module lifecycle](#module-lifecycle)
- [Limitations](#limitations)
* [Direct plugin access](#direct-plugin-access)
* [Conditional keybinds and autocommands](#conditional-keybinds-and-autocommands)
- [Module spec](#module-spec)
* [Implementing your own doom module](#implementing-your-own-doom-module)
+ [1. Setting up](#1-setting-up)
+ [2. Adding autocommands](#2-adding-autocommands)
+ [3. Enabling and testing your module](#3-enabling-and-testing-your-module)
+ [4. Adding the character counter](#4-adding-the-character-counter)
+ [5. Adding commands to get and reset the count](#5-adding-commands-to-get-and-reset-the-count)
+ [6. Adding keybinds](#6-adding-keybinds)
+ [7. Adding and lazyloading a plugin](#7-adding-and-lazyloading-a-plugin)
+ [8. Exposing settings to the user](#8-exposing-settings-to-the-user)
+ [9. You're done! Final output](#9-you-re-done---final-output)
## Introduction
Doom Nvim consists of around 40 plugins and growing.
@ -106,16 +131,50 @@ comment_module.binds = {
> **NOTE:** If you have the `lua` language module and `lsp` feature module enabled,
all of these properties should be auto completeable.
### Advanced
### Advanced guide
<!-- TODO: Proof-read and finalise this section -->
#### Module lifecycle
1. Doom nvim reads `modules.lua` to determine which modules to load.
2. Doom loads the config for each module, saves it to `doom.modules` global object.
3. User can override the settings for a module in `config.lua` using the `doom.modules` global object.
4. Doom executes the modules, installing plugins and setting keybinds/autocommands.
#### Limitations
#### Module lifecycle
1. Modules are defined in `modules.lua`.
2. Doom loads the config for each module, saves it to `doom.modules`.
3. User can override the settings for a module in `config.lua`
4. Doom executes the module, loads and installs the dependencies, sets the keybinds and autocommands.
##### Direct plugin access
The main limitation is that plugins are not yet loaded when `config.lua` is executed meaning you
cant have direct access to plugins. We're working on a more concrete solution but for now you can
splice your own custom configure function with the default one.
```lua
--- config.lua
local lsp = doom.modules.lsp
local old_nvim_cmp_config_function = lsp.configs['nvim-cmp']
lsp.configs['nvim-cmp'] = function()
old_nvim_cmp_config_function() -- Run the default config
local cmp = require("cmp") -- direct access to plugin
end
```
##### Conditional keybinds and autocommands
Sometimes keybinds and autocommands will be conditionally added/disabled depending on if another module
is enabled or if a specific config option is set. In this case we use a function that returns a valid config
instead of a simple table.
This is not ideal as it's harder to `vim.inspect` the defaults. As a workaround you can `vim.inspect` the returned value.
```lua
-- config.lua
print(type(doom.modules.lsp.binds)) -- "table" (not conditional)
print(vim.inspect(doom.modules.lsp.binds)) -- Shows config
print(type(doom.modules.telescope.binds)) -- "function" (adds extra keybinds if "lsp" module is enabled)
print(vim.inspect(doom.modules.telescope.binds())) -- You must execute the function to see the config.
```
#### Module spec
@ -181,3 +240,400 @@ module.autocmds = function()
return autocmds
end
```
## Implementing your own doom module
I will use an example of implementing a module that counts the number of chars that you've typed.
This module will:
- Use autocommands to count the number of chars in a buffer when you enter insert mode vs when you leave insert mode
- Add it to an accumulated sum
- Provide keybinds + commands to restart or display total count
- Use a plugin to display the results in a popup window
- Include some settings to change the displayed output
### 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.
```lua
-- lua/doom/modules/features/char_counter/init.lua
local char_counter = {}
return char_counter
```
### 2. Adding autocommands
> Autocommands are set using the `module.autocmds` field. And follow the structure of
> ```lua
> module.autocmds = {
> { "{event}", "{aupat}", "command or function" }
> }
> ```
For our example we need to hook into the [InsertEnter](https://neovim.io/doc/user/autocmd.html#InsertEnter)
and [InsertLeave](https://neovim.io/doc/user/autocmd.html#InsertLeave) auto commands.
```lua
-- lua/doom/modules/features/char_counter/init.lua
char_counter.autocmds = {
{ "InsertEnter", "*", function ()
print('Entered insert mode')
end},
{ "InsertLeave", "*", function ()
print('Exited insert mode')
end},
}
```
### 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.
```lua
-- modules.lua
return {
features = {
"char_counter" -- Must match the name of the folder i.e. `lua/doom/modules/features/char_counter`
}
}
```
### 4. Adding the character counter
Because modules are just tables, you can add any properties or functions that you need to the module table.
To implement the character counter we will add a few fields to the module table. Unless you want users to
access these fields we recommend prefixing them with an underscore.
1. A function that gets the character count of a buffer.
2. A field to store the character count when we enter insert mode.
3. A field to store the accumulated count when we exit insert mode.
We will also check if the [`buftype`](https://neovim.io/doc/user/options.html#'buftype') is empty, this
means we wont count other interactive buffers like terminals, prompts or quick fix lists.
```lua
-- lua/doom/modules/features/char_counter/init.lua
local char_counter = {}
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},
}
return char_counter
```
### 5. Adding commands to get and reset the count
Using the `module.cmds` property we can define and expose vim commands to the user. Here we will define a
`:CountPrint` and `:CountReset` command.
```lua
-- lua/doom/modules/features/char_counter/init.lua
char_counter.cmds = {
{ "CountPrint", function ()
local msg = ("char_counter: You have typed %s characters since I started counting."):format(char_counter._accumulated_difference)
vim.notify(msg, "info")
end},
{ "CountReset", function ()
char_counter._accumulated_difference = 0
vim.notify("char_counter: Reset count!", "info")
end}
}
```
> **NOTE**: Instead of using a function you can also provide a string that will be executed using `vim.cmd`
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.
```lua
-- lua/doom/modules/features/char_counter/init.lua
char_counter.binds = {
{ '<leader>i', name = '+info', { -- Adds a new `whichkey` folder called `+info`
{ 'c', '<cmd>:CountPrint<CR>', name = 'Print new chars' }, -- Binds `:CountPrint` to `<leader>ic`
{ 'r', '<cmd>:CountReset<CR>', name = 'Reset char count' } -- Binds `:CountPrint` to `<leader>ic`
} }
}
```
> **NOTE**: Instead of a cmd you can also provide a lua function that will be executed when the keybind is triggered.
### 7. Adding and lazyloading a plugin
Plugins are added using the `module.uses` field and are configured using the `module.configs` field.
We use the repository name as a key to connect the plugin to its config function.
The API for `module.uses` is passed to Packer nvim's use function. [DOCS](https://github.com/wbthomason/packer.nvim#specifying-plugins)
In this example I will add [nui.nvim](https://github.com/MunifTanjim/nui.nvim) to display the results in a popup when
the user uses the `CountPrint` command.
```lua
-- lua/doom/modules/features/char_counter/init.lua
-- Add these two fields to `char_counter` at the top of the file.
char_counter.uses = {
["nui.nvim"] = {
"MunifTanjim/nui.nvim",
cmd = { "CountPrint" } -- Here, nui.nvim wont be loaded until user does the `<leader>ic` or `:CountPrint` command.
}
}
char_counter.configs = {
["nui.nvim"] = function()
-- Log when nui loads so we can check that it's being lazy loaded correctly
vim.notify("char_counter: nui.nvim loaded", "info")
-- If your plugin requires a `.setup({ ... config ... })` function, this is where you'd execute it.
-- WARNING: Because of how Packer compiles plugin configs, this function does not have direct access to `char_counter` table.
-- Instead you must re-define it from the `doom` global object as `local char_counter` = doom.modules.char_counter
end
}
-- Modify `char_counter.cmds`
char_counter.cmds = {
{ "CountPrint", function ()
-- We can ensure that nui has loaded due to the `cmd = { "CountPrint" }` in the plugin's config
local Popup = require('nui.popup')
local popup = 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,
}
})
popup:mount()
popup:map("n", "<esc>", 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}
}
```
### 8. Exposing settings to the user
In order to keep doom-nvim flexible, it's best practice to expose settings for the module. A common practice is just to expose the entire config
object. This will allow users to tweak the config in their `config.lua` file without replacing and rewriting all of the logic for a small change.
```lua
-- lua/doom/modules/features/char_counter/init.lua
-- Copy the settings that are passed to the `Popup` function, place them in `char_counter.settings.popup`
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,
}
}
}
-- Modify the Popup function
char_counter.cmds = {
{ "CountPrint", function ()
local Popup = require('nui.popup')
local popup = Popup(char_counter.settings.popup) -- Configured via the `settings.popup` field.
popup:mount()
popup:map("n", "<esc>", 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}
}
```
### 9. 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
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", "<esc>", 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 = {
{ '<leader>i', name = '+info', { -- Adds a new `whichkey` folder called `+info`
{ 'c', '<cmd>:CountPrint<CR>', name = 'Print new chars' }, -- Binds `:CountPrint` to `<leader>ic`
{ 'r', '<cmd>:CountReset<CR>', name = 'Reset char count' } -- Binds `:CountPrint` to `<leader>ic`
} }
}
return char_counter
```

Loading…
Cancel
Save