Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

REPL arrow keys and other readline keys fail to work after C-z/fg on Mac OS X #6096

Closed
scabug opened this issue Jul 17, 2012 · 15 comments
Closed
Labels

Comments

@scabug
Copy link

scabug commented Jul 17, 2012

  1. Type 'scala' to start the interpreter.
  2. Arrow keys, C-f, C-b, C-p, etc. all work.
  3. C-z to exit to shell.
  4. fg to get back to REPL.
  5. First of all, no prompt is displayed, although it should be. Hit ENTER to get the prompt back.
  6. Now try arrow keys or C-f, C-b, C-p, etc.; they all show up literally (^F for C-f, ^B for C-b, ^[OA for up arrow, etc.).

This makes the REPL almost useless, because it's impossible to edit (other than with the Delete key) or retrieve previous lines.

@scabug
Copy link
Author

scabug commented Jul 17, 2012

Imported From: https://issues.scala-lang.org/browse/SI-6096?orig=1
Reporter: Ben Wing (benwing)
Affected Versions: 2.9.2
Attachments:

@scabug
Copy link
Author

scabug commented Jul 17, 2012

@paulp said:
For reference, this is fixed here:

paulp/scala@e3506a3583

But that's a whole separate universe of code.

@scabug
Copy link
Author

scabug commented Jul 21, 2012

Ben Wing (benwing) said:
How hard would it be to push this into Scala 2.10? The bug fix looks simple, from your code, and for me it's a huge issue.

@scabug
Copy link
Author

scabug commented Jul 22, 2012

@paulp said:
It depends on code which is no longer in trunk. Can't do it in the immediate future.

@scabug
Copy link
Author

scabug commented Dec 26, 2013

Ben Wing (benwing) said:
Paul -- you said above you had a fix for this sitting in an old workspace. Any chance of incorporating this into Scala?

@scabug
Copy link
Author

scabug commented Dec 27, 2013

@paulp said (edited on Dec 27, 2013 12:26:02 AM UTC):
I'm not sure why I'm still doing stuff like this, but here is a working version (handling both ctrl-Z and ctrl-C) but with a caveat:

https://github.com/paulp/scala/compare/repl-signal-handlers

It works like a charm as long as you're using oracle's java. The part I left for you is avoiding a hard dependency on sun.misc.*. That means rewriting it based on reflection, including implementing the SignalHandler interface via a proxy. I've done it before so I know it's doable.

(The "you" in "left for you" can be generic you, as long as it isn't me.)

@scabug
Copy link
Author

scabug commented Dec 27, 2013

@paulp said:
Since my scala branches have the life span of a butterfly lately, I'm also attaching the patch here.

@scabug
Copy link
Author

scabug commented Dec 27, 2013

Ben Wing (benwing) said:
Thanks. Haven't done much with Scala reflection but may be a good opportunity to get it working and figure out how to submit a patch.

@scabug
Copy link
Author

scabug commented Dec 27, 2013

@paulp said:
Oh, I meant with java reflection. I consider scala reflection unusable. But whatever will get the patch in sounds good to me.

@scabug
Copy link
Author

scabug commented Dec 27, 2013

Ben Wing (benwing) said:
Yeah I'm looking into it and coming to the same conclusion.

@scabug
Copy link
Author

scabug commented Dec 27, 2013

@paulp said:
Also, another approach might be to replicate those two classes with dummies and put them last on the classpath. That way it'll still work directly with oracle and be no-ops on other platforms.

@scabug
Copy link
Author

scabug commented Dec 27, 2013

Ben Wing (benwing) said:
Hmm, is this kosher for something to go into the main sources?

@scabug
Copy link
Author

scabug commented Dec 28, 2013

Ben Wing (benwing) said (edited on Dec 28, 2013 12:12:40 AM UTC):
OK, I wrote up code that should implement the proxy around sun.misc. Attached is a small program that implements exactly the code in object InstallSignalHandler in two ways: One by importing sun.misc directly, the other using reflection. I use it to catch SIGINT, and print a message, and both ways work the same. I haven't yet compiled the scala code myself so perhaps someone can put this together with Paul's code.

There may be better ways to handle the possibility of something going wrong; at least, convert the println statements into repldbg() or whatever. But the code as written will work.

package testproxy

import java.lang.reflect.{Array=>_,_}

object SignalProxy {
  val clazz = try {
     Class.forName("sun.misc.Signal")
  } catch {
    case e: ClassNotFoundException => null
  }

  def apply(name: String) = {
    try {
      clazz.getConstructor(classOf[String]).newInstance(name).asInstanceOf[Object]
    } catch {
      case e: Exception => {
        println("Caught exception in SignalProxy.apply: " + e)
        null
      }
    }
  }

  def handle(rsig: Object, proxy: Object): Boolean = {
    try {
      if (clazz != null) {
        val handlerclazz = SignalHandlerProxy.clazz
        if (handlerclazz != null) {
          val m = clazz.getMethod("handle", clazz, handlerclazz)
          m.invoke(null, rsig, proxy)
        }
      }
    } catch {
      case e: Exception => {
        println("Caught exception in SignalProxy.handle: " + e)
        return false
      }
    }
    return true
  }
}

object SignalHandlerProxy {
  val clazz = try {
     Class.forName("sun.misc.SignalHandler")
  } catch {
    case e: ClassNotFoundException => null
  }

  def apply(rsig: Object)(body: => Unit) = {
    if (clazz == null) null
    else try {
      Proxy.newProxyInstance(clazz.getClassLoader(), Array(clazz),
        new SignalHandlerProxy(rsig)(body))
    } catch {
      case e: Exception => {
        println("Caught exception in SignalHandlerProxy.apply: " + e)
        null
      }
    }
  }
}

class SignalHandlerProxy(rsig: Object)(body: => Unit) extends InvocationHandler
{
  def invoke(proxy: Object, m: Method, args: Array[Object]) = {
    
    if (m.getName == "handle")
    {
      val nsig = args(0)
      if (nsig == rsig)
        body
    }
    null
  }
}

object TestSignal1 extends App {
  object InstallSignalHandler {
    import sun.misc.{ Signal, SignalHandler }
    def apply(name: String)(body: => Unit): Unit = {
      val rsig = new Signal(name)
      Signal.handle(rsig, new SignalHandler { def handle(nsig: Signal): Unit = if (rsig == nsig) body })
    }
  }

  InstallSignalHandler("INT") {
    println("Caught SIGINT")
  }
  Thread.sleep(5000)
}

object TestSignal2 extends App {
  object InstallSignalHandler {
    def apply(name: String)(body: => Unit): Unit = {
      val rsig = SignalProxy(name)
      val handler =
        if (rsig != null) SignalHandlerProxy(rsig)(body) else null
      if (handler != null)
        SignalProxy.handle(rsig, handler)
      else
        println("Unable to install signal handler for " + name)
    }
  }

  InstallSignalHandler("INT") {
    println("Caught SIGINT")
  }
  Thread.sleep(5000)
}

@scabug
Copy link
Author

scabug commented Dec 28, 2013

@paulp said:
"Hmm, is this kosher for something to go into the main sources?"

That will probably depend on who you ask, but if it's written to do nothing on all platforms which aren't oracle (and that was the point of the exercise) there's no (good) reason why not.

@SethTisue
Copy link
Member

SethTisue commented Mar 2, 2018

stale, closing. comment/reopen if this is still an issue in current Scala (JLine has been upgraded multiple times since 2012)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants