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 { +fn compare_use_items(context: &RewriteContext, a: &ast::Item, b: &ast::Item) -> Option { 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::>(); 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/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( + &mut self, + items_left: &[ptr::P], + 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] = &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`