格式化工具

功能简介

CJFMT(Cangjie Formatter)仓颉格式化工具是仓颉语言提供的代码自动格式化工具。

使用说明

使用命令行操作 cjfmt [option] file [option] file

cjfmt -h 帮助信息,选项介绍

Usage:
     cjfmt -f fileName [-o fileName]
     cjfmt -d fileDir [-o fileDir]
Options:
   -h            Show usage
                     eg: cjfmt -h
   -f            Specifies the file in the required format. The value can be a relative path or an absolute path.
                     eg: cjfmt -f test.cj
   -d            Specifies the file directory in the required format. The value can be a relative path or an absolute path.
                     eg: cjfmt -d test/
   -o <value>    Output. If a single file is formatted, '-o' is followed by the file name. Relative and absolute paths are supported;
                 If a file in the file directory is formatted, a path must be added after -o. The path can be a relative path or an absolute path.
                     eg: cjfmt -f a.cj -o ./fmta.cj
                     eg: cjfmt -d ~/testsrc -o ./testout
   -l <region>   Only format lines in the specified region for the provided file. Only valid if a single file was specified.
                 Region has a format of [start:end] where 'start' and 'end' are integer numbers representing first and last lines to be formated in the specified file.
                 Line count starts with 1.
                     eg: cjfmt -f a.cj -o ./fmta.cj -l 1:25

文件格式化

cjfmt -f

  • 格式化并覆盖源文件,支持相对路径和绝对路径。
cjfmt -f ../../../test/uilang/Thread.cj
  • 选项-o 新建一个.cj文件导出格式化后的代码,源文件和输出文件支持相对路径和绝对路径。
cjfmt -f ../../../test/uilang/Thread.cj -o ../../../test/formated/Thread.cj

目录格式化

cjfmt -d

  • 选项-d 让开发者指定扫描仓颉源代码目录,对文件夹下的仓颉源码格式化,支持相对路径和绝对路径。
cjfmt -d test/              //源文件目录为相对目录。

cjfmt -d /home/xxx/test     //源文件目录为绝对目录。
  • 选项-o 为输出目录,可以是已存在的路径,若不存在则会创建相关的目录结构,支持相对路径和绝对路径;目录的最大长度 MAX_PATH 不同的系统之间存在差异,如 Windows 上这个值一般不能超过 260;在 Linux 上这个值一般建议不能超过 4096。
cjfmt -d test/ -o /home/xxx/testout

cjfmt -d /home/xxx/test -o ../testout/

cjfmt -d testsrc/ -o /home/../testout   // 源文件文件夹testsrc/不存在;报错:error: Source file path not exist!

片段格式化

cjfmt -l

  • 选项-l允许开发者指定应格式化文件的某一部分进行格式化,格式化程序将仅对提供的行范围内的源代码应用规则。
  • -l选项仅适用于格式化单个文件(选项-f)。如果指定了目录(选项-d),则-l选项无效。
cjfmt -f a.cj -o .cj -l 10:25 //仅格式化第10行至第25行

格式化规则

  • 一个源文件按顺序包含版权、package、import、顶层元素,且用空行分隔。

【正例】

// 第一部分,版权信息
/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2020-2020. All rights reserved.
 */

// 第二部分,package 声明
package com.huawei.myproduct.mymodule

// 第三部分,import 声明
import std.collection.HashMap   // 标准库

// 第四部分,public 元素定义
public class ListItem <: Component {
   // ...
}

// 第五部分,internal 元素定义
class Helper {
    // CODE
}

说明:

仓颉格式化工具不会强制用空行将版权信息部分与其他部分分隔,若开发者在版权信息下方留有一个或多个空行,则格式化工具会保留一个空行。

  • 采用一致的空格缩进,每次缩进 4 个空格。

【正例】

class ListItem  {
    var content: Array<Int64>   // 符合:相对类声明缩进 4 个空格
    init(
        content: Array<Int64>,  // 符合:函数参数相对函数声明缩进 4 个空格
        isShow!: Bool = true,
        id!: String = ""
    ) {
        this.content = content
    }
}
  • 使用统一的大括号换行风格,对于非空块状结构,大括号使用 K&R 风格。

【正例】

enum TimeUnit {             // 符合:跟随声明放行末,前置 1 空格
    Year | Month | Day | Hour
}                           // 符合:右大括号独占一行

class A {                   // 符合:跟随声明放行末,前置 1 空格
    var count = 1
}

func fn(a: Int64): Unit {       // 符合:跟随声明放行末,前置 1 空格
    if (a > 0) {            // 符合:跟随声明放行末,前置 1 空格
        // CODE
    } else {                  // 符合:右大括号和 else 在同一行
        // CODE
    }                         // 符合:右大括号独占一行
}

// lambda 函数
let add = { base: Int64, bonus: Int64 =>     // 符合: lambda 表达式中非空块遵循 K&R 风格
    print("符合 news")
    base + bonus
}
  • 使用空格突出关键字和重要信息。

【正例】

var isPresent: Bool = false  // 符合:变量声明冒号之后有一个空格
func method(isEmpty!: Bool): RetType { ... } // 符合:函数定义(命名参数 / 返回类型)中的冒号之后有一个空格

method(isEmpty: isPresent) // 符合: 命名参数传值中的冒号之后有一个空格

0..MAX_COUNT : -1 // 符合: range 操作符区间前后没有空格,步长冒号前后两侧有一个空格

var hundred = 0
do { // 符合:关键字 do 和后面的括号之间有一个空格
    hundred++
} while (hundred < 100) // 符合:关键字 while 和前面的括号之间有一个空格

func fn(paramName1: ArgType, paramName2: ArgType): ReturnType { // 符合:圆括号和内部相邻字符之间不出现空格
    ...
    for (i in 1..4) { // 符合:range 操作符左右两侧不留空格
        ...
    }
}

let listOne: Array<Int64> = [1, 2, 3, 4] // 符合:方括号和圆括号内部两侧不出现空格

let salary = base + bonus // 符合:二元操作符左右两侧留空格

x++ // 符合:一元操作符和操作数之间不留空格
  • 减少不必要的空行,保持代码紧凑。

【反例】

class MyApp <: App {
    let album = albumCreate()
    let page: Router
    // 空行
    // 空行
    // 空行
    init() {           // 不符合:类型定义内部使用连续空行
        this.page = Router("album", album)
    }

    override func onCreate(): Unit {

        println( "album Init." )  // 不符合:大括号内部首尾存在空行

    }
}
  • 减少不必要的分号,以代码简洁优先。

【格式化前】

package demo.analyzer.filter.impl; // 冗余的分号

internal import demo.analyzer.filter.StmtFilter; // 冗余的分号
internal import demo.analyzer.CJStatment; // 冗余的分号

func fn(a: Int64): Unit {
    println( "album Init." );
}

【格式化后】

package demo.analyzer.filter.impl // 删除冗余的分号

internal import demo.analyzer.filter.StmtFilter // 删除冗余的分号
internal import demo.analyzer.CJStatment // 删除冗余的分号

func fn(a: Int64): Unit {
    println( "album Init." ); // 表达式的分号予以保留,删除可能影响语义
}
  • 规定的优先级排列修饰符关键字。

以下是推荐的顶层元素的修饰符排列优先级:

public
open/abstract

以下是推荐的实例成员函数或实例成员属性的修饰符排序优先级:

public/protected/private
open
override

以下是推荐的静态成员函数的修饰符排序优先级:

public/protected/private
static
redef

以下是推荐的成员变量的修饰符排序优先级:

public/protected/private
static
  • 多行注释的格式化行为

* 开头的注释, * 会互相对齐, 不以 * 开头的注释,则会保持注释原样。

// 格式化前
/*
      * comment
      */

/*
        comment
        */

//格式化后
/*
 * comment
 */

 /*
        comment
  */

注意事项

  • 仓颉格式化工具暂不支持语法错误的代码的格式化。

  • 仓颉格式化工具暂不支持元编程的格式化。