graph

package
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 5, 2026 License: MIT Imports: 5 Imported by: 0

Documentation

Overview

Package graph resolves [[wikilinks]] the way Obsidian resolves them in this vault: four keys per note (filename stem, filename, path stem, path) plus frontmatter aliases, each folded by NormalizeKey — never the frontmatter title, which Obsidian does not resolve either. Two files sharing a key are reported in full and never guessed between.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func EscapedWikilinkAt added in v0.2.0

func EscapedWikilinkAt(text string, open int) bool

EscapedWikilinkAt reports whether the wikilink whose "[[" begins at open is written to be shown rather than followed: the CommonMark backslash escape, an odd-length run of '\' in front of it, counted from in front of an embed's '!' when the author wrote one. A shown name is not a cited name, so every reader of this vault answers this question the same way.

func FoldFragment added in v0.2.0

func FoldFragment(s string) string

FoldFragment folds the half of a link written after its "#" the way both kinds of fragment fold: Unicode form and letter case, and nothing else. Those are the two ways a name differs for reasons its author never chose; every other difference they did choose, and it is kept. A block address folds through here too, which is what keeps "^quote-1" and "^quote1" two names.

func NormalizeKey

func NormalizeKey(name string) string

NormalizeKey folds a resolution key the way index build and lookup both fold it: trim, Unicode NFC, lowercase. A caller building its own name index folds through this, so the two agree about which written names match.

func SectionID added in v0.2.0

func SectionID(name string) string

SectionID is the id a page stamps for a heading of this name, and therefore the fragment a link has to carry to reach it: fold, keep letters and digits, collapse every other run to one hyphen, trim the ends, and fall back to "section" when nothing is left. Keeping every Unicode letter is what lets a CJK heading produce a usable id, and folding first is what keeps か+◌゙ん and がん one id rather than two, since a combining mark left alone is not a letter and would become a hyphen.

Every face that stamps an id, follows one, or asks whether a note answers one reads it from here, so a link and the heading it names cannot drift apart.

func SplitWikilink(inner string) (target, display string, ok bool)

SplitWikilink is ParseWikilink for a caller that resolves a name and prints a label, and has no use for where inside the file the link pointed.

func WithoutRestatedNames added in v0.2.0

func WithoutRestatedNames(byName map[string][]string) map[string][]string

WithoutRestatedNames drops from a collision map every name that only restates another: a filename goes when its extension-less form claims exactly the same files, so one repair is not counted twice. It takes the map rather than the index so a caller that describes only some claimants asks over those.

Types

type Index

type Index struct {
	// contains filtered or unexported fields
}

Index maps every normalized name a note or resource is resolvable by to the vault-relative paths holding it. Read-only once built.

func BuildFromNotes

func BuildFromNotes(notes []NoteInput, resources []string) *Index

BuildFromNotes builds an Index from note paths, their already-extracted aliases, and resource paths, touching no disk.

func New

func New(notes []*vault.Note, resources []string) *Index

New builds an Index over notes and non-markdown resources already captured from the vault, extracting each note's aliases itself.

func (*Index) Collisions added in v0.2.0

func (idx *Index) Collisions() map[string][]string

Collisions reports every name more than one file answers to, mapped to the paths claiming it. Such a name resolves to nothing — the index refuses to choose. The returned map and slices belong to the caller.

func (*Index) DistinctCollisions added in v0.2.0

func (idx *Index) DistinctCollisions() map[string][]string

DistinctCollisions is Collisions with the restated names dropped.

func (*Index) Resolve

func (idx *Index) Resolve(name string) Resolution

Resolve looks name up against the index. An anchor is never verified here: [[X#heading]] resolves as long as X exists.

type Kind

type Kind int

Kind distinguishes the three possible outcomes of Resolve.

const (
	KindUnresolved Kind = iota
	KindUnique
	KindAmbiguous
)

func (Kind) String added in v0.2.0

func (k Kind) String() string

String names a resolution kind for a diagnostic or a log line.

type NoteInput

type NoteInput struct {
	RelPath string
	Aliases []string
}

NoteInput is what BuildFromNotes needs of one markdown note: its vault-relative path and its frontmatter aliases, never its title.

type Resolution

type Resolution struct {
	Kind Kind
	// RelPath is the resolved vault-relative path when Kind is KindUnique.
	RelPath string
	// Candidates lists every claimant, sorted, when Kind is KindAmbiguous;
	// the index never chooses between them.
	Candidates []string
}

Resolution is the outcome of resolving one wikilink target.

type Wikilink struct {
	// Target is the name to resolve, fragments and display text removed.
	// Empty means the link addresses a place in the current file.
	Target string
	// Display is the text a reader sees: the words after the display
	// separator, or the whole inner text when the author wrote none.
	Display string
	// Heading is the section name written after "#", empty when absent.
	Heading string
	// Block is the block name written after "^", empty when absent. Obsidian
	// writes a block address as "#^name", so a fragment opening with "^" is
	// one of these and not a section named "^name".
	Block string
}

Wikilink is one wikilink's inner text — the characters between its enclosing "[[" and "]]" — split into what resolution reads, what a reader sees, and the two fragments. The fragments are the raw authored text; nothing here checks that either addresses a place the target file actually has.

func ParseWikilink(inner string) (Wikilink, bool)

ParseWikilink splits inner into its target, display text, and fragments, stripping the markers in a fixed order: '|', then '#', then '^'. An escaped pipe, which a GFM table cell writes as "\|", splits the same as a bare one and yields the same target, so the escape never changes what resolves.

ok is false when the target strips to empty ("#heading" alone): a same-file anchor jump, not a cross-file link.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL