mirror of
https://github.com/commercialhaskell/stackage-server.git
synced 2026-02-04 07:10:25 +01:00
commit
5402f33a47
@ -1,4 +1,5 @@
|
|||||||
((haskell-mode . ((haskell-indent-spaces . 4)
|
((haskell-mode . ((haskell-indent-spaces . 4)
|
||||||
|
(hindent-style . "johan-tibell")
|
||||||
(haskell-process-type . cabal-repl)
|
(haskell-process-type . cabal-repl)
|
||||||
(haskell-process-use-ghci . t)))
|
(haskell-process-use-ghci . t)))
|
||||||
(hamlet-mode . ((hamlet/basic-offset . 4)
|
(hamlet-mode . ((hamlet/basic-offset . 4)
|
||||||
|
|||||||
@ -16,13 +16,11 @@ import Data.IORef
|
|||||||
import Foreign.Store
|
import Foreign.Store
|
||||||
import Network.Wai.Handler.Warp
|
import Network.Wai.Handler.Warp
|
||||||
import Yesod
|
import Yesod
|
||||||
import Yesod.Static
|
|
||||||
|
|
||||||
-- | Start the web server.
|
-- | Start the web server.
|
||||||
main :: IO (Store (IORef Application))
|
main :: IO (Store (IORef Application))
|
||||||
main =
|
main =
|
||||||
do s <- static "static"
|
do c <- newChan
|
||||||
c <- newChan
|
|
||||||
(settings,app) <- getApplicationDev
|
(settings,app) <- getApplicationDev
|
||||||
ref <- newIORef app
|
ref <- newIORef app
|
||||||
tid <- forkIO
|
tid <- forkIO
|
||||||
@ -46,7 +44,6 @@ update =
|
|||||||
do ref <- readStore store
|
do ref <- readStore store
|
||||||
c <- readStore (Store 2)
|
c <- readStore (Store 2)
|
||||||
writeChan c ()
|
writeChan c ()
|
||||||
s <- static "static"
|
(_,app) <- getApplicationDev
|
||||||
(_settings,app) <- getApplicationDev
|
|
||||||
writeIORef ref app
|
writeIORef ref app
|
||||||
return store
|
return store
|
||||||
|
|||||||
@ -20,57 +20,74 @@ shouldRedirect = False
|
|||||||
|
|
||||||
getHaddockR :: SnapName -> [Text] -> Handler TypedContent
|
getHaddockR :: SnapName -> [Text] -> Handler TypedContent
|
||||||
getHaddockR slug rest
|
getHaddockR slug rest
|
||||||
| shouldRedirect = redirect $ makeURL slug rest
|
| shouldRedirect = do
|
||||||
| final:_ <- reverse rest, ".html" `isSuffixOf` final = track "Handler.Haddock.getHaddockR" $ do
|
result <- redirectWithVersion slug rest
|
||||||
|
case result of
|
||||||
|
Just route -> redirect route
|
||||||
|
Nothing -> redirect $ makeURL slug rest
|
||||||
|
| final:_ <- reverse rest, ".html" `isSuffixOf` final = do
|
||||||
render <- getUrlRender
|
render <- getUrlRender
|
||||||
|
result <- redirectWithVersion slug rest
|
||||||
let stylesheet = render' $ StaticR haddock_style_css
|
case result of
|
||||||
script = render' $ StaticR haddock_script_js
|
Just route -> redirect route
|
||||||
bootstrap = render' $ StaticR haddock_bootstrap_css
|
Nothing -> do
|
||||||
jquery = render' $ StaticR haddock_jquery_js
|
let stylesheet = render' $ StaticR haddock_style_css
|
||||||
render' = return . ContentText . render
|
script = render' $ StaticR haddock_script_js
|
||||||
|
bootstrap = render' $ StaticR haddock_bootstrap_css
|
||||||
addExtra t@(EventEndElement "head") =
|
jquery = render' $ StaticR haddock_jquery_js
|
||||||
[ EventBeginElement "link"
|
render' = return . ContentText . render
|
||||||
[ ("rel", [ContentText "stylesheet"])
|
addExtra t@(EventEndElement "head") =
|
||||||
, ("href", bootstrap)
|
[ EventBeginElement "link"
|
||||||
]
|
[ ("rel", [ContentText "stylesheet"])
|
||||||
, EventEndElement "link"
|
, ("href", bootstrap)
|
||||||
, EventBeginElement "link"
|
]
|
||||||
[ ("rel", [ContentText "stylesheet"])
|
, EventEndElement "link"
|
||||||
, ("href", [ContentText "https://fonts.googleapis.com/css?family=Open+Sans"])
|
, EventBeginElement "link"
|
||||||
]
|
[ ("rel", [ContentText "stylesheet"])
|
||||||
, EventEndElement "link"
|
, ("href", [ContentText "https://fonts.googleapis.com/css?family=Open+Sans"])
|
||||||
, EventBeginElement "link"
|
]
|
||||||
[ ("rel", [ContentText "stylesheet"])
|
, EventEndElement "link"
|
||||||
, ("href", stylesheet)
|
, EventBeginElement "link"
|
||||||
]
|
[ ("rel", [ContentText "stylesheet"])
|
||||||
, EventEndElement "link"
|
, ("href", stylesheet)
|
||||||
, EventBeginElement "script"
|
]
|
||||||
[ ("src", jquery)
|
, EventEndElement "link"
|
||||||
]
|
, EventBeginElement "script"
|
||||||
, EventEndElement "script"
|
[ ("src", jquery)
|
||||||
, EventBeginElement "script"
|
]
|
||||||
[ ("src", script)
|
, EventEndElement "script"
|
||||||
]
|
, EventBeginElement "script"
|
||||||
, EventEndElement "script"
|
[ ("src", script)
|
||||||
, t
|
]
|
||||||
]
|
, EventEndElement "script"
|
||||||
addExtra t@(EventBeginElement "body" _) = [t] ++ nav
|
, t
|
||||||
addExtra t = [t]
|
]
|
||||||
|
addExtra t@(EventBeginElement "body" _) = [t] ++ nav
|
||||||
req <- parseUrl $ unpack $ makeURL slug rest
|
addExtra t = [t]
|
||||||
(_, res) <- acquireResponse req >>= allocateAcquire
|
req <- parseUrl $ unpack $ makeURL slug rest
|
||||||
|
(_, res) <- acquireResponse req >>= allocateAcquire
|
||||||
doc <- responseBody res
|
doc <- responseBody res
|
||||||
$$ eventConduit
|
$$ eventConduit
|
||||||
=$ concatMapC addExtra
|
=$ concatMapC addExtra
|
||||||
=$ mapC (Nothing, )
|
=$ mapC (Nothing, )
|
||||||
=$ fromEvents
|
=$ fromEvents
|
||||||
|
sendResponse $ toHtml doc
|
||||||
sendResponse $ toHtml doc
|
|
||||||
| otherwise = redirect $ makeURL slug rest
|
| otherwise = redirect $ makeURL slug rest
|
||||||
|
|
||||||
|
redirectWithVersion
|
||||||
|
:: (GetStackageDatabase m,MonadHandler m,RedirectUrl (HandlerSite m) (Route App))
|
||||||
|
=> SnapName -> [Text] -> m (Maybe (Route App))
|
||||||
|
redirectWithVersion slug rest =
|
||||||
|
case rest of
|
||||||
|
[pkg,file] -> do
|
||||||
|
Entity sid _ <- lookupSnapshot slug >>= maybe notFound return
|
||||||
|
mversion <- getPackageVersionBySnapshot sid pkg
|
||||||
|
case mversion of
|
||||||
|
Nothing -> return Nothing -- error "That package is not part of this snapshot."
|
||||||
|
Just version -> do
|
||||||
|
return (Just (HaddockR slug [pkg <> "-" <> version, file]))
|
||||||
|
_ -> return Nothing
|
||||||
|
|
||||||
nav :: [Event]
|
nav :: [Event]
|
||||||
nav =
|
nav =
|
||||||
el "nav"
|
el "nav"
|
||||||
@ -100,6 +117,6 @@ nav =
|
|||||||
close = [EventEndElement name]
|
close = [EventEndElement name]
|
||||||
|
|
||||||
getHaddockBackupR :: [Text] -> Handler ()
|
getHaddockBackupR :: [Text] -> Handler ()
|
||||||
getHaddockBackupR rest = track "Handler.Haddock.getHaddockBackupR" $ redirect $ concat
|
getHaddockBackupR rest = track "Handler.Haddock.getHaddockBackupR" $ redirect $ concat
|
||||||
$ "https://s3.amazonaws.com/haddock.stackage.org"
|
$ "https://s3.amazonaws.com/haddock.stackage.org"
|
||||||
: map (cons '/') rest
|
: map (cons '/') rest
|
||||||
|
|||||||
@ -1,15 +1,16 @@
|
|||||||
|
{-# LANGUAGE QuasiQuotes #-}
|
||||||
module Handler.Hoogle where
|
module Handler.Hoogle where
|
||||||
|
|
||||||
import Control.DeepSeq (NFData(..))
|
import Control.DeepSeq (NFData(..))
|
||||||
import Control.DeepSeq.Generics (genericRnf)
|
import Control.DeepSeq.Generics (genericRnf)
|
||||||
import Control.Spoon (spoon)
|
import Data.Data (Data)
|
||||||
import Data.Data (Data (..))
|
|
||||||
import Data.Text.Read (decimal)
|
import Data.Text.Read (decimal)
|
||||||
import qualified Hoogle
|
import qualified Hoogle
|
||||||
import Import
|
import Import
|
||||||
import Text.Blaze.Html (preEscapedToHtml)
|
import Text.Blaze.Html (preEscapedToHtml)
|
||||||
import Stackage.Database
|
import Stackage.Database
|
||||||
import qualified Stackage.Database.Cron as Cron
|
import qualified Stackage.Database.Cron as Cron
|
||||||
|
import qualified Data.Text as T
|
||||||
|
|
||||||
getHoogleDB :: SnapName -> Handler (Maybe FilePath)
|
getHoogleDB :: SnapName -> Handler (Maybe FilePath)
|
||||||
getHoogleDB name = track "Handler.Hoogle.getHoogleDB" $ do
|
getHoogleDB name = track "Handler.Hoogle.getHoogleDB" $ do
|
||||||
@ -21,7 +22,7 @@ getHoogleR name = track "Handler.Hoogle.getHoogleR" $ do
|
|||||||
Entity _ snapshot <- lookupSnapshot name >>= maybe notFound return
|
Entity _ snapshot <- lookupSnapshot name >>= maybe notFound return
|
||||||
mquery <- lookupGetParam "q"
|
mquery <- lookupGetParam "q"
|
||||||
mpage <- lookupGetParam "page"
|
mpage <- lookupGetParam "page"
|
||||||
exact <- maybe False (const True) <$> lookupGetParam "exact"
|
exact <- isJust <$> lookupGetParam "exact"
|
||||||
mresults' <- lookupGetParam "results"
|
mresults' <- lookupGetParam "results"
|
||||||
let count' =
|
let count' =
|
||||||
case decimal <$> mresults' of
|
case decimal <$> mresults' of
|
||||||
@ -33,25 +34,32 @@ getHoogleR name = track "Handler.Hoogle.getHoogleR" $ do
|
|||||||
_ -> 1
|
_ -> 1
|
||||||
offset = (page - 1) * perPage
|
offset = (page - 1) * perPage
|
||||||
mdatabasePath <- getHoogleDB name
|
mdatabasePath <- getHoogleDB name
|
||||||
heDatabase <- case mdatabasePath of
|
dbPath <- maybe (hoogleDatabaseNotAvailableFor name) return mdatabasePath
|
||||||
Just x -> return $ liftIO $ Hoogle.loadDatabase x
|
|
||||||
Nothing -> hoogleDatabaseNotAvailableFor name
|
|
||||||
|
|
||||||
-- Avoid concurrent Hoogle queries, see
|
-- Avoid concurrent Hoogle queries, see
|
||||||
-- https://github.com/fpco/stackage-server/issues/172
|
-- https://github.com/fpco/stackage-server/issues/172
|
||||||
lock <- appHoogleLock <$> getYesod
|
lock <- appHoogleLock <$> getYesod
|
||||||
mresults <- case mquery of
|
urlRender <- getUrlRender
|
||||||
Just query -> withMVar lock $ const $ runHoogleQuery heDatabase HoogleQueryInput
|
HoogleQueryOutput results mtotalCount <-
|
||||||
{ hqiQueryInput = query
|
case mquery of
|
||||||
, hqiExactSearch = if exact then Just query else Nothing
|
Just query -> do
|
||||||
, hqiLimitTo = count'
|
let input = HoogleQueryInput
|
||||||
, hqiOffsetBy = offset
|
{ hqiQueryInput = query
|
||||||
}
|
, hqiLimitTo = count'
|
||||||
Nothing -> return $ HoogleQueryOutput "" [] Nothing
|
, hqiOffsetBy = offset
|
||||||
|
, hqiExact = exact
|
||||||
|
}
|
||||||
|
|
||||||
|
liftIO $ withMVar lock
|
||||||
|
$ const
|
||||||
|
$ Hoogle.withDatabase dbPath
|
||||||
|
-- NB! I got a segfault when I didn't force with $!
|
||||||
|
$ \db -> return $! runHoogleQuery urlRender name db input
|
||||||
|
Nothing -> return $ HoogleQueryOutput [] Nothing
|
||||||
let queryText = fromMaybe "" mquery
|
let queryText = fromMaybe "" mquery
|
||||||
pageLink p = (SnapshotR name HoogleR
|
pageLink p = (SnapshotR name HoogleR
|
||||||
, (if exact then (("exact", "true"):) else id)
|
, (if exact then (("exact", "true"):) else id)
|
||||||
$ (maybe id (\q' -> (("q", q'):)) mquery)
|
$ maybe id (\q' -> (("q", q'):)) mquery
|
||||||
[("page", tshow p)])
|
[("page", tshow p)])
|
||||||
snapshotLink = SnapshotR name StackageHomeR
|
snapshotLink = SnapshotR name StackageHomeR
|
||||||
hoogleForm = $(widgetFile "hoogle-form")
|
hoogleForm = $(widgetFile "hoogle-form")
|
||||||
@ -85,15 +93,15 @@ perPage = 10
|
|||||||
|
|
||||||
data HoogleQueryInput = HoogleQueryInput
|
data HoogleQueryInput = HoogleQueryInput
|
||||||
{ hqiQueryInput :: Text
|
{ hqiQueryInput :: Text
|
||||||
, hqiExactSearch :: Maybe Text
|
|
||||||
, hqiLimitTo :: Int
|
, hqiLimitTo :: Int
|
||||||
, hqiOffsetBy :: Int
|
, hqiOffsetBy :: Int
|
||||||
|
, hqiExact :: Bool
|
||||||
}
|
}
|
||||||
deriving (Eq, Read, Show, Data, Typeable, Ord, Generic)
|
deriving (Eq, Read, Show, Data, Typeable, Ord, Generic)
|
||||||
|
|
||||||
data HoogleQueryOutput = HoogleQueryBad Text
|
data HoogleQueryOutput = HoogleQueryOutput [HoogleResult] (Maybe Int) -- ^ Int == total count
|
||||||
| HoogleQueryOutput Text [HoogleResult] (Maybe Int) -- ^ Text == HTML version of query, Int == total count
|
deriving (Read, Typeable, Data, Show, Eq, Generic)
|
||||||
deriving (Read, Typeable, Data, Show, Eq)
|
instance NFData HoogleQueryOutput where rnf = genericRnf
|
||||||
|
|
||||||
data HoogleResult = HoogleResult
|
data HoogleResult = HoogleResult
|
||||||
{ hrURL :: String
|
{ hrURL :: String
|
||||||
@ -119,57 +127,52 @@ instance NFData HoogleResult where rnf = genericRnf
|
|||||||
instance NFData PackageLink where rnf = genericRnf
|
instance NFData PackageLink where rnf = genericRnf
|
||||||
instance NFData ModuleLink where rnf = genericRnf
|
instance NFData ModuleLink where rnf = genericRnf
|
||||||
|
|
||||||
runHoogleQuery :: MonadIO m
|
runHoogleQuery :: (Route App -> Text)
|
||||||
=> m Hoogle.Database
|
-> SnapName
|
||||||
|
-> Hoogle.Database
|
||||||
-> HoogleQueryInput
|
-> HoogleQueryInput
|
||||||
-> m HoogleQueryOutput
|
-> HoogleQueryOutput
|
||||||
runHoogleQuery heDatabase HoogleQueryInput {..} =
|
runHoogleQuery renderUrl snapshot hoogledb HoogleQueryInput {..} =
|
||||||
track "Handler.Hoogle.runHoogleQuery" $
|
HoogleQueryOutput targets mcount
|
||||||
runQuery $ Hoogle.parseQuery Hoogle.Haskell query
|
|
||||||
where
|
where
|
||||||
query = unpack hqiQueryInput
|
allTargets = Hoogle.searchDatabase hoogledb query
|
||||||
|
targets = take (min 100 hqiLimitTo)
|
||||||
|
$ drop hqiOffsetBy
|
||||||
|
$ map fixResult allTargets
|
||||||
|
query = unpack $ hqiQueryInput ++ if hqiExact then " is:exact" else ""
|
||||||
|
|
||||||
runQuery (Left err) = return $ HoogleQueryBad (tshow err)
|
mcount = limitedLength 0 allTargets
|
||||||
runQuery (Right query') = do
|
|
||||||
hoogledb <- heDatabase
|
|
||||||
let query'' = Hoogle.queryExact classifier query'
|
|
||||||
rawRes = concatMap fixResult
|
|
||||||
$ Hoogle.search hoogledb query''
|
|
||||||
mres = spoon
|
|
||||||
$ take (min 100 hqiLimitTo)
|
|
||||||
$ drop hqiOffsetBy rawRes
|
|
||||||
mcount = spoon $ limitedLength 0 rawRes
|
|
||||||
limitedLength x [] = Just x
|
|
||||||
limitedLength x (_:rest)
|
|
||||||
| x >= 20 = Nothing
|
|
||||||
| otherwise = limitedLength (x + 1) rest
|
|
||||||
rendered = pack $ Hoogle.showTagHTML $ Hoogle.renderQuery query''
|
|
||||||
return $ case (,) <$> mres <*> mcount of
|
|
||||||
Nothing ->
|
|
||||||
HoogleQueryOutput rendered [] (Just 0)
|
|
||||||
Just (results, mcount') ->
|
|
||||||
HoogleQueryOutput rendered (take hqiLimitTo results) mcount'
|
|
||||||
|
|
||||||
classifier = maybe Nothing
|
limitedLength x [] = Just x
|
||||||
(const (Just Hoogle.UnclassifiedItem))
|
limitedLength x (_:rest)
|
||||||
hqiExactSearch
|
| x >= 20 = Nothing
|
||||||
|
| otherwise = limitedLength (x + 1) rest
|
||||||
|
|
||||||
fixResult (_, Hoogle.Result locs self docs) = do
|
fixResult Hoogle.Target {..} = HoogleResult
|
||||||
(loc, _) <- take 1 locs
|
{ hrURL = case sources of
|
||||||
let sources' = unionsWith (++) $
|
[(_,[ModuleLink _ m])] -> m ++ haddockAnchorFromUrl targetURL
|
||||||
mapMaybe (getPkgModPair . snd) locs
|
_ -> targetURL
|
||||||
return HoogleResult
|
, hrSources = sources
|
||||||
{ hrURL = loc
|
, hrTitle = -- FIXME find out why these replaces are necessary
|
||||||
, hrSources = mapToList sources'
|
unpack $ T.replace "<0>" "" $ T.replace "</0>" "" $ pack
|
||||||
, hrTitle = Hoogle.showTagHTML self
|
targetItem
|
||||||
, hrBody = fromMaybe "Problem loading documentation" $
|
, hrBody = targetDocs
|
||||||
spoon $ Hoogle.showTagText docs
|
}
|
||||||
}
|
where sources = toList $ do
|
||||||
|
(pname, _) <- targetPackage
|
||||||
|
(mname, _) <- targetModule
|
||||||
|
let p = PackageLink pname (makePackageLink pname)
|
||||||
|
m = ModuleLink
|
||||||
|
mname
|
||||||
|
(T.unpack
|
||||||
|
(renderUrl
|
||||||
|
(haddockUrl
|
||||||
|
snapshot
|
||||||
|
(T.pack pname)
|
||||||
|
(T.pack mname))))
|
||||||
|
Just (p, [m])
|
||||||
|
haddockAnchorFromUrl =
|
||||||
|
('#':) . reverse . takeWhile (/='#') . reverse
|
||||||
|
|
||||||
getPkgModPair :: [(String, String)]
|
makePackageLink :: String -> String
|
||||||
-> Maybe (Map PackageLink [ModuleLink])
|
makePackageLink pkg = "/package/" ++ pkg
|
||||||
getPkgModPair [(pkg, pkgname), (modu, moduname)] = do
|
|
||||||
let pkg' = PackageLink pkgname pkg
|
|
||||||
modu' = ModuleLink moduname modu
|
|
||||||
return $ asMap $ singletonMap pkg' [modu']
|
|
||||||
getPkgModPair _ = Nothing
|
|
||||||
|
|||||||
@ -15,6 +15,7 @@ module Stackage.Database
|
|||||||
, PackageListingInfo (..)
|
, PackageListingInfo (..)
|
||||||
, getAllPackages
|
, getAllPackages
|
||||||
, getPackages
|
, getPackages
|
||||||
|
, getPackageVersionBySnapshot
|
||||||
, createStackageDatabase
|
, createStackageDatabase
|
||||||
, openStackageDatabase
|
, openStackageDatabase
|
||||||
, ModuleListingInfo (..)
|
, ModuleListingInfo (..)
|
||||||
@ -526,6 +527,22 @@ getPackages sid = liftM (map toPLI) $ run $ do
|
|||||||
, pliIsCore = isCore
|
, pliIsCore = isCore
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getPackageVersionBySnapshot
|
||||||
|
:: GetStackageDatabase m
|
||||||
|
=> SnapshotId -> Text -> m (Maybe Text)
|
||||||
|
getPackageVersionBySnapshot sid name = liftM (listToMaybe . map toPLI) $ run $ do
|
||||||
|
E.select $ E.from $ \(p,sp) -> do
|
||||||
|
E.where_ $
|
||||||
|
(p E.^. PackageId E.==. sp E.^. SnapshotPackagePackage) E.&&.
|
||||||
|
(sp E.^. SnapshotPackageSnapshot E.==. E.val sid) E.&&.
|
||||||
|
(E.lower_ (p E.^. PackageName) E.==. E.lower_ (E.val name))
|
||||||
|
E.orderBy [E.asc $ E.lower_ $ p E.^. PackageName]
|
||||||
|
return
|
||||||
|
( sp E.^. SnapshotPackageVersion
|
||||||
|
)
|
||||||
|
where
|
||||||
|
toPLI (E.Value version) = version
|
||||||
|
|
||||||
data ModuleListingInfo = ModuleListingInfo
|
data ModuleListingInfo = ModuleListingInfo
|
||||||
{ mliName :: !Text
|
{ mliName :: !Text
|
||||||
, mliPackageVersion :: !Text
|
, mliPackageVersion :: !Text
|
||||||
|
|||||||
@ -7,16 +7,13 @@ module Stackage.Database.Cron
|
|||||||
import ClassyPrelude.Conduit
|
import ClassyPrelude.Conduit
|
||||||
import Stackage.PackageIndex.Conduit
|
import Stackage.PackageIndex.Conduit
|
||||||
import Database.Persist (Entity (Entity))
|
import Database.Persist (Entity (Entity))
|
||||||
import Data.Char (isAlpha)
|
|
||||||
import qualified Codec.Archive.Tar as Tar
|
import qualified Codec.Archive.Tar as Tar
|
||||||
import Stackage.Database
|
import Stackage.Database
|
||||||
import Network.HTTP.Client
|
import Network.HTTP.Client
|
||||||
import Network.HTTP.Client.Conduit (bodyReaderSource)
|
import Network.HTTP.Client.Conduit (bodyReaderSource)
|
||||||
import Filesystem (rename, removeTree, removeFile)
|
import Filesystem (rename, removeTree, removeFile, isFile, createTree)
|
||||||
import Web.PathPieces (toPathPiece)
|
import Web.PathPieces (toPathPiece)
|
||||||
import Filesystem (isFile, createTree)
|
|
||||||
import Filesystem.Path.CurrentOS (parent, fromText, encodeString)
|
import Filesystem.Path.CurrentOS (parent, fromText, encodeString)
|
||||||
import Control.Monad.State.Strict (StateT, get, put)
|
|
||||||
import Network.HTTP.Types (status200)
|
import Network.HTTP.Types (status200)
|
||||||
import Data.Streaming.Network (bindPortTCP)
|
import Data.Streaming.Network (bindPortTCP)
|
||||||
import Network.AWS (Credentials (Discover),
|
import Network.AWS (Credentials (Discover),
|
||||||
@ -35,6 +32,7 @@ import Data.Conduit.Zlib (WindowBits (WindowBits),
|
|||||||
compress, ungzip)
|
compress, ungzip)
|
||||||
import qualified Hoogle
|
import qualified Hoogle
|
||||||
import System.Directory (doesFileExist)
|
import System.Directory (doesFileExist)
|
||||||
|
import System.IO.Temp (withSystemTempDirectory)
|
||||||
|
|
||||||
filename' :: Text
|
filename' :: Text
|
||||||
filename' = concat
|
filename' = concat
|
||||||
@ -208,6 +206,7 @@ stackageServerCron = do
|
|||||||
|
|
||||||
createHoogleDB :: StackageDatabase -> Manager -> SnapName -> IO (Maybe FilePath)
|
createHoogleDB :: StackageDatabase -> Manager -> SnapName -> IO (Maybe FilePath)
|
||||||
createHoogleDB db man name = handleAny (\e -> print e $> Nothing) $ do
|
createHoogleDB db man name = handleAny (\e -> print e $> Nothing) $ do
|
||||||
|
putStrLn $ "Creating Hoogle DB for " ++ toPathPiece name
|
||||||
req' <- parseUrl $ unpack tarUrl
|
req' <- parseUrl $ unpack tarUrl
|
||||||
let req = req' { decompress = const True }
|
let req = req' { decompress = const True }
|
||||||
|
|
||||||
@ -222,16 +221,26 @@ createHoogleDB db man name = handleAny (\e -> print e $> Nothing) $ do
|
|||||||
void $ tryIO $ removeFile (fromString outname)
|
void $ tryIO $ removeFile (fromString outname)
|
||||||
createTree (fromString bindir)
|
createTree (fromString bindir)
|
||||||
|
|
||||||
dbs <- runResourceT
|
withSystemTempDirectory ("hoogle-" ++ unpack (toPathPiece name)) $ \tmpdir -> do
|
||||||
$ sourceTarFile False tarFP
|
runResourceT
|
||||||
$$ evalStateC 1 (mapMC (singleDB db name bindir))
|
$ sourceTarFile False tarFP
|
||||||
=$ sinkList
|
$$ mapM_C (liftIO . singleDB db name tmpdir)
|
||||||
|
|
||||||
putStrLn "Merging databases..."
|
let args =
|
||||||
Hoogle.mergeDatabase (catMaybes dbs) outname
|
[ "generate"
|
||||||
putStrLn "Merge done"
|
, "--database=" ++ outname
|
||||||
|
, "--local=" ++ tmpdir
|
||||||
|
]
|
||||||
|
putStrLn $ concat
|
||||||
|
[ "Merging databases... ("
|
||||||
|
, tshow args
|
||||||
|
, ")"
|
||||||
|
]
|
||||||
|
Hoogle.hoogle args
|
||||||
|
|
||||||
return $ Just outname
|
putStrLn "Merge done"
|
||||||
|
|
||||||
|
return $ Just outname
|
||||||
where
|
where
|
||||||
root = "hoogle-gen"
|
root = "hoogle-gen"
|
||||||
bindir = root </> "bindir"
|
bindir = root </> "bindir"
|
||||||
@ -243,81 +252,29 @@ createHoogleDB db man name = handleAny (\e -> print e $> Nothing) $ do
|
|||||||
|
|
||||||
singleDB :: StackageDatabase
|
singleDB :: StackageDatabase
|
||||||
-> SnapName
|
-> SnapName
|
||||||
-> FilePath -- ^ bindir to write to
|
-> FilePath -- ^ temp directory to write .txt files to
|
||||||
-> Tar.Entry
|
-> Tar.Entry
|
||||||
-> StateT Int (ResourceT IO) (Maybe FilePath)
|
-> IO ()
|
||||||
singleDB db sname bindir e@(Tar.entryContent -> Tar.NormalFile lbs _) = do
|
singleDB db sname tmpdir e@(Tar.entryContent -> Tar.NormalFile lbs _) = do
|
||||||
idx <- get
|
--putStrLn $ "Loading file for Hoogle: " ++ pack (Tar.entryPath e)
|
||||||
put $! idx + 1
|
|
||||||
putStrLn $ "Loading file for Hoogle: " ++ pack (Tar.entryPath e)
|
|
||||||
|
|
||||||
let pkg = pack $ takeWhile (/= '.') $ Tar.entryPath e
|
let pkg = pack $ takeWhile (/= '.') $ Tar.entryPath e
|
||||||
msp <- flip runReaderT db $ do
|
msp <- flip runReaderT db $ do
|
||||||
Just (Entity sid _) <- lookupSnapshot sname
|
Just (Entity sid _) <- lookupSnapshot sname
|
||||||
lookupSnapshotPackage sid pkg
|
lookupSnapshotPackage sid pkg
|
||||||
case msp of
|
case msp of
|
||||||
Nothing -> do
|
Nothing -> putStrLn $ "Unknown: " ++ pkg
|
||||||
putStrLn $ "Unknown: " ++ pkg
|
Just _ -> do
|
||||||
return Nothing
|
let out = tmpdir </> unpack pkg <.> "txt"
|
||||||
Just (Entity _ sp) -> do
|
-- FIXME add @url directive
|
||||||
let ver = snapshotPackageVersion sp
|
writeFile out lbs
|
||||||
pkgver = concat [pkg, "-", ver]
|
{-
|
||||||
out = bindir </> show idx <.> "hoo"
|
|
||||||
src' = unlines
|
|
||||||
$ haddockHacks (Just $ unpack docsUrl)
|
|
||||||
$ lines
|
|
||||||
$ unpack
|
|
||||||
$ decodeUtf8 lbs
|
|
||||||
docsUrl = concat
|
docsUrl = concat
|
||||||
[ "https://www.stackage.org/haddock/"
|
[ "https://www.stackage.org/haddock/"
|
||||||
, toPathPiece sname
|
, toPathPiece sname
|
||||||
, "/"
|
, "/"
|
||||||
, pkgver
|
, pkgver
|
||||||
, "/index.html"
|
, "/index.html"
|
||||||
]
|
] -}
|
||||||
|
|
||||||
_errs <- liftIO $ Hoogle.createDatabase "" Hoogle.Haskell [] src' out
|
singleDB _ _ _ _ = return ()
|
||||||
|
|
||||||
return $ Just out
|
|
||||||
singleDB _ _ _ _ = return Nothing
|
|
||||||
|
|
||||||
---------------------------------------------------------------------
|
|
||||||
-- HADDOCK HACKS
|
|
||||||
-- (Copied from hoogle-4.2.36/src/Recipe/Haddock.hs)
|
|
||||||
-- Modifications:
|
|
||||||
-- 1) Some name qualification
|
|
||||||
-- 2) Explicit type sig due to polymorphic elem
|
|
||||||
-- 3) Fixed an unused binding warning
|
|
||||||
|
|
||||||
-- Eliminate @version
|
|
||||||
-- Change :*: to (:*:), Haddock bug
|
|
||||||
-- Change !!Int to !Int, Haddock bug
|
|
||||||
-- Change instance [overlap ok] to instance, Haddock bug
|
|
||||||
-- Change instance [incoherent] to instance, Haddock bug
|
|
||||||
-- Change instance [safe] to instance, Haddock bug
|
|
||||||
-- Change !Int to Int, HSE bug
|
|
||||||
-- Drop {-# UNPACK #-}, Haddock bug
|
|
||||||
-- Drop everything after where, Haddock bug
|
|
||||||
|
|
||||||
haddockHacks :: Maybe Hoogle.URL -> [String] -> [String]
|
|
||||||
haddockHacks loc src = maybe id haddockPackageUrl loc (translate src)
|
|
||||||
where
|
|
||||||
translate :: [String] -> [String]
|
|
||||||
translate = map (unwords . g . map f . words) . filter (not . isPrefixOf "@version ")
|
|
||||||
|
|
||||||
f "::" = "::"
|
|
||||||
f (':':xs) = "(:" ++ xs ++ ")"
|
|
||||||
f ('!':'!':x:xs) | isAlpha x = xs
|
|
||||||
f ('!':x:xs) | isAlpha x || x `elem` ("[(" :: String) = x:xs
|
|
||||||
f x | x `elem` ["[overlap","ok]","[incoherent]","[safe]"] = ""
|
|
||||||
f x | x `elem` ["{-#","UNPACK","#-}"] = ""
|
|
||||||
f x = x
|
|
||||||
|
|
||||||
g ("where":_) = []
|
|
||||||
g (x:xs) = x : g xs
|
|
||||||
g [] = []
|
|
||||||
|
|
||||||
haddockPackageUrl :: Hoogle.URL -> [String] -> [String]
|
|
||||||
haddockPackageUrl x = concatMap f
|
|
||||||
where f y | "@package " `isPrefixOf` y = ["@url " ++ x, y]
|
|
||||||
| otherwise = [y]
|
|
||||||
|
|||||||
@ -1,4 +1,10 @@
|
|||||||
resolver: lts-5.15
|
resolver: lts-5.15
|
||||||
|
packages:
|
||||||
|
- .
|
||||||
|
- location:
|
||||||
|
git: https://github.com/snoyberg/hoogle.git
|
||||||
|
commit: 765bd653d687e8569cd989be1637de86dcb20d56
|
||||||
|
extra-dep: true
|
||||||
image:
|
image:
|
||||||
container:
|
container:
|
||||||
name: fpco/stackage-server
|
name: fpco/stackage-server
|
||||||
|
|||||||
@ -161,7 +161,7 @@ library
|
|||||||
, haddock-library >= 1.2.0 && < 1.3
|
, haddock-library >= 1.2.0 && < 1.3
|
||||||
, async >= 2.1 && < 2.2
|
, async >= 2.1 && < 2.2
|
||||||
, yesod-gitrepo >= 0.2 && < 0.3
|
, yesod-gitrepo >= 0.2 && < 0.3
|
||||||
, hoogle >= 4.2 && < 4.3
|
, hoogle
|
||||||
, spoon >= 0.3 && < 0.4
|
, spoon >= 0.3 && < 0.4
|
||||||
, deepseq >= 1.4 && < 1.5
|
, deepseq >= 1.4 && < 1.5
|
||||||
, deepseq-generics >= 0.1 && < 0.2
|
, deepseq-generics >= 0.1 && < 0.2
|
||||||
|
|||||||
@ -3,37 +3,32 @@
|
|||||||
<h1>Hoogle Search
|
<h1>Hoogle Search
|
||||||
<p>Within <a href=@{snapshotLink}>#{snapshotTitle snapshot}</a>
|
<p>Within <a href=@{snapshotLink}>#{snapshotTitle snapshot}</a>
|
||||||
^{hoogleForm}
|
^{hoogleForm}
|
||||||
$case mresults
|
$if null results
|
||||||
$of HoogleQueryBad err
|
<p>Your search produced no results.
|
||||||
<p>#{err}
|
$else
|
||||||
<p>For information on what queries should look like, see the <a href="http://www.haskell.org/haskellwiki/Hoogle">hoogle user manual</a>.
|
<ol .search-results>
|
||||||
$of HoogleQueryOutput _query results mtotalCount
|
$forall HoogleResult url sources self docs <- results
|
||||||
$if null results
|
<li>
|
||||||
<p>Your search produced no results.
|
<p .self>
|
||||||
$else
|
<a href=#{url}>#{preEscapedToHtml self}
|
||||||
<ol .search-results>
|
<table .sources>
|
||||||
$forall HoogleResult url sources self docs <- results
|
$forall (pkg, modus) <- sources
|
||||||
<li>
|
<tr>
|
||||||
<p .self>
|
<th>
|
||||||
<a href=#{url}>#{preEscapedToHtml self}
|
<a href=#{plURL pkg}>#{plName pkg}
|
||||||
<table .sources>
|
<td>
|
||||||
$forall (pkg, modus) <- sources
|
$forall ModuleLink name url' <- modus
|
||||||
<tr>
|
<a href=#{url'}>#{name}
|
||||||
<th>
|
$if null docs
|
||||||
<a href=#{plURL pkg}>#{plName pkg}
|
<p .nodocs>No documentation available.
|
||||||
<td>
|
$else
|
||||||
$forall ModuleLink name url' <- modus
|
<p .docs>#{preEscapedToHtml docs}
|
||||||
<a href=#{url'}>#{name}
|
<p .pagination>
|
||||||
$if null docs
|
$with mpageCount <- fmap getPageCount mtotalCount
|
||||||
<p .nodocs>No documentation available.
|
Page #{page} of #{maybe "many" show mpageCount} #
|
||||||
$else
|
$if page > 1
|
||||||
<p .docs>#{docs}
|
|
|
||||||
<p .pagination>
|
<a href=@?{pageLink $ page - 1}>Previous
|
||||||
$with mpageCount <- fmap getPageCount mtotalCount
|
$if maybe True ((<) page) mpageCount
|
||||||
Page #{page} of #{maybe "many" show mpageCount} #
|
|
|
||||||
$if page > 1
|
<a href=@?{pageLink $ page + 1}>Next
|
||||||
|
|
|
||||||
<a href=@?{pageLink $ page - 1}>Previous
|
|
||||||
$if maybe True ((<) page) mpageCount
|
|
||||||
|
|
|
||||||
<a href=@?{pageLink $ page + 1}>Next
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user