@@ -0,0 +1,238 @@
1+{-# LANGUAGE TupleSections #-}
2+{-# LANGUAGE OverloadedStrings #-}
3+{-# LANGUAGE RecordWildCards #-}
4+{-# LANGUAGE ViewPatterns #-}
5+
6+-- | Functions for rendering documentation generated from PureScript code.
7+
8+module Language.PureScript.Docs.Render (
9+ renderPackage,
10+ renderModule
11+) where
12+
13+import Control.Monad
14+import Data.Either
15+import Data.Version (Version)
16+import Data.Monoid ((<>))
17+import Data.Maybe (mapMaybe)
18+import Data.List (nub)
19+
20+import qualified Language.PureScript as P
21+
22+import Language.PureScript.Docs.MonoidExtras
23+import Language.PureScript.Docs.RenderedCode
24+import Language.PureScript.Docs.Types
25+
26+-- |
27+-- Render a Package, given its name, its current version, and a list of
28+-- contained modules.
29+--
30+renderPackage :: String -> Version -> [P.Module] -> RenderedPackage
31+renderPackage name vers mods =
32+ RenderedPackage name vers (map renderModule mods)
33+
34+-- |
35+-- Render a single Module.
36+--
37+renderModule :: P.Module -> RenderedModule
38+renderModule m@(P.Module coms moduleName _ exps) =
39+ RenderedModule (show moduleName) comments declarations
40+ where
41+ comments = renderComments coms
42+ declarations = groupChildren declarationsWithChildren
43+ declarationsWithChildren = mapMaybe go (P.exportedDeclarations m)
44+ go decl = getDeclarationTitle decl
45+ >>= renderDeclaration exps decl
46+
47+-- | An intermediate stage which we go through during rendering.
48+--
49+-- In the first pass, we take all top level declarations in the module, and
50+-- render those which should appear at the top level in the output, as well as
51+-- those which should appear as children of other declarations in the output.
52+--
53+-- In the second pass, we move all children under their respective parents,
54+-- or discard them if none are found.
55+--
56+-- This two-pass system is only necessary for type instance declarations, since
57+-- they appear at the top level in the AST, and since they might need to appear
58+-- as children in two places (for example, if a data type defined in a module
59+-- is an instance of a type class also defined in that module).
60+--
61+-- This data type is used as an intermediate type between the two stages. The
62+-- Left case is a child declaration, together with a list of parent declaration
63+-- titles which this may appear as a child of.
64+--
65+-- The Right case is a top level declaration which should pass straight through
66+-- the second stage; the only way it might change is if child declarations are
67+-- added to it.
68+type IntermediateDeclaration
69+ = Either ([String], RenderedChildDeclaration) RenderedDeclaration
70+
71+-- | Move child declarations into their respective parents; the second pass.
72+-- See the comments under the type synonym IntermediateDeclaration for more
73+-- information.
74+groupChildren :: [IntermediateDeclaration] -> [RenderedDeclaration]
75+groupChildren (partitionEithers -> (children, toplevels)) =
76+ foldl go toplevels children
77+ where
78+ go ds (parentTitles, child) =
79+ map (\d ->
80+ if rdTitle d `elem` parentTitles
81+ then d { rdChildren = rdChildren d ++ [child] }
82+ else d) ds
83+
84+getDeclarationTitle :: P.Declaration -> Maybe String
85+getDeclarationTitle (P.TypeDeclaration name _) = Just (show name)
86+getDeclarationTitle (P.ExternDeclaration _ name _ _) = Just (show name)
87+getDeclarationTitle (P.DataDeclaration _ name _ _) = Just (show name)
88+getDeclarationTitle (P.ExternDataDeclaration name _) = Just (show name)
89+getDeclarationTitle (P.TypeSynonymDeclaration name _ _) = Just (show name)
90+getDeclarationTitle (P.TypeClassDeclaration name _ _ _) = Just (show name)
91+getDeclarationTitle (P.TypeInstanceDeclaration name _ _ _ _) = Just (show name)
92+getDeclarationTitle (P.PositionedDeclaration _ _ d) = getDeclarationTitle d
93+getDeclarationTitle _ = Nothing
94+
95+basicDeclaration :: String -> RenderedCode -> Maybe IntermediateDeclaration
96+basicDeclaration title code = Just (Right (RenderedDeclaration title Nothing code Nothing []))
97+
98+renderDeclaration :: Maybe [P.DeclarationRef] -> P.Declaration -> String -> Maybe IntermediateDeclaration
99+renderDeclaration _ (P.TypeDeclaration ident' ty) title =
100+ basicDeclaration title code
101+ where
102+ code = ident (show ident')
103+ <> sp <> syntax "::" <> sp
104+ <> renderType ty
105+renderDeclaration _ (P.ExternDeclaration _ ident' _ ty) title =
106+ basicDeclaration title code
107+ where
108+ code = ident (show ident')
109+ <> sp <> syntax "::" <> sp
110+ <> renderType ty
111+renderDeclaration exps (P.DataDeclaration dtype name args ctors) title =
112+ Just (Right (RenderedDeclaration title Nothing code Nothing children))
113+ where
114+ typeApp = foldl P.TypeApp (P.TypeConstructor (P.Qualified Nothing name)) (map toTypeVar args)
115+ exported = filter (P.isDctorExported name exps . fst) ctors
116+ code = keyword (show dtype) <> sp <> renderType typeApp
117+ children = map renderCtor exported
118+ -- TODO: Comments for data constructors?
119+ renderCtor (ctor', tys) =
120+ let typeApp' = foldl P.TypeApp (P.TypeConstructor (P.Qualified Nothing ctor')) tys
121+ childCode = renderType typeApp'
122+ in RenderedChildDeclaration (show ctor') Nothing childCode Nothing ChildDataConstructor
123+renderDeclaration _ (P.ExternDataDeclaration name kind') title =
124+ basicDeclaration title code
125+ where
126+ code = keywordData <> sp
127+ <> renderType (P.TypeConstructor (P.Qualified Nothing name))
128+ <> sp <> syntax "::" <> sp
129+ <> renderKind kind'
130+renderDeclaration _ (P.TypeSynonymDeclaration name args ty) title =
131+ basicDeclaration title code
132+ where
133+ typeApp = foldl P.TypeApp (P.TypeConstructor (P.Qualified Nothing name)) (map toTypeVar args)
134+ code = mintersperse sp
135+ [ keywordType
136+ , renderType typeApp
137+ , syntax "="
138+ , renderType ty
139+ ]
140+renderDeclaration _ (P.TypeClassDeclaration name args implies ds) title = do
141+ Just (Right (RenderedDeclaration title Nothing code Nothing children))
142+ where
143+ code = mintersperse sp $
144+ [keywordClass]
145+ ++ maybe [] (:[]) superclasses
146+ ++ [renderType classApp]
147+ ++ if (not (null ds)) then [keywordWhere] else []
148+
149+ superclasses
150+ | null implies = Nothing
151+ | otherwise = Just $
152+ syntax "("
153+ <> mintersperse (syntax "," <> sp) (map renderImplies implies)
154+ <> syntax ") <="
155+
156+ renderImplies (pn, tys) =
157+ let supApp = foldl P.TypeApp (P.TypeConstructor pn) tys
158+ in renderType supApp
159+
160+ classApp = foldl P.TypeApp (P.TypeConstructor (P.Qualified Nothing name)) (map toTypeVar args)
161+
162+ children = map renderClassMember ds
163+
164+ -- TODO: Comments for type class members
165+ renderClassMember (P.PositionedDeclaration _ _ d) = renderClassMember d
166+ renderClassMember (P.TypeDeclaration ident' ty) =
167+ let childCode =
168+ mintersperse sp
169+ [ ident (show ident')
170+ , syntax "::"
171+ , renderType ty
172+ ]
173+ in RenderedChildDeclaration (show ident') Nothing childCode Nothing ChildTypeClassMember
174+ renderClassMember _ = error "Invalid argument to renderClassMember."
175+renderDeclaration _ (P.TypeInstanceDeclaration name constraints className tys _) title = do
176+ Just (Left (classNameString : typeNameStrings, childDecl))
177+ where
178+ classNameString = unQual className
179+ typeNameStrings = nub (concatMap (P.everythingOnTypes (++) extractProperNames) tys)
180+ unQual x = let (P.Qualified _ y) = x in show y
181+
182+ extractProperNames (P.TypeConstructor n) = [unQual n]
183+ extractProperNames (P.SaturatedTypeSynonym n _) = [unQual n]
184+ extractProperNames _ = []
185+
186+ childDecl = RenderedChildDeclaration title Nothing code Nothing ChildInstance
187+
188+ code =
189+ mintersperse sp $
190+ [ keywordInstance
191+ , ident (show name)
192+ , syntax "::"
193+ ] ++ maybe [] (:[]) constraints'
194+ ++ [ renderType classApp ]
195+
196+ constraints'
197+ | null constraints = Nothing
198+ | otherwise = Just (syntax "(" <> renderedConstraints <> syntax ") =>")
199+
200+ renderedConstraints = mintersperse (syntax "," <> sp) (map renderConstraint constraints)
201+
202+ renderConstraint (pn, tys') =
203+ let supApp = foldl P.TypeApp (P.TypeConstructor pn) tys'
204+ in renderType supApp
205+
206+ classApp = foldl P.TypeApp (P.TypeConstructor className) tys
207+renderDeclaration exps (P.PositionedDeclaration srcSpan com d') title =
208+ fmap (addComments . addSourceSpan) (renderDeclaration exps d' title)
209+ where
210+ addComments (Left (t, d)) = Left (t, d { rcdComments = renderComments com })
211+ addComments (Right d) = Right (d { rdComments = renderComments com })
212+
213+ addSourceSpan (Left (t, d)) = Left (t, d { rcdSourceSpan = Just srcSpan })
214+ addSourceSpan (Right d) = Right (d { rdSourceSpan = Just srcSpan })
215+renderDeclaration _ _ _ = Nothing
216+
217+renderComments :: [P.Comment] -> Maybe String
218+renderComments cs = do
219+ let raw = concatMap toLines cs
220+ guard (all hasPipe raw && not (null raw))
221+ return (go raw)
222+ where
223+ go = unlines . map stripPipes
224+
225+ toLines (P.LineComment s) = [s]
226+ toLines (P.BlockComment s) = lines s
227+
228+ hasPipe s = case dropWhile (== ' ') s of { ('|':_) -> True; _ -> False }
229+
230+ stripPipes = dropPipe . dropWhile (== ' ')
231+
232+ dropPipe ('|':' ':s) = s
233+ dropPipe ('|':s) = s
234+ dropPipe s = s
235+
236+toTypeVar :: (String, Maybe P.Kind) -> P.Type
237+toTypeVar (s, Nothing) = P.TypeVar s
238+toTypeVar (s, Just k) = P.KindedType (P.TypeVar s) k