mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-20 13:31:28 +07:00
JoinLines: better comments processing
1. Improve the support of other languages in CommentJoinLinesHandler 2. Respect right margin in CommentJoinLinesHandler 3. JoinLineProcessor#convertEndComments: do not convert if only whitespaces are going to be added 4. JoinLineProcessor#adjustWhiteSpace: do not add white-spaces before line-break Also fixes IDEA-125325 Join lines in custom file type should handle end-of-line comments GitOrigin-RevId: 392fe2455dcf19d21303a0b42ee85db7f824fa85
This commit is contained in:
committed by
intellij-monorepo-bot
parent
83aadefe01
commit
0d75036403
@@ -0,0 +1,5 @@
|
||||
class A {
|
||||
// foo<caret>
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
class A {
|
||||
<selection>// foo
|
||||
|
||||
|
||||
|
||||
</selection>
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
class A {
|
||||
// foo
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
class A {
|
||||
<selection>// foo
|
||||
|
||||
|
||||
|
||||
int x;</selection>
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
class A {
|
||||
/* foo*/ int x;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
class A {
|
||||
// foo<caret>
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
class A {
|
||||
// very very <caret>long comment line! very very long comment line!
|
||||
// very very long comment line! very very long comment line!
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
class A {
|
||||
// very very long comment line! very very long comment line! very very long
|
||||
// <caret>comment line! very very long comment line!
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
class A {
|
||||
<selection>// a
|
||||
// bb
|
||||
// ccc dddd
|
||||
// ccc
|
||||
// bb a bb a bb a
|
||||
// a
|
||||
// bb
|
||||
// ccc dddd
|
||||
// ccc
|
||||
// bb a bb a bb a
|
||||
// a
|
||||
// bb
|
||||
// ccc dddd
|
||||
// ccc
|
||||
// bb a bb a bb a
|
||||
// a
|
||||
// bb
|
||||
// ccc dddd
|
||||
// ccc
|
||||
// bb a bb a bb a
|
||||
// a
|
||||
// bb
|
||||
// ccc dddd
|
||||
// ccc
|
||||
// bb a bb a bb a
|
||||
// a
|
||||
// bb
|
||||
// ccc dddd
|
||||
// ccc
|
||||
// bb a bb a bb a
|
||||
// a
|
||||
// bb
|
||||
// ccc dddd
|
||||
// ccc
|
||||
// bb a bb a bb a</selection>
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
class A {
|
||||
// a bb ccc dddd ccc bb a bb a bb a a bb ccc
|
||||
// dddd ccc bb a bb a bb a a bb ccc dddd ccc bb
|
||||
// a bb a bb a a bb ccc dddd ccc bb a bb a bb a
|
||||
// a bb ccc dddd ccc bb a bb a bb a a bb ccc
|
||||
// dddd ccc bb a bb a bb a a bb ccc dddd ccc bb
|
||||
// a bb a bb a
|
||||
|
||||
}
|
||||
@@ -16,11 +16,13 @@
|
||||
package com.intellij.java.codeInsight;
|
||||
|
||||
import com.intellij.JavaTestUtil;
|
||||
import com.intellij.application.options.CodeStyle;
|
||||
import com.intellij.ide.DataManager;
|
||||
import com.intellij.lang.java.JavaLanguage;
|
||||
import com.intellij.openapi.actionSystem.IdeActions;
|
||||
import com.intellij.openapi.editor.actionSystem.EditorActionHandler;
|
||||
import com.intellij.openapi.editor.actionSystem.EditorActionManager;
|
||||
import com.intellij.psi.codeStyle.CodeStyleSettings;
|
||||
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
|
||||
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
|
||||
import com.intellij.testFramework.LightJavaCodeInsightTestCase;
|
||||
@@ -245,6 +247,21 @@ public class JoinLinesTest extends LightJavaCodeInsightTestCase {
|
||||
|
||||
public void testConvertComment() { doTest();}
|
||||
public void testConvertComment2() { doTest();}
|
||||
public void testConvertFinalLineComment() { doTest();}
|
||||
public void testConvertFinalLineComment2() { doTest();}
|
||||
public void testConvertFinalLineComment3() { doTest();}
|
||||
public void testConvertLongLine() {
|
||||
CommonCodeStyleSettings settings = getJavaSettings();
|
||||
settings.RIGHT_MARGIN = 79;
|
||||
|
||||
doTest();
|
||||
}
|
||||
public void testConvertMultiLongLine() {
|
||||
CommonCodeStyleSettings settings = getJavaSettings();
|
||||
settings.RIGHT_MARGIN = 50;
|
||||
|
||||
doTest();
|
||||
}
|
||||
public void testConvertManyEndOfLineComments() { doTest();}
|
||||
public void testConvertMixed() { doTest();}
|
||||
|
||||
|
||||
@@ -30,6 +30,9 @@ public interface JoinRawLinesHandlerDelegate extends JoinLinesHandlerDelegate {
|
||||
* In contrast to {@link JoinLinesHandlerDelegate#tryJoinLines(Document, PsiFile, int, int) tryJoinLines()}, this method
|
||||
* is called on an unmodified document.
|
||||
*
|
||||
* This joiner is allowed to keep number of lines the same,
|
||||
* but it should not increase number of lines in the document.
|
||||
*
|
||||
* @param document where the lines are
|
||||
* @param file where the lines are
|
||||
* @param start offset right after the last non-space char of first line;
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.codeInsight.editorActions;
|
||||
|
||||
import com.intellij.application.options.CodeStyle;
|
||||
import com.intellij.lang.CodeDocumentationAwareCommenter;
|
||||
import com.intellij.lang.Commenter;
|
||||
import com.intellij.lang.LanguageCommenters;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.PsiComment;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiWhiteSpace;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.text.CharArrayUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class CommentJoinLinesHandler implements JoinRawLinesHandlerDelegate {
|
||||
@@ -19,27 +22,59 @@ public class CommentJoinLinesHandler implements JoinRawLinesHandlerDelegate {
|
||||
|
||||
@Override
|
||||
public int tryJoinRawLines(@NotNull Document document, @NotNull PsiFile file, int start, int end) {
|
||||
PsiElement prevElement = file.findElementAt(start);
|
||||
if (prevElement instanceof PsiWhiteSpace) {
|
||||
prevElement = file.findElementAt(start - 1);
|
||||
}
|
||||
PsiComment prevComment = PsiTreeUtil.getNonStrictParentOfType(prevElement, PsiComment.class);
|
||||
CharSequence text = document.getText();
|
||||
if (start > 0 && text.charAt(start) == '\n') start--;
|
||||
PsiComment prevComment = PsiTreeUtil.getNonStrictParentOfType(file.findElementAt(start), PsiComment.class);
|
||||
PsiComment nextComment = PsiTreeUtil.getNonStrictParentOfType(file.findElementAt(end), PsiComment.class);
|
||||
if (prevComment == null || nextComment == null) return CANNOT_JOIN;
|
||||
boolean sameComment = prevComment == nextComment;
|
||||
boolean adjacentLineComments = false;
|
||||
CharSequence text = document.getText();
|
||||
if (text.charAt(end) == '*' && end < text.length() && text.charAt(end + 1) != '/') {
|
||||
Commenter commenter = LanguageCommenters.INSTANCE.forLanguage(file.getLanguage());
|
||||
if (!(commenter instanceof CodeDocumentationAwareCommenter)) return CANNOT_JOIN;
|
||||
|
||||
CodeDocumentationAwareCommenter docCommenter = (CodeDocumentationAwareCommenter)commenter;
|
||||
String lineCommentPrefix = docCommenter.getLineCommentPrefix();
|
||||
String blockCommentSuffix = docCommenter.getBlockCommentSuffix();
|
||||
|
||||
if ("*/".equals(blockCommentSuffix) && text.charAt(end) == '*' && end < text.length() && text.charAt(end + 1) != '/') {
|
||||
/* remove leading asterisk
|
||||
* <-- like this
|
||||
*/
|
||||
end = StringUtil.skipWhitespaceForward(text, end + 1);
|
||||
}
|
||||
else if (!sameComment &&
|
||||
!(start >= 2 && text.charAt(start - 2) == '*' && text.charAt(start - 1) == '/') &&
|
||||
text.charAt(end) == '/' && end + 1 < text.length() && text.charAt(end + 1) == '/') {
|
||||
else if (!sameComment && lineCommentPrefix != null &&
|
||||
!(blockCommentSuffix != null && CharArrayUtil.regionMatches(text, start - 2, blockCommentSuffix)) &&
|
||||
CharArrayUtil.regionMatches(text, end, lineCommentPrefix)) {
|
||||
// merge two line comments
|
||||
// like this
|
||||
adjacentLineComments = true;
|
||||
end = StringUtil.skipWhitespaceForward(text, end + 2);
|
||||
end = StringUtil.skipWhitespaceForward(text, end + lineCommentPrefix.length());
|
||||
int lineNumber = document.getLineNumber(start);
|
||||
int lineStart = document.getLineStartOffset(lineNumber);
|
||||
int nextEnd = document.getLineEndOffset(lineNumber + 1);
|
||||
int margin = CodeStyle.getSettings(file).getRightMargin(file.getLanguage());
|
||||
int lineLength = start + 1 - lineStart;
|
||||
if (lineLength <= margin && lineLength + (nextEnd - end) + 1 > margin) {
|
||||
// Respect right margin
|
||||
int allowedEnd = end + margin - lineLength - 1;
|
||||
assert allowedEnd < nextEnd;
|
||||
while (allowedEnd > end && !Character.isWhitespace(text.charAt(allowedEnd))) {
|
||||
allowedEnd--;
|
||||
}
|
||||
if (allowedEnd <= end) {
|
||||
// do nothing, just move the caret
|
||||
return end;
|
||||
}
|
||||
int endOfMovedPart = StringUtil.skipWhitespaceBackward(text, allowedEnd);
|
||||
CharSequence toMove = text.subSequence(end, endOfMovedPart);
|
||||
int lineBreakPos = CharArrayUtil.indexOf(text, "\n", start);
|
||||
document.deleteString(end, allowedEnd + 1);
|
||||
document.replaceString(start + 1, lineBreakPos, " " + toMove);
|
||||
return start + 2 + toMove.length() + (end - lineBreakPos);
|
||||
}
|
||||
}
|
||||
|
||||
document.replaceString(start, end, adjacentLineComments || sameComment ? " " : "");
|
||||
return start;
|
||||
document.replaceString(start + 1, end, adjacentLineComments || sameComment ? " " : "");
|
||||
return start + 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import com.intellij.psi.codeStyle.CodeStyleManager;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.DocumentUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.text.CharArrayUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -86,7 +87,8 @@ public class JoinLinesHandler extends EditorActionHandler {
|
||||
if (doc.getLineStartOffset(endLine) == caret.getSelectionEnd()) endLine--;
|
||||
}
|
||||
|
||||
// joining lines, several times if selection is multiline
|
||||
if (endLine >= doc.getLineCount()) return;
|
||||
|
||||
int lineCount = endLine - startLine;
|
||||
int line = startLine;
|
||||
|
||||
@@ -101,7 +103,7 @@ public class JoinLinesHandler extends EditorActionHandler {
|
||||
private static class JoinLineProcessor {
|
||||
private final @NotNull DocumentEx myDoc;
|
||||
private final @NotNull PsiFile myFile;
|
||||
private final int myLine;
|
||||
private int myLine;
|
||||
private final @NotNull PsiDocumentManager myManager;
|
||||
private final @NotNull CodeStyleManager myStyleManager;
|
||||
private final @NotNull ProgressIndicator myIndicator;
|
||||
@@ -154,8 +156,10 @@ public class JoinLinesHandler extends EditorActionHandler {
|
||||
if (lastNonSpaceOffset > myDoc.getLineStartOffset(line)) {
|
||||
PsiComment comment = getCommentElement(myFile.findElementAt(lastNonSpaceOffset - 1));
|
||||
if (comment != null) {
|
||||
int nextStart = StringUtil.skipWhitespaceForward(text, myDoc.getLineStartOffset(line + 1));
|
||||
if (getCommentElement(myFile.findElementAt(nextStart)) == null) {
|
||||
int nextStart = CharArrayUtil.shiftForward(text, myDoc.getLineStartOffset(line + 1), " \t\n");
|
||||
if (nextStart < text.length() &&
|
||||
myDoc.getLineNumber(nextStart) <= myLine + lineCount &&
|
||||
getCommentElement(myFile.findElementAt(nextStart)) == null) {
|
||||
endComments.add(comment);
|
||||
}
|
||||
}
|
||||
@@ -192,9 +196,11 @@ public class JoinLinesHandler extends EditorActionHandler {
|
||||
int start = limits.getStartOffset();
|
||||
int end = limits.getEndOffset();
|
||||
int rc = CANNOT_JOIN;
|
||||
JoinRawLinesHandlerDelegate rawJoiner = null;
|
||||
for (JoinLinesHandlerDelegate delegate : list) {
|
||||
if (delegate instanceof JoinRawLinesHandlerDelegate) {
|
||||
rc = ((JoinRawLinesHandlerDelegate)delegate).tryJoinRawLines(myDoc, myFile, start, end);
|
||||
rawJoiner = (JoinRawLinesHandlerDelegate)delegate;
|
||||
rc = rawJoiner.tryJoinRawLines(myDoc, myFile, start, end);
|
||||
if (rc != CANNOT_JOIN) {
|
||||
myCaretRestoreOffset = checkOffset(rc, delegate, myDoc);
|
||||
break;
|
||||
@@ -209,8 +215,18 @@ public class JoinLinesHandler extends EditorActionHandler {
|
||||
myManager.doPostponedOperationsAndUnblockDocument(myDoc);
|
||||
myManager.commitDocument(myDoc);
|
||||
int afterLines = myDoc.getLineCount();
|
||||
// Single Join two lines procedure could join more than two (e.g. if it removes braces)
|
||||
count += Math.max(beforeLines - afterLines, 1);
|
||||
if (afterLines > beforeLines) {
|
||||
LOG.error("Raw joiner increased number of lines: " + rawJoiner + " (" + rawJoiner.getClass() + ")");
|
||||
}
|
||||
if (afterLines >= beforeLines && myLine == startLine) {
|
||||
// if number of lines is the same, continue processing from the next line
|
||||
myLine++;
|
||||
startLine++;
|
||||
count++;
|
||||
} else {
|
||||
// Single Join two lines procedure could join more than two (e.g. if it removes braces)
|
||||
count += Math.max(beforeLines - afterLines, 1);
|
||||
}
|
||||
beforeLines = afterLines;
|
||||
text = myDoc.getCharsSequence();
|
||||
}
|
||||
@@ -297,7 +313,7 @@ public class JoinLinesHandler extends EditorActionHandler {
|
||||
RangeMarker marker = markers.get(i);
|
||||
if (!marker.isValid()) continue;
|
||||
int end = StringUtil.skipWhitespaceForward(text, marker.getStartOffset());
|
||||
int spacesToCreate = myStyleManager.getSpacing(myFile, end);
|
||||
int spacesToCreate = text.charAt(end) == '\n' ? 0 : myStyleManager.getSpacing(myFile, end);
|
||||
spacesToAdd[i] = spacesToCreate < 0 ? 1 : spacesToCreate;
|
||||
}
|
||||
DocumentUtil.executeInBulk(myDoc, size > 100, () -> {
|
||||
|
||||
@@ -20,7 +20,6 @@ import com.intellij.codeInsight.editorActions.JoinRawLinesHandlerDelegate;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.PsiComment;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.tree.TokenSet;
|
||||
@@ -53,7 +52,6 @@ public class PyJoinLinesHandler implements JoinRawLinesHandlerDelegate {
|
||||
new StringLiteralJoiner(),
|
||||
new StmtJoiner(), // strings before stmts to let doc strings join
|
||||
new BinaryExprJoiner(),
|
||||
new CommentJoiner(),
|
||||
new StripBackslashJoiner()
|
||||
};
|
||||
|
||||
@@ -327,34 +325,6 @@ public class PyJoinLinesHandler implements JoinRawLinesHandlerDelegate {
|
||||
return req.document.getLineStartOffset(lineNumber);
|
||||
}
|
||||
|
||||
private static class CommentJoiner implements Joiner {
|
||||
@Override
|
||||
public Result join(@NotNull Request req) {
|
||||
if (req.leftElem instanceof PsiComment && req.rightElem instanceof PsiComment) {
|
||||
final CharSequence text = req.document.getCharsSequence();
|
||||
final TextRange rightRange = req.rightElem.getTextRange();
|
||||
final int initialPos = rightRange.getStartOffset() + 1;
|
||||
int pos = initialPos; // cut '#'
|
||||
final int last = rightRange.getEndOffset();
|
||||
while (pos < last && " \t".indexOf(text.charAt(pos)) >= 0) pos += 1;
|
||||
int right = pos - initialPos + 1; // account for the '#'
|
||||
String substring = req.rightElem.getText().substring(right);
|
||||
|
||||
String replacement = " " + findReplacement(substring, getStringToJoinMaxLength(req, 0));
|
||||
right += replacement.length() - 1; // account for the '#'
|
||||
if (!replacement.trim().isEmpty()) {
|
||||
replacement += "\n";
|
||||
req.document.insertString(req.secondLineStartOffset + right, "# ");
|
||||
}
|
||||
|
||||
return new Result(replacement, 0, 0, right);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private static class StripBackslashJoiner implements Joiner {
|
||||
static final TokenSet SINGLE_QUOTED_STRINGS = TokenSet.create(PyTokenTypes.SINGLE_QUOTED_STRING, PyTokenTypes.SINGLE_QUOTED_UNICODE);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# this comment is very very very very very very long. And this is the second
|
||||
# line of this very long comment
|
||||
# this comment is very very very very very very long. And this is the second
|
||||
# <caret>line of this very long comment
|
||||
def test():
|
||||
pass
|
||||
Reference in New Issue
Block a user