PSI Language Service
Each custom language must register a PSI language service. This is a per-language component that derives from the LanguageService
abstract class. It provides the entry points for lexing, parsing and building the PSI tree of a custom language.
As it is the main entry point for functionality for a custom language, it is a rather large base class. Some of the type members have implementations, while others are virtual or abstract. The class looks like this (with implementations elided):
Lexing
Lexing is the process of reading a text buffer and producing a stream of tokens, identifying elements within a file, e.g. identifier, keyword, whitespace, comment, etc. The LanguageService
provides several methods for creating and working with lexers for the custom language.
GetPrimaryLexerFactory
(abstract) - returns an instance ofILexerFactory
that can be used to create anILexer
for the language. Typically this class is very simple, and will just create an instance of a class that implementsILexer
.CreateFilteringLexer
(abstract) - creates and returns anILexer
implementation that takes a givenILexer
and filters out unnecessary nodes, essentially whitespace and comments. This is used in several places where the code has no interest in whitespace or comments. The typical implementation will derive from theFilteringLexer
abstract class, and implement the abstractSkip
method. This class will wrap the original passed inILexer
, and callSkip
to see if a given token type should be skipped.CreateCachingLexer
- creates a lexer that operates over a cache of tokens, exposed in the returnedILazyCachingLexer.TokenBuffer
. This cache can be used to do incremental lexing of a file when only a portion changes, and if the language service implements a cache provider, can also be persisted to ReSharper's caches, to speed up parsing files when opening. TheLanguageService
class provides a default implementation of this method that uses the lexer returned fromGetPrimaryLexerFactory
.
Parsing
The parser uses the lexer's stream of tokens to produce a concrete syntax tree, or parse tree (aka PSI tree), that represents the structure of the file.
CreateParser
(abstract) - create an instance ofIParser
, given anILexer
, anIPsiSourceFile
and anIPsiModule
. The language service should return anIParser
that can parse the custom language.ParseFile
- usesCreateParser
to parse a file and create anIFile
instance that represents the root of the PSI tree.ParseUsingCapability
- parses some text and return anITreeNode
, passing in a string value to denote a language specific "capability". This allows modifying how the text is parsed, without the need to add extra overloads to the language service. For example, the C# parser allows parsing the text as a file, member declaration, statement or expression. The default implementation parses the text as a file, by callingIParser.ParseFile
.EnumerateParserCapabilities
- returns a list of string identifiers that list the parser's capabilities. Only used in the internal PSI Viewer Form utility.
Services
The language service provides an access point to other language specific services:
CacheProvider
(abstract) - returns an instance ofILanguageCacheProvider
that can participate in ReSharper's caches, such as caching lexed tokens, text buffers, type member declarations, and so on.CodeFormatter
- returns an instance ofICodeFormatter
that can be used to format the file, or code blocks within a file. This can be a per-language component injected into the constructor.ConstantValueService
- returns an instance ofIConstantValueService
used when working with constant values. This is injected into the constructor, and should be implemented as a per-language component. If a language specific implementation isn't found, then the defaultClrConstantValueService
is used instead.TypePresenter
(abstract) - return an instance ofITypePresenter
, used to get presentable names for anIType
. This can be a per-language component injected into the constructor.DeclaredElementPresenter
- return an instance ofIDeclaredElementPresenter
which can return language specific text for anIDeclaredElement
, as well as parameter kind and access rights (ref
,out
, andpublic
,private
, etc.).CreateReferenceContextCodec
- return an instance ofIReferenceContextCoded
, which is used to rebind references when inserting a newITreeNode
tree into an existing file. Typically, this value will either benull
, or a new instance ofReferenceContextCodec
.IsValidName
- returnstrue
if the given name is a valid name for the givenDeclaredElementType
. Used to check for valid names for identifiers, etc.IsTypeMemberVisible
- returnstrue
if the given type member is visible. For example, C# will returnfalse
when given an accessor to a property (such asget_Name
), while the property itself returnstrue
.GetUsedConditionalSymbols
- returns a list of preprocessor directives used in a given file, to allow invalidating caches when a conditional symbol changes.GetReferenceAccessType
- returns the usage of a given reference target, i.e. invocation, read, write, documentation, etc. Used to filter usages in the Find Results window.CreateElementPointer
,CreateTreeElementPointer
andCreateReferencePointer
- allows a language to provide a custom implementation of "pointers" to declared elements, tree nodes and references, such that the items can be found again after a file has been edited. Ifnull
is returned, a default implementation is used instead.OptimizeImportsAndRefs
- optimises import statements, such as C#'susing
directives. Legacy, only implemented by C# and VB. No longer used
Properties
Various properties can control the way the language service works:
IsCaseSensitive
- boolean value to indicate if the language is case sensitive or not.SupportTypeMemberCache
- returnstrue
if the language builds a tree where declarations can be cached.CanContainCachableDeclarations
- returns a value to indicate if the given tree node can contain cachable declarations. The default result istrue
. ReSharper walks PSI trees looking for specific tree nodes that can be cached, i.e. those that also implementICachedTypeMemberDeclaration
orICachedDeclaration2
.ParticipatesInClrCaches
- boolean value to indicate that this language uses the standard CLR caches. Only used to verify if the language of a CLR based project has source attached. All languages return true, apart from C++, which does not support CLR based output.