@NotNull
  private FindResult findInCommentsAndLiterals(@NotNull CharSequence text,
                                               char[] textArray,
                                               int offset,
                                               @NotNull FindModel model,
                                               @NotNull final VirtualFile file) {
    ThreadLocal<SoftReference<CommentsLiteralsSearchData>> data;
    synchronized (model) {
      data = model.getUserData(ourCommentsLiteralsSearchDataKey);
      assert data != null;
    }

    FileType ftype = file.getFileType();
    Language lang = LanguageUtil.getLanguageForPsi(myProject, file, ftype);

    SoftReference<CommentsLiteralsSearchData> currentThreadDataRef = data.get();
    CommentsLiteralsSearchData currentThreadData = currentThreadDataRef == null ? null : currentThreadDataRef.get();
    if (currentThreadData == null || !Comparing.equal(currentThreadData.lastFile, file) || !currentThreadData.model.equals(model)) {
      SyntaxHighlighter highlighter = getHighlighter(file, lang);

      if (highlighter == null) {
        // no syntax highlighter -> no search
        return NOT_FOUND_RESULT;
      }

      TokenSet tokensOfInterest = TokenSet.EMPTY;
      Set<Language> relevantLanguages;
      if (lang != null) {
        final Language finalLang = lang;
        relevantLanguages = ReadAction.compute(() -> {
          Set<Language> result = new HashSet<>();
          FileViewProvider viewProvider = PsiManager.getInstance(myProject).findViewProvider(file);
          if (viewProvider != null) {
            result.addAll(viewProvider.getLanguages());
          }

          if (result.isEmpty()) {
            result.add(finalLang);
          }
          return result;
        });

        for (Language relevantLanguage : relevantLanguages) {
          tokensOfInterest = addTokenTypesForLanguage(model, relevantLanguage, tokensOfInterest);
        }
      }
      else {
        relevantLanguages = new HashSet<>();
        if (ftype instanceof AbstractFileType) {
          if (model.isInCommentsOnly()) {
            tokensOfInterest = TokenSet.create(CustomHighlighterTokenType.LINE_COMMENT, CustomHighlighterTokenType.MULTI_LINE_COMMENT);
          }
          if (model.isInStringLiteralsOnly()) {
            tokensOfInterest = TokenSet.orSet(tokensOfInterest, TokenSet
              .create(CustomHighlighterTokenType.STRING, CustomHighlighterTokenType.SINGLE_QUOTED_STRING));
          }
        }
      }

      Matcher matcher = model.isRegularExpressions() ? compileRegExp(model, "") : null;
      StringSearcher searcher = matcher != null ? null : new StringSearcher(model.getStringToFind(), model.isCaseSensitive(), true);
      LayeredLexer.ourDisableLayersFlag.set(Boolean.TRUE);

      try {
        SyntaxHighlighterOverEditorHighlighter highlighterAdapter =
          ReadAction.compute(() -> new SyntaxHighlighterOverEditorHighlighter(highlighter, file, myProject));
        currentThreadData =
          new CommentsLiteralsSearchData(file, relevantLanguages, highlighterAdapter, tokensOfInterest, searcher, matcher, model.clone());
        currentThreadData.highlighter.restart(text);
      }
      finally {
        LayeredLexer.ourDisableLayersFlag.set(null);
      }

      data.set(new SoftReference<>(currentThreadData));
    }

    int initialStartOffset = model.isForward() && currentThreadData.startOffset < offset ? currentThreadData.startOffset : 0;
    currentThreadData.highlighter.resetPosition(initialStartOffset);
    final Lexer lexer = currentThreadData.highlighter.getHighlightingLexer();

    IElementType tokenType;
    TokenSet tokens = currentThreadData.tokensOfInterest;

    int lastGoodOffset = 0;
    boolean scanningForward = model.isForward();
    FindResultImpl prevFindResult = NOT_FOUND_RESULT;

    while ((tokenType = lexer.getTokenType()) != null) {
      if (lexer.getState() == 0) lastGoodOffset = lexer.getTokenStart();

      final TextAttributesKey[] keys = currentThreadData.highlighter.getTokenHighlights(tokenType);

      if (tokens.contains(tokenType) ||
          model.isInStringLiteralsOnly() && ChunkExtractor.isHighlightedAsString(keys) ||
          model.isInCommentsOnly() && ChunkExtractor.isHighlightedAsComment(keys)
        ) {
        int start = lexer.getTokenStart();
        int end = lexer.getTokenEnd();
        if (model.isInStringLiteralsOnly()) { // skip literal quotes itself from matching
          char c = text.charAt(start);
          if (c == '"' || c == '\'') {
            while (start < end && c == text.charAt(start)) {
              ++start;
              if (c == text.charAt(end - 1) && start < end) --end;
            }
          }
        }

        final int tokenContentStart = start;

        while (true) {
          FindResultImpl findResult = null;

          if (currentThreadData.searcher != null) {
            int matchStart = currentThreadData.searcher.scan(text, textArray, start, end);

            if (matchStart != -1 && matchStart >= start) {
              final int matchEnd = matchStart + model.getStringToFind().length();
              if (matchStart >= offset || !scanningForward)
                findResult = new FindResultImpl(matchStart, matchEnd);
              else {
                start = matchEnd;
                continue;
              }
            }
          }
          else if (start <= end) {
            currentThreadData.matcher.reset(StringPattern.newBombedCharSequence(text.subSequence(tokenContentStart, end)));
            currentThreadData.matcher.region(start - tokenContentStart, end - tokenContentStart);
            currentThreadData.matcher.useTransparentBounds(true);
            if (currentThreadData.matcher.find()) {
              final int matchEnd = tokenContentStart + currentThreadData.matcher.end();
              int matchStart = tokenContentStart + currentThreadData.matcher.start();
              if (matchStart >= offset || !scanningForward) {
                findResult = new FindResultImpl(matchStart, matchEnd);
              }
              else {
                int diff = 0;
                if (start == end || start == matchEnd) {
                  diff = 1;
                }
                start = matchEnd + diff;
                continue;
              }
            }
          }

          if (findResult != null) {
            if (scanningForward) {
              currentThreadData.startOffset = lastGoodOffset;
              return findResult;
            }
            else {

              if (findResult.getEndOffset() >= offset) return prevFindResult;
              prevFindResult = findResult;
              start = findResult.getEndOffset();
              continue;
            }
          }
          break;
        }
      }
      else {
        Language tokenLang = tokenType.getLanguage();
        if (tokenLang != lang && tokenLang != Language.ANY && !currentThreadData.relevantLanguages.contains(tokenLang)) {
          tokens = addTokenTypesForLanguage(model, tokenLang, tokens);
          currentThreadData.tokensOfInterest = tokens;
          currentThreadData.relevantLanguages.add(tokenLang);
        }
      }

      lexer.advance();
    }

    return prevFindResult;
  }
@NotNull
  private FindResult findInCommentsAndLiterals(@NotNull CharSequence text,
                                               char[] textArray,
                                               int offset,
                                               @NotNull FindModel model,
                                               @NotNull final VirtualFile file)