diff --git a/README.md b/README.md index c7479c425..ec68375c3 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,10 @@ This repository is prepared to use Gitpod for a web-based VSCode-style IDE. Clic [![Gitpod Ready-to-Code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/fsharp/fsautocomplete) +### Creating a new code fix + +Checkout [this guide](./docs/Creating%20a%20new%20code%20fix.md) to start with a new code fix. + ## Releasing * Update CHANGELOG.md with the release notes from the current release in the `Unreleased` section. Use section headings like `Added`, `Fixed`, etc from keepachangelog.com. diff --git a/build/Program.fs b/build/Program.fs index 9584e62ca..55afc5000 100644 --- a/build/Program.fs +++ b/build/Program.fs @@ -71,14 +71,13 @@ let init args = "reportgenerator" "-reports:test/FsAutoComplete.Tests.Lsp/coverage.xml -reporttypes:Html;HtmlSummary -targetdir:./coverage" |> fun r -> - if not r.OK then - failwithf "Errors while generating coverage report: %A" r.Errors) + if not r.OK then + failwithf "Errors while generating coverage report: %A" r.Errors) Target.create "ReleaseArchive" (fun _ -> Directory.ensure pkgsDir - !!(toolsDir "fsautocomplete.*.nupkg") - |> Shell.copy pkgsDir) + !!(toolsDir "fsautocomplete.*.nupkg") |> Shell.copy pkgsDir) Target.create "LocalRelease" (fun _ -> Directory.ensure toolsDir @@ -89,7 +88,9 @@ let init args = { p with OutputPath = Some(__SOURCE_DIRECTORY__ ".." toolsDir) Configuration = DotNet.BuildConfiguration.fromString configuration - MSBuildParams = { MSBuild.CliArguments.Create() with Properties = [ packAsToolProp ] } }) + MSBuildParams = + { MSBuild.CliArguments.Create() with + Properties = [ packAsToolProp ] } }) "src/FsAutoComplete") Target.create "Clean" (fun _ -> Shell.cleanDirs [ buildDir; buildReleaseDir; pkgsDir; toolsDir ]) @@ -98,7 +99,9 @@ let init args = Target.create "Build" (fun _ -> DotNet.build - (fun p -> { p with Configuration = DotNet.BuildConfiguration.fromString configuration }) + (fun p -> + { p with + Configuration = DotNet.BuildConfiguration.fromString configuration }) "FsAutoComplete.sln") Target.create "EnsureRepoConfig" (fun _ -> @@ -170,25 +173,35 @@ let init args = Target.create "Promote" ignore + Target.create "ScaffoldCodeFix" (fun ctx -> + let codeFixName = ctx.Context.Arguments |> List.tryHead + + match codeFixName with + | None -> failwith "Usage: dotnet run --project ./build/build.fsproj -- -t ScaffoldCodeFix " + | Some codeFixName -> ScaffoldCodeFix.scaffold codeFixName) + + Target.create "EnsureCanScaffoldCodeFix" (fun _ -> ScaffoldCodeFix.ensureScaffoldStillWorks ()) + "PromoteUnreleasedToVersion" ==> "CreateVersionTag" ==> "Promote" |> ignore "Restore" ==> "Build" |> ignore - "Build" ==> "LspTest" ==> "Coverage" ==> "Test" ==> "All" + "Build" + ==> "EnsureCanScaffoldCodeFix" + ==> "LspTest" + ==> "Coverage" + ==> "Test" + ==> "All" |> ignore - "Clean" - ==> "LocalRelease" - ==> "ReleaseArchive" - ==> "Release" - |> ignore + "Clean" ==> "LocalRelease" ==> "ReleaseArchive" ==> "Release" |> ignore "ReleaseArchive" ==> "All" |> ignore [] let main args = - init ((args |> List.ofArray)) + init (args |> List.ofArray) try Target.runOrDefaultWithArguments "ReleaseArchive" diff --git a/build/ScaffoldCodeFix.fs b/build/ScaffoldCodeFix.fs new file mode 100644 index 000000000..86e671107 --- /dev/null +++ b/build/ScaffoldCodeFix.fs @@ -0,0 +1,461 @@ +module ScaffoldCodeFix + +open System +open System.IO +open Fake.Core +open Fake.IO.FileSystemOperators +open Fantomas.Core.SyntaxOak + +let repositoryRoot = __SOURCE_DIRECTORY__ ".." + +let AdaptiveFSharpLspServerPath = + repositoryRoot + "src" + "FsAutoComplete" + "LspServers" + "AdaptiveFSharpLspServer.fs" + +let FsAutoCompleteLspPath = + repositoryRoot + "src" + "FsAutoComplete" + "LspServers" + "FsAutoComplete.Lsp.fs" + +let TestsPath = + repositoryRoot + "test" + "FsAutoComplete.Tests.Lsp" + "CodeFixTests" + "Tests.fs" + +let removeReturnCarriage (v: string) = v.Replace("\r", "") + +let mkCodeFixImplementation codeFixName = + let path = + repositoryRoot + "src" + "FsAutoComplete" + "CodeFixes" + $"{codeFixName}.fs" + + let content = + $"""module FsAutoComplete.CodeFix.%s{codeFixName} + +open FSharp.Compiler.Symbols +open FSharp.Compiler.Syntax +open FsToolkit.ErrorHandling +open Ionide.LanguageServerProtocol.Types +open FsAutoComplete.CodeFix.Types +open FsAutoComplete +open FsAutoComplete.LspHelpers + +// The syntax tree can be an intimidating set of types to work with. +// It is a tree structure but it consists out of many different types. +// See https://fsharp.github.io/fsharp-compiler-docs/reference/fsharp-compiler-syntax.html +// It can be useful to inspect a syntax tree via a code sample using https://fsprojects.github.io/fantomas-tools/#/ast +// For example `let a b c = ()` in +// https://fsprojects.github.io/fantomas-tools/#/ast?data=N4KABGBEAmCmBmBLAdrAzpAXFSAacUiaAYmolmPAIYA2as%%2BEkAxgPZwWQ2wAuYVYAEZhmYALxgAFAEo8BSLAAeAByrJoFHgCcArrBABfIA +// Let's say we want to find the (FCS) range for identifier `a`. +let visitSyntaxTree + (cursor: FSharp.Compiler.Text.pos) + (tree: ParsedInput) + = + // We will use a syntax visitor to traverse the tree from the top to the node of interest. + // See https://github.com/dotnet/fsharp/blob/main/src/Compiler/Service/ServiceParseTreeWalk.fsi + // We implement the different members of interest and allow the default traversal to move to the lower levels we care about. + let visitor = + // A visitor will report the first item it finds. + // Think of it as `List.tryPick` + // It is not an ideal solution to find all nodes inside a tree, be aware of that. + // For example finding all function names. + {{ new SyntaxVisitorBase() with + // We know that `a` will be part of a `SynPat.LongIdent` + // This was visible in the online tool. + member _.VisitPat(path, defaultTraverse, synPat) = + match synPat with + | SynPat.LongIdent(longDotId = SynLongIdent(id = [ functionNameIdent ])) -> + // When our code fix operates on the user's code there is no way of knowing what will be inside the syntax tree. + // So we need to be careful and verify that the pattern is indeed matching the position of the cursor. + if FSharp.Compiler.Text.Range.rangeContainsPos functionNameIdent.idRange cursor then + Some functionNameIdent.idRange + else + None + | _ -> None }} + + // Invoke the visitor and kick off the traversal. + SyntaxTraversal.Traverse(cursor, tree, visitor) + +// TODO: add proper title for code fix +let title = "%s{codeFixName} Codefix" + +let fix + (getParseResultsForFile: GetParseResultsForFile) + : CodeFix = + fun (codeActionParams: CodeActionParams) -> + asyncResult {{ + // Most code fixes have some general setup. + // We initially want to detect the state of the current code and whether we can propose any text edits to the user. + + let fileName = codeActionParams.TextDocument.GetFilePath() |> Utils.normalizePath + // The converted LSP start position to an FCS start position. + let fcsPos = protocolPosToPos codeActionParams.Range.Start + // The syntax tree and typed tree, current line and sourceText of the current file. + let! (parseAndCheckResults:ParseAndCheckResults, line:string, sourceText:IFSACSourceText) = + getParseResultsForFile fileName fcsPos + + // As an example, we want to check whether the users cursor is inside a function definition name. + // We will traverse the syntax tree to verify this is the case. + match visitSyntaxTree fcsPos parseAndCheckResults.GetParseResults.ParseTree with + | None -> + // The cursor is not in a position we are interested in. + // This code fix should not trigger any suggestions so we return an empty list. + return [] + | Some mBindingName -> + // It turns out we are inside a let binding and we have the range of the function name. + // Just for fun, we want to detect if there is a matching typed tree symbol present for the current name. + // We could have passed the function name from the syntax visitor, instead will we grab it from the source text. + let! functionName = sourceText.GetText mBindingName + // FSharpSymbolUse is reflecting the typed tree. + // See https://fsharp.github.io/fsharp-compiler-docs/fcs/symbols.html + let symbolUse: FSharp.Compiler.CodeAnalysis.FSharpSymbolUse option = + parseAndCheckResults.GetCheckResults.GetSymbolUseAtLocation(mBindingName.EndLine, mBindingName.EndColumn, line, [ functionName ]) + + let hasFunctionDefinitionSymbol = + match symbolUse with + | None -> false + | Some symbolUse -> + // We want to verify the found symbol is indeed a definition of a function + match symbolUse.Symbol with + | :? FSharpMemberOrFunctionOrValue -> true + | _ -> false + + if not hasFunctionDefinitionSymbol then + return [] + else + // Return a list of Fix records for when the code fix is applicable. + return [ + {{ + SourceDiagnostic = None + Title = title + File = codeActionParams.TextDocument + // Based on conditional logic, you typically want to suggest a text edit to the user. + Edits = [| + {{ + // When dealing with FCS, we typically want to use the FCS flavour of range. + // However, to interact correctly with the LSP protocol, we need to return an LSP range. + Range = fcsRangeToLsp mBindingName + NewText = "Text replaced by %s{codeFixName}" + }} + |] + Kind = FixKind.Fix + }} + ] + }} +""" + + File.WriteAllText(path, removeReturnCarriage content) + Trace.tracefn $"Generated %s{Path.GetRelativePath(repositoryRoot, path)}" + +let mkCodeFixSignature codeFixName = + let path = + repositoryRoot + "src" + "FsAutoComplete" + "CodeFixes" + $"{codeFixName}.fsi" + + let content = + $"""module FsAutoComplete.CodeFix.%s{codeFixName} + +open FsAutoComplete.CodeFix.Types + +val title: string +val fix: getParseResultsForFile: GetParseResultsForFile -> CodeFix +""" + + File.WriteAllText(path, removeReturnCarriage content) + Trace.tracefn $"Generated %s{Path.GetRelativePath(repositoryRoot, path)}" + +let updateProjectFiles () = + let fsAutoCompleteProject = + repositoryRoot "src" "FsAutoComplete" "FsAutoComplete.fsproj" + + File.SetLastWriteTime(fsAutoCompleteProject, DateTime.Now) + + let fsAutoCompleteTestsLsp = + repositoryRoot + "test" + "FsAutoComplete.Tests.Lsp" + "FsAutoComplete.Tests.Lsp.fsproj" + + File.SetLastWriteTime(fsAutoCompleteTestsLsp, DateTime.Now) + +let (|IdentName|_|) (name: string) (identListNode: IdentListNode) = + match identListNode.Content with + | [ IdentifierOrDot.Ident stn ] when stn.Text = name -> Some() + | _ -> None + +let getOakFor path = + let content = File.ReadAllText path + + Fantomas.Core.CodeFormatter.ParseOakAsync(false, content) + |> Async.RunSynchronously + |> Array.head + |> fst + +let appendItemToArrayOrList item path (node: ExprArrayOrListNode) = + let lastElement = node.Elements |> List.last |> Expr.Node + let startIndent = lastElement.Range.StartColumn + let lineIdx = lastElement.Range.EndLine - 1 + let arrayEndsOnLastElement = node.Range.EndLine = lastElement.Range.EndLine + + let updatedLines = + let lines = File.ReadAllLines path + let currentLastLine = lines.[lineIdx] + let spaces = String.replicate startIndent " " + + if arrayEndsOnLastElement then + let endOfLastElement = currentLastLine.Substring(0, lastElement.Range.EndColumn) + let endOfArray = currentLastLine.Substring(lastElement.Range.EndColumn) + + lines + |> Array.updateAt lineIdx $"{endOfLastElement}\n%s{spaces}%s{item}%s{endOfArray}" + else + lines |> Array.insertAt (lineIdx + 1) $"%s{spaces}%s{item}" + + let content = String.concat "\n" updatedLines + File.WriteAllText(path, content) + Trace.tracefn $"Added \"%s{item}\" to %s{Path.GetRelativePath(repositoryRoot, path)}" + +module List = + let exactlyOneOrFail (message: string) (items: 'T list) : 'T = + if items.Length = 1 then items.Head else failwith message + + let pickOrFail (message: string) (chooser: 'T -> 'U option) (items: 'T list) : 'U = + match List.tryPick chooser items with + | None -> failwith message + | Some u -> u + +let findArrayOrListOfFail (e: Expr) = + match e with + | Expr.ArrayOrList array -> array + | e -> failwithf $"Expected to find Expr.ArrayOrList, got %A{e}" + +let findTypeWithNameOfFail (typeName: string) (mn: ModuleOrNamespaceNode) : ITypeDefn = + mn.Declarations + |> List.pickOrFail $"Expected to find ModuleDecl.TypeDefn for %s{typeName}" (function + | ModuleDecl.TypeDefn t -> + let tdn = TypeDefn.TypeDefnNode t + + match tdn.TypeName.Identifier with + | IdentName typeName -> Some tdn + | _ -> None + | _ -> None) + +let findArrayInAdaptiveFSharpLspServer () : ExprArrayOrListNode = + let oak = getOakFor AdaptiveFSharpLspServerPath + + // namespace FsAutoComplete.Lsp + let ns = + oak.ModulesOrNamespaces + |> List.exactlyOneOrFail "Expected a single namespace in Oak." + + // type AdaptiveFSharpLspServer + let t = findTypeWithNameOfFail "AdaptiveFSharpLspServer" ns + + // let codefixes = + let codefixesValue = + t.Members + |> List.pickOrFail "Expected to find MemberDefn.LetBinding for codefixes" (function + | MemberDefn.LetBinding bindingList -> + match bindingList.Bindings with + | bindings -> + bindings + |> List.tryPick (fun binding -> + match binding.FunctionName with + | Choice1Of2(IdentName "codefixes") -> Some binding + | _ -> None) + | _ -> None) + + let infixApp = + match codefixesValue.Expr with + | Expr.CompExprBody body -> + match List.last body.Statements with + | ComputationExpressionStatement.OtherStatement other -> + match other with + | Expr.InfixApp infixApp -> infixApp + | e -> failwithf $"Expected to find Expr.InfixApp, got %A{e}" + | ces -> failwithf $"Expected to find ComputationExpressionStatement.OtherStatement, got %A{ces}" + | e -> failwithf $"Expected to find Expr.CompExprBody, got %A{e}" + + let appWithLambda = + match infixApp.RightHandSide with + | Expr.AppWithLambda appWithLambda -> appWithLambda + | e -> failwithf $"Expected to find Expr.AppWithLambda, got %A{e}" + + let lambda = + match appWithLambda.Lambda with + | Choice1Of2 lambda -> lambda + | Choice2Of2 ml -> failwithf $"Expected to find ExprLambdaNode, got %A{ml}" + + findArrayOrListOfFail lambda.Expr + +let wireCodeFixInAdaptiveFSharpLspServer codeFixName = + try + let array = findArrayInAdaptiveFSharpLspServer () + + appendItemToArrayOrList $"%s{codeFixName}.fix tryGetParseResultsForFile" AdaptiveFSharpLspServerPath array + with ex -> + Trace.traceException ex + + Trace.traceError + $"Unable to find array of codefixes in %s{AdaptiveFSharpLspServerPath}.\nDid the code structure change?" + +let findArrayInFsAutoCompleteLsp () = + let oak = getOakFor FsAutoCompleteLspPath + // namespace FsAutoComplete.Lsp + let ns = + oak.ModulesOrNamespaces + |> List.exactlyOneOrFail "Expected a single namespace in Oak." + + // type AdaptiveFSharpLspServer + let t = findTypeWithNameOfFail "FSharpLspServer" ns + + // interface IFSharpLspServer with + let iFSharpLspServer = + t.Members + |> List.pick (function + | MemberDefn.Interface i -> Some i + | _ -> None) + + // override _.Initialize(p: InitializeParams) = + let overrideMember = + iFSharpLspServer.Members + |> List.pick (function + | MemberDefn.Member mb -> + match mb.FunctionName with + | Choice1Of2 iln -> + match iln.Content with + | [ _; _; IdentifierOrDot.Ident ident ] when ident.Text = "Initialize" -> Some mb + | _ -> None + | Choice2Of2 _ -> None + | _ -> None) + + let asyncComp = + match overrideMember.Expr with + | Expr.NamedComputation namedComputation -> namedComputation + | e -> failwithf $"Expected Expr.NamedComputation, got %A{e}" + + let compBody = + match asyncComp.Body with + | Expr.CompExprBody body -> body + | e -> failwithf $"Expected Expr.CompExprBody, got %A{e}" + + compBody.Statements + |> List.pickOrFail "Expected to find ComputationExpressionStatement.OtherStatement" (function + | ComputationExpressionStatement.OtherStatement(Expr.LongIdentSet longIdentSet) -> + match longIdentSet.Identifier with + | IdentName "codefixes" -> + match longIdentSet.Expr with + | Expr.ArrayOrList array -> Some array + | _ -> None + | _ -> None + | _ -> None) + +let wireCodeFixInFsAutoCompleteLsp codeFixName = + try + let array = findArrayInFsAutoCompleteLsp () + appendItemToArrayOrList $"%s{codeFixName}.fix tryGetParseResultsForFile" FsAutoCompleteLspPath array + with ex -> + Trace.traceException ex + Trace.traceError $"Unable to find array of codefixes in %s{FsAutoCompleteLspPath}.\nDid the code structure change?" + +let mkCodeFixTests codeFixName = + let path = + repositoryRoot + "test" + "FsAutoComplete.Tests.Lsp" + "CodeFixTests" + $"%s{codeFixName}Tests.fs" + + let contents = + $"module private FsAutoComplete.Tests.CodeFixTests.%s{codeFixName}Tests + +open Expecto +open Helpers +open Utils.ServerTests +open Utils.CursorbasedTests +open FsAutoComplete.CodeFix + +let tests state = + serverTestList (nameof %s{codeFixName}) state defaultConfigDto None (fun server -> + [ let selectCodeFix = CodeFix.withTitle %s{codeFixName}.title + + ftestCaseAsync \"first unit test for %s{codeFixName}\" + <| CodeFix.check + server + \"let a$0 b c = ()\" + Diagnostics.acceptAll + selectCodeFix + \"let Text replaced by %s{codeFixName} b c = ()\" + ]) +" + + File.WriteAllText(path, removeReturnCarriage contents) + Trace.tracefn $"Generated %s{Path.GetRelativePath(repositoryRoot, path)}" + +let findListInTests () = + let oak = getOakFor TestsPath + // module FsAutoComplete.Tests.CodeFixTests.Tests + let testsModule = + oak.ModulesOrNamespaces + |> List.exactlyOneOrFail "Expected a single module in Oak." + + // let tests state = + let testBinding = + testsModule.Declarations + |> List.pickOrFail "Expected to find ModuleDecl.TopLevelBinding for tests" (function + | ModuleDecl.TopLevelBinding binding -> + match binding.FunctionName with + | Choice1Of2(IdentName "tests") -> Some binding + | _ -> None + | _ -> None) + + let appNode = + match testBinding.Expr with + | Expr.App appNode -> appNode + | e -> failwithf $"Expected Expr.App, got %A{e}" + + findArrayOrListOfFail (List.last appNode.Arguments) + +let wireCodeFixTests codeFixName = + try + let list = findListInTests () + appendItemToArrayOrList $"%s{codeFixName}Tests.tests state" TestsPath list + with ex -> + Trace.traceException ex + Trace.traceError $"Unable to find array of tests in %s{TestsPath}.\nDid the code structure change?" + +let scaffold (codeFixName: string) : unit = + // generate files in src/CodeFixes/ + mkCodeFixImplementation codeFixName + mkCodeFixSignature codeFixName + + // Wire up codefix to LSP servers + wireCodeFixInAdaptiveFSharpLspServer codeFixName + wireCodeFixInFsAutoCompleteLsp codeFixName + + // Add test file + mkCodeFixTests codeFixName + + // Wire up tests in test/FsAutoComplete.Tests.Lsp/CodeFixTests/Tests.fs + wireCodeFixTests codeFixName + + updateProjectFiles () + Trace.tracefn $"Scaffolding %s{codeFixName} complete!" + +let ensureScaffoldStillWorks () = + findArrayInAdaptiveFSharpLspServer () |> ignore + findArrayInFsAutoCompleteLsp () |> ignore + findListInTests () |> ignore diff --git a/build/ScaffoldCodeFix.fsi b/build/ScaffoldCodeFix.fsi new file mode 100644 index 000000000..fc48f497d --- /dev/null +++ b/build/ScaffoldCodeFix.fsi @@ -0,0 +1,14 @@ +module ScaffoldCodeFix + +/// Scaffold a new CodeFix by: +/// - Generating the implementation and signature files. +/// - Wire up the codefix AdaptiveFSharpLspServer.fs and FsAutoComplete.Lsp.fs. +/// - Generate a tests file with a focused test. +/// - Wire up the tests file. +/// - Update the last write time the project files. +val scaffold: codeFixName: string -> unit + +/// Verifies that the code fix scaffold target can still wire up a new codefix to the existing list. +/// Throws when any expected AST nodes can no longer be found. +/// If this code throws, you may need to revisit ScaffoldCodeFix.fs to tweak any recent changes. +val ensureScaffoldStillWorks: unit -> unit diff --git a/build/build.fsproj b/build/build.fsproj index 5525ec55c..129f74bc8 100644 --- a/build/build.fsproj +++ b/build/build.fsproj @@ -5,6 +5,8 @@ net7.0 + + diff --git a/build/paket.references b/build/paket.references index 84895ac7b..aa0513d00 100644 --- a/build/paket.references +++ b/build/paket.references @@ -15,3 +15,4 @@ group Build Microsoft.Build MSBuild.StructuredLogger Octokit + Fantomas.Core diff --git a/docs/Creating a new code fix.md b/docs/Creating a new code fix.md new file mode 100644 index 000000000..75778bb71 --- /dev/null +++ b/docs/Creating a new code fix.md @@ -0,0 +1,52 @@ +# Creating a New Code Fix + +A code fix, also referred to as a quick fix or code action, serves as a mechanism within the editor to propose and implement code changes within the current file. +This functionality is facilitated through the [Code Action Request](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_codeAction) provided by the Language Server Protocol (LSP). +You might associate code fixes with the concept of "light bulbs" found in certain integrated development environments (IDEs). + +To introduce a new code fix within the context of FSAutocomplete, there are several essential components to consider: + +1. **Code Fix File**: This pertains to the actual implementation of the code fix. + +2. **Registration in LSP Servers**: Registration of the code fix is required in both of the associated LSP servers. + +3. **Unit Test Setup**: Proper unit tests need to be established to ensure the correctness and effectiveness of the new code fix. + +To streamline the process of creating a new code fix, a convenient `FAKE` target has been provided. By executing the following command: + +```bash +dotnet run --project ./build/build.fsproj -- -t ScaffoldCodeFix YourCodeFixName +``` + +The above command accomplishes the following tasks: + +- Generation of three files: + - The implementation file for your code fix. + - A signature file associated with your code fix. + - A dedicated standalone unit test file. + +Furthermore, this command updates the following files to properly register the new code fix: + +- `src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs` +- `src/FsAutoComplete/LspServers/FsAutoComplete.Lsp.fs` +- `test/FsAutoComplete.Tests.Lsp/CodeFixTests/Tests.fs` + +The unit test file contains a [single focused test](https://github.com/haf/expecto#focusing-tests), allowing you to promptly verify the functionality. To run this initial test, you have two options: + +1. Using the `dotnet test` command: + ```bash +dotnet test -f net6.0 ./test/FsAutoComplete.Tests.Lsp/FsAutoComplete.Tests.Lsp.fsproj + ``` + +2. Alternatively, using the `dotnet run` command: + ```bash +dotnet run -f net6.0 --project ./test/FsAutoComplete.Tests.Lsp/FsAutoComplete.Tests.Lsp.fsproj + ``` + +This comprehensive approach ensures that the newly introduced code fix is properly integrated, tested, and ready for seamless integration into the FSAutocomplete environment. + +When preparing to submit a pull request, please consider the following guidelines: + +- Eliminate any extraneous code or comments that may remain from the sample code. +- Ensure proper source code formatting by running the command `dotnet fantomas src`. +- Avoid including focused tests, as they can cause the continuous integration build to fail. diff --git a/paket.dependencies b/paket.dependencies index e34f8d8f7..a604c2537 100644 --- a/paket.dependencies +++ b/paket.dependencies @@ -82,3 +82,4 @@ group Build nuget Microsoft.Build nuget MSBuild.StructuredLogger nuget Octokit 0.48 // there's an API break in 0.50, so we need to pin this to prevent errors + nuget Fantomas.Core 6.2.0 diff --git a/paket.lock b/paket.lock index 5e908fc45..91ba0bff7 100644 --- a/paket.lock +++ b/paket.lock @@ -613,6 +613,14 @@ NUGET Fake.Core.Trace (>= 5.23.1) Fake.IO.FileSystem (>= 5.23.1) FSharp.Core (>= 6.0) + Fantomas.Core (6.2) + Fantomas.FCS (>= 6.2) + FSharp.Core (>= 6.0.1) + Fantomas.FCS (6.2) + FSharp.Core (>= 6.0.1) + System.Diagnostics.DiagnosticSource (>= 7.0) + System.Memory (>= 4.5.5) + System.Runtime (>= 4.3.1) FParsec (1.1.1) FSharp.Core (>= 4.3.4) FSharp.Control.Reactive (5.0.5) @@ -647,6 +655,8 @@ NUGET System.Security.Principal.Windows (>= 5.0) - restriction: || (== net6.0) (&& (== net7.0) (>= net472)) System.Text.Encoding.CodePages (>= 7.0) - restriction: == net6.0 Microsoft.NET.StringTools (17.6.3) + Microsoft.NETCore.Platforms (7.0.4) + Microsoft.NETCore.Targets (5.0) Microsoft.VisualStudio.Setup.Configuration.Interop (3.6.2115) - restriction: || (&& (== net6.0) (>= net472)) (&& (== net6.0) (>= net7.0)) (== net7.0) Microsoft.Win32.Registry (5.0) System.Security.AccessControl (>= 5.0) @@ -678,17 +688,22 @@ NUGET System.Diagnostics.EventLog (>= 7.0) - restriction: || (&& (== net6.0) (>= net7.0)) (== net7.0) System.Security.Cryptography.ProtectedData (>= 7.0) System.Security.Permissions (>= 7.0) + System.Diagnostics.DiagnosticSource (7.0.2) + System.Runtime.CompilerServices.Unsafe (>= 6.0) - restriction: || (== net6.0) (&& (== net7.0) (>= net462)) (&& (== net7.0) (< net6.0)) System.Diagnostics.EventLog (7.0) - restriction: || (&& (== net6.0) (>= net7.0)) (== net7.0) System.Drawing.Common (7.0) Microsoft.Win32.SystemEvents (>= 7.0) System.Formats.Asn1 (6.0) - System.Memory (4.5.5) - restriction: || (== net6.0) (&& (== net7.0) (>= net472)) + System.Memory (4.5.5) System.Reactive (5.0) System.Reflection.Metadata (6.0.1) - restriction: || (&& (== net6.0) (>= net7.0)) (== net7.0) System.Collections.Immutable (>= 6.0) System.Reflection.MetadataLoadContext (6.0) - restriction: || (&& (== net6.0) (>= net472)) (&& (== net6.0) (>= net7.0)) (== net7.0) System.Collections.Immutable (>= 6.0) System.Reflection.Metadata (>= 6.0) + System.Runtime (4.3.1) + Microsoft.NETCore.Platforms (>= 1.1.1) + Microsoft.NETCore.Targets (>= 1.1.3) System.Runtime.CompilerServices.Unsafe (6.0) System.Security.AccessControl (6.0) - restriction: == net6.0 System.Security.Cryptography.Cng (5.0)