]> git.lizzy.rs Git - rust.git/blob - src/imports.rs
Merge pull request #2218 from pietroalbini/fix-ast-for-use_nested_groups
[rust.git] / src / imports.rs
1 // Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 use std::cmp::Ordering;
12
13 use syntax::ast;
14 use syntax::codemap::{BytePos, Span};
15
16
17 use spanned::Spanned;
18 use codemap::SpanUtils;
19 use comment::combine_strs_with_missing_comments;
20 use config::IndentStyle;
21 use lists::{definitive_tactic, itemize_list, write_list, DefinitiveListTactic, ListFormatting,
22             ListItem, Separator, SeparatorPlace, SeparatorTactic};
23 use rewrite::{Rewrite, RewriteContext};
24 use shape::Shape;
25 use types::{rewrite_path, PathContext};
26 use utils::{format_visibility, mk_sp};
27 use visitor::{rewrite_extern_crate, FmtVisitor};
28
29 fn compare_path_segments(a: &ast::PathSegment, b: &ast::PathSegment) -> Ordering {
30     a.identifier.name.as_str().cmp(&b.identifier.name.as_str())
31 }
32
33 fn compare_paths(a: &ast::Path, b: &ast::Path) -> Ordering {
34     for segment in a.segments.iter().zip(b.segments.iter()) {
35         let ord = compare_path_segments(segment.0, segment.1);
36         if ord != Ordering::Equal {
37             return ord;
38         }
39     }
40     a.segments.len().cmp(&b.segments.len())
41 }
42
43 fn compare_use_trees(a: &ast::UseTree, b: &ast::UseTree, nested: bool) -> Ordering {
44     use ast::UseTreeKind::*;
45
46     // `use_nested_groups` is not yet supported, remove the `if !nested` when support will be
47     // fully added
48     if !nested {
49         let paths_cmp = compare_paths(&a.prefix, &b.prefix);
50         if paths_cmp != Ordering::Equal {
51             return paths_cmp;
52         }
53     }
54
55     match (&a.kind, &b.kind) {
56         (&Simple(ident_a), &Simple(ident_b)) => {
57             let name_a = &*a.prefix.segments.last().unwrap().identifier.name.as_str();
58             let name_b = &*b.prefix.segments.last().unwrap().identifier.name.as_str();
59             let name_ordering = if name_a == "self" {
60                 if name_b == "self" {
61                     Ordering::Equal
62                 } else {
63                     Ordering::Less
64                 }
65             } else if name_b == "self" {
66                 Ordering::Greater
67             } else {
68                 name_a.cmp(name_b)
69             };
70             if name_ordering == Ordering::Equal {
71                 if ident_a.name.as_str() != name_a {
72                     if ident_b.name.as_str() != name_b {
73                         ident_a.name.as_str().cmp(&ident_b.name.as_str())
74                     } else {
75                         Ordering::Greater
76                     }
77                 } else {
78                     Ordering::Less
79                 }
80             } else {
81                 name_ordering
82             }
83         }
84         (&Glob, &Glob) => Ordering::Equal,
85         (&Simple(_), _) | (&Glob, &Nested(_)) => Ordering::Less,
86         (&Nested(ref a_items), &Nested(ref b_items)) => {
87             let mut a = a_items
88                 .iter()
89                 .map(|&(ref tree, _)| tree.clone())
90                 .collect::<Vec<_>>();
91             let mut b = b_items
92                 .iter()
93                 .map(|&(ref tree, _)| tree.clone())
94                 .collect::<Vec<_>>();
95             a.sort_by(|a, b| compare_use_trees(a, b, true));
96             b.sort_by(|a, b| compare_use_trees(a, b, true));
97             for comparison_pair in a.iter().zip(b.iter()) {
98                 let ord = compare_use_trees(comparison_pair.0, comparison_pair.1, true);
99                 if ord != Ordering::Equal {
100                     return ord;
101                 }
102             }
103             a.len().cmp(&b.len())
104         }
105         (&Glob, &Simple(_)) | (&Nested(_), _) => Ordering::Greater,
106     }
107 }
108
109 fn compare_use_items(context: &RewriteContext, a: &ast::Item, b: &ast::Item) -> Option<Ordering> {
110     match (&a.node, &b.node) {
111         (&ast::ItemKind::Use(ref a_tree), &ast::ItemKind::Use(ref b_tree)) => {
112             Some(compare_use_trees(&a_tree, &b_tree, false))
113         }
114         (&ast::ItemKind::ExternCrate(..), &ast::ItemKind::ExternCrate(..)) => {
115             Some(context.snippet(a.span).cmp(&context.snippet(b.span)))
116         }
117         _ => None,
118     }
119 }
120
121 // TODO (some day) remove unused imports, expand globs, compress many single
122 // imports into a list import.
123
124 fn rewrite_prefix(path: &ast::Path, context: &RewriteContext, shape: Shape) -> Option<String> {
125     let path_str = if path.segments.last().unwrap().identifier.to_string() == "self"
126         && path.segments.len() > 1
127     {
128         let path = &ast::Path {
129             span: path.span,
130             segments: path.segments[..path.segments.len() - 1].to_owned(),
131         };
132         rewrite_path(context, PathContext::Import, None, path, shape)?
133     } else {
134         rewrite_path(context, PathContext::Import, None, path, shape)?
135     };
136     Some(path_str)
137 }
138
139 impl Rewrite for ast::UseTree {
140     fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
141         match self.kind {
142             ast::UseTreeKind::Nested(ref items) => {
143                 rewrite_nested_use_tree(shape, &self.prefix, items, self.span, context)
144             }
145             ast::UseTreeKind::Glob => {
146                 let prefix_shape = shape.sub_width(3)?;
147
148                 if self.prefix.segments.len() > 0 {
149                     let path_str = rewrite_prefix(&self.prefix, context, prefix_shape)?;
150                     Some(format!("{}::*", path_str))
151                 } else {
152                     Some("*".into())
153                 }
154             }
155             ast::UseTreeKind::Simple(ident) => {
156                 let ident_str = ident.to_string();
157
158                 // 4 = " as ".len()
159                 let prefix_shape = shape.sub_width(ident_str.len() + 4)?;
160                 let path_str = rewrite_prefix(&self.prefix, context, prefix_shape)?;
161
162                 if self.prefix.segments.last().unwrap().identifier == ident {
163                     Some(path_str)
164                 } else {
165                     Some(format!("{} as {}", path_str, ident_str))
166                 }
167             }
168         }
169     }
170 }
171
172 // Rewrite `use foo;` WITHOUT attributes.
173 fn rewrite_import(
174     context: &RewriteContext,
175     vis: &ast::Visibility,
176     tree: &ast::UseTree,
177     attrs: &[ast::Attribute],
178     shape: Shape,
179 ) -> Option<String> {
180     let vis = format_visibility(vis);
181     // 4 = `use `, 1 = `;`
182     let rw = shape
183         .offset_left(vis.len() + 4)
184         .and_then(|shape| shape.sub_width(1))
185         .and_then(|shape| match tree.kind {
186             // If we have an empty nested group with no attributes, we erase it
187             ast::UseTreeKind::Nested(ref items) if items.is_empty() && attrs.is_empty() => {
188                 Some("".into())
189             }
190             _ => tree.rewrite(context, shape),
191         });
192     match rw {
193         Some(ref s) if !s.is_empty() => Some(format!("{}use {};", vis, s)),
194         _ => rw,
195     }
196 }
197
198 fn rewrite_imports(
199     context: &RewriteContext,
200     use_items: &[&ast::Item],
201     shape: Shape,
202     span: Span,
203 ) -> Option<String> {
204     let items = itemize_list(
205         context.codemap,
206         use_items.iter(),
207         "",
208         ";",
209         |item| item.span().lo(),
210         |item| item.span().hi(),
211         |item| {
212             let attrs_str = item.attrs.rewrite(context, shape)?;
213
214             let missed_span = if item.attrs.is_empty() {
215                 mk_sp(item.span.lo(), item.span.lo())
216             } else {
217                 mk_sp(item.attrs.last().unwrap().span.hi(), item.span.lo())
218             };
219
220             let item_str = match item.node {
221                 ast::ItemKind::Use(ref tree) => {
222                     rewrite_import(context, &item.vis, tree, &item.attrs, shape)?
223                 }
224                 ast::ItemKind::ExternCrate(..) => rewrite_extern_crate(context, item)?,
225                 _ => return None,
226             };
227
228             combine_strs_with_missing_comments(
229                 context,
230                 &attrs_str,
231                 &item_str,
232                 missed_span,
233                 shape,
234                 false,
235             )
236         },
237         span.lo(),
238         span.hi(),
239         false,
240     );
241     let mut item_pair_vec: Vec<_> = items.zip(use_items.iter()).collect();
242     item_pair_vec.sort_by(|a, b| compare_use_items(context, a.1, b.1).unwrap());
243     let item_vec: Vec<_> = item_pair_vec.into_iter().map(|pair| pair.0).collect();
244
245     let fmt = ListFormatting {
246         tactic: DefinitiveListTactic::Vertical,
247         separator: "",
248         trailing_separator: SeparatorTactic::Never,
249         separator_place: SeparatorPlace::Back,
250         shape: shape,
251         ends_with_newline: true,
252         preserve_newline: false,
253         config: context.config,
254     };
255
256     write_list(&item_vec, &fmt)
257 }
258
259 impl<'a> FmtVisitor<'a> {
260     pub fn format_imports(&mut self, use_items: &[&ast::Item]) {
261         if use_items.is_empty() {
262             return;
263         }
264
265         let lo = use_items.first().unwrap().span().lo();
266         let hi = use_items.last().unwrap().span().hi();
267         let span = mk_sp(lo, hi);
268         let rw = rewrite_imports(&self.get_context(), use_items, self.shape(), span);
269         self.push_rewrite(span, rw);
270     }
271
272     pub fn format_import(&mut self, item: &ast::Item, tree: &ast::UseTree) {
273         let span = item.span;
274         let shape = self.shape();
275         let rw = rewrite_import(&self.get_context(), &item.vis, tree, &item.attrs, shape);
276         match rw {
277             Some(ref s) if s.is_empty() => {
278                 // Format up to last newline
279                 let prev_span = mk_sp(self.last_pos, source!(self, span).lo());
280                 let span_end = match self.snippet(prev_span).rfind('\n') {
281                     Some(offset) => self.last_pos + BytePos(offset as u32),
282                     None => source!(self, span).lo(),
283                 };
284                 self.format_missing(span_end);
285                 self.last_pos = source!(self, span).hi();
286             }
287             Some(ref s) => {
288                 self.format_missing_with_indent(source!(self, span).lo());
289                 self.buffer.push_str(s);
290                 self.last_pos = source!(self, span).hi();
291             }
292             None => {
293                 self.format_missing_with_indent(source!(self, span).lo());
294                 self.format_missing(source!(self, span).hi());
295             }
296         }
297     }
298 }
299
300 fn rewrite_nested_use_tree_single(path_str: String, tree: &ast::UseTree) -> String {
301     if let ast::UseTreeKind::Simple(rename) = tree.kind {
302         let ident = tree.prefix.segments.last().unwrap().identifier;
303         let mut item_str = ident.name.to_string();
304         if item_str == "self" {
305             item_str = "".to_owned();
306         }
307
308         let path_item_str = if path_str.is_empty() {
309             if item_str.is_empty() {
310                 "self".to_owned()
311             } else {
312                 item_str
313             }
314         } else if item_str.is_empty() {
315             path_str
316         } else {
317             format!("{}::{}", path_str, item_str)
318         };
319
320         if ident == rename {
321             path_item_str
322         } else {
323             format!("{} as {}", path_item_str, rename)
324         }
325     } else {
326         unimplemented!("`use_nested_groups` is not yet fully supported");
327     }
328 }
329
330 fn rewrite_nested_use_tree_item(tree: &&ast::UseTree) -> Option<String> {
331     Some(if let ast::UseTreeKind::Simple(rename) = tree.kind {
332         let ident = tree.prefix.segments.last().unwrap().identifier;
333
334         if ident == rename {
335             ident.name.to_string()
336         } else {
337             format!("{} as {}", ident.name.to_string(), rename)
338         }
339     } else {
340         unimplemented!("`use_nested_groups` is not yet fully supported");
341     })
342 }
343
344 #[derive(Eq, PartialEq)]
345 enum ImportItem<'a> {
346     // `self` or `self as a`
347     SelfImport(&'a str),
348     // name_one, name_two, ...
349     SnakeCase(&'a str),
350     // NameOne, NameTwo, ...
351     CamelCase(&'a str),
352     // NAME_ONE, NAME_TWO, ...
353     AllCaps(&'a str),
354     // Failed to format the import item
355     Invalid,
356 }
357
358 impl<'a> ImportItem<'a> {
359     fn from_str(s: &str) -> ImportItem {
360         if s == "self" || s.starts_with("self as") {
361             ImportItem::SelfImport(s)
362         } else if s.chars().all(|c| c.is_lowercase() || c == '_' || c == ' ') {
363             ImportItem::SnakeCase(s)
364         } else if s.chars().all(|c| c.is_uppercase() || c == '_' || c == ' ') {
365             ImportItem::AllCaps(s)
366         } else {
367             ImportItem::CamelCase(s)
368         }
369     }
370
371     fn from_opt_str(s: Option<&String>) -> ImportItem {
372         s.map_or(ImportItem::Invalid, |s| ImportItem::from_str(s))
373     }
374
375     fn to_str(&self) -> Option<&str> {
376         match *self {
377             ImportItem::SelfImport(s)
378             | ImportItem::SnakeCase(s)
379             | ImportItem::CamelCase(s)
380             | ImportItem::AllCaps(s) => Some(s),
381             ImportItem::Invalid => None,
382         }
383     }
384
385     fn to_u32(&self) -> u32 {
386         match *self {
387             ImportItem::SelfImport(..) => 0,
388             ImportItem::SnakeCase(..) => 1,
389             ImportItem::CamelCase(..) => 2,
390             ImportItem::AllCaps(..) => 3,
391             ImportItem::Invalid => 4,
392         }
393     }
394 }
395
396 impl<'a> PartialOrd for ImportItem<'a> {
397     fn partial_cmp(&self, other: &ImportItem<'a>) -> Option<Ordering> {
398         Some(self.cmp(other))
399     }
400 }
401
402 impl<'a> Ord for ImportItem<'a> {
403     fn cmp(&self, other: &ImportItem<'a>) -> Ordering {
404         let res = self.to_u32().cmp(&other.to_u32());
405         if res != Ordering::Equal {
406             return res;
407         }
408         self.to_str().map_or(Ordering::Greater, |self_str| {
409             other
410                 .to_str()
411                 .map_or(Ordering::Less, |other_str| self_str.cmp(other_str))
412         })
413     }
414 }
415
416 // Pretty prints a multi-item import.
417 // If the path list is empty, it leaves the braces empty.
418 fn rewrite_nested_use_tree(
419     shape: Shape,
420     path: &ast::Path,
421     trees: &[(ast::UseTree, ast::NodeId)],
422     span: Span,
423     context: &RewriteContext,
424 ) -> Option<String> {
425     // Returns a different option to distinguish `::foo` and `foo`
426     let path_str = rewrite_path(context, PathContext::Import, None, path, shape)?;
427
428     match trees.len() {
429         0 => {
430             return rewrite_path(context, PathContext::Import, None, path, shape)
431                 .map(|path_str| format!("{}::{{}}", path_str));
432         }
433         // TODO: fix this
434         1 => return Some(rewrite_nested_use_tree_single(path_str, &trees[0].0)),
435         _ => (),
436     }
437
438     let path_str = if path_str.is_empty() {
439         path_str
440     } else {
441         format!("{}::", path_str)
442     };
443
444     // 2 = "{}"
445     let remaining_width = shape.width.checked_sub(path_str.len() + 2).unwrap_or(0);
446
447     let mut items = {
448         // Dummy value, see explanation below.
449         let mut items = vec![ListItem::from_str("")];
450         let iter = itemize_list(
451             context.codemap,
452             trees.iter().map(|ref tree| &tree.0),
453             "}",
454             ",",
455             |tree| tree.span.lo(),
456             |tree| tree.span.hi(),
457             rewrite_nested_use_tree_item,
458             context.codemap.span_after(span, "{"),
459             span.hi(),
460             false,
461         );
462         items.extend(iter);
463         items
464     };
465
466     // We prefixed the item list with a dummy value so that we can
467     // potentially move "self" to the front of the vector without touching
468     // the rest of the items.
469     let has_self = move_self_to_front(&mut items);
470     let first_index = if has_self { 0 } else { 1 };
471
472     if context.config.reorder_imported_names() {
473         items[1..].sort_by(|a, b| {
474             let a = ImportItem::from_opt_str(a.item.as_ref());
475             let b = ImportItem::from_opt_str(b.item.as_ref());
476             a.cmp(&b)
477         });
478     }
479
480     let tactic = definitive_tactic(
481         &items[first_index..],
482         context.config.imports_layout(),
483         Separator::Comma,
484         remaining_width,
485     );
486
487     let nested_indent = match context.config.imports_indent() {
488         IndentStyle::Block => shape.indent.block_indent(context.config),
489         // 1 = `{`
490         IndentStyle::Visual => shape.visual_indent(path_str.len() + 1).indent,
491     };
492
493     let nested_shape = match context.config.imports_indent() {
494         IndentStyle::Block => Shape::indented(nested_indent, context.config),
495         IndentStyle::Visual => Shape::legacy(remaining_width, nested_indent),
496     };
497
498     let ends_with_newline = context.config.imports_indent() == IndentStyle::Block
499         && tactic != DefinitiveListTactic::Horizontal;
500
501     let fmt = ListFormatting {
502         tactic: tactic,
503         separator: ",",
504         trailing_separator: if ends_with_newline {
505             context.config.trailing_comma()
506         } else {
507             SeparatorTactic::Never
508         },
509         separator_place: SeparatorPlace::Back,
510         shape: nested_shape,
511         ends_with_newline: ends_with_newline,
512         preserve_newline: true,
513         config: context.config,
514     };
515     let list_str = write_list(&items[first_index..], &fmt)?;
516
517     let result = if list_str.contains('\n') && context.config.imports_indent() == IndentStyle::Block
518     {
519         format!(
520             "{}{{\n{}{}\n{}}}",
521             path_str,
522             nested_shape.indent.to_string(context.config),
523             list_str,
524             shape.indent.to_string(context.config)
525         )
526     } else {
527         format!("{}{{{}}}", path_str, list_str)
528     };
529     Some(result)
530 }
531
532 // Returns true when self item was found.
533 fn move_self_to_front(items: &mut Vec<ListItem>) -> bool {
534     match items
535         .iter()
536         .position(|item| item.item.as_ref().map(|x| &x[..]) == Some("self"))
537     {
538         Some(pos) => {
539             items[0] = items.remove(pos);
540             true
541         }
542         None => false,
543     }
544 }