From 2673d2f9df6bedee8e2be63f928aa31a22bdc0b8 Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Wed, 2 May 2018 23:46:33 +0200 Subject: [PATCH] SSR: conditional visibility of constraints components --- .../JavaStructuralSearchProfile.java | 110 ++++++++++++++ .../StructuralSearchProfile.java | 24 +++- .../impl/matcher/CompiledPattern.java | 14 ++ .../matcher/compiler/PatternCompiler.java | 29 ++-- .../plugin/ui/EditVarConstraintsDialog.java | 136 +++++++++--------- .../plugin/ui/SearchDialog.java | 14 +- .../structuralsearch/plugin/ui/UIUtil.java | 8 ++ .../plugin/ui/VarConstraints.form | 90 ++++++------ .../util/resources/misc/registry.properties | 3 + 9 files changed, 301 insertions(+), 127 deletions(-) diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/JavaStructuralSearchProfile.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/JavaStructuralSearchProfile.java index 99a56b2f8fcf..4937c026eed6 100644 --- a/java/structuralsearch-java/src/com/intellij/structuralsearch/JavaStructuralSearchProfile.java +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/JavaStructuralSearchProfile.java @@ -18,6 +18,7 @@ import com.intellij.openapi.fileTypes.StdFileTypes; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; +import com.intellij.psi.impl.source.JavaDummyHolder; import com.intellij.psi.javadoc.PsiDocComment; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; @@ -831,4 +832,113 @@ public class JavaStructuralSearchProfile extends StructuralSearchProfile { return offset; } + + @Override + public boolean isApplicableConstraint(String constraintName, @Nullable PsiElement variableNode, boolean completePattern, boolean target) { + switch (constraintName) { + case UIUtil.TEXT: return !completePattern; + case UIUtil.TEXT_HIERARCHY: + if (variableNode != null) { + final PsiElement parent = variableNode.getParent(); + if (parent instanceof PsiJavaCodeReferenceElement && parent.getParent() instanceof PsiTypeElement || + parent instanceof PsiClass) { + return true; + } + } + return false; + case UIUtil.EXPECTED_TYPE: + if (variableNode != null) { + final PsiElement grandParent = variableNode.getParent().getParent(); + if (grandParent instanceof PsiExpressionStatement) { + if (hasSemicolon(grandParent)) return false; + } + else if (grandParent instanceof PsiStatement) return false; + } + case UIUtil.TYPE: + if (variableNode instanceof PsiExpressionStatement) { + final PsiElement child = variableNode.getLastChild(); + if (child instanceof PsiErrorElement) { + final PsiErrorElement errorElement = (PsiErrorElement)child; + return "';' expected".equals(errorElement.getErrorDescription()); + } + } + return variableNode != null && variableNode.getParent() instanceof PsiExpression; + case UIUtil.MINIMUM_ZERO: + if (target) return false; + return isApplicableCount(variableNode, false); + case UIUtil.MAXIMUM_UNLIMITED: + return isApplicableCount(variableNode, true); + case UIUtil.REFERENCE: + if (completePattern || variableNode == null) return false; + if (variableNode instanceof PsiLiteralExpression && ((PsiLiteralExpression)variableNode).getValue() instanceof String) return true; + final PsiElement parent = variableNode.getParent(); + return parent instanceof PsiReferenceExpression || parent instanceof PsiJavaCodeReferenceElement; + default: return super.isApplicableConstraint(constraintName, variableNode, completePattern, target); + } + } + + private static boolean isApplicableCount(@Nullable PsiElement variableNode, boolean max) { + if (variableNode != null) { + final PsiElement parent = variableNode.getParent(); + if (max && parent instanceof PsiLocalVariable) { + final PsiLocalVariable localVariable = (PsiLocalVariable)parent; + if (localVariable instanceof PsiResourceVariable) return false; + if (localVariable.getTypeElement().isInferredType()) return false; + return true; + } + else if (max && parent instanceof PsiField) { + return true; + } + if (parent instanceof PsiMember && !(parent instanceof PsiTypeParameter)) { + final PsiMember member = (PsiMember)parent; + final PsiClass aClass = member.getContainingClass(); + if (aClass == null) { + return false; + } + final String name = aClass.getName(); + return name != null && !"_Dummy_".equals(name); + } + final PsiElement grandParent = parent.getParent(); + if (!max && parent instanceof PsiReferenceExpression && grandParent instanceof PsiReferenceExpression) { + return true; + } + if (grandParent instanceof PsiAnnotation && !(grandParent.getParent().getNextSibling() instanceof PsiErrorElement)) { + return true; + } + if (grandParent instanceof PsiExpressionStatement && hasSemicolon(grandParent)) { + if (max) { + return true; + } + else { + final PsiElement greatGrandParent = grandParent.getParent(); + return !(greatGrandParent instanceof PsiCodeBlock) || !(greatGrandParent.getParent() instanceof JavaDummyHolder); + } + } + if (!max && grandParent instanceof PsiVariable && + PsiTreeUtil.isAncestor(((PsiVariable)grandParent).getInitializer(), variableNode, true)) { + return true; + } + if (grandParent instanceof PsiReferenceList) { + if (!max) return true; + final PsiReferenceList referenceList = (PsiReferenceList)grandParent; + final PsiElement greatGrandParent = referenceList.getParent(); + return !(greatGrandParent instanceof PsiClass) || ((PsiClass)greatGrandParent).getExtendsList() != referenceList; + } + if (grandParent instanceof PsiParameterList || grandParent instanceof PsiExpressionList || + grandParent instanceof PsiTypeParameterList || grandParent instanceof PsiResourceList) { + return true; + } + if (grandParent instanceof PsiTypeElement && grandParent.getParent() instanceof PsiReferenceParameterList) return true; + if (grandParent instanceof PsiPolyadicExpression && max) return true; + } + return false; + } + + private static boolean hasSemicolon(PsiElement element) { + PsiElement lastChild = element.getLastChild(); + while (lastChild instanceof PsiComment || lastChild instanceof PsiWhiteSpace) { + lastChild = lastChild.getPrevSibling(); + } + return PsiUtil.isJavaToken(lastChild, JavaTokenType.SEMICOLON); + } } diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/StructuralSearchProfile.java b/platform/structuralsearch/source/com/intellij/structuralsearch/StructuralSearchProfile.java index bf3411243de9..1f1e86dd02d2 100644 --- a/platform/structuralsearch/source/com/intellij/structuralsearch/StructuralSearchProfile.java +++ b/platform/structuralsearch/source/com/intellij/structuralsearch/StructuralSearchProfile.java @@ -211,7 +211,7 @@ public abstract class StructuralSearchProfile { int offset, ReplacementInfo replacementInfo) { if (info.getName().equals(match.getName())) { - String replacementString = match.getMatchImage(); + final String replacementString; boolean removeSemicolon = false; if (match.hasChildren() && !match.isScopeMatch()) { // compound matches @@ -237,6 +237,7 @@ public abstract class StructuralSearchProfile { if (info.isStatementContext()) { removeSemicolon = match.getMatch() instanceof PsiComment; } + replacementString = match.getMatchImage(); } offset = Replacer.insertSubstitution(result, offset, info, replacementString); @@ -280,4 +281,25 @@ public abstract class StructuralSearchProfile { public PsiElement getPresentableElement(PsiElement element) { return isIdentifier(element) ? element.getParent() : element; } + + /** + * Override this method to influence which UI controls are shown when editing the constraints of the specified variable. + * + * @param constraintName the name of the constraint controls for which applicability is considered. + * See {@link com.intellij.structuralsearch.plugin.ui.UIUtil} for predefined constraint names + * @param variableNode the psi element corresponding to the current variable + * @param completePattern true, if the current variableNode encompasses the complete pattern. The variableNode can also be null in this case. + * @param target true, if the current variableNode is the target of the search + * @return true, if the requested constraint is applicable and the corresponding UI should be shown when editing the variable; false otherwise + */ + public boolean isApplicableConstraint(String constraintName, @Nullable PsiElement variableNode, boolean completePattern, boolean target) { + switch (constraintName) { + case UIUtil.TEXT: + case UIUtil.MINIMUM_ZERO: + if (target) return false; + case UIUtil.MAXIMUM_UNLIMITED: + case UIUtil.REFERENCE: return !completePattern; + } + return false; + } } diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/CompiledPattern.java b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/CompiledPattern.java index 53d2df6a63d7..c140b1c7f0cb 100644 --- a/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/CompiledPattern.java +++ b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/CompiledPattern.java @@ -13,10 +13,13 @@ import com.intellij.structuralsearch.impl.matcher.handlers.MatchingHandler; import com.intellij.structuralsearch.impl.matcher.handlers.SimpleHandler; import com.intellij.structuralsearch.impl.matcher.handlers.SubstitutionHandler; import com.intellij.structuralsearch.impl.matcher.strategies.MatchingStrategy; +import com.intellij.util.SmartList; +import com.intellij.util.containers.MultiMap; import gnu.trove.THashMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Collection; import java.util.List; import java.util.Map; @@ -26,6 +29,7 @@ import java.util.Map; public abstract class CompiledPattern { public static final Key HANDLER_KEY = Key.create("ss.handler"); private final Map handlers = new THashMap<>(); + private final MultiMap variableNodes = MultiMap.createSmart(); private SearchScope scope; private NodeIterator nodes; private MatchingStrategy strategy; @@ -159,4 +163,14 @@ public abstract class CompiledPattern { public String getAlternativeTextToMatch(PsiElement node, String previousText) { return null; } + + @NotNull + public List getVariableNodes(@NotNull String name) { + final Collection elements = variableNodes.get(name); + return elements instanceof List ? (List)elements : new SmartList<>(elements); + } + + public void putVariableNode(@NotNull String name, @NotNull PsiElement node) { + variableNodes.putValue(name, node); + } } diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/compiler/PatternCompiler.java b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/compiler/PatternCompiler.java index 5f70b66e6f98..314b543ee438 100644 --- a/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/compiler/PatternCompiler.java +++ b/platform/structuralsearch/source/com/intellij/structuralsearch/impl/matcher/compiler/PatternCompiler.java @@ -23,6 +23,7 @@ import com.intellij.structuralsearch.impl.matcher.CompiledPattern; import com.intellij.structuralsearch.impl.matcher.MatcherImplUtil; import com.intellij.structuralsearch.impl.matcher.PatternTreeContext; import com.intellij.structuralsearch.impl.matcher.filters.LexicalNodesFilter; +import com.intellij.structuralsearch.impl.matcher.handlers.DelegatingHandler; import com.intellij.structuralsearch.impl.matcher.handlers.MatchingHandler; import com.intellij.structuralsearch.impl.matcher.handlers.SubstitutionHandler; import com.intellij.structuralsearch.impl.matcher.predicates.*; @@ -111,10 +112,15 @@ public class PatternCompiler { private static void checkForUnknownVariables(final CompiledPattern pattern, List elements) { for (PsiElement element : elements) { + pattern.putVariableNode(Configuration.CONTEXT_VAR_NAME, element); element.accept(new PsiRecursiveElementWalkingVisitor() { @Override public void visitElement(PsiElement element) { - if (element.getUserData(CompiledPattern.HANDLER_KEY) != null) { + final Object userData = element.getUserData(CompiledPattern.HANDLER_KEY); + if (userData != null) { + if (userData instanceof SubstitutionHandler) { + pattern.putVariableNode(((SubstitutionHandler)userData).getName(), element); + } return; } super.visitElement(element); @@ -131,10 +137,16 @@ public class PatternCompiler { } return; } - final MatchingHandler handler = pattern.getHandler(pattern.getTypedVarString(element)); + MatchingHandler handler = pattern.getHandler(pattern.getTypedVarString(element)); if (handler == null) { throw new MalformedPatternException(); } + if (handler instanceof DelegatingHandler) { + handler = ((DelegatingHandler)handler).getDelegate(); + } + if (handler instanceof SubstitutionHandler){ + pattern.putVariableNode(((SubstitutionHandler)handler).getName(), element); + } } }); } @@ -144,15 +156,9 @@ public class PatternCompiler { public static CompiledPattern getLastCompiledPattern(MatchOptions options) { synchronized (LOCK) { final CompiledPattern lastCompiledPattern = ourLastCompiledPattern == null ? null : ourLastCompiledPattern.get(); - if (lastCompiledPattern == null || !options.equals(ourLastMatchOptions)) { - return null; - } - if (lastCompiledPattern.getScope() != null) { - if (!options.getScope().equals(lastCompiledPattern.getScope())) { - return null; - } - } - else if (options.getScope() instanceof GlobalSearchScope) { + if (lastCompiledPattern == null || + !options.equals(ourLastMatchOptions) || + options.getScope() instanceof GlobalSearchScope && options.getScope() != ourLastMatchOptions.getScope()) { return null; } return lastCompiledPattern; @@ -506,7 +512,6 @@ public class PatternCompiler { buf.append(text.substring(prevOffset)); PsiElement[] patternElements; - try { patternElements = MatcherImplUtil.createTreeFromText(buf.toString(), PatternTreeContext.Block, options.getFileType(), options.getDialect(), options.getPatternContext(), project, false); diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/EditVarConstraintsDialog.java b/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/EditVarConstraintsDialog.java index e3f30de9487f..5c241425e3e0 100644 --- a/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/EditVarConstraintsDialog.java +++ b/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/EditVarConstraintsDialog.java @@ -19,6 +19,7 @@ import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.fileTypes.FileTypeManager; import com.intellij.openapi.fileTypes.FileTypes; import com.intellij.openapi.fileTypes.StdFileTypes; +import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.ComponentWithBrowseButton; import com.intellij.openapi.ui.DialogWrapper; @@ -26,13 +27,13 @@ import com.intellij.openapi.ui.MessageType; import com.intellij.openapi.ui.popup.Balloon; import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.wm.IdeFocusManager; -import com.intellij.psi.PsiCodeFragment; -import com.intellij.psi.PsiDocumentManager; -import com.intellij.psi.PsiFile; -import com.intellij.psi.PsiFileFactory; +import com.intellij.psi.*; import com.intellij.structuralsearch.*; +import com.intellij.structuralsearch.impl.matcher.CompiledPattern; +import com.intellij.structuralsearch.impl.matcher.compiler.PatternCompiler; import com.intellij.structuralsearch.impl.matcher.predicates.ScriptLog; import com.intellij.structuralsearch.impl.matcher.predicates.ScriptSupport; import com.intellij.structuralsearch.plugin.replace.ReplaceOptions; @@ -64,6 +65,8 @@ import java.util.regex.PatternSyntaxException; */ class EditVarConstraintsDialog extends DialogWrapper { private static final Logger LOG = Logger.getInstance("#com.intellij.structuralsearch.plugin.ui.EditVarConstraintsDialog"); + private final CompiledPattern myCompiledPattern; + private final StructuralSearchProfile myProfile; private IntegerField maxoccurs; private JCheckBox applyWithinTypeHierarchy; @@ -95,6 +98,8 @@ class EditVarConstraintsDialog extends DialogWrapper { private TextFieldWithAutoCompletionWithBrowseButton referenceTargetTextField; private JPanel referenceTargetConstraints; private JBCheckBox invertReferenceTarget; + private JPanel expectedTypeConstraints; + private JPanel scriptConstraints; private final Project myProject; @@ -103,6 +108,9 @@ class EditVarConstraintsDialog extends DialogWrapper { myProject = project; variables = _variables; myConfiguration = configuration; + final MatchOptions matchOptions = myConfiguration.getMatchOptions(); + myCompiledPattern = PatternCompiler.getLastCompiledPattern(matchOptions); + myProfile = StructuralSearchUtil.getProfileByFileType(fileType); setTitle(SSRBundle.message("editvarcontraints.edit.variables")); @@ -138,28 +146,6 @@ class EditVarConstraintsDialog extends DialogWrapper { variables.add(Configuration.CONTEXT_VAR_NAME); } - if (fileType == StdFileTypes.JAVA) { - formalArgTypeWithinHierarchy.setEnabled(true); - invertFormalArgType.setEnabled(true); - formalArgType.setEnabled(true); - - exprTypeWithinHierarchy.setEnabled(true); - notExprType.setEnabled(true); - regexprForExprType.setEnabled(true); - - applyWithinTypeHierarchy.setEnabled(true); - } else { - formalArgTypeWithinHierarchy.setEnabled(false); - invertFormalArgType.setEnabled(false); - formalArgType.setEnabled(false); - - exprTypeWithinHierarchy.setEnabled(false); - notExprType.setEnabled(false); - regexprForExprType.setEnabled(false); - - applyWithinTypeHierarchy.setEnabled(false); - } - parameterList.setModel( new AbstractListModel() { @Override @@ -173,9 +159,7 @@ class EditVarConstraintsDialog extends DialogWrapper { } } ); - - parameterList.setSelectionMode( ListSelectionModel.SINGLE_SELECTION ); - + parameterList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); parameterList.getSelectionModel().addListSelectionListener( new ListSelectionListener() { boolean rollingBackSelection; @@ -199,7 +183,6 @@ class EditVarConstraintsDialog extends DialogWrapper { } } ); - parameterList.setCellRenderer( new DefaultListCellRenderer() { @Override @@ -217,7 +200,7 @@ class EditVarConstraintsDialog extends DialogWrapper { customScriptCode.getButton().addActionListener(new ActionListener() { @Override public void actionPerformed(@NotNull final ActionEvent e) { - final List variableNames = ContainerUtil.newArrayList(myConfiguration.getMatchOptions().getVariableConstraintNames()); + final List variableNames = ContainerUtil.newArrayList(matchOptions.getVariableConstraintNames()); variableNames.add(ScriptLog.SCRIPT_LOG_VAR_NAME); final EditScriptDialog dialog = new EditScriptDialog(project, customScriptCode.getChildComponent().getText(), variableNames); dialog.show(); @@ -231,7 +214,7 @@ class EditVarConstraintsDialog extends DialogWrapper { if (!variables.isEmpty()) { final String variableName = configuration.getCurrentVariableName(); configuration.setCurrentVariableName(null); - final int selectedIndex = variableName != null ? variables.indexOf(variableName) : 0; + final int selectedIndex = variableName != null ? Math.max(0, variables.indexOf(variableName)) : 0; parameterList.setSelectedIndex(selectedIndex); } } @@ -337,15 +320,51 @@ class EditVarConstraintsDialog extends DialogWrapper { } void copyValuesToUI(String varName) { + if (varName == null) return; if (isReplacementVariable(varName)) { final ReplacementVariableDefinition definition = ((ReplaceConfiguration)myConfiguration).getReplaceOptions().getVariableDefinition(stripReplacementVarDecoration(varName)); restoreScriptCode(definition); - setSearchConstraintsVisible(false); + textConstraintsPanel.setVisible(false); + occurencePanel.setVisible(false); + expressionConstraints.setVisible(false); + partOfSearchResults.setVisible(false); + containedInConstraints.setVisible(false); + referenceTargetConstraints.setVisible(false); + scriptConstraints.setVisible(true); return; } else { - setSearchConstraintsVisible(true); + final List nodes = myCompiledPattern.getVariableNodes(varName); + final PsiElement node = nodes.size() == 1 ? nodes.get(0) : null; + final boolean completePattern = Configuration.CONTEXT_VAR_NAME.equals(varName); + + final boolean text = myProfile.isApplicableConstraint(UIUtil.TEXT, node, completePattern, false); + textConstraintsPanel.setVisible(text); + applyWithinTypeHierarchy.setVisible(text && myProfile.isApplicableConstraint(UIUtil.TEXT_HIERARCHY, node, completePattern, false)); + final boolean minZero = myProfile.isApplicableConstraint(UIUtil.MINIMUM_ZERO, node, completePattern, false); + final boolean maxUnlimited = myProfile.isApplicableConstraint(UIUtil.MAXIMUM_UNLIMITED, node, completePattern, false); + if (minZero || maxUnlimited) { + occurencePanel.setVisible(true); + minoccurs.setMinValue(minZero ? 0 : 1); + minoccurs.setDefaultValue(minZero ? 0 : 1); + minoccurs.setDefaultValueText(minZero ? "0" : "1"); + maxoccurs.setMaxValue(maxUnlimited ? Integer.MAX_VALUE : 1); + maxoccurs.setDefaultValue(maxUnlimited ? Integer.MAX_VALUE : 1); + maxoccurs.setDefaultValueText(maxUnlimited ? SSRBundle.message("editvarcontraints.unlimited") : "1"); + } + else { + occurencePanel.setVisible(false); + } + final boolean typeComponent = myProfile.isApplicableConstraint(UIUtil.TYPE, node, completePattern, false); + expressionConstraints.setVisible(typeComponent); + expectedTypeConstraints.setVisible(typeComponent && myProfile.isApplicableConstraint(UIUtil.EXPECTED_TYPE, node, completePattern, + false)); + referenceTargetConstraints.setVisible(myProfile.isApplicableConstraint(UIUtil.REFERENCE, node, completePattern, false)); + containedInConstraints.setVisible(completePattern); + scriptConstraints.setVisible(Registry.is("ssr.enable.script.constraint.on.all.variables") || completePattern); + + partOfSearchResults.setEnabled(!completePattern); } final MatchOptions matchOptions = myConfiguration.getMatchOptions(); @@ -406,22 +425,6 @@ class EditVarConstraintsDialog extends DialogWrapper { referenceTargetTextField.setText(StringUtil.unquoteString(varInfo.getReferenceConstraint())); invertReferenceTarget.setSelected(varInfo.isInvertReference()); } - - final boolean contextVar = Configuration.CONTEXT_VAR_NAME.equals(varName); - containedInConstraints.setVisible(contextVar); - textConstraintsPanel.setVisible(!contextVar); - partOfSearchResults.setEnabled(!contextVar); - occurencePanel.setVisible(!contextVar); - referenceTargetConstraints.setVisible(!contextVar); - } - - private void setSearchConstraintsVisible(boolean b) { - textConstraintsPanel.setVisible(b); - occurencePanel.setVisible(b); - expressionConstraints.setVisible(b); - partOfSearchResults.setVisible(b); - containedInConstraints.setVisible(b); - referenceTargetConstraints.setVisible(b); } private void restoreScriptCode(NamedScriptableDefinition varInfo) { @@ -471,30 +474,19 @@ class EditVarConstraintsDialog extends DialogWrapper { } private boolean validateCounts() { - final int minValue; - final String minoccursText = minoccurs.getText(); - if (!minoccursText.isEmpty()) { - try { - minValue = Integer.parseInt(minoccursText); - if (minValue < 0) throw new NumberFormatException(); - } - catch (NumberFormatException e) { - return showError(minoccurs, SSRBundle.message("invalid.occurence.count")); - } - } else { - minValue = 0; + try { + minoccurs.validateContent(); } - - final String maxoccursText = maxoccurs.getText(); - if (!maxoccursText.isEmpty()) { - try { - if (Integer.parseInt(maxoccursText) < minValue) throw new NumberFormatException(); - } - catch (NumberFormatException e) { - return showError(maxoccurs, SSRBundle.message("invalid.occurence.count")); - } + catch (ConfigurationException e) { + return showError(minoccurs, SSRBundle.message("invalid.occurence.count")); } - return true; + try { + maxoccurs.validateContent(); + } + catch (ConfigurationException e) { + return showError(maxoccurs, SSRBundle.message("invalid.occurence.count")); + } + return maxoccurs.getValue() >= minoccurs.getValue() || showError(maxoccurs, SSRBundle.message("invalid.occurence.count")); } private boolean showError(JComponent component, String message) { diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/SearchDialog.java b/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/SearchDialog.java index 3fcf5589732b..5b43f923b81c 100644 --- a/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/SearchDialog.java +++ b/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/SearchDialog.java @@ -38,6 +38,7 @@ import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.SearchScope; import com.intellij.structuralsearch.*; +import com.intellij.structuralsearch.impl.matcher.compiler.PatternCompiler; import com.intellij.structuralsearch.plugin.StructuralSearchPlugin; import com.intellij.ui.ComboboxSpeedSearch; import com.intellij.ui.IdeBorderFactory; @@ -94,6 +95,7 @@ public class SearchDialog extends DialogWrapper { private String mySavedEditorText; private JPanel myContentPanel; private JComponent myEditorPanel; + private JButton myEditVariablesButton; public SearchDialog(SearchContext searchContext) { this(searchContext, true, true); @@ -168,7 +170,10 @@ public class SearchDialog extends DialogWrapper { myAlarm.addRequest(() -> { try { final boolean valid = isValid(); - ApplicationManager.getApplication().invokeLater(() -> getOKAction().setEnabled(valid)); + ApplicationManager.getApplication().invokeLater(() -> { + myEditVariablesButton.setEnabled(PatternCompiler.getLastCompiledPattern(myConfiguration.getMatchOptions()) != null); + getOKAction().setEnabled(valid); + }); } catch (ProcessCanceledException e) { throw e; @@ -523,7 +528,7 @@ public class SearchDialog extends DialogWrapper { panel.add(Box.createHorizontalStrut(8)); panel.add( - createJButtonForAction( + myEditVariablesButton = createJButtonForAction( new AbstractAction() { { putValue(NAME, SSRBundle.message("edit.variables.button")); @@ -531,6 +536,10 @@ public class SearchDialog extends DialogWrapper { @Override public void actionPerformed(ActionEvent e) { + if (PatternCompiler.getLastCompiledPattern(myConfiguration.getMatchOptions()) == null) { + myEditVariablesButton.setEnabled(false); + return; + } new EditVarConstraintsDialog( searchContext.getProject(), myConfiguration, @@ -561,6 +570,7 @@ public class SearchDialog extends DialogWrapper { Configuration[] configurations = dialog.getSelectedConfigurations(); if (configurations.length == 1) { setSearchPattern(configurations[0]); + initiateValidation(); } } } diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/UIUtil.java b/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/UIUtil.java index 75153a32af15..5a54a5de7639 100644 --- a/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/UIUtil.java +++ b/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/UIUtil.java @@ -46,6 +46,14 @@ public class UIUtil { public static final NotificationGroup SSR_NOTIFICATION_GROUP = NotificationGroup.toolWindowGroup(SSRBundle.message("structural.search.title"), ToolWindowId.FIND); + @NonNls public static final String TEXT = "TEXT"; + @NonNls public static final String TEXT_HIERARCHY = "TEXT HIERARCHY"; + @NonNls public static final String REFERENCE = "REFERENCE"; + @NonNls public static final String TYPE = "TYPE"; + @NonNls public static final String EXPECTED_TYPE = "EXPECTED TYPE"; + @NonNls public static final String MINIMUM_ZERO = "MINIMUM ZERO"; + @NonNls public static final String MAXIMUM_UNLIMITED = "MAXIMUM UNLIMITED"; + @NotNull public static Editor createEditor(Document doc, final Project project, boolean editable, @Nullable TemplateContextType contextType) { return createEditor(doc, project, editable, false, contextType); diff --git a/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/VarConstraints.form b/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/VarConstraints.form index 82e839e59981..920dc91cbd96 100644 --- a/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/VarConstraints.form +++ b/platform/structuralsearch/source/com/intellij/structuralsearch/plugin/ui/VarConstraints.form @@ -1,6 +1,6 @@
- + @@ -36,7 +36,7 @@ - + @@ -86,10 +86,10 @@ - + - + @@ -129,43 +129,53 @@ - + + - - - - - - - - - - - + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -173,13 +183,13 @@ - + - + @@ -233,7 +243,7 @@ - + @@ -267,11 +277,11 @@ - + - + @@ -303,7 +313,7 @@ - + diff --git a/platform/util/resources/misc/registry.properties b/platform/util/resources/misc/registry.properties index d88049bff7e8..9f68abdea84f 100644 --- a/platform/util/resources/misc/registry.properties +++ b/platform/util/resources/misc/registry.properties @@ -1479,6 +1479,9 @@ ESLint.Language.Service.full.log.description=ESLint: include source code fragmen goto.action.skip.tophits.and.options=false goto.action.skip.tophits.and.options.description=Skip top hits and options in go to action +ssr.enable.script.constraint.on.all.variables=true +ssr.enable.script.constraint.on.all.variables.description=Restores ability to have script constraints on all variables instead of only on Complete Match and Replacement variables + dsm.show.cycle.count.in.tooltip=false dsm.show.cycle.count.in.tooltip.description=Shows number of cycles found in the tooltip of the Toggle Cycles toolbar button after some calculation time jdk.regex.soe.workaround=true