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; }
public void execute(FileViewProvider viewProvider) { final CodeFormatterFacade codeFormatter = getFormatterFacade(viewProvider); codeFormatter.processText( viewProvider.getPsi(viewProvider.getBaseLanguage()), myRanges.ensureNonEmpty(), false); }
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); } } } }