Add links on source types to go to definition
This commit is contained in:
parent
2f07ae408f
commit
023231a709
10 changed files with 454 additions and 106 deletions
|
@ -1950,6 +1950,11 @@ impl Span {
|
||||||
Self(sp.source_callsite())
|
Self(sp.source_callsite())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Unless you know what you're doing, use [`Self::from_rustc_span`] instead!
|
||||||
|
crate fn wrap(sp: rustc_span::Span) -> Span {
|
||||||
|
Self(sp)
|
||||||
|
}
|
||||||
|
|
||||||
crate fn inner(&self) -> rustc_span::Span {
|
crate fn inner(&self) -> rustc_span::Span {
|
||||||
self.0
|
self.0
|
||||||
}
|
}
|
||||||
|
|
|
@ -494,7 +494,10 @@ crate fn href(did: DefId, cx: &Context<'_>) -> Result<(String, ItemType, Vec<Str
|
||||||
if !did.is_local() && !cache.access_levels.is_public(did) && !cache.document_private {
|
if !did.is_local() && !cache.access_levels.is_public(did) && !cache.document_private {
|
||||||
return Err(HrefError::Private);
|
return Err(HrefError::Private);
|
||||||
}
|
}
|
||||||
|
// href_with_depth_inner(did, cache, || {
|
||||||
|
// let depth = CURRENT_DEPTH.with(|l| l.get());
|
||||||
|
// "../".repeat(depth)
|
||||||
|
// })
|
||||||
let (fqp, shortty, mut url_parts) = match cache.paths.get(&did) {
|
let (fqp, shortty, mut url_parts) = match cache.paths.get(&did) {
|
||||||
Some(&(ref fqp, shortty)) => (fqp, shortty, {
|
Some(&(ref fqp, shortty)) => (fqp, shortty, {
|
||||||
let module_fqp = to_module_fqp(shortty, fqp);
|
let module_fqp = to_module_fqp(shortty, fqp);
|
||||||
|
|
|
@ -5,16 +5,19 @@
|
||||||
//!
|
//!
|
||||||
//! Use the `render_with_highlighting` to highlight some rust code.
|
//! Use the `render_with_highlighting` to highlight some rust code.
|
||||||
|
|
||||||
|
use crate::clean;
|
||||||
use crate::html::escape::Escape;
|
use crate::html::escape::Escape;
|
||||||
|
use crate::html::render::Context;
|
||||||
|
|
||||||
use std::fmt::Display;
|
use std::fmt::{Display, Write};
|
||||||
use std::iter::Peekable;
|
use std::iter::Peekable;
|
||||||
|
|
||||||
use rustc_lexer::{LiteralKind, TokenKind};
|
use rustc_lexer::{LiteralKind, TokenKind};
|
||||||
use rustc_span::edition::Edition;
|
use rustc_span::edition::Edition;
|
||||||
use rustc_span::symbol::Symbol;
|
use rustc_span::symbol::Symbol;
|
||||||
|
|
||||||
use super::format::Buffer;
|
use super::format::{self, Buffer};
|
||||||
|
use super::render::LinkFromSrc;
|
||||||
|
|
||||||
/// Highlights `src`, returning the HTML output.
|
/// Highlights `src`, returning the HTML output.
|
||||||
crate fn render_with_highlighting(
|
crate fn render_with_highlighting(
|
||||||
|
@ -25,6 +28,9 @@ crate fn render_with_highlighting(
|
||||||
tooltip: Option<(Option<Edition>, &str)>,
|
tooltip: Option<(Option<Edition>, &str)>,
|
||||||
edition: Edition,
|
edition: Edition,
|
||||||
extra_content: Option<Buffer>,
|
extra_content: Option<Buffer>,
|
||||||
|
file_span_lo: u32,
|
||||||
|
context: Option<&Context<'_>>,
|
||||||
|
root_path: &str,
|
||||||
) {
|
) {
|
||||||
debug!("highlighting: ================\n{}\n==============", src);
|
debug!("highlighting: ================\n{}\n==============", src);
|
||||||
if let Some((edition_info, class)) = tooltip {
|
if let Some((edition_info, class)) = tooltip {
|
||||||
|
@ -41,7 +47,7 @@ crate fn render_with_highlighting(
|
||||||
}
|
}
|
||||||
|
|
||||||
write_header(out, class, extra_content);
|
write_header(out, class, extra_content);
|
||||||
write_code(out, &src, edition);
|
write_code(out, &src, edition, file_span_lo, context, root_path);
|
||||||
write_footer(out, playground_button);
|
write_footer(out, playground_button);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,12 +63,21 @@ fn write_header(out: &mut Buffer, class: Option<&str>, extra_content: Option<Buf
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_code(out: &mut Buffer, src: &str, edition: Edition) {
|
fn write_code(
|
||||||
|
out: &mut Buffer,
|
||||||
|
src: &str,
|
||||||
|
edition: Edition,
|
||||||
|
file_span_lo: u32,
|
||||||
|
context: Option<&Context<'_>>,
|
||||||
|
root_path: &str,
|
||||||
|
) {
|
||||||
// This replace allows to fix how the code source with DOS backline characters is displayed.
|
// This replace allows to fix how the code source with DOS backline characters is displayed.
|
||||||
let src = src.replace("\r\n", "\n");
|
let src = src.replace("\r\n", "\n");
|
||||||
Classifier::new(&src, edition).highlight(&mut |highlight| {
|
Classifier::new(&src, edition, file_span_lo).highlight(&mut |highlight| {
|
||||||
match highlight {
|
match highlight {
|
||||||
Highlight::Token { text, class } => string(out, Escape(text), class),
|
Highlight::Token { text, class } => {
|
||||||
|
string(out, Escape(text), class, context, root_path)
|
||||||
|
}
|
||||||
Highlight::EnterSpan { class } => enter_span(out, class),
|
Highlight::EnterSpan { class } => enter_span(out, class),
|
||||||
Highlight::ExitSpan => exit_span(out),
|
Highlight::ExitSpan => exit_span(out),
|
||||||
};
|
};
|
||||||
|
@ -82,14 +97,14 @@ enum Class {
|
||||||
KeyWord,
|
KeyWord,
|
||||||
// Keywords that do pointer/reference stuff.
|
// Keywords that do pointer/reference stuff.
|
||||||
RefKeyWord,
|
RefKeyWord,
|
||||||
Self_,
|
Self_((u32, u32)),
|
||||||
Op,
|
Op,
|
||||||
Macro,
|
Macro,
|
||||||
MacroNonTerminal,
|
MacroNonTerminal,
|
||||||
String,
|
String,
|
||||||
Number,
|
Number,
|
||||||
Bool,
|
Bool,
|
||||||
Ident,
|
Ident((u32, u32)),
|
||||||
Lifetime,
|
Lifetime,
|
||||||
PreludeTy,
|
PreludeTy,
|
||||||
PreludeVal,
|
PreludeVal,
|
||||||
|
@ -105,20 +120,27 @@ impl Class {
|
||||||
Class::Attribute => "attribute",
|
Class::Attribute => "attribute",
|
||||||
Class::KeyWord => "kw",
|
Class::KeyWord => "kw",
|
||||||
Class::RefKeyWord => "kw-2",
|
Class::RefKeyWord => "kw-2",
|
||||||
Class::Self_ => "self",
|
Class::Self_(_) => "self",
|
||||||
Class::Op => "op",
|
Class::Op => "op",
|
||||||
Class::Macro => "macro",
|
Class::Macro => "macro",
|
||||||
Class::MacroNonTerminal => "macro-nonterminal",
|
Class::MacroNonTerminal => "macro-nonterminal",
|
||||||
Class::String => "string",
|
Class::String => "string",
|
||||||
Class::Number => "number",
|
Class::Number => "number",
|
||||||
Class::Bool => "bool-val",
|
Class::Bool => "bool-val",
|
||||||
Class::Ident => "ident",
|
Class::Ident(_) => "ident",
|
||||||
Class::Lifetime => "lifetime",
|
Class::Lifetime => "lifetime",
|
||||||
Class::PreludeTy => "prelude-ty",
|
Class::PreludeTy => "prelude-ty",
|
||||||
Class::PreludeVal => "prelude-val",
|
Class::PreludeVal => "prelude-val",
|
||||||
Class::QuestionMark => "question-mark",
|
Class::QuestionMark => "question-mark",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_span(self) -> Option<(u32, u32)> {
|
||||||
|
match self {
|
||||||
|
Self::Ident(sp) | Self::Self_(sp) => Some(sp),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Highlight<'a> {
|
enum Highlight<'a> {
|
||||||
|
@ -144,14 +166,23 @@ impl Iterator for TokenIter<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_real_ident_class(text: &str, edition: Edition) -> Class {
|
/// Returns `None` if this is a `Class::Ident`.
|
||||||
match text {
|
fn get_real_ident_class(text: &str, edition: Edition, allow_path_keywords: bool) -> Option<Class> {
|
||||||
|
let ignore: &[&str] =
|
||||||
|
if allow_path_keywords { &["self", "Self", "super", "crate"] } else { &["self", "Self"] };
|
||||||
|
if ignore.iter().any(|k| *k == text) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(match text {
|
||||||
"ref" | "mut" => Class::RefKeyWord,
|
"ref" | "mut" => Class::RefKeyWord,
|
||||||
"self" | "Self" => Class::Self_,
|
|
||||||
"false" | "true" => Class::Bool,
|
"false" | "true" => Class::Bool,
|
||||||
_ if Symbol::intern(text).is_reserved(|| edition) => Class::KeyWord,
|
_ if Symbol::intern(text).is_reserved(|| edition) => Class::KeyWord,
|
||||||
_ => Class::Ident,
|
_ => return None,
|
||||||
}
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn move_span(file_span_lo: u32, start: u32, end: u32) -> (u32, u32) {
|
||||||
|
(start + file_span_lo, end + file_span_lo)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Processes program tokens, classifying strings of text by highlighting
|
/// Processes program tokens, classifying strings of text by highlighting
|
||||||
|
@ -163,11 +194,12 @@ struct Classifier<'a> {
|
||||||
in_macro_nonterminal: bool,
|
in_macro_nonterminal: bool,
|
||||||
edition: Edition,
|
edition: Edition,
|
||||||
byte_pos: u32,
|
byte_pos: u32,
|
||||||
|
file_span_lo: u32,
|
||||||
src: &'a str,
|
src: &'a str,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Classifier<'a> {
|
impl<'a> Classifier<'a> {
|
||||||
fn new(src: &str, edition: Edition) -> Classifier<'_> {
|
fn new(src: &str, edition: Edition, file_span_lo: u32) -> Classifier<'_> {
|
||||||
let tokens = TokenIter { src }.peekable();
|
let tokens = TokenIter { src }.peekable();
|
||||||
Classifier {
|
Classifier {
|
||||||
tokens,
|
tokens,
|
||||||
|
@ -176,6 +208,7 @@ impl<'a> Classifier<'a> {
|
||||||
in_macro_nonterminal: false,
|
in_macro_nonterminal: false,
|
||||||
edition,
|
edition,
|
||||||
byte_pos: 0,
|
byte_pos: 0,
|
||||||
|
file_span_lo,
|
||||||
src,
|
src,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -201,17 +234,17 @@ impl<'a> Classifier<'a> {
|
||||||
if has_ident {
|
if has_ident {
|
||||||
return vec![(TokenKind::Ident, start, pos), (TokenKind::Colon, pos, pos + nb)];
|
return vec![(TokenKind::Ident, start, pos), (TokenKind::Colon, pos, pos + nb)];
|
||||||
} else {
|
} else {
|
||||||
return vec![(TokenKind::Colon, pos, pos + nb)];
|
return vec![(TokenKind::Colon, start, pos + nb)];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some((Class::Ident, text)) = self.tokens.peek().map(|(token, text)| {
|
if let Some((None, text)) = self.tokens.peek().map(|(token, text)| {
|
||||||
if *token == TokenKind::Ident {
|
if *token == TokenKind::Ident {
|
||||||
let class = get_real_ident_class(text, edition);
|
let class = get_real_ident_class(text, edition, true);
|
||||||
(class, text)
|
(class, text)
|
||||||
} else {
|
} else {
|
||||||
// Doesn't matter which Class we put in here...
|
// Doesn't matter which Class we put in here...
|
||||||
(Class::Comment, text)
|
(Some(Class::Comment), text)
|
||||||
}
|
}
|
||||||
}) {
|
}) {
|
||||||
// We only "add" the colon if there is an ident behind.
|
// We only "add" the colon if there is an ident behind.
|
||||||
|
@ -221,7 +254,7 @@ impl<'a> Classifier<'a> {
|
||||||
} else if nb > 0 && has_ident {
|
} else if nb > 0 && has_ident {
|
||||||
return vec![(TokenKind::Ident, start, pos), (TokenKind::Colon, pos, pos + nb)];
|
return vec![(TokenKind::Ident, start, pos), (TokenKind::Colon, pos, pos + nb)];
|
||||||
} else if nb > 0 {
|
} else if nb > 0 {
|
||||||
return vec![(TokenKind::Colon, pos, pos + nb)];
|
return vec![(TokenKind::Colon, start, start + nb)];
|
||||||
} else if has_ident {
|
} else if has_ident {
|
||||||
return vec![(TokenKind::Ident, start, pos)];
|
return vec![(TokenKind::Ident, start, pos)];
|
||||||
} else {
|
} else {
|
||||||
|
@ -231,10 +264,11 @@ impl<'a> Classifier<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wraps the tokens iteration to ensure that the byte_pos is always correct.
|
/// Wraps the tokens iteration to ensure that the byte_pos is always correct.
|
||||||
fn next(&mut self) -> Option<(TokenKind, &'a str)> {
|
fn next(&mut self) -> Option<(TokenKind, &'a str, u32)> {
|
||||||
if let Some((kind, text)) = self.tokens.next() {
|
if let Some((kind, text)) = self.tokens.next() {
|
||||||
|
let before = self.byte_pos;
|
||||||
self.byte_pos += text.len() as u32;
|
self.byte_pos += text.len() as u32;
|
||||||
Some((kind, text))
|
Some((kind, text, before))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -254,14 +288,18 @@ impl<'a> Classifier<'a> {
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
{
|
{
|
||||||
let tokens = self.get_full_ident_path();
|
let tokens = self.get_full_ident_path();
|
||||||
|
let skip = !tokens.is_empty();
|
||||||
for (token, start, end) in tokens {
|
for (token, start, end) in tokens {
|
||||||
let text = &self.src[start..end];
|
let text = &self.src[start..end];
|
||||||
self.advance(token, text, sink);
|
self.advance(token, text, sink, start as u32);
|
||||||
self.byte_pos += text.len() as u32;
|
self.byte_pos += text.len() as u32;
|
||||||
}
|
}
|
||||||
|
if skip {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if let Some((token, text)) = self.next() {
|
if let Some((token, text, before)) = self.next() {
|
||||||
self.advance(token, text, sink);
|
self.advance(token, text, sink, before);
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -270,7 +308,13 @@ impl<'a> Classifier<'a> {
|
||||||
|
|
||||||
/// Single step of highlighting. This will classify `token`, but maybe also
|
/// Single step of highlighting. This will classify `token`, but maybe also
|
||||||
/// a couple of following ones as well.
|
/// a couple of following ones as well.
|
||||||
fn advance(&mut self, token: TokenKind, text: &'a str, sink: &mut dyn FnMut(Highlight<'a>)) {
|
fn advance(
|
||||||
|
&mut self,
|
||||||
|
token: TokenKind,
|
||||||
|
text: &'a str,
|
||||||
|
sink: &mut dyn FnMut(Highlight<'a>),
|
||||||
|
before: u32,
|
||||||
|
) {
|
||||||
let lookahead = self.peek();
|
let lookahead = self.peek();
|
||||||
let no_highlight = |sink: &mut dyn FnMut(_)| sink(Highlight::Token { text, class: None });
|
let no_highlight = |sink: &mut dyn FnMut(_)| sink(Highlight::Token { text, class: None });
|
||||||
let class = match token {
|
let class = match token {
|
||||||
|
@ -401,19 +445,30 @@ impl<'a> Classifier<'a> {
|
||||||
sink(Highlight::Token { text, class: None });
|
sink(Highlight::Token { text, class: None });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
TokenKind::Ident => match get_real_ident_class(text, self.edition) {
|
TokenKind::Ident => match get_real_ident_class(text, self.edition, false) {
|
||||||
Class::Ident => match text {
|
None => match text {
|
||||||
"Option" | "Result" => Class::PreludeTy,
|
"Option" | "Result" => Class::PreludeTy,
|
||||||
"Some" | "None" | "Ok" | "Err" => Class::PreludeVal,
|
"Some" | "None" | "Ok" | "Err" => Class::PreludeVal,
|
||||||
_ if self.in_macro_nonterminal => {
|
_ if self.in_macro_nonterminal => {
|
||||||
self.in_macro_nonterminal = false;
|
self.in_macro_nonterminal = false;
|
||||||
Class::MacroNonTerminal
|
Class::MacroNonTerminal
|
||||||
}
|
}
|
||||||
_ => Class::Ident,
|
"self" | "Self" => Class::Self_(move_span(
|
||||||
|
self.file_span_lo,
|
||||||
|
before,
|
||||||
|
before + text.len() as u32,
|
||||||
|
)),
|
||||||
|
_ => Class::Ident(move_span(
|
||||||
|
self.file_span_lo,
|
||||||
|
before,
|
||||||
|
before + text.len() as u32,
|
||||||
|
)),
|
||||||
},
|
},
|
||||||
c => c,
|
Some(c) => c,
|
||||||
},
|
},
|
||||||
TokenKind::RawIdent | TokenKind::UnknownPrefix => Class::Ident,
|
TokenKind::RawIdent | TokenKind::UnknownPrefix => {
|
||||||
|
Class::Ident(move_span(self.file_span_lo, before, before + text.len() as u32))
|
||||||
|
}
|
||||||
TokenKind::Lifetime { .. } => Class::Lifetime,
|
TokenKind::Lifetime { .. } => Class::Lifetime,
|
||||||
};
|
};
|
||||||
// Anything that didn't return above is the simple case where we the
|
// Anything that didn't return above is the simple case where we the
|
||||||
|
@ -448,11 +503,74 @@ fn exit_span(out: &mut Buffer) {
|
||||||
/// ```
|
/// ```
|
||||||
/// The latter can be thought of as a shorthand for the former, which is more
|
/// The latter can be thought of as a shorthand for the former, which is more
|
||||||
/// flexible.
|
/// flexible.
|
||||||
fn string<T: Display>(out: &mut Buffer, text: T, klass: Option<Class>) {
|
fn string<T: Display>(
|
||||||
|
out: &mut Buffer,
|
||||||
|
text: T,
|
||||||
|
klass: Option<Class>,
|
||||||
|
context: Option<&Context<'_>>,
|
||||||
|
root_path: &str,
|
||||||
|
) {
|
||||||
match klass {
|
match klass {
|
||||||
None => write!(out, "{}", text),
|
None => write!(out, "{}", text),
|
||||||
Some(klass) => write!(out, "<span class=\"{}\">{}</span>", klass.as_html(), text),
|
Some(klass) => {
|
||||||
|
if let Some(def_span) = klass.get_span() {
|
||||||
|
let mut text = text.to_string();
|
||||||
|
if text.contains("::") {
|
||||||
|
text =
|
||||||
|
text.split("::").enumerate().fold(String::new(), |mut path, (pos, t)| {
|
||||||
|
let pre = if pos != 0 { "::" } else { "" };
|
||||||
|
match t {
|
||||||
|
"self" | "Self" => write!(
|
||||||
|
&mut path,
|
||||||
|
"{}<span class=\"{}\">{}</span>",
|
||||||
|
pre,
|
||||||
|
Class::Self_((0, 0)).as_html(),
|
||||||
|
t
|
||||||
|
),
|
||||||
|
"crate" | "super" => write!(
|
||||||
|
&mut path,
|
||||||
|
"{}<span class=\"{}\">{}</span>",
|
||||||
|
pre,
|
||||||
|
Class::KeyWord.as_html(),
|
||||||
|
t
|
||||||
|
),
|
||||||
|
t => write!(&mut path, "{}{}", pre, t),
|
||||||
|
}
|
||||||
|
.expect("Failed to build source HTML path");
|
||||||
|
path
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if let Some(context) = context {
|
||||||
|
if let Some(href) =
|
||||||
|
context.shared.span_correspondance_map.get(&def_span).and_then(|href| {
|
||||||
|
match href {
|
||||||
|
LinkFromSrc::Local(span) => {
|
||||||
|
eprintln!("==> {:?}:{:?}", span.lo(), span.hi());
|
||||||
|
context
|
||||||
|
.href_from_span(clean::Span::wrap(*span))
|
||||||
|
.map(|s| format!("{}{}", root_path, s))
|
||||||
|
},
|
||||||
|
LinkFromSrc::External(def_id) => {
|
||||||
|
format::href(*def_id, context).map(|(url, _, _)| url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
{
|
||||||
|
write!(
|
||||||
|
out,
|
||||||
|
"<a class=\"{}\" href=\"{}\">{}</a>",
|
||||||
|
klass.as_html(),
|
||||||
|
href,
|
||||||
|
text
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
write!(out, "<span class=\"{}\">{}</span>", klass.as_html(), text);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
write!(out, "<span class=\"{}\">{}</span>", klass.as_html(), text);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -330,6 +330,9 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> {
|
||||||
tooltip,
|
tooltip,
|
||||||
edition,
|
edition,
|
||||||
None,
|
None,
|
||||||
|
0,
|
||||||
|
None,
|
||||||
|
"",
|
||||||
);
|
);
|
||||||
Some(Event::Html(s.into_inner().into()))
|
Some(Event::Html(s.into_inner().into()))
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,10 @@ use rustc_span::symbol::sym;
|
||||||
use super::cache::{build_index, ExternalLocation};
|
use super::cache::{build_index, ExternalLocation};
|
||||||
use super::print_item::{full_path, item_path, print_item};
|
use super::print_item::{full_path, item_path, print_item};
|
||||||
use super::write_shared::write_shared;
|
use super::write_shared::write_shared;
|
||||||
use super::{print_sidebar, settings, AllTypes, NameDoc, StylePath, BASIC_KEYWORDS};
|
use super::{
|
||||||
|
collect_spans_and_sources, print_sidebar, settings, AllTypes, LinkFromSrc, NameDoc, StylePath,
|
||||||
|
BASIC_KEYWORDS,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::clean;
|
use crate::clean;
|
||||||
use crate::clean::ExternalCrate;
|
use crate::clean::ExternalCrate;
|
||||||
|
@ -46,7 +49,7 @@ crate struct Context<'tcx> {
|
||||||
pub(crate) current: Vec<String>,
|
pub(crate) current: Vec<String>,
|
||||||
/// The current destination folder of where HTML artifacts should be placed.
|
/// The current destination folder of where HTML artifacts should be placed.
|
||||||
/// This changes as the context descends into the module hierarchy.
|
/// This changes as the context descends into the module hierarchy.
|
||||||
pub(super) dst: PathBuf,
|
crate dst: PathBuf,
|
||||||
/// A flag, which when `true`, will render pages which redirect to the
|
/// A flag, which when `true`, will render pages which redirect to the
|
||||||
/// real location of an item. This is used to allow external links to
|
/// real location of an item. This is used to allow external links to
|
||||||
/// publicly reused items to redirect to the right location.
|
/// publicly reused items to redirect to the right location.
|
||||||
|
@ -58,7 +61,7 @@ crate struct Context<'tcx> {
|
||||||
/// Issue for improving the situation: [#82381][]
|
/// Issue for improving the situation: [#82381][]
|
||||||
///
|
///
|
||||||
/// [#82381]: https://github.com/rust-lang/rust/issues/82381
|
/// [#82381]: https://github.com/rust-lang/rust/issues/82381
|
||||||
pub(super) shared: Rc<SharedContext<'tcx>>,
|
crate shared: Rc<SharedContext<'tcx>>,
|
||||||
/// The [`Cache`] used during rendering.
|
/// The [`Cache`] used during rendering.
|
||||||
///
|
///
|
||||||
/// Ideally the cache would be in [`SharedContext`], but it's mutated
|
/// Ideally the cache would be in [`SharedContext`], but it's mutated
|
||||||
|
@ -68,7 +71,11 @@ crate struct Context<'tcx> {
|
||||||
/// It's immutable once in `Context`, so it's not as bad that it's not in
|
/// It's immutable once in `Context`, so it's not as bad that it's not in
|
||||||
/// `SharedContext`.
|
/// `SharedContext`.
|
||||||
// FIXME: move `cache` to `SharedContext`
|
// FIXME: move `cache` to `SharedContext`
|
||||||
pub(super) cache: Rc<Cache>,
|
crate cache: Rc<Cache>,
|
||||||
|
/// This flag indicates whether `[src]` links should be generated or not. If
|
||||||
|
/// the source files are present in the html rendering, then this will be
|
||||||
|
/// `true`.
|
||||||
|
crate include_sources: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
// `Context` is cloned a lot, so we don't want the size to grow unexpectedly.
|
// `Context` is cloned a lot, so we don't want the size to grow unexpectedly.
|
||||||
|
@ -84,10 +91,6 @@ crate struct SharedContext<'tcx> {
|
||||||
/// This describes the layout of each page, and is not modified after
|
/// This describes the layout of each page, and is not modified after
|
||||||
/// creation of the context (contains info like the favicon and added html).
|
/// creation of the context (contains info like the favicon and added html).
|
||||||
crate layout: layout::Layout,
|
crate layout: layout::Layout,
|
||||||
/// This flag indicates whether `[src]` links should be generated or not. If
|
|
||||||
/// the source files are present in the html rendering, then this will be
|
|
||||||
/// `true`.
|
|
||||||
crate include_sources: bool,
|
|
||||||
/// The local file sources we've emitted and their respective url-paths.
|
/// The local file sources we've emitted and their respective url-paths.
|
||||||
crate local_sources: FxHashMap<PathBuf, String>,
|
crate local_sources: FxHashMap<PathBuf, String>,
|
||||||
/// Show the memory layout of types in the docs.
|
/// Show the memory layout of types in the docs.
|
||||||
|
@ -125,6 +128,10 @@ crate struct SharedContext<'tcx> {
|
||||||
redirections: Option<RefCell<FxHashMap<String, String>>>,
|
redirections: Option<RefCell<FxHashMap<String, String>>>,
|
||||||
|
|
||||||
pub(crate) templates: tera::Tera,
|
pub(crate) templates: tera::Tera,
|
||||||
|
|
||||||
|
/// Correspondance map used to link types used in the source code pages to allow to click on
|
||||||
|
/// links to jump to the type's definition.
|
||||||
|
crate span_correspondance_map: FxHashMap<(u32, u32), LinkFromSrc>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SharedContext<'_> {
|
impl SharedContext<'_> {
|
||||||
|
@ -293,15 +300,19 @@ impl<'tcx> Context<'tcx> {
|
||||||
/// may happen, for example, with externally inlined items where the source
|
/// may happen, for example, with externally inlined items where the source
|
||||||
/// of their crate documentation isn't known.
|
/// of their crate documentation isn't known.
|
||||||
pub(super) fn src_href(&self, item: &clean::Item) -> Option<String> {
|
pub(super) fn src_href(&self, item: &clean::Item) -> Option<String> {
|
||||||
if item.span(self.tcx()).is_dummy() {
|
self.href_from_span(item.span(self.tcx()))
|
||||||
|
}
|
||||||
|
|
||||||
|
crate fn href_from_span(&self, span: clean::Span) -> Option<String> {
|
||||||
|
if span.is_dummy() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let mut root = self.root_path();
|
let mut root = self.root_path();
|
||||||
let mut path = String::new();
|
let mut path = String::new();
|
||||||
let cnum = item.span(self.tcx()).cnum(self.sess());
|
let cnum = span.cnum(self.sess());
|
||||||
|
|
||||||
// We can safely ignore synthetic `SourceFile`s.
|
// We can safely ignore synthetic `SourceFile`s.
|
||||||
let file = match item.span(self.tcx()).filename(self.sess()) {
|
let file = match span.filename(self.sess()) {
|
||||||
FileName::Real(ref path) => path.local_path_if_available().to_path_buf(),
|
FileName::Real(ref path) => path.local_path_if_available().to_path_buf(),
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
|
@ -339,8 +350,8 @@ impl<'tcx> Context<'tcx> {
|
||||||
(&*symbol, &path)
|
(&*symbol, &path)
|
||||||
};
|
};
|
||||||
|
|
||||||
let loline = item.span(self.tcx()).lo(self.sess()).line;
|
let loline = span.lo(self.sess()).line;
|
||||||
let hiline = item.span(self.tcx()).hi(self.sess()).line;
|
let hiline = span.hi(self.sess()).line;
|
||||||
let lines =
|
let lines =
|
||||||
if loline == hiline { loline.to_string() } else { format!("{}-{}", loline, hiline) };
|
if loline == hiline { loline.to_string() } else { format!("{}-{}", loline, hiline) };
|
||||||
Some(format!(
|
Some(format!(
|
||||||
|
@ -362,9 +373,9 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
|
||||||
const RUN_ON_MODULE: bool = true;
|
const RUN_ON_MODULE: bool = true;
|
||||||
|
|
||||||
fn init(
|
fn init(
|
||||||
mut krate: clean::Crate,
|
krate: clean::Crate,
|
||||||
options: RenderOptions,
|
options: RenderOptions,
|
||||||
mut cache: Cache,
|
cache: Cache,
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
) -> Result<(Self, clean::Crate), Error> {
|
) -> Result<(Self, clean::Crate), Error> {
|
||||||
// need to save a copy of the options for rendering the index page
|
// need to save a copy of the options for rendering the index page
|
||||||
|
@ -444,13 +455,16 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let (mut krate, local_sources, matches) =
|
||||||
|
collect_spans_and_sources(tcx, krate, &src_root, include_sources);
|
||||||
|
|
||||||
let (sender, receiver) = channel();
|
let (sender, receiver) = channel();
|
||||||
let mut scx = SharedContext {
|
let mut scx = SharedContext {
|
||||||
tcx,
|
tcx,
|
||||||
collapsed: krate.collapsed,
|
collapsed: krate.collapsed,
|
||||||
src_root,
|
src_root,
|
||||||
include_sources,
|
local_sources,
|
||||||
local_sources: Default::default(),
|
|
||||||
issue_tracker_base_url,
|
issue_tracker_base_url,
|
||||||
layout,
|
layout,
|
||||||
created_dirs: Default::default(),
|
created_dirs: Default::default(),
|
||||||
|
@ -466,6 +480,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
|
||||||
redirections: if generate_redirect_map { Some(Default::default()) } else { None },
|
redirections: if generate_redirect_map { Some(Default::default()) } else { None },
|
||||||
show_type_layout,
|
show_type_layout,
|
||||||
templates,
|
templates,
|
||||||
|
span_correspondance_map: matches,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add the default themes to the `Vec` of stylepaths
|
// Add the default themes to the `Vec` of stylepaths
|
||||||
|
@ -483,12 +498,6 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
|
||||||
|
|
||||||
let dst = output;
|
let dst = output;
|
||||||
scx.ensure_dir(&dst)?;
|
scx.ensure_dir(&dst)?;
|
||||||
if emit_crate {
|
|
||||||
krate = sources::render(&dst, &mut scx, krate)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build our search index
|
|
||||||
let index = build_index(&krate, &mut cache, tcx);
|
|
||||||
|
|
||||||
let mut cx = Context {
|
let mut cx = Context {
|
||||||
current: Vec::new(),
|
current: Vec::new(),
|
||||||
|
@ -497,8 +506,16 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
|
||||||
id_map: RefCell::new(id_map),
|
id_map: RefCell::new(id_map),
|
||||||
shared: Rc::new(scx),
|
shared: Rc::new(scx),
|
||||||
cache: Rc::new(cache),
|
cache: Rc::new(cache),
|
||||||
|
include_sources,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if emit_crate {
|
||||||
|
krate = sources::render(&mut cx, krate)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build our search index
|
||||||
|
let index = build_index(&krate, Rc::get_mut(&mut cx.cache).unwrap(), tcx);
|
||||||
|
|
||||||
// Write shared runs within a flock; disable thread dispatching of IO temporarily.
|
// Write shared runs within a flock; disable thread dispatching of IO temporarily.
|
||||||
Rc::get_mut(&mut cx.shared).unwrap().fs.set_sync_only(true);
|
Rc::get_mut(&mut cx.shared).unwrap().fs.set_sync_only(true);
|
||||||
write_shared(&cx, &krate, index, &md_opts)?;
|
write_shared(&cx, &krate, index, &md_opts)?;
|
||||||
|
@ -514,6 +531,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
|
||||||
id_map: RefCell::new(IdMap::new()),
|
id_map: RefCell::new(IdMap::new()),
|
||||||
shared: Rc::clone(&self.shared),
|
shared: Rc::clone(&self.shared),
|
||||||
cache: Rc::clone(&self.cache),
|
cache: Rc::clone(&self.cache),
|
||||||
|
include_sources: self.include_sources,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,9 +30,11 @@ mod tests;
|
||||||
|
|
||||||
mod context;
|
mod context;
|
||||||
mod print_item;
|
mod print_item;
|
||||||
|
mod span_map;
|
||||||
mod write_shared;
|
mod write_shared;
|
||||||
|
|
||||||
crate use context::*;
|
crate use context::*;
|
||||||
|
crate use span_map::{collect_spans_and_sources, LinkFromSrc};
|
||||||
|
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
|
|
|
@ -119,7 +119,7 @@ pub(super) fn print_item(cx: &Context<'_>, item: &clean::Item, buf: &mut Buffer,
|
||||||
// [src] link in the downstream documentation will actually come back to
|
// [src] link in the downstream documentation will actually come back to
|
||||||
// this page, and this link will be auto-clicked. The `id` attribute is
|
// this page, and this link will be auto-clicked. The `id` attribute is
|
||||||
// used to find the link to auto-click.
|
// used to find the link to auto-click.
|
||||||
if cx.shared.include_sources && !item.is_primitive() {
|
if cx.include_sources && !item.is_primitive() {
|
||||||
write_srclink(cx, item, buf);
|
write_srclink(cx, item, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1081,6 +1081,9 @@ fn item_macro(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Mac
|
||||||
None,
|
None,
|
||||||
it.span(cx.tcx()).inner().edition(),
|
it.span(cx.tcx()).inner().edition(),
|
||||||
None,
|
None,
|
||||||
|
0,
|
||||||
|
None,
|
||||||
|
"",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
document(w, cx, it, None)
|
document(w, cx, it, None)
|
||||||
|
|
110
src/librustdoc/html/render/span_map.rs
Normal file
110
src/librustdoc/html/render/span_map.rs
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
use crate::clean;
|
||||||
|
use crate::html::sources;
|
||||||
|
|
||||||
|
use rustc_data_structures::fx::FxHashMap;
|
||||||
|
use rustc_hir::def::{DefKind, Res};
|
||||||
|
use rustc_hir::def_id::DefId;
|
||||||
|
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
|
||||||
|
use rustc_hir::{/*ExprKind, */ GenericParam, GenericParamKind, HirId};
|
||||||
|
use rustc_middle::ty::TyCtxt;
|
||||||
|
use rustc_span::Span;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
crate enum LinkFromSrc {
|
||||||
|
Local(Span),
|
||||||
|
External(DefId),
|
||||||
|
}
|
||||||
|
|
||||||
|
crate fn collect_spans_and_sources(
|
||||||
|
tcx: TyCtxt<'_>,
|
||||||
|
krate: clean::Crate,
|
||||||
|
src_root: &std::path::Path,
|
||||||
|
include_sources: bool,
|
||||||
|
) -> (clean::Crate, FxHashMap<std::path::PathBuf, String>, FxHashMap<(u32, u32), LinkFromSrc>) {
|
||||||
|
let mut visitor = SpanMapVisitor { tcx, matches: FxHashMap::default() };
|
||||||
|
|
||||||
|
if include_sources {
|
||||||
|
intravisit::walk_crate(&mut visitor, tcx.hir().krate());
|
||||||
|
let (krate, sources) = sources::collect_local_sources(tcx, src_root, krate);
|
||||||
|
(krate, sources, visitor.matches)
|
||||||
|
} else {
|
||||||
|
(krate, Default::default(), Default::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn span_to_tuple(span: Span) -> (u32, u32) {
|
||||||
|
(span.lo().0, span.hi().0)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SpanMapVisitor<'tcx> {
|
||||||
|
crate tcx: TyCtxt<'tcx>,
|
||||||
|
crate matches: FxHashMap<(u32, u32), LinkFromSrc>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx> SpanMapVisitor<'tcx> {
|
||||||
|
fn handle_path(&mut self, path: &rustc_hir::Path<'_>, path_span: Option<Span>) -> bool {
|
||||||
|
let info = match path.res {
|
||||||
|
Res::Def(kind, def_id) if kind != DefKind::TyParam => {
|
||||||
|
if matches!(kind, DefKind::Macro(_)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Some(def_id)
|
||||||
|
}
|
||||||
|
Res::Local(_) => None,
|
||||||
|
_ => return true,
|
||||||
|
};
|
||||||
|
if let Some(span) = self.tcx.hir().res_span(path.res) {
|
||||||
|
self.matches.insert(
|
||||||
|
path_span.map(span_to_tuple).unwrap_or_else(|| span_to_tuple(path.span)),
|
||||||
|
LinkFromSrc::Local(span),
|
||||||
|
);
|
||||||
|
} else if let Some(def_id) = info {
|
||||||
|
self.matches.insert(
|
||||||
|
path_span.map(span_to_tuple).unwrap_or_else(|| span_to_tuple(path.span)),
|
||||||
|
LinkFromSrc::External(def_id),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Visitor<'tcx> for SpanMapVisitor<'tcx> {
|
||||||
|
type Map = rustc_middle::hir::map::Map<'tcx>;
|
||||||
|
|
||||||
|
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
||||||
|
NestedVisitorMap::All(self.tcx.hir())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_generic_param(&mut self, p: &'tcx GenericParam<'tcx>) {
|
||||||
|
if !matches!(p.kind, GenericParamKind::Type { .. }) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for bound in p.bounds {
|
||||||
|
if let Some(trait_ref) = bound.trait_ref() {
|
||||||
|
self.handle_path(&trait_ref.path, None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_path(&mut self, path: &'tcx rustc_hir::Path<'tcx>, _id: HirId) {
|
||||||
|
self.handle_path(path, None);
|
||||||
|
intravisit::walk_path(self, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// fn visit_expr(&mut self, expr: &'tcx rustc_hir::Expr<'tcx>) {
|
||||||
|
// match expr.kind {
|
||||||
|
// ExprKind::MethodCall(segment, method_span, _, _) => {
|
||||||
|
// if let Some(hir_id) = segment.hir_id {
|
||||||
|
// // call https://doc.rust-lang.org/beta/nightly-rustc/rustc_middle/ty/context/struct.TypeckResults.html#method.type_dependent_def_id
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// _ => {}
|
||||||
|
// }
|
||||||
|
// intravisit::walk_expr(self, expr);
|
||||||
|
// }
|
||||||
|
|
||||||
|
fn visit_use(&mut self, path: &'tcx rustc_hir::Path<'tcx>, id: HirId) {
|
||||||
|
self.handle_path(path, None);
|
||||||
|
intravisit::walk_use(self, path, id);
|
||||||
|
}
|
||||||
|
}
|
|
@ -272,7 +272,7 @@ pub(super) fn write_shared(
|
||||||
write_minify("search.js", static_files::SEARCH_JS)?;
|
write_minify("search.js", static_files::SEARCH_JS)?;
|
||||||
write_minify("settings.js", static_files::SETTINGS_JS)?;
|
write_minify("settings.js", static_files::SETTINGS_JS)?;
|
||||||
|
|
||||||
if cx.shared.include_sources {
|
if cx.include_sources {
|
||||||
write_minify("source-script.js", static_files::sidebar::SOURCE_SCRIPT)?;
|
write_minify("source-script.js", static_files::sidebar::SOURCE_SCRIPT)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -398,7 +398,7 @@ pub(super) fn write_shared(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if cx.shared.include_sources {
|
if cx.include_sources {
|
||||||
let mut hierarchy = Hierarchy::new(OsString::new());
|
let mut hierarchy = Hierarchy::new(OsString::new());
|
||||||
for source in cx
|
for source in cx
|
||||||
.shared
|
.shared
|
||||||
|
|
|
@ -5,8 +5,10 @@ use crate::fold::DocFolder;
|
||||||
use crate::html::format::Buffer;
|
use crate::html::format::Buffer;
|
||||||
use crate::html::highlight;
|
use crate::html::highlight;
|
||||||
use crate::html::layout;
|
use crate::html::layout;
|
||||||
use crate::html::render::{SharedContext, BASIC_KEYWORDS};
|
use crate::html::render::{Context, BASIC_KEYWORDS};
|
||||||
|
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||||
use rustc_hir::def_id::LOCAL_CRATE;
|
use rustc_hir::def_id::LOCAL_CRATE;
|
||||||
|
use rustc_middle::ty::TyCtxt;
|
||||||
use rustc_session::Session;
|
use rustc_session::Session;
|
||||||
use rustc_span::edition::Edition;
|
use rustc_span::edition::Edition;
|
||||||
use rustc_span::source_map::FileName;
|
use rustc_span::source_map::FileName;
|
||||||
|
@ -14,52 +16,116 @@ use std::ffi::OsStr;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::{Component, Path, PathBuf};
|
use std::path::{Component, Path, PathBuf};
|
||||||
|
|
||||||
crate fn render(
|
crate fn render(cx: &mut Context<'_>, krate: clean::Crate) -> Result<clean::Crate, Error> {
|
||||||
dst: &Path,
|
|
||||||
scx: &mut SharedContext<'_>,
|
|
||||||
krate: clean::Crate,
|
|
||||||
) -> Result<clean::Crate, Error> {
|
|
||||||
info!("emitting source files");
|
info!("emitting source files");
|
||||||
let dst = dst.join("src").join(&*krate.name.as_str());
|
let dst = cx.dst.join("src").join(&*krate.name.as_str());
|
||||||
scx.ensure_dir(&dst)?;
|
cx.shared.ensure_dir(&dst)?;
|
||||||
let mut folder = SourceCollector { dst, scx };
|
let mut folder = SourceCollector { dst, cx, emitted_local_sources: FxHashSet::default() };
|
||||||
Ok(folder.fold_crate(krate))
|
Ok(folder.fold_crate(krate))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
crate fn collect_local_sources<'tcx>(
|
||||||
|
tcx: TyCtxt<'tcx>,
|
||||||
|
src_root: &Path,
|
||||||
|
krate: clean::Crate,
|
||||||
|
) -> (clean::Crate, FxHashMap<PathBuf, String>) {
|
||||||
|
let mut lsc = LocalSourcesCollector { tcx, local_sources: FxHashMap::default(), src_root };
|
||||||
|
|
||||||
|
let krate = lsc.fold_crate(krate);
|
||||||
|
(krate, lsc.local_sources)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LocalSourcesCollector<'a, 'tcx> {
|
||||||
|
tcx: TyCtxt<'tcx>,
|
||||||
|
local_sources: FxHashMap<PathBuf, String>,
|
||||||
|
src_root: &'a Path,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_real_and_local(span: clean::Span, sess: &Session) -> bool {
|
||||||
|
span.filename(sess).is_real() && span.cnum(sess) == LOCAL_CRATE
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LocalSourcesCollector<'_, '_> {
|
||||||
|
fn add_local_source(&mut self, item: &clean::Item) {
|
||||||
|
let sess = self.tcx.sess;
|
||||||
|
let span = item.span(self.tcx);
|
||||||
|
// skip all synthetic "files"
|
||||||
|
if !is_real_and_local(span, sess) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let filename = span.filename(sess);
|
||||||
|
let p = match filename {
|
||||||
|
FileName::Real(ref file) => match file.local_path() {
|
||||||
|
Some(p) => p.to_path_buf(),
|
||||||
|
_ => return,
|
||||||
|
},
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
|
if self.local_sources.contains_key(&*p) {
|
||||||
|
// We've already emitted this source
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut href = String::new();
|
||||||
|
clean_path(&self.src_root, &p, false, |component| {
|
||||||
|
href.push_str(&component.to_string_lossy());
|
||||||
|
href.push('/');
|
||||||
|
});
|
||||||
|
|
||||||
|
let src_fname = p.file_name().expect("source has no filename").to_os_string();
|
||||||
|
let mut fname = src_fname.clone();
|
||||||
|
fname.push(".html");
|
||||||
|
href.push_str(&fname.to_string_lossy());
|
||||||
|
self.local_sources.insert(p, href);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DocFolder for LocalSourcesCollector<'_, '_> {
|
||||||
|
fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
|
||||||
|
self.add_local_source(&item);
|
||||||
|
|
||||||
|
// FIXME: if `include_sources` isn't set and DocFolder didn't require consuming the crate by value,
|
||||||
|
// we could return None here without having to walk the rest of the crate.
|
||||||
|
Some(self.fold_item_recur(item))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Helper struct to render all source code to HTML pages
|
/// Helper struct to render all source code to HTML pages
|
||||||
struct SourceCollector<'a, 'tcx> {
|
struct SourceCollector<'a, 'tcx> {
|
||||||
scx: &'a mut SharedContext<'tcx>,
|
cx: &'a mut Context<'tcx>,
|
||||||
|
|
||||||
/// Root destination to place all HTML output into
|
/// Root destination to place all HTML output into
|
||||||
dst: PathBuf,
|
dst: PathBuf,
|
||||||
|
emitted_local_sources: FxHashSet<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DocFolder for SourceCollector<'_, '_> {
|
impl DocFolder for SourceCollector<'_, '_> {
|
||||||
fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
|
fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
|
||||||
|
let tcx = self.cx.tcx();
|
||||||
|
let span = item.span(tcx);
|
||||||
|
let sess = tcx.sess;
|
||||||
|
|
||||||
// If we're not rendering sources, there's nothing to do.
|
// If we're not rendering sources, there's nothing to do.
|
||||||
// If we're including source files, and we haven't seen this file yet,
|
// If we're including source files, and we haven't seen this file yet,
|
||||||
// then we need to render it out to the filesystem.
|
// then we need to render it out to the filesystem.
|
||||||
if self.scx.include_sources
|
if self.cx.include_sources && is_real_and_local(span, sess) {
|
||||||
// skip all synthetic "files"
|
let filename = span.filename(sess);
|
||||||
&& item.span(self.scx.tcx).filename(self.sess()).is_real()
|
let span = span.inner();
|
||||||
// skip non-local files
|
let start_pos = sess.source_map().lookup_source_file(span.lo()).start_pos;
|
||||||
&& item.span(self.scx.tcx).cnum(self.sess()) == LOCAL_CRATE
|
|
||||||
{
|
|
||||||
let filename = item.span(self.scx.tcx).filename(self.sess());
|
|
||||||
// If it turns out that we couldn't read this file, then we probably
|
// If it turns out that we couldn't read this file, then we probably
|
||||||
// can't read any of the files (generating html output from json or
|
// can't read any of the files (generating html output from json or
|
||||||
// something like that), so just don't include sources for the
|
// something like that), so just don't include sources for the
|
||||||
// entire crate. The other option is maintaining this mapping on a
|
// entire crate. The other option is maintaining this mapping on a
|
||||||
// per-file basis, but that's probably not worth it...
|
// per-file basis, but that's probably not worth it...
|
||||||
self.scx.include_sources = match self.emit_source(&filename) {
|
self.cx.include_sources = match self.emit_source(&filename, start_pos.0) {
|
||||||
Ok(()) => true,
|
Ok(()) => true,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
self.scx.tcx.sess.span_err(
|
self.cx.shared.tcx.sess.span_err(
|
||||||
item.span(self.scx.tcx).inner(),
|
span,
|
||||||
&format!(
|
&format!(
|
||||||
"failed to render source code for `{}`: {}",
|
"failed to render source code for `{}`: {}",
|
||||||
filename.prefer_local(),
|
filename.prefer_local(),
|
||||||
e
|
e,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
false
|
false
|
||||||
|
@ -73,12 +139,8 @@ impl DocFolder for SourceCollector<'_, '_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SourceCollector<'_, 'tcx> {
|
impl SourceCollector<'_, 'tcx> {
|
||||||
fn sess(&self) -> &'tcx Session {
|
|
||||||
&self.scx.tcx.sess
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Renders the given filename into its corresponding HTML source file.
|
/// Renders the given filename into its corresponding HTML source file.
|
||||||
fn emit_source(&mut self, filename: &FileName) -> Result<(), Error> {
|
fn emit_source(&mut self, filename: &FileName, file_span_lo: u32) -> Result<(), Error> {
|
||||||
let p = match *filename {
|
let p = match *filename {
|
||||||
FileName::Real(ref file) => {
|
FileName::Real(ref file) => {
|
||||||
if let Some(local_path) = file.local_path() {
|
if let Some(local_path) = file.local_path() {
|
||||||
|
@ -89,7 +151,7 @@ impl SourceCollector<'_, 'tcx> {
|
||||||
}
|
}
|
||||||
_ => return Ok(()),
|
_ => return Ok(()),
|
||||||
};
|
};
|
||||||
if self.scx.local_sources.contains_key(&*p) {
|
if self.emitted_local_sources.contains(&*p) {
|
||||||
// We've already emitted this source
|
// We've already emitted this source
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
@ -107,20 +169,17 @@ impl SourceCollector<'_, 'tcx> {
|
||||||
// Create the intermediate directories
|
// Create the intermediate directories
|
||||||
let mut cur = self.dst.clone();
|
let mut cur = self.dst.clone();
|
||||||
let mut root_path = String::from("../../");
|
let mut root_path = String::from("../../");
|
||||||
let mut href = String::new();
|
clean_path(&self.cx.shared.src_root, &p, false, |component| {
|
||||||
clean_path(&self.scx.src_root, &p, false, |component| {
|
|
||||||
cur.push(component);
|
cur.push(component);
|
||||||
root_path.push_str("../");
|
root_path.push_str("../");
|
||||||
href.push_str(&component.to_string_lossy());
|
|
||||||
href.push('/');
|
|
||||||
});
|
});
|
||||||
self.scx.ensure_dir(&cur)?;
|
|
||||||
|
self.cx.shared.ensure_dir(&cur)?;
|
||||||
|
|
||||||
let src_fname = p.file_name().expect("source has no filename").to_os_string();
|
let src_fname = p.file_name().expect("source has no filename").to_os_string();
|
||||||
let mut fname = src_fname.clone();
|
let mut fname = src_fname.clone();
|
||||||
fname.push(".html");
|
fname.push(".html");
|
||||||
cur.push(&fname);
|
cur.push(&fname);
|
||||||
href.push_str(&fname.to_string_lossy());
|
|
||||||
|
|
||||||
let title = format!("{} - source", src_fname.to_string_lossy());
|
let title = format!("{} - source", src_fname.to_string_lossy());
|
||||||
let desc = format!("Source of the Rust file `{}`.", filename.prefer_remapped());
|
let desc = format!("Source of the Rust file `{}`.", filename.prefer_remapped());
|
||||||
|
@ -128,23 +187,32 @@ impl SourceCollector<'_, 'tcx> {
|
||||||
title: &title,
|
title: &title,
|
||||||
css_class: "source",
|
css_class: "source",
|
||||||
root_path: &root_path,
|
root_path: &root_path,
|
||||||
static_root_path: self.scx.static_root_path.as_deref(),
|
static_root_path: self.cx.shared.static_root_path.as_deref(),
|
||||||
description: &desc,
|
description: &desc,
|
||||||
keywords: BASIC_KEYWORDS,
|
keywords: BASIC_KEYWORDS,
|
||||||
resource_suffix: &self.scx.resource_suffix,
|
resource_suffix: &self.cx.shared.resource_suffix,
|
||||||
extra_scripts: &[&format!("source-files{}", self.scx.resource_suffix)],
|
extra_scripts: &[&format!("source-files{}", self.cx.shared.resource_suffix)],
|
||||||
static_extra_scripts: &[&format!("source-script{}", self.scx.resource_suffix)],
|
static_extra_scripts: &[&format!("source-script{}", self.cx.shared.resource_suffix)],
|
||||||
};
|
};
|
||||||
let v = layout::render(
|
let v = layout::render(
|
||||||
&self.scx.templates,
|
&self.cx.shared.templates,
|
||||||
&self.scx.layout,
|
&self.cx.shared.layout,
|
||||||
&page,
|
&page,
|
||||||
"",
|
"",
|
||||||
|buf: &mut _| print_src(buf, contents, self.scx.edition()),
|
|buf: &mut _| {
|
||||||
&self.scx.style_files,
|
print_src(
|
||||||
|
buf,
|
||||||
|
contents,
|
||||||
|
self.cx.shared.edition(),
|
||||||
|
file_span_lo,
|
||||||
|
&self.cx,
|
||||||
|
&root_path,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
&self.cx.shared.style_files,
|
||||||
);
|
);
|
||||||
self.scx.fs.write(&cur, v.as_bytes())?;
|
self.cx.shared.fs.write(&cur, v.as_bytes())?;
|
||||||
self.scx.local_sources.insert(p, href);
|
self.emitted_local_sources.insert(p);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -178,7 +246,14 @@ where
|
||||||
|
|
||||||
/// Wrapper struct to render the source code of a file. This will do things like
|
/// Wrapper struct to render the source code of a file. This will do things like
|
||||||
/// adding line numbers to the left-hand side.
|
/// adding line numbers to the left-hand side.
|
||||||
fn print_src(buf: &mut Buffer, s: &str, edition: Edition) {
|
fn print_src(
|
||||||
|
buf: &mut Buffer,
|
||||||
|
s: &str,
|
||||||
|
edition: Edition,
|
||||||
|
file_span_lo: u32,
|
||||||
|
context: &Context<'_>,
|
||||||
|
root_path: &str,
|
||||||
|
) {
|
||||||
let lines = s.lines().count();
|
let lines = s.lines().count();
|
||||||
let mut line_numbers = Buffer::empty_from(buf);
|
let mut line_numbers = Buffer::empty_from(buf);
|
||||||
let mut cols = 0;
|
let mut cols = 0;
|
||||||
|
@ -192,5 +267,16 @@ fn print_src(buf: &mut Buffer, s: &str, edition: Edition) {
|
||||||
writeln!(line_numbers, "<span id=\"{0}\">{0:1$}</span>", i, cols);
|
writeln!(line_numbers, "<span id=\"{0}\">{0:1$}</span>", i, cols);
|
||||||
}
|
}
|
||||||
line_numbers.write_str("</pre>");
|
line_numbers.write_str("</pre>");
|
||||||
highlight::render_with_highlighting(s, buf, None, None, None, edition, Some(line_numbers));
|
highlight::render_with_highlighting(
|
||||||
|
s,
|
||||||
|
buf,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
edition,
|
||||||
|
Some(line_numbers),
|
||||||
|
file_span_lo,
|
||||||
|
Some(context),
|
||||||
|
root_path,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue