Created
May 8, 2017 19:07
-
-
Save Thorium/f18477eba8fc792a9f5c05a9e1bdf703 to your computer and use it in GitHub Desktop.
Visual Studio 2017 extension: Displaying Light Bulb Suggestions
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Example follows this, translated to FSharp: | |
// https://msdn.microsoft.com/en-us/library/dn903708.aspx | |
// Just instead of copy&pasting C#, add a new F# class library and paste this code to there, | |
// then add that to reference to your C#-project. | |
// If you want to deploy without C# project, see e.g. this: | |
// You need to use some VSIX-package to deploy the code: | |
// https://github.com/fsharp-vsix/FsVSIX | |
// One more thing to get this to work: Open the source.extension.vsixmanifest file | |
// Go to Assets -> Edit -> Project and change your fsharp dll to dropdown. | |
// This tells MEF to which dll to use to inject dependencies to Visual Studio. | |
// Corresponding XML: | |
// <Asset Type="Microsoft.VisualStudio.MefComponent" d:Source="Project" d:ProjectName="..." Path="|...|" /> | |
#if INTERACTIVE | |
#I @"C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VSSDK\VisualStudioIntegration\Common\Assemblies\v4.0\" | |
#r "Microsoft.VisualStudio.Imaging.Interop.14.0.DesignTime.dll" | |
#r "Microsoft.VisualStudio.Language.Intellisense.dll" | |
#r "Microsoft.VisualStudio.CoreUtility.dll" | |
#r "Microsoft.VisualStudio.Text.Data.dll" | |
#r "Microsoft.VisualStudio.Text.Logic.dll" | |
#r "Microsoft.VisualStudio.Text.UI.dll" | |
#r "Microsoft.VisualStudio.Text.UI.Wpf.dll" | |
#I @"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.6\" | |
#r "PresentationFramework.Core.dll" | |
#r "PresentationFramework.dll" | |
#r "WindowsBase.dll" | |
#r "System.ComponentModel.Composition.dll" | |
#endif | |
namespace VisualStudioTooltips | |
open Microsoft.VisualStudio.Imaging.Interop | |
open System.Windows | |
open System.Windows.Controls | |
open System.Windows.Documents | |
open Microsoft.VisualStudio.Text | |
open System.Threading.Tasks | |
open System | |
open System.Collections.Generic | |
open Microsoft.VisualStudio.Language.Intellisense | |
open Microsoft.VisualStudio.Text.Editor | |
open Microsoft.VisualStudio.Text.Operations | |
open Microsoft.VisualStudio.Utilities | |
open System.ComponentModel.Composition | |
type internal UpperCaseSuggestedAction (span:ITrackingSpan) = | |
let snapshot = span.TextBuffer.CurrentSnapshot | |
let upper = span.GetText(snapshot).ToUpper() | |
let display = sprintf "Convert '%s' to upper case" (span.GetText snapshot) | |
interface ISuggestedAction with | |
member __.GetPreviewAsync (_) = | |
let textBlock = TextBlock(Padding = Thickness(5.)) | |
textBlock.Inlines.Add (Run upper) | |
Task.FromResult<obj> textBlock | |
member __.GetActionSetsAsync (_) = | |
Task.FromResult<IEnumerable<SuggestedActionSet>>(null) | |
member val HasActionSets = false with get | |
member val DisplayText = display with get | |
member val IconMoniker = Unchecked.defaultof<ImageMoniker> with get | |
member val IconAutomationText = Unchecked.defaultof<string> with get | |
member val InputGestureText = Unchecked.defaultof<string> with get | |
member val HasPreview = true with get | |
member __.Invoke (_) = | |
span.TextBuffer.Replace(span.GetSpan(snapshot).Span, upper) |> ignore | |
() | |
member __.TryGetTelemetryId | |
([<System.Runtime.InteropServices.Out>] telemetryId : Guid byref) = | |
telemetryId <- Guid.Empty | |
false | |
member __.Dispose() = () | |
//type internal LowerCaseSuggestedAction : ISuggestedAction | |
[<Export(typeof<ISuggestedActionsSourceProvider>)>] | |
[<Name "Test Suggested Actions">] | |
[<ContentType "text">] | |
type internal TestSuggestedActionsSourceProvider() as this = | |
[<Import(typeof<ITextStructureNavigatorSelectorService>)>] | |
member val NavigatorService = | |
Unchecked.defaultof<ITextStructureNavigatorSelectorService> with get, set | |
interface ISuggestedActionsSourceProvider with | |
member this.CreateSuggestedActionsSource(textView, textBuffer) = | |
if textBuffer = null && textView = null then | |
Unchecked.defaultof<ISuggestedActionsSource> | |
else | |
new TestSuggestedActionsSource(this, textView, textBuffer) | |
:> ISuggestedActionsSource | |
and internal TestSuggestedActionsSource | |
( prov:TestSuggestedActionsSourceProvider, | |
textView:ITextView, textBuffer:ITextBuffer) = | |
let event = DelegateEvent<EventHandler<EventArgs>>() | |
let tryGetWordUnderCaret() = | |
let caret = textView.Caret | |
if caret.Position.BufferPosition.Position <= 0 then | |
false, Unchecked.defaultof<TextExtent> | |
else | |
let point = caret.Position.BufferPosition - 1 | |
let navigator = prov.NavigatorService.GetTextStructureNavigator textBuffer | |
true, navigator.GetExtentOfWord point | |
interface ISuggestedActionsSource with | |
member __.HasSuggestedActionsAsync(requestedActionCategories, range, cToken) = | |
System.Threading.Tasks.Task.Factory.StartNew(fun () -> | |
let ok, extent = tryGetWordUnderCaret() | |
if ok then // don't display the action if the extent has whitespace | |
extent.IsSignificant | |
else false | |
) | |
member __.GetSuggestedActions(requestedActionCategories, range, cToken) = | |
let ok, extent = tryGetWordUnderCaret() | |
if ok && extent.IsSignificant then | |
let trackingSpan = | |
range.Snapshot.CreateTrackingSpan( | |
extent.Span.Span, SpanTrackingMode.EdgeInclusive) | |
use upperAction = new UpperCaseSuggestedAction(trackingSpan) | |
//let lowerAction = LowerCaseSuggestedAction trackingSpan :> ISuggestedAction | |
[| (SuggestedActionSet [| upperAction; (* lowerAction *) |]) |] | |
:> IEnumerable<SuggestedActionSet> | |
else | |
[||] :> IEnumerable<SuggestedActionSet> | |
[<CLIEvent>] | |
member __.SuggestedActionsChanged = event.Publish | |
member __.TryGetTelemetryId( | |
[<System.Runtime.InteropServices.Out>] telemetryId : Guid byref) = | |
telemetryId <- Guid.Empty | |
false | |
interface IDisposable with | |
member __.Dispose() = () |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment