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 + -
显示快捷键?