Skip to content

Commit 4210825

Browse files
Rollup merge of rust-lang#154798 - notriddle:sub-word-path-match, r=GuillaumeGomez
rustdoc-search: match path components on words Since the length of a path is treated as sorting criteria, and every path that contains the query without exactly matching it must be longer, exact matches will always sort first if they exist. Fixes rust-lang#154733
2 parents 67898f8 + f1d240c commit 4210825

4 files changed

Lines changed: 128 additions & 22 deletions

File tree

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

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2799,6 +2799,8 @@ class DocSearch {
27992799
result_list.sort((aaa, bbb) => {
28002800
const aai = aaa.item;
28012801
const bbi = bbb.item;
2802+
const ap = aai.modulePath !== undefined ? aai.modulePath : "";
2803+
const bp = bbi.modulePath !== undefined ? bbi.modulePath : "";
28022804
/** @type {number} */
28032805
let a;
28042806
/** @type {number} */
@@ -2829,14 +2831,25 @@ class DocSearch {
28292831
if (a !== b) {
28302832
return a - b;
28312833
}
2832-
}
28332834

2834-
// Sort by distance in the path part, if specified
2835-
// (less changes required to match means higher rankings)
2836-
a = Number(aaa.path_dist);
2837-
b = Number(bbb.path_dist);
2838-
if (a !== b) {
2839-
return a - b;
2835+
if (parsedQuery.elems[0] &&
2836+
parsedQuery.elems[0].pathWithoutLast.length !== 0
2837+
) {
2838+
// Sort by distance in the path part, if specified
2839+
// (less changes required to match means higher rankings)
2840+
a = Number(aaa.path_dist);
2841+
b = Number(bbb.path_dist);
2842+
if (a !== b) {
2843+
return a - b;
2844+
}
2845+
2846+
// sort by path (longer goes later)
2847+
a = ap.length + (aai.parent ? aai.parent.name.length + 2 : 0);
2848+
b = bp.length + (bbi.parent ? bbi.parent.name.length + 2 : 0);
2849+
if (a !== b) {
2850+
return a - b;
2851+
}
2852+
}
28402853
}
28412854

28422855
// (later literal occurrence, if any, goes later)
@@ -2890,8 +2903,8 @@ class DocSearch {
28902903
}
28912904

28922905
// sort by item name (lexicographically larger goes later)
2893-
let aw = aai.normalizedName;
2894-
let bw = bbi.normalizedName;
2906+
const aw = aai.normalizedName;
2907+
const bw = bbi.normalizedName;
28952908
if (aw !== bw) {
28962909
return (aw > bw ? +1 : -1);
28972910
}
@@ -2914,12 +2927,8 @@ class DocSearch {
29142927
}
29152928

29162929
// sort by path (lexicographically larger goes later)
2917-
const ap = aai.modulePath;
2918-
const bp = bbi.modulePath;
2919-
aw = ap === undefined ? "" : ap;
2920-
bw = bp === undefined ? "" : bp;
2921-
if (aw !== bw) {
2922-
return (aw > bw ? +1 : -1);
2930+
if (ap !== bp) {
2931+
return (ap > bp ? +1 : -1);
29232932
}
29242933

29252934
// que sera, sera
@@ -3848,13 +3857,19 @@ class DocSearch {
38483857
let dist_total = 0;
38493858
for (let x = 0; x < clength; ++x) {
38503859
const [p, c] = [path[i + x], contains[x]];
3860+
const indexOf = p.indexOf(c);
38513861
if (parsedQuery.literalSearch && p !== c) {
38523862
continue pathiter;
3853-
} else if (Math.floor((p.length - c.length) / 3) <= maxPathEditDistance &&
3854-
p.indexOf(c) !== -1
3855-
) {
3863+
} else if (indexOf !== -1) {
38563864
// discount distance on substring match
3857-
dist_total += Math.floor((p.length - c.length) / 3);
3865+
// if component is surrounded by underscores or edges,
3866+
// count the distance as zero
3867+
if (
3868+
(indexOf !== 0 && p[indexOf - 1] !== "_") ||
3869+
(indexOf + c.length !== p.length && p[indexOf + c.length] !== "_")
3870+
) {
3871+
dist_total += Math.floor((p.length - c.length) / 3);
3872+
}
38583873
} else {
38593874
const dist = editDistance(p, c, maxPathEditDistance);
38603875
if (dist > maxPathEditDistance) {

tests/rustdoc-js-std/path-maxeditdistance.js

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,24 @@ const EXPECTED = [
1010
query: 'vec::iter',
1111
others: [
1212
// std::net::ToSocketAttrs::iter should not show up here
13+
{ 'path': 'std::collections::vec_deque', 'name': 'Iter' },
1314
{ 'path': 'std::collections::VecDeque', 'name': 'iter' },
15+
{ 'path': 'std::collections::vec_deque', 'name': 'IterMut' },
1416
{ 'path': 'std::collections::VecDeque', 'name': 'iter_mut' },
15-
{ 'path': 'std::vec::Vec', 'name': 'from_iter' },
1617
{ 'path': 'std::vec', 'name': 'IntoIter' },
18+
{ 'path': 'std::vec::Vec', 'name': 'from_iter' },
1719
{ 'path': 'std::vec::Vec', 'name': 'into_iter' },
18-
{ 'path': 'std::vec::ExtractIf', 'name': 'into_iter' },
1920
{ 'path': 'std::vec::Drain', 'name': 'into_iter' },
20-
{ 'path': 'std::vec::IntoIter', 'name': 'into_iter' },
2121
{ 'path': 'std::vec::Splice', 'name': 'into_iter' },
22+
{ 'path': 'std::vec::IntoIter', 'name': 'into_iter' },
23+
{ 'path': 'std::vec::ExtractIf', 'name': 'into_iter' },
24+
{ 'path': 'std::collections::vec_deque', 'name': 'IntoIter' },
25+
{ 'path': 'std::collections::vec_deque::Iter', 'name': 'into_iter' },
26+
{ 'path': 'std::collections::vec_deque::Drain', 'name': 'into_iter' },
27+
{ 'path': 'std::collections::vec_deque::Splice', 'name': 'into_iter' },
28+
{ 'path': 'std::collections::vec_deque::IterMut', 'name': 'into_iter' },
29+
{ 'path': 'std::collections::vec_deque::IntoIter', 'name': 'into_iter' },
30+
{ 'path': 'std::collections::vec_deque::ExtractIf', 'name': 'into_iter' },
2231
{ 'path': 'std::collections::VecDeque', 'name': 'from_iter' },
2332
{ 'path': 'std::collections::VecDeque', 'name': 'into_iter' },
2433
],

tests/rustdoc-js/path-substring.js

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// exact-check
2+
// ignore-tidy-linelength
3+
const EXPECTED = [
4+
// should match (substring)
5+
{
6+
'query': 'struct:now::Country',
7+
'others': [
8+
{ 'path': 'x::now_is_the_time_for_all_good_men_to_come_to_the_aid_of_their', 'name': 'Country' },
9+
],
10+
},
11+
{
12+
'query': 'struct:is::Country',
13+
'others': [
14+
{ 'path': 'x::now_is_the_time_for_all_good_men_to_come_to_the_aid_of_their', 'name': 'Country' },
15+
],
16+
},
17+
{
18+
'query': 'struct:is_the::Country',
19+
'others': [
20+
{ 'path': 'x::now_is_the_time_for_all_good_men_to_come_to_the_aid_of_their', 'name': 'Country' },
21+
],
22+
},
23+
{
24+
'query': 'struct:the::Country',
25+
'others': [
26+
{ 'path': 'x::now_is_the_time_for_all_good_men_to_come_to_the_aid_of_their', 'name': 'Country' },
27+
],
28+
},
29+
{
30+
'query': 'struct:their::Country',
31+
'others': [
32+
{ 'path': 'x::now_is_the_time_for_all_good_men_to_come_to_the_aid_of_their', 'name': 'Country' },
33+
],
34+
},
35+
// should not match
36+
{
37+
'query': 'struct:ood::Country',
38+
'others': [],
39+
},
40+
{
41+
'query': 'struct:goo::Country',
42+
'others': [],
43+
},
44+
{
45+
'query': 'struct:he::Country',
46+
'others': [],
47+
},
48+
{
49+
'query': 'struct:heir::Country',
50+
'others': [],
51+
},
52+
{
53+
'query': 'struct:hei::Country',
54+
'others': [],
55+
},
56+
{
57+
'query': 'struct:no::Country',
58+
'others': [],
59+
},
60+
// should match (edit distance)
61+
{
62+
'query': 'struct:nowisthetimeforallgoodmentocometotheaidoftheir::Country',
63+
'others': [
64+
{ 'path': 'x::nowisthetimeforallgoodmentocometotheaidoftheir', 'name': 'Country' },
65+
{ 'path': 'x::now_is_the_time_for_all_good_men_to_come_to_the_aid_of_their', 'name': 'Country' },
66+
],
67+
},
68+
{
69+
'query': 'struct:now_is_the_time_for_all_good_men_to_come_to_the_aid_of_their::Country',
70+
'others': [
71+
{ 'path': 'x::now_is_the_time_for_all_good_men_to_come_to_the_aid_of_their', 'name': 'Country' },
72+
{ 'path': 'x::nowisthetimeforallgoodmentocometotheaidoftheir', 'name': 'Country' },
73+
],
74+
},
75+
];

tests/rustdoc-js/path-substring.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#![crate_name = "x"]
2+
pub mod now_is_the_time_for_all_good_men_to_come_to_the_aid_of_their {
3+
pub struct Country;
4+
}
5+
pub mod nowisthetimeforallgoodmentocometotheaidoftheir {
6+
pub struct Country;
7+
}

0 commit comments

Comments
 (0)