11package com.github.tempest.framework.views.injection
22
3+ import com.github.tempest.framework.TempestFrameworkUtil
34import com.intellij.lang.injection.MultiHostInjector
45import com.intellij.lang.injection.MultiHostRegistrar
56import com.intellij.lang.tree.util.children
@@ -8,69 +9,59 @@ import com.intellij.psi.PsiElement
89import com.intellij.psi.PsiLanguageInjectionHost
910import com.intellij.psi.html.HtmlTag
1011import com.intellij.psi.impl.source.html.HtmlRawTextImpl
12+ import com.intellij.psi.util.PsiTreeUtil
1113import com.intellij.psi.xml.XmlAttribute
1214import com.intellij.psi.xml.XmlAttributeValue
1315import com.intellij.psi.xml.XmlText
1416import com.intellij.psi.xml.XmlToken
1517import com.jetbrains.php.lang.PhpLanguage
18+ import com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocComment
1619
1720class PHPLanguageInjector : MultiHostInjector {
18- val tagsMap = mapOf (
21+ private val templateTags = mapOf (
1922 " {!!" to " !!}" ,
2023 " {{" to " }}" ,
2124 )
2225
23- override fun getLanguagesToInject (
24- registrar : MultiHostRegistrar ,
25- element : PsiElement
26- ) {
26+ override fun getLanguagesToInject (registrar : MultiHostRegistrar , element : PsiElement ) {
2727 when (element) {
28- is XmlAttributeValue -> {
29- val attribute = element.parent as ? XmlAttribute ? : return
30-
31- if (! attribute.name.startsWith(' :' )) return
28+ is XmlAttributeValue -> injectIntoAttribute(element, registrar)
29+ is HtmlTag -> injectIntoHtmlTag(element, registrar)
30+ is XmlText -> (element as ? PsiLanguageInjectionHost )?.let { injectIntoText(it, registrar) }
31+ }
32+ }
3233
33- val injectableHost = element as ? PsiLanguageInjectionHost ? : return
34+ private fun injectIntoAttribute (element : XmlAttributeValue , registrar : MultiHostRegistrar ) {
35+ val attribute = element.parent as ? XmlAttribute ? : return
36+ if (! attribute.name.startsWith(' :' )) return
3437
35- registrar
36- .startInjecting(PhpLanguage .INSTANCE ? : return )
37- .addPlace(" <?=" , " ?>" , injectableHost, TextRange (0 , injectableHost.textLength))
38- .doneInjecting()
39- }
38+ val host = element as ? PsiLanguageInjectionHost ? : return
4039
41- is HtmlTag -> {
42- element.children
43- .mapNotNull { it as ? HtmlRawTextImpl }
44- .forEach { child ->
45- injectIntoText(HtmlTextInjectionHostWrapper (child), registrar)
46- }
47- }
40+ registrar
41+ .startInjecting(PhpLanguage .INSTANCE )
42+ .addPlace(getVarDeclarationsPrefix(element), " ?>" , host, TextRange (0 , host.textLength))
43+ .doneInjecting()
44+ }
4845
49- is XmlText -> {
50- // println("element: ${element.text}, ${element.javaClass.name} ${element is PsiLanguageInjectionHost}")
51- val injectableHost = element as ? PsiLanguageInjectionHost ? : return
52- injectIntoText(injectableHost, registrar)
53- }
54- }
46+ private fun injectIntoHtmlTag (element : HtmlTag , registrar : MultiHostRegistrar ) {
47+ element.children
48+ .filterIsInstance<HtmlRawTextImpl >()
49+ .forEach { injectIntoText(HtmlTextInjectionHostWrapper (it), registrar) }
5550 }
5651
57- private fun injectIntoText (
58- element : PsiLanguageInjectionHost ,
59- registrar : MultiHostRegistrar
60- ) {
61- val children = element.node.children().toList()
52+ private fun injectIntoText (element : PsiLanguageInjectionHost , registrar : MultiHostRegistrar ) {
53+ val tokens = element.node.children()
6254 .filter { it is XmlToken }
63- .apply { if (size < 2 ) return }
55+ .toList()
56+ .takeIf { it.size >= 2 } ? : return
6457
65- // println("children: $children")
66- val openTag = children .find { tagsMap.containsKey( it.text) }?.psi ? : return
67- val closeTag = children.find { it.text == tagsMap[ openTag.text] }?.psi ? : return
58+ val openTag = tokens.find { it.text in templateTags }?.psi ? : return
59+ val closeTag = tokens .find { it.text == templateTags[openTag.text] }?.psi ? : return
60+ val range = TextRange ( openTag.textRangeInParent.endOffset, closeTag.startOffsetInParent)
6861
69- // println("openTag: ${openTag.text}, closeTag: ${closeTag?.text}")
70- val textRange = TextRange (openTag.textRangeInParent.endOffset, closeTag.startOffsetInParent)
71- // println("injecting ${language} into $element, $textRange")
72- registrar.startInjecting(PhpLanguage .INSTANCE )
73- .addPlace(" <?=" , " ?>" , element, textRange)
62+ registrar
63+ .startInjecting(PhpLanguage .INSTANCE )
64+ .addPlace(getVarDeclarationsPrefix(element), " ?>" , element, range)
7465 .doneInjecting()
7566 }
7667
@@ -79,4 +70,20 @@ class PHPLanguageInjector : MultiHostInjector {
7970 XmlText ::class .java,
8071 HtmlTag ::class .java,
8172 )
73+
74+ private fun getVarDeclarationsPrefix (element : PsiElement ): String {
75+ val file = element.containingFile ? : return DEFAULT_PREFIX
76+ if (! file.name.endsWith(TempestFrameworkUtil .TEMPLATE_SUFFIX )) return DEFAULT_PREFIX
77+
78+ val searchFile = file.viewProvider.getPsi(PhpLanguage .INSTANCE ) ? : file
79+ val varDeclarations = PsiTreeUtil .findChildrenOfType(searchFile, PhpDocComment ::class .java)
80+ .filter { " @var" in it.text }
81+ .joinToString(" " ) { it.text }
82+
83+ return if (varDeclarations.isEmpty()) DEFAULT_PREFIX else " <?php $varDeclarations ?>$DEFAULT_PREFIX "
84+ }
85+
86+ companion object {
87+ private const val DEFAULT_PREFIX = " <?="
88+ }
8289}
0 commit comments