.NET Tools
Essential productivity kit for .NET and game developers
Language injections in ReSharper Ultimate 2016.3
ReSharper Ultimate 2016.3 introduces a new infrastructure for language injections in string literals, which enables ReSharper to treat particular string literal contents as a piece of code written in one of the supported programming languages: C# or ECMAScript regular expressions language, CSS, HTML, JSON or JavaScript.
You can inject a language into a string literal in one of the following ways.
Method 1: “Mark as injected language” context action
When you open the Alt+Enter menu on a string literal, you’ll see a “Mark as injected language” item. In the submenu, choose one of the supported languages. The contents of this string literal will be recognized as a piece of code in that language:
Even though this kind of injection tracks code changes and persists in many ordinary cases, it can disappear if you perform huge code reorganization, such as moving to another file or performing Extract method refactoring. Please take into account that tracking code changes has a performance penalty, so if you have many items injected using “Mark as” in the same file, in some cases you may notice a slowdown.
If you need to remove language injection inserted using the “Mark as injected language” context action, you can press Alt+Enter and select “Remove <language> injection mark” there:
Method 2: injection comment
You can use a comment to inject a language into a string literal.
The syntax is mostly compatible with the syntax used by IntelliJ IDEA for the same feature. That means that if your team members use both IntelliJ-based products and ReSharper, all of them will have the same experience for injected languages.
A language injection comment has the following form:
//language=CSS prefix=body{ postfix=}
where language
specifies a language ID to inject. It is case-insensitive. The following language IDs are currently supported:
- CSS – CSS language injection
- HTML – HTML language injection
- JSON – JSON language injection
- JSREGEXP – ECMAScript regular expression language injection
- REGEXP – .NET regular expression language injection
- JAVASCRIPT – JavaScript language injection
Please note that some of language injections are available only for particular target languages.
Availability of language injections using code comments is shown in the following table:
Injected language | In C# strings | In JavaScript strings |
CSS | Yes | Yes |
HTML | Yes | Yes |
JSON | Yes | Yes |
ECMAScript regular expression | No | Yes (prefix/postfix unsupported) |
.NET regular expression | Yes (prefix/postfix unsupported) | No |
JavaScript | Yes | Yes |
prefix=
and postfix=
options provide a way to specify a string that “precedes” your string and a string that “follows” your string, respectively. This is very helpful if you want to have language assistance only for a code fragment. For example, you can inject a CSS color value into a string by using the following comment:
It is important to know that prefix=
and postfix=
are matched textually, so that all the leading and trailing whitespaces specified in these options will be included in the target string. For example, if you want to have a prefix {
and a postfix }
without any whitespaces, then you need to specify the following options:
//language=LANG prefix={postfix=}
Language injections using code comments don’t have the problems of the “Mark as” action described in Method 1. They are stable as long as your comments are there, and they don’t cause any visible performance impact.
There are, however, several differences in how IntelliJ Platform implements the same feature (which is used in IntelliJ IDEA, WebStorm, and other IntelliJ-based products):
- In ReSharper implementation, quotes are not trimmed, neither from
language=
, nor fromprefix=
andpostfix=
. This is done intentionally, because you may want to have prefix/postfix with quotes, and also this is done to encourage using simple injected language IDs. - Comment positioning in ReSharper is more relaxed than in IntelliJ implementation. In IntelliJ, the injection comment always matches the nearest following literal, so you’ll locate it as close as possible to the target literal. In ReSharper, language specifics are taken into account, and the comment may be located after the literal itself, or after some simple construct embracing the literal, or even at the end of the line.
Method 3: automatic injections
This kind of injection is performed by ReSharper itself, so in most cases you will work with injections without even noticing it. For example, if you set the innerHTML
property of a JavaScript object, you’ll get HTML assistance in your assigned string value. Also, HTML and CSS injections are automatically provided for Angular templates in JavaScript code:
ReSharper has a dedicated Options page where you can see all the available automatic injections and disable or re-enable them as needed.
This mechanism is extensible, and it’s easy to provide your own places for language injections for supported languages in your ReSharper plugin. You’ll need to implement the interface called IInjectionNodeProvider
as a SolutionComponent
.
- In the
Check()
method, provide your logic for checking the particular syntax tree node. If it returns true, the target language will be injected in this node. You can save some intermediate computation results into “data”, to use them for prefix and postfix computation. GetPrefix()
/GetPostfix()
provide an ability to specify prefix and postfix for language injection (check ‘Comment’ section of this article to learn more about prefixes and postfixes). You can use a previously computed state “data” here.ProvidedLanguageID
should be one of the IDs described in the section ‘Method 2: Injection Comment’ above (usingInjectedLanguageIDs
static class properties is recommended).
As your plugin is loaded by ReSharper, you’ll see your own injections implementing IInjectionNodeProvider in the same options page where built-in injections are shown.
Current limitations and plans
Currently you can inject a language only into a single proper string literal. That means that you can’t inject a language into a string concatenation. Also currently you can’t inject a language into a C# 6 interpolated string or a ECMAScript 2015 template string if they have template arguments (for argument-less template strings, injection works). Note that even though injection works with C# string.Format
string, the string content is treated literally, without substituting the arguments. So, depending on your target language, it may or may not be a syntax error.
Currently there is no mechanism to provide language injections using ReSharper external annotations. One of the upcoming ReSharper releases will introduce a [Language]
external annotation for C# to provide you with one more way to manage language injections.
Please note that using prefix=
and postfix=
for language injections can cause some features to behave unexpectedly in certain situations. This may happen if you have significant constructs (such as variable declarations) in prefix/postfix code, or if some parts of the same syntax element are split between prefix/postfix and the real string contents. Use this possibility with care.
All the symbols defined inside language injections (for example, JavaScript variables or CSS classes defined inside string literals) are local to the injected piece of code, and are not guaranteed to be reachable from code external to the inject.
Language injections can be implemented only for the languages that are natively supported by ReSharper. For example, that means that until SQL is natively supported by ReSharper, it’s impossible to have injected SQL, despite the fact that it is a highly requested feature. But for any language natively supported by ReSharper language injections are doable. Please create a feature request if you’re missing one.
We’re going to address many of these limitations in our further releases. Stay tuned!