/** * Emulates pressing <code>Enter</code> at current caret position. * * @param editor target editor * @param project target project * @param shifts two-elements array which is expected to be filled with the following info: 1. The * first element holds added lines number; 2. The second element holds added symbols number; */ private static void emulateEnter( @NotNull final Editor editor, @NotNull Project project, int[] shifts) { final DataContext dataContext = prepareContext(editor.getComponent(), project); int caretOffset = editor.getCaretModel().getOffset(); Document document = editor.getDocument(); SelectionModel selectionModel = editor.getSelectionModel(); int startSelectionOffset = 0; int endSelectionOffset = 0; boolean restoreSelection = selectionModel.hasSelection(); if (restoreSelection) { startSelectionOffset = selectionModel.getSelectionStart(); endSelectionOffset = selectionModel.getSelectionEnd(); selectionModel.removeSelection(); } int textLengthBeforeWrap = document.getTextLength(); int lineCountBeforeWrap = document.getLineCount(); DataManager.getInstance() .saveInDataContext(dataContext, WRAP_LONG_LINE_DURING_FORMATTING_IN_PROGRESS_KEY, true); CommandProcessor commandProcessor = CommandProcessor.getInstance(); try { Runnable command = new Runnable() { @Override public void run() { EditorActionManager.getInstance() .getActionHandler(IdeActions.ACTION_EDITOR_ENTER) .execute(editor, dataContext); } }; if (commandProcessor.getCurrentCommand() == null) { commandProcessor.executeCommand(editor.getProject(), command, WRAP_LINE_COMMAND_NAME, null); } else { command.run(); } } finally { DataManager.getInstance() .saveInDataContext(dataContext, WRAP_LONG_LINE_DURING_FORMATTING_IN_PROGRESS_KEY, null); } int symbolsDiff = document.getTextLength() - textLengthBeforeWrap; if (restoreSelection) { int newSelectionStart = startSelectionOffset; int newSelectionEnd = endSelectionOffset; if (startSelectionOffset >= caretOffset) { newSelectionStart += symbolsDiff; } if (endSelectionOffset >= caretOffset) { newSelectionEnd += symbolsDiff; } selectionModel.setSelection(newSelectionStart, newSelectionEnd); } shifts[0] = document.getLineCount() - lineCountBeforeWrap; shifts[1] = symbolsDiff; }
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; }
public void doWrapLongLinesIfNecessary( @NotNull final Editor editor, @NotNull final Project project, @NotNull Document document, int startOffset, int endOffset) { // Normalization. int startOffsetToUse = Math.min(document.getTextLength(), Math.max(0, startOffset)); int endOffsetToUse = Math.min(document.getTextLength(), Math.max(0, endOffset)); LineWrapPositionStrategy strategy = LanguageLineWrapPositionStrategy.INSTANCE.forEditor(editor); CharSequence text = document.getCharsSequence(); int startLine = document.getLineNumber(startOffsetToUse); int endLine = document.getLineNumber(Math.max(0, endOffsetToUse - 1)); int maxLine = Math.min(document.getLineCount(), endLine + 1); int tabSize = EditorUtil.getTabSize(editor); if (tabSize <= 0) { tabSize = 1; } int spaceSize = EditorUtil.getSpaceWidth(Font.PLAIN, editor); int[] shifts = new int[2]; // shifts[0] - lines shift. // shift[1] - offset shift. for (int line = startLine; line < maxLine; line++) { int startLineOffset = document.getLineStartOffset(line); int endLineOffset = document.getLineEndOffset(line); final int preferredWrapPosition = calculatePreferredWrapPosition( editor, text, tabSize, spaceSize, startLineOffset, endLineOffset, endOffsetToUse); if (preferredWrapPosition < 0 || preferredWrapPosition >= endLineOffset) { continue; } if (preferredWrapPosition >= endOffsetToUse) { return; } // We know that current line exceeds right margin if control flow reaches this place, so, wrap // it. int wrapOffset = strategy.calculateWrapPosition( document, editor.getProject(), Math.max(startLineOffset, startOffsetToUse), Math.min(endLineOffset, endOffsetToUse), preferredWrapPosition, false, false); if (wrapOffset < 0 // No appropriate wrap position is found. // No point in splitting line when its left part contains only white spaces, example: // line start -> | | <- right margin // | aaaaaaaaaaaaaaaa|aaaaaaaaaaaaaaaaaaaa() <- don't want to wrap this // line even if it exceeds right margin || CharArrayUtil.shiftBackward(text, startLineOffset, wrapOffset - 1, " \t") < startLineOffset) { continue; } // Move caret to the target position and emulate pressing <enter>. editor.getCaretModel().moveToOffset(wrapOffset); emulateEnter(editor, project, shifts); // We know that number of lines is just increased, hence, update the data accordingly. maxLine += shifts[0]; endOffsetToUse += shifts[1]; } }
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); } } } }