]> git.lizzy.rs Git - rust.git/blobdiff - src/utils.rs
Merge pull request #2138 from topecongiro/comments-around-trait-bounds
[rust.git] / src / utils.rs
index bdf5c2e4b5e4a41148e3f06f413ff3f8fba04792..999ba9f67b9544f4a713e704ef10bd17f4f170b3 100644 (file)
@@ -9,17 +9,17 @@
 // except according to those terms.
 
 use std::borrow::Cow;
-use std::cmp::Ordering;
 
-use syntax::ast::{self, Visibility, Attribute, MetaItem, MetaItemKind, NestedMetaItem,
-                  NestedMetaItemKind, Path};
+use syntax::{abi, ptr};
+use syntax::ast::{self, Attribute, CrateSugar, MetaItem, MetaItemKind, NestedMetaItem,
+                  NestedMetaItemKind, Path, Visibility};
 use syntax::codemap::{BytePos, Span, NO_EXPANSION};
-use syntax::abi;
 
-use Shape;
-use rewrite::{Rewrite, RewriteContext};
+use rewrite::RewriteContext;
+use shape::Shape;
 
-use SKIP_ANNOTATION;
+// When we get scoped annotations, we should have rustfmt::skip.
+const SKIP_ANNOTATION: &str = "rustfmt_skip";
 
 // Computes the length of a string's last line, minus offset.
 pub fn extra_offset(text: &str, shape: Shape) -> usize {
@@ -37,7 +37,8 @@ pub fn format_visibility(vis: &Visibility) -> Cow<'static, str> {
     match *vis {
         Visibility::Public => Cow::from("pub "),
         Visibility::Inherited => Cow::from(""),
-        Visibility::Crate(_) => Cow::from("pub(crate) "),
+        Visibility::Crate(_, CrateSugar::PubCrate) => Cow::from("pub(crate) "),
+        Visibility::Crate(_, CrateSugar::JustCrate) => Cow::from("crate "),
         Visibility::Restricted { ref path, .. } => {
             let Path { ref segments, .. } = **path;
             let mut segments_iter = segments.iter().map(|seg| seg.identifier.name.to_string());
@@ -55,6 +56,22 @@ pub fn format_visibility(vis: &Visibility) -> Cow<'static, str> {
     }
 }
 
+#[inline]
+pub fn format_constness(constness: ast::Constness) -> &'static str {
+    match constness {
+        ast::Constness::Const => "const ",
+        ast::Constness::NotConst => "",
+    }
+}
+
+#[inline]
+pub fn format_defaultness(defaultness: ast::Defaultness) -> &'static str {
+    match defaultness {
+        ast::Defaultness::Default => "default ",
+        ast::Defaultness::Final => "",
+    }
+}
+
 #[inline]
 pub fn format_unsafety(unsafety: ast::Unsafety) -> &'static str {
     match unsafety {
@@ -72,14 +89,51 @@ pub fn format_mutability(mutability: ast::Mutability) -> &'static str {
 }
 
 #[inline]
-pub fn format_abi(abi: abi::Abi, explicit_abi: bool) -> String {
-    if abi == abi::Abi::C && !explicit_abi {
-        "extern ".into()
+pub fn format_abi(abi: abi::Abi, explicit_abi: bool, is_mod: bool) -> Cow<'static, str> {
+    if abi == abi::Abi::Rust && !is_mod {
+        Cow::from("")
+    } else if abi == abi::Abi::C && !explicit_abi {
+        Cow::from("extern ")
     } else {
-        format!("extern {} ", abi)
+        Cow::from(format!("extern {} ", abi))
     }
 }
 
+#[inline]
+// Transform `Vec<syntax::ptr::P<T>>` into `Vec<&T>`
+pub fn ptr_vec_to_ref_vec<T>(vec: &[ptr::P<T>]) -> Vec<&T> {
+    vec.iter().map(|x| &**x).collect::<Vec<_>>()
+}
+
+#[inline]
+pub fn filter_attributes(attrs: &[ast::Attribute], style: ast::AttrStyle) -> Vec<ast::Attribute> {
+    attrs
+        .iter()
+        .filter(|a| a.style == style)
+        .cloned()
+        .collect::<Vec<_>>()
+}
+
+#[inline]
+pub fn inner_attributes(attrs: &[ast::Attribute]) -> Vec<ast::Attribute> {
+    filter_attributes(attrs, ast::AttrStyle::Inner)
+}
+
+#[inline]
+pub fn outer_attributes(attrs: &[ast::Attribute]) -> Vec<ast::Attribute> {
+    filter_attributes(attrs, ast::AttrStyle::Outer)
+}
+
+#[inline]
+pub fn last_line_contains_single_line_comment(s: &str) -> bool {
+    s.lines().last().map_or(false, |l| l.contains("//"))
+}
+
+#[inline]
+pub fn is_attributes_extendable(attrs_str: &str) -> bool {
+    !attrs_str.contains('\n') && !last_line_contains_single_line_comment(attrs_str)
+}
+
 // The width of the first line in s.
 #[inline]
 pub fn first_line_width(s: &str) -> usize {
@@ -98,6 +152,16 @@ pub fn last_line_width(s: &str) -> usize {
     }
 }
 
+// The total used width of the last line.
+#[inline]
+pub fn last_line_used_width(s: &str, offset: usize) -> usize {
+    if s.contains('\n') {
+        last_line_width(s)
+    } else {
+        offset + s.len()
+    }
+}
+
 #[inline]
 pub fn trimmed_last_line_width(s: &str) -> usize {
     match s.rfind('\n') {
@@ -108,12 +172,18 @@ pub fn trimmed_last_line_width(s: &str) -> usize {
 
 #[inline]
 pub fn last_line_extendable(s: &str) -> bool {
-    s.lines().last().map_or(false, |s| {
-        s.ends_with("\"#") ||
-            s.trim()
-                .chars()
-                .all(|c| c == ')' || c == ']' || c == '}' || c == '?')
-    })
+    if s.ends_with("\"#") {
+        return true;
+    }
+    for c in s.chars().rev() {
+        match c {
+            ')' | ']' | '}' | '?' => continue,
+            '\n' => break,
+            _ if c.is_whitespace() => continue,
+            _ => return false,
+        }
+    }
+    true
 }
 
 #[inline]
@@ -152,7 +222,7 @@ pub fn end_typaram(typaram: &ast::TyParam) -> BytePos {
             ast::RegionTyParamBound(ref lt) => lt.span,
             ast::TraitTyParamBound(ref prt, _) => prt.span,
         })
-        .hi
+        .hi()
 }
 
 #[inline]
@@ -228,7 +298,6 @@ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
         impl<'de> ::serde::de::Deserialize<'de> for $e {
             fn deserialize<D>(d: D) -> Result<Self, D::Error>
                     where D: ::serde::Deserializer<'de> {
-                use std::ascii::AsciiExt;
                 use serde::de::{Error, Visitor};
                 use std::marker::PhantomData;
                 use std::fmt;
@@ -258,7 +327,6 @@ impl ::std::str::FromStr for $e {
             type Err = &'static str;
 
             fn from_str(s: &str) -> Result<Self, Self::Err> {
-                use std::ascii::AsciiExt;
                 $(
                     if stringify!($x).eq_ignore_ascii_case(s) {
                         return Ok($e::$x);
@@ -280,15 +348,6 @@ fn doc_hint() -> String {
     };
 }
 
-// Same as try!, but for Option
-#[macro_export]
-macro_rules! try_opt {
-    ($expr:expr) => (match $expr {
-        Some(val) => val,
-        None => { return None; }
-    })
-}
-
 macro_rules! msg {
     ($($arg:tt)*) => (
         match writeln!(&mut ::std::io::stderr(), $($arg)* ) {
@@ -307,79 +366,66 @@ macro_rules! source {
 }
 
 pub fn mk_sp(lo: BytePos, hi: BytePos) -> Span {
-    Span {
-        lo,
-        hi,
-        ctxt: NO_EXPANSION,
-    }
+    Span::new(lo, hi, NO_EXPANSION)
 }
 
-// Wraps string-like values in an Option. Returns Some when the string adheres
-// to the Rewrite constraints defined for the Rewrite trait and else otherwise.
-pub fn wrap_str<S: AsRef<str>>(s: S, max_width: usize, shape: Shape) -> Option<S> {
-    {
-        let snippet = s.as_ref();
-
-        if !snippet.is_empty() {
-            if !snippet.contains('\n') && snippet.len() > shape.width {
-                return None;
-            } else {
-                let mut lines = snippet.lines();
-
-                if lines.next().unwrap().len() > shape.width {
-                    return None;
-                }
-
-                // The other lines must fit within the maximum width.
-                if lines.any(|line| line.len() > max_width) {
-                    return None;
-                }
+// Return true if the given span does not intersect with file lines.
+macro_rules! out_of_file_lines_range {
+    ($self:ident, $span:expr) => {
+        !$self.config
+            .file_lines()
+            .intersects(&$self.codemap.lookup_line_range($span))
+    }
+}
 
-                // `width` is the maximum length of the last line, excluding
-                // indentation.
-                // A special check for the last line, since the caller may
-                // place trailing characters on this line.
-                if snippet.lines().rev().next().unwrap().len() > shape.used_width() + shape.width {
-                    return None;
-                }
-            }
+macro_rules! skip_out_of_file_lines_range {
+    ($self:ident, $span:expr) => {
+        if out_of_file_lines_range!($self, $span) {
+            return None;
         }
     }
+}
 
-    Some(s)
+macro_rules! skip_out_of_file_lines_range_visitor {
+    ($self:ident, $span:expr) => {
+        if out_of_file_lines_range!($self, $span) {
+            $self.push_rewrite($span, None);
+            return;
+        }
+    }
 }
 
-impl Rewrite for String {
-    fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
-        wrap_str(self, context.config.max_width(), shape).map(ToOwned::to_owned)
+// Wraps String in an Option. Returns Some when the string adheres to the
+// Rewrite constraints defined for the Rewrite trait and else otherwise.
+pub fn wrap_str(s: String, max_width: usize, shape: Shape) -> Option<String> {
+    if is_valid_str(&s, max_width, shape) {
+        Some(s)
+    } else {
+        None
     }
 }
 
-// Binary search in integer range. Returns the first Ok value returned by the
-// callback.
-// The callback takes an integer and returns either an Ok, or an Err indicating
-// whether the `guess' was too high (Ordering::Less), or too low.
-// This function is guaranteed to try to the hi value first.
-pub fn binary_search<C, T>(mut lo: usize, mut hi: usize, callback: C) -> Option<T>
-where
-    C: Fn(usize) -> Result<T, Ordering>,
-{
-    let mut middle = hi;
-
-    while lo <= hi {
-        match callback(middle) {
-            Ok(val) => return Some(val),
-            Err(Ordering::Less) => {
-                hi = middle - 1;
-            }
-            Err(..) => {
-                lo = middle + 1;
-            }
+fn is_valid_str(snippet: &str, max_width: usize, shape: Shape) -> bool {
+    if !snippet.is_empty() {
+        // First line must fits with `shape.width`.
+        if first_line_width(snippet) > shape.width {
+            return false;
+        }
+        // If the snippet does not include newline, we are done.
+        if first_line_width(snippet) == snippet.len() {
+            return true;
+        }
+        // The other lines must fit within the maximum width.
+        if snippet.lines().skip(1).any(|line| line.len() > max_width) {
+            return false;
+        }
+        // A special check for the last line, since the caller may
+        // place trailing characters on this line.
+        if last_line_width(snippet) > shape.used_width() + shape.width {
+            return false;
         }
-        middle = (hi + lo) / 2;
     }
-
-    None
+    true
 }
 
 #[inline]
@@ -401,22 +447,6 @@ pub fn paren_overhead(context: &RewriteContext) -> usize {
     }
 }
 
-#[test]
-fn bin_search_test() {
-    let closure = |i| match i {
-        4 => Ok(()),
-        j if j > 4 => Err(Ordering::Less),
-        j if j < 4 => Err(Ordering::Greater),
-        _ => unreachable!(),
-    };
-
-    assert_eq!(Some(()), binary_search(1, 10, &closure));
-    assert_eq!(None, binary_search(1, 3, &closure));
-    assert_eq!(Some(()), binary_search(0, 44, &closure));
-    assert_eq!(Some(()), binary_search(4, 125, &closure));
-    assert_eq!(None, binary_search(6, 100, &closure));
-}
-
 pub fn left_most_sub_expr(e: &ast::Expr) -> &ast::Expr {
     match e.node {
         ast::ExprKind::InPlace(ref e, _) |
@@ -434,3 +464,26 @@ pub fn left_most_sub_expr(e: &ast::Expr) -> &ast::Expr {
         _ => e,
     }
 }
+
+// isatty shamelessly adapted from cargo.
+#[cfg(unix)]
+pub fn isatty() -> bool {
+    extern crate libc;
+
+    unsafe { libc::isatty(libc::STDOUT_FILENO) != 0 }
+}
+#[cfg(windows)]
+pub fn isatty() -> bool {
+    extern crate kernel32;
+    extern crate winapi;
+
+    unsafe {
+        let handle = kernel32::GetStdHandle(winapi::winbase::STD_OUTPUT_HANDLE);
+        let mut out = 0;
+        kernel32::GetConsoleMode(handle, &mut out) != 0
+    }
+}
+
+pub fn starts_with_newline(s: &str) -> bool {
+    s.starts_with('\n') || s.starts_with("\r\n")
+}