rustdoc-search: search backend with partitioned suffix tree
This commit is contained in:
parent
c018ae5389
commit
8511e40e72
146 changed files with 9090 additions and 5057 deletions
10
Cargo.lock
10
Cargo.lock
|
|
@ -4812,6 +4812,7 @@ dependencies = [
|
|||
"serde_json",
|
||||
"sha2",
|
||||
"smallvec",
|
||||
"stringdex",
|
||||
"tempfile",
|
||||
"threadpool",
|
||||
"tracing",
|
||||
|
|
@ -5225,6 +5226,15 @@ dependencies = [
|
|||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stringdex"
|
||||
version = "0.0.1-alpha4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2841fd43df5b1ff1b042e167068a1fe9b163dc93041eae56ab2296859013a9a0"
|
||||
dependencies = [
|
||||
"stacker",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
8.6.0
|
||||
8.57.1
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import os.path
|
|||
import re
|
||||
import shlex
|
||||
from collections import namedtuple
|
||||
from pathlib import Path
|
||||
|
||||
try:
|
||||
from html.parser import HTMLParser
|
||||
|
|
@ -242,6 +243,11 @@ class CachedFiles(object):
|
|||
return self.last_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)
|
||||
|
||||
def get_file(self, path):
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ rustdoc-json-types = { path = "../rustdoc-json-types" }
|
|||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
smallvec = "1.8.1"
|
||||
stringdex = { version = "0.0.1-alpha4" }
|
||||
tempfile = "3"
|
||||
threadpool = "1.8.1"
|
||||
tracing = "0.1"
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ fn main() {
|
|||
"static/css/normalize.css",
|
||||
"static/js/main.js",
|
||||
"static/js/search.js",
|
||||
"static/js/stringdex.js",
|
||||
"static/js/settings.js",
|
||||
"static/js/src-script.js",
|
||||
"static/js/storage.js",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
use std::mem;
|
||||
|
||||
use rustc_ast::join_path_syms;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet};
|
||||
use rustc_hir::StabilityLevel;
|
||||
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),
|
||||
_ => item_def_id,
|
||||
};
|
||||
let path = join_path_syms(parent_path);
|
||||
let impl_id = if let Some(ParentStackItem::Impl { item_id, .. }) = cache.parent_stack.last() {
|
||||
item_id.as_def_id()
|
||||
} else {
|
||||
|
|
@ -593,11 +591,11 @@ fn add_item_to_search_index(tcx: TyCtxt<'_>, cache: &mut Cache, item: &clean::It
|
|||
ty: item.type_(),
|
||||
defid: Some(defid),
|
||||
name,
|
||||
path,
|
||||
module_path: parent_path.to_vec(),
|
||||
desc,
|
||||
parent: parent_did,
|
||||
parent_idx: None,
|
||||
exact_path: None,
|
||||
exact_module_path: None,
|
||||
impl_id,
|
||||
search_type,
|
||||
aliases,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use std::fmt;
|
|||
|
||||
use rustc_hir::def::{CtorOf, DefKind, MacroKinds};
|
||||
use rustc_span::hygiene::MacroKind;
|
||||
use serde::{Serialize, Serializer};
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
|
||||
|
||||
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 {
|
||||
fn from(item: &'a clean::Item) -> ItemType {
|
||||
let kind = match &item.kind {
|
||||
|
|
@ -198,6 +244,10 @@ impl ItemType {
|
|||
pub(crate) fn is_adt(&self) -> bool {
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ pub(crate) struct Layout {
|
|||
|
||||
pub(crate) struct Page<'a> {
|
||||
pub(crate) title: &'a str,
|
||||
pub(crate) short_title: &'a str,
|
||||
pub(crate) css_class: &'a str,
|
||||
pub(crate) root_path: &'a str,
|
||||
pub(crate) static_root_path: Option<&'a str>,
|
||||
|
|
|
|||
|
|
@ -204,6 +204,18 @@ impl<'tcx> Context<'tcx> {
|
|||
if !is_module {
|
||||
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 !is_module {
|
||||
title.push_str(" in ");
|
||||
|
|
@ -240,6 +252,7 @@ impl<'tcx> Context<'tcx> {
|
|||
root_path: &self.root_path(),
|
||||
static_root_path: self.shared.static_root_path.as_deref(),
|
||||
title: &title,
|
||||
short_title,
|
||||
description: &desc,
|
||||
resource_suffix: &self.shared.resource_suffix,
|
||||
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 mut page = layout::Page {
|
||||
title: "List of all items in this crate",
|
||||
short_title: "All",
|
||||
css_class: "mod sys",
|
||||
root_path: "../",
|
||||
static_root_path: shared.static_root_path.as_deref(),
|
||||
|
|
|
|||
|
|
@ -130,11 +130,11 @@ pub(crate) struct IndexItem {
|
|||
pub(crate) ty: ItemType,
|
||||
pub(crate) defid: Option<DefId>,
|
||||
pub(crate) name: Symbol,
|
||||
pub(crate) path: String,
|
||||
pub(crate) module_path: Vec<Symbol>,
|
||||
pub(crate) desc: String,
|
||||
pub(crate) parent: Option<DefId>,
|
||||
pub(crate) parent_idx: Option<isize>,
|
||||
pub(crate) exact_path: Option<String>,
|
||||
pub(crate) parent_idx: Option<usize>,
|
||||
pub(crate) exact_module_path: Option<Vec<Symbol>>,
|
||||
pub(crate) impl_id: Option<DefId>,
|
||||
pub(crate) search_type: Option<IndexItemFunctionType>,
|
||||
pub(crate) aliases: Box<[Symbol]>,
|
||||
|
|
@ -150,6 +150,19 @@ struct 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.
|
||||
// The contents of the lists are always integers in self-terminating hex
|
||||
// form, handled by `RenderTypeId::write_to_string`, so no commas are
|
||||
|
|
@ -191,6 +204,62 @@ impl RenderType {
|
|||
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)]
|
||||
|
|
@ -212,7 +281,20 @@ impl RenderTypeId {
|
|||
RenderTypeId::Index(idx) => (*idx).try_into().unwrap(),
|
||||
_ => 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 {
|
||||
fn write_to_string<'a>(
|
||||
&'a self,
|
||||
string: &mut String,
|
||||
backref_queue: &mut VecDeque<&'a IndexItemFunctionType>,
|
||||
) {
|
||||
assert!(backref_queue.len() <= 16);
|
||||
fn size(&self) -> usize {
|
||||
self.inputs.iter().map(RenderType::size).sum::<usize>()
|
||||
+ self.output.iter().map(RenderType::size).sum::<usize>()
|
||||
+ self
|
||||
.where_clause
|
||||
.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,
|
||||
// which is encoded as `` ` `` (see RenderTypeId::write_to_string).
|
||||
let has_missing = self
|
||||
|
|
@ -241,18 +375,7 @@ impl IndexItemFunctionType {
|
|||
.any(|i| i.id.is_none() && i.generics.is_none());
|
||||
if has_missing {
|
||||
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 {
|
||||
backref_queue.push_front(self);
|
||||
if backref_queue.len() > 16 {
|
||||
backref_queue.pop_back();
|
||||
}
|
||||
string.push('{');
|
||||
match &self.inputs[..] {
|
||||
[one] if one.generics.is_none() && one.bindings.is_none() => {
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ use crate::html::format::{
|
|||
visibility_print_with_space,
|
||||
};
|
||||
use crate::html::markdown::{HeadingOffset, MarkdownSummaryLine};
|
||||
use crate::html::render::sidebar::filters;
|
||||
use crate::html::render::{document_full, document_item_info};
|
||||
use crate::html::url_parts_builder::UrlPartsBuilder;
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,6 +1,4 @@
|
|||
use base64::prelude::*;
|
||||
|
||||
pub(crate) fn write_vlqhex_to_string(n: i32, string: &mut String) {
|
||||
pub(crate) fn write_signed_vlqhex_to_string(n: i32, string: &mut String) {
|
||||
let (sign, magnitude): (bool, u32) =
|
||||
if n >= 0 { (false, n.try_into().unwrap()) } else { (true, (-n).try_into().unwrap()) };
|
||||
// zig-zag encoding
|
||||
|
|
@ -37,206 +35,66 @@ pub(crate) fn write_vlqhex_to_string(n: i32, string: &mut String) {
|
|||
}
|
||||
}
|
||||
|
||||
// Used during bitmap encoding
|
||||
enum Container {
|
||||
/// number of ones, bits
|
||||
Bits(Box<[u64; 1024]>),
|
||||
/// list of entries
|
||||
Array(Vec<u16>),
|
||||
/// list of (start, len-1)
|
||||
Run(Vec<(u16, u16)>),
|
||||
pub fn read_signed_vlqhex_from_string(string: &[u8]) -> Option<(i32, usize)> {
|
||||
let mut n = 0i32;
|
||||
let mut i = 0;
|
||||
while let Some(&c) = string.get(i) {
|
||||
i += 1;
|
||||
n = (n << 4) | i32::from(c & 0xF);
|
||||
if c >= 96 {
|
||||
// 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 {
|
||||
match self {
|
||||
Container::Bits(bits) => bits.iter().copied().map(|x| x.count_ones()).sum(),
|
||||
Container::Array(array) => {
|
||||
array.len().try_into().expect("array can't be bigger than 2**32")
|
||||
}
|
||||
Container::Run(runs) => {
|
||||
runs.iter().copied().map(|(_, lenm1)| u32::from(lenm1) + 1).sum()
|
||||
}
|
||||
|
||||
pub fn write_postings_to_string(postings: &[Vec<u32>], buf: &mut Vec<u8>) {
|
||||
for list in postings {
|
||||
if list.is_empty() {
|
||||
buf.push(0);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
fn push(&mut self, value: u16) {
|
||||
match self {
|
||||
Container::Bits(bits) => bits[value as usize >> 6] |= 1 << (value & 0x3F),
|
||||
Container::Array(array) => {
|
||||
array.push(value);
|
||||
if array.len() >= 4096 {
|
||||
let array = std::mem::take(array);
|
||||
*self = Container::Bits(Box::new([0; 1024]));
|
||||
for value in array {
|
||||
self.push(value);
|
||||
}
|
||||
}
|
||||
let len_before = buf.len();
|
||||
stringdex::internals::encode::write_bitmap_to_bytes(&list, &mut *buf).unwrap();
|
||||
let len_after = buf.len();
|
||||
if len_after - len_before > 1 + (4 * list.len()) && list.len() < 0x3a {
|
||||
buf.truncate(len_before);
|
||||
buf.push(list.len() as u8);
|
||||
for &item in list {
|
||||
buf.push(item as u8);
|
||||
buf.push((item >> 8) as u8);
|
||||
buf.push((item >> 16) as u8);
|
||||
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
|
||||
// https://gitlab.com/notriddle/roaring-test
|
||||
pub(crate) fn write_bitmap_to_bytes(
|
||||
domain: &[u32],
|
||||
mut out: impl std::io::Write,
|
||||
) -> std::io::Result<()> {
|
||||
// https://arxiv.org/pdf/1603.06549.pdf
|
||||
let mut keys = Vec::<u16>::new();
|
||||
let mut containers = Vec::<Container>::new();
|
||||
let mut key: u16;
|
||||
let mut domain_iter = domain.iter().copied().peekable();
|
||||
let mut has_run = false;
|
||||
while let Some(entry) = domain_iter.next() {
|
||||
key = (entry >> 16).try_into().expect("shifted off the top 16 bits, so it should fit");
|
||||
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;
|
||||
pub fn read_postings_from_string(postings: &mut Vec<Vec<u32>>, mut buf: &[u8]) {
|
||||
use stringdex::internals::decode::RoaringBitmap;
|
||||
while let Some(&c) = buf.get(0) {
|
||||
if c < 0x3a {
|
||||
buf = &buf[1..];
|
||||
let mut slot = Vec::new();
|
||||
for _ in 0..c {
|
||||
slot.push(
|
||||
(buf[0] as u32)
|
||||
| ((buf[1] as u32) << 8)
|
||||
| ((buf[2] as u32) << 16)
|
||||
| ((buf[3] as u32) << 24),
|
||||
);
|
||||
buf = &buf[4..];
|
||||
}
|
||||
domain_iter.next().expect("peeking just succeeded");
|
||||
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)
|
||||
postings.push(slot);
|
||||
} else {
|
||||
4 + 8 * size + size.div_ceil(8)
|
||||
}
|
||||
} else {
|
||||
out.write_all(&u32::to_le_bytes(SERIAL_COOKIE_NO_RUNCONTAINER))?;
|
||||
out.write_all(&u32::to_le_bytes(containers.len().try_into().unwrap()))?;
|
||||
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,
|
||||
};
|
||||
let (bitmap, consumed_bytes_len) =
|
||||
RoaringBitmap::from_bytes(buf).unwrap_or_else(|| (RoaringBitmap::default(), 0));
|
||||
assert_ne!(consumed_bytes_len, 0);
|
||||
postings.push(bitmap.to_vec());
|
||||
buf = &buf[consumed_bytes_len..];
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,17 +65,17 @@ pub(crate) fn write_shared(
|
|||
// 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 SerializedSearchIndex { index, desc } = build_index(krate, &mut cx.shared.cache, tcx);
|
||||
write_search_desc(cx, krate, &desc)?; // does not need to be merged
|
||||
let search_index =
|
||||
build_index(krate, &mut cx.shared.cache, tcx, &cx.dst, &cx.shared.resource_suffix)?;
|
||||
|
||||
let crate_name = krate.name(cx.tcx());
|
||||
let crate_name = crate_name.as_str(); // 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 info = CrateInfo {
|
||||
version: CrateInfoVersion::V1,
|
||||
version: CrateInfoVersion::V2,
|
||||
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)?,
|
||||
crates_index: CratesIndexPart::get(crate_name, &external_crates)?,
|
||||
trait_impl: TraitAliasPart::get(cx, &crate_name_json)?,
|
||||
|
|
@ -141,7 +141,7 @@ pub(crate) fn write_not_crate_specific(
|
|||
resource_suffix: &str,
|
||||
include_sources: bool,
|
||||
) -> 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)?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -151,13 +151,18 @@ fn write_rendered_cross_crate_info(
|
|||
dst: &Path,
|
||||
opt: &RenderOptions,
|
||||
include_sources: bool,
|
||||
resource_suffix: &str,
|
||||
) -> Result<(), Error> {
|
||||
let m = &opt.should_merge;
|
||||
if opt.should_emit_crate() {
|
||||
if include_sources {
|
||||
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::<TraitAliasPart, _>(TraitAliasPart::blank, dst, crates, m)?;
|
||||
|
|
@ -215,38 +220,12 @@ fn write_static_files(
|
|||
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
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub(crate) struct CrateInfo {
|
||||
version: CrateInfoVersion,
|
||||
src_files_js: PartsAndLocations<SourcesPart>,
|
||||
search_index_js: PartsAndLocations<SearchIndexPart>,
|
||||
search_index: SerializedSearchIndex,
|
||||
all_crates: PartsAndLocations<AllCratesPart>,
|
||||
crates_index: PartsAndLocations<CratesIndexPart>,
|
||||
trait_impl: PartsAndLocations<TraitAliasPart>,
|
||||
|
|
@ -277,7 +256,7 @@ impl CrateInfo {
|
|||
/// to provide better diagnostics about including an invalid file.
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
enum CrateInfoVersion {
|
||||
V1,
|
||||
V2,
|
||||
}
|
||||
|
||||
/// 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>;
|
||||
}
|
||||
|
||||
#[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)]
|
||||
struct AllCrates;
|
||||
type AllCratesPart = Part<AllCrates, OrderedJson>;
|
||||
|
|
@ -426,6 +375,7 @@ impl CratesIndexPart {
|
|||
fn blank(cx: &Context<'_>) -> SortedTemplate<<Self as CciPart>::FileFormat> {
|
||||
let page = layout::Page {
|
||||
title: "Index of crates",
|
||||
short_title: "Crates",
|
||||
css_class: "mod sys",
|
||||
root_path: "./",
|
||||
static_root_path: cx.shared.static_root_path.as_deref(),
|
||||
|
|
|
|||
|
|
@ -29,14 +29,6 @@ fn sources_template() {
|
|||
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]
|
||||
fn all_crates_template() {
|
||||
let mut template = AllCratesPart::blank();
|
||||
|
|
@ -54,31 +46,6 @@ fn all_crates_parts() {
|
|||
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]
|
||||
fn crates_index_part() {
|
||||
let external_crates = ["bar".to_string(), "baz".to_string()];
|
||||
|
|
|
|||
|
|
@ -230,6 +230,7 @@ impl SourceCollector<'_, '_> {
|
|||
);
|
||||
let page = layout::Page {
|
||||
title: &title,
|
||||
short_title: &src_fname.to_string_lossy(),
|
||||
css_class: "src",
|
||||
root_path: &root_path,
|
||||
static_root_path: shared.static_root_path.as_deref(),
|
||||
|
|
|
|||
|
|
@ -258,6 +258,17 @@ h1, h2, h3, h4 {
|
|||
padding-bottom: 6px;
|
||||
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 {
|
||||
grid-area: main-heading-breadcrumbs;
|
||||
line-height: 1.25;
|
||||
|
|
@ -265,6 +276,16 @@ h1, h2, h3, h4 {
|
|||
position: relative;
|
||||
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 {
|
||||
padding: 5px 0 7px;
|
||||
}
|
||||
|
|
@ -305,7 +326,7 @@ h4.code-header {
|
|||
#crate-search,
|
||||
h1, h2, h3, h4, h5, h6,
|
||||
.sidebar,
|
||||
.mobile-topbar,
|
||||
rustdoc-topbar,
|
||||
.search-input,
|
||||
.search-results .result-name,
|
||||
.item-table dt > a,
|
||||
|
|
@ -317,6 +338,7 @@ rustdoc-toolbar,
|
|||
summary.hideme,
|
||||
.scraped-example-list,
|
||||
.rustdoc-breadcrumbs,
|
||||
.search-switcher,
|
||||
/* This selector is for the items listed in the "all items" page. */
|
||||
ul.all-items {
|
||||
font-family: "Fira Sans", Arial, NanumBarunGothic, sans-serif;
|
||||
|
|
@ -329,7 +351,7 @@ a.anchor,
|
|||
.rust a,
|
||||
.sidebar h2 a,
|
||||
.sidebar h3 a,
|
||||
.mobile-topbar h2 a,
|
||||
rustdoc-topbar h2 a,
|
||||
h1 a,
|
||||
.search-results a,
|
||||
.search-results li,
|
||||
|
|
@ -616,7 +638,7 @@ img {
|
|||
color: var(--sidebar-resizer-active);
|
||||
}
|
||||
|
||||
.sidebar, .mobile-topbar, .sidebar-menu-toggle,
|
||||
.sidebar, rustdoc-topbar, .sidebar-menu-toggle,
|
||||
#src-sidebar {
|
||||
background-color: var(--sidebar-background-color);
|
||||
}
|
||||
|
|
@ -857,7 +879,7 @@ ul.block, .block li, .block ul {
|
|||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.mobile-topbar {
|
||||
rustdoc-topbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
|
@ -1098,16 +1120,15 @@ div.where {
|
|||
nav.sub {
|
||||
flex-grow: 1;
|
||||
flex-flow: row nowrap;
|
||||
margin: 4px 0 0 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-items: start;
|
||||
margin-top: 4px;
|
||||
}
|
||||
.search-form {
|
||||
position: relative;
|
||||
display: flex;
|
||||
height: 34px;
|
||||
flex-grow: 1;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.src nav.sub {
|
||||
margin: 0 0 -10px 0;
|
||||
|
|
@ -1208,27 +1229,14 @@ table,
|
|||
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 {
|
||||
/* ensures that 100% in properties of #crate-search-div:after
|
||||
are relative to the size of this div */
|
||||
position: relative;
|
||||
/* allows this div (and with it the <select>-element "#crate-search") to be shrunk */
|
||||
min-width: 0;
|
||||
/* keep label text for switcher from moving down when this appears */
|
||||
margin-top: -1px;
|
||||
}
|
||||
#crate-search {
|
||||
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;
|
||||
background-color: var(--button-background-color);
|
||||
color: var(--search-color);
|
||||
max-width: 100%;
|
||||
}
|
||||
.search-input:focus {
|
||||
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 {
|
||||
--popover-arrow-offset: 202px;
|
||||
--popover-arrow-offset: 196px;
|
||||
top: calc(100% - 16px);
|
||||
}
|
||||
|
||||
/* use larger max-width for help popover, but not for help.html */
|
||||
#help.popover {
|
||||
max-width: 600px;
|
||||
--popover-arrow-offset: 118px;
|
||||
--popover-arrow-offset: 115px;
|
||||
top: calc(100% - 16px);
|
||||
}
|
||||
|
||||
|
|
@ -1929,10 +1938,12 @@ a.tooltip:hover::after {
|
|||
color: inherit;
|
||||
}
|
||||
#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);
|
||||
border-top-color: var(--search-tab-button-not-selected-border-top-color);
|
||||
}
|
||||
#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);
|
||||
border-top-color: var(--search-tab-button-selected-border-top-color);
|
||||
}
|
||||
|
|
@ -1941,6 +1952,73 @@ a.tooltip:hover::after {
|
|||
font-size: 1rem;
|
||||
font-variant-numeric: tabular-nums;
|
||||
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 {
|
||||
|
|
@ -1974,7 +2052,7 @@ a.tooltip:hover::after {
|
|||
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);
|
||||
display: flex;
|
||||
line-height: 1.25;
|
||||
|
|
@ -1989,69 +2067,100 @@ a.tooltip:hover::after {
|
|||
display: flex;
|
||||
margin-right: 4px;
|
||||
position: fixed;
|
||||
margin-top: 25px;
|
||||
left: 6px;
|
||||
height: 34px;
|
||||
width: 34px;
|
||||
z-index: calc(var(--desktop-sidebar-z-index) + 1);
|
||||
}
|
||||
.hide-sidebar #sidebar-button {
|
||||
left: 6px;
|
||||
background-color: var(--main-background-color);
|
||||
z-index: 1;
|
||||
}
|
||||
.src #sidebar-button {
|
||||
margin-top: 0;
|
||||
top: 8px;
|
||||
left: 8px;
|
||||
z-index: calc(var(--desktop-sidebar-z-index) + 1);
|
||||
border-color: var(--border-color);
|
||||
}
|
||||
.hide-sidebar .src #sidebar-button {
|
||||
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;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
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-radius: var(--button-border-radius);
|
||||
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;
|
||||
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;
|
||||
}
|
||||
#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;
|
||||
}
|
||||
#sidebar-button > a:hover, #sidebar-button > a:focus-visible {
|
||||
background-color: var(--main-background-color);
|
||||
.src #sidebar-button > a {
|
||||
background-color: var(--sidebar-background-color);
|
||||
border-color: var(--border-color);
|
||||
}
|
||||
|
||||
#settings-menu > a:hover, #settings-menu > a:focus-visible,
|
||||
#help-button > a:hover, #help-button > a:focus-visible,
|
||||
#search-button > a:hover, #search-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 {
|
||||
border-color: var(--settings-button-border-focus);
|
||||
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> */
|
||||
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">\
|
||||
<path d="M10.25,6c0-0.1243286-0.0261841-0.241333-0.0366211-0.362915l1.6077881-1.5545654l\
|
||||
-1.25-2.1650391 c0,0-1.2674561,0.3625488-2.1323853,0.6099854c-0.2034912-0.1431885-0.421875\
|
||||
-0.2639771-0.6494751-0.3701782L7.25,0h-2.5 c0,0-0.3214111,1.2857666-0.5393066,2.1572876\
|
||||
C3.9830933,2.2634888,3.7647095,2.3842773,3.5612183,2.5274658L1.428833,1.9174805 \
|
||||
l-1.25,2.1650391c0,0,0.9641113,0.9321899,1.6077881,1.5545654C1.7761841,5.758667,\
|
||||
1.75,5.8756714,1.75,6 s0.0261841,0.241333,0.0366211,0.362915L0.178833,7.9174805l1.25,\
|
||||
2.1650391l2.1323853-0.6099854 c0.2034912,0.1432495,0.421875,0.2639771,0.6494751,0.3701782\
|
||||
L4.75,12h2.5l0.5393066-2.1572876 c0.2276001-0.1062012,0.4459839-0.2269287,0.6494751\
|
||||
-0.3701782l2.1323853,0.6099854l1.25-2.1650391L10.2133789,6.362915 C10.2238159,6.241333,\
|
||||
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\
|
||||
,5.1715698,7.5,6 S6.8284302,7.5,6,7.5z" fill="black"/></svg>');
|
||||
<path d="m4.75 0s-0.32117 1.286-0.53906 2.1576c-0.2276 0.1062-0.44625 \
|
||||
0.2266-0.64974 0.36979l-2.1328-0.60938-1.25 2.1641s0.9644 0.93231 1.6081 1.5547c-0.010437 \
|
||||
0.12158-0.036458 0.23895-0.036458 0.36328s0.026021 0.2417 0.036458 0.36328l-1.6081 \
|
||||
1.5547 1.25 2.1641 2.1328-0.60937c0.20349 0.14325 0.42214 0.26359 0.64974 0.36979l0.53906 \
|
||||
2.1576h2.5l0.53906-2.1576c0.2276-0.1062 0.44625-0.22654 0.64974-0.36979l2.1328 0.60937 \
|
||||
1.25-2.1641-1.6081-1.5547c0.010437-0.12158 0.036458-0.23895 \
|
||||
0.036458-0.36328s-0.02602-0.2417-0.03646-0.36328l1.6081-1.5547-1.25-2.1641s-1.2679 \
|
||||
0.36194-2.1328 0.60938c-0.20349-0.14319-0.42214-0.26359-0.64974-0.36979l-0.53906-2.1576\
|
||||
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 \
|
||||
3.4505-1.9058 2.877e-4 -3.4508-1.5447-3.4505-3.4505-2.877e-4 -1.9058 1.5447-3.4508 \
|
||||
3.4505-3.4505z" fill="black"/>\
|
||||
<circle cx="6" cy="6" r="1.75" fill="none" stroke="black" stroke-width="1"/></svg>');
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
filter: var(--settings-menu-filter);
|
||||
|
|
@ -2067,36 +2176,51 @@ button#toggle-all-docs::before {
|
|||
filter: var(--settings-menu-filter);
|
||||
}
|
||||
|
||||
button#toggle-all-docs.will-expand::before {
|
||||
/* Custom arrow icon */
|
||||
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">\
|
||||
<path d="M2,5l4,-4l4,4M2,7l4,4l4,-4" stroke="black" fill="none" stroke-width="2px"/></svg>');
|
||||
}
|
||||
|
||||
#help-button > a::before {
|
||||
/* Question mark with circle */
|
||||
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" fill="none">\
|
||||
<circle r="5.25" cx="6" cy="6" stroke-width="1.25" stroke="black"/>\
|
||||
<text x="6" y="7" style="font:8px sans-serif;font-weight:1000" text-anchor="middle" \
|
||||
dominant-baseline="middle" fill="black">?</text></svg>');
|
||||
.help-menu > a::before {
|
||||
/* Question mark with "circle" */
|
||||
content: url('data:image/svg+xml,\
|
||||
<svg width="18" height="18" enable-background="new 0 0 12 12" fill="none" \
|
||||
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" \
|
||||
stroke-width="1.5"/>\
|
||||
<path d="m5.999 7.932c0.3111 0 0.7062 0.2915 0.7062 0.7257 0 0.5458-0.3951 \
|
||||
0.8099-0.7081 0.8099-0.2973 0-0.7023-0.266-0.7023-0.7668 0-0.4695 0.3834-0.7688 \
|
||||
0.7042-0.7688z" fill="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 \
|
||||
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 \
|
||||
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;
|
||||
height: 18px;
|
||||
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,
|
||||
#help-button > a::before,
|
||||
#settings-menu > a::before {
|
||||
.help-menu > a::before,
|
||||
.settings-menu > a::before {
|
||||
filter: var(--settings-menu-filter);
|
||||
margin: 8px;
|
||||
}
|
||||
|
||||
@media not (pointer: coarse) {
|
||||
#search-button > a:hover::before,
|
||||
button#toggle-all-docs:hover::before,
|
||||
#help-button > a:hover::before,
|
||||
#settings-menu > a:hover::before {
|
||||
.help-menu > a:hover::before,
|
||||
.settings-menu > a:hover::before {
|
||||
filter: var(--settings-menu-hover-filter);
|
||||
}
|
||||
}
|
||||
|
|
@ -2122,9 +2246,9 @@ rustdoc-toolbar span.label {
|
|||
/* sidebar resizer image */
|
||||
content: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" 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"/>\
|
||||
<circle cx="4.375" cy="4.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>');
|
||||
<rect x="1" y="2" width="20" height="18" ry="1.5" stroke-width="1.5" stroke="%23777"/>\
|
||||
<circle cx="4.375" cy="5.375" r="1" stroke-width=".75"/>\
|
||||
<path d="m7.6121 4v14 M5.375 8.625h-2 m2 3h-2 m2 3h-2" stroke-width="1.25"/></svg>');
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
}
|
||||
|
|
@ -2137,7 +2261,8 @@ rustdoc-toolbar span.label {
|
|||
margin-left: 10px;
|
||||
padding: 0;
|
||||
padding-left: 2px;
|
||||
border: 0;
|
||||
border: solid 1px transparent;
|
||||
border-radius: var(--button-border-radius);
|
||||
font-size: 0;
|
||||
}
|
||||
#copy-path::before {
|
||||
|
|
@ -2159,7 +2284,7 @@ rustdoc-toolbar span.label {
|
|||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
#settings-menu.rotate > a img {
|
||||
.settings-menu.rotate > a img {
|
||||
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;
|
||||
filter: var(--mobile-sidebar-menu-filter);
|
||||
}
|
||||
.src #sidebar-button > a:hover {
|
||||
background: var(--main-background-color);
|
||||
}
|
||||
.sidebar-menu-toggle:hover::before,
|
||||
.sidebar-menu-toggle:active::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 */
|
||||
|
||||
/* Make sure all the buttons line wrap at the same time */
|
||||
@media (max-width: 850px) {
|
||||
/* Make sure all the buttons line wrap at the same time */
|
||||
#search-tabs .count {
|
||||
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 {
|
||||
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,
|
||||
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] {
|
||||
scroll-margin-top: 45px;
|
||||
|
|
@ -2451,18 +2654,32 @@ in src-script.js and main.js
|
|||
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;
|
||||
}
|
||||
#settings-menu > a, #help-button > a, button#toggle-all-docs {
|
||||
rustdoc-topbar .settings-menu > a,
|
||||
rustdoc-topbar .help-menu > a {
|
||||
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 {
|
||||
--popover-arrow-offset: 86px;
|
||||
top: 32px;
|
||||
--popover-arrow-offset: 48px;
|
||||
}
|
||||
#help.popover {
|
||||
--popover-arrow-offset: 48px;
|
||||
top: 32px;
|
||||
--popover-arrow-offset: 12px;
|
||||
}
|
||||
|
||||
.rustdoc {
|
||||
|
|
@ -2471,13 +2688,13 @@ in src-script.js and main.js
|
|||
display: block;
|
||||
}
|
||||
|
||||
main {
|
||||
html:not(.hide-sidebar) main {
|
||||
padding-left: 15px;
|
||||
padding-top: 0px;
|
||||
}
|
||||
|
||||
/* 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 .location,
|
||||
.sidebar-resizer {
|
||||
|
|
@ -2510,6 +2727,9 @@ in src-script.js and main.js
|
|||
height: 100vh;
|
||||
border: 0;
|
||||
}
|
||||
html .src main {
|
||||
padding: 18px 0;
|
||||
}
|
||||
.src .search-form {
|
||||
margin-left: 40px;
|
||||
}
|
||||
|
|
@ -2529,9 +2749,9 @@ in src-script.js and main.js
|
|||
left: 0;
|
||||
}
|
||||
|
||||
.mobile-topbar h2 {
|
||||
rustdoc-topbar > h2 {
|
||||
padding-bottom: 0;
|
||||
margin: auto 0.5em auto auto;
|
||||
margin: auto;
|
||||
overflow: hidden;
|
||||
/* Rare exception to specifying font sizes in rem. Since the topbar
|
||||
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;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.mobile-topbar .logo-container > img {
|
||||
rustdoc-topbar .logo-container > img {
|
||||
max-width: 35px;
|
||||
max-height: 35px;
|
||||
margin: 5px 0 5px 20px;
|
||||
}
|
||||
|
||||
.mobile-topbar {
|
||||
rustdoc-topbar {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
position: sticky;
|
||||
z-index: 10;
|
||||
font-size: 2rem;
|
||||
height: 45px;
|
||||
width: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.hide-sidebar .mobile-topbar {
|
||||
.hide-sidebar rustdoc-topbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.sidebar-menu-toggle {
|
||||
width: 45px;
|
||||
/* prevent flexbox shrinking */
|
||||
width: 41px;
|
||||
min-width: 41px;
|
||||
border: none;
|
||||
line-height: 0;
|
||||
}
|
||||
|
|
@ -2591,9 +2813,13 @@ in src-script.js and main.js
|
|||
#sidebar-button > a::before {
|
||||
content: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" \
|
||||
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"/>\
|
||||
<circle cx="4.375" cy="4.375" r="1" stroke-width=".75"/>\
|
||||
<path d="m3 7.375h16m0-3h-4" stroke-width="1.25"/></svg>');
|
||||
<rect x="1" y="2" width="20" height="18" ry="1.5" stroke-width="1.5" stroke="%23777"/>\
|
||||
<g fill="black" stroke="none">\
|
||||
<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;
|
||||
height: 22px;
|
||||
}
|
||||
|
|
@ -3283,7 +3509,7 @@ Original by Dempfi (https://github.com/dempfi/ayu)
|
|||
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 {
|
||||
filter: invert(100);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,23 +54,6 @@ function showMain() {
|
|||
window.rootPath = getVar("root-path");
|
||||
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
|
||||
* given KeyboardEvent, ev.
|
||||
|
|
@ -84,6 +67,7 @@ function setMobileTopbar() {
|
|||
* So I guess you could say things are getting pretty interoperable.
|
||||
*
|
||||
* @param {KeyboardEvent} ev
|
||||
* @returns {string}
|
||||
*/
|
||||
function getVirtualKey(ev) {
|
||||
if ("key" in ev && typeof ev.key !== "undefined") {
|
||||
|
|
@ -98,18 +82,8 @@ function getVirtualKey(ev) {
|
|||
}
|
||||
|
||||
const MAIN_ID = "main-content";
|
||||
const SETTINGS_BUTTON_ID = "settings-menu";
|
||||
const ALTERNATIVE_DISPLAY_ID = "alternative-display";
|
||||
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.
|
||||
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
|
||||
* elements between the two `<section>` elements.
|
||||
*
|
||||
* @param {HTMLElement|null} elemToDisplay
|
||||
* @param {Element|null} elemToDisplay
|
||||
*/
|
||||
function switchDisplayedElement(elemToDisplay) {
|
||||
const el = getAlternativeDisplayElem();
|
||||
|
|
@ -239,14 +213,14 @@ function preLoadCss(cssUrl) {
|
|||
document.head.append(script);
|
||||
}
|
||||
|
||||
const settingsButton = getSettingsButton();
|
||||
if (settingsButton) {
|
||||
settingsButton.onclick = event => {
|
||||
onEachLazy(document.querySelectorAll(".settings-menu"), settingsMenu => {
|
||||
/** @param {MouseEvent} event */
|
||||
settingsMenu.querySelector("a").onclick = event => {
|
||||
if (event.ctrlKey || event.altKey || event.metaKey) {
|
||||
return;
|
||||
}
|
||||
window.hideAllModals(false);
|
||||
addClass(getSettingsButton(), "rotate");
|
||||
addClass(settingsMenu, "rotate");
|
||||
event.preventDefault();
|
||||
// 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.
|
||||
|
|
@ -268,15 +242,42 @@ function preLoadCss(cssUrl) {
|
|||
}
|
||||
}, 0);
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
window.searchState = {
|
||||
rustdocToolbar: document.querySelector("rustdoc-toolbar"),
|
||||
loadingText: "Loading search results...",
|
||||
// This will always be an HTMLInputElement, but tsc can't see that
|
||||
// @ts-expect-error
|
||||
input: document.getElementsByClassName("search-input")[0],
|
||||
outputElement: () => {
|
||||
inputElement: () => {
|
||||
let el = document.getElementsByClassName("search-input")[0];
|
||||
if (!el) {
|
||||
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");
|
||||
if (!el) {
|
||||
el = document.createElement("section");
|
||||
|
|
@ -285,6 +286,19 @@ function preLoadCss(cssUrl) {
|
|||
}
|
||||
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,
|
||||
titleBeforeSearch: document.title,
|
||||
timeout: null,
|
||||
|
|
@ -303,25 +317,52 @@ function preLoadCss(cssUrl) {
|
|||
}
|
||||
},
|
||||
isDisplayed: () => {
|
||||
const outputElement = window.searchState.outputElement();
|
||||
return !!outputElement &&
|
||||
!!outputElement.parentElement &&
|
||||
outputElement.parentElement.id === ALTERNATIVE_DISPLAY_ID;
|
||||
const container = window.searchState.containerElement();
|
||||
if (!container) {
|
||||
return false;
|
||||
}
|
||||
return !!container.parentElement && container.parentElement.id ===
|
||||
ALTERNATIVE_DISPLAY_ID;
|
||||
},
|
||||
// Sets the focus on the search bar at the top of the page
|
||||
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.
|
||||
defocus: () => {
|
||||
window.searchState.input && window.searchState.input.blur();
|
||||
nonnull(window.searchState.inputElement()).blur();
|
||||
},
|
||||
showResults: search => {
|
||||
if (search === null || typeof search === "undefined") {
|
||||
search = window.searchState.outputElement();
|
||||
toggle: () => {
|
||||
if (window.searchState.isDisplayed()) {
|
||||
window.searchState.defocus();
|
||||
window.searchState.hideResults();
|
||||
} else {
|
||||
window.searchState.focus();
|
||||
}
|
||||
switchDisplayedElement(search);
|
||||
},
|
||||
showResults: () => {
|
||||
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: () => {
|
||||
// We change the document title.
|
||||
|
|
@ -334,6 +375,10 @@ function preLoadCss(cssUrl) {
|
|||
switchDisplayedElement(null);
|
||||
// We also remove the query parameter from the URL.
|
||||
window.searchState.removeQueryParameters();
|
||||
const btnLabel = document.querySelector("#search-button a span.label");
|
||||
if (btnLabel) {
|
||||
btnLabel.innerHTML = "Search";
|
||||
}
|
||||
},
|
||||
getQueryStringParams: () => {
|
||||
/** @type {Object.<any, string>} */
|
||||
|
|
@ -348,11 +393,11 @@ function preLoadCss(cssUrl) {
|
|||
return params;
|
||||
},
|
||||
setup: () => {
|
||||
const search_input = window.searchState.input;
|
||||
let searchLoaded = false;
|
||||
const search_input = window.searchState.inputElement();
|
||||
if (!search_input) {
|
||||
return;
|
||||
}
|
||||
let searchLoaded = false;
|
||||
// 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.
|
||||
function sendSearchForm() {
|
||||
|
|
@ -363,21 +408,102 @@ function preLoadCss(cssUrl) {
|
|||
if (!searchLoaded) {
|
||||
searchLoaded = true;
|
||||
// @ts-expect-error
|
||||
loadScript(getVar("static-root-path") + getVar("search-js"), sendSearchForm);
|
||||
loadScript(resourcePath("search-index", ".js"), sendSearchForm);
|
||||
window.rr_ = data => {
|
||||
// @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", () => {
|
||||
window.searchState.origPlaceholder = search_input.placeholder;
|
||||
search_input.placeholder = "Type your search here.";
|
||||
loadSearch();
|
||||
});
|
||||
|
||||
if (search_input.value !== "") {
|
||||
loadSearch();
|
||||
const btn = document.getElementById("search-button");
|
||||
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();
|
||||
if (params.search !== undefined) {
|
||||
window.searchState.setLoadingSearch();
|
||||
|
|
@ -386,13 +512,9 @@ function preLoadCss(cssUrl) {
|
|||
},
|
||||
setLoadingSearch: () => {
|
||||
const search = window.searchState.outputElement();
|
||||
if (!search) {
|
||||
return;
|
||||
}
|
||||
search.innerHTML = "<h3 class=\"search-loading\">" +
|
||||
window.searchState.loadingText +
|
||||
"</h3>";
|
||||
window.searchState.showResults(search);
|
||||
nonnull(search).innerHTML = "<h3 class=\"search-loading\">" +
|
||||
window.searchState.loadingText + "</h3>";
|
||||
window.searchState.showResults();
|
||||
},
|
||||
descShards: new Map(),
|
||||
loadDesc: async function({descShard, descIndex}) {
|
||||
|
|
@ -1500,15 +1622,13 @@ function preLoadCss(cssUrl) {
|
|||
|
||||
// @ts-expect-error
|
||||
function helpBlurHandler(event) {
|
||||
// @ts-expect-error
|
||||
if (!getHelpButton().contains(document.activeElement) &&
|
||||
// @ts-expect-error
|
||||
!getHelpButton().contains(event.relatedTarget) &&
|
||||
// @ts-expect-error
|
||||
!getSettingsButton().contains(document.activeElement) &&
|
||||
// @ts-expect-error
|
||||
!getSettingsButton().contains(event.relatedTarget)
|
||||
) {
|
||||
const isInPopover = onEachLazy(
|
||||
document.querySelectorAll(".settings-menu, .help-menu"),
|
||||
menu => {
|
||||
return menu.contains(document.activeElement) || menu.contains(event.relatedTarget);
|
||||
},
|
||||
);
|
||||
if (!isInPopover) {
|
||||
window.hidePopoverMenus();
|
||||
}
|
||||
}
|
||||
|
|
@ -1571,10 +1691,9 @@ function preLoadCss(cssUrl) {
|
|||
|
||||
const container = document.createElement("div");
|
||||
if (!isHelpPage) {
|
||||
container.className = "popover";
|
||||
container.className = "popover content";
|
||||
}
|
||||
container.id = "help";
|
||||
container.style.display = "none";
|
||||
|
||||
const side_by_side = document.createElement("div");
|
||||
side_by_side.className = "side-by-side";
|
||||
|
|
@ -1590,17 +1709,16 @@ function preLoadCss(cssUrl) {
|
|||
help_section.appendChild(container);
|
||||
// @ts-expect-error
|
||||
document.getElementById("main-content").appendChild(help_section);
|
||||
container.style.display = "block";
|
||||
} else {
|
||||
const help_button = getHelpButton();
|
||||
// @ts-expect-error
|
||||
help_button.appendChild(container);
|
||||
|
||||
container.onblur = helpBlurHandler;
|
||||
// @ts-expect-error
|
||||
help_button.onblur = helpBlurHandler;
|
||||
// @ts-expect-error
|
||||
help_button.children[0].onblur = helpBlurHandler;
|
||||
onEachLazy(document.getElementsByClassName("help-menu"), menu => {
|
||||
if (menu.offsetWidth !== 0) {
|
||||
menu.appendChild(container);
|
||||
container.onblur = helpBlurHandler;
|
||||
menu.onblur = helpBlurHandler;
|
||||
menu.children[0].onblur = helpBlurHandler;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return container;
|
||||
|
|
@ -1621,80 +1739,57 @@ function preLoadCss(cssUrl) {
|
|||
* Hide all the popover menus.
|
||||
*/
|
||||
window.hidePopoverMenus = () => {
|
||||
onEachLazy(document.querySelectorAll("rustdoc-toolbar .popover"), elem => {
|
||||
onEachLazy(document.querySelectorAll(".settings-menu .popover"), elem => {
|
||||
elem.style.display = "none";
|
||||
});
|
||||
const button = getHelpButton();
|
||||
if (button) {
|
||||
removeClass(button, "help-open");
|
||||
}
|
||||
onEachLazy(document.querySelectorAll(".help-menu .popover"), elem => {
|
||||
elem.parentElement.removeChild(elem);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
function showHelp() {
|
||||
window.hideAllModals(false);
|
||||
// Prevent `blur` events from being dispatched as a result of closing
|
||||
// other modals.
|
||||
const button = getHelpButton();
|
||||
addClass(button, "help-open");
|
||||
// @ts-expect-error
|
||||
button.querySelector("a").focus();
|
||||
const menu = getHelpMenu(true);
|
||||
if (menu.style.display === "none") {
|
||||
// @ts-expect-error
|
||||
window.hideAllModals();
|
||||
menu.style.display = "";
|
||||
}
|
||||
onEachLazy(document.querySelectorAll(".help-menu a"), menu => {
|
||||
if (menu.offsetWidth !== 0) {
|
||||
menu.focus();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
buildHelpMenu();
|
||||
}
|
||||
|
||||
const helpLink = document.querySelector(`#${HELP_BUTTON_ID} > a`);
|
||||
if (isHelpPage) {
|
||||
buildHelpMenu();
|
||||
} else if (helpLink) {
|
||||
helpLink.addEventListener("click", event => {
|
||||
// By default, have help button open docs in a popover.
|
||||
// If user clicks with a moderator, though, use default browser behavior,
|
||||
// probably opening in a new window or tab.
|
||||
if (!helpLink.contains(helpLink) ||
|
||||
// @ts-expect-error
|
||||
event.ctrlKey ||
|
||||
// @ts-expect-error
|
||||
event.altKey ||
|
||||
// @ts-expect-error
|
||||
event.metaKey) {
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
const menu = getHelpMenu(true);
|
||||
const shouldShowHelp = menu.style.display === "none";
|
||||
if (shouldShowHelp) {
|
||||
showHelp();
|
||||
} else {
|
||||
window.hidePopoverMenus();
|
||||
}
|
||||
} else {
|
||||
onEachLazy(document.querySelectorAll(".help-menu > a"), helpLink => {
|
||||
helpLink.addEventListener(
|
||||
"click",
|
||||
/** @param {MouseEvent} event */
|
||||
event => {
|
||||
// By default, have help button open docs in a popover.
|
||||
// If user clicks with a moderator, though, use default browser behavior,
|
||||
// probably opening in a new window or tab.
|
||||
if (event.ctrlKey ||
|
||||
event.altKey ||
|
||||
event.metaKey) {
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
if (document.getElementById("help")) {
|
||||
window.hidePopoverMenus();
|
||||
} else {
|
||||
showHelp();
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
setMobileTopbar();
|
||||
addSidebarItems();
|
||||
addSidebarCrates();
|
||||
onHashChange(null);
|
||||
|
|
@ -1746,7 +1841,15 @@ function preLoadCss(cssUrl) {
|
|||
// On larger, "desktop-sized" viewports (though that includes many
|
||||
// tablets), it's fixed-position, appears in the left side margin,
|
||||
// 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) {
|
||||
sidebarButton.addEventListener("click", e => {
|
||||
removeClass(document.documentElement, "hide-sidebar");
|
||||
|
|
|
|||
252
src/librustdoc/html/static/js/rustdoc.d.ts
vendored
252
src/librustdoc/html/static/js/rustdoc.d.ts
vendored
|
|
@ -2,6 +2,8 @@
|
|||
// 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.
|
||||
|
||||
import { RoaringBitmap } from "./stringdex";
|
||||
|
||||
/* eslint-disable */
|
||||
declare global {
|
||||
/** 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);
|
||||
/** Defined and documented in `storage.js` */
|
||||
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 {
|
||||
/** Make the current theme easy to find */
|
||||
currentTheme: HTMLLinkElement|null;
|
||||
|
|
@ -95,29 +108,28 @@ declare namespace rustdoc {
|
|||
interface SearchState {
|
||||
rustdocToolbar: HTMLElement|null;
|
||||
loadingText: string;
|
||||
input: HTMLInputElement|null;
|
||||
inputElement: function(): HTMLInputElement|null;
|
||||
containerElement: function(): Element|null;
|
||||
title: string;
|
||||
titleBeforeSearch: string;
|
||||
timeout: number|null;
|
||||
timeout: ReturnType<typeof setTimeout>|null;
|
||||
currentTab: number;
|
||||
focusedByTab: [number|null, number|null, number|null];
|
||||
focusedByTab: [Element|null, Element|null, Element|null];
|
||||
clearInputTimeout: function;
|
||||
outputElement(): HTMLElement|null;
|
||||
focus();
|
||||
defocus();
|
||||
// note: an optional param is not the same as
|
||||
// a nullable/undef-able param.
|
||||
showResults(elem?: HTMLElement|null);
|
||||
removeQueryParameters();
|
||||
hideResults();
|
||||
getQueryStringParams(): Object.<any, string>;
|
||||
origPlaceholder: string;
|
||||
outputElement: function(): Element|null;
|
||||
focus: function();
|
||||
defocus: function();
|
||||
toggle: function();
|
||||
showResults: function();
|
||||
removeQueryParameters: function();
|
||||
hideResults: function();
|
||||
getQueryStringParams: function(): Object.<any, string>;
|
||||
setup: function();
|
||||
setLoadingSearch();
|
||||
descShards: Map<string, SearchDescShard[]>;
|
||||
loadDesc: function({descShard: SearchDescShard, descIndex: number}): Promise<string|null>;
|
||||
loadedDescShard(string, number, string);
|
||||
isDisplayed(): boolean,
|
||||
loadedDescShard: function(string, number, string);
|
||||
isDisplayed: function(): boolean;
|
||||
}
|
||||
|
||||
interface SearchDescShard {
|
||||
|
|
@ -131,12 +143,13 @@ declare namespace rustdoc {
|
|||
* A single parsed "atom" in a search query. For example,
|
||||
*
|
||||
* std::fmt::Formatter, Write -> Result<()>
|
||||
* ┏━━━━━━━━━━━━━━━━━━ ┌──── ┏━━━━━┅┅┅┅┄┄┄┄┄┄┄┄┄┄┄┄┄┄┐
|
||||
* ┃ │ ┗ QueryElement { ┊
|
||||
* ┃ │ name: Result ┊
|
||||
* ┃ │ generics: [ ┊
|
||||
* ┃ │ QueryElement ┘
|
||||
* ┃ │ name: ()
|
||||
* ┏━━━━━━━━━━━━━━━━━━ ┌──── ┏━━━━━┅┅┅┅┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┐
|
||||
* ┃ │ ┗ QueryElement { ┊
|
||||
* ┃ │ name: Result ┊
|
||||
* ┃ │ generics: [ ┊
|
||||
* ┃ │ QueryElement { ┘
|
||||
* ┃ │ name: ()
|
||||
* ┃ │ }
|
||||
* ┃ │ ]
|
||||
* ┃ │ }
|
||||
* ┃ └ QueryElement {
|
||||
|
|
@ -156,14 +169,14 @@ declare namespace rustdoc {
|
|||
normalizedPathLast: string,
|
||||
generics: Array<QueryElement>,
|
||||
bindings: Map<number, Array<QueryElement>>,
|
||||
typeFilter: number|null,
|
||||
typeFilter: number,
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as QueryElement, but bindings and typeFilter support strings
|
||||
*/
|
||||
interface ParserQueryElement {
|
||||
name: string|null,
|
||||
name: string,
|
||||
id: number|null,
|
||||
fullPath: Array<string>,
|
||||
pathWithoutLast: Array<string>,
|
||||
|
|
@ -172,7 +185,7 @@ declare namespace rustdoc {
|
|||
generics: Array<ParserQueryElement>,
|
||||
bindings: Map<string, Array<ParserQueryElement>>,
|
||||
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.
|
||||
*/
|
||||
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 {
|
||||
crate: string,
|
||||
descShard: SearchDescShard,
|
||||
id: number,
|
||||
// This is the name of the item. For doc aliases, if you want the name of the aliased
|
||||
// item, take a look at `Row.original.name`.
|
||||
crate: string,
|
||||
ty: ItemType,
|
||||
name: string,
|
||||
normalizedName: string,
|
||||
word: string,
|
||||
paramNames: string[],
|
||||
parent: ({ty: number, name: string, path: string, exactPath: string}|null|undefined),
|
||||
path: string,
|
||||
ty: number,
|
||||
type: FunctionSearchType | null,
|
||||
descIndex: number,
|
||||
bitIndex: number,
|
||||
implDisambiguator: String | null,
|
||||
is_alias?: boolean,
|
||||
original?: Row,
|
||||
modulePath: string,
|
||||
exactModulePath: string,
|
||||
entry: EntryData?,
|
||||
path: PathData?,
|
||||
type: FunctionData?,
|
||||
deprecated: boolean,
|
||||
parent: { path: PathData, name: string}?,
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
interface ResultsTable {
|
||||
in_args: Array<ResultObject>,
|
||||
returned: Array<ResultObject>,
|
||||
others: Array<ResultObject>,
|
||||
query: ParsedQuery,
|
||||
in_args: AsyncGenerator<ResultObject>,
|
||||
returned: AsyncGenerator<ResultObject>,
|
||||
others: AsyncGenerator<ResultObject>,
|
||||
query: ParsedQuery<rustdoc.ParserQueryElement>,
|
||||
}
|
||||
|
||||
type Results = { max_dist?: number } & Map<number, ResultObject>
|
||||
|
|
@ -252,25 +304,41 @@ declare namespace rustdoc {
|
|||
* An annotated `Row`, used in the viewmodel.
|
||||
*/
|
||||
interface ResultObject {
|
||||
desc: string,
|
||||
desc: Promise<string|null>,
|
||||
displayPath: string,
|
||||
fullPath: string,
|
||||
href: string,
|
||||
id: number,
|
||||
dist: number,
|
||||
path_dist: number,
|
||||
name: string,
|
||||
normalizedName: string,
|
||||
word: string,
|
||||
index: number,
|
||||
parent: (Object|undefined),
|
||||
path: string,
|
||||
ty: number,
|
||||
parent: ({
|
||||
path: string,
|
||||
exactPath: string,
|
||||
name: string,
|
||||
ty: number,
|
||||
}|undefined),
|
||||
type?: FunctionSearchType,
|
||||
paramNames?: string[],
|
||||
displayTypeSignature: Promise<rustdoc.DisplayTypeSignature> | null,
|
||||
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`
|
||||
* 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.
|
||||
|
|
@ -382,8 +462,8 @@ declare namespace rustdoc {
|
|||
*/
|
||||
interface FunctionType {
|
||||
id: null|number,
|
||||
ty: number|null,
|
||||
name?: string,
|
||||
ty: ItemType,
|
||||
name: string|null,
|
||||
path: string|null,
|
||||
exactPath: string|null,
|
||||
unboxFlag: boolean,
|
||||
|
|
@ -403,70 +483,6 @@ declare namespace rustdoc {
|
|||
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;
|
||||
|
||||
/**
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,25 +1,13 @@
|
|||
// Local js definitions:
|
||||
/* global getSettingValue, updateLocalStorage, updateTheme */
|
||||
/* global addClass, removeClass, onEach, onEachLazy */
|
||||
/* global MAIN_ID, getVar, getSettingsButton, getHelpButton, nonnull */
|
||||
/* global MAIN_ID, getVar, nonnull */
|
||||
|
||||
"use strict";
|
||||
|
||||
(function() {
|
||||
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"}
|
||||
* @param {string} settingName
|
||||
|
|
@ -305,10 +293,12 @@
|
|||
}
|
||||
} else {
|
||||
el.setAttribute("tabindex", "-1");
|
||||
const settingsBtn = getSettingsButton();
|
||||
if (settingsBtn !== null) {
|
||||
settingsBtn.appendChild(el);
|
||||
}
|
||||
onEachLazy(document.querySelectorAll(".settings-menu"), menu => {
|
||||
if (menu.offsetWidth !== 0) {
|
||||
menu.appendChild(el);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
return el;
|
||||
}
|
||||
|
|
@ -317,6 +307,15 @@
|
|||
|
||||
function displaySettings() {
|
||||
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 => {
|
||||
const val = getSettingValue(el.id);
|
||||
const checked = val === "true";
|
||||
|
|
@ -330,40 +329,37 @@
|
|||
* @param {FocusEvent} event
|
||||
*/
|
||||
function settingsBlurHandler(event) {
|
||||
const helpBtn = getHelpButton();
|
||||
const settingsBtn = getSettingsButton();
|
||||
const helpUnfocused = helpBtn === null ||
|
||||
(!helpBtn.contains(document.activeElement) &&
|
||||
!elemContainsTarget(helpBtn, event.relatedTarget));
|
||||
const settingsUnfocused = settingsBtn === null ||
|
||||
(!settingsBtn.contains(document.activeElement) &&
|
||||
!elemContainsTarget(settingsBtn, event.relatedTarget));
|
||||
if (helpUnfocused && settingsUnfocused) {
|
||||
const isInPopover = onEachLazy(
|
||||
document.querySelectorAll(".settings-menu, .help-menu"),
|
||||
menu => {
|
||||
return menu.contains(document.activeElement) || menu.contains(event.relatedTarget);
|
||||
},
|
||||
);
|
||||
if (!isInPopover) {
|
||||
window.hidePopoverMenus();
|
||||
}
|
||||
}
|
||||
|
||||
if (!isSettingsPage) {
|
||||
// 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"));
|
||||
settingsButton.onclick = event => {
|
||||
if (elemContainsTarget(settingsMenu, event.target)) {
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
const shouldDisplaySettings = settingsMenu.style.display === "none";
|
||||
onEachLazy(document.querySelectorAll(".settings-menu"), settingsButton => {
|
||||
/** @param {MouseEvent} event */
|
||||
settingsButton.querySelector("a").onclick = event => {
|
||||
if (!(event.target instanceof Element) || settingsMenu.contains(event.target)) {
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
const shouldDisplaySettings = settingsMenu.style.display === "none";
|
||||
|
||||
window.hideAllModals(false);
|
||||
if (shouldDisplaySettings) {
|
||||
displaySettings();
|
||||
}
|
||||
};
|
||||
settingsButton.onblur = settingsBlurHandler;
|
||||
// the settings button should always have a link in it
|
||||
nonnull(settingsButton.querySelector("a")).onblur = settingsBlurHandler;
|
||||
window.hideAllModals(false);
|
||||
if (shouldDisplaySettings) {
|
||||
displaySettings();
|
||||
}
|
||||
};
|
||||
settingsButton.onblur = settingsBlurHandler;
|
||||
settingsButton.querySelector("a").onblur = settingsBlurHandler;
|
||||
});
|
||||
onEachLazy(settingsMenu.querySelectorAll("input"), el => {
|
||||
el.onblur = settingsBlurHandler;
|
||||
});
|
||||
|
|
@ -377,6 +373,8 @@
|
|||
if (!isSettingsPage) {
|
||||
displaySettings();
|
||||
}
|
||||
removeClass(getSettingsButton(), "rotate");
|
||||
onEachLazy(document.querySelectorAll(".settings-menu"), settingsButton => {
|
||||
removeClass(settingsButton, "rotate");
|
||||
});
|
||||
}, 0);
|
||||
})();
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
/**
|
||||
* @import * as rustdoc from "./rustdoc.d.ts";
|
||||
* @import * as stringdex from "./stringdex.d.ts";
|
||||
*/
|
||||
|
||||
const builtinThemes = ["light", "dark", "ayu"];
|
||||
|
|
@ -172,7 +173,7 @@ function updateLocalStorage(name, value) {
|
|||
} else {
|
||||
window.localStorage.setItem("rustdoc-" + name, value);
|
||||
}
|
||||
} catch (e) {
|
||||
} catch {
|
||||
// localStorage is not accessible, do nothing
|
||||
}
|
||||
}
|
||||
|
|
@ -189,7 +190,7 @@ function updateLocalStorage(name, value) {
|
|||
function getCurrentValue(name) {
|
||||
try {
|
||||
return window.localStorage.getItem("rustdoc-" + name);
|
||||
} catch (e) {
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -375,32 +376,6 @@ window.addEventListener("pageshow", ev => {
|
|||
// That's also why this is in storage.js and not main.js.
|
||||
//
|
||||
// [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 {
|
||||
constructor() {
|
||||
super();
|
||||
|
|
@ -411,11 +386,15 @@ class RustdocToolbarElement extends HTMLElement {
|
|||
return;
|
||||
}
|
||||
const rootPath = getVar("root-path");
|
||||
const currentUrl = window.location.href.split("?")[0].split("#")[0];
|
||||
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>
|
||||
</div>
|
||||
<div id="help-button" tabindex="-1">
|
||||
<div class="help-menu" tabindex="-1">
|
||||
<a href="${rootPath}help.html"><span class="label">Help</span></a>
|
||||
</div>
|
||||
<button id="toggle-all-docs"
|
||||
|
|
@ -424,3 +403,31 @@ class="label">Summary</span></button>`;
|
|||
}
|
||||
}
|
||||
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);
|
||||
|
|
|
|||
165
src/librustdoc/html/static/js/stringdex.d.ts
vendored
Normal file
165
src/librustdoc/html/static/js/stringdex.d.ts
vendored
Normal 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>?;
|
||||
};
|
||||
}
|
||||
3217
src/librustdoc/html/static/js/stringdex.js
Normal file
3217
src/librustdoc/html/static/js/stringdex.js
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -10,6 +10,6 @@
|
|||
"skipLibCheck": true
|
||||
},
|
||||
"typeAcquisition": {
|
||||
"include": ["./rustdoc.d.ts"]
|
||||
"include": ["./rustdoc.d.ts", "./stringdex.d.ts"]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@ static_files! {
|
|||
normalize_css => "static/css/normalize.css",
|
||||
main_js => "static/js/main.js",
|
||||
search_js => "static/js/search.js",
|
||||
stringdex_js => "static/js/stringdex.js",
|
||||
settings_js => "static/js/settings.js",
|
||||
src_script_js => "static/js/src-script.js",
|
||||
storage_js => "static/js/storage.js",
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
data-rustdoc-version="{{rustdoc_version}}" {#+ #}
|
||||
data-channel="{{rust_channel}}" {#+ #}
|
||||
data-search-js="{{files.search_js}}" {#+ #}
|
||||
data-stringdex-js="{{files.stringdex_js}}" {#+ #}
|
||||
data-settings-js="{{files.settings_js}}" {#+ #}
|
||||
> {# #}
|
||||
<script src="{{static_root_path|safe}}{{files.storage_js}}"></script>
|
||||
|
|
@ -72,18 +73,9 @@
|
|||
<![endif]-->
|
||||
{{ layout.external_html.before_content|safe }}
|
||||
{% if page.css_class != "src" %}
|
||||
<nav class="mobile-topbar"> {# #}
|
||||
<button class="sidebar-menu-toggle" title="show sidebar"></button>
|
||||
{% if !layout.logo.is_empty() || page.rust_logo %}
|
||||
<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>
|
||||
<rustdoc-topbar> {# #}
|
||||
<h2><a href="#">{{page.short_title}}</a></h2> {# #}
|
||||
</rustdoc-topbar>
|
||||
{% endif %}
|
||||
<nav class="sidebar">
|
||||
{% if page.css_class != "src" %}
|
||||
|
|
@ -117,9 +109,6 @@
|
|||
<div class="sidebar-resizer" title="Drag to resize sidebar"></div> {# #}
|
||||
<main>
|
||||
{% 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>
|
||||
{% if page.css_class != "src" %}</div>{% endif %}
|
||||
</main>
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@
|
|||
<h1>
|
||||
{{typ}}
|
||||
<span{% if item_type != "mod" +%} class="{{item_type}}"{% endif %}>
|
||||
{{name}}
|
||||
</span> {# #}
|
||||
{{name|wrapped|safe}}
|
||||
</span> {# #}
|
||||
<button id="copy-path" title="Copy item path to clipboard"> {# #}
|
||||
Copy item path {# #}
|
||||
</button> {# #}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
/* global globalThis */
|
||||
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const { isGeneratorObject } = require("util/types");
|
||||
|
||||
function arrayToCode(array) {
|
||||
return array.map((value, index) => {
|
||||
|
|
@ -45,23 +46,16 @@ function shouldIgnoreField(fieldName) {
|
|||
}
|
||||
|
||||
function valueMapper(key, testOutput) {
|
||||
const isAlias = testOutput["is_alias"];
|
||||
let value = testOutput[key];
|
||||
// To make our life easier, if there is a "parent" type, we add it to the path.
|
||||
if (key === "path") {
|
||||
if (testOutput["parent"] !== undefined) {
|
||||
if (testOutput["parent"]) {
|
||||
if (value.length > 0) {
|
||||
value += "::" + testOutput["parent"]["name"];
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
|
|
@ -237,7 +231,7 @@ async function runSearch(query, expected, doSearch, loadedFile, queryName) {
|
|||
const ignore_order = loadedFile.ignore_order;
|
||||
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 = [];
|
||||
|
||||
for (const key in expected) {
|
||||
|
|
@ -247,37 +241,38 @@ async function runSearch(query, expected, doSearch, loadedFile, queryName) {
|
|||
if (!Object.prototype.hasOwnProperty.call(expected, key)) {
|
||||
continue;
|
||||
}
|
||||
if (!Object.prototype.hasOwnProperty.call(results, key)) {
|
||||
if (!Object.prototype.hasOwnProperty.call(resultsTable, key)) {
|
||||
error_text.push("==> Unknown key \"" + key + "\"");
|
||||
break;
|
||||
}
|
||||
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 +
|
||||
" results but found " + results[key].length + " in '" + key + "'");
|
||||
" results but found " + resultsTable[key].length + " in '" + key + "'");
|
||||
}
|
||||
|
||||
let prev_pos = -1;
|
||||
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) {
|
||||
error_text.push(queryName + "==> Result not found in '" + key + "': '" +
|
||||
JSON.stringify(elem) + "'");
|
||||
// By default, we just compare the two first items.
|
||||
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;
|
||||
}
|
||||
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) {
|
||||
error_text.push(queryName + "==> Exact check failed at position " + (prev_pos + 1) +
|
||||
": 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) {
|
||||
error_text.push(queryName + "==> '" + JSON.stringify(elem) + "' was supposed " +
|
||||
"to be before '" + JSON.stringify(results[key][prev_pos]) + "'");
|
||||
error_text.push(queryName + "==> '" +
|
||||
JSON.stringify(elem) + "' was supposed to be before '" +
|
||||
JSON.stringify(resultsTable[key][prev_pos]) + "'");
|
||||
} else {
|
||||
prev_pos = entry_pos;
|
||||
}
|
||||
|
|
@ -286,19 +281,20 @@ async function runSearch(query, expected, doSearch, loadedFile, queryName) {
|
|||
return error_text;
|
||||
}
|
||||
|
||||
async function runCorrections(query, corrections, getCorrections, loadedFile) {
|
||||
const qc = await getCorrections(query, loadedFile.FILTER_CRATE);
|
||||
async function runCorrections(query, corrections, doSearch, loadedFile) {
|
||||
const { parsedQuery } = await doSearch(query, loadedFile.FILTER_CRATE);
|
||||
const qc = parsedQuery.correction;
|
||||
const error_text = [];
|
||||
|
||||
if (corrections === null) {
|
||||
if (qc !== null) {
|
||||
error_text.push(`==> expected = null, found = ${qc}`);
|
||||
error_text.push(`==> [correction] expected = null, found = ${qc}`);
|
||||
}
|
||||
return error_text;
|
||||
}
|
||||
|
||||
if (qc !== corrections.toLowerCase()) {
|
||||
error_text.push(`==> expected = ${corrections}, found = ${qc}`);
|
||||
if (qc.toLowerCase() !== corrections.toLowerCase()) {
|
||||
error_text.push(`==> [correction] expected = ${corrections}, found = ${qc}`);
|
||||
}
|
||||
|
||||
return error_text;
|
||||
|
|
@ -320,7 +316,7 @@ function checkResult(error_text, loadedFile, displaySuccess) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
async function runCheckInner(callback, loadedFile, entry, getCorrections, extra) {
|
||||
async function runCheckInner(callback, loadedFile, entry, extra, doSearch) {
|
||||
if (typeof entry.query !== "string") {
|
||||
console.log("FAILED");
|
||||
console.log("==> Missing `query` field");
|
||||
|
|
@ -338,7 +334,7 @@ async function runCheckInner(callback, loadedFile, entry, getCorrections, extra)
|
|||
error_text = await runCorrections(
|
||||
entry.query,
|
||||
entry.correction,
|
||||
getCorrections,
|
||||
doSearch,
|
||||
loadedFile,
|
||||
);
|
||||
if (checkResult(error_text, loadedFile, false) !== 0) {
|
||||
|
|
@ -348,16 +344,16 @@ async function runCheckInner(callback, loadedFile, entry, getCorrections, extra)
|
|||
return true;
|
||||
}
|
||||
|
||||
async function runCheck(loadedFile, key, getCorrections, callback) {
|
||||
async function runCheck(loadedFile, key, doSearch, callback) {
|
||||
const expected = loadedFile[key];
|
||||
|
||||
if (Array.isArray(expected)) {
|
||||
for (const entry of expected) {
|
||||
if (!await runCheckInner(callback, loadedFile, entry, getCorrections, true)) {
|
||||
if (!await runCheckInner(callback, loadedFile, entry, true, doSearch)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
} else if (!await runCheckInner(callback, loadedFile, expected, getCorrections, false)) {
|
||||
} else if (!await runCheckInner(callback, loadedFile, expected, false, doSearch)) {
|
||||
return 1;
|
||||
}
|
||||
console.log("OK");
|
||||
|
|
@ -368,7 +364,7 @@ function hasCheck(content, 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 checkParsed = false;
|
||||
let testFileContent = readFile(testFile);
|
||||
|
|
@ -397,12 +393,12 @@ async function runChecks(testFile, doSearch, parseQuery, getCorrections) {
|
|||
let res = 0;
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
|
@ -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"
|
||||
* @returns {Object} - Object containing keys: `doSearch`, which runs a search
|
||||
* 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.
|
||||
*/
|
||||
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"));
|
||||
},
|
||||
};
|
||||
|
||||
async function loadSearchJS(doc_folder, resource_suffix) {
|
||||
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 searchModule = require(path.join(staticFiles, searchJs));
|
||||
searchModule.initSearch(searchIndex.searchIndex);
|
||||
const docSearch = searchModule.docSearch;
|
||||
globalThis.nonnull = (x, msg) => {
|
||||
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 {
|
||||
doSearch: async function(queryStr, filterCrate, currentCrate) {
|
||||
const result = await docSearch.execQuery(searchModule.parseQuery(queryStr),
|
||||
filterCrate, currentCrate);
|
||||
const parsedQuery = DocSearch.parseQuery(queryStr);
|
||||
const result = await docSearch.execQuery(parsedQuery, filterCrate, currentCrate);
|
||||
const resultsTable = {};
|
||||
for (const tab in result) {
|
||||
if (!Object.prototype.hasOwnProperty.call(result, tab)) {
|
||||
continue;
|
||||
}
|
||||
if (!(result[tab] instanceof Array)) {
|
||||
if (!isGeneratorObject(result[tab])) {
|
||||
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) {
|
||||
if (!Object.prototype.hasOwnProperty.call(entry, key)) {
|
||||
continue;
|
||||
}
|
||||
if (key === "displayTypeSignature" && entry.displayTypeSignature !== null) {
|
||||
const {type, mappedNames, whereClause} =
|
||||
await entry.displayTypeSignature;
|
||||
entry.displayType = arrayToCode(type);
|
||||
entry.displayMappedNames = [...mappedNames.entries()]
|
||||
if (key === "desc" && entry.desc !== null) {
|
||||
flatEntry.desc = await entry.desc;
|
||||
} else if (key === "displayTypeSignature" &&
|
||||
entry.displayTypeSignature !== null
|
||||
) {
|
||||
flatEntry.displayTypeSignature = await entry.displayTypeSignature;
|
||||
const {
|
||||
type,
|
||||
mappedNames,
|
||||
whereClause,
|
||||
} = flatEntry.displayTypeSignature;
|
||||
flatEntry.displayType = arrayToCode(type);
|
||||
flatEntry.displayMappedNames = [...mappedNames.entries()]
|
||||
.map(([name, qname]) => {
|
||||
return `${name} = ${qname}`;
|
||||
}).join(", ");
|
||||
entry.displayWhereClause = [...whereClause.entries()]
|
||||
flatEntry.displayWhereClause = [...whereClause.entries()]
|
||||
.flatMap(([name, value]) => {
|
||||
if (value.length === 0) {
|
||||
return [];
|
||||
|
|
@ -489,16 +503,12 @@ function loadSearchJS(doc_folder, resource_suffix) {
|
|||
}).join(", ");
|
||||
}
|
||||
}
|
||||
resultsTable[tab].push(flatEntry);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
return { resultsTable, parsedQuery };
|
||||
},
|
||||
getCorrections: function(queryStr, filterCrate, currentCrate) {
|
||||
const parsedQuery = searchModule.parseQuery(queryStr);
|
||||
docSearch.execQuery(parsedQuery, filterCrate, currentCrate);
|
||||
return parsedQuery.correction;
|
||||
},
|
||||
parseQuery: searchModule.parseQuery,
|
||||
parseQuery: DocSearch.parseQuery,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -570,7 +580,7 @@ async function main(argv) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
const parseAndSearch = loadSearchJS(
|
||||
const parseAndSearch = await loadSearchJS(
|
||||
opts["doc_folder"],
|
||||
opts["resource_suffix"],
|
||||
);
|
||||
|
|
@ -579,14 +589,11 @@ async function main(argv) {
|
|||
const doSearch = function(queryStr, filterCrate) {
|
||||
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) {
|
||||
for (const file of opts["test_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) {
|
||||
for (const file of fs.readdirSync(opts["test_folder"])) {
|
||||
|
|
@ -595,7 +602,7 @@ async function main(argv) {
|
|||
}
|
||||
process.stdout.write(`Testing ${file} ... `);
|
||||
errors += await runChecks(path.join(opts["test_folder"], file), doSearch,
|
||||
parseAndSearch.parseQuery, getCorrections);
|
||||
parseAndSearch.parseQuery);
|
||||
}
|
||||
}
|
||||
return errors > 0 ? 1 : 0;
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ fn main() {
|
|||
.args(&["--extend-css", "z.css"])
|
||||
.input("x.rs")
|
||||
.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/settings.html").exists());
|
||||
assert!(path("invocation-only/x/all.html").exists());
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ fn main() {
|
|||
rustdoc().input("foo.rs").out_dir(&bar_first).run();
|
||||
|
||||
diff()
|
||||
.expected_file(foo_first.join("search-index.js"))
|
||||
.actual_file(bar_first.join("search-index.js"))
|
||||
.expected_file(foo_first.join("search.index/root.js"))
|
||||
.actual_file(bar_first.join("search.index/root.js"))
|
||||
.run();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,25 +5,25 @@ include: "utils.goml"
|
|||
// First we check we "hover".
|
||||
move-cursor-to: ".example-wrap"
|
||||
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" })
|
||||
|
||||
// Now we check the click.
|
||||
assert-count: (".example-wrap:not(:hover) .button-holder.keep-visible", 0)
|
||||
click: ".example-wrap"
|
||||
move-cursor-to: ".search-input"
|
||||
move-cursor-to: "#search-button"
|
||||
// It should have a new class and be visible.
|
||||
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" })
|
||||
// Clicking again will remove the class.
|
||||
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-css: (".example-wrap .copy-button", { "visibility": "hidden" })
|
||||
|
||||
// Clicking on the "copy code" button shouldn't make the buttons stick.
|
||||
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-css: (".example-wrap .copy-button", { "visibility": "hidden" })
|
||||
// Since we clicked on the copy button, the clipboard content should have been updated.
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ define-function: (
|
|||
assert-count: (".example-wrap .copy-button", 1)
|
||||
// We now ensure it's only displayed when the example is hovered.
|
||||
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" })
|
||||
// Checking that the copy button has the same size as the "copy path" button.
|
||||
compare-elements-size: (
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
// This test ensures that several clickable items actually have the pointer cursor.
|
||||
include: "utils.goml"
|
||||
go-to: "file://" + |DOC_PATH| + "/lib2/struct.Foo.html"
|
||||
|
||||
// the `[+]/[-]` button
|
||||
|
|
@ -8,11 +9,7 @@ assert-css: ("#toggle-all-docs", {"cursor": "pointer"})
|
|||
assert-css: ("#copy-path", {"cursor": "pointer"})
|
||||
|
||||
// the search tabs
|
||||
write-into: (".search-input", "Foo")
|
||||
// To be SURE that the search will be run.
|
||||
press-key: 'Enter'
|
||||
// Waiting for the search results to appear...
|
||||
wait-for: "#search-tabs"
|
||||
call-function: ("perform-search", {"query": "Foo"})
|
||||
assert-css: ("#search-tabs > button", {"cursor": "pointer"})
|
||||
|
||||
// mobile sidebar toggle button
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ call-function: ("check-colors", {
|
|||
// and make sure it goes away.
|
||||
|
||||
// First, open the settings menu.
|
||||
click: "#settings-menu"
|
||||
click: "rustdoc-toolbar .settings-menu"
|
||||
wait-for: "#settings"
|
||||
assert-css: ("#settings", {"display": "block"})
|
||||
|
||||
|
|
@ -121,7 +121,7 @@ call-function: ("check-padding", {
|
|||
define-function: ("check-line-numbers-existence", [], block {
|
||||
assert-local-storage: {"rustdoc-line-numbers": "true" }
|
||||
assert-false: ".example-line-numbers"
|
||||
click: "#settings-menu"
|
||||
click: "rustdoc-toolbar .settings-menu"
|
||||
wait-for: "#settings"
|
||||
|
||||
// Then, click the toggle button.
|
||||
|
|
@ -137,7 +137,7 @@ define-function: ("check-line-numbers-existence", [], block {
|
|||
// Line numbers should still be there.
|
||||
assert-css: ("[data-nosnippet]", { "display": "block"})
|
||||
// Closing settings menu.
|
||||
click: "#settings-menu"
|
||||
click: "rustdoc-toolbar .settings-menu"
|
||||
wait-for-css: ("#settings", {"display": "none"})
|
||||
})
|
||||
|
||||
|
|
@ -168,7 +168,7 @@ assert: ".example-wrap > pre.rust"
|
|||
assert-count: (".example-wrap", 2)
|
||||
assert-count: (".example-wrap.digits-1", 2)
|
||||
|
||||
click: "#settings-menu"
|
||||
click: "rustdoc-toolbar .settings-menu"
|
||||
wait-for: "#settings"
|
||||
|
||||
// Then, click the toggle button.
|
||||
|
|
|
|||
|
|
@ -1,13 +1,10 @@
|
|||
// This test ensures that the "Escape" shortcut is handled correctly based on the
|
||||
// current content displayed.
|
||||
include: "utils.goml"
|
||||
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
|
||||
// First, we check that the search results are hidden when the Escape key is pressed.
|
||||
write-into: (".search-input", "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
|
||||
call-function: ("perform-search", {"query": "test"})
|
||||
// Check that the currently displayed element is search.
|
||||
wait-for: "#alternative-display #search"
|
||||
assert-attribute: ("#main-content", {"class": "content hidden"})
|
||||
assert-document-property: ({"URL": "index.html?search=test"}, ENDS_WITH)
|
||||
press-key: "Escape"
|
||||
|
|
@ -17,8 +14,8 @@ assert-false: "#alternative-display #search"
|
|||
assert-attribute: ("#main-content", {"class": "content"})
|
||||
assert-document-property: ({"URL": "index.html"}, [ENDS_WITH])
|
||||
|
||||
// Check that focusing the search input brings back the search results
|
||||
focus: ".search-input"
|
||||
// Check that clicking the search button brings back the search results
|
||||
click: "#search-button"
|
||||
wait-for: "#alternative-display #search"
|
||||
assert-attribute: ("#main-content", {"class": "content hidden"})
|
||||
assert-document-property: ({"URL": "index.html?search=test"}, ENDS_WITH)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ assert-css: ("body", {"font-family": |serif_font|})
|
|||
assert-css: ("p code", {"font-family": |serif_code_font|})
|
||||
|
||||
// We now switch to the sans serif font
|
||||
click: "#settings-menu"
|
||||
click: "main .settings-menu"
|
||||
wait-for: "#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|})
|
||||
|
||||
// We switch back to the serif font
|
||||
click: "#settings-menu"
|
||||
click: "main .settings-menu"
|
||||
wait-for: "#sans-serif-fonts"
|
||||
click: "#sans-serif-fonts"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// Make sure search stores its data in `window`
|
||||
// It needs to use a global to avoid racing on search-index.js and search.js
|
||||
// https://github.com/rust-lang/rust/pull/118961
|
||||
include: "utils.goml"
|
||||
|
||||
// URL query
|
||||
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
|
||||
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
|
||||
write-into: (".search-input", "Foo")
|
||||
press-key: 'Enter'
|
||||
wait-for: "#search-tabs"
|
||||
call-function: ("perform-search", {"query": "Foo"})
|
||||
assert-window-property-false: {"searchIndex": null}
|
||||
|
||||
// source sidebar
|
||||
|
|
|
|||
|
|
@ -6,12 +6,12 @@ assert-css: ("#help", {"display": "block"})
|
|||
assert-css: ("#help dd", {"font-size": "16px"})
|
||||
assert-false: "#help-button > a"
|
||||
assert-css: ("#help", {"display": "block"})
|
||||
compare-elements-property: (".sub", "#help", ["offsetWidth"])
|
||||
compare-elements-position: (".sub", "#help", ["x"])
|
||||
compare-elements-property: (".main-heading", "#help", ["offsetWidth"])
|
||||
compare-elements-position: (".main-heading", "#help", ["x"])
|
||||
set-window-size: (500, 1000) // Try mobile next.
|
||||
assert-css: ("#help", {"display": "block"})
|
||||
compare-elements-property: (".sub", "#help", ["offsetWidth"])
|
||||
compare-elements-position: (".sub", "#help", ["x"])
|
||||
compare-elements-property: (".main-heading", "#help", ["offsetWidth"])
|
||||
compare-elements-position: (".main-heading", "#help", ["x"])
|
||||
|
||||
// Checking the color of the elements of the help menu.
|
||||
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.
|
||||
set-window-size: (1000, 1000) // Only supported on desktop.
|
||||
assert-false: "#help"
|
||||
click: "#help-button > a"
|
||||
click: "rustdoc-toolbar .help-menu > a"
|
||||
assert-css: ("#help", {"display": "block"})
|
||||
assert-css: ("#help dd", {"font-size": "16px"})
|
||||
click: "#help-button > a"
|
||||
assert-css: ("#help", {"display": "none"})
|
||||
compare-elements-property-false: (".sub", "#help", ["offsetWidth"])
|
||||
compare-elements-position-false: (".sub", "#help", ["x"])
|
||||
click: "rustdoc-toolbar .help-menu > a"
|
||||
assert-false: "#help"
|
||||
|
||||
// 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"
|
||||
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.
|
||||
assert-false: "#help"
|
||||
click: "#help-button > a"
|
||||
click: "rustdoc-toolbar .help-menu > a"
|
||||
click: "//*[@id='help']//a[text()='the rustdoc book']"
|
||||
wait-for-document-property: ({"URL": "https://doc.rust-lang.org/"}, STARTS_WITH)
|
||||
|
|
|
|||
|
|
@ -1,20 +1,19 @@
|
|||
// 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)
|
||||
|
||||
// Verify that the "hide" option is unchecked
|
||||
click: "#settings-menu"
|
||||
wait-for: "#settings"
|
||||
assert-css: ("#settings", {"display": "block"})
|
||||
assert-property: ("#hide-sidebar", {"checked": "false"})
|
||||
assert-css: (".mobile-topbar", {"display": "flex"})
|
||||
assert-css: ("rustdoc-topbar", {"display": "flex"})
|
||||
|
||||
// Toggle it
|
||||
click: "#hide-sidebar"
|
||||
assert-property: ("#hide-sidebar", {"checked": "true"})
|
||||
assert-css: (".mobile-topbar", {"display": "none"})
|
||||
assert-css: ("rustdoc-topbar", {"display": "none"})
|
||||
|
||||
// Toggle it again
|
||||
click: "#hide-sidebar"
|
||||
assert-property: ("#hide-sidebar", {"checked": "false"})
|
||||
assert-css: (".mobile-topbar", {"display": "flex"})
|
||||
assert-css: ("rustdoc-topbar", {"display": "flex"})
|
||||
|
|
|
|||
|
|
@ -8,8 +8,3 @@ assert-property: (".sidebar-crate .logo-container", {"offsetWidth": "96", "offse
|
|||
// offsetWidth = width of sidebar, offsetHeight = height + top padding
|
||||
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"})
|
||||
|
||||
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})
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ store-position: (
|
|||
{"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_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.
|
||||
set-window-size: (1100, 800)
|
||||
|
|
|
|||
|
|
@ -5,18 +5,18 @@ go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
|
|||
// First we change the title to make it big.
|
||||
set-window-size: (350, 800)
|
||||
// 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.
|
||||
store-property: (".mobile-topbar .location", {"offsetHeight": height})
|
||||
store-property: ("rustdoc-topbar h2", {"offsetHeight": height})
|
||||
// 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.
|
||||
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.
|
||||
go-to: "file://" + |DOC_PATH| + "/test_docs/struct.Foo.html"
|
||||
// We store the height we know is correct.
|
||||
store-property: (".mobile-topbar .location", {"offsetHeight": height})
|
||||
set-text: (".mobile-topbar .location a", "Something_incredibly_long_because")
|
||||
store-property: ("rustdoc-topbar h2", {"offsetHeight": height})
|
||||
set-text: ("rustdoc-topbar h2 a", "Something_incredibly_long_because")
|
||||
// And we check that the size remained the same.
|
||||
assert-property: (".mobile-topbar .location", {"offsetHeight": |height|})
|
||||
assert-property: ("rustdoc-topbar h2", {"offsetHeight": |height|})
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ set-window-size: (400, 600)
|
|||
set-font-size: 18
|
||||
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
|
||||
// all be placed as a group on a line below the setting name "Theme."
|
||||
|
|
|
|||
|
|
@ -82,15 +82,6 @@ call-function: ("check-notable-tooltip-position", {
|
|||
"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.
|
||||
define-function: (
|
||||
"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:
|
||||
|
||||
// Check that pressing escape works
|
||||
|
|
@ -189,7 +189,7 @@ assert: "#method\.create_an_iterator_from_read .tooltip:focus"
|
|||
// Check that clicking outside works.
|
||||
click: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']"
|
||||
assert-count: ("//*[@class='tooltip popover']", 1)
|
||||
click: ".search-input"
|
||||
click: ".main-heading h1"
|
||||
assert-count: ("//*[@class='tooltip popover']", 0)
|
||||
assert-false: "#method\.create_an_iterator_from_read .tooltip:focus"
|
||||
|
||||
|
|
@ -219,14 +219,14 @@ define-function: (
|
|||
store-window-property: {"scrollY": scroll}
|
||||
click: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']"
|
||||
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
|
||||
// another popover.
|
||||
call-function: ("setup-popup", {})
|
||||
click: ".search-input"
|
||||
click: ".main-heading h1"
|
||||
// We ensure we didn't come back to the previous focused item.
|
||||
assert-window-property-false: {"scrollY": |scroll|}
|
||||
|
||||
|
|
@ -251,7 +251,7 @@ reload:
|
|||
assert-count: ("//*[@class='tooltip popover']", 0)
|
||||
click: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']"
|
||||
assert-count: ("//*[@class='tooltip popover']", 1)
|
||||
click: "#settings-menu a"
|
||||
click: "rustdoc-toolbar .settings-menu a"
|
||||
wait-for: "#settings"
|
||||
assert-count: ("//*[@class='tooltip popover']", 0)
|
||||
assert-false: "#method\.create_an_iterator_from_read .tooltip:focus"
|
||||
|
|
|
|||
|
|
@ -3,33 +3,33 @@ include: "utils.goml"
|
|||
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=test"
|
||||
wait-for: "#crate-search"
|
||||
// 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.
|
||||
click: "#help-button"
|
||||
assert: "#help-button .popover"
|
||||
assert-css: ("#help-button .popover", {"display": "block"})
|
||||
click: "rustdoc-toolbar .help-menu"
|
||||
assert: "rustdoc-toolbar .help-menu .popover"
|
||||
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
|
||||
// correctly.
|
||||
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.
|
||||
click: "#help-button"
|
||||
assert-css: ("#help-button .popover", {"display": "block"})
|
||||
click: "#settings-menu"
|
||||
assert-css: ("#help-button .popover", {"display": "none"})
|
||||
assert-css: ("#settings-menu .popover", {"display": "block"})
|
||||
click: "rustdoc-toolbar .help-menu"
|
||||
assert-css: ("rustdoc-toolbar .help-menu .popover", {"display": "block"})
|
||||
click: "rustdoc-toolbar .settings-menu"
|
||||
assert-false: "rustdoc-toolbar .help-menu .popover"
|
||||
assert-css: ("rustdoc-toolbar .settings-menu .popover", {"display": "block"})
|
||||
|
||||
// Now the other way.
|
||||
click: "#help-button"
|
||||
assert-css: ("#help-button .popover", {"display": "block"})
|
||||
assert-css: ("#settings-menu .popover", {"display": "none"})
|
||||
click: "rustdoc-toolbar .help-menu"
|
||||
assert-css: ("rustdoc-toolbar .help-menu .popover", {"display": "block"})
|
||||
assert-css: ("rustdoc-toolbar .settings-menu .popover", {"display": "none"})
|
||||
|
||||
// Now verify that clicking the help menu again closes it.
|
||||
click: "#help-button"
|
||||
assert-css: ("#help-button .popover", {"display": "none"})
|
||||
assert-css: ("#settings-menu .popover", {"display": "none"})
|
||||
click: "rustdoc-toolbar .help-menu"
|
||||
assert-false: "rustdoc-toolbar .help-menu .popover"
|
||||
assert-css: (".settings-menu .popover", {"display": "none"})
|
||||
|
||||
define-function: (
|
||||
"check-popover-colors",
|
||||
|
|
@ -37,13 +37,21 @@ define-function: (
|
|||
block {
|
||||
call-function: ("switch-theme", {"theme": |theme|})
|
||||
|
||||
click: "#help-button"
|
||||
click: "rustdoc-toolbar .help-menu"
|
||||
assert-css: (
|
||||
"#help-button .popover",
|
||||
"rustdoc-toolbar .help-menu .popover",
|
||||
{"display": "block", "border-color": |border_color|},
|
||||
)
|
||||
compare-elements-css: ("#help-button .popover", "#help-button .top", ["border-color"])
|
||||
compare-elements-css: ("#help-button .popover", "#help-button .bottom", ["border-color"])
|
||||
compare-elements-css: (
|
||||
"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.
|
||||
set-window-size: (650, 600)
|
||||
click: "#settings-menu a"
|
||||
assert-css: ("#settings-menu .popover", {"display": "block"})
|
||||
click: "rustdoc-topbar .settings-menu a"
|
||||
assert-css: ("rustdoc-topbar .settings-menu .popover", {"display": "block"})
|
||||
click: ".sidebar-menu-toggle"
|
||||
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"
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ define-function: (
|
|||
"color": |help_hover_color|,
|
||||
})
|
||||
// Moving the cursor to another item to not break next runs.
|
||||
move-cursor-to: ".search-input"
|
||||
move-cursor-to: "#search-button"
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -64,8 +64,8 @@ assert-size: (".more-scraped-examples .scraped-example .example-wrap", {
|
|||
store-value: (offset_y, 4)
|
||||
|
||||
// First with desktop
|
||||
assert-position: (".scraped-example", {"y": 256})
|
||||
assert-position: (".scraped-example .prev", {"y": 256 + |offset_y|})
|
||||
assert-position: (".scraped-example", {"y": 214})
|
||||
assert-position: (".scraped-example .prev", {"y": 214 + |offset_y|})
|
||||
|
||||
// Gradient background should be at the top of the code block.
|
||||
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
|
||||
set-window-size: (600, 600)
|
||||
store-size: (".scraped-example .scraped-example-title", {"height": title_height})
|
||||
assert-position: (".scraped-example", {"y": 291})
|
||||
assert-position: (".scraped-example .prev", {"y": 291 + |offset_y| + |title_height|})
|
||||
assert-position: (".scraped-example", {"y": 249})
|
||||
assert-position: (".scraped-example .prev", {"y": 249 + |offset_y| + |title_height|})
|
||||
|
||||
define-function: (
|
||||
"check_title_and_code_position",
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ define-function: (
|
|||
// We put the toggle in the original state.
|
||||
click: ".more-examples-toggle"
|
||||
// Moving cursor away from the toggle line to prevent disrupting next test.
|
||||
move-cursor-to: ".search-input"
|
||||
move-cursor-to: "rustdoc-toolbar #search-button"
|
||||
},
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ focus: ".search-input"
|
|||
press-key: "Enter"
|
||||
|
||||
wait-for: "#search-tabs"
|
||||
wait-for-false: "#search-tabs .count.loading"
|
||||
assert-count: ("#search-tabs button", 1)
|
||||
assert-count: (".search-results > a", 1)
|
||||
|
||||
|
|
@ -32,6 +33,7 @@ focus: ".search-input"
|
|||
press-key: "Enter"
|
||||
|
||||
wait-for: "#search-tabs"
|
||||
wait-for-false: "#search-tabs .count.loading"
|
||||
assert-text: ("//div[@class='type-signature']", "F -> WhereWhitespace<T>")
|
||||
assert-count: ("#search-tabs button", 1)
|
||||
assert-count: (".search-results > a", 1)
|
||||
|
|
|
|||
|
|
@ -1,101 +1,60 @@
|
|||
// ignore-tidy-linelength
|
||||
include: "utils.goml"
|
||||
|
||||
// Checks that the search tab result tell the user about corrections
|
||||
// First, try a search-by-name
|
||||
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
|
||||
// Intentionally wrong spelling of "NotableStructWithLongName"
|
||||
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"
|
||||
call-function: ("perform-search", {"query": "NotableStructWithLongNamr"})
|
||||
|
||||
// Corrections aren't shown on the "In Names" tab.
|
||||
assert: "#search-tabs button.selected:first-child"
|
||||
assert-css: (".search-corrections", {
|
||||
"display": "none"
|
||||
})
|
||||
assert-false: ".search-results:nth-child(1) .search-corrections"
|
||||
|
||||
// Corrections do get shown on the "In Parameters" tab.
|
||||
click: "#search-tabs button:nth-child(2)"
|
||||
assert: "#search-tabs button.selected:nth-child(2)"
|
||||
assert-css: (".search-corrections", {
|
||||
"display": "block"
|
||||
})
|
||||
assert-text: (
|
||||
".search-corrections",
|
||||
"Type \"NotableStructWithLongNamr\" not found. Showing results for closest type name \"notablestructwithlongname\" instead."
|
||||
".search-results:nth-child(2) .search-corrections",
|
||||
"Type \"NotableStructWithLongNamr\" not found. Showing results for closest type name \"NotableStructWithLongName\" instead."
|
||||
)
|
||||
|
||||
// Corrections do get shown on the "In Return Type" tab.
|
||||
click: "#search-tabs button:nth-child(3)"
|
||||
assert: "#search-tabs button.selected:nth-child(3)"
|
||||
assert-css: (".search-corrections", {
|
||||
"display": "block"
|
||||
})
|
||||
assert-text: (
|
||||
".search-corrections",
|
||||
"Type \"NotableStructWithLongNamr\" not found. Showing results for closest type name \"notablestructwithlongname\" instead."
|
||||
".search-results:nth-child(3) .search-corrections",
|
||||
"Type \"NotableStructWithLongNamr\" not found. Showing results for closest type name \"NotableStructWithLongName\" instead."
|
||||
)
|
||||
|
||||
// Now, explicit return values
|
||||
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
|
||||
// Intentionally wrong spelling of "NotableStructWithLongName"
|
||||
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"
|
||||
call-function: ("perform-search", {"query": "-> NotableStructWithLongNamr"})
|
||||
|
||||
assert-css: (".search-corrections", {
|
||||
"display": "block"
|
||||
})
|
||||
assert-text: (
|
||||
".search-corrections",
|
||||
"Type \"NotableStructWithLongNamr\" not found. Showing results for closest type name \"notablestructwithlongname\" instead."
|
||||
".search-results.active .search-corrections",
|
||||
"Type \"NotableStructWithLongNamr\" not found and used as generic parameter. Consider searching for \"NotableStructWithLongName\" instead."
|
||||
)
|
||||
|
||||
// Now, generic correction
|
||||
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
|
||||
// Intentionally wrong spelling of "NotableStructWithLongName"
|
||||
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"
|
||||
call-function: ("perform-search", {"query": "NotableStructWithLongNamr, NotableStructWithLongNamr"})
|
||||
|
||||
assert-css: (".search-corrections", {
|
||||
"display": "block"
|
||||
})
|
||||
assert-text: (
|
||||
".search-corrections",
|
||||
"Type \"NotableStructWithLongNamr\" not found and used as generic parameter. Consider searching for \"notablestructwithlongname\" instead."
|
||||
".search-failed.active .search-corrections",
|
||||
"Type \"NotableStructWithLongNamr\" not found and used as generic parameter. Consider searching for \"NotableStructWithLongName\" instead."
|
||||
)
|
||||
|
||||
// Now, generic correction plus error
|
||||
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
|
||||
// Intentionally wrong spelling of "NotableStructWithLongName"
|
||||
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"
|
||||
call-function: ("perform-search", {"query": "Foo<NotableStructWithLongNamr>,y"})
|
||||
|
||||
assert-css: (".search-corrections", {
|
||||
"display": "block"
|
||||
})
|
||||
assert-text: (
|
||||
".search-corrections",
|
||||
"Type \"NotableStructWithLongNamr\" not found and used as generic parameter. Consider searching for \"notablestructwithlongname\" instead."
|
||||
".search-failed.active .search-corrections",
|
||||
"Type \"NotableStructWithLongNamr\" not found and used as generic parameter. Consider searching for \"NotableStructWithLongName\" instead."
|
||||
)
|
||||
|
||||
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
|
||||
// Intentionally wrong spelling of "NotableStructWithLongName"
|
||||
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"
|
||||
call-function: ("perform-search", {"query": "generic:NotableStructWithLongNamr<x>,y"})
|
||||
|
||||
assert-css: (".error", {
|
||||
"display": "block"
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ define-function: (
|
|||
[theme, error_background],
|
||||
block {
|
||||
call-function: ("switch-theme", {"theme": |theme|})
|
||||
wait-for-false: "#search-tabs .count.loading"
|
||||
wait-for: "#search .error code"
|
||||
assert-css: ("#search .error code", {"background-color": |error_background|})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,11 +2,7 @@
|
|||
include: "utils.goml"
|
||||
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
|
||||
show-text: true
|
||||
write-into: (".search-input", "test")
|
||||
// To be SURE that the search will be run.
|
||||
press-key: 'Enter'
|
||||
// Waiting for the search results to appear...
|
||||
wait-for: "#search-tabs"
|
||||
call-function: ("perform-search", {"query": "test"})
|
||||
assert-text: ("#results .externcrate", "test_docs")
|
||||
|
||||
wait-for: "#crate-search"
|
||||
|
|
@ -21,6 +17,7 @@ press-key: "ArrowDown"
|
|||
press-key: "Enter"
|
||||
// Waiting for the search results to appear...
|
||||
wait-for: "#search-tabs"
|
||||
wait-for-false: "#search-tabs .count.loading"
|
||||
assert-document-property: ({"URL": "&filter-crate="}, CONTAINS)
|
||||
// We check that there is no more "test_docs" appearing.
|
||||
assert-false: "#results .externcrate"
|
||||
|
|
@ -31,7 +28,8 @@ assert-property: ("#crate-search", {"value": "lib2"})
|
|||
// crate filtering.
|
||||
press-key: "Escape"
|
||||
wait-for-css: ("#main-content", {"display": "block"})
|
||||
focus: ".search-input"
|
||||
click: "#search-button"
|
||||
wait-for: ".search-input"
|
||||
wait-for-css: ("#main-content", {"display": "none"})
|
||||
// We check that there is no more "test_docs" appearing.
|
||||
assert-false: "#results .externcrate"
|
||||
|
|
@ -47,6 +45,7 @@ press-key: "ArrowUp"
|
|||
press-key: "Enter"
|
||||
// Waiting for the search results to appear...
|
||||
wait-for: "#search-tabs"
|
||||
wait-for-false: "#search-tabs .count.loading"
|
||||
assert-property: ("#crate-search", {"value": "all crates"})
|
||||
|
||||
// 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"
|
||||
|
||||
// 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-results-title + .sub-heading", " in all crates", STARTS_WITH)
|
||||
assert-text: (".search-switcher", "Search results in all crates", STARTS_WITH)
|
||||
|
||||
// Checking the display of the crate filter.
|
||||
// We start with the light theme.
|
||||
|
|
@ -72,7 +70,7 @@ assert-css: ("#crate-search", {
|
|||
})
|
||||
|
||||
// We now check the dark theme.
|
||||
click: "#settings-menu"
|
||||
click: "rustdoc-toolbar .settings-menu"
|
||||
wait-for: "#settings"
|
||||
click: "#theme-dark"
|
||||
wait-for-css: ("#crate-search", {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
include: "utils.goml"
|
||||
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=test"
|
||||
wait-for: "#search-tabs" // Waiting for the search.js to load.
|
||||
wait-for-false: "#search-tabs .count.loading"
|
||||
show-text: true
|
||||
|
||||
define-function: (
|
||||
|
|
@ -31,7 +32,7 @@ define-function: (
|
|||
},
|
||||
)
|
||||
assert-css: (
|
||||
"#help-button > a",
|
||||
"rustdoc-toolbar .help-menu > a",
|
||||
{
|
||||
"color": |menu_button_a_color|,
|
||||
"border-color": "transparent",
|
||||
|
|
@ -39,9 +40,9 @@ define-function: (
|
|||
},
|
||||
)
|
||||
// Hover help button.
|
||||
move-cursor-to: "#help-button"
|
||||
move-cursor-to: "rustdoc-toolbar .help-menu"
|
||||
assert-css: (
|
||||
"#help-button > a",
|
||||
"rustdoc-toolbar .help-menu > a",
|
||||
{
|
||||
"color": |menu_button_a_color|,
|
||||
"border-color": |menu_button_a_border_hover|,
|
||||
|
|
@ -49,15 +50,15 @@ define-function: (
|
|||
},
|
||||
)
|
||||
// Link color inside
|
||||
click: "#help-button"
|
||||
click: "rustdoc-toolbar .help-menu"
|
||||
assert-css: (
|
||||
"#help a",
|
||||
"rustdoc-toolbar #help a",
|
||||
{
|
||||
"color": |menu_a_color|,
|
||||
},
|
||||
)
|
||||
assert-css: (
|
||||
"#settings-menu > a",
|
||||
"rustdoc-toolbar .settings-menu > a",
|
||||
{
|
||||
"color": |menu_button_a_color|,
|
||||
"border-color": "transparent",
|
||||
|
|
@ -65,9 +66,9 @@ define-function: (
|
|||
},
|
||||
)
|
||||
// Hover settings menu.
|
||||
move-cursor-to: "#settings-menu"
|
||||
move-cursor-to: "rustdoc-toolbar .settings-menu"
|
||||
assert-css: (
|
||||
"#settings-menu:hover > a",
|
||||
"rustdoc-toolbar .settings-menu:hover > a",
|
||||
{
|
||||
"color": |menu_button_a_color|,
|
||||
"border-color": |menu_button_a_border_hover|,
|
||||
|
|
@ -120,8 +121,10 @@ call-function: (
|
|||
// Check that search input correctly decodes form encoding.
|
||||
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-false: "#search-tabs .count.loading"
|
||||
assert-property: (".search-input", { "value": "a b" })
|
||||
// Check that literal + is not treated as space.
|
||||
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-false: "#search-tabs .count.loading"
|
||||
assert-property: (".search-input", { "value": "a+b" })
|
||||
|
|
|
|||
|
|
@ -2,10 +2,13 @@
|
|||
// The PR which fixed it is: https://github.com/rust-lang/rust/pull/81592
|
||||
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
|
||||
set-window-size: (463, 700)
|
||||
// We first check that the search input isn't already focused.
|
||||
assert-false: ("input.search-input:focus")
|
||||
click: "input.search-input"
|
||||
click: "#search-button"
|
||||
wait-for: ".search-input"
|
||||
assert: "input.search-input:focus"
|
||||
|
||||
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
|
||||
reload:
|
||||
set-window-size: (750, 700)
|
||||
click: "input.search-input"
|
||||
assert: ("input.search-input:focus")
|
||||
click: "#search-button"
|
||||
wait-for: ".search-input"
|
||||
assert: "input.search-input:focus"
|
||||
|
|
|
|||
|
|
@ -1,28 +1,25 @@
|
|||
// Checks that the search tab results work correctly with function signature syntax
|
||||
// First, try a search-by-name
|
||||
include: "utils.goml"
|
||||
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
|
||||
write-into: (".search-input", "Foo")
|
||||
// To be SURE that the search will be run.
|
||||
press-key: 'Enter'
|
||||
// Waiting for the search results to appear...
|
||||
wait-for: "#search-tabs"
|
||||
call-function: ("perform-search", {"query": "Foo"})
|
||||
|
||||
// Now use the keyboard commands to switch to the third result.
|
||||
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.
|
||||
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"
|
||||
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"
|
||||
assert: ".search-results.active > a:focus:nth-of-type(2)"
|
||||
wait-for: ".search-results.active > a:focus:nth-of-type(2)"
|
||||
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"
|
||||
assert: ".search-input:focus"
|
||||
wait-for: ".search-input:focus"
|
||||
press-key: "ArrowDown"
|
||||
assert: ".search-results.active > a:focus:nth-of-type(1)"
|
||||
wait-for: ".search-results.active > a:focus:nth-of-type(1)"
|
||||
|
|
|
|||
|
|
@ -6,10 +6,8 @@ call-function: ("switch-theme", {"theme": "dark"})
|
|||
// 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-css: ("//*[@id='reexport.TheStdReexport']", {"background-color": "rgba(0, 0, 0, 0)"})
|
||||
write-into: (".search-input", "TheStdReexport")
|
||||
// To be SURE that the search will be run.
|
||||
press-key: 'Enter'
|
||||
wait-for: "//a[@class='result-import']"
|
||||
call-function: ("perform-search", {"query": "TheStdReexport"})
|
||||
assert: "//a[@class='result-import']"
|
||||
assert-attribute: (
|
||||
"//a[@class='result-import']",
|
||||
{"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.
|
||||
// To be SURE that the search will be run.
|
||||
press-key: 'Enter'
|
||||
write-into: (".search-input", "AliasForTheStdReexport")
|
||||
wait-for: "//a[@class='result-import']"
|
||||
call-function: ("perform-search", {"query": "AliasForTheStdReexport"})
|
||||
assert: "//a[@class='result-import']"
|
||||
assert-text: (
|
||||
"a.result-import .result-name",
|
||||
"re-export AliasForTheStdReexport - see test_docs::TheStdReexport",
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ define-function: (
|
|||
|
||||
// Waiting for the search results to appear...
|
||||
wait-for: "#search-tabs"
|
||||
wait-for-false: "#search-tabs .count.loading"
|
||||
assert-css: (
|
||||
"#search-tabs > button > .count",
|
||||
{"color": |count_color|},
|
||||
|
|
@ -212,11 +213,7 @@ call-function: ("check-search-color", {
|
|||
// Check the alias.
|
||||
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
|
||||
|
||||
write-into: (".search-input", "thisisanalias")
|
||||
// To be SURE that the search will be run.
|
||||
press-key: 'Enter'
|
||||
// Waiting for the search results to appear...
|
||||
wait-for: "#search-tabs"
|
||||
call-function: ("perform-search", {"query": "thisisanalias"})
|
||||
|
||||
define-function: (
|
||||
"check-alias",
|
||||
|
|
|
|||
|
|
@ -2,4 +2,5 @@
|
|||
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=some_more_function"
|
||||
// Waiting for the search results to appear...
|
||||
wait-for: "#search-tabs"
|
||||
wait-for-false: "#search-tabs .count.loading"
|
||||
assert-text: (".search-results .desc code", "format!")
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ write-into: (".search-input", "test")
|
|||
// To be SURE that the search will be run.
|
||||
press-key: 'Enter'
|
||||
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
|
||||
// CSS rule which is "50%"...
|
||||
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".
|
||||
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=SuperIncrediblyLongLongLongLongLongLongLongGigaGigaGigaMegaLongLongLongStructName"
|
||||
wait-for: "#crate-search"
|
||||
wait-for-false: "#search-tabs .count.loading"
|
||||
compare-elements-position-near: (
|
||||
".search-results .result-name .typename",
|
||||
".search-results .result-name .path",
|
||||
|
|
@ -51,7 +53,7 @@ set-window-size: (900, 900)
|
|||
|
||||
// First we check the current width, height and position.
|
||||
assert-css: ("#crate-search", {"width": "159px"})
|
||||
store-size: (".search-results-title", {
|
||||
store-size: (".search-switcher", {
|
||||
"height": search_results_title_height,
|
||||
"width": search_results_title_width,
|
||||
})
|
||||
|
|
@ -64,8 +66,8 @@ set-text: (
|
|||
)
|
||||
|
||||
// Then we compare again to confirm the height didn't change.
|
||||
assert-size: ("#crate-search", {"width": 370})
|
||||
assert-size: (".search-results-title", {
|
||||
assert-size: ("#crate-search", {"width": 185})
|
||||
assert-size: (".search-switcher", {
|
||||
"height": |search_results_title_height|,
|
||||
})
|
||||
assert-css: ("#search", {"width": "640px"})
|
||||
|
|
@ -79,6 +81,7 @@ define-function: (
|
|||
block {
|
||||
call-function: ("switch-theme", {"theme": |theme|})
|
||||
wait-for: "#crate-search"
|
||||
wait-for-false: "#search-tabs .count.loading"
|
||||
assert-css: ("#crate-search", {"border": "1px solid " + |border|})
|
||||
assert-css: ("#crate-search-div::after", {"filter": |filter|})
|
||||
move-cursor-to: "#crate-search"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
// Waiting for the search results to appear...
|
||||
wait-for: "#search-tabs"
|
||||
wait-for-false: "#search-tabs .count.loading"
|
||||
assert-text-false: (".main-heading h1", "Struct test_docs::FooCopy item path")
|
||||
// Ensure that the search results are displayed, not the "normal" content.
|
||||
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"
|
||||
// Waiting for the page to load...
|
||||
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")
|
||||
|
|
|
|||
|
|
@ -1,15 +1,12 @@
|
|||
// ignore-tidy-linelength
|
||||
include: "utils.goml"
|
||||
|
||||
// Checks that, if a type has two methods with the same name, they both get
|
||||
// linked correctly.
|
||||
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
|
||||
|
||||
// This should link to the inherent impl
|
||||
write-into: (".search-input", "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"
|
||||
call-function: ("perform-search", {"query": "ZyxwvutMethodDisambiguation -> bool"})
|
||||
// Check the disambiguated link.
|
||||
assert-count: ("a.result-method", 1)
|
||||
assert-attribute: ("a.result-method", {
|
||||
|
|
@ -25,11 +22,7 @@ assert: "section:target"
|
|||
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
|
||||
|
||||
// This should link to the trait impl
|
||||
write-into: (".search-input", "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"
|
||||
call-function: ("perform-search", {"query": "ZyxwvutMethodDisambiguation, usize -> usize"})
|
||||
// Check the disambiguated link.
|
||||
assert-count: ("a.result-method", 1)
|
||||
assert-attribute: ("a.result-method", {
|
||||
|
|
@ -47,6 +40,7 @@ assert: "section:target"
|
|||
// impl block's disambiguator is also acted upon.
|
||||
go-to: "file://" + |DOC_PATH| + "/lib2/index.html?search=MultiImplBlockStruct->bool"
|
||||
wait-for: "#search-tabs"
|
||||
wait-for-false: "#search-tabs .count.loading"
|
||||
assert-count: ("a.result-method", 1)
|
||||
assert-attribute: ("a.result-method", {
|
||||
"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"
|
||||
wait-for: "#search-tabs"
|
||||
wait-for-false: "#search-tabs .count.loading"
|
||||
assert-count: ("a.result-method", 1)
|
||||
assert-attribute: ("a.result-method", {
|
||||
"href": "../lib2/another_mod/struct.MultiImplBlockStruct.html#impl-MultiImplBlockTrait-for-MultiImplBlockStruct/method.second_fn"
|
||||
|
|
|
|||
|
|
@ -1,8 +1,5 @@
|
|||
// Checks that the "keyword" results have the expected text alongside them.
|
||||
include: "utils.goml"
|
||||
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
|
||||
write-into: (".search-input", "for")
|
||||
// To be SURE that the search will be run.
|
||||
press-key: 'Enter'
|
||||
// Waiting for the search results to appear...
|
||||
wait-for: "#search-tabs"
|
||||
call-function: ("perform-search", {"query": "for"})
|
||||
assert-text: (".result-keyword .result-name", "keyword for")
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
// Checks that the search tab results work correctly with function signature syntax
|
||||
// First, try a search-by-name
|
||||
include: "utils.goml"
|
||||
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
|
||||
write-into: (".search-input", "Foo")
|
||||
// To be SURE that the search will be run.
|
||||
press-key: 'Enter'
|
||||
// Waiting for the search results to appear...
|
||||
wait-for: "#search-tabs"
|
||||
call-function: ("perform-search", {"query": "Foo"})
|
||||
|
||||
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: "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
|
||||
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
|
||||
write-into: (".search-input", "-> String")
|
||||
// To be SURE that the search will be run.
|
||||
press-key: 'Enter'
|
||||
// Waiting for the search results to appear...
|
||||
wait-for: "#search-tabs"
|
||||
call-function: ("perform-search", {"query": "-> String"})
|
||||
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: "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
|
||||
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
|
||||
write-into: (".search-input", "-> Something")
|
||||
// To be SURE that the search will be run.
|
||||
press-key: 'Enter'
|
||||
// Waiting for the search results to appear...
|
||||
wait-for: "#search-tabs"
|
||||
call-function: ("perform-search", {"query": "-> Something"})
|
||||
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)
|
||||
|
||||
// Try with a search-by-parameter
|
||||
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
|
||||
write-into: (".search-input", "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"
|
||||
call-function: ("perform-search", {"query": "usize,pattern"})
|
||||
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)
|
||||
|
||||
// Try with a search-by-parameter-and-return
|
||||
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
|
||||
write-into: (".search-input", "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"
|
||||
call-function: ("perform-search", {"query": "pattern -> str"})
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -15,7 +15,8 @@ define-function: (
|
|||
focus: ".search-input"
|
||||
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)", {
|
||||
"background-color": |background|,
|
||||
"border-bottom": |border_bottom|,
|
||||
|
|
|
|||
|
|
@ -5,10 +5,7 @@ go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
|
|||
store-value: (title, "test_docs - Rust")
|
||||
assert-document-property: {"title": |title|}
|
||||
|
||||
write-into: (".search-input", "test")
|
||||
// To be SURE that the search will be run.
|
||||
press-key: 'Enter'
|
||||
wait-for: "#crate-search"
|
||||
call-function: ("perform-search", {"query": "test"})
|
||||
|
||||
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.
|
||||
press-key: 'Enter'
|
||||
wait-for: "#crate-search"
|
||||
wait-for-false: "#search-tabs .count.loading"
|
||||
|
||||
assert-document-property: {"title": '"another one" Search - Rust'}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ define-function: (
|
|||
[storage_value, setting_attribute_value, toggle_attribute_value],
|
||||
block {
|
||||
assert-local-storage: {"rustdoc-auto-hide-large-items": |storage_value|}
|
||||
click: "#settings-menu"
|
||||
click: "rustdoc-toolbar .settings-menu"
|
||||
wait-for: "#settings"
|
||||
assert-property: ("#auto-hide-large-items", {"checked": |setting_attribute_value|})
|
||||
assert-attribute: (".item-decl .type-contents-toggle", {"open": |toggle_attribute_value|})
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ define-function: (
|
|||
[storage_value, setting_attribute_value, toggle_attribute_value],
|
||||
block {
|
||||
assert-local-storage: {"rustdoc-auto-hide-method-docs": |storage_value|}
|
||||
click: "#settings-menu"
|
||||
click: "rustdoc-toolbar .settings-menu"
|
||||
wait-for: "#settings"
|
||||
assert-property: ("#auto-hide-method-docs", {"checked": |setting_attribute_value|})
|
||||
assert-attribute: (".toggle.method-toggle", {"open": |toggle_attribute_value|})
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ define-function: (
|
|||
[storage_value, setting_attribute_value, toggle_attribute_value],
|
||||
block {
|
||||
assert-local-storage: {"rustdoc-auto-hide-trait-implementations": |storage_value|}
|
||||
click: "#settings-menu"
|
||||
click: "rustdoc-toolbar .settings-menu"
|
||||
wait-for: "#settings"
|
||||
assert-property: ("#auto-hide-trait-implementations", {"checked": |setting_attribute_value|})
|
||||
assert-attribute: ("#trait-implementations-list > details", {"open": |toggle_attribute_value|}, ALL)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ define-function: (
|
|||
[storage_value, setting_attribute_value],
|
||||
block {
|
||||
assert-local-storage: {"rustdoc-go-to-only-result": |storage_value|}
|
||||
click: "#settings-menu"
|
||||
click: "rustdoc-toolbar .settings-menu"
|
||||
wait-for: "#settings"
|
||||
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)
|
||||
|
||||
// Now we change its value.
|
||||
click: "#settings-menu"
|
||||
click: "rustdoc-toolbar .settings-menu"
|
||||
wait-for: "#settings"
|
||||
click: "#go-to-only-result"
|
||||
assert-local-storage: {"rustdoc-go-to-only-result": "true"}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ define-function: (
|
|||
[theme, filter],
|
||||
block {
|
||||
call-function: ("switch-theme", {"theme": |theme|})
|
||||
assert-css: ("#settings-menu > a::before", {
|
||||
assert-css: ("rustdoc-toolbar .settings-menu > a::before", {
|
||||
"filter": |filter|,
|
||||
"width": "18px",
|
||||
"height": "18px",
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ show-text: true // needed when we check for colors below.
|
|||
// First, we check that the settings page doesn't exist.
|
||||
assert-false: "#settings"
|
||||
// We now click on the settings button.
|
||||
click: "#settings-menu"
|
||||
click: "rustdoc-toolbar .settings-menu"
|
||||
wait-for: "#settings"
|
||||
assert-css: ("#settings", {"display": "block"})
|
||||
|
||||
|
|
@ -13,11 +13,11 @@ assert-css: ("#settings", {"display": "block"})
|
|||
store-css: (".setting-line", {"margin": setting_line_margin})
|
||||
|
||||
// Let's close it by clicking on the same button.
|
||||
click: "#settings-menu"
|
||||
click: "rustdoc-toolbar .settings-menu"
|
||||
wait-for-css: ("#settings", {"display": "none"})
|
||||
|
||||
// Let's check that pressing "ESCAPE" is closing it.
|
||||
click: "#settings-menu"
|
||||
click: "rustdoc-toolbar .settings-menu"
|
||||
wait-for-css: ("#settings", {"display": "block"})
|
||||
press-key: "Escape"
|
||||
wait-for-css: ("#settings", {"display": "none"})
|
||||
|
|
@ -28,7 +28,7 @@ write: "test"
|
|||
// To be SURE that the search will be run.
|
||||
press-key: 'Enter'
|
||||
wait-for: "#alternative-display #search"
|
||||
click: "#settings-menu"
|
||||
click: "rustdoc-toolbar .settings-menu"
|
||||
wait-for-css: ("#settings", {"display": "block"})
|
||||
// Ensure that the search is still displayed.
|
||||
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.
|
||||
reload:
|
||||
|
||||
click: "#settings-menu"
|
||||
click: "rustdoc-toolbar .settings-menu"
|
||||
wait-for: "#settings"
|
||||
|
||||
// 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"})
|
||||
|
||||
// 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.
|
||||
assert-css: (
|
||||
"#theme-dark",
|
||||
|
|
@ -194,7 +194,7 @@ assert-css: (
|
|||
"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.
|
||||
focus: "#auto-hide-large-items"
|
||||
assert-css: (
|
||||
|
|
@ -273,43 +273,43 @@ assert-local-storage: {"rustdoc-disable-shortcuts": "true"}
|
|||
press-key: "Escape"
|
||||
press-key: "?"
|
||||
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.
|
||||
click: "#disable-shortcuts"
|
||||
assert-local-storage: {"rustdoc-disable-shortcuts": "false"}
|
||||
press-key: "Escape"
|
||||
press-key: "?"
|
||||
wait-for-css: ("#help-button .popover", {"display": "block"})
|
||||
assert-css: ("#settings-menu .popover", {"display": "none"})
|
||||
wait-for-css: ("rustdoc-toolbar .help-menu .popover", {"display": "block"})
|
||||
assert-css: ("rustdoc-toolbar .settings-menu .popover", {"display": "none"})
|
||||
|
||||
// Now switch back to the settings popover, and make sure the keyboard
|
||||
// shortcut works when a check box is selected.
|
||||
click: "#settings-menu > a"
|
||||
wait-for-css: ("#settings-menu .popover", {"display": "block"})
|
||||
click: "rustdoc-toolbar .settings-menu > a"
|
||||
wait-for-css: ("rustdoc-toolbar .settings-menu .popover", {"display": "block"})
|
||||
focus: "#auto-hide-large-items"
|
||||
press-key: "?"
|
||||
wait-for-css: ("#settings-menu .popover", {"display": "none"})
|
||||
wait-for-css: ("#help-button .popover", {"display": "block"})
|
||||
wait-for-css: ("rustdoc-toolbar .settings-menu .popover", {"display": "none"})
|
||||
wait-for-css: ("rustdoc-toolbar .help-menu .popover", {"display": "block"})
|
||||
|
||||
// Now switch back to the settings popover, and make sure the keyboard
|
||||
// shortcut works when a check box is selected.
|
||||
click: "#settings-menu > a"
|
||||
wait-for-css: ("#settings-menu .popover", {"display": "block"})
|
||||
wait-for-css: ("#help-button .popover", {"display": "none"})
|
||||
click: "rustdoc-toolbar .settings-menu > a"
|
||||
wait-for-css: ("rustdoc-toolbar .settings-menu .popover", {"display": "block"})
|
||||
assert-false: "rustdoc-toolbar .help-menu .popover"
|
||||
focus: "#theme-system-preference"
|
||||
press-key: "?"
|
||||
wait-for-css: ("#settings-menu .popover", {"display": "none"})
|
||||
wait-for-css: ("#help-button .popover", {"display": "block"})
|
||||
wait-for-css: ("rustdoc-toolbar .settings-menu .popover", {"display": "none"})
|
||||
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.
|
||||
go-to: "file://" + |DOC_PATH| + "/settings.html"
|
||||
wait-for: "#settings"
|
||||
assert-false: "#settings-menu"
|
||||
assert-false: "rustdoc-toolbar .settings-menu"
|
||||
assert-css: (".setting-radio", {"cursor": "pointer"})
|
||||
|
||||
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.
|
||||
assert-css: (".setting-line", {"margin": |setting_line_margin|})
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@ press-key: "Escape"
|
|||
assert-false: "input.search-input:focus"
|
||||
// We now check for the help popup.
|
||||
press-key: "?"
|
||||
assert-css: ("#help-button .popover", {"display": "block"})
|
||||
assert-css: ("rustdoc-toolbar .help-menu .popover", {"display": "block"})
|
||||
press-key: "Escape"
|
||||
assert-css: ("#help-button .popover", {"display": "none"})
|
||||
assert-false: "rustdoc-toolbar .help-menu .popover"
|
||||
// Checking doc collapse and expand.
|
||||
// It should be displaying a "-":
|
||||
assert-text: ("#toggle-all-docs", "Summary")
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ assert-css: (".sidebar", {"display": "block", "left": "-1000px"})
|
|||
focus: ".sidebar-elems h3 a"
|
||||
assert-css: (".sidebar", {"display": "block", "left": "0px"})
|
||||
// When we tab out of the sidebar, close it.
|
||||
focus: ".search-input"
|
||||
focus: "#search-button"
|
||||
assert-css: (".sidebar", {"display": "block", "left": "-1000px"})
|
||||
|
||||
// Open the sidebar menu.
|
||||
|
|
@ -43,7 +43,7 @@ press-key: "Escape"
|
|||
assert-css: (".sidebar", {"display": "block", "left": "-1000px"})
|
||||
|
||||
// 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
|
||||
// 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.
|
||||
click: ".sidebar-menu-toggle"
|
||||
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.
|
||||
// Close the sidebar menu.
|
||||
|
|
@ -65,7 +65,7 @@ define-function: (
|
|||
"check-colors",
|
||||
[theme, color, background],
|
||||
block {
|
||||
call-function: ("switch-theme", {"theme": |theme|})
|
||||
call-function: ("switch-theme-mobile", {"theme": |theme|})
|
||||
reload:
|
||||
|
||||
// Open the sidebar menu.
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
|
||||
assert-property: (".sidebar", {"clientWidth": "199"})
|
||||
show-text: true
|
||||
click: "#settings-menu"
|
||||
click: "rustdoc-toolbar .settings-menu"
|
||||
wait-for: "#settings"
|
||||
assert-css: ("#settings", {"display": "block"})
|
||||
// normal resizing
|
||||
|
|
@ -12,7 +12,7 @@ assert-css: ("#settings", {"display": "none"})
|
|||
|
||||
// Now same thing, but for source code
|
||||
go-to: "file://" + |DOC_PATH| + "/src/test_docs/lib.rs.html"
|
||||
click: "#settings-menu"
|
||||
click: "rustdoc-toolbar .settings-menu"
|
||||
wait-for: "#settings"
|
||||
assert-css: ("#settings", {"display": "block"})
|
||||
assert-property: (".sidebar", {"clientWidth": "49"})
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ assert-property: (".sidebar", {"clientWidth": "199"})
|
|||
show-text: true
|
||||
|
||||
// Verify that the "hide" option is unchecked
|
||||
click: "#settings-menu"
|
||||
click: "rustdoc-toolbar .settings-menu"
|
||||
wait-for: "#settings"
|
||||
assert-css: ("#settings", {"display": "block"})
|
||||
assert-property: ("#hide-sidebar", {"checked": "false"})
|
||||
|
|
@ -15,7 +15,7 @@ drag-and-drop: ((205, 100), (5, 100))
|
|||
assert-css: (".sidebar", {"display": "none"})
|
||||
|
||||
// Verify that the "hide" option is checked
|
||||
focus: "#settings-menu a"
|
||||
focus: "rustdoc-toolbar .settings-menu a"
|
||||
press-key: "Enter"
|
||||
wait-for-css: ("#settings", {"display": "block"})
|
||||
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
|
||||
// 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)
|
||||
focus: "#settings-menu a"
|
||||
focus: "rustdoc-topbar .settings-menu a"
|
||||
press-key: "Enter"
|
||||
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"})
|
||||
store-position: ("#sidebar-button", {
|
||||
"y": sidebar_button_y,
|
||||
"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"})
|
||||
press-key: "Escape"
|
||||
// Clicking the sidebar button should work, and implicitly re-enable
|
||||
// the persistent navigation bar
|
||||
wait-for-css: ("#settings", {"display": "none"})
|
||||
assert-position: ("#sidebar-button", {
|
||||
"y": |sidebar_button_y|,
|
||||
"x": |sidebar_button_x|,
|
||||
})
|
||||
click: "#sidebar-button"
|
||||
wait-for-css: (".sidebar", {"display": "block"})
|
||||
|
|
|
|||
|
|
@ -141,7 +141,7 @@ click: "#sidebar-button"
|
|||
wait-for-css: (".src .sidebar > *", {"visibility": "hidden"})
|
||||
// We scroll to line 117 to change the scroll position.
|
||||
scroll-to: '//*[@id="117"]'
|
||||
store-value: (y_offset, "2578")
|
||||
store-value: (y_offset, "2567")
|
||||
assert-window-property: {"pageYOffset": |y_offset|}
|
||||
// Expanding the sidebar...
|
||||
click: "#sidebar-button"
|
||||
|
|
|
|||
|
|
@ -85,4 +85,4 @@ assert-false: ".src-sidebar-expanded"
|
|||
assert: "nav.sidebar"
|
||||
|
||||
// Check that the topbar is not visible
|
||||
assert-false: ".mobile-topbar"
|
||||
assert-false: "rustdoc-topbar"
|
||||
|
|
|
|||
|
|
@ -200,7 +200,7 @@ drag-and-drop: ((205, 100), (108, 100))
|
|||
assert-position: (".sidebar-crate > h2 > a", {"x": -3})
|
||||
|
||||
// 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.
|
||||
click: ".main-heading .src"
|
||||
assert-css: ("#sidebar-button a::before", {"content": |image_url|})
|
||||
|
|
@ -212,7 +212,7 @@ assert: |sidebar_background| != |sidebar_background_hover|
|
|||
click: "#sidebar-button a"
|
||||
wait-for: "html.src-sidebar-expanded"
|
||||
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|})
|
||||
// Closing sidebar.
|
||||
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.
|
||||
set-window-size: (500, 500)
|
||||
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})
|
||||
// The sidebar background is supposed to be the same as the main background.
|
||||
assert-css: ("body", {"background-color": |not_sidebar_background|})
|
||||
|
|
|
|||
|
|
@ -8,13 +8,13 @@ set-window-size: (600, 800)
|
|||
assert-property: ("html", {"scrollTop": "0"})
|
||||
|
||||
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"]'
|
||||
assert-property: ("html", {"scrollTop": "239"})
|
||||
assert-property: ("html", {"scrollTop": "228"})
|
||||
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
|
||||
// Extra information: the "sub_fn" function header is on line 1.
|
||||
click: '//*[@id="6"]'
|
||||
assert-property: ("html", {"scrollTop": "134"})
|
||||
assert-property: ("html", {"scrollTop": "123"})
|
||||
|
|
|
|||
|
|
@ -89,9 +89,9 @@ assert-css: ("a[data-nosnippet]", {"text-align": "right"}, ALL)
|
|||
// do anything (and certainly not add a `#NaN` to the URL!).
|
||||
go-to: "file://" + |DOC_PATH| + "/src/test_docs/lib.rs.html"
|
||||
// We use this assert-position to know where we will click.
|
||||
assert-position: ("//*[@id='1']", {"x": 81, "y": 169})
|
||||
// We click on the left of the "1" anchor but still in the `a[data-nosnippet]`.
|
||||
click: (77, 163)
|
||||
assert-position: ("//*[@id='1']", {"x": 81, "y": 141})
|
||||
// We click on the left of the "1" anchor but still in the "src-line-number" `<pre>`.
|
||||
click: (135, 77)
|
||||
assert-document-property: ({"URL": "/lib.rs.html"}, ENDS_WITH)
|
||||
|
||||
// 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,
|
||||
})
|
||||
|
||||
// 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).
|
||||
set-window-size: (700, 700)
|
||||
store-property: (".src-sidebar-title", {
|
||||
"offsetHeight": source_sidebar_title_height,
|
||||
"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,
|
||||
})
|
||||
|
||||
// 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).
|
||||
set-window-size: (450, 700)
|
||||
store-property: (".src-sidebar-title", {
|
||||
"offsetHeight": source_sidebar_title_height,
|
||||
"offsetTop": source_sidebar_title_y,
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ define-function: (
|
|||
)
|
||||
|
||||
store-size: (".rust code", {"width": width, "height": height})
|
||||
click: "#settings-menu"
|
||||
click: "main .settings-menu"
|
||||
wait-for: "#settings"
|
||||
call-function: ("click-code-wrapping", {"expected": "true"})
|
||||
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.
|
||||
go-to: "file://" + |DOC_PATH| + "/test_docs/trait_bounds/index.html"
|
||||
click: "#settings-menu"
|
||||
click: "main .settings-menu"
|
||||
wait-for: "#settings"
|
||||
|
||||
store-property: (".example-wrap .rust code", {"scrollWidth": rust_width, "scrollHeight": rust_height})
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ store-value: (background_light, "white")
|
|||
store-value: (background_dark, "#353535")
|
||||
store-value: (background_ayu, "#0f1419")
|
||||
|
||||
click: "#settings-menu"
|
||||
click: "rustdoc-toolbar .settings-menu"
|
||||
wait-for: "#theme-ayu"
|
||||
click: "#theme-ayu"
|
||||
// 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_custom_theme, "red")
|
||||
|
||||
click: "#settings-menu"
|
||||
click: "rustdoc-toolbar .settings-menu"
|
||||
wait-for: "#theme-ayu"
|
||||
click: "#theme-ayu"
|
||||
// should be the ayu theme so let's check the color.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// Ensure that the theme picker always starts with the actual defaults.
|
||||
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
|
||||
click: "#settings-menu"
|
||||
click: "rustdoc-toolbar .settings-menu"
|
||||
wait-for: "#theme-system-preference"
|
||||
assert: "#theme-system-preference:checked"
|
||||
assert: "#preferred-light-theme-light:checked"
|
||||
|
|
@ -16,7 +16,7 @@ set-local-storage: {
|
|||
"rustdoc-theme": "ayu"
|
||||
}
|
||||
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
|
||||
click: "#settings-menu"
|
||||
click: "rustdoc-toolbar .settings-menu"
|
||||
wait-for: "#theme-system-preference"
|
||||
assert: "#theme-system-preference:checked"
|
||||
assert: "#preferred-light-theme-light:checked"
|
||||
|
|
|
|||
|
|
@ -13,4 +13,4 @@ assert-attribute-false: (".impl-items .toggle", {"open": ""})
|
|||
// Click the "Trait" part of "impl Trait" and verify it navigates.
|
||||
click: "#impl-Trait-for-Foo h3 a:first-of-type"
|
||||
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")
|
||||
|
|
|
|||
|
|
@ -3,12 +3,12 @@
|
|||
go-to: "file://" + |DOC_PATH| + "/test_docs/struct.Foo.html"
|
||||
set-window-size: (433, 600)
|
||||
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": ""})
|
||||
click: (4, 270)
|
||||
click: (4, 230)
|
||||
assert-attribute: (".top-doc", {"open": ""})
|
||||
// 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 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
|
||||
set-window-size: (600, 600)
|
||||
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": ""})
|
||||
click: (4, 270)
|
||||
click: (4, 230)
|
||||
assert-attribute: (".top-doc", {"open": ""})
|
||||
// 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": ""})
|
||||
|
||||
// Same check on trait items.
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ define-function: (
|
|||
"filter": |filter|,
|
||||
})
|
||||
// moving the cursor somewhere else to not mess with next function calls.
|
||||
move-cursor-to: ".search-input"
|
||||
move-cursor-to: "#search-button"
|
||||
},
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -47,27 +47,27 @@ assert-property: ("pre.item-decl", {"scrollWidth": "950"})
|
|||
set-window-size: (600, 600)
|
||||
go-to: "file://" + |DOC_PATH| + "/lib2/too_long/struct.SuperIncrediblyLongLongLongLongLongLongLongGigaGigaGigaMegaLongLongLongStructName.html"
|
||||
// It shouldn't have an overflow in the topbar either.
|
||||
store-property: (".mobile-topbar", {"scrollWidth": scrollWidth})
|
||||
assert-property: (".mobile-topbar", {"clientWidth": |scrollWidth|})
|
||||
assert-css: (".mobile-topbar h2", {"overflow-x": "hidden"})
|
||||
store-property: ("rustdoc-topbar", {"scrollWidth": scrollWidth})
|
||||
assert-property: ("rustdoc-topbar", {"clientWidth": |scrollWidth|}, NEAR)
|
||||
assert-css: ("rustdoc-topbar h2", {"overflow-x": "hidden"})
|
||||
|
||||
// Check that main heading and toolbar go side-by-side, both on desktop and on mobile.
|
||||
set-window-size: (1100, 800)
|
||||
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-near-false: (".main-heading h1", ".main-heading rustdoc-toolbar", {"x": 550})
|
||||
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 #search-button", {"x": 300})
|
||||
go-to: "file://" + |DOC_PATH| + "/lib2/index.html"
|
||||
compare-elements-position: (".main-heading h1", ".main-heading rustdoc-toolbar", ["y"])
|
||||
compare-elements-position-near-false: (".main-heading h1", ".main-heading rustdoc-toolbar", {"x": 550})
|
||||
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 #search-button", {"x": 300})
|
||||
|
||||
// On mobile, they always wrap.
|
||||
set-window-size: (600, 600)
|
||||
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-near-false: (".main-heading h1", ".main-heading rustdoc-toolbar", {"x": 200})
|
||||
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 #search-button", {"x": 200})
|
||||
go-to: "file://" + |DOC_PATH| + "/lib2/index.html"
|
||||
compare-elements-position: (".main-heading h1", ".main-heading rustdoc-toolbar", ["y"])
|
||||
compare-elements-position-near-false: (".main-heading h1", ".main-heading rustdoc-toolbar", {"x": 200})
|
||||
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 #search-button", {"x": 200})
|
||||
|
||||
// Now we will check that the scrolling is working.
|
||||
// First on an item with "hidden methods".
|
||||
|
|
|
|||
|
|
@ -5,14 +5,47 @@ define-function: (
|
|||
block {
|
||||
// Set the theme.
|
||||
// Open the settings menu.
|
||||
click: "#settings-menu"
|
||||
click: "rustdoc-toolbar .settings-menu"
|
||||
// Wait for the popover to appear...
|
||||
wait-for: "#settings"
|
||||
// Change the setting.
|
||||
click: "#theme-"+ |theme|
|
||||
// Close the popover.
|
||||
click: "#settings-menu"
|
||||
click: "rustdoc-toolbar .settings-menu"
|
||||
// Ensure that the local storage was correctly updated.
|
||||
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"
|
||||
}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -6,5 +6,10 @@ const EXPECTED = {
|
|||
'name': 'reference',
|
||||
'desc': "References, <code>&T</code> and <code>&mut T</code>.",
|
||||
},
|
||||
{
|
||||
'path': 'std::ops',
|
||||
'name': 'BitAnd',
|
||||
'desc': "The bitwise AND operator <code>&</code>.",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
const EXPECTED = {
|
||||
'query': '+',
|
||||
'others': [
|
||||
{ 'path': 'std::ops', 'name': 'AddAssign' },
|
||||
{ 'path': 'std::ops', 'name': 'Add' },
|
||||
{ 'path': 'core::ops', 'name': 'AddAssign' },
|
||||
{ 'path': 'core::ops', 'name': 'Add' },
|
||||
{ 'path': 'std::ops', 'name': 'AddAssign' },
|
||||
],
|
||||
};
|
||||
|
|
|
|||
|
|
@ -9,6 +9,6 @@ const EXPECTED = {
|
|||
{ 'path': 'std::str', 'name': 'eq' },
|
||||
],
|
||||
'returned': [
|
||||
{ 'path': 'std::string::String', 'name': 'add' },
|
||||
{ 'path': 'std::string::String', 'name': 'new' },
|
||||
],
|
||||
};
|
||||
|
|
|
|||
|
|
@ -20,12 +20,12 @@ const PARSED = [
|
|||
pathLast: "c",
|
||||
normalizedPathLast: "c",
|
||||
generics: [],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
},
|
||||
]
|
||||
],
|
||||
],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
},
|
||||
],
|
||||
foundElems: 1,
|
||||
|
|
@ -51,11 +51,11 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "c",
|
||||
generics: [],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
}]
|
||||
],
|
||||
],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
},
|
||||
],
|
||||
foundElems: 1,
|
||||
|
|
@ -81,11 +81,11 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "never",
|
||||
generics: [],
|
||||
typeFilter: 1,
|
||||
typeFilter: "primitive",
|
||||
}]
|
||||
],
|
||||
],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
},
|
||||
],
|
||||
foundElems: 1,
|
||||
|
|
@ -111,11 +111,11 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "[]",
|
||||
generics: [],
|
||||
typeFilter: 1,
|
||||
typeFilter: "primitive",
|
||||
}]
|
||||
],
|
||||
],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
},
|
||||
],
|
||||
foundElems: 1,
|
||||
|
|
@ -147,14 +147,14 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "never",
|
||||
generics: [],
|
||||
typeFilter: 1,
|
||||
typeFilter: "primitive",
|
||||
},
|
||||
],
|
||||
typeFilter: 1,
|
||||
typeFilter: "primitive",
|
||||
}]
|
||||
],
|
||||
],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
},
|
||||
],
|
||||
foundElems: 1,
|
||||
|
|
@ -213,7 +213,7 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "c",
|
||||
generics: [],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
},
|
||||
{
|
||||
name: "X",
|
||||
|
|
@ -221,12 +221,12 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "x",
|
||||
generics: [],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
},
|
||||
],
|
||||
],
|
||||
],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
},
|
||||
],
|
||||
foundElems: 1,
|
||||
|
|
|
|||
|
|
@ -406,10 +406,10 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "x",
|
||||
generics: [],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
},
|
||||
],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
},
|
||||
{
|
||||
name: "y",
|
||||
|
|
@ -417,7 +417,7 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "y",
|
||||
generics: [],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
},
|
||||
],
|
||||
foundElems: 2,
|
||||
|
|
@ -440,7 +440,7 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "x",
|
||||
generics: [],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
},
|
||||
{
|
||||
name: "y",
|
||||
|
|
@ -448,10 +448,10 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "y",
|
||||
generics: [],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
},
|
||||
],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
},
|
||||
],
|
||||
foundElems: 1,
|
||||
|
|
@ -468,7 +468,7 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "p",
|
||||
generics: [],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
},
|
||||
{
|
||||
name: "x",
|
||||
|
|
@ -476,7 +476,7 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "x",
|
||||
generics: [],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
},
|
||||
{
|
||||
name: "y",
|
||||
|
|
@ -484,7 +484,7 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "y",
|
||||
generics: [],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
},
|
||||
],
|
||||
foundElems: 3,
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "foo",
|
||||
generics: [],
|
||||
typeFilter: 7,
|
||||
typeFilter: "fn",
|
||||
}],
|
||||
foundElems: 1,
|
||||
userQuery: "fn:foo",
|
||||
|
|
@ -22,7 +22,7 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "foo",
|
||||
generics: [],
|
||||
typeFilter: 6,
|
||||
typeFilter: "enum",
|
||||
}],
|
||||
foundElems: 1,
|
||||
userQuery: "enum : foo",
|
||||
|
|
@ -45,7 +45,7 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "macro",
|
||||
generics: [],
|
||||
typeFilter: 16,
|
||||
typeFilter: "macro",
|
||||
}],
|
||||
foundElems: 1,
|
||||
userQuery: "macro!",
|
||||
|
|
@ -60,7 +60,7 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "mac",
|
||||
generics: [],
|
||||
typeFilter: 16,
|
||||
typeFilter: "macro",
|
||||
}],
|
||||
foundElems: 1,
|
||||
userQuery: "macro:mac!",
|
||||
|
|
@ -75,7 +75,7 @@ const PARSED = [
|
|||
pathWithoutLast: ["a"],
|
||||
pathLast: "mac",
|
||||
generics: [],
|
||||
typeFilter: 16,
|
||||
typeFilter: "macro",
|
||||
}],
|
||||
foundElems: 1,
|
||||
userQuery: "a::mac!",
|
||||
|
|
@ -93,7 +93,7 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "foo",
|
||||
generics: [],
|
||||
typeFilter: 7,
|
||||
typeFilter: "fn",
|
||||
}],
|
||||
error: null,
|
||||
},
|
||||
|
|
@ -114,10 +114,10 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "bar",
|
||||
generics: [],
|
||||
typeFilter: 7,
|
||||
typeFilter: "fn",
|
||||
}
|
||||
],
|
||||
typeFilter: 7,
|
||||
typeFilter: "fn",
|
||||
}],
|
||||
error: null,
|
||||
},
|
||||
|
|
@ -138,7 +138,7 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "bar",
|
||||
generics: [],
|
||||
typeFilter: 7,
|
||||
typeFilter: "fn",
|
||||
},
|
||||
{
|
||||
name: "baz::fuzz",
|
||||
|
|
@ -146,10 +146,10 @@ const PARSED = [
|
|||
pathWithoutLast: ["baz"],
|
||||
pathLast: "fuzz",
|
||||
generics: [],
|
||||
typeFilter: 6,
|
||||
typeFilter: "enum",
|
||||
},
|
||||
],
|
||||
typeFilter: 7,
|
||||
typeFilter: "fn",
|
||||
}],
|
||||
error: null,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "p",
|
||||
generics: [],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
},
|
||||
{
|
||||
name: "u8",
|
||||
|
|
@ -24,7 +24,7 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "u8",
|
||||
generics: [],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
},
|
||||
],
|
||||
foundElems: 2,
|
||||
|
|
@ -49,7 +49,7 @@ const PARSED = [
|
|||
generics: [],
|
||||
},
|
||||
],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
},
|
||||
],
|
||||
foundElems: 1,
|
||||
|
|
@ -82,7 +82,7 @@ const PARSED = [
|
|||
],
|
||||
},
|
||||
],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
},
|
||||
],
|
||||
foundElems: 1,
|
||||
|
|
@ -122,7 +122,7 @@ const PARSED = [
|
|||
generics: [],
|
||||
},
|
||||
],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
},
|
||||
],
|
||||
foundElems: 1,
|
||||
|
|
@ -162,7 +162,7 @@ const PARSED = [
|
|||
],
|
||||
},
|
||||
],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
},
|
||||
],
|
||||
foundElems: 1,
|
||||
|
|
|
|||
|
|
@ -25,11 +25,11 @@ const PARSED = [
|
|||
generics: [],
|
||||
},
|
||||
],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
}],
|
||||
],
|
||||
],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
}],
|
||||
foundElems: 1,
|
||||
userQuery: "(-> F<P>)",
|
||||
|
|
@ -53,11 +53,11 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "p",
|
||||
generics: [],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
}],
|
||||
],
|
||||
],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
}],
|
||||
foundElems: 1,
|
||||
userQuery: "(-> P)",
|
||||
|
|
@ -81,11 +81,11 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "a",
|
||||
generics: [],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
}],
|
||||
],
|
||||
],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
}],
|
||||
foundElems: 1,
|
||||
userQuery: "(->,a)",
|
||||
|
|
@ -113,7 +113,7 @@ const PARSED = [
|
|||
generics: [],
|
||||
},
|
||||
],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
}],
|
||||
bindings: [
|
||||
[
|
||||
|
|
@ -121,7 +121,7 @@ const PARSED = [
|
|||
[],
|
||||
],
|
||||
],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
}],
|
||||
foundElems: 1,
|
||||
userQuery: "(F<P> ->)",
|
||||
|
|
@ -141,7 +141,7 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "p",
|
||||
generics: [],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
}],
|
||||
bindings: [
|
||||
[
|
||||
|
|
@ -149,7 +149,7 @@ const PARSED = [
|
|||
[],
|
||||
],
|
||||
],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
}],
|
||||
foundElems: 1,
|
||||
userQuery: "(P ->)",
|
||||
|
|
@ -169,7 +169,7 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "a",
|
||||
generics: [],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
}],
|
||||
bindings: [
|
||||
[
|
||||
|
|
@ -177,7 +177,7 @@ const PARSED = [
|
|||
[],
|
||||
],
|
||||
],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
}],
|
||||
foundElems: 1,
|
||||
userQuery: "(,a->)",
|
||||
|
|
@ -197,7 +197,7 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "aaaaa",
|
||||
generics: [],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
}],
|
||||
bindings: [
|
||||
[
|
||||
|
|
@ -208,11 +208,11 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "a",
|
||||
generics: [],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
}],
|
||||
],
|
||||
],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
}],
|
||||
foundElems: 1,
|
||||
userQuery: "(aaaaa->a)",
|
||||
|
|
@ -233,7 +233,7 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "aaaaa",
|
||||
generics: [],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
},
|
||||
{
|
||||
name: "b",
|
||||
|
|
@ -241,7 +241,7 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "b",
|
||||
generics: [],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
},
|
||||
],
|
||||
bindings: [
|
||||
|
|
@ -253,11 +253,11 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "a",
|
||||
generics: [],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
}],
|
||||
],
|
||||
],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
}],
|
||||
foundElems: 1,
|
||||
userQuery: "(aaaaa, b -> a)",
|
||||
|
|
@ -278,7 +278,7 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "aaaaa",
|
||||
generics: [],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
},
|
||||
{
|
||||
name: "b",
|
||||
|
|
@ -286,7 +286,7 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "b",
|
||||
generics: [],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
},
|
||||
],
|
||||
bindings: [
|
||||
|
|
@ -298,11 +298,11 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "a",
|
||||
generics: [],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
}],
|
||||
],
|
||||
],
|
||||
typeFilter: 1,
|
||||
typeFilter: "primitive",
|
||||
}],
|
||||
foundElems: 1,
|
||||
userQuery: "primitive:(aaaaa, b -> a)",
|
||||
|
|
@ -318,7 +318,7 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "x",
|
||||
generics: [],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
},
|
||||
{
|
||||
name: "->",
|
||||
|
|
@ -332,7 +332,7 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "aaaaa",
|
||||
generics: [],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
},
|
||||
{
|
||||
name: "b",
|
||||
|
|
@ -340,7 +340,7 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "b",
|
||||
generics: [],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
},
|
||||
],
|
||||
bindings: [
|
||||
|
|
@ -352,11 +352,11 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "a",
|
||||
generics: [],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
}],
|
||||
],
|
||||
],
|
||||
typeFilter: 10,
|
||||
typeFilter: "trait",
|
||||
}
|
||||
],
|
||||
foundElems: 2,
|
||||
|
|
@ -390,11 +390,11 @@ const PARSED = [
|
|||
generics: [],
|
||||
},
|
||||
],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
}],
|
||||
],
|
||||
],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
}],
|
||||
foundElems: 1,
|
||||
userQuery: "Fn () -> F<P>",
|
||||
|
|
@ -418,11 +418,11 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "p",
|
||||
generics: [],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
}],
|
||||
],
|
||||
],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
}],
|
||||
foundElems: 1,
|
||||
userQuery: "FnMut() -> P",
|
||||
|
|
@ -446,11 +446,11 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "p",
|
||||
generics: [],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
}],
|
||||
],
|
||||
],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
}],
|
||||
foundElems: 1,
|
||||
userQuery: "(FnMut() -> P)",
|
||||
|
|
@ -478,7 +478,7 @@ const PARSED = [
|
|||
generics: [],
|
||||
},
|
||||
],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
}],
|
||||
bindings: [
|
||||
[
|
||||
|
|
@ -486,7 +486,7 @@ const PARSED = [
|
|||
[],
|
||||
],
|
||||
],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
}],
|
||||
foundElems: 1,
|
||||
userQuery: "Fn(F<P>)",
|
||||
|
|
@ -507,7 +507,7 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "aaaaa",
|
||||
generics: [],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
},
|
||||
{
|
||||
name: "b",
|
||||
|
|
@ -515,7 +515,7 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "b",
|
||||
generics: [],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
},
|
||||
],
|
||||
bindings: [
|
||||
|
|
@ -527,11 +527,11 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "a",
|
||||
generics: [],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
}],
|
||||
],
|
||||
],
|
||||
typeFilter: 1,
|
||||
typeFilter: "primitive",
|
||||
}],
|
||||
foundElems: 1,
|
||||
userQuery: "primitive:fnonce(aaaaa, b) -> a",
|
||||
|
|
@ -552,7 +552,7 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "aaaaa",
|
||||
generics: [],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
},
|
||||
{
|
||||
name: "b",
|
||||
|
|
@ -560,7 +560,7 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "b",
|
||||
generics: [],
|
||||
typeFilter: 0,
|
||||
typeFilter: "keyword",
|
||||
},
|
||||
],
|
||||
bindings: [
|
||||
|
|
@ -572,11 +572,11 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "a",
|
||||
generics: [],
|
||||
typeFilter: 10,
|
||||
typeFilter: "trait",
|
||||
}],
|
||||
],
|
||||
],
|
||||
typeFilter: 1,
|
||||
typeFilter: "primitive",
|
||||
}],
|
||||
foundElems: 1,
|
||||
userQuery: "primitive:fnonce(aaaaa, keyword:b) -> trait:a",
|
||||
|
|
@ -592,7 +592,7 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "x",
|
||||
generics: [],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
},
|
||||
{
|
||||
name: "fn",
|
||||
|
|
@ -612,7 +612,7 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "aaaaa",
|
||||
generics: [],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
},
|
||||
{
|
||||
name: "b",
|
||||
|
|
@ -620,7 +620,7 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "b",
|
||||
generics: [],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
},
|
||||
],
|
||||
bindings: [
|
||||
|
|
@ -632,11 +632,11 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "a",
|
||||
generics: [],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
}],
|
||||
],
|
||||
],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
},
|
||||
],
|
||||
bindings: [
|
||||
|
|
@ -645,7 +645,7 @@ const PARSED = [
|
|||
[],
|
||||
]
|
||||
],
|
||||
typeFilter: 10,
|
||||
typeFilter: "trait",
|
||||
}
|
||||
],
|
||||
foundElems: 2,
|
||||
|
|
@ -662,7 +662,7 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "a",
|
||||
generics: [],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
},
|
||||
{
|
||||
name: "b",
|
||||
|
|
@ -675,7 +675,7 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "c",
|
||||
generics: [],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
}],
|
||||
bindings: [
|
||||
[
|
||||
|
|
@ -683,7 +683,7 @@ const PARSED = [
|
|||
[],
|
||||
]
|
||||
],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
}
|
||||
],
|
||||
foundElems: 2,
|
||||
|
|
|
|||
|
|
@ -13,10 +13,10 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "never",
|
||||
generics: [],
|
||||
typeFilter: 1,
|
||||
typeFilter: "primitive",
|
||||
},
|
||||
],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
}],
|
||||
foundElems: 1,
|
||||
userQuery: "R<!>",
|
||||
|
|
@ -31,7 +31,7 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "never",
|
||||
generics: [],
|
||||
typeFilter: 1,
|
||||
typeFilter: "primitive",
|
||||
}],
|
||||
foundElems: 1,
|
||||
userQuery: "!",
|
||||
|
|
@ -46,7 +46,7 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "a",
|
||||
generics: [],
|
||||
typeFilter: 16,
|
||||
typeFilter: "macro",
|
||||
}],
|
||||
foundElems: 1,
|
||||
userQuery: "a!",
|
||||
|
|
@ -77,7 +77,7 @@ const PARSED = [
|
|||
pathWithoutLast: ["never"],
|
||||
pathLast: "b",
|
||||
generics: [],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
}],
|
||||
foundElems: 1,
|
||||
userQuery: "!::b",
|
||||
|
|
@ -122,10 +122,10 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "t",
|
||||
generics: [],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
}
|
||||
],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
}],
|
||||
foundElems: 1,
|
||||
userQuery: "!::b<T>",
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ const PARSED = [
|
|||
generics: [],
|
||||
},
|
||||
],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
}],
|
||||
foundElems: 1,
|
||||
userQuery: "R<P>",
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ const PARSED = [
|
|||
pathWithoutLast: ["a"],
|
||||
pathLast: "b",
|
||||
generics: [],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
}],
|
||||
foundElems: 1,
|
||||
userQuery: "A::B",
|
||||
|
|
@ -22,7 +22,7 @@ const PARSED = [
|
|||
pathWithoutLast: ["a"],
|
||||
pathLast: "a",
|
||||
generics: [],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
}],
|
||||
foundElems: 1,
|
||||
userQuery: 'a:: a',
|
||||
|
|
@ -37,7 +37,7 @@ const PARSED = [
|
|||
pathWithoutLast: ["a"],
|
||||
pathLast: "a",
|
||||
generics: [],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
}],
|
||||
foundElems: 1,
|
||||
userQuery: 'a ::a',
|
||||
|
|
@ -52,7 +52,7 @@ const PARSED = [
|
|||
pathWithoutLast: ["a"],
|
||||
pathLast: "a",
|
||||
generics: [],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
}],
|
||||
foundElems: 1,
|
||||
userQuery: 'a :: a',
|
||||
|
|
@ -68,7 +68,7 @@ const PARSED = [
|
|||
pathWithoutLast: ["a"],
|
||||
pathLast: "b",
|
||||
generics: [],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
},
|
||||
{
|
||||
name: "C",
|
||||
|
|
@ -76,7 +76,7 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "c",
|
||||
generics: [],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
},
|
||||
],
|
||||
foundElems: 2,
|
||||
|
|
@ -101,7 +101,7 @@ const PARSED = [
|
|||
generics: [],
|
||||
},
|
||||
],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
},
|
||||
{
|
||||
name: "C",
|
||||
|
|
@ -109,7 +109,7 @@ const PARSED = [
|
|||
pathWithoutLast: [],
|
||||
pathLast: "c",
|
||||
generics: [],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
},
|
||||
],
|
||||
foundElems: 2,
|
||||
|
|
@ -125,7 +125,7 @@ const PARSED = [
|
|||
pathWithoutLast: ["mod"],
|
||||
pathLast: "a",
|
||||
generics: [],
|
||||
typeFilter: -1,
|
||||
typeFilter: null,
|
||||
}],
|
||||
foundElems: 1,
|
||||
userQuery: "mod::a",
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue