diff --git a/.github/workflows/bump-fcs.yml b/.github/workflows/bump-fcs.yml index cfbf40366..6a11213ba 100644 --- a/.github/workflows/bump-fcs.yml +++ b/.github/workflows/bump-fcs.yml @@ -23,6 +23,7 @@ jobs: - name: bump FCS run: dotnet paket update FSharp.Compiler.Service - name: check if there are changes + continue-on-error: true # nonzero exit codes are expected if there are changes id: check run: | git diff --exit-code diff --git a/CHANGELOG.md b/CHANGELOG.md index 818c44fef..771841b93 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,23 @@ # Changelog +## [0.59.2] - 2023-03-12 + +### Added + +* [A new codefix that converts 'bare' ///-comments to full XML documentation comments](https://github.com/fsharp/fsautocomplete/pull/1068) (thanks @dawedawe!) + +### Changed + +* [Enhancements to Find All References and Rename operations](https://github.com/fsharp/fsautocomplete/pull/1037) (thanks @BooksBaum and @theangrybyrd!) +* [Internal errors no longer report as LSP protocol errors](https://github.com/fsharp/fsautocomplete/pull/1069) +* [TestAdapterEntry items now include module information as well](https://github.com/fsharp/fsautocomplete/pull/1071) (thanks @kojo12228!) + +### Fixed + +* [IndexOutOfRange issue in signatureHelp](https://github.com/fsharp/fsautocomplete/pull/1067) (thanks @vain0x!) +* [ThreadPool exhaustion issue with ProgressListener](https://github.com/fsharp/fsautocomplete/pull/1070) (thanks @theangrybyrd!) +* [The 'convert positional DU usage to named patterns' codefix now works with multiple match clauses in the same pattern](https://github.com/fsharp/fsautocomplete/pull/1073) (thanks @dawedawe!) + ## [0.59.1] - 2023-02-26 ### Added diff --git a/src/FsAutoComplete.Core/Commands.fs b/src/FsAutoComplete.Core/Commands.fs index 5e753e46d..99544e501 100644 --- a/src/FsAutoComplete.Core/Commands.fs +++ b/src/FsAutoComplete.Core/Commands.fs @@ -58,9 +58,10 @@ type DocumentEdit = module private Result = let ofCoreResponse (r: CoreResponse<'a>) = match r with - | CoreResponse.Res a -> Ok a - | CoreResponse.ErrorRes msg - | CoreResponse.InfoRes msg -> Error msg + | CoreResponse.Res a -> Ok(Some a) + | CoreResponse.InfoRes _ -> Ok None + | CoreResponse.ErrorRes msg -> Error msg + module AsyncResult = @@ -1894,47 +1895,50 @@ type Commands(checker: FSharpCompilerServiceChecker, state: State, hasAnalyzers: let indentLength = lineStr.Length - trimmed.Length let indentString = String.replicate indentLength " " - let! (_, memberParameters, genericParameters) = - Commands.SignatureData tyRes triggerPosition lineStr |> Result.ofCoreResponse + match! Commands.SignatureData tyRes triggerPosition lineStr |> Result.ofCoreResponse with + | None -> return None + | Some(_, memberParameters, genericParameters) -> - let summarySection = "/// " - let parameterSection (name, _type) = - $"/// " + let summarySection = "/// " - let genericArg name = - $"/// " + let parameterSection (name, _type) = + $"/// " - let returnsSection = "/// " + let genericArg name = + $"/// " - let formattedXmlDoc = - seq { - yield summarySection + let returnsSection = "/// " - match memberParameters with - | [] -> () - | parameters -> - yield! - parameters - |> List.concat - |> List.mapi (fun _index parameter -> parameterSection parameter) + let formattedXmlDoc = + seq { + yield summarySection - match genericParameters with - | [] -> () - | generics -> yield! generics |> List.mapi (fun _index generic -> genericArg generic) + match memberParameters with + | [] -> () + | parameters -> + yield! + parameters + |> List.concat + |> List.mapi (fun _index parameter -> parameterSection parameter) - yield returnsSection - } - |> Seq.map (fun s -> indentString + s) - |> String.concat Environment.NewLine - |> fun s -> s + Environment.NewLine // need a newline at the very end + match genericParameters with + | [] -> () + | generics -> yield! generics |> List.mapi (fun _index generic -> genericArg generic) + + yield returnsSection + } + |> Seq.map (fun s -> indentString + s) + |> String.concat Environment.NewLine + |> fun s -> s + Environment.NewLine // need a newline at the very end - // always insert at the start of the line, because we've prepended the indent to the start of the summary section - let insertPosition = Position.mkPos triggerPosition.Line 0 + // always insert at the start of the line, because we've prepended the indent to the start of the summary section + let insertPosition = Position.mkPos triggerPosition.Line 0 - return - { InsertPosition = insertPosition - InsertText = formattedXmlDoc } + return + Some + { InsertPosition = insertPosition + InsertText = formattedXmlDoc } } member private x.GetDeclarationLocation(symbolUse, text) = diff --git a/src/FsAutoComplete.Core/TestAdapter.fs b/src/FsAutoComplete.Core/TestAdapter.fs index 7da530192..622d89723 100644 --- a/src/FsAutoComplete.Core/TestAdapter.fs +++ b/src/FsAutoComplete.Core/TestAdapter.fs @@ -10,6 +10,7 @@ type TestAdapterEntry<'range> = Childs: ResizeArray> Id: int List: bool + ModuleType: string Type: string } [] @@ -21,6 +22,18 @@ let private NUnitType = "NUnit" [] let private XUnitType = "XUnit" +[] +let private NoneModuleType = "NoneModule" + +[] +let private ModuleType = "Module" + +[] +let private TypeInModule = "TypeInModule" + +[] +let private ModuleWithSuffixType = "ModuleWithSuffix" + let rec private (|Sequentials|_|) = function | SynExpr.Sequential(_, _, e, Sequentials es, _) -> Some(e :: es) @@ -86,6 +99,7 @@ let getExpectoTests (ast: ParsedInput) : TestAdapterEntry list = Childs = ResizeArray() Id = ident List = true + ModuleType = NoneModuleType Type = ExpectoType } parent.Childs.Add entry @@ -103,6 +117,7 @@ let getExpectoTests (ast: ParsedInput) : TestAdapterEntry list = Childs = ResizeArray() Id = ident List = false + ModuleType = NoneModuleType Type = ExpectoType } parent.Childs.Add entry @@ -131,6 +146,7 @@ let getExpectoTests (ast: ParsedInput) : TestAdapterEntry list = Childs = ResizeArray() Id = ident List = false + ModuleType = NoneModuleType Type = ExpectoType } parent.Childs.Add entry @@ -208,6 +224,7 @@ let getExpectoTests (ast: ParsedInput) : TestAdapterEntry list = Childs = ResizeArray() Id = -1 List = false + ModuleType = NoneModuleType Type = "" } match ast with @@ -256,6 +273,13 @@ let getNUnitTest (ast: ParsedInput) : TestAdapterEntry list = let (SynTypeDefn(typeInfo = ci; typeRepr = om; members = members)) = t let (SynComponentInfo(longId = ids; range = r)) = ci let name = String.concat "." [ for i in ids -> i.idText ] + + let moduleType = + if parent.ModuleType = ModuleType || parent.ModuleType = ModuleWithSuffixType then + TypeInModule + else + NoneModuleType + ident <- ident + 1 let entry = @@ -264,6 +288,7 @@ let getNUnitTest (ast: ParsedInput) : TestAdapterEntry list = Childs = ResizeArray() Id = ident List = true + ModuleType = moduleType Type = NUnitType } parent.Childs.Add entry @@ -292,11 +317,27 @@ let getNUnitTest (ast: ParsedInput) : TestAdapterEntry list = Childs = ResizeArray() Id = ident List = false + ModuleType = NoneModuleType Type = NUnitType } parent.Childs.Add entry let rec visitDeclarations (parent: TestAdapterEntry) decls = + let typeNames = + decls + |> List.fold + (fun types declaration -> + match declaration with + | SynModuleDecl.Types(typeDefns, _) -> + typeDefns + |> List.map (fun (SynTypeDefn(typeInfo = ci)) -> + let (SynComponentInfo(longId = ids)) = ci + String.concat "." [ for i in ids -> i.idText ]) + |> List.append types + | _ -> types) + ([]) + |> Set.ofList + for declaration in decls do match declaration with | SynModuleDecl.Let(_, bindings, _) -> @@ -305,6 +346,13 @@ let getNUnitTest (ast: ParsedInput) : TestAdapterEntry list = | SynModuleDecl.NestedModule(moduleInfo = ci; decls = decls) -> let (SynComponentInfo(longId = ids; range = r)) = ci let name = String.concat "." [ for i in ids -> i.idText ] + + let moduleType = + if Set.contains name typeNames then + ModuleWithSuffixType + else + ModuleType + ident <- ident + 1 let entry = @@ -313,6 +361,7 @@ let getNUnitTest (ast: ParsedInput) : TestAdapterEntry list = Childs = ResizeArray() Id = ident List = true + ModuleType = moduleType Type = NUnitType } parent.Childs.Add entry @@ -327,8 +376,9 @@ let getNUnitTest (ast: ParsedInput) : TestAdapterEntry list = let visitModulesAndNamespaces parent modulesOrNss = Seq.iter - (fun (SynModuleOrNamespace(longId = ids; decls = decls; range = r)) -> + (fun (SynModuleOrNamespace(longId = ids; decls = decls; range = r; kind = kind)) -> let name = String.concat "." [ for i in ids -> i.idText ] + let moduleType = if kind.IsModule then ModuleType else NoneModuleType ident <- ident + 1 let entry = @@ -337,6 +387,7 @@ let getNUnitTest (ast: ParsedInput) : TestAdapterEntry list = Childs = ResizeArray() Id = ident List = true + ModuleType = moduleType Type = NUnitType } parent.Childs.Add entry @@ -352,6 +403,7 @@ let getNUnitTest (ast: ParsedInput) : TestAdapterEntry list = Childs = ResizeArray() Id = -1 List = false + ModuleType = NoneModuleType Type = "" } match ast with @@ -395,6 +447,13 @@ let getXUnitTest ast : TestAdapterEntry list = let (SynTypeDefn(typeInfo = ci; typeRepr = om; members = members)) = t let (SynComponentInfo(longId = ids; range = r)) = ci let name = String.concat "." [ for i in ids -> i.idText ] + + let moduleType = + if parent.ModuleType = ModuleType || parent.ModuleType = ModuleWithSuffixType then + TypeInModule + else + NoneModuleType + ident <- ident + 1 let entry = @@ -403,6 +462,7 @@ let getXUnitTest ast : TestAdapterEntry list = Childs = ResizeArray() Id = ident List = true + ModuleType = moduleType Type = XUnitType } parent.Childs.Add entry @@ -431,13 +491,27 @@ let getXUnitTest ast : TestAdapterEntry list = Childs = ResizeArray() Id = ident List = false - Type = XUnitType - - } + ModuleType = NoneModuleType + Type = XUnitType } parent.Childs.Add entry let rec visitDeclarations (parent: TestAdapterEntry) decls = + let typeNames = + decls + |> List.fold + (fun types declaration -> + match declaration with + | SynModuleDecl.Types(typeDefns, _) -> + typeDefns + |> List.map (fun (SynTypeDefn(typeInfo = ci)) -> + let (SynComponentInfo(longId = ids)) = ci + String.concat "." [ for i in ids -> i.idText ]) + |> List.append types + | _ -> types) + ([]) + |> Set.ofList + for declaration in decls do match declaration with | SynModuleDecl.Let(_, bindings, _) -> @@ -446,6 +520,13 @@ let getXUnitTest ast : TestAdapterEntry list = | SynModuleDecl.NestedModule(moduleInfo = ci; decls = decls) -> let (SynComponentInfo(longId = ids; range = r)) = ci let name = String.concat "." [ for i in ids -> i.idText ] + + let moduleType = + if Set.contains name typeNames then + ModuleWithSuffixType + else + ModuleType + ident <- ident + 1 let entry = @@ -454,6 +535,7 @@ let getXUnitTest ast : TestAdapterEntry list = Childs = ResizeArray() Id = ident List = true + ModuleType = moduleType Type = XUnitType } parent.Childs.Add entry @@ -468,8 +550,9 @@ let getXUnitTest ast : TestAdapterEntry list = let visitModulesAndNamespaces parent modulesOrNss = Seq.iter - (fun (SynModuleOrNamespace(longId = ids; decls = decls; range = r)) -> + (fun (SynModuleOrNamespace(longId = ids; decls = decls; range = r; kind = kind)) -> let name = String.concat "." [ for i in ids -> i.idText ] + let moduleType = if kind.IsModule then ModuleType else NoneModuleType ident <- ident + 1 let entry = @@ -478,6 +561,7 @@ let getXUnitTest ast : TestAdapterEntry list = Childs = ResizeArray() Id = ident List = true + ModuleType = moduleType Type = XUnitType } parent.Childs.Add entry @@ -493,6 +577,7 @@ let getXUnitTest ast : TestAdapterEntry list = Childs = ResizeArray() Id = -1 List = false + ModuleType = NoneModuleType Type = "" } match ast with diff --git a/src/FsAutoComplete/CodeFixes/ConvertPositionalDUToNamed.fs b/src/FsAutoComplete/CodeFixes/ConvertPositionalDUToNamed.fs index de7c949f0..9261194f8 100644 --- a/src/FsAutoComplete/CodeFixes/ConvertPositionalDUToNamed.fs +++ b/src/FsAutoComplete/CodeFixes/ConvertPositionalDUToNamed.fs @@ -24,6 +24,7 @@ open FsAutoComplete open FsAutoComplete.LspHelpers open FSharp.Compiler.CodeAnalysis open FSharp.Compiler.Symbols +open FSharp.Compiler.Text.Range open FsAutoComplete.FCSPatches open FSharp.Compiler.Syntax open FSharp.Compiler.Syntax.SyntaxTraversal @@ -65,14 +66,18 @@ type ParseAndCheckResults with | None -> clauses |> List.tryPick (function - | SynMatchClause(pat = UnionNameAndPatterns(ident, duFieldPatterns, parenRange)) -> + | SynMatchClause(pat = UnionNameAndPatterns(ident, duFieldPatterns, parenRange)) when + rangeContainsPos parenRange pos + -> Some(ident, duFieldPatterns, parenRange) | _ -> None) | _ -> defaultTraverse expr member x.VisitMatchClause(path, defaultTraverse, matchClause) = match matchClause with - | SynMatchClause(pat = UnionNameAndPatterns(ident, duFieldPatterns, parenRange)) -> + | SynMatchClause(pat = UnionNameAndPatterns(ident, duFieldPatterns, parenRange)) when + rangeContainsPos parenRange pos + -> Some(ident, duFieldPatterns, parenRange) | _ -> defaultTraverse matchClause } diff --git a/src/FsAutoComplete/CodeFixes/ConvertTripleSlashCommentToXmlTaggedDoc.fs b/src/FsAutoComplete/CodeFixes/ConvertTripleSlashCommentToXmlTaggedDoc.fs new file mode 100644 index 000000000..ba46ceb52 --- /dev/null +++ b/src/FsAutoComplete/CodeFixes/ConvertTripleSlashCommentToXmlTaggedDoc.fs @@ -0,0 +1,198 @@ +module FsAutoComplete.CodeFix.ConvertTripleSlashCommentToXmlTaggedDoc + +open FsToolkit.ErrorHandling +open FsAutoComplete.CodeFix.Types +open Ionide.LanguageServerProtocol.Types +open FsAutoComplete +open FsAutoComplete.LspHelpers +open FSharp.Compiler.Syntax +open FSharp.Compiler.Text.Range +open FSharp.Compiler.Xml +open System + +let title = "Convert '///' comment to XML-tagged doc comment" + +let private containsPosAndNotEmptyAndNotElaborated (pos: FSharp.Compiler.Text.Position) (xmlDoc: PreXmlDoc) = + let containsPosAndNoSummaryPresent (xd: PreXmlDoc) = + let d = xd.ToXmlDoc(false, None) + + if rangeContainsPos d.Range pos then + let summaryPresent = + d.UnprocessedLines |> Array.exists (fun s -> s.Contains("")) + + not summaryPresent + else + false + + not xmlDoc.IsEmpty && containsPosAndNoSummaryPresent xmlDoc + +let private isLowerAstElemWithPreXmlDoc input pos = + SyntaxTraversal.Traverse( + pos, + input, + { new SyntaxVisitorBase<_>() with + member _.VisitBinding(_, defaultTraverse, synBinding) = + match synBinding with + | SynBinding(xmlDoc = xmlDoc) when containsPosAndNotEmptyAndNotElaborated pos xmlDoc -> Some xmlDoc + | _ -> defaultTraverse synBinding + + member _.VisitComponentInfo(_, synComponentInfo) = + match synComponentInfo with + | SynComponentInfo(xmlDoc = xmlDoc) when containsPosAndNotEmptyAndNotElaborated pos xmlDoc -> Some xmlDoc + | _ -> None + + member _.VisitRecordDefn(_, fields, _) = + let isInLine c = + match c with + | SynField(xmlDoc = xmlDoc) when containsPosAndNotEmptyAndNotElaborated pos xmlDoc -> Some xmlDoc + | _ -> None + + fields |> List.tryPick isInLine + + member _.VisitUnionDefn(_, cases, _) = + let isInLine c = + match c with + | SynUnionCase(xmlDoc = xmlDoc) when containsPosAndNotEmptyAndNotElaborated pos xmlDoc -> Some xmlDoc + | _ -> None + + cases |> List.tryPick isInLine + + member _.VisitEnumDefn(_, cases, _) = + let isInLine b = + match b with + | SynEnumCase(xmlDoc = xmlDoc) when containsPosAndNotEmptyAndNotElaborated pos xmlDoc -> Some xmlDoc + | _ -> None + + cases |> List.tryPick isInLine + + member _.VisitLetOrUse(_, _, defaultTraverse, bindings, _) = + let isInLine b = + match b with + | SynBinding(xmlDoc = xmlDoc) when containsPosAndNotEmptyAndNotElaborated pos xmlDoc -> Some xmlDoc + | _ -> defaultTraverse b + + bindings |> List.tryPick isInLine + + member _.VisitExpr(_, _, defaultTraverse, expr) = defaultTraverse expr } // needed for nested let bindings + ) + +let private isModuleOrNamespaceOrAutoPropertyWithPreXmlDoc input pos = + SyntaxTraversal.Traverse( + pos, + input, + { new SyntaxVisitorBase<_>() with + + member _.VisitModuleOrNamespace(_, synModuleOrNamespace) = + match synModuleOrNamespace with + | SynModuleOrNamespace(xmlDoc = xmlDoc) when containsPosAndNotEmptyAndNotElaborated pos xmlDoc -> Some xmlDoc + | SynModuleOrNamespace(decls = decls) -> + + let rec findNested decls = + decls + |> List.tryPick (fun d -> + match d with + | SynModuleDecl.NestedModule(moduleInfo = moduleInfo; decls = decls) -> + match moduleInfo with + | SynComponentInfo(xmlDoc = xmlDoc) when containsPosAndNotEmptyAndNotElaborated pos xmlDoc -> + Some xmlDoc + | _ -> findNested decls + | SynModuleDecl.Types(typeDefns = typeDefns) -> + typeDefns + |> List.tryPick (fun td -> + match td with + | SynTypeDefn(typeRepr = SynTypeDefnRepr.ObjectModel(_, members, _)) -> + members + |> List.tryPick (fun m -> + match m with + | SynMemberDefn.AutoProperty(xmlDoc = xmlDoc) when + containsPosAndNotEmptyAndNotElaborated pos xmlDoc + -> + Some xmlDoc + | _ -> None) + | _ -> None) + | _ -> None) + + findNested decls } + ) + +let private isAstElemWithPreXmlDoc input pos = + match isLowerAstElemWithPreXmlDoc input pos with + | Some xml -> Some xml + | _ -> isModuleOrNamespaceOrAutoPropertyWithPreXmlDoc input pos + +let private collectCommentContents + (startPos: FSharp.Compiler.Text.Position) + (endPos: FSharp.Compiler.Text.Position) + (sourceText: NamedText) + = + let rec loop (p: FSharp.Compiler.Text.Position) acc = + if p.Line > endPos.Line then + acc + else + let currentLine = sourceText.GetLine p + + match currentLine with + | None -> acc + | Some line -> + let idx = line.IndexOf("///") + + if idx >= 0 then + let existingComment = line.TrimStart().Substring(3).TrimStart() + let acc = acc @ [ existingComment ] + + match sourceText.NextLine p with + | None -> acc + | Some nextLinePos -> loop nextLinePos acc + else + acc + + loop startPos List.empty + +let private wrapInSummary indent comments = + let indentation = String.replicate indent " " + + match comments with + | [] -> $"{indentation}/// " + | [ c ] -> $"{indentation}/// %s{c}" + | cs -> + seq { + yield $"{indentation}/// {Environment.NewLine}" + yield! cs |> List.map (fun s -> $"%s{indentation}/// %s{s}{Environment.NewLine}") + yield $"%s{indentation}/// " + } + |> String.concat "" + +let fix (getParseResultsForFile: GetParseResultsForFile) (getRangeText: GetRangeText) : CodeFix = + fun codeActionParams -> + asyncResult { + let filePath = codeActionParams.TextDocument.GetFilePath() |> Utils.normalizePath + let fcsPos = protocolPosToPos codeActionParams.Range.Start + let! (parseAndCheck, lineStr, sourceText) = getParseResultsForFile filePath fcsPos + let showFix = isAstElemWithPreXmlDoc parseAndCheck.GetAST fcsPos + + match showFix with + | Some xmlDoc -> + let d = xmlDoc.ToXmlDoc(false, None) + + let origCommentContents = + collectCommentContents d.Range.Start d.Range.End sourceText + + let indent = lineStr.IndexOf("///") + let summaryXmlDoc = wrapInSummary indent origCommentContents + + let range = + { Start = fcsPosToLsp (d.Range.Start.WithColumn 0) + End = fcsPosToLsp (d.Range.End) } + + let e = + { Range = range + NewText = summaryXmlDoc } + + return + [ { Edits = [| e |] + File = codeActionParams.TextDocument + Title = title + SourceDiagnostic = None + Kind = FixKind.Refactor } ] + | None -> return [] + } diff --git a/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs b/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs index 5258578c0..252966fbe 100644 --- a/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs +++ b/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs @@ -582,6 +582,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar List = r.List Name = r.Name Type = r.Type + ModuleType = r.ModuleType Range = fcsRangeToLsp r.Range Childs = ResizeArray(r.Childs |> Seq.map map) } @@ -1534,6 +1535,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar AddMissingInstanceMember.fix AddExplicitTypeAnnotation.fix tryGetParseResultsForFile ConvertPositionalDUToNamed.fix tryGetParseResultsForFile getRangeText + ConvertTripleSlashCommentToXmlTaggedDoc.fix tryGetParseResultsForFile getRangeText UseTripleQuotedInterpolation.fix tryGetParseResultsForFile getRangeText RenameParamToMatchSignature.fix tryGetParseResultsForFile |]) @@ -2434,7 +2436,11 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar | Some insertText -> helpText insertText |> Result.ofCoreResponse - |> Result.fold (mapHelpText ci) (fun _ -> ci) + |> Result.fold + (function + | None -> ci + | Some text -> mapHelpText ci text) + (fun _ -> ci) |> success with e -> @@ -2863,17 +2869,19 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar Commands.symbolImplementationProject getProjectOptions getUsesOfSymbol getAllProjects tyRes pos lineStr |> AsyncResult.ofCoreResponse + match res with + | None -> return None + | Some res -> + let ranges: FSharp.Compiler.Text.Range[] = + match res with + | LocationResponse.Use(_, uses) -> uses |> Array.map (fun u -> u.Range) - let ranges: FSharp.Compiler.Text.Range[] = - match res with - | LocationResponse.Use(_, uses) -> uses |> Array.map (fun u -> u.Range) - - let mappedRanges = ranges |> Array.map fcsRangeToLspLocation + let mappedRanges = ranges |> Array.map fcsRangeToLspLocation - match mappedRanges with - | [||] -> return None - | [| single |] -> return Some(GotoResult.Single single) - | multiple -> return Some(GotoResult.Multiple multiple) + match mappedRanges with + | [||] -> return None + | [| single |] -> return Some(GotoResult.Single single) + | multiple -> return Some(GotoResult.Multiple multiple) with e -> trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore @@ -3826,7 +3834,9 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar and! tyRes = forceGetTypeCheckResults filePath |> Result.ofStringErr let! tip = Commands.typesig tyRes pos lineStr |> Result.ofCoreResponse - return { Content = CommandResponse.typeSig FsAutoComplete.JsonSerializer.writeJson tip } + return + tip + |> Option.map (fun tip -> { Content = CommandResponse.typeSig FsAutoComplete.JsonSerializer.writeJson tip }) with e -> trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore @@ -3861,7 +3871,8 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let! (typ, parms, generics) = tyRes.TryGetSignatureData pos lineStr |> Result.ofStringErr return - { Content = CommandResponse.signatureData FsAutoComplete.JsonSerializer.writeJson (typ, parms, generics) } + Some + { Content = CommandResponse.signatureData FsAutoComplete.JsonSerializer.writeJson (typ, parms, generics) } with e -> trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore @@ -3893,24 +3904,27 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let! lineStr = namedText.Lines |> tryGetLineStr pos |> Result.ofStringErr and! tyRes = forceGetTypeCheckResults filePath |> Result.ofStringErr - let! { InsertPosition = insertPos - InsertText = text } = + match! Commands.GenerateXmlDocumentation(tyRes, pos, lineStr) |> AsyncResult.ofStringErr + with + | None -> return () + | Some { InsertPosition = insertPos + InsertText = text } -> + + let edit: ApplyWorkspaceEditParams = + { Label = Some "Generate Xml Documentation" + Edit = + { DocumentChanges = + Some + [| { TextDocument = p.TextDocument + Edits = + [| { Range = fcsPosToProtocolRange insertPos + NewText = text } |] } |] + Changes = None } } - let edit: ApplyWorkspaceEditParams = - { Label = Some "Generate Xml Documentation" - Edit = - { DocumentChanges = - Some - [| { TextDocument = p.TextDocument - Edits = - [| { Range = fcsPosToProtocolRange insertPos - NewText = text } |] } |] - Changes = None } } - - let! _ = lspClient.WorkspaceApplyEdit edit - return () + let! _ = lspClient.WorkspaceApplyEdit edit + return () with e -> trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore @@ -3924,7 +3938,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return! LspResult.internalError (string e) } - override __.FSharpLineLense(p: ProjectParms) = + override __.FSharpLineLens(p: ProjectParms) = asyncResult { let tags = [ "ProjectParms", box p ] use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags) @@ -3942,7 +3956,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar | Some decls -> let decls = decls |> Array.map (fun d -> d, fn) - return { Content = CommandResponse.declarations FsAutoComplete.JsonSerializer.writeJson decls } + return Some { Content = CommandResponse.declarations FsAutoComplete.JsonSerializer.writeJson decls } with e -> trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore @@ -3956,39 +3970,6 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return! LspResult.internalError (string e) } - override __.FSharpCompilerLocation(p: obj) = - asyncResult { - use trace = fsacActivitySource.StartActivityForType(thisType) - - try - logger.info ( - Log.setMessage "FSharpCompilerLocation Request: {parms}" - >> Log.addContextDestructured "parms" p - ) - - let checker = checker |> AVal.force - let! (fsc, fsi, msbuild, sdk) = Commands.CompilerLocation checker |> Result.ofCoreResponse - - return - { Content = - CommandResponse.compilerLocation - FsAutoComplete.JsonSerializer.writeJson - fsc - fsi - msbuild - (sdk |> Option.map (fun (di: DirectoryInfo) -> di.FullName)) } - with e -> - trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore - - logger.error ( - Log.setMessage "FSharpCompilerLocation Request Errored {p}" - >> Log.addContextDestructured "p" p - >> Log.addExn e - ) - - return! LspResult.internalError (string e) - } - override __.FSharpWorkspaceLoad(p: WorkspaceLoadParms) = asyncResult { let tags = [ "WorkspaceLoadParms", box p ] @@ -4124,8 +4105,10 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar >> Log.addContextDestructured "parms" p ) - let! funcs = Commands.DotnetNewList() |> AsyncResult.ofCoreResponse - return { Content = CommandResponse.dotnetnewlist FsAutoComplete.JsonSerializer.writeJson funcs } + match! Commands.DotnetNewList() |> AsyncResult.ofCoreResponse with + | Some funcs -> + return Some { Content = CommandResponse.dotnetnewlist FsAutoComplete.JsonSerializer.writeJson funcs } + | None -> return None with e -> trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore @@ -4152,8 +4135,9 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar do! Commands.DotnetNewRun p.Template p.Name p.Output [] |> AsyncResult.ofCoreResponse + |> AsyncResult.map ignore // mapping unit option to unit - return { Content = "" } + return None with e -> trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore @@ -4177,9 +4161,13 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar >> Log.addContextDestructured "parms" p ) - do! Commands.DotnetAddProject p.Target p.Reference |> AsyncResult.ofCoreResponse + do! + Commands.DotnetAddProject p.Target p.Reference + |> AsyncResult.ofCoreResponse + |> AsyncResult.map ignore // mapping unit option to unit + forceLoadProjects () |> ignore - return { Content = "" } + return None with e -> trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore @@ -4203,9 +4191,13 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar >> Log.addContextDestructured "parms" p ) - do! Commands.DotnetRemoveProject p.Target p.Reference |> AsyncResult.ofCoreResponse + do! + Commands.DotnetRemoveProject p.Target p.Reference + |> AsyncResult.ofCoreResponse + |> AsyncResult.map ignore + forceLoadProjects () |> ignore - return { Content = "" } + return None with e -> trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore @@ -4229,9 +4221,13 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar >> Log.addContextDestructured "parms" p ) - do! Commands.DotnetSlnAdd p.Target p.Reference |> AsyncResult.ofCoreResponse + do! + Commands.DotnetSlnAdd p.Target p.Reference + |> AsyncResult.ofCoreResponse + |> AsyncResult.map ignore + forceLoadProjects () |> ignore - return { Content = "" } + return None with e -> trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore @@ -4259,8 +4255,10 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let! (namedText) = forceFindOpenFileOrRead filePath |> Result.ofStringErr let! lineStr = namedText.Lines |> tryGetLineStr pos |> Result.ofStringErr and! tyRes = forceGetTypeCheckResults filePath |> Result.ofStringErr - let! t = Commands.Help tyRes pos lineStr |> Result.ofCoreResponse - return { Content = CommandResponse.help FsAutoComplete.JsonSerializer.writeJson t } + + match! Commands.Help tyRes pos lineStr |> Result.ofCoreResponse with + | Some t -> return Some { Content = CommandResponse.help FsAutoComplete.JsonSerializer.writeJson t } + | None -> return None with e -> trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore @@ -4289,8 +4287,11 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let! lineStr = namedText.Lines |> tryGetLineStr pos |> Result.ofStringErr and! tyRes = forceGetTypeCheckResults filePath |> Result.ofStringErr lastFSharpDocumentationTypeCheck <- Some tyRes - let! t = Commands.FormattedDocumentation tyRes pos lineStr |> Result.ofCoreResponse - return { Content = CommandResponse.formattedDocumentation FsAutoComplete.JsonSerializer.writeJson t } + + match! Commands.FormattedDocumentation tyRes pos lineStr |> Result.ofCoreResponse with + | Some t -> + return Some { Content = CommandResponse.formattedDocumentation FsAutoComplete.JsonSerializer.writeJson t } + | None -> return None with e -> trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore @@ -4319,24 +4320,28 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar |> Result.ofOption (fun () -> $"No typecheck results from FSharpDocumentation") |> Result.ofStringErr - let! (xml, assembly, doc, signature, footer, cn) = + match! Commands.FormattedDocumentationForSymbol tyRes p.XmlSig p.Assembly |> Result.ofCoreResponse + with + | None -> return None + | Some(xml, assembly, doc, signature, footer, cn) -> - let xmldoc = - match doc with - | FSharpXmlDoc.None -> [||] - | FSharpXmlDoc.FromXmlFile _ -> [||] - | FSharpXmlDoc.FromXmlText d -> d.GetElaboratedXmlLines() + let xmldoc = + match doc with + | FSharpXmlDoc.None -> [||] + | FSharpXmlDoc.FromXmlFile _ -> [||] + | FSharpXmlDoc.FromXmlText d -> d.GetElaboratedXmlLines() - return - { Content = - CommandResponse.formattedDocumentationForSymbol - FsAutoComplete.JsonSerializer.writeJson - xml - assembly - xmldoc - (signature, footer, cn) } + return + { Content = + CommandResponse.formattedDocumentationForSymbol + FsAutoComplete.JsonSerializer.writeJson + xml + assembly + xmldoc + (signature, footer, cn) } + |> Some with e -> trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore @@ -4386,9 +4391,11 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let filePath = p.TextDocument.GetFilePath() |> Utils.normalizePath let! tyRes = forceGetTypeCheckResults filePath |> Result.ofStringErr - let! res = Commands.pipelineHints forceFindSourceText tyRes |> Result.ofCoreResponse - return { Content = CommandResponse.pipelineHint FsAutoComplete.JsonSerializer.writeJson res } + match! Commands.pipelineHints forceFindSourceText tyRes |> Result.ofCoreResponse with + | None -> return None + | Some res -> + return Some { Content = CommandResponse.pipelineHint FsAutoComplete.JsonSerializer.writeJson res } with e -> trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore @@ -4415,9 +4422,10 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar do! Commands.FsProjMoveFileUp p.FsProj p.FileVirtualPath |> AsyncResult.ofCoreResponse + |> AsyncResult.map ignore forceLoadProjects () |> ignore - return { Content = "" } + return None with e -> trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore @@ -4445,9 +4453,10 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar do! Commands.FsProjMoveFileDown p.FsProj p.FileVirtualPath |> AsyncResult.ofCoreResponse + |> AsyncResult.map ignore forceLoadProjects () |> ignore - return { Content = "" } + return None with e -> trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore @@ -4475,9 +4484,10 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar do! Commands.addFileAbove p.FsProj p.FileVirtualPath p.NewFile |> AsyncResult.ofCoreResponse + |> AsyncResult.map ignore forceLoadProjects () |> ignore - return { Content = "" } + return None with e -> trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore @@ -4504,9 +4514,10 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar do! Commands.addFileBelow p.FsProj p.FileVirtualPath p.NewFile |> AsyncResult.ofCoreResponse + |> AsyncResult.map ignore forceLoadProjects () |> ignore - return { Content = "" } + return None with e -> trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore @@ -4531,9 +4542,13 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar >> Log.addContextDestructured "parms" p ) - do! Commands.addFile p.FsProj p.FileVirtualPath |> AsyncResult.ofCoreResponse + do! + Commands.addFile p.FsProj p.FileVirtualPath + |> AsyncResult.ofCoreResponse + |> AsyncResult.map ignore + forceLoadProjects () |> ignore - return { Content = "" } + return None with e -> trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore @@ -4558,12 +4573,17 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar ) let fullPath = Path.Combine(Path.GetDirectoryName p.FsProj, p.FileVirtualPath) - do! Commands.removeFile p.FsProj p.FileVirtualPath |> AsyncResult.ofCoreResponse + + do! + Commands.removeFile p.FsProj p.FileVirtualPath + |> AsyncResult.ofCoreResponse + |> AsyncResult.map ignore + forceLoadProjects () |> ignore let fileUri = Path.FilePathToUri fullPath diagnosticCollections.ClearFor fileUri - return { Content = "" } + return None with e -> trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore @@ -4590,9 +4610,10 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar do! Commands.addExistingFile p.FsProj p.FileVirtualPath |> AsyncResult.ofCoreResponse + |> AsyncResult.map ignore forceLoadProjects () |> ignore - return { Content = "" } + return None with e -> trace.SetStatusErrorSafe(e.Message).RecordExceptions(e) |> ignore @@ -4666,8 +4687,7 @@ module AdaptiveFSharpLspServer = |> Map.add "fsharp/signature" (serverRequestHandling (fun s p -> s.FSharpSignature(p))) |> Map.add "fsharp/signatureData" (serverRequestHandling (fun s p -> s.FSharpSignatureData(p))) |> Map.add "fsharp/documentationGenerator" (serverRequestHandling (fun s p -> s.FSharpDocumentationGenerator(p))) - |> Map.add "fsharp/lineLens" (serverRequestHandling (fun s p -> s.FSharpLineLense(p))) - |> Map.add "fsharp/compilerLocation" (serverRequestHandling (fun s p -> s.FSharpCompilerLocation(p))) + |> Map.add "fsharp/lineLens" (serverRequestHandling (fun s p -> s.FSharpLineLens(p))) |> Map.add "fsharp/workspaceLoad" (serverRequestHandling (fun s p -> s.FSharpWorkspaceLoad(p))) |> Map.add "fsharp/workspacePeek" (serverRequestHandling (fun s p -> s.FSharpWorkspacePeek(p))) |> Map.add "fsharp/project" (serverRequestHandling (fun s p -> s.FSharpProject(p))) diff --git a/src/FsAutoComplete/LspServers/Common.fs b/src/FsAutoComplete/LspServers/Common.fs index 8cf5ba530..22e6d3205 100644 --- a/src/FsAutoComplete/LspServers/Common.fs +++ b/src/FsAutoComplete/LspServers/Common.fs @@ -30,18 +30,15 @@ open FSharp.Compiler.Symbols open Fantomas.Client.Contracts open Fantomas.Client.LSPFantomasService - - - module Result = let ofStringErr r = r |> Result.mapError JsonRpc.Error.InternalErrorMessage let ofCoreResponse (r: CoreResponse<'a>) = match r with - | CoreResponse.Res a -> Ok a - | CoreResponse.ErrorRes msg - | CoreResponse.InfoRes msg -> Error(JsonRpc.Error.InternalErrorMessage msg) + | CoreResponse.Res a -> Ok(Some a) + | CoreResponse.ErrorRes msg -> Error(JsonRpc.Error.InternalErrorMessage msg) + | CoreResponse.InfoRes _ -> Ok None module AsyncResult = let ofCoreResponse (ar: Async>) = ar |> Async.map Result.ofCoreResponse diff --git a/src/FsAutoComplete/LspServers/FSharpLspClient.fs b/src/FsAutoComplete/LspServers/FSharpLspClient.fs index 2bebff8bb..2ddbce748 100644 --- a/src/FsAutoComplete/LspServers/FSharpLspClient.fs +++ b/src/FsAutoComplete/LspServers/FSharpLspClient.fs @@ -219,7 +219,7 @@ type ProgressListener(lspClient: FSharpLspClient, traceNamespace: string array) | None -> // if we don't get an event in 250 ms just loop again so we can analyze inflightEvents () - | Some(action, activity: Activity, reply: AsyncReplyChannel) -> + | Some(action, activity: Activity) -> match action with | "start" -> @@ -258,17 +258,14 @@ type ProgressListener(lspClient: FSharpLspClient, traceNamespace: string array) | _ -> () - reply.Reply() }) let shouldListenTo (act: ActivitySource) = act.Name = Tracing.fscServiceName - let activityStarted (act: Activity) = - mbp.PostAndReply(fun reply -> "start", act, reply) + let activityStarted (act: Activity) = mbp.Post("start", act) - let activityStopped (act: Activity) = - mbp.PostAndReply(fun reply -> "stop", act, reply) + let activityStopped (act: Activity) = mbp.Post("stop", act) let listener = new ActivityListener( diff --git a/src/FsAutoComplete/LspServers/FsAutoComplete.Lsp.fs b/src/FsAutoComplete/LspServers/FsAutoComplete.Lsp.fs index 5b828fcb5..5e2d2ebb4 100644 --- a/src/FsAutoComplete/LspServers/FsAutoComplete.Lsp.fs +++ b/src/FsAutoComplete/LspServers/FsAutoComplete.Lsp.fs @@ -368,6 +368,7 @@ type FSharpLspServer(state: State, lspClient: FSharpLspClient) = List = r.List Name = r.Name Type = r.Type + ModuleType = r.ModuleType Range = fcsRangeToLsp r.Range Childs = ResizeArray(r.Childs |> Seq.map map) } @@ -853,22 +854,6 @@ type FSharpLspServer(state: State, lspClient: FSharpLspClient) = | Error ex -> return LspResult.internalError ex } - member x.LineLensResolve(p) = - logger.info ( - Log.setMessage "LineLensResolve Request: {parms}" - >> Log.addContextDestructured "parms" p - ) - - p - |> x.positionHandler (fun p pos tyRes lineStr lines -> - (match Commands.SignatureData tyRes pos lineStr with - | CoreResponse.InfoRes msg - | CoreResponse.ErrorRes msg -> LspResult.internalError msg - | CoreResponse.Res(typ, parms, generics) -> - { Content = CommandResponse.signatureData FsAutoComplete.JsonSerializer.writeJson (typ, parms, generics) } - |> success) - |> async.Return) - member private x.handleSemanticTokens (getTokens: Async option>) : AsyncLspResult = @@ -878,16 +863,19 @@ type FSharpLspServer(state: State, lspClient: FSharpLspClient) = | Some rangesAndHighlights -> let! rangesAndHighlights = rangesAndHighlights |> Result.ofCoreResponse + match rangesAndHighlights with + | None -> return! success None + | Some rangesAndHighlights -> - let lspTypedRanges = - rangesAndHighlights - |> Array.map (fun item -> - let ty, mods = ClassificationUtils.map item.Type - struct (fcsRangeToLsp item.Range, ty, mods)) + let lspTypedRanges = + rangesAndHighlights + |> Array.map (fun item -> + let ty, mods = ClassificationUtils.map item.Type + struct (fcsRangeToLsp item.Range, ty, mods)) - match encodeSemanticHighlightRanges lspTypedRanges with - | None -> return! success None - | Some encoded -> return! success (Some { Data = encoded; ResultId = None }) // TODO: provide a resultId when we support delta ranges + match encodeSemanticHighlightRanges lspTypedRanges with + | None -> return! success None + | Some encoded -> return! success (Some { Data = encoded; ResultId = None }) // TODO: provide a resultId when we support delta ranges } member __.ScriptFileProjectOptions = commands.ScriptFileProjectOptions @@ -1197,6 +1185,7 @@ type FSharpLspServer(state: State, lspClient: FSharpLspClient) = AddMissingInstanceMember.fix AddExplicitTypeAnnotation.fix tryGetParseResultsForFile ConvertPositionalDUToNamed.fix tryGetParseResultsForFile getRangeText + ConvertTripleSlashCommentToXmlTaggedDoc.fix tryGetParseResultsForFile getRangeText UseTripleQuotedInterpolation.fix tryGetParseResultsForFile getRangeText RenameParamToMatchSignature.fix tryGetParseResultsForFile |] @@ -1480,7 +1469,11 @@ type FSharpLspServer(state: State, lspClient: FSharpLspClient) = let! res = commands.Helptext ci.InsertText.Value |> AsyncResult.ofCoreResponse - |> AsyncResult.foldResult (mapHelpText ci) (fun _ -> ci) + |> AsyncResult.foldResult + (function + | None -> ci + | Some text -> mapHelpText ci text) + (fun _ -> ci) return success res } @@ -1540,7 +1533,7 @@ type FSharpLspServer(state: State, lspClient: FSharpLspClient) = p |> x.positionHandler (fun p pos tyRes lineStr lines -> match commands.ToolTip tyRes pos lineStr with - | CoreResponse.InfoRes msg + | CoreResponse.InfoRes msg -> async.Return(success None) | CoreResponse.ErrorRes msg -> LspResult.internalError msg |> async.Return | CoreResponse.Res None -> async.Return(success None) | CoreResponse.Res(Some(tip, signature, footer, typeDoc)) -> @@ -1655,7 +1648,7 @@ type FSharpLspServer(state: State, lspClient: FSharpLspClient) = let res = match res with - | CoreResponse.InfoRes msg + | CoreResponse.InfoRes msg -> success None | CoreResponse.ErrorRes msg -> LspResult.internalError msg | CoreResponse.Res r -> findDeclToLspLocation r |> GotoResult.Single |> Some |> success @@ -1675,7 +1668,7 @@ type FSharpLspServer(state: State, lspClient: FSharpLspClient) = let res = match res with - | CoreResponse.InfoRes msg + | CoreResponse.InfoRes msg -> success None | CoreResponse.ErrorRes msg -> LspResult.internalError msg | CoreResponse.Res r -> findDeclToLspLocation r |> GotoResult.Single |> Some |> success @@ -1710,7 +1703,7 @@ type FSharpLspServer(state: State, lspClient: FSharpLspClient) = p |> x.positionHandler (fun p pos tyRes lineStr lines -> match commands.SymbolUse tyRes pos lineStr with - | CoreResponse.InfoRes msg + | CoreResponse.InfoRes msg -> async.Return(success None) | CoreResponse.ErrorRes msg -> async.Return(LspResult.internalError msg) | CoreResponse.Res(symbol, uses) -> uses @@ -1736,7 +1729,8 @@ type FSharpLspServer(state: State, lspClient: FSharpLspClient) = let ranges: FSharp.Compiler.Text.Range[] = match res with - | LocationResponse.Use(_, uses) -> uses |> Array.map (fun u -> u.Range) + | Some(LocationResponse.Use(_, uses)) -> uses |> Array.map (fun u -> u.Range) + | None -> [||] let mappedRanges = ranges |> Array.map fcsRangeToLspLocation @@ -1759,7 +1753,7 @@ type FSharpLspServer(state: State, lspClient: FSharpLspClient) = let res = match res with - | CoreResponse.InfoRes msg + | CoreResponse.InfoRes msg -> success None | CoreResponse.ErrorRes msg -> LspResult.internalError msg | CoreResponse.Res decls -> decls @@ -1785,7 +1779,7 @@ type FSharpLspServer(state: State, lspClient: FSharpLspClient) = let res = match res with - | CoreResponse.InfoRes msg + | CoreResponse.InfoRes msg -> success None | CoreResponse.ErrorRes msg -> LspResult.internalError msg | CoreResponse.Res(decls) -> decls @@ -1919,21 +1913,25 @@ type FSharpLspServer(state: State, lspClient: FSharpLspClient) = let fn = p.TextDocument.GetFilePath() |> Utils.normalizePath - let! decls = + let! declsAndPaths = commands.Declarations fn None (commands.TryGetFileVersion fn) |> AsyncResult.ofCoreResponse - |> AsyncResult.map (Array.map fst) - let res = - [| if config.LineLens.Enabled <> "replaceCodeLens" then - if config.CodeLenses.Signature.Enabled then - yield! decls |> Array.collect (getCodeLensInformation p.TextDocument.Uri "signature") + match declsAndPaths with + | None -> return None + | Some declsAndPaths -> + let decls = Array.map fst declsAndPaths + + let res = + [| if config.LineLens.Enabled <> "replaceCodeLens" then + if config.CodeLenses.Signature.Enabled then + yield! decls |> Array.collect (getCodeLensInformation p.TextDocument.Uri "signature") - // we have two options here because we're deprecating the EnableReferenceCodeLens one (namespacing, etc) - if config.EnableReferenceCodeLens || config.CodeLenses.References.Enabled then - yield! decls |> Array.collect (getCodeLensInformation p.TextDocument.Uri "reference") |] + // we have two options here because we're deprecating the EnableReferenceCodeLens one (namespacing, etc) + if config.EnableReferenceCodeLens || config.CodeLenses.References.Enabled then + yield! decls |> Array.collect (getCodeLensInformation p.TextDocument.Uri "reference") |] - return Some res + return Some res } override __.CodeLensResolve(p) = @@ -2167,11 +2165,12 @@ type FSharpLspServer(state: State, lspClient: FSharpLspClient) = |> x.positionHandler (fun p pos tyRes lineStr lines -> async { match commands.Typesig tyRes pos lineStr with - | CoreResponse.InfoRes msg + | CoreResponse.InfoRes msg -> return success None | CoreResponse.ErrorRes msg -> return LspResult.internalError msg | CoreResponse.Res tip -> return { Content = CommandResponse.typeSig FsAutoComplete.JsonSerializer.writeJson tip } + |> Some |> success }) } @@ -2220,10 +2219,11 @@ type FSharpLspServer(state: State, lspClient: FSharpLspClient) = p |> handler (fun p pos tyRes lineStr -> (match Commands.SignatureData tyRes pos lineStr with - | CoreResponse.InfoRes msg + | CoreResponse.InfoRes msg -> success None | CoreResponse.ErrorRes msg -> LspResult.internalError msg | CoreResponse.Res(typ, parms, generics) -> { Content = CommandResponse.signatureData FsAutoComplete.JsonSerializer.writeJson (typ, parms, generics) } + |> Some |> success) |> async.Return) @@ -2236,27 +2236,30 @@ type FSharpLspServer(state: State, lspClient: FSharpLspClient) = p |> x.positionHandler (fun p pos tyRes lineStr lines -> asyncResult { - let! { InsertPosition = insertPos - InsertText = text } = + match! Commands.GenerateXmlDocumentation(tyRes, pos, lineStr) |> AsyncResult.ofStringErr - - let edit: ApplyWorkspaceEditParams = - { Label = Some "Generate Xml Documentation" - Edit = - { DocumentChanges = - Some - [| { TextDocument = p.TextDocument - Edits = - [| { Range = fcsPosToProtocolRange insertPos - NewText = text } |] } |] - Changes = None } } - - let! response = lspClient.WorkspaceApplyEdit edit - return () + with + | None -> return () + | Some { InsertPosition = insertPos + InsertText = text } -> + + let edit: ApplyWorkspaceEditParams = + { Label = Some "Generate Xml Documentation" + Edit = + { DocumentChanges = + Some + [| { TextDocument = p.TextDocument + Edits = + [| { Range = fcsPosToProtocolRange insertPos + NewText = text } |] } |] + Changes = None } } + + let! response = lspClient.WorkspaceApplyEdit edit + return () }) - override __.FSharpLineLense(p) = + override __.FSharpLineLens(p) = async { logger.info ( Log.setMessage "FSharpLineLense Request: {parms}" @@ -2269,36 +2272,11 @@ type FSharpLspServer(state: State, lspClient: FSharpLspClient) = let res = match res with - | CoreResponse.InfoRes msg + | CoreResponse.InfoRes msg -> success None | CoreResponse.ErrorRes msg -> LspResult.internalError msg | CoreResponse.Res(decls) -> { Content = CommandResponse.declarations FsAutoComplete.JsonSerializer.writeJson decls } - |> success - - return res - } - - override __.FSharpCompilerLocation(p) = - async { - logger.info ( - Log.setMessage "FSharpCompilerLocation Request: {parms}" - >> Log.addContextDestructured "parms" p - ) - - let res = commands.CompilerLocation() - - let res = - match res with - | CoreResponse.InfoRes msg - | CoreResponse.ErrorRes msg -> LspResult.internalError msg - | CoreResponse.Res(fsc, fsi, msbuild, sdk) -> - { Content = - CommandResponse.compilerLocation - FsAutoComplete.JsonSerializer.writeJson - fsc - fsi - msbuild - (sdk |> Option.map (fun (di: DirectoryInfo) -> di.FullName)) } + |> Some |> success return res @@ -2396,15 +2374,13 @@ type FSharpLspServer(state: State, lspClient: FSharpLspClient) = let! res = Commands.DotnetNewList() - let res = - match res with - | CoreResponse.InfoRes msg - | CoreResponse.ErrorRes msg -> LspResult.internalError msg - | CoreResponse.Res(funcs) -> - { Content = CommandResponse.dotnetnewlist FsAutoComplete.JsonSerializer.writeJson funcs } - |> success + match res with + | CoreResponse.InfoRes msg -> return success None + | CoreResponse.ErrorRes msg -> return LspResult.internalError msg + | CoreResponse.Res(funcs) -> + return + success (Some { Content = CommandResponse.dotnetnewlist FsAutoComplete.JsonSerializer.writeJson funcs }) - return res } override __.FSharpDotnetNewRun(p: DotnetNewRunRequest) = @@ -2418,9 +2394,9 @@ type FSharpLspServer(state: State, lspClient: FSharpLspClient) = let res = match res with - | CoreResponse.InfoRes msg + | CoreResponse.InfoRes msg -> success None | CoreResponse.ErrorRes msg -> LspResult.internalError msg - | CoreResponse.Res(_) -> { Content = "" } |> success + | CoreResponse.Res(_) -> success None return res } @@ -2436,9 +2412,9 @@ type FSharpLspServer(state: State, lspClient: FSharpLspClient) = let res = match res with - | CoreResponse.InfoRes msg + | CoreResponse.InfoRes msg -> success None | CoreResponse.ErrorRes msg -> LspResult.internalError msg - | CoreResponse.Res(_) -> { Content = "" } |> success + | CoreResponse.Res(_) -> success None return res } @@ -2454,9 +2430,9 @@ type FSharpLspServer(state: State, lspClient: FSharpLspClient) = let res = match res with - | CoreResponse.InfoRes msg + | CoreResponse.InfoRes msg -> success None | CoreResponse.ErrorRes msg -> LspResult.internalError msg - | CoreResponse.Res(_) -> { Content = "" } |> success + | CoreResponse.Res(_) -> success None return res } @@ -2472,9 +2448,9 @@ type FSharpLspServer(state: State, lspClient: FSharpLspClient) = let res = match res with - | CoreResponse.InfoRes msg + | CoreResponse.InfoRes msg -> success None | CoreResponse.ErrorRes msg -> LspResult.internalError msg - | CoreResponse.Res(_) -> { Content = "" } |> success + | CoreResponse.Res(_) -> success None return res } @@ -2490,9 +2466,9 @@ type FSharpLspServer(state: State, lspClient: FSharpLspClient) = let res = match res with - | CoreResponse.InfoRes msg + | CoreResponse.InfoRes msg -> success None | CoreResponse.ErrorRes msg -> LspResult.internalError msg - | CoreResponse.Res(_) -> { Content = "" } |> success + | CoreResponse.Res(_) -> success None return res } @@ -2508,9 +2484,9 @@ type FSharpLspServer(state: State, lspClient: FSharpLspClient) = let res = match res with - | CoreResponse.InfoRes msg + | CoreResponse.InfoRes msg -> success None | CoreResponse.ErrorRes msg -> LspResult.internalError msg - | CoreResponse.Res(_) -> { Content = "" } |> success + | CoreResponse.Res(_) -> success None return res } @@ -2526,9 +2502,9 @@ type FSharpLspServer(state: State, lspClient: FSharpLspClient) = let res = match res with - | CoreResponse.InfoRes msg + | CoreResponse.InfoRes msg -> success None | CoreResponse.ErrorRes msg -> LspResult.internalError msg - | CoreResponse.Res(_) -> { Content = "" } |> success + | CoreResponse.Res(_) -> success None return res } @@ -2544,9 +2520,9 @@ type FSharpLspServer(state: State, lspClient: FSharpLspClient) = let res = match res with - | CoreResponse.InfoRes msg + | CoreResponse.InfoRes msg -> success None | CoreResponse.ErrorRes msg -> LspResult.internalError msg - | CoreResponse.Res(_) -> { Content = "" } |> success + | CoreResponse.Res(_) -> success None return res } @@ -2562,9 +2538,9 @@ type FSharpLspServer(state: State, lspClient: FSharpLspClient) = let res = match res with - | CoreResponse.InfoRes msg + | CoreResponse.InfoRes msg -> success None | CoreResponse.ErrorRes msg -> LspResult.internalError msg - | CoreResponse.Res(_) -> { Content = "" } |> success + | CoreResponse.Res(_) -> success None return res } @@ -2580,9 +2556,9 @@ type FSharpLspServer(state: State, lspClient: FSharpLspClient) = let res = match res with - | CoreResponse.InfoRes msg + | CoreResponse.InfoRes msg -> success None | CoreResponse.ErrorRes msg -> LspResult.internalError msg - | CoreResponse.Res(_) -> { Content = "" } |> success + | CoreResponse.Res(_) -> success None return res } @@ -2603,9 +2579,9 @@ type FSharpLspServer(state: State, lspClient: FSharpLspClient) = let res = match res with - | CoreResponse.InfoRes msg + | CoreResponse.InfoRes msg -> success None | CoreResponse.ErrorRes msg -> LspResult.internalError msg - | CoreResponse.Res(_) -> { Content = "" } |> success + | CoreResponse.Res(_) -> success None return res } @@ -2619,10 +2595,11 @@ type FSharpLspServer(state: State, lspClient: FSharpLspClient) = p |> x.positionHandler (fun p pos tyRes lineStr lines -> (match Commands.Help tyRes pos lineStr with - | CoreResponse.InfoRes msg + | CoreResponse.InfoRes msg -> success None | CoreResponse.ErrorRes msg -> LspResult.internalError msg | CoreResponse.Res(t) -> { Content = CommandResponse.help FsAutoComplete.JsonSerializer.writeJson t } + |> Some |> success) |> async.Return) @@ -2636,7 +2613,7 @@ type FSharpLspServer(state: State, lspClient: FSharpLspClient) = p |> x.positionHandler (fun p pos tyRes lineStr lines -> (match Commands.FormattedDocumentation tyRes pos lineStr with - | CoreResponse.InfoRes msg + | CoreResponse.InfoRes msg -> success None | CoreResponse.ErrorRes msg -> LspResult.internalError msg | CoreResponse.Res(tip, xml, signature, footer, cm) -> let notification: PlainNotification = @@ -2645,37 +2622,40 @@ type FSharpLspServer(state: State, lspClient: FSharpLspClient) = FsAutoComplete.JsonSerializer.writeJson (tip, xml, signature, footer, cm) } - success notification) + success (Some notification)) |> async.Return) override x.FSharpDocumentationSymbol(p: DocumentationForSymbolReuqest) = - logger.info ( - Log.setMessage "FSharpDocumentationSymbol Request: {parms}" - >> Log.addContextDestructured "parms" p - ) + async { + logger.info ( + Log.setMessage "FSharpDocumentationSymbol Request: {parms}" + >> Log.addContextDestructured "parms" p + ) - match commands.LastCheckResult with - | None -> AsyncLspResult.internalError "error" - | Some tyRes -> - match Commands.FormattedDocumentationForSymbol tyRes p.XmlSig p.Assembly with - | (CoreResponse.InfoRes msg) - | (CoreResponse.ErrorRes msg) -> AsyncLspResult.internalError msg - | (CoreResponse.Res(xml, assembly, doc, signature, footer, cn)) -> - let xmldoc = - match doc with - | FSharpXmlDoc.None -> [||] - | FSharpXmlDoc.FromXmlFile _ -> [||] - | FSharpXmlDoc.FromXmlText d -> d.GetElaboratedXmlLines() - - { Content = - CommandResponse.formattedDocumentationForSymbol - FsAutoComplete.JsonSerializer.writeJson - xml - assembly - xmldoc - (signature, footer, cn) } - |> success - |> async.Return + match commands.LastCheckResult with + | None -> return success None + | Some tyRes -> + match Commands.FormattedDocumentationForSymbol tyRes p.XmlSig p.Assembly with + | (CoreResponse.InfoRes msg) -> return success None + | (CoreResponse.ErrorRes msg) -> return! AsyncLspResult.internalError msg + | (CoreResponse.Res(xml, assembly, doc, signature, footer, cn)) -> + let xmldoc = + match doc with + | FSharpXmlDoc.None -> [||] + | FSharpXmlDoc.FromXmlFile _ -> [||] + | FSharpXmlDoc.FromXmlText d -> d.GetElaboratedXmlLines() + + return + { Content = + CommandResponse.formattedDocumentationForSymbol + FsAutoComplete.JsonSerializer.writeJson + xml + assembly + xmldoc + (signature, footer, cn) } + |> Some + |> success + } override __.LoadAnalyzers(path) = async { @@ -2831,10 +2811,11 @@ type FSharpLspServer(state: State, lspClient: FSharpLspClient) = p.TextDocument |> x.fileHandler (fun fn tyRes lines -> match commands.PipelineHints tyRes with - | CoreResponse.InfoRes msg + | CoreResponse.InfoRes msg -> async.Return(success None) | CoreResponse.ErrorRes msg -> AsyncLspResult.internalError msg | CoreResponse.Res(res) -> { Content = CommandResponse.pipelineHint FsAutoComplete.JsonSerializer.writeJson res } + |> Some |> success |> async.Return) @@ -2899,8 +2880,7 @@ module FSharpLspServer = |> Map.add "fsharp/signature" (serverRequestHandling (fun s p -> s.FSharpSignature(p))) |> Map.add "fsharp/signatureData" (serverRequestHandling (fun s p -> s.FSharpSignatureData(p))) |> Map.add "fsharp/documentationGenerator" (serverRequestHandling (fun s p -> s.FSharpDocumentationGenerator(p))) - |> Map.add "fsharp/lineLens" (serverRequestHandling (fun s p -> s.FSharpLineLense(p))) - |> Map.add "fsharp/compilerLocation" (serverRequestHandling (fun s p -> s.FSharpCompilerLocation(p))) + |> Map.add "fsharp/lineLens" (serverRequestHandling (fun s p -> s.FSharpLineLens(p))) |> Map.add "fsharp/workspaceLoad" (serverRequestHandling (fun s p -> s.FSharpWorkspaceLoad(p))) |> Map.add "fsharp/workspacePeek" (serverRequestHandling (fun s p -> s.FSharpWorkspacePeek(p))) |> Map.add "fsharp/project" (serverRequestHandling (fun s p -> s.FSharpProject(p))) diff --git a/src/FsAutoComplete/LspServers/IFSharpLspServer.fs b/src/FsAutoComplete/LspServers/IFSharpLspServer.fs index 154b6560b..0870ef3a1 100644 --- a/src/FsAutoComplete/LspServers/IFSharpLspServer.fs +++ b/src/FsAutoComplete/LspServers/IFSharpLspServer.fs @@ -19,30 +19,29 @@ type OptionallyVersionedTextDocumentPositionParams = [] type IFSharpLspServer = inherit ILspServer - abstract FSharpSignature: TextDocumentPositionParams -> Async> - abstract FSharpSignatureData: TextDocumentPositionParams -> Async> + abstract FSharpSignature: TextDocumentPositionParams -> Async> + abstract FSharpSignatureData: TextDocumentPositionParams -> Async> abstract FSharpDocumentationGenerator: OptionallyVersionedTextDocumentPositionParams -> AsyncLspResult - abstract FSharpLineLense: ProjectParms -> Async> - abstract FSharpCompilerLocation: obj -> Async> + abstract FSharpLineLens: ProjectParms -> Async> abstract FSharpWorkspaceLoad: WorkspaceLoadParms -> Async> abstract FSharpWorkspacePeek: WorkspacePeekRequest -> Async> abstract FSharpProject: ProjectParms -> Async> abstract FSharpFsdn: FsdnRequest -> Async> - abstract FSharpDotnetNewList: DotnetNewListRequest -> Async> - abstract FSharpDotnetNewRun: DotnetNewRunRequest -> Async> - abstract FSharpDotnetAddProject: DotnetProjectRequest -> Async> - abstract FSharpDotnetRemoveProject: DotnetProjectRequest -> Async> - abstract FSharpDotnetSlnAdd: DotnetProjectRequest -> Async> - abstract FSharpHelp: TextDocumentPositionParams -> Async> - abstract FSharpDocumentation: TextDocumentPositionParams -> Async> - abstract FSharpDocumentationSymbol: DocumentationForSymbolReuqest -> Async> + abstract FSharpDotnetNewList: DotnetNewListRequest -> Async> + abstract FSharpDotnetNewRun: DotnetNewRunRequest -> Async> + abstract FSharpDotnetAddProject: DotnetProjectRequest -> Async> + abstract FSharpDotnetRemoveProject: DotnetProjectRequest -> Async> + abstract FSharpDotnetSlnAdd: DotnetProjectRequest -> Async> + abstract FSharpHelp: TextDocumentPositionParams -> Async> + abstract FSharpDocumentation: TextDocumentPositionParams -> Async> + abstract FSharpDocumentationSymbol: DocumentationForSymbolReuqest -> Async> abstract FSharpLiterateRequest: FSharpLiterateRequest -> Async> abstract LoadAnalyzers: obj -> Async> - abstract FSharpPipelineHints: FSharpPipelineHintRequest -> Async> - abstract FsProjMoveFileUp: DotnetFileRequest -> Async> - abstract FsProjMoveFileDown: DotnetFileRequest -> Async> - abstract FsProjAddFileAbove: DotnetFile2Request -> Async> - abstract FsProjAddFileBelow: DotnetFile2Request -> Async> - abstract FsProjAddFile: DotnetFileRequest -> Async> - abstract FsProjRemoveFile: DotnetFileRequest -> Async> - abstract FsProjAddExistingFile: DotnetFileRequest -> Async> + abstract FSharpPipelineHints: FSharpPipelineHintRequest -> Async> + abstract FsProjMoveFileUp: DotnetFileRequest -> Async> + abstract FsProjMoveFileDown: DotnetFileRequest -> Async> + abstract FsProjAddFileAbove: DotnetFile2Request -> Async> + abstract FsProjAddFileBelow: DotnetFile2Request -> Async> + abstract FsProjAddFile: DotnetFileRequest -> Async> + abstract FsProjRemoveFile: DotnetFileRequest -> Async> + abstract FsProjAddExistingFile: DotnetFileRequest -> Async> diff --git a/test/FsAutoComplete.Tests.Lsp/CodeFixTests/Tests.fs b/test/FsAutoComplete.Tests.Lsp/CodeFixTests/Tests.fs index fb50240d7..dc63bdb79 100644 --- a/test/FsAutoComplete.Tests.Lsp/CodeFixTests/Tests.fs +++ b/test/FsAutoComplete.Tests.Lsp/CodeFixTests/Tests.fs @@ -573,6 +573,7 @@ let private convertPositionalDUToNamedTests state = type A = A of a: int * b: bool match A(1, true) with + | A(_, 23) -> () | A(a$0, b) -> () """ Diagnostics.acceptAll @@ -581,6 +582,7 @@ let private convertPositionalDUToNamedTests state = type A = A of a: int * b: bool match A(1, true) with + | A(_, 23) -> () | A(a = a; b = b;) -> () """ testCaseAsync "in parenthesized match" <| @@ -589,6 +591,7 @@ let private convertPositionalDUToNamedTests state = type A = A of a: int * b: bool match A(1, true) with + | (A(_, 23)) -> () | (A(a$0, b)) -> () """ Diagnostics.acceptAll @@ -597,6 +600,7 @@ let private convertPositionalDUToNamedTests state = type A = A of a: int * b: bool match A(1, true) with + | (A(_, 23)) -> () | (A(a = a; b = b;)) -> () """ testCaseAsync "when there is one new field on the DU" <| @@ -627,6 +631,325 @@ let private convertPositionalDUToNamedTests state = """ ]) +let private convertTripleSlashCommentToXmlTaggedDocTests state = + serverTestList (nameof ConvertTripleSlashCommentToXmlTaggedDoc) state defaultConfigDto None (fun server -> + [ let selectCodeFix = CodeFix.withTitle ConvertTripleSlashCommentToXmlTaggedDoc.title + + testCaseAsync "single line comment over top level function" + <| CodeFix.check + server + """ + /// $0line 1 + let f () = () + """ + Diagnostics.acceptAll + selectCodeFix + """ + /// line 1 + let f () = () + """ + + testCaseAsync "multiline comments over top level function" + <| CodeFix.check + server + """ + /// $0line 1 + /// line 2 + /// line 3 + /// line 4 + let f a b c = a + b + """ + Diagnostics.acceptAll + selectCodeFix + """ + /// + /// line 1 + /// line 2 + /// line 3 + /// line 4 + /// + let f a b c = a + b + """ + + testCaseAsync "multiline comments over nested function" + <| CodeFix.check + server + """ + /// line 1 + /// line 2 + /// line 3 + let g () = + /// line 1 + /// line 2 + /// line $03 + /// line 4 + let f x = x * x + () + """ + Diagnostics.acceptAll + selectCodeFix + """ + /// line 1 + /// line 2 + /// line 3 + let g () = + /// + /// line 1 + /// line 2 + /// line 3 + /// line 4 + /// + let f x = x * x + () + """ + + testCaseAsync "single line comment over use" + <| CodeFix.check + server + """ + let f a b _ = + /// line on use$0 + use r = new System.IO.BinaryReader(null) + + a + b + """ + Diagnostics.acceptAll + selectCodeFix + """ + let f a b _ = + /// line on use + use r = new System.IO.BinaryReader(null) + + a + b + """ + + testCaseAsync "multiline comments over record type" + <| CodeFix.check + server + """ + /// line 1 + /// line 2 + /// line 3 + $0/// line 4 + type MyRecord = { Foo: int } + """ + Diagnostics.acceptAll + selectCodeFix + """ + /// + /// line 1 + /// line 2 + /// line 3 + /// line 4 + /// + type MyRecord = { Foo: int } + """ + + testCaseAsync "multiline comments over discriminated union type" + <| CodeFix.check + server + """ + /// line 1 on DU + /// $0line 2 on DU + /// line 3 on DU + type DiscUnionTest = + /// line 1 on Field 1 + /// line 2 on Field 1 + | Field1 + /// line 1 on Field 2 + /// line 2 on Field 2 + | Field2 + """ + Diagnostics.acceptAll + selectCodeFix + """ + /// + /// line 1 on DU + /// line 2 on DU + /// line 3 on DU + /// + type DiscUnionTest = + /// line 1 on Field 1 + /// line 2 on Field 1 + | Field1 + /// line 1 on Field 2 + /// line 2 on Field 2 + | Field2 + """ + + testCaseAsync "multiline comments over discriminated union field" + <| CodeFix.check + server + """ + /// line 1 on DU + /// line 2 on DU + /// line 3 on DU + type DiscUnionTest = + /// line 1 on Field 1 + /// line 2 on Field 1 + | Field1 + /// line 1 $0on Field 2 + /// line 2 on Field 2 + | Field2 + """ + Diagnostics.acceptAll + selectCodeFix + """ + /// line 1 on DU + /// line 2 on DU + /// line 3 on DU + type DiscUnionTest = + /// line 1 on Field 1 + /// line 2 on Field 1 + | Field1 + /// + /// line 1 on Field 2 + /// line 2 on Field 2 + /// + | Field2 + """ + + testCaseAsync "multiline comments over enum" + <| CodeFix.check + server + """ + $0/// line 1 on enum + /// line 2 on enum + type myEnum = + | value1 = 1 + | value2 = 2 + """ + Diagnostics.acceptAll + selectCodeFix + """ + /// + /// line 1 on enum + /// line 2 on enum + /// + type myEnum = + | value1 = 1 + | value2 = 2 + """ + + testCaseAsync "multiline comment over class" + <| CodeFix.check + server + """ + //$0/ On Class 1 + /// On Class 2 + type MyClass() = + /// On member 1 + /// On member 2 + member val Name = "" with get, set + """ + Diagnostics.acceptAll + selectCodeFix + """ + /// + /// On Class 1 + /// On Class 2 + /// + type MyClass() = + /// On member 1 + /// On member 2 + member val Name = "" with get, set + """ + + testCaseAsync "multiline comment over member" + <| CodeFix.check + server + """ + type MyClass() = + /// on new 1 + $0/// on new 2 + new() = MyClass() + """ + Diagnostics.acceptAll + selectCodeFix + """ + type MyClass() = + /// + /// on new 1 + /// on new 2 + /// + new() = MyClass() + """ + + testCaseAsync "multiline comment over autoproperty" + <| CodeFix.check + server + """ + type MyClass() = + /// line 1 on autoproperty + /// li$0ne 2 on autoproperty + member val Name = "" with get, set + """ + Diagnostics.acceptAll + selectCodeFix + """ + type MyClass() = + /// + /// line 1 on autoproperty + /// line 2 on autoproperty + /// + member val Name = "" with get, set + """ + + testCaseAsync "multiline comment over named module" + <| CodeFix.check + server + """ + $0/// On named module 1 + /// On named module 2 + module M + let f x = x + """ + Diagnostics.acceptAll + selectCodeFix + """ + /// + /// On named module 1 + /// On named module 2 + /// + module M + let f x = x + """ + + testCaseAsync "multiline comment over nested module" + <| CodeFix.check + server + """ + module M + module MyNestedModule = + /// Line 1 on$0 MyNestedNestedModule + /// Line 2 on MyNestedNestedModule + module MyNestedNestedModule = + let x = 3 + """ + Diagnostics.acceptAll + selectCodeFix + """ + module M + module MyNestedModule = + /// + /// Line 1 on MyNestedNestedModule + /// Line 2 on MyNestedNestedModule + /// + module MyNestedNestedModule = + let x = 3 + """ + + testCaseAsync "is not applicable to existing xml tag comment" + <| CodeFix.checkNotApplicable + server + """ + /// + /// foo$0 + /// ... + """ + Diagnostics.acceptAll + selectCodeFix ]) + let private generateAbstractClassStubTests state = let config = { defaultConfigDto with AbstractClassStubGeneration = Some true } serverTestList (nameof GenerateAbstractClassStub) state config None (fun server -> [ @@ -1656,6 +1979,7 @@ let tests state = testList "CodeFix-tests" [ convertDoubleEqualsToSingleEqualsTests state convertInvalidRecordToAnonRecordTests state convertPositionalDUToNamedTests state + convertTripleSlashCommentToXmlTaggedDocTests state generateAbstractClassStubTests state generateRecordStubTests state generateUnionCasesTests state diff --git a/test/FsAutoComplete.Tests.Lsp/DetectUnitTests.fs b/test/FsAutoComplete.Tests.Lsp/DetectUnitTests.fs index 729ff4358..9ad1c1626 100644 --- a/test/FsAutoComplete.Tests.Lsp/DetectUnitTests.fs +++ b/test/FsAutoComplete.Tests.Lsp/DetectUnitTests.fs @@ -31,12 +31,23 @@ let tests state = (async { let! testNotification = geTestNotification "NUnitTests" "UnitTest1.fs" Expect.hasLength testNotification.Tests 1 "Expected to have found 1 nunit test" + + Expect.equal testNotification.Tests[0].Childs[1].Childs[0].Name "Inner" "Expect nested module to be named Inner" + Expect.equal testNotification.Tests[0].Childs[1].Childs[0].ModuleType "Module" "Expect nested module to be a module type" + + Expect.equal testNotification.Tests[0].Childs[1].Childs[1].Name "InnerClass" "Expect nested module to be named Inner" + Expect.equal testNotification.Tests[0].Childs[1].Childs[1].ModuleType "TypeInModule" "Expect nested module to be a module type" }) testCaseAsync "Find xunit test" (async { let! testNotification = geTestNotification "XUnitTests" "Tests.fs" - Expect.hasLength testNotification.Tests 1 "Expected to have found 1 xunit test" + Expect.hasLength testNotification.Tests 1 "Expected to have found 1 xunit test list" + Expect.equal testNotification.Tests[0].ModuleType "Module" "Expected top list to be module" + + Expect.hasLength testNotification.Tests[0].Childs 3 "Expected to have found 3 child tests of top test" + Expect.equal testNotification.Tests[0].Childs[0].ModuleType "NoneModule" "Expect My test to be none module type" + Expect.equal testNotification.Tests[0].Childs[2].ModuleType "ModuleWithSuffix" "Expect Clashing test to be a module with suffix" }) testCaseAsync "Find expecto tests" diff --git a/test/FsAutoComplete.Tests.Lsp/ExtensionsTests.fs b/test/FsAutoComplete.Tests.Lsp/ExtensionsTests.fs index b60b4c59e..c0b1a6837 100644 --- a/test/FsAutoComplete.Tests.Lsp/ExtensionsTests.fs +++ b/test/FsAutoComplete.Tests.Lsp/ExtensionsTests.fs @@ -380,7 +380,7 @@ let signatureTests state = Position = { Line = line; Character = character } } match! server.FSharpSignature pos with - | Ok { Content = content } -> + | Ok (Some { Content = content }) -> let r = JsonSerializer.readJson> (content) Expect.equal r.Kind "typesig" "Should have a kind of 'typesig'" @@ -388,6 +388,7 @@ let signatureTests state = r.Data expectedSignature (sprintf "Should have a signature of '%s' at character %d" expectedSignature character) + | Ok None -> failtestf "No signature found at character %d" character | Result.Error errors -> failtestf "Error while getting signature: %A" errors } diff --git a/test/FsAutoComplete.Tests.Lsp/InfoPanelTests.fs b/test/FsAutoComplete.Tests.Lsp/InfoPanelTests.fs index e440a2ccf..c7dd38c16 100644 --- a/test/FsAutoComplete.Tests.Lsp/InfoPanelTests.fs +++ b/test/FsAutoComplete.Tests.Lsp/InfoPanelTests.fs @@ -40,7 +40,7 @@ let docFormattingTest state = match doc with | Result.Error err -> failtest $"Doc error: {err.Message}" - | Result.Ok (As ([ [ model: FsAutoComplete.CommandResponse.DocumentationDescription ] ])) -> + | Result.Ok (Some(As ([ [ model: FsAutoComplete.CommandResponse.DocumentationDescription ] ]))) -> Expect.stringContains model.Signature "'Key, 'U" "Formatted doc contains both params separated by (, )" | Result.Ok _ -> failtest "couldn't parse doc as the json type we expected" }) @@ -57,7 +57,7 @@ let docFormattingTest state = match doc with | Result.Error err -> failtest $"Doc error: {err.Message}" - | Result.Ok (As ([ [ model: FsAutoComplete.CommandResponse.DocumentationDescription ] ])) -> + | Result.Ok (Some (As ([ [ model: FsAutoComplete.CommandResponse.DocumentationDescription ] ]))) -> Expect.stringContains model.Signature "'T1 * 'T2 * 'T3" "Formatted doc contains 3 params separated by ( * )" | Result.Ok _ -> failtest "couldn't parse doc as the json type we expected" }) ] diff --git a/test/FsAutoComplete.Tests.Lsp/TestCases/NUnitTests/UnitTest1.fs b/test/FsAutoComplete.Tests.Lsp/TestCases/NUnitTests/UnitTest1.fs index 2f381835c..3663b1410 100644 --- a/test/FsAutoComplete.Tests.Lsp/TestCases/NUnitTests/UnitTest1.fs +++ b/test/FsAutoComplete.Tests.Lsp/TestCases/NUnitTests/UnitTest1.fs @@ -9,3 +9,14 @@ let Setup () = [] let Test1 () = Assert.Pass() + +module Outer = + module Inner = + [] + let Test2 (i: int) = + Assert.Pass() + + type InnerClass() = + [] + member this.Test1 () = + Assert.Pass() diff --git a/test/FsAutoComplete.Tests.Lsp/TestCases/XUnitTests/Tests.fs b/test/FsAutoComplete.Tests.Lsp/TestCases/XUnitTests/Tests.fs index 7d11a6453..85a0e28a9 100644 --- a/test/FsAutoComplete.Tests.Lsp/TestCases/XUnitTests/Tests.fs +++ b/test/FsAutoComplete.Tests.Lsp/TestCases/XUnitTests/Tests.fs @@ -6,3 +6,16 @@ open Xunit [] let ``My test`` () = Assert.True(true) + +module Inner = + [] + let ``Other test`` () = + Assert.True(true) + +type NameClash () = + do () + +module NameClash = + [] + let ``Clashing test`` () = + Assert.True(true) \ No newline at end of file