@NotNull
  private static List<PsiElement> doCompile(@NotNull Project project,
                                            @NotNull MatchOptions options,
                                            @NotNull CompiledPattern result,
                                            @NotNull PrefixProvider prefixProvider,
                                            @NotNull CompileContext context,
                                            boolean checkForErrors) throws MalformedPatternException {
    result.clearHandlers();

    final StringBuilder buf = new StringBuilder();

    final Template template = TemplateManager.getInstance(project).createTemplate("", "", options.getSearchPattern());

    final int segmentsCount = template.getSegmentsCount();
    final String text = template.getTemplateText();
    int prevOffset = 0;
    final Set<String> variableNames = new HashSet<>();

    final LanguageFileType fileType = options.getFileType();
    assert fileType != null;
    for(int i = 0; i < segmentsCount; i++) {
      final int offset = template.getSegmentOffset(i);
      final String name = template.getSegmentName(i);

      final String prefix = prefixProvider.getPrefix(i);
      if (prefix == null) {
        if (checkForErrors) throw new MalformedPatternException();
        return Collections.emptyList();
      }

      final String compiledName = prefix + name;
      buf.append(text, prevOffset, offset).append(compiledName);

      final boolean repeated = !variableNames.add(name);
      final SubstitutionHandler existing = (SubstitutionHandler)result.getHandler(compiledName);
      if (existing != null) {
        existing.setRepeatedVar(repeated);
      }
      else {
        // the same variable can occur multiple times in a single template
        // no need to process it more than once
        try {
          MatchVariableConstraint constraint = options.getVariableConstraint(name);
          if (constraint == null) {
            // we do not edit the constraints
            constraint = options.addNewVariableConstraint(name);
          }

          final SubstitutionHandler handler = result.createSubstitutionHandler(
            name,
            compiledName,
            constraint.isPartOfSearchResults(),
            constraint.getMinCount(),
            constraint.getMaxCount(),
            constraint.isGreedy()
          );
          handler.setRepeatedVar(repeated);

          if (constraint.isWithinHierarchy()) {
            handler.setSubtype(true);
          }

          if (constraint.isStrictlyWithinHierarchy()) {
            handler.setStrictSubtype(true);
          }

          if (!StringUtil.isEmptyOrSpaces(constraint.getRegExp())) {
            MatchPredicate predicate = new RegExpPredicate(
              constraint.getRegExp(),
              options.isCaseSensitiveMatch(),
              name,
              constraint.isWholeWordsOnly(),
              constraint.isPartOfSearchResults()
            );
            if (constraint.isInvertRegExp()) {
              predicate = new NotPredicate(predicate);
            }
            addPredicate(handler, predicate);
          }

          if (!StringUtil.isEmptyOrSpaces(constraint.getReferenceConstraint())) {
            MatchPredicate predicate = new ReferencePredicate(constraint.getReferenceConstraint(), fileType, project);
            if (constraint.isInvertReference()) {
              predicate = new NotPredicate(predicate);
            }
            addPredicate(handler, predicate);
          }

          addExtensionPredicates(options, constraint, handler);
          addScriptConstraint(project, name, constraint, handler, variableNames, options, checkForErrors);

          if (!StringUtil.isEmptyOrSpaces(constraint.getContainsConstraint())) {
            MatchPredicate predicate = new ContainsPredicate(name, constraint.getContainsConstraint());
            if (constraint.isInvertContainsConstraint()) {
              predicate = new NotPredicate(predicate);
            }
            addPredicate(handler, predicate);
          }

          if (!StringUtil.isEmptyOrSpaces(constraint.getWithinConstraint())) {
            assert false;
          }
        } catch (MalformedPatternException e) {
          if (checkForErrors) throw e;
        }
      }
      prevOffset = offset;
    }

    final MatchVariableConstraint constraint = options.getVariableConstraint(Configuration.CONTEXT_VAR_NAME);
    if (constraint != null) {
      final SubstitutionHandler handler = result.createSubstitutionHandler(
        Configuration.CONTEXT_VAR_NAME,
        Configuration.CONTEXT_VAR_NAME,
        constraint.isPartOfSearchResults(),
        constraint.getMinCount(),
        constraint.getMaxCount(),
        constraint.isGreedy()
      );

      try {
        if (!StringUtil.isEmptyOrSpaces(constraint.getWithinConstraint())) {
          MatchPredicate predicate = new WithinPredicate(constraint.getWithinConstraint(), fileType, project);
          if (constraint.isInvertWithinConstraint()) {
            predicate = new NotPredicate(predicate);
          }
          addPredicate(handler, predicate);
        }

        addExtensionPredicates(options, constraint, handler);
        addScriptConstraint(project, Configuration.CONTEXT_VAR_NAME, constraint, handler, variableNames, options, checkForErrors);
      }
      catch (MalformedPatternException e) {
        if (checkForErrors) throw e;
      }
    }

    buf.append(text.substring(prevOffset));

    final PsiElement[] patternElements;
    try {
      final PatternContextInfo contextInfo = new PatternContextInfo(PatternTreeContext.Block,
                                                                    options.getPatternContext(),
                                                                    constraint != null ? constraint.getContextConstraint() : null);
      final Language dialect = options.getDialect();
      assert dialect != null;
      patternElements = MatcherImplUtil.createTreeFromText(buf.toString(), contextInfo, fileType, dialect, project, false);
      if (patternElements.length == 0 && checkForErrors) throw new MalformedPatternException();
    }
    catch (IncorrectOperationException e) {
      if (checkForErrors) throw new MalformedPatternException(e.getMessage());
      return Collections.emptyList();
    }

    final NodeFilter filter = LexicalNodesFilter.getInstance();
    final List<PsiElement> elements = new SmartList<>();
    for (PsiElement element : patternElements) {
      if (!filter.accepts(element)) {
        elements.add(element);
      }
    }

    final GlobalCompilingVisitor compilingVisitor = new GlobalCompilingVisitor();
    try {
      compilingVisitor.compile(elements.toArray(PsiElement.EMPTY_ARRAY), context);
    }
    catch (MalformedPatternException e) {
      if (checkForErrors) throw e;
    }
    new DeleteNodesAction(compilingVisitor.getLexicalNodes()).run();
    return elements;
  }
@NotNull
  private static List<PsiElement> doCompile(@NotNull Project project,
                                            @NotNull MatchOptions options,
                                            @NotNull CompiledPattern result,
                                            @NotNull PrefixProvider prefixProvider,
                                            @NotNull CompileContext context,
                                            boolean checkForErrors)