/* * Copyright 2013 Guidewire Software, Inc. */ package gw.plugin.ij.typeinfo; import com.intellij.icons.AllIcons; import com.intellij.ide.DataManager; import com.intellij.lang.injection.InjectedLanguageManager; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.impl.EditorComponentImpl; import com.intellij.openapi.editor.impl.EditorImpl; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.popup.JBPopup; import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.impl.PsiManagerImpl; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.ui.components.JBLabel; import com.intellij.ui.components.JBPanel; import com.intellij.ui.popup.NotLookupOrSearchCondition; import com.intellij.ui.popup.PopupPositionManager; import com.intellij.xml.util.XmlStringUtil; import gw.lang.parser.IExpression; import gw.lang.parser.IParsedElement; import gw.lang.parser.expressions.INameInDeclaration; import gw.lang.parser.expressions.IVarStatement; import gw.lang.parser.statements.IClassDeclaration; import gw.lang.parser.statements.IFunctionStatement; import gw.lang.reflect.IType; import gw.plugin.ij.actions.TypeSystemAwareAction; import gw.plugin.ij.lang.psi.IGosuPsiElement; import gw.plugin.ij.util.GosuBundle; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.StringSelection; public class TypeInfoAction extends TypeSystemAwareAction implements DumbAware { private static final Logger LOG = Logger.getInstance(TypeInfoAction.class); public TypeInfoAction() { super(GosuBundle.message("typeinfo.name"), GosuBundle.message("typeinfo.description"), null); } public void actionPerformed(@NotNull AnActionEvent e) { final Object data = e.getDataContext().getData(PlatformDataKeys.CONTEXT_COMPONENT.getName()); if (!(data instanceof EditorComponentImpl)) { return; } final EditorComponentImpl editorComponent = (EditorComponentImpl) data; final EditorImpl editor = editorComponent.getEditor(); final Project project = editor.getProject(); final int offset = editor.getCaretModel().getOffset(); final VirtualFile virtualFile = editor.getVirtualFile(); final PsiFile file = PsiManagerImpl.getInstance(project).findFile(virtualFile); final PsiElement element = file.findElementAt(offset); if (!showTooltip(editor, element, offset)) { final PsiElement injectedElement = InjectedLanguageManager.getInstance(project).findInjectedElementAt(file, offset); if (injectedElement != null) { showTooltip(editor, injectedElement, offset); } } } protected boolean showTooltip(EditorImpl editor, PsiElement element, int offset) { final IGosuPsiElement impl = PsiTreeUtil.getParentOfType(element, IGosuPsiElement.class); if (impl != null) { final IParsedElement parsedElement = impl.getParsedElement(); if (parsedElement != null) { final IType type = unwrapMetaType(getType(parsedElement)); if (type != null) { showInfoTooltip(type.getName(), editor); return true; } } } return false; } protected IType unwrapMetaType(IType type) { return type; //type instanceof IMetaType ? ((IMetaType) type).getType() : type; } @Nullable protected IType getType(@NotNull IParsedElement element) { if (element instanceof IClassDeclaration) { return ((IClassDeclaration) element).getGSClass(); } else if (element instanceof IFunctionStatement) { return element.getReturnType(); } else if (element instanceof INameInDeclaration) { return getType(element.getParent()); } else if (element instanceof IExpression) { //TODO-dp delete these and have INameInDeclaration.getType() work return ((IExpression) element).getType(); } else if (element instanceof IVarStatement) { return ((IVarStatement) element).getType(); } else { LOG.warn("Case for " + element + " is not supported yet."); return null; } } private static class CopyAction extends AnAction { private String _typeName; CopyAction(String typeName) { super("Copy", "Copy", AllIcons.Actions.Copy); _typeName = typeName; } @Override public void actionPerformed(AnActionEvent e) { Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); clipboard.setContents(new StringSelection(_typeName), null); } } public static void showInfoTooltip(String typeName, @NotNull final Editor editor) { final String text = String.format("<html><b>%s</b></html>", XmlStringUtil.escapeString(typeName)); Project project = editor.getProject(); JBPanel panel = new JBPanel(new BorderLayout()); panel.setBorder(BorderFactory.createEmptyBorder(0, 20, 0, 0)); panel.setBackground(Color.white); JBLabel textComponent = new JBLabel(text); panel.add(textComponent, BorderLayout.WEST); DefaultActionGroup actions = new DefaultActionGroup(); actions.add(new CopyAction(typeName)); JComponent component = ActionManager.getInstance().createActionToolbar(ActionPlaces.EDITOR_POPUP, actions, true).getComponent(); component.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); component.setOpaque(false); panel.add(component, BorderLayout.EAST); final JBPopup hint = JBPopupFactory.getInstance().createComponentPopupBuilder(panel, null) .setRequestFocusCondition(project, NotLookupOrSearchCondition.INSTANCE) .setProject(project) .setMovable(true) .setCancelOnClickOutside(true) .setCouldPin(null) .setModalContext(false) .createPopup(); final Component focusOwner = IdeFocusManager.getInstance(project).getFocusOwner(); DataContext dataContext = DataManager.getInstance().getDataContext(focusOwner); PopupPositionManager.positionPopupInBestPosition(hint, editor, dataContext); } }