diff --git a/CHANGES.txt b/CHANGES.txt
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,5 +1,14 @@
 Changelog for Hoogle (* = API change, @ = database format change)
 
+5.0.19, released 2026-01-19
+    Migrate (half of) parsing from haskell-src-exts to ghc-lib-parser
+    Support hyperlinks in search output
+    Colorize search output
+    Use --count when --info is passed
+    Change DB format to fix sizeOf in instance Storable Fingerprint
+    Do not force 'upgrade-insecure-requests' if we ourselves are running an HTTP server
+    #411, switched to xdg directory and deprecated legacy location
+    Require and support crypton-connection >= 0.4.1
 5.0.18.4, released 2024-01-14
     Don't test on GHC 9.2 or earlier
     Fix up the output of /stats to account for API changes
diff --git a/LICENSE b/LICENSE
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright Neil Mitchell 2004-2024.
+Copyright Neil Mitchell 2004-2026.
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -89,7 +89,7 @@
 
 # Background
 
-Hoogle work is licensed under the [BSD-3-Clause license](https://github.com/ndmitchell/hoogle/blob/master/docs/LICENSE).
+Hoogle work is licensed under the [BSD-3-Clause license](https://github.com/ndmitchell/hoogle/blob/master/LICENSE).
 
 ## Theoretical Foundations
 
diff --git a/hoogle.cabal b/hoogle.cabal
--- a/hoogle.cabal
+++ b/hoogle.cabal
@@ -1,13 +1,13 @@
 cabal-version:      1.18
 build-type:         Simple
 name:               hoogle
-version:            5.0.18.4
+version:            5.0.19.0
 license:            BSD3
 license-file:       LICENSE
 category:           Development
 author:             Neil Mitchell <ndmitchell@gmail.com>
 maintainer:         Neil Mitchell <ndmitchell@gmail.com>
-copyright:          Neil Mitchell 2004-2024
+copyright:          Neil Mitchell 2004-2026
 synopsis:           Haskell API Search
 description:
     Hoogle is a Haskell API search engine, which allows you to
@@ -23,6 +23,9 @@
     cbits/*.h
     cbits/*.c
 data-files:
+    misc/sample-data/henry.txt
+    misc/sample-data/emily-1.11/emily.txt
+    misc/sample-data/emily-1.2/emily.txt
     misc/settings.txt
     html/*.js
     html/*.png
@@ -43,22 +46,26 @@
 
     build-depends:
         QuickCheck,
-        aeson,
-        base > 4 && < 5,
+        aeson >= 2,
+        ansi-terminal,
+        base >= 4 && < 5,
         blaze-html,
         blaze-markup,
         binary,
         bytestring >= 0.10.2.0,
+        Cabal-syntax >= 3.8,
         cmdargs,
         conduit >= 1.3.0,
         conduit-extra >= 1.2.3.2,
         containers >= 0.5,
-        crypton-connection,
+        crypton-connection >= 0.4.1,
+        data-default-class,
         deepseq,
         directory,
         extra >= 1.6.6,
         filepath,
-        foundation >= 0.0.13,
+        ghc-lib-parser >= 9.6.5 && < 9.16,
+        hackage-revdeps >= 0.3 && < 0.4,
         old-locale,
         hashable,
         haskell-src-exts >= 1.22 && < 1.24,
@@ -73,16 +80,17 @@
         storable-tuple,
         tar,
         template-haskell,
-        text,
+        temporary,
+        text >= 2,
         time >= 1.5,
         transformers,
         uniplate,
-        utf8-string,
+        utf8-string >= 0.3.1,
         vector,
         wai,
         wai-logger,
         warp,
-        warp-tls,
+        warp-tls >= 3.4.2,
         zlib
 
     c-sources:        cbits/text_search.c
@@ -109,6 +117,7 @@
         Input.Download
         Input.Haddock
         Input.Item
+        Input.ParseDecl
         Input.Reorder
         Input.Set
         Input.Settings
@@ -131,8 +140,8 @@
 executable hoogle
     main-is:            src/Main.hs
     default-language:   Haskell2010
-    ghc-options:        -threaded
+    ghc-options:        -threaded -rtsopts
 
     build-depends:
-        base > 4 && < 5,
+        base >= 4 && < 5,
         hoogle
diff --git a/misc/sample-data/emily-1.11/emily.txt b/misc/sample-data/emily-1.11/emily.txt
new file mode 100644
--- /dev/null
+++ b/misc/sample-data/emily-1.11/emily.txt
@@ -0,0 +1,9 @@
+@url http://eghmitchell.com/
+@package emily
+@version 1.11
+
+module Mitchell
+
+@url #a_wife
+type family Wife (a :: Mother)
+paleo :: Bool
diff --git a/misc/sample-data/emily-1.2/emily.txt b/misc/sample-data/emily-1.2/emily.txt
new file mode 100644
--- /dev/null
+++ b/misc/sample-data/emily-1.2/emily.txt
@@ -0,0 +1,7 @@
+@url http://eghmitchell.co.uk/
+@package emily
+@version 1.2
+
+module Mitchell
+@url #a_wife
+type family Wife (a :: Mother)
diff --git a/misc/sample-data/henry.txt b/misc/sample-data/henry.txt
new file mode 100644
--- /dev/null
+++ b/misc/sample-data/henry.txt
@@ -0,0 +1,6 @@
+@package henry
+
+module Mitchell
+
+@url http://henry.com?too_long
+__prefix__really_long_string_take_might_overflow_any_poorly_chosen_word8_buffers_because_someone_decided_their_identifiers_should_be_untypeable_by_mere_humans__infix__and_that_their_sample_code_should_not_fit_into_tweets_or_even_single_lines_because_they_are_definitely_too_long__suffix__ :: a -> b
diff --git a/src/Action/CmdLine.hs b/src/Action/CmdLine.hs
--- a/src/Action/CmdLine.hs
+++ b/src/Action/CmdLine.hs
@@ -16,6 +16,7 @@
 import System.Directory
 import System.Environment
 import System.FilePath
+import System.IO
 
 data Language = Haskell | Frege deriving (Data,Typeable,Show,Eq,Enum,Bounded)
 
@@ -80,7 +81,25 @@
 
 defaultDatabaseLang :: Language -> IO FilePath
 defaultDatabaseLang lang = do
-    dir <- getAppUserDataDirectory "hoogle"
+    xdgLocation <- getXdgDirectory XdgData "hoogle"
+    legacyLocation <- getAppUserDataDirectory "hoogle"
+    doesXdgPathExist <- doesPathExist xdgLocation
+    doesLegacyPathExist <- doesPathExist legacyLocation
+
+    dir <- case (doesXdgPathExist, doesLegacyPathExist) of
+      -- On Windows XDG location and legacy location are identical
+      _ | xdgLocation == legacyLocation -> pure xdgLocation
+      (_, False) -> pure xdgLocation
+      (True, True) -> do
+        hPutStrLn stderr $
+          "Warning: Legacy location ignored (" ++ legacyLocation ++ "),"
+          ++ "since xdg location is available (" ++ xdgLocation ++")."
+        pure xdgLocation
+      (False, True) -> do
+        -- TODO: renable after release
+        --hPutStrLn stderr $ "Warning: " ++ legacyLocation ++ " is deprecated."
+        --  ++ "Consider moving it to $XDG_DATA_HOME/hoogle (" ++ xdgLocation ++ ")"
+        pure legacyLocation
     pure $ dir </> "default-" ++ lower (show lang) ++ "-" ++ showVersion (trimVersion 3 version) ++ ".hoo"
 
 getCmdLine :: [String] -> IO CmdLine
@@ -115,7 +134,7 @@
     ,jsonl = def &= name "jsonl" &= help "Get result as JSONL (JSON Lines)"
     ,link = def &= help "Give URL's for each result"
     ,numbers = def &= help "Give counter for each result"
-    ,info = def &= help "Give extended information about the first result"
+    ,info = def &= help "Give extended information about the first n results (set n with --count, default is 1)"
     ,database = def &= typFile &= help "Name of database to use (use .hoo extension)"
     ,count = Nothing &= name "n" &= help "Maximum number of results to return (defaults to 10)"
     ,query = def &= args &= typ "QUERY"
diff --git a/src/Action/Generate.hs b/src/Action/Generate.hs
--- a/src/Action/Generate.hs
+++ b/src/Action/Generate.hs
@@ -36,6 +36,7 @@
 import Action.CmdLine
 import General.Conduit
 import Control.DeepSeq
+import Distribution.Package (mkPackageName, unPackageName)
 
 {-
 
@@ -101,12 +102,12 @@
     hoogles  <- download "haskell-hoogle.tar.gz" "https://hackage.haskell.org/packages/hoogle.tar.gz"
 
     -- peakMegabytesAllocated = 2
-    setStackage <- Set.map strPack <$> (Set.union <$> setStackage stackageLts <*> setStackage stackageNightly)
-    setPlatform <- Set.map strPack <$> setPlatform platform
-    setGHC <- Set.map strPack <$> setGHC platform
+    setStackage <- Set.map mkPackageName <$> (Set.union <$> setStackage stackageLts <*> setStackage stackageNightly)
+    setPlatform <- Set.map mkPackageName <$> setPlatform platform
+    setGHC <- Set.map mkPackageName <$> setGHC platform
 
     cbl <- timed timing "Reading Cabal" $ parseCabalTarball settings cabals
-    let want = Set.insert (strPack "ghc") $ Set.unions [setStackage, setPlatform, setGHC]
+    let want = Set.insert (mkPackageName "ghc") $ Set.unions [setStackage, setPlatform, setGHC]
     cbl <- pure $ flip Map.mapWithKey cbl $ \name p ->
         p{packageTags =
             [(strPack "set",strPack "included-with-ghc") | name `Set.member` setGHC] ++
@@ -116,7 +117,7 @@
 
     let source = do
             tar <- liftIO $ tarballReadFiles hoogles
-            forM_ tar $ \(strPack . takeBaseName -> name, src) ->
+            forM_ tar $ \(mkPackageName . takeBaseName -> name, src) ->
                 yield (name, hackagePackageURL name, src)
     pure (cbl, want, source)
 
@@ -129,22 +130,23 @@
     -- We never distinguish on versions, so they are considered equal when reordering
     -- So put 2.0 first in the list and rely on stable sorting. A bit of a hack.
     let order a = second Down $ parseTrailingVersion a
-    let packages = map (strPack . takeBaseName &&& id) $ sortOn (map order . splitDirectories) $ filter ((==) ".txt" . takeExtension) files
+    let packages = map (mkPackageName . takeBaseName &&& id) $ sortOn (map order . splitDirectories) $ filter ((==) ".txt" . takeExtension) files
     cabals <- mapM parseCabal $ filter ((==) ".cabal" . takeExtension) files
     let source = forM_ packages $ \(name, file) -> do
             src <- liftIO $ bstrReadFile file
             dir <- liftIO $ canonicalizePath $ takeDirectory file
             let url = "file://" ++ ['/' | not $ "/" `isPrefixOf` dir] ++ replace "\\" "/" dir ++ "/"
-            yield (name, url, lbstrFromChunks [src])
+            when (isJust $ bstrSplitInfix (bstrPack "@package " <> bstrPack (unPackageName name)) src) $
+                yield (name, url, lbstrFromChunks [src])
     pure (Map.union
                 (Map.fromList cabals)
                 (Map.fromListWith (<>) $ map generateBarePackage packages)
            ,Set.fromList $ map fst packages, source)
   where
     parseCabal fp = do
-        src <- readFileUTF8' fp
+        src <- bstrReadFile fp
         let pkg = readCabal settings src
-        pure (strPack $ takeBaseName fp, pkg)
+        pure (mkPackageName $ takeBaseName fp, pkg)
 
     generateBarePackage (name, file) =
         (name, mempty{packageTags = (strPack "set", strPack "all") : sets})
@@ -154,11 +156,11 @@
 
 readFregeOnline :: Timing -> Download -> IO (Map.Map PkgName Package, Set.Set PkgName, ConduitT () (PkgName, URL, LBStr) IO ())
 readFregeOnline timing download = do
-    frege <- download "frege-frege.txt" "http://try.frege-lang.org/hoogle-frege.txt"
+    frege <- download "frege-frege.txt" "https://github.com/Frege/try-frege/raw/refs/heads/master/try-frege-web/src/main/webapp/hoogle-frege.txt"
     let source = do
             src <- liftIO $ bstrReadFile frege
-            yield (strPack "frege", "http://google.com/", lbstrFromChunks [src])
-    pure (Map.empty, Set.singleton $ strPack "frege", source)
+            yield (mkPackageName "frege", "http://google.com/", lbstrFromChunks [src])
+    pure (Map.empty, Set.singleton $ mkPackageName "frege", source)
 
 
 readHaskellGhcpkg :: Timing -> Settings -> IO (Map.Map PkgName Package, Set.Set PkgName, ConduitT () (PkgName, URL, LBStr) IO ())
@@ -166,7 +168,7 @@
     cbl <- timed timing "Reading ghc-pkg" $ readGhcPkg settings
     let source =
             forM_ (Map.toList cbl) $ \(name,Package{..}) -> whenJust packageDocs $ \docs -> do
-                let file = docs </> strUnpack name <.> "txt"
+                let file = docs </> unPackageName name <.> "txt"
                 whenM (liftIO $ doesFileExist file) $ do
                     src <- liftIO $ bstrReadFile file
                     docs <- liftIO $ canonicalizePath docs
@@ -203,8 +205,8 @@
     cbl <- timed timing "Reading ghc-pkg" $ readGhcPkg settings
     let source =
             forM_ (Map.toList cbl) $ \(name, p@Package{..}) -> do
-                let docs = docDir (strUnpack name) p
-                    file = docBaseDir </> docs </> (strUnpack name) <.> "txt"
+                let docs = docDir (unPackageName name) p
+                    file = docBaseDir </> docs </> unPackageName name <.> "txt"
                 whenM (liftIO $ doesFileExist file) $ do
                     src <- liftIO $ bstrReadFile file
                     let url = ['/' | not $ all isPathSeparator $ take 1 docs] ++
@@ -249,10 +251,7 @@
     cbl <- evaluate $ Map.map (\p -> p{packageDepends=[]}) cbl -- clear the memory, since the information is no longer used
     evaluate popularity
 
-    -- mtl is more popular than transformers, despite having dodgy docs, which is a shame, so we hack it
-    popularity <- evaluate $ Map.adjust (max $ 1 + Map.findWithDefault 0 (strPack "mtl") popularity) (strPack "transformers") popularity
-
-    want <- pure $ if include /= [] then Set.fromList $ map strPack include else want
+    want <- pure $ if include /= [] then Set.fromList $ map mkPackageName include else want
     want <- pure $ case count of Nothing -> want; Just count -> Set.fromList $ take count $ Set.toList want
 
     (stats, _) <- storeWriteFile database $ \store -> do
@@ -265,7 +264,7 @@
             let warning msg = do modifyIORef itemWarn succ; hPutStrLn warnings msg
 
             let consume :: ConduitM (Int, (PkgName, URL, LBStr)) (Maybe Target, [Item]) IO ()
-                consume = awaitForever $ \(i, (strUnpack -> pkg, url, body)) -> do
+                consume = awaitForever $ \(i, (unPackageName -> pkg, url, body)) -> do
                     timedOverwrite timing ("[" ++ show i ++ "/" ++ show (Set.size want) ++ "] " ++ pkg) $
                         parseHoogle (\msg -> warning $ pkg ++ ":" ++ msg) url body
 
@@ -275,12 +274,12 @@
                     filterC (flip Set.member want . fst3) .|
                     void ((|$|)
                         (zipFromC 1 .| consume)
-                        (do seen <- fmap Set.fromList $ mapMC (evaluate . force . strCopy . fst3) .| sinkList
+                        (do seen <- fmap Set.fromList $ mapMC (evaluate . force . fst3) .| sinkList
                             let missing = [x | x <- Set.toList $ want `Set.difference` seen
                                              , fmap packageLibrary (Map.lookup x cbl) /= Just False]
                             liftIO $ putStrLn ""
                             liftIO $ whenNormal $ when (missing /= []) $ do
-                                putStrLn $ "Packages missing documentation: " ++ unwords (sortOn lower $ map strUnpack missing)
+                                putStrLn $ "Packages missing documentation: " ++ unwords (sortOn lower $ map unPackageName missing)
                             liftIO $ when (Set.null seen) $
                                 exitFail "No packages were found, aborting (use no arguments to index all of Stackage)"
                             -- synthesise things for Cabal packages that are not documented
diff --git a/src/Action/Search.hs b/src/Action/Search.hs
--- a/src/Action/Search.hs
+++ b/src/Action/Search.hs
@@ -21,6 +21,11 @@
 import System.Directory
 import Text.Blaze.Renderer.Utf8
 import Safe
+import System.Console.ANSI
+    (hSupportsANSI, hyperlinkCode, setSGRCode
+    ,SGR (SetColor), ConsoleLayer (Foreground)
+    ,ColorIntensity (Vivid, Dull), Color (Yellow))
+import System.IO (stdout)
 
 import Action.CmdLine
 import General.Store
@@ -44,16 +49,21 @@
             count' <- pure $ fromMaybe 10 count
             (q, res) <- pure $ search store $ parseQuery $ unwords query
             whenLoud $ putStrLn $ "Query: " ++ unescapeHTML (LBS.unpack $ renderMarkup $ renderQuery q)
-            let (shown, hidden) = splitAt count' $ nubOrd $ map (targetResultDisplay link) res
+            color' <- case color of
+              Just b -> pure b
+              Nothing -> hSupportsANSI stdout
+            let (shown, hidden) = splitAt count' $ nubOrd $ map (targetResultDisplay link color' q) res
             if null res then
                 putStrLn "No results found"
              else if info then do
-                 putStr $ targetInfo $ headErr res
+                 mapM_ (putStr . targetInfo color' q)
+                  $ (case count of
+                    Just c -> take c
+                    Nothing -> singleton . headErr) res
              else do
-                let toShow = if numbers && not info then addCounter shown else shown
                 if | json -> LBS.putStrLn $ JSON.encode $ maybe id take count $ map unHTMLtargetItem res
                    | jsonl -> mapM_ (LBS.putStrLn . JSON.encode) $ maybe id take count $ map unHTMLtargetItem res
-                   | otherwise -> putStr $ unlines toShow
+                   | otherwise -> putStr $ unlines $ if numbers then addCounter shown else shown
                 when (hidden /= [] && not json) $ do
                     whenNormal $ putStrLn $ "-- plus more results not shown, pass --count=" ++ show (count'+10) ++ " to see more"
         else do
@@ -63,21 +73,30 @@
             putStr $ unlines $ searchFingerprintsDebug store (parseType $ unwords query) (map parseType compare_)
 
 -- | Returns the details printed out when hoogle --info is called
-targetInfo :: Target -> String
-targetInfo Target{..} =
-    unlines $ [ unHTML targetItem ] ++
+targetInfo :: Bool -> [Query] -> Target -> String
+targetInfo color qs Target{..} =
+    unlines $ [ unHTML . (if color then ansiHighlight qs else id) $ targetItem ] ++
               [ unwords packageModule | not $ null packageModule] ++
               [ unHTML targetDocs ]
             where packageModule = map fst $ catMaybes [targetPackage, targetModule]
 
 -- | Returns the Target formatted as an item to display in the results
--- | Bool argument decides whether links are shown
-targetResultDisplay :: Bool -> Target -> String
-targetResultDisplay link Target{..} = unHTML $ unwords $
+-- | Bool arguments decide whether links and colors are shown
+targetResultDisplay :: Bool -> Bool -> [Query] -> Target -> String
+targetResultDisplay link color qs Target{..} = unHTML $ unwords $
         map fst (maybeToList targetModule) ++
-        [targetItem] ++
+        [if color then highlightFull targetItem else targetItem] ++
         ["-- " ++ targetURL | link]
+     where
+        highlightFull = hyperlinkCode targetURL . ansiHighlight qs
 
+ansiHighlight :: [Query] -> String -> String
+ansiHighlight = highlightItem id id ((dull ++) . (++ rst)) ((bold ++) . (++ rst))
+    where
+        dull = setSGRCode [SetColor Foreground Dull Yellow]
+        bold = setSGRCode [SetColor Foreground Vivid Yellow]
+        rst = setSGRCode []
+
 unHTMLtargetItem :: Target -> Target
 unHTMLtargetItem target = target {targetItem = unHTML $ targetItem target}
 
@@ -149,7 +168,7 @@
         "Ord" === hackage "base/docs/Prelude.html#t:Ord"
         ">>=" === hackage "base/docs/Prelude.html#v:-62--62--61-"
         "sequen" === hackage "base/docs/Prelude.html#v:sequence"
-        "foldl'" === hackage "base/docs/Data-List.html#v:foldl-39-"
+        "foldl'" === hackage "base/docs/Prelude.html#v:foldl-39-" -- since base-4.20
         "Action package:shake" === "https://hackage.haskell.org/package/shake/docs/Development-Shake.html#t:Action"
         "Action package:shake set:stackage" === "https://hackage.haskell.org/package/shake/docs/Development-Shake.html#t:Action"
         "map -package:base" ==$ \x -> not $ "/base/" `isInfixOf` x
diff --git a/src/Action/Server.hs b/src/Action/Server.hs
--- a/src/Action/Server.hs
+++ b/src/Action/Server.hs
@@ -200,9 +200,10 @@
                         H.div ! H.class_ "links" $ H.a ! H.href (H.stringValue link) $ "Uses"
             H.div ! H.class_ "from" $ showFroms local haddock is
             H.div ! H.class_ "doc newline shut" $ H.preEscapedString targetDocs
-    H.ul ! H.id "left" $ do
-        H.li $ H.b "Packages"
-        mconcat [H.li $ f cat val | (cat,val) <- itemCategories $ concat results, QueryScope True cat val `notElem` query]
+    H.ul ! H.id "left" $ -- if there's already a scope query we don't show subquery links because bots will get lost in a maze of links.
+        if (any isQueryScope query) then pure () else do
+            H.li $ H.b "Packages"
+            mconcat [H.li $ f cat val | (cat,val) <- itemCategories $ concat results, QueryScope True cat val `notElem` query]
 
     where
         useLink :: [Target] -> Maybe String
@@ -254,23 +255,9 @@
 -------------------------------------------------------------
 -- DISPLAY AN ITEM (bold keywords etc)
 
-highlightItem :: [Query] -> String -> Markup
-highlightItem qs x
-    | Just (pre,x) <- stripInfix "<s0>" x, Just (name,post) <- stripInfix "</s0>" x
-        = H.preEscapedString pre <> highlight (unescapeHTML name) <> H.preEscapedString post
-    | otherwise = H.string x
-    where
-        highlight = mconcatMap (\xs@((b,_):_) -> let s = H.string $ map snd xs in if b then H.b s else s) .
-                    groupOn fst . (\x -> zip (f x) x)
-            where
-              f (x:xs) | m > 0 = replicate m True ++ drop (m - 1) (f xs)
-                  where m = maximum $ 0 : [length y | QueryName y <- qs, lower y `isPrefixOf` lower (x:xs)]
-              f (x:xs) = False : f xs
-              f [] = []
 
 displayItem :: [Query] -> String -> Markup
-displayItem = highlightItem
-
+displayItem = highlightItem H.string H.preEscapedString H.string (H.b . H.string)
 
 action_server_test_ :: IO ()
 action_server_test_ = do
diff --git a/src/General/Store.hs b/src/General/Store.hs
--- a/src/General/Store.hs
+++ b/src/General/Store.hs
@@ -33,8 +33,11 @@
 import Numeric.Extra
 import Paths_hoogle
 import Prelude
-import System.IO.Extra
+import System.Directory (renameFile)
+import System.FilePath (takeDirectory, takeFileName)
+import System.IO.Extra (Handle, hTell, hClose, hPutBuf)
 import System.IO.MMap
+import System.IO.Temp (withTempFile)
 import System.IO.Unsafe
 
 -- Ensure the string is always 25 chars long, so version numbers don't change its size
@@ -108,7 +111,7 @@
 storeWriteFile file act = do
     atoms <- newIORef Map.empty
     parts <- newIORef Nothing
-    withBinaryFile file WriteMode $ \h -> do
+    withTempFile (takeDirectory file) (takeFileName file) $ \tmpFile h -> do
         -- put the version string at the start and end, so we can tell truncation vs wrong version
         BS.hPut h verString
         ref <- newIORef $ SW h (BS.length verString) []
@@ -130,6 +133,8 @@
         let stats = prettyTable 0 "Bytes" $
                 ("Overheads", intToDouble $ fromIntegral final - sum (map atomSize $ Map.elems atoms)) :
                 [(name ++ " :: " ++ atomType, intToDouble atomSize) | (name, Atom{..}) <- Map.toList atoms]
+        hClose h
+        renameFile tmpFile file
         pure (stats, res)
 
 storeWrite :: (Typeable (t a), Typeable a, Stored a) => StoreWrite -> t a -> a -> IO ()
diff --git a/src/General/Str.hs b/src/General/Str.hs
--- a/src/General/Str.hs
+++ b/src/General/Str.hs
@@ -8,12 +8,11 @@
     BStr0, bstr0Join, bstr0Split
     ) where
 
-import qualified Foundation as Fdn
-import qualified Foundation.Collection as Fdn
 import qualified Data.ByteString.Char8 as BS
 import qualified Data.ByteString.UTF8 as US
 import qualified Data.ByteString.Lazy.Char8 as LBS
 import qualified Data.ByteString.Lazy.UTF8 as LUS
+import qualified Data.Text as T
 import Control.DeepSeq
 import Data.Char
 import Data.Data
@@ -23,8 +22,8 @@
 import Prelude
 
 
-newtype Str = Str {fromStr :: Fdn.String}
-    deriving (Data,Typeable,Eq,Ord,Semigroup,Monoid)
+newtype Str = Str { fromStr :: T.Text }
+    deriving (Data, Typeable, Eq, Ord, Semigroup, Monoid)
 
 instance Show Str where show = strUnpack
 instance NFData Str where rnf x = x `seq` ()
@@ -36,19 +35,19 @@
 
 
 strPack :: String -> Str
-strPack = Str . fromString
+strPack = Str . T.pack
 
 strUnpack :: Str -> String
-strUnpack = Fdn.toList . fromStr
+strUnpack = T.unpack . fromStr
 
 strCons :: Char -> Str -> Str
-strCons c = Str . Fdn.cons c . fromStr
+strCons c = Str . T.cons c . fromStr
 
 strCopy :: Str -> Str
-strCopy = Str . Fdn.copy . fromStr
+strCopy = Str . T.copy . fromStr
 
 strNull :: Str -> Bool
-strNull = Fdn.null . fromStr
+strNull = T.null . fromStr
 
 bstrPack :: String -> BStr
 bstrPack = US.fromString
diff --git a/src/General/Util.hs b/src/General/Util.hs
--- a/src/General/Util.hs
+++ b/src/General/Util.hs
@@ -56,9 +56,10 @@
 import Prelude
 import qualified Network.HTTP.Types.URI as URI
 import qualified Data.ByteString.UTF8 as UTF8
+import Distribution.Types.PackageName (PackageName, unPackageName)
 
 
-type PkgName = Str
+type PkgName = PackageName
 type ModName = Str
 
 -- | A URL, complete with a @https:@ prefix.
@@ -320,7 +321,7 @@
 
 
 hackagePackageURL :: PkgName -> URL
-hackagePackageURL x = "https://hackage.haskell.org/package/" ++ strUnpack x
+hackagePackageURL x = "https://hackage.haskell.org/package/" ++ unPackageName x
 
 hackageModuleURL :: ModName -> URL
 hackageModuleURL x = "/docs/" ++ ghcModuleURL x
diff --git a/src/General/Web.hs b/src/General/Web.hs
--- a/src/General/Web.hs
+++ b/src/General/Web.hs
@@ -125,7 +125,7 @@
               <> " form-action 'self';"
               -- Any request originating from this page and specifying http as
               -- its protocol will be automatically upgraded to https.
-              <> " upgrade-insecure-requests;"
+              <> (if https then " upgrade-insecure-requests;" else mempty)
               -- Do not display http content if the page was loaded under
               -- https.
               <> " block-all-mixed-content"),
diff --git a/src/Input/Cabal.hs b/src/Input/Cabal.hs
--- a/src/Input/Cabal.hs
+++ b/src/Input/Cabal.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE ViewPatterns, PatternGuards, TupleSections, RecordWildCards, ScopedTypeVariables #-}
+{-# OPTIONS_GHC -Wall -Wno-name-shadowing #-}
 
 -- | Module for reading Cabal files.
 module Input.Cabal(
@@ -12,7 +13,6 @@
 import Data.List.Extra
 import System.FilePath
 import Control.DeepSeq
-import Control.Exception
 import Control.Exception.Extra
 import Control.Monad
 import System.IO.Extra
@@ -21,16 +21,26 @@
 import qualified System.Process.ByteString as BS
 import qualified Data.ByteString.UTF8 as UTF8
 import System.Directory
-import Data.Char
 import Data.Maybe
 import Data.Tuple.Extra
 import qualified Data.Map.Strict as Map
 import General.Util
-import General.Conduit
 import Data.Semigroup
 import Control.Applicative
 import Prelude
 
+import Distribution.Compat.Lens (toListOf)
+import qualified Distribution.PackageDescription as PD
+import qualified Distribution.PackageDescription.Configuration as PD
+import qualified Distribution.PackageDescription.Parsec as PD
+import qualified Distribution.Pretty
+import qualified Distribution.Types.BuildInfo.Lens as Lens
+import Distribution.Types.PackageName (mkPackageName, unPackageName)
+import Distribution.Types.Version (versionNumbers)
+import Distribution.Utils.ShortText (fromShortText)
+import Hackage.RevDeps (lastVersionsOfPackages)
+import qualified Distribution.SPDX as SPDX
+
 ---------------------------------------------------------------------
 -- DATA TYPE
 
@@ -66,7 +76,7 @@
 packagePopularity cbl = mp `seq` (errs, mp)
     where
         mp = Map.map length good
-        errs =  [ strUnpack user ++ ".cabal: Import of non-existant package " ++ strUnpack name ++
+        errs =  [ unPackageName user ++ ".cabal: Import of non-existant package " ++ unPackageName name ++
                           (if null rest then "" else ", also imported by " ++ show (length rest) ++ " others")
                 | (name, user:rest) <- Map.toList bad]
         (good, bad)  = Map.partitionWithKey (\k _ -> k `Map.member` cbl) $
@@ -80,70 +90,91 @@
 readGhcPkg :: Settings -> IO (Map.Map PkgName Package)
 readGhcPkg settings = do
     topdir <- findExecutable "ghc-pkg"
+    (exit, stdout, stderr) <-
+    -- From GHC 9.0.1, the `haddock-html` field in `*.conf` files for GHC boot
+    -- libraries has used `${pkgroot}`, which can be expanded in the output.
+
+    -- On Windows, the `haddock-html` field in `*.conf` files for GHC boot
+    -- libraries for GHC >= 9.0 && < 9.10 contain errors. For example, this may
+    -- be specified:
+    --
+    --     haddock-html: ${pkgroot}/../../doc/html/libraries/base-4.18.0.0
+    --
+    -- when the correct specification would be:
+    --
+    --     haddock-html: ${pkgroot}/../doc/html/libraries/base-4.18.0.0
+    --
+    -- However haddock does not seek to correct that. It assumes that users will
+    -- correct manually the affected `*.conf` files.
+
     -- important to use BS process reading so it's in Binary format, see #194
-    (exit, stdout, stderr) <- BS.readProcessWithExitCode "ghc-pkg" ["dump"] mempty
+      BS.readProcessWithExitCode "ghc-pkg" ["dump", "--expand-pkgroot"] mempty
     when (exit /= ExitSuccess) $
         errorIO $ "Error when reading from ghc-pkg, " ++ show exit ++ "\n" ++ UTF8.toString stderr
     let g (stripPrefix "$topdir" -> Just x) | Just t <- topdir = takeDirectory t ++ x
+        -- ^ Backwards compatibility with GHC < 9.0
         g x = x
     let fixer p = p{packageLibrary = True, packageDocs = g <$> packageDocs p}
-    let f ((stripPrefix "name: " -> Just x):xs) = Just (strPack $ trimStart x, fixer $ readCabal settings $ unlines xs)
-        f xs = Nothing
+    let f ((stripPrefix "name: " -> Just x):xs) = Just (mkPackageName $ trimStart x, fixer $ readCabal settings $ bstrPack $ unlines xs)
+        f _ = Nothing
     pure $ Map.fromList $ mapMaybe f $ splitOn ["---"] $ lines $ filter (/= '\r') $ UTF8.toString stdout
 
 
 -- | Given a tarball of Cabal files, parse the latest version of each package.
 parseCabalTarball :: Settings -> FilePath -> IO (Map.Map PkgName Package)
--- items are stored as:
--- QuickCheck/2.7.5/QuickCheck.cabal
--- QuickCheck/2.7.6/QuickCheck.cabal
--- rely on the fact the highest version is last (using lastValues)
 parseCabalTarball settings tarfile = do
-    res <- runConduit $
-        (sourceList =<< liftIO (tarballReadFiles tarfile)) .|
-        mapC (first takeBaseName) .| groupOnLastC fst .| mapMC (evaluate . force) .|
-        pipelineC 10 (mapC (strPack *** readCabal settings . lbstrUnpack) .| mapMC (evaluate . force) .| sinkList)
-    pure $ Map.fromList res
+    lastVersions <- lastVersionsOfPackages (const True) tarfile Nothing
+    pure $ Map.map (readCabal settings) lastVersions
 
 
 ---------------------------------------------------------------------
 -- PARSERS
 
--- | Cabal information, plus who I depend on
-readCabal :: Settings -> String -> Package
-readCabal Settings{..} src = Package{..}
+readCabal :: Settings -> BStr -> Package
+readCabal settings src = case PD.parseGenericPackageDescriptionMaybe src of
+    Nothing -> Package
+        { packageTags = []
+        , packageLibrary = False
+        , packageSynopsis = mempty
+        , packageVersion = strPack "0.0"
+        , packageDepends = []
+        , packageDocs = Nothing
+        }
+    Just gpd -> readCabal' settings gpd
+
+readCabal' :: Settings -> PD.GenericPackageDescription -> Package
+readCabal' Settings{..} gpd = Package{..}
     where
-        mp = Map.fromListWith (++) $ lexCabal src
-        ask x = Map.findWithDefault [] x mp
+        pd = PD.flattenPackageDescription gpd
+        pkgId = PD.package pd
 
-        packageDepends =
-            map strPack $ nubOrd $ filter (/= "") $
-            map (intercalate "-" . takeWhile (all isAlpha . take 1) . splitOn "-" . fst . word1) $
-            concatMap (split (== ',')) (ask "build-depends") ++ concatMap words (ask "depends")
-        packageVersion = strPack $ headDef "0.0" $ dropWhile null (ask "version")
-        packageSynopsis = strPack $ unwords $ words $ unwords $ ask "synopsis"
-        packageLibrary = "library" `elem` map (lower . trim) (lines src)
-        packageDocs = find (not . null) $ ask "haddock-html"
+        packageDepends = nubOrd $ foldMap (map (\(PD.Dependency pkg _ _) -> pkg) . PD.targetBuildDepends) $ toListOf Lens.traverseBuildInfos gpd
+        packageVersion = strPack $ intercalate "." $ map show $ versionNumbers $ PD.pkgVersion pkgId
+        packageSynopsis = strPack $ fromShortText $ PD.synopsis pd
+        packageLibrary = PD.hasPublicLib pd
+        packageDocs = Nothing
 
+        unpackLicenseExpression (SPDX.EOr x y) = unpackLicenseExpression x ++ unpackLicenseExpression y
+        unpackLicenseExpression x = [x]
+
+        packageLicenses = case PD.license pd of
+            SPDX.NONE -> []
+            SPDX.License licExpr -> map (show . Distribution.Pretty.pretty) $
+                unpackLicenseExpression licExpr
+        packageCategories =
+            filter (not . null) $ split (`elem` " ,") $
+                fromShortText $ PD.category pd
+        packageAuthor = fromShortText $ PD.author pd
+        packageMaintainer = fromShortText $ PD.maintainer pd
+
         packageTags = map (both strPack) $ nubOrd $ concat
-            [ map (x,) $ concatMap cleanup $ concatMap ask xs
-            | xs@(x:_) <- [["license"],["category"],["author","maintainer"]]]
+            [ map ("license",) packageLicenses
+            , map ("category",) packageCategories
+            , map ("author",) (concatMap cleanup [packageAuthor, packageMaintainer])
+            ]
 
         -- split on things like "," "&" "and", then throw away email addresses, replace spaces with "-" and rename
         cleanup =
             filter (/= "") .
             map (renameTag . intercalate "-" . filter ('@' `notElem`) . words . takeWhile (`notElem` "<(")) .
             concatMap (map unwords . split (== "and") . words) . split (`elem` ",&")
-
-
--- Ignores nesting beacuse it's not interesting for any of the fields I care about
-lexCabal :: String -> [(String, [String])]
-lexCabal = f . lines
-    where
-        f (x:xs) | (white,x) <- span isSpace x
-                 , (name@(_:_),x) <- span (\c -> isAlpha c || c == '-') x
-                 , ':':x <- trim x
-                 , (xs1,xs2) <- span (\s -> length (takeWhile isSpace s) > length white) xs
-                 = (lower name, trim x : replace ["."] [""] (map (trim . fst . breakOn "--") xs1)) : f xs2
-        f (x:xs) = f xs
-        f [] = []
diff --git a/src/Input/Download.hs b/src/Input/Download.hs
--- a/src/Input/Download.hs
+++ b/src/Input/Download.hs
@@ -6,6 +6,7 @@
 import Control.Monad.Extra
 import System.Directory
 import Data.Conduit.Binary (sinkFile)
+import Data.Default.Class
 import qualified Network.HTTP.Conduit as C
 import Network.Connection
 import qualified Data.Conduit as C
@@ -45,7 +46,8 @@
       (TLSSettingsSimple {
         settingDisableCertificateValidation = insecure,
         settingDisableSession = False,
-        settingUseServerName = False
+        settingUseServerName = False,
+        settingClientSupported = def
       }) Nothing
     runResourceT $ do
         response <- C.http request manager
diff --git a/src/Input/Haddock.hs b/src/Input/Haddock.hs
--- a/src/Input/Haddock.hs
+++ b/src/Input/Haddock.hs
@@ -1,4 +1,6 @@
-{-# LANGUAGE ViewPatterns, PatternGuards, TupleSections, OverloadedStrings, Rank2Types, DeriveDataTypeable #-}
+{-# LANGUAGE ViewPatterns, PatternGuards, TupleSections, OverloadedStrings, Rank2Types, DeriveDataTypeable, LambdaCase #-}
+{-# OPTIONS_GHC -Wall #-}
+{-# OPTIONS_GHC -Wno-name-shadowing #-}
 
 module Input.Haddock(parseHoogle, fakePackage, input_haddock_test) where
 
@@ -8,22 +10,23 @@
 import Data.Maybe
 import Data.Data
 import Input.Item
+import Input.ParseDecl
 import General.Util
 import Control.DeepSeq
 import Control.Monad.Trans.Class
 import General.Conduit
 import Control.Monad.Extra
 import Control.Exception.Extra
-import Data.Generics.Uniplate.Data
 import General.Str
 import Safe
+import Distribution.Types.PackageName (unPackageName, mkPackageName)
 
 
 -- | An entry in the Hoogle DB
 data Entry = EPackage PkgName
            | EModule ModName
            | EDecl (Decl ())
-             deriving (Data,Typeable,Show)
+             deriving (Data, Show)
 
 
 fakePackage :: PkgName -> String -> (Maybe Target, [Item])
@@ -39,8 +42,12 @@
         f com url = do
             x <- await
             whenJust x $ \(i,s) -> case () of
-                _ | Just s <- bstrStripPrefix "-- | " s -> f [ignoreMath s] url
+                _ | s == "}" -> f [] ""
+                  -- Skip default methods like ($dmliftEq) and ($dmdisplayExceptionAnnotation)
+                  | Just{} <- bstrStripPrefix "($dm" s -> f [] ""
+                  | Just s <- bstrStripPrefix "-- | " s -> f [ignoreMath s] url
                   | Just s <- bstrStripPrefix "--" s -> f (if null com then [] else bstrTrimStart s : com) url
+                  | Just s <- bstrStripPrefix "    --" s -> f (if null com then [] else bstrTrimStart s : com) url
                   | Just s <- bstrStripPrefix "@url " s -> f com (bstrUnpack s)
                   | bstrNull $ bstrTrimStart s -> f [] ""
                   | otherwise -> do
@@ -61,11 +68,11 @@
              = fromMaybe x $ ". " `bstrStripPrefix` x
 ignoreMath x = x
 
-
-typeItem (EPackage x) = "package"
-typeItem (EModule x) = "module"
-typeItem _ = ""
-
+typeItem :: Entry -> String
+typeItem = \case
+    EPackage{} -> "package"
+    EModule{} -> "module"
+    EDecl{} -> ""
 
 -- FIXME: used to be in two different modules, now does and then undoes lots of stuff
 reformat :: [BStr] -> String
@@ -75,11 +82,11 @@
 hierarchyC :: Monad m => URL -> ConduitM (Target, Entry) (Maybe Target, [Item]) m ()
 hierarchyC packageUrl = void $ mapAccumC f (Nothing, Nothing)
     where
-        f (pkg, mod) (t, EPackage x) = ((Just (strUnpack x, url), Nothing), (Just t{targetURL=url}, [IPackage x]))
+        f (_pkg, _mod) (t, EPackage x) = ((Just (unPackageName x, url), Nothing), (Just t{targetURL=url}, [IPackage x]))
             where url = targetURL t `orIfNull` packageUrl
-        f (pkg, mod) (t, EModule x) = ((pkg, Just (strUnpack x, url)), (Just t{targetPackage=pkg, targetURL=url}, [IModule x]))
+        f (pkg, _mod) (t, EModule x) = ((pkg, Just (strUnpack x, url)), (Just t{targetPackage=pkg, targetURL=url}, [IModule x]))
             where url = targetURL t `orIfNull` (if isGhc then ghcModuleURL x else hackageModuleURL x)
-        f (pkg, mod) (t, EDecl i@InstDecl{}) = ((pkg, mod), (Nothing, hseToItem_ i))
+        f (pkg, mod) (_t, EDecl i@InstDecl{}) = ((pkg, mod), (Nothing, hseToItem_ i))
         f (pkg, mod) (t, EDecl x) = ((pkg, mod), (Just t{targetPackage=pkg, targetModule=mod, targetURL=url}, hseToItem_ x))
             where url = targetURL t `orIfNull` case x of
                             _ | [n] <- declNames x -> hackageDeclURL (isTypeSig x) n
@@ -91,8 +98,10 @@
         infix 1 `orIfNull`
         orIfNull x y = if null x then y else x
 
+renderPackage :: PkgName -> [Char]
+renderPackage x = "<b>package</b> <span class=name><s0>" ++ escapeHTML (unPackageName x) ++ "</s0></span>"
 
-renderPackage x = "<b>package</b> <span class=name><s0>" ++ escapeHTML (strUnpack x) ++ "</s0></span>"
+renderModule :: Str -> [Char]
 renderModule (breakEnd (== '.') . strUnpack -> (pre,post)) = "<b>module</b> " ++ escapeHTML pre ++ "<span class=name><s0>" ++ escapeHTML post ++ "</s0></span>"
 
 
@@ -121,7 +130,7 @@
 
 parseLine :: String -> Either String [Entry]
 parseLine x@('@':str) = case a of
-        "package" | [b] <- words b, b /= "" -> Right [EPackage $ strPack b]
+        "package" | [b] <- words b, b /= "" -> Right [EPackage $ mkPackageName b]
         "version" -> Right []
         _ -> Left $ "unknown attribute: " ++ x
     where (a,b) = word1 str
@@ -140,42 +149,36 @@
 fixLine (stripPrefix "(#) " -> Just x) = "( # ) " ++ x
 fixLine ('[':x:xs) | isAlpha x || x `elem` ("_(" :: String), (a,']':b) <- break (== ']') xs = x : a ++ b
 fixLine ('[':':':xs) | (a,']':b) <- break (== ']') xs = "(:" ++ a ++ ")" ++ b
+-- Record field accessor can start from '$', such as ($*) in algebra-4.3.1
+fixLine ('[':'$':xs) | (a,']':b) <- break (== ']') xs = "($" ++ a ++ ")" ++ b
+-- Record field accessor can start from '>', such as (>>-) in turtle-1.6.2
+fixLine ('[':'>':xs) | (a,']':b) <- break (== ']') xs = "(>" ++ a ++ ")" ++ b
 fixLine x | "class " `isPrefixOf` x = fst $ breakOn " where " x
 fixLine x = x
 
 
 readItem :: String -> Maybe (Decl ())
-readItem x | ParseOk y <- myParseDecl x = Just $ unGADT y
 readItem x -- newtype
     | Just x <- stripPrefix "newtype " x
     , ParseOk (DataDecl an _ b c d e) <- fmap unGADT $ myParseDecl $ "data " ++ x
     = Just $ DataDecl an (NewType ()) b c d e
-readItem x -- constructors
-    | ParseOk (GDataDecl _ _ _ _ _ [GadtDecl s name _ _ _ ty] _) <- myParseDecl $ "data Data where " ++ x
+readItem x@(x0 : _) -- constructors
+    | isUpper x0 || x0 == '('
+    , ParseOk (GDataDecl _ _ _ _ _ [GadtDecl s name _ _ _ ty] _) <- myParseDecl $ "data Data where " ++ x
     , let f (TyBang _ _ _ (TyParen _ x@TyApp{})) = x
+          f (TyParen _ x@TyApp{}) = x
           f (TyBang _ _ _ x) = x
           f x = x
     = Just $ TypeSig s [name] $ applyFun1 $ map f $ unapplyFun ty
-readItem ('(':xs) -- tuple constructors
-    | (com,')':rest) <- span (== ',') xs
-    , ParseOk (TypeSig s [Ident{}] ty) <- myParseDecl $ replicate (length com + 2) 'a' ++ rest
-    = Just $ TypeSig s [Ident s $ '(':com++")"] ty
-readItem (stripPrefix "data (" -> Just xs)  -- tuple data type
-    | (com,')':rest) <- span (== ',') xs
-    , ParseOk (DataDecl a b c d e f) <- fmap unGADT $ myParseDecl $
-        "data " ++ replicate (length com + 2) 'A' ++ rest
-    = Just $ DataDecl a b c (transform (op $ '(':com++")") d) e f
-    where op s DHead{} = DHead () $ Ident () s
-          op s x = x
+readItem x | ParseOk y <- myParseDecl x = Just $ unGADT y
 readItem _ = Nothing
 
-myParseDecl = fmap (fmap $ const ()) . parseDeclWithMode parseMode -- partial application, to share the initialisation cost
-
+unGADT :: Decl l -> Decl l
 unGADT (GDataDecl a b c d _  [] e) = DataDecl a b c d [] e
 unGADT x = x
 
 prettyItem :: Entry -> String
-prettyItem (EPackage x) = "package " ++ strUnpack x
+prettyItem (EPackage x) = "package " ++ unPackageName x
 prettyItem (EModule x) = "module " ++ strUnpack x
 prettyItem (EDecl x) = pretty x
 
@@ -192,16 +195,80 @@
     test "newtype Identity a"
     test "foo :: Int# -> b"
     test "(,,) :: a -> b -> c -> (a, b, c)"
-    test "data (,,) a b"
+    "data (,,) a b" === "data Tuple3 a b" -- when ghc-lib-parser >= 9.8
     test "reverse :: [a] -> [a]"
-    test "reverse :: [:a:] -> [:a:]"
+    -- Parallel Haskell has never been implemented
+    -- test "reverse :: [:a:] -> [:a:]"
     test "module Foo.Bar"
     test "data Char"
     "data Char :: *" === "data Char"
     "newtype ModuleName :: *" === "newtype ModuleName"
     "Progress :: !(Maybe String) -> {-# UNPACK #-} !Int -> !(Int -> Bool) -> Progress" ===
         "Progress :: Maybe String -> Int -> (Int -> Bool) -> Progress"
-    -- Broken in the last HSE release, fixed in HSE HEAD
-    -- test "quotRemInt# :: Int# -> Int# -> (# Int#, Int# #)"
+    test "quotRemInt# :: Int# -> Int# -> (# Int#, Int# #)"
     test "( # ) :: Int"
     test "pattern MyPattern :: ()"
+    test "degrees :: Floating x => Radians x -> Degrees x"
+    test "class Angle a"
+    test "instance Eq x => Eq (Degrees x)"
+    test "instance Angle Degrees"
+    test "type Queue a = Deque Nonthreadsafe Nonthreadsafe SingleEnd SingleEnd Grow Safe a"
+    test "class DequeClass d => PopL d"
+    test "tests_fifo :: DequeClass d => (forall elt . IO (d elt)) -> Test"
+    test "class ParUnsafe iv p | p -> iv"
+    test "(##) :: Diagram -> Diagram -> Diagram"
+    test "instance LayoutClass Positioned []"
+    test "data Ord a => Range a"
+    test "aPair :: Proxy (,)"
+    test "aTriple :: Proxy (,,)"
+    test "qop :: (Ord a, Show qtyp, Show (QFlipTyp qtyp), QFlipTyp (QFlipTyp qtyp) ~ qtyp) => Set (QueryRep QAtomTyp a) -> Set (QueryRep (QFlipTyp qtyp) a) -> QueryRep qtyp a"
+    test "reorient :: (Unbox a) => Bernsteinp Int a -> Bernsteinp Int a"
+    "type family PrimM a :: * -> *;" === "type family PrimM a :: * -> *"
+    test "HSNil :: HSet '[]"
+    "HSCons :: !elem -> HSet elems -> HSet (elem : elems)" === "HSCons :: elem -> HSet elems -> HSet (elem : elems)"
+    test "instance Data.HSet.Reverse.HReverse '[e] els1 els2 => Data.HSet.Reverse.HReverse '[] (e : els1) els2"
+    test "instance Data.HSet.Remove.HRemove (e : els) els 'TypeFun.Data.Peano.Z"
+    test "Free :: (forall m . Monad m => Effects effects m -> m a) -> Free effects a"
+    test "infixl 3 <||"
+    test "instance Data.String.IsString t => Data.String.IsString (t Yi.MiniBuffer.::: doc)"
+    test "runValueExpression :: (Functor f) => Expression a ((->) b) f r -> f ((a -> b) -> r)"
+    test "HCons :: (x :: *) -> HList xs -> HList (x : xs)"
+    test "instance forall k (key :: k) . Data.Traversable.Traversable (Data.ComposableAssociation.Association key)"
+    test "ReflH :: forall (k :: *) (t :: k) . HetEq t t"
+    test "egcd :: (PID d, (Euclidean d)) => d -> d -> (d, d, d)"
+    test "proc :: FilePath -> [String] -> CreateProcess"
+    test "unitTests :: Proxy '()"
+    test "type OneToFour = '[1, 2, 3, 4]"
+    test "data family Prio pol item :: *"
+    test "set :: (Monad m, ToByteString a) => Key -> a -> Opts \"SET\" -> Redis m Bool"
+    test "by :: ByteString -> Opts \"SORT\""
+    test "infixr 9 :+:"
+    test "instance forall k1 k2 (expectation1 :: k2) (expectation2 :: k1) . (Test.TypeSpec.Core.PrettyTypeSpec expectation1, Test.TypeSpec.Core.PrettyTypeSpec expectation2) => Test.TypeSpec.Core.PrettyTypeSpec '(expectation1, expectation2)"
+    test "SomeFoo :: Foo a => m a -> SomeFoo m"
+    test "(@~?) :: (HasCallStack, Ord a, Num a, Show a, ?epsilon :: a) => a -> a -> Assertion"
+    test "data Data where { Idx :: {idxChildren :: Index key (Node height key val)} -> Node ('S height) key val}"
+    test "UnexpectedResponse :: forall k a b . () => Host -> Response k a b -> ProtocolError"
+    test "(.) :: Category k cat => forall (b :: k) (c :: k) (a :: k) . cat b c -> cat a b -> cat a c"
+    test "infixl 3 `And`"
+    test "infix 1 `shouldBe`"
+    test "pattern The :: The d a => a -> d"
+    test "Html :: Element \"html\" '[] (Elements [\"head\", \"body\"]) (ManifestA & '[])"
+    test "instance forall k1 v1 (pk :: k1 -> GHC.Types.Constraint) (k2 :: k1) (pv :: v1 -> GHC.Types.Constraint) (v2 :: v1) . (pk k2, pv v2) => Type.Membership.KeyTargetAre pk pv (k2 'Type.Membership.Internal.:> v2)"
+    -- The following no longer parses since GHC 9.14 (which is the correct behaviour)
+    -- test "crDoubleBuffer :: CompactorReturn s -> {-# UNPACK #-} !DoubleBuffer s"
+    test "expectationFailure :: (?callStack :: CallStack) => String -> Expectation"
+    test "type family MapTyCon t xs = r | r -> xs"
+    test "pattern Id :: CRCategory k => (β ~ α, Object k α) => k α β"
+    test "pattern Stream :: () => () => Repetition"
+    test "In# :: (# #) -> In (a :: Effects) (b :: Effects)"
+    test "anyAsciiDecimalWord# :: Addr# -> Addr# -> (# (# #) | (# Word#, Addr# #) #)"
+    test "class SymbolToField (sym :: Symbol) rec typ | sym rec -> typ"
+    test "closestPairDist_spec :: _ => ([r] -> r) -> (r -> t) -> [b] -> Property"
+    -- Cannot faithfully represent ConstraintKind with ImplicitParams in HSE
+    -- test "type HasCallStack = ?callStack :: CallStack"
+    -- Cannot faithfully represent @r in HSE
+    -- test "Maybe# :: forall (r :: RuntimeRep) (a :: TYPE r). (# (# #) | a #) -> Maybe# @r a"
+    -- Cannot faithfully represent visible binders in HSE
+    -- test "data NDFamily_ :: forall (name :: Name) -> forall (ks :: Params name). ParamsProxy name ks -> Res name ks Any :~: r -> Args name ks -> Exp r"
+    -- Cannot faithfully represent standalone kind signatures in HSE
+    -- test "type MinBound :: a;"
diff --git a/src/Input/Item.hs b/src/Input/Item.hs
--- a/src/Input/Item.hs
+++ b/src/Input/Item.hs
@@ -4,7 +4,7 @@
 -- | Types used to generate the input.
 module Input.Item(
     Sig(..), Ctx(..), Ty(..), prettySig,
-    Item(..), itemName,
+    Item(..), itemName, highlightItem,
     Target(..), targetExpandURL, TargetId(..),
     splitIPackage, splitIModule,
     hseToSig, hseToItem, item_test,
@@ -30,6 +30,9 @@
 import qualified Data.Aeson as J
 import Data.Aeson.Types
 import Test.QuickCheck
+import Distribution.Types.PackageName (unPackageName, mkPackageName)
+import Query
+
 ---------------------------------------------------------------------
 -- TYPES
 
@@ -90,7 +93,7 @@
     rnf (IInstance a) = rnf a
 
 itemName :: Item -> Maybe Str
-itemName (IPackage x) = Just x
+itemName (IPackage x) = Just $ strPack $ unPackageName x
 itemName (IModule x) = Just x
 itemName (IName x) = Just x
 itemName (ISignature _) = Nothing
@@ -174,20 +177,43 @@
 unHTMLTarget :: Target -> Target
 unHTMLTarget t@Target {..} = t{targetItem=unHTML targetItem, targetDocs=unHTML targetDocs}
 
-splitIPackage, splitIModule :: [(a, Item)] -> [(Str, [(a, Item)])]
-splitIPackage = splitUsing $ \x -> case snd x of IPackage x -> Just x; _ -> Nothing
-splitIModule = splitUsing $ \x -> case snd x of IModule x -> Just x; _ -> Nothing
+splitIPackage :: [(a, Item)] -> [(PkgName, [(a, Item)])]
+splitIPackage = splitUsing (mkPackageName "") $ \x -> case snd x of
+    IPackage x -> Just x
+    _ -> Nothing
 
-splitUsing :: (a -> Maybe Str) -> [a] -> [(Str, [a])]
-splitUsing f = repeatedly $ \(x:xs) ->
+splitIModule :: [(a, Item)] -> [(Str, [(a, Item)])]
+splitIModule = splitUsing mempty $ \x -> case snd x of
+    IModule x -> Just x
+    _ -> Nothing
+
+splitUsing :: b -> (a -> Maybe b) -> [a] -> [(b, [a])]
+splitUsing def f = repeatedly $ \(x:xs) ->
     let (a,b) = break (isJust . f) xs
-    in ((fromMaybe mempty $ f x, x:a), b)
+    in ((fromMaybe def $ f x, x:a), b)
 
 item_test :: IO ()
 item_test = testing "Input.Item.Target JSON (encode . decode = id) " $ do
   quickCheck $ \(t :: Target) -> case J.eitherDecode $ J.encode t of
     (Left  e ) -> False
     (Right t') -> t == t'
+
+highlightItem:: Monoid m => (String -> m) -> (String -> m) -> (String -> m) -> (String -> m) -> [Query] -> String -> m
+highlightItem plain safe dull bold qs x
+    | Just (pre,x) <- stripInfix "<s0>" x, Just (name,post) <- stripInfix "</s0>" x
+        = safe pre <> highlight (unescapeHTML name) <> safe post
+    | otherwise = plain x
+    where
+        highlight x = mconcatMap (\xs@((b,_):_) -> let s = map snd xs in if b then bold s else dull s) $
+                    groupOn fst $ zip (findQueries x) x
+            where
+                -- generates a bool mask, which is only true for charachters that compose given queries
+                -- e.g. [ "query" "ya" ] -> [ "AqUeRyAA" ] -> 01111110
+                findQueries :: String -> [Bool]
+                findQueries (x:xs) | m > 0 = replicate m True ++ drop (m - 1) (findQueries xs)
+                    where m = maximum $ 0 : [length y | QueryName y <- qs, lower y `isPrefixOf` lower (x:xs)]
+                findQueries (x:xs) = False : findQueries xs
+                findQueries [] = []
 
 ---------------------------------------------------------------------
 -- HSE CONVERSION
diff --git a/src/Input/ParseDecl.hs b/src/Input/ParseDecl.hs
new file mode 100644
--- /dev/null
+++ b/src/Input/ParseDecl.hs
@@ -0,0 +1,609 @@
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE ImportQualifiedPost #-}
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE NamedFieldPuns #-}
+{-# LANGUAGE PatternSynonyms #-}
+{-# OPTIONS_GHC -Wall #-}
+{-# OPTIONS_GHC -Wno-missing-signatures -Wno-missing-pattern-synonym-signatures #-}
+
+module Input.ParseDecl (myParseDecl) where
+
+import Prelude hiding (Foldable(..))
+import Data.Char (isAlphaNum, isUpper)
+import Data.Foldable (Foldable(..))
+import Data.List.Extra (dropEnd1, drop1, enumerate, stripPrefix, unsnoc, isInfixOf)
+import Data.List.NonEmpty qualified as NE (toList)
+import Data.Maybe (isNothing)
+import GHC.Data.EnumSet qualified as EnumSet
+import GHC.Data.FastString (unpackFS)
+import GHC.Data.StringBuffer (stringToStringBuffer)
+import GHC.Hs
+import GHC.LanguageExtensions.Type (Extension (..))
+import GHC.Parser (parseDeclaration)
+import GHC.Parser.Lexer (P (..), ParseResult (..), initParserState, mkParserOpts)
+import GHC.Types.Fixity (Fixity (..), FixityDirection (..))
+import GHC.Types.Name (nameOccName, tvName)
+import GHC.Types.Name.Occurrence (OccName (..), occNameString)
+import GHC.Types.Name.Reader (RdrName (..), rdrNameOcc, rdrNameSpace)
+import GHC.Types.SourceText (SourceText (..))
+import GHC.Types.SrcLoc (GenLocated (..), mkRealSrcLoc, unLoc)
+import GHC.Unit (GenModule (..))
+import GHC.Utils.Error
+import GHC.Utils.Outputable (Outputable (..), defaultSDocContext, runSDoc)
+import Language.Haskell.Exts qualified as HSE
+import Text.Read (readMaybe)
+
+#if !MIN_VERSION_ghc_lib_parser(9,12,0)
+pattern HsBang x y <- HsSrcBang _ x y
+#endif
+
+#if MIN_VERSION_ghc_lib_parser(9,12,0)
+pattern MyFixity x y <- Fixity x y
+#else
+pattern MyFixity x y <- Fixity _ x y
+#endif
+
+#if MIN_VERSION_ghc_lib_parser(9,10,0)
+pattern MyPrefixConGADT x <- PrefixConGADT _ x
+pattern MyRecConGADT x <- RecConGADT _ x
+#else
+pattern MyPrefixConGADT x <- PrefixConGADT x
+pattern MyRecConGADT x <- RecConGADT x _
+#endif
+
+#if !MIN_VERSION_ghc_lib_parser(9,14,0)
+#define con_outer_bndrs con_bndrs
+#endif
+
+myParseDecl :: String -> HSE.ParseResult (HSE.Decl ())
+myParseDecl str = case runGhcLibParser str of
+    POk _state x -> case hsDeclToDecl (unLoc x) of
+        Nothing -> HSE.ParseFailed HSE.noLoc str
+        Just res -> HSE.ParseOk res
+    PFailed _state -> HSE.ParseFailed HSE.noLoc str
+
+hsDeclToDecl :: HsDecl GhcPs -> Maybe (HSE.Decl ())
+hsDeclToDecl (TyClD _ (SynDecl{tcdLName, tcdTyVars = HsQTvs{hsq_explicit}, tcdRhs})) =
+    Just $
+        HSE.TypeDecl
+            ()
+            ( foldl'
+                (\acc (L _ tv) -> HSE.DHApp () acc (hsTyVarBndrToTyVarBind tv))
+                (HSE.DHead () $ rdrNameToName $ unLoc tcdLName)
+                hsq_explicit
+            )
+            (hsTypeToType $ unLoc tcdRhs)
+hsDeclToDecl (TyClD _ (GHC.Hs.DataDecl{tcdLName, tcdTyVars = HsQTvs{hsq_explicit}, tcdDataDefn = HsDataDefn{dd_cons = DataTypeCons _ [], dd_ctxt, dd_kindSig = Nothing}})) =
+    Just $
+        HSE.DataDecl
+            ()
+            (HSE.DataType ())
+            (fmap (hsTypesToContext . unLoc) dd_ctxt)
+            ( foldl'
+                (\acc (L _ tv) -> HSE.DHApp () acc (hsTyVarBndrToTyVarBind tv))
+                (HSE.DHead () $ rdrNameToName $ unLoc tcdLName)
+                hsq_explicit
+            )
+            []
+            []
+hsDeclToDecl (TyClD _ (GHC.Hs.DataDecl{tcdLName, tcdTyVars = HsQTvs{hsq_explicit}, tcdDataDefn = HsDataDefn{dd_cons = DataTypeCons _ [], dd_kindSig = Just kind}})) =
+    Just $
+        HSE.GDataDecl
+            ()
+            (HSE.DataType ())
+            Nothing
+            ( foldl'
+                (\acc (L _ tv) -> HSE.DHApp () acc (hsTyVarBndrToTyVarBind tv))
+                (HSE.DHead () $ rdrNameToName $ unLoc tcdLName)
+                hsq_explicit
+            )
+            (Just $ hsTypeToType $ unLoc kind)
+            []
+            []
+hsDeclToDecl (TyClD _ (GHC.Hs.DataDecl{tcdLName, tcdTyVars = HsQTvs{hsq_explicit}, tcdDataDefn = HsDataDefn{dd_cons = DataTypeCons _ [L _ (ConDeclGADT{con_names, con_outer_bndrs, con_g_args = MyPrefixConGADT args, con_res_ty, con_mb_cxt})]}})) =
+    Just $
+        HSE.GDataDecl
+            ()
+            (HSE.DataType ())
+            Nothing
+            ( foldl'
+                (\acc (L _ tv) -> HSE.DHApp () acc (hsTyVarBndrToTyVarBind tv))
+                (HSE.DHead () $ rdrNameToName $ unLoc tcdLName)
+                hsq_explicit
+            )
+            Nothing
+            ( map
+                ( \con_name ->
+                    HSE.GadtDecl
+                        ()
+                        (rdrNameToName $ unLoc con_name)
+                        Nothing
+                        Nothing
+                        Nothing
+                        ( maybe id (\bs -> applyTyForall (Just bs) Nothing) (hsOuterTyVarBndrsToFoo $ unLoc con_outer_bndrs) $
+                            maybe id (applyTyForall Nothing . Just . hsTypesToContext . unLoc) con_mb_cxt $
+                                foldr
+#if MIN_VERSION_ghc_lib_parser(9,14,0)
+                                    (\CDF{cdf_type} -> HSE.TyFun () (hsTypeToType $ unLoc cdf_type))
+#else
+                                    (\(HsScaled _ a) -> HSE.TyFun () (hsTypeToType $ unLoc a))
+#endif
+                                    (hsTypeToType $ unLoc con_res_ty)
+                                    args
+                        )
+                )
+                (NE.toList con_names)
+            )
+            []
+hsDeclToDecl (TyClD _ (GHC.Hs.DataDecl{tcdLName, tcdTyVars = HsQTvs{hsq_explicit}, tcdDataDefn = HsDataDefn{dd_cons = DataTypeCons _ [L _ (ConDeclGADT{con_names, con_outer_bndrs, con_g_args = MyRecConGADT (L _ args), con_res_ty, con_mb_cxt})]}})) =
+    Just $
+        HSE.GDataDecl
+            ()
+            (HSE.DataType ())
+            Nothing
+            ( foldl'
+                (\acc (L _ tv) -> HSE.DHApp () acc (hsTyVarBndrToTyVarBind tv))
+                (HSE.DHead () $ rdrNameToName $ unLoc tcdLName)
+                hsq_explicit
+            )
+            Nothing
+            ( map
+                ( \con_name ->
+                    HSE.GadtDecl
+                        ()
+                        (rdrNameToName $ unLoc con_name)
+                        Nothing
+                        Nothing
+                        (Just $ map (conDeclFieldToFieldDecl . unLoc) args)
+                        ( maybe id (HSE.TyForall () Nothing . Just . hsTypesToContext . unLoc) con_mb_cxt $
+                            maybe id (\bs -> HSE.TyForall () (Just bs) Nothing) (hsOuterTyVarBndrsToFoo $ unLoc con_outer_bndrs) $
+                                hsTypeToType $
+                                    unLoc con_res_ty
+                        )
+                )
+                (NE.toList con_names)
+            )
+            []
+hsDeclToDecl (TyClD _ (FamDecl{tcdFam = FamilyDecl{fdLName, fdInfo = DataFamily, fdTyVars = HsQTvs{hsq_explicit}, fdResultSig}})) =
+    Just $
+        HSE.DataFamDecl
+            ()
+            Nothing
+            ( foldl'
+                (\acc (L _ tv) -> HSE.DHApp () acc (hsTyVarBndrToTyVarBind tv))
+                (HSE.DHead () $ rdrNameToName $ unLoc fdLName)
+                hsq_explicit
+            )
+            (familyResultSigToResultSig $ unLoc fdResultSig)
+hsDeclToDecl (TyClD _ (FamDecl{tcdFam = FamilyDecl{fdLName, fdTyVars = HsQTvs{hsq_explicit}, fdResultSig, fdInjectivityAnn}})) =
+    Just $
+        HSE.TypeFamDecl
+            ()
+            ( foldl'
+                (\acc (L _ tv) -> HSE.DHApp () acc (hsTyVarBndrToTyVarBind tv))
+                (HSE.DHead () $ rdrNameToName $ unLoc fdLName)
+                hsq_explicit
+            )
+            (familyResultSigToResultSig $ unLoc fdResultSig)
+            (fmap (injectivityAnnToInjectivityInfo . unLoc) fdInjectivityAnn)
+hsDeclToDecl (TyClD _ (GHC.Hs.ClassDecl{tcdCtxt, tcdLName, tcdTyVars = HsQTvs{hsq_explicit}, tcdFDs})) =
+    Just $
+        HSE.ClassDecl
+            ()
+            (fmap (hsTypesToContext . unLoc) tcdCtxt)
+            ( foldl'
+                (\acc (L _ tv) -> HSE.DHApp () acc (hsTyVarBndrToTyVarBind tv))
+                (HSE.DHead () $ rdrNameToName $ unLoc tcdLName)
+                hsq_explicit
+            )
+            (map (funDepToFunDep . unLoc) tcdFDs)
+            Nothing
+hsDeclToDecl (SigD _ (GHC.Hs.TypeSig _ names (HsWC{hswc_body = L _ HsSig{sig_body}}))) =
+    Just $
+        HSE.TypeSig
+            ()
+            (map (rdrNameToName . unLoc) names)
+            (hsTypeToType $ unLoc sig_body)
+hsDeclToDecl (SigD _ (GHC.Hs.PatSynSig _ names (L _ HsSig{sig_body}))) =
+    Just $ case hsTypeToType (unLoc sig_body) of
+        HSE.TyForall () Nothing (Just ctx1) (HSE.TyForall () Nothing (Just ctx2) ty) ->
+            HSE.PatSynSig
+                ()
+                (map (rdrNameToName . unLoc) names)
+                Nothing
+                (Just ctx1)
+                Nothing
+                (Just ctx2)
+                ty
+        HSE.TyForall () Nothing (Just ctx) ty ->
+            HSE.PatSynSig
+                ()
+                (map (rdrNameToName . unLoc) names)
+                Nothing
+                (Just ctx)
+                Nothing
+                Nothing
+                ty
+        ty ->
+            HSE.PatSynSig
+                ()
+                (map (rdrNameToName . unLoc) names)
+                Nothing
+                Nothing
+                Nothing
+                Nothing
+                ty
+hsDeclToDecl (SigD _ (FixSig _ (FixitySig _ names (MyFixity priority direction)))) =
+    Just $
+        HSE.InfixDecl
+            ()
+            (fixityDirectionToAssoc direction)
+            (Just priority)
+            (map (varOpOrConOp . rdrNameToName . unLoc) names)
+hsDeclToDecl (InstD _ (ClsInstD{cid_inst = ClsInstDecl{cid_poly_ty = (L _ HsSig{sig_bndrs, sig_body})}})) =
+    Just $ case hsTypeToType (unLoc sig_body) of
+        HSE.TyForall () Nothing ctxt body ->
+            HSE.InstDecl
+                ()
+                Nothing
+                (HSE.IRule () (hsOuterTyVarBndrsToFoo sig_bndrs) ctxt (typeToInstHead body))
+                Nothing
+        body ->
+            HSE.InstDecl
+                ()
+                Nothing
+                (HSE.IRule () (hsOuterTyVarBndrsToFoo sig_bndrs) Nothing (typeToInstHead body))
+                Nothing
+hsDeclToDecl _ = Nothing
+
+injectivityAnnToInjectivityInfo :: InjectivityAnn GhcPs -> HSE.InjectivityInfo ()
+injectivityAnnToInjectivityInfo = \case
+    InjectivityAnn _ lhs rhs ->
+        HSE.InjectivityInfo () (rdrNameToName $ unLoc lhs) (map (rdrNameToName . unLoc) rhs)
+
+#if MIN_VERSION_ghc_lib_parser(9,14,0)
+conDeclFieldToFieldDecl :: HsConDeclRecField GhcPs -> HSE.FieldDecl ()
+conDeclFieldToFieldDecl = \case
+    HsConDeclRecField{cdrf_names, cdrf_spec = CDF{cdf_type}} ->
+        HSE.FieldDecl
+            ()
+            (map (fieldOccToName . unLoc) cdrf_names)
+            (hsTypeToType $ unLoc cdf_type)
+#else
+conDeclFieldToFieldDecl :: ConDeclField GhcPs -> HSE.FieldDecl ()
+conDeclFieldToFieldDecl = \case
+    ConDeclField{cd_fld_names, cd_fld_type} ->
+        HSE.FieldDecl
+            ()
+            (map (fieldOccToName . unLoc) cd_fld_names)
+            (hsTypeToType $ unLoc cd_fld_type)
+#endif
+
+fieldOccToName :: FieldOcc GhcPs -> HSE.Name ()
+fieldOccToName = \case
+    FieldOcc{foLabel} -> rdrNameToName $ unLoc foLabel
+
+varOpOrConOp :: HSE.Name () -> HSE.Op ()
+varOpOrConOp name = case name of
+    HSE.Symbol () (':' : _) -> HSE.ConOp () name
+    HSE.Ident () (c : _)
+        | isUpper c -> HSE.ConOp () name
+        | otherwise -> HSE.VarOp () name
+    _ -> HSE.VarOp () name
+
+familyResultSigToResultSig :: FamilyResultSig GhcPs -> Maybe (HSE.ResultSig ())
+familyResultSigToResultSig = \case
+    NoSig{} -> Nothing
+    GHC.Hs.KindSig _ kind ->
+        Just $ HSE.KindSig () $ hsTypeToType $ unLoc kind
+    GHC.Hs.TyVarSig _ tvb ->
+        Just $ HSE.TyVarSig () $ hsTyVarBndrToTyVarBind $ unLoc tvb
+
+hsOuterTyVarBndrsToFoo :: HsOuterSigTyVarBndrs GhcPs -> Maybe [HSE.TyVarBind ()]
+hsOuterTyVarBndrsToFoo = \case
+    HsOuterImplicit{} -> Nothing
+    HsOuterExplicit{hso_bndrs} -> Just $ map (hsTyVarBndrToTyVarBind . unLoc) hso_bndrs
+
+funDepToFunDep :: GHC.Hs.FunDep GhcPs -> HSE.FunDep ()
+funDepToFunDep = \case
+    GHC.Hs.FunDep _ lhs rhs ->
+        HSE.FunDep () (map (rdrNameToName . unLoc) lhs) (map (rdrNameToName . unLoc) rhs)
+
+fixityDirectionToAssoc :: FixityDirection -> HSE.Assoc ()
+fixityDirectionToAssoc = \case
+    InfixL -> HSE.AssocLeft ()
+    InfixR -> HSE.AssocRight ()
+    InfixN -> HSE.AssocNone ()
+
+hsTyVarBndrToTyVarBind ::
+    HsTyVarBndr a GhcPs ->
+    HSE.TyVarBind ()
+#if MIN_VERSION_ghc_lib_parser(9,12,0)
+hsTyVarBndrToTyVarBind = \case
+    HsTvb _ _ (HsBndrVar _ (L _ var)) HsBndrNoKind{} ->
+        HSE.UnkindedVar () (rdrNameToName var)
+    HsTvb _ _ (HsBndrVar _ (L _ var)) (HsBndrKind _ (L _ kind)) ->
+        HSE.KindedVar () (rdrNameToName var) (hsTypeToType kind)
+    HsTvb _ _ (HsBndrWildCard _) _ ->
+        HSE.UnkindedVar () (HSE.Ident () "_")
+#else
+hsTyVarBndrToTyVarBind = \case
+    UserTyVar _ _ (L _ var) ->
+        HSE.UnkindedVar () (rdrNameToName var)
+    KindedTyVar _ _ (L _ var) (L _ kind) ->
+        HSE.KindedVar () (rdrNameToName var) (hsTypeToType kind)
+#endif
+
+occNameToName :: OccName -> HSE.Name ()
+occNameToName occ = case occNameString occ of
+    xs@(x : _)
+        | not (isAlphaNum x) && x /= '(' && x /= '_' -> HSE.Symbol () xs
+    xs -> HSE.Ident () xs
+
+rdrNameToName :: RdrName -> HSE.Name ()
+rdrNameToName = occNameToName . rdrNameOcc
+
+rdrNameToQName :: RdrName -> HSE.QName ()
+rdrNameToQName = \case
+    Unqual occName ->
+        HSE.UnQual () $ occNameToName occName
+    GHC.Types.Name.Reader.Qual modName occName ->
+        HSE.Qual
+            ()
+            (HSE.ModuleName () (moduleNameString modName))
+            (occNameToName occName)
+    Orig modul occName ->
+        HSE.Qual
+            ()
+            (HSE.ModuleName () (moduleNameString $ moduleName modul))
+            (occNameToName occName)
+    Exact name ->
+        HSE.UnQual () $ occNameToName (nameOccName name)
+
+hsTypeToType :: HsType GhcPs -> HSE.Type ()
+hsTypeToType = \case
+    HsListTy _ x ->
+        HSE.TyList () $ hsTypeToType $ unLoc x
+    HsTyVar _ _ (L _ (Exact x))
+        | show (runSDoc (ppr x) defaultSDocContext) == "[]" ->
+            HSE.TyCon () $ HSE.Special () $ HSE.ListCon ()
+        | show (runSDoc (ppr x) defaultSDocContext) == "->" ->
+            HSE.TyCon () $ HSE.Special () $ HSE.FunCon ()
+        | Just n <- stripPrefix "Tuple" (occNameString (nameOccName x))
+        , Just n' <- readMaybe n ->
+            HSE.TyCon () $ HSE.Special () $ HSE.TupleCon () HSE.Boxed n'
+    HsTyVar _ IsPromoted (L _ x) ->
+        HSE.TyPromoted () $ HSE.PromotedCon () True $ rdrNameToQName x
+    HsTyVar _ NotPromoted (L _ x) ->
+        case rdrNameSpace x of
+            ns
+                | ns == tvName ->
+                    HSE.TyVar () $ rdrNameToName x
+            _ -> HSE.TyCon () $ rdrNameToQName x
+    HsAppTy _ x y ->
+        HSE.TyApp () (hsTypeToType $ unLoc x) (hsTypeToType $ unLoc y)
+    HsFunTy _ _ x y ->
+        HSE.TyFun () (hsTypeToType $ unLoc x) (hsTypeToType $ unLoc y)
+    HsTupleTy _ HsBoxedOrConstraintTuple [] ->
+        HSE.TyCon () $ HSE.Special () $ HSE.UnitCon ()
+    HsTupleTy _ HsUnboxedTuple [] ->
+        HSE.TyCon () $ HSE.Special () $ HSE.UnboxedSingleCon ()
+    HsTupleTy _ boxed xs ->
+        HSE.TyTuple () (hsTupleSortToBoxed boxed) (map (hsTypeToType . unLoc) xs)
+    HsStarTy _ _ ->
+        HSE.TyStar ()
+#if !MIN_VERSION_ghc_lib_parser(9,14,0)
+    HsBangTy _ (HsBang unpackedness strictness) x ->
+        applyTyBang
+            (srcStrictnessToBangType strictness)
+            (srcUnpackednessToUnpackedness unpackedness)
+            (hsTypeToType $ unLoc x)
+#endif
+    HsParTy _ x -> case hsTypeToType (unLoc x) of
+        x'@HSE.TyKind{} -> x'
+        x' -> HSE.TyParen () x'
+    HsQualTy{hst_ctxt, hst_body} ->
+        applyTyForall Nothing (Just $ hsTypesToContext $ unLoc hst_ctxt) $
+            hsTypeToType $
+                unLoc hst_body
+    HsForAllTy{hst_tele = HsForAllInvis{hsf_invis_bndrs}, hst_body} ->
+        applyTyForall
+            (Just $ map (hsTyVarBndrToTyVarBind . unLoc) hsf_invis_bndrs)
+            Nothing
+            (hsTypeToType $ unLoc hst_body)
+    -- TODO FIXME when migrating to ghc-lib-parser completely:
+    -- HSE does not support forall with visible binders
+    HsForAllTy{hst_tele = HsForAllVis{hsf_vis_bndrs}, hst_body} ->
+        applyTyForall
+            (Just $ map (hsTyVarBndrToTyVarBind . unLoc) hsf_vis_bndrs)
+            Nothing
+            (hsTypeToType $ unLoc hst_body)
+    HsExplicitListTy _ IsPromoted xs ->
+        HSE.TyPromoted () $ HSE.PromotedList () True (map (hsTypeToType . unLoc) xs)
+    HsExplicitListTy _ NotPromoted [x] ->
+        HSE.TyList () $ hsTypeToType $ unLoc x
+    HsExplicitListTy _ NotPromoted xs ->
+        HSE.TyPromoted () $ HSE.PromotedList () False (map (hsTypeToType . unLoc) xs)
+
+#if MIN_VERSION_ghc_lib_parser(9,12,0)
+    HsExplicitTupleTy _ IsPromoted [] ->
+        HSE.TyPromoted () $ HSE.PromotedCon () True $ HSE.Special () $ HSE.UnitCon ()
+    HsExplicitTupleTy _ IsPromoted xs ->
+        HSE.TyPromoted () $ HSE.PromotedTuple () $ map (hsTypeToType . unLoc) xs
+    HsExplicitTupleTy _ NotPromoted xs ->
+        HSE.TyTuple () HSE.Boxed (map (hsTypeToType . unLoc) xs)
+#else
+    HsExplicitTupleTy _ [] ->
+        HSE.TyPromoted () $ HSE.PromotedCon () True $ HSE.Special () $ HSE.UnitCon ()
+    HsExplicitTupleTy _ xs ->
+        HSE.TyPromoted () $ HSE.PromotedTuple () $ map (hsTypeToType . unLoc) xs
+#endif
+
+    HsOpTy _ _ x (L _ (Unqual y)) z
+        | occNameString y == "~" ->
+            HSE.TyEquals () (hsTypeToType $ unLoc x) (hsTypeToType $ unLoc z)
+    HsOpTy _ promotion x (L _ (Exact y)) z
+        | occNameString (nameOccName y) == ":" ->
+            HSE.TyInfix
+                ()
+                (hsTypeToType $ unLoc x)
+                (promotionFlagToMaybePromotedName promotion $ HSE.Special () $ HSE.Cons ())
+                (hsTypeToType $ unLoc z)
+    HsOpTy _ promotion x y z ->
+        HSE.TyInfix
+            ()
+            (hsTypeToType $ unLoc x)
+            (promotionFlagToMaybePromotedName promotion $ rdrNameToQName $ unLoc y)
+            (hsTypeToType $ unLoc z)
+    HsKindSig _ lhs rhs ->
+        HSE.TyKind () (hsTypeToType $ unLoc lhs) (hsTypeToType $ unLoc rhs)
+    HsTyLit _ (HsNumTy (SourceText txt) val) ->
+        HSE.TyPromoted () $ HSE.PromotedInteger () val (unpackFSOrId txt)
+    HsTyLit _ (HsStrTy (SourceText txt) val) ->
+        HSE.TyPromoted () $ HSE.PromotedString () (unpackFS val) (drop1 $ dropEnd1 $ unpackFSOrId txt)
+    HsSumTy _ xs ->
+        HSE.TyUnboxedSum () $ map (hsTypeToType . unLoc) xs
+    HsWildCardTy _ ->
+        HSE.TyWildCard () Nothing
+    -- Everything else cannot be represented in HSE, so replacing with a wildcard
+    _ -> HSE.TyWildCard () Nothing
+
+promotionFlagToMaybePromotedName ::
+    PromotionFlag -> HSE.QName () -> HSE.MaybePromotedName ()
+promotionFlagToMaybePromotedName = \case
+    NotPromoted -> HSE.UnpromotedName ()
+    IsPromoted -> HSE.PromotedName ()
+
+applyTyForall ::
+    Maybe [HSE.TyVarBind ()] -> Maybe (HSE.Context ()) -> HSE.Type () -> HSE.Type ()
+applyTyForall mArg1 mArg2 = \case
+    HSE.TyForall () Nothing mArg2' ty
+        | isNothing mArg2 -> HSE.TyForall () mArg1 mArg2' ty
+    ty -> HSE.TyForall () mArg1 mArg2 ty
+
+#if !MIN_VERSION_ghc_lib_parser(9,14,0)
+applyTyBang ::
+    HSE.BangType () -> HSE.Unpackedness () -> HSE.Type () -> HSE.Type ()
+applyTyBang bang unpack = \case
+    HSE.TyBang () (HSE.NoStrictAnnot ()) unpack' ty
+        | unpack == HSE.NoUnpackPragma () -> HSE.TyBang () bang unpack' ty
+    HSE.TyBang () bang' (HSE.NoUnpackPragma ()) ty
+        | bang == HSE.NoStrictAnnot () -> HSE.TyBang () bang' unpack ty
+    HSE.TyApp () x y -> HSE.TyApp () (applyTyBang bang unpack x) y
+    ty -> HSE.TyBang () bang unpack ty
+
+srcStrictnessToBangType :: SrcStrictness -> HSE.BangType ()
+srcStrictnessToBangType = \case
+    SrcLazy -> HSE.LazyTy ()
+    SrcStrict -> HSE.BangedTy ()
+    NoSrcStrict -> HSE.NoStrictAnnot ()
+
+srcUnpackednessToUnpackedness :: SrcUnpackedness -> HSE.Unpackedness ()
+srcUnpackednessToUnpackedness = \case
+    SrcUnpack -> HSE.Unpack ()
+    SrcNoUnpack -> HSE.NoUnpack ()
+    NoSrcUnpack -> HSE.NoUnpackPragma ()
+#endif
+
+typeToInstHead :: HSE.Type () -> HSE.InstHead ()
+typeToInstHead = \case
+    HSE.TyApp () x y -> HSE.IHApp () (typeToInstHead x) y
+    HSE.TyCon () x -> HSE.IHCon () x
+    HSE.TyInfix () x (HSE.UnpromotedName () y) z -> HSE.IHApp () (HSE.IHInfix () x y) z
+    -- The rest happens only in ghc-prim, which are likely some magical forms.
+    -- Let's skip them.
+    _ -> HSE.IHCon () $ HSE.Special () $ HSE.UnitCon ()
+
+hsTypesToContext ::
+    [GenLocated SrcSpanAnnA (HsType GhcPs)] ->
+    HSE.Context ()
+hsTypesToContext = \case
+    [] -> HSE.CxEmpty ()
+    [x] -> HSE.CxSingle () $ hsTypeToAsst $ unLoc x
+    xs -> HSE.CxTuple () $ map (hsTypeToAsst . unLoc) xs
+
+hsTypeToAsst :: HsType GhcPs -> HSE.Asst ()
+hsTypeToAsst = \case
+    HsIParamTy _ name t ->
+        HSE.IParam () (hsIPNameToIPName $ unLoc name) (hsTypeToType $ unLoc t)
+    HsParTy _ t ->
+        HSE.ParenA () $ hsTypeToAsst $ unLoc t
+    t -> case hsTypeToType t of
+        HSE.TyParen () ty -> HSE.ParenA () $ HSE.TypeA () ty
+        ty -> HSE.TypeA () ty
+
+hsIPNameToIPName :: HsIPName -> HSE.IPName ()
+hsIPNameToIPName = HSE.IPDup () . unpackFS . hsIPNameFS
+
+hsTupleSortToBoxed :: HsTupleSort -> HSE.Boxed
+hsTupleSortToBoxed = \case
+    HsUnboxedTuple -> HSE.Unboxed
+    HsBoxedOrConstraintTuple -> HSE.Boxed
+
+runGhcLibParser ::
+    String ->
+    GHC.Parser.Lexer.ParseResult (GenLocated SrcSpanAnnA (HsDecl GhcPs))
+runGhcLibParser str
+    | Just (str', ';') <- unsnoc str =
+        runGhcLibParser str'
+runGhcLibParser str = case runGhcLibParserWithExtensions almostAllExtensions str of
+    PFailed{}
+        | '#' `elem` str -> runGhcLibParserWithExtensions noUnboxed str
+        | '*' `elem` str -> runGhcLibParserWithExtensions noStarIsType str
+        | "pattern" `isInfixOf` str -> runGhcLibParserWithExtensions noPatternSynonyms str
+    res -> res
+
+allExtensions :: EnumSet.EnumSet Extension
+allExtensions = EnumSet.fromList enumerate
+
+almostAllExtensions :: EnumSet.EnumSet Extension
+almostAllExtensions =
+    foldr
+        EnumSet.delete
+        allExtensions
+        [ Arrows -- makes "proc" a keyword
+        , RecursiveDo -- makes "mdo" and "rec" keywords
+        , StaticPointers -- makes "static" a keyword
+        , TransformListComp -- makes "by", "group" and "using" keywords
+        ]
+
+noUnboxed :: EnumSet.EnumSet Extension
+noUnboxed =
+    foldr
+        EnumSet.delete
+        almostAllExtensions
+        [ UnboxedSums
+        , UnboxedTuples
+        ]
+
+noPatternSynonyms :: EnumSet.EnumSet Extension
+noPatternSynonyms = EnumSet.delete PatternSynonyms almostAllExtensions
+
+noStarIsType :: EnumSet.EnumSet Extension
+noStarIsType = EnumSet.delete StarIsType almostAllExtensions
+
+runGhcLibParserWithExtensions ::
+    EnumSet.EnumSet Extension ->
+    String ->
+    GHC.Parser.Lexer.ParseResult (GenLocated SrcSpanAnnA (HsDecl GhcPs))
+runGhcLibParserWithExtensions extensions str = unP parseDeclaration parseState
+  where
+#if MIN_VERSION_ghc_lib_parser(9,14,0)
+    opts = mkParserOpts extensions emptyDiagOpts False False False False
+#else
+    opts = mkParserOpts extensions emptyDiagOpts [] False False False False
+#endif
+    dummyLocation = mkRealSrcLoc mempty 1 1
+    buffer = stringToStringBuffer str
+    parseState = initParserState opts buffer dummyLocation
+
+#if !MIN_VERSION_ghc_lib_parser(9,8,0)
+emptyDiagOpts :: DiagOpts
+emptyDiagOpts = DiagOpts mempty mempty False False Nothing defaultSDocContext
+#endif
+
+#if MIN_VERSION_ghc_lib_parser(9,8,0)
+unpackFSOrId = unpackFS
+#else
+unpackFSOrId = id
+#endif
diff --git a/src/Input/Reorder.hs b/src/Input/Reorder.hs
--- a/src/Input/Reorder.hs
+++ b/src/Input/Reorder.hs
@@ -8,15 +8,16 @@
 import Data.Tuple.Extra
 import General.Util
 import General.Str
+import Distribution.Types.PackageName (mkPackageName, unPackageName)
 
 
 pkgGhc :: PkgName
-pkgGhc = strPack "ghc"
+pkgGhc = mkPackageName "ghc"
 
 packageOrderHacks :: (PkgName -> Int) -> PkgName -> Int
 -- 'ghc' is the canonical module that both 'ghc-lib-parser' and 'ghc-lib' copy from, so better to pick that
 -- even though ghc-lib-* are used more on Stackage (but a lot less on Hackage)
-packageOrderHacks f x | x == pkgGhc = min (f x) $ min (f $ strPack "ghc-lib-parser") (f $ strPack "ghc-lib") - 1
+packageOrderHacks f x | x == pkgGhc = min (f x) $ min (f $ mkPackageName "ghc-lib-parser") (f $ mkPackageName "ghc-lib") - 1
 packageOrderHacks f x = f x
 
 
@@ -28,4 +29,4 @@
     where
         refunc = map $ second $ \(x:xs) -> x : sortOn (itemName . snd) xs
         rebase (x, xs) = (x, concatMap snd $ sortOn (((negate . f . strUnpack) &&& id) . fst) $ refunc $ splitIModule xs)
-            where f = reorderModule (strUnpack x)
+            where f = reorderModule (unPackageName x)
diff --git a/src/Input/Set.hs b/src/Input/Set.hs
--- a/src/Input/Set.hs
+++ b/src/Input/Set.hs
@@ -27,7 +27,8 @@
 setPlatformWith :: FilePath -> [String] -> IO (Set.Set String)
 setPlatformWith file names = do
     src <- lines <$> readFile' file
-    pure $ Set.fromList [read lib | ",":name:lib:_ <- map words src, name `elem` names]
+    pure $ Set.delete "bin-package-db" $
+        Set.fromList [read lib | ",":name:lib:_ <- map words src, name `elem` names]
 
 setGHC :: FilePath -> IO (Set.Set String)
 setGHC file = setPlatformWith file ["incGHCLib"]
diff --git a/src/Input/Settings.hs b/src/Input/Settings.hs
--- a/src/Input/Settings.hs
+++ b/src/Input/Settings.hs
@@ -1,4 +1,4 @@
-{-# LANGUAGE RecordWildCards, PatternGuards, TemplateHaskell, CPP #-}
+{-# LANGUAGE PackageImports, RecordWildCards, PatternGuards, TemplateHaskell, CPP #-}
 
 
 -- | Module for reading settings files.
@@ -9,7 +9,7 @@
 import Control.Exception (catch, throwIO)
 import Data.List.Extra
 import Data.Maybe
-import Language.Haskell.TH.Syntax (lift, runIO)
+import "template-haskell" Language.Haskell.TH.Syntax (lift, runIO)
 import System.FilePath
 import System.IO.Error (isDoesNotExistError)
 import System.IO.Extra
diff --git a/src/Output/Tags.hs b/src/Output/Tags.hs
--- a/src/Output/Tags.hs
+++ b/src/Output/Tags.hs
@@ -18,6 +18,7 @@
 import General.Util
 import General.Store
 import General.Str
+import Distribution.Types.PackageName (unPackageName, mkPackageName)
 
 ---------------------------------------------------------------------
 -- DATA TYPE
@@ -43,23 +44,23 @@
 writeTags :: StoreWrite -> (PkgName -> Bool) -> (PkgName -> [(String,String)]) -> [(Maybe TargetId, Item)] -> IO ()
 writeTags store keep extra xs = do
     let splitPkg = splitIPackage xs
-    let packages = addRange splitPkg
-    storeWrite store Packages (bstr0Join $ map (strUnpack . fst) packages, V.fromList $ map snd packages)
+    let packages = addRange (== mkPackageName "") splitPkg
+    storeWrite store Packages (bstr0Join $ map (unPackageName . fst) packages, V.fromList $ map snd packages)
 
     let categories = map (bimap snd reverse) $ Map.toList $ Map.fromListWith (++)
             [(((weightTag ex, both lower ex), joinPair ":" ex),[rng]) | (p,rng) <- packages, ex <- extra p]
     storeWrite store Categories (bstr0Join $ map fst categories, jaggedFromList $ map snd categories)
 
-    let modules = addRange $ concatMap (splitIModule . snd) splitPkg
+    let modules = addRange strNull $ concatMap (splitIModule . snd) splitPkg
     storeWrite store Modules (bstr0Join $ map (lower . strUnpack . fst) modules, V.fromList $ map snd modules)
 
     storeWrite store Completions $ bstr0Join $
         takeWhile ("set:" `isPrefixOf`) (map fst categories) ++
-        map ("package:"++) (sortOn lower $ map strUnpack $ nubOrd $ filter keep $ map fst packages) ++
+        map ("package:"++) (sortOn lower $ map unPackageName $ nubOrd $ filter keep $ map fst packages) ++
         map (joinPair ":") (sortOn (weightTag &&& both lower) $ nubOrd [ex | (p,_) <- packages, keep p, ex <- extra p, fst ex /= "set"])
     where
-        addRange :: [(Str, [(Maybe TargetId,a)])] -> [(Str, (TargetId, TargetId))]
-        addRange xs = [(a, (minimum' is, maximum' is)) | (a,b) <- xs, let is = mapMaybe fst b, not $ strNull a, is /= []]
+        addRange :: (str -> Bool) -> [(str, [(Maybe TargetId,a)])] -> [(str, (TargetId, TargetId))]
+        addRange isNull xs = [(a, (minimum' is, maximum' is)) | (a,b) <- xs, let is = mapMaybe fst b, not (isNull a), is /= []]
 
         weightTag ("set",x) = fromMaybe 0.9 $ lookup x [("stackage",0.0),("haskell-platform",0.1)]
         weightTag ("package",x) = 1
diff --git a/src/Output/Types.hs b/src/Output/Types.hs
--- a/src/Output/Types.hs
+++ b/src/Output/Types.hs
@@ -267,7 +267,7 @@
 fpRaresFold g f Fingerprint{..} = f fpRare1 `g` f fpRare2 `g` f fpRare3
 
 instance Storable Fingerprint where
-    sizeOf _ = 3*sizeOf name0 + 2
+    sizeOf _ = 4 * sizeOf name0
     alignment _ = 4
     peekByteOff ptr i = Fingerprint
         <$> peekByteOff ptr (i+0) <*> peekByteOff ptr (i+1*w) <*> peekByteOff ptr (i+2*w)
