// 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 {
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());
}
}
+#[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 {
}
#[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 {
}
}
+// 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') {
#[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]
ast::RegionTyParamBound(ref lt) => lt.span,
ast::TraitTyParamBound(ref prt, _) => prt.span,
})
- .hi
+ .hi()
}
#[inline]
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;
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);
};
}
-// 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)* ) {
}
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]
}
}
-#[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, _) |
_ => 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")
+}