1{-# LANGUAGE OverloadedStrings, DeriveDataTypeable #-}
4import System.Console.CmdArgs
10import Control.Applicative ((<$>))
11import Control.Arrow (second)
13import Data.Aeson.Encode.Pretty
14import qualified Data.List as List
15import qualified Data.Map as Map
16import qualified Data.ByteString.Lazy as BS
17import qualified Data.Text as Text
19import qualified SourceSyntax.Helpers as Help
20import qualified SourceSyntax.Type as T
21import qualified SourceSyntax.Expression as E
22import qualified SourceSyntax.Declaration as D
24import Text.Parsec hiding (newline,spaces)
25import Parse.Declaration (alias,datatype,infixDecl)
26import Parse.Expression (typeAnnotation)
28import Parse.Module (moduleDef)
31 { files :: [FilePath] }
32 deriving (Data,Typeable,Show,Eq)
36 { files = def &= args &= typ "FILES"
37 } &= help "Generate documentation for Elm"
38 &= summary ("Generate documentation for Elm, (c) Evan Czaplicki")
42 flags <- cmdArgs defaultFlags
43 mapM_ parseFile (files flags)
46config = Config { confIndent = 2, confCompare = keyOrder keys }
48 keys = ["tag","name","document","comment","raw","aliases","datatypes"
49 ,"values","typeVariables","type","constructors"]
51parseFile :: FilePath -> IO ()
53 source <- readFile path
54 case iParse docs source of
56 putStrLn $ "Documenting " ++ path
57 let docPath = "docs" </> replaceExtension path ".json"
58 createDirectoryIfMissing True (dropFileName docPath)
59 BS.writeFile docPath (encodePretty' config json)
61 hPutStrLn stderr $ "Parse error in " ++ path ++ " at " ++ show err
66 (name, exports, structure) <- moduleDocs
68 return $ documentToJson name exports structure things
70docComment :: IParser String
73 contents <- closeComment
74 let reversed = dropWhile (`elem` " \n\r") . drop 2 $ reverse contents
75 return $ dropWhile (==' ') (reverse reversed)
77moduleDocs :: IParser (String, [String], String)
80 (names,exports) <- moduleDef
81 manyTill (string " " <|> newline <?> "more whitespace")
82 (lookAhead (string "{-|") <?> "module documentation comment")
83 structure <- docComment
84 return (List.intercalate "." names, exports, structure)
86document :: IParser [(String, D.ParseDeclaration, String)]
87document = onFreshLines (\t ts -> ts ++ [t]) [] docThing
89docThing :: IParser (String, D.ParseDeclaration, String)
90docThing = uncommentable <|> commented <|> uncommented ""
101 uncommented comment = do
102 (src,def) <- withSource $ choice [ alias, datatype, D.Definition <$> typeAnnotation ]
103 return (comment, def, src)
106documentToJson name exports structure things =
107 object $ [ "name" .= name
108 , "document" .= structure
109 , "values" .= toList values
110 , "aliases" .= toList aliases
111 , "datatypes" .= toList adts
114 (values, aliases, adts) = collect Map.empty Map.empty Map.empty Map.empty things
116 toList dict = map object . Map.elems $ filterPublics dict
118 exportMap = Map.fromList (zip exports exports)
120 case Map.null exportMap of
122 False -> Map.filterWithKey (\k _ -> Map.member k exportMap) dict
124collect infixes types aliases adts things =
126 [] -> (Map.union customOps nonCustomOps, aliases, adts)
128 nonCustomOps = Map.mapWithKey addDefaultInfix $ Map.difference types infixes
129 addDefaultInfix name pairs
130 | all Help.isSymbol name = addInfix (D.L, 9 :: Int) pairs
133 customOps = Map.intersectionWith addInfix infixes types
134 addInfix (assoc,prec) pairs =
135 [ "associativity" .= show assoc, "precedence" .= prec ] ++ pairs
137 (comment, decl, source) : rest ->
139 D.Fixity assoc prec name ->
140 collect (Map.insert name (assoc,prec) infixes) types aliases adts rest
141 D.Definition (E.TypeAnnotation name tipe) ->
142 collect infixes (insert name [ "type" .= tipe ] types) aliases adts rest
143 D.TypeAlias name vars tipe ->
144 let fields = ["typeVariables" .= vars, "type" .= tipe ]
145 in collect infixes types (insert name fields aliases) adts rest
146 D.Datatype name vars ctors ->
147 let tipe = T.Data name (map T.Var vars)
148 fields = ["typeVariables" .= vars
149 , "constructors" .= map (ctorToJson tipe) ctors ]
150 in collect infixes types aliases (insert name fields adts) rest
152 insert name fields dict = Map.insert name (obj name fields) dict
154 [ "name" .= name, "raw" .= source, "comment" .= comment ] ++ fields
156instance ToJSON T.Type where
161 let tipes = T.collectLambdas tipe in
162 [ "tag" .= ("function" :: Text.Text)
163 , "args" .= toJSON (init tipes)
164 , "result" .= toJSON (last tipes)
168 [ "tag" .= ("var" :: Text.Text)
173 [ "tag" .= ("adt" :: Text.Text)
174 , "name" .= toJSON name
175 , "args" .= map toJSON ts
178 T.Record fields ext ->
179 [ "tag" .= ("record" :: Text.Text)
180 , "fields" .= toJSON (map (toJSON . second toJSON) fields)
181 , "extension" .= toJSON ext
184ctorToJson :: T.Type -> (String, [T.Type]) -> Value
185ctorToJson tipe (ctor, tipes) =
186 object [ "name" .= ctor
187 , "type" .= foldr T.Lambda tipe tipes ]
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list