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))); } }
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 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]; }
@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); } }); }