@@ -4,30 +4,92 @@ import Docs.Search.Config (config)
44import Docs.Search.Declarations (Declarations(..))
55import Docs.Search.SearchResult (SearchResult(..))
66import Docs.Search.Types (ModuleName, PackageName)
7+import Docs.Search.Extra (stringToList)
8+import Docs.Search.Score (Scores, normalizePackageName)
79
810import Prelude
11+
12+import Control.Monad.State (execState, modify_)
913import Control.Promise (Promise, toAffE)
1014import Data.Argonaut.Core (Json)
1115import Data.Argonaut.Decode (decodeJson)
16+import Data.Array as Array
1217import Data.Either (hush)
13−import Data.Foldable (foldr)
14−import Data.List (List)
18+import Data.Foldable (foldl)
19+import Data.Lens ((%~))
20+import Data.Lens.Record (prop)
21+import Data.List (List, (:))
1522import Data.Map (Map)
1623import Data.Map as Map
1724import Data.Maybe (fromMaybe)
25+import Data.Search.Trie (Trie)
1826import Data.Search.Trie as Trie
1927import Data.Set (Set)
2028import Data.Set as Set
29+import Data.String.CodeUnits (toCharArray) as String
30+import Data.String.Common (split, toLower) as String
31+import Data.String.Pattern (Pattern(..))
32+import Data.Symbol (SProxy(..))
33+import Data.Traversable (foldr, for_)
34+import Data.Tuple.Nested ((/\))
2135import Effect (Effect)
2236import Effect.Aff (Aff)
2337
2438
25−type ModuleIndex = Map PackageName (Set ModuleName)
39+-- | Module index that is actually stored in a JS file.
40+type PackedModuleIndex = Map PackageName (Set ModuleName)
41+
42+-- | "Expanded" module index that can be queried quickly.
43+type ModuleIndex = { packageModules :: Map PackageName (Set ModuleName)
44+ , modulePackages :: Map ModuleName PackageName
45+ , index :: Trie Char ModuleName
46+ }
47+
48+
49+type ModuleResult
50+ = { name :: ModuleName
51+ , package :: PackageName
52+ , score :: Int
53+ }
54+
55+
56+unpackModuleIndex :: PackedModuleIndex -> ModuleIndex
57+unpackModuleIndex packageModules =
58+ flip execState { packageModules, modulePackages: mempty, index: mempty } do
59+ for_ (Map.toUnfoldableUnordered packageModules :: Array _)
60+ \(packageName /\ moduleNames) -> do
61+ for_ moduleNames \moduleName -> do
62+ modify_ $ _modulePackages %~ Map.insert moduleName packageName
63+ for_ (extractModuleNameParts moduleName) \part -> do
64+ let partPath = Array.toUnfoldable $ String.toCharArray part
65+ modify_ $ _index %~ Trie.insert partPath moduleName
66+
67+
68+-- | E.g. `"Data.Array.ST" -> ["data.array.st", "array.st", "st"]`.
69+extractModuleNameParts :: ModuleName -> List String
70+extractModuleNameParts =
71+ foldl (\acc el -> el : map (_ <> "." <> el) acc) mempty <<< String.split (Pattern ".") <<< String.toLower
72+
73+
74+queryModuleIndex
75+ :: Scores
76+ -> ModuleIndex
77+ -> String
78+ -> Array ModuleResult
79+queryModuleIndex scores { index, modulePackages } query =
80+ let path = stringToList $ String.toLower query in
81+ Trie.queryValues path index #
82+ Array.fromFoldable #
83+ Array.nub <#>
84+ (\name -> do
85+ package <- Map.lookup name modulePackages
86+ pure { name, package, score: fromMaybe 0 $ Map.lookup (normalizePackageName package) scores }) #
87+ Array.catMaybes
2688
2789
2890-- | Constructs a mapping from packages to modules
29−mkModuleIndex :: Declarations -> ModuleIndex
30−mkModuleIndex (Declarations trie) =
91+mkPackedModuleIndex :: Declarations -> PackedModuleIndex
92+mkPackedModuleIndex (Declarations trie) =
3193 foldr (Map.unionWith Set.union) mempty $ extract <$> Trie.values trie
3294 where
3395 extract
@@ -39,7 +101,7 @@ mkModuleIndex (Declarations trie) =
39101 Map.singleton packageName (Set.singleton moduleName)
40102
41103
42−loadModuleIndex :: Aff ModuleIndex
104+loadModuleIndex :: Aff PackedModuleIndex
43105loadModuleIndex = do
44106 json <- toAffE $ load config.moduleIndexLoadPath
45107 pure $ fromMaybe mempty $ hush $ decodeJson json
@@ -48,3 +110,10 @@ loadModuleIndex = do
48110foreign import load
49111 :: String
50112 -> Effect (Promise Json)
113+
114+
115+_modulePackages :: forall a b rest. (a -> b) -> { modulePackages :: a | rest } -> { modulePackages :: b | rest }
116+_modulePackages = prop (SProxy :: SProxy "modulePackages")
117+
118+_index :: forall a b rest. (a -> b) -> { index :: a | rest } -> { index :: b | rest }
119+_index = prop (SProxy :: SProxy "index")