graph

package
v0.1.3 Latest Latest
Warning

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

Go to latest
Published: Aug 3, 2026 License: Apache-2.0 Imports: 8 Imported by: 0

Documentation

Overview

Package graph builds the document link graph from the stored link edges: it resolves each raw link target (a relative path or a wikilink) against the set of indexed files, then answers the questions worth asking of a docs tree — what's orphaned, what links are broken, what points at a given file. Resolution lives here (not at index time) so it always reflects the current file set: adding or renaming a target fixes resolution on the next query without rewriting every source file's edges.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Edge

type Edge struct {
	From   string `json:"from"`
	To     string `json:"to,omitempty"`     // resolved rel-path; empty = unresolved
	Raw    string `json:"target"`           // raw destination as written
	Anchor string `json:"anchor,omitempty"` // #section fragment, if any
	Kind   string `json:"kind"`             // chunk.LinkMarkdown | chunk.LinkWiki
	Line   int    `json:"line"`
}

Edge is one outbound link with its target resolved to an indexed file, or left empty when the target resolves to nothing (a candidate broken link).

func (*Edge) DestWithAnchor

func (e *Edge) DestWithAnchor() string

DestWithAnchor renders the edge's target as written, re-attaching its #section anchor (stored apart from the path) for display.

type Graph

type Graph struct {
	Files []string
	Edges []Edge
	// contains filtered or unexported fields
}

Graph is the resolved document link graph.

func Build

func Build(src Source, root string) (*Graph, error)

Build loads files and links from src and resolves every edge. root is the vault directory on disk; it lets the resolver recognize links to real directories that hold no indexed files (e.g. an assets folder) as valid rather than broken. Pass "" to resolve purely off the index (tests).

func (g *Graph) Backlinks(target string) []Edge

Backlinks returns the edges pointing at target (a rel-path), i.e. the files that link to it.

func (*Graph) Broken

func (g *Graph) Broken() []Edge

Broken returns edges whose doc-like target resolved to no file — dead links to fix. Links to assets (images, PDFs, other non-markdown extensions) are not flagged, since those targets aren't indexed by design. Ordered by source path then line (the order AllLinks returns).

func (*Graph) BrokenAnchors

func (g *Graph) BrokenAnchors() []Edge

BrokenAnchors returns edges that resolve to a file but whose #section fragment matches no heading in that file — a link pointed at the right doc but a stale or mistyped section. Edges with no anchor, or whose target file didn't resolve (already covered by Broken), are excluded. Ordered by source path then line (the order AllLinks returns).

func (*Graph) Orphans

func (g *Graph) Orphans() []string

Orphans returns markdown files with no inbound resolved link, sorted. Go files are excluded — they're not part of the document graph, so they'd all read as orphans. An orphan may be intentional (a top-level index nothing links to); the list is a cleanup prompt, not a verdict.

func (*Graph) Stats

func (g *Graph) Stats() Stats

Stats computes the summary counts in one pass.

type Resolver

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

Resolver maps raw link targets onto indexed files. It's exported so the lint layer resolves inline-code doc paths through the same logic as real links.

func NewResolver

func NewResolver(files []string) *Resolver

NewResolver builds a resolver over the given indexed file set.

func (*Resolver) Candidates

func (r *Resolver) Candidates(target string) []string

Candidates reports every indexed file a bare basename (no directory, e.g. "service.go") could match, filtered to the same extension as target — exposing when resolveWiki's first-match pick among same-named files across different directories is arbitrary rather than a genuine single answer. Always empty for a target with a directory component, since that resolves to at most one exact path. Exported for the lint layer, which flags a multi-candidate bare-basename LinkCode reference as ambiguous instead of silently promoting it to whichever file sorts first.

func (*Resolver) IsDir

func (r *Resolver) IsDir(src, target string) bool

IsDir reports whether a markdown link target points at a directory (e.g. `../services/`). GitHub renders such a link as that directory's listing, so it isn't a broken link. It resolves the target to a cleaned rel-path and accepts it as a directory if: it's the tree root ("."); it holds an indexed file (the index-derived set); or — when a vault root is known — it exists on disk as a directory. The on-disk check is what catches directories that hold only unindexed content (an assets or data folder with no markdown).

func (*Resolver) IsUnindexedFile added in v0.1.2

func (r *Resolver) IsUnindexedFile(src, target string) bool

IsUnindexedFile reports whether an extensionless markdown link target names a real file that simply isn't indexed — LICENSE, NOTICE, CODEOWNERS, Makefile. Having no extension is what makes isDocLike treat them as documents, so unlike an asset they reach the broken check, and Resolve finds nothing because nothing indexed them. The link renders on GitHub like any other, so reporting it broken would flag the LICENSE link in every Apache-2.0 repo.

Only the extensionless case is checked. A target that carries an extension is either indexed or deliberately skipped as an asset, and neither needs a stat.

func (*Resolver) Resolve

func (r *Resolver) Resolve(src, target, kind string) string

Resolve returns the indexed file a raw target points at, or "" if none. Wikilinks resolve vault-root-relative (or by basename for a bare name). Inline-code paths try the same vault-root-relative/basename resolution first, then fall back to resolving relative to the source file's own directory — many docs write an inline-code path that way (e.g. a module's own README mentioning "pkg/foo.go" to mean its own pkg/foo.go), the same convention markdown links already use. Markdown links resolve relative to the source file's dir only.

type Source

type Source interface {
	AllFiles() ([]string, error)
	AllLinks() ([]index.LinkRow, error)
	FileHeadings() (map[string]map[string]bool, error)
}

Source is the read side of the index the graph needs. *index.Store satisfies it; tests supply a fake.

type Stats

type Stats struct {
	Files         int
	Markdown      int
	Edges         int
	Resolved      int
	Broken        int
	BrokenAnchors int
	Orphans       int
}

Stats summarizes the graph for the default report.

Jump to

Keyboard shortcuts

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