]> git.lizzy.rs Git - rust.git/blobdiff - src/items.rs
Implement basic enum formatting
[rust.git] / src / items.rs
index a2027d89b888ef271c4b111d93c9bca2a7dea1b0..a24e02957faddfc9c86ee35c24eda4f51489f174 100644 (file)
@@ -11,7 +11,7 @@
 // Formatting top-level items - functions, structs, enums, traits, impls.
 
 use {ReturnIndent, BraceStyle};
-use utils::make_indent;
+use utils::{format_visibility, make_indent};
 use lists::{write_list, ListFormatting, SeparatorTactic, ListTactic};
 use visitor::FmtVisitor;
 use syntax::{ast, abi};
@@ -27,6 +27,7 @@ pub fn rewrite_fn(&mut self,
                       explicit_self: Option<&ast::ExplicitSelf>,
                       generics: &ast::Generics,
                       unsafety: &ast::Unsafety,
+                      constness: &ast::Constness,
                       abi: &abi::Abi,
                       vis: ast::Visibility,
                       span_end: BytePos)
@@ -40,6 +41,7 @@ pub fn rewrite_fn(&mut self,
                                               explicit_self,
                                               generics,
                                               unsafety,
+                                              constness,
                                               abi,
                                               vis,
                                               span_end,
@@ -74,6 +76,7 @@ pub fn rewrite_required_fn(&mut self,
                                               Some(&sig.explicit_self),
                                               &sig.generics,
                                               &sig.unsafety,
+                                              &sig.constness,
                                               &sig.abi,
                                               ast::Visibility::Inherited,
                                               span_end,
@@ -92,6 +95,7 @@ fn rewrite_fn_base(&mut self,
                        explicit_self: Option<&ast::ExplicitSelf>,
                        generics: &ast::Generics,
                        unsafety: &ast::Unsafety,
+                       constness: &ast::Constness,
                        abi: &abi::Abi,
                        vis: ast::Visibility,
                        span_end: BytePos,
@@ -105,12 +109,14 @@ fn rewrite_fn_base(&mut self,
 
         let mut result = String::with_capacity(1024);
         // Vis unsafety abi.
-        if vis == ast::Visibility::Public {
-            result.push_str("pub ");
-        }
+        result.push_str(format_visibility(vis));
+
         if let &ast::Unsafety::Unsafe = unsafety {
             result.push_str("unsafe ");
         }
+        if let &ast::Constness::Const = constness {
+            result.push_str("const ");
+        }
         if *abi != abi::Rust {
             result.push_str("extern ");
             result.push_str(&abi.to_string());
@@ -130,13 +136,27 @@ fn rewrite_fn_base(&mut self,
         let ret_str = self.rewrite_return(&fd.output);
 
         // Args.
-        let (one_line_budget, multi_line_budget, arg_indent) =
-            self.compute_budgets_for_args(&mut result, indent, ret_str.len(), newline_brace);
+        let (one_line_budget, multi_line_budget, mut arg_indent) =
+            self.compute_budgets_for_args(&result, indent, ret_str.len(), newline_brace);
 
         debug!("rewrite_fn: one_line_budget: {}, multi_line_budget: {}, arg_indent: {}",
                one_line_budget, multi_line_budget, arg_indent);
 
-        result.push('(');
+        // Check if vertical layout was forced by compute_budget_for_args.
+        if one_line_budget <= 0 {
+            if config!(fn_args_paren_newline) {
+                result.push('\n');
+                result.push_str(&make_indent(arg_indent));
+                arg_indent = arg_indent + 1; // extra space for `(`
+                result.push('(');
+            } else {
+                result.push_str("(\n");
+                result.push_str(&make_indent(arg_indent));
+            }
+        } else {
+            result.push('(');
+        }
+
         result.push_str(&self.rewrite_args(&fd.inputs,
                                            explicit_self,
                                            one_line_budget,
@@ -330,7 +350,7 @@ fn make_comments_for_list<T, I, F1, F2>(&self,
     }
 
     fn compute_budgets_for_args(&self,
-                                result: &mut String,
+                                result: &str,
                                 indent: usize,
                                 ret_str_len: usize,
                                 newline_brace: bool)
@@ -365,17 +385,14 @@ fn compute_budgets_for_args(&self,
 
         // Didn't work. we must force vertical layout and put args on a newline.
         if let None = budgets {
-            result.push('\n');
-            result.push_str(&make_indent(indent + 4));
-            // 6 = new indent + `()`
-            let used_space = indent + 6;
+            let new_indent = indent + config!(tab_spaces);
+            let used_space = new_indent + 2; // account for `(` and `)`
             let max_space = config!(ideal_width) + config!(leeway);
             if used_space > max_space {
                 // Whoops! bankrupt.
                 // TODO take evasive action, perhaps kill the indent or something.
             } else {
-                // 5 = new indent + `(`
-                budgets = Some((0, max_space - used_space, indent + 5));
+                budgets = Some((0, max_space - used_space, new_indent));
             }
         }
 
@@ -390,6 +407,118 @@ fn newline_for_brace(&self, where_clause: &ast::WhereClause) -> bool {
         }
     }
 
+    pub fn visit_enum(&mut self,
+                      ident: ast::Ident,
+                      vis: ast::Visibility,
+                      enum_def: &ast::EnumDef,
+                      generics: &ast::Generics,
+                      span: Span)
+    {
+        let header_str = self.format_header("enum", ident, vis);
+        self.changes.push_str_span(span, &header_str);
+
+        let enum_snippet = self.snippet(span);
+        // FIXME this will give incorrect results if there is a { in a comment.
+        let body_start = span.lo + BytePos(enum_snippet.find('{').unwrap() as u32 + 1);
+        let generics_str = self.format_generics(generics, body_start);
+        self.changes.push_str_span(span, &generics_str);
+
+        self.last_pos = body_start;
+        self.block_indent += config!(tab_spaces);
+        for (i, f) in enum_def.variants.iter().enumerate() {
+            let next_span_start: BytePos = if i == enum_def.variants.len() - 1 {
+                span.hi
+            } else {
+                enum_def.variants[i + 1].span.lo
+            };
+
+            self.visit_variant(f, i == enum_def.variants.len() - 1, next_span_start);
+        }
+        self.block_indent -= config!(tab_spaces);
+
+        self.format_missing_with_indent(span.lo + BytePos(enum_snippet.rfind('}').unwrap() as u32));
+        self.changes.push_str_span(span, "}");
+    }
+
+    // Variant of an enum
+    fn visit_variant(&mut self,
+                     field: &ast::Variant,
+                     last_field: bool,
+                     next_span_start: BytePos)
+    {
+        if self.visit_attrs(&field.node.attrs) {
+            return;
+        }
+
+        if let ast::VariantKind::TupleVariantKind(ref types) = field.node.kind {
+            self.format_missing_with_indent(field.span.lo);
+
+            let vis = format_visibility(field.node.vis);
+            self.changes.push_str_span(field.span, vis);
+            let name = field.node.name.to_string();
+            self.changes.push_str_span(field.span, &name);
+
+            let mut result = String::new();
+
+            if types.len() > 0 {
+                let comments = self.make_comments_for_list(Vec::new(),
+                                                           types.iter().map(|arg| arg.ty.span),
+                                                           ",",
+                                                           ")",
+                                                           |span| span.lo,
+                                                           |span| span.hi,
+                                                           next_span_start);
+
+                let type_strings: Vec<_> = types.iter()
+                                                .map(|arg| pprust::ty_to_string(&arg.ty))
+                                                .zip(comments.into_iter())
+                                                .collect();
+
+                result.push('(');
+
+                let indent = self.block_indent
+                             + vis.len()
+                             + field.node.name.to_string().len()
+                             + 1; // 1 = (
+
+                let comma_cost = if config!(enum_trailing_comma) { 1 } else { 0 };
+                let budget = config!(ideal_width) - indent - comma_cost - 1; // 1 = )
+
+                let fmt = ListFormatting {
+                    tactic: ListTactic::HorizontalVertical,
+                    separator: ",",
+                    trailing_separator: SeparatorTactic::Never,
+                    indent: indent,
+                    h_width: budget,
+                    v_width: budget,
+                };
+                result.push_str(&write_list(&type_strings, &fmt));
+                result.push(')');
+            }
+
+            if let Some(ref expr) = field.node.disr_expr {
+                result.push_str(" = ");
+                let expr_snippet = self.snippet(expr.span);
+                result.push_str(&expr_snippet);
+
+                // Make sure we do not exceed column limit
+                // 4 = " = ,"
+                assert!(config!(max_width) >= vis.len() + name.len() + expr_snippet.len() + 4,
+                        "Enum variant exceeded column limit");
+            }
+
+            self.changes.push_str_span(field.span, &result);
+
+            if !last_field || config!(enum_trailing_comma) {
+                self.changes.push_str_span(field.span, ",");
+            }
+        }
+
+        // TODO: deal with struct-like variants
+
+        self.last_pos = field.span.hi + BytePos(1);
+    }
+
     pub fn visit_struct(&mut self,
                         ident: ast::Ident,
                         vis: ast::Visibility,
@@ -397,7 +526,7 @@ pub fn visit_struct(&mut self,
                         generics: &ast::Generics,
                         span: Span)
     {
-        let header_str = self.struct_header(ident, vis);
+        let header_str = self.format_header("struct", ident, vis);
         self.changes.push_str_span(span, &header_str);
 
         if struct_def.fields.len() == 0 {
@@ -410,29 +539,16 @@ pub fn visit_struct(&mut self,
             return;
         }
 
-        let mut generics_buf = String::new();
-        let generics_str = self.rewrite_generics(generics, self.block_indent, struct_def.fields[0].span.lo);
-        generics_buf.push_str(&generics_str);
-
-        if generics.where_clause.predicates.len() > 0 {
-            generics_buf.push_str(&self.rewrite_where_clause(&generics.where_clause,
-                                                             self.block_indent,
-                                                             struct_def.fields[0].span.lo));
-            generics_buf.push_str(&make_indent(self.block_indent));
-            generics_buf.push_str("\n{");
-
-        } else {
-            generics_buf.push_str(" {");
-        }
-        self.changes.push_str_span(span, &generics_buf);
+        let generics_str = self.format_generics(generics, struct_def.fields[0].span.lo);
+        self.changes.push_str_span(span, &generics_str);
 
         let struct_snippet = self.snippet(span);
-        // FIXME this will give incorrect results if there is a { in a commet.
+        // FIXME this will give incorrect results if there is a { in a comment.
         self.last_pos = span.lo + BytePos(struct_snippet.find('{').unwrap() as u32 + 1);
 
         self.block_indent += config!(tab_spaces);
-        for f in &struct_def.fields {
-            self.visit_field(f, span.lo, &struct_snippet);
+        for (i, f) in struct_def.fields.iter().enumerate() {
+            self.visit_field(f, i == struct_def.fields.len() - 1, span.lo, &struct_snippet);
         }
         self.block_indent -= config!(tab_spaces);
 
@@ -440,23 +556,40 @@ pub fn visit_struct(&mut self,
         self.changes.push_str_span(span, "}");
     }
 
-    fn struct_header(&self,
+    fn format_header(&self,
+                     item_name: &str,
                      ident: ast::Ident,
                      vis: ast::Visibility)
         -> String
     {
-        let vis = if vis == ast::Visibility::Public {
-            "pub "
+        format!("{}{} {}", format_visibility(vis), item_name, &token::get_ident(ident))
+    }
+
+    fn format_generics(&self,
+                       generics: &ast::Generics,
+                       span_end: BytePos)
+        -> String
+    {
+        let mut result = self.rewrite_generics(generics, self.block_indent, span_end);
+
+        if generics.where_clause.predicates.len() > 0 {
+            result.push_str(&self.rewrite_where_clause(&generics.where_clause,
+                                                             self.block_indent,
+                                                             span_end));
+            result.push_str(&make_indent(self.block_indent));
+            result.push_str("\n{");
+
         } else {
-            ""
-        };
+            result.push_str(" {");
+        }
 
-        format!("{}struct {}", vis, &token::get_ident(ident))
+        result
     }
 
     // Field of a struct
     fn visit_field(&mut self,
                    field: &ast::StructField,
+                   last_field: bool,
                    // These two args are for missing spans hacks.
                    struct_start: BytePos,
                    struct_snippet: &str)
@@ -472,29 +605,29 @@ fn visit_field(&mut self,
         };
         let vis = match field.node.kind {
             ast::StructFieldKind::NamedField(_, vis) |
-            ast::StructFieldKind::UnnamedField(vis) => if vis == ast::Visibility::Public {
-                "pub "
-            } else {
-                ""
-            }
+            ast::StructFieldKind::UnnamedField(vis) => format_visibility(vis)
         };
         let typ = pprust::ty_to_string(&field.node.ty);
 
-        let field_str = match name {
+        let mut field_str = match name {
             Some(name) => {
                 let budget = config!(ideal_width) - self.block_indent;
+                // 3 is being conservative and assuming that there will be a trailing comma.
                 if self.block_indent + vis.len() + name.len() + typ.len() + 3 > budget {
-                    format!("{}{}:\n{}{},",
+                    format!("{}{}:\n{}{}",
                             vis,
                             name,
                             &make_indent(self.block_indent + config!(tab_spaces)),
                             typ)
                 } else {
-                    format!("{}{}: {},", vis, name, typ)
+                    format!("{}{}: {}", vis, name, typ)
                 }
             }
-            None => format!("{}{},", vis, typ),
+            None => format!("{}{}", vis, typ),
         };
+        if !last_field || config!(struct_trailing_comma) {
+            field_str.push(',');
+        }
         self.changes.push_str_span(field.span, &field_str);
 
         // This hack makes sure we only add comments etc. after the comma, and