>> Hello everyone and welcome to
another episode of Visual Studio Toolbox Extensibility edition.
We are continuing our mini-series all
about the new VisualStudio.Extensibility model,
what it is, how you can take advantage of it.
We've been doing a series of deep dives into how to go about
implementing some of the primary features that
come with writing an extension with this new model.
This time around, last time we actually talked about,
what did we talk about last time?
We talked about so many things.
We talked about the user prompts and
notifications and how you can apply those to your extensions.
This time around, we're going to be focused all
about the editor today.
We've mentioned the editor space a couple of times but is
integral to many different extensions
that you probably have in mind.
Joining us today is Christian who is going
to share more about that. Hi, Christian.
>> Hi. Thank you for the introduction.
>> You want to tell us a little bit more about
yourself before we dive into it?
>> Sure. Yeah, my name is Christian.
I'm a developer on the Visual Studio editor team.
Zooming out on what that means exactly,
we're the ones who bring you-all the goodness inside
the text editor on Digital Studio Windows.
We double a little bit via Smack and a lot of
the productivity in search features that you use every day.
>> Awesome stuff. Just as a refresher,
what is the Editor as it relates to Visual Studio?
>> For the most part, the Editor is this gizmo here.
It's the thing that you interact with more or less.
It's the ability to type into the text editor.
It's the code colorization.
It's all the various completion and [inaudible]
features you get as well as all the margins.
We have error list,
we have various search controls and productivity features.
>> Cool, so everything we modify within that space
that's technically editor related?
>> Yeah, for the most part.
>> Exciting. Can you share
just what are some of the common scenarios
that people use on
a regular basis when they're writing
their editor-based extensions?
>> Sure. Typically,
an extender of the editor is going to
be trying to accomplish one or two things.
Either they may be trying to integrate
a language service and actually light up a language for
the first time or they may be trying to add
some additional goodness on top of an existing language.
To use this example we have here today,
we have a markdown file and we have a markdown linter extension.
They're hard to see in this particular example but they are
these little one-character squiggles
which are showing diagnostics in this particular document.
>> Cool.
>> Yeah. In fact,
if we open up the error list here,
you can see these diagnostics and you can navigate between them.
This is actually an example of a doc extension.
It's interacting with the editor as
the user makes changes it's updating
those squiggles live on
the fly and it's updating their list, etc.
>> Awesome. How does one
even go about getting that functionality started?
We talked a little bit about it when we
were covering commands and stuff definitely.
But what's happening behind the scenes there in addition to that?
>> Let's jump into the code.
Looking at this particular example,
this all really starts in
a TextViewEventListener class in this particular example.
Just starting out with a little bit of geography, if you will,
there's an extension part class here
which essentially says that this class
is something that can be lit up
by the VisualStudio.Extensibility infrastructure.
It implements two different contracts.
We have one, for a lifetime listener.
Lifetime listener gets informed anytime
a TextView is opened or closed.
We have a change listener which gets informed
anytime the user changes text in the TextView.
>> Cool. With lifetime listener,
does that also mean you never have to remember to disable it
or it's just always going to be on and so it's going to be
watching for when those changes are occurring?
>> That's a good question. It's all based on this Metadata here.
When you create a TextViewLifetimeListener,
generally what you want to do is scope
it down to the particular language you care about.
In this case, we're building a Markdown linter.
We're scoping it down to the document type, Markdown.
>> I'm assuming, is there a larger list
of different document types
that you can refer back to because I only care about C#.
>> Yeah.
>> Cool.
>> If you look at the extensibility documentation
for VisualStudio.Extensibility on GitHub,
we do actually have a list of
known languages in the editor walk-through.
This one in particular is one that we're defining
here as part of our content type definition
in the extension sample.
You can see we have a document-type definition.
We're calling it markdown and we are
associating it with these three file extensions.
>> Cool. That makes sense.
You can basically define
your own definitions beyond the default ones in other words?
>> Yeah, absolutely. You can define your own definitions.
You can also extend an existing language.
We don't do this based purely on
file extension because we want to give
you the ability to blank and support something.
I can say I want to support all XML documents or I want to
just support CS projet XML documents, for example.
>> Good stuff.
>> Moving on from there,
when the user opens the TextView for the first time,
this TextView event listener is lazily created.
Notice in the constructor here we have
two or three different parameters.
All of these are objects that are
dependency-injected by the
VisualStudio.Extensibility infrastructure.
One here, this markdown diagnostic service, for example,
is another class that's actually
defined in this particular sample project.
Moving along, what actually gets triggered
off when we open this TextView is we call
this TextViewCreatedAsync method and you are given
an eye TextView snapshot with the state of
the world at that point in time in which the event happened.
>> Nice. I'm liking these intuitive names.
>> Let's drill into TextView snapshot for second,
does that make sense?
>> Yeah.
>> TextView snapshot is going to be a mirror of
what the user is seeing at the point
in time at which the event occurs.
There's two or three things I want to call out about this type.
One is that the snapshot
implies that this is a thing that doesn't change.
It is a point-in-time image of what the TextView had,
be that care positions or text content.
The other thing I want to call out is if
we use completion to drill into this,
there's various properties that you can look at it.
You can look at the selection locations and
there's also a method here for getting the text document content.
>> Of course, that's retrieving all the text document.
>> Yes. Up until this point
we haven't actually requested that text content.
Regardless of how your particular extension is running,
we're just operating on URIs up till this point.
This asynchronous method here is what
actually gets us the full-text content to work on.
>> All right.
>> We've talked a little bit about TextViewCreated.
In addition to that, we also have a TextViewClosed method which,
as I mentioned before, is called in this particular scenario.
>> That's basically when the TextView or when that text has been
selected as close, do whatever function?
>> For example, when a TextView is open,
we can start listening to linter events
and when it's closed we can cancel all of
those pending events and we can remove them from the error list.
>> Now I see how that would be a little messy
if he just left it as it is even after you close.
>> Yeah. Let's take a look at how this actually works.
If we look at ProcessTextViewAsync
and I'm going to go ahead and move this to
full screen so we get a little bit more screen space.
You'll notice the first call here is that thing that I
had mentioned before, GetTextdocumentAsync.
We end up getting back a ITextDocumentsSnapshot.
Notice again, this is that same term we saw before, snapshot,
which means that this is an immutable view
of the document at a point in time.
Let's see what we have access to on the document.
We have a length.
This is the number of characters in that particular document.
We have a lines collection which has the number of
lines and we can index into that as well.
If you wanted to get just the content in the first line you
can look at the length of that line,
you can even copy it to a string if you want to
operate on it as a C Sharp native string type.
>> That was great that you can narrow it down like that.
I got a little worried when you just put dot length to get
the number of characters, I was like, what do we do?
I'm sure that's useful for something,
but being able to draw by line that stuff is great.
>> The key thing with the document snapshots is
you don't ever want to copy the whole thing into a string.
This is why this method is so scarily named copy.
It's a very expensive operation if you have
a very large file and so ideally you
want to work in the smallest bits possible.
>> That's my point.
>> Or even index in character by character.
We can index into a particular line to get the first character.
>> Sweet.
>> In this particular sample, we've
gotten that ITextDocumentSnapshot,
just as I was talking about before.
We're going to go ahead and process
that document and effectively,
we're going to delegate this to that linter process.
In this particular case, the linter is an existing tool,
something that we pulled in off of NPM and so
we're able to just shell out to that particular process,
run those diagnostics and then
parse them off of these standard out.
As we get those back,
we call into a diagnosticsReporter,
which is another API offered by the editor
that allows you to put error squiggles,
warning squiggles, etc.,
both in the text editor if you have one
visible as well as in the error list as we showed before.
>> Great. In this process method
that we have what was it called in folds process?
Process document, cool.
Is that a default method or is this
basically just the method that you created just to be like,
here's what this piece of the service is going to
be doing once the document is actually input?
>> That's a great question and in this case,
we created this method.
The reason why we created this method specifically is that
when you're opening a document in the text editor,
you want to be operating not on some file URI,
not operating on that file directly,
but on the ITextDocumentSnapshot we give you.
The reason for that is as a user is typing,
you want to be able to draw
error squiggles live on the content they have,
which may not be saved yet.
This particular method is
something that we actually wrote ourselves
to operate on that live content
as we get those notifications from VS.
>> Cool.
>> Let's see. We've covered
content types and we've covered documents,
and we've covered TextViews.
>> That's pretty great. What other areas have you got?
>> Those are the core elements that we have up till now.
There will be additional elements
coming at some point in the future
as we identify additional scenarios of interest.
>> Awesome. I think having diagnostics and stuff is pretty good,
especially since linters is what we've enabled
and that's a pretty core thing to have, definitely.
Just seeing all the things that you can manipulate with
the document itself is exciting to see.
I can't wait to see how this continues to evolve.
>> Absolutely.
>> Sweet.
>> Anything else before we close out?
>> Yeah, I'm glad you asked. Here's one more thing.
Let's take a look at a second extension sample.
We have one here, the InsertGuidExtension.
This one's also open source on GitHub and the same
repository as the visualstudio.extensibilitysamples.
This one is just a single file.
We have a visualstudio.extensibility command here.
We're not going to drill too much into that,
I believe there is another video in the series that covers that.
>> Yes, we are.
>> But let's go ahead and take a look at what actually
we do in the execution
of this command to interact with the editor.
Upon executing the command,
we have this client context
and this is something that the command provides to you.
It's information about the state of
the world at the time at which the command was invoked.
Here on line 50,
we're able to use this extension method, get ActiveTextViewAsync,
which unpacks from the context,
the ITextViewSnapshot upon which the action actually occurred.
We have this ITextViewSnapshot and from that,
we're able to get an ITextDocumentSnapshot.
Like we discussed before,
the ITextDocumentSnapshot has the full contents of
the text document at the time in which the action occurred.
To actually insert a guid,
what we can do here is we can use
this Extensibility.Editor.EditAsync method to
call back into Visual Studio.
>> Editor referring to just the general service
of editor based functions?
>> Yeah. Editor referring to anything
that touches the the ITextEditor or the ITextDocument.
>> Cool.
>> We can get an editable copy of that document with
our current edit transaction and we can do one or more operations.
In this case, we're just doing.Replace,
but we could turn this into a batch of
edits and we could do multiple additional operations.
We Edit.Replace, we could do Edit.Insert.
Finally, when we run this EditAsync method,
by the time this method returns,
the edit should have been applied to the text document
in the text editor that the user has opened at that point in time.
>> Awesome. We had to use EditAsync
because the snapshots is mutable, right?
>> Yeah. The key thing to remember here is in Visual Studio,
we care tremendously about the quality of the experience.
One of the big things we want to be able to do there is not have
extensions contribute to any user headaches or paper cuts.
All of your extensions run on a separate thread or in
a separate process and all of these operations
happen asynchronously with VS
doing a lot of the work such as the text editing on your behalf.
>> Cool. Less hangs in Visual Studio for your users?
>> Precisely.
>> Is that all that we got for now?
>> Yeah. That's most of what we have for now.
We'll be continuing to add features
selectively as we get user feedback.
>> Sweet, looking forward to it.
Speaking of user feedback,
make sure you check out
the VS extensibility repo where you can try out
the sample code and get started
on making your own extensions and of course,
sharing your feedback in the issues category.
Real human beings look at that issues section.
Please do share your feedback because it's super helpful,
especially since we're still a work in
progress and working on improving it for you all.
With that, thank you so much Christian for being here.
>> Thank you.
>> Sweet. Stay tuned for
more editor news and also stay tuned when we talk
about remote UI and
tool Windows in the next episode of
Visual Studio toolbox so stay tuned for that.
Until then, happy coding.
[MUSIC]