From ffac0fb76389204c8a5cd1ff7361987896097f58 Mon Sep 17 00:00:00 2001 From: despresc Date: Tue, 15 Sep 2020 14:18:36 -0400 Subject: [PATCH] [API change] Add a Ref inline element and Reference type The Ref element is analogous to the \ref family of LaTeX commands. It is a collection of references to other identified elements, similar to the Cite element. It includes a RefType type that distinguishes between number references and page references. The Reference type represents a single reference to an identified element. Unlike a Link, the text of a Reference is generally expected to be replaced with attributes of the referenced element. These attributes are normally either its number or page number, and a possibly-configurable prefix representing the type of thing referenced (like "figure" or "table"). The ReferenceMode type records any local modification to the prefix of a reference, recognizing explicit upper case and lower case prefix selection, prefix suppression, and normal (default) references. --- src/Text/Pandoc/Arbitrary.hs | 21 ++++++++++++++++ src/Text/Pandoc/Builder.hs | 8 +++++++ src/Text/Pandoc/Definition.hs | 33 +++++++++++++++++++++++++ src/Text/Pandoc/Walk.hs | 45 +++++++++++++++++++++++++++++++++-- test/test-pandoc-types.hs | 43 +++++++++++++++++++++++++++++++++ 5 files changed, 148 insertions(+), 2 deletions(-) diff --git a/src/Text/Pandoc/Arbitrary.hs b/src/Text/Pandoc/Arbitrary.hs index 10172fb..fa6fe09 100644 --- a/src/Text/Pandoc/Arbitrary.hs +++ b/src/Text/Pandoc/Arbitrary.hs @@ -52,6 +52,7 @@ instance Arbitrary Inlines where flattenInline (SmallCaps ils) = ils flattenInline (Quoted _ ils) = ils flattenInline (Cite _ ils) = ils + flattenInline (Ref _ _ _ ils) = ils flattenInline Code{} = [] flattenInline Space = [] flattenInline SoftBreak = [] @@ -125,6 +126,9 @@ instance Arbitrary Inline where shrink (Quoted qtype ils) = Quoted qtype <$> shrinkInlineList ils shrink (Cite cits ils) = (Cite cits <$> shrinkInlineList ils) ++ (flip Cite ils <$> shrink cits) + shrink (Ref attr rt refs ils) = [Ref attr rt refs ils' | ils' <- shrinkInlineList ils] + ++ [Ref attr rt refs' ils | refs' <- shrink refs] + ++ [Ref attr' rt refs ils | attr' <- shrinkAttr attr] shrink (Code attr s) = (Code attr <$> shrinkText s) ++ (flip Code s <$> shrinkAttr attr) shrink Space = [] @@ -173,6 +177,7 @@ arbInline n = frequency $ [ (60, Str <$> realString) , (10, Link <$> arbAttr <*> arbInlines (n-1) <*> ((,) <$> realString <*> realString)) , (10, Image <$> arbAttr <*> arbInlines (n-1) <*> ((,) <$> realString <*> realString)) , (2, Cite <$> arbitrary <*> arbInlines 1) + , (2, Ref <$> arbAttr <*> arbitrary <*> arbitrary <*> arbInlines 1) , (2, Note <$> resize 3 (listOf1 $ arbBlock (n-1))) ] @@ -301,6 +306,22 @@ instance Arbitrary Citation where <*> arbitrary <*> arbitrary +instance Arbitrary ReferenceMode where + arbitrary + = arbitraryBoundedEnum + +instance Arbitrary RefType where + arbitrary + = arbitraryBoundedEnum + +instance Arbitrary Reference where + arbitrary + = Reference <$> fmap T.pack (listOf $ elements $ ['a'..'z'] ++ ['0'..'9'] ++ ['_']) + <*> arbInlines 1 + <*> arbInlines 1 + <*> arbitrary + <*> arbitrary + instance Arbitrary Row where arbitrary = resize 3 $ arbRow 2 shrink (Row attr body) diff --git a/src/Text/Pandoc/Builder.hs b/src/Text/Pandoc/Builder.hs index 77af1f3..3664e58 100644 --- a/src/Text/Pandoc/Builder.hs +++ b/src/Text/Pandoc/Builder.hs @@ -130,6 +130,8 @@ module Text.Pandoc.Builder ( module Text.Pandoc.Definition , singleQuoted , doubleQuoted , cite + , ref + , refWith , codeWith , code , space @@ -380,6 +382,12 @@ quoted qt = singleton . Quoted qt . toList cite :: [Citation] -> Inlines -> Inlines cite cts = singleton . Cite cts . toList +ref :: RefType -> [Reference] -> Inlines -> Inlines +ref = refWith nullAttr + +refWith :: Attr -> RefType -> [Reference] -> Inlines -> Inlines +refWith a rt rfs = singleton . Ref a rt rfs . toList + -- | Inline code with attributes. codeWith :: Attr -> Text -> Inlines codeWith attrs = singleton . Code attrs diff --git a/src/Text/Pandoc/Definition.hs b/src/Text/Pandoc/Definition.hs index 5a0fed2..9ab7be9 100644 --- a/src/Text/Pandoc/Definition.hs +++ b/src/Text/Pandoc/Definition.hs @@ -82,6 +82,9 @@ module Text.Pandoc.Definition ( Pandoc(..) , MathType(..) , Citation(..) , CitationMode(..) + , Reference(..) + , ReferenceMode(..) + , RefType(..) , pandocTypesVersion ) where @@ -327,6 +330,7 @@ data Inline | SmallCaps [Inline] -- ^ Small caps text (list of inlines) | Quoted QuoteType [Inline] -- ^ Quoted text (list of inlines) | Cite [Citation] [Inline] -- ^ Citation (list of inlines) + | Ref Attr RefType [Reference] [Inline] -- ^ Reference (list of inlines) | Code Attr Text -- ^ Inline code (literal) | Space -- ^ Inter-word space | SoftBreak -- ^ Soft line break @@ -354,6 +358,29 @@ instance Ord Citation where data CitationMode = AuthorInText | SuppressAuthor | NormalCitation deriving (Show, Eq, Ord, Read, Typeable, Data, Generic) +data Reference = Reference + { referenceId :: Text + , referencePrefix :: [Inline] + , referenceSuffix :: [Inline] + , referenceMode :: ReferenceMode + , referenceHash :: Int + } deriving (Show, Eq, Read, Typeable, Data, Generic) + +instance Ord Reference where + compare = comparing referenceHash + +data ReferenceMode + = UpperCasePrefix + | LowerCasePrefix + | SuppressPrefix + | NormalReference + deriving (Show, Eq, Read, Enum, Bounded, Typeable, Data, Generic) + +data RefType + = NumberRef + | PageRef + | RefTypeDefault + deriving (Show, Eq, Ord, Read, Enum, Bounded, Typeable, Data, Generic) -- ToJSON/FromJSON instances. Some are defined by hand so that we have -- more control over the format. @@ -366,6 +393,9 @@ $(let jsonOpts = defaultOptions [ ''MetaValue , ''CitationMode , ''Citation + , ''ReferenceMode + , ''RefType + , ''Reference , ''QuoteType , ''MathType , ''ListNumberStyle @@ -422,6 +452,7 @@ instance ToJSON Pandoc where instance NFData MetaValue instance NFData Meta instance NFData Citation +instance NFData Reference instance NFData Alignment instance NFData RowSpan instance NFData ColSpan @@ -435,6 +466,8 @@ instance NFData Inline instance NFData MathType instance NFData Format instance NFData CitationMode +instance NFData ReferenceMode +instance NFData RefType instance NFData QuoteType instance NFData ListNumberDelim instance NFData ListNumberStyle diff --git a/src/Text/Pandoc/Walk.hs b/src/Text/Pandoc/Walk.hs index a71d848..374f533 100644 --- a/src/Text/Pandoc/Walk.hs +++ b/src/Text/Pandoc/Walk.hs @@ -96,6 +96,7 @@ module Text.Pandoc.Walk , queryTableFoot , queryCell , queryCitation + , queryReference , queryInline , queryMetaValue , queryPandoc @@ -107,6 +108,7 @@ module Text.Pandoc.Walk , walkTableFootM , walkCellM , walkCitationM + , walkReferenceM , walkInlineM , walkMetaValueM , walkPandocM @@ -388,12 +390,31 @@ instance Walkable [Block] Citation where walkM = walkCitationM query = queryCitation +-- +-- Walk Reference +-- +instance Walkable Inline Reference where + walkM = walkReferenceM + query = queryReference + +instance Walkable [Inline] Reference where + walkM = walkReferenceM + query = queryReference + +instance Walkable Block Reference where + walkM = walkReferenceM + query = queryReference + +instance Walkable [Block] Reference where + walkM = walkReferenceM + query = queryReference + -- | Helper method to walk to elements nested below @'Inline'@ nodes. -- -- When walking an inline with this function, only the contents of the traversed -- inline element may change. The element itself, i.e. its constructor, cannot -- be changed. -walkInlineM :: (Walkable a Citation, Walkable a [Block], +walkInlineM :: (Walkable a Citation, Walkable a Reference, Walkable a [Block], Walkable a [Inline], Monad m, Applicative m, Functor m) => (a -> m a) -> Inline -> m Inline walkInlineM _ (Str xs) = return (Str xs) @@ -410,6 +431,7 @@ walkInlineM f (Image atr xs t) = Image atr <$> walkM f xs <*> pure t walkInlineM f (Note bs) = Note <$> walkM f bs walkInlineM f (Span attr xs) = Span attr <$> walkM f xs walkInlineM f (Cite cs xs) = Cite <$> walkM f cs <*> walkM f xs +walkInlineM f (Ref a rt cs xs) = Ref a rt <$> walkM f cs <*> walkM f xs walkInlineM _ LineBreak = return LineBreak walkInlineM _ SoftBreak = return SoftBreak walkInlineM _ Space = return Space @@ -420,7 +442,7 @@ walkInlineM _ x@RawInline {} = return x -- | Perform a query on elements nested below an @'Inline'@ element by -- querying nested lists of @Inline@s, @Block@s, or @Citation@s. queryInline :: (Walkable a Citation, Walkable a [Block], - Walkable a [Inline], Monoid c) + Walkable a Reference, Walkable a [Inline], Monoid c) => (a -> c) -> Inline -> c queryInline _ (Str _) = mempty queryInline f (Emph xs) = query f xs @@ -432,6 +454,7 @@ queryInline f (Superscript xs)= query f xs queryInline f (SmallCaps xs) = query f xs queryInline f (Quoted _ xs) = query f xs queryInline f (Cite cs xs) = query f cs <> query f xs +queryInline f (Ref _ _ cs xs) = query f cs <> query f xs queryInline _ (Code _ _) = mempty queryInline _ Space = mempty queryInline _ SoftBreak = mempty @@ -544,6 +567,24 @@ queryCitation :: (Walkable a [Inline], Monoid c) => (a -> c) -> Citation -> c queryCitation f (Citation _ pref suff _ _ _) = query f pref <> query f suff +-- | Helper method to walk to elements nested below @'Reference'@ nodes. +-- +-- The non-inline contents of a reference will remain unchanged during traversal. +-- Only the inline contents, viz. the reference's prefix and postfix, will be +-- traversed further and can thus be changed during this operation. +walkReferenceM :: (Walkable a [Inline], Monad m, Applicative m, Functor m) + => (a -> m a) -> Reference -> m Reference +walkReferenceM f (Reference id' pref suff mode hash) = + do pref' <- walkM f pref + suff' <- walkM f suff + return $ Reference id' pref' suff' mode hash + +-- | Perform a query on elements nested below a @'Reference'@ element by +-- querying the prefix and postfix @Inline@ lists. +queryReference :: (Walkable a [Inline], Monoid c) + => (a -> c) -> Reference -> c +queryReference f (Reference _ pref suff _ _) = query f pref <> query f suff + -- | Helper method to walk the elements nested below @'Row'@ nodes. The -- @'Attr'@ component is not changed by this operation. walkRowM :: (Walkable a Cell, Monad m) diff --git a/test/test-pandoc-types.hs b/test/test-pandoc-types.hs index f450d19..b6b4339 100644 --- a/test/test-pandoc-types.hs +++ b/test/test-pandoc-types.hs @@ -161,6 +161,37 @@ t_citation = ( Citation { citationId = "jameson:unconscious", , [s|{"citationId":"jameson:unconscious","citationPrefix":[{"t":"Str","c":"cf"}],"citationSuffix":[{"t":"Space"},{"t":"Str","c":"123"}],"citationMode":{"t":"NormalCitation"},"citationNoteNum":0,"citationHash":0}|] ) +t_uppercaseprefix :: (ReferenceMode, ByteString) +t_uppercaseprefix = (UpperCasePrefix, [s|{"t":"UpperCasePrefix"}|]) + +t_lowercaseprefix :: (ReferenceMode, ByteString) +t_lowercaseprefix = (LowerCasePrefix, [s|{"t":"LowerCasePrefix"}|]) + +t_suppressprefix :: (ReferenceMode, ByteString) +t_suppressprefix = (SuppressPrefix, [s|{"t":"SuppressPrefix"}|]) + +t_normalreference :: (ReferenceMode, ByteString) +t_normalreference = (NormalReference, [s|{"t":"NormalReference"}|]) + +t_numberref :: (RefType, ByteString) +t_numberref = (NumberRef, [s|{"t":"NumberRef"}|]) + +t_pageref :: (RefType, ByteString) +t_pageref = (PageRef, [s|{"t":"PageRef"}|]) + +t_reftypedefault :: (RefType, ByteString) +t_reftypedefault = (RefTypeDefault, [s|{"t":"RefTypeDefault"}|]) + +t_reference :: (Reference, ByteString) +t_reference + = ( Reference { referenceId = "fig:demonstration" + , referencePrefix = [Str "cf"] + , referenceSuffix = [Space,Str "123"] + , referenceMode = NormalReference + , referenceHash = 0 } + , [s|{"referenceId":"fig:demonstration","referencePrefix":[{"t":"Str","c":"cf"}],"referenceSuffix":[{"t":"Space"},{"t":"Str","c":"123"}],"referenceMode":{"t":"NormalReference"},"referenceHash":0}|] + ) + t_displaymath :: (MathType, ByteString) t_displaymath = ( DisplayMath, [s|{"t":"DisplayMath"}|]) @@ -678,6 +709,18 @@ tests = , testEncodeDecode "NormalCitation" t_normalcitation ] , testEncodeDecode "Citation" t_citation + , testGroup "ReferenceMode" + [ testEncodeDecode "UpperCasePrefix" t_uppercaseprefix + , testEncodeDecode "LowerCasePrefix" t_lowercaseprefix + , testEncodeDecode "SuppressPrefix" t_suppressprefix + , testEncodeDecode "NormalReference" t_normalreference + ] + , testGroup "RefType" + [ testEncodeDecode "NumberRef" t_numberref + , testEncodeDecode "PageRef" t_pageref + , testEncodeDecode "RefTypeDefault" t_reftypedefault + ] + , testEncodeDecode "Reference" t_reference , testGroup "MathType" [ testEncodeDecode "DisplayMath" t_displaymath , testEncodeDecode "InlineMath" t_inlinemath