Skip to content

Commit c3309ef

Browse files
committed
Make html rendered by rustdoc allow searching non-English identifier / alias. Fix alias search result showing undefined description.
1 parent 72fdf91 commit c3309ef

File tree

1 file changed

+78
-70
lines changed

1 file changed

+78
-70
lines changed

src/librustdoc/html/static/js/search.js

Lines changed: 78 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,10 @@ const ROOT_PATH = typeof window !== "undefined" ? window.rootPath : "../";
8989
// of permutations we need to check.
9090
const UNBOXING_LIMIT = 5;
9191

92+
// used for search query verification
93+
const REGEX_IDENT = /\p{ID_Start}\p{ID_Continue}*|_\p{ID_Continue}+/uy;
94+
const REGEX_INVALID_TYPE_FILTER = /[^a-z]/ui;
95+
9296
// In the search display, allows to switch between tabs.
9397
function printTab(nb) {
9498
let iter = 0;
@@ -410,18 +414,21 @@ function initSearch(rawSearchIndex) {
410414
}
411415

412416
/**
413-
* Returns `true` if the given `c` character is valid for an ident.
417+
* If the current parser position is at the beginning of an identifier,
418+
* move the position to the end of it and return `true`. Otherwise, return `false`.
414419
*
415-
* @param {string} c
420+
* @param {ParserState} parserState
416421
*
417422
* @return {boolean}
418423
*/
419-
function isIdentCharacter(c) {
420-
return (
421-
c === "_" ||
422-
(c >= "0" && c <= "9") ||
423-
(c >= "a" && c <= "z") ||
424-
(c >= "A" && c <= "Z"));
424+
function consumeIdent(parserState) {
425+
REGEX_IDENT.lastIndex = parserState.pos;
426+
const match = parserState.userQuery.match(REGEX_IDENT);
427+
if (match) {
428+
parserState.pos += match[0].length;
429+
return true;
430+
}
431+
return false;
425432
}
426433

427434
/**
@@ -619,69 +626,61 @@ function initSearch(rawSearchIndex) {
619626
*/
620627
function getIdentEndPosition(parserState) {
621628
const start = parserState.pos;
629+
let afterIdent = consumeIdent(parserState);
622630
let end = parserState.pos;
623-
let foundExclamation = -1;
631+
let macroExclamation = -1;
624632
while (parserState.pos < parserState.length) {
625633
const c = parserState.userQuery[parserState.pos];
626-
if (!isIdentCharacter(c)) {
627-
if (c === "!") {
628-
if (foundExclamation !== -1) {
629-
throw ["Cannot have more than one ", "!", " in an ident"];
630-
} else if (parserState.pos + 1 < parserState.length &&
631-
isIdentCharacter(parserState.userQuery[parserState.pos + 1])
632-
) {
634+
if (c === "!") {
635+
if (macroExclamation !== -1) {
636+
throw ["Cannot have more than one ", "!", " in an ident"];
637+
} else if (parserState.pos + 1 < parserState.length) {
638+
const pos = parserState.pos;
639+
parserState.pos++;
640+
const beforeIdent = consumeIdent(parserState);
641+
parserState.pos = pos;
642+
if (beforeIdent) {
633643
throw ["Unexpected ", "!", ": it can only be at the end of an ident"];
634644
}
635-
foundExclamation = parserState.pos;
636-
} else if (isPathSeparator(c)) {
637-
if (c === ":") {
638-
if (!isPathStart(parserState)) {
645+
}
646+
if (afterIdent) macroExclamation = parserState.pos;
647+
} else if (isPathSeparator(c)) {
648+
if (c === ":") {
649+
if (!isPathStart(parserState)) {
650+
break;
651+
}
652+
// Skip current ":".
653+
parserState.pos += 1;
654+
} else {
655+
while (parserState.pos + 1 < parserState.length) {
656+
const next_c = parserState.userQuery[parserState.pos + 1];
657+
if (next_c !== " ") {
639658
break;
640659
}
641-
// Skip current ":".
642660
parserState.pos += 1;
643-
} else {
644-
while (parserState.pos + 1 < parserState.length) {
645-
const next_c = parserState.userQuery[parserState.pos + 1];
646-
if (next_c !== " ") {
647-
break;
648-
}
649-
parserState.pos += 1;
650-
}
651661
}
652-
if (foundExclamation !== -1) {
653-
if (foundExclamation !== start &&
654-
isIdentCharacter(parserState.userQuery[foundExclamation - 1])
655-
) {
656-
throw ["Cannot have associated items in macros"];
657-
} else {
658-
// while the never type has no associated macros, we still
659-
// can parse a path like that
660-
foundExclamation = -1;
661-
}
662-
}
663-
} else if (
664-
c === "[" ||
665-
c === "(" ||
666-
isEndCharacter(c) ||
667-
isSpecialStartCharacter(c) ||
668-
isSeparatorCharacter(c)
669-
) {
670-
break;
671-
} else if (parserState.pos > 0) {
672-
throw ["Unexpected ", c, " after ", parserState.userQuery[parserState.pos - 1]];
673-
} else {
674-
throw ["Unexpected ", c];
675662
}
663+
if (macroExclamation !== -1) {
664+
throw ["Cannot have associated items in macros"];
665+
}
666+
} else if (
667+
c === "[" ||
668+
c === "(" ||
669+
isEndCharacter(c) ||
670+
isSpecialStartCharacter(c) ||
671+
isSeparatorCharacter(c)
672+
) {
673+
break;
674+
} else if (parserState.pos > 0) {
675+
throw ["Unexpected ", c, " after ", parserState.userQuery[parserState.pos - 1]];
676+
} else {
677+
throw ["Unexpected ", c];
676678
}
677679
parserState.pos += 1;
680+
afterIdent = consumeIdent(parserState);
678681
end = parserState.pos;
679682
}
680-
// if start == end - 1, we got the never type
681-
if (foundExclamation !== -1 &&
682-
foundExclamation !== start &&
683-
isIdentCharacter(parserState.userQuery[foundExclamation - 1])
684-
) {
683+
if (macroExclamation !== -1) {
685684
if (parserState.typeFilter === null) {
686685
parserState.typeFilter = "macro";
687686
} else if (parserState.typeFilter !== "macro") {
@@ -693,7 +692,7 @@ function initSearch(rawSearchIndex) {
693692
" both specified",
694693
];
695694
}
696-
end = foundExclamation;
695+
end = macroExclamation;
697696
}
698697
return end;
699698
}
@@ -1071,16 +1070,15 @@ function initSearch(rawSearchIndex) {
10711070
function checkExtraTypeFilterCharacters(start, parserState) {
10721071
const query = parserState.userQuery.slice(start, parserState.pos).trim();
10731072

1074-
for (const c in query) {
1075-
if (!isIdentCharacter(query[c])) {
1076-
throw [
1077-
"Unexpected ",
1078-
query[c],
1079-
" in type filter (before ",
1080-
":",
1081-
")",
1082-
];
1083-
}
1073+
const match = query.match(REGEX_INVALID_TYPE_FILTER);
1074+
if (match) {
1075+
throw [
1076+
"Unexpected ",
1077+
match[0],
1078+
" in type filter (before ",
1079+
":",
1080+
")",
1081+
];
10841082
}
10851083
}
10861084

@@ -2127,7 +2125,7 @@ function initSearch(rawSearchIndex) {
21272125
};
21282126
}
21292127

2130-
function handleAliases(ret, query, filterCrates, currentCrate) {
2128+
async function handleAliases(ret, query, filterCrates, currentCrate) {
21312129
const lowerQuery = query.toLowerCase();
21322130
// We separate aliases and crate aliases because we want to have current crate
21332131
// aliases to be before the others in the displayed results.
@@ -2163,6 +2161,14 @@ function initSearch(rawSearchIndex) {
21632161
crateAliases.sort(sortFunc);
21642162
aliases.sort(sortFunc);
21652163

2164+
const fetchDesc = alias => {
2165+
return searchIndexEmptyDesc.get(alias.crate).contains(alias.bitIndex) ? "" : searchState.loadDesc(alias);
2166+
};
2167+
const [crateDescs, descs] = await Promise.all([
2168+
Promise.all(crateAliases.map(fetchDesc)),
2169+
Promise.all(aliases.map(fetchDesc))
2170+
]);
2171+
21662172
const pushFunc = alias => {
21672173
alias.alias = query;
21682174
const res = buildHrefAndPath(alias);
@@ -2176,7 +2182,9 @@ function initSearch(rawSearchIndex) {
21762182
}
21772183
};
21782184

2185+
aliases.forEach((alias, i) => alias.desc = descs[i]);
21792186
aliases.forEach(pushFunc);
2187+
crateAliases.forEach((alias, i) => alias.desc = crateDescs[i]);
21802188
crateAliases.forEach(pushFunc);
21812189
}
21822190

@@ -2538,7 +2546,7 @@ function initSearch(rawSearchIndex) {
25382546
sorted_returned,
25392547
sorted_others,
25402548
parsedQuery);
2541-
handleAliases(ret, parsedQuery.original.replace(/"/g, ""), filterCrates, currentCrate);
2549+
await handleAliases(ret, parsedQuery.original.replace(/"/g, ""), filterCrates, currentCrate);
25422550
await Promise.all([ret.others, ret.returned, ret.in_args].map(async list => {
25432551
const descs = await Promise.all(list.map(result => {
25442552
return searchIndexEmptyDesc.get(result.crate).contains(result.bitIndex) ?

0 commit comments

Comments
 (0)