NixOS / nix-idea

Nix plugin for the IntelliJ IDEA IDE
Apache License 2.0
164 stars 28 forks source link

Support Language injections #81

Open Cottand opened 2 months ago

Cottand commented 2 months ago

I would love support for language injections. I think there is value for this in Nix specificially, because in NixOS and Nix derivations we deal with other languages inside Nix strings quite often (bash in derivations, TOML and YAML in configs...).

I think there are two steps to this:

  1. Support the 'inject language here' action
    • I think all that's needed is something like this in NixIndString
    • I also think this is not a lot of effort for good value, because other IDEs (ahem VSCode) cannot do language injections as well as IDEA.
  2. Support 'comments as injection markers' (a lot like the @Language(...) annotation in Java or Kotlin), for more permanent injections
    • This is trickier and would involve following this guide

I tried to follow the example to implement the former myself, but I find I cannot edit PSI elements because they are all code-generated. Would moving them out be an opation?

JojOatXGME commented 2 months ago

Hi, I agree that language injections would be a nice feature.

2. Support 'comments as injection markers' (a lot like the @Language(...) annotation in Java or Kotlin), for more permanent injections

This can probably be separated into two parts. Comments next to the string, and comments on the option declaration when using Nix modules. However, the second part would be tricky and work only when we can resolve references of Nix Modules.

I find I cannot edit PSI elements because they are all code-generated. Would moving them out be an opation?

You cannot modify the generated classes, but you can modify the sources from which they are generated. The following should work:

diff --git a/core/src/main/lang/Nix.bnf b/core/src/main/lang/Nix.bnf
index 5aba71c..948fdad 100644
--- a/core/src/main/lang/Nix.bnf
+++ b/core/src/main/lang/Nix.bnf
@@ -24,6 +24,10 @@
   // no one really needs to know that + - * / are expected at any offset
   consumeTokenMethod("expr_op.*")="consumeTokenFast"

+  // Make Strings to Language Injection Hosts
+  implements("string")="com.intellij.psi.PsiLanguageInjectionHost"
+  mixin("string")="org.nixos.idea.psi.impl.AbstractNixString"
+
   tokens = [
     // This list does not contain all tokens. This list only defines the debug
     // names for tokens which have a distinct text representation. Other tokens

You would then have to create the abstract class org.nixos.idea.psi.impl.AbstractNixString which should extend AbstractNixPsiElement or NixExprStringImpl (doesn't really matter which of the two, but I would prefer the first) and the NixString interface. Within this abstract class, you can implement the methods for PsiLanguageInjectionHost.

Note that there is already NixStringUtil for unescaping strings. For the other direction, the class currently only supports escaping “normal” strings. You properly have to extend the class to support escaping in indented strings.

EDIT: And there is also already NixElementFactory, which can be used (maybe with slight modification) to create new instances of NixString.

EDIT 2: NixStringUtil.parse does not yet remove the common indentation of indented strings.

Note that you may have to handle string interpolation somehow. I think IntelliJ has some support for string interpolation in such cases, but I don't know if there are any special steps required. You could probably also limit support for language injection to strings without interpolations for now, in case that would be much easier.

Cottand commented 2 months ago

thanks, your diff should be exactly what I need to get unblocked. I will get to work this weekend and report back 🙏

as for string interpolation, I'll see how it looks without doing anything special and see from there. Thanks again