-
-
Notifications
You must be signed in to change notification settings - Fork 137
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
#2148 Support for Twig Component HTML Syntax
- Loading branch information
Showing
15 changed files
with
730 additions
and
33 deletions.
There are no files selected for viewing
63 changes: 63 additions & 0 deletions
63
src/main/java/fr/adrienbrault/idea/symfony2plugin/stubs/indexes/UxTemplateStubIndex.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package fr.adrienbrault.idea.symfony2plugin.stubs.indexes; | ||
|
||
import com.intellij.util.indexing.*; | ||
import com.intellij.util.io.DataExternalizer; | ||
import com.intellij.util.io.EnumeratorStringDescriptor; | ||
import com.intellij.util.io.KeyDescriptor; | ||
import com.jetbrains.php.lang.psi.PhpFile; | ||
import com.jetbrains.php.lang.psi.stubs.indexes.PhpConstantNameIndex; | ||
import fr.adrienbrault.idea.symfony2plugin.util.UxUtil; | ||
import org.jetbrains.annotations.NotNull; | ||
|
||
import java.util.HashMap; | ||
import java.util.Map; | ||
|
||
/** | ||
* @author Daniel Espendiller <daniel@espendiller.net> | ||
*/ | ||
public class UxTemplateStubIndex extends FileBasedIndexExtension<String, String> { | ||
public static final ID<String, String> KEY = ID.create("fr.adrienbrault.idea.symfony2plugin.ux_template_index"); | ||
|
||
private final KeyDescriptor<String> myKeyDescriptor = new EnumeratorStringDescriptor(); | ||
@Override | ||
public @NotNull ID<String, String> getName() { | ||
return KEY; | ||
} | ||
|
||
@Override | ||
public @NotNull DataIndexer<String, String, FileContent> getIndexer() { | ||
return inputData -> { | ||
Map<String, String> map = new HashMap<>(); | ||
|
||
if(inputData.getPsiFile() instanceof PhpFile phpFile) { | ||
UxUtil.visitAsTwigComponent(phpFile, pair -> map.put(pair.getFirst(), pair.getSecond().getFQN())); | ||
} | ||
|
||
return map; | ||
}; | ||
} | ||
|
||
@Override | ||
public @NotNull KeyDescriptor<String> getKeyDescriptor() { | ||
return this.myKeyDescriptor; | ||
} | ||
|
||
@Override | ||
public @NotNull DataExternalizer<String> getValueExternalizer() { | ||
return EnumeratorStringDescriptor.INSTANCE; | ||
} | ||
|
||
@Override | ||
public int getVersion() { | ||
return 1; | ||
} | ||
|
||
public FileBasedIndex.@NotNull InputFilter getInputFilter() { | ||
return PhpConstantNameIndex.PHP_INPUT_FILTER; | ||
} | ||
|
||
@Override | ||
public boolean dependsOnFileContent() { | ||
return true; | ||
} | ||
} |
78 changes: 78 additions & 0 deletions
78
...va/fr/adrienbrault/idea/symfony2plugin/templating/HtmlTemplateGoToDeclarationHandler.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
package fr.adrienbrault.idea.symfony2plugin.templating; | ||
|
||
import com.intellij.codeInsight.navigation.actions.GotoDeclarationHandler; | ||
import com.intellij.openapi.editor.Editor; | ||
import com.intellij.psi.PsiElement; | ||
import com.intellij.psi.html.HtmlTag; | ||
import com.intellij.psi.xml.XmlElementType; | ||
import com.intellij.psi.xml.XmlToken; | ||
import com.intellij.psi.xml.XmlTokenType; | ||
import com.jetbrains.php.lang.psi.elements.Field; | ||
import com.jetbrains.php.lang.psi.elements.PhpClass; | ||
import fr.adrienbrault.idea.symfony2plugin.Symfony2ProjectComponent; | ||
import fr.adrienbrault.idea.symfony2plugin.templating.util.TwigHtmlCompletionUtil; | ||
import fr.adrienbrault.idea.symfony2plugin.util.UxUtil; | ||
import org.apache.commons.lang.StringUtils; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Collection; | ||
|
||
/** | ||
* @author Daniel Espendiller <daniel@espendiller.net> | ||
*/ | ||
public class HtmlTemplateGoToDeclarationHandler implements GotoDeclarationHandler { | ||
public PsiElement[] getGotoDeclarationTargets(PsiElement psiElement, int offset, Editor editor) { | ||
if (!Symfony2ProjectComponent.isEnabled(psiElement)) { | ||
return null; | ||
} | ||
|
||
Collection<PsiElement> targets = new ArrayList<>(); | ||
|
||
// <twig:a | ||
if (psiElement instanceof XmlToken && psiElement.getNode().getElementType() == XmlTokenType.XML_NAME && psiElement.getText().startsWith("twig:")) { | ||
String text = psiElement.getText(); | ||
if (!text.startsWith("twig:")) { | ||
return null; | ||
} | ||
|
||
|
||
int calulatedOffset = offset - psiElement.getTextRange().getStartOffset(); | ||
if (calulatedOffset < 0) { | ||
calulatedOffset = 5; | ||
} | ||
|
||
// <twig:a | ||
if (calulatedOffset > 5) { | ||
if (TwigHtmlCompletionUtil.getTwigNamespacePattern().accepts(psiElement)) { | ||
|
||
String componentName = StringUtils.stripStart(text, "twig:"); | ||
if (!componentName.isBlank()) { | ||
targets.addAll(UxUtil.getTwigComponentNameTargets(psiElement.getProject(), componentName)); | ||
} | ||
} | ||
} else { | ||
// <twig | ||
targets.addAll(UxUtil.getTwigComponentAllTargets(psiElement.getProject())); | ||
} | ||
} | ||
|
||
// <twig:Foo :message="" message=""> | ||
if (psiElement instanceof XmlToken) { | ||
PsiElement parent = psiElement.getParent(); | ||
if (parent.getNode().getElementType() == XmlElementType.XML_ATTRIBUTE) { | ||
if (parent.getParent() instanceof HtmlTag htmlTag && htmlTag.getName().startsWith("twig:")) { | ||
String text = psiElement.getText(); | ||
|
||
for (PhpClass phpClass : UxUtil.getTwigComponentNameTargets(psiElement.getProject(), htmlTag.getName().substring(5))) { | ||
Field fieldByName = phpClass.findFieldByName(StringUtils.stripStart(text, ":"), false); | ||
if (fieldByName != null) { | ||
targets.add(fieldByName); | ||
} | ||
} | ||
}; | ||
} | ||
} | ||
|
||
return targets.toArray(new PsiElement[0]); | ||
} | ||
} |
123 changes: 123 additions & 0 deletions
123
...n/java/fr/adrienbrault/idea/symfony2plugin/templating/TwigComponentHtmlTagExtensions.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
package fr.adrienbrault.idea.symfony2plugin.templating; | ||
|
||
import com.intellij.codeInsight.lookup.LookupElement; | ||
import com.intellij.codeInsight.lookup.LookupElementBuilder; | ||
import com.intellij.codeInspection.XmlSuppressionProvider; | ||
import com.intellij.openapi.project.Project; | ||
import com.intellij.openapi.util.TextRange; | ||
import com.intellij.psi.PsiElement; | ||
import com.intellij.psi.PsiFile; | ||
import com.intellij.psi.html.HtmlTag; | ||
import com.intellij.psi.impl.source.xml.SchemaPrefix; | ||
import com.intellij.psi.xml.XmlFile; | ||
import com.intellij.psi.xml.XmlTag; | ||
import com.intellij.psi.xml.XmlToken; | ||
import com.intellij.psi.xml.XmlTokenType; | ||
import com.intellij.xml.XmlExtension; | ||
import com.intellij.xml.XmlTagNameProvider; | ||
import com.jetbrains.twig.TwigFile; | ||
import fr.adrienbrault.idea.symfony2plugin.Symfony2Icons; | ||
import fr.adrienbrault.idea.symfony2plugin.templating.util.TwigUtil; | ||
import fr.adrienbrault.idea.symfony2plugin.util.UxUtil; | ||
import org.jetbrains.annotations.NotNull; | ||
import org.jetbrains.annotations.Nullable; | ||
|
||
import java.util.Collections; | ||
import java.util.List; | ||
|
||
/** | ||
* @author Daniel Espendiller <daniel@espendiller.net> | ||
*/ | ||
public class TwigComponentHtmlTagExtensions { | ||
public static class TwigTemplateTagNameProvider implements XmlTagNameProvider { | ||
@Override | ||
public void addTagNameVariants(List<LookupElement> elements, @NotNull XmlTag tag, String prefix) { | ||
PsiElement elementOnTwigViewProvider = TwigUtil.getElementOnTwigViewProvider(tag); | ||
|
||
if (elementOnTwigViewProvider != null && !(elementOnTwigViewProvider.getContainingFile() instanceof TwigFile)) { | ||
return; | ||
} | ||
|
||
for (String twigComponentName : UxUtil.getTwigComponentNames(tag.getProject())) { | ||
elements.add(LookupElementBuilder.create("twig:" + twigComponentName).withIcon(Symfony2Icons.SYMFONY)); | ||
} | ||
} | ||
} | ||
|
||
public static class TwigTemplateXmlExtension extends XmlExtension { | ||
@Override | ||
public boolean isAvailable(PsiFile file) { | ||
return true; | ||
} | ||
|
||
@Override | ||
public @NotNull List<TagInfo> getAvailableTagNames(@NotNull XmlFile file, @NotNull XmlTag context) { | ||
return Collections.emptyList(); | ||
} | ||
|
||
@Override | ||
public @Nullable SchemaPrefix getPrefixDeclaration(XmlTag context, String namespacePrefix) { | ||
if (namespacePrefix.equals("twig") && context instanceof HtmlTag && context.getName().startsWith("twig")) { | ||
return new NullableParentShouldOverwriteSchemaPrefix( | ||
context.getProject(), | ||
context.getContainingFile(), | ||
new TextRange(0, 4), | ||
"twig" | ||
); | ||
} | ||
|
||
return null; | ||
} | ||
|
||
private static class NullableParentShouldOverwriteSchemaPrefix extends SchemaPrefix { | ||
private final Project project; | ||
private final PsiFile psiFile; | ||
|
||
public NullableParentShouldOverwriteSchemaPrefix(@NotNull Project project, @NotNull PsiFile psiFile, TextRange range, String name) { | ||
super(null, range, name); | ||
this.project = project; | ||
this.psiFile = psiFile; | ||
} | ||
|
||
@Override | ||
public PsiFile getContainingFile() { | ||
return this.psiFile; | ||
} | ||
|
||
@Override | ||
public @NotNull Project getProject() { | ||
return this.project; | ||
} | ||
} | ||
} | ||
|
||
public static class TwigTemplateXmlSuppressionProvider extends XmlSuppressionProvider { | ||
|
||
@Override | ||
public boolean isProviderAvailable(@NotNull PsiFile file) { | ||
return true; | ||
} | ||
|
||
@Override | ||
public boolean isSuppressedFor(@NotNull PsiElement element, @NotNull String inspectionId) { | ||
if (inspectionId.equals("HtmlUnknownTag") && element instanceof XmlToken xmlToken && element.getNode().getElementType() == XmlTokenType.XML_NAME) { | ||
String text = xmlToken.getText(); | ||
|
||
return text.startsWith("twig:") | ||
&& UxUtil.getTwigComponentNames(element.getProject()).contains(text.substring(5)); | ||
} | ||
|
||
return false; | ||
} | ||
|
||
@Override | ||
public void suppressForFile(@NotNull PsiElement element, @NotNull String inspectionId) { | ||
|
||
} | ||
|
||
@Override | ||
public void suppressForTag(@NotNull PsiElement element, @NotNull String inspectionId) { | ||
|
||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.