git.iko.soy/rowtype-yoga/purescript-docs-search
on master help sign in
added: class members and data constructors to search results
commit0f3f5665d644de0e5ab7e55d2dff06ac34a4a49a authorklntsky <klntsky@gmail.com> dateTue 25 Jun 2019, 15:10 (7 years ago) parent4a5d3b8
3 files changed+247−73
src/Spago/Search/App/SearchResults.purs+1−8 src/Spago/Search/Declarations.purs+104−4 src/Spago/Search/Index.purs+142−61
src/Spago/Search/App/SearchResults.purs+1−8
@@ -164,14 +164,7 @@ renderResult = unwrap >>> \result ->
164164 , HE.onClick $ const $ Just $ SearchResultClicked result.moduleName
165165 , HP.href $
166166 result.moduleName <> ".html#" <>
167−
168− ( case result.declType of
169− "value" -> "v"
170− "alias" -> "v"
171− _ -> "t"
172− ) <>
173−
174− ":" <> result.name
167+ result.hashAnchor <> ":" <> result.name
175168 ]
176169 [ HH.text result.name ]
177170 ]
src/Spago/Search/Declarations.purs+104−4
@@ -3,26 +3,96 @@ module Spago.Search.Declarations where
33import Prelude
44
55import Control.Promise (Promise, toAffE)
6−import Data.Argonaut.Core (Json)
6+import Data.Argonaut.Core (Json, fromString, stringify, toString)
77import Data.Argonaut.Decode (class DecodeJson, decodeJson, (.:), (.:?))
88import Data.Argonaut.Encode (class EncodeJson, encodeJson)
99import Data.Either (Either(..))
1010import Data.Generic.Rep (class Generic)
1111import Data.Generic.Rep.Show (genericShow)
12−import Data.Maybe (Maybe)
12+import Data.Maybe (Maybe(..))
1313import Data.Newtype (class Newtype, unwrap)
1414import Effect (Effect)
1515import Effect.Aff (Aff)
1616
17+-- See `src/Language/Purescript/Docs/Types.hs`
18+data DeclType
19+ = DeclValue
20+ | DeclData
21+ | DeclExternData
22+ | DeclTypeSynonym
23+ | DeclTypeClass
24+ | DeclAlias
25+ | DeclExternKind
26+
27+derive instance eqDeclType :: Eq DeclType
28+derive instance genericDeclType :: Generic DeclType _
29+
30+instance showDeclType :: Show DeclType where
31+ show = genericShow
32+
33+instance encodeJsonDeclType :: EncodeJson DeclType where
34+ encodeJson = fromString <<< case _ of
35+ DeclValue -> "value"
36+ DeclData -> "data"
37+ DeclExternData -> "externData"
38+ DeclTypeSynonym -> "typeSynonym"
39+ DeclTypeClass -> "typeClass"
40+ DeclAlias -> "alias"
41+ DeclExternKind -> "kind"
42+
43+instance decodeJsonDeclType :: DecodeJson DeclType where
44+ decodeJson json =
45+ case toString json of
46+ Just string ->
47+ case string of
48+ "value" -> Right DeclValue
49+ "data" -> Right DeclData
50+ "externData" -> Right DeclExternData
51+ "typeSynonym" -> Right DeclTypeSynonym
52+ "typeClass" -> Right DeclTypeClass
53+ "alias" -> Right DeclAlias
54+ "kind" -> Right DeclExternKind
55+ _ -> Left $ "Couldn't decode DeclType: " <> string
56+ Nothing -> Left $ "Couldn't decode DeclType: " <> stringify json
57+
58+data ChildDeclType
59+ = ChildDeclInstance
60+ | ChildDeclDataConstructor
61+ | ChildDeclTypeClassMember
62+
63+derive instance eqChildDeclType :: Eq ChildDeclType
64+derive instance genericChildDeclType :: Generic ChildDeclType _
65+
66+instance showChildDeclType :: Show ChildDeclType where
67+ show = genericShow
68+
69+instance encodeJsonChildDeclType :: EncodeJson ChildDeclType where
70+ encodeJson = fromString <<< case _ of
71+ ChildDeclInstance -> "instance"
72+ ChildDeclDataConstructor -> "dataConstructor"
73+ ChildDeclTypeClassMember -> "typeClassMember"
74+
75+instance decodeJsonChildDeclType :: DecodeJson ChildDeclType where
76+ decodeJson json =
77+ case toString json of
78+ Just string ->
79+ case string of
80+ "instance" -> Right ChildDeclInstance
81+ "dataConstructor" -> Right ChildDeclDataConstructor
82+ "typeClassMember" -> Right ChildDeclTypeClassMember
83+ _ -> Left $ "Couldn't decode ChildDeclType: " <> string
84+ Nothing -> Left $ "Couldn't decode ChildDeclType: " <> stringify json
85+
1786newtype IndexEntry
1887 = IndexEntry { title :: String
1988 , comments :: Maybe String
20− , info :: { declType :: String
89+ , info :: { declType :: DeclType
2190 }
2291 , sourceSpan :: { start :: Array Int
2392 , end :: Array Int
2493 , name :: String
2594 }
95+ , children :: Array ChildIndexEntry
2696 }
2797
2898derive instance eqIndexEntry :: Eq IndexEntry
@@ -37,13 +107,43 @@ instance decodeJsonIndexEntry :: DecodeJson IndexEntry where
37107 handle <- decodeJson json
38108 title <- handle .: "title"
39109 comments <- handle .:? "comments"
110+ children <- handle .: "children"
40111 info <- handle .: "info"
41112 sourceSpan <- handle .: "sourceSpan"
42− pure { title, comments, info, sourceSpan }
113+ pure { title, comments, info, sourceSpan, children }
43114
44115instance encodeJsonIndexEntry :: EncodeJson IndexEntry where
45116 encodeJson = encodeJson <<< unwrap
46117
118+newtype ChildIndexEntry
119+ = ChildIndexEntry { title :: String
120+ , comments :: Maybe String
121+ , info :: { declType :: ChildDeclType
122+ }
123+ , mbSourceSpan :: Maybe { start :: Array Int
124+ , end :: Array Int
125+ , name :: String
126+ }
127+ }
128+
129+derive instance eqChildIndexEntry :: Eq ChildIndexEntry
130+derive instance genericChildIndexEntry :: Generic ChildIndexEntry _
131+derive instance newtypeChildIndexEntry :: Newtype ChildIndexEntry _
132+instance showChildIndexEntry :: Show ChildIndexEntry where
133+ show = genericShow
134+
135+instance decodeJsonChildIndexEntry :: DecodeJson ChildIndexEntry where
136+ decodeJson json = ChildIndexEntry <$> do
137+ handle <- decodeJson json
138+ title <- handle .: "title"
139+ comments <- handle .:? "comments"
140+ info <- handle .: "info"
141+ mbSourceSpan <- handle .:? "sourceSpan"
142+ pure { title, comments, info, mbSourceSpan }
143+
144+instance encodeJsonChildIndexEntry :: EncodeJson ChildIndexEntry where
145+ encodeJson = encodeJson <<< unwrap
146+
47147newtype Declarations
48148 = Declarations { name :: String
49149 , declarations :: Array IndexEntry
src/Spago/Search/Index.purs+142−61
@@ -2,19 +2,20 @@ module Spago.Search.Index where
22
33import Prelude
44
5−import Spago.Search.Declarations (Declarations(..), IndexEntry(..))
5+import Spago.Search.Declarations (ChildDeclType(..), ChildIndexEntry, DeclType(..), Declarations(..), IndexEntry(..))
66
7−import Data.Foldable (foldr)
87import Data.Array ((!!))
9−import Data.List (List)
8+import Data.Array as Array
9+import Data.Foldable (foldr)
10+import Data.List (List, (:))
1011import Data.List as List
1112import Data.Maybe (Maybe(..), fromMaybe)
12−import Data.String.Common as String
13−import Data.String.Pattern (Pattern(..))
14−import Data.Newtype (class Newtype)
13+import Data.Newtype (class Newtype, unwrap)
1514import Data.Search.Trie (Trie, alter)
16−import Data.String.CodeUnits (toCharArray)
15+import Data.String.CodeUnits (stripPrefix, stripSuffix, toCharArray)
1716import Data.String.Common (toLower)
17+import Data.String.Common as String
18+import Data.String.Pattern (Pattern(..))
1819
1920newtype SearchIndex = SearchIndex (Trie Char (List SearchResult))
2021
@@ -23,7 +24,7 @@ derive instance newtypeSearchIndex :: Newtype SearchIndex _
2324newtype SearchResult
2425 = SearchResult { name :: String
2526 , comments :: Maybe String
26− , declType :: String
27+ , hashAnchor :: String
2728 , moduleName :: String
2829 , packageName :: String
2930 , sourceSpan :: { start :: Array Int
@@ -36,57 +37,137 @@ derive instance newtypeSearchResult :: Newtype SearchResult _
3637
3738mkSearchIndex :: Array Declarations -> SearchIndex
3839mkSearchIndex = SearchIndex <<< foldr insertDeclarations mempty
39− where
40− insertDeclarations (Declarations { name, declarations }) trie
41− = foldr (insertIndexEntry name) trie declarations
42−
43− insertIndexEntry moduleName entry@(IndexEntry { title }) trie
44− = foldr insertSearchResult trie
45− (resultsForEntry moduleName entry)
46−
47− insertSearchResult :: { path :: String
48− , result :: SearchResult
49− } -> Trie Char (List SearchResult) -> Trie Char (List SearchResult)
50− insertSearchResult { path, result } trie =
51− let path' = List.fromFoldable $ toCharArray $ toLower path in
52− alter path' (updateResults result) trie
53−
54− updateResults result mbOldResults =
55− case mbOldResults of
56− Just oldResults ->
57− Just $ List.singleton result <> oldResults
40+
41+insertDeclarations
42+ :: Declarations
43+ -> Trie Char (List SearchResult)
44+ -> Trie Char (List SearchResult)
45+insertDeclarations (Declarations { name, declarations }) trie
46+ = foldr (insertIndexEntry name) trie declarations
47+
48+insertIndexEntry
49+ :: String
50+ -> IndexEntry
51+ -> Trie Char (List SearchResult)
52+ -> Trie Char (List SearchResult)
53+insertIndexEntry moduleName entry@(IndexEntry { title }) trie
54+ = foldr insertSearchResult trie (resultsForEntry moduleName entry)
55+
56+insertSearchResult
57+ :: { path :: String
58+ , result :: SearchResult
59+ }
60+ -> Trie Char (List SearchResult)
61+ -> Trie Char (List SearchResult)
62+insertSearchResult { path, result } trie =
63+ let path' = List.fromFoldable $ toCharArray $ toLower path in
64+ alter path' updateResults trie
65+ where
66+ updateResults mbOldResults =
67+ case mbOldResults of
68+ Just oldResults ->
69+ Just $ result : oldResults
70+ Nothing ->
71+ Just $ List.singleton result
72+
73+resultsForEntry
74+ :: String
75+ -> IndexEntry
76+ -> List { path :: String
77+ , result :: SearchResult
78+ }
79+resultsForEntry moduleName (IndexEntry { title
80+ , comments
81+ , info
82+ , sourceSpan
83+ , children }) =
84+ let { name, hashAnchor } = extractHashAnchor info.declType title
85+ packageName = extractPackageName sourceSpan.name
86+ in
87+ { path: name
88+ , result: SearchResult { name: title
89+ , comments
90+ , hashAnchor
91+ , moduleName
92+ , sourceSpan
93+ , packageName
94+ }
95+ } : (
96+ List.fromFoldable $
97+ extractChildren sourceSpan packageName moduleName children
98+ )
99+
100+extractHashAnchor
101+ :: DeclType
102+ -> String
103+ -> { hashAnchor :: String
104+ , name :: String
105+ }
106+extractHashAnchor DeclValue name = { name, hashAnchor: "v" }
107+extractHashAnchor DeclData name = { name, hashAnchor: "t" }
108+extractHashAnchor DeclTypeSynonym name = { name, hashAnchor: "t" }
109+extractHashAnchor DeclTypeClass name = { name, hashAnchor: "v" }
110+extractHashAnchor DeclAlias title =
111+ case stripPrefix (Pattern "(") title of
112+ Just rest ->
113+ case stripSuffix (Pattern ")") rest of
114+ -- An infix operator
115+ Just name -> { name, hashAnchor: "v" }
116+ -- Impossible
117+ Nothing -> { name: title, hashAnchor: "v" }
118+ Nothing ->
119+ case stripPrefix (Pattern "type (") title of
58120 Nothing ->
59− Just $ List.singleton result
60−
61− resultsForEntry
62− :: String
63− -> IndexEntry
64− -> List { path :: String
65− , result :: SearchResult
66− }
67− resultsForEntry moduleName (IndexEntry { title
68− , comments
69− , info
70− , sourceSpan }) =
71− List.fromFoldable
72− [ { path: title
73− , result: SearchResult { name: title
74− , comments
75− , declType: info.declType
76− , moduleName
77− , sourceSpan
78− , packageName:
79− extractPackageName sourceSpan
80− }
81− }
82− ]
83−
84− extractPackageName { name } =
85− let chunks = String.split (Pattern "/") name in
86− fromMaybe "<unknown>" $
87− chunks !! 0 >>= \dir ->
88− -- TODO: is it safe to assume that directory name is ".spago"?
89− if dir == ".spago" then
90− chunks !! 1
91− else
92− Just "<local package>"
121+ { name: title, hashAnchor: "v" }
122+ -- An infix type operator
123+ Just rest ->
124+ case stripSuffix (Pattern ")") rest of
125+ Just name ->
126+ { name, hashAnchor: "t" }
127+ Nothing ->
128+ { name: title, hashAnchor: "v" }
129+extractHashAnchor DeclExternData name = { name, hashAnchor: "t" }
130+extractHashAnchor DeclExternKind name = { name, hashAnchor: "k" }
131+
132+
133+extractPackageName :: String -> String
134+extractPackageName name =
135+ let chunks = String.split (Pattern "/") name in
136+ fromMaybe "<unknown>" $
137+ chunks !! 0 >>= \dir ->
138+ -- TODO: is it safe to assume that directory name is ".spago"?
139+ if dir == ".spago" then
140+ chunks !! 1
141+ else
142+ Just "<local package>"
143+
144+extractChildren
145+ :: { end :: Array Int
146+ , name :: String
147+ , start :: Array Int
148+ }
149+ -> String
150+ -> String
151+ -> Array ChildIndexEntry
152+ -> Array { path :: String
153+ , result :: SearchResult
154+ }
155+extractChildren sourceSpan' packageName moduleName =
156+ Array.concatMap $ unwrap >>>
157+ \ { title, comments, info, mbSourceSpan } ->
158+
159+ let sourceSpan = fromMaybe sourceSpan' mbSourceSpan in
160+ if info.declType == ChildDeclInstance
161+ then mempty
162+ else
163+ -- `info.declType` is either `ChildDeclDataConstructor` or
164+ -- `ChildDeclTypeClassMember`.
165+ [ { path: title
166+ , result: SearchResult { name: title
167+ , comments
168+ , hashAnchor: "v"
169+ , moduleName
170+ , packageName
171+ , sourceSpan }
172+ }
173+ ]