Swing Scala REPL: What Is Wrong Here?
22 March 2011 4 comments
Reading time:
3 minutes
Word count:
550
I just spent the best part of two hours attempting figure out an interactive REPL for Scala, a la the Groovy Console, but a very basic version. My progress has not been lightning fast. Why?
package swingrepl import java.io._ import scala.swing._ import scala.swing.event._ import javax.swing.SwingUtilities import scala.tools.nsc._ import scala.tools.nsc.interpreter._ /** * Swing version of the REPL * User: Peter * Date: 22/03/11 * Time: 14:34 */ object SwingRepl extends SimpleSwingApplication { def outputPane = new TextArea { editable = false text = "Text output" } def replInvoker = new ReplInvoker( outputPane ) replInvoker.start() def inputPane: TextArea = new TextArea { text = "List(1,2,3,4,5) map ( n => n * n )" editable = true listenTo(keys) reactions += { case e: KeyPressed => { // println("keyCode =" +e.peer.getKeyCode+", keyChar = "+e.peer.getKeyChar) if ( e.peer.isControlDown && e.key == Key.Enter ) { replInvoker.interpret( text ) } } case _ => } } def scrollInputPane = new ScrollPane( inputPane ) def scrollOutputPane = new ScrollPane( outputPane ) def splitPane = new SplitPane( Orientation.Horizontal, scrollInputPane, scrollOutputPane ) { dividerLocation = 100 } def top = new MainFrame { title = "Swing REPL" preferredSize = new Dimension(900, 900 ) contents = splitPane } /** * Programmatically launching the Scala REPL */ class ReplInvoker( val outputPane: TextComponent ) { private val swriter = new StringWriter() private val pwriter = new PrintWriter( swriter ) { override def println( line: String ) { super.println( line ) System.out.println("println line="+line) outputPane.text = swriter.toString() } override def print( line: String ) { super.print( line ) System.out.println("print line="+line) System.out.println("swriter.toString="+swriter.toString ) SwingUtilities.invokeLater( new Runnable() { override def run(): Unit = { outputPane.text = outputPane.text +"\n"+ line +"\n" outputPane.peer.validate() } }) } } private val cmd = new InterpreterCommand(Nil, println ) println("cmd="+cmd) private val settings = cmd.settings println("settings="+settings) settings.usejavacp.value = true private val interpreter = new Interpreter(settings, pwriter ) { override def reset() = { super.reset; unleash() } override def unleash() = { super.unleash; } } println("interpreter="+interpreter) private val completion = new Completion(interpreter) def start() { println("ReplInvoker.start") } def interpret(cmd: String): Unit = { println("replInvoker.interpret "+cmd ) interpreter.interpret(cmd) } } }
I really do not understand why this code is not working at all. It is a Swing application to programmatically invokes Scala NSC REPL. I have two JEditorPanes in set horizontally in a JSplitPane. The upper editor pane is a reserved for text entry. The lower editor pane is not editable is reserved for capturing the output of the Scala.
To fire the REPL interactively in the upper pane, hold the CTRL key and pressed the RETURN/ENTER key. Invoking the REPL works fine. However it is the update to the lower pane that causes concern. I can see the interactive REPL returning a result, yet the JEditorPane is not being updated. Is this a Swing Scala bug? Or does Java Swing bug?
I suspected the cause was a Swing multithreading problem hence I used SwingUtilites.invokeLater to make sure the update was on the Event Dispatch Thread, however to no avail. JComponent.repaint() nada, JComponent.validate() nada. What gives?
Oh yes in case you are wondering, JavaFX 2.0 early access does not yet have a multi-line equivalent of JTextComponent.
I will be very interested in finding out what is wrong with this picture? If you know the answer, then a pint of Staropramen is on me. Thanks