-- | The Hoogle API. To perform a search you call 'search' with a 'Database' (obtained by 'loadDatabase') and a -- 'Query' (obtained by 'parseQuery'). module Hoogle( -- * Utility types TagStr(..), showTagText, showTagANSI, showTagHTML, showTagHTMLWith, H.ParseError(..), URL, H.Language(..), -- * Database Database, loadDatabase, saveDatabase, createDatabase, mergeDatabase, showDatabase, defaultDatabaseLocation, -- * Query Query, parseQuery, H.renderQuery, H.queryDatabases, H.queryPackages, H.querySetPackage, -- * Score Score, H.scoring, -- * Search Result(..), search, suggestions, completions, queryExact, H.ItemKind(..) ) where import Hoogle.Store.All import General.Base import General.System import System.FilePath import Hoogle.DataBase2.Type import Hoogle.DataBase2.Str import System.IO.Unsafe import Paths_hoogle import Hoogle.Type.TagStr import qualified Hoogle.DataBase.All as H import qualified Hoogle.Query.All as H import qualified Hoogle.Score.All as H import qualified Hoogle.Search.All as H import qualified Hoogle.Type.All as H import qualified Hoogle.Language.Haskell as H import Hoogle.Query.All(Query, exactSearch) import Hoogle.Score.All(Score) -- Turn on the new index/search pieces new = False new2 = False -- * Database -- | A Hoogle database, containing a set of functions/items which can be searched. The 'Database' type is used -- for a variety of purposes: -- -- [Creation] A database is created by merging existing databases with the 'Monoid' instance and 'mappend', -- or by creating a new 'Database' from an input file with 'createDatabase'. -- -- [Serialization] A database is saved to disk with 'saveDatabase' and loaded from disk with 'loadDatabase'. -- -- [Searching] A database is searched using 'search'. newtype Database = Database [(FilePath, H.DataBase)] toDataBase (Database x) = H.combineDataBase $ map snd x instance NFData Database where rnf (Database a) = rnf a instance Monoid Database where mempty = Database [] mappend (Database xs) (Database ys) = Database $ xs ++ ys instance Show Database where show = show . toDataBase -- | Save a database to a file. saveDatabase :: FilePath -> Database -> IO () saveDatabase file x@(Database xs) = do performGC H.saveDataBase file $ toDataBase x when new $ do performGC mergeStr [x <.> "str" | (x,_) <- xs] (file <.> "str") mergeDatabase :: [FilePath] -> FilePath -> IO () mergeDatabase src out = do x <- mapM loadDatabase src saveDatabase out $ mconcat x -- | Load a database from a file. If the database was not saved with the same version of Hoogle, -- it will probably throw an error. loadDatabase :: FilePath -> IO Database loadDatabase x = do db <- H.loadDataBase x; return $ Database [(x, db)] defaultDatabaseLocation :: IO FilePath defaultDatabaseLocation = getDataDir -- | Create a database from an input definition. Source files for Hoogle databases are usually -- stored in UTF8 format, and should be read using 'hSetEncoding' and 'utf8'. createDatabase :: H.HackageURL -> H.Language -- ^ Which format the input definition is in. -> [Database] -- ^ A list of databases which contain definitions this input definition relies upon (e.g. types, aliases, instances). -> String -- ^ The input definitions, usually with one definition per line, in a format specified by the 'Language'. -> FilePath -- ^ Output file -> IO [H.ParseError] -- ^ A list of any parse errors present in the input definition that were skipped. createDatabase url _ dbs src out = do let (err,res) = H.parseInputHaskell url src let xs = concat [map snd x | Database x <- dbs] let db = H.createDataBase xs res performGC items <- H.saveDataBase out db -- don't build .str for .dep files when (new && takeExtension out == ".hoo") $ do createStr' (newPackage $ takeBaseName out) (map (Pos *** fromOnce) items) (out <.> "str") when (new2 && takeExtension out == ".hoo") $ do items <- fmap (map snd) $ H.saveDataBase (dropExtension out <.> "idx.hoo") $ H.createDataBaseEntries res items <- return $ flip map items $ unsafeFmapOnce $ \e -> e{H.entryLocations = map (first $ const "") $ H.entryLocations e, H.entryName="", H.entryText=mempty, H.entryDocs=mempty} H.saveDataBase (dropExtension out <.> "str.hoo") $ H.createDataBaseText items H.saveDataBase (dropExtension out <.> "typ.hoo") $ H.createDataBaseType xs res items return () return err -- | Show debugging information on some parts of the database. If the second argument -- is 'Nothing' the whole database will be shown. Otherwise, the listed parts will be shown. showDatabase :: Database -> Maybe [String] -> String showDatabase x sects = concatMap (`H.showDataBase` toDataBase x) $ fromMaybe [""] sects -- Hoogle.Query -- | Parse a query for a given language, returning either a parse error, or a query. parseQuery :: H.Language -> String -> Either H.ParseError Query parseQuery _ = H.parseQuery -- Hoogle.Search -- Invariant: locations will not be empty data Result = Result {locations :: [(URL, [(URL, String)])] -- your location, your parents ,self :: TagStr -- ^ Rendered view for the entry, including name/keywords/type as appropriate, colors matching 'renderQuery' ,docs :: TagStr -- ^ Documentation for the entry } deriving (Eq, Show) toResult :: H.Result -> (Score,Result) toResult r@(H.Result ent view score) = (score, Result parents self docs) where self = H.renderResult r parents = map (second $ map f) $ H.entryLocations ent f = (H.entryURL &&& H.entryName) . fromOnce docs = H.renderDocs $ H.entryDocs ent -- | Perform a search. The results are returned lazily. search :: Database -> Query -> [(Score,Result)] search (Database xs@((root,_):_)) (H.Query [name] Nothing scopes Nothing False) | new && all simple scopes = unsafePerformIO $ map toResult <$> searchStr' resolve (map fst xs) name where resolve pkg pos = runSGetAt pos (takeDirectory root pkg <.> "hoo") get simple (H.Scope a b _) = a && b == H.Package search (Database xs) q = map toResult $ H.search (map snd xs) q -- | Given a query and a database optionally give a list of what the user might have meant. suggestions :: Database -> Query -> Maybe TagStr suggestions (Database dbs) q = H.suggestQuery (map snd dbs) q -- | Given a query string and a database return a list of the possible completions for the search. completions :: Database -> String -> [String] completions x = H.completions (toDataBase x) -- FIXME: Doing a merge on completions? Bad idea. -- | Given a query, set whether it is an exact query. queryExact :: Maybe H.ItemKind -> Query -> Query queryExact kind q = q { exactSearch = kind }