| package com.intellij.codeInsight.editorActions.fillParagraph; |
| |
| import com.intellij.openapi.command.CommandProcessor; |
| import com.intellij.openapi.editor.Document; |
| import com.intellij.openapi.editor.Editor; |
| import com.intellij.openapi.util.TextRange; |
| import com.intellij.openapi.util.text.CharFilter; |
| import com.intellij.openapi.util.text.StringUtil; |
| import com.intellij.psi.*; |
| import com.intellij.psi.codeStyle.CodeStyleSettingsManager; |
| import com.intellij.psi.impl.source.codeStyle.CodeFormatterFacade; |
| import com.intellij.psi.tree.IElementType; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.util.List; |
| |
| /** |
| * Defines general re-flow paragraph functionality. |
| * Serves plain text files. |
| * |
| * User : ktisha |
| */ |
| public class ParagraphFillHandler { |
| |
| protected void performOnElement(@NotNull final PsiElement element, @NotNull final Editor editor) { |
| final Document document = editor.getDocument(); |
| |
| final TextRange textRange = getTextRange(element, editor); |
| if (textRange.isEmpty()) return; |
| final String text = textRange.substring(element.getContainingFile().getText()); |
| |
| final List<String> subStrings = StringUtil.split(text, "\n", true); |
| final String prefix = getPrefix(element); |
| final String postfix = getPostfix(element); |
| |
| final StringBuilder stringBuilder = new StringBuilder(); |
| appendPrefix(element, text, stringBuilder); |
| |
| for (String string : subStrings) { |
| final String startTrimmed = StringUtil.trimStart(string.trim(), prefix.trim()); |
| final String str = StringUtil.trimEnd(startTrimmed, postfix.trim()); |
| final String finalString = str.trim(); |
| if (!StringUtil.isEmptyOrSpaces(finalString)) |
| stringBuilder.append(finalString).append(" "); |
| } |
| appendPostfix(element, text, stringBuilder); |
| |
| final String replacementText = stringBuilder.toString(); |
| |
| CommandProcessor.getInstance().executeCommand(element.getProject(), new Runnable() { |
| @Override |
| public void run() { |
| document.replaceString(textRange.getStartOffset(), textRange.getEndOffset(), |
| replacementText); |
| final CodeFormatterFacade codeFormatter = new CodeFormatterFacade( |
| CodeStyleSettingsManager.getSettings(element.getProject()), element.getLanguage()); |
| codeFormatter.doWrapLongLinesIfNecessary(editor, element.getProject(), document, |
| textRange.getStartOffset(), |
| textRange.getStartOffset() + replacementText.length() + 1); |
| } |
| }, null, document); |
| |
| } |
| |
| protected void appendPostfix(@NotNull final PsiElement element, |
| @NotNull final String text, |
| @NotNull final StringBuilder stringBuilder) { |
| final String postfix = getPostfix(element); |
| if (text.endsWith(postfix.trim())) |
| stringBuilder.append(postfix); |
| } |
| |
| protected void appendPrefix(@NotNull final PsiElement element, |
| @NotNull final String text, |
| @NotNull final StringBuilder stringBuilder) { |
| final String prefix = getPrefix(element); |
| if (text.startsWith(prefix.trim())) |
| stringBuilder.append(prefix); |
| } |
| |
| private TextRange getTextRange(@NotNull final PsiElement element, @NotNull final Editor editor) { |
| int startOffset = getStartOffset(element, editor); |
| int endOffset = getEndOffset(element, editor); |
| return TextRange.create(startOffset, endOffset); |
| } |
| |
| private int getStartOffset(@NotNull final PsiElement element, @NotNull final Editor editor) { |
| if (isBunchOfElement(element)) { |
| final PsiElement firstElement = getFirstElement(element); |
| return firstElement != null? firstElement.getTextRange().getStartOffset() |
| : element.getTextRange().getStartOffset(); |
| } |
| final int offset = editor.getCaretModel().getOffset(); |
| final int elementTextOffset = element.getTextOffset(); |
| final Document document = editor.getDocument(); |
| int lineNumber = document.getLineNumber(offset); |
| |
| while (lineNumber != document.getLineNumber(elementTextOffset)) { |
| final String text = document.getText(TextRange.create(document.getLineStartOffset(lineNumber), |
| document.getLineEndOffset(lineNumber))); |
| if (StringUtil.isEmptyOrSpaces(text)) { |
| lineNumber += 1; |
| break; |
| } |
| lineNumber -= 1; |
| } |
| final int lineStartOffset = document.getLineStartOffset(lineNumber); |
| final String lineText = document |
| .getText(TextRange.create(lineStartOffset, document.getLineEndOffset(lineNumber))); |
| int shift = StringUtil.findFirst(lineText, CharFilter.NOT_WHITESPACE_FILTER); |
| |
| return lineStartOffset + shift; |
| } |
| |
| protected boolean isBunchOfElement(PsiElement element) { |
| return element instanceof PsiComment; |
| } |
| |
| private int getEndOffset(@NotNull final PsiElement element, @NotNull final Editor editor) { |
| if (isBunchOfElement(element)) { |
| final PsiElement next = getLastElement(element); |
| return next != null? next.getTextRange().getEndOffset() |
| : element.getTextRange().getEndOffset(); |
| } |
| final int offset = editor.getCaretModel().getOffset(); |
| final int elementTextOffset = element.getTextRange().getEndOffset(); |
| final Document document = editor.getDocument(); |
| int lineNumber = document.getLineNumber(offset); |
| |
| while (lineNumber != document.getLineNumber(elementTextOffset)) { |
| final String text = document.getText(TextRange.create(document.getLineStartOffset(lineNumber), |
| document.getLineEndOffset(lineNumber))); |
| if (StringUtil.isEmptyOrSpaces(text)) { |
| lineNumber -= 1; |
| break; |
| } |
| lineNumber += 1; |
| } |
| return document.getLineEndOffset(lineNumber); |
| } |
| |
| @Nullable |
| private PsiElement getFirstElement(@NotNull final PsiElement element) { |
| final IElementType elementType = element.getNode().getElementType(); |
| PsiElement prevSibling = element.getPrevSibling(); |
| PsiElement result = element; |
| while (prevSibling != null && (prevSibling.getNode().getElementType().equals(elementType) || |
| (atWhitespaceToken(prevSibling) && |
| StringUtil.countChars(prevSibling.getText(), '\n') <= 1))) { |
| String text = prevSibling.getText(); |
| final String prefix = getPrefix(element); |
| final String postfix = getPostfix(element); |
| text = StringUtil.trimStart(text.trim(), prefix.trim()); |
| text = StringUtil.trimEnd(text, postfix); |
| |
| if (prevSibling.getNode().getElementType().equals(elementType) && |
| StringUtil.isEmptyOrSpaces(text)) { |
| break; |
| } |
| if (prevSibling.getNode().getElementType().equals(elementType)) |
| result = prevSibling; |
| prevSibling = prevSibling.getPrevSibling(); |
| } |
| return result; |
| } |
| |
| @Nullable |
| private PsiElement getLastElement(@NotNull final PsiElement element) { |
| final IElementType elementType = element.getNode().getElementType(); |
| PsiElement nextSibling = element.getNextSibling(); |
| PsiElement result = element; |
| while (nextSibling != null && (nextSibling.getNode().getElementType().equals(elementType) || |
| (atWhitespaceToken(nextSibling) && |
| StringUtil.countChars(nextSibling.getText(), '\n') <= 1))) { |
| String text = nextSibling.getText(); |
| final String prefix = getPrefix(element); |
| final String postfix = getPostfix(element); |
| text = StringUtil.trimStart(text.trim(), prefix.trim()); |
| text = StringUtil.trimEnd(text, postfix); |
| |
| if (nextSibling.getNode().getElementType().equals(elementType) && |
| StringUtil.isEmptyOrSpaces(text)) { |
| break; |
| } |
| if (nextSibling.getNode().getElementType().equals(elementType)) |
| result = nextSibling; |
| nextSibling = nextSibling.getNextSibling(); |
| } |
| return result; |
| } |
| |
| protected boolean atWhitespaceToken(@Nullable final PsiElement element) { |
| return element instanceof PsiWhiteSpace; |
| } |
| |
| protected boolean isAvailableForElement(@Nullable final PsiElement element) { |
| return element != null; |
| } |
| |
| protected boolean isAvailableForFile(@Nullable final PsiFile psiFile) { |
| return psiFile instanceof PsiPlainTextFile; |
| } |
| |
| @NotNull |
| protected String getPrefix(@NotNull final PsiElement element) { |
| return ""; |
| } |
| |
| @NotNull |
| protected String getPostfix(@NotNull final PsiElement element) { |
| return ""; |
| } |
| |
| } |