How-to: Provide Automatic Brace Matching

When automatic brace matching is enabled, the language service highlights matching braces whenever the user types them. The code in the Babel service also handles brace matching when the user types CTRL+] in Visual Studio. Providing brace matching is done in the following three steps (see the procedures that follow for details):

  • Setting Matching Brace Properties

  • Adding Brace Match Rules

  • Adding Triggers

注意

You must provide your own lex/yacc-compatible tools; these tools are not included in the VS SDK and the language service project will not build without them. To build the language service using the sample grammar and parser for the sample My C Package, you must use version 1.24 or later of Bison and version 2.5.4a or later of Flex. Place the Bison and Flex executables in the Babel Tools folder, for example, <InstallPath>\VisualStudioIntegration\Babel\Tools. These version numbers are specific to the My C sample.

Setting Matching Brace Properties

To enable automatic brace matching, you can set the following properties:

  • Set the MatchBraces property to 1 to enable brace matching.

  • When the ShowMatchingBrace property is set to 1, Visual Studio shows the line of the matching brace in the status bar, which is useful when the matching brace is not visible in the view.

  • The MatchBracesAtCaret property determines if the environment highlights matching braces even when just positioning the caret on a brace. Typically, the environment only highlights when a brace is typed. This feature is quite useful in practice, especially for parenthesis heavy languages like LISP and Scheme.

Adding Brace Match Rules

After you have set brace properties, you need to add grammar rules to parser.y that specify when matching braces are found. The standard service g_service (see the stdservice.h file in the Babel Common folder; for example, [drive]\Program Files\VSIP 8.0\EnvSDK\Babel\Common\stdservice.h) provides the following methods to signal matching braces:

void matchPair  ( in const Location& loc1, 
                  in const Location& loc2 ) const;
void matchTriple( in const Location& loc1, 
                  in const Location& loc2, 
                  in const Location& loc3 ) const;

Call one of these from your grammar with the locations of the braces whenever a matching pair is found. The following example from the My C sample shows the code that matched parenthesized expressions:

Example of Brace Matching in My C

ParenExpr
    : '(' Expr ')'   { g_service->matchPair($1,$3); }
    | '(' Expr error 
          { g_service->expectError( "unmatched parenthesis", ")" ); }
    ;

Match braces whenever appropriate. The following example shows how braces are matched parsing a parameter list:

Example of Brace Matching a Parameter List

ParenParams
    :  '(' ')'            { g_service->matchPair($1,$2); }
    |  '(' Params1 ')'    { g_service->matchPair($1,$3); }
    |  '(' Params1 error  { g_service->expectError( "unmatched parenthesis", ")" ); }
    |  '(' error ')'      { g_service->matchPair($1,$3); 
                            g_service->syntaxError( "parameters", &$2 ); }
Arbitrary ranges of text can be braces. Even triple braces can be matched.

Adding Triggers

It is very time-consuming and not possible to run the parser on each editor command, including keystrokes. To recognize matching braces, the Babel service needs to know when a parse might be needed. For example, a parse is indicated when a token is entered that looks like a brace token. The Babel service achieves the necessary performance boost by using triggers, which are specified in the Service::getTokenInfo method.

Open the service.cpp file and add the TriggerMatchBraces trigger to each token that is (or might be) a brace for your language (see TriggerClass for more details on triggers). An example of using triggers is shown for the My C language in the following example:

Example of Using Triggers in My C

override const TokenInfo* Service::getTokenInfo() const
{
  static TokenInfo tokenInfoTable[] =
  {
    //TODO: Add your token information here.
    { IDENTIFIER, ClassIdentifier,  "identifier '%s'",  CharIdentifier },
    ...
    { KWVOID, ClassKeyword, "void", CharKeyword   },
    { '(',    ClassText,  "'('"   , CharDelimiter, TriggerMatchBraces }, 
    { ')',    ClassText,  "')'"   , CharDelimiter, TriggerMatchBraces },
    { '{',    ClassText,  "'{'"   , CharDelimiter, TriggerMatchBraces },
    { '}',    ClassText,  "'}'"   , CharDelimiter, TriggerMatchBraces },
    { '.',    ClassText,  "'.'"   , CharDelimiter },
    ...
    //Always end with the 'TokenEnd' token.
    { TokenEnd,     ClassText,      "<unknown>"   }
  };

  return tokenInfoTable;
};

See Also

Concepts

Language Service How-to Topics