@@ -407,22 +407,30 @@ inlineCommands = M.unions
407407 , (" sl" , extractSpaces emph <$> inlines)
408408 , (" bf" , extractSpaces strong <$> inlines)
409409 , (" tt" , formatCode nullAttr <$> inlines)
410+ , (" ttfamily" , extractSpaces (formatCode nullAttr) <$> inlines)
410411 , (" rm" , inlines)
411412 , (" itshape" , extractSpaces emph <$> inlines)
412413 , (" slshape" , extractSpaces emph <$> inlines)
413414 , (" scshape" , extractSpaces smallcaps <$> inlines)
414415 , (" bfseries" , extractSpaces strong <$> inlines)
415- , (" MakeUppercase" , makeUppercase <$> tok)
416- , (" MakeTextUppercase" , makeUppercase <$> tok) -- textcase
417- , (" uppercase" , makeUppercase <$> tok)
418- , (" MakeLowercase" , makeLowercase <$> tok)
419- , (" MakeTextLowercase" , makeLowercase <$> tok)
420- , (" lowercase" , makeLowercase <$> tok)
416+ , (" MakeUppercase" , caseTransform " upper" T. toUpper)
417+ , (" MakeTextUppercase" , caseTransform " upper" T. toUpper) -- textcase
418+ , (" uppercase" , caseTransform " upper" T. toUpper)
419+ , (" MakeLowercase" , caseTransform " lower" T. toLower)
420+ , (" MakeTextLowercase" , caseTransform " lower" T. toLower)
421+ , (" lowercase" , caseTransform " lower" T. toLower)
422+ , (" MakeTitlecase" , makeTitlecaseCommand)
423+ , (" NoCaseChange" , spanWith (" " ,[" nocasechange" ],[] ) <$> tok)
424+ , (" CaseSwitch" , tok <* tok <* tok <* tok)
421425 , (" thanks" , skipopts >> note <$> grouped block)
422426 , (" footnote" , skipopts >> footnote)
423427 , (" footnotemark" , footnotemark)
424428 , (" footnotetext" , footnotetext)
425429 , (" newline" , pure B. linebreak)
430+ -- xparse argument markers, in case they leak into the document:
431+ , (" NoValue" , pure (B. str " -NoValue-" ))
432+ , (" BooleanTrue" , pure mempty )
433+ , (" BooleanFalse" , pure mempty )
426434 , (" passthrough" , fixPassthroughEscapes <$> tok)
427435 -- \passthrough macro used by latex writer
428436 -- for listings
@@ -457,6 +465,7 @@ inlineCommands = M.unions
457465 , (" iftoggle" , try $ ifToggle >> inline)
458466 -- include
459467 , (" input" , rawInlineOr " input" $ include " input" )
468+ , (" expandableinput" , rawInlineOr " expandableinput" $ include " input" )
460469 -- soul package
461470 , (" st" , extractSpaces strikeout <$> tok)
462471 , (" ul" , underline <$> tok)
@@ -471,6 +480,19 @@ inlineCommands = M.unions
471480 -- this is used internally by pandoc but the definition is too complicated
472481 -- for pandoc to handle (see #11140):
473482 , (" pandocbounded" , tok)
483+ -- LaTeX3 constants
484+ , (" c_ampersand_str" , pure (str " &" ))
485+ , (" c_atsign_str" , pure (str " @" ))
486+ , (" c_backslash_str" , pure (str " \\ " ))
487+ , (" c_left_brace_str" , pure (str " {" ))
488+ , (" c_right_brace_str" , pure (str " }" ))
489+ , (" c_circumflex_str" , pure (str " ^" ))
490+ , (" c_colon_str" , pure (str " :" ))
491+ , (" c_dollar_str" , pure (str " $" ))
492+ , (" c_hash_str" , pure (str " #" ))
493+ , (" c_percent_str" , pure (str " %" ))
494+ , (" c_tilde_str" , pure (str " ~" ))
495+ , (" c_underscore_str" , pure (str " _" ))
474496 ]
475497
476498bracedFilename :: PandocMonad m => LP m Text
@@ -547,15 +569,97 @@ ifdim = do
547569 contents <- manyTill anyTok (controlSeq " fi" )
548570 return $ rawInline " latex" $ " \\ ifdim" <> untokenize contents <> " \\ fi"
549571
550- makeUppercase :: Inlines -> Inlines
551- makeUppercase = fromList . walk (alterStr T. toUpper) . toList
552-
553- makeLowercase :: Inlines -> Inlines
554- makeLowercase = fromList . walk (alterStr T. toLower) . toList
572+ -- | Parse the argument of a case-changing command (\MakeUppercase,
573+ -- \MakeLowercase) and apply the case transformation, honoring
574+ -- exclusions declared with \Declare*caseExclusions.
575+ caseTransform :: PandocMonad m => Text -> (Text -> Text ) -> LP m Inlines
576+ caseTransform kind f = do
577+ void $ option [] keyvals -- locale options, ignored
578+ excl <- M. findWithDefault Set. empty kind . sCaseExclusions <$> getState
579+ caseTransformWith excl f <$> tok
580+
581+ -- | Apply a case transformation to text, leaving math, code and
582+ -- citations untouched, unwrapping (and skipping) \NoCaseChange
583+ -- content, and skipping excluded words.
584+ caseTransformWith :: Set. Set Text -> (Text -> Text ) -> Inlines -> Inlines
585+ caseTransformWith excl f = fromList . go . toList
586+ where
587+ go = concatMap goInline
588+ goInline (Span (" " ,[" nocasechange" ],[] ) ils) = ils
589+ goInline (Str t)
590+ | t `Set.member` excl = [Str t]
591+ | otherwise = [Str (f t)]
592+ goInline (Emph ils) = [Emph (go ils)]
593+ goInline (Strong ils) = [Strong (go ils)]
594+ goInline (Underline ils) = [Underline (go ils)]
595+ goInline (Strikeout ils) = [Strikeout (go ils)]
596+ goInline (Superscript ils) = [Superscript (go ils)]
597+ goInline (Subscript ils) = [Subscript (go ils)]
598+ goInline (SmallCaps ils) = [SmallCaps (go ils)]
599+ goInline (Quoted qt ils) = [Quoted qt (go ils)]
600+ goInline (Span attr ils) = [Span attr (go ils)]
601+ goInline (Link attr ils target) = [Link attr (go ils) target]
602+ goInline x = [x] -- Math, Code, Cite, Space, etc.
603+
604+ -- | Parse the arguments of \MakeTitlecase, supporting the
605+ -- @words=all@ option and title-case exclusions.
606+ makeTitlecaseCommand :: PandocMonad m => LP m Inlines
607+ makeTitlecaseCommand = do
608+ options <- option [] keyvals
609+ excl <- M. findWithDefault Set. empty " title" . sCaseExclusions <$> getState
610+ (if lookup " words" options == Just " all"
611+ then makeTitlecaseAll excl
612+ else makeTitlecase) <$> tok
613+
614+ -- | Titlecase the first letter of each word (\MakeTitlecase with
615+ -- @words=all@), skipping excluded words.
616+ makeTitlecaseAll :: Set. Set Text -> Inlines -> Inlines
617+ makeTitlecaseAll excl = fromList . go . toList
618+ where
619+ go [] = []
620+ go xs =
621+ let (w, rest) = break isSep xs
622+ (seps, rest') = span isSep rest
623+ in tcWord w ++ seps ++ go rest'
624+ isSep Space = True
625+ isSep SoftBreak = True
626+ isSep LineBreak = True
627+ isSep _ = False
628+ tcWord w
629+ | stringify w `Set.member` excl = w
630+ | otherwise = toList (makeTitlecase (fromList w))
631+
632+ -- | Handle \DeclareUppercaseExclusions and friends: store a
633+ -- comma-separated list of words excluded from case changing.
634+ declareCaseExclusions :: PandocMonad m => Text -> LP m Blocks
635+ declareCaseExclusions kind = do
636+ ws <- map T. strip . T. splitOn " ," . untokenize <$> braced
637+ updateState $ \ st -> st{ sCaseExclusions =
638+ M. insertWith Set. union kind (Set. fromList ws) (sCaseExclusions st) }
639+ return mempty
555640
556- alterStr :: (Text -> Text ) -> Inline -> Inline
557- alterStr f (Str xs) = Str (f xs)
558- alterStr _ x = x
641+ -- | Uppercase the first character of the first string (LaTeX3
642+ -- \MakeTitlecase, which title-cases only the first word by default).
643+ makeTitlecase :: Inlines -> Inlines
644+ makeTitlecase = fromList . snd . go . toList
645+ where
646+ go :: [Inline ] -> (Bool , [Inline ])
647+ go (x : xs) =
648+ case goInline x of
649+ (True , x') -> (True , x' : xs)
650+ (False , x') -> (x' : ) <$> go xs
651+ go [] = (False , [] )
652+ goInline (Str t) | not (T. null t) =
653+ (True , Str (T. toTitle (T. take 1 t) <> T. drop 1 t))
654+ goInline (Emph ils) = Emph <$> go ils
655+ goInline (Strong ils) = Strong <$> go ils
656+ goInline (Underline ils) = Underline <$> go ils
657+ goInline (SmallCaps ils) = SmallCaps <$> go ils
658+ goInline (Span attr ils) = Span attr <$> go ils
659+ goInline (Link attr ils target) =
660+ (\ ils' -> Link attr ils' target) <$> go ils
661+ goInline (Quoted qt ils) = Quoted qt <$> go ils
662+ goInline x = (False , x)
559663
560664fixPassthroughEscapes :: Inlines -> Inlines
561665fixPassthroughEscapes = walk go
@@ -1062,10 +1166,30 @@ blockCommands = M.fromList
10621166 -- include
10631167 , (" include" , rawBlockOr " include" $ include " include" )
10641168 , (" input" , rawBlockOr " input" $ include " input" )
1169+ , (" expandableinput" , rawBlockOr " expandableinput" $ include " input" )
10651170 , (" subfile" , rawBlockOr " subfile" doSubfile)
10661171 , (" usepackage" , rawBlockOr " usepackage" usepackage)
10671172 -- preamble
10681173 , (" PackageError" , mempty <$ (braced >> braced >> braced))
1174+ -- LaTeX3 conveniences, parsed and ignored:
1175+ , (" ExplSyntaxOn" , pure mempty )
1176+ , (" ExplSyntaxOff" , pure mempty )
1177+ , (" ShowCommand" , mempty <$ withVerbatimMode (spaces *> anyControlSeq))
1178+ , (" ShowEnvironment" , mempty <$ braced)
1179+ , (" DeclareKeys" , mempty <$ (skipopts *> braced))
1180+ , (" DeclareUnknownKeyHandler" , mempty <$ (skipopts *> braced))
1181+ , (" ProcessKeyOptions" , mempty <$ skipopts)
1182+ , (" SetKeys" , mempty <$ (skipopts *> braced))
1183+ -- LaTeX3 case changing
1184+ , (" DeclareUppercaseExclusions" , declareCaseExclusions " upper" )
1185+ , (" DeclareLowercaseExclusions" , declareCaseExclusions " lower" )
1186+ , (" DeclareTitlecaseExclusions" , declareCaseExclusions " title" )
1187+ , (" AddToNoCaseChangeList" , mempty <$ braced)
1188+ , (" DeclareCaseChangeEquivalent" , mempty <$
1189+ (withVerbatimMode (spaces *> anyControlSeq) *> braced))
1190+ , (" DeclareUppercaseMapping" , mempty <$ (skipopts *> braced *> braced))
1191+ , (" DeclareLowercaseMapping" , mempty <$ (skipopts *> braced *> braced))
1192+ , (" DeclareTitlecaseMapping" , mempty <$ (skipopts *> braced *> braced))
10691193 -- epigraph package
10701194 , (" epigraph" , epigraph)
10711195 -- alignment
0 commit comments