Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 023231a

Browse files
Add links on source types to go to definition
1 parent 2f07ae4 commit 023231a

File tree

10 files changed

+454
-106
lines changed

10 files changed

+454
-106
lines changed

src/librustdoc/clean/types.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1950,6 +1950,11 @@ impl Span {
19501950
Self(sp.source_callsite())
19511951
}
19521952

1953+
/// Unless you know what you're doing, use [`Self::from_rustc_span`] instead!
1954+
crate fn wrap(sp: rustc_span::Span) -> Span {
1955+
Self(sp)
1956+
}
1957+
19531958
crate fn inner(&self) -> rustc_span::Span {
19541959
self.0
19551960
}

src/librustdoc/html/format.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -494,7 +494,10 @@ crate fn href(did: DefId, cx: &Context<'_>) -> Result<(String, ItemType, Vec<Str
494494
if !did.is_local() && !cache.access_levels.is_public(did) && !cache.document_private {
495495
return Err(HrefError::Private);
496496
}
497-
497+
// href_with_depth_inner(did, cache, || {
498+
// let depth = CURRENT_DEPTH.with(|l| l.get());
499+
// "../".repeat(depth)
500+
// })
498501
let (fqp, shortty, mut url_parts) = match cache.paths.get(&did) {
499502
Some(&(ref fqp, shortty)) => (fqp, shortty, {
500503
let module_fqp = to_module_fqp(shortty, fqp);

src/librustdoc/html/highlight.rs

Lines changed: 152 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,19 @@
55
//!
66
//! Use the `render_with_highlighting` to highlight some rust code.
77
8+
use crate::clean;
89
use crate::html::escape::Escape;
10+
use crate::html::render::Context;
911

10-
use std::fmt::Display;
12+
use std::fmt::{Display, Write};
1113
use std::iter::Peekable;
1214

1315
use rustc_lexer::{LiteralKind, TokenKind};
1416
use rustc_span::edition::Edition;
1517
use rustc_span::symbol::Symbol;
1618

17-
use super::format::Buffer;
19+
use super::format::{self, Buffer};
20+
use super::render::LinkFromSrc;
1821

1922
/// Highlights `src`, returning the HTML output.
2023
crate fn render_with_highlighting(
@@ -25,6 +28,9 @@ crate fn render_with_highlighting(
2528
tooltip: Option<(Option<Edition>, &str)>,
2629
edition: Edition,
2730
extra_content: Option<Buffer>,
31+
file_span_lo: u32,
32+
context: Option<&Context<'_>>,
33+
root_path: &str,
2834
) {
2935
debug!("highlighting: ================\n{}\n==============", src);
3036
if let Some((edition_info, class)) = tooltip {
@@ -41,7 +47,7 @@ crate fn render_with_highlighting(
4147
}
4248

4349
write_header(out, class, extra_content);
44-
write_code(out, &src, edition);
50+
write_code(out, &src, edition, file_span_lo, context, root_path);
4551
write_footer(out, playground_button);
4652
}
4753

@@ -57,12 +63,21 @@ fn write_header(out: &mut Buffer, class: Option<&str>, extra_content: Option<Buf
5763
}
5864
}
5965

60-
fn write_code(out: &mut Buffer, src: &str, edition: Edition) {
66+
fn write_code(
67+
out: &mut Buffer,
68+
src: &str,
69+
edition: Edition,
70+
file_span_lo: u32,
71+
context: Option<&Context<'_>>,
72+
root_path: &str,
73+
) {
6174
// This replace allows to fix how the code source with DOS backline characters is displayed.
6275
let src = src.replace("\r\n", "\n");
63-
Classifier::new(&src, edition).highlight(&mut |highlight| {
76+
Classifier::new(&src, edition, file_span_lo).highlight(&mut |highlight| {
6477
match highlight {
65-
Highlight::Token { text, class } => string(out, Escape(text), class),
78+
Highlight::Token { text, class } => {
79+
string(out, Escape(text), class, context, root_path)
80+
}
6681
Highlight::EnterSpan { class } => enter_span(out, class),
6782
Highlight::ExitSpan => exit_span(out),
6883
};
@@ -82,14 +97,14 @@ enum Class {
8297
KeyWord,
8398
// Keywords that do pointer/reference stuff.
8499
RefKeyWord,
85-
Self_,
100+
Self_((u32, u32)),
86101
Op,
87102
Macro,
88103
MacroNonTerminal,
89104
String,
90105
Number,
91106
Bool,
92-
Ident,
107+
Ident((u32, u32)),
93108
Lifetime,
94109
PreludeTy,
95110
PreludeVal,
@@ -105,20 +120,27 @@ impl Class {
105120
Class::Attribute => "attribute",
106121
Class::KeyWord => "kw",
107122
Class::RefKeyWord => "kw-2",
108-
Class::Self_ => "self",
123+
Class::Self_(_) => "self",
109124
Class::Op => "op",
110125
Class::Macro => "macro",
111126
Class::MacroNonTerminal => "macro-nonterminal",
112127
Class::String => "string",
113128
Class::Number => "number",
114129
Class::Bool => "bool-val",
115-
Class::Ident => "ident",
130+
Class::Ident(_) => "ident",
116131
Class::Lifetime => "lifetime",
117132
Class::PreludeTy => "prelude-ty",
118133
Class::PreludeVal => "prelude-val",
119134
Class::QuestionMark => "question-mark",
120135
}
121136
}
137+
138+
fn get_span(self) -> Option<(u32, u32)> {
139+
match self {
140+
Self::Ident(sp) | Self::Self_(sp) => Some(sp),
141+
_ => None,
142+
}
143+
}
122144
}
123145

124146
enum Highlight<'a> {
@@ -144,14 +166,23 @@ impl Iterator for TokenIter<'a> {
144166
}
145167
}
146168

147-
fn get_real_ident_class(text: &str, edition: Edition) -> Class {
148-
match text {
169+
/// Returns `None` if this is a `Class::Ident`.
170+
fn get_real_ident_class(text: &str, edition: Edition, allow_path_keywords: bool) -> Option<Class> {
171+
let ignore: &[&str] =
172+
if allow_path_keywords { &["self", "Self", "super", "crate"] } else { &["self", "Self"] };
173+
if ignore.iter().any(|k| *k == text) {
174+
return None;
175+
}
176+
Some(match text {
149177
"ref" | "mut" => Class::RefKeyWord,
150-
"self" | "Self" => Class::Self_,
151178
"false" | "true" => Class::Bool,
152179
_ if Symbol::intern(text).is_reserved(|| edition) => Class::KeyWord,
153-
_ => Class::Ident,
154-
}
180+
_ => return None,
181+
})
182+
}
183+
184+
fn move_span(file_span_lo: u32, start: u32, end: u32) -> (u32, u32) {
185+
(start + file_span_lo, end + file_span_lo)
155186
}
156187

157188
/// Processes program tokens, classifying strings of text by highlighting
@@ -163,11 +194,12 @@ struct Classifier<'a> {
163194
in_macro_nonterminal: bool,
164195
edition: Edition,
165196
byte_pos: u32,
197+
file_span_lo: u32,
166198
src: &'a str,
167199
}
168200

169201
impl<'a> Classifier<'a> {
170-
fn new(src: &str, edition: Edition) -> Classifier<'_> {
202+
fn new(src: &str, edition: Edition, file_span_lo: u32) -> Classifier<'_> {
171203
let tokens = TokenIter { src }.peekable();
172204
Classifier {
173205
tokens,
@@ -176,6 +208,7 @@ impl<'a> Classifier<'a> {
176208
in_macro_nonterminal: false,
177209
edition,
178210
byte_pos: 0,
211+
file_span_lo,
179212
src,
180213
}
181214
}
@@ -201,17 +234,17 @@ impl<'a> Classifier<'a> {
201234
if has_ident {
202235
return vec![(TokenKind::Ident, start, pos), (TokenKind::Colon, pos, pos + nb)];
203236
} else {
204-
return vec![(TokenKind::Colon, pos, pos + nb)];
237+
return vec![(TokenKind::Colon, start, pos + nb)];
205238
}
206239
}
207240

208-
if let Some((Class::Ident, text)) = self.tokens.peek().map(|(token, text)| {
241+
if let Some((None, text)) = self.tokens.peek().map(|(token, text)| {
209242
if *token == TokenKind::Ident {
210-
let class = get_real_ident_class(text, edition);
243+
let class = get_real_ident_class(text, edition, true);
211244
(class, text)
212245
} else {
213246
// Doesn't matter which Class we put in here...
214-
(Class::Comment, text)
247+
(Some(Class::Comment), text)
215248
}
216249
}) {
217250
// We only "add" the colon if there is an ident behind.
@@ -221,7 +254,7 @@ impl<'a> Classifier<'a> {
221254
} else if nb > 0 && has_ident {
222255
return vec![(TokenKind::Ident, start, pos), (TokenKind::Colon, pos, pos + nb)];
223256
} else if nb > 0 {
224-
return vec![(TokenKind::Colon, pos, pos + nb)];
257+
return vec![(TokenKind::Colon, start, start + nb)];
225258
} else if has_ident {
226259
return vec![(TokenKind::Ident, start, pos)];
227260
} else {
@@ -231,10 +264,11 @@ impl<'a> Classifier<'a> {
231264
}
232265

233266
/// Wraps the tokens iteration to ensure that the byte_pos is always correct.
234-
fn next(&mut self) -> Option<(TokenKind, &'a str)> {
267+
fn next(&mut self) -> Option<(TokenKind, &'a str, u32)> {
235268
if let Some((kind, text)) = self.tokens.next() {
269+
let before = self.byte_pos;
236270
self.byte_pos += text.len() as u32;
237-
Some((kind, text))
271+
Some((kind, text, before))
238272
} else {
239273
None
240274
}
@@ -254,14 +288,18 @@ impl<'a> Classifier<'a> {
254288
.unwrap_or(false)
255289
{
256290
let tokens = self.get_full_ident_path();
291+
let skip = !tokens.is_empty();
257292
for (token, start, end) in tokens {
258293
let text = &self.src[start..end];
259-
self.advance(token, text, sink);
294+
self.advance(token, text, sink, start as u32);
260295
self.byte_pos += text.len() as u32;
261296
}
297+
if skip {
298+
continue;
299+
}
262300
}
263-
if let Some((token, text)) = self.next() {
264-
self.advance(token, text, sink);
301+
if let Some((token, text, before)) = self.next() {
302+
self.advance(token, text, sink, before);
265303
} else {
266304
break;
267305
}
@@ -270,7 +308,13 @@ impl<'a> Classifier<'a> {
270308

271309
/// Single step of highlighting. This will classify `token`, but maybe also
272310
/// a couple of following ones as well.
273-
fn advance(&mut self, token: TokenKind, text: &'a str, sink: &mut dyn FnMut(Highlight<'a>)) {
311+
fn advance(
312+
&mut self,
313+
token: TokenKind,
314+
text: &'a str,
315+
sink: &mut dyn FnMut(Highlight<'a>),
316+
before: u32,
317+
) {
274318
let lookahead = self.peek();
275319
let no_highlight = |sink: &mut dyn FnMut(_)| sink(Highlight::Token { text, class: None });
276320
let class = match token {
@@ -401,19 +445,30 @@ impl<'a> Classifier<'a> {
401445
sink(Highlight::Token { text, class: None });
402446
return;
403447
}
404-
TokenKind::Ident => match get_real_ident_class(text, self.edition) {
405-
Class::Ident => match text {
448+
TokenKind::Ident => match get_real_ident_class(text, self.edition, false) {
449+
None => match text {
406450
"Option" | "Result" => Class::PreludeTy,
407451
"Some" | "None" | "Ok" | "Err" => Class::PreludeVal,
408452
_ if self.in_macro_nonterminal => {
409453
self.in_macro_nonterminal = false;
410454
Class::MacroNonTerminal
411455
}
412-
_ => Class::Ident,
456+
"self" | "Self" => Class::Self_(move_span(
457+
self.file_span_lo,
458+
before,
459+
before + text.len() as u32,
460+
)),
461+
_ => Class::Ident(move_span(
462+
self.file_span_lo,
463+
before,
464+
before + text.len() as u32,
465+
)),
413466
},
414-
c => c,
467+
Some(c) => c,
415468
},
416-
TokenKind::RawIdent | TokenKind::UnknownPrefix => Class::Ident,
469+
TokenKind::RawIdent | TokenKind::UnknownPrefix => {
470+
Class::Ident(move_span(self.file_span_lo, before, before + text.len() as u32))
471+
}
417472
TokenKind::Lifetime { .. } => Class::Lifetime,
418473
};
419474
// Anything that didn't return above is the simple case where we the
@@ -448,11 +503,74 @@ fn exit_span(out: &mut Buffer) {
448503
/// ```
449504
/// The latter can be thought of as a shorthand for the former, which is more
450505
/// flexible.
451-
fn string<T: Display>(out: &mut Buffer, text: T, klass: Option<Class>) {
506+
fn string<T: Display>(
507+
out: &mut Buffer,
508+
text: T,
509+
klass: Option<Class>,
510+
context: Option<&Context<'_>>,
511+
root_path: &str,
512+
) {
452513
match klass {
453514
None => write!(out, "{}", text),
454-
Some(klass) => write!(out, "<span class=\"{}\">{}</span>", klass.as_html(), text),
515+
Some(klass) => {
516+
if let Some(def_span) = klass.get_span() {
517+
let mut text = text.to_string();
518+
if text.contains("::") {
519+
text =
520+
text.split("::").enumerate().fold(String::new(), |mut path, (pos, t)| {
521+
let pre = if pos != 0 { "::" } else { "" };
522+
match t {
523+
"self" | "Self" => write!(
524+
&mut path,
525+
"{}<span class=\"{}\">{}</span>",
526+
pre,
527+
Class::Self_((0, 0)).as_html(),
528+
t
529+
),
530+
"crate" | "super" => write!(
531+
&mut path,
532+
"{}<span class=\"{}\">{}</span>",
533+
pre,
534+
Class::KeyWord.as_html(),
535+
t
536+
),
537+
t => write!(&mut path, "{}{}", pre, t),
538+
}
539+
.expect("Failed to build source HTML path");
540+
path
541+
});
542+
}
543+
if let Some(context) = context {
544+
if let Some(href) =
545+
context.shared.span_correspondance_map.get(&def_span).and_then(|href| {
546+
match href {
547+
LinkFromSrc::Local(span) => {
548+
eprintln!("==> {:?}:{:?}", span.lo(), span.hi());
549+
context
550+
.href_from_span(clean::Span::wrap(*span))
551+
.map(|s| format!("{}{}", root_path, s))
552+
},
553+
LinkFromSrc::External(def_id) => {
554+
format::href(*def_id, context).map(|(url, _, _)| url)
555+
}
556+
}
557+
})
558+
{
559+
write!(
560+
out,
561+
"<a class=\"{}\" href=\"{}\">{}</a>",
562+
klass.as_html(),
563+
href,
564+
text
565+
);
566+
return;
567+
}
568+
}
569+
}
570+
write!(out, "<span class=\"{}\">{}</span>", klass.as_html(), text);
571+
}
455572
}
573+
write!(out, "<span class=\"{}\">{}</span>", klass.as_html(), text);
456574
}
457575

458576
#[cfg(test)]

src/librustdoc/html/markdown.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,9 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> {
330330
tooltip,
331331
edition,
332332
None,
333+
0,
334+
None,
335+
"",
333336
);
334337
Some(Event::Html(s.into_inner().into()))
335338
}

0 commit comments

Comments
 (0)