Skip to content
lmdevvPublic

About

Markdown workspace tools for Neovim

Resources

Stars

2 stars

Watchers

0 watching

Forks

Repository files navigation

mdw.nvim

Markdown workspace tools for Neovim 0.11 and newer. Search notes by path, title, alias, and tag. Follow links, keep a sidebar, rename a note and its references, create notes and daily notes, and edit lists. Formatting and diagnostics are optional.

mdw maps no keys. Behavior details live in :help mdw.txt.

Requirements

Neovim 0.11 or newer. Setup still succeeds when an optional tool is missing.

Tool Used for
mini.pick, snacks.nvim, or telescope.nvim :mdw search, :mdw dailies, and the link chooser. Otherwise vim.ui.select
rumdl :mdw format and :mdw lint
wl-paste or xclip :mdw image on Linux. Other systems set edit.clipboard, for example { "pngpaste" }
render-markdown.nvim In-buffer rendering when render.enabled is true
markdown-oxide LSP when lsp.enabled is true
Obsidian CLI Preview vault notes with :mdw or :mdw preview obsidian, open with :mdw obsidian, and create notes when create.backend is "obsidian"
Trouble :mdw trouble

:checkhealth mdw reports which of these are available.

Installation

Call setup() after install. Until then the plugin adds no commands.

lazy.nvim

{
  "lmdevv/mdw.nvim",
  config = function()
    require("mdw").setup()
  end,
}

vim.pack

Neovim 0.12 and newer:

vim.pack.add({ "https://github.com/lmdevv/mdw.nvim" })
require("mdw").setup()

The command name is :Mdw. Typing :mdw is rewritten to :Mdw. Inside an Obsidian vault, :mdw opens the current saved note in Obsidian's Reading view. Outside a vault it shows command usage.

NixVim

inputs.mdw.url = "github:lmdevv/mdw.nvim";

programs.nixvim = {
  imports = [ inputs.mdw.nixvimModules.default ];

  plugins.mdw = {
    enable = true;
    settings = {
      search.picker = "auto";
    };
    extraPackages = [ pkgs.rumdl pkgs.git ];
  };
};

settings is passed to require("mdw").setup(). extraPackages is added to Neovim's PATH. A standalone NixVim configuration imports the same module through nixvim.lib.evalNixvim.

NixVim does not ship this plugin. Import nixvimModules.default from this flake. That module installs the plugin and calls setup().

Try it

With Nix, on x86_64-linux, aarch64-linux, or aarch64-darwin. From this repository:

nix run

From anywhere:

nix run github:lmdevv/mdw.nvim

This starts a separate Neovim in a writable copy of a walkthrough vault. It does not change your own configuration. The start screen has one action, Open the walkthrough. That note tells you the next command, then links to the next note with gd. Press space and the next key is listed. Inline images are drawn by snacks.image when the terminal supports Kitty graphics placeholders. The demo enables the Snacks picker, dashboard, and image module, plus mini.clue, a color scheme, rumdl, markdown-oxide, and render-markdown. Those are not turned on by installing mdw. Leader is space. These keys exist only in the demo:

Key Action
<leader>ms Search notes
<leader>sf Search files
<leader>mh Health
<leader>mb Sidebar
<leader>md Today's daily note
gd Follow the link under the cursor
<CR> in insert, o in normal Continue a list item
>> / << Nest or unnest
<leader>mt Toggle a checkbox
<leader>q Quit all without saving changes

What it does

Search. :mdw search matches path, filename, title, aliases, and tags. #parent matches that tag exactly and does not match parent/child. An empty query lists every note. Note bodies stay in your picker's grep. With search.enrich_files, file search in mini.pick, Snacks, and Telescope also matches title, alias, and tag while the directory is inside the workspace.

Links. gd follows the Markdown link or wikilink under the cursor. One match opens the note, at the heading or block when the link names one. Several matches open a chooser. :mdw sidebar opens with outline, backlinks, and outgoing links stacked in three panes. A legend at the bottom lists the keys: a restores all panes, o, b, and l show one view, Enter opens a result, and q closes the sidebar. :mdw backlinks, :mdw outgoing, and :mdw outline fill the quickfix. :mdw rename new/path.md previews references, updates them, and moves the file.

Notes. :mdw new path/note.md asks, then creates the note. :mdw daily opens today's YYYY-MM-DD note, or creates it when that file is missing. :mdw daily prev and :mdw daily next move among daily notes that already exist. :mdw dailies opens the note picker with only those daily notes.

When .obsidian/daily-notes.json or .obsidian/templates.json is present, unset daily and template options are filled from those files. Note creation uses the CLI when create.backend is "obsidian", or one command passes backend=obsidian. One template is used on its own. Several templates open a picker, including a blank note. template=Trip skips the picker. {{title}}, {{date}}, {{time}}, and {{date:YYYY-MM-DD}} are filled in.

Obsidian preview. :mdw, :mdw preview, and :mdw preview obsidian open the current note in the desktop app and select Reading view. :mdw obsidian opens it using the app's existing view mode. The nearest parent containing a .obsidian directory identifies the vault, including vaults nested inside a Git repository. Save the note with :write first; these commands do not save buffer changes. Obsidian reflects subsequent saves while the note is open.

Enable Settings → General → Command line interface in Obsidian 1.12.7 or newer, and open the folder as a vault in Obsidian at least once. Keep the desktop app running; some installations cannot launch it through the CLI. See the Obsidian CLI setup guide. mdw prefers obsidian-cli when that executable is available, otherwise obsidian. Set obsidian.command to a custom executable path if needed. Preview uses the CLI's open and eval commands; it requires no community plugin.

Browser preview. :mdw preview browser opens the latest saved note at md.luismario.me. Unsaved buffer changes are ignored, and the command never writes the file. The plugin reads bytes from disk, compresses a JSON snapshot with a bundled pure Lua DEFLATE implementation, and opens a base64url link. No Node.js, gzip executable, local server, or live connection is required.

The viewer has reading themes, an outline, math, Mermaid, code highlighting, editing, downloads, and document or passage sharing. QR sharing works when the link fits a QR code. The document is carried in the URL fragment, which the browser does not send in its HTTP request; anyone with the full link can read it. Media is outside this version. Links are snapshots, so later saves do not update them. Notes larger than 1 MiB or links longer than 60,000 characters are rejected.

Set preview.backend = "browser" to make :mdw preview and bare :mdw on a note use the browser. preview.url changes the viewer URL, for example "http://localhost:5184" for a local mdweb build. The default backend remains "obsidian". The web app lives separately in lmdevv/mdweb.

Editing. :mdw list continue, nest, unnest, and check edit the current list item. Bind them with lists.maps if you want keys. :mdw format and :mdw lint use rumdl. :mdw image saves a clipboard PNG under assets/ and inserts a Markdown image.

How it works

The workspace is the git toplevel of the file in the current window. A file outside git uses that file's directory. workspace.root pins one directory and skips discovery.

The index is Lua. It reads a frontmatter title, otherwise the first heading, otherwise the filename. Aliases come from aliases or alias. Tags come from tags or tag, plus inline #tags outside fenced code, inline code, and link destinations. A malformed note is skipped and reported. Unsaved buffer text wins over the file on disk. :mdw index rebuilds the workspace.

Dot-directories and node_modules are skipped. Notes are .md, .markdown, .mdc, .mdx, and .mkd.

:help mdw.txt has the ranking rules, the frontmatter subset, and the full command list.

Configuration

setup() replaces the options. Calling it again does not duplicate commands or autocmds. This is the default:

require("mdw").setup({
  workspace = {
    root = nil, -- git toplevel of the current file
  },
  search = {
    picker = "auto", -- mini, snacks, telescope, or select
    enrich_files = false,
  },
  create = {
    backend = "local", -- "obsidian" uses the Obsidian CLI
    default_template = nil,
  },
  daily = {
    folder = nil, -- filled from .obsidian/daily-notes.json when unset
    format = nil,
    template = nil,
  },
  lists = {
    maps = {
      continue = nil, -- insert mode
      open = nil, -- normal mode
      nest = nil,
      unnest = nil,
      check = nil,
    },
  },
  preview = {
    backend = "obsidian", -- or "browser"
    url = "https://md.luismario.me",
  },
  format = {
    lint = true,
    format_on_save = false,
  },
})

Set obsidian.import_daily to false to ignore .obsidian/daily-notes.json. Set navigation.gd to false to leave gd to the LSP. Set lsp.rename to true to leave rename on markdown-oxide.

Contributing

nix develop
make test

nix develop provides Neovim, git, and rumdl. Tests run headless. The nix run demo is for trying the plugin. Design notes are not part of the published tree.

License

MIT. See LICENSE. The bundled LibDeflate retains its zlib license.

About

Markdown workspace tools for Neovim

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages