about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorStéphane Campinas <stephane.campinas@gmail.com>2018-10-08 11:44:23 +0200
committerStéphane Campinas <stephane.campinas@gmail.com>2018-10-09 00:44:59 +0200
commitc0b7222e2d33843102060d222fbe60a55ec438b5 (patch)
tree7a6069a5486b1d1cf558a561abcf8a7491ac21ed /src
parent8b3788d875a921e367c7df014eb2456cdde30eac (diff)
handle itemized items inside comments
Diffstat (limited to 'src')
-rw-r--r--src/comment.rs175
1 files changed, 134 insertions, 41 deletions
diff --git a/src/comment.rs b/src/comment.rs
index d4af68e7d25..41301527653 100644
--- a/src/comment.rs
+++ b/src/comment.rs
@@ -433,6 +433,58 @@ impl CodeBlockAttribute {
     }
 }
 
+/// Block that is formatted as an item.
+///
+/// An item starts with either a star `*` or a dash `-`. Different level of indentation are
+/// handled.
+struct ItemizedBlock {
+    /// the number of whitespaces up to the item sigil
+    indent: usize,
+    /// the string that marks the start of an item
+    opener: String,
+    /// sequence of whitespaces to prefix new lines that are part of the item
+    line_start: String,
+}
+
+impl ItemizedBlock {
+    /// Returns true if the line is formatted as an item
+    fn is_itemized_line(line: &str) -> bool {
+        let trimmed = line.trim_left();
+        trimmed.starts_with("* ") || trimmed.starts_with("- ")
+    }
+
+    /// Creates a new ItemizedBlock described with the given line.
+    /// The `is_itemized_line` needs to be called first.
+    fn new(line: &str) -> ItemizedBlock {
+        let space_to_sigil = line.chars().take_while(|c| c.is_whitespace()).count();
+        let indent = space_to_sigil + 2;
+        ItemizedBlock {
+            indent,
+            opener: line[..indent].to_string(),
+            line_start: " ".repeat(indent),
+        }
+    }
+
+    /// Returns a `StringFormat` used for formatting the content of an item
+    fn create_string_format<'a>(&'a self, fmt: &'a StringFormat) -> StringFormat<'a> {
+        StringFormat {
+            opener: "",
+            closer: "",
+            line_start: "",
+            line_end: "",
+            shape: Shape::legacy(fmt.shape.width.saturating_sub(self.indent), Indent::empty()),
+            trim_end: true,
+            config: fmt.config,
+        }
+    }
+
+    /// Returns true if the line is part of the current itemized block
+    fn in_block(&self, line: &str) -> bool {
+        !ItemizedBlock::is_itemized_line(line)
+            && self.indent <= line.chars().take_while(|c| c.is_whitespace()).count()
+    }
+}
+
 fn rewrite_comment_inner(
     orig: &str,
     block_style: bool,
@@ -493,15 +545,17 @@ fn rewrite_comment_inner(
     let mut code_block_buffer = String::with_capacity(128);
     let mut is_prev_line_multi_line = false;
     let mut code_block_attr = None;
+    let mut item_block_buffer = String::with_capacity(128);
+    let mut item_block: Option<ItemizedBlock> = None;
     let comment_line_separator = format!("{}{}", indent_str, line_start);
-    let join_code_block_with_comment_line_separator = |s: &str| {
+    let join_block = |s: &str, sep: &str| {
         let mut result = String::with_capacity(s.len() + 128);
         let mut iter = s.lines().peekable();
         while let Some(line) = iter.next() {
             result.push_str(line);
             result.push_str(match iter.peek() {
-                Some(next_line) if next_line.is_empty() => comment_line_separator.trim_right(),
-                Some(..) => &comment_line_separator,
+                Some(next_line) if next_line.is_empty() => sep.trim_right(),
+                Some(..) => &sep,
                 None => "",
             });
         }
@@ -511,7 +565,26 @@ fn rewrite_comment_inner(
     for (i, (line, has_leading_whitespace)) in lines.enumerate() {
         let is_last = i == count_newlines(orig);
 
-        if let Some(ref attr) = code_block_attr {
+        if let Some(ref ib) = item_block {
+            if ib.in_block(&line) {
+                item_block_buffer.push_str(&line);
+                item_block_buffer.push('\n');
+                continue;
+            }
+            is_prev_line_multi_line = false;
+            fmt.shape = Shape::legacy(max_chars, fmt_indent);
+            let item_fmt = ib.create_string_format(&fmt);
+            result.push_str(&comment_line_separator);
+            result.push_str(&ib.opener);
+            match rewrite_string(&item_block_buffer.replace("\n", " "), &item_fmt) {
+                Some(s) => result.push_str(&join_block(
+                    &s,
+                    &format!("{}{}", &comment_line_separator, ib.line_start),
+                )),
+                None => result.push_str(&join_block(&item_block_buffer, &comment_line_separator)),
+            };
+            item_block_buffer.clear();
+        } else if let Some(ref attr) = code_block_attr {
             if line.starts_with("```") {
                 let code_block = match attr {
                     CodeBlockAttribute::Ignore | CodeBlockAttribute::Text => {
@@ -529,7 +602,7 @@ fn rewrite_comment_inner(
                 };
                 if !code_block.is_empty() {
                     result.push_str(&comment_line_separator);
-                    result.push_str(&join_code_block_with_comment_line_separator(&code_block));
+                    result.push_str(&join_block(&code_block, &comment_line_separator));
                 }
                 code_block_buffer.clear();
                 result.push_str(&comment_line_separator);
@@ -538,46 +611,42 @@ fn rewrite_comment_inner(
             } else {
                 code_block_buffer.push_str(&hide_sharp_behind_comment(line));
                 code_block_buffer.push('\n');
-
-                if is_last {
-                    // There is a code block that is not properly enclosed by backticks.
-                    // We will leave them untouched.
-                    result.push_str(&comment_line_separator);
-                    result.push_str(&join_code_block_with_comment_line_separator(
-                        &trim_custom_comment_prefix(&code_block_buffer),
-                    ));
-                }
             }
+            continue;
+        }
 
+        code_block_attr = None;
+        item_block = None;
+        if line.starts_with("```") {
+            code_block_attr = Some(CodeBlockAttribute::new(&line[3..]))
+        } else if config.wrap_comments() && ItemizedBlock::is_itemized_line(&line) {
+            let ib = ItemizedBlock::new(&line);
+            item_block_buffer.push_str(&line[ib.indent..]);
+            item_block_buffer.push('\n');
+            item_block = Some(ib);
             continue;
-        } else {
-            code_block_attr = if line.starts_with("```") {
-                Some(CodeBlockAttribute::new(&line[3..]))
-            } else {
-                None
-            };
+        }
 
-            if result == opener {
-                let force_leading_whitespace = opener == "/* " && count_newlines(orig) == 0;
-                if !has_leading_whitespace && !force_leading_whitespace && result.ends_with(' ') {
-                    result.pop();
-                }
-                if line.is_empty() {
-                    continue;
-                }
-            } else if is_prev_line_multi_line && !line.is_empty() {
-                result.push(' ')
-            } else if is_last && line.is_empty() {
-                // trailing blank lines are unwanted
-                if !closer.is_empty() {
-                    result.push_str(&indent_str);
-                }
-                break;
-            } else {
-                result.push_str(&comment_line_separator);
-                if !has_leading_whitespace && result.ends_with(' ') {
-                    result.pop();
-                }
+        if result == opener {
+            let force_leading_whitespace = opener == "/* " && count_newlines(orig) == 0;
+            if !has_leading_whitespace && !force_leading_whitespace && result.ends_with(' ') {
+                result.pop();
+            }
+            if line.is_empty() {
+                continue;
+            }
+        } else if is_prev_line_multi_line && !line.is_empty() {
+            result.push(' ')
+        } else if is_last && line.is_empty() {
+            // trailing blank lines are unwanted
+            if !closer.is_empty() {
+                result.push_str(&indent_str);
+            }
+            break;
+        } else {
+            result.push_str(&comment_line_separator);
+            if !has_leading_whitespace && result.ends_with(' ') {
+                result.pop();
             }
         }
 
@@ -631,6 +700,30 @@ fn rewrite_comment_inner(
             is_prev_line_multi_line = false;
         }
     }
+    if !code_block_buffer.is_empty() {
+        // There is a code block that is not properly enclosed by backticks.
+        // We will leave them untouched.
+        result.push_str(&comment_line_separator);
+        result.push_str(&join_block(
+            &trim_custom_comment_prefix(&code_block_buffer),
+            &comment_line_separator,
+        ));
+    }
+    if !item_block_buffer.is_empty() {
+        // the last few lines are part of an itemized block
+        let ib = item_block.unwrap();
+        fmt.shape = Shape::legacy(max_chars, fmt_indent);
+        let item_fmt = ib.create_string_format(&fmt);
+        result.push_str(&comment_line_separator);
+        result.push_str(&ib.opener);
+        match rewrite_string(&item_block_buffer.replace("\n", " "), &item_fmt) {
+            Some(s) => result.push_str(&join_block(
+                &s,
+                &format!("{}{}", &comment_line_separator, ib.line_start),
+            )),
+            None => result.push_str(&join_block(&item_block_buffer, &comment_line_separator)),
+        };
+    }
 
     result.push_str(closer);
     if result.ends_with(opener) && opener.ends_with(' ') {