From 1227fcaba7b647370151aa46f709a0451794033c Mon Sep 17 00:00:00 2001 From: dawe Date: Fri, 5 May 2023 09:05:01 +0200 Subject: [PATCH 01/18] Use workaround from 628 for nested modules, too (#2870) * apply workaround from 628 to nested modules to keep indentation * add define tests to be on the safe side --- CHANGELOG.md | 5 ++ .../CompilerDirectivesTests.fs | 80 +++++++++++++++++++ src/Fantomas.Core/CodePrinter.fs | 23 ++++-- 3 files changed, 101 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b4558db7a6..62f9def8db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## [Unreleased] + +### Fixed +* Incorrect indentation of compiler-conditional accessibility modifier for module definition. [#2867](https://github.com/fsprojects/fantomas/issues/2867) + ## [6.0.1] - 2023-04-19 ### Fixed diff --git a/src/Fantomas.Core.Tests/CompilerDirectivesTests.fs b/src/Fantomas.Core.Tests/CompilerDirectivesTests.fs index a908a74cab..460142e85c 100644 --- a/src/Fantomas.Core.Tests/CompilerDirectivesTests.fs +++ b/src/Fantomas.Core.Tests/CompilerDirectivesTests.fs @@ -3206,3 +3206,83 @@ fun config -> #if LOGGING_DEBUG #endif """ + +[] +let ``incorrect indentation of compiler-conditional accessibility modifier for module definition, 2867`` () = + formatSourceString + false + """ +module +#if DEBUG +#else + internal +#endif + A = + let f x = x + x +""" + config + |> prepend newline + |> should + equal + """ +module +#if DEBUG +#else + internal +#endif + A = + let f x = x + x +""" + +[] +let ``incorrect indentation of compiler-conditional accessibility modifier for module definition, no defines`` () = + formatSourceStringWithDefines + [] + """ +module +#if DEBUG +#else + internal +#endif + A = + let f x = x + x +""" + config + |> prepend newline + |> should + equal + """ +module +#if DEBUG +#else + internal +#endif + A = + let f x = x + x +""" + +[] +let ``incorrect indentation of compiler-conditional accessibility modifier for module definition, DEBUG`` () = + formatSourceStringWithDefines + [ "DEBUG" ] + """ +module +#if DEBUG +#else + internal +#endif + A = + let f x = x + x +""" + config + |> prepend newline + |> should + equal + """ +module +#if DEBUG +#else +#endif + A = + let f x = x + x +""" diff --git a/src/Fantomas.Core/CodePrinter.fs b/src/Fantomas.Core/CodePrinter.fs index 67ea9875af..ef787fa2a0 100644 --- a/src/Fantomas.Core/CodePrinter.fs +++ b/src/Fantomas.Core/CodePrinter.fs @@ -3203,6 +3203,15 @@ let genImplicitConstructor (node: ImplicitConstructorNode) = +> sepSpace) node.Self +let hasTriviaAfterLeadingKeyword (identifier: IdentListNode) (accessibility: SingleTextNode option) = + let beforeAccess = + match accessibility with + | Some n -> n.HasContentBefore + | _ -> false + + let beforeIdentifier = identifier.HasContentBefore + beforeAccess || beforeIdentifier + let genTypeDefn (td: TypeDefn) = let typeDefnNode = TypeDefn.TypeDefnNode td let typeName = typeDefnNode.TypeName @@ -3213,13 +3222,7 @@ let genTypeDefn (td: TypeDefn) = // Workaround for https://github.com/fsprojects/fantomas/issues/628 let hasTriviaAfterLeadingKeyword = - let beforeAccess = - match typeName.Accessibility with - | Some n -> n.HasContentBefore - | _ -> false - - let beforeIdentifier = typeName.Identifier.HasContentBefore - beforeAccess || beforeIdentifier + hasTriviaAfterLeadingKeyword typeName.Identifier typeName.Accessibility genXml typeName.XmlDoc +> onlyIfNot hasAndKeyword (genAttributes typeName.Attributes) @@ -3712,13 +3715,19 @@ let genModuleDecl (md: ModuleDecl) = +> genIdentListNode node.Alias |> genNode (ModuleDecl.Node md) | ModuleDecl.NestedModule node -> + // Workaround for https://github.com/fsprojects/fantomas/issues/2867 + let hasTriviaAfterLeadingKeyword = + hasTriviaAfterLeadingKeyword node.Identifier node.Accessibility + genXml node.XmlDoc +> genAttributes node.Attributes +> genSingleTextNode node.Module +> sepSpace + +> onlyIf hasTriviaAfterLeadingKeyword indent +> genAccessOpt node.Accessibility +> onlyIf node.IsRecursive (sepSpace +> !- "rec" +> sepSpace) +> genIdentListNode node.Identifier + +> onlyIf hasTriviaAfterLeadingKeyword unindent +> sepSpace +> genSingleTextNode node.Equals +> indentSepNlnUnindent ( From 27a224de6e7a6d0401739738d1bcb8c67a355b64 Mon Sep 17 00:00:00 2001 From: Florian Verdonck Date: Fri, 5 May 2023 10:12:13 +0200 Subject: [PATCH 02/18] Automate releases (#2872) * WIP: See what CI says. * Create draft GitHub release. * Combine NuGet push and GitHub release. * Remove extracted getNuGetPackages function. * Use GitHub last publish date. --- .github/workflows/main.yml | 8 +- build.fsx | 181 ++++++++++++++++++++++++++++++++++--- 2 files changed, 175 insertions(+), 14 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c00c78407b..d5cf1505f0 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -32,4 +32,10 @@ jobs: uses: peaceiris/actions-gh-pages@v3 with: github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ./output \ No newline at end of file + publish_dir: ./output + - name: "Publish" + if: matrix.os == 'windows-latest' && github.ref == 'refs/heads/main' + env: + NUGET_KEY: ${{ secrets.NUGET_KEY }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: dotnet fsi build.fsx -p Release diff --git a/build.fsx b/build.fsx index 12d5e8237e..3a5addcfec 100644 --- a/build.fsx +++ b/build.fsx @@ -1,7 +1,10 @@ #r "nuget: Fun.Build, 0.3.8" #r "nuget: CliWrap, 3.5.0" #r "nuget: FSharp.Data, 5.0.2" +#r "nuget: Ionide.KeepAChangelog, 0.1.8" +#r "nuget: Humanizer.Core, 2.14.1" +open System open System.IO open Fun.Build open CliWrap @@ -9,6 +12,10 @@ open CliWrap.Buffered open FSharp.Data open System.Xml.Linq open System.Xml.XPath +open Ionide.KeepAChangelog +open Ionide.KeepAChangelog.Domain +open SemVersion +open Humanizer let () a b = Path.Combine(a, b) @@ -39,7 +46,7 @@ let semanticVersioning = let pushPackage nupkg = async { - let key = System.Environment.GetEnvironmentVariable("NUGET_KEY") + let key = Environment.GetEnvironmentVariable("NUGET_KEY") let! result = Cli .Wrap("dotnet") @@ -77,18 +84,6 @@ pipeline "Build" { run $"dotnet fsdocs build --clean --properties Configuration=Release --fscoptions \" -r:{semanticVersioning}\" --eval --strict --nonpublic" } - stage "Push" { - whenCmdArg "--push" - run (fun _ -> - async { - let! exitCodes = - Directory.EnumerateFiles("bin", "*.nupkg", SearchOption.TopDirectoryOnly) - |> Seq.filter (fun nupkg -> not (nupkg.Contains("Fantomas.Client"))) - |> Seq.map pushPackage - |> Async.Sequential - return Seq.max exitCodes - }) - } runIfOnlySpecified false } @@ -313,3 +308,163 @@ pipeline "Init" { } runIfOnlySpecified true } + +type GithubRelease = + { Version: string + Title: string + Date: DateTime + PublishedDate: string option + Draft: string } + +let mkGithubRelease (v: SemanticVersion, d: DateTime, cd: ChangelogData option) = + match cd with + | None -> failwith "Each Fantomas release is expected to have at least one section." + | Some cd -> + let version = $"{v.Major}.{v.Minor}.{v.Patch}" + let title = + let month = d.ToString("MMMM") + let day = d.Day.Ordinalize() + $"{month} {day} Release" + + let prefixedVersion = $"v{version}" + let publishDate = + let cmdResult = + Cli + .Wrap("gh") + .WithArguments($"release view {prefixedVersion} --json publishedAt -t \"{{{{.publishedAt}}}}\"") + .WithValidation(CommandResultValidation.None) + .ExecuteBufferedAsync() + .Task.Result + if cmdResult.ExitCode <> 0 then + None + else + let output = cmdResult.StandardOutput.Trim() + let lastIdx = output.LastIndexOf("Z") + Some(output.Substring(0, lastIdx)) + + let sections = + [ "Added", cd.Added + "Changed", cd.Changed + "Fixed", cd.Fixed + "Deprecated", cd.Deprecated + "Removed", cd.Removed + "Security", cd.Security + yield! (Map.toList cd.Custom) ] + |> List.choose (fun (header, lines) -> + if lines.IsEmpty then + None + else + lines + |> List.map (fun line -> line.TrimStart()) + |> String.concat "\n" + |> sprintf "### %s\n%s" header + |> Some) + |> String.concat "\n\n" + + let draft = + $"""# {version} + +{sections}""" + + { Version = version + Title = title + Date = d + PublishedDate = publishDate + Draft = draft } + +let getReleaseNotes currentRelease (lastRelease: GithubRelease) = + let date = lastRelease.PublishedDate.Value + let authorMsg = + let authors = + Cli + .Wrap("gh") + .WithArguments( + $"pr list -S \"state:closed base:main closed:>{date} -author:app/robot\" --json author --jq \".[].author.login\"" + ) + .ExecuteBufferedAsync() + .Task.Result.StandardOutput.Split([| '\n' |], StringSplitOptions.RemoveEmptyEntries) + |> Array.distinct + |> Array.sort + + if authors.Length = 1 then + $"Special thanks to %s{authors.[0]}!" + else + let lastAuthor = Array.last authors + let otherAuthors = + if authors.Length = 2 then + $"@{authors.[0]}" + else + authors + |> Array.take (authors.Length - 1) + |> Array.map (sprintf "@%s") + |> String.concat ", " + $"Special thanks to %s{otherAuthors} and @%s{lastAuthor}!" + + $"""{currentRelease.Draft} + +{authorMsg} + +[https://www.nuget.org/packages/fantomas/{currentRelease.Version}](https://www.nuget.org/packages/fantomas/{currentRelease.Version}) + """ + +let getCurrentAndLastReleaseFromChangelog () = + let changelog = FileInfo(__SOURCE_DIRECTORY__ "CHANGELOG.md") + let changeLogResult = + match Parser.parseChangeLog changelog with + | Error error -> failwithf "%A" error + | Ok result -> result + + let lastReleases = + changeLogResult.Releases + |> List.filter (fun (v, _, _) -> String.IsNullOrEmpty v.Prerelease) + |> List.sortByDescending (fun (_, d, _) -> d) + |> List.take 2 + + match lastReleases with + | [ current; last ] -> mkGithubRelease current, mkGithubRelease last + | _ -> failwith "Could not find the current and last release from CHANGELOG.md" + +pipeline "Release" { + workingDir __SOURCE_DIRECTORY__ + stage "Release" { + run (fun _ -> + async { + let currentRelease, lastRelease = getCurrentAndLastReleaseFromChangelog () + + if Option.isSome currentRelease.PublishedDate then + return 0 + else + // Push packages to NuGet + let nugetPackages = + Directory.EnumerateFiles("bin", "*.nupkg", SearchOption.TopDirectoryOnly) + |> Seq.filter (fun nupkg -> not (nupkg.Contains("Fantomas.Client"))) + |> Seq.toArray + + let! nugetExitCodes = nugetPackages |> Array.map pushPackage |> Async.Sequential + + let notes = getReleaseNotes currentRelease lastRelease + let noteFile = Path.GetTempFileName() + File.WriteAllText(noteFile, notes) + let files = nugetPackages |> String.concat " " + + // We create a draft release that requires a manual publish. + // This is to allow us to add additional release notes when it makes sense. + let! draftResult = + Cli + .Wrap("gh") + .WithArguments( + $"release create v{currentRelease.Version} {files} --draft --title \"{currentRelease.Title}\" --notes-file \"{noteFile}\"" + ) + .WithValidation(CommandResultValidation.None) + .ExecuteAsync() + .Task + |> Async.AwaitTask + + if File.Exists noteFile then + File.Delete(noteFile) + + return Seq.max [| yield! nugetExitCodes; yield draftResult.ExitCode |] + }) + } + runIfOnlySpecified true +} From 4491b9339812c26bece154319ac881f1d52a5a5c Mon Sep 17 00:00:00 2001 From: Josh DeGraw <18509575+josh-degraw@users.noreply.github.com> Date: Fri, 5 May 2023 02:31:53 -0600 Subject: [PATCH 03/18] Fix stroustrup formatting issue with trivia (#2873) * Fix stroustrup formatting issue with trivia * Update changelog --------- Co-authored-by: Florian Verdonck --- CHANGELOG.md | 3 ++- .../SynTypeDefnSimpleReprRecordTests.fs | 27 +++++++++++++++++-- src/Fantomas.Core/CodePrinter.fs | 2 +- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 62f9def8db..f4223ed0cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,10 @@ # Changelog -## [Unreleased] +## [6.0.2] - 2023-05-05 ### Fixed * Incorrect indentation of compiler-conditional accessibility modifier for module definition. [#2867](https://github.com/fsprojects/fantomas/issues/2867) +* Comment above the record bracket beaks formatting when MultilineBracketStyle is Stroustrup. [#2871](https://github.com/fsprojects/fantomas/issues/2871) ## [6.0.1] - 2023-04-19 diff --git a/src/Fantomas.Core.Tests/Stroustrup/SynTypeDefnSimpleReprRecordTests.fs b/src/Fantomas.Core.Tests/Stroustrup/SynTypeDefnSimpleReprRecordTests.fs index 03653f643b..2cfe501e3e 100644 --- a/src/Fantomas.Core.Tests/Stroustrup/SynTypeDefnSimpleReprRecordTests.fs +++ b/src/Fantomas.Core.Tests/Stroustrup/SynTypeDefnSimpleReprRecordTests.fs @@ -296,7 +296,7 @@ type MangaDexAtHomeResponse = { """ [] -let ``record interface declarations can break with Stroustrup enabled, 2787 `` () = +let ``record interface declarations can break with Stroustrup enabled, 2787`` () = formatSourceString false """ @@ -329,7 +329,7 @@ type UpdatedName = { PreviousName: string } """ [] -let ``record member declarations can break with Stroustrup enabled, 2787 `` () = +let ``record member declarations can break with Stroustrup enabled, 2787`` () = formatSourceString false """ @@ -354,3 +354,26 @@ type SomeEvent = { type UpdatedName = { PreviousName: string } """ + +[] +let ``comment above record bracket breaks formatting when Stroustrup enabled, 2871`` () = + formatSourceString + false + """ +type Event = + // TODO: Add LulaSafe conclusion and scores per assessment + { + Metadata: AssessmentMetadata + } +""" + config + |> prepend newline + |> should + equal + """ +type Event = + // TODO: Add LulaSafe conclusion and scores per assessment + { + Metadata: AssessmentMetadata + } +""" diff --git a/src/Fantomas.Core/CodePrinter.fs b/src/Fantomas.Core/CodePrinter.fs index ef787fa2a0..c1ea86dbff 100644 --- a/src/Fantomas.Core/CodePrinter.fs +++ b/src/Fantomas.Core/CodePrinter.fs @@ -3375,7 +3375,7 @@ let genTypeDefn (td: TypeDefn) = let size = getRecordSize ctx node.Fields let short = bodyExpr size - if ctx.Config.IsStroustrupStyle then + if ctx.Config.IsStroustrupStyle && not node.OpeningBrace.HasContentBefore then (sepSpace +> short) ctx else isSmallExpression size short (indentSepNlnUnindent short) ctx From 4093fa59701158aa447af923a84dc2bbcf416941 Mon Sep 17 00:00:00 2001 From: Florian Verdonck Date: Fri, 5 May 2023 11:52:15 +0200 Subject: [PATCH 04/18] Don't create a draft for revisions. (#2874) --- build.fsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/build.fsx b/build.fsx index 3a5addcfec..cfa72284da 100644 --- a/build.fsx +++ b/build.fsx @@ -447,13 +447,17 @@ pipeline "Release" { File.WriteAllText(noteFile, notes) let files = nugetPackages |> String.concat " " - // We create a draft release that requires a manual publish. + // We create a draft release for minor and majors. Those that requires a manual publish. // This is to allow us to add additional release notes when it makes sense. let! draftResult = + let isDraft = + let isRevision = lastRelease.Version.Split('.') |> Array.last |> int |> (<>) 0 + if isRevision then String.Empty else "--draft" + Cli .Wrap("gh") .WithArguments( - $"release create v{currentRelease.Version} {files} --draft --title \"{currentRelease.Title}\" --notes-file \"{noteFile}\"" + $"release create v{currentRelease.Version} {files} {isDraft} --title \"{currentRelease.Title}\" --notes-file \"{noteFile}\"" ) .WithValidation(CommandResultValidation.None) .ExecuteAsync() From 6f3dbd665eb5202eb440c570291b573dc7085a38 Mon Sep 17 00:00:00 2001 From: JohSand Date: Wed, 10 May 2023 22:21:45 +0200 Subject: [PATCH 05/18] Fix mkSynTypar to account for quotes around type parameter (#2876) --- CHANGELOG.md | 5 ++++ .../FunctionDefinitionTests.fs | 30 +++++++++++++++++++ src/Fantomas.Core.Tests/SignatureTests.fs | 14 +++++++++ src/Fantomas.Core/ASTTransformer.fs | 12 ++++++-- 4 files changed, 59 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f4223ed0cc..dd96eab8bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## [Unreleased] + +### Fixed +* Preserves quotes around type parameter names. [#2875](https://github.com/fsprojects/fantomas/issues/2875) + ## [6.0.2] - 2023-05-05 ### Fixed diff --git a/src/Fantomas.Core.Tests/FunctionDefinitionTests.fs b/src/Fantomas.Core.Tests/FunctionDefinitionTests.fs index 404295c913..e521cc4103 100644 --- a/src/Fantomas.Core.Tests/FunctionDefinitionTests.fs +++ b/src/Fantomas.Core.Tests/FunctionDefinitionTests.fs @@ -1323,3 +1323,33 @@ let a : int * string * bool = 1, "", false """ let a: int * string * bool = 1, "", false """ + +[] +let ``should preserve quotes around type parameters, 2875`` () = + formatSourceString + false + """ +let repro (a: '``QuotedWithIllegalChar<'T>``) = () +""" + config + |> prepend newline + |> should + equal + """ +let repro (a: '``QuotedWithIllegalChar<'T>``) = () +""" + +[] +let ``should preserve quotes around statically resolved type parameters`` () = + formatSourceString + false + """ +let inline repro (a: ^``QuotedWithIllegalChar<'T>``) = () +""" + config + |> prepend newline + |> should + equal + """ +let inline repro (a: ^``QuotedWithIllegalChar<'T>``) = () +""" diff --git a/src/Fantomas.Core.Tests/SignatureTests.fs b/src/Fantomas.Core.Tests/SignatureTests.fs index 8d9490011b..a4ad702af8 100644 --- a/src/Fantomas.Core.Tests/SignatureTests.fs +++ b/src/Fantomas.Core.Tests/SignatureTests.fs @@ -2277,3 +2277,17 @@ type ILMethodDef = metadataIndex: int32 -> ILMethodDef """ + +let ``should preserve quotes around type parameters, 2875`` () = + formatSourceString + true + """ +val repro: '``QuotedWithIllegalChar<'T>`` -> unit +""" + config + |> prepend newline + |> should + equal + """ +val repro: '``QuotedWithIllegalChar<'T>`` -> unit +""" diff --git a/src/Fantomas.Core/ASTTransformer.fs b/src/Fantomas.Core/ASTTransformer.fs index 0f8ab2e499..bd0dea112a 100644 --- a/src/Fantomas.Core/ASTTransformer.fs +++ b/src/Fantomas.Core/ASTTransformer.fs @@ -1980,9 +1980,17 @@ let mkSynTypar (SynTypar(ident, req, _)) = (Position.mkPos ident.idRange.StartLine (ident.idRange.StartColumn - 1)) ident.idRange.End + let identText = + let width = ident.idRange.EndColumn - ident.idRange.StartColumn + // 5 because of ^ or ' and `` on each side + if ident.idText.Length + 5 = width then + $"``{ident.idText}``" + else + ident.idText + match req with - | TyparStaticReq.None -> stn $"'{ident}" range - | TyparStaticReq.HeadType -> stn $"^{ident.idText}" range + | TyparStaticReq.None -> stn $"'{identText}" range + | TyparStaticReq.HeadType -> stn $"^{identText}" range let mkSynTypeConstraint (creationAide: CreationAide) (tc: SynTypeConstraint) : TypeConstraint = match tc with From 680ec8bdf414e0af14d7b1a9e140ae740672d177 Mon Sep 17 00:00:00 2001 From: Florian Verdonck Date: Sat, 13 May 2023 15:30:51 +0200 Subject: [PATCH 06/18] Use signature files for *.fsy files. (#2879) --- src/Fantomas.FCS/Fantomas.FCS.fsproj | 17 +++++++++++++---- src/Fantomas.FCS/packages.lock.json | 12 ++++++------ 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/Fantomas.FCS/Fantomas.FCS.fsproj b/src/Fantomas.FCS/Fantomas.FCS.fsproj index b1eb774d92..4005c8a660 100644 --- a/src/Fantomas.FCS/Fantomas.FCS.fsproj +++ b/src/Fantomas.FCS/Fantomas.FCS.fsproj @@ -181,7 +181,7 @@ AbstractIL\illex.fsl - --module FSharp.Compiler.AbstractIL.AsciiParser --open FSharp.Compiler.AbstractIL --internal --lexlib Internal.Utilities.Text.Lexing --parslib Internal.Utilities.Text.Parsing + --module FSharp.Compiler.AbstractIL.AsciiParser --open FSharp.Compiler.AbstractIL --open FSharp.Compiler.AbstractIL.AsciiConstants --open FSharp.Compiler.AbstractIL.IL --internal --lexlib Internal.Utilities.Text.Lexing --parslib Internal.Utilities.Text.Parsing --buffer-type-argument char AbstractIL\ilpars.fsy @@ -205,6 +205,9 @@ AbstractIL\ilascii.fs + + AbstractIL\FsYaccOut\ilpars.fsi + AbstractIL\FsYaccOut\ilpars.fs @@ -222,7 +225,7 @@ SyntaxTree\pplex.fsl - --module FSharp.Compiler.PPParser --open FSharp.Compiler --open FSharp.Compiler.Syntax --internal --lexlib Internal.Utilities.Text.Lexing --parslib Internal.Utilities.Text.Parsing + --module FSharp.Compiler.PPParser --open FSharp.Compiler --open FSharp.Compiler.Syntax --open FSharp.Compiler.ParseHelpers --internal --lexlib Internal.Utilities.Text.Lexing --parslib Internal.Utilities.Text.Parsing --buffer-type-argument char SyntaxTree\pppars.fsy @@ -230,7 +233,7 @@ SyntaxTree\lex.fsl - -v --module FSharp.Compiler.Parser --open FSharp.Compiler --open FSharp.Compiler.Syntax --internal --lexlib Internal.Utilities.Text.Lexing --parslib Internal.Utilities.Text.Parsing + -v --module FSharp.Compiler.Parser --open FSharp.Compiler --open FSharp.Compiler.Syntax --open FSharp.Compiler.Text --internal --lexlib Internal.Utilities.Text.Lexing --parslib Internal.Utilities.Text.Parsing --buffer-type-argument char SyntaxTree\pars.fsy @@ -281,9 +284,15 @@ SyntaxTree\ParseHelpers.fs + + SyntaxTree\FsYaccOutput\pppars.fsi + SyntaxTree\FsYaccOutput\pppars.fs + + SyntaxTree\FsYaccOutput\pars.fsi + SyntaxTree\FsYaccOutput\pars.fs @@ -313,7 +322,7 @@ - + diff --git a/src/Fantomas.FCS/packages.lock.json b/src/Fantomas.FCS/packages.lock.json index e462ff44f1..9b730c85df 100644 --- a/src/Fantomas.FCS/packages.lock.json +++ b/src/Fantomas.FCS/packages.lock.json @@ -22,12 +22,12 @@ }, "FsLexYacc": { "type": "Direct", - "requested": "[11.0.1, )", - "resolved": "11.0.1", - "contentHash": "2ra352oQaLFe67FisXkTeVTdlX96pOfI5zz1MvJeBwdgqGDSmVT46nM1ugUxWXts5u3dx7FGKTdZDoRfnwFAgw==", + "requested": "[11.1.0, )", + "resolved": "11.1.0", + "contentHash": "JoGbIVZVuJs4CsCED0Wi7Winhf6isw0qrblm4hNAdpaZX3Sp5EmPZa07Nhv6SVmoDNPP/HfjCBVtFbaxyzOD2w==", "dependencies": { "FSharp.Core": "4.6.2", - "FsLexYacc.Runtime": "11.0.1" + "FsLexYacc.Runtime": "11.1.0" } }, "Ionide.KeepAChangelog.Tasks": { @@ -78,8 +78,8 @@ }, "FsLexYacc.Runtime": { "type": "Transitive", - "resolved": "11.0.1", - "contentHash": "6fZlgcVHPJBAB3MGJpGpJIhHFM87LDulG0hQA0pZicr3fKUrq6TPd2UNdRO02U7PEAvAMHE2qmyPiEll7mgILg==", + "resolved": "11.1.0", + "contentHash": "NtCnm45VhoJKyz2Ie8OzSSUgrUYQ0TKp2LbgEBeEOr7j6+/0om3lf7NmmoDZDjuNx/KRXb95ZMDjGZRZD92ZfA==", "dependencies": { "FSharp.Core": "4.6.2" } From 25b6f95b1e17b0e54e4c8fe807ee3d748043d5b6 Mon Sep 17 00:00:00 2001 From: Florian Verdonck Date: Sun, 14 May 2023 21:53:12 +0200 Subject: [PATCH 07/18] Take commas into account (#2880) * Update FCS to e267bb9f8d590feed1b94b469d78cfce61afecad. * Use commas as nodes in PatTupleNode. * Respect commas in SynSimplePats.SimplePats. * Pick correct comma in pattern in mkPropertyGetSetBinding. * Update src/Fantomas.Core.Tests/TypeDeclarationTests.fs Co-authored-by: dawe * 6.0.3 release --------- Co-authored-by: dawe --- CHANGELOG.md | 7 +- Directory.Build.props | 2 +- src/Fantomas.Core.Tests/CommentTests.fs | 6 +- .../CompilerDirectivesTests.fs | 36 ++++++ src/Fantomas.Core.Tests/LetBindingTests.fs | 25 ++++ .../TypeDeclarationTests.fs | 33 ++++++ src/Fantomas.Core/ASTTransformer.fs | 107 +++++++++++------- src/Fantomas.Core/CodePrinter.fs | 49 +++++--- src/Fantomas.Core/SyntaxOak.fs | 20 +++- 9 files changed, 218 insertions(+), 67 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dd96eab8bb..f470d52d18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,14 @@ # Changelog -## [Unreleased] +## [6.0.3] - 2023-05-14 ### Fixed * Preserves quotes around type parameter names. [#2875](https://github.com/fsprojects/fantomas/issues/2875) +* Additional whitespace for LineCommentAfterSourceCode when last character is a `,`. [#2589](https://github.com/fsprojects/fantomas/issues/2589) +* Tupled parameter wrapped in conditional directive. [#2877](https://github.com/fsprojects/fantomas/issues/2877) + +### Changed +* Update FCS to 'Add commas to tuple pat and simple pats', commit e267bb9f8d590feed1b94b469d78cfce61afecad ## [6.0.2] - 2023-05-05 diff --git a/Directory.Build.props b/Directory.Build.props index e9a6b4ab0f..eb91c9c62f 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -39,7 +39,7 @@ Some common use cases include: - ba6647ebf5b94823c4d6fafd1e7d5f806d915ee0 + e267bb9f8d590feed1b94b469d78cfce61afecad 2.8.28 6.0.1 diff --git a/src/Fantomas.Core.Tests/CommentTests.fs b/src/Fantomas.Core.Tests/CommentTests.fs index 2448ca0300..996c811baa 100644 --- a/src/Fantomas.Core.Tests/CommentTests.fs +++ b/src/Fantomas.Core.Tests/CommentTests.fs @@ -2080,8 +2080,8 @@ type CreateFSharpManifestResourceName public () = ( fileName: string, linkFileName: string, - rootNamespace: string, // may be null - dependentUponFileName: string, // may be null + rootNamespace: string, // may be null + dependentUponFileName: string, // may be null binaryStream: Stream // may be null ) : string = () @@ -2570,7 +2570,7 @@ type MyType2 = type MyType = member _.MyMethod ( - [] inputA: string, // my comment 1 + [] inputA: string, // my comment 1 [] inputB: string // my comment 2 ) = inputA diff --git a/src/Fantomas.Core.Tests/CompilerDirectivesTests.fs b/src/Fantomas.Core.Tests/CompilerDirectivesTests.fs index 460142e85c..a678882456 100644 --- a/src/Fantomas.Core.Tests/CompilerDirectivesTests.fs +++ b/src/Fantomas.Core.Tests/CompilerDirectivesTests.fs @@ -3286,3 +3286,39 @@ module A = let f x = x + x """ + +[] +let ``conditional directives around last tuple pattern, 2877`` () = + formatSourceString + false + """ +// Link all the assemblies together and produce the input typecheck accumulator +let CombineImportedAssembliesTask + ( + a, + b +#if !NO_TYPEPROVIDERS + , c +#endif + ) = + + () +""" + config + |> prepend newline + |> should + equal + """ +// Link all the assemblies together and produce the input typecheck accumulator +let CombineImportedAssembliesTask + ( + a, + b +#if !NO_TYPEPROVIDERS + , + c +#endif + ) = + + () +""" diff --git a/src/Fantomas.Core.Tests/LetBindingTests.fs b/src/Fantomas.Core.Tests/LetBindingTests.fs index 3c21233243..dba78cf6d6 100644 --- a/src/Fantomas.Core.Tests/LetBindingTests.fs +++ b/src/Fantomas.Core.Tests/LetBindingTests.fs @@ -2140,3 +2140,28 @@ let inline (!!) (x: ^a) : ^b = ((^a or ^b): (static member op_Implicit: ^a -> ^b let inline (!!) (x: ^a) : ^b = ((^a or ^b): (static member op_Implicit: ^a -> ^b) x) """ + +[] +let ``avoid additional whitespace after comma, 2589`` () = + formatSourceString + false + """ +let x + ( + a: string, // test + b: string // test + ) = + print "hello" +""" + config + |> prepend newline + |> should + equal + """ +let x + ( + a: string, // test + b: string // test + ) = + print "hello" +""" diff --git a/src/Fantomas.Core.Tests/TypeDeclarationTests.fs b/src/Fantomas.Core.Tests/TypeDeclarationTests.fs index 97f2baf403..979052c77a 100644 --- a/src/Fantomas.Core.Tests/TypeDeclarationTests.fs +++ b/src/Fantomas.Core.Tests/TypeDeclarationTests.fs @@ -3587,3 +3587,36 @@ type ArrayBuffer = abstract byteLength: int abstract slice: ``begin``: int * ?``end``: int -> ArrayBuffer """ + +[] +let ``trivia before comma in primary constructor`` () = + formatSourceString + false + """ +type Meh + ( + a, + b +#if !NO_TYPEPROVIDERS + , c +#endif + ) = + class end +""" + config + |> prepend newline + |> should + equal + """ +type Meh + ( + a, + b +#if !NO_TYPEPROVIDERS + , + c +#endif + ) = + class + end +""" diff --git a/src/Fantomas.Core/ASTTransformer.fs b/src/Fantomas.Core/ASTTransformer.fs index bd0dea112a..09cedff756 100644 --- a/src/Fantomas.Core/ASTTransformer.fs +++ b/src/Fantomas.Core/ASTTransformer.fs @@ -1573,6 +1573,18 @@ let (|PatParameter|_|) (p: SynPat) = let mkUnit (StartEndRange 1 (lpr, m, rpr)) = UnitNode(stn "(" lpr, stn ")" rpr, m) +let mkTuplePat (creationAide: CreationAide) (pats: SynPat list) (commas: range list) (m: range) = + match pats with + | [] -> failwith "SynPat.Tuple with no elements" + | head :: tail -> + let rest = + assert (tail.Length = commas.Length) + + List.zip commas tail + |> List.collect (fun (c, e) -> [ yield Choice2Of2(stn "," c); yield Choice1Of2(mkPat creationAide e) ]) + + PatTupleNode([ yield Choice1Of2(mkPat creationAide head); yield! rest ], m) + let mkPat (creationAide: CreationAide) (p: SynPat) = let patternRange = p.Range @@ -1650,8 +1662,8 @@ let mkPat (creationAide: CreationAide) (p: SynPat) = | SynPat.Paren(p, StartEndRange 1 (lpr, _, rpr)) -> PatParenNode(stn "(" lpr, mkPat creationAide p, stn ")" rpr, patternRange) |> Pattern.Paren - | SynPat.Tuple(false, ps, _) -> PatTupleNode(List.map (mkPat creationAide) ps, patternRange) |> Pattern.Tuple - | SynPat.Tuple(true, ps, _) -> + | SynPat.Tuple(false, ps, commas, _) -> mkTuplePat creationAide ps commas patternRange |> Pattern.Tuple + | SynPat.Tuple(true, ps, _, _) -> PatStructTupleNode(List.map (mkPat creationAide) ps, patternRange) |> Pattern.StructTuple | SynPat.ArrayOrList(isArray, ps, range) -> @@ -1862,7 +1874,8 @@ let mkExternBinding let identifier, openNode, parameters, closeNode = match pat with | SynPat.LongIdent( - longDotId = longDotId; argPats = SynArgPats.Pats [ SynPat.Tuple(_, ps, StartEndRange 1 (mOpen, _, mClose)) ]) -> + longDotId = longDotId + argPats = SynArgPats.Pats [ SynPat.Tuple(_, ps, _, StartEndRange 1 (mOpen, _, mClose)) ]) -> mkSynLongIdent longDotId, stn "(" mOpen, List.map mkExternPat ps, stn ")" mClose | _ -> failwith "expecting a SynPat.LongIdent for extern binding" @@ -2255,6 +2268,28 @@ let mkSynUnionCase fullRange ) +let mkSynSimplePat creationAide (pat: SynSimplePat) = + match pat with + | SynSimplePat.Attrib(SynSimplePat.Typed(SynSimplePat.Id(ident = ident; isOptional = isOptional), t, _), + attributes, + m) -> + Some( + SimplePatNode( + mkAttributes creationAide attributes, + isOptional, + mkIdent ident, + Some(mkType creationAide t), + m + ) + ) + | SynSimplePat.Typed(SynSimplePat.Id(ident = ident; isOptional = isOptional), t, m) -> + Some(SimplePatNode(mkAttributes creationAide [], isOptional, mkIdent ident, Some(mkType creationAide t), m)) + | SynSimplePat.Attrib(SynSimplePat.Id(ident = ident; isOptional = isOptional), attributes, m) -> + Some(SimplePatNode(mkAttributes creationAide attributes, isOptional, mkIdent ident, None, m)) + | SynSimplePat.Id(ident = ident; isOptional = isOptional; range = m) -> + Some(SimplePatNode(mkAttributes creationAide [], isOptional, mkIdent ident, None, m)) + | _ -> None + let mkImplicitCtor creationAide vis @@ -2263,43 +2298,30 @@ let mkImplicitCtor (self: (range * Ident) option) (xmlDoc: PreXmlDoc) = - let openNode, closeNode = + let openNode, pats, commas, closeNode = match pats with - | SynSimplePats.SimplePats(range = StartEndRange 1 (mOpen, _, mClose)) - | SynSimplePats.Typed(range = StartEndRange 1 (mOpen, _, mClose)) -> stn "(" mOpen, stn ")" mClose + | SynSimplePats.SimplePats(pats = pats; commaRanges = commas; range = StartEndRange 1 (mOpen, _, mClose)) -> + stn "(" mOpen, pats, commas, stn ")" mClose let pats = match pats with - | SynSimplePats.SimplePats(pats = pats) -> pats - | SynSimplePats.Typed _ -> [] - |> List.choose (function - | SynSimplePat.Attrib(SynSimplePat.Typed(SynSimplePat.Id(ident = ident; isOptional = isOptional), t, _), - attributes, - m) -> - Some( - SimplePatNode( - mkAttributes creationAide attributes, - isOptional, - mkIdent ident, - Some(mkType creationAide t), - m - ) - ) - | SynSimplePat.Typed(SynSimplePat.Id(ident = ident; isOptional = isOptional), t, m) -> - Some( - SimplePatNode( - mkAttributes creationAide [], - isOptional, - mkIdent ident, - Some(mkType creationAide t), - m - ) - ) - | SynSimplePat.Attrib(SynSimplePat.Id(ident = ident; isOptional = isOptional), attributes, m) -> - Some(SimplePatNode(mkAttributes creationAide attributes, isOptional, mkIdent ident, None, m)) - | SynSimplePat.Id(ident = ident; isOptional = isOptional; range = m) -> - Some(SimplePatNode(mkAttributes creationAide [], isOptional, mkIdent ident, None, m)) - | _ -> None) + | [] -> + // Unit pattern + [] + | head :: tail -> + let rest = + assert (tail.Length = commas.Length) + + List.zip commas tail + |> List.collect (fun (c, p) -> + match mkSynSimplePat creationAide p with + | None -> [] + | Some simplePat -> [ Choice2Of2(stn "," c); Choice1Of2 simplePat ]) + + [ match mkSynSimplePat creationAide head with + | None -> () + | Some simplePat -> yield Choice1Of2 simplePat + yield! rest ] let range = let startRange = @@ -2540,18 +2562,25 @@ let mkPropertyGetSetBinding let pats = match ps with - | [ SynPat.Tuple(false, [ p1; p2; p3 ], _) ] -> + | [ SynPat.Tuple(false, [ p1; p2; p3 ], [ comma ], _) ] -> let mTuple = unionRanges p1.Range p2.Range [ PatParenNode( stn "(" Range.Zero, - Pattern.Tuple(PatTupleNode([ mkPat creationAide p1; mkPat creationAide p2 ], mTuple)), + Pattern.Tuple( + PatTupleNode( + [ Choice1Of2(mkPat creationAide p1) + Choice2Of2(stn "," comma) + Choice1Of2(mkPat creationAide p2) ], + mTuple + ) + ), stn ")" Range.Zero, mTuple ) |> Pattern.Paren mkPat creationAide p3 ] - | [ SynPat.Tuple(false, [ p1; p2 ], _) ] -> [ mkPat creationAide p1; mkPat creationAide p2 ] + | [ SynPat.Tuple(false, [ p1; p2 ], _, _) ] -> [ mkPat creationAide p1; mkPat creationAide p2 ] | ps -> List.map (mkPat creationAide) ps let range = unionRanges extraIdent.idRange e.Range diff --git a/src/Fantomas.Core/CodePrinter.fs b/src/Fantomas.Core/CodePrinter.fs index c1ea86dbff..8b762e8139 100644 --- a/src/Fantomas.Core/CodePrinter.fs +++ b/src/Fantomas.Core/CodePrinter.fs @@ -398,12 +398,12 @@ let genExpr (e: Expr) = +> sepSpace +> genExpr node.Arguments |> genNode node - | Expr.Tuple node -> genTuple node + | Expr.Tuple node -> genTupleExpr node | Expr.StructTuple node -> genSingleTextNode node.Struct +> sepSpace +> sepOpenT - +> genTuple node.Tuple + +> genTupleExpr node.Tuple +> genSingleTextNode node.ClosingParen |> genNode node | Expr.ArrayOrList node -> fun ctx -> genArrayOrList (ctx.Config.MultilineBracketStyle = Cramped) node ctx @@ -1833,7 +1833,7 @@ let genMultilineFunctionApplicationArguments (argExpr: Expr) = | _ -> genExpr parenNode.Expr |> argsInsideParenthesis parenNode | _ -> genExpr argExpr -let genTuple (node: ExprTupleNode) = +let genTupleExpr (node: ExprTupleNode) = // if a tuple element is an InfixApp with a lambda or if-then-else expression on the rhs, // we need to wrap the rhs in parenthesis to avoid a parse error caused by the higher precedence of "," over the rhs expression. // see 2819 @@ -2464,6 +2464,23 @@ let genTyparDecls (td: TyparDecls) = |> genNode node | TyparDecls.SinglePrefix node -> genTyparDecl true node +let genTuplePatLong (node: PatTupleNode) = + let padUntilAtCurrentColumn ctx = + addFixedSpaces ctx.WriterModel.AtColumn ctx + + col padUntilAtCurrentColumn node.Items (function + | Choice1Of2 p -> genPat p + | Choice2Of2 comma -> genSingleTextNode comma +> sepNln) + +let genTuplePat (node: PatTupleNode) = + let short = + col sepNone node.Items (function + | Choice1Of2 p -> genPat p + | Choice2Of2 comma -> genSingleTextNode comma +> addSpaceIfSpaceAfterComma) + + atCurrentColumn (expressionFitsOnRestOfLine short (atCurrentColumn (genTuplePatLong node))) + |> genNode node + let genPat (p: Pattern) = match p with | Pattern.OptionalVal n -> genSingleTextNode n @@ -2532,14 +2549,7 @@ let genPat (p: Pattern) = +> genPat node.Pattern +> genSingleTextNode node.ClosingParen |> genNode node - | Pattern.Tuple node -> - let padUntilAtCurrentColumn ctx = - addFixedSpaces ctx.WriterModel.AtColumn ctx - - expressionFitsOnRestOfLine - (col sepComma node.Patterns genPat) - (atCurrentColumn (col (padUntilAtCurrentColumn +> sepComma +> sepNln) node.Patterns genPat)) - |> genNode node + | Pattern.Tuple node -> genTuplePat node | Pattern.StructTuple node -> !- "struct " +> sepOpenT @@ -2745,9 +2755,7 @@ let genBinding (b: BindingNode) (ctx: Context) : Context = match parenNode.Pattern with | Pattern.Tuple tupleNode -> (genSingleTextNode parenNode.OpeningParen - +> indentSepNlnUnindent ( - (col (sepComma +> sepNln) tupleNode.Patterns genPat) |> genNode tupleNode - ) + +> indentSepNlnUnindent (genTuplePatLong tupleNode |> genNode tupleNode) +> sepNln +> genSingleTextNode parenNode.ClosingParen |> genNode parenNode), @@ -3158,11 +3166,18 @@ let genImplicitConstructor (node: ImplicitConstructorNode) = +> optSingle (fun t -> sepColon +> autoIndentAndNlnIfExpressionExceedsPageWidth (genType t)) node.Type |> genNode node - let shortPats = col sepComma node.Parameters genSimplePat + let shortPats = + col sepNone node.Items (function + | Choice1Of2 p -> genSimplePat p + | Choice2Of2 comma -> genSingleTextNode comma +> addSpaceIfSpaceAfterComma) let longPats = - indentSepNlnUnindent (col (sepComma +> sepNln) node.Parameters genSimplePat) - +> sepNln + let genPats = + col sepNone node.Items (function + | Choice1Of2 p -> genSimplePat p + | Choice2Of2 comma -> genSingleTextNode comma +> sepNln) + + indentSepNlnUnindent genPats +> sepNln let short = genXml node.XmlDoc diff --git a/src/Fantomas.Core/SyntaxOak.fs b/src/Fantomas.Core/SyntaxOak.fs index 4ff3c525b9..09d3f54b58 100644 --- a/src/Fantomas.Core/SyntaxOak.fs +++ b/src/Fantomas.Core/SyntaxOak.fs @@ -502,11 +502,16 @@ type PatParenNode(openingParen: SingleTextNode, pat: Pattern, closingParen: Sing member val Pattern = pat member val ClosingParen = closingParen -type PatTupleNode(pats: Pattern list, range) = +type PatTupleNode(items: Choice list, range) = inherit NodeBase(range) - override val Children: Node array = [| yield! (List.map Pattern.Node pats) |] - member val Patterns = pats + override val Children: Node array = + [| for item in items do + match item with + | Choice1Of2 p -> Pattern.Node p + | Choice2Of2 comma -> comma |] + + member val Items = items type PatStructTupleNode(pats: Pattern list, range) = inherit NodeBase(range) @@ -2229,7 +2234,7 @@ type ImplicitConstructorNode attributes: MultipleAttributeListNode option, accessibility: SingleTextNode option, openingParen: SingleTextNode, - parameters: SimplePatNode list, + items: Choice list, closingParen: SingleTextNode, self: AsSelfIdentifierNode option, range @@ -2241,7 +2246,10 @@ type ImplicitConstructorNode yield! noa attributes yield! noa accessibility yield openingParen - yield! nodes parameters + for item in items do + match item with + | Choice1Of2 node -> yield node + | Choice2Of2 comma -> yield comma yield closingParen yield! noa self |] @@ -2249,7 +2257,7 @@ type ImplicitConstructorNode member val Attributes = attributes member val Accessibility = accessibility member val OpeningParen = openingParen - member val Parameters = parameters + member val Items = items member val ClosingParen = closingParen member val Self = self From f1305e671d8e152622cf4c4f992917c3aed0149c Mon Sep 17 00:00:00 2001 From: Florian Verdonck Date: Sun, 21 May 2023 15:58:20 +0200 Subject: [PATCH 08/18] Update FCS to 'Prototype signature files for lex files' (#2881) --- CHANGELOG.md | 5 +++++ Directory.Build.props | 2 +- src/Fantomas.FCS/Fantomas.FCS.fsproj | 17 +++++++++++++---- src/Fantomas.FCS/packages.lock.json | 12 ++++++------ 4 files changed, 25 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f470d52d18..ce9f0a218c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## [Unreleased] + +### Changed +* Update FCS to 'Prototype signature files for lex files', commit 5b74995780a0e9d24a5db025e68b31e6d910f71f + ## [6.0.3] - 2023-05-14 ### Fixed diff --git a/Directory.Build.props b/Directory.Build.props index eb91c9c62f..fca503b1ae 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -39,7 +39,7 @@ Some common use cases include: - e267bb9f8d590feed1b94b469d78cfce61afecad + 5b74995780a0e9d24a5db025e68b31e6d910f71f 2.8.28 6.0.1 diff --git a/src/Fantomas.FCS/Fantomas.FCS.fsproj b/src/Fantomas.FCS/Fantomas.FCS.fsproj index 4005c8a660..75131bca54 100644 --- a/src/Fantomas.FCS/Fantomas.FCS.fsproj +++ b/src/Fantomas.FCS/Fantomas.FCS.fsproj @@ -174,7 +174,7 @@ Facilities\prim-parsing.fs - --unicode --lexlib Internal.Utilities.Text.Lexing + --module FSharp.Compiler.AbstractIL.AsciiLexer --internal --open Internal.Utilities.Text.Lexing --open FSharp.Compiler.AbstractIL.AsciiParser --unicode --lexlib Internal.Utilities.Text.Lexing AbstractIL\illex.fsl @@ -211,6 +211,9 @@ AbstractIL\FsYaccOut\ilpars.fs + + AbstractIL\FsLexOut\illex.fsi + AbstractIL\FsLexOut\illex.fs @@ -221,7 +224,7 @@ SyntaxTree\PrettyNaming.fs - --unicode --lexlib Internal.Utilities.Text.Lexing + --module FSharp.Compiler.PPLexer --internal --open FSharp.Compiler.Lexhelp --open Internal.Utilities.Text.Lexing --open FSharp.Compiler.PPParser --unicode --lexlib Internal.Utilities.Text.Lexing SyntaxTree\pplex.fsl @@ -229,7 +232,7 @@ SyntaxTree\pppars.fsy - --unicode --lexlib Internal.Utilities.Text.Lexing + --module FSharp.Compiler.Lexer --open FSharp.Compiler.Lexhelp --open Internal.Utilities.Text.Lexing --open FSharp.Compiler.Parser --open FSharp.Compiler.Text --open FSharp.Compiler.ParseHelpers --internal --unicode --lexlib Internal.Utilities.Text.Lexing SyntaxTree\lex.fsl @@ -302,9 +305,15 @@ SyntaxTree\LexHelpers.fs + + SyntaxTree\FsLexOutput\pplex.fsi + SyntaxTree\FsLexOutput\pplex.fs + + SyntaxTree\FsLexOutput\lex.fsi + SyntaxTree\FsLexOutput\lex.fs @@ -322,7 +331,7 @@ - + diff --git a/src/Fantomas.FCS/packages.lock.json b/src/Fantomas.FCS/packages.lock.json index 9b730c85df..26e990f7a2 100644 --- a/src/Fantomas.FCS/packages.lock.json +++ b/src/Fantomas.FCS/packages.lock.json @@ -22,12 +22,12 @@ }, "FsLexYacc": { "type": "Direct", - "requested": "[11.1.0, )", - "resolved": "11.1.0", - "contentHash": "JoGbIVZVuJs4CsCED0Wi7Winhf6isw0qrblm4hNAdpaZX3Sp5EmPZa07Nhv6SVmoDNPP/HfjCBVtFbaxyzOD2w==", + "requested": "[11.2.0, )", + "resolved": "11.2.0", + "contentHash": "YfCWpEr1z34mtfBLINYLndmsUTLgADnwY1j1WibS1EbuGVVX9r6pkdqmN8/vWqBi5qvY/s7pBjdq51Ks1Dk5MQ==", "dependencies": { "FSharp.Core": "4.6.2", - "FsLexYacc.Runtime": "11.1.0" + "FsLexYacc.Runtime": "11.2.0" } }, "Ionide.KeepAChangelog.Tasks": { @@ -78,8 +78,8 @@ }, "FsLexYacc.Runtime": { "type": "Transitive", - "resolved": "11.1.0", - "contentHash": "NtCnm45VhoJKyz2Ie8OzSSUgrUYQ0TKp2LbgEBeEOr7j6+/0om3lf7NmmoDZDjuNx/KRXb95ZMDjGZRZD92ZfA==", + "resolved": "11.2.0", + "contentHash": "psv1xPIm2NOEdd1RfaBSnN7y6M7XRS69QYB2kP2hD8LKaepGIGM5qAC7xHNpqO1v7QfyMFmuZ5/KqyWhfAfKJg==", "dependencies": { "FSharp.Core": "4.6.2" } From 2d3e9e658c5ef4cd59cc8a98456b0b43611fbcdc Mon Sep 17 00:00:00 2001 From: Florian Verdonck Date: Tue, 23 May 2023 08:44:30 +0200 Subject: [PATCH 09/18] Update sdk (#2883) * Map DiagnosticWithText exception in FCS. * Bump SDK and add additional test flags. --- Directory.Build.props | 2 +- global.json | 2 +- src/Fantomas.FCS/Parse.fs | 129 +++++++++++++++++++------------------- 3 files changed, 67 insertions(+), 66 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index fca503b1ae..d154e9e3a4 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -34,7 +34,7 @@ Some common use cases include: true true - $(OtherFlags) --test:GraphBasedChecking + $(OtherFlags) --test:GraphBasedChecking --test:ParallelOptimization --test:ParallelIlxGen diff --git a/global.json b/global.json index 89195d0c85..ae81ae5aef 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "7.0.400-preview.23218.20", + "version": "7.0.400-preview.23272.51", "rollForward": "latestPatch" } } \ No newline at end of file diff --git a/src/Fantomas.FCS/Parse.fs b/src/Fantomas.FCS/Parse.fs index 17271c0347..d87b34666c 100644 --- a/src/Fantomas.FCS/Parse.fs +++ b/src/Fantomas.FCS/Parse.fs @@ -23,15 +23,15 @@ open FSharp.Compiler.SyntaxTreeOps open FSharp.Compiler.IO open FSharp.Compiler.ParseHelpers -let private FSharpSigFileSuffixes = [ ".mli"; ".fsi" ] +let FSharpSigFileSuffixes = [ ".mli"; ".fsi" ] -let private mlCompatSuffixes = [ ".mli"; ".ml" ] +let mlCompatSuffixes = [ ".mli"; ".ml" ] -let private FSharpImplFileSuffixes = [ ".ml"; ".fs"; ".fsscript"; ".fsx" ] +let FSharpImplFileSuffixes = [ ".ml"; ".fs"; ".fsscript"; ".fsx" ] -let private FSharpScriptFileSuffixes = [ ".fsscript"; ".fsx" ] +let FSharpScriptFileSuffixes = [ ".fsscript"; ".fsx" ] -let private CanonicalizeFilename filename = +let CanonicalizeFilename filename = let basic = FileSystemUtils.fileNameOfPath filename String.capitalize ( @@ -41,7 +41,7 @@ let private CanonicalizeFilename filename = basic ) -let private ComputeAnonModuleName check defaultNamespace filename (m: range) = +let ComputeAnonModuleName check defaultNamespace filename (m: range) = let modname = CanonicalizeFilename filename if @@ -75,11 +75,11 @@ let private ComputeAnonModuleName check defaultNamespace filename (m: range) = pathToSynLid anonymousModuleNameRange (splitNamespace combined) -let private IsScript filename = +let IsScript filename = let lower = String.lowercase filename FSharpScriptFileSuffixes |> List.exists (FileSystemUtils.checkSuffix lower) -let private PostParseModuleImpl (_i, defaultNamespace, _isLastCompiland, filename, impl) = +let PostParseModuleImpl (_i, defaultNamespace, _isLastCompiland, filename, impl) = match impl with | ParsedImplFileFragment.NamedModule(SynModuleOrNamespace(lid, isRec, @@ -133,39 +133,39 @@ let private PostParseModuleImpl (_i, defaultNamespace, _isLastCompiland, filenam // Give a unique name to the different kinds of inputs. Used to correlate signature and implementation files // QualFileNameOfModuleName - files with a single module declaration or an anonymous module -let private QualFileNameOfModuleName m filename modname = +let QualFileNameOfModuleName m filename modname = QualifiedNameOfFile(mkSynId m (textOfLid modname + (if IsScript filename then "$fsx" else ""))) -let private QualFileNameOfFilename m filename = +let QualFileNameOfFilename m filename = QualifiedNameOfFile(mkSynId m (CanonicalizeFilename filename + (if IsScript filename then "$fsx" else ""))) -let private QualFileNameOfSpecs filename specs = +let QualFileNameOfSpecs filename specs = match specs with | [ SynModuleOrNamespaceSig(longId = modname; kind = kind; range = m) ] when kind.IsModule -> QualFileNameOfModuleName m filename modname | [ SynModuleOrNamespaceSig(kind = kind; range = m) ] when not kind.IsModule -> QualFileNameOfFilename m filename | _ -> QualFileNameOfFilename (mkRange filename pos0 pos0) filename -let private QualFileNameOfImpls filename specs = +let QualFileNameOfImpls filename specs = match specs with | [ SynModuleOrNamespace(longId = modname; kind = kind; range = m) ] when kind.IsModule -> QualFileNameOfModuleName m filename modname | [ SynModuleOrNamespace(kind = kind; range = m) ] when not kind.IsModule -> QualFileNameOfFilename m filename | _ -> QualFileNameOfFilename (mkRange filename pos0 pos0) filename -let private GetScopedPragmasForInput input = +let GetScopedPragmasForInput input = match input with | ParsedInput.SigFile(ParsedSigFileInput(scopedPragmas = pragmas)) -> pragmas | ParsedInput.ImplFile(ParsedImplFileInput(scopedPragmas = pragmas)) -> pragmas -let private collectCodeComments (lexbuf: UnicodeLexing.Lexbuf) (tripleSlashComments: range list) = +let collectCodeComments (lexbuf: UnicodeLexing.Lexbuf) (tripleSlashComments: range list) = [ yield! LexbufCommentStore.GetComments(lexbuf) yield! (List.map CommentTrivia.LineComment tripleSlashComments) ] |> List.sortBy (function | CommentTrivia.LineComment r | CommentTrivia.BlockComment r -> r.StartLine, r.StartColumn) -let private PostParseModuleImpls +let PostParseModuleImpls ( defaultNamespace, filename, @@ -210,7 +210,7 @@ let private PostParseModuleImpls ) ) -let private PostParseModuleSpec (_i, defaultNamespace, _isLastCompiland, filename, intf) = +let PostParseModuleSpec (_i, defaultNamespace, _isLastCompiland, filename, intf) = match intf with | ParsedSigFileFragment.NamedModule(SynModuleOrNamespaceSig(lid, isRec, @@ -272,7 +272,7 @@ let private PostParseModuleSpec (_i, defaultNamespace, _isLastCompiland, filenam SynModuleOrNamespaceSig(lid, isRecursive, kind, decls, xmlDoc, attributes, None, range, trivia) -let private PostParseModuleSpecs +let PostParseModuleSpecs ( defaultNamespace, filename, @@ -314,7 +314,7 @@ let private PostParseModuleSpecs ) ) -let private ParseInput +let ParseInput ( lexer, errorLogger: CapturingDiagnosticsLogger, @@ -374,7 +374,7 @@ let private ParseInput let filteringErrorLogger = errorLogger // TODO: does this matter? //GetErrorLoggerFilteringByScopedPragmas(false, scopedPragmas, diagnosticOptions, errorLogger) delayLogger.CommitDelayedDiagnostics filteringErrorLogger -let private EmptyParsedInput (filename, isLastCompiland) = +let EmptyParsedInput (filename, isLastCompiland) = let lower = String.lowercase filename if FSharpSigFileSuffixes |> List.exists (FileSystemUtils.checkSuffix lower) then @@ -406,10 +406,10 @@ let private EmptyParsedInput (filename, isLastCompiland) = ) ) -let private createLexbuf langVersion sourceText = +let createLexbuf langVersion sourceText = UnicodeLexing.SourceTextAsLexbuf(true, LanguageVersion(langVersion), sourceText) -let private createLexerFunction (defines: string list) lexbuf (errorLogger: CapturingDiagnosticsLogger) = +let createLexerFunction (defines: string list) lexbuf (errorLogger: CapturingDiagnosticsLogger) = let lightStatus = IndentationAwareSyntaxStatus(true, true) // Note: we don't really attempt to intern strings across a large scope. @@ -437,132 +437,132 @@ type FSharpParserDiagnostic = ErrorNumber: int option Message: string } -let private getErrorString key = SR.GetString key +let getErrorString key = SR.GetString key -let private UnexpectedEndOfInputE () = +let UnexpectedEndOfInputE () = DeclareResourceString("UnexpectedEndOfInput", "") -let private OBlockEndSentenceE () = +let OBlockEndSentenceE () = DeclareResourceString("BlockEndSentence", "") -let private UnexpectedE () = +let UnexpectedE () = DeclareResourceString("Unexpected", "%s") -let private NONTERM_interactionE () = +let NONTERM_interactionE () = DeclareResourceString("NONTERM.interaction", "") -let private NONTERM_hashDirectiveE () = +let NONTERM_hashDirectiveE () = DeclareResourceString("NONTERM.hashDirective", "") -let private NONTERM_fieldDeclE () = +let NONTERM_fieldDeclE () = DeclareResourceString("NONTERM.fieldDecl", "") -let private NONTERM_unionCaseReprE () = +let NONTERM_unionCaseReprE () = DeclareResourceString("NONTERM.unionCaseRepr", "") -let private NONTERM_localBindingE () = +let NONTERM_localBindingE () = DeclareResourceString("NONTERM.localBinding", "") -let private NONTERM_hardwhiteLetBindingsE () = +let NONTERM_hardwhiteLetBindingsE () = DeclareResourceString("NONTERM.hardwhiteLetBindings", "") -let private NONTERM_classDefnMemberE () = +let NONTERM_classDefnMemberE () = DeclareResourceString("NONTERM.classDefnMember", "") -let private NONTERM_defnBindingsE () = +let NONTERM_defnBindingsE () = DeclareResourceString("NONTERM.defnBindings", "") -let private NONTERM_classMemberSpfnE () = +let NONTERM_classMemberSpfnE () = DeclareResourceString("NONTERM.classMemberSpfn", "") -let private NONTERM_valSpfnE () = +let NONTERM_valSpfnE () = DeclareResourceString("NONTERM.valSpfn", "") -let private NONTERM_tyconSpfnE () = +let NONTERM_tyconSpfnE () = DeclareResourceString("NONTERM.tyconSpfn", "") -let private NONTERM_anonLambdaExprE () = +let NONTERM_anonLambdaExprE () = DeclareResourceString("NONTERM.anonLambdaExpr", "") -let private NONTERM_attrUnionCaseDeclE () = +let NONTERM_attrUnionCaseDeclE () = DeclareResourceString("NONTERM.attrUnionCaseDecl", "") -let private NONTERM_cPrototypeE () = +let NONTERM_cPrototypeE () = DeclareResourceString("NONTERM.cPrototype", "") -let private NONTERM_objectImplementationMembersE () = +let NONTERM_objectImplementationMembersE () = DeclareResourceString("NONTERM.objectImplementationMembers", "") -let private NONTERM_ifExprCasesE () = +let NONTERM_ifExprCasesE () = DeclareResourceString("NONTERM.ifExprCases", "") -let private NONTERM_openDeclE () = +let NONTERM_openDeclE () = DeclareResourceString("NONTERM.openDecl", "") -let private NONTERM_fileModuleSpecE () = +let NONTERM_fileModuleSpecE () = DeclareResourceString("NONTERM.fileModuleSpec", "") -let private NONTERM_patternClausesE () = +let NONTERM_patternClausesE () = DeclareResourceString("NONTERM.patternClauses", "") -let private NONTERM_beginEndExprE () = +let NONTERM_beginEndExprE () = DeclareResourceString("NONTERM.beginEndExpr", "") -let private NONTERM_recdExprE () = +let NONTERM_recdExprE () = DeclareResourceString("NONTERM.recdExpr", "") -let private NONTERM_tyconDefnE () = +let NONTERM_tyconDefnE () = DeclareResourceString("NONTERM.tyconDefn", "") -let private NONTERM_exconCoreE () = +let NONTERM_exconCoreE () = DeclareResourceString("NONTERM.exconCore", "") -let private NONTERM_typeNameInfoE () = +let NONTERM_typeNameInfoE () = DeclareResourceString("NONTERM.typeNameInfo", "") -let private NONTERM_attributeListE () = +let NONTERM_attributeListE () = DeclareResourceString("NONTERM.attributeList", "") -let private NONTERM_quoteExprE () = +let NONTERM_quoteExprE () = DeclareResourceString("NONTERM.quoteExpr", "") -let private NONTERM_typeConstraintE () = +let NONTERM_typeConstraintE () = DeclareResourceString("NONTERM.typeConstraint", "") -let private NONTERM_Category_ImplementationFileE () = +let NONTERM_Category_ImplementationFileE () = DeclareResourceString("NONTERM.Category.ImplementationFile", "") -let private NONTERM_Category_DefinitionE () = +let NONTERM_Category_DefinitionE () = DeclareResourceString("NONTERM.Category.Definition", "") -let private NONTERM_Category_SignatureFileE () = +let NONTERM_Category_SignatureFileE () = DeclareResourceString("NONTERM.Category.SignatureFile", "") -let private NONTERM_Category_PatternE () = +let NONTERM_Category_PatternE () = DeclareResourceString("NONTERM.Category.Pattern", "") -let private NONTERM_Category_ExprE () = +let NONTERM_Category_ExprE () = DeclareResourceString("NONTERM.Category.Expr", "") -let private NONTERM_Category_TypeE () = +let NONTERM_Category_TypeE () = DeclareResourceString("NONTERM.Category.Type", "") -let private NONTERM_typeArgsActualE () = +let NONTERM_typeArgsActualE () = DeclareResourceString("NONTERM.typeArgsActual", "") -let private TokenName1E () = +let TokenName1E () = DeclareResourceString("TokenName1", "%s") -let private TokenName1TokenName2E () = +let TokenName1TokenName2E () = DeclareResourceString("TokenName1TokenName2", "%s%s") -let private TokenName1TokenName2TokenName3E () = +let TokenName1TokenName2TokenName3E () = DeclareResourceString("TokenName1TokenName2TokenName3", "%s%s%s") -let private LibraryUseOnlyE () = +let LibraryUseOnlyE () = DeclareResourceString("LibraryUseOnly", "") -let private getSyntaxErrorMessage ctxt = +let getSyntaxErrorMessage ctxt = let ctxt = unbox> ctxt @@ -1053,6 +1053,7 @@ let parseFile | :? IndentationProblem as ip -> Some ip.Data1, ip.Data0, Some 58 | :? SyntaxError as se -> Some se.range, (getSyntaxErrorMessage se.Data0), Some 10 | :? LibraryUseOnly as luo -> Some luo.range, LibraryUseOnlyE().Format, Some 42 + | :? DiagnosticWithText as dwt -> Some dwt.range, dwt.message, Some dwt.number | _ -> None, p.Exception.Message, None { Severity = severity From a6cf0bbd1f6f15978980865e0670d2846acb9ae1 Mon Sep 17 00:00:00 2001 From: dawe Date: Tue, 30 May 2023 09:57:23 +0200 Subject: [PATCH 10/18] add FsHttp as a Fantomas user (#2886) --- docs/images/users/fshttp.png | Bin 0 -> 102512 bytes docs/index.html | 3 +++ 2 files changed, 3 insertions(+) create mode 100644 docs/images/users/fshttp.png diff --git a/docs/images/users/fshttp.png b/docs/images/users/fshttp.png new file mode 100644 index 0000000000000000000000000000000000000000..b7082ed1e754bdf8c1c173eefbef1fe94dec8723 GIT binary patch literal 102512 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 z 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%0DELY-jo_dWzi*w$21WR>T0TRKyI1Lw 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 uses Fantomas? FAKE logo + + FsHttp logo +

Your logo here?

From e9653fd93fc2b473d0f9995e4c89be8cc1485c98 Mon Sep 17 00:00:00 2001 From: Florian Verdonck Date: Thu, 1 Jun 2023 15:22:49 +0200 Subject: [PATCH 11/18] Add space before arrow when trivia before is present. (#2889) --- CHANGELOG.md | 3 +++ .../PatternMatchingTests.fs | 23 +++++++++++++++++++ src/Fantomas.Core/CodePrinter.fs | 11 ++++++++- 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ce9f0a218c..28f86947ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ ### Changed * Update FCS to 'Prototype signature files for lex files', commit 5b74995780a0e9d24a5db025e68b31e6d910f71f +### Fixed +* Missing space before arrow. [#2888](https://github.com/fsprojects/fantomas/issues/2888) + ## [6.0.3] - 2023-05-14 ### Fixed diff --git a/src/Fantomas.Core.Tests/PatternMatchingTests.fs b/src/Fantomas.Core.Tests/PatternMatchingTests.fs index 29b35ea90b..d8830d73bd 100644 --- a/src/Fantomas.Core.Tests/PatternMatchingTests.fs +++ b/src/Fantomas.Core.Tests/PatternMatchingTests.fs @@ -2321,3 +2321,26 @@ let f () = tail -> 1 | _ -> None """ + +[] +let ``multiline trivia for arrow, 2888`` () = + formatSourceString + false + """ +match subcategory with +| BuildPhaseSubcategory.Internal +// Getting here means the compiler has ICE-d. Let's not pile on by showing the unknownSubcategory assert below. +// Just treat as an unknown-to-LanguageService error. + -> false +""" + config + |> prepend newline + |> should + equal + """ +match subcategory with +| BuildPhaseSubcategory.Internal +// Getting here means the compiler has ICE-d. Let's not pile on by showing the unknownSubcategory assert below. +// Just treat as an unknown-to-LanguageService error. + -> false +""" diff --git a/src/Fantomas.Core/CodePrinter.fs b/src/Fantomas.Core/CodePrinter.fs index 8b762e8139..252cfd8102 100644 --- a/src/Fantomas.Core/CodePrinter.fs +++ b/src/Fantomas.Core/CodePrinter.fs @@ -2013,7 +2013,16 @@ let genClause (isLastItem: bool) (node: MatchClauseNode) = expressionFitsOnRestOfLine (sepSpace +> genExpr node.BodyExpr) long - (genSingleTextNodeWithSpaceSuffix sepSpace node.Arrow + let sepArrow = + if not node.Arrow.HasContentBefore then + genSingleTextNodeWithSpaceSuffix sepSpace node.Arrow + else + // In the weird case where the arrow has content before it and there is no multiline whenExpr, + // we need to ensure a space was written before the arrow to avoid offset errors. + // See https://github.com/fsprojects/fantomas/issues/2888 + !- $" {node.Arrow.Text} " |> genNode node.Arrow + + (sepArrow +> ifElse (ctx.Config.ExperimentalKeepIndentInBranch && isLastItem) genKeepIndentInBranch From 6424a615ecd7e9d6772c9720a442bd63c95de09b Mon Sep 17 00:00:00 2001 From: Florian Verdonck Date: Fri, 2 Jun 2023 12:15:24 +0200 Subject: [PATCH 12/18] Use exact dotnet version in EndToEndTests. (#2893) --- src/Fantomas.Client.Tests/EndToEndTests.fs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Fantomas.Client.Tests/EndToEndTests.fs b/src/Fantomas.Client.Tests/EndToEndTests.fs index dd19813439..dc86112524 100644 --- a/src/Fantomas.Client.Tests/EndToEndTests.fs +++ b/src/Fantomas.Client.Tests/EndToEndTests.fs @@ -40,7 +40,16 @@ type EndToEndTests() = // This sdk version must match the version used in this repository. // It will be the version which the CI/CD pipeline has access to. - do! dotnet "new globaljson --sdk-version 7.0.202 --roll-forward latestPatch" + let! dotnetVersionResult = + Cli + .Wrap("dotnet") + .WithWorkingDirectory(__SOURCE_DIRECTORY__) + .WithArguments("--version") + .ExecuteBufferedAsync() + .Task + + let dotnetVersion = dotnetVersionResult.StandardOutput.Trim() + do! dotnet $"new globaljson --sdk-version %s{dotnetVersion} --roll-forward latestPatch" do! dotnet "new tool-manifest" do! From bfb4afc3c52d68b0126bcc86a1442c33d7a65ed9 Mon Sep 17 00:00:00 2001 From: clkbug Date: Fri, 2 Jun 2023 19:27:41 +0900 Subject: [PATCH 13/18] Add regression test for cons pattern in let binding (#1996) (#2891) * Add regression test for cons pattern in let binding (#1996) * Correct test. * Add changelog entry --------- Co-authored-by: nojaf --- CHANGELOG.md | 1 + src/Fantomas.Core.Tests/LetBindingTests.fs | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 28f86947ae..7cc55cc0f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ ### Fixed * Missing space before arrow. [#2888](https://github.com/fsprojects/fantomas/issues/2888) +* Cons pattern in let bindings is converted to invalid code. [#1996](https://github.com/fsprojects/fantomas/issues/1996) ## [6.0.3] - 2023-05-14 diff --git a/src/Fantomas.Core.Tests/LetBindingTests.fs b/src/Fantomas.Core.Tests/LetBindingTests.fs index dba78cf6d6..1128d2b931 100644 --- a/src/Fantomas.Core.Tests/LetBindingTests.fs +++ b/src/Fantomas.Core.Tests/LetBindingTests.fs @@ -2165,3 +2165,18 @@ let x ) = print "hello" """ + +[] +let ``cons pattern in let binding, 1996`` () = + formatSourceString + false + """ +let x::y = [] +""" + config + |> prepend newline + |> should + equal + """ +let x :: y = [] +""" From 187cb01e48bfa9ed98d16e06143ccdeb78ddb2d2 Mon Sep 17 00:00:00 2001 From: Florian Verdonck Date: Fri, 2 Jun 2023 12:41:00 +0200 Subject: [PATCH 14/18] Remove Context.HasSource (#2890) * Create context with HasSource true. * Correct interpolated string parts in ASTTransformer fallback. --- src/Fantomas.Core.Tests/CodeFormatterTests.fs | 20 +++++++++++++++++++ .../InterpolatedStringTests.fs | 15 ++++++++++++++ src/Fantomas.Core/ASTTransformer.fs | 18 +++++++++++++++-- src/Fantomas.Core/CodeFormatter.fs | 4 ++-- src/Fantomas.Core/CodeFormatterImpl.fs | 2 +- src/Fantomas.Core/CodePrinter.fs | 9 ++------- src/Fantomas.Core/Context.fs | 8 ++------ src/Fantomas.Core/Context.fsi | 15 +++++--------- src/Fantomas.Core/Selection.fs | 2 +- 9 files changed, 64 insertions(+), 29 deletions(-) diff --git a/src/Fantomas.Core.Tests/CodeFormatterTests.fs b/src/Fantomas.Core.Tests/CodeFormatterTests.fs index 1899b64510..7b06ef4716 100644 --- a/src/Fantomas.Core.Tests/CodeFormatterTests.fs +++ b/src/Fantomas.Core.Tests/CodeFormatterTests.fs @@ -47,3 +47,23 @@ let ``trivia is parsed for Oak`` () = |> fst Assert.True(oak.ModulesOrNamespaces.[0].HasContentAfter) + +[] +let ``parsed oak can be formatted back to source`` () = + let source = "$\"gc{i}\"" + + let oak = + CodeFormatter.ParseOakAsync(false, source) + |> Async.RunSynchronously + |> Array.head + |> fst + + let formatted = + CodeFormatter.FormatOakAsync( + oak, + { FormatConfig.Default with + InsertFinalNewline = false } + ) + |> Async.RunSynchronously + + Assert.AreEqual(source, formatted) diff --git a/src/Fantomas.Core.Tests/InterpolatedStringTests.fs b/src/Fantomas.Core.Tests/InterpolatedStringTests.fs index bc4d084836..a615065a77 100644 --- a/src/Fantomas.Core.Tests/InterpolatedStringTests.fs +++ b/src/Fantomas.Core.Tests/InterpolatedStringTests.fs @@ -78,6 +78,21 @@ let text = "foo" let s = $"%s{text} bar" """ +[] +let ``interpolation from AST with multiple fillExprs`` () = + formatAST + false + """ +$"%s{text} %i{bar} %f{meh}" +""" + config + |> prepend newline + |> should + equal + """ +$"%s{text} %i{bar} %f{meh}" +""" + [] let ``multiline expression in multiline string`` () = formatSourceString diff --git a/src/Fantomas.Core/ASTTransformer.fs b/src/Fantomas.Core/ASTTransformer.fs index 09cedff756..1c02a27f01 100644 --- a/src/Fantomas.Core/ASTTransformer.fs +++ b/src/Fantomas.Core/ASTTransformer.fs @@ -1466,11 +1466,25 @@ let mkExpr (creationAide: CreationAide) (e: SynExpr) : Expr = ) |> Expr.LibraryOnlyStaticOptimization | SynExpr.InterpolatedString(parts, _, _) -> + let lastIndex = parts.Length - 1 + let parts = parts - |> List.map (function + |> List.mapi (fun idx part -> + match part with | SynInterpolatedStringPart.String(v, r) -> - stn (creationAide.TextFromSource (fun () -> v) r) r |> Choice1Of2 + stn + (creationAide.TextFromSource + (fun () -> + if idx = 0 && not (v.StartsWith("$")) then + $"$\"%s{v}{{" + elif idx = lastIndex && not (v.EndsWith("\"")) then + $"}}%s{v}\"" + else + $"}}{v}{{") + r) + r + |> Choice1Of2 | SynInterpolatedStringPart.FillExpr(fillExpr, qualifiers) -> let m = match qualifiers with diff --git a/src/Fantomas.Core/CodeFormatter.fs b/src/Fantomas.Core/CodeFormatter.fs index 5ec631b156..bff11ccd2e 100644 --- a/src/Fantomas.Core/CodeFormatter.fs +++ b/src/Fantomas.Core/CodeFormatter.fs @@ -80,14 +80,14 @@ type CodeFormatter = static member FormatOakAsync(oak: Oak) : Async = async { - let context = Context.Context.Create false FormatConfig.Default + let context = Context.Context.Create FormatConfig.Default let result = context |> CodePrinter.genFile oak |> Context.dump false return result.Code } static member FormatOakAsync(oak: Oak, config: FormatConfig) : Async = async { - let context = Context.Context.Create false config + let context = Context.Context.Create config let result = context |> CodePrinter.genFile oak |> Context.dump false return result.Code } diff --git a/src/Fantomas.Core/CodeFormatterImpl.fs b/src/Fantomas.Core/CodeFormatterImpl.fs index f051473709..857eeea741 100644 --- a/src/Fantomas.Core/CodeFormatterImpl.fs +++ b/src/Fantomas.Core/CodeFormatterImpl.fs @@ -57,7 +57,7 @@ let formatAST (config: FormatConfig) (cursor: pos option) : FormatResult = - let context = Context.Context.Create sourceText.IsSome config + let context = Context.Context.Create config let oak = match sourceText with diff --git a/src/Fantomas.Core/CodePrinter.fs b/src/Fantomas.Core/CodePrinter.fs index 252cfd8102..818166255c 100644 --- a/src/Fantomas.Core/CodePrinter.fs +++ b/src/Fantomas.Core/CodePrinter.fs @@ -1524,8 +1524,7 @@ let genExpr (e: Expr) = |> fun ctx -> { ctx with Config = currentConfig } |> atCurrentColumnIndent - onlyIfCtx (fun ctx -> not ctx.HasSource) (!- "$\"") - +> col sepNone node.Parts (fun part -> + col sepNone node.Parts (fun part -> match part with | Choice1Of2 stringNode -> genSingleTextNode stringNode | Choice2Of2 fillNode -> @@ -1534,11 +1533,7 @@ let genExpr (e: Expr) = genInterpolatedFillExpr fillNode.Expr +> optSingle (fun format -> sepColonFixed +> genSingleTextNode format) fillNode.Ident - if not ctx.HasSource then - (!- "{" +> genFill +> !- "}") ctx - else - genFill ctx) - +> onlyIfCtx (fun ctx -> not ctx.HasSource) (!- "\"") + genFill ctx) |> genNode node | Expr.IndexRangeWildcard node -> genSingleTextNode node | Expr.TripleNumberIndexRange node -> diff --git a/src/Fantomas.Core/Context.fs b/src/Fantomas.Core/Context.fs index 64f6996655..3c906a23cf 100644 --- a/src/Fantomas.Core/Context.fs +++ b/src/Fantomas.Core/Context.fs @@ -181,7 +181,6 @@ module WriterEvents = [] type Context = { Config: FormatConfig - HasSource: bool WriterModel: WriterModel WriterEvents: Queue FormattedCursor: pos option } @@ -189,15 +188,12 @@ type Context = /// Initialize with a string writer and use space as delimiter static member Default = { Config = FormatConfig.Default - HasSource = false WriterModel = WriterModel.init WriterEvents = Queue.empty FormattedCursor = None } - static member Create hasSource config : Context = - { Context.Default with - Config = config - HasSource = hasSource } + static member Create config : Context = + { Context.Default with Config = config } member x.WithDummy(writerCommands, ?keepPageWidth) = let keepPageWidth = keepPageWidth |> Option.defaultValue false diff --git a/src/Fantomas.Core/Context.fsi b/src/Fantomas.Core/Context.fsi index d54c6938c6..80e4d4921e 100644 --- a/src/Fantomas.Core/Context.fsi +++ b/src/Fantomas.Core/Context.fsi @@ -53,19 +53,14 @@ type WriterModel = [] type Context = - { - Config: FormatConfig - /// Indicates the presence of source code. - /// This could be absent in the case we are formatting from AST. - HasSource: bool - WriterModel: WriterModel - WriterEvents: Queue - FormattedCursor: pos option - } + { Config: FormatConfig + WriterModel: WriterModel + WriterEvents: Queue + FormattedCursor: pos option } /// Initialize with a string writer and use space as delimiter static member Default: Context - static member Create: hasSource: bool -> config: FormatConfig -> Context + static member Create: config: FormatConfig -> Context member WithDummy: writerCommands: Queue * ?keepPageWidth: bool -> Context member WithShortExpression: maxWidth: int * ?startColumn: int -> Context member Column: int diff --git a/src/Fantomas.Core/Selection.fs b/src/Fantomas.Core/Selection.fs index f664accf94..bff65e4bbe 100644 --- a/src/Fantomas.Core/Selection.fs +++ b/src/Fantomas.Core/Selection.fs @@ -404,7 +404,7 @@ let formatSelection MaxLineLength = maxLineLength } let formattedSelection = - let context = Context.Context.Create true selectionConfig + let context = Context.Context.Create selectionConfig match tree with | TreeForSelection.Unsupported -> From f95faa9845031b1c49fd7d5ab5deb80dda33efe1 Mon Sep 17 00:00:00 2001 From: dawe Date: Fri, 2 Jun 2023 13:12:39 +0200 Subject: [PATCH 15/18] add a newline after the operator in SameInfix applications to let comment be printed (#2892) * in the multilineExpr of SameInfix Apps, add a newline after the operator if there's ContentAfter * Slightly stricter check. * Add release notes. --------- Co-authored-by: Florian Verdonck --- CHANGELOG.md | 3 +- src/Fantomas.Core.Tests/CommentTests.fs | 42 +++++++++++++++++++++++++ src/Fantomas.Core/CodePrinter.fs | 2 +- 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7cc55cc0f9..016611ed1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## [Unreleased] +## [6.0.4] - 2023-06-02 ### Changed * Update FCS to 'Prototype signature files for lex files', commit 5b74995780a0e9d24a5db025e68b31e6d910f71f @@ -8,6 +8,7 @@ ### Fixed * Missing space before arrow. [#2888](https://github.com/fsprojects/fantomas/issues/2888) * Cons pattern in let bindings is converted to invalid code. [#1996](https://github.com/fsprojects/fantomas/issues/1996) +* Trivia after infix operator is not restored correctly. [#2887](https://github.com/fsprojects/fantomas/issues/2887) ## [6.0.3] - 2023-05-14 diff --git a/src/Fantomas.Core.Tests/CommentTests.fs b/src/Fantomas.Core.Tests/CommentTests.fs index 996c811baa..1edd288d95 100644 --- a/src/Fantomas.Core.Tests/CommentTests.fs +++ b/src/Fantomas.Core.Tests/CommentTests.fs @@ -2612,3 +2612,45 @@ let (* this comment disappears after formatting *) a = [] """ let (* this comment disappears after formatting *) a = [] """ + +[] +let ``trivia after infix operator is not restored correctly, 2887`` () = + formatSourceString + false + """ +let EnableHeapTerminationOnCorruption() = + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && Environment.OSVersion.Version.Major >= 6 && // If OS is Vista or higher + Environment.Version.Major < 3) then // and CLR not 3.0 or higher + let HeapEnableTerminationOnCorruption = 1u : uint32 + if not (HeapSetInformation(GetProcessHeap(), HeapEnableTerminationOnCorruption, UIntPtr.Zero, UIntPtr.Zero)) then + raise (System.Security.SecurityException( + "Unable to enable unmanaged process execution option TerminationOnCorruption. " + + "HeapSetInformation() returned FALSE; LastError = 0x" + + GetLastError().ToString("X").PadLeft(8, '0') + ".")) +""" + config + |> prepend newline + |> should + equal + """ +let EnableHeapTerminationOnCorruption () = + if + (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) + && Environment.OSVersion.Version.Major >= 6 + && // If OS is Vista or higher + Environment.Version.Major < 3) + then // and CLR not 3.0 or higher + let HeapEnableTerminationOnCorruption = 1u: uint32 + + if + not (HeapSetInformation(GetProcessHeap(), HeapEnableTerminationOnCorruption, UIntPtr.Zero, UIntPtr.Zero)) + then + raise ( + System.Security.SecurityException( + "Unable to enable unmanaged process execution option TerminationOnCorruption. " + + "HeapSetInformation() returned FALSE; LastError = 0x" + + GetLastError().ToString("X").PadLeft(8, '0') + + "." + ) + ) +""" diff --git a/src/Fantomas.Core/CodePrinter.fs b/src/Fantomas.Core/CodePrinter.fs index 818166255c..5bd47fe009 100644 --- a/src/Fantomas.Core/CodePrinter.fs +++ b/src/Fantomas.Core/CodePrinter.fs @@ -772,7 +772,7 @@ let genExpr (e: Expr) = +> sepNln +> col sepNln es (fun (operator, e) -> genSingleTextNode operator - +> sepSpace + +> sepNlnWhenWriteBeforeNewlineNotEmptyOr sepSpace +> (fun ctx -> match e with | Expr.Lambda _ when From 838436b61cf61e0b52bc964ea73fa8847a667c50 Mon Sep 17 00:00:00 2001 From: Florian Verdonck Date: Tue, 6 Jun 2023 14:56:38 +0200 Subject: [PATCH 16/18] Verify each test case twice. (#2898) --- src/Fantomas.Core.Tests/ClassTests.fs | 25 ------ .../CompilerDirectivesTests.fs | 27 ------ .../ControlStructureTests.fs | 63 ------------- src/Fantomas.Core.Tests/DotSetTests.fs | 6 -- .../KeepIndentInBranchTests.fs | 67 -------------- src/Fantomas.Core.Tests/LambdaTests.fs | 38 -------- .../MultiLineLambdaClosingNewlineTests.fs | 46 ---------- ...ineBetweenTypeDefinitionAndMembersTests.fs | 20 ----- .../Stroustrup/ExperimentalElmishTests.fs | 90 ------------------- src/Fantomas.Core.Tests/TestHelpers.fs | 13 ++- 10 files changed, 10 insertions(+), 385 deletions(-) diff --git a/src/Fantomas.Core.Tests/ClassTests.fs b/src/Fantomas.Core.Tests/ClassTests.fs index 2acea8706f..4b44d9aaf3 100644 --- a/src/Fantomas.Core.Tests/ClassTests.fs +++ b/src/Fantomas.Core.Tests/ClassTests.fs @@ -1041,31 +1041,6 @@ type C() = member _.Run() = 1 """ -[] -let ``explicit class/end with members, idempotent`` () = - formatSourceString - false - """ -type C() = - class - member x.P = 1 - end - - member _.Run() = 1 -""" - config - |> prepend newline - |> should - equal - """ -type C() = - class - member x.P = 1 - end - - member _.Run() = 1 -""" - [] let ``static member with get unit should be formatted the same as without, 1913`` () = formatSourceString diff --git a/src/Fantomas.Core.Tests/CompilerDirectivesTests.fs b/src/Fantomas.Core.Tests/CompilerDirectivesTests.fs index a678882456..213b7c2f82 100644 --- a/src/Fantomas.Core.Tests/CompilerDirectivesTests.fs +++ b/src/Fantomas.Core.Tests/CompilerDirectivesTests.fs @@ -52,33 +52,6 @@ SetupTesting.generateSetupScript __SOURCE_DIRECTORY__ #endif """ -[] -let ``should keep compiler directives, idempotent`` () = - formatSourceString - false - """ -#if INTERACTIVE -#else -#load "../FSharpx.TypeProviders/SetupTesting.fsx" -SetupTesting.generateSetupScript __SOURCE_DIRECTORY__ - -#load "__setup__.fsx" -#endif -""" - config - |> prepend newline - |> should - equal - """ -#if INTERACTIVE -#else -#load "../FSharpx.TypeProviders/SetupTesting.fsx" -SetupTesting.generateSetupScript __SOURCE_DIRECTORY__ - -#load "__setup__.fsx" -#endif -""" - [] let ``line, file and path identifiers`` () = formatSourceString diff --git a/src/Fantomas.Core.Tests/ControlStructureTests.fs b/src/Fantomas.Core.Tests/ControlStructureTests.fs index ebce6cda87..56f4de4d36 100644 --- a/src/Fantomas.Core.Tests/ControlStructureTests.fs +++ b/src/Fantomas.Core.Tests/ControlStructureTests.fs @@ -826,35 +826,6 @@ with false """ -[] -let ``comment above pipe of try/with, idempotent`` () = - formatSourceString - false - """ -try - let defaultTime = - (DateTime.FromFileTimeUtc 0L).ToLocalTime() - - foo.CreationTime <> defaultTime -with -// hmm -:? FileNotFoundException -> false -""" - config - |> prepend newline - |> should - equal - """ -try - let defaultTime = (DateTime.FromFileTimeUtc 0L).ToLocalTime() - - foo.CreationTime <> defaultTime -with -// hmm -| :? FileNotFoundException -> - false -""" - [] let ``comment above pipe of try/with named clause, 1686`` () = formatSourceString @@ -890,40 +861,6 @@ module Foo = failwith "" """ -[] -let ``comment above pipe of try/with named clause, idempotent`` () = - formatSourceString - false - """ -namespace Foo - -module Foo = - let a = - try - failwith "" - with - // hi! - | :? Exception as e -> failwith "" -""" - { config with - SpaceBeforeColon = true - SpaceBeforeSemicolon = true } - |> prepend newline - |> should - equal - """ -namespace Foo - -module Foo = - let a = - try - failwith "" - with - // hi! - | :? Exception as e -> - failwith "" -""" - [] let ``respect IndentOnTryWith setting when there is trivia before SynMatchClause_Clause, 1647`` () = formatSourceString diff --git a/src/Fantomas.Core.Tests/DotSetTests.fs b/src/Fantomas.Core.Tests/DotSetTests.fs index 9985bf339c..9c4bc59e2c 100644 --- a/src/Fantomas.Core.Tests/DotSetTests.fs +++ b/src/Fantomas.Core.Tests/DotSetTests.fs @@ -31,12 +31,6 @@ let foo = """ { config with SpaceBeforeUppercaseInvocation = true } - |> fun formatted -> - formatSourceString - false - formatted - { config with - SpaceBeforeUppercaseInvocation = true } |> prepend newline |> should equal diff --git a/src/Fantomas.Core.Tests/KeepIndentInBranchTests.fs b/src/Fantomas.Core.Tests/KeepIndentInBranchTests.fs index 9a7570fdc1..b884d79457 100644 --- a/src/Fantomas.Core.Tests/KeepIndentInBranchTests.fs +++ b/src/Fantomas.Core.Tests/KeepIndentInBranchTests.fs @@ -1241,73 +1241,6 @@ module Foo = failwith "" """ -[] -let ``ifdef trivia should not influence outcome, idempotent`` () = - formatSourceString - false - """ -module Foo = - [] - let blah<'a> config : Type = -#if DEBUG - failwith "" -#endif - DoThing.doIt () - let result = Runner.Run<'a> config - - if successful |> List.isEmpty then - result - else - - let errors = - unsuccessful - |> List.filter (fun report -> - not report.BuildResult.IsBuildSuccess - || not report.BuildResult.IsGenerateSuccess - ) - |> List.map (fun report -> report.BuildResult.ErrorMessage) - - failwith "" -""" - { config with - SpaceBeforeUppercaseInvocation = true - SpaceBeforeClassConstructor = true - SpaceBeforeMember = true - SpaceBeforeColon = true - SpaceBeforeSemicolon = true - MultilineBracketStyle = Aligned - AlignFunctionSignatureToIndentation = true - AlternativeLongMemberDefinitions = true - MultiLineLambdaClosingNewline = true - ExperimentalKeepIndentInBranch = true } - |> prepend newline - |> should - equal - """ -module Foo = - [] - let blah<'a> config : Type = -#if DEBUG - failwith "" -#endif - DoThing.doIt () - let result = Runner.Run<'a> config - - if successful |> List.isEmpty then - result - else - - let errors = - unsuccessful - |> List.filter (fun report -> - not report.BuildResult.IsBuildSuccess - || not report.BuildResult.IsGenerateSuccess - ) - |> List.map (fun report -> report.BuildResult.ErrorMessage) - - failwith "" -""" - [] let ``multiple nested Sequential expressions, 1714`` () = formatSourceString diff --git a/src/Fantomas.Core.Tests/LambdaTests.fs b/src/Fantomas.Core.Tests/LambdaTests.fs index 0ddfb3c8d4..720f7d5cee 100644 --- a/src/Fantomas.Core.Tests/LambdaTests.fs +++ b/src/Fantomas.Core.Tests/LambdaTests.fs @@ -1261,44 +1261,6 @@ Task.Run(task) |> ignore> """ -[] -let ``don't add space before linq lambda and idempotent, 2231`` () = - formatSourceString - false - """ -open System.Linq - -type Item() = - member val ValidFrom = DateTime.MinValue - member val Value = 23.42m - -let items = [ Item(); Item(); Item() ] - -let firstOrDef = items.FirstOrDefault(fun x -> - x.ValidFrom <= DateTime.Now || x.ValidFrom > DateTime.Now).Value -""" - { config with - MaxInfixOperatorExpression = 50 } - |> prepend newline - |> should - equal - """ -open System.Linq - -type Item() = - member val ValidFrom = DateTime.MinValue - member val Value = 23.42m - -let items = [ Item(); Item(); Item() ] - -let firstOrDef = - items - .FirstOrDefault(fun x -> - x.ValidFrom <= DateTime.Now - || x.ValidFrom > DateTime.Now) - .Value -""" - [] let ``lambda with long list of arguments`` () = formatSourceString diff --git a/src/Fantomas.Core.Tests/MultiLineLambdaClosingNewlineTests.fs b/src/Fantomas.Core.Tests/MultiLineLambdaClosingNewlineTests.fs index 6d9ff37398..6a0b7a6682 100644 --- a/src/Fantomas.Core.Tests/MultiLineLambdaClosingNewlineTests.fs +++ b/src/Fantomas.Core.Tests/MultiLineLambdaClosingNewlineTests.fs @@ -609,52 +609,6 @@ module Foo = y """ -[] -let ``comment before paren function arg, idempotent`` () = - formatSourceString - false - """ -namespace Bar - -[] -module Foo = - /// Blah - let bang<'a when 'a : equality> (a : Foo<'a>) (ans : ('a * System.TimeSpan) list) : bool = - List.length x = List.length y - && List.forall2 - // - (fun (a, ta) (b, tb) -> a.Equals b && ta = tb) - x - y -""" - { config with - MaxLineLength = 100 - SpaceBeforeUppercaseInvocation = true - SpaceBeforeClassConstructor = true - SpaceBeforeMember = true - SpaceBeforeColon = true - SpaceBeforeSemicolon = true - MultilineBracketStyle = Aligned - MultiLineLambdaClosingNewline = true - ExperimentalKeepIndentInBranch = true } - |> prepend newline - |> should - equal - """ -namespace Bar - -[] -module Foo = - /// Blah - let bang<'a when 'a : equality> (a : Foo<'a>) (ans : ('a * System.TimeSpan) list) : bool = - List.length x = List.length y - && List.forall2 - // - (fun (a, ta) (b, tb) -> a.Equals b && ta = tb) - x - y -""" - [] let ``multiline infix application with piped match expression`` () = formatSourceString diff --git a/src/Fantomas.Core.Tests/NewlineBetweenTypeDefinitionAndMembersTests.fs b/src/Fantomas.Core.Tests/NewlineBetweenTypeDefinitionAndMembersTests.fs index 467096c7c3..d88c1bbae1 100644 --- a/src/Fantomas.Core.Tests/NewlineBetweenTypeDefinitionAndMembersTests.fs +++ b/src/Fantomas.Core.Tests/NewlineBetweenTypeDefinitionAndMembersTests.fs @@ -119,26 +119,6 @@ type Color = member this.ToInt = () """ -[] -let ``type abbreviation with members`` () = - formatSourceString - false - """ -type A = string -with - member this.X = () -""" - config - |> prepend newline - |> should - equal - """ -type A = string - with - - member this.X = () -""" - [] let ``type augmentation with members`` () = formatSourceString diff --git a/src/Fantomas.Core.Tests/Stroustrup/ExperimentalElmishTests.fs b/src/Fantomas.Core.Tests/Stroustrup/ExperimentalElmishTests.fs index 18fec7ab26..6382fb2691 100644 --- a/src/Fantomas.Core.Tests/Stroustrup/ExperimentalElmishTests.fs +++ b/src/Fantomas.Core.Tests/Stroustrup/ExperimentalElmishTests.fs @@ -1015,51 +1015,6 @@ let html = ] """ -[] -let ``don't repeat comment in nested Elmish element, idempotent check`` () = - formatSourceString - false - """ -let html = - Html.div [ - prop.className "navbar-menu" - prop.children [ - Html.div [ - prop.className "navbar-start" - prop.children [ - Html.a [ prop.className "navbar-item" ] - (* - Html.a [ prop.className "navbar-item"; prop.href (baseUrl +/ "Files") ] [ - prop.text "Files" - ]*) - ] - ] - ] - ] -""" - config - |> prepend newline - |> should - equal - """ -let html = - Html.div [ - prop.className "navbar-menu" - prop.children [ - Html.div [ - prop.className "navbar-start" - prop.children [ - Html.a [ prop.className "navbar-item" ] - (* - Html.a [ prop.className "navbar-item"; prop.href (baseUrl +/ "Files") ] [ - prop.text "Files" - ]*) - ] - ] - ] - ] -""" - [] let ``don't repeat comment in nested Elmish element, single element mode`` () = formatSourceString @@ -1107,51 +1062,6 @@ let html = ] """ -[] -let ``don't repeat comment in nested Elmish element, single element mode idempotent`` () = - formatSourceString - false - """ -let html = - Html.div [ - prop.className "navbar-menu" - prop.children [ - Html.div [ - prop.className "navbar-start" - prop.children [ - Html.a [ prop.className "navbar-item" ] - (* - Html.a [ prop.className "navbar-item"; prop.href (baseUrl +/ "Files") ] [ - prop.text "Files" - ]*) - ] - ] - ] - ] -""" - config - |> prepend newline - |> should - equal - """ -let html = - Html.div [ - prop.className "navbar-menu" - prop.children [ - Html.div [ - prop.className "navbar-start" - prop.children [ - Html.a [ prop.className "navbar-item" ] - (* - Html.a [ prop.className "navbar-item"; prop.href (baseUrl +/ "Files") ] [ - prop.text "Files" - ]*) - ] - ] - ] - ] -""" - [] let ``don't repeat comment in nested Elmish element, short block comment`` () = formatSourceString diff --git a/src/Fantomas.Core.Tests/TestHelpers.fs b/src/Fantomas.Core.Tests/TestHelpers.fs index eb0d935249..13bae076e5 100644 --- a/src/Fantomas.Core.Tests/TestHelpers.fs +++ b/src/Fantomas.Core.Tests/TestHelpers.fs @@ -19,12 +19,19 @@ let newline = "\n" let formatSourceString isFsiFile (s: string) config = async { let! formatted = CodeFormatter.FormatDocumentAsync(isFsiFile, s, config) - let! isValid = CodeFormatter.IsValidFSharpCodeAsync(isFsiFile, formatted.Code) + let formattedCode = formatted.Code.Replace("\r\n", "\n") + let! isValid = CodeFormatter.IsValidFSharpCodeAsync(isFsiFile, formattedCode) if not isValid then - failwithf $"The formatted result is not valid F# code or contains warnings\n%s{formatted.Code}" + failwithf $"The formatted result is not valid F# code or contains warnings\n%s{formattedCode}" + + let! secondFormat = CodeFormatter.FormatDocumentAsync(isFsiFile, formattedCode, config) + let secondFormattedCode = secondFormat.Code.Replace("\r\n", "\n") + + if formattedCode <> secondFormattedCode then + failwithf $"The formatted result was not idempotent.\n%s{formattedCode}" - return formatted.Code.Replace("\r\n", "\n") + return formattedCode } |> Async.RunSynchronously From d8e7276795204e9e8a06d3974bf187fe8d60b0f0 Mon Sep 17 00:00:00 2001 From: GamerZero Date: Tue, 6 Jun 2023 16:07:07 +0300 Subject: [PATCH 17/18] Fix idempotency problem when reformatting type declaration with large when clause (#2897) * Fix idempotency problem when reformatting type declaration with large when clause * Update unit test. * Add 6.0.5 release version --------- Co-authored-by: Florian Verdonck --- CHANGELOG.md | 5 ++++ .../FunctionDefinitionTests.fs | 27 +++++++++++++++++++ src/Fantomas.Core/CodePrinter.fs | 8 +----- 3 files changed, 33 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 016611ed1c..56c61eea7a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## [6.0.5] - 2023-06-06 + +### Fixed +* Idempotency problem when reformatting type declaration with large when clause. [#2896](https://github.com/fsprojects/fantomas/issues/2896) + ## [6.0.4] - 2023-06-02 ### Changed diff --git a/src/Fantomas.Core.Tests/FunctionDefinitionTests.fs b/src/Fantomas.Core.Tests/FunctionDefinitionTests.fs index e521cc4103..2085cc56d9 100644 --- a/src/Fantomas.Core.Tests/FunctionDefinitionTests.fs +++ b/src/Fantomas.Core.Tests/FunctionDefinitionTests.fs @@ -1353,3 +1353,30 @@ let inline repro (a: ^``QuotedWithIllegalChar<'T>``) = () """ let inline repro (a: ^``QuotedWithIllegalChar<'T>``) = () """ + +[] +let ``multiline member constraints on type parameters, 2896`` () = + formatSourceString + false + """ +let inline func + (arg: + 'a when 'a: (member a: int) and 'a: (member b: int) and 'a: (member c: int) and 'a: (member d: int) and 'a: (member e: int)) + = 0 + """ + config + |> prepend newline + |> should + equal + """ +let inline func + (arg: + 'a + when 'a: (member a: int) + and 'a: (member b: int) + and 'a: (member c: int) + and 'a: (member d: int) + and 'a: (member e: int)) + = + 0 +""" diff --git a/src/Fantomas.Core/CodePrinter.fs b/src/Fantomas.Core/CodePrinter.fs index 5bd47fe009..f0930b3681 100644 --- a/src/Fantomas.Core/CodePrinter.fs +++ b/src/Fantomas.Core/CodePrinter.fs @@ -3062,13 +3062,7 @@ let genType (t: Type) = +> genSingleTextNode node.ClosingParen |> genNode node | Type.WithSubTypeConstraint tc -> genTypeConstraint tc - | Type.WithGlobalConstraints node -> - leadingExpressionIsMultiline (genType node.Type) (fun isMultiline -> - if isMultiline then - indentSepNlnUnindent (genTypeConstraints node.TypeConstraints) - else - sepSpaceOrIndentAndNlnIfExpressionExceedsPageWidth (genTypeConstraints node.TypeConstraints)) - |> genNode node + | Type.WithGlobalConstraints node -> genType node.Type +> genTypeConstraints node.TypeConstraints |> genNode node | Type.LongIdent idn -> genIdentListNode idn | Type.AnonRecord node -> let genStruct = From 4f2f9c1e3dc89240707970ff62d2713ea1c91c71 Mon Sep 17 00:00:00 2001 From: Florian Verdonck Date: Thu, 8 Jun 2023 15:47:44 +0200 Subject: [PATCH 18/18] Add cursor regression test. (#2899) --- src/Fantomas.Core.Tests/CursorTests.fs | 27 ++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/Fantomas.Core.Tests/CursorTests.fs b/src/Fantomas.Core.Tests/CursorTests.fs index 08d645e1e0..8667d8028b 100644 --- a/src/Fantomas.Core.Tests/CursorTests.fs +++ b/src/Fantomas.Core.Tests/CursorTests.fs @@ -44,3 +44,30 @@ let ``cursor inside a node between defines`` () = """ (3, 4) |> assertCursor (2, 0) + +[] +let ``cursor after try keyword`` () = + formatWithCursor + """ +namespace JetBrains.ReSharper.Plugins.FSharp.Services.Formatter + +[] +type FSharpReformatCode(textControlManager: ITextControlManager) = + member x.Process(sourceFile, rangeMarker, _, _, _) = + if isNotNull rangeMarker then + try + let range = ofDocumentRange rangeMarker.DocumentRange + let formatted = fantomasHost.FormatSelection(filePath, range, text, settings, parsingOptions, newLineText) + let offset = rangeMarker.DocumentRange.StartOffset.Offset + let oldLength = rangeMarker.DocumentRange.Length + let documentChange = DocumentChange(document, offset, oldLength, formatted, stamp, modificationSide) + use _ = WriteLockCookie.Create() + document.ChangeDocument(documentChange, TimeStamp.NextValue) + sourceFile.GetPsiServices().Files.CommitAllDocuments() + with _ -> () + else + let textControl = textControlManager.VisibleTextControls |> Seq.find (fun c -> c.Document == document) + cursorPosition = textControl.Caret.Position.Value.ToDocLineColumn(); +""" + (8, 19) + |> assertCursor (7, 15)