lint

package
v0.23.0 Latest Latest
Warning

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

Go to latest
Published: May 21, 2026 License: MIT Imports: 18 Imported by: 0

Documentation

Index

Constants

View Source
const DefaultMaxInputBytes int64 = 2 * 1024 * 1024

DefaultMaxInputBytes is the default file-size cap (2 MB, binary).

Variables

View Source
var KindProcessingInstruction = markdown.KindProcessingInstruction

KindProcessingInstruction is the ast.NodeKind for ProcessingInstruction, re-exported from pkg/markdown so there is a single registered kind value in the tree.

Functions

func CollectCodeBlockLines

func CollectCodeBlockLines(f *File) map[int]bool

CollectCodeBlockLines returns a set of 1-based line numbers that belong to fenced code blocks (including fence lines) or indented code blocks. The walk is computed once per File and cached; the returned map is shared read-only and must not be mutated.

func CollectPIBlockLines added in v0.13.0

func CollectPIBlockLines(f *File) map[int]bool

CollectPIBlockLines returns a set of 1-based line numbers that belong to processing-instruction blocks, including the opening <?... line and the closing ?> line. The walk is computed once per File and cached; the returned map is shared read-only and must not be mutated.

func CountLines

func CountLines(b []byte) int

CountLines returns the number of newline-terminated lines in b, forwarded from pkg/markdown.

func FindFencedOpenLine added in v0.16.0

func FindFencedOpenLine(f *File, fcb *ast.FencedCodeBlock) int

FindFencedOpenLine returns the 1-based line number of the opening fence. Returns 0 when the block has neither an info string nor any content lines — the truly-empty fenced shape that goldmark exposes no source position for. Callers must NOT clamp 0 to 1 for section- range filtering or diagnostic anchoring: clamping would mis-locate the block at the top of the document and silently move any diagnostic to a line that has nothing to do with the source. The preferred fallback is sibling-derived inference — see internal/schema.topLevelBlocks for an implementation that walks adjacent blocks to recover a sensible position.

func NewParser added in v0.6.0

func NewParser() parser.Parser

NewParser returns mdsmith's canonical goldmark parser, forwarded from pkg/markdown. Rules that need to re-inspect a document (for example, to consult the link reference definition map) should use this so that processing-instruction blocks and other mdsmith-specific parsing decisions stay consistent with the original lint parse.

func PIBlockParserPrioritized added in v0.3.0

func PIBlockParserPrioritized() util.PrioritizedValue

PIBlockParserPrioritized returns the PI parser with its priority for registration, forwarded from pkg/markdown. Kept because internal/schema registers it directly; the parser logic itself lives in pkg/markdown.

func ParseFrontMatterFields added in v0.14.0

func ParseFrontMatterFields(fm []byte) (map[string]any, error)

ParseFrontMatterFields decodes a YAML front-matter block (including its --- delimiters) into a map of top-level keys to raw values. Returns (nil, nil) when fm is empty, whitespace-only, or decodes to YAML null. Returns an error when the payload is a non-null scalar or a sequence — both reject because the field-presence selector requires named keys — or when the YAML is otherwise invalid. Used by the kind-assignment field-presence selector; a field is considered present when its value is non-null.

func ParseFrontMatterKinds added in v0.7.0

func ParseFrontMatterKinds(fm []byte) ([]string, error)

ParseFrontMatterKinds extracts the kinds: list from a YAML front-matter block (including its --- delimiters). Returns nil kinds and nil error if the block is nil or the kinds key is absent. Returns an error if the YAML contains anchors/aliases or cannot be parsed.

func ReadFSFileLimited added in v0.6.0

func ReadFSFileLimited(fsys fs.FS, name string, max int64) ([]byte, error)

ReadFSFileLimited reads name from fsys, returning an error if the file exceeds max bytes. When max <= 0 or max == math.MaxInt64 no limit is applied (unlimited mode).

func ReadFileLimited added in v0.6.0

func ReadFileLimited(path string, max int64) ([]byte, error)

ReadFileLimited reads path from disk, returning an error if the file exceeds max bytes. When max <= 0 or max == math.MaxInt64 no limit is applied (unlimited mode). MaxInt64 is treated as unlimited because the +1 sentinel used internally would overflow.

func ResolveFiles

func ResolveFiles(args []string) ([]string, error)

ResolveFiles takes positional arguments and returns deduplicated, sorted markdown file paths. It supports individual files, directories (recursive *.md and *.markdown), and glob patterns. Returns an error for nonexistent paths (that are not glob patterns). By default, directory walking respects .gitignore files.

func ResolveFilesWithOpts

func ResolveFilesWithOpts(args []string, opts ResolveOpts) ([]string, error)

ResolveFilesWithOpts is like ResolveFiles but accepts options to control behavior such as gitignore filtering.

func StripFrontMatter

func StripFrontMatter(source []byte) (prefix, content []byte)

StripFrontMatter removes YAML front matter delimited by "---\n" from the beginning of source, forwarding to pkg/markdown so the front-matter split lives in one place. It returns the front matter block (including delimiters) and the remaining content; if no front matter is found, prefix is nil and content equals source.

Types

type Diagnostic

type Diagnostic struct {
	File            string
	Line            int
	Column          int
	RuleID          string
	RuleName        string
	Severity        Severity
	Message         string
	SourceLines     []string // context lines around the diagnostic; empty if unavailable
	SourceStartLine int      // 1-based line number of first entry in SourceLines
	// Explanation, when non-nil, attaches per-leaf provenance for the
	// rule that fired. Populated by the CLI when --explain is on.
	Explanation *Explanation
}

Diagnostic represents a single lint finding.

type Explanation added in v0.7.1

type Explanation struct {
	Rule   string
	Leaves []ExplanationLeaf
}

Explanation describes the provenance of a rule's effective config at the file that produced a diagnostic. It is attached to a Diagnostic when the CLI runs with --explain so the trailer / JSON output can name the rule and the source of each setting that contributed.

type ExplanationLeaf added in v0.7.1

type ExplanationLeaf struct {
	Path   string
	Value  any
	Source string
}

ExplanationLeaf is one leaf setting and the layer that set its final value, formatted for surface output.

type File

type File struct {
	Path        string
	Source      []byte
	Lines       [][]byte
	AST         ast.Node
	FS          fs.FS
	RootFS      fs.FS
	RootDir     string
	FrontMatter []byte
	LineOffset  int

	// StripFrontMatter records whether this file was parsed in
	// front-matter-stripping mode. Rules that read other files
	// from the corpus should mirror the same mode so that line
	// numbers in cross-file diagnostics are computed against the
	// same coordinate system as the current file.
	StripFrontMatter bool

	// MaxInputBytes is the maximum file size in bytes that rules
	// should enforce when reading secondary files (includes, schemas,
	// cross-references). Zero or negative means unlimited.
	MaxInputBytes int64

	// GitignoreFunc is a lazy factory for the gitignore matcher.
	// It is called at most once (on first access via GetGitignore)
	// and the result is cached. Rules that do not call GetGitignore
	// never trigger matcher construction. sync.Once keeps the lazy
	// build race-free if a *File is shared across goroutines.
	GitignoreFunc func() *GitignoreMatcher

	// GeneratedRanges records the content line ranges of generated
	// sections (<?include?> / <?catalog?> bodies). Diagnostics whose
	// line falls within these ranges are suppressed when linting the
	// host file — the source file is responsible for those bytes.
	GeneratedRanges []LineRange

	// RunCache is the engine-owned read cache shared by every File
	// processed in one engine.Run pass. Catalog and include rules
	// consult it before falling back to per-Check Memo so a target
	// globbed by N host-file catalogs is read once per run, not N
	// times. nil for struct-literal Files in unit tests; the
	// catalog rule then takes the per-Check fallback path.
	RunCache *RunCache
	// contains filtered or unexported fields
}

File holds a parsed Markdown document and its source.

func NewFile

func NewFile(path string, source []byte) (*File, error)

NewFile parses source as Markdown and returns a File. The parse itself is delegated to pkg/markdown's pooled canonical parser, so a single goldmark configuration backs every parse path.

func NewFileFromSource

func NewFileFromSource(path string, source []byte, stripFrontMatter bool) (*File, error)

NewFileFromSource creates a File from raw source bytes. When stripFrontMatter is true it strips YAML front matter, stores the prefix in FrontMatter, computes LineOffset via CountLines, and parses only the stripped content.

func (*File) AdjustDiagnostics

func (f *File) AdjustDiagnostics(diags []Diagnostic)

AdjustDiagnostics adds the file's LineOffset to each diagnostic's Line.

func (*File) ColumnOfOffset added in v0.8.0

func (f *File) ColumnOfOffset(offset int) int

ColumnOfOffset converts a byte offset in Source to a 1-based column number on its line.

func (*File) FullSource

func (f *File) FullSource(body []byte) []byte

FullSource prepends the stored FrontMatter to body. It allocates a new slice to avoid mutating FrontMatter's backing array.

func (*File) GetGitignore added in v0.5.0

func (f *File) GetGitignore() *GitignoreMatcher

GetGitignore returns the gitignore matcher for this file, creating it lazily on first call. Returns nil if no GitignoreFunc was configured.

func (*File) LineOfOffset

func (f *File) LineOfOffset(offset int) int

LineOfOffset converts a byte offset in Source to a 1-based line number. The line is 1 plus the number of newlines that occur strictly before offset (a newline exactly at offset starts the next line, so it does not count) — identical to a linear scan, but O(log n) via binary search over the cached newline index.

func (*File) LinkReferences added in v0.20.0

func (f *File) LinkReferences() []Reference

LinkReferences returns the link reference definitions goldmark found in this document. It is computed once and cached. On the normal path it reads the context from the parse NewFile already performed (no extra parse); a File built as a struct literal has no such context, so the first call parses Source once. The returned slice is shared read-only.

func (*File) Memo added in v0.23.0

func (f *File) Memo(key string, build func() any) any

Memo returns the value for key, computing it once via build on the first request within this File's lifetime and serving the cached value thereafter. It exists so a rule whose passes would otherwise recompute the same expensive per-Check derivation can share one result: the catalog directive's resolved entries, for example, are otherwise rebuilt by the generate, injection, and case-mismatch passes — three globs and front-matter reads of every matched file per directive. The File is discarded after each Check, so nothing is cached across files or runs.

build is invoked directly (no wrapping closure) so the call adds no per-Memo-call allocation beyond the cold-path memoEntry itself.

Panic safety mirrors sync.Once: if build panics, the entry is still marked done (via the deferred Store) and the mutex is released (via the deferred Unlock), so the panic propagates without leaving the per-File memo in a deadlocked state. Subsequent calls on the same key serve the zero-value cached result instead of re-running build, matching upstream sync.Once.

func (*File) MemoFile added in v0.23.0

func (f *File) MemoFile(key string, build func(*File) any) any

MemoFile is the *File-passing variant of Memo: build receives this File as an argument instead of capturing it in a closure. Callers whose build needs nothing beyond File data can pass a package- level function value, which avoids the per-call closure allocation the plain `Memo` form forces on every invocation. The hot astutil.CollectSectionParagraphs path is the canonical user.

Panic safety matches Memo's contract: defer Unlock + defer done.Store(true) keep the per-entry mutex from leaking a lock and match sync.Once's "panic still marks done" semantics.

func (*File) SetRootDir added in v0.6.0

func (f *File) SetRootDir(dir string)

SetRootDir configures the project root directory and its fs.FS together.

type GitignoreMatcher

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

GitignoreMatcher checks whether a given path is ignored according to .gitignore rules. It supports multiple .gitignore files at different directory levels, including negation patterns.

func NewGitignoreMatcher

func NewGitignoreMatcher(root string) *GitignoreMatcher

NewGitignoreMatcher creates a matcher by collecting .gitignore files from the given root directory and all its subdirectories. It also looks for .gitignore files in ancestor directories up to the filesystem root.

func (*GitignoreMatcher) IsIgnored

func (m *GitignoreMatcher) IsIgnored(absPath string, isDir bool) bool

IsIgnored returns true if the given absolute path should be ignored. isDir indicates whether the path is a directory.

type LineRange added in v0.7.0

type LineRange struct {
	From int
	To   int
}

LineRange is an inclusive 1-based line range within a source file.

func (LineRange) Contains added in v0.7.0

func (r LineRange) Contains(l int) bool

Contains reports whether the 1-based line l falls within r.

type ProcessingInstruction added in v0.3.0

type ProcessingInstruction = markdown.ProcessingInstruction

ProcessingInstruction is the custom AST block node for <?name ... ?> blocks. It is an alias for the canonical type in pkg/markdown so the linter's many callers (type switches in schema, index, export, rename, gensection, …) keep working without importing the public package directly while the node definition lives in exactly one place.

type Reference added in v0.20.0

type Reference = parser.Reference

Reference is a link reference definition discovered during the parse, re-exported from goldmark so callers of LinkReferences need not import the parser package.

type ResolveOpts

type ResolveOpts struct {
	// UseGitignore enables filtering of walked directories by .gitignore
	// rules. When true (the default), files matched by .gitignore patterns
	// are skipped during directory walking. Explicitly named file paths are
	// never filtered by gitignore. Defaults to true when the zero value is
	// used (see DefaultResolveOpts).
	UseGitignore *bool

	// FollowSymlinks opts in to following symbolic links that
	// resolve to regular files. The zero value skips all symlinks,
	// which is the secure default.
	//
	// Symlinks that resolve to anything other than a regular file
	// are always skipped, regardless of this flag: directories
	// (filepath.Walk is Lstat-based and does not descend a symlink
	// root) as well as FIFOs, devices, and sockets (reading them
	// during linting could block or fail unexpectedly).
	FollowSymlinks bool
}

ResolveOpts controls how file resolution behaves.

func DefaultResolveOpts

func DefaultResolveOpts() ResolveOpts

DefaultResolveOpts returns options with defaults applied.

type RunCache added in v0.23.0

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

RunCache memoizes per-target-file reads (front matter, include adjacency) across every host file processed in one engine.Run pass. Cache keys are absolute filesystem paths, so two host files whose catalogs match the same target share a single read of that target — closing the cross-host redundancy the per-File Memo could not.

The cache is safe for concurrent readers (the parallel file worker pool and the LSP's concurrent request goroutines).

A one-shot mdsmith check sees an immutable corpus, so the cache is trivially safe there. The LSP keeps one RunCache for the server lifetime and calls Invalidate when a document edit could change what the next Check would read from disk.

func NewRunCache added in v0.23.0

func NewRunCache() *RunCache

NewRunCache returns an empty cache ready to be installed on engine.Runner.RunCache.

func (*RunCache) FrontMatter added in v0.23.0

func (c *RunCache) FrontMatter(absPath string, build func() any) any

FrontMatter returns build's result for absPath, computed at most once per absPath in this cache's lifetime. Concurrent callers with the same key block on the same once and observe the same value.

func (*RunCache) Includes added in v0.23.0

func (c *RunCache) Includes(absPath string, build func() []string) []string

Includes returns build's result for absPath. The value is the list of absolute filesystem paths every <?include?> in the file at absPath resolves to. Position-independent so two host files whose f.FS roots differ can still share the cached adjacency.

func (*RunCache) Invalidate added in v0.23.0

func (c *RunCache) Invalidate(absPath string)

Invalidate drops the front-matter and include entries for absPath. The LSP calls this from didChange / didSave / didChangeWatchedFiles so the next Check that crosses absPath re-reads from disk.

type Severity

type Severity string

Severity indicates the severity level of a diagnostic.

const (
	Error   Severity = "error"
	Warning Severity = "warning"
)

Severity levels.

Jump to

Keyboard shortcuts

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