Walkthrough: Adding Outlining to a Managed Babel Language Service
[This documentation is for preview only, and is subject to change in later releases. Blank topics are included as placeholders.]
This walkthrough shows how to add outlining to a basic Managed Babel language service. It builds on the basic language service that is created in Walkthrough: Using Managed Babel. Because the MPLex and MPPG tools emit C# code, this walkthrough has example code only in C#.
Note
|
|---|
|
This walkthrough shows how to extend Managed Babel classes by adding code to those classes rather than by subclassing them. It also adds tokens to the lexer.lex and parser.y files. |
The first step in supporting outlining in your language is to determine the tokens that define the start and end of an outlined section. For example, in C# the #region and #endregion preprocessor directives can be used to define sections that can be expanded or collapsed. In this walkthrough, the token that defines the start of an outlined section is [, and the token that defines the end of an outlined section is ]. The region that is defined in this manner can contain anything that a block of code that is delimited by braces (the { and } characters) can contain.
To add region tokens to the lexer.lex and parser.y files
-
Open the YourManagedPackage project that you created in Walkthrough: Using Managed Babel.
-
In the Generated folder, open the lexer.lex file.
-
You must add the instructions for defining a token in the rules section of the lexer.lex file. The rules section starts with %% and ends with %%. Find the line that defines the period (search for return (int)'.';). After that line, add the following lines:
\[{ return (int)'['; } \]{ return (int)']'; } -
Save and close lexer.lex.
-
Now you must add the hidden region markers to the grammar that is defined in the parser.y file. That is, you must define what can appear before and after a region, and also what can be contained in the region. For this walkthrough, the region that is delimited by the [ and ] tokens is defined in the same manner as the Block, which is delimited by { and }.
-
Open parser.y. In the rules section of the file, define the OpenHide and CloseHide symbols to resemble the OpenBlock and CloseBlock symbols. Find the section that resembles the following one:
OpenBlock : '{' { /* */ } ; CloseBlock : '}' { /* */ } ;Add OpenHide and CloseHide symbols in the same way, as follows:
OpenHide : '[' { /* */ } ; CloseHide : ']' { /* */ } ; -
Next you must specify the symbols that can appear between the OpenHide and CloseHide symbols. Find the rule that defines Block. It should have a rule that resembles the following one:
Block : OpenBlock CloseBlock { Match(@1, @2); } | OpenBlock BlockContent1 CloseBlock { Match(@1, @3); } | OpenBlock BlockContent1 error { CallHdlr("missing '}'", @3); } | OpenBlock error CloseBlock { Match(@1, @3); } ; -
Add a Hide symbol that resembles the Block symbol. This rule will enable the same content to occur inside a hidden region as inside a Block, and will match the OpenHide with the CloseHide and throw an error if the hidden region is not closed correctly.
Hide : OpenHide CloseHide { Match(@1, @2); } | OpenHide BlockContent1 CloseHide { Match(@1, @3); } | OpenHide BlockContent1 error { CallHdlr("missing '}'", @3); } | OpenHide error CloseHide { Match(@1, @3); } ; -
Now add the Hide symbol as a part of the Declaration_rule. This will enable the hidden region to occur before or after any other block of code. Change this rule to resemble the following one:
Declaration_ : Class1 Type IDENTIFIER ParenParams Block | Class1 IDENTIFIER ParenParams Block | Type IDENTIFIER ParenParams Block | IDENTIFIER ParenParams Block | SimpleDeclaration | Hide ; -
Save and close lexer.lex.
A language service that supports outlining must register as such by using the ProvideLanguageServiceAttribute with the named parameter AutoOutlining set to true.
To register outlining for your language
-
In the UserSupplied folder of the YourManagedPackage project, open the Package.cs file.
-
Add the AutoOutlining parameter to the ProvideLanguageService attribute so that it resembles the following:
[Microsoft.VisualStudio.Shell.ProvideLanguageService(typeof(Babel.LanguageService), Configuration.Name, 0, AutoOutlining = true, EnableCommenting = true, MatchBraces = true, ShowMatchingBrace = true)]
You must specify the class, color, and triggers for the new [ and ] tokens. They should be defined in the same manner as the { and } tokens are defined.
To define the outlining tokens
-
In the UserSupplied folder of the YourManagedPackage project, open the Configuration.cs file.
-
In the section of the Configuration constructor where the tokens are defined, add the following lines:
ColorToken((int)'[', TokenType.Delimiter, TokenColor.Comment, TokenTriggers.MatchBraces); ColorToken((int)']', TokenType.Delimiter, TokenColor.Comment, TokenTriggers.MatchBraces);
After you register your language service for outlining, you must add some code to the ParseSource method to detect the outlining tokens and construct a TextSpan object that defines the hidden region. The code that is given in the following procedure illustrates the general approach to take. A full language implementation would integrate this procedure into more general parsing procedures.
Important
|
|---|
|
In this procedure you will edit the Managed Babel source code files. The code is meant only to demonstrate how to construct a hidden region. A full parser implementation would carry out the parse differently. |
To detect outline tokens and construct a hidden region
-
In the Invariant folder, open the LanguageService.cs file.
-
Add some code to detect the region tokens in the ParseSource method. Because the tokens that define a hidden region should also be matched, the region tokens can be found when the ParseReason is HighlightBraces, MatchBraces, or MemberSelectAndHighlightBraces.
-
Find the switch statement and the line case ParseReason.MemberSelectAndHighlightBraces. Add the following code to the block. This code constructs a TextSpan, sets ProcessHiddenRegions to true, and adds the hidden region to the list that is kept by the AuthoringSink object.
if (brace.Length == 2) { if (req.Sink.HiddenRegions == true) { string first = source.GetText(brace[0]); if (source.GetText(brace[0]).Contains("[") && source.GetText(brace[1]).Contains("]")) { //construct a TextSpan of everything between the braces TextSpan hideSpan = new TextSpan(); hideSpan.iStartIndex = brace[0].iStartIndex; hideSpan.iStartLine = brace[0].iStartLine; hideSpan.iEndIndex = brace[1].iEndIndex; hideSpan.iEndLine = brace[1].iEndLine; req.Sink.ProcessHiddenRegions = true; req.Sink.AddHiddenRegion(hideSpan); } } req.Sink.MatchPair(brace[0], brace[1], 1); } -
This code should cause an expanded hidden region to be displayed. If you want to be notified when a user changes the state of this region (from expanded to collapsed or vice versa), you can add the following code to the Source.cs file in the Invariant folder:
public override void OnHiddenRegionChange(IVsHiddenRegion region, HIDDEN_REGION_EVENT evt, int fBufferModifiable) { Console.WriteLine("got OnHiddenRegionChange"); base.OnHiddenRegionChange(region, evt, fBufferModifiable); } -
Save all the files and rebuild the project.
Now that you have implemented outlining in your language service, you can test it by using a code file for your language.
To test outlining in your language service
-
Build and run the project.
-
Open a new file in the new instance of Visual Studio and save it as File.ym.
-
Type something like the following in the File.ym file:
[ int newInt; ]
-
You should see that outlining appears on the block of text. When you expand or collapse the region, you should get an OnHiddenRegionChange event.
Note