<commit_before>/*
 * Created by IntelliJ IDEA.
 * User: mike
 * Date: Sep 27, 2002
 * Time: 3:10:17 PM
 * To change template for new class use 
 * Code Style | Class Templates options (Tools | IDE Options).
 */
package com.intellij.codeInsight.highlighting;

import com.intellij.codeInsight.CodeInsightSettings;
import com.intellij.codeInsight.hint.EditorFragmentComponent;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.LogicalPosition;
import com.intellij.openapi.editor.colors.CodeInsightColors;
import com.intellij.openapi.editor.colors.EditorColorsManager;
import com.intellij.openapi.editor.colors.EditorColorsScheme;
import com.intellij.openapi.editor.ex.DocumentEx;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.editor.highlighter.EditorHighlighter;
import com.intellij.openapi.editor.highlighter.HighlighterIterator;
import com.intellij.openapi.editor.markup.*;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.Computable;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiPlainTextFile;
import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
import com.intellij.psi.tree.IElementType;
import com.intellij.util.Alarm;
import com.intellij.util.Processor;
import com.intellij.concurrency.Job;
import com.intellij.concurrency.JobScheduler;
import org.jetbrains.annotations.NotNull;

import java.awt.*;
import java.util.ArrayList;
import java.util.List;

public class BraceHighlightingHandler {
  private static final Key<List<RangeHighlighter>> BRACE_HIGHLIGHTERS_IN_EDITOR_VIEW_KEY = Key.create("BraceHighlighter.BRACE_HIGHLIGHTERS_IN_EDITOR_VIEW_KEY");
  private static final Key<RangeHighlighter> LINE_MARKER_IN_EDITOR_KEY = Key.create("BraceHighlighter.LINE_MARKER_IN_EDITOR_KEY");

  private final Project myProject;
  private final Editor myEditor;
  private final Alarm myAlarm;

  private final DocumentEx myDocument;
  private final PsiFile myPsiFile;
  private FileType myFileType;
  private final CodeInsightSettings myCodeInsightSettings;

  private BraceHighlightingHandler(@NotNull Project project, @NotNull Editor editor, @NotNull Alarm alarm, PsiFile psiFile) {
    myProject = project;

    myEditor = editor;
    myAlarm = alarm;
    myDocument = (DocumentEx)myEditor.getDocument();

    myPsiFile = psiFile;
    myCodeInsightSettings = CodeInsightSettings.getInstance();
    if (myPsiFile != null) {
      myFileType = myPsiFile.getFileType();
    }
  }

  public static void lookForInjectedAndMatchBracesInOtherThread(@NotNull final Editor editor, @NotNull final Alarm alarm, final Processor<BraceHighlightingHandler> processor) {
    final Project project = editor.getProject();
    if (project == null) return;
    final int offset = editor.getCaretModel().getOffset();
    Job<Object> job = JobScheduler.getInstance().createJob("Brace highlighter", Job.DEFAULT_PRIORITY);
    job.addTask(new Runnable() {
      public void run() {
        final PsiFile injected = ApplicationManager.getApplication().runReadAction(new Computable<PsiFile>() {
          public PsiFile compute() {
            Document document = editor.getDocument();
            PsiFile psiFile = PsiDocumentManager.getInstance(project).getPsiFile(document);
            return getInjectedFileIfAny(editor, project, offset, psiFile, alarm);
          }
        });
        ApplicationManager.getApplication().invokeLater(new Runnable(){
          public void run() {
            Project editorProject = editor.getProject();
            if (editorProject != null && !editorProject.isDisposed() && !project.isDisposed() && editor.getComponent().isShowing() && !editor.isViewer()) {
              Editor newEditor = InjectedLanguageUtil.getInjectedEditorForInjectedFileIfAny(editor, injected);
              BraceHighlightingHandler handler = new BraceHighlightingHandler(project, newEditor, alarm, injected);
              processor.process(handler);
            }
          }
        }, ModalityState.stateForComponent(editor.getComponent()));
      }
    });
    job.schedule();
  }

  private static final Alarm ourAlarm = new Alarm();
  public static PsiFile getInjectedFileIfAny(final Editor editor, final Project project, int offset, PsiFile psiFile, final Alarm alarm) {
    Document document = editor.getDocument();
    // when document is committed, try to highlight braces in injected lang - it's fast
    if (!PsiDocumentManager.getInstance(project).isUncommited(document)) {
      PsiFile injected = InjectedLanguageUtil.findInjectedPsiAt(psiFile, offset);
      if (injected != null) {
        return injected;
      }
    }
    else if (alarm != null) {
      ourAlarm.cancelAllRequests();
      ourAlarm.addRequest(new Runnable(){
        public void run() {
          if (!project.isDisposed() && !editor.isDisposed()) {
            PsiDocumentManager.getInstance(project).commitAllDocuments();
            BraceHighlighter.updateBraces(editor, alarm);
          }
        }
      }, 300, ModalityState.stateForComponent(editor.getComponent()));
    }
    return psiFile;
  }

  public void updateBraces() {
    if (myPsiFile == null) return;

    clearBraceHighlighters();

    if (!myCodeInsightSettings.HIGHLIGHT_BRACES) return;

    if (myEditor.getSelectionModel().hasSelection()) return;

    int offset = myEditor.getCaretModel().getOffset();
    final CharSequence chars = myEditor.getDocument().getCharsSequence();

    if (myEditor.offsetToLogicalPosition(offset).column != myEditor.getCaretModel().getLogicalPosition().column) {
      // we are in virtual space
      final int caretLineNumber = myEditor.getCaretModel().getLogicalPosition().line;
      if (caretLineNumber >= myDocument.getLineCount()) return;
      offset = myDocument.getLineEndOffset(caretLineNumber) + myDocument.getLineSeparatorLength(caretLineNumber);
    }

    final int originalOffset = offset;

    HighlighterIterator iterator = getEditorHighlighter().createIterator(offset);

    if (iterator.atEnd()) {
      offset--;
    }
    else if (BraceMatchingUtil.isRBraceToken(iterator, chars, myFileType)) {
      offset--;
    }
    else if (!BraceMatchingUtil.isLBraceToken(iterator, chars, myFileType)) {
      offset--;

      if (offset >= 0) {
        final HighlighterIterator i = getEditorHighlighter().createIterator(offset);
        if (!BraceMatchingUtil.isRBraceToken(i, chars, myFileType)) offset++;
      }
    }

    if (offset < 0) {
      removeLineMarkers();
      return;
    }

    iterator = getEditorHighlighter().createIterator(offset);

    myAlarm.cancelAllRequests();

    if (BraceMatchingUtil.isLBraceToken(iterator, chars, myFileType) ||
        BraceMatchingUtil.isRBraceToken(iterator, chars, myFileType)) {
      doHighlight(offset, originalOffset);
    }

    //highlight scope
    if (!myCodeInsightSettings.HIGHLIGHT_SCOPE) {
      removeLineMarkers();
      return;
    }

    final int _offset = offset;
    myAlarm.addRequest(new Runnable() {
      public void run() {
        if (!myProject.isDisposed() && !myEditor.isDisposed()) {
          highlightScope(_offset);
        }
      }
    }, 300);
  }

  EditorHighlighter getEditorHighlighter() {
    return ((EditorEx)myEditor).getHighlighter();
  }

  private void highlightScope(int offset) {
    if (myEditor.getFoldingModel().isOffsetCollapsed(offset)) return;
    if (myEditor.getDocument().getTextLength() <= offset) return;

    HighlighterIterator iterator = getEditorHighlighter().createIterator(offset);
    final CharSequence chars = myDocument.getCharsSequence();

    if (!BraceMatchingUtil.isStructuralBraceToken(myFileType, iterator, chars)) {
//      if (BraceMatchingUtil.isRBraceTokenToHighlight(myFileType, iterator) || BraceMatchingUtil.isLBraceTokenToHighlight(myFileType, iterator)) return;
    }
    else {
      if (BraceMatchingUtil.isRBraceToken(iterator, chars, myFileType) || BraceMatchingUtil.isLBraceToken(iterator, chars, myFileType)) return;
    }

    if (!BraceMatchingUtil.findStructuralLeftBrace(myFileType, iterator, chars)) {
      removeLineMarkers();
      return;
    }

    highlightLeftBrace(iterator, true);
  }

  private void doHighlight(int offset, int originalOffset) {
    if (myEditor.getFoldingModel().isOffsetCollapsed(offset)) return;

    HighlighterIterator iterator = getEditorHighlighter().createIterator(offset);
    final CharSequence chars = myDocument.getCharsSequence();

    if (BraceMatchingUtil.isLBraceToken(iterator, chars, myFileType)) {
      IElementType tokenType = iterator.getTokenType();

      iterator.advance();
      if (!iterator.atEnd() && BraceMatchingUtil.isRBraceToken(iterator, chars, myFileType)) {
        if (BraceMatchingUtil.isPairBraces(tokenType, iterator.getTokenType(), myFileType) && originalOffset == iterator.getStart()) return;
      }

      iterator.retreat();
      highlightLeftBrace(iterator, false);

      if (offset > 0) {
        iterator = getEditorHighlighter().createIterator(offset - 1);
        if (BraceMatchingUtil.isRBraceToken(iterator, chars, myFileType)) {
          highlightRightBrace(iterator);
        }
      }
    }
    else if (BraceMatchingUtil.isRBraceToken(iterator, chars, myFileType)) {
      highlightRightBrace(iterator);
    }
  }

  private void highlightRightBrace(HighlighterIterator iterator) {
    int brace1End = iterator.getEnd();

    boolean matched = BraceMatchingUtil.matchBrace(myDocument.getCharsSequence(), myFileType, iterator, false);

    int brace2Start;

    if (!iterator.atEnd()) {
      brace2Start = iterator.getStart();
    }
    else {
      brace2Start = -1;
    }

    highlightBraces(brace2Start, brace1End - 1, matched, false);
  }

  private void highlightLeftBrace(HighlighterIterator iterator, boolean scopeHighlighting) {
    int brace1Start = iterator.getStart();
    boolean matched = BraceMatchingUtil.matchBrace(myDocument.getCharsSequence(), myFileType, iterator, true);

    int brace2End;
    if (!iterator.atEnd()) {
      brace2End = iterator.getEnd() - 1;
    }
    else {
      brace2End = -1;
    }

    highlightBraces(brace1Start, brace2End, matched, scopeHighlighting);
  }

  private void highlightBraces(final int lBraceOffset, int rBraceOffset, boolean matched, boolean scopeHighlighting) {
    EditorColorsScheme scheme = EditorColorsManager.getInstance().getGlobalScheme();
    final TextAttributes attributes =
        matched ? scheme.getAttributes(CodeInsightColors.MATCHED_BRACE_ATTRIBUTES)
        : scheme.getAttributes(CodeInsightColors.UNMATCHED_BRACE_ATTRIBUTES);

    if (rBraceOffset >= 0 && !scopeHighlighting) {
      highlightBrace(rBraceOffset, matched);
    }

    if (lBraceOffset >= 0) {
      if (!scopeHighlighting) {
        highlightBrace(lBraceOffset, matched);
      }
    }

    if (!myEditor.equals(FileEditorManager.getInstance(myProject).getSelectedTextEditor())) {
      return;
    }

    if (lBraceOffset >= 0 && rBraceOffset >= 0) {
      final int startLine = myEditor.offsetToLogicalPosition(lBraceOffset).line;
      final int endLine = myEditor.offsetToLogicalPosition(rBraceOffset).line;
      if (endLine - startLine > 0) {
        final Runnable runnable = new Runnable() {
          public void run() {
            if (myProject.isDisposed() || myEditor.isDisposed()) return;
            Color color = attributes.getBackgroundColor();
            if (color == null) return;
            color = color.darker();
            lineMarkFragment(startLine, endLine, color);
          }
        };

        if (!scopeHighlighting) {
          myAlarm.addRequest(runnable, 300);
        }
        else {
          runnable.run();
        }
      }
      else {
        if (!myCodeInsightSettings.HIGHLIGHT_SCOPE) {
          removeLineMarkers();
        }
      }

      if (!scopeHighlighting) {
        showScopeHint(lBraceOffset, lBraceOffset + 1);
      }
    }
    else {
      if (!myCodeInsightSettings.HIGHLIGHT_SCOPE) {
        removeLineMarkers();
      }
    }
  }

  private void highlightBrace(int rBraceOffset, boolean matched) {
    EditorColorsScheme scheme = EditorColorsManager.getInstance().getGlobalScheme();
    final TextAttributes attributes =
        matched ? scheme.getAttributes(CodeInsightColors.MATCHED_BRACE_ATTRIBUTES)
        : scheme.getAttributes(CodeInsightColors.UNMATCHED_BRACE_ATTRIBUTES);


    RangeHighlighter rbraceHighlighter =
        myEditor.getMarkupModel().addRangeHighlighter(
            rBraceOffset, rBraceOffset + 1, HighlighterLayer.LAST + 1, attributes, HighlighterTargetArea.EXACT_RANGE);
    rbraceHighlighter.setGreedyToLeft(false);
    rbraceHighlighter.setGreedyToRight(false);
    registerHighlighter(rbraceHighlighter);
  }

  private void registerHighlighter(RangeHighlighter highlighter) {
    List<RangeHighlighter> highlighters = myEditor.getUserData(BRACE_HIGHLIGHTERS_IN_EDITOR_VIEW_KEY);
    if (highlighters == null) {
      highlighters = new ArrayList<RangeHighlighter>();
      myEditor.putUserData(BRACE_HIGHLIGHTERS_IN_EDITOR_VIEW_KEY, highlighters);
    }

    highlighters.add(highlighter);
  }

  private void showScopeHint(final int lbraceStart, final int lbraceEnd) {
    LogicalPosition bracePosition = myEditor.offsetToLogicalPosition(lbraceStart);
    Point braceLocation = myEditor.logicalPositionToXY(bracePosition);
    final int y = braceLocation.y;
    myAlarm.addRequest(
        new Runnable() {
          public void run() {
            if (!myEditor.getComponent().isShowing()) return;
            Rectangle viewRect = myEditor.getScrollingModel().getVisibleArea();
            if (y < viewRect.y) {
              int start = lbraceStart;
              if (!(myPsiFile instanceof PsiPlainTextFile)) {
                PsiDocumentManager.getInstance(myProject).commitAllDocuments();
                start = BraceMatchingUtil.getBraceMatcher(myFileType).getCodeConstructStart(myPsiFile, lbraceStart);
              }
              TextRange range = new TextRange(start, lbraceEnd);
              int line1 = myDocument.getLineNumber(range.getStartOffset());
              int line2 = myDocument.getLineNumber(range.getEndOffset());
              line1 = Math.max(line1, line2 - 5);
              range = new TextRange(myDocument.getLineStartOffset(line1), range.getEndOffset());
              EditorFragmentComponent.showEditorFragmentHint(myEditor, range, true);
            }
          }
        },
        300, ModalityState.stateForComponent(myEditor.getComponent()));
  }

  public void clearBraceHighlighters() {
    List<RangeHighlighter> highlighters = myEditor.getUserData(BRACE_HIGHLIGHTERS_IN_EDITOR_VIEW_KEY);
    if (highlighters == null) return;
    myEditor.putUserData(BRACE_HIGHLIGHTERS_IN_EDITOR_VIEW_KEY, null);
    for (final Object highlighter : highlighters) {
      RangeHighlighter rangeHighlighter = (RangeHighlighter)highlighter;
      myEditor.getMarkupModel().removeHighlighter(rangeHighlighter);
    }
  }

  private void lineMarkFragment(int startLine, int endLine, Color color) {
    removeLineMarkers();

    if (startLine >= endLine || endLine >= myDocument.getLineCount()) return;

    int startOffset = myDocument.getLineStartOffset(startLine);
    int endOffset = myDocument.getLineStartOffset(endLine);

    RangeHighlighter highlighter = myEditor.getMarkupModel().addRangeHighlighter(startOffset, endOffset, 0, null, HighlighterTargetArea.LINES_IN_RANGE);
    highlighter.setLineMarkerRenderer(new MyLineMarkerRenderer(color));
    myEditor.putUserData(LINE_MARKER_IN_EDITOR_KEY, highlighter);
  }

  private void removeLineMarkers() {
    RangeHighlighter marker = myEditor.getUserData(LINE_MARKER_IN_EDITOR_KEY);
    if (marker != null) {
      myEditor.getMarkupModel().removeHighlighter(marker);
      myEditor.putUserData(LINE_MARKER_IN_EDITOR_KEY, null);
    }
  }

  private static class MyLineMarkerRenderer implements LineMarkerRenderer {
    private static final int DEEPNESS = 2;
    private static final int THICKNESS = 2;
    private final Color myColor;

    public MyLineMarkerRenderer(Color color) {
      myColor = color;
    }

    public void paint(Editor editor, Graphics g, Rectangle r) {
      int height = r.height + editor.getLineHeight();
      g.setColor(myColor);
      g.fillRect(r.x, r.y, THICKNESS, height);
      g.fillRect(r.x + THICKNESS, r.y, DEEPNESS, THICKNESS);
      g.fillRect(r.x + THICKNESS, r.y + height - THICKNESS, DEEPNESS, THICKNESS);
    }
  }
}
<commit_msg>cdata brace highlighter in injected fragments<commit_after>/*
 * Created by IntelliJ IDEA.
 * User: mike
 * Date: Sep 27, 2002
 * Time: 3:10:17 PM
 * To change template for new class use 
 * Code Style | Class Templates options (Tools | IDE Options).
 */
package com.intellij.codeInsight.highlighting;

import com.intellij.codeInsight.CodeInsightSettings;
import com.intellij.codeInsight.hint.EditorFragmentComponent;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.LogicalPosition;
import com.intellij.openapi.editor.colors.CodeInsightColors;
import com.intellij.openapi.editor.colors.EditorColorsManager;
import com.intellij.openapi.editor.colors.EditorColorsScheme;
import com.intellij.openapi.editor.ex.DocumentEx;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.editor.highlighter.EditorHighlighter;
import com.intellij.openapi.editor.highlighter.HighlighterIterator;
import com.intellij.openapi.editor.markup.*;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.Computable;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
import com.intellij.psi.tree.IElementType;
import com.intellij.util.Alarm;
import com.intellij.util.Processor;
import com.intellij.concurrency.Job;
import com.intellij.concurrency.JobScheduler;
import org.jetbrains.annotations.NotNull;

import java.awt.*;
import java.util.ArrayList;
import java.util.List;

public class BraceHighlightingHandler {
  private static final Key<List<RangeHighlighter>> BRACE_HIGHLIGHTERS_IN_EDITOR_VIEW_KEY = Key.create("BraceHighlighter.BRACE_HIGHLIGHTERS_IN_EDITOR_VIEW_KEY");
  private static final Key<RangeHighlighter> LINE_MARKER_IN_EDITOR_KEY = Key.create("BraceHighlighter.LINE_MARKER_IN_EDITOR_KEY");

  private final Project myProject;
  private final Editor myEditor;
  private final Alarm myAlarm;

  private final DocumentEx myDocument;
  private final PsiFile myPsiFile;
  private FileType myFileType;
  private final CodeInsightSettings myCodeInsightSettings;

  private BraceHighlightingHandler(@NotNull Project project, @NotNull Editor editor, @NotNull Alarm alarm, PsiFile psiFile) {
    myProject = project;

    myEditor = editor;
    myAlarm = alarm;
    myDocument = (DocumentEx)myEditor.getDocument();

    myPsiFile = psiFile;
    myCodeInsightSettings = CodeInsightSettings.getInstance();
    if (myPsiFile != null) {
      myFileType = myPsiFile.getFileType();
    }
  }

  public static void lookForInjectedAndMatchBracesInOtherThread(@NotNull final Editor editor, @NotNull final Alarm alarm, final Processor<BraceHighlightingHandler> processor) {
    final Project project = editor.getProject();
    if (project == null) return;
    final int offset = editor.getCaretModel().getOffset();
    Job<Object> job = JobScheduler.getInstance().createJob("Brace highlighter", Job.DEFAULT_PRIORITY);
    job.addTask(new Runnable() {
      public void run() {
        final PsiFile injected = ApplicationManager.getApplication().runReadAction(new Computable<PsiFile>() {
          public PsiFile compute() {
            Document document = editor.getDocument();
            PsiFile psiFile = PsiDocumentManager.getInstance(project).getPsiFile(document);
            return getInjectedFileIfAny(editor, project, offset, psiFile, alarm);
          }
        });
        ApplicationManager.getApplication().invokeLater(new Runnable(){
          public void run() {
            Project editorProject = editor.getProject();
            if (editorProject != null && !editorProject.isDisposed() && !project.isDisposed() && editor.getComponent().isShowing() && !editor.isViewer()) {
              Editor newEditor = InjectedLanguageUtil.getInjectedEditorForInjectedFileIfAny(editor, injected);
              BraceHighlightingHandler handler = new BraceHighlightingHandler(project, newEditor, alarm, injected);
              processor.process(handler);
            }
          }
        }, ModalityState.stateForComponent(editor.getComponent()));
      }
    });
    job.schedule();
  }

  private static final Alarm ourAlarm = new Alarm();
  public static PsiFile getInjectedFileIfAny(final Editor editor, final Project project, int offset, PsiFile psiFile, final Alarm alarm) {
    Document document = editor.getDocument();
    // when document is committed, try to highlight braces in injected lang - it's fast
    if (!PsiDocumentManager.getInstance(project).isUncommited(document)) {
      final PsiElement injectedElement = InjectedLanguageUtil.findInjectedElementAt(psiFile, offset);
      if (injectedElement != null && !(injectedElement instanceof PsiWhiteSpace)) {
        final PsiFile injected = injectedElement.getContainingFile();
        if (injected != null) {
          return injected;
        }
      }
    }
    else if (alarm != null) {
      ourAlarm.cancelAllRequests();
      ourAlarm.addRequest(new Runnable(){
        public void run() {
          if (!project.isDisposed() && !editor.isDisposed()) {
            PsiDocumentManager.getInstance(project).commitAllDocuments();
            BraceHighlighter.updateBraces(editor, alarm);
          }
        }
      }, 300, ModalityState.stateForComponent(editor.getComponent()));
    }
    return psiFile;
  }

  public void updateBraces() {
    if (myPsiFile == null) return;

    clearBraceHighlighters();

    if (!myCodeInsightSettings.HIGHLIGHT_BRACES) return;

    if (myEditor.getSelectionModel().hasSelection()) return;

    int offset = myEditor.getCaretModel().getOffset();
    final CharSequence chars = myEditor.getDocument().getCharsSequence();

    if (myEditor.offsetToLogicalPosition(offset).column != myEditor.getCaretModel().getLogicalPosition().column) {
      // we are in virtual space
      final int caretLineNumber = myEditor.getCaretModel().getLogicalPosition().line;
      if (caretLineNumber >= myDocument.getLineCount()) return;
      offset = myDocument.getLineEndOffset(caretLineNumber) + myDocument.getLineSeparatorLength(caretLineNumber);
    }

    final int originalOffset = offset;

    HighlighterIterator iterator = getEditorHighlighter().createIterator(offset);

    if (iterator.atEnd()) {
      offset--;
    }
    else if (BraceMatchingUtil.isRBraceToken(iterator, chars, myFileType)) {
      offset--;
    }
    else if (!BraceMatchingUtil.isLBraceToken(iterator, chars, myFileType)) {
      offset--;

      if (offset >= 0) {
        final HighlighterIterator i = getEditorHighlighter().createIterator(offset);
        if (!BraceMatchingUtil.isRBraceToken(i, chars, myFileType)) offset++;
      }
    }

    if (offset < 0) {
      removeLineMarkers();
      return;
    }

    iterator = getEditorHighlighter().createIterator(offset);

    myAlarm.cancelAllRequests();

    if (BraceMatchingUtil.isLBraceToken(iterator, chars, myFileType) ||
        BraceMatchingUtil.isRBraceToken(iterator, chars, myFileType)) {
      doHighlight(offset, originalOffset);
    }

    //highlight scope
    if (!myCodeInsightSettings.HIGHLIGHT_SCOPE) {
      removeLineMarkers();
      return;
    }

    final int _offset = offset;
    myAlarm.addRequest(new Runnable() {
      public void run() {
        if (!myProject.isDisposed() && !myEditor.isDisposed()) {
          highlightScope(_offset);
        }
      }
    }, 300);
  }

  EditorHighlighter getEditorHighlighter() {
    return ((EditorEx)myEditor).getHighlighter();
  }

  private void highlightScope(int offset) {
    if (myEditor.getFoldingModel().isOffsetCollapsed(offset)) return;
    if (myEditor.getDocument().getTextLength() <= offset) return;

    HighlighterIterator iterator = getEditorHighlighter().createIterator(offset);
    final CharSequence chars = myDocument.getCharsSequence();

    if (!BraceMatchingUtil.isStructuralBraceToken(myFileType, iterator, chars)) {
//      if (BraceMatchingUtil.isRBraceTokenToHighlight(myFileType, iterator) || BraceMatchingUtil.isLBraceTokenToHighlight(myFileType, iterator)) return;
    }
    else {
      if (BraceMatchingUtil.isRBraceToken(iterator, chars, myFileType) || BraceMatchingUtil.isLBraceToken(iterator, chars, myFileType)) return;
    }

    if (!BraceMatchingUtil.findStructuralLeftBrace(myFileType, iterator, chars)) {
      removeLineMarkers();
      return;
    }

    highlightLeftBrace(iterator, true);
  }

  private void doHighlight(int offset, int originalOffset) {
    if (myEditor.getFoldingModel().isOffsetCollapsed(offset)) return;

    HighlighterIterator iterator = getEditorHighlighter().createIterator(offset);
    final CharSequence chars = myDocument.getCharsSequence();

    if (BraceMatchingUtil.isLBraceToken(iterator, chars, myFileType)) {
      IElementType tokenType = iterator.getTokenType();

      iterator.advance();
      if (!iterator.atEnd() && BraceMatchingUtil.isRBraceToken(iterator, chars, myFileType)) {
        if (BraceMatchingUtil.isPairBraces(tokenType, iterator.getTokenType(), myFileType) && originalOffset == iterator.getStart()) return;
      }

      iterator.retreat();
      highlightLeftBrace(iterator, false);

      if (offset > 0) {
        iterator = getEditorHighlighter().createIterator(offset - 1);
        if (BraceMatchingUtil.isRBraceToken(iterator, chars, myFileType)) {
          highlightRightBrace(iterator);
        }
      }
    }
    else if (BraceMatchingUtil.isRBraceToken(iterator, chars, myFileType)) {
      highlightRightBrace(iterator);
    }
  }

  private void highlightRightBrace(HighlighterIterator iterator) {
    int brace1End = iterator.getEnd();

    boolean matched = BraceMatchingUtil.matchBrace(myDocument.getCharsSequence(), myFileType, iterator, false);

    int brace2Start;

    if (!iterator.atEnd()) {
      brace2Start = iterator.getStart();
    }
    else {
      brace2Start = -1;
    }

    highlightBraces(brace2Start, brace1End - 1, matched, false);
  }

  private void highlightLeftBrace(HighlighterIterator iterator, boolean scopeHighlighting) {
    int brace1Start = iterator.getStart();
    boolean matched = BraceMatchingUtil.matchBrace(myDocument.getCharsSequence(), myFileType, iterator, true);

    int brace2End;
    if (!iterator.atEnd()) {
      brace2End = iterator.getEnd() - 1;
    }
    else {
      brace2End = -1;
    }

    highlightBraces(brace1Start, brace2End, matched, scopeHighlighting);
  }

  private void highlightBraces(final int lBraceOffset, int rBraceOffset, boolean matched, boolean scopeHighlighting) {
    EditorColorsScheme scheme = EditorColorsManager.getInstance().getGlobalScheme();
    final TextAttributes attributes =
        matched ? scheme.getAttributes(CodeInsightColors.MATCHED_BRACE_ATTRIBUTES)
        : scheme.getAttributes(CodeInsightColors.UNMATCHED_BRACE_ATTRIBUTES);

    if (rBraceOffset >= 0 && !scopeHighlighting) {
      highlightBrace(rBraceOffset, matched);
    }

    if (lBraceOffset >= 0) {
      if (!scopeHighlighting) {
        highlightBrace(lBraceOffset, matched);
      }
    }

    if (!myEditor.equals(FileEditorManager.getInstance(myProject).getSelectedTextEditor())) {
      return;
    }

    if (lBraceOffset >= 0 && rBraceOffset >= 0) {
      final int startLine = myEditor.offsetToLogicalPosition(lBraceOffset).line;
      final int endLine = myEditor.offsetToLogicalPosition(rBraceOffset).line;
      if (endLine - startLine > 0) {
        final Runnable runnable = new Runnable() {
          public void run() {
            if (myProject.isDisposed() || myEditor.isDisposed()) return;
            Color color = attributes.getBackgroundColor();
            if (color == null) return;
            color = color.darker();
            lineMarkFragment(startLine, endLine, color);
          }
        };

        if (!scopeHighlighting) {
          myAlarm.addRequest(runnable, 300);
        }
        else {
          runnable.run();
        }
      }
      else {
        if (!myCodeInsightSettings.HIGHLIGHT_SCOPE) {
          removeLineMarkers();
        }
      }

      if (!scopeHighlighting) {
        showScopeHint(lBraceOffset, lBraceOffset + 1);
      }
    }
    else {
      if (!myCodeInsightSettings.HIGHLIGHT_SCOPE) {
        removeLineMarkers();
      }
    }
  }

  private void highlightBrace(int rBraceOffset, boolean matched) {
    EditorColorsScheme scheme = EditorColorsManager.getInstance().getGlobalScheme();
    final TextAttributes attributes =
        matched ? scheme.getAttributes(CodeInsightColors.MATCHED_BRACE_ATTRIBUTES)
        : scheme.getAttributes(CodeInsightColors.UNMATCHED_BRACE_ATTRIBUTES);


    RangeHighlighter rbraceHighlighter =
        myEditor.getMarkupModel().addRangeHighlighter(
            rBraceOffset, rBraceOffset + 1, HighlighterLayer.LAST + 1, attributes, HighlighterTargetArea.EXACT_RANGE);
    rbraceHighlighter.setGreedyToLeft(false);
    rbraceHighlighter.setGreedyToRight(false);
    registerHighlighter(rbraceHighlighter);
  }

  private void registerHighlighter(RangeHighlighter highlighter) {
    List<RangeHighlighter> highlighters = myEditor.getUserData(BRACE_HIGHLIGHTERS_IN_EDITOR_VIEW_KEY);
    if (highlighters == null) {
      highlighters = new ArrayList<RangeHighlighter>();
      myEditor.putUserData(BRACE_HIGHLIGHTERS_IN_EDITOR_VIEW_KEY, highlighters);
    }

    highlighters.add(highlighter);
  }

  private void showScopeHint(final int lbraceStart, final int lbraceEnd) {
    LogicalPosition bracePosition = myEditor.offsetToLogicalPosition(lbraceStart);
    Point braceLocation = myEditor.logicalPositionToXY(bracePosition);
    final int y = braceLocation.y;
    myAlarm.addRequest(
        new Runnable() {
          public void run() {
            if (!myEditor.getComponent().isShowing()) return;
            Rectangle viewRect = myEditor.getScrollingModel().getVisibleArea();
            if (y < viewRect.y) {
              int start = lbraceStart;
              if (!(myPsiFile instanceof PsiPlainTextFile)) {
                PsiDocumentManager.getInstance(myProject).commitAllDocuments();
                start = BraceMatchingUtil.getBraceMatcher(myFileType).getCodeConstructStart(myPsiFile, lbraceStart);
              }
              TextRange range = new TextRange(start, lbraceEnd);
              int line1 = myDocument.getLineNumber(range.getStartOffset());
              int line2 = myDocument.getLineNumber(range.getEndOffset());
              line1 = Math.max(line1, line2 - 5);
              range = new TextRange(myDocument.getLineStartOffset(line1), range.getEndOffset());
              EditorFragmentComponent.showEditorFragmentHint(myEditor, range, true);
            }
          }
        },
        300, ModalityState.stateForComponent(myEditor.getComponent()));
  }

  public void clearBraceHighlighters() {
    List<RangeHighlighter> highlighters = myEditor.getUserData(BRACE_HIGHLIGHTERS_IN_EDITOR_VIEW_KEY);
    if (highlighters == null) return;
    myEditor.putUserData(BRACE_HIGHLIGHTERS_IN_EDITOR_VIEW_KEY, null);
    for (final Object highlighter : highlighters) {
      RangeHighlighter rangeHighlighter = (RangeHighlighter)highlighter;
      myEditor.getMarkupModel().removeHighlighter(rangeHighlighter);
    }
  }

  private void lineMarkFragment(int startLine, int endLine, Color color) {
    removeLineMarkers();

    if (startLine >= endLine || endLine >= myDocument.getLineCount()) return;

    int startOffset = myDocument.getLineStartOffset(startLine);
    int endOffset = myDocument.getLineStartOffset(endLine);

    RangeHighlighter highlighter = myEditor.getMarkupModel().addRangeHighlighter(startOffset, endOffset, 0, null, HighlighterTargetArea.LINES_IN_RANGE);
    highlighter.setLineMarkerRenderer(new MyLineMarkerRenderer(color));
    myEditor.putUserData(LINE_MARKER_IN_EDITOR_KEY, highlighter);
  }

  private void removeLineMarkers() {
    RangeHighlighter marker = myEditor.getUserData(LINE_MARKER_IN_EDITOR_KEY);
    if (marker != null) {
      myEditor.getMarkupModel().removeHighlighter(marker);
      myEditor.putUserData(LINE_MARKER_IN_EDITOR_KEY, null);
    }
  }

  private static class MyLineMarkerRenderer implements LineMarkerRenderer {
    private static final int DEEPNESS = 2;
    private static final int THICKNESS = 2;
    private final Color myColor;

    public MyLineMarkerRenderer(Color color) {
      myColor = color;
    }

    public void paint(Editor editor, Graphics g, Rectangle r) {
      int height = r.height + editor.getLineHeight();
      g.setColor(myColor);
      g.fillRect(r.x, r.y, THICKNESS, height);
      g.fillRect(r.x + THICKNESS, r.y, DEEPNESS, THICKNESS);
      g.fillRect(r.x + THICKNESS, r.y + height - THICKNESS, DEEPNESS, THICKNESS);
    }
  }
}

{'fraction_non_alphanumeric': 0.1001596877217885, 'fraction_numerical': 0.003607759640406908, 'mean_word_length': 3.743582550147286, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 41, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '986061', 'n_tokens_mistral': 10235, 'n_tokens_neox': 9867, 'n_words': 1864}