All Downloads are FREE. Search and download functionalities are using the official Maven repository.

editor.ParameterInfoPopup Maven / Gradle / Ivy

There is a newer version: 1.18.1
Show newest version
package editor;

import editor.util.HTMLEscapeUtil;
import gw.lang.parser.IDynamicFunctionSymbol;
import gw.lang.parser.IFunctionSymbol;
import gw.lang.parser.IHasArguments;
import gw.lang.parser.IParseTree;
import gw.lang.parser.IParsedElement;
import gw.lang.parser.ISymbol;
import gw.lang.parser.expressions.IArgumentListClause;
import gw.lang.parser.expressions.IBeanMethodCallExpression;
import gw.lang.parser.expressions.IMethodCallExpression;
import gw.lang.parser.expressions.INewExpression;
import gw.lang.reflect.IBlockType;
import gw.lang.reflect.IConstructorInfo;
import gw.lang.reflect.IErrorType;
import gw.lang.reflect.IFeatureInfo;
import gw.lang.reflect.IFunctionType;
import gw.lang.reflect.IMethodInfo;
import gw.lang.reflect.IParameterInfo;
import gw.lang.reflect.IType;
import gw.lang.reflect.gs.IGosuClass;

import javax.swing.*;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.event.UndoableEditListener;
import javax.swing.text.BadLocationException;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class ParameterInfoPopup extends JPopupMenu
{

  private JPanel _pane = new JPanel();
  private GosuEditor _editor;
  private EditorKeyListener _editorKeyListener;
  private CaretListener _editorCaretListener;
  private UndoableEditListener _docListener;
  private JPanel _labelContainer;
  private int _iArgPosition;


  public ParameterInfoPopup( GosuEditor editor )
  {
    super();
    _editor = editor;
    initLayout();
  }

  protected void initLayout()
  {
    setOpaque( true );
    setDoubleBuffered( true );

    GridBagLayout gridBag = new GridBagLayout();
    _pane.setLayout( gridBag );
    GridBagConstraints c = new GridBagConstraints();

    IParameterInfo[][] paramInfoLists = getParamInfoLists( _editor.getFunctionCallAtCaret() );
    if( paramInfoLists == null || paramInfoLists.length == 0 )
    {
      EventQueue.invokeLater( () -> setVisible( false ) );
      return;
    }
    _labelContainer = new JPanel();
    _labelContainer.setLayout( new BoxLayout( _labelContainer, BoxLayout.Y_AXIS ) );
    _labelContainer.setBackground( Scheme.active().getTooltipBackground() );
    _labelContainer.setBorder( BorderFactory.createEmptyBorder( 1, 3, 1, 3 ) );
    int iArgIndex = getArgIndex();
    for( int i = 0; i < paramInfoLists.length; i++ )
    {
      IParameterInfo[] paramInfoList = paramInfoLists[i];
      addParameterListLabel( paramInfoList, _labelContainer, i != paramInfoLists.length - 1, iArgIndex );
    }
    c.anchor = GridBagConstraints.CENTER;
    c.fill = GridBagConstraints.BOTH;
    c.gridx = 0;
    c.gridy = 0;
    c.gridwidth = GridBagConstraints.REMAINDER;
    c.gridheight = 1;
    c.weightx = 1;
    c.weighty = 1;
    _pane.add( _labelContainer, c );

    //
    // The Symbol list
    //

    add( _pane );

    if( _editor != null )
    {
      _editorKeyListener = new EditorKeyListener();
      _editorCaretListener = new EditorCaretListener();
      _docListener = e -> filterDisplay();
    }
  }

  private int getArgIndex()
  {
    IParseTree deepest = _editor.getDeepestLocationAtCaret();
    if( deepest == null )
    {
      return -1;
    }
    while( deepest != null && !(deepest.getParsedElement() instanceof IArgumentListClause) )
    {
      deepest = deepest.getParent();
      if( deepest == null )
      {
        return -1;
      }
    }
    IArgumentListClause argListClause = (IArgumentListClause)deepest.getParsedElement();
    int iCaretPos = _editor.getEditor().getCaretPosition();
    List args = argListClause.getLocation().getChildren();
    for( int i = 0; i < args.size(); i++ )
    {
      IParseTree location = args.get( i );
      if( location.getExtent() + 1 >= iCaretPos )
      {
        return i;
      }
    }
    return args.size() -1;
  }

  private void addParameterListLabel( IParameterInfo[] paramList, JPanel container, boolean bBorder, int iArgIndex )
  {
    JLabel label = new JLabel();
    label.setBorder( bBorder ? BorderFactory.createMatteBorder( 0, 0, 1, 0, Scheme.active().getControlShadow() ) : null );
    label.setFont( _editor.getEditor().getFont() );
    label.setText( makeParamInfoContent( paramList, iArgIndex ) );
    container.add( label );
  }

  private String makeParamInfoContent( IParameterInfo[] paramInfoList, int iArgIndex )
  {
    String strContent = "";
    if( paramInfoList == null || paramInfoList.length == 0 )
    {
      strContent += "<" + "no parameters" + ">";
    }
    else
    {
      boolean bUseName = false;
      for( int j = 0; j < paramInfoList.length; j++ )
      {
        IParameterInfo pi = paramInfoList[j];
        if( pi.getName() != null && pi.getName().length() > 0 && !pi.getName().equalsIgnoreCase( pi.getFeatureType().getRelativeName() ) )
        {
          bUseName = true;
          break;
        }
      }

      Color typeClr = Scheme.active().getCodeTypeLiteral();
      String typeColor = String.format( "#%02x%02x%02x", typeClr.getRed(), typeClr.getGreen(), typeClr.getBlue() );
      for( int j = 0; j < paramInfoList.length; j++ )
      {
        IParameterInfo pi = paramInfoList[j];
        boolean bBlock = pi.getFeatureType() instanceof IBlockType;
        String strTypeName = bBlock
                             ? (pi.getName() + ((IBlockType)pi.getFeatureType()).getRelativeNameSansBlock())
                             : pi.getFeatureType().getRelativeName();
        strTypeName = HTMLEscapeUtil.escape( strTypeName );
        if( !bBlock &&
            bUseName &&
            pi.getName() != null &&
            pi.getName().length() > 0 )
        {
          if( j == iArgIndex )
          {
            strContent += "" + pi.getName() + ": ";
          }
          else
          {
            strContent += "" + pi.getName() + ": ";
          }
        }
        if( j == iArgIndex )
        {
          strContent += "" + strTypeName + "";
        }
        else
        {
          strContent += strTypeName;
        }
        if( j < paramInfoList.length - 1 )
        {
          strContent += ", ";
        }
      }
    }
    return strContent + "";
  }

  private IParameterInfo[][] getParamInfoLists( IParsedElement parsedElement )
  {
    if( parsedElement instanceof IHasArguments )
    {
      int iArgPosition = ((IHasArguments)parsedElement).getArgPosition();
      if( _editor.getEditor().getCaretPosition() < iArgPosition )
      {
        // Caret must be positioned within the arg-list
        return getParamInfoLists( _editor.findFunction( parsedElement.getParent() ) );
      }
      _iArgPosition = iArgPosition;

      List paramInfoLists = new ArrayList<>();
      if( parsedElement instanceof IMethodCallExpression )
      {
        IFunctionSymbol fs = ((IMethodCallExpression)parsedElement).getFunctionSymbol();
        if( fs != null )
        {
          IFunctionType funcType = (IFunctionType)fs.getType();
          if( funcType.getMethodInfo() != null )
          {
            return new IParameterInfo[][]{funcType.getMethodInfo().getParameters()};
          }
          else if( funcType.getScriptPart() != null && funcType.getScriptPart().getContainingType() instanceof IGosuClass )
          {
            IGosuClass type = (IGosuClass)funcType.getScriptPart().getContainingType();
            String strName = funcType.getName();
            List methods = type.getTypeInfo().getMethods( type );
            for( Object method : methods )
            {
              IMethodInfo mi = (IMethodInfo)method;
              if( mi.getDisplayName().equalsIgnoreCase( strName ) && mi.isVisible( _editor.getScriptabilityModifier() ) )
              {
                paramInfoLists.add( mi.getParameters() );
              }
            }
          }
          else
          {
            Map> dfsDecls = _editor.getParser().getDfsDecls();
            List dynamicFunctionSymbols = dfsDecls.get( funcType.getName() );
            if( dynamicFunctionSymbols != null )
            {
              for( IFunctionSymbol symbol : dynamicFunctionSymbols )
              {
                assert symbol instanceof IDynamicFunctionSymbol;
                IDynamicFunctionSymbol dfs = (IDynamicFunctionSymbol)symbol;
                IParameterInfo[] params = new IParameterInfo[dfs.getArgs().size()];
                for( int i = 0; i < dfs.getArgs().size(); i++ )
                {
                  final ISymbol arg = dfs.getArgs().get( i );
                  params[i] = new ParameterInfoStub( arg );
                }
                paramInfoLists.add( params );
              }
            }
          }
        }
        return paramInfoLists.toArray( new IParameterInfo[0][0] );
      }
      if( parsedElement instanceof IBeanMethodCallExpression )
      {
        IMethodInfo mi = ((IBeanMethodCallExpression)parsedElement).getMethodDescriptor();
        if( mi != null )
        {
          String strName = mi.getDisplayName();
          IType type = mi.getOwnersType();
          List methods = type.getTypeInfo().getMethods();
          for( int i = 0; i < methods.size(); i++ )
          {
            mi = (IMethodInfo)methods.get( i );
            if( mi.getDisplayName().equalsIgnoreCase( strName ) && mi.isVisible( _editor.getScriptabilityModifier() ) )
            {
              paramInfoLists.add( mi.getParameters() );
            }
          }
        }
        return paramInfoLists.toArray( new IParameterInfo[0][0] );
      }
      if( parsedElement instanceof INewExpression )
      {
        INewExpression newExpression = (INewExpression)parsedElement;
        if( !(newExpression.getType() instanceof IErrorType) )
        {
          IType type = newExpression.getType();
          List ctors = type.getTypeInfo().getConstructors();
          //noinspection Convert2streamapi
          for( IConstructorInfo ctor : ctors )
          {
            if( ctor.isVisible( _editor.getScriptabilityModifier() ) )
            {
              paramInfoLists.add( ctor.getParameters() );
            }
          }
          return paramInfoLists.toArray( new IParameterInfo[0][0] );
        }
      }
    }
    return null;
  }

  @Override
  public void setVisible( boolean bVisible )
  {
    super.setVisible( bVisible );

    if( _editor == null )
    {
      return;
    }

    if( bVisible )
    {
      registerListeners();
    }
    else
    {
      unregisterListeners();
      _editor.getEditor().requestFocus();
    }
  }

  void registerListeners()
  {
    unregisterListeners();

    _editor.getEditor().addKeyListener( _editorKeyListener );
    _editor.getEditor().addCaretListener( _editorCaretListener );
    _editor.getEditor().getDocument().addUndoableEditListener( _docListener );
  }

  void unregisterListeners()
  {
    _editor.getEditor().getDocument().removeUndoableEditListener( _docListener );
    _editor.getEditor().removeCaretListener( _editorCaretListener );
    _editor.getEditor().removeKeyListener( _editorKeyListener );
  }

  void filterDisplay()
  {
    filterDisplay( null );
  }

  void filterDisplay( String strWholePath )
  {
  }

  public void display( int iPosition ) throws BadLocationException
  {
    Rectangle rcCaretBounds = _editor.getEditor().modelToView( iPosition );
    if( rcCaretBounds == null )
    { // the editor doesn't have a positive size - most likely we're in a test
      show( null, 0, 0 );
    }
    else
    {
      FontMetrics fm = getToolkit().getFontMetrics( _editor.getFont() );
      int iLineHeight = fm.getHeight();

      show( _editor.getEditor(), rcCaretBounds.x, rcCaretBounds.y + iLineHeight );
    }
  }

  public static ParameterInfoPopup invoke( final GosuEditor gsEditor, int iPosition )
  {
    try
    {
      ParameterInfoPopup parameterInfoPopup = new ParameterInfoPopup( gsEditor );
      parameterInfoPopup.display( iPosition );

      EventQueue.invokeLater(
        () -> {
          gsEditor.getEditor().requestFocus();
          gsEditor.getEditor().repaint();
        } );
      return parameterInfoPopup;
    }
    catch( BadLocationException e )
    {
      editor.util.EditorUtilities.handleUncaughtException( e );
    }
    return null;
  }

  String getLabelAt( int index )
  {
    return ((JLabel)_labelContainer.getComponent( index )).getText();
  }

  class EditorCaretListener implements CaretListener
  {
    @Override
    public void caretUpdate( CaretEvent e )
    {
      EventQueue.invokeLater( () -> invoke( _editor, _editor.getEditor().getCaretPosition() ) );
    }
  }

  class EditorKeyListener extends KeyAdapter
  {
    @Override
    public void keyPressed( KeyEvent e )
    {
      if( e.getKeyCode() == KeyEvent.VK_ENTER )
      {
        setVisible( false );
        EventQueue.invokeLater( () -> invoke( _editor, _editor.getEditor().getCaretPosition() ) );
      }
      else if( e.getKeyCode() == KeyEvent.VK_ESCAPE )
      {
        setVisible( false );
        e.consume();
      }
    }
  }

  private static class ParameterInfoStub implements IParameterInfo
  {
    private final ISymbol _arg;

    public ParameterInfoStub( ISymbol arg )
    {
      _arg = arg;
    }

    @Override
    public String getName()
    {
      return _arg.getName();
    }

    @Override
    public IType getFeatureType()
    {
      return _arg.getType();
    }

    @Override
    public IFeatureInfo getContainer()
    {
      return null;
    }

    @Override
    public IType getOwnersType()
    {
      return null;
    }

    @Override
    public String getDisplayName()
    {
      return null;
    }

    @Override
    public String getDescription()
    {
      return null;
    }

  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy