1
1
package dotty .tools .scaladoc
2
2
3
+ import scala .concurrent .{ Future , ExecutionContext }
4
+ import concurrent .ExecutionContext .Implicits .global
3
5
import math .Ordering .Implicits .seqOrdering
4
6
import org .scalajs .dom .Node
5
7
@@ -16,24 +18,28 @@ import scala.annotation.tailrec
16
18
* - Optimize.
17
19
*/
18
20
class PageSearchEngine (pages : List [PageEntry ]):
19
- def query (query : NameAndKindQuery ): List [MatchResult ] =
20
- println(" QUERYING: " + query)
21
+
22
+ def query (query : NameAndKindQuery ): Future [List [MatchResult ]] = Future {
23
+ val time = System .currentTimeMillis()
21
24
matchPages(query)
22
25
.filter {
23
26
case MatchResult (score, _, _) => score >= 0
24
27
}
25
28
.sortBy {
26
29
case MatchResult (score, _, _) => - score
27
30
}
31
+ }
28
32
29
33
private def kindScoreBonus (kind : String ): Int = kind match {
30
34
case " class" => 5
31
- case " object" | " enu, " => 4
35
+ case " object" | " enum " => 4
32
36
case " trait" => 3
33
37
case " def" | " val" | " given" | " type" => 1
34
38
case _ => 0
35
39
}
36
40
41
+ private val positionScores = List (8 ,4 ,2 ,1 ).orElse(PartialFunction .fromFunction(_ => 0 ))
42
+
37
43
private def matchCompletnessBonus (nameCharacters : Int , matchCharacters : Int ): Int =
38
44
(matchCharacters * 3 ) / nameCharacters
39
45
@@ -47,53 +53,45 @@ class PageSearchEngine(pages: List[PageEntry]):
47
53
.map(MatchResult (1 , _, Set .empty))
48
54
case NameAndKindQuery (Some (nameSearch), kind) =>
49
55
val kindFiltered = kind.fold(pages)(filterKind(pages, _))
50
- println(" PREMATCHING: " + nameSearch)
51
56
val prematchedPages = prematchPages(kindFiltered, nameSearch)
52
- println(" PREMATCHED " + prematchedPages.length)
53
- var totalMatched = 0
54
57
55
- if nameSearch.length > 2 then
56
- prematchedPages.map( prematched =>
58
+ if nameSearch.length > 1 then
59
+ prematchedPages.map { prematched =>
57
60
val finalMatch = matchPage(prematched, nameSearch)
58
61
val bonusScore = kindScoreBonus(prematched.pageEntry.kind)
59
62
+ matchCompletnessBonus(prematched.pageEntry.shortName.length, nameSearch.length)
60
63
finalMatch.copy(score = finalMatch.score + bonusScore)
61
- )
64
+ }
62
65
else prematchedPages
63
66
64
67
65
68
private def filterKind (pages : List [PageEntry ], kind : String ): List [PageEntry ] =
66
69
pages.filter(_.kind == kind)
67
70
68
71
def prematchPages (pages : List [PageEntry ], search : String ): List [MatchResult ] =
69
- pages.map(page => MatchResult (1 , page, getIndicesOfSearchLetters(page.shortName, search)))
70
- .filter(_.indices.nonEmpty)
72
+ pages.map(prematchPage(_, search)).filter(_.indices.nonEmpty)
71
73
72
- val debuggedTypes = Set (" list" , " lazylist" , " classmanifest" )
73
-
74
- private def getIndicesOfSearchLetters (pageName : String , search : String ): Set [Int ] =
75
- if debuggedTypes.contains(pageName) then println(" Prematching List!" )
74
+ private def prematchPage (page : PageEntry , search : String ): MatchResult =
75
+ val pageName = page.shortName
76
76
@ tailrec
77
- def getIndicesAcc (nameIndex : Int , searchIndex : Int , acc : Set [Int ]) : Set [ Int ] =
77
+ def prematchPageAcc (nameIndex : Int , searchIndex : Int , acc : Set [Int ], scoreAcc : Int , consecutiveMatches : Int ) : MatchResult =
78
78
if searchIndex >= search.length then
79
- if debuggedTypes.contains(pageName) then println(" Matched!" )
80
- acc
79
+ MatchResult (scoreAcc, page, acc)
81
80
else if nameIndex >= pageName.length then
82
- if debuggedTypes.contains(pageName) then println(" Not matched :(" )
83
- Set .empty
81
+ MatchResult (0 , page, Set .empty)
84
82
else if pageName(nameIndex).toLower == search(searchIndex).toLower then
85
- if debuggedTypes.contains(pageName) then println( " Matched name: " + nameIndex + " ( " + pageName(nameIndex ) + " ) with search: " + searchIndex + " ( " + search(searchIndex) + " ) " )
86
- getIndicesAcc (nameIndex + 1 , searchIndex + 1 , acc + nameIndex)
83
+ val score = ( if consecutiveMatches > 0 then 1 else 0 ) + positionScores(nameIndex )
84
+ prematchPageAcc (nameIndex + 1 , searchIndex + 1 , acc + nameIndex, scoreAcc + score, consecutiveMatches + 1 )
87
85
else
88
- if debuggedTypes.contains(pageName) then println(" Not matched: " + nameIndex + " (" + pageName(nameIndex) + " ) with search:" + searchIndex + " (" + search(searchIndex) + " )" )
89
- getIndicesAcc(nameIndex + 1 , searchIndex, acc)
90
- getIndicesAcc(0 , 0 , Set .empty)
86
+ prematchPageAcc(nameIndex + 1 , searchIndex, acc, scoreAcc, 0 )
87
+
88
+ val result = prematchPageAcc(0 , 0 , Set .empty, 0 , 0 )
89
+ result.copy(score = result.score + kindScoreBonus(page.kind))
91
90
92
91
private def matchPage (prematched : MatchResult , nameSearch : String ): MatchResult =
93
92
val searchTokens : List [List [Char ]] = StringUtils .createCamelCaseTokens(nameSearch).map(_.toList) // todo extract
94
93
val pageTokens : List [List [Char ]] = prematched.pageEntry.tokens.map(_.toList)
95
94
val pageName = prematched.pageEntry.shortName
96
- if debuggedTypes.contains(pageName) then println(" Found " + pageName + " !" )
97
95
val searchTokensLifted = searchTokens.lift
98
96
val pageTokensLifted = pageTokens.lift
99
97
@@ -105,7 +103,7 @@ class PageSearchEngine(pages: List[PageEntry]):
105
103
if searchHead == pageHead then
106
104
matchTokens(searchTokenIndex + 1 , pageTokenIndex + 1 , acc + ((searchTokenIndex, pageTokenIndex)))
107
105
else
108
- matchTokens(searchTokenIndex + 1 , pageTokenIndex + 1 , acc)
106
+ matchTokens(searchTokenIndex, pageTokenIndex + 1 , acc)
109
107
// empty tokens edge cases
110
108
case (Some (_), Some (_ :: _)) => matchTokens(searchTokenIndex + 1 , pageTokenIndex, acc)
111
109
case (Some (_ :: _), Some (_)) => matchTokens(searchTokenIndex, pageTokenIndex + 1 , acc)
@@ -115,11 +113,6 @@ class PageSearchEngine(pages: List[PageEntry]):
115
113
val matchedTokens = matchTokens(0 , 0 , Set .empty)
116
114
val searchTokenPositions = searchTokens.map(_.length).scanLeft(0 )(_ + _)
117
115
val pageTokensPositions = pageTokens.map(_.length).scanLeft(0 )(_ + _)
118
- if debuggedTypes.contains(pageName) then
119
- println(" List tokens: " + matchedTokens)
120
- println(" Search: " + searchTokenPositions)
121
- println(" Page: " + pageTokensPositions)
122
-
123
116
124
117
@ tailrec
125
118
def findHighScoreMatch (
@@ -132,39 +125,30 @@ class PageSearchEngine(pages: List[PageEntry]):
132
125
consecutiveMatches : Int
133
126
): Option [MatchResult ] =
134
127
if searchPosition >= nameSearch.length then
135
- if debuggedTypes.contains(pageName) then println(" Matched " + pageName + " with score " + scoreAcc)
136
128
Some (MatchResult (scoreAcc, prematched.pageEntry, positionAcc))
137
129
else if pagePosition >= pageName.length then
138
- if debuggedTypes.contains(pageName) then println(" Failed to match " + pageName + " :(" )
139
130
None
140
131
else
141
- if debuggedTypes.contains(pageName) then
142
- println(" At page: " + pageTokenIndex + " /" + pagePosition + " (" + pageName(pagePosition) + " )" + " ; search: " + searchTokenIndex + " /" + searchPosition + " (" + nameSearch(searchPosition) + " )" )
143
132
val currentSearchTokenStart = searchTokenPositions(searchTokenIndex)
144
- val currentPageTokenStart = pageTokensPositions(pageTokenIndex)
145
- val atSearchTokenBeggining = searchPosition == currentSearchTokenStart
146
- val matchingPageToken = matchedTokens.find(_._1 == currentSearchTokenStart).map(_._2)
133
+ val matchingPageToken = matchedTokens.find(_._1 == searchTokenIndex).map(_._2)
147
134
val searchChar = nameSearch.charAt(searchPosition).toLower
148
135
val pageChar = pageName.charAt(pagePosition).toLower
149
136
150
- def recalculateTokenIndex (tokenPositions : Seq [Int ], lastIndex : Int , position : Int ): Int =
151
- if tokenPositions.length <= lastIndex + 1 || tokenPositions(lastIndex + 1 ) > position then
152
- lastIndex
137
+ def recalculateTokenIndex (tokenPositions : Seq [Int ], previousIndex : Int , position : Int ): Int =
138
+ if tokenPositions.length <= previousIndex + 1 || tokenPositions(previousIndex + 1 ) > position then
139
+ previousIndex
153
140
else
154
- lastIndex + 1
141
+ previousIndex + 1
155
142
156
- val positionScores = List (8 ,4 ,2 ,1 ).orElse(PartialFunction .fromFunction(_ => 0 ))
157
143
def getMatchScore (matchedPagePosition : Int , matchedPageTokenStart : Int ): Int =
158
144
val consecutiveMatchesScore = if consecutiveMatches > 0 then 1 else 0
159
145
val matchPositionScore = positionScores(matchedPagePosition - matchedPageTokenStart)
160
146
val firstTokenScore = if matchPositionScore > 0 && matchedPageTokenStart == 0 then 3 else 0
161
- if debuggedTypes.contains(pageName) then println(" [" + pageName + " ] + score " + (consecutiveMatchesScore + matchPositionScore))
162
147
consecutiveMatchesScore + matchPositionScore + firstTokenScore
163
148
164
149
matchingPageToken match
165
150
case Some (matchingToken) if searchPosition == currentSearchTokenStart =>
166
151
val matchedTokenPosition = pageTokensPositions(matchingToken)
167
- if debuggedTypes.contains(pageName) then println(" Matched tokens! " + matchingToken + " at " + matchedTokenPosition)
168
152
findHighScoreMatch(
169
153
recalculateTokenIndex(searchTokenPositions, searchTokenIndex, searchPosition + 1 ),
170
154
searchPosition + 1 ,
@@ -175,7 +159,6 @@ class PageSearchEngine(pages: List[PageEntry]):
175
159
consecutiveMatches + 1
176
160
)
177
161
case _ if searchChar == pageChar =>
178
- if debuggedTypes.contains(pageName) then println(" Matched char! " + searchChar + " at " + pagePosition)
179
162
val matchedTokenPosition = matchingPageToken.map(pageTokensPositions).getOrElse(0 )
180
163
findHighScoreMatch(
181
164
recalculateTokenIndex(searchTokenPositions, searchTokenIndex, searchPosition + 1 ),
0 commit comments