Java tutorial
/* * Copyright 2000-2012 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.intellij.codeInsight.highlighting; import com.intellij.codeInsight.CodeInsightBundle; import com.intellij.codeInsight.TargetElementUtil; import com.intellij.find.EditorSearchComponent; import com.intellij.find.findUsages.PsiElement2UsageTargetAdapter; import com.intellij.injected.editor.EditorWindow; import com.intellij.lang.injection.InjectedLanguageManager; import com.intellij.navigation.NavigationItem; import com.intellij.openapi.actionSystem.ActionManager; import com.intellij.openapi.actionSystem.IdeActions; import com.intellij.openapi.actionSystem.Shortcut; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.SelectionModel; import com.intellij.openapi.editor.colors.EditorColors; import com.intellij.openapi.editor.colors.EditorColorsManager; import com.intellij.openapi.editor.markup.HighlighterLayer; import com.intellij.openapi.editor.markup.RangeHighlighter; import com.intellij.openapi.editor.markup.TextAttributes; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.keymap.KeymapUtil; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.wm.WindowManager; import com.intellij.pom.PomTarget; import com.intellij.pom.PomTargetPsiElement; import com.intellij.pom.PsiDeclaredTarget; import com.intellij.psi.*; import com.intellij.psi.util.PsiUtilBase; import com.intellij.psi.util.PsiUtilCore; import com.intellij.usages.UsageTarget; import com.intellij.usages.UsageTargetUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.mustbe.consulo.RequiredDispatchThread; import javax.swing.*; import java.util.*; public class HighlightUsagesHandler extends HighlightHandlerBase { private static final Logger LOG = Logger .getInstance("#com.intellij.codeInsight.highlighting.HighlightUsagesHandler"); @RequiredDispatchThread public static void invoke(@NotNull Project project, @NotNull Editor editor, PsiFile file) { PsiDocumentManager.getInstance(project).commitAllDocuments(); SelectionModel selectionModel = editor.getSelectionModel(); if (file == null && !selectionModel.hasSelection()) { selectionModel.selectWordAtCaret(false); } if (file == null || selectionModel.hasSelection()) { doRangeHighlighting(editor, project); return; } final HighlightUsagesHandlerBase handler = createCustomHandler(editor, file); if (handler != null) { handler.highlightUsages(); return; } UsageTarget[] usageTargets = UsageTargetUtil.findUsageTargets(editor, file); if (usageTargets == null) { PsiElement targetElement = getTargetElement(editor, file); if (targetElement != null) { if (!(targetElement instanceof NavigationItem)) { targetElement = targetElement.getNavigationElement(); } if (targetElement instanceof NavigationItem) { usageTargets = new UsageTarget[] { new PsiElement2UsageTargetAdapter(targetElement) }; } } } if (usageTargets == null) { PsiReference ref = TargetElementUtil.findReference(editor); if (ref instanceof PsiPolyVariantReference) { ResolveResult[] results = ((PsiPolyVariantReference) ref).multiResolve(false); if (results.length > 0) { usageTargets = new UsageTarget[results.length]; for (int i = 0; i < results.length; ++i) { usageTargets[i] = new PsiElement2UsageTargetAdapter(results[i].getElement()); } } } } if (usageTargets == null) { if (file.findElementAt(editor.getCaretModel().getOffset()) instanceof PsiWhiteSpace) return; selectionModel.selectWordAtCaret(false); String selection = selectionModel.getSelectedText(); LOG.assertTrue(selection != null); for (int i = 0; i < selection.length(); i++) { if (!Character.isJavaIdentifierPart(selection.charAt(i))) { selectionModel.removeSelection(); return; } } doRangeHighlighting(editor, project); selectionModel.removeSelection(); return; } boolean clearHighlights = isClearHighlights(editor); for (UsageTarget target : usageTargets) { target.highlightUsages(file, editor, clearHighlights); } } @Nullable public static HighlightUsagesHandlerBase createCustomHandler(final Editor editor, final PsiFile file) { for (HighlightUsagesHandlerFactory factory : Extensions .getExtensions(HighlightUsagesHandlerFactory.EP_NAME)) { final HighlightUsagesHandlerBase handler = factory.createHighlightUsagesHandler(editor, file); if (handler != null) { return handler; } } return null; } @Nullable @RequiredDispatchThread private static PsiElement getTargetElement(Editor editor, PsiFile file) { PsiElement target = TargetElementUtil.findTargetElement(editor, TargetElementUtil.getReferenceSearchFlags()); if (target == null) { int offset = TargetElementUtil.adjustOffset(file, editor.getDocument(), editor.getCaretModel().getOffset()); PsiElement element = file.findElementAt(offset); if (element == null) return null; } return target; } private static void doRangeHighlighting(Editor editor, Project project) { if (!editor.getSelectionModel().hasSelection()) return; final String text = editor.getSelectionModel().getSelectedText(); if (text == null) return; if (editor instanceof EditorWindow) { // highlight selection in the whole editor, not injected fragment only editor = ((EditorWindow) editor).getDelegate(); } final JComponent oldHeader = editor.getHeaderComponent(); if (oldHeader instanceof EditorSearchComponent) { final EditorSearchComponent oldSearch = (EditorSearchComponent) oldHeader; if (oldSearch.hasMatches()) { String oldText = oldSearch.getTextInField(); if (!oldSearch.isRegexp()) { oldText = StringUtil.escapeToRegexp(oldText); oldSearch.setRegexp(true); } String newText = oldText + '|' + StringUtil.escapeToRegexp(text); oldSearch.setTextInField(newText); return; } } final EditorSearchComponent header = new EditorSearchComponent(editor, project); header.setRegexp(false); editor.setHeaderComponent(header); } public static class DoHighlightRunnable implements Runnable { private final List<PsiReference> myRefs; private final Project myProject; private final PsiElement myTarget; private final Editor myEditor; private final PsiFile myFile; private final boolean myClearHighlights; public DoHighlightRunnable(@NotNull List<PsiReference> refs, @NotNull Project project, @NotNull PsiElement target, Editor editor, PsiFile file, boolean clearHighlights) { myRefs = refs; myProject = project; myTarget = target; myEditor = editor instanceof EditorWindow ? ((EditorWindow) editor).getDelegate() : editor; myFile = file; myClearHighlights = clearHighlights; } @Override public void run() { highlightReferences(myProject, myTarget, myRefs, myEditor, myFile, myClearHighlights); setStatusText(myProject, getElementName(myTarget), myRefs.size(), myClearHighlights); } } public static void highlightOtherOccurrences(final List<PsiElement> otherOccurrences, Editor editor, boolean clearHighlights) { EditorColorsManager manager = EditorColorsManager.getInstance(); TextAttributes attributes = manager.getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES); PsiElement[] elements = PsiUtilCore.toPsiElementArray(otherOccurrences); doHighlightElements(editor, elements, attributes, clearHighlights); } public static void highlightReferences(@NotNull Project project, @NotNull PsiElement element, @NotNull List<PsiReference> refs, Editor editor, PsiFile file, boolean clearHighlights) { HighlightManager highlightManager = HighlightManager.getInstance(project); EditorColorsManager manager = EditorColorsManager.getInstance(); TextAttributes attributes = manager.getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES); TextAttributes writeAttributes = manager.getGlobalScheme() .getAttributes(EditorColors.WRITE_SEARCH_RESULT_ATTRIBUTES); setupFindModel(project); ReadWriteAccessDetector detector = ReadWriteAccessDetector.findDetector(element); if (detector != null) { List<PsiReference> readRefs = new ArrayList<PsiReference>(); List<PsiReference> writeRefs = new ArrayList<PsiReference>(); for (PsiReference ref : refs) { if (detector.getReferenceAccess(element, ref) == ReadWriteAccessDetector.Access.Read) { readRefs.add(ref); } else { writeRefs.add(ref); } } doHighlightRefs(highlightManager, editor, readRefs, attributes, clearHighlights); doHighlightRefs(highlightManager, editor, writeRefs, writeAttributes, clearHighlights); } else { doHighlightRefs(highlightManager, editor, refs, attributes, clearHighlights); } TextRange range = getNameIdentifierRange(file, element); if (range != null) { TextAttributes nameAttributes = attributes; if (detector != null && detector.isDeclarationWriteAccess(element)) { nameAttributes = writeAttributes; } highlightRanges(highlightManager, editor, nameAttributes, clearHighlights, Arrays.asList(range)); } } @Nullable public static TextRange getNameIdentifierRange(PsiFile file, PsiElement element) { final InjectedLanguageManager injectedManager = InjectedLanguageManager.getInstance(element.getProject()); if (element instanceof PomTargetPsiElement) { final PomTarget target = ((PomTargetPsiElement) element).getTarget(); if (target instanceof PsiDeclaredTarget) { final PsiDeclaredTarget declaredTarget = (PsiDeclaredTarget) target; final TextRange range = declaredTarget.getNameIdentifierRange(); if (range != null) { if (range.getStartOffset() < 0 || range.getLength() <= 0) { return null; } final PsiElement navElement = declaredTarget.getNavigationElement(); if (PsiUtilBase.isUnderPsiRoot(file, navElement)) { return injectedManager.injectedToHost(navElement, range.shiftRight(navElement.getTextRange().getStartOffset())); } } } } if (!PsiUtilBase.isUnderPsiRoot(file, element)) { return null; } PsiElement identifier = getNameIdentifier(element); if (identifier != null && PsiUtilBase.isUnderPsiRoot(file, identifier)) { return injectedManager.injectedToHost(identifier, identifier.getTextRange()); } return null; } public static void doHighlightElements(Editor editor, PsiElement[] elements, TextAttributes attributes, boolean clearHighlights) { HighlightManager highlightManager = HighlightManager.getInstance(editor.getProject()); List<TextRange> textRanges = new ArrayList<TextRange>(elements.length); for (PsiElement element : elements) { TextRange range = element.getTextRange(); // injection occurs range = InjectedLanguageManager.getInstance(element.getProject()).injectedToHost(element, range); textRanges.add(range); } highlightRanges(highlightManager, editor, attributes, clearHighlights, textRanges); } public static void highlightRanges(HighlightManager highlightManager, Editor editor, TextAttributes attributes, boolean clearHighlights, List<TextRange> textRanges) { if (clearHighlights) { clearHighlights(editor, highlightManager, textRanges, attributes); return; } ArrayList<RangeHighlighter> highlighters = new ArrayList<RangeHighlighter>(); for (TextRange range : textRanges) { highlightManager.addRangeHighlight(editor, range.getStartOffset(), range.getEndOffset(), attributes, false, highlighters); } for (RangeHighlighter highlighter : highlighters) { String tooltip = getLineTextErrorStripeTooltip(editor.getDocument(), highlighter.getStartOffset(), true); highlighter.setErrorStripeTooltip(tooltip); } } public static boolean isClearHighlights(Editor editor) { if (editor instanceof EditorWindow) editor = ((EditorWindow) editor).getDelegate(); RangeHighlighter[] highlighters = ((HighlightManagerImpl) HighlightManager.getInstance(editor.getProject())) .getHighlighters(editor); int caretOffset = editor.getCaretModel().getOffset(); for (RangeHighlighter highlighter : highlighters) { if (TextRange.create(highlighter).grown(1).contains(caretOffset)) { return true; } } return false; } private static void clearHighlights(Editor editor, HighlightManager highlightManager, List<TextRange> rangesToHighlight, TextAttributes attributes) { if (editor instanceof EditorWindow) editor = ((EditorWindow) editor).getDelegate(); RangeHighlighter[] highlighters = ((HighlightManagerImpl) highlightManager).getHighlighters(editor); Arrays.sort(highlighters, new Comparator<RangeHighlighter>() { @Override public int compare(RangeHighlighter o1, RangeHighlighter o2) { return o1.getStartOffset() - o2.getStartOffset(); } }); Collections.sort(rangesToHighlight, new Comparator<TextRange>() { @Override public int compare(TextRange o1, TextRange o2) { return o1.getStartOffset() - o2.getStartOffset(); } }); int i = 0; int j = 0; while (i < highlighters.length && j < rangesToHighlight.size()) { RangeHighlighter highlighter = highlighters[i]; TextRange highlighterRange = TextRange.create(highlighter); TextRange refRange = rangesToHighlight.get(j); if (refRange.equals(highlighterRange) && attributes.equals(highlighter.getTextAttributes()) && highlighter.getLayer() == HighlighterLayer.SELECTION - 1) { highlightManager.removeSegmentHighlighter(editor, highlighter); i++; } else if (refRange.getStartOffset() > highlighterRange.getEndOffset()) { i++; } else if (refRange.getEndOffset() < highlighterRange.getStartOffset()) { j++; } else { i++; j++; } } } private static void doHighlightRefs(HighlightManager highlightManager, @NotNull Editor editor, @NotNull List<PsiReference> refs, TextAttributes attributes, boolean clearHighlights) { List<TextRange> textRanges = new ArrayList<TextRange>(refs.size()); for (PsiReference ref : refs) { textRanges.addAll(getRangesToHighlight(ref)); } highlightRanges(highlightManager, editor, attributes, clearHighlights, textRanges); } public static List<TextRange> getRangesToHighlight(final PsiReference ref) { final List<TextRange> relativeRanges = ReferenceRange.getRanges(ref); List<TextRange> answer = new ArrayList<TextRange>(relativeRanges.size()); for (TextRange relativeRange : relativeRanges) { PsiElement element = ref.getElement(); TextRange range = safeCut(element.getTextRange(), relativeRange); // injection occurs answer.add(InjectedLanguageManager.getInstance(element.getProject()).injectedToHost(element, range)); } return answer; } private static TextRange safeCut(TextRange range, TextRange relative) { int start = Math.min(range.getEndOffset(), range.getStartOffset() + relative.getStartOffset()); int end = Math.min(range.getEndOffset(), range.getStartOffset() + relative.getEndOffset()); return new TextRange(start, end); } @Nullable public static PsiElement getNameIdentifier(@NotNull PsiElement element) { if (element instanceof PsiNameIdentifierOwner) { return ((PsiNameIdentifierOwner) element).getNameIdentifier(); } if (element.isPhysical() && element instanceof PsiNamedElement && element.getContainingFile() != null && element.getTextRange() != null) { // Quite hacky way to get name identifier. Depends on getTextOffset overriden properly. final PsiElement potentialIdentifier = element .findElementAt(element.getTextOffset() - element.getTextRange().getStartOffset()); if (potentialIdentifier != null && Comparing.equal(potentialIdentifier.getText(), ((PsiNamedElement) element).getName(), false)) { return potentialIdentifier; } } return null; } public static void setStatusText(Project project, final String elementName, int refCount, boolean clearHighlights) { String message; if (clearHighlights) { message = ""; } else if (refCount > 0) { message = CodeInsightBundle.message( elementName != null ? "status.bar.highlighted.usages.message" : "status.bar.highlighted.usages.no.target.message", refCount, elementName, getShortcutText()); } else { message = CodeInsightBundle .message(elementName != null ? "status.bar.highlighted.usages.not.found.message" : "status.bar.highlighted.usages.not.found.no.target.message", elementName); } WindowManager.getInstance().getStatusBar(project).setInfo(message); } private static String getElementName(final PsiElement element) { return ElementDescriptionUtil.getElementDescription(element, HighlightUsagesDescriptionLocation.INSTANCE); } public static String getShortcutText() { final Shortcut[] shortcuts = ActionManager.getInstance() .getAction(IdeActions.ACTION_HIGHLIGHT_USAGES_IN_FILE).getShortcutSet().getShortcuts(); if (shortcuts.length == 0) { return "<no key assigned>"; } return KeymapUtil.getShortcutText(shortcuts[0]); } }