diff --git a/kclvm/Cargo.lock b/kclvm/Cargo.lock index f8ab9f114..b6432f200 100644 --- a/kclvm/Cargo.lock +++ b/kclvm/Cargo.lock @@ -37,6 +37,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "anyhow" +version = "1.0.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc" + [[package]] name = "arrayvec" version = "0.7.2" @@ -633,11 +639,14 @@ dependencies = [ name = "kclvm-tools" version = "0.1.0" dependencies = [ + "anyhow", "fancy-regex", "indexmap", "kclvm-ast", + "kclvm-config", "kclvm-error", "kclvm-parser", + "walkdir", ] [[package]] diff --git a/kclvm/tools/Cargo.lock b/kclvm/tools/Cargo.lock index 6951c33ae..88964b9ac 100644 --- a/kclvm/tools/Cargo.lock +++ b/kclvm/tools/Cargo.lock @@ -37,6 +37,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "anyhow" +version = "1.0.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc" + [[package]] name = "arrayvec" version = "0.7.2" @@ -542,12 +548,15 @@ dependencies = [ name = "kclvm-tools" version = "0.1.0" dependencies = [ + "anyhow", "fancy-regex", "indexmap", "kclvm-ast", + "kclvm-config", "kclvm-error", "kclvm-parser", "pretty_assertions", + "walkdir", ] [[package]] @@ -1075,6 +1084,15 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "scoped-tls" version = "1.0.0" @@ -1439,6 +1457,17 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "walkdir" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + [[package]] name = "wasi" version = "0.10.0+wasi-snapshot-preview1" diff --git a/kclvm/tools/Cargo.toml b/kclvm/tools/Cargo.toml index c1b9d3a0b..7f5877ecf 100644 --- a/kclvm/tools/Cargo.toml +++ b/kclvm/tools/Cargo.toml @@ -8,10 +8,13 @@ edition = "2021" [dependencies] indexmap = "1.0" fancy-regex = "0.7.1" +walkdir = "2" +anyhow = "1.0" kclvm-ast = {path = "../ast", version = "0.1.0"} kclvm-error = {path = "../error", version = "0.1.0"} kclvm-parser = {path = "../parser", version = "0.1.0"} +kclvm-config = {path = "../config", version = "0.1.0"} [dev-dependencies] pretty_assertions = "1.2.1" diff --git a/kclvm/tools/src/format/mod.rs b/kclvm/tools/src/format/mod.rs new file mode 100644 index 000000000..547181f73 --- /dev/null +++ b/kclvm/tools/src/format/mod.rs @@ -0,0 +1,90 @@ +//! [kclvm_tools::format] module mainly contains some functions of language formatting, +//! the main API function is `format`, which accepts a path to be formatted and +//! formatted options. +//! +//! The basic principle is to call the [kclvm_parser::parse_file] function to parse the +//! AST Module, and then use the AST printer [kclvm_tools::printer::print_ast_module] +//! to print it as source code string. +use anyhow::{anyhow, Result}; +use std::path::Path; + +use crate::{printer::print_ast_module, util::get_kcl_files}; +use kclvm_parser::parse_file; + +#[cfg(test)] +mod tests; + +/// FormatOptions contains two options: +/// - is_stdout: whether to output the formatted result to stdout. +/// - recursively: whether to recursively traverse a folder and format all KCL files in it. +#[derive(Debug, Default)] +pub struct FormatOptions { + pub is_stdout: bool, + pub recursively: bool, +} + +/// Formats kcl file or directory path contains kcl files and +/// returns the changed file paths. +/// +/// # Examples +/// +/// ```no_run +/// use kclvm_tools::format::{format, FormatOptions}; +/// +/// // Format a single file. +/// format("path_to_a_single_file.k", &FormatOptions::default()).unwrap(); +/// // Format a folder contains kcl files +/// format("path_to_a_folder", &FormatOptions::default()).unwrap(); +/// ``` +pub fn format>(path: P, opts: &FormatOptions) -> Result> { + let mut changed_paths: Vec = vec![]; + let path_ref = path.as_ref(); + if path_ref.is_dir() { + for file in &get_kcl_files(path, opts.recursively)? { + if format_file(file, opts)? { + changed_paths.push(file.clone()) + } + } + } else if path_ref.is_file() { + let file = path_ref.to_str().unwrap().to_string(); + if format_file(&file, opts)? { + changed_paths.push(file) + } + } + if !opts.is_stdout { + let n = changed_paths.len(); + println!( + "KCL format done and {} {} formatted:", + n, + if n <= 1 { "file was" } else { "files were" } + ); + for p in &changed_paths { + println!("{}", p); + } + } + Ok(changed_paths) +} + +/// Format a file and returns whether the file has been formatted and modified. +fn format_file(file: &str, opts: &FormatOptions) -> Result { + let src = std::fs::read_to_string(file)?; + let (source, is_formatted) = format_source(&src)?; + if opts.is_stdout { + println!("{}", source); + } else { + std::fs::write(file, &source)? + } + Ok(is_formatted) +} + +/// Format a code source and return the formatted source and +/// whether the source is changed. +fn format_source(src: &str) -> Result<(String, bool)> { + let module = match parse_file("", Some(src.to_string())) { + Ok(module) => module, + Err(err) => return Err(anyhow!("{}", err)), + }; + let formatted_src = print_ast_module(&module); + let is_formatted = src != formatted_src; + Ok((formatted_src, is_formatted)) +} diff --git a/kclvm/tools/src/format/test_data/format_data/assert.golden b/kclvm/tools/src/format/test_data/format_data/assert.golden new file mode 100644 index 000000000..f2597860e --- /dev/null +++ b/kclvm/tools/src/format/test_data/format_data/assert.golden @@ -0,0 +1,4 @@ +assert True if True, "message" +# Comment +assert False if data, "message" +assert 1 diff --git a/kclvm/tools/src/format/test_data/format_data/assert.input b/kclvm/tools/src/format/test_data/format_data/assert.input new file mode 100644 index 000000000..f0a13a045 --- /dev/null +++ b/kclvm/tools/src/format/test_data/format_data/assert.input @@ -0,0 +1,3 @@ +assert True if True, "message" +assert False if data , "message" # Comment +assert 1 diff --git a/kclvm/tools/src/format/test_data/format_data/blankline.golden b/kclvm/tools/src/format/test_data/format_data/blankline.golden new file mode 100644 index 000000000..777bbd6b7 --- /dev/null +++ b/kclvm/tools/src/format/test_data/format_data/blankline.golden @@ -0,0 +1,4 @@ +a = 1 +b = 2 +c = 3 +d = 4 diff --git a/kclvm/tools/src/format/test_data/format_data/blankline.input b/kclvm/tools/src/format/test_data/format_data/blankline.input new file mode 100644 index 000000000..b80849aac --- /dev/null +++ b/kclvm/tools/src/format/test_data/format_data/blankline.input @@ -0,0 +1,12 @@ + +a=1 + + +b= 2 + + +c =3 + + + +d = 4 \ No newline at end of file diff --git a/kclvm/tools/src/format/test_data/format_data/breakline.golden b/kclvm/tools/src/format/test_data/format_data/breakline.golden new file mode 100644 index 000000000..eb95f0d96 --- /dev/null +++ b/kclvm/tools/src/format/test_data/format_data/breakline.golden @@ -0,0 +1,9 @@ +import math +schema Base: + name: str + +schema Person(Base): + age: int + +person = Person {} + diff --git a/kclvm/tools/src/format/test_data/format_data/breakline.input b/kclvm/tools/src/format/test_data/format_data/breakline.input new file mode 100644 index 000000000..a8b397101 --- /dev/null +++ b/kclvm/tools/src/format/test_data/format_data/breakline.input @@ -0,0 +1,6 @@ +import math +schema Base: + name: str +schema Person(Base): + age: int +person = Person{} \ No newline at end of file diff --git a/kclvm/tools/src/format/test_data/format_data/check.golden b/kclvm/tools/src/format/test_data/format_data/check.golden new file mode 100644 index 000000000..64dd69bc4 --- /dev/null +++ b/kclvm/tools/src/format/test_data/format_data/check.golden @@ -0,0 +1,13 @@ +schema Person: + firstName: str = "John" + lastName: str + times: int + + check: + len(lastName) > 0 if times > 5 + +JohnDoe = Person { + "lastName": "Doe" + "times": 10 +} + diff --git a/kclvm/tools/src/format/test_data/format_data/check.input b/kclvm/tools/src/format/test_data/format_data/check.input new file mode 100644 index 000000000..861b042ac --- /dev/null +++ b/kclvm/tools/src/format/test_data/format_data/check.input @@ -0,0 +1,12 @@ + +schema Person: + firstName: str = "John" + lastName: str + times: int + check: + len(lastName) > 0 if times > 5 + +JohnDoe = Person { + "lastName": "Doe" + "times":10 +} \ No newline at end of file diff --git a/kclvm/tools/src/format/test_data/format_data/codelayout.golden b/kclvm/tools/src/format/test_data/format_data/codelayout.golden new file mode 100644 index 000000000..d217f4b5e --- /dev/null +++ b/kclvm/tools/src/format/test_data/format_data/codelayout.golden @@ -0,0 +1,80 @@ +import math as alias_math +schema Person(Base): + # inline comment + name: str + age: int + + check: + age > 0 if age, "age must > 0" + +person = Person { + name: "Alice" + age: 18 +} + +if True: + a = 1 +elif True: + b = 2 +else: + c = 3 +d = 1 + 2 +e = (1 + 2) +f = [1, 2, 3] +g = {"key": "value"} +# block comment +print(1) +dct = {"key": "value"} +lst = [1, 2, 3] +h = dct['key'] +i = lst[1] +x = 1 +y = 2 +long_variable = 3 +i = i + 1 +submitted += 1 +x = x * 2 - 1 +hypot2 = x * x + y * y +_c = (a + b) * (a - b) +_b = 2 +_c = 3 +_d = 4 +_value = (1 + 2 * 3) +_value = (1 + 2 * 3) +_value = 1 + -2 * ~3 +_list = [1, 2, 3] +_list = [*_list, [4, 5, 6]] +_list = [*_list, [4, 5, 6]] +_dict = {**{"k": "v"}, **{"k": "v"}} +a = [1, 2, 3] +b = [ + 1 + 2 + 3 + 4 + 5 + 6 +] +_dict = { + "k1": "v1" + "k2": "v2" + "k3": "v3" + "k4": "v4" + "k5": "v5" +} +foo = 1 +if foo is not None: + _a = 1 + _dict |= {} + hello = "world{}".format(1)[2:4:].lower() + range_int = [i for i in range(10) if i > 1] + +op = 1 + 2 - -3 + (3 - 1) // 3 +op += 1 +op -= 12 + 23 +print(" ", end='') +log = math.log(12) +aa = 1 +assert aa == 1 if aa, "message" +aaaa = (1 + 2 / 2) if _a == 2 + +134.3 else ("a" * 3) +bbbb = "{}".format(a) diff --git a/kclvm/tools/src/format/test_data/format_data/codelayout.input b/kclvm/tools/src/format/test_data/format_data/codelayout.input new file mode 100644 index 000000000..2d2e2a0c9 --- /dev/null +++ b/kclvm/tools/src/format/test_data/format_data/codelayout.input @@ -0,0 +1,76 @@ + + + +import math as alias_math +schema Person ( Base): + name:str# inline comment + age:int + check : + age >0 if age, "age must > 0" +person = Person{ + name:"Alice" + age:18 +} +if True: + a = 1 +elif True: + b = 2 +else: + c = 3 +d = 1 + 2 +e = ( 1 + 2 ) +f=[ 1, 2, 3 ] +g = { "key" : "value" } +# block comment +print (1) +dct={"key": "value"} +lst=[1,2,3] +h = dct [ 'key' ] +i = lst [ 1 ] +x = 1 +y = 2 +long_variable = 3 +i = i+1 +submitted+=1 +x = x*2 - 1 +hypot2 = x*x + y*y +_c = (a+b) * (a-b) +_b=2 +_c= 3 +_d =4 + +_value = (1 + 2 * 3) +_value = (1+2*3) +_value =1+ - 2 * ~ 3 +_list = [1, 2, 3] +_list = [*_list, [4, 5 ,6]] +_list = [* _list, [4, 5 ,6]] + +_dict = {** {"k": "v"}, ** {"k": "v"}} +a = [1,2,3] +b = [ + 1,2,3, + 4,5,6, +] +_dict={ + "k1":"v1" + "k2" :"v2" + "k3": "v3" + "k4" : "v4" + "k5" : "v5" +} +foo=1 +if foo is not None: + _a = 1 + _dict|={} + hello = "world{}" . format( 1 )[2 : 4] . lower( ) + range_int = [ i for i in range( 10 ) if i > 1 ] +op = 1+2 - - 3 + (3 - 1) // 3 +op += 1 +op -= 12 + 23 +print( " " , end= '') +log = math. log(12) +aa = 1 +assert aa == 1 if aa,"message" +aaaa = (1 + 2 / 2) if _a == 2 + + 134.3 else ("a"*3) +bbbb = "{}". format(a) \ No newline at end of file diff --git a/kclvm/tools/src/format/test_data/format_data/collection_if.golden b/kclvm/tools/src/format/test_data/format_data/collection_if.golden new file mode 100644 index 000000000..25fb0aa88 --- /dev/null +++ b/kclvm/tools/src/format/test_data/format_data/collection_if.golden @@ -0,0 +1,32 @@ +dataDict0 = { + if True: + age = 101 + elif True: + age = 123 + else: + age = 111 +} +dataDict1 = { + if True: + age: 101 + elif True: + age: 123 + else: + age: 111 +} +dataList0 = [ + if True: + 1 + elif False: + 2 + else: + 3 +] +dataList1 = [ + if True: + *[1] + elif False: + 2 + else: + 3 +] diff --git a/kclvm/tools/src/format/test_data/format_data/collection_if.input b/kclvm/tools/src/format/test_data/format_data/collection_if.input new file mode 100644 index 000000000..a1e25c13f --- /dev/null +++ b/kclvm/tools/src/format/test_data/format_data/collection_if.input @@ -0,0 +1,32 @@ +dataDict0 = { + if True: + age = 101 + elif True: + age = 123 + else: + age = 111 +} +dataDict1 = { + if True: + age : 101 + elif True: + age : 123 + else: + age : 111 +} +dataList0 = [ + if True: + 1 + elif False: + 2 + else : + 3 +] +dataList1 = [ + if True: + * [1] + elif False: + 2 + else : + 3 +] diff --git a/kclvm/tools/src/format/test_data/format_data/comment.golden b/kclvm/tools/src/format/test_data/format_data/comment.golden new file mode 100644 index 000000000..2c1dce9df --- /dev/null +++ b/kclvm/tools/src/format/test_data/format_data/comment.golden @@ -0,0 +1,12 @@ +# Block comment +# Inline comment +a = 1 +schema Person: + """ + Schema doc string + """ + # Inline comment in schema + name: str = "Alice" + # Block comment in schema + age: int = 18 + diff --git a/kclvm/tools/src/format/test_data/format_data/comment.input b/kclvm/tools/src/format/test_data/format_data/comment.input new file mode 100644 index 000000000..2134faeb9 --- /dev/null +++ b/kclvm/tools/src/format/test_data/format_data/comment.input @@ -0,0 +1,9 @@ +# Block comment +a = 1# Inline comment +schema Person: + """ + Schema doc string + """ + name:str="Alice"# Inline comment in schema + # Block comment in schema + age:int=18 \ No newline at end of file diff --git a/kclvm/tools/src/format/test_data/format_data/comp_for.golden b/kclvm/tools/src/format/test_data/format_data/comp_for.golden new file mode 100644 index 000000000..f304a1ffc --- /dev/null +++ b/kclvm/tools/src/format/test_data/format_data/comp_for.golden @@ -0,0 +1,2 @@ +data0 = [i + 1 for i in range(10) if i > 1] +data1 = [i + 1 for i in range(10) if i > 1] diff --git a/kclvm/tools/src/format/test_data/format_data/comp_for.input b/kclvm/tools/src/format/test_data/format_data/comp_for.input new file mode 100644 index 000000000..a4bb9ab42 --- /dev/null +++ b/kclvm/tools/src/format/test_data/format_data/comp_for.input @@ -0,0 +1,7 @@ +data0 = [ + i + 1 for i in range(10) if i > 1 +] +data1 = [ + i + 1 for i in range(10) + if i > 1 +] diff --git a/kclvm/tools/src/format/test_data/format_data/empty.golden b/kclvm/tools/src/format/test_data/format_data/empty.golden new file mode 100644 index 000000000..e69de29bb diff --git a/kclvm/tools/src/format/test_data/format_data/empty.input b/kclvm/tools/src/format/test_data/format_data/empty.input new file mode 100644 index 000000000..e69de29bb diff --git a/kclvm/tools/src/format/test_data/format_data/import.golden b/kclvm/tools/src/format/test_data/format_data/import.golden new file mode 100644 index 000000000..3b4302338 --- /dev/null +++ b/kclvm/tools/src/format/test_data/format_data/import.golden @@ -0,0 +1,6 @@ +import a +import b +import c +import d +import e as e +a = 1 diff --git a/kclvm/tools/src/format/test_data/format_data/import.input b/kclvm/tools/src/format/test_data/format_data/import.input new file mode 100644 index 000000000..977a13711 --- /dev/null +++ b/kclvm/tools/src/format/test_data/format_data/import.input @@ -0,0 +1,10 @@ +import a + +import b + +import c +import d + +import e as e + +a = 1 diff --git a/kclvm/tools/src/format/test_data/format_data/indent.golden b/kclvm/tools/src/format/test_data/format_data/indent.golden new file mode 100644 index 000000000..738448654 --- /dev/null +++ b/kclvm/tools/src/format/test_data/format_data/indent.golden @@ -0,0 +1,14 @@ +if True: + a = 1 +elif True: + b = 2 + if True: + c = 3 + else: + d = 4 +else: + e = 8 +schema Person: + name: str + age: int + diff --git a/kclvm/tools/src/format/test_data/format_data/indent.input b/kclvm/tools/src/format/test_data/format_data/indent.input new file mode 100644 index 000000000..40d99bce0 --- /dev/null +++ b/kclvm/tools/src/format/test_data/format_data/indent.input @@ -0,0 +1,14 @@ +if True: + a = 1 +elif True: + b = 2 + if True: + c = 3 + else: + d = 4 +else: + e = 8 + +schema Person: + name: str + age: int \ No newline at end of file diff --git a/kclvm/tools/src/format/test_data/format_data/inline_comment.golden b/kclvm/tools/src/format/test_data/format_data/inline_comment.golden new file mode 100644 index 000000000..f9c221663 --- /dev/null +++ b/kclvm/tools/src/format/test_data/format_data/inline_comment.golden @@ -0,0 +1,17 @@ +# Inline comment 1 +# Inline comment 2 +# Inline comment 3 +a = 1 +# Inline comment 4 +# Inline comment 5 +# +# +# Inline comment 6 +# Inline comment 7 +# +# Inline comment 8 +b = 2 +# Same inline comment +# Same inline comment +# Same inline comment +c = b + 1 diff --git a/kclvm/tools/src/format/test_data/format_data/inline_comment.input b/kclvm/tools/src/format/test_data/format_data/inline_comment.input new file mode 100644 index 000000000..04aa1815b --- /dev/null +++ b/kclvm/tools/src/format/test_data/format_data/inline_comment.input @@ -0,0 +1,17 @@ +# Inline comment 1 +# Inline comment 2 +# Inline comment 3 +a = 1 +# Inline comment 4 +# Inline comment 5 +# +# +# Inline comment 6 +# Inline comment 7 +# +# Inline comment 8 +b=2 +# Same inline comment +# Same inline comment +# Same inline comment +c=b+1 diff --git a/kclvm/tools/src/format/test_data/format_data/lambda.golden b/kclvm/tools/src/format/test_data/format_data/lambda.golden new file mode 100644 index 000000000..b30ef70a1 --- /dev/null +++ b/kclvm/tools/src/format/test_data/format_data/lambda.golden @@ -0,0 +1,8 @@ +f0 = lambda { + 1 + 1 + +} +f1 = lambda x: int, y: int -> int { + x + y + +} diff --git a/kclvm/tools/src/format/test_data/format_data/lambda.input b/kclvm/tools/src/format/test_data/format_data/lambda.input new file mode 100644 index 000000000..55039b367 --- /dev/null +++ b/kclvm/tools/src/format/test_data/format_data/lambda.input @@ -0,0 +1,6 @@ +f0 = lambda { + 1 + 1 +} +f1 = lambda x : int , y : int ->int{ + x + y +} diff --git a/kclvm/tools/src/format/test_data/format_data/quant.golden b/kclvm/tools/src/format/test_data/format_data/quant.golden new file mode 100644 index 000000000..4d35a3ba6 --- /dev/null +++ b/kclvm/tools/src/format/test_data/format_data/quant.golden @@ -0,0 +1,12 @@ +a = all x in [-1, 0, 1, 2, 3] { + x >= 1 if x > 0 +} +b = any x in {k1 = "v1", k2 = "v2"} { + x in ["k1", "v2"] +} +c = map x in {k1 = "v1", k2 = "v2"} { + x +} +d = filter x in [1, 2, 3] { + x > 1 +} diff --git a/kclvm/tools/src/format/test_data/format_data/quant.input b/kclvm/tools/src/format/test_data/format_data/quant.input new file mode 100644 index 000000000..a6a65a3c2 --- /dev/null +++ b/kclvm/tools/src/format/test_data/format_data/quant.input @@ -0,0 +1,8 @@ +a = all x in [-1, 0, 1, 2, 3] { + x >= 1 if x > 0 +} +b = any x in {k1 = "v1", k2 = "v2"} {x in ["k1", "v2"]} +c = map x in {k1 = "v1", k2 = "v2"} {x} +d = filter x in [1, 2, 3] { + x > 1 +} diff --git a/kclvm/tools/src/format/test_data/format_data/schema.golden b/kclvm/tools/src/format/test_data/format_data/schema.golden new file mode 100644 index 000000000..901994fb1 --- /dev/null +++ b/kclvm/tools/src/format/test_data/format_data/schema.golden @@ -0,0 +1,25 @@ +import math +mixin XXMixin: + nameVar: str + +schema Base: + """ + Base schema doc string + """ + mixin [ + XXMixin, + XXMixin + ] + name: str = "Alice" + labels: {str:str} = None + +schema Person[para1: str = "value", para2 = "value"](Base): + age: int = 18 + name = para + + check: + True + bool(math.log(10)) + +person = Person(para1="12") {} + diff --git a/kclvm/tools/src/format/test_data/format_data/schema.input b/kclvm/tools/src/format/test_data/format_data/schema.input new file mode 100644 index 000000000..2c877a1a1 --- /dev/null +++ b/kclvm/tools/src/format/test_data/format_data/schema.input @@ -0,0 +1,17 @@ +import math +schema XXMixin: + nameVar: str +schema Base: + """ + Base schema doc string + """ + mixin[XXMixin,XXMixin] + name:str="Alice" + labels: {str : str}=None +schema Person[para1:str="value",para2="value"] ( Base ) : + age:int=18 + name=para + check : + True + bool (math. log(10)) +person = Person(para1 = "12"){} \ No newline at end of file diff --git a/kclvm/tools/src/format/test_data/format_data/string.golden b/kclvm/tools/src/format/test_data/format_data/string.golden new file mode 100644 index 000000000..2c66a7158 --- /dev/null +++ b/kclvm/tools/src/format/test_data/format_data/string.golden @@ -0,0 +1,4 @@ +strA = '123' +strB = ''' +long string +''' diff --git a/kclvm/tools/src/format/test_data/format_data/string.input b/kclvm/tools/src/format/test_data/format_data/string.input new file mode 100644 index 000000000..91b9f9486 --- /dev/null +++ b/kclvm/tools/src/format/test_data/format_data/string.input @@ -0,0 +1,4 @@ +strA='123' +strB= ''' +long string +''' \ No newline at end of file diff --git a/kclvm/tools/src/format/test_data/format_data/type_alias.golden b/kclvm/tools/src/format/test_data/format_data/type_alias.golden new file mode 100644 index 000000000..7bc0ef6b1 --- /dev/null +++ b/kclvm/tools/src/format/test_data/format_data/type_alias.golden @@ -0,0 +1,7 @@ +schema Person: + name: str = "kcl" + age: int = 1 + +type PersonOther = Person +type Int = int +type UnionType = int|float|str diff --git a/kclvm/tools/src/format/test_data/format_data/type_alias.input b/kclvm/tools/src/format/test_data/format_data/type_alias.input new file mode 100644 index 000000000..237ab7f85 --- /dev/null +++ b/kclvm/tools/src/format/test_data/format_data/type_alias.input @@ -0,0 +1,7 @@ +schema Person: + name: str = "kcl" + age: int = 1 + +type PersonOther = Person +type Int = int +type UnionType = int | float | str diff --git a/kclvm/tools/src/format/test_data/format_data/unary.golden b/kclvm/tools/src/format/test_data/format_data/unary.golden new file mode 100644 index 000000000..a08f55c96 --- /dev/null +++ b/kclvm/tools/src/format/test_data/format_data/unary.golden @@ -0,0 +1,3 @@ +x = not True or not False +y = +1 + -1 +z = ~17 diff --git a/kclvm/tools/src/format/test_data/format_data/unary.input b/kclvm/tools/src/format/test_data/format_data/unary.input new file mode 100644 index 000000000..15b91de48 --- /dev/null +++ b/kclvm/tools/src/format/test_data/format_data/unary.input @@ -0,0 +1,3 @@ +x = not True or not False +y = +1 + -1 +z = ~ 0x11 diff --git a/kclvm/tools/src/format/test_data/format_path_data/internal_pkg/test.k b/kclvm/tools/src/format/test_data/format_path_data/internal_pkg/test.k new file mode 100644 index 000000000..457e49fa0 --- /dev/null +++ b/kclvm/tools/src/format/test_data/format_path_data/internal_pkg/test.k @@ -0,0 +1,14 @@ +if True: + a = 1 +elif True: + b = 2 + if True: + c = 3 + else: + d = 4 +else: + e = 8 + +schema Person: + name: str + age: int diff --git a/kclvm/tools/src/format/test_data/format_path_data/test.k b/kclvm/tools/src/format/test_data/format_path_data/test.k new file mode 100644 index 000000000..457e49fa0 --- /dev/null +++ b/kclvm/tools/src/format/test_data/format_path_data/test.k @@ -0,0 +1,14 @@ +if True: + a = 1 +elif True: + b = 2 + if True: + c = 3 + else: + d = 4 +else: + e = 8 + +schema Person: + name: str + age: int diff --git a/kclvm/tools/src/format/tests.rs b/kclvm/tools/src/format/tests.rs new file mode 100644 index 000000000..2dea590e1 --- /dev/null +++ b/kclvm/tools/src/format/tests.rs @@ -0,0 +1,68 @@ +use super::*; +use pretty_assertions::assert_eq; + +const FILE_INPUT_SUFFIX: &str = ".input"; +const FILE_OUTPUT_SUFFIX: &str = ".golden"; +const TEST_CASES: &[&'static str; 18] = &[ + "assert", + "check", + "blankline", + "breakline", + "codelayout", + "collection_if", + "comment", + "comp_for", + "empty", + "import", + "indent", + "inline_comment", + "lambda", + "quant", + "schema", + "string", + "type_alias", + "unary", +]; + +fn read_data(data_name: &str) -> (String, String) { + let src = std::fs::read_to_string(&format!( + "./src/format/test_data/format_data/{}{}", + data_name, FILE_INPUT_SUFFIX + )) + .unwrap(); + + ( + format_source(&src).unwrap().0, + std::fs::read_to_string(&format!( + "./src/format/test_data/format_data/{}{}", + data_name, FILE_OUTPUT_SUFFIX + )) + .unwrap(), + ) +} + +#[test] +fn test_format_source() { + for case in TEST_CASES { + let (data_input, data_output) = read_data(case); + assert_eq!(data_input, data_output, "Test failed on {}", case); + } +} + +#[test] +fn test_format() { + let opts = FormatOptions { + is_stdout: true, + recursively: false, + }; + let changed_files = format("./src/format/test_data/format_path_data/test.k", &opts).unwrap(); + assert_eq!(changed_files.len(), 1); + let changed_files = format("./src/format/test_data/format_path_data/", &opts).unwrap(); + assert_eq!(changed_files.len(), 1); + let opts = FormatOptions { + is_stdout: true, + recursively: true, + }; + let changed_files = format("./src/format/test_data/format_path_data/", &opts).unwrap(); + assert_eq!(changed_files.len(), 2); +} diff --git a/kclvm/tools/src/lib.rs b/kclvm/tools/src/lib.rs index 066865e5b..4247b607b 100644 --- a/kclvm/tools/src/lib.rs +++ b/kclvm/tools/src/lib.rs @@ -1,5 +1,7 @@ +pub mod format; pub mod printer; pub mod query; +mod util; #[macro_use] extern crate kclvm_error; diff --git a/kclvm/tools/src/printer/node.rs b/kclvm/tools/src/printer/node.rs index 20b0947a7..293d5c7fd 100644 --- a/kclvm/tools/src/printer/node.rs +++ b/kclvm/tools/src/printer/node.rs @@ -118,15 +118,14 @@ impl<'p, 'ctx> MutSelfTypedResultWalker<'ctx> for Printer<'p> { self.write_indentation(Indentation::Dedent); if !if_stmt.orelse.is_empty() { if let ast::Stmt::If(elif_stmt) = &if_stmt.orelse[0].node { - self.write("el"); + self.fill("el"); self.walk_if_stmt(elif_stmt); } else { - self.write("else:"); - self.write_newline(); + self.fill("else:"); + self.write_newline_without_fill(); self.write_indentation(Indentation::Indent); self.stmts(&if_stmt.orelse); self.write_indentation(Indentation::Dedent); - self.write_newline_without_fill(); } } else { self.write_newline_without_fill(); @@ -348,7 +347,7 @@ impl<'p, 'ctx> MutSelfTypedResultWalker<'ctx> for Printer<'p> { self.write_space(); } if let Some(value) = &schema_attr.value { - self.expr(&value); + self.expr(value); } self.write_newline_without_fill(); } @@ -363,6 +362,9 @@ impl<'p, 'ctx> MutSelfTypedResultWalker<'ctx> for Printer<'p> { fn walk_unary_expr(&mut self, unary_expr: &'ctx ast::UnaryExpr) -> Self::Result { self.write(unary_expr.op.symbol()); + if matches!(unary_expr.op, ast::UnaryOp::Not) { + self.write_space(); + } self.expr(&unary_expr.operand); } diff --git a/kclvm/tools/src/printer/test_data/codelayout.output b/kclvm/tools/src/printer/test_data/codelayout.output index 61df4a554..28fa41ccc 100644 --- a/kclvm/tools/src/printer/test_data/codelayout.output +++ b/kclvm/tools/src/printer/test_data/codelayout.output @@ -17,7 +17,6 @@ elif True: b = 2 else: c = 3 - d = 1 + 2 e = (1 + 2) f = [1, 2, 3] diff --git a/kclvm/tools/src/util/mod.rs b/kclvm/tools/src/util/mod.rs new file mode 100644 index 000000000..34cccbafa --- /dev/null +++ b/kclvm/tools/src/util/mod.rs @@ -0,0 +1,19 @@ +use anyhow::Result; +use kclvm_config::modfile::KCL_FILE_SUFFIX; +use std::path::Path; +use walkdir::WalkDir; + +/// Get kcl files from path. +pub(crate) fn get_kcl_files>(path: P, recursively: bool) -> Result> { + let mut files = vec![]; + for entry in WalkDir::new(path).into_iter().filter_map(|e| e.ok()) { + let path = entry.path(); + if path.is_file() { + let file = path.to_str().unwrap(); + if file.ends_with(KCL_FILE_SUFFIX) && (recursively || entry.depth() == 1) { + files.push(file.to_string()) + } + } + } + Ok(files) +}