Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[VS] Consolidate Roslyn workspace and FCS #11694

Merged
merged 28 commits into from
Jun 24, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ open FSharp.Compiler.Tokenization

#nowarn "57"

type SemanticClassificationData = SemanticClassificationView option
type SemanticClassificationData = SemanticClassificationView
type SemanticClassificationLookup = IReadOnlyDictionary<int, ResizeArray<SemanticClassificationItem>>

[<Sealed>]
Expand Down Expand Up @@ -68,10 +68,7 @@ type DocumentCache<'Value when 'Value : not struct>() =
type internal FSharpClassificationService
[<ImportingConstructor>]
(
checkerProvider: FSharpCheckerProvider,
projectInfoManager: FSharpProjectOptionsManager
) =
static let userOpName = "SemanticColorization"

static let getLexicalClassifications(filePath: string, defines, text: SourceText, textSpan: TextSpan, ct) =
let text = text.GetSubText(textSpan)
Expand Down Expand Up @@ -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<int, ResizeArray<SemanticClassificationItem>>()
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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stylistically I'd prefer d.ForEach(fun dataItem -> ...) rather than using a local function. But that's unrelated to this PR


System.Collections.ObjectModel.ReadOnlyDictionary lookup :> IReadOnlyDictionary<_, _>

Expand All @@ -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.GetFSharpQuickDefines()
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
Expand All @@ -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<ClassifiedSpan>, cancellationToken: CancellationToken) =
asyncMaybe {
member this.AddSemanticClassificationsAsync(document: Document, textSpan: TextSpan, result: List<ClassifiedSpan>, cancellationToken: CancellationToken) =
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(nameof(FSharpClassificationService))
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(nameof(IFSharpClassificationService))
let targetRange = RoslynHelpers.TextSpanToFSharpRange(document.FilePath, textSpan, sourceText)
let classificationData = checkResults.GetSemanticClassification (Some targetRange)
addSemanticClassification sourceText textSpan classificationData result
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,9 @@ open FSharp.Compiler.CodeAnalysis
type internal FSharpAddMissingFunKeywordCodeFixProvider
[<ImportingConstructor>]
(
projectInfoManager: FSharpProjectOptionsManager
) =
inherit CodeFixProvider()

static let userOpName = "AddMissingFunKeyword"

let fixableDiagnosticIds = set ["FS0010"]

override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds
Expand All @@ -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.GetFSharpCompilationDefinesAsync(nameof(FSharpAddMissingFunKeywordCodeFixProvider)) |> liftAsync

let adjustedPosition =
let rec loop ch pos =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,9 @@ open Microsoft.CodeAnalysis.CodeActions
type internal FSharpAddMissingRecToMutuallyRecFunctionsCodeFixProvider
[<ImportingConstructor>]
(
projectInfoManager: FSharpProjectOptionsManager
) =
inherit CodeFixProvider()

static let userOpName = "AddMissingRecToMutuallyRecFunctions"
let fixableDiagnosticIds = set ["FS0576"]

let createCodeFix (context: CodeFixContext, symbolName: string, titleFormat: string, textChange: TextChange, diagnostics: ImmutableArray<Diagnostic>) =
Expand All @@ -45,9 +43,8 @@ type internal FSharpAddMissingRecToMutuallyRecFunctionsCodeFixProvider

override _.RegisterCodeFixesAsync context =
asyncMaybe {
let! defines = context.Document.GetFSharpCompilationDefinesAsync(nameof(FSharpAddMissingRecToMutuallyRecFunctionsCodeFixProvider)) |> 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 =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,12 @@ open FSharp.Compiler.Text
type internal FSharpAddOpenCodeFixProvider
[<ImportingConstructor>]
(
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) =
Expand Down Expand Up @@ -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(nameof(FSharpAddOpenCodeFixProvider)) |> 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.GetFSharpCompilationDefinesAsync(nameof(FSharpAddOpenCodeFixProvider)) |> liftAsync

let! symbol =
maybe {
Expand All @@ -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
Expand All @@ -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
Expand All @@ -138,10 +134,10 @@ 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 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,9 @@ open Microsoft.CodeAnalysis.CodeActions
type internal FSharpAddTypeAnnotationToObjectOfIndeterminateTypeFixProvider
[<ImportingConstructor>]
(
checkerProvider: FSharpCheckerProvider,
projectInfoManager: FSharpProjectOptionsManager
) =
inherit CodeFixProvider()

static let userOpName = "AddTypeAnnotationToObjectOfIndeterminateType"

let fixableDiagnosticIds = set ["FS0072"; "FS3245"]

override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds
Expand All @@ -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, nameof(FSharpAddTypeAnnotationToObjectOfIndeterminateTypeFixProvider))

let! _, checkFileResults = document.GetFSharpParseAndCheckResultsAsync(nameof(FSharpAddTypeAnnotationToObjectOfIndeterminateTypeFixProvider)) |> 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,18 @@ open Microsoft.CodeAnalysis.CodeFixes
type internal FSharpChangeRefCellDerefToNotExpressionCodeFixProvider
[<ImportingConstructor>]
(
checkerProvider: FSharpCheckerProvider,
projectInfoManager: FSharpProjectOptionsManager
) =
inherit CodeFixProvider()

static let userOpName = "FSharpChangeRefCellDerefToNotExpressionCodeFix"
let fixableDiagnosticIds = set ["FS0001"]

override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds

override this.RegisterCodeFixesAsync context : Task =
asyncMaybe {
let document = context.Document
let! parsingOptions, _ = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, context.CancellationToken, userOpName)
let! parseResults = document.GetFSharpParseResultsAsync(nameof(FSharpChangeRefCellDerefToNotExpressionCodeFixProvider)) |> 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,16 @@ open Microsoft.CodeAnalysis.CodeFixes
type internal FSharpConvertCSharpLambdaToFSharpLambdaCodeFixProvider
[<ImportingConstructor>]
(
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(nameof(FSharpConvertCSharpLambdaToFSharpLambdaCodeFixProvider)) |> liftAsync

let! sourceText = context.Document.GetTextAsync(context.CancellationToken)
let errorRange = RoslynHelpers.TextSpanToFSharpRange(context.Document.FilePath, context.Span, sourceText)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,17 @@ open Microsoft.CodeAnalysis.CodeActions
type internal FSharpConvertToAnonymousRecordCodeFixProvider
[<ImportingConstructor>]
(
checkerProvider: FSharpCheckerProvider,
projectInfoManager: FSharpProjectOptionsManager
) =
inherit CodeFixProvider()

static let userOpName = "ConvertToAnonymousRecord"

let fixableDiagnosticIds = set ["FS0039"]

override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds

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(nameof(FSharpConvertToAnonymousRecordCodeFixProvider)) |> liftAsync

let! sourceText = context.Document.GetTextAsync(context.CancellationToken)
let errorRange = RoslynHelpers.TextSpanToFSharpRange(document.FilePath, context.Span, sourceText)
Expand Down
Loading