Skip to content

Commit 02b6d8e

Browse files
dawedawenojaf
andauthored
Move profile logic to Format.fs and use Spectre (#2770)
* Move profile logic to Format.fs and use Spectre * Rename ProfileInfos -> ProfileInfo Don't touch Fantomas.Core * Reuse the oks and unchanged lists from partitionResults for the call to reportProfileInfos * Show shorter time format. * Bundle parameters to formatContentAsync and formatFileAsync in a single record FormatParams * Move profile logic to Format.fs and use Spectre * Rename ProfileInfos -> ProfileInfo Don't touch Fantomas.Core * Remove ProcessResult and use FormatResult for everything * centralize exception creation and fix wording * Final nits --------- Co-authored-by: nojaf <[email protected]>
1 parent 2becbe1 commit 02b6d8e

File tree

3 files changed

+248
-177
lines changed

3 files changed

+248
-177
lines changed

src/Fantomas/Format.fs

Lines changed: 141 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,115 +1,168 @@
1-
module Fantomas.Format
1+
namespace Fantomas
22

33
open System
44
open System.IO
55
open Fantomas.Core
66

7+
type ProfileInfo = { LineCount: int; TimeTaken: TimeSpan }
8+
79
type FormatResult =
8-
| Formatted of filename: string * formattedContent: string
9-
| Unchanged of filename: string
10+
| Formatted of filename: string * formattedContent: string * profileInfo: ProfileInfo option
11+
| Unchanged of filename: string * profileInfo: ProfileInfo option
1012
| InvalidCode of filename: string * formattedContent: string
1113
| Error of filename: string * formattingError: Exception
1214
| IgnoredFile of filename: string
1315

14-
let private formatContentInternalAsync
15-
(compareWithoutLineEndings: bool)
16-
(config: FormatConfig)
17-
(file: string)
18-
(originalContent: string)
19-
: Async<FormatResult> =
20-
if IgnoreFile.isIgnoredFile (IgnoreFile.current.Force()) file then
21-
async { return IgnoredFile file }
22-
else
23-
async {
24-
try
25-
let isSignatureFile = Path.GetExtension(file) = ".fsi"
16+
type FormatParams =
17+
{ Config: FormatConfig
18+
CompareWithoutLineEndings: bool
19+
Profile: bool
20+
File: string }
2621

27-
let! { Code = formattedContent } =
28-
CodeFormatter.FormatDocumentAsync(isSignatureFile, originalContent, config)
22+
static member Create(config: FormatConfig, compareWithoutLineEndings: bool, profile: bool, file: string) =
23+
{ Config = config
24+
CompareWithoutLineEndings = compareWithoutLineEndings
25+
Profile = profile
26+
File = file }
2927

30-
let contentChanged =
31-
if compareWithoutLineEndings then
32-
let stripNewlines (s: string) =
33-
System.Text.RegularExpressions.Regex.Replace(s, @"\r", String.Empty)
28+
static member Create(compareWithoutLineEndings: bool, profile: bool, file: string) =
29+
{ Config = EditorConfig.readConfiguration file
30+
CompareWithoutLineEndings = compareWithoutLineEndings
31+
Profile = profile
32+
File = file }
3433

35-
(stripNewlines originalContent) <> (stripNewlines formattedContent)
36-
else
37-
originalContent <> formattedContent
34+
type CheckResult =
35+
{ Errors: (string * exn) list
36+
Formatted: string list }
3837

39-
if contentChanged then
40-
let! isValid = CodeFormatter.IsValidFSharpCodeAsync(isSignatureFile, formattedContent)
38+
member this.HasErrors = List.isNotEmpty this.Errors
39+
member this.NeedsFormatting = List.isNotEmpty this.Formatted
40+
member this.IsValid = List.isEmpty this.Errors && List.isEmpty this.Formatted
4141

42-
if not isValid then
43-
return InvalidCode(filename = file, formattedContent = formattedContent)
42+
module Format =
43+
44+
let private formatContentInternalAsync
45+
(formatParams: FormatParams)
46+
(originalContent: string)
47+
: Async<FormatResult> =
48+
if IgnoreFile.isIgnoredFile (IgnoreFile.current.Force()) formatParams.File then
49+
async { return IgnoredFile formatParams.File }
50+
else
51+
async {
52+
try
53+
let isSignatureFile = Path.GetExtension(formatParams.File) = ".fsi"
54+
55+
let! { Code = formattedContent }, profileInfo =
56+
if formatParams.Profile then
57+
async {
58+
let sw = Diagnostics.Stopwatch.StartNew()
59+
60+
let! res =
61+
CodeFormatter.FormatDocumentAsync(
62+
isSignatureFile,
63+
originalContent,
64+
formatParams.Config
65+
)
66+
67+
sw.Stop()
68+
69+
let count =
70+
originalContent.Length - originalContent.Replace(Environment.NewLine, "").Length
71+
72+
let profileInfo =
73+
{ LineCount = count
74+
TimeTaken = sw.Elapsed }
75+
76+
return res, Some profileInfo
77+
}
78+
else
79+
async {
80+
let! res =
81+
CodeFormatter.FormatDocumentAsync(
82+
isSignatureFile,
83+
originalContent,
84+
formatParams.Config
85+
)
86+
87+
return res, None
88+
}
89+
90+
let contentChanged =
91+
if formatParams.CompareWithoutLineEndings then
92+
let stripNewlines (s: string) =
93+
System.Text.RegularExpressions.Regex.Replace(s, @"\r", String.Empty)
94+
95+
(stripNewlines originalContent) <> (stripNewlines formattedContent)
96+
else
97+
originalContent <> formattedContent
98+
99+
if contentChanged then
100+
let! isValid = CodeFormatter.IsValidFSharpCodeAsync(isSignatureFile, formattedContent)
101+
102+
if not isValid then
103+
return InvalidCode(filename = formatParams.File, formattedContent = formattedContent)
104+
else
105+
return
106+
Formatted(
107+
filename = formatParams.File,
108+
formattedContent = formattedContent,
109+
profileInfo = profileInfo
110+
)
44111
else
45-
return Formatted(filename = file, formattedContent = formattedContent)
46-
else
47-
return Unchanged(filename = file)
48-
with ex ->
49-
return Error(file, ex)
50-
}
112+
return Unchanged(filename = formatParams.File, profileInfo = profileInfo)
113+
with ex ->
114+
return Error(formatParams.File, ex)
115+
}
51116

52-
let formatContentAsync = formatContentInternalAsync false
117+
let formatContentAsync = formatContentInternalAsync
53118

54-
let private formatFileInternalAsync (compareWithoutLineEndings: bool) (file: string) =
55-
let config = EditorConfig.readConfiguration file
119+
let private formatFileInternalAsync (parms: FormatParams) =
120+
if IgnoreFile.isIgnoredFile (IgnoreFile.current.Force()) parms.File then
121+
async { return IgnoredFile parms.File }
122+
else
56123

57-
if IgnoreFile.isIgnoredFile (IgnoreFile.current.Force()) file then
58-
async { return IgnoredFile file }
59-
else
124+
async {
125+
let! originalContent = File.ReadAllTextAsync parms.File |> Async.AwaitTask
60126

61-
async {
62-
let! originalContent = File.ReadAllTextAsync file |> Async.AwaitTask
127+
let! formatted = originalContent |> formatContentInternalAsync parms
63128

129+
return formatted
130+
}
131+
132+
let formatFileAsync = formatFileInternalAsync
133+
134+
/// Runs a check on the given files and reports the result to the given output:
135+
///
136+
/// * It shows the paths of the files that need formatting
137+
/// * It shows the path and the error message of files that failed the format check
138+
///
139+
/// Returns:
140+
///
141+
/// A record with the file names that were formatted and the files that encounter problems while formatting.
142+
let checkCode (filenames: seq<string>) =
143+
async {
64144
let! formatted =
65-
originalContent
66-
|> formatContentInternalAsync compareWithoutLineEndings config file
145+
filenames
146+
|> Seq.filter (IgnoreFile.isIgnoredFile (IgnoreFile.current.Force()) >> not)
147+
|> Seq.map (fun f -> formatFileInternalAsync (FormatParams.Create(true, false, f)))
148+
|> Async.Parallel
67149

68-
return formatted
69-
}
150+
let getChangedFile =
151+
function
152+
| FormatResult.Unchanged _
153+
| FormatResult.IgnoredFile _ -> None
154+
| FormatResult.Formatted(f, _, _)
155+
| FormatResult.Error(f, _)
156+
| FormatResult.InvalidCode(f, _) -> Some f
70157

71-
let formatFileAsync = formatFileInternalAsync false
158+
let changes = formatted |> Seq.choose getChangedFile |> Seq.toList
72159

73-
type CheckResult =
74-
{ Errors: (string * exn) list
75-
Formatted: string list }
160+
let getErrors =
161+
function
162+
| FormatResult.Error(f, e) -> Some(f, e)
163+
| _ -> None
76164

77-
member this.HasErrors = List.isNotEmpty this.Errors
78-
member this.NeedsFormatting = List.isNotEmpty this.Formatted
79-
member this.IsValid = List.isEmpty this.Errors && List.isEmpty this.Formatted
165+
let errors = formatted |> Seq.choose getErrors |> Seq.toList
80166

81-
/// Runs a check on the given files and reports the result to the given output:
82-
///
83-
/// * It shows the paths of the files that need formatting
84-
/// * It shows the path and the error message of files that failed the format check
85-
///
86-
/// Returns:
87-
///
88-
/// A record with the file names that were formatted and the files that encounter problems while formatting.
89-
let checkCode (filenames: seq<string>) =
90-
async {
91-
let! formatted =
92-
filenames
93-
|> Seq.filter (IgnoreFile.isIgnoredFile (IgnoreFile.current.Force()) >> not)
94-
|> Seq.map (formatFileInternalAsync true)
95-
|> Async.Parallel
96-
97-
let getChangedFile =
98-
function
99-
| FormatResult.Unchanged _
100-
| FormatResult.IgnoredFile _ -> None
101-
| FormatResult.Formatted(f, _)
102-
| FormatResult.Error(f, _)
103-
| FormatResult.InvalidCode(f, _) -> Some f
104-
105-
let changes = formatted |> Seq.choose getChangedFile |> Seq.toList
106-
107-
let getErrors =
108-
function
109-
| FormatResult.Error(f, e) -> Some(f, e)
110-
| _ -> None
111-
112-
let errors = formatted |> Seq.choose getErrors |> Seq.toList
113-
114-
return { Errors = errors; Formatted = changes }
115-
}
167+
return { Errors = errors; Formatted = changes }
168+
}

src/Fantomas/Format.fsi

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,25 @@
1-
module Fantomas.Format
1+
namespace Fantomas
22

33
open System
44
open Fantomas.Core
55

6+
type ProfileInfo = { LineCount: int; TimeTaken: TimeSpan }
7+
68
type FormatResult =
7-
| Formatted of filename: string * formattedContent: string
8-
| Unchanged of filename: string
9+
| Formatted of filename: string * formattedContent: string * profileInfo: ProfileInfo option
10+
| Unchanged of filename: string * profileInfo: ProfileInfo option
911
| InvalidCode of filename: string * formattedContent: string
1012
| Error of filename: string * formattingError: Exception
1113
| IgnoredFile of filename: string
1214

13-
val formatContentAsync: (FormatConfig -> string -> string -> Async<FormatResult>)
15+
type FormatParams =
16+
{ Config: FormatConfig
17+
CompareWithoutLineEndings: bool
18+
Profile: bool
19+
File: string }
1420

15-
val formatFileAsync: (string -> Async<FormatResult>)
21+
static member Create: bool * bool * string -> FormatParams
22+
static member Create: FormatConfig * bool * bool * string -> FormatParams
1623

1724
type CheckResult =
1825
{ Errors: (string * exn) list
@@ -24,12 +31,17 @@ type CheckResult =
2431

2532
member NeedsFormatting: bool
2633

27-
/// Runs a check on the given files and reports the result to the given output:
28-
///
29-
/// * It shows the paths of the files that need formatting
30-
/// * It shows the path and the error message of files that failed the format check
31-
///
32-
/// Returns:
33-
///
34-
/// A record with the file names that were formatted and the files that encounter problems while formatting.
35-
val checkCode: filenames: seq<string> -> Async<CheckResult>
34+
module Format =
35+
val formatContentAsync: (FormatParams -> string -> Async<FormatResult>)
36+
37+
val formatFileAsync: (FormatParams -> Async<FormatResult>)
38+
39+
/// Runs a check on the given files and reports the result to the given output:
40+
///
41+
/// * It shows the paths of the files that need formatting
42+
/// * It shows the path and the error message of files that failed the format check
43+
///
44+
/// Returns:
45+
///
46+
/// A record with the file names that were formatted and the files that encounter problems while formatting.
47+
val checkCode: filenames: seq<string> -> Async<CheckResult>

0 commit comments

Comments
 (0)