Implementing a Language Service (Managed Package Framework)
To implement a language service using the managed package framework (MPF), you must derive a class from the LanguageService class and implement the following abstract methods and properties:
The GetLanguagePreferences method
The GetScanner method
The ParseSource method
The Name property
See the appropriate sections below for details on implementing these methods and properties.
To support additional features, your language service may have to derive a class from one of the MPF language service classes; for example, to support additional menu commands, you must derive a class from the ViewFilter class and override several of the command handling methods (see ViewFilter for details). The LanguageService class provides a number of methods that are called to create new instances of various classes and you override the appropriate creation method to provide an instance of your class. For example, you need to override the CreateViewFilter method in the LanguageService class to return an instance of your own ViewFilter class. See the "Instantiating Custom Classes" section for more details.
Your language service can also supply its own icons, which are used in many places. For example, when an IntelliSense completion list is shown, each item in the list can have an icon associated with it, marking the item as a method, class, namespace, property, or whatever is necessary for your language. These icons are used in all IntelliSense lists, the Navigation bar, and in the Error List task window. See the "Language Service Images" section below for details.
GetLanguagePreferences Method
The GetLanguagePreferences method always returns the same instance of a LanguagePreferences class. You can use the base LanguagePreferences class if you do not need any additional preferences for your language service. The MPF language service classes assume the presence of at least the base LanguagePreferences class.
Example
This example shows a typical implementation of the GetLanguagePreferences method. This example uses the base LanguagePreferences class.
using Microsoft.VisualStudio.Package;
using Microsoft.VisualStudio.TextManager.Interop;
namespace TestLanguagePackage
{
public class TestLanguageService : LanguageService
{
private LanguagePreferences m_preferences;
public override LanguagePreferences GetLanguagePreferences()
{
if (m_preferences == null)
{
m_preferences = new LanguagePreferences(this.Site,
typeof(TestLanguageService).GUID,
this.Name );
m_preferences.Init();
}
return m_preferences;
}
}
}
GetScanner Method
This method returns an instance of an IScanner object that implements a line-oriented parser or scanner used for obtaining tokens and their types and triggers. This scanner is used in the Colorizer class for colorization although the scanner can also be used for getting token types and triggers as a prelude to a more complex parsing operation. You must supply the class that implements the IScanner interface and you must implement all the methods on the IScanner interface.
Example
This example shows a typical implementation of the GetScanner method. The TestScanner class implements the IScanner interface (not shown).
using Microsoft.VisualStudio.Package;
using Microsoft.VisualStudio.TextManager.Interop;
namespace TestLanguagePackage
{
public class TestLanguageService : LanguageService
{
private TestScanner m_scanner;
public override IScanner GetScanner(IVsTextLines buffer)
{
if (m_scanner == null)
{
m_scanner = new TestScanner(buffer);
}
return m_scanner;
}
}
}
internal class TestScanner : IScanner
{
private IVsTextBuffer m_buffer;
string m_source;
public TestScanner(IVsTextBuffer buffer)
{
m_buffer = buffer;
}
bool IScanner.ScanTokenAndProvideInfoAboutIt(TokenInfo tokenInfo, ref int state)
{
tokenInfo.Type = TokenType.Unknown;
tokenInfo.Color = TokenColor.Text;
return true;
}
void IScanner.SetSource(string source, int offset)
{
m_source = source.Substring(offset);
}
}
ParseSource Method
Parses the source file based on a number of different reasons. This method is given a ParseRequest object that describes what is expected from a particular parsing operation. The ParseSource method invokes a more complex parser that determines token functionality and scope. The ParseSource method is used in support for IntelliSense operations as well as brace matching. Even if you do not support such advanced operations, you still must return a valid AuthoringScope object and that requires you to create a class that implements the AuthoringScope interface and implement all methods on that interface. You can return null values from all methods but the AuthoringScope object itself must not be a null value.
Example
This example shows a minimal implementation of the ParseSource method and the AuthoringScope class, sufficient to allow the language service to compile and function without actually supporting any of the more advanced features.
using Microsoft.VisualStudio.Package;
using Microsoft.VisualStudio.TextManager.Interop;
namespace TestLanguagePackage
{
public class TestLanguageService : LanguageService
{
public override AuthoringScope ParseSource(ParseRequest req)
{
return new TestAuthoringScope();
}
}
internal class TestAuthoringScope : AuthoringScope
{
public override string GetDataTipText(int line, int col, out TextSpan span)
{
span = new TextSpan();
return null;
}
public override Declarations GetDeclarations(IVsTextView view,
int line,
int col,
TokenInfo info,
ParseReason reason)
{
return null;
}
public override string Goto(VSConstants.VSStd97CmdID cmd, IVsTextView textView, int line, int col, out TextSpan span)
{
span = new TextSpan();
return null;
}
public override Methods GetMethods(int line, int col, string name)
{
return null;
}
}
Name Property
This property returns the name of the language service. This must be the same name given when the language service was registered. This name is used in a number of places, the most prominent of which is the LanguagePreferences class where the name is used to access the registry. The name returned by this property must not be localized as it is used in the registry for registry entry and key names.
Example
This example shows one possible implementation of the Name property. Note that the name here is hard-coded: the actual name should be obtained from a resource file so it can be used in registering a language service (see Registering a Language Service (Managed Package Framework)).
using Microsoft.VisualStudio.Package;
using Microsoft.VisualStudio.TextManager.Interop;
namespace TestLanguagePackage
{
public class TestLanguageService : LanguageService
{
public override string Name
{
get { return "Test Language"; }
}
}
}
Instantiating Custom Classes
The following methods in the specified classes can be overridden to provide instances of your own versions of each class.
In the LanguageService Class
Method |
Class Returned |
Description |
---|---|---|
To support custom additions to the text view. |
||
To support custom document properties. |
||
To support the Navigation bar. |
||
To support functions in code snippet templates. |
||
To support code snippets (this method is typically not overridden). |
||
To support customization of the ParseRequest structure (this method is typically not overridden). |
||
To support formatting source code, specifying comment characters, and customizing method signatures. |
||
To support additional menu commands. |
||
To support syntax highlighting (this method is typically not overridden). |
||
To support access to language preferences. This method must be implemented but can return an instance of the base class. |
||
To provide a parser used for identifying types of tokens on a line. This method must be implemented and IScanner must be derived from. |
||
To provide a parser used for identifying functionality and scope throughout an entire source file. This method must be implemented and must return an instance of your version of the AuthoringScope class. If all you want to support is syntax highlighting (which requires the IScanner parser returned from the GetScanner method), you can do nothing in this method other than return a version of the AuthoringScope class whose methods all return null values. |
In the Source Class
Method |
Class Returned |
Description |
---|---|---|
For customizing the display of IntelliSense completion lists (this method is typically not overridden). |
||
For supporting markers in the Error List task list; specifically, support for features beyond opening the file and jumping to the line that caused the error. |
||
For customizing the display of IntelliSense Parameter Info ToolTips. |
||
For supporting commenting code. |
||
For gathering information during the parse operation. |
In the AuthoringScope Class
Method |
Class Returned |
Description |
---|---|---|
Provides a list of declarations such as members or types. This method must be implemented but can return a null value. If this method returns a valid object, the object must be an instance of your version of the Declarations class. |
||
Provides a list of method signatures for a given context. This method must be implemented but can return a null value. If this method returns a valid object, the object must be an instance of your version of the Methods class. |
Language Service Images
To provide a list of icons to be used throughout the language service, override the GetImageList method in the LanguageService class and return an ImageList containing the icons. The base LanguageService class loads a default set of icons. Since you specify the exact image index in those places that need icons, how you arrange your own image list is entirely up to you.
Images Used In IntelliSense Completion Lists
For IntelliSense completion lists, the image index is specified for each item in the GetGlyph method of the Declarations class, which you must override if you want to supply an image index. The value returned from the GetGlyph method is an index into the image list supplied to the CompletionSet class constructor and that is the same image list returned from the GetImageList method in the LanguageService class (you can change which image list to use for the CompletionSet if you override the CreateCompletionSet method in the Source class to supply a different image list).
Images Used in the Navigation Bar
The Navigation bar displays lists of types and members and is used for quick navigation can show icons. These icons are obtained from the GetImageList method in the LanguageService class and cannot be overridden specifically for the Navigation bar. The indices used for each item in the combo-boxes are specified when the lists representing the combo-boxes are filled in the OnSynchronizeDropdowns method in the TypeAndMemberDropdownBars class (see Support for the Navigation Bar (Managed Package Framework)). These image indices are obtained somehow from the parser, typically through your version of the Declarations class. How the indices are obtained is entirely up to you.
Images Used in the Error List Task Window
Whenever the ParseSource method parser (see Language Service Parser and Scanner (Managed Package Framework)) encounters an error and passes that error to the AddError method in the AuthoringSink class, the error is reported in the Error List task window. An icon can be associated with each item that appears in the task window and that icon comes from the same image list returned from the GetImageList method in the LanguageService class. The default behavior of the MPF classes is to not show an image with the error message. However, you can override this behavior by deriving a class from the Source class and overriding the CreateErrorTaskItem method. In that method, you create a new DocumentTask object. Before returning that object, you can use the ImageIndex property on the DocumentTask object to set the image index. This would look something like the following example. Note that TestIconImageIndex is an enumeration that lists all icons and is specific to this example. You may have a different way of identifying icons in your language service.
using Microsoft.VisualStudio.Package;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.TextManager.Interop;
namespace TestLanguagePackage
{
class TestSource : Source
{
public override DocumentTask CreateErrorTaskItem(
TextSpan span,
string filename,
string message,
TastPriority priority,
TaskCategory category,
MARKERTYPE markerType,
TaskErrorCategory errorCategory)
{
DocumentTask taskItem = base.CreateErrorTaskItem(
span,
filename,
message,
priority,
category,
markerType,
errorCategory);
if (errorCategory == TaskErrorCategory.Error)
{
taskItem.ImageIndex = TestIconImageIndex.Error;
}
return taskItem;
}
}
}
The Default Image List for a Language Service
The default image list supplied with the base MPF language service classes contains a number of icons associated with the more common language elements. The bulk of these icons are arranged in sets of six variations, corresponding to the access concepts of public, internal, friend, protected, private, and shortcut. For example, you can have different icons for a method depending on whether it is public, protected or private.
The following enumeration specifies typical names for each icon set and specifies the associated index. For example, based on the enumeration, you can specify the image index for a protected method as (int)IconImageIndex.Method + (int)IconImageIndex.AccessProtected. You can change the names in this enumeration as desired.
public enum IconImageIndex
{
// access types
AccessPublic = 0,
AccessInternal = 1,
AccessFriend = 2,
AccessProtected = 3,
AccessPrivate = 4,
AccessShortcut = 5,
Base = 6,
// Each of the following icon type has 6 versions,
//corresponding to the access types
Class = Base + 0,
Constant = Base + 1,
Delegate = Base + 2,
Enumeration = Base + 3,
EnumMember = Base + 4,
Event = Base + 5,
Exception = Base + 6,
Field = Base + 7,
Interface = Base + 8,
Macro = Base + 9,
Map = Base + 10,
MapItem = Base + 11,
Method = Base + 12,
OverloadedMethod = Base + 13,
Module = Base + 14,
Namespace = Base + 15,
Operator = Base + 16,
Property = Base + 17,
Struct = Base + 18,
Template = Base + 19,
Typedef = Base + 20,
Type = Base + 21,
Union = Base + 22,
Variable = Base + 23,
ValueType = Base + 24,
Intrinsic = Base + 25,
JavaMethod = Base + 26,
JavaField = Base + 27,
JavaClass = Base + 28,
JavaNamespace = Base + 29,
JavaInterface = Base + 30,
// Miscellaneous icons with one icon for each type.
Error = 187,
GreyedClass = 188,
GreyedPrivateMethod = 189,
GreyedProtectedMethod = 190,
GreyedPublicMethod = 191,
BrowseResourceFile = 192,
Reference = 193,
Library = 194,
VBProject = 195,
VBWebProject = 196,
CSProject = 197,
CSWebProject = 198,
VB6Project = 199,
CPlusProject = 200,
Form = 201,
OpenFolder = 202,
ClosedFolder = 203,
Arrow = 204,
CSClass = 205,
Snippet = 206,
Keyword = 207,
Info = 208,
CallBrowserCall = 209,
CallBrowserCallRecursive = 210,
XMLEditor = 211,
VJProject = 212,
VJClass = 213,
ForwardedType = 214,
CallsTo = 215,
CallsFrom = 216,
Warning = 217,
}
See Also
Concepts
Language Service Overview (Managed Package Framework)
Registering a Language Service (Managed Package Framework)
Language Service Parser and Scanner (Managed Package Framework)
Other Resources
Implementing a Language Service By Using the Managed Package Framework