WEB-43327 React.js: "Expand selection" works incorrectly in Redirect tag expression - improve code readability

IDEA-CR-57481

GitOrigin-RevId: 1fe3993dfde741666dd102255e29065c3c5c37aa
This commit is contained in:
Piotr Tomiak
2020-01-21 10:01:27 +00:00
committed by intellij-monorepo-bot
parent e2c4eb74d7
commit eb94c86d0d
@@ -19,6 +19,9 @@ import one.util.streamex.StreamEx;
import org.jetbrains.annotations.NotNull;
import java.util.*;
import java.util.stream.IntStream;
import static com.intellij.util.ObjectUtils.notNull;
/**
* This selectioner tries to guess location of file segments within a particular element.
@@ -44,18 +47,14 @@ public class InjectedFileReferenceSelectioner extends AbstractWordSelectioner {
PsiElement valueElement = findValueElement(host, realRange);
List<TextRange> nonLeafChildren = StreamEx.of(valueElement.getChildren())
.filter(child -> !(child instanceof LeafPsiElement))
.map(PsiElement::getTextRange)
.sorted(Comparator.comparing(TextRange::getStartOffset).reversed())
.toList();
realRange = limitToCurrentLineAndStripWhiteSpace(editorText, cursorOffset, realRange);
Set<Integer> charEscapeLocations = isWithinLiteral(e, host) ? findCharEscapeLocations(editor, editorText, host.getTextRange())
: Collections.emptySet();
BitSet charEscapeLocations = isWithinLiteral(e, host)
? findCharEscapeLocations(editor, editorText, host.getTextRange(), realRange.getStartOffset())
: new BitSet(0);
List<TextRange> segments = buildSegments(editorText, cursorOffset, charEscapeLocations, realRange, nonLeafChildren);
BitSet compositeIndexes = createCompositeIndexesSet(valueElement, realRange.getStartOffset());
List<TextRange> segments = buildSegments(editorText, cursorOffset, realRange, charEscapeLocations, compositeIndexes);
if (!segments.isEmpty()) {
int endOffsetAlignment = segments.get(segments.size() - 1).getEndOffset();
for (ListIterator<TextRange> it = segments.listIterator(); it.hasNext(); ) {
@@ -70,19 +69,19 @@ public class InjectedFileReferenceSelectioner extends AbstractWordSelectioner {
@NotNull
private static PsiElement findValueElement(@NotNull PsiElement host, @NotNull TextRange valueRange) {
PsiElement result = host.getContainingFile().findElementAt(valueRange.getStartOffset());
while (result != null && result != host && !result.getTextRange().contains(valueRange)) {
result = result.getParent();
}
return result != null ? result : host;
return notNull(
PsiTreeUtil.findFirstParent(
host.getContainingFile().findElementAt(valueRange.getStartOffset()), false,
parent -> parent == host || parent.getTextRange().contains(valueRange)),
host);
}
@NotNull
private static List<TextRange> buildSegments(@NotNull CharSequence editorText,
final int cursorOffset,
@NotNull Set<Integer> charEscapeLocations,
@NotNull TextRange range,
List<TextRange> nonLeafChildren) {
@NotNull BitSet charEscapeLocations,
@NotNull BitSet compositeIndexes) {
if (range.getLength() == 0) {
return Collections.emptyList();
}
@@ -94,11 +93,14 @@ public class InjectedFileReferenceSelectioner extends AbstractWordSelectioner {
boolean segmentsFinished = false;
int hardSegmentCount = 0;
for (int i = hostTextOffset; i < hostTextEndOffset; i = findNextIndexInLeafElement(i, nonLeafChildren)) {
for (int i = hostTextOffset; i < hostTextEndOffset; i++) {
if (compositeIndexes.get(i - hostTextOffset)) {
continue;
}
char ch = editorText.charAt(i);
if (!segmentsFinished) {
if (ch == '/'
|| (ch == '\\' && !charEscapeLocations.contains(i))
|| (ch == '\\' && !charEscapeLocations.get(i - hostTextOffset))
//treat space as soft segment marker
|| (ch == ' ' && i <= cursorOffset)) {
if (rangeStart < i) {
@@ -120,7 +122,7 @@ public class InjectedFileReferenceSelectioner extends AbstractWordSelectioner {
// URLs - expand to content after '?' first, but count it as soft segment
else if (ch == '?') {
segments.add(new TextRange(rangeStart, i));
if (i + 1< hostTextEndOffset) {
if (i + 1 < hostTextEndOffset) {
segments.add(new TextRange(i + 1, hostTextEndOffset));
}
segments.add(new TextRange(rangeStart, hostTextEndOffset));
@@ -142,15 +144,15 @@ public class InjectedFileReferenceSelectioner extends AbstractWordSelectioner {
return segments;
}
private static int findNextIndexInLeafElement(int currentIndex, List<TextRange> nonLeafChildren) {
// skip any non leaf children - ranges sorted backwards to optimize element removal
while (!nonLeafChildren.isEmpty() && nonLeafChildren.get(nonLeafChildren.size() - 1).getStartOffset() <= currentIndex) {
TextRange nonLeafRange = nonLeafChildren.remove(nonLeafChildren.size() - 1);
if (nonLeafRange.getEndOffset() - 1 > currentIndex) {
return nonLeafRange.getEndOffset();
}
}
return currentIndex + 1;
@NotNull
private static BitSet createCompositeIndexesSet(@NotNull PsiElement valueElement, int indexesOffset) {
return StreamEx.of(valueElement.getChildren())
.filter(child -> !(child instanceof LeafPsiElement))
.map(PsiElement::getTextRange)
.flatMapToInt(range -> IntStream.range(range.getStartOffset(), range.getEndOffset()))
.map(index -> index - indexesOffset)
.atLeast(0)
.toBitSet();
}
@NotNull
@@ -178,17 +180,21 @@ public class InjectedFileReferenceSelectioner extends AbstractWordSelectioner {
|| SkipAutopopupInStrings.isInStringLiteral(e);
}
private static Set<Integer> findCharEscapeLocations(@NotNull Editor editor, @NotNull CharSequence text, @NotNull TextRange range) {
private static BitSet findCharEscapeLocations(@NotNull Editor editor,
@NotNull CharSequence text,
@NotNull TextRange range,
int indexesOffset) {
HighlighterIterator iterator =
((EditorEx)editor).getHighlighter().createIterator(range.getStartOffset());
int rangeEnd = range.getEndOffset();
Set<Integer> locations = new HashSet<>();
BitSet locations = new BitSet(range.getLength());
int pos;
while (!iterator.atEnd() && (pos = iterator.getStart()) < rangeEnd) {
if (text.charAt(pos) == '\\'
&& (pos + 1 >= rangeEnd || text.charAt(pos + 1) != '\\')) {
locations.add(pos);
&& (pos + 1 >= rangeEnd || text.charAt(pos + 1) != '\\')
&& pos >= indexesOffset) {
locations.set(pos - indexesOffset);
}
iterator.advance();
}