Merge pull request #162 from cassiersg/sep-mods
Move modules listing outside of FmtVisitor
This commit is contained in:
commit
d07eb28150
22
Cargo.lock
generated
22
Cargo.lock
generated
@ -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)",
|
||||
|
256
src/changes.rs
256
src/changes.rs
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<String, StringBuffer>,
|
||||
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<String, String>), ::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<Option<String>, ::std::io::Error> {
|
||||
let text = &self.file_map[filename];
|
||||
|
||||
// prints all newlines either as `\n` or as `\r\n`
|
||||
fn write_system_newlines<T>(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<u8>
|
||||
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(())
|
||||
}
|
||||
}
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
112
src/filemap.rs
Normal file
112
src/filemap.rs
Normal file
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<String, StringBuffer>;
|
||||
|
||||
// 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<String, String>), 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<Option<String>, io::Error> {
|
||||
|
||||
// prints all newlines either as `\n` or as `\r\n`
|
||||
fn write_system_newlines<T>(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<u8>
|
||||
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)
|
||||
}
|
16
src/items.rs
16
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;
|
||||
}
|
||||
|
||||
|
35
src/lib.rs
35
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<usize> = 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),
|
||||
|
@ -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<F: Fn(&mut FmtVisitor, &str, &str, &str)>(&mut self,
|
||||
end: BytePos,
|
||||
process_last_snippet: F) {
|
||||
fn format_missing_inner<F: Fn(&mut FmtVisitor, &str, &str)>(&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<F: Fn(&mut FmtVisitor, &str, &str, &str)>(&mut self,
|
||||
snippet: &str,
|
||||
file_name: &str,
|
||||
last_snippet: bool,
|
||||
process_last_snippet: F) {
|
||||
fn write_snippet<F: Fn(&mut FmtVisitor, &str, &str)>(&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..]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
72
src/modules.rs
Normal file
72
src/modules.rs
Normal file
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<PathBuf, &'a ast::Mod> {
|
||||
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<PathBuf, &'a ast::Mod>) {
|
||||
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)
|
||||
}
|
||||
}
|
@ -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 => {
|
||||
|
@ -4,6 +4,7 @@ mod mod2b;
|
||||
|
||||
mod mymod1 {
|
||||
use mod2a::{Foo,Bar};
|
||||
mod mod3a;
|
||||
}
|
||||
|
||||
#[path="mod2c.rs"]
|
||||
|
2
tests/source/nestedmod/mymod1/mod3a.rs
Normal file
2
tests/source/nestedmod/mymod1/mod3a.rs
Normal file
@ -0,0 +1,2 @@
|
||||
// Another mod
|
||||
fn a( ) { }
|
@ -4,6 +4,7 @@ mod mod2b;
|
||||
|
||||
mod mymod1 {
|
||||
use mod2a::{Foo, Bar};
|
||||
mod mod3a;
|
||||
}
|
||||
|
||||
#[path="mod2c.rs"]
|
||||
|
3
tests/target/nestedmod/mymod1/mod3a.rs
Normal file
3
tests/target/nestedmod/mymod1/mod3a.rs
Normal file
@ -0,0 +1,3 @@
|
||||
// Another mod
|
||||
fn a() {
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user