-
-
Notifications
You must be signed in to change notification settings - Fork 190
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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 <florian.verdonck@outlook.com>
- Loading branch information
Showing
3 changed files
with
248 additions
and
177 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,115 +1,168 @@ | ||
module Fantomas.Format | ||
namespace Fantomas | ||
|
||
open System | ||
open System.IO | ||
open Fantomas.Core | ||
|
||
type ProfileInfo = { LineCount: int; TimeTaken: TimeSpan } | ||
|
||
type FormatResult = | ||
| Formatted of filename: string * formattedContent: string | ||
| Unchanged of filename: string | ||
| Formatted of filename: string * formattedContent: string * profileInfo: ProfileInfo option | ||
| Unchanged of filename: string * profileInfo: ProfileInfo option | ||
| InvalidCode of filename: string * formattedContent: string | ||
| Error of filename: string * formattingError: Exception | ||
| IgnoredFile of filename: string | ||
|
||
let private formatContentInternalAsync | ||
(compareWithoutLineEndings: bool) | ||
(config: FormatConfig) | ||
(file: string) | ||
(originalContent: string) | ||
: Async<FormatResult> = | ||
if IgnoreFile.isIgnoredFile (IgnoreFile.current.Force()) file then | ||
async { return IgnoredFile file } | ||
else | ||
async { | ||
try | ||
let isSignatureFile = Path.GetExtension(file) = ".fsi" | ||
type FormatParams = | ||
{ Config: FormatConfig | ||
CompareWithoutLineEndings: bool | ||
Profile: bool | ||
File: string } | ||
|
||
let! { Code = formattedContent } = | ||
CodeFormatter.FormatDocumentAsync(isSignatureFile, originalContent, config) | ||
static member Create(config: FormatConfig, compareWithoutLineEndings: bool, profile: bool, file: string) = | ||
{ Config = config | ||
CompareWithoutLineEndings = compareWithoutLineEndings | ||
Profile = profile | ||
File = file } | ||
|
||
let contentChanged = | ||
if compareWithoutLineEndings then | ||
let stripNewlines (s: string) = | ||
System.Text.RegularExpressions.Regex.Replace(s, @"\r", String.Empty) | ||
static member Create(compareWithoutLineEndings: bool, profile: bool, file: string) = | ||
{ Config = EditorConfig.readConfiguration file | ||
CompareWithoutLineEndings = compareWithoutLineEndings | ||
Profile = profile | ||
File = file } | ||
|
||
(stripNewlines originalContent) <> (stripNewlines formattedContent) | ||
else | ||
originalContent <> formattedContent | ||
type CheckResult = | ||
{ Errors: (string * exn) list | ||
Formatted: string list } | ||
|
||
if contentChanged then | ||
let! isValid = CodeFormatter.IsValidFSharpCodeAsync(isSignatureFile, formattedContent) | ||
member this.HasErrors = List.isNotEmpty this.Errors | ||
member this.NeedsFormatting = List.isNotEmpty this.Formatted | ||
member this.IsValid = List.isEmpty this.Errors && List.isEmpty this.Formatted | ||
|
||
if not isValid then | ||
return InvalidCode(filename = file, formattedContent = formattedContent) | ||
module Format = | ||
|
||
let private formatContentInternalAsync | ||
(formatParams: FormatParams) | ||
(originalContent: string) | ||
: Async<FormatResult> = | ||
if IgnoreFile.isIgnoredFile (IgnoreFile.current.Force()) formatParams.File then | ||
async { return IgnoredFile formatParams.File } | ||
else | ||
async { | ||
try | ||
let isSignatureFile = Path.GetExtension(formatParams.File) = ".fsi" | ||
|
||
let! { Code = formattedContent }, profileInfo = | ||
if formatParams.Profile then | ||
async { | ||
let sw = Diagnostics.Stopwatch.StartNew() | ||
|
||
let! res = | ||
CodeFormatter.FormatDocumentAsync( | ||
isSignatureFile, | ||
originalContent, | ||
formatParams.Config | ||
) | ||
|
||
sw.Stop() | ||
|
||
let count = | ||
originalContent.Length - originalContent.Replace(Environment.NewLine, "").Length | ||
|
||
let profileInfo = | ||
{ LineCount = count | ||
TimeTaken = sw.Elapsed } | ||
|
||
return res, Some profileInfo | ||
} | ||
else | ||
async { | ||
let! res = | ||
CodeFormatter.FormatDocumentAsync( | ||
isSignatureFile, | ||
originalContent, | ||
formatParams.Config | ||
) | ||
|
||
return res, None | ||
} | ||
|
||
let contentChanged = | ||
if formatParams.CompareWithoutLineEndings then | ||
let stripNewlines (s: string) = | ||
System.Text.RegularExpressions.Regex.Replace(s, @"\r", String.Empty) | ||
|
||
(stripNewlines originalContent) <> (stripNewlines formattedContent) | ||
else | ||
originalContent <> formattedContent | ||
|
||
if contentChanged then | ||
let! isValid = CodeFormatter.IsValidFSharpCodeAsync(isSignatureFile, formattedContent) | ||
|
||
if not isValid then | ||
return InvalidCode(filename = formatParams.File, formattedContent = formattedContent) | ||
else | ||
return | ||
Formatted( | ||
filename = formatParams.File, | ||
formattedContent = formattedContent, | ||
profileInfo = profileInfo | ||
) | ||
else | ||
return Formatted(filename = file, formattedContent = formattedContent) | ||
else | ||
return Unchanged(filename = file) | ||
with ex -> | ||
return Error(file, ex) | ||
} | ||
return Unchanged(filename = formatParams.File, profileInfo = profileInfo) | ||
with ex -> | ||
return Error(formatParams.File, ex) | ||
} | ||
|
||
let formatContentAsync = formatContentInternalAsync false | ||
let formatContentAsync = formatContentInternalAsync | ||
|
||
let private formatFileInternalAsync (compareWithoutLineEndings: bool) (file: string) = | ||
let config = EditorConfig.readConfiguration file | ||
let private formatFileInternalAsync (parms: FormatParams) = | ||
if IgnoreFile.isIgnoredFile (IgnoreFile.current.Force()) parms.File then | ||
async { return IgnoredFile parms.File } | ||
else | ||
|
||
if IgnoreFile.isIgnoredFile (IgnoreFile.current.Force()) file then | ||
async { return IgnoredFile file } | ||
else | ||
async { | ||
let! originalContent = File.ReadAllTextAsync parms.File |> Async.AwaitTask | ||
|
||
async { | ||
let! originalContent = File.ReadAllTextAsync file |> Async.AwaitTask | ||
let! formatted = originalContent |> formatContentInternalAsync parms | ||
|
||
return formatted | ||
} | ||
|
||
let formatFileAsync = formatFileInternalAsync | ||
|
||
/// Runs a check on the given files and reports the result to the given output: | ||
/// | ||
/// * It shows the paths of the files that need formatting | ||
/// * It shows the path and the error message of files that failed the format check | ||
/// | ||
/// Returns: | ||
/// | ||
/// A record with the file names that were formatted and the files that encounter problems while formatting. | ||
let checkCode (filenames: seq<string>) = | ||
async { | ||
let! formatted = | ||
originalContent | ||
|> formatContentInternalAsync compareWithoutLineEndings config file | ||
filenames | ||
|> Seq.filter (IgnoreFile.isIgnoredFile (IgnoreFile.current.Force()) >> not) | ||
|> Seq.map (fun f -> formatFileInternalAsync (FormatParams.Create(true, false, f))) | ||
|> Async.Parallel | ||
|
||
return formatted | ||
} | ||
let getChangedFile = | ||
function | ||
| FormatResult.Unchanged _ | ||
| FormatResult.IgnoredFile _ -> None | ||
| FormatResult.Formatted(f, _, _) | ||
| FormatResult.Error(f, _) | ||
| FormatResult.InvalidCode(f, _) -> Some f | ||
|
||
let formatFileAsync = formatFileInternalAsync false | ||
let changes = formatted |> Seq.choose getChangedFile |> Seq.toList | ||
|
||
type CheckResult = | ||
{ Errors: (string * exn) list | ||
Formatted: string list } | ||
let getErrors = | ||
function | ||
| FormatResult.Error(f, e) -> Some(f, e) | ||
| _ -> None | ||
|
||
member this.HasErrors = List.isNotEmpty this.Errors | ||
member this.NeedsFormatting = List.isNotEmpty this.Formatted | ||
member this.IsValid = List.isEmpty this.Errors && List.isEmpty this.Formatted | ||
let errors = formatted |> Seq.choose getErrors |> Seq.toList | ||
|
||
/// Runs a check on the given files and reports the result to the given output: | ||
/// | ||
/// * It shows the paths of the files that need formatting | ||
/// * It shows the path and the error message of files that failed the format check | ||
/// | ||
/// Returns: | ||
/// | ||
/// A record with the file names that were formatted and the files that encounter problems while formatting. | ||
let checkCode (filenames: seq<string>) = | ||
async { | ||
let! formatted = | ||
filenames | ||
|> Seq.filter (IgnoreFile.isIgnoredFile (IgnoreFile.current.Force()) >> not) | ||
|> Seq.map (formatFileInternalAsync true) | ||
|> Async.Parallel | ||
|
||
let getChangedFile = | ||
function | ||
| FormatResult.Unchanged _ | ||
| FormatResult.IgnoredFile _ -> None | ||
| FormatResult.Formatted(f, _) | ||
| FormatResult.Error(f, _) | ||
| FormatResult.InvalidCode(f, _) -> Some f | ||
|
||
let changes = formatted |> Seq.choose getChangedFile |> Seq.toList | ||
|
||
let getErrors = | ||
function | ||
| FormatResult.Error(f, e) -> Some(f, e) | ||
| _ -> None | ||
|
||
let errors = formatted |> Seq.choose getErrors |> Seq.toList | ||
|
||
return { Errors = errors; Formatted = changes } | ||
} | ||
return { Errors = errors; Formatted = changes } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.