microsoft / vs-editor-api Goto Github PK
View Code? Open in Web Editor NEWMicrosoft Visual Studio Editor API definitions
License: MIT License
Microsoft Visual Studio Editor API definitions
License: MIT License
In the following file:
It is written that we need to use the Export attribute like this:
[Export(NameSource=typeof(ITextViewModelProvider))]
Where is this Export attribute that has a NameSource property?
Please comment ๐ฑโ๐ค, and I will add emoji to your comment depending on my progress
๐ I will do it
๐ Done
Table of contents:
The API is defined in Microsoft.VisualStudio.Language
nuget, in the Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion
namespace.
All helper types are defined in Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion.Data
namespace.
Completion in VS uses MEF to discover extensions. Specifically, VS is looking for exports of type IAsyncCompletionSourceProvider
, IAsyncCompletionItemManagerProvider
, IAsyncCompletionCommitManagerProvider
and ICompletionPresenterProvider
. Each export must be decorated with Name
and ContentType
metadata. It may be decorated with TextViewRoles
and Order
metadata.
In general, methods named ...Async
are invoked on the background thread and are cancelable. Otherwise, methods are invoked on the UI thread.
Prior to starting, completion checks the following:
IFeatureService.IsEnabled(PredefinedEditorFeatureNames.Completion)
IFeatureService
allows features to be disabled per view or per applicationIFeatureService
allows a group of features to be disabled, e.g. "disable all popups"Is flight "CompletionAPI"
enabled in IVsExperimentationService
IAsyncCompletionBroker.IsCompletionSupported
checks if there are any
IAsyncCompletionSourceProvider
s andIAsyncCompletionItemManager
s for a target content typeImplement IAsyncCompletionSourceProvider that returns an instance of IAsyncCompletionSource
When user interacts with Visual Studio, e.g. by typing, the editor will see if it is appropriate to begin a completion session. We do so by calling TryGetApplicableSpan. This method is invoked on UI thread while the user is typing, therefore it is important to return promptly. Usually, you just need to make a syntactic check whether completion is appropriate at the given location. Despite being called on the UI thread, we a supply CancellationToken
that you should check and respect.
If at least one IAsyncCompletionSource
returned true
from TryGetApplicableSpan
, completion session will start and begin processing on the background thread.
We will attempt to get completion items by asynchronously calling GetCompletionContextAsync where you provide completion items.
This method will be called on all available IAsyncCompletionSource
s, even if they returned false
from TryGetApplicableSpan
.
This is to accommodate for extensions who wish to add completion items without the need to care about the language's syntax, and potential interference with the main language service.
Items from all sources will be combined and eventually displayed in the UI. The UI will call GetDescriptionAsync to build tooltips for items you provided.
Implement IAsyncCompletionCommitManagerProvider that returns an instance of IAsyncCompletionCommitManager
We use this interface to determine under which circumstances to commit (insert the completion text into the text buffer and close the completion UI) a completion item.
There must be one IAsyncCompletionCommitManager
available to begin completion session.
When we first create the completion session, we access the PotentialCommitCharacters property that returns characters that potentially commit completion when user types them. We access this property, therefore it should return a preallocated array.
Typically, the commit characters include space and other token delimeters such as .
, (
, )
. Don't worry about Tab and Enter, as they are handled separately. If a character is a commit character in some, but not all situations, you must add it to this collection. Characters from available IAsyncCompletionCommitManager
s are combined into a single collection for the duration of the completion session.
We maintain this list so that editor's completion feature can quickly ignore characters that are not commit characters. If user types a character found in the provided array, Editor will call ShouldCommitCompletion on the UI thread. This is an opportunity to tell whether certain character is indeed a commit character in the given location. In most cases, simply return true
, which means that every character in PotentialCommitCharacters
will trigger the commit behavior.
When the completion item is about to be committed, Editor calls TryCommit on available IAsyncCompletionCommitManager
s. This method is also called on UI thread and offers complete access to the ITextView
and ITextBuffer
, so that the language service can customize the way text is entered into the buffer. This method returns CommitResult which provides two pieces of information:
bool
that indicates whether the item was committed - if not, Editor will call TryCommit
on another IAsyncCompletionCommitManager
None
.Speaking of complicated language services - TryCommit
was written for these language services. In most cases, feel free to return CommitResult.Unhandled. When all IAsyncCompletionCommitManager
s return CommitResult.Unhandled
, Editor will simply insert the completion item into the text buffer in the appropriate location.
The async completion API allows you to create extension that adds new completion items, without concerning you with the syntax tree or how to commit the item. These questions will be delegated to the language service. You just need to
implement IAsyncCompletionSourceProvider that returns an instance of IAsyncCompletionSource
When you implement TryGetApplicableSpan, return false and leave applicableSpan
as default
. As long as a single language service returns true and sets the applicableSpan
, completion will start. Returning false
does not exclude you from participating in completion!
Implement GetCompletionContextAsync where you provide completion items. They will be added to items from other sources. Implement GetDescriptionAsync that will provide tooltips for items you provided.
Visual Studio provides standard sorting and filtering facilities, but you may want to provide custom behavior for ContentType and TextViewRoles of your choice.
Implement IAsyncCompletionItemManagerProvider that returns an instance of IAsyncCompletionItemManager. Decorate IAsyncCompletionItemManagerProvider
with MEF metadata ContentType
and optionally TextViewRoles
to narrow the scope of your extension.
The completion feature in Visual Studio is represented by IAsyncCompletionSession
(we'll now call it "Session"). This object is active from the moment completion is "triggered" (see TryGetApplicableSpan) until it is Dismissed (pressing Escape or clicking away from completion UI) or until a completion item is Commited. The Session object holds properties that don't drastically change throughout the lifetime of the session: it stores the ITrackingSpan
where completion is happening, has a reference to the ITextView
(which may be null in Cascade!) and has a PropertyBag
.
Immediately after obtaining CompletionItem
s from IAsyncCompletionSource
s, the Session calls SortCompletionListAsync. Then, the Session calls UpdateCompletionListAsync and will do so as long as the user is typing. Both methods are called asynchronously and take similar paremeters which we will cover shortly.
The purpose of SortCompletionListAsync
is to sort items we received from multiple IAsyncCompletionSource
s.
UpdateCompletionListAsync
will receive this sorted list. This is merely a performance improvement so that UpdateCompletionListAsync
doesn't need to sort.
The purpose of UpdateCompletionListAsync
is to produce a final list of items to display in the UI, and to provide information on how to display items in the UI. All the information is bundled in FilteredCompletionModel
Important:
Let's go over the asynchronous computation model.
Suppose user is continuously typing. At every keystroke, we take a reference to the text snapshot in the editor. If we have time, we call UpdateCompletionListAsync
. If user typed quickly, we won't call UpdateCompletionListAsync
. Or perhaps user typed (and modified editor's contents) while you were processing UpdateCompletionListAsync
.
This means that information from IAsyncCompletionSession
or its reference to ITextView
may be stale. This is why we maintain an immutable model that stores all important properties of the completion session. We select the relevant bits and put them into second parameter of SortCompletionListAsync
and UpdateCompletionListAsync
: SessionInitialData and SessionInstantenousData respectively.
We introduced these data transfer objects to keep method signatures short.
Visual Studio provides standard UI, but you may want to create a custom UI for specific ContentType or TextViewRoles. Decorate ICompletionPresenterProvider
with MEF metadata to narrow the scope for your UI.
Implement ICompletionPresenterProvider that returns an instance of ICompletionPresenter
This interface represents a class that manages the user interface. When we first show the completion UI, we call the Open method, and subsequently we call the Update method. Both methods accept a single parameter of type CompletionPresentationViewModel which contains data required to render the UI. We call these methods on the UI thread.
Notice that completion items are represented by CompletionItemWithHighlight - a struct that combines CompletionItem
and array of Span
s that should be bolded in the UI.
Completion filters are represented by CompletionFilterWithState that combines CompletionFilter
with two bools that indicate whether filter is available and whether it is selected by the user. As the user types and narrows down the list of completion items, they also narrow down list of completion filters. Unavailable completion filters are not associated with any items that are visible at the moment, and we represent them with dim icons.
The CompletionFilter
itself has displayText
that appears in the tooltip, accessKey
which is bound to a keyboard shortcut and image
to represent it in the UI.
When user clicks a filter button, create a new instance of CompletionFilterWithState
by calling CompletionFilterWithState.WithSelected, then raise CompletionFilterChaned
event (TODO: event's documentation is not available). Editor will recompute completion items and call Update
with new data.
When user changes the selected item by clicking on it, call CompletionItemSelected event (TODO: event's documentation is not available)
Handling of Up, Down, Page Up and Page Down keys is done on the Editor's side, the UI should not handle these cases. When handling Page Up and Page Down keys, we use the ICompletionPresenterProvider.ResultsPerPage property to select appropriate item.
The completion session depends on the ICompletionPresenter
to accurately report the state of the UI using the following events:
FiltersChanged
that computes new set of items to display after user changed completion filtersCompletionItemSelected
that updates the selected item after user clicked itCommitRequested
when user double-clicked an itemCompletionClosed
when the UI is closed.IAsyncCompletionBroker is the entry point to the completion feature:
ITextView
ITextView
or returns null.IAsyncCompletionSourceProvider
s for a given IContentType
.IAsyncCompletionSession exposes the following:
TextView
which is a reference to pertinent text view
ITextSnapshot
may be different from what's used during computation. See SessionInstantenousData for the correct ITextSnapshot
.ApplicableSpan
which tracks span
CompletionTriggered
when completion session is triggeredItemCommitted
when completion commits and closes,Dismissed
when completion closes without committing.GetComputedItems
method that blocks to finish computation and returns ComputedCompletionItems
ItemsUpdated
that retruns ComputedCompletionItems
after computation finished.The ComputedCompletionItems
, available from IAsyncCompletionSession.ItemsUpdated
and IAsyncCompletionSession.GetComputedItems()
stores:
To minimize number of allocations, create icons and filters once, and use their references in CompletionItem
. For example:
static readonly ImageElement PropertyImage = new AccessibleImageElement(KnownMonikers.Property.ToImageId(), "Property image");
static readonly CompletionFilter PropertyFilter = new CompletionFilter("Properties", "P", PropertyImage);
static readonly ImmutableArray<CompletionFilter> PropertyFilters = new CompletionFilter[] { PropertyFilter }.ToImmutableArray();
alot of times when I am trying to push to GitHub though vs, my changes are not recognized upon saving. and once I save changes, all changes that are still not pushed are not changes anymore. if a video or image is needed to explain what i mean further I will provide one
While both editors are built on this codebase, many aspects of the editor are not open source, including the WPF and Cocoa UI layers.
It is written that WPF layer of this API is not yet available as open source. Is there any plan that WPF implementation of this API will be open sourced in upcoming releases and developers can embed the vs-editor in WPF app ?
In modern editor commanding CommandArgs serves as both unique command identifier and a container for command specific arguments. While most commands do not accept any input arguments and their CommandArgs don't need any command specific arguments beside text view and subject buffer, some commands do. A prime example is the TypeCharCommandArgs, which contains the typed char. Extracting that typed character and instantiating such CommandArgs is host specific (in VS it's passed via IOleCommandTarget.Execโs pvaIn argument) and currently handled by the editor host. At the moment therefore there is no way for extenders to create a new command that accepts host specific input.
Keep command handlers cross IDE and introduce the concept of host specific CommandArgs factories. Such a factory allows creating CommandArgs instances using custom logic that utilizes IDE specific command input such as typed char in TypeCharCommandArgs or command arguments when a command is called via Command Window in VS. In VS such a factory gets access to IOleCommandTarget.Execโs nCmdexecopt and pvaIn arguments.
Other hosts will need to provide a similar functionality.
Here is a sample CommandArgs facrtory:
[Export(typeof(IVsCommandArgsFactory))]
[Command(typeof(ShowContextMenuCommandArgs))]
internal class ShowContextMenuCommandArgsFactory : IVsEditorCommandArgsFactory<ShowContextMenuCommandArgs>
{
/// <summary>
/// This one is used in <see cref="ICommandHandler{T}.GetCommandState(T)"/>.
/// </summary>
public ShowContextMenuCommandArgs CreateCommandArgs(ITextView textView, ITextBuffer subjectBuffer)
{
return new ShowContextMenuCommandArgs(textView, subjectBuffer);
}
/// <summary>
/// This one is used in <see cref="ICommandHandler{T}.ExecuteCommand(T, CommandExecutionContext)"/>.
/// </summary>
public ShowContextMenuCommandArgs CreateCommandArgs(ITextView textView, ITextBuffer subjectBuffer,
uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut)
{
if (pvaIn != IntPtr.Zero)
{
//the coordiantes are passed as variants containing short values. The y coordinate is an offset sizeof(variant)
//from pvaIn (which is 16 bytes)
object xCoordinateVariant = Marshal.GetObjectForNativeVariant(pvaIn);
object yCoordinateVariant = Marshal.GetObjectForNativeVariant(pvaIn + 16);
short? xCoordinate = xCoordinateVariant as short?;
short? yCoordinate = yCoordinateVariant as short?;
if (xCoordinate.HasValue && yCoordinate.HasValue)
{
return new ShowContextMenuCommandArgs(textView, subjectBuffer, xCoordinate.Value, yCoordinate.Value);
}
}
return new ShowContextMenuCommandArgs(textView, subjectBuffer);
}
}
Note that it creates 2 instances of ShowContextMenuCommandArgs, one that doesn't contain any input and is intended to be passed to ICommandHandler.GetCommandState(T) methods and another, which does contain the input and is intended to be passed to ICommandHandler.ExecuteCommand(T, CommandExecutionContext). This weird dichotomy is required because in VS IOleCommandTarget.QueryStatus doesn't have access to command arguments.
Alternatively we could drop first method entirely and require such CommandArgs classes to have a constructor that only accepts ITextView and ITextBuffer, but I prefer to keep all factory logic in one place.
Alternative solution would be to stop treating CommandArgs as both command ID and command input input container and leave it being just command ID and add CommandInput concept. Then ICommandHandler.GetCommandState will only get CommandArgs, but ICommandHandler.ExecuteComamnd - both CommandArgs and CommandInput. That goes against current commanding design in a pretty fundamental way though.
Additionally note [Command] attribute on the factory. Unlike command handlers it doesn't make sense for CommandArgs factories to have [ContentType]/[TextView] attributes so we need some metadata to avoid loading all factories just to determine which can produce a specific CommandArgs.
Alternatively we can introduce a custom attribute for exporting CommandArgs factories:
[ExportCommandArgsFactory(typeof(ShowContextMenuCommandArgs))]
internal class ShowContextMenuCommandArgsFactory : IVsEditorCommandArgsFactory<ShowContextMenuCommandArgs>
{
...
}
In a few profiling analysis sessions done in VSMac, I have observed that ITextUndoHistory
is being leaked in UndoHistoryRegistryImpl.histories
Dictionary.
From a few quick source greps, I observe that:
UndoHistoryRegistryImpl.RemoveHistory
is only called by TextBufferUndoManager.UnregisterUndoHistory
TextBufferUndoManager.UnregisterUndoHistory
.Thus, all docs will leak an UndoHistoryRegistryImpl.
Hey Guys,
I don't know whether I am here on the right place, but I will try this.
The Editor component of VS has some accessibility problems, because some infos are not communicated over the UIA interface or are wrong.
The Issues could be resolved with a custom UIA pattern and some fixes, let's try to find a solution for this problems because this would give the accessibility of the whole VS a very push forward.
all the best,
Christopher
Hi im writing a voice plugin for intellisense and used this as reference. The linq code for the filters seems to performs really badly. Just stepped through it and rewrote it in normal c#. 10x performance increase. not sure why though
Is it possible to use the Visual Studio Editor for mac in a Xamarin for mac application? We're currently doing our own syntax highlight and a few other stuff on top of the standard Cocoa editor, but it would be great to replace it totally by your editor.
Hello. I am a programmer who uses C#/.NET with WinForms. I have heard of issues with WinForms being incompatible with MacOS and especially Linux. While at least one developer was able to get Linux working with WinForms, I don't think that program is compatible with MacOS. Even with "The Linux Update", people on Linux can't use Visual Studio itself to compile the most recent commits. It shouldn't matter the Distro of Linux being used, and both WinForms and Visual Studio should be natively cross compatible. Sure MacOS may be able to use Visual Studio but WinForms is still WIndows only. In a future update for Visual Studio, add Linux Compatibility, and if WinForms ever gets an update, that should also be cross compatible. Maybe the WinForms part shouldn't be in this Issue but I couldn't find a repository for it so I put it here.
-IonicPIxels
bottom text
It is not documented how an extension developer can create their own implementation of ITextViewModelProvider and how such implementation can be used by Visual Studio. Please add more details about this interface.
I like to migrate to the new IAsync* QuickInfo api, but I can't find any examples that use this new API. I'm a bit confused how to migrate the following piece of code. Is the IntellisenseController still part of the new API? Where do I call the AsyncQuickInfoBroker?
internal sealed class QuickInfoController : IIntellisenseController
{
private readonly IList<ITextBuffer> _subjectBuffers;
private readonly IAsyncQuickInfoBroker _quickInfoBroker;
private ITextView _textView;
internal QuickInfoController(
ITextView textView,
IList<ITextBuffer> subjectBuffers,
IAsyncQuickInfoBroker quickInfoBroker)
{
this._textView = textView;
this._subjectBuffers = subjectBuffers;
this._quickInfoBroker = quickInfoBroker;
this._textView.MouseHover += (o, e) => {
SnapshotPoint? point = GetMousePosition(new SnapshotPoint(this._textView.TextSnapshot, e.Position));
if (point.HasValue)
{
ITrackingPoint triggerPoint = point.Value.Snapshot.CreateTrackingPoint(point.Value.Position, PointTrackingMode.Positive);
if (!this._quickInfoBroker.IsQuickInfoActive(this._textView))
{
this._quickInfoBroker.TriggerQuickInfoAsync(this._textView, triggerPoint);
}
}
};
}
Attached is a sample solution (including the ActivityLog.xml detailing the crash) which has been created by following to the letter the instructions in:
When using the obsolete constructor (line 90 in TestSuggestedActionsSource.cs), the extension behaves as expected.
When commenting out line 90 and using the non-obsolete constructor in line 91, the extension will throw an exception and crash VS when invoked via the keyboard shortcut.
Although unrelated to this particular issue, you guys might want to include a comment somewhere that, when using nested actions in the 'Quick Actions' menu (i.e., effectively, submenus), the return collection in GetActionSetsAsync
must apparently be 'Immutable'. Otherwise, undecipherable crashes take place.
Is there any chance you can port this to support Linux as well please. Since this has been introduced into the monodevelop repository we've been unable to build any 8.* branches on Linux. You're alienating the entire Linux community by breaking the open-source monodevelop IDE and preventing us from using it. We're stuck on the older 7.* versions which means we're not able to fully utilise the advancements being made in the dotnet world, things like Balzor just don't work properly.
Please can you show your supposed love of Linux and actually support monodevelop properly on Linux and Mac. There have been open tickets in the monodevelop repository for over 6 months trying to get this resolved.
I see from the readme that the wpf text editor control itself isn't open source (yet?). But is it possible to instantiate the control in a wpf app outside visual studio. The Vs text editor is amazing, and I've always wanted to use it for my own open source projects.
Are there any plans to do for Signature Help, what has already been done for Quick Info and Completion? i.e. something like a IAsyncSignatureHelpSource
, IAsyncSignatureHelpSourceProvider
, etc., built on the modern commanding APIs.
At the moment a fair amount of boilerplate is still required to create a robust implementation of signature help. It would be great to see some of this boilerplate move down from Roslyn to VS :)
I would like to keep a Quick Info open when the mouse moves away. Thus, when I hover my mouse over a textView I issue a Trigger:
this._quickInfoBroker.TriggerQuickInfoAsync(this._textView, triggerPoint, QuickInfoSessionOptions.None);
Yet the option QuickInfoSessionOptions does not have any observable difference between QuickInfoSessionOptions.None
and QuickInfoSessionOptions.TrackMouse
.
Working example can be found here.
Question: What should I do to keep a Quick Info open when the mouse moves away?
Nowadays a tooltip will display at the right side of selected item in the AutoCompletion list.
In VS 2017, we can create a class implementing IUIElementProvider<Completion, ICompletionSession>
to override The RoslynToolTipProvider, like the below code shows.
[Export(typeof(IUIElementProvider<Completion, ICompletionSession>))]
[Name(nameof(CSharpCompletionTooltip))]
//Roslyn is the default Tooltip Provider. We must override it if we wish to use custom tooltips
[Order(Before = "RoslynToolTipProvider")]
[ContentType("CSharp")]
internal sealed class CSharpCompletionTooltipProvider : IUIElementProvider<Completion, ICompletionSession>
In VS 2022, the logic is changed.
It seems that no extension point is provided to override the completion tooltip any more.
What can we do if we are to change the appearance and content of a completion tooltip?
Sorry, unintentionally submitted; pressed the wrong button by mistake.
Hi
I'm using the new IAsync* QuickInfo API. Thanks for the help in here.
I'm not sure where to ask for help on this issue: my tooltip windows are redrawn when UserControls are clicked.
Minimal code can be found here. This behavior can also be observed with the older QuickInfo API.
Questions: would this be the place to ask this question. How to prevent that the tooltip is redrawn, or how to debug this.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.