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

editor.GosuPanel Maven / Gradle / Ivy

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

import com.sun.jdi.Location;
import com.sun.jdi.VirtualMachine;
import editor.debugger.BreakpointManager;
import editor.debugger.DebugPanel;
import editor.debugger.Debugger;
import editor.run.IProcessRunner;
import editor.run.IRunConfig;
import editor.run.RunState;
import editor.search.SearchPanel;
import editor.shipit.ExperimentBuild;
import editor.shipit.ShipIt;
import editor.splitpane.CollapsibleSplitPane;
import editor.tabpane.ITab;
import editor.tabpane.TabPane;
import editor.tabpane.TabPosition;
import editor.undo.AtomicUndoManager;
import editor.util.BrowserUtil;
import editor.util.EditorUtilities;
import editor.util.Experiment;
import editor.util.GosuTextifier;
import editor.util.LabStatusBar;
import editor.util.LabToolbarButton;
import editor.util.LabelListPopup;
import gw.util.PathUtil;
import editor.util.SettleModalEventQueue;
import editor.util.SmartMenu;
import editor.util.SmartMenuItem;
import editor.util.SourceFileCreator;
import editor.util.ToolBar;
import editor.util.TypeNameUtil;
import gw.fs.IFile;
import gw.internal.ext.org.objectweb.asm.ClassReader;
import gw.internal.ext.org.objectweb.asm.util.TraceClassVisitor;
import gw.lang.Gosu;
import gw.lang.parser.IParseIssue;
import gw.lang.parser.IParseTree;
import gw.lang.parser.IParsedElement;
import gw.lang.parser.IScriptPartId;
import gw.lang.parser.ScriptPartId;
import gw.lang.parser.exceptions.ParseResultsException;
import gw.lang.parser.expressions.IBlockExpression;
import gw.lang.parser.resources.ResourceKey;
import gw.lang.parser.statements.IClassStatement;
import gw.lang.reflect.IType;
import gw.lang.reflect.ITypeRef;
import gw.lang.reflect.TypeSystem;
import gw.lang.reflect.gs.IGosuClass;
import gw.util.StreamUtil;

import java.io.Writer;
import java.nio.file.Path;
import java.io.File;
import java.util.Set;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.filechooser.FileFilter;
import javax.swing.text.AbstractDocument;
import javax.swing.text.BadLocationException;
import javax.swing.text.Element;
import javax.swing.undo.CompoundEdit;
import java.awt.*;
import java.awt.datatransfer.Clipboard;
import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;

/**
 */
public class GosuPanel extends JPanel
{
  private static final int MAX_TABS = 12;

  private SystemPanel _consolePanel;
  private CollapsibleSplitPane _outerSplitPane;
  private CollapsibleSplitPane _splitPane;
  private ExperimentView _experimentView;
  private JFrame _parentFrame;
  private RunState _runState;
  private TabPane _editorTabPane;
  private TabPane _bottomTabPane;
  private AtomicUndoManager _defaultUndoMgr;
  private NavigationHistory _history;
  private LabStatusBar _statusBar;
  private boolean _initialFile;
  private TypeNameCache _typeNamesCache;
  private Experiment _experiment;
  private OutputStreamWriter _inWriter;
  private SysInListener _sysInListener;
  private InputStream _oldIn;
  private MessagesPanel _messages;
  private SearchPanel _searches;
  private DebugPanel _debugPanel;
  private IProcessRunner _processRunner;
  private BreakpointManager _breakpointManager;
  private Debugger _debugger;

  public GosuPanel( JFrame frame )
  {
    _parentFrame = frame;
    _defaultUndoMgr = new AtomicUndoManager( 10 );
    _typeNamesCache = new TypeNameCache();
    _runState = RunState.None;
    _breakpointManager = new BreakpointManager();
    configUI();
  }

  public NavigationHistory getTabSelectionHistory()
  {
    return _history;
  }

  void configUI()
  {
    setLayout( new BorderLayout() );

    JPanel bottom = new JPanel( new BorderLayout() );
    _bottomTabPane = new TabPane( TabPane.MINIMIZABLE | TabPane.RESTORABLE | TabPane.TOP_BORDER_ONLY | TabPane.DYNAMIC );
    bottom.add( _bottomTabPane, BorderLayout.CENTER );

    _editorTabPane = new TabPane( TabPosition.TOP, TabPane.DYNAMIC | TabPane.MIN_MAX_REST );

    _history = new NavigationHistory( _editorTabPane );
    getTabSelectionHistory().setTabHistoryHandler( new EditorTabHistoryHandler() );

    _editorTabPane.addSelectionListener(
      e -> {
        if( !_editorTabPane.isVisible() )
        {
          // clearing tabs, don't save etc.
          return;
        }
        savePreviousTab();
        updateTitle();
        if( getCurrentEditor() == null )
        {
          return;
        }
        // Don't set focus here, otherwise it could be stealing it from somewhere that wants it after it instructed Lab to display an editor,
        // but wants to retain focus, like the debug panel when switching between stakc frames.
        //getCurrentEditor().getEditor().requestFocus();
        parse();
        storeExperimentState();
      } );

    _experimentView = new ExperimentView();
    _experimentView.setBackground( Scheme.active().getWindow() );
    TabPane experimentViewTabPane = new TabPane( TabPosition.TOP, TabPane.MINIMIZABLE | TabPane.RESTORABLE | TabPane.TOP_BORDER_ONLY );
    experimentViewTabPane.addTab( "Experiment", null, _experimentView );


    _splitPane = new CollapsibleSplitPane( SwingConstants.HORIZONTAL, experimentViewTabPane, _editorTabPane );
    _outerSplitPane = new CollapsibleSplitPane( SwingConstants.VERTICAL, _splitPane, bottom );

    add( _outerSplitPane, BorderLayout.CENTER );

    JPanel statPanel = makeStatusBar();
    add( statPanel, BorderLayout.SOUTH );

    ToolBar toolbar = makeMainToolbar();
    add( toolbar, BorderLayout.NORTH );

    JMenuBar menuBar = makeMenuBar();
    _parentFrame.setJMenuBar( menuBar );
    handleMacStuff();


    EventQueue.invokeLater( () -> _outerSplitPane.collapseBottom( _bottomTabPane ) );

    EventQueue.invokeLater( this::mapKeystrokes );
  }

  private ToolBar makeMainToolbar()
  {
    ToolBar toolbar = new ToolBar();
    toolbar.setDynamicBorder( BorderFactory.createCompoundBorder( BorderFactory.createMatteBorder( 1, 0, 0, 0, Scheme.active().getControlLigthShadow() ), BorderFactory.createEmptyBorder( 1, 1, 2, 1 ) ) );
    LabToolbarButton item;

    item = new LabToolbarButton( new CommonMenus.OpenProjectActionHandler() );
    toolbar.add( item );
    item = new LabToolbarButton( new CommonMenus.SaveActionHandler() );
    toolbar.add( item );

    toolbar.add( makeSeparator() );

    item = new LabToolbarButton( new CommonMenus.UndoActionHandler() );
    toolbar.add( item );
    item = new LabToolbarButton( new CommonMenus.RedoActionHandler() );
    toolbar.add( item );

    toolbar.add( makeSeparator() );

    item = new LabToolbarButton( new CommonMenus.CutActionHandler( this::getCurrentEditor ) );
    toolbar.add( item );
    item = new LabToolbarButton( new CommonMenus.CopyActionHandler( this::getCurrentEditor ) );
    toolbar.add( item );
    item = new LabToolbarButton( new CommonMenus.PasteActionHandler( this::getCurrentEditor ) );
    toolbar.add( item );

    toolbar.add( makeSeparator() );

    item = new LabToolbarButton( new CommonMenus.FindActionHandler( this::getCurrentEditor ) );
    toolbar.add( item );
    item = new LabToolbarButton( new CommonMenus.ReplaceActionHandler( this::getCurrentEditor ) );
    toolbar.add( item );

    toolbar.add( makeSeparator() );

    item = new LabToolbarButton( new CommonMenus.GoBackActionHandler() );
    toolbar.add( item );
    item = new LabToolbarButton( new CommonMenus.GoForwardActionHandler() );
    toolbar.add( item );

    toolbar.add( makeSeparator() );

    item = new LabToolbarButton( new CommonMenus.MakeActionHandler() );
    toolbar.add( item );

    toolbar.add( makeSeparator() );

    item = new LabToolbarButton( new CommonMenus.ClearAndRunActionHandler( this::getRunConfig ) );
    item.setToolTipSupplier( () -> {
      IRunConfig rc = getRunConfig();
      return rc == null ? "Run..." : "Run '" + rc.getName() + "'";
    } );
    toolbar.add( item );

    item = new LabToolbarButton( new CommonMenus.ClearAndDebugActionHandler( this::getRunConfig ) );
    item.setToolTipSupplier( () -> {
      IRunConfig rc = getRunConfig();
      return rc == null ? "Debug..." : "Debug '" + rc.getName() + "'";
    } );
    toolbar.add( item );

    toolbar.add( makeSeparator() );

    item = new LabToolbarButton( new CommonMenus.ShipItActionHandler() );
    toolbar.add( item );

    toolbar.add( makeSeparator() );

    item = new LabToolbarButton( new CommonMenus.SettingsActionHandler() );
    toolbar.add( item );

    return toolbar;
  }
  private JComponent makeSeparator()
  {
    JPanel separator = new JPanel( new BorderLayout() ) {
      @Override
      protected void paintComponent( Graphics g )
      {
        super.paintComponent( g );
        g.setColor( Scheme.active().getSeparator2() );
        g.drawLine( getWidth()/2, 0, getWidth()/2, getHeight()-1 );
      }
    };
    separator.setMaximumSize( new Dimension( 9, 20 ) );
    separator.setBackground( Scheme.active().getMenu() );
    return separator;
  }

  public ExperimentView getExperimentView()
  {
    return _experimentView;
  }

  public MessagesPanel getMessagesPanel()
  {
    return _messages;
  }

  public SearchPanel getSearchPanel()
  {
    return _searches;
  }
  
  public SystemPanel getConsolePanel()
  {
    return _consolePanel;
  }

  public DebugPanel getDebugPanel()
  {
    return _debugPanel;
  }

  public MessagesPanel showMessages( boolean bShow )
  {
    return _messages = showTab( bShow, "Messages", null, _messages, MessagesPanel::new );
  }

  public SearchPanel showSearches( boolean bShow )
  {
    return _searches = showTab( bShow, "Search", null, _searches, SearchPanel::new );
  }

  public SystemPanel showConsole( boolean bShow )
  {
    return _consolePanel = showTab( bShow, "Console", EditorUtilities.loadIcon( "images/console.png" ), _consolePanel, SystemPanel::new );
  }

  public 

P showTab( boolean bShow, String title, Icon icon, P panel, Supplier

creator ) { if( bShow ) { EventQueue.invokeLater( _outerSplitPane::restorePane ); ITab tab = _bottomTabPane.findTabWithContent( panel ); if( tab == null ) { panel = creator.get(); _bottomTabPane.addTab( title, icon, panel ); return panel; } else { _bottomTabPane.selectTab( tab, false ); return panel; } } else { _bottomTabPane.removeTabWithContent( panel ); EventQueue.invokeLater( () -> { SettleModalEventQueue.instance().run(); if( _bottomTabPane.getTabCount() == 0 && !_outerSplitPane.isMin() ) { _outerSplitPane.toggleCollapse( _bottomTabPane ); } } ); return null; } } private void handleMacStuff() { //## we are using a plaform independent LAF now with menubar on the frame where it belongs // if( PlatformUtil.isMac() ) // { // System.setProperty( "apple.laf.useScreenMenuBar", "true" ); // System.setProperty( "com.apple.mrj.application.apple.menu.about.name", "Gosu Editor" ); // } } public void clearTabs() { _editorTabPane.setVisible( false ); try { _editorTabPane.removeAllTabs(); } finally { _editorTabPane.setVisible( true ); } showMessages( false ); showSearches( false ); SettleModalEventQueue.instance().run(); getTabSelectionHistory().dispose(); } private void storeExperimentState() { if( _initialFile ) { return; } getExperiment().save(); LabFrame.instance().saveLabState( _experiment ); } public Experiment getExperiment() { return _experiment; } public void restoreExperimentState( Experiment experiment ) { _experiment = experiment; RunMe.reinitializeGosu( experiment ); TypeSystem.refresh( TypeSystem.getGlobalModule() ); LabFrame.instance().addExperiment( experiment ); _experimentView.load( _experiment ); for( String openFile : experiment.getOpenFiles() ) { Path file = PathUtil.create( openFile ); if( PathUtil.isFile( file ) ) { openFile( file, false ); } } String activeFile = experiment.getActiveFile(); if( activeFile == null ) { EventQueue.invokeLater( () -> SourceFileCreator.instance().getOrMakeUntitledProgram( experiment ) ); } else { openTab( PathUtil.create( activeFile ), true ); } SettleModalEventQueue.instance().run(); EventQueue.invokeLater( () -> { parse(); EditorHost currentEditor = getCurrentEditor(); if( currentEditor != null ) { currentEditor.getEditor().requestFocus(); } } ); } private JPanel makeStatusBar() { _statusBar = new LabStatusBar(); return _statusBar; } @SuppressWarnings("UnusedDeclaration") public void setStatus( String status ) { _statusBar.setStatus( status ); } private void parse() { EventQueue.invokeLater( () -> { if( getCurrentEditor() != null ) { getCurrentEditor().parse(); } } ); } private void savePreviousTab() { EditorHost editor = getTabSelectionHistory().getPreviousEditor(); if( editor != null ) { if( isDirty( editor ) ) { save( (Path)editor.getClientProperty( "_file" ), editor ); } else { if( editor.getParsedClass() != null ) { // Refresh the class so that it SourceFileHandle will have a non-null file, // otherwise the editor's transient string one will be there -- there is code // around that presumes all GosuClasses in tabs are also on disk TypeSystem.refresh( (ITypeRef)editor.getParsedClass() ); } } } } private EditorHost createEditor( Path file, IScriptPartId partId ) { EditorHost editorHost = EditorFactory.createEditor( file, partId ); editorHost.setBorder( BorderFactory.createEmptyBorder() ); addDirtyListener( editorHost ); EventQueue.invokeLater( () -> ((AbstractDocument)editorHost.getEditor().getDocument()).setDocumentFilter( new GosuPanelDocumentFilter( editorHost ) ) ); return editorHost; } private void addDirtyListener( final EditorHost editor ) { editor.getUndoManager().addChangeListener( new ChangeListener() { private ChangeEvent _lastChangeEvent; @Override public void stateChanged( ChangeEvent e ) { if( e != _lastChangeEvent ) { _lastChangeEvent = e; setDirty( editor, true ); } } } ); } private JMenuBar makeMenuBar() { JMenuBar menuBar = new JMenuBar(); makeFileMenu( menuBar ); makeEditMenu( menuBar ); makeSearchMenu( menuBar ); makeCodeMenu( menuBar ); makeBuildMenu( menuBar ); makeRunMenu( menuBar ); makeWindowMenu( menuBar ); makeHelpMenu( menuBar ); return menuBar; } private void makeHelpMenu( JMenuBar menuBar ) { JMenu helpMenu = new SmartMenu( "Help" ); helpMenu.setMnemonic( 'H' ); menuBar.add( helpMenu ); JMenuItem gosuItem = new SmartMenuItem( new AbstractAction( "Gosu Online" ) { @Override public void actionPerformed( ActionEvent e ) { BrowserUtil.openURL( "http://gosu-lang.org" ); } } ); gosuItem.setMnemonic( 'G' ); helpMenu.add( gosuItem ); helpMenu.addSeparator(); JMenuItem helpItem = new SmartMenuItem( new AbstractAction( "The Basics" ) { @Override public void actionPerformed( ActionEvent e ) { BrowserUtil.openURL( "http://gosu-lang.github.io/docs.html" ); } } ); helpItem.setMnemonic( 'B' ); helpMenu.add( helpItem ); helpMenu.addSeparator(); JMenuItem playItem = new SmartMenuItem( new AbstractAction( "Web Editor" ) { @Override public void actionPerformed( ActionEvent e ) { BrowserUtil.openURL( "http://gosu-lang.github.io/play.html" ); } } ); playItem.setMnemonic( 'W' ); helpMenu.add( playItem ); helpMenu.addSeparator(); JMenuItem discussItem = new SmartMenuItem( new AbstractAction( "Discuss" ) { @Override public void actionPerformed( ActionEvent e ) { BrowserUtil.openURL( "http://groups.google.com/group/gosu-lang" ); } } ); discussItem.setMnemonic( 'D' ); helpMenu.add( discussItem ); helpMenu.addSeparator(); JMenuItem plugin = new SmartMenuItem( new AbstractAction( "IntelliJ Plugin" ) { @Override public void actionPerformed( ActionEvent e ) { BrowserUtil.openURL( "http://gosu-lang.github.io/intellij.html" ); } } ); plugin.setMnemonic( 'I' ); helpMenu.add( plugin ); } private void makeWindowMenu( JMenuBar menuBar ) { JMenu windowMenu = new SmartMenu( "Window" ); windowMenu.setMnemonic( 'W' ); menuBar.add( windowMenu ); JMenuItem backItem = new SmartMenuItem( new CommonMenus.GoBackActionHandler() ); backItem.setMnemonic( 'B' ); backItem.setAccelerator( KeyStroke.getKeyStroke( "alt LEFT" ) ); windowMenu.add( backItem ); JMenuItem forwardItem = new SmartMenuItem( new CommonMenus.GoForwardActionHandler() ); forwardItem.setMnemonic( 'F' ); forwardItem.setAccelerator( KeyStroke.getKeyStroke( "alt RIGHT" ) ); windowMenu.add( forwardItem ); windowMenu.addSeparator(); JMenuItem recentItem = new SmartMenuItem( new AbstractAction( "Recent Files" ) { @Override public void actionPerformed( ActionEvent e ) { displayRecentViewsPopup(); } } ); recentItem.setMnemonic( 'R' ); recentItem.setAccelerator( KeyStroke.getKeyStroke( EditorUtilities.CONTROL_KEY_NAME + " E" ) ); windowMenu.add( recentItem ); windowMenu.addSeparator(); JMenuItem closeActiveItem = new SmartMenuItem( new AbstractAction( "Close Active Editor" ) { @Override public void actionPerformed( ActionEvent e ) { saveIfDirty(); closeActiveEditor(); } } ); closeActiveItem.setMnemonic( 'C' ); closeActiveItem.setAccelerator( KeyStroke.getKeyStroke( EditorUtilities.CONTROL_KEY_NAME + " F4" ) ); windowMenu.add( closeActiveItem ); JMenuItem closeOthersItem = new SmartMenuItem( new AbstractAction( "Close Others" ) { @Override public void actionPerformed( ActionEvent e ) { closeOthers(); } } ); closeOthersItem.setMnemonic( 'O' ); windowMenu.add( closeOthersItem ); } private void makeCodeMenu( JMenuBar menuBar ) { JMenu codeMenu = new SmartMenu( "Code" ); codeMenu.setMnemonic( 'd' ); menuBar.add( codeMenu ); codeMenu.add( CommonMenus.makeCodeComplete( this::getCurrentGosuEditor ) ); codeMenu.addSeparator(); codeMenu.add( CommonMenus.makeParameterInfo( this::getCurrentGosuEditor ) ); codeMenu.add( CommonMenus.makeExpressionType( this::getCurrentGosuEditor ) ); codeMenu.addSeparator(); codeMenu.add( CommonMenus.makeGotoDeclaration( this::getCurrentGosuEditor ) ); codeMenu.addSeparator(); codeMenu.add( CommonMenus.makeShowFileInTree( this::getCurrentEditor ) ); codeMenu.addSeparator(); codeMenu.add( CommonMenus.makeQuickDocumentation( this::getCurrentGosuEditor ) ); codeMenu.addSeparator(); JMenuItem openTypeItem = new SmartMenuItem( new AbstractAction( "Open Type..." ) { @Override public void actionPerformed( ActionEvent e ) { GotoTypePopup.display(); } } ); openTypeItem.setMnemonic( 'O' ); openTypeItem.setAccelerator( KeyStroke.getKeyStroke( EditorUtilities.CONTROL_KEY_NAME + " N" ) ); codeMenu.add( openTypeItem ); if( "true".equals( System.getProperty( "spec" ) ) ) { codeMenu.addSeparator(); JMenuItem markItem = new SmartMenuItem( new AbstractAction( "Mark Errors For Gosu Language Test" ) { @Override public void actionPerformed( ActionEvent e ) { markErrorsForGosuLanguageTest(); } } ); markItem.setMnemonic( 'M' ); markItem.setAccelerator( KeyStroke.getKeyStroke( EditorUtilities.CONTROL_KEY_NAME + " M" ) ); codeMenu.add( markItem ); } codeMenu.addSeparator(); JMenuItem viewBytecodeItem = new SmartMenuItem( new AbstractAction( "View Bytecode" ) { @Override public void actionPerformed( ActionEvent e ) { dumpBytecode(); } @Override public boolean isEnabled() { return getCurrentGosuEditor() != null && getCurrentEditor().getScriptPart() != null && getCurrentEditor().getScriptPart().getContainingType() != null; } } ); codeMenu.add( viewBytecodeItem ); } public GosuEditor getCurrentGosuEditor() { EditorHost editor = getCurrentEditor(); return editor instanceof GosuEditor ? (GosuEditor)editor : null; } private void makeBuildMenu( JMenuBar menuBar ) { JMenu buildMenu = new SmartMenu( "Build" ); buildMenu.setMnemonic( 'b' ); menuBar.add( buildMenu ); JMenuItem make = new SmartMenuItem( new CommonMenus.MakeActionHandler() ); make.setMnemonic( 'm' ); make.setAccelerator( KeyStroke.getKeyStroke( EditorUtilities.CONTROL_KEY_NAME + " F9" ) ); buildMenu.add( make ); JMenuItem rebuild = new SmartMenuItem( new CommonMenus.RebuildActionHandler() ); rebuild.setMnemonic( 'b' ); buildMenu.add( rebuild ); buildMenu.addSeparator(); JMenuItem shipIt = new SmartMenuItem( new CommonMenus.ShipItActionHandler() ); shipIt.setMnemonic( 'p' ); shipIt.setAccelerator( KeyStroke.getKeyStroke( EditorUtilities.CONTROL_KEY_NAME + " F10" ) ); buildMenu.add( shipIt ); } public EditorHost getCurrentEditor() { ITab selectedTab = _editorTabPane.getSelectedTab(); return selectedTab == null ? null : (EditorHost)selectedTab.getContentPane(); } public IRunConfig getRunConfig() { // Get the current editor's type IType type = getCurrentEditor() == null ? null : getCurrentEditor().getScriptPart() == null ? null : getCurrentEditor().getScriptPart().getContainingType(); if( !EditorUtilities.isRunnable( type ) ) { type = null; // The current type is not runnable, use the most recently run type IRunConfig mruRunConfig = getExperiment() == null ? null : getExperiment().getMruRunConfig(); if( mruRunConfig != null && mruRunConfig.isValid() ) { return mruRunConfig; } } return type == null ? null : getExperiment() == null ? null : getExperiment().getOrCreateRunConfig( type ) ; } private void makeRunMenu( JMenuBar menuBar ) { JMenu runMenu = new SmartMenu( "Run" ); runMenu.setMnemonic( 'R' ); menuBar.add( runMenu ); runMenu.add( CommonMenus.makeRun( this::getRunConfig ) ); runMenu.add( CommonMenus.makeDebug( this::getRunConfig ) ); runMenu.add( CommonMenus.makeRunConfig() ); runMenu.add( CommonMenus.makeDebugConfig() ); runMenu.addSeparator(); runMenu.add( CommonMenus.makeStop( () -> this ) ); runMenu.addSeparator(); runMenu.add( CommonMenus.makeStepOver( this::getDebugger ) ); runMenu.add( CommonMenus.makeStepInto( this::getDebugger ) ); runMenu.add( CommonMenus.makeStepOut( this::getDebugger ) ); runMenu.add( CommonMenus.makeRunToCursor( this::getDebugger, this::getBreakpointManager, this::getCurrentGosuEditor ) ); //noinspection Convert2MethodRef runMenu.add( CommonMenus.makeDropFrame( this::getDebugger, () -> _debugPanel.getDropToFrame() ) ); runMenu.add( CommonMenus.makePause( this::getDebugger ) ); runMenu.add( CommonMenus.makeResume( this::getDebugger ) ); runMenu.addSeparator(); runMenu.add( CommonMenus.makeEvaluateExpression( this::getDebugger ) ); runMenu.add( CommonMenus.makeShowExecutionPoint( this::getDebugger ) ); runMenu.addSeparator(); runMenu.add( CommonMenus.makeToggleBreakpoint( this::getBreakpointManager, this::getCurrentGosuEditor ) ); runMenu.add( CommonMenus.makeViewBreakpoints( () -> null ) ); runMenu.add( CommonMenus.makeMuteBreakpoints( this::getBreakpointManager ) ); runMenu.addSeparator(); runMenu.add( CommonMenus.makeClear( () -> this ) ); } private void makeSearchMenu( JMenuBar menuBar ) { JMenu searchMenu = new SmartMenu( "Search" ); searchMenu.setMnemonic( 'S' ); menuBar.add( searchMenu ); JMenuItem findItem = new SmartMenuItem( new CommonMenus.FindActionHandler( this::getCurrentEditor ) ); findItem.setMnemonic( 'F' ); findItem.setAccelerator( KeyStroke.getKeyStroke( EditorUtilities.CONTROL_KEY_NAME + " F" ) ); searchMenu.add( findItem ); JMenuItem replaceItem = new SmartMenuItem( new CommonMenus.ReplaceActionHandler( this::getCurrentEditor ) ); replaceItem.setMnemonic( 'R' ); replaceItem.setAccelerator( KeyStroke.getKeyStroke( EditorUtilities.CONTROL_KEY_NAME + " R" ) ); searchMenu.add( replaceItem ); JMenuItem nextItem = new SmartMenuItem( new AbstractAction( "Next" ) { @Override public void actionPerformed( ActionEvent e ) { if( isEnabled() ) { getCurrentEditor().gotoNextUsageHighlight(); } } @Override public boolean isEnabled() { return getCurrentEditor() != null && getCurrentEditor().getEditor().getHighlighter().getHighlights().length > 0; } } ); nextItem.setMnemonic( 'N' ); nextItem.setAccelerator( KeyStroke.getKeyStroke( "F3" ) ); searchMenu.add( nextItem ); JMenuItem previousItem = new SmartMenuItem( new AbstractAction( "Previous" ) { @Override public void actionPerformed( ActionEvent e ) { if( isEnabled() ) { getCurrentEditor().gotoPrevUsageHighlight(); } } @Override public boolean isEnabled() { return getCurrentEditor() != null && getCurrentEditor().getEditor().getHighlighter().getHighlights().length > 0; } } ); previousItem.setMnemonic( 'P' ); previousItem.setAccelerator( KeyStroke.getKeyStroke( "shift F3" ) ); searchMenu.add( previousItem ); searchMenu.addSeparator(); JMenuItem findIInPathItem = new SmartMenuItem( new CommonMenus.FindInPathActionHandler( FileTreeUtil::getRoot ) ); findIInPathItem.setMnemonic( 'P' ); findIInPathItem.setAccelerator( KeyStroke.getKeyStroke( EditorUtilities.CONTROL_KEY_NAME + " shift F" ) ); searchMenu.add( findIInPathItem ); JMenuItem replaceInPathItem = new SmartMenuItem( new CommonMenus.ReplaceInPathActionHandler( FileTreeUtil::getRoot ) ); replaceInPathItem.setMnemonic( 'A' ); replaceInPathItem.setAccelerator( KeyStroke.getKeyStroke( EditorUtilities.CONTROL_KEY_NAME + " shift R" ) ); searchMenu.add( replaceInPathItem ); searchMenu.addSeparator(); searchMenu.add( CommonMenus.makeFindUsages( FileTreeUtil::getRoot ) ); searchMenu.add( CommonMenus.makeFindUsagesInFile() ); searchMenu.add( CommonMenus.makeHighlightFindUsagesInFile() ); searchMenu.addSeparator(); searchMenu.add( CommonMenus.makePrevOccurrent( () -> getGosuPanel() == null ? null : getGosuPanel().getSearchPanel() ) ); searchMenu.add( CommonMenus.makeNextOccurrent( () -> getGosuPanel() == null ? null : getGosuPanel().getSearchPanel() ) ); searchMenu.addSeparator(); JMenuItem gotoLineItem = new SmartMenuItem( new AbstractAction( "Go To Line" ) { @Override public void actionPerformed( ActionEvent e ) { getCurrentEditor().displayGotoLinePopup(); } } ); gotoLineItem.setMnemonic( 'G' ); gotoLineItem.setAccelerator( KeyStroke.getKeyStroke( EditorUtilities.CONTROL_KEY_NAME + " G" ) ); searchMenu.add( gotoLineItem ); } private GosuPanel getGosuPanel() { return LabFrame.instance().getGosuPanel(); } private void makeEditMenu( JMenuBar menuBar ) { JMenu editMenu = new SmartMenu( "Edit" ); editMenu.setMnemonic( 'E' ); menuBar.add( editMenu ); JMenuItem undoItem = new SmartMenuItem( new CommonMenus.UndoActionHandler() ); undoItem.setMnemonic( 'U' ); undoItem.setAccelerator( KeyStroke.getKeyStroke( EditorUtilities.CONTROL_KEY_NAME + " Z" ) ); editMenu.add( undoItem ); JMenuItem redoItem = new SmartMenuItem( new CommonMenus.RedoActionHandler() ); redoItem.setMnemonic( 'R' ); redoItem.setAccelerator( KeyStroke.getKeyStroke( EditorUtilities.CONTROL_KEY_NAME + " shift Z" ) ); editMenu.add( redoItem ); editMenu.addSeparator(); editMenu.add( CommonMenus.makeCut( this::getCurrentEditor ) ); editMenu.add( CommonMenus.makeCopy( this::getCurrentEditor ) ); editMenu.add( CommonMenus.makePaste( this::getCurrentEditor ) ); editMenu.add( CommonMenus.makePasteJavaAsGosu( this::getCurrentGosuEditor ) ); editMenu.addSeparator(); JMenuItem deleteItem = new SmartMenuItem( new AbstractAction( "Delete" ) { @Override public void actionPerformed( ActionEvent e ) { if( getCurrentEditor() != null && EditorUtilities.containsFocus( getCurrentEditor() ) ) { getCurrentEditor().delete(); } } } ); deleteItem.setMnemonic( 'D' ); deleteItem.setAccelerator( KeyStroke.getKeyStroke( "DELETE" ) ); editMenu.add( deleteItem ); JMenuItem deletewordItem = new SmartMenuItem( new AbstractAction( "Delete Word" ) { @Override public void actionPerformed( ActionEvent e ) { getCurrentEditor().deleteWord(); } } ); deletewordItem.setMnemonic( 'e' ); deletewordItem.setAccelerator( KeyStroke.getKeyStroke( EditorUtilities.CONTROL_KEY_NAME + " BACKSPACE" ) ); editMenu.add( deletewordItem ); JMenuItem deleteWordForwardItem = new SmartMenuItem( new AbstractAction( "Delete Word Forward" ) { @Override public void actionPerformed( ActionEvent e ) { getCurrentEditor().deleteWordForwards(); } } ); deleteWordForwardItem.setMnemonic( 'F' ); deleteWordForwardItem.setAccelerator( KeyStroke.getKeyStroke( EditorUtilities.CONTROL_KEY_NAME + " DELETE" ) ); editMenu.add( deleteWordForwardItem ); JMenuItem deleteLine = new SmartMenuItem( new AbstractAction( "Delete Line" ) { @Override public void actionPerformed( ActionEvent e ) { getCurrentEditor().deleteLine(); } } ); deleteLine.setMnemonic( 'L' ); deleteLine.setAccelerator( KeyStroke.getKeyStroke( EditorUtilities.CONTROL_KEY_NAME + " Y" ) ); editMenu.add( deleteLine ); editMenu.addSeparator(); JMenuItem selectWord = new SmartMenuItem( new AbstractAction( "Select Word" ) { @Override public void actionPerformed( ActionEvent e ) { getCurrentGosuEditor().selectWord(); } @Override public boolean isEnabled() { return getCurrentGosuEditor() != null; } } ); selectWord.setMnemonic( 'W' ); selectWord.setAccelerator( KeyStroke.getKeyStroke( EditorUtilities.CONTROL_KEY_NAME + " W" ) ); editMenu.add( selectWord ); JMenuItem narraowSelection = new SmartMenuItem( new AbstractAction( "Narrow Selection" ) { @Override public void actionPerformed( ActionEvent e ) { getCurrentGosuEditor().narrowSelectWord(); } @Override public boolean isEnabled() { return getCurrentGosuEditor() != null; } } ); narraowSelection.setMnemonic( 'N' ); narraowSelection.setAccelerator( KeyStroke.getKeyStroke( EditorUtilities.CONTROL_KEY_NAME + " shift W" ) ); editMenu.add( narraowSelection ); editMenu.addSeparator(); JMenuItem duplicateItem = new SmartMenuItem( new AbstractAction( "Duplicate" ) { @Override public void actionPerformed( ActionEvent e ) { getCurrentEditor().duplicate(); } } ); duplicateItem.setAccelerator( KeyStroke.getKeyStroke( EditorUtilities.CONTROL_KEY_NAME + " D" ) ); editMenu.add( duplicateItem ); JMenuItem joinItem = new SmartMenuItem( new AbstractAction( "Join Lines" ) { @Override public void actionPerformed( ActionEvent e ) { getCurrentEditor().joinLines(); } } ); joinItem.setAccelerator( KeyStroke.getKeyStroke( EditorUtilities.CONTROL_KEY_NAME + " J" ) ); editMenu.add( joinItem ); JMenuItem indentItem = new SmartMenuItem( new AbstractAction( "Indent Selection" ) { @Override public void actionPerformed( ActionEvent e ) { if( !getCurrentEditor().isCompletionPopupShowing() ) { getCurrentEditor().handleBulkIndent( false ); } } } ); indentItem.setMnemonic( 'I' ); indentItem.setAccelerator( KeyStroke.getKeyStroke( "TAB" ) ); editMenu.add( indentItem ); JMenuItem outdentItem = new SmartMenuItem( new AbstractAction( "Outdent Selection" ) { @Override public void actionPerformed( ActionEvent e ) { if( !getCurrentEditor().isCompletionPopupShowing() ) { getCurrentEditor().handleBulkIndent( true ); } } } ); outdentItem.setMnemonic( 'O' ); outdentItem.setAccelerator( KeyStroke.getKeyStroke( "shift TAB" ) ); editMenu.add( outdentItem ); } private void makeFileMenu( JMenuBar menuBar ) { JMenu fileMenu = new SmartMenu( "File" ); fileMenu.setMnemonic( 'F' ); menuBar.add( fileMenu ); JMenuItem newExperimentItem = new SmartMenuItem( new AbstractAction( "New Experiment..." ) { @Override public void actionPerformed( ActionEvent e ) { newExperiment(); } } ); newExperimentItem.setMnemonic( 'P' ); fileMenu.add( newExperimentItem ); JMenuItem openExperimentItem = new SmartMenuItem( new CommonMenus.OpenProjectActionHandler() ); openExperimentItem.setMnemonic( 'O' ); fileMenu.add( openExperimentItem ); fileMenu.addSeparator(); JMenu reopenExperiment = new SmartMenu( "Reopen Experiment" ); ReopenExperimentPopup.initialize( reopenExperiment ); fileMenu.add( reopenExperiment ); fileMenu.addSeparator(); JMenu newItem = new SmartMenu( "New" ); NewFilePopup.addMenuItems( newItem ); fileMenu.add( newItem ); JMenuItem openItem = new SmartMenuItem( new AbstractAction( "Open..." ) { @Override public void actionPerformed( ActionEvent e ) { openFile(); } } ); openItem.setMnemonic( 'O' ); fileMenu.add( openItem ); JMenuItem saveItem = new SmartMenuItem( new CommonMenus.SaveActionHandler() ); saveItem.setMnemonic( 'S' ); saveItem.setAccelerator( KeyStroke.getKeyStroke( EditorUtilities.CONTROL_KEY_NAME + " S" ) ); fileMenu.add( saveItem ); fileMenu.addSeparator(); JMenuItem settings = new SmartMenuItem( new CommonMenus.SettingsActionHandler() ) ; saveItem.setMnemonic( 'G' ); saveItem.setAccelerator( KeyStroke.getKeyStroke( EditorUtilities.CONTROL_KEY_NAME + " alt S" ) ); fileMenu.add( settings ); JMenuItem classpathItem = new SmartMenuItem( new AbstractAction( "Dependencies..." ) { @Override public void actionPerformed( ActionEvent e ) { displayClasspath(); } } ); classpathItem.setMnemonic( 'd' ); fileMenu.add( classpathItem ); fileMenu.addSeparator(); JMenuItem exitItem = new SmartMenuItem( new AbstractAction( "Exit" ) { @Override public void actionPerformed( ActionEvent e ) { exit(); } } ); exitItem.setMnemonic( 'x' ); fileMenu.add( exitItem ); } private void closeActiveEditor() { if( _editorTabPane.getTabCount() > 1 ) { _editorTabPane.removeTab( _editorTabPane.getSelectedTab() ); } else { exit(); } } private void closeOthers() { _editorTabPane.setVisible( false ); try { for( int i = 0; i < _editorTabPane.getTabCount(); i++ ) { if( _editorTabPane.getSelectedTabIndex() != i ) { _editorTabPane.removeTab( _editorTabPane.getTabAt( i ) ); } } } finally { _editorTabPane.setVisible( true ); } } private void displayClasspath() { ClasspathDialog dlg = new ClasspathDialog( new File( "." ) ); dlg.setVisible( true ); } public void shipIt() { ShipIt.instance().shipIt( getExperiment() ); } @SuppressWarnings("ThrowableResultOfMethodCallIgnored") public boolean make() { SettleModalEventQueue.instance().run(); saveIfDirty(); if( getMessagesPanel() != null ) { getMessagesPanel().clear(); } showMessages( true ); //## NOTE: We distinguish between making during a debug session and not. This is primarily for the //## case where the user is running from source (NOT compiling bytecode to disk), in which case //## we only want to compile and reload classes that have changed since the debugger started. Debugger debugger = getDebugger(); ExperimentBuild expBuild = debugger != null ? debugger.getClassRedefiner() : ExperimentBuild.instance(); return expBuild.make( c -> true ); } public boolean compile( Set types ) { SettleModalEventQueue.instance().run(); saveIfDirty(); if( getMessagesPanel() != null ) { getMessagesPanel().clear(); } showMessages( true ); //## NOTE: We distinguish between making during a debug session and not. This is primarily for the //## case where the user is running from source (NOT compiling bytecode to disk), in which case //## we only want to compile and reload classes that have changed since the debugger started. Debugger debugger = getDebugger(); ExperimentBuild expBuild = debugger != null ? debugger.getClassRedefiner() : ExperimentBuild.instance(); return expBuild.compile( c -> true, types ); } public boolean rebuild() { SettleModalEventQueue.instance().run(); saveIfDirty(); if( getMessagesPanel() != null ) { getMessagesPanel().clear(); } showMessages( true ); return ExperimentBuild.instance().rebuild( c -> true ); } public void exit() { if( saveIfDirty() ) { System.exit( 0 ); } } public void setEditorSplitPosition( int iPos ) { if( _splitPane != null ) { _splitPane.setPosition( iPos ); } } public void setExperimentSplitPosition( int iPos ) { if( _outerSplitPane != null ) { _outerSplitPane.setPosition( iPos ); } } public EditorHost getGosuEditor() { return getCurrentEditor(); } /** * */ private void mapKeystrokes() { // Undo/Redo mapKeystroke( KeyStroke.getKeyStroke( KeyEvent.VK_Z, EditorUtilities.CONTROL_KEY_MASK ), "Undo", new UndoActionHandler() ); mapKeystroke( KeyStroke.getKeyStroke( KeyEvent.VK_Z, EditorUtilities.CONTROL_KEY_MASK | InputEvent.SHIFT_MASK ), "Redo", new RedoActionHandler() ); //## conflicts with Delete Line, which is also ctrl+y (same as IJ) // mapKeystroke( KeyStroke.getKeyStroke( KeyEvent.VK_Y, EditorUtilities.CONTROL_KEY_MASK ), // "Redo2", new RedoActionHandler() ); // Old-style undo/redo mapKeystroke( KeyStroke.getKeyStroke( KeyEvent.VK_BACK_SPACE, InputEvent.ALT_MASK ), "UndoOldStyle", new UndoActionHandler() ); mapKeystroke( KeyStroke.getKeyStroke( KeyEvent.VK_BACK_SPACE, InputEvent.ALT_MASK | InputEvent.SHIFT_MASK ), "RetoOldStyle", new RedoActionHandler() ); } private void mapKeystroke( KeyStroke ks, String strCmd, Action action ) { enableInputMethods( true ); enableEvents( AWTEvent.KEY_EVENT_MASK ); InputMap imap = getInputMap( JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT ); Object key = imap.get( ks ); if( key == null ) { key = strCmd; imap.put( ks, key ); } getActionMap().put( key, action ); } void resetChangeHandler() { ScriptChangeHandler handler = new ScriptChangeHandler( getUndoManager() ); handler.establishUndoableEditListener( getCurrentEditor() ); } public void openFile() { JFileChooser fc = new JFileChooser( getCurrentFile().getParent().toFile() ); fc.setDialogTitle( "Open Gosu Path" ); fc.setDialogType( JFileChooser.OPEN_DIALOG ); fc.setCurrentDirectory( getCurrentFile().getParent().toFile() ); fc.setFileFilter( new FileFilter() { public boolean accept( File f ) { return f.isDirectory() || isValidGosuSourceFile( f.toPath() ); } public String getDescription() { return "Gosu source file (*.gsp; *.gs; *.gsx; *.gst)"; } } ); int returnVal = fc.showOpenDialog( editor.util.EditorUtilities.frameForComponent( this ) ); if( returnVal == JFileChooser.APPROVE_OPTION ) { openFile( fc.getSelectedFile().toPath(), true ); } } public void openFile( Path file, boolean bFocus ) { openFile( makePartId( file ), file, bFocus ); } public boolean openType( String fqn, boolean bFocus ) { FileTree fileTree = FileTreeUtil.find( fqn ); if( fileTree != null ) { openFile( fileTree.getFileOrDir(), bFocus ); return true; } return false; } public static IScriptPartId makePartId( Path file ) { TypeSystem.pushGlobalModule(); try { if( file == null ) { return new ScriptPartId( "New Program", null ); } String classNameForFile = TypeNameUtil.getTypeNameForFile( file ); return new ScriptPartId( classNameForFile, null ); } finally { TypeSystem.popGlobalModule(); } } public void openInitialFile( IScriptPartId partId, Path file ) { _initialFile = true; try { if( file != null || _editorTabPane.getTabCount() == 0 ) { openFile( partId, file, true ); } } finally { _initialFile = false; } } private void openFile( IScriptPartId partId, Path file, boolean bFocus ) { if( openTab( file, bFocus ) ) { return; } final EditorHost editor = createEditor( file, partId ); if( partId == null ) { throw new IllegalArgumentException( "partId should be non-null" ); } file = file == null ? SourceFileCreator.instance().getOrMakeUntitledProgram( _experiment ) : file; editor.putClientProperty( "_file", file ); removeLruTab(); String classNameForFile = TypeNameUtil.getTypeNameForFile( file ); IType type = TypeSystem.getByFullNameIfValid( classNameForFile ); if( type != null ) { _editorTabPane.addTab( type.getRelativeName(), EditorUtilities.findIcon( type ), editor ); } else { _editorTabPane.addTab( PathUtil.getName( file ), EditorUtilities.findIcon( file ), editor ); } _editorTabPane.selectTab( _editorTabPane.findTabWithContent( editor ), true ); String strSource; if( !PathUtil.exists( file ) ) { strSource = ""; } else { try( InputStream in = PathUtil.createInputStream( file ) ) { strSource = StreamUtil.getContent( StreamUtil.getInputStreamReader( in ) ); } catch( IOException e ) { throw new RuntimeException( e ); } } if( _parentFrame != null ) { updateTitle(); } try { editor.read( partId, strSource ); resetChangeHandler(); if( bFocus ) { EventQueue.invokeLater( () -> editor.getEditor().requestFocus() ); } } catch( Throwable t ) { throw new RuntimeException( t ); } } private void removeLruTab() { if( _editorTabPane.getTabCount() < MAX_TABS ) { return; } List mruList = getTabSelectionHistory().getTabMruList(); for( int i = mruList.size() - 1; i >= 0; i-- ) { ITabHistoryContext tabCtx = mruList.get( i ); Path file = (Path)tabCtx.getContentId(); EditorHost editor = findTab( file ); if( editor != null ) { closeTab( file ); } } } private void updateTitle() { Path file = getCurrentFile(); Experiment experiment = getExperiment(); String currentFilePath = file == null ? " " : " - ..." + File.separator + experiment.makeExperimentRelativePath( file ) + " - "; String title = experiment.getName() + " - [" + PathUtil.getAbsolutePathName( experiment.getExperimentDir() ) + "]" + currentFilePath + "Gosu Lab " + Gosu.getVersion(); _parentFrame.setTitle( title ); } private boolean openTab( Path file, boolean bFocus ) { EditorHost editor = findTab( file ); if( editor != null ) { _editorTabPane.selectTab( _editorTabPane.findTabWithContent( editor ), bFocus ); return true; } return false; } public EditorHost findTab( Path file ) { if( file == null ) { return null; } for( int i = 0; i < _editorTabPane.getTabCount(); i++ ) { EditorHost editor = (EditorHost)_editorTabPane.getTabAt( i ).getContentPane(); if( editor != null && file.equals( editor.getClientProperty( "_file" ) ) ) { return editor; } } return null; } private void setCurrentFile( Path file ) { getCurrentEditor().putClientProperty( "_file", file ); openFile( file, false ); } public Path getCurrentFile() { EditorHost currentEditor = getCurrentEditor(); return currentEditor == null ? null : (Path)currentEditor.getClientProperty( "_file" ); } public boolean save() { if( getCurrentFile() == null ) { JFileChooser fc = new JFileChooser(); fc.setDialogTitle( "Save Gosu Path" ); fc.setDialogType( JFileChooser.SAVE_DIALOG ); fc.setCurrentDirectory( new File( "." ) ); fc.setFileFilter( new FileFilter() { public boolean accept( File f ) { return f.isDirectory() || isValidGosuSourceFile( f.toPath() ); } public String getDescription() { return "Gosu source file (*.gsp; *.gs; *.gsx; *.gst)"; } } ); int returnVal = fc.showOpenDialog( editor.util.EditorUtilities.frameForComponent( this ) ); if( returnVal == JFileChooser.APPROVE_OPTION ) { setCurrentFile( fc.getSelectedFile().toPath() ); } else { return false; } } if( !PathUtil.exists( getCurrentFile() ) ) { String msg = ""; if( !PathUtil.createNewFile( getCurrentFile() ) ) { JOptionPane.showMessageDialog( this, "Could not create file " + PathUtil.getName( getCurrentFile() ) + msg ); return false; } } saveAndReloadType( getCurrentFile(), getCurrentEditor() ); return true; } public boolean save( Path file, EditorHost editor ) { if( !PathUtil.exists( file ) ) { String msg = ""; if( !PathUtil.createNewFile( file ) ) { JOptionPane.showMessageDialog( this, "Could not create file " + PathUtil.getName( file ) + msg ); return false; } } saveAndReloadType( file, editor ); return true; } private void saveAndReloadType( Path file, EditorHost editor ) { if( !PathUtil.canWrite( file ) ) { return; } FileTree fileTree = FileTreeUtil.getRoot().find( file ); if( fileTree != null ) { fileTree.setLastModified(); } try( Writer out = PathUtil.createWriter( file ) ) { StreamUtil.copy( new StringReader( editor.getText() ), out ); setDirty( editor, false ); reload( editor.getScriptPart().getContainingType() ); } catch( IOException ex ) { throw new RuntimeException( ex ); } } void reload( IType type ) { if( type == null ) { return; } for( IFile file: type.getSourceFiles() ) { // This is more thorough re related files than refresh( type ) TypeSystem.refreshed( file ); } } public boolean saveIfDirty() { if( isDirty( getCurrentEditor() ) ) { return save(); } return true; } /** * This should only be called when either the file's contents change externally, * or when the file saves to disk. */ public void refresh( Path file ) { EditorHost editor = findTab( file ); if( editor != null ) { // The file is open in an editor, refresh it with the contents of the file try( Reader reader = PathUtil.createReader( file ) ) { editor.refresh( StreamUtil.getContent( reader ) ); setDirty( editor, false ); } catch( IOException e ) { throw new RuntimeException( e ); } } FileTree root = FileTreeUtil.getRoot(); FileTree node = root.find( file ); if( node != null ) { // Refresh the type system to include the changes IType type = node.getType(); if( type != null ) { reload( type ); } } } public void newExperiment() { Path untitled = PathUtil.create( getExperiment().getExperimentDir().getParent(), "Untitled" ); //noinspection ResultOfMethodCallIgnored PathUtil.mkdirs( untitled ); JFileChooser fc = new JFileChooser( untitled.toFile() ); fc.setDialogTitle( "New Experiment" ); fc.setDialogType( JFileChooser.OPEN_DIALOG ); fc.setFileSelectionMode( JFileChooser.DIRECTORIES_ONLY ); fc.setMultiSelectionEnabled( false ); fc.setFileFilter( new FileFilter() { public boolean accept( File f ) { return !new File( f, f.getName() + ".prj" ).exists(); } public String getDescription() { return "Gosu Experiment Directory (directory name is your experiment name)"; } } ); int returnVal = fc.showOpenDialog( editor.util.EditorUtilities.frameForComponent( this ) ); if( returnVal != JFileChooser.APPROVE_OPTION ) { return; } Path selectedFile = fc.getSelectedFile().toPath(); Experiment experiment = new Experiment( PathUtil.getName( selectedFile ), selectedFile, this ); clearTabs(); EventQueue.invokeLater( () -> restoreExperimentState( experiment ) ); } public void openExperiment() { FileDialog fc = new FileDialog( EditorUtilities.frameForComponent( this ), "Open Experiment", FileDialog.LOAD ); fc.setDirectory( PathUtil.getAbsolutePathName( getExperiment().getExperimentDir() ) ); fc.setMultipleMode( false ); fc.setFile( "*.prj" ); fc.setVisible( true ); String selectedFile = fc.getFile(); if( selectedFile != null ) { Path prjFile = PathUtil.create( fc.getDirectory(), selectedFile ); if( PathUtil.isFile( prjFile ) ) { Path experimentDir = prjFile.getParent(); openExperiment( experimentDir ); } } } public void openExperiment( Path experimentDir ) { storeExperimentState(); clearTabs(); EventQueue.invokeLater( () -> restoreExperimentState( new Experiment( experimentDir, this ) ) ); } private boolean isValidGosuSourceFile( Path file ) { if( file == null ) { return false; } String strName = PathUtil.getName( file ).toLowerCase(); return strName.endsWith( ".gs" ) || strName.endsWith( ".gsx" ) || strName.endsWith( ".gst" ) || strName.endsWith( ".gsp" ); } public void saveAs() { JFileChooser fc = new JFileChooser( getCurrentFile().toFile() ); fc.setDialogTitle( "Save Gosu Path" ); fc.setDialogType( JFileChooser.SAVE_DIALOG ); fc.setCurrentDirectory( getCurrentFile() != null ? getCurrentFile().getParent().toFile() : PathUtil.create( "." ).toFile() ); fc.setFileFilter( new FileFilter() { public boolean accept( File f ) { return f.isDirectory() || isValidGosuSourceFile( f.toPath() ); } public String getDescription() { return "Gosu source file (*.gsp; *.gs; *.gsx; *.gst)"; } } ); int returnVal = fc.showOpenDialog( editor.util.EditorUtilities.frameForComponent( this ) ); if( returnVal == JFileChooser.APPROVE_OPTION ) { setCurrentFile( fc.getSelectedFile().toPath() ); save(); } } public void dumpBytecode() { saveAndReloadType( getCurrentFile(), getCurrentEditor() ); showConsole( true ); clearOutput(); byte[] bytes = TypeSystem.getGosuClassLoader().getBytes( getClassAtCaret() ); ClassReader cr = new ClassReader( bytes ); //int flags = ClassReader.SKIP_FRAMES; int flags = 0; StringWriter out = new StringWriter(); cr.accept( new TraceClassVisitor( null, new GosuTextifier(), new PrintWriter( out ) ), flags ); System.out.println( out ); } private IGosuClass getClassAtCaret() { IParseTree locAtCaret = getCurrentGosuEditor().getDeepestLocationAtCaret(); if( locAtCaret == null ) { return getCurrentGosuEditor().getParsedClass(); } IParsedElement elemAtCaret = locAtCaret.getParsedElement(); while( elemAtCaret != null && !(elemAtCaret instanceof IClassStatement) && !(elemAtCaret instanceof IBlockExpression) ) { elemAtCaret = elemAtCaret.getParent(); } if( elemAtCaret == null ) { return getCurrentGosuEditor().getParsedClass(); } if( elemAtCaret instanceof IClassStatement ) { return elemAtCaret.getGosuClass(); } if( elemAtCaret instanceof IBlockExpression ) { return ((IBlockExpression)elemAtCaret).getBlockGosuClass(); } throw new IllegalStateException( "Unexpected parse element: " + elemAtCaret.getClass().getName() ); } public void execute( IRunConfig runConfig ) { if( _runState != RunState.None ) { return; } saveAndReloadType( getCurrentFile(), getCurrentEditor() ); getExperiment().addRunConfig( runConfig ); _processRunner = runConfig.run(); } public void debug( IRunConfig runConfig ) { if( _runState != RunState.None ) { return; } saveAndReloadType( getCurrentFile(), getCurrentEditor() ); getExperiment().addRunConfig( runConfig ); _processRunner = runConfig.debug(); } public boolean isRunning() { return _runState == RunState.Run; } public boolean isDebugging() { return _runState == RunState.Debug; } public TypeNameCache getTypeNamesCache() { return _typeNamesCache; } public void addBusySignal( RunState runState ) { _runState = runState; } public void pipeInput() { EventQueue.invokeLater( () -> { try { _oldIn = System.in; PipedInputStream sysIn = new PipedInputStream(); Process process = _processRunner.getProcess(); if( process == null ) { // Assume we are in-process _inWriter = new OutputStreamWriter( new PipedOutputStream( sysIn ) ); System.setIn( sysIn ); } else { _inWriter = new OutputStreamWriter( process.getOutputStream() ); } } catch( IOException e ) { throw new RuntimeException( e ); } JTextPane outputPanel = _consolePanel.getOutputPanel(); outputPanel.setEditable( true ); _sysInListener = new SysInListener(); outputPanel.addKeyListener( _sysInListener ); } ); } public void killProcess() { if( _processRunner != null ) { if( _debugger != null && (_debugger.isSuspended() || _debugger.isPaused()) ) { _debugger.resumeExecution(); } Process process = _processRunner.getProcess(); if( process != null && process.isAlive() ) { process.destroyForcibly(); } } } public Debugger getDebugger() { return _debugger; } public void clearDebugger() { _debugger = null; EventQueue.invokeLater( () -> { showDebugger( false ); if( getConsolePanel() != null ) { showConsole( true ); } } ); } public void makeDebugger( VirtualMachine vm ) { EventQueue.invokeLater( () -> { _debugger = new Debugger( vm, _breakpointManager ); _debugger.addChangeListener( dbg -> EventQueue.invokeLater( this::handleDebuggerStateChange ) ); showDebugger( true ); showConsole( true ); _debugger.startDebugging(); } ); } private void handleDebuggerStateChange() { if( !EventQueue.isDispatchThread() ) { throw new Error(); } EditorHost editor = getCurrentEditor(); if( editor != null ) { editor.repaint(); } if( _debugger != null && _debugger.isSuspended() ) { Location location = _debugger.getSuspendedLocation(); if( location != null ) { jumptToBreakpoint( location, false ); } } } public void jumptToBreakpoint( Location location, boolean bFocus ) { String fqn = Debugger.getOutermostType( location.declaringType() ); int line = location.lineNumber(); if( line <= 0 ) { // ignore invalid line numbers e.g., sometimes -1 is a line number for generated code return; } java.awt.EventQueue.invokeLater( () -> { if( openType( fqn, bFocus ) ) { getCurrentEditor().gotoLine( line ); } Debugger debugger = getDebugger(); if( debugger != null && debugger.getEventName() != null && (debugger.getEventName().contains( "Breakpoint" ) || debugger.getEventName().contains( "Exception" )) ) { showDebugger( true ); } } ); } public void showDebugger( boolean bShow ) { if( bShow ) { if( _debugPanel == null ) { _debugPanel = new DebugPanel( _debugger ); _bottomTabPane.addTab( _processRunner.getRunConfig().getName(), EditorUtilities.loadIcon( "images/debug.png" ), _debugPanel ); _debugPanel.addLocationListener( loc -> jumptToBreakpoint( loc, false ) ); } else { ITab tab = _bottomTabPane.findTabWithContent( _debugPanel ); _bottomTabPane.selectTab( tab, false ); } } else { _bottomTabPane.removeTabWithContent( _debugPanel ); if( _bottomTabPane.getTabCount() == 0 ) { _outerSplitPane.collapseBottom( _bottomTabPane ); } _debugPanel = null; } } public BreakpointManager getBreakpointManager() { return _breakpointManager; } public TabPane getEditorTabPane() { return _editorTabPane; } public List getOpenFilesInProject() { List files = new ArrayList<>(); for( ITab tab: getEditorTabPane().getTabs() ) { EditorHost editor = (EditorHost)tab.getContentPane(); if( editor != null ) { Path file = (Path)editor.getClientProperty( "_file" ); if( file != null ) { FileTree tree = FileTreeUtil.getRoot().find( file ); if( tree != null ) { files.add( tree ); } } } } return files; } class SysInListener extends KeyAdapter { @Override public void keyReleased( KeyEvent e ) { if( e.getKeyCode() == KeyEvent.VK_ENTER ) { JTextPane op = _consolePanel.getOutputPanel(); Element elem = getElementAt( op.getCaretPosition() - 1 ); try { String text = _consolePanel.getOutputPanel().getText( elem.getStartOffset(), elem.getEndOffset() - elem.getStartOffset() ); _inWriter.write( text ); _inWriter.flush(); } catch( Exception e1 ) { //throw new RuntimeException( e1 ); } } } public Element getElementAt( int offset ) { return getElementAt( _consolePanel.getOutputPanel().getDocument().getDefaultRootElement(), offset ); } private Element getElementAt( Element parent, int offset ) { if( parent.isLeaf() ) { return parent; } return getElementAt( parent.getElement( parent.getElementIndex( offset ) ), offset ); } } public void removeBusySignal() { if( _runState != RunState.None ) { _runState = RunState.None; if( _consolePanel != null ) { _consolePanel.getOutputPanel().setEditable( false ); _consolePanel.getOutputPanel().removeKeyListener( _sysInListener ); } _inWriter = null; System.setIn( _oldIn ); } } public void clearOutput() { _consolePanel.clear(); } public AtomicUndoManager getUndoManager() { return getCurrentEditor() != null ? getCurrentEditor().getUndoManager() : _defaultUndoMgr; } public void selectTab( Path file ) { for( int i = 0; i < _editorTabPane.getTabCount(); i++ ) { EditorHost editor = (EditorHost)_editorTabPane.getTabAt( i ).getContentPane(); if( editor != null ) { if( editor.getClientProperty( "_file" ).equals( file ) ) { _editorTabPane.selectTab( _editorTabPane.getTabAt( i ), true ); return; } } } openFile( file, true ); } public void closeTab( Path file ) { for( int i = 0; i < _editorTabPane.getTabCount(); i++ ) { EditorHost editor = (EditorHost)_editorTabPane.getTabAt( i ).getContentPane(); if( editor != null ) { if( editor.getClientProperty( "_file" ).equals( file ) ) { _editorTabPane.removeTab( _editorTabPane.getTabAt( i ) ); return; } } } } public void goBackward() { getTabSelectionHistory().goBackward(); } public boolean canGoBackward() { return getTabSelectionHistory().canGoBackward(); } public void goForward() { getTabSelectionHistory().goForward(); } public boolean canGoForward() { return getTabSelectionHistory().canGoForward(); } public void displayRecentViewsPopup() { List mruViewsList = new ArrayList<>( getTabSelectionHistory().getTabMruList() ); for( int i = 0; i < mruViewsList.size(); i++ ) { ITabHistoryContext ctx = mruViewsList.get( i ); if( ctx != null && ctx.represents( getCurrentEditor() ) ) { mruViewsList.remove( ctx ); } } LabelListPopup popup = new LabelListPopup( "Recent Files", mruViewsList, "No recent files" ); popup.addNodeChangeListener( e -> { ITabHistoryContext context = (ITabHistoryContext)e.getSource(); getTabSelectionHistory().getTabHistoryHandler().selectTab( context ); } ); popup.show( this, getWidth() / 2 - 100, getHeight() / 2 - 200 ); } public boolean isDirty( EditorHost editor ) { if( editor == null ) { return false; } Boolean bDirty = (Boolean)editor.getClientProperty( "_bDirty" ); return bDirty == null ? false : bDirty; } public void setDirty( EditorHost editor, boolean bDirty ) { editor.putClientProperty( "_bDirty", bDirty ); } class UndoActionHandler extends AbstractAction { public void actionPerformed( ActionEvent e ) { if( isEnabled() ) { getUndoManager().undo(); } } public boolean isEnabled() { return getUndoManager().canUndo(); } } class RedoActionHandler extends AbstractAction { public void actionPerformed( ActionEvent e ) { if( isEnabled() ) { getUndoManager().redo(); } } public boolean isEnabled() { return getUndoManager().canRedo(); } } public Clipboard getClipboard() { return Toolkit.getDefaultToolkit().getSystemClipboard(); } private void markErrorsForGosuLanguageTest() { GosuDocument document = getCurrentGosuEditor().getDocument(); //noinspection ThrowableResultOfMethodCallIgnored ParseResultsException pre = document.getParseResultsException(); if( pre == null || (!pre.hasParseExceptions() && !pre.hasParseWarnings()) ) { return; } final Map> map = new HashMap<>(); for( IParseIssue pi : pre.getParseIssues() ) { ResourceKey messageKey = pi.getMessageKey(); if( messageKey != null ) { String issue = messageKey.getKey(); int iLine = pi.getLine(); List issues = map.get( iLine ); if( issues == null ) { map.put( iLine, issues = new ArrayList<>() ); } issues.add( issue ); } } final List lines = new ArrayList<>( map.keySet() ); Collections.sort( lines ); String text; try { text = document.getText( 0, document.getLength() ); String[] strLines = text.split( "\n" ); removeOldIssueKeyMarkers( strLines ); addIssueKeyMarkers( strLines, lines, map ); CompoundEdit atom = getUndoManager().beginUndoAtom( "Mark Phase" ); document.replace( 0, text.length(), joinLines( strLines ), null ); getUndoManager().endUndoAtom( atom ); } catch( BadLocationException e ) { e.printStackTrace(); } } private String joinLines( String[] strLines ) { StringBuilder sb = new StringBuilder(); for( String line : strLines ) { sb.append( line ).append( '\n' ); } return sb.toString(); } private void removeOldIssueKeyMarkers( String[] lines ) { for( int i = 0; i < lines.length; i++ ) { int issueIndex = lines[i].indexOf( " //## issuekeys:" ); if( issueIndex != -1 ) { lines[i] = lines[i].substring( 0, issueIndex ); } } } private void addIssueKeyMarkers( String[] strLines, List lines, Map> map ) { for( int iLine : lines ) { String issues = makeIssueString( map.get( iLine ) ); strLines[iLine - 1] = strLines[iLine - 1].concat( issues ); } } private String makeIssueString( List issues ) { StringBuilder sb = new StringBuilder(); for( String issue : issues ) { sb.append( sb.length() != 0 ? ", " : "" ).append( issue ); } sb.insert( 0, " //## issuekeys: " ); return sb.toString(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy