private String setup(
     String text,
     boolean isDisabled,
     boolean isStrikeout,
     Color background,
     @Nullable TextRange range) {
   Map<TextRange, ParameterInfoUIContextEx.Flag> flagsMap = new TreeMap<>(TEXT_RANGE_COMPARATOR);
   if (range != null) flagsMap.put(range, ParameterInfoUIContextEx.Flag.HIGHLIGHT);
   if (isDisabled)
     flagsMap.put(TextRange.create(0, text.length()), ParameterInfoUIContextEx.Flag.DISABLE);
   if (isStrikeout)
     flagsMap.put(TextRange.create(0, text.length()), ParameterInfoUIContextEx.Flag.STRIKEOUT);
   return setup(text, flagsMap, background);
 }
 public static FragmentContent fromRangeMarker(RangeMarker rangeMarker, Project project) {
   Document document = rangeMarker.getDocument();
   VirtualFile file = FileDocumentManager.getInstance().getFile(document);
   FileType type = FileTypeManager.getInstance().getFileTypeByFile(file);
   return new FragmentContent(
       new DocumentContent(project, document), TextRange.create(rangeMarker), project, type);
 }
  public void testDocSynchronizerPrefersLineBoundaryChanges() throws Exception {
    String text =
        "import java.awt.List;\n"
            + "[import java.util.ArrayList;\n]"
            + "import java.util.HashMap;\n"
            + "import java.util.Map;";
    RangeMarker marker = createMarker(text);
    synchronizer.startTransaction(getProject(), document, psiFile);

    String newText = StringUtil.replaceSubstring(document.getText(), TextRange.create(marker), "");
    synchronizer.replaceString(document, 0, document.getTextLength(), newText);

    final List<DocumentEvent> events = new ArrayList<DocumentEvent>();
    document.addDocumentListener(
        new DocumentAdapter() {
          @Override
          public void documentChanged(DocumentEvent e) {
            events.add(e);
          }
        });
    synchronizer.commitTransaction(document);

    assertEquals(newText, document.getText());
    DocumentEvent event = assertOneElement(events);
    assertEquals(
        "DocumentEventImpl[myOffset=22, myOldLength=28, myNewLength=0, myOldString='import java.util.ArrayList;\n', myNewString=''].",
        event.toString());
  }
  void loadFromEditor(@NotNull Editor editor) {
    assertDispatchThread();
    LOG.assertTrue(!editor.isDisposed());
    clear();

    PsiDocumentManager documentManager = PsiDocumentManager.getInstance(myProject);
    documentManager.commitDocument(editor.getDocument());
    PsiFile file = documentManager.getPsiFile(editor.getDocument());

    SmartPointerManager smartPointerManager = SmartPointerManager.getInstance(myProject);
    EditorFoldingInfo info = EditorFoldingInfo.get(editor);
    FoldRegion[] foldRegions = editor.getFoldingModel().getAllFoldRegions();
    for (FoldRegion region : foldRegions) {
      if (!region.isValid()) continue;
      PsiElement element = info.getPsiElement(region);
      boolean expanded = region.isExpanded();
      boolean collapseByDefault =
          element != null
              && FoldingPolicy.isCollapseByDefault(element)
              && !FoldingUtil.caretInsideRange(editor, TextRange.create(region));
      if (collapseByDefault == expanded || element == null) {
        FoldingInfo fi = new FoldingInfo(region.getPlaceholderText(), expanded);
        if (element != null) {
          myPsiElements.add(smartPointerManager.createSmartPsiElementPointer(element, file));
          element.putUserData(FOLDING_INFO_KEY, fi);
        } else if (region.isValid()) {
          myRangeMarkers.add(region);
          region.putUserData(FOLDING_INFO_KEY, fi);
        }
      }
    }
  }
 @Nullable
 private PyType getImplicitlyResolvedType(
     @NotNull List<Token<PyElementType>> tokens,
     @NotNull TypeEvalContext context,
     @NotNull Map<TextRange, PyType> types,
     @NotNull Map<PyType, TextRange> fullRanges,
     TextRange firstRange) {
   PyType type = null;
   QualifiedName qName = null;
   while (!tokens.isEmpty()) {
     final Token<PyElementType> token = tokens.get(0);
     final String name = token.getText().toString();
     qName = qName != null ? qName.append(name) : QualifiedName.fromComponents(name);
     PsiElement module =
         new QualifiedNameResolverImpl(qName).fromElement(myAnchor).firstResult();
     if (module == null) {
       break;
     }
     if (module instanceof PsiDirectory) {
       module = PyUtil.getPackageElement((PsiDirectory) module, myAnchor);
     }
     if (module instanceof PyTypedElement) {
       final PyType moduleType = context.getType((PyTypedElement) module);
       if (moduleType != null) {
         type = moduleType;
         types.put(token.getRange(), type);
         fullRanges.put(
             type,
             TextRange.create(firstRange.getStartOffset(), token.getRange().getEndOffset()));
       }
     }
     tokens.remove(0);
   }
   return type;
 }
 @Override
 public void documentChanged(DocumentEvent e) {
   if (!myTaskFile.isTrackChanges()) {
     return;
   }
   if (myAnswerPlaceholders.isEmpty()) return;
   if (e instanceof DocumentEventImpl) {
     DocumentEventImpl event = (DocumentEventImpl) e;
     Document document = e.getDocument();
     int offset = e.getOffset();
     int change = event.getNewLength() - event.getOldLength();
     for (AnswerPlaceholderWrapper answerPlaceholderWrapper : myAnswerPlaceholders) {
       int twStart = answerPlaceholderWrapper.getTwStart();
       if (twStart > offset) {
         twStart += change;
       }
       int twEnd = answerPlaceholderWrapper.getTwEnd();
       if (twEnd >= offset) {
         twEnd += change;
       }
       AnswerPlaceholder answerPlaceholder = answerPlaceholderWrapper.getAnswerPlaceholder();
       int line = document.getLineNumber(twStart);
       int start = twStart - document.getLineStartOffset(line);
       int length = twEnd - twStart;
       answerPlaceholder.setLine(line);
       answerPlaceholder.setStart(start);
       if (usePossibleAnswerLength) {
         answerPlaceholder.setPossibleAnswer(
             document.getText(TextRange.create(twStart, twStart + length)));
       } else if (myTrackLength) {
         answerPlaceholder.setLength(length);
       }
     }
   }
 }
  @Override
  public void visitField(PsiField field) {
    // There is a possible case that more than one field is declared for the same type like 'int i,
    // j;'. We want to process only
    // the first one then.
    PsiElement fieldPrev = getPreviousNonWsComment(field.getPrevSibling(), 0);
    if (fieldPrev instanceof PsiJavaToken
        && ((PsiJavaToken) fieldPrev).getTokenType() == JavaTokenType.COMMA) {
      return;
    }

    // There is a possible case that fields which share the same type declaration are located on
    // different document lines, e.g.:
    //    int i1,
    //        i2;
    // We want to consider only the first declaration then but need to expand its range to all
    // affected lines (up to semicolon).
    TextRange range = field.getTextRange();
    PsiElement child = field.getLastChild();
    boolean needSpecialProcessing = true;
    if (isSemicolon(child)) {
      needSpecialProcessing = false;
    } else if (child instanceof PsiComment) {
      // There is a possible field definition like below:
      //   int f; // my comment.
      // The comment goes into field PSI here, that's why we need to handle it properly.
      PsiElement prev = getPreviousNonWsComment(child, range.getStartOffset());
      needSpecialProcessing = prev != null && !isSemicolon(prev);
    }

    if (needSpecialProcessing) {
      for (PsiElement e = field.getNextSibling(); e != null; e = e.getNextSibling()) {
        if (e instanceof PsiWhiteSpace || e instanceof PsiComment) { // Skip white space and comment
          continue;
        } else if (e instanceof PsiJavaToken) {
          if (((PsiJavaToken) e).getTokenType() == JavaTokenType.COMMA) { // Skip comma
            continue;
          } else {
            break;
          }
        } else if (e instanceof PsiField) {
          PsiElement c = e.getLastChild();
          if (c != null) {
            c = getPreviousNonWsComment(c, range.getStartOffset());
          }
          // Stop if current field ends by a semicolon.
          if (c instanceof PsiErrorElement // Incomplete field without trailing semicolon
              || (c instanceof PsiJavaToken
                  && ((PsiJavaToken) c).getTokenType() == JavaTokenType.SEMICOLON)) {
            range = TextRange.create(range.getStartOffset(), c.getTextRange().getEndOffset());
          } else {
            continue;
          }
        }
        break;
      }
    }
    JavaElementArrangementEntry entry = createNewEntry(field, range, FIELD, field.getName(), true);
    processEntry(entry, field, field.getInitializer());
  }
 @NotNull
 public String getText() {
   RangeHighlighterEx highlighter = this.highlighter;
   if (highlighter == null) throw new RuntimeException("info not applied yet");
   if (!highlighter.isValid()) return "";
   return highlighter.getDocument().getText(TextRange.create(highlighter));
 }
  protected static List<TextRange> getSelectedRanges(@NotNull SelectionModel selectionModel) {
    final List<TextRange> ranges = new SmartList<TextRange>();
    if (selectionModel.hasSelection()) {
      TextRange range =
          TextRange.create(selectionModel.getSelectionStart(), selectionModel.getSelectionEnd());
      ranges.add(range);
    } else if (selectionModel.hasBlockSelection()) {
      int[] starts = selectionModel.getBlockSelectionStarts();
      int[] ends = selectionModel.getBlockSelectionEnds();
      for (int i = 0; i < starts.length; i++) {
        ranges.add(TextRange.create(starts[i], ends[i]));
      }
    }

    return ranges;
  }
 static TextRange processDocument(
     Document document, RangeMarker marker, Commenter commenter, boolean escape) {
   if (commenter instanceof EscapingCommenter) {
     if (escape) {
       ((EscapingCommenter) commenter).escape(document, marker);
     } else {
       ((EscapingCommenter) commenter).unescape(document, marker);
     }
   }
   return TextRange.create(marker.getStartOffset(), marker.getEndOffset());
 }
 public boolean changesRange(TextRange range) {
   if (myAltFullRange != null) {
     return range.intersects(myAltFullRange.getStartOffset(), myAltFullRange.getEndOffset());
   } else if (!myMarkers.isEmpty()) {
     TextRange hostRange =
         TextRange.create(
             myMarkers.get(0).first.getStartOffset(),
             myMarkers.get(myMarkers.size() - 1).first.getEndOffset());
     return range.intersects(hostRange);
   }
   return false;
 }
  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;
      }
    }
  }
 private boolean isWhiteSpaceOrComment(@NotNull PsiElement element, @NotNull TextRange range) {
   final TextRange textRange = element.getTextRange();
   TextRange intersection = range.intersection(textRange);
   if (intersection == null) {
     return false;
   }
   intersection =
       TextRange.create(
           Math.max(intersection.getStartOffset() - textRange.getStartOffset(), 0),
           Math.min(
               intersection.getEndOffset() - textRange.getStartOffset(), textRange.getLength()));
   return isWhiteSpaceOrComment(element)
       || intersection.substring(element.getText()).trim().length() == 0;
 }
 @NotNull
 private static RangeMarker combineScopes(
     RangeMarker old, @NotNull TextRange scope, int textLength, @NotNull Document document) {
   if (old == null) {
     if (scope.equalsToRange(0, textLength)) return WHOLE_FILE_DIRTY_MARKER;
     return document.createRangeMarker(scope);
   }
   if (old == WHOLE_FILE_DIRTY_MARKER) return old;
   TextRange oldRange = TextRange.create(old);
   TextRange union = scope.union(oldRange);
   if (old.isValid() && union.equals(oldRange)) {
     return old;
   }
   if (union.getEndOffset() > textLength) {
     union = union.intersection(new TextRange(0, textLength));
   }
   assert union != null;
   return document.createRangeMarker(union);
 }
 private static List<Token<PyElementType>> tokenize(@NotNull String s) {
   final List<Token<PyElementType>> tokens = new ArrayList<>();
   final _PyTypeLexer lexer = new _PyTypeLexer(new StringReader(s));
   lexer.reset(s, 0, s.length(), lexer.yystate());
   try {
     PyElementType type;
     while ((type = lexer.advance()) != null) {
       if (type == PyTypeTokenTypes.SPACE || type == PyTypeTokenTypes.MARKUP) {
         continue;
       }
       final TextRange range = TextRange.create(lexer.getTokenStart(), lexer.getTokenEnd());
       final Token<PyElementType> token = new Token<>(type, lexer.yytext(), range);
       tokens.add(token);
     }
   } catch (IOException e) {
     return Collections.emptyList();
   } catch (Error e) {
     return Collections.emptyList();
   }
   return tokens;
 }
 /**
  * @return null for processed file, whole file for untouched or entirely dirty file, range(usually
  *     code block) for dirty region (optimization)
  */
 @Nullable
 public TextRange getFileDirtyScope(@NotNull Document document, int passId) {
   synchronized (myDocumentToStatusMap) {
     PsiFile file = PsiDocumentManager.getInstance(myProject).getPsiFile(document);
     if (!ProblemHighlightFilter.shouldHighlightFile(file)) return null;
     FileStatus status = myDocumentToStatusMap.get(document);
     if (status == null) {
       return file == null ? null : file.getTextRange();
     }
     if (status.defensivelyMarked) {
       status.markWholeFileDirty(myProject);
       status.defensivelyMarked = false;
     }
     LOG.assertTrue(status.dirtyScopes.containsKey(passId), "Unknown pass " + passId);
     RangeMarker marker = status.dirtyScopes.get(passId);
     return marker == null
         ? null
         : marker.isValid()
             ? TextRange.create(marker)
             : new TextRange(0, document.getTextLength());
   }
 }
  public static boolean hasTemplatePrefix(LookupImpl lookup, char shortcutChar) {
    lookup.refreshUi(false, false); // to bring the list model up to date

    CompletionProcess completion = CompletionService.getCompletionService().getCurrentCompletion();
    if (completion == null || !completion.isAutopopupCompletion()) {
      return false;
    }

    if (lookup.isSelectionTouched()) {
      return false;
    }

    final PsiFile file = lookup.getPsiFile();
    if (file == null) return false;

    final Editor editor = lookup.getEditor();
    PsiDocumentManager.getInstance(file.getProject()).commitDocument(editor.getDocument());

    final int end = editor.getCaretModel().getOffset();
    final int start = lookup.getLookupStart();
    final String prefix =
        !lookup.getItems().isEmpty()
            ? editor.getDocument().getText(TextRange.create(start, end))
            : ListTemplatesHandler.getPrefix(editor.getDocument(), end);

    if (TemplateSettings.getInstance().getTemplates(prefix).isEmpty()) {
      return false;
    }

    for (TemplateImpl template :
        SurroundWithTemplateHandler.getApplicableTemplates(editor, file, false)) {
      if (prefix.equals(template.getKey())
          && shortcutChar == TemplateSettings.getInstance().getShortcutChar(template)) {
        return true;
      }
    }
    return false;
  }
  public ASTNode processRange(final ASTNode element, final int startOffset, final int endOffset) {
    final PsiElement psiElement = SourceTreeToPsiMap.treeElementToPsi(element);
    assert psiElement != null;
    final PsiFile file = psiElement.getContainingFile();
    final Document document = file.getViewProvider().getDocument();
    final RangeMarker rangeMarker =
        document != null && endOffset < document.getTextLength()
            ? document.createRangeMarker(startOffset, endOffset)
            : null;

    PsiElement elementToFormat =
        document instanceof DocumentWindow
            ? InjectedLanguageManager.getInstance(file.getProject()).getTopLevelFile(file)
            : psiElement;
    final PsiFile fileToFormat = elementToFormat.getContainingFile();

    final FormattingModelBuilder builder = LanguageFormatting.INSTANCE.forContext(fileToFormat);
    if (builder != null) {
      TextRange range = preprocess(element, TextRange.create(startOffset, endOffset));
      if (document instanceof DocumentWindow) {
        DocumentWindow documentWindow = (DocumentWindow) document;
        range = documentWindow.injectedToHost(range);
      }

      // final SmartPsiElementPointer pointer =
      // SmartPointerManager.getInstance(psiElement.getProject()).createSmartPsiElementPointer(psiElement);
      final FormattingModel model =
          CoreFormatterUtil.buildModel(
              builder, elementToFormat, mySettings, FormattingMode.REFORMAT);
      if (file.getTextLength() > 0) {
        try {
          FormatterEx.getInstanceEx()
              .format(
                  model,
                  mySettings,
                  mySettings.getIndentOptions(fileToFormat.getFileType()),
                  new FormatTextRanges(range, true));

          wrapLongLinesIfNecessary(file, document, startOffset, endOffset);
        } catch (IncorrectOperationException e) {
          LOG.error(e);
        }
      }

      if (!psiElement.isValid()) {
        if (rangeMarker != null) {
          final PsiElement at = file.findElementAt(rangeMarker.getStartOffset());
          final PsiElement result = PsiTreeUtil.getParentOfType(at, psiElement.getClass(), false);
          assert result != null;
          rangeMarker.dispose();
          return result.getNode();
        } else {
          assert false;
        }
      }
      //      return SourceTreeToPsiMap.psiElementToTree(pointer.getElement());
    }

    if (rangeMarker != null) {
      rangeMarker.dispose();
    }
    return element;
  }
  private static TextRange preprocess(@NotNull final ASTNode node, @NotNull TextRange range) {
    TextRange result = range;
    PsiElement psi = node.getPsi();
    if (!psi.isValid()) {
      for (PreFormatProcessor processor : Extensions.getExtensions(PreFormatProcessor.EP_NAME)) {
        result = processor.process(node, result);
      }
      return result;
    }

    PsiFile file = psi.getContainingFile();

    // We use a set here because we encountered a situation when more than one PSI leaf points to
    // the same injected fragment
    // (at least for sql injected into sql).
    final LinkedHashSet<TextRange> injectedFileRangesSet = ContainerUtilRt.newLinkedHashSet();

    if (!psi.getProject().isDefault()) {
      List<DocumentWindow> injectedDocuments =
          InjectedLanguageUtil.getCachedInjectedDocuments(file);
      if (!injectedDocuments.isEmpty()) {
        for (DocumentWindow injectedDocument : injectedDocuments) {
          injectedFileRangesSet.add(
              TextRange.from(injectedDocument.injectedToHost(0), injectedDocument.getTextLength()));
        }
      } else {
        Collection<PsiLanguageInjectionHost> injectionHosts = collectInjectionHosts(file, range);
        PsiLanguageInjectionHost.InjectedPsiVisitor visitor =
            new PsiLanguageInjectionHost.InjectedPsiVisitor() {
              @Override
              public void visit(
                  @NotNull PsiFile injectedPsi,
                  @NotNull List<PsiLanguageInjectionHost.Shred> places) {
                for (PsiLanguageInjectionHost.Shred place : places) {
                  Segment rangeMarker = place.getHostRangeMarker();
                  injectedFileRangesSet.add(
                      TextRange.create(rangeMarker.getStartOffset(), rangeMarker.getEndOffset()));
                }
              }
            };
        for (PsiLanguageInjectionHost host : injectionHosts) {
          InjectedLanguageUtil.enumerate(host, visitor);
        }
      }
    }

    if (!injectedFileRangesSet.isEmpty()) {
      List<TextRange> ranges = ContainerUtilRt.newArrayList(injectedFileRangesSet);
      Collections.reverse(ranges);
      for (TextRange injectedFileRange : ranges) {
        int startHostOffset = injectedFileRange.getStartOffset();
        int endHostOffset = injectedFileRange.getEndOffset();
        if (startHostOffset >= range.getStartOffset() && endHostOffset <= range.getEndOffset()) {
          PsiFile injected = InjectedLanguageUtil.findInjectedPsiNoCommit(file, startHostOffset);
          if (injected != null) {
            int startInjectedOffset =
                range.getStartOffset() > startHostOffset
                    ? startHostOffset - range.getStartOffset()
                    : 0;
            int endInjectedOffset = injected.getTextLength();
            if (range.getEndOffset() < endHostOffset) {
              endInjectedOffset -= endHostOffset - range.getEndOffset();
            }
            final TextRange initialInjectedRange =
                TextRange.create(startInjectedOffset, endInjectedOffset);
            TextRange injectedRange = initialInjectedRange;
            for (PreFormatProcessor processor :
                Extensions.getExtensions(PreFormatProcessor.EP_NAME)) {
              injectedRange = processor.process(injected.getNode(), injectedRange);
            }

            // Allow only range expansion (not reduction) for injected context.
            if ((initialInjectedRange.getStartOffset() > injectedRange.getStartOffset()
                    && initialInjectedRange.getStartOffset() > 0)
                || (initialInjectedRange.getEndOffset() < injectedRange.getEndOffset()
                    && initialInjectedRange.getEndOffset() < injected.getTextLength())) {
              range =
                  TextRange.create(
                      range.getStartOffset()
                          + injectedRange.getStartOffset()
                          - initialInjectedRange.getStartOffset(),
                      range.getEndOffset()
                          + initialInjectedRange.getEndOffset()
                          - injectedRange.getEndOffset());
            }
          }
        }
      }
    }

    for (PreFormatProcessor processor : Extensions.getExtensions(PreFormatProcessor.EP_NAME)) {
      result = processor.process(node, result);
    }

    return result;
  }
  public void processText(
      PsiFile file, final FormatTextRanges ranges, boolean doPostponedFormatting) {
    final Project project = file.getProject();
    Document document = PsiDocumentManager.getInstance(project).getDocument(file);
    final List<FormatTextRanges.FormatTextRange> textRanges = ranges.getRanges();
    if (document instanceof DocumentWindow) {
      file = InjectedLanguageManager.getInstance(file.getProject()).getTopLevelFile(file);
      final DocumentWindow documentWindow = (DocumentWindow) document;
      for (FormatTextRanges.FormatTextRange range : textRanges) {
        range.setTextRange(documentWindow.injectedToHost(range.getTextRange()));
      }
      document = documentWindow.getDelegate();
    }

    final FormattingModelBuilder builder = LanguageFormatting.INSTANCE.forContext(file);

    if (builder != null) {
      if (file.getTextLength() > 0) {
        try {
          final PsiElement startElement =
              file.findElementAt(textRanges.get(0).getTextRange().getStartOffset());
          final PsiElement endElement =
              file.findElementAt(
                  textRanges.get(textRanges.size() - 1).getTextRange().getEndOffset() - 1);
          final PsiElement commonParent =
              startElement != null && endElement != null
                  ? PsiTreeUtil.findCommonParent(startElement, endElement)
                  : null;
          ASTNode node = null;
          if (commonParent != null) {
            node = commonParent.getNode();
          }
          if (node == null) {
            node = file.getNode();
          }
          for (FormatTextRanges.FormatTextRange range : ranges.getRanges()) {
            TextRange rangeToUse = preprocess(node, range.getTextRange());
            range.setTextRange(rangeToUse);
          }
          if (doPostponedFormatting) {
            RangeMarker[] markers = new RangeMarker[textRanges.size()];
            int i = 0;
            for (FormatTextRanges.FormatTextRange range : textRanges) {
              TextRange textRange = range.getTextRange();
              int start = textRange.getStartOffset();
              int end = textRange.getEndOffset();
              if (start >= 0 && end > start && end <= document.getTextLength()) {
                markers[i] = document.createRangeMarker(textRange);
                markers[i].setGreedyToLeft(true);
                markers[i].setGreedyToRight(true);
                i++;
              }
            }
            final PostprocessReformattingAspect component =
                file.getProject().getComponent(PostprocessReformattingAspect.class);
            FormattingProgressTask.FORMATTING_CANCELLED_FLAG.set(false);
            component.doPostponedFormatting(file.getViewProvider());
            i = 0;
            for (FormatTextRanges.FormatTextRange range : textRanges) {
              RangeMarker marker = markers[i];
              if (marker != null) {
                range.setTextRange(TextRange.create(marker));
                marker.dispose();
              }
              i++;
            }
          }
          if (FormattingProgressTask.FORMATTING_CANCELLED_FLAG.get()) {
            return;
          }

          final FormattingModel originalModel =
              CoreFormatterUtil.buildModel(builder, file, mySettings, FormattingMode.REFORMAT);
          final FormattingModel model =
              new DocumentBasedFormattingModel(
                  originalModel.getRootBlock(),
                  document,
                  project,
                  mySettings,
                  file.getFileType(),
                  file);

          FormatterEx formatter = FormatterEx.getInstanceEx();
          if (CodeStyleManager.getInstance(project).isSequentialProcessingAllowed()) {
            formatter.setProgressTask(new FormattingProgressTask(project, file, document));
          }

          CommonCodeStyleSettings.IndentOptions indentOptions = null;
          if (builder instanceof FormattingModelBuilderEx) {
            indentOptions =
                ((FormattingModelBuilderEx) builder)
                    .getIndentOptionsToUse(file, ranges, mySettings);
          }
          if (indentOptions == null) {
            indentOptions = mySettings.getIndentOptions(file.getFileType());
          }

          formatter.format(model, mySettings, indentOptions, ranges);
          for (FormatTextRanges.FormatTextRange range : textRanges) {
            TextRange textRange = range.getTextRange();
            wrapLongLinesIfNecessary(
                file, document, textRange.getStartOffset(), textRange.getEndOffset());
          }
        } catch (IncorrectOperationException e) {
          LOG.error(e);
        }
      }
    }
  }
  private void dumpEditorMarkupAndSelection(PrintStream dumpStream) {
    dumpStream.println(mySearchResults.getFindModel());
    if (myReplacementPreviewText != null) {
      dumpStream.println("--");
      dumpStream.println("Replacement Preview: " + myReplacementPreviewText);
    }
    dumpStream.println("--");

    Editor editor = mySearchResults.getEditor();

    RangeHighlighter[] highlighters = editor.getMarkupModel().getAllHighlighters();
    List<Pair<Integer, Character>> ranges = new ArrayList<Pair<Integer, Character>>();
    for (RangeHighlighter highlighter : highlighters) {
      ranges.add(new Pair<Integer, Character>(highlighter.getStartOffset(), '['));
      ranges.add(new Pair<Integer, Character>(highlighter.getEndOffset(), ']'));
    }

    SelectionModel selectionModel = editor.getSelectionModel();

    if (selectionModel.getSelectionStart() != selectionModel.getSelectionEnd()) {
      ranges.add(new Pair<Integer, Character>(selectionModel.getSelectionStart(), '<'));
      ranges.add(new Pair<Integer, Character>(selectionModel.getSelectionEnd(), '>'));
    }
    ranges.add(new Pair<Integer, Character>(-1, '\n'));
    ranges.add(new Pair<Integer, Character>(editor.getDocument().getTextLength() + 1, '\n'));
    ContainerUtil.sort(
        ranges,
        new Comparator<Pair<Integer, Character>>() {
          @Override
          public int compare(Pair<Integer, Character> pair, Pair<Integer, Character> pair2) {
            int res = pair.first - pair2.first;
            if (res == 0) {

              Character c1 = pair.second;
              Character c2 = pair2.second;
              if (c1 == '<' && c2 == '[') {
                return 1;
              } else if (c1 == '[' && c2 == '<') {
                return -1;
              }
              return c1.compareTo(c2);
            }
            return res;
          }
        });

    Document document = editor.getDocument();
    for (int i = 0; i < ranges.size() - 1; ++i) {
      Pair<Integer, Character> pair = ranges.get(i);
      Pair<Integer, Character> pair1 = ranges.get(i + 1);
      dumpStream.print(
          pair.second
              + document.getText(
                  TextRange.create(
                      Math.max(pair.first, 0), Math.min(pair1.first, document.getTextLength()))));
    }
    dumpStream.println("\n--");

    if (NotFound) {
      dumpStream.println("Not Found");
      dumpStream.println("--");
      NotFound = false;
    }

    for (RangeHighlighter highlighter : highlighters) {
      dumpStream.println(highlighter + " : " + highlighter.getTextAttributes());
    }
    dumpStream.println("------------");
  }
    public String setup(
        final String[] texts,
        Function<String, String> escapeFunction,
        final EnumSet<ParameterInfoUIContextEx.Flag>[] flags,
        final Color background) {
      StringBuilder buf = new StringBuilder();
      removeAll();
      setBackground(background);
      int index = 0;
      int curOffset = 0;
      final ArrayList<OneLineComponent> components = new ArrayList<>();

      Map<TextRange, ParameterInfoUIContextEx.Flag> flagsMap = new TreeMap<>(TEXT_RANGE_COMPARATOR);

      String line = "";
      for (int i = 0; i < texts.length; i++) {
        String paramText = escapeString(texts[i], escapeFunction);
        if (paramText == null) break;
        line += texts[i];
        final EnumSet<ParameterInfoUIContextEx.Flag> flag = flags[i];
        if (flag.contains(ParameterInfoUIContextEx.Flag.HIGHLIGHT)) {
          flagsMap.put(
              TextRange.create(curOffset, curOffset + paramText.trim().length()),
              ParameterInfoUIContextEx.Flag.HIGHLIGHT);
        }

        if (flag.contains(ParameterInfoUIContextEx.Flag.DISABLE)) {
          flagsMap.put(
              TextRange.create(curOffset, curOffset + paramText.trim().length()),
              ParameterInfoUIContextEx.Flag.DISABLE);
        }

        if (flag.contains(ParameterInfoUIContextEx.Flag.STRIKEOUT)) {
          flagsMap.put(
              TextRange.create(curOffset, curOffset + paramText.trim().length()),
              ParameterInfoUIContextEx.Flag.STRIKEOUT);
        }

        curOffset += paramText.length();
        if (line.length() >= 50) {
          final OneLineComponent component = new OneLineComponent();
          buf.append(component.setup(escapeString(line, escapeFunction), flagsMap, background));
          add(
              component,
              new GridBagConstraints(
                  0,
                  index,
                  1,
                  1,
                  1,
                  0,
                  GridBagConstraints.WEST,
                  GridBagConstraints.NONE,
                  new Insets(0, 0, 0, 0),
                  0,
                  0));
          index += 1;
          flagsMap.clear();
          curOffset = 0;
          line = "";
          components.add(component);
        }
      }
      final OneLineComponent component = new OneLineComponent();
      buf.append(component.setup(escapeString(line, escapeFunction), flagsMap, background));
      add(
          component,
          new GridBagConstraints(
              0,
              index,
              1,
              1,
              1,
              0,
              GridBagConstraints.WEST,
              GridBagConstraints.NONE,
              new Insets(0, 0, 0, 0),
              0,
              0));
      components.add(component);
      myOneLineComponents = components.toArray(new OneLineComponent[components.size()]);
      return buf.toString();
    }
    @Nullable
    @Override
    public ParseResult fun(@NotNull Pair<Token<PyElementType>, List<Token<PyElementType>>> value) {
      final Token<PyElementType> first = value.getFirst();
      final List<Token<PyElementType>> rest = value.getSecond();
      final TextRange firstRange = first.getRange();
      final boolean unqualified = rest.isEmpty();

      if (unqualified) {
        final ParseResult result = parseBuiltinType(first);
        if (result != null) {
          return result;
        }
      }

      final PsiFile file = myAnchor.getContainingFile();
      final List<Token<PyElementType>> tokens = new ArrayList<>();
      tokens.add(first);
      tokens.addAll(rest);

      if (file instanceof PyFile) {
        final PyFile pyFile = (PyFile) file;
        final TypeEvalContext context = TypeEvalContext.codeInsightFallback(file.getProject());
        final Map<TextRange, PyType> types = new HashMap<>();
        final Map<PyType, TextRange> fullRanges = new HashMap<>();
        final Map<PyType, PyImportElement> imports = new HashMap<>();

        PyType type = resolveQualifierType(tokens, pyFile, context, types, fullRanges, imports);
        PsiElement resolved = type != null ? getElement(type) : null;

        if (type != null) {
          final PyResolveContext resolveContext =
              PyResolveContext.defaultContext().withTypeEvalContext(context);
          final PyExpression expression =
              myAnchor instanceof PyExpression ? (PyExpression) myAnchor : null;

          for (Token<PyElementType> token : tokens) {
            final PyType qualifierType = type;
            type = null;
            final List<? extends RatedResolveResult> results =
                qualifierType.resolveMember(
                    token.getText().toString(), expression, AccessDirection.READ, resolveContext);
            if (results != null && !results.isEmpty()) {
              resolved = results.get(0).getElement();
              if (resolved instanceof PyTypedElement) {
                type = context.getType((PyTypedElement) resolved);
                if (type != null && !allowResolveToType(type)) {
                  type = null;
                  break;
                }
                if (type instanceof PyClassLikeType) {
                  type = ((PyClassLikeType) type).toInstance();
                }
              }
            }
            if (type == null) {
              break;
            }
            types.put(token.getRange(), type);
            fullRanges.put(
                type,
                TextRange.create(firstRange.getStartOffset(), token.getRange().getEndOffset()));
          }
          if (type != null) {
            return new ParseResult(resolved, type, types, fullRanges, imports);
          }
        }
      }

      return EMPTY_RESULT;
    }
    public String setup(
        final String[] texts,
        final EnumSet<ParameterInfoUIContextEx.Flag>[] flags,
        final Color background) {
      StringBuilder buf = new StringBuilder();
      removeAll();
      final String[] lines =
          UIUtil.splitText(StringUtil.join(texts), getFontMetrics(BOLD_FONT), myWidthLimit, ',');

      int index = 0;
      int curOffset = 0;

      myOneLineComponents = new OneLineComponent[lines.length];

      Map<TextRange, ParameterInfoUIContextEx.Flag> flagsMap =
          new TreeMap<TextRange, ParameterInfoUIContextEx.Flag>(TEXT_RANGE_COMPARATOR);

      int added = 0;
      for (int i = 0; i < texts.length; i++) {
        String line = escapeString(texts[i]);
        if (lines.length <= index) break;
        String text = lines[index];
        final int paramCount = StringUtil.split(text, ", ").size();
        final EnumSet<ParameterInfoUIContextEx.Flag> flag = flags[i];
        if (flag.contains(ParameterInfoUIContextEx.Flag.HIGHLIGHT)) {
          flagsMap.put(
              TextRange.create(curOffset, curOffset + line.trim().length()),
              ParameterInfoUIContextEx.Flag.HIGHLIGHT);
        }

        if (flag.contains(ParameterInfoUIContextEx.Flag.DISABLE)) {
          flagsMap.put(
              TextRange.create(curOffset, curOffset + line.trim().length()),
              ParameterInfoUIContextEx.Flag.DISABLE);
        }

        if (flag.contains(ParameterInfoUIContextEx.Flag.STRIKEOUT)) {
          flagsMap.put(
              TextRange.create(curOffset, curOffset + line.trim().length()),
              ParameterInfoUIContextEx.Flag.STRIKEOUT);
        }

        curOffset += line.length();
        if (i == paramCount + added - 1) {
          myOneLineComponents[index] = new OneLineComponent();
          setBackground(background);
          buf.append(myOneLineComponents[index].setup(escapeString(text), flagsMap, background));
          add(
              myOneLineComponents[index],
              new GridBagConstraints(
                  0,
                  index,
                  1,
                  1,
                  1,
                  0,
                  GridBagConstraints.WEST,
                  GridBagConstraints.NONE,
                  new Insets(0, 0, 0, 0),
                  0,
                  0));
          index += 1;
          flagsMap.clear();
          curOffset = 1;
          added += paramCount;
        }
      }
      return buf.toString();
    }
  public void performAction(IntroduceOperation operation) {
    final PsiFile file = operation.getFile();
    if (!CommonRefactoringUtil.checkReadOnlyStatus(file)) {
      return;
    }
    final Editor editor = operation.getEditor();
    if (editor.getSettings().isVariableInplaceRenameEnabled()) {
      final TemplateState templateState =
          TemplateManagerImpl.getTemplateState(operation.getEditor());
      if (templateState != null && !templateState.isFinished()) {
        return;
      }
    }

    PsiElement element1 = null;
    PsiElement element2 = null;
    final SelectionModel selectionModel = editor.getSelectionModel();
    boolean singleElementSelection = false;
    if (selectionModel.hasSelection()) {
      element1 = file.findElementAt(selectionModel.getSelectionStart());
      element2 = file.findElementAt(selectionModel.getSelectionEnd() - 1);
      if (element1 instanceof PsiWhiteSpace) {
        int startOffset = element1.getTextRange().getEndOffset();
        element1 = file.findElementAt(startOffset);
      }
      if (element2 instanceof PsiWhiteSpace) {
        int endOffset = element2.getTextRange().getStartOffset();
        element2 = file.findElementAt(endOffset - 1);
      }
      if (element1 == element2) {
        singleElementSelection = true;
      }
    } else {
      if (smartIntroduce(operation)) {
        return;
      }
      final CaretModel caretModel = editor.getCaretModel();
      final Document document = editor.getDocument();
      int lineNumber = document.getLineNumber(caretModel.getOffset());
      if ((lineNumber >= 0) && (lineNumber < document.getLineCount())) {
        element1 = file.findElementAt(document.getLineStartOffset(lineNumber));
        element2 = file.findElementAt(document.getLineEndOffset(lineNumber) - 1);
      }
    }
    final Project project = operation.getProject();
    if (element1 == null || element2 == null) {
      showCannotPerformError(project, editor);
      return;
    }

    element1 = PyRefactoringUtil.getSelectedExpression(project, file, element1, element2);
    if (element1 == null) {
      showCannotPerformError(project, editor);
      return;
    }

    if (singleElementSelection && element1 instanceof PyStringLiteralExpression) {
      final PyStringLiteralExpression literal = (PyStringLiteralExpression) element1;
      // Currently introduce for substrings of a multi-part string literals is not supported
      if (literal.getStringNodes().size() > 1) {
        showCannotPerformError(project, editor);
        return;
      }
      final int offset = element1.getTextOffset();
      final TextRange selectionRange =
          TextRange.create(selectionModel.getSelectionStart(), selectionModel.getSelectionEnd());
      final TextRange elementRange = element1.getTextRange();
      if (!elementRange.equals(selectionRange) && elementRange.contains(selectionRange)) {
        final TextRange innerRange = literal.getStringValueTextRange();
        final TextRange intersection = selectionRange.shiftRight(-offset).intersection(innerRange);
        final TextRange finalRange = intersection != null ? intersection : selectionRange;
        final String text = literal.getText();
        if (getFormatValueExpression(literal) != null && breaksStringFormatting(text, finalRange)
            || getNewStyleFormatValueExpression(literal) != null
                && breaksNewStyleStringFormatting(text, finalRange)
            || breaksStringEscaping(text, finalRange)) {
          showCannotPerformError(project, editor);
          return;
        }
        element1.putUserData(
            PyReplaceExpressionUtil.SELECTION_BREAKS_AST_NODE, Pair.create(element1, finalRange));
      }
    }

    if (!checkIntroduceContext(file, editor, element1)) {
      return;
    }
    operation.setElement(element1);
    performActionOnElement(operation);
  }
Example #26
0
  private static void doPaste(
      final Editor editor,
      final Project project,
      final PsiFile file,
      final Document document,
      final Producer<Transferable> producer) {
    Transferable content = null;

    if (producer != null) {
      content = producer.produce();
    } else {
      CopyPasteManager manager = CopyPasteManager.getInstance();
      if (manager.areDataFlavorsAvailable(DataFlavor.stringFlavor)) {
        content = manager.getContents();
        if (content != null) {
          manager.stopKillRings();
        }
      }
    }

    if (content != null) {
      String text = null;
      try {
        text = (String) content.getTransferData(DataFlavor.stringFlavor);
      } catch (Exception e) {
        editor.getComponent().getToolkit().beep();
      }
      if (text == null) return;

      final CodeInsightSettings settings = CodeInsightSettings.getInstance();

      final Map<CopyPastePostProcessor, TextBlockTransferableData> extraData =
          new HashMap<CopyPastePostProcessor, TextBlockTransferableData>();
      for (CopyPastePostProcessor processor :
          Extensions.getExtensions(CopyPastePostProcessor.EP_NAME)) {
        TextBlockTransferableData data = processor.extractTransferableData(content);
        if (data != null) {
          extraData.put(processor, data);
        }
      }

      text = TextBlockTransferable.convertLineSeparators(text, "\n", extraData.values());

      final CaretModel caretModel = editor.getCaretModel();
      final SelectionModel selectionModel = editor.getSelectionModel();
      final int col = caretModel.getLogicalPosition().column;

      // There is a possible case that we want to perform paste while there is an active selection
      // at the editor and caret is located
      // inside it (e.g. Ctrl+A is pressed while caret is not at the zero column). We want to insert
      // the text at selection start column
      // then, hence, inserted block of text should be indented according to the selection start as
      // well.
      final int blockIndentAnchorColumn;
      final int caretOffset = caretModel.getOffset();
      if (selectionModel.hasSelection() && caretOffset >= selectionModel.getSelectionStart()) {
        blockIndentAnchorColumn =
            editor.offsetToLogicalPosition(selectionModel.getSelectionStart()).column;
      } else {
        blockIndentAnchorColumn = col;
      }

      // We assume that EditorModificationUtil.insertStringAtCaret() is smart enough to remove
      // currently selected text (if any).

      RawText rawText = RawText.fromTransferable(content);
      String newText = text;
      for (CopyPastePreProcessor preProcessor :
          Extensions.getExtensions(CopyPastePreProcessor.EP_NAME)) {
        newText = preProcessor.preprocessOnPaste(project, file, editor, newText, rawText);
      }
      int indentOptions =
          text.equals(newText) ? settings.REFORMAT_ON_PASTE : CodeInsightSettings.REFORMAT_BLOCK;
      text = newText;

      if (LanguageFormatting.INSTANCE.forContext(file) == null
          && indentOptions != CodeInsightSettings.NO_REFORMAT) {
        indentOptions = CodeInsightSettings.INDENT_BLOCK;
      }

      final String _text = text;
      ApplicationManager.getApplication()
          .runWriteAction(
              new Runnable() {
                @Override
                public void run() {
                  EditorModificationUtil.insertStringAtCaret(editor, _text, false, true);
                }
              });

      int length = text.length();
      int offset = caretModel.getOffset() - length;
      if (offset < 0) {
        length += offset;
        offset = 0;
      }
      final RangeMarker bounds = document.createRangeMarker(offset, offset + length);

      caretModel.moveToOffset(bounds.getEndOffset());
      editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
      selectionModel.removeSelection();

      final Ref<Boolean> indented = new Ref<Boolean>(Boolean.FALSE);
      for (Map.Entry<CopyPastePostProcessor, TextBlockTransferableData> e : extraData.entrySet()) {
        //noinspection unchecked
        e.getKey()
            .processTransferableData(project, editor, bounds, caretOffset, indented, e.getValue());
      }

      boolean pastedTextContainsWhiteSpacesOnly =
          CharArrayUtil.shiftForward(document.getCharsSequence(), bounds.getStartOffset(), " \n\t")
              >= bounds.getEndOffset();

      VirtualFile virtualFile = file.getVirtualFile();
      if (!pastedTextContainsWhiteSpacesOnly
          && (virtualFile == null
              || !SingleRootFileViewProvider.isTooLargeForIntelligence(virtualFile))) {
        final int indentOptions1 = indentOptions;
        ApplicationManager.getApplication()
            .runWriteAction(
                new Runnable() {
                  @Override
                  public void run() {
                    switch (indentOptions1) {
                      case CodeInsightSettings.INDENT_BLOCK:
                        if (!indented.get()) {
                          indentBlock(
                              project,
                              editor,
                              bounds.getStartOffset(),
                              bounds.getEndOffset(),
                              blockIndentAnchorColumn);
                        }
                        break;

                      case CodeInsightSettings.INDENT_EACH_LINE:
                        if (!indented.get()) {
                          indentEachLine(
                              project, editor, bounds.getStartOffset(), bounds.getEndOffset());
                        }
                        break;

                      case CodeInsightSettings.REFORMAT_BLOCK:
                        indentEachLine(
                            project,
                            editor,
                            bounds.getStartOffset(),
                            bounds
                                .getEndOffset()); // this is needed for example when inserting a
                                                  // comment before method
                        reformatBlock(
                            project, editor, bounds.getStartOffset(), bounds.getEndOffset());
                        break;
                    }
                  }
                });
      }

      if (bounds.isValid()) {
        caretModel.moveToOffset(bounds.getEndOffset());
        editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
        selectionModel.removeSelection();
        editor.putUserData(EditorEx.LAST_PASTED_REGION, TextRange.create(bounds));
      }
    }
  }
  private List<PostponedAction> normalizeAndReorderPostponedActions(
      final TreeSet<PostprocessFormattingTask> rangesToProcess, Document document) {
    final List<PostprocessFormattingTask> freeFormatingActions =
        new ArrayList<PostprocessFormattingTask>();
    final List<ReindentTask> indentActions = new ArrayList<ReindentTask>();

    PostprocessFormattingTask accumulatedTask = null;
    Iterator<PostprocessFormattingTask> iterator = rangesToProcess.iterator();
    while (iterator.hasNext()) {
      final PostprocessFormattingTask currentTask = iterator.next();
      if (accumulatedTask == null) {
        accumulatedTask = currentTask;
        iterator.remove();
      } else if (accumulatedTask.getStartOffset() > currentTask.getEndOffset()
          || accumulatedTask.getStartOffset() == currentTask.getEndOffset()
              && !canStickActionsTogether(accumulatedTask, currentTask)) {
        // action can be pushed
        if (accumulatedTask instanceof ReindentTask) {
          indentActions.add((ReindentTask) accumulatedTask);
        } else {
          freeFormatingActions.add(accumulatedTask);
        }

        accumulatedTask = currentTask;
        iterator.remove();
      } else if (accumulatedTask instanceof ReformatTask && currentTask instanceof ReindentTask) {
        // split accumulated reformat range into two
        if (accumulatedTask.getStartOffset() < currentTask.getStartOffset()) {
          final RangeMarker endOfRange =
              document.createRangeMarker(
                  accumulatedTask.getStartOffset(), currentTask.getStartOffset());
          // add heading reformat part
          rangesToProcess.add(new ReformatTask(endOfRange));
          // and manage heading whitespace because formatter does not edit it in previous action
          iterator = rangesToProcess.iterator();
          //noinspection StatementWithEmptyBody
          while (iterator.next().getRange() != currentTask.getRange()) ;
        }
        final RangeMarker rangeToProcess =
            document.createRangeMarker(currentTask.getEndOffset(), accumulatedTask.getEndOffset());
        freeFormatingActions.add(new ReformatWithHeadingWhitespaceTask(rangeToProcess));
        accumulatedTask = currentTask;
        iterator.remove();
      } else {
        if (!(accumulatedTask instanceof ReindentTask)) {
          iterator.remove();

          boolean withLeadingWhitespace =
              accumulatedTask instanceof ReformatWithHeadingWhitespaceTask;
          if (accumulatedTask instanceof ReformatTask
              && currentTask instanceof ReformatWithHeadingWhitespaceTask
              && accumulatedTask.getStartOffset() == currentTask.getStartOffset()) {
            withLeadingWhitespace = true;
          } else if (accumulatedTask instanceof ReformatWithHeadingWhitespaceTask
              && currentTask instanceof ReformatTask
              && accumulatedTask.getStartOffset() < currentTask.getStartOffset()) {
            withLeadingWhitespace = false;
          }
          int newStart = Math.min(accumulatedTask.getStartOffset(), currentTask.getStartOffset());
          int newEnd = Math.max(accumulatedTask.getEndOffset(), currentTask.getEndOffset());
          RangeMarker rangeMarker;

          if (accumulatedTask.getStartOffset() == newStart
              && accumulatedTask.getEndOffset() == newEnd) {
            rangeMarker = accumulatedTask.getRange();
          } else if (currentTask.getStartOffset() == newStart
              && currentTask.getEndOffset() == newEnd) {
            rangeMarker = currentTask.getRange();
          } else {
            rangeMarker = document.createRangeMarker(newStart, newEnd);
          }

          if (withLeadingWhitespace) {
            accumulatedTask = new ReformatWithHeadingWhitespaceTask(rangeMarker);
          } else {
            accumulatedTask = new ReformatTask(rangeMarker);
          }
        } else if (currentTask instanceof ReindentTask) {
          iterator.remove();
        } // TODO[ik]: need to be fixed to correctly process indent inside indent
      }
    }
    if (accumulatedTask != null) {
      if (accumulatedTask instanceof ReindentTask) {
        indentActions.add((ReindentTask) accumulatedTask);
      } else {
        freeFormatingActions.add(accumulatedTask);
      }
    }

    final List<PostponedAction> result = new ArrayList<PostponedAction>();
    Collections.reverse(freeFormatingActions);
    Collections.reverse(indentActions);

    if (!freeFormatingActions.isEmpty()) {
      FormatTextRanges ranges = new FormatTextRanges();
      for (PostprocessFormattingTask action : freeFormatingActions) {
        TextRange range = TextRange.create(action);
        ranges.add(range, action instanceof ReformatWithHeadingWhitespaceTask);
      }
      result.add(new ReformatRangesAction(ranges));
    }

    if (!indentActions.isEmpty()) {
      ReindentRangesAction reindentRangesAction = new ReindentRangesAction();
      for (ReindentTask action : indentActions) {
        reindentRangesAction.add(action.getRange(), action.getOldIndent());
      }
      result.add(reindentRangesAction);
    }

    return result;
  }
    private String setup(
        String text,
        Function<String, String> escapeFunction,
        int highlightStartOffset,
        int highlightEndOffset,
        boolean isDisabled,
        boolean strikeout,
        boolean isDisabledBeforeHighlight,
        Color background) {
      StringBuilder buf = new StringBuilder();
      removeAll();

      String[] lines = UIUtil.splitText(text, getFontMetrics(BOLD_FONT), myWidthLimit, ',');

      myOneLineComponents = new OneLineComponent[lines.length];

      int lineOffset = 0;

      boolean hasHighlighting =
          highlightStartOffset >= 0 && highlightEndOffset > highlightStartOffset;
      TextRange highlightingRange =
          hasHighlighting ? new TextRange(highlightStartOffset, highlightEndOffset) : null;

      for (int i = 0; i < lines.length; i++) {
        String line = lines[i];

        myOneLineComponents[i] = new OneLineComponent();

        TextRange lRange = new TextRange(lineOffset, lineOffset + line.length());
        TextRange hr = highlightingRange == null ? null : lRange.intersection(highlightingRange);
        hr = hr == null ? null : hr.shiftRight(-lineOffset);

        String before =
            escapeString(
                hr == null ? line : line.substring(0, hr.getStartOffset()), escapeFunction);
        String in = hr == null ? "" : escapeString(hr.substring(line), escapeFunction);
        String after =
            hr == null
                ? ""
                : escapeString(line.substring(hr.getEndOffset(), line.length()), escapeFunction);

        TextRange escapedHighlightingRange =
            in.isEmpty() ? null : TextRange.create(before.length(), before.length() + in.length());
        buf.append(
            myOneLineComponents[i].setup(
                before + in + after, isDisabled, strikeout, background, escapedHighlightingRange));

        if (isDisabledBeforeHighlight) {
          if (highlightStartOffset < 0 || highlightEndOffset > lineOffset) {
            myOneLineComponents[i].setDisabledBeforeHighlight();
          }
        }

        add(
            myOneLineComponents[i],
            new GridBagConstraints(
                0,
                i,
                1,
                1,
                1,
                0,
                GridBagConstraints.WEST,
                GridBagConstraints.HORIZONTAL,
                new Insets(0, 0, 0, 0),
                0,
                0));

        lineOffset += line.length();
      }
      return buf.toString();
    }