1{-# LANGUAGE PatternGuards #-}
2{-# LANGUAGE OverloadedStrings #-}
3{-# OPTIONS_GHC -fwarn-incomplete-patterns #-}
5-- | Generation of HTML documentation for Idris code
6module Idris.IdrisDoc (generateDocs) where
8import Idris.Core.TT (Name (..), sUN, SpecialName (..), OutputAnnotation (..),
9 TextFormatting (..), txt, str, nsroot, constIsType, toAlist)
10import Idris.Core.Evaluate (ctxtAlist, Def (..), lookupDefAcc,
11 Accessibility (..), isDConName, isFnName,
13import Idris.ParseHelpers (opChars)
16import Idris.Docstrings (nullDocstring)
17import qualified Idris.Docstrings as Docstrings
19import IRTS.System (getDataFileName)
21import Control.Applicative ((<|>))
22import Control.Monad (forM_)
23import Control.Monad.Trans.Except
24import Control.Monad.Trans.State.Strict
28import qualified Data.ByteString as BS
29import qualified Data.ByteString.Lazy as BS2
30import qualified Data.Text.Encoding as E
31import qualified Data.List as L
32import qualified Data.List.Split as LS
33import qualified Data.Map as M hiding ((!))
34import Data.Monoid (mempty)
35import qualified Data.Ord (compare)
36import qualified Data.Set as S
37import qualified Data.Text as T
42import System.Directory
44import Text.PrettyPrint.Annotated.Leijen (displayDecorated, renderCompact)
46import Text.Blaze (toValue, contents)
47import Text.Blaze.Internal (MarkupM (Empty))
48import Text.Blaze.Html5 ((!), toHtml, preEscapedToHtml)
49import qualified Text.Blaze.Html5 as H
50import Text.Blaze.Html5.Attributes as A
51import Text.Blaze.Html.Renderer.Utf8 (renderHtml)
52import qualified Text.Blaze.Html.Renderer.String as R
53import Text.Blaze.Renderer.String (renderMarkup)
55-- ---------------------------------------------------------------- [ Public ]
57-- | Generates HTML documentation for a series of loaded namespaces
58-- and their dependencies.
59generateDocs :: IState -- ^ IState where all necessary information is
61 -> [Name] -- ^ List of namespaces to generate
63 -> FilePath -- ^ The directory to which documentation will
65 -> IO (Either String ())
66generateDocs ist nss' out =
67 do let nss = map toNsName nss'
68 docs <- fetchInfo ist nss
69 let (c, io) = foldl (checker docs) (0, return ()) nss
72 then catchIOError (createDocs ist docs out) (err . show)
73 else err "No namespaces to generate documentation for"
75 where checker docs st ns | M.member ns docs = st
76 checker docs (c, io) ns = (c+1, do prev <- io; warnMissing ns)
78 putStrLn $ "Warning: Ignoring empty or non-existing namespace '" ++
79 (nsName2Str ns) ++ "'"
81-- ----------------------------------------------------------------- [ Types ]
83-- | Either an error message or a result
84type Failable = Either String
86-- | Internal representation of a fully qualified namespace name
89-- | All information to be documented about a single namespace member
90type NsItem = (Name, Maybe Docs, Accessibility)
92-- | Docstrings containing fully elaborated term annotations
93type FullDocstring = Docstrings.Docstring Docstrings.DocTerm
95-- | All information to be documented about a namespace
96data NsInfo = NsInfo { nsDocstring :: Maybe FullDocstring,
97 nsContents :: [NsItem]
100-- | A map from namespace names to information about them
101type NsDict = M.Map NsName NsInfo
103-- --------------------------------------------------------------- [ Utility ]
105-- | Make an error message
106err :: String -> IO (Failable ())
107err s = return $ Left s
113-- | Converts a Name into a [Text] corresponding to the namespace
115toNsName :: Name -- ^ Name to convert
118toNsName (NS n ns) = (toNsName n) ++ ns
122-- | Retrieves the namespace part of a Name
123getNs :: Name -- ^ Name to retrieve namespace for
129-- | String to replace for the root namespace
131rootNsStr = "[builtins]"
134-- | Converts a NsName to string form
135nsName2Str :: NsName -- ^ NsName to convert
137nsName2Str n = if null n then rootNsStr else name n
141 name (ns:nss) = (name nss) ++ ('.' : str ns)
143-- --------------------------------------------------------- [ Info Fetching ]
145-- | Fetch info about namespaces and their contents
146fetchInfo :: IState -- ^ IState to fetch info from
147 -> [NsName] -- ^ List of namespaces to fetch info for
148 -> IO NsDict -- ^ Mapping from namespace name to
149 -- info about its contents
151 do let originNss = S.fromList nss
153 let accessible = M.map (filterContents filterInclude) info
154 nonOrphan = M.map (updateContents removeOrphans) accessible
155 nonEmpty = M.filter (not . null . nsContents) nonOrphan
156 reachedNss = traceNss nonEmpty originNss S.empty
157 return $ M.filterWithKey (\k _ -> S.member k reachedNss) nonEmpty
160 filterContents p (NsInfo md ns) = NsInfo md (filter p ns)
161 updateContents f x = x { nsContents = f (nsContents x) }
163-- | Removes loose class methods and data constructors,
164-- leaving them documented only under their parent.
165removeOrphans :: [NsItem] -- ^ List to remove orphans from
166 -> [NsItem] -- ^ Orphan-free list
168 let children = S.fromList $ concatMap (names . (\(_, d, _) -> d)) list
169 in filter ((`S.notMember` children) . (\(n, _, _) -> n)) list
171 where names (Just (DataDoc _ fds)) = map (\(FD n _ _ _ _) -> n) fds
172 names (Just (ClassDoc _ _ fds _ _ _ _)) = map (\(FD n _ _ _ _) -> n) fds
175-- | Whether a Name names something which should be documented
176filterName :: Name -- ^ Name to check
177 -> Bool -- ^ Predicate result
178filterName (UN n) | '@':'@':_ <- str n = False
179filterName (UN _) = True
180filterName (NS n _) = filterName n
184-- | Whether a NsItem should be included in the documentation.
185-- It must not be Hidden and filterName must return True for the name.
186-- Also it must have Docs -- without Docs, nothing can be done.
187filterInclude :: NsItem -- ^ Accessibility to check
188 -> Bool -- ^ Predicate result
189filterInclude (name, Just _, Public) | filterName name = True
190filterInclude (name, Just _, Frozen) | filterName name = True
191filterInclude _ = False
194-- | Finds all namespaces indirectly referred by a set of namespaces.
195-- The NsItems of the namespaces are searched for references.
196traceNss :: NsDict -- ^ Mappings of namespaces and their contents
197 -> S.Set NsName -- ^ Set of namespaces to trace
198 -> S.Set NsName -- ^ Set of namespaces which has been traced
199 -> S.Set NsName -- ^ Set of namespaces to trace and all traced one
201 let nsTracer ns | Just nsis <- M.lookup ns nsd = map referredNss (nsContents nsis)
202 nsTracer _ = [S.empty] -- Ignore
203 reached = S.unions $ concatMap nsTracer (S.toList sT)
204 processed = S.union sT sD
205 untraced = S.difference reached processed
206 in if S.null untraced then processed
207 else traceNss nsd untraced processed
210-- | Gets all namespaces directly referred by a NsItem
211referredNss :: NsItem -- ^ The name to get all directly
212 -- referred namespaces for
214referredNss (_, Nothing, _) = S.empty
215referredNss (n, Just d, _) =
216 let fds = getFunDocs d
217 ts = concatMap types fds
218 names = concatMap (extractPTermNames) ts
219 in S.map getNs $ S.fromList names
221 where getFunDocs (FunDoc f) = [f]
222 getFunDocs (DataDoc f fs) = f:fs
223 getFunDocs (ClassDoc _ _ fs _ _ _ _) = fs
224 getFunDocs (NamedInstanceDoc _ fd) = [fd]
225 getFunDocs (ModDoc _ _) = []
226 types (FD _ _ args t _) = t:(map second args)
227 second (_, x, _, _) = x
230-- | Returns an NsDict of containing all known namespaces and their contents
233nsDict ist = flip (foldl addModDoc) modDocs $ foldl adder (return M.empty) nameDefList
234 where nameDefList = ctxtAlist $ tt_ctxt ist
235 adder m (n, _) = do map <- m
236 doc <- loadDocs ist n
237 let access = getAccess ist n
238 nInfo = NsInfo Nothing [(n, doc, access)]
239 return $ M.insertWith addNameInfo (getNs n) nInfo map
240 addNameInfo (NsInfo m ns) (NsInfo m' ns') = NsInfo (m <|> m') (ns ++ ns')
241 modDocs = map (\(mn, d) -> (mn, NsInfo (Just d) [])) $ toAlist (idris_moduledocs ist)
242 addModDoc :: IO NsDict -> (Name, NsInfo) -> IO NsDict
243 addModDoc dict (mn, d) = fmap (M.insertWith addNameInfo (getNs mn) d) dict
248-- | Gets the Accessibility for a Name
249getAccess :: IState -- ^ IState containing accessibility information
250 -> Name -- ^ The Name to retrieve access for
253 let res = lookupDefAcc n False (tt_ctxt ist)
258-- | Simple predicate for whether an NsItem has Docs
259hasDoc :: NsItem -- ^ The NsItem to test
260 -> Bool -- ^ The result
261hasDoc (_, Just _, _) = True
265-- | Predicate saying whether a Name possibly may have docs defined
266-- Without this, getDocs from Idris.Docs may fail a pattern match.
267mayHaveDocs :: Name -- ^ The Name to test
268 -> Bool -- ^ The result
269mayHaveDocs (UN _) = True
270mayHaveDocs (NS n _) = mayHaveDocs n
274-- | Retrieves the Docs for a Name
275loadDocs :: IState -- ^ IState to extract infomation from
276 -> Name -- ^ Name to load Docs for
279 | mayHaveDocs n = do docs <- runExceptT $ evalStateT (getDocs n FullDocs) ist
280 case docs of Right d -> return (Just d)
281 Left _ -> return Nothing
282 | otherwise = return Nothing
285-- | Extracts names referred from a type.
286-- The covering of all PTerms ensures that we avoid unanticipated cases,
287-- though all of them are not needed. The author just did not know which!
288-- TODO: Remove unnecessary cases
289extractPTermNames :: PTerm -- ^ Where to extract names from
290 -> [Name] -- ^ Extracted names
291extractPTermNames (PRef _ n) = [n]
292extractPTermNames (PInferRef _ n) = [n]
293extractPTermNames (PPatvar _ n) = [n]
294extractPTermNames (PLam _ n p1 p2) = n : concatMap extract [p1, p2]
295extractPTermNames (PPi _ n p1 p2) = n : concatMap extract [p1, p2]
296extractPTermNames (PLet _ n p1 p2 p3) = n : concatMap extract [p1, p2, p3]
297extractPTermNames (PTyped p1 p2) = concatMap extract [p1, p2]
298extractPTermNames (PApp _ p pas) = let names = concatMap extractPArg pas
299 in (extract p) ++ names
300extractPTermNames (PAppBind _ p pas) = let names = concatMap extractPArg pas
301 in (extract p) ++ names
302extractPTermNames (PMatchApp _ n) = [n]
303extractPTermNames (PCase _ p ps) = let (ps1, ps2) = unzip ps
304 in concatMap extract (p:(ps1 ++ ps2))
305extractPTermNames (PRefl _ p) = extract p
306extractPTermNames (PEq _ _ _ p1 p2) = concatMap extract [p1, p2]
307extractPTermNames (PRewrite _ a b m) | Just c <- m =
308 concatMap extract [a, b, c]
309extractPTermNames (PRewrite _ a b _) = concatMap extract [a, b]
310extractPTermNames (PPair _ _ p1 p2) = concatMap extract [p1, p2]
311extractPTermNames (PDPair _ _ a b c) = concatMap extract [a, b, c]
312extractPTermNames (PAlternative _ l) = concatMap extract l
313extractPTermNames (PHidden p) = extract p
314extractPTermNames (PGoal _ p1 n p2) = n : concatMap extract [p1, p2]
315extractPTermNames (PDoBlock pdos) = concatMap extractPDo pdos
316extractPTermNames (PIdiom _ p) = extract p
317extractPTermNames (PMetavar n) = [n]
318extractPTermNames (PProof tacts) = concatMap extractPTactic tacts
319extractPTermNames (PTactics tacts) = concatMap extractPTactic tacts
320extractPTermNames (PCoerced p) = extract p
321extractPTermNames (PDisamb _ p) = extract p
322extractPTermNames (PUnifyLog p) = extract p
323extractPTermNames (PNoImplicits p) = extract p
324extractPTermNames (PRunTactics _ p) = extract p
325extractPTermNames _ = []
327-- | Shorter name for extractPTermNames
328extract :: PTerm -- ^ Where to extract names from
329 -> [Name] -- ^ Extracted names
330extract = extractPTermNames
332-- | Helper function for extractPTermNames
333extractPArg :: PArg -> [Name]
334extractPArg (PImp {pname=n, getTm=p}) = n : extract p
335extractPArg (PExp {getTm=p}) = extract p
336extractPArg (PConstraint {getTm=p}) = extract p
337extractPArg (PTacImplicit {pname=n, getScript=p1, getTm=p2})
338 = n : (concatMap extract [p1, p2])
340-- | Helper function for extractPTermNames
341extractPDo :: PDo -> [Name]
342extractPDo (DoExp _ p) = extract p
343extractPDo (DoBind _ n p) = n : extract p
344extractPDo (DoBindP _ p1 p2 ps) = let (ps1, ps2) = unzip ps
346 in concatMap extract (p1 : p2 : ps')
347extractPDo (DoLet _ n p1 p2) = n : concatMap extract [p1, p2]
348extractPDo (DoLetP _ p1 p2) = concatMap extract [p1, p2]
350-- | Helper function for extractPTermNames
351extractPTactic :: PTactic -> [Name]
352extractPTactic (Intro ns) = ns
353extractPTactic (Focus n) = [n]
354extractPTactic (Refine n _) = [n]
355extractPTactic (Rewrite p) = extract p
356extractPTactic (Induction p) = extract p
357extractPTactic (CaseTac p) = extract p
358extractPTactic (Equiv p) = extract p
359extractPTactic (MatchRefine n) = [n]
360extractPTactic (LetTac n p) = n : extract p
361extractPTactic (LetTacTy n p1 p2) = n : concatMap extract [p1, p2]
362extractPTactic (Exact p) = extract p
363extractPTactic (ProofSearch _ _ _ m ns) | Just n <- m = n : ns
364extractPTactic (ProofSearch _ _ _ _ ns) = ns
365extractPTactic (Try t1 t2) = concatMap extractPTactic [t1, t2]
366extractPTactic (TSeq t1 t2) = concatMap extractPTactic [t1, t2]
367extractPTactic (ApplyTactic p) = extract p
368extractPTactic (ByReflection p) = extract p
369extractPTactic (Reflect p) = extract p
370extractPTactic (Fill p) = extract p
371extractPTactic (GoalType _ t) = extractPTactic t
372extractPTactic (TCheck p) = extract p
373extractPTactic (TEval p) = extract p
376-- ------------------------------------------------------- [ HTML Generation ]
378-- | Generates the actual HTML output based on info from a NsDict
379-- A merge of the new docs and any existing docs located in the output dir
381-- TODO: Ensure the merge always succeeds.
382-- Currently the content of 'docs/<builtins>.html' may change between
383-- runs, thus not always containing all items referred from other
384-- namespace .html files.
385createDocs :: IState -- ^ Needed to determine the types of names
386 -> NsDict -- ^ All info from which to generate docs
387 -> FilePath -- ^ The base directory to which
388 -- documentation will be written.
390createDocs ist nsd out =
391 do new <- not `fmap` (doesFileExist $ out </> "IdrisDoc")
392 existing_nss <- existingNamespaces out
393 let nss = S.union (M.keysSet nsd) existing_nss
394 dExists <- doesDirectoryExist out
395 if new && dExists then err $ "Output directory (" ++ out ++ ") is" ++
396 " already in use for other than IdrisDoc."
398 createDirectoryIfMissing True out
399 foldl docGen (return ()) (M.toList nsd)
401 -- Create an empty IdrisDoc file to signal 'out' is used for IdrisDoc
402 if new -- But only if it not already existed...
403 then withFile (out </> "IdrisDoc") WriteMode ((flip hPutStr) "")
408 where docGen io (n, c) = do io; createNsDoc ist n c out
411-- | (Over)writes the 'index.html' file in the given directory with
412-- an (updated) index of namespaces in the documentation
413createIndex :: S.Set NsName -- ^ Set of namespace names to
414 -- include in the index
415 -> FilePath -- ^ The base directory to which
416 -- documentation will be written.
419 do (path, h) <- openTempFile out "index.html"
420 BS2.hPut h $ renderHtml $ wrapper Nothing $ do
422 H.ul ! class_ "names" $ do
423 let path ns = "docs" ++ "/" ++ genRelNsPath ns "html"
424 item ns = do let n = toHtml $ nsName2Str ns
425 link = toValue $ path ns
426 H.li $ H.a ! href link ! class_ "code" $ n
427 sort = L.sortBy (\n1 n2 -> reverse n1 `compare` reverse n2)
428 forM_ (sort $ S.toList nss) item
430 renameFile path (out </> "index.html")
433-- | Generates a HTML file for a namespace and its contents.
434-- The location for e.g. Prelude.Algebra is <base>/Prelude/Algebra.html
435createNsDoc :: IState -- ^ Needed to determine the types of names
436 -> NsName -- ^ The name of the namespace to
437 -- create documentation for
438 -> NsInfo -- ^ The contents of the namespace
439 -> FilePath -- ^ The base directory to which
440 -- documentation will be written.
442createNsDoc ist ns content out =
443 do let tpath = out </> "docs" </> (genRelNsPath ns "html")
444 dir = takeDirectory tpath
445 file = takeFileName tpath
446 haveDocs (_, Just d, _) = [d]
448 -- We cannot do anything without a Doc
449 content' = concatMap haveDocs (nsContents content)
450 createDirectoryIfMissing True dir
451 (path, h) <- openTempFile dir file
452 BS2.hPut h $ renderHtml $ wrapper (Just ns) $ do
453 H.h1 $ toHtml (nsName2Str ns)
454 case nsDocstring content of
456 Just docstring -> Docstrings.renderHtml docstring
457 H.dl ! class_ "decls" $ forM_ content' (createOtherDoc ist)
459 renameFile path tpath
462-- | Generates a relative filepath for a namespace, appending an extension
463genRelNsPath :: NsName -- ^ Namespace to generate a path for
464 -> String -- ^ Extension suffix
466genRelNsPath ns suffix = nsName2Str ns <.> suffix
469-- | Generates a HTML type signature with proper tags
470-- TODO: Turn docstrings into title attributes more robustly
471genTypeHeader :: IState -- ^ Needed to determine the types of names
472 -> FunDoc -- ^ Type to generate type declaration for
473 -> H.Html -- ^ Resulting HTML
474genTypeHeader ist (FD n _ args ftype _) = do
475 H.span ! class_ (toValue $ "name " ++ getType n)
476 ! title (toValue $ show n)
477 $ toHtml $ name $ nsroot n
478 H.span ! class_ "word" $ do nbsp; ":"; nbsp
479 H.span ! class_ "signature" $ preEscapedToHtml htmlSignature
482 htmlSignature = displayDecorated decorator $ renderCompact signature
483 signature = pprintPTerm defaultPPOption [] names (idris_infixes ist) ftype
484 names = [ n | (n@(UN n'), _, _, _) <- args,
485 not (T.isPrefixOf (txt "__") n') ]
487 decorator (AnnConst c) str | constIsType c = htmlSpan str "type" str
488 | otherwise = htmlSpan str "data" str
489 decorator (AnnData _ _) str = htmlSpan str "data" str
490 decorator (AnnType _ _) str = htmlSpan str "type" str
491 decorator AnnKeyword str = htmlSpan "" "keyword" str
492 decorator (AnnBoundName n i) str | Just t <- M.lookup n docs =
493 let cs = (if i then "implicit " else "") ++ "documented boundvar"
495 decorator (AnnBoundName _ i) str =
496 let cs = (if i then "implicit " else "") ++ "boundvar"
497 in htmlSpan "" cs str
498 decorator (AnnName n _ _ _) str
499 | filterName n = htmlLink (show n) (getType n) (link n) str
500 | otherwise = htmlSpan "" (getType n) str
501 decorator (AnnTextFmt BoldText) str = "<b>" ++ str ++ "</b>"
502 decorator (AnnTextFmt UnderlineText) str = "<u>" ++ str ++ "</u>"
503 decorator (AnnTextFmt ItalicText) str = "<i>" ++ str ++ "</i>"
504 decorator _ str = str
506 htmlSpan :: String -> String -> String -> String
507 htmlSpan t cs str = do
508 R.renderHtml $ H.span ! class_ (toValue cs)
511 htmlLink :: String -> String -> String -> String -> String
512 htmlLink t cs a str = do
513 R.renderHtml $ H.a ! class_ (toValue cs)
514 ! title (toValue t) ! href (toValue a)
517 docs = M.fromList $ mapMaybe docExtractor args
518 docExtractor (_, _, _, Nothing) = Nothing
519 docExtractor (n, _, _, Just d) = Just (n, doc2Str d)
520 -- TODO: Remove <p> tags more robustly
521 doc2Str d = let dirty = renderMarkup $ contents $ Docstrings.renderHtml $ d
522 in take (length dirty - 8) $ drop 3 dirty
524 name (NS n ns) = show (NS (sUN $ name n) ns)
525 name n = let n' = show n
526 in if (head n') `elem` opChars
530 link n = let path = genRelNsPath (getNs n) "html"
531 in path ++ "#" ++ (show n)
533 getType :: Name -> String
534 getType n = let ctxt = tt_ctxt ist
536 _ | isDConName n ctxt -> "constructor"
537 _ | isFnName n ctxt -> "function"
538 _ | isTConName n ctxt -> "type"
541-- | Generates HTML documentation for a function.
542createFunDoc :: IState -- ^ Needed to determine the types of names
543 -> FunDoc -- ^ Function to generate block for
544 -> H.Html -- ^ Resulting HTML
545createFunDoc ist fd@(FD name docstring args ftype fixity) = do
546 H.dt ! (A.id $ toValue $ show name) $ genTypeHeader ist fd
548 (if nullDocstring docstring then Empty else Docstrings.renderHtml docstring)
549 let args' = filter (\(_, _, _, d) -> isJust d) args
550 if (not $ null args') || (isJust fixity)
552 if (isJust fixity) then do
553 H.dt ! class_ "fixity" $ "Fixity"
554 let f = fromJust fixity
555 H.dd ! class_ "fixity" ! title (toValue $ show f) $ genFix f
560 where genFix (Infixl {prec=p}) =
561 toHtml $ "Left associative, precedence " ++ show p
562 genFix (Infixr {prec=p}) =
563 toHtml $ "Left associative, precedence " ++ show p
564 genFix (InfixN {prec=p}) =
565 toHtml $ "Non-associative, precedence " ++ show p
566 genFix (PrefixN {prec=p}) =
567 toHtml $ "Prefix, precedence " ++ show p
568 genArg (_, _, _, Nothing) = Empty
569 genArg (name, _, _, Just docstring) = do
570 H.dt $ toHtml $ show name
571 H.dd $ Docstrings.renderHtml docstring
574-- | Generates HTML documentation for any Docs type
575-- TODO: Generate actual signatures for typeclasses
576createOtherDoc :: IState -- ^ Needed to determine the types of names
577 -> Docs -- ^ Namespace item to generate HTML block for
578 -> H.Html -- ^ Resulting HTML
579createOtherDoc ist (FunDoc fd) = createFunDoc ist fd
581createOtherDoc ist (ClassDoc n docstring fds _ _ _ _) = do
582 H.dt ! (A.id $ toValue $ show n) $ do
583 H.span ! class_ "word" $ do "class"; nbsp
584 H.span ! class_ "name type"
585 ! title (toValue $ show n)
586 $ toHtml $ name $ nsroot n
587 H.span ! class_ "signature" $ nbsp
589 (if nullDocstring docstring then Empty else Docstrings.renderHtml docstring)
590 H.dl ! class_ "decls" $ forM_ fds (createFunDoc ist)
592 where name (NS n ns) = show (NS (sUN $ name n) ns)
593 name n = let n' = show n
594 in if (head n') `elem` opChars
598createOtherDoc ist (DataDoc fd@(FD n docstring args _ _) fds) = do
599 H.dt ! (A.id $ toValue $ show n) $ do
600 H.span ! class_ "word" $ do "data"; nbsp
603 (if nullDocstring docstring then Empty else Docstrings.renderHtml docstring)
604 let args' = filter (\(_, _, _, d) -> isJust d) args
606 then H.dl $ forM_ args' genArg
608 H.dl ! class_ "decls" $ forM_ fds (createFunDoc ist)
610 where genArg (_, _, _, Nothing) = Empty
611 genArg (name, _, _, Just docstring) = do
612 H.dt $ toHtml $ show name
613 H.dd $ Docstrings.renderHtml docstring
615createOtherDoc ist (NamedInstanceDoc _ fd) = createFunDoc ist fd
617createOtherDoc ist (ModDoc _ docstring) = do
618 Docstrings.renderHtml docstring
620-- | Generates everything but the actual content of the page
621wrapper :: Maybe NsName -- ^ Namespace name, unless it is the index
622 -> H.Html -- ^ Inner HTML
625 let (index, str) = extract ns
626 base = if index then "" else "../"
627 styles = base ++ "styles.css" :: String
628 indexPage = base ++ "index.html" :: String
629 in H.docTypeHtml $ do
633 if index then " Index" else do
636 H.link ! type_ "text/css" ! rel "stylesheet"
637 ! href (toValue styles)
638 H.body ! class_ (if index then "index" else "namespace") $ do
639 H.div ! class_ "wrapper" $ do
642 if index then Empty else do
645 H.nav $ H.a ! href (toValue indexPage) $ "Index"
646 H.div ! class_ "container" $ inner
648 "Produced by IdrisDoc version "
651 where extract (Just ns) = (False, nsName2Str ns)
652 extract _ = (True, "")
655-- | Non-break space character
657nbsp = preEscapedToHtml (" " :: String)
660-- | Returns a list of namespaces already documented in a IdrisDoc directory
661existingNamespaces :: FilePath -- ^ The base directory containing the
662 -- 'docs' directory with existing
665existingNamespaces out = do
666 let docs = out ++ "/" ++ "docs"
667 str2Ns s | s == rootNsStr = []
668 str2Ns s = reverse $ T.splitOn (T.singleton '.') (txt s)
669 toNs fp = do isFile <- doesFileExist $ docs </> fp
670 let isHtml = ".html" == takeExtension fp
671 name = dropExtension fp
673 return $ if isFile && isHtml then Just ns else Nothing
674 docsExists <- doesDirectoryExist docs
677 else do contents <- getDirectoryContents docs
678 namespaces <- catMaybes `fmap` (sequence $ map toNs contents)
679 return $ S.fromList namespaces
682-- | Copies IdrisDoc dependencies such as stylesheets to a directory
683copyDependencies :: FilePath -- ^ The base directory to which
684 -- dependencies should be written
686copyDependencies dir =
687 do styles <- getDataFileName $ "idrisdoc" </> "styles.css"
688 copyFile styles (dir </> "styles.css")
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