/*
* Copyright 2010 Jean-Paul Balabanian and Yngve Devik Hammersland
*
* This file is part of glsl4idea.
*
* Glsl4idea is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* Glsl4idea is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with glsl4idea. If not, see <http://www.gnu.org/licenses/>.
*/
package glslplugin.actions;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.SelectionModel;
import com.intellij.openapi.ui.popup.Balloon;
import com.intellij.openapi.ui.popup.BalloonBuilder;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.ui.awt.RelativePoint;
import glslplugin.lang.GLSLLanguage;
import glslplugin.lang.elements.expressions.GLSLExpression;
import glslplugin.lang.elements.types.GLSLType;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
/**
* GLSLDeduceExpressionTypeAction is ...
*
* @author Yngve Devik Hammersland
* Date: Feb 7, 2009
* Time: 12:24:02 AM
*/
public class GLSLDeduceExpressionTypeAction extends AnAction {
public void actionPerformed(@NotNull AnActionEvent e) {
PsiElement element = getPsiElementEnclosingSelection(e);
while (element != null && !(element instanceof GLSLExpression)) {
element = element.getParent();
if (element instanceof PsiFile) {
element = null;
}
}
String typename = null;
String expressionText = null;
String value = null;
GLSLExpression expr = (GLSLExpression) element;
if (expr != null) {
expressionText = expr.getText();
GLSLType type = expr.getType();
typename = type.getTypename();
if(expr.isConstantValue()){
final Object constValue = expr.getConstantValue();
if(constValue != null){
value = constValue.toString();
}
}
}
if(expressionText == null && element != null){
expressionText = element.getText();
}
showBalloon(e, createHtml(expressionText, typename, value));
}
@Override
public void update(AnActionEvent e) {
final PsiFile psiFile = e.getData(CommonDataKeys.PSI_FILE);
if(psiFile != null){
e.getPresentation().setEnabledAndVisible(GLSLLanguage.GLSL_LANGUAGE.equals(psiFile.getLanguage()));
}
}
private void showBalloon(AnActionEvent e, String html) {
final Editor editor = e.getData(CommonDataKeys.EDITOR_EVEN_IF_INACTIVE);
if(editor == null) return;
final JBPopupFactory factory = JBPopupFactory.getInstance();
final BalloonBuilder builder = factory.createBalloonBuilder(new JLabel(html));
Balloon balloon = builder.createBalloon();
RelativePoint position = factory.guessBestPopupLocation(editor);
balloon.show(position, Balloon.Position.below);
}
private static String createHtml(String expression, String type, String value) {
expression = makeExpressionTextHtml(expression, type != null);
type = makeTypenameHtml(type);
value = escapeHtml(value);
//noinspection StringBufferReplaceableByString
StringBuilder b = new StringBuilder();
b.append("<html><table>");
b.append("<tr><th align='right'>Expression:</th>");
b.append("<td>").append(expression).append("</td></tr>");
b.append("<tr><th align='right'>Type:</th>");
b.append("<td>").append(type).append("</td></tr>");
if(value != null){
b.append("<tr><th align='right'>Value:</th>");
b.append("<td>").append(value).append("</td></tr>");
}
b.append("</table></html>");
return b.toString();
}
private static String escapeHtml(String text){
return org.apache.commons.lang.StringEscapeUtils.escapeHtml(text);
}
private static String makeTypenameHtml(String type) {
if (type == null) return "unable to deduce type";
else return "<code>" + escapeHtml(type) + "</code>";
}
private static String makeExpressionTextHtml(String expression, boolean valid) {
if(valid){
return "<code>" + escapeHtml(expression) + "</code>";
}else if(expression != null){
return "<code>" + escapeHtml(expression) + "</code> (Not an expression)";
}else{
return "selection is not an expression";
}
}
private PsiElement getPsiElementEnclosingSelection(AnActionEvent e) {
final PsiFile file = e.getData(CommonDataKeys.PSI_FILE);
final Editor editor = e.getData(CommonDataKeys.EDITOR_EVEN_IF_INACTIVE);
assert file != null;
assert editor != null;
final SelectionModel selectionModel = editor.getSelectionModel();
final int start = selectionModel.getSelectionStart();
final int end = selectionModel.getSelectionEnd();
PsiElement elt = getElementInclusiveRange(file, new TextRange(start, end));
assert elt != null;
return elt;
}
// From https://github.com/JetBrains/intellij-community/blob/master/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java#L399
private static PsiElement getElementInclusiveRange(PsiElement scope, TextRange range) {
PsiElement psiElement = scope.findElementAt(range.getStartOffset());
while (psiElement != null && !psiElement.getTextRange().contains(range)) {
if (psiElement == scope) return null;
psiElement = psiElement.getParent();
}
return psiElement;
}
}