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 givenTemplateAcceptanceContext
instance, 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.dotSettings
format, 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.WriteToXml
method, which is still supported, but no longer exposed by ReSharper.The
ScopeProvider
abstract base class used by all of the built-in providers will create a new instance based on the short type name in thetype
attribute 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.CreateScope
should create an instance ofITemplateScopePoint
based 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.dotSettings
file, which stores all information as name/value pairs.Again, the
ScopeProvider
abstract base class will create a new instance based on the given type name and populate theITemplateScopePoint.UID
with 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:
IsSubsetOf
Prefix
CalcPrefix
WriteToXml
- 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.dotSettings
format.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 thePsiLanguageType
that applies to this scope point. If the scope point is language agnostic, the default implementation fromTemplateScopePoint.RelatedLanguage
will returnnull
.UID
- a property containing the unique ID for this instance of the scope point. Automatically set when creating the scope viaIScopeProvider.CreateScope
.GetDefaultUID
GetTagName
- returns the short type name, saved to the.dotSettings
file, and used to pass toIScopeProvider.CreateScope
. The default implementation in theTemplateScopePoint
returns 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.dotSettings
file format. TheTemplateScopePoint
abstract base class returns an empty enumeration.
IMainScopePoint
used by QuickListSupport, IScopeCategoryUIProvider and SupportedQuickList