@@ -132,17 +132,12 @@ window.initSearch = function(rawSearchIndex) {
132
132
return "(<\"" . indexOf ( c ) !== - 1 ;
133
133
}
134
134
135
- function isStopCharacter ( c ) {
136
- return isWhitespace ( c ) || "),>-= " . indexOf ( c ) !== - 1 ;
135
+ function isEndCharacter ( c ) {
136
+ return "),>-" . indexOf ( c ) !== - 1 ;
137
137
}
138
138
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 ) ;
146
141
}
147
142
148
143
function itemTypeFromName ( typename ) {
@@ -151,7 +146,8 @@ window.initSearch = function(rawSearchIndex) {
151
146
return i ;
152
147
}
153
148
}
154
- return NO_TYPE_FILTER ;
149
+
150
+ throw new Error ( "Unknown type filter `" + typename + "`" ) ;
155
151
}
156
152
157
153
/**
@@ -189,22 +185,6 @@ window.initSearch = function(rawSearchIndex) {
189
185
query . literalSearch = true ;
190
186
}
191
187
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
-
208
188
/**
209
189
* Returns `true` if the current parser position is starting with "::".
210
190
*
@@ -233,15 +213,27 @@ window.initSearch = function(rawSearchIndex) {
233
213
* @param {Array<QueryElement> } generics - List of generics of this query element.
234
214
*/
235
215
function createQueryElement ( query , parserState , elems , name , generics ) {
236
- removeEmptyStringsFromArray ( generics ) ;
237
216
if ( name === '*' || ( name . length === 0 && generics . length === 0 ) ) {
238
217
return ;
239
218
}
240
219
if ( query . literalSearch && parserState . totalElems > 0 ) {
241
220
throw new Error ( "You cannot have more than one element if you use quotes" ) ;
242
221
}
243
222
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
+ }
245
237
// In case we only have something like `<p>`, there is no name but it remains valid.
246
238
if ( pathSegments . length === 0 ) {
247
239
pathSegments = [ "" ] ;
@@ -272,7 +264,6 @@ window.initSearch = function(rawSearchIndex) {
272
264
start += 1 ;
273
265
getStringElem ( query , parserState , isInGenerics ) ;
274
266
end = parserState . pos - 1 ;
275
- skipWhitespaces ( parserState ) ;
276
267
} else {
277
268
while ( parserState . pos < parserState . length ) {
278
269
var c = parserState . userQuery [ parserState . pos ] ;
@@ -289,7 +280,6 @@ window.initSearch = function(rawSearchIndex) {
289
280
}
290
281
parserState . pos += 1 ;
291
282
end = parserState . pos ;
292
- skipWhitespaces ( parserState ) ;
293
283
}
294
284
}
295
285
if ( parserState . pos < parserState . length &&
@@ -317,22 +307,36 @@ window.initSearch = function(rawSearchIndex) {
317
307
* character.
318
308
*/
319
309
function getItemsBefore ( query , parserState , elems , limit ) {
310
+ var turns = 0 ;
320
311
while ( parserState . pos < parserState . length ) {
321
312
var c = parserState . userQuery [ parserState . pos ] ;
322
313
if ( c === limit ) {
323
314
break ;
324
- } else if ( c === '(' || c === ":" ) {
325
- // Something weird is going on in here. Ignoring it!
315
+ } else if ( c === "," && limit !== "" && turns > 0 ) {
326
316
parserState . pos += 1 ;
327
317
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 ) ;
328
330
}
329
331
var posBefore = parserState . pos ;
330
332
getNextElem ( query , parserState , elems , limit === ">" ) ;
333
+ turns += 1 ;
331
334
if ( posBefore === parserState . pos ) {
332
335
parserState . pos += 1 ;
333
336
}
334
337
}
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.
336
340
parserState . pos += 1 ;
337
341
}
338
342
@@ -356,9 +360,13 @@ window.initSearch = function(rawSearchIndex) {
356
360
break ;
357
361
} else if ( c === ":" &&
358
362
parserState . typeFilter === null &&
359
- ! isPathStart ( parserState ) &&
360
- query . elems . length === 1 )
363
+ ! isPathStart ( parserState ) )
361
364
{
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
+ }
362
370
if ( query . literalSearch ) {
363
371
throw new Error ( "You cannot use quotes on type filter" ) ;
364
372
}
@@ -531,6 +539,10 @@ window.initSearch = function(rawSearchIndex) {
531
539
532
540
try {
533
541
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
+ }
534
546
} catch ( err ) {
535
547
query = newParsedQuery ( userQuery ) ;
536
548
query . error = err . message ;
@@ -548,10 +560,6 @@ window.initSearch = function(rawSearchIndex) {
548
560
createQueryElement ( query , parserState , query . elems , userQuery , [ ] ) ;
549
561
query . foundElems += 1 ;
550
562
}
551
- if ( parserState . typeFilter !== null ) {
552
- var typeFilter = parserState . typeFilter . replace ( / ^ c o n s t $ / , "constant" ) ;
553
- query . typeFilter = itemTypeFromName ( typeFilter ) ;
554
- }
555
563
return query ;
556
564
}
557
565
@@ -582,9 +590,6 @@ window.initSearch = function(rawSearchIndex) {
582
590
* @return {ResultsTable }
583
591
*/
584
592
function execQuery ( parsedQuery , searchWords , filterCrates ) {
585
- if ( parsedQuery . error !== null ) {
586
- createQueryResults ( [ ] , [ ] , [ ] , parsedQuery ) ;
587
- }
588
593
var results_others = { } , results_in_args = { } , results_returned = { } ;
589
594
590
595
function transformResults ( results ) {
@@ -1267,14 +1272,21 @@ window.initSearch = function(rawSearchIndex) {
1267
1272
}
1268
1273
}
1269
1274
}
1270
- innerRunQuery ( ) ;
1275
+
1276
+ if ( parsedQuery . error === null ) {
1277
+ innerRunQuery ( ) ;
1278
+ }
1271
1279
1272
1280
var ret = createQueryResults (
1273
1281
sortResults ( results_in_args , true ) ,
1274
1282
sortResults ( results_returned , true ) ,
1275
1283
sortResults ( results_others , false ) ,
1276
1284
parsedQuery ) ;
1277
1285
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
+ }
1278
1290
return ret ;
1279
1291
}
1280
1292
@@ -1413,7 +1425,7 @@ window.initSearch = function(rawSearchIndex) {
1413
1425
1414
1426
var output = document . createElement ( "div" ) ;
1415
1427
var length = 0 ;
1416
- if ( array . length > 0 && query . error === null ) {
1428
+ if ( array . length > 0 ) {
1417
1429
output . className = "search-results " + extraClass ;
1418
1430
1419
1431
array . forEach ( function ( item ) {
@@ -1466,10 +1478,7 @@ window.initSearch = function(rawSearchIndex) {
1466
1478
link . appendChild ( wrapper ) ;
1467
1479
output . appendChild ( link ) ;
1468
1480
} ) ;
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 ) {
1473
1482
output . className = "search-failed" + extraClass ;
1474
1483
output . innerHTML = "No results :(<br/>" +
1475
1484
"Try on <a href=\"https://duckduckgo.com/?q=" +
@@ -1552,15 +1561,19 @@ window.initSearch = function(rawSearchIndex) {
1552
1561
}
1553
1562
crates += `</select>` ;
1554
1563
}
1564
+
1555
1565
var typeFilter = "" ;
1556
1566
if ( results . query . typeFilter !== NO_TYPE_FILTER ) {
1557
- typeFilter = " (type: " + escape ( results . query . typeFilter ) + ")" ;
1567
+ typeFilter = " (type: " + escape ( itemTypes [ results . query . typeFilter ] ) + ")" ;
1558
1568
}
1559
1569
1560
1570
var output = `<div id="search-settings">` +
1561
1571
`<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">` +
1564
1577
makeTabHeader ( 0 , "In Names" , ret_others [ 1 ] ) +
1565
1578
makeTabHeader ( 1 , "In Parameters" , ret_in_args [ 1 ] ) +
1566
1579
makeTabHeader ( 2 , "In Return Types" , ret_returned [ 1 ] ) +
0 commit comments