Use config file for constants

This commit is contained in:
Nick Cameron 2015-05-23 17:02:59 +12:00
parent aa6f7e8d3c
commit 1a09a6d00a
11 changed files with 115 additions and 56 deletions

1
Cargo.lock generated
View file

@ -3,6 +3,7 @@ name = "rustfmt"
version = "0.0.1"
dependencies = [
"diff 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.14 (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)",
]

View file

@ -13,6 +13,9 @@ build = "build.rs"
strings = "0.0.1"
git = "https://github.com/nrc/strings.rs.git"
[dependencies]
toml = "0.1.20"
rustc-serialize = "0.3.14"
[dev-dependencies]
diff = "0.1.0"
toml = "0.1.20"

View file

@ -17,11 +17,8 @@ fn main() {
let in_file = Path::new("src/default.toml");
let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
let profile = env::var("PROFILE").unwrap();
let mut out_file = PathBuf::new();
out_file.push(manifest_dir);
out_file.push("target");
out_file.push(profile);
out_file.push("default.toml");
std::fs::copy(in_file, out_file).unwrap();

View file

@ -15,10 +15,18 @@ extern crate rustfmt;
use rustfmt::{WriteMode, run};
use std::fs::File;
use std::io::Read;
fn main() {
let args: Vec<_> = std::env::args().collect();
//run(args, WriteMode::Display);
run(args, WriteMode::Overwrite);
let mut def_config_file = File::open("default.toml").unwrap();
let mut def_config = String::new();
def_config_file.read_to_string(&mut def_config).unwrap();
//run(args, WriteMode::Display, &def_config);
run(args, WriteMode::Overwrite, &def_config);
std::env::set_exit_status(0);
// TODO unit tests

View file

@ -20,7 +20,6 @@ use std::fmt;
use std::fs::File;
use std::io::{Write, stdout};
use WriteMode;
use NEWLINE_STYLE;
use NewlineStyle;
// This is basically a wrapper around a bunch of Ropes which makes it convenient
@ -157,7 +156,7 @@ impl<'a> ChangeSet<'a> {
-> Result<(), ::std::io::Error>
where T: Write,
{
match NEWLINE_STYLE {
match config!(newline_style) {
NewlineStyle::Unix => write!(writer, "{}", text),
NewlineStyle::Windows => {
for (c, _) in text.chars() {

View file

@ -1,2 +1,7 @@
max-width = 100
ideal-width = 80
max_width = 100
ideal_width = 80
leeway = 5
tab_spaces = 4
newline_style = "Unix"
fn_brace_style = "SameLineWhere"
fn_return_indent = "WithArgs"

View file

@ -15,7 +15,7 @@ use lists::{write_list, ListFormatting, SeparatorTactic, ListTactic};
use syntax::{ast, ptr};
use syntax::codemap::{Span, Pos};
use {MAX_WIDTH, MIN_STRING};
use MIN_STRING;
impl<'a> FmtVisitor<'a> {
// TODO NEEDS TESTS
@ -26,7 +26,7 @@ impl<'a> FmtVisitor<'a> {
// strings, or if the string is too long for the line.
let l_loc = self.codemap.lookup_char_pos(span.lo);
let r_loc = self.codemap.lookup_char_pos(span.hi);
if l_loc.line == r_loc.line && r_loc.col.to_usize() <= MAX_WIDTH {
if l_loc.line == r_loc.line && r_loc.col.to_usize() <= config!(max_width) {
return self.snippet(span);
}

View file

@ -8,8 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use {ReturnIndent, MAX_WIDTH, BraceStyle,
IDEAL_WIDTH, LEEWAY, FN_BRACE_STYLE, FN_RETURN_INDENT};
use {ReturnIndent, BraceStyle};
use utils::make_indent;
use lists::{write_list, ListFormatting, SeparatorTactic, ListTactic};
use visitor::FmtVisitor;
@ -149,8 +148,8 @@ impl<'a> FmtVisitor<'a> {
// If we've already gone multi-line, or the return type would push
// over the max width, then put the return type on a new line.
if result.contains("\n") ||
result.len() + indent + ret_str.len() > MAX_WIDTH {
let indent = match FN_RETURN_INDENT {
result.len() + indent + ret_str.len() > config!(max_width) {
let indent = match config!(fn_return_indent) {
ReturnIndent::WithWhereClause => indent + 4,
// TODO we might want to check that using the arg indent doesn't
// blow our budget, and if it does, then fallback to the where
@ -344,15 +343,15 @@ impl<'a> FmtVisitor<'a> {
if !newline_brace {
used_space += 2;
}
let one_line_budget = if used_space > MAX_WIDTH {
let one_line_budget = if used_space > config!(max_width) {
0
} else {
MAX_WIDTH - used_space
config!(max_width) - used_space
};
// 2 = `()`
let used_space = indent + result.len() + 2;
let max_space = IDEAL_WIDTH + LEEWAY;
let max_space = config!(ideal_width) + config!(leeway);
debug!("compute_budgets_for_args: used_space: {}, max_space: {}",
used_space, max_space);
if used_space < max_space {
@ -368,7 +367,7 @@ impl<'a> FmtVisitor<'a> {
result.push_str(&make_indent(indent + 4));
// 6 = new indent + `()`
let used_space = indent + 6;
let max_space = IDEAL_WIDTH + LEEWAY;
let max_space = config!(ideal_width) + config!(leeway);
if used_space > max_space {
// Whoops! bankrupt.
// TODO take evasive action, perhaps kill the indent or something.
@ -382,7 +381,7 @@ impl<'a> FmtVisitor<'a> {
}
fn newline_for_brace(&self, where_clause: &ast::WhereClause) -> bool {
match FN_BRACE_STYLE {
match config!(fn_brace_style) {
BraceStyle::AlwaysNextLine => true,
BraceStyle::SameLineWhere if where_clause.predicates.len() > 0 => true,
_ => false,
@ -399,7 +398,7 @@ impl<'a> FmtVisitor<'a> {
return result;
}
let budget = MAX_WIDTH - indent - 2;
let budget = config!(max_width) - indent - 2;
// TODO might need to insert a newline if the generics are really long
result.push('<');
@ -475,7 +474,7 @@ impl<'a> FmtVisitor<'a> {
.zip(comments.into_iter())
.collect();
let budget = IDEAL_WIDTH + LEEWAY - indent - 10;
let budget = config!(ideal_width) + config!(leeway) - indent - 10;
let fmt = ListFormatting {
tactic: ListTactic::Vertical,
separator: ",",

View file

@ -24,13 +24,17 @@ extern crate getopts;
extern crate rustc;
extern crate rustc_driver;
extern crate syntax;
extern crate rustc_serialize;
extern crate strings;
use rustc::session::Session;
use rustc::session::config::{self, Input};
use rustc::session::config as rustc_config;
use rustc::session::config::Input;
use rustc_driver::{driver, CompilerCalls, Compilation};
use rustc_serialize::{Decodable, Decoder};
use syntax::ast;
use syntax::codemap::CodeMap;
use syntax::diagnostics;
@ -42,6 +46,8 @@ use std::collections::HashMap;
use changes::ChangeSet;
use visitor::FmtVisitor;
#[macro_use]
mod config;
mod changes;
mod visitor;
mod functions;
@ -52,17 +58,12 @@ mod types;
mod expr;
mod imports;
const IDEAL_WIDTH: usize = 80;
const LEEWAY: usize = 5;
const MAX_WIDTH: usize = 100;
const MIN_STRING: usize = 10;
const TAB_SPACES: usize = 4;
const NEWLINE_STYLE: NewlineStyle = NewlineStyle::Unix;
const FN_BRACE_STYLE: BraceStyle = BraceStyle::SameLineWhere;
const FN_RETURN_INDENT: ReturnIndent = ReturnIndent::WithArgs;
// When we get scoped annotations, we should have rustfmt::skip.
const SKIP_ANNOTATION: &'static str = "rustfmt_skip";
static mut CONFIG: Option<config::Config> = None;
#[derive(Copy, Clone)]
pub enum WriteMode {
Overwrite,
@ -75,13 +76,24 @@ pub enum WriteMode {
}
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
enum NewlineStyle {
pub enum NewlineStyle {
Windows, // \r\n
Unix, // \n
}
impl Decodable for NewlineStyle {
fn decode<D: Decoder>(d: &mut D) -> Result<Self, D::Error> {
let s = try!(d.read_str());
match &*s {
"Windows" => Ok(NewlineStyle::Windows),
"Unix" => Ok(NewlineStyle::Unix),
_ => Err(d.error("Bad variant")),
}
}
}
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
enum BraceStyle {
pub enum BraceStyle {
AlwaysNextLine,
PreferSameLine,
// Prefer same line except where there is a where clause, in which case force
@ -89,15 +101,39 @@ enum BraceStyle {
SameLineWhere,
}
impl Decodable for BraceStyle {
fn decode<D: Decoder>(d: &mut D) -> Result<Self, D::Error> {
let s = try!(d.read_str());
match &*s {
"AlwaysNextLine" => Ok(BraceStyle::AlwaysNextLine),
"PreferSameLine" => Ok(BraceStyle::PreferSameLine),
"SameLineWhere" => Ok(BraceStyle::SameLineWhere),
_ => Err(d.error("Bad variant")),
}
}
}
// How to indent a function's return type.
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
enum ReturnIndent {
pub enum ReturnIndent {
// Aligned with the arguments
WithArgs,
// Aligned with the where clause
WithWhereClause,
}
// TODO could use a macro for all these Decodable impls.
impl Decodable for ReturnIndent {
fn decode<D: Decoder>(d: &mut D) -> Result<Self, D::Error> {
let s = try!(d.read_str());
match &*s {
"WithArgs" => Ok(ReturnIndent::WithArgs),
"WithWhereClause" => Ok(ReturnIndent::WithWhereClause),
_ => Err(d.error("Bad variant")),
}
}
}
// Formatting which depends on the AST.
fn fmt_ast<'a>(krate: &ast::Crate, codemap: &'a CodeMap) -> ChangeSet<'a> {
let mut visitor = FmtVisitor::from_codemap(codemap);
@ -133,10 +169,10 @@ fn fmt_lines(changes: &mut ChangeSet) {
line_len -= b - lw;
}
// Check for any line width errors we couldn't correct.
if line_len > MAX_WIDTH {
if line_len > config!(max_width) {
// TODO store the error rather than reporting immediately.
println!("Rustfmt couldn't fix (sorry). {}:{}: line longer than {} characters",
f, cur_line, MAX_WIDTH);
f, cur_line, config!(max_width));
}
line_len = 0;
cur_line += 1;
@ -200,7 +236,7 @@ impl<'a> CompilerCalls<'a> for RustFmtCalls {
fn no_input(&mut self,
_: &getopts::Matches,
_: &config::Options,
_: &rustc_config::Options,
_: &Option<PathBuf>,
_: &Option<PathBuf>,
_: &diagnostics::registry::Registry)
@ -248,7 +284,14 @@ impl<'a> CompilerCalls<'a> for RustFmtCalls {
}
}
pub fn run(args: Vec<String>, write_mode: WriteMode) {
// args are the arguments passed on the command line, generally passed through
// to the compiler.
// write_mode determines what happens to the result of running rustfmt, see
// WriteMode.
// default_config is a string of toml data to be used to configure rustfmt.
pub fn run(args: Vec<String>, write_mode: WriteMode, default_config: &str) {
config::set_config(default_config);
let mut call_ctxt = RustFmtCalls { input_path: None, write_mode: write_mode };
rustc_driver::run_compiler(&args, &mut call_ctxt);
}

View file

@ -14,7 +14,7 @@ use syntax::visit;
use utils;
use {IDEAL_WIDTH, MAX_WIDTH, TAB_SPACES, SKIP_ANNOTATION};
use SKIP_ANNOTATION;
use changes::ChangeSet;
pub struct FmtVisitor<'a> {
@ -32,7 +32,7 @@ impl<'a, 'v> visit::Visitor<'v> for FmtVisitor<'a> {
self.codemap.lookup_char_pos(ex.span.hi));
self.format_missing(ex.span.lo);
let offset = self.changes.cur_offset_span(ex.span);
let new_str = self.rewrite_expr(ex, MAX_WIDTH - offset, offset);
let new_str = self.rewrite_expr(ex, config!(max_width) - offset, offset);
self.changes.push_str_span(ex.span, &new_str);
self.last_pos = ex.span.hi;
}
@ -65,7 +65,7 @@ impl<'a, 'v> visit::Visitor<'v> for FmtVisitor<'a> {
self.changes.push_str_span(b.span, "{");
self.last_pos = self.last_pos + BytePos(1);
self.block_indent += TAB_SPACES;
self.block_indent += config!(tab_spaces);
for stmt in &b.stmts {
self.visit_stmt(&stmt)
@ -78,7 +78,7 @@ impl<'a, 'v> visit::Visitor<'v> for FmtVisitor<'a> {
None => {}
}
self.block_indent -= TAB_SPACES;
self.block_indent -= 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, "}");
@ -149,8 +149,8 @@ impl<'a, 'v> visit::Visitor<'v> for FmtVisitor<'a> {
match vp.node {
ast::ViewPath_::ViewPathList(ref path, ref path_list) => {
let block_indent = self.block_indent;
let one_line_budget = MAX_WIDTH - block_indent;
let multi_line_budget = IDEAL_WIDTH - block_indent;
let one_line_budget = config!(max_width) - block_indent;
let multi_line_budget = config!(ideal_width) - block_indent;
let new_str = self.rewrite_use_list(block_indent,
one_line_budget,
multi_line_budget,
@ -170,9 +170,9 @@ impl<'a, 'v> visit::Visitor<'v> for FmtVisitor<'a> {
ast::Item_::ItemImpl(..) |
ast::Item_::ItemMod(_) |
ast::Item_::ItemTrait(..) => {
self.block_indent += TAB_SPACES;
self.block_indent += config!(tab_spaces);
visit::walk_item(self, item);
self.block_indent -= TAB_SPACES;
self.block_indent -= config!(tab_spaces);
}
ast::Item_::ItemExternCrate(_) => {
self.format_missing_with_indent(item.span.lo);

View file

@ -58,7 +58,6 @@ fn idempotent_tests() {
// Compare output to input.
fn print_mismatches(result: HashMap<String, String>) {
for (file_name, fmt_text) in result {
println!("Mismatch in {}.", file_name);
println!("{}", fmt_text);
}
}
@ -68,14 +67,16 @@ static HANDLE_RESULT: &'static Fn(HashMap<String, String>) = &handle_result;
pub fn idempotent_check(filename: String) -> Result<(), HashMap<String, String>> {
let args = vec!["rustfmt".to_owned(), filename];
let mut def_config_file = fs::File::open("default.toml").unwrap();
let mut def_config = String::new();
def_config_file.read_to_string(&mut def_config).unwrap();
// this thread is not used for concurrency, but rather to workaround the issue that the passed
// function handle needs to have static lifetime. Instead of using a global RefCell, we use
// panic to return a result in case of failure. This has the advantage of smoothing the road to
// multithreaded rustfmt
thread::catch_panic(move || {
run(args, WriteMode::Return(HANDLE_RESULT));
run(args, WriteMode::Return(HANDLE_RESULT), &def_config);
}).map_err(|any|
// i know it is a hashmap
*any.downcast().unwrap()
)
}
@ -90,8 +91,8 @@ fn handle_result(result: HashMap<String, String>) {
// TODO: speedup by running through bytes iterator
f.read_to_string(&mut text).unwrap();
if fmt_text != text {
show_diff(&file_name, &fmt_text, &text);
failures.insert(file_name, fmt_text);
let diff_str = make_diff(&file_name, &fmt_text, &text);
failures.insert(file_name, diff_str);
}
}
if !failures.is_empty() {
@ -100,24 +101,25 @@ fn handle_result(result: HashMap<String, String>) {
}
fn show_diff(file_name: &str, expected: &str, actual: &str) {
fn make_diff(file_name: &str, expected: &str, actual: &str) -> String {
let mut line_number = 1;
let mut prev_both = true;
let mut text = String::new();
for result in diff::lines(expected, actual) {
match result {
diff::Result::Left(str) => {
if prev_both {
println!("Mismatch @ {}:{}", file_name, line_number);
text.push_str(&format!("Mismatch @ {}:{}\n", file_name, line_number));
}
println!("-{}", str);
text.push_str(&format!("-{}\n", str));
prev_both = false;
}
diff::Result::Right(str) => {
if prev_both {
println!("Mismatch @ {}:{}", file_name, line_number);
text.push_str(&format!("Mismatch @ {}:{}\n", file_name, line_number));
}
println!("+{}", str);
text.push_str(&format!("+{}\n", str));
prev_both = false;
line_number += 1;
}
@ -127,4 +129,6 @@ fn show_diff(file_name: &str, expected: &str, actual: &str) {
}
}
}
text
}