Skip to content

Commit

Permalink
Add support for editorconfig (#940)
Browse files Browse the repository at this point in the history
* Editorconfig WIP

* Merging editorconfig.core with ILRepack

* Replace EditorConfig.Core dll with unlisted nuget package.

* Renamed IndentSpaceNum and PageWidth

* Remove json configuration

* Added unit tests for editorconfig support.

* Update commandline tool

* Bump to alpha 12

* Restore unit tests

* Update code comment
  • Loading branch information
nojaf authored Jul 6, 2020
1 parent 344d442 commit 07fb56b
Show file tree
Hide file tree
Showing 44 changed files with 444 additions and 641 deletions.
3 changes: 2 additions & 1 deletion RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
### 4.0.0-alpha-011 - 06/2020
### 4.0.0-alpha-012 - 06/2020
* WIP for [#705](https://github.com/fsprojects/fantomas/issues/705)
* FCS 36
* Replaced json configuration with .editorconfig

### 3.3.0 - 02/2020

Expand Down
84 changes: 31 additions & 53 deletions docs/Documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -244,61 +244,39 @@ More preferences will be added depending on use cases.

##### `--config <Path to file or folder>`

Use a JSON configuration file based on a [schema](../src/Fantomas/schema.json) to set the formatting options.

A default configuration file would look like
```json
{
"IndentSpaceNum":4,
"PageWidth":120,
"SemicolonAtEndOfLine":false,
"SpaceBeforeParameter":true,
"SpaceBeforeLowercaseInvocation":true,
"SpaceBeforeUppercaseInvocation":false,
"SpaceBeforeClassConstructor":false,
"SpaceBeforeMember":false,
"SpaceBeforeColon":false,
"SpaceAfterComma":true ,
"SpaceBeforeSemicolon": false,
"SpaceAfterSemicolon":true ,
"IndentOnTryWith":false,
"SpaceAroundDelimiter":true ,
"MaxIfThenElseShortWidth":40,
"KeepIfThenInSameLine":false,
"MaxInfixOperatorExpression":50,
"MaxRecordWidth":40,
"MaxArrayOrListWidth":40,
"MaxValueBindingWidth":40,
"MaxFunctionBindingWidth":40,
"MultilineBlockBracketsOnSameColumn":false,
"NewlineBetweenTypeDefinitionAndMembers":false,
"MaxElmishWidth": 40,
"StrictMode":false
}
Use an .editorconfig configuration file to set the formatting options.

A default .editorconfig file would look like
```ini
[*.fs]
indent_size=4
max_line_length=120
fsharp_semicolon_at_end_of_line=false
fsharp_space_before_parameter=true
fsharp_space_before_lowercase_invocation=true
fsharp_space_before_uppercase_invocation=false
fsharp_space_before_class_constructor=false
fsharp_space_before_member=false
fsharp_space_before_colon=false
fsharp_space_after_comma=true
fsharp_space_before_semicolon=false
fsharp_space_after_semicolon=true
fsharp_indent_on_try_with=false
fsharp_space_around_delimiter=true
fsharp_max_if_then_else_short_width=40
fsharp_max_infix_operator_expression=50
fsharp_max_record_width=40
fsharp_max_array_or_list_width=40
fsharp_max_value_binding_width=40
fsharp_max_function_binding_width=40
fsharp_multiline_block_brackets_on_same_column=false
fsharp_newline_between_type_definition_and_members=false
fsharp_keep_if_then_in_same_line=false
fsharp_max_elmish_width=40
fsharp_single_argument_web_mode=false
fsharp_strict_mode=false
```

However, **a configuration file overwrites options** from [the default configuration](../src/Fantomas/FormatConfig.fs).

The argument passed after `--config ` can be a file named `fantomas-config.json` or a folder.
In both cases a Fantomas will try and locate `fantomas-config.json` in all the parent folders.
The found configuration files are then being applied to to the default configuration from top to bottom.

F.ex.

```
C:\
Temp\
fantomas-config.json
MyProject\
fantomas-config.json
```

Formatting with `dotnet fantomas MyFile.fs --config "C:\Temp\MyProject"` will first apply the settings in `C:\Temp\fantomas-config.json` and then those of `C:\Temp\MyProject\fantomas-config.json`.

If the `--config` is used in combination with other settings, the configuration is applied first and then the other arguments.

Warnings will be given if settings in the configuration no longer apply for the current version of Fantomas.

### Using the API

See [CodeFormatter.fsi](../src/Fantomas/CodeFormatter.fsi) to view the API of Fantomas.
2 changes: 2 additions & 0 deletions paket.dependencies
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,7 @@ nuget NUnit3TestAdapter 3.15.1
nuget NunitXml.TestLogger
nuget Argu
nuget BenchmarkDotNet
nuget Fantomas.EditorConfig.Core copy_local: true
nuget ILRepack.MSBuild.Task copy_local: true

github: fsprojects/fantomas:829faa6ba834f99afed9b4434b3a1680536474b2 src/Fantomas/CodePrinter.fs
20 changes: 16 additions & 4 deletions paket.lock
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ NUGET
System.ValueTuple (>= 4.5)
BenchmarkDotNet.Annotations (0.12.1)
CommandLineParser (2.8)
Fantomas.EditorConfig.Core (1.0) - copy_local: true
FsCheck (2.14)
FSharp.Core (>= 4.2.3)
FSharp.Compiler.Service (36.0.1)
Expand All @@ -40,6 +41,17 @@ NUGET
NETStandard.Library (>= 2.0.3)
NUnit (>= 3.12 < 4.0)
Iced (1.7)
ILRepack.MSBuild.Task (2.0.13) - copy_local: true
Microsoft.Build.Framework (>= 15.9.20)
Microsoft.Build.Utilities.Core (>= 15.9.20)
Microsoft.Build.Framework (16.6) - copy_local: true
System.Security.Permissions (>= 4.7)
Microsoft.Build.Utilities.Core (16.6) - copy_local: true
Microsoft.Build.Framework (>= 16.6)
Microsoft.Win32.Registry (>= 4.3)
System.Collections.Immutable (>= 1.5)
System.Security.Permissions (>= 4.7)
System.Text.Encoding.CodePages (>= 4.0.1)
Microsoft.CodeAnalysis.Analyzers (3.0)
Microsoft.CodeAnalysis.Common (3.6)
Microsoft.CodeAnalysis.Analyzers (>= 3.0)
Expand Down Expand Up @@ -85,7 +97,7 @@ NUGET
System.Memory (>= 4.5.3) - restriction: || (&& (== netcoreapp3.1) (< netcoreapp2.0)) (&& (== netcoreapp3.1) (< netcoreapp2.1)) (&& (== netcoreapp3.1) (>= uap10.1)) (== netstandard2.0)
System.Security.AccessControl (>= 4.7)
System.Security.Principal.Windows (>= 4.7)
Microsoft.Win32.SystemEvents (4.7) - restriction: || (== netcoreapp3.1) (&& (== netstandard2.0) (>= netcoreapp3.0))
Microsoft.Win32.SystemEvents (4.7) - copy_local: true, restriction: || (== netcoreapp3.1) (&& (== netstandard2.0) (>= netcoreapp3.0))
Microsoft.NETCore.Platforms (>= 3.1) - restriction: || (== netcoreapp3.1) (&& (== netstandard2.0) (>= netcoreapp2.0))
NETStandard.Library (2.0.3)
Microsoft.NETCore.Platforms (>= 1.1)
Expand Down Expand Up @@ -190,7 +202,7 @@ NUGET
System.Threading.Tasks (>= 4.3)
System.Threading.Thread (>= 4.3)
System.Threading.ThreadPool (>= 4.3)
System.Drawing.Common (4.7) - restriction: || (== netcoreapp3.1) (&& (== netstandard2.0) (>= netcoreapp3.0))
System.Drawing.Common (4.7) - copy_local: true, restriction: || (== netcoreapp3.1) (&& (== netstandard2.0) (>= netcoreapp3.0))
Microsoft.NETCore.Platforms (>= 3.1) - restriction: || (== netcoreapp3.1) (&& (== netstandard2.0) (>= netcoreapp2.0))
Microsoft.Win32.SystemEvents (>= 4.7) - restriction: || (== netcoreapp3.1) (&& (== netstandard2.0) (>= netcoreapp2.0))
System.Globalization (4.3) - restriction: || (== netcoreapp3.1) (&& (== netstandard2.0) (>= netcoreapp1.0))
Expand Down Expand Up @@ -301,7 +313,7 @@ NUGET
System.Security.Principal.Windows (>= 4.7)
System.Security.Cryptography.ProtectedData (4.7)
System.Memory (>= 4.5.3) - restriction: || (&& (== netcoreapp3.1) (< netcoreapp2.1)) (== netstandard2.0)
System.Security.Permissions (4.7)
System.Security.Permissions (4.7) - copy_local: true
System.Security.AccessControl (>= 4.7)
System.Windows.Extensions (>= 4.7) - restriction: || (== netcoreapp3.1) (&& (== netstandard2.0) (>= netcoreapp3.0))
System.Security.Principal.Windows (4.7)
Expand Down Expand Up @@ -334,7 +346,7 @@ NUGET
System.Runtime (>= 4.3)
System.Runtime.Handles (>= 4.3)
System.ValueTuple (4.5)
System.Windows.Extensions (4.7) - restriction: || (== netcoreapp3.1) (&& (== netstandard2.0) (>= netcoreapp3.0))
System.Windows.Extensions (4.7) - copy_local: true, restriction: || (== netcoreapp3.1) (&& (== netstandard2.0) (>= netcoreapp3.0))
System.Drawing.Common (>= 4.7) - restriction: || (== netcoreapp3.1) (&& (== netstandard2.0) (>= netcoreapp3.0))
System.Xml.ReaderWriter (4.3.1) - restriction: || (== netcoreapp3.1) (&& (== netstandard2.0) (>= netcoreapp1.0))
System.Collections (>= 4.3)
Expand Down
20 changes: 14 additions & 6 deletions src/Fantomas.CoreGlobalTool.Tests/ConfigTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,28 @@ module Fantomas.CoreGlobalTool.Tests.ConfigTests
open NUnit.Framework
open FsUnit
open Fantomas.CoreGlobalTool.Tests.TestHelpers
open Fantomas.FormatConfig

[<Test>]
let ``config file in working directory should not require relative prefix, 821`` () =
use fileFixture =
new TemporaryFileCodeSample("let a = 9")
new TemporaryFileCodeSample("let a = // foo
9")

use configFixture =
new ConfigurationFile({ FormatConfig.Default with
IndentSpaceNum = 2 })
new ConfigurationFile("""
[*.fs]
indent_size=2
""" )

let (exitCode, output) =
runFantomasTool (sprintf "--config fantomas-config.json %s" fileFixture.Filename)
runFantomasTool fileFixture.Filename

exitCode |> should equal 0
output
|> should startWith (sprintf "Processing %s" fileFixture.Filename)
|> should startWith (sprintf "Processing %s" fileFixture.Filename)

let result = System.IO.File.ReadAllText(fileFixture.Filename)
result
|> should equal """let a = // foo
9
"""
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable>
<GenerateProgramFile>false</GenerateProgramFile>
<Version>4.0.0-alpha-011</Version>
<Version>4.0.0-alpha-012</Version>
<NoWarn>FS0988</NoWarn>
</PropertyGroup>
<ItemGroup>
Expand Down
5 changes: 2 additions & 3 deletions src/Fantomas.CoreGlobalTool.Tests/TestHelpers.fs
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,8 @@ type OutputFile internal () =
if File.Exists(filename) then
File.Delete(filename)

type ConfigurationFile internal (config: Fantomas.FormatConfig.FormatConfig) =
let filename = Path.Join(Path.GetTempPath(), "fantomas-config.json")
let content = Config.configToJson config
type ConfigurationFile internal (content: string) =
let filename = Path.Join(Path.GetTempPath(), ".editorconfig")
do File.WriteAllText(filename, content)
member _.Filename: string = filename
interface IDisposable with
Expand Down
2 changes: 1 addition & 1 deletion src/Fantomas.CoreGlobalTool/Fantomas.CoreGlobalTool.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<TargetFramework>netcoreapp3.1</TargetFramework>
<ToolCommandName>fantomas</ToolCommandName>
<PackAsTool>True</PackAsTool>
<Version>4.0.0-alpha-011</Version>
<Version>4.0.0-alpha-012</Version>
<AssemblyName>fantomas-tool</AssemblyName>
</PropertyGroup>
<ItemGroup>
Expand Down
61 changes: 21 additions & 40 deletions src/Fantomas.CoreGlobalTool/Program.fs
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,14 @@ open System.Text
let extensions = set [| ".fs"; ".fsx"; ".fsi"; ".ml"; ".mli"; |]

type Arguments =
| [<Unique>] Recurse
| [<Unique; AltCommandLine("-r")>] Recurse
| [<Unique>] Force
| [<Unique>] Profile
| [<Unique>] Fsi of string
| [<Unique>] Stdin
| [<Unique>] Stdout
| [<Unique>] Out of string
| [<Unique>] Indent of int
| [<Unique>] Check
| [<Unique;AltCommandLine("-c")>] Config of string
| [<Unique;AltCommandLine("-v")>] Version
| [<MainCommand>] Input of string
with
Expand All @@ -31,9 +29,7 @@ with
| Fsi _ -> "Read F# source from stdin as F# signatures."
| Stdin -> "Read F# source from standard input."
| Stdout -> " Write the formatted source code to standard output."
| Indent _ -> "Set number of spaces for indentation (default = 4). The value should be between 1 and 10."
| Check -> "Don't format files, just check if they have changed. Exits with 0 if it's formatted correctly, with 1 if some files need formatting and 99 if there was an internal error"
| Config _ -> "Use configuration found in file or folder."
| Version -> "Displays the version of Fantomas"
| Input _ -> sprintf "Input path: can be a folder or file with %s extension." (Seq.map (fun s -> "*" + s) extensions |> String.concat ",")

Expand Down Expand Up @@ -108,9 +104,9 @@ let processSourceString isFsiFile s (tw : Choice<TextWriter, string>) config =
|> Async.RunSynchronously

/// Format inFile and write to text writer
let processSourceFile inFile (tw : TextWriter) config =
let processSourceFile inFile (tw : TextWriter) =
async {
let! formatted = inFile |> FakeHelpers.formatFileAsync config
let! formatted = FakeHelpers.formatFileAsync inFile

match formatted with
| FakeHelpers.FormatResult.Formatted(_, formattedContent) ->
Expand Down Expand Up @@ -159,8 +155,8 @@ let private reportCheckResults (output: TextWriter) (checkResult: FakeHelpers.Ch
|> List.map (fun filename -> sprintf "%s needs formatting" filename)
|> Seq.iter output.WriteLine

let runCheckCommand (config: FormatConfig) (recurse: bool) (inputPath: InputPath) : int =
let check files = Async.RunSynchronously (FakeHelpers.checkCode config files)
let runCheckCommand (recurse: bool) (inputPath: InputPath) : int =
let check files = Async.RunSynchronously (FakeHelpers.checkCode files)

let processCheckResult (checkResult: FakeHelpers.CheckResult) =
if checkResult.IsValid then
Expand Down Expand Up @@ -228,23 +224,7 @@ let main argv =
let recurse = results.Contains<@ Arguments.Recurse @>
let version = results.TryGetResult<@ Arguments.Version @>

let config =
results.TryGetResult<@ Arguments.Config @>
|> Option.map (fun configPath ->
let configResult = CodeFormatter.ReadConfiguration configPath
match configResult with
| Success s -> s
| PartialSuccess (ps, warnings) ->
List.iter (writeInColor ConsoleColor.DarkYellow) warnings
ps
| Failure e ->
writeInColor ConsoleColor.DarkRed "Couldn't process one or more Fantomas configuration files, falling back to the default configuration"
writeInColor ConsoleColor.DarkRed (e.ToString())
FormatConfig.Default
)
|> Option.defaultValue FormatConfig.Default

let fileToFile (inFile : string) (outFile : string) config =
let fileToFile (inFile : string) (outFile : string) =
try
printfn "Processing %s" inFile
let hasByteOrderMark = hasByteOrderMark inFile
Expand All @@ -258,9 +238,9 @@ let main argv =

if profile then
File.ReadLines(inFile) |> Seq.length |> printfn "Line count: %i"
time (fun () -> processSourceFile inFile buffer config)
time (fun () -> processSourceFile inFile buffer)
else
processSourceFile inFile buffer config
processSourceFile inFile buffer
buffer.Flush()
printfn "%s has been written." outFile
with
Expand Down Expand Up @@ -297,12 +277,13 @@ let main argv =
if force then
stdout.Write(s)

let processFile inputFile outputFile config =
let processFile inputFile outputFile =
if inputFile <> outputFile then
fileToFile inputFile outputFile config
fileToFile inputFile outputFile
else
printfn "Processing %s" inputFile
let content = File.ReadAllText inputFile
let config = CodeFormatter.ReadConfiguration(inputFile)
stringToFile content inputFile config

let processFolder inputFolder outputFolder =
Expand All @@ -317,13 +298,13 @@ let main argv =
Path.Combine(outputFolder, suffix)
else i

processFile i o config)
processFile i o)

let fileToStdOut inFile config =
let fileToStdOut inFile =
try
use buffer = new StringWriter()
// Don't record running time when output formatted content to console
processSourceFile inFile buffer config
processSourceFile inFile buffer
stdout.Write(buffer.ToString())
with
| exn ->
Expand All @@ -338,26 +319,26 @@ let main argv =
let check = results.Contains<@ Arguments.Check @>
if check then
inputPath
|> runCheckCommand config recurse
|> runCheckCommand recurse
|> exit
else
match inputPath, outputPath with
| InputPath.Unspecified, _ ->
eprintfn "Input path is missing..."
exit 1
| InputPath.Folder p1, OutputPath.Notknown -> processFolder p1 p1
| InputPath.File p1, OutputPath.Notknown -> processFile p1 p1 config
| InputPath.File p1, OutputPath.Notknown -> processFile p1 p1
| InputPath.File p1, OutputPath.IO p2 ->
processFile p1 p2 config
processFile p1 p2
| InputPath.Folder p1, OutputPath.IO p2 -> processFolder p1 p2
| InputPath.StdIn s, OutputPath.IO p ->
stringToFile s p config
stringToFile s p FormatConfig.Default
| InputPath.StdIn s, OutputPath.Notknown
| InputPath.StdIn s, OutputPath.StdOut ->
stringToStdOut s config
stringToStdOut s FormatConfig.Default
| InputPath.File p, OutputPath.StdOut ->
fileToStdOut p config
fileToStdOut p
| InputPath.Folder p, OutputPath.StdOut ->
allFiles recurse p
|> Seq.iter (fun p -> fileToStdOut p config)
|> Seq.iter fileToStdOut
0
Loading

0 comments on commit 07fb56b

Please sign in to comment.