From 499513d7bd236bfbdee49a1fd51887f5125d0651 Mon Sep 17 00:00:00 2001 From: bohdan-harniuk Date: Fri, 15 Apr 2022 21:38:26 +0300 Subject: [PATCH 1/3] 600: Developed configurations autocompletion --- resources/META-INF/plugin.xml | 10 +- .../xml/ConfigXmlTagNameProvider.java | 230 ++++++++++++++++++ .../xml/FieldNameCompletionProvider.java | 114 +++++++++ .../xml/GroupNameCompletionProvider.java | 114 +++++++++ .../xml/SectionNameCompletionProvider.java | 86 +++++++ .../xml/SystemConfigurationContributor.java | 130 ++++++++++ .../magento2plugin/indexes/IndexManager.java | 80 +++--- .../magento/files/ModuleConfigXmlFile.java | 5 + .../magento/files/ModuleSystemXmlFile.java | 6 + .../indexes/xml/SystemXmlFieldIndex.java | 106 ++++++++ .../indexes/xml/SystemXmlGroupIndex.java | 99 ++++++++ .../indexes/xml/SystemXmlSectionIndex.java | 99 ++++++++ .../xml/SystemConfigurationParserUtil.java | 185 ++++++++++++++ 13 files changed, 1225 insertions(+), 39 deletions(-) create mode 100644 src/com/magento/idea/magento2plugin/completion/provider/xml/ConfigXmlTagNameProvider.java create mode 100644 src/com/magento/idea/magento2plugin/completion/provider/xml/FieldNameCompletionProvider.java create mode 100644 src/com/magento/idea/magento2plugin/completion/provider/xml/GroupNameCompletionProvider.java create mode 100644 src/com/magento/idea/magento2plugin/completion/provider/xml/SectionNameCompletionProvider.java create mode 100644 src/com/magento/idea/magento2plugin/completion/xml/SystemConfigurationContributor.java create mode 100644 src/com/magento/idea/magento2plugin/stubs/indexes/xml/SystemXmlFieldIndex.java create mode 100644 src/com/magento/idea/magento2plugin/stubs/indexes/xml/SystemXmlGroupIndex.java create mode 100644 src/com/magento/idea/magento2plugin/stubs/indexes/xml/SystemXmlSectionIndex.java create mode 100644 src/com/magento/idea/magento2plugin/util/magento/xml/SystemConfigurationParserUtil.java diff --git a/resources/META-INF/plugin.xml b/resources/META-INF/plugin.xml index 86467690c..4837ddaf9 100644 --- a/resources/META-INF/plugin.xml +++ b/resources/META-INF/plugin.xml @@ -178,8 +178,11 @@ - - + + + + + @@ -209,6 +212,9 @@ + + + diff --git a/src/com/magento/idea/magento2plugin/completion/provider/xml/ConfigXmlTagNameProvider.java b/src/com/magento/idea/magento2plugin/completion/provider/xml/ConfigXmlTagNameProvider.java new file mode 100644 index 000000000..5bc913b52 --- /dev/null +++ b/src/com/magento/idea/magento2plugin/completion/provider/xml/ConfigXmlTagNameProvider.java @@ -0,0 +1,230 @@ +/* + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +package com.magento.idea.magento2plugin.completion.provider.xml; + +import static com.magento.idea.magento2plugin.magento.files.ModuleConfigXmlFile.FILE_NAME; +import static com.magento.idea.magento2plugin.magento.files.ModuleConfigXmlFile.ROOT_TAG_NAME; + +import com.intellij.codeInsight.lookup.LookupElement; +import com.intellij.openapi.project.Project; +import com.intellij.psi.xml.XmlFile; +import com.intellij.psi.xml.XmlTag; +import com.intellij.xml.XmlTagNameProvider; +import com.magento.idea.magento2plugin.project.Settings; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class ConfigXmlTagNameProvider implements XmlTagNameProvider { + + @Override + public void addTagNameVariants( + final List elements, + final @NotNull XmlTag tag, + final String prefix + ) { + if (!Settings.isEnabled(tag.getProject()) || !validate(tag)) { + return; + } + final int tagDepth = getTagDepth(tag); + + if (tagDepth == -1) { + return; + } + final ConfigTagScope tagScope = getTagScope(tag); + + if (tagScope == null) { + return; + } + final ConfigTagTypeByScopedDepth configType = ConfigTagTypeByScopedDepth + .getTagTypeByScopeAndDepth(tagDepth, tagScope); + + if (configType == null) { + return; + } + final String indexKey = configType.getConfigPath(tag); + final String phrase = indexKey.isEmpty() ? prefix : indexKey + "." + prefix; + + elements.addAll(configType.makeCompletion(phrase, tag.getProject())); + } + + private boolean validate(final @NotNull XmlTag tag) { + if (!(tag.getContainingFile() instanceof XmlFile)) { + return false; + } + final XmlFile file = (XmlFile) tag.getContainingFile(); + final XmlTag rootTag = file.getRootTag(); + + if (!FILE_NAME.equals(file.getName()) + || rootTag == null + || !ROOT_TAG_NAME.equals(rootTag.getName())) { + return false; + } + final int tagDepth = getTagDepth(tag); + + return tagDepth >= 2 && tagDepth <= 5; + } + + private int getTagDepth(final @NotNull XmlTag tag) { + if (!(tag.getContainingFile() instanceof XmlFile)) { + return -1; + } + final XmlFile file = (XmlFile) tag.getContainingFile(); + final XmlTag rootTag = file.getRootTag(); + + if (rootTag == null) { + return -1; + } + int depth = 0; + XmlTag iterable = tag; + + while (!iterable.equals(rootTag)) { + depth++; + iterable = iterable.getParentTag(); + + if (iterable == null) { + break; + } + } + + return depth; + } + + private @Nullable ConfigTagScope getTagScope(final @NotNull XmlTag tag) { + if (!(tag.getContainingFile() instanceof XmlFile)) { + return null; + } + final XmlFile file = (XmlFile) tag.getContainingFile(); + final XmlTag rootTag = file.getRootTag(); + + if (rootTag == null) { + return null; + } + XmlTag scopeTag = null; + XmlTag iterable = tag; + + while (!iterable.equals(rootTag)) { + scopeTag = iterable; + iterable = iterable.getParentTag(); + + if (iterable == null) { + break; + } + } + + if (scopeTag == null) { + return null; + } + + return ConfigTagScope.getScopeByTagName(scopeTag.getName()); + } + + private enum ConfigTagTypeByScopedDepth { + + SECTION(2, 3, 3, 0), + GROUP(3, 4, 4, 1), + FIELD(4, 5, 5, 2); + + private final int defaultScope; + private final int websitesScope; + private final int storesScope; + private final int fallbackDepth; + + ConfigTagTypeByScopedDepth( + final int defaultScope, + final int websitesScope, + final int storesScope, + final int fallbackDepth + ) { + this.defaultScope = defaultScope; + this.websitesScope = websitesScope; + this.storesScope = storesScope; + this.fallbackDepth = fallbackDepth; + } + + public static ConfigTagTypeByScopedDepth getTagTypeByScopeAndDepth( + final int depth, + final ConfigTagScope scope + ) { + for (final ConfigTagTypeByScopedDepth tagType : ConfigTagTypeByScopedDepth.values()) { + int depthForScope = -1; + + if (ConfigTagScope.DEFAULT.equals(scope)) { + depthForScope = tagType.defaultScope; + } else if (ConfigTagScope.WEBSITES.equals(scope)) { + depthForScope = tagType.websitesScope; + } else if (ConfigTagScope.STORES.equals(scope)) { + depthForScope = tagType.storesScope; + } + + if (depth == depthForScope) { + return tagType; + } + } + + return null; + } + + public @NotNull String getConfigPath(final @NotNull XmlTag tag) { + final List parts = new ArrayList<>(); + XmlTag iterable = tag.getParentTag(); + + for (int i = 0; i < fallbackDepth; i++) { + if (iterable != null) { + parts.add(iterable.getName()); + iterable = iterable.getParentTag(); + } + } + + if (parts.isEmpty()) { + return ""; + } + Collections.reverse(parts); + + return String.join(".", parts); + } + + public List makeCompletion( + final @NotNull String phrase, + final @NotNull Project project + ) { + if (this.equals(ConfigTagTypeByScopedDepth.SECTION)) { + return SectionNameCompletionProvider.makeCompletion(phrase, project); + } else if (this.equals(ConfigTagTypeByScopedDepth.GROUP)) { + return GroupNameCompletionProvider.makeCompletion(phrase, project); + } else if (this.equals(ConfigTagTypeByScopedDepth.FIELD)) { + return FieldNameCompletionProvider.makeCompletion(phrase, project); + } + + return new ArrayList<>(); + } + } + + private enum ConfigTagScope { + + DEFAULT("default"), + WEBSITES("websites"), + STORES("stores"); + + private final String scopeTagName; + + ConfigTagScope(final String scopeTagName) { + this.scopeTagName = scopeTagName; + } + + public static ConfigTagScope getScopeByTagName(final @NotNull String tagName) { + for (final ConfigTagScope scope : ConfigTagScope.values()) { + if (tagName.equals(scope.scopeTagName)) { + return scope; + } + } + + return null; + } + } +} diff --git a/src/com/magento/idea/magento2plugin/completion/provider/xml/FieldNameCompletionProvider.java b/src/com/magento/idea/magento2plugin/completion/provider/xml/FieldNameCompletionProvider.java new file mode 100644 index 000000000..08c1e3e6c --- /dev/null +++ b/src/com/magento/idea/magento2plugin/completion/provider/xml/FieldNameCompletionProvider.java @@ -0,0 +1,114 @@ +/* + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +package com.magento.idea.magento2plugin.completion.provider.xml; + +import com.intellij.codeInsight.completion.CompletionParameters; +import com.intellij.codeInsight.completion.CompletionProvider; +import com.intellij.codeInsight.completion.CompletionResultSet; +import com.intellij.codeInsight.lookup.LookupElement; +import com.intellij.codeInsight.lookup.LookupElementBuilder; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiElement; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.xml.XmlTag; +import com.intellij.util.ProcessingContext; +import com.intellij.util.indexing.FileBasedIndex; +import com.magento.idea.magento2plugin.MagentoIcons; +import com.magento.idea.magento2plugin.project.Settings; +import com.magento.idea.magento2plugin.stubs.indexes.xml.SystemXmlFieldIndex; +import com.magento.idea.magento2plugin.util.magento.xml.SystemConfigurationParserUtil; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import org.jetbrains.annotations.NotNull; + +public class FieldNameCompletionProvider extends CompletionProvider { + + @Override + protected void addCompletions( + final @NotNull CompletionParameters parameters, + final @NotNull ProcessingContext context, + final @NotNull CompletionResultSet result + ) { + final PsiElement position = parameters.getPosition().getOriginalElement(); + + if (position == null || !Settings.isEnabled(position.getProject())) { + return; + } + final String prefix = result.getPrefixMatcher().getPrefix().trim(); + + final XmlTag startingTag = PsiTreeUtil.getParentOfType( + position, + XmlTag.class + ); + + if (startingTag == null) { + return; + } + final String currentPath = SystemConfigurationParserUtil + .parseOuterConfigPath( + startingTag, + SystemConfigurationParserUtil.ParsingDepth.GROUP_ID + ); + + if (currentPath == null) { + return; + } + final String capture = currentPath + "." + prefix; + + for (final LookupElement element : makeCompletion(capture, position.getProject())) { + result.addElement(element); + } + } + + /** + * Make completion for fields. + * + * @param phrase String + * @param project Project + * + * @return List[LookupElement] + */ + public static List makeCompletion( + final @NotNull String phrase, + final @NotNull Project project + ) { + final List result = new ArrayList<>(); + final FileBasedIndex index = FileBasedIndex.getInstance(); + final Collection allKeys = index.getAllKeys(SystemXmlFieldIndex.KEY, project); + final AtomicInteger resultsCounter = new AtomicInteger(0); + + allKeys.stream() + .filter(input -> input.startsWith(phrase) && !input.equals(phrase)) + .takeWhile(input -> resultsCounter.get() < 5) + .forEach( + input -> { + if (index.getValues( + SystemXmlFieldIndex.KEY, + input, + GlobalSearchScope.allScope(project)).isEmpty() + ) { + return; + } + final String[] pathParts = input.split("\\."); + + if (pathParts.length != 3) { + return; + } + final String fieldId = pathParts[2]; + result.add( + LookupElementBuilder.create(fieldId) + .withIcon(MagentoIcons.MODULE) + ); + resultsCounter.incrementAndGet(); + } + ); + + return result; + } +} diff --git a/src/com/magento/idea/magento2plugin/completion/provider/xml/GroupNameCompletionProvider.java b/src/com/magento/idea/magento2plugin/completion/provider/xml/GroupNameCompletionProvider.java new file mode 100644 index 000000000..b0fb41af2 --- /dev/null +++ b/src/com/magento/idea/magento2plugin/completion/provider/xml/GroupNameCompletionProvider.java @@ -0,0 +1,114 @@ +/* + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +package com.magento.idea.magento2plugin.completion.provider.xml; + +import com.intellij.codeInsight.completion.CompletionParameters; +import com.intellij.codeInsight.completion.CompletionProvider; +import com.intellij.codeInsight.completion.CompletionResultSet; +import com.intellij.codeInsight.lookup.LookupElement; +import com.intellij.codeInsight.lookup.LookupElementBuilder; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiElement; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.xml.XmlTag; +import com.intellij.util.ProcessingContext; +import com.intellij.util.indexing.FileBasedIndex; +import com.magento.idea.magento2plugin.MagentoIcons; +import com.magento.idea.magento2plugin.project.Settings; +import com.magento.idea.magento2plugin.stubs.indexes.xml.SystemXmlGroupIndex; +import com.magento.idea.magento2plugin.util.magento.xml.SystemConfigurationParserUtil; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import org.jetbrains.annotations.NotNull; + +public class GroupNameCompletionProvider extends CompletionProvider { + + @Override + protected void addCompletions( + final @NotNull CompletionParameters parameters, + final @NotNull ProcessingContext context, + final @NotNull CompletionResultSet result + ) { + final PsiElement position = parameters.getPosition().getOriginalElement(); + + if (position == null || !Settings.isEnabled(position.getProject())) { + return; + } + final String prefix = result.getPrefixMatcher().getPrefix().trim(); + final XmlTag startingTag = PsiTreeUtil.getParentOfType( + position, + XmlTag.class + ); + if (startingTag == null) { + return; + } + final String currentPath = SystemConfigurationParserUtil + .parseOuterConfigPath( + startingTag, + SystemConfigurationParserUtil.ParsingDepth.SECTION_ID + ); + + if (currentPath == null) { + return; + } + final String capture = currentPath + "." + prefix; + + for (final LookupElement element : makeCompletion(capture, position.getProject())) { + result.addElement(element); + } + } + + /** + * Make completion for groups. + * + * @param phrase String + * @param project Project + * + * @return List[LookupElement] + */ + public static List makeCompletion( + final @NotNull String phrase, + final @NotNull Project project + ) { + final List result = new ArrayList<>(); + final Collection allKeys = FileBasedIndex.getInstance().getAllKeys( + SystemXmlGroupIndex.KEY, + project + ); + final AtomicInteger resultsCounter = new AtomicInteger(0); + + allKeys.stream() + .filter(input -> input.startsWith(phrase) && !input.equals(phrase)) + .takeWhile(input -> resultsCounter.get() < 5) + .forEach( + input -> { + if (FileBasedIndex.getInstance().getValues( + SystemXmlGroupIndex.KEY, + input, + GlobalSearchScope.allScope(project)).isEmpty() + ) { + return; + } + final String[] groupNameParts = input.split("\\."); + + if (groupNameParts.length != 2) { + return; + } + final String groupId = groupNameParts[1]; + result.add( + LookupElementBuilder.create(groupId) + .withIcon(MagentoIcons.MODULE) + ); + resultsCounter.incrementAndGet(); + } + ); + + return result; + } +} diff --git a/src/com/magento/idea/magento2plugin/completion/provider/xml/SectionNameCompletionProvider.java b/src/com/magento/idea/magento2plugin/completion/provider/xml/SectionNameCompletionProvider.java new file mode 100644 index 000000000..2858b298c --- /dev/null +++ b/src/com/magento/idea/magento2plugin/completion/provider/xml/SectionNameCompletionProvider.java @@ -0,0 +1,86 @@ +/* + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +package com.magento.idea.magento2plugin.completion.provider.xml; + +import com.intellij.codeInsight.completion.CompletionParameters; +import com.intellij.codeInsight.completion.CompletionProvider; +import com.intellij.codeInsight.completion.CompletionResultSet; +import com.intellij.codeInsight.lookup.LookupElement; +import com.intellij.codeInsight.lookup.LookupElementBuilder; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiElement; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.util.ProcessingContext; +import com.intellij.util.indexing.FileBasedIndex; +import com.magento.idea.magento2plugin.MagentoIcons; +import com.magento.idea.magento2plugin.project.Settings; +import com.magento.idea.magento2plugin.stubs.indexes.xml.SystemXmlSectionIndex; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import org.jetbrains.annotations.NotNull; + +public class SectionNameCompletionProvider extends CompletionProvider { + + @Override + protected void addCompletions( + final @NotNull CompletionParameters parameters, + final @NotNull ProcessingContext context, + final @NotNull CompletionResultSet result + ) { + final PsiElement position = parameters.getPosition().getOriginalElement(); + + if (position == null || !Settings.isEnabled(position.getProject())) { + return; + } + final String prefix = result.getPrefixMatcher().getPrefix().trim(); + + for (final LookupElement element : makeCompletion(prefix, position.getProject())) { + result.addElement(element); + } + } + + /** + * Make completion for sections. + * + * @param phrase String + * @param project Project + * + * @return List[LookupElement] + */ + public static List makeCompletion( + final @NotNull String phrase, + final @NotNull Project project + ) { + final List result = new ArrayList<>(); + final Collection allKeys = FileBasedIndex.getInstance().getAllKeys( + SystemXmlSectionIndex.KEY, + project + ); + final AtomicInteger resultsCounter = new AtomicInteger(0); + + allKeys.stream() + .filter(input -> input.startsWith(phrase) && !input.equals(phrase)) + .takeWhile(input -> resultsCounter.get() < 5) + .forEach( + input -> { + if (FileBasedIndex.getInstance().getValues( + SystemXmlSectionIndex.KEY, + input, + GlobalSearchScope.allScope(project)).isEmpty() + ) { + return; + } + result.add(LookupElementBuilder + .create(input).withIcon(MagentoIcons.MODULE)); + resultsCounter.incrementAndGet(); + } + ); + + return result; + } +} diff --git a/src/com/magento/idea/magento2plugin/completion/xml/SystemConfigurationContributor.java b/src/com/magento/idea/magento2plugin/completion/xml/SystemConfigurationContributor.java new file mode 100644 index 000000000..5dac34f57 --- /dev/null +++ b/src/com/magento/idea/magento2plugin/completion/xml/SystemConfigurationContributor.java @@ -0,0 +1,130 @@ +/* + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +package com.magento.idea.magento2plugin.completion.xml; + +import com.intellij.codeInsight.completion.CompletionContributor; +import com.intellij.codeInsight.completion.CompletionType; +import com.intellij.patterns.PlatformPatterns; +import com.intellij.patterns.PsiElementPattern; +import com.intellij.patterns.StandardPatterns; +import com.intellij.patterns.XmlFilePattern; +import com.intellij.patterns.XmlNamedElementPattern; +import com.intellij.patterns.XmlPatterns; +import com.intellij.patterns.XmlTagPattern; +import com.intellij.psi.PsiElement; +import com.intellij.psi.xml.XmlTokenType; +import com.magento.idea.magento2plugin.completion.provider.xml.FieldNameCompletionProvider; +import com.magento.idea.magento2plugin.completion.provider.xml.GroupNameCompletionProvider; +import com.magento.idea.magento2plugin.completion.provider.xml.SectionNameCompletionProvider; +import com.magento.idea.magento2plugin.magento.files.ModuleConfigXmlFile; +import com.magento.idea.magento2plugin.magento.files.ModuleSystemXmlFile; + +public class SystemConfigurationContributor extends CompletionContributor { + + /** + * Contributes completions to the system.xml and config.xml files. + */ + public SystemConfigurationContributor() { + final XmlFilePattern.Capture systemXmlFileCapture = XmlPatterns + .xmlFile() + .withName(StandardPatterns.string().endsWith(ModuleSystemXmlFile.FILE_NAME)); + final XmlFilePattern.Capture configXmlFileCapture = XmlPatterns + .xmlFile() + .withName(StandardPatterns.string().endsWith(ModuleConfigXmlFile.FILE_NAME)); + final XmlNamedElementPattern.XmlAttributePattern idAttributeCapture = XmlPatterns + .xmlAttribute() + .withName("id"); + final XmlTagPattern.Capture sectionTagCapture = XmlPatterns + .xmlTag() + .withName(ModuleSystemXmlFile.SECTION_TAG_NAME); + final XmlTagPattern.Capture groupTagCapture = XmlPatterns + .xmlTag() + .withName(ModuleSystemXmlFile.GROUP_TAG_NAME); + final XmlTagPattern.Capture fieldTagCapture = XmlPatterns + .xmlTag() + .withName(ModuleSystemXmlFile.FIELD_TAG_NAME); + + //
in the system.xml file + extend( + CompletionType.BASIC, + PlatformPatterns.psiElement(XmlTokenType.XML_ATTRIBUTE_VALUE_TOKEN) + .inside(idAttributeCapture) + .inside(sectionTagCapture) + .inFile(systemXmlFileCapture) + .withSuperParent(3, sectionTagCapture), + new SectionNameCompletionProvider() + ); + + // in the system.xml file + extend( + CompletionType.BASIC, + PlatformPatterns.psiElement(XmlTokenType.XML_ATTRIBUTE_VALUE_TOKEN) + .inside(idAttributeCapture) + .inside(groupTagCapture) + .inside(sectionTagCapture) + .inFile(systemXmlFileCapture) + .withSuperParent(3, groupTagCapture), + new GroupNameCompletionProvider() + ); + + // in the system.xml file + extend( + CompletionType.BASIC, + PlatformPatterns.psiElement(XmlTokenType.XML_ATTRIBUTE_VALUE_TOKEN) + .inside(idAttributeCapture) + .inside(fieldTagCapture) + .inside(groupTagCapture) + .inside(sectionTagCapture) + .inFile(systemXmlFileCapture) + .withSuperParent(3, fieldTagCapture), + new FieldNameCompletionProvider() + ); + + final PsiElementPattern.Capture tagTextInConfigFile = PlatformPatterns + .psiElement(XmlTokenType.XML_DATA_CHARACTERS) + .inside(XmlPatterns.xmlTag().withName(ModuleConfigXmlFile.ROOT_TAG_NAME)) + .inFile(configXmlFileCapture); + final XmlTagPattern.Capture defaultScopeTag = XmlPatterns.xmlTag() + .withName(ModuleConfigXmlFile.DEFAULT_SCOPE); + final XmlTagPattern.Capture websiteScopeTag = XmlPatterns.xmlTag() + .withName(ModuleConfigXmlFile.WEBSITE_SCOPE); + final XmlTagPattern.Capture storeScopeTag = XmlPatterns.xmlTag() + .withName(ModuleConfigXmlFile.STORE_SCOPE); + + // for sections in the config.xml file + extend( + CompletionType.BASIC, + PlatformPatterns.or( + tagTextInConfigFile.withSuperParent(2, defaultScopeTag), + tagTextInConfigFile.withSuperParent(3, websiteScopeTag), + tagTextInConfigFile.withSuperParent(3, storeScopeTag) + ), + new SectionNameCompletionProvider() + ); + + // for groups in the config.xml file + extend( + CompletionType.BASIC, + PlatformPatterns.or( + tagTextInConfigFile.withSuperParent(3, defaultScopeTag), + tagTextInConfigFile.withSuperParent(4, websiteScopeTag), + tagTextInConfigFile.withSuperParent(4, storeScopeTag) + ), + new GroupNameCompletionProvider() + ); + + // for fields in the config.xml file + extend( + CompletionType.BASIC, + PlatformPatterns.or( + tagTextInConfigFile.withSuperParent(4, defaultScopeTag), + tagTextInConfigFile.withSuperParent(5, websiteScopeTag), + tagTextInConfigFile.withSuperParent(5, storeScopeTag) + ), + new FieldNameCompletionProvider() + ); + } +} diff --git a/src/com/magento/idea/magento2plugin/indexes/IndexManager.java b/src/com/magento/idea/magento2plugin/indexes/IndexManager.java index 61c05fca5..0d48528e1 100644 --- a/src/com/magento/idea/magento2plugin/indexes/IndexManager.java +++ b/src/com/magento/idea/magento2plugin/indexes/IndexManager.java @@ -30,6 +30,9 @@ import com.magento.idea.magento2plugin.stubs.indexes.xml.MenuIndex; import com.magento.idea.magento2plugin.stubs.indexes.xml.PhpClassNameIndex; import com.magento.idea.magento2plugin.stubs.indexes.xml.ProductTypeIndex; +import com.magento.idea.magento2plugin.stubs.indexes.xml.SystemXmlFieldIndex; +import com.magento.idea.magento2plugin.stubs.indexes.xml.SystemXmlGroupIndex; +import com.magento.idea.magento2plugin.stubs.indexes.xml.SystemXmlSectionIndex; import com.magento.idea.magento2plugin.stubs.indexes.xml.UIComponentIndex; @SuppressWarnings({"PMD.ClassNamingConventions", "PMD.UseUtilityClass"}) @@ -39,45 +42,48 @@ public class IndexManager { * Refresh Magento 2 indexes. */ public static void manualReindex() { - final ID[] indexIds = new ID[] {//NOPMD - // php - ModulePackageIndex.KEY, - // xml|di configuration - PluginIndex.KEY, - VirtualTypeIndex.KEY, - DeclarativeSchemaElementsIndex.KEY, - // layouts - BlockNameIndex.KEY, - ContainerNameIndex.KEY, - UIComponentIndex.KEY, - // events - EventNameIndex.KEY, - EventObserverIndex.KEY, - // webapi - WebApiTypeIndex.KEY, - ModuleNameIndex.KEY, - PhpClassNameIndex.KEY, - //acl - AclResourceIndex.KEY, - //menu - MenuIndex.KEY, - //require_js - RequireJsIndex.KEY, - MagentoLibJsIndex.KEY, - // mftf - ActionGroupIndex.KEY, - DataIndex.KEY, - PageIndex.KEY, - SectionIndex.KEY, - TestNameIndex.KEY, - TestExtendsIndex.KEY, - //graphql - GraphQlResolverIndex.KEY, - //product types - ProductTypeIndex.KEY + final ID[] indexIds = new ID[]{//NOPMD + // php + ModulePackageIndex.KEY, + // xml|di configuration + PluginIndex.KEY, + VirtualTypeIndex.KEY, + DeclarativeSchemaElementsIndex.KEY, + SystemXmlSectionIndex.KEY, + SystemXmlGroupIndex.KEY, + SystemXmlFieldIndex.KEY, + // layouts + BlockNameIndex.KEY, + ContainerNameIndex.KEY, + UIComponentIndex.KEY, + // events + EventNameIndex.KEY, + EventObserverIndex.KEY, + // webapi + WebApiTypeIndex.KEY, + ModuleNameIndex.KEY, + PhpClassNameIndex.KEY, + //acl + AclResourceIndex.KEY, + //menu + MenuIndex.KEY, + //require_js + RequireJsIndex.KEY, + MagentoLibJsIndex.KEY, + // mftf + ActionGroupIndex.KEY, + DataIndex.KEY, + PageIndex.KEY, + SectionIndex.KEY, + TestNameIndex.KEY, + TestExtendsIndex.KEY, + //graphql + GraphQlResolverIndex.KEY, + //product types + ProductTypeIndex.KEY }; - for (final ID id: indexIds) { + for (final ID id : indexIds) { try { FileBasedIndexImpl.getInstance().requestRebuild(id); FileBasedIndexImpl.getInstance().scheduleRebuild(id, new Throwable());//NOPMD diff --git a/src/com/magento/idea/magento2plugin/magento/files/ModuleConfigXmlFile.java b/src/com/magento/idea/magento2plugin/magento/files/ModuleConfigXmlFile.java index 2726ead9c..22768c73d 100644 --- a/src/com/magento/idea/magento2plugin/magento/files/ModuleConfigXmlFile.java +++ b/src/com/magento/idea/magento2plugin/magento/files/ModuleConfigXmlFile.java @@ -13,6 +13,11 @@ public final class ModuleConfigXmlFile implements ModuleFileInterface { public static final String FILE_NAME = "config.xml"; public static final String TEMPLATE = "Magento Config XML"; + public static final String ROOT_TAG_NAME = "config"; + public static final String DEFAULT_SCOPE = "default"; + public static final String STORE_SCOPE = "stores"; + public static final String WEBSITE_SCOPE = "websites"; + @Override public String getFileName() { return FILE_NAME; diff --git a/src/com/magento/idea/magento2plugin/magento/files/ModuleSystemXmlFile.java b/src/com/magento/idea/magento2plugin/magento/files/ModuleSystemXmlFile.java index 11017dd18..db7c6b807 100644 --- a/src/com/magento/idea/magento2plugin/magento/files/ModuleSystemXmlFile.java +++ b/src/com/magento/idea/magento2plugin/magento/files/ModuleSystemXmlFile.java @@ -18,6 +18,12 @@ public final class ModuleSystemXmlFile implements ModuleFileInterface { public static final String XML_TAG_FRONTEND_MODEL = "frontend_model"; public static final String XML_TAG_BACKEND_MODEL = "backend_model"; + public static final String ROOT_TAG_NAME = "config"; + public static final String SYSTEM_TAG_NAME = "system"; + public static final String SECTION_TAG_NAME = "section"; + public static final String GROUP_TAG_NAME = "group"; + public static final String FIELD_TAG_NAME = "field"; + @Override public String getFileName() { return FILE_NAME; diff --git a/src/com/magento/idea/magento2plugin/stubs/indexes/xml/SystemXmlFieldIndex.java b/src/com/magento/idea/magento2plugin/stubs/indexes/xml/SystemXmlFieldIndex.java new file mode 100644 index 000000000..85eb9450b --- /dev/null +++ b/src/com/magento/idea/magento2plugin/stubs/indexes/xml/SystemXmlFieldIndex.java @@ -0,0 +1,106 @@ +/* + * 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.psi.PsiFile; +import com.intellij.psi.xml.XmlDocument; +import com.intellij.psi.xml.XmlFile; +import com.intellij.psi.xml.XmlTag; +import com.intellij.util.indexing.DataIndexer; +import com.intellij.util.indexing.FileBasedIndex; +import com.intellij.util.indexing.FileBasedIndexExtension; +import com.intellij.util.indexing.FileContent; +import com.intellij.util.indexing.ID; +import com.intellij.util.io.DataExternalizer; +import com.intellij.util.io.EnumeratorStringDescriptor; +import com.intellij.util.io.KeyDescriptor; +import com.intellij.util.io.VoidDataExternalizer; +import com.magento.idea.magento2plugin.magento.files.ModuleSystemXmlFile; +import com.magento.idea.magento2plugin.project.Settings; +import com.magento.idea.magento2plugin.util.magento.xml.SystemConfigurationParserUtil; +import java.util.HashMap; +import java.util.Map; +import org.jetbrains.annotations.NotNull; + +public class SystemXmlFieldIndex extends FileBasedIndexExtension { + + public static final ID KEY = ID.create( + "com.magento.idea.magento2plugin.stubs.indexes.xml.SystemXmlFieldIndex" + ); + private final KeyDescriptor myKeyDescriptor = new EnumeratorStringDescriptor(); + private final DataExternalizer myDataExternalizer = new VoidDataExternalizer(); + + @Override + public @NotNull + DataIndexer getIndexer() { + return inputData -> { + final Map map = new HashMap<>(); + final PsiFile psiFile = inputData.getPsiFile(); + + if (!Settings.isEnabled(psiFile.getProject()) + || !(psiFile instanceof XmlFile) + || !ModuleSystemXmlFile.FILE_NAME.equals(psiFile.getName())) { + return map; + } + final XmlFile xmlFile = (XmlFile) psiFile; + final XmlDocument document = xmlFile.getDocument(); + + if (document == null) { + return map; + } + final XmlTag rootTag = xmlFile.getRootTag(); + + if (rootTag == null) { + return map; + } + + SystemConfigurationParserUtil.parseConfigurationTags( + rootTag, + SystemConfigurationParserUtil.ParsingDepth.FIELD_ID, + (sectionId, groupId, fieldId) -> { + if (sectionId == null || groupId == null || fieldId == null) { + return; + } + map.put(sectionId + "." + groupId + "." + fieldId, null); + } + ); + + return map; + }; + } + + @Override + public @NotNull FileBasedIndex.InputFilter getInputFilter() { + return virtualFile -> virtualFile.getFileType() == XmlFileType.INSTANCE + && virtualFile.getNameWithoutExtension().equals("system"); + } + + @Override + public @NotNull ID getName() { + return KEY; + } + + @Override + public @NotNull KeyDescriptor getKeyDescriptor() { + return myKeyDescriptor; + } + + @Override + public @NotNull DataExternalizer getValueExternalizer() { + return myDataExternalizer; + } + + @Override + public boolean dependsOnFileContent() { + return true; + } + + @Override + public int getVersion() { + return 1; + } +} diff --git a/src/com/magento/idea/magento2plugin/stubs/indexes/xml/SystemXmlGroupIndex.java b/src/com/magento/idea/magento2plugin/stubs/indexes/xml/SystemXmlGroupIndex.java new file mode 100644 index 000000000..668693d04 --- /dev/null +++ b/src/com/magento/idea/magento2plugin/stubs/indexes/xml/SystemXmlGroupIndex.java @@ -0,0 +1,99 @@ +/* + * 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.psi.PsiFile; +import com.intellij.psi.xml.XmlFile; +import com.intellij.psi.xml.XmlTag; +import com.intellij.util.indexing.DataIndexer; +import com.intellij.util.indexing.FileBasedIndex; +import com.intellij.util.indexing.FileBasedIndexExtension; +import com.intellij.util.indexing.FileContent; +import com.intellij.util.indexing.ID; +import com.intellij.util.io.DataExternalizer; +import com.intellij.util.io.EnumeratorStringDescriptor; +import com.intellij.util.io.KeyDescriptor; +import com.intellij.util.io.VoidDataExternalizer; +import com.magento.idea.magento2plugin.magento.files.ModuleSystemXmlFile; +import com.magento.idea.magento2plugin.project.Settings; +import com.magento.idea.magento2plugin.util.magento.xml.SystemConfigurationParserUtil; +import java.util.HashMap; +import java.util.Map; +import org.jetbrains.annotations.NotNull; + +public class SystemXmlGroupIndex extends FileBasedIndexExtension { + + public static final ID KEY = ID.create( + "com.magento.idea.magento2plugin.stubs.indexes.xml.SystemXmlGroupIndex" + ); + private final KeyDescriptor myKeyDescriptor = new EnumeratorStringDescriptor(); + private final DataExternalizer myDataExternalizer = new VoidDataExternalizer(); + + @Override + public @NotNull DataIndexer getIndexer() { + return inputData -> { + final Map map = new HashMap<>(); + final PsiFile psiFile = inputData.getPsiFile(); + + if (!Settings.isEnabled(psiFile.getProject()) + || !(psiFile instanceof XmlFile) + || !ModuleSystemXmlFile.FILE_NAME.equals(psiFile.getName())) { + return map; + } + final XmlFile xmlFile = (XmlFile) psiFile; + final XmlTag rootTag = xmlFile.getRootTag(); + + if (rootTag == null) { + return map; + } + + SystemConfigurationParserUtil.parseConfigurationTags( + rootTag, + SystemConfigurationParserUtil.ParsingDepth.GROUP_ID, + (sectionId, groupId, fieldId) -> { + if (sectionId == null || groupId == null) { + return; + } + map.put(sectionId + "." + groupId, null); + } + ); + + return map; + }; + } + + @Override + public @NotNull FileBasedIndex.InputFilter getInputFilter() { + return virtualFile -> virtualFile.getFileType() == XmlFileType.INSTANCE + && virtualFile.getNameWithoutExtension().equals("system"); + } + + @Override + public @NotNull ID getName() { + return KEY; + } + + @Override + public @NotNull KeyDescriptor getKeyDescriptor() { + return myKeyDescriptor; + } + + @Override + public @NotNull DataExternalizer getValueExternalizer() { + return myDataExternalizer; + } + + @Override + public boolean dependsOnFileContent() { + return true; + } + + @Override + public int getVersion() { + return 1; + } +} diff --git a/src/com/magento/idea/magento2plugin/stubs/indexes/xml/SystemXmlSectionIndex.java b/src/com/magento/idea/magento2plugin/stubs/indexes/xml/SystemXmlSectionIndex.java new file mode 100644 index 000000000..f11d2f6c2 --- /dev/null +++ b/src/com/magento/idea/magento2plugin/stubs/indexes/xml/SystemXmlSectionIndex.java @@ -0,0 +1,99 @@ +/* + * 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.psi.PsiFile; +import com.intellij.psi.xml.XmlFile; +import com.intellij.psi.xml.XmlTag; +import com.intellij.util.indexing.DataIndexer; +import com.intellij.util.indexing.FileBasedIndex; +import com.intellij.util.indexing.FileBasedIndexExtension; +import com.intellij.util.indexing.FileContent; +import com.intellij.util.indexing.ID; +import com.intellij.util.io.DataExternalizer; +import com.intellij.util.io.EnumeratorStringDescriptor; +import com.intellij.util.io.KeyDescriptor; +import com.intellij.util.io.VoidDataExternalizer; +import com.magento.idea.magento2plugin.magento.files.ModuleSystemXmlFile; +import com.magento.idea.magento2plugin.project.Settings; +import com.magento.idea.magento2plugin.util.magento.xml.SystemConfigurationParserUtil; +import java.util.HashMap; +import java.util.Map; +import org.jetbrains.annotations.NotNull; + +public class SystemXmlSectionIndex extends FileBasedIndexExtension { + + public static final ID KEY = ID.create( + "com.magento.idea.magento2plugin.stubs.indexes.xml.SystemXmlSectionIndex" + ); + private final KeyDescriptor myKeyDescriptor = new EnumeratorStringDescriptor(); + private final DataExternalizer myDataExternalizer = new VoidDataExternalizer(); + + @Override + public @NotNull DataIndexer getIndexer() { + return inputData -> { + final Map map = new HashMap<>(); + final PsiFile psiFile = inputData.getPsiFile(); + + if (!Settings.isEnabled(psiFile.getProject()) + || !(psiFile instanceof XmlFile) + || !ModuleSystemXmlFile.FILE_NAME.equals(psiFile.getName())) { + return map; + } + final XmlFile xmlFile = (XmlFile) psiFile; + final XmlTag rootTag = xmlFile.getRootTag(); + + if (rootTag == null) { + return map; + } + + SystemConfigurationParserUtil.parseConfigurationTags( + rootTag, + SystemConfigurationParserUtil.ParsingDepth.SECTION_ID, + (sectionId, groupId, fieldId) -> { + if (sectionId == null) { + return; + } + map.put(sectionId, null); + } + ); + + return map; + }; + } + + @Override + public @NotNull FileBasedIndex.InputFilter getInputFilter() { + return virtualFile -> virtualFile.getFileType() == XmlFileType.INSTANCE + && virtualFile.getNameWithoutExtension().equals("system"); + } + + @Override + public @NotNull ID getName() { + return KEY; + } + + @Override + public @NotNull KeyDescriptor getKeyDescriptor() { + return myKeyDescriptor; + } + + @Override + public @NotNull DataExternalizer getValueExternalizer() { + return myDataExternalizer; + } + + @Override + public boolean dependsOnFileContent() { + return true; + } + + @Override + public int getVersion() { + return 1; + } +} diff --git a/src/com/magento/idea/magento2plugin/util/magento/xml/SystemConfigurationParserUtil.java b/src/com/magento/idea/magento2plugin/util/magento/xml/SystemConfigurationParserUtil.java new file mode 100644 index 000000000..495abda2e --- /dev/null +++ b/src/com/magento/idea/magento2plugin/util/magento/xml/SystemConfigurationParserUtil.java @@ -0,0 +1,185 @@ +/* + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +package com.magento.idea.magento2plugin.util.magento.xml; + +import static com.magento.idea.magento2plugin.magento.files.ModuleSystemXmlFile.FIELD_TAG_NAME; +import static com.magento.idea.magento2plugin.magento.files.ModuleSystemXmlFile.GROUP_TAG_NAME; +import static com.magento.idea.magento2plugin.magento.files.ModuleSystemXmlFile.SECTION_TAG_NAME; +import static com.magento.idea.magento2plugin.magento.files.ModuleSystemXmlFile.SYSTEM_TAG_NAME; +import static com.magento.idea.magento2plugin.util.xml.XmlPsiTreeUtil.findSubTagsOfParent; + +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.xml.XmlTag; +import com.magento.idea.magento2plugin.magento.files.ModuleConfigXmlFile; +import com.magento.idea.magento2plugin.magento.files.ModuleSystemXmlFile; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +@SuppressWarnings("PMD.TooManyStaticImports") +public final class SystemConfigurationParserUtil { + + private SystemConfigurationParserUtil() {} + + /** + * Parse system configuration tags (section, group, field) with the specified depth. + * + * @param rootTag XmlTag + * @param parsingDepth ParsingDepth + * @param action RunnableOnPathElement + */ + public static void parseConfigurationTags( + final @NotNull XmlTag rootTag, + final ParsingDepth parsingDepth, + final RunnableOnPathElement action + ) { + final XmlTag systemTag = getSystemTag(rootTag); + + if (systemTag == null) { + return; + } + + for (final XmlTag sectionTag : findSubTagsOfParent(systemTag, SECTION_TAG_NAME)) { + if (ParsingDepth.SECTION_ID.equals(parsingDepth)) { + action.run(sectionTag.getAttributeValue("id"), null, null); + continue; + } + for (final XmlTag groupTag : findSubTagsOfParent(sectionTag, GROUP_TAG_NAME)) { + if (ParsingDepth.GROUP_ID.equals(parsingDepth)) { + action.run( + sectionTag.getAttributeValue("id"), + groupTag.getAttributeValue("id"), + null + ); + continue; + } + for (final XmlTag fieldTag : findSubTagsOfParent(groupTag, FIELD_TAG_NAME)) { + action.run( + sectionTag.getAttributeValue("id"), + groupTag.getAttributeValue("id"), + fieldTag.getAttributeValue("id") + ); + } + } + } + } + + /** + * Parse system configuration path from the specified xml tag. + * + * @param tag XmlTag + * @param startFrom ParsingDepth + * + * @return String + */ + public static @Nullable String parseOuterConfigPath( + final @NotNull XmlTag tag, + final @NotNull ParsingDepth startFrom + ) { + final String filename = tag.getContainingFile().getName(); + ParsingDepth currentDepth = startFrom; + XmlTag currentTag = tag; + final List parts = new ArrayList<>(); + + if (ModuleSystemXmlFile.FILE_NAME.equals(filename)) { + currentTag = PsiTreeUtil.getParentOfType( + currentTag, + XmlTag.class + ); + } + + while (currentDepth != null) { + if (currentTag == null) { + break; + } + if (ModuleSystemXmlFile.FILE_NAME.equals(filename)) { + final String identity = currentTag.getAttributeValue("id"); + + if (identity != null) { + parts.add(identity); + } + } else if (ModuleConfigXmlFile.FILE_NAME.equals(filename)) { + parts.add(currentTag.getName()); + } + currentDepth = ParsingDepth.getPrevious(currentDepth); + currentTag = PsiTreeUtil.getParentOfType( + currentTag, + XmlTag.class + ); + } + + if (parts.isEmpty()) { + return null; + } + Collections.reverse(parts); + + return String.join(".", parts); + } + + public enum ParsingDepth { + + SECTION_ID(0), + GROUP_ID(1), + FIELD_ID(2); + + private final int depth; + + /** + * Parsing depth ENUM constructor. + * + * @param depth int + */ + ParsingDepth(final int depth) { + this.depth = depth; + } + + /** + * Get ENUM value by its depth number. + * + * @param depth int + * + * @return ParsingDepth + */ + public static @Nullable ParsingDepth getByDepth(final int depth) { + for (final ParsingDepth parsingDepth : ParsingDepth.values()) { + if (parsingDepth.depth == depth) { + return parsingDepth; + } + } + + return null; + } + + /** + * Get previous ENUM value by its depth number. + * + * @param current ParsingDepth + * + * @return ParsingDepth + */ + public static @Nullable ParsingDepth getPrevious(final @NotNull ParsingDepth current) { + return getByDepth(current.depth - 1); + } + } + + public interface RunnableOnPathElement { + void run( + final @Nullable String sectionId, + final @Nullable String groupId, + final @Nullable String fieldId + ); + } + + private static @Nullable XmlTag getSystemTag(final @NotNull XmlTag rootTag) { + if (!ModuleSystemXmlFile.FILE_NAME.equals(rootTag.getContainingFile().getName())) { + return null; + } + + return rootTag.findFirstSubTag(SYSTEM_TAG_NAME); + } +} From 0add2b29256a1d017a8068b0ad5828bc6f098ec3 Mon Sep 17 00:00:00 2001 From: bohdan-harniuk Date: Fri, 15 Apr 2022 21:57:42 +0300 Subject: [PATCH 2/3] 600: Code refactoring --- .../completion/provider/xml/FieldNameCompletionProvider.java | 5 ++--- .../completion/provider/xml/GroupNameCompletionProvider.java | 4 ++-- .../completion/xml/SystemConfigurationContributor.java | 2 ++ 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/com/magento/idea/magento2plugin/completion/provider/xml/FieldNameCompletionProvider.java b/src/com/magento/idea/magento2plugin/completion/provider/xml/FieldNameCompletionProvider.java index 08c1e3e6c..a8c101901 100644 --- a/src/com/magento/idea/magento2plugin/completion/provider/xml/FieldNameCompletionProvider.java +++ b/src/com/magento/idea/magento2plugin/completion/provider/xml/FieldNameCompletionProvider.java @@ -40,8 +40,6 @@ protected void addCompletions( if (position == null || !Settings.isEnabled(position.getProject())) { return; } - final String prefix = result.getPrefixMatcher().getPrefix().trim(); - final XmlTag startingTag = PsiTreeUtil.getParentOfType( position, XmlTag.class @@ -59,6 +57,7 @@ protected void addCompletions( if (currentPath == null) { return; } + final String prefix = result.getPrefixMatcher().getPrefix().trim(); final String capture = currentPath + "." + prefix; for (final LookupElement element : makeCompletion(capture, position.getProject())) { @@ -97,7 +96,7 @@ public static List makeCompletion( } final String[] pathParts = input.split("\\."); - if (pathParts.length != 3) { + if (pathParts.length != 3) { //NOPMD return; } final String fieldId = pathParts[2]; diff --git a/src/com/magento/idea/magento2plugin/completion/provider/xml/GroupNameCompletionProvider.java b/src/com/magento/idea/magento2plugin/completion/provider/xml/GroupNameCompletionProvider.java index b0fb41af2..dd5ac371a 100644 --- a/src/com/magento/idea/magento2plugin/completion/provider/xml/GroupNameCompletionProvider.java +++ b/src/com/magento/idea/magento2plugin/completion/provider/xml/GroupNameCompletionProvider.java @@ -40,7 +40,6 @@ protected void addCompletions( if (position == null || !Settings.isEnabled(position.getProject())) { return; } - final String prefix = result.getPrefixMatcher().getPrefix().trim(); final XmlTag startingTag = PsiTreeUtil.getParentOfType( position, XmlTag.class @@ -57,6 +56,7 @@ protected void addCompletions( if (currentPath == null) { return; } + final String prefix = result.getPrefixMatcher().getPrefix().trim(); final String capture = currentPath + "." + prefix; for (final LookupElement element : makeCompletion(capture, position.getProject())) { @@ -97,7 +97,7 @@ public static List makeCompletion( } final String[] groupNameParts = input.split("\\."); - if (groupNameParts.length != 2) { + if (groupNameParts.length != 2) { //NOPMD return; } final String groupId = groupNameParts[1]; diff --git a/src/com/magento/idea/magento2plugin/completion/xml/SystemConfigurationContributor.java b/src/com/magento/idea/magento2plugin/completion/xml/SystemConfigurationContributor.java index 5dac34f57..532ef8862 100644 --- a/src/com/magento/idea/magento2plugin/completion/xml/SystemConfigurationContributor.java +++ b/src/com/magento/idea/magento2plugin/completion/xml/SystemConfigurationContributor.java @@ -27,7 +27,9 @@ public class SystemConfigurationContributor extends CompletionContributor { /** * Contributes completions to the system.xml and config.xml files. */ + @SuppressWarnings("PMD.ExcessiveMethodLength") public SystemConfigurationContributor() { + super(); final XmlFilePattern.Capture systemXmlFileCapture = XmlPatterns .xmlFile() .withName(StandardPatterns.string().endsWith(ModuleSystemXmlFile.FILE_NAME)); From 32d53d9e3ea11c51c9fde46e07d1b57f8c2e83a6 Mon Sep 17 00:00:00 2001 From: bohdan-harniuk Date: Wed, 8 Jun 2022 17:09:34 +0300 Subject: [PATCH 3/3] 600: Added test cases --- .../config.xml | 17 ++ .../config.xml | 15 ++ .../config.xml | 13 + .../system.xml | 17 ++ .../system.xml | 16 ++ .../system.xml | 14 + .../module-catalog/etc/adminhtml/system.xml | 240 ++++++++++++++++++ ...ystemConfigurationPathsCompletionTest.java | 66 +++++ 8 files changed, 398 insertions(+) create mode 100644 testData/completion/xml/SystemConfigurationPathsCompletion/configXmlFieldMustProvideCompletion/config.xml create mode 100644 testData/completion/xml/SystemConfigurationPathsCompletion/configXmlGroupMustProvideCompletion/config.xml create mode 100644 testData/completion/xml/SystemConfigurationPathsCompletion/configXmlSectionMustProvideCompletion/config.xml create mode 100644 testData/completion/xml/SystemConfigurationPathsCompletion/systemXmlFieldMustProvideCompletion/system.xml create mode 100644 testData/completion/xml/SystemConfigurationPathsCompletion/systemXmlGroupMustProvideCompletion/system.xml create mode 100644 testData/completion/xml/SystemConfigurationPathsCompletion/systemXmlSectionMustProvideCompletion/system.xml create mode 100644 testData/project/magento2/vendor/magento/module-catalog/etc/adminhtml/system.xml create mode 100644 tests/com/magento/idea/magento2plugin/completion/xml/SystemConfigurationPathsCompletionTest.java diff --git a/testData/completion/xml/SystemConfigurationPathsCompletion/configXmlFieldMustProvideCompletion/config.xml b/testData/completion/xml/SystemConfigurationPathsCompletion/configXmlFieldMustProvideCompletion/config.xml new file mode 100644 index 000000000..b0764cc0b --- /dev/null +++ b/testData/completion/xml/SystemConfigurationPathsCompletion/configXmlFieldMustProvideCompletion/config.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + diff --git a/testData/completion/xml/SystemConfigurationPathsCompletion/configXmlGroupMustProvideCompletion/config.xml b/testData/completion/xml/SystemConfigurationPathsCompletion/configXmlGroupMustProvideCompletion/config.xml new file mode 100644 index 000000000..f16461cbd --- /dev/null +++ b/testData/completion/xml/SystemConfigurationPathsCompletion/configXmlGroupMustProvideCompletion/config.xml @@ -0,0 +1,15 @@ + + + + + + f + + + diff --git a/testData/completion/xml/SystemConfigurationPathsCompletion/configXmlSectionMustProvideCompletion/config.xml b/testData/completion/xml/SystemConfigurationPathsCompletion/configXmlSectionMustProvideCompletion/config.xml new file mode 100644 index 000000000..aca08fa78 --- /dev/null +++ b/testData/completion/xml/SystemConfigurationPathsCompletion/configXmlSectionMustProvideCompletion/config.xml @@ -0,0 +1,13 @@ + + + + + + + diff --git a/testData/completion/xml/SystemConfigurationPathsCompletion/systemXmlFieldMustProvideCompletion/system.xml b/testData/completion/xml/SystemConfigurationPathsCompletion/systemXmlFieldMustProvideCompletion/system.xml new file mode 100644 index 000000000..796bed441 --- /dev/null +++ b/testData/completion/xml/SystemConfigurationPathsCompletion/systemXmlFieldMustProvideCompletion/system.xml @@ -0,0 +1,17 @@ + + + + +
+ + + +
+
+
diff --git a/testData/completion/xml/SystemConfigurationPathsCompletion/systemXmlGroupMustProvideCompletion/system.xml b/testData/completion/xml/SystemConfigurationPathsCompletion/systemXmlGroupMustProvideCompletion/system.xml new file mode 100644 index 000000000..a70e629b8 --- /dev/null +++ b/testData/completion/xml/SystemConfigurationPathsCompletion/systemXmlGroupMustProvideCompletion/system.xml @@ -0,0 +1,16 @@ + + + + +
+ + +
+
+
diff --git a/testData/completion/xml/SystemConfigurationPathsCompletion/systemXmlSectionMustProvideCompletion/system.xml b/testData/completion/xml/SystemConfigurationPathsCompletion/systemXmlSectionMustProvideCompletion/system.xml new file mode 100644 index 000000000..9dce5aead --- /dev/null +++ b/testData/completion/xml/SystemConfigurationPathsCompletion/systemXmlSectionMustProvideCompletion/system.xml @@ -0,0 +1,14 @@ + + + + +
+
+
+
diff --git a/testData/project/magento2/vendor/magento/module-catalog/etc/adminhtml/system.xml b/testData/project/magento2/vendor/magento/module-catalog/etc/adminhtml/system.xml new file mode 100644 index 000000000..a1b220230 --- /dev/null +++ b/testData/project/magento2/vendor/magento/module-catalog/etc/adminhtml/system.xml @@ -0,0 +1,240 @@ + + + + + + + + + + +
+ separator-top + + catalog + Magento_Catalog::config_catalog + + + + + Use {{name}} as Product Name placeholder + + + + Use {{name}} as Product Name placeholder + + + + Use {{name}} as Product Name or {{sku}} as Product SKU placeholders + + + + Use {{name}} and {{description}} as Product Name and Product Description placeholders + + + + + + + validate-number validate-zero-or-greater + + + + validate-number validate-zero-or-greater + + + + Magento\Config\Model\Config\Source\Yesno + + + + + + + Magento\Catalog\Model\Config\Source\ListMode + + + + Comma-separated. + validate-per-page-value-list required-entry + + + + Must be in the allowed values list. + validate-per-page-value + + + + Comma-separated. + validate-per-page-value-list required-entry + + + + Must be in the allowed values list. + validate-per-page-value + + + + Magento\Catalog\Model\Indexer\Category\Flat\System\Config\Mode + Magento\Config\Model\Config\Source\Yesno + + + + Magento\Catalog\Model\Indexer\Product\Flat\System\Config\Mode + Magento\Config\Model\Config\Source\Yesno + + + + Applies to category pages. + Magento\Catalog\Model\Config\Source\ListSort + + + + Whether to show "All" option in the "Show X Per Page" dropdown. + Magento\Config\Model\Config\Source\Yesno + + + + Changing may affect SEO and cache storage consumption. + Magento\Config\Model\Config\Source\Yesno + + + + + 1 + Magento\Catalog\Model\Config\CatalogClone\Media\Image + + Magento\Config\Model\Config\Backend\Image + catalog/product/placeholder + catalog/product/placeholder + + + + + + + + + + Magento\Config\Model\Config\Source\Yesno + + + + Magento\Config\Model\Config\Source\Yesno + + + + + + + "Currency Options" > "Base Currency").]]> + Magento\Catalog\Model\Indexer\Product\Price\System\Config\PriceScope + Magento\Catalog\Model\Config\Source\Price\Scope + 1 + + + + + + Magento\Config\Model\Config\Source\Yesno + + + + + + + validate-digits validate-zero-or-greater + + + + + + + Magento\Config\Model\Config\Source\Yesno + + + + Magento\Catalog\Block\Adminhtml\Form\Renderer\Config\DateFieldsOrder + + + + Magento\Catalog\Model\Config\Source\TimeFormat + + + + validate-digits validate-zero-or-greater validate-number-range number-range-1000-9999 + Please use a four-digit year format. + Magento\Catalog\Block\Adminhtml\Form\Renderer\Config\YearRange + + +
+
+ + + + Media content will be inserted into the editor as a static URL. Media content is not updated if the system configuration base URL changes. + Magento\Config\Model\Config\Source\Yesno + + disabled + + + +
+
+ + + + + Magento\Config\Model\Config\Source\Enabledisable + + + + Magento\Config\Model\Config\Source\Enabledisable + + + + Magento\Config\Model\Config\Source\Enabledisable + + +
+
+ + + + + Magento\Catalog\Model\Config\Source\LayoutList + + + + Magento\Catalog\Model\Config\Source\LayoutList + + + + + + Magento\Catalog\Model\Config\Source\Web\CatalogMediaUrlFormat + Learn more about catalog URL formats.

Warning! If you switch back to legacy mode, you must use the CLI to regenerate images.]]>
+
+
+
+
+ separator-top + + advanced + Magento_Config::config_system + + + + + validate-digits validate-digits-range digits-range-1-100 required-entry + Jpeg quality for resized images 1-100%. + + +
+
+
diff --git a/tests/com/magento/idea/magento2plugin/completion/xml/SystemConfigurationPathsCompletionTest.java b/tests/com/magento/idea/magento2plugin/completion/xml/SystemConfigurationPathsCompletionTest.java new file mode 100644 index 000000000..935a4179b --- /dev/null +++ b/tests/com/magento/idea/magento2plugin/completion/xml/SystemConfigurationPathsCompletionTest.java @@ -0,0 +1,66 @@ +/* + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +package com.magento.idea.magento2plugin.completion.xml; + +import com.magento.idea.magento2plugin.magento.files.ModuleConfigXmlFile; +import com.magento.idea.magento2plugin.magento.files.ModuleSystemXmlFile; + +public class SystemConfigurationPathsCompletionTest extends CompletionXmlFixtureTestCase { + + /** + * Test system xml section element completion. + */ + public void testSystemXmlSectionMustProvideCompletion() { + final String filePath = this.getFixturePath(ModuleSystemXmlFile.FILE_NAME); + myFixture.copyFileToProject(filePath); + assertCompletionContains(filePath, "catalog"); + } + + /** + * Test system xml group element completion. + */ + public void testSystemXmlGroupMustProvideCompletion() { + final String filePath = this.getFixturePath(ModuleSystemXmlFile.FILE_NAME); + myFixture.copyFileToProject(filePath); + assertCompletionContains(filePath, "frontend"); + } + + /** + * Test system xml field element completion. + */ + public void testSystemXmlFieldMustProvideCompletion() { + final String filePath = this.getFixturePath(ModuleSystemXmlFile.FILE_NAME); + myFixture.copyFileToProject(filePath); + assertCompletionContains(filePath, "list_allow_all"); + } + + /** + * Test config xml section element completion. + */ + public void testConfigXmlSectionMustProvideCompletion() { + final String filePath = this.getFixturePath(ModuleConfigXmlFile.FILE_NAME); + myFixture.copyFileToProject(filePath); + assertCompletionContains(filePath, "catalog"); + } + + /** + * Test config xml group element completion. + */ + public void testConfigXmlGroupMustProvideCompletion() { + final String filePath = this.getFixturePath(ModuleConfigXmlFile.FILE_NAME); + myFixture.copyFileToProject(filePath); + assertCompletionContains(filePath, "frontend"); + } + + /** + * Test config xml field element completion. + */ + public void testConfigXmlFieldMustProvideCompletion() { + final String filePath = this.getFixturePath(ModuleConfigXmlFile.FILE_NAME); + myFixture.copyFileToProject(filePath); + assertCompletionContains(filePath, "list_allow_all"); + } +}