Tracking changes in C# interactive window
Asked Answered
T

1

8

VS now comes with an interactive window, but unlike running the raw CSI.EXE Roslyn process, Visual Studio adds IntelliSense and a few other features such as being able to load in the current project.

I want to write a VS plug-in that tracks all text editor changes in this window. Is this possible? What I'm looking for is something akin to PreviewKeyDown/PreviewTextInput WPF events. Can I get those on the C# interactive window and, if so, how?

Here's how far I got so far:

var dte = Shell.Instance.GetComponent<DTE>();
foreach (Window window in dte.MainWindow.Collection)
{
  if (window.Kind.ToUpper().Contains("TOOL"))
  {
    if (window.Caption == "C# Interactive")
    {
      WpfWindow wpfWindow = (WpfWindow)HwndSource.FromHwnd((IntPtr) window.HWnd).RootVisual;
      for (int i = 0; i < VTH.GetChildrenCount(wpfWindow); ++i)
      {
        // now what?
      }
    }
  }
}
Transept answered 14/12, 2017 at 6:47 Comment(3)
Meh, another case of its undocumented, literally there is no documentation for using EnvDTE Interactive: learn.microsoft.com/en-us/dotnet/api/…Mundt
Are you looking for the IWpfTextViewHost of the C# interactive Window? Would that be ok for you?Handicap
@SimonMourier not sure. I need a concrete control that can 1) give me its entire buffer and 2) notify on any changes. basically I need to track every change in the interactive window.Transept
H
10

Here is some code that will get an IWpfTextViewHost reference on the C# interactive Window. From there, you can have access to all text services from Visual Studio: Text lines, Text buffer, etc. (or you can hook directly on WPF's controls, which I don't recommend)

// get global UI shell service from a service provider
var shell = (IVsUIShell)ServiceProvider.GetService(typeof(SVsUIShell));

// try to find the C# interactive Window frame from it's package Id
// with different Guids, it could also work for other interactive Windows (F#, VB, etc.)
var CSharpVsInteractiveWindowPackageId = new Guid("{ca8cc5c7-0231-406a-95cd-aa5ed6ac0190}");

// you can use a flag here to force open it
var flags = __VSFINDTOOLWIN.FTW_fFindFirst;
shell.FindToolWindow((uint)flags, ref CSharpVsInteractiveWindowPackageId, out IVsWindowFrame frame);

// available?
if (frame != null)
{
    // get its view (it's a WindowPane)
    frame.GetProperty((int)__VSFPROPID.VSFPROPID_DocView, out object dv);

    // this pane implements IVsInteractiveWindow (you need to add the Microsoft.VisualStudio.VsInteractiveWindow nuget package)
    var iw = (IVsInteractiveWindow)dv;

    // now get the wpf view host
    // using an extension method from Microsoft.VisualStudio.VsInteractiveWindowExtensions class
    IWpfTextViewHost host = iw.InteractiveWindow.GetTextViewHost();

    // you can get lines with this
    var lines = host.TextView.TextViewLines;

    // and subscribe to events in text with this
    host.TextView.TextBuffer.Changed += TextBuffer_Changed;
}

private void TextBuffer_Changed(object sender, TextContentChangedEventArgs e)
{
    // text has changed
}

Note "Microsoft.VisualStudio.VsInteractiveWindow" assembly is not specifically documented but the source is open: http://sourceroslyn.io/#Microsoft.VisualStudio.VsInteractiveWindow

Handicap answered 26/2, 2018 at 11:30 Comment(3)
This looks like it might work! Quick question: how would I go about hooking into the underlying VS control? Reason I'm asking is I'd also like caret and selection information.Transept
Everything should be available from IWpfTextViewHost and below. Most interesting stuff is in ITextView: Caret, Selection, etc. learn.microsoft.com/en-us/dotnet/api/…Handicap
The source is open, nice answer!Mundt

© 2022 - 2024 — McMap. All rights reserved.