blob: 326bbecf159685706382cd87df88ec612809bfdc [file] [log] [blame]
/*
* Copyright 2000-2014 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.usages.impl;
import com.intellij.lang.injection.InjectedLanguageManager;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.*;
import com.intellij.openapi.editor.colors.EditorColors;
import com.intellij.openapi.editor.colors.EditorColorsManager;
import com.intellij.openapi.editor.markup.*;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.usageView.UsageInfo;
import com.intellij.usageView.UsageViewBundle;
import com.intellij.usages.UsageContextPanel;
import com.intellij.usages.UsageView;
import com.intellij.usages.UsageViewPresentation;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.util.List;
/**
* @author cdr
*/
public class UsagePreviewPanel extends UsageContextPanelBase {
private static final Logger LOG = Logger.getInstance("#com.intellij.usages.impl.UsagePreviewPanel");
private Editor myEditor;
public UsagePreviewPanel(@NotNull Project project, @NotNull UsageViewPresentation presentation) {
super(project, presentation);
}
public static class Provider implements UsageContextPanel.Provider {
@NotNull
@Override
public UsageContextPanel create(@NotNull UsageView usageView) {
return new UsagePreviewPanel(((UsageViewImpl)usageView).getProject(), usageView.getPresentation());
}
@Override
public boolean isAvailableFor(@NotNull UsageView usageView) {
return true;
}
@NotNull
@Override
public String getTabTitle() {
return "Preview";
}
}
private void resetEditor(@NotNull final List<UsageInfo> infos) {
ApplicationManager.getApplication().assertIsDispatchThread();
PsiElement psiElement = infos.get(0).getElement();
if (psiElement == null) return;
PsiFile psiFile = psiElement.getContainingFile();
if (psiFile == null) return;
PsiLanguageInjectionHost host = InjectedLanguageManager.getInstance(myProject).getInjectionHost(psiFile);
if (host != null) {
psiFile = host.getContainingFile();
if (psiFile == null) return;
}
final Document document = PsiDocumentManager.getInstance(psiFile.getProject()).getDocument(psiFile);
if (document == null) return;
if (myEditor == null || document != myEditor.getDocument()) {
releaseEditor();
removeAll();
myEditor = createEditor(psiFile, document);
if (myEditor == null) return;
myEditor.setBorder(null);
add(myEditor.getComponent(), BorderLayout.CENTER);
revalidate();
}
final Editor editor = myEditor;
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
if (myProject.isDisposed()) return;
highlight(infos, editor);
}
});
}
private static final Key<Boolean> IN_PREVIEW_USAGE_FLAG = Key.create("IN_PREVIEW_USAGE_FLAG");
private void highlight(@NotNull List<UsageInfo> infos, @NotNull Editor editor) {
if (editor != myEditor) return; //already disposed
PsiDocumentManager.getInstance(myProject).commitAllDocuments();
MarkupModel markupModel = myEditor.getMarkupModel();
for (RangeHighlighter highlighter : markupModel.getAllHighlighters()) {
if (highlighter.getUserData(IN_PREVIEW_USAGE_FLAG) != null) {
highlighter.dispose();
}
}
for (int i = infos.size()-1; i>=0; i--) { // finish with the first usage so that caret end up there
UsageInfo info = infos.get(i);
PsiElement psiElement = info.getElement();
if (psiElement == null || !psiElement.isValid()) continue;
int offsetInFile = psiElement.getTextOffset();
EditorColorsManager colorManager = EditorColorsManager.getInstance();
TextAttributes attributes = colorManager.getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES);
TextRange elementRange = psiElement.getTextRange();
TextRange infoRange = info.getRangeInElement();
TextRange textRange = infoRange == null ? null : elementRange.cutOut(infoRange);
if (textRange == null) textRange = elementRange;
// hack to determine element range to highlight
if (psiElement instanceof PsiNamedElement && !(psiElement instanceof PsiFile)) {
PsiFile psiFile = psiElement.getContainingFile();
PsiElement nameElement = psiFile.findElementAt(offsetInFile);
if (nameElement != null) {
textRange = nameElement.getTextRange();
}
}
// highlight injected element in host document textrange
textRange = InjectedLanguageManager.getInstance(myProject).injectedToHost(psiElement, textRange);
RangeHighlighter highlighter = markupModel.addRangeHighlighter(textRange.getStartOffset(), textRange.getEndOffset(),
HighlighterLayer.ADDITIONAL_SYNTAX, attributes,
HighlighterTargetArea.EXACT_RANGE);
highlighter.putUserData(IN_PREVIEW_USAGE_FLAG, Boolean.TRUE);
myEditor.getCaretModel().moveToOffset(textRange.getEndOffset());
}
myEditor.getScrollingModel().scrollToCaret(ScrollType.CENTER);
}
private static final Key<UsagePreviewPanel> PREVIEW_EDITOR_FLAG = Key.create("PREVIEW_EDITOR_FLAG");
private Editor createEditor(final PsiFile psiFile, Document document) {
if (isDisposed) return null;
Project project = psiFile.getProject();
Editor editor = EditorFactory.getInstance().createEditor(document, project, psiFile.getVirtualFile(), true);
EditorSettings settings = editor.getSettings();
settings.setLineMarkerAreaShown(false);
settings.setIndentGuidesShown(false);
settings.setFoldingOutlineShown(false);
settings.setAdditionalColumnsCount(0);
settings.setAdditionalLinesCount(0);
settings.setVirtualSpace(true);
editor.putUserData(PREVIEW_EDITOR_FLAG, this);
return editor;
}
@Override
public void dispose() {
isDisposed = true;
releaseEditor();
for (Editor editor : EditorFactory.getInstance().getAllEditors()) {
if (editor.getProject() == myProject && editor.getUserData(PREVIEW_EDITOR_FLAG) == this) {
LOG.error("Editor was not released:"+editor);
}
}
}
private void releaseEditor() {
if (myEditor != null) {
EditorFactory.getInstance().releaseEditor(myEditor);
myEditor = null;
}
}
@Override
public void updateLayoutLater(@Nullable final List<UsageInfo> infos) {
if (infos == null) {
releaseEditor();
removeAll();
JComponent titleComp = new JLabel(UsageViewBundle.message("select.the.usage.to.preview", myPresentation.getUsagesWord()), SwingConstants.CENTER);
add(titleComp, BorderLayout.CENTER);
revalidate();
}
else {
resetEditor(infos);
}
}
}