diff --git a/MANUAL.txt b/MANUAL.txt index fd2a2fbb9..d24dcb7cc 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -3673,7 +3673,7 @@ automatically assigned a unique identifier based on the heading text. This extension can be enabled/disabled for the following formats: input formats -: `markdown`, `latex`, `rst`, `mediawiki`, `textile`, `man` +: `markdown`, `latex`, `rst`, `mediawiki`, `textile`, `man`, `typst` output formats : `markdown`, `muse` diff --git a/src/Text/Pandoc/Extensions.hs b/src/Text/Pandoc/Extensions.hs index 71b5da799..2a0e130b1 100644 --- a/src/Text/Pandoc/Extensions.hs +++ b/src/Text/Pandoc/Extensions.hs @@ -662,7 +662,8 @@ getAllExtensions f = universalExtensions <> getAll f getAll "mediawiki" = autoIdExtensions <> extensionsFromList [ Ext_smart ] - getAll "typst" = extensionsFromList [Ext_citations, Ext_smart] + getAll "typst" = autoIdExtensions <> + extensionsFromList [Ext_citations, Ext_smart] getAll "djot" = extensionsFromList [Ext_sourcepos] getAll "man" = autoIdExtensions getAll _ = mempty diff --git a/src/Text/Pandoc/Readers/Typst.hs b/src/Text/Pandoc/Readers/Typst.hs index 9fe7cc432..c320dbb07 100644 --- a/src/Text/Pandoc/Readers/Typst.hs +++ b/src/Text/Pandoc/Readers/Typst.hs @@ -31,6 +31,7 @@ import Typst ( parseTypst, evaluateTypst ) import Text.Pandoc.Error (PandocError(..)) import Text.Pandoc.Translations (Term(References), translateTerm) import Text.Pandoc.Shared (tshow, blocksToInlines) +import Text.Pandoc.Parsing (registerHeader, reportLogMessages) import Control.Monad.Except (throwError) import Control.Monad (MonadPlus (mplus), void, guard, foldM) import Control.Monad.Trans (lift) @@ -79,7 +80,7 @@ readTypst opts inp = do Right content -> do let content' = fixNesting content let labs = findLabels [content'] - runParserT pPandoc defaultPState{ sLabels = labs } + runParserT pPandoc defaultPState{ sLabels = labs, sOptions = opts } inputName [content'] >>= either (throwError . PandocParseError . T.pack . show) pure @@ -188,6 +189,7 @@ pPandoc = do (if null keywords then id else B.setMeta "keywords" keywords) $ meta + reportLogMessages pure $ Pandoc meta' (B.toList bs) pBlocks :: PandocMonad m => P m B.Blocks @@ -264,8 +266,9 @@ blockHandlers = M.fromList ,("heading", \_ mbident fields -> do body <- getField "body" fields lev <- getField "level" fields <|> pure 1 - B.headerWith (fromMaybe "" mbident,[],[]) lev - <$> pWithContents pInlines body) + ils <- pWithContents pInlines body + attr <- registerHeader (fromMaybe "" mbident,[],[]) ils + pure $ B.headerWith attr lev ils) ,("quote", \_ _ fields -> do getField "block" fields >>= guard body <- getField "body" fields >>= pWithContents pBlocks diff --git a/src/Text/Pandoc/Readers/Typst/Parsing.hs b/src/Text/Pandoc/Readers/Typst/Parsing.hs index 92a2be4ca..24ff0e432 100644 --- a/src/Text/Pandoc/Readers/Typst/Parsing.hs +++ b/src/Text/Pandoc/Readers/Typst/Parsing.hs @@ -16,10 +16,13 @@ module Text.Pandoc.Readers.Typst.Parsing where import Control.Monad (MonadPlus) import Control.Monad.Reader (lift) +import Data.Default (def) import qualified Data.Foldable as F import qualified Data.Map as M import Data.Maybe (fromMaybe) import Data.Sequence (Seq) +import Data.Set (Set) +import qualified Data.Set as Set import Data.Text (Text) import Text.Parsec ( ParsecT, getInput, setInput, tokenPrim ) @@ -27,18 +30,38 @@ import Typst.Types ( Identifier, Content(Elt), FromVal(..), Val(VNone) ) import Text.Pandoc.Class.PandocMonad ( PandocMonad, report ) import Text.Pandoc.Logging (LogMessage(..)) +import Text.Pandoc.Options (ReaderOptions) +import Text.Pandoc.Parsing.Capabilities + ( HasReaderOptions(..), HasIdentifierList(..), HasLogMessages(..) ) import Text.Pandoc.Definition data PState = PState { sLabels :: [Text] - , sMeta :: Meta } + , sMeta :: Meta + , sOptions :: ReaderOptions + , sIdentifiers :: Set Text + , sLogMessages :: [LogMessage] } deriving (Show) +instance HasReaderOptions PState where + extractReaderOptions = sOptions + +instance HasIdentifierList PState where + extractIdentifierList = sIdentifiers + updateIdentifierList f st = st{ sIdentifiers = f (sIdentifiers st) } + +instance HasLogMessages PState where + addLogMessage m st = st{ sLogMessages = m : sLogMessages st } + getLogMessages = reverse . sLogMessages + defaultPState :: PState defaultPState = PState { sLabels = [] - , sMeta = mempty } + , sMeta = mempty + , sOptions = def + , sIdentifiers = Set.empty + , sLogMessages = [] } type P m a = ParsecT [Content] PState m a -- state tracks a list of labels in the document diff --git a/test/command/typst-auto-identifiers.md b/test/command/typst-auto-identifiers.md new file mode 100644 index 000000000..abe67f04e --- /dev/null +++ b/test/command/typst-auto-identifiers.md @@ -0,0 +1,37 @@ +With `auto_identifiers` enabled, headings without an explicit label get +automatic identifiers, with collisions disambiguated: + +``` +% pandoc -f typst+auto_identifiers -t native += Introduction + +Text. + +== A Sub Section + += Introduction +^D +[ Header + 1 ( "introduction" , [] , [] ) [ Str "Introduction" ] +, Para [ Str "Text." ] +, Header + 2 + ( "a-sub-section" , [] , [] ) + [ Str "A" , Space , Str "Sub" , Space , Str "Section" ] +, Header + 1 ( "introduction-1" , [] , [] ) [ Str "Introduction" ] +] +``` + +The extension is off by default, so headings have no identifiers: + +``` +% pandoc -f typst -t native += Introduction + +Text. +^D +[ Header 1 ( "" , [] , [] ) [ Str "Introduction" ] +, Para [ Str "Text." ] +] +```