rustdoc-search: search backend with partitioned suffix tree

This commit is contained in:
Michael Howell
2024-11-22 12:58:20 -07:00
parent c018ae5389
commit 8511e40e72
146 changed files with 9090 additions and 5057 deletions

View File

@@ -4812,6 +4812,7 @@ dependencies = [
"serde_json", "serde_json",
"sha2", "sha2",
"smallvec", "smallvec",
"stringdex",
"tempfile", "tempfile",
"threadpool", "threadpool",
"tracing", "tracing",
@@ -5225,6 +5226,15 @@ dependencies = [
"quote", "quote",
] ]
[[package]]
name = "stringdex"
version = "0.0.1-alpha4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2841fd43df5b1ff1b042e167068a1fe9b163dc93041eae56ab2296859013a9a0"
dependencies = [
"stacker",
]
[[package]] [[package]]
name = "strsim" name = "strsim"
version = "0.11.1" version = "0.11.1"

View File

@@ -1 +1 @@
8.6.0 8.57.1

View File

@@ -15,6 +15,7 @@ import os.path
import re import re
import shlex import shlex
from collections import namedtuple from collections import namedtuple
from pathlib import Path
try: try:
from html.parser import HTMLParser from html.parser import HTMLParser
@@ -242,6 +243,11 @@ class CachedFiles(object):
return self.last_path return self.last_path
def get_absolute_path(self, path): def get_absolute_path(self, path):
if "*" in path:
paths = list(Path(self.root).glob(path))
if len(paths) != 1:
raise FailedCheck("glob path does not resolve to one file")
path = str(paths[0])
return os.path.join(self.root, path) return os.path.join(self.root, path)
def get_file(self, path): def get_file(self, path):

View File

@@ -21,6 +21,7 @@ rustdoc-json-types = { path = "../rustdoc-json-types" }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
smallvec = "1.8.1" smallvec = "1.8.1"
stringdex = { version = "0.0.1-alpha4" }
tempfile = "3" tempfile = "3"
threadpool = "1.8.1" threadpool = "1.8.1"
tracing = "0.1" tracing = "0.1"

View File

@@ -10,6 +10,7 @@ fn main() {
"static/css/normalize.css", "static/css/normalize.css",
"static/js/main.js", "static/js/main.js",
"static/js/search.js", "static/js/search.js",
"static/js/stringdex.js",
"static/js/settings.js", "static/js/settings.js",
"static/js/src-script.js", "static/js/src-script.js",
"static/js/storage.js", "static/js/storage.js",

View File

@@ -1,6 +1,5 @@
use std::mem; use std::mem;
use rustc_ast::join_path_syms;
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet}; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet};
use rustc_hir::StabilityLevel; use rustc_hir::StabilityLevel;
use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIdSet}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIdSet};
@@ -574,7 +573,6 @@ fn add_item_to_search_index(tcx: TyCtxt<'_>, cache: &mut Cache, item: &clean::It
clean::ItemKind::ImportItem(import) => import.source.did.unwrap_or(item_def_id), clean::ItemKind::ImportItem(import) => import.source.did.unwrap_or(item_def_id),
_ => item_def_id, _ => item_def_id,
}; };
let path = join_path_syms(parent_path);
let impl_id = if let Some(ParentStackItem::Impl { item_id, .. }) = cache.parent_stack.last() { let impl_id = if let Some(ParentStackItem::Impl { item_id, .. }) = cache.parent_stack.last() {
item_id.as_def_id() item_id.as_def_id()
} else { } else {
@@ -593,11 +591,11 @@ fn add_item_to_search_index(tcx: TyCtxt<'_>, cache: &mut Cache, item: &clean::It
ty: item.type_(), ty: item.type_(),
defid: Some(defid), defid: Some(defid),
name, name,
path, module_path: parent_path.to_vec(),
desc, desc,
parent: parent_did, parent: parent_did,
parent_idx: None, parent_idx: None,
exact_path: None, exact_module_path: None,
impl_id, impl_id,
search_type, search_type,
aliases, aliases,

View File

@@ -4,7 +4,7 @@ use std::fmt;
use rustc_hir::def::{CtorOf, DefKind, MacroKinds}; use rustc_hir::def::{CtorOf, DefKind, MacroKinds};
use rustc_span::hygiene::MacroKind; use rustc_span::hygiene::MacroKind;
use serde::{Serialize, Serializer}; use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
use crate::clean; use crate::clean;
@@ -68,6 +68,52 @@ impl Serialize for ItemType {
} }
} }
impl<'de> Deserialize<'de> for ItemType {
fn deserialize<D>(deserializer: D) -> Result<ItemType, D::Error>
where
D: Deserializer<'de>,
{
struct ItemTypeVisitor;
impl<'de> de::Visitor<'de> for ItemTypeVisitor {
type Value = ItemType;
fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(formatter, "an integer between 0 and 25")
}
fn visit_u64<E: de::Error>(self, v: u64) -> Result<ItemType, E> {
Ok(match v {
0 => ItemType::Keyword,
1 => ItemType::Primitive,
2 => ItemType::Module,
3 => ItemType::ExternCrate,
4 => ItemType::Import,
5 => ItemType::Struct,
6 => ItemType::Enum,
7 => ItemType::Function,
8 => ItemType::TypeAlias,
9 => ItemType::Static,
10 => ItemType::Trait,
11 => ItemType::Impl,
12 => ItemType::TyMethod,
13 => ItemType::Method,
14 => ItemType::StructField,
15 => ItemType::Variant,
16 => ItemType::Macro,
17 => ItemType::AssocType,
18 => ItemType::Constant,
19 => ItemType::AssocConst,
20 => ItemType::Union,
21 => ItemType::ForeignType,
23 => ItemType::ProcAttribute,
24 => ItemType::ProcDerive,
25 => ItemType::TraitAlias,
_ => return Err(E::missing_field("unknown number")),
})
}
}
deserializer.deserialize_any(ItemTypeVisitor)
}
}
impl<'a> From<&'a clean::Item> for ItemType { impl<'a> From<&'a clean::Item> for ItemType {
fn from(item: &'a clean::Item) -> ItemType { fn from(item: &'a clean::Item) -> ItemType {
let kind = match &item.kind { let kind = match &item.kind {
@@ -198,6 +244,10 @@ impl ItemType {
pub(crate) fn is_adt(&self) -> bool { pub(crate) fn is_adt(&self) -> bool {
matches!(self, ItemType::Struct | ItemType::Union | ItemType::Enum) matches!(self, ItemType::Struct | ItemType::Union | ItemType::Enum)
} }
/// Keep this the same as isFnLikeTy in search.js
pub(crate) fn is_fn_like(&self) -> bool {
matches!(self, ItemType::Function | ItemType::Method | ItemType::TyMethod)
}
} }
impl fmt::Display for ItemType { impl fmt::Display for ItemType {

View File

@@ -27,6 +27,7 @@ pub(crate) struct Layout {
pub(crate) struct Page<'a> { pub(crate) struct Page<'a> {
pub(crate) title: &'a str, pub(crate) title: &'a str,
pub(crate) short_title: &'a str,
pub(crate) css_class: &'a str, pub(crate) css_class: &'a str,
pub(crate) root_path: &'a str, pub(crate) root_path: &'a str,
pub(crate) static_root_path: Option<&'a str>, pub(crate) static_root_path: Option<&'a str>,

View File

@@ -204,6 +204,18 @@ impl<'tcx> Context<'tcx> {
if !is_module { if !is_module {
title.push_str(it.name.unwrap().as_str()); title.push_str(it.name.unwrap().as_str());
} }
let short_title;
let short_title = if is_module {
let module_name = self.current.last().unwrap();
short_title = if it.is_crate() {
format!("Crate {module_name}")
} else {
format!("Module {module_name}")
};
&short_title[..]
} else {
it.name.as_ref().unwrap().as_str()
};
if !it.is_primitive() && !it.is_keyword() { if !it.is_primitive() && !it.is_keyword() {
if !is_module { if !is_module {
title.push_str(" in "); title.push_str(" in ");
@@ -240,6 +252,7 @@ impl<'tcx> Context<'tcx> {
root_path: &self.root_path(), root_path: &self.root_path(),
static_root_path: self.shared.static_root_path.as_deref(), static_root_path: self.shared.static_root_path.as_deref(),
title: &title, title: &title,
short_title,
description: &desc, description: &desc,
resource_suffix: &self.shared.resource_suffix, resource_suffix: &self.shared.resource_suffix,
rust_logo: has_doc_flag(self.tcx(), LOCAL_CRATE.as_def_id(), sym::rust_logo), rust_logo: has_doc_flag(self.tcx(), LOCAL_CRATE.as_def_id(), sym::rust_logo),
@@ -617,6 +630,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
let shared = &self.shared; let shared = &self.shared;
let mut page = layout::Page { let mut page = layout::Page {
title: "List of all items in this crate", title: "List of all items in this crate",
short_title: "All",
css_class: "mod sys", css_class: "mod sys",
root_path: "../", root_path: "../",
static_root_path: shared.static_root_path.as_deref(), static_root_path: shared.static_root_path.as_deref(),

View File

@@ -130,11 +130,11 @@ pub(crate) struct IndexItem {
pub(crate) ty: ItemType, pub(crate) ty: ItemType,
pub(crate) defid: Option<DefId>, pub(crate) defid: Option<DefId>,
pub(crate) name: Symbol, pub(crate) name: Symbol,
pub(crate) path: String, pub(crate) module_path: Vec<Symbol>,
pub(crate) desc: String, pub(crate) desc: String,
pub(crate) parent: Option<DefId>, pub(crate) parent: Option<DefId>,
pub(crate) parent_idx: Option<isize>, pub(crate) parent_idx: Option<usize>,
pub(crate) exact_path: Option<String>, pub(crate) exact_module_path: Option<Vec<Symbol>>,
pub(crate) impl_id: Option<DefId>, pub(crate) impl_id: Option<DefId>,
pub(crate) search_type: Option<IndexItemFunctionType>, pub(crate) search_type: Option<IndexItemFunctionType>,
pub(crate) aliases: Box<[Symbol]>, pub(crate) aliases: Box<[Symbol]>,
@@ -150,6 +150,19 @@ struct RenderType {
} }
impl RenderType { impl RenderType {
fn size(&self) -> usize {
let mut size = 1;
if let Some(generics) = &self.generics {
size += generics.iter().map(RenderType::size).sum::<usize>();
}
if let Some(bindings) = &self.bindings {
for (_, constraints) in bindings.iter() {
size += 1;
size += constraints.iter().map(RenderType::size).sum::<usize>();
}
}
size
}
// Types are rendered as lists of lists, because that's pretty compact. // Types are rendered as lists of lists, because that's pretty compact.
// The contents of the lists are always integers in self-terminating hex // The contents of the lists are always integers in self-terminating hex
// form, handled by `RenderTypeId::write_to_string`, so no commas are // form, handled by `RenderTypeId::write_to_string`, so no commas are
@@ -191,6 +204,62 @@ impl RenderType {
write_optional_id(self.id, string); write_optional_id(self.id, string);
} }
} }
fn read_from_bytes(string: &[u8]) -> (RenderType, usize) {
let mut i = 0;
if string[i] == b'{' {
i += 1;
let (id, offset) = RenderTypeId::read_from_bytes(&string[i..]);
i += offset;
let generics = if string[i] == b'{' {
i += 1;
let mut generics = Vec::new();
while string[i] != b'}' {
let (ty, offset) = RenderType::read_from_bytes(&string[i..]);
i += offset;
generics.push(ty);
}
assert!(string[i] == b'}');
i += 1;
Some(generics)
} else {
None
};
let bindings = if string[i] == b'{' {
i += 1;
let mut bindings = Vec::new();
while string[i] == b'{' {
i += 1;
let (binding, boffset) = RenderTypeId::read_from_bytes(&string[i..]);
i += boffset;
let mut bconstraints = Vec::new();
assert!(string[i] == b'{');
i += 1;
while string[i] != b'}' {
let (constraint, coffset) = RenderType::read_from_bytes(&string[i..]);
i += coffset;
bconstraints.push(constraint);
}
assert!(string[i] == b'}');
i += 1;
bindings.push((binding.unwrap(), bconstraints));
assert!(string[i] == b'}');
i += 1;
}
assert!(string[i] == b'}');
i += 1;
Some(bindings)
} else {
None
};
assert!(string[i] == b'}');
i += 1;
(RenderType { id, generics, bindings }, i)
} else {
let (id, offset) = RenderTypeId::read_from_bytes(string);
i += offset;
(RenderType { id, generics: None, bindings: None }, i)
}
}
} }
#[derive(Clone, Copy, Debug, Eq, PartialEq)] #[derive(Clone, Copy, Debug, Eq, PartialEq)]
@@ -212,7 +281,20 @@ impl RenderTypeId {
RenderTypeId::Index(idx) => (*idx).try_into().unwrap(), RenderTypeId::Index(idx) => (*idx).try_into().unwrap(),
_ => panic!("must convert render types to indexes before serializing"), _ => panic!("must convert render types to indexes before serializing"),
}; };
search_index::encode::write_vlqhex_to_string(id, string); search_index::encode::write_signed_vlqhex_to_string(id, string);
}
fn read_from_bytes(string: &[u8]) -> (Option<RenderTypeId>, usize) {
let Some((value, offset)) = search_index::encode::read_signed_vlqhex_from_string(string)
else {
return (None, 0);
};
let value = isize::try_from(value).unwrap();
let ty = match value {
..0 => Some(RenderTypeId::Index(value)),
0 => None,
1.. => Some(RenderTypeId::Index(value - 1)),
};
(ty, offset)
} }
} }
@@ -226,12 +308,64 @@ pub(crate) struct IndexItemFunctionType {
} }
impl IndexItemFunctionType { impl IndexItemFunctionType {
fn write_to_string<'a>( fn size(&self) -> usize {
&'a self, self.inputs.iter().map(RenderType::size).sum::<usize>()
string: &mut String, + self.output.iter().map(RenderType::size).sum::<usize>()
backref_queue: &mut VecDeque<&'a IndexItemFunctionType>, + self
) { .where_clause
assert!(backref_queue.len() <= 16); .iter()
.map(|constraints| constraints.iter().map(RenderType::size).sum::<usize>())
.sum::<usize>()
}
fn read_from_string_without_param_names(string: &[u8]) -> (IndexItemFunctionType, usize) {
let mut i = 0;
if string[i] == b'`' {
return (
IndexItemFunctionType {
inputs: Vec::new(),
output: Vec::new(),
where_clause: Vec::new(),
param_names: Vec::new(),
},
1,
);
}
assert_eq!(b'{', string[i]);
i += 1;
fn read_args_from_string(string: &[u8]) -> (Vec<RenderType>, usize) {
let mut i = 0;
let mut params = Vec::new();
if string[i] == b'{' {
// multiple params
i += 1;
while string[i] != b'}' {
let (ty, offset) = RenderType::read_from_bytes(&string[i..]);
i += offset;
params.push(ty);
}
i += 1;
} else if string[i] != b'}' {
let (tyid, offset) = RenderTypeId::read_from_bytes(&string[i..]);
params.push(RenderType { id: tyid, generics: None, bindings: None });
i += offset;
}
(params, i)
}
let (inputs, offset) = read_args_from_string(&string[i..]);
i += offset;
let (output, offset) = read_args_from_string(&string[i..]);
i += offset;
let mut where_clause = Vec::new();
while string[i] != b'}' {
let (constraint, offset) = read_args_from_string(&string[i..]);
i += offset;
where_clause.push(constraint);
}
assert_eq!(b'}', string[i], "{} {}", String::from_utf8_lossy(&string), i);
i += 1;
(IndexItemFunctionType { inputs, output, where_clause, param_names: Vec::new() }, i)
}
fn write_to_string_without_param_names<'a>(&'a self, string: &mut String) {
// If we couldn't figure out a type, just write 0, // If we couldn't figure out a type, just write 0,
// which is encoded as `` ` `` (see RenderTypeId::write_to_string). // which is encoded as `` ` `` (see RenderTypeId::write_to_string).
let has_missing = self let has_missing = self
@@ -241,18 +375,7 @@ impl IndexItemFunctionType {
.any(|i| i.id.is_none() && i.generics.is_none()); .any(|i| i.id.is_none() && i.generics.is_none());
if has_missing { if has_missing {
string.push('`'); string.push('`');
} else if let Some(idx) = backref_queue.iter().position(|other| *other == self) {
// The backref queue has 16 items, so backrefs use
// a single hexit, disjoint from the ones used for numbers.
string.push(
char::try_from('0' as u32 + u32::try_from(idx).unwrap())
.expect("last possible value is '?'"),
);
} else { } else {
backref_queue.push_front(self);
if backref_queue.len() > 16 {
backref_queue.pop_back();
}
string.push('{'); string.push('{');
match &self.inputs[..] { match &self.inputs[..] {
[one] if one.generics.is_none() && one.bindings.is_none() => { [one] if one.generics.is_none() && one.bindings.is_none() => {

View File

@@ -35,6 +35,7 @@ use crate::html::format::{
visibility_print_with_space, visibility_print_with_space,
}; };
use crate::html::markdown::{HeadingOffset, MarkdownSummaryLine}; use crate::html::markdown::{HeadingOffset, MarkdownSummaryLine};
use crate::html::render::sidebar::filters;
use crate::html::render::{document_full, document_item_info}; use crate::html::render::{document_full, document_item_info};
use crate::html::url_parts_builder::UrlPartsBuilder; use crate::html::url_parts_builder::UrlPartsBuilder;

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,4 @@
use base64::prelude::*; pub(crate) fn write_signed_vlqhex_to_string(n: i32, string: &mut String) {
pub(crate) fn write_vlqhex_to_string(n: i32, string: &mut String) {
let (sign, magnitude): (bool, u32) = let (sign, magnitude): (bool, u32) =
if n >= 0 { (false, n.try_into().unwrap()) } else { (true, (-n).try_into().unwrap()) }; if n >= 0 { (false, n.try_into().unwrap()) } else { (true, (-n).try_into().unwrap()) };
// zig-zag encoding // zig-zag encoding
@@ -37,206 +35,66 @@ pub(crate) fn write_vlqhex_to_string(n: i32, string: &mut String) {
} }
} }
// Used during bitmap encoding pub fn read_signed_vlqhex_from_string(string: &[u8]) -> Option<(i32, usize)> {
enum Container { let mut n = 0i32;
/// number of ones, bits let mut i = 0;
Bits(Box<[u64; 1024]>), while let Some(&c) = string.get(i) {
/// list of entries i += 1;
Array(Vec<u16>), n = (n << 4) | i32::from(c & 0xF);
/// list of (start, len-1) if c >= 96 {
Run(Vec<(u16, u16)>), // zig-zag encoding
let (sign, magnitude) = (n & 1, n >> 1);
let value = if sign == 0 { 1 } else { -1 } * magnitude;
return Some((value, i));
}
}
None
} }
impl Container {
fn popcount(&self) -> u32 { pub fn write_postings_to_string(postings: &[Vec<u32>], buf: &mut Vec<u8>) {
match self { for list in postings {
Container::Bits(bits) => bits.iter().copied().map(|x| x.count_ones()).sum(), if list.is_empty() {
Container::Array(array) => { buf.push(0);
array.len().try_into().expect("array can't be bigger than 2**32") continue;
}
Container::Run(runs) => {
runs.iter().copied().map(|(_, lenm1)| u32::from(lenm1) + 1).sum()
}
} }
} let len_before = buf.len();
fn push(&mut self, value: u16) { stringdex::internals::encode::write_bitmap_to_bytes(&list, &mut *buf).unwrap();
match self { let len_after = buf.len();
Container::Bits(bits) => bits[value as usize >> 6] |= 1 << (value & 0x3F), if len_after - len_before > 1 + (4 * list.len()) && list.len() < 0x3a {
Container::Array(array) => { buf.truncate(len_before);
array.push(value); buf.push(list.len() as u8);
if array.len() >= 4096 { for &item in list {
let array = std::mem::take(array); buf.push(item as u8);
*self = Container::Bits(Box::new([0; 1024])); buf.push((item >> 8) as u8);
for value in array { buf.push((item >> 16) as u8);
self.push(value); buf.push((item >> 24) as u8);
}
}
} }
Container::Run(runs) => {
if let Some(r) = runs.last_mut()
&& r.0 + r.1 + 1 == value
{
r.1 += 1;
} else {
runs.push((value, 0));
}
}
}
}
fn try_make_run(&mut self) -> bool {
match self {
Container::Bits(bits) => {
let mut r: u64 = 0;
for (i, chunk) in bits.iter().copied().enumerate() {
let next_chunk =
i.checked_add(1).and_then(|i| bits.get(i)).copied().unwrap_or(0);
r += !chunk & u64::from((chunk << 1).count_ones());
r += !next_chunk & u64::from((chunk >> 63).count_ones());
}
if (2 + 4 * r) >= 8192 {
return false;
}
let bits = std::mem::replace(bits, Box::new([0; 1024]));
*self = Container::Run(Vec::new());
for (i, bits) in bits.iter().copied().enumerate() {
if bits == 0 {
continue;
}
for j in 0..64 {
let value = (u16::try_from(i).unwrap() << 6) | j;
if bits & (1 << j) != 0 {
self.push(value);
}
}
}
true
}
Container::Array(array) if array.len() <= 5 => false,
Container::Array(array) => {
let mut r = 0;
let mut prev = None;
for value in array.iter().copied() {
if value.checked_sub(1) != prev {
r += 1;
}
prev = Some(value);
}
if 2 + 4 * r >= 2 * array.len() + 2 {
return false;
}
let array = std::mem::take(array);
*self = Container::Run(Vec::new());
for value in array {
self.push(value);
}
true
}
Container::Run(_) => true,
} }
} }
} }
// checked against roaring-rs in pub fn read_postings_from_string(postings: &mut Vec<Vec<u32>>, mut buf: &[u8]) {
// https://gitlab.com/notriddle/roaring-test use stringdex::internals::decode::RoaringBitmap;
pub(crate) fn write_bitmap_to_bytes( while let Some(&c) = buf.get(0) {
domain: &[u32], if c < 0x3a {
mut out: impl std::io::Write, buf = &buf[1..];
) -> std::io::Result<()> { let mut slot = Vec::new();
// https://arxiv.org/pdf/1603.06549.pdf for _ in 0..c {
let mut keys = Vec::<u16>::new(); slot.push(
let mut containers = Vec::<Container>::new(); (buf[0] as u32)
let mut key: u16; | ((buf[1] as u32) << 8)
let mut domain_iter = domain.iter().copied().peekable(); | ((buf[2] as u32) << 16)
let mut has_run = false; | ((buf[3] as u32) << 24),
while let Some(entry) = domain_iter.next() { );
key = (entry >> 16).try_into().expect("shifted off the top 16 bits, so it should fit"); buf = &buf[4..];
let value: u16 = (entry & 0x00_00_FF_FF).try_into().expect("AND 16 bits, so it should fit");
let mut container = Container::Array(vec![value]);
while let Some(entry) = domain_iter.peek().copied() {
let entry_key: u16 =
(entry >> 16).try_into().expect("shifted off the top 16 bits, so it should fit");
if entry_key != key {
break;
} }
domain_iter.next().expect("peeking just succeeded"); postings.push(slot);
container
.push((entry & 0x00_00_FF_FF).try_into().expect("AND 16 bits, so it should fit"));
}
keys.push(key);
has_run = container.try_make_run() || has_run;
containers.push(container);
}
// https://github.com/RoaringBitmap/RoaringFormatSpec
const SERIAL_COOKIE_NO_RUNCONTAINER: u32 = 12346;
const SERIAL_COOKIE: u32 = 12347;
const NO_OFFSET_THRESHOLD: u32 = 4;
let size: u32 = containers.len().try_into().unwrap();
let start_offset = if has_run {
out.write_all(&u32::to_le_bytes(SERIAL_COOKIE | ((size - 1) << 16)))?;
for set in containers.chunks(8) {
let mut b = 0;
for (i, container) in set.iter().enumerate() {
if matches!(container, &Container::Run(..)) {
b |= 1 << i;
}
}
out.write_all(&[b])?;
}
if size < NO_OFFSET_THRESHOLD {
4 + 4 * size + size.div_ceil(8)
} else { } else {
4 + 8 * size + size.div_ceil(8) let (bitmap, consumed_bytes_len) =
} RoaringBitmap::from_bytes(buf).unwrap_or_else(|| (RoaringBitmap::default(), 0));
} else { assert_ne!(consumed_bytes_len, 0);
out.write_all(&u32::to_le_bytes(SERIAL_COOKIE_NO_RUNCONTAINER))?; postings.push(bitmap.to_vec());
out.write_all(&u32::to_le_bytes(containers.len().try_into().unwrap()))?; buf = &buf[consumed_bytes_len..];
4 + 4 + 4 * size + 4 * size
};
for (&key, container) in keys.iter().zip(&containers) {
// descriptive header
let key: u32 = key.into();
let count: u32 = container.popcount() - 1;
out.write_all(&u32::to_le_bytes((count << 16) | key))?;
}
if !has_run || size >= NO_OFFSET_THRESHOLD {
// offset header
let mut starting_offset = start_offset;
for container in &containers {
out.write_all(&u32::to_le_bytes(starting_offset))?;
starting_offset += match container {
Container::Bits(_) => 8192u32,
Container::Array(array) => u32::try_from(array.len()).unwrap() * 2,
Container::Run(runs) => 2 + u32::try_from(runs.len()).unwrap() * 4,
};
} }
} }
for container in &containers {
match container {
Container::Bits(bits) => {
for chunk in bits.iter() {
out.write_all(&u64::to_le_bytes(*chunk))?;
}
}
Container::Array(array) => {
for value in array.iter() {
out.write_all(&u16::to_le_bytes(*value))?;
}
}
Container::Run(runs) => {
out.write_all(&u16::to_le_bytes(runs.len().try_into().unwrap()))?;
for (start, lenm1) in runs.iter().copied() {
out.write_all(&u16::to_le_bytes(start))?;
out.write_all(&u16::to_le_bytes(lenm1))?;
}
}
}
}
Ok(())
}
pub(crate) fn bitmap_to_string(domain: &[u32]) -> String {
let mut buf = Vec::new();
let mut strbuf = String::new();
write_bitmap_to_bytes(domain, &mut buf).unwrap();
BASE64_STANDARD.encode_string(&buf, &mut strbuf);
strbuf
} }

View File

@@ -65,17 +65,17 @@ pub(crate) fn write_shared(
// Write shared runs within a flock; disable thread dispatching of IO temporarily. // Write shared runs within a flock; disable thread dispatching of IO temporarily.
let _lock = try_err!(flock::Lock::new(&lock_file, true, true, true), &lock_file); let _lock = try_err!(flock::Lock::new(&lock_file, true, true, true), &lock_file);
let SerializedSearchIndex { index, desc } = build_index(krate, &mut cx.shared.cache, tcx); let search_index =
write_search_desc(cx, krate, &desc)?; // does not need to be merged build_index(krate, &mut cx.shared.cache, tcx, &cx.dst, &cx.shared.resource_suffix)?;
let crate_name = krate.name(cx.tcx()); let crate_name = krate.name(cx.tcx());
let crate_name = crate_name.as_str(); // rand let crate_name = crate_name.as_str(); // rand
let crate_name_json = OrderedJson::serialize(crate_name).unwrap(); // "rand" let crate_name_json = OrderedJson::serialize(crate_name).unwrap(); // "rand"
let external_crates = hack_get_external_crate_names(&cx.dst, &cx.shared.resource_suffix)?; let external_crates = hack_get_external_crate_names(&cx.dst, &cx.shared.resource_suffix)?;
let info = CrateInfo { let info = CrateInfo {
version: CrateInfoVersion::V1, version: CrateInfoVersion::V2,
src_files_js: SourcesPart::get(cx, &crate_name_json)?, src_files_js: SourcesPart::get(cx, &crate_name_json)?,
search_index_js: SearchIndexPart::get(index, &cx.shared.resource_suffix)?, search_index,
all_crates: AllCratesPart::get(crate_name_json.clone(), &cx.shared.resource_suffix)?, all_crates: AllCratesPart::get(crate_name_json.clone(), &cx.shared.resource_suffix)?,
crates_index: CratesIndexPart::get(crate_name, &external_crates)?, crates_index: CratesIndexPart::get(crate_name, &external_crates)?,
trait_impl: TraitAliasPart::get(cx, &crate_name_json)?, trait_impl: TraitAliasPart::get(cx, &crate_name_json)?,
@@ -141,7 +141,7 @@ pub(crate) fn write_not_crate_specific(
resource_suffix: &str, resource_suffix: &str,
include_sources: bool, include_sources: bool,
) -> Result<(), Error> { ) -> Result<(), Error> {
write_rendered_cross_crate_info(crates, dst, opt, include_sources)?; write_rendered_cross_crate_info(crates, dst, opt, include_sources, resource_suffix)?;
write_static_files(dst, opt, style_files, css_file_extension, resource_suffix)?; write_static_files(dst, opt, style_files, css_file_extension, resource_suffix)?;
Ok(()) Ok(())
} }
@@ -151,13 +151,18 @@ fn write_rendered_cross_crate_info(
dst: &Path, dst: &Path,
opt: &RenderOptions, opt: &RenderOptions,
include_sources: bool, include_sources: bool,
resource_suffix: &str,
) -> Result<(), Error> { ) -> Result<(), Error> {
let m = &opt.should_merge; let m = &opt.should_merge;
if opt.should_emit_crate() { if opt.should_emit_crate() {
if include_sources { if include_sources {
write_rendered_cci::<SourcesPart, _>(SourcesPart::blank, dst, crates, m)?; write_rendered_cci::<SourcesPart, _>(SourcesPart::blank, dst, crates, m)?;
} }
write_rendered_cci::<SearchIndexPart, _>(SearchIndexPart::blank, dst, crates, m)?; crates
.iter()
.fold(SerializedSearchIndex::default(), |a, b| a.union(&b.search_index))
.sort()
.write_to(dst, resource_suffix)?;
write_rendered_cci::<AllCratesPart, _>(AllCratesPart::blank, dst, crates, m)?; write_rendered_cci::<AllCratesPart, _>(AllCratesPart::blank, dst, crates, m)?;
} }
write_rendered_cci::<TraitAliasPart, _>(TraitAliasPart::blank, dst, crates, m)?; write_rendered_cci::<TraitAliasPart, _>(TraitAliasPart::blank, dst, crates, m)?;
@@ -215,38 +220,12 @@ fn write_static_files(
Ok(()) Ok(())
} }
/// Write the search description shards to disk
fn write_search_desc(
cx: &mut Context<'_>,
krate: &Crate,
search_desc: &[(usize, String)],
) -> Result<(), Error> {
let crate_name = krate.name(cx.tcx()).to_string();
let encoded_crate_name = OrderedJson::serialize(&crate_name).unwrap();
let path = PathBuf::from_iter([&cx.dst, Path::new("search.desc"), Path::new(&crate_name)]);
if path.exists() {
try_err!(fs::remove_dir_all(&path), &path);
}
for (i, (_, part)) in search_desc.iter().enumerate() {
let filename = static_files::suffix_path(
&format!("{crate_name}-desc-{i}-.js"),
&cx.shared.resource_suffix,
);
let path = path.join(filename);
let part = OrderedJson::serialize(part).unwrap();
let part = format!("searchState.loadedDescShard({encoded_crate_name}, {i}, {part})");
create_parents(&path)?;
try_err!(fs::write(&path, part), &path);
}
Ok(())
}
/// Contains pre-rendered contents to insert into the CCI template /// Contains pre-rendered contents to insert into the CCI template
#[derive(Serialize, Deserialize, Clone, Debug)] #[derive(Serialize, Deserialize, Clone, Debug)]
pub(crate) struct CrateInfo { pub(crate) struct CrateInfo {
version: CrateInfoVersion, version: CrateInfoVersion,
src_files_js: PartsAndLocations<SourcesPart>, src_files_js: PartsAndLocations<SourcesPart>,
search_index_js: PartsAndLocations<SearchIndexPart>, search_index: SerializedSearchIndex,
all_crates: PartsAndLocations<AllCratesPart>, all_crates: PartsAndLocations<AllCratesPart>,
crates_index: PartsAndLocations<CratesIndexPart>, crates_index: PartsAndLocations<CratesIndexPart>,
trait_impl: PartsAndLocations<TraitAliasPart>, trait_impl: PartsAndLocations<TraitAliasPart>,
@@ -277,7 +256,7 @@ impl CrateInfo {
/// to provide better diagnostics about including an invalid file. /// to provide better diagnostics about including an invalid file.
#[derive(Serialize, Deserialize, Clone, Debug)] #[derive(Serialize, Deserialize, Clone, Debug)]
enum CrateInfoVersion { enum CrateInfoVersion {
V1, V2,
} }
/// Paths (relative to the doc root) and their pre-merge contents /// Paths (relative to the doc root) and their pre-merge contents
@@ -331,36 +310,6 @@ trait CciPart: Sized + fmt::Display + DeserializeOwned + 'static {
fn from_crate_info(crate_info: &CrateInfo) -> &PartsAndLocations<Self>; fn from_crate_info(crate_info: &CrateInfo) -> &PartsAndLocations<Self>;
} }
#[derive(Serialize, Deserialize, Clone, Default, Debug)]
struct SearchIndex;
type SearchIndexPart = Part<SearchIndex, EscapedJson>;
impl CciPart for SearchIndexPart {
type FileFormat = sorted_template::Js;
fn from_crate_info(crate_info: &CrateInfo) -> &PartsAndLocations<Self> {
&crate_info.search_index_js
}
}
impl SearchIndexPart {
fn blank() -> SortedTemplate<<Self as CciPart>::FileFormat> {
SortedTemplate::from_before_after(
r"var searchIndex = new Map(JSON.parse('[",
r"]'));
if (typeof exports !== 'undefined') exports.searchIndex = searchIndex;
else if (window.initSearch) window.initSearch(searchIndex);",
)
}
fn get(
search_index: OrderedJson,
resource_suffix: &str,
) -> Result<PartsAndLocations<Self>, Error> {
let path = suffix_path("search-index.js", resource_suffix);
let search_index = EscapedJson::from(search_index);
Ok(PartsAndLocations::with(path, search_index))
}
}
#[derive(Serialize, Deserialize, Clone, Default, Debug)] #[derive(Serialize, Deserialize, Clone, Default, Debug)]
struct AllCrates; struct AllCrates;
type AllCratesPart = Part<AllCrates, OrderedJson>; type AllCratesPart = Part<AllCrates, OrderedJson>;
@@ -426,6 +375,7 @@ impl CratesIndexPart {
fn blank(cx: &Context<'_>) -> SortedTemplate<<Self as CciPart>::FileFormat> { fn blank(cx: &Context<'_>) -> SortedTemplate<<Self as CciPart>::FileFormat> {
let page = layout::Page { let page = layout::Page {
title: "Index of crates", title: "Index of crates",
short_title: "Crates",
css_class: "mod sys", css_class: "mod sys",
root_path: "./", root_path: "./",
static_root_path: cx.shared.static_root_path.as_deref(), static_root_path: cx.shared.static_root_path.as_deref(),

View File

@@ -29,14 +29,6 @@ fn sources_template() {
assert_eq!(but_last_line(&template.to_string()), r#"createSrcSidebar('["u","v"]');"#); assert_eq!(but_last_line(&template.to_string()), r#"createSrcSidebar('["u","v"]');"#);
} }
#[test]
fn sources_parts() {
let parts =
SearchIndexPart::get(OrderedJson::serialize(["foo", "bar"]).unwrap(), "suffix").unwrap();
assert_eq!(&parts.parts[0].0, Path::new("search-indexsuffix.js"));
assert_eq!(&parts.parts[0].1.to_string(), r#"["foo","bar"]"#);
}
#[test] #[test]
fn all_crates_template() { fn all_crates_template() {
let mut template = AllCratesPart::blank(); let mut template = AllCratesPart::blank();
@@ -54,31 +46,6 @@ fn all_crates_parts() {
assert_eq!(&parts.parts[0].1.to_string(), r#""crate""#); assert_eq!(&parts.parts[0].1.to_string(), r#""crate""#);
} }
#[test]
fn search_index_template() {
let mut template = SearchIndexPart::blank();
assert_eq!(
but_last_line(&template.to_string()),
r"var searchIndex = new Map(JSON.parse('[]'));
if (typeof exports !== 'undefined') exports.searchIndex = searchIndex;
else if (window.initSearch) window.initSearch(searchIndex);"
);
template.append(EscapedJson::from(OrderedJson::serialize([1, 2]).unwrap()).to_string());
assert_eq!(
but_last_line(&template.to_string()),
r"var searchIndex = new Map(JSON.parse('[[1,2]]'));
if (typeof exports !== 'undefined') exports.searchIndex = searchIndex;
else if (window.initSearch) window.initSearch(searchIndex);"
);
template.append(EscapedJson::from(OrderedJson::serialize([4, 3]).unwrap()).to_string());
assert_eq!(
but_last_line(&template.to_string()),
r"var searchIndex = new Map(JSON.parse('[[1,2],[4,3]]'));
if (typeof exports !== 'undefined') exports.searchIndex = searchIndex;
else if (window.initSearch) window.initSearch(searchIndex);"
);
}
#[test] #[test]
fn crates_index_part() { fn crates_index_part() {
let external_crates = ["bar".to_string(), "baz".to_string()]; let external_crates = ["bar".to_string(), "baz".to_string()];

View File

@@ -230,6 +230,7 @@ impl SourceCollector<'_, '_> {
); );
let page = layout::Page { let page = layout::Page {
title: &title, title: &title,
short_title: &src_fname.to_string_lossy(),
css_class: "src", css_class: "src",
root_path: &root_path, root_path: &root_path,
static_root_path: shared.static_root_path.as_deref(), static_root_path: shared.static_root_path.as_deref(),

View File

@@ -258,6 +258,17 @@ h1, h2, h3, h4 {
padding-bottom: 6px; padding-bottom: 6px;
margin-bottom: 15px; margin-bottom: 15px;
} }
.search-results-main-heading {
grid-template-areas:
"main-heading-breadcrumbs main-heading-placeholder"
"main-heading-breadcrumbs main-heading-toolbar "
"main-heading-h1 main-heading-toolbar ";
}
.search-results-main-heading nav.sub {
grid-area: main-heading-h1;
align-items: end;
margin: 4px 0 8px 0;
}
.rustdoc-breadcrumbs { .rustdoc-breadcrumbs {
grid-area: main-heading-breadcrumbs; grid-area: main-heading-breadcrumbs;
line-height: 1.25; line-height: 1.25;
@@ -265,6 +276,16 @@ h1, h2, h3, h4 {
position: relative; position: relative;
z-index: 1; z-index: 1;
} }
.search-switcher {
grid-area: main-heading-breadcrumbs;
line-height: 1.5;
display: flex;
color: var(--main-color);
align-items: baseline;
white-space: nowrap;
padding-top: 8px;
min-height: 34px;
}
.rustdoc-breadcrumbs a { .rustdoc-breadcrumbs a {
padding: 5px 0 7px; padding: 5px 0 7px;
} }
@@ -305,7 +326,7 @@ h4.code-header {
#crate-search, #crate-search,
h1, h2, h3, h4, h5, h6, h1, h2, h3, h4, h5, h6,
.sidebar, .sidebar,
.mobile-topbar, rustdoc-topbar,
.search-input, .search-input,
.search-results .result-name, .search-results .result-name,
.item-table dt > a, .item-table dt > a,
@@ -317,6 +338,7 @@ rustdoc-toolbar,
summary.hideme, summary.hideme,
.scraped-example-list, .scraped-example-list,
.rustdoc-breadcrumbs, .rustdoc-breadcrumbs,
.search-switcher,
/* This selector is for the items listed in the "all items" page. */ /* This selector is for the items listed in the "all items" page. */
ul.all-items { ul.all-items {
font-family: "Fira Sans", Arial, NanumBarunGothic, sans-serif; font-family: "Fira Sans", Arial, NanumBarunGothic, sans-serif;
@@ -329,7 +351,7 @@ a.anchor,
.rust a, .rust a,
.sidebar h2 a, .sidebar h2 a,
.sidebar h3 a, .sidebar h3 a,
.mobile-topbar h2 a, rustdoc-topbar h2 a,
h1 a, h1 a,
.search-results a, .search-results a,
.search-results li, .search-results li,
@@ -616,7 +638,7 @@ img {
color: var(--sidebar-resizer-active); color: var(--sidebar-resizer-active);
} }
.sidebar, .mobile-topbar, .sidebar-menu-toggle, .sidebar, rustdoc-topbar, .sidebar-menu-toggle,
#src-sidebar { #src-sidebar {
background-color: var(--sidebar-background-color); background-color: var(--sidebar-background-color);
} }
@@ -857,7 +879,7 @@ ul.block, .block li, .block ul {
margin-bottom: 1rem; margin-bottom: 1rem;
} }
.mobile-topbar { rustdoc-topbar {
display: none; display: none;
} }
@@ -1098,16 +1120,15 @@ div.where {
nav.sub { nav.sub {
flex-grow: 1; flex-grow: 1;
flex-flow: row nowrap; flex-flow: row nowrap;
margin: 4px 0 0 0;
display: flex; display: flex;
align-items: center; align-items: start;
margin-top: 4px;
} }
.search-form { .search-form {
position: relative; position: relative;
display: flex; display: flex;
height: 34px; height: 34px;
flex-grow: 1; flex-grow: 1;
margin-bottom: 4px;
} }
.src nav.sub { .src nav.sub {
margin: 0 0 -10px 0; margin: 0 0 -10px 0;
@@ -1208,27 +1229,14 @@ table,
margin-left: 0; margin-left: 0;
} }
.search-results-title {
margin-top: 0;
white-space: nowrap;
/* flex layout allows shrinking the <select> appropriately if it becomes too large */
display: flex;
/* make things look like in a line, despite the fact that we're using a layout
with boxes (i.e. from the flex layout) */
align-items: baseline;
}
.search-results-title + .sub-heading {
color: var(--main-color);
display: flex;
align-items: baseline;
white-space: nowrap;
}
#crate-search-div { #crate-search-div {
/* ensures that 100% in properties of #crate-search-div:after /* ensures that 100% in properties of #crate-search-div:after
are relative to the size of this div */ are relative to the size of this div */
position: relative; position: relative;
/* allows this div (and with it the <select>-element "#crate-search") to be shrunk */ /* allows this div (and with it the <select>-element "#crate-search") to be shrunk */
min-width: 0; min-width: 0;
/* keep label text for switcher from moving down when this appears */
margin-top: -1px;
} }
#crate-search { #crate-search {
padding: 0 23px 0 4px; padding: 0 23px 0 4px;
@@ -1294,6 +1302,7 @@ so that we can apply CSS-filters to change the arrow color in themes */
flex-grow: 1; flex-grow: 1;
background-color: var(--button-background-color); background-color: var(--button-background-color);
color: var(--search-color); color: var(--search-color);
max-width: 100%;
} }
.search-input:focus { .search-input:focus {
border-color: var(--search-input-focused-border-color); border-color: var(--search-input-focused-border-color);
@@ -1459,14 +1468,14 @@ so that we can apply CSS-filters to change the arrow color in themes */
} }
#settings.popover { #settings.popover {
--popover-arrow-offset: 202px; --popover-arrow-offset: 196px;
top: calc(100% - 16px); top: calc(100% - 16px);
} }
/* use larger max-width for help popover, but not for help.html */ /* use larger max-width for help popover, but not for help.html */
#help.popover { #help.popover {
max-width: 600px; max-width: 600px;
--popover-arrow-offset: 118px; --popover-arrow-offset: 115px;
top: calc(100% - 16px); top: calc(100% - 16px);
} }
@@ -1929,10 +1938,12 @@ a.tooltip:hover::after {
color: inherit; color: inherit;
} }
#search-tabs button:not(.selected) { #search-tabs button:not(.selected) {
--search-tab-button-background: var(--search-tab-button-not-selected-background);
background-color: var(--search-tab-button-not-selected-background); background-color: var(--search-tab-button-not-selected-background);
border-top-color: var(--search-tab-button-not-selected-border-top-color); border-top-color: var(--search-tab-button-not-selected-border-top-color);
} }
#search-tabs button:hover, #search-tabs button.selected { #search-tabs button:hover, #search-tabs button.selected {
--search-tab-button-background: var(--search-tab-button-selected-background);
background-color: var(--search-tab-button-selected-background); background-color: var(--search-tab-button-selected-background);
border-top-color: var(--search-tab-button-selected-border-top-color); border-top-color: var(--search-tab-button-selected-border-top-color);
} }
@@ -1941,6 +1952,73 @@ a.tooltip:hover::after {
font-size: 1rem; font-size: 1rem;
font-variant-numeric: tabular-nums; font-variant-numeric: tabular-nums;
color: var(--search-tab-title-count-color); color: var(--search-tab-title-count-color);
position: relative;
}
#search-tabs .count.loading {
color: transparent;
}
.search-form.loading {
--search-tab-button-background: var(--button-background-color);
}
#search-tabs .count.loading::before,
.search-form.loading::before
{
width: 16px;
height: 16px;
border-radius: 16px;
background: radial-gradient(
var(--search-tab-button-background) 0 50%,
transparent 50% 100%
), conic-gradient(
var(--code-highlight-kw-color) 0deg 30deg,
var(--code-highlight-prelude-color) 30deg 60deg,
var(--code-highlight-number-color) 90deg 120deg,
var(--code-highlight-lifetime-color ) 120deg 150deg,
var(--code-highlight-comment-color) 150deg 180deg,
var(--code-highlight-self-color) 180deg 210deg,
var(--code-highlight-attribute-color) 210deg 240deg,
var(--code-highlight-literal-color) 210deg 240deg,
var(--code-highlight-macro-color) 240deg 270deg,
var(--code-highlight-question-mark-color) 270deg 300deg,
var(--code-highlight-prelude-val-color) 300deg 330deg,
var(--code-highlight-doc-comment-color) 330deg 360deg
);
content: "";
position: absolute;
left: 2px;
top: 2px;
animation: rotating 1.25s linear infinite;
}
#search-tabs .count.loading::after,
.search-form.loading::after
{
width: 18px;
height: 18px;
border-radius: 18px;
background: conic-gradient(
var(--search-tab-button-background) 0deg 180deg,
transparent 270deg 360deg
);
content: "";
position: absolute;
left: 1px;
top: 1px;
animation: rotating 0.66s linear infinite;
}
.search-form.loading::before {
left: auto;
right: 9px;
top: 8px;
}
.search-form.loading::after {
left: auto;
right: 8px;
top: 8px;
} }
#search .error code { #search .error code {
@@ -1974,7 +2052,7 @@ a.tooltip:hover::after {
border-bottom: 1px solid var(--border-color); border-bottom: 1px solid var(--border-color);
} }
#settings-menu, #help-button, button#toggle-all-docs { #search-button, .settings-menu, .help-menu, button#toggle-all-docs {
margin-left: var(--button-left-margin); margin-left: var(--button-left-margin);
display: flex; display: flex;
line-height: 1.25; line-height: 1.25;
@@ -1989,69 +2067,100 @@ a.tooltip:hover::after {
display: flex; display: flex;
margin-right: 4px; margin-right: 4px;
position: fixed; position: fixed;
margin-top: 25px;
left: 6px;
height: 34px; height: 34px;
width: 34px; width: 34px;
z-index: calc(var(--desktop-sidebar-z-index) + 1);
} }
.hide-sidebar #sidebar-button { .hide-sidebar #sidebar-button {
left: 6px; left: 6px;
background-color: var(--main-background-color); background-color: var(--main-background-color);
z-index: 1;
} }
.src #sidebar-button { .src #sidebar-button {
margin-top: 0;
top: 8px;
left: 8px; left: 8px;
z-index: calc(var(--desktop-sidebar-z-index) + 1); border-color: var(--border-color);
} }
.hide-sidebar .src #sidebar-button { .hide-sidebar .src #sidebar-button {
position: static; position: static;
} }
#settings-menu > a, #help-button > a, #sidebar-button > a, button#toggle-all-docs { #search-button > a,
.settings-menu > a,
.help-menu > a,
#sidebar-button > a,
button#toggle-all-docs {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
flex-direction: column; flex-direction: column;
} }
#settings-menu > a, #help-button > a, button#toggle-all-docs { #search-button > a,
.settings-menu > a,
.help-menu > a,
button#toggle-all-docs {
border: 1px solid transparent; border: 1px solid transparent;
border-radius: var(--button-border-radius); border-radius: var(--button-border-radius);
color: var(--main-color); color: var(--main-color);
} }
#settings-menu > a, #help-button > a, button#toggle-all-docs { #search-button > a, .settings-menu > a, .help-menu > a, button#toggle-all-docs {
width: 80px; width: 80px;
border-radius: var(--toolbar-button-border-radius); border-radius: var(--toolbar-button-border-radius);
} }
#settings-menu > a, #help-button > a { #search-button > a, .settings-menu > a, .help-menu > a {
min-width: 0; min-width: 0;
} }
#sidebar-button > a { #sidebar-button > a {
background-color: var(--sidebar-background-color); border: solid 1px transparent;
border-radius: var(--button-border-radius);
background-color: var(--button-background-color);
width: 33px; width: 33px;
} }
#sidebar-button > a:hover, #sidebar-button > a:focus-visible { .src #sidebar-button > a {
background-color: var(--main-background-color); background-color: var(--sidebar-background-color);
border-color: var(--border-color);
} }
#settings-menu > a:hover, #settings-menu > a:focus-visible, #search-button > a:hover, #search-button > a:focus-visible,
#help-button > a:hover, #help-button > a:focus-visible, .settings-menu > a:hover, .settings-menu > a:focus-visible,
.help-menu > a:hover, #help-menu > a:focus-visible,
#sidebar-button > a:hover, #sidebar-button > a:focus-visible,
#copy-path:hover, #copy-path:focus-visible,
button#toggle-all-docs:hover, button#toggle-all-docs:focus-visible { button#toggle-all-docs:hover, button#toggle-all-docs:focus-visible {
border-color: var(--settings-button-border-focus); border-color: var(--settings-button-border-focus);
text-decoration: none; text-decoration: none;
} }
#settings-menu > a::before { #search-button > a::before {
/* Magnifying glass */
content: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" \
width="18" height="18" viewBox="0 0 16 16">\
<circle r="5" cy="7" cx="7" style="fill:none;stroke:black;stroke-width:3"/><path \
d="M14.5,14.5 12,12" style="fill:none;stroke:black;stroke-width:3;stroke-linecap:round">\
</path><desc>Search</desc>\
</svg>');
width: 18px;
height: 18px;
filter: var(--settings-menu-filter);
}
.settings-menu > a::before {
/* Wheel <https://www.svgrepo.com/svg/384069/settings-cog-gear> */ /* Wheel <https://www.svgrepo.com/svg/384069/settings-cog-gear> */
content: url('data:image/svg+xml,<svg width="18" height="18" viewBox="0 0 12 12" \ content: url('data:image/svg+xml,<svg width="18" height="18" viewBox="0 0 12 12" \
enable-background="new 0 0 12 12" xmlns="http://www.w3.org/2000/svg">\ enable-background="new 0 0 12 12" xmlns="http://www.w3.org/2000/svg">\
<path d="M10.25,6c0-0.1243286-0.0261841-0.241333-0.0366211-0.362915l1.6077881-1.5545654l\ <path d="m4.75 0s-0.32117 1.286-0.53906 2.1576c-0.2276 0.1062-0.44625 \
-1.25-2.1650391 c0,0-1.2674561,0.3625488-2.1323853,0.6099854c-0.2034912-0.1431885-0.421875\ 0.2266-0.64974 0.36979l-2.1328-0.60938-1.25 2.1641s0.9644 0.93231 1.6081 1.5547c-0.010437 \
-0.2639771-0.6494751-0.3701782L7.25,0h-2.5 c0,0-0.3214111,1.2857666-0.5393066,2.1572876\ 0.12158-0.036458 0.23895-0.036458 0.36328s0.026021 0.2417 0.036458 0.36328l-1.6081 \
C3.9830933,2.2634888,3.7647095,2.3842773,3.5612183,2.5274658L1.428833,1.9174805 \ 1.5547 1.25 2.1641 2.1328-0.60937c0.20349 0.14325 0.42214 0.26359 0.64974 0.36979l0.53906 \
l-1.25,2.1650391c0,0,0.9641113,0.9321899,1.6077881,1.5545654C1.7761841,5.758667,\ 2.1576h2.5l0.53906-2.1576c0.2276-0.1062 0.44625-0.22654 0.64974-0.36979l2.1328 0.60937 \
1.75,5.8756714,1.75,6 s0.0261841,0.241333,0.0366211,0.362915L0.178833,7.9174805l1.25,\ 1.25-2.1641-1.6081-1.5547c0.010437-0.12158 0.036458-0.23895 \
2.1650391l2.1323853-0.6099854 c0.2034912,0.1432495,0.421875,0.2639771,0.6494751,0.3701782\ 0.036458-0.36328s-0.02602-0.2417-0.03646-0.36328l1.6081-1.5547-1.25-2.1641s-1.2679 \
L4.75,12h2.5l0.5393066-2.1572876 c0.2276001-0.1062012,0.4459839-0.2269287,0.6494751\ 0.36194-2.1328 0.60938c-0.20349-0.14319-0.42214-0.26359-0.64974-0.36979l-0.53906-2.1576\
-0.3701782l2.1323853,0.6099854l1.25-2.1650391L10.2133789,6.362915 C10.2238159,6.241333,\ zm1.25 2.5495c1.9058-2.877e-4 3.4508 1.5447 3.4505 3.4505 2.877e-4 1.9058-1.5447 3.4508-3.4505 \
10.25,6.1243286,10.25,6z M6,7.5C5.1715698,7.5,4.5,6.8284302,4.5,6S5.1715698,4.5,6,4.5S7.5\ 3.4505-1.9058 2.877e-4 -3.4508-1.5447-3.4505-3.4505-2.877e-4 -1.9058 1.5447-3.4508 \
,5.1715698,7.5,6 S6.8284302,7.5,6,7.5z" fill="black"/></svg>'); 3.4505-3.4505z" fill="black"/>\
<circle cx="6" cy="6" r="1.75" fill="none" stroke="black" stroke-width="1"/></svg>');
width: 18px; width: 18px;
height: 18px; height: 18px;
filter: var(--settings-menu-filter); filter: var(--settings-menu-filter);
@@ -2067,36 +2176,51 @@ button#toggle-all-docs::before {
filter: var(--settings-menu-filter); filter: var(--settings-menu-filter);
} }
button#toggle-all-docs.will-expand::before { .help-menu > a::before {
/* Custom arrow icon */ /* Question mark with "circle" */
content: url('data:image/svg+xml,<svg width="18" height="18" viewBox="0 0 12 12" \ content: url('data:image/svg+xml,\
enable-background="new 0 0 12 12" xmlns="http://www.w3.org/2000/svg">\ <svg width="18" height="18" enable-background="new 0 0 12 12" fill="none" \
<path d="M2,5l4,-4l4,4M2,7l4,4l4,-4" stroke="black" fill="none" stroke-width="2px"/></svg>'); version="1.1" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg"> \
} <path d="m6.007 0.6931c2.515 0 5.074 1.908 5.074 5.335 0 3.55-2.567 5.278-5.088 \
5.278-2.477 0-5.001-1.742-5.001-5.3 0-3.38 2.527-5.314 5.014-5.314z" stroke="black" \
#help-button > a::before { stroke-width="1.5"/>\
/* Question mark with circle */ <path d="m5.999 7.932c0.3111 0 0.7062 0.2915 0.7062 0.7257 0 0.5458-0.3951 \
content: url('data:image/svg+xml,<svg width="18" height="18" viewBox="0 0 12 12" \ 0.8099-0.7081 0.8099-0.2973 0-0.7023-0.266-0.7023-0.7668 0-0.4695 0.3834-0.7688 \
enable-background="new 0 0 12 12" xmlns="http://www.w3.org/2000/svg" fill="none">\ 0.7042-0.7688z" fill="black"/>\
<circle r="5.25" cx="6" cy="6" stroke-width="1.25" stroke="black"/>\ <path d="m4.281 3.946c0.0312-0.03057 0.06298-0.06029 0.09528-0.08916 0.4833-0.432 1.084-0.6722 \
<text x="6" y="7" style="font:8px sans-serif;font-weight:1000" text-anchor="middle" \ 1.634-0.6722 1.141 0 1.508 1.043 1.221 1.621-0.2753 0.5542-1.061 0.5065-1.273 \
dominant-baseline="middle" fill="black">?</text></svg>'); 1.595-0.05728 0.2939 0.0134 0.9812 0.0134 1.205" fill="none" stroke="black" \
stroke-width="1.25"/>\
</svg>');
width: 18px; width: 18px;
height: 18px; height: 18px;
filter: var(--settings-menu-filter); filter: var(--settings-menu-filter);
} }
/* design hack to cope with "Help" being far shorter than "Settings" etc */
.help-menu > a {
width: 74px;
}
.help-menu > a > .label {
padding-right: 1px;
}
#toggle-all-docs:not(.will-expand) > .label {
padding-left: 1px;
}
#search-button > a::before,
button#toggle-all-docs::before, button#toggle-all-docs::before,
#help-button > a::before, .help-menu > a::before,
#settings-menu > a::before { .settings-menu > a::before {
filter: var(--settings-menu-filter); filter: var(--settings-menu-filter);
margin: 8px; margin: 8px;
} }
@media not (pointer: coarse) { @media not (pointer: coarse) {
#search-button > a:hover::before,
button#toggle-all-docs:hover::before, button#toggle-all-docs:hover::before,
#help-button > a:hover::before, .help-menu > a:hover::before,
#settings-menu > a:hover::before { .settings-menu > a:hover::before {
filter: var(--settings-menu-hover-filter); filter: var(--settings-menu-hover-filter);
} }
} }
@@ -2122,9 +2246,9 @@ rustdoc-toolbar span.label {
/* sidebar resizer image */ /* sidebar resizer image */
content: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22" \ content: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22" \
fill="none" stroke="black">\ fill="none" stroke="black">\
<rect x="1" y="1" width="20" height="20" ry="1.5" stroke-width="1.5" stroke="%23777"/>\ <rect x="1" y="2" width="20" height="18" ry="1.5" stroke-width="1.5" stroke="%23777"/>\
<circle cx="4.375" cy="4.375" r="1" stroke-width=".75"/>\ <circle cx="4.375" cy="5.375" r="1" stroke-width=".75"/>\
<path d="m7.6121 3v16 M5.375 7.625h-2 m2 3h-2 m2 3h-2" stroke-width="1.25"/></svg>'); <path d="m7.6121 4v14 M5.375 8.625h-2 m2 3h-2 m2 3h-2" stroke-width="1.25"/></svg>');
width: 22px; width: 22px;
height: 22px; height: 22px;
} }
@@ -2137,7 +2261,8 @@ rustdoc-toolbar span.label {
margin-left: 10px; margin-left: 10px;
padding: 0; padding: 0;
padding-left: 2px; padding-left: 2px;
border: 0; border: solid 1px transparent;
border-radius: var(--button-border-radius);
font-size: 0; font-size: 0;
} }
#copy-path::before { #copy-path::before {
@@ -2159,7 +2284,7 @@ rustdoc-toolbar span.label {
transform: rotate(360deg); transform: rotate(360deg);
} }
} }
#settings-menu.rotate > a img { .settings-menu.rotate > a img {
animation: rotating 2s linear infinite; animation: rotating 2s linear infinite;
} }
@@ -2402,6 +2527,9 @@ However, it's not needed with smaller screen width because the doc/code block is
opacity: 0.75; opacity: 0.75;
filter: var(--mobile-sidebar-menu-filter); filter: var(--mobile-sidebar-menu-filter);
} }
.src #sidebar-button > a:hover {
background: var(--main-background-color);
}
.sidebar-menu-toggle:hover::before, .sidebar-menu-toggle:hover::before,
.sidebar-menu-toggle:active::before, .sidebar-menu-toggle:active::before,
.sidebar-menu-toggle:focus::before { .sidebar-menu-toggle:focus::before {
@@ -2410,8 +2538,8 @@ However, it's not needed with smaller screen width because the doc/code block is
/* Media Queries */ /* Media Queries */
/* Make sure all the buttons line wrap at the same time */
@media (max-width: 850px) { @media (max-width: 850px) {
/* Make sure all the buttons line wrap at the same time */
#search-tabs .count { #search-tabs .count {
display: block; display: block;
} }
@@ -2421,6 +2549,81 @@ However, it's not needed with smaller screen width because the doc/code block is
.side-by-side > div { .side-by-side > div {
width: auto; width: auto;
} }
/* Text label takes up too much space at this size. */
.main-heading {
grid-template-areas:
"main-heading-breadcrumbs main-heading-toolbar"
"main-heading-h1 main-heading-toolbar"
"main-heading-sub-heading main-heading-toolbar";
}
.search-results-main-heading {
display: grid;
grid-template-areas:
"main-heading-breadcrumbs main-heading-toolbar"
"main-heading-breadcrumbs main-heading-toolbar"
"main-heading-h1 main-heading-toolbar";
}
rustdoc-toolbar {
margin-top: -10px;
display: grid;
grid-template-areas:
"x settings help"
"search summary summary";
grid-template-rows: 35px 1fr;
}
.search-results-main-heading rustdoc-toolbar {
display: grid;
grid-template-areas:
"settings help"
"search search";
}
.search-results-main-heading #toggle-all-docs {
display: none;
}
rustdoc-toolbar .settings-menu span.label,
rustdoc-toolbar .help-menu span.label
{
display: none;
}
rustdoc-toolbar .settings-menu {
grid-area: settings;
}
rustdoc-toolbar .help-menu {
grid-area: help;
}
rustdoc-toolbar .settings-menu {
grid-area: settings;
}
rustdoc-toolbar #search-button {
grid-area: search;
}
rustdoc-toolbar #toggle-all-docs {
grid-area: summary;
}
rustdoc-toolbar .settings-menu,
rustdoc-toolbar .help-menu {
height: 35px;
}
rustdoc-toolbar .settings-menu > a,
rustdoc-toolbar .help-menu > a {
border-radius: 2px;
text-align: center;
width: 34px;
padding: 5px 0;
}
rustdoc-toolbar .settings-menu > a:before,
rustdoc-toolbar .help-menu > a:before {
margin: 0 4px;
}
#settings.popover {
top: 16px;
--popover-arrow-offset: 58px;
}
#help.popover {
top: 16px;
--popover-arrow-offset: 16px;
}
} }
/* /*
@@ -2435,7 +2638,7 @@ in src-script.js and main.js
/* When linking to an item with an `id` (for instance, by clicking a link in the sidebar, /* When linking to an item with an `id` (for instance, by clicking a link in the sidebar,
or visiting a URL with a fragment like `#method.new`, we don't want the item to be obscured or visiting a URL with a fragment like `#method.new`, we don't want the item to be obscured
by the topbar. Anything with an `id` gets scroll-margin-top equal to .mobile-topbar's size. by the topbar. Anything with an `id` gets scroll-margin-top equal to rustdoc-topbar's size.
*/ */
*[id] { *[id] {
scroll-margin-top: 45px; scroll-margin-top: 45px;
@@ -2451,18 +2654,32 @@ in src-script.js and main.js
visibility: hidden; visibility: hidden;
} }
/* Text label takes up too much space at this size. */
rustdoc-toolbar span.label { /* Pull settings and help up into the top bar. */
rustdoc-topbar span.label,
html:not(.hide-sidebar) .rustdoc:not(.src) rustdoc-toolbar .settings-menu > a,
html:not(.hide-sidebar) .rustdoc:not(.src) rustdoc-toolbar .help-menu > a
{
display: none; display: none;
} }
#settings-menu > a, #help-button > a, button#toggle-all-docs { rustdoc-topbar .settings-menu > a,
rustdoc-topbar .help-menu > a {
width: 33px; width: 33px;
line-height: 0;
}
rustdoc-topbar .settings-menu > a:hover,
rustdoc-topbar .help-menu > a:hover {
border: none;
background: var(--main-background-color);
border-radius: 0;
} }
#settings.popover { #settings.popover {
--popover-arrow-offset: 86px; top: 32px;
--popover-arrow-offset: 48px;
} }
#help.popover { #help.popover {
--popover-arrow-offset: 48px; top: 32px;
--popover-arrow-offset: 12px;
} }
.rustdoc { .rustdoc {
@@ -2471,13 +2688,13 @@ in src-script.js and main.js
display: block; display: block;
} }
main { html:not(.hide-sidebar) main {
padding-left: 15px; padding-left: 15px;
padding-top: 0px; padding-top: 0px;
} }
/* Hide the logo and item name from the sidebar. Those are displayed /* Hide the logo and item name from the sidebar. Those are displayed
in the mobile-topbar instead. */ in the rustdoc-topbar instead. */
.sidebar .logo-container, .sidebar .logo-container,
.sidebar .location, .sidebar .location,
.sidebar-resizer { .sidebar-resizer {
@@ -2510,6 +2727,9 @@ in src-script.js and main.js
height: 100vh; height: 100vh;
border: 0; border: 0;
} }
html .src main {
padding: 18px 0;
}
.src .search-form { .src .search-form {
margin-left: 40px; margin-left: 40px;
} }
@@ -2529,9 +2749,9 @@ in src-script.js and main.js
left: 0; left: 0;
} }
.mobile-topbar h2 { rustdoc-topbar > h2 {
padding-bottom: 0; padding-bottom: 0;
margin: auto 0.5em auto auto; margin: auto;
overflow: hidden; overflow: hidden;
/* Rare exception to specifying font sizes in rem. Since the topbar /* Rare exception to specifying font sizes in rem. Since the topbar
height is specified in pixels, this also has to be specified in height is specified in pixels, this also has to be specified in
@@ -2540,32 +2760,34 @@ in src-script.js and main.js
font-size: 24px; font-size: 24px;
white-space: nowrap; white-space: nowrap;
text-overflow: ellipsis; text-overflow: ellipsis;
text-align: center;
} }
.mobile-topbar .logo-container > img { rustdoc-topbar .logo-container > img {
max-width: 35px; max-width: 35px;
max-height: 35px; max-height: 35px;
margin: 5px 0 5px 20px; margin: 5px 0 5px 20px;
} }
.mobile-topbar { rustdoc-topbar {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
position: sticky; position: sticky;
z-index: 10; z-index: 10;
font-size: 2rem;
height: 45px; height: 45px;
width: 100%; width: 100%;
left: 0; left: 0;
top: 0; top: 0;
} }
.hide-sidebar .mobile-topbar { .hide-sidebar rustdoc-topbar {
display: none; display: none;
} }
.sidebar-menu-toggle { .sidebar-menu-toggle {
width: 45px; /* prevent flexbox shrinking */
width: 41px;
min-width: 41px;
border: none; border: none;
line-height: 0; line-height: 0;
} }
@@ -2591,9 +2813,13 @@ in src-script.js and main.js
#sidebar-button > a::before { #sidebar-button > a::before {
content: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" \ content: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" \
viewBox="0 0 22 22" fill="none" stroke="black">\ viewBox="0 0 22 22" fill="none" stroke="black">\
<rect x="1" y="1" width="20" height="20" ry="1.5" stroke-width="1.5" stroke="%23777"/>\ <rect x="1" y="2" width="20" height="18" ry="1.5" stroke-width="1.5" stroke="%23777"/>\
<circle cx="4.375" cy="4.375" r="1" stroke-width=".75"/>\ <g fill="black" stroke="none">\
<path d="m3 7.375h16m0-3h-4" stroke-width="1.25"/></svg>'); <circle cx="4.375" cy="5.375" r="1" stroke-width=".75"/>\
<circle cx="17.375" cy="5.375" r="1" stroke-width=".75"/>\
<circle cx="14.375" cy="5.375" r="1" stroke-width=".75"/>\
</g>\
<path d="m3 8.375h16" stroke-width="1.25"/></svg>');
width: 22px; width: 22px;
height: 22px; height: 22px;
} }
@@ -3283,7 +3509,7 @@ Original by Dempfi (https://github.com/dempfi/ayu)
border-bottom: 1px solid rgba(242, 151, 24, 0.3); border-bottom: 1px solid rgba(242, 151, 24, 0.3);
} }
:root[data-theme="ayu"] #settings-menu > a img, :root[data-theme="ayu"] .settings-menu > a img,
:root[data-theme="ayu"] #sidebar-button > a::before { :root[data-theme="ayu"] #sidebar-button > a::before {
filter: invert(100); filter: invert(100);
} }

View File

@@ -54,23 +54,6 @@ function showMain() {
window.rootPath = getVar("root-path"); window.rootPath = getVar("root-path");
window.currentCrate = getVar("current-crate"); window.currentCrate = getVar("current-crate");
function setMobileTopbar() {
// FIXME: It would be nicer to generate this text content directly in HTML,
// but with the current code it's hard to get the right information in the right place.
const mobileTopbar = document.querySelector(".mobile-topbar");
const locationTitle = document.querySelector(".sidebar h2.location");
if (mobileTopbar) {
const mobileTitle = document.createElement("h2");
mobileTitle.className = "location";
if (hasClass(document.querySelector(".rustdoc"), "crate")) {
mobileTitle.innerHTML = `Crate <a href="#">${window.currentCrate}</a>`;
} else if (locationTitle) {
mobileTitle.innerHTML = locationTitle.innerHTML;
}
mobileTopbar.appendChild(mobileTitle);
}
}
/** /**
* Gets the human-readable string for the virtual-key code of the * Gets the human-readable string for the virtual-key code of the
* given KeyboardEvent, ev. * given KeyboardEvent, ev.
@@ -84,6 +67,7 @@ function setMobileTopbar() {
* So I guess you could say things are getting pretty interoperable. * So I guess you could say things are getting pretty interoperable.
* *
* @param {KeyboardEvent} ev * @param {KeyboardEvent} ev
* @returns {string}
*/ */
function getVirtualKey(ev) { function getVirtualKey(ev) {
if ("key" in ev && typeof ev.key !== "undefined") { if ("key" in ev && typeof ev.key !== "undefined") {
@@ -98,18 +82,8 @@ function getVirtualKey(ev) {
} }
const MAIN_ID = "main-content"; const MAIN_ID = "main-content";
const SETTINGS_BUTTON_ID = "settings-menu";
const ALTERNATIVE_DISPLAY_ID = "alternative-display"; const ALTERNATIVE_DISPLAY_ID = "alternative-display";
const NOT_DISPLAYED_ID = "not-displayed"; const NOT_DISPLAYED_ID = "not-displayed";
const HELP_BUTTON_ID = "help-button";
function getSettingsButton() {
return document.getElementById(SETTINGS_BUTTON_ID);
}
function getHelpButton() {
return document.getElementById(HELP_BUTTON_ID);
}
// Returns the current URL without any query parameter or hash. // Returns the current URL without any query parameter or hash.
function getNakedUrl() { function getNakedUrl() {
@@ -174,7 +148,7 @@ function getNotDisplayedElem() {
* contains the displayed element (there can be only one at the same time!). So basically, we switch * contains the displayed element (there can be only one at the same time!). So basically, we switch
* elements between the two `<section>` elements. * elements between the two `<section>` elements.
* *
* @param {HTMLElement|null} elemToDisplay * @param {Element|null} elemToDisplay
*/ */
function switchDisplayedElement(elemToDisplay) { function switchDisplayedElement(elemToDisplay) {
const el = getAlternativeDisplayElem(); const el = getAlternativeDisplayElem();
@@ -239,14 +213,14 @@ function preLoadCss(cssUrl) {
document.head.append(script); document.head.append(script);
} }
const settingsButton = getSettingsButton(); onEachLazy(document.querySelectorAll(".settings-menu"), settingsMenu => {
if (settingsButton) { /** @param {MouseEvent} event */
settingsButton.onclick = event => { settingsMenu.querySelector("a").onclick = event => {
if (event.ctrlKey || event.altKey || event.metaKey) { if (event.ctrlKey || event.altKey || event.metaKey) {
return; return;
} }
window.hideAllModals(false); window.hideAllModals(false);
addClass(getSettingsButton(), "rotate"); addClass(settingsMenu, "rotate");
event.preventDefault(); event.preventDefault();
// Sending request for the CSS and the JS files at the same time so it will // Sending request for the CSS and the JS files at the same time so it will
// hopefully be loaded when the JS will generate the settings content. // hopefully be loaded when the JS will generate the settings content.
@@ -268,15 +242,42 @@ function preLoadCss(cssUrl) {
} }
}, 0); }, 0);
}; };
} });
window.searchState = { window.searchState = {
rustdocToolbar: document.querySelector("rustdoc-toolbar"), rustdocToolbar: document.querySelector("rustdoc-toolbar"),
loadingText: "Loading search results...", loadingText: "Loading search results...",
// This will always be an HTMLInputElement, but tsc can't see that inputElement: () => {
// @ts-expect-error let el = document.getElementsByClassName("search-input")[0];
input: document.getElementsByClassName("search-input")[0], if (!el) {
outputElement: () => { const out = nonnull(nonnull(window.searchState.outputElement()).parentElement);
const hdr = document.createElement("div");
hdr.className = "main-heading search-results-main-heading";
const params = window.searchState.getQueryStringParams();
const autofocusParam = params.search === "" ? "autofocus" : "";
hdr.innerHTML = `<nav class="sub">
<form class="search-form loading">
<span></span> <!-- This empty span is a hacky fix for Safari: see #93184 -->
<input
${autofocusParam}
class="search-input"
name="search"
aria-label="Run search in the documentation"
autocomplete="off"
spellcheck="false"
placeholder="Type S or / to search, ? for more options…"
type="search">
</form>
</nav><div class="search-switcher"></div>`;
out.insertBefore(hdr, window.searchState.outputElement());
el = document.getElementsByClassName("search-input")[0];
}
if (el instanceof HTMLInputElement) {
return el;
}
return null;
},
containerElement: () => {
let el = document.getElementById("search"); let el = document.getElementById("search");
if (!el) { if (!el) {
el = document.createElement("section"); el = document.createElement("section");
@@ -285,6 +286,19 @@ function preLoadCss(cssUrl) {
} }
return el; return el;
}, },
outputElement: () => {
const container = window.searchState.containerElement();
if (!container) {
return null;
}
let el = container.querySelector(".search-out");
if (!el) {
el = document.createElement("div");
el.className = "search-out";
container.appendChild(el);
}
return el;
},
title: document.title, title: document.title,
titleBeforeSearch: document.title, titleBeforeSearch: document.title,
timeout: null, timeout: null,
@@ -303,25 +317,52 @@ function preLoadCss(cssUrl) {
} }
}, },
isDisplayed: () => { isDisplayed: () => {
const outputElement = window.searchState.outputElement(); const container = window.searchState.containerElement();
return !!outputElement && if (!container) {
!!outputElement.parentElement && return false;
outputElement.parentElement.id === ALTERNATIVE_DISPLAY_ID; }
return !!container.parentElement && container.parentElement.id ===
ALTERNATIVE_DISPLAY_ID;
}, },
// Sets the focus on the search bar at the top of the page // Sets the focus on the search bar at the top of the page
focus: () => { focus: () => {
window.searchState.input && window.searchState.input.focus(); const inputElement = window.searchState.inputElement();
window.searchState.showResults();
if (inputElement) {
inputElement.focus();
// Avoid glitch if something focuses the search button after clicking.
requestAnimationFrame(() => inputElement.focus());
}
}, },
// Removes the focus from the search bar. // Removes the focus from the search bar.
defocus: () => { defocus: () => {
window.searchState.input && window.searchState.input.blur(); nonnull(window.searchState.inputElement()).blur();
}, },
showResults: search => { toggle: () => {
if (search === null || typeof search === "undefined") { if (window.searchState.isDisplayed()) {
search = window.searchState.outputElement(); window.searchState.defocus();
window.searchState.hideResults();
} else {
window.searchState.focus();
} }
switchDisplayedElement(search); },
showResults: () => {
document.title = window.searchState.title; document.title = window.searchState.title;
if (window.searchState.isDisplayed()) {
return;
}
const search = window.searchState.containerElement();
switchDisplayedElement(search);
const btn = document.querySelector("#search-button a");
if (browserSupportsHistoryApi() && btn instanceof HTMLAnchorElement &&
window.searchState.getQueryStringParams().search === undefined
) {
history.pushState(null, "", btn.href);
}
const btnLabel = document.querySelector("#search-button a span.label");
if (btnLabel) {
btnLabel.innerHTML = "Exit";
}
}, },
removeQueryParameters: () => { removeQueryParameters: () => {
// We change the document title. // We change the document title.
@@ -334,6 +375,10 @@ function preLoadCss(cssUrl) {
switchDisplayedElement(null); switchDisplayedElement(null);
// We also remove the query parameter from the URL. // We also remove the query parameter from the URL.
window.searchState.removeQueryParameters(); window.searchState.removeQueryParameters();
const btnLabel = document.querySelector("#search-button a span.label");
if (btnLabel) {
btnLabel.innerHTML = "Search";
}
}, },
getQueryStringParams: () => { getQueryStringParams: () => {
/** @type {Object.<any, string>} */ /** @type {Object.<any, string>} */
@@ -348,11 +393,11 @@ function preLoadCss(cssUrl) {
return params; return params;
}, },
setup: () => { setup: () => {
const search_input = window.searchState.input; let searchLoaded = false;
const search_input = window.searchState.inputElement();
if (!search_input) { if (!search_input) {
return; return;
} }
let searchLoaded = false;
// If you're browsing the nightly docs, the page might need to be refreshed for the // If you're browsing the nightly docs, the page might need to be refreshed for the
// search to work because the hash of the JS scripts might have changed. // search to work because the hash of the JS scripts might have changed.
function sendSearchForm() { function sendSearchForm() {
@@ -363,21 +408,102 @@ function preLoadCss(cssUrl) {
if (!searchLoaded) { if (!searchLoaded) {
searchLoaded = true; searchLoaded = true;
// @ts-expect-error // @ts-expect-error
loadScript(getVar("static-root-path") + getVar("search-js"), sendSearchForm); window.rr_ = data => {
loadScript(resourcePath("search-index", ".js"), sendSearchForm); // @ts-expect-error
window.searchIndex = data;
};
if (!window.StringdexOnload) {
window.StringdexOnload = [];
}
window.StringdexOnload.push(() => {
loadScript(
// @ts-expect-error
getVar("static-root-path") + getVar("search-js"),
sendSearchForm,
);
});
// @ts-expect-error
loadScript(getVar("static-root-path") + getVar("stringdex-js"), sendSearchForm);
loadScript(resourcePath("search.index/root", ".js"), sendSearchForm);
} }
} }
search_input.addEventListener("focus", () => { search_input.addEventListener("focus", () => {
window.searchState.origPlaceholder = search_input.placeholder;
search_input.placeholder = "Type your search here.";
loadSearch(); loadSearch();
}); });
if (search_input.value !== "") { const btn = document.getElementById("search-button");
loadSearch(); if (btn) {
btn.onclick = event => {
if (event.ctrlKey || event.altKey || event.metaKey) {
return;
}
event.preventDefault();
window.searchState.toggle();
loadSearch();
};
} }
// Push and pop states are used to add search results to the browser
// history.
if (browserSupportsHistoryApi()) {
// Store the previous <title> so we can revert back to it later.
const previousTitle = document.title;
window.addEventListener("popstate", e => {
const params = window.searchState.getQueryStringParams();
// Revert to the previous title manually since the History
// API ignores the title parameter.
document.title = previousTitle;
// Synchronize search bar with query string state and
// perform the search. This will empty the bar if there's
// nothing there, which lets you really go back to a
// previous state with nothing in the bar.
const inputElement = window.searchState.inputElement();
if (params.search !== undefined && inputElement !== null) {
loadSearch();
inputElement.value = params.search;
// Some browsers fire "onpopstate" for every page load
// (Chrome), while others fire the event only when actually
// popping a state (Firefox), which is why search() is
// called both here and at the end of the startSearch()
// function.
e.preventDefault();
window.searchState.showResults();
if (params.search === "") {
window.searchState.focus();
}
} else {
// When browsing back from search results the main page
// visibility must be reset.
window.searchState.hideResults();
}
});
}
// This is required in firefox to avoid this problem: Navigating to a search result
// with the keyboard, hitting enter, and then hitting back would take you back to
// the doc page, rather than the search that should overlay it.
// This was an interaction between the back-forward cache and our handlers
// that try to sync state between the URL and the search input. To work around it,
// do a small amount of re-init on page show.
window.onpageshow = () => {
const inputElement = window.searchState.inputElement();
const qSearch = window.searchState.getQueryStringParams().search;
if (qSearch !== undefined && inputElement !== null) {
if (inputElement.value === "") {
inputElement.value = qSearch;
}
window.searchState.showResults();
if (qSearch === "") {
loadSearch();
window.searchState.focus();
}
} else {
window.searchState.hideResults();
}
};
const params = window.searchState.getQueryStringParams(); const params = window.searchState.getQueryStringParams();
if (params.search !== undefined) { if (params.search !== undefined) {
window.searchState.setLoadingSearch(); window.searchState.setLoadingSearch();
@@ -386,13 +512,9 @@ function preLoadCss(cssUrl) {
}, },
setLoadingSearch: () => { setLoadingSearch: () => {
const search = window.searchState.outputElement(); const search = window.searchState.outputElement();
if (!search) { nonnull(search).innerHTML = "<h3 class=\"search-loading\">" +
return; window.searchState.loadingText + "</h3>";
} window.searchState.showResults();
search.innerHTML = "<h3 class=\"search-loading\">" +
window.searchState.loadingText +
"</h3>";
window.searchState.showResults(search);
}, },
descShards: new Map(), descShards: new Map(),
loadDesc: async function({descShard, descIndex}) { loadDesc: async function({descShard, descIndex}) {
@@ -1500,15 +1622,13 @@ function preLoadCss(cssUrl) {
// @ts-expect-error // @ts-expect-error
function helpBlurHandler(event) { function helpBlurHandler(event) {
// @ts-expect-error const isInPopover = onEachLazy(
if (!getHelpButton().contains(document.activeElement) && document.querySelectorAll(".settings-menu, .help-menu"),
// @ts-expect-error menu => {
!getHelpButton().contains(event.relatedTarget) && return menu.contains(document.activeElement) || menu.contains(event.relatedTarget);
// @ts-expect-error },
!getSettingsButton().contains(document.activeElement) && );
// @ts-expect-error if (!isInPopover) {
!getSettingsButton().contains(event.relatedTarget)
) {
window.hidePopoverMenus(); window.hidePopoverMenus();
} }
} }
@@ -1571,10 +1691,9 @@ function preLoadCss(cssUrl) {
const container = document.createElement("div"); const container = document.createElement("div");
if (!isHelpPage) { if (!isHelpPage) {
container.className = "popover"; container.className = "popover content";
} }
container.id = "help"; container.id = "help";
container.style.display = "none";
const side_by_side = document.createElement("div"); const side_by_side = document.createElement("div");
side_by_side.className = "side-by-side"; side_by_side.className = "side-by-side";
@@ -1590,17 +1709,16 @@ function preLoadCss(cssUrl) {
help_section.appendChild(container); help_section.appendChild(container);
// @ts-expect-error // @ts-expect-error
document.getElementById("main-content").appendChild(help_section); document.getElementById("main-content").appendChild(help_section);
container.style.display = "block";
} else { } else {
const help_button = getHelpButton(); onEachLazy(document.getElementsByClassName("help-menu"), menu => {
// @ts-expect-error if (menu.offsetWidth !== 0) {
help_button.appendChild(container); menu.appendChild(container);
container.onblur = helpBlurHandler;
container.onblur = helpBlurHandler; menu.onblur = helpBlurHandler;
// @ts-expect-error menu.children[0].onblur = helpBlurHandler;
help_button.onblur = helpBlurHandler; return true;
// @ts-expect-error }
help_button.children[0].onblur = helpBlurHandler; });
} }
return container; return container;
@@ -1621,80 +1739,57 @@ function preLoadCss(cssUrl) {
* Hide all the popover menus. * Hide all the popover menus.
*/ */
window.hidePopoverMenus = () => { window.hidePopoverMenus = () => {
onEachLazy(document.querySelectorAll("rustdoc-toolbar .popover"), elem => { onEachLazy(document.querySelectorAll(".settings-menu .popover"), elem => {
elem.style.display = "none"; elem.style.display = "none";
}); });
const button = getHelpButton(); onEachLazy(document.querySelectorAll(".help-menu .popover"), elem => {
if (button) { elem.parentElement.removeChild(elem);
removeClass(button, "help-open"); });
}
}; };
/**
* Returns the help menu element (not the button).
*
* @param {boolean} buildNeeded - If this argument is `false`, the help menu element won't be
* built if it doesn't exist.
*
* @return {HTMLElement}
*/
function getHelpMenu(buildNeeded) {
// @ts-expect-error
let menu = getHelpButton().querySelector(".popover");
if (!menu && buildNeeded) {
menu = buildHelpMenu();
}
// @ts-expect-error
return menu;
}
/** /**
* Show the help popup menu. * Show the help popup menu.
*/ */
function showHelp() { function showHelp() {
window.hideAllModals(false);
// Prevent `blur` events from being dispatched as a result of closing // Prevent `blur` events from being dispatched as a result of closing
// other modals. // other modals.
const button = getHelpButton(); onEachLazy(document.querySelectorAll(".help-menu a"), menu => {
addClass(button, "help-open"); if (menu.offsetWidth !== 0) {
// @ts-expect-error menu.focus();
button.querySelector("a").focus(); return true;
const menu = getHelpMenu(true); }
if (menu.style.display === "none") { });
// @ts-expect-error buildHelpMenu();
window.hideAllModals();
menu.style.display = "";
}
} }
const helpLink = document.querySelector(`#${HELP_BUTTON_ID} > a`);
if (isHelpPage) { if (isHelpPage) {
buildHelpMenu(); buildHelpMenu();
} else if (helpLink) { } else {
helpLink.addEventListener("click", event => { onEachLazy(document.querySelectorAll(".help-menu > a"), helpLink => {
// By default, have help button open docs in a popover. helpLink.addEventListener(
// If user clicks with a moderator, though, use default browser behavior, "click",
// probably opening in a new window or tab. /** @param {MouseEvent} event */
if (!helpLink.contains(helpLink) || event => {
// @ts-expect-error // By default, have help button open docs in a popover.
event.ctrlKey || // If user clicks with a moderator, though, use default browser behavior,
// @ts-expect-error // probably opening in a new window or tab.
event.altKey || if (event.ctrlKey ||
// @ts-expect-error event.altKey ||
event.metaKey) { event.metaKey) {
return; return;
} }
event.preventDefault(); event.preventDefault();
const menu = getHelpMenu(true); if (document.getElementById("help")) {
const shouldShowHelp = menu.style.display === "none"; window.hidePopoverMenus();
if (shouldShowHelp) { } else {
showHelp(); showHelp();
} else { }
window.hidePopoverMenus(); },
} );
}); });
} }
setMobileTopbar();
addSidebarItems(); addSidebarItems();
addSidebarCrates(); addSidebarCrates();
onHashChange(null); onHashChange(null);
@@ -1746,7 +1841,15 @@ function preLoadCss(cssUrl) {
// On larger, "desktop-sized" viewports (though that includes many // On larger, "desktop-sized" viewports (though that includes many
// tablets), it's fixed-position, appears in the left side margin, // tablets), it's fixed-position, appears in the left side margin,
// and it can be activated by resizing the sidebar into nothing. // and it can be activated by resizing the sidebar into nothing.
const sidebarButton = document.getElementById("sidebar-button"); let sidebarButton = document.getElementById("sidebar-button");
const body = document.querySelector(".main-heading");
if (!sidebarButton && body) {
sidebarButton = document.createElement("div");
sidebarButton.id = "sidebar-button";
const path = `${window.rootPath}${window.currentCrate}/all.html`;
sidebarButton.innerHTML = `<a href="${path}" title="show sidebar"></a>`;
body.insertBefore(sidebarButton, body.firstChild);
}
if (sidebarButton) { if (sidebarButton) {
sidebarButton.addEventListener("click", e => { sidebarButton.addEventListener("click", e => {
removeClass(document.documentElement, "hide-sidebar"); removeClass(document.documentElement, "hide-sidebar");

View File

@@ -2,6 +2,8 @@
// not put into the JavaScript we include as part of the documentation. It is used for // not put into the JavaScript we include as part of the documentation. It is used for
// type checking. See README.md in this directory for more info. // type checking. See README.md in this directory for more info.
import { RoaringBitmap } from "./stringdex";
/* eslint-disable */ /* eslint-disable */
declare global { declare global {
/** Search engine data used by main.js and search.js */ /** Search engine data used by main.js and search.js */
@@ -10,6 +12,17 @@ declare global {
declare function nonnull(x: T|null, msg: string|undefined); declare function nonnull(x: T|null, msg: string|undefined);
/** Defined and documented in `storage.js` */ /** Defined and documented in `storage.js` */
declare function nonundef(x: T|undefined, msg: string|undefined); declare function nonundef(x: T|undefined, msg: string|undefined);
interface PromiseConstructor {
/**
* Polyfill
* @template T
*/
withResolvers: function(): {
"promise": Promise<T>,
"resolve": (function(T): void),
"reject": (function(any): void)
};
}
interface Window { interface Window {
/** Make the current theme easy to find */ /** Make the current theme easy to find */
currentTheme: HTMLLinkElement|null; currentTheme: HTMLLinkElement|null;
@@ -95,29 +108,28 @@ declare namespace rustdoc {
interface SearchState { interface SearchState {
rustdocToolbar: HTMLElement|null; rustdocToolbar: HTMLElement|null;
loadingText: string; loadingText: string;
input: HTMLInputElement|null; inputElement: function(): HTMLInputElement|null;
containerElement: function(): Element|null;
title: string; title: string;
titleBeforeSearch: string; titleBeforeSearch: string;
timeout: number|null; timeout: ReturnType<typeof setTimeout>|null;
currentTab: number; currentTab: number;
focusedByTab: [number|null, number|null, number|null]; focusedByTab: [Element|null, Element|null, Element|null];
clearInputTimeout: function; clearInputTimeout: function;
outputElement(): HTMLElement|null; outputElement: function(): Element|null;
focus(); focus: function();
defocus(); defocus: function();
// note: an optional param is not the same as toggle: function();
// a nullable/undef-able param. showResults: function();
showResults(elem?: HTMLElement|null); removeQueryParameters: function();
removeQueryParameters(); hideResults: function();
hideResults(); getQueryStringParams: function(): Object.<any, string>;
getQueryStringParams(): Object.<any, string>;
origPlaceholder: string;
setup: function(); setup: function();
setLoadingSearch(); setLoadingSearch();
descShards: Map<string, SearchDescShard[]>; descShards: Map<string, SearchDescShard[]>;
loadDesc: function({descShard: SearchDescShard, descIndex: number}): Promise<string|null>; loadDesc: function({descShard: SearchDescShard, descIndex: number}): Promise<string|null>;
loadedDescShard(string, number, string); loadedDescShard: function(string, number, string);
isDisplayed(): boolean, isDisplayed: function(): boolean;
} }
interface SearchDescShard { interface SearchDescShard {
@@ -131,12 +143,13 @@ declare namespace rustdoc {
* A single parsed "atom" in a search query. For example, * A single parsed "atom" in a search query. For example,
* *
* std::fmt::Formatter, Write -> Result<()> * std::fmt::Formatter, Write -> Result<()>
* ┏━━━━━━━━━━━━━━━━━━ ┌──── ┏━━━━━┅┅┅┅┄┄┄┄┄┄┄┄┄┄┄┄┄┄┐ * ┏━━━━━━━━━━━━━━━━━━ ┌──── ┏━━━━━┅┅┅┅┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
* ┃ │ ┗ QueryElement { ┊ * ┃ │ ┗ QueryElement {
* ┃ │ name: Result ┊ * ┃ │ name: Result
* ┃ │ generics: [ ┊ * ┃ │ generics: [
* ┃ │ QueryElement ┘ * ┃ │ QueryElement {
* ┃ │ name: () * ┃ │ name: ()
* ┃ │ }
* ┃ │ ] * ┃ │ ]
* ┃ │ } * ┃ │ }
* ┃ └ QueryElement { * ┃ └ QueryElement {
@@ -156,14 +169,14 @@ declare namespace rustdoc {
normalizedPathLast: string, normalizedPathLast: string,
generics: Array<QueryElement>, generics: Array<QueryElement>,
bindings: Map<number, Array<QueryElement>>, bindings: Map<number, Array<QueryElement>>,
typeFilter: number|null, typeFilter: number,
} }
/** /**
* Same as QueryElement, but bindings and typeFilter support strings * Same as QueryElement, but bindings and typeFilter support strings
*/ */
interface ParserQueryElement { interface ParserQueryElement {
name: string|null, name: string,
id: number|null, id: number|null,
fullPath: Array<string>, fullPath: Array<string>,
pathWithoutLast: Array<string>, pathWithoutLast: Array<string>,
@@ -172,7 +185,7 @@ declare namespace rustdoc {
generics: Array<ParserQueryElement>, generics: Array<ParserQueryElement>,
bindings: Map<string, Array<ParserQueryElement>>, bindings: Map<string, Array<ParserQueryElement>>,
bindingName: {name: string|null, generics: ParserQueryElement[]}|null, bindingName: {name: string|null, generics: ParserQueryElement[]}|null,
typeFilter: number|string|null, typeFilter: string|null,
} }
/** /**
@@ -215,35 +228,74 @@ declare namespace rustdoc {
/** /**
* An entry in the search index database. * An entry in the search index database.
*/ */
interface EntryData {
krate: number,
ty: ItemType,
modulePath: number?,
exactModulePath: number?,
parent: number?,
deprecated: boolean,
associatedItemDisambiguator: string?,
}
/**
* A path in the search index database
*/
interface PathData {
ty: ItemType,
modulePath: string,
exactModulePath: string?,
}
/**
* A function signature in the search index database
*
* Note that some non-function items (eg. constants, struct fields) have a function signature so they can appear in type-based search.
*/
interface FunctionData {
functionSignature: FunctionSearchType|null,
paramNames: string[],
elemCount: number,
}
/**
* A function signature in the search index database
*/
interface TypeData {
searchUnbox: boolean,
invertedFunctionSignatureIndex: RoaringBitmap[],
}
/**
* A search entry of some sort.
*/
interface Row { interface Row {
crate: string,
descShard: SearchDescShard,
id: number, id: number,
// This is the name of the item. For doc aliases, if you want the name of the aliased crate: string,
// item, take a look at `Row.original.name`. ty: ItemType,
name: string, name: string,
normalizedName: string, normalizedName: string,
word: string, modulePath: string,
paramNames: string[], exactModulePath: string,
parent: ({ty: number, name: string, path: string, exactPath: string}|null|undefined), entry: EntryData?,
path: string, path: PathData?,
ty: number, type: FunctionData?,
type: FunctionSearchType | null, deprecated: boolean,
descIndex: number, parent: { path: PathData, name: string}?,
bitIndex: number,
implDisambiguator: String | null,
is_alias?: boolean,
original?: Row,
} }
type ItemType = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | 22 | 23 | 24 | 25 | 26;
/** /**
* The viewmodel for the search engine results page. * The viewmodel for the search engine results page.
*/ */
interface ResultsTable { interface ResultsTable {
in_args: Array<ResultObject>, in_args: AsyncGenerator<ResultObject>,
returned: Array<ResultObject>, returned: AsyncGenerator<ResultObject>,
others: Array<ResultObject>, others: AsyncGenerator<ResultObject>,
query: ParsedQuery, query: ParsedQuery<rustdoc.ParserQueryElement>,
} }
type Results = { max_dist?: number } & Map<number, ResultObject> type Results = { max_dist?: number } & Map<number, ResultObject>
@@ -252,25 +304,41 @@ declare namespace rustdoc {
* An annotated `Row`, used in the viewmodel. * An annotated `Row`, used in the viewmodel.
*/ */
interface ResultObject { interface ResultObject {
desc: string, desc: Promise<string|null>,
displayPath: string, displayPath: string,
fullPath: string, fullPath: string,
href: string, href: string,
id: number, id: number,
dist: number, dist: number,
path_dist: number, path_dist: number,
name: string,
normalizedName: string,
word: string,
index: number, index: number,
parent: (Object|undefined), parent: ({
path: string, path: string,
ty: number, exactPath: string,
name: string,
ty: number,
}|undefined),
type?: FunctionSearchType, type?: FunctionSearchType,
paramNames?: string[], paramNames?: string[],
displayTypeSignature: Promise<rustdoc.DisplayTypeSignature> | null, displayTypeSignature: Promise<rustdoc.DisplayTypeSignature> | null,
item: Row, item: Row,
dontValidate?: boolean, is_alias: boolean,
alias?: string,
}
/**
* An annotated `Row`, used in the viewmodel.
*/
interface PlainResultObject {
id: number,
dist: number,
path_dist: number,
index: number,
elems: rustdoc.QueryElement[],
returned: rustdoc.QueryElement[],
is_alias: boolean,
alias?: string,
original?: rustdoc.Rlow,
} }
/** /**
@@ -364,7 +432,19 @@ declare namespace rustdoc {
* Numeric IDs are *ONE-indexed* into the paths array (`p`). Zero is used as a sentinel for `null` * Numeric IDs are *ONE-indexed* into the paths array (`p`). Zero is used as a sentinel for `null`
* because `null` is four bytes while `0` is one byte. * because `null` is four bytes while `0` is one byte.
*/ */
type RawFunctionType = number | [number, Array<RawFunctionType>]; type RawFunctionType = number | [number, Array<RawFunctionType>] | [number, Array<RawFunctionType>, Array<[RawFunctionType, RawFunctionType[]]>];
/**
* Utility typedef for deserializing compact JSON.
*
* R is the required part, O is the optional part, which goes afterward.
* For example, `ArrayWithOptionals<[A, B], [C, D]>` matches
* `[A, B] | [A, B, C] | [A, B, C, D]`.
*/
type ArrayWithOptionals<R extends any[], O extends any[]> =
O extends [infer First, ...infer Rest] ?
R | ArrayWithOptionals<[...R, First], Rest> :
R;
/** /**
* The type signature entry in the decoded search index. * The type signature entry in the decoded search index.
@@ -382,8 +462,8 @@ declare namespace rustdoc {
*/ */
interface FunctionType { interface FunctionType {
id: null|number, id: null|number,
ty: number|null, ty: ItemType,
name?: string, name: string|null,
path: string|null, path: string|null,
exactPath: string|null, exactPath: string|null,
unboxFlag: boolean, unboxFlag: boolean,
@@ -403,70 +483,6 @@ declare namespace rustdoc {
bindings: Map<number, FingerprintableType[]>; bindings: Map<number, FingerprintableType[]>;
}; };
/**
* The raw search data for a given crate. `n`, `t`, `d`, `i`, and `f`
* are arrays with the same length. `q`, `a`, and `c` use a sparse
* representation for compactness.
*
* `n[i]` contains the name of an item.
*
* `t[i]` contains the type of that item
* (as a string of characters that represent an offset in `itemTypes`).
*
* `d[i]` contains the description of that item.
*
* `q` contains the full paths of the items. For compactness, it is a set of
* (index, path) pairs used to create a map. If a given index `i` is
* not present, this indicates "same as the last index present".
*
* `i[i]` contains an item's parent, usually a module. For compactness,
* it is a set of indexes into the `p` array.
*
* `f` contains function signatures, or `0` if the item isn't a function.
* More information on how they're encoded can be found in rustc-dev-guide
*
* Functions are themselves encoded as arrays. The first item is a list of
* types representing the function's inputs, and the second list item is a list
* of types representing the function's output. Tuples are flattened.
* Types are also represented as arrays; the first item is an index into the `p`
* array, while the second is a list of types representing any generic parameters.
*
* b[i] contains an item's impl disambiguator. This is only present if an item
* is defined in an impl block and, the impl block's type has more than one associated
* item with the same name.
*
* `a` defines aliases with an Array of pairs: [name, offset], where `offset`
* points into the n/t/d/q/i/f arrays.
*
* `doc` contains the description of the crate.
*
* `p` is a list of path/type pairs. It is used for parents and function parameters.
* The first item is the type, the second is the name, the third is the visible path (if any) and
* the fourth is the canonical path used for deduplication (if any).
*
* `r` is the canonical path used for deduplication of re-exported items.
* It is not used for associated items like methods (that's the fourth element
* of `p`) but is used for modules items like free functions.
*
* `c` is an array of item indices that are deprecated.
*/
type RawSearchIndexCrate = {
doc: string,
a: { [key: string]: number[] },
n: Array<string>,
t: string,
D: string,
e: string,
q: Array<[number, string]>,
i: string,
f: string,
p: Array<[number, string] | [number, string, number] | [number, string, number, number] | [number, string, number, number, string]>,
b: Array<[number, String]>,
c: string,
r: Array<[number, number]>,
P: Array<[number, string]>,
};
type VlqData = VlqData[] | number; type VlqData = VlqData[] | number;
/** /**

File diff suppressed because it is too large Load Diff

View File

@@ -1,25 +1,13 @@
// Local js definitions: // Local js definitions:
/* global getSettingValue, updateLocalStorage, updateTheme */ /* global getSettingValue, updateLocalStorage, updateTheme */
/* global addClass, removeClass, onEach, onEachLazy */ /* global addClass, removeClass, onEach, onEachLazy */
/* global MAIN_ID, getVar, getSettingsButton, getHelpButton, nonnull */ /* global MAIN_ID, getVar, nonnull */
"use strict"; "use strict";
(function() { (function() {
const isSettingsPage = window.location.pathname.endsWith("/settings.html"); const isSettingsPage = window.location.pathname.endsWith("/settings.html");
/**
* @param {Element} elem
* @param {EventTarget|null} target
*/
function elemContainsTarget(elem, target) {
if (target instanceof Node) {
return elem.contains(target);
} else {
return false;
}
}
/** /**
* @overload {"theme"|"preferred-dark-theme"|"preferred-light-theme"} * @overload {"theme"|"preferred-dark-theme"|"preferred-light-theme"}
* @param {string} settingName * @param {string} settingName
@@ -305,10 +293,12 @@
} }
} else { } else {
el.setAttribute("tabindex", "-1"); el.setAttribute("tabindex", "-1");
const settingsBtn = getSettingsButton(); onEachLazy(document.querySelectorAll(".settings-menu"), menu => {
if (settingsBtn !== null) { if (menu.offsetWidth !== 0) {
settingsBtn.appendChild(el); menu.appendChild(el);
} return true;
}
});
} }
return el; return el;
} }
@@ -317,6 +307,15 @@
function displaySettings() { function displaySettings() {
settingsMenu.style.display = ""; settingsMenu.style.display = "";
onEachLazy(document.querySelectorAll(".settings-menu"), menu => {
if (menu.offsetWidth !== 0) {
if (!menu.contains(settingsMenu) && settingsMenu.parentElement) {
settingsMenu.parentElement.removeChild(settingsMenu);
menu.appendChild(settingsMenu);
}
return true;
}
});
onEachLazy(settingsMenu.querySelectorAll("input[type='checkbox']"), el => { onEachLazy(settingsMenu.querySelectorAll("input[type='checkbox']"), el => {
const val = getSettingValue(el.id); const val = getSettingValue(el.id);
const checked = val === "true"; const checked = val === "true";
@@ -330,40 +329,37 @@
* @param {FocusEvent} event * @param {FocusEvent} event
*/ */
function settingsBlurHandler(event) { function settingsBlurHandler(event) {
const helpBtn = getHelpButton(); const isInPopover = onEachLazy(
const settingsBtn = getSettingsButton(); document.querySelectorAll(".settings-menu, .help-menu"),
const helpUnfocused = helpBtn === null || menu => {
(!helpBtn.contains(document.activeElement) && return menu.contains(document.activeElement) || menu.contains(event.relatedTarget);
!elemContainsTarget(helpBtn, event.relatedTarget)); },
const settingsUnfocused = settingsBtn === null || );
(!settingsBtn.contains(document.activeElement) && if (!isInPopover) {
!elemContainsTarget(settingsBtn, event.relatedTarget));
if (helpUnfocused && settingsUnfocused) {
window.hidePopoverMenus(); window.hidePopoverMenus();
} }
} }
if (!isSettingsPage) { if (!isSettingsPage) {
// We replace the existing "onclick" callback. // We replace the existing "onclick" callback.
// These elements must exist, as (outside of the settings page)
// `settings.js` is only loaded after the settings button is clicked.
const settingsButton = nonnull(getSettingsButton());
const settingsMenu = nonnull(document.getElementById("settings")); const settingsMenu = nonnull(document.getElementById("settings"));
settingsButton.onclick = event => { onEachLazy(document.querySelectorAll(".settings-menu"), settingsButton => {
if (elemContainsTarget(settingsMenu, event.target)) { /** @param {MouseEvent} event */
return; settingsButton.querySelector("a").onclick = event => {
} if (!(event.target instanceof Element) || settingsMenu.contains(event.target)) {
event.preventDefault(); return;
const shouldDisplaySettings = settingsMenu.style.display === "none"; }
event.preventDefault();
const shouldDisplaySettings = settingsMenu.style.display === "none";
window.hideAllModals(false); window.hideAllModals(false);
if (shouldDisplaySettings) { if (shouldDisplaySettings) {
displaySettings(); displaySettings();
} }
}; };
settingsButton.onblur = settingsBlurHandler; settingsButton.onblur = settingsBlurHandler;
// the settings button should always have a link in it settingsButton.querySelector("a").onblur = settingsBlurHandler;
nonnull(settingsButton.querySelector("a")).onblur = settingsBlurHandler; });
onEachLazy(settingsMenu.querySelectorAll("input"), el => { onEachLazy(settingsMenu.querySelectorAll("input"), el => {
el.onblur = settingsBlurHandler; el.onblur = settingsBlurHandler;
}); });
@@ -377,6 +373,8 @@
if (!isSettingsPage) { if (!isSettingsPage) {
displaySettings(); displaySettings();
} }
removeClass(getSettingsButton(), "rotate"); onEachLazy(document.querySelectorAll(".settings-menu"), settingsButton => {
removeClass(settingsButton, "rotate");
});
}, 0); }, 0);
})(); })();

View File

@@ -7,6 +7,7 @@
/** /**
* @import * as rustdoc from "./rustdoc.d.ts"; * @import * as rustdoc from "./rustdoc.d.ts";
* @import * as stringdex from "./stringdex.d.ts";
*/ */
const builtinThemes = ["light", "dark", "ayu"]; const builtinThemes = ["light", "dark", "ayu"];
@@ -172,7 +173,7 @@ function updateLocalStorage(name, value) {
} else { } else {
window.localStorage.setItem("rustdoc-" + name, value); window.localStorage.setItem("rustdoc-" + name, value);
} }
} catch (e) { } catch {
// localStorage is not accessible, do nothing // localStorage is not accessible, do nothing
} }
} }
@@ -189,7 +190,7 @@ function updateLocalStorage(name, value) {
function getCurrentValue(name) { function getCurrentValue(name) {
try { try {
return window.localStorage.getItem("rustdoc-" + name); return window.localStorage.getItem("rustdoc-" + name);
} catch (e) { } catch {
return null; return null;
} }
} }
@@ -375,32 +376,6 @@ window.addEventListener("pageshow", ev => {
// That's also why this is in storage.js and not main.js. // That's also why this is in storage.js and not main.js.
// //
// [parser]: https://html.spec.whatwg.org/multipage/parsing.html // [parser]: https://html.spec.whatwg.org/multipage/parsing.html
class RustdocSearchElement extends HTMLElement {
constructor() {
super();
}
connectedCallback() {
const rootPath = getVar("root-path");
const currentCrate = getVar("current-crate");
this.innerHTML = `<nav class="sub">
<form class="search-form">
<span></span> <!-- This empty span is a hacky fix for Safari - See #93184 -->
<div id="sidebar-button" tabindex="-1">
<a href="${rootPath}${currentCrate}/all.html" title="show sidebar"></a>
</div>
<input
class="search-input"
name="search"
aria-label="Run search in the documentation"
autocomplete="off"
spellcheck="false"
placeholder="Type S or / to search, ? for more options…"
type="search">
</form>
</nav>`;
}
}
window.customElements.define("rustdoc-search", RustdocSearchElement);
class RustdocToolbarElement extends HTMLElement { class RustdocToolbarElement extends HTMLElement {
constructor() { constructor() {
super(); super();
@@ -411,11 +386,15 @@ class RustdocToolbarElement extends HTMLElement {
return; return;
} }
const rootPath = getVar("root-path"); const rootPath = getVar("root-path");
const currentUrl = window.location.href.split("?")[0].split("#")[0];
this.innerHTML = ` this.innerHTML = `
<div id="settings-menu" tabindex="-1"> <div id="search-button" tabindex="-1">
<a href="${currentUrl}?search="><span class="label">Search</span></a>
</div>
<div class="settings-menu" tabindex="-1">
<a href="${rootPath}settings.html"><span class="label">Settings</span></a> <a href="${rootPath}settings.html"><span class="label">Settings</span></a>
</div> </div>
<div id="help-button" tabindex="-1"> <div class="help-menu" tabindex="-1">
<a href="${rootPath}help.html"><span class="label">Help</span></a> <a href="${rootPath}help.html"><span class="label">Help</span></a>
</div> </div>
<button id="toggle-all-docs" <button id="toggle-all-docs"
@@ -424,3 +403,31 @@ class="label">Summary</span></button>`;
} }
} }
window.customElements.define("rustdoc-toolbar", RustdocToolbarElement); window.customElements.define("rustdoc-toolbar", RustdocToolbarElement);
class RustdocTopBarElement extends HTMLElement {
constructor() {
super();
}
connectedCallback() {
const rootPath = getVar("root-path");
const tmplt = document.createElement("template");
tmplt.innerHTML = `
<slot name="sidebar-menu-toggle"></slot>
<slot></slot>
<slot name="settings-menu"></slot>
<slot name="help-menu"></slot>
`;
const shadow = this.attachShadow({ mode: "open" });
shadow.appendChild(tmplt.content.cloneNode(true));
this.innerHTML += `
<button class="sidebar-menu-toggle" slot="sidebar-menu-toggle" title="show sidebar">
</button>
<div class="settings-menu" slot="settings-menu" tabindex="-1">
<a href="${rootPath}settings.html"><span class="label">Settings</span></a>
</div>
<div class="help-menu" slot="help-menu" tabindex="-1">
<a href="${rootPath}help.html"><span class="label">Help</span></a>
</div>
`;
}
}
window.customElements.define("rustdoc-topbar", RustdocTopBarElement);

View File

@@ -0,0 +1,165 @@
export = stringdex;
declare namespace stringdex {
/**
* The client interface to Stringdex.
*/
interface Database {
getIndex(colname: string): SearchTree|undefined;
getData(colname: string): DataColumn|undefined;
}
/**
* A search index file.
*/
interface SearchTree {
trie(): Trie;
search(name: Uint8Array|string): Promise<Trie?>;
searchLev(name: Uint8Array|string): AsyncGenerator<Trie>;
}
/**
* A compressed node in the search tree.
*
* This object logically addresses two interleaved trees:
* a "prefix tree", and a "suffix tree". If you ask for
* generic matches, you get both, but if you ask for one
* that excludes suffix-only entries, you'll get prefixes
* alone.
*/
interface Trie {
matches(): RoaringBitmap;
substringMatches(): AsyncGenerator<RoaringBitmap>;
prefixMatches(): AsyncGenerator<RoaringBitmap>;
keys(): Uint8Array;
keysExcludeSuffixOnly(): Uint8Array;
children(): [number, Promise<Trie>][];
childrenExcludeSuffixOnly(): [number, Promise<Trie>][];
child(id: number): Promise<Trie>?;
}
/**
* The client interface to Stringdex.
*/
interface DataColumn {
isEmpty(id: number): boolean;
at(id: number): Promise<Uint8Array|undefined>;
length: number,
}
/**
* Callbacks for a host application and VFS backend.
*
* These functions are calleb with mostly-raw data,
* except the JSONP wrapper is removed. For example,
* a file with the contents `rr_('{"A":"B"}')` should,
* after being pulled in, result in the `rr_` callback
* being invoked.
*
* The success callbacks don't need to supply the name of
* the file that succeeded, but, if you want successful error
* reporting, you'll need to remember which files are
* in flight and report the filename as the first parameter.
*/
interface Callbacks {
/**
* Load the root of the search database
* @param {string} dataString
*/
rr_: function(string);
err_rr_: function(any);
/**
* Load a nodefile in the search tree.
* A node file may contain multiple nodes;
* each node has five fields, separated by newlines.
* @param {string} inputBase64
*/
rn_: function(string);
err_rn_: function(string, any);
/**
* Load a database column partition from a string
* @param {string} dataString
*/
rd_: function(string);
err_rd_: function(string, any);
/**
* Load a database column partition from base64
* @param {string} dataString
*/
rb_: function(string);
err_rb_: function(string, any);
};
/**
* Hooks that a VFS layer must provide for stringdex to load data.
*
* When the root is loaded, the Callbacks object is provided. These
* functions should result in callback functions being called with
* the contents of the file, or in error callbacks being invoked with
* the failed-to-load filename.
*/
interface Hooks {
/**
* The first function invoked as part of loading a search database.
* This function must, eventually, invoke `rr_` with the string
* representation of the root file (the function call wrapper,
* `rr_('` and `')`, must be removed).
*
* The supplied callbacks object is used to feed search data back
* to the search engine core. You have to store it, so that
* loadTreeByHash and loadDataByNameAndHash can use it.
*
* If this fails, either throw an exception, or call `err_rr_`
* with the error object.
*/
loadRoot: function(Callbacks);
/**
* Load a subtree file from the search index.
*
* If this function succeeds, call `rn_` on the callbacks
* object. If it fails, call `err_rn_(hashHex, error)`.
*
* @param {string} hashHex
*/
loadTreeByHash: function(string);
/**
* Load a column partition from the search database.
*
* If this function succeeds, call `rd_` or `rb_` on the callbacks
* object. If it fails, call `err_rd_(hashHex, error)`. or `err_rb_`.
* To determine which one, the wrapping function call in the js file
* specifies it.
*
* @param {string} columnName
* @param {string} hashHex
*/
loadDataByNameAndHash: function(string, string);
};
class RoaringBitmap {
constructor(array: Uint8Array|null, start?: number);
static makeSingleton(number: number);
static everything(): RoaringBitmap;
static empty(): RoaringBitmap;
isEmpty(): boolean;
union(that: RoaringBitmap): RoaringBitmap;
intersection(that: RoaringBitmap): RoaringBitmap;
contains(number: number): boolean;
entries(): Generator<number>;
first(): number|null;
consumed_len_bytes: number;
};
type Stringdex = {
/**
* Initialize Stringdex with VFS hooks.
* Returns a database that you can use.
*/
loadDatabase: function(Hooks): Promise<Database>,
};
const Stringdex: Stringdex;
const RoaringBitmap: Class<stringdex.RoaringBitmap>;
}
declare global {
interface Window {
Stringdex: stringdex.Stringdex;
RoaringBitmap: Class<stringdex.RoaringBitmap>;
StringdexOnload: Array<function(stringdex.Stringdex): any>?;
};
}

File diff suppressed because it is too large Load Diff

View File

@@ -10,6 +10,6 @@
"skipLibCheck": true "skipLibCheck": true
}, },
"typeAcquisition": { "typeAcquisition": {
"include": ["./rustdoc.d.ts"] "include": ["./rustdoc.d.ts", "./stringdex.d.ts"]
} }
} }

View File

@@ -80,6 +80,7 @@ static_files! {
normalize_css => "static/css/normalize.css", normalize_css => "static/css/normalize.css",
main_js => "static/js/main.js", main_js => "static/js/main.js",
search_js => "static/js/search.js", search_js => "static/js/search.js",
stringdex_js => "static/js/stringdex.js",
settings_js => "static/js/settings.js", settings_js => "static/js/settings.js",
src_script_js => "static/js/src-script.js", src_script_js => "static/js/src-script.js",
storage_js => "static/js/storage.js", storage_js => "static/js/storage.js",

View File

@@ -29,6 +29,7 @@
data-rustdoc-version="{{rustdoc_version}}" {#+ #} data-rustdoc-version="{{rustdoc_version}}" {#+ #}
data-channel="{{rust_channel}}" {#+ #} data-channel="{{rust_channel}}" {#+ #}
data-search-js="{{files.search_js}}" {#+ #} data-search-js="{{files.search_js}}" {#+ #}
data-stringdex-js="{{files.stringdex_js}}" {#+ #}
data-settings-js="{{files.settings_js}}" {#+ #} data-settings-js="{{files.settings_js}}" {#+ #}
> {# #} > {# #}
<script src="{{static_root_path|safe}}{{files.storage_js}}"></script> <script src="{{static_root_path|safe}}{{files.storage_js}}"></script>
@@ -72,18 +73,9 @@
<![endif]--> <![endif]-->
{{ layout.external_html.before_content|safe }} {{ layout.external_html.before_content|safe }}
{% if page.css_class != "src" %} {% if page.css_class != "src" %}
<nav class="mobile-topbar"> {# #} <rustdoc-topbar> {# #}
<button class="sidebar-menu-toggle" title="show sidebar"></button> <h2><a href="#">{{page.short_title}}</a></h2> {# #}
{% if !layout.logo.is_empty() || page.rust_logo %} </rustdoc-topbar>
<a class="logo-container" href="{{page.root_path|safe}}{{display_krate_with_trailing_slash|safe}}index.html">
{% if page.rust_logo %}
<img class="rust-logo" src="{{static_root_path|safe}}{{files.rust_logo_svg}}" alt="">
{% else if !layout.logo.is_empty() %}
<img src="{{layout.logo}}" alt="">
{% endif %}
</a>
{% endif %}
</nav>
{% endif %} {% endif %}
<nav class="sidebar"> <nav class="sidebar">
{% if page.css_class != "src" %} {% if page.css_class != "src" %}
@@ -117,9 +109,6 @@
<div class="sidebar-resizer" title="Drag to resize sidebar"></div> {# #} <div class="sidebar-resizer" title="Drag to resize sidebar"></div> {# #}
<main> <main>
{% if page.css_class != "src" %}<div class="width-limiter">{% endif %} {% if page.css_class != "src" %}<div class="width-limiter">{% endif %}
{# defined in storage.js to avoid duplicating complex UI across every page #}
{# and because the search form only works if JS is enabled anyway #}
<rustdoc-search></rustdoc-search> {# #}
<section id="main-content" class="content">{{ content|safe }}</section> <section id="main-content" class="content">{{ content|safe }}</section>
{% if page.css_class != "src" %}</div>{% endif %} {% if page.css_class != "src" %}</div>{% endif %}
</main> </main>

View File

@@ -12,8 +12,8 @@
<h1> <h1>
{{typ}} {{typ}}
<span{% if item_type != "mod" +%} class="{{item_type}}"{% endif %}> <span{% if item_type != "mod" +%} class="{{item_type}}"{% endif %}>
{{name}} {{name|wrapped|safe}}
</span> {# #} </span>&nbsp;{# #}
<button id="copy-path" title="Copy item path to clipboard"> {# #} <button id="copy-path" title="Copy item path to clipboard"> {# #}
Copy item path {# #} Copy item path {# #}
</button> {# #} </button> {# #}

View File

@@ -1,7 +1,8 @@
/* global globalThis */ /* global globalThis */
const fs = require("fs"); const fs = require("fs");
const path = require("path"); const path = require("path");
const { isGeneratorObject } = require("util/types");
function arrayToCode(array) { function arrayToCode(array) {
return array.map((value, index) => { return array.map((value, index) => {
@@ -45,23 +46,16 @@ function shouldIgnoreField(fieldName) {
} }
function valueMapper(key, testOutput) { function valueMapper(key, testOutput) {
const isAlias = testOutput["is_alias"];
let value = testOutput[key]; let value = testOutput[key];
// To make our life easier, if there is a "parent" type, we add it to the path. // To make our life easier, if there is a "parent" type, we add it to the path.
if (key === "path") { if (key === "path") {
if (testOutput["parent"] !== undefined) { if (testOutput["parent"]) {
if (value.length > 0) { if (value.length > 0) {
value += "::" + testOutput["parent"]["name"]; value += "::" + testOutput["parent"]["name"];
} else { } else {
value = testOutput["parent"]["name"]; value = testOutput["parent"]["name"];
} }
} else if (testOutput["is_alias"]) {
value = valueMapper(key, testOutput["original"]);
} }
} else if (isAlias && key === "alias") {
value = testOutput["name"];
} else if (isAlias && ["name"].includes(key)) {
value = testOutput["original"][key];
} }
return value; return value;
} }
@@ -237,7 +231,7 @@ async function runSearch(query, expected, doSearch, loadedFile, queryName) {
const ignore_order = loadedFile.ignore_order; const ignore_order = loadedFile.ignore_order;
const exact_check = loadedFile.exact_check; const exact_check = loadedFile.exact_check;
const results = await doSearch(query, loadedFile.FILTER_CRATE); const { resultsTable } = await doSearch(query, loadedFile.FILTER_CRATE);
const error_text = []; const error_text = [];
for (const key in expected) { for (const key in expected) {
@@ -247,37 +241,38 @@ async function runSearch(query, expected, doSearch, loadedFile, queryName) {
if (!Object.prototype.hasOwnProperty.call(expected, key)) { if (!Object.prototype.hasOwnProperty.call(expected, key)) {
continue; continue;
} }
if (!Object.prototype.hasOwnProperty.call(results, key)) { if (!Object.prototype.hasOwnProperty.call(resultsTable, key)) {
error_text.push("==> Unknown key \"" + key + "\""); error_text.push("==> Unknown key \"" + key + "\"");
break; break;
} }
const entry = expected[key]; const entry = expected[key];
if (exact_check && entry.length !== results[key].length) { if (exact_check && entry.length !== resultsTable[key].length) {
error_text.push(queryName + "==> Expected exactly " + entry.length + error_text.push(queryName + "==> Expected exactly " + entry.length +
" results but found " + results[key].length + " in '" + key + "'"); " results but found " + resultsTable[key].length + " in '" + key + "'");
} }
let prev_pos = -1; let prev_pos = -1;
for (const [index, elem] of entry.entries()) { for (const [index, elem] of entry.entries()) {
const entry_pos = lookForEntry(elem, results[key]); const entry_pos = lookForEntry(elem, resultsTable[key]);
if (entry_pos === -1) { if (entry_pos === -1) {
error_text.push(queryName + "==> Result not found in '" + key + "': '" + error_text.push(queryName + "==> Result not found in '" + key + "': '" +
JSON.stringify(elem) + "'"); JSON.stringify(elem) + "'");
// By default, we just compare the two first items. // By default, we just compare the two first items.
let item_to_diff = 0; let item_to_diff = 0;
if ((!ignore_order || exact_check) && index < results[key].length) { if ((!ignore_order || exact_check) && index < resultsTable[key].length) {
item_to_diff = index; item_to_diff = index;
} }
error_text.push("Diff of first error:\n" + error_text.push("Diff of first error:\n" +
betterLookingDiff(elem, results[key][item_to_diff])); betterLookingDiff(elem, resultsTable[key][item_to_diff]));
} else if (exact_check === true && prev_pos + 1 !== entry_pos) { } else if (exact_check === true && prev_pos + 1 !== entry_pos) {
error_text.push(queryName + "==> Exact check failed at position " + (prev_pos + 1) + error_text.push(queryName + "==> Exact check failed at position " + (prev_pos + 1) +
": expected '" + JSON.stringify(elem) + "' but found '" + ": expected '" + JSON.stringify(elem) + "' but found '" +
JSON.stringify(results[key][index]) + "'"); JSON.stringify(resultsTable[key][index]) + "'");
} else if (ignore_order === false && entry_pos < prev_pos) { } else if (ignore_order === false && entry_pos < prev_pos) {
error_text.push(queryName + "==> '" + JSON.stringify(elem) + "' was supposed " + error_text.push(queryName + "==> '" +
"to be before '" + JSON.stringify(results[key][prev_pos]) + "'"); JSON.stringify(elem) + "' was supposed to be before '" +
JSON.stringify(resultsTable[key][prev_pos]) + "'");
} else { } else {
prev_pos = entry_pos; prev_pos = entry_pos;
} }
@@ -286,19 +281,20 @@ async function runSearch(query, expected, doSearch, loadedFile, queryName) {
return error_text; return error_text;
} }
async function runCorrections(query, corrections, getCorrections, loadedFile) { async function runCorrections(query, corrections, doSearch, loadedFile) {
const qc = await getCorrections(query, loadedFile.FILTER_CRATE); const { parsedQuery } = await doSearch(query, loadedFile.FILTER_CRATE);
const qc = parsedQuery.correction;
const error_text = []; const error_text = [];
if (corrections === null) { if (corrections === null) {
if (qc !== null) { if (qc !== null) {
error_text.push(`==> expected = null, found = ${qc}`); error_text.push(`==> [correction] expected = null, found = ${qc}`);
} }
return error_text; return error_text;
} }
if (qc !== corrections.toLowerCase()) { if (qc.toLowerCase() !== corrections.toLowerCase()) {
error_text.push(`==> expected = ${corrections}, found = ${qc}`); error_text.push(`==> [correction] expected = ${corrections}, found = ${qc}`);
} }
return error_text; return error_text;
@@ -320,7 +316,7 @@ function checkResult(error_text, loadedFile, displaySuccess) {
return 1; return 1;
} }
async function runCheckInner(callback, loadedFile, entry, getCorrections, extra) { async function runCheckInner(callback, loadedFile, entry, extra, doSearch) {
if (typeof entry.query !== "string") { if (typeof entry.query !== "string") {
console.log("FAILED"); console.log("FAILED");
console.log("==> Missing `query` field"); console.log("==> Missing `query` field");
@@ -338,7 +334,7 @@ async function runCheckInner(callback, loadedFile, entry, getCorrections, extra)
error_text = await runCorrections( error_text = await runCorrections(
entry.query, entry.query,
entry.correction, entry.correction,
getCorrections, doSearch,
loadedFile, loadedFile,
); );
if (checkResult(error_text, loadedFile, false) !== 0) { if (checkResult(error_text, loadedFile, false) !== 0) {
@@ -348,16 +344,16 @@ async function runCheckInner(callback, loadedFile, entry, getCorrections, extra)
return true; return true;
} }
async function runCheck(loadedFile, key, getCorrections, callback) { async function runCheck(loadedFile, key, doSearch, callback) {
const expected = loadedFile[key]; const expected = loadedFile[key];
if (Array.isArray(expected)) { if (Array.isArray(expected)) {
for (const entry of expected) { for (const entry of expected) {
if (!await runCheckInner(callback, loadedFile, entry, getCorrections, true)) { if (!await runCheckInner(callback, loadedFile, entry, true, doSearch)) {
return 1; return 1;
} }
} }
} else if (!await runCheckInner(callback, loadedFile, expected, getCorrections, false)) { } else if (!await runCheckInner(callback, loadedFile, expected, false, doSearch)) {
return 1; return 1;
} }
console.log("OK"); console.log("OK");
@@ -368,7 +364,7 @@ function hasCheck(content, checkName) {
return content.startsWith(`const ${checkName}`) || content.includes(`\nconst ${checkName}`); return content.startsWith(`const ${checkName}`) || content.includes(`\nconst ${checkName}`);
} }
async function runChecks(testFile, doSearch, parseQuery, getCorrections) { async function runChecks(testFile, doSearch, parseQuery) {
let checkExpected = false; let checkExpected = false;
let checkParsed = false; let checkParsed = false;
let testFileContent = readFile(testFile); let testFileContent = readFile(testFile);
@@ -397,12 +393,12 @@ async function runChecks(testFile, doSearch, parseQuery, getCorrections) {
let res = 0; let res = 0;
if (checkExpected) { if (checkExpected) {
res += await runCheck(loadedFile, "EXPECTED", getCorrections, (query, expected, text) => { res += await runCheck(loadedFile, "EXPECTED", doSearch, (query, expected, text) => {
return runSearch(query, expected, doSearch, loadedFile, text); return runSearch(query, expected, doSearch, loadedFile, text);
}); });
} }
if (checkParsed) { if (checkParsed) {
res += await runCheck(loadedFile, "PARSED", getCorrections, (query, expected, text) => { res += await runCheck(loadedFile, "PARSED", doSearch, (query, expected, text) => {
return runParser(query, expected, parseQuery, text); return runParser(query, expected, parseQuery, text);
}); });
} }
@@ -416,71 +412,89 @@ async function runChecks(testFile, doSearch, parseQuery, getCorrections) {
* @param {string} resource_suffix - Version number between filename and .js, e.g. "1.59.0" * @param {string} resource_suffix - Version number between filename and .js, e.g. "1.59.0"
* @returns {Object} - Object containing keys: `doSearch`, which runs a search * @returns {Object} - Object containing keys: `doSearch`, which runs a search
* with the loaded index and returns a table of results; `parseQuery`, which is the * with the loaded index and returns a table of results; `parseQuery`, which is the
* `parseQuery` function exported from the search module; and `getCorrections`, which runs * `parseQuery` function exported from the search module, which runs
* a search but returns type name corrections instead of results. * a search but returns type name corrections instead of results.
*/ */
function loadSearchJS(doc_folder, resource_suffix) { async function loadSearchJS(doc_folder, resource_suffix) {
const searchIndexJs = path.join(doc_folder, "search-index" + resource_suffix + ".js");
const searchIndex = require(searchIndexJs);
globalThis.searchState = {
descShards: new Map(),
loadDesc: async function({descShard, descIndex}) {
if (descShard.promise === null) {
descShard.promise = new Promise((resolve, reject) => {
descShard.resolve = resolve;
const ds = descShard;
const fname = `${ds.crate}-desc-${ds.shard}-${resource_suffix}.js`;
fs.readFile(
`${doc_folder}/search.desc/${descShard.crate}/${fname}`,
(err, data) => {
if (err) {
reject(err);
} else {
eval(data.toString("utf8"));
}
},
);
});
}
const list = await descShard.promise;
return list[descIndex];
},
loadedDescShard: function(crate, shard, data) {
this.descShards.get(crate)[shard].resolve(data.split("\n"));
},
};
const staticFiles = path.join(doc_folder, "static.files"); const staticFiles = path.join(doc_folder, "static.files");
const stringdexJs = fs.readdirSync(staticFiles).find(f => f.match(/stringdex.*\.js$/));
const stringdexModule = require(path.join(staticFiles, stringdexJs));
const searchJs = fs.readdirSync(staticFiles).find(f => f.match(/search.*\.js$/)); const searchJs = fs.readdirSync(staticFiles).find(f => f.match(/search.*\.js$/));
const searchModule = require(path.join(staticFiles, searchJs)); const searchModule = require(path.join(staticFiles, searchJs));
searchModule.initSearch(searchIndex.searchIndex); globalThis.nonnull = (x, msg) => {
const docSearch = searchModule.docSearch; if (x === null) {
throw (msg || "unexpected null value!");
} else {
return x;
}
};
const { docSearch, DocSearch } = await searchModule.initSearch(
stringdexModule.Stringdex,
stringdexModule.RoaringBitmap,
{
loadRoot: callbacks => {
for (const key in callbacks) {
if (Object.hasOwn(callbacks, key)) {
globalThis[key] = callbacks[key];
}
}
const rootJs = readFile(path.join(doc_folder, "search.index/root" +
resource_suffix + ".js"));
eval(rootJs);
},
loadTreeByHash: hashHex => {
const shardJs = readFile(path.join(doc_folder, "search.index/" + hashHex + ".js"));
eval(shardJs);
},
loadDataByNameAndHash: (name, hashHex) => {
const shardJs = readFile(path.join(doc_folder, "search.index/" + name + "/" +
hashHex + ".js"));
eval(shardJs);
},
},
);
return { return {
doSearch: async function(queryStr, filterCrate, currentCrate) { doSearch: async function(queryStr, filterCrate, currentCrate) {
const result = await docSearch.execQuery(searchModule.parseQuery(queryStr), const parsedQuery = DocSearch.parseQuery(queryStr);
filterCrate, currentCrate); const result = await docSearch.execQuery(parsedQuery, filterCrate, currentCrate);
const resultsTable = {};
for (const tab in result) { for (const tab in result) {
if (!Object.prototype.hasOwnProperty.call(result, tab)) { if (!Object.prototype.hasOwnProperty.call(result, tab)) {
continue; continue;
} }
if (!(result[tab] instanceof Array)) { if (!isGeneratorObject(result[tab])) {
continue; continue;
} }
for (const entry of result[tab]) { resultsTable[tab] = [];
for await (const entry of result[tab]) {
const flatEntry = Object.assign({
crate: entry.item.crate,
name: entry.item.name,
path: entry.item.modulePath,
exactPath: entry.item.exactModulePath,
ty: entry.item.ty,
}, entry);
for (const key in entry) { for (const key in entry) {
if (!Object.prototype.hasOwnProperty.call(entry, key)) { if (!Object.prototype.hasOwnProperty.call(entry, key)) {
continue; continue;
} }
if (key === "displayTypeSignature" && entry.displayTypeSignature !== null) { if (key === "desc" && entry.desc !== null) {
const {type, mappedNames, whereClause} = flatEntry.desc = await entry.desc;
await entry.displayTypeSignature; } else if (key === "displayTypeSignature" &&
entry.displayType = arrayToCode(type); entry.displayTypeSignature !== null
entry.displayMappedNames = [...mappedNames.entries()] ) {
flatEntry.displayTypeSignature = await entry.displayTypeSignature;
const {
type,
mappedNames,
whereClause,
} = flatEntry.displayTypeSignature;
flatEntry.displayType = arrayToCode(type);
flatEntry.displayMappedNames = [...mappedNames.entries()]
.map(([name, qname]) => { .map(([name, qname]) => {
return `${name} = ${qname}`; return `${name} = ${qname}`;
}).join(", "); }).join(", ");
entry.displayWhereClause = [...whereClause.entries()] flatEntry.displayWhereClause = [...whereClause.entries()]
.flatMap(([name, value]) => { .flatMap(([name, value]) => {
if (value.length === 0) { if (value.length === 0) {
return []; return [];
@@ -489,16 +503,12 @@ function loadSearchJS(doc_folder, resource_suffix) {
}).join(", "); }).join(", ");
} }
} }
resultsTable[tab].push(flatEntry);
} }
} }
return result; return { resultsTable, parsedQuery };
}, },
getCorrections: function(queryStr, filterCrate, currentCrate) { parseQuery: DocSearch.parseQuery,
const parsedQuery = searchModule.parseQuery(queryStr);
docSearch.execQuery(parsedQuery, filterCrate, currentCrate);
return parsedQuery.correction;
},
parseQuery: searchModule.parseQuery,
}; };
} }
@@ -570,7 +580,7 @@ async function main(argv) {
return 1; return 1;
} }
const parseAndSearch = loadSearchJS( const parseAndSearch = await loadSearchJS(
opts["doc_folder"], opts["doc_folder"],
opts["resource_suffix"], opts["resource_suffix"],
); );
@@ -579,14 +589,11 @@ async function main(argv) {
const doSearch = function(queryStr, filterCrate) { const doSearch = function(queryStr, filterCrate) {
return parseAndSearch.doSearch(queryStr, filterCrate, opts["crate_name"]); return parseAndSearch.doSearch(queryStr, filterCrate, opts["crate_name"]);
}; };
const getCorrections = function(queryStr, filterCrate) {
return parseAndSearch.getCorrections(queryStr, filterCrate, opts["crate_name"]);
};
if (opts["test_file"].length !== 0) { if (opts["test_file"].length !== 0) {
for (const file of opts["test_file"]) { for (const file of opts["test_file"]) {
process.stdout.write(`Testing ${file} ... `); process.stdout.write(`Testing ${file} ... `);
errors += await runChecks(file, doSearch, parseAndSearch.parseQuery, getCorrections); errors += await runChecks(file, doSearch, parseAndSearch.parseQuery);
} }
} else if (opts["test_folder"].length !== 0) { } else if (opts["test_folder"].length !== 0) {
for (const file of fs.readdirSync(opts["test_folder"])) { for (const file of fs.readdirSync(opts["test_folder"])) {
@@ -595,7 +602,7 @@ async function main(argv) {
} }
process.stdout.write(`Testing ${file} ... `); process.stdout.write(`Testing ${file} ... `);
errors += await runChecks(path.join(opts["test_folder"], file), doSearch, errors += await runChecks(path.join(opts["test_folder"], file), doSearch,
parseAndSearch.parseQuery, getCorrections); parseAndSearch.parseQuery);
} }
} }
return errors > 0 ? 1 : 0; return errors > 0 ? 1 : 0;

View File

@@ -19,7 +19,7 @@ fn main() {
.args(&["--extend-css", "z.css"]) .args(&["--extend-css", "z.css"])
.input("x.rs") .input("x.rs")
.run(); .run();
assert!(path("invocation-only/search-index-xxx.js").exists()); assert!(path("invocation-only/search.index/root-xxx.js").exists());
assert!(path("invocation-only/crates-xxx.js").exists()); assert!(path("invocation-only/crates-xxx.js").exists());
assert!(path("invocation-only/settings.html").exists()); assert!(path("invocation-only/settings.html").exists());
assert!(path("invocation-only/x/all.html").exists()); assert!(path("invocation-only/x/all.html").exists());

View File

@@ -15,7 +15,7 @@ fn main() {
rustdoc().input("foo.rs").out_dir(&bar_first).run(); rustdoc().input("foo.rs").out_dir(&bar_first).run();
diff() diff()
.expected_file(foo_first.join("search-index.js")) .expected_file(foo_first.join("search.index/root.js"))
.actual_file(bar_first.join("search-index.js")) .actual_file(bar_first.join("search.index/root.js"))
.run(); .run();
} }

View File

@@ -5,25 +5,25 @@ include: "utils.goml"
// First we check we "hover". // First we check we "hover".
move-cursor-to: ".example-wrap" move-cursor-to: ".example-wrap"
assert-css: (".example-wrap .copy-button", { "visibility": "visible" }) assert-css: (".example-wrap .copy-button", { "visibility": "visible" })
move-cursor-to: ".search-input" move-cursor-to: "#search-button"
assert-css: (".example-wrap .copy-button", { "visibility": "hidden" }) assert-css: (".example-wrap .copy-button", { "visibility": "hidden" })
// Now we check the click. // Now we check the click.
assert-count: (".example-wrap:not(:hover) .button-holder.keep-visible", 0) assert-count: (".example-wrap:not(:hover) .button-holder.keep-visible", 0)
click: ".example-wrap" click: ".example-wrap"
move-cursor-to: ".search-input" move-cursor-to: "#search-button"
// It should have a new class and be visible. // It should have a new class and be visible.
wait-for-count: (".example-wrap:not(:hover) .button-holder.keep-visible", 1) wait-for-count: (".example-wrap:not(:hover) .button-holder.keep-visible", 1)
wait-for-css: (".example-wrap:not(:hover) .button-holder.keep-visible", { "visibility": "visible" }) wait-for-css: (".example-wrap:not(:hover) .button-holder.keep-visible", { "visibility": "visible" })
// Clicking again will remove the class. // Clicking again will remove the class.
click: ".example-wrap" click: ".example-wrap"
move-cursor-to: ".search-input" move-cursor-to: "rustdoc-toolbar #search-button"
assert-count: (".example-wrap:not(:hover) .button-holder.keep-visible", 0) assert-count: (".example-wrap:not(:hover) .button-holder.keep-visible", 0)
assert-css: (".example-wrap .copy-button", { "visibility": "hidden" }) assert-css: (".example-wrap .copy-button", { "visibility": "hidden" })
// Clicking on the "copy code" button shouldn't make the buttons stick. // Clicking on the "copy code" button shouldn't make the buttons stick.
click: ".example-wrap .copy-button" click: ".example-wrap .copy-button"
move-cursor-to: ".search-input" move-cursor-to: "#search-button"
assert-count: (".example-wrap:not(:hover) .button-holder.keep-visible", 0) assert-count: (".example-wrap:not(:hover) .button-holder.keep-visible", 0)
assert-css: (".example-wrap .copy-button", { "visibility": "hidden" }) assert-css: (".example-wrap .copy-button", { "visibility": "hidden" })
// Since we clicked on the copy button, the clipboard content should have been updated. // Since we clicked on the copy button, the clipboard content should have been updated.

View File

@@ -12,7 +12,7 @@ define-function: (
assert-count: (".example-wrap .copy-button", 1) assert-count: (".example-wrap .copy-button", 1)
// We now ensure it's only displayed when the example is hovered. // We now ensure it's only displayed when the example is hovered.
assert-css: (".example-wrap .copy-button", { "visibility": "visible" }) assert-css: (".example-wrap .copy-button", { "visibility": "visible" })
move-cursor-to: ".search-input" move-cursor-to: "rustdoc-toolbar #search-button"
assert-css: (".example-wrap .copy-button", { "visibility": "hidden" }) assert-css: (".example-wrap .copy-button", { "visibility": "hidden" })
// Checking that the copy button has the same size as the "copy path" button. // Checking that the copy button has the same size as the "copy path" button.
compare-elements-size: ( compare-elements-size: (

View File

@@ -1,4 +1,5 @@
// This test ensures that several clickable items actually have the pointer cursor. // This test ensures that several clickable items actually have the pointer cursor.
include: "utils.goml"
go-to: "file://" + |DOC_PATH| + "/lib2/struct.Foo.html" go-to: "file://" + |DOC_PATH| + "/lib2/struct.Foo.html"
// the `[+]/[-]` button // the `[+]/[-]` button
@@ -8,11 +9,7 @@ assert-css: ("#toggle-all-docs", {"cursor": "pointer"})
assert-css: ("#copy-path", {"cursor": "pointer"}) assert-css: ("#copy-path", {"cursor": "pointer"})
// the search tabs // the search tabs
write-into: (".search-input", "Foo") call-function: ("perform-search", {"query": "Foo"})
// To be SURE that the search will be run.
press-key: 'Enter'
// Waiting for the search results to appear...
wait-for: "#search-tabs"
assert-css: ("#search-tabs > button", {"cursor": "pointer"}) assert-css: ("#search-tabs > button", {"cursor": "pointer"})
// mobile sidebar toggle button // mobile sidebar toggle button

View File

@@ -69,7 +69,7 @@ call-function: ("check-colors", {
// and make sure it goes away. // and make sure it goes away.
// First, open the settings menu. // First, open the settings menu.
click: "#settings-menu" click: "rustdoc-toolbar .settings-menu"
wait-for: "#settings" wait-for: "#settings"
assert-css: ("#settings", {"display": "block"}) assert-css: ("#settings", {"display": "block"})
@@ -121,7 +121,7 @@ call-function: ("check-padding", {
define-function: ("check-line-numbers-existence", [], block { define-function: ("check-line-numbers-existence", [], block {
assert-local-storage: {"rustdoc-line-numbers": "true" } assert-local-storage: {"rustdoc-line-numbers": "true" }
assert-false: ".example-line-numbers" assert-false: ".example-line-numbers"
click: "#settings-menu" click: "rustdoc-toolbar .settings-menu"
wait-for: "#settings" wait-for: "#settings"
// Then, click the toggle button. // Then, click the toggle button.
@@ -137,7 +137,7 @@ define-function: ("check-line-numbers-existence", [], block {
// Line numbers should still be there. // Line numbers should still be there.
assert-css: ("[data-nosnippet]", { "display": "block"}) assert-css: ("[data-nosnippet]", { "display": "block"})
// Closing settings menu. // Closing settings menu.
click: "#settings-menu" click: "rustdoc-toolbar .settings-menu"
wait-for-css: ("#settings", {"display": "none"}) wait-for-css: ("#settings", {"display": "none"})
}) })
@@ -168,7 +168,7 @@ assert: ".example-wrap > pre.rust"
assert-count: (".example-wrap", 2) assert-count: (".example-wrap", 2)
assert-count: (".example-wrap.digits-1", 2) assert-count: (".example-wrap.digits-1", 2)
click: "#settings-menu" click: "rustdoc-toolbar .settings-menu"
wait-for: "#settings" wait-for: "#settings"
// Then, click the toggle button. // Then, click the toggle button.

View File

@@ -1,13 +1,10 @@
// This test ensures that the "Escape" shortcut is handled correctly based on the // This test ensures that the "Escape" shortcut is handled correctly based on the
// current content displayed. // current content displayed.
include: "utils.goml"
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
// First, we check that the search results are hidden when the Escape key is pressed. // First, we check that the search results are hidden when the Escape key is pressed.
write-into: (".search-input", "test") call-function: ("perform-search", {"query": "test"})
// To be SURE that the search will be run.
press-key: 'Enter'
wait-for: "#search h1" // The search element is empty before the first search
// Check that the currently displayed element is search. // Check that the currently displayed element is search.
wait-for: "#alternative-display #search"
assert-attribute: ("#main-content", {"class": "content hidden"}) assert-attribute: ("#main-content", {"class": "content hidden"})
assert-document-property: ({"URL": "index.html?search=test"}, ENDS_WITH) assert-document-property: ({"URL": "index.html?search=test"}, ENDS_WITH)
press-key: "Escape" press-key: "Escape"
@@ -17,8 +14,8 @@ assert-false: "#alternative-display #search"
assert-attribute: ("#main-content", {"class": "content"}) assert-attribute: ("#main-content", {"class": "content"})
assert-document-property: ({"URL": "index.html"}, [ENDS_WITH]) assert-document-property: ({"URL": "index.html"}, [ENDS_WITH])
// Check that focusing the search input brings back the search results // Check that clicking the search button brings back the search results
focus: ".search-input" click: "#search-button"
wait-for: "#alternative-display #search" wait-for: "#alternative-display #search"
assert-attribute: ("#main-content", {"class": "content hidden"}) assert-attribute: ("#main-content", {"class": "content hidden"})
assert-document-property: ({"URL": "index.html?search=test"}, ENDS_WITH) assert-document-property: ({"URL": "index.html?search=test"}, ENDS_WITH)

View File

@@ -8,7 +8,7 @@ assert-css: ("body", {"font-family": |serif_font|})
assert-css: ("p code", {"font-family": |serif_code_font|}) assert-css: ("p code", {"font-family": |serif_code_font|})
// We now switch to the sans serif font // We now switch to the sans serif font
click: "#settings-menu" click: "main .settings-menu"
wait-for: "#sans-serif-fonts" wait-for: "#sans-serif-fonts"
click: "#sans-serif-fonts" click: "#sans-serif-fonts"
@@ -23,7 +23,7 @@ assert-css: ("body", {"font-family": |font|})
assert-css: ("p code", {"font-family": |code_font|}) assert-css: ("p code", {"font-family": |code_font|})
// We switch back to the serif font // We switch back to the serif font
click: "#settings-menu" click: "main .settings-menu"
wait-for: "#sans-serif-fonts" wait-for: "#sans-serif-fonts"
click: "#sans-serif-fonts" click: "#sans-serif-fonts"

View File

@@ -1,6 +1,7 @@
// Make sure search stores its data in `window` // Make sure search stores its data in `window`
// It needs to use a global to avoid racing on search-index.js and search.js // It needs to use a global to avoid racing on search-index.js and search.js
// https://github.com/rust-lang/rust/pull/118961 // https://github.com/rust-lang/rust/pull/118961
include: "utils.goml"
// URL query // URL query
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=sa'%3Bda'%3Bds" go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=sa'%3Bda'%3Bds"
@@ -9,9 +10,7 @@ assert-window-property-false: {"searchIndex": null}
// Form input // Form input
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
write-into: (".search-input", "Foo") call-function: ("perform-search", {"query": "Foo"})
press-key: 'Enter'
wait-for: "#search-tabs"
assert-window-property-false: {"searchIndex": null} assert-window-property-false: {"searchIndex": null}
// source sidebar // source sidebar

View File

@@ -6,12 +6,12 @@ assert-css: ("#help", {"display": "block"})
assert-css: ("#help dd", {"font-size": "16px"}) assert-css: ("#help dd", {"font-size": "16px"})
assert-false: "#help-button > a" assert-false: "#help-button > a"
assert-css: ("#help", {"display": "block"}) assert-css: ("#help", {"display": "block"})
compare-elements-property: (".sub", "#help", ["offsetWidth"]) compare-elements-property: (".main-heading", "#help", ["offsetWidth"])
compare-elements-position: (".sub", "#help", ["x"]) compare-elements-position: (".main-heading", "#help", ["x"])
set-window-size: (500, 1000) // Try mobile next. set-window-size: (500, 1000) // Try mobile next.
assert-css: ("#help", {"display": "block"}) assert-css: ("#help", {"display": "block"})
compare-elements-property: (".sub", "#help", ["offsetWidth"]) compare-elements-property: (".main-heading", "#help", ["offsetWidth"])
compare-elements-position: (".sub", "#help", ["x"]) compare-elements-position: (".main-heading", "#help", ["x"])
// Checking the color of the elements of the help menu. // Checking the color of the elements of the help menu.
show-text: true show-text: true
@@ -54,19 +54,17 @@ go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=a"
wait-for: "#search-tabs" // Waiting for the search.js to load. wait-for: "#search-tabs" // Waiting for the search.js to load.
set-window-size: (1000, 1000) // Only supported on desktop. set-window-size: (1000, 1000) // Only supported on desktop.
assert-false: "#help" assert-false: "#help"
click: "#help-button > a" click: "rustdoc-toolbar .help-menu > a"
assert-css: ("#help", {"display": "block"}) assert-css: ("#help", {"display": "block"})
assert-css: ("#help dd", {"font-size": "16px"}) assert-css: ("#help dd", {"font-size": "16px"})
click: "#help-button > a" click: "rustdoc-toolbar .help-menu > a"
assert-css: ("#help", {"display": "none"}) assert-false: "#help"
compare-elements-property-false: (".sub", "#help", ["offsetWidth"])
compare-elements-position-false: (".sub", "#help", ["x"])
// This test ensures that the "the rustdoc book" anchor link within the help popover works. // This test ensures that the "the rustdoc book" anchor link within the help popover works.
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=a" go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=a"
wait-for: "#search-tabs" // Waiting for the search.js to load. wait-for: "#search-tabs" // Waiting for the search.js to load.
set-window-size: (1000, 1000) // Popover only appears when the screen width is >700px. set-window-size: (1000, 1000) // Popover only appears when the screen width is >700px.
assert-false: "#help" assert-false: "#help"
click: "#help-button > a" click: "rustdoc-toolbar .help-menu > a"
click: "//*[@id='help']//a[text()='the rustdoc book']" click: "//*[@id='help']//a[text()='the rustdoc book']"
wait-for-document-property: ({"URL": "https://doc.rust-lang.org/"}, STARTS_WITH) wait-for-document-property: ({"URL": "https://doc.rust-lang.org/"}, STARTS_WITH)

View File

@@ -1,20 +1,19 @@
// Checks sidebar resizing stays synced with the setting // Checks sidebar resizing stays synced with the setting
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" go-to: "file://" + |DOC_PATH| + "/settings.html"
set-window-size: (400, 600) set-window-size: (400, 600)
// Verify that the "hide" option is unchecked // Verify that the "hide" option is unchecked
click: "#settings-menu"
wait-for: "#settings" wait-for: "#settings"
assert-css: ("#settings", {"display": "block"}) assert-css: ("#settings", {"display": "block"})
assert-property: ("#hide-sidebar", {"checked": "false"}) assert-property: ("#hide-sidebar", {"checked": "false"})
assert-css: (".mobile-topbar", {"display": "flex"}) assert-css: ("rustdoc-topbar", {"display": "flex"})
// Toggle it // Toggle it
click: "#hide-sidebar" click: "#hide-sidebar"
assert-property: ("#hide-sidebar", {"checked": "true"}) assert-property: ("#hide-sidebar", {"checked": "true"})
assert-css: (".mobile-topbar", {"display": "none"}) assert-css: ("rustdoc-topbar", {"display": "none"})
// Toggle it again // Toggle it again
click: "#hide-sidebar" click: "#hide-sidebar"
assert-property: ("#hide-sidebar", {"checked": "false"}) assert-property: ("#hide-sidebar", {"checked": "false"})
assert-css: (".mobile-topbar", {"display": "flex"}) assert-css: ("rustdoc-topbar", {"display": "flex"})

View File

@@ -8,8 +8,3 @@ assert-property: (".sidebar-crate .logo-container", {"offsetWidth": "96", "offse
// offsetWidth = width of sidebar, offsetHeight = height + top padding // offsetWidth = width of sidebar, offsetHeight = height + top padding
assert-property: (".sidebar-crate .logo-container img", {"offsetWidth": "48", "offsetHeight": 64}) assert-property: (".sidebar-crate .logo-container img", {"offsetWidth": "48", "offsetHeight": 64})
assert-css: (".sidebar-crate .logo-container img", {"border-top-width": "16px", "margin-top": "-16px"}) assert-css: (".sidebar-crate .logo-container img", {"border-top-width": "16px", "margin-top": "-16px"})
set-window-size: (400, 600)
// offset = size + margin
assert-property: (".mobile-topbar .logo-container", {"offsetWidth": "55", "offsetHeight": 45})
assert-property: (".mobile-topbar .logo-container img", {"offsetWidth": "35", "offsetHeight": 35})

View File

@@ -20,7 +20,7 @@ store-position: (
{"x": second_line_x, "y": second_line_y}, {"x": second_line_x, "y": second_line_y},
) )
assert: |first_line_x| != |second_line_x| && |first_line_x| == 521 && |second_line_x| == 277 assert: |first_line_x| != |second_line_x| && |first_line_x| == 521 && |second_line_x| == 277
assert: |first_line_y| != |second_line_y| && |first_line_y| == 718 && |second_line_y| == 741 assert: |first_line_y| != |second_line_y| && |first_line_y| == 676 && |second_line_y| == 699
// Now we ensure that they're not rendered on the same line. // Now we ensure that they're not rendered on the same line.
set-window-size: (1100, 800) set-window-size: (1100, 800)

View File

@@ -5,18 +5,18 @@ go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
// First we change the title to make it big. // First we change the title to make it big.
set-window-size: (350, 800) set-window-size: (350, 800)
// We ensure that the "format" of the title is the same as the one we'll use. // We ensure that the "format" of the title is the same as the one we'll use.
assert-text: (".mobile-topbar .location a", "test_docs") assert-text: ("rustdoc-topbar h2 a", "Crate test_docs")
// We store the height we know is correct. // We store the height we know is correct.
store-property: (".mobile-topbar .location", {"offsetHeight": height}) store-property: ("rustdoc-topbar h2", {"offsetHeight": height})
// We change the crate name to something longer. // We change the crate name to something longer.
set-text: (".mobile-topbar .location a", "cargo_packager_resource_resolver") set-text: ("rustdoc-topbar h2 a", "cargo_packager_resource_resolver")
// And we check that the size remained the same. // And we check that the size remained the same.
assert-property: (".mobile-topbar .location", {"offsetHeight": |height|}) assert-property: ("rustdoc-topbar h2", {"offsetHeight": |height|})
// Now we check if it works for the non-crate pages as well. // Now we check if it works for the non-crate pages as well.
go-to: "file://" + |DOC_PATH| + "/test_docs/struct.Foo.html" go-to: "file://" + |DOC_PATH| + "/test_docs/struct.Foo.html"
// We store the height we know is correct. // We store the height we know is correct.
store-property: (".mobile-topbar .location", {"offsetHeight": height}) store-property: ("rustdoc-topbar h2", {"offsetHeight": height})
set-text: (".mobile-topbar .location a", "Something_incredibly_long_because") set-text: ("rustdoc-topbar h2 a", "Something_incredibly_long_because")
// And we check that the size remained the same. // And we check that the size remained the same.
assert-property: (".mobile-topbar .location", {"offsetHeight": |height|}) assert-property: ("rustdoc-topbar h2", {"offsetHeight": |height|})

View File

@@ -5,7 +5,7 @@ set-window-size: (400, 600)
set-font-size: 18 set-font-size: 18
wait-for: 100 // wait a bit for the resize and the font-size change to be fully taken into account. wait-for: 100 // wait a bit for the resize and the font-size change to be fully taken into account.
assert-property: (".mobile-topbar h2", {"offsetHeight": 33}) assert-property: ("rustdoc-topbar h2", {"offsetHeight": 33})
// On the settings page, the theme buttons should not line-wrap. Instead, they should // On the settings page, the theme buttons should not line-wrap. Instead, they should
// all be placed as a group on a line below the setting name "Theme." // all be placed as a group on a line below the setting name "Theme."

View File

@@ -82,15 +82,6 @@ call-function: ("check-notable-tooltip-position", {
"i_x": 528, "i_x": 528,
}) })
// Checking on mobile now.
set-window-size: (650, 600)
wait-for-size: ("body", {"width": 650})
call-function: ("check-notable-tooltip-position-complete", {
"x": 26,
"i_x": 305,
"popover_x": 0,
})
// Now check the colors. // Now check the colors.
define-function: ( define-function: (
"check-colors", "check-colors",
@@ -176,6 +167,15 @@ call-function: (
}, },
) )
// Checking on mobile now.
set-window-size: (650, 600)
wait-for-size: ("body", {"width": 650})
call-function: ("check-notable-tooltip-position-complete", {
"x": 26,
"i_x": 305,
"popover_x": 0,
})
reload: reload:
// Check that pressing escape works // Check that pressing escape works
@@ -189,7 +189,7 @@ assert: "#method\.create_an_iterator_from_read .tooltip:focus"
// Check that clicking outside works. // Check that clicking outside works.
click: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']" click: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']"
assert-count: ("//*[@class='tooltip popover']", 1) assert-count: ("//*[@class='tooltip popover']", 1)
click: ".search-input" click: ".main-heading h1"
assert-count: ("//*[@class='tooltip popover']", 0) assert-count: ("//*[@class='tooltip popover']", 0)
assert-false: "#method\.create_an_iterator_from_read .tooltip:focus" assert-false: "#method\.create_an_iterator_from_read .tooltip:focus"
@@ -219,14 +219,14 @@ define-function: (
store-window-property: {"scrollY": scroll} store-window-property: {"scrollY": scroll}
click: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']" click: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']"
wait-for: "//*[@class='tooltip popover']" wait-for: "//*[@class='tooltip popover']"
click: "#settings-menu a" click: ".main-heading h1"
} }
) )
// Now we check that the focus isn't given back to the wrong item when opening // Now we check that the focus isn't given back to the wrong item when opening
// another popover. // another popover.
call-function: ("setup-popup", {}) call-function: ("setup-popup", {})
click: ".search-input" click: ".main-heading h1"
// We ensure we didn't come back to the previous focused item. // We ensure we didn't come back to the previous focused item.
assert-window-property-false: {"scrollY": |scroll|} assert-window-property-false: {"scrollY": |scroll|}
@@ -251,7 +251,7 @@ reload:
assert-count: ("//*[@class='tooltip popover']", 0) assert-count: ("//*[@class='tooltip popover']", 0)
click: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']" click: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']"
assert-count: ("//*[@class='tooltip popover']", 1) assert-count: ("//*[@class='tooltip popover']", 1)
click: "#settings-menu a" click: "rustdoc-toolbar .settings-menu a"
wait-for: "#settings" wait-for: "#settings"
assert-count: ("//*[@class='tooltip popover']", 0) assert-count: ("//*[@class='tooltip popover']", 0)
assert-false: "#method\.create_an_iterator_from_read .tooltip:focus" assert-false: "#method\.create_an_iterator_from_read .tooltip:focus"

View File

@@ -3,33 +3,33 @@ include: "utils.goml"
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=test" go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=test"
wait-for: "#crate-search" wait-for: "#crate-search"
// First we check that the help menu doesn't exist yet. // First we check that the help menu doesn't exist yet.
assert-false: "#help-button .popover" assert-false: "rustdoc-toolbar .help-menu .popover"
// Then we display the help menu. // Then we display the help menu.
click: "#help-button" click: "rustdoc-toolbar .help-menu"
assert: "#help-button .popover" assert: "rustdoc-toolbar .help-menu .popover"
assert-css: ("#help-button .popover", {"display": "block"}) assert-css: ("rustdoc-toolbar .help-menu .popover", {"display": "block"})
// Now we click somewhere else on the page to ensure it is handling the blur event // Now we click somewhere else on the page to ensure it is handling the blur event
// correctly. // correctly.
click: ".sidebar" click: ".sidebar"
assert-css: ("#help-button .popover", {"display": "none"}) assert-false: "rustdoc-toolbar .help-menu .popover"
// Now we will check that we cannot have two "pocket menus" displayed at the same time. // Now we will check that we cannot have two "pocket menus" displayed at the same time.
click: "#help-button" click: "rustdoc-toolbar .help-menu"
assert-css: ("#help-button .popover", {"display": "block"}) assert-css: ("rustdoc-toolbar .help-menu .popover", {"display": "block"})
click: "#settings-menu" click: "rustdoc-toolbar .settings-menu"
assert-css: ("#help-button .popover", {"display": "none"}) assert-false: "rustdoc-toolbar .help-menu .popover"
assert-css: ("#settings-menu .popover", {"display": "block"}) assert-css: ("rustdoc-toolbar .settings-menu .popover", {"display": "block"})
// Now the other way. // Now the other way.
click: "#help-button" click: "rustdoc-toolbar .help-menu"
assert-css: ("#help-button .popover", {"display": "block"}) assert-css: ("rustdoc-toolbar .help-menu .popover", {"display": "block"})
assert-css: ("#settings-menu .popover", {"display": "none"}) assert-css: ("rustdoc-toolbar .settings-menu .popover", {"display": "none"})
// Now verify that clicking the help menu again closes it. // Now verify that clicking the help menu again closes it.
click: "#help-button" click: "rustdoc-toolbar .help-menu"
assert-css: ("#help-button .popover", {"display": "none"}) assert-false: "rustdoc-toolbar .help-menu .popover"
assert-css: ("#settings-menu .popover", {"display": "none"}) assert-css: (".settings-menu .popover", {"display": "none"})
define-function: ( define-function: (
"check-popover-colors", "check-popover-colors",
@@ -37,13 +37,21 @@ define-function: (
block { block {
call-function: ("switch-theme", {"theme": |theme|}) call-function: ("switch-theme", {"theme": |theme|})
click: "#help-button" click: "rustdoc-toolbar .help-menu"
assert-css: ( assert-css: (
"#help-button .popover", "rustdoc-toolbar .help-menu .popover",
{"display": "block", "border-color": |border_color|}, {"display": "block", "border-color": |border_color|},
) )
compare-elements-css: ("#help-button .popover", "#help-button .top", ["border-color"]) compare-elements-css: (
compare-elements-css: ("#help-button .popover", "#help-button .bottom", ["border-color"]) "rustdoc-toolbar .help-menu .popover",
"rustdoc-toolbar .help-menu .top",
["border-color"],
)
compare-elements-css: (
"rustdoc-toolbar .help-menu .popover",
"rustdoc-toolbar .help-menu .bottom",
["border-color"],
)
} }
) )
@@ -63,8 +71,21 @@ call-function: ("check-popover-colors", {
// Opening the mobile sidebar should close the settings popover. // Opening the mobile sidebar should close the settings popover.
set-window-size: (650, 600) set-window-size: (650, 600)
click: "#settings-menu a" click: "rustdoc-topbar .settings-menu a"
assert-css: ("#settings-menu .popover", {"display": "block"}) assert-css: ("rustdoc-topbar .settings-menu .popover", {"display": "block"})
click: ".sidebar-menu-toggle" click: ".sidebar-menu-toggle"
assert: "//*[@class='sidebar shown']" assert: "//*[@class='sidebar shown']"
assert-css: ("#settings-menu .popover", {"display": "none"}) assert-css: ("rustdoc-topbar .settings-menu .popover", {"display": "none"})
// Opening the settings popover should close the sidebar.
click: ".settings-menu a"
assert-css: ("rustdoc-topbar .settings-menu .popover", {"display": "block"})
assert-false: "//*[@class='sidebar shown']"
// Opening the settings popover at start (which async loads stuff) should also close.
reload:
click: ".sidebar-menu-toggle"
assert: "//*[@class='sidebar shown']"
assert-false: "rustdoc-topbar .settings-menu .popover"
click: "rustdoc-topbar .settings-menu a"
assert-false: "//*[@class='sidebar shown']"
wait-for: "rustdoc-topbar .settings-menu .popover"

View File

@@ -27,7 +27,7 @@ define-function: (
"color": |help_hover_color|, "color": |help_hover_color|,
}) })
// Moving the cursor to another item to not break next runs. // Moving the cursor to another item to not break next runs.
move-cursor-to: ".search-input" move-cursor-to: "#search-button"
} }
) )

View File

@@ -64,8 +64,8 @@ assert-size: (".more-scraped-examples .scraped-example .example-wrap", {
store-value: (offset_y, 4) store-value: (offset_y, 4)
// First with desktop // First with desktop
assert-position: (".scraped-example", {"y": 256}) assert-position: (".scraped-example", {"y": 214})
assert-position: (".scraped-example .prev", {"y": 256 + |offset_y|}) assert-position: (".scraped-example .prev", {"y": 214 + |offset_y|})
// Gradient background should be at the top of the code block. // Gradient background should be at the top of the code block.
assert-css: (".scraped-example .example-wrap::before", {"top": "0px"}) assert-css: (".scraped-example .example-wrap::before", {"top": "0px"})
@@ -74,8 +74,8 @@ assert-css: (".scraped-example .example-wrap::after", {"bottom": "0px"})
// Then with mobile // Then with mobile
set-window-size: (600, 600) set-window-size: (600, 600)
store-size: (".scraped-example .scraped-example-title", {"height": title_height}) store-size: (".scraped-example .scraped-example-title", {"height": title_height})
assert-position: (".scraped-example", {"y": 291}) assert-position: (".scraped-example", {"y": 249})
assert-position: (".scraped-example .prev", {"y": 291 + |offset_y| + |title_height|}) assert-position: (".scraped-example .prev", {"y": 249 + |offset_y| + |title_height|})
define-function: ( define-function: (
"check_title_and_code_position", "check_title_and_code_position",

View File

@@ -25,7 +25,7 @@ define-function: (
// We put the toggle in the original state. // We put the toggle in the original state.
click: ".more-examples-toggle" click: ".more-examples-toggle"
// Moving cursor away from the toggle line to prevent disrupting next test. // Moving cursor away from the toggle line to prevent disrupting next test.
move-cursor-to: ".search-input" move-cursor-to: "rustdoc-toolbar #search-button"
}, },
) )

View File

@@ -7,6 +7,7 @@ focus: ".search-input"
press-key: "Enter" press-key: "Enter"
wait-for: "#search-tabs" wait-for: "#search-tabs"
wait-for-false: "#search-tabs .count.loading"
assert-count: ("#search-tabs button", 1) assert-count: ("#search-tabs button", 1)
assert-count: (".search-results > a", 1) assert-count: (".search-results > a", 1)
@@ -32,6 +33,7 @@ focus: ".search-input"
press-key: "Enter" press-key: "Enter"
wait-for: "#search-tabs" wait-for: "#search-tabs"
wait-for-false: "#search-tabs .count.loading"
assert-text: ("//div[@class='type-signature']", "F -> WhereWhitespace<T>") assert-text: ("//div[@class='type-signature']", "F -> WhereWhitespace<T>")
assert-count: ("#search-tabs button", 1) assert-count: ("#search-tabs button", 1)
assert-count: (".search-results > a", 1) assert-count: (".search-results > a", 1)

View File

@@ -1,101 +1,60 @@
// ignore-tidy-linelength // ignore-tidy-linelength
include: "utils.goml"
// Checks that the search tab result tell the user about corrections // Checks that the search tab result tell the user about corrections
// First, try a search-by-name // First, try a search-by-name
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
// Intentionally wrong spelling of "NotableStructWithLongName" call-function: ("perform-search", {"query": "NotableStructWithLongNamr"})
write-into: (".search-input", "NotableStructWithLongNamr")
// To be SURE that the search will be run.
press-key: 'Enter'
// Waiting for the search results to appear...
wait-for: "#search-tabs"
// Corrections aren't shown on the "In Names" tab. // Corrections aren't shown on the "In Names" tab.
assert: "#search-tabs button.selected:first-child" assert: "#search-tabs button.selected:first-child"
assert-css: (".search-corrections", { assert-false: ".search-results:nth-child(1) .search-corrections"
"display": "none"
})
// Corrections do get shown on the "In Parameters" tab. // Corrections do get shown on the "In Parameters" tab.
click: "#search-tabs button:nth-child(2)" click: "#search-tabs button:nth-child(2)"
assert: "#search-tabs button.selected:nth-child(2)" assert: "#search-tabs button.selected:nth-child(2)"
assert-css: (".search-corrections", {
"display": "block"
})
assert-text: ( assert-text: (
".search-corrections", ".search-results:nth-child(2) .search-corrections",
"Type \"NotableStructWithLongNamr\" not found. Showing results for closest type name \"notablestructwithlongname\" instead." "Type \"NotableStructWithLongNamr\" not found. Showing results for closest type name \"NotableStructWithLongName\" instead."
) )
// Corrections do get shown on the "In Return Type" tab. // Corrections do get shown on the "In Return Type" tab.
click: "#search-tabs button:nth-child(3)" click: "#search-tabs button:nth-child(3)"
assert: "#search-tabs button.selected:nth-child(3)" assert: "#search-tabs button.selected:nth-child(3)"
assert-css: (".search-corrections", {
"display": "block"
})
assert-text: ( assert-text: (
".search-corrections", ".search-results:nth-child(3) .search-corrections",
"Type \"NotableStructWithLongNamr\" not found. Showing results for closest type name \"notablestructwithlongname\" instead." "Type \"NotableStructWithLongNamr\" not found. Showing results for closest type name \"NotableStructWithLongName\" instead."
) )
// Now, explicit return values // Now, explicit return values
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
// Intentionally wrong spelling of "NotableStructWithLongName" call-function: ("perform-search", {"query": "-> NotableStructWithLongNamr"})
write-into: (".search-input", "-> NotableStructWithLongNamr")
// To be SURE that the search will be run.
press-key: 'Enter'
// Waiting for the search results to appear...
wait-for: "#search-tabs"
assert-css: (".search-corrections", {
"display": "block"
})
assert-text: ( assert-text: (
".search-corrections", ".search-results.active .search-corrections",
"Type \"NotableStructWithLongNamr\" not found. Showing results for closest type name \"notablestructwithlongname\" instead." "Type \"NotableStructWithLongNamr\" not found and used as generic parameter. Consider searching for \"NotableStructWithLongName\" instead."
) )
// Now, generic correction // Now, generic correction
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
// Intentionally wrong spelling of "NotableStructWithLongName" call-function: ("perform-search", {"query": "NotableStructWithLongNamr, NotableStructWithLongNamr"})
write-into: (".search-input", "NotableStructWithLongNamr, NotableStructWithLongNamr")
// To be SURE that the search will be run.
press-key: 'Enter'
// Waiting for the search results to appear...
wait-for: "#search-tabs"
assert-css: (".search-corrections", {
"display": "block"
})
assert-text: ( assert-text: (
".search-corrections", ".search-failed.active .search-corrections",
"Type \"NotableStructWithLongNamr\" not found and used as generic parameter. Consider searching for \"notablestructwithlongname\" instead." "Type \"NotableStructWithLongNamr\" not found and used as generic parameter. Consider searching for \"NotableStructWithLongName\" instead."
) )
// Now, generic correction plus error // Now, generic correction plus error
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
// Intentionally wrong spelling of "NotableStructWithLongName" call-function: ("perform-search", {"query": "Foo<NotableStructWithLongNamr>,y"})
write-into: (".search-input", "Foo<NotableStructWithLongNamr>,y")
// To be SURE that the search will be run.
press-key: 'Enter'
// Waiting for the search results to appear...
wait-for: "#search-tabs"
assert-css: (".search-corrections", {
"display": "block"
})
assert-text: ( assert-text: (
".search-corrections", ".search-failed.active .search-corrections",
"Type \"NotableStructWithLongNamr\" not found and used as generic parameter. Consider searching for \"notablestructwithlongname\" instead." "Type \"NotableStructWithLongNamr\" not found and used as generic parameter. Consider searching for \"NotableStructWithLongName\" instead."
) )
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
// Intentionally wrong spelling of "NotableStructWithLongName" call-function: ("perform-search", {"query": "generic:NotableStructWithLongNamr<x>,y"})
write-into: (".search-input", "generic:NotableStructWithLongNamr<x>,y")
// To be SURE that the search will be run.
press-key: 'Enter'
// Waiting for the search results to appear...
wait-for: "#search-tabs"
assert-css: (".error", { assert-css: (".error", {
"display": "block" "display": "block"

View File

@@ -8,6 +8,7 @@ define-function: (
[theme, error_background], [theme, error_background],
block { block {
call-function: ("switch-theme", {"theme": |theme|}) call-function: ("switch-theme", {"theme": |theme|})
wait-for-false: "#search-tabs .count.loading"
wait-for: "#search .error code" wait-for: "#search .error code"
assert-css: ("#search .error code", {"background-color": |error_background|}) assert-css: ("#search .error code", {"background-color": |error_background|})
} }

View File

@@ -2,11 +2,7 @@
include: "utils.goml" include: "utils.goml"
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
show-text: true show-text: true
write-into: (".search-input", "test") call-function: ("perform-search", {"query": "test"})
// To be SURE that the search will be run.
press-key: 'Enter'
// Waiting for the search results to appear...
wait-for: "#search-tabs"
assert-text: ("#results .externcrate", "test_docs") assert-text: ("#results .externcrate", "test_docs")
wait-for: "#crate-search" wait-for: "#crate-search"
@@ -21,6 +17,7 @@ press-key: "ArrowDown"
press-key: "Enter" press-key: "Enter"
// Waiting for the search results to appear... // Waiting for the search results to appear...
wait-for: "#search-tabs" wait-for: "#search-tabs"
wait-for-false: "#search-tabs .count.loading"
assert-document-property: ({"URL": "&filter-crate="}, CONTAINS) assert-document-property: ({"URL": "&filter-crate="}, CONTAINS)
// We check that there is no more "test_docs" appearing. // We check that there is no more "test_docs" appearing.
assert-false: "#results .externcrate" assert-false: "#results .externcrate"
@@ -31,7 +28,8 @@ assert-property: ("#crate-search", {"value": "lib2"})
// crate filtering. // crate filtering.
press-key: "Escape" press-key: "Escape"
wait-for-css: ("#main-content", {"display": "block"}) wait-for-css: ("#main-content", {"display": "block"})
focus: ".search-input" click: "#search-button"
wait-for: ".search-input"
wait-for-css: ("#main-content", {"display": "none"}) wait-for-css: ("#main-content", {"display": "none"})
// We check that there is no more "test_docs" appearing. // We check that there is no more "test_docs" appearing.
assert-false: "#results .externcrate" assert-false: "#results .externcrate"
@@ -47,6 +45,7 @@ press-key: "ArrowUp"
press-key: "Enter" press-key: "Enter"
// Waiting for the search results to appear... // Waiting for the search results to appear...
wait-for: "#search-tabs" wait-for: "#search-tabs"
wait-for-false: "#search-tabs .count.loading"
assert-property: ("#crate-search", {"value": "all crates"}) assert-property: ("#crate-search", {"value": "all crates"})
// Checking that the URL parameter is taken into account for crate filtering. // Checking that the URL parameter is taken into account for crate filtering.
@@ -56,8 +55,7 @@ assert-property: ("#crate-search", {"value": "lib2"})
assert-false: "#results .externcrate" assert-false: "#results .externcrate"
// Checking that the text for the "title" is correct (the "all crates" comes from the "<select>"). // Checking that the text for the "title" is correct (the "all crates" comes from the "<select>").
assert-text: (".search-results-title", "Results", STARTS_WITH) assert-text: (".search-switcher", "Search results in all crates", STARTS_WITH)
assert-text: (".search-results-title + .sub-heading", " in all crates", STARTS_WITH)
// Checking the display of the crate filter. // Checking the display of the crate filter.
// We start with the light theme. // We start with the light theme.
@@ -72,7 +70,7 @@ assert-css: ("#crate-search", {
}) })
// We now check the dark theme. // We now check the dark theme.
click: "#settings-menu" click: "rustdoc-toolbar .settings-menu"
wait-for: "#settings" wait-for: "#settings"
click: "#theme-dark" click: "#theme-dark"
wait-for-css: ("#crate-search", { wait-for-css: ("#crate-search", {

View File

@@ -2,6 +2,7 @@
include: "utils.goml" include: "utils.goml"
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=test" go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=test"
wait-for: "#search-tabs" // Waiting for the search.js to load. wait-for: "#search-tabs" // Waiting for the search.js to load.
wait-for-false: "#search-tabs .count.loading"
show-text: true show-text: true
define-function: ( define-function: (
@@ -31,7 +32,7 @@ define-function: (
}, },
) )
assert-css: ( assert-css: (
"#help-button > a", "rustdoc-toolbar .help-menu > a",
{ {
"color": |menu_button_a_color|, "color": |menu_button_a_color|,
"border-color": "transparent", "border-color": "transparent",
@@ -39,9 +40,9 @@ define-function: (
}, },
) )
// Hover help button. // Hover help button.
move-cursor-to: "#help-button" move-cursor-to: "rustdoc-toolbar .help-menu"
assert-css: ( assert-css: (
"#help-button > a", "rustdoc-toolbar .help-menu > a",
{ {
"color": |menu_button_a_color|, "color": |menu_button_a_color|,
"border-color": |menu_button_a_border_hover|, "border-color": |menu_button_a_border_hover|,
@@ -49,15 +50,15 @@ define-function: (
}, },
) )
// Link color inside // Link color inside
click: "#help-button" click: "rustdoc-toolbar .help-menu"
assert-css: ( assert-css: (
"#help a", "rustdoc-toolbar #help a",
{ {
"color": |menu_a_color|, "color": |menu_a_color|,
}, },
) )
assert-css: ( assert-css: (
"#settings-menu > a", "rustdoc-toolbar .settings-menu > a",
{ {
"color": |menu_button_a_color|, "color": |menu_button_a_color|,
"border-color": "transparent", "border-color": "transparent",
@@ -65,9 +66,9 @@ define-function: (
}, },
) )
// Hover settings menu. // Hover settings menu.
move-cursor-to: "#settings-menu" move-cursor-to: "rustdoc-toolbar .settings-menu"
assert-css: ( assert-css: (
"#settings-menu:hover > a", "rustdoc-toolbar .settings-menu:hover > a",
{ {
"color": |menu_button_a_color|, "color": |menu_button_a_color|,
"border-color": |menu_button_a_border_hover|, "border-color": |menu_button_a_border_hover|,
@@ -120,8 +121,10 @@ call-function: (
// Check that search input correctly decodes form encoding. // Check that search input correctly decodes form encoding.
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=a+b" go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=a+b"
wait-for: "#search-tabs" // Waiting for the search.js to load. wait-for: "#search-tabs" // Waiting for the search.js to load.
wait-for-false: "#search-tabs .count.loading"
assert-property: (".search-input", { "value": "a b" }) assert-property: (".search-input", { "value": "a b" })
// Check that literal + is not treated as space. // Check that literal + is not treated as space.
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=a%2Bb" go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=a%2Bb"
wait-for: "#search-tabs" // Waiting for the search.js to load. wait-for: "#search-tabs" // Waiting for the search.js to load.
wait-for-false: "#search-tabs .count.loading"
assert-property: (".search-input", { "value": "a+b" }) assert-property: (".search-input", { "value": "a+b" })

View File

@@ -2,10 +2,13 @@
// The PR which fixed it is: https://github.com/rust-lang/rust/pull/81592 // The PR which fixed it is: https://github.com/rust-lang/rust/pull/81592
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
set-window-size: (463, 700) set-window-size: (463, 700)
// We first check that the search input isn't already focused. click: "#search-button"
assert-false: ("input.search-input:focus") wait-for: ".search-input"
click: "input.search-input" assert: "input.search-input:focus"
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
reload: reload:
set-window-size: (750, 700) set-window-size: (750, 700)
click: "input.search-input" click: "#search-button"
assert: ("input.search-input:focus") wait-for: ".search-input"
assert: "input.search-input:focus"

View File

@@ -1,28 +1,25 @@
// Checks that the search tab results work correctly with function signature syntax // Checks that the search tab results work correctly with function signature syntax
// First, try a search-by-name // First, try a search-by-name
include: "utils.goml"
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
write-into: (".search-input", "Foo") call-function: ("perform-search", {"query": "Foo"})
// To be SURE that the search will be run.
press-key: 'Enter'
// Waiting for the search results to appear...
wait-for: "#search-tabs"
// Now use the keyboard commands to switch to the third result. // Now use the keyboard commands to switch to the third result.
press-key: "ArrowDown" press-key: "ArrowDown"
press-key: "ArrowDown" press-key: "ArrowDown"
press-key: "ArrowDown" press-key: "ArrowDown"
assert: ".search-results.active > a:focus:nth-of-type(3)" wait-for: ".search-results.active > a:focus:nth-of-type(3)"
// Now switch to the second tab, then back to the first one, then arrow back up. // Now switch to the second tab, then back to the first one, then arrow back up.
press-key: "ArrowRight" press-key: "ArrowRight"
assert: ".search-results.active:nth-of-type(2) > a:focus:nth-of-type(1)" wait-for: ".search-results.active:nth-of-type(2) > a:focus:nth-of-type(1)"
press-key: "ArrowLeft" press-key: "ArrowLeft"
assert: ".search-results.active:nth-of-type(1) > a:focus:nth-of-type(3)" wait-for: ".search-results.active:nth-of-type(1) > a:focus:nth-of-type(3)"
press-key: "ArrowUp" press-key: "ArrowUp"
assert: ".search-results.active > a:focus:nth-of-type(2)" wait-for: ".search-results.active > a:focus:nth-of-type(2)"
press-key: "ArrowUp" press-key: "ArrowUp"
assert: ".search-results.active > a:focus:nth-of-type(1)" wait-for: ".search-results.active > a:focus:nth-of-type(1)"
press-key: "ArrowUp" press-key: "ArrowUp"
assert: ".search-input:focus" wait-for: ".search-input:focus"
press-key: "ArrowDown" press-key: "ArrowDown"
assert: ".search-results.active > a:focus:nth-of-type(1)" wait-for: ".search-results.active > a:focus:nth-of-type(1)"

View File

@@ -6,10 +6,8 @@ call-function: ("switch-theme", {"theme": "dark"})
// First we check that the reexport has the correct ID and no background color. // First we check that the reexport has the correct ID and no background color.
assert-text: ("//*[@id='reexport.TheStdReexport']", "pub use ::std as TheStdReexport;") assert-text: ("//*[@id='reexport.TheStdReexport']", "pub use ::std as TheStdReexport;")
assert-css: ("//*[@id='reexport.TheStdReexport']", {"background-color": "rgba(0, 0, 0, 0)"}) assert-css: ("//*[@id='reexport.TheStdReexport']", {"background-color": "rgba(0, 0, 0, 0)"})
write-into: (".search-input", "TheStdReexport") call-function: ("perform-search", {"query": "TheStdReexport"})
// To be SURE that the search will be run. assert: "//a[@class='result-import']"
press-key: 'Enter'
wait-for: "//a[@class='result-import']"
assert-attribute: ( assert-attribute: (
"//a[@class='result-import']", "//a[@class='result-import']",
{"href": "../test_docs/index.html#reexport.TheStdReexport"}, {"href": "../test_docs/index.html#reexport.TheStdReexport"},
@@ -21,9 +19,8 @@ wait-for-css: ("//*[@id='reexport.TheStdReexport']", {"background-color": "#494a
// We now check that the alias is working as well on the reexport. // We now check that the alias is working as well on the reexport.
// To be SURE that the search will be run. // To be SURE that the search will be run.
press-key: 'Enter' call-function: ("perform-search", {"query": "AliasForTheStdReexport"})
write-into: (".search-input", "AliasForTheStdReexport") assert: "//a[@class='result-import']"
wait-for: "//a[@class='result-import']"
assert-text: ( assert-text: (
"a.result-import .result-name", "a.result-import .result-name",
"re-export AliasForTheStdReexport - see test_docs::TheStdReexport", "re-export AliasForTheStdReexport - see test_docs::TheStdReexport",

View File

@@ -14,6 +14,7 @@ define-function: (
// Waiting for the search results to appear... // Waiting for the search results to appear...
wait-for: "#search-tabs" wait-for: "#search-tabs"
wait-for-false: "#search-tabs .count.loading"
assert-css: ( assert-css: (
"#search-tabs > button > .count", "#search-tabs > button > .count",
{"color": |count_color|}, {"color": |count_color|},
@@ -212,11 +213,7 @@ call-function: ("check-search-color", {
// Check the alias. // Check the alias.
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
write-into: (".search-input", "thisisanalias") call-function: ("perform-search", {"query": "thisisanalias"})
// To be SURE that the search will be run.
press-key: 'Enter'
// Waiting for the search results to appear...
wait-for: "#search-tabs"
define-function: ( define-function: (
"check-alias", "check-alias",

View File

@@ -2,4 +2,5 @@
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=some_more_function" go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=some_more_function"
// Waiting for the search results to appear... // Waiting for the search results to appear...
wait-for: "#search-tabs" wait-for: "#search-tabs"
wait-for-false: "#search-tabs .count.loading"
assert-text: (".search-results .desc code", "format!") assert-text: (".search-results .desc code", "format!")

View File

@@ -7,6 +7,7 @@ write-into: (".search-input", "test")
// To be SURE that the search will be run. // To be SURE that the search will be run.
press-key: 'Enter' press-key: 'Enter'
wait-for: "#crate-search" wait-for: "#crate-search"
wait-for-false: "#search-tabs .count.loading"
// The width is returned by "getComputedStyle" which returns the exact number instead of the // The width is returned by "getComputedStyle" which returns the exact number instead of the
// CSS rule which is "50%"... // CSS rule which is "50%"...
assert-size: (".search-results div.desc", {"width": 248}) assert-size: (".search-results div.desc", {"width": 248})
@@ -34,6 +35,7 @@ assert: |new_width| < |width| - 10
// Check that if the search is too long on mobile, it'll go under the "typename". // Check that if the search is too long on mobile, it'll go under the "typename".
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=SuperIncrediblyLongLongLongLongLongLongLongGigaGigaGigaMegaLongLongLongStructName" go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=SuperIncrediblyLongLongLongLongLongLongLongGigaGigaGigaMegaLongLongLongStructName"
wait-for: "#crate-search" wait-for: "#crate-search"
wait-for-false: "#search-tabs .count.loading"
compare-elements-position-near: ( compare-elements-position-near: (
".search-results .result-name .typename", ".search-results .result-name .typename",
".search-results .result-name .path", ".search-results .result-name .path",
@@ -51,7 +53,7 @@ set-window-size: (900, 900)
// First we check the current width, height and position. // First we check the current width, height and position.
assert-css: ("#crate-search", {"width": "159px"}) assert-css: ("#crate-search", {"width": "159px"})
store-size: (".search-results-title", { store-size: (".search-switcher", {
"height": search_results_title_height, "height": search_results_title_height,
"width": search_results_title_width, "width": search_results_title_width,
}) })
@@ -64,8 +66,8 @@ set-text: (
) )
// Then we compare again to confirm the height didn't change. // Then we compare again to confirm the height didn't change.
assert-size: ("#crate-search", {"width": 370}) assert-size: ("#crate-search", {"width": 185})
assert-size: (".search-results-title", { assert-size: (".search-switcher", {
"height": |search_results_title_height|, "height": |search_results_title_height|,
}) })
assert-css: ("#search", {"width": "640px"}) assert-css: ("#search", {"width": "640px"})
@@ -79,6 +81,7 @@ define-function: (
block { block {
call-function: ("switch-theme", {"theme": |theme|}) call-function: ("switch-theme", {"theme": |theme|})
wait-for: "#crate-search" wait-for: "#crate-search"
wait-for-false: "#search-tabs .count.loading"
assert-css: ("#crate-search", {"border": "1px solid " + |border|}) assert-css: ("#crate-search", {"border": "1px solid " + |border|})
assert-css: ("#crate-search-div::after", {"filter": |filter|}) assert-css: ("#crate-search-div::after", {"filter": |filter|})
move-cursor-to: "#crate-search" move-cursor-to: "#crate-search"

View File

@@ -9,6 +9,7 @@ assert-text-false: (".main-heading h1", "Struct test_docs::FooCopy item path")
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=struct%3AFoo" go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=struct%3AFoo"
// Waiting for the search results to appear... // Waiting for the search results to appear...
wait-for: "#search-tabs" wait-for: "#search-tabs"
wait-for-false: "#search-tabs .count.loading"
assert-text-false: (".main-heading h1", "Struct test_docs::FooCopy item path") assert-text-false: (".main-heading h1", "Struct test_docs::FooCopy item path")
// Ensure that the search results are displayed, not the "normal" content. // Ensure that the search results are displayed, not the "normal" content.
assert-css: ("#main-content", {"display": "none"}) assert-css: ("#main-content", {"display": "none"})
@@ -17,4 +18,4 @@ assert-css: ("#main-content", {"display": "none"})
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=struct%3AFoo&go_to_first=true" go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=struct%3AFoo&go_to_first=true"
// Waiting for the page to load... // Waiting for the page to load...
wait-for-text: (".main-heading .rustdoc-breadcrumbs", "test_docs") wait-for-text: (".main-heading .rustdoc-breadcrumbs", "test_docs")
wait-for-text: (".main-heading h1", "Struct FooCopy item path") wait-for-text: (".main-heading h1", "Struct Foo Copy item path")

View File

@@ -1,15 +1,12 @@
// ignore-tidy-linelength // ignore-tidy-linelength
include: "utils.goml"
// Checks that, if a type has two methods with the same name, they both get // Checks that, if a type has two methods with the same name, they both get
// linked correctly. // linked correctly.
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
// This should link to the inherent impl // This should link to the inherent impl
write-into: (".search-input", "ZyxwvutMethodDisambiguation -> bool") call-function: ("perform-search", {"query": "ZyxwvutMethodDisambiguation -> bool"})
// To be SURE that the search will be run.
press-key: 'Enter'
// Waiting for the search results to appear...
wait-for: "#search-tabs"
// Check the disambiguated link. // Check the disambiguated link.
assert-count: ("a.result-method", 1) assert-count: ("a.result-method", 1)
assert-attribute: ("a.result-method", { assert-attribute: ("a.result-method", {
@@ -25,11 +22,7 @@ assert: "section:target"
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
// This should link to the trait impl // This should link to the trait impl
write-into: (".search-input", "ZyxwvutMethodDisambiguation, usize -> usize") call-function: ("perform-search", {"query": "ZyxwvutMethodDisambiguation, usize -> usize"})
// To be SURE that the search will be run.
press-key: 'Enter'
// Waiting for the search results to appear...
wait-for: "#search-tabs"
// Check the disambiguated link. // Check the disambiguated link.
assert-count: ("a.result-method", 1) assert-count: ("a.result-method", 1)
assert-attribute: ("a.result-method", { assert-attribute: ("a.result-method", {
@@ -47,6 +40,7 @@ assert: "section:target"
// impl block's disambiguator is also acted upon. // impl block's disambiguator is also acted upon.
go-to: "file://" + |DOC_PATH| + "/lib2/index.html?search=MultiImplBlockStruct->bool" go-to: "file://" + |DOC_PATH| + "/lib2/index.html?search=MultiImplBlockStruct->bool"
wait-for: "#search-tabs" wait-for: "#search-tabs"
wait-for-false: "#search-tabs .count.loading"
assert-count: ("a.result-method", 1) assert-count: ("a.result-method", 1)
assert-attribute: ("a.result-method", { assert-attribute: ("a.result-method", {
"href": "../lib2/another_mod/struct.MultiImplBlockStruct.html#impl-MultiImplBlockStruct/method.second_fn" "href": "../lib2/another_mod/struct.MultiImplBlockStruct.html#impl-MultiImplBlockStruct/method.second_fn"
@@ -56,6 +50,7 @@ wait-for: "details:has(summary > #impl-MultiImplBlockStruct-1) > div section[id=
go-to: "file://" + |DOC_PATH| + "/lib2/index.html?search=MultiImplBlockStruct->u32" go-to: "file://" + |DOC_PATH| + "/lib2/index.html?search=MultiImplBlockStruct->u32"
wait-for: "#search-tabs" wait-for: "#search-tabs"
wait-for-false: "#search-tabs .count.loading"
assert-count: ("a.result-method", 1) assert-count: ("a.result-method", 1)
assert-attribute: ("a.result-method", { assert-attribute: ("a.result-method", {
"href": "../lib2/another_mod/struct.MultiImplBlockStruct.html#impl-MultiImplBlockTrait-for-MultiImplBlockStruct/method.second_fn" "href": "../lib2/another_mod/struct.MultiImplBlockStruct.html#impl-MultiImplBlockTrait-for-MultiImplBlockStruct/method.second_fn"

View File

@@ -1,8 +1,5 @@
// Checks that the "keyword" results have the expected text alongside them. // Checks that the "keyword" results have the expected text alongside them.
include: "utils.goml"
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
write-into: (".search-input", "for") call-function: ("perform-search", {"query": "for"})
// To be SURE that the search will be run.
press-key: 'Enter'
// Waiting for the search results to appear...
wait-for: "#search-tabs"
assert-text: (".result-keyword .result-name", "keyword for") assert-text: (".result-keyword .result-name", "keyword for")

View File

@@ -1,11 +1,9 @@
// Checks that the search tab results work correctly with function signature syntax // Checks that the search tab results work correctly with function signature syntax
// First, try a search-by-name // First, try a search-by-name
include: "utils.goml"
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
write-into: (".search-input", "Foo") call-function: ("perform-search", {"query": "Foo"})
// To be SURE that the search will be run.
press-key: 'Enter'
// Waiting for the search results to appear...
wait-for: "#search-tabs"
assert-attribute: ("#search-tabs > button:nth-of-type(1)", {"class": "selected"}) assert-attribute: ("#search-tabs > button:nth-of-type(1)", {"class": "selected"})
assert-text: ("#search-tabs > button:nth-of-type(1)", "In Names", STARTS_WITH) assert-text: ("#search-tabs > button:nth-of-type(1)", "In Names", STARTS_WITH)
assert: "input.search-input:focus" assert: "input.search-input:focus"
@@ -23,11 +21,7 @@ wait-for-attribute: ("#search-tabs > button:nth-of-type(3)", {"class": "selected
// Now try search-by-return // Now try search-by-return
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
write-into: (".search-input", "-> String") call-function: ("perform-search", {"query": "-> String"})
// To be SURE that the search will be run.
press-key: 'Enter'
// Waiting for the search results to appear...
wait-for: "#search-tabs"
assert-attribute: ("#search-tabs > button:nth-of-type(1)", {"class": "selected"}) assert-attribute: ("#search-tabs > button:nth-of-type(1)", {"class": "selected"})
assert-text: ("#search-tabs > button:nth-of-type(1)", "In Function Return Types", STARTS_WITH) assert-text: ("#search-tabs > button:nth-of-type(1)", "In Function Return Types", STARTS_WITH)
assert: "input.search-input:focus" assert: "input.search-input:focus"
@@ -45,30 +39,18 @@ wait-for-attribute: ("#search-tabs > button:nth-of-type(1)", {"class": "selected
// Try with a search-by-return with no results // Try with a search-by-return with no results
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
write-into: (".search-input", "-> Something") call-function: ("perform-search", {"query": "-> Something"})
// To be SURE that the search will be run.
press-key: 'Enter'
// Waiting for the search results to appear...
wait-for: "#search-tabs"
assert-attribute: ("#search-tabs > button:nth-of-type(1)", {"class": "selected"}) assert-attribute: ("#search-tabs > button:nth-of-type(1)", {"class": "selected"})
assert-text: ("#search-tabs > button:nth-of-type(1)", "In Function Return Types", STARTS_WITH) assert-text: ("#search-tabs > button:nth-of-type(1)", "In Function Return Types", STARTS_WITH)
// Try with a search-by-parameter // Try with a search-by-parameter
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
write-into: (".search-input", "usize,pattern") call-function: ("perform-search", {"query": "usize,pattern"})
// To be SURE that the search will be run.
press-key: 'Enter'
// Waiting for the search results to appear...
wait-for: "#search-tabs"
assert-attribute: ("#search-tabs > button:nth-of-type(1)", {"class": "selected"}) assert-attribute: ("#search-tabs > button:nth-of-type(1)", {"class": "selected"})
assert-text: ("#search-tabs > button:nth-of-type(1)", "In Function Parameters", STARTS_WITH) assert-text: ("#search-tabs > button:nth-of-type(1)", "In Function Parameters", STARTS_WITH)
// Try with a search-by-parameter-and-return // Try with a search-by-parameter-and-return
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
write-into: (".search-input", "pattern -> str") call-function: ("perform-search", {"query": "pattern -> str"})
// To be SURE that the search will be run.
press-key: 'Enter'
// Waiting for the search results to appear...
wait-for: "#search-tabs"
assert-attribute: ("#search-tabs > button:nth-of-type(1)", {"class": "selected"}) assert-attribute: ("#search-tabs > button:nth-of-type(1)", {"class": "selected"})
assert-text: ("#search-tabs > button:nth-of-type(1)", "In Function Signatures", STARTS_WITH) assert-text: ("#search-tabs > button:nth-of-type(1)", "In Function Signatures", STARTS_WITH)

View File

@@ -15,7 +15,8 @@ define-function: (
focus: ".search-input" focus: ".search-input"
press-key: "Enter" press-key: "Enter"
wait-for: "#search-tabs" wait-for: "#search-tabs .count"
wait-for-false: "#search-tabs .count.loading"
assert-css: ("#search-tabs > button:not(.selected)", { assert-css: ("#search-tabs > button:not(.selected)", {
"background-color": |background|, "background-color": |background|,
"border-bottom": |border_bottom|, "border-bottom": |border_bottom|,

View File

@@ -5,10 +5,7 @@ go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
store-value: (title, "test_docs - Rust") store-value: (title, "test_docs - Rust")
assert-document-property: {"title": |title|} assert-document-property: {"title": |title|}
write-into: (".search-input", "test") call-function: ("perform-search", {"query": "test"})
// To be SURE that the search will be run.
press-key: 'Enter'
wait-for: "#crate-search"
assert-document-property: {"title": '"test" Search - Rust'} assert-document-property: {"title": '"test" Search - Rust'}
@@ -16,6 +13,7 @@ set-property: (".search-input", {"value": "another one"})
// To be SURE that the search will be run. // To be SURE that the search will be run.
press-key: 'Enter' press-key: 'Enter'
wait-for: "#crate-search" wait-for: "#crate-search"
wait-for-false: "#search-tabs .count.loading"
assert-document-property: {"title": '"another one" Search - Rust'} assert-document-property: {"title": '"another one" Search - Rust'}

View File

@@ -9,7 +9,7 @@ define-function: (
[storage_value, setting_attribute_value, toggle_attribute_value], [storage_value, setting_attribute_value, toggle_attribute_value],
block { block {
assert-local-storage: {"rustdoc-auto-hide-large-items": |storage_value|} assert-local-storage: {"rustdoc-auto-hide-large-items": |storage_value|}
click: "#settings-menu" click: "rustdoc-toolbar .settings-menu"
wait-for: "#settings" wait-for: "#settings"
assert-property: ("#auto-hide-large-items", {"checked": |setting_attribute_value|}) assert-property: ("#auto-hide-large-items", {"checked": |setting_attribute_value|})
assert-attribute: (".item-decl .type-contents-toggle", {"open": |toggle_attribute_value|}) assert-attribute: (".item-decl .type-contents-toggle", {"open": |toggle_attribute_value|})

View File

@@ -6,7 +6,7 @@ define-function: (
[storage_value, setting_attribute_value, toggle_attribute_value], [storage_value, setting_attribute_value, toggle_attribute_value],
block { block {
assert-local-storage: {"rustdoc-auto-hide-method-docs": |storage_value|} assert-local-storage: {"rustdoc-auto-hide-method-docs": |storage_value|}
click: "#settings-menu" click: "rustdoc-toolbar .settings-menu"
wait-for: "#settings" wait-for: "#settings"
assert-property: ("#auto-hide-method-docs", {"checked": |setting_attribute_value|}) assert-property: ("#auto-hide-method-docs", {"checked": |setting_attribute_value|})
assert-attribute: (".toggle.method-toggle", {"open": |toggle_attribute_value|}) assert-attribute: (".toggle.method-toggle", {"open": |toggle_attribute_value|})

View File

@@ -5,7 +5,7 @@ define-function: (
[storage_value, setting_attribute_value, toggle_attribute_value], [storage_value, setting_attribute_value, toggle_attribute_value],
block { block {
assert-local-storage: {"rustdoc-auto-hide-trait-implementations": |storage_value|} assert-local-storage: {"rustdoc-auto-hide-trait-implementations": |storage_value|}
click: "#settings-menu" click: "rustdoc-toolbar .settings-menu"
wait-for: "#settings" wait-for: "#settings"
assert-property: ("#auto-hide-trait-implementations", {"checked": |setting_attribute_value|}) assert-property: ("#auto-hide-trait-implementations", {"checked": |setting_attribute_value|})
assert-attribute: ("#trait-implementations-list > details", {"open": |toggle_attribute_value|}, ALL) assert-attribute: ("#trait-implementations-list > details", {"open": |toggle_attribute_value|}, ALL)

View File

@@ -5,7 +5,7 @@ define-function: (
[storage_value, setting_attribute_value], [storage_value, setting_attribute_value],
block { block {
assert-local-storage: {"rustdoc-go-to-only-result": |storage_value|} assert-local-storage: {"rustdoc-go-to-only-result": |storage_value|}
click: "#settings-menu" click: "rustdoc-toolbar .settings-menu"
wait-for: "#settings" wait-for: "#settings"
assert-property: ("#go-to-only-result", {"checked": |setting_attribute_value|}) assert-property: ("#go-to-only-result", {"checked": |setting_attribute_value|})
} }
@@ -25,7 +25,7 @@ wait-for: "#search"
assert-document-property: ({"URL": "/lib2/index.html"}, CONTAINS) assert-document-property: ({"URL": "/lib2/index.html"}, CONTAINS)
// Now we change its value. // Now we change its value.
click: "#settings-menu" click: "rustdoc-toolbar .settings-menu"
wait-for: "#settings" wait-for: "#settings"
click: "#go-to-only-result" click: "#go-to-only-result"
assert-local-storage: {"rustdoc-go-to-only-result": "true"} assert-local-storage: {"rustdoc-go-to-only-result": "true"}

View File

@@ -9,7 +9,7 @@ define-function: (
[theme, filter], [theme, filter],
block { block {
call-function: ("switch-theme", {"theme": |theme|}) call-function: ("switch-theme", {"theme": |theme|})
assert-css: ("#settings-menu > a::before", { assert-css: ("rustdoc-toolbar .settings-menu > a::before", {
"filter": |filter|, "filter": |filter|,
"width": "18px", "width": "18px",
"height": "18px", "height": "18px",

View File

@@ -5,7 +5,7 @@ show-text: true // needed when we check for colors below.
// First, we check that the settings page doesn't exist. // First, we check that the settings page doesn't exist.
assert-false: "#settings" assert-false: "#settings"
// We now click on the settings button. // We now click on the settings button.
click: "#settings-menu" click: "rustdoc-toolbar .settings-menu"
wait-for: "#settings" wait-for: "#settings"
assert-css: ("#settings", {"display": "block"}) assert-css: ("#settings", {"display": "block"})
@@ -13,11 +13,11 @@ assert-css: ("#settings", {"display": "block"})
store-css: (".setting-line", {"margin": setting_line_margin}) store-css: (".setting-line", {"margin": setting_line_margin})
// Let's close it by clicking on the same button. // Let's close it by clicking on the same button.
click: "#settings-menu" click: "rustdoc-toolbar .settings-menu"
wait-for-css: ("#settings", {"display": "none"}) wait-for-css: ("#settings", {"display": "none"})
// Let's check that pressing "ESCAPE" is closing it. // Let's check that pressing "ESCAPE" is closing it.
click: "#settings-menu" click: "rustdoc-toolbar .settings-menu"
wait-for-css: ("#settings", {"display": "block"}) wait-for-css: ("#settings", {"display": "block"})
press-key: "Escape" press-key: "Escape"
wait-for-css: ("#settings", {"display": "none"}) wait-for-css: ("#settings", {"display": "none"})
@@ -28,7 +28,7 @@ write: "test"
// To be SURE that the search will be run. // To be SURE that the search will be run.
press-key: 'Enter' press-key: 'Enter'
wait-for: "#alternative-display #search" wait-for: "#alternative-display #search"
click: "#settings-menu" click: "rustdoc-toolbar .settings-menu"
wait-for-css: ("#settings", {"display": "block"}) wait-for-css: ("#settings", {"display": "block"})
// Ensure that the search is still displayed. // Ensure that the search is still displayed.
wait-for: "#alternative-display #search" wait-for: "#alternative-display #search"
@@ -41,7 +41,7 @@ set-local-storage: {"rustdoc-theme": "dark", "rustdoc-use-system-theme": "false"
// We reload the page so the local storage settings are being used. // We reload the page so the local storage settings are being used.
reload: reload:
click: "#settings-menu" click: "rustdoc-toolbar .settings-menu"
wait-for: "#settings" wait-for: "#settings"
// We check that the "Use system theme" is disabled. // We check that the "Use system theme" is disabled.
@@ -55,7 +55,7 @@ assert: "#preferred-light-theme.setting-line.hidden"
assert-property: ("#theme .setting-radio-choices #theme-dark", {"checked": "true"}) assert-property: ("#theme .setting-radio-choices #theme-dark", {"checked": "true"})
// Some style checks... // Some style checks...
move-cursor-to: "#settings-menu > a" move-cursor-to: "rustdoc-toolbar .settings-menu > a"
// First we check the "default" display for radio buttons. // First we check the "default" display for radio buttons.
assert-css: ( assert-css: (
"#theme-dark", "#theme-dark",
@@ -194,7 +194,7 @@ assert-css: (
"border-width": "2px", "border-width": "2px",
}, },
) )
move-cursor-to: "#settings-menu > a" move-cursor-to: "rustdoc-toolbar .settings-menu > a"
// Let's now check with the focus for toggles. // Let's now check with the focus for toggles.
focus: "#auto-hide-large-items" focus: "#auto-hide-large-items"
assert-css: ( assert-css: (
@@ -273,43 +273,43 @@ assert-local-storage: {"rustdoc-disable-shortcuts": "true"}
press-key: "Escape" press-key: "Escape"
press-key: "?" press-key: "?"
assert-false: "#help-button .popover" assert-false: "#help-button .popover"
wait-for-css: ("#settings-menu .popover", {"display": "block"}) wait-for-css: ("rustdoc-toolbar .settings-menu .popover", {"display": "block"})
// Now turn keyboard shortcuts back on, and see if they work. // Now turn keyboard shortcuts back on, and see if they work.
click: "#disable-shortcuts" click: "#disable-shortcuts"
assert-local-storage: {"rustdoc-disable-shortcuts": "false"} assert-local-storage: {"rustdoc-disable-shortcuts": "false"}
press-key: "Escape" press-key: "Escape"
press-key: "?" press-key: "?"
wait-for-css: ("#help-button .popover", {"display": "block"}) wait-for-css: ("rustdoc-toolbar .help-menu .popover", {"display": "block"})
assert-css: ("#settings-menu .popover", {"display": "none"}) assert-css: ("rustdoc-toolbar .settings-menu .popover", {"display": "none"})
// Now switch back to the settings popover, and make sure the keyboard // Now switch back to the settings popover, and make sure the keyboard
// shortcut works when a check box is selected. // shortcut works when a check box is selected.
click: "#settings-menu > a" click: "rustdoc-toolbar .settings-menu > a"
wait-for-css: ("#settings-menu .popover", {"display": "block"}) wait-for-css: ("rustdoc-toolbar .settings-menu .popover", {"display": "block"})
focus: "#auto-hide-large-items" focus: "#auto-hide-large-items"
press-key: "?" press-key: "?"
wait-for-css: ("#settings-menu .popover", {"display": "none"}) wait-for-css: ("rustdoc-toolbar .settings-menu .popover", {"display": "none"})
wait-for-css: ("#help-button .popover", {"display": "block"}) wait-for-css: ("rustdoc-toolbar .help-menu .popover", {"display": "block"})
// Now switch back to the settings popover, and make sure the keyboard // Now switch back to the settings popover, and make sure the keyboard
// shortcut works when a check box is selected. // shortcut works when a check box is selected.
click: "#settings-menu > a" click: "rustdoc-toolbar .settings-menu > a"
wait-for-css: ("#settings-menu .popover", {"display": "block"}) wait-for-css: ("rustdoc-toolbar .settings-menu .popover", {"display": "block"})
wait-for-css: ("#help-button .popover", {"display": "none"}) assert-false: "rustdoc-toolbar .help-menu .popover"
focus: "#theme-system-preference" focus: "#theme-system-preference"
press-key: "?" press-key: "?"
wait-for-css: ("#settings-menu .popover", {"display": "none"}) wait-for-css: ("rustdoc-toolbar .settings-menu .popover", {"display": "none"})
wait-for-css: ("#help-button .popover", {"display": "block"}) wait-for-css: ("rustdoc-toolbar .help-menu .popover", {"display": "block"})
// Now we go to the settings page to check that the CSS is loaded as expected. // Now we go to the settings page to check that the CSS is loaded as expected.
go-to: "file://" + |DOC_PATH| + "/settings.html" go-to: "file://" + |DOC_PATH| + "/settings.html"
wait-for: "#settings" wait-for: "#settings"
assert-false: "#settings-menu" assert-false: "rustdoc-toolbar .settings-menu"
assert-css: (".setting-radio", {"cursor": "pointer"}) assert-css: (".setting-radio", {"cursor": "pointer"})
assert-attribute-false: ("#settings", {"class": "popover"}, CONTAINS) assert-attribute-false: ("#settings", {"class": "popover"}, CONTAINS)
compare-elements-position: (".sub form", "#settings", ["x"]) compare-elements-position: (".main-heading", "#settings", ["x"])
// Check that setting-line has the same margin in this mode as in the popover. // Check that setting-line has the same margin in this mode as in the popover.
assert-css: (".setting-line", {"margin": |setting_line_margin|}) assert-css: (".setting-line", {"margin": |setting_line_margin|})

View File

@@ -8,9 +8,9 @@ press-key: "Escape"
assert-false: "input.search-input:focus" assert-false: "input.search-input:focus"
// We now check for the help popup. // We now check for the help popup.
press-key: "?" press-key: "?"
assert-css: ("#help-button .popover", {"display": "block"}) assert-css: ("rustdoc-toolbar .help-menu .popover", {"display": "block"})
press-key: "Escape" press-key: "Escape"
assert-css: ("#help-button .popover", {"display": "none"}) assert-false: "rustdoc-toolbar .help-menu .popover"
// Checking doc collapse and expand. // Checking doc collapse and expand.
// It should be displaying a "-": // It should be displaying a "-":
assert-text: ("#toggle-all-docs", "Summary") assert-text: ("#toggle-all-docs", "Summary")

View File

@@ -17,7 +17,7 @@ assert-css: (".sidebar", {"display": "block", "left": "-1000px"})
focus: ".sidebar-elems h3 a" focus: ".sidebar-elems h3 a"
assert-css: (".sidebar", {"display": "block", "left": "0px"}) assert-css: (".sidebar", {"display": "block", "left": "0px"})
// When we tab out of the sidebar, close it. // When we tab out of the sidebar, close it.
focus: ".search-input" focus: "#search-button"
assert-css: (".sidebar", {"display": "block", "left": "-1000px"}) assert-css: (".sidebar", {"display": "block", "left": "-1000px"})
// Open the sidebar menu. // Open the sidebar menu.
@@ -43,7 +43,7 @@ press-key: "Escape"
assert-css: (".sidebar", {"display": "block", "left": "-1000px"}) assert-css: (".sidebar", {"display": "block", "left": "-1000px"})
// Check that the topbar is visible // Check that the topbar is visible
assert-property: (".mobile-topbar", {"clientHeight": "45"}) assert-property: ("rustdoc-topbar", {"clientHeight": "45"})
// Check that clicking an element from the sidebar scrolls to the right place // Check that clicking an element from the sidebar scrolls to the right place
// so the target is not obscured by the topbar. // so the target is not obscured by the topbar.
@@ -54,7 +54,7 @@ assert-position: ("#method\.must_use", {"y": 46})
// Check that the bottom-most item on the sidebar menu can be scrolled fully into view. // Check that the bottom-most item on the sidebar menu can be scrolled fully into view.
click: ".sidebar-menu-toggle" click: ".sidebar-menu-toggle"
scroll-to: ".block.keyword li:nth-child(1)" scroll-to: ".block.keyword li:nth-child(1)"
compare-elements-position-near: (".block.keyword li:nth-child(1)", ".mobile-topbar", {"y": 544}) compare-elements-position-near: (".block.keyword li:nth-child(1)", "rustdoc-topbar", {"y": 544})
// Now checking the background color of the sidebar. // Now checking the background color of the sidebar.
// Close the sidebar menu. // Close the sidebar menu.
@@ -65,7 +65,7 @@ define-function: (
"check-colors", "check-colors",
[theme, color, background], [theme, color, background],
block { block {
call-function: ("switch-theme", {"theme": |theme|}) call-function: ("switch-theme-mobile", {"theme": |theme|})
reload: reload:
// Open the sidebar menu. // Open the sidebar menu.

View File

@@ -2,7 +2,7 @@
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
assert-property: (".sidebar", {"clientWidth": "199"}) assert-property: (".sidebar", {"clientWidth": "199"})
show-text: true show-text: true
click: "#settings-menu" click: "rustdoc-toolbar .settings-menu"
wait-for: "#settings" wait-for: "#settings"
assert-css: ("#settings", {"display": "block"}) assert-css: ("#settings", {"display": "block"})
// normal resizing // normal resizing
@@ -12,7 +12,7 @@ assert-css: ("#settings", {"display": "none"})
// Now same thing, but for source code // Now same thing, but for source code
go-to: "file://" + |DOC_PATH| + "/src/test_docs/lib.rs.html" go-to: "file://" + |DOC_PATH| + "/src/test_docs/lib.rs.html"
click: "#settings-menu" click: "rustdoc-toolbar .settings-menu"
wait-for: "#settings" wait-for: "#settings"
assert-css: ("#settings", {"display": "block"}) assert-css: ("#settings", {"display": "block"})
assert-property: (".sidebar", {"clientWidth": "49"}) assert-property: (".sidebar", {"clientWidth": "49"})

View File

@@ -4,7 +4,7 @@ assert-property: (".sidebar", {"clientWidth": "199"})
show-text: true show-text: true
// Verify that the "hide" option is unchecked // Verify that the "hide" option is unchecked
click: "#settings-menu" click: "rustdoc-toolbar .settings-menu"
wait-for: "#settings" wait-for: "#settings"
assert-css: ("#settings", {"display": "block"}) assert-css: ("#settings", {"display": "block"})
assert-property: ("#hide-sidebar", {"checked": "false"}) assert-property: ("#hide-sidebar", {"checked": "false"})
@@ -15,7 +15,7 @@ drag-and-drop: ((205, 100), (5, 100))
assert-css: (".sidebar", {"display": "none"}) assert-css: (".sidebar", {"display": "none"})
// Verify that the "hide" option is checked // Verify that the "hide" option is checked
focus: "#settings-menu a" focus: "rustdoc-toolbar .settings-menu a"
press-key: "Enter" press-key: "Enter"
wait-for-css: ("#settings", {"display": "block"}) wait-for-css: ("#settings", {"display": "block"})
assert-property: ("#hide-sidebar", {"checked": "true"}) assert-property: ("#hide-sidebar", {"checked": "true"})
@@ -24,28 +24,28 @@ wait-for-css: (".sidebar", {"display": "block"})
// Verify that hiding the sidebar hides the source sidebar // Verify that hiding the sidebar hides the source sidebar
// and puts the button in static position mode on mobile // and puts the button in static position mode on mobile
go-to: "file://" + |DOC_PATH| + "/src/test_docs/lib.rs.html" go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
set-window-size: (600, 600) set-window-size: (600, 600)
focus: "#settings-menu a" focus: "rustdoc-topbar .settings-menu a"
press-key: "Enter" press-key: "Enter"
wait-for-css: ("#settings", {"display": "block"}) wait-for-css: ("#settings", {"display": "block"})
wait-for-css: ("#sidebar-button", {"position": "static"})
assert-property: ("#hide-sidebar", {"checked": "false"})
click: "#hide-sidebar"
wait-for-css: (".sidebar", {"display": "none"})
wait-for-css: ("#sidebar-button", {"position": "fixed"}) wait-for-css: ("#sidebar-button", {"position": "fixed"})
store-position: ("#sidebar-button", { store-position: ("#sidebar-button", {
"y": sidebar_button_y, "y": sidebar_button_y,
"x": sidebar_button_x, "x": sidebar_button_x,
}) })
assert-property: ("#hide-sidebar", {"checked": "false"})
click: "#hide-sidebar"
wait-for-css: (".sidebar", {"display": "none"})
wait-for-css: ("#sidebar-button", {"position": "static"})
assert-position: ("#sidebar-button", {
"y": |sidebar_button_y|,
"x": |sidebar_button_x|,
})
assert-property: ("#hide-sidebar", {"checked": "true"}) assert-property: ("#hide-sidebar", {"checked": "true"})
press-key: "Escape" press-key: "Escape"
// Clicking the sidebar button should work, and implicitly re-enable // Clicking the sidebar button should work, and implicitly re-enable
// the persistent navigation bar // the persistent navigation bar
wait-for-css: ("#settings", {"display": "none"}) wait-for-css: ("#settings", {"display": "none"})
assert-position: ("#sidebar-button", {
"y": |sidebar_button_y|,
"x": |sidebar_button_x|,
})
click: "#sidebar-button" click: "#sidebar-button"
wait-for-css: (".sidebar", {"display": "block"}) wait-for-css: (".sidebar", {"display": "block"})

View File

@@ -141,7 +141,7 @@ click: "#sidebar-button"
wait-for-css: (".src .sidebar > *", {"visibility": "hidden"}) wait-for-css: (".src .sidebar > *", {"visibility": "hidden"})
// We scroll to line 117 to change the scroll position. // We scroll to line 117 to change the scroll position.
scroll-to: '//*[@id="117"]' scroll-to: '//*[@id="117"]'
store-value: (y_offset, "2578") store-value: (y_offset, "2567")
assert-window-property: {"pageYOffset": |y_offset|} assert-window-property: {"pageYOffset": |y_offset|}
// Expanding the sidebar... // Expanding the sidebar...
click: "#sidebar-button" click: "#sidebar-button"

View File

@@ -85,4 +85,4 @@ assert-false: ".src-sidebar-expanded"
assert: "nav.sidebar" assert: "nav.sidebar"
// Check that the topbar is not visible // Check that the topbar is not visible
assert-false: ".mobile-topbar" assert-false: "rustdoc-topbar"

View File

@@ -200,7 +200,7 @@ drag-and-drop: ((205, 100), (108, 100))
assert-position: (".sidebar-crate > h2 > a", {"x": -3}) assert-position: (".sidebar-crate > h2 > a", {"x": -3})
// Check that the mobile sidebar and the source sidebar use the same icon. // Check that the mobile sidebar and the source sidebar use the same icon.
store-css: (".mobile-topbar .sidebar-menu-toggle::before", {"content": image_url}) store-css: ("rustdoc-topbar .sidebar-menu-toggle::before", {"content": image_url})
// Then we go to a source page. // Then we go to a source page.
click: ".main-heading .src" click: ".main-heading .src"
assert-css: ("#sidebar-button a::before", {"content": |image_url|}) assert-css: ("#sidebar-button a::before", {"content": |image_url|})
@@ -212,7 +212,7 @@ assert: |sidebar_background| != |sidebar_background_hover|
click: "#sidebar-button a" click: "#sidebar-button a"
wait-for: "html.src-sidebar-expanded" wait-for: "html.src-sidebar-expanded"
assert-css: ("#sidebar-button a:hover", {"background-color": |sidebar_background_hover|}) assert-css: ("#sidebar-button a:hover", {"background-color": |sidebar_background_hover|})
move-cursor-to: "#settings-menu" move-cursor-to: "#search-button"
assert-css: ("#sidebar-button a:not(:hover)", {"background-color": |sidebar_background|}) assert-css: ("#sidebar-button a:not(:hover)", {"background-color": |sidebar_background|})
// Closing sidebar. // Closing sidebar.
click: "#sidebar-button a" click: "#sidebar-button a"
@@ -220,7 +220,7 @@ wait-for: "html:not(.src-sidebar-expanded)"
// Now we check the same when the sidebar button is moved alongside the search. // Now we check the same when the sidebar button is moved alongside the search.
set-window-size: (500, 500) set-window-size: (500, 500)
store-css: ("#sidebar-button a:hover", {"background-color": not_sidebar_background_hover}) store-css: ("#sidebar-button a:hover", {"background-color": not_sidebar_background_hover})
move-cursor-to: "#settings-menu" move-cursor-to: "rustdoc-toolbar #search-button"
store-css: ("#sidebar-button a:not(:hover)", {"background-color": not_sidebar_background}) store-css: ("#sidebar-button a:not(:hover)", {"background-color": not_sidebar_background})
// The sidebar background is supposed to be the same as the main background. // The sidebar background is supposed to be the same as the main background.
assert-css: ("body", {"background-color": |not_sidebar_background|}) assert-css: ("body", {"background-color": |not_sidebar_background|})

View File

@@ -8,13 +8,13 @@ set-window-size: (600, 800)
assert-property: ("html", {"scrollTop": "0"}) assert-property: ("html", {"scrollTop": "0"})
click: '//a[text() = "barbar" and @href="#5-7"]' click: '//a[text() = "barbar" and @href="#5-7"]'
assert-property: ("html", {"scrollTop": "206"}) assert-property: ("html", {"scrollTop": "195"})
click: '//a[text() = "bar" and @href="#28-36"]' click: '//a[text() = "bar" and @href="#28-36"]'
assert-property: ("html", {"scrollTop": "239"}) assert-property: ("html", {"scrollTop": "228"})
click: '//a[normalize-space() = "sub_fn" and @href="#2-4"]' click: '//a[normalize-space() = "sub_fn" and @href="#2-4"]'
assert-property: ("html", {"scrollTop": "134"}) assert-property: ("html", {"scrollTop": "123"})
// We now check that clicking on lines doesn't change the scroll // We now check that clicking on lines doesn't change the scroll
// Extra information: the "sub_fn" function header is on line 1. // Extra information: the "sub_fn" function header is on line 1.
click: '//*[@id="6"]' click: '//*[@id="6"]'
assert-property: ("html", {"scrollTop": "134"}) assert-property: ("html", {"scrollTop": "123"})

View File

@@ -89,9 +89,9 @@ assert-css: ("a[data-nosnippet]", {"text-align": "right"}, ALL)
// do anything (and certainly not add a `#NaN` to the URL!). // do anything (and certainly not add a `#NaN` to the URL!).
go-to: "file://" + |DOC_PATH| + "/src/test_docs/lib.rs.html" go-to: "file://" + |DOC_PATH| + "/src/test_docs/lib.rs.html"
// We use this assert-position to know where we will click. // We use this assert-position to know where we will click.
assert-position: ("//*[@id='1']", {"x": 81, "y": 169}) assert-position: ("//*[@id='1']", {"x": 81, "y": 141})
// We click on the left of the "1" anchor but still in the `a[data-nosnippet]`. // We click on the left of the "1" anchor but still in the "src-line-number" `<pre>`.
click: (77, 163) click: (135, 77)
assert-document-property: ({"URL": "/lib.rs.html"}, ENDS_WITH) assert-document-property: ({"URL": "/lib.rs.html"}, ENDS_WITH)
// Checking the source code sidebar. // Checking the source code sidebar.
@@ -156,27 +156,8 @@ call-function: ("check-sidebar-dir-entry", {
"y": |source_sidebar_title_y| + |source_sidebar_title_height| + 7, "y": |source_sidebar_title_y| + |source_sidebar_title_height| + 7,
}) })
// Check the search form
assert-css: ("nav.sub", {"flex-direction": "row"})
// The goal of this test is to ensure the search input is perfectly centered
// between the top of the page and the header.
// To check this, we maintain the invariant:
//
// offsetTop[nav.sub form] = offsetTop[#main-content] - offsetHeight[nav.sub form] - offsetTop[nav.sub form]
assert-position: ("nav.sub form", {"y": 15})
assert-property: ("nav.sub form", {"offsetHeight": 34})
assert-position: ("h1", {"y": 68})
// 15 = 64 - 34 - 15
// Now do the same check on moderately-sized, tablet mobile.
set-window-size: (700, 700)
assert-css: ("nav.sub", {"flex-direction": "row"})
assert-position: ("nav.sub form", {"y": 8})
assert-property: ("nav.sub form", {"offsetHeight": 34})
assert-position: ("h1", {"y": 54})
// 8 = 50 - 34 - 8
// Check the sidebar directory entries have a marker and spacing (tablet). // Check the sidebar directory entries have a marker and spacing (tablet).
set-window-size: (700, 700)
store-property: (".src-sidebar-title", { store-property: (".src-sidebar-title", {
"offsetHeight": source_sidebar_title_height, "offsetHeight": source_sidebar_title_height,
"offsetTop": source_sidebar_title_y, "offsetTop": source_sidebar_title_y,
@@ -187,11 +168,8 @@ call-function: ("check-sidebar-dir-entry", {
"y": |source_sidebar_title_y| + |source_sidebar_title_height| + 7, "y": |source_sidebar_title_y| + |source_sidebar_title_height| + 7,
}) })
// Tiny, phone mobile gets a different display where the logo is stacked on top.
set-window-size: (450, 700)
assert-css: ("nav.sub", {"flex-direction": "column"})
// Check the sidebar directory entries have a marker and spacing (phone). // Check the sidebar directory entries have a marker and spacing (phone).
set-window-size: (450, 700)
store-property: (".src-sidebar-title", { store-property: (".src-sidebar-title", {
"offsetHeight": source_sidebar_title_height, "offsetHeight": source_sidebar_title_height,
"offsetTop": source_sidebar_title_y, "offsetTop": source_sidebar_title_y,

View File

@@ -13,7 +13,7 @@ define-function: (
) )
store-size: (".rust code", {"width": width, "height": height}) store-size: (".rust code", {"width": width, "height": height})
click: "#settings-menu" click: "main .settings-menu"
wait-for: "#settings" wait-for: "#settings"
call-function: ("click-code-wrapping", {"expected": "true"}) call-function: ("click-code-wrapping", {"expected": "true"})
wait-for-size-false: (".rust code", {"width": |width|, "height": |height|}) wait-for-size-false: (".rust code", {"width": |width|, "height": |height|})
@@ -28,7 +28,7 @@ assert-size: (".rust code", {"width": |width|, "height": |height|})
// Now let's check in docs code examples. // Now let's check in docs code examples.
go-to: "file://" + |DOC_PATH| + "/test_docs/trait_bounds/index.html" go-to: "file://" + |DOC_PATH| + "/test_docs/trait_bounds/index.html"
click: "#settings-menu" click: "main .settings-menu"
wait-for: "#settings" wait-for: "#settings"
store-property: (".example-wrap .rust code", {"scrollWidth": rust_width, "scrollHeight": rust_height}) store-property: (".example-wrap .rust code", {"scrollWidth": rust_width, "scrollHeight": rust_height})

View File

@@ -7,7 +7,7 @@ store-value: (background_light, "white")
store-value: (background_dark, "#353535") store-value: (background_dark, "#353535")
store-value: (background_ayu, "#0f1419") store-value: (background_ayu, "#0f1419")
click: "#settings-menu" click: "rustdoc-toolbar .settings-menu"
wait-for: "#theme-ayu" wait-for: "#theme-ayu"
click: "#theme-ayu" click: "#theme-ayu"
// should be the ayu theme so let's check the color. // should be the ayu theme so let's check the color.
@@ -75,7 +75,7 @@ store-value: (background_dark, "#353535")
store-value: (background_ayu, "#0f1419") store-value: (background_ayu, "#0f1419")
store-value: (background_custom_theme, "red") store-value: (background_custom_theme, "red")
click: "#settings-menu" click: "rustdoc-toolbar .settings-menu"
wait-for: "#theme-ayu" wait-for: "#theme-ayu"
click: "#theme-ayu" click: "#theme-ayu"
// should be the ayu theme so let's check the color. // should be the ayu theme so let's check the color.

View File

@@ -1,6 +1,6 @@
// Ensure that the theme picker always starts with the actual defaults. // Ensure that the theme picker always starts with the actual defaults.
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
click: "#settings-menu" click: "rustdoc-toolbar .settings-menu"
wait-for: "#theme-system-preference" wait-for: "#theme-system-preference"
assert: "#theme-system-preference:checked" assert: "#theme-system-preference:checked"
assert: "#preferred-light-theme-light:checked" assert: "#preferred-light-theme-light:checked"
@@ -16,7 +16,7 @@ set-local-storage: {
"rustdoc-theme": "ayu" "rustdoc-theme": "ayu"
} }
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
click: "#settings-menu" click: "rustdoc-toolbar .settings-menu"
wait-for: "#theme-system-preference" wait-for: "#theme-system-preference"
assert: "#theme-system-preference:checked" assert: "#theme-system-preference:checked"
assert: "#preferred-light-theme-light:checked" assert: "#preferred-light-theme-light:checked"

View File

@@ -13,4 +13,4 @@ assert-attribute-false: (".impl-items .toggle", {"open": ""})
// Click the "Trait" part of "impl Trait" and verify it navigates. // Click the "Trait" part of "impl Trait" and verify it navigates.
click: "#impl-Trait-for-Foo h3 a:first-of-type" click: "#impl-Trait-for-Foo h3 a:first-of-type"
assert-text: (".main-heading .rustdoc-breadcrumbs", "lib2") assert-text: (".main-heading .rustdoc-breadcrumbs", "lib2")
assert-text: (".main-heading h1", "Trait TraitCopy item path") assert-text: (".main-heading h1", "Trait Trait Copy item path")

View File

@@ -3,12 +3,12 @@
go-to: "file://" + |DOC_PATH| + "/test_docs/struct.Foo.html" go-to: "file://" + |DOC_PATH| + "/test_docs/struct.Foo.html"
set-window-size: (433, 600) set-window-size: (433, 600)
assert-attribute: (".top-doc", {"open": ""}) assert-attribute: (".top-doc", {"open": ""})
click: (4, 270) // This is the position of the top doc comment toggle click: (4, 230) // This is the position of the top doc comment toggle
assert-attribute-false: (".top-doc", {"open": ""}) assert-attribute-false: (".top-doc", {"open": ""})
click: (4, 270) click: (4, 230)
assert-attribute: (".top-doc", {"open": ""}) assert-attribute: (".top-doc", {"open": ""})
// To ensure that the toggle isn't over the text, we check that the toggle isn't clicked. // To ensure that the toggle isn't over the text, we check that the toggle isn't clicked.
click: (3, 270) click: (3, 230)
assert-attribute: (".top-doc", {"open": ""}) assert-attribute: (".top-doc", {"open": ""})
// Assert the position of the toggle on the top doc block. // Assert the position of the toggle on the top doc block.
@@ -24,12 +24,12 @@ assert-position: (
// Now we do the same but with a little bigger width // Now we do the same but with a little bigger width
set-window-size: (600, 600) set-window-size: (600, 600)
assert-attribute: (".top-doc", {"open": ""}) assert-attribute: (".top-doc", {"open": ""})
click: (4, 270) // New Y position since all search elements are back on one line. click: (4, 230) // New Y position since all search elements are back on one line.
assert-attribute-false: (".top-doc", {"open": ""}) assert-attribute-false: (".top-doc", {"open": ""})
click: (4, 270) click: (4, 230)
assert-attribute: (".top-doc", {"open": ""}) assert-attribute: (".top-doc", {"open": ""})
// To ensure that the toggle isn't over the text, we check that the toggle isn't clicked. // To ensure that the toggle isn't over the text, we check that the toggle isn't clicked.
click: (3, 270) click: (3, 230)
assert-attribute: (".top-doc", {"open": ""}) assert-attribute: (".top-doc", {"open": ""})
// Same check on trait items. // Same check on trait items.

View File

@@ -64,7 +64,7 @@ define-function: (
"filter": |filter|, "filter": |filter|,
}) })
// moving the cursor somewhere else to not mess with next function calls. // moving the cursor somewhere else to not mess with next function calls.
move-cursor-to: ".search-input" move-cursor-to: "#search-button"
}, },
) )

View File

@@ -47,27 +47,27 @@ assert-property: ("pre.item-decl", {"scrollWidth": "950"})
set-window-size: (600, 600) set-window-size: (600, 600)
go-to: "file://" + |DOC_PATH| + "/lib2/too_long/struct.SuperIncrediblyLongLongLongLongLongLongLongGigaGigaGigaMegaLongLongLongStructName.html" go-to: "file://" + |DOC_PATH| + "/lib2/too_long/struct.SuperIncrediblyLongLongLongLongLongLongLongGigaGigaGigaMegaLongLongLongStructName.html"
// It shouldn't have an overflow in the topbar either. // It shouldn't have an overflow in the topbar either.
store-property: (".mobile-topbar", {"scrollWidth": scrollWidth}) store-property: ("rustdoc-topbar", {"scrollWidth": scrollWidth})
assert-property: (".mobile-topbar", {"clientWidth": |scrollWidth|}) assert-property: ("rustdoc-topbar", {"clientWidth": |scrollWidth|}, NEAR)
assert-css: (".mobile-topbar h2", {"overflow-x": "hidden"}) assert-css: ("rustdoc-topbar h2", {"overflow-x": "hidden"})
// Check that main heading and toolbar go side-by-side, both on desktop and on mobile. // Check that main heading and toolbar go side-by-side, both on desktop and on mobile.
set-window-size: (1100, 800) set-window-size: (1100, 800)
go-to: "file://" + |DOC_PATH| + "/lib2/too_long/struct.SuperIncrediblyLongLongLongLongLongLongLongGigaGigaGigaMegaLongLongLongStructName.html" go-to: "file://" + |DOC_PATH| + "/lib2/too_long/struct.SuperIncrediblyLongLongLongLongLongLongLongGigaGigaGigaMegaLongLongLongStructName.html"
compare-elements-position: (".main-heading h1", ".main-heading rustdoc-toolbar", ["y"]) compare-elements-position: (".main-heading h1", ".main-heading rustdoc-toolbar #search-button", ["y"])
compare-elements-position-near-false: (".main-heading h1", ".main-heading rustdoc-toolbar", {"x": 550}) compare-elements-position-near-false: (".main-heading h1", ".main-heading rustdoc-toolbar #search-button", {"x": 300})
go-to: "file://" + |DOC_PATH| + "/lib2/index.html" go-to: "file://" + |DOC_PATH| + "/lib2/index.html"
compare-elements-position: (".main-heading h1", ".main-heading rustdoc-toolbar", ["y"]) compare-elements-position: (".main-heading h1", ".main-heading rustdoc-toolbar #search-button", ["y"])
compare-elements-position-near-false: (".main-heading h1", ".main-heading rustdoc-toolbar", {"x": 550}) compare-elements-position-near-false: (".main-heading h1", ".main-heading rustdoc-toolbar #search-button", {"x": 300})
// On mobile, they always wrap. // On mobile, they always wrap.
set-window-size: (600, 600) set-window-size: (600, 600)
go-to: "file://" + |DOC_PATH| + "/lib2/too_long/struct.SuperIncrediblyLongLongLongLongLongLongLongGigaGigaGigaMegaLongLongLongStructName.html" go-to: "file://" + |DOC_PATH| + "/lib2/too_long/struct.SuperIncrediblyLongLongLongLongLongLongLongGigaGigaGigaMegaLongLongLongStructName.html"
compare-elements-position: (".main-heading h1", ".main-heading rustdoc-toolbar", ["y"]) compare-elements-position: (".main-heading h1", ".main-heading rustdoc-toolbar #search-button", ["y"])
compare-elements-position-near-false: (".main-heading h1", ".main-heading rustdoc-toolbar", {"x": 200}) compare-elements-position-near-false: (".main-heading h1", ".main-heading rustdoc-toolbar #search-button", {"x": 200})
go-to: "file://" + |DOC_PATH| + "/lib2/index.html" go-to: "file://" + |DOC_PATH| + "/lib2/index.html"
compare-elements-position: (".main-heading h1", ".main-heading rustdoc-toolbar", ["y"]) compare-elements-position: (".main-heading h1", ".main-heading rustdoc-toolbar #search-button", ["y"])
compare-elements-position-near-false: (".main-heading h1", ".main-heading rustdoc-toolbar", {"x": 200}) compare-elements-position-near-false: (".main-heading h1", ".main-heading rustdoc-toolbar #search-button", {"x": 200})
// Now we will check that the scrolling is working. // Now we will check that the scrolling is working.
// First on an item with "hidden methods". // First on an item with "hidden methods".

View File

@@ -5,14 +5,47 @@ define-function: (
block { block {
// Set the theme. // Set the theme.
// Open the settings menu. // Open the settings menu.
click: "#settings-menu" click: "rustdoc-toolbar .settings-menu"
// Wait for the popover to appear... // Wait for the popover to appear...
wait-for: "#settings" wait-for: "#settings"
// Change the setting. // Change the setting.
click: "#theme-"+ |theme| click: "#theme-"+ |theme|
// Close the popover. // Close the popover.
click: "#settings-menu" click: "rustdoc-toolbar .settings-menu"
// Ensure that the local storage was correctly updated. // Ensure that the local storage was correctly updated.
assert-local-storage: {"rustdoc-theme": |theme|} assert-local-storage: {"rustdoc-theme": |theme|}
}, },
) )
define-function: (
"switch-theme-mobile",
[theme],
block {
// Set the theme.
// Open the settings menu.
click: "rustdoc-topbar .settings-menu"
// Wait for the popover to appear...
wait-for: "#settings"
// Change the setting.
click: "#theme-"+ |theme|
// Close the popover.
click: "rustdoc-topbar .settings-menu"
// Ensure that the local storage was correctly updated.
assert-local-storage: {"rustdoc-theme": |theme|}
},
)
define-function: (
"perform-search",
[query],
block {
click: "#search-button"
wait-for: ".search-input"
write-into: (".search-input", |query|)
press-key: 'Enter'
// wait for the search to start
wait-for: "#search-tabs"
// then wait for it to finish
wait-for-false: "#search-tabs .count.loading"
}
)

View File

@@ -6,5 +6,10 @@ const EXPECTED = {
'name': 'reference', 'name': 'reference',
'desc': "References, <code>&amp;T</code> and <code>&amp;mut T</code>.", 'desc': "References, <code>&amp;T</code> and <code>&amp;mut T</code>.",
}, },
{
'path': 'std::ops',
'name': 'BitAnd',
'desc': "The bitwise AND operator <code>&amp;</code>.",
},
], ],
}; };

View File

@@ -1,9 +1,7 @@
const EXPECTED = { const EXPECTED = {
'query': '+', 'query': '+',
'others': [ 'others': [
{ 'path': 'std::ops', 'name': 'AddAssign' },
{ 'path': 'std::ops', 'name': 'Add' }, { 'path': 'std::ops', 'name': 'Add' },
{ 'path': 'core::ops', 'name': 'AddAssign' }, { 'path': 'std::ops', 'name': 'AddAssign' },
{ 'path': 'core::ops', 'name': 'Add' },
], ],
}; };

View File

@@ -9,6 +9,6 @@ const EXPECTED = {
{ 'path': 'std::str', 'name': 'eq' }, { 'path': 'std::str', 'name': 'eq' },
], ],
'returned': [ 'returned': [
{ 'path': 'std::string::String', 'name': 'add' }, { 'path': 'std::string::String', 'name': 'new' },
], ],
}; };

View File

@@ -20,12 +20,12 @@ const PARSED = [
pathLast: "c", pathLast: "c",
normalizedPathLast: "c", normalizedPathLast: "c",
generics: [], generics: [],
typeFilter: -1, typeFilter: null,
}, },
] ]
], ],
], ],
typeFilter: -1, typeFilter: null,
}, },
], ],
foundElems: 1, foundElems: 1,
@@ -51,11 +51,11 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "c", pathLast: "c",
generics: [], generics: [],
typeFilter: -1, typeFilter: null,
}] }]
], ],
], ],
typeFilter: -1, typeFilter: null,
}, },
], ],
foundElems: 1, foundElems: 1,
@@ -81,11 +81,11 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "never", pathLast: "never",
generics: [], generics: [],
typeFilter: 1, typeFilter: "primitive",
}] }]
], ],
], ],
typeFilter: -1, typeFilter: null,
}, },
], ],
foundElems: 1, foundElems: 1,
@@ -111,11 +111,11 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "[]", pathLast: "[]",
generics: [], generics: [],
typeFilter: 1, typeFilter: "primitive",
}] }]
], ],
], ],
typeFilter: -1, typeFilter: null,
}, },
], ],
foundElems: 1, foundElems: 1,
@@ -147,14 +147,14 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "never", pathLast: "never",
generics: [], generics: [],
typeFilter: 1, typeFilter: "primitive",
}, },
], ],
typeFilter: 1, typeFilter: "primitive",
}] }]
], ],
], ],
typeFilter: -1, typeFilter: null,
}, },
], ],
foundElems: 1, foundElems: 1,
@@ -213,7 +213,7 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "c", pathLast: "c",
generics: [], generics: [],
typeFilter: -1, typeFilter: null,
}, },
{ {
name: "X", name: "X",
@@ -221,12 +221,12 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "x", pathLast: "x",
generics: [], generics: [],
typeFilter: -1, typeFilter: null,
}, },
], ],
], ],
], ],
typeFilter: -1, typeFilter: null,
}, },
], ],
foundElems: 1, foundElems: 1,

View File

@@ -406,10 +406,10 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "x", pathLast: "x",
generics: [], generics: [],
typeFilter: -1, typeFilter: null,
}, },
], ],
typeFilter: -1, typeFilter: null,
}, },
{ {
name: "y", name: "y",
@@ -417,7 +417,7 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "y", pathLast: "y",
generics: [], generics: [],
typeFilter: -1, typeFilter: null,
}, },
], ],
foundElems: 2, foundElems: 2,
@@ -440,7 +440,7 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "x", pathLast: "x",
generics: [], generics: [],
typeFilter: -1, typeFilter: null,
}, },
{ {
name: "y", name: "y",
@@ -448,10 +448,10 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "y", pathLast: "y",
generics: [], generics: [],
typeFilter: -1, typeFilter: null,
}, },
], ],
typeFilter: -1, typeFilter: null,
}, },
], ],
foundElems: 1, foundElems: 1,
@@ -468,7 +468,7 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "p", pathLast: "p",
generics: [], generics: [],
typeFilter: -1, typeFilter: null,
}, },
{ {
name: "x", name: "x",
@@ -476,7 +476,7 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "x", pathLast: "x",
generics: [], generics: [],
typeFilter: -1, typeFilter: null,
}, },
{ {
name: "y", name: "y",
@@ -484,7 +484,7 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "y", pathLast: "y",
generics: [], generics: [],
typeFilter: -1, typeFilter: null,
}, },
], ],
foundElems: 3, foundElems: 3,

View File

@@ -7,7 +7,7 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "foo", pathLast: "foo",
generics: [], generics: [],
typeFilter: 7, typeFilter: "fn",
}], }],
foundElems: 1, foundElems: 1,
userQuery: "fn:foo", userQuery: "fn:foo",
@@ -22,7 +22,7 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "foo", pathLast: "foo",
generics: [], generics: [],
typeFilter: 6, typeFilter: "enum",
}], }],
foundElems: 1, foundElems: 1,
userQuery: "enum : foo", userQuery: "enum : foo",
@@ -45,7 +45,7 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "macro", pathLast: "macro",
generics: [], generics: [],
typeFilter: 16, typeFilter: "macro",
}], }],
foundElems: 1, foundElems: 1,
userQuery: "macro!", userQuery: "macro!",
@@ -60,7 +60,7 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "mac", pathLast: "mac",
generics: [], generics: [],
typeFilter: 16, typeFilter: "macro",
}], }],
foundElems: 1, foundElems: 1,
userQuery: "macro:mac!", userQuery: "macro:mac!",
@@ -75,7 +75,7 @@ const PARSED = [
pathWithoutLast: ["a"], pathWithoutLast: ["a"],
pathLast: "mac", pathLast: "mac",
generics: [], generics: [],
typeFilter: 16, typeFilter: "macro",
}], }],
foundElems: 1, foundElems: 1,
userQuery: "a::mac!", userQuery: "a::mac!",
@@ -93,7 +93,7 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "foo", pathLast: "foo",
generics: [], generics: [],
typeFilter: 7, typeFilter: "fn",
}], }],
error: null, error: null,
}, },
@@ -114,10 +114,10 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "bar", pathLast: "bar",
generics: [], generics: [],
typeFilter: 7, typeFilter: "fn",
} }
], ],
typeFilter: 7, typeFilter: "fn",
}], }],
error: null, error: null,
}, },
@@ -138,7 +138,7 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "bar", pathLast: "bar",
generics: [], generics: [],
typeFilter: 7, typeFilter: "fn",
}, },
{ {
name: "baz::fuzz", name: "baz::fuzz",
@@ -146,10 +146,10 @@ const PARSED = [
pathWithoutLast: ["baz"], pathWithoutLast: ["baz"],
pathLast: "fuzz", pathLast: "fuzz",
generics: [], generics: [],
typeFilter: 6, typeFilter: "enum",
}, },
], ],
typeFilter: 7, typeFilter: "fn",
}], }],
error: null, error: null,
}, },

View File

@@ -16,7 +16,7 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "p", pathLast: "p",
generics: [], generics: [],
typeFilter: -1, typeFilter: null,
}, },
{ {
name: "u8", name: "u8",
@@ -24,7 +24,7 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "u8", pathLast: "u8",
generics: [], generics: [],
typeFilter: -1, typeFilter: null,
}, },
], ],
foundElems: 2, foundElems: 2,
@@ -49,7 +49,7 @@ const PARSED = [
generics: [], generics: [],
}, },
], ],
typeFilter: -1, typeFilter: null,
}, },
], ],
foundElems: 1, foundElems: 1,
@@ -82,7 +82,7 @@ const PARSED = [
], ],
}, },
], ],
typeFilter: -1, typeFilter: null,
}, },
], ],
foundElems: 1, foundElems: 1,
@@ -122,7 +122,7 @@ const PARSED = [
generics: [], generics: [],
}, },
], ],
typeFilter: -1, typeFilter: null,
}, },
], ],
foundElems: 1, foundElems: 1,
@@ -162,7 +162,7 @@ const PARSED = [
], ],
}, },
], ],
typeFilter: -1, typeFilter: null,
}, },
], ],
foundElems: 1, foundElems: 1,

View File

@@ -25,11 +25,11 @@ const PARSED = [
generics: [], generics: [],
}, },
], ],
typeFilter: -1, typeFilter: null,
}], }],
], ],
], ],
typeFilter: -1, typeFilter: null,
}], }],
foundElems: 1, foundElems: 1,
userQuery: "(-> F<P>)", userQuery: "(-> F<P>)",
@@ -53,11 +53,11 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "p", pathLast: "p",
generics: [], generics: [],
typeFilter: -1, typeFilter: null,
}], }],
], ],
], ],
typeFilter: -1, typeFilter: null,
}], }],
foundElems: 1, foundElems: 1,
userQuery: "(-> P)", userQuery: "(-> P)",
@@ -81,11 +81,11 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "a", pathLast: "a",
generics: [], generics: [],
typeFilter: -1, typeFilter: null,
}], }],
], ],
], ],
typeFilter: -1, typeFilter: null,
}], }],
foundElems: 1, foundElems: 1,
userQuery: "(->,a)", userQuery: "(->,a)",
@@ -113,7 +113,7 @@ const PARSED = [
generics: [], generics: [],
}, },
], ],
typeFilter: -1, typeFilter: null,
}], }],
bindings: [ bindings: [
[ [
@@ -121,7 +121,7 @@ const PARSED = [
[], [],
], ],
], ],
typeFilter: -1, typeFilter: null,
}], }],
foundElems: 1, foundElems: 1,
userQuery: "(F<P> ->)", userQuery: "(F<P> ->)",
@@ -141,7 +141,7 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "p", pathLast: "p",
generics: [], generics: [],
typeFilter: -1, typeFilter: null,
}], }],
bindings: [ bindings: [
[ [
@@ -149,7 +149,7 @@ const PARSED = [
[], [],
], ],
], ],
typeFilter: -1, typeFilter: null,
}], }],
foundElems: 1, foundElems: 1,
userQuery: "(P ->)", userQuery: "(P ->)",
@@ -169,7 +169,7 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "a", pathLast: "a",
generics: [], generics: [],
typeFilter: -1, typeFilter: null,
}], }],
bindings: [ bindings: [
[ [
@@ -177,7 +177,7 @@ const PARSED = [
[], [],
], ],
], ],
typeFilter: -1, typeFilter: null,
}], }],
foundElems: 1, foundElems: 1,
userQuery: "(,a->)", userQuery: "(,a->)",
@@ -197,7 +197,7 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "aaaaa", pathLast: "aaaaa",
generics: [], generics: [],
typeFilter: -1, typeFilter: null,
}], }],
bindings: [ bindings: [
[ [
@@ -208,11 +208,11 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "a", pathLast: "a",
generics: [], generics: [],
typeFilter: -1, typeFilter: null,
}], }],
], ],
], ],
typeFilter: -1, typeFilter: null,
}], }],
foundElems: 1, foundElems: 1,
userQuery: "(aaaaa->a)", userQuery: "(aaaaa->a)",
@@ -233,7 +233,7 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "aaaaa", pathLast: "aaaaa",
generics: [], generics: [],
typeFilter: -1, typeFilter: null,
}, },
{ {
name: "b", name: "b",
@@ -241,7 +241,7 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "b", pathLast: "b",
generics: [], generics: [],
typeFilter: -1, typeFilter: null,
}, },
], ],
bindings: [ bindings: [
@@ -253,11 +253,11 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "a", pathLast: "a",
generics: [], generics: [],
typeFilter: -1, typeFilter: null,
}], }],
], ],
], ],
typeFilter: -1, typeFilter: null,
}], }],
foundElems: 1, foundElems: 1,
userQuery: "(aaaaa, b -> a)", userQuery: "(aaaaa, b -> a)",
@@ -278,7 +278,7 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "aaaaa", pathLast: "aaaaa",
generics: [], generics: [],
typeFilter: -1, typeFilter: null,
}, },
{ {
name: "b", name: "b",
@@ -286,7 +286,7 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "b", pathLast: "b",
generics: [], generics: [],
typeFilter: -1, typeFilter: null,
}, },
], ],
bindings: [ bindings: [
@@ -298,11 +298,11 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "a", pathLast: "a",
generics: [], generics: [],
typeFilter: -1, typeFilter: null,
}], }],
], ],
], ],
typeFilter: 1, typeFilter: "primitive",
}], }],
foundElems: 1, foundElems: 1,
userQuery: "primitive:(aaaaa, b -> a)", userQuery: "primitive:(aaaaa, b -> a)",
@@ -318,7 +318,7 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "x", pathLast: "x",
generics: [], generics: [],
typeFilter: -1, typeFilter: null,
}, },
{ {
name: "->", name: "->",
@@ -332,7 +332,7 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "aaaaa", pathLast: "aaaaa",
generics: [], generics: [],
typeFilter: -1, typeFilter: null,
}, },
{ {
name: "b", name: "b",
@@ -340,7 +340,7 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "b", pathLast: "b",
generics: [], generics: [],
typeFilter: -1, typeFilter: null,
}, },
], ],
bindings: [ bindings: [
@@ -352,11 +352,11 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "a", pathLast: "a",
generics: [], generics: [],
typeFilter: -1, typeFilter: null,
}], }],
], ],
], ],
typeFilter: 10, typeFilter: "trait",
} }
], ],
foundElems: 2, foundElems: 2,
@@ -390,11 +390,11 @@ const PARSED = [
generics: [], generics: [],
}, },
], ],
typeFilter: -1, typeFilter: null,
}], }],
], ],
], ],
typeFilter: -1, typeFilter: null,
}], }],
foundElems: 1, foundElems: 1,
userQuery: "Fn () -> F<P>", userQuery: "Fn () -> F<P>",
@@ -418,11 +418,11 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "p", pathLast: "p",
generics: [], generics: [],
typeFilter: -1, typeFilter: null,
}], }],
], ],
], ],
typeFilter: -1, typeFilter: null,
}], }],
foundElems: 1, foundElems: 1,
userQuery: "FnMut() -> P", userQuery: "FnMut() -> P",
@@ -446,11 +446,11 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "p", pathLast: "p",
generics: [], generics: [],
typeFilter: -1, typeFilter: null,
}], }],
], ],
], ],
typeFilter: -1, typeFilter: null,
}], }],
foundElems: 1, foundElems: 1,
userQuery: "(FnMut() -> P)", userQuery: "(FnMut() -> P)",
@@ -478,7 +478,7 @@ const PARSED = [
generics: [], generics: [],
}, },
], ],
typeFilter: -1, typeFilter: null,
}], }],
bindings: [ bindings: [
[ [
@@ -486,7 +486,7 @@ const PARSED = [
[], [],
], ],
], ],
typeFilter: -1, typeFilter: null,
}], }],
foundElems: 1, foundElems: 1,
userQuery: "Fn(F<P>)", userQuery: "Fn(F<P>)",
@@ -507,7 +507,7 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "aaaaa", pathLast: "aaaaa",
generics: [], generics: [],
typeFilter: -1, typeFilter: null,
}, },
{ {
name: "b", name: "b",
@@ -515,7 +515,7 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "b", pathLast: "b",
generics: [], generics: [],
typeFilter: -1, typeFilter: null,
}, },
], ],
bindings: [ bindings: [
@@ -527,11 +527,11 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "a", pathLast: "a",
generics: [], generics: [],
typeFilter: -1, typeFilter: null,
}], }],
], ],
], ],
typeFilter: 1, typeFilter: "primitive",
}], }],
foundElems: 1, foundElems: 1,
userQuery: "primitive:fnonce(aaaaa, b) -> a", userQuery: "primitive:fnonce(aaaaa, b) -> a",
@@ -552,7 +552,7 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "aaaaa", pathLast: "aaaaa",
generics: [], generics: [],
typeFilter: -1, typeFilter: null,
}, },
{ {
name: "b", name: "b",
@@ -560,7 +560,7 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "b", pathLast: "b",
generics: [], generics: [],
typeFilter: 0, typeFilter: "keyword",
}, },
], ],
bindings: [ bindings: [
@@ -572,11 +572,11 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "a", pathLast: "a",
generics: [], generics: [],
typeFilter: 10, typeFilter: "trait",
}], }],
], ],
], ],
typeFilter: 1, typeFilter: "primitive",
}], }],
foundElems: 1, foundElems: 1,
userQuery: "primitive:fnonce(aaaaa, keyword:b) -> trait:a", userQuery: "primitive:fnonce(aaaaa, keyword:b) -> trait:a",
@@ -592,7 +592,7 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "x", pathLast: "x",
generics: [], generics: [],
typeFilter: -1, typeFilter: null,
}, },
{ {
name: "fn", name: "fn",
@@ -612,7 +612,7 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "aaaaa", pathLast: "aaaaa",
generics: [], generics: [],
typeFilter: -1, typeFilter: null,
}, },
{ {
name: "b", name: "b",
@@ -620,7 +620,7 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "b", pathLast: "b",
generics: [], generics: [],
typeFilter: -1, typeFilter: null,
}, },
], ],
bindings: [ bindings: [
@@ -632,11 +632,11 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "a", pathLast: "a",
generics: [], generics: [],
typeFilter: -1, typeFilter: null,
}], }],
], ],
], ],
typeFilter: -1, typeFilter: null,
}, },
], ],
bindings: [ bindings: [
@@ -645,7 +645,7 @@ const PARSED = [
[], [],
] ]
], ],
typeFilter: 10, typeFilter: "trait",
} }
], ],
foundElems: 2, foundElems: 2,
@@ -662,7 +662,7 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "a", pathLast: "a",
generics: [], generics: [],
typeFilter: -1, typeFilter: null,
}, },
{ {
name: "b", name: "b",
@@ -675,7 +675,7 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "c", pathLast: "c",
generics: [], generics: [],
typeFilter: -1, typeFilter: null,
}], }],
bindings: [ bindings: [
[ [
@@ -683,7 +683,7 @@ const PARSED = [
[], [],
] ]
], ],
typeFilter: -1, typeFilter: null,
} }
], ],
foundElems: 2, foundElems: 2,

View File

@@ -13,10 +13,10 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "never", pathLast: "never",
generics: [], generics: [],
typeFilter: 1, typeFilter: "primitive",
}, },
], ],
typeFilter: -1, typeFilter: null,
}], }],
foundElems: 1, foundElems: 1,
userQuery: "R<!>", userQuery: "R<!>",
@@ -31,7 +31,7 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "never", pathLast: "never",
generics: [], generics: [],
typeFilter: 1, typeFilter: "primitive",
}], }],
foundElems: 1, foundElems: 1,
userQuery: "!", userQuery: "!",
@@ -46,7 +46,7 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "a", pathLast: "a",
generics: [], generics: [],
typeFilter: 16, typeFilter: "macro",
}], }],
foundElems: 1, foundElems: 1,
userQuery: "a!", userQuery: "a!",
@@ -77,7 +77,7 @@ const PARSED = [
pathWithoutLast: ["never"], pathWithoutLast: ["never"],
pathLast: "b", pathLast: "b",
generics: [], generics: [],
typeFilter: -1, typeFilter: null,
}], }],
foundElems: 1, foundElems: 1,
userQuery: "!::b", userQuery: "!::b",
@@ -122,10 +122,10 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "t", pathLast: "t",
generics: [], generics: [],
typeFilter: -1, typeFilter: null,
} }
], ],
typeFilter: -1, typeFilter: null,
}], }],
foundElems: 1, foundElems: 1,
userQuery: "!::b<T>", userQuery: "!::b<T>",

View File

@@ -15,7 +15,7 @@ const PARSED = [
generics: [], generics: [],
}, },
], ],
typeFilter: -1, typeFilter: null,
}], }],
foundElems: 1, foundElems: 1,
userQuery: "R<P>", userQuery: "R<P>",

View File

@@ -7,7 +7,7 @@ const PARSED = [
pathWithoutLast: ["a"], pathWithoutLast: ["a"],
pathLast: "b", pathLast: "b",
generics: [], generics: [],
typeFilter: -1, typeFilter: null,
}], }],
foundElems: 1, foundElems: 1,
userQuery: "A::B", userQuery: "A::B",
@@ -22,7 +22,7 @@ const PARSED = [
pathWithoutLast: ["a"], pathWithoutLast: ["a"],
pathLast: "a", pathLast: "a",
generics: [], generics: [],
typeFilter: -1, typeFilter: null,
}], }],
foundElems: 1, foundElems: 1,
userQuery: 'a:: a', userQuery: 'a:: a',
@@ -37,7 +37,7 @@ const PARSED = [
pathWithoutLast: ["a"], pathWithoutLast: ["a"],
pathLast: "a", pathLast: "a",
generics: [], generics: [],
typeFilter: -1, typeFilter: null,
}], }],
foundElems: 1, foundElems: 1,
userQuery: 'a ::a', userQuery: 'a ::a',
@@ -52,7 +52,7 @@ const PARSED = [
pathWithoutLast: ["a"], pathWithoutLast: ["a"],
pathLast: "a", pathLast: "a",
generics: [], generics: [],
typeFilter: -1, typeFilter: null,
}], }],
foundElems: 1, foundElems: 1,
userQuery: 'a :: a', userQuery: 'a :: a',
@@ -68,7 +68,7 @@ const PARSED = [
pathWithoutLast: ["a"], pathWithoutLast: ["a"],
pathLast: "b", pathLast: "b",
generics: [], generics: [],
typeFilter: -1, typeFilter: null,
}, },
{ {
name: "C", name: "C",
@@ -76,7 +76,7 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "c", pathLast: "c",
generics: [], generics: [],
typeFilter: -1, typeFilter: null,
}, },
], ],
foundElems: 2, foundElems: 2,
@@ -101,7 +101,7 @@ const PARSED = [
generics: [], generics: [],
}, },
], ],
typeFilter: -1, typeFilter: null,
}, },
{ {
name: "C", name: "C",
@@ -109,7 +109,7 @@ const PARSED = [
pathWithoutLast: [], pathWithoutLast: [],
pathLast: "c", pathLast: "c",
generics: [], generics: [],
typeFilter: -1, typeFilter: null,
}, },
], ],
foundElems: 2, foundElems: 2,
@@ -125,7 +125,7 @@ const PARSED = [
pathWithoutLast: ["mod"], pathWithoutLast: ["mod"],
pathLast: "a", pathLast: "a",
generics: [], generics: [],
typeFilter: -1, typeFilter: null,
}], }],
foundElems: 1, foundElems: 1,
userQuery: "mod::a", userQuery: "mod::a",

Some files were not shown because too many files have changed in this diff Show More