Skip to content

Configuration

Configuration is optional. This is all of it.

defaults:
  unexported: private     # package | private

rules:
  naming:
    qualify: never        # always | never | ondemand
    exported: false       # true | false
    vocabulary:
      mouse: [wheel]
  boundary: on           # off | on
  surplus: loose         # off | loose | strict

filter:
  only: []              # nothing outside these, when set
  omit:
    - "**/mock_*.go"    # and not these

baseline: .declscope-baseline.yaml
Key Values Default Effect
defaults.unexported package, private private Scope of a declaration that carries no directive and inherits none
rules.naming.qualify always, never, ondemand never When a name must carry its namespace. See the naming rule
rules.naming.exported true, false false Whether the naming rule also reaches exported declarations. The rename is never offered there
rules.naming.vocabulary Namespace to a list of words None Extra words that carry a namespace. See what carries a namespace
rules.boundary off, on on Whether the boundary rule reports. See reach without a boundary
rules.surplus off, loose, strict loose How much the surplus rule reports. See strict
filter.only Path globs None When set, no file outside them is read. Empty places no restriction
filter.omit Path globs None Files taken back out, whether or not only let them through
baseline A path relative to the config file The nearest .declscope-baseline.yaml The baseline to consult
  • The file is .declscope.yaml or .declscope.yml. An empty one changes nothing.
  • -config names one file, and reads no other.
  • An unknown key is an error, and so is a value a key does not accept. The message names what the section does take, so a typo never leaves a rule at its default in silence.

defaults takes only unexported, because an exported declaration has no scope to default.

How two config files compose

Every .declscope.yaml between the analyzed package and the module root is read, outermost first. A nearer file does not replace the one above it. Each key composes on its own.

Key Down the chain
defaults.*, rules.* except vocabulary, baseline The nearest file that states the key wins. A key no file states takes the built-in default
rules.naming.vocabulary Merged per namespace. The nearer file wins the namespaces it states
filter.only Intersected. A file is read when it matches every stating file's list
filter.omit Unioned. A file matching any level's list is not read

Important

A config file can only ever shrink what is read. An omit written at the root holds everywhere below it, and no nested file can undo it. filter has no negation, so nothing like .gitignore's ! can put a path back.

The filter rule

filter decides which files are read at all. A file matches a list when it matches any pattern in it.

Written Read
Neither Every file
only alone Nothing outside the patterns
omit alone Everything except the patterns
Both What only admits, minus what omit names

An empty only places no restriction, which is why a repository with no config is read whole.

A pattern is read against the directory of the config file that states it. Anchoring follows the rules of .gitignore.

Pattern Matches
gen.go A file of that name at any depth
/gen.go The one beside this config file
gen/** That directory beside this config file, and no other
**/gen/** That directory at any depth
*, ? Within one path segment
Why patterns are anchored where they are
  • A pattern holding a separator is anchored whether or not it starts with one, so /gen/** and gen/** are the same rule. The leading / matters only on a bare name, which would otherwise match at any depth.
  • The same line means different things in different files. internal/tui/** in the root config reaches internal/tui. In internal/.declscope.yaml it reaches internal/internal/tui.
  • A .. in a pattern is an error. A pattern cannot leave its own directory.
  • The origin is the config file's directory, not the module root. A config governs only the packages below it. A pattern anchored higher than that could only name files that never read this config.

One report comes from the configuration rather than from the code. It fires when a file's only matches files, but an only above it removes them all, so the package is read as empty.

sub/a.go:1:1: filter.only stated in /repo/sub matches 1 file(s) here, but an only above it removes them all, so this package is read as empty

The report is that narrow on purpose. A package that reads nothing is usually the point, as with a root only naming one subtree, or an omit naming a directory.