Skip to content

Instantly share code, notes, and snippets.

@Karry
Last active August 29, 2015 14:17
Show Gist options
  • Select an option

  • Save Karry/2f2735e0aa8b0ae99b42 to your computer and use it in GitHub Desktop.

Select an option

Save Karry/2f2735e0aa8b0ae99b42 to your computer and use it in GitHub Desktop.
Scala JLine input with support of navigation using CTRL+arrow
/**
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
* Version 2, December 2004
*
* Copyright (C) 2015 Lukáš Karas <lukas.karas@centrum.cz>
*
* Everyone is permitted to copy and distribute verbatim or modified
* copies of this license document, and changing it is allowed as long
* as the name is changed.
*
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
* TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
*
* 0. You just DO WHAT THE FUCK YOU WANT TO.
*/
import java.awt.event.{ActionEvent, ActionListener}
import scala.tools.jline.console.ConsoleReader
/**
* JLine don't support skipping words on input by classic shortcut
* (CTRL + Left / Right arrow). Terminal generates sequence of
* virtual keys when CTRL + other char is pressed (49 59 53 ...).
* So it is simple to write listener on these sequences.
*/
object Console{
class HistoryInputStream(in: InputStream, bufferLen: Int) extends InputStream {
var offset: Int = 0
val ringBuffer = new Array[Int](bufferLen)
def history(histOff: Int): Int = {
val off = histOff - 1
if (off > 0 || (bufferLen + off) < 0) 0
else ringBuffer((offset + bufferLen + off) % bufferLen)
}
override def read(): Int = {
val i = in.read()
ringBuffer(offset) = i
offset = (offset + 1) % bufferLen
i
}
}
def apply(): ConsoleReader = {
val characterInput = new HistoryInputStream(new FileInputStream(FileDescriptor.in), 8)
val consoleReader = new ConsoleReader(characterInput, System.out, null, null)
val terminal = consoleReader.getTerminal
val cursorBuffer = consoleReader.getCursorBuffer()
def skipWord(ch: () => Char, move: () => Boolean): Unit = {
while (ch().isWhitespace && move()) {}
while (!ch().isWhitespace && move()) {}
}
if (terminal.isSupported && terminal.isInstanceOf[UnixTerminal]) {
consoleReader.addTriggeredAction(49, new ActionListener {
override def actionPerformed(e: ActionEvent): Unit = {
// This is little bit hack. Ctrl prefix is consumed by UnixTerminal, we have to look into history...
if (characterInput.history(-2) != 27 /*ARROW_START*/ || characterInput.history(-1) != 91 /*ARROW_PREFIX*/ ) {
consoleReader.putString(49.toChar.toString) // if Ctrl prefix not found in history, 49 is just character "1"
} else {
if (characterInput.read() == 59 && characterInput.read() == 53) {
// Ctrl + ...
characterInput.read() match {
// Ctrl + Right Arrow
case 67 => skipWord(
() => cursorBuffer.charAtCursor(),
() => consoleReader.moveCursor(+1) != 0)
// Ctrl + Left Arrow
case 68 => skipWord(
() => cursorBuffer.charLeftOfCursor(),
() => consoleReader.moveCursor(-1) != 0)
case _ => {}
}
}
}
}
})
}
consoleReader
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment