/** Result computed show it... Takes AnnotateLines and shows them. */
  public void annotationLines(File file, List<AnnotateLine> annotateLines) {
    // set repository root for popup menu, now should be the right time
    repositoryRoot = Mercurial.getInstance().getRepositoryRoot(getCurrentFile());
    final List<AnnotateLine> lines = new LinkedList<AnnotateLine>(annotateLines);
    int lineCount = lines.size();
    /** 0 based line numbers => 1 based line numbers */
    final int ann2editorPermutation[] = new int[lineCount];
    for (int i = 0; i < lineCount; i++) {
      ann2editorPermutation[i] = i + 1;
    }

    DiffProvider diff = (DiffProvider) Lookup.getDefault().lookup(DiffProvider.class);
    if (diff != null) {
      Reader r = new LinesReader(lines);
      Reader docReader = Utils.getDocumentReader(doc);
      try {

        Difference[] differences = diff.computeDiff(r, docReader);

        // customize annotation line numbers to match different reality
        // compule line permutation

        for (Difference d : differences) {
          int offset, editorStart;
          if (d.getType() == Difference.ADD) {
            offset = d.getSecondEnd() - d.getSecondStart() + 1;
            editorStart = d.getFirstStart();
          } else if (d.getType() == Difference.DELETE) {
            offset = d.getFirstEnd() - d.getFirstStart() + 1;
            editorStart = d.getFirstEnd();
            for (int c = editorStart - offset; c < editorStart; c++) {
              ann2editorPermutation[c] = -1;
            }
            offset = -offset;
          } else {
            // change
            int firstLen = d.getFirstEnd() - d.getFirstStart();
            int secondLen = d.getSecondEnd() - d.getSecondStart();
            offset = secondLen - firstLen;
            if (offset == 0) continue;
            editorStart = d.getFirstEnd();
            for (int c = d.getFirstStart(); c < editorStart; c++) {
              ann2editorPermutation[c] += -1;
            }
          }
          for (int c = editorStart; c < lineCount; c++) {
            ann2editorPermutation[c] += offset;
          }
        }

      } catch (IOException e) {
        Mercurial.LOG.log(
            Level.INFO,
            "Cannot compute local diff required for annotations, ignoring..."); // NOI18N
      }
    }

    doc.render(
        new Runnable() {
          @Override
          public void run() {
            StyledDocument sd = (StyledDocument) doc;
            Iterator<AnnotateLine> it = lines.iterator();
            previousRevisions = Collections.synchronizedMap(new HashMap<String, HgRevision>());
            originalFiles = Collections.synchronizedMap(new HashMap<String, File>());
            elementAnnotations =
                Collections.synchronizedMap(new HashMap<Element, AnnotateLine>(lines.size()));
            while (it.hasNext()) {
              AnnotateLine line = it.next();
              int lineNum = ann2editorPermutation[line.getLineNum() - 1];
              if (lineNum == -1) {
                continue;
              }
              try {
                int lineOffset = NbDocument.findLineOffset(sd, lineNum - 1);
                Element element = sd.getParagraphElement(lineOffset);
                elementAnnotations.put(element, line);
              } catch (IndexOutOfBoundsException ex) {
                // TODO how could I get line behind document end?
                // furtunately user does not spot it
                Mercurial.LOG.log(Level.INFO, null, ex);
              }
            }
          }
        });

    final String url = HgUtils.getRemoteRepository(repositoryRoot);
    final boolean isKenaiRepository = url != null && HgKenaiAccessor.getInstance().isKenai(url);
    if (isKenaiRepository) {
      kenaiUsersMap = new HashMap<String, KenaiUser>();
      Iterator<AnnotateLine> it = lines.iterator();
      while (it.hasNext()) {
        AnnotateLine line = it.next();
        String author = line.getAuthor();
        if (author != null && !author.equals("") && !kenaiUsersMap.keySet().contains(author)) {
          KenaiUser ku = HgKenaiAccessor.getInstance().forName(author, url);
          if (ku != null) {
            kenaiUsersMap.put(author, ku);
          }
        }
      }
    }

    // lazy listener registration
    caret.addChangeListener(this);
    this.caretTimer = new Timer(500, this);
    caretTimer.setRepeats(false);

    elementAnnotationsSubstitute = "";
    onCurrentLine();
    revalidate();
    repaint();
  }