diff --git a/vsintegration/packages.config b/vsintegration/packages.config index 5b5d870661f..17f244f2504 100644 --- a/vsintegration/packages.config +++ b/vsintegration/packages.config @@ -18,6 +18,7 @@ + diff --git a/vsintegration/src/FSharp.Editor/Common/CommonHelpers.fs b/vsintegration/src/FSharp.Editor/Common/CommonHelpers.fs index 4bed68c1602..0ccc490d05f 100644 --- a/vsintegration/src/FSharp.Editor/Common/CommonHelpers.fs +++ b/vsintegration/src/FSharp.Editor/Common/CommonHelpers.fs @@ -372,3 +372,92 @@ module internal Extensions = let! result = input return f result } + +[] +module internal TypedAstExtensionHelpers = + type FSharpEntity with + member this.TryGetFullName() = + match (try this.TryFullName with _ -> None) with + | Some x -> Some x + | None -> + try Some (String.Join(".", this.AccessPath, this.DisplayName)) + with _ -> None + + type FSharpMemberOrFunctionOrValue with + // FullType may raise exceptions (see https://github.com/fsharp/fsharp/issues/307). + member x.FullTypeSafe = try Some x.FullType with _ -> None + + let (|Public|Internal|Protected|Private|) (a: FSharpAccessibility) = + if a.IsPublic then Public + elif a.IsInternal then Internal + elif a.IsPrivate then Private + else Protected + +module internal Glyph = + let forSymbol (symbol: FSharpSymbol) = + match symbol with + | :? FSharpUnionCase as x -> + match x.Accessibility with + | Public -> Glyph.EnumPublic + | Internal -> Glyph.EnumInternal + | Protected -> Glyph.EnumProtected + | Private -> Glyph.EnumPrivate + | :? FSharpActivePatternCase -> Glyph.EnumPublic + | :? FSharpField as x -> + match x.Accessibility with + | Public -> Glyph.FieldPublic + | Internal -> Glyph.FieldInternal + | Protected -> Glyph.FieldProtected + | Private -> Glyph.FieldPrivate + | :? FSharpParameter -> Glyph.Parameter + | :? FSharpMemberOrFunctionOrValue as x -> + if x.IsExtensionMember then + match x.Accessibility with + | Public -> Glyph.ExtensionMethodPublic + | Internal -> Glyph.ExtensionMethodInternal + | Protected -> Glyph.ExtensionMethodProtected + | Private -> Glyph.ExtensionMethodPrivate + elif x.IsProperty || x.IsPropertyGetterMethod || x.IsPropertySetterMethod then + match x.Accessibility with + | Public -> Glyph.PropertyPublic + | Internal -> Glyph.PropertyInternal + | Protected -> Glyph.PropertyProtected + | Private -> Glyph.PropertyPrivate + elif x.IsEvent then + match x.Accessibility with + | Public -> Glyph.EventPublic + | Internal -> Glyph.EventInternal + | Protected -> Glyph.EventProtected + | Private -> Glyph.EventPrivate + else + match x.Accessibility with + | Public -> Glyph.MethodPublic + | Internal -> Glyph.MethodInternal + | Protected -> Glyph.MethodProtected + | Private -> Glyph.MethodPrivate + | :? FSharpEntity as x -> + if x.IsFSharpModule then + match x.Accessibility with + | Public -> Glyph.ModulePublic + | Internal -> Glyph.ModuleInternal + | Protected -> Glyph.ModuleProtected + | Private -> Glyph.ModulePrivate + elif x.IsEnum || x.IsFSharpUnion then + match x.Accessibility with + | Public -> Glyph.EnumPublic + | Internal -> Glyph.EnumInternal + | Protected -> Glyph.EnumProtected + | Private -> Glyph.EnumPrivate + elif x.IsInterface then + match x.Accessibility with + | Public -> Glyph.InterfacePublic + | Internal -> Glyph.InterfaceInternal + | Protected -> Glyph.InterfaceProtected + | Private -> Glyph.InterfacePrivate + else + match x.Accessibility with + | Public -> Glyph.ClassPublic + | Internal -> Glyph.ClassInternal + | Protected -> Glyph.ClassProtected + | Private -> Glyph.ClassPrivate + | _ -> Glyph.None diff --git a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj index e39dd58aa40..ec45b88d2e7 100644 --- a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj +++ b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj @@ -1,4 +1,4 @@ - + @@ -18,7 +18,8 @@ LIBRARY v4.6 $(NoWarn);75 - false $(OtherFlags) --warnon:1182 --subsystemversion:6.00 + false + $(OtherFlags) --warnon:1182 --subsystemversion:6.00 true false false @@ -85,6 +86,9 @@ + + + @@ -115,6 +119,9 @@ $(FSharpSourcesRoot)\..\packages\Microsoft.VisualStudio.CoreUtility.$(RoslynVSPackagesVersion)\lib\net45\Microsoft.VisualStudio.CoreUtility.dll + + $(FSharpSourcesRoot)\..\packages\Microsoft.VisualStudio.Imaging.$(RoslynVSPackagesVersion)\lib\net45\Microsoft.VisualStudio.Imaging.dll + $(FSharpSourcesRoot)\..\packages\Microsoft.VisualStudio.Utilities.$(RoslynVSPackagesVersion)\lib\net45\Microsoft.VisualStudio.Utilities.dll diff --git a/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs b/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs index 5abd076bd06..4bdd261d0d9 100644 --- a/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs +++ b/vsintegration/src/FSharp.Editor/QuickInfo/QuickInfoProvider.fs @@ -25,7 +25,9 @@ open Microsoft.CodeAnalysis.Formatting open Microsoft.CodeAnalysis.Host.Mef open Microsoft.CodeAnalysis.Options open Microsoft.CodeAnalysis.Text +open Microsoft.CodeAnalysis.Editor.Shared.Extensions +open Microsoft.VisualStudio.Language.Intellisense open Microsoft.VisualStudio.FSharp.LanguageService open Microsoft.VisualStudio.Text open Microsoft.VisualStudio.Text.Classification @@ -33,20 +35,48 @@ open Microsoft.VisualStudio.Text.Tagging open Microsoft.VisualStudio.Text.Formatting open Microsoft.VisualStudio.Shell open Microsoft.VisualStudio.Shell.Interop +open Microsoft.VisualStudio.Imaging +open Microsoft.VisualStudio.Imaging.Interop +open Microsoft.VisualStudio.PlatformUI open Microsoft.FSharp.Compiler open Microsoft.FSharp.Compiler.Parser open Microsoft.FSharp.Compiler.Range open Microsoft.FSharp.Compiler.SourceCodeServices + open System.Windows.Documents +open System.Windows.Controls +open System.Windows.Data +open System.Windows.Media // FSROSLYNTODO: with the merge of the below PR, the QuickInfo API should be changed // to allow for a more flexible syntax for defining the content of the tooltip. // The below interface should be discarded then or updated accourdingly. // https://github.com/dotnet/roslyn/pull/13623 -type internal FSharpDeferredQuickInfoContent(content: string, textProperties: TextFormattingRunProperties) = +type internal FSharpDeferredQuickInfoContent(content: string, textProperties: TextFormattingRunProperties, glyph: Glyph) = interface IDeferredQuickInfoContent with override this.Create() : FrameworkElement = + let moniker = GlyphExtensions.GetImageMoniker(glyph) + let image = new CrispImage() + image.Moniker <- (box moniker) :?> _ + + // Inform the ImageService of the background color so that images have the correct background. + let binding = + Binding( + "Background", + Converter = new BrushToColorConverter(), + RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof, 1)) + + image.SetBinding(ImageThemingUtilities.ImageBackgroundColorProperty, binding) |> ignore + + image.Margin <- new Thickness(1., 1., 3., 1.) + let symbolGlyphBorder = + new Border( + BorderThickness = new Thickness(0.), + BorderBrush = Brushes.Transparent, + VerticalAlignment = VerticalAlignment.Top, + Child = image) + let textBlock = TextBlock(Run(content), TextWrapping = TextWrapping.Wrap, TextTrimming = TextTrimming.None) textBlock.SetValue(TextElement.BackgroundProperty, textProperties.BackgroundBrush) textBlock.SetValue(TextElement.ForegroundProperty, textProperties.ForegroundBrush) @@ -54,7 +84,19 @@ type internal FSharpDeferredQuickInfoContent(content: string, textProperties: Te textBlock.SetValue(TextElement.FontSizeProperty, textProperties.FontRenderingEmSize) textBlock.SetValue(TextElement.FontStyleProperty, if textProperties.Italic then FontStyles.Italic else FontStyles.Normal) textBlock.SetValue(TextElement.FontWeightProperty, if textProperties.Bold then FontWeights.Bold else FontWeights.Normal) - upcast textBlock + + let symbolGlyphAndMainDescriptionDock = + new DockPanel( + LastChildFill = true, + HorizontalAlignment = HorizontalAlignment.Stretch, + Background = Brushes.Transparent) + + symbolGlyphAndMainDescriptionDock.Children.Add(symbolGlyphBorder) |> ignore + symbolGlyphAndMainDescriptionDock.Children.Add(textBlock) |> ignore + + let panel = StackPanel(Orientation = Orientation.Vertical) + panel.Children.Add(symbolGlyphAndMainDescriptionDock) |> ignore + upcast panel [] [] @@ -70,7 +112,8 @@ type internal FSharpQuickInfoProvider let xmlMemberIndexService = serviceProvider.GetService(typeof) :?> IVsXMLMemberIndexService let documentationBuilder = XmlDocumentation.CreateDocumentationBuilder(xmlMemberIndexService, serviceProvider.DTE) - static member ProvideQuickInfo(checker: FSharpChecker, documentId: DocumentId, sourceText: SourceText, filePath: string, position: int, options: FSharpProjectOptions, textVersionHash: int, cancellationToken: CancellationToken) = + static member ProvideQuickInfo(checker: FSharpChecker, documentId: DocumentId, sourceText: SourceText, filePath: string, position: int, + options: FSharpProjectOptions, textVersionHash: int, cancellationToken: CancellationToken) = async { let! _parseResults, checkResultsAnswer = checker.ParseAndCheckFileInProject(filePath, textVersionHash, sourceText.ToString(), options) let checkFileResults = @@ -84,6 +127,7 @@ type internal FSharpQuickInfoProvider let textLineColumn = textLinePos.Character //let qualifyingNames, partialName = QuickParse.GetPartialLongNameEx(textLine.ToString(), textLineColumn - 1) let defines = CompilerEnvironment.GetCompilationDefinesForEditing(filePath, options.OtherOptions |> Seq.toList) + let tryClassifyAtPosition position = CommonHelpers.tryClassifyAtPosition(documentId, sourceText, filePath, defines, position, SymbolSearchKind.DoesNotIncludeRightColumn, cancellationToken) @@ -95,10 +139,14 @@ type internal FSharpQuickInfoProvider match quickParseInfo with | Some (islandColumn, qualifiers, textSpan) -> let! res = checkFileResults.GetToolTipTextAlternate(textLineNumber, islandColumn, textLine.ToString(), qualifiers, FSharpTokenTag.IDENT) - return - match res with - | FSharpToolTipText [] -> None - | _ -> Some(res, textSpan) + match res with + | FSharpToolTipText [] -> return None + | _ -> + let! symbolUse = checkFileResults.GetSymbolUseAtLocation(textLineNumber, islandColumn, textLine.ToString(), qualifiers) + match symbolUse with + | Some symbolUse -> + return Some(res, textSpan, symbolUse.Symbol) + | None -> return None | None -> return None } @@ -117,10 +165,10 @@ type internal FSharpQuickInfoProvider let! textVersion = document.GetTextVersionAsync(cancellationToken) |> Async.AwaitTask let! quickInfoResult = FSharpQuickInfoProvider.ProvideQuickInfo(checkerProvider.Checker, document.Id, sourceText, document.FilePath, position, options, textVersion.GetHashCode(), cancellationToken) match quickInfoResult with - | Some(toolTipElement, textSpan) -> + | Some(toolTipElement, textSpan, symbol) -> let dataTipText = XmlDocumentation.BuildDataTipText(documentationBuilder, toolTipElement) let textProperties = classificationFormatMapService.GetClassificationFormatMap("tooltip").DefaultTextProperties - return QuickInfoItem(textSpan, FSharpDeferredQuickInfoContent(dataTipText, textProperties)) + return QuickInfoItem(textSpan, FSharpDeferredQuickInfoContent(dataTipText, textProperties, Glyph.forSymbol symbol)) | None -> return null | None -> return null | None -> return null diff --git a/vsintegration/src/FSharp.LanguageService.Base/FSharp.LanguageService.Base.csproj b/vsintegration/src/FSharp.LanguageService.Base/FSharp.LanguageService.Base.csproj index 3cdd734ab5e..32dae6d49b4 100644 --- a/vsintegration/src/FSharp.LanguageService.Base/FSharp.LanguageService.Base.csproj +++ b/vsintegration/src/FSharp.LanguageService.Base/FSharp.LanguageService.Base.csproj @@ -124,6 +124,9 @@ $(FSharpSourcesRoot)\..\packages\Roslyn.Microsoft.VisualStudio.ComponentModelHost.0.0.2\lib\net46\Microsoft.VisualStudio.ComponentModelHost.dll + + $(FSharpSourcesRoot)\..\packages\Microsoft.VisualStudio.Imaging.$(RoslynVSPackagesVersion)\lib\net45\Microsoft.VisualStudio.Imaging.dll + {DED3BBD7-53F4-428A-8C9F-27968E768605} FSharp.Core diff --git a/vsintegration/src/FSharp.LanguageService/FSharp.LanguageService.fsproj b/vsintegration/src/FSharp.LanguageService/FSharp.LanguageService.fsproj index 54af1207843..fdbee63438b 100644 --- a/vsintegration/src/FSharp.LanguageService/FSharp.LanguageService.fsproj +++ b/vsintegration/src/FSharp.LanguageService/FSharp.LanguageService.fsproj @@ -138,6 +138,9 @@ $(FSharpSourcesRoot)\..\packages\Microsoft.VisualFSharp.Microsoft.VisualStudio.Shell.UI.Internal.14.0.25420\lib\net45\Microsoft.VisualStudio.Shell.UI.Internal.dll + + $(FSharpSourcesRoot)\..\packages\Microsoft.VisualStudio.Imaging.$(RoslynVSPackagesVersion)\lib\net45\Microsoft.VisualStudio.Imaging.dll + {DED3BBD7-53F4-428A-8C9F-27968E768605} diff --git a/vsintegration/tests/Salsa/VisualFSharp.Salsa.fsproj b/vsintegration/tests/Salsa/VisualFSharp.Salsa.fsproj index 57291e05efe..dd5a33d1512 100644 --- a/vsintegration/tests/Salsa/VisualFSharp.Salsa.fsproj +++ b/vsintegration/tests/Salsa/VisualFSharp.Salsa.fsproj @@ -101,6 +101,9 @@ $(FSharpSourcesRoot)\..\packages\Microsoft.VisualStudio.Shell.$(RoslynVSBinariesVersion).$(RoslynVSPackagesVersion)\lib\Microsoft.VisualStudio.Shell.$(RoslynVSBinariesVersion).dll True + + $(FSharpSourcesRoot)\..\packages\Microsoft.VisualStudio.Imaging.$(RoslynVSPackagesVersion)\lib\net45\Microsoft.VisualStudio.Imaging.dll + True $(NUnitLibDir)\nunit.framework.dll diff --git a/vsintegration/tests/unittests/QuickInfoProviderTests.fs b/vsintegration/tests/unittests/QuickInfoProviderTests.fs index d6157fc8317..c57093a5055 100644 --- a/vsintegration/tests/unittests/QuickInfoProviderTests.fs +++ b/vsintegration/tests/unittests/QuickInfoProviderTests.fs @@ -100,5 +100,5 @@ Full name: System.Console" FSharpQuickInfoProvider.ProvideQuickInfo(FSharpChecker.Instance, documentId, SourceText.From(fileContents), filePath, caretPosition, options, 0, CancellationToken.None) |> Async.RunSynchronously - let actual = quickInfo |> Option.map (fun (text, _) -> getQuickInfoText text) + let actual = quickInfo |> Option.map (fun (text, _, _) -> getQuickInfoText text) Assert.AreEqual(expected, actual) \ No newline at end of file diff --git a/vsintegration/tests/unittests/VisualFSharp.Unittests.fsproj b/vsintegration/tests/unittests/VisualFSharp.Unittests.fsproj index 6f59277cbb1..2b7968ff7f0 100644 --- a/vsintegration/tests/unittests/VisualFSharp.Unittests.fsproj +++ b/vsintegration/tests/unittests/VisualFSharp.Unittests.fsproj @@ -207,6 +207,9 @@ $(FSharpSourcesRoot)\..\packages\Microsoft.VisualStudio.Editor.$(RoslynVSPackagesVersion)\lib\net45\Microsoft.VisualStudio.Editor.dll True + + $(FSharpSourcesRoot)\..\packages\Microsoft.VisualStudio.Imaging.$(RoslynVSPackagesVersion)\lib\net45\Microsoft.VisualStudio.Imaging.dll +