How to: Provide Statement Completion
Statement completion is the completion of a keyword or member name when the triggering token is typed in the editor. Traditional statement completion is invoked by pressing CTRL+<LEFT ARROW> or CTRL+SPACEBAR. The Visual Studio environment completes the current word if possible or shows a list of possible candidates if there is ambiguity. The key combination CTRL+J shows list of all identifiers that are in scope. Providing statement completion is done in the following steps (see the sections that follow for details):
Setting Statement Completion Properties
Providing Scope Information
This topic is based on the Visual Studio Language Package solution, which provides a basic implementation of the Babel package. For more information about creating one of these solutions, see Walkthrough: Creating a Language Service Package.
Setting Statement Completion Properties
Statement completion is determined by two language properties. See the Globals section in the service.cpp file for the g_LanguageProperties list.
The ShowCompletion property determines whether the options Show Member List and Show Parameter List are visible in the Visual Studio environment. This property is enabled by default.
The SortMemberList property determines whether the list is alphabetized. This property is also enabled by default.
Providing Scope Information
In order to implement statement completion, you must provide information about scope with the IScope Interface. The standard service has two methods that provide the default set of information, addScope and addScopeText. You may need to override these methods if your language has needs that go beyond the default information.
For each scope, the addScope or addScopeText method is called, giving the start and end locations. These locations give the lexical scope of this declaration. If a declaration does not have a lexical scope, such as a variable declaration, the locations might simply coincide.
The ScopeKind parameter passed in to addScope or addScopeText determines whether a declaration has parameters or can contain other scopes. A method information window is displayed only for scopes that have the ScopeProcedure kind (see the ScopeKind Enumeration enumeration for details).
The ScopeAccess parameter determines the visibility of the declaration and its members. See the ScopeAccess Enumeration enumeration for details.
The ScopeStorage parameter is used to calculate space for an icon if one is not given and the parameter is used to find the parameters of a method. See the ScopeStorage Enumeration enumeration for more details on scope storage.
The name parameter contains the name of a declaration. The descStart and descEnd parameters contain the start and end location of the declaration. The text between those locations is the description of this declaration. The type location contains the type of this declaration. Set the merge flag to TRUE if the declaration is specified in multiple places and you want the information to be merged with the previous entries.
You can extend the rules in parser.y with calls to the addScope method. The following example shows a declaration of a public procedure:
Example of Declaring a Public Procedure
Declaration
: Type IDENTIFIER ParenParams Block
{ g_service->addScope( $1, $4,
ScopeProcedure, AccessPublic, StorageType,
$2, $1, $3, &$1 ); }
;
The scope of the method extends from the start of the Type to the end of the Block, that is, $1 and $4. The name is given by IDENTIFIER, that is, $2. The description of the scope that is shown as quick info is just the declaration without the block of code, that is, $1 to $3.
By default, the location of a production is the same as that of its first token; each rule contains the implicit assignment: $$ = $0;. To verify that the location of the Block production includes the entire block (and not just the first token brace), add the following rules:
Block
: '{' '}'
{ $$ = g_service->range($1,$2);
g_service->matchPair($1,$2);
g_service->addScopeText( $1, $2,
ScopeBlock, AccessPublic, StorageOther);
}
| '{' BlockContent1 '}'
{ $$ = g_service->range($1,$3);
g_service->matchPair($1,$3);
g_service->addScopeText( $1, $3,
ScopeBlock, AccessPublic, StorageOther);
}
;
The rules assign the whole range, beginning to end, to the location of the Block. In this particular case, it would suffice to assign $$ = $2 and $$ = $3 respectively, but using the range method is a good coding practice.
A block itself imposes a lexical scope although it has no name or description. Here, the method addScopeText is called with the ScopeBlock kind and no text for the name, description, or type.
Change History
Date |
History |
Reason |
---|---|---|
July 2008 |
Rewrote and refactored project. |
Content bug fix. |