mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
CR-IC-41 Vertical line block match disapear view line in java code (IDEA-99572)
1. Limit 'shiftForward()' by line end offset; 2. Minor refactoring; 3. Don't show indent guide for commented lines which would remove indent on un-commenting;
This commit is contained in:
+8
-4
@@ -43,8 +43,8 @@ class Test {
|
||||
| //test();
|
||||
| if (true) {
|
||||
| | int i = 1;
|
||||
| | |
|
||||
//| | | comment;
|
||||
| |
|
||||
//| | int k;
|
||||
| | int j = 1;
|
||||
| }
|
||||
}
|
||||
@@ -141,8 +141,12 @@ class Test {
|
||||
myFixture.configureByText("${getTestName(false)}.java", testData.documentText)
|
||||
CodeInsightTestFixtureImpl.instantiateAndRun(myFixture.file, myFixture.editor, ArrayUtilRt.EMPTY_INT_ARRAY, false)
|
||||
IndentsModelImpl model = myFixture.editor.indentsModel as IndentsModelImpl
|
||||
assertEquals("expected to find ${testData.guides.size()} indent guide but got ${model.indents.size()}",
|
||||
testData.guides.size(), model.indents.size())
|
||||
assertEquals(
|
||||
"expected to find ${testData.guides.size()} indent guides (" +
|
||||
"${testData.guides.collect { startLine, endLine, level -> "$level ($startLine-$endLine)"}}) " +
|
||||
"but got ${model.indents.size()} (${model.indents})",
|
||||
testData.guides.size(), model.indents.size()
|
||||
)
|
||||
|
||||
testData.guides.each {
|
||||
def descriptor = model.getDescriptor(it[0], it[1])
|
||||
|
||||
@@ -26,7 +26,6 @@ import com.intellij.openapi.editor.*;
|
||||
import com.intellij.openapi.editor.colors.EditorColors;
|
||||
import com.intellij.openapi.editor.colors.EditorColorsScheme;
|
||||
import com.intellij.openapi.editor.ex.EditorEx;
|
||||
import com.intellij.openapi.editor.highlighter.EditorHighlighter;
|
||||
import com.intellij.openapi.editor.highlighter.HighlighterIterator;
|
||||
import com.intellij.openapi.editor.markup.CustomHighlighterRenderer;
|
||||
import com.intellij.openapi.editor.markup.HighlighterTargetArea;
|
||||
@@ -44,8 +43,10 @@ import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.tree.TokenSet;
|
||||
import com.intellij.util.DocumentUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.ContainerUtilRt;
|
||||
import com.intellij.util.containers.IntStack;
|
||||
import com.intellij.util.text.CharArrayUtil;
|
||||
import gnu.trove.TIntIntHashMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.awt.*;
|
||||
@@ -93,8 +94,9 @@ public class IndentsPass extends TextEditorHighlightingPass implements DumbAware
|
||||
|
||||
final CharSequence chars = doc.getCharsSequence();
|
||||
do {
|
||||
int pos = doc.getLineStartOffset(startLine);
|
||||
off = CharArrayUtil.shiftForward(chars, pos, " \t");
|
||||
int start = doc.getLineStartOffset(startLine);
|
||||
int end = doc.getLineEndOffset(startLine);
|
||||
off = CharArrayUtil.shiftForward(chars, start, end, " \t");
|
||||
startLine--;
|
||||
}
|
||||
while (startLine > 1 && off < doc.getTextLength() && chars.charAt(off) == '\n');
|
||||
@@ -296,7 +298,10 @@ public class IndentsPass extends TextEditorHighlightingPass implements DumbAware
|
||||
private List<IndentGuideDescriptor> buildDescriptors() {
|
||||
if (!myEditor.getSettings().isIndentGuidesShown()) return Collections.emptyList();
|
||||
|
||||
int[] lineIndents = calcIndents();
|
||||
IndentsCalculator calculator = new IndentsCalculator();
|
||||
calculator.calculate();
|
||||
int[] lineIndents = calculator.lineIndents;
|
||||
TIntIntHashMap effectiveCommentColumns = calculator.indentAfterUncomment;
|
||||
|
||||
List<IndentGuideDescriptor> descriptors = new ArrayList<IndentGuideDescriptor>();
|
||||
|
||||
@@ -316,7 +321,18 @@ public class IndentsPass extends TextEditorHighlightingPass implements DumbAware
|
||||
final int level = indents.pop();
|
||||
int startLine = lines.pop();
|
||||
if (level > 0) {
|
||||
descriptors.add(createDescriptor(level, startLine, line, chars));
|
||||
boolean addDescriptor = effectiveCommentColumns.contains(startLine); // Indent started at comment
|
||||
if (!addDescriptor) {
|
||||
for (int i = startLine; i < line; i++) {
|
||||
if (level != lineIndents[i] && level != effectiveCommentColumns.get(i)) {
|
||||
addDescriptor = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (addDescriptor) {
|
||||
descriptors.add(createDescriptor(level, startLine, line, chars));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -332,8 +348,8 @@ public class IndentsPass extends TextEditorHighlightingPass implements DumbAware
|
||||
while (!indents.empty()) {
|
||||
ProgressManager.checkCanceled();
|
||||
final int level = indents.pop();
|
||||
int startLine = lines.pop();
|
||||
if (level > 0) {
|
||||
int startLine = lines.pop();
|
||||
descriptors.add(createDescriptor(level, startLine, myDocument.getLineCount(), chars));
|
||||
}
|
||||
}
|
||||
@@ -351,109 +367,8 @@ public class IndentsPass extends TextEditorHighlightingPass implements DumbAware
|
||||
return true;
|
||||
}
|
||||
int startOffset = document.getLineStartOffset(line);
|
||||
return CharArrayUtil.shiftForward(chars, startOffset, " \t") >= myDocument.getLineEndOffset(line);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates line indents for the {@link #myDocument target document}.
|
||||
*
|
||||
* @return line indents array which uses lines as indices
|
||||
*/
|
||||
@NotNull
|
||||
private int[] calcIndents() {
|
||||
Context context = new Context(myDocument, myFile.getLanguage(), myEditor.getHighlighter());
|
||||
int[] lineIndents = context.lineIndents;
|
||||
final FileType fileType = myFile.getFileType();
|
||||
int prevLineIndent = -1;
|
||||
|
||||
for (int line = 0; line < lineIndents.length; line++) {
|
||||
ProgressManager.checkCanceled();
|
||||
int lineStart = myDocument.getLineStartOffset(line);
|
||||
int lineEnd = myDocument.getLineEndOffset(line);
|
||||
final int nonWhitespaceOffset = CharArrayUtil.shiftForward(context.chars, lineStart, " \t");
|
||||
final int column;
|
||||
if (nonWhitespaceOffset == lineEnd) {
|
||||
lineIndents[line] = -1; // Blank line marker
|
||||
}
|
||||
else if (nonWhitespaceOffset > lineEnd) {
|
||||
lineIndents[line] = calcIndent(context, line, lineStart, lineEnd, 0);
|
||||
prevLineIndent = lineIndents[line];
|
||||
}
|
||||
else {
|
||||
column = myEditor.calcColumnNumber(nonWhitespaceOffset, line);
|
||||
if (prevLineIndent > 0 && prevLineIndent > column) {
|
||||
lineIndents[line] = calcIndent(context, line, nonWhitespaceOffset, lineEnd, column);
|
||||
}
|
||||
else {
|
||||
lineIndents[line] = column;
|
||||
}
|
||||
prevLineIndent = lineIndents[line];
|
||||
}
|
||||
}
|
||||
|
||||
int topIndent = 0;
|
||||
for (int line = 0; line < lineIndents.length; line++) {
|
||||
ProgressManager.checkCanceled();
|
||||
if (lineIndents[line] >= 0) {
|
||||
topIndent = lineIndents[line];
|
||||
}
|
||||
else {
|
||||
int startLine = line;
|
||||
while (line < lineIndents.length && lineIndents[line] < 0) {
|
||||
//noinspection AssignmentToForLoopParameter
|
||||
line++;
|
||||
}
|
||||
|
||||
int bottomIndent = line < lineIndents.length ? lineIndents[line] : topIndent;
|
||||
|
||||
int indent = Math.min(topIndent, bottomIndent);
|
||||
if (bottomIndent < topIndent) {
|
||||
int nonWhitespaceOffset = CharArrayUtil.shiftForward(context.chars, myDocument.getLineStartOffset(line), " \t");
|
||||
HighlighterIterator iterator = context.highlighter.createIterator(nonWhitespaceOffset);
|
||||
if (BraceMatchingUtil.isRBraceToken(iterator, context.chars, fileType)) {
|
||||
indent = topIndent;
|
||||
}
|
||||
}
|
||||
|
||||
for (int blankLine = startLine; blankLine < line; blankLine++) {
|
||||
assert lineIndents[blankLine] == -1;
|
||||
lineIndents[blankLine] = Math.min(topIndent, indent);
|
||||
}
|
||||
|
||||
//noinspection AssignmentToForLoopParameter
|
||||
line--; // will be incremented back at the end of the loop;
|
||||
}
|
||||
}
|
||||
|
||||
return lineIndents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to calculate given line's indent column assuming that there might be a comment at the given indent offset
|
||||
* (see {@link #parseComments(IElementType)}).
|
||||
*
|
||||
* @param context current calculation context
|
||||
* @param line target line
|
||||
* @param indentOffset start indent offset to use for the given line
|
||||
* @param lineEndOffset given line's end offset
|
||||
* @param fallbackColumn column to return if it's not possible to apply comment-specific indent calculation rules
|
||||
* @return given line's indent column to use
|
||||
*/
|
||||
private int calcIndent(@NotNull Context context, int line, int indentOffset, int lineEndOffset, int fallbackColumn) {
|
||||
final HighlighterIterator it = context.highlighter.createIterator(indentOffset);
|
||||
if (context.comments.contains(it.getTokenType()) && indentOffset == it.getStart()) {
|
||||
String prefix = COMMENT_PREFIXES.get(it.getTokenType());
|
||||
if (prefix == null) {
|
||||
prefix = parseComments(it.getTokenType());
|
||||
}
|
||||
if (!NO_COMMENT_INFO_MARKER.equals(prefix)) {
|
||||
final int indentInsideCommentOffset = CharArrayUtil.shiftForward(context.chars, indentOffset + prefix.length(), " \t");
|
||||
if (indentInsideCommentOffset < lineEndOffset) {
|
||||
return myEditor.calcColumnNumber(indentInsideCommentOffset, line);
|
||||
}
|
||||
}
|
||||
}
|
||||
return fallbackColumn;
|
||||
int endOffset = document.getLineEndOffset(line);
|
||||
return CharArrayUtil.shiftForward(chars, startOffset, endOffset, " \t") >= myDocument.getLineEndOffset(line);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -461,7 +376,7 @@ public class IndentsPass extends TextEditorHighlightingPass implements DumbAware
|
||||
* <p/>
|
||||
* Example:
|
||||
* <pre>
|
||||
* it (true) {
|
||||
* if (true) {
|
||||
* int i1;
|
||||
* // int i2;
|
||||
* int i3;
|
||||
@@ -478,7 +393,7 @@ public class IndentsPass extends TextEditorHighlightingPass implements DumbAware
|
||||
* {@link #NO_COMMENT_INFO_MARKER} otherwise
|
||||
*/
|
||||
@NotNull
|
||||
private static String parseComments(@NotNull IElementType commentType) {
|
||||
private static String getCommentPrefix(@NotNull IElementType commentType) {
|
||||
Commenter c = LanguageCommenters.INSTANCE.forLanguage(commentType.getLanguage());
|
||||
if (!(c instanceof CodeDocumentationAwareCommenter)) {
|
||||
COMMENT_PREFIXES.put(commentType, NO_COMMENT_INFO_MARKER);
|
||||
@@ -521,21 +436,132 @@ public class IndentsPass extends TextEditorHighlightingPass implements DumbAware
|
||||
return answer != 0 ? answer : r.getEndOffset() - h.getEndOffset();
|
||||
}
|
||||
|
||||
/**
|
||||
* 'Parameter object' for {@link #calcIndent(Context, int, int, int, int)}.
|
||||
*/
|
||||
private static class Context {
|
||||
private class IndentsCalculator {
|
||||
|
||||
@NotNull public final int[] lineIndents;
|
||||
@NotNull public final CharSequence chars;
|
||||
@NotNull public final TokenSet comments;
|
||||
@NotNull public final EditorHighlighter highlighter;
|
||||
@NotNull public final Map<Language, TokenSet> myComments = ContainerUtilRt.newHashMap();
|
||||
|
||||
Context(@NotNull Document document, @NotNull Language language, @NotNull EditorHighlighter highlighter) {
|
||||
lineIndents = new int[document.getLineCount()];
|
||||
chars = document.getCharsSequence();
|
||||
comments = LanguageParserDefinitions.INSTANCE.forLanguage(language).getCommentTokens();
|
||||
this.highlighter = highlighter;
|
||||
/**
|
||||
* We need to treat specially commented lines. Consider a situation like below:
|
||||
* <pre>
|
||||
* void test() {
|
||||
* if (true) {
|
||||
* int i;
|
||||
* // int j;
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
* We don't want to show indent guide after 'int i;' line because un-commented line below ('int j;') would have the same indent
|
||||
* level. That's why we remember 'indents after un-comment' at this collection.
|
||||
*/
|
||||
@NotNull public final TIntIntHashMap/* line -> indent column after un-comment */ indentAfterUncomment = new TIntIntHashMap();
|
||||
|
||||
@NotNull public final int[] lineIndents;
|
||||
@NotNull public final CharSequence myChars;
|
||||
|
||||
IndentsCalculator() {
|
||||
assert myDocument != null;
|
||||
lineIndents = new int[myDocument.getLineCount()];
|
||||
myChars = myDocument.getCharsSequence();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates line indents for the {@link #myDocument target document}.
|
||||
*/
|
||||
void calculate() {
|
||||
final FileType fileType = myFile.getFileType();
|
||||
int prevLineIndent = -1;
|
||||
|
||||
for (int line = 0; line < lineIndents.length; line++) {
|
||||
ProgressManager.checkCanceled();
|
||||
int lineStart = myDocument.getLineStartOffset(line);
|
||||
int lineEnd = myDocument.getLineEndOffset(line);
|
||||
final int nonWhitespaceOffset = CharArrayUtil.shiftForward(myChars, lineStart, lineEnd, " \t");
|
||||
final int column;
|
||||
if (nonWhitespaceOffset == lineEnd) {
|
||||
lineIndents[line] = -1; // Blank line marker
|
||||
}
|
||||
else {
|
||||
column = myEditor.calcColumnNumber(nonWhitespaceOffset, line);
|
||||
if (prevLineIndent > 0 && prevLineIndent > column) {
|
||||
lineIndents[line] = calcIndent(line, nonWhitespaceOffset, lineEnd, column);
|
||||
}
|
||||
else {
|
||||
lineIndents[line] = column;
|
||||
}
|
||||
prevLineIndent = lineIndents[line];
|
||||
}
|
||||
}
|
||||
|
||||
int topIndent = 0;
|
||||
for (int line = 0; line < lineIndents.length; line++) {
|
||||
ProgressManager.checkCanceled();
|
||||
if (lineIndents[line] >= 0) {
|
||||
topIndent = lineIndents[line];
|
||||
}
|
||||
else {
|
||||
int startLine = line;
|
||||
while (line < lineIndents.length && lineIndents[line] < 0) {
|
||||
//noinspection AssignmentToForLoopParameter
|
||||
line++;
|
||||
}
|
||||
|
||||
int bottomIndent = line < lineIndents.length ? lineIndents[line] : topIndent;
|
||||
|
||||
int indent = Math.min(topIndent, bottomIndent);
|
||||
if (bottomIndent < topIndent) {
|
||||
int lineStart = myDocument.getLineStartOffset(line);
|
||||
int lineEnd = myDocument.getLineEndOffset(line);
|
||||
int nonWhitespaceOffset = CharArrayUtil.shiftForward(myChars, lineStart, lineEnd, " \t");
|
||||
HighlighterIterator iterator = myEditor.getHighlighter().createIterator(nonWhitespaceOffset);
|
||||
if (BraceMatchingUtil.isRBraceToken(iterator, myChars, fileType)) {
|
||||
indent = topIndent;
|
||||
}
|
||||
}
|
||||
|
||||
for (int blankLine = startLine; blankLine < line; blankLine++) {
|
||||
assert lineIndents[blankLine] == -1;
|
||||
lineIndents[blankLine] = Math.min(topIndent, indent);
|
||||
}
|
||||
|
||||
//noinspection AssignmentToForLoopParameter
|
||||
line--; // will be incremented back at the end of the loop;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to calculate given line's indent column assuming that there might be a comment at the given indent offset
|
||||
* (see {@link #getCommentPrefix(IElementType)}).
|
||||
*
|
||||
* @param line target line
|
||||
* @param indentOffset start indent offset to use for the given line
|
||||
* @param lineEndOffset given line's end offset
|
||||
* @param fallbackColumn column to return if it's not possible to apply comment-specific indent calculation rules
|
||||
* @return given line's indent column to use
|
||||
*/
|
||||
private int calcIndent(int line, int indentOffset, int lineEndOffset, int fallbackColumn) {
|
||||
final HighlighterIterator it = myEditor.getHighlighter().createIterator(indentOffset);
|
||||
IElementType tokenType = it.getTokenType();
|
||||
Language language = tokenType.getLanguage();
|
||||
TokenSet comments = myComments.get(language);
|
||||
if (comments == null) {
|
||||
myComments.put(language, comments = LanguageParserDefinitions.INSTANCE.forLanguage(language).getCommentTokens());
|
||||
}
|
||||
if (comments.contains(tokenType) && indentOffset == it.getStart()) {
|
||||
String prefix = COMMENT_PREFIXES.get(tokenType);
|
||||
if (prefix == null) {
|
||||
prefix = getCommentPrefix(tokenType);
|
||||
}
|
||||
if (!NO_COMMENT_INFO_MARKER.equals(prefix)) {
|
||||
final int indentInsideCommentOffset = CharArrayUtil.shiftForward(myChars, indentOffset + prefix.length(), lineEndOffset, " \t");
|
||||
if (indentInsideCommentOffset < lineEndOffset) {
|
||||
int indent = myEditor.calcColumnNumber(indentInsideCommentOffset, line);
|
||||
indentAfterUncomment.put(line, indent - prefix.length());
|
||||
return indent;
|
||||
}
|
||||
}
|
||||
}
|
||||
return fallbackColumn;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,17 +194,36 @@ public class CharArrayUtil {
|
||||
}
|
||||
|
||||
public static int shiftForward(@NotNull CharSequence buffer, int offset, @NotNull String chars) {
|
||||
while (true) {
|
||||
if (offset >= buffer.length()) break;
|
||||
return shiftForward(buffer, offset, buffer.length(), chars);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to find an offset from the <code>[startOffset; endOffset)</code> interval such that a char from the given buffer is
|
||||
* not contained at the given 'chars' string.
|
||||
* <p/>
|
||||
* Example:
|
||||
* {@code buffer="abc", startOffset=0, endOffset = 3, chars="ab". Result: 2}
|
||||
*
|
||||
* @param buffer target buffer which symbols should be checked
|
||||
* @param startOffset start offset to use within the given buffer (inclusive)
|
||||
* @param endOffset end offset to use within the given buffer (exclusive)
|
||||
* @param chars pass-through symbols
|
||||
* @return offset from the <code>[startOffset; endOffset)</code> which points to a symbol at the given buffer such
|
||||
* as that that symbol is not contained at the given 'chars';
|
||||
* <code>endOffset</code> otherwise
|
||||
*/
|
||||
public static int shiftForward(@NotNull CharSequence buffer, final int startOffset, final int endOffset, @NotNull String chars) {
|
||||
for (int offset = startOffset, limit = Math.min(endOffset, buffer.length()); offset < limit; offset++) {
|
||||
char c = buffer.charAt(offset);
|
||||
int i;
|
||||
int i;
|
||||
for (i = 0; i < chars.length(); i++) {
|
||||
if (c == chars.charAt(i)) break;
|
||||
}
|
||||
if (i == chars.length()) break;
|
||||
offset++;
|
||||
if (i >= chars.length()) {
|
||||
return offset;
|
||||
}
|
||||
}
|
||||
return offset;
|
||||
return endOffset;
|
||||
}
|
||||
|
||||
public static int shiftForwardCarefully(@NotNull CharSequence buffer, int offset, @NotNull String chars) {
|
||||
|
||||
Reference in New Issue
Block a user