Working on UI using ANSI escape sequences.
This commit is contained in:
parent
f9e700f27b
commit
769245b690
BIN
lib/compile/jar/jansi-1.11.jar
Normal file
BIN
lib/compile/jar/jansi-1.11.jar
Normal file
Binary file not shown.
BIN
lib/compile/jar/jdb-util-1.8.2.jar
Normal file
BIN
lib/compile/jar/jdb-util-1.8.2.jar
Normal file
Binary file not shown.
BIN
lib/compile/jar/nailgun-0.7.1.jar
Normal file
BIN
lib/compile/jar/nailgun-0.7.1.jar
Normal file
Binary file not shown.
BIN
lib/compile/jar/timestamper-lib-1.2.jar
Normal file
BIN
lib/compile/jar/timestamper-lib-1.2.jar
Normal file
Binary file not shown.
BIN
lib/runtime/jar/jansi-1.11.jar
Normal file
BIN
lib/runtime/jar/jansi-1.11.jar
Normal file
Binary file not shown.
BIN
lib/runtime/jar/jdb-util-1.8.2.jar
Normal file
BIN
lib/runtime/jar/jdb-util-1.8.2.jar
Normal file
Binary file not shown.
BIN
lib/runtime/jar/nailgun-0.7.1.jar
Normal file
BIN
lib/runtime/jar/nailgun-0.7.1.jar
Normal file
Binary file not shown.
BIN
lib/runtime/jar/timestamper-lib-1.2.jar
Normal file
BIN
lib/runtime/jar/timestamper-lib-1.2.jar
Normal file
Binary file not shown.
166
src/main/com/jdblabs/timestamper/cli/TimeStamperCLI.groovy
Normal file
166
src/main/com/jdblabs/timestamper/cli/TimeStamperCLI.groovy
Normal 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) }
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user