Brace Matching (Managed Package Framework)

Brace matching helps the developer track language elements that need to occur together, such as parentheses and curly braces. When a developer enters a closing brace, the opening brace is highlighted.

You can match two or three co-occurring elements, called pairs and triples. Triples are sets of three co-occurring elements. For example, in C#, the foreach statement forms a triple: "foreach()", "{", and "}". All three elements are highlighted when the closing brace is typed.

The AuthoringSink class supports both pairs and triples with the MatchPair and MatchTriple methods.

Implementation

The language service needs to identify all matched elements in the language and then locate all matching pairs. This is typically accomplished by implementing IScanner to detect a matched language and then using the ParseSource method to match the elements.

The OnCommand method calls the scanner to tokenize the line and return the token just before the caret. The scanner indicates that a language element pair has been found by setting a token trigger value of MatchBraces on the current token. The OnCommand method calls the MatchBraces method that in turn calls the BeginParse method with the parse reason value of MatchBraces to locate the matching language element. When the matching language element is found, both elements are highlighted.

For a complete description of how typing a brace triggers the brace highlighting, see the "Example Parse Operation" section in the topic Language Service Parser and Scanner (Managed Package Framework).

Enabling Support for Brace Matching

The ProvideLanguageServiceAttribute attribute can set the MatchBraces, MatchBracesAtCaret, and ShowMatchingBrace named parameters that set the corresponding properties of the LanguagePreferences class. Language preference properties can also be set by the user.

Registry Entry

Property

Description

MatchBraces

EnableMatchBraces

Enables brace matching

MatchBracesAtCaret

EnableMatchBracesAtCaret

Enables brace matching as the caret moves.

ShowMatchingBrace

EnableShowMatchingBrace

Highlights the matching brace.

Matching Conditional Statements

You can match conditional statements, such as if, else if, and else, or #if, #elif, #else, #endif, in the same way as matching delimiters. You can subclass the AuthoringSink class and provide a method that can add text spans as well as delimiters to the internal array of matching elements.

Setting the Trigger

The following example shows how to detect matching parentheses, curly braces and square braces, and setting the trigger for it in the scanner. The OnCommand method on the Source class detects the trigger and calls the parser to find the matching pair (see the "Finding the Match" section in this topic). This example is for illustrative purposes only. It assumes that your scanner contains a method GetNextToken that identifies and returns tokens from a line of text.

using Microsoft.VisualStudio.Package;
using Microsoft.VisualStudio.TextManager.Interop;

namespace TestLanguagePackage
{
    public class TestScanner : IScanner
    {
        private const string braces = "()[]{}";
        private Lexer lex;

        public bool ScanTokenAndProvideInfoAboutIt(TokenInfo tokenInfo,
                                                   ref int state)
        {
            bool foundToken = false;
            string token = lex.GetNextToken();
            if (token != null)
            {
                foundToken = true;
                char firstChar = token[0];
                if (Char.IsPunctuation(firstChar) && token.Length == 0)
                {
                    if (braces.IndexOf(c) != -1)
                    {
                        tokenInfo.Trigger = TokenTriggers.MatchBraces;
                    }
                }
            }
            return foundToken;
        }

Matching the Braces

Here is a simplified example for matching the language elements { }, ( ), and [ ], and adding their spans to the AuthoringSink object. This approach is not a recommended approach to parsing source code; it is for illustrative purposes only.

using Microsoft.VisualStudio.Package;
using Microsoft.VisualStudio.TextManager.Interop;

namespace TestLanguagePackage
{
    public class Parser
    {
         private IList<TextSpan[]> m_braces;
         public IList<TextSpan[]> Braces
         {
             get { return m_braces; }
         }
         private void AddMatchingBraces(TextSpan braceSpan1, TextSpan braceSpan2)
         {
             if IsMatch(braceSpan1, braceSpan2)
                 m_braces.Add(new TextSpan[] { braceSpan1, braceSpan2 });
         }

         private bool IsMatch(TextSpan braceSpan1, TextSpan braceSpan2)
         {
             //definition for matching here
          }
    }

    public class TestLanguageService : LanguageService
    {
         Parser parser = new Parser();
         Source source = (Source) this.GetSource(req.FileName);

         private AuthoringScope ParseSource(ParseRequest req)
         {
             if (req.Sink.BraceMatching)
             {
                 if (req.Reason==ParseReason.MatchBraces)
                 {
                     foreach (TextSpan[] brace in parser.Braces)
                     {
                         req.Sink.MatchPair(brace[0], brace[1], 1);
                     }
                 }
             }
             return new TestAuthoringScope();
         }
    }
}

See Also

Concepts

Language Service Parser and Scanner (Managed Package Framework)

Other Resources

Language Service Features (Managed Package Framework)