git.iko.soy/rowtype-yoga/purescript-docs-search
on master help sign in
refactor IndexBuilder + some tests (#3)
commit6ba28b3de8c91c6f0f4f9c2b3c55263bc17fb055 authorVladimir Kalnitsky <klntsky@gmail.com> dateMon 15 Jul 2019, 22:25 (7 years ago) parent80880f1
5 files changed+104−67
src/Docs/Search/IndexBuilder.purs+44−38 test/Main.purs+5−11 test/Test/Extra.purs+23 test/Test/IndexBuilder.purs+30 test/Test/TypeQuery.purs+2−18
src/Docs/Search/IndexBuilder.purs+44−38
@@ -4,6 +4,7 @@ import Prelude
44
55import Docs.Search.Config (config)
66import Docs.Search.Declarations (Declarations(..), mkDeclarations)
7+import Docs.Search.DocsJson (DocsJson)
78import Docs.Search.Extra ((>#>))
89import Docs.Search.Index (getPartId)
910import Docs.Search.SearchResult (SearchResult)
@@ -26,7 +27,6 @@ import Data.Set as Set
2627import Data.String.CodePoints (contains) as String
2728import Data.String.CodeUnits (singleton) as String
2829import Data.String.Common (replace) as String
29−import Data.String.Pattern (Pattern(..)) as String
3030import Data.String.Pattern (Pattern(..), Replacement(..))
3131import Data.Traversable (for, for_)
3232import Data.Tuple (Tuple(..), fst, snd)
@@ -38,7 +38,6 @@ import Node.Encoding (Encoding(UTF8))
3838import Node.FS.Aff (exists, mkdir, readTextFile, readdir, stat, writeTextFile)
3939import Node.FS.Stats (isDirectory, isFile)
4040import Node.Process as Process
41−import Docs.Search.DocsJson (DocsJson)
4241
4342main :: Effect Unit
4443main = launchAff_ mainAff
@@ -82,7 +81,7 @@ collectDocsJsons :: String -> Aff (Array DocsJson)
8281collectDocsJsons outputDir = do
8382 paths <- readdir outputDir
8483
85− mbs <- for paths \moduleName -> do
84+ Array.catMaybes <$> for paths \moduleName -> do
8685 let jsonFile = "output/" <> moduleName <> "/docs.json"
8786 doesExist <- fileExists jsonFile
8887 if doesExist then do
@@ -103,8 +102,6 @@ collectDocsJsons outputDir = do
103102 "Couldn't find docs.json for " <> moduleName
104103 pure Nothing
105104
106− pure $ Array.catMaybes mbs
107−
108105writeTypeIndex :: TypeIndex -> Aff Unit
109106writeTypeIndex typeIndex =
110107 for_ entries \(Tuple typeShape results) -> do
@@ -117,33 +114,34 @@ writeTypeIndex typeIndex =
117114 entries :: Array _
118115 entries = Map.toUnfoldableUnordered (unwrap typeIndex)
119116
120−writeIndex :: Declarations -> Aff Unit
121−writeIndex (Declarations trie) = do
122− let
123− prefixes :: Array (List Char)
124− prefixes =
117+-- | Get a mapping from index parts to index contents.
118+getIndex :: Declarations -> Map Int (Array (Tuple String (Array SearchResult)))
119+getIndex (Declarations trie) =
120+ Array.foldr insert mempty parts
121+ where
122+ prefixes :: Array (List Char)
123+ prefixes =
125124 Set.toUnfoldable $
126125 List.foldr (\entry -> Set.insert (List.take 2 $ fst entry)) mempty $
127126 Trie.entriesUnordered trie
128127
129− parts
130− :: Array { prefix :: List Char
131− , results :: Array (Tuple String (Array SearchResult))
132− }
133− parts = prefixes <#> \prefix ->
134− let results =
135− Array.fromFoldable $
136− Trie.query prefix trie <#>
137− \(Tuple path value) ->
138− Tuple (path >#> String.singleton) (Array.fromFoldable value)
139− in
140− { prefix, results }
141−
142− resultsMap :: Map Int (Array (Tuple String (Array SearchResult)))
143− resultsMap = Array.foldr insert mempty parts
144−
145− insert part = Map.insertWith append (getPartId part.prefix) part.results
128+ parts
129+ :: Array { prefix :: List Char
130+ , results :: Array (Tuple String (Array SearchResult))
131+ }
132+ parts = prefixes <#> \prefix ->
133+ let results =
134+ Array.fromFoldable $
135+ Trie.query prefix trie <#>
136+ \(Tuple path value) ->
137+ Tuple (path >#> String.singleton) (Array.fromFoldable value)
138+ in
139+ { prefix, results }
140+
141+ insert part = Map.insertWith append (getPartId part.prefix) part.results
146142
143+writeIndex :: Declarations -> Aff Unit
144+writeIndex = getIndex >>> \resultsMap -> do
147145 for_ (Map.toUnfoldableUnordered resultsMap :: Array _)
148146 \(Tuple indexPartId results) -> do
149147 let header =
@@ -153,17 +151,24 @@ writeIndex (Declarations trie) = do
153151 writeTextFile UTF8 (config.mkIndexPartPath indexPartId) $
154152 header <> stringify (encodeJson results)
155153
154+patchHTML :: String -> Tuple Boolean String
155+patchHTML html =
156+ let
157+ pattern = Pattern "</body>"
158+ patch = "<!-- Docs search index. -->" <>
159+ "<script type=\"text/javascript\" src=\"../docs-search-app.js\"></script>" <>
160+ "<script type=\"text/javascript\">" <>
161+ "window.DocsSearchTypeIndex = {};" <>
162+ "window.DocsSearchIndex = {};" <>
163+ "</script>" <>
164+ "</body>"
165+ in if not $ String.contains (Pattern patch) html
166+ then Tuple true $ String.replace pattern (Replacement patch) html
167+ else Tuple false html
168+
156169patchDocs :: Aff Unit
157170patchDocs = do
158171 let dirname = "generated-docs/"
159− pattern = Pattern "</body>"
160− patch = "<!-- Docs search index. -->" <>
161− "<script type=\"text/javascript\" src=\"../docs-search-app.js\"></script>" <>
162− "<script type=\"text/javascript\">" <>
163− "window.DocsSearchTypeIndex = {};" <>
164− "window.DocsSearchIndex = {};" <>
165− "</script>" <>
166− "</body>"
167172
168173 files <- readdir (dirname <> "html")
169174
@@ -172,9 +177,10 @@ patchDocs = do
172177
173178 whenM (fileExists path) do
174179 contents <- readTextFile UTF8 path
175− when (not $ String.contains (String.Pattern patch) contents) do
176− writeTextFile UTF8 path $
177− String.replace pattern (Replacement patch) contents
180+ case patchHTML contents of
181+ Tuple true patchedContents -> do
182+ writeTextFile UTF8 path patchedContents
183+ _ -> pure unit
178184
179185createDirectories :: Aff Unit
180186createDirectories = do
test/Main.purs+5−11
@@ -4,17 +4,18 @@ import Prelude
44
55import Docs.Search.TypeDecoder (Constraint(..), FunDep(..), FunDeps(..), Kind(..), QualifiedName(..), Type(..))
66import Test.TypeQuery as TypeQuery
7+import Test.IndexBuilder as IndexBuilder
8+
9+import Test.Extra (assertRight)
710
811import Data.Argonaut.Decode (decodeJson)
912import Data.Argonaut.Encode (encodeJson)
1013import Data.Argonaut.Parser (jsonParser)
11−import Data.Either (Either(..), fromRight)
14+import Data.Either (fromRight)
1215import Data.Maybe (Maybe(..))
1316import Effect (Effect)
14−import Effect.Aff (Aff)
1517import Partial.Unsafe (unsafePartial)
1618import Test.Unit (TestSuite, suite, test)
17−import Test.Unit.Assert as Assert
1819import Test.Unit.Main (runTest)
1920
2021main :: Effect Unit
@@ -24,6 +25,7 @@ main = do
2425mainTest :: TestSuite
2526mainTest = do
2627 TypeQuery.tests
28+ IndexBuilder.tests
2729
2830 let mkJson x = unsafePartial $ fromRight $ jsonParser x
2931 suite "FunDeps decoder" do
@@ -415,11 +417,3 @@ mainTest = do
415417
416418qualified :: Array String -> String -> QualifiedName
417419qualified moduleName name = QualifiedName { moduleName, name }
418−
419−assertRight :: forall a. Show a => Eq a => Either String a -> a -> Aff Unit
420−assertRight eiActual expected =
421− case eiActual of
422− Left string -> do
423− Assert.equal (Right expected) eiActual
424− Right actual -> do
425− Assert.equal (Right expected) eiActual
test/Test/Extra.pursnew file+23
@@ -0,0 +1,23 @@
1+module Test.Extra where
2+
3+import Prelude
4+
5+import Data.Either (Either(..))
6+import Effect.Aff (Aff)
7+import Test.Unit.Assert as Assert
8+
9+assertRight
10+ :: forall a b
11+ . Show a
12+ => Show b
13+ => Eq a
14+ => Eq b
15+ => Either b a
16+ -> a
17+ -> Aff Unit
18+assertRight eiActual expected =
19+ case eiActual of
20+ Left string -> do
21+ Assert.equal (Right expected) eiActual
22+ Right actual -> do
23+ Assert.equal (Right expected) eiActual
test/Test/IndexBuilder.pursnew file+30
@@ -0,0 +1,30 @@
1+module Test.IndexBuilder where
2+
3+import Prelude
4+
5+import Test.Extra
6+
7+import Docs.Search.IndexBuilder
8+
9+import Data.Either (Either(..))
10+import Data.Foldable (class Foldable)
11+import Data.List (List(..), (:))
12+import Data.List as List
13+import Data.List.NonEmpty (NonEmptyList)
14+import Data.List.NonEmpty as NonEmptyList
15+import Data.Set as Set
16+import Data.Tuple (Tuple(..), snd)
17+import Effect.Aff (Aff)
18+import Test.Unit (TestSuite, suite, test)
19+import Test.Unit.Assert as Assert
20+
21+tests :: TestSuite
22+tests = do
23+ suite "IndexBuilder" do
24+ suite "patchHTML" do
25+ test "works" do
26+ let input = "</body>"
27+ Assert.assertFalse "patchHTML works" (snd (patchHTML input) == input)
28+ test "is idempotent" do
29+ let input = "</body>"
30+ Assert.equal (snd $ patchHTML $ snd $ patchHTML input) (snd $ patchHTML input)
test/Test/TypeQuery.purs+2−18
@@ -2,11 +2,12 @@ module Test.TypeQuery where
22
33import Prelude
44
5+import Test.Extra (assertRight)
6+
57import Docs.Search.TypeQuery (Substitution(..), TypeQuery(..), getFreeVariables, parseTypeQuery, penalty, typeVarPenalty)
68import Docs.Search.TypeShape (ShapeChunk(..), shapeOfTypeQuery)
79import Docs.Search.TypeDecoder (QualifiedName(..), Type(..))
810
9−import Data.Either (Either(..))
1011import Data.Foldable (class Foldable)
1112import Data.List (List(..), (:))
1213import Data.List as List
@@ -14,7 +15,6 @@ import Data.List.NonEmpty (NonEmptyList)
1415import Data.List.NonEmpty as NonEmptyList
1516import Data.Set as Set
1617import Data.Tuple (Tuple(..))
17−import Effect.Aff (Aff)
1818import Test.Unit (TestSuite, suite, test)
1919import Test.Unit.Assert as Assert
2020
@@ -368,21 +368,5 @@ unitType = TypeConstructor (QualifiedName { moduleName: []
368368 , name: "Unit"
369369 })
370370
371−assertRight
372− :: forall a b
373− . Show a
374− => Show b
375− => Eq a
376− => Eq b
377− => Either b a
378− -> a
379− -> Aff Unit
380−assertRight eiActual expected =
381− case eiActual of
382− Left string -> do
383− Assert.equal (Right expected) eiActual
384− Right actual -> do
385− Assert.equal (Right expected) eiActual
386−
387371countFreeVars :: TypeQuery -> Int
388372countFreeVars = getFreeVariables >>> Set.size