Repository: domluna/JuliaFormatter.jl
Pull request #240 (2020-05-26 22:24:46)

Make format_file and format return true if file(s) already formatted
Makes the functions `format` and `format_file` return a boolean: `true` if the files were already formatted and `false` if not.  This simplifies the process of checking whether files are formatted during CI. E.g. `julia -e 'using JuliaFormatter; format(".") || exit(1)'`

Previously those functions return `nothing` so this should not break most code.

domluna: 👍

This may break some existing github actions so I'll have to verify that.

Patch:
diff --git a/docs/src/index.md b/docs/src/index.md
index 1cbc0365..2c966576 100644
--- a/docs/src/index.md
+++ b/docs/src/index.md
@@ -64,7 +64,7 @@ format(
 )
 ```
 
-The `text` argument to `format_text` is a string containing the code to be formatted; the formatted code is retuned as a new string. The `file` argument to `format_file` is the path of a file to be formatted. The `format` function is either called with a singe string to format if it is a `.jl` file or to recuse into looking for `.jl` files if it is a directory. It can also be called with a collection of such paths to iterate over.
+The `text` argument to `format_text` is a string containing the code to be formatted; the formatted code is retuned as a new string. The `file` argument to `format_file` is the path of a file to be formatted. The `format` function is either called with a singe string to format if it is a `.jl` file or to recuse into looking for `.jl` files if it is a directory. It can also be called with a collection of such paths to iterate over. The `format_file` and `format` functions will return `true` if the file(s) are already formatted (i.e. if no changes were made to the file(s)) and `false` if changes were required to format the file(s).
 
 [`format`](@ref) calls [`format_file`](@ref) which in turn calls [`format_text`](@ref).
 
diff --git a/src/JuliaFormatter.jl b/src/JuliaFormatter.jl
index a38cc231..710904e7 100644
--- a/src/JuliaFormatter.jl
+++ b/src/JuliaFormatter.jl
@@ -239,7 +239,7 @@ end
         overwrite::Bool = true,
         verbose::Bool = false,
         format_options...,
-    )
+    )::Bool
 
 Formats the contents of `filename` assuming it's a Julia source file.
 
@@ -255,13 +255,18 @@ to `stdout`.
 ### Formatting Options
 
 See [`format_text`](@ref) for description of formatting options.
+
+### Output
+ 
+Returns a boolean indicating whether the file was already formatted (`true`)
+or not (`false`). 
 """
 function format_file(
     filename::AbstractString;
     overwrite::Bool = true,
     verbose::Bool = false,
     format_options...,
-)
+)::Bool
     path, ext = splitext(filename)
     shebang_pattern = r"^#!\s*/.*\bjulia[0-9.-]*\b"
     if ext != ".jl" && match(shebang_pattern, readline(filename)) === nothing
@@ -269,10 +274,10 @@ function format_file(
     end
     verbose && println("Formatting $filename")
     str = String(read(filename))
-    str = format_text(str; format_options...)
-    str = replace(str, r"\n*$" => "\n")
-    overwrite ? write(filename, str) : write(path * "_fmt" * ext, str)
-    nothing
+    formatted_str = format_text(str; format_options...)
+    formatted_str = replace(formatted_str, r"\n*$" => "\n")
+    overwrite ? write(filename, formatted_str) : write(path * "_fmt" * ext, formatted_str)
+    return formatted_str == str
 end
 
 if VERSION < v"1.1.0"
@@ -321,7 +326,7 @@ const CONFIG_FILE_NAME = ".JuliaFormatter.toml"
     format(
         paths; # a path or collection of paths
         options...,
-    )
+    )::Bool
 
 Recursively descend into files and directories, formatting any `.jl`
 files by calling `format_file` on them.
@@ -332,9 +337,15 @@ This function will look for `.JuliaFormatter.toml` in the location of the file b
 formatted, and searching _up_ the file tree until a config file is (or isn't) found.
 When found, the configurations in the file will overwrite the given `options`.
 See ["Configuration File"](@id) for more details.
+
+### Output
+ 
+Returns a boolean indicating whether the file was already formatted (`true`)
+or not (`false`). 
 """
-function format(paths; options...)
+function format(paths; options...)::Bool
     dir2config = Dict{String,Any}()
+    already_formatted = true
     function find_config_file(dir)
         next_dir = dirname(dir)
         config = if (next_dir == dir || # ensure to escape infinite recursion
@@ -350,7 +361,7 @@ function format(paths; options...)
     end
 
     for path in paths
-        if isfile(path)
+        already_formatted &= if isfile(path)
             dir = dirname(realpath(path))
             opts = if (config = find_config_file(dir)) !== nothing
                 overwrite_options(options, config)
@@ -359,24 +370,28 @@ function format(paths; options...)
             end
             format_file(path; opts...)
         else
-            for (root, dirs, files) in walkdir(path)
-                for file in files
+            reduce(walkdir(path), init = true) do formatted_path, dir_branch
+                root, dirs, files = dir_branch
+                formatted_path & reduce(files, init = true) do formatted_file, file
                     _, ext = splitext(file)
-                    ext == ".jl" || continue
                     full_path = joinpath(root, file)
-                    ".git" in splitpath(full_path) && continue
-                    dir = realpath(root)
-                    opts = if (config = find_config_file(dir)) !== nothing
-                        overwrite_options(options, config)
+                    formatted_file &
+                    if ext == ".jl" && !(".git" in splitpath(full_path))
+                        dir = realpath(root)
+                        opts = if (config = find_config_file(dir)) !== nothing
+                            overwrite_options(options, config)
+                        else
+                            options
+                        end
+                        format_file(full_path; opts...)
                     else
-                        options
+                        true
                     end
-                    format_file(full_path; opts...)
                 end
             end
         end
     end
-    nothing
+    return already_formatted
 end
 format(path::AbstractString; options...) = format((path,); options...)
 


Test patch:
diff --git a/test/config.jl b/test/config.jl
index eda42a13..7a8493db 100644
--- a/test/config.jl
+++ b/test/config.jl
@@ -17,7 +17,7 @@
         open(io -> write(io, config2), config_path, "w")
         open(io -> write(io, before), code_path, "w")
 
-        format(code_path)
+        @test format(code_path) == false
         @test read(code_path, String) == after2
     finally
         rm(sandbox_dir; recursive = true)
@@ -44,10 +44,12 @@
         open(io -> write(io, before), sub_code_path, "w")
         open(io -> write(io, before), subsub_code_path, "w")
 
-        format(sub_code_path)
+        @test format(sub_code_path) == false
         @test read(sub_code_path, String) == after2
-        format(subsub_code_path)
+        @test format(subsub_code_path) == false
         @test read(subsub_code_path, String) == after2
+        @test format(sub_code_path) == true
+        @test format(subsub_code_path) == true
     finally
         rm(sandbox_dir; recursive = true)
     end
@@ -70,9 +72,10 @@
         open(io -> write(io, before), code_path, "w")
         open(io -> write(io, before), sub_code_path, "w")
 
-        format(sandbox_dir)
+        @test format(sandbox_dir) == false
         @test read(code_path, String) == after2
         @test read(sub_code_path, String) == after2
+        @test format(sandbox_dir) == true
     finally
         rm(sandbox_dir; recursive = true)
     end
@@ -104,10 +107,11 @@
         open(io -> write(io, before), sub_code1_path, "w")
         open(io -> write(io, before), sub_code2_path, "w")
 
-        format(sandbox_dir)
+        @test format(sandbox_dir) == false
         @test read(code_path, String) == after2
         @test read(sub_code1_path, String) == after4
         @test read(sub_code2_path, String) == after2
+        @test format(sandbox_dir) == true
     finally
         rm(sandbox_dir; recursive = true)
     end
@@ -144,10 +148,11 @@
         open(io -> write(io, before), sub_code2_path, "w")
 
         cd(sandbox_dir)
-        format(".")
+        @test format(".") == false
         @test read(code_path, String) == after2
         @test read(sub_code1_path, String) == after4
         @test read(sub_code2_path, String) == after2
+        @test format(".") == true
     finally
         cd(original_dir)
         rm(sandbox_dir; recursive = true)
diff --git a/test/default_style.jl b/test/default_style.jl
index 8e4b32dc..449fca55 100644
--- a/test/default_style.jl
+++ b/test/default_style.jl
@@ -3630,8 +3630,8 @@
         open(f1, "w") do io
             write(io, "a = 10\n\n\n\n\n\n")
         end
-        format_file(f1)
-        format_file(f1)
+        @test format_file(f1) == false
+        @test format_file(f1) == true
         open(f1) do io
             res = read(io, String)
             @test res == str