public static PsiElement[] parsePattern( Project project, String context, String pattern, FileType fileType, Language language, String extension, boolean physical) { int offset = context.indexOf(PATTERN_PLACEHOLDER); final int patternLength = pattern.length(); final String patternInContext = context.replace(PATTERN_PLACEHOLDER, pattern); final String ext = extension != null ? extension : fileType.getDefaultExtension(); final String name = "__dummy." + ext; final PsiFileFactory factory = PsiFileFactory.getInstance(project); final PsiFile file = language == null ? factory.createFileFromText( name, fileType, patternInContext, LocalTimeCounter.currentTime(), physical, true) : factory.createFileFromText(name, language, patternInContext, physical, true); if (file == null) { return PsiElement.EMPTY_ARRAY; } final List<PsiElement> result = new ArrayList<PsiElement>(); PsiElement element = file.findElementAt(offset); if (element == null) { return PsiElement.EMPTY_ARRAY; } PsiElement topElement = element; element = element.getParent(); while (element != null) { if (element.getTextRange().getStartOffset() == offset && element.getTextLength() <= patternLength) { topElement = element; } element = element.getParent(); } if (topElement instanceof PsiFile) { return topElement.getChildren(); } final int endOffset = offset + patternLength; result.add(topElement); topElement = topElement.getNextSibling(); while (topElement != null && topElement.getTextRange().getEndOffset() <= endOffset) { result.add(topElement); topElement = topElement.getNextSibling(); } return result.toArray(new PsiElement[result.size()]); }
@Override public void visitElement(PsiElement element) { super.visitElement(element); final EquivalenceDescriptorProvider descriptorProvider = EquivalenceDescriptorProvider.getInstance(element); if (descriptorProvider != null) { final EquivalenceDescriptor descriptor1 = descriptorProvider.buildDescriptor(element); final EquivalenceDescriptor descriptor2 = descriptorProvider.buildDescriptor(myGlobalVisitor.getElement()); if (descriptor1 != null && descriptor2 != null) { final boolean result = DuplocatorUtil.match( descriptor1, descriptor2, myGlobalVisitor, Collections.<PsiElementRole>emptySet(), null); myGlobalVisitor.setResult(result); return; } } if (isLiteral(element)) { visitLiteral(element); return; } if (canBePatternVariable(element) && myGlobalVisitor.getMatchContext().getPattern().isRealTypedVar(element) && !shouldIgnoreVarNode(element)) { PsiElement matchedElement = myGlobalVisitor.getElement(); PsiElement newElement = SkippingHandler.skipNodeIfNeccessary(matchedElement); while (newElement != matchedElement) { matchedElement = newElement; newElement = SkippingHandler.skipNodeIfNeccessary(matchedElement); } myGlobalVisitor.setResult(myGlobalVisitor.handleTypedElement(element, matchedElement)); } else if (element instanceof LeafElement) { myGlobalVisitor.setResult(element.getText().equals(myGlobalVisitor.getElement().getText())); } else if (element.getFirstChild() == null && element.getTextLength() == 0) { myGlobalVisitor.setResult(true); } else { PsiElement patternChild = element.getFirstChild(); PsiElement matchedChild = myGlobalVisitor.getElement().getFirstChild(); FilteringNodeIterator patternIterator = new SsrFilteringNodeIterator(patternChild); FilteringNodeIterator matchedIterator = new SsrFilteringNodeIterator(matchedChild); boolean matched = myGlobalVisitor.matchSequentially(patternIterator, matchedIterator); myGlobalVisitor.setResult(matched); } }
private static boolean isLiteral(PsiElement element) { if (element == null) return false; final ASTNode astNode = element.getNode(); if (astNode == null) { return false; } final IElementType elementType = astNode.getElementType(); final ParserDefinition parserDefinition = LanguageParserDefinitions.INSTANCE.forLanguage(element.getLanguage()); if (parserDefinition != null) { final TokenSet literals = parserDefinition.getStringLiteralElements(); return literals.contains(elementType); } return false; }
private static boolean checkErrorElements(PsiElement element) { final boolean[] result = {true}; final int endOffset = element.getTextRange().getEndOffset(); element.accept( new PsiRecursiveElementWalkingVisitor() { @Override public void visitElement(PsiElement element) { super.visitElement(element); if (element instanceof PsiErrorElement && element.getTextRange().getEndOffset() == endOffset) { result[0] = false; } } }); return result[0]; }
private void doVisitElement(PsiElement element) { CompiledPattern pattern = myGlobalVisitor.getContext().getPattern(); if (myGlobalVisitor.getCodeBlockLevel() == 0) { initTopLevelElement(element); return; } if (canBePatternVariable(element) && pattern.isRealTypedVar(element)) { myGlobalVisitor.handle(element); final MatchingHandler handler = pattern.getHandler(element); handler.setFilter( new NodeFilter() { public boolean accepts(PsiElement other) { return canBePatternVariableValue(other); } }); super.visitElement(element); return; } super.visitElement(element); if (myGlobalVisitor.getContext().getSearchHelper().doOptimizing() && element instanceof LeafElement) { ParserDefinition parserDefinition = LanguageParserDefinitions.INSTANCE.forLanguage(element.getLanguage()); if (parserDefinition != null) { String text = element.getText(); // todo: support variables inside comments boolean flag = true; if (StringUtil.isJavaIdentifier(text) && flag) { myGlobalVisitor.processTokenizedName( text, true, GlobalCompilingVisitor.OccurenceKind.CODE); } } } }
public void visitLiteral(PsiElement literal) { final PsiElement l2 = myGlobalVisitor.getElement(); MatchingHandler handler = (MatchingHandler) literal.getUserData(CompiledPattern.HANDLER_KEY); if (handler instanceof SubstitutionHandler) { int offset = 0; int length = l2.getTextLength(); final String text = l2.getText(); if (length > 2 && (text.charAt(0) == '"' && text.charAt(length - 1) == '"') || (text.charAt(0) == '\'' && text.charAt(length - 1) == '\'')) { length--; offset++; } myGlobalVisitor.setResult( ((SubstitutionHandler) handler) .handle(l2, offset, length, myGlobalVisitor.getMatchContext())); } else if (handler != null) { myGlobalVisitor.setResult(handler.match(literal, l2, myGlobalVisitor.getMatchContext())); } else { myGlobalVisitor.setResult(literal.textMatches(l2)); } }
private void visitLiteral(PsiElement literal) { String value = literal.getText(); if (value.length() > 2 && (value.charAt(0) == '"' && value.charAt(value.length() - 1) == '"') || (value.charAt(0) == '\'' && value.charAt(value.length() - 1) == '\'')) { if (mySubstitutionPatterns == null) { final String[] prefixes = myGlobalVisitor.getContext().getPattern().getTypedVarPrefixes(); mySubstitutionPatterns = createPatterns(prefixes); } for (Pattern substitutionPattern : mySubstitutionPatterns) { @Nullable MatchingHandler handler = myGlobalVisitor.processPatternStringWithFragments( value, GlobalCompilingVisitor.OccurenceKind.LITERAL, substitutionPattern); if (handler != null) { literal.putUserData(CompiledPattern.HANDLER_KEY, handler); break; } } } }
private static boolean checkOptionalChildren(PsiElement root) { final boolean[] result = {true}; root.accept( new PsiRecursiveElementWalkingVisitor() { @Override public void visitElement(PsiElement element) { super.visitElement(element); if (element instanceof LeafElement) { return; } final EquivalenceDescriptorProvider provider = EquivalenceDescriptorProvider.getInstance(element); if (provider == null) { return; } final EquivalenceDescriptor descriptor = provider.buildDescriptor(element); if (descriptor == null) { return; } for (SingleChildDescriptor childDescriptor : descriptor.getSingleChildDescriptors()) { if (childDescriptor.getType() == SingleChildDescriptor.MyType.OPTIONALLY_IN_PATTERN && childDescriptor.getElement() == null) { result[0] = false; } } for (MultiChildDescriptor childDescriptor : descriptor.getMultiChildDescriptors()) { if (childDescriptor.getType() == MultiChildDescriptor.MyType.OPTIONALLY_IN_PATTERN) { PsiElement[] elements = childDescriptor.getElements(); if (elements == null || elements.length == 0) { result[0] = false; } } } } }); return result[0]; }
private void initTopLevelElement(PsiElement element) { CompiledPattern pattern = myGlobalVisitor.getContext().getPattern(); PsiElement newElement = SkippingHandler.skipNodeIfNeccessary(element); if (element != newElement && newElement != null) { // way to support partial matching (ex. if ($condition$) ) newElement.accept(this); pattern.setHandler(element, new LightTopLevelMatchingHandler(pattern.getHandler(element))); } else { myGlobalVisitor.setCodeBlockLevel(myGlobalVisitor.getCodeBlockLevel() + 1); for (PsiElement el = element.getFirstChild(); el != null; el = el.getNextSibling()) { if (GlobalCompilingVisitor.getFilter().accepts(el)) { if (el instanceof PsiWhiteSpace) { myGlobalVisitor.addLexicalNode(el); } } else { el.accept(this); MatchingHandler matchingHandler = pattern.getHandler(el); pattern.setHandler( el, element == myTopElement ? new TopLevelMatchingHandler(matchingHandler) : new LightTopLevelMatchingHandler(matchingHandler)); /* do not assign light-top-level handlers through skipping, because it is incorrect; src: if (...) { st1; st2; } pattern: if (...) {$a$;} $a$ will have top-level handler, so matching will be considered as correct, although "st2;" is left! */ } } myGlobalVisitor.setCodeBlockLevel(myGlobalVisitor.getCodeBlockLevel() - 1); pattern.setHandler(element, new TopLevelMatchingHandler(pattern.getHandler(element))); } }
@Override public void compile(PsiElement[] elements, @NotNull final GlobalCompilingVisitor globalVisitor) { final PsiElement topElement = elements[0].getParent(); final PsiElement element = elements.length > 1 ? topElement : elements[0]; element.accept(new MyCompilingVisitor(globalVisitor, topElement)); element.accept( new PsiRecursiveElementVisitor() { @Override public void visitElement(PsiElement element) { super.visitElement(element); if (DuplocatorUtil.isIgnoredNode(element)) { return; } CompiledPattern pattern = globalVisitor.getContext().getPattern(); MatchingHandler handler = pattern.getHandler(element); if (!(handler instanceof SubstitutionHandler) && !(handler instanceof TopLevelMatchingHandler) && !(handler instanceof LightTopLevelMatchingHandler)) { pattern.setHandler(element, new SkippingHandler(handler)); } // todo: simplify logic /* place skipping handler under top-level handler, because when we skip top-level node we can get non top-level handler, so depth matching won't be done!; */ if (handler instanceof LightTopLevelMatchingHandler) { MatchingHandler delegate = ((LightTopLevelMatchingHandler) handler).getDelegate(); if (!(delegate instanceof SubstitutionHandler)) { pattern.setHandler( element, new LightTopLevelMatchingHandler(new SkippingHandler(delegate))); } } } }); final Language baseLanguage = element.getContainingFile().getLanguage(); // todo: try to optimize it: too heavy strategy! globalVisitor .getContext() .getPattern() .setStrategy( new MatchingStrategy() { @Override public boolean continueMatching(PsiElement start) { Language language = start.getLanguage(); PsiFile file = start.getContainingFile(); if (file != null) { Language fileLanguage = file.getLanguage(); if (fileLanguage.isKindOf(language)) { // dialect language = fileLanguage; } } return language == baseLanguage; } @Override public boolean shouldSkip(PsiElement element, PsiElement elementToMatchWith) { return DuplocatorUtil.shouldSkip(element, elementToMatchWith); } }); }
@Override public boolean canBeVarDelimeter(@NotNull PsiElement element) { final ASTNode node = element.getNode(); return node != null && getVariableDelimiters().contains(node.getElementType()); }
private static boolean canBePatternVariableValue(PsiElement element) { // can be leaf element! (ex. var a = 1 <-> var $a$ = 1) return !containsOnlyDelimeters(element.getText()); }