A minimal knowledge layer for Markdown in Neovim.
Ma.nvim lets structure emerge from filenames rather than directories. Notes live in a flat filesystem, while hierarchy is inferred through dot-separated segments and hyphenated words.
Inspired by Dendron and Obsidian, Ma provides vault-scoped navigation and safe file operations without imposing a folder-based structure.
Ma (間) is a Japanese concept often translated as interval or space between.
It does not mean emptiness as absence. It refers to meaningful space: the gap that makes relationships possible.
In Ma.nvim, structure is not imposed through folders. It emerges in the intervals:
Ma also implies time: the pause between actions, the space we take to write, reflect, and connect ideas in plain text.
architecture.norman-foster.apple-piazza-liberty.md
Ma interprets this as a hierarchy:
📁 architecture └ 📁 norman-foster └ 📄 apple-piazza-liberty

Required:
Optional:
icons column. If missing, Ma falls back to built-in defaults ( for folders, for files).gd on links, semantic link resolution, and document symbols.Pinning to the latest release tag is recommended.
With lazy.nvim:
{
"gmcusaro/ma.nvim",
version = "*",
dependencies = {
"nvim-lua/plenary.nvim",
"nvim-telescope/telescope.nvim",
{ "nvim-telescope/telescope-fzf-native.nvim", build = "make" },
{ "nvim-tree/nvim-web-devicons", opts = {} },
},
opts = {
vaults = {
{ name = "My Brain", path = "~/notes" },
},
},
}
require("ma").setup({
vaults = {
{ name = "My Brain", path = "~/notes" },
},
respect_gitignore = true,
autochdir = "lcd",
depth = nil,
delete_to_trash = true,
picker_actions = {
{ "c", "create" },
{ "r", "rename" },
{ "d", "delete" },
},
date_format_frontmatter = "%Y %b %d - %H:%M:%S",
telescope = {},
columns = { "git", "icons" },
sort = { by = "name", order = "asc" },
daily_notes = {
date_format = nil,
locale = nil,
}
})
:MaOpen the Telescope navigator for the active root. Use it to browse notes as a dot-based tree and run picker actions.
:Ma vault / :Ma vault <name>Pick and switch the active vault.
:Ma vault opens the vault picker and switches the active vault.:Ma vault <name> switches directly to an existing configured vault by name.When a vault is selected, Ma sets it as active and reopens navigation in that vault. The active root is the currently selected vault path, or Neovim's current working directory if no valid vault is configured.
Use when:
:Ma createCreate or open a note from the command line.
Behavior:
Name handling:
.md is added automatically if omitted..md or .markdown suffixes are normalized and not duplicated./ is normalized to ..-.The final segment of the name always creates a file. Ma does not create standalone "folders"; hierarchy exists conceptually through dot-separated segments.
Use when:
:Ma dailyCreate or open the daily note. Use it to keep daily journal/log notes.
Builds daily.<formatted-date>.md using daily_notes options.
:Ma renameRename the current managed note. Works only for markdown files under the active root. This command renames only the current note file.
Use when:
Picker folder rename (multi-file):
Example:
architecture.foster.apple-piazza-liberty
into
architecture.norman-foster.apple-piazza-libertyarchitecture.foster.30-st-mary-axe
architecture.foster.millennium-bridge
into
architecture.norman-foster.30-st-mary-axe
architecture.norman-foster.millennium-bridge:Ma deleteDelete the current note with confirmation.
Works only for managed markdown notes under the active root.
In the navigator picker, multi-select deletion is supported via Telescope multi-select.
Uses trash-first or hard-delete mode (see delete_to_trash).
:Ma linkCreate a markdown link from the current visual selection.
Prompts for target note, opens/creates it, and replaces selection with [label](target.md).
The target note name is normalized and lowercased.
Important:
Use when:
Enter: open folder/file.Backspace (normal mode): go up when prompt is empty.- (normal mode): force go up.Ctrl-h (insert mode): force go up.picker_actions in normal mode.vaultsList of vault roots Ma can use as note workspaces.
Type:
{ { name?: string, path: string } } | nilDefault:
{}Behavior:
name falls back to folder basename.Example:
vaults = {
{ name = "Personal", path = "~/notes/personal" },
{ name = "Work", path = "~/notes/work" },
}
Important:
Use when:
respect_gitignoreControl whether scans exclude files ignored by .gitignore.
Type:
booleanDefault:
trueExample:
respect_gitignore = false
Use when:
false only if you intentionally want ignored markdown files included.autochdirControl whether Ma changes Neovim's working directory to the active root before major actions.
Type:
false | "lcd" | "tcd" | "cd"Default:
"lcd"Behavior:
false: no directory change."lcd": window-local cwd."tcd": tab-local cwd."cd": global cwd.Example:
autochdir = "tcd"
Important:
Use when:
depthMaximum directory recursion depth during note scanning.
Type:
integer | nilDefault:
nilBehavior:
nil: unlimited recursion.Example:
depth = 3
Use when:
delete_to_trashChoose trash-first deletion or direct deletion.
Type:
booleanDefault:
trueBehavior:
osascript on macOS, gio, trash-put, kioclient5) and falls back to file removal.Example:
delete_to_trash = true
Important:
Use when:
picker_actionsKeybindings available inside the navigator picker.
Type:
{ { string, "create"|"rename"|"delete" } } | { [string]: "create"|"rename"|"delete" } | falseDefault:
{
{ "c", "create" },
{ "r", "rename" },
{ "d", "delete" },
}
Behavior:
false disables picker actions.Example:
picker_actions = {
{ "n", "create" },
{ "x", "delete" },
}
Use when:
date_format_frontmatterDate format for created and updated frontmatter fields.
Type:
stringDefault:
"%Y %b %d - %H:%M:%S"Behavior:
Used for created/updated timestamps in frontmatter.
updated is refreshed only if:
updated: keyExample:
date_format_frontmatter = "%Y-%m-%d %H:%M"
Important:
updated: only if frontmatter exists at top of file and already contains an updated key.Use when:
telescopeOverride Telescope picker options specifically for Ma.
Type:
tableDefault:
{}Behavior:
prompt_prefix, selection_caret, initial_mode, and layout_config.initial_mode accepts only "insert" or "normal".Supported options are documented in the Telescope README.
Example:
telescope = {
prompt_prefix = " ",
selection_caret = "| ",
initial_mode = "normal",
layout_config = {
prompt_position = "top",
width = 0.9,
height = 0.9,
preview_width = 0.6,
mirror = false,
preview_cutoff = 120,
},
}
Use when:
columnsChoose which navigator columns are shown and in what order.
Type:
tableDefault:
{ "git", "icons" }
Behavior:
"git", "icons".git column is not active, Ma skips Git status computation.Use when:
columns.gitNested key inside columns used to override Git status symbols for the git column.
Type:
tableDefault:
nil (unset)Built-in symbols (used when git column is enabled):
{
clean = " ",
modified = "M ",
added = "A ",
deleted = "D ",
renamed = "R ",
copied = "C ",
untracked = "? ",
ignored = "! ",
conflicted = "U ",
unknown = "~ ",
}
Behavior:
git = {...} auto-enables the git column if missing.Example:
columns = {
git = {
modified = "✱ ",
untracked = "… ",
conflicted = "‼ ",
},
"icons",
}
columns.iconsNested key inside columns used to customize folder/file icons in the icons column.
Type:
{ folder?: string, file?: string }Behavior:
icons = {...} auto-enables the icons column if missing.icons.folder or icons.file value is used directly.folder icon, Ma uses fallback .file icon, Ma uses nvim-web-devicons when available, else fallback .Examples:
-- default
columns = { "git", "icons" }
-- custom fixed icons (dependency optional)
columns = {
"git",
icons = {
folder = "F ",
file = "md",
},
}
Use when:
nvim-web-devicons.sortSort strategy for entries in each picker level.
Type:
{ by?: "name"|"update"|"creation", order?: "asc"|"desc" }Default:
{ by = "name", order = "asc" }
Behavior:
name: case-insensitive by segment text.update: by file mtime.creation: by birthtime/ctime fallback.Example:
sort = { by = "update", order = "desc" }
Use when:
daily_notesEnable/disable daily note generation and control date formatting.
Type:
table | falseDefault:
{ date_format = nil, locale = nil }
Behavior:
false: disables :Ma daily.daily.<date>.md.Example:
daily_notes = {
date_format = "%Y.%b.%d",
locale = "en_US.UTF-8",
}
Example output: daily.2026.Mar.03.md
Use when:
daily_notes.date_formatDate token pattern used in daily note filename and title.
Type:
string | nilDefault:
"%Y.%b-%d" (when nil)Behavior:
os.date/strftime-style tokens.Example:
daily_notes = { date_format = "%Y-%m-%d" }
Important:
. and -) affect hierarchy and readability.Use when:
daily_notes.localeLocale used while formatting daily note dates.
Type:
string | nilDefault:
nil (current process locale)Behavior:
os.setlocale(locale, "time"), formats the date, then restores previous locale.Example:
daily_notes = { locale = "it_IT.UTF-8" }
Use when:
When creating a new note via :Ma create or :Ma daily,
Ma generates YAML frontmatter with this structure:
---
id: <23-char random id>
title: <title>
desc: <desc>
updated: <formatted timestamp>
created: <formatted timestamp>
---
Behavior:
created and updated use date_format_frontmatter.updated is refreshed on BufWritePre for managed markdown files under the active root.updated is only modified if frontmatter exists and contains an updated: field.id is generated automatically and is not currently used for indexing or linking.All contributions are welcome! Just open a pull request.