diff --git a/CHANGELOG.md b/CHANGELOG.md index 19e4773e063..9547ae04bd8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ * ![Enhancement][badge-enhancement] If the TeX compilation fails for the PDF/LaTeX output, `makedocs` now throws an exception. ([#1166](github-1166)) +* ![Bugfix][badge-bugfix] `LaTeXWriter` now outputs valid LaTeX if an `@contents` block is nested by more than two levels, or if `@contents` or `@index` blocks do not contain any items. ([#1166](github-1166)) + * ![BREAKING][badge-breaking] Documenter no longer creates a symlink between the old `latest` url to specified `devurl`. Make sure to update links in e.g. the package readme. ([#1151][github-1151]) ## Version `v0.23.4` diff --git a/src/Writers/LaTeXWriter.jl b/src/Writers/LaTeXWriter.jl index 8ca7eaceafe..de3137c9619 100644 --- a/src/Writers/LaTeXWriter.jl +++ b/src/Writers/LaTeXWriter.jl @@ -140,7 +140,9 @@ function render(doc::Documents.Document, settings::LaTeX=LaTeX()) # Debug: if DOCUMENTER_LATEX_DEBUG environment variable is set, copy the LaTeX # source files over to a directory under doc.user.root. if haskey(ENV, "DOCUMENTER_LATEX_DEBUG") - sources = cp(pwd(), mktempdir(doc.user.root), force=true) + dst = isempty(ENV["DOCUMENTER_LATEX_DEBUG"]) ? mktempdir(doc.user.root) : + joinpath(doc.user.root, ENV["DOCUMENTER_LATEX_DEBUG"]) + sources = cp(pwd(), dst, force=true) @info "LaTeX sources copied for debugging to $(sources)" end @@ -303,6 +305,10 @@ end ## Index, Contents, and Eval Nodes. function latex(io::IO, index::Documents.IndexNode, page, doc) + # Having an empty itemize block in LaTeX throws an error, so we bail early + # in that situation: + isempty(index.elements) && (_println(io); return) + _println(io, "\\begin{itemize}") for (object, _, page, mod, cat) in index.elements id = string(hash(string(Utilities.slugify(object)))) @@ -316,22 +322,40 @@ function latex(io::IO, index::Documents.IndexNode, page, doc) end function latex(io::IO, contents::Documents.ContentsNode, page, doc) + # Having an empty itemize block in LaTeX throws an error, so we bail early + # in that situation: + isempty(contents.elements) && (_println(io); return) + depth = 1 - needs_end = false _println(io, "\\begin{itemize}") for (count, path, anchor) in contents.elements header = anchor.object level = Utilities.header_level(header) id = string(hash(string(anchor.id, "-", anchor.nth))) - level < depth && (_println(io, "\\end{itemize}"); needs_end = false) - level > depth && (_println(io, "\\begin{itemize}"); needs_end = true) + # If we're changing depth, we need to make sure we always print the + # correct number of \begin{itemize} and \end{itemize} statements. + if level > depth + for k in 1:(level - depth) + # if we jump by more than one level deeper we need to put empty + # \items in -- otherwise LaTeX will complain + (k >= 2) && _println(io, "\\item ~") + _println(io, "\\begin{itemize}") + depth += 1 + end + elseif level < depth + for _ in 1:(depth - level) + _println(io, "\\end{itemize}") + depth -= 1 + end + end + # Print the corresponding \item statement _print(io, "\\item \\hyperlink{", id, "}{") latexinline(io, header.text) _println(io, "}") - depth = level + @assert depth == level end - needs_end && _println(io, "\\end{itemize}") - _println(io, "\\end{itemize}") + # print any remaining missing \end{itemize} statements + for _ = 1:depth; _println(io, "\\end{itemize}"); end _println(io) end diff --git a/test/examples/make.jl b/test/examples/make.jl index 672943858eb..6628b04cff0 100644 --- a/test/examples/make.jl +++ b/test/examples/make.jl @@ -160,6 +160,7 @@ htmlbuild_pages = Any[ "expandorder/AA.md", ], "unicode.md", + "latex.md", ] # Build with pretty URLs and canonical links and a PNG logo @@ -260,6 +261,46 @@ else nothing end +examples_latex_doc = if "latex" in EXAMPLE_BUILDS + @info("Building mock package docs: LaTeXWriter/latex") + @quietly makedocs( + format = Documenter.Writers.LaTeXWriter.LaTeX(platform = "docker"), + sitename = "Documenter LaTeX", + root = examples_root, + build = "builds/latex", + pages = htmlbuild_pages = Any[ + "General" => [ + "index.md", + "latex.md", + "unicode.md", + hide("hidden.md"), + ], + # man/tutorial.md can't be built because it contains SVG images + # "Manual" => ["man/tutorial.md"], + hide("Hidden Pages" => "hidden/index.md", Any[ + "Page X" => "hidden/x.md", + "hidden/y.md", + "hidden/z.md", + ]), + "Library" => [ + "lib/functions.md", + "lib/autodocs.md", + "lib/editurl.md", + ], + "Expandorder" => [ + "expandorder/00.md", + "expandorder/01.md", + "expandorder/AA.md", + ], + ], + doctest = false, + debug = true, + ) +else + @info "Skipping build: LaTeXWriter/latex" EXAMPLE_BUILDS get(ENV, "DOCUMENTER_TEST_EXAMPLES", nothing) + nothing +end + examples_latex_simple_nondocker_doc = if "latex_simple_nondocker" in EXAMPLE_BUILDS @info("Building mock package docs: LaTeXWriter/latex_simple_nondocker") @quietly makedocs( diff --git a/test/examples/src/latex.md b/test/examples/src/latex.md new file mode 100644 index 00000000000..be5b7ef6d69 --- /dev/null +++ b/test/examples/src/latex.md @@ -0,0 +1,22 @@ +# LaTeX MWEs + +## `ContentsNode` level jumps + +```@contents +Pages = ["latex.md"] +Depth = 9000 +``` + +#### Level 4 +## Level 2 again +### Level 3 + +## Empty `ContentsNode` and `IndexNode` + +```@contents +Pages = ["does-not-exist.md"] +``` + +```@index +Pages = ["does-not-exist.md"] +``` diff --git a/test/examples/tests_latex.jl b/test/examples/tests_latex.jl index a4925923dd4..cd7ff59c765 100644 --- a/test/examples/tests_latex.jl +++ b/test/examples/tests_latex.jl @@ -24,4 +24,12 @@ end @test joinpath(build_dir, "DocumenterLaTeXSimple.pdf") |> isfile end end + + @testset "PDF/LaTeX" begin + doc = Main.examples_latex_doc + @test isa(doc, Documenter.Documents.Document) + let build_dir = joinpath(examples_root, "builds", "latex") + @test joinpath(build_dir, "DocumenterLaTeX.pdf") |> isfile + end + end end