rustdoc-search: show types signatures in results

This commit is contained in:
Michael Howell 2024-09-24 14:31:44 -07:00
parent 5973005d93
commit 20a4b4fea1
10 changed files with 995 additions and 126 deletions

View File

@ -264,6 +264,7 @@ a.anchor,
.mobile-topbar h2 a, .mobile-topbar h2 a,
h1 a, h1 a,
.search-results a, .search-results a,
.search-results li,
.stab, .stab,
.result-name i { .result-name i {
color: var(--main-color); color: var(--main-color);
@ -379,7 +380,7 @@ details:not(.toggle) summary {
margin-bottom: .6em; margin-bottom: .6em;
} }
code, pre, .code-header { code, pre, .code-header, .type-signature {
font-family: "Source Code Pro", monospace; font-family: "Source Code Pro", monospace;
} }
.docblock code, .docblock-short code { .docblock code, .docblock-short code {
@ -1205,22 +1206,28 @@ so that we can apply CSS-filters to change the arrow color in themes */
.search-results.active { .search-results.active {
display: block; display: block;
margin: 0;
padding: 0;
} }
.search-results > a { .search-results > a {
display: flex; display: grid;
grid-template-areas:
"search-result-name search-result-desc"
"search-result-type-signature search-result-type-signature";
grid-template-columns: .6fr .4fr;
/* A little margin ensures the browser's outlining of focused links has room to display. */ /* A little margin ensures the browser's outlining of focused links has room to display. */
margin-left: 2px; margin-left: 2px;
margin-right: 2px; margin-right: 2px;
border-bottom: 1px solid var(--search-result-border-color); border-bottom: 1px solid var(--search-result-border-color);
gap: 1em; column-gap: 1em;
} }
.search-results > a > div.desc { .search-results > a > div.desc {
white-space: nowrap; white-space: nowrap;
text-overflow: ellipsis; text-overflow: ellipsis;
overflow: hidden; overflow: hidden;
flex: 2; grid-area: search-result-desc;
} }
.search-results a:hover, .search-results a:hover,
@ -1232,7 +1239,7 @@ so that we can apply CSS-filters to change the arrow color in themes */
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: start; justify-content: start;
flex: 3; grid-area: search-result-name;
} }
.search-results .result-name .alias { .search-results .result-name .alias {
color: var(--search-results-alias-color); color: var(--search-results-alias-color);
@ -1253,6 +1260,10 @@ so that we can apply CSS-filters to change the arrow color in themes */
.search-results .result-name .path > * { .search-results .result-name .path > * {
display: inline; display: inline;
} }
.search-results .type-signature {
grid-area: search-result-type-signature;
white-space: pre-wrap;
}
.popover { .popover {
position: absolute; position: absolute;

View File

@ -92,6 +92,9 @@ let Results;
* parent: (Object|undefined), * parent: (Object|undefined),
* path: string, * path: string,
* ty: number, * ty: number,
* type: FunctionSearchType?,
* displayType: Promise<Array<Array<string>>>|null,
* displayTypeMappedNames: Promise<Array<[string, Array<string>]>>|null,
* }} * }}
*/ */
let ResultObject; let ResultObject;

File diff suppressed because it is too large Load Diff

View File

@ -2,6 +2,14 @@
const fs = require("fs"); const fs = require("fs");
const path = require("path"); const path = require("path");
function arrayToCode(array) {
return array.map((value, index) => {
value = value.split("&nbsp;").join(" ");
return (index % 2 === 1) ? ("`" + value + "`") : value;
}).join("");
}
function loadContent(content) { function loadContent(content) {
const Module = module.constructor; const Module = module.constructor;
const m = new Module(); const m = new Module();
@ -180,15 +188,7 @@ function valueCheck(fullPath, expected, result, error_text, queryName) {
if (!result_v.forEach) { if (!result_v.forEach) {
throw result_v; throw result_v;
} }
result_v.forEach((value, index) => { result_v = arrayToCode(result_v);
value = value.split("&nbsp;").join(" ");
if (index % 2 === 1) {
result_v[index] = "`" + value + "`";
} else {
result_v[index] = value;
}
});
result_v = result_v.join("");
} }
const obj_path = fullPath + (fullPath.length > 0 ? "." : "") + key; const obj_path = fullPath + (fullPath.length > 0 ? "." : "") + key;
valueCheck(obj_path, expected[key], result_v, error_text, queryName); valueCheck(obj_path, expected[key], result_v, error_text, queryName);
@ -436,9 +436,41 @@ function loadSearchJS(doc_folder, resource_suffix) {
searchModule.initSearch(searchIndex.searchIndex); searchModule.initSearch(searchIndex.searchIndex);
const docSearch = searchModule.docSearch; const docSearch = searchModule.docSearch;
return { return {
doSearch: function(queryStr, filterCrate, currentCrate) { doSearch: async function(queryStr, filterCrate, currentCrate) {
return docSearch.execQuery(searchModule.parseQuery(queryStr), const result = await docSearch.execQuery(searchModule.parseQuery(queryStr),
filterCrate, currentCrate); filterCrate, currentCrate);
for (const tab in result) {
if (!Object.prototype.hasOwnProperty.call(result, tab)) {
continue;
}
if (!(result[tab] instanceof Array)) {
continue;
}
for (const entry of result[tab]) {
for (const key in entry) {
if (!Object.prototype.hasOwnProperty.call(entry, key)) {
continue;
}
if (key === "displayTypeSignature") {
const {type, mappedNames, whereClause} =
await entry.displayTypeSignature;
entry.displayType = arrayToCode(type);
entry.displayMappedNames = [...mappedNames.entries()]
.map(([name, qname]) => {
return `${name} = ${qname}`;
}).join(", ");
entry.displayWhereClause = [...whereClause.entries()]
.flatMap(([name, value]) => {
if (value.length === 0) {
return [];
}
return [`${name}: ${arrayToCode(value)}`];
}).join(", ");
}
}
}
}
return result;
}, },
getCorrections: function(queryStr, filterCrate, currentCrate) { getCorrections: function(queryStr, filterCrate, currentCrate) {
const parsedQuery = searchModule.parseQuery(queryStr); const parsedQuery = searchModule.parseQuery(queryStr);

View File

@ -0,0 +1,42 @@
// Check the "About this Result" popover.
// Try a complex result.
go-to: "file://" + |DOC_PATH| + "/lib2/index.html?search=scroll_traits::Iterator<T>,(T->bool)->(Extend<T>,Extend<T>)"
// These two commands are used to be sure the search will be run.
focus: ".search-input"
press-key: "Enter"
wait-for: "#search-tabs"
assert-count: ("#search-tabs button", 1)
assert-count: (".search-results > a", 1)
assert: "//div[@class='type-signature']/strong[text()='Iterator']"
assert: "//div[@class='type-signature']/strong[text()='(']"
assert: "//div[@class='type-signature']/strong[text()=')']"
assert: "//div[@class='type-signature']/div[@class='where']/strong[text()='FnMut']"
assert: "//div[@class='type-signature']/div[@class='where']/strong[text()='Iterator::Item']"
assert: "//div[@class='type-signature']/div[@class='where']/strong[text()='bool']"
assert: "//div[@class='type-signature']/div[@class='where']/strong[text()='Extend']"
assert-text: ("div.type-signature div.where:nth-child(4)", "where")
assert-text: ("div.type-signature div.where:nth-child(5)", " T matches Iterator::Item")
assert-text: ("div.type-signature div.where:nth-child(6)", " F: FnMut (&Iterator::Item) -> bool")
assert-text: ("div.type-signature div.where:nth-child(7)", " B: Default + Extend<Iterator::Item>")
// Try a simple result that *won't* give an info box.
go-to: "file://" + |DOC_PATH| + "/lib2/index.html?search=F->lib2::WhereWhitespace<T>"
// These two commands are used to be sure the search will be run.
focus: ".search-input"
press-key: "Enter"
wait-for: "#search-tabs"
assert-text: ("//div[@class='type-signature']", "F -> WhereWhitespace<T>")
assert-count: ("#search-tabs button", 1)
assert-count: (".search-results > a", 1)
assert-count: ("//div[@class='type-signature']/div[@class='where']", 0)
assert: "//div[@class='type-signature']/strong[text()='F']"
assert: "//div[@class='type-signature']/strong[text()='WhereWhitespace']"
assert: "//div[@class='type-signature']/strong[text()='T']"

View File

@ -6,79 +6,217 @@ const EXPECTED = [
{ {
'query': 'option, fnonce -> option', 'query': 'option, fnonce -> option',
'others': [ 'others': [
{ 'path': 'std::option::Option', 'name': 'map' }, {
'path': 'std::option::Option',
'name': 'map',
'displayType': '`Option`<T>, F -> `Option`<U>',
'displayWhereClause': "F: `FnOnce` (T) -> U",
},
],
},
{
'query': 'option<t>, fnonce -> option',
'others': [
{
'path': 'std::option::Option',
'name': 'map',
'displayType': '`Option`<`T`>, F -> `Option`<U>',
'displayWhereClause': "F: `FnOnce` (T) -> U",
},
], ],
}, },
{ {
'query': 'option -> default', 'query': 'option -> default',
'others': [ 'others': [
{ 'path': 'std::option::Option', 'name': 'unwrap_or_default' }, {
{ 'path': 'std::option::Option', 'name': 'get_or_insert_default' }, 'path': 'std::option::Option',
'name': 'unwrap_or_default',
'displayType': '`Option`<T> -> `T`',
'displayWhereClause': "T: `Default`",
},
{
'path': 'std::option::Option',
'name': 'get_or_insert_default',
'displayType': '&mut `Option`<T> -> &mut `T`',
'displayWhereClause': "T: `Default`",
},
], ],
}, },
{ {
'query': 'option -> []', 'query': 'option -> []',
'others': [ 'others': [
{ 'path': 'std::option::Option', 'name': 'as_slice' }, {
{ 'path': 'std::option::Option', 'name': 'as_mut_slice' }, 'path': 'std::option::Option',
'name': 'as_slice',
'displayType': '&`Option`<T> -> &`[`T`]`',
},
{
'path': 'std::option::Option',
'name': 'as_mut_slice',
'displayType': '&mut `Option`<T> -> &mut `[`T`]`',
},
], ],
}, },
{ {
'query': 'option<t>, option<t> -> option<t>', 'query': 'option<t>, option<t> -> option<t>',
'others': [ 'others': [
{ 'path': 'std::option::Option', 'name': 'or' }, {
{ 'path': 'std::option::Option', 'name': 'xor' }, 'path': 'std::option::Option',
'name': 'or',
'displayType': '`Option`<`T`>, `Option`<`T`> -> `Option`<`T`>',
},
{
'path': 'std::option::Option',
'name': 'xor',
'displayType': '`Option`<`T`>, `Option`<`T`> -> `Option`<`T`>',
},
], ],
}, },
{ {
'query': 'option<t>, option<u> -> option<u>', 'query': 'option<t>, option<u> -> option<u>',
'others': [ 'others': [
{ 'path': 'std::option::Option', 'name': 'and' }, {
{ 'path': 'std::option::Option', 'name': 'zip' }, 'path': 'std::option::Option',
'name': 'and',
'displayType': '`Option`<`T`>, `Option`<`U`> -> `Option`<`U`>',
},
{
'path': 'std::option::Option',
'name': 'zip',
'displayType': '`Option`<`T`>, `Option`<`U`> -> `Option`<(T, `U`)>',
},
], ],
}, },
{ {
'query': 'option<t>, option<u> -> option<t>', 'query': 'option<t>, option<u> -> option<t>',
'others': [ 'others': [
{ 'path': 'std::option::Option', 'name': 'and' }, {
{ 'path': 'std::option::Option', 'name': 'zip' }, 'path': 'std::option::Option',
'name': 'and',
'displayType': '`Option`<`T`>, `Option`<`U`> -> `Option`<`U`>',
},
{
'path': 'std::option::Option',
'name': 'zip',
'displayType': '`Option`<`T`>, `Option`<`U`> -> `Option`<(`T`, U)>',
},
], ],
}, },
{ {
'query': 'option<t>, option<u> -> option<t, u>', 'query': 'option<t>, option<u> -> option<t, u>',
'others': [ 'others': [
{ 'path': 'std::option::Option', 'name': 'zip' }, {
'path': 'std::option::Option',
'name': 'zip',
'displayType': '`Option`<`T`>, `Option`<`U`> -> `Option`<(`T`, `U`)>',
},
], ],
}, },
{ {
'query': 'option<t>, e -> result<t, e>', 'query': 'option<t>, e -> result<t, e>',
'others': [ 'others': [
{ 'path': 'std::option::Option', 'name': 'ok_or' }, {
{ 'path': 'std::result::Result', 'name': 'transpose' }, 'path': 'std::option::Option',
'name': 'ok_or',
'displayType': '`Option`<`T`>, `E` -> `Result`<`T`, `E`>',
},
{
'path': 'std::result::Result',
'name': 'transpose',
'displayType': 'Result<`Option`<`T`>, `E`> -> Option<`Result`<`T`, `E`>>',
},
], ],
}, },
{ {
'query': 'result<option<t>, e> -> option<result<t, e>>', 'query': 'result<option<t>, e> -> option<result<t, e>>',
'others': [ 'others': [
{ 'path': 'std::result::Result', 'name': 'transpose' }, {
'path': 'std::result::Result',
'name': 'transpose',
'displayType': '`Result`<`Option`<`T`>, `E`> -> `Option`<`Result`<`T`, `E`>>',
},
], ],
}, },
{ {
'query': 'option<t>, option<t> -> bool', 'query': 'option<t>, option<t> -> bool',
'others': [ 'others': [
{ 'path': 'std::option::Option', 'name': 'eq' }, {
'path': 'std::option::Option',
'name': 'eq',
'displayType': '&`Option`<`T`>, &`Option`<`T`> -> `bool`',
},
], ],
}, },
{ {
'query': 'option<option<t>> -> option<t>', 'query': 'option<option<t>> -> option<t>',
'others': [ 'others': [
{ 'path': 'std::option::Option', 'name': 'flatten' }, {
'path': 'std::option::Option',
'name': 'flatten',
'displayType': '`Option`<`Option`<`T`>> -> `Option`<`T`>',
},
], ],
}, },
{ {
'query': 'option<t>', 'query': 'option<t>',
'returned': [ 'returned': [
{ 'path': 'std::result::Result', 'name': 'ok' }, {
'path': 'std::result::Result',
'name': 'ok',
'displayType': 'Result<T, E> -> `Option`<`T`>',
},
],
},
{
'query': 'option<t>, (fnonce () -> u) -> option',
'others': [
{
'path': 'std::option::Option',
'name': 'map',
'displayType': '`Option`<`T`>, F -> `Option`<U>',
'displayMappedNames': `T = t, U = u`,
'displayWhereClause': "F: `FnOnce` (T) -> `U`",
},
{
'path': 'std::option::Option',
'name': 'and_then',
'displayType': '`Option`<`T`>, F -> `Option`<U>',
'displayMappedNames': `T = t, U = u`,
'displayWhereClause': "F: `FnOnce` (T) -> Option<`U`>",
},
{
'path': 'std::option::Option',
'name': 'zip_with',
'displayType': 'Option<T>, `Option`<`U`>, F -> `Option`<R>',
'displayMappedNames': `U = t, R = u`,
'displayWhereClause': "F: `FnOnce` (T, U) -> `R`",
},
{
'path': 'std::task::Poll',
'name': 'map_ok',
'displayType': 'Poll<`Option`<Result<`T`, E>>>, F -> Poll<`Option`<Result<U, E>>>',
'displayMappedNames': `T = t, U = u`,
'displayWhereClause': "F: `FnOnce` (T) -> `U`",
},
{
'path': 'std::task::Poll',
'name': 'map_err',
'displayType': 'Poll<`Option`<Result<`T`, E>>>, F -> Poll<`Option`<Result<T, U>>>',
'displayMappedNames': `T = t, U = u`,
'displayWhereClause': "F: `FnOnce` (E) -> `U`",
},
],
},
{
'query': 'option<t>, (fnonce () -> option<u>) -> option',
'others': [
{
'path': 'std::option::Option',
'name': 'and_then',
'displayType': '`Option`<`T`>, F -> `Option`<U>',
'displayMappedNames': `T = t, U = u`,
'displayWhereClause': "F: `FnOnce` (T) -> `Option`<`U`>",
},
], ],
}, },
]; ];

View File

@ -0,0 +1,39 @@
// exact-check
const EXPECTED = [
// Trait-associated types (that is, associated types with no constraints)
// are treated like type parameters, so that you can "pattern match"
// them. We should avoid redundant output (no `Item=MyIter::Item` stuff)
// and should give reasonable results
{
'query': 'MyIter<T> -> Option<T>',
'correction': null,
'others': [
{
'path': 'assoc_type_unbound::MyIter',
'name': 'next',
'displayType': '&mut `MyIter` -> `Option`<`MyIter::Item`>',
'displayMappedNames': 'MyIter::Item = T',
'displayWhereClause': '',
},
],
},
{
'query': 'MyIter<Item=T> -> Option<T>',
'correction': null,
'others': [
{
'path': 'assoc_type_unbound::MyIter',
'name': 'next',
'displayType': '&mut `MyIter` -> `Option`<`MyIter::Item`>',
'displayMappedNames': 'MyIter::Item = T',
'displayWhereClause': '',
},
],
},
{
'query': 'MyIter<T> -> Option<Item=T>',
'correction': null,
'others': [],
},
];

View File

@ -0,0 +1,4 @@
pub trait MyIter {
type Item;
fn next(&mut self) -> Option<Self::Item>;
}

View File

@ -7,16 +7,40 @@ const EXPECTED = [
'query': 'iterator<something> -> u32', 'query': 'iterator<something> -> u32',
'correction': null, 'correction': null,
'others': [ 'others': [
{ 'path': 'assoc_type::my', 'name': 'other_fn' }, {
{ 'path': 'assoc_type', 'name': 'my_fn' }, 'path': 'assoc_type::my',
'name': 'other_fn',
'displayType': 'X -> `u32`',
'displayMappedNames': '',
'displayWhereClause': 'X: `Iterator`<`Something`>',
},
{
'path': 'assoc_type',
'name': 'my_fn',
'displayType': 'X -> `u32`',
'displayMappedNames': '',
'displayWhereClause': 'X: `Iterator`<Item=`Something`>',
},
], ],
}, },
{ {
'query': 'iterator<something>', 'query': 'iterator<something>',
'correction': null, 'correction': null,
'in_args': [ 'in_args': [
{ 'path': 'assoc_type::my', 'name': 'other_fn' }, {
{ 'path': 'assoc_type', 'name': 'my_fn' }, 'path': 'assoc_type::my',
'name': 'other_fn',
'displayType': 'X -> u32',
'displayMappedNames': '',
'displayWhereClause': 'X: `Iterator`<`Something`>',
},
{
'path': 'assoc_type',
'name': 'my_fn',
'displayType': 'X -> u32',
'displayMappedNames': '',
'displayWhereClause': 'X: `Iterator`<Item=`Something`>',
},
], ],
}, },
{ {
@ -26,8 +50,20 @@ const EXPECTED = [
{ 'path': 'assoc_type', 'name': 'Something' }, { 'path': 'assoc_type', 'name': 'Something' },
], ],
'in_args': [ 'in_args': [
{ 'path': 'assoc_type::my', 'name': 'other_fn' }, {
{ 'path': 'assoc_type', 'name': 'my_fn' }, 'path': 'assoc_type::my',
'name': 'other_fn',
'displayType': '`X` -> u32',
'displayMappedNames': '',
'displayWhereClause': 'X: Iterator<`Something`>',
},
{
'path': 'assoc_type',
'name': 'my_fn',
'displayType': '`X` -> u32',
'displayMappedNames': '',
'displayWhereClause': 'X: Iterator<Item=`Something`>',
},
], ],
}, },
// if I write an explicit binding, only it shows up // if I write an explicit binding, only it shows up

View File

@ -5,10 +5,22 @@ const EXPECTED = [
'query': 'Result<SomeTrait>', 'query': 'Result<SomeTrait>',
'correction': null, 'correction': null,
'in_args': [ 'in_args': [
{ 'path': 'generics_trait', 'name': 'beta' }, {
'path': 'generics_trait',
'name': 'beta',
'displayType': '`Result`<`T`, ()> -> ()',
'displayMappedNames': '',
'displayWhereClause': 'T: `SomeTrait`',
},
], ],
'returned': [ 'returned': [
{ 'path': 'generics_trait', 'name': 'bet' }, {
'path': 'generics_trait',
'name': 'bet',
'displayType': ' -> `Result`<`T`, ()>',
'displayMappedNames': '',
'displayWhereClause': 'T: `SomeTrait`',
},
], ],
}, },
{ {
@ -25,20 +37,44 @@ const EXPECTED = [
'query': 'OtherThingxxxxxxxx', 'query': 'OtherThingxxxxxxxx',
'correction': null, 'correction': null,
'in_args': [ 'in_args': [
{ 'path': 'generics_trait', 'name': 'alpha' }, {
'path': 'generics_trait',
'name': 'alpha',
'displayType': 'Result<`T`, ()> -> ()',
'displayMappedNames': '',
'displayWhereClause': 'T: `OtherThingxxxxxxxx`',
},
], ],
'returned': [ 'returned': [
{ 'path': 'generics_trait', 'name': 'alef' }, {
'path': 'generics_trait',
'name': 'alef',
'displayType': ' -> Result<`T`, ()>',
'displayMappedNames': '',
'displayWhereClause': 'T: `OtherThingxxxxxxxx`',
},
], ],
}, },
{ {
'query': 'OtherThingxxxxxxxy', 'query': 'OtherThingxxxxxxxy',
'correction': 'OtherThingxxxxxxxx', 'correction': 'OtherThingxxxxxxxx',
'in_args': [ 'in_args': [
{ 'path': 'generics_trait', 'name': 'alpha' }, {
'path': 'generics_trait',
'name': 'alpha',
'displayType': 'Result<`T`, ()> -> ()',
'displayMappedNames': '',
'displayWhereClause': 'T: `OtherThingxxxxxxxx`',
},
], ],
'returned': [ 'returned': [
{ 'path': 'generics_trait', 'name': 'alef' }, {
'path': 'generics_trait',
'name': 'alef',
'displayType': ' -> Result<`T`, ()>',
'displayMappedNames': '',
'displayWhereClause': 'T: `OtherThingxxxxxxxx`',
},
], ],
}, },
]; ];