Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Closed
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
06fe278
Fix unindent behavior between different doc comments
GuillaumeGomez Oct 26, 2020
b4c3536
Add test for doc comments unindent fix
GuillaumeGomez Oct 26, 2020
f3e6d88
Fix typos and replace static vector with slice
bugadani Oct 28, 2020
a21f3a7
Clean up encode_dep_graph
bugadani Oct 28, 2020
5248b20
Reuse memory
bugadani Oct 28, 2020
2fa3598
Avoid reallocating cgu_path_components
bugadani Oct 28, 2020
a8803d3
Delete files immediately, instead of collecting into vector
bugadani Oct 28, 2020
6bbb7fd
Change a bit how the first doc comment lines are handled
GuillaumeGomez Oct 29, 2020
fcee70f
Update tests
GuillaumeGomez Oct 29, 2020
ad27894
Move compiletest meta tests to a separate directory
tmiasko Oct 30, 2020
affb47f
Add a test for compiletest rustc-env & unset-rustc-env directives
tmiasko Oct 30, 2020
289c0d8
Retagging: do not retag 'raw reborrows'
RalfJung Oct 31, 2020
fef9c63
Rust coverage before splitting instrument_coverage.rs
richkadel Oct 5, 2020
0bb09c1
Splitting transform/instrument_coverage.rs into transform/coverage/...
richkadel Oct 23, 2020
7b87ae4
Implemented CoverageGraph of BasicCoverageBlocks
richkadel Oct 23, 2020
af0c84c
Adds coverage graphviz
richkadel Oct 23, 2020
868de57
Injecting expressions in place of counters where helpful
richkadel Oct 22, 2020
da20b67
Added comments on remapping expression IDs, and URL to spanviews
richkadel Oct 25, 2020
0edf4a5
Addressed all feedback to date
richkadel Oct 25, 2020
3685689
Responded to all feedback as of 2020-10-30
richkadel Oct 30, 2020
87f2897
Improve code in unindent_comment a bit more
GuillaumeGomez Nov 1, 2020
c0cbf63
inliner: Remove redundant loop
tmiasko Nov 2, 2020
a8dfb26
Document -Zinstrument-coverage
richkadel Oct 31, 2020
c83c635
Fix intrinsic size_of stable link
pickfire Nov 2, 2020
e78e9d4
Treat trailing semicolon as a statement in macro call
Aaron1011 Oct 25, 2020
6d94911
addressed feedback
richkadel Nov 2, 2020
3d7baee
fix cross-platform test bugs
richkadel Nov 2, 2020
974c03d
Rollup merge of #78267 - richkadel:llvm-coverage-counters-2.0.3r1, r=…
Dylan-DPC Nov 3, 2020
3a110ef
Rollup merge of #78376 - Aaron1011:feature/consistent-empty-expr, r=p…
Dylan-DPC Nov 3, 2020
1425ac0
Rollup merge of #78400 - GuillaumeGomez:fix-unindent, r=jyn514
Dylan-DPC Nov 3, 2020
01e7c01
Rollup merge of #78489 - bugadani:array, r=estebank
Dylan-DPC Nov 3, 2020
5f0c9c1
Rollup merge of #78575 - tmiasko:compiletest-rustc-env, r=Aaron1011
Dylan-DPC Nov 3, 2020
8088713
Rollup merge of #78597 - RalfJung:raw-retag, r=oli-obk
Dylan-DPC Nov 3, 2020
d56e305
Rollup merge of #78616 - richkadel:unstable-book-instr-cov, r=tmandry
Dylan-DPC Nov 3, 2020
bb3ef75
Rollup merge of #78664 - pickfire:patch-4, r=jonas-schievink
Dylan-DPC Nov 3, 2020
04245d1
Rollup merge of #78668 - tmiasko:inline, r=oli-obk
Dylan-DPC Nov 3, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 70 additions & 55 deletions src/librustdoc/passes/unindent_comments.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use std::cmp;
use std::string::String;

use crate::clean::{self, DocFragment, Item};
use crate::clean::{self, DocFragment, DocFragmentKind, Item};
use crate::core::DocContext;
use crate::fold::{self, DocFolder};
use crate::passes::Pass;
Expand Down Expand Up @@ -35,65 +34,81 @@ impl clean::Attributes {
}

fn unindent_fragments(docs: &mut Vec<DocFragment>) {
for fragment in docs {
fragment.doc = unindent(&fragment.doc);
}
}

fn unindent(s: &str) -> String {
let lines = s.lines().collect::<Vec<&str>>();
let mut saw_first_line = false;
let mut saw_second_line = false;
let min_indent = lines.iter().fold(usize::MAX, |min_indent, line| {
// After we see the first non-whitespace line, look at
// the line we have. If it is not whitespace, and therefore
// part of the first paragraph, then ignore the indentation
// level of the first line
let ignore_previous_indents =
saw_first_line && !saw_second_line && !line.chars().all(|c| c.is_whitespace());
// `add` is used in case the most common sugared doc syntax is used ("/// "). The other
// fragments kind's lines are never starting with a whitespace unless they are using some
// markdown formatting requiring it. Therefore, if the doc block have a mix between the two,
// we need to take into account the fact that the minimum indent minus one (to take this
// whitespace into account).
//
// For example:
//
// /// hello!
// #[doc = "another"]
//
// In this case, you want "hello! another" and not "hello! another".
let add = if docs.windows(2).any(|arr| arr[0].kind != arr[1].kind)
&& docs.iter().any(|d| d.kind == DocFragmentKind::SugaredDoc)
{
// In case we have a mix of sugared doc comments and "raw" ones, we want the sugared one to
// "decide" how much the minimum indent will be.
1
} else {
0
};

let min_indent = if ignore_previous_indents { usize::MAX } else { min_indent };
// `min_indent` is used to know how much whitespaces from the start of each lines must be
// removed. Example:
//
// /// hello!
// #[doc = "another"]
//
// In here, the `min_indent` is 1 (because non-sugared fragment are always counted with minimum
// 1 whitespace), meaning that "hello!" will be considered a codeblock because it starts with 4
// (5 - 1) whitespaces.
let min_indent = match docs
.iter()
.map(|fragment| {
fragment.doc.lines().fold(usize::MAX, |min_indent, line| {
if line.chars().all(|c| c.is_whitespace()) {
min_indent
} else {
// Compare against either space or tab, ignoring whether they are
// mixed or not.
let whitespace = line.chars().take_while(|c| *c == ' ' || *c == '\t').count();
cmp::min(min_indent, whitespace)
+ if fragment.kind == DocFragmentKind::SugaredDoc { 0 } else { add }
}
})
})
.min()
{
Some(x) => x,
None => return,
};

if saw_first_line {
saw_second_line = true;
for fragment in docs {
if fragment.doc.lines().count() == 0 {
continue;
}

if line.chars().all(|c| c.is_whitespace()) {
min_indent
let min_indent = if fragment.kind != DocFragmentKind::SugaredDoc && min_indent > 0 {
min_indent - add
} else {
saw_first_line = true;
let mut whitespace = 0;
line.chars().all(|char| {
// Compare against either space or tab, ignoring whether they
// are mixed or not
if char == ' ' || char == '\t' {
whitespace += 1;
true
min_indent
};

fragment.doc = fragment
.doc
.lines()
.map(|line| {
if line.chars().all(|c| c.is_whitespace()) {
line.to_string()
} else {
false
assert!(line.len() >= min_indent);
line[min_indent..].to_string()
}
});
cmp::min(min_indent, whitespace)
}
});

if !lines.is_empty() {
let mut unindented = vec![lines[0].trim_start().to_string()];
unindented.extend_from_slice(
&lines[1..]
.iter()
.map(|&line| {
if line.chars().all(|c| c.is_whitespace()) {
line.to_string()
} else {
assert!(line.len() >= min_indent);
line[min_indent..].to_string()
}
})
.collect::<Vec<_>>(),
);
unindented.join("\n")
} else {
s.to_string()
})
.collect::<Vec<_>>()
.join("\n");
}
}
65 changes: 28 additions & 37 deletions src/librustdoc/passes/unindent_comments/tests.rs
Original file line number Diff line number Diff line change
@@ -1,72 +1,63 @@
use super::*;
use rustc_span::source_map::DUMMY_SP;

fn create_doc_fragment(s: &str) -> Vec<DocFragment> {
vec![DocFragment {
line: 0,
span: DUMMY_SP,
parent_module: None,
doc: s.to_string(),
kind: DocFragmentKind::SugaredDoc,
}]
}

#[track_caller]
fn run_test(input: &str, expected: &str) {
let mut s = create_doc_fragment(input);
unindent_fragments(&mut s);
assert_eq!(s[0].doc, expected);
}

#[test]
fn should_unindent() {
let s = " line1\n line2".to_string();
let r = unindent(&s);
assert_eq!(r, "line1\nline2");
run_test(" line1\n line2", "line1\nline2");
}

#[test]
fn should_unindent_multiple_paragraphs() {
let s = " line1\n\n line2".to_string();
let r = unindent(&s);
assert_eq!(r, "line1\n\nline2");
run_test(" line1\n\n line2", "line1\n\nline2");
}

#[test]
fn should_leave_multiple_indent_levels() {
// Line 2 is indented another level beyond the
// base indentation and should be preserved
let s = " line1\n\n line2".to_string();
let r = unindent(&s);
assert_eq!(r, "line1\n\n line2");
run_test(" line1\n\n line2", "line1\n\n line2");
}

#[test]
fn should_ignore_first_line_indent() {
// The first line of the first paragraph may not be indented as
// far due to the way the doc string was written:
//
// #[doc = "Start way over here
// and continue here"]
let s = "line1\n line2".to_string();
let r = unindent(&s);
assert_eq!(r, "line1\nline2");
run_test("line1\n line2", "line1\n line2");
}

#[test]
fn should_not_ignore_first_line_indent_in_a_single_line_para() {
let s = "line1\n\n line2".to_string();
let r = unindent(&s);
assert_eq!(r, "line1\n\n line2");
run_test("line1\n\n line2", "line1\n\n line2");
}

#[test]
fn should_unindent_tabs() {
let s = "\tline1\n\tline2".to_string();
let r = unindent(&s);
assert_eq!(r, "line1\nline2");
run_test("\tline1\n\tline2", "line1\nline2");
}

#[test]
fn should_trim_mixed_indentation() {
let s = "\t line1\n\t line2".to_string();
let r = unindent(&s);
assert_eq!(r, "line1\nline2");

let s = " \tline1\n \tline2".to_string();
let r = unindent(&s);
assert_eq!(r, "line1\nline2");
run_test("\t line1\n\t line2", "line1\nline2");
run_test(" \tline1\n \tline2", "line1\nline2");
}

#[test]
fn should_not_trim() {
let s = "\t line1 \n\t line2".to_string();
let r = unindent(&s);
assert_eq!(r, "line1 \nline2");

let s = " \tline1 \n \tline2".to_string();
let r = unindent(&s);
assert_eq!(r, "line1 \nline2");
run_test("\t line1 \n\t line2", "line1 \nline2");
run_test(" \tline1 \n \tline2", "line1 \nline2");
}
1 change: 1 addition & 0 deletions src/test/rustdoc/unindent.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Just some text.
64 changes: 64 additions & 0 deletions src/test/rustdoc/unindent.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#![feature(external_doc)]

#![crate_name = "foo"]

// @has foo/struct.Example.html
// @matches - '//pre[@class="rust rust-example-rendered"]' \
// '(?m)let example = Example::new\(\)\n \.first\(\)\n \.second\(\)\n \.build\(\);\Z'
/// ```rust
/// let example = Example::new()
/// .first()
#[cfg_attr(not(feature = "one"), doc = " .second()")]
/// .build();
/// ```
pub struct Example;

// @has foo/struct.F.html
// @matches - '//pre[@class="rust rust-example-rendered"]' \
// '(?m)let example = Example::new\(\)\n \.first\(\)\n \.another\(\)\n \.build\(\);\Z'
///```rust
///let example = Example::new()
/// .first()
#[cfg_attr(not(feature = "one"), doc = " .another()")]
/// .build();
/// ```
pub struct F;

// @has foo/struct.G.html
// @matches - '//pre[@class="rust rust-example-rendered"]' \
// '(?m)let example = Example::new\(\)\n\.first\(\)\n \.another\(\)\n\.build\(\);\Z'
///```rust
///let example = Example::new()
///.first()
#[cfg_attr(not(feature = "one"), doc = " .another()")]
///.build();
///```
pub struct G;

// @has foo/struct.H.html
// @has - '//div[@class="docblock"]/p' 'no whitespace lol'
///no whitespace
#[doc = " lol"]
pub struct H;

// @has foo/struct.I.html
// @matches - '//pre[@class="rust rust-example-rendered"]' '(?m)4 whitespaces!\Z'
/// 4 whitespaces!
#[doc = "something"]
pub struct I;

// @has foo/struct.J.html
// @matches - '//div[@class="docblock"]/p' '(?m)a\nno whitespace\nJust some text.\Z'
///a
///no whitespace
#[doc(include = "unindent.md")]
pub struct J;

// @has foo/struct.K.html
// @matches - '//pre[@class="rust rust-example-rendered"]' '(?m)4 whitespaces!\Z'
///a
///
/// 4 whitespaces!
///
#[doc(include = "unindent.md")]
pub struct K;