diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 8c91cae493103..15bf968e0fc77 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -54,7 +54,7 @@ pub(crate) fn render_example_with_highlighting( extra_classes: &[String], ) { write_header(out, "rust-example-rendered", None, tooltip, extra_classes); - write_code(out, src, None, None); + write_code(out, src, None, None, None); write_footer(out, playground_button); } @@ -150,6 +150,7 @@ struct TokenHandler<'a, 'tcx, F: Write> { /// used to generate links. pending_elems: Vec<(&'a str, Option)>, href_context: Option>, + write_line_number: fn(&mut F, u32, &'static str), } impl TokenHandler<'_, '_, F> { @@ -182,7 +183,14 @@ impl TokenHandler<'_, '_, F> { && can_merge(current_class, Some(*parent_class), "") { for (text, class) in self.pending_elems.iter() { - string(self.out, EscapeBodyText(text), *class, &self.href_context, false); + string( + self.out, + EscapeBodyText(text), + *class, + &self.href_context, + false, + self.write_line_number, + ); } } else { // We only want to "open" the tag ourselves if we have more than one pending and if the @@ -204,6 +212,7 @@ impl TokenHandler<'_, '_, F> { *class, &self.href_context, close_tag.is_none(), + self.write_line_number, ); } if let Some(close_tag) = close_tag { @@ -213,6 +222,11 @@ impl TokenHandler<'_, '_, F> { self.pending_elems.clear(); true } + + #[inline] + fn write_line_number(&mut self, line: u32, extra: &'static str) { + (self.write_line_number)(&mut self.out, line, extra); + } } impl Drop for TokenHandler<'_, '_, F> { @@ -226,6 +240,43 @@ impl Drop for TokenHandler<'_, '_, F> { } } +fn write_scraped_line_number(out: &mut impl Write, line: u32, extra: &'static str) { + // https://developers.google.com/search/docs/crawling-indexing/robots-meta-tag#data-nosnippet-attr + // Do not show "1 2 3 4 5 ..." in web search results. + write!(out, "{extra}{line}",).unwrap(); +} + +fn write_line_number(out: &mut impl Write, line: u32, extra: &'static str) { + // https://developers.google.com/search/docs/crawling-indexing/robots-meta-tag#data-nosnippet-attr + // Do not show "1 2 3 4 5 ..." in web search results. + write!(out, "{extra}{line}",).unwrap(); +} + +fn empty_line_number(out: &mut impl Write, _: u32, extra: &'static str) { + out.write_str(extra).unwrap(); +} + +#[derive(Clone, Copy)] +pub(super) struct LineInfo { + pub(super) start_line: u32, + max_lines: u32, + pub(super) is_scraped_example: bool, +} + +impl LineInfo { + pub(super) fn new(max_lines: u32) -> Self { + Self { start_line: 1, max_lines: max_lines + 1, is_scraped_example: false } + } + + pub(super) fn new_scraped(max_lines: u32, start_line: u32) -> Self { + Self { + start_line: start_line + 1, + max_lines: max_lines + start_line + 1, + is_scraped_example: true, + } + } +} + /// Convert the given `src` source code into HTML by adding classes for highlighting. /// /// This code is used to render code blocks (in the documentation) as well as the source code pages. @@ -242,6 +293,7 @@ pub(super) fn write_code( src: &str, href_context: Option>, decoration_info: Option<&DecorationInfo>, + line_info: Option, ) { // This replace allows to fix how the code source with DOS backline characters is displayed. let src = src.replace("\r\n", "\n"); @@ -252,6 +304,23 @@ pub(super) fn write_code( current_class: None, pending_elems: Vec::new(), href_context, + write_line_number: match line_info { + Some(line_info) => { + if line_info.is_scraped_example { + write_scraped_line_number + } else { + write_line_number + } + } + None => empty_line_number, + }, + }; + + let (mut line, max_lines) = if let Some(line_info) = line_info { + token_handler.write_line_number(line_info.start_line, ""); + (line_info.start_line, line_info.max_lines) + } else { + (0, u32::MAX) }; Classifier::new( @@ -282,7 +351,14 @@ pub(super) fn write_code( if need_current_class_update { token_handler.current_class = class.map(Class::dummy); } - token_handler.pending_elems.push((text, class)); + if text == "\n" { + line += 1; + if line < max_lines { + token_handler.pending_elems.push((text, Some(Class::Backline(line)))); + } + } else { + token_handler.pending_elems.push((text, class)); + } } Highlight::EnterSpan { class } => { let mut should_add = true; @@ -348,6 +424,7 @@ enum Class { PreludeVal(Span), QuestionMark, Decoration(&'static str), + Backline(u32), } impl Class { @@ -396,6 +473,7 @@ impl Class { Class::PreludeVal(_) => "prelude-val", Class::QuestionMark => "question-mark", Class::Decoration(kind) => kind, + Class::Backline(_) => "", } } @@ -419,7 +497,8 @@ impl Class { | Self::Bool | Self::Lifetime | Self::QuestionMark - | Self::Decoration(_) => None, + | Self::Decoration(_) + | Self::Backline(_) => None, } } } @@ -694,8 +773,13 @@ impl<'src> Classifier<'src> { ) { let lookahead = self.peek(); let no_highlight = |sink: &mut dyn FnMut(_)| sink(Highlight::Token { text, class: None }); + let whitespace = |sink: &mut dyn FnMut(_)| { + for part in text.split('\n').intersperse("\n").filter(|s| !s.is_empty()) { + sink(Highlight::Token { text: part, class: None }); + } + }; let class = match token { - TokenKind::Whitespace => return no_highlight(sink), + TokenKind::Whitespace => return whitespace(sink), TokenKind::LineComment { doc_style } | TokenKind::BlockComment { doc_style, .. } => { if doc_style.is_some() { Class::DocComment @@ -716,7 +800,7 @@ impl<'src> Classifier<'src> { // or a reference or pointer type. Unless, of course, it looks like // a logical and or a multiplication operator: `&&` or `* `. TokenKind::Star => match self.tokens.peek() { - Some((TokenKind::Whitespace, _)) => return no_highlight(sink), + Some((TokenKind::Whitespace, _)) => return whitespace(sink), Some((TokenKind::Ident, "mut")) => { self.next(); sink(Highlight::Token { text: "*mut", class: Some(Class::RefKeyWord) }); @@ -740,7 +824,7 @@ impl<'src> Classifier<'src> { sink(Highlight::Token { text: "&=", class: None }); return; } - Some((TokenKind::Whitespace, _)) => return no_highlight(sink), + Some((TokenKind::Whitespace, _)) => return whitespace(sink), Some((TokenKind::Ident, "mut")) => { self.next(); sink(Highlight::Token { text: "&mut", class: Some(Class::RefKeyWord) }); @@ -887,7 +971,9 @@ impl<'src> Classifier<'src> { }; // Anything that didn't return above is the simple case where we the // class just spans a single token, so we can use the `string` method. - sink(Highlight::Token { text, class: Some(class) }); + for part in text.split('\n').intersperse("\n").filter(|s| !s.is_empty()) { + sink(Highlight::Token { text: part, class: Some(class) }); + } } fn peek(&mut self) -> Option { @@ -939,14 +1025,18 @@ fn exit_span(out: &mut impl Write, closing_tag: &str) { /// Note that if `context` is not `None` and that the given `klass` contains a `Span`, the function /// will then try to find this `span` in the `span_correspondence_map`. If found, it'll then /// generate a link for this element (which corresponds to where its definition is located). -fn string( - out: &mut impl Write, +fn string( + out: &mut W, text: T, klass: Option, href_context: &Option>, open_tag: bool, + write_line_number_callback: fn(&mut W, u32, &'static str), ) { - if let Some(closing_tag) = string_without_closing_tag(out, text, klass, href_context, open_tag) + if let Some(Class::Backline(line)) = klass { + write_line_number_callback(out, line, "\n"); + } else if let Some(closing_tag) = + string_without_closing_tag(out, text, klass, href_context, open_tag) { out.write_str(closing_tag).unwrap(); } diff --git a/src/librustdoc/html/highlight/tests.rs b/src/librustdoc/html/highlight/tests.rs index fccbb98f80ff3..8f39130bb836e 100644 --- a/src/librustdoc/html/highlight/tests.rs +++ b/src/librustdoc/html/highlight/tests.rs @@ -23,7 +23,7 @@ fn test_html_highlighting() { let src = include_str!("fixtures/sample.rs"); let html = { let mut out = Buffer::new(); - write_code(&mut out, src, None, None); + write_code(&mut out, src, None, None, None); format!("{STYLE}
{}
\n", out.into_inner()) }; expect_file!["fixtures/sample.html"].assert_eq(&html); @@ -37,7 +37,7 @@ fn test_dos_backline() { println!(\"foo\");\r\n\ }\r\n"; let mut html = Buffer::new(); - write_code(&mut html, src, None, None); + write_code(&mut html, src, None, None, None); expect_file!["fixtures/dos_line.html"].assert_eq(&html.into_inner()); }); } @@ -51,7 +51,7 @@ let x = super::b::foo; let y = Self::whatever;"; let mut html = Buffer::new(); - write_code(&mut html, src, None, None); + write_code(&mut html, src, None, None, None); expect_file!["fixtures/highlight.html"].assert_eq(&html.into_inner()); }); } @@ -61,7 +61,7 @@ fn test_union_highlighting() { create_default_session_globals_then(|| { let src = include_str!("fixtures/union.rs"); let mut html = Buffer::new(); - write_code(&mut html, src, None, None); + write_code(&mut html, src, None, None, None); expect_file!["fixtures/union.html"].assert_eq(&html.into_inner()); }); } @@ -78,7 +78,7 @@ let a = 4;"; decorations.insert("example2", vec![(22, 32)]); let mut html = Buffer::new(); - write_code(&mut html, src, None, Some(&DecorationInfo(decorations))); + write_code(&mut html, src, None, Some(&DecorationInfo(decorations)), None); expect_file!["fixtures/decorations.html"].assert_eq(&html.into_inner()); }); } diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs index 1ac0c10c61241..7839ba9ac8321 100644 --- a/src/librustdoc/html/sources.rs +++ b/src/librustdoc/html/sources.rs @@ -1,6 +1,5 @@ use std::cell::RefCell; use std::ffi::OsStr; -use std::ops::RangeInclusive; use std::path::{Component, Path, PathBuf}; use std::{fmt, fs}; @@ -303,16 +302,16 @@ pub(crate) struct ScrapedInfo<'a> { #[template(path = "scraped_source.html")] struct ScrapedSource<'a, Code: std::fmt::Display> { info: ScrapedInfo<'a>, - lines: RangeInclusive, code_html: Code, + max_nb_digits: u32, } #[derive(Template)] #[template(path = "source.html")] struct Source { - lines: RangeInclusive, code_html: Code, file_path: Option<(String, String)>, + max_nb_digits: u32, } pub(crate) enum SourceContext<'a> { @@ -331,6 +330,15 @@ pub(crate) fn print_src( decoration_info: &highlight::DecorationInfo, source_context: SourceContext<'_>, ) { + let mut lines = s.lines().count(); + let line_info = if let SourceContext::Embedded(ref info) = source_context { + highlight::LineInfo::new_scraped(lines as u32, info.offset as u32) + } else { + highlight::LineInfo::new(lines as u32) + }; + if line_info.is_scraped_example { + lines += line_info.start_line as usize; + } let code = fmt::from_fn(move |fmt| { let current_href = context .href_from_span(clean::Span::new(file_span), false) @@ -340,13 +348,13 @@ pub(crate) fn print_src( s, Some(highlight::HrefContext { context, file_span, root_path, current_href }), Some(decoration_info), + Some(line_info), ); Ok(()) }); - let lines = s.lines().count(); + let max_nb_digits = if lines > 0 { lines.ilog(10) + 1 } else { 1 }; match source_context { SourceContext::Standalone { file_path } => Source { - lines: (1..=lines), code_html: code, file_path: if let Some(file_name) = file_path.file_name() && let Some(file_path) = file_path.parent() @@ -355,12 +363,14 @@ pub(crate) fn print_src( } else { None }, + max_nb_digits, } .render_into(&mut writer) .unwrap(), SourceContext::Embedded(info) => { - let lines = (1 + info.offset)..=(lines + info.offset); - ScrapedSource { info, lines, code_html: code }.render_into(&mut writer).unwrap(); + ScrapedSource { info, code_html: code, max_nb_digits } + .render_into(&mut writer) + .unwrap(); } }; } diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index d0612e997fd7b..f39c0e4a31400 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -40,6 +40,7 @@ xmlns="http://www.w3.org/2000/svg" fill="black" height="18px">\ --docblock-indent: 24px; --font-family: "Source Serif 4", NanumBarunGothic, serif; --font-family-code: "Source Code Pro", monospace; + --line-number-padding: 4px; } :root.sans-serif { @@ -450,9 +451,7 @@ pre.item-decl { .src .content pre { padding: 20px; -} -.rustdoc.src .example-wrap .src-line-numbers { - padding: 20px 0 20px 4px; + padding-left: 16px; } img { @@ -901,29 +900,58 @@ both the code example and the line numbers, so we need to remove the radius in t min-width: fit-content; /* prevent collapsing into nothing in truncated scraped examples */ flex-grow: 0; text-align: right; + -moz-user-select: none; -webkit-user-select: none; + -ms-user-select: none; user-select: none; padding: 14px 8px; padding-right: 2px; color: var(--src-line-numbers-span-color); } -.rustdoc .scraped-example .example-wrap .src-line-numbers { - padding: 0; +.example-wrap.digits-1 [data-nosnippet] { + width: calc(1ch + var(--line-number-padding) * 2); +} +.example-wrap.digits-2 [data-nosnippet] { + width: calc(2ch + var(--line-number-padding) * 2); +} +.example-wrap.digits-3 [data-nosnippet] { + width: calc(3ch + var(--line-number-padding) * 2); +} +.example-wrap.digits-4 [data-nosnippet] { + width: calc(4ch + var(--line-number-padding) * 2); +} +.example-wrap.digits-5 [data-nosnippet] { + width: calc(5ch + var(--line-number-padding) * 2); +} +.example-wrap.digits-6 [data-nosnippet] { + width: calc(6ch + var(--line-number-padding) * 2); +} +.example-wrap.digits-7 [data-nosnippet] { + width: calc(7ch + var(--line-number-padding) * 2); } -.rustdoc .src-line-numbers pre { - padding: 14px 0; +.example-wrap.digits-8 [data-nosnippet] { + width: calc(8ch + var(--line-number-padding) * 2); } -.src-line-numbers a, .src-line-numbers span { +.example-wrap.digits-9 [data-nosnippet] { + width: calc(9ch + var(--line-number-padding) * 2); +} + +.example-wrap [data-nosnippet] { color: var(--src-line-numbers-span-color); - padding: 0 8px; + text-align: right; + display: inline-block; + margin-right: 20px; + -moz-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; + padding: 0 4px; } -.src-line-numbers :target { - background-color: transparent; +.example-wrap [data-nosnippet]:target { border-right: none; - padding: 0 8px; } -.src-line-numbers .line-highlighted { +.example-wrap .line-highlighted[data-nosnippet] { background-color: var(--src-line-number-highlighted-background-color); } @@ -1110,7 +1138,7 @@ because of the `[-]` element which would overlap with it. */ } .main-heading a:hover, -.example-wrap .rust a:hover, +.example-wrap .rust a:hover:not([data-nosnippet]), .all-items a:hover, .docblock a:not(.scrape-help):not(.tooltip):hover:not(.doc-anchor), .item-table dd a:not(.scrape-help):not(.tooltip):hover, @@ -1568,7 +1596,7 @@ pre.rust .doccomment { color: var(--code-highlight-doc-comment-color); } -.rustdoc.src .example-wrap pre.rust a { +.rustdoc.src .example-wrap pre.rust a:not([data-nosnippet]) { background: var(--codeblock-link-background); } @@ -1759,8 +1787,7 @@ instead, we check that it's not a "finger" cursor. } } -:target { - padding-right: 3px; +:target:not([data-nosnippet]) { background-color: var(--target-background-color); border-right: 3px solid var(--target-border-color); } @@ -3153,7 +3180,7 @@ Original by Dempfi (https://github.com/dempfi/ayu) color: #ff7733; } -:root[data-theme="ayu"] .src-line-numbers .line-highlighted { +:root[data-theme="ayu"] a[data-nosnippet].line-highlighted { color: #708090; padding-right: 7px; border-right: 1px solid #ffb44c; diff --git a/src/librustdoc/html/static/js/scrape-examples.js b/src/librustdoc/html/static/js/scrape-examples.js index d08f15a5bfa86..99cbe6daf3177 100644 --- a/src/librustdoc/html/static/js/scrape-examples.js +++ b/src/librustdoc/html/static/js/scrape-examples.js @@ -16,7 +16,7 @@ // Scroll code block to the given code location function scrollToLoc(elt, loc, isHidden) { - const lines = elt.querySelector(".src-line-numbers > pre"); + const lines = elt.querySelectorAll("[data-nosnippet]"); let scrollOffset; // If the block is greater than the size of the viewer, @@ -25,17 +25,17 @@ const maxLines = isHidden ? HIDDEN_MAX_LINES : DEFAULT_MAX_LINES; if (loc[1] - loc[0] > maxLines) { const line = Math.max(0, loc[0] - 1); - scrollOffset = lines.children[line].offsetTop; + scrollOffset = lines[line].offsetTop; } else { const halfHeight = elt.offsetHeight / 2; - const offsetTop = lines.children[loc[0]].offsetTop; - const lastLine = lines.children[loc[1]]; + const offsetTop = lines[loc[0]].offsetTop; + const lastLine = lines[loc[1]]; const offsetBot = lastLine.offsetTop + lastLine.offsetHeight; const offsetMid = (offsetTop + offsetBot) / 2; scrollOffset = offsetMid - halfHeight; } - lines.parentElement.scrollTo(0, scrollOffset); + lines[0].parentElement.scrollTo(0, scrollOffset); elt.querySelector(".rust").scrollTo(0, scrollOffset); } diff --git a/src/librustdoc/html/static/js/src-script.js b/src/librustdoc/html/static/js/src-script.js index 8f712f4c20c7b..fc27241334bfe 100644 --- a/src/librustdoc/html/static/js/src-script.js +++ b/src/librustdoc/html/static/js/src-script.js @@ -138,10 +138,8 @@ function highlightSrcLines() { if (x) { x.scrollIntoView(); } - onEachLazy(document.getElementsByClassName("src-line-numbers"), e => { - onEachLazy(e.getElementsByTagName("a"), i_e => { - removeClass(i_e, "line-highlighted"); - }); + onEachLazy(document.querySelectorAll("a[data-nosnippet]"), e => { + removeClass(e, "line-highlighted"); }); for (let i = from; i <= to; ++i) { elem = document.getElementById(i); @@ -200,7 +198,7 @@ const handleSrcHighlight = (function() { window.addEventListener("hashchange", highlightSrcLines); -onEachLazy(document.getElementsByClassName("src-line-numbers"), el => { +onEachLazy(document.querySelectorAll("a[data-nosnippet]"), el => { el.addEventListener("click", handleSrcHighlight); }); diff --git a/src/librustdoc/html/templates/scraped_source.html b/src/librustdoc/html/templates/scraped_source.html index bd54bbf58d508..3e69f1c8cad71 100644 --- a/src/librustdoc/html/templates/scraped_source.html +++ b/src/librustdoc/html/templates/scraped_source.html @@ -2,17 +2,7 @@
{{info.name +}} ({{info.title}}) {# #}
{# #} -
- {# https://developers.google.com/search/docs/crawling-indexing/robots-meta-tag#data-nosnippet-attr - Do not show "1 2 3 4 5 ..." in web search results. #} -
{# #} -
-                {% for line in lines.clone() %}
-                    {# ~#}
-                    {{line|safe}}
-                {% endfor %}
-            
{# #} -
{# #} +
{# #}
 {# #}
             
                 {{code_html|safe}}
diff --git a/src/librustdoc/html/templates/source.html b/src/librustdoc/html/templates/source.html
index ea530087e6f09..454d4c27f1ad6 100644
--- a/src/librustdoc/html/templates/source.html
+++ b/src/librustdoc/html/templates/source.html
@@ -9,15 +9,7 @@ 

{# #}

{% else %} {% endmatch %} -
- {# https://developers.google.com/search/docs/crawling-indexing/robots-meta-tag#data-nosnippet-attr - Do not show "1 2 3 4 5 ..." in web search results. #} -
-        {% for line in lines.clone() %}
-            {# ~#}
-            {{line|safe}}
-        {% endfor %}
-    
{# #} +
{# #}
 {# #}
         
             {{code_html|safe}}
diff --git a/tests/rustdoc-gui/basic-code.goml b/tests/rustdoc-gui/basic-code.goml
deleted file mode 100644
index 22ac531618423..0000000000000
--- a/tests/rustdoc-gui/basic-code.goml
+++ /dev/null
@@ -1,6 +0,0 @@
-// Small test to ensure the "src-line-numbers" element is only present once on
-// the page.
-go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
-click: "a.src"
-wait-for: ".src-line-numbers"
-assert-count: (".src-line-numbers", 1)
diff --git a/tests/rustdoc-gui/docblock-code-block-line-number.goml b/tests/rustdoc-gui/docblock-code-block-line-number.goml
index 3c16626336ea6..032746a6bdf44 100644
--- a/tests/rustdoc-gui/docblock-code-block-line-number.goml
+++ b/tests/rustdoc-gui/docblock-code-block-line-number.goml
@@ -111,28 +111,6 @@ wait-for: "pre.example-line-numbers"
 // Same check with scraped examples line numbers.
 go-to: "file://" + |DOC_PATH| + "/scrape_examples/fn.test_many.html"
 
-assert-css: (
-    ".scraped-example .src-line-numbers > pre",
-    {
-        // There should not be a radius on the right of the line numbers.
-        "border-top-left-radius": "6px",
-        "border-bottom-left-radius": "6px",
-        "border-top-right-radius": "0px",
-        "border-bottom-right-radius": "0px",
-    },
-    ALL,
-)
-assert-css: (
-    ".scraped-example .src-line-numbers",
-    {
-        // There should not be a radius on the right of the line numbers.
-        "border-top-left-radius": "6px",
-        "border-bottom-left-radius": "6px",
-        "border-top-right-radius": "0px",
-        "border-bottom-right-radius": "0px",
-    },
-    ALL,
-)
 assert-css: (
     ".scraped-example .rust",
     {
@@ -149,23 +127,15 @@ define-function: (
     "check-padding",
     [path, padding_bottom],
     block {
-        assert-css: (|path| + " .src-line-numbers", {
+        assert-css: (|path| + " span[data-nosnippet]", {
             "padding-top": "0px",
             "padding-bottom": "0px",
-            "padding-left": "0px",
-            "padding-right": "0px",
-        }, ALL)
-        assert-css: (|path| + " .src-line-numbers > pre", {
-            "padding-top": "14px",
-            "padding-bottom": |padding_bottom|,
-            "padding-left": "0px",
-            "padding-right": "0px",
-        }, ALL)
-        assert-css: (|path| + " .src-line-numbers > pre > span", {
-            "padding-top": "0px",
-            "padding-bottom": "0px",
-            "padding-left": "8px",
-            "padding-right": "8px",
+            "padding-left": "4px",
+            "padding-right": "4px",
+            "margin-right": "20px",
+            "margin-left": "0px",
+            "margin-top": "0px",
+            "margin-bottom": "0px",
         }, ALL)
     },
 )
@@ -196,13 +166,13 @@ define-function: ("check-line-numbers-existence", [], block {
     wait-for-local-storage-false: {"rustdoc-line-numbers": "true" }
     assert-false: ".example-line-numbers"
     // Line numbers should still be there.
-    assert: ".src-line-numbers"
+    assert-css: ("[data-nosnippet]", { "display": "inline-block"})
     // Now disabling the setting.
     click: "input#line-numbers"
     wait-for-local-storage: {"rustdoc-line-numbers": "true" }
     assert-false: ".example-line-numbers"
     // Line numbers should still be there.
-    assert: ".src-line-numbers"
+    assert-css: ("[data-nosnippet]", { "display": "inline-block"})
     // Closing settings menu.
     click: "#settings-menu"
     wait-for-css: ("#settings", {"display": "none"})
@@ -214,18 +184,16 @@ call-function: ("check-line-numbers-existence", {})
 
 // Now checking the line numbers in the source code page.
 click: ".src"
-assert-css: (".src-line-numbers", {
-    "padding-top": "20px",
-    "padding-bottom": "20px",
-    "padding-left": "4px",
-    "padding-right": "0px",
-})
-assert-css: (".src-line-numbers > a", {
+assert-css: ("a[data-nosnippet]", {
     "padding-top": "0px",
     "padding-bottom": "0px",
-    "padding-left": "8px",
-    "padding-right": "8px",
-})
+    "padding-left": "4px",
+    "padding-right": "4px",
+    "margin-top": "0px",
+    "margin-bottom": "0px",
+    "margin-left": "0px",
+    "margin-right": "20px",
+}, ALL)
 // Checking that turning off the line numbers setting won't remove line numbers.
 call-function: ("check-line-numbers-existence", {})
 
diff --git a/tests/rustdoc-gui/jump-to-def-background.goml b/tests/rustdoc-gui/jump-to-def-background.goml
index 71320360740c4..de5ea6c9b0b08 100644
--- a/tests/rustdoc-gui/jump-to-def-background.goml
+++ b/tests/rustdoc-gui/jump-to-def-background.goml
@@ -8,7 +8,7 @@ define-function: (
     block {
         call-function: ("switch-theme", {"theme": |theme|})
         assert-css: (
-            "body.src .example-wrap pre.rust a",
+            "body.src .example-wrap pre.rust a:not([data-nosnippet])",
             {"background-color": |background_color|},
             ALL,
         )
diff --git a/tests/rustdoc-gui/scrape-examples-button-focus.goml b/tests/rustdoc-gui/scrape-examples-button-focus.goml
index 83ed6a219b25a..d53993ac08bae 100644
--- a/tests/rustdoc-gui/scrape-examples-button-focus.goml
+++ b/tests/rustdoc-gui/scrape-examples-button-focus.goml
@@ -4,52 +4,18 @@ go-to: "file://" + |DOC_PATH| + "/scrape_examples/fn.test.html"
 
 // The next/prev buttons vertically scroll the code viewport between examples
 move-cursor-to: ".scraped-example-list > .scraped-example"
-store-property: (".scraped-example-list > .scraped-example .src-line-numbers", {
-    "scrollTop": initialScrollTop,
-})
+wait-for: ".scraped-example-list > .scraped-example .next"
+store-value: (initialScrollTop, 250)
 assert-property: (".scraped-example-list > .scraped-example .rust", {
     "scrollTop": |initialScrollTop|,
-})
+}, NEAR)
 focus: ".scraped-example-list > .scraped-example .next"
 press-key: "Enter"
-assert-property-false: (".scraped-example-list > .scraped-example .src-line-numbers", {
-    "scrollTop": |initialScrollTop|
-}, NEAR)
 assert-property-false: (".scraped-example-list > .scraped-example .rust", {
     "scrollTop": |initialScrollTop|
 }, NEAR)
 focus: ".scraped-example-list > .scraped-example .prev"
 press-key: "Enter"
-assert-property: (".scraped-example-list > .scraped-example .src-line-numbers", {
-    "scrollTop": |initialScrollTop|
-}, NEAR)
 assert-property: (".scraped-example-list > .scraped-example .rust", {
     "scrollTop": |initialScrollTop|
 }, NEAR)
-
-// The expand button increases the scrollHeight of the minimized code viewport
-store-property: (".scraped-example-list > .scraped-example pre", {"offsetHeight": smallOffsetHeight})
-assert-property: (".scraped-example-list > .scraped-example .src-line-numbers", {
-    "scrollHeight": |smallOffsetHeight|
-}, NEAR)
-assert-property: (".scraped-example-list > .scraped-example .rust", {
-    "scrollHeight": |smallOffsetHeight|
-}, NEAR)
-focus: ".scraped-example-list > .scraped-example .expand"
-press-key: "Enter"
-assert-property-false: (".scraped-example-list > .scraped-example .src-line-numbers", {
-    "offsetHeight": |smallOffsetHeight|
-}, NEAR)
-assert-property-false: (".scraped-example-list > .scraped-example .rust", {
-    "offsetHeight": |smallOffsetHeight|
-}, NEAR)
-store-property: (".scraped-example-list > .scraped-example .src-line-numbers", {
-    "offsetHeight": fullOffsetHeight,
-})
-assert-property: (".scraped-example-list > .scraped-example .rust", {
-    "offsetHeight": |fullOffsetHeight|,
-    "scrollHeight": |fullOffsetHeight|,
-})
-assert-property: (".scraped-example-list > .scraped-example .src-line-numbers", {
-    "scrollHeight": |fullOffsetHeight|
-}, NEAR)
diff --git a/tests/rustdoc-gui/scrape-examples-layout.goml b/tests/rustdoc-gui/scrape-examples-layout.goml
index 5187ac486b088..85a3b2a628734 100644
--- a/tests/rustdoc-gui/scrape-examples-layout.goml
+++ b/tests/rustdoc-gui/scrape-examples-layout.goml
@@ -3,35 +3,38 @@ go-to: "file://" + |DOC_PATH| + "/scrape_examples/fn.test_many.html"
 
 set-window-size: (1000, 1000)
 
+// We move the mouse over the scraped example for the prev button to be generated.
+move-cursor-to: ".scraped-example"
+
 // Check that it's not zero.
 assert-property-false: (
-    ".more-scraped-examples .scraped-example .src-line-numbers",
+    ".more-scraped-examples .scraped-example span[data-nosnippet]",
     {"clientWidth": "0"}
 )
 
 // Check that examples with very long lines have the same width as ones that don't.
 store-property: (
-    ".more-scraped-examples .scraped-example:nth-child(2) .src-line-numbers",
+    ".more-scraped-examples .scraped-example:nth-child(2) span[data-nosnippet]",
     {"clientWidth": clientWidth},
 )
 
 assert-property: (
-    ".more-scraped-examples .scraped-example:nth-child(3) .src-line-numbers",
+    ".more-scraped-examples .scraped-example:nth-child(3) span[data-nosnippet]",
     {"clientWidth": |clientWidth|}
 )
 
 assert-property: (
-    ".more-scraped-examples .scraped-example:nth-child(4) .src-line-numbers",
+    ".more-scraped-examples .scraped-example:nth-child(4) span[data-nosnippet]",
     {"clientWidth": |clientWidth|}
 )
 
 assert-property: (
-    ".more-scraped-examples .scraped-example:nth-child(5) .src-line-numbers",
+    ".more-scraped-examples .scraped-example:nth-child(5) span[data-nosnippet]",
     {"clientWidth": |clientWidth|}
 )
 
 assert-property: (
-    ".more-scraped-examples .scraped-example:nth-child(6) .src-line-numbers",
+    ".more-scraped-examples .scraped-example:nth-child(6) span[data-nosnippet]",
     {"clientWidth": |clientWidth|}
 )
 
@@ -55,25 +58,6 @@ assert-size: (".more-scraped-examples .scraped-example .example-wrap", {
     "width": |width|,
 })
 
-// Check that the expand button works and also that line number aligns with code.
-move-cursor-to: ".scraped-example .rust"
-click: ".scraped-example .button-holder .expand"
-wait-for: ".scraped-example.expanded"
-// They should have the same y position.
-compare-elements-position: (
-    ".scraped-example.expanded .src-line-numbers pre span",
-    ".scraped-example.expanded .rust code",
-    ["y"],
-)
-// And they should have the same height.
-compare-elements-size: (
-    ".scraped-example.expanded .src-line-numbers",
-    ".scraped-example.expanded .rust",
-    ["height"],
-)
-// Collapse code again.
-click: ".scraped-example .button-holder .expand"
-
 // Check that for both mobile and desktop sizes, the buttons in scraped examples are displayed
 // correctly.
 
@@ -98,7 +82,7 @@ define-function: (
     [],
     block {
         // Title should be above the code.
-        store-position: (".scraped-example .example-wrap .src-line-numbers", {"x": x, "y": y})
+        store-position: (".scraped-example .example-wrap", {"x": x, "y": y})
         store-size: (".scraped-example .scraped-example-title", { "height": title_height })
 
         assert-position: (".scraped-example .scraped-example-title", {
@@ -107,10 +91,13 @@ define-function: (
         })
 
         // Line numbers should be right beside the code.
-        compare-elements-position: (
-            ".scraped-example .example-wrap .src-line-numbers",
-            ".scraped-example .example-wrap .rust",
-            ["y"],
+        compare-elements-position-near: (
+            ".scraped-example .example-wrap span[data-nosnippet]",
+            // On the first line, the code starts with `fn main` so we have a keyword.
+            ".scraped-example .example-wrap .rust span.kw",
+            // They're not exactly the same size but since they're on the same line,
+            // it's kinda the same.
+            {"y": 2},
         )
     }
 )
diff --git a/tests/rustdoc-gui/source-anchor-scroll.goml b/tests/rustdoc-gui/source-anchor-scroll.goml
index 4ad65bbbd61ac..c005af1e7a1c6 100644
--- a/tests/rustdoc-gui/source-anchor-scroll.goml
+++ b/tests/rustdoc-gui/source-anchor-scroll.goml
@@ -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": "208"})
+assert-property: ("html", {"scrollTop": "206"})
 click: '//a[text() = "bar" and @href="#28-36"]'
 assert-property: ("html", {"scrollTop": "239"})
 click: '//a[normalize-space() = "sub_fn" and @href="#2-4"]'
-assert-property: ("html", {"scrollTop": "136"})
+assert-property: ("html", {"scrollTop": "134"})
 
 // 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": "136"})
+assert-property: ("html", {"scrollTop": "134"})
diff --git a/tests/rustdoc-gui/source-code-page-code-scroll.goml b/tests/rustdoc-gui/source-code-page-code-scroll.goml
index 60012db6c8c8b..c15a9ae7983c6 100644
--- a/tests/rustdoc-gui/source-code-page-code-scroll.goml
+++ b/tests/rustdoc-gui/source-code-page-code-scroll.goml
@@ -2,7 +2,7 @@
 go-to: "file://" + |DOC_PATH| + "/src/test_docs/lib.rs.html"
 set-window-size: (800, 1000)
 // "scrollWidth" should be superior than "clientWidth".
-assert-property: ("body", {"scrollWidth": 1776, "clientWidth": 800})
+assert-property: ("body", {"scrollWidth": 1780, "clientWidth": 800})
 
 // Both properties should be equal (ie, no scroll on the code block).
-assert-property: (".example-wrap .rust", {"scrollWidth": 1662, "clientWidth": 1662})
+assert-property: (".example-wrap .rust", {"scrollWidth": 1715, "clientWidth": 1715})
diff --git a/tests/rustdoc-gui/source-code-page.goml b/tests/rustdoc-gui/source-code-page.goml
index afb194625210c..aa5a16aac7048 100644
--- a/tests/rustdoc-gui/source-code-page.goml
+++ b/tests/rustdoc-gui/source-code-page.goml
@@ -3,7 +3,7 @@ include: "utils.goml"
 go-to: "file://" + |DOC_PATH| + "/src/test_docs/lib.rs.html"
 show-text: true
 // Check that we can click on the line number.
-click: ".src-line-numbers > a:nth-child(4)" // This is the anchor for line 4.
+click: "//a[@data-nosnippet and text()='4']" // This is the anchor for line 4.
 // Ensure that the page URL was updated.
 assert-document-property: ({"URL": "lib.rs.html#4"}, ENDS_WITH)
 assert-attribute: ("//*[@id='4']", {"class": "line-highlighted"})
@@ -14,11 +14,11 @@ assert-attribute: ("//*[@id='4']", {"class": "line-highlighted"})
 assert-css: ("//*[@id='4']", {"border-right-width": "0px"})
 // We now check that the good anchors are highlighted
 go-to: "file://" + |DOC_PATH| + "/src/test_docs/lib.rs.html#4-6"
-assert-attribute-false: (".src-line-numbers > a:nth-child(3)", {"class": "line-highlighted"})
-assert-attribute: (".src-line-numbers > a:nth-child(4)", {"class": "line-highlighted"})
-assert-attribute: (".src-line-numbers > a:nth-child(5)", {"class": "line-highlighted"})
-assert-attribute: (".src-line-numbers > a:nth-child(6)", {"class": "line-highlighted"})
-assert-attribute-false: (".src-line-numbers > a:nth-child(7)", {"class": "line-highlighted"})
+assert-attribute-false: ("//a[@data-nosnippet and text()='3']", {"class": "line-highlighted"})
+assert-attribute: ("//a[@data-nosnippet and text()='4']", {"class": "line-highlighted"})
+assert-attribute: ("//a[@data-nosnippet and text()='5']", {"class": "line-highlighted"})
+assert-attribute: ("//a[@data-nosnippet and text()='6']", {"class": "line-highlighted"})
+assert-attribute-false: ("//a[@data-nosnippet and text()='7']", {"class": "line-highlighted"})
 
 define-function: (
     "check-colors",
@@ -26,12 +26,12 @@ define-function: (
     block {
         call-function: ("switch-theme", {"theme": |theme|})
         assert-css: (
-            ".src-line-numbers > a:not(.line-highlighted)",
+            "a[data-nosnippet]:not(.line-highlighted)",
             {"color": |color|, "background-color": |background_color|},
             ALL,
         )
         assert-css: (
-            ".src-line-numbers > a.line-highlighted",
+            "a[data-nosnippet].line-highlighted",
             {"color": |highlight_color|, "background-color": |highlight_background_color|},
             ALL,
         )
@@ -61,37 +61,37 @@ call-function: ("check-colors", {
 })
 
 // This is to ensure that the content is correctly align with the line numbers.
-compare-elements-position: ("//*[@id='1']", ".rust > code > span", ["y"])
+compare-elements-position-near: ("//*[@id='1']", ".rust > code > span", {"y": 2})
 // Check the `href` property so that users can treat anchors as links.
-assert-property: (".src-line-numbers > a:nth-child(1)", {
+assert-property: ("//a[@data-nosnippet and text()='1']", {
     "href": |DOC_PATH| + "/src/test_docs/lib.rs.html#1"
 }, ENDS_WITH)
-assert-property: (".src-line-numbers > a:nth-child(2)", {
+assert-property: ("//a[@data-nosnippet and text()='2']", {
     "href": |DOC_PATH| + "/src/test_docs/lib.rs.html#2"
 }, ENDS_WITH)
-assert-property: (".src-line-numbers > a:nth-child(3)", {
+assert-property: ("//a[@data-nosnippet and text()='3']", {
     "href": |DOC_PATH| + "/src/test_docs/lib.rs.html#3"
 }, ENDS_WITH)
-assert-property: (".src-line-numbers > a:nth-child(4)", {
+assert-property: ("//a[@data-nosnippet and text()='4']", {
     "href": |DOC_PATH| + "/src/test_docs/lib.rs.html#4"
 }, ENDS_WITH)
-assert-property: (".src-line-numbers > a:nth-child(5)", {
+assert-property: ("//a[@data-nosnippet and text()='5']", {
     "href": |DOC_PATH| + "/src/test_docs/lib.rs.html#5"
 }, ENDS_WITH)
-assert-property: (".src-line-numbers > a:nth-child(6)", {
+assert-property: ("//a[@data-nosnippet and text()='6']", {
     "href": |DOC_PATH| + "/src/test_docs/lib.rs.html#6"
 }, ENDS_WITH)
 
 // Assert that the line numbers text is aligned to the right.
-assert-css: (".src-line-numbers", {"text-align": "right"})
+assert-css: ("a[data-nosnippet]", {"text-align": "right"}, ALL)
 
 // Now let's check that clicking on something else than the line number doesn't
 // 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": 88, "y": 171})
-// We click on the left of the "1" anchor but still in the "src-line-number" `
`.
-click: (163, 77)
+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-document-property: ({"URL": "/lib.rs.html"}, ENDS_WITH)
 
 // Checking the source code sidebar.
diff --git a/tests/rustdoc/check-source-code-urls-to-def.rs b/tests/rustdoc/check-source-code-urls-to-def.rs
index 8703287abc553..d701b88bf9fd0 100644
--- a/tests/rustdoc/check-source-code-urls-to-def.rs
+++ b/tests/rustdoc/check-source-code-urls-to-def.rs
@@ -31,7 +31,8 @@ fn babar() {}
 //@ has - '//pre[@class="rust"]//a/@href' '/struct.String.html'
 //@ has - '//pre[@class="rust"]//a/@href' '/primitive.u32.html'
 //@ has - '//pre[@class="rust"]//a/@href' '/primitive.str.html'
-//@ count - '//pre[@class="rust"]//a[@href="#23"]' 5
+// The 5 links to line 23 and the line 23 itself.
+//@ count - '//pre[@class="rust"]//a[@href="#23"]' 6
 //@ has - '//pre[@class="rust"]//a[@href="../../source_code/struct.SourceCode.html"]' \
 //        'source_code::SourceCode'
 pub fn foo(a: u32, b: &str, c: String, d: Foo, e: bar::Bar, f: source_code::SourceCode) {
@@ -50,8 +51,8 @@ pub fn foo2(t: &T, v: &V, b: bool) {}
 pub trait AnotherTrait {}
 pub trait WhyNot {}
 
-//@ has - '//pre[@class="rust"]//a[@href="#50"]' 'AnotherTrait'
-//@ has - '//pre[@class="rust"]//a[@href="#51"]' 'WhyNot'
+//@ has - '//pre[@class="rust"]//a[@href="#51"]' 'AnotherTrait'
+//@ has - '//pre[@class="rust"]//a[@href="#52"]' 'WhyNot'
 pub fn foo3(t: &T, v: &V)
 where
     T: AnotherTrait,
@@ -60,7 +61,7 @@ where
 
 pub trait AnotherTrait2 {}
 
-//@ has - '//pre[@class="rust"]//a[@href="#61"]' 'AnotherTrait2'
+//@ has - '//pre[@class="rust"]//a[@href="#62"]' 'AnotherTrait2'
 pub fn foo4() {
     let x: Vec<&dyn AnotherTrait2> = Vec::new();
 }
diff --git a/tests/rustdoc/source-line-numbers.rs b/tests/rustdoc/source-line-numbers.rs
new file mode 100644
index 0000000000000..0b654b1a00476
--- /dev/null
+++ b/tests/rustdoc/source-line-numbers.rs
@@ -0,0 +1,35 @@
+// This test ensures that we have the expected number of line generated.
+
+#![crate_name = "foo"]
+
+//@ has 'src/foo/source-line-numbers.rs.html'
+//@ count - '//a[@data-nosnippet]' 35
+//@ has - '//a[@id="35"]' '35'
+
+#[
+macro_export
+]
+macro_rules! bar {
+    ($x:ident) => {{
+        $x += 2;
+        $x *= 2;
+    }}
+}
+
+/*
+multi line
+comment
+*/
+fn x(_: u8, _: u8) {}
+
+fn foo() {
+    let mut y = 0;
+    bar!(y);
+    println!("
+    {y}
+    ");
+    x(
+      1,
+      2,
+    );
+}