|
23 | 23 |
|
24 | 24 | import com.intellij.navigation.GotoRelatedItem; |
25 | 25 | import com.intellij.navigation.GotoRelatedProvider; |
| 26 | +import com.intellij.openapi.editor.Document; |
| 27 | +import com.intellij.openapi.project.Project; |
| 28 | +import com.intellij.openapi.util.TextRange; |
| 29 | +import com.intellij.psi.PsiDocumentManager; |
26 | 30 | import com.intellij.psi.PsiElement; |
27 | 31 | import com.intellij.psi.PsiReference; |
28 | 32 | import com.intellij.psi.impl.source.tree.LeafPsiElement; |
| 33 | +import com.intellij.util.containers.SortedList; |
29 | 34 | import de.halirutan.mathematica.parsing.MathematicaElementTypes; |
30 | 35 | import de.halirutan.mathematica.parsing.psi.api.Symbol; |
31 | | -import de.halirutan.mathematica.parsing.psi.util.GlobalDefinitionCollector; |
32 | | -import de.halirutan.mathematica.parsing.psi.util.GlobalDefinitionCollector.AssignmentProperty; |
33 | | -import de.halirutan.mathematica.parsing.psi.util.LocalizationConstruct.ConstructType; |
34 | 36 | import org.jetbrains.annotations.NotNull; |
35 | 37 |
|
36 | | -import java.util.ArrayList; |
37 | | -import java.util.HashSet; |
| 38 | +import java.util.Comparator; |
38 | 39 | import java.util.List; |
39 | | -import java.util.Map; |
40 | 40 |
|
41 | 41 | /** |
| 42 | + * Provides functionality to navigate through different usages of the symbol under the caret. |
| 43 | + * It will take the correct scope which means that it won't stupidly highlight symbols with the same name. |
| 44 | + * Rather, it will find out if the symbol is globally defined, in a Module, or in any other scoping construct. |
| 45 | + * Then, the list of suggestions contains only correct places which really refer to usages of the current symbol. |
42 | 46 | * @author patrick (28.12.16). |
43 | 47 | */ |
44 | 48 | public class MathematicaGotoRelatedProvider extends GotoRelatedProvider { |
45 | 49 |
|
46 | 50 | @NotNull |
47 | 51 | @Override |
48 | 52 | public List<? extends GotoRelatedItem> getItems(@NotNull PsiElement psiElement) { |
49 | | - ArrayList<GotoRelatedItem> declarations = new ArrayList<>(); |
| 53 | + // I want the entries in the suggestion window to be sorted by line number! |
| 54 | + SortedList<GotoSymbolItem> declarations = new SortedList<>(Comparator.comparingInt(GotoSymbolItem::getLineNumber)); |
50 | 55 | if (psiElement instanceof LeafPsiElement && ((LeafPsiElement) psiElement).getElementType().equals(MathematicaElementTypes.IDENTIFIER)) { |
51 | 56 | psiElement = psiElement.getParent(); |
52 | 57 | } |
53 | 58 | if (psiElement instanceof Symbol) { |
| 59 | + final Project project = psiElement.getProject(); |
| 60 | + final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project); |
| 61 | + final Document document = documentManager.getDocument(psiElement.getContainingFile()); |
| 62 | + if (document == null) { |
| 63 | + return declarations; |
| 64 | + } |
54 | 65 | PsiReference ref = psiElement.getReference(); |
55 | 66 | if (ref != null) { |
56 | 67 | PsiElement resolve = ref.resolve(); |
57 | 68 | if (resolve != null) { |
58 | | - if (resolve instanceof Symbol && ((Symbol) resolve).getLocalizationConstruct().equals(ConstructType.NULL)) { |
59 | | - GlobalDefinitionCollector collector = new GlobalDefinitionCollector(psiElement.getContainingFile()); |
60 | | - Map<String, HashSet<AssignmentProperty>> assignments = collector.getAssignments(); |
61 | | - for (AssignmentProperty property : assignments.get(((Symbol) resolve).getSymbolName())) { |
62 | | - String text = property.myLhsOfAssignment.getText(); |
63 | | - GotoRelatedItem item = new GotoSymbolItem(property.myAssignmentSymbol, text.substring(0, Math.min(text.length(),50))); |
64 | | - declarations.add(item); |
| 69 | + if (resolve instanceof Symbol) { |
| 70 | + final PsiReference[] resolveReferences = resolve.getReferences(); |
| 71 | + for (PsiReference reference : resolveReferences) { |
| 72 | + final PsiElement usageElement = reference.getElement(); |
| 73 | + if (usageElement instanceof Symbol && usageElement.isValid()) { |
| 74 | + // What follows is that want to collect code around the found element. |
| 75 | + // I will collect neighbouring PsiElements but not more than 20 characters to the right and |
| 76 | + // to the left of the current usageElement. |
| 77 | + final int elementTextOffset = usageElement.getTextOffset(); |
| 78 | + final int lineNumber = document.getLineNumber(elementTextOffset); |
| 79 | + final int lineStartOffset = document.getLineStartOffset(lineNumber); |
| 80 | + final int lineEndOffset = document.getLineEndOffset(lineNumber); |
| 81 | + assert lineStartOffset <= lineEndOffset; |
| 82 | + |
| 83 | + PsiElement displayStart = usageElement; |
| 84 | + PsiElement displayEnd = usageElement; |
| 85 | + while (displayStart.getPrevSibling() != null) { |
| 86 | + PsiElement tmpStart = displayStart.getPrevSibling(); |
| 87 | + if (usageElement.getTextOffset() - tmpStart.getTextOffset() > 20 |
| 88 | + || tmpStart.getTextOffset() < lineStartOffset) break; |
| 89 | + |
| 90 | + displayStart = tmpStart; |
| 91 | + } |
| 92 | + |
| 93 | + while (displayEnd.getNextSibling() != null) { |
| 94 | + PsiElement tmpEnd = displayEnd.getNextSibling(); |
| 95 | + if (tmpEnd.getTextOffset() - (usageElement.getTextOffset() + usageElement.getTextLength()) > 20 |
| 96 | + || tmpEnd.getTextOffset() + tmpEnd.getTextLength() > lineEndOffset) break; |
| 97 | + |
| 98 | + displayEnd = tmpEnd; |
| 99 | + } |
| 100 | + |
| 101 | + final String lineText = document.getText( |
| 102 | + TextRange.create(displayStart.getTextOffset(), displayEnd.getTextOffset() + displayEnd.getTextLength())); |
| 103 | + final GotoSymbolItem item = new GotoSymbolItem( |
| 104 | + usageElement, |
| 105 | + lineText, |
| 106 | + "(" + (lineNumber+1) + " in " + ((Symbol) resolve).getLocalizationConstruct() +")", lineNumber); |
| 107 | + declarations.add(item); |
| 108 | + |
| 109 | + } |
| 110 | + |
65 | 111 | } |
66 | 112 | } |
67 | 113 | } |
|
0 commit comments