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( "