@@ -5,6 +5,7 @@ import { qs, escapeHtmlEntities, isBlank, getQueryParamByName, getProjectNameAnd
55import { setSearchInputValue } from './search-bar'
66import searchResultsTemplate from './handlebars/templates/search-results.handlebars'
77import { getSearchNodes } from './globals'
8+ import { highlightMatches } from './highlighter'
89
910const EXCERPT_RADIUS = 80
1011const SEARCH_CONTAINER_SELECTOR = '#search'
@@ -24,7 +25,7 @@ lunr.Pipeline.registerFunction(docTrimmerFunction, 'docTrimmer')
2425window . addEventListener ( 'swup:page:view' , initialize )
2526initialize ( )
2627
27- function initialize ( ) {
28+ function initialize ( ) {
2829 const pathname = window . location . pathname
2930 if ( pathname . endsWith ( '/search.html' ) || pathname . endsWith ( '/search' ) ) {
3031 const query = getQueryParamByName ( 'q' )
@@ -33,7 +34,7 @@ function initialize () {
3334 }
3435}
3536
36- async function search ( value , queryType ) {
37+ async function search ( value , queryType ) {
3738 if ( isBlank ( value ) ) {
3839 renderResults ( { value } )
3940 } else {
@@ -56,7 +57,7 @@ async function search (value, queryType) {
5657 }
5758}
5859
59- async function localSearch ( value ) {
60+ async function localSearch ( value ) {
6061 const index = await getIndex ( )
6162
6263 // We cannot match on atoms :foo because that would be considered
@@ -65,7 +66,7 @@ async function localSearch (value) {
6566 return searchResultsToDecoratedSearchItems ( index . search ( fixedValue ) )
6667}
6768
68- async function remoteSearch ( value , queryType , searchNodes ) {
69+ async function remoteSearch ( value , queryType , searchNodes ) {
6970 let filterNodes = searchNodes
7071
7172 if ( queryType === 'latest' ) {
@@ -86,7 +87,7 @@ async function remoteSearch (value, queryType, searchNodes) {
8687 return payload . hits . map ( result => {
8788 const [ packageName , packageVersion ] = result . document . package . split ( '-' )
8889
89- const doc = result . document . doc
90+ const doc = highlightMatches ( result . document . doc , value , { multiline : true } )
9091 const excerpts = [ doc ]
9192 const metadata = { }
9293 const ref = `https://hexdocs.pm/${ packageName } /${ packageVersion } /${ result . document . ref } `
@@ -107,13 +108,13 @@ async function remoteSearch (value, queryType, searchNodes) {
107108 }
108109}
109110
110- function renderResults ( { value, results, errorMessage } ) {
111+ function renderResults ( { value, results, errorMessage } ) {
111112 const searchContainer = qs ( SEARCH_CONTAINER_SELECTOR )
112113 const resultsHtml = searchResultsTemplate ( { value, results, errorMessage } )
113114 searchContainer . innerHTML = resultsHtml
114115}
115116
116- async function getIndex ( ) {
117+ async function getIndex ( ) {
117118 const cachedIndex = await loadIndex ( )
118119 if ( cachedIndex ) { return cachedIndex }
119120
@@ -122,7 +123,7 @@ async function getIndex () {
122123 return index
123124}
124125
125- async function loadIndex ( ) {
126+ async function loadIndex ( ) {
126127 try {
127128 const serializedIndex = sessionStorage . getItem ( indexStorageKey ( ) )
128129 if ( serializedIndex ) {
@@ -137,7 +138,7 @@ async function loadIndex () {
137138 }
138139}
139140
140- async function saveIndex ( index ) {
141+ async function saveIndex ( index ) {
141142 try {
142143 const serializedIndex = await compress ( index )
143144 sessionStorage . setItem ( indexStorageKey ( ) , serializedIndex )
@@ -146,7 +147,7 @@ async function saveIndex (index) {
146147 }
147148}
148149
149- async function compress ( index ) {
150+ async function compress ( index ) {
150151 const stream = new Blob ( [ JSON . stringify ( index ) ] , {
151152 type : 'application/json'
152153 } ) . stream ( ) . pipeThrough ( new window . CompressionStream ( 'gzip' ) )
@@ -156,7 +157,7 @@ async function compress (index) {
156157 return b64encode ( buffer )
157158}
158159
159- async function decompress ( index ) {
160+ async function decompress ( index ) {
160161 const stream = new Blob ( [ b64decode ( index ) ] , {
161162 type : 'application/json'
162163 } ) . stream ( ) . pipeThrough ( new window . DecompressionStream ( 'gzip' ) )
@@ -165,7 +166,7 @@ async function decompress (index) {
165166 return JSON . parse ( blob )
166167}
167168
168- function b64encode ( buffer ) {
169+ function b64encode ( buffer ) {
169170 let binary = ''
170171 const bytes = new Uint8Array ( buffer )
171172 const len = bytes . byteLength
@@ -175,7 +176,7 @@ function b64encode (buffer) {
175176 return window . btoa ( binary )
176177}
177178
178- function b64decode ( str ) {
179+ function b64decode ( str ) {
179180 const binaryString = window . atob ( str )
180181 const len = binaryString . length
181182 const bytes = new Uint8Array ( new ArrayBuffer ( len ) )
@@ -185,11 +186,11 @@ function b64decode (str) {
185186 return bytes
186187}
187188
188- function indexStorageKey ( ) {
189+ function indexStorageKey ( ) {
189190 return `idv5:${ getProjectNameAndVersion ( ) } `
190191}
191192
192- function createIndex ( ) {
193+ function createIndex ( ) {
193194 return lunr ( function ( ) {
194195 this . ref ( 'ref' )
195196 this . field ( 'title' , { boost : 3 } )
@@ -207,11 +208,11 @@ function createIndex () {
207208 } )
208209}
209210
210- function docTokenSplitter ( builder ) {
211+ function docTokenSplitter ( builder ) {
211212 builder . pipeline . before ( lunr . stemmer , docTokenFunction )
212213}
213214
214- function docTokenFunction ( token ) {
215+ function docTokenFunction ( token ) {
215216 // If we have something with an arity, we split on : . to make partial
216217 // matches easier. We split only when tokenizing, not when searching.
217218 // Below we use ExDoc.Markdown.to_ast/2 as an example.
@@ -275,11 +276,11 @@ function docTokenFunction (token) {
275276 return tokens
276277}
277278
278- function docTrimmer ( builder ) {
279+ function docTrimmer ( builder ) {
279280 builder . pipeline . before ( lunr . stemmer , docTrimmerFunction )
280281}
281282
282- function docTrimmerFunction ( token ) {
283+ function docTrimmerFunction ( token ) {
283284 // Preserve @ and : at the beginning of tokens,
284285 // and ? and ! at the end of tokens. It needs to
285286 // be done before stemming, otherwise search and
@@ -289,7 +290,7 @@ function docTrimmerFunction (token) {
289290 } )
290291}
291292
292- function searchResultsToDecoratedSearchItems ( results ) {
293+ function searchResultsToDecoratedSearchItems ( results ) {
293294 return results
294295 // If the docs are regenerated without changing its version,
295296 // a reference may have been doc'ed false in the code but
@@ -306,11 +307,11 @@ function searchResultsToDecoratedSearchItems (results) {
306307 } )
307308}
308309
309- function getSearchItemByRef ( ref ) {
310+ function getSearchItemByRef ( ref ) {
310311 return searchData . items . find ( searchItem => searchItem . ref === ref ) || null
311312}
312313
313- function getExcerpts ( searchItem , metadata ) {
314+ function getExcerpts ( searchItem , metadata ) {
314315 const { doc } = searchItem
315316 const searchTerms = Object . keys ( metadata )
316317
@@ -331,7 +332,7 @@ function getExcerpts (searchItem, metadata) {
331332 return excerpts . slice ( 0 , 1 )
332333}
333334
334- function excerpt ( doc , sliceStart , sliceLength ) {
335+ function excerpt ( doc , sliceStart , sliceLength ) {
335336 const startPos = Math . max ( sliceStart - EXCERPT_RADIUS , 0 )
336337 const endPos = Math . min ( sliceStart + sliceLength + EXCERPT_RADIUS , doc . length )
337338 return [
0 commit comments