]> git.lizzy.rs Git - rust.git/commitdiff
Add merge_derives config option
authortopecongiro <seuchida@gmail.com>
Wed, 23 Aug 2017 14:20:32 +0000 (23:20 +0900)
committertopecongiro <seuchida@gmail.com>
Thu, 24 Aug 2017 13:29:56 +0000 (22:29 +0900)
Configurations.md
src/config.rs
src/lists.rs
src/visitor.rs
tests/source/configs-merge_derives-true.rs [new file with mode: 0644]
tests/target/configs-merge_derives-true.rs [new file with mode: 0644]

index 13a704240c377afa89c6a2eff0475867592c8ece..373447bf24f605fbd3f63dcc655af8d3dcab4176 100644 (file)
@@ -1230,7 +1230,7 @@ Put a match sub-patterns' separator (`|`) in front or back.
 - **Default value**: `"Back"`
 - **Possible values**: `"Back"`, `"Front"`
 
-#### `"Back"`
+#### `"Back"`:
 
 ```rust
 match m {
@@ -1243,7 +1243,7 @@ match m {
 }
 ```
 
-#### `Front`
+#### `Front`:
 
 ```rust
 match m {
@@ -1265,25 +1265,42 @@ Maximum width of each line
 
 See also [`error_on_line_overflow`](#error_on_line_overflow).
 
-## `multiline_closure_forces_block`
+## `merge_derives`
 
-Force multiline closure bodies to be wrapped in a block
+Merge multiple derives into a single one.
 
-- **Default value**: `false`
-- **Possible values**: `false`, `true`
+- **Default value**: `true`
+- **Possible values**: `true`, `false`
+
+*Note*: The merged derives will be put after all other attributes or doc comments.
+
+#### `true`:
+
+```rust
+#[derive(Eq, PartialEq, Debug, Copy, Clone)]
+pub enum Foo {}
+```
 
 #### `false`:
 
 ```rust
-result.and_then(|maybe_value| match maybe_value {
-    None => ...,
-    Some(value) => ...,
-})
+#[derive(Eq, PartialEq)]
+#[derive(Debug)]
+#[derive(Copy, Clone)]
+pub enum Foo {}
 ```
 
+## `multiline_closure_forces_block`
+
+Force multiline closure bodies to be wrapped in a block
+
+- **Default value**: `false`
+- **Possible values**: `false`, `true`
+
 #### `true`:
 
 ```rust
+
 result.and_then(|maybe_value| {
     match maybe_value {
         None => ...,
@@ -1292,6 +1309,15 @@ result.and_then(|maybe_value| {
 })
 ```
 
+#### `false`:
+
+```rust
+result.and_then(|maybe_value| match maybe_value {
+    None => ...,
+    Some(value) => ...,
+})
+```
+
 ## `multiline_match_arm_forces_block`
 
 Force multiline match arm bodies to be wrapped in a block
index 9659f19377d06227983f8df20608769551656321..31eccf2494195367658a608959ea7513ac9cf462 100644 (file)
@@ -619,6 +619,7 @@ pub fn get_toml_path(dir: &Path) -> Result<Option<PathBuf>, Error> {
         "Force multiline closure bodies to be wrapped in a block";
     multiline_match_arm_forces_block: bool, false,
         "Force multiline match arm bodies to be wrapped in a block";
+    merge_derives: bool, true, "Merge multiple `#[derive(...)]` into a single one";
 }
 
 #[cfg(test)]
index ea34cdfc4801f403cccfa791b15b7e99b6517d31..8787850634e48af4e557fc3899395873930e3bb2 100644 (file)
 use rewrite::RewriteContext;
 use utils::{first_line_width, last_line_width, mk_sp};
 
-#[derive(Eq, PartialEq, Debug, Copy, Clone)]
 /// Formatting tactic for lists. This will be cast down to a
 /// DefinitiveListTactic depending on the number and length of the items and
 /// their comments.
+#[derive(Eq, PartialEq, Debug, Copy, Clone)]
 pub enum ListTactic {
     // One item per row.
     Vertical,
@@ -144,8 +144,8 @@ pub fn from_str<S: Into<String>>(s: S) -> ListItem {
     }
 }
 
-#[derive(Eq, PartialEq, Debug, Copy, Clone)]
 /// The definitive formatting tactic for lists.
+#[derive(Eq, PartialEq, Debug, Copy, Clone)]
 pub enum DefinitiveListTactic {
     Vertical,
     Horizontal,
index 49772f97e2d72260441a53b26e3f71eea5259a23..b0df0bcaf9bfcf195e31747e42c0134a01a78d68 100644 (file)
@@ -925,6 +925,8 @@ fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
         }
         let indent = shape.indent.to_string(context.config);
 
+        let mut derive_args = Vec::new();
+
         for (i, a) in self.iter().enumerate() {
             let a_str = try_opt!(a.rewrite(context, shape));
 
@@ -956,13 +958,51 @@ fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
             }
 
             // Write the attribute itself.
-            result.push_str(&a_str);
+            if context.config.merge_derives() {
+                if let Some(mut args) = get_derive_args(context, a) {
+                    // If the attribute is `#[derive(...)]`, take the arguments and skip rewriting.
+                    // We will merge the all arguments into a single `#[derive(...)]` at last.
+                    derive_args.append(&mut args);
+                } else {
+                    result.push_str(&a_str);
+
+                    if i < self.len() - 1 {
+                        result.push('\n');
+                    }
+                }
+            } else {
+                result.push_str(&a_str);
 
-            if i < self.len() - 1 {
+                if i < self.len() - 1 {
+                    result.push('\n');
+                }
+            }
+        }
+
+        // Add merged `#[derive(...)]` at last.
+        if context.config.merge_derives() && !derive_args.is_empty() {
+            if !result.is_empty() && !result.ends_with('\n') {
+                result.push_str(&indent);
                 result.push('\n');
             }
+            result.push_str(&format!("#[derive({})]", derive_args.join(", ")));
         }
 
         Some(result)
     }
 }
+
+/// Returns the arguments of `#[derive(...)]`.
+fn get_derive_args(context: &RewriteContext, attr: &ast::Attribute) -> Option<Vec<String>> {
+    attr.meta().and_then(|meta_item| match meta_item.node {
+        ast::MetaItemKind::List(ref args) if meta_item.name.as_str() == "derive" => {
+            // Every argument of `derive` should be `NestedMetaItemKind::Literal`.
+            Some(
+                args.iter()
+                    .map(|a| context.snippet(a.span))
+                    .collect::<Vec<_>>(),
+            )
+        }
+        _ => None,
+    })
+}
diff --git a/tests/source/configs-merge_derives-true.rs b/tests/source/configs-merge_derives-true.rs
new file mode 100644 (file)
index 0000000..804a48c
--- /dev/null
@@ -0,0 +1,10 @@
+// rustfmt-merge_derives: true
+// Merge multiple derives to a single one.
+
+#[bar]
+#[derive(Eq, PartialEq)]
+#[foo]
+#[derive(Debug)]
+#[foobar]
+#[derive(Copy, Clone)]
+pub enum Foo {}
diff --git a/tests/target/configs-merge_derives-true.rs b/tests/target/configs-merge_derives-true.rs
new file mode 100644 (file)
index 0000000..8cf7b9a
--- /dev/null
@@ -0,0 +1,8 @@
+// rustfmt-merge_derives: true
+// Merge multiple derives to a single one.
+
+#[bar]
+#[foo]
+#[foobar]
+#[derive(Eq, PartialEq, Debug, Copy, Clone)]
+pub enum Foo {}