]> git.lizzy.rs Git - rust.git/blobdiff - src/imports.rs
Merge pull request #128 from marcusklaas/subexpr
[rust.git] / src / imports.rs
index e7557242e1be635f297a9a06314d2591ad6bc849..d9eb7b772a3b2ca3a9ad332168d2d70c7b9226de 100644 (file)
 // except according to those terms.
 
 use visitor::FmtVisitor;
-use lists::{write_list, ListFormatting, SeparatorTactic, ListTactic};
+use lists::{write_list, itemize_list, ListItem, ListFormatting, SeparatorTactic, ListTactic};
+use utils::{span_after, format_visibility};
 
 use syntax::ast;
-use syntax::codemap::Span;
 use syntax::parse::token;
 use syntax::print::pprust;
+use syntax::codemap::Span;
 
-use {IDEAL_WIDTH, MAX_WIDTH};
-
-// TODO change import lists with one item to a single import
-//      remove empty lists (if they're even possible)
 // TODO (some day) remove unused imports, expand globs, compress many single imports into a list import
 
+fn rewrite_single_use_list(path_str: String, vpi: ast::PathListItem, vis: &str) -> String {
+    if let ast::PathListItem_::PathListIdent{ name, .. } = vpi.node {
+        let name_str = token::get_ident(name).to_string();
+        if path_str.len() == 0 {
+            format!("{}use {};", vis, name_str)
+        } else {
+            format!("{}use {}::{};", vis, path_str, name_str)
+        }
+    } else {
+        if path_str.len() != 0 {
+            format!("{}use {};", vis, path_str)
+        } else {
+            // This catches the import: use {self}, which is a compiler error, so we just
+            // leave it alone.
+            format!("{}use {{self}};", vis)
+        }
+    }
+}
+
 impl<'a> FmtVisitor<'a> {
     // Basically just pretty prints a multi-item import.
-    pub fn rewrite_use_list(&mut self,
+    // Returns None when the import can be removed.
+    pub fn rewrite_use_list(&self,
+                            block_indent: usize,
+                            one_line_budget: usize, // excluding indentation
+                            multi_line_budget: usize,
                             path: &ast::Path,
                             path_list: &[ast::PathListItem],
-                            vp_span: Span) -> String {
-        // FIXME remove unused imports
+                            visibility: ast::Visibility,
+                            span: Span)
+                            -> Option<String> {
+        let path_str = pprust::path_to_string(path);
+        let vis = format_visibility(visibility);
 
-        // FIXME check indentation
-        let l_loc = self.codemap.lookup_char_pos(vp_span.lo);
+        match path_list.len() {
+            0 => return None,
+            1 => return Some(rewrite_single_use_list(path_str, path_list[0], vis)),
+            _ => ()
+        }
 
-        let path_str = pprust::path_to_string(&path);
+        // 2 = ::
+        let path_separation_w = if path_str.len() > 0 { 2 } else { 0 };
+        // 5 = "use " + {
+        let indent = path_str.len() + 5 + path_separation_w + vis.len();
 
-        // 3 = :: + {
-        let indent = l_loc.col.0 + path_str.len() + 3;
         // 2 = } + ;
-        let used_width = indent + path_str.len() + 2;
-        let budget = if used_width >= IDEAL_WIDTH {
-            if used_width < MAX_WIDTH {
-                MAX_WIDTH - used_width
-            } else {
-                // Give up
-                return String::new();
-            }
-        } else {
-            IDEAL_WIDTH - used_width
-        };
-        let fmt = ListFormatting {
-            tactic: ListTactic::Mixed,
-            separator: ",",
-            trailing_separator: SeparatorTactic::Never,
-            indent: indent,
-            h_width: budget,
-            v_width: budget,
-        };
-
-        // TODO handle any comments inbetween items.
-        // If `self` is in the list, put it first.
-        let head = if path_list.iter().any(|vpi|
-            if let ast::PathListItem_::PathListMod{ .. } = vpi.node {
-                true
-            } else {
-                false
-            }
-        ) {
-            Some(("self".to_string(), String::new()))
+        let used_width = indent + 2;
+
+        // Break as early as possible when we've blown our budget.
+        let remaining_line_budget = one_line_budget.checked_sub(used_width).unwrap_or(0);
+        let remaining_multi_budget = multi_line_budget.checked_sub(used_width).unwrap_or(0);
+
+        let fmt = ListFormatting { tactic: ListTactic::Mixed,
+                                   separator: ",",
+                                   trailing_separator: SeparatorTactic::Never,
+                                   indent: block_indent + indent,
+                                   h_width: remaining_line_budget,
+                                   v_width: remaining_multi_budget,
+                                   ends_with_newline: true, };
+
+        let mut items = itemize_list(self.codemap,
+                                     vec![ListItem::from_str("")], /* Dummy value, explanation
+                                                                    * below */
+                                     path_list.iter(),
+                                     ",",
+                                     "}",
+                                     |vpi| vpi.span.lo,
+                                     |vpi| vpi.span.hi,
+                                     |vpi| match vpi.node {
+                                         ast::PathListItem_::PathListIdent{ name, .. } => {
+                                             token::get_ident(name).to_string()
+                                         }
+                                         ast::PathListItem_::PathListMod{ .. } => {
+                                             "self".to_owned()
+                                         }
+                                     },
+                                     span_after(span, "{", self.codemap),
+                                     span.hi);
+
+        // We prefixed the item list with a dummy value so that we can
+        // potentially move "self" to the front of the vector without touching
+        // the rest of the items.
+        // FIXME: Make more efficient by using a linked list? That would
+        // require changes to the signatures of itemize_list and write_list.
+        let has_self = move_self_to_front(&mut items);
+        let first_index = if has_self { 0 } else { 1 };
+
+        if self.config.reorder_imports {
+            items.tail_mut().sort_by(|a, b| a.item.cmp(&b.item));
+        }
+
+        let list = write_list(&items[first_index..], &fmt);
+
+        Some(if path_str.len() == 0 {
+            format!("{}use {{{}}};", vis, list)
         } else {
-            None
-        };
-
-        let items: Vec<_> = head.into_iter().chain(path_list.iter().filter_map(|vpi| {
-            match vpi.node {
-                ast::PathListItem_::PathListIdent{ name, .. } => {
-                    Some((token::get_ident(name).to_string(), String::new()))
-                }
-                // Skip `self`, because we added it above.
-                ast::PathListItem_::PathListMod{ .. } => None,
-            }
-        })).collect();
-
-        format!("use {}::{{{}}};", path_str, write_list(&items, &fmt))
+            format!("{}use {}::{{{}}};", vis, path_str, list)
+        })
+    }
+}
+
+// Returns true when self item was found.
+fn move_self_to_front(items: &mut Vec<ListItem>) -> bool {
+    match items.iter().position(|item| item.item == "self") {
+        Some(pos) => {
+            items[0] = items.remove(pos);
+            true
+        },
+        None => false
     }
 }