@@ -132,17 +132,12 @@ window.initSearch = function(rawSearchIndex) {
132132 return "(<\"" . indexOf ( c ) !== - 1 ;
133133 }
134134
135- function isStopCharacter ( c ) {
136- return isWhitespace ( c ) || "),>-= " . indexOf ( c ) !== - 1 ;
135+ function isEndCharacter ( c ) {
136+ return "),>-" . indexOf ( c ) !== - 1 ;
137137 }
138138
139- function removeEmptyStringsFromArray ( arr ) {
140- for ( var i = 0 , len = arr . length ; i < len ; ++ i ) {
141- if ( arr [ i ] === "" ) {
142- arr . splice ( i , 1 ) ;
143- i -= 1 ;
144- }
145- }
139+ function isStopCharacter ( c ) {
140+ return isWhitespace ( c ) || isEndCharacter ( c ) ;
146141 }
147142
148143 function itemTypeFromName ( typename ) {
@@ -151,7 +146,8 @@ window.initSearch = function(rawSearchIndex) {
151146 return i ;
152147 }
153148 }
154- return NO_TYPE_FILTER ;
149+
150+ throw new Error ( "Unknown type filter `" + typename + "`" ) ;
155151 }
156152
157153 /**
@@ -189,22 +185,6 @@ window.initSearch = function(rawSearchIndex) {
189185 query . literalSearch = true ;
190186 }
191187
192- /**
193- * Increase the parser position as long as the character is a whitespace. This check is
194- * performed with the `isWhitespace` function.
195- *
196- * @param {ParserState } parserState
197- */
198- function skipWhitespaces ( parserState ) {
199- while ( parserState . pos < parserState . length ) {
200- var c = parserState . userQuery [ parserState . pos ] ;
201- if ( ! isWhitespace ( c ) ) {
202- break ;
203- }
204- parserState . pos += 1 ;
205- }
206- }
207-
208188 /**
209189 * Returns `true` if the current parser position is starting with "::".
210190 *
@@ -233,15 +213,27 @@ window.initSearch = function(rawSearchIndex) {
233213 * @param {Array<QueryElement> } generics - List of generics of this query element.
234214 */
235215 function createQueryElement ( query , parserState , elems , name , generics ) {
236- removeEmptyStringsFromArray ( generics ) ;
237216 if ( name === '*' || ( name . length === 0 && generics . length === 0 ) ) {
238217 return ;
239218 }
240219 if ( query . literalSearch && parserState . totalElems > 0 ) {
241220 throw new Error ( "You cannot have more than one element if you use quotes" ) ;
242221 }
243222 var pathSegments = name . split ( "::" ) ;
244- removeEmptyStringsFromArray ( pathSegments ) ;
223+ if ( pathSegments . length > 1 ) {
224+ for ( var i = 0 , len = pathSegments . length ; i < len ; ++ i ) {
225+ var pathSegment = pathSegments [ i ] ;
226+
227+ if ( pathSegment . length === 0 ) {
228+ if ( i === 0 ) {
229+ throw new Error ( "Paths cannot start with `::`" ) ;
230+ } else if ( i + 1 === len ) {
231+ throw new Error ( "Paths cannot end with `::`" ) ;
232+ }
233+ throw new Error ( "Unexpected `::::`" ) ;
234+ }
235+ }
236+ }
245237 // In case we only have something like `<p>`, there is no name but it remains valid.
246238 if ( pathSegments . length === 0 ) {
247239 pathSegments = [ "" ] ;
@@ -272,7 +264,6 @@ window.initSearch = function(rawSearchIndex) {
272264 start += 1 ;
273265 getStringElem ( query , parserState , isInGenerics ) ;
274266 end = parserState . pos - 1 ;
275- skipWhitespaces ( parserState ) ;
276267 } else {
277268 while ( parserState . pos < parserState . length ) {
278269 var c = parserState . userQuery [ parserState . pos ] ;
@@ -289,7 +280,6 @@ window.initSearch = function(rawSearchIndex) {
289280 }
290281 parserState . pos += 1 ;
291282 end = parserState . pos ;
292- skipWhitespaces ( parserState ) ;
293283 }
294284 }
295285 if ( parserState . pos < parserState . length &&
@@ -317,22 +307,36 @@ window.initSearch = function(rawSearchIndex) {
317307 * character.
318308 */
319309 function getItemsBefore ( query , parserState , elems , limit ) {
310+ var turns = 0 ;
320311 while ( parserState . pos < parserState . length ) {
321312 var c = parserState . userQuery [ parserState . pos ] ;
322313 if ( c === limit ) {
323314 break ;
324- } else if ( c === '(' || c === ":" ) {
325- // Something weird is going on in here. Ignoring it!
315+ } else if ( c === "," && limit !== "" && turns > 0 ) {
326316 parserState . pos += 1 ;
327317 continue ;
318+ } else if ( c === ":" && isPathStart ( parserState ) ) {
319+ throw new Error ( "Unexpected `::`: paths cannot start with `::`" ) ;
320+ } else if ( c === "(" || c === ":" || isEndCharacter ( c ) ) {
321+ var extra = "" ;
322+ if ( limit === ">" ) {
323+ extra = "`<`" ;
324+ } else if ( limit === ")" ) {
325+ extra = "`(`" ;
326+ } else if ( limit === "" ) {
327+ extra = "`->`" ;
328+ }
329+ throw new Error ( "Unexpected `" + c + "` after " + extra ) ;
328330 }
329331 var posBefore = parserState . pos ;
330332 getNextElem ( query , parserState , elems , limit === ">" ) ;
333+ turns += 1 ;
331334 if ( posBefore === parserState . pos ) {
332335 parserState . pos += 1 ;
333336 }
334337 }
335- // We skip the "limit".
338+ // We are either at the end of the string or on the "limit" character, let's move forward
339+ // in any case.
336340 parserState . pos += 1 ;
337341 }
338342
@@ -356,9 +360,13 @@ window.initSearch = function(rawSearchIndex) {
356360 break ;
357361 } else if ( c === ":" &&
358362 parserState . typeFilter === null &&
359- ! isPathStart ( parserState ) &&
360- query . elems . length === 1 )
363+ ! isPathStart ( parserState ) )
361364 {
365+ if ( query . elems . length === 0 ) {
366+ throw new Error ( "Expected type filter before `:`" ) ;
367+ } else if ( query . elems . length !== 1 || parserState . totalElems !== 1 ) {
368+ throw new Error ( "Unexpected `:`" ) ;
369+ }
362370 if ( query . literalSearch ) {
363371 throw new Error ( "You cannot use quotes on type filter" ) ;
364372 }
@@ -531,6 +539,10 @@ window.initSearch = function(rawSearchIndex) {
531539
532540 try {
533541 parseInput ( query , parserState ) ;
542+ if ( parserState . typeFilter !== null ) {
543+ var typeFilter = parserState . typeFilter . replace ( / ^ c o n s t $ / , "constant" ) ;
544+ query . typeFilter = itemTypeFromName ( typeFilter ) ;
545+ }
534546 } catch ( err ) {
535547 query = newParsedQuery ( userQuery ) ;
536548 query . error = err . message ;
@@ -548,10 +560,6 @@ window.initSearch = function(rawSearchIndex) {
548560 createQueryElement ( query , parserState , query . elems , userQuery , [ ] ) ;
549561 query . foundElems += 1 ;
550562 }
551- if ( parserState . typeFilter !== null ) {
552- var typeFilter = parserState . typeFilter . replace ( / ^ c o n s t $ / , "constant" ) ;
553- query . typeFilter = itemTypeFromName ( typeFilter ) ;
554- }
555563 return query ;
556564 }
557565
@@ -582,9 +590,6 @@ window.initSearch = function(rawSearchIndex) {
582590 * @return {ResultsTable }
583591 */
584592 function execQuery ( parsedQuery , searchWords , filterCrates ) {
585- if ( parsedQuery . error !== null ) {
586- createQueryResults ( [ ] , [ ] , [ ] , parsedQuery ) ;
587- }
588593 var results_others = { } , results_in_args = { } , results_returned = { } ;
589594
590595 function transformResults ( results ) {
@@ -1267,14 +1272,21 @@ window.initSearch = function(rawSearchIndex) {
12671272 }
12681273 }
12691274 }
1270- innerRunQuery ( ) ;
1275+
1276+ if ( parsedQuery . error === null ) {
1277+ innerRunQuery ( ) ;
1278+ }
12711279
12721280 var ret = createQueryResults (
12731281 sortResults ( results_in_args , true ) ,
12741282 sortResults ( results_returned , true ) ,
12751283 sortResults ( results_others , false ) ,
12761284 parsedQuery ) ;
12771285 handleAliases ( ret , parsedQuery . original . replace ( / " / g, "" ) , filterCrates ) ;
1286+ if ( parsedQuery . error !== null && ret . others . length !== 0 ) {
1287+ // It means some doc aliases were found so let's "remove" the error!
1288+ ret . query . error = null ;
1289+ }
12781290 return ret ;
12791291 }
12801292
@@ -1413,7 +1425,7 @@ window.initSearch = function(rawSearchIndex) {
14131425
14141426 var output = document . createElement ( "div" ) ;
14151427 var length = 0 ;
1416- if ( array . length > 0 && query . error === null ) {
1428+ if ( array . length > 0 ) {
14171429 output . className = "search-results " + extraClass ;
14181430
14191431 array . forEach ( function ( item ) {
@@ -1466,10 +1478,7 @@ window.initSearch = function(rawSearchIndex) {
14661478 link . appendChild ( wrapper ) ;
14671479 output . appendChild ( link ) ;
14681480 } ) ;
1469- } else if ( query . error !== null ) {
1470- output . className = "search-failed" + extraClass ;
1471- output . innerHTML = "Syntax error: " + query . error ;
1472- } else {
1481+ } else if ( query . error === null ) {
14731482 output . className = "search-failed" + extraClass ;
14741483 output . innerHTML = "No results :(<br/>" +
14751484 "Try on <a href=\"https://duckduckgo.com/?q=" +
@@ -1552,15 +1561,19 @@ window.initSearch = function(rawSearchIndex) {
15521561 }
15531562 crates += `</select>` ;
15541563 }
1564+
15551565 var typeFilter = "" ;
15561566 if ( results . query . typeFilter !== NO_TYPE_FILTER ) {
1557- typeFilter = " (type: " + escape ( results . query . typeFilter ) + ")" ;
1567+ typeFilter = " (type: " + escape ( itemTypes [ results . query . typeFilter ] ) + ")" ;
15581568 }
15591569
15601570 var output = `<div id="search-settings">` +
15611571 `<h1 class="search-results-title">Results for ${ escape ( results . query . userQuery ) } ` +
1562- `${ typeFilter } </h1> in ${ crates } </div>` +
1563- `<div id="titles">` +
1572+ `${ typeFilter } </h1> in ${ crates } </div>` ;
1573+ if ( results . query . error !== null ) {
1574+ output += `<h3>Query parser error: "${ results . query . error } ".</h3>` ;
1575+ }
1576+ output += `<div id="titles">` +
15641577 makeTabHeader ( 0 , "In Names" , ret_others [ 1 ] ) +
15651578 makeTabHeader ( 1 , "In Parameters" , ret_in_args [ 1 ] ) +
15661579 makeTabHeader ( 2 , "In Return Types" , ret_returned [ 1 ] ) +
0 commit comments