Commit Graph
19314 Commits
Author SHA1 Message Date
John MacFarlaneandClaude cbbbf312ea Text.Pandoc.Class.PandocMonad: avoid copying input in toTextM.
Skip the CR-filtering copy when the input contains no CRs,
as already done in Text.Pandoc.UTF8.toText.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-09 17:38:41 +00:00
John MacFarlaneandClaude 762d6384b2 Text.Pandoc.UTF8: make readFile exception-safe.
Use withFile instead of openFile so the handle is closed even if
reading throws. (B.hGetContents closes the handle on successful
end-of-file, but an exception mid-read would previously leak it.)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-09 17:26:54 +00:00
John MacFarlaneandClaude ad7b7f9fd9 Text.Pandoc.UTF8: avoid copying input when it contains no CRs.
toText and toTextLazy unconditionally ran a CR-removing filter over
the input, allocating a full copy of the document even in the common
case where no CRs are present. Check for a CR first (B.elem, a fast
memchr) and reuse the input buffer unchanged if none is found; for
the lazy variant, do this chunk-wise to preserve laziness.

On a 10 MB LF-only input this makes toText over 4x faster; when CRs
are present the extra scan is not measurable.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-09 16:43:52 +00:00
zenor0 2666fbab72 docs: fix fill syntax in Typst property examples (#11855) 2026-09-08 22:44:58 -07:00
John MacFarlaneandClaude 0e399cc799 Update test for raw_html-aware inline <style> handling.
With raw_html disabled (the default for the HTML reader), an inline
<style> element is now ignored rather than passed through as a
RawInline; the raw_html-enabled behavior is still tested.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-09 03:12:53 +00:00
John MacFarlaneandClaude 94caebb0a9 HTML reader: fix pre/code attribute precedence for first-wins dedup.
pCodeBlock put the code element's attributes first when merging,
relying on the old last-duplicate-wins behavior of toStringAttr to
give the pre element's attributes precedence.  Now that the first
duplicate wins, put pre's attributes first.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-09 03:10:04 +00:00
John MacFarlaneandClaude c1f46cd891 HTML reader: pass the tag name to pSpanLike.
The inline dispatcher already knows which span-like element it is
looking at, so there is no need for pSpanLike to try a parser for
every element in htmlSpanLikeElements.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-09 03:08:25 +00:00
John MacFarlaneandClaude ba416103e7 htmlTag: don't copy the remaining input on each invocation.
A space was appended to the remaining input to guarantee a
TagPosition token after the parsed tag; since the input is a strict
Text, this copied the entire remaining input every time htmlTag was
called (e.g. for every inline HTML tag in a markdown document),
giving quadratic behavior in tag-dense documents.  Instead, handle
the case where the tag is the final token by computing the
end-of-input position directly.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-09 03:07:14 +00:00
John MacFarlaneandClaude 26e3404455 HTML reader: limit iframe nesting depth.
pIframe fetches the iframe's src and parses it recursively; a
cycle of iframes embedding each other caused infinite recursion.
Track the nesting depth and skip iframes (with a warning) more
than 5 levels deep.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-09 03:05:33 +00:00
John MacFarlaneandClaude 4be508863c HTML reader: find list style anywhere in the class attribute.
Previously `<ol class="fancy lower-roman">` got DefaultStyle,
because the whole class attribute was compared against the known
style names.  Check each class individually.  As a side effect, an
unrecognized class no longer prevents falling back to the style
attribute.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-09 03:03:51 +00:00
John MacFarlaneandClaude 729a66fa9a HTML reader: report the noteref position for unresolved notes.
The ReferenceNotFound warning was logged after the whole document
had been parsed, so it always pointed at the end of the input.
Record the position of the first reference to each note and use it
in the warning.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-09 03:03:07 +00:00
John MacFarlaneandClaude 184e114982 HTML reader: use a Map for the note table.
Avoids a linear scan per noteref (quadratic overall in the number
of notes).  As before, the most recently parsed note with a given
identifier wins.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-09 03:01:37 +00:00
John MacFarlaneandClaude ef16134d91 HTML reader: respect raw_html for inline <style> elements.
Inline <style> elements (invalid but common, see #10643) were
always turned into RawInline, even with the raw_html extension
disabled.  Skip them with a warning in that case, as is already
done for block-level style elements.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-09 03:00:18 +00:00
John MacFarlaneandClaude 910130eff3 HTML reader: let the first of duplicate attributes win.
toStringAttr deduplicated attributes with a right fold, so the last
occurrence of a duplicated attribute was kept.  HTML specifies that
the first occurrence wins (and that lang takes precedence over
xml:lang).  Deduplicate left to right with a seen-set instead.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-09 02:59:30 +00:00
John MacFarlaneandClaude 8ba5f27673 HTML reader: don't drop cells when there are too few <col> elements.
The column specs are zipped with the computed alignments, so a
<col> list shorter than the actual number of columns truncated the
colspecs, and the table builder then dropped the cells in the extra
columns.  Pad the width list with ColWidthDefault instead.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-09 02:58:41 +00:00
John MacFarlaneandClaude 6b14302c16 HTML reader: allow omitted </tr> in tables.
The </tr> closing tag is optional in HTML.  Previously pRow and
pHeaderRow required it, so a table using the omitted form was
silently degraded to a sequence of plain blocks.  The closing tag
is now optional; pHeaderRow instead checks that the row isn't
followed by a <td> cell, so body rows are still parsed by pRow.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-09 02:58:08 +00:00
John MacFarlaneandClaude a88ef25605 HTML reader: don't require a closing tag for checkbox inputs.
`<input>` is a void element, so `<input type="checkbox">` produces
no TagClose token.  Previously pCheckbox required one and failed
after consuming the open tag, silently destroying the surrounding
list structure.  Use pSelfClosing, as with `<br>`.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-09 01:02:54 +00:00
John MacFarlane 1b175477a7 updateLastStrPos: add a bang pattern.
This reduces allocations by a small amount.
2026-09-08 17:34:27 -07:00
John MacFarlaneandClaude ff53ffe73a LaTeX reader: keep nested conditionals balanced in \iftrue etc.
The parser for \iftrue, \iffalse, and \ifmmode stopped at the first
\else or \fi it saw, so a nested conditional inside a skipped branch
(e.g. an unsupported primitive like \ifdim) made the outer
conditional end early and leaked tokens from the skipped branch.
Replace the sub-parse with a pure token scan that tracks nesting
depth, recognizing TeX's primitive conditionals as well as
\newif-defined ones, and (like TeX) does not expand tokens while
skipping a branch.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-08 16:10:11 -07:00
John MacFarlaneandClaude cd949780c6 LaTeX reader: don't discard state changes made in optional arguments.
The `opt` parsers in LaTeX.hs and Citation.hs parsed the bracketed
tokens in a separate runParserT call, discarding any state changes
(macro definitions, labels, notes) made while parsing them.  Since
an optional argument is not a TeX group, such changes should persist.
Use parseFromToks, which runs the sub-parse in the same parser run.
This also removes a per-optional-argument state copy.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-08 16:10:11 -07:00
John MacFarlaneandClaude 63a1710cd4 LaTeX reader: keep ASCII quotes when ligatures are disabled.
Commit 47610fe3d (#10781) disabled the `--`, `---`, and `"` ligatures
inside `\texttt`, but single `` ` `` and `'` characters were still
converted to curly quotes, so `\texttt{it's}` produced code containing
a Unicode right single quotation mark.  Gate those conversions on the
ligatures flag too, so quote characters inside `\texttt` stay as
typed.  Update test/command/3958.md accordingly.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-08 16:10:11 -07:00
John MacFarlaneandClaude ce060e98e6 LaTeX reader: build command dispatch maps once per parse.
The dispatch maps (inlineCommands, blockCommands, environments) have
types that mention the parser monad, so they are compiled as functions
of the PandocMonad dictionary and were being rebuilt from scratch --
hundreds of Map entries -- every time a command or environment was
dispatched.  SPECIALIZE pragmas can't fix this on the CLI path, since
the reader table abstracts over the monad dictionary.

Instead, add a ReaderT layer to the LP monad carrying a LaTeXEnv
record with the three dispatch maps, built once per parse in readLaTeX
(and once per chunk in rawLaTeXParser's callers), and consult it via
askEnv at the dispatch sites.  Internal `lift $ runParserT` sub-parses
share the environment automatically.

Benchmark (1.8MB LaTeX file): allocations drop from 17.85GB to 9.97GB
and wall time from ~4.7s to ~3.4s.  On a stress test of 100k \emph
commands, allocations drop from 36GB to 10.5GB.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-08 16:10:11 -07:00
John MacFarlaneandClaude 4f987038c4 LaTeX reader: skip doMacros state update for non-macro tokens.
doMacros runs before every token consumption; when the head of the
stream is not a control sequence no expansion can occur, so avoid
scanning and rewriting the input stream (which allocated a new
TokStream per consumed token) in that case.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-08 16:10:11 -07:00
John MacFarlaneandClaude 4dcf5d5db3 LaTeX reader: peek at next token directly in peekTok.
peekTok went through lookAhead (satisfyTok (const True)), paying
for tokenPrim position bookkeeping, the raw-token state update, and
lookAhead state save/restore on every call; peekTok is used by the
main inline and block dispatchers for every element.  Just examine
the head of the input stream after expanding macros.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-08 16:10:11 -07:00
John MacFarlaneandClaude 0a46c96458 LaTeX reader: fail macroDef fast on non-macro-defining commands.
macroDef is tried for every control sequence in inline and block
context, and previously attempted ~30 controlSeq alternatives in
turn before failing.  Peek at the next control sequence and check
membership in a Set of macro-defining command names first, so the
common case fails after a single lookup.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-08 16:10:11 -07:00
John MacFarlaneandClaude 8b209f1b04 LaTeX reader: make untokenize linear instead of quadratic.
untokenize used foldr with Text (<>), which repeatedly copies the
accumulated suffix and is quadratic in the total text length.  Use
T.concat on a list of chunks instead, preserving the space
insertion after control words (#5836).  Simplify untoken to return
the token text directly (equivalent to the old behavior with an
empty accumulator).

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-08 16:10:11 -07:00
John MacFarlaneandClaude 7b93dea386 LaTeX reader: make raw token capture O(1) per token.
Previously satisfyTok updated an IntMap of reversed token lists,
one entry per active withRaw scope, so every consumed token cost
O(number of nested withRaw scopes) time and allocation, and the
state was updated even when no withRaw was pending.  Replace the
IntMap with a single accumulated list plus a count and a counter of
active scopes: satisfyTok now conses at most once per token (and
does nothing when no scope is active); withRaw takes its slice of
the accumulated list by count difference.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-08 16:10:11 -07:00
John MacFarlaneandClaude 113568e6e2 LaTeX reader: fix doubled source positions in retokenizeComment.
`tokenize pos` already produces tokens positioned relative to pos,
but the result was then shifted again by pos's line and column,
roughly doubling the reported positions of tokens retokenized from
comments in URL arguments.  Tokenize from the position just after
the '%' instead, and drop the extra shift.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-08 16:10:11 -07:00
John MacFarlaneandClaude 1cffc95ceb LaTeX reader: fix position off-by-one after ## in tokenizer.
When tokenizing `##` not followed by digits, the continuation
restarted at column pos+1 instead of pos+2, shifting the source
positions of all subsequent tokens on the line one column left.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-08 16:10:11 -07:00
John MacFarlaneandClaude 0a3bcc4cc4 LaTeX reader: require that \newif names begin with "if".
Previously \newif\foobar would blindly do `T.drop 2` on the name,
defining a bogus \foobar -> \iffalse plus junk "obartrue"/"obarfalse"
macros; the \iffalse expansion could then swallow following text up
to \fi.  Now newif checks the "if" prefix (as TeX requires) and
otherwise falls through to normal unknown-command handling.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-08 16:10:11 -07:00
John MacFarlaneandClaude 4131ea907a LaTeX reader: fix unescapeURL handling of escaped backslash.
unescapeURL used T.splitOn "\\", which conflates consecutive
backslashes, so "\\" in a URL argument was left as two backslashes
even though '\' is in the escapable set.  Rewrite as a direct scan:
a backslash followed by an escapable character is unescaped, and
"\\\\" now yields a single backslash.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-08 16:10:11 -07:00
John MacFarlaneandClaude 9de0d6b630 LaTeX reader: fix \qed to produce U+00A0 (nbsp) instead of BEL.
The string literal "\a0\x25FB" parses as BEL + '0' + WHITE MEDIUM
SQUARE; the intent (cf. qedSign in T.P.Readers.LaTeX.Math) was
"\xa0\x25FB", a non-breaking space before the square.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-08 16:10:11 -07:00
John MacFarlane f7c55b6c55 Use dev version of asciidoc-hs.
Big performance improvements.

In addition, treatment of tables now aligns with asciidoctor;
the first line is only implicitly treated as a header if it
is on a line by itself, followed by a blank line.
2026-09-08 14:44:20 -07:00
John MacFarlaneandClaude abd98ee4ab Improve performance of uriScheme by using a trie.
Up to 15% faster in URL-heavy documents with `autolink_bare_uris`.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-08 16:39:52 +00:00
John MacFarlane c54ad9ef75 Typst writer: emit label for bare table if present.
Previously a label was only emitted if the table was figurized
(and not `.typst:no-figure`).

Closes #11849.
2026-09-08 08:34:30 -07:00
John MacFarlane 8813d0f984 T.P.Parsing.Lists: make anyOrderedListMarker more efficient. 2026-09-07 23:01:22 -07:00
John MacFarlane b2a03ef811 Remove duplicate import. 2026-09-07 23:01:14 -07:00
John MacFarlane 56b32dd6da T.P.Parsing.Math: remove $ checks when delim is \( or \\(. 2026-09-07 22:36:36 -07:00
John MacFarlane 77d0f95fab T.P.Parsing.General: use fail instead of error. 2026-09-07 22:31:41 -07:00
John MacFarlane d26ff7ffd7 T.P.Parsing.General: delete duplicated export. 2026-09-07 22:31:23 -07:00
John MacFarlane 0243bf68c5 T.P.Parsing.General: fmap instead of liftM. 2026-09-07 22:31:10 -07:00
John MacFarlane a01e49ee71 T.P.Parsing.Future: remove some unnecessary imports and definitions. 2026-09-07 19:45:30 -07:00
John MacFarlaneandClaude a533fa4c8d ODT reader: rewrite in monadic style instead of arrows.
The ODT reader was written in an idiosyncratic arrows-based style,
using a custom ArrowState arrow and dozens of special combinators.
Replace this with an ordinary monad:

    type XMLConverter nsID extraState
       = ExceptT () (State (XMLConverterState nsID extraState))

This preserves the original semantics (state changes persist through
failure; recovery via Alternative's <|>) while allowing all readers
to be written in plain do/applicative notation.

- Rewrite Generic/XMLConverter.hs around the new monad; replace the
  parent-element stack with a single currentElement field, and make
  matchContent matchers simple (namespace, name, reader) triples
  whose results are mconcat'd in document order.
- Port StyleReader.hs and ContentReader.hs to monadic style.
- constructList now runs its child reader once instead of twice.
- Delete Arrows/State.hs, Arrows/Utils.hs, and Base.hs; trim
  Generic/Fallible.hs and Generic/Utils.hs to what is still used.

Net -994 lines. No changes to test output.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-07 19:28:39 -07:00
John MacFarlane 1791dd80ff Remove redundant imports. 2026-09-07 18:27:48 -07:00
John MacFarlane 1a47b954dc toSubscript: handle minus sign. 2026-09-07 09:35:12 -07:00
John MacFarlane 62ed765a7e stripLeadingTrailingSpace - strip multiple Space, if present. 2026-09-07 09:32:48 -07:00
John MacFarlane f0314bb112 htmlAttrs: escape id and class attributes, like the others. 2026-09-07 09:29:18 -07:00
John MacFarlane b724efe93c T.P.Writers.Shared: Fix logic bug in splitSentences. 2026-09-07 09:24:52 -07:00
John MacFarlane 69e13ec2ac SelfContained: escape only what is needed in textual data URIs. 2026-09-07 08:52:36 -07:00
John MacFarlaneandClaude a93f2fdc7f SelfContained: only add role and aria-label when inlining SVGs.
role="img" and an aria-label copied from alt were being added to
every element with a src-like attribute, not just inlined SVGs.
That is redundant on img elements, whose alt already provides the
accessible name, and actively wrong on elements like video, audio,
iframe, and embed, where role="img" misrepresents interactive
content as a static image to assistive technology.

The attributes are only needed when an img element is replaced by
an inline svg element, since the alt text loses its meaning there;
restrict them to that path.  Inline SVG output is unchanged.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-07 15:41:18 +00:00