Skip to content

Commit 7db0a69

Browse files
committed
wip
1 parent 4324d85 commit 7db0a69

File tree

3 files changed

+90
-42
lines changed

3 files changed

+90
-42
lines changed

src/librustdoc/html/render/search_index.rs

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use crate::formats::item_type::ItemType;
1818
use crate::html::format::join_with_double_colon;
1919
use crate::html::markdown::short_markdown_summary;
2020
use crate::html::render::{self, IndexItem, IndexItemFunctionType, RenderType, RenderTypeId};
21+
use crate::html::render::sorted_json::SortedJson;
2122

2223
use encode::{bitmap_to_string, write_vlqhex_to_string};
2324

@@ -47,7 +48,7 @@ use encode::{bitmap_to_string, write_vlqhex_to_string};
4748
/// [2]: https://en.wikipedia.org/wiki/Sliding_window_protocol#Basic_concept
4849
/// [3]: https://learn.microsoft.com/en-us/troubleshoot/windows-server/networking/description-tcp-features
4950
pub(crate) struct SerializedSearchIndex {
50-
pub(crate) index: String,
51+
pub(crate) index: SortedJson,
5152
pub(crate) desc: Vec<(usize, String)>,
5253
}
5354

@@ -684,24 +685,19 @@ pub(crate) fn build_index<'tcx>(
684685
// The index, which is actually used to search, is JSON
685686
// It uses `JSON.parse(..)` to actually load, since JSON
686687
// parses faster than the full JavaScript syntax.
687-
let index = format!(
688-
r#"["{}",{}]"#,
689-
krate.name(tcx),
690-
serde_json::to_string(&CrateData {
691-
items: crate_items,
692-
paths: crate_paths,
693-
aliases: &aliases,
694-
associated_item_disambiguators: &associated_item_disambiguators,
695-
desc_index,
696-
empty_desc,
697-
})
698-
.expect("failed serde conversion")
699-
// All these `replace` calls are because we have to go through JS string for JSON content.
700-
.replace('\\', r"\\")
701-
.replace('\'', r"\'")
702-
// We need to escape double quotes for the JSON.
703-
.replace("\\\"", "\\\\\"")
704-
);
688+
let crate_name = krate.name(tcx);
689+
let data = CrateData {
690+
items: crate_items,
691+
paths: crate_paths,
692+
aliases: &aliases,
693+
associated_item_disambiguators: &associated_item_disambiguators,
694+
desc_index,
695+
empty_desc,
696+
};
697+
let index = SortedJson::array_unsorted([
698+
SortedJson::serialize(crate_name.as_str()),
699+
SortedJson::serialize(data),
700+
]);
705701
SerializedSearchIndex { index, desc }
706702
}
707703

src/librustdoc/html/render/sorted_json.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,13 @@ impl SortedJson {
2323
SortedJson(serde_json::to_string(&item).unwrap())
2424
}
2525

26+
/// Assumes that `item` is already JSON encoded
27+
///
28+
/// TODO: remove this, and use SortedJson everywhere JSON is rendered
29+
pub fn preserialized(item: String) -> Self {
30+
SortedJson(item)
31+
}
32+
2633
/// Serializes and sorts
2734
pub fn array<T: Borrow<SortedJson>, I: IntoIterator<Item=T>>(items: I) -> Self {
2835
let items = items.into_iter()

src/librustdoc/html/render/write_shared.rs

Lines changed: 68 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use std::path::{Component, Path, PathBuf};
2323
use std::rc::{Rc, Weak};
2424
use std::ffi::OsString;
2525
use std::collections::hash_map::Entry;
26+
use std::iter::once;
2627

2728
use indexmap::IndexMap;
2829
use itertools::Itertools;
@@ -34,6 +35,7 @@ use rustc_span::def_id::DefId;
3435
use rustc_span::Symbol;
3536
use serde::ser::SerializeSeq;
3637
use serde::{Serialize, Deserialize, de::DeserializeOwned, Serializer};
38+
use regex::Regex;
3739

3840
use super::{collect_paths_for_type, ensure_trailing_slash, Context, RenderMode};
3941
use crate::html::render::search_index::build_index;
@@ -54,6 +56,12 @@ use crate::html::static_files::{self, suffix_path};
5456
use crate::visit::DocVisitor;
5557
use crate::{try_err, try_none};
5658

59+
// TODO
60+
// sort template have diff thing
61+
// weird behavior; extract crate list from search index? or somewhere similar
62+
// string faster loading tetchnique
63+
// run rest of the tests and fix things up
64+
5765
pub(crate) fn write_shared(
5866
cx: &mut Context<'_>,
5967
krate: &Crate,
@@ -64,12 +72,17 @@ pub(crate) fn write_shared(
6472
let crate_name = crate_name.as_str(); // rand
6573
let crate_name_json = SortedJson::serialize(crate_name); // "rand"
6674

67-
// for current crate
75+
if crate_name != "foo" {
76+
panic!("{crate_name} index{} read{} write{}", opt.enable_index_page, opt.read_rendered_cci, opt.write_rendered_cci);
77+
}
78+
79+
let external_crates = hack_get_external_crate_names(cx)?;
80+
6881
let sources = PartsAndLocations::<SourcesPart>::get(cx)?;
69-
let serialized_search_index = build_index(&krate, &mut Rc::get_mut(&mut cx.shared).unwrap().cache, tcx);
70-
let search_index = PartsAndLocations::<SearchIndexPart>::get(cx, &serialized_search_index)?;
82+
let SerializedSearchIndex { index, desc } = build_index(&krate, &mut Rc::get_mut(&mut cx.shared).unwrap().cache, tcx);
83+
let search_index = PartsAndLocations::<SearchIndexPart>::get(cx, index)?;
7184
let all_crates = PartsAndLocations::<AllCratesPart>::get(crate_name_json.clone())?;
72-
let crates_index = PartsAndLocations::<CratesIndexPart>::get(&crate_name)?;
85+
let crates_index = PartsAndLocations::<CratesIndexPart>::get(&crate_name, &external_crates)?;
7386
let trait_aliases = PartsAndLocations::<TraitAliasPart>::get(cx, &crate_name_json)?;
7487
let type_aliases = PartsAndLocations::<TypeAliasPart>::get(cx, krate, &crate_name_json)?;
7588

@@ -90,7 +103,7 @@ pub(crate) fn write_shared(
90103

91104
if opt.write_rendered_cci {
92105
write_static_files(cx, &opt)?;
93-
write_search_desc(cx, &krate, &serialized_search_index)?;
106+
write_search_desc(cx, &krate, &desc)?;
94107
if opt.emit.is_empty() || opt.emit.contains(&EmitType::InvocationSpecific) {
95108
if cx.include_sources {
96109
write_rendered_cci(cx, opt.read_rendered_cci, &opt.parts_paths, &sources)?;
@@ -165,14 +178,14 @@ fn write_static_files(
165178
}
166179

167180
/// Write the search description shards to disk
168-
fn write_search_desc(cx: &Context<'_>, krate: &Crate, search_index: &SerializedSearchIndex) -> Result<(), Error> {
181+
fn write_search_desc(cx: &mut Context<'_>, krate: &Crate, search_desc: &[(usize, String)]) -> Result<(), Error> {
169182
let crate_name = krate.name(cx.tcx()).to_string();
170183
let encoded_crate_name = SortedJson::serialize(&crate_name);
171184
let path = PathBuf::from_iter([&cx.dst, Path::new("search.desc"), Path::new(&crate_name)]);
172185
if Path::new(&path).exists() {
173186
try_err!(fs::remove_dir_all(&path), &path);
174187
}
175-
for (i, (_, part)) in search_index.desc.iter().enumerate() {
188+
for (i, (_, part)) in search_desc.iter().enumerate() {
176189
let filename = static_files::suffix_path(
177190
&format!("{crate_name}-desc-{i}-.js"),
178191
&cx.shared.resource_suffix,
@@ -212,7 +225,7 @@ pub(crate) trait NamedPart: Sized {
212225
}
213226

214227
/// Paths (relative to `doc/`) and their pre-merge contents
215-
#[derive(Serialize, Deserialize)]
228+
#[derive(Serialize, Deserialize, Debug, Clone)]
216229
#[serde(transparent)]
217230
struct PartsAndLocations<P> {
218231
parts: Vec<(PathBuf, P)>,
@@ -239,7 +252,7 @@ impl<T, U> PartsAndLocations<Part<T, U>> {
239252
impl<T, U: Serialize> PartsAndLocations<Part<T, U>>
240253
where Part<T, U>: NamedPart,
241254
{
242-
fn write(&self, cx: &Context<'_>, parts_path: &PathToParts) -> Result<(), Error> {
255+
fn write(&self, cx: &mut Context<'_>, parts_path: &PathToParts) -> Result<(), Error> {
243256
let name = ExternalCrate::LOCAL.name(cx.tcx());
244257
let path = parts_path.cci_path::<Part<T, U>>(name.as_str());
245258
write_create_parents(cx, path, serde_json::to_string(self).unwrap())?;
@@ -260,9 +273,9 @@ else if (window.initSearch) window.initSearch(searchIndex);")
260273
}
261274
}
262275
impl PartsAndLocations<SearchIndexPart> {
263-
fn get(cx: &Context<'_>, search_index: &SerializedSearchIndex) -> Result<Self, Error> {
276+
fn get(cx: &Context<'_>, search_index: SortedJson) -> Result<Self, Error> {
264277
let path = suffix_path("search-index.js", &cx.shared.resource_suffix);
265-
Ok(Self::with(path, SortedJson::serialize(&search_index.index)))
278+
Ok(Self::with(path, search_index))
266279
}
267280
}
268281

@@ -282,6 +295,24 @@ impl PartsAndLocations<AllCratesPart> {
282295
Ok(Self::with(path, crate_name_json))
283296
}
284297
}
298+
/// Reads `crates.js`, which seems like the best
299+
/// place to obtain the list of externally documented crates if the index
300+
/// page was disabled when documenting the deps
301+
fn hack_get_external_crate_names(cx: &Context<'_>) -> Result<Vec<String>, Error> {
302+
let path = cx.dst.join("crates.js");
303+
let Ok(content) = fs::read_to_string(&path) else {
304+
// they didn't emit invocation specific, so we just say there were no crates
305+
return Ok(Vec::default())
306+
};
307+
// this is run only one once so it's fine not to cache it
308+
// dot_matches_new_line false: all crates on same line. greedy: match last bracket
309+
let regex = Regex::new(r"\[.*\]").unwrap();
310+
let Some(content) = regex.find(&content) else {
311+
return Err(Error::new("could not find crates list in crates.js", path));
312+
};
313+
let content: Vec<String> = try_err!(serde_json::from_str(content.as_str()), &path);
314+
Ok(content)
315+
}
285316

286317
#[derive(Serialize, Deserialize, Clone, Default, Debug)]
287318
struct CratesIndex;
@@ -314,13 +345,18 @@ impl NamedPart for CratesIndexPart {
314345
}
315346
}
316347
impl PartsAndLocations<CratesIndexPart> {
317-
fn get(crate_name: &str) -> Result<Self, Error> {
348+
/// Might return parts that are duplicate with ones in prexisting index.html
349+
fn get(crate_name: &str, external_crates: &[String]) -> Result<Self, Error> {
350+
let mut ret = Self::default();
318351
let path = PathBuf::from("index.html");
319-
let part = format!(
320-
"<li><a href=\"{trailing_slash}index.html\">{crate_name}</a></li>",
321-
trailing_slash = ensure_trailing_slash(crate_name),
322-
);
323-
Ok(Self::with(path, part))
352+
for crate_name in external_crates.iter().map(|s| s.as_str()).chain(once(crate_name)) {
353+
let part = format!(
354+
"<li><a href=\"{trailing_slash}index.html\">{crate_name}</a></li>",
355+
trailing_slash = ensure_trailing_slash(crate_name),
356+
);
357+
ret.push(path.clone(), part);
358+
}
359+
Ok(ret)
324360
}
325361
}
326362

@@ -784,19 +820,25 @@ impl Serialize for AliasSerializableImpl {
784820
}
785821
}
786822

787-
fn write_create_parents(cx: &Context<'_>, path: PathBuf, content: String) -> Result<(), Error> {
823+
fn create_parents(cx: &mut Context<'_>, path: &Path) -> Result<(), Error> {
788824
let parent = path.parent().expect("trying to write to an empty path");
825+
// TODO: check cache for whether this directory has already been created
789826
try_err!(cx.shared.fs.create_dir_all(parent), parent);
827+
Ok(())
828+
}
829+
830+
fn write_create_parents(cx: &mut Context<'_>, path: PathBuf, content: String) -> Result<(), Error> {
831+
create_parents(cx, &path)?;
790832
cx.shared.fs.write(path, content)?;
791833
Ok(())
792834
}
793835

794-
fn write_rendered_cci<T: NamedPart + DeserializeOwned + fmt::Display>(
795-
cx: &Context<'_>,
836+
fn write_rendered_cci<T: NamedPart + DeserializeOwned + fmt::Display + fmt::Debug>(
837+
cx: &mut Context<'_>,
796838
read_rendered_cci: bool,
797839
parts_paths: &FxHashMap<String, PathToParts>,
798840
our_parts_and_locations: &PartsAndLocations<T>,
799-
) -> Result<(), Error> {
841+
) -> Result<(), Error> where <T as NamedPart>::FileFormat: std::fmt::Debug {
800842
// read parts from disk
801843
let path_parts = parts_paths.iter()
802844
.map(|(crate_name, parts_path)| {
@@ -810,11 +852,12 @@ fn write_rendered_cci<T: NamedPart + DeserializeOwned + fmt::Display>(
810852
.map(|parts_and_locations| parts_and_locations.parts.iter())
811853
.flatten()
812854
.chain(our_parts_and_locations.parts.iter());
813-
// read previous cci from storage, append to them
855+
// read previous rendered cci from storage, append to them
814856
let mut templates: FxHashMap<PathBuf, OffsetTemplate<T::FileFormat>> = Default::default();
815857
for (path, part) in path_parts {
816858
let part = format!("{part}");
817-
match templates.entry(cx.dst.join(&path)) {
859+
let path = cx.dst.join(&path);
860+
match templates.entry(path.clone()) {
818861
Entry::Vacant(entry) => {
819862
let template = entry.insert(if read_rendered_cci {
820863
match fs::read_to_string(&path) {
@@ -832,6 +875,8 @@ fn write_rendered_cci<T: NamedPart + DeserializeOwned + fmt::Display>(
832875
}
833876
// write the merged cci to disk
834877
for (path, template) in templates {
878+
dbg!(&path, &template, &cx.dst);
879+
create_parents(cx, &path)?;
835880
let file = try_err!(File::create(&path), &path);
836881
let mut file = BufWriter::new(file);
837882
try_err!(write!(file, "{template}"), &path);

0 commit comments

Comments
 (0)