]> git.lizzy.rs Git - rust.git/blobdiff - src/string.rs
Merge commit 'c4416f20dcaec5d93077f72470e83e150fb923b1' into sync-rustfmt
[rust.git] / src / string.rs
index cb17484173825652ae14b82510cd64b4cf5a07a0..78b72a50cb2f96250ec94b1c86579d1246798424 100644 (file)
@@ -1,13 +1,3 @@
-// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
 // Format string literals.
 
 use regex::Regex;
 const MIN_STRING: usize = 10;
 
 /// Describes the layout of a piece of text.
-pub struct StringFormat<'a> {
+pub(crate) struct StringFormat<'a> {
     /// The opening sequence of characters for the piece of text
-    pub opener: &'a str,
+    pub(crate) opener: &'a str,
     /// The closing sequence of characters for the piece of text
-    pub closer: &'a str,
+    pub(crate) closer: &'a str,
     /// The opening sequence of characters for a line
-    pub line_start: &'a str,
+    pub(crate) line_start: &'a str,
     /// The closing sequence of characters for a line
-    pub line_end: &'a str,
+    pub(crate) line_end: &'a str,
     /// The allocated box to fit the text into
-    pub shape: Shape,
+    pub(crate) shape: Shape,
     /// Trim trailing whitespaces
-    pub trim_end: bool,
-    pub config: &'a Config,
+    pub(crate) trim_end: bool,
+    pub(crate) config: &'a Config,
 }
 
 impl<'a> StringFormat<'a> {
-    pub fn new(shape: Shape, config: &'a Config) -> StringFormat<'a> {
+    pub(crate) fn new(shape: Shape, config: &'a Config) -> StringFormat<'a> {
         StringFormat {
             opener: "\"",
             closer: "\"",
@@ -67,11 +57,11 @@ fn max_width_with_indent(&self) -> Option<usize> {
     /// This allows to fit more graphemes from the string on a line when
     /// SnippetState::EndWithLineFeed.
     fn max_width_without_indent(&self) -> Option<usize> {
-        Some(self.config.max_width().checked_sub(self.line_end.len())?)
+        self.config.max_width().checked_sub(self.line_end.len())
     }
 }
 
-pub fn rewrite_string<'a>(
+pub(crate) fn rewrite_string<'a>(
     orig: &str,
     fmt: &StringFormat<'a>,
     newline_max_chars: usize,
@@ -109,7 +99,7 @@ pub fn rewrite_string<'a>(
                 if is_new_line(grapheme) {
                     // take care of blank lines
                     result = trim_end_but_line_feed(fmt.trim_end, result);
-                    result.push_str("\n");
+                    result.push('\n');
                     if !is_bareline_ok && cur_start + i + 1 < graphemes.len() {
                         result.push_str(&indent_without_newline);
                         result.push_str(fmt.line_start);
@@ -163,21 +153,22 @@ pub fn rewrite_string<'a>(
     wrap_str(result, fmt.config.max_width(), fmt.shape)
 }
 
-/// Returns the index to the end of the url if the given string includes an
-/// URL or alike. Otherwise, returns None;
+/// Returns the index to the end of the URL if the split at index of the given string includes a
+/// URL or alike. Otherwise, returns `None`.
 fn detect_url(s: &[&str], index: usize) -> Option<usize> {
     let start = match s[..=index].iter().rposition(|g| is_whitespace(g)) {
         Some(pos) => pos + 1,
         None => 0,
     };
+    // 8 = minimum length for a string to contain a URL
     if s.len() < start + 8 {
         return None;
     }
-    let prefix = s[start..start + 8].concat();
-    if prefix.starts_with("https://")
-        || prefix.starts_with("http://")
-        || prefix.starts_with("ftp://")
-        || prefix.starts_with("file://")
+    let split = s[start..].concat();
+    if split.contains("https://")
+        || split.contains("http://")
+        || split.contains("ftp://")
+        || split.contains("file://")
     {
         match s[index..].iter().position(|g| is_whitespace(g)) {
             Some(pos) => Some(index + pos - 1),
@@ -287,6 +278,9 @@ fn break_string(max_width: usize, trim_end: bool, line_end: &str, input: &[&str]
         }
         cur_index
     };
+    if max_width_index_in_input == 0 {
+        return SnippetState::EndOfInput(input.concat());
+    }
 
     // Find the position in input for breaking the string
     if line_end.is_empty()
@@ -310,7 +304,7 @@ fn break_string(max_width: usize, trim_end: bool, line_end: &str, input: &[&str]
         return if trim_end {
             SnippetState::LineEnd(input[..=url_index_end].concat(), index_plus_ws + 1)
         } else {
-            return SnippetState::LineEnd(input[..=index_plus_ws].concat(), index_plus_ws + 1);
+            SnippetState::LineEnd(input[..=index_plus_ws].concat(), index_plus_ws + 1)
         };
     }
 
@@ -321,20 +315,21 @@ fn break_string(max_width: usize, trim_end: bool, line_end: &str, input: &[&str]
         // Found a whitespace and what is on its left side is big enough.
         Some(index) if index >= MIN_STRING => break_at(index),
         // No whitespace found, try looking for a punctuation instead
-        _ => match input[0..max_width_index_in_input]
-            .iter()
-            .rposition(|grapheme| is_punctuation(grapheme))
+        _ => match (0..max_width_index_in_input)
+            .rev()
+            .skip_while(|pos| !is_valid_linebreak(input, *pos))
+            .next()
         {
             // Found a punctuation and what is on its left side is big enough.
             Some(index) if index >= MIN_STRING => break_at(index),
             // Either no boundary character was found to the left of `input[max_chars]`, or the line
             // got too small. We try searching for a boundary character to the right.
-            _ => match input[max_width_index_in_input..]
-                .iter()
-                .position(|grapheme| is_whitespace(grapheme) || is_punctuation(grapheme))
+            _ => match (max_width_index_in_input..input.len())
+                .skip_while(|pos| !is_valid_linebreak(input, *pos))
+                .next()
             {
                 // A boundary was found after the line limit
-                Some(index) => break_at(max_width_index_in_input + index),
+                Some(index) => break_at(index),
                 // No boundary to the right, the input cannot be broken
                 None => SnippetState::EndOfInput(input.concat()),
             },
@@ -342,17 +337,36 @@ fn break_string(max_width: usize, trim_end: bool, line_end: &str, input: &[&str]
     }
 }
 
+fn is_valid_linebreak(input: &[&str], pos: usize) -> bool {
+    let is_whitespace = is_whitespace(input[pos]);
+    if is_whitespace {
+        return true;
+    }
+    let is_punctuation = is_punctuation(input[pos]);
+    if is_punctuation && !is_part_of_type(input, pos) {
+        return true;
+    }
+    false
+}
+
+fn is_part_of_type(input: &[&str], pos: usize) -> bool {
+    input.get(pos..=pos + 1) == Some(&[":", ":"])
+        || input.get(pos.saturating_sub(1)..=pos) == Some(&[":", ":"])
+}
+
 fn is_new_line(grapheme: &str) -> bool {
     let bytes = grapheme.as_bytes();
     bytes.starts_with(b"\n") || bytes.starts_with(b"\r\n")
 }
 
 fn is_whitespace(grapheme: &str) -> bool {
-    grapheme.chars().all(|c| c.is_whitespace())
+    grapheme.chars().all(char::is_whitespace)
 }
 
 fn is_punctuation(grapheme: &str) -> bool {
-    grapheme.chars().all(|c| c.is_punctuation_other())
+    grapheme
+        .chars()
+        .all(UnicodeCategories::is_punctuation_other)
 }
 
 fn graphemes_width(graphemes: &[&str]) -> usize {
@@ -373,6 +387,19 @@ fn issue343() {
         rewrite_string("eq_", &fmt, 2);
     }
 
+    #[test]
+    fn line_break_at_valid_points_test() {
+        let string = "[TheName](Dont::break::my::type::That::would::be::very::nice) break here";
+        let graphemes = UnicodeSegmentation::graphemes(&*string, false).collect::<Vec<&str>>();
+        assert_eq!(
+            break_string(20, false, "", &graphemes[..]),
+            SnippetState::LineEnd(
+                "[TheName](Dont::break::my::type::That::would::be::very::nice) ".to_string(),
+                62
+            )
+        );
+    }
+
     #[test]
     fn should_break_on_whitespace() {
         let string = "Placerat felis. Mauris porta ante sagittis purus.";