diff --git a/vsintegration/src/FSharp.Editor/BlockComment/CommentUncommentService.fs b/vsintegration/src/FSharp.Editor/BlockComment/CommentUncommentService.fs index 4776a8a349bc64bc4312a42f9c6427dad27771f3..c686b7ac818356db6afeb0c22b0992840001da1b 100644 --- a/vsintegration/src/FSharp.Editor/BlockComment/CommentUncommentService.fs +++ b/vsintegration/src/FSharp.Editor/BlockComment/CommentUncommentService.fs @@ -5,7 +5,7 @@ open Microsoft.CodeAnalysis.Host.Mef open System.Composition [] -[, FSharpCommonConstants.FSharpLanguageName)>] +[, FSharpConstants.FSharpLanguageName)>] type CommentUncommentService() = interface ICommentUncommentService with member this.SingleLineCommentString = "//" diff --git a/vsintegration/src/FSharp.Editor/Classification/ColorizationService.fs b/vsintegration/src/FSharp.Editor/Classification/ColorizationService.fs index 3bb5f5e7d437ffd30feba88d3255033bd9cc7667..05d5e9441754a59d83e8110a8d23e8f3d14d4273 100644 --- a/vsintegration/src/FSharp.Editor/Classification/ColorizationService.fs +++ b/vsintegration/src/FSharp.Editor/Classification/ColorizationService.fs @@ -14,7 +14,7 @@ open Microsoft.CodeAnalysis.Text open Microsoft.FSharp.Compiler.SourceCodeServices -[, FSharpCommonConstants.FSharpLanguageName)>] +[, FSharpConstants.FSharpLanguageName)>] type internal FSharpColorizationService [] ( @@ -30,8 +30,8 @@ type internal FSharpColorizationService async { let defines = projectInfoManager.GetCompilationDefinesForEditingDocument(document) let! sourceText = document.GetTextAsync(cancellationToken) - result.AddRange(CommonHelpers.getColorizationData(document.Id, sourceText, textSpan, Some(document.FilePath), defines, cancellationToken)) - } |> CommonRoslynHelpers.StartAsyncUnitAsTask cancellationToken + result.AddRange(Tokenizer.getColorizationData(document.Id, sourceText, textSpan, Some(document.FilePath), defines, cancellationToken)) + } |> RoslynHelpers.StartAsyncUnitAsTask cancellationToken member this.AddSemanticClassificationsAsync(document: Document, textSpan: TextSpan, result: List, cancellationToken: CancellationToken) = asyncMaybe { @@ -39,14 +39,14 @@ type internal FSharpColorizationService let! sourceText = document.GetTextAsync(cancellationToken) let! _, _, checkResults = checkerProvider.Checker.ParseAndCheckDocument(document, options, sourceText = sourceText, allowStaleResults = false) // it's crucial to not return duplicated or overlapping `ClassifiedSpan`s because Find Usages service crashes. - let targetRange = CommonRoslynHelpers.TextSpanToFSharpRange(document.FilePath, textSpan, sourceText) + let targetRange = RoslynHelpers.TextSpanToFSharpRange(document.FilePath, textSpan, sourceText) let colorizationData = checkResults.GetSemanticClassification (Some targetRange) |> Array.distinctBy fst for (range, classificationType) in colorizationData do - let span = CommonHelpers.fixupSpan(sourceText, CommonRoslynHelpers.FSharpRangeToTextSpan(sourceText, range)) + let span = Tokenizer.fixupSpan(sourceText, RoslynHelpers.FSharpRangeToTextSpan(sourceText, range)) result.Add(ClassifiedSpan(span, FSharpClassificationTypes.getClassificationTypeName(classificationType))) } - |> Async.Ignore |> CommonRoslynHelpers.StartAsyncUnitAsTask cancellationToken + |> Async.Ignore |> RoslynHelpers.StartAsyncUnitAsTask cancellationToken // Do not perform classification if we don't have project options (#defines matter) member this.AdjustStaleClassification(_: SourceText, classifiedSpan: ClassifiedSpan) : ClassifiedSpan = classifiedSpan diff --git a/vsintegration/src/FSharp.Editor/CodeFix/AddNewKeywordToDisposableConstructorInvocation.fs b/vsintegration/src/FSharp.Editor/CodeFix/AddNewKeywordToDisposableConstructorInvocation.fs index 68c66becc8184b3d60ef795f63b3374594c79d6b..33ee8caf414faf3eca5add569db716105d27119d 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/AddNewKeywordToDisposableConstructorInvocation.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/AddNewKeywordToDisposableConstructorInvocation.fs @@ -11,7 +11,7 @@ open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes open Microsoft.CodeAnalysis.CodeActions -[] +[] type internal FSharpAddNewKeywordCodeFixProvider() = inherit CodeFixProvider() @@ -27,7 +27,7 @@ type internal FSharpAddNewKeywordCodeFixProvider() = async { let! sourceText = context.Document.GetTextAsync() return context.Document.WithText(sourceText.WithChanges(TextChange(TextSpan(context.Span.Start, 0), "new "))) - } |> CommonRoslynHelpers.StartAsyncAsTask(cancellationToken)), + } |> RoslynHelpers.StartAsyncAsTask(cancellationToken)), title), context.Diagnostics |> Seq.filter (fun x -> this.FixableDiagnosticIds.Contains x.Id) |> Seq.toImmutableArray) - } |> CommonRoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) + } |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) \ No newline at end of file diff --git a/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs b/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs index 8d0b7dc775541ceee9dbc12f772a57028ddb83d6..d8511fd9901e412615ac5d1a6ad889ecc78ebc8d 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/AddOpenCodeFixProvider.fs @@ -17,7 +17,7 @@ open Microsoft.FSharp.Compiler.Range open Microsoft.FSharp.Compiler.SourceCodeServices open Microsoft.FSharp.Compiler.AbstractIL.Internal.Library -[] +[] type internal FSharpAddOpenCodeFixProvider [] ( @@ -38,7 +38,7 @@ type internal FSharpAddOpenCodeFixProvider async { let! sourceText = context.Document.GetTextAsync() return context.Document.WithText(sourceText.Replace(context.Span, qualifier)) - } |> CommonRoslynHelpers.StartAsyncAsTask(cancellationToken)) + } |> RoslynHelpers.StartAsyncAsTask(cancellationToken)) let openNamespaceFix (context: CodeFixContext) ctx name ns multipleNames = let displayText = "open " + ns + if multipleNames then " (" + name + ")" else "" @@ -50,7 +50,7 @@ type internal FSharpAddOpenCodeFixProvider let! sourceText = context.Document.GetTextAsync() let changedText, _ = OpenDeclarationHelper.insertOpenDeclaration sourceText ctx ns return context.Document.WithText(changedText) - } |> CommonRoslynHelpers.StartAsyncAsTask(cancellationToken)), + } |> RoslynHelpers.StartAsyncAsTask(cancellationToken)), displayText) let getSuggestions (context: CodeFixContext) (candidates: (Entity * InsertContext) list) : unit = @@ -101,7 +101,7 @@ type internal FSharpAddOpenCodeFixProvider let! symbol = asyncMaybe { - let! lexerSymbol = CommonHelpers.getSymbolAtPosition(document.Id, sourceText, context.Span.End, document.FilePath, defines, SymbolLookupKind.Greedy) + let! lexerSymbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, context.Span.End, document.FilePath, defines, Tokenizer.SymbolLookupKind.Greedy) return! checkResults.GetSymbolUseAtLocation(Line.fromZ linePos.Line, lexerSymbol.Ident.idRange.EndColumn, line.ToString(), lexerSymbol.FullIsland) } |> liftAsync @@ -145,5 +145,5 @@ type internal FSharpAddOpenCodeFixProvider return entities |> Seq.map createEntity |> Seq.concat |> Seq.toList |> getSuggestions context } |> Async.Ignore - |> CommonRoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) + |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) \ No newline at end of file diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs b/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs index bb701001e3e0d2d15f2050cd4a4a956a2c60738d..2f5d48af43e6e043f060e67a3b762195d1388407 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/ImplementInterfaceCodeFixProvider.fs @@ -24,7 +24,7 @@ type internal InterfaceState = AppendBracketAt: int option Tokens: FSharpTokenInfo list } -[] +[] type internal FSharpImplementInterfaceCodeFixProvider [] ( @@ -125,7 +125,7 @@ type internal FSharpImplementInterfaceCodeFixProvider InterfaceStubGenerator.getImplementedMemberSignatures getMemberByLocation displayContext state.InterfaceData let newSourceText = applyImplementInterface sourceText state displayContext implementedMemberSignatures entity indentSize verboseMode return context.Document.WithText(newSourceText) - } |> CommonRoslynHelpers.StartAsyncAsTask(cancellationToken)), + } |> RoslynHelpers.StartAsyncAsTask(cancellationToken)), title) context.RegisterCodeFix(codeAction, diagnostics) @@ -146,7 +146,7 @@ type internal FSharpImplementInterfaceCodeFixProvider let defines = CompilerEnvironment.GetCompilationDefinesForEditing(context.Document.FilePath, options.OtherOptions |> Seq.toList) // 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 - let tokens = CommonHelpers.tokenizeLine(context.Document.Id, sourceText, context.Span.Start, context.Document.FilePath, defines) + let tokens = Tokenizer.tokenizeLine(context.Document.Id, sourceText, context.Span.Start, context.Document.FilePath, defines) let startLeftColumn = context.Span.Start - textLine.Start let rec tryFindIdentifierToken acc tokens = match tokens with @@ -169,11 +169,11 @@ type internal FSharpImplementInterfaceCodeFixProvider | _ -> Some context.Span.End let! interfaceState = queryInterfaceState appendBracketAt interfacePos tokens parsedInput - let! symbol = CommonHelpers.getSymbolAtPosition(context.Document.Id, sourceText, fixupPosition, context.Document.FilePath, defines, SymbolLookupKind.Greedy) + let! symbol = Tokenizer.getSymbolAtPosition(context.Document.Id, sourceText, fixupPosition, context.Document.FilePath, defines, Tokenizer.SymbolLookupKind.Greedy) let fcsTextLineNumber = textLine.LineNumber + 1 let lineContents = textLine.ToString() let! options = context.Document.GetOptionsAsync(cancellationToken) - let tabSize = options.GetOption(FormattingOptions.TabSize, FSharpCommonConstants.FSharpLanguageName) + let tabSize = options.GetOption(FormattingOptions.TabSize, FSharpConstants.FSharpLanguageName) let! symbolUse = checkFileResults.GetSymbolUseAtLocation(fcsTextLineNumber, symbol.Ident.idRange.EndColumn, lineContents, symbol.FullIsland) let! entity, displayContext = match symbolUse.Symbol with @@ -185,4 +185,4 @@ type internal FSharpImplementInterfaceCodeFixProvider registerSuggestions (context, checkFileResults, interfaceState, displayContext, entity, tabSize) } |> Async.Ignore - |> CommonRoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) + |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/PrefixUnusedValueWithUnderscore.fs b/vsintegration/src/FSharp.Editor/CodeFix/PrefixUnusedValueWithUnderscore.fs index db3516316096c57191f6bb99835552946347dac5..13c9a43c60655f7a2a9ef9b954cc994157ba9deb 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/PrefixUnusedValueWithUnderscore.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/PrefixUnusedValueWithUnderscore.fs @@ -13,7 +13,7 @@ open Microsoft.CodeAnalysis.CodeActions open Microsoft.FSharp.Compiler -[] +[] type internal FSharpPrefixUnusedValueWithUnderscoreCodeFixProvider() = inherit CodeFixProvider() let fixableDiagnosticIds = ["FS1182"] @@ -25,7 +25,7 @@ type internal FSharpPrefixUnusedValueWithUnderscoreCodeFixProvider() = async { let! sourceText = context.Document.GetTextAsync() return context.Document.WithText(sourceText.WithChanges(textChange)) - } |> CommonRoslynHelpers.StartAsyncAsTask(cancellationToken)), + } |> RoslynHelpers.StartAsyncAsTask(cancellationToken)), title) override __.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds @@ -41,4 +41,4 @@ type internal FSharpPrefixUnusedValueWithUnderscoreCodeFixProvider() = let diagnostics = context.Diagnostics |> Seq.filter (fun x -> fixableDiagnosticIds |> List.contains x.Id) |> Seq.toImmutableArray context.RegisterCodeFix(createCodeFix(SR.PrefixValueNameWithUnderscore.Value, context, TextChange(TextSpan(context.Span.Start, 0), "_")), diagnostics) context.RegisterCodeFix(createCodeFix(SR.RenameValueToUnderscore.Value, context, TextChange(context.Span, "_")), diagnostics) - } |> CommonRoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) \ No newline at end of file + } |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) \ No newline at end of file diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ProposeUppercaseLabel.fs b/vsintegration/src/FSharp.Editor/CodeFix/ProposeUppercaseLabel.fs index 3b20970944b275f4b270d137a3eec21c1e6f80bd..4b5abcef60d0a93d58c1133696dff23b02de37bc 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ProposeUppercaseLabel.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/ProposeUppercaseLabel.fs @@ -7,7 +7,7 @@ open System.Threading.Tasks open Microsoft.CodeAnalysis.CodeFixes open Microsoft.CodeAnalysis.CodeActions -[] +[] type internal FSharpProposeUpperCaseLabelCodeFixProvider [] ( @@ -27,4 +27,4 @@ type internal FSharpProposeUpperCaseLabelCodeFixProvider context.RegisterCodeFix( CodeAction.Create(title, solutionChanger, title), context.Diagnostics |> Seq.filter (fun x -> fixableDiagnosticIds |> List.contains x.Id) |> Seq.toImmutableArray) - } |> Async.Ignore |> CommonRoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) \ No newline at end of file + } |> Async.Ignore |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) \ No newline at end of file diff --git a/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedOpens.fs b/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedOpens.fs index a8a12a217b1b8b87282966a58abd4181ee0d1841..e9276b66ca526d62396d25dca30820b2e62ad07c 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedOpens.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedOpens.fs @@ -14,7 +14,7 @@ open Microsoft.CodeAnalysis.CodeActions open Microsoft.FSharp.Compiler.Range -[] +[] type internal FSharpRemoveUnusedOpensCodeFixProvider [] ( @@ -44,7 +44,7 @@ type internal FSharpRemoveUnusedOpensCodeFixProvider return document.WithText(sourceText.WithChanges(changes)) } |> Async.map (Option.defaultValue context.Document) - |> CommonRoslynHelpers.StartAsyncAsTask(cancellationToken)), + |> RoslynHelpers.StartAsyncAsTask(cancellationToken)), title) override __.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds @@ -53,7 +53,7 @@ type internal FSharpRemoveUnusedOpensCodeFixProvider async { let diagnostics = context.Diagnostics |> Seq.filter (fun x -> fixableDiagnosticIds |> List.contains x.Id) |> Seq.toImmutableArray context.RegisterCodeFix(createCodeFix(SR.RemoveUnusedOpens.Value, context), diagnostics) - } |> CommonRoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) + } |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) override __.GetFixAllProvider() = WellKnownFixAllProviders.BatchFixer \ No newline at end of file diff --git a/vsintegration/src/FSharp.Editor/CodeFix/ReplaceWithSuggestion.fs b/vsintegration/src/FSharp.Editor/CodeFix/ReplaceWithSuggestion.fs index e05da5d01bf39e745e6141b9a569bf09c9d8854a..916e635f5b4ea4e401ef82f37abcf7d2ff2e8060 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/ReplaceWithSuggestion.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/ReplaceWithSuggestion.fs @@ -13,7 +13,7 @@ open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes open Microsoft.CodeAnalysis.CodeActions -[] +[] type internal FSharpReplaceWithSuggestionCodeFixProvider() = inherit CodeFixProvider() let fixableDiagnosticIds = set ["FS0039"; "FS1129"; "FS0495"] @@ -26,7 +26,7 @@ type internal FSharpReplaceWithSuggestionCodeFixProvider() = async { let! sourceText = context.Document.GetTextAsync() return context.Document.WithText(sourceText.WithChanges(textChange)) - } |> CommonRoslynHelpers.StartAsyncAsTask(cancellationToken)), + } |> RoslynHelpers.StartAsyncAsTask(cancellationToken)), title) override __.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds @@ -53,4 +53,4 @@ type internal FSharpReplaceWithSuggestionCodeFixProvider() = context, TextChange(context.Span, replacement)) context.RegisterCodeFix(codefix, diagnostics)) - } |> CommonRoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) + } |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/SimplifyName.fs b/vsintegration/src/FSharp.Editor/CodeFix/SimplifyName.fs index 0a5c2aa671c287bebfd28481b4f2b812742f214d..66e8efe0126e059002ffac2b2023893b91ed035f 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/SimplifyName.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/SimplifyName.fs @@ -13,7 +13,7 @@ open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes open Microsoft.CodeAnalysis.CodeActions -[] +[] type internal FSharpSimplifyNameCodeFixProvider() = inherit CodeFixProvider() let fixableDiagnosticId = IDEDiagnosticIds.SimplifyNamesDiagnosticId @@ -25,7 +25,7 @@ type internal FSharpSimplifyNameCodeFixProvider() = async { let! sourceText = context.Document.GetTextAsync() return context.Document.WithText(sourceText.WithChanges(textChange)) - } |> CommonRoslynHelpers.StartAsyncAsTask(cancellationToken)), + } |> RoslynHelpers.StartAsyncAsTask(cancellationToken)), title) override __.FixableDiagnosticIds = ImmutableArray.Create(fixableDiagnosticId) @@ -42,4 +42,4 @@ type internal FSharpSimplifyNameCodeFixProvider() = createCodeFix(title, context, TextChange(context.Span, "")), ImmutableArray.Create(diagnostic)) } - |> CommonRoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) \ No newline at end of file + |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) \ No newline at end of file diff --git a/vsintegration/src/FSharp.Editor/Commands/FsiCommandService.fs b/vsintegration/src/FSharp.Editor/Commands/FsiCommandService.fs index e6b9564d98d60f63217eeb85da177194c2f15be7..ba86f7713eea065b425f11474c440cd176903216 100644 --- a/vsintegration/src/FSharp.Editor/Commands/FsiCommandService.fs +++ b/vsintegration/src/FSharp.Editor/Commands/FsiCommandService.fs @@ -73,7 +73,7 @@ type internal FsiCommandFilter(serviceProvider: System.IServiceProvider) = VSConstants.E_FAIL [)>] -[] +[] [] type internal FsiCommandFilterProvider [] ([)>] serviceProvider: System.IServiceProvider, diff --git a/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs b/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs index 30e5d13be46501a31e575f334d641462b41c530d..d411b8c69522ff5203cc3ba9d134fc1c5832ea9b 100644 --- a/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs +++ b/vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs @@ -14,7 +14,7 @@ open Microsoft.CodeAnalysis.Host.Mef open Microsoft.FSharp.Compiler.Range [] -[, FSharpCommonConstants.FSharpLanguageName)>] +[, FSharpConstants.FSharpLanguageName)>] type internal FSharpHelpContextService [] ( @@ -93,8 +93,8 @@ type internal FSharpHelpContextService } interface IHelpContextService with - member this.Language = FSharpCommonConstants.FSharpLanguageLongName - member this.Product = FSharpCommonConstants.FSharpLanguageLongName + member this.Language = FSharpConstants.FSharpLanguageLongName + member this.Product = FSharpConstants.FSharpLanguageLongName member this.GetHelpTermAsync(document, textSpan, cancellationToken) = asyncMaybe { @@ -103,11 +103,11 @@ type internal FSharpHelpContextService let! textVersion = document.GetTextVersionAsync(cancellationToken) let defines = projectInfoManager.GetCompilationDefinesForEditingDocument(document) let textLine = sourceText.Lines.GetLineFromPosition(textSpan.Start) - let tokens = CommonHelpers.getColorizationData(document.Id, sourceText, textLine.Span, Some document.Name, defines, cancellationToken) + let tokens = Tokenizer.getColorizationData(document.Id, sourceText, textLine.Span, Some document.Name, defines, cancellationToken) return! FSharpHelpContextService.GetHelpTerm(checkerProvider.Checker, sourceText, document.FilePath, options, textSpan, tokens, textVersion.GetHashCode()) } |> Async.map (Option.defaultValue "") - |> CommonRoslynHelpers.StartAsyncAsTask cancellationToken + |> RoslynHelpers.StartAsyncAsTask cancellationToken member this.FormatSymbol(_symbol) = Unchecked.defaultof<_> diff --git a/vsintegration/src/FSharp.Editor/Commands/XmlDocCommandService.fs b/vsintegration/src/FSharp.Editor/Commands/XmlDocCommandService.fs index 55b8169678299cf9ed22cfac708ee50d2182be53..dcca77d3ca0394748a9ab6f1cc533bb3b0ebbc33 100644 --- a/vsintegration/src/FSharp.Editor/Commands/XmlDocCommandService.fs +++ b/vsintegration/src/FSharp.Editor/Commands/XmlDocCommandService.fs @@ -109,7 +109,7 @@ type internal XmlDocCommandFilter VSConstants.E_FAIL [)>] -[] +[] [] type internal XmlDocCommandFilterProvider [] diff --git a/vsintegration/src/FSharp.Editor/Common/CommonHelpers.fs b/vsintegration/src/FSharp.Editor/Common/CommonHelpers.fs deleted file mode 100644 index 66776327267c5db5c4cf74487489105791ad7fdc..0000000000000000000000000000000000000000 --- a/vsintegration/src/FSharp.Editor/Common/CommonHelpers.fs +++ /dev/null @@ -1,889 +0,0 @@ -// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -namespace Microsoft.VisualStudio.FSharp.Editor - -open System -open System.Collections.Generic -open System.Threading -open System.Threading.Tasks -open System.Runtime.CompilerServices - -open Microsoft.CodeAnalysis -open Microsoft.CodeAnalysis.Classification -open Microsoft.CodeAnalysis.Text - -open Microsoft.VisualStudio.FSharp.LanguageService -open Microsoft.FSharp.Compiler -open Microsoft.FSharp.Compiler.Ast -open Microsoft.FSharp.Compiler.SourceCodeServices - -type internal ISetThemeColors = abstract member SetColors: unit -> unit - -[] -type internal LexerSymbolKind = - | Ident - | Operator - | Punctuation - | GenericTypeParameter - | StaticallyResolvedTypeParameter - | Other - -type internal LexerSymbol = - { Kind: LexerSymbolKind - /// Last part of `LongIdent` - Ident: Ident - /// All parts of `LongIdent` - FullIsland: string list } - member x.Range: Range.range = x.Ident.idRange - -[] -type internal SymbolLookupKind = - /// Position must lay inside symbol range. - | Precise - /// Position may lay one column outside of symbol range to the right. - | Greedy - -module internal CommonHelpers = - type private SourceLineData(lineStart: int, lexStateAtStartOfLine: FSharpTokenizerLexState, lexStateAtEndOfLine: FSharpTokenizerLexState, - hashCode: int, classifiedSpans: IReadOnlyList, tokens: FSharpTokenInfo list) = - member val LineStart = lineStart - member val LexStateAtStartOfLine = lexStateAtStartOfLine - member val LexStateAtEndOfLine = lexStateAtEndOfLine - member val HashCode = hashCode - member val ClassifiedSpans = classifiedSpans - member val Tokens = tokens - - member data.IsValid(textLine: TextLine) = - data.LineStart = textLine.Start && - let lineContents = textLine.Text.ToString(textLine.Span) - data.HashCode = lineContents.GetHashCode() - - type private SourceTextData(approxLines: int) = - let data = ResizeArray(approxLines) - let extendTo i = - if i >= data.Count then - data.Capacity <- i + 1 - for j in data.Count .. i do - data.Add(None) - member x.Item - with get (i:int) = extendTo i; data.[i] - and set (i:int) v = extendTo i; data.[i] <- v - - member x.ClearFrom(n) = - let mutable i = n - while i < data.Count && data.[i].IsSome do - data.[i] <- None - i <- i + 1 - - let private dataCache = ConditionalWeakTable() - - let internal compilerTokenToRoslynToken(colorKind: FSharpTokenColorKind) : string = - match colorKind with - | FSharpTokenColorKind.Comment -> ClassificationTypeNames.Comment - | FSharpTokenColorKind.Identifier -> ClassificationTypeNames.Identifier - | FSharpTokenColorKind.Keyword -> ClassificationTypeNames.Keyword - | FSharpTokenColorKind.String -> ClassificationTypeNames.StringLiteral - | FSharpTokenColorKind.Text -> ClassificationTypeNames.Text - | FSharpTokenColorKind.UpperIdentifier -> ClassificationTypeNames.Identifier - | FSharpTokenColorKind.Number -> ClassificationTypeNames.NumericLiteral - | FSharpTokenColorKind.InactiveCode -> ClassificationTypeNames.ExcludedCode - | FSharpTokenColorKind.PreprocessorKeyword -> ClassificationTypeNames.PreprocessorKeyword - | FSharpTokenColorKind.Operator -> ClassificationTypeNames.Operator - | FSharpTokenColorKind.Punctuation -> ClassificationTypeNames.Punctuation - | FSharpTokenColorKind.Default | _ -> ClassificationTypeNames.Text - - let private scanSourceLine(sourceTokenizer: FSharpSourceTokenizer, textLine: TextLine, lineContents: string, lexState: FSharpTokenizerLexState) : SourceLineData = - let colorMap = Array.create textLine.Span.Length ClassificationTypeNames.Text - let lineTokenizer = sourceTokenizer.CreateLineTokenizer(lineContents) - let tokens = ResizeArray() - - let scanAndColorNextToken(lineTokenizer: FSharpLineTokenizer, lexState: Ref) : Option = - let tokenInfoOption, nextLexState = lineTokenizer.ScanToken(lexState.Value) - lexState.Value <- nextLexState - if tokenInfoOption.IsSome then - let classificationType = compilerTokenToRoslynToken(tokenInfoOption.Value.ColorClass) - for i = tokenInfoOption.Value.LeftColumn to tokenInfoOption.Value.RightColumn do - Array.set colorMap i classificationType - tokens.Add tokenInfoOption.Value - tokenInfoOption - - let previousLexState = ref lexState - let mutable tokenInfoOption = scanAndColorNextToken(lineTokenizer, previousLexState) - while tokenInfoOption.IsSome do - tokenInfoOption <- scanAndColorNextToken(lineTokenizer, previousLexState) - - let mutable startPosition = 0 - let mutable endPosition = startPosition - let classifiedSpans = new List() - - while startPosition < colorMap.Length do - let classificationType = colorMap.[startPosition] - endPosition <- startPosition - while endPosition < colorMap.Length && classificationType = colorMap.[endPosition] do - endPosition <- endPosition + 1 - let textSpan = new TextSpan(textLine.Start + startPosition, endPosition - startPosition) - classifiedSpans.Add(new ClassifiedSpan(classificationType, textSpan)) - startPosition <- endPosition - - SourceLineData(textLine.Start, lexState, previousLexState.Value, lineContents.GetHashCode(), classifiedSpans, List.ofSeq tokens) - - let getColorizationData(documentKey: DocumentId, sourceText: SourceText, textSpan: TextSpan, fileName: string option, defines: string list, - cancellationToken: CancellationToken) : List = - try - let sourceTokenizer = FSharpSourceTokenizer(defines, fileName) - let lines = sourceText.Lines - // We keep incremental data per-document. When text changes we correlate text line-by-line (by hash codes of lines) - let sourceTextData = dataCache.GetValue(documentKey, fun key -> SourceTextData(lines.Count)) - - let startLine = lines.GetLineFromPosition(textSpan.Start).LineNumber - let endLine = lines.GetLineFromPosition(textSpan.End).LineNumber - // Go backwards to find the last cached scanned line that is valid - let scanStartLine = - let mutable i = startLine - while i > 0 && (match sourceTextData.[i] with Some data -> not (data.IsValid(lines.[i])) | None -> true) do - i <- i - 1 - i - // Rescan the lines if necessary and report the information - let result = new List() - let mutable lexState = if scanStartLine = 0 then 0L else sourceTextData.[scanStartLine - 1].Value.LexStateAtEndOfLine - - for i = scanStartLine to endLine do - cancellationToken.ThrowIfCancellationRequested() - let textLine = lines.[i] - let lineContents = textLine.Text.ToString(textLine.Span) - - let lineData = - // We can reuse the old data when - // 1. the line starts at the same overall position - // 2. the hash codes match - // 3. the start-of-line lex states are the same - match sourceTextData.[i] with - | Some data when data.IsValid(textLine) && data.LexStateAtStartOfLine = lexState -> - data - | _ -> - // Otherwise, we recompute - let newData = scanSourceLine(sourceTokenizer, textLine, lineContents, lexState) - sourceTextData.[i] <- Some newData - newData - - lexState <- lineData.LexStateAtEndOfLine - - if startLine <= i then - result.AddRange(lineData.ClassifiedSpans |> Seq.filter(fun token -> - textSpan.Contains(token.TextSpan.Start) || - textSpan.Contains(token.TextSpan.End - 1) || - (token.TextSpan.Start <= textSpan.Start && textSpan.End <= token.TextSpan.End))) - - // If necessary, invalidate all subsequent lines after endLine - if endLine < lines.Count - 1 then - match sourceTextData.[endLine+1] with - | Some data -> - if data.LexStateAtStartOfLine <> lexState then - sourceTextData.ClearFrom (endLine+1) - | None -> () - result - with - | :? System.OperationCanceledException -> reraise() - | ex -> - Assert.Exception(ex) - List() - - type private DraftToken = - { Kind: LexerSymbolKind - Token: FSharpTokenInfo - RightColumn: int } - static member inline Create kind token = - { Kind = kind; Token = token; RightColumn = token.LeftColumn + token.FullMatchedLength - 1 } - - /// Returns symbol at a given position. - let private getSymbolFromTokens (fileName: string, tokens: FSharpTokenInfo list, linePos: LinePosition, lineStr: string, lookupKind: SymbolLookupKind) : LexerSymbol option = - let isIdentifier t = t.CharClass = FSharpTokenCharKind.Identifier - let isOperator t = t.ColorClass = FSharpTokenColorKind.Operator - let isPunctuation t = t.ColorClass = FSharpTokenColorKind.Punctuation - - let inline (|GenericTypeParameterPrefix|StaticallyResolvedTypeParameterPrefix|Other|) (token: FSharpTokenInfo) = - if token.Tag = FSharpTokenTag.QUOTE then GenericTypeParameterPrefix - elif token.Tag = FSharpTokenTag.INFIX_AT_HAT_OP then - // The lexer return INFIX_AT_HAT_OP token for both "^" and "@" symbols. - // We have to check the char itself to distinguish one from another. - if token.FullMatchedLength = 1 && lineStr.[token.LeftColumn] = '^' then - StaticallyResolvedTypeParameterPrefix - else Other - else Other - - // Operators: Filter out overlapped operators (>>= operator is tokenized as three distinct tokens: GREATER, GREATER, EQUALS. - // Each of them has FullMatchedLength = 3. So, we take the first GREATER and skip the other two). - // - // Generic type parameters: we convert QUOTE + IDENT tokens into single IDENT token, altering its LeftColumn - // and FullMathedLength (for "'type" which is tokenized as (QUOTE, left=2) + (IDENT, left=3, length=4) - // we'll get (IDENT, left=2, length=5). - // - // Statically resolved type parameters: we convert INFIX_AT_HAT_OP + IDENT tokens into single IDENT token, altering its LeftColumn - // and FullMathedLength (for "^type" which is tokenized as (INFIX_AT_HAT_OP, left=2) + (IDENT, left=3, length=4) - // we'll get (IDENT, left=2, length=5). - let tokens = - let tokensCount = tokens.Length - tokens - |> List.foldi (fun (acc, lastToken) index (token: FSharpTokenInfo) -> - match lastToken with - | Some t when token.LeftColumn <= t.RightColumn -> acc, lastToken - | _ -> - let isLastToken = index = tokensCount - 1 - match token with - | GenericTypeParameterPrefix when not isLastToken -> acc, Some (DraftToken.Create LexerSymbolKind.GenericTypeParameter token) - | StaticallyResolvedTypeParameterPrefix when not isLastToken -> acc, Some (DraftToken.Create LexerSymbolKind.StaticallyResolvedTypeParameter token) - | _ -> - let draftToken = - match lastToken with - | Some { Kind = LexerSymbolKind.GenericTypeParameter | LexerSymbolKind.StaticallyResolvedTypeParameter as kind } when isIdentifier token -> - DraftToken.Create kind { token with LeftColumn = token.LeftColumn - 1 - FullMatchedLength = token.FullMatchedLength + 1 } - // ^ operator - | Some { Kind = LexerSymbolKind.StaticallyResolvedTypeParameter } -> - DraftToken.Create LexerSymbolKind.Operator { token with LeftColumn = token.LeftColumn - 1 - FullMatchedLength = 1 } - | _ -> - let kind = - if isOperator token then LexerSymbolKind.Operator - elif isIdentifier token then LexerSymbolKind.Ident - elif isPunctuation token then LexerSymbolKind.Punctuation - else LexerSymbolKind.Other - - DraftToken.Create kind token - draftToken :: acc, Some draftToken - ) ([], None) - |> fst - - // One or two tokens that in touch with the cursor (for "let x|(g) = ()" the tokens will be "x" and "(") - let tokensUnderCursor = - let rightColumnCorrection = - match lookupKind with - | SymbolLookupKind.Precise -> 0 - | SymbolLookupKind.Greedy -> 1 - - tokens |> List.filter (fun x -> x.Token.LeftColumn <= linePos.Character && (x.RightColumn + rightColumnCorrection) >= linePos.Character) - - // Select IDENT token. If failed, select OPERATOR token. - tokensUnderCursor - |> List.tryFind (fun { DraftToken.Kind = k } -> - match k with - | LexerSymbolKind.Ident - | LexerSymbolKind.GenericTypeParameter - | LexerSymbolKind.StaticallyResolvedTypeParameter -> true - | _ -> false) - |> Option.orElseWith (fun _ -> tokensUnderCursor |> List.tryFind (fun { DraftToken.Kind = k } -> k = LexerSymbolKind.Operator)) - |> Option.map (fun token -> - let plid, _ = QuickParse.GetPartialLongNameEx(lineStr, token.RightColumn) - let identStr = lineStr.Substring(token.Token.LeftColumn, token.Token.FullMatchedLength) - { Kind = token.Kind - Ident = - Ident - (identStr, - Range.mkRange - fileName - (Range.mkPos (linePos.Line + 1) token.Token.LeftColumn) - (Range.mkPos (linePos.Line + 1) (token.RightColumn + 1))) - FullIsland = plid @ [identStr] }) - - let private getCachedSourceLineData(documentKey: DocumentId, sourceText: SourceText, position: int, fileName: string, defines: string list) = - let textLine = sourceText.Lines.GetLineFromPosition(position) - let textLinePos = sourceText.Lines.GetLinePosition(position) - let lineNumber = textLinePos.Line + 1 // FCS line number - let sourceTokenizer = FSharpSourceTokenizer(defines, Some fileName) - let lines = sourceText.Lines - // We keep incremental data per-document. When text changes we correlate text line-by-line (by hash codes of lines) - let sourceTextData = dataCache.GetValue(documentKey, fun key -> SourceTextData(lines.Count)) - // Go backwards to find the last cached scanned line that is valid - let scanStartLine = - let mutable i = min (lines.Count - 1) lineNumber - while i > 0 && - (match sourceTextData.[i] with - | Some data -> not (data.IsValid(lines.[i])) - | None -> true - ) do - i <- i - 1 - i - let lexState = if scanStartLine = 0 then 0L else sourceTextData.[scanStartLine - 1].Value.LexStateAtEndOfLine - let lineContents = textLine.Text.ToString(textLine.Span) - - // We can reuse the old data when - // 1. the line starts at the same overall position - // 2. the hash codes match - // 3. the start-of-line lex states are the same - match sourceTextData.[lineNumber] with - | Some data when data.IsValid(textLine) && data.LexStateAtStartOfLine = lexState -> - data, textLinePos, lineContents - | _ -> - // Otherwise, we recompute - let newData = scanSourceLine(sourceTokenizer, textLine, lineContents, lexState) - sourceTextData.[lineNumber] <- Some newData - newData, textLinePos, lineContents - - let tokenizeLine (documentKey, sourceText, position, fileName, defines) = - try - let lineData, _, _ = getCachedSourceLineData(documentKey, sourceText, position, fileName, defines) - lineData.Tokens - with - | ex -> - Assert.Exception(ex) - [] - - let getSymbolAtPosition(documentKey: DocumentId, sourceText: SourceText, position: int, fileName: string, defines: string list, lookupKind: SymbolLookupKind) : LexerSymbol option = - try - let lineData, textLinePos, lineContents = getCachedSourceLineData(documentKey, sourceText, position, fileName, defines) - getSymbolFromTokens(fileName, lineData.Tokens, textLinePos, lineContents, lookupKind) - with - | :? System.OperationCanceledException -> reraise() - | ex -> - Assert.Exception(ex) - None - - /// Fix invalid span if it appears to have redundant suffix and prefix. - let fixupSpan (sourceText: SourceText, span: TextSpan) : TextSpan = - let text = sourceText.GetSubText(span).ToString() - match text.LastIndexOf '.' with - | -1 | 0 -> span - | index -> TextSpan(span.Start + index + 1, text.Length - index - 1) - - let isValidNameForSymbol (lexerSymbolKind: LexerSymbolKind, symbol: FSharpSymbol, name: string) : bool = - let doubleBackTickDelimiter = "``" - - let isDoubleBacktickIdent (s: string) = - let doubledDelimiter = 2 * doubleBackTickDelimiter.Length - if s.StartsWith(doubleBackTickDelimiter) && s.EndsWith(doubleBackTickDelimiter) && s.Length > doubledDelimiter then - let inner = s.Substring(doubleBackTickDelimiter.Length, s.Length - doubledDelimiter) - not (inner.Contains(doubleBackTickDelimiter)) - else false - - let isIdentifier (ident: string) = - if isDoubleBacktickIdent ident then - true - else - ident - |> Seq.mapi (fun i c -> i, c) - |> Seq.forall (fun (i, c) -> - if i = 0 then PrettyNaming.IsIdentifierFirstCharacter c - else PrettyNaming.IsIdentifierPartCharacter c) - - let isFixableIdentifier (s: string) = - not (String.IsNullOrEmpty s) && Lexhelp.Keywords.NormalizeIdentifierBackticks s |> isIdentifier - - let forbiddenChars = [| '.'; '+'; '$'; '&'; '['; ']'; '/'; '\\'; '*'; '\'' |] - - let isTypeNameIdent (s: string) = - not (String.IsNullOrEmpty s) && s.IndexOfAny forbiddenChars = -1 && isFixableIdentifier s - - let isUnionCaseIdent (s: string) = - isTypeNameIdent s && Char.IsUpper(s.Replace(doubleBackTickDelimiter, "").[0]) - - let isTypeParameter (prefix: char) (s: string) = - s.Length >= 2 && s.[0] = prefix && isIdentifier s.[1..] - - let isGenericTypeParameter = isTypeParameter ''' - let isStaticallyResolvedTypeParameter = isTypeParameter '^' - - match lexerSymbolKind, symbol with - | _, :? FSharpUnionCase -> isUnionCaseIdent name - | _, :? FSharpActivePatternCase -> - // Different from union cases, active patterns don't accept double-backtick identifiers - isFixableIdentifier name && not (String.IsNullOrEmpty name) && Char.IsUpper(name.[0]) - | LexerSymbolKind.Operator, _ -> PrettyNaming.IsOperatorName name - | LexerSymbolKind.Punctuation, _ -> PrettyNaming.IsPunctuation name - | LexerSymbolKind.GenericTypeParameter, _ -> isGenericTypeParameter name - | LexerSymbolKind.StaticallyResolvedTypeParameter, _ -> isStaticallyResolvedTypeParameter name - | (LexerSymbolKind.Ident | LexerSymbolKind.Other), _ -> - match symbol with - | :? FSharpEntity as e when e.IsClass || e.IsFSharpRecord || e.IsFSharpUnion || e.IsValueType || e.IsFSharpModule || e.IsInterface -> isTypeNameIdent name - | _ -> isFixableIdentifier name - -[] -type internal SymbolDeclarationLocation = - | CurrentDocument - | Projects of Project list * isLocalForProject: bool - -[] -module internal Extensions = - open System - open System.IO - open Microsoft.FSharp.Compiler.Ast - open Microsoft.FSharp.Compiler.Lib - open Microsoft.VisualStudio.FSharp.Editor.Logging - - type System.IServiceProvider with - - /// Retrieve a MEF Visual Studio Service of type 'T - member x.GetService<'T>() = x.GetService(typeof<'T>) :?> 'T - - /// Retrieve a SVs MEF Service of type 'S and cast it to type 'T - member x.GetService<'S,'T>() = x.GetService(typeof<'S>) :?> 'T - - type Path with - static member GetFullPathSafe path = - try Path.GetFullPath path - with _ -> path - - type CheckResults = - | Ready of (FSharpParseFileResults * FSharpCheckFileResults) option - | StillRunning of Async<(FSharpParseFileResults * FSharpCheckFileResults) option> - - let getFreshFileCheckResultsTimeoutMillis = GetEnvInteger "VFT_GetFreshFileCheckResultsTimeoutMillis" 1000 - - type FSharpChecker with - member this.ParseDocument(document: Document, options: FSharpProjectOptions, sourceText: string) = - asyncMaybe { - let! fileParseResults = this.ParseFileInProject(document.FilePath, sourceText, options) |> liftAsync - return! fileParseResults.ParseTree - } - - member this.ParseDocument(document: Document, options: FSharpProjectOptions, ?sourceText: SourceText) = - asyncMaybe { - let! sourceText = - match sourceText with - | Some x -> Task.FromResult x - | None -> document.GetTextAsync() - return! this.ParseDocument(document, options, sourceText.ToString()) - } - - member this.ParseAndCheckDocument(filePath: string, textVersionHash: int, sourceText: string, options: FSharpProjectOptions, allowStaleResults: bool) : Async<(FSharpParseFileResults * Ast.ParsedInput * FSharpCheckFileResults) option> = - let parseAndCheckFile = - async { - let! parseResults, checkFileAnswer = this.ParseAndCheckFileInProject(filePath, textVersionHash, sourceText, options) - return - match checkFileAnswer with - | FSharpCheckFileAnswer.Aborted -> - None - | FSharpCheckFileAnswer.Succeeded(checkFileResults) -> - Some (parseResults, checkFileResults) - } - - let tryGetFreshResultsWithTimeout() : Async = - async { - try - let! worker = Async.StartChild(parseAndCheckFile, getFreshFileCheckResultsTimeoutMillis) - let! result = worker - return Ready result - with :? TimeoutException -> - return StillRunning parseAndCheckFile - } - - let bindParsedInput(results: (FSharpParseFileResults * FSharpCheckFileResults) option) = - match results with - | Some(parseResults, checkResults) -> - match parseResults.ParseTree with - | Some parsedInput -> Some (parseResults, parsedInput, checkResults) - | None -> None - | None -> None - - if allowStaleResults then - async { - let! freshResults = tryGetFreshResultsWithTimeout() - - let! results = - match freshResults with - | Ready x -> async.Return x - | StillRunning worker -> - async { - match allowStaleResults, this.TryGetRecentCheckResultsForFile(filePath, options) with - | true, Some (parseResults, checkFileResults, _) -> - return Some (parseResults, checkFileResults) - | _ -> - return! worker - } - return bindParsedInput results - } - else parseAndCheckFile |> Async.map bindParsedInput - - member this.ParseAndCheckDocument(document: Document, options: FSharpProjectOptions, allowStaleResults: bool, ?sourceText: SourceText) : Async<(FSharpParseFileResults * Ast.ParsedInput * FSharpCheckFileResults) option> = - async { - let! cancellationToken = Async.CancellationToken - let! sourceText = - match sourceText with - | Some x -> Task.FromResult x - | None -> document.GetTextAsync() - let! textVersion = document.GetTextVersionAsync(cancellationToken) - return! this.ParseAndCheckDocument(document.FilePath, textVersion.GetHashCode(), sourceText.ToString(), options, allowStaleResults) - } - - type FSharpSymbol with - member this.IsInternalToProject = - match this with - | :? FSharpParameter -> true - | :? FSharpMemberOrFunctionOrValue as m -> not m.IsModuleValueOrMember || not m.Accessibility.IsPublic - | :? FSharpEntity as m -> not m.Accessibility.IsPublic - | :? FSharpGenericParameter -> true - | :? FSharpUnionCase as m -> not m.Accessibility.IsPublic - | :? FSharpField as m -> not m.Accessibility.IsPublic - | _ -> false - - type FSharpSymbolUse with - member this.GetDeclarationLocation (currentDocument: Document) : SymbolDeclarationLocation option = - if this.IsPrivateToFile then - Some SymbolDeclarationLocation.CurrentDocument - else - let isSymbolLocalForProject = this.Symbol.IsInternalToProject - - let declarationLocation = - match this.Symbol.ImplementationLocation with - | Some x -> Some x - | None -> this.Symbol.DeclarationLocation - - match declarationLocation with - | Some loc -> - let filePath = Path.GetFullPathSafe loc.FileName - let isScript = isScriptFile filePath - if isScript && filePath = currentDocument.FilePath then - Some SymbolDeclarationLocation.CurrentDocument - elif isScript then - // The standalone script might include other files via '#load' - // These files appear in project options and the standalone file - // should be treated as an individual project - Some (SymbolDeclarationLocation.Projects ([currentDocument.Project], isSymbolLocalForProject)) - else - let projects = - currentDocument.Project.Solution.GetDocumentIdsWithFilePath(filePath) - |> Seq.map (fun x -> x.ProjectId) - |> Seq.distinct - |> Seq.map currentDocument.Project.Solution.GetProject - |> Seq.toList - match projects with - | [] -> None - | projects -> Some (SymbolDeclarationLocation.Projects (projects, isSymbolLocalForProject)) - | None -> None - - member this.IsPrivateToFile = - let isPrivate = - match this.Symbol with - | :? FSharpMemberOrFunctionOrValue as m -> not m.IsModuleValueOrMember || m.Accessibility.IsPrivate - | :? FSharpEntity as m -> m.Accessibility.IsPrivate - | :? FSharpGenericParameter -> true - | :? FSharpUnionCase as m -> m.Accessibility.IsPrivate - | :? FSharpField as m -> m.Accessibility.IsPrivate - | _ -> false - - let declarationLocation = - match this.Symbol.SignatureLocation with - | Some x -> Some x - | _ -> - match this.Symbol.DeclarationLocation with - | Some x -> Some x - | _ -> this.Symbol.ImplementationLocation - - let declaredInTheFile = - match declarationLocation with - | Some declRange -> declRange.FileName = this.RangeAlternate.FileName - | _ -> false - - isPrivate && declaredInTheFile - - type FSharpMemberOrFunctionOrValue with - - member x.IsConstructor = x.CompiledName = ".ctor" - - member x.IsOperatorOrActivePattern = - let name = x.DisplayName - if name.StartsWith "( " && name.EndsWith " )" && name.Length > 4 - then name.Substring (2, name.Length - 4) |> String.forall (fun c -> c <> ' ') - else false - - member x.EnclosingEntitySafe = - try - Some x.EnclosingEntity - with :? InvalidOperationException -> None - - type FSharpEntity with - member x.AllBaseTypes = - let rec allBaseTypes (entity:FSharpEntity) = - [ - match entity.TryFullName with - | Some _ -> - match entity.BaseType with - | Some bt -> - yield bt - if bt.HasTypeDefinition then - yield! allBaseTypes bt.TypeDefinition - | _ -> () - | _ -> () - ] - allBaseTypes x - - type FSharpNavigationDeclarationItem with - member x.RoslynGlyph : Glyph = - match x.Glyph with - | FSharpGlyph.Class - | FSharpGlyph.Typedef - | FSharpGlyph.Type - | FSharpGlyph.Exception -> - match x.Access with - | Some SynAccess.Private -> Glyph.ClassPrivate - | Some SynAccess.Internal -> Glyph.ClassInternal - | _ -> Glyph.ClassPublic - | FSharpGlyph.Constant -> - match x.Access with - | Some SynAccess.Private -> Glyph.ConstantPrivate - | Some SynAccess.Internal -> Glyph.ConstantInternal - | _ -> Glyph.ConstantPublic - | FSharpGlyph.Delegate -> - match x.Access with - | Some SynAccess.Private -> Glyph.DelegatePrivate - | Some SynAccess.Internal -> Glyph.DelegateInternal - | _ -> Glyph.DelegatePublic - | FSharpGlyph.Union - | FSharpGlyph.Enum -> - match x.Access with - | Some SynAccess.Private -> Glyph.EnumPrivate - | Some SynAccess.Internal -> Glyph.EnumInternal - | _ -> Glyph.EnumPublic - | FSharpGlyph.EnumMember - | FSharpGlyph.Variable - | FSharpGlyph.Field -> - match x.Access with - | Some SynAccess.Private -> Glyph.FieldPrivate - | Some SynAccess.Internal -> Glyph.FieldInternal - | _ -> Glyph.FieldPublic - | FSharpGlyph.Event -> - match x.Access with - | Some SynAccess.Private -> Glyph.EventPrivate - | Some SynAccess.Internal -> Glyph.EventInternal - | _ -> Glyph.EventPublic - | FSharpGlyph.Interface -> - match x.Access with - | Some SynAccess.Private -> Glyph.InterfacePrivate - | Some SynAccess.Internal -> Glyph.InterfaceInternal - | _ -> Glyph.InterfacePublic - | FSharpGlyph.Method - | FSharpGlyph.OverridenMethod -> - match x.Access with - | Some SynAccess.Private -> Glyph.MethodPrivate - | Some SynAccess.Internal -> Glyph.MethodInternal - | _ -> Glyph.MethodPublic - | FSharpGlyph.Module -> - match x.Access with - | Some SynAccess.Private -> Glyph.ModulePrivate - | Some SynAccess.Internal -> Glyph.ModuleInternal - | _ -> Glyph.ModulePublic - | FSharpGlyph.NameSpace -> Glyph.Namespace - | FSharpGlyph.Property -> - match x.Access with - | Some SynAccess.Private -> Glyph.PropertyPrivate - | Some SynAccess.Internal -> Glyph.PropertyInternal - | _ -> Glyph.PropertyPublic - | FSharpGlyph.Struct -> - match x.Access with - | Some SynAccess.Private -> Glyph.StructurePrivate - | Some SynAccess.Internal -> Glyph.StructureInternal - | _ -> Glyph.StructurePublic - | FSharpGlyph.ExtensionMethod -> - match x.Access with - | Some SynAccess.Private -> Glyph.ExtensionMethodPrivate - | Some SynAccess.Internal -> Glyph.ExtensionMethodInternal - | _ -> Glyph.ExtensionMethodPublic - | FSharpGlyph.Error -> Glyph.Error - -/// Active patterns over `FSharpSymbolUse`. -module internal SymbolUse = - let (|ActivePatternCase|_|) (symbol : FSharpSymbolUse) = - match symbol.Symbol with - | :? FSharpActivePatternCase as ap-> ActivePatternCase(ap) |> Some - | _ -> None - - let private attributeSuffixLength = "Attribute".Length - - let (|Entity|_|) (symbol : FSharpSymbolUse) : (FSharpEntity * (* cleanFullNames *) string list) option = - match symbol.Symbol with - | :? FSharpEntity as ent -> - // strip generic parameters count suffix (List`1 => List) - let cleanFullName = - // `TryFullName` for type aliases is always `None`, so we have to make one by our own - if ent.IsFSharpAbbreviation then - [ent.AccessPath + "." + ent.DisplayName] - else - ent.TryFullName - |> Option.toList - |> List.map (fun fullName -> - if ent.GenericParameters.Count > 0 && fullName.Length > 2 then - fullName.[0..fullName.Length - 3] - else fullName) - - let cleanFullNames = - cleanFullName - |> List.collect (fun cleanFullName -> - if ent.IsAttributeType then - [cleanFullName; cleanFullName.[0..cleanFullName.Length - attributeSuffixLength - 1]] - else [cleanFullName] - ) - Some (ent, cleanFullNames) - | _ -> None - - let (|Field|_|) (symbol : FSharpSymbolUse) = - match symbol.Symbol with - | :? FSharpField as field-> Some field - | _ -> None - - let (|GenericParameter|_|) (symbol: FSharpSymbolUse) = - match symbol.Symbol with - | :? FSharpGenericParameter as gp -> Some gp - | _ -> None - - let (|MemberFunctionOrValue|_|) (symbol : FSharpSymbolUse) = - match symbol.Symbol with - | :? FSharpMemberOrFunctionOrValue as func -> Some func - | _ -> None - - let (|ActivePattern|_|) = function - | MemberFunctionOrValue m when m.IsActivePattern -> Some m | _ -> None - - let (|Parameter|_|) (symbol : FSharpSymbolUse) = - match symbol.Symbol with - | :? FSharpParameter as param -> Some param - | _ -> None - - let (|StaticParameter|_|) (symbol : FSharpSymbolUse) = - match symbol.Symbol with - | :? FSharpStaticParameter as sp -> Some sp - | _ -> None - - let (|UnionCase|_|) (symbol : FSharpSymbolUse) = - match symbol.Symbol with - | :? FSharpUnionCase as uc-> Some uc - | _ -> None - - //let (|Constructor|_|) = function - // | MemberFunctionOrValue func when func.IsConstructor || func.IsImplicitConstructor -> Some func - // | _ -> None - - let (|TypeAbbreviation|_|) = function - | Entity (entity, _) when entity.IsFSharpAbbreviation -> Some entity - | _ -> None - - let (|Class|_|) = function - | Entity (entity, _) when entity.IsClass -> Some entity - | Entity (entity, _) when entity.IsFSharp && - entity.IsOpaque && - not entity.IsFSharpModule && - not entity.IsNamespace && - not entity.IsDelegate && - not entity.IsFSharpUnion && - not entity.IsFSharpRecord && - not entity.IsInterface && - not entity.IsValueType -> Some entity - | _ -> None - - let (|Delegate|_|) = function - | Entity (entity, _) when entity.IsDelegate -> Some entity - | _ -> None - - let (|Event|_|) = function - | MemberFunctionOrValue symbol when symbol.IsEvent -> Some symbol - | _ -> None - - let (|Property|_|) = function - | MemberFunctionOrValue symbol when symbol.IsProperty || symbol.IsPropertyGetterMethod || symbol.IsPropertySetterMethod -> Some symbol - | _ -> None - - let inline private notCtorOrProp (symbol:FSharpMemberOrFunctionOrValue) = - not symbol.IsConstructor && not symbol.IsPropertyGetterMethod && not symbol.IsPropertySetterMethod - - let (|Method|_|) (symbolUse:FSharpSymbolUse) = - match symbolUse with - | MemberFunctionOrValue symbol when - symbol.IsModuleValueOrMember && - not symbolUse.IsFromPattern && - not symbol.IsOperatorOrActivePattern && - not symbol.IsPropertyGetterMethod && - not symbol.IsPropertySetterMethod -> Some symbol - | _ -> None - - let (|Function|_|) (symbolUse:FSharpSymbolUse) = - match symbolUse with - | MemberFunctionOrValue symbol when - notCtorOrProp symbol && - symbol.IsModuleValueOrMember && - not symbol.IsOperatorOrActivePattern && - not symbolUse.IsFromPattern -> - - match symbol.FullTypeSafe with - | Some fullType when fullType.IsFunctionType -> Some symbol - | _ -> None - | _ -> None - - let (|Operator|_|) (symbolUse:FSharpSymbolUse) = - match symbolUse with - | MemberFunctionOrValue symbol when - notCtorOrProp symbol && - not symbolUse.IsFromPattern && - not symbol.IsActivePattern && - symbol.IsOperatorOrActivePattern -> - - match symbol.FullTypeSafe with - | Some fullType when fullType.IsFunctionType -> Some symbol - | _ -> None - | _ -> None - - let (|Pattern|_|) (symbolUse:FSharpSymbolUse) = - match symbolUse with - | MemberFunctionOrValue symbol when - notCtorOrProp symbol && - not symbol.IsOperatorOrActivePattern && - symbolUse.IsFromPattern -> - - match symbol.FullTypeSafe with - | Some fullType when fullType.IsFunctionType ->Some symbol - | _ -> None - | _ -> None - - - let (|ClosureOrNestedFunction|_|) = function - | MemberFunctionOrValue symbol when - notCtorOrProp symbol && - not symbol.IsOperatorOrActivePattern && - not symbol.IsModuleValueOrMember -> - - match symbol.FullTypeSafe with - | Some fullType when fullType.IsFunctionType -> Some symbol - | _ -> None - | _ -> None - - - let (|Val|_|) = function - | MemberFunctionOrValue symbol when notCtorOrProp symbol && - not symbol.IsOperatorOrActivePattern -> - match symbol.FullTypeSafe with - | Some _fullType -> Some symbol - | _ -> None - | _ -> None - - let (|Enum|_|) = function - | Entity (entity, _) when entity.IsEnum -> Some entity - | _ -> None - - let (|Interface|_|) = function - | Entity (entity, _) when entity.IsInterface -> Some entity - | _ -> None - - let (|Module|_|) = function - | Entity (entity, _) when entity.IsFSharpModule -> Some entity - | _ -> None - - let (|Namespace|_|) = function - | Entity (entity, _) when entity.IsNamespace -> Some entity - | _ -> None - - let (|Record|_|) = function - | Entity (entity, _) when entity.IsFSharpRecord -> Some entity - | _ -> None - - let (|Union|_|) = function - | Entity (entity, _) when entity.IsFSharpUnion -> Some entity - | _ -> None - - let (|ValueType|_|) = function - | Entity (entity, _) when entity.IsValueType && not entity.IsEnum -> Some entity - | _ -> None - - let (|ComputationExpression|_|) (symbol:FSharpSymbolUse) = - if symbol.IsFromComputationExpression then Some symbol - else None - - let (|Attribute|_|) = function - | Entity (entity, _) when entity.IsAttributeType -> Some entity - | _ -> None \ No newline at end of file diff --git a/vsintegration/src/FSharp.Editor/Common/CommonRoslynHelpers.fs b/vsintegration/src/FSharp.Editor/Common/CommonRoslynHelpers.fs deleted file mode 100644 index c88459b4e8e4ede6d2f60fb9f4f28ef898a2ce0b..0000000000000000000000000000000000000000 --- a/vsintegration/src/FSharp.Editor/Common/CommonRoslynHelpers.fs +++ /dev/null @@ -1,344 +0,0 @@ -// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -namespace Microsoft.VisualStudio.FSharp.Editor - -open System -open System.Collections.Immutable -open System.Collections.Generic -open System.Threading.Tasks -open Microsoft.CodeAnalysis -open Microsoft.CodeAnalysis.Text -open Microsoft.CodeAnalysis.Diagnostics -open Microsoft.FSharp.Compiler -open Microsoft.FSharp.Compiler.Layout -open Microsoft.FSharp.Compiler.SourceCodeServices -open Microsoft.FSharp.Compiler.Range -open Microsoft.VisualStudio.FSharp.LanguageService - -module internal CommonRoslynHelpers = - - let FSharpRangeToTextSpan(sourceText: SourceText, range: range) = - // Roslyn TextLineCollection is zero-based, F# range lines are one-based - let startPosition = sourceText.Lines.[range.StartLine - 1].Start + range.StartColumn - let endPosition = sourceText.Lines.[range.EndLine - 1].Start + range.EndColumn - TextSpan(startPosition, endPosition - startPosition) - - let TryFSharpRangeToTextSpan(sourceText: SourceText, range: range) : TextSpan option = - try Some(FSharpRangeToTextSpan(sourceText, range)) - with e -> - //Assert.Exception(e) - None - - let TextSpanToFSharpRange(fileName: string, textSpan: TextSpan, sourceText: SourceText) : range = - let startLine = sourceText.Lines.GetLineFromPosition textSpan.Start - let endLine = sourceText.Lines.GetLineFromPosition textSpan.End - mkRange - fileName - (Pos.fromZ startLine.LineNumber (textSpan.Start - startLine.Start)) - (Pos.fromZ endLine.LineNumber (textSpan.End - endLine.Start)) - - let GetCompletedTaskResult(task: Task<'TResult>) = - if task.Status = TaskStatus.RanToCompletion then - task.Result - else - Assert.Exception(task.Exception.GetBaseException()) - raise(task.Exception.GetBaseException()) - - /// maps from `LayoutTag` of the F# Compiler to Roslyn `TextTags` for use in tooltips - let roslynTag = function - | LayoutTag.ActivePatternCase - | LayoutTag.ActivePatternResult - | LayoutTag.UnionCase - | LayoutTag.Enum -> TextTags.Enum - | LayoutTag.Alias - | LayoutTag.Class - | LayoutTag.Union - | LayoutTag.Record - | LayoutTag.UnknownType -> TextTags.Class - | LayoutTag.Delegate -> TextTags.Delegate - | LayoutTag.Event -> TextTags.Event - | LayoutTag.Field -> TextTags.Field - | LayoutTag.Interface -> TextTags.Interface - | LayoutTag.Struct -> TextTags.Struct - | LayoutTag.Keyword -> TextTags.Keyword - | LayoutTag.Local -> TextTags.Local - | LayoutTag.Member - | LayoutTag.ModuleBinding - | LayoutTag.RecordField - | LayoutTag.Property -> TextTags.Property - | LayoutTag.Method -> TextTags.Method - | LayoutTag.Namespace -> TextTags.Namespace - | LayoutTag.Module -> TextTags.Module - | LayoutTag.LineBreak -> TextTags.LineBreak - | LayoutTag.Space -> TextTags.Space - | LayoutTag.NumericLiteral -> TextTags.NumericLiteral - | LayoutTag.Operator -> TextTags.Operator - | LayoutTag.Parameter -> TextTags.Parameter - | LayoutTag.TypeParameter -> TextTags.TypeParameter - | LayoutTag.Punctuation -> TextTags.Punctuation - | LayoutTag.StringLiteral -> TextTags.StringLiteral - | LayoutTag.Text - | LayoutTag.UnknownEntity -> TextTags.Text - - let CollectTaggedText (list: List<_>) (t:TaggedText) = list.Add(TaggedText(roslynTag t.Tag, t.Text)) - - let StartAsyncAsTask cancellationToken computation = - let computation = - async { - try - return! computation - with e -> - Assert.Exception(e) - return Unchecked.defaultof<_> - } - Async.StartAsTask(computation, TaskCreationOptions.None, cancellationToken) - - let StartAsyncUnitAsTask cancellationToken (computation:Async) = - StartAsyncAsTask cancellationToken computation :> Task - - let SupportedDiagnostics() = - // We are constructing our own descriptors at run-time. Compiler service is already doing error formatting and localization. - let dummyDescriptor = DiagnosticDescriptor("0", String.Empty, String.Empty, String.Empty, DiagnosticSeverity.Error, true, null, null) - ImmutableArray.Create(dummyDescriptor) - - let ConvertError(error: FSharpErrorInfo, location: Location) = - let id = "FS" + error.ErrorNumber.ToString("0000") - let emptyString = LocalizableString.op_Implicit("") - let description = LocalizableString.op_Implicit(error.Message) - let severity = if error.Severity = FSharpErrorSeverity.Error then DiagnosticSeverity.Error else DiagnosticSeverity.Warning - let customTags = - match error.ErrorNumber with - | 1182 -> DiagnosticCustomTags.Unnecessary - | _ -> null - let descriptor = new DiagnosticDescriptor(id, emptyString, description, error.Subcategory, severity, true, emptyString, String.Empty, customTags) - Diagnostic.Create(descriptor, location) - - let inline (|Public|Internal|Protected|Private|) (a: FSharpAccessibility option) = - match a with - | None -> Public - | Some a -> - if a.IsPublic then Public - elif a.IsInternal then Internal - elif a.IsPrivate then Private - else Protected - - let FSharpGlyphToRoslynGlyph (glyph: FSharpGlyph, accessibility: FSharpAccessibility option) = - match glyph with - | FSharpGlyph.Class - | FSharpGlyph.Exception - | FSharpGlyph.Typedef - | FSharpGlyph.Type -> - match accessibility with - | Public -> Glyph.ClassPublic - | Internal -> Glyph.ClassInternal - | Protected -> Glyph.ClassProtected - | Private -> Glyph.ClassPrivate - | FSharpGlyph.Constant -> - match accessibility with - | Public -> Glyph.ConstantPublic - | Internal -> Glyph.ConstantInternal - | Protected -> Glyph.ConstantProtected - | Private -> Glyph.ConstantPrivate - | FSharpGlyph.Delegate -> - match accessibility with - | Public -> Glyph.DelegatePublic - | Internal -> Glyph.DelegateInternal - | Protected -> Glyph.DelegateProtected - | Private -> Glyph.DelegatePrivate - | FSharpGlyph.Enum - | FSharpGlyph.Union -> - match accessibility with - | Public -> Glyph.EnumPublic - | Internal -> Glyph.EnumInternal - | Protected -> Glyph.EnumProtected - | Private -> Glyph.EnumPrivate - | FSharpGlyph.EnumMember -> Glyph.EnumMember - | FSharpGlyph.Event -> - match accessibility with - | Public -> Glyph.EventPublic - | Internal -> Glyph.EventInternal - | Protected -> Glyph.EventProtected - | Private -> Glyph.EventPrivate - | FSharpGlyph.Field -> - match accessibility with - | Public -> Glyph.FieldPublic - | Internal -> Glyph.FieldInternal - | Protected -> Glyph.FieldProtected - | Private -> Glyph.FieldPrivate - | FSharpGlyph.Interface -> - match accessibility with - | Public -> Glyph.InterfacePublic - | Internal -> Glyph.InterfaceInternal - | Protected -> Glyph.InterfaceProtected - | Private -> Glyph.InterfacePrivate - | FSharpGlyph.Method - | FSharpGlyph.OverridenMethod -> - match accessibility with - | Public -> Glyph.MethodPublic - | Internal -> Glyph.MethodInternal - | Protected -> Glyph.MethodProtected - | Private -> Glyph.MethodPrivate - | FSharpGlyph.ExtensionMethod -> - match accessibility with - | Public -> Glyph.ExtensionMethodPublic - | Internal -> Glyph.ExtensionMethodInternal - | Protected -> Glyph.ExtensionMethodProtected - | Private -> Glyph.ExtensionMethodPrivate - | FSharpGlyph.Module -> - match accessibility with - | Public -> Glyph.ModulePublic - | Internal -> Glyph.ModuleInternal - | Protected -> Glyph.ModuleProtected - | Private -> Glyph.ModulePrivate - | FSharpGlyph.NameSpace -> Glyph.Namespace - | FSharpGlyph.Property -> - match accessibility with - | Public -> Glyph.PropertyPublic - | Internal -> Glyph.PropertyInternal - | Protected -> Glyph.PropertyProtected - | Private -> Glyph.PropertyPrivate - | FSharpGlyph.Struct -> - match accessibility with - | Public -> Glyph.StructurePublic - | Internal -> Glyph.StructureInternal - | Protected -> Glyph.StructureProtected - | Private -> Glyph.StructurePrivate - | FSharpGlyph.Variable -> Glyph.Local - | FSharpGlyph.Error -> Glyph.Error - - let GetGlyphForSymbol (symbol: FSharpSymbol, kind: LexerSymbolKind) = - match kind with - | LexerSymbolKind.Operator -> Glyph.Operator - | _ -> - match symbol with - | :? FSharpUnionCase as x -> - match Some x.Accessibility with - | Public -> Glyph.EnumPublic - | Internal -> Glyph.EnumInternal - | Protected -> Glyph.EnumProtected - | Private -> Glyph.EnumPrivate - | :? FSharpActivePatternCase -> Glyph.EnumPublic - | :? FSharpField as x -> - if x.IsLiteral then - match Some x.Accessibility with - | Public -> Glyph.ConstantPublic - | Internal -> Glyph.ConstantInternal - | Protected -> Glyph.ConstantProtected - | Private -> Glyph.ConstantPrivate - else - match Some x.Accessibility with - | Public -> Glyph.FieldPublic - | Internal -> Glyph.FieldInternal - | Protected -> Glyph.FieldProtected - | Private -> Glyph.FieldPrivate - | :? FSharpParameter -> Glyph.Parameter - | :? FSharpMemberOrFunctionOrValue as x -> - if x.LiteralValue.IsSome then - match Some x.Accessibility with - | Public -> Glyph.ConstantPublic - | Internal -> Glyph.ConstantInternal - | Protected -> Glyph.ConstantProtected - | Private -> Glyph.ConstantPrivate - elif x.IsExtensionMember then - match Some x.Accessibility with - | Public -> Glyph.ExtensionMethodPublic - | Internal -> Glyph.ExtensionMethodInternal - | Protected -> Glyph.ExtensionMethodProtected - | Private -> Glyph.ExtensionMethodPrivate - elif x.IsProperty || x.IsPropertyGetterMethod || x.IsPropertySetterMethod then - match Some x.Accessibility with - | Public -> Glyph.PropertyPublic - | Internal -> Glyph.PropertyInternal - | Protected -> Glyph.PropertyProtected - | Private -> Glyph.PropertyPrivate - elif x.IsEvent then - match Some x.Accessibility with - | Public -> Glyph.EventPublic - | Internal -> Glyph.EventInternal - | Protected -> Glyph.EventProtected - | Private -> Glyph.EventPrivate - else - match Some x.Accessibility with - | Public -> Glyph.MethodPublic - | Internal -> Glyph.MethodInternal - | Protected -> Glyph.MethodProtected - | Private -> Glyph.MethodPrivate - | :? FSharpEntity as x -> - if x.IsValueType then - match Some x.Accessibility with - | Public -> Glyph.StructurePublic - | Internal -> Glyph.StructureInternal - | Protected -> Glyph.StructureProtected - | Private -> Glyph.StructurePrivate - elif x.IsFSharpModule then - match Some x.Accessibility with - | Public -> Glyph.ModulePublic - | Internal -> Glyph.ModuleInternal - | Protected -> Glyph.ModuleProtected - | Private -> Glyph.ModulePrivate - elif x.IsEnum || x.IsFSharpUnion then - match Some x.Accessibility with - | Public -> Glyph.EnumPublic - | Internal -> Glyph.EnumInternal - | Protected -> Glyph.EnumProtected - | Private -> Glyph.EnumPrivate - elif x.IsInterface then - match Some x.Accessibility with - | Public -> Glyph.InterfacePublic - | Internal -> Glyph.InterfaceInternal - | Protected -> Glyph.InterfaceProtected - | Private -> Glyph.InterfacePrivate - elif x.IsDelegate then - match Some x.Accessibility with - | Public -> Glyph.DelegatePublic - | Internal -> Glyph.DelegateInternal - | Protected -> Glyph.DelegateProtected - | Private -> Glyph.DelegatePrivate - elif x.IsNamespace then - Glyph.Namespace - else - match Some x.Accessibility with - | Public -> Glyph.ClassPublic - | Internal -> Glyph.ClassInternal - | Protected -> Glyph.ClassProtected - | Private -> Glyph.ClassPrivate - | _ -> Glyph.None - - let RangeToLocation (r: range, sourceText: SourceText, filePath: string) : Location = - let linePositionSpan = LinePositionSpan(LinePosition(Line.toZ r.StartLine, r.StartColumn), LinePosition(Line.toZ r.EndLine, r.EndColumn)) - let textSpan = sourceText.Lines.GetTextSpan linePositionSpan - Location.Create(filePath, textSpan, linePositionSpan) - -module internal OpenDeclarationHelper = - /// - /// Inserts open declaration into `SourceText`. - /// - /// SourceText. - /// Insertion context. Typically returned from tryGetInsertionContext - /// Namespace to open. - let insertOpenDeclaration (sourceText: SourceText) (ctx: InsertContext) (ns: string) : SourceText * int = - let mutable minPos = None - - let insert line lineStr (sourceText: SourceText) : SourceText = - let pos = sourceText.Lines.[line].Start - minPos <- match minPos with None -> Some pos | Some oldPos -> Some (min oldPos pos) - sourceText.WithChanges(TextChange(TextSpan(pos, 0), lineStr + Environment.NewLine)) - - let getLineStr line = sourceText.Lines.[line].ToString().Trim() - let pos = ParsedInput.adjustInsertionPoint getLineStr ctx - let docLine = pos.Line - 1 - let lineStr = (String.replicate pos.Column " ") + "open " + ns - let sourceText = sourceText |> insert docLine lineStr - // if there's no a blank line between open declaration block and the rest of the code, we add one - let sourceText = - if sourceText.Lines.[docLine + 1].ToString().Trim() <> "" then - sourceText |> insert (docLine + 1) "" - else sourceText - let sourceText = - // for top level module we add a blank line between the module declaration and first open statement - if (pos.Column = 0 || ctx.ScopeKind = ScopeKind.Namespace) && docLine > 0 - && not (sourceText.Lines.[docLine - 1].ToString().Trim().StartsWith "open") then - sourceText |> insert docLine "" - else sourceText - sourceText, minPos |> Option.defaultValue 0 - diff --git a/vsintegration/src/FSharp.Editor/Common/CommonConstants.fs b/vsintegration/src/FSharp.Editor/Common/Constants.fs similarity index 96% rename from vsintegration/src/FSharp.Editor/Common/CommonConstants.fs rename to vsintegration/src/FSharp.Editor/Common/Constants.fs index 5b1db191019f24ed02169770004792d11c3a7856..6fb62e9f19369a734b4749e2b1b00adab20509db 100644 --- a/vsintegration/src/FSharp.Editor/Common/CommonConstants.fs +++ b/vsintegration/src/FSharp.Editor/Common/Constants.fs @@ -8,7 +8,7 @@ open System.Diagnostics open Microsoft.CodeAnalysis.Classification [] -module internal FSharpCommonConstants = +module internal FSharpConstants = [] /// "871D2A70-12A2-4e42-9440-425DD92A4116" diff --git a/vsintegration/src/FSharp.Editor/Common/ContentType.fs b/vsintegration/src/FSharp.Editor/Common/ContentType.fs index 041c346b0f20a05b6f624573f468dac7c731cb3b..d8465a60c5b5e96c4662a64281a2babeda84a7c9 100644 --- a/vsintegration/src/FSharp.Editor/Common/ContentType.fs +++ b/vsintegration/src/FSharp.Editor/Common/ContentType.fs @@ -9,19 +9,19 @@ open Microsoft.VisualStudio.Utilities module FSharpStaticTypeDefinitions = [] - [] + [] [] let FSharpContentTypeDefinition = ContentTypeDefinition() [] - [] + [] [] let FSharpSignatureHelpContentTypeDefinition = ContentTypeDefinition() -[] +[] type FSharpContentType [](contentTypeRegistry : IContentTypeRegistryService) = member this.contentTypeRegistryService = contentTypeRegistry interface IContentTypeLanguageService with member this.GetDefaultContentType() = - this.contentTypeRegistryService.GetContentType(FSharpCommonConstants.FSharpContentTypeName) + this.contentTypeRegistryService.GetContentType(FSharpConstants.FSharpContentTypeName) diff --git a/vsintegration/src/FSharp.Editor/Common/Extensions.fs b/vsintegration/src/FSharp.Editor/Common/Extensions.fs new file mode 100644 index 0000000000000000000000000000000000000000..cfd7d131177a829e95ad3a23af0c22132efa3641 --- /dev/null +++ b/vsintegration/src/FSharp.Editor/Common/Extensions.fs @@ -0,0 +1,202 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +[] +/// Type and Module Extensions +module internal Microsoft.VisualStudio.FSharp.Editor.Extensions + +open System +open System.IO +open Microsoft.CodeAnalysis +open Microsoft.FSharp.Compiler.Ast +open Microsoft.FSharp.Compiler.SourceCodeServices + + +type Path with + static member GetFullPathSafe path = + try Path.GetFullPath path + with _ -> path + + static member GetFileNameSafe path = + try Path.GetFileName path + with _ -> path + + +type System.IServiceProvider with + member x.GetService<'T>() = x.GetService(typeof<'T>) :?> 'T + member x.GetService<'S, 'T>() = x.GetService(typeof<'S>) :?> 'T + + +type FSharpNavigationDeclarationItem with + member x.RoslynGlyph : Glyph = + match x.Glyph with + | FSharpGlyph.Class + | FSharpGlyph.Typedef + | FSharpGlyph.Type + | FSharpGlyph.Exception -> + match x.Access with + | Some SynAccess.Private -> Glyph.ClassPrivate + | Some SynAccess.Internal -> Glyph.ClassInternal + | _ -> Glyph.ClassPublic + | FSharpGlyph.Constant -> + match x.Access with + | Some SynAccess.Private -> Glyph.ConstantPrivate + | Some SynAccess.Internal -> Glyph.ConstantInternal + | _ -> Glyph.ConstantPublic + | FSharpGlyph.Delegate -> + match x.Access with + | Some SynAccess.Private -> Glyph.DelegatePrivate + | Some SynAccess.Internal -> Glyph.DelegateInternal + | _ -> Glyph.DelegatePublic + | FSharpGlyph.Union + | FSharpGlyph.Enum -> + match x.Access with + | Some SynAccess.Private -> Glyph.EnumPrivate + | Some SynAccess.Internal -> Glyph.EnumInternal + | _ -> Glyph.EnumPublic + | FSharpGlyph.EnumMember + | FSharpGlyph.Variable + | FSharpGlyph.Field -> + match x.Access with + | Some SynAccess.Private -> Glyph.FieldPrivate + | Some SynAccess.Internal -> Glyph.FieldInternal + | _ -> Glyph.FieldPublic + | FSharpGlyph.Event -> + match x.Access with + | Some SynAccess.Private -> Glyph.EventPrivate + | Some SynAccess.Internal -> Glyph.EventInternal + | _ -> Glyph.EventPublic + | FSharpGlyph.Interface -> + match x.Access with + | Some SynAccess.Private -> Glyph.InterfacePrivate + | Some SynAccess.Internal -> Glyph.InterfaceInternal + | _ -> Glyph.InterfacePublic + | FSharpGlyph.Method + | FSharpGlyph.OverridenMethod -> + match x.Access with + | Some SynAccess.Private -> Glyph.MethodPrivate + | Some SynAccess.Internal -> Glyph.MethodInternal + | _ -> Glyph.MethodPublic + | FSharpGlyph.Module -> + match x.Access with + | Some SynAccess.Private -> Glyph.ModulePrivate + | Some SynAccess.Internal -> Glyph.ModuleInternal + | _ -> Glyph.ModulePublic + | FSharpGlyph.NameSpace -> Glyph.Namespace + | FSharpGlyph.Property -> + match x.Access with + | Some SynAccess.Private -> Glyph.PropertyPrivate + | Some SynAccess.Internal -> Glyph.PropertyInternal + | _ -> Glyph.PropertyPublic + | FSharpGlyph.Struct -> + match x.Access with + | Some SynAccess.Private -> Glyph.StructurePrivate + | Some SynAccess.Internal -> Glyph.StructureInternal + | _ -> Glyph.StructurePublic + | FSharpGlyph.ExtensionMethod -> + match x.Access with + | Some SynAccess.Private -> Glyph.ExtensionMethodPrivate + | Some SynAccess.Internal -> Glyph.ExtensionMethodInternal + | _ -> Glyph.ExtensionMethodPublic + | FSharpGlyph.Error -> Glyph.Error + + + +[] +module String = + + let getLines (str: string) = + use reader = new StringReader(str) + [| let mutable line = reader.ReadLine() + while not (isNull line) do + yield line + line <- reader.ReadLine() + if str.EndsWith("\n") then + // last trailing space not returned + // http://stackoverflow.com/questions/19365404/stringreader-omits-trailing-linebreak + yield String.Empty + |] + + +[] +module Option = + + let guard (x: bool) : Option = + if x then Some() else None + + let attempt (f: unit -> 'T) = try Some <| f() with _ -> None + + let inline ofNull value = + if obj.ReferenceEquals(value, null) then None else Some value + + /// Gets the option if Some x, otherwise try to get another value + let inline orTry f = + function + | Some x -> Some x + | None -> f() + + /// Gets the value if Some x, otherwise try to get another value by calling a function + let inline getOrTry f = + function + | Some x -> x + | None -> f() + + +[] +module List = + let foldi (folder : 'State -> int -> 'T -> 'State) (state : 'State) (xs : 'T list) = + let mutable state = state + let mutable i = 0 + for x in xs do + state <- folder state i x + i <- i + 1 + state + + +[] +module Seq = + open System.Collections.Immutable + + let toImmutableArray (xs: seq<'a>) : ImmutableArray<'a> = xs.ToImmutableArray() + + +[] +module Array = + /// Optimized arrays equality. ~100x faster than `array1 = array2` on strings. + /// ~2x faster for floats + /// ~0.8x slower for ints + let areEqual (xs: 'T []) (ys: 'T []) = + match xs, ys with + | null, null -> true + | [||], [||] -> true + | null, _ | _, null -> false + | _ when xs.Length <> ys.Length -> false + | _ -> + let mutable break' = false + let mutable i = 0 + let mutable result = true + while i < xs.Length && not break' do + if xs.[i] <> ys.[i] then + break' <- true + result <- false + i <- i + 1 + result + + /// check if subArray is found in the wholeArray starting + /// at the provided index + let isSubArray (subArray: 'T []) (wholeArray:'T []) index = + if isNull subArray || isNull wholeArray then false + elif subArray.Length = 0 then true + elif subArray.Length > wholeArray.Length then false + elif subArray.Length = wholeArray.Length then areEqual subArray wholeArray else + let rec loop subidx idx = + if subidx = subArray.Length then true + elif subArray.[subidx] = wholeArray.[idx] then loop (subidx+1) (idx+1) + else false + loop 0 index + + /// Returns true if one array has another as its subset from index 0. + let startsWith (prefix: _ []) (whole: _ []) = + isSubArray prefix whole 0 + + /// Returns true if one array has trailing elements equal to another's. + let endsWith (suffix: _ []) (whole: _ []) = + isSubArray suffix whole (whole.Length-suffix.Length) \ No newline at end of file diff --git a/vsintegration/src/FSharp.Editor/Common/Pervasive.fs b/vsintegration/src/FSharp.Editor/Common/Pervasive.fs index fdb0fc85502d0d09b38f016e49706d8c56162a64..dd68b5514e750d6215d1528aa2004edf8dd02dfa 100644 --- a/vsintegration/src/FSharp.Editor/Common/Pervasive.fs +++ b/vsintegration/src/FSharp.Editor/Common/Pervasive.fs @@ -3,7 +3,6 @@ module Microsoft.VisualStudio.FSharp.Editor.Pervasive open System open System.IO -open System.Threading open System.Threading.Tasks open System.Diagnostics @@ -20,36 +19,8 @@ let isScriptFile (filePath:string) = /// Path combination operator let () path1 path2 = Path.Combine (path1, path2) +type internal ISetThemeColors = abstract member SetColors: unit -> unit -type Path with - static member GetFullPathSafe path = - try Path.GetFullPath path - with _ -> path - - static member GetFileNameSafe path = - try Path.GetFileName path - with _ -> path - - -[] -module String = - - let getLines (str: string) = - use reader = new StringReader(str) - [| let mutable line = reader.ReadLine() - while not (isNull line) do - yield line - line <- reader.ReadLine() - if str.EndsWith("\n") then - // last trailing space not returned - // http://stackoverflow.com/questions/19365404/stringreader-omits-trailing-linebreak - yield String.Empty - |] - - -type System.IServiceProvider with - member x.GetService<'T>() = x.GetService(typeof<'T>) :?> 'T - member x.GetService<'S, 'T>() = x.GetService(typeof<'S>) :?> 'T [] type MaybeBuilder () = @@ -271,62 +242,3 @@ type AsyncBuilder with member __.ReturnFrom(computation: System.Threading.Tasks.Task<'a>): Async<'a> = Async.AwaitTask computation -module Option = - let guard (x: bool) : Option = - if x then Some() else None - -module List = - let foldi (folder : 'State -> int -> 'T -> 'State) (state : 'State) (xs : 'T list) = - let mutable state = state - let mutable i = 0 - for x in xs do - state <- folder state i x - i <- i + 1 - state - -module Seq = - open System.Collections.Immutable - - let toImmutableArray (xs: seq<'a>) : ImmutableArray<'a> = xs.ToImmutableArray() - -module Array = - /// Optimized arrays equality. ~100x faster than `array1 = array2` on strings. - /// ~2x faster for floats - /// ~0.8x slower for ints - let areEqual (xs: 'T []) (ys: 'T []) = - match xs, ys with - | null, null -> true - | [||], [||] -> true - | null, _ | _, null -> false - | _ when xs.Length <> ys.Length -> false - | _ -> - let mutable break' = false - let mutable i = 0 - let mutable result = true - while i < xs.Length && not break' do - if xs.[i] <> ys.[i] then - break' <- true - result <- false - i <- i + 1 - result - - /// check if subArray is found in the wholeArray starting - /// at the provided index - let isSubArray (subArray: 'T []) (wholeArray:'T []) index = - if isNull subArray || isNull wholeArray then false - elif subArray.Length = 0 then true - elif subArray.Length > wholeArray.Length then false - elif subArray.Length = wholeArray.Length then areEqual subArray wholeArray else - let rec loop subidx idx = - if subidx = subArray.Length then true - elif subArray.[subidx] = wholeArray.[idx] then loop (subidx+1) (idx+1) - else false - loop 0 index - - /// Returns true if one array has another as its subset from index 0. - let startsWith (prefix: _ []) (whole: _ []) = - isSubArray prefix whole 0 - - /// Returns true if one array has trailing elements equal to another's. - let endsWith (suffix: _ []) (whole: _ []) = - isSubArray suffix whole (whole.Length-suffix.Length) \ No newline at end of file diff --git a/vsintegration/src/FSharp.Editor/Common/RoslynHelpers.fs b/vsintegration/src/FSharp.Editor/Common/RoslynHelpers.fs new file mode 100644 index 0000000000000000000000000000000000000000..981cf1826e6d080a8b8269dae6a3d9dd02874c52 --- /dev/null +++ b/vsintegration/src/FSharp.Editor/Common/RoslynHelpers.fs @@ -0,0 +1,159 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.VisualStudio.FSharp.Editor + +open System +open System.Collections.Immutable +open System.Collections.Generic +open System.Threading.Tasks +open Microsoft.CodeAnalysis +open Microsoft.CodeAnalysis.Text +open Microsoft.CodeAnalysis.Diagnostics +open Microsoft.FSharp.Compiler +open Microsoft.FSharp.Compiler.Layout +open Microsoft.FSharp.Compiler.SourceCodeServices +open Microsoft.FSharp.Compiler.Range +open Microsoft.FSharp.Compiler.Ast +open Microsoft.FSharp.Compiler.SourceCodeServices +open Microsoft.VisualStudio.FSharp.LanguageService + +module internal RoslynHelpers = + + let FSharpRangeToTextSpan(sourceText: SourceText, range: range) = + // Roslyn TextLineCollection is zero-based, F# range lines are one-based + let startPosition = sourceText.Lines.[range.StartLine - 1].Start + range.StartColumn + let endPosition = sourceText.Lines.[range.EndLine - 1].Start + range.EndColumn + TextSpan(startPosition, endPosition - startPosition) + + let TryFSharpRangeToTextSpan(sourceText: SourceText, range: range) : TextSpan option = + try Some(FSharpRangeToTextSpan(sourceText, range)) + with e -> + //Assert.Exception(e) + None + + let TextSpanToFSharpRange(fileName: string, textSpan: TextSpan, sourceText: SourceText) : range = + let startLine = sourceText.Lines.GetLineFromPosition textSpan.Start + let endLine = sourceText.Lines.GetLineFromPosition textSpan.End + mkRange + fileName + (Pos.fromZ startLine.LineNumber (textSpan.Start - startLine.Start)) + (Pos.fromZ endLine.LineNumber (textSpan.End - endLine.Start)) + + let GetCompletedTaskResult(task: Task<'TResult>) = + if task.Status = TaskStatus.RanToCompletion then + task.Result + else + Assert.Exception(task.Exception.GetBaseException()) + raise(task.Exception.GetBaseException()) + + + + /// maps from `LayoutTag` of the F# Compiler to Roslyn `TextTags` for use in tooltips + let roslynTag = function + | LayoutTag.ActivePatternCase + | LayoutTag.ActivePatternResult + | LayoutTag.UnionCase + | LayoutTag.Enum -> TextTags.Enum + | LayoutTag.Alias + | LayoutTag.Class + | LayoutTag.Union + | LayoutTag.Record + | LayoutTag.UnknownType -> TextTags.Class + | LayoutTag.Delegate -> TextTags.Delegate + | LayoutTag.Event -> TextTags.Event + | LayoutTag.Field -> TextTags.Field + | LayoutTag.Interface -> TextTags.Interface + | LayoutTag.Struct -> TextTags.Struct + | LayoutTag.Keyword -> TextTags.Keyword + | LayoutTag.Local -> TextTags.Local + | LayoutTag.Member + | LayoutTag.ModuleBinding + | LayoutTag.RecordField + | LayoutTag.Property -> TextTags.Property + | LayoutTag.Method -> TextTags.Method + | LayoutTag.Namespace -> TextTags.Namespace + | LayoutTag.Module -> TextTags.Module + | LayoutTag.LineBreak -> TextTags.LineBreak + | LayoutTag.Space -> TextTags.Space + | LayoutTag.NumericLiteral -> TextTags.NumericLiteral + | LayoutTag.Operator -> TextTags.Operator + | LayoutTag.Parameter -> TextTags.Parameter + | LayoutTag.TypeParameter -> TextTags.TypeParameter + | LayoutTag.Punctuation -> TextTags.Punctuation + | LayoutTag.StringLiteral -> TextTags.StringLiteral + | LayoutTag.Text + | LayoutTag.UnknownEntity -> TextTags.Text + + let CollectTaggedText (list: List<_>) (t:TaggedText) = list.Add(TaggedText(roslynTag t.Tag, t.Text)) + + let StartAsyncAsTask cancellationToken computation = + let computation = + async { + try + return! computation + with e -> + Assert.Exception(e) + return Unchecked.defaultof<_> + } + Async.StartAsTask(computation, TaskCreationOptions.None, cancellationToken) + + let StartAsyncUnitAsTask cancellationToken (computation:Async) = + StartAsyncAsTask cancellationToken computation :> Task + + let SupportedDiagnostics() = + // We are constructing our own descriptors at run-time. Compiler service is already doing error formatting and localization. + let dummyDescriptor = DiagnosticDescriptor("0", String.Empty, String.Empty, String.Empty, DiagnosticSeverity.Error, true, null, null) + ImmutableArray.Create(dummyDescriptor) + + let ConvertError(error: FSharpErrorInfo, location: Location) = + let id = "FS" + error.ErrorNumber.ToString("0000") + let emptyString = LocalizableString.op_Implicit("") + let description = LocalizableString.op_Implicit(error.Message) + let severity = if error.Severity = FSharpErrorSeverity.Error then DiagnosticSeverity.Error else DiagnosticSeverity.Warning + let customTags = + match error.ErrorNumber with + | 1182 -> DiagnosticCustomTags.Unnecessary + | _ -> null + let descriptor = new DiagnosticDescriptor(id, emptyString, description, error.Subcategory, severity, true, emptyString, String.Empty, customTags) + Diagnostic.Create(descriptor, location) + + + let RangeToLocation (r: range, sourceText: SourceText, filePath: string) : Location = + let linePositionSpan = LinePositionSpan(LinePosition(Line.toZ r.StartLine, r.StartColumn), LinePosition(Line.toZ r.EndLine, r.EndColumn)) + let textSpan = sourceText.Lines.GetTextSpan linePositionSpan + Location.Create(filePath, textSpan, linePositionSpan) + + +module internal OpenDeclarationHelper = + /// + /// Inserts open declaration into `SourceText`. + /// + /// SourceText. + /// Insertion context. Typically returned from tryGetInsertionContext + /// Namespace to open. + let insertOpenDeclaration (sourceText: SourceText) (ctx: InsertContext) (ns: string) : SourceText * int = + let mutable minPos = None + + let insert line lineStr (sourceText: SourceText) : SourceText = + let pos = sourceText.Lines.[line].Start + minPos <- match minPos with None -> Some pos | Some oldPos -> Some (min oldPos pos) + sourceText.WithChanges(TextChange(TextSpan(pos, 0), lineStr + Environment.NewLine)) + + let getLineStr line = sourceText.Lines.[line].ToString().Trim() + let pos = ParsedInput.adjustInsertionPoint getLineStr ctx + let docLine = pos.Line - 1 + let lineStr = (String.replicate pos.Column " ") + "open " + ns + let sourceText = sourceText |> insert docLine lineStr + // if there's no a blank line between open declaration block and the rest of the code, we add one + let sourceText = + if sourceText.Lines.[docLine + 1].ToString().Trim() <> "" then + sourceText |> insert (docLine + 1) "" + else sourceText + let sourceText = + // for top level module we add a blank line between the module declaration and first open statement + if (pos.Column = 0 || ctx.ScopeKind = ScopeKind.Namespace) && docLine > 0 + && not (sourceText.Lines.[docLine - 1].ToString().Trim().StartsWith "open") then + sourceText |> insert docLine "" + else sourceText + sourceText, minPos |> Option.defaultValue 0 + diff --git a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs index 48020a82cce74ebfbe870df259369262c15de32f..22279df5b318d58e338de3e9468783983d6207bd 100644 --- a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs +++ b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs @@ -21,6 +21,8 @@ open Microsoft.VisualStudio.Shell.Interop open Microsoft.FSharp.Compiler open Microsoft.FSharp.Compiler.Range open Microsoft.FSharp.Compiler.SourceCodeServices +open Tokenizer + type internal FSharpCompletionProvider ( @@ -140,7 +142,7 @@ type internal FSharpCompletionProvider let maxHints = if mruItems.Values.Count = 0 then 0 else Seq.max mruItems.Values sortedDeclItems |> Array.iteri (fun number declItem -> - let glyph = CommonRoslynHelpers.FSharpGlyphToRoslynGlyph (declItem.Glyph, declItem.Accessibility) + let glyph = FSharpGlyphToRoslynGlyph (declItem.Glyph, declItem.Accessibility) let name = match entityKind, declItem.NamespaceToOpen with | Some EntityKind.Attribute, _ when declItem.IsAttribute && declItem.Name.EndsWith "Attribute" -> @@ -226,7 +228,7 @@ type internal FSharpCompletionProvider FSharpCompletionProvider.ProvideCompletionsAsyncAux(checkerProvider.Checker, sourceText, context.Position, options, document.FilePath, textVersion.GetHashCode(), getAllSymbols) context.AddItems(results) - } |> Async.Ignore |> CommonRoslynHelpers.StartAsyncUnitAsTask context.CancellationToken + } |> Async.Ignore |> RoslynHelpers.StartAsyncUnitAsTask context.CancellationToken override this.GetDescriptionAsync(_: Document, completionItem: Completion.CompletionItem, cancellationToken: CancellationToken): Task = async { @@ -234,13 +236,13 @@ type internal FSharpCompletionProvider if exists then let! description = declarationItem.StructuredDescriptionTextAsync let documentation = List() - let collector = CommonRoslynHelpers.CollectTaggedText documentation + let collector = RoslynHelpers.CollectTaggedText documentation // mix main description and xmldoc by using one collector XmlDocumentation.BuildDataTipText(documentationBuilder, collector, collector, description) return CompletionDescription.Create(documentation.ToImmutableArray()) else return CompletionDescription.Empty - } |> CommonRoslynHelpers.StartAsyncAsTask cancellationToken + } |> RoslynHelpers.StartAsyncAsTask cancellationToken override this.GetChangeAsync(document, item, _, cancellationToken) : Task = async { @@ -285,4 +287,4 @@ type internal FSharpCompletionProvider } |> Async.map (Option.defaultValue (CompletionChange.Create(TextChange(item.Span, nameInCode)))) - } |> CommonRoslynHelpers.StartAsyncAsTask cancellationToken \ No newline at end of file + } |> RoslynHelpers.StartAsyncAsTask cancellationToken \ 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 151813d6ff24d0b63524ab5c4b849818da5b7840..d9b8fa435d9e565f6d8ba5dcdb26976106270943 100644 --- a/vsintegration/src/FSharp.Editor/Completion/CompletionService.fs +++ b/vsintegration/src/FSharp.Editor/Completion/CompletionService.fs @@ -36,12 +36,12 @@ type internal FSharpCompletionService .WithDismissIfLastCharacterDeleted(true) .WithDefaultEnterKeyRule(EnterKeyRule.Never) - override this.Language = FSharpCommonConstants.FSharpLanguageName + override this.Language = FSharpConstants.FSharpLanguageName override this.GetBuiltInProviders() = builtInProviders override this.GetRules() = completionRules [] -[, FSharpCommonConstants.FSharpLanguageName)>] +[, FSharpConstants.FSharpLanguageName)>] type internal FSharpCompletionServiceFactory [] ( diff --git a/vsintegration/src/FSharp.Editor/Completion/CompletionUtils.fs b/vsintegration/src/FSharp.Editor/Completion/CompletionUtils.fs index 5c09af2f2bc4a93ee91501d4d3262aed1753bf38..7af64b4ebf85af190be8a3ab5a150e4f7f39189f 100644 --- a/vsintegration/src/FSharp.Editor/Completion/CompletionUtils.fs +++ b/vsintegration/src/FSharp.Editor/Completion/CompletionUtils.fs @@ -85,7 +85,7 @@ module internal CompletionUtils = let shouldProvideCompletion (documentId: DocumentId, filePath: string, defines: string list, text: SourceText, position: int) : bool = let textLines = text.Lines let triggerLine = textLines.GetLineFromPosition position - let colorizationData = CommonHelpers.getColorizationData(documentId, text, triggerLine.Span, Some filePath, defines, CancellationToken.None) + let colorizationData = Tokenizer.getColorizationData(documentId, text, triggerLine.Span, Some filePath, defines, CancellationToken.None) colorizationData.Count = 0 || // we should provide completion at the start of empty line, where there are no tokens at all colorizationData.Exists (fun classifiedSpan -> classifiedSpan.TextSpan.IntersectsWith position && diff --git a/vsintegration/src/FSharp.Editor/Completion/FileSystemCompletion.fs b/vsintegration/src/FSharp.Editor/Completion/FileSystemCompletion.fs index fa73954acb2bc87674e14b39022ba89b99746441..18b48b7e1bac1db8bd6b28644bcea5e6ee073a9c 100644 --- a/vsintegration/src/FSharp.Editor/Completion/FileSystemCompletion.fs +++ b/vsintegration/src/FSharp.Editor/Completion/FileSystemCompletion.fs @@ -58,7 +58,7 @@ type internal HashDirectiveCompletionProvider(workspace: Workspace, projectInfoM let defines = projectInfoManager.GetCompilationDefinesForEditingDocument(document) let textLines = text.Lines let triggerLine = textLines.GetLineFromPosition(position) - CommonHelpers.getColorizationData(documentId, text, triggerLine.Span, Some document.FilePath, defines, CancellationToken.None) + Tokenizer.getColorizationData(documentId, text, triggerLine.Span, Some document.FilePath, defines, CancellationToken.None) let isInStringLiteral(text: SourceText, position: int) : bool = getColorizationData(text, position) @@ -143,7 +143,7 @@ type internal HashDirectiveCompletionProvider(workspace: Workspace, projectInfoM context.AddItems(helper.GetItems(pathThroughLastSlash, documentPath)) } |> Async.Ignore - |> CommonRoslynHelpers.StartAsyncUnitAsTask context.CancellationToken + |> RoslynHelpers.StartAsyncUnitAsTask context.CancellationToken override __.IsInsertionTrigger(text, position, _) = // Bring up completion when the user types a quote (i.e.: #r "), or if they type a slash diff --git a/vsintegration/src/FSharp.Editor/Completion/SignatureHelp.fs b/vsintegration/src/FSharp.Editor/Completion/SignatureHelp.fs index 8a684c098648151be423b6e041bec07168491069..b739a9924e7409a83dc50d3ba0d26beec472e0f0 100644 --- a/vsintegration/src/FSharp.Editor/Completion/SignatureHelp.fs +++ b/vsintegration/src/FSharp.Editor/Completion/SignatureHelp.fs @@ -20,7 +20,7 @@ open Microsoft.FSharp.Compiler.Range open Microsoft.FSharp.Compiler.SourceCodeServices [] -[] +[] type internal FSharpSignatureHelpProvider [] ( @@ -159,16 +159,16 @@ type internal FSharpSignatureHelpProvider // Create the documentation. Note, do this on the background thread, since doing it in the documentationBuild fails to build the XML index let mainDescription = List() let documentation = List() - XmlDocumentation.BuildMethodOverloadTipText(documentationBuilder, CommonRoslynHelpers.CollectTaggedText mainDescription, CommonRoslynHelpers.CollectTaggedText documentation, method.StructuredDescription, false) + XmlDocumentation.BuildMethodOverloadTipText(documentationBuilder, RoslynHelpers.CollectTaggedText mainDescription, RoslynHelpers.CollectTaggedText documentation, method.StructuredDescription, false) let parameters = let parameters = if isStaticArgTip then method.StaticParameters else method.Parameters [| for p in parameters do let doc = List() // FSROSLYNTODO: compute the proper help text for parameters, c.f. AppendParameter in XmlDocumentation.fs - XmlDocumentation.BuildMethodParamText(documentationBuilder, CommonRoslynHelpers.CollectTaggedText doc, method.XmlDoc, p.ParameterName) + XmlDocumentation.BuildMethodParamText(documentationBuilder, RoslynHelpers.CollectTaggedText doc, method.XmlDoc, p.ParameterName) let parts = List() - renderL (taggedTextListR (CommonRoslynHelpers.CollectTaggedText parts)) p.StructuredDisplay |> ignore + renderL (taggedTextListR (RoslynHelpers.CollectTaggedText parts)) p.StructuredDisplay |> ignore yield (p.ParameterName, p.IsOptional, doc, parts) |] @@ -221,7 +221,7 @@ type internal FSharpSignatureHelpProvider return! None } |> Async.map Option.toObj - |> CommonRoslynHelpers.StartAsyncAsTask cancellationToken + |> RoslynHelpers.StartAsyncAsTask cancellationToken open System.ComponentModel.Composition open Microsoft.VisualStudio.Utilities @@ -232,7 +232,7 @@ open Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.SignatureHelp.Pre // Enable colorized signature help for F# buffers [)>] -[] +[] type internal FSharpSignatureHelpClassifierProvider [] (typeMap) = interface IClassifierProvider with override __.GetClassifier (buffer: ITextBuffer) = diff --git a/vsintegration/src/FSharp.Editor/Debugging/BreakpointResolutionService.fs b/vsintegration/src/FSharp.Editor/Debugging/BreakpointResolutionService.fs index 4cf150f25ccc8dfc8c87f2ba662805a50b8a464a..85c587b3565c3b7854c00405de9f032c58cae094 100644 --- a/vsintegration/src/FSharp.Editor/Debugging/BreakpointResolutionService.fs +++ b/vsintegration/src/FSharp.Editor/Debugging/BreakpointResolutionService.fs @@ -17,7 +17,7 @@ open Microsoft.FSharp.Compiler.SourceCodeServices open Microsoft.FSharp.Compiler.Range [] -[, FSharpCommonConstants.FSharpLanguageName)>] +[, FSharpConstants.FSharpLanguageName)>] type internal FSharpBreakpointResolutionService [] ( @@ -45,10 +45,10 @@ type internal FSharpBreakpointResolutionService let! options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document) let! sourceText = document.GetTextAsync(cancellationToken) let! range = FSharpBreakpointResolutionService.GetBreakpointLocation(checkerProvider.Checker, sourceText, document.Name, textSpan, options) - return BreakpointResolutionResult.CreateSpanResult(document, CommonRoslynHelpers.FSharpRangeToTextSpan(sourceText, range)) + return BreakpointResolutionResult.CreateSpanResult(document, RoslynHelpers.FSharpRangeToTextSpan(sourceText, range)) } |> Async.map Option.toObj - |> CommonRoslynHelpers.StartAsyncAsTask cancellationToken + |> RoslynHelpers.StartAsyncAsTask cancellationToken // FSROSLYNTODO: enable placing breakpoints by when user suplies fully-qualified function names member this.ResolveBreakpointsAsync(_, _, _): Task> = diff --git a/vsintegration/src/FSharp.Editor/Debugging/LanguageDebugInfoService.fs b/vsintegration/src/FSharp.Editor/Debugging/LanguageDebugInfoService.fs index 8c0b4c03c2e422c33fb2c31a80de08ffa2106744..45647420e6adb41d70397b0ebe1ba9d0f289d6c8 100644 --- a/vsintegration/src/FSharp.Editor/Debugging/LanguageDebugInfoService.fs +++ b/vsintegration/src/FSharp.Editor/Debugging/LanguageDebugInfoService.fs @@ -17,7 +17,7 @@ open Microsoft.CodeAnalysis.Text open Microsoft.VisualStudio.FSharp.LanguageService [] -[, FSharpCommonConstants.FSharpLanguageName)>] +[, FSharpConstants.FSharpLanguageName)>] type internal FSharpLanguageDebugInfoService [](projectInfoManager: ProjectInfoManager) = static member GetDataTipInformation(sourceText: SourceText, position: int, tokens: List): TextSpan option = @@ -55,13 +55,13 @@ type internal FSharpLanguageDebugInfoService [](projectInf let defines = projectInfoManager.GetCompilationDefinesForEditingDocument(document) let! sourceText = document.GetTextAsync(cancellationToken) let textSpan = TextSpan.FromBounds(0, sourceText.Length) - let tokens = CommonHelpers.getColorizationData(document.Id, sourceText, textSpan, Some(document.Name), defines, cancellationToken) + let tokens = Tokenizer.getColorizationData(document.Id, sourceText, textSpan, Some(document.Name), defines, cancellationToken) let result = match FSharpLanguageDebugInfoService.GetDataTipInformation(sourceText, position, tokens) with | None -> DebugDataTipInfo() | Some textSpan -> DebugDataTipInfo(textSpan, sourceText.GetSubText(textSpan).ToString()) return result } - |> CommonRoslynHelpers.StartAsyncAsTask(cancellationToken) + |> RoslynHelpers.StartAsyncAsTask(cancellationToken) \ No newline at end of file diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs index 788c0cef180ba379753809df0dcce194160dff21..d8f3a53e326a790493744b52fe3e670a245779d8 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs @@ -26,7 +26,7 @@ type internal DiagnosticsType = | Syntax | Semantic -[] +[] type internal FSharpDocumentDiagnosticAnalyzer() = inherit DocumentDiagnosticAnalyzer() @@ -103,12 +103,12 @@ type internal FSharpDocumentDiagnosticAnalyzer() = TextSpan.FromBounds(start, sourceText.Length) let location = Location.Create(filePath, correctedTextSpan , linePositionSpan) - Some(CommonRoslynHelpers.ConvertError(error, location))) + Some(RoslynHelpers.ConvertError(error, location))) |> Seq.toImmutableArray return results } - override this.SupportedDiagnostics = CommonRoslynHelpers.SupportedDiagnostics() + override this.SupportedDiagnostics = RoslynHelpers.SupportedDiagnostics() override this.AnalyzeSyntaxAsync(document: Document, cancellationToken: CancellationToken): Task> = let projectInfoManager = getProjectInfoManager document @@ -121,7 +121,7 @@ type internal FSharpDocumentDiagnosticAnalyzer() = |> liftAsync } |> Async.map (Option.defaultValue ImmutableArray.Empty) - |> CommonRoslynHelpers.StartAsyncAsTask cancellationToken + |> RoslynHelpers.StartAsyncAsTask cancellationToken override this.AnalyzeSemanticsAsync(document: Document, cancellationToken: CancellationToken): Task> = let projectInfoManager = getProjectInfoManager document @@ -134,7 +134,7 @@ type internal FSharpDocumentDiagnosticAnalyzer() = |> liftAsync } |> Async.map (Option.defaultValue ImmutableArray.Empty) - |> CommonRoslynHelpers.StartAsyncAsTask cancellationToken + |> RoslynHelpers.StartAsyncAsTask cancellationToken interface IBuiltInAnalyzer with member __.GetAnalyzerCategory() : DiagnosticAnalyzerCategory = DiagnosticAnalyzerCategory.SemanticDocumentAnalysis diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/SimplifyNameDiagnosticAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/SimplifyNameDiagnosticAnalyzer.fs index 5df7ef6488d8c265190a6e5fbcfdbc3ff727d410..f4f839d683f02db4914eb7fd9155d3d5a2f3fd4b 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/SimplifyNameDiagnosticAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/SimplifyNameDiagnosticAnalyzer.fs @@ -108,7 +108,7 @@ type internal SimplifyNameDiagnosticAnalyzer() = result.Add( Diagnostic.Create( Descriptor, - CommonRoslynHelpers.RangeToLocation(unnecessaryRange, sourceText, document.FilePath), + RoslynHelpers.RangeToLocation(unnecessaryRange, sourceText, document.FilePath), properties = (dict [SimplifyNameDiagnosticAnalyzer.LongIdentPropertyKey, relativeName]).ToImmutableDictionary())) let diagnostics = result.ToImmutableArray() @@ -118,7 +118,7 @@ type internal SimplifyNameDiagnosticAnalyzer() = finally guard.Release() |> ignore } |> Async.map (Option.defaultValue ImmutableArray.Empty) - |> CommonRoslynHelpers.StartAsyncAsTask cancellationToken + |> RoslynHelpers.StartAsyncAsTask cancellationToken interface IBuiltInAnalyzer with member __.OpenFileOnly _ = true diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs index 4b2b2a84431892ae3d48d2ad13524cd04cc21977..7549f60e5ce12681f0520e0c58915ea5bd662ccd 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs @@ -8,14 +8,17 @@ open System.Threading open System.Threading.Tasks open Microsoft.CodeAnalysis +open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.Diagnostics open Microsoft.FSharp.Compiler open Microsoft.FSharp.Compiler.Ast open Microsoft.FSharp.Compiler.Range open Microsoft.FSharp.Compiler.SourceCodeServices +open Symbols + module private UnusedOpens = - open Microsoft.CodeAnalysis.Text + let rec visitSynModuleOrNamespaceDecls (parent: Ast.LongIdent) decls : (Set * range) list = [ for decl in decls do @@ -70,7 +73,7 @@ module private UnusedOpens = | None -> [] let symbolIsFullyQualified (sourceText: SourceText) (sym: FSharpSymbolUse) (fullName: string) = - match CommonRoslynHelpers.TryFSharpRangeToTextSpan(sourceText, sym.RangeAlternate) with + match RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, sym.RangeAlternate) with | Some span // check that the symbol hasn't provided an invalid span when sourceText.Length < span.Start || sourceText.Length < span.End -> false @@ -135,7 +138,7 @@ module private UnusedOpens = let openStatements = getOpenStatements parsedInput openStatements |> filter |> List.map snd -[] +[] type internal UnusedOpensDiagnosticAnalyzer() = inherit DocumentDiagnosticAnalyzer() @@ -175,11 +178,11 @@ type internal UnusedOpensDiagnosticAnalyzer() = |> List.map (fun m -> Diagnostic.Create( Descriptor, - CommonRoslynHelpers.RangeToLocation(m, sourceText, document.FilePath))) + RoslynHelpers.RangeToLocation(m, sourceText, document.FilePath))) |> Seq.toImmutableArray } |> Async.map (Option.defaultValue ImmutableArray.Empty) - |> CommonRoslynHelpers.StartAsyncAsTask cancellationToken + |> RoslynHelpers.StartAsyncAsTask cancellationToken interface IBuiltInAnalyzer with member __.OpenFileOnly _ = true diff --git a/vsintegration/src/FSharp.Editor/DocumentHighlights/DocumentHighlightsService.fs b/vsintegration/src/FSharp.Editor/DocumentHighlights/DocumentHighlightsService.fs index 785d3d65e058164a6db4c2649c6b575f6558ddb6..450b028958ef9ae1bae3eb82803b6acb79f1ddd9 100644 --- a/vsintegration/src/FSharp.Editor/DocumentHighlights/DocumentHighlightsService.fs +++ b/vsintegration/src/FSharp.Editor/DocumentHighlights/DocumentHighlightsService.fs @@ -21,7 +21,7 @@ type internal FSharpHighlightSpan = override this.ToString() = sprintf "%+A" this [] -[, FSharpCommonConstants.FSharpLanguageName)>] +[, FSharpConstants.FSharpLanguageName)>] type internal FSharpDocumentHighlightsService [] (checkerProvider: FSharpCheckerProvider, projectInfoManager: ProjectInfoManager) = /// Fix invalid spans if they appear to have redundant suffix and prefix. @@ -56,14 +56,14 @@ type internal FSharpDocumentHighlightsService [] (checkerP let textLine = sourceText.Lines.GetLineFromPosition(position) let textLinePos = sourceText.Lines.GetLinePosition(position) let fcsTextLineNumber = Line.fromZ textLinePos.Line - let! symbol = CommonHelpers.getSymbolAtPosition(documentKey, sourceText, position, filePath, defines, SymbolLookupKind.Greedy) + let! symbol = Tokenizer.getSymbolAtPosition(documentKey, sourceText, position, filePath, defines, Tokenizer.SymbolLookupKind.Greedy) let! _, _, checkFileResults = checker.ParseAndCheckDocument(filePath, textVersionHash, sourceText.ToString(), options, allowStaleResults = true) let! symbolUse = checkFileResults.GetSymbolUseAtLocation(fcsTextLineNumber, symbol.Ident.idRange.EndColumn, textLine.ToString(), symbol.FullIsland) let! symbolUses = checkFileResults.GetUsesOfSymbolInFile(symbolUse.Symbol) |> liftAsync return [| for symbolUse in symbolUses do yield { IsDefinition = symbolUse.IsFromDefinition - TextSpan = CommonRoslynHelpers.FSharpRangeToTextSpan(sourceText, symbolUse.RangeAlternate) } |] + TextSpan = RoslynHelpers.FSharpRangeToTextSpan(sourceText, symbolUse.RangeAlternate) } |] |> fixInvalidSymbolSpans sourceText symbol.Ident.idText } @@ -86,4 +86,4 @@ type internal FSharpDocumentHighlightsService [] (checkerP return ImmutableArray.Create(DocumentHighlights(document, highlightSpans)) } |> Async.map (Option.defaultValue ImmutableArray.Empty) - |> CommonRoslynHelpers.StartAsyncAsTask(cancellationToken) + |> RoslynHelpers.StartAsyncAsTask(cancellationToken) diff --git a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj index f93de2ab6dfa0edcf21507ba9d7795ed5bc86e3c..b86cce018edb6abfedcf6d7297689cb5fe5a4dba 100644 --- a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj +++ b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj @@ -33,15 +33,19 @@ - + + - - + - - - + + + + + + + @@ -106,7 +110,6 @@ True - @@ -120,7 +123,6 @@ - $(FSharpSourcesRoot)\..\packages\EnvDTE.8.0.1\lib\net10\EnvDTE.dll diff --git a/vsintegration/src/FSharp.Editor/Formatting/BraceMatchingService.fs b/vsintegration/src/FSharp.Editor/Formatting/BraceMatchingService.fs index e24d713b15df26be0060ca775cdfec8c64dc93b0..52e9675f9ad9bda64045fbcfe902a47e5ee5096e 100644 --- a/vsintegration/src/FSharp.Editor/Formatting/BraceMatchingService.fs +++ b/vsintegration/src/FSharp.Editor/Formatting/BraceMatchingService.fs @@ -7,7 +7,7 @@ open System.ComponentModel.Composition open Microsoft.CodeAnalysis.Editor open Microsoft.FSharp.Compiler.SourceCodeServices -[] +[] type internal FSharpBraceMatchingService [] ( @@ -18,7 +18,7 @@ type internal FSharpBraceMatchingService static member GetBraceMatchingResult(checker: FSharpChecker, sourceText, fileName, options, position: int) = async { let! matchedBraces = checker.MatchBracesAlternate(fileName, sourceText.ToString(), options) - let isPositionInRange range = CommonRoslynHelpers.FSharpRangeToTextSpan(sourceText, range).Contains(position) + let isPositionInRange range = RoslynHelpers.FSharpRangeToTextSpan(sourceText, range).Contains(position) return matchedBraces |> Array.tryFind(fun (left, right) -> isPositionInRange left || isPositionInRange right) } @@ -30,8 +30,8 @@ type internal FSharpBraceMatchingService let! (left, right) = FSharpBraceMatchingService.GetBraceMatchingResult(checkerProvider.Checker, sourceText, document.Name, options, position) return BraceMatchingResult( - CommonRoslynHelpers.FSharpRangeToTextSpan(sourceText, left), - CommonRoslynHelpers.FSharpRangeToTextSpan(sourceText, right)) + RoslynHelpers.FSharpRangeToTextSpan(sourceText, left), + RoslynHelpers.FSharpRangeToTextSpan(sourceText, right)) } |> Async.map Option.toNullable - |> CommonRoslynHelpers.StartAsyncAsTask cancellationToken + |> RoslynHelpers.StartAsyncAsTask cancellationToken diff --git a/vsintegration/src/FSharp.Editor/Formatting/IndentationService.fs b/vsintegration/src/FSharp.Editor/Formatting/IndentationService.fs index f391da6e88b6aebd0b62997c323f606eaa29d01d..d1ae31768d4ef5312b2bca881bf5d466f21aa080 100644 --- a/vsintegration/src/FSharp.Editor/Formatting/IndentationService.fs +++ b/vsintegration/src/FSharp.Editor/Formatting/IndentationService.fs @@ -13,7 +13,7 @@ open Microsoft.CodeAnalysis.Host.Mef open Microsoft.CodeAnalysis.Text [] -[, FSharpCommonConstants.FSharpLanguageName)>] +[, FSharpConstants.FSharpLanguageName)>] type internal FSharpIndentationService() = static member GetDesiredIndentation(sourceText: SourceText, lineNumber: int, tabSize: int): Option = @@ -47,7 +47,7 @@ type internal FSharpIndentationService() = async { let! sourceText = document.GetTextAsync(cancellationToken) let! options = document.GetOptionsAsync(cancellationToken) - let tabSize = options.GetOption(FormattingOptions.TabSize, FSharpCommonConstants.FSharpLanguageName) + let tabSize = options.GetOption(FormattingOptions.TabSize, FSharpConstants.FSharpLanguageName) return match FSharpIndentationService.GetDesiredIndentation(sourceText, lineNumber, tabSize) with diff --git a/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs b/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs index b95430b98d6caf2efee6b88af19c76d20f038fa7..1ae5d080c8fc0c885221757d91bd989e1dddd3a1 100644 --- a/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs +++ b/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs @@ -17,6 +17,9 @@ open Microsoft.CodeAnalysis.Text open Microsoft.FSharp.Compiler open Microsoft.FSharp.Compiler.Range open Microsoft.FSharp.Compiler.SourceCodeServices +open Tokenizer +open Symbols + type internal FailureInlineRenameInfo private () = interface IInlineRenameInfo with @@ -63,11 +66,11 @@ type internal InlineRenameLocationSet(locationsByDocument: DocumentLocations [], return { new IInlineRenameReplacementInfo with member __.NewSolution = newSolution - member __.ReplacementTextValid = CommonHelpers.isValidNameForSymbol(symbolKind, symbol, replacementText) + member __.ReplacementTextValid = Tokenizer.isValidNameForSymbol(symbolKind, symbol, replacementText) member __.DocumentIds = locationsByDocument |> Seq.map (fun doc -> doc.Document.Id) member __.GetReplacements(documentId) = Seq.empty } } - |> CommonRoslynHelpers.StartAsyncAsTask(cancellationToken) + |> RoslynHelpers.StartAsyncAsTask(cancellationToken) type internal InlineRenameInfo ( @@ -87,8 +90,8 @@ type internal InlineRenameInfo | _ -> document.GetTextAsync(cancellationToken).Result let triggerSpan = - let span = CommonRoslynHelpers.FSharpRangeToTextSpan(sourceText, symbolUse.RangeAlternate) - CommonHelpers.fixupSpan(sourceText, span) + let span = RoslynHelpers.FSharpRangeToTextSpan(sourceText, symbolUse.RangeAlternate) + Tokenizer.fixupSpan(sourceText, span) let symbolUses = SymbolHelpers.getSymbolUsesInSolution(symbolUse.Symbol, declLoc, checkFileResults, projectInfoManager, checker, document.Project.Solution) @@ -107,7 +110,7 @@ type internal InlineRenameInfo member __.GetReferenceEditSpan(location, cancellationToken) = let text = getDocumentText location.Document cancellationToken - CommonHelpers.fixupSpan(text, location.TextSpan) + Tokenizer.fixupSpan(text, location.TextSpan) member __.GetConflictEditSpan(location, _replacementText, _cancellationToken) = Nullable(location.TextSpan) @@ -123,18 +126,18 @@ type internal InlineRenameInfo let locations = symbolUses |> Array.map (fun symbolUse -> - let textSpan = CommonHelpers.fixupSpan(sourceText, CommonRoslynHelpers.FSharpRangeToTextSpan(sourceText, symbolUse.RangeAlternate)) + let textSpan = Tokenizer.fixupSpan(sourceText, RoslynHelpers.FSharpRangeToTextSpan(sourceText, symbolUse.RangeAlternate)) InlineRenameLocation(document, textSpan)) return { Document = document; Locations = locations } }) |> Async.Parallel return InlineRenameLocationSet(locationsByDocument, document.Project.Solution, lexerSymbol.Kind, symbolUse.Symbol) :> IInlineRenameLocationSet - } |> CommonRoslynHelpers.StartAsyncAsTask(cancellationToken) + } |> RoslynHelpers.StartAsyncAsTask(cancellationToken) member __.TryOnBeforeGlobalSymbolRenamed(_workspace, _changedDocumentIDs, _replacementText) = true member __.TryOnAfterGlobalSymbolRenamed(_workspace, _changedDocumentIDs, _replacementText) = true -[, FSharpCommonConstants.FSharpLanguageName); Shared>] +[, FSharpConstants.FSharpLanguageName); Shared>] type internal InlineRenameService [] ( @@ -149,7 +152,7 @@ type internal InlineRenameService let textLine = sourceText.Lines.GetLineFromPosition(position) let textLinePos = sourceText.Lines.GetLinePosition(position) let fcsTextLineNumber = Line.fromZ textLinePos.Line - let! symbol = CommonHelpers.getSymbolAtPosition(document.Id, sourceText, position, document.FilePath, defines, SymbolLookupKind.Greedy) + let! symbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, position, document.FilePath, defines, SymbolLookupKind.Greedy) let! _, _, checkFileResults = checker.ParseAndCheckDocument(document, options, allowStaleResults = true) let! symbolUse = checkFileResults.GetSymbolUseAtLocation(fcsTextLineNumber, symbol.Ident.idRange.EndColumn, textLine.Text.ToString(), symbol.FullIsland) let! declLoc = symbolUse.GetDeclarationLocation(document) @@ -165,4 +168,4 @@ type internal InlineRenameService return! InlineRenameService.GetInlineRenameInfo(checkerProvider.Checker, projectInfoManager, document, sourceText, position, defines, options) } |> Async.map (Option.defaultValue FailureInlineRenameInfo.Instance) - |> CommonRoslynHelpers.StartAsyncAsTask(cancellationToken) + |> RoslynHelpers.StartAsyncAsTask(cancellationToken) diff --git a/vsintegration/src/FSharp.Editor/Common/AssemblyContentProvider.fs b/vsintegration/src/FSharp.Editor/LanguageService/AssemblyContentProvider.fs similarity index 100% rename from vsintegration/src/FSharp.Editor/Common/AssemblyContentProvider.fs rename to vsintegration/src/FSharp.Editor/LanguageService/AssemblyContentProvider.fs diff --git a/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerExtensions.fs new file mode 100644 index 0000000000000000000000000000000000000000..919b291baa062f88b6147268c1f83a2c41f8a497 --- /dev/null +++ b/vsintegration/src/FSharp.Editor/LanguageService/FSharpCheckerExtensions.fs @@ -0,0 +1,197 @@ +[] +module internal Microsoft.VisualStudio.FSharp.Editor.FSharpCheckerExtensions + +open System +open System.Threading.Tasks +open Microsoft.CodeAnalysis +open Microsoft.CodeAnalysis.Text +open Microsoft.FSharp.Compiler +open Microsoft.FSharp.Compiler.Ast +open Microsoft.FSharp.Compiler.SourceCodeServices +open Tokenizer +open Symbols +open TypedAstUtils + + +type CheckResults = + | Ready of (FSharpParseFileResults * FSharpCheckFileResults) option + | StillRunning of Async<(FSharpParseFileResults * FSharpCheckFileResults) option> + + +type FSharpChecker with + member this.ParseDocument(document: Document, options: FSharpProjectOptions, sourceText: string) = + asyncMaybe { + let! fileParseResults = this.ParseFileInProject(document.FilePath, sourceText, options) |> liftAsync + return! fileParseResults.ParseTree + } + + member this.ParseDocument(document: Document, options: FSharpProjectOptions, ?sourceText: SourceText) = + asyncMaybe { + let! sourceText = + match sourceText with + | Some x -> Task.FromResult x + | None -> document.GetTextAsync() + return! this.ParseDocument(document, options, sourceText.ToString()) + } + + member this.ParseAndCheckDocument(filePath: string, textVersionHash: int, sourceText: string, options: FSharpProjectOptions, allowStaleResults: bool) : Async<(FSharpParseFileResults * Ast.ParsedInput * FSharpCheckFileResults) option> = + let parseAndCheckFile = + async { + let! parseResults, checkFileAnswer = this.ParseAndCheckFileInProject(filePath, textVersionHash, sourceText, options) + return + match checkFileAnswer with + | FSharpCheckFileAnswer.Aborted -> + None + | FSharpCheckFileAnswer.Succeeded(checkFileResults) -> + Some (parseResults, checkFileResults) + } + + let tryGetFreshResultsWithTimeout() : Async = + async { + try + let! worker = Async.StartChild(parseAndCheckFile, 2000) + let! result = worker + return Ready result + with :? TimeoutException -> + return StillRunning parseAndCheckFile + } + + let bindParsedInput(results: (FSharpParseFileResults * FSharpCheckFileResults) option) = + match results with + | Some(parseResults, checkResults) -> + match parseResults.ParseTree with + | Some parsedInput -> Some (parseResults, parsedInput, checkResults) + | None -> None + | None -> None + + if allowStaleResults then + async { + let! freshResults = tryGetFreshResultsWithTimeout() + + let! results = + match freshResults with + | Ready x -> async.Return x + | StillRunning worker -> + async { + match allowStaleResults, this.TryGetRecentCheckResultsForFile(filePath, options) with + | true, Some (parseResults, checkFileResults, _) -> + return Some (parseResults, checkFileResults) + | _ -> + return! worker + } + return bindParsedInput results + } + else parseAndCheckFile |> Async.map bindParsedInput + + + member this.ParseAndCheckDocument(document: Document, options: FSharpProjectOptions, allowStaleResults: bool, ?sourceText: SourceText) : Async<(FSharpParseFileResults * Ast.ParsedInput * FSharpCheckFileResults) option> = + async { + let! cancellationToken = Async.CancellationToken + let! sourceText = + match sourceText with + | Some x -> Task.FromResult x + | None -> document.GetTextAsync() + let! textVersion = document.GetTextVersionAsync(cancellationToken) + return! this.ParseAndCheckDocument(document.FilePath, textVersion.GetHashCode(), sourceText.ToString(), options, allowStaleResults) + } + + + member self.TryParseAndCheckFileInProject (projectOptions, fileName, source) = async { + let! (parseResults, checkAnswer) = self.ParseAndCheckFileInProject (fileName,0, source,projectOptions) + match checkAnswer with + | FSharpCheckFileAnswer.Aborted -> return None + | FSharpCheckFileAnswer.Succeeded checkResults -> return Some (parseResults,checkResults) + } + + + member self.GetAllUsesOfAllSymbolsInSourceString (projectOptions, fileName, source: string, checkForUnusedOpens) = async { + + let! parseAndCheckResults = self.TryParseAndCheckFileInProject (projectOptions, fileName, source) + match parseAndCheckResults with + | None -> return [||] + | Some(_parseResults,checkResults) -> + let! fsharpSymbolsUses = checkResults.GetAllUsesOfAllSymbolsInFile() + let allSymbolsUses = + fsharpSymbolsUses + |> Array.map (fun symbolUse -> + let fullNames = + match symbolUse.Symbol with + // Make sure that unsafe manipulation isn't executed if unused opens are disabled + | _ when not checkForUnusedOpens -> None + | TypedAstPatterns.MemberFunctionOrValue func when func.IsExtensionMember -> + if func.IsProperty then + let fullNames = + [| if func.HasGetterMethod then + yield func.GetterMethod.EnclosingEntity.TryGetFullName() + if func.HasSetterMethod then + yield func.SetterMethod.EnclosingEntity.TryGetFullName() + |] + |> Array.choose id + match fullNames with + | [||] -> None + | _ -> Some fullNames + else + match func.EnclosingEntity with + // C# extension method + | TypedAstPatterns.FSharpEntity TypedAstPatterns.Class -> + let fullName = symbolUse.Symbol.FullName.Split '.' + if fullName.Length > 2 then + (* For C# extension methods FCS returns full name including the class name, like: + Namespace.StaticClass.ExtensionMethod + So, in order to properly detect that "open Namespace" actually opens ExtensionMethod, + we remove "StaticClass" part. This makes C# extension methods looks identically + with F# extension members. + *) + let fullNameWithoutClassName = + Array.append fullName.[0..fullName.Length - 3] fullName.[fullName.Length - 1..] + Some [|String.Join (".", fullNameWithoutClassName)|] + else None + | _ -> None + // Operators + | TypedAstPatterns.MemberFunctionOrValue func -> + match func with + | TypedAstPatterns.Constructor _ -> + // full name of a constructor looks like "UnusedSymbolClassifierTests.PrivateClass.( .ctor )" + // to make well formed full name parts we cut "( .ctor )" from the tail. + let fullName = func.FullName + let ctorSuffix = ".( .ctor )" + let fullName = + if fullName.EndsWith ctorSuffix then + fullName.[0..fullName.Length - ctorSuffix.Length - 1] + else fullName + Some [| fullName |] + | _ -> + Some [| yield func.FullName + match func.TryGetFullCompiledOperatorNameIdents() with + | Some idents -> yield String.concat "." idents + | None -> () + |] + | TypedAstPatterns.FSharpEntity e -> + match e with + | e, TypedAstPatterns.Attribute, _ -> + e.TryGetFullName () + |> Option.map (fun fullName -> + [| fullName; fullName.Substring(0, fullName.Length - "Attribute".Length) |]) + | e, _, _ -> + e.TryGetFullName () |> Option.map (fun fullName -> [| fullName |]) + | TypedAstPatterns.RecordField _ + | TypedAstPatterns.UnionCase _ as symbol -> + Some [| let fullName = symbol.FullName + yield fullName + let idents = fullName.Split '.' + // Union cases/Record fields can be accessible without mentioning the enclosing type. + // So we add a FullName without having the type part. + if idents.Length > 1 then + yield String.Join (".", Array.append idents.[0..idents.Length - 3] idents.[idents.Length - 1..]) + |] + | _ -> None + |> Option.defaultValue [|symbolUse.Symbol.FullName|] + |> Array.map (fun fullName -> fullName.Split '.') + + { SymbolUse = symbolUse + IsUsed = true + FullNames = fullNames + }) + return allSymbolsUses + } + diff --git a/vsintegration/src/FSharp.Editor/Common/LanguageService.fs b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs similarity index 91% rename from vsintegration/src/FSharp.Editor/Common/LanguageService.fs rename to vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs index 479a7031bb2aca379a45f2fbba739a462cc0a870..d92b09a1d11d19e776cf39098d39273931295276 100644 --- a/vsintegration/src/FSharp.Editor/Common/LanguageService.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs @@ -33,7 +33,7 @@ open Microsoft.VisualStudio.ComponentModelHost // Workaround to access non-public settings persistence type. // GetService( ) with this will work as long as the GUID matches the real type. -[] +[] type internal SVsSettingsPersistenceManager = class end // Exposes FSharpChecker as MEF export @@ -176,7 +176,7 @@ type internal RoamingProfileStorageLocation(keyName: string) = match languageName with | null -> unsubstitutedKeyName | _ -> - let substituteLanguageName = if languageName = FSharpCommonConstants.FSharpLanguageName then "FSharp" else languageName + let substituteLanguageName = if languageName = FSharpConstants.FSharpLanguageName then "FSharp" else languageName unsubstitutedKeyName.Replace("%LANGUAGE%", substituteLanguageName) [] @@ -194,9 +194,9 @@ type internal FSharpCheckerWorkspaceServiceFactory member this.ProjectInfoManager = projectInfoManager } type - [] + [] [, - strLanguageName = FSharpCommonConstants.FSharpLanguageName, + strLanguageName = FSharpConstants.FSharpLanguageName, languageResourceID = 100, MatchBraces = true, MatchBracesAtCaret = true, @@ -214,7 +214,7 @@ type internal FSharpPackage() = inherit AbstractPackage() - override this.RoslynLanguageName = FSharpCommonConstants.FSharpLanguageName + override this.RoslynLanguageName = FSharpConstants.FSharpLanguageName override this.CreateWorkspace() = this.ComponentModel.GetService() @@ -226,19 +226,19 @@ type override this.RegisterMiscellaneousFilesWorkspaceInformation(_) = () and - [] + [] [, ".fs")>] [, ".fsi")>] [, ".fsx")>] [, ".fsscript")>] [, ".ml")>] [, ".mli")>] - [] - [] - [] - [] - [] - [] + [] + [] + [] + [] + [] + [] internal FSharpLanguageService(package : FSharpPackage) = inherit AbstractLanguageService(package) @@ -260,8 +260,8 @@ and override this.Initialize() = base.Initialize() - this.Workspace.Options <- this.Workspace.Options.WithChangedOption(Completion.CompletionOptions.BlockForCompletionItems, FSharpCommonConstants.FSharpLanguageName, false) - this.Workspace.Options <- this.Workspace.Options.WithChangedOption(Shared.Options.ServiceFeatureOnOffOptions.ClosedFileDiagnostic, FSharpCommonConstants.FSharpLanguageName, Nullable false) + this.Workspace.Options <- this.Workspace.Options.WithChangedOption(Completion.CompletionOptions.BlockForCompletionItems, FSharpConstants.FSharpLanguageName, false) + this.Workspace.Options <- this.Workspace.Options.WithChangedOption(Shared.Options.ServiceFeatureOnOffOptions.ClosedFileDiagnostic, FSharpConstants.FSharpLanguageName, Nullable false) this.Workspace.DocumentClosed.Add <| fun args -> tryRemoveSingleFileProject args.Document.Project.Id @@ -331,14 +331,14 @@ and let projectContext = projectContextFactory.CreateProjectContext( - FSharpCommonConstants.FSharpLanguageName, projectDisplayName, projectFileName, projectGuid, siteProvider, null, errorReporter) + FSharpConstants.FSharpLanguageName, projectDisplayName, projectFileName, projectGuid, siteProvider, null, errorReporter) let project = projectContext :?> AbstractProject this.SyncProject(project, projectContext, site, forceUpdate=false) - site.AdviseProjectSiteChanges(FSharpCommonConstants.FSharpLanguageServiceCallbackName, + site.AdviseProjectSiteChanges(FSharpConstants.FSharpLanguageServiceCallbackName, AdviseProjectSiteChanges(fun () -> this.SyncProject(project, projectContext, site, forceUpdate=true))) - site.AdviseProjectSiteClosed(FSharpCommonConstants.FSharpLanguageServiceCallbackName, + site.AdviseProjectSiteClosed(FSharpConstants.FSharpLanguageServiceCallbackName, AdviseProjectSiteChanges(fun () -> projectInfoManager.ClearProjectInfo(project.Id) project.Disconnect())) @@ -363,17 +363,17 @@ and let projectContextFactory = package.ComponentModel.GetService(); let errorReporter = ProjectExternalErrorReporter(projectId, "FS", this.SystemServiceProvider) - let projectContext = projectContextFactory.CreateProjectContext(FSharpCommonConstants.FSharpLanguageName, projectDisplayName, projectFileName, projectId.Id, hier, null, errorReporter) + let projectContext = projectContextFactory.CreateProjectContext(FSharpConstants.FSharpLanguageName, projectDisplayName, projectFileName, projectId.Id, hier, null, errorReporter) projectContext.AddSourceFile(fileName) let project = projectContext :?> AbstractProject singleFileProjects.[projectId] <- project - override this.ContentTypeName = FSharpCommonConstants.FSharpContentTypeName - override this.LanguageName = FSharpCommonConstants.FSharpLanguageName - override this.RoslynLanguageName = FSharpCommonConstants.FSharpLanguageName + override this.ContentTypeName = FSharpConstants.FSharpContentTypeName + override this.LanguageName = FSharpConstants.FSharpLanguageName + override this.RoslynLanguageName = FSharpConstants.FSharpLanguageName - override this.LanguageServiceId = new Guid(FSharpCommonConstants.languageServiceGuidString) + override this.LanguageServiceId = new Guid(FSharpConstants.languageServiceGuidString) override this.DebuggerLanguageId = DebuggerEnvironment.GetLanguageID() override this.CreateContext(_,_,_,_,_) = raise(System.NotImplementedException()) diff --git a/vsintegration/src/FSharp.Editor/Common/SymbolHelpers.fs b/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs similarity index 92% rename from vsintegration/src/FSharp.Editor/Common/SymbolHelpers.fs rename to vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs index 49cfcc89108f5237ffb5e7bdc9f5464e0d373438..256ef503b5b2966d19ea2372cfcae937437b2790 100644 --- a/vsintegration/src/FSharp.Editor/Common/SymbolHelpers.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/SymbolHelpers.fs @@ -11,6 +11,8 @@ open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.Text open Microsoft.FSharp.Compiler.SourceCodeServices +open Symbols + module internal SymbolHelpers = let getSymbolUsesInSolution (symbol: FSharpSymbol, declLoc: SymbolDeclarationLocation, checkFileResults: FSharpCheckFileResults, @@ -63,7 +65,7 @@ module internal SymbolHelpers = do! Option.guard (originalText.Length > 0) let! options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject document let defines = CompilerEnvironment.GetCompilationDefinesForEditing(document.Name, options.OtherOptions |> Seq.toList) - let! symbol = CommonHelpers.getSymbolAtPosition(document.Id, sourceText, symbolSpan.Start, document.FilePath, defines, SymbolLookupKind.Greedy) + let! symbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, symbolSpan.Start, document.FilePath, defines, Tokenizer.SymbolLookupKind.Greedy) let! _, _, checkFileResults = checker.ParseAndCheckDocument(document, options, allowStaleResults = true) let textLine = sourceText.Lines.GetLineFromPosition(symbolSpan.Start) let textLinePos = sourceText.Lines.GetLinePosition(symbolSpan.Start) @@ -85,10 +87,10 @@ module internal SymbolHelpers = let! sourceText = document.GetTextAsync(cancellationToken) let mutable sourceText = sourceText for symbolUse in symbolUses do - let textSpan = CommonHelpers.fixupSpan(sourceText, CommonRoslynHelpers.FSharpRangeToTextSpan(sourceText, symbolUse.RangeAlternate)) + let textSpan = Tokenizer.fixupSpan(sourceText, RoslynHelpers.FSharpRangeToTextSpan(sourceText, symbolUse.RangeAlternate)) sourceText <- sourceText.Replace(textSpan, newText) solution <- solution.WithDocumentText(documentId, sourceText) return solution - } |> CommonRoslynHelpers.StartAsyncAsTask cancellationToken), + } |> RoslynHelpers.StartAsyncAsTask cancellationToken), originalText } diff --git a/vsintegration/src/FSharp.Editor/LanguageService/Symbols.fs b/vsintegration/src/FSharp.Editor/LanguageService/Symbols.fs new file mode 100644 index 0000000000000000000000000000000000000000..ce76593457c655769c8570f38a484d0dc86adf85 --- /dev/null +++ b/vsintegration/src/FSharp.Editor/LanguageService/Symbols.fs @@ -0,0 +1,351 @@ +[] +module internal Microsoft.VisualStudio.FSharp.Editor.Symbols + +open System +open System.Collections.Generic +open System.Threading +open System.Threading.Tasks +open System.Runtime.CompilerServices + +open Microsoft.CodeAnalysis +open Microsoft.CodeAnalysis.Classification +open Microsoft.CodeAnalysis.Text + +open Microsoft.VisualStudio.FSharp.LanguageService +open Microsoft.FSharp.Compiler +open Microsoft.FSharp.Compiler.Ast +open Microsoft.FSharp.Compiler.SourceCodeServices +open System.IO + + +[] +type SymbolDeclarationLocation = + | CurrentDocument + | Projects of Project list * isLocalForProject: bool + + +[] +type SymbolUse = + { SymbolUse: FSharpSymbolUse + IsUsed: bool + FullNames: Idents[] } + + +type FSharpSymbol with + member this.IsInternalToProject = + match this with + | :? FSharpParameter -> true + | :? FSharpMemberOrFunctionOrValue as m -> not m.IsModuleValueOrMember || not m.Accessibility.IsPublic + | :? FSharpEntity as m -> not m.Accessibility.IsPublic + | :? FSharpGenericParameter -> true + | :? FSharpUnionCase as m -> not m.Accessibility.IsPublic + | :? FSharpField as m -> not m.Accessibility.IsPublic + | _ -> false + + +type FSharpSymbolUse with + member this.GetDeclarationLocation (currentDocument: Document) : SymbolDeclarationLocation option = + if this.IsPrivateToFile then + Some SymbolDeclarationLocation.CurrentDocument + else + let isSymbolLocalForProject = this.Symbol.IsInternalToProject + + let declarationLocation = + match this.Symbol.ImplementationLocation with + | Some x -> Some x + | None -> this.Symbol.DeclarationLocation + + match declarationLocation with + | Some loc -> + let filePath = Path.GetFullPathSafe loc.FileName + let isScript = isScriptFile filePath + if isScript && filePath = currentDocument.FilePath then + Some SymbolDeclarationLocation.CurrentDocument + elif isScript then + // The standalone script might include other files via '#load' + // These files appear in project options and the standalone file + // should be treated as an individual project + Some (SymbolDeclarationLocation.Projects ([currentDocument.Project], isSymbolLocalForProject)) + else + let projects = + currentDocument.Project.Solution.GetDocumentIdsWithFilePath(filePath) + |> Seq.map (fun x -> x.ProjectId) + |> Seq.distinct + |> Seq.map currentDocument.Project.Solution.GetProject + |> Seq.toList + match projects with + | [] -> None + | projects -> Some (SymbolDeclarationLocation.Projects (projects, isSymbolLocalForProject)) + | None -> None + + member this.IsPrivateToFile = + let isPrivate = + match this.Symbol with + | :? FSharpMemberOrFunctionOrValue as m -> not m.IsModuleValueOrMember || m.Accessibility.IsPrivate + | :? FSharpEntity as m -> m.Accessibility.IsPrivate + | :? FSharpGenericParameter -> true + | :? FSharpUnionCase as m -> m.Accessibility.IsPrivate + | :? FSharpField as m -> m.Accessibility.IsPrivate + | _ -> false + + let declarationLocation = + match this.Symbol.SignatureLocation with + | Some x -> Some x + | _ -> + match this.Symbol.DeclarationLocation with + | Some x -> Some x + | _ -> this.Symbol.ImplementationLocation + + let declaredInTheFile = + match declarationLocation with + | Some declRange -> declRange.FileName = this.RangeAlternate.FileName + | _ -> false + + isPrivate && declaredInTheFile + + +type FSharpMemberOrFunctionOrValue with + + member x.IsConstructor = x.CompiledName = ".ctor" + + member x.IsOperatorOrActivePattern = + let name = x.DisplayName + if name.StartsWith "( " && name.EndsWith " )" && name.Length > 4 + then name.Substring (2, name.Length - 4) |> String.forall (fun c -> c <> ' ') + else false + + member x.EnclosingEntitySafe = + try + Some x.EnclosingEntity + with :? InvalidOperationException -> None + + +type FSharpEntity with + member x.AllBaseTypes = + let rec allBaseTypes (entity:FSharpEntity) = + [ + match entity.TryFullName with + | Some _ -> + match entity.BaseType with + | Some bt -> + yield bt + if bt.HasTypeDefinition then + yield! allBaseTypes bt.TypeDefinition + | _ -> () + | _ -> () + ] + allBaseTypes x + + + + +/// Active patterns over `FSharpSymbolUse`. +module SymbolUse = + + let (|ActivePatternCase|_|) (symbol : FSharpSymbolUse) = + match symbol.Symbol with + | :? FSharpActivePatternCase as ap-> ActivePatternCase(ap) |> Some + | _ -> None + + let private attributeSuffixLength = "Attribute".Length + + let (|Entity|_|) (symbol : FSharpSymbolUse) : (FSharpEntity * (* cleanFullNames *) string list) option = + match symbol.Symbol with + | :? FSharpEntity as ent -> + // strip generic parameters count suffix (List`1 => List) + let cleanFullName = + // `TryFullName` for type aliases is always `None`, so we have to make one by our own + if ent.IsFSharpAbbreviation then + [ent.AccessPath + "." + ent.DisplayName] + else + ent.TryFullName + |> Option.toList + |> List.map (fun fullName -> + if ent.GenericParameters.Count > 0 && fullName.Length > 2 then + fullName.[0..fullName.Length - 3] + else fullName) + + let cleanFullNames = + cleanFullName + |> List.collect (fun cleanFullName -> + if ent.IsAttributeType then + [cleanFullName; cleanFullName.[0..cleanFullName.Length - attributeSuffixLength - 1]] + else [cleanFullName] + ) + Some (ent, cleanFullNames) + | _ -> None + + + let (|Field|_|) (symbol : FSharpSymbolUse) = + match symbol.Symbol with + | :? FSharpField as field-> Some field + | _ -> None + + let (|GenericParameter|_|) (symbol: FSharpSymbolUse) = + match symbol.Symbol with + | :? FSharpGenericParameter as gp -> Some gp + | _ -> None + + let (|MemberFunctionOrValue|_|) (symbol : FSharpSymbolUse) = + match symbol.Symbol with + | :? FSharpMemberOrFunctionOrValue as func -> Some func + | _ -> None + + let (|ActivePattern|_|) = function + | MemberFunctionOrValue m when m.IsActivePattern -> Some m | _ -> None + + let (|Parameter|_|) (symbol : FSharpSymbolUse) = + match symbol.Symbol with + | :? FSharpParameter as param -> Some param + | _ -> None + + let (|StaticParameter|_|) (symbol : FSharpSymbolUse) = + match symbol.Symbol with + | :? FSharpStaticParameter as sp -> Some sp + | _ -> None + + let (|UnionCase|_|) (symbol : FSharpSymbolUse) = + match symbol.Symbol with + | :? FSharpUnionCase as uc-> Some uc + | _ -> None + + //let (|Constructor|_|) = function + // | MemberFunctionOrValue func when func.IsConstructor || func.IsImplicitConstructor -> Some func + // | _ -> None + + let (|TypeAbbreviation|_|) = function + | Entity (entity, _) when entity.IsFSharpAbbreviation -> Some entity + | _ -> None + + let (|Class|_|) = function + | Entity (entity, _) when entity.IsClass -> Some entity + | Entity (entity, _) when entity.IsFSharp && + entity.IsOpaque && + not entity.IsFSharpModule && + not entity.IsNamespace && + not entity.IsDelegate && + not entity.IsFSharpUnion && + not entity.IsFSharpRecord && + not entity.IsInterface && + not entity.IsValueType -> Some entity + | _ -> None + + let (|Delegate|_|) = function + | Entity (entity, _) when entity.IsDelegate -> Some entity + | _ -> None + + let (|Event|_|) = function + | MemberFunctionOrValue symbol when symbol.IsEvent -> Some symbol + | _ -> None + + let (|Property|_|) = function + | MemberFunctionOrValue symbol when symbol.IsProperty || symbol.IsPropertyGetterMethod || symbol.IsPropertySetterMethod -> Some symbol + | _ -> None + + let inline private notCtorOrProp (symbol:FSharpMemberOrFunctionOrValue) = + not symbol.IsConstructor && not symbol.IsPropertyGetterMethod && not symbol.IsPropertySetterMethod + + let (|Method|_|) (symbolUse:FSharpSymbolUse) = + match symbolUse with + | MemberFunctionOrValue symbol when + symbol.IsModuleValueOrMember && + not symbolUse.IsFromPattern && + not symbol.IsOperatorOrActivePattern && + not symbol.IsPropertyGetterMethod && + not symbol.IsPropertySetterMethod -> Some symbol + | _ -> None + + let (|Function|_|) (symbolUse:FSharpSymbolUse) = + match symbolUse with + | MemberFunctionOrValue symbol when + notCtorOrProp symbol && + symbol.IsModuleValueOrMember && + not symbol.IsOperatorOrActivePattern && + not symbolUse.IsFromPattern -> + + match symbol.FullTypeSafe with + | Some fullType when fullType.IsFunctionType -> Some symbol + | _ -> None + | _ -> None + + let (|Operator|_|) (symbolUse:FSharpSymbolUse) = + match symbolUse with + | MemberFunctionOrValue symbol when + notCtorOrProp symbol && + not symbolUse.IsFromPattern && + not symbol.IsActivePattern && + symbol.IsOperatorOrActivePattern -> + + match symbol.FullTypeSafe with + | Some fullType when fullType.IsFunctionType -> Some symbol + | _ -> None + | _ -> None + + let (|Pattern|_|) (symbolUse:FSharpSymbolUse) = + match symbolUse with + | MemberFunctionOrValue symbol when + notCtorOrProp symbol && + not symbol.IsOperatorOrActivePattern && + symbolUse.IsFromPattern -> + + match symbol.FullTypeSafe with + | Some fullType when fullType.IsFunctionType ->Some symbol + | _ -> None + | _ -> None + + + let (|ClosureOrNestedFunction|_|) = function + | MemberFunctionOrValue symbol when + notCtorOrProp symbol && + not symbol.IsOperatorOrActivePattern && + not symbol.IsModuleValueOrMember -> + + match symbol.FullTypeSafe with + | Some fullType when fullType.IsFunctionType -> Some symbol + | _ -> None + | _ -> None + + + let (|Val|_|) = function + | MemberFunctionOrValue symbol when notCtorOrProp symbol && + not symbol.IsOperatorOrActivePattern -> + match symbol.FullTypeSafe with + | Some _fullType -> Some symbol + | _ -> None + | _ -> None + + let (|Enum|_|) = function + | Entity (entity, _) when entity.IsEnum -> Some entity + | _ -> None + + let (|Interface|_|) = function + | Entity (entity, _) when entity.IsInterface -> Some entity + | _ -> None + + let (|Module|_|) = function + | Entity (entity, _) when entity.IsFSharpModule -> Some entity + | _ -> None + + let (|Namespace|_|) = function + | Entity (entity, _) when entity.IsNamespace -> Some entity + | _ -> None + + let (|Record|_|) = function + | Entity (entity, _) when entity.IsFSharpRecord -> Some entity + | _ -> None + + let (|Union|_|) = function + | Entity (entity, _) when entity.IsFSharpUnion -> Some entity + | _ -> None + + let (|ValueType|_|) = function + | Entity (entity, _) when entity.IsValueType && not entity.IsEnum -> Some entity + | _ -> None + + let (|ComputationExpression|_|) (symbol:FSharpSymbolUse) = + if symbol.IsFromComputationExpression then Some symbol + else None + + let (|Attribute|_|) = function + | Entity (entity, _) when entity.IsAttributeType -> Some entity + | _ -> None \ No newline at end of file diff --git a/vsintegration/src/FSharp.Editor/LanguageService/Tokenizer.fs b/vsintegration/src/FSharp.Editor/LanguageService/Tokenizer.fs new file mode 100644 index 0000000000000000000000000000000000000000..19fed27cb220757352090ed7f74c2be785e921d5 --- /dev/null +++ b/vsintegration/src/FSharp.Editor/LanguageService/Tokenizer.fs @@ -0,0 +1,587 @@ +module internal Microsoft.VisualStudio.FSharp.Editor.Tokenizer + +open System +open System.Collections.Generic +open System.Threading +open System.Threading.Tasks +open System.Runtime.CompilerServices + +open Microsoft.CodeAnalysis +open Microsoft.CodeAnalysis.Classification +open Microsoft.CodeAnalysis.Text + +open Microsoft.VisualStudio.FSharp.LanguageService +open Microsoft.FSharp.Compiler +open Microsoft.FSharp.Compiler.Ast +open Microsoft.FSharp.Compiler.SourceCodeServices + + +[] +type LexerSymbolKind = + | Ident + | Operator + | Punctuation + | GenericTypeParameter + | StaticallyResolvedTypeParameter + | Other + +type LexerSymbol = + { Kind: LexerSymbolKind + /// Last part of `LongIdent` + Ident: Ident + /// All parts of `LongIdent` + FullIsland: string list } + member x.Range: Range.range = x.Ident.idRange + +[] +type SymbolLookupKind = + /// Position must lay inside symbol range. + | Precise + /// Position may lay one column outside of symbol range to the right. + | Greedy + + + +let inline (|Public|Internal|Protected|Private|) (a: FSharpAccessibility option) = + match a with + | None -> Public + | Some a -> + if a.IsPublic then Public + elif a.IsInternal then Internal + elif a.IsPrivate then Private + else Protected + +let FSharpGlyphToRoslynGlyph (glyph: FSharpGlyph, accessibility: FSharpAccessibility option) = + match glyph with + | FSharpGlyph.Class + | FSharpGlyph.Exception + | FSharpGlyph.Typedef + | FSharpGlyph.Type -> + match accessibility with + | Public -> Glyph.ClassPublic + | Internal -> Glyph.ClassInternal + | Protected -> Glyph.ClassProtected + | Private -> Glyph.ClassPrivate + | FSharpGlyph.Constant -> + match accessibility with + | Public -> Glyph.ConstantPublic + | Internal -> Glyph.ConstantInternal + | Protected -> Glyph.ConstantProtected + | Private -> Glyph.ConstantPrivate + | FSharpGlyph.Delegate -> + match accessibility with + | Public -> Glyph.DelegatePublic + | Internal -> Glyph.DelegateInternal + | Protected -> Glyph.DelegateProtected + | Private -> Glyph.DelegatePrivate + | FSharpGlyph.Enum + | FSharpGlyph.Union -> + match accessibility with + | Public -> Glyph.EnumPublic + | Internal -> Glyph.EnumInternal + | Protected -> Glyph.EnumProtected + | Private -> Glyph.EnumPrivate + | FSharpGlyph.EnumMember -> Glyph.EnumMember + | FSharpGlyph.Event -> + match accessibility with + | Public -> Glyph.EventPublic + | Internal -> Glyph.EventInternal + | Protected -> Glyph.EventProtected + | Private -> Glyph.EventPrivate + | FSharpGlyph.Field -> + match accessibility with + | Public -> Glyph.FieldPublic + | Internal -> Glyph.FieldInternal + | Protected -> Glyph.FieldProtected + | Private -> Glyph.FieldPrivate + | FSharpGlyph.Interface -> + match accessibility with + | Public -> Glyph.InterfacePublic + | Internal -> Glyph.InterfaceInternal + | Protected -> Glyph.InterfaceProtected + | Private -> Glyph.InterfacePrivate + | FSharpGlyph.Method + | FSharpGlyph.OverridenMethod -> + match accessibility with + | Public -> Glyph.MethodPublic + | Internal -> Glyph.MethodInternal + | Protected -> Glyph.MethodProtected + | Private -> Glyph.MethodPrivate + | FSharpGlyph.ExtensionMethod -> + match accessibility with + | Public -> Glyph.ExtensionMethodPublic + | Internal -> Glyph.ExtensionMethodInternal + | Protected -> Glyph.ExtensionMethodProtected + | Private -> Glyph.ExtensionMethodPrivate + | FSharpGlyph.Module -> + match accessibility with + | Public -> Glyph.ModulePublic + | Internal -> Glyph.ModuleInternal + | Protected -> Glyph.ModuleProtected + | Private -> Glyph.ModulePrivate + | FSharpGlyph.NameSpace -> Glyph.Namespace + | FSharpGlyph.Property -> + match accessibility with + | Public -> Glyph.PropertyPublic + | Internal -> Glyph.PropertyInternal + | Protected -> Glyph.PropertyProtected + | Private -> Glyph.PropertyPrivate + | FSharpGlyph.Struct -> + match accessibility with + | Public -> Glyph.StructurePublic + | Internal -> Glyph.StructureInternal + | Protected -> Glyph.StructureProtected + | Private -> Glyph.StructurePrivate + | FSharpGlyph.Variable -> Glyph.Local + | FSharpGlyph.Error -> Glyph.Error + +let GetGlyphForSymbol (symbol: FSharpSymbol, kind: LexerSymbolKind) = + match kind with + | LexerSymbolKind.Operator -> Glyph.Operator + | _ -> + match symbol with + | :? FSharpUnionCase as x -> + match Some x.Accessibility with + | Public -> Glyph.EnumPublic + | Internal -> Glyph.EnumInternal + | Protected -> Glyph.EnumProtected + | Private -> Glyph.EnumPrivate + | :? FSharpActivePatternCase -> Glyph.EnumPublic + | :? FSharpField as x -> + if x.IsLiteral then + match Some x.Accessibility with + | Public -> Glyph.ConstantPublic + | Internal -> Glyph.ConstantInternal + | Protected -> Glyph.ConstantProtected + | Private -> Glyph.ConstantPrivate + else + match Some x.Accessibility with + | Public -> Glyph.FieldPublic + | Internal -> Glyph.FieldInternal + | Protected -> Glyph.FieldProtected + | Private -> Glyph.FieldPrivate + | :? FSharpParameter -> Glyph.Parameter + | :? FSharpMemberOrFunctionOrValue as x -> + if x.LiteralValue.IsSome then + match Some x.Accessibility with + | Public -> Glyph.ConstantPublic + | Internal -> Glyph.ConstantInternal + | Protected -> Glyph.ConstantProtected + | Private -> Glyph.ConstantPrivate + elif x.IsExtensionMember then + match Some x.Accessibility with + | Public -> Glyph.ExtensionMethodPublic + | Internal -> Glyph.ExtensionMethodInternal + | Protected -> Glyph.ExtensionMethodProtected + | Private -> Glyph.ExtensionMethodPrivate + elif x.IsProperty || x.IsPropertyGetterMethod || x.IsPropertySetterMethod then + match Some x.Accessibility with + | Public -> Glyph.PropertyPublic + | Internal -> Glyph.PropertyInternal + | Protected -> Glyph.PropertyProtected + | Private -> Glyph.PropertyPrivate + elif x.IsEvent then + match Some x.Accessibility with + | Public -> Glyph.EventPublic + | Internal -> Glyph.EventInternal + | Protected -> Glyph.EventProtected + | Private -> Glyph.EventPrivate + else + match Some x.Accessibility with + | Public -> Glyph.MethodPublic + | Internal -> Glyph.MethodInternal + | Protected -> Glyph.MethodProtected + | Private -> Glyph.MethodPrivate + | :? FSharpEntity as x -> + if x.IsValueType then + match Some x.Accessibility with + | Public -> Glyph.StructurePublic + | Internal -> Glyph.StructureInternal + | Protected -> Glyph.StructureProtected + | Private -> Glyph.StructurePrivate + elif x.IsFSharpModule then + match Some x.Accessibility with + | Public -> Glyph.ModulePublic + | Internal -> Glyph.ModuleInternal + | Protected -> Glyph.ModuleProtected + | Private -> Glyph.ModulePrivate + elif x.IsEnum || x.IsFSharpUnion then + match Some x.Accessibility with + | Public -> Glyph.EnumPublic + | Internal -> Glyph.EnumInternal + | Protected -> Glyph.EnumProtected + | Private -> Glyph.EnumPrivate + elif x.IsInterface then + match Some x.Accessibility with + | Public -> Glyph.InterfacePublic + | Internal -> Glyph.InterfaceInternal + | Protected -> Glyph.InterfaceProtected + | Private -> Glyph.InterfacePrivate + elif x.IsDelegate then + match Some x.Accessibility with + | Public -> Glyph.DelegatePublic + | Internal -> Glyph.DelegateInternal + | Protected -> Glyph.DelegateProtected + | Private -> Glyph.DelegatePrivate + elif x.IsNamespace then + Glyph.Namespace + else + match Some x.Accessibility with + | Public -> Glyph.ClassPublic + | Internal -> Glyph.ClassInternal + | Protected -> Glyph.ClassProtected + | Private -> Glyph.ClassPrivate + | _ -> Glyph.None + + +type private SourceLineData(lineStart: int, lexStateAtStartOfLine: FSharpTokenizerLexState, lexStateAtEndOfLine: FSharpTokenizerLexState, + hashCode: int, classifiedSpans: IReadOnlyList, tokens: FSharpTokenInfo list) = + member val LineStart = lineStart + member val LexStateAtStartOfLine = lexStateAtStartOfLine + member val LexStateAtEndOfLine = lexStateAtEndOfLine + member val HashCode = hashCode + member val ClassifiedSpans = classifiedSpans + member val Tokens = tokens + + member data.IsValid(textLine: TextLine) = + data.LineStart = textLine.Start && + let lineContents = textLine.Text.ToString(textLine.Span) + data.HashCode = lineContents.GetHashCode() + +type private SourceTextData(approxLines: int) = + let data = ResizeArray(approxLines) + let extendTo i = + if i >= data.Count then + data.Capacity <- i + 1 + for j in data.Count .. i do + data.Add(None) + member x.Item + with get (i:int) = extendTo i; data.[i] + and set (i:int) v = extendTo i; data.[i] <- v + + member x.ClearFrom(n) = + let mutable i = n + while i < data.Count && data.[i].IsSome do + data.[i] <- None + i <- i + 1 + +let private dataCache = ConditionalWeakTable() + +let compilerTokenToRoslynToken(colorKind: FSharpTokenColorKind) : string = + match colorKind with + | FSharpTokenColorKind.Comment -> ClassificationTypeNames.Comment + | FSharpTokenColorKind.Identifier -> ClassificationTypeNames.Identifier + | FSharpTokenColorKind.Keyword -> ClassificationTypeNames.Keyword + | FSharpTokenColorKind.String -> ClassificationTypeNames.StringLiteral + | FSharpTokenColorKind.Text -> ClassificationTypeNames.Text + | FSharpTokenColorKind.UpperIdentifier -> ClassificationTypeNames.Identifier + | FSharpTokenColorKind.Number -> ClassificationTypeNames.NumericLiteral + | FSharpTokenColorKind.InactiveCode -> ClassificationTypeNames.ExcludedCode + | FSharpTokenColorKind.PreprocessorKeyword -> ClassificationTypeNames.PreprocessorKeyword + | FSharpTokenColorKind.Operator -> ClassificationTypeNames.Operator + | FSharpTokenColorKind.Punctuation -> ClassificationTypeNames.Punctuation + | FSharpTokenColorKind.Default | _ -> ClassificationTypeNames.Text + +let private scanSourceLine(sourceTokenizer: FSharpSourceTokenizer, textLine: TextLine, lineContents: string, lexState: FSharpTokenizerLexState) : SourceLineData = + let colorMap = Array.create textLine.Span.Length ClassificationTypeNames.Text + let lineTokenizer = sourceTokenizer.CreateLineTokenizer(lineContents) + let tokens = ResizeArray() + + let scanAndColorNextToken(lineTokenizer: FSharpLineTokenizer, lexState: Ref) : Option = + let tokenInfoOption, nextLexState = lineTokenizer.ScanToken(lexState.Value) + lexState.Value <- nextLexState + if tokenInfoOption.IsSome then + let classificationType = compilerTokenToRoslynToken(tokenInfoOption.Value.ColorClass) + for i = tokenInfoOption.Value.LeftColumn to tokenInfoOption.Value.RightColumn do + Array.set colorMap i classificationType + tokens.Add tokenInfoOption.Value + tokenInfoOption + + let previousLexState = ref lexState + let mutable tokenInfoOption = scanAndColorNextToken(lineTokenizer, previousLexState) + while tokenInfoOption.IsSome do + tokenInfoOption <- scanAndColorNextToken(lineTokenizer, previousLexState) + + let mutable startPosition = 0 + let mutable endPosition = startPosition + let classifiedSpans = new List() + + while startPosition < colorMap.Length do + let classificationType = colorMap.[startPosition] + endPosition <- startPosition + while endPosition < colorMap.Length && classificationType = colorMap.[endPosition] do + endPosition <- endPosition + 1 + let textSpan = new TextSpan(textLine.Start + startPosition, endPosition - startPosition) + classifiedSpans.Add(new ClassifiedSpan(classificationType, textSpan)) + startPosition <- endPosition + + SourceLineData(textLine.Start, lexState, previousLexState.Value, lineContents.GetHashCode(), classifiedSpans, List.ofSeq tokens) + +let getColorizationData(documentKey: DocumentId, sourceText: SourceText, textSpan: TextSpan, fileName: string option, defines: string list, + cancellationToken: CancellationToken) : List = + try + let sourceTokenizer = FSharpSourceTokenizer(defines, fileName) + let lines = sourceText.Lines + // We keep incremental data per-document. When text changes we correlate text line-by-line (by hash codes of lines) + let sourceTextData = dataCache.GetValue(documentKey, fun key -> SourceTextData(lines.Count)) + + let startLine = lines.GetLineFromPosition(textSpan.Start).LineNumber + let endLine = lines.GetLineFromPosition(textSpan.End).LineNumber + // Go backwards to find the last cached scanned line that is valid + let scanStartLine = + let mutable i = startLine + while i > 0 && (match sourceTextData.[i] with Some data -> not (data.IsValid(lines.[i])) | None -> true) do + i <- i - 1 + i + // Rescan the lines if necessary and report the information + let result = new List() + let mutable lexState = if scanStartLine = 0 then 0L else sourceTextData.[scanStartLine - 1].Value.LexStateAtEndOfLine + + for i = scanStartLine to endLine do + cancellationToken.ThrowIfCancellationRequested() + let textLine = lines.[i] + let lineContents = textLine.Text.ToString(textLine.Span) + + let lineData = + // We can reuse the old data when + // 1. the line starts at the same overall position + // 2. the hash codes match + // 3. the start-of-line lex states are the same + match sourceTextData.[i] with + | Some data when data.IsValid(textLine) && data.LexStateAtStartOfLine = lexState -> + data + | _ -> + // Otherwise, we recompute + let newData = scanSourceLine(sourceTokenizer, textLine, lineContents, lexState) + sourceTextData.[i] <- Some newData + newData + + lexState <- lineData.LexStateAtEndOfLine + + if startLine <= i then + result.AddRange(lineData.ClassifiedSpans |> Seq.filter(fun token -> + textSpan.Contains(token.TextSpan.Start) || + textSpan.Contains(token.TextSpan.End - 1) || + (token.TextSpan.Start <= textSpan.Start && textSpan.End <= token.TextSpan.End))) + + // If necessary, invalidate all subsequent lines after endLine + if endLine < lines.Count - 1 then + match sourceTextData.[endLine+1] with + | Some data -> + if data.LexStateAtStartOfLine <> lexState then + sourceTextData.ClearFrom (endLine+1) + | None -> () + result + with + | :? System.OperationCanceledException -> reraise() + | ex -> + Assert.Exception(ex) + List() + +type private DraftToken = { + Kind: LexerSymbolKind + Token: FSharpTokenInfo + RightColumn: int +} with + static member inline Create kind token = + { Kind = kind; Token = token; RightColumn = token.LeftColumn + token.FullMatchedLength - 1 } + +/// Returns symbol at a given position. +let private getSymbolFromTokens (fileName: string, tokens: FSharpTokenInfo list, linePos: LinePosition, lineStr: string, lookupKind: SymbolLookupKind) : LexerSymbol option = + let isIdentifier t = t.CharClass = FSharpTokenCharKind.Identifier + let isOperator t = t.ColorClass = FSharpTokenColorKind.Operator + let isPunctuation t = t.ColorClass = FSharpTokenColorKind.Punctuation + + let inline (|GenericTypeParameterPrefix|StaticallyResolvedTypeParameterPrefix|Other|) (token: FSharpTokenInfo) = + if token.Tag = FSharpTokenTag.QUOTE then GenericTypeParameterPrefix + elif token.Tag = FSharpTokenTag.INFIX_AT_HAT_OP then + // The lexer return INFIX_AT_HAT_OP token for both "^" and "@" symbols. + // We have to check the char itself to distinguish one from another. + if token.FullMatchedLength = 1 && lineStr.[token.LeftColumn] = '^' then + StaticallyResolvedTypeParameterPrefix + else Other + else Other + + // Operators: Filter out overlapped operators (>>= operator is tokenized as three distinct tokens: GREATER, GREATER, EQUALS. + // Each of them has FullMatchedLength = 3. So, we take the first GREATER and skip the other two). + // + // Generic type parameters: we convert QUOTE + IDENT tokens into single IDENT token, altering its LeftColumn + // and FullMathedLength (for "'type" which is tokenized as (QUOTE, left=2) + (IDENT, left=3, length=4) + // we'll get (IDENT, left=2, length=5). + // + // Statically resolved type parameters: we convert INFIX_AT_HAT_OP + IDENT tokens into single IDENT token, altering its LeftColumn + // and FullMathedLength (for "^type" which is tokenized as (INFIX_AT_HAT_OP, left=2) + (IDENT, left=3, length=4) + // we'll get (IDENT, left=2, length=5). + let tokens = + let tokensCount = tokens.Length + tokens + |> List.foldi (fun (acc, lastToken) index (token: FSharpTokenInfo) -> + match lastToken with + | Some t when token.LeftColumn <= t.RightColumn -> acc, lastToken + | _ -> + let isLastToken = index = tokensCount - 1 + match token with + | GenericTypeParameterPrefix when not isLastToken -> acc, Some (DraftToken.Create LexerSymbolKind.GenericTypeParameter token) + | StaticallyResolvedTypeParameterPrefix when not isLastToken -> acc, Some (DraftToken.Create LexerSymbolKind.StaticallyResolvedTypeParameter token) + | _ -> + let draftToken = + match lastToken with + | Some { Kind = LexerSymbolKind.GenericTypeParameter | LexerSymbolKind.StaticallyResolvedTypeParameter as kind } when isIdentifier token -> + DraftToken.Create kind { token with LeftColumn = token.LeftColumn - 1 + FullMatchedLength = token.FullMatchedLength + 1 } + // ^ operator + | Some { Kind = LexerSymbolKind.StaticallyResolvedTypeParameter } -> + DraftToken.Create LexerSymbolKind.Operator { token with LeftColumn = token.LeftColumn - 1 + FullMatchedLength = 1 } + | _ -> + let kind = + if isOperator token then LexerSymbolKind.Operator + elif isIdentifier token then LexerSymbolKind.Ident + elif isPunctuation token then LexerSymbolKind.Punctuation + else LexerSymbolKind.Other + + DraftToken.Create kind token + draftToken :: acc, Some draftToken + ) ([], None) + |> fst + + // One or two tokens that in touch with the cursor (for "let x|(g) = ()" the tokens will be "x" and "(") + let tokensUnderCursor = + let rightColumnCorrection = + match lookupKind with + | SymbolLookupKind.Precise -> 0 + | SymbolLookupKind.Greedy -> 1 + + tokens |> List.filter (fun x -> x.Token.LeftColumn <= linePos.Character && (x.RightColumn + rightColumnCorrection) >= linePos.Character) + + // Select IDENT token. If failed, select OPERATOR token. + tokensUnderCursor + |> List.tryFind (fun { DraftToken.Kind = k } -> + match k with + | LexerSymbolKind.Ident + | LexerSymbolKind.GenericTypeParameter + | LexerSymbolKind.StaticallyResolvedTypeParameter -> true + | _ -> false) + |> Option.orElseWith (fun _ -> tokensUnderCursor |> List.tryFind (fun { DraftToken.Kind = k } -> k = LexerSymbolKind.Operator)) + |> Option.map (fun token -> + let plid, _ = QuickParse.GetPartialLongNameEx(lineStr, token.RightColumn) + let identStr = lineStr.Substring(token.Token.LeftColumn, token.Token.FullMatchedLength) + { Kind = token.Kind + Ident = + Ident(identStr, + Range.mkRange + fileName + (Range.mkPos (linePos.Line + 1) token.Token.LeftColumn) + (Range.mkPos (linePos.Line + 1) (token.RightColumn + 1))) + FullIsland = plid @ [identStr] }) + +let private getCachedSourceLineData(documentKey: DocumentId, sourceText: SourceText, position: int, fileName: string, defines: string list) = + let textLine = sourceText.Lines.GetLineFromPosition(position) + let textLinePos = sourceText.Lines.GetLinePosition(position) + let lineNumber = textLinePos.Line + 1 // FCS line number + let sourceTokenizer = FSharpSourceTokenizer(defines, Some fileName) + let lines = sourceText.Lines + // We keep incremental data per-document. When text changes we correlate text line-by-line (by hash codes of lines) + let sourceTextData = dataCache.GetValue(documentKey, fun key -> SourceTextData(lines.Count)) + // Go backwards to find the last cached scanned line that is valid + let scanStartLine = + let mutable i = min (lines.Count - 1) lineNumber + while i > 0 && + (match sourceTextData.[i] with + | Some data -> not (data.IsValid(lines.[i])) + | None -> true + ) do + i <- i - 1 + i + let lexState = if scanStartLine = 0 then 0L else sourceTextData.[scanStartLine - 1].Value.LexStateAtEndOfLine + let lineContents = textLine.Text.ToString(textLine.Span) + + // We can reuse the old data when + // 1. the line starts at the same overall position + // 2. the hash codes match + // 3. the start-of-line lex states are the same + match sourceTextData.[lineNumber] with + | Some data when data.IsValid(textLine) && data.LexStateAtStartOfLine = lexState -> + data, textLinePos, lineContents + | _ -> + // Otherwise, we recompute + let newData = scanSourceLine(sourceTokenizer, textLine, lineContents, lexState) + sourceTextData.[lineNumber] <- Some newData + newData, textLinePos, lineContents + +let tokenizeLine (documentKey, sourceText, position, fileName, defines) = + try + let lineData, _, _ = getCachedSourceLineData(documentKey, sourceText, position, fileName, defines) + lineData.Tokens + with + | ex -> + Assert.Exception(ex) + [] + +let getSymbolAtPosition(documentKey: DocumentId, sourceText: SourceText, position: int, fileName: string, defines: string list, lookupKind: SymbolLookupKind) : LexerSymbol option = + try + let lineData, textLinePos, lineContents = getCachedSourceLineData(documentKey, sourceText, position, fileName, defines) + getSymbolFromTokens(fileName, lineData.Tokens, textLinePos, lineContents, lookupKind) + with + | :? System.OperationCanceledException -> reraise() + | ex -> + Assert.Exception(ex) + None + +/// Fix invalid span if it appears to have redundant suffix and prefix. +let fixupSpan (sourceText: SourceText, span: TextSpan) : TextSpan = + let text = sourceText.GetSubText(span).ToString() + match text.LastIndexOf '.' with + | -1 | 0 -> span + | index -> TextSpan(span.Start + index + 1, text.Length - index - 1) + +let isValidNameForSymbol (lexerSymbolKind: LexerSymbolKind, symbol: FSharpSymbol, name: string) : bool = + let doubleBackTickDelimiter = "``" + + let isDoubleBacktickIdent (s: string) = + let doubledDelimiter = 2 * doubleBackTickDelimiter.Length + if s.StartsWith(doubleBackTickDelimiter) && s.EndsWith(doubleBackTickDelimiter) && s.Length > doubledDelimiter then + let inner = s.Substring(doubleBackTickDelimiter.Length, s.Length - doubledDelimiter) + not (inner.Contains(doubleBackTickDelimiter)) + else false + + let isIdentifier (ident: string) = + if isDoubleBacktickIdent ident then + true + else + ident + |> Seq.mapi (fun i c -> i, c) + |> Seq.forall (fun (i, c) -> + if i = 0 then PrettyNaming.IsIdentifierFirstCharacter c + else PrettyNaming.IsIdentifierPartCharacter c) + + let isFixableIdentifier (s: string) = + not (String.IsNullOrEmpty s) && Lexhelp.Keywords.NormalizeIdentifierBackticks s |> isIdentifier + + let forbiddenChars = [| '.'; '+'; '$'; '&'; '['; ']'; '/'; '\\'; '*'; '\'' |] + + let isTypeNameIdent (s: string) = + not (String.IsNullOrEmpty s) && s.IndexOfAny forbiddenChars = -1 && isFixableIdentifier s + + let isUnionCaseIdent (s: string) = + isTypeNameIdent s && Char.IsUpper(s.Replace(doubleBackTickDelimiter, "").[0]) + + let isTypeParameter (prefix: char) (s: string) = + s.Length >= 2 && s.[0] = prefix && isIdentifier s.[1..] + + let isGenericTypeParameter = isTypeParameter ''' + let isStaticallyResolvedTypeParameter = isTypeParameter '^' + + match lexerSymbolKind, symbol with + | _, :? FSharpUnionCase -> isUnionCaseIdent name + | _, :? FSharpActivePatternCase -> + // Different from union cases, active patterns don't accept double-backtick identifiers + isFixableIdentifier name && not (String.IsNullOrEmpty name) && Char.IsUpper(name.[0]) + | LexerSymbolKind.Operator, _ -> PrettyNaming.IsOperatorName name + | LexerSymbolKind.Punctuation, _ -> PrettyNaming.IsPunctuation name + | LexerSymbolKind.GenericTypeParameter, _ -> isGenericTypeParameter name + | LexerSymbolKind.StaticallyResolvedTypeParameter, _ -> isStaticallyResolvedTypeParameter name + | (LexerSymbolKind.Ident | LexerSymbolKind.Other), _ -> + match symbol with + | :? FSharpEntity as e when e.IsClass || e.IsFSharpRecord || e.IsFSharpUnion || e.IsValueType || e.IsFSharpModule || e.IsInterface -> isTypeNameIdent name + | _ -> isFixableIdentifier name diff --git a/vsintegration/src/FSharp.Editor/LanguageService/TypedAstUtils.fs b/vsintegration/src/FSharp.Editor/LanguageService/TypedAstUtils.fs new file mode 100644 index 0000000000000000000000000000000000000000..93bf5f3916d753271952426435fc9796d4c0d632 --- /dev/null +++ b/vsintegration/src/FSharp.Editor/LanguageService/TypedAstUtils.fs @@ -0,0 +1,212 @@ +module internal Microsoft.VisualStudio.FSharp.Editor.TypedAstUtils + +open System +open Microsoft.FSharp.Compiler.Ast +open Microsoft.FSharp.Compiler.SourceCodeServices +open Microsoft.VisualStudio.FSharp.Editor + + +open System.Text.RegularExpressions + +let isAttribute<'T> (attribute: FSharpAttribute) = + // CompiledName throws exception on DataContractAttribute generated by SQLProvider + match Option.attempt (fun _ -> attribute.AttributeType.CompiledName) with + | Some name when name = typeof<'T>.Name -> true + | _ -> false + +let tryGetAttribute<'T> (attributes: seq) = + attributes |> Seq.tryFind isAttribute<'T> + +let hasModuleSuffixAttribute (entity: FSharpEntity) = + entity.Attributes + |> tryGetAttribute + |> Option.bind (fun a -> + Option.attempt (fun _ -> a.ConstructorArguments) + |> Option.bind (fun args -> args |> Seq.tryPick (fun (_, arg) -> + let res = + match arg with + | :? int32 as arg when arg = int CompilationRepresentationFlags.ModuleSuffix -> + Some() + | :? CompilationRepresentationFlags as arg when arg = CompilationRepresentationFlags.ModuleSuffix -> + Some() + | _ -> + None + res))) + |> Option.isSome + +let isOperator (name: string) = + name.StartsWith "( " && name.EndsWith " )" && name.Length > 4 + && name.Substring (2, name.Length - 4) + |> String.forall (fun c -> c <> ' ' && not (Char.IsLetter c)) + +let private UnnamedUnionFieldRegex = Regex("^Item(\d+)?$", RegexOptions.Compiled) + +let isUnnamedUnionCaseField (field: FSharpField) = UnnamedUnionFieldRegex.IsMatch(field.Name) + + +module TypedAstPatterns = + + let (|AbbreviatedType|_|) (entity: FSharpEntity) = + if entity.IsFSharpAbbreviation then Some entity.AbbreviatedType + else None + + let (|TypeWithDefinition|_|) (ty: FSharpType) = + if ty.HasTypeDefinition then Some ty.TypeDefinition + else None + + let rec getEntityAbbreviatedType (entity: FSharpEntity) = + if entity.IsFSharpAbbreviation then + match entity.AbbreviatedType with + | TypeWithDefinition def -> getEntityAbbreviatedType def + | abbreviatedType -> entity, Some abbreviatedType + else entity, None + + let rec getAbbreviatedType (fsharpType: FSharpType) = + if fsharpType.IsAbbreviation then + getAbbreviatedType fsharpType.AbbreviatedType + else fsharpType + + let (|Attribute|_|) (entity: FSharpEntity) = + let isAttribute (entity: FSharpEntity) = + let getBaseType (entity: FSharpEntity) = + try + match entity.BaseType with + | Some (TypeWithDefinition def) -> Some def + | _ -> None + with _ -> None + + let rec isAttributeType (ty: FSharpEntity option) = + match ty with + | None -> false + | Some ty -> + match ty.TryGetFullName() with + | None -> false + | Some fullName -> + fullName = "System.Attribute" || isAttributeType (getBaseType ty) + isAttributeType (Some entity) + if isAttribute entity then Some() else None + + let (|ValueType|_|) (e: FSharpEntity) = + if e.IsEnum || e.IsValueType || hasAttribute e.Attributes then Some() + else None + + let (|Class|_|) (original: FSharpEntity, abbreviated: FSharpEntity, _) = + if abbreviated.IsClass + && (not abbreviated.IsStaticInstantiation || original.IsFSharpAbbreviation) then Some() + else None + + let (|Record|_|) (e: FSharpEntity) = if e.IsFSharpRecord then Some() else None + let (|UnionType|_|) (e: FSharpEntity) = if e.IsFSharpUnion then Some() else None + let (|Delegate|_|) (e: FSharpEntity) = if e.IsDelegate then Some() else None + let (|FSharpException|_|) (e: FSharpEntity) = if e.IsFSharpExceptionDeclaration then Some() else None + let (|Interface|_|) (e: FSharpEntity) = if e.IsInterface then Some() else None + let (|AbstractClass|_|) (e: FSharpEntity) = + if hasAttribute e.Attributes then Some() else None + + let (|FSharpType|_|) (e: FSharpEntity) = + if e.IsDelegate || e.IsFSharpExceptionDeclaration || e.IsFSharpRecord || e.IsFSharpUnion + || e.IsInterface || e.IsMeasure + || (e.IsFSharp && e.IsOpaque && not e.IsFSharpModule && not e.IsNamespace) then Some() + else None + + let (|ProvidedType|_|) (e: FSharpEntity) = + if (e.IsProvided || e.IsProvidedAndErased || e.IsProvidedAndGenerated) && e.CompiledName = e.DisplayName then + Some() + else None + + let (|ByRef|_|) (e: FSharpEntity) = if e.IsByRef then Some() else None + let (|Array|_|) (e: FSharpEntity) = if e.IsArrayType then Some() else None + let (|FSharpModule|_|) (entity: FSharpEntity) = if entity.IsFSharpModule then Some() else None + + let (|Namespace|_|) (entity: FSharpEntity) = if entity.IsNamespace then Some() else None + let (|ProvidedAndErasedType|_|) (entity: FSharpEntity) = if entity.IsProvidedAndErased then Some() else None + let (|Enum|_|) (entity: FSharpEntity) = if entity.IsEnum then Some() else None + + let (|Tuple|_|) (ty: FSharpType option) = + ty |> Option.bind (fun ty -> if ty.IsTupleType then Some() else None) + + let (|RefCell|_|) (ty: FSharpType) = + match getAbbreviatedType ty with + | TypeWithDefinition def when + def.IsFSharpRecord && def.FullName = "Microsoft.FSharp.Core.FSharpRef`1" -> Some() + | _ -> None + + let (|FunctionType|_|) (ty: FSharpType) = + if ty.IsFunctionType then Some() + else None + + let (|Pattern|_|) (symbol: FSharpSymbol) = + match symbol with + | :? FSharpUnionCase + | :? FSharpActivePatternCase -> Some() + | _ -> None + + /// Field (field, fieldAbbreviatedType) + let (|Field|_|) (symbol: FSharpSymbol) = + match symbol with + | :? FSharpField as field -> Some (field, getAbbreviatedType field.FieldType) + | _ -> None + + let (|MutableVar|_|) (symbol: FSharpSymbol) = + let isMutable = + match symbol with + | :? FSharpField as field -> field.IsMutable && not field.IsLiteral + | :? FSharpMemberOrFunctionOrValue as func -> func.IsMutable + | _ -> false + if isMutable then Some() else None + + /// Entity (originalEntity, abbreviatedEntity, abbreviatedType) + let (|FSharpEntity|_|) (symbol: FSharpSymbol) = + match symbol with + | :? FSharpEntity as entity -> + let abbreviatedEntity, abbreviatedType = getEntityAbbreviatedType entity + Some (entity, abbreviatedEntity, abbreviatedType) + | _ -> None + + let (|Parameter|_|) (symbol: FSharpSymbol) = + match symbol with + | :? FSharpParameter -> Some() + | _ -> None + + let (|UnionCase|_|) (e: FSharpSymbol) = + match e with + | :? FSharpUnionCase as uc -> Some uc + | _ -> None + + let (|RecordField|_|) (e: FSharpSymbol) = + match e with + | :? FSharpField as field -> + if field.DeclaringEntity.IsFSharpRecord then Some field else None + | _ -> None + + let (|ActivePatternCase|_|) (symbol: FSharpSymbol) = + match symbol with + | :? FSharpActivePatternCase as case -> Some case + | _ -> None + + /// Func (memberFunctionOrValue, fullType) + let (|MemberFunctionOrValue|_|) (symbol: FSharpSymbol) = + match symbol with + | :? FSharpMemberOrFunctionOrValue as func -> Some func + | _ -> None + + /// Constructor (enclosingEntity) + let (|Constructor|_|) (func: FSharpMemberOrFunctionOrValue) = + match func.CompiledName with + | ".ctor" | ".cctor" -> Some func.EnclosingEntity + | _ -> None + + let (|Function|_|) excluded (func: FSharpMemberOrFunctionOrValue) = + match func.FullTypeSafe |> Option.map getAbbreviatedType with + | Some typ when typ.IsFunctionType + && not func.IsPropertyGetterMethod + && not func.IsPropertySetterMethod + && not excluded + && not (isOperator func.DisplayName) -> Some() + | _ -> None + + let (|ExtensionMember|_|) (func: FSharpMemberOrFunctionOrValue) = + if func.IsExtensionMember then Some() else None + + let (|Event|_|) (func: FSharpMemberOrFunctionOrValue) = + if func.IsEvent then Some () else None \ No newline at end of file diff --git a/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs b/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs index 26296c3e4a76c748db3b72186b11c5f1f40e3a13..d1f05b859de56155da840f2cda65284eb7e5fd7e 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs @@ -15,7 +15,7 @@ open Microsoft.CodeAnalysis.FindUsages open Microsoft.FSharp.Compiler.Range open Microsoft.FSharp.Compiler.SourceCodeServices -[, FSharpCommonConstants.FSharpLanguageName); Shared>] +[, FSharpConstants.FSharpLanguageName); Shared>] type internal FSharpFindUsagesService [] ( @@ -34,9 +34,9 @@ type internal FSharpFindUsagesService async { let doc = solution.GetDocument(documentId) let! sourceText = doc.GetTextAsync(cancellationToken) - match CommonRoslynHelpers.TryFSharpRangeToTextSpan(sourceText, range) with + match RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, range) with | Some span -> - let span = CommonHelpers.fixupSpan(sourceText, span) + let span = Tokenizer.fixupSpan(sourceText, span) return Some (DocumentSpan(doc, span)) | None -> return None }) @@ -54,10 +54,10 @@ type internal FSharpFindUsagesService let lineNumber = sourceText.Lines.GetLinePosition(position).Line + 1 let defines = CompilerEnvironment.GetCompilationDefinesForEditing(document.FilePath, options.OtherOptions |> Seq.toList) - let! symbol = CommonHelpers.getSymbolAtPosition(document.Id, sourceText, position, document.FilePath, defines, SymbolLookupKind.Greedy) + let! symbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, position, document.FilePath, defines, Tokenizer.SymbolLookupKind.Greedy) let! symbolUse = checkFileResults.GetSymbolUseAtLocation(lineNumber, symbol.Ident.idRange.EndColumn, textLine, symbol.FullIsland) let! declaration = checkFileResults.GetDeclarationLocationAlternate (lineNumber, symbol.Ident.idRange.EndColumn, textLine, symbol.FullIsland, false) |> liftAsync - let tags = GlyphTags.GetTags(CommonRoslynHelpers.GetGlyphForSymbol (symbolUse.Symbol, symbol.Kind)) + let tags = GlyphTags.GetTags(Tokenizer.GetGlyphForSymbol (symbolUse.Symbol, symbol.Kind)) let declarationRange = match declaration with @@ -142,8 +142,8 @@ type internal FSharpFindUsagesService interface IFindUsagesService with member __.FindReferencesAsync(document, position, context) = findReferencedSymbolsAsync(document, position, context, true) - |> CommonRoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) + |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) member __.FindImplementationsAsync(document, position, context) = findReferencedSymbolsAsync(document, position, context, false) - |> CommonRoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) + |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) \ No newline at end of file diff --git a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinitionService.fs b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinitionService.fs index 714e3a5481da6ca454e88e981299273fa8dd65e1..9eac1cf2bc4441f6b899331ea90fe080b8df24af 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinitionService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinitionService.fs @@ -45,7 +45,7 @@ module internal FSharpGoToDefinition = let textLine = sourceText.Lines.GetLineFromPosition position let textLinePos = sourceText.Lines.GetLinePosition position let fcsTextLineNumber = Line.fromZ textLinePos.Line - let! lexerSymbol = CommonHelpers.getSymbolAtPosition(documentKey, sourceText, position, filePath, defines, SymbolLookupKind.Greedy) + let! lexerSymbol = Tokenizer.getSymbolAtPosition(documentKey, sourceText, position, filePath, defines, Tokenizer.SymbolLookupKind.Greedy) let! _, _, checkFileResults = checker.ParseAndCheckDocument (filePath, textVersionHash, sourceText.ToString(), options, allowStaleResults = preferSignature) @@ -71,7 +71,7 @@ module internal FSharpGoToDefinition = let refDocumentId = refDocumentIds.First() let refDocument = document.Project.Solution.GetDocument refDocumentId let! refSourceText = refDocument.GetTextAsync() - let refTextSpan = CommonRoslynHelpers.FSharpRangeToTextSpan (refSourceText, range) + let refTextSpan = RoslynHelpers.FSharpRangeToTextSpan (refSourceText, range) return Some (FSharpNavigableItem (refDocument, refTextSpan)) else return None } @@ -83,12 +83,12 @@ module internal FSharpGoToDefinition = let! projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject originDocument let defines = CompilerEnvironment.GetCompilationDefinesForEditing (originDocument.FilePath, projectOptions.OtherOptions |> Seq.toList) - let originTextSpan = CommonRoslynHelpers.FSharpRangeToTextSpan (sourceText, originRange) + let originTextSpan = RoslynHelpers.FSharpRangeToTextSpan (sourceText, originRange) let position = originTextSpan.Start let! lexerSymbol = - CommonHelpers.getSymbolAtPosition - (originDocument.Id, sourceText, position, originDocument.FilePath, defines, SymbolLookupKind.Greedy) + Tokenizer.getSymbolAtPosition + (originDocument.Id, sourceText, position, originDocument.FilePath, defines, Tokenizer.SymbolLookupKind.Greedy) let textLinePos = sourceText.Lines.GetLinePosition position let fcsTextLineNumber = Line.fromZ textLinePos.Line @@ -115,7 +115,7 @@ module internal FSharpGoToDefinition = let! symbolUses = checkFileResults.GetUsesOfSymbolInFile symbol |> liftAsync let! implSymbol = symbolUses |> Array.tryHead - let implTextSpan = CommonRoslynHelpers.FSharpRangeToTextSpan (implSourceText, implSymbol.RangeAlternate) + let implTextSpan = RoslynHelpers.FSharpRangeToTextSpan (implSourceText, implSymbol.RangeAlternate) return FSharpNavigableItem (implDoc, implTextSpan) else let! targetDocument = originDocument.Project.Solution.TryGetDocumentFromFSharpRange fsSymbolUse.RangeAlternate @@ -152,7 +152,7 @@ module internal FSharpGoToDefinition = open FSharpGoToDefinition [] -[, FSharpCommonConstants.FSharpLanguageName)>] +[, FSharpConstants.FSharpLanguageName)>] [)>] type internal FSharpGoToDefinitionService [] (checkerProvider: FSharpCheckerProvider, @@ -253,7 +253,7 @@ type internal FSharpGoToDefinitionService [] let textLine = sourceText.Lines.GetLineFromPosition position let textLinePos = sourceText.Lines.GetLinePosition position let fcsTextLineNumber = Line.fromZ textLinePos.Line - let! lexerSymbol = CommonHelpers.getSymbolAtPosition(documentKey, sourceText, position, filePath, defines, SymbolLookupKind.Greedy) + let! lexerSymbol = Tokenizer.getSymbolAtPosition(documentKey, sourceText, position, filePath, defines, Tokenizer.SymbolLookupKind.Greedy) let! _, _, checkFileResults = checker.ParseAndCheckDocument (filePath, textVersionHash, sourceText.ToString(), options, allowStaleResults = true) |> Async.RunSynchronously @@ -287,8 +287,8 @@ type internal FSharpGoToDefinitionService [] checkerProvider.Checker.ParseAndCheckDocument (originDocument, projectOptions, allowStaleResults=true, sourceText=sourceText) let! lexerSymbol = - CommonHelpers.getSymbolAtPosition - (originDocument.Id, sourceText, position,originDocument.FilePath, defines, SymbolLookupKind.Greedy) + Tokenizer.getSymbolAtPosition + (originDocument.Id, sourceText, position,originDocument.FilePath, defines, Tokenizer.SymbolLookupKind.Greedy) let idRange = lexerSymbol.Ident.idRange let! declarations = @@ -314,7 +314,7 @@ type internal FSharpGoToDefinitionService [] findSymbolDeclarationInFile (targetSymbolUse, implFilePath, implSourceText.ToString(), checkerProvider.Checker, projectOptions, implVersion.GetHashCode()) - let implTextSpan = CommonRoslynHelpers.FSharpRangeToTextSpan (implSourceText, targetRange) + let implTextSpan = RoslynHelpers.FSharpRangeToTextSpan (implSourceText, targetRange) let navItem = FSharpNavigableItem (implDocument, implTextSpan) results.Add navItem return results.AsEnumerable() @@ -327,7 +327,7 @@ type internal FSharpGoToDefinitionService [] | FSharpFindDeclResult.DeclFound targetRange -> let! sigDocument = originDocument.Project.Solution.TryGetDocumentFromPath targetRange.FileName let! sigSourceText = sigDocument.GetTextAsync () |> liftTaskAsync - let sigTextSpan = CommonRoslynHelpers.FSharpRangeToTextSpan (sigSourceText, targetRange) + let sigTextSpan = RoslynHelpers.FSharpRangeToTextSpan (sigSourceText, targetRange) let navItem = FSharpNavigableItem (sigDocument, sigTextSpan) results.Add navItem return results.AsEnumerable() @@ -338,7 +338,7 @@ type internal FSharpGoToDefinitionService [] else let! sigDocument = originDocument.Project.Solution.TryGetDocumentFromPath targetRange.FileName let! sigSourceText = sigDocument.GetTextAsync () |> liftTaskAsync - let sigTextSpan = CommonRoslynHelpers.FSharpRangeToTextSpan (sigSourceText, targetRange) + let sigTextSpan = RoslynHelpers.FSharpRangeToTextSpan (sigSourceText, targetRange) // if the gotodef call originated from a signature and the returned target is a signature, navigate there if isSignatureFile targetRange.FileName && preferSignature then let navItem = FSharpNavigableItem (sigDocument, sigTextSpan) @@ -354,13 +354,13 @@ type internal FSharpGoToDefinitionService [] let! targetRange = findSymbolDeclarationInFile (targetSymbolUse, implFilePath, implSourceText.ToString(), checkerProvider.Checker, projectOptions, implVersion.GetHashCode()) - let implTextSpan = CommonRoslynHelpers.FSharpRangeToTextSpan (implSourceText, targetRange) + let implTextSpan = RoslynHelpers.FSharpRangeToTextSpan (implSourceText, targetRange) let navItem = FSharpNavigableItem (implDocument, implTextSpan) results.Add navItem return results.AsEnumerable() | _ -> return! None } |> Async.map (Option.defaultValue Seq.empty) - |> CommonRoslynHelpers.StartAsyncAsTask cancellationToken + |> RoslynHelpers.StartAsyncAsTask cancellationToken interface IGoToDefinitionService with diff --git a/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs b/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs index b267fb42f50cf8ccf1322fe9393a887c4445ddac..8a56cc56e1f7ab20a0417c4ff0eab5161435bd59 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs @@ -177,7 +177,7 @@ module private Utils = | _ -> container.Name typeAsString + name -[, FSharpCommonConstants.FSharpLanguageName); Shared>] +[, FSharpConstants.FSharpLanguageName); Shared>] type internal FSharpNavigateToSearchService [] ( @@ -195,7 +195,7 @@ type internal FSharpNavigateToSearchService match parseResults.ParseTree |> Option.map NavigateTo.getNavigableItems with | Some items -> [| for item in items do - let sourceSpan = CommonRoslynHelpers.FSharpRangeToTextSpan(sourceText, item.Range) + let sourceSpan = RoslynHelpers.FSharpRangeToTextSpan(sourceText, item.Range) let glyph = Utils.navigateToItemKindToGlyph item.Kind let kind = Utils.navigateToItemKindToRoslynKind item.Kind let additionalInfo = Utils.containerToString item.Container document.Project @@ -257,7 +257,7 @@ type internal FSharpNavigateToSearchService } |> Async.map (Option.defaultValue [||]) |> Async.map Seq.toImmutableArray - |> CommonRoslynHelpers.StartAsyncAsTask(cancellationToken) + |> RoslynHelpers.StartAsyncAsTask(cancellationToken) member __.SearchDocumentAsync(document, searchPattern, cancellationToken) : Task> = asyncMaybe { @@ -267,4 +267,4 @@ type internal FSharpNavigateToSearchService } |> Async.map (Option.defaultValue [||]) |> Async.map Seq.toImmutableArray - |> CommonRoslynHelpers.StartAsyncAsTask(cancellationToken) \ No newline at end of file + |> RoslynHelpers.StartAsyncAsTask(cancellationToken) \ No newline at end of file diff --git a/vsintegration/src/FSharp.Editor/Navigation/NavigationBarItemService.fs b/vsintegration/src/FSharp.Editor/Navigation/NavigationBarItemService.fs index 478b6d9e21230537e956290db5a5d8d6d0d7e7eb..d5281a81684ab11d6e7b4eff200b9e00d84bc251 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/NavigationBarItemService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/NavigationBarItemService.fs @@ -18,7 +18,7 @@ open Microsoft.FSharp.Compiler.SourceCodeServices type internal NavigationBarSymbolItem(text, glyph, spans, childItems) = inherit NavigationBarItem(text, glyph, spans, childItems) -[, FSharpCommonConstants.FSharpLanguageName); Shared>] +[, FSharpConstants.FSharpLanguageName); Shared>] type internal FSharpNavigationBarItemService [] ( @@ -35,7 +35,7 @@ type internal FSharpNavigationBarItemService let! sourceText = document.GetTextAsync(cancellationToken) let! parsedInput = checkerProvider.Checker.ParseDocument(document, options, sourceText) let navItems = NavigationImpl.getNavigation parsedInput - let rangeToTextSpan range = CommonRoslynHelpers.TryFSharpRangeToTextSpan(sourceText, range) + let rangeToTextSpan range = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, range) return navItems.Declarations |> Array.choose (fun topLevelDecl -> @@ -52,7 +52,7 @@ type internal FSharpNavigationBarItemService :> NavigationBarItem)) :> IList<_> } |> Async.map (Option.defaultValue emptyResult) - |> CommonRoslynHelpers.StartAsyncAsTask(cancellationToken) + |> RoslynHelpers.StartAsyncAsTask(cancellationToken) member __.ShowItemGrayedIfNear (_item) : bool = false diff --git a/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs b/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs index 82ea00e5ab4948d26aea266eb1985a49465cf168..38ea132499e958eb562d40e3c42489df1b41e48e 100644 --- a/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs +++ b/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs @@ -19,6 +19,7 @@ open Microsoft.CodeAnalysis.Editor.Shared.Extensions open Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.QuickInfo open Microsoft.CodeAnalysis.Text + open Microsoft.VisualStudio.FSharp.LanguageService open Microsoft.VisualStudio.Shell open Microsoft.VisualStudio.Shell.Interop @@ -30,8 +31,9 @@ open Microsoft.FSharp.Compiler.Range open Microsoft.FSharp.Compiler open Internal.Utilities.StructuredFormat -open CommonRoslynHelpers +open Tokenizer open System.Text +open RoslynHelpers module private SessionHandling = let mutable currentSession = None @@ -39,7 +41,7 @@ module private SessionHandling = [)>] [] [] - [] + [] type SourceProviderForCapturingSession () = interface IQuickInfoSourceProvider with member x.TryCreateQuickInfoSource _ = @@ -99,7 +101,7 @@ module private FSharpQuickInfo = let extDocument = solution.GetProject(extDocId.ProjectId).GetDocument extDocId let! extSourceText = extDocument.GetTextAsync cancellationToken - let extSpan = CommonRoslynHelpers.FSharpRangeToTextSpan (extSourceText, declRange) + let extSpan = RoslynHelpers.FSharpRangeToTextSpan (extSourceText, declRange) let extLineText = (extSourceText.Lines.GetLineFromPosition extSpan.Start).ToString() // project options need to be retrieved because the signature file could be in another project @@ -109,7 +111,7 @@ module private FSharpQuickInfo = (extDocument.FilePath, extProjectOptions.OtherOptions |> Seq.toList) let! extLexerSymbol = - CommonHelpers.getSymbolAtPosition + Tokenizer.getSymbolAtPosition (extDocId, extSourceText, extSpan.Start, declRange.FileName, extDefines, SymbolLookupKind.Greedy) let! _, _, extCheckFileResults = @@ -127,14 +129,14 @@ module private FSharpQuickInfo = let! extSymbolUse = extCheckFileResults.GetSymbolUseAtLocation(declRange.StartLine, extLexerSymbol.Ident.idRange.EndColumn, extLineText, extLexerSymbol.FullIsland) - let extTextSpan = CommonRoslynHelpers.FSharpRangeToTextSpan (extSourceText, extLexerSymbol.Range) + let extTextSpan = RoslynHelpers.FSharpRangeToTextSpan (extSourceText, extLexerSymbol.Range) return! Some (extTooltipText, extTextSpan, extSymbolUse, extLexerSymbol.Kind) } let! sourceText = document.GetTextAsync cancellationToken let! projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject document let defines = CompilerEnvironment.GetCompilationDefinesForEditing(document.FilePath, projectOptions.OtherOptions |> Seq.toList) - let! lexerSymbol = CommonHelpers.getSymbolAtPosition(document.Id, sourceText, position, document.FilePath, defines, SymbolLookupKind.Greedy) + let! lexerSymbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, position, document.FilePath, defines, SymbolLookupKind.Greedy) let idRange = lexerSymbol.Ident.idRange let! _, _, checkFileResults = checker.ParseAndCheckDocument(document, projectOptions, allowStaleResults = true, sourceText=sourceText) let textLinePos = sourceText.Lines.GetLinePosition position @@ -153,7 +155,7 @@ module private FSharpQuickInfo = | FSharpToolTipText [FSharpStructuredToolTipElement.None] -> return! None | _ -> let! symbolUse = checkFileResults.GetSymbolUseAtLocation (fcsTextLineNumber, idRange.EndColumn, lineText, lexerSymbol.FullIsland) - let targetTextSpan = CommonRoslynHelpers.FSharpRangeToTextSpan (sourceText, lexerSymbol.Range) + let targetTextSpan = RoslynHelpers.FSharpRangeToTextSpan (sourceText, lexerSymbol.Range) return! Some (targetTooltip, targetTextSpan, symbolUse, lexerSymbol.Kind) } @@ -194,7 +196,7 @@ module private FSharpQuickInfo = return (None, Some backupTooltipInfo) } -[] +[] type internal FSharpQuickInfoProvider [] ( @@ -219,7 +221,7 @@ type internal FSharpQuickInfoProvider let targetPath = range.FileName let! targetDoc = solution.TryGetDocumentFromFSharpRange (range,initialDoc.Project.Id) let! targetSource = targetDoc.GetTextAsync() - let! targetTextSpan = CommonRoslynHelpers.TryFSharpRangeToTextSpan (targetSource, range) + let! targetTextSpan = RoslynHelpers.TryFSharpRangeToTextSpan (targetSource, range) // 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 // - signature files (.fsi) should navigate to other signature files @@ -284,14 +286,14 @@ type internal FSharpQuickInfoProvider let textLine = sourceText.Lines.GetLineFromPosition position let textLineNumber = textLine.LineNumber + 1 // Roslyn line numbers are zero-based let defines = CompilerEnvironment.GetCompilationDefinesForEditing (filePath, options.OtherOptions |> Seq.toList) - let! symbol = CommonHelpers.getSymbolAtPosition (documentId, sourceText, position, filePath, defines, SymbolLookupKind.Precise) + let! symbol = Tokenizer.getSymbolAtPosition (documentId, sourceText, position, filePath, defines, SymbolLookupKind.Precise) let! res = checkFileResults.GetStructuredToolTipTextAlternate (textLineNumber, symbol.Ident.idRange.EndColumn, textLine.ToString(), symbol.FullIsland, FSharpTokenTag.IDENT) |> liftAsync match res with | FSharpToolTipText [] | FSharpToolTipText [FSharpStructuredToolTipElement.None] -> return! None | _ -> let! symbolUse = checkFileResults.GetSymbolUseAtLocation (textLineNumber, symbol.Ident.idRange.EndColumn, textLine.ToString(), symbol.FullIsland) - return! Some (res, CommonRoslynHelpers.FSharpRangeToTextSpan (sourceText, symbol.Range), symbolUse.Symbol, symbol.Kind) + return! Some (res, RoslynHelpers.FSharpRangeToTextSpan (sourceText, symbol.Range), symbolUse.Symbol, symbol.Kind) } interface IQuickInfoProvider with @@ -309,7 +311,7 @@ type internal FSharpQuickInfoProvider XmlDocumentation.BuildDataTipText(documentationBuilder, mainDescription.Add, documentation.Add, toolTipElement) let content = FSharpQuickInfo.tooltip - (SymbolGlyphDeferredContent(CommonRoslynHelpers.GetGlyphForSymbol(symbolUse.Symbol, symbolKind), glyphService), + (SymbolGlyphDeferredContent(GetGlyphForSymbol(symbolUse.Symbol, symbolKind), glyphService), fragment (mainDescription, typeMap, document, symbolUse.RangeAlternate), fragment (documentation, typeMap, document, symbolUse.RangeAlternate)) return QuickInfoItem (textSpan, content) @@ -352,10 +354,10 @@ type internal FSharpQuickInfoProvider let content = FSharpQuickInfo.tooltip - (SymbolGlyphDeferredContent (CommonRoslynHelpers.GetGlyphForSymbol (targetSymbolUse.Symbol, targetSymbolKind), glyphService), + (SymbolGlyphDeferredContent (GetGlyphForSymbol (targetSymbolUse.Symbol, targetSymbolKind), glyphService), fragment (description, typeMap, document, targetSymbolUse.RangeAlternate), fragment (documentation, typeMap, document, targetSymbolUse.RangeAlternate)) return QuickInfoItem (targetTextSpan, content) } |> Async.map Option.toObj - |> CommonRoslynHelpers.StartAsyncAsTask cancellationToken + |> RoslynHelpers.StartAsyncAsTask cancellationToken diff --git a/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs b/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs index 7f595776ac855c3b4b4717a099235829d2fd3abc..4598fc81be476287bdb181c448458ca120cfd718 100644 --- a/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs +++ b/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs @@ -73,9 +73,9 @@ module internal BlockStructure = |> Seq.distinctBy (fun x -> x.Range.StartLine) |> Seq.choose (fun scopeRange -> // the range of text to collapse - let textSpan = CommonRoslynHelpers.TryFSharpRangeToTextSpan(sourceText, scopeRange.CollapseRange) + let textSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, scopeRange.CollapseRange) // the range of the entire expression - let hintSpan = CommonRoslynHelpers.TryFSharpRangeToTextSpan(sourceText, scopeRange.Range) + let hintSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, scopeRange.Range) match textSpan,hintSpan with | Some textSpan, Some hintSpan -> let line = sourceText.Lines.GetLineFromPosition textSpan.Start @@ -93,7 +93,7 @@ open BlockStructure type internal FSharpBlockStructureService(checker: FSharpChecker, projectInfoManager: ProjectInfoManager) = inherit BlockStructureService() - override __.Language = FSharpCommonConstants.FSharpLanguageName + override __.Language = FSharpConstants.FSharpLanguageName override __.GetBlockStructureAsync(document, cancellationToken) : Task = asyncMaybe { @@ -104,9 +104,9 @@ type internal FSharpBlockStructureService(checker: FSharpChecker, projectInfoMan } |> Async.map (Option.defaultValue ImmutableArray<_>.Empty) |> Async.map BlockStructure - |> CommonRoslynHelpers.StartAsyncAsTask(cancellationToken) + |> RoslynHelpers.StartAsyncAsTask(cancellationToken) -[, FSharpCommonConstants.FSharpLanguageName); Shared>] +[, FSharpConstants.FSharpLanguageName); Shared>] type internal FSharpBlockStructureServiceFactory [](checkerProvider: FSharpCheckerProvider, projectInfoManager: ProjectInfoManager) = interface ILanguageServiceFactory with member __.CreateLanguageService(_languageServices) = diff --git a/vsintegration/src/FSharp.ProjectSystem.FSharp/Project.fs b/vsintegration/src/FSharp.ProjectSystem.FSharp/Project.fs index caaff45807bf5af5390b8be129c843ecf10c8bc8..8bf634c189ffb8bb1552db84acb54ba61942154c 100644 --- a/vsintegration/src/FSharp.ProjectSystem.FSharp/Project.fs +++ b/vsintegration/src/FSharp.ProjectSystem.FSharp/Project.fs @@ -731,7 +731,7 @@ namespace rec Microsoft.VisualStudio.FSharp.ProjectSystem override x.GetGuidProperty(propid:int, guid:byref ) = if (enum propid = __VSHPROPID.VSHPROPID_PreferredLanguageSID) then - guid <- new Guid(FSharpCommonConstants.languageServiceGuidString) + guid <- new Guid(FSharpConstants.languageServiceGuidString) VSConstants.S_OK // below is how VS decide 'which templates' to associate with an 'add new item' call in this project elif (enum propid = __VSHPROPID2.VSHPROPID_AddItemTemplatesGuid) then @@ -1611,7 +1611,7 @@ namespace rec Microsoft.VisualStudio.FSharp.ProjectSystem // in the registry hive so that more editors can be added without changing this part of the // code. FSharp only makes usage of one Editor Factory and therefore we will return // that guid - guidEditorType <- new Guid(FSharpCommonConstants.editorFactoryGuidString) + guidEditorType <- new Guid(FSharpConstants.editorFactoryGuidString) VSConstants.S_OK interface IVsProjectSpecificEditorMap2 with @@ -1625,7 +1625,7 @@ namespace rec Microsoft.VisualStudio.FSharp.ProjectSystem // in the registry hive so that more editors can be added without changing this part of the // code. FSharp only makes usage of one Editor Factory and therefore we will return // that guid - guidEditorType <- new Guid(FSharpCommonConstants.editorFactoryGuidString) + guidEditorType <- new Guid(FSharpConstants.editorFactoryGuidString) VSConstants.S_OK member x.GetSpecificLanguageService(_mkDocument:string, guidLanguageService:byref ) = diff --git a/vsintegration/tests/Salsa/FSharpLanguageServiceTestable.fs b/vsintegration/tests/Salsa/FSharpLanguageServiceTestable.fs index 652ab7bcd00d6c48bb6d3aa1b0be22a09d475b3b..4903c16aa743404aa0dbcee54c0ece7c4da559a2 100644 --- a/vsintegration/tests/Salsa/FSharpLanguageServiceTestable.fs +++ b/vsintegration/tests/Salsa/FSharpLanguageServiceTestable.fs @@ -147,9 +147,9 @@ type internal FSharpLanguageServiceTestable() as this = match hier with | :? IProvideProjectSite as siteProvider -> let site = siteProvider.GetProjectSite() - site.AdviseProjectSiteChanges(FSharpCommonConstants.FSharpLanguageServiceCallbackName, + site.AdviseProjectSiteChanges(FSharpConstants.FSharpLanguageServiceCallbackName, new AdviseProjectSiteChanges(fun () -> this.OnProjectSettingsChanged(site))) - site.AdviseProjectSiteCleaned(FSharpCommonConstants.FSharpLanguageServiceCallbackName, + site.AdviseProjectSiteCleaned(FSharpConstants.FSharpLanguageServiceCallbackName, new AdviseProjectSiteChanges(fun () -> this.OnProjectCleaned(site))) | _ -> // This can happen when the file is in a solution folder or in, say, a C# project. diff --git a/vsintegration/tests/unittests/BraceMatchingServiceTests.fs b/vsintegration/tests/unittests/BraceMatchingServiceTests.fs index 3f81b1e444476401948ce2fa30bbc3585c3be397..818c0c6ce1ad96f07378352b178e5c250e9b7ca5 100644 --- a/vsintegration/tests/unittests/BraceMatchingServiceTests.fs +++ b/vsintegration/tests/unittests/BraceMatchingServiceTests.fs @@ -50,7 +50,7 @@ type BraceMatchingServiceTests() = | None -> Assert.Fail("Didn't find a match for start brace at position '{0}", startMarkerPosition) | Some(left, right) -> let endPositionInRange(range) = - let span = CommonRoslynHelpers.FSharpRangeToTextSpan(sourceText, range) + let span = RoslynHelpers.FSharpRangeToTextSpan(sourceText, range) span.Start <= endMarkerPosition && endMarkerPosition <= span.End Assert.IsTrue(endPositionInRange(left) || endPositionInRange(right), "Found end match at incorrect position") diff --git a/vsintegration/tests/unittests/BreakpointResolutionService.fs b/vsintegration/tests/unittests/BreakpointResolutionService.fs index 9641ad3743d3b09359382d7780a46b305dc128ad..51a5c300d413b21d945e7729c4bf7f59f8302ad2 100644 --- a/vsintegration/tests/unittests/BreakpointResolutionService.fs +++ b/vsintegration/tests/unittests/BreakpointResolutionService.fs @@ -76,7 +76,7 @@ let main argv = match actualResolutionOption with | None -> Assert.IsTrue(expectedResolution.IsNone, "BreakpointResolutionService failed to resolve breakpoint position") | Some(actualResolutionRange) -> - let actualResolution = sourceText.GetSubText(CommonRoslynHelpers.FSharpRangeToTextSpan(sourceText, actualResolutionRange)).ToString() + let actualResolution = sourceText.GetSubText(RoslynHelpers.FSharpRangeToTextSpan(sourceText, actualResolutionRange)).ToString() Assert.IsTrue(expectedResolution.IsSome, "BreakpointResolutionService resolved a breakpoint while it shouldn't at: {0}", actualResolution) Assert.AreEqual(expectedResolution.Value, actualResolution, "Expected and actual resolutions should match") \ No newline at end of file diff --git a/vsintegration/tests/unittests/ColorizationServiceTests.fs b/vsintegration/tests/unittests/ColorizationServiceTests.fs index 304247b20c69730f807f03de71fdf4dc78005d14..2a016069b06390630268faea7b058237de38d624 100644 --- a/vsintegration/tests/unittests/ColorizationServiceTests.fs +++ b/vsintegration/tests/unittests/ColorizationServiceTests.fs @@ -10,6 +10,7 @@ open Microsoft.CodeAnalysis.Classification open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.Text open Microsoft.VisualStudio.FSharp.Editor +open RoslynHelpers [][] type ColorizationServiceTests() = @@ -18,7 +19,7 @@ type ColorizationServiceTests() = let textSpan = TextSpan(0, fileContents.Length) let fileName = if isScriptFile.IsSome && isScriptFile.Value then "test.fsx" else "test.fs" let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) - let tokens = CommonHelpers.getColorizationData(documentId, SourceText.From(fileContents), textSpan, Some(fileName), defines, CancellationToken.None) + let tokens = Tokenizer.getColorizationData(documentId, SourceText.From(fileContents), textSpan, Some(fileName), defines, CancellationToken.None) let markerPosition = fileContents.IndexOf(marker) Assert.IsTrue(markerPosition >= 0, "Cannot find marker '{0}' in file contents", marker) (tokens, markerPosition) diff --git a/vsintegration/tests/unittests/DocumentHighlightsServiceTests.fs b/vsintegration/tests/unittests/DocumentHighlightsServiceTests.fs index 87de83c842db8db06bee0a1d5db2159c150c5eea..8714652033d3e463512e5a1a7468d63b7c776c04 100644 --- a/vsintegration/tests/unittests/DocumentHighlightsServiceTests.fs +++ b/vsintegration/tests/unittests/DocumentHighlightsServiceTests.fs @@ -58,7 +58,7 @@ let private getSpans (sourceText: SourceText) (caretPosition: int) = let private span sourceText isDefinition (startLine, startCol) (endLine, endCol) = let range = Range.mkRange filePath (Range.mkPos startLine startCol) (Range.mkPos endLine endCol) { IsDefinition = isDefinition - TextSpan = CommonRoslynHelpers.FSharpRangeToTextSpan(sourceText, range) } + TextSpan = RoslynHelpers.FSharpRangeToTextSpan(sourceText, range) } [] let ShouldHighlightAllSimpleLocalSymbolReferences() = diff --git a/vsintegration/tests/unittests/HelpContextServiceTests.fs b/vsintegration/tests/unittests/HelpContextServiceTests.fs index fcc838fbbbdac173650bc460a630a9fb86ff2626..6b5e76aeac811f1a8de9896ff9a0505ea2050691 100644 --- a/vsintegration/tests/unittests/HelpContextServiceTests.fs +++ b/vsintegration/tests/unittests/HelpContextServiceTests.fs @@ -58,7 +58,7 @@ type HelpContextServiceTests() = let span = TextSpan(marker, 0) let textLine = sourceText.Lines.GetLineFromPosition(marker) let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) - let tokens = CommonHelpers.getColorizationData(documentId, sourceText, textLine.Span, Some "test.fs", [], CancellationToken.None) + let tokens = Tokenizer.getColorizationData(documentId, sourceText, textLine.Span, Some "test.fs", [], CancellationToken.None) yield FSharpHelpContextService.GetHelpTerm(FSharpChecker.Instance, sourceText, fileName, newOptions, span, tokens, version) |> Async.RunSynchronously diff --git a/vsintegration/tests/unittests/LanguageDebugInfoServiceTests.fs b/vsintegration/tests/unittests/LanguageDebugInfoServiceTests.fs index 3a5ad68ee8f1170df74f0cc38d56d390fba5718b..aabfc32fa99d17e8e66251766ac4aaf1fcc6969d 100644 --- a/vsintegration/tests/unittests/LanguageDebugInfoServiceTests.fs +++ b/vsintegration/tests/unittests/LanguageDebugInfoServiceTests.fs @@ -54,7 +54,7 @@ let main argv = let sourceText = SourceText.From(code) let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) - let tokens = CommonHelpers.getColorizationData(documentId, sourceText, TextSpan.FromBounds(0, sourceText.Length), Some(fileName), defines, CancellationToken.None) + let tokens = Tokenizer.getColorizationData(documentId, sourceText, TextSpan.FromBounds(0, sourceText.Length), Some(fileName), defines, CancellationToken.None) let actualDataTipSpanOption = FSharpLanguageDebugInfoService.GetDataTipInformation(sourceText, searchPosition, tokens) match actualDataTipSpanOption with