Development setup
Build the plugin and run your checkout.
Requirements
- Rust 1.86 or newer
- Node.js 20.9 or newer with pnpm (or npx, which fetches pnpm)
- Neovim, and Vim 8.1+ (the tests run both)
Build
git clone https://github.com/sammaji/markdown-preview.nvim.git
cd markdown-preview.nvim
cargo build --releasecargo build also builds the page (app/) whenever a file in it changed, and
installs its dependencies first if app/node_modules is missing. The page is
embedded into the binary, so the binary is all the plugin needs at runtime. To
skip the page build and reuse the existing app/out, set
MKDP_SKIP_PAGE_BUILD=1.
Run your checkout
Load your checkout instead of the published plugin. With lazy.nvim:
{
dir = "~/path/to/markdown-preview.nvim",
name = "markdown-preview.nvim",
cmd = { "MarkdownPreviewToggle", "MarkdownPreview", "MarkdownPreviewStop" },
ft = { "markdown" },
build = "cargo build --release",
}Or without a plugin manager:
nvim --cmd 'set rtp^=~/path/to/markdown-preview.nvim' examples/features.mdA build in target/release is used before a downloaded binary.
:checkhealth mkdp shows which binary is used.
Making changes
| You changed | To see it |
|---|---|
src/ (the server) | cargo build --release, then :MarkdownPreviewStop and :MarkdownPreview |
app/ (the page) | the same: the page is embedded in the binary |
| Vim script or Lua | restart the editor |
Each editor keeps its server running, so an open preview uses the old binary until you stop it.
To work on the page with hot reload, run pnpm dev in app/. It serves the
page without a server, so it shows "Open this page with :MarkdownPreview";
this is useful for styling, less so for anything that needs a buffer.
examples/features.md uses every feature; preview it
to check a change by eye.
Working on the docs
This site is a Fumadocs app in docs/. The pages are
MDX files in docs/content/docs/, and meta.json files set the order of the
sidebar. Run it with hot reload:
cd docs
pnpm install
pnpm devand open http://localhost:3000. Link to other pages by their file, like
[Testing](./testing.mdx#writing-tests); the site turns these into page
links. In MDX, {, } and < outside code start JavaScript and JSX, so put
them in backticks. For notes, use <Callout> (type="info", "warn" or
"error") rather than GitHub's > [!NOTE].
pnpm build exports the static site to docs/out. The site is hosted on
Vercel at mkdp.sammaji.com, from the docs/
directory.
Checks
Run these before opening a pull request:
cargo fmt --check
cargo clippy --all-targets
cd app
pnpm typecheck
pnpm buildand the tests, see Testing.
Debugging
Start the editor with debug logging:
NVIM_MKDP_LOG_LEVEL=debug NVIM_MKDP_LOG_FILE=/tmp/mkdp.log nvim examples/features.mdand follow the log with tail -f /tmp/mkdp.log. Without these variables, the
log is mkdp-nvim-<uid>.log or mkdp-nvim-<uid>.1.log, whichever is newer
(mkdp-nvim.log on Windows), in the system temp directory, and debug logging
without NVIM_MKDP_LOG_FILE writes a new
mkdp-nvim-<uid>.debug-<time>-<pid>.log for each session. The page logs to
the browser console.
The server talks to the editor over stdout, so never print to stdout or
stderr from it; use the info!, debug! and error! macros from
src/logger.rs.
Project conventions
- Tests live in their own files:
src/<module>_test.rsnext to the module,app/test/,e2e/tests/. Application files only include them with#[cfg(test)] #[path = "<module>_test.rs"] mod tests;. - Comments explain why, not what.
- Options are
g:mkdp_*variables, read by the server withget_varwhen they are used, so they can change while the editor runs.