The PSI is designed to work with multiple languages. It supports many features, from parsing and code rewriting, to formatting and navigation. The PSI provides cross language infrastructure to support these features, but each feature requires language specific implementations, for example, the
LanguageService abstract base class provides the interface to create lexers and parsers for a particular file type, but the implementation of the class must be specific to each language.
As such, the normal
[SolutionComponent] components do not help here. These component attributes can be used to create multiple implementations of an interface or abstract base class, but consumers receive a list of instances, while the PSI requires a single instance for a specific language.
Furthermore, languages are hierarchical, allowing for more specific or perhaps fallback implementations of services. For example, XAML inherits from the XML language, so could provide its own specific code formatter, or fall back to the XML formatter.
The PSI needs a mechanism that will return a single implementation of an interface or base class for a given language type. It handles this with a custom component container.
The PSI defines the
[Language] attribute that derives from
PartAttribute, in the same way that
SolutionComponentAttribute do. Since it derives directly from
PartAttribute, it doesn't get included in either the shell or solution component container. Instead, the
ILanguageManager component maintains a custom container (strictly speaking, it does this through inheritance, so it is a custom container) that filters parts in the product catalogue sets by the
The component specifies the language it relates to by passing the type in as a parameter to the constructor:
The language type is used as a key for lookup, using the
ILanguageManager methods, where
T is the type of the service being retrieved, and
TLanguage is the type of the language. Alternatively, the language is specified as an instance of the language type, perhaps retrieved via
The instance of
ILanguageManager can be retrieved via normal shell component rules, either by constructor injection, or by using
ILanguageManager component is decorated with the
[PsiSharedComponent] attribute, which derives from
ShellComponentAttribute, which means that
ILanguageManager is a normal shell component, and has the normal lifetime of a shell component. When the
Lifetime terminates, all of the parts it manages are also terminated.
The use of the
[PsiSharedComponent] attribute is not significant in and of itself. Its only significance is to indicate that this component is part of the implementation of the PSI, and instances are shared across solutions. As such, it can simply inherit from
[ShellComponent] to get the desired behaviour.