diff --git a/resources/META-INF/plugin.xml b/resources/META-INF/plugin.xml index 11e9c1026..8af540006 100644 --- a/resources/META-INF/plugin.xml +++ b/resources/META-INF/plugin.xml @@ -125,6 +125,7 @@ + diff --git a/src/com/magento/idea/magento2plugin/completion/provider/UiComponentCompletionProvider.java b/src/com/magento/idea/magento2plugin/completion/provider/UiComponentCompletionProvider.java new file mode 100644 index 000000000..0091aae2b --- /dev/null +++ b/src/com/magento/idea/magento2plugin/completion/provider/UiComponentCompletionProvider.java @@ -0,0 +1,42 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +package com.magento.idea.magento2plugin.completion.provider; + +import com.intellij.codeInsight.completion.CompletionParameters; +import com.intellij.codeInsight.completion.CompletionProvider; +import com.intellij.codeInsight.completion.CompletionResultSet; +import com.intellij.codeInsight.lookup.LookupElementBuilder; +import com.intellij.psi.PsiElement; +import com.intellij.psi.xml.XmlFile; +import com.intellij.util.ProcessingContext; +import com.jetbrains.php.PhpIcons; +import com.magento.idea.magento2plugin.indexes.UIComponentIndex; +import java.util.List; +import org.jetbrains.annotations.NotNull; + + +public class UiComponentCompletionProvider extends CompletionProvider { + + @Override + protected void addCompletions(@NotNull final CompletionParameters parameters, + final ProcessingContext context, + @NotNull final CompletionResultSet result) { + final PsiElement position = parameters.getPosition().getOriginalElement(); + if (position == null) { + return; + } + + final List targets = UIComponentIndex.getUiComponentFiles(position.getProject()); + if (!targets.isEmpty()) { + for (final XmlFile file : targets) { + result.addElement(LookupElementBuilder + .create(file.getVirtualFile().getNameWithoutExtension()) + .withIcon(PhpIcons.XML_TAG_ICON) + ); + } + } + } +} diff --git a/src/com/magento/idea/magento2plugin/completion/xml/XmlCompletionContributor.java b/src/com/magento/idea/magento2plugin/completion/xml/XmlCompletionContributor.java index 9e4b53b8c..cc2e5e651 100644 --- a/src/com/magento/idea/magento2plugin/completion/xml/XmlCompletionContributor.java +++ b/src/com/magento/idea/magento2plugin/completion/xml/XmlCompletionContributor.java @@ -88,6 +88,13 @@ public XmlCompletionContributor() { new FilePathCompletionProvider() ); + // + extend(CompletionType.BASIC, psiElement(XmlTokenType.XML_ATTRIBUTE_VALUE_TOKEN) + .inside(XmlPatterns.xmlAttribute().withName(LayoutXml.NAME_ATTRIBUTE) + .withParent(XmlPatterns.xmlTag().withName(LayoutXml.UI_COMPONENT_TAG_NAME))), + new UiComponentCompletionProvider() + ); + extend(CompletionType.BASIC, psiElement(XmlTokenType.XML_DATA_CHARACTERS) .withParent(XmlPatterns.xmlText().withParent( XmlPatterns.xmlTag().withName(UiComponentXml.XML_TAG_ITEM).withChild( @@ -126,20 +133,20 @@ public XmlCompletionContributor() { // extend(CompletionType.BASIC, psiElement(XmlTokenType.XML_ATTRIBUTE_VALUE_TOKEN) - .inside(XmlPatterns.xmlAttribute().withName(ModuleEventsXml.INSTANCE_ATTRIBUTE) - .withParent(XmlPatterns.xmlTag().withName(ModuleEventsXml.OBSERVER_TAG) - ) - ).inFile(xmlFile().withName(string().matches(ModuleEventsXml.FILE_NAME))), - new PhpClassCompletionProvider() + .inside(XmlPatterns.xmlAttribute().withName(ModuleEventsXml.INSTANCE_ATTRIBUTE) + .withParent(XmlPatterns.xmlTag().withName(ModuleEventsXml.OBSERVER_TAG) + ) + ).inFile(xmlFile().withName(string().matches(ModuleEventsXml.FILE_NAME))), + new PhpClassCompletionProvider() ); // extend(CompletionType.BASIC, psiElement(XmlTokenType.XML_ATTRIBUTE_VALUE_TOKEN) - .inside(XmlPatterns.xmlAttribute().withName(CommonXml.ATTR_INSTANCE) - .withParent(XmlPatterns.xmlTag().withName(CrontabXmlTemplate.CRON_JOB_TAG) - ) - ).inFile(xmlFile().withName(string().matches(CrontabXmlTemplate.FILE_NAME))), - new PhpClassCompletionProvider() + .inside(XmlPatterns.xmlAttribute().withName(CommonXml.ATTR_INSTANCE) + .withParent(XmlPatterns.xmlTag().withName(CrontabXmlTemplate.CRON_JOB_TAG) + ) + ).inFile(xmlFile().withName(string().matches(CrontabXmlTemplate.FILE_NAME))), + new PhpClassCompletionProvider() ); // php class completion in system.xml files. diff --git a/src/com/magento/idea/magento2plugin/indexes/UIComponentIndex.java b/src/com/magento/idea/magento2plugin/indexes/UIComponentIndex.java new file mode 100644 index 000000000..9f5f80ccf --- /dev/null +++ b/src/com/magento/idea/magento2plugin/indexes/UIComponentIndex.java @@ -0,0 +1,136 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +package com.magento.idea.magento2plugin.indexes; + +import com.intellij.ide.highlighter.XmlFileType; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiManager; +import com.intellij.psi.search.FilenameIndex; +import com.intellij.psi.xml.XmlFile; +import com.intellij.util.indexing.FileBasedIndex; +import com.intellij.util.indexing.ID; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import org.jetbrains.annotations.Nullable; + +@SuppressWarnings("PMD") +public final class UIComponentIndex { + + private UIComponentIndex() { + throw new AssertionError("Instantiating utility class..."); + } + + /** + * Available ui component file. + * + * @param virtualFile VirtualFile + * @return boolean + */ + public static boolean isUiComponentFile(final VirtualFile virtualFile) { + final VirtualFile parent = virtualFile.getParent(); + return virtualFile.getFileType() == XmlFileType.INSTANCE && parent.isDirectory() + && parent.getName().endsWith("ui_component"); + } + + /** + * Get ui component files. + * + * @param project Project + * @param fileName String + * @return List + */ + public static List getUiComponentFiles( + final Project project, + final @Nullable String fileName + ) { + final List results = new ArrayList();//NOPMD + final Collection xmlFiles = FilenameIndex.getAllFilesByExt(project, "xml"); + + final PsiManager psiManager = PsiManager.getInstance(project); + for (final VirtualFile xmlFile: xmlFiles) { + if (isUiComponentFile(xmlFile)) { + if (fileName != null && !xmlFile.getNameWithoutExtension().equals(fileName)) { + continue; + } + + final PsiFile file = psiManager.findFile(xmlFile); + if (file != null) { + results.add((XmlFile)file); + } + } + } + + return results; + } + + /** + * Get ui component files. + * + * @param project Project + * @return List + */ + public static List getUiComponentFiles(final Project project) { + return getUiComponentFiles(project, null); + } + + /** + * Get All Keys. + * + * @param identifier ID + * @param project Project + * @return Collection + */ + public static Collection getAllKeys( + final ID identifier, + final Project project + ) { + return FileBasedIndex.getInstance().getAllKeys(identifier, project); + } + + /** + * Get ui component files. + * + * @param project Project + * @param fileName String + * @return List + */ + public static List getUIComponentFiles(Project project, @Nullable String fileName) { + List results = new ArrayList(); + Collection xmlFiles = FilenameIndex.getAllFilesByExt(project, "xml"); + + PsiManager psiManager = PsiManager.getInstance(project); + for (VirtualFile xmlFile: xmlFiles) { + if (isUIComponentFile(xmlFile)) { + if (fileName != null && !xmlFile.getNameWithoutExtension().equals(fileName)) { + continue; + } + + PsiFile file = psiManager.findFile(xmlFile); + if (file != null) { + results.add((XmlFile)file); + } + } + } + + return results; + } + + /** + * Available ui component file. + * + * @param virtualFile VirtualFile + * @return boolean + */ + public static boolean isUIComponentFile(VirtualFile virtualFile) { + VirtualFile parent = virtualFile.getParent(); + return virtualFile.getFileType() == XmlFileType.INSTANCE && parent.isDirectory() + && parent.getName().endsWith("ui_component"); + } + +} diff --git a/src/com/magento/idea/magento2plugin/reference/provider/UIComponentReferenceProvider.java b/src/com/magento/idea/magento2plugin/reference/provider/UIComponentReferenceProvider.java new file mode 100644 index 000000000..9e9a95b9d --- /dev/null +++ b/src/com/magento/idea/magento2plugin/reference/provider/UIComponentReferenceProvider.java @@ -0,0 +1,38 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +package com.magento.idea.magento2plugin.reference.provider; + +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiReference; +import com.intellij.psi.PsiReferenceProvider; +import com.intellij.psi.xml.XmlFile; +import com.intellij.util.ProcessingContext; +import com.magento.idea.magento2plugin.indexes.UIComponentIndex; +import com.magento.idea.magento2plugin.reference.xml.PolyVariantReferenceBase; +import java.util.List; +import org.jetbrains.annotations.NotNull; + + +public class UIComponentReferenceProvider extends PsiReferenceProvider { + + @NotNull + @Override + public PsiReference[] getReferencesByElement( + @NotNull final PsiElement element, + @NotNull final ProcessingContext context + ) { + final String value = StringUtil.unquoteString(element.getText()); + final List targets = UIComponentIndex.getUIComponentFiles( + element.getProject(), + value + ); + if (!targets.isEmpty()) { + return new PsiReference[] {new PolyVariantReferenceBase(element, targets)}; + } + return PsiReference.EMPTY_ARRAY; + } +} diff --git a/src/com/magento/idea/magento2plugin/reference/xml/XmlReferenceContributor.java b/src/com/magento/idea/magento2plugin/reference/xml/XmlReferenceContributor.java index c4bde6ca0..c29142ca4 100644 --- a/src/com/magento/idea/magento2plugin/reference/xml/XmlReferenceContributor.java +++ b/src/com/magento/idea/magento2plugin/reference/xml/XmlReferenceContributor.java @@ -132,6 +132,16 @@ public void registerReferenceProviders(@NotNull PsiReferenceRegistrar registrar) new LayoutUpdateReferenceProvider() ); + // + registrar.registerReferenceProvider( + XmlPatterns.xmlAttributeValue().withParent( + XmlPatterns.xmlAttribute().withName("name").withParent( + XmlPatterns.xmlTag().withName("uiComponent") + ) + ), + new UIComponentReferenceProvider() + ); + // registrar.registerReferenceProvider( XmlPatterns.xmlAttributeValue().withParent( diff --git a/src/com/magento/idea/magento2plugin/stubs/indexes/xml/UIComponentIndex.java b/src/com/magento/idea/magento2plugin/stubs/indexes/xml/UIComponentIndex.java new file mode 100644 index 000000000..f08623465 --- /dev/null +++ b/src/com/magento/idea/magento2plugin/stubs/indexes/xml/UIComponentIndex.java @@ -0,0 +1,87 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +package com.magento.idea.magento2plugin.stubs.indexes.xml; + +import com.intellij.ide.highlighter.XmlFileType; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiFile; +import com.intellij.util.indexing.DataIndexer; +import com.intellij.util.indexing.FileBasedIndex; +import com.intellij.util.indexing.FileContent; +import com.intellij.util.indexing.ID; +import com.intellij.util.indexing.ScalarIndexExtension; +import com.intellij.util.io.EnumeratorStringDescriptor; +import com.intellij.util.io.KeyDescriptor; +import com.intellij.util.xml.impl.DomApplicationComponent; +import com.magento.idea.magento2plugin.project.Settings; +import java.util.HashMap; +import java.util.Map; +import org.jetbrains.annotations.NotNull; + + +public class UIComponentIndex extends ScalarIndexExtension { + + public static final ID KEY = + ID.create("com.magento.idea.magento2plugin.stubs.indexes.ui_component"); + + @NotNull + @Override + public ID getName() { + return KEY; + } + + /** + * Indexer for `ui_component` files. + * + * @return this + */ + @NotNull + @Override + public DataIndexer getIndexer() { + return inputData -> { + final Map map = new HashMap<>();//NOPMD + + final PsiFile psiFile = inputData.getPsiFile(); + if (!Settings.isEnabled(psiFile.getProject())) { + return map; + } + + final VirtualFile file = inputData.getFile(); + if (!file.getPath().matches("uiComponent")) { + return map; + } + + final String key = file.getName(); + if (!key.isEmpty()) { + map.put(key, null); + } + + return map; + }; + } + + @NotNull + @Override + public KeyDescriptor getKeyDescriptor() { + return new EnumeratorStringDescriptor(); + } + + @NotNull + @Override + public FileBasedIndex.InputFilter getInputFilter() { + return file -> file.getFileType() == XmlFileType.INSTANCE; + } + + @Override + public boolean dependsOnFileContent() { + return true; + } + + @Override + public int getVersion() { + return DomApplicationComponent.getInstance().getCumulativeVersion(false); + } +}