diff --git a/xml/impl/src/com/intellij/codeInsight/daemon/impl/tagTreeHighlighting/XmlTagTreeHighlightingPass.java b/xml/impl/src/com/intellij/codeInsight/daemon/impl/tagTreeHighlighting/XmlTagTreeHighlightingPass.java index 9024b00a281b..098776cfdd3f 100644 --- a/xml/impl/src/com/intellij/codeInsight/daemon/impl/tagTreeHighlighting/XmlTagTreeHighlightingPass.java +++ b/xml/impl/src/com/intellij/codeInsight/daemon/impl/tagTreeHighlighting/XmlTagTreeHighlightingPass.java @@ -75,7 +75,8 @@ public class XmlTagTreeHighlightingPass extends TextEditorHighlightingPass { } final int offset = myEditor.getCaretModel().getOffset(); - PsiElement[] elements = PsiFileBreadcrumbsCollector.getLinePsiElements(offset, + PsiElement[] elements = PsiFileBreadcrumbsCollector.getLinePsiElements(myEditor, + offset, myFile.getVirtualFile(), myProject, myInfoProvider); diff --git a/xml/impl/src/com/intellij/xml/breadcrumbs/PsiFileBreadcrumbsCollector.java b/xml/impl/src/com/intellij/xml/breadcrumbs/PsiFileBreadcrumbsCollector.java index 5add84d309dd..8000828dfe56 100644 --- a/xml/impl/src/com/intellij/xml/breadcrumbs/PsiFileBreadcrumbsCollector.java +++ b/xml/impl/src/com/intellij/xml/breadcrumbs/PsiFileBreadcrumbsCollector.java @@ -15,11 +15,13 @@ import com.intellij.openapi.util.TextRange; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; import com.intellij.psi.availability.PsiAvailabilityService; +import com.intellij.psi.util.PsiTreeUtil; import com.intellij.ui.breadcrumbs.BreadcrumbsProvider; import com.intellij.ui.breadcrumbs.BreadcrumbsUtil; import com.intellij.ui.components.breadcrumbs.Crumb; import com.intellij.util.Consumer; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.text.CharArrayUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -107,7 +109,7 @@ public class PsiFileBreadcrumbsCollector extends FileBreadcrumbsCollector { BreadcrumbsProvider defaultInfoProvider = findProvider(editor, file); Collection> pairs = - getLineElements(offset, file, myProject, defaultInfoProvider, true); + getLineElements(editor, offset, file, myProject, defaultInfoProvider, true); if (pairs == null) return null; @@ -137,12 +139,13 @@ public class PsiFileBreadcrumbsCollector extends FileBreadcrumbsCollector { } @Nullable - private static Collection> getLineElements(int offset, + private static Collection> getLineElements(Editor editor, + int offset, VirtualFile file, Project project, BreadcrumbsProvider defaultInfoProvider, boolean checkSettings) { - PsiElement element = findFirstBreadcrumbedElement(offset, file, project, defaultInfoProvider, checkSettings); + PsiElement element = findStartElement(editor, offset, file, project, defaultInfoProvider, checkSettings); if (element == null) return null; LinkedList> result = new LinkedList<>(); @@ -159,6 +162,41 @@ public class PsiFileBreadcrumbsCollector extends FileBreadcrumbsCollector { return result; } + /** + * Finds first breadcrumb-rendering element, possibly shifting offset backwards, skipping whitespaces and grabbing previous element + * This logic solves inconsistency with brace matcher. For example, + *

+   *   class Foo {
+   *     public void bar() {
+   *
+   *     } <caret>
+   *   }
+   * 
+ * will highlight bar's braces, looking backwards. So it should include it to breadcrumbs, too. + */ + @Nullable + private static PsiElement findStartElement(Editor editor, + int offset, + VirtualFile file, + Project project, + BreadcrumbsProvider defaultInfoProvider, + boolean checkSettings) { + PsiElement middleElement = findFirstBreadcrumbedElement(offset, file, project, defaultInfoProvider, checkSettings); + + // Let's simulate brace matcher logic of searching brace backwards (see `BraceHighlightingHandler.updateBraces`) + CharSequence chars = editor.getDocument().getCharsSequence(); + int leftOffset = CharArrayUtil.shiftBackward(chars, offset - 1, "\t "); + leftOffset = leftOffset >= 0 ? leftOffset : offset - 1; + + PsiElement leftElement = findFirstBreadcrumbedElement(leftOffset, file, project, defaultInfoProvider, checkSettings); + if (leftElement != null && (middleElement == null || PsiTreeUtil.isAncestor(middleElement, leftElement, true))) { + return leftElement; + } + else { + return middleElement; + } + } + @Nullable private static PsiElement findFirstBreadcrumbedElement(final int offset, final VirtualFile file, @@ -225,8 +263,12 @@ public class PsiFileBreadcrumbsCollector extends FileBreadcrumbsCollector { } @Nullable - public static PsiElement[] getLinePsiElements(int offset, VirtualFile file, Project project, BreadcrumbsProvider infoProvider) { - Collection> pairs = getLineElements(offset, file, project, infoProvider, false); + public static PsiElement[] getLinePsiElements(Editor editor, + int offset, + VirtualFile file, + Project project, + BreadcrumbsProvider infoProvider) { + Collection> pairs = getLineElements(editor, offset, file, project, infoProvider, false); return pairs == null ? null : toPsiElementArray(pairs); } }