Skip to content

Commit

Permalink
Merge pull request #363 from mattirn/history
Browse files Browse the repository at this point in the history
History command: implemented some missing options
  • Loading branch information
gnodet authored Mar 15, 2019
2 parents 2a5798c + 6d8f049 commit c4b521c
Show file tree
Hide file tree
Showing 5 changed files with 304 additions and 42 deletions.
132 changes: 112 additions & 20 deletions builtins/src/main/java/org/jline/builtins/Commands.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
import java.io.InputStream;
import java.io.PrintStream;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
Expand All @@ -24,13 +26,15 @@
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Pattern;
import java.util.Set;
import java.util.TreeSet;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;

import org.jline.builtins.Completers.CompletionData;
import org.jline.builtins.Options;
import org.jline.builtins.Source.StdInSource;
import org.jline.builtins.Source.URLSource;
import org.jline.keymap.KeyMap;
Expand Down Expand Up @@ -159,54 +163,142 @@ public static void less(Terminal terminal, InputStream in, PrintStream out, Prin
}

public static void history(LineReader reader, PrintStream out, PrintStream err,
String[] argv) throws IOException {
String[] argv) throws IOException, IllegalArgumentException {
final String[] usage = {
"history - list history of commands",
"Usage: history [OPTIONS]",
"Usage: history [-dnrfEi] [-m match] [first] [last]",
" history -ARWI [filename]",
" history --clear",
" history --save",
" -? --help Displays command help",
" --clear Clear history",
" --save Save history",
" -d Print timestamps for each event"};

" -m match If option -m is present the first argument is taken as a pattern",
" and only the history events matching the pattern will be shown",
" -d Print timestamps for each event",
" -f Print full time-date stamps in the US format",
" -E Print full time-date stamps in the European format",
" -i Print full time-date stamps in ISO8601 format",
" -n Suppresses command numbers",
" -r Reverses the order of the commands",
" -A Appends the history out to the given file",
" -R Reads the history from the given file",
" -W Writes the history out to the given file",
" -I If added to -R, only the events that are not contained within the internal list are added",
" If added to -W/A, only the events that are new since the last incremental operation to",
" the file are added",
" [first] [last] These optional arguments are numbers. A negative number is",
" used as an offset to the current history event number"};
Options opt = Options.compile(usage).parse(argv);

if (opt.isSet("help")) {
opt.usage(err);
return;
}
if (!opt.args().isEmpty()) {
err.println("usage: history [OPTIONS]");
return;
}

History history = reader.getHistory();
boolean done = true;
boolean increment = opt.isSet("I") ? true : false;
if (opt.isSet("clear")) {
history.purge();
}
if (opt.isSet("save")) {
} else if (opt.isSet("save")) {
history.save();
} else if (opt.isSet("A")) {
Path file = opt.args().size() > 0 ? Paths.get(opt.args().get(0)) : null;
history.append(file, increment);
} else if (opt.isSet("R")) {
Path file = opt.args().size() > 0 ? Paths.get(opt.args().get(0)) : null;
history.read(file, increment);
} else if (opt.isSet("W")) {
Path file = opt.args().size() > 0 ? Paths.get(opt.args().get(0)) : null;
history.write(file, increment);
} else {
done = false;
}
if (opt.isSet("clear") || opt.isSet("save")) {
if (done) {
return;
}
int argId = 0;
Pattern pattern = null;
if (opt.isSet("m")) {
if (opt.args().size() == 0) {
throw new IllegalArgumentException();
}
String sp = opt.args().get(argId++);
pattern = Pattern.compile(sp.toString());
}
int firstId = opt.args().size() > argId ? parseInteger(opt.args().get(argId++)) : -17;
int lastId = opt.args().size() > argId ? parseInteger(opt.args().get(argId++)) : -1;
firstId = historyId(firstId, history.size() - 1);
lastId = historyId(lastId, history.size() - 1);
if (firstId > lastId) {
throw new IllegalArgumentException();
}
int tot = lastId - firstId + 1;
int listed = 0;
final Highlighter highlighter = reader.getHighlighter();
for (History.Entry entry : history) {
Iterator<History.Entry> iter = null;
if (opt.isSet("r")) {
iter = history.reverseIterator(lastId);
} else {
iter = history.iterator(firstId);
}
while (iter.hasNext() && listed < tot) {
History.Entry entry = iter.next();
listed++;
if (pattern != null && !pattern.matcher(entry.line()).matches()) {
continue;
}
AttributedStringBuilder sb = new AttributedStringBuilder();
sb.append(" ");
sb.styled(AttributedStyle::bold, String.format("%3d", entry.index() + 1));
if (opt.isSet("d")) {
if (!opt.isSet("n")) {
sb.append(" ");
LocalTime lt = LocalTime.from(entry.time().atZone(ZoneId.systemDefault()))
.truncatedTo(ChronoUnit.SECONDS);
DateTimeFormatter.ISO_LOCAL_TIME.formatTo(lt, sb);
sb.styled(AttributedStyle::bold, String.format("%3d", entry.index()));
}
if (opt.isSet("d") || opt.isSet("f") || opt.isSet("E") || opt.isSet("i")) {
sb.append(" ");
if (opt.isSet("d")) {
LocalTime lt = LocalTime.from(entry.time().atZone(ZoneId.systemDefault()))
.truncatedTo(ChronoUnit.SECONDS);
DateTimeFormatter.ISO_LOCAL_TIME.formatTo(lt, sb);
} else {
LocalDateTime lt = LocalDateTime.from(entry.time().atZone(ZoneId.systemDefault())
.truncatedTo(ChronoUnit.MINUTES));
String format = "yyyy-MM-dd hh:mm";
if (opt.isSet("f")) {
format = "MM/dd/yy hh:mm";
} else if (opt.isSet("E")) {
format = "dd.MM.yyyy hh:mm";
}
DateTimeFormatter.ofPattern(format).formatTo(lt, sb);
}
}
sb.append(" ");
sb.append(highlighter.highlight(reader, entry.line()));
out.println(sb.toAnsi(reader.getTerminal()));
}
}

public static void complete(LineReader reader, PrintStream out, PrintStream err,
private static int historyId(int id, int maxId) {
int out = id;
if (id < 0) {
out = maxId + id + 1;
}
if (out < 0) {
out = 0;
} else if (out > maxId) {
out = maxId;
}
return out;
}

private static int parseInteger(String s) throws IllegalArgumentException {
try {
return Integer.parseInt(s);
} catch (NumberFormatException ex) {
throw new IllegalArgumentException();
}
}

public static void complete(LineReader reader, PrintStream out, PrintStream err,
Map<String, List<CompletionData>> completions,
String[] argv) {
final String[] usage = {
Expand Down
2 changes: 1 addition & 1 deletion builtins/src/main/java/org/jline/builtins/Options.java
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,7 @@ else if (needArg != null) {
needArg = null;
needOpt = null;
}
else if (!arg.startsWith("-") || "-".equals(oarg)) {
else if (!arg.startsWith("-") || (arg.length() > 1 && Character.isDigit(arg.charAt(1))) || "-".equals(oarg)) {
if (optionsFirst)
endOpt = true;
xargs.add(oarg);
Expand Down
7 changes: 6 additions & 1 deletion builtins/src/test/java/org/jline/example/Example.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import org.jline.builtins.Commands;
import org.jline.builtins.Completers;
import org.jline.builtins.Completers.TreeCompleter;
import org.jline.keymap.KeyMap;
Expand Down Expand Up @@ -134,7 +135,7 @@ public static void main(String[] args) throws IOException {
case "brackets":
prompt = "long-prompt> ";
DefaultParser p2 = new DefaultParser();
p2.eofOnUnclosedBracket(Bracket.CURLY,Bracket.ROUND,Bracket.SQUARE);
p2.setEofOnUnclosedBracket(Bracket.CURLY, Bracket.ROUND, Bracket.SQUARE);
parser = p2;
break label;
case "foo":
Expand Down Expand Up @@ -305,6 +306,7 @@ public void complete(LineReader reader, ParsedLine line, List<Candidate> candida
break;
}
ParsedLine pl = reader.getParser().parse(line, 0);
String[] argv = pl.words().subList(1, pl.words().size()).toArray(new String[0]);
if ("set".equals(pl.word())) {
if (pl.words().size() == 3) {
reader.setVariable(pl.words().get(1), pl.words().get(2));
Expand Down Expand Up @@ -380,6 +382,9 @@ else if ("cls".equals(pl.word())) {
else if ("sleep".equals(pl.word())) {
Thread.sleep(3000);
}
else if ("history".equals(pl.word())) {
Commands.history(reader, System.out, System.err, argv);
}
}
}
catch (Throwable t) {
Expand Down
21 changes: 21 additions & 0 deletions reader/src/main/java/org/jline/reader/History.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
package org.jline.reader;

import java.io.IOException;
import java.nio.file.Path;
import java.time.Instant;
import java.util.Iterator;
import java.util.ListIterator;
Expand Down Expand Up @@ -41,6 +42,26 @@ public interface History extends Iterable<History.Entry>
*/
void save() throws IOException;

/**
* Write history to the file. If incremental only the events that are new since the last incremental operation to
* the file are added.
* @throws IOException if a problem occurs
*/
void write(Path file, boolean incremental) throws IOException;

/**
* Append history to the file. If incremental only the events that are new since the last incremental operation to
* the file are added.
* @throws IOException if a problem occurs
*/
void append(Path file, boolean incremental) throws IOException;

/**
* Read history from the file. If incremental only the events that are not contained within the internal list are added.
* @throws IOException if a problem occurs
*/
void read(Path file, boolean incremental) throws IOException;

/**
* Purge history.
* @throws IOException if a problem occurs
Expand Down
Loading

0 comments on commit c4b521c

Please sign in to comment.