@@ -10,215 +10,221 @@ import System.IO
11import Control.Applicative ((<$>))
11import Control.Applicative ((<$>))
12import Control.Arrow (second)
12import Control.Arrow (second)
14−import Data.Aeson.Encode.Pretty
13+import qualified Data.Aeson.Encode.Pretty as Json
15−import Data.Aeson.Types (Pair)
16−import qualified Data.List as List
17import qualified Data.Map as Map
14import qualified Data.Map as Map
18−import qualified Data.ByteString.Lazy as BS
15+import qualified Data.ByteString.Lazy.Char8 as BS
19−import qualified Data.Text as Text
21−import qualified AST.Helpers as Help
17+import qualified AST.Type as Type
22−import qualified AST.Type as T
23−import qualified AST.Declaration as D
18+import qualified AST.Declaration as Decl
24−import qualified AST.Expression.Source as Src
19+import qualified AST.Expression.Source as Source
25import qualified AST.Variable as Var
20import qualified AST.Variable as Var
27−import Text.Parsec hiding (newline,spaces)
22+import Text.Parsec hiding (newline, spaces)
28−import Parse.Declaration (typeDecl,infixDecl)
23+import qualified Parse.Declaration as Parse (typeDecl, infixDecl)
29−import Parse.Expression (typeAnnotation)
24+import qualified Parse.Expression as Parse (typeAnnotation)
30−import Parse.Helpers
25+import qualified Parse.Helpers as Parse
31−import qualified Parse.Module as Module
26+import qualified Parse.Module as Parse (header)
27+import qualified Elm.Compiler.Module as Module
28+import qualified Elm.Docs as Docs
33data Flags = Flags
33data Flags = Flags
34− { files :: [FilePath] }
34+ { input :: FilePath
35+ , output :: Maybe FilePath
35 deriving (Data,Typeable,Show,Eq)
37 deriving (Data,Typeable,Show,Eq)
37defaultFlags :: Flags
40defaultFlags :: Flags
38defaultFlags = Flags
41defaultFlags = Flags
39− { files = def &= args &= typ "FILES"
42+ { input = def &= args &= typ "FILE"
44+ , output = Nothing &= typFile
45+ &= help "file name for generated JSON documentation"
40 } &= help "Generate documentation for Elm"
47 } &= help "Generate documentation for Elm"
41− &= summary ("Generate documentation for Elm, (c) Evan Czaplicki")
48+ &= summary "Generate documentation for Elm, (c) Evan Czaplicki"
45− flags <- cmdArgs defaultFlags
46− mapM_ parseFile (files flags)
51+-- GENERATE DOCUMENTATION
55+ do flags <- cmdArgs defaultFlags
56+ source <- readFile (input flags)
57+ case Parse.iParse documentation source of
59+ let json = Json.encodePretty' config docs in
61+ Nothing -> BS.putStrLn json
63+ do createDirectoryIfMissing True (dropFileName docPath)
64+ BS.writeFile docPath json
67+ do hPutStrLn stderr (show err)
48−config :: Config
71+config :: Json.Config
74+ { Json.confIndent = 2
49−config = Config { confIndent = 2, confCompare = keyOrder keys }
75+ , Json.confCompare = Json.keyOrder keys
51− keys = ["tag","name","document","comment","raw","aliases","datatypes"
78+ keys =
52− ,"values","typeVariables","type","constructors"]
79+ [ "tag", "name", "comment", "aliases", "types"
53−
80+ , "values", "func", "args", "type", "cases"
54−parseFile :: FilePath -> IO ()
56− source <- readFile path
57− case iParse docs source of
59− putStrLn $ "Documenting " ++ path
60− let docPath = "docs" </> replaceExtension path ".json"
61− createDirectoryIfMissing True (dropFileName docPath)
62− BS.writeFile docPath (encodePretty' config json)
64− hPutStrLn stderr $ "Parse error in " ++ path ++ " at " ++ show err
69− (name, _exports, structure) <- moduleDocs
71− return $ documentToJson name structure things
73−docComment :: IParser String
76− contents <- closeComment
77− let reversed = dropWhile (`elem` " \n\r") . drop 2 $ reverse contents
78− return $ dropWhile (==' ') (reverse reversed)
80−moduleDocs :: IParser (String, Var.Listing Var.Value, String)
83− (names,exports) <- Module.header
84− manyTill (string " " <|> newline <?> "more whitespace")
85− (lookAhead (string "{-|") <?> "module documentation comment")
86− structure <- docComment
87− return (List.intercalate "." names, exports, structure)
89−document :: IParser [(String, D.SourceDecl, String)]
90−document = onFreshLines (\t ts -> ts ++ [t]) [] docThing
92−docThing :: IParser (String, D.SourceDecl, String)
93−docThing = uncommentable <|> commented <|> uncommented ""
100− comment <- docComment
104− uncommented comment = do
105− (src,def) <- withSource $ choice [ typeDecl, D.Definition <$> typeAnnotation ]
106− return (comment, def, src)
109−documentToJson :: String -> String -> [(String, D.SourceDecl, String)] -> Value
110−documentToJson name structure things =
113− , "document" .= structure
114− , "values" .= toList values
115− , "aliases" .= toList aliases
116− , "datatypes" .= toList adts
84+-- PARSE DOCUMENTATION
86+documentation :: Parse.IParser Docs.Documentation
88+ do optional Parse.freshLine
89+ (names, _exports) <- Parse.header
91+ manyTill (string " " <|> Parse.newline <?> "more whitespace")
92+ (lookAhead (string "{-|") <?> "module documentation comment")
94+ overview <- docComment
96+ decls <- allDeclarations
98+ let (aliases, unions, values) = categorizeDeclarations decls
100+ return (Docs.Documentation (Module.Name names) overview aliases unions values)
103+docComment :: Parse.IParser String
105+ do try (string "{-|")
106+ contents <- Parse.closeComment
109+ dropWhile (`elem` " \n\r") . drop 2 $ reverse contents
111+ return $ dropWhile (==' ') (reverse reversed)
114+allDeclarations :: Parse.IParser [(String, Decl.SourceDecl)]
116+ Parse.onFreshLines (:) [] declaration
119+declaration :: Parse.IParser (String, Decl.SourceDecl)
121+ uncommentable <|> commented <|> uncommented ""
124+ (,) "" <$> Parse.infixDecl
127+ do comment <- docComment
131+ uncommented comment =
133+ <$> choice [ Parse.typeDecl, Decl.Definition <$> Parse.typeAnnotation ]
136+-- CATEGORIZE DECLARATIONS
138+data CategoryInfo = CategoryInfo
139+ { aliases :: [Docs.Alias]
140+ , unions :: [Docs.Union]
141+ , values :: Map.Map String Docs.Value
142+ , infixes :: Map.Map String (String, Int)
146+emptyInfo :: CategoryInfo
148+ CategoryInfo [] [] Map.empty Map.empty
151+categorizeDeclarations :: [(String, Decl.SourceDecl)] -> ([Docs.Alias], [Docs.Union], [Docs.Value])
152+categorizeDeclarations decls =
153+ (aliases, unions, Map.elems values)
119− (values, aliases, adts) =
155+ (CategoryInfo aliases unions rawValues infixes) =
120− collect Map.empty Map.empty Map.empty Map.empty things
156+ foldr collectInfo emptyInfo decls
122− toList :: Map.Map String [Pair] -> [Value]
158+ values =
123− toList dict =
159+ Map.union
124− map object (Map.elems dict)
126−type Pairs = Map.Map String [Pair]
128−collect :: Map.Map String (D.Assoc, Int) -> Pairs -> Pairs -> Pairs
129− -> [(String, D.SourceDecl, String)]
130− -> (Pairs, Pairs, Pairs)
131−collect infixes types aliases adts things =
133− [] -> (Map.union customOps nonCustomOps, aliases, adts)
135− nonCustomOps = Map.mapWithKey addDefaultInfix $ Map.difference types infixes
136− addDefaultInfix name pairs
137− | all Help.isSymbol name = addInfix (D.L, 9 :: Int) pairs
140− customOps = Map.intersectionWith addInfix infixes types
160+ (Map.intersectionWith addInfixInfo rawValues infixes)
141− addInfix (assoc,prec) pairs =
161+ rawValues
142− [ "associativity" .= show assoc, "precedence" .= prec ] ++ pairs
162+
144− (comment, decl, source) : rest ->
164+addInfixInfo :: Docs.Value -> (String, Int) -> Docs.Value
145− let insert name fields dict = Map.insert name (obj name fields) dict
165+addInfixInfo value infixInfo =
146− obj name fields =
166+ value { Docs.valueAssocPrec = Just infixInfo }
147− [ "name" .= name
167+
148− , "raw" .= source
168+
149− , "comment" .= comment
169+collectInfo :: (String, Decl.SourceDecl) -> CategoryInfo -> CategoryInfo
150− ] ++ fields
170+collectInfo (comment, decl) info =
172+ Decl.Definition def ->
174+ Source.Definition _ _ -> error errorMessage
175+ Source.TypeAnnotation name tipe ->
176+ let value = Docs.Value name comment (toDocType tipe) Nothing
178+ info { values = Map.insert name value (values info) }
180+ Decl.Datatype name args cases ->
181+ let cases' = map (second (map toDocType)) cases
182+ union = Docs.Union name comment args cases'
184+ info { unions = union : unions info }
186+ Decl.TypeAlias name args tipe ->
187+ let alias = Docs.Alias name comment args (toDocType tipe)
152− case decl of
189+ info { aliases = alias : aliases info }
153− D.Definition def ->
190+
155− Src.TypeAnnotation name tipe ->
156− collect infixes (insert name [ "type" .= tipe ] types) aliases adts rest
158− Src.Definition _ _ ->
159− collect infixes types aliases adts rest
161− D.Datatype name vars ctors ->
162− let tipe = T.App (T.Type (Var.Raw name)) (map T.Var vars)
163− fields = [ "typeVariables" .= vars
164− , "constructors" .= map (ctorToJson tipe) ctors
166− in collect infixes types aliases (insert name fields adts) rest
168− D.TypeAlias name vars tipe ->
169− let fields = [ "typeVariables" .= vars, "type" .= tipe ]
170− in collect infixes types (insert name fields aliases) adts rest
173− collect infixes types aliases adts rest
175− D.Fixity assoc prec name ->
191+ Decl.Fixity assoc prec name ->
176− collect (Map.insert name (assoc,prec) infixes) types aliases adts rest
192+ let infixInfo = (Decl.assocToString assoc, prec)
178−
194+ info { infixes = Map.insert name infixInfo (infixes info) }
179−instance Var.ToString var => ToJSON (T.Type var) where
195+
180− toJSON tipe =
196+ Decl.Port _ ->
181− object (getFields tipe)
197+ error errorMessage
183− getFields tipe =
199+
184− case tipe of
200+errorMessage :: String
185− T.Lambda _ _ ->
201+errorMessage =
186− let tipes = T.collectLambdas tipe in
202+ "there appears to be a bug in this tool.\n" ++
187− [ "tag" .= ("function" :: Text.Text)
188− , "args" .= toJSON (init tipes)
189− , "result" .= toJSON (last tipes)
193− [ "tag" .= ("var" :: Text.Text)
194− , "name" .= toJSON x
198− [ "tag" .= ("adt" :: Text.Text)
199− , "name" .= toJSON (Var.toString name)
200− , "args" .= ([] :: [()])
203− T.App (T.Type name) ts ->
204− [ "tag" .= ("adt" :: Text.Text)
205− , "name" .= toJSON (Var.toString name)
206− , "args" .= map toJSON ts
209− T.App _ _ -> error "This is an unexpected error with elm-doc, please report it at <https://github.com/elm-lang/Elm/issues>"
203+ "Please report it to <https://github.com/elm-lang/Elm/issues>"
211− T.Record fields ext ->
205+
212− [ "tag" .= ("record" :: Text.Text)
206+-- AST TYPE TO DOC TYPE
213− , "fields" .= toJSON (map (toJSON . second toJSON) fields)
207+
214− , "extension" .= toJSON ext
208+toDocType :: Type.Type Var.Raw -> Docs.Type
215− ]
209+toDocType astType =
211+ Type.Lambda t1 t2 ->
212+ Docs.Lambda (toDocType t1) (toDocType t2)
217+ Type.Type (Var.Raw name) ->
221+ Docs.App (toDocType t) (map toDocType ts)
223+ Type.Record fields ext ->
224+ Docs.Record (map (second toDocType) fields) (fmap toDocType ext)
217− T.Aliased _ t ->
226+ Type.Aliased _ t ->
218− getFields t
227+ toDocType t
220−ctorToJson :: T.RawType -> (String, [T.RawType]) -> Value
229+
221−ctorToJson tipe (ctor, tipes) =
230+
222− object [ "name" .= ctor
223− , "type" .= foldr T.Lambda tipe tipes