@@ -1,26 +1,90 @@
1+{-# LANGUAGE DeriveFunctor, ScopedTypeVariables #-}
2+{-# OPTIONS_GHC -fwarn-incomplete-patterns #-}
3+
14-- | Wrapper around Markdown library
25module Idris.Docstrings (
3− Docstring, parseDocstring, renderDocstring, emptyDocstring, nullDocstring, noDocs,
4− overview, containsText
6+ Docstring(..), Block(..), Inline(..), parseDocstring, renderDocstring, emptyDocstring, nullDocstring, noDocs,
7+ overview, containsText, renderHtml, annotCode
58 ) where
69
710import qualified Cheapskate as C
811import qualified Cheapskate.Types as CT
12+import Cheapskate.Html (renderDoc)
913
1014import Util.Pretty
1115
12−import Idris.Core.TT (OutputAnnotation(..), TextFormatting(..), Name)
16+import Idris.Core.TT (OutputAnnotation(..), TextFormatting(..), Name, Term)
1317
1418import qualified Data.Text as T
1519import qualified Data.Foldable as F
20+import Data.Foldable (Foldable)
21+import Data.Traversable (Traversable)
1622import qualified Data.Sequence as S
1723
18−-- | Representation of Idris's inline documentation
19−type Docstring = CT.Doc
24+import Control.DeepSeq (NFData(..))
25+
26+import Text.Blaze.Html (Html)
27+
28+-- | Representation of Idris's inline documentation. The type paramter
29+-- represents the type of terms that are associated with code blocks.
30+data Docstring a = DocString CT.Options (Blocks a)
31+ deriving (Show, Functor, Foldable, Traversable)
32+
33+type Blocks a = S.Seq (Block a)
34+
35+-- | Block-level elements.
36+data Block a = Para (Inlines a)
37+ | Header Int (Inlines a)
38+ | Blockquote (Blocks a)
39+ | List Bool CT.ListType [Blocks a]
40+ | CodeBlock CT.CodeAttr T.Text a
41+ | HtmlBlock T.Text
42+ | HRule
43+ deriving (Show, Functor, Foldable, Traversable)
44+
45+data Inline a = Str T.Text
46+ | Space
47+ | SoftBreak
48+ | LineBreak
49+ | Emph (Inlines a)
50+ | Strong (Inlines a)
51+ | Code T.Text a
52+ | Link (Inlines a) T.Text T.Text
53+ | Image (Inlines a) T.Text T.Text
54+ | Entity T.Text
55+ | RawHtml T.Text
56+ deriving (Show, Functor, Foldable, Traversable)
57+
58+type Inlines a = S.Seq (Inline a)
59+
2060
2161-- | Construct a docstring from a Text that contains Markdown-formatted docs
22−parseDocstring :: T.Text -> CT.Doc
23−parseDocstring = C.markdown options
62+parseDocstring :: T.Text -> Docstring ()
63+parseDocstring = toDocstring . C.markdown options
64+ where toDocstring :: CT.Doc -> Docstring ()
65+ toDocstring (CT.Doc opts blocks) = DocString opts (fmap toBlock blocks)
66+
67+ toBlock :: CT.Block -> Block ()
68+ toBlock (CT.Para inlines) = Para (fmap toInline inlines)
69+ toBlock (CT.Header i inlines) = Header i (fmap toInline inlines)
70+ toBlock (CT.Blockquote blocks) = Blockquote (fmap toBlock blocks)
71+ toBlock (CT.List b t blocks) = List b t (fmap (fmap toBlock) blocks)
72+ toBlock (CT.CodeBlock attrs text) = CodeBlock attrs text ()
73+ toBlock (CT.HtmlBlock src) = HtmlBlock src
74+ toBlock CT.HRule = HRule
75+
76+ toInline :: CT.Inline -> Inline ()
77+ toInline (CT.Str t) = Str t
78+ toInline CT.Space = Space
79+ toInline CT.SoftBreak = SoftBreak
80+ toInline CT.LineBreak = LineBreak
81+ toInline (CT.Emph is) = Emph (fmap toInline is)
82+ toInline (CT.Strong is) = Strong (fmap toInline is)
83+ toInline (CT.Code src) = Code src ()
84+ toInline (CT.Link is url title) = Link (fmap toInline is) url title
85+ toInline (CT.Image is url title) = Image (fmap toInline is) url title
86+ toInline (CT.Entity txt) = Entity txt
87+ toInline (CT.RawHtml src) = RawHtml src
2488
2589
2690options = CT.Options { CT.sanitize = True
@@ -30,90 +94,151 @@ options = CT.Options { CT.sanitize = True
3094 }
3195
3296-- | Convert a docstring to be shown by the pretty-printer
33−renderDocstring :: Docstring -> Doc OutputAnnotation
34−renderDocstring (CT.Doc _ blocks) = renderBlocks blocks
97+renderDocstring :: (a -> Doc OutputAnnotation) -> Docstring (Maybe a) -> Doc OutputAnnotation
98+renderDocstring pp (DocString _ blocks) = renderBlocks pp blocks
3599
36100-- | Construct a docstring consisting of the first block-level element of the
37101-- argument docstring, for use in summaries.
38−overview :: Docstring -> Docstring
39−overview (CT.Doc opts blocks) = CT.Doc opts (S.take 1 blocks)
40−
41−renderBlocks :: CT.Blocks -> Doc OutputAnnotation
42−renderBlocks blocks | S.length blocks > 1 = F.foldr1 (\b1 b2 -> b1 <> line <> line <> b2) $
43− fmap renderBlock blocks
44− | S.length blocks == 1 = renderBlock (S.index blocks 0)
45− | otherwise = empty
46−
47−renderBlock :: CT.Block -> Doc OutputAnnotation
48−renderBlock (CT.Para inlines) = renderInlines inlines
49−renderBlock (CT.Header lvl inlines) = renderInlines inlines <+> parens (text (show lvl))
50−renderBlock (CT.Blockquote blocks) = indent 8 $ renderBlocks blocks
51−renderBlock (CT.List b ty blockss) = renderList b ty blockss
52−renderBlock (CT.CodeBlock attr src) = indent 8 $ text (T.unpack src)
53−renderBlock (CT.HtmlBlock txt) = text "<html block>" -- TODO
54−renderBlock CT.HRule = text "----------------------"
55−
56−renderList :: Bool -> CT.ListType -> [CT.Blocks] -> Doc OutputAnnotation
57−renderList b (CT.Bullet c) blockss = vsep $ map (hang 4 . (char c <+>) . renderBlocks) blockss
58−renderList b (CT.Numbered nw i) blockss =
102+overview :: Docstring a -> Docstring a
103+overview (DocString opts blocks) = DocString opts (S.take 1 blocks)
104+
105+renderBlocks :: (a -> Doc OutputAnnotation) -> Blocks (Maybe a) -> Doc OutputAnnotation
106+renderBlocks pp blocks | S.length blocks > 1 = F.foldr1 (\b1 b2 -> b1 <> line <> line <> b2) $
107+ fmap (renderBlock pp) blocks
108+ | S.length blocks == 1 = renderBlock pp (S.index blocks 0)
109+ | otherwise = empty
110+
111+renderBlock :: (a -> Doc OutputAnnotation) -> Block (Maybe a) -> Doc OutputAnnotation
112+renderBlock pp (Para inlines) = renderInlines pp inlines
113+renderBlock pp (Header lvl inlines) = renderInlines pp inlines <+> parens (text (show lvl))
114+renderBlock pp (Blockquote blocks) = indent 8 $ renderBlocks pp blocks
115+renderBlock pp (List b ty blockss) = renderList pp b ty blockss
116+renderBlock pp (CodeBlock attr src Nothing) = indent 8 $ text (T.unpack src)
117+renderBlock pp (CodeBlock attr src (Just tm)) = indent 8 $ pp tm
118+renderBlock pp (HtmlBlock txt) = text "<html block>" -- TODO
119+renderBlock pp HRule = text "----------------------"
120+
121+renderList :: (a -> Doc OutputAnnotation) -> Bool -> CT.ListType -> [Blocks (Maybe a)] -> Doc OutputAnnotation
122+renderList pp b (CT.Bullet c) blockss = vsep $ map (hang 4 . (char c <+>) . renderBlocks pp) blockss
123+renderList pp b (CT.Numbered nw i) blockss =
59124 vsep $
60125 zipWith3 (\n p txt -> hang 4 $ text (show n) <> p <+> txt)
61− [i..] (repeat punc) (map renderBlocks blockss)
126+ [i..] (repeat punc) (map (renderBlocks pp) blockss)
62127 where punc = case nw of
63128 CT.PeriodFollowing -> char '.'
64129 CT.ParenFollowing -> char '('
65130
66−renderInlines :: CT.Inlines -> Doc OutputAnnotation
67−renderInlines = F.foldr (<>) empty . fmap renderInline
68−
69−renderInline :: CT.Inline -> Doc OutputAnnotation
70−renderInline (CT.Str s) = text $ T.unpack s
71−renderInline CT.Space = softline
72−renderInline CT.SoftBreak = softline
73−renderInline CT.LineBreak = line
74−renderInline (CT.Emph txt) = annotate (AnnTextFmt ItalicText) $ renderInlines txt -- TODO
75−renderInline (CT.Strong txt) = annotate (AnnTextFmt BoldText) $ renderInlines txt -- TODO
76−renderInline (CT.Code txt) = text $ T.unpack txt
77−renderInline (CT.Link body url title) = annotate (AnnTextFmt UnderlineText) (renderInlines body) <+>
131+renderInlines :: (a -> Doc OutputAnnotation) -> Inlines (Maybe a) -> Doc OutputAnnotation
132+renderInlines pp = F.foldr (<>) empty . fmap (renderInline pp)
133+
134+renderInline :: (a -> Doc OutputAnnotation) -> Inline (Maybe a) -> Doc OutputAnnotation
135+renderInline pp (Str s) = text $ T.unpack s
136+renderInline pp Space = softline
137+renderInline pp SoftBreak = softline
138+renderInline pp LineBreak = line
139+renderInline pp (Emph txt) = annotate (AnnTextFmt ItalicText) $ renderInlines pp txt
140+renderInline pp (Strong txt) = annotate (AnnTextFmt BoldText) $ renderInlines pp txt
141+renderInline pp (Code txt Nothing) = text $ T.unpack txt
142+renderInline pp (Code txt (Just tm)) = pp tm
143+renderInline pp (Link body url title) = annotate (AnnTextFmt UnderlineText) (renderInlines pp body) <+>
78144 parens (text $ T.unpack url)
79−renderInline (CT.Image body url title) = text "<image>" -- TODO
80−renderInline (CT.Entity a) = text $ "<entity " ++ T.unpack a ++ ">" -- TODO
81−renderInline (CT.RawHtml txt) = text "<html content>" --TODO
145+renderInline pp (Image body url title) = text "<image>" -- TODO
146+renderInline pp (Entity a) = text $ "<entity " ++ T.unpack a ++ ">" -- TODO
147+renderInline pp (RawHtml txt) = text "<html content>" --TODO
82148
83149-- | The empty docstring
84−emptyDocstring :: Docstring
85−emptyDocstring = CT.Doc options S.empty
150+emptyDocstring :: Docstring a
151+emptyDocstring = DocString options S.empty
86152
87153-- | Check whether a docstring is emtpy
88−nullDocstring :: Docstring -> Bool
89−nullDocstring (CT.Doc _ blocks) = S.null blocks
154+nullDocstring :: Docstring a -> Bool
155+nullDocstring (DocString _ blocks) = S.null blocks
90156
91157-- | Empty documentation for a definition
92−noDocs :: (Docstring, [(Name, Docstring)])
158+noDocs :: (Docstring a, [(Name, Docstring a)])
93159noDocs = (emptyDocstring, [])
94160
95161-- | Does a string occur in the docstring?
96−containsText :: T.Text -> Docstring -> Bool
97−containsText str (CT.Doc _ blocks) = F.any (blockContains (T.toLower str)) blocks
162+containsText :: T.Text -> Docstring a -> Bool
163+containsText str (DocString _ blocks) = F.any (blockContains (T.toLower str)) blocks
98164 -- blockContains and inlineContains should always be called with a lower-case search string
99− where blockContains :: T.Text -> CT.Block -> Bool
100− blockContains str (CT.Para inlines) = F.any (inlineContains str) inlines
101− blockContains str (CT.Header lvl inlines) = F.any (inlineContains str) inlines
102− blockContains str (CT.Blockquote blocks) = F.any (blockContains str) blocks
103− blockContains str (CT.List b ty blockss) = F.any (F.any (blockContains str)) blockss
104− blockContains str (CT.CodeBlock attr src) = T.isInfixOf str (T.toLower src)
105− blockContains str (CT.HtmlBlock txt) = False -- TODO
106− blockContains str CT.HRule = False
107−
108− inlineContains :: T.Text -> CT.Inline -> Bool
109− inlineContains str (CT.Str s) = T.isInfixOf str (T.toLower s)
110− inlineContains str CT.Space = False
111− inlineContains str CT.SoftBreak = False
112− inlineContains str CT.LineBreak = False
113− inlineContains str (CT.Emph txt) = F.any (inlineContains str) txt
114− inlineContains str (CT.Strong txt) = F.any (inlineContains str) txt
115− inlineContains str (CT.Code txt) = T.isInfixOf str (T.toLower txt)
116− inlineContains str (CT.Link body url title) = F.any (inlineContains str) body
117− inlineContains str (CT.Image body url title) = False
118− inlineContains str (CT.Entity a) = False
119− inlineContains str (CT.RawHtml txt) = T.isInfixOf str (T.toLower txt)
165+ where blockContains :: T.Text -> Block a -> Bool
166+ blockContains str (Para inlines) = F.any (inlineContains str) inlines
167+ blockContains str (Header lvl inlines) = F.any (inlineContains str) inlines
168+ blockContains str (Blockquote blocks) = F.any (blockContains str) blocks
169+ blockContains str (List b ty blockss) = F.any (F.any (blockContains str)) blockss
170+ blockContains str (CodeBlock attr src _) = T.isInfixOf str (T.toLower src)
171+ blockContains str (HtmlBlock txt) = False -- TODO
172+ blockContains str HRule = False
173+
174+ inlineContains :: T.Text -> Inline a -> Bool
175+ inlineContains str (Str s) = T.isInfixOf str (T.toLower s)
176+ inlineContains str Space = False
177+ inlineContains str SoftBreak = False
178+ inlineContains str LineBreak = False
179+ inlineContains str (Emph txt) = F.any (inlineContains str) txt
180+ inlineContains str (Strong txt) = F.any (inlineContains str) txt
181+ inlineContains str (Code txt _) = T.isInfixOf str (T.toLower txt)
182+ inlineContains str (Link body url title) = F.any (inlineContains str) body
183+ inlineContains str (Image body url title) = False
184+ inlineContains str (Entity a) = False
185+ inlineContains str (RawHtml txt) = T.isInfixOf str (T.toLower txt)
186+
187+
188+renderHtml :: Docstring (Maybe Term) -> Html
189+renderHtml = renderDoc . fromDocstring
190+ where
191+ fromDocstring :: Docstring (Maybe Term) -> CT.Doc
192+ fromDocstring (DocString opts blocks) = CT.Doc opts (fmap fromBlock blocks)
193+
194+ fromBlock :: Block (Maybe Term) -> CT.Block
195+ fromBlock (Para inlines) = CT.Para (fmap fromInline inlines)
196+ fromBlock (Header i inlines) = CT.Header i (fmap fromInline inlines)
197+ fromBlock (Blockquote blocks) = CT.Blockquote (fmap fromBlock blocks)
198+ fromBlock (List b t blocks) = CT.List b t (fmap (fmap fromBlock) blocks)
199+ fromBlock (CodeBlock attrs text _) = CT.CodeBlock attrs text
200+ fromBlock (HtmlBlock src) = CT.HtmlBlock src
201+ fromBlock HRule = CT.HRule
202+
203+ fromInline :: Inline (Maybe Term) -> CT.Inline
204+ fromInline (Str t) = CT.Str t
205+ fromInline Space = CT.Space
206+ fromInline SoftBreak = CT.SoftBreak
207+ fromInline LineBreak = CT.LineBreak
208+ fromInline (Emph is) = CT.Emph (fmap fromInline is)
209+ fromInline (Strong is) = CT.Strong (fmap fromInline is)
210+ fromInline (Code src _) = CT.Code src
211+ fromInline (Link is url title) = CT.Link (fmap fromInline is) url title
212+ fromInline (Image is url title) = CT.Image (fmap fromInline is) url title
213+ fromInline (Entity txt) = CT.Entity txt
214+ fromInline (RawHtml src) = CT.RawHtml src
215+
216+
217+-- | Annotate the code samples in a docstring
218+annotCode :: forall a b. (String -> b) -- ^ How to annotate code samples
219+ -> Docstring a
220+ -> Docstring b
221+annotCode annot (DocString opts blocks)
222+ = DocString opts $ fmap annotCodeBlock blocks
223+ where
224+ annotCodeBlock :: Block a -> Block b
225+ annotCodeBlock (Para inlines) = Para (fmap annotCodeInline inlines)
226+ annotCodeBlock (Header i inlines) = Header i (fmap annotCodeInline inlines)
227+ annotCodeBlock (Blockquote blocks) = Blockquote (fmap annotCodeBlock blocks)
228+ annotCodeBlock (List b t blocks) = List b t (fmap (fmap annotCodeBlock) blocks)
229+ annotCodeBlock (CodeBlock attrs src _) = CodeBlock attrs src (annot (T.unpack src))
230+ annotCodeBlock (HtmlBlock src) = HtmlBlock src
231+ annotCodeBlock HRule = HRule
232+
233+ annotCodeInline :: Inline a -> Inline b
234+ annotCodeInline (Str t) = Str t
235+ annotCodeInline Space = Space
236+ annotCodeInline SoftBreak = SoftBreak
237+ annotCodeInline LineBreak = LineBreak
238+ annotCodeInline (Emph is) = Emph (fmap annotCodeInline is)
239+ annotCodeInline (Strong is) = Strong (fmap annotCodeInline is)
240+ annotCodeInline (Code src _) = Code src (annot (T.unpack src))
241+ annotCodeInline (Link is url title) = Link (fmap annotCodeInline is) url title
242+ annotCodeInline (Image is url title) = Image (fmap annotCodeInline is) url title
243+ annotCodeInline (Entity txt) = Entity txt
244+ annotCodeInline (RawHtml src) = RawHtml src