From 7bf9aa25be0dc15be1640e72780871adf61a3f4d Mon Sep 17 00:00:00 2001
From: topecongiro <seuchida@gmail.com>
Date: Wed, 9 Aug 2017 00:16:18 +0900
Subject: [PATCH 1/2] Add reorder_extern_crates and
 reorder_extern_crates_in_group config options

---
 src/config.rs  |  2 ++
 src/imports.rs |  9 ++++--
 src/visitor.rs | 86 ++++++++++++++++++++++++++++++++++----------------
 3 files changed, 68 insertions(+), 29 deletions(-)

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/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`

From c28df858c79521dde6571a6fe153ba8af92f79d4 Mon Sep 17 00:00:00 2001
From: topecongiro <seuchida@gmail.com>
Date: Wed, 9 Aug 2017 00:16:35 +0900
Subject: [PATCH 2/2] Format source codes and update tests

---
 src/bin/rustfmt.rs                  |  4 ++--
 src/lib.rs                          | 14 +++++---------
 tests/source/extern.rs              |  7 +++++++
 tests/system.rs                     |  2 +-
 tests/target/attrib-extern-crate.rs |  4 ++--
 tests/target/extern.rs              |  9 ++++++++-
 6 files changed, 25 insertions(+), 15 deletions(-)

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/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/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);