Use config file for constants
This commit is contained in:
parent
aa6f7e8d3c
commit
1a09a6d00a
11 changed files with 115 additions and 56 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -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)",
|
||||
]
|
||||
|
|
|
@ -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"
|
||||
|
|
3
build.rs
3
build.rs
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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: ",",
|
||||
|
|
73
src/lib.rs
73
src/lib.rs
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue