rFmt ---- Rust source code formatter

Related tags

Miscellaneous rfmt
Overview

rFmt ---- Rust source code formatter

https://github.com/zBaitu/rfmt

Overview

rfmt is a Rust source code formatter. Yes, there is already an official tool rustfmt from Rust Nursery. So why write another one?

  • rustfmt is great for configurable, but there are still some style that i don't like in my personal taste.
  • Write a code formatter for Rust can make me learn Rust more deeply, for example, the AST of Rust.
  • For fun: )

Version

Support for Rust 1.36.0

Install, Build

  • Install
cargo install rfmt
  • Build
git clone [email protected]:zBaitu/rfmt.git
cargo build --release

Usage

rfmt 1.36.0
baitu 
   
    
Another Rust source code formatter.

USAGE:
    rfmt [FLAGS] [input]

FLAGS:
    -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
    -h, --help         Prints help information
    -o, --overwrite    Overwrite the source file
    -p, --print        Print the rfmt ir simple format
    -V, --version      Prints version information

ARGS:
    
        Input file or dir. If `input` is a dir, rfmt will do action for all files in this dir recursively. If
               neither `options` nor `input` is specified, rfmt will format source code from stdin.

   

Running rfmt from your editor(Copy from rustfmt)

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']

Features

Comparing to rustfmt, there are some main different features from rfmt:

  • Keep wrap from user input.
  • Different align strategy.
  • Group crate, use, mod, attributes and sort them.
  • DO NOT format doc, comment, string. You can use the check function to show exceed lines and trailing white space lines.
  • Provide check, directory recursively, ast dump, debug.
  • Nightly features.

The following part will show such features in detail, with some existing issues from rustfmt.

Keep wrap from user input

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);
}
  • rustfmt
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.

  • rfmt
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];
}
  • rustfmt
fn main() {
    let ref_packet = [
        0xde, 0xf0, 0x12, 0x34, 0x45, 0x67, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0x86, 0xdd,
    ];
}
  • rfmt
fn main() {
    let ref_packet = [0xde, 0xf0, 0x12, 0x34, 0x45, 0x67,
                      0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc,
                      0x86, 0xdd];
}

Different align strategy

I prefer to put parameters on one line as much as possible.

fn main() {
    f(123456789, "abcdefg", "hijklmn", 0987654321, "opqrst", "uvwxyz", 123456789, "abcdefg", "hijklmn", 0987654321, "opqrst", "uvwxyz");
}
  • rustfmt
fn main() {
    f(
        123456789, "abcdefg", "hijklmn", 0987654321, "opqrst", "uvwxyz", 123456789, "abcdefg",
        "hijklmn", 0987654321, "opqrst", "uvwxyz",
    );
}
  • rfmt
fn main() {
    f(123456789, "abcdefg", "hijklmn", 0987654321, "opqrst", "uvwxyz", 123456789, "abcdefg",
      "hijklmn", 0987654321, "opqrst", "uvwxyz");
}

If the left align position is beyond limit(It is 40 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::
   
    
      >();
}

    
   
  • rustfmt
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::
   
    
     >();
}

    
   
  • rfmt
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::
   
    
     >();
}

    
   

The result from rfmt is not changed, because this source code fits rfmt's code style.

Group 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;
  • 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.

DO NOT format 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.

Provide check, directory recursively, ast dump

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 g.rs

"g.rs"
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 {
    loc: Loc(7, 19, nl),
    attrs: [],
    module: Mod {
        loc: Loc(7, 19, nl),
        name: "main",
        items: [
            Item {
                loc: Loc(7, 19, nl),
                attrs: [],
                vis: "",
                item: Fn(
                    Fn {
                        header: FnHeader {
                            is_unsafe: false,
                            is_async: false,
                            is_const: false,
                            abi: "\"Rust\"",
                        },
                        name: "main",
                        generics: Generics {
                            lifetime_defs: [],
                            type_params: [],
                            wh: Where {
                                clauses: [],
                            },
                        },
                        sig: FnSig {
                            args: [],
                            ret: Return {
                                nl: false,
                                ret: None,
                            },
                        },
                        block: Block {
                            loc: Loc(17, 19),
                            is_unsafe: false,
                            stmts: [],
                        },
                    },
                ),
            },
        ],
    },
}

------------------------------------------------------------------------------------------------------------------------

{
    7: [
        "// AST",
    ],
}
{}

Drawbacks

As rfmt is written as a personal tool(toy) for my daily develop, it lacks some common features now.

  • No config
    rustfmt provide lots of config option, but rfmt provide none. Code style is something like food, everyone has his taste. Although rustfmt has much configs now, there are still new config require open in issues. If majority part of rfmt's style suit your taste, you can clone and make some small modification, such as LF, max width, indent.
  • Only support for some kinds of comment
    Comment can appear anywhere in source code, is it difficult to support all kinds of comment, as comment info does not exists on AST node. On the other hand, I don't think some tricky comment is really need. The following source with comment, which comment disappeared means that it is not supported by rfmt 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
  • rfmt
// aaaaa

// bbbbb
struct A {
    // ddddd
    a: bool, // eeeee
    b: i32, // ffff
    // ggggg
} // 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
You might also like...
Polydrive an experimental open source alternative to Google Drive

Polydrive is an experimental open source alternative to Google Drive. It allows users to synchronize their files on multiple devices.

A modern and open source twist to classic pastebin sites.
A modern and open source twist to classic pastebin sites.

Turbine A modern and open-source twist to classic pastebin sites. What is this? Turbine originally started out as a simple pastebin idea so I could ha

Open Source Application Stack & PaaS

mycelia Open Source Application Stack & PaaS Installation cargo xtask build NOTE: We opted for cargo-xtask because Cargo build.rs is not supported for

A stupid macro that compiles and executes Rust and spits the output directly into your Rust code

inline-rust This is a stupid macro inspired by inline-python that compiles and executes Rust and spits the output directly into your Rust code. There

Telegram bot help you to run Rust code in Telegram via Rust playground
Telegram bot help you to run Rust code in Telegram via Rust playground

RPG_BOT (Rust Playground Bot) Telegram bot help you to run Rust code in Telegram via Rust playground Bot interface The bot supports 3 straightforward

Rust ABI safe code generator

CGlue offers an easy way to ABI (application binary interface) safety. Just a few annotations and your trait is ready to go!

A boiler plate code to create dynamic link library in rust.

🔭 rust-dll-bp This is a boiler plate code that will be generated as a dll binary. I personally cache this here for me but if you're intend to create

Advent of Code 2015, done entirely in Rust both for the challenge and as a way to learn

Advent of Code 2015 In preparation for Advent of Code 2021, I wanted to go back and try some of the older challenges. I figured it made the most sense

Solutions of Advent of Code 2021 in Rust, and some other languages.

advent-of-rust Solutions of Advent of Code 2021 in Rust, and some other languages. Puzzles Puzzle Stars Languages Day 1: Sonar Sweep ⭐ ⭐ Rust Python D

Owner
baitu
baitu
Source code for the Telegram channel @pixiv_daily

PixivDaily (Rust) This repository contains the source code of the program running the Telegram channel @pixiv_daily. Usage First, you'll need to clone

K4YT3X 5 Jun 23, 2022
ttasm is an assembler for assembling source code to TTVM executable.

ttasm is an assembler for assembling source code to TTVM executable.

maDeveloper 1 Nov 8, 2021
Download Apple's open source code from opensource.apple.com

Apple Open Source Downloader This repository defines a Rust crate and CLI program to automate the downloading of Apple's open source code from https:/

Gregory Szorc 6 Aug 19, 2022
The source code of SSPŠ KB discord bot

Workshop bot This repository contains the source code of SSPŠ KB discord bot. Deploying This project is automatically built and deployed with docker,

SSPŠ KB 16 Mar 30, 2023
unFlow is a Design as Code implementation, a DSL for UX & backend modeling. DSL to Sketch file, Sketch to DSL, DSL to code.

unflow 是一个低代码、无代码设计语言。unFlow is a Design as Code implementation, a DSL for UX & backend modeling. DSL to Sketch file, Sketch to DSL, DSL to code.

Inherd OS Team (硬核开源小组) 70 Nov 27, 2022
A CLI tool to convet Hex color code or RGB to color code, RGB, HSL and color name(if exists)

iro -色- A CLI tool to convert the hex color code or RGB to color code, RGB, HSL, color name(if exists, according to jonathantneal/color-names). Usage

Kyohei Uto 3 Dec 9, 2022
MeiliSearch is a powerful, fast, open-source, easy to use and deploy search engine

MeiliSearch is a powerful, fast, open-source, easy to use and deploy search engine. Both searching and indexing are highly customizable. Features such as typo-tolerance, filters, and synonyms are provided out-of-the-box. For more information about features go to our documentation.

MeiliSearch 31.6k Dec 30, 2022
An open source virtual tabletop that is currently purely a concept.

An open source virtual tabletop that is currently purely a concept.

null 6 Oct 15, 2022
Blueboat is an open-source alternative to Cloudflare Workers. The monolithic engine for serverless web apps.

Blueboat Blueboat is an open-source alternative to Cloudflare Workers. Blueboat aims to be a developer-friendly, multi-tenant platform for serverless

Heyang Zhou 1.8k Jan 9, 2023
Turbo Resin: open-source firmware for resin printers

Turbo Resin: open-source firmware for resin printers Turbo Resin is an open-source firmware for chitu based controller board printers. It currently su

Nicolas Viennot 139 Dec 22, 2022