mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-165197 NullPointerException in IndentsPass may break plugins
This commit is contained in:
@@ -81,15 +81,7 @@ public interface RangeMarker extends UserDataHolder, Segment {
|
||||
*/
|
||||
void setGreedyToRight(boolean greedy);
|
||||
|
||||
Comparator<RangeMarker> BY_START_OFFSET = new Comparator<RangeMarker>() {
|
||||
@Override
|
||||
public int compare(RangeMarker r1, RangeMarker r2) {
|
||||
int result = r1.getStartOffset() - r2.getStartOffset();
|
||||
if (result == 0) result = r1.getEndOffset() - r2.getEndOffset();
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
Comparator<? super RangeMarker> BY_START_OFFSET = BY_START_OFFSET_THEN_END_OFFSET;
|
||||
|
||||
boolean isGreedyToRight();
|
||||
boolean isGreedyToLeft();
|
||||
|
||||
@@ -40,6 +40,7 @@ import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.project.DumbAware;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.Segment;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
@@ -56,17 +57,13 @@ import java.util.List;
|
||||
|
||||
public class IndentsPass extends TextEditorHighlightingPass implements DumbAware {
|
||||
private static final Key<List<RangeHighlighter>> INDENT_HIGHLIGHTERS_IN_EDITOR_KEY = Key.create("INDENT_HIGHLIGHTERS_IN_EDITOR_KEY");
|
||||
private static final Key<Long> LAST_TIME_INDENTS_BUILT = Key.create("LAST_TIME_INDENTS_BUILT");
|
||||
private static final Key<Long> LAST_TIME_INDENTS_BUILT = Key.create("LAST_TIME_INDENTS_BUILT");
|
||||
|
||||
private final EditorEx myEditor;
|
||||
private final PsiFile myFile;
|
||||
public static final Comparator<TextRange> RANGE_COMPARATOR = (o1, o2) -> {
|
||||
if (o1.getStartOffset() == o2.getStartOffset()) {
|
||||
return o1.getEndOffset() - o2.getEndOffset();
|
||||
}
|
||||
|
||||
return o1.getStartOffset() - o2.getStartOffset();
|
||||
};
|
||||
private volatile List<TextRange> myRanges = Collections.emptyList();
|
||||
private volatile List<IndentGuideDescriptor> myDescriptors = Collections.emptyList();
|
||||
|
||||
private static final CustomHighlighterRenderer RENDERER = (editor, highlighter, g) -> {
|
||||
int startOffset = highlighter.getStartOffset();
|
||||
@@ -92,7 +89,7 @@ public class IndentsPass extends TextEditorHighlightingPass implements DumbAware
|
||||
final VisualPosition startPosition = editor.offsetToVisualPosition(off);
|
||||
int indentColumn = startPosition.column;
|
||||
|
||||
// It's considered that indent guide can cross not only white space but comments, javadocs etc. Hence, there is a possible
|
||||
// It's considered that indent guide can cross not only white space but comments, javadoc etc. Hence, there is a possible
|
||||
// case that the first indent guide line is, say, single-line comment where comment symbols ('//') are located at the first
|
||||
// visual column. We need to calculate correct indent guide column then.
|
||||
int lineShift = 1;
|
||||
@@ -139,7 +136,7 @@ public class IndentsPass extends TextEditorHighlightingPass implements DumbAware
|
||||
}
|
||||
|
||||
final EditorColorsScheme scheme = editor.getColorsScheme();
|
||||
g.setColor(selected ? scheme.getColor(EditorColors.SELECTED_INDENT_GUIDE_COLOR) : scheme.getColor(EditorColors.INDENT_GUIDE_COLOR));
|
||||
g.setColor(scheme.getColor(selected ? EditorColors.SELECTED_INDENT_GUIDE_COLOR : EditorColors.INDENT_GUIDE_COLOR));
|
||||
|
||||
// There is a possible case that indent line intersects soft wrap-introduced text. Example:
|
||||
// this is a long line <soft-wrap>
|
||||
@@ -191,10 +188,8 @@ public class IndentsPass extends TextEditorHighlightingPass implements DumbAware
|
||||
}
|
||||
}
|
||||
};
|
||||
private volatile List<TextRange> myRanges;
|
||||
private volatile List<IndentGuideDescriptor> myDescriptors;
|
||||
|
||||
public IndentsPass(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) {
|
||||
IndentsPass(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) {
|
||||
super(project, editor.getDocument(), false);
|
||||
myEditor = (EditorEx)editor;
|
||||
myFile = file;
|
||||
@@ -216,7 +211,7 @@ public class IndentsPass extends TextEditorHighlightingPass implements DumbAware
|
||||
ranges.add(new TextRange(myDocument.getLineStartOffset(descriptor.startLine), endOffset));
|
||||
}
|
||||
|
||||
Collections.sort(ranges, RANGE_COMPARATOR);
|
||||
Collections.sort(ranges, Segment.BY_START_OFFSET_THEN_END_OFFSET);
|
||||
myRanges = ranges;
|
||||
}
|
||||
|
||||
@@ -350,11 +345,9 @@ public class IndentsPass extends TextEditorHighlightingPass implements DumbAware
|
||||
}
|
||||
|
||||
private class IndentsCalculator {
|
||||
|
||||
@NotNull public final Map<Language, TokenSet> myComments = ContainerUtilRt.newHashMap();
|
||||
|
||||
@NotNull final int[] lineIndents; // negative value means the line is empty (or contains a comment) and indent
|
||||
// (denoted by absolute value) was deduced from enclosing non-empty lines
|
||||
@NotNull final Map<Language, TokenSet> myComments = ContainerUtilRt.newHashMap();
|
||||
@NotNull final int[] lineIndents; // negative value means the line is empty (or contains a comment) and indent
|
||||
// (denoted by absolute value) was deduced from enclosing non-empty lines
|
||||
@NotNull final CharSequence myChars;
|
||||
|
||||
IndentsCalculator() {
|
||||
@@ -391,12 +384,9 @@ public class IndentsPass extends TextEditorHighlightingPass implements DumbAware
|
||||
}
|
||||
offset++;
|
||||
}
|
||||
if (offset == lineEnd || isComment(offset)) { // treating commented lines in the same way as empty lines
|
||||
lineIndents[line] = -1; // Blank line marker
|
||||
}
|
||||
else {
|
||||
lineIndents[line] = column;
|
||||
}
|
||||
// treating commented lines in the same way as empty lines
|
||||
// Blank line marker
|
||||
lineIndents[line] = offset == lineEnd || isComment(offset) ? -1 : column;
|
||||
}
|
||||
|
||||
int topIndent = 0;
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package com.intellij.openapi.util;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* User: cdr
|
||||
*/
|
||||
@@ -22,4 +24,13 @@ public interface Segment {
|
||||
Segment[] EMPTY_ARRAY = new Segment[0];
|
||||
int getStartOffset();
|
||||
int getEndOffset();
|
||||
|
||||
Comparator<Segment> BY_START_OFFSET_THEN_END_OFFSET = new Comparator<Segment>() {
|
||||
@Override
|
||||
public int compare(Segment r1, Segment r2) {
|
||||
int result = r1.getStartOffset() - r2.getStartOffset();
|
||||
if (result == 0) result = r1.getEndOffset() - r2.getEndOffset();
|
||||
return result;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -17,26 +17,24 @@ package org.jetbrains.idea.devkit.actions;
|
||||
|
||||
import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerEx;
|
||||
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
|
||||
import com.intellij.codeInsight.daemon.impl.IndentsPass;
|
||||
import com.intellij.lang.annotation.HighlightSeverity;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.actionSystem.CommonDataKeys;
|
||||
import com.intellij.openapi.application.WriteAction;
|
||||
import com.intellij.openapi.command.CommandProcessor;
|
||||
import com.intellij.openapi.command.CommandProcessorEx;
|
||||
import com.intellij.openapi.command.UndoConfirmationPolicy;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.SelectionModel;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Segment;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@@ -57,7 +55,7 @@ public class ToggleHighlightingMarkupAction extends AnAction {
|
||||
PsiFile file = CommonDataKeys.PSI_FILE.getData(e.getDataContext());
|
||||
if (editor == null || file == null) return;
|
||||
final Project project = file.getProject();
|
||||
CommandProcessorEx commandProcessor = (CommandProcessorEx)CommandProcessorEx.getInstance();
|
||||
CommandProcessorEx commandProcessor = (CommandProcessorEx)CommandProcessor.getInstance();
|
||||
Object commandToken = commandProcessor.startCommand(project, e.getPresentation().getText(), e.getPresentation().getText(), UndoConfirmationPolicy.DEFAULT);
|
||||
try {
|
||||
WriteAction.run(() -> {
|
||||
@@ -101,7 +99,7 @@ public class ToggleHighlightingMarkupAction extends AnAction {
|
||||
pos = Math.max(matcher.end(1), matcher.end(2));
|
||||
}
|
||||
while (matcher.find(pos));
|
||||
Collections.sort(ranges, IndentsPass.RANGE_COMPARATOR);
|
||||
Collections.sort(ranges, Segment.BY_START_OFFSET_THEN_END_OFFSET);
|
||||
}
|
||||
if (!ranges.isEmpty()) {
|
||||
int pos = 0;
|
||||
@@ -112,7 +110,7 @@ public class ToggleHighlightingMarkupAction extends AnAction {
|
||||
sb.append(sequence, pos, sequence.length());
|
||||
}
|
||||
else {
|
||||
final int[] offset = new int[] {0};
|
||||
final int[] offset = {0};
|
||||
final ArrayList<HighlightInfo> infos = new ArrayList<>();
|
||||
DaemonCodeAnalyzerEx.processHighlights(
|
||||
document, project, HighlightSeverity.WARNING, 0, sequence.length(),
|
||||
@@ -120,11 +118,11 @@ public class ToggleHighlightingMarkupAction extends AnAction {
|
||||
if (info.getSeverity() != HighlightSeverity.WARNING && info.getSeverity() != HighlightSeverity.ERROR) return true;
|
||||
if (info.getStartOffset() >= endOffset) return false;
|
||||
if (info.getEndOffset() > startOffset) {
|
||||
offset[0] = appendInfo(info, sb, sequence, offset[0], infos, false);
|
||||
offset[0] = appendInfo(info, sb, sequence, offset[0], infos);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
offset[0] = appendInfo(null, sb, sequence, offset[0], infos, false);
|
||||
offset[0] = appendInfo(null, sb, sequence, offset[0], infos);
|
||||
sb.append(sequence.subSequence(offset[0], sequence.length()));
|
||||
}
|
||||
document.setText(sb);
|
||||
@@ -134,27 +132,27 @@ public class ToggleHighlightingMarkupAction extends AnAction {
|
||||
StringBuilder sb,
|
||||
CharSequence sequence,
|
||||
int offset,
|
||||
ArrayList<HighlightInfo> infos, final boolean compact) {
|
||||
ArrayList<HighlightInfo> infos) {
|
||||
if (info == null || !infos.isEmpty() && getMaxEnd(infos) < info.getStartOffset()) {
|
||||
if (infos.size() == 1) {
|
||||
HighlightInfo cur = infos.remove(0);
|
||||
sb.append(sequence.subSequence(offset, cur.getStartOffset()));
|
||||
appendTag(sb, cur, true, compact);
|
||||
appendTag(sb, cur, true);
|
||||
sb.append(sequence.subSequence(cur.getStartOffset(), cur.getEndOffset()));
|
||||
appendTag(sb, cur, false, compact);
|
||||
appendTag(sb, cur, false);
|
||||
offset = cur.getEndOffset();
|
||||
}
|
||||
else {
|
||||
// process overlapped
|
||||
LinkedList<HighlightInfo> stack = new LinkedList<>();
|
||||
for (HighlightInfo cur : infos) {
|
||||
offset = processStack(stack, sb, sequence, offset, cur.getStartOffset(), compact);
|
||||
offset = processStack(stack, sb, sequence, offset, cur.getStartOffset());
|
||||
sb.append(sequence.subSequence(offset, cur.getStartOffset()));
|
||||
offset = cur.getStartOffset();
|
||||
appendTag(sb, cur, true, compact);
|
||||
appendTag(sb, cur, true);
|
||||
stack.addLast(cur);
|
||||
}
|
||||
offset = processStack(stack, sb, sequence, offset, sequence.length(), compact);
|
||||
offset = processStack(stack, sb, sequence, offset, sequence.length());
|
||||
infos.clear();
|
||||
}
|
||||
}
|
||||
@@ -185,24 +183,20 @@ public class ToggleHighlightingMarkupAction extends AnAction {
|
||||
StringBuilder sb,
|
||||
CharSequence sequence,
|
||||
int offset,
|
||||
final int endOffset,
|
||||
final boolean compact) {
|
||||
final int endOffset) {
|
||||
if (stack.isEmpty()) return offset;
|
||||
for (HighlightInfo cur = stack.peekLast(); cur != null && cur.getEndOffset() <= endOffset; cur = stack.peekLast()) {
|
||||
stack.removeLast();
|
||||
if (offset <= cur.getEndOffset()) {
|
||||
sb.append(sequence.subSequence(offset, cur.getEndOffset()));
|
||||
}
|
||||
else {
|
||||
//System.out.println("Incorrect overlapping infos: " + offset + " > " + cur.getEndOffset());
|
||||
}
|
||||
offset = cur.getEndOffset();
|
||||
appendTag(sb, cur, false, compact);
|
||||
appendTag(sb, cur, false);
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
private static void appendTag(StringBuilder sb, HighlightInfo cur, boolean opening, final boolean compact) {
|
||||
private static void appendTag(StringBuilder sb, HighlightInfo cur, boolean opening) {
|
||||
sb.append("<");
|
||||
if (!opening) sb.append("/");
|
||||
if (cur.isAfterEndOfLine()) {
|
||||
@@ -211,7 +205,7 @@ public class ToggleHighlightingMarkupAction extends AnAction {
|
||||
else {
|
||||
sb.append(cur.getSeverity() == HighlightSeverity.WARNING ? "warning" : "error");
|
||||
}
|
||||
if (opening && !compact) {
|
||||
if (opening) {
|
||||
sb.append(" descr=\"").append(cur.getDescription()).append("\"");
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user