Rules¶
Rules are specified in rule files using YAML. Each file may define one rule at most. There are seven rule types which determine the general characteristics of the performed check and any user-defined rule must be based on one of these types.
Note
A rule is violated if the condition it defines matches at any point in the linted text.
Severity Levels¶
There are three severity levels: error, warning and suggestion. You
may set up a threshold to filter the output and suppress undesired violations
using the --severity option. By default, the threshold is set to
suggestion which provides full output. If instead the severity is lowered to
warning, this would suppress suggestions but still display errors and
warnings.
Message Strings¶
When a rule is violated, a message is displayed providing the user with
information about the matching text. The message string is defined in the rule
file together with the other parameters and may have static and dynamic
content. The dynamic content is controlled by format specifiers such as $1
and $2. These entries indicate where to insert the match’s dynamic content.
Precisely what constitutes dynamic content depends on the rule type but a
common field is the matching text. Consider the following string,
Please remove '$1'.
If the rule matches the word ‘foo’, the message presented to the user will be
Please remove 'foo'.
The remaining sections will demonstrate the use of message strings in the context of each rule type.
Note
Strings without quotation marks and strings enclosed in single quotation
marks (') are interpreted literally, i.e. \n will print the two
characters \ and n. Strings enclosed in double quotation marks
(") will interpret the contents using escape sequences.
Scopes¶
A rule may define a scope section to specify that the rule should only be
enforced in certain parts of the text. The section expects a list of scope
identifiers (as strings), and the available identifiers and their meaning
depends on the linter. Identifiers unknown to the active linter have no effect:
scope:
- paragraph # Only used by the plain text linter.
- comment # Only used by the LaTeX linter.
The sections below define the scope identifiers known to each linter.
Plain text¶
Label |
Target |
|---|---|
|
Entire document |
|
Each paragraph (see the occurrence rule) |
LaTeX¶
There is a rule file section specific to the LaTeX linter. The identifiers in the table below can be considered shorthand for the more general way of specifying scopes in a LaTeX document.
Label |
Target |
|---|---|
|
Text between |
|
Comments, i.e. text starting from the character |
|
Math environments:
|
|
Control sequences used to define section titles:
|
Exceptions¶
A rule may define an exceptions section, listing exceptions to the rule. The
section contents should be a list of strings and a minimum of one entry is
required. The list entries may be regular expressions.
exceptions:
- this is fine
- this too
Using a regular expression:
exceptions:
- this (is fine|too)
If a rule matches at any point in the linted text, the match is checked against the exceptions before a violation is generated.
Linter¶
A rule may define a linter section to specify that the rule should only be
enabled when the target linter is being used. Currently, there are two linters
available, identified as plain and latex. The linter section accepts
a list of these identifiers. For example,
linter:
- latex
would cause the rule to only be used by the LaTeX linter. Conversely,
linter:
- plain
would only enable the rule when the plain text linter is used. By default, the rule is used by all the linters.
LaTeX¶
Each rule may define a latex section to specify in which context the rule
should be enforced when the LaTeX linter is used. The section consists of a list
of scope entries where each entry accepts the following fields:
Label |
Description |
Default |
|---|---|---|
|
The name of the document element to match. Cannot be a regular expression. |
N/A |
|
The type of document element to match:
|
N/A |
|
Regular expression with access to the raw text leading up to the scope entry (see contexts). This raw text is limited to 20 characters. |
An empty string |
|
A scope entry where For example, we could define a rule saying
that a |
|
|
The Whether to enforce the rule or not is determined according to:
where
As a special case, if the list only
consists of entries marked with |
|
Note
Fields with default values are optional.
Let us look at an example:
latex:
- name: foo
type: control sequence
leading: required\s$
- name: bar
type: environment
logic: and
- name: baz
type: control sequence
logic: and
The scope defined by the section above will enforce the rule for
any text inside the
\foocontrol sequence, provided it is preceded by the string “required” followed by a space character (note the$character anchoring the regular expression to the end of the text), i.e.Some introductory text is required \foo{to cause the rule to be enforced in here}{and here too} but \foo{the rule is not enforced in here}.
any text inside both the
barenvironment and thebazcontrol sequence, i.e.The rule will \baz{not be enforced here} \begin{bar} and not here either. \baz{However, this text will be targeted by the rule.} \end{bar}
Note
Additional examples of rules specific to the LaTeX linter can be found here.
Existence¶
The existence rule checks for the presence of any of its tokens and
reports a violation if there’s a match in the linted text. The message
string will be supplied the matching text as a replacement field.
extends: existence
message: "Consider removing '$1'."
ignorecase: true
level: warning
tokens:
- foo
- bar
The rule definition above translates to the regular expression
(?i)\b(foo|bar)\b, where \b indicates a word boundary and (?i) is
the case insensitivity modifier. The nonword field (boolean) may be
specified to instead match anywhere in the text.
The raw field may be used to gain access to the regular expression directly
whereby any listed item is prepended (in the order they appear) to the final
expression. Consider the following example which defines a rule to catch a few
uncomparables.
extends: existence
message: "'$1' is not comparable."
ignorecase: true
level: error
raw:
- \b(?:most|more|less|least|very)\b\s*
tokens:
- absolute
- adequate
- complete
- unique
Here, the resulting regular expression will be
(?i)\b(?:most|more|less|least|very)\b\s*\b(absolute|adequate|complete|unique)\b
which will catch occurrences of “very unique”, “less complete” etc.
Inverting the Matching Logic¶
The invert field (false by default) causes the matching logic to be
inverted if set to true, i.e. if the text segment does not contain any of
the listed tokens, a violation is generated. One important detail is that
when invert is true, the message is not provided any text to match
the replacement field $1.
Important
When invert is true, the message is not provided any text to
match the replacement field $1.
Substitution¶
The substitution rule checks for the presence of any of the keys defined in
its key-value list swap and reports a violation if there’s a match in the
linted text. The message string will be provided the key and value of
the matching swap entry as format specifiers $1 and $2,
respectively.
extends: substitution
message: "Prefer '$2' over '$1'."
ignorecase: true
level: warning
swap:
catch on fire: catch fire
'(cell phone|cell-phone)': cellphone
Keys are interpreted as regular expressions and word boundaries (\b) are
added unless the nonword field is set to true. If the regular expression
defines multiple capture groups, the first group will be used for the message
replacement text. Non-capturing groups (?: may be used to modify the
behavior as needed.
Lastly, there is one additional feature to this rule: if the expression given as the key matches text which is already equal to the substitution value, the violation is ignored. This is needed to write compact key expressions which sometimes cover the ‘correct’ case in addition to all error cases. For example,
swap:
analog[ -]to[ -]digital: analog-to-digital
covers all the error combinations with one single regular expression, but also covers the correct case. This feature prevents the latter from being reported as a violation.
Occurrence¶
The occurrence rule enforces a requirement on the maximum/minimum number of
times a token may/should occur in a particular scope. The
message string for this rule doesn’t accept a format specifier.
extends: occurrence
message: "Don't use 'however' more than once in one paragraph."
level: suggestion
ignorecase: true
scope:
- paragraph
limit: 1
limit_kind: max
token: '\bHowever\b'
Repetition¶
The repetition rule checks for repetitions of its tokens. The tokens are
converted to lowercase if the ignorecase field is set to true. In
contrast to the occurrence rule, this rule counts unique matches. That means
that while the token '\b(\w+)\b' will match both ‘foo’ and ‘bar’, a the rule
is not violated until ‘foo’ or ‘bar’ is repeated again in the target scope.
The matching token is provided as input to the message string.
extends: repetition
message: "'$1' is repeated."
level: warning
ignorecase: true
scope:
- paragraph
token: '\b(\w+)\b'
Consistency¶
The consistency rule checks for occurrences of either the key or the value
specified as key-value pairs in its either list. For each pair, the earliest
match in the linted text is assumed to be the preferred version and occurrences
of its undesired counterpart will generate a rule violation.
extends: consistency
message: "Inconsistent spelling of '$1'."
level: error
ignorecase: true
scope:
- text
either:
organize: organise
recognize: recognise
analog: analogue
The keys and values may be general regular expressions but unless the
nonword field is set to true, word boundary tokens \b are added to
the expression. The message string will be supplied the matching text as a
replacement field.
We can generalize the example above as:
extends: consistency
message: "Inconsistent spelling of '$1'."
level: error
ignorecase: true
scope:
- text
either:
(?:\w+)nize: (?:\w+)nise
(?:\w+)log: (?:\w+)logue
Conditional¶
The conditional rule checks that first occurs before second in the
given scope. In the case of a violation, the match for second is
provided as input to the message string.
extends: conditional
message: "'$1' found without finding 'foo'."
level: warning
ignorecase: true
scope:
- text
first: 'foo'
second: '(bar|baz)'
Definition¶
Documentation coming soon.