jsslintr checks LaTeX/BibTeX manuscripts against the Journal of Statistical Software (JSS) style guide and can apply many of the fixes automatically. This vignette walks through the typical workflow: lint, inspect, fix, re-lint.
Let’s write a deliberately sloppy two-file manuscript to work with:
writeLines(c(
"\\documentclass[nojss]{jss}",
"\\author{Jane Doe\\\\University of Somewhere}",
"\\title{A great package for doing things in R}",
"\\Abstract{We present a package for R, e.g. for plotting.}",
"\\Keywords{stuff, things}",
"\\begin{document}",
"\\section{introduction}",
"We use R and the ggplot2 package for our p-values.",
"\\end{document}"
), "paper.tex")
writeLines(c(
"@article{smith2020,",
" author = {John Smith},",
" title = {Some Methods for Data},",
" journal = {Journal of Statistical Software},",
" year = {2020},",
" volume = {95},",
" pages = {1--20}",
"}"
), "refs.bib")jsslint() takes the file paths, reads them, and returns
a check result. Printing it gives a quick overview; each line is one
issue in file:line:column form. (Called with no arguments,
jsslint() checks every
.tex/.ltx/.bib file in the
working directory.)
library("jsslintr")
res <- jsslint(c("paper.tex", "refs.bib"))
res
#> JSS style check (journal "jss"): paper.tex, refs.bib
#> Compliance: 75.0%
#>
#> 6 issues (6 warnings; 5 auto-fixable)
#>
#> paper.tex:3:1 [warning] JSS-CAP-001 \title{} is in title style (principal words capitalised)
#> paper.tex:4:36 [warning] JSS-MARKUP-001 Programming-language names in prose are wrapped in \proglang{}
#> paper.tex:4:39 [warning] JSS-HOUSE-001 "e.g." and "i.e." are followed by a comma so LaTeX does not treat the period as a sentence end
#> paper.tex:8:8 [warning] JSS-MARKUP-001 Programming-language names in prose are wrapped in \proglang{}
#> paper.tex:8:18 [warning] JSS-MARKUP-002 Software-package names in prose are wrapped in \pkg{}
#> paper.tex:8:42 [warning] JSS-OPER-001 Symbol-plus-noun constructs like p-value and t-statistic are typeset as $p$~value and $t$~statistic (tie, no hyphen)
#>
#> Use summary() for a breakdown by rule and category, and jssfix() to apply the 5 automatic fixes.summary() breaks the issues down by rule, file, and
style-guide category:
summary(res)
#> JSS style check (journal "jss"): paper.tex, refs.bib
#> Compliance: 75.0% -- 6 issues (6 warnings; 5 auto-fixable)
#>
#> Files:
#> file error warning info total
#> paper.tex 0 6 0 6
#> refs.bib 0 0 0 0
#>
#> Issues by rule:
#> rule_id severity issues fixable guide_section
#> JSS-MARKUP-001 warning 2 2 §4.1 Markup
#> JSS-CAP-001 warning 1 0 §6.2 Capitalisation
#> JSS-HOUSE-001 warning 1 1 §8.1 House style
#> JSS-MARKUP-002 warning 1 1 §4.1 Markup
#> JSS-OPER-001 warning 1 1 §4.4 Mathematical notation
#>
#> Categories:
#> category status rules_passed issues
#> Preamble PASS 8/8 0
#> Structure PASS 6/6 0
#> Markup FAIL 2/4 3
#> Citations PASS 3/3 0
#> References PASS 6/6 0
#> BibTeX PASS 5/5 0
#> Naming PASS 2/2 0
#> Capitalization FAIL 2/3 1
#> Typography PASS 4/4 0
#> Abbreviations PASS 1/1 0
#> Code style PASS 3/3 0
#> Code width PASS 1/1 0
#> Operators FAIL 3/4 1
#> Cross-references PASS 7/7 0
#> House style FAIL 2/3 1
#> Project PASS 2/2 0For programmatic access — filtering, counting, or feeding issues into
your own tooling — as.data.frame() gives one row per issue,
including the suggested rewrite and whether an automatic fix exists:
df <- as.data.frame(res)
df[df$fixable, c("file", "line", "rule_id", "suggestion")]
#> file line rule_id
#> 2 paper.tex 4 JSS-MARKUP-001
#> 3 paper.tex 4 JSS-HOUSE-001
#> 4 paper.tex 8 JSS-MARKUP-001
#> 5 paper.tex 8 JSS-MARKUP-002
#> 6 paper.tex 8 JSS-OPER-001
#> suggestion
#> 2 Wrap 'R' in \\proglang{R}.
#> 3 Add a comma after 'e.g.': 'e.g.,'.
#> 4 Wrap 'R' in \\proglang{R}.
#> 5 Wrap 'ggplot2' in \\pkg{ggplot2}.
#> 6 Replace 'p-values' with '$p$~values' (italicized symbol, tie).jssfix() applies the automatic fixes in place. Pass the
check result (its files and options are reused) or file paths directly.
With dry_run = TRUE it only shows the diff of what would
change:
jssfix(res, dry_run = TRUE)
#> --- paper.tex
#> +++ paper.tex
#> @@ -1,9 +1,9 @@
#> \documentclass[nojss]{jss}
#> \author{Jane Doe\\University of Somewhere}
#> \title{A great package for doing things in R}
#> -\Abstract{We present a package for R, e.g. for plotting.}
#> +\Abstract{We present a package for \proglang{R}, e.g., for plotting.}
#> \Keywords{stuff, things}
#> \begin{document}
#> \section{introduction}
#> -We use R and the ggplot2 package for our p-values.
#> +We use \proglang{R} and the \pkg{ggplot2} package for our $p$~values.
#> \end{document}
#>
#> Dry run: 5 fixes would be applied to 1 file. Re-run with dry_run = FALSE to write.That looks right, so let it write the files:
jssfix(res)
#> Applied 5 fixes to 1 file:
#> paper.tex: 5 fixes
#> Re-run jsslint() to review the remaining issues.Fixes are applied atomically, and a fix that would reintroduce its
own violation is rolled back. Overlapping fixes are resolved to one
winner per span; if any were skipped as conflicting, running
jssfix() again picks them up. A rules argument
restricts fixing to selected rules, e.g.,
jssfix(res, rules = "JSS-HOUSE-001").
The remaining issues need manual attention — the printed suggestions say what to do:
jsslint(c("paper.tex", "refs.bib"))
#> JSS style check (journal "jss"): paper.tex, refs.bib
#> Compliance: 87.5%
#>
#> 2 issues (2 warnings; 0 auto-fixable)
#>
#> paper.tex:3:1 [warning] JSS-CAP-001 \title{} is in title style (principal words capitalised)
#> paper.tex:8:29 [warning] JSS-CITE-002 First occurrence of a software package has a citation within the same paragraph
#>
#> Use summary() for a breakdown by rule and category.render() is the bare in-memory binding underneath
jsslint(): it takes a named character vector (names =
paths, values = contents) and returns a report rendered as
"terminal", "json", "sarif", or
"html" text — useful when the files never touch disk or you
want machine-readable output:
cat(substr(render(c("paper.tex" = "\\documentclass{article}"),
output = "json"
), 1, 300), "...\n")
#> {
#> "categories": [
#> {
#> "category_id": "preamble",
#> "rules_applied": 8,
#> "rules_passed": 7,
#> "status": "FAIL",
#> "title": "Preamble"
#> },
#> {
#> "category_id": "structure",
#> "rules_applied": 6,
#> "rules_passed": 6,
#> "status": "PASS",
#> "title": "St ...