diff --git a/Cargo.lock b/Cargo.lock index 97593cfad4c..82dbafd47d6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,16 +2,16 @@ name = "rustfmt" version = "0.0.1" dependencies = [ - "diff 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)", + "diff 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", "strings 0.0.1 (git+https://github.com/nrc/strings.rs.git)", - "toml 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)", + "toml 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "aho-corasick" -version = "0.2.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "memchr 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -19,7 +19,7 @@ dependencies = [ [[package]] name = "diff" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -37,17 +37,17 @@ dependencies = [ [[package]] name = "regex" -version = "0.1.38" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "aho-corasick 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "aho-corasick 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "regex-syntax" -version = "0.1.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -58,11 +58,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "strings" version = "0.0.1" -source = "git+https://github.com/nrc/strings.rs.git#b7f37c4545b7dba24fb28161cd9c405fae978be4" +source = "git+https://github.com/nrc/strings.rs.git#6d748148fbe3bf2d9e5ac2ede65ac503d7491a4f" [[package]] name = "toml" -version = "0.1.20" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "rustc-serialize 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/src/changes.rs b/src/changes.rs deleted file mode 100644 index ab5968dbf76..00000000000 --- a/src/changes.rs +++ /dev/null @@ -1,256 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - - -// TODO -// print to files -// tests - -use strings::string_buffer::StringBuffer; -use std::collections::HashMap; -use syntax::codemap::{CodeMap, Span, BytePos}; -use std::fmt; -use std::fs::File; -use std::io::{Write, stdout}; -use WriteMode; -use NewlineStyle; -use config::Config; -use utils::round_up_to_power_of_two; - -// This is basically a wrapper around a bunch of Ropes which makes it convenient -// to work with libsyntax. It is badly named. -pub struct ChangeSet<'a> { - file_map: HashMap, - codemap: &'a CodeMap, - file_spans: Vec<(u32, u32)>, -} - -impl<'a> ChangeSet<'a> { - // Create a new ChangeSet for a given libsyntax CodeMap. - pub fn from_codemap(codemap: &'a CodeMap) -> ChangeSet<'a> { - let mut result = ChangeSet { - file_map: HashMap::new(), - codemap: codemap, - file_spans: Vec::with_capacity(codemap.files.borrow().len()), - }; - - for f in codemap.files.borrow().iter() { - // Use the length of the file as a heuristic for how much space we - // need. Round to the next power of two. - let buffer_cap = round_up_to_power_of_two(f.src.as_ref().unwrap().len()); - - result.file_map.insert(f.name.clone(), StringBuffer::with_capacity(buffer_cap)); - result.file_spans.push((f.start_pos.0, f.end_pos.0)); - } - - result.file_spans.sort(); - - result - } - - pub fn filespans_for_span(&self, start: BytePos, end: BytePos) -> Vec<(u32, u32)> { - assert!(start.0 <= end.0); - - if self.file_spans.len() == 0 { - return Vec::new(); - } - - // idx is the index into file_spans which indicates the current file, we - // with the file start denotes. - let mut idx = match self.file_spans.binary_search(&(start.0, ::std::u32::MAX)) { - Ok(i) => i, - Err(0) => 0, - Err(i) => i - 1, - }; - - let mut result = Vec::new(); - let mut start = start.0; - loop { - let cur_file = &self.file_spans[idx]; - idx += 1; - - if idx >= self.file_spans.len() || start >= end.0 { - if start < end.0 { - result.push((start, end.0)); - } - return result; - } - - let end = ::std::cmp::min(cur_file.1 - 1, end.0); - if start < end { - result.push((start, end)); - } - start = self.file_spans[idx].0; - } - } - - pub fn push_str(&mut self, filename: &str, text: &str) { - let buf = self.file_map.get_mut(&*filename).unwrap(); - buf.push_str(text) - } - - pub fn push_str_span(&mut self, span: Span, text: &str) { - let file_name = self.codemap.span_to_filename(span); - self.push_str(&file_name, text) - } - - // Fetch the output buffer for the given file name. - // Panics on unknown files. - pub fn get(&mut self, file_name: &str) -> &StringBuffer { - self.file_map.get(file_name).unwrap() - } - - // Fetch a mutable reference to the output buffer for the given file name. - // Panics on unknown files. - pub fn get_mut(&mut self, file_name: &str) -> &mut StringBuffer { - self.file_map.get_mut(file_name).unwrap() - } - - pub fn cur_offset(&mut self, filename: &str) -> usize { - self.file_map[&*filename].cur_offset() - } - - pub fn cur_offset_span(&mut self, span: Span) -> usize { - let filename = self.codemap.span_to_filename(span); - self.cur_offset(&filename) - } - - // Return an iterator over the entire changed text. - pub fn text<'c>(&'c self) -> FileIterator<'c, 'a> { - FileIterator { change_set: self, keys: self.file_map.keys().collect(), cur_key: 0 } - } - - // Append a newline to the end of each file. - pub fn append_newlines(&mut self) { - for (_, s) in self.file_map.iter_mut() { - s.push_str("\n"); - } - } - - pub fn write_all_files(&self, - mode: WriteMode, - config: &Config) - -> Result<(HashMap), ::std::io::Error> { - let mut result = HashMap::new(); - for filename in self.file_map.keys() { - let one_result = try!(self.write_file(filename, mode, config)); - if let Some(r) = one_result { - result.insert(filename.clone(), r); - } - } - - Ok(result) - } - - pub fn write_file(&self, - filename: &str, - mode: WriteMode, - config: &Config) - -> Result, ::std::io::Error> { - let text = &self.file_map[filename]; - - // prints all newlines either as `\n` or as `\r\n` - fn write_system_newlines(mut writer: T, - text: &StringBuffer, - config: &Config) - -> Result<(), ::std::io::Error> - where T: Write - { - match config.newline_style { - NewlineStyle::Unix => write!(writer, "{}", text), - NewlineStyle::Windows => { - for (c, _) in text.chars() { - match c { - '\n' => try!(write!(writer, "\r\n")), - '\r' => continue, - c => try!(write!(writer, "{}", c)), - } - } - Ok(()) - }, - } - } - - match mode { - WriteMode::Overwrite => { - // Do a little dance to make writing safer - write to a temp file - // rename the original to a .bk, then rename the temp file to the - // original. - let tmp_name = filename.to_owned() + ".tmp"; - let bk_name = filename.to_owned() + ".bk"; - { - // Write text to temp file - let tmp_file = try!(File::create(&tmp_name)); - try!(write_system_newlines(tmp_file, text, config)); - } - - try!(::std::fs::rename(filename, bk_name)); - try!(::std::fs::rename(tmp_name, filename)); - } - WriteMode::NewFile(extn) => { - let filename = filename.to_owned() + "." + extn; - let file = try!(File::create(&filename)); - try!(write_system_newlines(file, text, config)); - } - WriteMode::Display => { - println!("{}:\n", filename); - let stdout = stdout(); - let stdout_lock = stdout.lock(); - try!(write_system_newlines(stdout_lock, text, config)); - } - WriteMode::Return(_) => { - // io::Write is not implemented for String, working around with Vec - let mut v = Vec::new(); - try!(write_system_newlines(&mut v, text, config)); - // won't panic, we are writing correct utf8 - return Ok(Some(String::from_utf8(v).unwrap())); - } - } - - Ok(None) - } - - pub fn is_changed(&self, filename: &str) -> bool { - self.file_map.get(filename).expect("Unknown filename").len != 0 - } -} - -// Iterates over each file in the ChangSet. Yields the filename and the changed -// text for that file. -pub struct FileIterator<'c, 'a: 'c> { - change_set: &'c ChangeSet<'a>, - keys: Vec<&'c String>, - cur_key: usize, -} - -impl<'c, 'a> Iterator for FileIterator<'c, 'a> { - type Item = (&'c str, &'c StringBuffer); - - fn next(&mut self) -> Option<(&'c str, &'c StringBuffer)> { - if self.cur_key >= self.keys.len() { - return None; - } - - let key = self.keys[self.cur_key]; - self.cur_key += 1; - return Some((&key, &self.change_set.file_map[&*key])) - } -} - -impl<'a> fmt::Display for ChangeSet<'a> { - // Prints the entire changed text. - fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { - for (f, r) in self.text() { - try!(write!(fmt, "{}:\n", f)); - try!(write!(fmt, "{}\n\n", r)); - } - Ok(()) - } -} diff --git a/src/expr.rs b/src/expr.rs index b8c37dcab4c..334c509c59d 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -138,13 +138,9 @@ impl Rewrite for ast::Block { // Push text between last block item and end of block let snippet = visitor.snippet(mk_sp(visitor.last_pos, self.span.hi)); - visitor.changes.push_str_span(self.span, &snippet); + visitor.buffer.push_str(&snippet); - // Stringify visitor - let file_name = context.codemap.span_to_filename(self.span); - let string_buffer = visitor.changes.get(&file_name); - - Some(format!("{}{}", prefix, string_buffer)) + Some(format!("{}{}", prefix, visitor.buffer)) } } diff --git a/src/filemap.rs b/src/filemap.rs new file mode 100644 index 00000000000..8e09837df0a --- /dev/null +++ b/src/filemap.rs @@ -0,0 +1,112 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + + +// TODO tests + +use strings::string_buffer::StringBuffer; +use std::collections::HashMap; +use std::fs::{self, File}; +use std::io::{self, Write, stdout}; +use WriteMode; +use NewlineStyle; +use config::Config; + +// A map of the files of a crate, with their new content +pub type FileMap = HashMap; + +// Append a newline to the end of each file. +pub fn append_newlines(file_map: &mut FileMap) { + for (_, s) in file_map.iter_mut() { + s.push_str("\n"); + } +} + +pub fn write_all_files(file_map: &FileMap, + mode: WriteMode, + config: &Config) + -> Result<(HashMap), io::Error> { + let mut result = HashMap::new(); + for filename in file_map.keys() { + let one_result = try!(write_file(&file_map[filename], filename, mode, config)); + if let Some(r) = one_result { + result.insert(filename.clone(), r); + } + } + + Ok(result) +} + +fn write_file(text: &StringBuffer, + filename: &str, + mode: WriteMode, + config: &Config) + -> Result, io::Error> { + + // prints all newlines either as `\n` or as `\r\n` + fn write_system_newlines(mut writer: T, + text: &StringBuffer, + config: &Config) + -> Result<(), io::Error> + where T: Write + { + match config.newline_style { + NewlineStyle::Unix => write!(writer, "{}", text), + NewlineStyle::Windows => { + for (c, _) in text.chars() { + match c { + '\n' => try!(write!(writer, "\r\n")), + '\r' => continue, + c => try!(write!(writer, "{}", c)), + } + } + Ok(()) + }, + } + } + + match mode { + WriteMode::Overwrite => { + // Do a little dance to make writing safer - write to a temp file + // rename the original to a .bk, then rename the temp file to the + // original. + let tmp_name = filename.to_owned() + ".tmp"; + let bk_name = filename.to_owned() + ".bk"; + { + // Write text to temp file + let tmp_file = try!(File::create(&tmp_name)); + try!(write_system_newlines(tmp_file, text, config)); + } + + try!(fs::rename(filename, bk_name)); + try!(fs::rename(tmp_name, filename)); + } + WriteMode::NewFile(extn) => { + let filename = filename.to_owned() + "." + extn; + let file = try!(File::create(&filename)); + try!(write_system_newlines(file, text, config)); + } + WriteMode::Display => { + println!("{}:\n", filename); + let stdout = stdout(); + let stdout_lock = stdout.lock(); + try!(write_system_newlines(stdout_lock, text, config)); + } + WriteMode::Return(_) => { + // io::Write is not implemented for String, working around with Vec + let mut v = Vec::new(); + try!(write_system_newlines(&mut v, text, config)); + // won't panic, we are writing correct utf8 + return Ok(Some(String::from_utf8(v).unwrap())); + } + } + + Ok(None) +} diff --git a/src/items.rs b/src/items.rs index eb727a20a56..378d822d413 100644 --- a/src/items.rs +++ b/src/items.rs @@ -382,7 +382,7 @@ impl<'a> FmtVisitor<'a> { generics: &ast::Generics, span: Span) { let header_str = self.format_header("enum ", ident, vis); - self.changes.push_str_span(span, &header_str); + self.buffer.push_str(&header_str); let enum_snippet = self.snippet(span); let body_start = span.lo + BytePos(enum_snippet.find_uncommented("{").unwrap() as u32 + 1); @@ -391,7 +391,7 @@ impl<'a> FmtVisitor<'a> { self.block_indent + self.config.tab_spaces, codemap::mk_sp(span.lo, body_start)); - self.changes.push_str_span(span, &generics_str); + self.buffer.push_str(&generics_str); self.last_pos = body_start; self.block_indent += self.config.tab_spaces; @@ -407,7 +407,7 @@ impl<'a> FmtVisitor<'a> { self.block_indent -= self.config.tab_spaces; self.format_missing_with_indent(span.lo + BytePos(enum_snippet.rfind('}').unwrap() as u32)); - self.changes.push_str_span(span, "}"); + self.buffer.push_str("}"); } // Variant of an enum @@ -421,9 +421,9 @@ impl<'a> FmtVisitor<'a> { let result = match field.node.kind { ast::VariantKind::TupleVariantKind(ref types) => { let vis = format_visibility(field.node.vis); - self.changes.push_str_span(field.span, vis); + self.buffer.push_str(vis); let name = field.node.name.to_string(); - self.changes.push_str_span(field.span, &name); + self.buffer.push_str(&name); let mut result = String::new(); @@ -491,10 +491,10 @@ impl<'a> FmtVisitor<'a> { self.block_indent) } }; - self.changes.push_str_span(field.span, &result); + self.buffer.push_str(&result); if !last_field || self.config.enum_trailing_comma { - self.changes.push_str_span(field.span, ","); + self.buffer.push_str(","); } self.last_pos = field.span.hi + BytePos(1); @@ -621,7 +621,7 @@ impl<'a> FmtVisitor<'a> { Some(generics), span, indent); - self.changes.push_str_span(span, &result); + self.buffer.push_str(&result); self.last_pos = span.hi; } diff --git a/src/lib.rs b/src/lib.rs index 93b66f45b56..c0aff9a05b3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -37,7 +37,6 @@ use rustc_driver::{driver, CompilerCalls, Compilation}; use syntax::ast; use syntax::codemap::CodeMap; use syntax::diagnostics; -use syntax::visit; use std::path::PathBuf; use std::collections::HashMap; @@ -45,14 +44,14 @@ use std::fmt; use std::mem::swap; use issues::{BadIssueSeeker, Issue}; -use changes::ChangeSet; +use filemap::FileMap; use visitor::FmtVisitor; use config::Config; #[macro_use] mod utils; pub mod config; -mod changes; +mod filemap; mod visitor; mod items; mod missed_spans; @@ -64,6 +63,7 @@ mod issues; mod rewrite; mod string; mod comment; +mod modules; const MIN_STRING: usize = 10; // When we get scoped annotations, we should have rustfmt::skip. @@ -196,21 +196,26 @@ impl fmt::Display for FormatReport { } // Formatting which depends on the AST. -fn fmt_ast<'a>(krate: &ast::Crate, codemap: &'a CodeMap, config: &'a Config) -> ChangeSet<'a> { - let mut visitor = FmtVisitor::from_codemap(codemap, config); - visit::walk_crate(&mut visitor, krate); - visitor.changes +fn fmt_ast(krate: &ast::Crate, codemap: &CodeMap, config: &Config) -> FileMap { + let mut file_map = FileMap::new(); + for (path, module) in modules::list_files(krate, codemap) { + let path = path.to_str().unwrap(); + let mut visitor = FmtVisitor::from_codemap(codemap, config); + visitor.format_separate_mod(module, path); + file_map.insert(path.to_owned(), visitor.buffer); + } + file_map } // Formatting done on a char by char or line by line basis. // TODO warn on bad license // TODO other stuff for parity with make tidy -fn fmt_lines(changes: &mut ChangeSet, config: &Config) -> FormatReport { +fn fmt_lines(file_map: &mut FileMap, config: &Config) -> FormatReport { let mut truncate_todo = Vec::new(); let mut report = FormatReport { file_error_map: HashMap::new() }; - // Iterate over the chars in the change set. - for (f, text) in changes.text() { + // Iterate over the chars in the file map. + for (f, text) in file_map.iter() { let mut trims = vec![]; let mut last_wspace: Option = None; let mut line_len = 0; @@ -278,7 +283,7 @@ fn fmt_lines(changes: &mut ChangeSet, config: &Config) -> FormatReport { } for (f, l) in truncate_todo { - changes.get_mut(&f).truncate(l); + file_map.get_mut(&f).unwrap().truncate(l); } report @@ -312,13 +317,13 @@ impl<'a> CompilerCalls<'a> for RustFmtCalls { control.after_parse.callback = Box::new(move |state| { let krate = state.krate.unwrap(); let codemap = state.session.codemap(); - let mut changes = fmt_ast(krate, codemap, &*config); + let mut file_map = fmt_ast(krate, codemap, &*config); // For some reason, the codemap does not include terminating newlines // so we must add one on for each file. This is sad. - changes.append_newlines(); - println!("{}", fmt_lines(&mut changes, &*config)); + filemap::append_newlines(&mut file_map); + println!("{}", fmt_lines(&mut file_map, &*config)); - let result = changes.write_all_files(write_mode, &*config); + let result = filemap::write_all_files(&file_map, write_mode, &*config); match result { Err(msg) => println!("Error writing files: {}", msg), diff --git a/src/missed_spans.rs b/src/missed_spans.rs index d560b3f2027..64025550410 100644 --- a/src/missed_spans.rs +++ b/src/missed_spans.rs @@ -17,34 +17,33 @@ impl<'a> FmtVisitor<'a> { // TODO these format_missing methods are ugly. Refactor and add unit tests // for the central whitespace stripping loop. pub fn format_missing(&mut self, end: BytePos) { - self.format_missing_inner(end, |this, last_snippet, file_name, _| { - this.changes.push_str(file_name, last_snippet) + self.format_missing_inner(end, |this, last_snippet, _| { + this.buffer.push_str(last_snippet) }) } pub fn format_missing_with_indent(&mut self, end: BytePos) { - self.format_missing_inner(end, |this, last_snippet, file_name, snippet| { - this.changes.push_str(file_name, last_snippet.trim_right()); + self.format_missing_inner(end, |this, last_snippet, snippet| { + this.buffer.push_str(last_snippet.trim_right()); if last_snippet == snippet { // No new lines in the snippet. - this.changes.push_str(file_name, "\n"); + this.buffer.push_str("\n"); } let indent = make_indent(this.block_indent); - this.changes.push_str(file_name, &indent); + this.buffer.push_str(&indent); }) } - fn format_missing_inner(&mut self, - end: BytePos, - process_last_snippet: F) { + fn format_missing_inner(&mut self, + end: BytePos, + process_last_snippet: F) { let start = self.last_pos; debug!("format_missing_inner: {:?} to {:?}", self.codemap.lookup_char_pos(start), self.codemap.lookup_char_pos(end)); if start == end { - let file_name = &self.codemap.lookup_char_pos(start).file.name; - process_last_snippet(self, "", file_name, ""); + process_last_snippet(self, "", ""); return; } @@ -54,24 +53,18 @@ impl<'a> FmtVisitor<'a> { self.codemap.lookup_char_pos(end)); self.last_pos = end; - let spans = self.changes.filespans_for_span(start, end); - for (i, &(start, end)) in spans.iter().enumerate() { - let span = codemap::mk_sp(BytePos(start), BytePos(end)); - let file_name = &self.codemap.span_to_filename(span); - let snippet = self.snippet(span); + let span = codemap::mk_sp(start, end); + let snippet = self.snippet(span); - self.write_snippet(&snippet, - file_name, - i == spans.len() - 1, - &process_last_snippet); - } + self.write_snippet(&snippet, + true, + &process_last_snippet); } - fn write_snippet(&mut self, - snippet: &str, - file_name: &str, - last_snippet: bool, - process_last_snippet: F) { + fn write_snippet(&mut self, + snippet: &str, + last_snippet: bool, + process_last_snippet: F) { // Trim whitespace from the right hand side of each line. // Annoyingly, the library functions for splitting by lines etc. are not // quite right, so we must do it ourselves. @@ -80,10 +73,10 @@ impl<'a> FmtVisitor<'a> { for (i, c) in snippet.char_indices() { if c == '\n' { if let Some(lw) = last_wspace { - self.changes.push_str(file_name, &snippet[line_start..lw]); - self.changes.push_str(file_name, "\n"); + self.buffer.push_str(&snippet[line_start..lw]); + self.buffer.push_str("\n"); } else { - self.changes.push_str(file_name, &snippet[line_start..i+1]); + self.buffer.push_str(&snippet[line_start..i+1]); } line_start = i + 1; @@ -99,9 +92,9 @@ impl<'a> FmtVisitor<'a> { } } if last_snippet { - process_last_snippet(self, &snippet[line_start..], file_name, snippet); + process_last_snippet(self, &snippet[line_start..], snippet); } else { - self.changes.push_str(file_name, &snippet[line_start..]); + self.buffer.push_str(&snippet[line_start..]); } } } diff --git a/src/modules.rs b/src/modules.rs new file mode 100644 index 00000000000..2f40fd33a55 --- /dev/null +++ b/src/modules.rs @@ -0,0 +1,72 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use utils; + +use std::path::{Path, PathBuf}; +use std::collections::HashMap; + +use syntax::ast; +use syntax::codemap; +use syntax::parse::parser; + + +/// List all the files containing modules of a crate. +/// If a file is used twice in a crate, it appears only once. +pub fn list_files<'a>(krate: &'a ast::Crate, + codemap: &codemap::CodeMap) + -> HashMap { + let mut result = HashMap::new(); + let root_filename: PathBuf = codemap.span_to_filename(krate.span).into(); + list_submodules(&krate.module, root_filename.parent().unwrap(), codemap, &mut result); + result.insert(root_filename, &krate.module); + result +} + +/// Recursively list all external modules included in a module. +fn list_submodules<'a>(module: &'a ast::Mod, + search_dir: &Path, + codemap: &codemap::CodeMap, + result: &mut HashMap) { + debug!("list_submodules: search_dir: {:?}", search_dir); + for item in module.items.iter() { + if let ast::ItemMod(ref sub_mod) = item.node { + if !utils::contains_skip(&item.attrs) { + let is_internal = codemap.span_to_filename(item.span) == + codemap.span_to_filename(sub_mod.inner); + let dir_path = if is_internal { + search_dir.join(&item.ident.to_string()) + } else { + let mod_path = module_file(item.ident, &item.attrs, search_dir, codemap); + let dir_path = mod_path.parent().unwrap().to_owned(); + result.insert(mod_path, sub_mod); + dir_path + }; + list_submodules(sub_mod, &dir_path, codemap, result); + } + } + } +} + +/// Find the file corresponding to an external mod +fn module_file(id: ast::Ident, + attrs: &[ast::Attribute], + dir_path: &Path, + codemap: &codemap::CodeMap) + -> PathBuf { + if let Some(path) = parser::Parser::submod_path_from_attr(attrs, &dir_path) { + return path; + } + + match parser::Parser::default_submod_path(id, &dir_path, codemap).result { + Ok(parser::ModulePathSuccess { path, .. }) => path, + Err(_) => panic!("Couldn't find module {}", id) + } +} diff --git a/src/visitor.rs b/src/visitor.rs index f4ccfdd5c25..23908edf8d0 100644 --- a/src/visitor.rs +++ b/src/visitor.rs @@ -11,18 +11,16 @@ use syntax::ast; use syntax::codemap::{self, CodeMap, Span, BytePos}; use syntax::visit; -use syntax::parse::parser; -use std::path::PathBuf; + +use strings::string_buffer::StringBuffer; use utils; use config::Config; - -use changes::ChangeSet; use rewrite::{Rewrite, RewriteContext}; pub struct FmtVisitor<'a> { pub codemap: &'a CodeMap, - pub changes: ChangeSet<'a>, + pub buffer: StringBuffer, pub last_pos: BytePos, // TODO RAII util for indenting pub block_indent: usize, @@ -35,7 +33,7 @@ impl<'a, 'v> visit::Visitor<'v> for FmtVisitor<'a> { self.codemap.lookup_char_pos(ex.span.lo), self.codemap.lookup_char_pos(ex.span.hi)); self.format_missing(ex.span.lo); - let offset = self.changes.cur_offset_span(ex.span); + let offset = self.buffer.cur_offset(); let context = RewriteContext { codemap: self.codemap, config: self.config, @@ -44,7 +42,7 @@ impl<'a, 'v> visit::Visitor<'v> for FmtVisitor<'a> { let rewrite = ex.rewrite(&context, self.config.max_width - offset, offset); if let Some(new_str) = rewrite { - self.changes.push_str_span(ex.span, &new_str); + self.buffer.push_str(&new_str); self.last_pos = ex.span.hi; } } @@ -74,7 +72,7 @@ impl<'a, 'v> visit::Visitor<'v> for FmtVisitor<'a> { self.codemap.lookup_char_pos(b.span.lo), self.codemap.lookup_char_pos(b.span.hi)); - self.changes.push_str_span(b.span, "{"); + self.buffer.push_str("{"); self.last_pos = self.last_pos + BytePos(1); self.block_indent += self.config.tab_spaces; @@ -93,7 +91,7 @@ impl<'a, 'v> visit::Visitor<'v> for FmtVisitor<'a> { self.block_indent -= self.config.tab_spaces; // TODO we should compress any newlines here to just one self.format_missing_with_indent(b.span.hi - BytePos(1)); - self.changes.push_str_span(b.span, "}"); + self.buffer.push_str("}"); self.last_pos = b.span.hi; } @@ -126,7 +124,7 @@ impl<'a, 'v> visit::Visitor<'v> for FmtVisitor<'a> { abi, vis, codemap::mk_sp(s.lo, b.span.lo)); - self.changes.push_str_span(s, &new_fn); + self.buffer.push_str(&new_fn); } visit::FkMethod(ident, ref sig, vis) => { let new_fn = self.rewrite_fn(indent, @@ -139,7 +137,7 @@ impl<'a, 'v> visit::Visitor<'v> for FmtVisitor<'a> { &sig.abi, vis.unwrap_or(ast::Visibility::Inherited), codemap::mk_sp(s.lo, b.span.lo)); - self.changes.push_str_span(s, &new_fn); + self.buffer.push_str(&new_fn); } visit::FkFnBlock(..) => {} } @@ -175,7 +173,7 @@ impl<'a, 'v> visit::Visitor<'v> for FmtVisitor<'a> { ast::Item_::ItemExternCrate(_) => { self.format_missing_with_indent(item.span.lo); let new_str = self.snippet(item.span); - self.changes.push_str_span(item.span, &new_str); + self.buffer.push_str(&new_str); self.last_pos = item.span.hi; } ast::Item_::ItemStruct(ref def, ref generics) => { @@ -197,7 +195,7 @@ impl<'a, 'v> visit::Visitor<'v> for FmtVisitor<'a> { } ast::Item_::ItemMod(ref module) => { self.format_missing_with_indent(item.span.lo); - self.format_mod(module, item.span, item.ident, &item.attrs); + self.format_mod(module, item.span, item.ident); } _ => { visit::walk_item(self, item); @@ -219,7 +217,7 @@ impl<'a, 'v> visit::Visitor<'v> for FmtVisitor<'a> { sig, ti.span); - self.changes.push_str_span(ti.span, &new_fn); + self.buffer.push_str(&new_fn); self.last_pos = ti.span.hi; } // TODO format trait types @@ -237,19 +235,13 @@ impl<'a, 'v> visit::Visitor<'v> for FmtVisitor<'a> { fn visit_mac(&mut self, mac: &'v ast::Mac) { visit::walk_mac(self, mac) } - - fn visit_mod(&mut self, m: &'v ast::Mod, s: Span, _: ast::NodeId) { - // This is only called for the root module - let filename = self.codemap.span_to_filename(s); - self.format_separate_mod(m, &filename); - } } impl<'a> FmtVisitor<'a> { pub fn from_codemap<'b>(codemap: &'b CodeMap, config: &'b Config) -> FmtVisitor<'b> { FmtVisitor { codemap: codemap, - changes: ChangeSet::from_codemap(codemap), + buffer: StringBuffer::new(), last_pos: BytePos(0), block_indent: 0, config: config, @@ -281,7 +273,7 @@ impl<'a> FmtVisitor<'a> { true } else { let rewrite = self.rewrite_attrs(attrs, self.block_indent); - self.changes.push_str_span(first.span, &rewrite); + self.buffer.push_str(&rewrite); let last = attrs.last().unwrap(); self.last_pos = last.span.hi; false @@ -322,7 +314,7 @@ impl<'a> FmtVisitor<'a> { result } - fn format_mod(&mut self, m: &ast::Mod, s: Span, ident: ast::Ident, attrs: &[ast::Attribute]) { + fn format_mod(&mut self, m: &ast::Mod, s: Span, ident: ast::Ident) { debug!("FmtVisitor::format_mod: ident: {:?}, span: {:?}", ident, s); // Decide whether this is an inline mod or an external mod. @@ -337,49 +329,15 @@ impl<'a> FmtVisitor<'a> { visit::walk_mod(self, m); debug!("... last_pos after: {:?}", self.last_pos); self.block_indent -= self.config.tab_spaces; - } else { - debug!("FmtVisitor::format_mod: external mod"); - let file_path = self.module_file(ident, attrs, local_file_name); - let filename = file_path.to_str().unwrap(); - if self.changes.is_changed(filename) { - // The file has already been reformatted, do nothing - } else { - self.format_separate_mod(m, filename); - } - } - - debug!("FmtVisitor::format_mod: exit"); - } - - /// Find the file corresponding to an external mod - fn module_file(&self, id: ast::Ident, attrs: &[ast::Attribute], filename: String) -> PathBuf { - let dir_path = { - let mut path = PathBuf::from(&filename); - path.pop(); - path - }; - - if let Some(path) = parser::Parser::submod_path_from_attr(attrs, &dir_path) { - return path; - } - - match parser::Parser::default_submod_path(id, &dir_path, &self.codemap).result { - Ok(parser::ModulePathSuccess { path, .. }) => path, - _ => panic!("Couldn't find module {}", id) } } - /// Format the content of a module into a separate file - fn format_separate_mod(&mut self, m: &ast::Mod, filename: &str) { - let last_pos = self.last_pos; - let block_indent = self.block_indent; + pub fn format_separate_mod(&mut self, m: &ast::Mod, filename: &str) { let filemap = self.codemap.get_filemap(filename); self.last_pos = filemap.start_pos; self.block_indent = 0; visit::walk_mod(self, m); self.format_missing(filemap.end_pos); - self.last_pos = last_pos; - self.block_indent = block_indent; } fn format_import(&mut self, vis: ast::Visibility, vp: &ast::ViewPath, span: Span) { @@ -405,7 +363,7 @@ impl<'a> FmtVisitor<'a> { Some(ref s) => { let s = format!("{}use {};", vis, s); self.format_missing_with_indent(span.lo); - self.changes.push_str_span(span, &s); + self.buffer.push_str(&s); self.last_pos = span.hi; } None => { diff --git a/tests/source/nestedmod/mod.rs b/tests/source/nestedmod/mod.rs index 23dfa444257..d04e49570a4 100644 --- a/tests/source/nestedmod/mod.rs +++ b/tests/source/nestedmod/mod.rs @@ -4,6 +4,7 @@ mod mod2b; mod mymod1 { use mod2a::{Foo,Bar}; +mod mod3a; } #[path="mod2c.rs"] diff --git a/tests/source/nestedmod/mymod1/mod3a.rs b/tests/source/nestedmod/mymod1/mod3a.rs new file mode 100644 index 00000000000..f28bde5e56d --- /dev/null +++ b/tests/source/nestedmod/mymod1/mod3a.rs @@ -0,0 +1,2 @@ +// Another mod +fn a( ) { } diff --git a/tests/target/nestedmod/mod.rs b/tests/target/nestedmod/mod.rs index be22f6d4037..b3456bf0d0f 100644 --- a/tests/target/nestedmod/mod.rs +++ b/tests/target/nestedmod/mod.rs @@ -4,6 +4,7 @@ mod mod2b; mod mymod1 { use mod2a::{Foo, Bar}; + mod mod3a; } #[path="mod2c.rs"] diff --git a/tests/target/nestedmod/mymod1/mod3a.rs b/tests/target/nestedmod/mymod1/mod3a.rs new file mode 100644 index 00000000000..a6399f5565d --- /dev/null +++ b/tests/target/nestedmod/mymod1/mod3a.rs @@ -0,0 +1,3 @@ +// Another mod +fn a() { +}