diff --git a/topics/intro/content_updates.md b/topics/intro/content_updates.md index 7f8ddbef8..c13977fad 100644 --- a/topics/intro/content_updates.md +++ b/topics/intro/content_updates.md @@ -9,6 +9,12 @@ See [GitHub Changelog](https://github.com/JetBrains/intellij-sdk-docs/commits/ma ## 2021 +### November-21 + +Language Injection +: Add [Language Injection](language_injection.md) section that shows how the IntelliJ Platform handles different languages within the same source file. + + ### September-21 IDE Infrastructure diff --git a/topics/reference_guide/custom_language_support/language_injection.md b/topics/reference_guide/custom_language_support/language_injection.md index 74b396b5b..087927374 100644 --- a/topics/reference_guide/custom_language_support/language_injection.md +++ b/topics/reference_guide/custom_language_support/language_injection.md @@ -1,6 +1,6 @@ [//]: # (title: Language Injection) - + Language injection is the way the IntelliJ Platform handles different languages within the same source file. Well-known examples are: @@ -12,13 +12,13 @@ Injected code is always bound to a specific context that depends on the surround - Table of Contents + Regex Language Injection - Table of Contents + SQL Language Injection - Table of Contents + Markdown Language Injection @@ -46,7 +46,7 @@ The injections shown are configured through XML files and loaded automatically. Let’s take a look at the Java `String.matches()` method that injects the RegExp language into the string of the first argument. In the IntelliLang settings, it is defined as one possible injection in Java code. -Table of Contents +![Language Injection Settings](language_injection_settings.png){border-effect="line"} Double-clicking on this entry shows the exact context where a RegExp can be injected, and `String.matches()` is one of several possibilities. On the plugin side, these entries are defined in the file [`javaInjections.xml`](upsource:///plugins/IntelliLang/java-support/resources/javaInjections.xml): @@ -55,7 +55,12 @@ On the plugin side, these entries are defined in the file [`javaInjections.xml`] String (java.lang) ... - + ``` @@ -107,13 +112,13 @@ For instance, injecting SQLite into Python code is specified by the following op Inside an injection, the following tags can be used: -| XML Tag | Description | -|---------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `` | A short name for the injection. | -| `` | The element pattern that defines where an injection will take place. The content is wrapped in `![CDATA[...]]`. | -| `` and `` | Static content that is wrapped around the injected code, e.g., to make it a valid expression. For example, to a CSS color specification inside a string, it can be wrapped with the prefix `div { color:` and the suffix `;}` to make it a valid CSS expression. | -| `` | A regex for the content that specifies when this injection should be applied. | -| `` | A regex for the content that specifies when this injection should not be applied. | +| XML Tag | Description | +|---------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `` | A short name for the injection. | +| `` | The element pattern that defines where an injection will take place. The content is wrapped in `![CDATA[...]]`. | +| `` and `` | Static content that is wrapped around the injected code, e.g., to make it a valid expression. For example, to a CSS color specification inside a string, it can be wrapped with the prefix `div { color:` and the suffix `;}` to make it a valid CSS expression. | +| `` | A regex for the content that specifies when this injection should be applied. Regex groups can specify the text range of the injection (e.g. `^javascript:(.+)`, see [`xmlInjections-html.xml`](upsource:///plugins/IntelliLang/xml-support/resources/xmlInjections-html.xml)). | +| `` | A regex for the content that specifies when this injection should not be applied. | #### Create an XML File to Load the Configuration @@ -134,7 +139,9 @@ The injections are an optional dependency that only work when IntelliLang is ena Therefore, you load the configuration optionally in your main plugin.xml: ````xml -org.intellij.intelliLang + + org.intellij.intelliLang + ```` ## LanguageInjectionContributor and LanguageInjectionPerformer @@ -149,10 +156,12 @@ public final class MyConfigInjector implements LanguageInjectionContributor { public Injection getInjection(@NotNull PsiElement context) { if (!isConfigPlace(context)) return null; if (shouldInjectYaml(context)) { - return new SimpleInjection(YAMLLanguage.INSTANCE.getID(), "", "", null); + return new SimpleInjection( + YAMLLanguage.INSTANCE.getID(), "", "", null); } else if (shouldInjectJSON(context)) { - return new SimpleInjection(JsonLanguage.INSTANCE.getID(), "", "", null); + return new SimpleInjection( + JsonLanguage.INSTANCE.getID(), "", "", null); } return null; } @@ -162,7 +171,9 @@ public Injection getInjection(@NotNull PsiElement context) { Register the implementation in your plugin.xml: ```xml - + ``` If you want more control over how the injection should be done then implement the `com.intellij.languageInjectionPerformer` EP which allows for complex language injections, e.g. for concatenation or interpolation of strings. @@ -184,11 +195,15 @@ Plugin authors need to implement `getLanguagesToInject()` to provide a list of p For example, to inject regular expressions into Java string literal, you can override this method with something similar to this: ```java -class MyRegExpToJavaInjector implements MultiHostInjector { - void getLanguagesToInject(MultiHostRegistrar registrar, PsiElement context) { - if (context instanceof PsiLiteralExpression && looksLikeAGoodPlaceToInject(context)) { - registrar.startInjecting(REGEXP_LANG) - .addPlace(null,null,context,innerRangeStrippingQuotes(context)); +public class MyRegExpToJavaInjector implements MultiHostInjector { + public void getLanguagesToInject( + MultiHostRegistrar registrar, PsiElement context) { + if (context instanceof PsiLiteralExpression && + looksLikeAGoodPlaceToInject(context)) { + registrar + .startInjecting(REGEXP_LANG) + .addPlace(null,null, + context,innerRangeStrippingQuotes(context)); } } } @@ -198,7 +213,12 @@ A more complex example is when you need to inject into several fragments at once For example, if we have an XML-based DSL: ```xml -fooSystem.out.println(42); + + + foo + System.out.println(42); + + ``` which should be converted to the equivalent Java code: @@ -210,13 +230,19 @@ class MyDsl { void foo() { System.out.println(42); } } Here, we need to inject Java into several places at once, i.e. method name and its body: ```java -class MyBizarreDSLInjector implements MultiHostInjector { - void getLanguagesToInject(MultiHostRegistrar registrar, PsiElement context) { +public class MyBizarreDSLInjector implements MultiHostInjector { + public void getLanguagesToInject( + MultiHostRegistrar registrar, PsiElement context) { if (isMethodTag(context)) { registrar.startInjecting(JavaLanguage.INSTANCE); - // construct class header, method header, inject method name, append code block start - registrar.addPlace("class MyDsl { void ", "() {", context, rangeForMethodName(context)); - // inject method body, append closing braces to form a valid Java class structure + + // construct class header, method header, + // inject method name, append code block start + registrar.addPlace("class MyDsl { void ", "() {", + context, rangeForMethodName(context)); + + // inject method body, append closing braces + // to form a valid Java class structure registrar.addPlace(null, "}}", context, rangeForBody(context)); registrar.doneInjecting(); }