I'm developing a Visual Studio Extension/VSIX package for work that involves changing the caret's position. Of course, dealing with a single caret is easy:
/// Get the Host of current active view
private async Task<IWpfTextViewHost> GetCurrentViewHostAsync()
{
// First get the active view:
var txtManager = (IVsTextManager)await ServiceProvider.GetServiceAsync(typeof(SVsTextManager));
Assumes.Present(txtManager);
IVsTextView vTextView = null;
const int mustHaveFocus = 1;
textManager.GetActiveView(mustHaveFocus, null, out vTextView);
if (vTextView is IVsUserData userData)
{
// Get host
IWpfTextViewHost viewHost;
object holder;
Guid guidViewHost = DefGuidList.guidWpfTextViewHost;
userData.GetData(ref guidViewHost, out holder);
viewHost = (IWpfTextViewHost)holder;
return viewHost;
}
return null;
}
// Later:
private async void ExecCommand(object sender, EventArgs e)
{
IWpfTextViewHost host = await GetCurrentViewHostAsync();
// Access single caret
host.TextView.Caret.MoveTo(/* New position here */);
}
But let's say I use the "Insert Next Matching Caret" command to insert another caret, so I have two different carets now. Moving the caret using the above method will remove the second caret, and as far as I can tell, IVsTextView
only has a single Caret property.
My next idea was that I'm supposed to access other cursors with other IVsTextManager interfaces in the host
, but the closest thing I can find is its EnumViews(IVsTextBuffer, IVsEnumTextViews)
method, which always returns some negative, non-S_OK
value and leaves the IVsEnumTextViews
item as null
. A similar thing happens with the EnumIndependentViews
method.
Am I approaching this right? How does the "Multiple Carets" thing even work? I can't find any documentation on it. Does the API even let me do this?
It turns out you have to use ITextView2
's (Not ITextView
) MultiSelectionBroker
property, which can only be accessed with TextView's GetMultiSelectionBroker
Extension method. See docs for more details on it.
private async void ExecCommand(object sender, EventArgs e)
{
// ConfigureAwait(true) is just to silence warnings about adding ConfigureAwait(false)
// "false" will cause this to not function properly. (Carets won't update).
IWpfTextViewHost host = await GetCurrentViewHostAsync().ConfigureAwait(true);
// Because TextView is only ITextView and not ITextView2,
// we can only access the MultiSelectionBroker property with the extension method:
IMultiSelectionBroker selectionBroker = host.TextView.GetMultiSelectionBroker();
foreach (var selection in selectionBroker.AllSelections)
{
var nextPoint = GetSomePointHere(host, selection);
selectionBroker.TryPerformActionOnSelection(
selection,
t => t.MoveTo(nextPoint, false, PositionAffinity.Successor),
out Selection after
);
}
}
Don't know why, but I must have completely missed the docs for ITextView2
when I wrote the question.
Note: The default Microsoft.VisualStudio.Text.UI.dll
will not contain this method because it is an extension method. You can remove the dll reference from the solution and re-add the correct one (under Extensions) to fix this.