private static int @NotNull [] getTextOccurrences(@NotNull CharSequence text,
                                                    int startOffset,
                                                    int endOffset,
                                                    @NotNull StringSearcher searcher) {
    if (endOffset > text.length()) {
      throw new IllegalArgumentException("end: " + endOffset + " > length: " + text.length());
    }
    Map<StringSearcher, int[]> cachedMap = cache.get(text);
    int[] cachedOccurrences = cachedMap == null ? null : cachedMap.get(searcher);
    boolean hasCachedOccurrences = cachedOccurrences != null && cachedOccurrences[0] <= startOffset && cachedOccurrences[1] >= endOffset;
    if (!hasCachedOccurrences) {
      IntList occurrences = new IntArrayList();
      int newStart = Math.min(startOffset, cachedOccurrences == null ? startOffset : cachedOccurrences[0]);
      int newEnd = Math.max(endOffset, cachedOccurrences == null ? endOffset : cachedOccurrences[1]);
      occurrences.add(newStart);
      occurrences.add(newEnd);
      for (int index = newStart; index < newEnd; index++) {
        ProgressManager.checkCanceled();
        //noinspection AssignmentToForLoopParameter
        index = searcher.scan(text, index, newEnd);
        if (index < 0) break;
        if (checkJavaIdentifier(text, searcher, index)) {
          occurrences.add(index);
        }
      }
      cachedOccurrences = occurrences.toIntArray();
      if (cachedMap == null) {
        cachedMap = ConcurrencyUtil.cacheOrGet(cache, text, CollectionFactory.createConcurrentSoftMap());
      }
      cachedMap.put(searcher, cachedOccurrences);
    }
    IntList offsets = new IntArrayList(cachedOccurrences.length - 2);
    for (int i = 2; i < cachedOccurrences.length; i++) {
      int occurrence = cachedOccurrences[i];
      if (occurrence > endOffset - searcher.getPatternLength()) break;
      if (occurrence >= startOffset) {
        offsets.add(occurrence);
      }
    }
    return offsets.toIntArray();
  }
private static int @NotNull [] getTextOccurrences(@NotNull CharSequence text,
                                                    int startOffset,
                                                    int endOffset,
                                                    @NotNull StringSearcher searcher)