Working on UI using ANSI escape sequences.

This commit is contained in:
Jonathan Bernard 2013-08-08 14:21:00 -05:00
parent f9e700f27b
commit 769245b690
10 changed files with 166 additions and 0 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,166 @@
package com.jdblabs.timestamper.cli
import com.jdbernard.util.SmartConfig
import com.jdbernard.util.LightOptionParser
import com.jdblabs.timestamper.core.Timeline
import com.jdblabs.timestamper.core.TimelineMarker
import com.jdblabs.timestamper.core.TimelineProperties
import com.martiansoftware.nailgun.NGContext
import org.fusesource.jansi.AnsiConsole
import static org.fusesource.jansi.Ansi.*
import static org.fusesource.jansi.Ansi.*
import static org.fusesource.jansi.Ansi.Color.*
public class TimeStamperCLI {
private static String EOL = System.getProperty("line.separator")
private static TimeStamperCLI nailgunInst
protected TimelineProperties timelineProperties
protected Timeline timeline
public static final String VERSION = "0.1"
protected static def cli = [
'v': [longOpt: 'version'],
'd': [longOpt: 'working-directory', arguments: 1],
't': [longOpt: 'timeline-config', arguments: 1] ]
public static void main(String[] args) {
TimeStamperCLI inst = new TimeStamperCLI()
doMain(inst, args, System.in, System.out, System.err); }
public static void nailMain(NGContext context) {
if (nailgunInst == null)
nailgunInst = new TimeStamperCLI()
doMain(nailgunInst, context.args, context.in, context.out, context.err) }
protected static doMain(TimeStamperCLI inst, String[] args,
def sin, def out, def err) {
out = new PrintStream(AnsiConsole.wrapOutputStream(out))
def opts = LightOptionParser.parseOptions(cli, args as List)
File workingDir = new File(opts.d ?: '.')
if (opts.t) {
File propFile = new File(workingDir, opts.t)
inst.showTimeline(propFile, sin, out, err) }
else if (inst.timeline == null) {
// Look for .timestamperrc user config file
File cfgFile = new File(
System.getProperty('user.home'), ".timestamperrc")
if (!cfgFile.exists() || !cfgFile.isFile())
err.println "Could not find the user configuration file: " +
cfgFile.canonicalPath
else {
def cfg = new SmartConfig(cfgFile)
inst.showTimeline(new File(cfg.lastUsed), sin, out, err) } }
else { inst.showTimeline(sin, out, err) } }
public TimeStamperCLI() { }
public void showTimeline(File timelinePropertiesFile,
def sin, def out, def err) {
if (!timelinePropertiesFile.exists() ||
!timelinePropertiesFile.isFile()) {
err.println "No such timeline property file: " +
timelinePropertiesFile.canonicalPath
return }
this.timelineProperties = new TimelineProperties(timelinePropertiesFile)
this.timeline = timelineProperties.timeline
showTimeline(sin, out, err) }
public void showTimeline(final def sin, def out, def err) {
//out.println ""
def currentMarker = timeline.getLastMarker(new Date())
boolean running = true
def reader = new NonBlockingReader(sin)
def readerThread = new Thread(reader)
readerThread.start()
def blockingReadLine = {
String line = null;
while (line == null && readerThread.isAlive()) {
line = reader.readLine()
Thread.sleep(200) }
return line }
String line = null
println ""
while (running && readerThread.isAlive()) {
// Refresh the display
out.print(
ansi().saveCursorPosition().
cursorUp(1).eraseLine(Erase.ALL).fgBright(CYAN).
a(Timeline.shortFormat.format(currentMarker.timestamp) + " ").
fg(GREEN).a("(" + getDuration(currentMarker) + ") ").
fg(WHITE).a(currentMarker.mark).a(EOL).restorCursorPosition()
)
// Handle user input
if ((line = reader.readLine()) != null) {
if (line =~ /quit|exit/) running = false }
Thread.sleep(200); }
if (readerThread.isAlive()) {
readerThread.interrupt();
readerThread.join(500);
if (readerThread.isAlive()) readerThread.stop(); }
}
protected String getDuration(TimelineMarker tm) {
Date currentTime = new Date()
long seconds = currentTime.time - tm.timestamp.time
seconds /= 1000
long minutes = seconds / 60
seconds = seconds % 60
long hours = minutes / 60
minutes %= 60
long days = hours / 24
hours %= 24
StringBuilder sb = new StringBuilder()
if (days > 0) sb.append(days + "day ")
if (hours > 0) sb.append(hours + "hr ")
if (minutes > 0) sb.append(minutes + "min ")
sb.append(seconds + "sec")
return sb.toString() }
class NonBlockingReader implements Runnable {
private Reader rin
private LinkedList buffer = []
public void run() {
String line = null
try {
while((line = rin.readLine()) != null &&
!Thread.currentThread().isInterrupted())
storeLine(line) }
catch (InterruptedException ie) { Thread.currentThread().interrupt() } }
public synchronized String readLine() { return buffer.poll() }
private synchronized void storeLine(String line) { buffer << line }
public NonBlockingReader(def sin) {
this.rin = new InputStreamReader(sin) }
}
}