Workspace search — full-text across every .md file

Recursive grep across every Markdown file in the open workspace. Press Ctrl+Shift+F. Click any hit to open the file with the find bar pre-filled to highlight the match. Built for searching vaults, note collections, and project docs.

Last updated

Workspace search runs a recursive substring scan across every Markdown file in the open folder, line by line, and surfaces a list of matches in the workspace sidebar. Click any hit to open that file with the in-document find bar pre-filled to the same query — so the match is already highlighted and you can cycle through other matches in the same file with Enter.

It’s the right tool when you remember a phrase but not which note it’s in: a CLAUDE.md across many repos, a knowledge vault, a documentation tree, a folder of meeting notes.

How to open it

  1. Open a folder as a workspace — drag-drop a folder onto the window, pick File → Open Folder… (Ctrl+Shift+O), or run mdview <folder> from your shell.
  2. Press Ctrl+Shift+F (or pick View → Search Workspace…). The search input at the top of the workspace sidebar takes focus.
  3. Start typing. Results stream in below the input, grouped by file and line, after a 200 ms debounce.
  4. Click any result — or press Enter on it after tabbing — and MD View opens that file with the find bar pre-filled and the match highlighted.

What it indexes

The walk is recursive from the workspace root and skips unhelpful noise:

  • Files included: .md, .markdown, .mdx (case-insensitive on extension).
  • Folders skipped: node_modules, .git, dist, build, target, vendor (case-insensitive). These are dropped at the walker level, so they never appear in the file tree either.
  • Symlinks: skipped to avoid cycles.
  • Files larger than 2 MB: skipped and reported as “N files skipped (too large)” in the result status. Big markdown files are almost always generated logs or transcripts and pollute results without adding value.

There is no on-disk index. The walk runs fresh on every search. For a 500-file vault that’s well under 100 ms; for 10,000+ note vaults, query latency starts to show.

How matches work

  • Case-insensitive substring. No regex, no glob, no fuzzy matching. Plain lower-cased haystack contains lower-cased needle. Whitespace, punctuation, and special characters are matched literally.
  • Line by line. Each match is a single line; multi-line patterns aren’t supported.
  • Stable order. Results sort by full path, so re-running the same query against the same workspace produces identical output every time.
  • First-hit-per-line. A line with multiple occurrences contributes one row, not multiple.
  • Preview clipping. Lines longer than ~400 bytes are clipped around the match with ellipses on each side, at character boundaries (no mid-codepoint slicing).

Click-to-jump

The handoff from workspace search → in-document find is the part that earns its keep. Click a result and:

  1. The file opens. If it was already open in another tab, MD View navigates to it; if not, it’s loaded fresh.
  2. The renderer fires a mdview:rendered event when the new document is mounted in the DOM.
  3. The find bar opens with the search query pre-filled.
  4. Every match in the document highlights via the CSS Custom Highlight API, and the first one centers in the viewport.

So a search for verify_paths across a 50-file project ends with you reading the function reference, with the term highlighted and the rest of its appearances one Enter away.

Limits and known behaviors

  • No regex. Power users coming from VS Code’s Ctrl+Shift+F may miss it. Tracked as a possible v2.
  • No Unicode case-folding. The lowercase fold is ASCII-only via to_ascii_lowercase; Turkish dotted-I, German ß, and other locale-specific cases will not match what you might expect. v1 scope; full Unicode folding requires a dependency we’re avoiding.
  • No filename matching. Only file contents are searched. To find a file by name, use Quick Open (Ctrl+K).
  • No “match in heading” weighting. Hits in headings sort the same as hits in body prose.
  • No multi-line patterns. Each match is a single line.
  • 200-result cap. Beyond 200 hits the result is marked truncated and you’re prompted to refine. The cap exists to bound IPC payload size and keep the UI snappy on a 50,000-hit query.
  • No persistent index. The walk runs fresh on every search. Adding/editing a file invalidates nothing because there’s nothing to invalidate. Trade-off: 10,000-file workspaces start to feel the latency.
  • Workspace-only. A piped stdin document, a single file opened directly, and a recents-list document have no workspace root, so the shortcut shows a “Open a folder first” notice instead.

Comparison

AppWorkspace searchClick-to-jumpRegex / fuzzyIndex
MD ViewYes — recursive substringYes (find bar pre-fill)No / NoNone (live grep)
ObsidianYes — indexedYesYes / NoPersistent
Foam (VS Code)VS Code’s Ctrl+Shift+FYes (Cmd+Click)Yes / NoInherited
iA WriterLibrary searchYesNo / NoPersistent
LogseqYes — graph + full-textYesYes / YesPersistent
VS Code (raw)Ctrl+Shift+FYesYes / NoLive ripgrep

MD View’s design choice: keep search simple and predictable. No hidden index that can rot, no PCRE engine to ship, no fuzzy algorithm to tune. The trade is honest: at 10K+ files, an indexed competitor will be faster.

See also