Created
December 8, 2012 18:29
-
-
Save ignatov/4241249 to your computer and use it in GitHub Desktop.
better rule introduction
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Index: support/org/intellij/grammar/refactor/BnfIntroduceRuleHandler.java | |
IDEA additional info: | |
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP | |
<+>UTF-8 | |
Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP | |
<+>/*\n * Copyright 2011-2011 Gregory Shrago\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.intellij.grammar.refactor;\n\nimport com.intellij.openapi.actionSystem.DataContext;\nimport com.intellij.openapi.application.ApplicationManager;\nimport com.intellij.openapi.command.WriteCommandAction;\nimport com.intellij.openapi.editor.Editor;\nimport com.intellij.openapi.editor.SelectionModel;\nimport com.intellij.openapi.project.Project;\nimport com.intellij.openapi.util.Pass;\nimport com.intellij.openapi.util.TextRange;\nimport com.intellij.psi.*;\nimport com.intellij.psi.util.PsiTreeUtil;\nimport com.intellij.refactoring.RefactoringActionHandler;\nimport com.intellij.refactoring.introduce.inplace.OccurrencesChooser;\nimport gnu.trove.THashSet;\nimport org.intellij.grammar.generator.ParserGeneratorUtil;\nimport org.intellij.grammar.psi.*;\nimport org.intellij.grammar.psi.impl.BnfElementFactory;\nimport org.intellij.grammar.psi.impl.BnfFileImpl;\nimport org.intellij.grammar.psi.impl.GrammarUtil;\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\n\nimport java.util.*;\n\n/**\n * Created by IntelliJ IDEA.\n * Date: 8/16/11\n * Time: 5:25 PM\n *\n * @author Vadim Romansky\n * @author gregsh\n */\npublic class BnfIntroduceRuleHandler implements RefactoringActionHandler {\n public static final String REFACTORING_NAME = \"Extract Rule\";\n\n @Override\n public void invoke(final @NotNull Project project, @NotNull PsiElement[] elements, DataContext dataContext) {\n // do not support this case\n }\n\n @Override\n public void invoke(@NotNull final Project project, final Editor editor, final PsiFile file, @Nullable DataContext dataContext) {\n if (!(file instanceof BnfFileImpl)) return;\n\n final BnfFileImpl bnfFile = (BnfFileImpl)file;\n final SelectionModel selectionModel = editor.getSelectionModel();\n int[] starts = selectionModel.getBlockSelectionStarts();\n int[] ends = selectionModel.getBlockSelectionEnds();\n if (starts.length == 0) return;\n\n int startOffset = starts[0];\n int endOffset = ends[ends.length-1];\n final BnfRule currentRule = PsiTreeUtil.getParentOfType(file.findElementAt(startOffset), BnfRule.class);\n if (currentRule == null) return;\n final BnfExpression parentExpression = findParentExpression(bnfFile, startOffset, endOffset-1);\n if (parentExpression == null) return;\n final List<BnfExpression> selectedExpression = findSelectedExpressionsInRange(parentExpression, new TextRange(startOffset, endOffset));\n if (selectedExpression.isEmpty()) return;\n final TextRange fixedRange = new TextRange(selectedExpression.get(0).getTextRange().getStartOffset(), selectedExpression.get(selectedExpression.size()-1).getTextRange().getEndOffset());\n final BnfRule ruleFromText = BnfElementFactory.createRuleFromText(file.getProject(), \"a ::= \" + fixedRange.substring(file.getText()));\n BnfExpressionOptimizer.optimize(ruleFromText.getExpression());\n\n final LinkedHashMap<OccurrencesChooser.ReplaceChoice, List<BnfExpression[]>> occurrencesMap = new LinkedHashMap<OccurrencesChooser.ReplaceChoice, List<BnfExpression[]>>();\n occurrencesMap.put(OccurrencesChooser.ReplaceChoice.NO, Collections.singletonList(selectedExpression.toArray(new BnfExpression[selectedExpression.size()])));\n occurrencesMap.put(OccurrencesChooser.ReplaceChoice.ALL, new ArrayList<BnfExpression[]>());\n file.acceptChildren(new PsiRecursiveElementWalkingVisitor() {\n @Override\n public void visitElement(PsiElement element) {\n if (element instanceof BnfExpression) {\n findOccurrences((BnfExpression)element, selectedExpression, occurrencesMap);\n }\n else if (element instanceof BnfAttrs) {\n return;\n }\n super.visitElement(element);\n }\n });\n if (occurrencesMap.get(OccurrencesChooser.ReplaceChoice.ALL).size() <= 1 && !ApplicationManager.getApplication().isUnitTestMode()) {\n occurrencesMap.remove(OccurrencesChooser.ReplaceChoice.ALL);\n }\n \n final Pass<OccurrencesChooser.ReplaceChoice> callback = new Pass<OccurrencesChooser.ReplaceChoice>() {\n @Override\n public void pass(final OccurrencesChooser.ReplaceChoice choice) {\n new WriteCommandAction.Simple(project, REFACTORING_NAME, file) {\n @Override\n public void run() {\n final PsiFile containingFile = currentRule.getContainingFile();\n String newRuleName = choseRuleName(containingFile);\n String newRuleText = \"private \" + newRuleName + \" ::= \" + ruleFromText.getExpression().getText();\n BnfRule addedRule = addNextRule(project, currentRule, newRuleText);\n if (choice == OccurrencesChooser.ReplaceChoice.ALL) {\n List<BnfExpression[]> exprToReplace = occurrencesMap.get(OccurrencesChooser.ReplaceChoice.ALL);\n replaceUsages(project, exprToReplace, addedRule.getId());\n }\n else {\n List<BnfExpression[]> exprToReplace = occurrencesMap.get(OccurrencesChooser.ReplaceChoice.NO);\n replaceUsages(project, exprToReplace, addedRule.getId());\n }\n final BnfIntroduceRulePopup popup = new BnfIntroduceRulePopup(project, editor, addedRule, addedRule.getExpression());\n\n editor.getCaretModel().moveToOffset(addedRule.getTextOffset());\n PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(editor.getDocument());\n popup.performInplaceRefactoring(null);\n }\n }.execute();\n }\n };\n if(ApplicationManager.getApplication().isUnitTestMode()){\n callback.pass(OccurrencesChooser.ReplaceChoice.ALL);\n } else {\n new OccurrencesChooser<BnfExpression[]>(editor){\n @Override\n protected TextRange getOccurrenceRange(BnfExpression[] occurrence) {\n return new TextRange(occurrence[0].getTextRange().getStartOffset(), occurrence[occurrence.length-1].getTextRange().getEndOffset());\n }\n }.showChooser(callback, occurrencesMap);\n }\n }\n\n public static BnfRule addNextRule(Project project, BnfRule currentRule, String newRuleText) {\n BnfRule addedRule = (BnfRule)currentRule.getParent().addAfter(BnfElementFactory.createRuleFromText(project, newRuleText), currentRule);\n currentRule.getParent().addBefore(BnfElementFactory.createLeafFromText(project, \"\\n\"), addedRule);\n if (endsWithSemicolon(currentRule)) {\n addedRule.addBefore(BnfElementFactory.createLeafFromText(project, \";\"), null);\n if (currentRule.getNextSibling() instanceof PsiWhiteSpace) {\n currentRule.getParent().addAfter(BnfElementFactory.createLeafFromText(project, \"\\n\"), addedRule);\n }\n }\n return addedRule;\n }\n\n public static boolean endsWithSemicolon(BnfRule rule) {\n return rule.getLastChild().getNode().getElementType() == BnfTypes.BNF_SEMICOLON;\n }\n\n private static List<BnfExpression> findSelectedExpressionsInRange(BnfExpression parentExpression, TextRange range) {\n if (parentExpression.getTextRange().equals(range)) {\n if (parentExpression instanceof BnfSequence) return ((BnfSequence)parentExpression).getExpressionList();\n if (parentExpression instanceof BnfChoice) return ((BnfChoice)parentExpression).getExpressionList();\n return Collections.singletonList(parentExpression);\n }\n LinkedList<BnfExpression> list = new LinkedList<BnfExpression>();\n for (PsiElement c = parentExpression.getFirstChild(); c != null; c = c.getNextSibling()) {\n if (c instanceof PsiWhiteSpace) continue;\n if (c.getTextRange().intersectsStrict(range)) {\n if (c instanceof BnfExpression) {\n list.add((BnfExpression)c);\n }\n else if (c == parentExpression.getFirstChild() || c == parentExpression.getLastChild()) {\n return Collections.singletonList(parentExpression);\n }\n }\n }\n return list;\n }\n\n private static void replaceUsages(Project project, List<BnfExpression[]> exprToReplace, PsiElement id) {\n for (BnfExpression[] expression : exprToReplace) {\n replaceExpression(project, expression, id);\n }\n }\n\n private static void replaceExpression(Project project, BnfExpression[] oldExpression, PsiElement id) {\n PsiElement parent = oldExpression[0].getParent();\n parent.addBefore(BnfElementFactory.createRuleFromText(project, \"a::=\"+id.getText()).getExpression(), oldExpression[0]);\n parent.deleteChildRange(oldExpression[0], oldExpression[oldExpression.length - 1]);\n //BnfExpressionOptimizer.optimize(parent);\n }\n\n private static void findOccurrences(BnfExpression expression,\n List<BnfExpression> selectedExpressions,\n Map<OccurrencesChooser.ReplaceChoice, List<BnfExpression[]>> occurrencesMap) {\n if (selectedExpressions.size() == 1) {\n if (GrammarUtil.equalsElement(expression, selectedExpressions.get(0))) {\n addOccurrence(OccurrencesChooser.ReplaceChoice.ALL, occurrencesMap, expression);\n }\n }\n else if (!GrammarUtil.isOneTokenExpression(expression)) {\n final PsiElement selectedParent = selectedExpressions.get(0).getParent();\n if (ParserGeneratorUtil.getEffectiveType(expression) != ParserGeneratorUtil.getEffectiveType(selectedParent)) return; \n int pos = 0;\n BnfExpression[] result = new BnfExpression[selectedExpressions.size()];\n for (PsiElement c = expression.getFirstChild(), s = null; c != null; c = c.getNextSibling()) {\n if (!(c instanceof BnfExpression)) continue;\n if (GrammarUtil.equalsElement((BnfExpression)c, selectedExpressions.get(pos))) {\n if (pos == 0) s = c;\n result[pos] = (BnfExpression)c;\n if (++ pos == result.length) {\n addOccurrence(OccurrencesChooser.ReplaceChoice.ALL, occurrencesMap, result.clone());\n pos = 0;\n }\n }\n else if (s != null) {\n c = s;\n pos = 0;\n s = null;\n }\n }\n }\n }\n\n private static void addOccurrence(OccurrencesChooser.ReplaceChoice choice,\n Map<OccurrencesChooser.ReplaceChoice, List<BnfExpression[]>> occurrencesMap,\n BnfExpression... expressions) {\n List<BnfExpression[]> list = occurrencesMap.get(choice);\n if (list == null) occurrencesMap.put(choice, list = new LinkedList<BnfExpression[]>());\n list.add(expressions);\n }\n\n private static String choseRuleName(PsiFile containingFile) {\n final Set<String> existingNames = new THashSet<String>();\n containingFile.accept(new PsiRecursiveElementWalkingVisitor() {\n @Override\n public void visitElement(PsiElement element) {\n if (element instanceof BnfAttrs) return;\n if (element instanceof BnfReferenceOrToken) {\n existingNames.add(((BnfReferenceOrToken)element).getId().getText());\n }\n else if (element instanceof BnfRule) {\n existingNames.add(((BnfRule)element).getId().getText());\n }\n super.visitElement(element);\n }\n });\n String name = \"rule\";\n for (int i = 1; existingNames.contains(name); i++) {\n name = \"rule\" + i;\n }\n return name;\n }\n\n @Nullable\n private static BnfExpression findParentExpression(PsiFile file, int startOffset, int endOffset) {\n final PsiElement startElement = file.findElementAt(startOffset);\n final PsiElement endElement = file.findElementAt(endOffset);\n if (startElement == null || endElement == null) return null;\n final PsiElement commonParent = PsiTreeUtil.findCommonParent(startElement, endElement);\n return PsiTreeUtil.getParentOfType(commonParent, BnfExpression.class, false);\n }\n}\n | |
=================================================================== | |
--- support/org/intellij/grammar/refactor/BnfIntroduceRuleHandler.java (revision 9f189511ee6b5de517389db375e181abf9118800) | |
+++ support/org/intellij/grammar/refactor/BnfIntroduceRuleHandler.java (revision ) | |
@@ -24,6 +24,7 @@ | |
import com.intellij.openapi.project.Project; | |
import com.intellij.openapi.util.Pass; | |
import com.intellij.openapi.util.TextRange; | |
+import com.intellij.openapi.util.text.StringUtil; | |
import com.intellij.psi.*; | |
import com.intellij.psi.util.PsiTreeUtil; | |
import com.intellij.refactoring.RefactoringActionHandler; | |
@@ -103,7 +104,10 @@ | |
@Override | |
public void run() { | |
final PsiFile containingFile = currentRule.getContainingFile(); | |
- String newRuleName = choseRuleName(containingFile); | |
+ InitializerTextBuilder builder = new InitializerTextBuilder(); | |
+ ruleFromText.getExpression().accept(builder); | |
+ String newRuleName = ApplicationManager.getApplication().isUnitTestMode() ? "rule" : builder.result(); | |
+ newRuleName = choseRuleName(containingFile, newRuleName); | |
String newRuleText = "private " + newRuleName + " ::= " + ruleFromText.getExpression().getText(); | |
BnfRule addedRule = addNextRule(project, currentRule, newRuleText); | |
if (choice == OccurrencesChooser.ReplaceChoice.ALL) { | |
@@ -225,7 +229,7 @@ | |
list.add(expressions); | |
} | |
- private static String choseRuleName(PsiFile containingFile) { | |
+ private static String choseRuleName(PsiFile containingFile, String newRuleName) { | |
final Set<String> existingNames = new THashSet<String>(); | |
containingFile.accept(new PsiRecursiveElementWalkingVisitor() { | |
@Override | |
@@ -240,9 +244,9 @@ | |
super.visitElement(element); | |
} | |
}); | |
- String name = "rule"; | |
+ String name = newRuleName; | |
for (int i = 1; existingNames.contains(name); i++) { | |
- name = "rule" + i; | |
+ name = newRuleName + i; | |
} | |
return name; | |
} | |
@@ -254,5 +258,49 @@ | |
if (startElement == null || endElement == null) return null; | |
final PsiElement commonParent = PsiTreeUtil.findCommonParent(startElement, endElement); | |
return PsiTreeUtil.getParentOfType(commonParent, BnfExpression.class, false); | |
+ } | |
+ | |
+ private static class InitializerTextBuilder extends PsiRecursiveElementVisitor { | |
+ private List<String> myResult = new ArrayList<String>(); | |
+ | |
+ @Override | |
+ public void visitWhiteSpace(@NotNull PsiWhiteSpace space) { | |
+// myResult.add(space.getText().replace('\n', ' ')); | |
+ } | |
+ | |
+ @Override | |
+ public void visitElement(@NotNull PsiElement element) { | |
+ if (element.getChildren().length == 0) { | |
+ String e = StringUtil.toLowerCase(element.getText().trim()); | |
+ boolean emptyOrSpaces = StringUtil.isEmptyOrSpaces(e); | |
+ if (!emptyOrSpaces) { | |
+ myResult.add(e); | |
+ } | |
+ return; | |
+ } | |
+ super.visitElement(element); | |
+ } | |
+ | |
+ @NotNull | |
+ public String result() { | |
+ int count = 10; | |
+ if (myResult.size() > count) { | |
+ myResult = myResult.subList(0, count - 1); | |
+ } | |
+ String join = StringUtil.join(myResult, "_"); | |
+ String s = join | |
+ .replaceAll("\\|", "_or") | |
+ .replaceAll("\\?", "_opt") | |
+ .replaceAll(" ", "_") | |
+ .replaceAll("]", "_opt") | |
+ .replaceAll("_*\\*", "s") | |
+ .replaceAll("_*\\+", "s") | |
+ .replaceAll("[^\\w]+", "_") | |
+ .replaceAll("_+", "_"); | |
+ while (s.startsWith("_")) { | |
+ s = s.replaceFirst("_", ""); | |
+ } | |
+ return s.isEmpty() ? "PlaceHolder" : s; | |
+ } | |
- } | |
+ } | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment