Crates.io | rfmt |
lib.rs | rfmt |
version | 0.1.0 |
source | src |
created_at | 2016-04-25 16:25:32.206745 |
updated_at | 2016-04-25 16:25:32.206745 |
description | Another Rust source code formatter. |
homepage | https://github.com/zBaitu/rfmt |
repository | https://github.com/zBaitu/rfmt |
max_upload_size | |
id | 4858 |
size | 211,255 |
https://github.com/zBaitu/rfmt
rfmt is a Rust source code formatter. Yes, there is already an official tool rustfmt from Rust Nursery. So why write another one?
cargo install rfmt
git clone git@github.com:zBaitu/rfmt.git
cargo build --release
Usage: rfmt [options] [path]
If `path` is a dir, rfmt will do action for all files in this dir recursively.
If `path` is not specified, use the current dir by default.
If neither `options` nor `path` is specified, rfmt will format source code from stdin.
Options:
-a, --ast print the rust original syntax ast debug info
-c, --check check exceed lines and trailing white space lines
-d, --debug print the rfmt ir debug info
-o, --overwrite overwrite the source file
-v, --version show version
-h, --help show help
In fact, I only use rfmt for Vim now. I do not test for other editors. It is just to replace rustfmt
to rfmt
. For example, Vim:
let g:formatdef_rfmt = '"rfmt"'
let g:formatters_rust = ['rfmt']
Comparing to rustfmt, there are some main different features from rfmt:
crate
, use
, mod
, attributes
and sort them.doc
, comment
, string
. You can use the check function to show exceed lines and trailing white space lines.expr?
, default fn
.The following part will show such features in detail, with some existing issues from rustfmt.
What happen when you format the following source by rustfmt when you edit on editor.
// lib.rs
pub mod a;
pub mod b;
pub mod c;
pub mod d;
...
It will parse all sub modules, this is the default action of the Rust parser. But in fact most of such scenario I just want to format only this file that I editing now.
rfmt use a custom Rust parser, rSyntax, it is cloned from the libsyntax of Rust. The main difference between rSyntax and Rust libsyntax is that, rSyntax skip sub module parse. So rfmt can format quickly on editor scenario.
If you want to format all the source code in a project, just specify the project directory as rfmt command argument:
rfmt project_dir
For the issue: rustfmt reformats bit manipiulations.
fn main() {
let (a, b, c, d) = (0, 0, 0, 0);
let _ = u32::from_be(((a as u32) << 24) |
((b as u32) << 16) |
((c as u32) << 8) |
(d as u32) << 0);
}
fn main() {
let (a, b, c, d) = (0, 0, 0, 0);
let _ = u32::from_be(((a as u32) << 24) | ((b as u32) << 16) | ((c as u32) << 8) |
(d as u32) << 0);
}
Of cause you can use #[rustfmt_skip]
to avoid such code, but in my personal opinon, I really don't like to add other code just for the source formatting tool.
fn main() {
let (a, b, c, d) = (0, 0, 0, 0);
let _ = u32::from_be(((a as u32) << 24) |
((b as u32) << 16) |
((c as u32) << 8) |
(d as u32) << 0);
}
It looks OK, isn't it? Why rfmt can keep the user wrap? Because of the rfmt ir. The custom ir of Rust AST record location information of every element as far as possible. Look another example:
fn main() {
let ref_packet = [0xde, 0xf0, 0x12, 0x34, 0x45, 0x67,
0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc,
0x86, 0xdd];
}
fn main() {
let ref_packet = [0xde, 0xf0, 0x12, 0x34, 0x45, 0x67, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc,
0x86, 0xdd];
}
fn main() {
let ref_packet = [0xde, 0xf0, 0x12, 0x34, 0x45, 0x67,
0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc,
0x86, 0xdd];
}
fn main() {
f(123456789, "abcdefg", "hijklmn", 0987654321, "opqrst", "uvwxyz", 123456789, "abcdefg", "hijklmn", 0987654321, "opqrst", "uvwxyz");
}
fn main() {
f(123456789,
"abcdefg",
"hijklmn",
0987654321,
"opqrst",
"uvwxyz",
123456789,
"abcdefg",
"hijklmn",
0987654321,
"opqrst",
"uvwxyz");
}
fn main() {
f(123456789, "abcdefg", "hijklmn", 0987654321, "opqrst", "uvwxyz", 123456789, "abcdefg",
"hijklmn", 0987654321, "opqrst", "uvwxyz");
}
I prefer to put parameters on one line as much as possible. This is only for my personal preferences. But another case I really think it is bad looking.
fn main() {
fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff(123456789, "abcdefg", "hijklmn", 0987654321, "opqrst", "uvwxyz", 123456789, "abcdefg", "hijklmn", 0987654321, "opqrst", "uvwxyz");
}
fn main() {
fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff(123456789,
"abcdefg",
"hijklmn",
0987654321,
"opqrst",
"uvwxyz",
123456789,
"abcdefg",
"hijklmn",
0987654321,
"opqrst",
"uvwxyz");
}
fn main() {
fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff(123456789,
"abcdefg", "hijklmn", 0987654321, "opqrst", "uvwxyz", 123456789, "abcdefg",
"hijklmn", 0987654321, "opqrst", "uvwxyz");
}
If the left align position is beyond limit(It is 50 for now), rfmt prefer double indent align to function call align. rfmt make source code left lean, while rustfmt is right lean, I think. An exsiting issue: rustfmt should avoid rightwards drifting big blocks of code
fn main() {
let mut arms = variants.iter().enumerate().map(|(i, &(ident, v_span, ref summary))| {
let i_expr = cx.expr_usize(v_span, i);
let pat = cx.pat_lit(v_span, i_expr);
let path = cx.path(v_span, vec![substr.type_ident, ident]);
let thing = rand_thing(cx, v_span, path, summary, |cx, sp| rand_call(cx, sp));
cx.arm(v_span, vec!( pat ), thing)
}).collect::<Vec<ast::Arm> >();
}
fn main() {
let mut arms = variants.iter()
.enumerate()
.map(|(i, &(ident, v_span, ref summary))| {
let i_expr = cx.expr_usize(v_span, i);
let pat = cx.pat_lit(v_span, i_expr);
let path = cx.path(v_span, vec![substr.type_ident, ident]);
let thing = rand_thing(cx,
v_span,
path,
summary,
|cx, sp| rand_call(cx, sp));
cx.arm(v_span, vec![pat], thing)
})
.collect::<Vec<ast::Arm>>();
}
fn main() {
let mut arms = variants.iter().enumerate().map(|(i, &(ident, v_span, ref summary))| {
let i_expr = cx.expr_usize(v_span, i);
let pat = cx.pat_lit(v_span, i_expr);
let path = cx.path(v_span, vec![substr.type_ident, ident]);
let thing = rand_thing(cx, v_span, path, summary, |cx, sp| rand_call(cx, sp));
cx.arm(v_span, vec!(pat), thing)
}).collect::<Vec<ast::Arm>>();
}
The result from rfmt is not changed, because this source code fits rfmt's code style.
crate
, use
, mod
, attributes
and sort them#![feature(custom_derive)]
#![deny(warnings)]
#![feature(question_mark)]
#![feature(iter_arith)]
#![feature(rustc_private)]
extern crate rst;
extern crate getopts;
extern crate walkdir;
use std::env;
use getopts::Options;
#[macro_use]
mod ts;
mod ir;
mod ft;
mod tr;
mod rfmt;
#![deny(warnings)]
#![feature(custom_derive)]
#![feature(iter_arith)]
#![feature(question_mark)]
#![feature(rustc_private)]
extern crate getopts;
extern crate rst;
extern crate walkdir;
use getopts::Options;
use std::env;
#[macro_use]
mod ts;
mod ft;
mod ir;
mod rfmt;
mod tr;
rfmt only group items that appear continuously. If on item is special that it must keep its order, like the mod ts;
, make it separate from others.
doc
, comment
, string
There are many issues about doc, comment, string, raw string from rustfmt. I think such element can leave free for user to write anything, any format they want.
If you want to check is there some line break the code style limit, rfmt provide check function.
// aaaaa
// bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
fn main() {
let a = r#"aaaaaaaaaaaaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbb"#;
}
rfmt -c main.rs
a.rs
exceed_lines: {2}
trailing_ws_lines: {1, 4}
----------------------------------------
You can check or overwrite all files in a directory.
rfmt -c rust/src/libcore
rfmt -o rust/src/libstd
Maybe you are interested to see the Rust AST of a source code.
// AST
fn main() {}
rfmt -a a.rs
Crate {
module: Mod {
inner: Span { lo: BytePos(7), hi: BytePos(19), expn_id: ExpnId(4294967295) },
items: [
Item {
ident: main#0,
attrs: [],
id: 4294967295,
node: Fn(
FnDecl {
......
}
}
}
]
},
attrs: [],
config: [],
span: Span { lo: BytePos(7), hi: BytePos(18), expn_id: ExpnId(4294967295) },
exported_macros: []
}
----------------------------------------
0: Isolated [
"// AST"
]
----------------------------------------
expr?
, default fn
The rSyntax is cloned from Rust nightly(1.10.0-nightly), so it supports the latest language feature.
struct A;
impl A {
default fn f() -> bool { true }
}
fn f() -> Result<bool, String> { Ok() }
fn ff() -> Result<bool, String> {
f()?
}
fn main() {
ff();
}
struct A;
impl A {
default fn f() -> bool {
true
}
}
fn f() -> Result<bool, String> {
Ok()
}
fn ff() -> Result<bool, String> {
f()?
}
fn main() {
ff();
}
As rfmt is written as a personal tool(toy) for my daily develop, it lacks some common features now.
// aaaaa
// bbbbb
struct A { // ccccc-DISAPPEARED
// ddddd
a: bool, // eeeee
b: i32, // ffff
// ggggg
} // hhhhh
// iiiii
fn f(a: bool, /* jjjjj-DISAPPEARED */ b: i32, /* kkkkk-DISAPPEARED */) -> bool { // lllll-DISAPPEARED
// mmmmm
const b: bool = false; // nnnnn
let mut a = true; // ooooo
a = false; // ppppp
a!();// qqqqq
a // rrrrr
} // sssss
// ttttt
// uuuuu
// aaaaa
// bbbbb
struct A {
// ddddd
a: bool, // eeeee
b: i32, // ffff
} // hhhhh
// iiiii
fn f(a: bool, b: i32) -> bool {
// mmmmm
const b: bool = false; // nnnnn
let mut a = true; // ooooo
a = false; // ppppp
a!(); // qqqqq
a // rrrrr
} // sssss
// ttttt
// uuuuu