console.groovy

来自「Groovy动态语言 运行在JVM中的动态语言 可以方便的处理业务逻辑变化大的业」· GROOVY 代码 · 共 606 行 · 第 1/2 页

GROOVY
606
字号
package groovy.ui

import groovy.swing.SwingBuilder
import groovy.inspect.swingui.ObjectBrowser

import java.awt.BorderLayout
import java.awt.EventQueue
import java.awt.Color
import java.awt.Font
import java.awt.Insets
import java.awt.Toolkit
import java.awt.event.KeyEvent
import java.io.PrintWriter
import java.io.StringWriter
import java.util.EventObject

import javax.swing.*
import javax.swing.text.*
import javax.swing.event.*

import org.codehaus.groovy.runtime.InvokerHelper

/**
 * Groovy Swing console.
 *
 * Allows user to interactively enter and execute Groovy. 
 *
 * @author Danno Ferrin
 * @author Dierk Koenig, changed Layout, included Selection sensitivity, included ObjectBrowser
 * @author Alan Green more features: history, System.out capture, bind result to _
 */
class Console implements CaretListener {

	// Whether or not std output should be captured to the console
	def captureStdOut = true

	// Maximum size of history
	int maxHistory = 10
	
	// Maximum number of characters to show on console at any time
	int maxOutputChars = 10000

	// UI
    SwingBuilder swing
    JFrame frame
    JTextArea inputArea
    JTextPane outputArea
    JLabel statusLabel
    JDialog runWaitDialog
    
    // Styles for output area
    Style promptStyle;
    Style commandStyle;
    Style outputStyle;
    Style resultStyle;
    
	// Internal history
    List history = []
    int historyIndex = 1 // valid values are 0..history.length()

	// Current editor state
    boolean dirty
    int textSelectionStart  // keep track of selections in inputArea
    int textSelectionEnd
    def scriptFile

	// Running scripts
	GroovyShell shell
    int scriptNameCounter = 0
    def systemOutInterceptor
    def runThread = null
    

    static void main(args) {
        def console = new Console()
        console.run()
    }
    
    Console() {
    	shell = new GroovyShell()
    }
    
    Console(Binding binding) {
    	shell = new GroovyShell(binding)
    }
    
    Console(ClassLoader parent, Binding binding) {
    	shell = new GroovyShell(parent,binding)	
    }

    void run() {
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName())
        // if menu modifier is two keys we are out of luck as the javadocs
        // indicates it returns "Control+Shift" instead of "Control Shift"
        def menuModifier = KeyEvent.getKeyModifiersText(
            Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()).toLowerCase() + ' '

        swing = new SwingBuilder()
        frame = swing.frame(
            title:'GroovyConsole',
            location:[100,100],
            size:[500,400],
            defaultCloseOperation:javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE) {
            def newFileAction = action(
                name:'New File', closure: this.&fileNewFile, mnemonic: 'N', 
                accelerator: menuModifier + 'Q'
            )
            def newWindowAction = action(
                name:'New Window', closure: this.&fileNewWindow, mnemonic: 'W'
            )
            def openAction = action(
                name:'Open', closure: this.&fileOpen, mnemonic: 'O', accelerator: menuModifier + 'O'
            )
            def saveAction = action(
                name:'Save', closure: this.&fileSave, mnemonic: 'S', accelerator: menuModifier + 'S'
            )
            def exitAction = action(
                name:'Exit', closure: this.&exit, mnemonic: 'x', accelerator: 'alt F4'
            )	            
            def historyPrevAction = action(
                name:'Previous', closure: this.&historyPrev, mnemonic: 'P', accelerator: 'ctrl P'
            )            
            def historyNextAction = action(
            	name: 'Next', closure: this.&historyNext, mnemonic: 'N', accelerator: 'ctrl N'
            )            
            def clearOutputAction = action(
                name:'Clear Output', closure: this.&clearOutput, mnemonic: 'l', keyStroke: 'ctrl W',
                accelerator: 'ctrl W'
            )
            def runAction = action(
                name:'Run', closure: this.&runScript, mnemonic: 'R', keyStroke: 'ctrl ENTER',
                accelerator: 'ctrl R'
            )
            def inspectLastAction = action(
                name:'Inspect Last', closure: this.&inspectLast, mnemonic: 'I', keyStroke: 'ctrl I',
                accelerator: 'ctrl I'
            )
            def inspectVariablesAction = action(
            	name:'Inspect Variables', closure: this.&inspectVariables, mnemonic: 'V', keyStroke: 'ctrl J',
                accelerator: 'ctrl J'
            )
            def captureStdOutAction = action(
            	name:'Capture Standard Output', closure: this.&captureStdOut, mnemonic: 'C'
            )
            def largerFontAction = action(
                name:'Larger Font', closure: this.&largerFont, mnemonic: 'L', keyStroke: 'alt shift L',
                accelerator: 'alt shift L'
            )
            def smallerFontAction = action(
                name:'Smaller Font', closure: this.&smallerFont, mnemonic: 'S', keyStroke: 'alt shift S',
                accelerator: 'alt shift S'
            )
            def aboutAction = action(name:'About', closure: this.&showAbout, mnemonic: 'A')
            menuBar {
                menu(text:'File', mnemonic: 'F') {
                    menuItem() { action(newFileAction) }
                    menuItem() { action(newWindowAction) }
                    menuItem() { action(openAction) }
                    separator()
                    menuItem() { action(saveAction) }
                    separator()
                    menuItem() { action(exitAction) }
                }
                menu(text:'Edit', mnemonic: 'E') {
                	menuItem() { action(historyNextAction) }
                	menuItem() { action(historyPrevAction) }
                	separator()
                	menuItem() { action(clearOutputAction) }
                }
                menu(text:'Actions', mnemonic: 'A') {
                    menuItem() { action(runAction) }
                    menuItem() { action(inspectLastAction) }
                    menuItem() { action(inspectVariablesAction) }
                    separator()
                    checkBoxMenuItem(selected: captureStdOut) { action(captureStdOutAction) }
                    separator()
                    menuItem() { action(largerFontAction) }
                    menuItem() { action(smallerFontAction) }
                }
                menu(text:'Help', mnemonic: 'H') {
                    menuItem() { action(aboutAction) }
                }
            }
            
            borderLayout()
            
            splitPane(id:'splitPane', resizeWeight:0.50F, 
            	orientation:JSplitPane.VERTICAL_SPLIT, constraints: BorderLayout.CENTER) 
            {
                scrollPane {
                    inputArea = textArea(
                        margin: new Insets(3,3,3,3), font: new Font('Monospaced',Font.PLAIN,12)
                    ) { action(runAction) }
                }
                scrollPane {
                    outputArea = textPane(editable:false, background: new Color(255,255,218))
                    addStylesToDocument(outputArea)
                }
            }
            
            statusLabel = label(id:'status', text: 'Welcome to the Groovy.', constraints: BorderLayout.SOUTH,
            	border: BorderFactory.createLoweredBevelBorder())
        }   // end of frame
        
        runWaitDialog = swing.dialog(title: 'Groovy executing', owner: frame, modal: true) {
        	//boxLayout(axis: BoxLayout.Y_AXIS)  // todo mittie: dialog.setLayout -> dialog.contentPane.setLayout()
        	label(text: "Groovy is now executing. Please wait.", 
        		border: BorderFactory.createEmptyBorder(10, 10, 10, 10), alignmentX: 0.5f)
        	button(action: action(name: 'Interrupt', closure: this.&confirmRunInterrupt),
	        	border: BorderFactory.createEmptyBorder(10, 10, 10, 10), alignmentX: 0.5f)
        } // end of runWaitDialog

        // add listeners
        frame.windowClosing = this.&exit
        inputArea.addCaretListener(this)
        inputArea.document.undoableEditHappened = { setDirty(true) }
        
        systemOutInterceptor = new SystemOutputInterceptor(this.&notifySystemOut)
        systemOutInterceptor.start();
        
        bindResults()
        
        frame.show()
        SwingUtilities.invokeLater({inputArea.requestFocus()});
    }

	void addStylesToDocument(JTextPane outputArea) {
        StyledDocument doc = outputArea.getStyledDocument();

        Style defStyle = StyleContext.getDefaultStyleContext().getStyle(StyleContext.DEFAULT_STYLE);

        Style regular = doc.addStyle("regular", defStyle);
        StyleConstants.setFontFamily(regular, "Monospaced")

        promptStyle = doc.addStyle("prompt", regular)
        StyleConstants.setForeground(promptStyle, Color.BLUE)

        commandStyle = doc.addStyle("command", regular);
        StyleConstants.setForeground(commandStyle, Color.MAGENTA)

        outputStyle = regular 
        
        resultStyle = doc.addStyle("result", regular)
        StyleConstants.setBackground(resultStyle, Color.BLUE)
        StyleConstants.setBackground(resultStyle, Color.YELLOW)
    }
    
    void addToHistory(record) {
    	history.add(record)
    	// history.size here just retrieves method closure
    	if (history.size() > maxHistory) {
    		history.remove(0)
    	}
    	// history.size doesn't work here either
    	historyIndex = history.size()
    }
    
	// Append a string to the output area
    void appendOutput(text, style){
    	def doc = outputArea.styledDocument
        doc.insertString(doc.length, text, style)
        
        // Ensure we don't have too much in console (takes too much memory)
        if (doc.length > maxOutputChars) {
        	doc.remove(0, doc.length - maxOutputChars)
        }
    }

	// Append a string to the output area on a new line
    void appendOutputNl(text, style){
    	def doc = outputArea.styledDocument
    	def len = doc.length
    	if (len > 0 && doc.getText(len - 1, 1) != "\n") {
    		appendOutput("\n", style);
    	} 
    	appendOutput(text, style)
    }
    
    // Return false if use elected to cancel
    boolean askToSaveFile() {
    	if (scriptFile == null || !dirty) {
    		return true
    	}
        switch (JOptionPane.showConfirmDialog(frame,
            "Save changes to " + scriptFile.name + "?",
            "GroovyConsole", JOptionPane.YES_NO_CANCEL_OPTION))
        {
            case JOptionPane.YES_OPTION:
                return fileSave()
            case JOptionPane.NO_OPTION:
            	return true
            default:
            	return false
        }
    }

    private static void beep() {
    	Toolkit.defaultToolkit.beep()
    }
    
    // Binds the "_" and "__" variables in the shell
    void bindResults() {
		shell.setVariable("_", getLastResult()) // lastResult doesn't seem to work

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?