From ea673de43f6144c1e2e460e958f958b43af21c9c Mon Sep 17 00:00:00 2001 From: Will Smith Date: Fri, 18 Jun 2021 13:28:46 -0700 Subject: [PATCH 01/27] Consolidating Roslyn workspace and FCS --- .../Classification/ClassificationService.fs | 54 +++++----- .../CodeFix/AddOpenCodeFixProvider.fs | 18 ++-- ...peAnnotationToObjectOfIndeterminateType.fs | 18 ++-- .../ChangeRefCellDerefToNotExpression.fs | 6 +- .../ConvertCSharpLambdaToFSharpLambda.fs | 6 +- .../CodeFix/ConvertToAnonymousRecord.fs | 7 +- .../ImplementInterfaceCodeFixProvider.fs | 10 +- .../CodeFix/MakeDeclarationMutable.fs | 12 +-- .../CodeFix/MakeOuterBindingRecursive.fs | 6 +- .../CodeFix/ProposeUppercaseLabel.fs | 5 +- .../CodeFix/RemoveReturnOrYield.fs | 6 +- .../CodeFix/RemoveUnusedBinding.fs | 7 +- .../CodeFix/RemoveUnusedOpens.fs | 8 +- .../CodeFix/RenameParamToMatchSignature.fs | 6 +- .../CodeFix/RenameUnusedValue.fs | 11 +-- .../CodeFix/ReplaceWithSuggestion.fs | 7 +- .../CodeFix/UseMutationWhenValueIsMutable.fs | 14 +-- .../CodeLens/CodeLensProvider.fs | 5 +- .../CodeLens/FSharpCodeLensService.fs | 9 +- .../Commands/HelpContextService.fs | 10 +- .../Commands/XmlDocCommandService.fs | 19 ++-- .../Completion/CompletionProvider.fs | 23 ++--- .../Completion/CompletionService.fs | 6 +- .../FSharp.Editor/Completion/SignatureHelp.fs | 18 +--- .../Debugging/BreakpointResolutionService.fs | 10 +- .../Diagnostics/DocumentDiagnosticAnalyzer.fs | 30 ++---- .../SimplifyNameDiagnosticAnalyzer.fs | 6 +- .../Diagnostics/UnusedDeclarationsAnalyzer.fs | 21 ++-- .../UnusedOpensDiagnosticAnalyzer.fs | 12 +-- .../DocumentHighlightsService.fs | 22 ++--- .../src/FSharp.Editor/FSharp.Editor.fsproj | 2 +- .../InlineRename/InlineRenameService.fs | 28 ++---- .../FSharpCheckerExtensions.fs | 98 ------------------- .../LanguageService/SymbolHelpers.fs | 50 +++------- .../Navigation/FindUsagesService.fs | 19 ++-- .../Navigation/GoToDefinition.fs | 38 +++---- .../Navigation/GoToDefinitionService.fs | 5 +- .../Navigation/NavigableSymbolsService.fs | 9 +- .../Navigation/NavigateToSearchService.fs | 66 ++++++------- .../Navigation/NavigationBarItemService.fs | 8 +- .../src/FSharp.Editor/QuickInfo/Navigation.fs | 3 +- .../QuickInfo/QuickInfoProvider.fs | 39 +++----- .../Refactor/AddExplicitTypeToParameter.fs | 12 +-- .../Refactor/ChangeDerefToValueRefactoring.fs | 7 +- .../ChangeTypeofWithNameToNameofExpression.fs | 7 +- .../Structure/BlockStructureService.fs | 7 +- .../UnitTests/BreakpointResolutionService.fs | 4 +- .../UnitTests/CompletionProviderTests.fs | 5 +- .../DocumentDiagnosticAnalyzerTests.fs | 6 +- .../DocumentHighlightsServiceTests.fs | 3 +- .../UnitTests/FsxCompletionProviderTests.fs | 3 +- .../UnitTests/GoToDefinitionServiceTests.fs | 8 +- .../tests/UnitTests/QuickInfoTests.fs | 2 +- .../SemanticColorizationServiceTests.fs | 3 +- .../UnitTests/SignatureHelpProviderTests.fs | 46 +++------ 55 files changed, 250 insertions(+), 620 deletions(-) delete mode 100644 vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerExtensions.fs diff --git a/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs b/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs index a3844ae749e..3ac78d993ba 100644 --- a/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs +++ b/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs @@ -28,7 +28,7 @@ open FSharp.Compiler.Tokenization #nowarn "57" -type SemanticClassificationData = SemanticClassificationView option +type SemanticClassificationData = SemanticClassificationView type SemanticClassificationLookup = IReadOnlyDictionary> [] @@ -68,10 +68,7 @@ type DocumentCache<'Value when 'Value : not struct>() = type internal FSharpClassificationService [] ( - checkerProvider: FSharpCheckerProvider, - projectInfoManager: FSharpProjectOptionsManager ) = - static let userOpName = "SemanticColorization" static let getLexicalClassifications(filePath: string, defines, text: SourceText, textSpan: TextSpan, ct) = let text = text.GetSubText(textSpan) @@ -117,23 +114,20 @@ type internal FSharpClassificationService | true, items -> addSemanticClassification sourceText targetSpan items outputResult | _ -> () - static let toSemanticClassificationLookup (data: SemanticClassificationData) = + static let toSemanticClassificationLookup (d: SemanticClassificationData) = let lookup = System.Collections.Generic.Dictionary>() - match data with - | None -> () - | Some d -> - let f (dataItem: SemanticClassificationItem) = - let items = - match lookup.TryGetValue dataItem.Range.StartLine with - | true, items -> items - | _ -> - let items = ResizeArray() - lookup.[dataItem.Range.StartLine] <- items - items - - items.Add dataItem - - d.ForEach(f) + let f (dataItem: SemanticClassificationItem) = + let items = + match lookup.TryGetValue dataItem.Range.StartLine with + | true, items -> items + | _ -> + let items = ResizeArray() + lookup.[dataItem.Range.StartLine] <- items + items + + items.Add dataItem + + d.ForEach(f) System.Collections.ObjectModel.ReadOnlyDictionary lookup :> IReadOnlyDictionary<_, _> @@ -147,8 +141,8 @@ type internal FSharpClassificationService async { use _logBlock = Logger.LogBlock(LogEditorFunctionId.Classification_Syntactic) - let defines = projectInfoManager.GetCompilationDefinesForEditingDocument(document) - let! sourceText = document.GetTextAsync(cancellationToken) |> Async.AwaitTask + let defines = document.GetFSharpSyntaxDefines() + let! sourceText = document.GetTextAsync(cancellationToken) |> Async.AwaitTask // For closed documents, only get classification for the text within the span. // This may be inaccurate for multi-line tokens such as string literals, but this is ok for now @@ -157,29 +151,29 @@ type internal FSharpClassificationService result.AddRange(getLexicalClassifications(document.FilePath, defines, sourceText, textSpan, cancellationToken)) else result.AddRange(Tokenizer.getClassifiedSpans(document.Id, sourceText, textSpan, Some(document.FilePath), defines, cancellationToken)) - } |> RoslynHelpers.StartAsyncUnitAsTask cancellationToken + } + |> RoslynHelpers.StartAsyncUnitAsTask cancellationToken member _.AddSemanticClassificationsAsync(document: Document, textSpan: TextSpan, result: List, cancellationToken: CancellationToken) = - asyncMaybe { + async { use _logBlock = Logger.LogBlock(LogEditorFunctionId.Classification_Semantic) - let! _, _, projectOptions = projectInfoManager.TryGetOptionsForDocumentOrProject(document, cancellationToken, userOpName) - let! sourceText = document.GetTextAsync(cancellationToken) + let! sourceText = document.GetTextAsync(cancellationToken) |> Async.AwaitTask // If we are trying to get semantic classification for a document that is not open, get the results from the background and cache it. // We do this for find all references when it is populating results. // We cache it temporarily so we do not have to continously call into the checker and perform a background operation. if not (document.Project.Solution.Workspace.IsDocumentOpen document.Id) then - match! semanticClassificationCache.TryGetValueAsync document |> liftAsync with + match! semanticClassificationCache.TryGetValueAsync document with | ValueSome classificationDataLookup -> addSemanticClassificationByLookup sourceText textSpan classificationDataLookup result | _ -> - let! classificationData = checkerProvider.Checker.GetBackgroundSemanticClassificationForFile(document.FilePath, projectOptions, userOpName=userOpName) |> liftAsync + let! classificationData = document.GetFSharpSemanticClassificationAsync() let classificationDataLookup = toSemanticClassificationLookup classificationData - do! semanticClassificationCache.SetAsync(document, classificationDataLookup) |> liftAsync + do! semanticClassificationCache.SetAsync(document, classificationDataLookup) addSemanticClassificationByLookup sourceText textSpan classificationDataLookup result else - let! _, _, checkResults = checkerProvider.Checker.ParseAndCheckDocument(document, projectOptions, allowStaleResults = false, userOpName=userOpName) + let! _, checkResults = document.GetFSharpParseAndCheckResultsAsync() let targetRange = RoslynHelpers.TextSpanToFSharpRange(document.FilePath, textSpan, sourceText) let classificationData = checkResults.GetSemanticClassification (Some targetRange) addSemanticClassification sourceText textSpan classificationData result diff --git a/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs b/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs index 3890eba616f..3f21b29a21c 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs @@ -21,16 +21,12 @@ open FSharp.Compiler.Text type internal FSharpAddOpenCodeFixProvider [] ( - checkerProvider: FSharpCheckerProvider, - projectInfoManager: FSharpProjectOptionsManager, assemblyContentProvider: AssemblyContentProvider ) = inherit CodeFixProvider() - static let userOpName = "FSharpAddOpenCodeFixProvider" let fixableDiagnosticIds = ["FS0039"; "FS0043"] - let checker = checkerProvider.Checker let fixUnderscoresInMenuText (text: string) = text.Replace("_", "__") let qualifySymbolFix (context: CodeFixContext) (fullName, qualifier) = @@ -88,12 +84,12 @@ type internal FSharpAddOpenCodeFixProvider override _.RegisterCodeFixesAsync context : Task = asyncMaybe { let document = context.Document - let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, context.CancellationToken, userOpName) - let! sourceText = context.Document.GetTextAsync(context.CancellationToken) - let! _, parsedInput, checkResults = checker.ParseAndCheckDocument(document, projectOptions, userOpName = userOpName) + + let! sourceText = document.GetTextAsync(context.CancellationToken) + let! parseResults, checkResults = document.GetFSharpParseAndCheckResultsAsync() |> liftAsync let line = sourceText.Lines.GetLineFromPosition(context.Span.End) let linePos = sourceText.Lines.GetLinePosition(context.Span.End) - let defines = CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions + let! defines = document.Project.GetFSharpProjectDefinesAsync() |> liftAsync let! symbol = maybe { @@ -110,7 +106,7 @@ type internal FSharpAddOpenCodeFixProvider let endPos = Position.fromZ endLinePos.Line endLinePos.Character Range.mkRange context.Document.FilePath startPos endPos - let isAttribute = ParsedInput.GetEntityKind(unresolvedIdentRange.Start, parsedInput) = Some EntityKind.Attribute + let isAttribute = ParsedInput.GetEntityKind(unresolvedIdentRange.Start, parseResults.ParseTree) = Some EntityKind.Attribute let entities = assemblyContentProvider.GetAllEntitiesInProjectAndReferencedAssemblies checkResults @@ -126,7 +122,7 @@ type internal FSharpAddOpenCodeFixProvider s.CleanedIdents |> Array.replace (s.CleanedIdents.Length - 1) (lastIdent.Substring(0, lastIdent.Length - 9)) ]) - let longIdent = ParsedInput.GetLongIdentAt parsedInput unresolvedIdentRange.End + let longIdent = ParsedInput.GetLongIdentAt parseResults.ParseTree unresolvedIdentRange.End let! maybeUnresolvedIdents = longIdent @@ -141,7 +137,7 @@ type internal FSharpAddOpenCodeFixProvider if document.FSharpOptions.CodeFixes.AlwaysPlaceOpensAtTopLevel then OpenStatementInsertionPoint.TopLevel else OpenStatementInsertionPoint.Nearest - let createEntity = ParsedInput.TryFindInsertionContext unresolvedIdentRange.StartLine parsedInput maybeUnresolvedIdents insertionPoint + let createEntity = ParsedInput.TryFindInsertionContext unresolvedIdentRange.StartLine parseResults.ParseTree maybeUnresolvedIdents insertionPoint return entities |> Seq.map createEntity |> Seq.concat |> Seq.toList |> addSuggestionsAsCodeFixes context } |> Async.Ignore diff --git a/vsintegration/src/FSharp.Editor/CodeFix/AddTypeAnnotationToObjectOfIndeterminateType.fs b/vsintegration/src/FSharp.Editor/CodeFix/AddTypeAnnotationToObjectOfIndeterminateType.fs index cbf1b645648..22d41b04b10 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/AddTypeAnnotationToObjectOfIndeterminateType.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/AddTypeAnnotationToObjectOfIndeterminateType.fs @@ -20,13 +20,9 @@ open Microsoft.CodeAnalysis.CodeActions type internal FSharpAddTypeAnnotationToObjectOfIndeterminateTypeFixProvider [] ( - checkerProvider: FSharpCheckerProvider, - projectInfoManager: FSharpProjectOptionsManager ) = inherit CodeFixProvider() - static let userOpName = "AddTypeAnnotationToObjectOfIndeterminateType" - let fixableDiagnosticIds = set ["FS0072"; "FS3245"] override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds @@ -40,23 +36,21 @@ type internal FSharpAddTypeAnnotationToObjectOfIndeterminateTypeFixProvider let document = context.Document let position = context.Span.Start - let checker = checkerProvider.Checker - let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, context.CancellationToken, userOpName) - let! sourceText = document.GetTextAsync () |> liftTaskAsync - let defines = CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions + + let! sourceText = document.GetTextAsync(context.CancellationToken) let textLine = sourceText.Lines.GetLineFromPosition position let textLinePos = sourceText.Lines.GetLinePosition position let fcsTextLineNumber = Line.fromZ textLinePos.Line - let! _, _, checkFileResults = checker.ParseAndCheckDocument (document, projectOptions, userOpName=userOpName) - let! lexerSymbol = Tokenizer.getSymbolAtPosition (document.Id, sourceText, position, document.FilePath, defines, SymbolLookupKind.Greedy, false, false) + let! lexerSymbol = document.TryFindFSharpLexerSymbolAsync(position, SymbolLookupKind.Greedy, false, false) + + let! _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync() |> liftAsync let decl = checkFileResults.GetDeclarationLocation (fcsTextLineNumber, lexerSymbol.Ident.idRange.EndColumn, textLine.ToString(), lexerSymbol.FullIsland, false) match decl with | FindDeclResult.DeclFound declRange when declRange.FileName = document.FilePath -> let! declSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, declRange) let declTextLine = sourceText.Lines.GetLineFromPosition declSpan.Start - let! declLexerSymbol = Tokenizer.getSymbolAtPosition (document.Id, sourceText, position, document.FilePath, defines, SymbolLookupKind.Greedy, false, false) - let! symbolUse = checkFileResults.GetSymbolUseAtLocation(declRange.StartLine, declRange.EndColumn, declTextLine.ToString(), declLexerSymbol.FullIsland) + let! symbolUse = checkFileResults.GetSymbolUseAtLocation(declRange.StartLine, declRange.EndColumn, declTextLine.ToString(), lexerSymbol.FullIsland) match symbolUse.Symbol with | :? FSharpMemberOrFunctionOrValue as mfv -> let typeString = mfv.FullType.FormatWithConstraints symbolUse.DisplayContext diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ChangeRefCellDerefToNotExpression.fs b/vsintegration/src/FSharp.Editor/CodeFix/ChangeRefCellDerefToNotExpression.fs index b595cc21e55..ce93cd15087 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ChangeRefCellDerefToNotExpression.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/ChangeRefCellDerefToNotExpression.fs @@ -12,12 +12,9 @@ open Microsoft.CodeAnalysis.CodeFixes type internal FSharpChangeRefCellDerefToNotExpressionCodeFixProvider [] ( - checkerProvider: FSharpCheckerProvider, - projectInfoManager: FSharpProjectOptionsManager ) = inherit CodeFixProvider() - static let userOpName = "FSharpChangeRefCellDerefToNotExpressionCodeFix" let fixableDiagnosticIds = set ["FS0001"] override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds @@ -25,9 +22,8 @@ type internal FSharpChangeRefCellDerefToNotExpressionCodeFixProvider override this.RegisterCodeFixesAsync context : Task = asyncMaybe { let document = context.Document - let! parsingOptions, _ = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, context.CancellationToken, userOpName) + let! parseResults = document.GetFSharpParseResultsAsync() |> liftAsync let! sourceText = context.Document.GetTextAsync(context.CancellationToken) - let! parseResults = checkerProvider.Checker.ParseDocument(document, parsingOptions, userOpName=userOpName) let errorRange = RoslynHelpers.TextSpanToFSharpRange(document.FilePath, context.Span, sourceText) let! derefRange = parseResults.TryRangeOfRefCellDereferenceContainingPos errorRange.Start diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ConvertCSharpLambdaToFSharpLambda.fs b/vsintegration/src/FSharp.Editor/CodeFix/ConvertCSharpLambdaToFSharpLambda.fs index 169588e1c84..c476b396171 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ConvertCSharpLambdaToFSharpLambda.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/ConvertCSharpLambdaToFSharpLambda.fs @@ -11,20 +11,16 @@ open Microsoft.CodeAnalysis.CodeFixes type internal FSharpConvertCSharpLambdaToFSharpLambdaCodeFixProvider [] ( - checkerProvider: FSharpCheckerProvider, - projectInfoManager: FSharpProjectOptionsManager ) = inherit CodeFixProvider() - static let userOpName = "ConvertCSharpLambdaToFSharpLambda" let fixableDiagnosticIds = set ["FS0039"; "FS0043"] override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds override _.RegisterCodeFixesAsync context = asyncMaybe { - let! parsingOptions, _ = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(context.Document, context.CancellationToken, userOpName) - let! parseResults = checkerProvider.Checker.ParseDocument(context.Document, parsingOptions, userOpName) + let! parseResults = context.Document.GetFSharpParseResultsAsync() |> liftAsync let! sourceText = context.Document.GetTextAsync(context.CancellationToken) let errorRange = RoslynHelpers.TextSpanToFSharpRange(context.Document.FilePath, context.Span, sourceText) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ConvertToAnonymousRecord.fs b/vsintegration/src/FSharp.Editor/CodeFix/ConvertToAnonymousRecord.fs index 3710101ed4e..d225624891f 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ConvertToAnonymousRecord.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/ConvertToAnonymousRecord.fs @@ -14,13 +14,9 @@ open Microsoft.CodeAnalysis.CodeActions type internal FSharpConvertToAnonymousRecordCodeFixProvider [] ( - checkerProvider: FSharpCheckerProvider, - projectInfoManager: FSharpProjectOptionsManager ) = inherit CodeFixProvider() - static let userOpName = "ConvertToAnonymousRecord" - let fixableDiagnosticIds = set ["FS0039"] override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds @@ -28,8 +24,7 @@ type internal FSharpConvertToAnonymousRecordCodeFixProvider override _.RegisterCodeFixesAsync context : Task = asyncMaybe { let document = context.Document - let! parsingOptions, _ = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, context.CancellationToken, userOpName) - let! parseResults = checkerProvider.Checker.ParseDocument(document, parsingOptions, userOpName=userOpName) + let! parseResults = document.GetFSharpParseResultsAsync() |> liftAsync let! sourceText = context.Document.GetTextAsync(context.CancellationToken) let errorRange = RoslynHelpers.TextSpanToFSharpRange(document.FilePath, context.Span, sourceText) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs b/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs index c21a1123380..8bbad41ac1c 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs @@ -32,13 +32,9 @@ type internal InterfaceState = type internal FSharpImplementInterfaceCodeFixProvider [] ( - checkerProvider: FSharpCheckerProvider, - projectInfoManager: FSharpProjectOptionsManager ) = inherit CodeFixProvider() let fixableDiagnosticIds = ["FS0366"] - let checker = checkerProvider.Checker - static let userOpName = "ImplementInterfaceCodeFixProvider" let queryInterfaceState appendBracketAt (pos: pos) (tokens: Tokenizer.SavedTokenInfo[]) (ast: ParsedInput) = asyncMaybe { @@ -142,11 +138,11 @@ type internal FSharpImplementInterfaceCodeFixProvider override _.RegisterCodeFixesAsync context : Task = asyncMaybe { - let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(context.Document, context.CancellationToken, userOpName) + let! parseResults, checkFileResults = context.Document.GetFSharpParseAndCheckResultsAsync() |> liftAsync let cancellationToken = context.CancellationToken let! sourceText = context.Document.GetTextAsync(cancellationToken) - let! _, parsedInput, checkFileResults = checker.ParseAndCheckDocument(context.Document, projectOptions, userOpName = userOpName) let textLine = sourceText.Lines.GetLineFromPosition context.Span.Start + let! _, _, parsingOptions, _ = context.Document.Project.GetFSharpProjectOptionsAsync() |> liftAsync let defines = CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions // Notice that context.Span doesn't return reliable ranges to find tokens at exact positions. // That's why we tokenize the line and try to find the last successive identifier token @@ -172,7 +168,7 @@ type internal FSharpImplementInterfaceCodeFixProvider | '}' -> None | _ -> Some context.Span.End - let! interfaceState = queryInterfaceState appendBracketAt interfacePos tokens parsedInput + let! interfaceState = queryInterfaceState appendBracketAt interfacePos tokens parseResults.ParseTree let! symbol = Tokenizer.getSymbolAtPosition(context.Document.Id, sourceText, fixupPosition, context.Document.FilePath, defines, SymbolLookupKind.Greedy, false, false) let fcsTextLineNumber = textLine.LineNumber + 1 let lineContents = textLine.ToString() diff --git a/vsintegration/src/FSharp.Editor/CodeFix/MakeDeclarationMutable.fs b/vsintegration/src/FSharp.Editor/CodeFix/MakeDeclarationMutable.fs index 9aab9475320..eede648c21d 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/MakeDeclarationMutable.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/MakeDeclarationMutable.fs @@ -18,13 +18,9 @@ open FSharp.Compiler.Text type internal FSharpMakeDeclarationMutableFixProvider [] ( - checkerProvider: FSharpCheckerProvider, - projectInfoManager: FSharpProjectOptionsManager ) = inherit CodeFixProvider() - static let userOpName = "MakeDeclarationMutable" - let fixableDiagnosticIds = set ["FS0027"] override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds @@ -39,15 +35,13 @@ type internal FSharpMakeDeclarationMutableFixProvider let document = context.Document do! Option.guard (not(isSignatureFile document.FilePath)) let position = context.Span.Start - let checker = checkerProvider.Checker - let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, CancellationToken.None, userOpName) + + let! lexerSymbol = document.TryFindFSharpLexerSymbolAsync(position, SymbolLookupKind.Greedy, false, false) let! sourceText = document.GetTextAsync () |> liftTaskAsync - let defines = CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions let textLine = sourceText.Lines.GetLineFromPosition position let textLinePos = sourceText.Lines.GetLinePosition position let fcsTextLineNumber = Line.fromZ textLinePos.Line - let! parseFileResults, _, checkFileResults = checker.ParseAndCheckDocument (document, projectOptions, userOpName=userOpName) - let! lexerSymbol = Tokenizer.getSymbolAtPosition (document.Id, sourceText, position, document.FilePath, defines, SymbolLookupKind.Greedy, false, false) + let! parseFileResults, checkFileResults = document.GetFSharpParseAndCheckResultsAsync() |> liftAsync let decl = checkFileResults.GetDeclarationLocation (fcsTextLineNumber, lexerSymbol.Ident.idRange.EndColumn, textLine.ToString(), lexerSymbol.FullIsland, false) match decl with diff --git a/vsintegration/src/FSharp.Editor/CodeFix/MakeOuterBindingRecursive.fs b/vsintegration/src/FSharp.Editor/CodeFix/MakeOuterBindingRecursive.fs index 67b586688e5..86ead9637b9 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/MakeOuterBindingRecursive.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/MakeOuterBindingRecursive.fs @@ -12,20 +12,16 @@ open Microsoft.CodeAnalysis.CodeFixes type internal FSharpMakeOuterBindingRecursiveCodeFixProvider [] ( - checkerProvider: FSharpCheckerProvider, - projectInfoManager: FSharpProjectOptionsManager ) = inherit CodeFixProvider() - static let userOpName = "MakeOuterBindingRecursive" let fixableDiagnosticIds = set ["FS0039"] override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds override _.RegisterCodeFixesAsync context = asyncMaybe { - let! parsingOptions, _ = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(context.Document, context.CancellationToken, userOpName) - let! parseResults = checkerProvider.Checker.ParseDocument(context.Document, parsingOptions, userOpName) + let! parseResults = context.Document.GetFSharpParseResultsAsync() |> liftAsync let! sourceText = context.Document.GetTextAsync(context.CancellationToken) let diagnosticRange = RoslynHelpers.TextSpanToFSharpRange(context.Document.FilePath, context.Span, sourceText) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ProposeUppercaseLabel.fs b/vsintegration/src/FSharp.Editor/CodeFix/ProposeUppercaseLabel.fs index 71bfb049b51..65ebe4edb33 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ProposeUppercaseLabel.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/ProposeUppercaseLabel.fs @@ -12,19 +12,16 @@ open FSharp.Compiler.Diagnostics type internal FSharpProposeUpperCaseLabelCodeFixProvider [] ( - checkerProvider: FSharpCheckerProvider, - projectInfoManager: FSharpProjectOptionsManager ) = inherit CodeFixProvider() let fixableDiagnosticIds = ["FS0053"] - static let userOpName = "ProposeUpperCaseLabel" override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds override _.RegisterCodeFixesAsync context : Task = asyncMaybe { let textChanger (originalText: string) = originalText.[0].ToString().ToUpper() + originalText.Substring(1) - let! solutionChanger, originalText = SymbolHelpers.changeAllSymbolReferences(context.Document, context.Span, textChanger, projectInfoManager, checkerProvider.Checker, userOpName) + let! solutionChanger, originalText = SymbolHelpers.changeAllSymbolReferences(context.Document, context.Span, textChanger) let title = CompilerDiagnostics.GetErrorMessage (FSharpDiagnosticKind.ReplaceWithSuggestion <| textChanger originalText) context.RegisterCodeFix( CodeAction.Create(title, solutionChanger, title), diff --git a/vsintegration/src/FSharp.Editor/CodeFix/RemoveReturnOrYield.fs b/vsintegration/src/FSharp.Editor/CodeFix/RemoveReturnOrYield.fs index 59e6ff102c1..cec7283f4b6 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/RemoveReturnOrYield.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/RemoveReturnOrYield.fs @@ -11,20 +11,16 @@ open Microsoft.CodeAnalysis.CodeFixes type internal FSharpRemoveReturnOrYieldCodeFixProvider [] ( - checkerProvider: FSharpCheckerProvider, - projectInfoManager: FSharpProjectOptionsManager ) = inherit CodeFixProvider() - static let userOpName = "RemoveReturnOrYield" let fixableDiagnosticIds = set ["FS0748"; "FS0747"] override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds override _.RegisterCodeFixesAsync context = asyncMaybe { - let! parsingOptions, _ = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(context.Document, context.CancellationToken, userOpName) - let! parseResults = checkerProvider.Checker.ParseDocument(context.Document, parsingOptions, userOpName=userOpName) + let! parseResults = context.Document.GetFSharpParseResultsAsync() |> liftAsync let! sourceText = context.Document.GetTextAsync(context.CancellationToken) let errorRange = RoslynHelpers.TextSpanToFSharpRange(context.Document.FilePath, context.Span, sourceText) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedBinding.fs b/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedBinding.fs index 35e489e8675..4325ae11604 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedBinding.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedBinding.fs @@ -13,12 +13,10 @@ open Microsoft.CodeAnalysis.CodeFixes type internal FSharpRemoveUnusedBindingCodeFixProvider [] ( - checkerProvider: FSharpCheckerProvider, - projectInfoManager: FSharpProjectOptionsManager ) = inherit CodeFixProvider() - static let userOpName = "RemoveUnusedBinding" + let fixableDiagnosticIds = set ["FS1182"] override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds @@ -31,8 +29,7 @@ type internal FSharpRemoveUnusedBindingCodeFixProvider let document = context.Document let! sourceText = document.GetTextAsync(context.CancellationToken) - let! parsingOptions, _ = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, context.CancellationToken, userOpName) - let! parseResults = checkerProvider.Checker.ParseDocument(document, parsingOptions, userOpName=userOpName) + let! parseResults = context.Document.GetFSharpParseResultsAsync() |> liftAsync let diagnostics = context.Diagnostics diff --git a/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedOpens.fs b/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedOpens.fs index 1cc40bfa47b..9bc06368210 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedOpens.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedOpens.fs @@ -15,11 +15,9 @@ open FSharp.Compiler.Text type internal FSharpRemoveUnusedOpensCodeFixProvider [] ( - checkerProvider: FSharpCheckerProvider, - projectInfoManager: FSharpProjectOptionsManager ) = inherit CodeFixProvider() - let userOpName = "FSharpRemoveUnusedOpensCodeFixProvider" + let fixableDiagnosticIds = [FSharpIDEDiagnosticIds.RemoveUnnecessaryImportsDiagnosticId] override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds @@ -28,9 +26,7 @@ type internal FSharpRemoveUnusedOpensCodeFixProvider asyncMaybe { let document = context.Document let! sourceText = document.GetTextAsync() - let checker = checkerProvider.Checker - let! _, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, context.CancellationToken, userOpName) - let! unusedOpens = UnusedOpensDiagnosticAnalyzer.GetUnusedOpenRanges(document, projectOptions, checker) + let! unusedOpens = UnusedOpensDiagnosticAnalyzer.GetUnusedOpenRanges(document) let changes = unusedOpens |> List.map (fun m -> diff --git a/vsintegration/src/FSharp.Editor/CodeFix/RenameParamToMatchSignature.fs b/vsintegration/src/FSharp.Editor/CodeFix/RenameParamToMatchSignature.fs index c1671a31c95..64632f5bbf4 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/RenameParamToMatchSignature.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/RenameParamToMatchSignature.fs @@ -17,12 +17,10 @@ open FSharp.Compiler.Tokenization.FSharpKeywords type internal FSharpRenameParamToMatchSignature [] ( - checkerProvider: FSharpCheckerProvider, - projectInfoManager: FSharpProjectOptionsManager ) = inherit CodeFixProvider() - static let userOpName = "RenameParamToMatchSignature" + let fixableDiagnosticIds = ["FS3218"] @@ -44,7 +42,7 @@ type internal FSharpRenameParamToMatchSignature let document = context.Document let! cancellationToken = Async.CancellationToken |> liftAsync let! sourceText = document.GetTextAsync(cancellationToken) - let! symbolUses = getSymbolUsesOfSymbolAtLocationInDocument (document, context.Span.Start, projectInfoManager, checkerProvider.Checker, userOpName) + let! symbolUses = getSymbolUsesOfSymbolAtLocationInDocument (document, context.Span.Start) let changes = [| for symbolUse in symbolUses do match RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, symbolUse.Range) with diff --git a/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs b/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs index 96ecbcd4351..09cd2ccce57 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs @@ -18,14 +18,11 @@ open FSharp.Compiler.Syntax type internal FSharpRenameUnusedValueCodeFixProvider [] ( - checkerProvider: FSharpCheckerProvider, - projectInfoManager: FSharpProjectOptionsManager ) = inherit CodeFixProvider() - static let userOpName = "RenameUnusedValueCodeFix" + let fixableDiagnosticIds = set ["FS1182"] - let checker = checkerProvider.Checker override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds @@ -41,12 +38,10 @@ type internal FSharpRenameUnusedValueCodeFixProvider // We have to use the additional check for backtickes because `IsOperatorOrBacktickedName` operates on display names // where backtickes are replaced with parens. if not (PrettyNaming.IsOperatorOrBacktickedName ident) && not (ident.StartsWith "``") then - let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, context.CancellationToken, userOpName) - let! _, _, checkResults = checker.ParseAndCheckDocument(document, projectOptions, userOpName=userOpName) + let! lexerSymbol = document.TryFindFSharpLexerSymbolAsync(context.Span.Start, SymbolLookupKind.Greedy, false, false) let m = RoslynHelpers.TextSpanToFSharpRange(document.FilePath, context.Span, sourceText) - let defines = CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions - let! lexerSymbol = Tokenizer.getSymbolAtPosition (document.Id, sourceText, context.Span.Start, document.FilePath, defines, SymbolLookupKind.Greedy, false, false) let lineText = (sourceText.Lines.GetLineFromPosition context.Span.Start).ToString() + let! _, checkResults = document.GetFSharpParseAndCheckResultsAsync() |> liftAsync let! symbolUse = checkResults.GetSymbolUseAtLocation(m.StartLine, m.EndColumn, lineText, lexerSymbol.FullIsland) let symbolName = symbolUse.Symbol.DisplayName diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ReplaceWithSuggestion.fs b/vsintegration/src/FSharp.Editor/CodeFix/ReplaceWithSuggestion.fs index fdbd65bd228..ab56f001c06 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ReplaceWithSuggestion.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/ReplaceWithSuggestion.fs @@ -17,15 +17,11 @@ open FSharp.Compiler.Tokenization type internal FSharpReplaceWithSuggestionCodeFixProvider [] ( - checkerProvider: FSharpCheckerProvider, - projectInfoManager: FSharpProjectOptionsManager, settings: EditorOptions ) = inherit CodeFixProvider() - static let userOpName = "ReplaceWithSuggestionCodeFix" let fixableDiagnosticIds = set ["FS0039"; "FS1129"; "FS0495"] - let checker = checkerProvider.Checker override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds @@ -34,8 +30,7 @@ type internal FSharpReplaceWithSuggestionCodeFixProvider do! Option.guard settings.CodeFixes.SuggestNamesForErrors let document = context.Document - let! _, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, context.CancellationToken, userOpName) - let! parseFileResults, _, checkFileResults = checker.ParseAndCheckDocument(document, projectOptions, userOpName=userOpName) + let! parseFileResults, checkFileResults = document.GetFSharpParseAndCheckResultsAsync() |> liftAsync // This is all needed to get a declaration list let! sourceText = document.GetTextAsync(context.CancellationToken) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/UseMutationWhenValueIsMutable.fs b/vsintegration/src/FSharp.Editor/CodeFix/UseMutationWhenValueIsMutable.fs index 44a1fa5682d..dfbee7adf6d 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/UseMutationWhenValueIsMutable.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/UseMutationWhenValueIsMutable.fs @@ -19,13 +19,9 @@ open FSharp.Compiler.Text type internal FSharpUseMutationWhenValueIsMutableFixProvider [] ( - checkerProvider: FSharpCheckerProvider, - projectInfoManager: FSharpProjectOptionsManager ) = inherit CodeFixProvider() - static let userOpName = "UseMutationWhenValueIsMutable" - let fixableDiagnosticIds = set ["FS0020"] override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds @@ -39,10 +35,8 @@ type internal FSharpUseMutationWhenValueIsMutableFixProvider let document = context.Document do! Option.guard (not(isSignatureFile document.FilePath)) - let checker = checkerProvider.Checker - let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, CancellationToken.None, userOpName) - let! sourceText = document.GetTextAsync () |> liftTaskAsync - let defines = CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions + + let! sourceText = document.GetTextAsync(context.CancellationToken) let adjustedPosition = let rec loop ch pos = @@ -56,8 +50,8 @@ type internal FSharpUseMutationWhenValueIsMutableFixProvider let textLine = sourceText.Lines.GetLineFromPosition adjustedPosition let textLinePos = sourceText.Lines.GetLinePosition adjustedPosition let fcsTextLineNumber = Line.fromZ textLinePos.Line - let! _, _, checkFileResults = checker.ParseAndCheckDocument (document, projectOptions, userOpName=userOpName) - let! lexerSymbol = Tokenizer.getSymbolAtPosition (document.Id, sourceText, adjustedPosition, document.FilePath, defines, SymbolLookupKind.Greedy, false, false) + let! lexerSymbol = document.TryFindFSharpLexerSymbolAsync(adjustedPosition, SymbolLookupKind.Greedy, false, false) + let! _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync() |> liftAsync let! symbolUse = checkFileResults.GetSymbolUseAtLocation(fcsTextLineNumber, lexerSymbol.Ident.idRange.EndColumn, textLine.ToString(), lexerSymbol.FullIsland) match symbolUse.Symbol with diff --git a/vsintegration/src/FSharp.Editor/CodeLens/CodeLensProvider.fs b/vsintegration/src/FSharp.Editor/CodeLens/CodeLensProvider.fs index 096ac474d16..5a4f5fda4d3 100644 --- a/vsintegration/src/FSharp.Editor/CodeLens/CodeLensProvider.fs +++ b/vsintegration/src/FSharp.Editor/CodeLens/CodeLensProvider.fs @@ -24,7 +24,6 @@ type internal CodeLensProvider [)>] serviceProvider: IServiceProvider, textDocumentFactory: ITextDocumentFactoryService, checkerProvider: FSharpCheckerProvider, - projectInfoManager: FSharpProjectOptionsManager, typeMap : FSharpClassificationTypeMap Lazy, settings: EditorOptions ) = @@ -50,7 +49,7 @@ type internal CodeLensProvider ) let tagger = CodeLensGeneralTagger(wpfView, buffer) - let service = FSharpCodeLensService(serviceProvider, workspace, documentId, buffer, checkerProvider, projectInfoManager, componentModel.GetService(), typeMap, tagger, settings) + let service = FSharpCodeLensService(serviceProvider, workspace, documentId, buffer, checkerProvider, componentModel.GetService(), typeMap, tagger, settings) let provider = (wpfView, (tagger, service)) wpfView.Closed.Add (fun _ -> taggers.Remove provider |> ignore) taggers.Add((wpfView, (tagger, service))) @@ -69,7 +68,7 @@ type internal CodeLensProvider | _ -> None |> Option.get ) - let service = FSharpCodeLensService(serviceProvider, workspace, documentId, buffer, checkerProvider, projectInfoManager, componentModel.GetService(), typeMap, LineLensDisplayService(wpfView, buffer), settings) + let service = FSharpCodeLensService(serviceProvider, workspace, documentId, buffer, checkerProvider, componentModel.GetService(), typeMap, LineLensDisplayService(wpfView, buffer), settings) let provider = (wpfView, service) wpfView.Closed.Add (fun _ -> lineLensProvider.Remove provider |> ignore) lineLensProvider.Add(provider) diff --git a/vsintegration/src/FSharp.Editor/CodeLens/FSharpCodeLensService.fs b/vsintegration/src/FSharp.Editor/CodeLens/FSharpCodeLensService.fs index a9b1fc84584..3498828559b 100644 --- a/vsintegration/src/FSharp.Editor/CodeLens/FSharpCodeLensService.fs +++ b/vsintegration/src/FSharp.Editor/CodeLens/FSharpCodeLensService.fs @@ -45,7 +45,6 @@ type internal FSharpCodeLensService documentId: Lazy, buffer: ITextBuffer, checkerProvider: FSharpCheckerProvider, - projectInfoManager: FSharpProjectOptionsManager, classificationFormatMapService: IClassificationFormatMapService, typeMap: Lazy, codeLens : CodeLensDisplayService, @@ -53,8 +52,6 @@ type internal FSharpCodeLensService ) as self = let lineLens = codeLens - let userOpName = "FSharpCodeLensService" - let checker = checkerProvider.Checker let visit pos parseTree = SyntaxTraversal.Traverse(pos, parseTree, { new SyntaxVisitorBase<_>() with @@ -157,8 +154,8 @@ type internal FSharpCodeLensService logInfof "Rechecking code due to buffer edit!" #endif let! document = workspace.CurrentSolution.GetDocument(documentId.Value) |> Option.ofObj - let! _, options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, bufferChangedCts.Token, userOpName) - let! _, parsedInput, checkFileResults = checker.ParseAndCheckDocument(document, options, "LineLens", allowStaleResults=true) + let! parseFileResults, checkFileResults = document.GetFSharpParseAndCheckResultsAsync() |> liftAsync + let parsedInput = parseFileResults.ParseTree #if DEBUG logInfof "Getting uses of all symbols!" #endif @@ -194,7 +191,7 @@ type internal FSharpCodeLensService let taggedText = ResizeArray() typeLayout |> Seq.iter taggedText.Add let statusBar = StatusBar(serviceProvider.GetService()) - let navigation = QuickInfoNavigation(statusBar, checkerProvider, projectInfoManager, document, realPosition) + let navigation = QuickInfoNavigation(statusBar, checkerProvider, document, realPosition) // Because the data is available notify that this line should be updated, displaying the results return Some (taggedText, navigation) | None -> diff --git a/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs b/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs index 1f77bba3428..16562620157 100644 --- a/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs +++ b/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs @@ -20,14 +20,12 @@ open Microsoft.CodeAnalysis type internal FSharpHelpContextService [] ( - checkerProvider: FSharpCheckerProvider, projectInfoManager: FSharpProjectOptionsManager ) = - static let userOpName = "ImplementInterfaceCodeFix" - static member GetHelpTerm(checker: FSharpChecker, document: Document, options, span: TextSpan, tokens: List, perfOptions) : Async = + static member GetHelpTerm(document: Document, span: TextSpan, tokens: List) : Async = asyncMaybe { - let! _, _, check = checker.ParseAndCheckDocument(document, options, perfOptions, userOpName) + let! _, check = document.GetFSharpParseAndCheckResultsAsync() |> liftAsync let! sourceText = document.GetTextAsync() |> liftTaskAsync let textLines = sourceText.Lines let lineInfo = textLines.GetLineFromPosition(span.Start) @@ -101,13 +99,11 @@ type internal FSharpHelpContextService member this.GetHelpTermAsync(document, textSpan, cancellationToken) = asyncMaybe { - let! _parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, cancellationToken, userOpName) let! sourceText = document.GetTextAsync(cancellationToken) let defines = projectInfoManager.GetCompilationDefinesForEditingDocument(document) let textLine = sourceText.Lines.GetLineFromPosition(textSpan.Start) let classifiedSpans = Tokenizer.getClassifiedSpans(document.Id, sourceText, textLine.Span, Some document.Name, defines, cancellationToken) - let perfOptions = document.FSharpOptions.LanguageServicePerformance - return! FSharpHelpContextService.GetHelpTerm(checkerProvider.Checker, document, projectOptions, textSpan, classifiedSpans, perfOptions) + return! FSharpHelpContextService.GetHelpTerm(document, textSpan, classifiedSpans) } |> Async.map (Option.defaultValue "") |> RoslynHelpers.StartAsyncAsTask cancellationToken diff --git a/vsintegration/src/FSharp.Editor/Commands/XmlDocCommandService.fs b/vsintegration/src/FSharp.Editor/Commands/XmlDocCommandService.fs index 4dd22f0ffa1..d41e8a2561f 100644 --- a/vsintegration/src/FSharp.Editor/Commands/XmlDocCommandService.fs +++ b/vsintegration/src/FSharp.Editor/Commands/XmlDocCommandService.fs @@ -20,16 +20,10 @@ open FSharp.Compiler.EditorServices type internal XmlDocCommandFilter ( wpfTextView: IWpfTextView, - filePath: string, - checkerProvider: FSharpCheckerProvider, - projectInfoManager: FSharpProjectOptionsManager, + filePath: string, workspace: VisualStudioWorkspace ) = - static let userOpName = "XmlDocCommand" - - let checker = checkerProvider.Checker - let document = // There may be multiple documents with the same file path. // However, for the purpose of generating XmlDoc comments, it is ok to keep only the first document. @@ -67,10 +61,9 @@ type internal XmlDocCommandFilter // XmlDocable line #1 are 1-based, editor is 0-based let curLineNum = wpfTextView.Caret.Position.BufferPosition.GetContainingLine().LineNumber + 1 let! document = document.Value - let! parsingOptions, _options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, CancellationToken.None, userOpName) let! cancellationToken = Async.CancellationToken |> liftAsync let! sourceText = document.GetTextAsync(cancellationToken) - let! parseResults = checker.ParseDocument(document, parsingOptions, userOpName) + let! parseResults = document.GetFSharpParseResultsAsync() |> liftAsync let xmlDocables = XmlDocParser.GetXmlDocables (sourceText.ToFSharpSourceText(), parseResults.ParseTree) let xmlDocablesBelowThisLine = // +1 because looking below current line for e.g. a 'member' @@ -118,11 +111,11 @@ type internal XmlDocCommandFilter [] type internal XmlDocCommandFilterProvider [] - (checkerProvider: FSharpCheckerProvider, - projectInfoManager: FSharpProjectOptionsManager, + ( workspace: VisualStudioWorkspace, textDocumentFactoryService: ITextDocumentFactoryService, - editorFactory: IVsEditorAdaptersFactoryService) = + editorFactory: IVsEditorAdaptersFactoryService + ) = interface IWpfTextViewCreationListener with member _.TextViewCreated(textView) = match editorFactory.GetViewAdapter(textView) with @@ -130,6 +123,6 @@ type internal XmlDocCommandFilterProvider | textViewAdapter -> match textDocumentFactoryService.TryGetTextDocument(textView.TextBuffer) with | true, doc -> - let commandFilter = XmlDocCommandFilter(textView, doc.FilePath, checkerProvider, projectInfoManager, workspace) + let commandFilter = XmlDocCommandFilter(textView, doc.FilePath, workspace) commandFilter.AttachToViewAdapter textViewAdapter | _ -> () \ No newline at end of file diff --git a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs index f99d696c62e..027fac2ec84 100644 --- a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs +++ b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs @@ -28,14 +28,11 @@ type internal FSharpCompletionProvider ( workspace: Workspace, serviceProvider: SVsServiceProvider, - checkerProvider: FSharpCheckerProvider, - projectInfoManager: FSharpProjectOptionsManager, assemblyContentProvider: AssemblyContentProvider ) = inherit CompletionProvider() - static let userOpName = "CompletionProvider" // Save the backing data in a cache, we need to save for at least the length of the completion session // See https://github.com/Microsoft/visualfsharp/issues/4714 static let mutable declarationItems: DeclarationListItem[] = [||] @@ -59,8 +56,6 @@ type internal FSharpCompletionProvider sortText = sprintf "%06d" (1000000 + n)) .AddProperty(KeywordDescription, description)) - let checker = checkerProvider.Checker - let settings: EditorOptions = workspace.Services.GetService() let documentationBuilder = XmlDocumentation.CreateDocumentationBuilder(serviceProvider.XMLMemberIndexService) @@ -107,12 +102,11 @@ type internal FSharpCompletionProvider (triggerChar = '.' || (intelliSenseOptions.ShowAfterCharIsTyped && CompletionUtils.isStartingNewWord(sourceText, triggerPosition))) - static member ProvideCompletionsAsyncAux(checker: FSharpChecker, document: Document, caretPosition: int, options: FSharpProjectOptions, - getAllSymbols: FSharpCheckFileResults -> AssemblySymbol list, languageServicePerformanceOptions: LanguageServicePerformanceOptions, intellisenseOptions: IntelliSenseOptions) = + static member ProvideCompletionsAsyncAux(document: Document, caretPosition: int, getAllSymbols: FSharpCheckFileResults -> AssemblySymbol list, intellisenseOptions: IntelliSenseOptions) = asyncMaybe { - let! parseResults, _, checkFileResults = checker.ParseAndCheckDocument(document, options, languageServicePerformanceOptions, userOpName = userOpName) - let! sourceText = document.GetTextAsync() |> liftTaskAsync + let! parseResults, checkFileResults = document.GetFSharpParseAndCheckResultsAsync() |> liftAsync + let! sourceText = document.GetTextAsync() let textLines = sourceText.Lines let caretLinePos = textLines.GetLinePosition(caretPosition) let caretLine = textLines.GetLineFromPosition(caretPosition) @@ -211,7 +205,7 @@ type internal FSharpCompletionProvider let getInfo() = let documentId = workspace.GetDocumentIdInCurrentContext(sourceText.Container) let document = workspace.CurrentSolution.GetDocument(documentId) - let defines = projectInfoManager.GetCompilationDefinesForEditingDocument(document) + let defines = document.GetFSharpSyntaxDefines() (documentId, document.FilePath, defines) FSharpCompletionProvider.ShouldTriggerCompletionAux(sourceText, caretPosition, trigger.Kind, getInfo, settings.IntelliSense) @@ -221,16 +215,14 @@ type internal FSharpCompletionProvider use _logBlock = Logger.LogBlockMessage context.Document.Name LogEditorFunctionId.Completion_ProvideCompletionsAsync let document = context.Document let! sourceText = context.Document.GetTextAsync(context.CancellationToken) - let defines = projectInfoManager.GetCompilationDefinesForEditingDocument(document) + let defines = document.GetFSharpSyntaxDefines() do! Option.guard (CompletionUtils.shouldProvideCompletion(document.Id, document.FilePath, defines, sourceText, context.Position)) - let! _parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, context.CancellationToken, userOpName) let getAllSymbols(fileCheckResults: FSharpCheckFileResults) = if settings.IntelliSense.IncludeSymbolsFromUnopenedNamespacesOrModules then assemblyContentProvider.GetAllEntitiesInProjectAndReferencedAssemblies(fileCheckResults) else [] let! results = - FSharpCompletionProvider.ProvideCompletionsAsyncAux(checker, context.Document, context.Position, projectOptions, - getAllSymbols, settings.LanguageServicePerformance, settings.IntelliSense) + FSharpCompletionProvider.ProvideCompletionsAsyncAux(context.Document, context.Position, getAllSymbols, settings.IntelliSense) context.AddItems(results) } |> Async.Ignore |> RoslynHelpers.StartAsyncUnitAsTask context.CancellationToken @@ -291,8 +283,7 @@ type internal FSharpCompletionProvider let! sourceText = document.GetTextAsync(cancellationToken) let textWithItemCommitted = sourceText.WithChanges(TextChange(item.Span, nameInCode)) let line = sourceText.Lines.GetLineFromPosition(item.Span.Start) - let! parsingOptions, _options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, cancellationToken, userOpName) - let! parseResults = checker.ParseDocument(document, parsingOptions, userOpName) + let! parseResults = document.GetFSharpParseResultsAsync() |> liftAsync let fullNameIdents = fullName |> Option.map (fun x -> x.Split '.') |> Option.defaultValue [||] let insertionPoint = diff --git a/vsintegration/src/FSharp.Editor/Completion/CompletionService.fs b/vsintegration/src/FSharp.Editor/Completion/CompletionService.fs index e2c03429d55..6550e25a951 100644 --- a/vsintegration/src/FSharp.Editor/Completion/CompletionService.fs +++ b/vsintegration/src/FSharp.Editor/Completion/CompletionService.fs @@ -17,7 +17,6 @@ type internal FSharpCompletionService ( workspace: Workspace, serviceProvider: SVsServiceProvider, - checkerProvider: FSharpCheckerProvider, projectInfoManager: FSharpProjectOptionsManager, assemblyContentProvider: AssemblyContentProvider, settings: EditorOptions @@ -26,7 +25,7 @@ type internal FSharpCompletionService let builtInProviders = ImmutableArray.Create( - FSharpCompletionProvider(workspace, serviceProvider, checkerProvider, projectInfoManager, assemblyContentProvider), + FSharpCompletionProvider(workspace, serviceProvider, assemblyContentProvider), FSharpCommonCompletionProvider.Create(HashDirectiveCompletionProvider.Create(workspace, projectInfoManager))) override _.Language = FSharpConstants.FSharpLanguageName @@ -56,13 +55,12 @@ type internal FSharpCompletionServiceFactory [] ( serviceProvider: SVsServiceProvider, - checkerProvider: FSharpCheckerProvider, projectInfoManager: FSharpProjectOptionsManager, assemblyContentProvider: AssemblyContentProvider, settings: EditorOptions ) = interface ILanguageServiceFactory with member _.CreateLanguageService(hostLanguageServices: HostLanguageServices) : ILanguageService = - upcast new FSharpCompletionService(hostLanguageServices.WorkspaceServices.Workspace, serviceProvider, checkerProvider, projectInfoManager, assemblyContentProvider, settings) + upcast new FSharpCompletionService(hostLanguageServices.WorkspaceServices.Workspace, serviceProvider, projectInfoManager, assemblyContentProvider, settings) diff --git a/vsintegration/src/FSharp.Editor/Completion/SignatureHelp.fs b/vsintegration/src/FSharp.Editor/Completion/SignatureHelp.fs index 96ff3b2d7a2..e05adb17799 100644 --- a/vsintegration/src/FSharp.Editor/Completion/SignatureHelp.fs +++ b/vsintegration/src/FSharp.Editor/Completion/SignatureHelp.fs @@ -53,12 +53,9 @@ type SignatureHelpData = type internal FSharpSignatureHelpProvider [] ( - serviceProvider: SVsServiceProvider, - checkerProvider: FSharpCheckerProvider, - projectInfoManager: FSharpProjectOptionsManager + serviceProvider: SVsServiceProvider ) = - static let userOpName = "SignatureHelpProvider" let documentationBuilder = XmlDocumentation.CreateDocumentationBuilder(serviceProvider.XMLMemberIndexService) static let oneColAfter (lp: LinePosition) = LinePosition(lp.Line,lp.Character+1) @@ -500,23 +497,20 @@ type internal FSharpSignatureHelpProvider ( document: Document, defines: string list, - checker: FSharpChecker, documentationBuilder: IDocumentationBuilder, caretPosition: int, - options: FSharpProjectOptions, triggerTypedChar: char option, possibleCurrentSignatureHelpSessionKind: CurrentSignatureHelpSessionKind option ) = asyncMaybe { + let! parseResults, checkFileResults = document.GetFSharpParseAndCheckResultsAsync() |> liftAsync + let! sourceText = document.GetTextAsync() |> liftTaskAsync let textLines = sourceText.Lines - let perfOptions = document.FSharpOptions.LanguageServicePerformance let caretLinePos = textLines.GetLinePosition(caretPosition) let caretLineColumn = caretLinePos.Character - let! parseResults, _, checkFileResults = checker.ParseAndCheckDocument(document, options, perfOptions, userOpName = userOpName) - let adjustedColumnInSource = let rec loop ch pos = if Char.IsWhiteSpace(ch) then @@ -575,9 +569,7 @@ type internal FSharpSignatureHelpProvider member _.GetItemsAsync(document, position, triggerInfo, cancellationToken) = asyncMaybe { - let! _, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, cancellationToken, userOpName) - let defines = projectInfoManager.GetCompilationDefinesForEditingDocument(document) - let checker = checkerProvider.Checker + let defines = document.GetFSharpSyntaxDefines() let triggerTypedChar = if triggerInfo.TriggerCharacter.HasValue && triggerInfo.TriggerReason = FSharpSignatureHelpTriggerReason.TypeCharCommand then @@ -590,10 +582,8 @@ type internal FSharpSignatureHelpProvider FSharpSignatureHelpProvider.ProvideSignatureHelp( document, defines, - checker, documentationBuilder, position, - projectOptions, triggerTypedChar, possibleCurrentSignatureHelpSessionKind) match signatureHelpDataOpt with diff --git a/vsintegration/src/FSharp.Editor/Debugging/BreakpointResolutionService.fs b/vsintegration/src/FSharp.Editor/Debugging/BreakpointResolutionService.fs index 77f3081d879..315cc394604 100644 --- a/vsintegration/src/FSharp.Editor/Debugging/BreakpointResolutionService.fs +++ b/vsintegration/src/FSharp.Editor/Debugging/BreakpointResolutionService.fs @@ -21,12 +21,9 @@ open FSharp.Compiler.Text.Position type internal FSharpBreakpointResolutionService [] ( - checkerProvider: FSharpCheckerProvider, - projectInfoManager: FSharpProjectOptionsManager ) = - static let userOpName = "BreakpointResolution" - static member GetBreakpointLocation(checker: FSharpChecker, document: Document, textSpan: TextSpan, parsingOptions: FSharpParsingOptions) = + static member GetBreakpointLocation(document: Document, textSpan: TextSpan) = async { let! ct = Async.CancellationToken @@ -40,7 +37,7 @@ type internal FSharpBreakpointResolutionService else let textLineColumn = textLinePos.Character let fcsTextLineNumber = Line.fromZ textLinePos.Line // Roslyn line numbers are zero-based, FSharp.Compiler.Service line numbers are 1-based - let! parseResults = checker.ParseDocument(document, parsingOptions, userOpName) + let! parseResults = document.GetFSharpParseResultsAsync() |> liftAsync match parseResults with | Some parseResults -> return parseResults.ValidateBreakpointLocation(mkPos fcsTextLineNumber textLineColumn) | _ -> return None @@ -49,8 +46,7 @@ type internal FSharpBreakpointResolutionService interface IFSharpBreakpointResolutionService with member this.ResolveBreakpointAsync(document: Document, textSpan: TextSpan, cancellationToken: CancellationToken): Task = asyncMaybe { - let! parsingOptions, _options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, cancellationToken, userOpName) - let! range = FSharpBreakpointResolutionService.GetBreakpointLocation(checkerProvider.Checker, document, textSpan, parsingOptions) + let! range = FSharpBreakpointResolutionService.GetBreakpointLocation(document, textSpan) let! sourceText = document.GetTextAsync(cancellationToken) let! span = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, range) return FSharpBreakpointResolutionResult.CreateSpanResult(document, span) diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs index dd26fbf1026..2ebe818c0c6 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs @@ -25,12 +25,8 @@ type internal DiagnosticsType = type internal FSharpDocumentDiagnosticAnalyzer [] ( - checkerProvider: FSharpCheckerProvider, - projectInfoManager: FSharpProjectOptionsManager ) = - static let userOpName = "DocumentDiagnosticAnalyzer" - static let errorInfoEqualityComparer = { new IEqualityComparer with member _.Equals (x, y) = @@ -56,14 +52,11 @@ type internal FSharpDocumentDiagnosticAnalyzer hash } - static member GetDiagnostics(checker: FSharpChecker, document: Document, parsingOptions: FSharpParsingOptions, options: FSharpProjectOptions, diagnosticType: DiagnosticsType) = + static member GetDiagnostics(document: Document, diagnosticType: DiagnosticsType) = async { let! ct = Async.CancellationToken - let! parseResults = checker.ParseDocument(document, parsingOptions, userOpName) - match parseResults with - | None -> return ImmutableArray.Empty - | Some parseResults -> + let! parseResults = document.GetFSharpParseResultsAsync() let! sourceText = document.GetTextAsync(ct) |> Async.AwaitTask let filePath = document.FilePath @@ -72,14 +65,11 @@ type internal FSharpDocumentDiagnosticAnalyzer async { match diagnosticType with | DiagnosticsType.Semantic -> - let! checkResultsAnswer = checker.CheckDocument(document, parseResults, options, userOpName) - match checkResultsAnswer with - | FSharpCheckFileAnswer.Aborted -> return [||] - | FSharpCheckFileAnswer.Succeeded results -> - // In order to eleminate duplicates, we should not return parse errors here because they are returned by `AnalyzeSyntaxAsync` method. - let allErrors = HashSet(results.Diagnostics, errorInfoEqualityComparer) - allErrors.ExceptWith(parseResults.Diagnostics) - return Seq.toArray allErrors + let! _, checkResults = document.GetFSharpParseAndCheckResultsAsync() + // In order to eleminate duplicates, we should not return parse errors here because they are returned by `AnalyzeSyntaxAsync` method. + let allErrors = HashSet(checkResults.Diagnostics, errorInfoEqualityComparer) + allErrors.ExceptWith(parseResults.Diagnostics) + return Seq.toArray allErrors | DiagnosticsType.Syntax -> return parseResults.Diagnostics } @@ -119,9 +109,8 @@ type internal FSharpDocumentDiagnosticAnalyzer else asyncMaybe { - let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, cancellationToken, userOpName) return! - FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(checkerProvider.Checker, document, parsingOptions, projectOptions, DiagnosticsType.Syntax) + FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(document, DiagnosticsType.Syntax) |> liftAsync } |> Async.map (Option.defaultValue ImmutableArray.Empty) @@ -132,9 +121,8 @@ type internal FSharpDocumentDiagnosticAnalyzer else asyncMaybe { - let! parsingOptions, _, projectOptions = projectInfoManager.TryGetOptionsForDocumentOrProject(document, cancellationToken, userOpName) return! - FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(checkerProvider.Checker, document, parsingOptions, projectOptions, DiagnosticsType.Semantic) + FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(document, DiagnosticsType.Semantic) |> liftAsync } |> Async.map (Option.defaultValue ImmutableArray.Empty) diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/SimplifyNameDiagnosticAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/SimplifyNameDiagnosticAnalyzer.fs index 64007b19de8..435d09d6530 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/SimplifyNameDiagnosticAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/SimplifyNameDiagnosticAnalyzer.fs @@ -20,8 +20,6 @@ type private PerDocumentSavedData = { Hash: int; Diagnostics: ImmutableArray] ( - checkerProvider: FSharpCheckerProvider, - projectInfoManager: FSharpProjectOptionsManager ) = static let userOpName = "SimplifyNameDiagnosticAnalyzer" @@ -40,7 +38,6 @@ type internal SimplifyNameDiagnosticAnalyzer asyncMaybe { do! Option.guard document.FSharpOptions.CodeFixes.SimplifyName do Trace.TraceInformation("{0:n3} (start) SimplifyName", DateTime.Now.TimeOfDay.TotalSeconds) - let! _parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, cancellationToken, userOpName) let! textVersion = document.GetTextVersionAsync(cancellationToken) let textVersionHash = textVersion.GetHashCode() let! _ = guard.WaitAsync(cancellationToken) |> Async.AwaitTask |> liftAsync @@ -50,8 +47,7 @@ type internal SimplifyNameDiagnosticAnalyzer | :? PerDocumentSavedData as data when data.Hash = textVersionHash -> return data.Diagnostics | _ -> let! sourceText = document.GetTextAsync() - let checker = checkerProvider.Checker - let! _, _, checkResults = checker.ParseAndCheckDocument(document, projectOptions, userOpName=userOpName) + let! _, checkResults = document.GetFSharpParseAndCheckResultsAsync() |> liftAsync let! result = SimplifyNames.getSimplifiableNames(checkResults, fun lineNumber -> sourceText.Lines.[Line.toZ lineNumber].ToString()) |> liftAsync let mutable diag = ResizeArray() for r in result do diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs index 094796c9e36..892b45e7b0a 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs @@ -15,11 +15,7 @@ open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Diagnostics type internal UnusedDeclarationsAnalyzer [] ( - checkerProvider: FSharpCheckerProvider, - projectInfoManager: FSharpProjectOptionsManager ) = - - static let userOpName = "UnusedDeclarationsAnalyzer" interface IFSharpUnusedDeclarationsDiagnosticAnalyzer with @@ -31,16 +27,13 @@ type internal UnusedDeclarationsAnalyzer do! Option.guard document.FSharpOptions.CodeFixes.UnusedDeclarations do Trace.TraceInformation("{0:n3} (start) UnusedDeclarationsAnalyzer", DateTime.Now.TimeOfDay.TotalSeconds) - match! projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, cancellationToken, userOpName) with - | (_parsingOptions, projectOptions) -> - let! sourceText = document.GetTextAsync() - let checker = checkerProvider.Checker - let! _, _, checkResults = checker.ParseAndCheckDocument(document, projectOptions, userOpName = userOpName) - let! unusedRanges = UnusedDeclarations.getUnusedDeclarations( checkResults, (isScriptFile document.FilePath)) |> liftAsync - return - unusedRanges - |> Seq.map (fun m -> Diagnostic.Create(descriptor, RoslynHelpers.RangeToLocation(m, sourceText, document.FilePath))) - |> Seq.toImmutableArray + let! _, checkResults = document.GetFSharpParseAndCheckResultsAsync() |> liftAsync + let! unusedRanges = UnusedDeclarations.getUnusedDeclarations( checkResults, (isScriptFile document.FilePath)) |> liftAsync + let! sourceText = document.GetTextAsync() + return + unusedRanges + |> Seq.map (fun m -> Diagnostic.Create(descriptor, RoslynHelpers.RangeToLocation(m, sourceText, document.FilePath))) + |> Seq.toImmutableArray } |> Async.map (Option.defaultValue ImmutableArray.Empty) |> RoslynHelpers.StartAsyncAsTask cancellationToken diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs index c63341ecd0a..71792e2a08a 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs @@ -20,17 +20,13 @@ open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Diagnostics type internal UnusedOpensDiagnosticAnalyzer [] ( - checkerProvider: FSharpCheckerProvider, - projectInfoManager: FSharpProjectOptionsManager ) = - static let userOpName = "UnusedOpensAnalyzer" - - static member GetUnusedOpenRanges(document: Document, options, checker: FSharpChecker) : Async> = + static member GetUnusedOpenRanges(document: Document) : Async> = asyncMaybe { do! Option.guard document.FSharpOptions.CodeFixes.UnusedOpens let! sourceText = document.GetTextAsync() - let! _, _, checkResults = checker.ParseAndCheckDocument(document, options, userOpName = userOpName) + let! _, checkResults = document.GetFSharpParseAndCheckResultsAsync() |> liftAsync let! unusedOpens = UnusedOpens.getUnusedOpens(checkResults, fun lineNumber -> sourceText.Lines.[Line.toZ lineNumber].ToString()) |> liftAsync return unusedOpens } @@ -43,10 +39,8 @@ type internal UnusedOpensDiagnosticAnalyzer asyncMaybe { do Trace.TraceInformation("{0:n3} (start) UnusedOpensAnalyzer", DateTime.Now.TimeOfDay.TotalSeconds) - let! _parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, cancellationToken, userOpName) let! sourceText = document.GetTextAsync() - let checker = checkerProvider.Checker - let! unusedOpens = UnusedOpensDiagnosticAnalyzer.GetUnusedOpenRanges(document, projectOptions, checker) + let! unusedOpens = UnusedOpensDiagnosticAnalyzer.GetUnusedOpenRanges(document) return unusedOpens diff --git a/vsintegration/src/FSharp.Editor/DocumentHighlights/DocumentHighlightsService.fs b/vsintegration/src/FSharp.Editor/DocumentHighlights/DocumentHighlightsService.fs index c2309227658..1071fa8eabd 100644 --- a/vsintegration/src/FSharp.Editor/DocumentHighlights/DocumentHighlightsService.fs +++ b/vsintegration/src/FSharp.Editor/DocumentHighlights/DocumentHighlightsService.fs @@ -21,9 +21,7 @@ type internal FSharpHighlightSpan = override this.ToString() = sprintf "%+A" this [)>] -type internal FSharpDocumentHighlightsService [] (checkerProvider: FSharpCheckerProvider, projectInfoManager: FSharpProjectOptionsManager) = - - static let userOpName = "DocumentHighlights" +type internal FSharpDocumentHighlightsService [] () = /// Fix invalid spans if they appear to have redundant suffix and prefix. static let fixInvalidSymbolSpans (sourceText: SourceText) (lastIdent: string) (spans: FSharpHighlightSpan []) = @@ -51,16 +49,17 @@ type internal FSharpDocumentHighlightsService [] (checkerP |> Seq.distinctBy (fun span -> span.TextSpan.Start) |> Seq.toArray - static member GetDocumentHighlights(checker: FSharpChecker, document: Document, position: int, - defines: string list, options: FSharpProjectOptions, languageServicePerformanceOptions: LanguageServicePerformanceOptions) : Async = + static member GetDocumentHighlights(document: Document, position: int) : Async = asyncMaybe { - let! sourceText = document.GetTextAsync() |> liftTaskAsync - let filePath = document.FilePath + let! symbol = document.TryFindFSharpLexerSymbolAsync(position, SymbolLookupKind.Greedy, false, false) + + let! ct = Async.CancellationToken |> liftAsync + let! sourceText = document.GetTextAsync(ct) let textLine = sourceText.Lines.GetLineFromPosition(position) let textLinePos = sourceText.Lines.GetLinePosition(position) let fcsTextLineNumber = Line.fromZ textLinePos.Line - let! symbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, position, filePath, defines, SymbolLookupKind.Greedy, false, false) - let! _, _, checkFileResults = checker.ParseAndCheckDocument(document, options, languageServicePerformanceOptions, userOpName = userOpName) + + let! _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync() |> liftAsync let! symbolUse = checkFileResults.GetSymbolUseAtLocation(fcsTextLineNumber, symbol.Ident.idRange.EndColumn, textLine.ToString(), symbol.FullIsland) let symbolUses = checkFileResults.GetUsesOfSymbolInFile(symbolUse.Symbol) return @@ -76,10 +75,7 @@ type internal FSharpDocumentHighlightsService [] (checkerP interface IFSharpDocumentHighlightsService with member _.GetDocumentHighlightsAsync(document, position, _documentsToSearch, cancellationToken) : Task> = asyncMaybe { - let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, cancellationToken, userOpName) - let defines = CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions - let perfOptions = document.FSharpOptions.LanguageServicePerformance - let! spans = FSharpDocumentHighlightsService.GetDocumentHighlights(checkerProvider.Checker, document, position, defines, projectOptions, perfOptions) + let! spans = FSharpDocumentHighlightsService.GetDocumentHighlights(document, position) let highlightSpans = spans |> Array.map (fun span -> diff --git a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj index 39f449db8bc..59a76f18659 100644 --- a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj +++ b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj @@ -42,7 +42,6 @@ - @@ -52,6 +51,7 @@ + diff --git a/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs b/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs index d08d365f0c6..79f4033fcec 100644 --- a/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs +++ b/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs @@ -71,8 +71,6 @@ type internal InlineRenameLocationSet(locations: FSharpInlineRenameLocation [], type internal InlineRenameInfo ( - checker: FSharpChecker, - projectInfoManager: FSharpProjectOptionsManager, document: Document, triggerSpan: TextSpan, lexerSymbol: LexerSymbol, @@ -81,15 +79,13 @@ type internal InlineRenameInfo checkFileResults: FSharpCheckFileResults ) = - static let userOpName = "InlineRename" - let getDocumentText (document: Document) cancellationToken = match document.TryGetText() with | true, text -> text | _ -> document.GetTextAsync(cancellationToken).Result - let symbolUses = - SymbolHelpers.getSymbolUsesInSolution(symbolUse.Symbol, declLoc, checkFileResults, projectInfoManager, checker, document.Project.Solution, userOpName) + let symbolUses = + SymbolHelpers.getSymbolUsesInSolution(symbolUse.Symbol, declLoc, checkFileResults, document.Project.Solution) |> Async.cache interface IFSharpInlineRenameInfo with @@ -144,35 +140,31 @@ type internal InlineRenameInfo type internal InlineRenameService [] ( - projectInfoManager: FSharpProjectOptionsManager, - checkerProvider: FSharpCheckerProvider ) = - static let userOpName = "InlineRename" - static member GetInlineRenameInfo(checker: FSharpChecker, projectInfoManager: FSharpProjectOptionsManager, document: Document, sourceText: SourceText, position: int, - defines: string list, options: FSharpProjectOptions) : Async = + static member GetInlineRenameInfo(document: Document, position: int) : Async = asyncMaybe { + let! ct = Async.CancellationToken |> liftAsync + let! sourceText = document.GetTextAsync(ct) let textLine = sourceText.Lines.GetLineFromPosition(position) let textLinePos = sourceText.Lines.GetLinePosition(position) let fcsTextLineNumber = Line.fromZ textLinePos.Line - let! symbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, position, document.FilePath, defines, SymbolLookupKind.Greedy, false, false) - let! _, _, checkFileResults = checker.ParseAndCheckDocument(document, options, userOpName = userOpName) + let! symbol = document.TryFindFSharpLexerSymbolAsync(position, SymbolLookupKind.Greedy, false, false) + + let! _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync() |> liftAsync let! symbolUse = checkFileResults.GetSymbolUseAtLocation(fcsTextLineNumber, symbol.Ident.idRange.EndColumn, textLine.Text.ToString(), symbol.FullIsland) let! declLoc = symbolUse.GetDeclarationLocation(document) let! span = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, symbolUse.Range) let triggerSpan = Tokenizer.fixupSpan(sourceText, span) - return InlineRenameInfo(checker, projectInfoManager, document, triggerSpan, symbol, symbolUse, declLoc, checkFileResults) :> IFSharpInlineRenameInfo + return InlineRenameInfo(document, triggerSpan, symbol, symbolUse, declLoc, checkFileResults) :> IFSharpInlineRenameInfo } interface IFSharpEditorInlineRenameService with member _.GetRenameInfoAsync(document: Document, position: int, cancellationToken: CancellationToken) : Task = asyncMaybe { - let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, cancellationToken, userOpName) - let! sourceText = document.GetTextAsync(cancellationToken) - let defines = CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions - return! InlineRenameService.GetInlineRenameInfo(checkerProvider.Checker, projectInfoManager, document, sourceText, position, defines, projectOptions) + return! InlineRenameService.GetInlineRenameInfo(document, position) } |> Async.map (Option.defaultValue FailureInlineRenameInfo.Instance) |> RoslynHelpers.StartAsyncAsTask(cancellationToken) diff --git a/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerExtensions.fs deleted file mode 100644 index 44e2b4b9684..00000000000 --- a/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerExtensions.fs +++ /dev/null @@ -1,98 +0,0 @@ -[] -module internal Microsoft.VisualStudio.FSharp.Editor.FSharpCheckerExtensions - -open System - -open Microsoft.CodeAnalysis -open Microsoft.CodeAnalysis.Text - -open FSharp.Compiler.CodeAnalysis - -type FSharpChecker with - member checker.ParseDocument(document: Document, parsingOptions: FSharpParsingOptions, userOpName: string) = - asyncMaybe { - let! ct = Async.CancellationToken |> liftAsync - - let! sourceText = document.GetTextAsync(ct) |> liftTaskAsync - - let! fileParseResults = checker.ParseFile(document.FilePath, sourceText.ToFSharpSourceText(), parsingOptions, userOpName=userOpName) |> liftAsync - return fileParseResults - } - - member checker.CheckDocument(document: Document, parseResults: FSharpParseFileResults, options: FSharpProjectOptions, userOpName: string) = - async { - let! ct = Async.CancellationToken - - let! sourceText = document.GetTextAsync(ct) |> Async.AwaitTask - let! textVersion = document.GetTextVersionAsync(ct) |> Async.AwaitTask - - let filePath = document.FilePath - let textVersionHash = textVersion.GetHashCode() - - return! checker.CheckFileInProject(parseResults, filePath, textVersionHash, sourceText.ToFSharpSourceText(), options,userOpName=userOpName) - } - - member checker.ParseAndCheckDocument(document: Document, options: FSharpProjectOptions, languageServicePerformanceOptions: LanguageServicePerformanceOptions, userOpName: string) = - async { - let! ct = Async.CancellationToken - - let! sourceText = document.GetTextAsync(ct) |> Async.AwaitTask - let! textVersion = document.GetTextVersionAsync(ct) |> Async.AwaitTask - - let filePath = document.FilePath - let textVersionHash = textVersion.GetHashCode() - - let parseAndCheckFile = - async { - let! (parseResults, checkFileAnswer) = checker.ParseAndCheckFileInProject(filePath, textVersionHash, sourceText.ToFSharpSourceText(), options, userOpName=userOpName) - return - match checkFileAnswer with - | FSharpCheckFileAnswer.Aborted -> - None - | FSharpCheckFileAnswer.Succeeded(checkFileResults) -> - Some (parseResults, checkFileResults) - } - - let tryGetFreshResultsWithTimeout() = - async { - let! worker = Async.StartChild(async { try return! parseAndCheckFile with | _ -> return None }, millisecondsTimeout=languageServicePerformanceOptions.TimeUntilStaleCompletion) - try - return! worker - with :? TimeoutException -> - return None // worker is cancelled at this point, we cannot return it and wait its completion anymore - } - - let bindParsedInput(results: (FSharpParseFileResults * FSharpCheckFileResults) option) = - match results with - | Some(parseResults, checkResults) -> - Some (parseResults, parseResults.ParseTree, checkResults) - | None -> None - - if languageServicePerformanceOptions.AllowStaleCompletionResults then - let! freshResults = tryGetFreshResultsWithTimeout() - - let! results = - match freshResults with - | Some x -> async.Return (Some x) - | None -> - async { - match checker.TryGetRecentCheckResultsForFile(filePath, options, userOpName=userOpName) with - | Some (parseResults, checkFileResults, _) -> - return Some (parseResults, checkFileResults) - | None -> - return! parseAndCheckFile - } - return bindParsedInput results - else - let! results = parseAndCheckFile - return bindParsedInput results - } - - member checker.ParseAndCheckDocument(document: Document, options: FSharpProjectOptions, userOpName: string, ?allowStaleResults: bool) = - async { - let perfOpts = - match allowStaleResults with - | Some b -> { document.FSharpOptions.LanguageServicePerformance with AllowStaleCompletionResults = b } - | _ -> document.FSharpOptions.LanguageServicePerformance - return! checker.ParseAndCheckDocument(document, options, perfOpts, userOpName=userOpName) - } diff --git a/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs b/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs index 3ed760c5bae..06cc961d73e 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs @@ -18,51 +18,29 @@ open Microsoft.VisualStudio.FSharp.Editor.Symbols module internal SymbolHelpers = /// Used for local code fixes in a document, e.g. to rename local parameters - let getSymbolUsesOfSymbolAtLocationInDocument (document: Document, position: int, projectInfoManager: FSharpProjectOptionsManager, checker: FSharpChecker, userOpName) = + let getSymbolUsesOfSymbolAtLocationInDocument (document: Document, position: int) = asyncMaybe { + let! _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync() |> liftAsync + let! defines = document.Project.GetFSharpProjectDefinesAsync() |> liftAsync + let! cancellationToken = Async.CancellationToken |> liftAsync let! sourceText = document.GetTextAsync(cancellationToken) let textLine = sourceText.Lines.GetLineFromPosition(position) let textLinePos = sourceText.Lines.GetLinePosition(position) let fcsTextLineNumber = Line.fromZ textLinePos.Line - let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, cancellationToken, userOpName) - let defines = CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions let! symbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, position, document.FilePath, defines, SymbolLookupKind.Greedy, false, false) - let settings = document.FSharpOptions - let! _, _, checkFileResults = checker.ParseAndCheckDocument(document, projectOptions, settings.LanguageServicePerformance, userOpName = userOpName) let! symbolUse = checkFileResults.GetSymbolUseAtLocation(fcsTextLineNumber, symbol.Ident.idRange.EndColumn, textLine.ToString(), symbol.FullIsland) let! ct = Async.CancellationToken |> liftAsync let symbolUses = checkFileResults.GetUsesOfSymbolInFile(symbolUse.Symbol, cancellationToken=ct) return symbolUses } - let getSymbolUsesInProjects (symbol: FSharpSymbol, projectInfoManager: FSharpProjectOptionsManager, checker: FSharpChecker, projects: Project list, onFound: Document -> TextSpan -> range -> Async, userOpName) = + let getSymbolUsesInProjects (symbol: FSharpSymbol, projects: Project list, onFound: Document -> TextSpan -> range -> Async) = projects - |> Seq.map (fun project -> - async { - match! projectInfoManager.TryGetOptionsByProject(project, CancellationToken.None) with - | Some (_parsingOptions, projectOptions) -> - for filePath in projectOptions.SourceFiles do - let! symbolUses = checker.FindBackgroundReferencesInFile(filePath, projectOptions, symbol, canInvalidateProject = false, userOpName = userOpName) - let documentOpt = project.Solution.TryGetDocumentFromPath(filePath, project.Id) - match documentOpt with - | Some document -> - let! ct = Async.CancellationToken - let! sourceText = document.GetTextAsync ct |> Async.AwaitTask - for symbolUse in symbolUses do - match RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, symbolUse) with - | Some textSpan -> - do! onFound document textSpan symbolUse - | _ -> - () - | _ -> - () - | _ -> () - }) + |> Seq.map (fun project -> project.FindFSharpReferencesAsync(symbol, onFound)) |> Async.Sequential - let getSymbolUsesInSolution (symbol: FSharpSymbol, declLoc: SymbolDeclarationLocation, checkFileResults: FSharpCheckFileResults, - projectInfoManager: FSharpProjectOptionsManager, checker: FSharpChecker, solution: Solution, userOpName) = + let getSymbolUsesInSolution (symbol: FSharpSymbol, declLoc: SymbolDeclarationLocation, checkFileResults: FSharpCheckFileResults, solution: Solution) = async { let toDict (symbolUseRanges: range seq) = let groups = @@ -94,7 +72,7 @@ module internal SymbolHelpers = fun _ _ symbolUseRange -> async { symbolUseRanges.Add symbolUseRange } - let! _ = getSymbolUsesInProjects (symbol, projectInfoManager, checker, projects, onFound, userOpName) + let! _ = getSymbolUsesInProjects (symbol, projects, onFound) // Distinct these down because each TFM will produce a new 'project'. // Unless guarded by a #if define, symbols with the same range will be added N times @@ -112,7 +90,7 @@ module internal SymbolHelpers = // A better approach is to use something like createTextChangeCodeFix below, with a delayed function to compute a set of changes to be applied // simultaneously. But that doesn't work for this case, as we want a set of changes to apply acrosss the whole solution. - let changeAllSymbolReferences (document: Document, symbolSpan: TextSpan, textChanger: string -> string, projectInfoManager: FSharpProjectOptionsManager, checker: FSharpChecker, userOpName) + let changeAllSymbolReferences (document: Document, symbolSpan: TextSpan, textChanger: string -> string) : Async<(Func> * OriginalText) option> = asyncMaybe { do! Option.guard (symbolSpan.Length > 0) @@ -120,13 +98,13 @@ module internal SymbolHelpers = let! sourceText = document.GetTextAsync(cancellationToken) let originalText = sourceText.ToString(symbolSpan) do! Option.guard (originalText.Length > 0) - let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, cancellationToken, userOpName) - let defines = CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions - let! symbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, symbolSpan.Start, document.FilePath, defines, SymbolLookupKind.Greedy, false, false) - let! _, _, checkFileResults = checker.ParseAndCheckDocument(document, projectOptions, userOpName = userOpName) + + let! symbol = document.TryFindFSharpLexerSymbolAsync(symbolSpan.Start, SymbolLookupKind.Greedy, false, false) let textLine = sourceText.Lines.GetLineFromPosition(symbolSpan.Start) let textLinePos = sourceText.Lines.GetLinePosition(symbolSpan.Start) let fcsTextLineNumber = Line.fromZ textLinePos.Line + + let! _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync() |> liftAsync let! symbolUse = checkFileResults.GetSymbolUseAtLocation(fcsTextLineNumber, symbol.Ident.idRange.EndColumn, textLine.ToString(), symbol.FullIsland) let! declLoc = symbolUse.GetDeclarationLocation(document) let newText = textChanger originalText @@ -135,7 +113,7 @@ module internal SymbolHelpers = Func<_,_>(fun (cancellationToken: CancellationToken) -> async { let! symbolUsesByDocumentId = - getSymbolUsesInSolution(symbolUse.Symbol, declLoc, checkFileResults, projectInfoManager, checker, document.Project.Solution, userOpName) + getSymbolUsesInSolution(symbolUse.Symbol, declLoc, checkFileResults, document.Project.Solution) let mutable solution = document.Project.Solution diff --git a/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs b/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs index 53a0e68bd25..cd76a961892 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs @@ -20,11 +20,7 @@ open Microsoft.CodeAnalysis.Text type internal FSharpFindUsagesService [] ( - checkerProvider: FSharpCheckerProvider, - projectInfoManager: FSharpProjectOptionsManager ) = - - static let userOpName = "FindUsages" // File can be included in more than one project, hence single `range` may results with multiple `Document`s. let rangeToDocumentSpans (solution: Solution, range: range) = @@ -48,17 +44,14 @@ type internal FSharpFindUsagesService return spans |> Array.choose id |> Array.toList } - let findReferencedSymbolsAsync(document: Document, position: int, context: IFSharpFindUsagesContext, allReferences: bool, userOpName: string) : Async = + let findReferencedSymbolsAsync(document: Document, position: int, context: IFSharpFindUsagesContext, allReferences: bool) : Async = asyncMaybe { let! sourceText = document.GetTextAsync(context.CancellationToken) |> Async.AwaitTask |> liftAsync - let checker = checkerProvider.Checker - let! parsingOptions, _, projectOptions = projectInfoManager.TryGetOptionsForDocumentOrProject(document, context.CancellationToken, userOpName) - let! _, _, checkFileResults = checker.ParseAndCheckDocument(document, projectOptions, userOpName = userOpName) let textLine = sourceText.Lines.GetLineFromPosition(position).ToString() let lineNumber = sourceText.Lines.GetLinePosition(position).Line + 1 - let defines = CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions + let! symbol = document.TryFindFSharpLexerSymbolAsync(position, SymbolLookupKind.Greedy, false, false) - let! symbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, position, document.FilePath, defines, SymbolLookupKind.Greedy, false, false) + let! _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync() |> liftAsync let! symbolUse = checkFileResults.GetSymbolUseAtLocation(lineNumber, symbol.Ident.idRange.EndColumn, textLine, symbol.FullIsland) let declaration = checkFileResults.GetDeclarationLocation (lineNumber, symbol.Ident.idRange.EndColumn, textLine, symbol.FullIsland, false) let tags = FSharpGlyphTags.GetTags(Tokenizer.GetGlyphForSymbol (symbolUse.Symbol, symbol.Kind)) @@ -129,15 +122,15 @@ type internal FSharpFindUsagesService // In order to find all its usages we have to check all F# projects. | _ -> Seq.toList document.Project.Solution.Projects - let! _ = SymbolHelpers.getSymbolUsesInProjects (symbolUse.Symbol, projectInfoManager, checker, projectsToCheck, onFound, userOpName) |> liftAsync + let! _ = SymbolHelpers.getSymbolUsesInProjects (symbolUse.Symbol, projectsToCheck, onFound) |> liftAsync () } |> Async.Ignore interface IFSharpFindUsagesService with member _.FindReferencesAsync(document, position, context) = - findReferencedSymbolsAsync(document, position, context, true, userOpName) + findReferencedSymbolsAsync(document, position, context, true) |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) member _.FindImplementationsAsync(document, position, context) = - findReferencedSymbolsAsync(document, position, context, false, userOpName) + findReferencedSymbolsAsync(document, position, context, false) |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) \ No newline at end of file diff --git a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs index 240207ad780..eaa8aa490e5 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs @@ -151,9 +151,7 @@ type internal FSharpGoToDefinitionResult = | NavigableItem of FSharpNavigableItem | ExternalAssembly of FSharpSymbolUse * MetadataReference seq -type internal GoToDefinition(checkerProvider: FSharpCheckerProvider, projectInfoManager: FSharpProjectOptionsManager) = - let userOpName = "GoToDefinition" - let checker = checkerProvider.Checker +type internal GoToDefinition(checkerProvider: FSharpCheckerProvider) = let metadataAsSourceService = checkerProvider.MetadataAsSource /// Use an origin document to provide the solution & workspace used to @@ -176,18 +174,15 @@ type internal GoToDefinition(checkerProvider: FSharpCheckerProvider, projectInfo /// Helper function that is used to determine the navigation strategy to apply, can be tuned towards signatures or implementation files. member private _.FindSymbolHelper (originDocument: Document, originRange: range, sourceText: SourceText, preferSignature: bool) = asyncMaybe { - let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(originDocument, CancellationToken.None, userOpName) - let defines = CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions let! originTextSpan = RoslynHelpers.TryFSharpRangeToTextSpan (sourceText, originRange) let position = originTextSpan.Start - let! lexerSymbol = Tokenizer.getSymbolAtPosition (originDocument.Id, sourceText, position, originDocument.FilePath, defines, SymbolLookupKind.Greedy, false, false) - + let! lexerSymbol = originDocument.TryFindFSharpLexerSymbolAsync(position, SymbolLookupKind.Greedy, false, false) let textLinePos = sourceText.Lines.GetLinePosition position let fcsTextLineNumber = Line.fromZ textLinePos.Line let lineText = (sourceText.Lines.GetLineFromPosition position).ToString() - - let! _, _, checkFileResults = checker.ParseAndCheckDocument (originDocument, projectOptions, userOpName=userOpName) let idRange = lexerSymbol.Ident.idRange + + let! _, checkFileResults = originDocument.GetFSharpParseAndCheckResultsAsync() |> liftAsync let! fsSymbolUse = checkFileResults.GetSymbolUseAtLocation (fcsTextLineNumber, idRange.EndColumn, lineText, lexerSymbol.FullIsland) let symbol = fsSymbolUse.Symbol // if the tooltip was spawned in an implementation file and we have a range targeting @@ -198,8 +193,7 @@ type internal GoToDefinition(checkerProvider: FSharpCheckerProvider, projectInfo if not (File.Exists fsfilePath) then return! None else let! implDoc = originDocument.Project.Solution.TryGetDocumentFromPath fsfilePath let! implSourceText = implDoc.GetTextAsync () - let! _parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(implDoc, CancellationToken.None, userOpName) - let! _, _, checkFileResults = checker.ParseAndCheckDocument (implDoc, projectOptions, userOpName=userOpName) + let! _, checkFileResults = implDoc.GetFSharpParseAndCheckResultsAsync() |> liftAsync let symbolUses = checkFileResults.GetUsesOfSymbolInFile symbol let! implSymbol = symbolUses |> Array.tryHead let! implTextSpan = RoslynHelpers.TryFSharpRangeToTextSpan (implSourceText, implSymbol.Range) @@ -212,13 +206,13 @@ type internal GoToDefinition(checkerProvider: FSharpCheckerProvider, projectInfo /// if the symbol is defined in the given file, return its declaration location, otherwise use the targetSymbol to find the first /// instance of its presence in the provided source file. The first case is needed to return proper declaration location for /// recursive type definitions, where the first its usage may not be the declaration. - member _.FindSymbolDeclarationInDocument(targetSymbolUse: FSharpSymbolUse, document: Document, options: FSharpProjectOptions) = + member _.FindSymbolDeclarationInDocument(targetSymbolUse: FSharpSymbolUse, document: Document) = asyncMaybe { let filePath = document.FilePath match targetSymbolUse.Symbol.DeclarationLocation with | Some decl when decl.FileName = filePath -> return decl | _ -> - let! _, _, checkFileResults = checker.ParseAndCheckDocument(document, options, userOpName) + let! _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync() |> liftAsync let symbolUses = checkFileResults.GetUsesOfSymbolInFile targetSymbolUse.Symbol let! implSymbol = symbolUses |> Array.tryHead return implSymbol.Range @@ -226,9 +220,7 @@ type internal GoToDefinition(checkerProvider: FSharpCheckerProvider, projectInfo member private this.FindDefinitionAtPosition(originDocument: Document, position: int, cancellationToken: CancellationToken) = asyncMaybe { - let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(originDocument, CancellationToken.None, userOpName) - let! sourceText = originDocument.GetTextAsync(cancellationToken) |> liftTaskAsync - let defines = CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions + let! sourceText = originDocument.GetTextAsync(cancellationToken) let textLine = sourceText.Lines.GetLineFromPosition position let textLinePos = sourceText.Lines.GetLinePosition position let textLineString = textLine.ToString() @@ -236,12 +228,11 @@ type internal GoToDefinition(checkerProvider: FSharpCheckerProvider, projectInfo let lineText = (sourceText.Lines.GetLineFromPosition position).ToString() let preferSignature = isSignatureFile originDocument.FilePath - - let! _, _, checkFileResults = checker.ParseAndCheckDocument (originDocument, projectOptions, userOpName=userOpName) - let! lexerSymbol = Tokenizer.getSymbolAtPosition (originDocument.Id, sourceText, position,originDocument.FilePath, defines, SymbolLookupKind.Greedy, false, false) + let! lexerSymbol = originDocument.TryFindFSharpLexerSymbolAsync(position, SymbolLookupKind.Greedy, false, false) let idRange = lexerSymbol.Ident.idRange + let! _, checkFileResults = originDocument.GetFSharpParseAndCheckResultsAsync() |> liftAsync let declarations = checkFileResults.GetDeclarationLocation (fcsTextLineNumber, lexerSymbol.Ident.idRange.EndColumn, textLineString, lexerSymbol.FullIsland, preferSignature) let! targetSymbolUse = checkFileResults.GetSymbolUseAtLocation (fcsTextLineNumber, idRange.EndColumn, lineText, lexerSymbol.FullIsland) @@ -285,7 +276,7 @@ type internal GoToDefinition(checkerProvider: FSharpCheckerProvider, projectInfo if not (File.Exists implFilePath) then return! None else let! implDocument = originDocument.Project.Solution.TryGetDocumentFromPath implFilePath - let! targetRange = this.FindSymbolDeclarationInDocument(targetSymbolUse, implDocument, projectOptions) + let! targetRange = this.FindSymbolDeclarationInDocument(targetSymbolUse, implDocument) let! implSourceText = implDocument.GetTextAsync(cancellationToken) |> liftTaskAsync let! implTextSpan = RoslynHelpers.TryFSharpRangeToTextSpan (implSourceText, targetRange) let navItem = FSharpGoToDefinitionNavigableItem (implDocument, implTextSpan) @@ -320,10 +311,8 @@ type internal GoToDefinition(checkerProvider: FSharpCheckerProvider, projectInfo else sigDocument.FilePath let! implDocument = originDocument.Project.Solution.TryGetDocumentFromPath implFilePath - - let! _, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(implDocument, cancellationToken, userOpName) - let! targetRange = this.FindSymbolDeclarationInDocument(targetSymbolUse, implDocument, projectOptions) + let! targetRange = this.FindSymbolDeclarationInDocument(targetSymbolUse, implDocument) let! implSourceText = implDocument.GetTextAsync () |> liftTaskAsync let! implTextSpan = RoslynHelpers.TryFSharpRangeToTextSpan (implSourceText, targetRange) @@ -425,8 +414,7 @@ type internal GoToDefinition(checkerProvider: FSharpCheckerProvider, projectInfo | Some tmpShownDoc -> let goToAsync = asyncMaybe { - let! _, _, projectOptions = projectInfoManager.TryGetOptionsForDocumentOrProject (tmpShownDoc, cancellationToken, userOpName) - let! _, _, checkResults = checker.ParseAndCheckDocument(tmpShownDoc, projectOptions, userOpName) + let! _, checkResults = tmpShownDoc.GetFSharpParseAndCheckResultsAsync() |> liftAsync let! r = let rec areTypesEqual (ty1: FSharpType) (ty2: FSharpType) = let ty1 = ty1.StripAbbreviations() diff --git a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinitionService.fs b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinitionService.fs index d74b8049dfa..a61c6b5627f 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinitionService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinitionService.fs @@ -18,11 +18,10 @@ open Microsoft.VisualStudio.Shell.Interop type internal FSharpGoToDefinitionService [] ( - checkerProvider: FSharpCheckerProvider, - projectInfoManager: FSharpProjectOptionsManager + checkerProvider: FSharpCheckerProvider ) = - let gtd = GoToDefinition(checkerProvider, projectInfoManager) + let gtd = GoToDefinition(checkerProvider) let statusBar = StatusBar(ServiceProvider.GlobalProvider.GetService()) interface IFSharpGoToDefinitionService with diff --git a/vsintegration/src/FSharp.Editor/Navigation/NavigableSymbolsService.fs b/vsintegration/src/FSharp.Editor/Navigation/NavigableSymbolsService.fs index 6bc1822438c..78cfeaa3a40 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/NavigableSymbolsService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/NavigableSymbolsService.fs @@ -28,10 +28,10 @@ type internal FSharpNavigableSymbol(item: FSharpNavigableItem, span: SnapshotSpa member _.SymbolSpan = span -type internal FSharpNavigableSymbolSource(checkerProvider: FSharpCheckerProvider, projectInfoManager: FSharpProjectOptionsManager, serviceProvider: IServiceProvider) = +type internal FSharpNavigableSymbolSource(checkerProvider: FSharpCheckerProvider, serviceProvider: IServiceProvider) = let mutable disposed = false - let gtd = GoToDefinition(checkerProvider, projectInfoManager) + let gtd = GoToDefinition(checkerProvider) let statusBar = StatusBar(serviceProvider.GetService()) interface INavigableSymbolSource with @@ -107,10 +107,9 @@ type internal FSharpNavigableSymbolService [] ( [)>] serviceProvider: IServiceProvider, - checkerProvider: FSharpCheckerProvider, - projectInfoManager: FSharpProjectOptionsManager + checkerProvider: FSharpCheckerProvider ) = interface INavigableSymbolSourceProvider with member _.TryCreateNavigableSymbolSource(_: ITextView, _: ITextBuffer) = - new FSharpNavigableSymbolSource(checkerProvider, projectInfoManager, serviceProvider) :> INavigableSymbolSource \ No newline at end of file + new FSharpNavigableSymbolSource(checkerProvider, serviceProvider) :> INavigableSymbolSource \ No newline at end of file diff --git a/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs b/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs index 821dab6624c..6675e7c5399 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs @@ -169,49 +169,43 @@ module private Utils = type internal FSharpNavigateToSearchService [] ( - checkerProvider: FSharpCheckerProvider, - projectInfoManager: FSharpProjectOptionsManager ) = - let userOpName = "FSharpNavigateToSearchService" let kindsProvided = ImmutableHashSet.Create(FSharpNavigateToItemKind.Module, FSharpNavigateToItemKind.Class, FSharpNavigateToItemKind.Field, FSharpNavigateToItemKind.Property, FSharpNavigateToItemKind.Method, FSharpNavigateToItemKind.Enum, FSharpNavigateToItemKind.EnumItem) :> IImmutableSet // Save the backing navigation data in a memory cache held in a sliding window let itemsByDocumentId = new MemoryCache("FSharp.Editor.FSharpNavigateToSearchService") - let GetNavigableItems(document: Document, parsingOptions: FSharpParsingOptions, kinds: IImmutableSet) = + let GetNavigableItems(document: Document, kinds: IImmutableSet) = async { let! cancellationToken = Async.CancellationToken - let! parseResults = checkerProvider.Checker.ParseDocument(document, parsingOptions, userOpName) - match parseResults with - | None -> return [||] - | Some parseResults -> - let! sourceText = document.GetTextAsync(cancellationToken) |> Async.AwaitTask - let navItems parsedInput = - NavigateTo.GetNavigableItems parsedInput - |> Array.filter (fun i -> kinds.Contains(navigateToItemKindToRoslynKind i.Kind)) - - let items = parseResults.ParseTree |> navItems - let navigableItems = - [| - for item in items do - match RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, item.Range) with - | None -> () - | Some sourceSpan -> - let glyph = navigateToItemKindToGlyph item.Kind - let kind = navigateToItemKindToRoslynKind item.Kind - let additionalInfo = containerToString item.Container document - let _name = - if isSignatureFile document.FilePath then - item.Name + " (signature)" - else - item.Name - yield NavigableItem(document, sourceSpan, glyph, item.Name, kind, additionalInfo) - |] - return navigableItems + let! parseResults = document.GetFSharpParseResultsAsync() + let! sourceText = document.GetTextAsync(cancellationToken) |> Async.AwaitTask + let navItems parsedInput = + NavigateTo.GetNavigableItems parsedInput + |> Array.filter (fun i -> kinds.Contains(navigateToItemKindToRoslynKind i.Kind)) + + let items = parseResults.ParseTree |> navItems + let navigableItems = + [| + for item in items do + match RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, item.Range) with + | None -> () + | Some sourceSpan -> + let glyph = navigateToItemKindToGlyph item.Kind + let kind = navigateToItemKindToRoslynKind item.Kind + let additionalInfo = containerToString item.Container document + let _name = + if isSignatureFile document.FilePath then + item.Name + " (signature)" + else + item.Name + yield NavigableItem(document, sourceSpan, glyph, item.Name, kind, additionalInfo) + |] + return navigableItems } - let getCachedIndexedNavigableItems(document: Document, parsingOptions: FSharpParsingOptions, kinds: IImmutableSet) = + let getCachedIndexedNavigableItems(document: Document, kinds: IImmutableSet) = async { let! cancellationToken = Async.CancellationToken let! textVersion = document.GetTextVersionAsync(cancellationToken) |> Async.AwaitTask @@ -220,7 +214,7 @@ type internal FSharpNavigateToSearchService match itemsByDocumentId.Get(key) with | :? PerDocumentSavedData as data when data.Hash = textVersionHash -> return data.Items | _ -> - let! items = GetNavigableItems(document, parsingOptions, kinds) + let! items = GetNavigableItems(document, kinds) let indexedItems = Index.build items let data = { Hash= textVersionHash; Items = indexedItems } let cacheItem = CacheItem(key, data) @@ -239,10 +233,9 @@ type internal FSharpNavigateToSearchService interface IFSharpNavigateToSearchService with member _.SearchProjectAsync(project, _priorityDocuments, searchPattern, kinds, cancellationToken) : Task> = asyncMaybe { - let! parsingOptions, _options = projectInfoManager.TryGetOptionsByProject(project, cancellationToken) let! items = project.Documents - |> Seq.map (fun document -> getCachedIndexedNavigableItems(document, parsingOptions, kinds)) + |> Seq.map (fun document -> getCachedIndexedNavigableItems(document, kinds)) |> Async.Parallel |> liftAsync @@ -271,8 +264,7 @@ type internal FSharpNavigateToSearchService member _.SearchDocumentAsync(document, searchPattern, kinds, cancellationToken) : Task> = asyncMaybe { - let! parsingOptions, _, _ = projectInfoManager.TryGetOptionsForDocumentOrProject(document, cancellationToken, userOpName) - let! items = getCachedIndexedNavigableItems(document, parsingOptions, kinds) |> liftAsync + let! items = getCachedIndexedNavigableItems(document, kinds) |> liftAsync return items.Find(searchPattern) } |> Async.map (Option.defaultValue [||]) diff --git a/vsintegration/src/FSharp.Editor/Navigation/NavigationBarItemService.fs b/vsintegration/src/FSharp.Editor/Navigation/NavigationBarItemService.fs index c83fb363a2b..0998b54d472 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/NavigationBarItemService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/NavigationBarItemService.fs @@ -17,20 +17,16 @@ type internal NavigationBarSymbolItem(text, glyph, spans, childItems) = type internal FSharpNavigationBarItemService [] ( - checkerProvider: FSharpCheckerProvider, - projectInfoManager: FSharpProjectOptionsManager ) = - static let userOpName = "NavigationBarItem" static let emptyResult: IList = upcast [||] interface IFSharpNavigationBarItemService with member _.GetItemsAsync(document, cancellationToken) : Task> = asyncMaybe { - let! parsingOptions, _options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, cancellationToken, userOpName) - let! sourceText = document.GetTextAsync(cancellationToken) - let! parseResults = checkerProvider.Checker.ParseDocument(document, parsingOptions, userOpName=userOpName) + let! parseResults = document.GetFSharpParseResultsAsync() |> liftAsync let navItems = Navigation.getNavigation parseResults.ParseTree + let! sourceText = document.GetTextAsync(cancellationToken) let rangeToTextSpan range = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, range) return navItems.Declarations diff --git a/vsintegration/src/FSharp.Editor/QuickInfo/Navigation.fs b/vsintegration/src/FSharp.Editor/QuickInfo/Navigation.fs index e596239cc23..2f8d1e7198c 100644 --- a/vsintegration/src/FSharp.Editor/QuickInfo/Navigation.fs +++ b/vsintegration/src/FSharp.Editor/QuickInfo/Navigation.fs @@ -15,7 +15,6 @@ type internal QuickInfoNavigation ( statusBar: StatusBar, checkerProvider: FSharpCheckerProvider, - projectInfoManager: FSharpProjectOptionsManager, initialDoc: Document, thisSymbolUseRange: range ) = @@ -43,7 +42,7 @@ type internal QuickInfoNavigation let! targetDoc = solution.TryGetDocumentFromFSharpRange (range, initialDoc.Project.Id) let! targetSource = targetDoc.GetTextAsync() let! targetTextSpan = RoslynHelpers.TryFSharpRangeToTextSpan (targetSource, range) - let gtd = GoToDefinition(checkerProvider, projectInfoManager) + let gtd = GoToDefinition(checkerProvider) // To ensure proper navigation decsions, we need to check the type of document the navigation call // is originating from and the target we're provided by default: diff --git a/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs b/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs index 0d383f76136..f027d310692 100644 --- a/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs +++ b/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs @@ -39,8 +39,6 @@ module internal FSharpQuickInfo = // therefore we should include these doccoms in our design time quick info let getQuickInfoFromRange ( - checker: FSharpChecker, - projectInfoManager: FSharpProjectOptionsManager, document: Document, declRange: range, cancellationToken: CancellationToken @@ -57,10 +55,8 @@ module internal FSharpQuickInfo = let extLineText = (extSourceText.Lines.GetLineFromPosition extSpan.Start).ToString() // project options need to be retrieved because the signature file could be in another project - let! extParsingOptions, extProjectOptions = projectInfoManager.TryGetOptionsByProject(extDocument.Project, cancellationToken) - let extDefines = CompilerEnvironment.GetCompilationDefinesForEditing extParsingOptions - let! extLexerSymbol = Tokenizer.getSymbolAtPosition(extDocId, extSourceText, extSpan.Start, declRange.FileName, extDefines, SymbolLookupKind.Greedy, true, true) - let! _, _, extCheckFileResults = checker.ParseAndCheckDocument(extDocument, extProjectOptions, allowStaleResults=true, userOpName = userOpName) + let! extLexerSymbol = extDocument.TryFindFSharpLexerSymbolAsync(extSpan.Start, SymbolLookupKind.Greedy, true, true) + let! _, extCheckFileResults = extDocument.GetFSharpParseAndCheckResultsAsync() |> liftAsync let extQuickInfoText = extCheckFileResults.GetToolTip @@ -83,8 +79,6 @@ module internal FSharpQuickInfo = /// Get QuickInfo combined from doccom of Signature and definition let getQuickInfo ( - checker: FSharpChecker, - projectInfoManager: FSharpProjectOptionsManager, document: Document, position: int, cancellationToken: CancellationToken @@ -92,12 +86,10 @@ module internal FSharpQuickInfo = : Async<(range * QuickInfo option * QuickInfo option) option> = asyncMaybe { + let! lexerSymbol = document.TryFindFSharpLexerSymbolAsync(position, SymbolLookupKind.Greedy, true, true) + let! _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync() |> liftAsync let! sourceText = document.GetTextAsync cancellationToken - let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, cancellationToken, userOpName) - let defines = CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions - let! lexerSymbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, position, document.FilePath, defines, SymbolLookupKind.Greedy, true, true) let idRange = lexerSymbol.Ident.idRange - let! _, _, checkFileResults = checker.ParseAndCheckDocument(document, projectOptions, allowStaleResults = true, userOpName = userOpName) let textLinePos = sourceText.Lines.GetLinePosition position let fcsTextLineNumber = Line.fromZ textLinePos.Line let lineText = (sourceText.Lines.GetLineFromPosition position).ToString() @@ -144,7 +136,7 @@ module internal FSharpQuickInfo = match findSigDeclarationResult with | FindDeclResult.DeclFound declRange when isSignatureFile declRange.FileName -> asyncMaybe { - let! sigQuickInfo = getQuickInfoFromRange(checker, projectInfoManager, document, declRange, cancellationToken) + let! sigQuickInfo = getQuickInfoFromRange(document, declRange, cancellationToken) // if the target was declared in a signature file, and the current file // is not the corresponding module implementation file for that signature, @@ -157,7 +149,7 @@ module internal FSharpQuickInfo = | FindDeclResult.ExternalDecl _ -> return symbolUse.Range, Some sigQuickInfo, None | FindDeclResult.DeclFound declRange -> - let! implQuickInfo = getQuickInfoFromRange(checker, projectInfoManager, document, declRange, cancellationToken) + let! implQuickInfo = getQuickInfoFromRange(document, declRange, cancellationToken) return symbolUse.Range, Some sigQuickInfo, Some { implQuickInfo with Span = targetQuickInfo.Span } } | _ -> async.Return None @@ -171,22 +163,20 @@ type internal FSharpAsyncQuickInfoSource statusBar: StatusBar, xmlMemberIndexService: IVsXMLMemberIndexService, checkerProvider:FSharpCheckerProvider, - projectInfoManager:FSharpProjectOptionsManager, textBuffer:ITextBuffer, _settings: EditorOptions ) = // test helper - static member ProvideQuickInfo(checker:FSharpChecker, document: Document, position:int, parsingOptions:FSharpParsingOptions, options:FSharpProjectOptions, languageServicePerformanceOptions: LanguageServicePerformanceOptions) = + static member ProvideQuickInfo(document: Document, position:int) = asyncMaybe { - let! _, _, checkFileResults = checker.ParseAndCheckDocument(document, options, languageServicePerformanceOptions, userOpName=FSharpQuickInfo.userOpName) let! sourceText = document.GetTextAsync() - let filePath = document.FilePath let textLine = sourceText.Lines.GetLineFromPosition position let textLineNumber = textLine.LineNumber + 1 // Roslyn line numbers are zero-based let textLineString = textLine.ToString() - let defines = CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions - let! symbol = Tokenizer.getSymbolAtPosition (document.Id, sourceText, position, filePath, defines, SymbolLookupKind.Precise, true, true) + let! symbol = document.TryFindFSharpLexerSymbolAsync(position, SymbolLookupKind.Precise, true, true) + + let! _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync() |> liftAsync let res = checkFileResults.GetToolTip (textLineNumber, symbol.Ident.idRange.EndColumn, textLineString, symbol.FullIsland, FSharpTokenTag.IDENT) match res with | ToolTipText [] @@ -219,7 +209,7 @@ type internal FSharpAsyncQuickInfoSource let triggerPoint = triggerPoint.GetValueOrDefault() asyncMaybe { let document = textBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges() - let! symbolUseRange, sigQuickInfo, targetQuickInfo = FSharpQuickInfo.getQuickInfo(checkerProvider.Checker, projectInfoManager, document, triggerPoint.Position, cancellationToken) + let! symbolUseRange, sigQuickInfo, targetQuickInfo = FSharpQuickInfo.getQuickInfo(document, triggerPoint.Position, cancellationToken) let getTrackingSpan (span:TextSpan) = textBuffer.CurrentSnapshot.CreateTrackingSpan(span.Start, span.Length, SpanTrackingMode.EdgeInclusive) @@ -230,7 +220,7 @@ type internal FSharpAsyncQuickInfoSource | None, Some quickInfo -> let mainDescription, docs = FSharpAsyncQuickInfoSource.BuildSingleQuickInfoItem documentationBuilder quickInfo let imageId = Tokenizer.GetImageIdForSymbol(quickInfo.Symbol, quickInfo.SymbolKind) - let navigation = QuickInfoNavigation(statusBar, checkerProvider, projectInfoManager, document, symbolUseRange) + let navigation = QuickInfoNavigation(statusBar, checkerProvider, document, symbolUseRange) let content = QuickInfoViewProvider.provideContent(imageId, mainDescription, docs, navigation) let span = getTrackingSpan quickInfo.Span return QuickInfoItem(span, content) @@ -260,7 +250,7 @@ type internal FSharpAsyncQuickInfoSource ] |> ResizeArray let docs = RoslynHelpers.joinWithLineBreaks [documentation; typeParameterMap; usage; exceptions] let imageId = Tokenizer.GetImageIdForSymbol(targetQuickInfo.Symbol, targetQuickInfo.SymbolKind) - let navigation = QuickInfoNavigation(statusBar, checkerProvider, projectInfoManager, document, symbolUseRange) + let navigation = QuickInfoNavigation(statusBar, checkerProvider, document, symbolUseRange) let content = QuickInfoViewProvider.provideContent(imageId, mainDescription, docs, navigation) let span = getTrackingSpan targetQuickInfo.Span return QuickInfoItem(span, content) @@ -276,7 +266,6 @@ type internal FSharpAsyncQuickInfoSourceProvider ( [)>] serviceProvider: IServiceProvider, checkerProvider:FSharpCheckerProvider, - projectInfoManager:FSharpProjectOptionsManager, settings: EditorOptions ) = @@ -286,4 +275,4 @@ type internal FSharpAsyncQuickInfoSourceProvider // It is safe to do it here (see #4713) let statusBar = StatusBar(serviceProvider.GetService()) let xmlMemberIndexService = serviceProvider.XMLMemberIndexService - new FSharpAsyncQuickInfoSource(statusBar, xmlMemberIndexService, checkerProvider, projectInfoManager, textBuffer, settings) :> IAsyncQuickInfoSource + new FSharpAsyncQuickInfoSource(statusBar, xmlMemberIndexService, checkerProvider, textBuffer, settings) :> IAsyncQuickInfoSource diff --git a/vsintegration/src/FSharp.Editor/Refactor/AddExplicitTypeToParameter.fs b/vsintegration/src/FSharp.Editor/Refactor/AddExplicitTypeToParameter.fs index f2c73274dd4..fb9ba68ff5b 100644 --- a/vsintegration/src/FSharp.Editor/Refactor/AddExplicitTypeToParameter.fs +++ b/vsintegration/src/FSharp.Editor/Refactor/AddExplicitTypeToParameter.fs @@ -20,26 +20,20 @@ open Microsoft.CodeAnalysis.CodeActions type internal FSharpAddExplicitTypeToParameterRefactoring [] ( - checkerProvider: FSharpCheckerProvider, - projectInfoManager: FSharpProjectOptionsManager ) = inherit CodeRefactoringProvider() - static let userOpName = "AddExplicitTypeToParameter" - override _.ComputeRefactoringsAsync context = asyncMaybe { let document = context.Document let position = context.Span.Start - let checker = checkerProvider.Checker - let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, CancellationToken.None, userOpName) let! sourceText = document.GetTextAsync () |> liftTaskAsync - let defines = CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions let textLine = sourceText.Lines.GetLineFromPosition position let textLinePos = sourceText.Lines.GetLinePosition position let fcsTextLineNumber = Line.fromZ textLinePos.Line - let! parseFileResults, _, checkFileResults = checker.ParseAndCheckDocument (document, projectOptions, userOpName=userOpName) - let! lexerSymbol = Tokenizer.getSymbolAtPosition (document.Id, sourceText, position, document.FilePath, defines, SymbolLookupKind.Greedy, false, false) + let! lexerSymbol = document.TryFindFSharpLexerSymbolAsync(position, SymbolLookupKind.Greedy, false, false) + + let! parseFileResults, checkFileResults = document.GetFSharpParseAndCheckResultsAsync() |> liftAsync let! symbolUse = checkFileResults.GetSymbolUseAtLocation(fcsTextLineNumber, lexerSymbol.Ident.idRange.EndColumn, textLine.ToString(), lexerSymbol.FullIsland) let isValidParameterWithoutTypeAnnotation (funcOrValue: FSharpMemberOrFunctionOrValue) (symbolUse: FSharpSymbolUse) = diff --git a/vsintegration/src/FSharp.Editor/Refactor/ChangeDerefToValueRefactoring.fs b/vsintegration/src/FSharp.Editor/Refactor/ChangeDerefToValueRefactoring.fs index 2b51b4c3444..7c94bd8e8b2 100644 --- a/vsintegration/src/FSharp.Editor/Refactor/ChangeDerefToValueRefactoring.fs +++ b/vsintegration/src/FSharp.Editor/Refactor/ChangeDerefToValueRefactoring.fs @@ -20,19 +20,14 @@ open Microsoft.CodeAnalysis.CodeActions type internal FSharpChangeDerefToValueRefactoring [] ( - checkerProvider: FSharpCheckerProvider, - projectInfoManager: FSharpProjectOptionsManager ) = inherit CodeRefactoringProvider() - static let userOpName = "ChangeDerefToValue" - override _.ComputeRefactoringsAsync context = asyncMaybe { let document = context.Document - let! parsingOptions, _ = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, context.CancellationToken, userOpName) let! sourceText = context.Document.GetTextAsync(context.CancellationToken) - let! parseResults = checkerProvider.Checker.ParseDocument(document, parsingOptions, userOpName=userOpName) + let! parseResults = document.GetFSharpParseResultsAsync() |> liftAsync let selectionRange = RoslynHelpers.TextSpanToFSharpRange(document.FilePath, context.Span, sourceText) let! derefRange = parseResults.TryRangeOfRefCellDereferenceContainingPos selectionRange.Start diff --git a/vsintegration/src/FSharp.Editor/Refactor/ChangeTypeofWithNameToNameofExpression.fs b/vsintegration/src/FSharp.Editor/Refactor/ChangeTypeofWithNameToNameofExpression.fs index e1ef1dcf8af..88ff117a543 100644 --- a/vsintegration/src/FSharp.Editor/Refactor/ChangeTypeofWithNameToNameofExpression.fs +++ b/vsintegration/src/FSharp.Editor/Refactor/ChangeTypeofWithNameToNameofExpression.fs @@ -20,19 +20,14 @@ open Microsoft.CodeAnalysis.CodeActions type internal FSharpChangeTypeofWithNameToNameofExpressionRefactoring [] ( - checkerProvider: FSharpCheckerProvider, - projectInfoManager: FSharpProjectOptionsManager ) = inherit CodeRefactoringProvider() - static let userOpName = "ChangeTypeofWithNameToNameofExpression" - override _.ComputeRefactoringsAsync context = asyncMaybe { let document = context.Document - let! parsingOptions, _ = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, context.CancellationToken, userOpName) let! sourceText = context.Document.GetTextAsync(context.CancellationToken) - let! parseResults = checkerProvider.Checker.ParseDocument(document, parsingOptions, userOpName=userOpName) + let! parseResults = document.GetFSharpParseResultsAsync() |> liftAsync let selectionRange = RoslynHelpers.TextSpanToFSharpRange(document.FilePath, context.Span, sourceText) let! namedTypeOfResults = parseResults.TryRangeOfTypeofWithNameAndTypeExpr(selectionRange.Start) diff --git a/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs b/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs index 2a4a7a58da6..c9b3205cc40 100644 --- a/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs +++ b/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs @@ -141,17 +141,14 @@ module internal BlockStructure = open BlockStructure [)>] -type internal FSharpBlockStructureService [] (checkerProvider: FSharpCheckerProvider, projectInfoManager: FSharpProjectOptionsManager) = - - static let userOpName = "FSharpBlockStructure" +type internal FSharpBlockStructureService [] () = interface IFSharpBlockStructureService with member _.GetBlockStructureAsync(document, cancellationToken) : Task = asyncMaybe { - let! parsingOptions, _options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, cancellationToken, userOpName) let! sourceText = document.GetTextAsync(cancellationToken) - let! parseResults = checkerProvider.Checker.ParseDocument(document, parsingOptions, userOpName) + let! parseResults = document.GetFSharpParseResultsAsync() |> liftAsync return createBlockSpans document.FSharpOptions.Advanced.IsBlockStructureEnabled sourceText parseResults.ParseTree |> Seq.toImmutableArray } |> Async.map (Option.defaultValue ImmutableArray<_>.Empty) diff --git a/vsintegration/tests/UnitTests/BreakpointResolutionService.fs b/vsintegration/tests/UnitTests/BreakpointResolutionService.fs index e9437195d8e..09b5e105857 100644 --- a/vsintegration/tests/UnitTests/BreakpointResolutionService.fs +++ b/vsintegration/tests/UnitTests/BreakpointResolutionService.fs @@ -75,9 +75,7 @@ let main argv = let document, sourceText = RoslynTestHelpers.CreateDocument(fileName, code) let searchSpan = TextSpan.FromBounds(searchPosition, searchPosition + searchToken.Length) - // let document = Microsoft.CodeAnalysis.DocumentInfo.Create(Microsoft.CodeAnalysis.DocumentId() - let parsingOptions, _ = checker.GetParsingOptionsFromProjectOptions projectOptions - let actualResolutionOption = FSharpBreakpointResolutionService.GetBreakpointLocation(checker, document, searchSpan, parsingOptions) |> Async.RunSynchronously + let actualResolutionOption = FSharpBreakpointResolutionService.GetBreakpointLocation(document, searchSpan) |> Async.RunSynchronously match actualResolutionOption with | None -> Assert.IsTrue(expectedResolution.IsNone, "BreakpointResolutionService failed to resolve breakpoint position") diff --git a/vsintegration/tests/UnitTests/CompletionProviderTests.fs b/vsintegration/tests/UnitTests/CompletionProviderTests.fs index 8e2c70cfb47..9003031df00 100644 --- a/vsintegration/tests/UnitTests/CompletionProviderTests.fs +++ b/vsintegration/tests/UnitTests/CompletionProviderTests.fs @@ -56,7 +56,7 @@ let VerifyCompletionListWithOptions(fileContents: string, marker: string, expect let caretPosition = fileContents.IndexOf(marker) + marker.Length let document, _ = RoslynTestHelpers.CreateDocument(filePath, fileContents) let results = - FSharpCompletionProvider.ProvideCompletionsAsyncAux(checker, document, caretPosition, projectOptions opts, (fun _ -> []), LanguageServicePerformanceOptions.Default, IntelliSenseOptions.Default) + FSharpCompletionProvider.ProvideCompletionsAsyncAux(document, caretPosition, (fun _ -> []), IntelliSenseOptions.Default) |> Async.RunSynchronously |> Option.defaultValue (ResizeArray()) |> Seq.map(fun result -> result.DisplayText) @@ -104,11 +104,10 @@ let VerifyCompletionList(fileContents, marker, expected, unexpected) = let VerifyCompletionListExactly(fileContents: string, marker: string, expected: string list) = - let projectOptions = { projectOptions [| |] with ProjectId = Some(Guid.NewGuid().ToString()) } let caretPosition = fileContents.IndexOf(marker) + marker.Length let document, _ = RoslynTestHelpers.CreateDocument(filePath, fileContents) let actual = - FSharpCompletionProvider.ProvideCompletionsAsyncAux(checker, document, caretPosition, projectOptions, (fun _ -> []), LanguageServicePerformanceOptions.Default, IntelliSenseOptions.Default) + FSharpCompletionProvider.ProvideCompletionsAsyncAux(document, caretPosition, (fun _ -> []), IntelliSenseOptions.Default) |> Async.RunSynchronously |> Option.defaultValue (ResizeArray()) |> Seq.toList diff --git a/vsintegration/tests/UnitTests/DocumentDiagnosticAnalyzerTests.fs b/vsintegration/tests/UnitTests/DocumentDiagnosticAnalyzerTests.fs index 25e6f9f4060..2f3b76b2965 100644 --- a/vsintegration/tests/UnitTests/DocumentDiagnosticAnalyzerTests.fs +++ b/vsintegration/tests/UnitTests/DocumentDiagnosticAnalyzerTests.fs @@ -40,11 +40,9 @@ type DocumentDiagnosticAnalyzerTests() = let getDiagnostics (fileContents: string) = async { - let projectOptions = { projectOptions with ProjectId = Some(Guid.NewGuid().ToString()) } let document, _ = RoslynTestHelpers.CreateDocument(filePath, fileContents) - let parsingOptions, _ = checker.GetParsingOptionsFromProjectOptions projectOptions - let! syntacticDiagnostics = FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(checker, document, parsingOptions, projectOptions, DiagnosticsType.Syntax) - let! semanticDiagnostics = FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(checker, document, parsingOptions, projectOptions, DiagnosticsType.Semantic) + let! syntacticDiagnostics = FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(document, DiagnosticsType.Syntax) + let! semanticDiagnostics = FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(document, DiagnosticsType.Semantic) return syntacticDiagnostics.AddRange(semanticDiagnostics) } |> Async.RunSynchronously diff --git a/vsintegration/tests/UnitTests/DocumentHighlightsServiceTests.fs b/vsintegration/tests/UnitTests/DocumentHighlightsServiceTests.fs index baf54d9ee50..f7074122dd5 100644 --- a/vsintegration/tests/UnitTests/DocumentHighlightsServiceTests.fs +++ b/vsintegration/tests/UnitTests/DocumentHighlightsServiceTests.fs @@ -52,9 +52,8 @@ let internal projectOptions = { } let private getSpans (sourceText: SourceText) (caretPosition: int) = - let projectOptions = { projectOptions with ProjectId = Some(Guid.NewGuid().ToString()) } let document = RoslynTestHelpers.CreateDocument(filePath, sourceText) - FSharpDocumentHighlightsService.GetDocumentHighlights(checker, document, caretPosition, [], projectOptions, LanguageServicePerformanceOptions.Default) + FSharpDocumentHighlightsService.GetDocumentHighlights(document, caretPosition) |> Async.RunSynchronously |> Option.defaultValue [||] diff --git a/vsintegration/tests/UnitTests/FsxCompletionProviderTests.fs b/vsintegration/tests/UnitTests/FsxCompletionProviderTests.fs index e9c7188d064..0fabf20b3fd 100644 --- a/vsintegration/tests/UnitTests/FsxCompletionProviderTests.fs +++ b/vsintegration/tests/UnitTests/FsxCompletionProviderTests.fs @@ -55,12 +55,11 @@ type Worker () = } member _.VerifyCompletionListExactly(fileContents: string, marker: string, expected: List) = - let projectOptions = { projectOptions with ProjectId = Some(Guid.NewGuid().ToString()) } let caretPosition = fileContents.IndexOf(marker) + marker.Length let document, _ = RoslynTestHelpers.CreateDocument(filePath, fileContents) let expected = expected |> Seq.toList let actual = - let x = FSharpCompletionProvider.ProvideCompletionsAsyncAux(checker, document, caretPosition, projectOptions, (fun _ -> []), LanguageServicePerformanceOptions.Default, IntelliSenseOptions.Default) + let x = FSharpCompletionProvider.ProvideCompletionsAsyncAux(document, caretPosition, (fun _ -> []), IntelliSenseOptions.Default) |> Async.RunSynchronously x |> Option.defaultValue (ResizeArray()) |> Seq.toList diff --git a/vsintegration/tests/UnitTests/GoToDefinitionServiceTests.fs b/vsintegration/tests/UnitTests/GoToDefinitionServiceTests.fs index f77a3e6e9cf..ec79767afaa 100644 --- a/vsintegration/tests/UnitTests/GoToDefinitionServiceTests.fs +++ b/vsintegration/tests/UnitTests/GoToDefinitionServiceTests.fs @@ -41,19 +41,17 @@ module GoToDefinitionServiceTests = let private findDefinition ( - checker: FSharpChecker, document: Document, sourceText: SourceText, position: int, - defines: string list, - options: FSharpProjectOptions + defines: string list ) : range option = maybe { let textLine = sourceText.Lines.GetLineFromPosition position let textLinePos = sourceText.Lines.GetLinePosition position let fcsTextLineNumber = Line.fromZ textLinePos.Line let! lexerSymbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, position, document.FilePath, defines, SymbolLookupKind.Greedy, false, false) - let! _, _, checkFileResults = checker.ParseAndCheckDocument (document, options, LanguageServicePerformanceOptions.Default, userOpName=userOpName) |> Async.RunSynchronously + let _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync() |> Async.RunSynchronously let declarations = checkFileResults.GetDeclarationLocation (fcsTextLineNumber, lexerSymbol.Ident.idRange.EndColumn, textLine.ToString(), lexerSymbol.FullIsland, false) @@ -86,7 +84,7 @@ module GoToDefinitionServiceTests = let caretPosition = fileContents.IndexOf(caretMarker) + caretMarker.Length - 1 // inside the marker let document, sourceText = RoslynTestHelpers.CreateDocument(filePath, fileContents) let actual = - findDefinition(checker, document, sourceText, caretPosition, [], options) + findDefinition(document, sourceText, caretPosition, []) |> Option.map (fun range -> (range.StartLine, range.EndLine, range.StartColumn, range.EndColumn)) if actual <> expected then diff --git a/vsintegration/tests/UnitTests/QuickInfoTests.fs b/vsintegration/tests/UnitTests/QuickInfoTests.fs index 688ca2624ee..b53905f57df 100644 --- a/vsintegration/tests/UnitTests/QuickInfoTests.fs +++ b/vsintegration/tests/UnitTests/QuickInfoTests.fs @@ -16,7 +16,7 @@ let internal GetQuickInfo (project:FSharpProject) (fileName:string) (caretPositi async { let code = File.ReadAllText(fileName) let document, _ = RoslynTestHelpers.CreateDocument(fileName, code) - return! FSharpAsyncQuickInfoSource.ProvideQuickInfo(checker, document, caretPosition, FSharpParsingOptions.Default, project.Options, LanguageServicePerformanceOptions.Default) + return! FSharpAsyncQuickInfoSource.ProvideQuickInfo(document, caretPosition) } |> Async.RunSynchronously let GetQuickInfoText (project:FSharpProject) (fileName:string) (caretPosition:int) = diff --git a/vsintegration/tests/UnitTests/SemanticColorizationServiceTests.fs b/vsintegration/tests/UnitTests/SemanticColorizationServiceTests.fs index 3846e2b6308..9fde535eb71 100644 --- a/vsintegration/tests/UnitTests/SemanticColorizationServiceTests.fs +++ b/vsintegration/tests/UnitTests/SemanticColorizationServiceTests.fs @@ -32,10 +32,9 @@ type SemanticClassificationServiceTests() = let perfOptions = { LanguageServicePerformanceOptions.Default with AllowStaleCompletionResults = false } let getRanges (source: string) : SemanticClassificationItem list = - let projectOptions = { projectOptions with ProjectId = Some(Guid.NewGuid().ToString()) } asyncMaybe { let document, _ = RoslynTestHelpers.CreateDocument(filePath, source) - let! _, _, checkFileResults = checker.ParseAndCheckDocument(document, projectOptions, perfOptions, "") + let! _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync() |> liftAsync return checkFileResults.GetSemanticClassification(None) } |> Async.RunSynchronously diff --git a/vsintegration/tests/UnitTests/SignatureHelpProviderTests.fs b/vsintegration/tests/UnitTests/SignatureHelpProviderTests.fs index df7ff193736..796ea2c4a9f 100644 --- a/vsintegration/tests/UnitTests/SignatureHelpProviderTests.fs +++ b/vsintegration/tests/UnitTests/SignatureHelpProviderTests.fs @@ -45,7 +45,7 @@ let private DefaultDocumentationProvider = override doc.AppendDocumentation(_, _, _, _, _, _, _) = () } -let GetSignatureHelp (project:FSharpProject) (fileName:string) (caretPosition:int) = +let GetSignatureHelp (_project:FSharpProject) (fileName:string) (caretPosition:int) = async { let triggerChar = None let fileContents = File.ReadAllText(fileName) @@ -53,14 +53,11 @@ let GetSignatureHelp (project:FSharpProject) (fileName:string) (caretPosition:in let textLines = sourceText.Lines let caretLinePos = textLines.GetLinePosition(caretPosition) let caretLineColumn = caretLinePos.Character - let perfOptions = LanguageServicePerformanceOptions.Default let document = RoslynTestHelpers.CreateDocument(fileName, sourceText) - let parseResults, _, checkFileResults = - let x = - checker.ParseAndCheckDocument(document, project.Options, perfOptions, "TestSignatureHelpProvider") - |> Async.RunSynchronously - x.Value + let parseResults, checkFileResults = + document.GetFSharpParseAndCheckResultsAsync() + |> Async.RunSynchronously let paramInfoLocations = parseResults.FindParameterLocations(Position.fromZ caretLinePos.Line caretLineColumn).Value let triggered = @@ -106,14 +103,9 @@ let assertSignatureHelpForMethodCalls (fileContents: string) (marker: string) (e let perfOptions = LanguageServicePerformanceOptions.Default let document = RoslynTestHelpers.CreateDocument(filePath, sourceText) - let parseResults, _, checkFileResults = - let x = - checker.ParseAndCheckDocument(document, projectOptions, perfOptions, "TestSignatureHelpProvider") - |> Async.RunSynchronously - - if x.IsNone then - Assert.Fail("Could not parse and check document.") - x.Value + let parseResults, checkFileResults = + document.GetFSharpParseAndCheckResultsAsync() + |> Async.RunSynchronously let actual = let paramInfoLocations = parseResults.FindParameterLocations(Position.fromZ caretLinePos.Line caretLineColumn) @@ -142,16 +134,10 @@ let assertSignatureHelpForMethodCalls (fileContents: string) (marker: string) (e let assertSignatureHelpForFunctionApplication (fileContents: string) (marker: string) expectedArgumentCount expectedArgumentIndex = let caretPosition = fileContents.LastIndexOf(marker) + marker.Length let document, sourceText = RoslynTestHelpers.CreateDocument(filePath, fileContents) - let perfOptions = LanguageServicePerformanceOptions.Default - let parseResults, _, checkFileResults = - let x = - checker.ParseAndCheckDocument(document, projectOptions, perfOptions, "TestSignatureHelpProvider") - |> Async.RunSynchronously - - if x.IsNone then - Assert.Fail("Could not parse and check document.") - x.Value + let parseResults, checkFileResults = + document.GetFSharpParseAndCheckResultsAsync() + |> Async.RunSynchronously let adjustedColumnInSource = let rec loop ch pos = @@ -429,16 +415,10 @@ M.f let caretPosition = fileContents.IndexOf(marker) + marker.Length let document, sourceText = RoslynTestHelpers.CreateDocument(filePath, fileContents) - let perfOptions = LanguageServicePerformanceOptions.Default - let parseResults, _, checkFileResults = - let x = - checker.ParseAndCheckDocument(document, projectOptions, perfOptions, "TestSignatureHelpProvider") - |> Async.RunSynchronously - - if x.IsNone then - Assert.Fail("Could not parse and check document.") - x.Value + let parseResults, checkFileResults = + document.GetFSharpParseAndCheckResultsAsync() + |> Async.RunSynchronously let adjustedColumnInSource = let rec loop ch pos = From ba1a55cd2f4c8407a82ab25a4cd47af6ccb2de4b Mon Sep 17 00:00:00 2001 From: Will Smith Date: Fri, 18 Jun 2021 13:28:55 -0700 Subject: [PATCH 02/27] Added WorkspaceExtensions --- .../LanguageService/WorkspaceExtensions.fs | 198 ++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs diff --git a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs new file mode 100644 index 00000000000..5f5bcda5f7b --- /dev/null +++ b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs @@ -0,0 +1,198 @@ +[] +module internal Microsoft.VisualStudio.FSharp.Editor.WorkspaceExtensions + +open System +open System.Runtime.CompilerServices +open Microsoft.CodeAnalysis +open FSharp.Compiler +open FSharp.Compiler.CodeAnalysis + +[] +module private CheckerExtensions = + + type FSharpChecker with + member checker.ParseDocument(document: Document, parsingOptions: FSharpParsingOptions, userOpName: string) = + async { + let! ct = Async.CancellationToken + let! sourceText = document.GetTextAsync(ct) |> Async.AwaitTask + + return! checker.ParseFile(document.FilePath, sourceText.ToFSharpSourceText(), parsingOptions, userOpName=userOpName) + } + + member checker.CheckDocument(document: Document, parseResults: FSharpParseFileResults, options: FSharpProjectOptions, userOpName: string) = + async { + let! ct = Async.CancellationToken + + let! sourceText = document.GetTextAsync(ct) |> Async.AwaitTask + let! textVersion = document.GetTextVersionAsync(ct) |> Async.AwaitTask + + let filePath = document.FilePath + let textVersionHash = textVersion.GetHashCode() + + return! checker.CheckFileInProject(parseResults, filePath, textVersionHash, sourceText.ToFSharpSourceText(), options,userOpName=userOpName) + } + + member checker.ParseAndCheckDocument(document: Document, options: FSharpProjectOptions, languageServicePerformanceOptions: LanguageServicePerformanceOptions, userOpName: string) = + async { + let! ct = Async.CancellationToken + + let! sourceText = document.GetTextAsync(ct) |> Async.AwaitTask + let! textVersion = document.GetTextVersionAsync(ct) |> Async.AwaitTask + + let filePath = document.FilePath + let textVersionHash = textVersion.GetHashCode() + + let parseAndCheckFile = + async { + let! (parseResults, checkFileAnswer) = checker.ParseAndCheckFileInProject(filePath, textVersionHash, sourceText.ToFSharpSourceText(), options, userOpName=userOpName) + return + match checkFileAnswer with + | FSharpCheckFileAnswer.Aborted -> + None + | FSharpCheckFileAnswer.Succeeded(checkFileResults) -> + Some (parseResults, checkFileResults) + } + + let tryGetFreshResultsWithTimeout() = + async { + let! worker = Async.StartChild(async { try return! parseAndCheckFile with | _ -> return None }, millisecondsTimeout=languageServicePerformanceOptions.TimeUntilStaleCompletion) + try + return! worker + with :? TimeoutException -> + return None // worker is cancelled at this point, we cannot return it and wait its completion anymore + } + + let bindParsedInput(results: (FSharpParseFileResults * FSharpCheckFileResults) option) = + match results with + | Some(parseResults, checkResults) -> + Some (parseResults, parseResults.ParseTree, checkResults) + | None -> None + + if languageServicePerformanceOptions.AllowStaleCompletionResults then + let! freshResults = tryGetFreshResultsWithTimeout() + + let! results = + match freshResults with + | Some x -> async.Return (Some x) + | None -> + async { + match checker.TryGetRecentCheckResultsForFile(filePath, options, userOpName=userOpName) with + | Some (parseResults, checkFileResults, _) -> + return Some (parseResults, checkFileResults) + | None -> + return! parseAndCheckFile + } + return bindParsedInput results + else + let! results = parseAndCheckFile + return bindParsedInput results + } + + member checker.ParseAndCheckDocument(document: Document, options: FSharpProjectOptions, userOpName: string, ?allowStaleResults: bool) = + async { + let perfOpts = + match allowStaleResults with + | Some b -> { document.FSharpOptions.LanguageServicePerformance with AllowStaleCompletionResults = b } + | _ -> document.FSharpOptions.LanguageServicePerformance + return! checker.ParseAndCheckDocument(document, options, perfOpts, userOpName=userOpName) + } + +[] +module private ProjectCache = + + let Projects = ConditionalWeakTable() + +type Solution with + + member this.GetFSharpService() = + this.Workspace.Services.GetRequiredService() + +type Project with + + member this.GetFSharpProjectOptionsAsync() = + async { + if this.Language = LanguageNames.FSharp then + match ProjectCache.Projects.TryGetValue(this) with + | true, result -> return result + | _ -> + let service = this.Solution.GetFSharpService() + let projectOptionsManager = service.FSharpProjectOptionsManager + let! ct = Async.CancellationToken + match! projectOptionsManager.TryGetOptionsByProject(this, ct) with + | None -> return raise(System.OperationCanceledException("FSharp project options not found.")) + | Some(parsingOptions, projectOptions) -> + let result = (service.Checker, projectOptionsManager, parsingOptions, projectOptions) + ProjectCache.Projects.Add(this, result) + return result + else + return raise(System.OperationCanceledException("Project is not a FSharp project.")) + } + + member this.GetFSharpProjectDefinesAsync() = + async { + let! _, _, parsingOptions, _ = this.GetFSharpProjectOptionsAsync() + return CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions + } + +type Document with + + member this.GetFSharpSyntaxDefines() = + if this.Project.Language = LanguageNames.FSharp then + let service = this.Project.Solution.GetFSharpService() + service.FSharpProjectOptionsManager.GetCompilationDefinesForEditingDocument(this) + else + [] + + member this.GetFSharpParseResultsAsync() = + async { + let! checker, _, parsingOptions, _ = this.Project.GetFSharpProjectOptionsAsync() + return! checker.ParseDocument(this, parsingOptions, nameof(this.GetFSharpParseResultsAsync)) + } + + member this.GetFSharpParseAndCheckResultsAsync() = + async { + let! checker, _, _, projectOptions = this.Project.GetFSharpProjectOptionsAsync() + match! checker.ParseAndCheckDocument(this, projectOptions, nameof(this.GetFSharpParseAndCheckResultsAsync), allowStaleResults = false) with + | Some(parseResults, _, checkResults) -> + return (parseResults, checkResults) + | _ -> + return raise(System.OperationCanceledException("Unable to get FSharp parse and check results.")) + } + + member this.GetFSharpSemanticClassificationAsync() = + async { + let! checker, _, _, projectOptions = this.Project.GetFSharpProjectOptionsAsync() + match! checker.GetBackgroundSemanticClassificationForFile(this.FilePath, projectOptions) with + | Some results -> return results + | _ -> return raise(System.OperationCanceledException("Unable to get FSharp semantic classification.")) + } + + member this.FindFSharpReferencesAsync(symbol, onFound) = + async { + let! checker, _, _, projectOptions = this.Project.GetFSharpProjectOptionsAsync() + let! symbolUses = checker.FindBackgroundReferencesInFile(this.FilePath, projectOptions, symbol, canInvalidateProject = false) + let! ct = Async.CancellationToken + let! sourceText = this.GetTextAsync ct |> Async.AwaitTask + for symbolUse in symbolUses do + match RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, symbolUse) with + | Some textSpan -> + do! onFound textSpan symbolUse + | _ -> + () + } + + member this.TryFindFSharpLexerSymbolAsync(position, lookupKind, wholeActivePattern, allowStringToken) = + async { + let! defines = this.Project.GetFSharpProjectDefinesAsync() + let! ct = Async.CancellationToken + let! sourceText = this.GetTextAsync(ct) |> Async.AwaitTask + return Tokenizer.getSymbolAtPosition(this.Id, sourceText, position, this.FilePath, defines, lookupKind, wholeActivePattern, allowStringToken) + } + +type Project with + + member this.FindFSharpReferencesAsync(symbol, onFound) = + async { + for doc in this.Documents do + do! doc.FindFSharpReferencesAsync(symbol, fun textSpan range -> onFound doc textSpan range) + } From 57806746213419b0ba9c0b4ece54bdb048ac9fb1 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Fri, 18 Jun 2021 18:10:54 -0700 Subject: [PATCH 03/27] More cleanup. Trying to fix tests. --- .../CodeLens/CodeLensProvider.fs | 6 +- .../CodeLens/FSharpCodeLensService.fs | 4 +- .../src/FSharp.Editor/Common/Extensions.fs | 2 + .../LanguageService/FSharpCheckerProvider.fs | 49 +++++++------ .../FSharpProjectOptionsManager.fs | 36 +++++----- .../LanguageService/LanguageService.fs | 7 +- .../LanguageService/MetadataAsSource.fs | 4 +- .../LanguageService/WorkspaceExtensions.fs | 4 +- .../Navigation/GoToDefinition.fs | 5 +- .../Navigation/GoToDefinitionService.fs | 4 +- .../Navigation/NavigableSymbolsService.fs | 8 +-- .../src/FSharp.Editor/QuickInfo/Navigation.fs | 4 +- .../QuickInfo/QuickInfoProvider.fs | 10 +-- .../UnitTests/GoToDefinitionServiceTests.fs | 1 - .../tests/UnitTests/Tests.RoslynHelpers.fs | 70 +++++++++++++++---- .../UnitTests/VisualFSharp.UnitTests.fsproj | 2 + 16 files changed, 133 insertions(+), 83 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/CodeLens/CodeLensProvider.fs b/vsintegration/src/FSharp.Editor/CodeLens/CodeLensProvider.fs index 5a4f5fda4d3..8d3be2fcf35 100644 --- a/vsintegration/src/FSharp.Editor/CodeLens/CodeLensProvider.fs +++ b/vsintegration/src/FSharp.Editor/CodeLens/CodeLensProvider.fs @@ -23,7 +23,7 @@ type internal CodeLensProvider ( [)>] serviceProvider: IServiceProvider, textDocumentFactory: ITextDocumentFactoryService, - checkerProvider: FSharpCheckerProvider, + metadataAsSource: FSharpMetadataAsSourceService, typeMap : FSharpClassificationTypeMap Lazy, settings: EditorOptions ) = @@ -49,7 +49,7 @@ type internal CodeLensProvider ) let tagger = CodeLensGeneralTagger(wpfView, buffer) - let service = FSharpCodeLensService(serviceProvider, workspace, documentId, buffer, checkerProvider, componentModel.GetService(), typeMap, tagger, settings) + let service = FSharpCodeLensService(serviceProvider, workspace, documentId, buffer, metadataAsSource, componentModel.GetService(), typeMap, tagger, settings) let provider = (wpfView, (tagger, service)) wpfView.Closed.Add (fun _ -> taggers.Remove provider |> ignore) taggers.Add((wpfView, (tagger, service))) @@ -68,7 +68,7 @@ type internal CodeLensProvider | _ -> None |> Option.get ) - let service = FSharpCodeLensService(serviceProvider, workspace, documentId, buffer, checkerProvider, componentModel.GetService(), typeMap, LineLensDisplayService(wpfView, buffer), settings) + let service = FSharpCodeLensService(serviceProvider, workspace, documentId, buffer, metadataAsSource, componentModel.GetService(), typeMap, LineLensDisplayService(wpfView, buffer), settings) let provider = (wpfView, service) wpfView.Closed.Add (fun _ -> lineLensProvider.Remove provider |> ignore) lineLensProvider.Add(provider) diff --git a/vsintegration/src/FSharp.Editor/CodeLens/FSharpCodeLensService.fs b/vsintegration/src/FSharp.Editor/CodeLens/FSharpCodeLensService.fs index 3498828559b..85f93c233e2 100644 --- a/vsintegration/src/FSharp.Editor/CodeLens/FSharpCodeLensService.fs +++ b/vsintegration/src/FSharp.Editor/CodeLens/FSharpCodeLensService.fs @@ -44,7 +44,7 @@ type internal FSharpCodeLensService workspace: Workspace, documentId: Lazy, buffer: ITextBuffer, - checkerProvider: FSharpCheckerProvider, + metadataAsSource: FSharpMetadataAsSourceService, classificationFormatMapService: IClassificationFormatMapService, typeMap: Lazy, codeLens : CodeLensDisplayService, @@ -191,7 +191,7 @@ type internal FSharpCodeLensService let taggedText = ResizeArray() typeLayout |> Seq.iter taggedText.Add let statusBar = StatusBar(serviceProvider.GetService()) - let navigation = QuickInfoNavigation(statusBar, checkerProvider, document, realPosition) + let navigation = QuickInfoNavigation(statusBar, metadataAsSource, document, realPosition) // Because the data is available notify that this line should be updated, displaying the results return Some (taggedText, navigation) | None -> diff --git a/vsintegration/src/FSharp.Editor/Common/Extensions.fs b/vsintegration/src/FSharp.Editor/Common/Extensions.fs index 825a010f387..69f0b63ec0e 100644 --- a/vsintegration/src/FSharp.Editor/Common/Extensions.fs +++ b/vsintegration/src/FSharp.Editor/Common/Extensions.fs @@ -42,6 +42,8 @@ type Project with member this.IsFSharpMiscellaneous = this.Name = FSharpConstants.FSharpMiscellaneousFilesName member this.IsFSharpMetadata = this.Name.StartsWith(FSharpConstants.FSharpMetadataName) member this.IsFSharpMiscellaneousOrMetadata = this.IsFSharpMiscellaneous || this.IsFSharpMetadata + member this.IsFSharpProject = + this.Name.EndsWith(".fsproj", StringComparison.OrdinalIgnoreCase) type Document with member this.TryGetLanguageService<'T when 'T :> ILanguageService>() = diff --git a/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerProvider.fs b/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerProvider.fs index ae2f2ca3bf4..67aecff6e26 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerProvider.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerProvider.fs @@ -18,39 +18,40 @@ open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Diagnostics #nowarn "9" // NativePtr.toNativeInt // Exposes FSharpChecker as MEF export -[); Composition.Shared>] +[); Composition.Shared;Composition.Export(typeof)>] type internal FSharpCheckerProvider - [] + [] ( - [)>] workspace: VisualStudioWorkspace, - projectContextFactory: IWorkspaceProjectContextFactory, + [)>] workspace: Workspace, settings: EditorOptions ) = - let metadataAsSource = FSharpMetadataAsSourceService(projectContextFactory) - let tryGetMetadataSnapshot (path, timeStamp) = - try - let md = Microsoft.CodeAnalysis.ExternalAccess.FSharp.LanguageServices.FSharpVisualStudioWorkspaceExtensions.GetMetadata(workspace, path, timeStamp) - let amd = (md :?> AssemblyMetadata) - let mmd = amd.GetModules().[0] - let mmr = mmd.GetMetadataReader() + match workspace with + | :? VisualStudioWorkspace as workspace -> + try + let md = Microsoft.CodeAnalysis.ExternalAccess.FSharp.LanguageServices.FSharpVisualStudioWorkspaceExtensions.GetMetadata(workspace, path, timeStamp) + let amd = (md :?> AssemblyMetadata) + let mmd = amd.GetModules().[0] + let mmr = mmd.GetMetadataReader() - // "lifetime is timed to Metadata you got from the GetMetadata(...). As long as you hold it strongly, raw - // memory we got from metadata reader will be alive. Once you are done, just let everything go and - // let finalizer handle resource rather than calling Dispose from Metadata directly. It is shared metadata. - // You shouldn't dispose it directly." + // "lifetime is timed to Metadata you got from the GetMetadata(...). As long as you hold it strongly, raw + // memory we got from metadata reader will be alive. Once you are done, just let everything go and + // let finalizer handle resource rather than calling Dispose from Metadata directly. It is shared metadata. + // You shouldn't dispose it directly." - let objToHold = box md + let objToHold = box md - // We don't expect any ilread WeakByteFile to be created when working in Visual Studio - // Debug.Assert((FSharp.Compiler.AbstractIL.ILBinaryReader.GetStatistics().weakByteFileCount = 0), "Expected weakByteFileCount to be zero when using F# in Visual Studio. Was there a problem reading a .NET binary?") + // We don't expect any ilread WeakByteFile to be created when working in Visual Studio + // Debug.Assert((FSharp.Compiler.AbstractIL.ILBinaryReader.GetStatistics().weakByteFileCount = 0), "Expected weakByteFileCount to be zero when using F# in Visual Studio. Was there a problem reading a .NET binary?") - Some (objToHold, NativePtr.toNativeInt mmr.MetadataPointer, mmr.MetadataLength) - with ex -> - // We catch all and let the backup routines in the F# compiler find the error - Assert.Exception(ex) - None + Some (objToHold, NativePtr.toNativeInt mmr.MetadataPointer, mmr.MetadataLength) + with ex -> + // We catch all and let the backup routines in the F# compiler find the error + Assert.Exception(ex) + None + | _ -> + None let checker = lazy @@ -69,5 +70,3 @@ type internal FSharpCheckerProvider member this.Checker = checker.Value - member _.MetadataAsSource = metadataAsSource - diff --git a/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs b/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs index aa5d35b0815..a1402780e84 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs @@ -75,7 +75,7 @@ module private FSharpProjectOptionsHelpers = let p2 = newProject.Solution.GetProject(p2.ProjectId) doesProjectIdDiffer || ( - if p1.Language = LanguageNames.FSharp then + if p1.IsFSharpProject then p1.Version <> p2.Version else let v1 = p1.GetDependentVersionAsync(ct).Result @@ -462,7 +462,7 @@ type internal FSharpProjectOptionsManager [] ( checkerProvider: FSharpCheckerProvider, - [)>] workspace: VisualStudioWorkspace, + [)>] workspace: Workspace, [)>] serviceProvider: System.IServiceProvider, settings: EditorOptions ) = @@ -484,7 +484,7 @@ type internal FSharpProjectOptionsManager workspace.DocumentClosed.Add(fun args -> let doc = args.Document let proj = doc.Project - if proj.Language = LanguageNames.FSharp && proj.IsFSharpMiscellaneousOrMetadata then + if proj.IsFSharpProject && proj.IsFSharpMiscellaneousOrMetadata then reactor.ClearSingleFileOptionsCache(doc.Id) ) @@ -545,22 +545,24 @@ type internal FSharpProjectOptionsManager member _.HandleCommandLineChanges(path:string, sources:ImmutableArray, _references:ImmutableArray, options:ImmutableArray) = use _logBlock = Logger.LogBlock(LogEditorFunctionId.LanguageService_HandleCommandLineArgs) - let projectId = - match Microsoft.CodeAnalysis.ExternalAccess.FSharp.LanguageServices.FSharpVisualStudioWorkspaceExtensions.TryGetProjectIdByBinPath(workspace, path) with - | true, projectId -> projectId - | false, _ -> Microsoft.CodeAnalysis.ExternalAccess.FSharp.LanguageServices.FSharpVisualStudioWorkspaceExtensions.GetOrCreateProjectIdForPath(workspace, path, projectDisplayNameOf path) - let path = Microsoft.CodeAnalysis.ExternalAccess.FSharp.LanguageServices.FSharpVisualStudioWorkspaceExtensions.GetProjectFilePath(workspace, projectId) + match workspace with + | :? VisualStudioWorkspace as workspace -> + let projectId = + match Microsoft.CodeAnalysis.ExternalAccess.FSharp.LanguageServices.FSharpVisualStudioWorkspaceExtensions.TryGetProjectIdByBinPath(workspace, path) with + | true, projectId -> projectId + | false, _ -> Microsoft.CodeAnalysis.ExternalAccess.FSharp.LanguageServices.FSharpVisualStudioWorkspaceExtensions.GetOrCreateProjectIdForPath(workspace, path, projectDisplayNameOf path) + let path = Microsoft.CodeAnalysis.ExternalAccess.FSharp.LanguageServices.FSharpVisualStudioWorkspaceExtensions.GetProjectFilePath(workspace, projectId) - let getFullPath p = - let p' = - if Path.IsPathRooted(p) || path = null then p - else Path.Combine(Path.GetDirectoryName(path), p) - Path.GetFullPathSafe(p') + let getFullPath p = + let p' = + if Path.IsPathRooted(p) || path = null then p + else Path.Combine(Path.GetDirectoryName(path), p) + Path.GetFullPathSafe(p') - let sourcePaths = sources |> Seq.map(fun s -> getFullPath s.Path) |> Seq.toArray + let sourcePaths = sources |> Seq.map(fun s -> getFullPath s.Path) |> Seq.toArray - reactor.SetCpsCommandLineOptions(projectId, sourcePaths, options.ToArray()) + reactor.SetCpsCommandLineOptions(projectId, sourcePaths, options.ToArray()) + | _ -> + () member _.Checker = checkerProvider.Checker - - member _.MetadataAsSource = checkerProvider.MetadataAsSource diff --git a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs index 5b981fb3903..17d3d9a7971 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs @@ -57,13 +57,13 @@ type internal FSharpCheckerWorkspaceServiceFactory member _.FSharpProjectOptionsManager = projectInfoManager } [] -type private FSharpSolutionEvents(projectManager: FSharpProjectOptionsManager) = +type private FSharpSolutionEvents(projectManager: FSharpProjectOptionsManager, metadataAsSource: FSharpMetadataAsSourceService) = interface IVsSolutionEvents with member _.OnAfterCloseSolution(_) = projectManager.Checker.ClearLanguageServiceRootCachesAndCollectAndFinalizeAllTransients() - projectManager.MetadataAsSource.ClearGeneratedFiles() + metadataAsSource.ClearGeneratedFiles() projectManager.ClearAllCaches() VSConstants.S_OK @@ -177,9 +177,10 @@ type internal FSharpPackage() as this = // FSI-LINKAGE-POINT: private method GetDialogPage forces fsi options to be loaded let _fsiPropertyPage = this.GetDialogPage(typeof) let projectInfoManager = this.ComponentModel.DefaultExportProvider.GetExport().Value + let metadataAsSource = this.ComponentModel.DefaultExportProvider.GetExport().Value let solution = this.GetServiceAsync(typeof).Result let solution = solution :?> IVsSolution - let solutionEvents = FSharpSolutionEvents(projectInfoManager) + let solutionEvents = FSharpSolutionEvents(projectInfoManager, metadataAsSource) let rdt = this.GetServiceAsync(typeof).Result let rdt = rdt :?> IVsRunningDocumentTable diff --git a/vsintegration/src/FSharp.Editor/LanguageService/MetadataAsSource.fs b/vsintegration/src/FSharp.Editor/LanguageService/MetadataAsSource.fs index 82d39f38c25..1121c0c8609 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/MetadataAsSource.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/MetadataAsSource.fs @@ -11,6 +11,7 @@ open System.Linq open System.Text open System.Runtime.InteropServices open System.Reflection.PortableExecutable +open System.ComponentModel.Composition open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.FindSymbols @@ -91,7 +92,8 @@ module internal MetadataAsSource = None [] -type internal FSharpMetadataAsSourceService(projectContextFactory: IWorkspaceProjectContextFactory) = +[); Composition.Shared>] +type internal FSharpMetadataAsSourceService [] (projectContextFactory: IWorkspaceProjectContextFactory) = let serviceProvider = ServiceProvider.GlobalProvider let projs = System.Collections.Concurrent.ConcurrentDictionary() diff --git a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs index 5f5bcda5f7b..5db9c8978ba 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs @@ -111,7 +111,7 @@ type Project with member this.GetFSharpProjectOptionsAsync() = async { - if this.Language = LanguageNames.FSharp then + if this.IsFSharpProject then match ProjectCache.Projects.TryGetValue(this) with | true, result -> return result | _ -> @@ -137,7 +137,7 @@ type Project with type Document with member this.GetFSharpSyntaxDefines() = - if this.Project.Language = LanguageNames.FSharp then + if this.Project.IsFSharpProject then let service = this.Project.Solution.GetFSharpService() service.FSharpProjectOptionsManager.GetCompilationDefinesForEditingDocument(this) else diff --git a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs index eaa8aa490e5..946977049d5 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs @@ -151,8 +151,7 @@ type internal FSharpGoToDefinitionResult = | NavigableItem of FSharpNavigableItem | ExternalAssembly of FSharpSymbolUse * MetadataReference seq -type internal GoToDefinition(checkerProvider: FSharpCheckerProvider) = - let metadataAsSourceService = checkerProvider.MetadataAsSource +type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = /// Use an origin document to provide the solution & workspace used to /// find the corresponding textSpan and INavigableItem for the range @@ -409,7 +408,7 @@ type internal GoToDefinition(checkerProvider: FSharpCheckerProvider) = AssemblyIdentity(targetSymbolUse.Symbol.Assembly.QualifiedName), fileName, metadataReferences) - let tmpShownDocOpt = metadataAsSourceService.ShowDocument(tmpProjInfo, tmpDocInfo.FilePath, SourceText.From(text.ToString())) + let tmpShownDocOpt = metadataAsSource.ShowDocument(tmpProjInfo, tmpDocInfo.FilePath, SourceText.From(text.ToString())) match tmpShownDocOpt with | Some tmpShownDoc -> let goToAsync = diff --git a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinitionService.fs b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinitionService.fs index a61c6b5627f..6ed309bf573 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinitionService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinitionService.fs @@ -18,10 +18,10 @@ open Microsoft.VisualStudio.Shell.Interop type internal FSharpGoToDefinitionService [] ( - checkerProvider: FSharpCheckerProvider + metadataAsSource: FSharpMetadataAsSourceService ) = - let gtd = GoToDefinition(checkerProvider) + let gtd = GoToDefinition(metadataAsSource) let statusBar = StatusBar(ServiceProvider.GlobalProvider.GetService()) interface IFSharpGoToDefinitionService with diff --git a/vsintegration/src/FSharp.Editor/Navigation/NavigableSymbolsService.fs b/vsintegration/src/FSharp.Editor/Navigation/NavigableSymbolsService.fs index 78cfeaa3a40..d1af75a598a 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/NavigableSymbolsService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/NavigableSymbolsService.fs @@ -28,10 +28,10 @@ type internal FSharpNavigableSymbol(item: FSharpNavigableItem, span: SnapshotSpa member _.SymbolSpan = span -type internal FSharpNavigableSymbolSource(checkerProvider: FSharpCheckerProvider, serviceProvider: IServiceProvider) = +type internal FSharpNavigableSymbolSource(metadataAsSource, serviceProvider: IServiceProvider) = let mutable disposed = false - let gtd = GoToDefinition(checkerProvider) + let gtd = GoToDefinition(metadataAsSource) let statusBar = StatusBar(serviceProvider.GetService()) interface INavigableSymbolSource with @@ -107,9 +107,9 @@ type internal FSharpNavigableSymbolService [] ( [)>] serviceProvider: IServiceProvider, - checkerProvider: FSharpCheckerProvider + metadataAsSource: FSharpMetadataAsSourceService ) = interface INavigableSymbolSourceProvider with member _.TryCreateNavigableSymbolSource(_: ITextView, _: ITextBuffer) = - new FSharpNavigableSymbolSource(checkerProvider, serviceProvider) :> INavigableSymbolSource \ No newline at end of file + new FSharpNavigableSymbolSource(metadataAsSource, serviceProvider) :> INavigableSymbolSource \ No newline at end of file diff --git a/vsintegration/src/FSharp.Editor/QuickInfo/Navigation.fs b/vsintegration/src/FSharp.Editor/QuickInfo/Navigation.fs index 2f8d1e7198c..0ea6a4a3a06 100644 --- a/vsintegration/src/FSharp.Editor/QuickInfo/Navigation.fs +++ b/vsintegration/src/FSharp.Editor/QuickInfo/Navigation.fs @@ -14,7 +14,7 @@ open Microsoft.VisualStudio.Shell.Interop type internal QuickInfoNavigation ( statusBar: StatusBar, - checkerProvider: FSharpCheckerProvider, + metadataAsSource: FSharpMetadataAsSourceService, initialDoc: Document, thisSymbolUseRange: range ) = @@ -42,7 +42,7 @@ type internal QuickInfoNavigation let! targetDoc = solution.TryGetDocumentFromFSharpRange (range, initialDoc.Project.Id) let! targetSource = targetDoc.GetTextAsync() let! targetTextSpan = RoslynHelpers.TryFSharpRangeToTextSpan (targetSource, range) - let gtd = GoToDefinition(checkerProvider) + let gtd = GoToDefinition(metadataAsSource) // To ensure proper navigation decsions, we need to check the type of document the navigation call // is originating from and the target we're provided by default: diff --git a/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs b/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs index f027d310692..be00fb8e8c7 100644 --- a/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs +++ b/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs @@ -162,7 +162,7 @@ type internal FSharpAsyncQuickInfoSource ( statusBar: StatusBar, xmlMemberIndexService: IVsXMLMemberIndexService, - checkerProvider:FSharpCheckerProvider, + metadataAsSource: FSharpMetadataAsSourceService, textBuffer:ITextBuffer, _settings: EditorOptions ) = @@ -220,7 +220,7 @@ type internal FSharpAsyncQuickInfoSource | None, Some quickInfo -> let mainDescription, docs = FSharpAsyncQuickInfoSource.BuildSingleQuickInfoItem documentationBuilder quickInfo let imageId = Tokenizer.GetImageIdForSymbol(quickInfo.Symbol, quickInfo.SymbolKind) - let navigation = QuickInfoNavigation(statusBar, checkerProvider, document, symbolUseRange) + let navigation = QuickInfoNavigation(statusBar, metadataAsSource, document, symbolUseRange) let content = QuickInfoViewProvider.provideContent(imageId, mainDescription, docs, navigation) let span = getTrackingSpan quickInfo.Span return QuickInfoItem(span, content) @@ -250,7 +250,7 @@ type internal FSharpAsyncQuickInfoSource ] |> ResizeArray let docs = RoslynHelpers.joinWithLineBreaks [documentation; typeParameterMap; usage; exceptions] let imageId = Tokenizer.GetImageIdForSymbol(targetQuickInfo.Symbol, targetQuickInfo.SymbolKind) - let navigation = QuickInfoNavigation(statusBar, checkerProvider, document, symbolUseRange) + let navigation = QuickInfoNavigation(statusBar, metadataAsSource, document, symbolUseRange) let content = QuickInfoViewProvider.provideContent(imageId, mainDescription, docs, navigation) let span = getTrackingSpan targetQuickInfo.Span return QuickInfoItem(span, content) @@ -265,7 +265,7 @@ type internal FSharpAsyncQuickInfoSourceProvider [] ( [)>] serviceProvider: IServiceProvider, - checkerProvider:FSharpCheckerProvider, + metadataAsSource: FSharpMetadataAsSourceService, settings: EditorOptions ) = @@ -275,4 +275,4 @@ type internal FSharpAsyncQuickInfoSourceProvider // It is safe to do it here (see #4713) let statusBar = StatusBar(serviceProvider.GetService()) let xmlMemberIndexService = serviceProvider.XMLMemberIndexService - new FSharpAsyncQuickInfoSource(statusBar, xmlMemberIndexService, checkerProvider, textBuffer, settings) :> IAsyncQuickInfoSource + new FSharpAsyncQuickInfoSource(statusBar, xmlMemberIndexService, metadataAsSource, textBuffer, settings) :> IAsyncQuickInfoSource diff --git a/vsintegration/tests/UnitTests/GoToDefinitionServiceTests.fs b/vsintegration/tests/UnitTests/GoToDefinitionServiceTests.fs index ec79767afaa..52a62dd75cf 100644 --- a/vsintegration/tests/UnitTests/GoToDefinitionServiceTests.fs +++ b/vsintegration/tests/UnitTests/GoToDefinitionServiceTests.fs @@ -78,7 +78,6 @@ module GoToDefinitionServiceTests = let GoToDefinitionTest (fileContents: string, caretMarker: string, expected) = let filePath = Path.GetTempFileName() + ".fs" - let options = makeOptions filePath [| |] File.WriteAllText(filePath, fileContents) let caretPosition = fileContents.IndexOf(caretMarker) + caretMarker.Length - 1 // inside the marker diff --git a/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs b/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs index 6ccdbacc715..9c8e0a39008 100644 --- a/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs +++ b/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs @@ -2,28 +2,59 @@ open System open System.IO +open System.Reflection +open Microsoft.VisualStudio.Composition open Microsoft.CodeAnalysis +open Microsoft.CodeAnalysis.Host open Microsoft.CodeAnalysis.Text +open Microsoft.VisualStudio.FSharp.Editor + +type TestHostWorkspaceServices(hostServices: HostServices, workspace: Workspace) = + inherit HostWorkspaceServices() + + let resolver = Resolver.DefaultInstance + let catalog = + let asms = AppDomain.CurrentDomain.GetAssemblies() + let partDiscovery = PartDiscovery.Combine(new AttributedPartDiscoveryV1(resolver), new AttributedPartDiscovery(resolver, isNonPublicSupported = true)); + let parts = partDiscovery.CreatePartsAsync(asms).Result + let catalog = ComposableCatalog.Create(resolver) + catalog.AddParts(parts) + + let configuration = CompositionConfiguration.Create(catalog.WithCompositionService()) + let runtimeComposition = RuntimeComposition.CreateRuntimeComposition(configuration) + let exportProviderFactory = runtimeComposition.CreateExportProviderFactory() + let exportProvider = exportProviderFactory.CreateExportProvider().AsExportProvider() + + override _.Workspace = workspace + + override _.GetService<'T when 'T :> IWorkspaceService>() = + exportProvider.GetExport<'T>().Value + + override _.FindLanguageServices(filter) = Seq.empty + + override _.HostServices = hostServices + +type TestHostServices() = + inherit HostServices() + + static let instance = TestHostServices() + static member Instance = instance + + override this.CreateWorkspaceServices(workspace) = + TestHostWorkspaceServices(this, workspace) :> HostWorkspaceServices [] type RoslynTestHelpers private () = static member CreateDocument (filePath, text: SourceText) = let isScript = String.Equals(Path.GetExtension(filePath), ".fsx", StringComparison.OrdinalIgnoreCase) - let workspace = new AdhocWorkspace() - let projInfo = - let projId = ProjectId.CreateNewId() - ProjectInfo.Create( - projId, - VersionStamp.Create(DateTime.UtcNow), - "test.fsproj", - "test.dll", - LanguageNames.CSharp - ) - let proj = workspace.AddProject(projInfo) + let asms = AppDomain.CurrentDomain.GetAssemblies() + let workspace = new AdhocWorkspace(Host.Mef.MefHostServices.Create(asms)) + + let projId = ProjectId.CreateNewId() + let docId = DocumentId.CreateNewId(projId) let docInfo = - let docId = DocumentId.CreateNewId(proj.Id) DocumentInfo.Create( docId, filePath, @@ -31,7 +62,20 @@ type RoslynTestHelpers private () = filePath=filePath, sourceCodeKind= if isScript then SourceCodeKind.Script else SourceCodeKind.Regular) - workspace.AddDocument(docInfo) + let projInfo = + ProjectInfo.Create( + projId, + VersionStamp.Create(DateTime.UtcNow), + "test.fsproj", + "test.dll", + LanguageNames.CSharp, // We cannot use LanguageNames.FSharp as Roslyn doesn't support creating adhoc projects with F# language name. + documents = [docInfo] + ) + + let solutionInfo = SolutionInfo.Create(SolutionId.CreateNewId(), VersionStamp.Create(DateTime.UtcNow), "test.sln", [projInfo]) + + let solution = workspace.AddSolution(solutionInfo) + solution.GetProject(projId).GetDocument(docId) static member CreateDocument (filePath, code: string) = let text = SourceText.From(code) diff --git a/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj b/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj index b0ce865f554..baa2c21ae98 100644 --- a/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj +++ b/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj @@ -196,6 +196,8 @@ + + From db794353c488bd0aed8ba6a5f7697019fa27f5e6 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Mon, 21 Jun 2021 11:00:09 -0700 Subject: [PATCH 04/27] Fixing IsFSharp --- vsintegration/src/FSharp.Editor/Common/Extensions.fs | 4 ++-- .../FSharp.Editor/LanguageService/FSharpCheckerProvider.fs | 4 ++-- .../LanguageService/FSharpProjectOptionsManager.fs | 4 ++-- .../src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/Common/Extensions.fs b/vsintegration/src/FSharp.Editor/Common/Extensions.fs index 69f0b63ec0e..4b9ed235b0e 100644 --- a/vsintegration/src/FSharp.Editor/Common/Extensions.fs +++ b/vsintegration/src/FSharp.Editor/Common/Extensions.fs @@ -42,8 +42,8 @@ type Project with member this.IsFSharpMiscellaneous = this.Name = FSharpConstants.FSharpMiscellaneousFilesName member this.IsFSharpMetadata = this.Name.StartsWith(FSharpConstants.FSharpMetadataName) member this.IsFSharpMiscellaneousOrMetadata = this.IsFSharpMiscellaneous || this.IsFSharpMetadata - member this.IsFSharpProject = - this.Name.EndsWith(".fsproj", StringComparison.OrdinalIgnoreCase) + member this.IsFSharp = + this.FilePath.EndsWith(".fsproj", StringComparison.OrdinalIgnoreCase) type Document with member this.TryGetLanguageService<'T when 'T :> ILanguageService>() = diff --git a/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerProvider.fs b/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerProvider.fs index 67aecff6e26..6e0571a052c 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerProvider.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerProvider.fs @@ -18,9 +18,9 @@ open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Diagnostics #nowarn "9" // NativePtr.toNativeInt // Exposes FSharpChecker as MEF export -[); Composition.Shared;Composition.Export(typeof)>] +[); Composition.Shared>] type internal FSharpCheckerProvider - [] + [] ( [)>] workspace: Workspace, settings: EditorOptions diff --git a/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs b/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs index a1402780e84..dd8c03416e1 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs @@ -75,7 +75,7 @@ module private FSharpProjectOptionsHelpers = let p2 = newProject.Solution.GetProject(p2.ProjectId) doesProjectIdDiffer || ( - if p1.IsFSharpProject then + if p1.IsFSharp then p1.Version <> p2.Version else let v1 = p1.GetDependentVersionAsync(ct).Result @@ -484,7 +484,7 @@ type internal FSharpProjectOptionsManager workspace.DocumentClosed.Add(fun args -> let doc = args.Document let proj = doc.Project - if proj.IsFSharpProject && proj.IsFSharpMiscellaneousOrMetadata then + if proj.IsFSharp && proj.IsFSharpMiscellaneousOrMetadata then reactor.ClearSingleFileOptionsCache(doc.Id) ) diff --git a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs index 5db9c8978ba..f96c390bfc2 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs @@ -111,7 +111,7 @@ type Project with member this.GetFSharpProjectOptionsAsync() = async { - if this.IsFSharpProject then + if this.IsFSharp then match ProjectCache.Projects.TryGetValue(this) with | true, result -> return result | _ -> @@ -137,7 +137,7 @@ type Project with type Document with member this.GetFSharpSyntaxDefines() = - if this.Project.IsFSharpProject then + if this.Project.IsFSharp then let service = this.Project.Solution.GetFSharpService() service.FSharpProjectOptionsManager.GetCompilationDefinesForEditingDocument(this) else From dbdc77d9ff1c1ae9b675418d22ed7cffc8aecf66 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Mon, 21 Jun 2021 16:51:06 -0700 Subject: [PATCH 05/27] Got mef to almost work in testing --- .../src/FSharp.Editor/Common/Extensions.fs | 3 +- .../FSharpProjectOptionsManager.fs | 6 +- .../LanguageService/LanguageService.fs | 19 ++ .../tests/UnitTests/Tests.RoslynHelpers.fs | 195 ++++++++++++++++-- 4 files changed, 200 insertions(+), 23 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/Common/Extensions.fs b/vsintegration/src/FSharp.Editor/Common/Extensions.fs index 4b9ed235b0e..f6f07117b52 100644 --- a/vsintegration/src/FSharp.Editor/Common/Extensions.fs +++ b/vsintegration/src/FSharp.Editor/Common/Extensions.fs @@ -42,8 +42,7 @@ type Project with member this.IsFSharpMiscellaneous = this.Name = FSharpConstants.FSharpMiscellaneousFilesName member this.IsFSharpMetadata = this.Name.StartsWith(FSharpConstants.FSharpMetadataName) member this.IsFSharpMiscellaneousOrMetadata = this.IsFSharpMiscellaneous || this.IsFSharpMetadata - member this.IsFSharp = - this.FilePath.EndsWith(".fsproj", StringComparison.OrdinalIgnoreCase) + member this.IsFSharp = this.Language = LanguageNames.FSharp type Document with member this.TryGetLanguageService<'T when 'T :> ILanguageService>() = diff --git a/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs b/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs index dd8c03416e1..bd7a2d1f952 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs @@ -99,7 +99,7 @@ type private FSharpProjectOptionsMessage = | ClearSingleFileOptionsCache of DocumentId [] -type private FSharpProjectOptionsReactor (workspace: Workspace, settings: EditorOptions, _serviceProvider, checkerProvider: FSharpCheckerProvider) = +type private FSharpProjectOptionsReactor (settings: EditorOptions, _serviceProvider, checkerProvider: FSharpCheckerProvider) = let cancellationTokenSource = new CancellationTokenSource() // Hack to store command line options from HandleCommandLineChanges @@ -303,7 +303,7 @@ type private FSharpProjectOptionsReactor (workspace: Workspace, settings: Editor return None else // Clear any caches that need clearing and invalidate the project. - let currentSolution = workspace.CurrentSolution + let currentSolution = project.Solution.Workspace.CurrentSolution let projectsToClearCache = cache |> Seq.filter (fun pair -> not (currentSolution.ContainsProject pair.Key)) @@ -471,7 +471,7 @@ type internal FSharpProjectOptionsManager if String.IsNullOrWhiteSpace projectFileName then projectFileName else Path.GetFileNameWithoutExtension projectFileName - let reactor = new FSharpProjectOptionsReactor(workspace, settings, serviceProvider, checkerProvider) + let reactor = new FSharpProjectOptionsReactor(settings, serviceProvider, checkerProvider) do // We need to listen to this event for lifecycle purposes. diff --git a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs index 17d3d9a7971..4119f4b3fe9 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs @@ -22,6 +22,7 @@ open Microsoft.VisualStudio.Shell.Interop open Microsoft.VisualStudio.Text.Outlining open Microsoft.CodeAnalysis.ExternalAccess.FSharp open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Diagnostics +open Microsoft.CodeAnalysis.Host.Mef // Used to expose FSharpChecker/ProjectInfo manager to diagnostic providers // Diagnostic providers can be executed in environment that does not use MEF so they can rely only @@ -31,6 +32,24 @@ type internal FSharpCheckerWorkspaceService = abstract Checker: FSharpChecker abstract FSharpProjectOptionsManager: FSharpProjectOptionsManager +//[, ServiceLayer.Default); Composition.Shared>] +//type internal ImplFSharpCheckerWorkspaceService +// [] +// ( +// checkerProvider: FSharpCheckerProvider, +// projectInfoManager: FSharpProjectOptionsManager +// ) = + +// do +// Console.WriteLine("test") +// Console.WriteLine("test") + +// interface FSharpCheckerWorkspaceService with + +// member _.Checker = checkerProvider.Checker + +// member _.FSharpProjectOptionsManager = projectInfoManager + type internal RoamingProfileStorageLocation(keyName: string) = inherit OptionStorageLocation() diff --git a/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs b/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs index 9c8e0a39008..37591b78387 100644 --- a/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs +++ b/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs @@ -3,35 +3,193 @@ open System open System.IO open System.Reflection +open System.Linq +open System.Composition.Hosting +open System.Collections.Generic open Microsoft.VisualStudio.Composition open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.Host open Microsoft.CodeAnalysis.Text open Microsoft.VisualStudio.FSharp.Editor - -type TestHostWorkspaceServices(hostServices: HostServices, workspace: Workspace) = +open Microsoft.CodeAnalysis.Host.Mef +open Microsoft.VisualStudio.LanguageServices + +[] +module MefHelpers = + + let getAssemblies() = + let self = Assembly.GetExecutingAssembly() + let here = AppContext.BaseDirectory + + let imports = [| + //"Microsoft.CodeAnalysis.LanguageServer.Protocol.dll" + // "Microsoft.CodeAnalysis.Features.dll" + "Microsoft.CodeAnalysis.Workspaces.dll" + "Microsoft.CodeAnalysis.Remote.Workspaces.dll" + // "Microsoft.CodeAnalysis.EditorFeatures.dll" + // "Microsoft.CodeAnalysis.CSharp.EditorFeatures.dll" + // "Microsoft.CodeAnalysis.EditorFeatures.Text.dll" + // "Microsoft.VisualStudio.Text.Logic.dll" + // "Microsoft.VisualStudio.LanguageServices.dll" + "FSharp.Editor.dll" + |] + + let resolvedImports = imports.Select(fun name -> Path.Combine(here, name)).ToList() + let missingDlls = resolvedImports.Where(fun path -> not(File.Exists(path))).ToList() + if (missingDlls.Any()) then + failwith "Missing imports" + + let loadedImports = resolvedImports.Select(fun p -> Assembly.LoadFrom(p)).ToList() + + let result = loadedImports.ToDictionary(fun k -> Path.GetFileNameWithoutExtension(k.Location)) + result.Values + |> Seq.append [|self|] + |> Seq.append MefHostServices.DefaultAssemblies + |> Array.ofSeq + + let exportProvider = + let resolver = Resolver.DefaultInstance + let catalog = + let asms = getAssemblies() + let partDiscovery = PartDiscovery.Combine(new AttributedPartDiscoveryV1(resolver), new AttributedPartDiscovery(resolver, isNonPublicSupported = true)); + let parts = partDiscovery.CreatePartsAsync(asms).Result + let catalog = ComposableCatalog.Create(resolver) + catalog.AddParts(parts) + + let configuration = CompositionConfiguration.Create(catalog.WithCompositionService()) + let runtimeComposition = RuntimeComposition.CreateRuntimeComposition(configuration) + let exportProviderFactory = runtimeComposition.CreateExportProviderFactory() + exportProviderFactory.CreateExportProvider() + +type TestWorkspaceServiceMetadata(serviceType: string, layer: string) = + + member _.ServiceType = serviceType + member _.Layer = layer + + new(data: IDictionary) = + let serviceType = + match data.TryGetValue("ServiceType") with + | true, result -> result :?> string + | _ -> Unchecked.defaultof<_> + + let layer = + match data.TryGetValue("Layer") with + | true, result -> result :?> string + | _ -> Unchecked.defaultof<_> + TestWorkspaceServiceMetadata(serviceType, layer) + + new(serviceType: Type, layer: string) = + TestWorkspaceServiceMetadata(serviceType.AssemblyQualifiedName, layer) + +type TestLanguageServiceMetadata(language: string, serviceType: string, layer: string, data: IDictionary) = + + member _.Language = language + member _.ServiceType = serviceType + member _.Layer = layer + member _.Data = data + + new(data: IDictionary) = + let language = + match data.TryGetValue("Language") with + | true, result -> result :?> string + | _ -> Unchecked.defaultof<_> + + let serviceType = + match data.TryGetValue("ServiceType") with + | true, result -> result :?> string + | _ -> Unchecked.defaultof<_> + + let layer = + match data.TryGetValue("Layer") with + | true, result -> result :?> string + | _ -> Unchecked.defaultof<_> + TestLanguageServiceMetadata(language, serviceType, layer, data) + +type TestHostLanguageServices(workspaceServices: HostWorkspaceServices, language: string) as this = + inherit HostLanguageServices() + + let services1 = + exportProvider.GetExports() + |> Seq.filter (fun x -> x.Metadata.Language = language) + + let factories1 = + exportProvider.GetExports() + |> Seq.filter (fun x -> x.Metadata.Language = language) + |> Seq.map (fun x -> + Lazy<_, _>((fun () -> x.Value.CreateLanguageService(this)), x.Metadata) + ) + + let otherServices1 = Seq.append factories1 services1 + + let otherServicesMap1 = + otherServices1 + |> Seq.map (fun x -> + KeyValuePair(x.Metadata.ServiceType, x) + ) + |> Seq.distinctBy (fun x -> x.Key) + |> System.Collections.Concurrent.ConcurrentDictionary + + override this.WorkspaceServices = workspaceServices + + override this.Language = language + + override this.GetService<'T when 'T :> ILanguageService>() : 'T = + match otherServicesMap1.TryGetValue(typeof<'T>.AssemblyQualifiedName) with + | true, otherService -> + otherService.Value :?> 'T + | _ -> + try + exportProvider.GetExport<'T>().Value + with + | _ -> + Unchecked.defaultof<'T> + +type TestHostWorkspaceServices(hostServices: HostServices, workspace: Workspace) as this = inherit HostWorkspaceServices() - let resolver = Resolver.DefaultInstance - let catalog = - let asms = AppDomain.CurrentDomain.GetAssemblies() - let partDiscovery = PartDiscovery.Combine(new AttributedPartDiscoveryV1(resolver), new AttributedPartDiscovery(resolver, isNonPublicSupported = true)); - let parts = partDiscovery.CreatePartsAsync(asms).Result - let catalog = ComposableCatalog.Create(resolver) - catalog.AddParts(parts) + let services1 = + exportProvider.GetExports() + + let factories1 = + exportProvider.GetExports() + |> Seq.map (fun x -> + Lazy<_, _>((fun () -> x.Value.CreateService(this)), x.Metadata) + ) + + let otherServices1 = Seq.append factories1 services1 - let configuration = CompositionConfiguration.Create(catalog.WithCompositionService()) - let runtimeComposition = RuntimeComposition.CreateRuntimeComposition(configuration) - let exportProviderFactory = runtimeComposition.CreateExportProviderFactory() - let exportProvider = exportProviderFactory.CreateExportProvider().AsExportProvider() + let otherServicesMap1 = + otherServices1 + |> Seq.map (fun x -> + KeyValuePair(x.Metadata.ServiceType, x) + ) + |> Seq.distinctBy (fun x -> x.Key) + |> System.Collections.Concurrent.ConcurrentDictionary + + let langServices = TestHostLanguageServices(this, LanguageNames.FSharp) override _.Workspace = workspace - override _.GetService<'T when 'T :> IWorkspaceService>() = - exportProvider.GetExport<'T>().Value + override this.GetService<'T when 'T :> IWorkspaceService>() : 'T = + match otherServicesMap1.TryGetValue(typeof<'T>.AssemblyQualifiedName) with + | true, otherService -> + otherService.Value :?> 'T + | _ -> + try + exportProvider.GetExport<'T>().Value + with + | _ -> + Unchecked.defaultof<'T> override _.FindLanguageServices(filter) = Seq.empty + override _.GetLanguageServices(languageName) = + match languageName with + | LanguageNames.FSharp -> + langServices :> HostLanguageServices + | _ -> + raise(NotSupportedException(sprintf "Language '%s' not supported in FSharp VS tests." languageName)) + override _.HostServices = hostServices type TestHostServices() = @@ -48,8 +206,9 @@ type RoslynTestHelpers private () = static member CreateDocument (filePath, text: SourceText) = let isScript = String.Equals(Path.GetExtension(filePath), ".fsx", StringComparison.OrdinalIgnoreCase) - let asms = AppDomain.CurrentDomain.GetAssemblies() - let workspace = new AdhocWorkspace(Host.Mef.MefHostServices.Create(asms)) + + let hostServices = TestHostServices() + let workspace = new AdhocWorkspace(hostServices) let projId = ProjectId.CreateNewId() let docId = DocumentId.CreateNewId(projId) @@ -68,7 +227,7 @@ type RoslynTestHelpers private () = VersionStamp.Create(DateTime.UtcNow), "test.fsproj", "test.dll", - LanguageNames.CSharp, // We cannot use LanguageNames.FSharp as Roslyn doesn't support creating adhoc projects with F# language name. + LanguageNames.FSharp, documents = [docInfo] ) From 2809166951100825a40f5d81be9d5c86d5fa7ee0 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Mon, 21 Jun 2021 16:58:02 -0700 Subject: [PATCH 06/27] More cleanup --- vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs b/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs index 37591b78387..a66c0cf6b12 100644 --- a/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs +++ b/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs @@ -25,7 +25,6 @@ module MefHelpers = //"Microsoft.CodeAnalysis.LanguageServer.Protocol.dll" // "Microsoft.CodeAnalysis.Features.dll" "Microsoft.CodeAnalysis.Workspaces.dll" - "Microsoft.CodeAnalysis.Remote.Workspaces.dll" // "Microsoft.CodeAnalysis.EditorFeatures.dll" // "Microsoft.CodeAnalysis.CSharp.EditorFeatures.dll" // "Microsoft.CodeAnalysis.EditorFeatures.Text.dll" @@ -47,7 +46,7 @@ module MefHelpers = |> Seq.append MefHostServices.DefaultAssemblies |> Array.ofSeq - let exportProvider = + let createExportProvider() = let resolver = Resolver.DefaultInstance let catalog = let asms = getAssemblies() @@ -105,7 +104,7 @@ type TestLanguageServiceMetadata(language: string, serviceType: string, layer: s | _ -> Unchecked.defaultof<_> TestLanguageServiceMetadata(language, serviceType, layer, data) -type TestHostLanguageServices(workspaceServices: HostWorkspaceServices, language: string) as this = +type TestHostLanguageServices(workspaceServices: HostWorkspaceServices, language: string, exportProvider: ExportProvider) as this = inherit HostLanguageServices() let services1 = @@ -147,6 +146,8 @@ type TestHostLanguageServices(workspaceServices: HostWorkspaceServices, language type TestHostWorkspaceServices(hostServices: HostServices, workspace: Workspace) as this = inherit HostWorkspaceServices() + let exportProvider = createExportProvider() + let services1 = exportProvider.GetExports() @@ -166,7 +167,7 @@ type TestHostWorkspaceServices(hostServices: HostServices, workspace: Workspace) |> Seq.distinctBy (fun x -> x.Key) |> System.Collections.Concurrent.ConcurrentDictionary - let langServices = TestHostLanguageServices(this, LanguageNames.FSharp) + let langServices = TestHostLanguageServices(this, LanguageNames.FSharp, exportProvider) override _.Workspace = workspace @@ -207,8 +208,7 @@ type RoslynTestHelpers private () = static member CreateDocument (filePath, text: SourceText) = let isScript = String.Equals(Path.GetExtension(filePath), ".fsx", StringComparison.OrdinalIgnoreCase) - let hostServices = TestHostServices() - let workspace = new AdhocWorkspace(hostServices) + let workspace = new AdhocWorkspace(TestHostServices.Instance) let projId = ProjectId.CreateNewId() let docId = DocumentId.CreateNewId(projId) From ea356718ac48a46bcfe971df00aca72ca2fd5f45 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Mon, 21 Jun 2021 17:53:27 -0700 Subject: [PATCH 07/27] cleanup --- .../LanguageService/LanguageService.fs | 28 ++++--------------- .../LanguageService/WorkspaceExtensions.fs | 2 +- .../tests/UnitTests/Tests.RoslynHelpers.fs | 15 ++++++++-- 3 files changed, 18 insertions(+), 27 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs index 4119f4b3fe9..3a85c5e8e4f 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs @@ -27,29 +27,11 @@ open Microsoft.CodeAnalysis.Host.Mef // Used to expose FSharpChecker/ProjectInfo manager to diagnostic providers // Diagnostic providers can be executed in environment that does not use MEF so they can rely only // on services exposed by the workspace -type internal FSharpCheckerWorkspaceService = +type internal IFSharpWorkspaceService = inherit Microsoft.CodeAnalysis.Host.IWorkspaceService abstract Checker: FSharpChecker abstract FSharpProjectOptionsManager: FSharpProjectOptionsManager -//[, ServiceLayer.Default); Composition.Shared>] -//type internal ImplFSharpCheckerWorkspaceService -// [] -// ( -// checkerProvider: FSharpCheckerProvider, -// projectInfoManager: FSharpProjectOptionsManager -// ) = - -// do -// Console.WriteLine("test") -// Console.WriteLine("test") - -// interface FSharpCheckerWorkspaceService with - -// member _.Checker = checkerProvider.Checker - -// member _.FSharpProjectOptionsManager = projectInfoManager - type internal RoamingProfileStorageLocation(keyName: string) = inherit OptionStorageLocation() @@ -62,16 +44,16 @@ type internal RoamingProfileStorageLocation(keyName: string) = unsubstitutedKeyName.Replace("%LANGUAGE%", substituteLanguageName) [] -[, Microsoft.CodeAnalysis.Host.Mef.ServiceLayer.Default)>] -type internal FSharpCheckerWorkspaceServiceFactory +[, ServiceLayer.Default)>] +type internal FSharpWorkspaceServiceFactory [] ( checkerProvider: FSharpCheckerProvider, projectInfoManager: FSharpProjectOptionsManager ) = - interface Microsoft.CodeAnalysis.Host.Mef.IWorkspaceServiceFactory with + interface IWorkspaceServiceFactory with member _.CreateService(_workspaceServices) = - upcast { new FSharpCheckerWorkspaceService with + upcast { new IFSharpWorkspaceService with member _.Checker = checkerProvider.Checker member _.FSharpProjectOptionsManager = projectInfoManager } diff --git a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs index f96c390bfc2..d0ac1acaf2c 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs @@ -105,7 +105,7 @@ module private ProjectCache = type Solution with member this.GetFSharpService() = - this.Workspace.Services.GetRequiredService() + this.Workspace.Services.GetRequiredService() type Project with diff --git a/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs b/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs index a66c0cf6b12..83926473fec 100644 --- a/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs +++ b/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs @@ -13,6 +13,7 @@ open Microsoft.CodeAnalysis.Text open Microsoft.VisualStudio.FSharp.Editor open Microsoft.CodeAnalysis.Host.Mef open Microsoft.VisualStudio.LanguageServices +open Microsoft.VisualStudio.Shell [] module MefHelpers = @@ -30,6 +31,7 @@ module MefHelpers = // "Microsoft.CodeAnalysis.EditorFeatures.Text.dll" // "Microsoft.VisualStudio.Text.Logic.dll" // "Microsoft.VisualStudio.LanguageServices.dll" + // "Microsoft.VisualStudio.Shell.Framework.dll" "FSharp.Editor.dll" |] @@ -148,6 +150,11 @@ type TestHostWorkspaceServices(hostServices: HostServices, workspace: Workspace) let exportProvider = createExportProvider() + do + let vsworkspace = exportProvider.GetExportedValue() + let serviceProvider = exportProvider.GetExportedValue() + () + let services1 = exportProvider.GetExports() @@ -157,7 +164,8 @@ type TestHostWorkspaceServices(hostServices: HostServices, workspace: Workspace) Lazy<_, _>((fun () -> x.Value.CreateService(this)), x.Metadata) ) - let otherServices1 = Seq.append factories1 services1 + let otherServices1 = + Seq.append factories1 services1 let otherServicesMap1 = otherServices1 @@ -172,7 +180,8 @@ type TestHostWorkspaceServices(hostServices: HostServices, workspace: Workspace) override _.Workspace = workspace override this.GetService<'T when 'T :> IWorkspaceService>() : 'T = - match otherServicesMap1.TryGetValue(typeof<'T>.AssemblyQualifiedName) with + let ty = typeof<'T> + match otherServicesMap1.TryGetValue(ty.AssemblyQualifiedName) with | true, otherService -> otherService.Value :?> 'T | _ -> @@ -216,7 +225,7 @@ type RoslynTestHelpers private () = let docInfo = DocumentInfo.Create( docId, - filePath, + filePath, loader=TextLoader.From(text.Container, VersionStamp.Create(DateTime.UtcNow)), filePath=filePath, sourceCodeKind= if isScript then SourceCodeKind.Script else SourceCodeKind.Regular) From f9c0c6e6da5050a83ed44e7ea4443706b8ea2a91 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Mon, 21 Jun 2021 18:40:42 -0700 Subject: [PATCH 08/27] minor cleanup --- vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs b/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs index 83926473fec..5db190a0729 100644 --- a/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs +++ b/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs @@ -23,15 +23,8 @@ module MefHelpers = let here = AppContext.BaseDirectory let imports = [| - //"Microsoft.CodeAnalysis.LanguageServer.Protocol.dll" - // "Microsoft.CodeAnalysis.Features.dll" "Microsoft.CodeAnalysis.Workspaces.dll" - // "Microsoft.CodeAnalysis.EditorFeatures.dll" - // "Microsoft.CodeAnalysis.CSharp.EditorFeatures.dll" - // "Microsoft.CodeAnalysis.EditorFeatures.Text.dll" - // "Microsoft.VisualStudio.Text.Logic.dll" - // "Microsoft.VisualStudio.LanguageServices.dll" - // "Microsoft.VisualStudio.Shell.Framework.dll" + "Microsoft.VisualStudio.Shell.15.0.dll" "FSharp.Editor.dll" |] @@ -151,8 +144,9 @@ type TestHostWorkspaceServices(hostServices: HostServices, workspace: Workspace) let exportProvider = createExportProvider() do - let vsworkspace = exportProvider.GetExportedValue() + // let vsworkspace = exportProvider.GetExportedValue() let serviceProvider = exportProvider.GetExportedValue() + let editorOptions = exportProvider.GetExportedValue() () let services1 = From 5c96c8298c9a2f140e861a15a23c893209b1f056 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Mon, 21 Jun 2021 19:05:59 -0700 Subject: [PATCH 09/27] Added IFSharpVisualStudioService --- .../Common/IFSharpVisualStudioService.fs | 29 +++++++++++++++++++ .../src/FSharp.Editor/FSharp.Editor.fsproj | 1 + .../LanguageService/FSharpCheckerProvider.fs | 4 +-- .../FSharpProjectOptionsManager.fs | 15 +++++----- .../FSharp.Editor/Options/EditorOptions.fs | 2 +- 5 files changed, 40 insertions(+), 11 deletions(-) create mode 100644 vsintegration/src/FSharp.Editor/Common/IFSharpVisualStudioService.fs diff --git a/vsintegration/src/FSharp.Editor/Common/IFSharpVisualStudioService.fs b/vsintegration/src/FSharp.Editor/Common/IFSharpVisualStudioService.fs new file mode 100644 index 00000000000..8ab8daa567f --- /dev/null +++ b/vsintegration/src/FSharp.Editor/Common/IFSharpVisualStudioService.fs @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace rec Microsoft.VisualStudio.FSharp.Editor + +open System +open Microsoft.CodeAnalysis +open Microsoft.VisualStudio.LanguageServices +open Microsoft.VisualStudio.Shell + +type internal IFSharpVisualStudioService = + + abstract Workspace: Workspace + + abstract ServiceProvider: IServiceProvider + +[] +[)>] +type internal FSharpVisualStudioService + [] + ( + [)>] workspace, + [)>] serviceProvider + ) = + + interface IFSharpVisualStudioService with + + member _.Workspace = workspace + + member _.ServiceProvider = serviceProvider \ No newline at end of file diff --git a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj index 59a76f18659..9bca90a9ccd 100644 --- a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj +++ b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj @@ -34,6 +34,7 @@ + diff --git a/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerProvider.fs b/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerProvider.fs index 6e0571a052c..3d218b18a6b 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerProvider.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerProvider.fs @@ -22,12 +22,12 @@ open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Diagnostics type internal FSharpCheckerProvider [] ( - [)>] workspace: Workspace, + fsVsService: IFSharpVisualStudioService, settings: EditorOptions ) = let tryGetMetadataSnapshot (path, timeStamp) = - match workspace with + match fsVsService.Workspace with | :? VisualStudioWorkspace as workspace -> try let md = Microsoft.CodeAnalysis.ExternalAccess.FSharp.LanguageServices.FSharpVisualStudioWorkspaceExtensions.GetMetadata(workspace, path, timeStamp) diff --git a/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs b/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs index bd7a2d1f952..1bb852a1877 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs @@ -99,7 +99,7 @@ type private FSharpProjectOptionsMessage = | ClearSingleFileOptionsCache of DocumentId [] -type private FSharpProjectOptionsReactor (settings: EditorOptions, _serviceProvider, checkerProvider: FSharpCheckerProvider) = +type private FSharpProjectOptionsReactor (settings: EditorOptions, checkerProvider: FSharpCheckerProvider) = let cancellationTokenSource = new CancellationTokenSource() // Hack to store command line options from HandleCommandLineChanges @@ -462,26 +462,25 @@ type internal FSharpProjectOptionsManager [] ( checkerProvider: FSharpCheckerProvider, - [)>] workspace: Workspace, - [)>] serviceProvider: System.IServiceProvider, - settings: EditorOptions + settings: EditorOptions, + fsVsService: IFSharpVisualStudioService ) = let projectDisplayNameOf projectFileName = if String.IsNullOrWhiteSpace projectFileName then projectFileName else Path.GetFileNameWithoutExtension projectFileName - let reactor = new FSharpProjectOptionsReactor(settings, serviceProvider, checkerProvider) + let reactor = new FSharpProjectOptionsReactor(settings, checkerProvider) do // We need to listen to this event for lifecycle purposes. - workspace.WorkspaceChanged.Add(fun args -> + fsVsService.Workspace.WorkspaceChanged.Add(fun args -> match args.Kind with | WorkspaceChangeKind.ProjectRemoved -> reactor.ClearOptionsByProjectId(args.ProjectId) | _ -> () ) - workspace.DocumentClosed.Add(fun args -> + fsVsService.Workspace.DocumentClosed.Add(fun args -> let doc = args.Document let proj = doc.Project if proj.IsFSharp && proj.IsFSharpMiscellaneousOrMetadata then @@ -545,7 +544,7 @@ type internal FSharpProjectOptionsManager member _.HandleCommandLineChanges(path:string, sources:ImmutableArray, _references:ImmutableArray, options:ImmutableArray) = use _logBlock = Logger.LogBlock(LogEditorFunctionId.LanguageService_HandleCommandLineArgs) - match workspace with + match fsVsService.Workspace with | :? VisualStudioWorkspace as workspace -> let projectId = match Microsoft.CodeAnalysis.ExternalAccess.FSharp.LanguageServices.FSharpVisualStudioWorkspaceExtensions.TryGetProjectIdByBinPath(workspace, path) with diff --git a/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs b/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs index 9261bdc104e..d0cd538c5d8 100644 --- a/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs +++ b/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs @@ -100,7 +100,7 @@ type FormattingOptions = type EditorOptions [] ( - [)>] serviceProvider: IServiceProvider + [)>] serviceProvider: IServiceProvider ) = let store = SettingsStore(serviceProvider) From 95492d6627ef27e4b56d3afd94382bb14c6f1655 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Mon, 21 Jun 2021 20:00:54 -0700 Subject: [PATCH 10/27] Some tests are passing. Removed use of EditorOptions in CheckerProvider and OptionsManager --- .../LanguageService/FSharpCheckerProvider.fs | 7 +-- .../FSharpProjectOptionsManager.fs | 38 ++++++++------- .../tests/UnitTests/Tests.RoslynHelpers.fs | 46 ++++++++++++++++--- 3 files changed, 60 insertions(+), 31 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerProvider.fs b/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerProvider.fs index 3d218b18a6b..bc845341bf5 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerProvider.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerProvider.fs @@ -22,8 +22,7 @@ open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Diagnostics type internal FSharpCheckerProvider [] ( - fsVsService: IFSharpVisualStudioService, - settings: EditorOptions + fsVsService: IFSharpVisualStudioService ) = let tryGetMetadataSnapshot (path, timeStamp) = @@ -57,10 +56,8 @@ type internal FSharpCheckerProvider lazy let checker = FSharpChecker.Create( - projectCacheSize = settings.LanguageServicePerformance.ProjectCheckCacheSize, + projectCacheSize = 5000, // We do not care how big the cache is. VS will actually tell FCS to clear caches, so this is fine. keepAllBackgroundResolutions = false, - // Enabling this would mean that if devenv.exe goes above 2.3GB we do a one-off downsize of the F# Compiler Service caches - (* , MaxMemory = 2300 *) legacyReferenceResolver=LegacyMSBuildReferenceResolver.getResolver(), tryGetMetadataSnapshot = tryGetMetadataSnapshot, keepAllBackgroundSymbolUses = false, diff --git a/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs b/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs index 1bb852a1877..0158f703db3 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs @@ -84,12 +84,9 @@ module private FSharpProjectOptionsHelpers = ) ) - let isProjectInvalidated (oldProject: Project) (newProject: Project) (settings: EditorOptions) ct = + let isProjectInvalidated (oldProject: Project) (newProject: Project) ct = let hasProjectVersionChanged = hasProjectVersionChanged oldProject newProject - if settings.LanguageServicePerformance.EnableInMemoryCrossProjectReferences then - hasProjectVersionChanged || hasDependentVersionChanged oldProject newProject ct - else - hasProjectVersionChanged + hasProjectVersionChanged || hasDependentVersionChanged oldProject newProject ct [] type private FSharpProjectOptionsMessage = @@ -99,7 +96,7 @@ type private FSharpProjectOptionsMessage = | ClearSingleFileOptionsCache of DocumentId [] -type private FSharpProjectOptionsReactor (settings: EditorOptions, checkerProvider: FSharpCheckerProvider) = +type private FSharpProjectOptionsReactor (checkerProvider: FSharpCheckerProvider) = let cancellationTokenSource = new CancellationTokenSource() // Hack to store command line options from HandleCommandLineChanges @@ -245,17 +242,16 @@ type private FSharpProjectOptionsReactor (settings: EditorOptions, checkerProvid let referencedProjects = ResizeArray() - if settings.LanguageServicePerformance.EnableInMemoryCrossProjectReferences then - for projectReference in project.ProjectReferences do - let referencedProject = project.Solution.GetProject(projectReference.ProjectId) - if referencedProject.Language = FSharpConstants.FSharpLanguageName then - match! tryComputeOptions referencedProject ct with - | None -> canBail <- true - | Some(_, projectOptions) -> referencedProjects.Add(FSharpReferencedProject.CreateFSharp(referencedProject.OutputFilePath, projectOptions)) - elif referencedProject.SupportsCompilation then - let! comp = referencedProject.GetCompilationAsync(ct) |> Async.AwaitTask - let peRef = createPEReference referencedProject comp - referencedProjects.Add(peRef) + for projectReference in project.ProjectReferences do + let referencedProject = project.Solution.GetProject(projectReference.ProjectId) + if referencedProject.Language = FSharpConstants.FSharpLanguageName then + match! tryComputeOptions referencedProject ct with + | None -> canBail <- true + | Some(_, projectOptions) -> referencedProjects.Add(FSharpReferencedProject.CreateFSharp(referencedProject.OutputFilePath, projectOptions)) + elif referencedProject.SupportsCompilation then + let! comp = referencedProject.GetCompilationAsync(ct) |> Async.AwaitTask + let peRef = createPEReference referencedProject comp + referencedProjects.Add(peRef) if canBail then return None @@ -333,7 +329,7 @@ type private FSharpProjectOptionsReactor (settings: EditorOptions, checkerProvid return Some(parsingOptions, projectOptions) | true, (oldProject, parsingOptions, projectOptions) -> - if isProjectInvalidated oldProject project settings ct then + if isProjectInvalidated oldProject project ct then cache.TryRemove(projectId) |> ignore return! tryComputeOptions project ct else @@ -462,7 +458,6 @@ type internal FSharpProjectOptionsManager [] ( checkerProvider: FSharpCheckerProvider, - settings: EditorOptions, fsVsService: IFSharpVisualStudioService ) = @@ -470,7 +465,7 @@ type internal FSharpProjectOptionsManager if String.IsNullOrWhiteSpace projectFileName then projectFileName else Path.GetFileNameWithoutExtension projectFileName - let reactor = new FSharpProjectOptionsReactor(settings, checkerProvider) + let reactor = new FSharpProjectOptionsReactor(checkerProvider) do // We need to listen to this event for lifecycle purposes. @@ -534,6 +529,9 @@ type internal FSharpProjectOptionsManager | Some (_, parsingOptions, _) -> parsingOptions | _ -> { FSharpParsingOptions.Default with IsInteractive = CompilerEnvironment.IsScriptFile document.Name } + member this.SetCommandLineOptions(projectId, sourcePaths, options: ImmutableArray) = + reactor.SetCpsCommandLineOptions(projectId, sourcePaths, options.ToArray()) + member this.ClearAllCaches() = reactor.ClearAllCaches() diff --git a/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs b/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs index 5db190a0729..027e8962851 100644 --- a/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs +++ b/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs @@ -6,6 +6,7 @@ open System.Reflection open System.Linq open System.Composition.Hosting open System.Collections.Generic +open System.Collections.Immutable open Microsoft.VisualStudio.Composition open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.Host @@ -138,17 +139,39 @@ type TestHostLanguageServices(workspaceServices: HostWorkspaceServices, language | _ -> Unchecked.defaultof<'T> +[); Composition.Shared>] +type internal MockFSharpVisualStudioService() = + + let workspace = new AdhocWorkspace() :> Workspace + + interface IFSharpVisualStudioService with + + member _.Workspace = workspace + + member _.ServiceProvider = null + +[); Composition.Shared>] +type internal MockFSharpCheckerProvider() = + inherit FSharpCheckerProvider(MockFSharpVisualStudioService()) + +[); Composition.Shared>] +type internal MockFSharpWorkspaceService() = + + let checkerProvider = MockFSharpCheckerProvider() + let fsVsService = MockFSharpVisualStudioService() + let manager = FSharpProjectOptionsManager(checkerProvider, fsVsService) + + interface IFSharpWorkspaceService with + + member _.Checker = checkerProvider.Checker + + member _.FSharpProjectOptionsManager = manager + type TestHostWorkspaceServices(hostServices: HostServices, workspace: Workspace) as this = inherit HostWorkspaceServices() let exportProvider = createExportProvider() - do - // let vsworkspace = exportProvider.GetExportedValue() - let serviceProvider = exportProvider.GetExportedValue() - let editorOptions = exportProvider.GetExportedValue() - () - let services1 = exportProvider.GetExports() @@ -171,6 +194,13 @@ type TestHostWorkspaceServices(hostServices: HostServices, workspace: Workspace) let langServices = TestHostLanguageServices(this, LanguageNames.FSharp, exportProvider) + do + let x = exportProvider.GetExportedValue() + let y = exportProvider.GetExportedValue() + Console.WriteLine(x) + Console.WriteLine(y) + () + override _.Workspace = workspace override this.GetService<'T when 'T :> IWorkspaceService>() : 'T = @@ -237,6 +267,10 @@ type RoslynTestHelpers private () = let solutionInfo = SolutionInfo.Create(SolutionId.CreateNewId(), VersionStamp.Create(DateTime.UtcNow), "test.sln", [projInfo]) let solution = workspace.AddSolution(solutionInfo) + + let workspaceService = workspace.Services.GetService() + workspaceService.FSharpProjectOptionsManager.SetCommandLineOptions(projId, [|filePath|], ImmutableArray.Empty) + solution.GetProject(projId).GetDocument(docId) static member CreateDocument (filePath, code: string) = From 241dc4aff7a955ec29a25bb79b5a507579dfef4a Mon Sep 17 00:00:00 2001 From: Will Smith Date: Mon, 21 Jun 2021 20:07:27 -0700 Subject: [PATCH 11/27] cleanup --- .../tests/UnitTests/Tests.RoslynHelpers.fs | 25 +++++++------------ 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs b/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs index 027e8962851..14a79a5e5d2 100644 --- a/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs +++ b/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs @@ -142,24 +142,24 @@ type TestHostLanguageServices(workspaceServices: HostWorkspaceServices, language [); Composition.Shared>] type internal MockFSharpVisualStudioService() = - let workspace = new AdhocWorkspace() :> Workspace + static let mockWorkspace = new AdhocWorkspace() + static let instance = MockFSharpVisualStudioService() + static member Instance = instance interface IFSharpVisualStudioService with - member _.Workspace = workspace + member _.Workspace = mockWorkspace :> Workspace member _.ServiceProvider = null -[); Composition.Shared>] -type internal MockFSharpCheckerProvider() = - inherit FSharpCheckerProvider(MockFSharpVisualStudioService()) - [); Composition.Shared>] type internal MockFSharpWorkspaceService() = - let checkerProvider = MockFSharpCheckerProvider() - let fsVsService = MockFSharpVisualStudioService() - let manager = FSharpProjectOptionsManager(checkerProvider, fsVsService) + static let checkerProvider = FSharpCheckerProvider(MockFSharpVisualStudioService.Instance) + static let manager = FSharpProjectOptionsManager(checkerProvider, MockFSharpVisualStudioService.Instance) + + static let instance = MockFSharpWorkspaceService() + static member Instance = instance interface IFSharpWorkspaceService with @@ -194,13 +194,6 @@ type TestHostWorkspaceServices(hostServices: HostServices, workspace: Workspace) let langServices = TestHostLanguageServices(this, LanguageNames.FSharp, exportProvider) - do - let x = exportProvider.GetExportedValue() - let y = exportProvider.GetExportedValue() - Console.WriteLine(x) - Console.WriteLine(y) - () - override _.Workspace = workspace override this.GetService<'T when 'T :> IWorkspaceService>() : 'T = From 4014bec3f15baef8fb9cd156299cf503ff3ab5b7 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Tue, 22 Jun 2021 11:25:52 -0700 Subject: [PATCH 12/27] Added Project extensions to get editor options --- .../CodeFix/AddOpenCodeFixProvider.fs | 2 +- .../CodeFix/RemoveUnusedBinding.fs | 2 +- .../CodeFix/RenameUnusedValue.fs | 2 +- .../SimplifyNameDiagnosticAnalyzer.fs | 2 +- .../Diagnostics/UnusedDeclarationsAnalyzer.fs | 2 +- .../UnusedOpensDiagnosticAnalyzer.fs | 2 +- .../FSharpProjectOptionsManager.fs | 26 ++++---- .../LanguageService/WorkspaceExtensions.fs | 16 ++--- .../FSharp.Editor/Options/EditorOptions.fs | 61 ++++++++++++++++--- .../Structure/BlockStructureService.fs | 2 +- 10 files changed, 84 insertions(+), 33 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs b/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs index 3f21b29a21c..3abcae6ba1b 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs @@ -134,7 +134,7 @@ type internal FSharpAddOpenCodeFixProvider |> List.toArray) let insertionPoint = - if document.FSharpOptions.CodeFixes.AlwaysPlaceOpensAtTopLevel then OpenStatementInsertionPoint.TopLevel + if document.Project.IsFSharpCodeFixesAlwaysPlaceOpensAtTopLevelEnabled then OpenStatementInsertionPoint.TopLevel else OpenStatementInsertionPoint.Nearest let createEntity = ParsedInput.TryFindInsertionContext unresolvedIdentRange.StartLine parseResults.ParseTree maybeUnresolvedIdents insertionPoint diff --git a/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedBinding.fs b/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedBinding.fs index 4325ae11604..3ae92a56c4a 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedBinding.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedBinding.fs @@ -24,7 +24,7 @@ type internal FSharpRemoveUnusedBindingCodeFixProvider override _.RegisterCodeFixesAsync context : Task = asyncMaybe { // Don't show code fixes for unused values, even if they are compiler-generated. - do! Option.guard context.Document.FSharpOptions.CodeFixes.UnusedDeclarations + do! Option.guard context.Document.Project.IsFSharpCodeFixesUnusedDeclarationsEnabled let document = context.Document let! sourceText = document.GetTextAsync(context.CancellationToken) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs b/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs index 09cd2ccce57..2678dd52d31 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs @@ -29,7 +29,7 @@ type internal FSharpRenameUnusedValueCodeFixProvider override _.RegisterCodeFixesAsync context : Task = asyncMaybe { // Don't show code fixes for unused values, even if they are compiler-generated. - do! Option.guard context.Document.FSharpOptions.CodeFixes.UnusedDeclarations + do! Option.guard context.Document.Project.IsFSharpCodeFixesUnusedDeclarationsEnabled let document = context.Document let! sourceText = document.GetTextAsync(context.CancellationToken) diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/SimplifyNameDiagnosticAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/SimplifyNameDiagnosticAnalyzer.fs index 435d09d6530..aeef5b41539 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/SimplifyNameDiagnosticAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/SimplifyNameDiagnosticAnalyzer.fs @@ -36,7 +36,7 @@ type internal SimplifyNameDiagnosticAnalyzer else asyncMaybe { - do! Option.guard document.FSharpOptions.CodeFixes.SimplifyName + do! Option.guard document.Project.IsFSharpCodeFixesSimplifyNameEnabled do Trace.TraceInformation("{0:n3} (start) SimplifyName", DateTime.Now.TimeOfDay.TotalSeconds) let! textVersion = document.GetTextVersionAsync(cancellationToken) let textVersionHash = textVersion.GetHashCode() diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs index 892b45e7b0a..564762fb4c1 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs @@ -24,7 +24,7 @@ type internal UnusedDeclarationsAnalyzer else asyncMaybe { - do! Option.guard document.FSharpOptions.CodeFixes.UnusedDeclarations + do! Option.guard document.Project.IsFSharpCodeFixesUnusedDeclarationsEnabled do Trace.TraceInformation("{0:n3} (start) UnusedDeclarationsAnalyzer", DateTime.Now.TimeOfDay.TotalSeconds) let! _, checkResults = document.GetFSharpParseAndCheckResultsAsync() |> liftAsync diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs index 71792e2a08a..eaffc3e6214 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs @@ -24,7 +24,7 @@ type internal UnusedOpensDiagnosticAnalyzer static member GetUnusedOpenRanges(document: Document) : Async> = asyncMaybe { - do! Option.guard document.FSharpOptions.CodeFixes.UnusedOpens + do! Option.guard document.Project.IsFSharpCodeFixesUnusedOpensEnabled let! sourceText = document.GetTextAsync() let! _, checkResults = document.GetFSharpParseAndCheckResultsAsync() |> liftAsync let! unusedOpens = UnusedOpens.getUnusedOpens(checkResults, fun lineNumber -> sourceText.Lines.[Line.toZ lineNumber].ToString()) |> liftAsync diff --git a/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs b/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs index 0158f703db3..06c23d3778b 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs @@ -86,7 +86,10 @@ module private FSharpProjectOptionsHelpers = let isProjectInvalidated (oldProject: Project) (newProject: Project) ct = let hasProjectVersionChanged = hasProjectVersionChanged oldProject newProject - hasProjectVersionChanged || hasDependentVersionChanged oldProject newProject ct + if newProject.IsFSharpCrossProjectReferencingEnabled then + hasProjectVersionChanged || hasDependentVersionChanged oldProject newProject ct + else + hasProjectVersionChanged [] type private FSharpProjectOptionsMessage = @@ -242,16 +245,17 @@ type private FSharpProjectOptionsReactor (checkerProvider: FSharpCheckerProvider let referencedProjects = ResizeArray() - for projectReference in project.ProjectReferences do - let referencedProject = project.Solution.GetProject(projectReference.ProjectId) - if referencedProject.Language = FSharpConstants.FSharpLanguageName then - match! tryComputeOptions referencedProject ct with - | None -> canBail <- true - | Some(_, projectOptions) -> referencedProjects.Add(FSharpReferencedProject.CreateFSharp(referencedProject.OutputFilePath, projectOptions)) - elif referencedProject.SupportsCompilation then - let! comp = referencedProject.GetCompilationAsync(ct) |> Async.AwaitTask - let peRef = createPEReference referencedProject comp - referencedProjects.Add(peRef) + if project.IsFSharpCrossProjectReferencingEnabled then + for projectReference in project.ProjectReferences do + let referencedProject = project.Solution.GetProject(projectReference.ProjectId) + if referencedProject.Language = FSharpConstants.FSharpLanguageName then + match! tryComputeOptions referencedProject ct with + | None -> canBail <- true + | Some(_, projectOptions) -> referencedProjects.Add(FSharpReferencedProject.CreateFSharp(referencedProject.OutputFilePath, projectOptions)) + elif referencedProject.SupportsCompilation then + let! comp = referencedProject.GetCompilationAsync(ct) |> Async.AwaitTask + let peRef = createPEReference referencedProject comp + referencedProjects.Add(peRef) if canBail then return None diff --git a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs index d0ac1acaf2c..29390cf9db2 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs @@ -32,7 +32,7 @@ module private CheckerExtensions = return! checker.CheckFileInProject(parseResults, filePath, textVersionHash, sourceText.ToFSharpSourceText(), options,userOpName=userOpName) } - member checker.ParseAndCheckDocument(document: Document, options: FSharpProjectOptions, languageServicePerformanceOptions: LanguageServicePerformanceOptions, userOpName: string) = + member checker.ParseAndCheckDocumentWithPossibleStaleResults(document: Document, options: FSharpProjectOptions, allowStaleResults: bool, userOpName: string) = async { let! ct = Async.CancellationToken @@ -55,7 +55,7 @@ module private CheckerExtensions = let tryGetFreshResultsWithTimeout() = async { - let! worker = Async.StartChild(async { try return! parseAndCheckFile with | _ -> return None }, millisecondsTimeout=languageServicePerformanceOptions.TimeUntilStaleCompletion) + let! worker = Async.StartChild(async { try return! parseAndCheckFile with | _ -> return None }, millisecondsTimeout=document.Project.FSharpTimeUntilStaleCompletion) try return! worker with :? TimeoutException -> @@ -68,7 +68,7 @@ module private CheckerExtensions = Some (parseResults, parseResults.ParseTree, checkResults) | None -> None - if languageServicePerformanceOptions.AllowStaleCompletionResults then + if allowStaleResults then let! freshResults = tryGetFreshResultsWithTimeout() let! results = @@ -90,11 +90,11 @@ module private CheckerExtensions = member checker.ParseAndCheckDocument(document: Document, options: FSharpProjectOptions, userOpName: string, ?allowStaleResults: bool) = async { - let perfOpts = + let allowStaleResults = match allowStaleResults with - | Some b -> { document.FSharpOptions.LanguageServicePerformance with AllowStaleCompletionResults = b } - | _ -> document.FSharpOptions.LanguageServicePerformance - return! checker.ParseAndCheckDocument(document, options, perfOpts, userOpName=userOpName) + | Some b -> b + | _ -> document.Project.IsFSharpStaleCompletionResultsEnabled + return! checker.ParseAndCheckDocumentWithPossibleStaleResults(document, options, allowStaleResults, userOpName=userOpName) } [] @@ -104,7 +104,7 @@ module private ProjectCache = type Solution with - member this.GetFSharpService() = + member private this.GetFSharpService() = this.Workspace.Services.GetRequiredService() type Project with diff --git a/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs b/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs index d0cd538c5d8..77eb38332fb 100644 --- a/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs +++ b/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs @@ -5,6 +5,7 @@ open System.ComponentModel.Composition open System.Runtime.InteropServices open Microsoft.VisualStudio.Shell open Microsoft.VisualStudio.FSharp.UIResources +open Microsoft.CodeAnalysis module DefaultTuning = /// How long is the per-document data saved before it is eligible for eviction from the cache? 10 seconds. @@ -128,13 +129,6 @@ type EditorOptions member _.LoadSettings() = store.LoadSettings() member _.SaveSettings(settings) = store.SaveSettings(settings) - -[] -module internal WorkspaceSettingFromDocumentExtension = - type Microsoft.CodeAnalysis.Document with - member this.FSharpOptions = - this.Project.Solution.Workspace.Services.GetService() : EditorOptions - module internal OptionsUI = open OptionsUIHelpers @@ -194,3 +188,56 @@ module internal OptionsUI = inherit AbstractOptionPage() override _.CreateView() = upcast FormattingOptionsControl() + +[] +module EditorOptionsExtensions = + + type Project with + + member this.IsFSharpCrossProjectReferencingEnabled = + let editorOptions = this.Solution.Workspace.Services.GetService() + match box editorOptions with + | null -> true + | _ -> editorOptions.LanguageServicePerformance.EnableInMemoryCrossProjectReferences + + member this.IsFSharpCodeFixesAlwaysPlaceOpensAtTopLevelEnabled = + let editorOptions = this.Solution.Workspace.Services.GetService() + match box editorOptions with + | null -> false + | _ -> editorOptions.CodeFixes.AlwaysPlaceOpensAtTopLevel + + member this.IsFSharpCodeFixesUnusedDeclarationsEnabled = + let editorOptions = this.Solution.Workspace.Services.GetService() + match box editorOptions with + | null -> false + | _ -> editorOptions.CodeFixes.UnusedDeclarations + + member this.IsFSharpStaleCompletionResultsEnabled = + let editorOptions = this.Solution.Workspace.Services.GetService() + match box editorOptions with + | null -> false + | _ -> editorOptions.LanguageServicePerformance.AllowStaleCompletionResults + + member this.FSharpTimeUntilStaleCompletion = + let editorOptions = this.Solution.Workspace.Services.GetService() + match box editorOptions with + | null -> 0 + | _ -> editorOptions.LanguageServicePerformance.TimeUntilStaleCompletion + + member this.IsFSharpCodeFixesSimplifyNameEnabled = + let editorOptions = this.Solution.Workspace.Services.GetService() + match box editorOptions with + | null -> false + | _ -> editorOptions.CodeFixes.SimplifyName + + member this.IsFSharpCodeFixesUnusedOpensEnabled = + let editorOptions = this.Solution.Workspace.Services.GetService() + match box editorOptions with + | null -> false + | _ -> editorOptions.CodeFixes.UnusedOpens + + member this.IsFSharpBlockStructureEnabled = + let editorOptions = this.Solution.Workspace.Services.GetService() + match box editorOptions with + | null -> false + | _ -> editorOptions.Advanced.IsBlockStructureEnabled \ No newline at end of file diff --git a/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs b/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs index c9b3205cc40..612cde6dd4e 100644 --- a/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs +++ b/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs @@ -149,7 +149,7 @@ type internal FSharpBlockStructureService [] () = asyncMaybe { let! sourceText = document.GetTextAsync(cancellationToken) let! parseResults = document.GetFSharpParseResultsAsync() |> liftAsync - return createBlockSpans document.FSharpOptions.Advanced.IsBlockStructureEnabled sourceText parseResults.ParseTree |> Seq.toImmutableArray + return createBlockSpans document.Project.IsFSharpBlockStructureEnabled sourceText parseResults.ParseTree |> Seq.toImmutableArray } |> Async.map (Option.defaultValue ImmutableArray<_>.Empty) |> Async.map FSharpBlockStructure From 84d033ca9a2fe915c4449356d889cffe6ba179bf Mon Sep 17 00:00:00 2001 From: Will Smith Date: Tue, 22 Jun 2021 11:45:48 -0700 Subject: [PATCH 13/27] Simplifying --- .../FSharp.Editor/Common/IFSharpVisualStudioService.fs | 9 ++------- .../src/FSharp.Editor/LanguageService/LanguageService.fs | 4 ++-- vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs | 8 +++----- .../tests/UnitTests/VisualFSharp.UnitTests.fsproj | 4 ++-- 4 files changed, 9 insertions(+), 16 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/Common/IFSharpVisualStudioService.fs b/vsintegration/src/FSharp.Editor/Common/IFSharpVisualStudioService.fs index 8ab8daa567f..f0f08fd0f17 100644 --- a/vsintegration/src/FSharp.Editor/Common/IFSharpVisualStudioService.fs +++ b/vsintegration/src/FSharp.Editor/Common/IFSharpVisualStudioService.fs @@ -11,19 +11,14 @@ type internal IFSharpVisualStudioService = abstract Workspace: Workspace - abstract ServiceProvider: IServiceProvider - [] [)>] type internal FSharpVisualStudioService [] ( - [)>] workspace, - [)>] serviceProvider + [)>] workspace ) = interface IFSharpVisualStudioService with - member _.Workspace = workspace - - member _.ServiceProvider = serviceProvider \ No newline at end of file + member _.Workspace = workspace \ No newline at end of file diff --git a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs index 3a85c5e8e4f..892cd39a699 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs @@ -21,14 +21,14 @@ open Microsoft.VisualStudio.Shell open Microsoft.VisualStudio.Shell.Interop open Microsoft.VisualStudio.Text.Outlining open Microsoft.CodeAnalysis.ExternalAccess.FSharp -open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Diagnostics +open Microsoft.CodeAnalysis.Host open Microsoft.CodeAnalysis.Host.Mef // Used to expose FSharpChecker/ProjectInfo manager to diagnostic providers // Diagnostic providers can be executed in environment that does not use MEF so they can rely only // on services exposed by the workspace type internal IFSharpWorkspaceService = - inherit Microsoft.CodeAnalysis.Host.IWorkspaceService + inherit IWorkspaceService abstract Checker: FSharpChecker abstract FSharpProjectOptionsManager: FSharpProjectOptionsManager diff --git a/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs b/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs index 14a79a5e5d2..e78fd95d98d 100644 --- a/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs +++ b/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs @@ -1,4 +1,4 @@ -namespace Microsoft.VisualStudio.FSharp.Editor.Tests.Roslyn +namespace rec Microsoft.VisualStudio.FSharp.Editor.Tests.Roslyn open System open System.IO @@ -142,15 +142,13 @@ type TestHostLanguageServices(workspaceServices: HostWorkspaceServices, language [); Composition.Shared>] type internal MockFSharpVisualStudioService() = - static let mockWorkspace = new AdhocWorkspace() + static let workspace = new AdhocWorkspace() static let instance = MockFSharpVisualStudioService() static member Instance = instance interface IFSharpVisualStudioService with - member _.Workspace = mockWorkspace :> Workspace - - member _.ServiceProvider = null + member _.Workspace = workspace :> Workspace [); Composition.Shared>] type internal MockFSharpWorkspaceService() = diff --git a/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj b/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj index baa2c21ae98..63c7fe2f60b 100644 --- a/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj +++ b/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj @@ -131,7 +131,7 @@ - + From 97a3300bdce6a7de9d7fd8c0dcdcb9ff1644461a Mon Sep 17 00:00:00 2001 From: Will Smith Date: Tue, 22 Jun 2021 12:31:17 -0700 Subject: [PATCH 14/27] Removing all importing of FSharpProjectOptionsManager. Removed mocks as we do not need them anymore. --- .../CodeFix/AddMissingFunKeyword.fs | 6 +- .../AddMissingRecToMutuallyRecFunctions.fs | 5 +- .../CodeFix/AddOpenCodeFixProvider.fs | 2 +- .../ImplementInterfaceCodeFixProvider.fs | 2 +- .../Commands/HelpContextService.fs | 3 +- .../Common/IFSharpVisualStudioService.fs | 24 ------ .../Completion/CompletionService.fs | 4 +- .../Debugging/LanguageDebugInfoService.fs | 4 +- .../src/FSharp.Editor/FSharp.Editor.fsproj | 2 - .../Formatting/BraceMatchingService.fs | 6 +- .../Formatting/EditorFormattingService.fs | 8 +- .../Formatting/IndentationService.fs | 4 +- .../LanguageService/FSharpCheckerProvider.fs | 69 ---------------- .../FSharpProjectOptionsManager.fs | 38 ++++----- .../LanguageService/LanguageService.fs | 80 +++++++++++++++++-- .../LanguageService/SymbolHelpers.fs | 2 +- .../LanguageService/WorkspaceExtensions.fs | 35 ++++---- .../tests/UnitTests/Tests.RoslynHelpers.fs | 31 +------ 18 files changed, 128 insertions(+), 197 deletions(-) delete mode 100644 vsintegration/src/FSharp.Editor/Common/IFSharpVisualStudioService.fs delete mode 100644 vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerProvider.fs diff --git a/vsintegration/src/FSharp.Editor/CodeFix/AddMissingFunKeyword.fs b/vsintegration/src/FSharp.Editor/CodeFix/AddMissingFunKeyword.fs index f188e25b9eb..22c02520bcc 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/AddMissingFunKeyword.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/AddMissingFunKeyword.fs @@ -15,12 +15,9 @@ open FSharp.Compiler.CodeAnalysis type internal FSharpAddMissingFunKeywordCodeFixProvider [] ( - projectInfoManager: FSharpProjectOptionsManager ) = inherit CodeFixProvider() - static let userOpName = "AddMissingFunKeyword" - let fixableDiagnosticIds = set ["FS0010"] override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds @@ -34,8 +31,7 @@ type internal FSharpAddMissingFunKeywordCodeFixProvider // Only trigger when failing to parse `->`, which arises when `fun` is missing do! Option.guard (textOfError = "->") - let! parsingOptions, _ = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, context.CancellationToken, userOpName) - let defines = CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions + let! defines = document.Project.GetFSharpCompilationDefinesAsync() |> liftAsync let adjustedPosition = let rec loop ch pos = diff --git a/vsintegration/src/FSharp.Editor/CodeFix/AddMissingRecToMutuallyRecFunctions.fs b/vsintegration/src/FSharp.Editor/CodeFix/AddMissingRecToMutuallyRecFunctions.fs index 62c569dfaf0..ddfe2a4304a 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/AddMissingRecToMutuallyRecFunctions.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/AddMissingRecToMutuallyRecFunctions.fs @@ -20,11 +20,9 @@ open Microsoft.CodeAnalysis.CodeActions type internal FSharpAddMissingRecToMutuallyRecFunctionsCodeFixProvider [] ( - projectInfoManager: FSharpProjectOptionsManager ) = inherit CodeFixProvider() - static let userOpName = "AddMissingRecToMutuallyRecFunctions" let fixableDiagnosticIds = set ["FS0576"] let createCodeFix (context: CodeFixContext, symbolName: string, titleFormat: string, textChange: TextChange, diagnostics: ImmutableArray) = @@ -45,9 +43,8 @@ type internal FSharpAddMissingRecToMutuallyRecFunctionsCodeFixProvider override _.RegisterCodeFixesAsync context = asyncMaybe { + let! defines = context.Document.Project.GetFSharpCompilationDefinesAsync() |> liftAsync let! sourceText = context.Document.GetTextAsync(context.CancellationToken) - let! parsingOptions, _ = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(context.Document, context.CancellationToken, userOpName) - let defines = CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions let funcStartPos = let rec loop ch pos = diff --git a/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs b/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs index 3abcae6ba1b..cf37e0e7f36 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs @@ -89,7 +89,7 @@ type internal FSharpAddOpenCodeFixProvider let! parseResults, checkResults = document.GetFSharpParseAndCheckResultsAsync() |> liftAsync let line = sourceText.Lines.GetLineFromPosition(context.Span.End) let linePos = sourceText.Lines.GetLinePosition(context.Span.End) - let! defines = document.Project.GetFSharpProjectDefinesAsync() |> liftAsync + let! defines = document.Project.GetFSharpCompilationDefinesAsync() |> liftAsync let! symbol = maybe { diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs b/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs index 8bbad41ac1c..cc976b08ad7 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs @@ -142,7 +142,7 @@ type internal FSharpImplementInterfaceCodeFixProvider let cancellationToken = context.CancellationToken let! sourceText = context.Document.GetTextAsync(cancellationToken) let textLine = sourceText.Lines.GetLineFromPosition context.Span.Start - let! _, _, parsingOptions, _ = context.Document.Project.GetFSharpProjectOptionsAsync() |> liftAsync + let! _, _, parsingOptions, _ = context.Document.Project.GetFSharpCompilationOptionsAsync() |> liftAsync let defines = CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions // Notice that context.Span doesn't return reliable ranges to find tokens at exact positions. // That's why we tokenize the line and try to find the last successive identifier token diff --git a/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs b/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs index 16562620157..cb2ba6049ac 100644 --- a/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs +++ b/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs @@ -20,7 +20,6 @@ open Microsoft.CodeAnalysis type internal FSharpHelpContextService [] ( - projectInfoManager: FSharpProjectOptionsManager ) = static member GetHelpTerm(document: Document, span: TextSpan, tokens: List) : Async = @@ -100,7 +99,7 @@ type internal FSharpHelpContextService member this.GetHelpTermAsync(document, textSpan, cancellationToken) = asyncMaybe { let! sourceText = document.GetTextAsync(cancellationToken) - let defines = projectInfoManager.GetCompilationDefinesForEditingDocument(document) + let defines = document.GetFSharpSyntaxDefines() let textLine = sourceText.Lines.GetLineFromPosition(textSpan.Start) let classifiedSpans = Tokenizer.getClassifiedSpans(document.Id, sourceText, textLine.Span, Some document.Name, defines, cancellationToken) return! FSharpHelpContextService.GetHelpTerm(document, textSpan, classifiedSpans) diff --git a/vsintegration/src/FSharp.Editor/Common/IFSharpVisualStudioService.fs b/vsintegration/src/FSharp.Editor/Common/IFSharpVisualStudioService.fs deleted file mode 100644 index f0f08fd0f17..00000000000 --- a/vsintegration/src/FSharp.Editor/Common/IFSharpVisualStudioService.fs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. - -namespace rec Microsoft.VisualStudio.FSharp.Editor - -open System -open Microsoft.CodeAnalysis -open Microsoft.VisualStudio.LanguageServices -open Microsoft.VisualStudio.Shell - -type internal IFSharpVisualStudioService = - - abstract Workspace: Workspace - -[] -[)>] -type internal FSharpVisualStudioService - [] - ( - [)>] workspace - ) = - - interface IFSharpVisualStudioService with - - member _.Workspace = workspace \ No newline at end of file diff --git a/vsintegration/src/FSharp.Editor/Completion/CompletionService.fs b/vsintegration/src/FSharp.Editor/Completion/CompletionService.fs index 6550e25a951..1846a6283ea 100644 --- a/vsintegration/src/FSharp.Editor/Completion/CompletionService.fs +++ b/vsintegration/src/FSharp.Editor/Completion/CompletionService.fs @@ -55,12 +55,12 @@ type internal FSharpCompletionServiceFactory [] ( serviceProvider: SVsServiceProvider, - projectInfoManager: FSharpProjectOptionsManager, + workspaceService: IFSharpWorkspaceService, assemblyContentProvider: AssemblyContentProvider, settings: EditorOptions ) = interface ILanguageServiceFactory with member _.CreateLanguageService(hostLanguageServices: HostLanguageServices) : ILanguageService = - upcast new FSharpCompletionService(hostLanguageServices.WorkspaceServices.Workspace, serviceProvider, projectInfoManager, assemblyContentProvider, settings) + upcast new FSharpCompletionService(hostLanguageServices.WorkspaceServices.Workspace, serviceProvider, workspaceService.FSharpProjectOptionsManager, assemblyContentProvider, settings) diff --git a/vsintegration/src/FSharp.Editor/Debugging/LanguageDebugInfoService.fs b/vsintegration/src/FSharp.Editor/Debugging/LanguageDebugInfoService.fs index f633b7d54d5..82c239bf727 100644 --- a/vsintegration/src/FSharp.Editor/Debugging/LanguageDebugInfoService.fs +++ b/vsintegration/src/FSharp.Editor/Debugging/LanguageDebugInfoService.fs @@ -15,7 +15,7 @@ open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Editor.Implementation.Debugging [)>] -type internal FSharpLanguageDebugInfoService [](projectInfoManager: FSharpProjectOptionsManager) = +type internal FSharpLanguageDebugInfoService []() = static member GetDataTipInformation(sourceText: SourceText, position: int, tokens: List): TextSpan option = let tokenIndex = tokens |> Seq.tryFindIndex(fun t -> t.TextSpan.Contains(position)) @@ -49,7 +49,7 @@ type internal FSharpLanguageDebugInfoService [](projectInf member this.GetDataTipInfoAsync(document: Document, position: int, cancellationToken: CancellationToken): Task = async { - let defines = projectInfoManager.GetCompilationDefinesForEditingDocument(document) + let defines = document.GetFSharpSyntaxDefines() let! cancellationToken = Async.CancellationToken let! sourceText = document.GetTextAsync(cancellationToken) |> Async.AwaitTask let textSpan = TextSpan.FromBounds(0, sourceText.Length) diff --git a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj index 9bca90a9ccd..aff4b4c25d8 100644 --- a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj +++ b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj @@ -34,7 +34,6 @@ - @@ -46,7 +45,6 @@ - diff --git a/vsintegration/src/FSharp.Editor/Formatting/BraceMatchingService.fs b/vsintegration/src/FSharp.Editor/Formatting/BraceMatchingService.fs index 531e8c22e4e..a1bad7fbca0 100644 --- a/vsintegration/src/FSharp.Editor/Formatting/BraceMatchingService.fs +++ b/vsintegration/src/FSharp.Editor/Formatting/BraceMatchingService.fs @@ -12,8 +12,6 @@ open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Editor type internal FSharpBraceMatchingService [] ( - checkerProvider: FSharpCheckerProvider, - projectInfoManager: FSharpProjectOptionsManager ) = static let userOpName = "BraceMatching" @@ -36,9 +34,9 @@ type internal FSharpBraceMatchingService interface IFSharpBraceMatcher with member this.FindBracesAsync(document, position, cancellationToken) = asyncMaybe { - let! parsingOptions, _options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, cancellationToken, userOpName) + let! checker, _, parsingOptions, _ = document.Project.GetFSharpCompilationOptionsAsync() |> liftAsync let! sourceText = document.GetTextAsync(cancellationToken) - let! (left, right) = FSharpBraceMatchingService.GetBraceMatchingResult(checkerProvider.Checker, sourceText, document.Name, parsingOptions, position, userOpName) + let! (left, right) = FSharpBraceMatchingService.GetBraceMatchingResult(checker, sourceText, document.Name, parsingOptions, position, userOpName) let! leftSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, left) let! rightSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, right) return FSharpBraceMatchingResult(leftSpan, rightSpan) diff --git a/vsintegration/src/FSharp.Editor/Formatting/EditorFormattingService.fs b/vsintegration/src/FSharp.Editor/Formatting/EditorFormattingService.fs index 04f21aa306d..b35d122d04b 100644 --- a/vsintegration/src/FSharp.Editor/Formatting/EditorFormattingService.fs +++ b/vsintegration/src/FSharp.Editor/Formatting/EditorFormattingService.fs @@ -20,8 +20,6 @@ open System.Windows.Forms type internal FSharpEditorFormattingService [] ( - checkerProvider: FSharpCheckerProvider, - projectInfoManager: FSharpProjectOptionsManager, settings: EditorOptions ) = @@ -153,8 +151,8 @@ type internal FSharpEditorFormattingService let! sourceText = document.GetTextAsync(cancellationToken) |> Async.AwaitTask let! options = document.GetOptionsAsync(cancellationToken) |> Async.AwaitTask let indentStyle = options.GetOption(FormattingOptions.SmartIndent, FSharpConstants.FSharpLanguageName) - let parsingOptions = projectInfoManager.TryGetQuickParsingOptionsForEditingDocumentOrProject(document) - let! textChange = FSharpEditorFormattingService.GetFormattingChanges(document.Id, sourceText, document.FilePath, checkerProvider.Checker, indentStyle, parsingOptions, position) + let parsingOptions = document.GetFSharpQuickParsingOptions() + let! textChange = FSharpEditorFormattingService.GetFormattingChanges(document.Id, sourceText, document.FilePath, document.GetFSharpChecker(), indentStyle, parsingOptions, position) return textChange |> Option.toList |> toIList } @@ -164,7 +162,7 @@ type internal FSharpEditorFormattingService let! options = document.GetOptionsAsync(cancellationToken) |> Async.AwaitTask let tabSize = options.GetOption(FormattingOptions.TabSize, FSharpConstants.FSharpLanguageName) - let parsingOptions = projectInfoManager.TryGetQuickParsingOptionsForEditingDocumentOrProject(document) + let parsingOptions = document.GetFSharpQuickParsingOptions() let! textChanges = FSharpEditorFormattingService.GetPasteChanges(document.Id, sourceText, document.FilePath, settings.Formatting, tabSize, parsingOptions, currentClipboard, span) return textChanges |> Option.defaultValue Seq.empty |> toIList } diff --git a/vsintegration/src/FSharp.Editor/Formatting/IndentationService.fs b/vsintegration/src/FSharp.Editor/Formatting/IndentationService.fs index c70c38b991f..b540e06caf4 100644 --- a/vsintegration/src/FSharp.Editor/Formatting/IndentationService.fs +++ b/vsintegration/src/FSharp.Editor/Formatting/IndentationService.fs @@ -21,7 +21,7 @@ open FSharp.Compiler.Tokenization [)>] type internal FSharpIndentationService [] - (projectInfoManager: FSharpProjectOptionsManager) = + () = static member IsSmartIndentEnabled (options: Microsoft.CodeAnalysis.Options.OptionSet) = options.GetOption(FormattingOptions.SmartIndent, FSharpConstants.FSharpLanguageName) = FormattingOptions.IndentStyle.Smart @@ -101,7 +101,7 @@ type internal FSharpIndentationService let! options = document.GetOptionsAsync(cancellationToken) |> Async.AwaitTask let tabSize = options.GetOption(FormattingOptions.TabSize, FSharpConstants.FSharpLanguageName) let indentStyle = options.GetOption(FormattingOptions.SmartIndent, FSharpConstants.FSharpLanguageName) - let parsingOptions = projectInfoManager.TryGetQuickParsingOptionsForEditingDocumentOrProject(document) + let parsingOptions = document.GetFSharpQuickParsingOptions() let indent = FSharpIndentationService.GetDesiredIndentation(document.Id, sourceText, document.FilePath, lineNumber, tabSize, indentStyle, parsingOptions) return match indent with diff --git a/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerProvider.fs b/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerProvider.fs deleted file mode 100644 index bc845341bf5..00000000000 --- a/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerProvider.fs +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. - -namespace Microsoft.VisualStudio.FSharp.Editor - -open System -open System.ComponentModel.Composition -open System.Diagnostics -open FSharp.Compiler.CodeAnalysis -open FSharp.Compiler.CodeAnalysis -open FSharp.NativeInterop -open Microsoft.CodeAnalysis -open Microsoft.VisualStudio -open Microsoft.VisualStudio.FSharp.Editor -open Microsoft.VisualStudio.LanguageServices -open Microsoft.VisualStudio.LanguageServices.ProjectSystem -open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Diagnostics - -#nowarn "9" // NativePtr.toNativeInt - -// Exposes FSharpChecker as MEF export -[); Composition.Shared>] -type internal FSharpCheckerProvider - [] - ( - fsVsService: IFSharpVisualStudioService - ) = - - let tryGetMetadataSnapshot (path, timeStamp) = - match fsVsService.Workspace with - | :? VisualStudioWorkspace as workspace -> - try - let md = Microsoft.CodeAnalysis.ExternalAccess.FSharp.LanguageServices.FSharpVisualStudioWorkspaceExtensions.GetMetadata(workspace, path, timeStamp) - let amd = (md :?> AssemblyMetadata) - let mmd = amd.GetModules().[0] - let mmr = mmd.GetMetadataReader() - - // "lifetime is timed to Metadata you got from the GetMetadata(...). As long as you hold it strongly, raw - // memory we got from metadata reader will be alive. Once you are done, just let everything go and - // let finalizer handle resource rather than calling Dispose from Metadata directly. It is shared metadata. - // You shouldn't dispose it directly." - - let objToHold = box md - - // We don't expect any ilread WeakByteFile to be created when working in Visual Studio - // Debug.Assert((FSharp.Compiler.AbstractIL.ILBinaryReader.GetStatistics().weakByteFileCount = 0), "Expected weakByteFileCount to be zero when using F# in Visual Studio. Was there a problem reading a .NET binary?") - - Some (objToHold, NativePtr.toNativeInt mmr.MetadataPointer, mmr.MetadataLength) - with ex -> - // We catch all and let the backup routines in the F# compiler find the error - Assert.Exception(ex) - None - | _ -> - None - - let checker = - lazy - let checker = - FSharpChecker.Create( - projectCacheSize = 5000, // We do not care how big the cache is. VS will actually tell FCS to clear caches, so this is fine. - keepAllBackgroundResolutions = false, - legacyReferenceResolver=LegacyMSBuildReferenceResolver.getResolver(), - tryGetMetadataSnapshot = tryGetMetadataSnapshot, - keepAllBackgroundSymbolUses = false, - enableBackgroundItemKeyStoreAndSemanticClassification = true, - enablePartialTypeChecking = true) - checker - - member this.Checker = checker.Value - diff --git a/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs b/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs index 06c23d3778b..0c9482f9790 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs @@ -99,7 +99,7 @@ type private FSharpProjectOptionsMessage = | ClearSingleFileOptionsCache of DocumentId [] -type private FSharpProjectOptionsReactor (checkerProvider: FSharpCheckerProvider) = +type private FSharpProjectOptionsReactor (checker: FSharpChecker) = let cancellationTokenSource = new CancellationTokenSource() // Hack to store command line options from HandleCommandLineChanges @@ -171,7 +171,7 @@ type private FSharpProjectOptionsReactor (checkerProvider: FSharpCheckerProvider let! sourceText = document.GetTextAsync(ct) |> Async.AwaitTask let! scriptProjectOptions, _ = - checkerProvider.Checker.GetProjectOptionsFromScript(document.FilePath, + checker.GetProjectOptionsFromScript(document.FilePath, sourceText.ToFSharpSourceText(), SessionsProperties.fsiPreview, assumeDotNetFramework=not SessionsProperties.fsiUseNetCore, @@ -209,7 +209,7 @@ type private FSharpProjectOptionsReactor (checkerProvider: FSharpCheckerProvider Stamp = Some(int64 (fileStamp.GetHashCode())) } - let parsingOptions, _ = checkerProvider.Checker.GetParsingOptionsFromProjectOptions(projectOptions) + let parsingOptions, _ = checker.GetParsingOptionsFromProjectOptions(projectOptions) singleFileCache.[document.Id] <- (fileStamp, parsingOptions, projectOptions) @@ -316,7 +316,7 @@ type private FSharpProjectOptionsReactor (checkerProvider: FSharpCheckerProvider |> Seq.map (fun pair -> let _, _, projectOptions = pair.Value projectOptions) - checkerProvider.Checker.ClearCache(options, userOpName = "tryComputeOptions") + checker.ClearCache(options, userOpName = "tryComputeOptions") lastSuccessfulCompilations.ToArray() |> Array.iter (fun pair -> @@ -324,9 +324,9 @@ type private FSharpProjectOptionsReactor (checkerProvider: FSharpCheckerProvider lastSuccessfulCompilations.TryRemove(pair.Key) |> ignore ) - checkerProvider.Checker.InvalidateConfiguration(projectOptions, userOpName = "tryComputeOptions") + checker.InvalidateConfiguration(projectOptions, userOpName = "tryComputeOptions") - let parsingOptions, _ = checkerProvider.Checker.GetParsingOptionsFromProjectOptions(projectOptions) + let parsingOptions, _ = checker.GetParsingOptionsFromProjectOptions(projectOptions) cache.[projectId] <- (project, parsingOptions, projectOptions) @@ -401,7 +401,7 @@ type private FSharpProjectOptionsReactor (checkerProvider: FSharpCheckerProvider match cache.TryRemove(projectId) with | true, (_, _, projectOptions) -> lastSuccessfulCompilations.TryRemove(projectId) |> ignore - checkerProvider.Checker.ClearCache([projectOptions]) + checker.ClearCache([projectOptions]) | _ -> () legacyProjectSites.TryRemove(projectId) |> ignore @@ -409,7 +409,7 @@ type private FSharpProjectOptionsReactor (checkerProvider: FSharpCheckerProvider match singleFileCache.TryRemove(documentId) with | true, (_, _, projectOptions) -> lastSuccessfulCompilations.TryRemove(documentId.ProjectId) |> ignore - checkerProvider.Checker.ClearCache([projectOptions]) + checker.ClearCache([projectOptions]) | _ -> () } @@ -452,34 +452,28 @@ type private FSharpProjectOptionsReactor (checkerProvider: FSharpCheckerProvider cancellationTokenSource.Dispose() (agent :> IDisposable).Dispose() -/// Exposes FCS FSharpProjectOptions information management as MEF component. -// -// This service allows analyzers to get an appropriate FSharpProjectOptions value for a project or single file. -// It also allows a 'cheaper' route to get the project options relevant to parsing (e.g. the #define values). -// The main entrypoints are TryGetOptionsForDocumentOrProject and TryGetOptionsForEditingDocumentOrProject. -[); Composition.Shared>] +/// Manages mappings of Roslyn workspace Projects/Documents to FCS. type internal FSharpProjectOptionsManager - [] ( - checkerProvider: FSharpCheckerProvider, - fsVsService: IFSharpVisualStudioService + checker: FSharpChecker, + workspace: Workspace ) = let projectDisplayNameOf projectFileName = if String.IsNullOrWhiteSpace projectFileName then projectFileName else Path.GetFileNameWithoutExtension projectFileName - let reactor = new FSharpProjectOptionsReactor(checkerProvider) + let reactor = new FSharpProjectOptionsReactor(checker) do // We need to listen to this event for lifecycle purposes. - fsVsService.Workspace.WorkspaceChanged.Add(fun args -> + workspace.WorkspaceChanged.Add(fun args -> match args.Kind with | WorkspaceChangeKind.ProjectRemoved -> reactor.ClearOptionsByProjectId(args.ProjectId) | _ -> () ) - fsVsService.Workspace.DocumentClosed.Add(fun args -> + workspace.DocumentClosed.Add(fun args -> let doc = args.Document let proj = doc.Project if proj.IsFSharp && proj.IsFSharpMiscellaneousOrMetadata then @@ -546,7 +540,7 @@ type internal FSharpProjectOptionsManager member _.HandleCommandLineChanges(path:string, sources:ImmutableArray, _references:ImmutableArray, options:ImmutableArray) = use _logBlock = Logger.LogBlock(LogEditorFunctionId.LanguageService_HandleCommandLineArgs) - match fsVsService.Workspace with + match workspace with | :? VisualStudioWorkspace as workspace -> let projectId = match Microsoft.CodeAnalysis.ExternalAccess.FSharp.LanguageServices.FSharpVisualStudioWorkspaceExtensions.TryGetProjectIdByBinPath(workspace, path) with @@ -566,4 +560,4 @@ type internal FSharpProjectOptionsManager | _ -> () - member _.Checker = checkerProvider.Checker + member _.Checker = checker diff --git a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs index 892cd39a699..ddc38e31d1a 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs @@ -10,6 +10,7 @@ open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.Options open FSharp.Compiler open FSharp.Compiler.CodeAnalysis +open FSharp.NativeInterop open Microsoft.VisualStudio open Microsoft.VisualStudio.Editor open Microsoft.VisualStudio.FSharp.Editor @@ -24,6 +25,8 @@ open Microsoft.CodeAnalysis.ExternalAccess.FSharp open Microsoft.CodeAnalysis.Host open Microsoft.CodeAnalysis.Host.Mef +#nowarn "9" // NativePtr.toNativeInt + // Used to expose FSharpChecker/ProjectInfo manager to diagnostic providers // Diagnostic providers can be executed in environment that does not use MEF so they can rely only // on services exposed by the workspace @@ -48,14 +51,79 @@ type internal RoamingProfileStorageLocation(keyName: string) = type internal FSharpWorkspaceServiceFactory [] ( - checkerProvider: FSharpCheckerProvider, - projectInfoManager: FSharpProjectOptionsManager ) = + + + static let gate = obj() + + // We only ever want to have a single FSharpChecker. + static let mutable checkerSingleton = None + interface IWorkspaceServiceFactory with - member _.CreateService(_workspaceServices) = + member _.CreateService(workspaceServices) = + + let workspace = workspaceServices.Workspace + + let tryGetMetadataSnapshot (path, timeStamp) = + match workspace with + | :? VisualStudioWorkspace as workspace -> + try + let md = Microsoft.CodeAnalysis.ExternalAccess.FSharp.LanguageServices.FSharpVisualStudioWorkspaceExtensions.GetMetadata(workspace, path, timeStamp) + let amd = (md :?> AssemblyMetadata) + let mmd = amd.GetModules().[0] + let mmr = mmd.GetMetadataReader() + + // "lifetime is timed to Metadata you got from the GetMetadata(...). As long as you hold it strongly, raw + // memory we got from metadata reader will be alive. Once you are done, just let everything go and + // let finalizer handle resource rather than calling Dispose from Metadata directly. It is shared metadata. + // You shouldn't dispose it directly." + + let objToHold = box md + + // We don't expect any ilread WeakByteFile to be created when working in Visual Studio + // Debug.Assert((FSharp.Compiler.AbstractIL.ILBinaryReader.GetStatistics().weakByteFileCount = 0), "Expected weakByteFileCount to be zero when using F# in Visual Studio. Was there a problem reading a .NET binary?") + + Some (objToHold, NativePtr.toNativeInt mmr.MetadataPointer, mmr.MetadataLength) + with ex -> + // We catch all and let the backup routines in the F# compiler find the error + Assert.Exception(ex) + None + | _ -> + None + + lock gate (fun () -> + match checkerSingleton with + | Some _ -> () + | _ -> + let checker = + lazy + let checker = + FSharpChecker.Create( + projectCacheSize = 5000, // We do not care how big the cache is. VS will actually tell FCS to clear caches, so this is fine. + keepAllBackgroundResolutions = false, + legacyReferenceResolver=LegacyMSBuildReferenceResolver.getResolver(), + tryGetMetadataSnapshot = tryGetMetadataSnapshot, + keepAllBackgroundSymbolUses = false, + enableBackgroundItemKeyStoreAndSemanticClassification = true, + enablePartialTypeChecking = true) + checker + checkerSingleton <- Some checker + ) + + let optionsManager = + lazy + match checkerSingleton with + | Some checker -> + FSharpProjectOptionsManager(checker.Value, workspaceServices.Workspace) + | _ -> + failwith "Checker not set." + upcast { new IFSharpWorkspaceService with - member _.Checker = checkerProvider.Checker - member _.FSharpProjectOptionsManager = projectInfoManager } + member _.Checker = + match checkerSingleton with + | Some checker -> checker.Value + | _ -> failwith "Checker not set." + member _.FSharpProjectOptionsManager = optionsManager.Value } [] type private FSharpSolutionEvents(projectManager: FSharpProjectOptionsManager, metadataAsSource: FSharpMetadataAsSourceService) = @@ -177,7 +245,7 @@ type internal FSharpPackage() as this = // FSI-LINKAGE-POINT: private method GetDialogPage forces fsi options to be loaded let _fsiPropertyPage = this.GetDialogPage(typeof) - let projectInfoManager = this.ComponentModel.DefaultExportProvider.GetExport().Value + let projectInfoManager = this.ComponentModel.DefaultExportProvider.GetExport().Value.FSharpProjectOptionsManager let metadataAsSource = this.ComponentModel.DefaultExportProvider.GetExport().Value let solution = this.GetServiceAsync(typeof).Result let solution = solution :?> IVsSolution diff --git a/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs b/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs index 06cc961d73e..f08b98fb197 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs @@ -21,7 +21,7 @@ module internal SymbolHelpers = let getSymbolUsesOfSymbolAtLocationInDocument (document: Document, position: int) = asyncMaybe { let! _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync() |> liftAsync - let! defines = document.Project.GetFSharpProjectDefinesAsync() |> liftAsync + let! defines = document.Project.GetFSharpCompilationDefinesAsync() |> liftAsync let! cancellationToken = Async.CancellationToken |> liftAsync let! sourceText = document.GetTextAsync(cancellationToken) diff --git a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs index 29390cf9db2..c273b39a295 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs @@ -104,18 +104,18 @@ module private ProjectCache = type Solution with - member private this.GetFSharpService() = + member private this.GetFSharpWorkspaceService() = this.Workspace.Services.GetRequiredService() type Project with - member this.GetFSharpProjectOptionsAsync() = + member this.GetFSharpCompilationOptionsAsync() = async { if this.IsFSharp then match ProjectCache.Projects.TryGetValue(this) with | true, result -> return result | _ -> - let service = this.Solution.GetFSharpService() + let service = this.Solution.GetFSharpWorkspaceService() let projectOptionsManager = service.FSharpProjectOptionsManager let! ct = Async.CancellationToken match! projectOptionsManager.TryGetOptionsByProject(this, ct) with @@ -128,30 +128,35 @@ type Project with return raise(System.OperationCanceledException("Project is not a FSharp project.")) } - member this.GetFSharpProjectDefinesAsync() = + member this.GetFSharpCompilationDefinesAsync() = async { - let! _, _, parsingOptions, _ = this.GetFSharpProjectOptionsAsync() + let! _, _, parsingOptions, _ = this.GetFSharpCompilationOptionsAsync() return CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions } type Document with + member this.GetFSharpChecker() = + let workspaceService = this.Project.Solution.GetFSharpWorkspaceService() + workspaceService.Checker + + member this.GetFSharpQuickParsingOptions() = + let workspaceService = this.Project.Solution.GetFSharpWorkspaceService() + workspaceService.FSharpProjectOptionsManager.TryGetQuickParsingOptionsForEditingDocumentOrProject(this) + member this.GetFSharpSyntaxDefines() = - if this.Project.IsFSharp then - let service = this.Project.Solution.GetFSharpService() - service.FSharpProjectOptionsManager.GetCompilationDefinesForEditingDocument(this) - else - [] + let workspaceService = this.Project.Solution.GetFSharpWorkspaceService() + workspaceService.FSharpProjectOptionsManager.GetCompilationDefinesForEditingDocument(this) member this.GetFSharpParseResultsAsync() = async { - let! checker, _, parsingOptions, _ = this.Project.GetFSharpProjectOptionsAsync() + let! checker, _, parsingOptions, _ = this.Project.GetFSharpCompilationOptionsAsync() return! checker.ParseDocument(this, parsingOptions, nameof(this.GetFSharpParseResultsAsync)) } member this.GetFSharpParseAndCheckResultsAsync() = async { - let! checker, _, _, projectOptions = this.Project.GetFSharpProjectOptionsAsync() + let! checker, _, _, projectOptions = this.Project.GetFSharpCompilationOptionsAsync() match! checker.ParseAndCheckDocument(this, projectOptions, nameof(this.GetFSharpParseAndCheckResultsAsync), allowStaleResults = false) with | Some(parseResults, _, checkResults) -> return (parseResults, checkResults) @@ -161,7 +166,7 @@ type Document with member this.GetFSharpSemanticClassificationAsync() = async { - let! checker, _, _, projectOptions = this.Project.GetFSharpProjectOptionsAsync() + let! checker, _, _, projectOptions = this.Project.GetFSharpCompilationOptionsAsync() match! checker.GetBackgroundSemanticClassificationForFile(this.FilePath, projectOptions) with | Some results -> return results | _ -> return raise(System.OperationCanceledException("Unable to get FSharp semantic classification.")) @@ -169,7 +174,7 @@ type Document with member this.FindFSharpReferencesAsync(symbol, onFound) = async { - let! checker, _, _, projectOptions = this.Project.GetFSharpProjectOptionsAsync() + let! checker, _, _, projectOptions = this.Project.GetFSharpCompilationOptionsAsync() let! symbolUses = checker.FindBackgroundReferencesInFile(this.FilePath, projectOptions, symbol, canInvalidateProject = false) let! ct = Async.CancellationToken let! sourceText = this.GetTextAsync ct |> Async.AwaitTask @@ -183,7 +188,7 @@ type Document with member this.TryFindFSharpLexerSymbolAsync(position, lookupKind, wholeActivePattern, allowStringToken) = async { - let! defines = this.Project.GetFSharpProjectDefinesAsync() + let! defines = this.Project.GetFSharpCompilationDefinesAsync() let! ct = Async.CancellationToken let! sourceText = this.GetTextAsync(ct) |> Async.AwaitTask return Tokenizer.getSymbolAtPosition(this.Id, sourceText, position, this.FilePath, defines, lookupKind, wholeActivePattern, allowStringToken) diff --git a/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs b/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs index e78fd95d98d..d3b3568578a 100644 --- a/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs +++ b/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs @@ -139,32 +139,6 @@ type TestHostLanguageServices(workspaceServices: HostWorkspaceServices, language | _ -> Unchecked.defaultof<'T> -[); Composition.Shared>] -type internal MockFSharpVisualStudioService() = - - static let workspace = new AdhocWorkspace() - static let instance = MockFSharpVisualStudioService() - static member Instance = instance - - interface IFSharpVisualStudioService with - - member _.Workspace = workspace :> Workspace - -[); Composition.Shared>] -type internal MockFSharpWorkspaceService() = - - static let checkerProvider = FSharpCheckerProvider(MockFSharpVisualStudioService.Instance) - static let manager = FSharpProjectOptionsManager(checkerProvider, MockFSharpVisualStudioService.Instance) - - static let instance = MockFSharpWorkspaceService() - static member Instance = instance - - interface IFSharpWorkspaceService with - - member _.Checker = checkerProvider.Checker - - member _.FSharpProjectOptionsManager = manager - type TestHostWorkspaceServices(hostServices: HostServices, workspace: Workspace) as this = inherit HostWorkspaceServices() @@ -220,9 +194,6 @@ type TestHostWorkspaceServices(hostServices: HostServices, workspace: Workspace) type TestHostServices() = inherit HostServices() - static let instance = TestHostServices() - static member Instance = instance - override this.CreateWorkspaceServices(workspace) = TestHostWorkspaceServices(this, workspace) :> HostWorkspaceServices @@ -232,7 +203,7 @@ type RoslynTestHelpers private () = static member CreateDocument (filePath, text: SourceText) = let isScript = String.Equals(Path.GetExtension(filePath), ".fsx", StringComparison.OrdinalIgnoreCase) - let workspace = new AdhocWorkspace(TestHostServices.Instance) + let workspace = new AdhocWorkspace(TestHostServices()) let projId = ProjectId.CreateNewId() let docId = DocumentId.CreateNewId(projId) From f0dfcd12daa9dd3f6845af4cbd133ac4f2c0f9eb Mon Sep 17 00:00:00 2001 From: Will Smith Date: Tue, 22 Jun 2021 12:32:21 -0700 Subject: [PATCH 15/27] Uncomment --- vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj b/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj index 63c7fe2f60b..baa2c21ae98 100644 --- a/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj +++ b/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj @@ -131,7 +131,7 @@ - + From efd7e753332b32a12facf71c30833112bc46f2ee Mon Sep 17 00:00:00 2001 From: Will Smith Date: Tue, 22 Jun 2021 12:54:49 -0700 Subject: [PATCH 16/27] Trying to fix build --- .../LanguageService/LanguageService.fs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs index ddc38e31d1a..9f2b153ffd1 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs @@ -46,14 +46,13 @@ type internal RoamingProfileStorageLocation(keyName: string) = let substituteLanguageName = if languageName = FSharpConstants.FSharpLanguageName then "FSharp" else languageName unsubstitutedKeyName.Replace("%LANGUAGE%", substituteLanguageName) -[] +[] [, ServiceLayer.Default)>] type internal FSharpWorkspaceServiceFactory - [] + [] ( ) = - static let gate = obj() // We only ever want to have a single FSharpChecker. @@ -245,11 +244,13 @@ type internal FSharpPackage() as this = // FSI-LINKAGE-POINT: private method GetDialogPage forces fsi options to be loaded let _fsiPropertyPage = this.GetDialogPage(typeof) - let projectInfoManager = this.ComponentModel.DefaultExportProvider.GetExport().Value.FSharpProjectOptionsManager + + let workspace = this.ComponentModel.GetService() + let optionsManager = workspace.Services.GetService().FSharpProjectOptionsManager let metadataAsSource = this.ComponentModel.DefaultExportProvider.GetExport().Value let solution = this.GetServiceAsync(typeof).Result let solution = solution :?> IVsSolution - let solutionEvents = FSharpSolutionEvents(projectInfoManager, metadataAsSource) + let solutionEvents = FSharpSolutionEvents(optionsManager, metadataAsSource) let rdt = this.GetServiceAsync(typeof).Result let rdt = rdt :?> IVsRunningDocumentTable @@ -263,10 +264,10 @@ type internal FSharpPackage() as this = new SingleFileWorkspaceMap( workspace, miscFilesWorkspace, - projectInfoManager, + optionsManager, projectContextFactory, rdt) - let _legacyProjectWorkspaceMap = new LegacyProjectWorkspaceMap(solution, projectInfoManager, projectContextFactory) + let _legacyProjectWorkspaceMap = new LegacyProjectWorkspaceMap(solution, optionsManager, projectContextFactory) () let awaiter = this.JoinableTaskFactory.SwitchToMainThreadAsync().GetAwaiter() if awaiter.IsCompleted then From 1bb9cbc3cc2b8684f4869af5bfb6357143996779 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Tue, 22 Jun 2021 13:12:12 -0700 Subject: [PATCH 17/27] Some refactoring --- .../FSharpProjectOptionsManager.fs | 39 ------------------- .../LanguageService/LanguageService.fs | 37 ++++++++++++++++++ 2 files changed, 37 insertions(+), 39 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs b/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs index 0c9482f9790..57b4a0dae9f 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs @@ -6,21 +6,13 @@ open System open System.Collections.Generic open System.Collections.Concurrent open System.Collections.Immutable -open System.ComponentModel.Composition open System.IO open System.Linq open Microsoft.CodeAnalysis open FSharp.Compiler open FSharp.Compiler.CodeAnalysis -open Microsoft.VisualStudio open Microsoft.VisualStudio.FSharp.Editor -open Microsoft.VisualStudio.LanguageServices -open Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem -open Microsoft.VisualStudio.Shell open System.Threading -open Microsoft.VisualStudio.Shell.Interop -open Microsoft.VisualStudio.LanguageServices.Implementation.TaskList -open Microsoft.CodeAnalysis.ExternalAccess.FSharp.LanguageServices open Microsoft.VisualStudio.FSharp.Interactive.Session open System.Runtime.CompilerServices @@ -459,10 +451,6 @@ type internal FSharpProjectOptionsManager workspace: Workspace ) = - let projectDisplayNameOf projectFileName = - if String.IsNullOrWhiteSpace projectFileName then projectFileName - else Path.GetFileNameWithoutExtension projectFileName - let reactor = new FSharpProjectOptionsReactor(checker) do @@ -533,31 +521,4 @@ type internal FSharpProjectOptionsManager member this.ClearAllCaches() = reactor.ClearAllCaches() - [] - /// This handles commandline change notifications from the Dotnet Project-system - /// Prior to VS 15.7 path contained path to project file, post 15.7 contains target binpath - /// binpath is more accurate because a project file can have multiple in memory projects based on configuration - member _.HandleCommandLineChanges(path:string, sources:ImmutableArray, _references:ImmutableArray, options:ImmutableArray) = - use _logBlock = Logger.LogBlock(LogEditorFunctionId.LanguageService_HandleCommandLineArgs) - - match workspace with - | :? VisualStudioWorkspace as workspace -> - let projectId = - match Microsoft.CodeAnalysis.ExternalAccess.FSharp.LanguageServices.FSharpVisualStudioWorkspaceExtensions.TryGetProjectIdByBinPath(workspace, path) with - | true, projectId -> projectId - | false, _ -> Microsoft.CodeAnalysis.ExternalAccess.FSharp.LanguageServices.FSharpVisualStudioWorkspaceExtensions.GetOrCreateProjectIdForPath(workspace, path, projectDisplayNameOf path) - let path = Microsoft.CodeAnalysis.ExternalAccess.FSharp.LanguageServices.FSharpVisualStudioWorkspaceExtensions.GetProjectFilePath(workspace, projectId) - - let getFullPath p = - let p' = - if Path.IsPathRooted(p) || path = null then p - else Path.Combine(Path.GetDirectoryName(path), p) - Path.GetFullPathSafe(p') - - let sourcePaths = sources |> Seq.map(fun s -> getFullPath s.Path) |> Seq.toArray - - reactor.SetCpsCommandLineOptions(projectId, sourcePaths, options.ToArray()) - | _ -> - () - member _.Checker = checker diff --git a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs index 9f2b153ffd1..ad800d90229 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs @@ -6,6 +6,8 @@ open System open System.ComponentModel.Design open System.Runtime.InteropServices open System.Threading +open System.IO +open System.Collections.Immutable open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.Options open FSharp.Compiler @@ -323,3 +325,38 @@ type internal FSharpLanguageService(package : FSharpPackage) = if not (isNull outliningManager) then let settings = this.Workspace.Services.GetService() outliningManager.Enabled <- settings.Advanced.IsOutliningEnabled + +[] +[)>] +type HackCpsCommandLineChanges + ( + [)>] workspace: VisualStudioWorkspace + ) = + + static let projectDisplayNameOf projectFileName = + if String.IsNullOrWhiteSpace projectFileName then projectFileName + else Path.GetFileNameWithoutExtension projectFileName + + [] + /// This handles commandline change notifications from the Dotnet Project-system + /// Prior to VS 15.7 path contained path to project file, post 15.7 contains target binpath + /// binpath is more accurate because a project file can have multiple in memory projects based on configuration + member _.HandleCommandLineChanges(path:string, sources:ImmutableArray, _references:ImmutableArray, options:ImmutableArray) = + use _logBlock = Logger.LogBlock(LogEditorFunctionId.LanguageService_HandleCommandLineArgs) + + let projectId = + match Microsoft.CodeAnalysis.ExternalAccess.FSharp.LanguageServices.FSharpVisualStudioWorkspaceExtensions.TryGetProjectIdByBinPath(workspace, path) with + | true, projectId -> projectId + | false, _ -> Microsoft.CodeAnalysis.ExternalAccess.FSharp.LanguageServices.FSharpVisualStudioWorkspaceExtensions.GetOrCreateProjectIdForPath(workspace, path, projectDisplayNameOf path) + let path = Microsoft.CodeAnalysis.ExternalAccess.FSharp.LanguageServices.FSharpVisualStudioWorkspaceExtensions.GetProjectFilePath(workspace, projectId) + + let getFullPath p = + let p' = + if Path.IsPathRooted(p) || path = null then p + else Path.Combine(Path.GetDirectoryName(path), p) + Path.GetFullPathSafe(p') + + let sourcePaths = sources |> Seq.map(fun s -> getFullPath s.Path) |> Seq.toArray + + let workspaceService = workspace.Services.GetRequiredService() + workspaceService.FSharpProjectOptionsManager.SetCommandLineOptions(projectId, sourcePaths, options) From 524fa8da7a2bfadc61d143cfacadd074e0b24bb7 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Tue, 22 Jun 2021 13:31:54 -0700 Subject: [PATCH 18/27] Make this internal --- .../src/FSharp.Editor/LanguageService/LanguageService.fs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs index ad800d90229..53e98915caf 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs @@ -247,7 +247,9 @@ type internal FSharpPackage() as this = // FSI-LINKAGE-POINT: private method GetDialogPage forces fsi options to be loaded let _fsiPropertyPage = this.GetDialogPage(typeof) + let workspace = this.ComponentModel.GetService() + let _ = this.ComponentModel.DefaultExportProvider.GetExport() let optionsManager = workspace.Services.GetService().FSharpProjectOptionsManager let metadataAsSource = this.ComponentModel.DefaultExportProvider.GetExport().Value let solution = this.GetServiceAsync(typeof).Result @@ -260,7 +262,6 @@ type internal FSharpPackage() as this = solution.AdviseSolutionEvents(solutionEvents) |> ignore let projectContextFactory = this.ComponentModel.GetService() - let workspace = this.ComponentModel.GetService() let miscFilesWorkspace = this.ComponentModel.GetService() let _singleFileWorkspaceMap = new SingleFileWorkspaceMap( @@ -328,7 +329,7 @@ type internal FSharpLanguageService(package : FSharpPackage) = [] [)>] -type HackCpsCommandLineChanges +type internal HackCpsCommandLineChanges ( [)>] workspace: VisualStudioWorkspace ) = From afce9ede66f0fbc70ac7a991854729b89b77b31d Mon Sep 17 00:00:00 2001 From: Will Smith Date: Tue, 22 Jun 2021 13:57:43 -0700 Subject: [PATCH 19/27] Forgot the importing constructor --- .../src/FSharp.Editor/LanguageService/LanguageService.fs | 1 + 1 file changed, 1 insertion(+) diff --git a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs index 53e98915caf..33580b72c4b 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs @@ -330,6 +330,7 @@ type internal FSharpLanguageService(package : FSharpPackage) = [] [)>] type internal HackCpsCommandLineChanges + [] ( [)>] workspace: VisualStudioWorkspace ) = From 27f44bebf1f79690577de9036a8c5188f20f6241 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Tue, 22 Jun 2021 14:21:48 -0700 Subject: [PATCH 20/27] More refactoring --- .../CodeFix/AddMissingFunKeyword.fs | 2 +- .../AddMissingRecToMutuallyRecFunctions.fs | 2 +- .../CodeFix/AddOpenCodeFixProvider.fs | 2 +- .../ImplementInterfaceCodeFixProvider.fs | 2 +- .../Formatting/BraceMatchingService.fs | 2 +- .../FSharpProjectOptionsManager.fs | 16 +++++------ .../LanguageService/SymbolHelpers.fs | 2 +- .../LanguageService/WorkspaceExtensions.fs | 28 +++++++++---------- 8 files changed, 27 insertions(+), 29 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/AddMissingFunKeyword.fs b/vsintegration/src/FSharp.Editor/CodeFix/AddMissingFunKeyword.fs index 22c02520bcc..16b4fc27a50 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/AddMissingFunKeyword.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/AddMissingFunKeyword.fs @@ -31,7 +31,7 @@ type internal FSharpAddMissingFunKeywordCodeFixProvider // Only trigger when failing to parse `->`, which arises when `fun` is missing do! Option.guard (textOfError = "->") - let! defines = document.Project.GetFSharpCompilationDefinesAsync() |> liftAsync + let! defines = document.GetFSharpCompilationDefinesAsync() |> liftAsync let adjustedPosition = let rec loop ch pos = diff --git a/vsintegration/src/FSharp.Editor/CodeFix/AddMissingRecToMutuallyRecFunctions.fs b/vsintegration/src/FSharp.Editor/CodeFix/AddMissingRecToMutuallyRecFunctions.fs index ddfe2a4304a..7f7a623a8a8 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/AddMissingRecToMutuallyRecFunctions.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/AddMissingRecToMutuallyRecFunctions.fs @@ -43,7 +43,7 @@ type internal FSharpAddMissingRecToMutuallyRecFunctionsCodeFixProvider override _.RegisterCodeFixesAsync context = asyncMaybe { - let! defines = context.Document.Project.GetFSharpCompilationDefinesAsync() |> liftAsync + let! defines = context.Document.GetFSharpCompilationDefinesAsync() |> liftAsync let! sourceText = context.Document.GetTextAsync(context.CancellationToken) let funcStartPos = diff --git a/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs b/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs index cf37e0e7f36..e4bc4e05682 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs @@ -89,7 +89,7 @@ type internal FSharpAddOpenCodeFixProvider let! parseResults, checkResults = document.GetFSharpParseAndCheckResultsAsync() |> liftAsync let line = sourceText.Lines.GetLineFromPosition(context.Span.End) let linePos = sourceText.Lines.GetLinePosition(context.Span.End) - let! defines = document.Project.GetFSharpCompilationDefinesAsync() |> liftAsync + let! defines = document.GetFSharpCompilationDefinesAsync() |> liftAsync let! symbol = maybe { diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs b/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs index cc976b08ad7..f0976a139bc 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs @@ -142,7 +142,7 @@ type internal FSharpImplementInterfaceCodeFixProvider let cancellationToken = context.CancellationToken let! sourceText = context.Document.GetTextAsync(cancellationToken) let textLine = sourceText.Lines.GetLineFromPosition context.Span.Start - let! _, _, parsingOptions, _ = context.Document.Project.GetFSharpCompilationOptionsAsync() |> liftAsync + let! _, _, parsingOptions, _ = context.Document.GetFSharpCompilationOptionsAsync() |> liftAsync let defines = CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions // Notice that context.Span doesn't return reliable ranges to find tokens at exact positions. // That's why we tokenize the line and try to find the last successive identifier token diff --git a/vsintegration/src/FSharp.Editor/Formatting/BraceMatchingService.fs b/vsintegration/src/FSharp.Editor/Formatting/BraceMatchingService.fs index a1bad7fbca0..b57edf93ab3 100644 --- a/vsintegration/src/FSharp.Editor/Formatting/BraceMatchingService.fs +++ b/vsintegration/src/FSharp.Editor/Formatting/BraceMatchingService.fs @@ -34,7 +34,7 @@ type internal FSharpBraceMatchingService interface IFSharpBraceMatcher with member this.FindBracesAsync(document, position, cancellationToken) = asyncMaybe { - let! checker, _, parsingOptions, _ = document.Project.GetFSharpCompilationOptionsAsync() |> liftAsync + let! checker, _, parsingOptions, _ = document.GetFSharpCompilationOptionsAsync() |> liftAsync let! sourceText = document.GetTextAsync(cancellationToken) let! (left, right) = FSharpBraceMatchingService.GetBraceMatchingResult(checker, sourceText, document.Name, parsingOptions, position, userOpName) let! leftSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, left) diff --git a/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs b/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs index 57b4a0dae9f..e7f29de8684 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs @@ -94,8 +94,8 @@ type private FSharpProjectOptionsMessage = type private FSharpProjectOptionsReactor (checker: FSharpChecker) = let cancellationTokenSource = new CancellationTokenSource() - // Hack to store command line options from HandleCommandLineChanges - let cpsCommandLineOptions = ConcurrentDictionary() + // Store command line options + let commandLineOptions = ConcurrentDictionary() let legacyProjectSites = ConcurrentDictionary() @@ -217,8 +217,8 @@ type private FSharpProjectOptionsReactor (checker: FSharpChecker) = let tryGetProjectSite (project: Project) = // Cps - if cpsCommandLineOptions.ContainsKey project.Id then - Some (mapCpsProjectToSite(project, cpsCommandLineOptions)) + if commandLineOptions.ContainsKey project.Id then + Some (mapCpsProjectToSite(project, commandLineOptions)) else // Legacy match legacyProjectSites.TryGetValue project.Id with @@ -420,8 +420,8 @@ type private FSharpProjectOptionsReactor (checker: FSharpChecker) = member _.ClearSingleFileOptionsCache(documentId) = agent.Post(FSharpProjectOptionsMessage.ClearSingleFileOptionsCache(documentId)) - member _.SetCpsCommandLineOptions(projectId, sourcePaths, options) = - cpsCommandLineOptions.[projectId] <- (sourcePaths, options) + member _.SetCommandLineOptions(projectId, sourcePaths, options) = + commandLineOptions.[projectId] <- (sourcePaths, options) member _.SetLegacyProjectSite (projectId, projectSite) = legacyProjectSites.[projectId] <- projectSite @@ -432,7 +432,7 @@ type private FSharpProjectOptionsReactor (checker: FSharpChecker) = | _ -> None member _.ClearAllCaches() = - cpsCommandLineOptions.Clear() + commandLineOptions.Clear() legacyProjectSites.Clear() cache.Clear() singleFileCache.Clear() @@ -516,7 +516,7 @@ type internal FSharpProjectOptionsManager | _ -> { FSharpParsingOptions.Default with IsInteractive = CompilerEnvironment.IsScriptFile document.Name } member this.SetCommandLineOptions(projectId, sourcePaths, options: ImmutableArray) = - reactor.SetCpsCommandLineOptions(projectId, sourcePaths, options.ToArray()) + reactor.SetCommandLineOptions(projectId, sourcePaths, options.ToArray()) member this.ClearAllCaches() = reactor.ClearAllCaches() diff --git a/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs b/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs index f08b98fb197..d0747ca3aef 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs @@ -21,7 +21,7 @@ module internal SymbolHelpers = let getSymbolUsesOfSymbolAtLocationInDocument (document: Document, position: int) = asyncMaybe { let! _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync() |> liftAsync - let! defines = document.Project.GetFSharpCompilationDefinesAsync() |> liftAsync + let! defines = document.GetFSharpCompilationDefinesAsync() |> liftAsync let! cancellationToken = Async.CancellationToken |> liftAsync let! sourceText = document.GetTextAsync(cancellationToken) diff --git a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs index c273b39a295..80f079996ad 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs @@ -107,25 +107,25 @@ type Solution with member private this.GetFSharpWorkspaceService() = this.Workspace.Services.GetRequiredService() -type Project with +type Document with member this.GetFSharpCompilationOptionsAsync() = async { - if this.IsFSharp then - match ProjectCache.Projects.TryGetValue(this) with + if this.Project.IsFSharp then + match ProjectCache.Projects.TryGetValue(this.Project) with | true, result -> return result | _ -> - let service = this.Solution.GetFSharpWorkspaceService() + let service = this.Project.Solution.GetFSharpWorkspaceService() let projectOptionsManager = service.FSharpProjectOptionsManager let! ct = Async.CancellationToken - match! projectOptionsManager.TryGetOptionsByProject(this, ct) with + match! projectOptionsManager.TryGetOptionsForDocumentOrProject(this, ct, nameof(this.GetFSharpCompilationOptionsAsync)) with | None -> return raise(System.OperationCanceledException("FSharp project options not found.")) - | Some(parsingOptions, projectOptions) -> + | Some(parsingOptions, _, projectOptions) -> let result = (service.Checker, projectOptionsManager, parsingOptions, projectOptions) - ProjectCache.Projects.Add(this, result) + ProjectCache.Projects.Add(this.Project, result) return result else - return raise(System.OperationCanceledException("Project is not a FSharp project.")) + return raise(System.OperationCanceledException("Document is not a FSharp document.")) } member this.GetFSharpCompilationDefinesAsync() = @@ -134,8 +134,6 @@ type Project with return CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions } -type Document with - member this.GetFSharpChecker() = let workspaceService = this.Project.Solution.GetFSharpWorkspaceService() workspaceService.Checker @@ -150,13 +148,13 @@ type Document with member this.GetFSharpParseResultsAsync() = async { - let! checker, _, parsingOptions, _ = this.Project.GetFSharpCompilationOptionsAsync() + let! checker, _, parsingOptions, _ = this.GetFSharpCompilationOptionsAsync() return! checker.ParseDocument(this, parsingOptions, nameof(this.GetFSharpParseResultsAsync)) } member this.GetFSharpParseAndCheckResultsAsync() = async { - let! checker, _, _, projectOptions = this.Project.GetFSharpCompilationOptionsAsync() + let! checker, _, _, projectOptions = this.GetFSharpCompilationOptionsAsync() match! checker.ParseAndCheckDocument(this, projectOptions, nameof(this.GetFSharpParseAndCheckResultsAsync), allowStaleResults = false) with | Some(parseResults, _, checkResults) -> return (parseResults, checkResults) @@ -166,7 +164,7 @@ type Document with member this.GetFSharpSemanticClassificationAsync() = async { - let! checker, _, _, projectOptions = this.Project.GetFSharpCompilationOptionsAsync() + let! checker, _, _, projectOptions = this.GetFSharpCompilationOptionsAsync() match! checker.GetBackgroundSemanticClassificationForFile(this.FilePath, projectOptions) with | Some results -> return results | _ -> return raise(System.OperationCanceledException("Unable to get FSharp semantic classification.")) @@ -174,7 +172,7 @@ type Document with member this.FindFSharpReferencesAsync(symbol, onFound) = async { - let! checker, _, _, projectOptions = this.Project.GetFSharpCompilationOptionsAsync() + let! checker, _, _, projectOptions = this.GetFSharpCompilationOptionsAsync() let! symbolUses = checker.FindBackgroundReferencesInFile(this.FilePath, projectOptions, symbol, canInvalidateProject = false) let! ct = Async.CancellationToken let! sourceText = this.GetTextAsync ct |> Async.AwaitTask @@ -188,7 +186,7 @@ type Document with member this.TryFindFSharpLexerSymbolAsync(position, lookupKind, wholeActivePattern, allowStringToken) = async { - let! defines = this.Project.GetFSharpCompilationDefinesAsync() + let! defines = this.GetFSharpCompilationDefinesAsync() let! ct = Async.CancellationToken let! sourceText = this.GetTextAsync(ct) |> Async.AwaitTask return Tokenizer.getSymbolAtPosition(this.Id, sourceText, position, this.FilePath, defines, lookupKind, wholeActivePattern, allowStringToken) From bc96749cfb706f762c41c72b55c9c3080a238a8e Mon Sep 17 00:00:00 2001 From: Will Smith Date: Tue, 22 Jun 2021 14:51:10 -0700 Subject: [PATCH 21/27] Fixed completion service --- .../src/FSharp.Editor/Completion/CompletionService.fs | 6 +++--- .../FSharp.Editor/LanguageService/WorkspaceExtensions.fs | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/Completion/CompletionService.fs b/vsintegration/src/FSharp.Editor/Completion/CompletionService.fs index 1846a6283ea..ccdadf504f7 100644 --- a/vsintegration/src/FSharp.Editor/Completion/CompletionService.fs +++ b/vsintegration/src/FSharp.Editor/Completion/CompletionService.fs @@ -17,12 +17,13 @@ type internal FSharpCompletionService ( workspace: Workspace, serviceProvider: SVsServiceProvider, - projectInfoManager: FSharpProjectOptionsManager, assemblyContentProvider: AssemblyContentProvider, settings: EditorOptions ) = inherit CompletionServiceWithProviders(workspace) + let projectInfoManager = workspace.Services.GetRequiredService().FSharpProjectOptionsManager + let builtInProviders = ImmutableArray.Create( FSharpCompletionProvider(workspace, serviceProvider, assemblyContentProvider), @@ -55,12 +56,11 @@ type internal FSharpCompletionServiceFactory [] ( serviceProvider: SVsServiceProvider, - workspaceService: IFSharpWorkspaceService, assemblyContentProvider: AssemblyContentProvider, settings: EditorOptions ) = interface ILanguageServiceFactory with member _.CreateLanguageService(hostLanguageServices: HostLanguageServices) : ILanguageService = - upcast new FSharpCompletionService(hostLanguageServices.WorkspaceServices.Workspace, serviceProvider, workspaceService.FSharpProjectOptionsManager, assemblyContentProvider, settings) + upcast new FSharpCompletionService(hostLanguageServices.WorkspaceServices.Workspace, serviceProvider, assemblyContentProvider, settings) diff --git a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs index 80f079996ad..5e6dd0dfd19 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs @@ -122,8 +122,7 @@ type Document with | None -> return raise(System.OperationCanceledException("FSharp project options not found.")) | Some(parsingOptions, _, projectOptions) -> let result = (service.Checker, projectOptionsManager, parsingOptions, projectOptions) - ProjectCache.Projects.Add(this.Project, result) - return result + return ProjectCache.Projects.GetValue(this.Project, Runtime.CompilerServices.ConditionalWeakTable<_,_>.CreateValueCallback(fun _ -> result)) else return raise(System.OperationCanceledException("Document is not a FSharp document.")) } From ec59ec03e1f2253b53e8fa8909145eb5bbed048a Mon Sep 17 00:00:00 2001 From: Will Smith Date: Tue, 22 Jun 2021 15:25:23 -0700 Subject: [PATCH 22/27] fixing tests --- .../tests/GetTypesVS.UnitTests/GetTypesVS.UnitTests.fsproj | 1 + vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/vsintegration/tests/GetTypesVS.UnitTests/GetTypesVS.UnitTests.fsproj b/vsintegration/tests/GetTypesVS.UnitTests/GetTypesVS.UnitTests.fsproj index 911e5e9a944..b8714ee2dad 100644 --- a/vsintegration/tests/GetTypesVS.UnitTests/GetTypesVS.UnitTests.fsproj +++ b/vsintegration/tests/GetTypesVS.UnitTests/GetTypesVS.UnitTests.fsproj @@ -23,6 +23,7 @@ + diff --git a/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs b/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs index d3b3568578a..c786f1ad4fd 100644 --- a/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs +++ b/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs @@ -216,14 +216,16 @@ type RoslynTestHelpers private () = filePath=filePath, sourceCodeKind= if isScript then SourceCodeKind.Script else SourceCodeKind.Regular) + let projFilePath = "C:\\test.fsproj" let projInfo = ProjectInfo.Create( projId, VersionStamp.Create(DateTime.UtcNow), - "test.fsproj", + projFilePath, "test.dll", LanguageNames.FSharp, - documents = [docInfo] + documents = [docInfo], + filePath = projFilePath ) let solutionInfo = SolutionInfo.Create(SolutionId.CreateNewId(), VersionStamp.Create(DateTime.UtcNow), "test.sln", [projInfo]) From 21053215902021f764d3c5c2d0374d4c69cc74fd Mon Sep 17 00:00:00 2001 From: Will Smith Date: Tue, 22 Jun 2021 16:02:44 -0700 Subject: [PATCH 23/27] Trying to fix tests --- .../LanguageService/WorkspaceExtensions.fs | 9 +++++++++ .../GetTypesVS.UnitTests.fsproj | 2 +- .../tests/UnitTests/SignatureHelpProviderTests.fs | 4 ++-- .../tests/UnitTests/Tests.RoslynHelpers.fs | 14 +++++++++++--- 4 files changed, 23 insertions(+), 6 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs index 5e6dd0dfd19..701de619c03 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs @@ -3,6 +3,7 @@ module internal Microsoft.VisualStudio.FSharp.Editor.WorkspaceExtensions open System open System.Runtime.CompilerServices +open System.Threading open Microsoft.CodeAnalysis open FSharp.Compiler open FSharp.Compiler.CodeAnalysis @@ -191,6 +192,14 @@ type Document with return Tokenizer.getSymbolAtPosition(this.Id, sourceText, position, this.FilePath, defines, lookupKind, wholeActivePattern, allowStringToken) } + member this.SetFSharpProjectOptionsForTesting(projectOptions: FSharpProjectOptions) = + let workspaceService = this.Project.Solution.GetFSharpWorkspaceService() + let parsingOptions, _, _ = + workspaceService.FSharpProjectOptionsManager.TryGetOptionsForDocumentOrProject(this, CancellationToken.None, nameof(this.SetFSharpProjectOptionsForTesting)) + |> Async.RunSynchronously + |> Option.get + ProjectCache.Projects.Add(this.Project, (workspaceService.Checker, workspaceService.FSharpProjectOptionsManager, parsingOptions, projectOptions)) + type Project with member this.FindFSharpReferencesAsync(symbol, onFound) = diff --git a/vsintegration/tests/GetTypesVS.UnitTests/GetTypesVS.UnitTests.fsproj b/vsintegration/tests/GetTypesVS.UnitTests/GetTypesVS.UnitTests.fsproj index b8714ee2dad..4fa4549e645 100644 --- a/vsintegration/tests/GetTypesVS.UnitTests/GetTypesVS.UnitTests.fsproj +++ b/vsintegration/tests/GetTypesVS.UnitTests/GetTypesVS.UnitTests.fsproj @@ -23,7 +23,7 @@ - + diff --git a/vsintegration/tests/UnitTests/SignatureHelpProviderTests.fs b/vsintegration/tests/UnitTests/SignatureHelpProviderTests.fs index 796ea2c4a9f..35024c4b8c2 100644 --- a/vsintegration/tests/UnitTests/SignatureHelpProviderTests.fs +++ b/vsintegration/tests/UnitTests/SignatureHelpProviderTests.fs @@ -45,7 +45,7 @@ let private DefaultDocumentationProvider = override doc.AppendDocumentation(_, _, _, _, _, _, _) = () } -let GetSignatureHelp (_project:FSharpProject) (fileName:string) (caretPosition:int) = +let GetSignatureHelp (project:FSharpProject) (fileName:string) (caretPosition:int) = async { let triggerChar = None let fileContents = File.ReadAllText(fileName) @@ -54,7 +54,7 @@ let GetSignatureHelp (_project:FSharpProject) (fileName:string) (caretPosition:i let caretLinePos = textLines.GetLinePosition(caretPosition) let caretLineColumn = caretLinePos.Character - let document = RoslynTestHelpers.CreateDocument(fileName, sourceText) + let document = RoslynTestHelpers.CreateDocument(fileName, sourceText, options = project.Options) let parseResults, checkFileResults = document.GetFSharpParseAndCheckResultsAsync() |> Async.RunSynchronously diff --git a/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs b/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs index c786f1ad4fd..5859e11acff 100644 --- a/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs +++ b/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs @@ -200,7 +200,7 @@ type TestHostServices() = [] type RoslynTestHelpers private () = - static member CreateDocument (filePath, text: SourceText) = + static member CreateDocument (filePath, text: SourceText, ?options: FSharp.Compiler.CodeAnalysis.FSharpProjectOptions) = let isScript = String.Equals(Path.GetExtension(filePath), ".fsx", StringComparison.OrdinalIgnoreCase) let workspace = new AdhocWorkspace(TestHostServices()) @@ -233,9 +233,17 @@ type RoslynTestHelpers private () = let solution = workspace.AddSolution(solutionInfo) let workspaceService = workspace.Services.GetService() - workspaceService.FSharpProjectOptionsManager.SetCommandLineOptions(projId, [|filePath|], ImmutableArray.Empty) - solution.GetProject(projId).GetDocument(docId) + let document = solution.GetProject(projId).GetDocument(docId) + + match options with + | Some options -> + workspaceService.FSharpProjectOptionsManager.SetCommandLineOptions(projId, options.SourceFiles, options.OtherOptions |> ImmutableArray.CreateRange) + document.SetFSharpProjectOptionsForTesting(options) + | _ -> + workspaceService.FSharpProjectOptionsManager.SetCommandLineOptions(projId, [|filePath|], ImmutableArray.Empty) + + document static member CreateDocument (filePath, code: string) = let text = SourceText.From(code) From 69b923a7cc53fd655184ecde0141c55943c85668 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Tue, 22 Jun 2021 16:09:17 -0700 Subject: [PATCH 24/27] Another test fix --- vsintegration/tests/UnitTests/FsxCompletionProviderTests.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vsintegration/tests/UnitTests/FsxCompletionProviderTests.fs b/vsintegration/tests/UnitTests/FsxCompletionProviderTests.fs index 0fabf20b3fd..7441639316f 100644 --- a/vsintegration/tests/UnitTests/FsxCompletionProviderTests.fs +++ b/vsintegration/tests/UnitTests/FsxCompletionProviderTests.fs @@ -56,7 +56,7 @@ type Worker () = member _.VerifyCompletionListExactly(fileContents: string, marker: string, expected: List) = let caretPosition = fileContents.IndexOf(marker) + marker.Length - let document, _ = RoslynTestHelpers.CreateDocument(filePath, fileContents) + let document = RoslynTestHelpers.CreateDocument(filePath, SourceText.From(fileContents), options = projectOptions) let expected = expected |> Seq.toList let actual = let x = FSharpCompletionProvider.ProvideCompletionsAsyncAux(document, caretPosition, (fun _ -> []), IntelliSenseOptions.Default) From 59f1fd787945c2089579862ef7ca72548fb591df Mon Sep 17 00:00:00 2001 From: Will Smith Date: Tue, 22 Jun 2021 16:15:36 -0700 Subject: [PATCH 25/27] More cleanup --- vsintegration/tests/UnitTests/SignatureHelpProviderTests.fs | 1 - vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/vsintegration/tests/UnitTests/SignatureHelpProviderTests.fs b/vsintegration/tests/UnitTests/SignatureHelpProviderTests.fs index 35024c4b8c2..2cc7b327206 100644 --- a/vsintegration/tests/UnitTests/SignatureHelpProviderTests.fs +++ b/vsintegration/tests/UnitTests/SignatureHelpProviderTests.fs @@ -100,7 +100,6 @@ let assertSignatureHelpForMethodCalls (fileContents: string) (marker: string) (e let textLines = sourceText.Lines let caretLinePos = textLines.GetLinePosition(caretPosition) let caretLineColumn = caretLinePos.Character - let perfOptions = LanguageServicePerformanceOptions.Default let document = RoslynTestHelpers.CreateDocument(filePath, sourceText) let parseResults, checkFileResults = diff --git a/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs b/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs index 5859e11acff..7481753ec30 100644 --- a/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs +++ b/vsintegration/tests/UnitTests/Tests.RoslynHelpers.fs @@ -238,6 +238,7 @@ type RoslynTestHelpers private () = match options with | Some options -> + let options = { options with ProjectId = Some(Guid.NewGuid().ToString()) } workspaceService.FSharpProjectOptionsManager.SetCommandLineOptions(projId, options.SourceFiles, options.OtherOptions |> ImmutableArray.CreateRange) document.SetFSharpProjectOptionsForTesting(options) | _ -> From 4bd16d6f7e9995eca81e4b5d4239f5cc269be66a Mon Sep 17 00:00:00 2001 From: Will Smith Date: Tue, 22 Jun 2021 16:30:30 -0700 Subject: [PATCH 26/27] fixing tests --- vsintegration/tests/UnitTests/SignatureHelpProviderTests.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vsintegration/tests/UnitTests/SignatureHelpProviderTests.fs b/vsintegration/tests/UnitTests/SignatureHelpProviderTests.fs index 2cc7b327206..f995d0742a0 100644 --- a/vsintegration/tests/UnitTests/SignatureHelpProviderTests.fs +++ b/vsintegration/tests/UnitTests/SignatureHelpProviderTests.fs @@ -101,7 +101,7 @@ let assertSignatureHelpForMethodCalls (fileContents: string) (marker: string) (e let caretLinePos = textLines.GetLinePosition(caretPosition) let caretLineColumn = caretLinePos.Character - let document = RoslynTestHelpers.CreateDocument(filePath, sourceText) + let document = RoslynTestHelpers.CreateDocument(filePath, sourceText, options = projectOptions) let parseResults, checkFileResults = document.GetFSharpParseAndCheckResultsAsync() |> Async.RunSynchronously From 2050e327636eeaa12865acc0ee087b2df78201c4 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Wed, 23 Jun 2021 13:01:29 -0700 Subject: [PATCH 27/27] Feedback changes --- .../Classification/ClassificationService.fs | 8 +- .../CodeFix/AddMissingFunKeyword.fs | 2 +- .../AddMissingRecToMutuallyRecFunctions.fs | 2 +- .../CodeFix/AddOpenCodeFixProvider.fs | 4 +- ...peAnnotationToObjectOfIndeterminateType.fs | 4 +- .../ChangeRefCellDerefToNotExpression.fs | 2 +- .../ConvertCSharpLambdaToFSharpLambda.fs | 2 +- .../CodeFix/ConvertToAnonymousRecord.fs | 2 +- .../ImplementInterfaceCodeFixProvider.fs | 4 +- .../CodeFix/MakeDeclarationMutable.fs | 4 +- .../CodeFix/MakeOuterBindingRecursive.fs | 2 +- .../CodeFix/RemoveReturnOrYield.fs | 2 +- .../CodeFix/RemoveUnusedBinding.fs | 2 +- .../CodeFix/RenameUnusedValue.fs | 4 +- .../CodeFix/ReplaceWithSuggestion.fs | 2 +- .../CodeFix/UseMutationWhenValueIsMutable.fs | 4 +- .../CodeLens/FSharpCodeLensService.fs | 2 +- .../Commands/HelpContextService.fs | 4 +- .../Commands/XmlDocCommandService.fs | 2 +- .../Completion/CompletionProvider.fs | 8 +- .../FSharp.Editor/Completion/SignatureHelp.fs | 4 +- .../Debugging/BreakpointResolutionService.fs | 2 +- .../Debugging/LanguageDebugInfoService.fs | 2 +- .../Diagnostics/DocumentDiagnosticAnalyzer.fs | 4 +- .../SimplifyNameDiagnosticAnalyzer.fs | 2 +- .../Diagnostics/UnusedDeclarationsAnalyzer.fs | 2 +- .../UnusedOpensDiagnosticAnalyzer.fs | 2 +- .../DocumentHighlightsService.fs | 4 +- .../Formatting/BraceMatchingService.fs | 6 +- .../InlineRename/InlineRenameService.fs | 4 +- .../FSharpProjectOptionsManager.fs | 4 +- .../LanguageService/LanguageService.fs | 8 +- .../LanguageService/SymbolHelpers.fs | 12 +-- .../LanguageService/WorkspaceExtensions.fs | 76 ++++++++++--------- .../Navigation/FindUsagesService.fs | 4 +- .../Navigation/GoToDefinition.fs | 16 ++-- .../Navigation/NavigateToSearchService.fs | 2 +- .../Navigation/NavigationBarItemService.fs | 2 +- .../FSharp.Editor/Options/EditorOptions.fs | 2 +- .../QuickInfo/QuickInfoProvider.fs | 14 ++-- .../Refactor/AddExplicitTypeToParameter.fs | 4 +- .../Refactor/ChangeDerefToValueRefactoring.fs | 2 +- .../ChangeTypeofWithNameToNameofExpression.fs | 2 +- .../Structure/BlockStructureService.fs | 2 +- .../UnitTests/GoToDefinitionServiceTests.fs | 2 +- .../SemanticColorizationServiceTests.fs | 2 +- .../UnitTests/SignatureHelpProviderTests.fs | 8 +- 47 files changed, 138 insertions(+), 122 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs b/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs index 3ac78d993ba..bb16634ff28 100644 --- a/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs +++ b/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs @@ -141,7 +141,7 @@ type internal FSharpClassificationService async { use _logBlock = Logger.LogBlock(LogEditorFunctionId.Classification_Syntactic) - let defines = document.GetFSharpSyntaxDefines() + let defines = document.GetFSharpQuickDefines() let! sourceText = document.GetTextAsync(cancellationToken) |> Async.AwaitTask // For closed documents, only get classification for the text within the span. @@ -154,7 +154,7 @@ type internal FSharpClassificationService } |> RoslynHelpers.StartAsyncUnitAsTask cancellationToken - member _.AddSemanticClassificationsAsync(document: Document, textSpan: TextSpan, result: List, cancellationToken: CancellationToken) = + member this.AddSemanticClassificationsAsync(document: Document, textSpan: TextSpan, result: List, cancellationToken: CancellationToken) = async { use _logBlock = Logger.LogBlock(LogEditorFunctionId.Classification_Semantic) @@ -168,12 +168,12 @@ type internal FSharpClassificationService | ValueSome classificationDataLookup -> addSemanticClassificationByLookup sourceText textSpan classificationDataLookup result | _ -> - let! classificationData = document.GetFSharpSemanticClassificationAsync() + let! classificationData = document.GetFSharpSemanticClassificationAsync(nameof(FSharpClassificationService)) let classificationDataLookup = toSemanticClassificationLookup classificationData do! semanticClassificationCache.SetAsync(document, classificationDataLookup) addSemanticClassificationByLookup sourceText textSpan classificationDataLookup result else - let! _, checkResults = document.GetFSharpParseAndCheckResultsAsync() + let! _, checkResults = document.GetFSharpParseAndCheckResultsAsync(nameof(IFSharpClassificationService)) let targetRange = RoslynHelpers.TextSpanToFSharpRange(document.FilePath, textSpan, sourceText) let classificationData = checkResults.GetSemanticClassification (Some targetRange) addSemanticClassification sourceText textSpan classificationData result diff --git a/vsintegration/src/FSharp.Editor/CodeFix/AddMissingFunKeyword.fs b/vsintegration/src/FSharp.Editor/CodeFix/AddMissingFunKeyword.fs index 16b4fc27a50..14149d507f1 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/AddMissingFunKeyword.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/AddMissingFunKeyword.fs @@ -31,7 +31,7 @@ type internal FSharpAddMissingFunKeywordCodeFixProvider // Only trigger when failing to parse `->`, which arises when `fun` is missing do! Option.guard (textOfError = "->") - let! defines = document.GetFSharpCompilationDefinesAsync() |> liftAsync + let! defines = document.GetFSharpCompilationDefinesAsync(nameof(FSharpAddMissingFunKeywordCodeFixProvider)) |> liftAsync let adjustedPosition = let rec loop ch pos = diff --git a/vsintegration/src/FSharp.Editor/CodeFix/AddMissingRecToMutuallyRecFunctions.fs b/vsintegration/src/FSharp.Editor/CodeFix/AddMissingRecToMutuallyRecFunctions.fs index 7f7a623a8a8..55b73b1fee6 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/AddMissingRecToMutuallyRecFunctions.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/AddMissingRecToMutuallyRecFunctions.fs @@ -43,7 +43,7 @@ type internal FSharpAddMissingRecToMutuallyRecFunctionsCodeFixProvider override _.RegisterCodeFixesAsync context = asyncMaybe { - let! defines = context.Document.GetFSharpCompilationDefinesAsync() |> liftAsync + let! defines = context.Document.GetFSharpCompilationDefinesAsync(nameof(FSharpAddMissingRecToMutuallyRecFunctionsCodeFixProvider)) |> liftAsync let! sourceText = context.Document.GetTextAsync(context.CancellationToken) let funcStartPos = diff --git a/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs b/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs index e4bc4e05682..392773a637a 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs @@ -86,10 +86,10 @@ type internal FSharpAddOpenCodeFixProvider let document = context.Document let! sourceText = document.GetTextAsync(context.CancellationToken) - let! parseResults, checkResults = document.GetFSharpParseAndCheckResultsAsync() |> liftAsync + let! parseResults, checkResults = document.GetFSharpParseAndCheckResultsAsync(nameof(FSharpAddOpenCodeFixProvider)) |> liftAsync let line = sourceText.Lines.GetLineFromPosition(context.Span.End) let linePos = sourceText.Lines.GetLinePosition(context.Span.End) - let! defines = document.GetFSharpCompilationDefinesAsync() |> liftAsync + let! defines = document.GetFSharpCompilationDefinesAsync(nameof(FSharpAddOpenCodeFixProvider)) |> liftAsync let! symbol = maybe { diff --git a/vsintegration/src/FSharp.Editor/CodeFix/AddTypeAnnotationToObjectOfIndeterminateType.fs b/vsintegration/src/FSharp.Editor/CodeFix/AddTypeAnnotationToObjectOfIndeterminateType.fs index 22d41b04b10..9f5c441cb83 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/AddTypeAnnotationToObjectOfIndeterminateType.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/AddTypeAnnotationToObjectOfIndeterminateType.fs @@ -41,9 +41,9 @@ type internal FSharpAddTypeAnnotationToObjectOfIndeterminateTypeFixProvider let textLine = sourceText.Lines.GetLineFromPosition position let textLinePos = sourceText.Lines.GetLinePosition position let fcsTextLineNumber = Line.fromZ textLinePos.Line - let! lexerSymbol = document.TryFindFSharpLexerSymbolAsync(position, SymbolLookupKind.Greedy, false, false) + let! lexerSymbol = document.TryFindFSharpLexerSymbolAsync(position, SymbolLookupKind.Greedy, false, false, nameof(FSharpAddTypeAnnotationToObjectOfIndeterminateTypeFixProvider)) - let! _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync() |> liftAsync + let! _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync(nameof(FSharpAddTypeAnnotationToObjectOfIndeterminateTypeFixProvider)) |> liftAsync let decl = checkFileResults.GetDeclarationLocation (fcsTextLineNumber, lexerSymbol.Ident.idRange.EndColumn, textLine.ToString(), lexerSymbol.FullIsland, false) match decl with diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ChangeRefCellDerefToNotExpression.fs b/vsintegration/src/FSharp.Editor/CodeFix/ChangeRefCellDerefToNotExpression.fs index ce93cd15087..f7f33d4c198 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ChangeRefCellDerefToNotExpression.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/ChangeRefCellDerefToNotExpression.fs @@ -22,7 +22,7 @@ type internal FSharpChangeRefCellDerefToNotExpressionCodeFixProvider override this.RegisterCodeFixesAsync context : Task = asyncMaybe { let document = context.Document - let! parseResults = document.GetFSharpParseResultsAsync() |> liftAsync + let! parseResults = document.GetFSharpParseResultsAsync(nameof(FSharpChangeRefCellDerefToNotExpressionCodeFixProvider)) |> liftAsync let! sourceText = context.Document.GetTextAsync(context.CancellationToken) let errorRange = RoslynHelpers.TextSpanToFSharpRange(document.FilePath, context.Span, sourceText) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ConvertCSharpLambdaToFSharpLambda.fs b/vsintegration/src/FSharp.Editor/CodeFix/ConvertCSharpLambdaToFSharpLambda.fs index c476b396171..824bc52e2a6 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ConvertCSharpLambdaToFSharpLambda.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/ConvertCSharpLambdaToFSharpLambda.fs @@ -20,7 +20,7 @@ type internal FSharpConvertCSharpLambdaToFSharpLambdaCodeFixProvider override _.RegisterCodeFixesAsync context = asyncMaybe { - let! parseResults = context.Document.GetFSharpParseResultsAsync() |> liftAsync + let! parseResults = context.Document.GetFSharpParseResultsAsync(nameof(FSharpConvertCSharpLambdaToFSharpLambdaCodeFixProvider)) |> liftAsync let! sourceText = context.Document.GetTextAsync(context.CancellationToken) let errorRange = RoslynHelpers.TextSpanToFSharpRange(context.Document.FilePath, context.Span, sourceText) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ConvertToAnonymousRecord.fs b/vsintegration/src/FSharp.Editor/CodeFix/ConvertToAnonymousRecord.fs index d225624891f..3250e7e33f5 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ConvertToAnonymousRecord.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/ConvertToAnonymousRecord.fs @@ -24,7 +24,7 @@ type internal FSharpConvertToAnonymousRecordCodeFixProvider override _.RegisterCodeFixesAsync context : Task = asyncMaybe { let document = context.Document - let! parseResults = document.GetFSharpParseResultsAsync() |> liftAsync + let! parseResults = document.GetFSharpParseResultsAsync(nameof(FSharpConvertToAnonymousRecordCodeFixProvider)) |> liftAsync let! sourceText = context.Document.GetTextAsync(context.CancellationToken) let errorRange = RoslynHelpers.TextSpanToFSharpRange(document.FilePath, context.Span, sourceText) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs b/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs index f0976a139bc..93d2686c5c9 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs @@ -138,11 +138,11 @@ type internal FSharpImplementInterfaceCodeFixProvider override _.RegisterCodeFixesAsync context : Task = asyncMaybe { - let! parseResults, checkFileResults = context.Document.GetFSharpParseAndCheckResultsAsync() |> liftAsync + let! parseResults, checkFileResults = context.Document.GetFSharpParseAndCheckResultsAsync(nameof(FSharpImplementInterfaceCodeFixProvider)) |> liftAsync let cancellationToken = context.CancellationToken let! sourceText = context.Document.GetTextAsync(cancellationToken) let textLine = sourceText.Lines.GetLineFromPosition context.Span.Start - let! _, _, parsingOptions, _ = context.Document.GetFSharpCompilationOptionsAsync() |> liftAsync + let! _, _, parsingOptions, _ = context.Document.GetFSharpCompilationOptionsAsync(nameof(FSharpImplementInterfaceCodeFixProvider)) |> liftAsync let defines = CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions // Notice that context.Span doesn't return reliable ranges to find tokens at exact positions. // That's why we tokenize the line and try to find the last successive identifier token diff --git a/vsintegration/src/FSharp.Editor/CodeFix/MakeDeclarationMutable.fs b/vsintegration/src/FSharp.Editor/CodeFix/MakeDeclarationMutable.fs index eede648c21d..e93e9f4db13 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/MakeDeclarationMutable.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/MakeDeclarationMutable.fs @@ -36,12 +36,12 @@ type internal FSharpMakeDeclarationMutableFixProvider do! Option.guard (not(isSignatureFile document.FilePath)) let position = context.Span.Start - let! lexerSymbol = document.TryFindFSharpLexerSymbolAsync(position, SymbolLookupKind.Greedy, false, false) + let! lexerSymbol = document.TryFindFSharpLexerSymbolAsync(position, SymbolLookupKind.Greedy, false, false, nameof(FSharpMakeDeclarationMutableFixProvider)) let! sourceText = document.GetTextAsync () |> liftTaskAsync let textLine = sourceText.Lines.GetLineFromPosition position let textLinePos = sourceText.Lines.GetLinePosition position let fcsTextLineNumber = Line.fromZ textLinePos.Line - let! parseFileResults, checkFileResults = document.GetFSharpParseAndCheckResultsAsync() |> liftAsync + let! parseFileResults, checkFileResults = document.GetFSharpParseAndCheckResultsAsync(nameof(FSharpMakeDeclarationMutableFixProvider)) |> liftAsync let decl = checkFileResults.GetDeclarationLocation (fcsTextLineNumber, lexerSymbol.Ident.idRange.EndColumn, textLine.ToString(), lexerSymbol.FullIsland, false) match decl with diff --git a/vsintegration/src/FSharp.Editor/CodeFix/MakeOuterBindingRecursive.fs b/vsintegration/src/FSharp.Editor/CodeFix/MakeOuterBindingRecursive.fs index 86ead9637b9..842de538108 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/MakeOuterBindingRecursive.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/MakeOuterBindingRecursive.fs @@ -21,7 +21,7 @@ type internal FSharpMakeOuterBindingRecursiveCodeFixProvider override _.RegisterCodeFixesAsync context = asyncMaybe { - let! parseResults = context.Document.GetFSharpParseResultsAsync() |> liftAsync + let! parseResults = context.Document.GetFSharpParseResultsAsync(nameof(FSharpMakeOuterBindingRecursiveCodeFixProvider)) |> liftAsync let! sourceText = context.Document.GetTextAsync(context.CancellationToken) let diagnosticRange = RoslynHelpers.TextSpanToFSharpRange(context.Document.FilePath, context.Span, sourceText) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/RemoveReturnOrYield.fs b/vsintegration/src/FSharp.Editor/CodeFix/RemoveReturnOrYield.fs index cec7283f4b6..863a9343e72 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/RemoveReturnOrYield.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/RemoveReturnOrYield.fs @@ -20,7 +20,7 @@ type internal FSharpRemoveReturnOrYieldCodeFixProvider override _.RegisterCodeFixesAsync context = asyncMaybe { - let! parseResults = context.Document.GetFSharpParseResultsAsync() |> liftAsync + let! parseResults = context.Document.GetFSharpParseResultsAsync(nameof(FSharpRemoveReturnOrYieldCodeFixProvider)) |> liftAsync let! sourceText = context.Document.GetTextAsync(context.CancellationToken) let errorRange = RoslynHelpers.TextSpanToFSharpRange(context.Document.FilePath, context.Span, sourceText) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedBinding.fs b/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedBinding.fs index 3ae92a56c4a..fe0a4200e47 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedBinding.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedBinding.fs @@ -29,7 +29,7 @@ type internal FSharpRemoveUnusedBindingCodeFixProvider let document = context.Document let! sourceText = document.GetTextAsync(context.CancellationToken) - let! parseResults = context.Document.GetFSharpParseResultsAsync() |> liftAsync + let! parseResults = context.Document.GetFSharpParseResultsAsync(nameof(FSharpRemoveUnusedBindingCodeFixProvider)) |> liftAsync let diagnostics = context.Diagnostics diff --git a/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs b/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs index 2678dd52d31..9f44097b25b 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/RenameUnusedValue.fs @@ -38,10 +38,10 @@ type internal FSharpRenameUnusedValueCodeFixProvider // We have to use the additional check for backtickes because `IsOperatorOrBacktickedName` operates on display names // where backtickes are replaced with parens. if not (PrettyNaming.IsOperatorOrBacktickedName ident) && not (ident.StartsWith "``") then - let! lexerSymbol = document.TryFindFSharpLexerSymbolAsync(context.Span.Start, SymbolLookupKind.Greedy, false, false) + let! lexerSymbol = document.TryFindFSharpLexerSymbolAsync(context.Span.Start, SymbolLookupKind.Greedy, false, false, nameof(FSharpRenameUnusedValueCodeFixProvider)) let m = RoslynHelpers.TextSpanToFSharpRange(document.FilePath, context.Span, sourceText) let lineText = (sourceText.Lines.GetLineFromPosition context.Span.Start).ToString() - let! _, checkResults = document.GetFSharpParseAndCheckResultsAsync() |> liftAsync + let! _, checkResults = document.GetFSharpParseAndCheckResultsAsync(nameof(FSharpRenameUnusedValueCodeFixProvider)) |> liftAsync let! symbolUse = checkResults.GetSymbolUseAtLocation(m.StartLine, m.EndColumn, lineText, lexerSymbol.FullIsland) let symbolName = symbolUse.Symbol.DisplayName diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ReplaceWithSuggestion.fs b/vsintegration/src/FSharp.Editor/CodeFix/ReplaceWithSuggestion.fs index ab56f001c06..8fb23402339 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ReplaceWithSuggestion.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/ReplaceWithSuggestion.fs @@ -30,7 +30,7 @@ type internal FSharpReplaceWithSuggestionCodeFixProvider do! Option.guard settings.CodeFixes.SuggestNamesForErrors let document = context.Document - let! parseFileResults, checkFileResults = document.GetFSharpParseAndCheckResultsAsync() |> liftAsync + let! parseFileResults, checkFileResults = document.GetFSharpParseAndCheckResultsAsync(nameof(FSharpReplaceWithSuggestionCodeFixProvider)) |> liftAsync // This is all needed to get a declaration list let! sourceText = document.GetTextAsync(context.CancellationToken) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/UseMutationWhenValueIsMutable.fs b/vsintegration/src/FSharp.Editor/CodeFix/UseMutationWhenValueIsMutable.fs index dfbee7adf6d..ecf9c59708b 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/UseMutationWhenValueIsMutable.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/UseMutationWhenValueIsMutable.fs @@ -50,8 +50,8 @@ type internal FSharpUseMutationWhenValueIsMutableFixProvider let textLine = sourceText.Lines.GetLineFromPosition adjustedPosition let textLinePos = sourceText.Lines.GetLinePosition adjustedPosition let fcsTextLineNumber = Line.fromZ textLinePos.Line - let! lexerSymbol = document.TryFindFSharpLexerSymbolAsync(adjustedPosition, SymbolLookupKind.Greedy, false, false) - let! _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync() |> liftAsync + let! lexerSymbol = document.TryFindFSharpLexerSymbolAsync(adjustedPosition, SymbolLookupKind.Greedy, false, false, nameof(FSharpUseMutationWhenValueIsMutableFixProvider)) + let! _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync(nameof(FSharpUseMutationWhenValueIsMutableFixProvider)) |> liftAsync let! symbolUse = checkFileResults.GetSymbolUseAtLocation(fcsTextLineNumber, lexerSymbol.Ident.idRange.EndColumn, textLine.ToString(), lexerSymbol.FullIsland) match symbolUse.Symbol with diff --git a/vsintegration/src/FSharp.Editor/CodeLens/FSharpCodeLensService.fs b/vsintegration/src/FSharp.Editor/CodeLens/FSharpCodeLensService.fs index 85f93c233e2..ac322ac733f 100644 --- a/vsintegration/src/FSharp.Editor/CodeLens/FSharpCodeLensService.fs +++ b/vsintegration/src/FSharp.Editor/CodeLens/FSharpCodeLensService.fs @@ -154,7 +154,7 @@ type internal FSharpCodeLensService logInfof "Rechecking code due to buffer edit!" #endif let! document = workspace.CurrentSolution.GetDocument(documentId.Value) |> Option.ofObj - let! parseFileResults, checkFileResults = document.GetFSharpParseAndCheckResultsAsync() |> liftAsync + let! parseFileResults, checkFileResults = document.GetFSharpParseAndCheckResultsAsync(nameof(FSharpUseMutationWhenValueIsMutableFixProvider)) |> liftAsync let parsedInput = parseFileResults.ParseTree #if DEBUG logInfof "Getting uses of all symbols!" diff --git a/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs b/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs index cb2ba6049ac..3b1afa222a2 100644 --- a/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs +++ b/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs @@ -24,7 +24,7 @@ type internal FSharpHelpContextService static member GetHelpTerm(document: Document, span: TextSpan, tokens: List) : Async = asyncMaybe { - let! _, check = document.GetFSharpParseAndCheckResultsAsync() |> liftAsync + let! _, check = document.GetFSharpParseAndCheckResultsAsync(nameof(FSharpHelpContextService)) |> liftAsync let! sourceText = document.GetTextAsync() |> liftTaskAsync let textLines = sourceText.Lines let lineInfo = textLines.GetLineFromPosition(span.Start) @@ -99,7 +99,7 @@ type internal FSharpHelpContextService member this.GetHelpTermAsync(document, textSpan, cancellationToken) = asyncMaybe { let! sourceText = document.GetTextAsync(cancellationToken) - let defines = document.GetFSharpSyntaxDefines() + let defines = document.GetFSharpQuickDefines() let textLine = sourceText.Lines.GetLineFromPosition(textSpan.Start) let classifiedSpans = Tokenizer.getClassifiedSpans(document.Id, sourceText, textLine.Span, Some document.Name, defines, cancellationToken) return! FSharpHelpContextService.GetHelpTerm(document, textSpan, classifiedSpans) diff --git a/vsintegration/src/FSharp.Editor/Commands/XmlDocCommandService.fs b/vsintegration/src/FSharp.Editor/Commands/XmlDocCommandService.fs index d41e8a2561f..0771f84d4d8 100644 --- a/vsintegration/src/FSharp.Editor/Commands/XmlDocCommandService.fs +++ b/vsintegration/src/FSharp.Editor/Commands/XmlDocCommandService.fs @@ -63,7 +63,7 @@ type internal XmlDocCommandFilter let! document = document.Value let! cancellationToken = Async.CancellationToken |> liftAsync let! sourceText = document.GetTextAsync(cancellationToken) - let! parseResults = document.GetFSharpParseResultsAsync() |> liftAsync + let! parseResults = document.GetFSharpParseResultsAsync(nameof(XmlDocCommandFilter)) |> liftAsync let xmlDocables = XmlDocParser.GetXmlDocables (sourceText.ToFSharpSourceText(), parseResults.ParseTree) let xmlDocablesBelowThisLine = // +1 because looking below current line for e.g. a 'member' diff --git a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs index 027fac2ec84..8cf74d0d665 100644 --- a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs +++ b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs @@ -105,7 +105,7 @@ type internal FSharpCompletionProvider static member ProvideCompletionsAsyncAux(document: Document, caretPosition: int, getAllSymbols: FSharpCheckFileResults -> AssemblySymbol list, intellisenseOptions: IntelliSenseOptions) = asyncMaybe { - let! parseResults, checkFileResults = document.GetFSharpParseAndCheckResultsAsync() |> liftAsync + let! parseResults, checkFileResults = document.GetFSharpParseAndCheckResultsAsync("ProvideCompletionsAsyncAux") |> liftAsync let! sourceText = document.GetTextAsync() let textLines = sourceText.Lines let caretLinePos = textLines.GetLinePosition(caretPosition) @@ -205,7 +205,7 @@ type internal FSharpCompletionProvider let getInfo() = let documentId = workspace.GetDocumentIdInCurrentContext(sourceText.Container) let document = workspace.CurrentSolution.GetDocument(documentId) - let defines = document.GetFSharpSyntaxDefines() + let defines = document.GetFSharpQuickDefines() (documentId, document.FilePath, defines) FSharpCompletionProvider.ShouldTriggerCompletionAux(sourceText, caretPosition, trigger.Kind, getInfo, settings.IntelliSense) @@ -215,7 +215,7 @@ type internal FSharpCompletionProvider use _logBlock = Logger.LogBlockMessage context.Document.Name LogEditorFunctionId.Completion_ProvideCompletionsAsync let document = context.Document let! sourceText = context.Document.GetTextAsync(context.CancellationToken) - let defines = document.GetFSharpSyntaxDefines() + let defines = document.GetFSharpQuickDefines() do! Option.guard (CompletionUtils.shouldProvideCompletion(document.Id, document.FilePath, defines, sourceText, context.Position)) let getAllSymbols(fileCheckResults: FSharpCheckFileResults) = if settings.IntelliSense.IncludeSymbolsFromUnopenedNamespacesOrModules @@ -283,7 +283,7 @@ type internal FSharpCompletionProvider let! sourceText = document.GetTextAsync(cancellationToken) let textWithItemCommitted = sourceText.WithChanges(TextChange(item.Span, nameInCode)) let line = sourceText.Lines.GetLineFromPosition(item.Span.Start) - let! parseResults = document.GetFSharpParseResultsAsync() |> liftAsync + let! parseResults = document.GetFSharpParseResultsAsync(nameof(FSharpCompletionProvider)) |> liftAsync let fullNameIdents = fullName |> Option.map (fun x -> x.Split '.') |> Option.defaultValue [||] let insertionPoint = diff --git a/vsintegration/src/FSharp.Editor/Completion/SignatureHelp.fs b/vsintegration/src/FSharp.Editor/Completion/SignatureHelp.fs index e05adb17799..8bc06631418 100644 --- a/vsintegration/src/FSharp.Editor/Completion/SignatureHelp.fs +++ b/vsintegration/src/FSharp.Editor/Completion/SignatureHelp.fs @@ -503,7 +503,7 @@ type internal FSharpSignatureHelpProvider possibleCurrentSignatureHelpSessionKind: CurrentSignatureHelpSessionKind option ) = asyncMaybe { - let! parseResults, checkFileResults = document.GetFSharpParseAndCheckResultsAsync() |> liftAsync + let! parseResults, checkFileResults = document.GetFSharpParseAndCheckResultsAsync("ProvideSignatureHelp") |> liftAsync let! sourceText = document.GetTextAsync() |> liftTaskAsync @@ -569,7 +569,7 @@ type internal FSharpSignatureHelpProvider member _.GetItemsAsync(document, position, triggerInfo, cancellationToken) = asyncMaybe { - let defines = document.GetFSharpSyntaxDefines() + let defines = document.GetFSharpQuickDefines() let triggerTypedChar = if triggerInfo.TriggerCharacter.HasValue && triggerInfo.TriggerReason = FSharpSignatureHelpTriggerReason.TypeCharCommand then diff --git a/vsintegration/src/FSharp.Editor/Debugging/BreakpointResolutionService.fs b/vsintegration/src/FSharp.Editor/Debugging/BreakpointResolutionService.fs index 315cc394604..f0fe54af81d 100644 --- a/vsintegration/src/FSharp.Editor/Debugging/BreakpointResolutionService.fs +++ b/vsintegration/src/FSharp.Editor/Debugging/BreakpointResolutionService.fs @@ -37,7 +37,7 @@ type internal FSharpBreakpointResolutionService else let textLineColumn = textLinePos.Character let fcsTextLineNumber = Line.fromZ textLinePos.Line // Roslyn line numbers are zero-based, FSharp.Compiler.Service line numbers are 1-based - let! parseResults = document.GetFSharpParseResultsAsync() |> liftAsync + let! parseResults = document.GetFSharpParseResultsAsync(nameof(FSharpBreakpointResolutionService)) |> liftAsync match parseResults with | Some parseResults -> return parseResults.ValidateBreakpointLocation(mkPos fcsTextLineNumber textLineColumn) | _ -> return None diff --git a/vsintegration/src/FSharp.Editor/Debugging/LanguageDebugInfoService.fs b/vsintegration/src/FSharp.Editor/Debugging/LanguageDebugInfoService.fs index 82c239bf727..d2b30fc01bb 100644 --- a/vsintegration/src/FSharp.Editor/Debugging/LanguageDebugInfoService.fs +++ b/vsintegration/src/FSharp.Editor/Debugging/LanguageDebugInfoService.fs @@ -49,7 +49,7 @@ type internal FSharpLanguageDebugInfoService []() = member this.GetDataTipInfoAsync(document: Document, position: int, cancellationToken: CancellationToken): Task = async { - let defines = document.GetFSharpSyntaxDefines() + let defines = document.GetFSharpQuickDefines() let! cancellationToken = Async.CancellationToken let! sourceText = document.GetTextAsync(cancellationToken) |> Async.AwaitTask let textSpan = TextSpan.FromBounds(0, sourceText.Length) diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs index 2ebe818c0c6..4f8e55f23fd 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs @@ -56,7 +56,7 @@ type internal FSharpDocumentDiagnosticAnalyzer async { let! ct = Async.CancellationToken - let! parseResults = document.GetFSharpParseResultsAsync() + let! parseResults = document.GetFSharpParseResultsAsync("GetDiagnostics") let! sourceText = document.GetTextAsync(ct) |> Async.AwaitTask let filePath = document.FilePath @@ -65,7 +65,7 @@ type internal FSharpDocumentDiagnosticAnalyzer async { match diagnosticType with | DiagnosticsType.Semantic -> - let! _, checkResults = document.GetFSharpParseAndCheckResultsAsync() + let! _, checkResults = document.GetFSharpParseAndCheckResultsAsync("GetDiagnostics") // In order to eleminate duplicates, we should not return parse errors here because they are returned by `AnalyzeSyntaxAsync` method. let allErrors = HashSet(checkResults.Diagnostics, errorInfoEqualityComparer) allErrors.ExceptWith(parseResults.Diagnostics) diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/SimplifyNameDiagnosticAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/SimplifyNameDiagnosticAnalyzer.fs index aeef5b41539..0c853a404cf 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/SimplifyNameDiagnosticAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/SimplifyNameDiagnosticAnalyzer.fs @@ -47,7 +47,7 @@ type internal SimplifyNameDiagnosticAnalyzer | :? PerDocumentSavedData as data when data.Hash = textVersionHash -> return data.Diagnostics | _ -> let! sourceText = document.GetTextAsync() - let! _, checkResults = document.GetFSharpParseAndCheckResultsAsync() |> liftAsync + let! _, checkResults = document.GetFSharpParseAndCheckResultsAsync(nameof(SimplifyNameDiagnosticAnalyzer)) |> liftAsync let! result = SimplifyNames.getSimplifiableNames(checkResults, fun lineNumber -> sourceText.Lines.[Line.toZ lineNumber].ToString()) |> liftAsync let mutable diag = ResizeArray() for r in result do diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs index 564762fb4c1..c94b193e6aa 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs @@ -27,7 +27,7 @@ type internal UnusedDeclarationsAnalyzer do! Option.guard document.Project.IsFSharpCodeFixesUnusedDeclarationsEnabled do Trace.TraceInformation("{0:n3} (start) UnusedDeclarationsAnalyzer", DateTime.Now.TimeOfDay.TotalSeconds) - let! _, checkResults = document.GetFSharpParseAndCheckResultsAsync() |> liftAsync + let! _, checkResults = document.GetFSharpParseAndCheckResultsAsync(nameof(UnusedDeclarationsAnalyzer)) |> liftAsync let! unusedRanges = UnusedDeclarations.getUnusedDeclarations( checkResults, (isScriptFile document.FilePath)) |> liftAsync let! sourceText = document.GetTextAsync() return diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs index eaffc3e6214..9c75bab971e 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs @@ -26,7 +26,7 @@ type internal UnusedOpensDiagnosticAnalyzer asyncMaybe { do! Option.guard document.Project.IsFSharpCodeFixesUnusedOpensEnabled let! sourceText = document.GetTextAsync() - let! _, checkResults = document.GetFSharpParseAndCheckResultsAsync() |> liftAsync + let! _, checkResults = document.GetFSharpParseAndCheckResultsAsync(nameof(UnusedOpensDiagnosticAnalyzer)) |> liftAsync let! unusedOpens = UnusedOpens.getUnusedOpens(checkResults, fun lineNumber -> sourceText.Lines.[Line.toZ lineNumber].ToString()) |> liftAsync return unusedOpens } diff --git a/vsintegration/src/FSharp.Editor/DocumentHighlights/DocumentHighlightsService.fs b/vsintegration/src/FSharp.Editor/DocumentHighlights/DocumentHighlightsService.fs index 1071fa8eabd..207c1dd1e5c 100644 --- a/vsintegration/src/FSharp.Editor/DocumentHighlights/DocumentHighlightsService.fs +++ b/vsintegration/src/FSharp.Editor/DocumentHighlights/DocumentHighlightsService.fs @@ -51,7 +51,7 @@ type internal FSharpDocumentHighlightsService [] () = static member GetDocumentHighlights(document: Document, position: int) : Async = asyncMaybe { - let! symbol = document.TryFindFSharpLexerSymbolAsync(position, SymbolLookupKind.Greedy, false, false) + let! symbol = document.TryFindFSharpLexerSymbolAsync(position, SymbolLookupKind.Greedy, false, false, nameof(FSharpDocumentHighlightsService.GetDocumentHighlights)) let! ct = Async.CancellationToken |> liftAsync let! sourceText = document.GetTextAsync(ct) @@ -59,7 +59,7 @@ type internal FSharpDocumentHighlightsService [] () = let textLinePos = sourceText.Lines.GetLinePosition(position) let fcsTextLineNumber = Line.fromZ textLinePos.Line - let! _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync() |> liftAsync + let! _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync(nameof(FSharpDocumentHighlightsService)) |> liftAsync let! symbolUse = checkFileResults.GetSymbolUseAtLocation(fcsTextLineNumber, symbol.Ident.idRange.EndColumn, textLine.ToString(), symbol.FullIsland) let symbolUses = checkFileResults.GetUsesOfSymbolInFile(symbolUse.Symbol) return diff --git a/vsintegration/src/FSharp.Editor/Formatting/BraceMatchingService.fs b/vsintegration/src/FSharp.Editor/Formatting/BraceMatchingService.fs index b57edf93ab3..edd3920d7c0 100644 --- a/vsintegration/src/FSharp.Editor/Formatting/BraceMatchingService.fs +++ b/vsintegration/src/FSharp.Editor/Formatting/BraceMatchingService.fs @@ -13,8 +13,6 @@ type internal FSharpBraceMatchingService [] ( ) = - - static let userOpName = "BraceMatching" static member GetBraceMatchingResult(checker: FSharpChecker, sourceText: SourceText, fileName, parsingOptions: FSharpParsingOptions, position: int, userOpName: string, [] forFormatting: bool) = async { @@ -34,9 +32,9 @@ type internal FSharpBraceMatchingService interface IFSharpBraceMatcher with member this.FindBracesAsync(document, position, cancellationToken) = asyncMaybe { - let! checker, _, parsingOptions, _ = document.GetFSharpCompilationOptionsAsync() |> liftAsync + let! checker, _, parsingOptions, _ = document.GetFSharpCompilationOptionsAsync(nameof(FSharpBraceMatchingService)) |> liftAsync let! sourceText = document.GetTextAsync(cancellationToken) - let! (left, right) = FSharpBraceMatchingService.GetBraceMatchingResult(checker, sourceText, document.Name, parsingOptions, position, userOpName) + let! (left, right) = FSharpBraceMatchingService.GetBraceMatchingResult(checker, sourceText, document.Name, parsingOptions, position, nameof(FSharpBraceMatchingService)) let! leftSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, left) let! rightSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, right) return FSharpBraceMatchingResult(leftSpan, rightSpan) diff --git a/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs b/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs index 79f4033fcec..51a9efe9951 100644 --- a/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs +++ b/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs @@ -149,9 +149,9 @@ type internal InlineRenameService let textLine = sourceText.Lines.GetLineFromPosition(position) let textLinePos = sourceText.Lines.GetLinePosition(position) let fcsTextLineNumber = Line.fromZ textLinePos.Line - let! symbol = document.TryFindFSharpLexerSymbolAsync(position, SymbolLookupKind.Greedy, false, false) + let! symbol = document.TryFindFSharpLexerSymbolAsync(position, SymbolLookupKind.Greedy, false, false, nameof(InlineRenameService)) - let! _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync() |> liftAsync + let! _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync(nameof(InlineRenameService)) |> liftAsync let! symbolUse = checkFileResults.GetSymbolUseAtLocation(fcsTextLineNumber, symbol.Ident.idRange.EndColumn, textLine.Text.ToString(), symbol.FullIsland) let! declLoc = symbolUse.GetDeclarationLocation(document) diff --git a/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs b/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs index e7f29de8684..3700d639e1e 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/FSharpProjectOptionsManager.fs @@ -78,7 +78,7 @@ module private FSharpProjectOptionsHelpers = let isProjectInvalidated (oldProject: Project) (newProject: Project) ct = let hasProjectVersionChanged = hasProjectVersionChanged oldProject newProject - if newProject.IsFSharpCrossProjectReferencingEnabled then + if newProject.AreFSharpInMemoryCrossProjectReferencesEnabled then hasProjectVersionChanged || hasDependentVersionChanged oldProject newProject ct else hasProjectVersionChanged @@ -237,7 +237,7 @@ type private FSharpProjectOptionsReactor (checker: FSharpChecker) = let referencedProjects = ResizeArray() - if project.IsFSharpCrossProjectReferencingEnabled then + if project.AreFSharpInMemoryCrossProjectReferencesEnabled then for projectReference in project.ProjectReferences do let referencedProject = project.Solution.GetProject(projectReference.ProjectId) if referencedProject.Language = FSharpConstants.FSharpLanguageName then diff --git a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs index 33580b72c4b..6a921b01cb7 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs @@ -55,6 +55,10 @@ type internal FSharpWorkspaceServiceFactory ( ) = + // We have a lock just in case if multi-threads try to create a new IFSharpWorkspaceService - + // but we only want to have a single instance of the FSharpChecker regardless if there are multiple instances of IFSharpWorkspaceService. + // In VS, we only ever have a single IFSharpWorkspaceService, but for testing we may have mutliple; we still only want a + // single FSharpChecker instance shared across them. static let gate = obj() // We only ever want to have a single FSharpChecker. @@ -119,12 +123,12 @@ type internal FSharpWorkspaceServiceFactory | _ -> failwith "Checker not set." - upcast { new IFSharpWorkspaceService with + { new IFSharpWorkspaceService with member _.Checker = match checkerSingleton with | Some checker -> checker.Value | _ -> failwith "Checker not set." - member _.FSharpProjectOptionsManager = optionsManager.Value } + member _.FSharpProjectOptionsManager = optionsManager.Value } :> _ [] type private FSharpSolutionEvents(projectManager: FSharpProjectOptionsManager, metadataAsSource: FSharpMetadataAsSourceService) = diff --git a/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs b/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs index d0747ca3aef..5a16321171a 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs @@ -20,8 +20,9 @@ module internal SymbolHelpers = /// Used for local code fixes in a document, e.g. to rename local parameters let getSymbolUsesOfSymbolAtLocationInDocument (document: Document, position: int) = asyncMaybe { - let! _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync() |> liftAsync - let! defines = document.GetFSharpCompilationDefinesAsync() |> liftAsync + let userOpName = "getSymbolUsesOfSymbolAtLocationInDocument" + let! _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync(userOpName) |> liftAsync + let! defines = document.GetFSharpCompilationDefinesAsync(userOpName) |> liftAsync let! cancellationToken = Async.CancellationToken |> liftAsync let! sourceText = document.GetTextAsync(cancellationToken) @@ -37,7 +38,7 @@ module internal SymbolHelpers = let getSymbolUsesInProjects (symbol: FSharpSymbol, projects: Project list, onFound: Document -> TextSpan -> range -> Async) = projects - |> Seq.map (fun project -> project.FindFSharpReferencesAsync(symbol, onFound)) + |> Seq.map (fun project -> project.FindFSharpReferencesAsync(symbol, onFound, "getSymbolUsesInProjects")) |> Async.Sequential let getSymbolUsesInSolution (symbol: FSharpSymbol, declLoc: SymbolDeclarationLocation, checkFileResults: FSharpCheckFileResults, solution: Solution) = @@ -93,18 +94,19 @@ module internal SymbolHelpers = let changeAllSymbolReferences (document: Document, symbolSpan: TextSpan, textChanger: string -> string) : Async<(Func> * OriginalText) option> = asyncMaybe { + let userOpName = "changeAllSymbolReferences" do! Option.guard (symbolSpan.Length > 0) let! cancellationToken = liftAsync Async.CancellationToken let! sourceText = document.GetTextAsync(cancellationToken) let originalText = sourceText.ToString(symbolSpan) do! Option.guard (originalText.Length > 0) - let! symbol = document.TryFindFSharpLexerSymbolAsync(symbolSpan.Start, SymbolLookupKind.Greedy, false, false) + let! symbol = document.TryFindFSharpLexerSymbolAsync(symbolSpan.Start, SymbolLookupKind.Greedy, false, false, userOpName) let textLine = sourceText.Lines.GetLineFromPosition(symbolSpan.Start) let textLinePos = sourceText.Lines.GetLinePosition(symbolSpan.Start) let fcsTextLineNumber = Line.fromZ textLinePos.Line - let! _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync() |> liftAsync + let! _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync(userOpName) |> liftAsync let! symbolUse = checkFileResults.GetSymbolUseAtLocation(fcsTextLineNumber, symbol.Ident.idRange.EndColumn, textLine.ToString(), symbol.FullIsland) let! declLoc = symbolUse.GetDeclarationLocation(document) let newText = textChanger originalText diff --git a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs index 701de619c03..11a265d3b04 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs @@ -12,6 +12,7 @@ open FSharp.Compiler.CodeAnalysis module private CheckerExtensions = type FSharpChecker with + /// Parse the source text from the Roslyn document. member checker.ParseDocument(document: Document, parsingOptions: FSharpParsingOptions, userOpName: string) = async { let! ct = Async.CancellationToken @@ -20,19 +21,7 @@ module private CheckerExtensions = return! checker.ParseFile(document.FilePath, sourceText.ToFSharpSourceText(), parsingOptions, userOpName=userOpName) } - member checker.CheckDocument(document: Document, parseResults: FSharpParseFileResults, options: FSharpProjectOptions, userOpName: string) = - async { - let! ct = Async.CancellationToken - - let! sourceText = document.GetTextAsync(ct) |> Async.AwaitTask - let! textVersion = document.GetTextVersionAsync(ct) |> Async.AwaitTask - - let filePath = document.FilePath - let textVersionHash = textVersion.GetHashCode() - - return! checker.CheckFileInProject(parseResults, filePath, textVersionHash, sourceText.ToFSharpSourceText(), options,userOpName=userOpName) - } - + /// Parse and check the source text from the Roslyn document with possible stale results. member checker.ParseAndCheckDocumentWithPossibleStaleResults(document: Document, options: FSharpProjectOptions, allowStaleResults: bool, userOpName: string) = async { let! ct = Async.CancellationToken @@ -89,6 +78,7 @@ module private CheckerExtensions = return bindParsedInput results } + /// Parse and check the source text from the Roslyn document. member checker.ParseAndCheckDocument(document: Document, options: FSharpProjectOptions, userOpName: string, ?allowStaleResults: bool) = async { let allowStaleResults = @@ -101,16 +91,21 @@ module private CheckerExtensions = [] module private ProjectCache = + /// This is a cache to maintain FSharpParsingOptions and FSharpProjectOptions per Roslyn Project. + /// The Roslyn Project is held weakly meaning when it is cleaned up by the GC, the FSharParsingOptions and FSharpProjectOptions will be cleaned up by the GC. + /// At some point, this will be the main caching mechanism for FCS projects instead of FCS itself. let Projects = ConditionalWeakTable() type Solution with + /// Get the instance of IFSharpWorkspaceService. member private this.GetFSharpWorkspaceService() = this.Workspace.Services.GetRequiredService() type Document with - member this.GetFSharpCompilationOptionsAsync() = + /// Get the FSharpParsingOptions and FSharpProjectOptions from the F# project that is associated with the given F# document. + member this.GetFSharpCompilationOptionsAsync(userOpName) = async { if this.Project.IsFSharp then match ProjectCache.Projects.TryGetValue(this.Project) with @@ -119,7 +114,7 @@ type Document with let service = this.Project.Solution.GetFSharpWorkspaceService() let projectOptionsManager = service.FSharpProjectOptionsManager let! ct = Async.CancellationToken - match! projectOptionsManager.TryGetOptionsForDocumentOrProject(this, ct, nameof(this.GetFSharpCompilationOptionsAsync)) with + match! projectOptionsManager.TryGetOptionsForDocumentOrProject(this, ct, userOpName) with | None -> return raise(System.OperationCanceledException("FSharp project options not found.")) | Some(parsingOptions, _, projectOptions) -> let result = (service.Checker, projectOptionsManager, parsingOptions, projectOptions) @@ -128,51 +123,61 @@ type Document with return raise(System.OperationCanceledException("Document is not a FSharp document.")) } - member this.GetFSharpCompilationDefinesAsync() = + /// Get the compilation defines from F# project that is associated with the given F# document. + member this.GetFSharpCompilationDefinesAsync(userOpName) = async { - let! _, _, parsingOptions, _ = this.GetFSharpCompilationOptionsAsync() + let! _, _, parsingOptions, _ = this.GetFSharpCompilationOptionsAsync(userOpName) return CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions } + /// Get the instance of the FSharpChecker from the workspace by the given F# document. member this.GetFSharpChecker() = let workspaceService = this.Project.Solution.GetFSharpWorkspaceService() workspaceService.Checker + /// A non-async call that quickly gets FSharpParsingOptions of the given F# document. + /// This tries to get the FSharpParsingOptions by looking at an internal cache; if it doesn't exist in the cache it will create an inaccurate but usable form of the FSharpParsingOptions. member this.GetFSharpQuickParsingOptions() = let workspaceService = this.Project.Solution.GetFSharpWorkspaceService() workspaceService.FSharpProjectOptionsManager.TryGetQuickParsingOptionsForEditingDocumentOrProject(this) - member this.GetFSharpSyntaxDefines() = - let workspaceService = this.Project.Solution.GetFSharpWorkspaceService() - workspaceService.FSharpProjectOptionsManager.GetCompilationDefinesForEditingDocument(this) + /// A non-async call that quickly gets the defines of the given F# document. + /// This tries to get the defines by looking at an internal cache; if it doesn't exist in the cache it will create an inaccurate but usable form of the defines. + member this.GetFSharpQuickDefines() = + let workspaceService = this.Project.Solution.GetFSharpWorkspaceService() + workspaceService.FSharpProjectOptionsManager.GetCompilationDefinesForEditingDocument(this) - member this.GetFSharpParseResultsAsync() = + /// Parses the given F# document. + member this.GetFSharpParseResultsAsync(userOpName) = async { - let! checker, _, parsingOptions, _ = this.GetFSharpCompilationOptionsAsync() - return! checker.ParseDocument(this, parsingOptions, nameof(this.GetFSharpParseResultsAsync)) + let! checker, _, parsingOptions, _ = this.GetFSharpCompilationOptionsAsync(userOpName) + return! checker.ParseDocument(this, parsingOptions, userOpName) } - member this.GetFSharpParseAndCheckResultsAsync() = + /// Parses and checks the given F# document. + member this.GetFSharpParseAndCheckResultsAsync(userOpName) = async { - let! checker, _, _, projectOptions = this.GetFSharpCompilationOptionsAsync() - match! checker.ParseAndCheckDocument(this, projectOptions, nameof(this.GetFSharpParseAndCheckResultsAsync), allowStaleResults = false) with + let! checker, _, _, projectOptions = this.GetFSharpCompilationOptionsAsync(userOpName) + match! checker.ParseAndCheckDocument(this, projectOptions, userOpName, allowStaleResults = false) with | Some(parseResults, _, checkResults) -> return (parseResults, checkResults) | _ -> return raise(System.OperationCanceledException("Unable to get FSharp parse and check results.")) } - member this.GetFSharpSemanticClassificationAsync() = + /// Get the semantic classifications of the given F# document. + member this.GetFSharpSemanticClassificationAsync(userOpName) = async { - let! checker, _, _, projectOptions = this.GetFSharpCompilationOptionsAsync() + let! checker, _, _, projectOptions = this.GetFSharpCompilationOptionsAsync(userOpName) match! checker.GetBackgroundSemanticClassificationForFile(this.FilePath, projectOptions) with | Some results -> return results | _ -> return raise(System.OperationCanceledException("Unable to get FSharp semantic classification.")) } - member this.FindFSharpReferencesAsync(symbol, onFound) = + /// Find F# references in the given F# document. + member this.FindFSharpReferencesAsync(symbol, onFound, userOpName) = async { - let! checker, _, _, projectOptions = this.GetFSharpCompilationOptionsAsync() + let! checker, _, _, projectOptions = this.GetFSharpCompilationOptionsAsync(userOpName) let! symbolUses = checker.FindBackgroundReferencesInFile(this.FilePath, projectOptions, symbol, canInvalidateProject = false) let! ct = Async.CancellationToken let! sourceText = this.GetTextAsync ct |> Async.AwaitTask @@ -184,14 +189,16 @@ type Document with () } - member this.TryFindFSharpLexerSymbolAsync(position, lookupKind, wholeActivePattern, allowStringToken) = + /// Try to find a F# lexer/token symbol of the given F# document and position. + member this.TryFindFSharpLexerSymbolAsync(position, lookupKind, wholeActivePattern, allowStringToken, userOpName) = async { - let! defines = this.GetFSharpCompilationDefinesAsync() + let! defines = this.GetFSharpCompilationDefinesAsync(userOpName) let! ct = Async.CancellationToken let! sourceText = this.GetTextAsync(ct) |> Async.AwaitTask return Tokenizer.getSymbolAtPosition(this.Id, sourceText, position, this.FilePath, defines, lookupKind, wholeActivePattern, allowStringToken) } + /// This is only used for testing purposes. It sets the ProjectCache.Projects with the given FSharpProjectOptions and F# document's project. member this.SetFSharpProjectOptionsForTesting(projectOptions: FSharpProjectOptions) = let workspaceService = this.Project.Solution.GetFSharpWorkspaceService() let parsingOptions, _, _ = @@ -202,8 +209,9 @@ type Document with type Project with - member this.FindFSharpReferencesAsync(symbol, onFound) = + /// Find F# references in the given project. + member this.FindFSharpReferencesAsync(symbol, onFound, userOpName) = async { for doc in this.Documents do - do! doc.FindFSharpReferencesAsync(symbol, fun textSpan range -> onFound doc textSpan range) + do! doc.FindFSharpReferencesAsync(symbol, (fun textSpan range -> onFound doc textSpan range), userOpName) } diff --git a/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs b/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs index cd76a961892..2819eb65d38 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs @@ -49,9 +49,9 @@ type internal FSharpFindUsagesService let! sourceText = document.GetTextAsync(context.CancellationToken) |> Async.AwaitTask |> liftAsync let textLine = sourceText.Lines.GetLineFromPosition(position).ToString() let lineNumber = sourceText.Lines.GetLinePosition(position).Line + 1 - let! symbol = document.TryFindFSharpLexerSymbolAsync(position, SymbolLookupKind.Greedy, false, false) + let! symbol = document.TryFindFSharpLexerSymbolAsync(position, SymbolLookupKind.Greedy, false, false, "findReferencedSymbolsAsync") - let! _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync() |> liftAsync + let! _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync(nameof(FSharpFindUsagesService)) |> liftAsync let! symbolUse = checkFileResults.GetSymbolUseAtLocation(lineNumber, symbol.Ident.idRange.EndColumn, textLine, symbol.FullIsland) let declaration = checkFileResults.GetDeclarationLocation (lineNumber, symbol.Ident.idRange.EndColumn, textLine, symbol.FullIsland, false) let tags = FSharpGlyphTags.GetTags(Tokenizer.GetGlyphForSymbol (symbolUse.Symbol, symbol.Kind)) diff --git a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs index 946977049d5..90474acfd3c 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs @@ -173,15 +173,16 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = /// Helper function that is used to determine the navigation strategy to apply, can be tuned towards signatures or implementation files. member private _.FindSymbolHelper (originDocument: Document, originRange: range, sourceText: SourceText, preferSignature: bool) = asyncMaybe { + let userOpName = "FindSymbolHelper" let! originTextSpan = RoslynHelpers.TryFSharpRangeToTextSpan (sourceText, originRange) let position = originTextSpan.Start - let! lexerSymbol = originDocument.TryFindFSharpLexerSymbolAsync(position, SymbolLookupKind.Greedy, false, false) + let! lexerSymbol = originDocument.TryFindFSharpLexerSymbolAsync(position, SymbolLookupKind.Greedy, false, false, userOpName) let textLinePos = sourceText.Lines.GetLinePosition position let fcsTextLineNumber = Line.fromZ textLinePos.Line let lineText = (sourceText.Lines.GetLineFromPosition position).ToString() let idRange = lexerSymbol.Ident.idRange - let! _, checkFileResults = originDocument.GetFSharpParseAndCheckResultsAsync() |> liftAsync + let! _, checkFileResults = originDocument.GetFSharpParseAndCheckResultsAsync(nameof(GoToDefinition)) |> liftAsync let! fsSymbolUse = checkFileResults.GetSymbolUseAtLocation (fcsTextLineNumber, idRange.EndColumn, lineText, lexerSymbol.FullIsland) let symbol = fsSymbolUse.Symbol // if the tooltip was spawned in an implementation file and we have a range targeting @@ -192,7 +193,7 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = if not (File.Exists fsfilePath) then return! None else let! implDoc = originDocument.Project.Solution.TryGetDocumentFromPath fsfilePath let! implSourceText = implDoc.GetTextAsync () - let! _, checkFileResults = implDoc.GetFSharpParseAndCheckResultsAsync() |> liftAsync + let! _, checkFileResults = implDoc.GetFSharpParseAndCheckResultsAsync(userOpName) |> liftAsync let symbolUses = checkFileResults.GetUsesOfSymbolInFile symbol let! implSymbol = symbolUses |> Array.tryHead let! implTextSpan = RoslynHelpers.TryFSharpRangeToTextSpan (implSourceText, implSymbol.Range) @@ -211,7 +212,7 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = match targetSymbolUse.Symbol.DeclarationLocation with | Some decl when decl.FileName = filePath -> return decl | _ -> - let! _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync() |> liftAsync + let! _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync("FindSymbolDeclarationInDocument") |> liftAsync let symbolUses = checkFileResults.GetUsesOfSymbolInFile targetSymbolUse.Symbol let! implSymbol = symbolUses |> Array.tryHead return implSymbol.Range @@ -219,6 +220,7 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = member private this.FindDefinitionAtPosition(originDocument: Document, position: int, cancellationToken: CancellationToken) = asyncMaybe { + let userOpName = "FindDefinitionAtPosition" let! sourceText = originDocument.GetTextAsync(cancellationToken) let textLine = sourceText.Lines.GetLineFromPosition position let textLinePos = sourceText.Lines.GetLinePosition position @@ -228,10 +230,10 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = let preferSignature = isSignatureFile originDocument.FilePath - let! lexerSymbol = originDocument.TryFindFSharpLexerSymbolAsync(position, SymbolLookupKind.Greedy, false, false) + let! lexerSymbol = originDocument.TryFindFSharpLexerSymbolAsync(position, SymbolLookupKind.Greedy, false, false, userOpName) let idRange = lexerSymbol.Ident.idRange - let! _, checkFileResults = originDocument.GetFSharpParseAndCheckResultsAsync() |> liftAsync + let! _, checkFileResults = originDocument.GetFSharpParseAndCheckResultsAsync(userOpName) |> liftAsync let declarations = checkFileResults.GetDeclarationLocation (fcsTextLineNumber, lexerSymbol.Ident.idRange.EndColumn, textLineString, lexerSymbol.FullIsland, preferSignature) let! targetSymbolUse = checkFileResults.GetSymbolUseAtLocation (fcsTextLineNumber, idRange.EndColumn, lineText, lexerSymbol.FullIsland) @@ -413,7 +415,7 @@ type internal GoToDefinition(metadataAsSource: FSharpMetadataAsSourceService) = | Some tmpShownDoc -> let goToAsync = asyncMaybe { - let! _, checkResults = tmpShownDoc.GetFSharpParseAndCheckResultsAsync() |> liftAsync + let! _, checkResults = tmpShownDoc.GetFSharpParseAndCheckResultsAsync("NavigateToExternalDeclaration") |> liftAsync let! r = let rec areTypesEqual (ty1: FSharpType) (ty2: FSharpType) = let ty1 = ty1.StripAbbreviations() diff --git a/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs b/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs index 6675e7c5399..6fd2bf231cf 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs @@ -179,7 +179,7 @@ type internal FSharpNavigateToSearchService let GetNavigableItems(document: Document, kinds: IImmutableSet) = async { let! cancellationToken = Async.CancellationToken - let! parseResults = document.GetFSharpParseResultsAsync() + let! parseResults = document.GetFSharpParseResultsAsync(nameof(FSharpNavigateToSearchService)) let! sourceText = document.GetTextAsync(cancellationToken) |> Async.AwaitTask let navItems parsedInput = NavigateTo.GetNavigableItems parsedInput diff --git a/vsintegration/src/FSharp.Editor/Navigation/NavigationBarItemService.fs b/vsintegration/src/FSharp.Editor/Navigation/NavigationBarItemService.fs index 0998b54d472..d09647a2396 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/NavigationBarItemService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/NavigationBarItemService.fs @@ -24,7 +24,7 @@ type internal FSharpNavigationBarItemService interface IFSharpNavigationBarItemService with member _.GetItemsAsync(document, cancellationToken) : Task> = asyncMaybe { - let! parseResults = document.GetFSharpParseResultsAsync() |> liftAsync + let! parseResults = document.GetFSharpParseResultsAsync(nameof(FSharpNavigationBarItemService)) |> liftAsync let navItems = Navigation.getNavigation parseResults.ParseTree let! sourceText = document.GetTextAsync(cancellationToken) let rangeToTextSpan range = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, range) diff --git a/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs b/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs index 77eb38332fb..578b675ae14 100644 --- a/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs +++ b/vsintegration/src/FSharp.Editor/Options/EditorOptions.fs @@ -194,7 +194,7 @@ module EditorOptionsExtensions = type Project with - member this.IsFSharpCrossProjectReferencingEnabled = + member this.AreFSharpInMemoryCrossProjectReferencesEnabled = let editorOptions = this.Solution.Workspace.Services.GetService() match box editorOptions with | null -> true diff --git a/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs b/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs index be00fb8e8c7..a4b37b7c908 100644 --- a/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs +++ b/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs @@ -46,6 +46,7 @@ module internal FSharpQuickInfo = : Async = asyncMaybe { + let userOpName = "getQuickInfoFromRange" let solution = document.Project.Solution // ascertain the location of the target declaration in the signature file let! extDocId = solution.GetDocumentIdsWithFilePath declRange.FileName |> Seq.tryHead @@ -55,8 +56,8 @@ module internal FSharpQuickInfo = let extLineText = (extSourceText.Lines.GetLineFromPosition extSpan.Start).ToString() // project options need to be retrieved because the signature file could be in another project - let! extLexerSymbol = extDocument.TryFindFSharpLexerSymbolAsync(extSpan.Start, SymbolLookupKind.Greedy, true, true) - let! _, extCheckFileResults = extDocument.GetFSharpParseAndCheckResultsAsync() |> liftAsync + let! extLexerSymbol = extDocument.TryFindFSharpLexerSymbolAsync(extSpan.Start, SymbolLookupKind.Greedy, true, true, userOpName) + let! _, extCheckFileResults = extDocument.GetFSharpParseAndCheckResultsAsync(userOpName) |> liftAsync let extQuickInfoText = extCheckFileResults.GetToolTip @@ -86,8 +87,9 @@ module internal FSharpQuickInfo = : Async<(range * QuickInfo option * QuickInfo option) option> = asyncMaybe { - let! lexerSymbol = document.TryFindFSharpLexerSymbolAsync(position, SymbolLookupKind.Greedy, true, true) - let! _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync() |> liftAsync + let userOpName = "getQuickInfo" + let! lexerSymbol = document.TryFindFSharpLexerSymbolAsync(position, SymbolLookupKind.Greedy, true, true, userOpName) + let! _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync(userOpName) |> liftAsync let! sourceText = document.GetTextAsync cancellationToken let idRange = lexerSymbol.Ident.idRange let textLinePos = sourceText.Lines.GetLinePosition position @@ -174,9 +176,9 @@ type internal FSharpAsyncQuickInfoSource let textLine = sourceText.Lines.GetLineFromPosition position let textLineNumber = textLine.LineNumber + 1 // Roslyn line numbers are zero-based let textLineString = textLine.ToString() - let! symbol = document.TryFindFSharpLexerSymbolAsync(position, SymbolLookupKind.Precise, true, true) + let! symbol = document.TryFindFSharpLexerSymbolAsync(position, SymbolLookupKind.Precise, true, true, nameof(FSharpAsyncQuickInfoSource)) - let! _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync() |> liftAsync + let! _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync(nameof(FSharpAsyncQuickInfoSource)) |> liftAsync let res = checkFileResults.GetToolTip (textLineNumber, symbol.Ident.idRange.EndColumn, textLineString, symbol.FullIsland, FSharpTokenTag.IDENT) match res with | ToolTipText [] diff --git a/vsintegration/src/FSharp.Editor/Refactor/AddExplicitTypeToParameter.fs b/vsintegration/src/FSharp.Editor/Refactor/AddExplicitTypeToParameter.fs index fb9ba68ff5b..6eea90f188d 100644 --- a/vsintegration/src/FSharp.Editor/Refactor/AddExplicitTypeToParameter.fs +++ b/vsintegration/src/FSharp.Editor/Refactor/AddExplicitTypeToParameter.fs @@ -31,9 +31,9 @@ type internal FSharpAddExplicitTypeToParameterRefactoring let textLine = sourceText.Lines.GetLineFromPosition position let textLinePos = sourceText.Lines.GetLinePosition position let fcsTextLineNumber = Line.fromZ textLinePos.Line - let! lexerSymbol = document.TryFindFSharpLexerSymbolAsync(position, SymbolLookupKind.Greedy, false, false) + let! lexerSymbol = document.TryFindFSharpLexerSymbolAsync(position, SymbolLookupKind.Greedy, false, false, nameof(FSharpAddExplicitTypeToParameterRefactoring)) - let! parseFileResults, checkFileResults = document.GetFSharpParseAndCheckResultsAsync() |> liftAsync + let! parseFileResults, checkFileResults = document.GetFSharpParseAndCheckResultsAsync(nameof(FSharpAddExplicitTypeToParameterRefactoring)) |> liftAsync let! symbolUse = checkFileResults.GetSymbolUseAtLocation(fcsTextLineNumber, lexerSymbol.Ident.idRange.EndColumn, textLine.ToString(), lexerSymbol.FullIsland) let isValidParameterWithoutTypeAnnotation (funcOrValue: FSharpMemberOrFunctionOrValue) (symbolUse: FSharpSymbolUse) = diff --git a/vsintegration/src/FSharp.Editor/Refactor/ChangeDerefToValueRefactoring.fs b/vsintegration/src/FSharp.Editor/Refactor/ChangeDerefToValueRefactoring.fs index 7c94bd8e8b2..ab0a748276d 100644 --- a/vsintegration/src/FSharp.Editor/Refactor/ChangeDerefToValueRefactoring.fs +++ b/vsintegration/src/FSharp.Editor/Refactor/ChangeDerefToValueRefactoring.fs @@ -27,7 +27,7 @@ type internal FSharpChangeDerefToValueRefactoring asyncMaybe { let document = context.Document let! sourceText = context.Document.GetTextAsync(context.CancellationToken) - let! parseResults = document.GetFSharpParseResultsAsync() |> liftAsync + let! parseResults = document.GetFSharpParseResultsAsync(nameof(FSharpChangeDerefToValueRefactoring)) |> liftAsync let selectionRange = RoslynHelpers.TextSpanToFSharpRange(document.FilePath, context.Span, sourceText) let! derefRange = parseResults.TryRangeOfRefCellDereferenceContainingPos selectionRange.Start diff --git a/vsintegration/src/FSharp.Editor/Refactor/ChangeTypeofWithNameToNameofExpression.fs b/vsintegration/src/FSharp.Editor/Refactor/ChangeTypeofWithNameToNameofExpression.fs index 88ff117a543..5fd447fcc24 100644 --- a/vsintegration/src/FSharp.Editor/Refactor/ChangeTypeofWithNameToNameofExpression.fs +++ b/vsintegration/src/FSharp.Editor/Refactor/ChangeTypeofWithNameToNameofExpression.fs @@ -27,7 +27,7 @@ type internal FSharpChangeTypeofWithNameToNameofExpressionRefactoring asyncMaybe { let document = context.Document let! sourceText = context.Document.GetTextAsync(context.CancellationToken) - let! parseResults = document.GetFSharpParseResultsAsync() |> liftAsync + let! parseResults = document.GetFSharpParseResultsAsync(nameof(FSharpChangeTypeofWithNameToNameofExpressionRefactoring)) |> liftAsync let selectionRange = RoslynHelpers.TextSpanToFSharpRange(document.FilePath, context.Span, sourceText) let! namedTypeOfResults = parseResults.TryRangeOfTypeofWithNameAndTypeExpr(selectionRange.Start) diff --git a/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs b/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs index 612cde6dd4e..8211b8f4bc2 100644 --- a/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs +++ b/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs @@ -148,7 +148,7 @@ type internal FSharpBlockStructureService [] () = member _.GetBlockStructureAsync(document, cancellationToken) : Task = asyncMaybe { let! sourceText = document.GetTextAsync(cancellationToken) - let! parseResults = document.GetFSharpParseResultsAsync() |> liftAsync + let! parseResults = document.GetFSharpParseResultsAsync(nameof(FSharpBlockStructureService)) |> liftAsync return createBlockSpans document.Project.IsFSharpBlockStructureEnabled sourceText parseResults.ParseTree |> Seq.toImmutableArray } |> Async.map (Option.defaultValue ImmutableArray<_>.Empty) diff --git a/vsintegration/tests/UnitTests/GoToDefinitionServiceTests.fs b/vsintegration/tests/UnitTests/GoToDefinitionServiceTests.fs index 52a62dd75cf..49527630ff6 100644 --- a/vsintegration/tests/UnitTests/GoToDefinitionServiceTests.fs +++ b/vsintegration/tests/UnitTests/GoToDefinitionServiceTests.fs @@ -51,7 +51,7 @@ module GoToDefinitionServiceTests = let textLinePos = sourceText.Lines.GetLinePosition position let fcsTextLineNumber = Line.fromZ textLinePos.Line let! lexerSymbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, position, document.FilePath, defines, SymbolLookupKind.Greedy, false, false) - let _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync() |> Async.RunSynchronously + let _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync(nameof(userOpName)) |> Async.RunSynchronously let declarations = checkFileResults.GetDeclarationLocation (fcsTextLineNumber, lexerSymbol.Ident.idRange.EndColumn, textLine.ToString(), lexerSymbol.FullIsland, false) diff --git a/vsintegration/tests/UnitTests/SemanticColorizationServiceTests.fs b/vsintegration/tests/UnitTests/SemanticColorizationServiceTests.fs index 9fde535eb71..78d0732493b 100644 --- a/vsintegration/tests/UnitTests/SemanticColorizationServiceTests.fs +++ b/vsintegration/tests/UnitTests/SemanticColorizationServiceTests.fs @@ -34,7 +34,7 @@ type SemanticClassificationServiceTests() = let getRanges (source: string) : SemanticClassificationItem list = asyncMaybe { let document, _ = RoslynTestHelpers.CreateDocument(filePath, source) - let! _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync() |> liftAsync + let! _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync("SemanticClassificationServiceTests") |> liftAsync return checkFileResults.GetSemanticClassification(None) } |> Async.RunSynchronously diff --git a/vsintegration/tests/UnitTests/SignatureHelpProviderTests.fs b/vsintegration/tests/UnitTests/SignatureHelpProviderTests.fs index f995d0742a0..77b8038e98c 100644 --- a/vsintegration/tests/UnitTests/SignatureHelpProviderTests.fs +++ b/vsintegration/tests/UnitTests/SignatureHelpProviderTests.fs @@ -56,7 +56,7 @@ let GetSignatureHelp (project:FSharpProject) (fileName:string) (caretPosition:in let document = RoslynTestHelpers.CreateDocument(fileName, sourceText, options = project.Options) let parseResults, checkFileResults = - document.GetFSharpParseAndCheckResultsAsync() + document.GetFSharpParseAndCheckResultsAsync("GetSignatureHelp") |> Async.RunSynchronously let paramInfoLocations = parseResults.FindParameterLocations(Position.fromZ caretLinePos.Line caretLineColumn).Value @@ -103,7 +103,7 @@ let assertSignatureHelpForMethodCalls (fileContents: string) (marker: string) (e let document = RoslynTestHelpers.CreateDocument(filePath, sourceText, options = projectOptions) let parseResults, checkFileResults = - document.GetFSharpParseAndCheckResultsAsync() + document.GetFSharpParseAndCheckResultsAsync("assertSignatureHelpForMethodCalls") |> Async.RunSynchronously let actual = @@ -135,7 +135,7 @@ let assertSignatureHelpForFunctionApplication (fileContents: string) (marker: st let document, sourceText = RoslynTestHelpers.CreateDocument(filePath, fileContents) let parseResults, checkFileResults = - document.GetFSharpParseAndCheckResultsAsync() + document.GetFSharpParseAndCheckResultsAsync("assertSignatureHelpForFunctionApplication") |> Async.RunSynchronously let adjustedColumnInSource = @@ -416,7 +416,7 @@ M.f let document, sourceText = RoslynTestHelpers.CreateDocument(filePath, fileContents) let parseResults, checkFileResults = - document.GetFSharpParseAndCheckResultsAsync() + document.GetFSharpParseAndCheckResultsAsync("function application in single pipeline with no additional args") |> Async.RunSynchronously let adjustedColumnInSource =