@Contract("null, _, _ -> null")
  private static String toCanonicalPath(
      @Nullable String path, char separatorChar, boolean removeLastSlash) {
    if (path == null || path.isEmpty()) {
      return path;
    } else if (".".equals(path)) {
      return "";
    }

    path = path.replace(separatorChar, '/');
    if (path.indexOf('/') == -1) {
      return path;
    }

    int start = pathRootEnd(path) + 1, dots = 0;
    boolean separator = true;

    StringBuilder result = new StringBuilder(path.length());
    result.append(path, 0, start);

    for (int i = start; i < path.length(); ++i) {
      char c = path.charAt(i);
      if (c == '/') {
        if (!separator) {
          processDots(result, dots, start);
          dots = 0;
        }
        separator = true;
      } else if (c == '.') {
        if (separator || dots > 0) {
          ++dots;
        } else {
          result.append('.');
        }
        separator = false;
      } else {
        if (dots > 0) {
          StringUtil.repeatSymbol(result, '.', dots);
          dots = 0;
        }
        result.append(c);
        separator = false;
      }
    }

    if (dots > 0) {
      processDots(result, dots, start);
    }

    int lastChar = result.length() - 1;
    if (removeLastSlash && lastChar >= 0 && result.charAt(lastChar) == '/' && lastChar > start) {
      result.deleteCharAt(lastChar);
    }

    return result.toString();
  }
  protected String doCalculateSignature(PsiMethod method) {
    final StringBuilder buffer = new StringBuilder();
    final PsiModifierList modifierList = method.getModifierList();
    String modifiers = modifierList.getText();
    final String oldModifier = VisibilityUtil.getVisibilityModifier(modifierList);
    final String newModifier = getVisibility();
    String newModifierStr = VisibilityUtil.getVisibilityString(newModifier);
    if (!newModifier.equals(oldModifier)) {
      int index = modifiers.indexOf(oldModifier);
      if (index >= 0) {
        final StringBuilder buf = new StringBuilder(modifiers);
        buf.replace(
            index,
            index + oldModifier.length() + ("".equals(newModifierStr) ? 1 : 0),
            newModifierStr);
        modifiers = buf.toString();
      } else {
        if (!StringUtil.isEmpty(newModifierStr)) {
          newModifierStr += " ";
        }
        modifiers = newModifierStr + modifiers;
      }
    }

    buffer.append(modifiers);
    if (modifiers.length() > 0
        && !StringUtil.endsWithChar(modifiers, '\n')
        && !StringUtil.endsWithChar(modifiers, '\r')
        && !StringUtil.endsWithChar(modifiers, ' ')) {
      buffer.append(" ");
    }

    if (!method.isConstructor()) {
      final CanonicalTypes.Type type = getReturnType();
      if (type != null) {
        buffer.append(type.getTypeText());
      }
      buffer.append(" ");
    }
    buffer.append(getMethodName());
    buffer.append("(");

    final int lineBreakIdx = buffer.lastIndexOf("\n");
    String indent =
        StringUtil.repeatSymbol(
            ' ', lineBreakIdx >= 0 ? buffer.length() - lineBreakIdx - 1 : buffer.length());
    List<ParameterTableModelItemBase<ParameterInfoImpl>> items = myParametersTableModel.getItems();
    int curIndent = indent.length();
    for (int i = 0; i < items.size(); i++) {
      final ParameterTableModelItemBase<ParameterInfoImpl> item = items.get(i);
      if (i > 0) {
        buffer.append(",");
        buffer.append("\n");
        buffer.append(indent);
      }
      final String text = item.typeCodeFragment.getText();
      buffer.append(text).append(" ");
      final String name = item.parameter.getName();
      buffer.append(name);
      curIndent = indent.length() + text.length() + 1 + name.length();
    }
    // if (!items.isEmpty()) {
    //  buffer.append("\n");
    // }
    buffer.append(")");
    PsiTypeCodeFragment[] thrownExceptionsFragments = myExceptionsModel.getTypeCodeFragments();
    if (thrownExceptionsFragments.length > 0) {
      // buffer.append("\n");
      buffer.append(" throws ");
      curIndent += 9; // ") throws ".length()
      indent = StringUtil.repeatSymbol(' ', curIndent);
      for (int i = 0; i < thrownExceptionsFragments.length; i++) {
        String text = thrownExceptionsFragments[i].getText();
        if (i != 0) buffer.append(indent);
        buffer.append(text);
        if (i < thrownExceptionsFragments.length - 1) {
          buffer.append(",");
        }
        buffer.append("\n");
      }
    }

    return buffer.toString();
  }
  @NotNull
  private static String getMultilineDocCommentText(final @NotNull DartDocComment docComment) {
    final StringBuilder buf = new StringBuilder();
    boolean afterAsterisk = false;

    for (PsiElement child = docComment.getFirstChild();
        child != null;
        child = child.getNextSibling()) {
      final IElementType elementType = child.getNode().getElementType();
      final String text = child.getText();

      if (elementType != DartTokenTypesSets.MULTI_LINE_DOC_COMMENT_START
          && elementType != DartTokenTypesSets.DOC_COMMENT_LEADING_ASTERISK
          && elementType != DartTokenTypesSets.MULTI_LINE_COMMENT_END) {
        int newLinesCount;
        if (child instanceof PsiWhiteSpace
            && (newLinesCount = StringUtil.countNewLines(text)) > 0) {
          buf.append(StringUtil.repeatSymbol('\n', newLinesCount));
        } else {
          if (afterAsterisk && text.startsWith(" ")) {
            buf.append(text.substring(1));
          } else {
            buf.append(text);
          }
        }
      }

      afterAsterisk = elementType == DartTokenTypesSets.DOC_COMMENT_LEADING_ASTERISK;
    }

    return buf.toString();
  }
  @NotNull
  public static String shiftIndentInside(
      @NotNull String initial, final int i, boolean shiftEmptyLines) {
    StringBuilder result = new StringBuilder(initial.length());
    List<byte[]> lines;
    try {
      LineReader reader =
          new LineReader(new ByteArrayInputStream(initial.getBytes(CharsetToolkit.UTF8_CHARSET)));
      lines = reader.readLines();
    } catch (IOException e) {
      throw new RuntimeException(e);
    }

    boolean first = true;
    for (byte[] line : lines) {
      try {
        if (!first) result.append('\n');
        if (line.length > 0 || shiftEmptyLines) {
          StringUtil.repeatSymbol(result, ' ', i);
        }
        result.append(new String(line));
      } finally {
        first = false;
      }
    }

    return result.toString();
  }
 public void testE5() {
   DocumentEx document =
       (DocumentEx) EditorFactory.getInstance().createDocument(StringUtil.repeatSymbol(' ', 10));
   List<RangeMarker> mm = add(document, 9, 9, 4, 4, 1, 7, 7, 7, 4, 7);
   edit(document, 1, 5, 0);
   delete(mm, 3);
 }
 public void testE13() {
   DocumentEx document =
       (DocumentEx) EditorFactory.getInstance().createDocument(StringUtil.repeatSymbol(' ', 10));
   List<RangeMarker> mm = add(document, 5, 9, 9, 9, 7, 7, 6, 8);
   edit(document, 2, 1, 0);
   delete(mm, 0, 2);
 }
  public void testRangeHighlighterLinesInRangeForLongLinePerformance() throws Exception {
    final int N = 50000;
    Document document =
        EditorFactory.getInstance().createDocument(StringUtil.repeatSymbol('x', 2 * N));

    final MarkupModelEx markupModel =
        (MarkupModelEx) DocumentMarkupModel.forDocument(document, ourProject, true);
    for (int i = 0; i < N - 1; i++) {
      markupModel.addRangeHighlighter(2 * i, 2 * i + 1, 0, null, HighlighterTargetArea.EXACT_RANGE);
    }
    markupModel.addRangeHighlighter(
        N / 2, N / 2 + 1, 0, null, HighlighterTargetArea.LINES_IN_RANGE);

    PlatformTestUtil.startPerformanceTest(
            "slow highlighters lookup",
            (int) (N * Math.log(N) / 1000),
            new ThrowableRunnable() {
              @Override
              public void run() {
                List<RangeHighlighterEx> list = new ArrayList<RangeHighlighterEx>();
                CommonProcessors.CollectProcessor<RangeHighlighterEx> coll =
                    new CommonProcessors.CollectProcessor<RangeHighlighterEx>(list);
                for (int i = 0; i < N - 1; i++) {
                  list.clear();
                  markupModel.processRangeHighlightersOverlappingWith(2 * i, 2 * i + 1, coll);
                  assertEquals(2, list.size()); // 1 line plus one exact range marker
                }
              }
            })
        .assertTiming();
  }
 static void log(
     ProgressIndicator progressIndicator,
     TextEditorHighlightingPass pass,
     @NonNls @NotNull Object... info) {
   if (LOG.isDebugEnabled()) {
     CharSequence docText =
         pass == null || pass.getDocument() == null
             ? ""
             : ": '" + StringUtil.first(pass.getDocument().getCharsSequence(), 10, true) + "'";
     synchronized (PassExecutorService.class) {
       String infos = StringUtil.join(info, Functions.TO_STRING(), " ");
       String message =
           StringUtil.repeatSymbol(' ', getThreadNum() * 4)
               + " "
               + pass
               + " "
               + infos
               + "; progress="
               + (progressIndicator == null ? null : progressIndicator.hashCode())
               + " "
               + (progressIndicator == null ? "?" : progressIndicator.isCanceled() ? "X" : "V")
               + docText;
       LOG.debug(message);
       // System.out.println(message);
     }
   }
 }
Beispiel #9
0
  @SuppressWarnings("ForLoopThatDoesntUseLoopVariable")
  private static void indentPlainTextBlock(
      final Document document, final int startOffset, final int endOffset, final int indentLevel) {
    CharSequence chars = document.getCharsSequence();
    int spaceEnd = CharArrayUtil.shiftForward(chars, startOffset, " \t");
    int line = document.getLineNumber(startOffset);
    if (spaceEnd > endOffset
        || indentLevel <= 0
        || line >= document.getLineCount() - 1
        || chars.charAt(spaceEnd) == '\n') {
      return;
    }

    int linesToAdjustIndent = 0;
    for (int i = line + 1; i < document.getLineCount(); i++) {
      if (document.getLineStartOffset(i) >= endOffset) {
        break;
      }
      linesToAdjustIndent++;
    }

    String indentString = StringUtil.repeatSymbol(' ', indentLevel);

    for (; linesToAdjustIndent > 0; linesToAdjustIndent--) {
      int lineStartOffset = document.getLineStartOffset(++line);
      document.insertString(lineStartOffset, indentString);
    }
  }
 public void testE11() {
   DocumentEx document =
       (DocumentEx) EditorFactory.getInstance().createDocument(StringUtil.repeatSymbol(' ', 10));
   List<RangeMarker> mm = add(document, 9, 9, 7, 7, 1, 6, 3, 7);
   // edit(document, 0,0,0);
   delete(mm, 1);
 }
 public void testE4() {
   DocumentEx document =
       (DocumentEx) EditorFactory.getInstance().createDocument(StringUtil.repeatSymbol(' ', 10));
   List<RangeMarker> mm = add(document, 3, 5, 5, 6, 4, 8, 6, 9, 8, 9);
   edit(document, 6, 0, 0, 3, 0, 2);
   delete(mm, 1, 0);
 }
  private static void edit(DocumentEx document, int... offsets) {
    for (int i = 0; i < offsets.length; i += 3) {
      int offset = offsets[i];
      int oldlength = offsets[i + 1];
      int newlength = offsets[i + 2];

      document.replaceString(offset, offset + oldlength, StringUtil.repeatSymbol(' ', newlength));
    }
  }
 public static void log(@NonNls @NotNull Object... info) {
   if (IN_TESTS) {
     if (log.length() > 10000) {
       log.replace(0, log.length() - 5000, "");
     }
     String s = StringUtil.repeatSymbol(' ', getThreadNum() * 4) + Arrays.asList(info) + "\n";
     log.append(s);
   }
 }
 public void testE14() {
   DocumentEx document =
       (DocumentEx) EditorFactory.getInstance().createDocument(StringUtil.repeatSymbol(' ', 100));
   List<RangeMarker> mm =
       add(
           document, 6, 11, 2, 13, 17, 17, 13, 19, 2, 3, 9, 10, 10, 11, 14, 14, 1, 3, 4, 12, 14,
           15, 3, 10, 14, 14, 4, 4, 4, 8, 6, 14, 8, 16, 2, 12, 11, 19, 10, 13);
   edit(document, 19, 0, 0, 7, 3, 0, 16, 0, 3);
 }
 public void testE15() {
   DocumentEx document =
       (DocumentEx) EditorFactory.getInstance().createDocument(StringUtil.repeatSymbol(' ', 100));
   List<RangeMarker> mm =
       add(
           document, 90, 93, 0, 9, 44, 79, 4, 48, 44, 99, 53, 64, 59, 82, 12, 99, 81, 86, 8, 40,
           24, 55, 32, 50, 74, 79, 14, 94, 7, 14);
   edit(document, 34, 0, 4, 99, 0, 3);
 }
 public void testE16() {
   DocumentEx document =
       (DocumentEx) EditorFactory.getInstance().createDocument(StringUtil.repeatSymbol(' ', 100));
   List<RangeMarker> mm =
       add(
           document, 29, 63, 47, 52, 72, 86, 19, 86, 13, 55, 18, 57, 92, 95, 83, 99, 41, 80, 53,
           85, 10, 30, 28, 44, 23, 32, 70, 95, 14, 28);
   edit(document, 67, 5, 0, 1, 0, 4);
   delete(mm, 11);
 }
  public void testX() {
    RangeMarkerEx marker1 = createMarker(StringUtil.repeatSymbol(' ', 10), 3, 6);
    DocumentEx document = (DocumentEx) marker1.getDocument();
    document.createRangeMarker(2, 3);
    document.createRangeMarker(3, 8);
    document.createRangeMarker(7, 9);
    RangeMarkerEx r1 = (RangeMarkerEx) document.createRangeMarker(6, 8);

    r1.dispose();
    marker1.dispose();
  }
  public void testE17() {
    DocumentEx document =
        (DocumentEx) EditorFactory.getInstance().createDocument(StringUtil.repeatSymbol(' ', 100));

    List<RangeMarker> mm =
        add(
            document, 15, 85, 79, 88, 90, 94, 43, 67, 54, 89, 81, 98, 1, 34, 58, 93, 22, 23, 44, 45,
            63, 84, 45, 76, 58, 87, 40, 59, 5, 81, 95, 95, 12, 61, 52, 65, 80, 95, 6, 16, 7, 67, 59,
            63, 91, 96, 99, 99, 50, 96, 72, 78, 78, 78, 85, 85, 5, 51, 90, 91);
    edit(document, 20, 26, 0, 15, 0, 4, 64, 4, 0);
  }
  private static void printImpl(
      JTree tree,
      Object root,
      Collection<String> strings,
      int level,
      boolean withSelection,
      @Nullable Condition<String> nodePrintCondition) {
    DefaultMutableTreeNode defaultMutableTreeNode = (DefaultMutableTreeNode) root;

    final Object userObject = defaultMutableTreeNode.getUserObject();
    String nodeText;
    if (userObject != null) {
      nodeText = toString(userObject, null);
    } else {
      nodeText = "null";
    }

    if (nodePrintCondition != null && !nodePrintCondition.value(nodeText)) return;

    final StringBuilder buff = new StringBuilder();
    StringUtil.repeatSymbol(buff, ' ', level);

    final boolean expanded = tree.isExpanded(new TreePath(defaultMutableTreeNode.getPath()));
    if (!defaultMutableTreeNode.isLeaf()) {
      buff.append(expanded ? "-" : "+");
    }

    final boolean selected =
        tree.getSelectionModel().isPathSelected(new TreePath(defaultMutableTreeNode.getPath()));
    if (withSelection && selected) {
      buff.append("[");
    }

    buff.append(nodeText);

    if (withSelection && selected) {
      buff.append("]");
    }

    strings.add(buff.toString());

    int childCount = tree.getModel().getChildCount(root);
    if (expanded) {
      for (int i = 0; i < childCount; i++) {
        printImpl(
            tree,
            tree.getModel().getChild(root, i),
            strings,
            level + 1,
            withSelection,
            nodePrintCondition);
      }
    }
  }
  public void _testRandomAddRemove() {
    int N = 100;

    for (int ti = 0; ; ti++) {
      if (ti % 10000 == 0) System.out.println(ti);
      DocumentEx document =
          (DocumentEx) EditorFactory.getInstance().createDocument(StringUtil.repeatSymbol(' ', N));

      Random gen = new Random();
      List<Pair<RangeMarker, TextRange>> adds = new ArrayList<Pair<RangeMarker, TextRange>>();
      List<Pair<RangeMarker, TextRange>> dels = new ArrayList<Pair<RangeMarker, TextRange>>();

      try {
        for (int i = 0; i < 30; i++) {
          int x = gen.nextInt(N);
          int y = x + gen.nextInt(N - x);
          if (gen.nextBoolean()) {
            x = 0;
            y = document.getTextLength();
          }
          RangeMarkerEx r = (RangeMarkerEx) document.createRangeMarker(x, y);
          adds.add(Pair.create((RangeMarker) r, TextRange.create(r)));
        }
        List<Pair<RangeMarker, TextRange>> candidates =
            new ArrayList<Pair<RangeMarker, TextRange>>(adds);
        while (!candidates.isEmpty()) {
          int size = candidates.size();
          int x = gen.nextInt(size);
          Pair<RangeMarker, TextRange> c = candidates.remove(x);
          RangeMarkerEx r = (RangeMarkerEx) c.first;
          assertEquals(size - 1, candidates.size());
          dels.add(c);
          r.dispose();
        }
      } catch (AssertionError e) {
        String s = "adds: ";
        for (Pair<RangeMarker, TextRange> c : adds) {
          TextRange t = c.second;
          s += t.getStartOffset() + "," + t.getEndOffset() + ", ";
        }
        s += "\ndels: ";

        for (Pair<RangeMarker, TextRange> c : dels) {
          int index = adds.indexOf(c);
          assertSame(c, adds.get(index));
          s += index + ", ";
        }
        System.err.println(s);
        throw e;
      }
    }
  }
  @Override
  protected JComponent getRowPresentation(
      ParameterTableModelItemBase<ParameterInfoImpl> item,
      boolean selected,
      final boolean focused) {
    final JPanel panel = new JPanel(new BorderLayout());
    final String typeText = item.typeCodeFragment.getText();
    final String separator =
        StringUtil.repeatSymbol(' ', getTypesMaxLength() - typeText.length() + 1);
    String text = typeText + separator + item.parameter.getName();
    final String defaultValue = item.defaultValueCodeFragment.getText();
    String tail = "";
    if (StringUtil.isNotEmpty(defaultValue)) {
      tail += " default value = " + defaultValue;
    }
    if (item.parameter.isUseAnySingleVariable()) {
      if (StringUtil.isNotEmpty(defaultValue)) {
        tail += ";";
      }
      tail += " Use any var.";
    }
    if (!StringUtil.isEmpty(tail)) {
      text += " //" + tail;
    }
    final EditorTextField field =
        new EditorTextField(" " + text, getProject(), getFileType()) {
          @Override
          protected boolean shouldHaveBorder() {
            return false;
          }
        };

    Font font = EditorColorsManager.getInstance().getGlobalScheme().getFont(EditorFontType.PLAIN);
    font = new Font(font.getFontName(), font.getStyle(), 12);
    field.setFont(font);

    if (selected && focused) {
      panel.setBackground(UIUtil.getTableSelectionBackground());
      field.setAsRendererWithSelection(
          UIUtil.getTableSelectionBackground(), UIUtil.getTableSelectionForeground());
    } else {
      panel.setBackground(UIUtil.getTableBackground());
      if (selected && !focused) {
        panel.setBorder(new DottedBorder(UIUtil.getTableForeground()));
      }
    }
    panel.add(field, BorderLayout.WEST);
    return panel;
  }
 public static void deleteToTargetPosition(@NotNull Editor editor, @NotNull LogicalPosition pos) {
   LogicalPosition logicalPosition = editor.getCaretModel().getLogicalPosition();
   if (logicalPosition.line != pos.line) {
     LOGGER.error(
         "Unexpected caret position: " + logicalPosition + ", target indent position: " + pos);
     return;
   }
   if (pos.column < logicalPosition.column) {
     int targetOffset = editor.logicalPositionToOffset(pos);
     int offset = editor.getCaretModel().getOffset();
     editor.getSelectionModel().setSelection(targetOffset, offset);
     EditorModificationUtil.deleteSelectedText(editor);
   } else if (pos.column > logicalPosition.column) {
     EditorModificationUtil.insertStringAtCaret(
         editor, StringUtil.repeatSymbol(' ', pos.column - logicalPosition.column));
   }
 }
  private static int doPrint(
      StringBuilder buffer,
      int currentLevel,
      Object node,
      AbstractTreeStructure structure,
      @Nullable Comparator comparator,
      int maxRowCount,
      int currentLine,
      char paddingChar,
      @Nullable Queryable.PrintInfo printInfo) {
    if (currentLine >= maxRowCount && maxRowCount != -1) return currentLine;

    StringUtil.repeatSymbol(buffer, paddingChar, currentLevel);
    buffer.append(toString(node, printInfo)).append("\n");
    currentLine++;
    Object[] children = structure.getChildElements(node);

    if (comparator != null) {
      ArrayList<?> list = new ArrayList<Object>(Arrays.asList(children));
      @SuppressWarnings({"UnnecessaryLocalVariable", "unchecked"})
      Comparator<Object> c = comparator;
      Collections.sort(list, c);
      children = ArrayUtil.toObjectArray(list);
    }
    for (Object child : children) {
      currentLine =
          doPrint(
              buffer,
              currentLevel + 1,
              child,
              structure,
              comparator,
              maxRowCount,
              currentLine,
              paddingChar,
              printInfo);
    }

    return currentLine;
  }
  private static void addFormattedDocString(
      PsiElement element,
      @NotNull String docstring,
      ChainIterable<String> formattedOutput,
      ChainIterable<String> unformattedOutput) {
    final Project project = element.getProject();

    List<String> formatted = PyStructuredDocstringFormatter.formatDocstring(element, docstring);
    if (formatted != null) {
      unformattedOutput.add(formatted);
      return;
    }

    boolean isFirstLine;
    final List<String> result = new ArrayList<String>();
    String[] lines = removeCommonIndentation(docstring);

    // reconstruct back, dropping first empty fragment as needed
    isFirstLine = true;
    int tabSize = CodeStyleSettingsManager.getSettings(project).getTabSize(PythonFileType.INSTANCE);
    for (String line : lines) {
      if (isFirstLine && ourSpacesPattern.matcher(line).matches())
        continue; // ignore all initial whitespace
      if (isFirstLine) {
        isFirstLine = false;
      } else {
        result.add(BR);
      }
      int leadingTabs = 0;
      while (leadingTabs < line.length() && line.charAt(leadingTabs) == '\t') {
        leadingTabs++;
      }
      if (leadingTabs > 0) {
        line = StringUtil.repeatSymbol(' ', tabSize * leadingTabs) + line.substring(leadingTabs);
      }
      result.add(combUp(line));
    }
    formattedOutput.add(result);
  }
 private static void processDots(StringBuilder result, int dots, int start) {
   if (dots == 2) {
     int pos = -1;
     if (!StringUtil.endsWith(result, "/../") && !StringUtil.equals(result, "../")) {
       pos = StringUtil.lastIndexOf(result, '/', start, result.length() - 1);
       if (pos >= 0) {
         ++pos; // separator found, trim to next char
       } else if (start > 0) {
         pos = start; // path is absolute, trim to root ('/..' -> '/')
       } else if (result.length() > 0) {
         pos = 0; // path is relative, trim to default ('a/..' -> '')
       }
     }
     if (pos >= 0) {
       result.delete(pos, result.length());
     } else {
       result.append("../"); // impossible to traverse, keep as-is
     }
   } else if (dots != 1) {
     StringUtil.repeatSymbol(result, '.', dots);
     result.append('/');
   }
 }
 public void testE9() {
   DocumentEx document =
       (DocumentEx) EditorFactory.getInstance().createDocument(StringUtil.repeatSymbol(' ', 10));
   List<RangeMarker> mm = add(document, 4, 5, 9, 9, 1, 2, 0, 3);
   edit(document, 0, 3, 0);
 }
 public void testE10() {
   DocumentEx document =
       (DocumentEx) EditorFactory.getInstance().createDocument(StringUtil.repeatSymbol(' ', 10));
   List<RangeMarker> mm = add(document, 9, 9, 6, 8, 8, 8, 5, 9);
   edit(document, 2, 6, 0, 2, 0, 4);
 }
  void doPrintNotification(final Notification notification) {
    Editor editor = myLogEditor.getValue();
    if (editor.isDisposed()) {
      return;
    }

    Document document = editor.getDocument();
    boolean scroll =
        document.getTextLength() == editor.getCaretModel().getOffset()
            || !editor.getContentComponent().hasFocus();

    Long notificationTime = myProjectModel.getNotificationTime(notification);
    if (notificationTime == null) {
      return;
    }

    String date = DateFormatUtil.formatTimeWithSeconds(notificationTime) + " ";
    append(document, date);

    int startLine = document.getLineCount() - 1;

    EventLog.LogEntry pair =
        EventLog.formatForLog(notification, StringUtil.repeatSymbol(' ', date.length()));

    final NotificationType type = notification.getType();
    TextAttributesKey key =
        type == NotificationType.ERROR
            ? ConsoleViewContentType.LOG_ERROR_OUTPUT_KEY
            : type == NotificationType.INFORMATION
                ? ConsoleViewContentType.NORMAL_OUTPUT_KEY
                : ConsoleViewContentType.LOG_WARNING_OUTPUT_KEY;

    int msgStart = document.getTextLength();
    String message = pair.message;
    append(document, message);

    TextAttributes attributes =
        EditorColorsManager.getInstance().getGlobalScheme().getAttributes(key);
    int layer = HighlighterLayer.CARET_ROW + 1;
    editor
        .getMarkupModel()
        .addRangeHighlighter(
            msgStart,
            document.getTextLength(),
            layer,
            attributes,
            HighlighterTargetArea.EXACT_RANGE);

    for (Pair<TextRange, HyperlinkInfo> link : pair.links) {
      myHyperlinkSupport
          .getValue()
          .addHyperlink(
              link.first.getStartOffset() + msgStart,
              link.first.getEndOffset() + msgStart,
              null,
              link.second);
    }

    append(document, "\n");

    if (scroll) {
      editor.getCaretModel().moveToOffset(document.getTextLength());
      editor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE);
    }

    if (notification.isImportant()) {
      highlightNotification(notification, pair.status, startLine, document.getLineCount() - 1);
    }
  }
 private static String genSameLineFeed(String text) {
   final int count = StringUtil.countChars(text, '\n');
   return StringUtil.repeatSymbol('\n', count);
 }
  public void testRandomEdit_NoCommand() {
    final int N = 100;

    final Random gen = new Random();
    int N_TRIES = Timings.adjustAccordingToMySpeed(7000, false);
    System.out.println("N_TRIES = " + N_TRIES);
    DocumentEx document = null;
    for (int tryn = 0; tryn < N_TRIES; tryn++) {
      ((UndoManagerImpl) UndoManager.getInstance(getProject())).flushCurrentCommandMerger();
      ((UndoManagerImpl) UndoManager.getGlobalInstance()).flushCurrentCommandMerger();
      if (document != null) {
        ((UndoManagerImpl) UndoManager.getInstance(getProject()))
            .clearUndoRedoQueueInTests(document);
        ((UndoManagerImpl) UndoManager.getGlobalInstance()).clearUndoRedoQueueInTests(document);
      }

      if (tryn % 10000 == 0) {
        System.out.println(tryn);
      }
      document =
          (DocumentEx) EditorFactory.getInstance().createDocument(StringUtil.repeatSymbol(' ', N));

      final DocumentEx finalDocument = document;
      new WriteCommandAction(getProject()) {
        @Override
        protected void run(@NotNull Result result) throws Exception {
          List<Pair<RangeMarker, TextRange>> adds = new ArrayList<Pair<RangeMarker, TextRange>>();
          List<Pair<RangeMarker, TextRange>> dels = new ArrayList<Pair<RangeMarker, TextRange>>();
          List<Trinity<Integer, Integer, Integer>> edits =
              new ArrayList<Trinity<Integer, Integer, Integer>>();

          try {
            for (int i = 0; i < 30; i++) {
              int x = gen.nextInt(N);
              int y = x + gen.nextInt(N - x);
              RangeMarkerEx r = (RangeMarkerEx) finalDocument.createRangeMarker(x, y);
              adds.add(Pair.create((RangeMarker) r, TextRange.create(r)));
            }

            for (int i = 0; i < 10; i++) {
              int offset = gen.nextInt(finalDocument.getTextLength());
              if (gen.nextBoolean()) {
                int length = gen.nextInt(5);
                edits.add(Trinity.create(offset, 0, length));
                finalDocument.insertString(offset, StringUtil.repeatSymbol(' ', length));
              } else {
                int length = gen.nextInt(finalDocument.getTextLength() - offset);
                edits.add(Trinity.create(offset, length, 0));
                finalDocument.deleteString(offset, offset + length);
              }
            }
            List<Pair<RangeMarker, TextRange>> candidates =
                new ArrayList<Pair<RangeMarker, TextRange>>(adds);
            while (!candidates.isEmpty()) {
              int size = candidates.size();
              int x = gen.nextInt(size);
              Pair<RangeMarker, TextRange> c = candidates.remove(x);
              RangeMarkerEx r = (RangeMarkerEx) c.first;
              assertEquals(size - 1, candidates.size());
              dels.add(c);
              r.dispose();
            }
          } catch (AssertionError e) {
            String s = "adds: ";
            for (Pair<RangeMarker, TextRange> c : adds) {
              TextRange t = c.second;
              s += t.getStartOffset() + "," + t.getEndOffset() + ", ";
            }

            s += "\nedits: ";
            for (Trinity<Integer, Integer, Integer> edit : edits) {
              s += edit.first + "," + edit.second + "," + edit.third + ",  ";
            }
            s += "\ndels: ";

            for (Pair<RangeMarker, TextRange> c : dels) {
              int index = adds.indexOf(c);
              assertSame(c, adds.get(index));
              s += index + ", ";
            }
            System.err.println(s);
            throw e;
          }
        }
      }.execute();
    }
  }