-
Notifications
You must be signed in to change notification settings - Fork 1
REPL Bash
Namek edited this page May 29, 2016
·
1 revision
To learn more about snippets below:
> Read a blogpost: Cygwin Bash as a REPL inside The Console
Input and output are not synchronized in same thread. That causes two things:
- it's a little easier
- auto-completion doesn't simply auto-complete command line but instead it only prints possibilites. To see this fixed, look at Synchronous version
var InputStreamReader = Java.type("java.io.InputStreamReader")
var BufferedReader = Java.type("java.io.BufferedReader")
var ProcessBuilder = Java.type("java.lang.ProcessBuilder")
var Thread = Java.type("java.lang.Thread")
var CYGWIN_DIR = "N:/.babun/cygwin/"
var ENTER = 10
var pb, process, inputThread, keepAlive
function getBashCommand() {
return CYGWIN_DIR + "bin/bash.exe"
}
exports.commandLineHandler = {
init: function() {
pb = new ProcessBuilder(getBashCommand())
pb.redirectErrorStream(true)
var env = pb.environment()
env.put('Path', env.get('Path') + ";" + CYGWIN_DIR + "bin")
process = pb.start()
keepAlive = true
inputThread = new Thread(listenToInput.bind(null, this.context))
inputThread.start()
},
handleCompletion: function(input) {
var os = process.getOutputStream()
var toComplete = 'compgen -c ' + input
os.write(toComplete.getBytes())
os.write(ENTER)
os.flush()
},
handleExecution: function(input, utils, context) {
var os = process.getOutputStream()
// display in console what a user typed in
context.output.addInputEntry(input)
// send input text to bash
os.write(input.getBytes())
os.write(ENTER)
os.flush()
},
dispose: function() {
console.log('exiting repl')
keepAlive = false
inputThread = null
}
}
function listenToInput(consoleContext) {
var input = new BufferedReader(new InputStreamReader(process.getInputStream(), "UTF-8"))
var output = consoleContext.getOutput()
while (keepAlive) {
if (!process.isAlive()) {
// this can be achieved by invoking `exit` command
output.addErrorEntry("bash process is dead.")
// bring back standard handler
consoleContext.commandLineService.resetHandler()
return
}
while (input.ready()) {
output.addTextEntry(input.readLine())
}
// don't consume too much CPU
Thread.sleep(50)
}
}
This version synchronizes input with output by introducing input queue. This fixes a problem of how to connect given amount of output with command completion.
var InputStreamReader = Java.type("java.io.InputStreamReader")
var BufferedReader = Java.type("java.io.BufferedReader")
var ProcessBuilder = Java.type("java.lang.ProcessBuilder")
var Thread = Java.type("java.lang.Thread")
var CYGWIN_DIR = "N:/.babun/cygwin/"
var ENTER = 10
var INPUT_TYPE_CMD = 'cmd'
var INPUT_TYPE_COMPLETION = 'completion'
var pb, process, thread, keepAlive
var inputQueue = []
function getBashCommand() {
return CYGWIN_DIR + "bin/bash.exe"
}
exports.commandLineHandler = {
init: function() {
pb = new ProcessBuilder(getBashCommand())
pb.redirectErrorStream(true)
var env = pb.environment()
env.put('Path', env.get('Path') + ";" + CYGWIN_DIR + "bin")
process = pb.start()
keepAlive = true
thread = new Thread(workWithBash.bind(null, this.context, this.utils))
thread.start()
},
handleCompletion: function(input) {
inputQueue.push({
type: INPUT_TYPE_COMPLETION,
text: input
})
},
handleExecution: function(input, utils, context) {
// display in console what a user typed in
context.output.addInputEntry(input)
// queue request for execution
inputQueue.push({
type: INPUT_TYPE_CMD,
text: input
})
},
dispose: function() {
keepAlive = false
thread = null
}
}
function workWithBash(consoleContext, consoleInputUtils) {
var input = new BufferedReader(new InputStreamReader(process.getInputStream(), "UTF-8"))
var output = process.getOutputStream()
var consoleOutput = consoleContext.getOutput()
while (keepAlive) {
if (!process.isAlive()) {
// this can be achieved by invoking `exit` command
consoleOutput.addErrorEntry("bash process is dead.")
// bring back standard handler
consoleContext.commandLineService.resetHandler()
return
}
while (inputQueue.length > 0) {
var inputCommand = inputQueue.shift()
var cmd = inputCommand.text
if (inputCommand.type == INPUT_TYPE_COMPLETION) {
cmd = 'compgen -c ' + cmd
}
// send input text to bash
output.write(cmd.getBytes())
output.write(ENTER)
output.flush()
if (inputCommand.type == INPUT_TYPE_COMPLETION) {
var lines = []
while (input.ready()) {
lines.push(input.readLine())
}
// auto-complete command!
if (lines.length > 0) {
consoleInputUtils.setInput(lines[0])
}
}
else if (inputCommand.type == INPUT_TYPE_CMD) {
while (input.ready()) {
consoleOutput.addTextEntry(input.readLine())
}
}
}
// don't consume too much CPU waiting for another bunch of input
Thread.sleep(50)
}
}