Skip to content

Commit c8da5f8

Browse files
committed
feat: inject php language into html text elements
1 parent 363de6a commit c8da5f8

File tree

4 files changed

+108
-69
lines changed

4 files changed

+108
-69
lines changed

src/main/kotlin/com/github/tempest/framework/views/PHPLanguageInjector.kt

Lines changed: 0 additions & 68 deletions
This file was deleted.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package com.github.tempest.framework.views.injection
2+
3+
import com.intellij.extapi.psi.ASTWrapperPsiElement
4+
import com.intellij.lang.ASTFactory
5+
import com.intellij.lang.ASTNode
6+
import com.intellij.psi.LiteralTextEscaper
7+
import com.intellij.psi.PsiLanguageInjectionHost
8+
import com.intellij.psi.impl.source.html.HtmlRawTextImpl
9+
import com.intellij.psi.impl.source.xml.XmlTextImpl
10+
11+
abstract class InjectionHostWrapper(node: ASTNode) : ASTWrapperPsiElement(node), PsiLanguageInjectionHost {
12+
override fun isValidHost(): Boolean = true
13+
14+
override fun updateText(text: String): PsiLanguageInjectionHost {
15+
this.node.treeParent.replaceChild(this.node, ASTFactory.leaf(this.node.elementType, text))
16+
return this
17+
}
18+
}
19+
20+
class XmlTextInjectionHostWrapper(val myElement: XmlTextImpl) : InjectionHostWrapper(myElement.node) {
21+
override fun createLiteralTextEscaper() = myElement.createLiteralTextEscaper()
22+
}
23+
24+
class HtmlTextInjectionHostWrapper(val myElement: HtmlRawTextImpl) : InjectionHostWrapper(myElement.node) {
25+
override fun createLiteralTextEscaper() = LiteralTextEscaper.createSimple(this, false)
26+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package com.github.tempest.framework.views.injection
2+
3+
import com.intellij.lang.Language
4+
import com.intellij.lang.injection.MultiHostInjector
5+
import com.intellij.lang.injection.MultiHostRegistrar
6+
import com.intellij.lang.tree.util.children
7+
import com.intellij.openapi.util.TextRange
8+
import com.intellij.psi.PsiElement
9+
import com.intellij.psi.PsiLanguageInjectionHost
10+
import com.intellij.psi.html.HtmlTag
11+
import com.intellij.psi.impl.source.html.HtmlRawTextImpl
12+
import com.intellij.psi.xml.XmlAttribute
13+
import com.intellij.psi.xml.XmlAttributeValue
14+
import com.intellij.psi.xml.XmlText
15+
import com.intellij.psi.xml.XmlToken
16+
17+
class PHPLanguageInjector : MultiHostInjector {
18+
override fun getLanguagesToInject(
19+
registrar: MultiHostRegistrar,
20+
element: PsiElement
21+
) {
22+
when (element) {
23+
is XmlAttributeValue -> {
24+
val attribute = element.parent as? XmlAttribute ?: return
25+
26+
if (!attribute.name.startsWith(':') || attribute.name.startsWith("::")) return
27+
28+
val injectableHost = element as? PsiLanguageInjectionHost ?: return
29+
30+
registrar
31+
.startInjecting(Language.findLanguageByID("PHP") ?: return)
32+
.addPlace("<?=", "?>", injectableHost, TextRange(0, injectableHost.textLength))
33+
.doneInjecting()
34+
}
35+
36+
is HtmlTag -> {
37+
element.children
38+
.mapNotNull { it as? HtmlRawTextImpl }
39+
.forEach { child->
40+
injectIntoText(HtmlTextInjectionHostWrapper(child), registrar)
41+
}
42+
}
43+
44+
is XmlText -> {
45+
// println("element: ${element.text}, ${element.javaClass.name} ${element is PsiLanguageInjectionHost}")
46+
val injectableHost = element as? PsiLanguageInjectionHost ?: return
47+
injectIntoText(injectableHost, registrar)
48+
}
49+
}
50+
}
51+
52+
private fun injectIntoText(
53+
element: PsiLanguageInjectionHost,
54+
registrar: MultiHostRegistrar
55+
) {
56+
val children = element.node.children().toList()
57+
.filter { it is XmlToken }
58+
.apply { if (size < 2) return }
59+
60+
// println("children: $children")
61+
val openTag = children.firstOrNull()?.psi ?: return
62+
val closeTag = children.lastOrNull()?.psi
63+
64+
// println("openTag: ${openTag.text}, closeTag: ${closeTag?.text}")
65+
if ((openTag.text == "{!!" && closeTag?.text == "!!}") || (openTag.text == "{{" && closeTag?.text == "}}")) {
66+
val language = Language.findLanguageByID("PHP") ?: return
67+
68+
val textRange = TextRange(openTag.textRangeInParent.endOffset, closeTag.startOffsetInParent)
69+
// println("injecting ${language} into $element, $textRange")
70+
registrar.startInjecting(language)
71+
.addPlace("<?=", "?>", element, textRange)
72+
.doneInjecting()
73+
}
74+
}
75+
76+
override fun elementsToInjectIn() = listOf(
77+
XmlAttributeValue::class.java,
78+
XmlText::class.java,
79+
HtmlTag::class.java,
80+
)
81+
}

src/main/resources/META-INF/plugin.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
<extensions defaultExtensionNs="com.intellij">
1313
<multiHostInjector
14-
implementation="com.github.tempest.framework.views.PHPLanguageInjector"/>
14+
implementation="com.github.tempest.framework.views.injection.PHPLanguageInjector"/>
1515
<webSymbols.webTypes
1616
source="META-INF/webTypes.json"
1717
enableByDefault="true"/>

0 commit comments

Comments
 (0)