/**
   * Installs this highlighter into the specified StyledText object. Client can manually call
   * detach() method, then wants to destroy this object.
   */
  public void attach(StyledText parent) {
    detach();

    text = parent;
    text.addDisposeListener(ml);
    text.addLineStyleListener(ml);
    text.addLineBackgroundListener(ml);
    text.addPaintListener(ml);
    text.addVerifyListener(ml);
    text.addExtendedModifyListener(ml);
    text.addControlListener(ml);
    text.addKeyListener(ml);
    text.addTraverseListener(ml);
    text.addMouseListener(ml);
    text.addSelectionListener(ml);
    text.getContent().addTextChangeListener(ml);
    ScrollBar sb = text.getVerticalBar();
    if (sb != null) sb.addSelectionListener(ml);
    updateViewport();

    new Thread() {
      public void run() {
        // setPriority(Thread.NORM_PRIORITY-1);
        while (true) {
          try {
            sleep(300);
          } catch (InterruptedException e) {
          }
          if (baseEditor == null || text == null) break;
          if (backParserDelay) {
            backParserDelay = false;
            try {
              sleep(1500);
            } catch (InterruptedException e) {
            }
            continue;
          }
          ;
          Display.getDefault()
              .syncExec(
                  new Runnable() {
                    public void run() {
                      if (baseEditor == null || text == null) return;
                      if (text.isDisposed()) return;
                      // System.out.println(System.currentTimeMillis());
                      baseEditor.idleJob(80);
                      // redrawFrom(text.getLineAtOffset(text.getCaretOffset()));
                    }
                  });
        }
        ;
      };
    }.start();
  }
  /** @see IPainter#paint(int) */
  public final void paint(final int reason) {
    Point selection = mSourceViewer.getSelectedRange();
    if (selection.y > 0) {
      deactivate(true);
      return;
    }

    SexpNavigator explorer = mEditor.getExplorer();

    boolean backward = true;
    boolean closeToParen = false;
    int offset = selection.x;
    IDocument document = mEditor.getDocument();
    try {
      char previousChar = '\0';
      char nextChar = '\0';

      if (selection.x > 0) previousChar = document.getChar(selection.x - 1);

      if (selection.x > 0
          && SchemeScannerUtilities.isClosingParenthesis(previousChar)
          && SchemeTextUtilities.getPartition(document, selection.x - 1).getType()
              == IDocument.DEFAULT_CONTENT_TYPE) {
        closeToParen = true;
      } else {
        nextChar = document.getChar(selection.x);
        if (selection.x < document.getLength() - 1
            && SchemeScannerUtilities.isOpeningParenthesis(nextChar)
            && SchemeTextUtilities.getPartition(document, selection.x).getType()
                == IDocument.DEFAULT_CONTENT_TYPE) {
          closeToParen = true;
          backward = false;
        }
      }

      if (closeToParen && backward && explorer.backwardSexpression(selection.x)) {
        offset = explorer.getListStart();
        char matchingChar = document.getChar(offset);
        mMismatch =
            SchemeScannerUtilities.getParenthesisType(previousChar)
                != SchemeScannerUtilities.getParenthesisType(matchingChar);
      } else {
        if (closeToParen && !backward && explorer.forwardSexpression(selection.x)) {
          offset = explorer.getSexpEnd() - 1;
          char matchingChar = document.getChar(offset);
          mMismatch =
              SchemeScannerUtilities.getParenthesisType(nextChar)
                  != SchemeScannerUtilities.getParenthesisType(matchingChar);
        } else {
          deactivate(true);
          return;
        }
      }

    } catch (BadLocationException exception) {
      deactivate(true);
      return;
    }

    if (mIsActive) {
      // only if different
      if (offset != mBracketPosition.getOffset()) {
        // remove old highlighting
        handleDrawRequest(null);
        // update position
        mBracketPosition.isDeleted = false;
        mBracketPosition.offset = offset;
        mBracketPosition.length = 1;
        // apply new highlighting
        handleDrawRequest(null);
      }
    } else {
      mIsActive = true;

      mBracketPosition.isDeleted = false;
      mBracketPosition.offset = offset;
      mBracketPosition.length = 1;

      mTextWidget.addPaintListener(this);
      mPositionManager.managePosition(mBracketPosition);
      handleDrawRequest(null);
    }
  }