From 06fe27866920b3eaf6502f321ccb239cc617db10 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 26 Oct 2020 15:01:39 +0100 Subject: [PATCH] Fix unindent behavior between different doc comments --- src/librustdoc/passes/unindent_comments.rs | 131 ++++++++++++--------- 1 file changed, 75 insertions(+), 56 deletions(-) diff --git a/src/librustdoc/passes/unindent_comments.rs b/src/librustdoc/passes/unindent_comments.rs index a9cf5a87f549..1c856b1da53f 100644 --- a/src/librustdoc/passes/unindent_comments.rs +++ b/src/librustdoc/passes/unindent_comments.rs @@ -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; @@ -35,65 +34,85 @@ impl clean::Attributes { } fn unindent_fragments(docs: &mut Vec) { - for fragment in docs { - fragment.doc = unindent(&fragment.doc); - } -} - -fn unindent(s: &str) -> String { - let lines = s.lines().collect::>(); 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()); - - let min_indent = if ignore_previous_indents { usize::MAX } else { min_indent }; - - if saw_first_line { - saw_second_line = true; - } - if line.chars().all(|c| c.is_whitespace()) { - min_indent - } 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 + let add = if !docs.windows(2).all(|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 = match docs + .iter() + .map(|fragment| { + fragment.doc.lines().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()); + + let min_indent = if ignore_previous_indents { usize::MAX } else { min_indent }; + + if saw_first_line { + saw_second_line = true; + } + + if line.chars().all(|c| c.is_whitespace()) { + min_indent } else { - false + saw_first_line = true; + // 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 } } - }); - cmp::min(min_indent, whitespace) + }) + }) + .min() + { + Some(x) => x, + None => return, + }; + + let mut first_ignored = false; + for fragment in docs { + let lines: Vec<_> = fragment.doc.lines().collect(); + + if !lines.is_empty() { + let min_indent = if fragment.kind != DocFragmentKind::SugaredDoc && min_indent > 0 { + min_indent - add + } else { + min_indent + }; + + let mut iter = lines.iter(); + let mut result = if !first_ignored { + first_ignored = true; + vec![iter.next().unwrap().trim_start().to_string()] + } else { + Vec::new() + }; + result.extend_from_slice( + &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::>(), + ); + fragment.doc = result.join("\n"); } - }); - - 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::>(), - ); - unindented.join("\n") - } else { - s.to_string() } }