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:

Mark as injected 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:

Remove injection mark

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:

HTML injected into JavaScript

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):

  1. In ReSharper implementation, quotes are not trimmed, neither from language=, nor from prefix= and postfix=. 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.
  2. 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:

Automatic injection in AngularJS

ReSharper has a dedicated Options page where you can see all the available automatic injections and disable or re-enable them as needed.

Language injection settings

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 (using InjectedLanguageIDs 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!

Comments below can no longer be edited.

21 Responses to Language injections in ReSharper Ultimate 2016.3

  1. Avatar

    Vlad says:

    December 26, 2016

    What about SQL code in C# ?

    • Avatar

      Mark says:

      December 27, 2016

      I guess that SQL syntax is (almost) impossible to do right, because the dialects of SQL database engines are too different. Just take a look at PostgreSQL (example: JSON support) and SQL Server (example: column and table quotes).

      • Avatar

        Tyler Benzing says:

        December 30, 2016

        It’s implemented in PHPStorm. Also, all they need to do is have an option for specifying the database provider you’re using. Otherwise, assume the SQL standard definition.

      • Avatar

        Anton Lobov says:

        January 2, 2017

        It is possible to do right, and our DataGrip IDE is fine with many syntax varieties of SQL. DataGrip is based on IntelliJ platform and provides SQL features for many other IntelliJ-based products (IDEA, PhpStorm, PyCharm, …).
        But ReSharper is based on its own platform (because it’s .net-based), and it can’t include support provided by DataGrip, at least currently.

    • Avatar

      feO2x says:

      December 27, 2016

      Yeah – SQL in C# would be a great next step for this feature!

    • Avatar

      Anton Lobov says:

      January 2, 2017

      Language injections mechanism provides only languages, which are natively supported by ReSharper. Unfortunately, SQL is not supported by ReSharper now, so currently it’s impossible to integrate it as an injected language.

      We’ll investigate this question for one of the further releases.

      • Avatar

        Mike-EEE says:

        January 30, 2017

        I just tried this out and was surprised to see XML was not in the list. I thought this was a language that R# supported? I get all sorts of niceties when typing out elements and such… or is that not R#? 😛

        • Avatar

          Anton Lobov says:

          January 30, 2017

          Thanks, create a request:
          Unfortunately, R# doesn’t support XML schema, though. But everything else will be there.

          • Avatar

            Mike-EEE says:

            January 30, 2017

            Ah now I am a little confused, unfortunately. Are you saying that R# is not responsible for all the nice features of XML (and Xaml)? Last I checked in VS all that goodness was due to R#. I guess there must be a difference between supported and “natively supported” or that you support certain features of XML but don’t have full-blown AST support around it. Even in that case it does seem that if you support HTML you would have support for XML since they are so similar.

            In any case, thank you for creating the issue.

            • Avatar

              Anton Lobov says:

              January 30, 2017

              Sorry if you understood it wrong 🙂

              R# is responsible for all the nice features for XAML, that’s not VS.

              R# is responsible for all the quick fixes and context actions in all XML-based languages, that’s also not VS.

              R# supports XML, of course, because it supports many XML-based languages (XAML, MSBuild, NAnt, etc.).

              The only thing that R# doesn’t support for XML is code completion and validation based on XML Schema. 🙂

  2. Avatar

    Uwe says:

    December 27, 2016

    To me it looks strange/wrong that the injection comments do not start with “// ReSharper …” like all the other ReSharper comments like e.g. “// ReSharper disable once PossiblyMistakenUseOfParamsMethod”.

    I would vote for a “unified comment policy”.

    • Avatar

      Anton Lobov says:

      January 2, 2017

      Thanks for your request. We tried to be consistent with the syntax used by IDEA, so that if some parts of the same team use IDEA-based products, and some ReSharper for the same project, they’ll have the same feature out-of-the-box.

      You’re right, it would be nice to support alternative syntax to be consistent with style used by other ReSharper-specific comments. I created a feature request:

  3. Avatar

    Enzi says:

    December 28, 2016

    Is this extensible so we can implement custom language providers?

    • Avatar

      Anton Lobov says:

      January 2, 2017

      If you need to implement an automatic injection for one of already supported languages, you just need to implement the interface called IInjectionNodeProvider as a SolutionComponent.

      If you want to support your own language, this feature is extensible as well, though the API is currently a bit complicated.
      1) you need to create your own Language implementation, as an example, take a look at JsRegexLanguage if you need a language-from-scratch or take a look as HtmlInJsStringsLanguage if you need an implementation based on an existing language:
      – create a component marked by LanguageDefinition attribute (should extend KnownLanguage),
      – create language service: extend LanguageService, mark with Language attribute,
      – create grammar and psi definitions,
      – create lexer implementation: extend InjectedLanguageLexerWithTranslationBase (look at the existing implementations for examples)
      2) you need to implement InjectorProviderInLiteralsWithRangeMarkersBase, mark as SolutionComponent,
      3) you need to create a stub implementing LiteralsInjectionPsiProvider, mark as SolutionComponent.

      I’ll simplify this API for the next version of ReSharper, to make it easier to introduce own languages.

  4. Avatar

    mister-cook says:

    March 18, 2017

    Adding support for XML would be very useful.

    Also, a really cool feature would be XML with option to specify an XSD to validate the XML inline. Very handy for writing XML snippets for unit tests.

    • Avatar

      Anton Lobov says:

      March 21, 2017

      Please upvote this feature request to stay updated about injected XML: Planned for 2017.2.

      Unfortunately, ReSharper currently doesn’t have own support for XSD schemas.

      Validation and coding assistance based on schemas in injects will be available in 2017.2, but only for JSON (because we support JSON schemas natively).

  5. Avatar

    Harprasad says:

    April 7, 2017

    I have generated all c# classes in one file throw XSD command line code generation and now we want to refactor each classes into separate file. May i know is this refactoring is possible through Re sharper or not?, As of now all classes marked in partial class in generated code file.

  6. Avatar

    Dr. Yorick says:

    July 6, 2017

    I often use JS injections in xml files that are actually descriptions for JS classes. Is it possible for ReSharper to highlight JavaScript in xml? Is there any chance that javascript-in-xml injection type will be added to ReSharper options in the not too distant future?

    • Avatar

      Anton Lobov says:

      July 6, 2017

      Thanks for the feedback.
      It is not so hard to implement, but please describe your use-case a bit more.

      Do you have your JS code in XML attribute values, or in CDATA section in XML tag bodies, or just in XML tag bodies without CDATA sections?

      Each of these cases requires us creating a separate lexer. So having an example what you need will help us a lot.

      I created a feature request: Feel free to upvote it and please comment with more details.
      Thank you!

      • Avatar

        Dr. Yorick says:

        July 6, 2017

        Thank you for your prompt reply. I’ve answered your question in the feature request comments.

Discover more