Auto merge of #109802 - notriddle:notriddle/rustdoc-search-generics-nested, r=GuillaumeGomez
rustdoc-search: add support for nested generics This change allows `search.js` to parse nested generics (which look `Like<This<Example>>`) and match them. It maintains the existing "bag semantics", so that the order of type parameters is ignored but the number is required to be greater than or equal to what's in the query. For example, a function with the signature `fn read_all(&mut self: impl Read) -> Result<Vec<u8>, Error>` will match these queries: * `Read -> Result<Vec<u8>, Error>` * `Read -> Result<Error, Vec>` * `Read -> Result<Vec<u8>>` But it *does not* match `Result<Vec, u8>` or `Result<u8<Vec>>`.
This commit is contained in:
commit
3312a3053b
@ -94,6 +94,17 @@ can be matched with the following queries:
|
||||
* `trait:Iterator<primitive:u32> -> primitive:usize`
|
||||
* `Iterator -> usize`
|
||||
|
||||
Generics and function parameters are order-agnostic, but sensitive to nesting
|
||||
and number of matches. For example, a function with the signature
|
||||
`fn read_all(&mut self: impl Read) -> Result<Vec<u8>, Error>`
|
||||
will match these queries:
|
||||
|
||||
* `Read -> Result<Vec<u8>, Error>`
|
||||
* `Read -> Result<Error, Vec>`
|
||||
* `Read -> Result<Vec<u8>>`
|
||||
|
||||
But it *does not* match `Result<Vec, u8>` or `Result<u8<Vec>>`.
|
||||
|
||||
### Changing displayed theme
|
||||
|
||||
You can change the displayed theme by opening the settings menu (the gear
|
||||
|
@ -461,9 +461,7 @@ function initSearch(rawSearchIndex) {
|
||||
if (parserState.pos < parserState.length &&
|
||||
parserState.userQuery[parserState.pos] === "<"
|
||||
) {
|
||||
if (isInGenerics) {
|
||||
throw ["Unexpected ", "<", " after ", "<"];
|
||||
} else if (start >= end) {
|
||||
if (start >= end) {
|
||||
throw ["Found generics without a path"];
|
||||
}
|
||||
parserState.pos += 1;
|
||||
@ -765,13 +763,10 @@ function initSearch(rawSearchIndex) {
|
||||
* ident = *(ALPHA / DIGIT / "_")
|
||||
* path = ident *(DOUBLE-COLON ident) [!]
|
||||
* arg = [type-filter *WS COLON *WS] path [generics]
|
||||
* arg-without-generic = [type-filter *WS COLON *WS] path
|
||||
* type-sep = COMMA/WS *(COMMA/WS)
|
||||
* nonempty-arg-list = *(type-sep) arg *(type-sep arg) *(type-sep)
|
||||
* nonempty-arg-list-without-generics = *(type-sep) arg-without-generic
|
||||
* *(type-sep arg-without-generic) *(type-sep)
|
||||
* generics = OPEN-ANGLE-BRACKET [ nonempty-arg-list-without-generics ] *(type-sep)
|
||||
* CLOSE-ANGLE-BRACKET/EOF
|
||||
* generics = OPEN-ANGLE-BRACKET [ nonempty-arg-list ] *(type-sep)
|
||||
* CLOSE-ANGLE-BRACKET
|
||||
* return-args = RETURN-ARROW *(type-sep) nonempty-arg-list
|
||||
*
|
||||
* exact-search = [type-filter *WS COLON] [ RETURN-ARROW ] *WS QUOTE ident QUOTE [ generics ]
|
||||
@ -1127,7 +1122,7 @@ function initSearch(rawSearchIndex) {
|
||||
currentEntryElems = [];
|
||||
elems.set(entry.name, currentEntryElems);
|
||||
}
|
||||
currentEntryElems.push(entry.ty);
|
||||
currentEntryElems.push(entry);
|
||||
}
|
||||
// We need to find the type that matches the most to remove it in order
|
||||
// to move forward.
|
||||
@ -1136,8 +1131,12 @@ function initSearch(rawSearchIndex) {
|
||||
return false;
|
||||
}
|
||||
const matchElems = elems.get(generic.name);
|
||||
const matchIdx = matchElems.findIndex(tmp_elem =>
|
||||
typePassesFilter(generic.typeFilter, tmp_elem));
|
||||
const matchIdx = matchElems.findIndex(tmp_elem => {
|
||||
if (checkGenerics(tmp_elem, generic, 0, maxEditDistance) !== 0) {
|
||||
return false;
|
||||
}
|
||||
return typePassesFilter(generic.typeFilter, tmp_elem.ty);
|
||||
});
|
||||
if (matchIdx === -1) {
|
||||
return false;
|
||||
}
|
||||
|
@ -1,4 +1,11 @@
|
||||
const QUERY = ['A<B<C<D>, E>', 'p<> u8', '"p"<a>'];
|
||||
const QUERY = [
|
||||
'A<B<C<D>, E>',
|
||||
'p<> u8',
|
||||
'"p"<a>',
|
||||
'p<u<x>>',
|
||||
'p<u<x>, r>',
|
||||
'p<u<x, r>>',
|
||||
];
|
||||
|
||||
const PARSED = [
|
||||
{
|
||||
@ -7,7 +14,7 @@ const PARSED = [
|
||||
original: 'A<B<C<D>, E>',
|
||||
returned: [],
|
||||
userQuery: 'a<b<c<d>, e>',
|
||||
error: 'Unexpected `<` after `<`',
|
||||
error: 'Unclosed `<`',
|
||||
},
|
||||
{
|
||||
elems: [
|
||||
@ -59,4 +66,117 @@ const PARSED = [
|
||||
userQuery: '"p"<a>',
|
||||
error: null,
|
||||
},
|
||||
{
|
||||
elems: [
|
||||
{
|
||||
name: "p",
|
||||
fullPath: ["p"],
|
||||
pathWithoutLast: [],
|
||||
pathLast: "p",
|
||||
generics: [
|
||||
{
|
||||
name: "u",
|
||||
fullPath: ["u"],
|
||||
pathWithoutLast: [],
|
||||
pathLast: "u",
|
||||
generics: [
|
||||
{
|
||||
name: "x",
|
||||
fullPath: ["x"],
|
||||
pathWithoutLast: [],
|
||||
pathLast: "x",
|
||||
generics: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
typeFilter: -1,
|
||||
},
|
||||
],
|
||||
foundElems: 1,
|
||||
original: 'p<u<x>>',
|
||||
returned: [],
|
||||
userQuery: 'p<u<x>>',
|
||||
error: null,
|
||||
},
|
||||
{
|
||||
elems: [
|
||||
{
|
||||
name: "p",
|
||||
fullPath: ["p"],
|
||||
pathWithoutLast: [],
|
||||
pathLast: "p",
|
||||
generics: [
|
||||
{
|
||||
name: "u",
|
||||
fullPath: ["u"],
|
||||
pathWithoutLast: [],
|
||||
pathLast: "u",
|
||||
generics: [
|
||||
{
|
||||
name: "x",
|
||||
fullPath: ["x"],
|
||||
pathWithoutLast: [],
|
||||
pathLast: "x",
|
||||
generics: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "r",
|
||||
fullPath: ["r"],
|
||||
pathWithoutLast: [],
|
||||
pathLast: "r",
|
||||
generics: [],
|
||||
},
|
||||
],
|
||||
typeFilter: -1,
|
||||
},
|
||||
],
|
||||
foundElems: 1,
|
||||
original: 'p<u<x>, r>',
|
||||
returned: [],
|
||||
userQuery: 'p<u<x>, r>',
|
||||
error: null,
|
||||
},
|
||||
{
|
||||
elems: [
|
||||
{
|
||||
name: "p",
|
||||
fullPath: ["p"],
|
||||
pathWithoutLast: [],
|
||||
pathLast: "p",
|
||||
generics: [
|
||||
{
|
||||
name: "u",
|
||||
fullPath: ["u"],
|
||||
pathWithoutLast: [],
|
||||
pathLast: "u",
|
||||
generics: [
|
||||
{
|
||||
name: "x",
|
||||
fullPath: ["x"],
|
||||
pathWithoutLast: [],
|
||||
pathLast: "x",
|
||||
generics: [],
|
||||
},
|
||||
{
|
||||
name: "r",
|
||||
fullPath: ["r"],
|
||||
pathWithoutLast: [],
|
||||
pathLast: "r",
|
||||
generics: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
typeFilter: -1,
|
||||
},
|
||||
],
|
||||
foundElems: 1,
|
||||
original: 'p<u<x, r>>',
|
||||
returned: [],
|
||||
userQuery: 'p<u<x, r>>',
|
||||
error: null,
|
||||
},
|
||||
];
|
||||
|
33
tests/rustdoc-js/generics-nested.js
Normal file
33
tests/rustdoc-js/generics-nested.js
Normal file
@ -0,0 +1,33 @@
|
||||
// exact-check
|
||||
|
||||
const QUERY = [
|
||||
'-> Out<First<Second>>',
|
||||
'-> Out<Second<First>>',
|
||||
'-> Out<First, Second>',
|
||||
'-> Out<Second, First>',
|
||||
];
|
||||
|
||||
const EXPECTED = [
|
||||
{
|
||||
// -> Out<First<Second>>
|
||||
'others': [
|
||||
{ 'path': 'generics_nested', 'name': 'alef' },
|
||||
],
|
||||
},
|
||||
{
|
||||
// -> Out<Second<First>>
|
||||
'others': [],
|
||||
},
|
||||
{
|
||||
// -> Out<First, Second>
|
||||
'others': [
|
||||
{ 'path': 'generics_nested', 'name': 'bet' },
|
||||
],
|
||||
},
|
||||
{
|
||||
// -> Out<Second, First>
|
||||
'others': [
|
||||
{ 'path': 'generics_nested', 'name': 'bet' },
|
||||
],
|
||||
},
|
||||
];
|
19
tests/rustdoc-js/generics-nested.rs
Normal file
19
tests/rustdoc-js/generics-nested.rs
Normal file
@ -0,0 +1,19 @@
|
||||
pub struct Out<A, B = ()> {
|
||||
a: A,
|
||||
b: B,
|
||||
}
|
||||
|
||||
pub struct First<In = ()> {
|
||||
in_: In,
|
||||
}
|
||||
|
||||
pub struct Second;
|
||||
|
||||
// Out<First<Second>>
|
||||
pub fn alef() -> Out<First<Second>> {
|
||||
loop {}
|
||||
}
|
||||
|
||||
pub fn bet() -> Out<First, Second> {
|
||||
loop {}
|
||||
}
|
Loading…
Reference in New Issue
Block a user