Scopes
ReSharper's Live Templates can declare the scope in which they are available. That is, instead of allowing all templates to be inserted at any location in any file, a template can be constrained to certain file types, and even to certain locations within those files. For example, a template can be constrained not just to C# files, but to any location where a namespace declaration, type definition, or an expression is valid.
Furthermore, the template scope can be parameterised. For example, a C# template can state that it is only available in C# 3.0 files (perhaps the template wants to make use of LINQ), or in files that match a specific file pattern (e.g. "actions.xml", "*.html", etc.)
A template can have multiple scope points, in which case, the template is available if any of the scope points match the current context.
In this way, templates can be constrained to be more targeted and useful, even allowing multiple templates with the same shortcut, as long as they are valid in different scopes.
Implementing a custom scope provider
The TemplateScopeManager shell component is the main entry point for working with scope points, and it maintains a list of IScopeProvider shell components. As such, it can be extended by a class implementing IScopeProvider and decorated with the [ShellComponent] attribute.
The IScopeProvider interface declares three methods that are used to create scope points:
ProvideScopePoints- returns all scope points that are valid at the context described by the givenTemplateAcceptanceContextinstance, which provides details about file and text caret position. Parameterised scope points are either set to a reasonable value (for example, C# scope points can set a language version of 3.0 when the current context is in a LINQ query), or replaced by a scope point that makes more sense for the current context (instead of creating a file mask scope point of "*.*", a project file scope point is returned. This scope point knows how to compare itself against a file mask scope point used in a template)ReadFromXml- used to read a scope point from an XML file. This is not typically used any more, as recent versions of ReSharper store and export templates in.dotSettingsformat, rather than the proprietary XML read by this method. However, the built-in providers still support this, in order to upgrade existing files in the old format. The complement to this method is theITemplateScopePoint.WriteToXmlmethod, which is still supported, but no longer exposed by ReSharper.The
ScopeProviderabstract base class used by all of the built-in providers will create a new instance based on the short type name in thetypeattribute of the XML element. Deriving classes should call the base method to create the instance, then populate it based on other attributes or child elements of the given XML element.CreateScopeshould create an instance ofITemplateScopePointbased on the given short type name. It receives a GUID identifier that is unique for each template scope point instance (remember, scope points might be parameterised) and custom properties as string name/value pairs. This method is used to read the scope point from a.dotSettingsfile, which stores all information as name/value pairs.Again, the
ScopeProviderabstract base class will create a new instance based on the given type name and populate theITemplateScopePoint.UIDwith the unique ID. Derived classes should call the base class and populate the new instance based on the custom properties.
The ScopeProvider abstract base class provides a mechanism for creating specific instances of ITemplateScopePoint based on a given type name. While it can simply instantiate the type based on the type name, it defers to a list of creator functions that take in the type name and create the scope point instance. This provides an extension point for custom creation, while also making it easy to create simple instances.
For example, the CSS scope provider will set up a list of creator functions that simply defer to ScopeProvider.TryToCreate<T> which will create the type if the type name matches:
Example IScopeProvider.ProvideScopePoints implementation
The following is an example of an implementation of IScopeProvider.ProvideScopePoints for CSS files. It examines the current context, as provided by TemplateAcceptanceContext and returns scope points that are valid:
The call to LiveTemplatesManager.GetPrefix gets the "word" that precedes the current text caret position. It is used here to find the position in the document that marks the start of the context of the scope point. This position is used to retrieve the node from the abstract syntax tree, which can then be used to see if the scope point is inside an expression or a statement, or so on.
A "word" is defined as any character preceding the current text caret that is a letter or digit, or is one of a set of allowed characters. The overload shown above does not specify any additional characters, so uses the default of the underscore character '_'. Another overload takes an array of allowed chars.
For example, the HTMLScopeProvider class calls GetPrefix with an array of { '_' } when fetching the prefix for file scope, and uses { '_', '<', ':' } when fetching the prefix for tag scope.
Implementing a custom scope point
A scope point is implemented by deriving from ITemplateScopePoint, and is usually implemented by deriving from the TemplateScopePoint abstract base class. It is not a component from the Component Model, but just a class created by an IScopeProvider. The interface is:
IsSubsetOfPrefixCalcPrefixWriteToXml- used to write the scope point, including parameters, to XML. This is the complement toIScopeProvider.ReadFromXml. ReSharper no longer uses this method for saving scope points, as templates are now saved in.dotSettingsformat.PresentableShortName- a short description of the scope point, displayed in the template editor UI. Can be a static string, or reflecting the parameters, such as"'*.*; *.html' files".RelatedLanguage- returns thePsiLanguageTypethat applies to this scope point. If the scope point is language agnostic, the default implementation fromTemplateScopePoint.RelatedLanguagewill returnnull.UID- a property containing the unique ID for this instance of the scope point. Automatically set when creating the scope viaIScopeProvider.CreateScope.GetDefaultUIDGetTagName- returns the short type name, saved to the.dotSettingsfile, and used to pass toIScopeProvider.CreateScope. The default implementation in theTemplateScopePointreturns the short type name.EnumerateCustomProperties- returns a set of name/value string pairs listing the custom properties of the scope point. Used to save the properties to the.dotSettingsfile format. TheTemplateScopePointabstract base class returns an empty enumeration.
IMainScopePoint used by QuickListSupport, IScopeCategoryUIProvider and SupportedQuickList