Skip to content

Commit a1fd68d

Browse files
authored
Merge pull request #1948 from topecongiro/issue-1137
Use write_list() to format imports
2 parents 3b6a5fa + 903d815 commit a1fd68d

File tree

4 files changed

+147
-142
lines changed

4 files changed

+147
-142
lines changed

src/imports.rs

Lines changed: 106 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,21 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
use std::cmp::{self, Ordering};
11+
use std::cmp::Ordering;
1212

1313
use syntax::{ast, ptr};
1414
use syntax::codemap::{BytePos, Span};
1515

16-
use Shape;
16+
use {Shape, Spanned};
1717
use codemap::SpanUtils;
18+
use comment::combine_strs_with_missing_comments;
1819
use config::IndentStyle;
1920
use lists::{definitive_tactic, itemize_list, write_list, DefinitiveListTactic, ListFormatting,
2021
ListItem, Separator, SeparatorPlace, SeparatorTactic};
2122
use rewrite::{Rewrite, RewriteContext};
2223
use types::{rewrite_path, PathContext};
23-
use utils;
24-
use visitor::FmtVisitor;
24+
use utils::{format_visibility, mk_sp};
25+
use visitor::{rewrite_extern_crate, FmtVisitor};
2526

2627
fn path_of(a: &ast::ViewPath_) -> &ast::Path {
2728
match *a {
@@ -185,95 +186,115 @@ impl Rewrite for ast::ViewPath {
185186
}
186187
}
187188

188-
impl<'a> FmtVisitor<'a> {
189-
pub fn format_imports(&mut self, use_items: &[ptr::P<ast::Item>]) {
190-
// Find the location immediately before the first use item in the run. This must not lie
191-
// before the current `self.last_pos`
192-
let pos_before_first_use_item = use_items
193-
.first()
194-
.map(|p_i| {
195-
cmp::max(
196-
self.last_pos,
197-
p_i.attrs
198-
.iter()
199-
.map(|attr| attr.span.lo())
200-
.min()
201-
.unwrap_or(p_i.span.lo()),
202-
)
203-
})
204-
.unwrap_or(self.last_pos);
205-
// Construct a list of pairs, each containing a `use` item and the start of span before
206-
// that `use` item.
207-
let mut last_pos_of_prev_use_item = pos_before_first_use_item;
208-
let mut ordered_use_items = use_items
209-
.iter()
210-
.map(|p_i| {
211-
let new_item = (&*p_i, last_pos_of_prev_use_item);
212-
last_pos_of_prev_use_item = p_i.span.hi();
213-
new_item
214-
})
215-
.collect::<Vec<_>>();
216-
let pos_after_last_use_item = last_pos_of_prev_use_item;
217-
// Order the imports by view-path & other import path properties
218-
ordered_use_items.sort_by(|a, b| {
219-
compare_use_items(&self.get_context(), a.0, b.0).unwrap()
220-
});
221-
// First, output the span before the first import
222-
let prev_span_str = self.snippet(utils::mk_sp(self.last_pos, pos_before_first_use_item));
223-
// Look for purely trailing space at the start of the prefix snippet before a linefeed, or
224-
// a prefix that's entirely horizontal whitespace.
225-
let prefix_span_start = match prev_span_str.find('\n') {
226-
Some(offset) if prev_span_str[..offset].trim().is_empty() => {
227-
self.last_pos + BytePos(offset as u32)
228-
}
229-
None if prev_span_str.trim().is_empty() => pos_before_first_use_item,
230-
_ => self.last_pos,
231-
};
232-
// Look for indent (the line part preceding the use is all whitespace) and excise that
233-
// from the prefix
234-
let span_end = match prev_span_str.rfind('\n') {
235-
Some(offset) if prev_span_str[offset..].trim().is_empty() => {
236-
self.last_pos + BytePos(offset as u32)
189+
// Rewrite `use foo;` WITHOUT attributes.
190+
fn rewrite_import(
191+
context: &RewriteContext,
192+
vis: &ast::Visibility,
193+
vp: &ast::ViewPath,
194+
attrs: &[ast::Attribute],
195+
shape: Shape,
196+
) -> Option<String> {
197+
let vis = format_visibility(vis);
198+
// 4 = `use `, 1 = `;`
199+
let rw = shape
200+
.offset_left(vis.len() + 4)
201+
.and_then(|shape| shape.sub_width(1))
202+
.and_then(|shape| match vp.node {
203+
// If we have an empty path list with no attributes, we erase it
204+
ast::ViewPath_::ViewPathList(_, ref path_list)
205+
if path_list.is_empty() && attrs.is_empty() =>
206+
{
207+
Some("".into())
237208
}
238-
_ => pos_before_first_use_item,
239-
};
209+
_ => vp.rewrite(context, shape),
210+
});
211+
match rw {
212+
Some(ref s) if !s.is_empty() => Some(format!("{}use {};", vis, s)),
213+
_ => rw,
214+
}
215+
}
240216

241-
self.last_pos = prefix_span_start;
242-
self.format_missing(span_end);
243-
for ordered in ordered_use_items {
244-
// Fake out the formatter by setting `self.last_pos` to the appropriate location before
245-
// each item before visiting it.
246-
self.last_pos = ordered.1;
247-
self.visit_item(ordered.0);
217+
fn rewrite_imports(
218+
context: &RewriteContext,
219+
use_items: &[ptr::P<ast::Item>],
220+
shape: Shape,
221+
span: Span,
222+
) -> Option<String> {
223+
let items = itemize_list(
224+
context.codemap,
225+
use_items.iter(),
226+
"",
227+
|item| item.span().lo(),
228+
|item| item.span().hi(),
229+
|item| {
230+
let attrs_str = try_opt!(item.attrs.rewrite(context, shape));
231+
232+
let missed_span = if item.attrs.is_empty() {
233+
mk_sp(item.span.lo(), item.span.lo())
234+
} else {
235+
mk_sp(item.attrs.last().unwrap().span.hi(), item.span.lo())
236+
};
237+
238+
let item_str = match item.node {
239+
ast::ItemKind::Use(ref vp) => {
240+
try_opt!(rewrite_import(context, &item.vis, vp, &item.attrs, shape))
241+
}
242+
ast::ItemKind::ExternCrate(..) => try_opt!(rewrite_extern_crate(context, item)),
243+
_ => return None,
244+
};
245+
246+
combine_strs_with_missing_comments(
247+
context,
248+
&attrs_str,
249+
&item_str,
250+
missed_span,
251+
shape,
252+
false,
253+
)
254+
},
255+
span.lo(),
256+
span.hi(),
257+
false,
258+
);
259+
let mut item_pair_vec: Vec<_> = items.zip(use_items.iter()).collect();
260+
item_pair_vec.sort_by(|a, b| compare_use_items(context, a.1, b.1).unwrap());
261+
let item_vec: Vec<_> = item_pair_vec.into_iter().map(|pair| pair.0).collect();
262+
263+
let fmt = ListFormatting {
264+
tactic: DefinitiveListTactic::Vertical,
265+
separator: "",
266+
trailing_separator: SeparatorTactic::Never,
267+
separator_place: SeparatorPlace::Back,
268+
shape: shape,
269+
ends_with_newline: true,
270+
preserve_newline: false,
271+
config: context.config,
272+
};
273+
274+
write_list(&item_vec, &fmt)
275+
}
276+
277+
impl<'a> FmtVisitor<'a> {
278+
pub fn format_imports(&mut self, use_items: &[ptr::P<ast::Item>]) {
279+
if use_items.is_empty() {
280+
return;
248281
}
249-
self.last_pos = pos_after_last_use_item;
282+
283+
let lo = use_items.first().unwrap().span().lo();
284+
let hi = use_items.last().unwrap().span().hi();
285+
let span = mk_sp(lo, hi);
286+
let rw = rewrite_imports(&self.get_context(), use_items, self.shape(), span);
287+
self.push_rewrite(span, rw);
250288
}
251289

252-
pub fn format_import(
253-
&mut self,
254-
vis: &ast::Visibility,
255-
vp: &ast::ViewPath,
256-
span: Span,
257-
attrs: &[ast::Attribute],
258-
) {
259-
let vis = utils::format_visibility(vis);
260-
// 4 = `use `, 1 = `;`
261-
let rw = self.shape()
262-
.offset_left(vis.len() + 4)
263-
.and_then(|shape| shape.sub_width(1))
264-
.and_then(|shape| match vp.node {
265-
// If we have an empty path list with no attributes, we erase it
266-
ast::ViewPath_::ViewPathList(_, ref path_list)
267-
if path_list.is_empty() && attrs.is_empty() =>
268-
{
269-
Some("".into())
270-
}
271-
_ => vp.rewrite(&self.get_context(), shape),
272-
});
290+
pub fn format_import(&mut self, item: &ast::Item, vp: &ast::ViewPath) {
291+
let span = item.span;
292+
let shape = self.shape();
293+
let rw = rewrite_import(&self.get_context(), &item.vis, vp, &item.attrs, shape);
273294
match rw {
274295
Some(ref s) if s.is_empty() => {
275296
// Format up to last newline
276-
let prev_span = utils::mk_sp(self.last_pos, source!(self, span).lo());
297+
let prev_span = mk_sp(self.last_pos, source!(self, span).lo());
277298
let span_end = match self.snippet(prev_span).rfind('\n') {
278299
Some(offset) => self.last_pos + BytePos(offset as u32),
279300
None => source!(self, span).lo(),
@@ -282,7 +303,6 @@ impl<'a> FmtVisitor<'a> {
282303
self.last_pos = source!(self, span).hi();
283304
}
284305
Some(ref s) => {
285-
let s = format!("{}use {};", vis, s);
286306
self.format_missing_with_indent(source!(self, span).lo());
287307
self.buffer.push_str(&s);
288308
self.last_pos = source!(self, span).hi();

src/visitor.rs

Lines changed: 41 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,9 @@ use comment::{contains_comment, recover_missing_comment_in_span, CodeCharKind, C
2222
FindUncommented};
2323
use comment::rewrite_comment;
2424
use config::{BraceStyle, Config};
25-
use items::{format_impl, format_trait, rewrite_associated_impl_type, rewrite_associated_type,
26-
rewrite_static, rewrite_type_alias};
25+
use items::{format_impl, format_struct, format_struct_struct, format_trait,
26+
rewrite_associated_impl_type, rewrite_associated_type, rewrite_static,
27+
rewrite_type_alias};
2728
use lists::{itemize_list, write_list, DefinitiveListTactic, ListFormatting, SeparatorPlace,
2829
SeparatorTactic};
2930
use macros::{rewrite_macro, MacroPosition};
@@ -296,7 +297,8 @@ impl<'a> FmtVisitor<'a> {
296297
// complex in the module case. It is complex because the module could be
297298
// in a separate file and there might be attributes in both files, but
298299
// the AST lumps them all together.
299-
let mut attrs = item.attrs.clone();
300+
let filterd_attrs;
301+
let mut attrs = &item.attrs;
300302
match item.node {
301303
ast::ItemKind::Mod(ref m) => {
302304
let outer_file = self.codemap.lookup_char_pos(item.span.lo()).file;
@@ -314,7 +316,7 @@ impl<'a> FmtVisitor<'a> {
314316
} else {
315317
// Module is not inline and should not be skipped. We want
316318
// to process only the attributes in the current file.
317-
let filterd_attrs = item.attrs
319+
filterd_attrs = item.attrs
318320
.iter()
319321
.filter_map(|a| {
320322
let attr_file = self.codemap.lookup_char_pos(a.span.lo()).file;
@@ -328,7 +330,7 @@ impl<'a> FmtVisitor<'a> {
328330
// Assert because if we should skip it should be caught by
329331
// the above case.
330332
assert!(!self.visit_attrs(&filterd_attrs, ast::AttrStyle::Outer));
331-
attrs = filterd_attrs;
333+
attrs = &filterd_attrs;
332334
}
333335
}
334336
_ => if self.visit_attrs(&item.attrs, ast::AttrStyle::Outer) {
@@ -338,62 +340,38 @@ impl<'a> FmtVisitor<'a> {
338340
}
339341

340342
match item.node {
341-
ast::ItemKind::Use(ref vp) => {
342-
self.format_import(&item.vis, vp, item.span, &item.attrs);
343-
}
343+
ast::ItemKind::Use(ref vp) => self.format_import(&item, vp),
344344
ast::ItemKind::Impl(..) => {
345-
self.format_missing_with_indent(source!(self, item.span).lo());
346345
let snippet = self.snippet(item.span);
347346
let where_span_end = snippet
348347
.find_uncommented("{")
349348
.map(|x| (BytePos(x as u32)) + source!(self, item.span).lo());
350-
if let Some(impl_str) =
351-
format_impl(&self.get_context(), item, self.block_indent, where_span_end)
352-
{
353-
self.buffer.push_str(&impl_str);
354-
self.last_pos = source!(self, item.span).hi();
355-
}
349+
let rw = format_impl(&self.get_context(), item, self.block_indent, where_span_end);
350+
self.push_rewrite(item.span, rw);
356351
}
357352
ast::ItemKind::Trait(..) => {
358-
self.format_missing_with_indent(item.span.lo());
359-
if let Some(trait_str) = format_trait(&self.get_context(), item, self.block_indent)
360-
{
361-
self.buffer.push_str(&trait_str);
362-
self.last_pos = source!(self, item.span).hi();
363-
}
353+
let rw = format_trait(&self.get_context(), item, self.block_indent);
354+
self.push_rewrite(item.span, rw);
364355
}
365356
ast::ItemKind::ExternCrate(_) => {
366-
self.format_missing_with_indent(source!(self, item.span).lo());
367-
let new_str = self.snippet(item.span);
368-
if contains_comment(&new_str) {
369-
self.buffer.push_str(&new_str)
370-
} else {
371-
let no_whitespace =
372-
&new_str.split_whitespace().collect::<Vec<&str>>().join(" ");
373-
self.buffer
374-
.push_str(&Regex::new(r"\s;").unwrap().replace(no_whitespace, ";"));
375-
}
376-
self.last_pos = source!(self, item.span).hi();
357+
let rw = rewrite_extern_crate(&self.get_context(), item);
358+
self.push_rewrite(item.span, rw);
377359
}
378360
ast::ItemKind::Struct(ref def, ref generics) => {
379-
let rewrite = {
380-
let indent = self.block_indent;
381-
let context = self.get_context();
382-
::items::format_struct(
383-
&context,
384-
"struct ",
385-
item.ident,
386-
&item.vis,
387-
def,
388-
Some(generics),
389-
item.span,
390-
indent,
391-
None,
392-
).map(|s| match *def {
393-
ast::VariantData::Tuple(..) => s + ";",
394-
_ => s,
395-
})
396-
};
361+
let rewrite = format_struct(
362+
&self.get_context(),
363+
"struct ",
364+
item.ident,
365+
&item.vis,
366+
def,
367+
Some(generics),
368+
item.span,
369+
self.block_indent,
370+
None,
371+
).map(|s| match *def {
372+
ast::VariantData::Tuple(..) => s + ";",
373+
_ => s,
374+
});
397375
self.push_rewrite(item.span, rewrite);
398376
}
399377
ast::ItemKind::Enum(ref def, ref generics) => {
@@ -474,7 +452,7 @@ impl<'a> FmtVisitor<'a> {
474452
self.push_rewrite(item.span, rewrite);
475453
}
476454
ast::ItemKind::Union(ref def, ref generics) => {
477-
let rewrite = ::items::format_struct_struct(
455+
let rewrite = format_struct_struct(
478456
&self.get_context(),
479457
"union ",
480458
item.ident,
@@ -1042,3 +1020,15 @@ fn get_derive_args(context: &RewriteContext, attr: &ast::Attribute) -> Option<Ve
10421020
_ => None,
10431021
})
10441022
}
1023+
1024+
// Rewrite `extern crate foo;` WITHOUT attributes.
1025+
pub fn rewrite_extern_crate(context: &RewriteContext, item: &ast::Item) -> Option<String> {
1026+
assert!(is_extern_crate(item));
1027+
let new_str = context.snippet(item.span);
1028+
Some(if contains_comment(&new_str) {
1029+
new_str
1030+
} else {
1031+
let no_whitespace = &new_str.split_whitespace().collect::<Vec<&str>>().join(" ");
1032+
String::from(&*Regex::new(r"\s;").unwrap().replace(no_whitespace, ";"))
1033+
})
1034+
}

tests/target/configs-reorder_imports_in_group-false.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
use dolor;
66
/// This comment should stay with `use ipsum;`
77
use ipsum;
8-
98
use lorem;
109
use sit;
1110
use std::io;

tests/target/issue-1124.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,6 @@ mod a {
1212
}
1313

1414
use a;
15-
16-
17-
1815
use x;
19-
2016
use y;
2117
use z;

0 commit comments

Comments
 (0)