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

editor.GosuEditor Maven / Gradle / Ivy

package editor;

import editor.undo.AtomicUndoManager;
import editor.util.EditorUtilities;
import editor.util.HTMLEscapeUtil;
import editor.util.IReplaceWordCallback;
import editor.util.PlatformUtil;
import editor.util.SettleModalEventQueue;
import editor.util.TaskQueue;
import editor.util.TextComponentUtil;
import editor.util.XPToolbarButton;
import editor.util.transform.java.JavaToGosu;
import gw.fs.IFile;
import gw.lang.GosuShop;
import gw.lang.parser.GosuParserFactory;
import gw.lang.parser.IDynamicFunctionSymbol;
import gw.lang.parser.IDynamicPropertySymbol;
import gw.lang.parser.IDynamicSymbol;
import gw.lang.parser.IExpression;
import gw.lang.parser.IFunctionSymbol;
import gw.lang.parser.IGosuParser;
import gw.lang.parser.IGosuProgramParser;
import gw.lang.parser.IGosuValidator;
import gw.lang.parser.IParseIssue;
import gw.lang.parser.IParseResult;
import gw.lang.parser.IParseTree;
import gw.lang.parser.IParsedElement;
import gw.lang.parser.IParsedElementWithAtLeastOneDeclaration;
import gw.lang.parser.IScriptPartId;
import gw.lang.parser.ISymbol;
import gw.lang.parser.ISymbolTable;
import gw.lang.parser.ITokenizerInstructor;
import gw.lang.parser.ITypeUsesMap;
import gw.lang.parser.Keyword;
import gw.lang.parser.ParserOptions;
import gw.lang.parser.ScriptPartId;
import gw.lang.parser.StandardSymbolTable;
import gw.lang.parser.exceptions.ParseException;
import gw.lang.parser.exceptions.ParseResultsException;
import gw.lang.parser.exceptions.ParseWarning;
import gw.lang.parser.expressions.IBeanMethodCallExpression;
import gw.lang.parser.expressions.IIdentifierExpression;
import gw.lang.parser.expressions.IImplicitTypeAsExpression;
import gw.lang.parser.expressions.IInferredNewExpression;
import gw.lang.parser.expressions.ILocalVarDeclaration;
import gw.lang.parser.expressions.IMemberAccessExpression;
import gw.lang.parser.expressions.IMethodCallExpression;
import gw.lang.parser.expressions.INewExpression;
import gw.lang.parser.expressions.ITypeLiteralExpression;
import gw.lang.parser.expressions.IVarStatement;
import gw.lang.parser.statements.IClassDeclaration;
import gw.lang.parser.statements.IClassFileStatement;
import gw.lang.parser.statements.IClassStatement;
import gw.lang.parser.statements.IForEachStatement;
import gw.lang.parser.statements.IFunctionStatement;
import gw.lang.parser.statements.IMethodCallStatement;
import gw.lang.parser.statements.IPropertyStatement;
import gw.lang.parser.statements.IStatementList;
import gw.lang.parser.template.ITemplateGenerator;
import gw.lang.reflect.FunctionType;
import gw.lang.reflect.IFeatureInfo;
import gw.lang.reflect.IMetaType;
import gw.lang.reflect.IScriptabilityModifier;
import gw.lang.reflect.IType;
import gw.lang.reflect.ITypeInfo;
import gw.lang.reflect.ITypeLoaderListener;
import gw.lang.reflect.ITypeRef;
import gw.lang.reflect.RefreshRequest;
import gw.lang.reflect.TypeSystem;
import gw.lang.reflect.gs.ClassType;
import gw.lang.reflect.gs.IGosuClass;
import gw.lang.reflect.gs.IGosuClassTypeInfo;
import gw.lang.reflect.gs.IGosuEnhancement;
import gw.lang.reflect.gs.IGosuMethodInfo;
import gw.lang.reflect.gs.IGosuPropertyInfo;
import gw.lang.reflect.gs.IGosuVarPropertyInfo;
import gw.lang.reflect.gs.StringSourceFileHandle;
import gw.lang.reflect.java.JavaTypes;
import gw.util.GosuStringUtil;

import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.UndoableEditListener;
import javax.swing.text.AbstractDocument;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.DocumentFilter;
import javax.swing.text.Element;
import javax.swing.text.Highlighter;
import javax.swing.text.JTextComponent;
import javax.swing.text.StyleConstants;
import javax.swing.undo.CompoundEdit;
import java.awt.*;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.InputEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static editor.util.EditorUtilities.handleUncaughtException;
import static editor.util.TextComponentUtil.Direction.BACKWARD;
import static editor.util.TextComponentUtil.Direction.FORWARD;

/**
 * A component for editing Gosu source.
 */
public class GosuEditor extends JPanel implements IScriptEditor, IGosuPanel, ITypeLoaderListener
{
  /**
   * Platform dependent keystroke info
   */
  public static final String CONTROL_KEY_NAME;
  public static final int CONTROL_KEY_MASK;

  static
  {
    if( PlatformUtil.isMac() )
    {
      CONTROL_KEY_MASK = KeyEvent.META_DOWN_MASK;
      CONTROL_KEY_NAME = "meta";
    }
    else
    {
      CONTROL_KEY_MASK = KeyEvent.CTRL_DOWN_MASK;
      CONTROL_KEY_NAME = "control";
    }
  }

  public static final String INTELLISENSE_TASK_QUEUE = "_intellisenseParser";

  /**
   * Parse result code for a valid parse.
   */
  public static final int RESCODE_VALID = 0;

  /**
   * Parse result code for a valid parse with warnings.
   */
  public static final int RESCODE_WARNINGS = 1;

  /**
   * Parse result code for an invalid parse.
   */
  public static final int RESCODE_ERRORS = 2;

  /**
   * Parse result code during parsing.
   */
  public static final int RESCODE_PENDING = 4;

  /**
   * Delay in millis for code completion to wait for key presses
   * before displaying.
   */
  static int COMPLETION_DELAY = 500;

  public static final int MIN_LINENUMBER_WIDTH = 16;

  /**
   * The number of spacess assigned to a tab
   */
  public static final int TAB_SIZE = 2;

  private JLabel _labelCaption;
  private GosuEditorFeedbackPanel _panelFeedback;
  private GosuEditorPane _editor;
  private GosuDocumentHandler _docHandler;
  private IContextMenuHandler _contextMenuHandler;
  private volatile IGosuParser _parser;
  private boolean _bStatement;
  private boolean _bProgram;
  private boolean _bClass;
  private boolean _bEnhancement;
  private boolean _bEmptyTextOk;
  private ISymbolTable _symTable;
  private UndoableEditListener _uel;
  BeanInfoPopup _beanInfoPopup;
  JPopupMenu _valuePopup;
  private XPToolbarButton _btnAdvice;
  private Runnable _adviceRunner;
  private IReplaceWordCallback _replaceWordCallback;
  private boolean _bTemplate;
  private boolean _bCompleteCode;
  private IScriptabilityModifier _scriptabilityModifier;
  private AtomicUndoManager _undoMgr;
  private IScriptPartId _partId;
  private EditorScrollPane _scroller;
  private boolean _bAltDown;
  private ParseResultsException _pe;
  boolean _bEnterPressedConsumed;
  private boolean _bTestResource;
  private boolean _bAcceptUses;
  private int _iTimerCount;
  private boolean _bParserSuspended;
  private IGosuClass _parsedGosuClass;
  private String _enhancedTypeName;
  private Map _functionStmtsByLineNumber;
  private volatile List _overriddenFunctions;

  private IGosuValidator _validator;
  private HighlightMode _highlightMode = HighlightMode.SEARCH;
  private IGosuParser.ParseType _parseType;
  private List _parseListeners = new ArrayList();

  private SmartFixManager _smartFixManager;
  private ContextHighlighter _ctxHighlighter;
  private DynamicSelectionManager _selectionManager;
  private CodeRefactorManager _codeManager;

  //This rectangle is used to signal that the editor is being used in test mode and that popups should therefore
  //not display
  private static final Rectangle TEST_RECTANGLE = new Rectangle( 0, 0, 0, 0 );

  private List _specialFunctions = new ArrayList();
  private Map _specialFunctionGotoDeclHandlers = new HashMap();
  private ITokenizerInstructor _tokenizerInstructor;

  private ITypeUsesMap _typeUsesMap;
  private ITypeUsesMap _typeUsesMapFromMostRecentParse;

  private JavadocPopup _javadocPopup;
  private AbstractPopup _spinnerPopup;

  private static TimerPool _timerPool = new TimerPool();
  private IType _programSuperType;

  private enum HighlightMode
  {
    SEARCH,
    USAGES,
  }

  public GosuEditor( ILineInfoManager lineInfoRenderer,
                     AtomicUndoManager undoMgr,
                     IScriptabilityModifier scriptabilityConstraint,
                     IContextMenuHandler contextMenuHandler,
                     boolean bStatement, boolean bEmptyTextOk )
  {
    _undoMgr = undoMgr;
    _contextMenuHandler = contextMenuHandler == null
                          ? new DefaultContextMenuHandler()
                          : contextMenuHandler;
    _bStatement = bStatement;
    _bEmptyTextOk = bEmptyTextOk;
    _bAcceptUses = true;
    _docHandler = new GosuDocumentHandler();
    _parser = null;
    _scriptabilityModifier = scriptabilityConstraint;

    _replaceWordCallback = new ReplaceWordCallback();
    _parseType = IGosuParser.ParseType.EXPRESSION_OR_PROGRAM;

    _ctxHighlighter = new ContextHighlighter( this );

    configureLayout( lineInfoRenderer );
    _smartFixManager = new SmartFixManager( this );
    _selectionManager = new DynamicSelectionManager( this );
    _codeManager = new CodeRefactorManager( this );
  }

  void configureLayout( ILineInfoManager lineInfoRenderer )
  {
    setBorder( UIManager.getBorder( "TextField.border" ) );
    setLayout( new BorderLayout() );

    _editor = createEditorPane();
    ToolTipManager.sharedInstance().registerComponent( _editor );
    ToolTipManager.sharedInstance().setDismissDelay( 60000 );
    ScrollableEditorRootPane editorRootPane = new ScrollableEditorRootPane( _editor );
    editorRootPane.setContentPane( _editor );
    editorRootPane.setBorder( null );

    GosuEditorKit kit = new GosuEditorKit();
    _editor.setEditorKitForContentType( "text/gosu", kit );
    _editor.setContentType( "text/gosu" );
    _editor.setMargin( new Insets( 3, 3, 3, 3 ) ); // set margin directly, otherwise some other platforms (cough, mac, cough) don't have a margin at all
    _editor.setFont( new Font( GosuEditorKit.getStylePreferences().getFontFamily(), Font.PLAIN,
                               GosuEditorKit.getStylePreferences().getFontSize() ) );
    _editor.setBackground( EditorUtilities.WINDOW );
    _editor.setForeground( Color.black );
    _editor.putClientProperty( "caretWidth", 2 );
    _editor.setCaretColor( StyleConstants.getForeground( kit.getViewFactory().getStyle( GosuStyleContext.STYLE_Caret ) ) );
    _editor.setEditable( true );
    _editor.addKeyListener( new EditorKeyHandler() );
    _editor.addMouseListener( new ScriptEditorPopupHandler( this, _contextMenuHandler ) );
    _editor.addMouseWheelListener( new ScriptEditorMouseWheelHandler( this ) );
    _editor.addCaretListener( new ErrorAtCaretHandler() );
    _editor.addCaretListener( _ctxHighlighter );
    _editor.addFocusListener( _ctxHighlighter );
    _editor.addFocusListener( new FocusAdapter()
      {
        @Override
        public void focusGained( FocusEvent e )
        {
          _smartFixManager.updateState();
        }
      } );
    TypeSystem.addTypeLoaderListenerAsWeakRef( this );
    MouseInEditorHandler mouseInEditorHandler = new MouseInEditorHandler( this );
    _editor.addMouseListener( mouseInEditorHandler );
    _editor.addMouseMotionListener( mouseInEditorHandler );
    _editor.addMouseListener( new MouseAdapter()
      {
        @Override
        public void mouseClicked( MouseEvent e )
        {
          setCompleteCode( false );
        }

        @Override
        public void mousePressed( MouseEvent e )
        {
          setCompleteCode( false );
        }

        @Override
        public void mouseReleased( MouseEvent e )
        {
          setCompleteCode( false );
        }
      } );

    addDocumentListener();

    addKeyHandlers();

    // Sets the editor's width such that lines won't word wrap
    EventQueue.invokeLater(
      new Runnable()
      {
        @Override
        public void run()
        {
          _editor.setSize( 1000, _editor.getHeight() );
        }
      } );

    _scroller = new EditorScrollPane( lineInfoRenderer, _editor, editorRootPane );
    _scroller.setBorder( null );
    JViewport vp = _scroller.getViewport();
    vp.setScrollMode( JViewport.BLIT_SCROLL_MODE );
    _btnAdvice = new XPToolbarButton( editor.util.EditorUtilities.loadIcon( "images/advice.png" ) );
    _btnAdvice.setToolTipText( "Display Smart Help" );
    _btnAdvice.setBorderConstant( true );
    _btnAdvice.addActionListener( new ActionListener()
    {
      @Override
      public void actionPerformed( ActionEvent e )
      {
        if( _adviceRunner != null )
        {
          _adviceRunner.run();
        }
      }
    } );
    _btnAdvice.setVisible( false );
    editorRootPane.getLayeredPane().add( _btnAdvice, JLayeredPane.PALETTE_LAYER );

    add( BorderLayout.CENTER, _scroller );

    _labelCaption = new JLabel( "