Simple and improved Markdown note taking.
Mdnotes aims to be a lightweight plugin that improves the Markdown note-taking experience in Neovim, with minimal configuration required. It also exposes most of the functions used internally, so that the user can create a hyper-extensible note-taking experience similar to Neovim's philosophy.
It provides the typical Markdown features like inserting/editing inline links, ordered/unordered/task lists, generating ToC, table helpers, and formatting. The highlights are that it provides better WikiLink support, inserting/deleting assets, sequential Markdown buffer history, asset management, and link referencing/renaming. Please see the Features below for a descriptive list of features and their commands!
In the Recommendations section I've written some notes on my recommended mdnotes setup, and please see the Supported Markdown Format section to see how mdnotes aims to format your notes.
If you are migrating from another note-taking application, then MIGRATING.md might be of interest to you, and I've also written some useful tips in TIPS.md for when writing notes in OOTB Neovim. Lastly, a disclaimer I must unfortunately say, is if you are executing any mass data-altering commands, ensure you have a notes backup!
All documentation is available with :h mdnotes.txt. Execute :checkhealth mdnotes to ensure there are no problems with your plugin config.
All the features of mdnotes and their associated commands are listed and categorised below.
:Mdn inline_link open.:Mdn inline_link toggle which pastes your copied text over the selected text or word under cursor. This command also removes the inline link and saves it to be used later with the same command.:Mdn inline_link rename. :Mdn inline_link relink. :Mdn inline_link normalize to have consistent paths. :Mdn inline_link validate. This ensures that your inline link has a valid destination.:Mdn index and :Mdn journal.:Mdn history go_back and :Mdn history go_forward.:Mdn heading next/previous to easily navigate headings.:Mdn formatting strong/emphasis/inline_code/strikethrough/autolink_toggle.<CR>, o, and O and can be disabled.auto_list_renumber = true by default, can also be done manually).:Mdn formatting task_list_toggle. Also works with linewise visual mode to toggle multiple tasks at a time.:Mdn formatting unformat_lines.mdnotes integrates with Neovim to edit tables.ROW by COLS table with :Mdn table create ROW COLS.:Mdn table best_fit and can also add padding around your cells (table_best_fit_padding in config).:Mdn table column_insert_left/right.:Mdn table column_move_left/right.:Mdn table column_delete.:Mdn table column_duplicate.:Mdn table column_alignment_toggle.:Mdn table row_insert_above/below.:Mdn wikilink create.:Mdn wikilink follow.:Mdn wikilink rename_references. Also rename references of the current buffer when not hovering over a Wikilink.:Mdn wikilink show_references. Also show references of the current buffer when not hovering over a Wikilink.:Mdn wikilink undo_rename. Only available when prefer_lsp = false.:Mdn wikilink delete. :Mdn wikilink normalize. :Mdn wikilink show_orphans.:Mdn assets delete_unused to easily cleanup assets that you no longer use.:Mdn assets move_unused to move unused assets to a separate folder.:Mdn assets insert_image or :Mdn assets insert_file which creates the appropriate link and copies or moves the image to your assets folder. Requires xclip or wl-clipboard for Linux.:Mdn assets open_containing_folder. :Mdn assets download_website_html.:Mdn toc generate. Can also customise the depth of the ToC by changing the toc_depth = 4.:Mdn outliner_toggle. Make sure to exit afterwards by re-toggling. Can also use outliner-like indentation with :Mdn outliner indent/unindent.:Mdn journal insert_entry. :Mdn index open_containing_folder. prefer_lsp = true.Using the lazy.nvim package manager,
{
"ymich9963/mdnotes.nvim",
}
and specify your config using opts = {} or with a setup({}) function,
{
"ymich9963/mdnotes.nvim",
opts = {
-- Config here
}
-- or
config = {
require("mdnotes").setup({
-- Config here
})
}
}
{
index_file = "",
journal_file = "",
assets_path = "",
asset_insert_behaviour = "copy", -- "copy" or "move" files when inserting from clipboard
overwrite_behaviour = "error", -- "overwrite" or "error" when finding assset file conflicts
open_behaviour = "buffer", -- "buffer", "tab", "split", or "vsplit" to open when following links
date_format = "%a %d %b %Y" -- date format based on :h strftime()
prefer_lsp = false, -- to prefer LSP functions than the mdnotes functions
auto_list = true, -- automatic list continuation
auto_list_renumber = true, -- automatic renumbering of ordered lists
auto_table_best_fit = true, -- automatic table best fit
default_keymaps = false,
autocmds = true, -- enable or disable plugin autocmds
table_best_fit_padding = 0, -- add padding around cell contents when using tables_best_fit
toc_depth = 4 -- depth shown in the ToC
}
Sample directory structure for mdnotes is shown below. See RATIONALE.md for reasons regarding the accepted file structure.
notes/
ββββassets/
β ββββfire.png
β ββββwater.pdf
ββββmusic.md
ββββelectronics.md
etc.
This plugin was made with this type of directory structure in mind because this is how I use it. If this directory configuration doesn't suit you please make an issue and hopefully I'll be able to accomodate anyone's needs.
I've specified below some recommended plugins, keymaps, and optional settings for a great experience with mdnotes.
For the best Neovim Markdown note-taking experience, I've listed some other projects to optionally install alongside mdnotes,
markdown, markdown_inline, and latex parsers. The keymappings below can be enabled by setting default_keymaps = true as they are not enabled by default, and they will only be available in Markdown buffers. Place any mdnotes keymaps in a <Neovim config path>/after/ftplugin/markdown.lua file so that they're also Markdown specific. For organisation they use the <leader>m prefix.
vim.keymap.set('n', '<leader>mgx', ':Mdn inline_link open<CR>', { buffer = true, desc = "Open inline link URI under cursor" })
vim.keymap.set('n', '<leader>mgf', ':Mdn wikilink follow<CR>', { buffer = true, desc = "Open markdown file from WikiLink" })
vim.keymap.set('n', '<leader>mgrr', ':Mdn wikilink show_references<CR>', { buffer = true, desc = "Show references of link or buffer" })
vim.keymap.set('n', '<leader>mgrn', ':Mdn wikilink rename_references<CR>', { buffer = true, desc = "Rename references of link or current buffer" })
vim.keymap.set({"v", "n"}, "<leader>mk", ":Mdn inline_link toggle<CR>", { buffer = true, desc = "Toggle inline link" })
vim.keymap.set("n", "<leader>mh", ":Mdn history go_back<CR>", { buffer = true, desc = "Go to back to previously visited Markdown buffer" })
vim.keymap.set("n", "<leader>ml", ":Mdn history go_forward<CR>", { buffer = true, desc = "Go to next visited Markdown buffer" })
vim.keymap.set({"v", "n"}, "<leader>mb", ":Mdn formatting strong_toggle<CR>", { buffer = true, desc = "Toggle strong formatting" })
vim.keymap.set({"v", "n"}, "<leader>mi", ":Mdn formatting emphasis_toggle<CR>", { buffer = true, desc = "Toggle emphasis formatting" })
vim.keymap.set({"v", "n"}, "<leader>mt", ":Mdn formatting task_list_toggle<CR>", { buffer = true, desc = "Toggle task list status" })
vim.keymap.set("n", "<leader>mp", ":Mdn heading previous<CR>", { buffer = true, desc = "Go to previous Markdown heading" })
vim.keymap.set("n", "<leader>mn", ":Mdn heading next<CR>", { buffer = true, desc = "Go to next Markdown heading" })
Place these settings in your <Neovim config path>/after/ftplugin/markdown.lua file so that they are Markdown-specific. First one here is to enable wrapping only for the current Markdown buffer.
vim.wo[vim.api.nvim_get_current_win()][0].wrap = true -- Enable wrap for current .md buffer
Second one is to disable LSP diagnostics in the current Markdown buffer.
vim.diagnostic.enable(false, { bufnr = 0 }) -- Disable diagnostics for current .md buffer
Last one here is for the glorious Neovim Windows users. Setting this keymap will allow you to use the built in <C-x> <C-f> file completion for WikiLinks or just for using file paths in Markdown buffers.
vim.keymap.set("i", "<C-x><C-f>", "<cmd>set isfname-=[,]<CR><C-x><C-f><cmd>set isfname+=[,]<CR>",
{
desc = "Mdnotes i_CTRL-X_CTRL-F smart remap to allow path completion on Windows",
buffer = true
})
The main reason I started this project was dissatisfaction with Markdown LSPs at the time, and I really wanted to use Neovim as my notes editor. Therefore, mdnotes is designed to work with Markdown LSPs by trying to fill the gaps and to also complement their current functionality. Unfortunately, I don't think the Markdown LSPs are there yet, so the default behaviour of the plugin is to have prefer_lsp = false. Please see the table below for how mdnotes tries to work with LSPs and Neovim itself.
| Feature | mdnotes | LSP | Neovim |
|---|---|---|---|
| Showing references | Y (:Mdn wikilink show_references) |
Y (:h vim.lsp.buf.references() or grr) |
N |
| Rename links to current buffer | Y (:Mdn wikilink rename_references) |
Y (:h vim.lsp.buf.rename() or grn, markdown-oxide only) |
N |
| Rename links to hovered WikiLink | Y (:Mdn wikilink rename_references) |
? (:h vim.lsp.buf.rename(), should work but it does not) |
N |
| Buffer History | Y (Sequential :Mdn history go_back/forward) |
N | Y (Not Sequential :h bp/:h bn |
| Path Completion | N | Y (:h lsp-completion) |
Y (:h i_CTRL-X_CTRL-F) |
| Opening WikiLinks | Y (:Mdn wikilink follow) |
Y (:h vim.lsp.buf.definition() or CTRL-]) |
Y (:h gf, needs .md extension in link, requires settings for Windows) |
Note: Not all of the features of mdnotes are listed in this table, just the ones that are relevant to this section. Some LSPs provide more than just LSP features and their documentation should also be referenced along with this table.
mdnotes tries to complement Neovim functionality to make editing tables as easy as possible. See the table below for what functions Neovim does and what functions are done by mdnotes.
| Feature | mdnotes | Neovim |
|---|---|---|
| Insert empty rows | Y (:Mdn table row_insert_above/below) |
N |
| Duplicate row | N | Y (:h yy) |
| Delete row | N | Y (:h dd) |
| Move row | N | Y (:h dd and :h p) |
| Insert empty columns | Y (:Mdn table column_insert_left/right) |
N |
| Duplicate column | Y (:Mdn table column_duplicate) |
Y (:h visual-block) |
| Delete column | Y (:Mdn table column_delete) |
Y (:h visual-block) |
| Move column | Y (:Mdn table column_move_left/right) |
N |
Note: Not all of the features of mdnotes are listed in this table, just the ones that are relevant to this section.
Here is the supported Markdown formatting for mdnotes.nvim. The plugin tries to adhere to the CommonMark and GitHub Flavoured Markdown (GFM) spec as well as providing WikiLink support. If any problems arise please don't hesitate to create an issue for it!
Opened with :Mdn inline_link open. Inserted with the :Mdn assets insert_file/image and :Mdn inline_link toggle commands. If no extension is given to file below, it is treated as .md.
[link](www.neovim.io)
[link](path/to/file#fragment)
[link](path/to/file#gfm-style-fragment-wth-spaces)
[link](<path/to/file with spaces.md#fragment>)
[link](#Original Section)
[link](#original-fragment)
[link](path/to/file.extension)

Opened with :Mdn wikilink follow. Can only be filenames, so link can also be link.md.
[[link]]
[[link#fragment]]
[[link#fragment with spaces]]
[[link#fragment-with-spaces]]
Toggled with :Mdn formatting <format>_toggle. Using _ for the strong and emphasis formats needs to be specified in the strong_format and emphasis_format config options.
**strong**
__strong__
*emphasis*
_emphasis_
~~strikethrough~~
`inline code`
<autolink>
All ordered and unordered CommonMark lists along with GFM task lists are supported.
- Item
+ Item
* Item
1) Item
2. Item
- [x] Task lists with all ordered and unordered lists above
The GFM table specification is supported.
|1r1c|1r2c|1r3c|
|----|----|----|
|2r1c|2r2c|2r3c|
|3r1c|3r2c|3r3c|
I wanted to make a more Neovim-centric Markdown notes plugin that tries to work the available Markdown LSPs, is command/subcommand focused, concise, adheres to the CommonMark and GFM specs, while also providing the more widespread WikiLink support other note-taking apps provide. I hope I did in fact accomplish this for you as well as for me and if I have not then please create an issue! Thanks for reading this :).
Using mini.test for testing. For this project, if you want to run the tests then you need to install mini.test as a plugin locally. This was done to minimise dependencies in the repo. If you're not using lazy then you need to specify the mini.test location, using the mini_path variable in scripts/minimal_init.lua. To run the tests execute the following command in the project root,
nvim --headless --noplugin -u ./scripts/minimal_init.lua -c "lua MiniTest.run()"