Hard Wrap

by alethiophile
5
4
3
2
1
Score: 35/100

Description

This plugin has not been manually reviewed by Obsidian staff. Transparently hard-wrap markdown files on disk while displaying them as soft-wrapped paragraphs in the editor.

Reviews

No reviews yet.

Stats

stars
17
downloads
0
forks
1
days
NaN
days
NaN
days
0
total PRs
0
open PRs
0
closed PRs
0
merged PRs
0
total issues
0
open issues
0
closed issues
0
commits

Latest Version

Invalid date

Changelog

README file from

Github

Hard Line Wrap for Obsidian

Transparently hard-wrap markdown files on disk while displaying them as soft-wrapped paragraphs in the editor.

Files are always stored with hard line breaks at a configurable column width (default 80), making them pleasant to read in a terminal, emacs, git diffs, etc. Obsidian's editor sees the unwrapped form — one long line per paragraph — and handles visual wrapping natively.

Configuration

Global settings

In Settings > Hard Wrap:

Setting Default Description
Enable hard-wrapping on Master toggle
Column width 80 Wrap target (40–200)

Per-file override

Add a hard-wrap key to a file's frontmatter:

---
hard-wrap: 60       # wrap at 60 columns
---
---
hard-wrap: false    # disable for this file
---
---
hard-wrap: true     # enable with global default columns
---

Omitting the key uses the global default.

Commands

  • Reformat current file with hard wraps — normalizes the editor content by running unwrap → wrap → unwrap. This cleans up hard breaks that the plugin didn't insert — e.g. text pasted from an external source, or soft wraps added manually — without requiring a save/reload cycle. Also useful after changing the column width or after external edits.

How it works

The plugin monkey-patches three methods on MarkdownView.prototype using monkey-around:

  • On load (setViewData): hard-wrapped paragraphs are joined into single lines before the content reaches the editor.
  • On save (getViewData): the editor's single-line paragraphs are re-wrapped at the configured column width before being written to disk.
  • On mode switch (setMode): when switching back to source mode, this.data is re-unwrapped. Obsidian's setMode bypasses setViewData and feeds this.data directly to the editor, so without this patch the editor would show hard-wrapped lines after toggling between source and reading mode.

Both auto-save (the debounced requestSave) and manual save (Ctrl+S, close) go through getViewData, so all save paths produce hard-wrapped output. Patching getViewData rather than save is important because Obsidian also calls getViewData outside the save path. When a file is modified externally while the editor has unsaved changes, Obsidian performs a 3-way merge between lastSavedData, the current getViewData() return value, and the new disk content. Since lastSavedData and the disk content are both in hard-wrapped form, getViewData() must return wrapped text too — otherwise the merge would treat every re-wrapped line break as a conflicting change. Obsidian also uses getViewData() for change detection (comparing against lastSavedData to decide whether a save is needed), which likewise requires consistent representations.

On plugin unload, all open markdown files are force-saved so they remain hard-wrapped on disk.

What gets wrapped

The transform follows CommonMark soft-line-break semantics. Anything a markdown renderer would join into a single paragraph gets wrapped/unwrapped:

  • Plain paragraphs
  • Blockquote content (including nested blockquotes)
  • List item content (unordered, ordered, nested)
  • Footnote definitions
  • Callout body text

Everything else passes through verbatim:

  • Frontmatter (YAML between --- fences)
  • Fenced code blocks (backtick and tilde)
  • Indented code blocks
  • ATX and setext headings
  • Tables
  • Thematic breaks
  • HTML blocks
  • Callout headers ([!type] lines)

Within wrappable text, inline elements are never broken across lines:

  • Links: [text](url) and [text][ref]
  • Images: ![alt](https://raw.githubusercontent.com/alethiophile/obsidian-hard-wrap/HEAD/url)
  • Inline code: `code`

Hard line breaks (trailing \ or two trailing spaces) are preserved.

Build

Requires Node.js >= 18.

npm install
npm run build        # production build → main.js
npm run dev          # watch mode (rebuilds on change)
npm test             # run unit tests

The build uses esbuild. Output is a single main.js (CommonJS). The obsidian package and @codemirror/* are externals (provided by Obsidian at runtime). monkey-around is bundled.

Known limitations

  • Search: Obsidian indexes on-disk (hard-wrapped) content. A search for a phrase that spans a wrap boundary won't match.

Note on code authorship

This plugin and its documentation were substantially created using AI. They have been tested and reviewed by the human author.