Skip to content

Latest commit

 

History

History
173 lines (112 loc) · 12.7 KB

File metadata and controls

173 lines (112 loc) · 12.7 KB

Marksplice User Guide

Use this page to choose the shortest path to the task you have. If this is your first time using Marksplice, start with Getting Started.

I want to...

Goal Start here Runnable example
Load a Markdown file and inspect its structure Inspect a document go run ./examples/inspect
Rename, replace, check, remove, insert, move, or combine edits Edit an existing document go run ./examples/edit
Create Markdown from structured Go values Create a document go run ./examples/build
Export one deterministic normalized Markdown representation Render canonical Markdown go run ./examples/render --markdown
Render deterministic HTML or correlate Markdown ranges with emitted HTML Render HTML go run ./examples/render / go run ./examples/render --map
Work with list hierarchies, sections, or GFM tables Lists, sections, and tables go run ./examples/query
Discover/follow Markdown files, resolve fragments, build backlinks, or validate a document set Links and workspaces go run ./examples/workspace
Observe application-specific syntax without changing core GFM Read-only extensions go run ./examples/extensions
Check whether a feature is supported Capability matrix —
Find an exact function or method signature API Reference —

The model in one minute

Marksplice deliberately separates two jobs.

Existing Markdown: Parse → inspect → Prepare... → Apply

A parsed Document is immutable and owns an exact source snapshot. Existing-document operations prepare narrow ChangeSet values against that snapshot. Applying a change to different bytes fails with ErrSourceConflict.

This is the path to use when author formatting matters.

New Markdown: DocumentBuilder → Markdown

A DocumentBuilder represents construction intent. It writes deterministic canonical GFM because there is no existing author source to preserve.

Do not use the builder to round-trip an existing document when your goal is a small edit.

Reading a parsed document

The public API exposes several levels of detail:

  • Nodes() gives source-ordered promoted structural summaries.
  • Typed accessors such as Heading, Task, TableCell, FencedCode, InlineLink, and ReferenceDefinition expose operation-specific detail; Heading.Text() provides parser-derived semantic heading text while Heading.Range() retains exact authored source ownership.
  • Higher-level views such as Sections, FencedBlocks, Alerts, MathExpressions, FootnoteDefinitions, and FrontMatter expose reviewed semantics that do not always imply mutation authority.
  • QueryNodes and QuerySections provide bounded structural selection.
  • HeadingAnchors, ResolveFragment, and LinkRelationships provide navigation and relationship intelligence; LocalFragmentContinuity validates how already-resolved local-fragment relationships behave across one prepared ChangeSet without comparing snapshot-scoped node IDs.

A public Range always means exactly what the accessor documents. Marksplice intentionally does not define one universal "full node range" for every construct.

Editing existing source

Most mutation APIs are named Prepare.... Typical families include:

  • paragraph, heading, task, fenced-code/info, inline, direct-link/image/autolink, reference-definition, front-matter, HTML, footnote, math, and table-cell replacements;
  • source-proven blockquote content replacement and GitHub alert kind/body mutation for uniform marker/EOL forms;
  • section replacement/removal/insertion/movement/child append;
  • list-item content/subtree replacement, removal, sibling insertion/movement, and child append;
  • table row, alignment, and complete-column operations;
  • thematic-break and complete-blockquote removal;
  • managed TOC synchronization, including ComposeChangesAndSyncTOC when heading/section changes and the derived TOC must agree in one final atomic result;
  • ComposeChanges for independent operations prepared from the same snapshot.

The exact supported shapes are deliberately conservative. If Marksplice cannot prove the source ownership or surviving structure required by an operation, it returns an error instead of rewriting a wider region.

See Edit an existing document.

Creating new Markdown

DocumentBuilder supports reviewed GFM construction for common document families, including:

  • headings and paragraphs;
  • typed inline text, code, emphasis, strong, strikethrough, links, images, autolinks, references, footnote references, and reviewed math forms;
  • ordered/unordered lists and task lists, including reviewed homogeneous nesting;
  • tables and alignments;
  • fenced code;
  • blockquotes and GitHub alerts;
  • reference definitions plus immediate/deferred single-line and reviewed canonical multiline footnote definitions;
  • YAML/TOML front-matter envelopes;
  • thematic breaks and mathematical blocks.

Generated content is reparsed and checked against construction expectations before Markdown() returns it.

See Create a document.

Rendering canonical Markdown

Canonical Markdown is an explicit export path, not an implementation detail of source-preserving editing. Document.RenderCanonicalMarkdown streams one deterministic Markdown representation to an io.Writer; Document.CanonicalMarkdown returns caller-owned bytes when buffering the complete result is useful.

The writer consumes the same Native semantic walk used by the HTML renderer and does not parse Markdown syntax a second time or retain a rendering AST in Document. It intentionally exposes no style configuration: formatting normalization is the purpose of this export, and one stable Marksplice profile keeps semantic round-trip and byte-idempotence testable.

The parsed source snapshot remains untouched. Use ordinary Prepare... operations and ChangeSet.Apply when the goal is a narrow edit that preserves unrelated author bytes. Use canonical rendering only when the caller explicitly wants normalized Markdown output.

See Render canonical Markdown or run go run ./examples/render --markdown.

Rendering HTML

HTML rendering is an explicit export path, not an implementation detail of editing. Document.RenderHTML streams a deterministic fragment to an io.Writer; Document.HTML returns caller-owned bytes when buffering the whole result is useful.

The renderer consumes the same Native semantic walk used by the other rendering paths and does not parse Markdown syntax a second time. HTMLRenderOptions makes three policy boundaries explicit: raw HTML preservation versus escaping, dangerous-URL suppression versus allowance, and the published GFM tag filter. The zero value preserves parser-proven raw HTML, enables the GFM tag filter, and suppresses dangerous URL schemes.

Preserved raw HTML is not a sanitizer. Use HTMLRawEscape or an application-appropriate downstream sanitization boundary for untrusted input. Rendering does not fetch URLs or images, run templates, highlight code, execute fenced content, or invoke a mathematical rendering engine.

Standalone RenderHTMLDocument/HTMLDocument wrap the same body renderer in deterministic doctype/html/head/charset/body markup. HTMLDocumentOptions reuses HTMLRenderOptions for the body and maps only exact lower-case title, description, author, and safe lang values from already source-proven simple top-level front matter. Complex, duplicate, nested, invalid-UTF-8, or escape-dependent values are omitted rather than interpreted by a YAML/TOML parser. HTMLMetadataOmit disables that mapping.

For preview/editor integration, the ...WithSourceMap variants return caller-owned HTMLSourceMapEntry values that correlate snapshot-local Markdown byte ranges with byte ranges in the exact emitted HTML. The map is semantic-event granular rather than complete byte coverage: nested ranges may overlap, synthetic HTML can be unmapped, and standalone offsets are absolute from the beginning of the complete HTML document. Mapping is opt-in; ordinary rendering retains no result map.

See Render HTML, run go run ./examples/render for HTML, or go run ./examples/render --map to inspect source/output correlations.

Navigation and multi-document work

Marksplice can understand document relationships without hidden filesystem or URL authority. Root graph APIs remain in-memory; workspacefs is a separate read-only adapter that operates only on a caller-supplied fs.FS under explicit finite limits.

For one document:

  • derive GitHub-compatible heading anchors;
  • resolve local fragments;
  • generate and conservatively synchronize managed TOCs;
  • enumerate semantic link/image/autolink relationships.

For filesystem-backed documentation:

  • workspacefs.Scan discovers .md/.markdown files under one explicit fs.FS root and assigns deterministic slash-relative keys;
  • workspacefs.Follow starts from explicit entries and follows reviewed local Markdown URI paths, normalizing relative dot segments against each source document, percent-decoding path components once, ignoring query text for file lookup, preserving target fragments, and visiting cycles once;
  • absolute/scheme/protocol-relative paths, backslashes, encoded traversal or separators, directory targets, and extensionless targets are not followed. Case and symlink behavior comes from the fs.FS supplied by your application;
  • both operations enforce caller-supplied document, byte, depth, and relationship limits and perform no writes, network access, or command execution.

For several documents that your application already loaded, or for the documents returned by workspacefs:

  • BuildDocumentGraph creates an immutable graph over explicit caller keys;
  • a caller resolver decides which non-local relationships map to which already-supplied documents;
  • graph queries expose outgoing edges, backlinks, reachability, and related documents;
  • ValidateWorkspace adds deterministic link/fragment/reference/orphan/managed-TOC diagnostics and conservative repair planning;
  • BuildKnowledgeIndex adds caller-declared aliases, tags, and logical references without inventing Markdown syntax.

See Links and workspaces.

Extensions

ParseWithOptions can attach namespaced read-only observations produced by caller-linked recognizers. Extension nodes cannot replace core GFM nodes or gain generic editing, construction, graph, filesystem, network, or command authority.

Use this for product-specific syntax such as a private [[wikilink]] convention when observation is enough. See Read-only extensions.

Errors

Use errors.Is with public sentinel families rather than comparing messages:

  • ErrNodeNotFound
  • ErrInvalidReplacement
  • ErrInvalidTargetKind
  • ErrSourceConflict
  • ErrInvalidConstruction
  • ErrInvalidQuery
  • ErrInvalidGraph
  • ErrInvalidWorkspace
  • ErrInvalidKnowledge
  • ErrInvalidExtension
  • ErrInvalidRender

The separate workspacefs package classifies malformed filesystem-workspace input with workspacefs.ErrInvalidInput and exhausted load/traversal limits with workspacefs.ErrBudgetExceeded.

Diagnostic strings are not compatibility contracts.

Concurrency and ownership

Successfully built immutable Document, DocumentGraph, KnowledgeIndex, WorkspaceReport, workspacefs.Workspace, and prepared ChangeSet values may be read concurrently.

DocumentBuilder is mutable and requires caller synchronization for concurrent use. Resolver and extension callbacks are invoked synchronously and are not retained after the build/parse call returns.

Public variable-length results are caller-owned unless an API explicitly states otherwise.

What Marksplice does not own

The root document/graph APIs perform no implicit filesystem, network, or command I/O. workspacefs adds only explicit read-only filesystem access through the caller's fs.FS; it does not write files, fetch URLs, or execute commands. Marksplice emits canonical Markdown or HTML only when explicitly requested; it does not render PDF, execute fenced languages, serialize arbitrary YAML/TOML, run templates, run a LaTeX/math engine, fetch assets, or normalize an existing document as a side effect of a structural edit.

Those boundaries are summarized in Capabilities. Architecture and conformance rationale live in the maintainer documentation.