private static @NotNull List<InjectedLanguageUtilBase.TokenInfo>
          obtainHighlightTokensFromLexer(@NotNull CharSequence outChars,
                                         @NotNull VirtualFileWindow virtualFile,
                                         @NotNull Project project,
                                         @NotNull List<? extends PlaceInfo> placeInfos) {
    VirtualFile file = (VirtualFile)virtualFile;
    FileType fileType = file.getFileType();
    EditorHighlighterProvider provider = FileTypeEditorHighlighterProviders.getInstance().forFileType(fileType);
    EditorColorsScheme scheme = EditorColorsManager.getInstance().getGlobalScheme();
    EditorHighlighter highlighter = provider.getEditorHighlighter(project, fileType, file, scheme);
    highlighter.setText(outChars);
    HighlighterIterator iterator = highlighter.createIterator(0);
    int hostNum = -1;
    int prevHostEndOffset = 0;
    LiteralTextEscaper<?> escaper = null;
    int prefixLength = 0;
    int suffixLength = 0;
    TextRange rangeInsideHost = null;
    int shredEndOffset = -1;
    List<InjectedLanguageUtilBase.TokenInfo> tokens = new ArrayList<>(outChars.length()/5); // avg. token per 5 chars
    while (!iterator.atEnd()) {
      IElementType tokenType = iterator.getTokenType();
      TextRange range = new ProperTextRange(iterator.getStart(), iterator.getEnd());
      while (range != null && !range.isEmpty()) {
        if (range.getStartOffset() >= shredEndOffset) {
          hostNum++;
          PlaceInfo info = placeInfos.get(hostNum);
          shredEndOffset = info.rangeInDecodedPSI.getEndOffset();
          prevHostEndOffset = range.getStartOffset();
          escaper = info.myEscaper;
          rangeInsideHost = info.rangeInHostElement;
          prefixLength = info.prefix.length();
          suffixLength = info.suffix.length();
        }
        //in prefix/suffix or spills over to next fragment
        if (range.getStartOffset() < prevHostEndOffset + prefixLength) {
          range = new UnfairTextRange(prevHostEndOffset + prefixLength, range.getEndOffset());
        }
        TextRange spilled = null;
        if (range.getEndOffset() > shredEndOffset - suffixLength) {
          spilled = new UnfairTextRange(shredEndOffset, range.getEndOffset());
          range = new UnfairTextRange(range.getStartOffset(), shredEndOffset-suffixLength);
        }
        if (!range.isEmpty()) {
          int start = escaper.getOffsetInHost(range.getStartOffset() - prevHostEndOffset - prefixLength, rangeInsideHost);
          if (start == -1) start = rangeInsideHost.getStartOffset();
          int end = escaper.getOffsetInHost(range.getEndOffset() - prevHostEndOffset - prefixLength, rangeInsideHost);
          if (end == -1) {
            end = rangeInsideHost.getEndOffset();
            prevHostEndOffset = shredEndOffset;
          }
          ProperTextRange rangeInHost = new ProperTextRange(start, end);
          tokens.add(new InjectedLanguageUtilBase.TokenInfo(tokenType, rangeInHost, hostNum,
                                                            iterator.getTextAttributesKeys()));
        }
        range = spilled;
      }
      iterator.advance();
    }
    return tokens;
  }
private static @NotNull List<InjectedLanguageUtilBase.TokenInfo>
          obtainHighlightTokensFromLexer(@NotNull CharSequence outChars,
                                         @NotNull VirtualFileWindow virtualFile,
                                         @NotNull Project project,
                                         @NotNull List<? extends PlaceInfo> placeInfos)