diff --git a/src/bin/rustfmt.rs b/src/bin/rustfmt.rs index 2de572f616e..d1f7a83187b 100644 --- a/src/bin/rustfmt.rs +++ b/src/bin/rustfmt.rs @@ -11,11 +11,11 @@ #![cfg(not(test))] +extern crate env_logger; +extern crate getopts; extern crate log; extern crate rustfmt_nightly as rustfmt; extern crate toml; -extern crate env_logger; -extern crate getopts; use std::{env, error}; use std::fs::File; diff --git a/src/config.rs b/src/config.rs index 65292d60b26..7b2fb9f629d 100644 --- a/src/config.rs +++ b/src/config.rs @@ -558,6 +558,8 @@ create_config! { exceeds `chain_one_line_max`"; imports_indent: IndentStyle, IndentStyle::Visual, "Indent of imports"; imports_layout: ListTactic, ListTactic::Mixed, "Item layout inside a import block"; + reorder_extern_crates: bool, true, "Reorder extern crate statements alphabetically"; + reorder_extern_crates_in_group: bool, true, "Reorder extern crate statements in group"; reorder_imports: bool, false, "Reorder import statements alphabetically"; reorder_imports_in_group: bool, false, "Reorder import statements in group"; reorder_imported_names: bool, true, diff --git a/src/imports.rs b/src/imports.rs index a65d8bb066a..b73c85ddb56 100644 --- a/src/imports.rs +++ b/src/imports.rs @@ -113,11 +113,14 @@ fn compare_view_paths(a: &ast::ViewPath_, b: &ast::ViewPath_) -> Ordering { } } -fn compare_use_items(a: &ast::Item, b: &ast::Item) -> Option<Ordering> { +fn compare_use_items(context: &RewriteContext, a: &ast::Item, b: &ast::Item) -> Option<Ordering> { match (&a.node, &b.node) { (&ast::ItemKind::Use(ref a_vp), &ast::ItemKind::Use(ref b_vp)) => { Some(compare_view_paths(&a_vp.node, &b_vp.node)) } + (&ast::ItemKind::ExternCrate(..), &ast::ItemKind::ExternCrate(..)) => { + Some(context.snippet(a.span).cmp(&context.snippet(b.span))) + } _ => None, } } @@ -214,7 +217,9 @@ impl<'a> FmtVisitor<'a> { .collect::<Vec<_>>(); let pos_after_last_use_item = last_pos_of_prev_use_item; // Order the imports by view-path & other import path properties - ordered_use_items.sort_by(|a, b| compare_use_items(a.0, b.0).unwrap()); + ordered_use_items.sort_by(|a, b| { + compare_use_items(&self.get_context(), a.0, b.0).unwrap() + }); // First, output the span before the first import let prev_span_str = self.snippet(utils::mk_sp(self.last_pos, pos_before_first_use_item)); // Look for purely trailing space at the start of the prefix snippet before a linefeed, or diff --git a/src/lib.rs b/src/lib.rs index e1017fab959..eaac371aa5b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,23 +10,19 @@ #![feature(rustc_private)] +extern crate diff; #[macro_use] extern crate log; - +extern crate regex; +extern crate rustc_errors as errors; extern crate serde; #[macro_use] extern crate serde_derive; extern crate serde_json; - -extern crate syntax; -extern crate rustc_errors as errors; - extern crate strings; - -extern crate unicode_segmentation; -extern crate regex; -extern crate diff; +extern crate syntax; extern crate term; +extern crate unicode_segmentation; use std::collections::HashMap; use std::fmt; diff --git a/src/visitor.rs b/src/visitor.rs index c9fa7065511..3427867274a 100644 --- a/src/visitor.rs +++ b/src/visitor.rs @@ -36,6 +36,13 @@ fn is_use_item(item: &ast::Item) -> bool { } } +fn is_extern_crate(item: &ast::Item) -> bool { + match item.node { + ast::ItemKind::ExternCrate(..) => true, + _ => false, + } +} + pub struct FmtVisitor<'a> { pub parse_session: &'a ParseSess, pub codemap: &'a CodeMap, @@ -627,6 +634,44 @@ impl<'a> FmtVisitor<'a> { false } + fn reorder_items<F>( + &mut self, + items_left: &[ptr::P<ast::Item>], + is_item: &F, + in_group: bool, + ) -> usize + where + F: Fn(&ast::Item) -> bool, + { + let mut last = self.codemap.lookup_line_range(items_left[0].span()); + let item_length = items_left + .iter() + .take_while(|ppi| { + is_item(&***ppi) && (!in_group || { + let current = self.codemap.lookup_line_range(ppi.span()); + let in_same_group = current.lo < last.hi + 2; + last = current; + in_same_group + }) + }) + .count(); + let items = &items_left[..item_length]; + + let at_least_one_in_file_lines = items + .iter() + .any(|item| !out_of_file_lines_range!(self, item.span)); + + if at_least_one_in_file_lines { + self.format_imports(items); + } else { + for item in items { + self.push_rewrite(item.span, None); + } + } + + item_length + } + fn walk_mod_items(&mut self, m: &ast::Mod) { let mut items_left: &[ptr::P<ast::Item>] = &m.items; while !items_left.is_empty() { @@ -634,33 +679,20 @@ impl<'a> FmtVisitor<'a> { // to be potentially reordered within `format_imports`. Otherwise, just format the // next item for output. if self.config.reorder_imports() && is_use_item(&*items_left[0]) { - let reorder_imports_in_group = self.config.reorder_imports_in_group(); - let mut last = self.codemap.lookup_line_range(items_left[0].span()); - let use_item_length = items_left - .iter() - .take_while(|ppi| { - is_use_item(&***ppi) && (!reorder_imports_in_group || { - let current = self.codemap.lookup_line_range(ppi.span()); - let in_same_group = current.lo < last.hi + 2; - last = current; - in_same_group - }) - }) - .count(); - let (use_items, rest) = items_left.split_at(use_item_length); - - let at_least_one_in_file_lines = use_items - .iter() - .any(|item| !out_of_file_lines_range!(self, item.span)); - - if at_least_one_in_file_lines { - self.format_imports(use_items); - } else { - for item in use_items { - self.push_rewrite(item.span, None); - } - } - + let used_items_len = self.reorder_items( + &items_left, + &is_use_item, + self.config.reorder_imports_in_group(), + ); + let (_, rest) = items_left.split_at(used_items_len); + items_left = rest; + } else if self.config.reorder_extern_crates() && is_extern_crate(&*items_left[0]) { + let used_items_len = self.reorder_items( + &items_left, + &is_extern_crate, + self.config.reorder_extern_crates_in_group(), + ); + let (_, rest) = items_left.split_at(used_items_len); items_left = rest; } else { // `unwrap()` is safe here because we know `items_left` diff --git a/tests/source/extern.rs b/tests/source/extern.rs index 7f14d27b779..5546b217226 100644 --- a/tests/source/extern.rs +++ b/tests/source/extern.rs @@ -3,6 +3,13 @@ extern crate foo ; extern crate foo as bar ; +extern crate futures; +extern crate dotenv; +extern crate chrono; + +extern crate foo; +extern crate bar; + extern "C" { fn c_func(x: *mut *mut libc::c_void); diff --git a/tests/system.rs b/tests/system.rs index d915e11d534..6e148147a48 100644 --- a/tests/system.rs +++ b/tests/system.rs @@ -8,9 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -extern crate rustfmt_nightly as rustfmt; extern crate diff; extern crate regex; +extern crate rustfmt_nightly as rustfmt; extern crate term; use std::collections::HashMap; diff --git a/tests/target/attrib-extern-crate.rs b/tests/target/attrib-extern-crate.rs index fe06195b154..ed64a0aeb07 100644 --- a/tests/target/attrib-extern-crate.rs +++ b/tests/target/attrib-extern-crate.rs @@ -1,17 +1,17 @@ // Attributes on extern crate. -extern crate Foo; #[Attr1] extern crate Bar; #[Attr2] #[Attr2] extern crate Baz; +extern crate Foo; fn foo() { - extern crate Foo; #[Attr1] extern crate Bar; #[Attr2] #[Attr2] extern crate Baz; + extern crate Foo; } diff --git a/tests/target/extern.rs b/tests/target/extern.rs index 1431c384db9..c0601a4d0e1 100644 --- a/tests/target/extern.rs +++ b/tests/target/extern.rs @@ -1,7 +1,14 @@ // rustfmt-normalize_comments: true -extern crate foo; extern crate foo as bar; +extern crate foo; + +extern crate chrono; +extern crate dotenv; +extern crate futures; + +extern crate bar; +extern crate foo; extern "C" { fn c_func(x: *mut *mut libc::c_void);