@Override
 public EditorHighlighter getHighlighter() {
   EditorColorsScheme scheme = EditorColorsManager.getInstance().getGlobalScheme();
   EditorHighlighter highlighter =
       HighlighterFactory.createHighlighter(myInjectedFile.getVirtualFile(), scheme, getProject());
   highlighter.setText(getDocument().getText());
   return highlighter;
 }
 @NotNull
 protected Runnable loadEditorInBackground() {
   EditorColorsScheme scheme = EditorColorsManager.getInstance().getGlobalScheme();
   EditorHighlighter highlighter =
       EditorHighlighterFactory.getInstance().createEditorHighlighter(myFile, scheme, myProject);
   EditorEx editor = (EditorEx) getEditor();
   highlighter.setText(editor.getDocument().getImmutableCharSequence());
   return () -> editor.setHighlighter(highlighter);
 }
  /**
   * Calculates number of unmatched left braces before the given offset.
   *
   * @param editor target editor
   * @param offset target offset
   * @param fileType target file type
   * @return number of unmatched braces before the given offset; negative value if it's not possible
   *     to perform the calculation or if there are no unmatched left braces before the given offset
   */
  protected static int getUnmatchedLBracesNumberBefore(
      Editor editor, int offset, FileType fileType) {
    if (offset == 0) {
      return -1;
    }
    CharSequence chars = editor.getDocument().getCharsSequence();
    if (chars.charAt(offset - 1) != '{') {
      return -1;
    }

    EditorHighlighter highlighter = ((EditorEx) editor).getHighlighter();
    HighlighterIterator iterator = highlighter.createIterator(offset - 1);
    BraceMatcher braceMatcher = BraceMatchingUtil.getBraceMatcher(fileType, iterator);

    if (!braceMatcher.isLBraceToken(iterator, chars, fileType)
        || !braceMatcher.isStructuralBrace(iterator, chars, fileType)) {
      return -1;
    }

    Language language = iterator.getTokenType().getLanguage();

    iterator = highlighter.createIterator(0);
    int lBracesBeforeOffset = 0;
    int lBracesAfterOffset = 0;
    int rBracesBeforeOffset = 0;
    int rBracesAfterOffset = 0;
    for (; !iterator.atEnd(); iterator.advance()) {
      IElementType tokenType = iterator.getTokenType();
      if (!tokenType.getLanguage().equals(language)
          || !braceMatcher.isStructuralBrace(iterator, chars, fileType)) {
        continue;
      }

      boolean beforeOffset = iterator.getStart() < offset;

      if (braceMatcher.isLBraceToken(iterator, chars, fileType)) {
        if (beforeOffset) {
          lBracesBeforeOffset++;
        } else {
          lBracesAfterOffset++;
        }
      } else if (braceMatcher.isRBraceToken(iterator, chars, fileType)) {
        if (beforeOffset) {
          rBracesBeforeOffset++;
        } else {
          rBracesAfterOffset++;
        }
      }
    }

    return lBracesBeforeOffset - rBracesBeforeOffset - (rBracesAfterOffset - lBracesAfterOffset);
  }
    @Override
    @NotNull
    public Map<TodoIndexEntry, Integer> map(@NotNull final FileContent inputData) {
      if (IndexPatternUtil.getIndexPatternCount() > 0) {
        final CharSequence chars = inputData.getContentAsText();
        final OccurrenceConsumer occurrenceConsumer = new OccurrenceConsumer(null, true);
        EditorHighlighter highlighter;

        final EditorHighlighter editorHighlighter = inputData.getUserData(EDITOR_HIGHLIGHTER);
        if (editorHighlighter != null
            && checkCanUseCachedEditorHighlighter(chars, editorHighlighter)) {
          highlighter = editorHighlighter;
        } else {
          highlighter = HighlighterFactory.createHighlighter(inputData.getProject(), myFile);
          highlighter.setText(chars);
        }

        final int documentLength = chars.length();
        BaseFilterLexer.TodoScanningState todoScanningState = null;
        final HighlighterIterator iterator = highlighter.createIterator(0);

        while (!iterator.atEnd()) {
          final IElementType token = iterator.getTokenType();

          if (myCommentTokens.contains(token) || CacheUtil.isInComments(token)) {
            int start = iterator.getStart();
            if (start >= documentLength) break;
            int end = iterator.getEnd();

            todoScanningState =
                BaseFilterLexer.advanceTodoItemsCount(
                    chars.subSequence(start, Math.min(end, documentLength)),
                    occurrenceConsumer,
                    todoScanningState);
            if (end > documentLength) break;
          }
          iterator.advance();
        }
        final Map<TodoIndexEntry, Integer> map = new HashMap<>();
        for (IndexPattern pattern : IndexPatternUtil.getIndexPatterns()) {
          final int count = occurrenceConsumer.getOccurrenceCount(pattern);
          if (count > 0) {
            map.put(
                new TodoIndexEntry(pattern.getPatternString(), pattern.isCaseSensitive()), count);
          }
        }
        return map;
      }
      return Collections.emptyMap();
    }
 public HighlighterRangeIterator(
     @NotNull EditorHighlighter highlighter, int startOffset, int endOffset) {
   myStartOffset = startOffset;
   myEndOffset = endOffset;
   myIterator = highlighter.createIterator(startOffset);
   skipBadCharacters();
 }
 public static List<IElementType> getAllTokens(EditorHighlighter highlighter) {
   List<IElementType> tokens = new ArrayList<IElementType>();
   HighlighterIterator iterator = highlighter.createIterator(0);
   while (!iterator.atEnd()) {
     tokens.add(iterator.getTokenType());
     iterator.advance();
   }
   return tokens;
 }
 private void paintBorderEffect(
     Graphics2D g,
     ClipDetector clipDetector,
     EditorHighlighter highlighter,
     int clipStartOffset,
     int clipEndOffset) {
   HighlighterIterator it = highlighter.createIterator(clipStartOffset);
   while (!it.atEnd() && it.getStart() < clipEndOffset) {
     TextAttributes attributes = it.getTextAttributes();
     if (isBorder(attributes)) {
       paintBorderEffect(g, clipDetector, it.getStart(), it.getEnd(), attributes);
     }
     it.advance();
   }
 }
 public void paintHighlighters(EditorHighlighter highlighter) {
   int startOffset = startOfLineByOffset(myStartOffset);
   if (startOffset < 0 || startOffset >= myEditor.getDocument().getTextLength()) return;
   RangeIterator iterator =
       new RangeIterator(
           new FoldingOrNewLineGaps(myEditor),
           SAME_COLOR_BOXES,
           highlighter.createIterator(startOffset),
           BOX_FILTER);
   iterator.init(myRange);
   while (!iterator.atEnd()) {
     iterator.advance();
     paintBorder(
         myGraphics,
         myEditor,
         iterator.getStart(),
         iterator.getEnd(),
         iterator.getTextAttributes().getEffectColor(),
         iterator.getTextAttributes().getEffectType());
   }
 }
  @Nullable
  @Override
  public TextBlockTransferableData collectTransferableData(
      PsiFile file, Editor editor, int[] startOffsets, int[] endOffsets) {
    if (!Registry.is("editor.richcopy.enable")) {
      return null;
    }

    try {
      for (TextWithMarkupBuilder builder : myBuilders) {
        builder.reset();
      }
      SelectionModel selectionModel = editor.getSelectionModel();
      if (selectionModel.hasBlockSelection()) {
        return null; // unsupported legacy mode
      }

      RichCopySettings settings = RichCopySettings.getInstance();
      List<Caret> carets = editor.getCaretModel().getAllCarets();
      Caret firstCaret = carets.get(0);
      final int indentSymbolsToStrip;
      final int firstLineStartOffset;
      if (settings.isStripIndents() && carets.size() == 1) {
        Pair<Integer, Integer> p =
            calcIndentSymbolsToStrip(
                editor.getDocument(), firstCaret.getSelectionStart(), firstCaret.getSelectionEnd());
        firstLineStartOffset = p.first;
        indentSymbolsToStrip = p.second;
      } else {
        firstLineStartOffset = firstCaret.getSelectionStart();
        indentSymbolsToStrip = 0;
      }
      logInitial(editor, startOffsets, endOffsets, indentSymbolsToStrip, firstLineStartOffset);
      CharSequence text = editor.getDocument().getCharsSequence();
      EditorColorsScheme schemeToUse = settings.getColorsScheme(editor.getColorsScheme());
      EditorHighlighter highlighter =
          HighlighterFactory.createHighlighter(
              file.getVirtualFile(), schemeToUse, file.getProject());
      highlighter.setText(text);
      MarkupModel markupModel =
          DocumentMarkupModel.forDocument(editor.getDocument(), file.getProject(), false);
      Context context = new Context(text, schemeToUse, indentSymbolsToStrip);
      int shift = 0;
      int endOffset = 0;
      Caret prevCaret = null;

      for (Caret caret : carets) {
        int caretSelectionStart = caret.getSelectionStart();
        int caretSelectionEnd = caret.getSelectionEnd();
        int startOffsetToUse;
        if (caret == firstCaret) {
          startOffsetToUse = firstLineStartOffset;
        } else {
          startOffsetToUse = caretSelectionStart;
          assert prevCaret != null;
          String prevCaretSelectedText = prevCaret.getSelectedText();
          // Block selection fills short lines by white spaces.
          int fillStringLength =
              prevCaretSelectedText == null
                  ? 0
                  : prevCaretSelectedText.length()
                      - (prevCaret.getSelectionEnd() - prevCaret.getSelectionStart());
          int endLineOffset = endOffset + shift + fillStringLength;
          context.builder.addText(endLineOffset, endLineOffset + 1);
          shift++; // Block selection ends '\n' at line end
          shift += fillStringLength;
        }
        shift += endOffset - caretSelectionStart;
        endOffset = caretSelectionEnd;
        context.reset(shift);
        prevCaret = caret;
        if (endOffset <= startOffsetToUse) {
          continue;
        }
        MarkupIterator markupIterator =
            new MarkupIterator(
                text,
                new CompositeRangeIterator(
                    schemeToUse,
                    new HighlighterRangeIterator(highlighter, startOffsetToUse, endOffset),
                    new MarkupModelRangeIterator(
                        markupModel, schemeToUse, startOffsetToUse, endOffset)),
                schemeToUse);
        try {
          context.iterate(markupIterator, endOffset);
        } finally {
          markupIterator.dispose();
        }
      }
      SyntaxInfo syntaxInfo = context.finish();
      logSyntaxInfo(syntaxInfo);

      for (TextWithMarkupBuilder builder : myBuilders) {
        builder.build(syntaxInfo);
      }
    } catch (Exception e) {
      // catching the exception so that the rest of copy/paste functionality can still work fine
      LOG.error(e);
    }
    return null;
  }
  protected String addTextRangeToHistory(
      TextRange textRange, final EditorEx consoleEditor, boolean preserveMarkup) {
    final Document history = myHistoryViewer.getDocument();
    final MarkupModel markupModel = DocumentMarkupModel.forDocument(history, myProject, true);
    if (myPrompt != null) {
      appendToHistoryDocument(history, myPrompt);
    }
    markupModel.addRangeHighlighter(
        history.getTextLength() - StringUtil.length(myPrompt),
        history.getTextLength(),
        HighlighterLayer.SYNTAX,
        ConsoleViewContentType.USER_INPUT.getAttributes(),
        HighlighterTargetArea.EXACT_RANGE);
    final int localStartOffset = textRange.getStartOffset();
    String text;
    EditorHighlighter highlighter;
    if (consoleEditor instanceof EditorWindow) {
      EditorWindow editorWindow = (EditorWindow) consoleEditor;
      EditorColorsScheme scheme = EditorColorsManager.getInstance().getGlobalScheme();
      PsiFile file = editorWindow.getInjectedFile();
      final VirtualFile virtualFile = file.getVirtualFile();
      assert virtualFile != null;
      highlighter = HighlighterFactory.createHighlighter(virtualFile, scheme, getProject());
      String fullText = InjectedLanguageUtil.getUnescapedText(file, null, null);
      highlighter.setText(fullText);
      text = textRange.substring(fullText);
    } else {
      text = consoleEditor.getDocument().getText(textRange);
      highlighter = consoleEditor.getHighlighter();
    }
    // offset can be changed after text trimming after insert due to buffer constraints
    appendToHistoryDocument(history, text);
    int offset = history.getTextLength() - text.length();

    final HighlighterIterator iterator = highlighter.createIterator(localStartOffset);
    final int localEndOffset = textRange.getEndOffset();
    while (!iterator.atEnd()) {
      final int itStart = iterator.getStart();
      if (itStart > localEndOffset) break;
      final int itEnd = iterator.getEnd();
      if (itEnd >= localStartOffset) {
        final int start = Math.max(itStart, localStartOffset) - localStartOffset + offset;
        final int end = Math.min(itEnd, localEndOffset) - localStartOffset + offset;
        markupModel.addRangeHighlighter(
            start,
            end,
            HighlighterLayer.SYNTAX,
            iterator.getTextAttributes(),
            HighlighterTargetArea.EXACT_RANGE);
      }
      iterator.advance();
    }
    if (preserveMarkup) {
      duplicateHighlighters(
          markupModel,
          DocumentMarkupModel.forDocument(consoleEditor.getDocument(), myProject, true),
          offset,
          textRange);
      duplicateHighlighters(markupModel, consoleEditor.getMarkupModel(), offset, textRange);
    }
    if (!text.endsWith("\n")) {
      appendToHistoryDocument(history, "\n");
    }
    return text;
  }