Skip to content

Commit

Permalink
history command: implemented options -e and -s
Browse files Browse the repository at this point in the history
  • Loading branch information
mattirn committed Sep 2, 2019
1 parent 93f63bc commit 2eb14ad
Show file tree
Hide file tree
Showing 7 changed files with 208 additions and 61 deletions.
154 changes: 127 additions & 27 deletions builtins/src/main/java/org/jline/builtins/Commands.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
*/
package org.jline.builtins;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
Expand Down Expand Up @@ -186,8 +190,9 @@ public static void history(LineReader reader, PrintStream out, PrintStream err,
String[] argv) throws Exception {
final String[] usage = {
"history - list history of commands",
"Usage: history [-dnrfEi] [-m match] [first] [last]",
"Usage: history [-dnrfEie] [-m match] [first] [last]",
" history -ARWI [filename]",
" history -s [old=new] [command]",
" history --clear",
" history --save",
" -? --help Displays command help",
Expand All @@ -209,7 +214,9 @@ public static void history(LineReader reader, PrintStream out, PrintStream err,
" to the file are added",
" [first] [last] These optional arguments may be specified as a number or as a string. A negative number",
" is used as an offset to the current history event number. A string specifies the most",
" recent event beginning with the given string."};
" recent event beginning with the given string.",
" -e Uses the nano editor to edit the commands before executing",
" -s Re-executes the command without invoking an editor"};
Options opt = Options.compile(usage).parse(argv);

if (opt.isSet("help")) {
Expand Down Expand Up @@ -237,9 +244,11 @@ public static void history(LineReader reader, PrintStream out, PrintStream err,
if (done) {
return;
}
int argId = 0;
ReExecute execute = new ReExecute(history, opt);
int argId = execute.getArgId();

Pattern pattern = null;
if (opt.isSet("m") && opt.args().size() > 0) {
if (opt.isSet("m") && opt.args().size() > argId) {
StringBuilder sb = new StringBuilder();
char prev = '0';
for (char c: opt.args().get(argId++).toCharArray()) {
Expand All @@ -251,11 +260,11 @@ public static void history(LineReader reader, PrintStream out, PrintStream err,
}
pattern = Pattern.compile(sb.toString(), Pattern.DOTALL);
}
boolean reverse = opt.isSet("r") || (opt.isSet("s") && opt.args().size() <= argId);
int firstId = opt.args().size() > argId ? retrieveHistoryId(history, opt.args().get(argId++)) : -17;
int lastId = opt.args().size() > argId ? retrieveHistoryId(history, opt.args().get(argId++)) : -1;
firstId = historyId(firstId, history.first(), history.last());
lastId = historyId(lastId, history.first(), history.last());
boolean reverse = opt.isSet("r");
if (firstId > lastId) {
int tmpId = firstId;
firstId = lastId;
Expand All @@ -271,41 +280,132 @@ public static void history(LineReader reader, PrintStream out, PrintStream err,
} 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();
if (!opt.isSet("n")) {
sb.append(" ");
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);
if (execute.isExecute()) {
if (execute.isEdit()) {
execute.addCommandInFile(entry.line());
} 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";
execute.addCommandInBuffer(reader, entry.line());
break;
}
} else {
AttributedStringBuilder sb = new AttributedStringBuilder();
if (!opt.isSet("n")) {
sb.append(" ");
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);
}
DateTimeFormatter.ofPattern(format).formatTo(lt, sb);
}
sb.append(" ");
sb.append(highlighter.highlight(reader, entry.line()));
out.println(sb.toAnsi(reader.getTerminal()));
}
sb.append(" ");
sb.append(highlighter.highlight(reader, entry.line()));
out.println(sb.toAnsi(reader.getTerminal()));
}
execute.editCommandsAndClose(reader);
}

private static class ReExecute {
private final boolean execute;
private final boolean edit;
private String oldParam;
private String newParam;
private FileWriter cmdWriter;
private File cmdFile;
private int argId = 0;

public ReExecute(History history, Options opt) throws IOException {
execute = opt.isSet("e") || opt.isSet("s");
edit = opt.isSet("e");
if (execute) {
Iterator<History.Entry> iter = history.reverseIterator(history.last());
if (iter.hasNext()) {
iter.next();
iter.remove();
}
if (edit) {
cmdFile = File.createTempFile("jline-history-", null);
cmdWriter = new FileWriter(cmdFile);
} else if (opt.args().size() > 0 ) {
String[] s = opt.args().get(argId).split("=");
if (s.length == 2) {
argId = argId + 1;
oldParam = s[0];
newParam = s[1];
}
}
}
}

public int getArgId() {
return argId;
}

public boolean isEdit() {
return edit;
}

public boolean isExecute() {
return execute;
}

public void addCommandInFile(String command) throws IOException {
cmdWriter.write(command + "\n");
}

public void addCommandInBuffer(LineReader reader, String command) {
reader.addCommandsInBuffer(Arrays.asList(replaceParam(command)));
}

private String replaceParam(String command) {
String out = command;
if (oldParam != null && newParam != null) {
out = command.replaceAll(oldParam, newParam);
}
return out;
}

public void editCommandsAndClose(LineReader reader) throws IOException {
if (edit) {
cmdWriter.close();
Nano editor = new Nano(reader.getTerminal(), new File(cmdFile.getParent()));
editor.setRestricted(true);
editor.open(Arrays.asList(cmdFile.getName()));
editor.run();
List<String> commands = new ArrayList<>();
BufferedReader br = new BufferedReader(new FileReader(cmdFile));
String line;
while ((line = br.readLine()) != null) {
commands.add(line);
}
br.close();
reader.addCommandsInBuffer(commands);
cmdFile.delete();
}
}
}

private static int historyId(int id, int minId, int maxId) {
int out = id;
if (id < 0) {
Expand Down
6 changes: 5 additions & 1 deletion builtins/src/main/java/org/jline/builtins/Nano.java
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ public class Nano {
protected final BindingReader bindingReader;
protected final Size size;
protected final Path root;
protected final boolean restricted;
protected final int vsusp;

// Keys
Expand Down Expand Up @@ -109,6 +108,7 @@ public class Nano {
protected boolean searchCaseSensitive;
protected boolean searchRegexp;
protected boolean searchBackwards;
protected boolean restricted;
protected String searchTerm;
protected List<String> searchTerms = new ArrayList<>();
protected int searchTermId = -1;
Expand Down Expand Up @@ -961,6 +961,10 @@ public Nano(Terminal terminal, Path root, Options opts) {
bindKeys();
}

public void setRestricted(boolean restricted) {
this.restricted = restricted;
}

public void open(String... files) throws IOException {
open(Arrays.asList(files));
}
Expand Down
53 changes: 23 additions & 30 deletions builtins/src/test/java/org/jline/example/Example.java
Original file line number Diff line number Diff line change
Expand Up @@ -340,38 +340,28 @@ public void complete(LineReader reader, ParsedLine line, List<Candidate> candida
String line = null;
try {
line = reader.readLine(prompt, rightPrompt, (MaskingCallback) null, null);
} catch (UserInterruptException e) {
// Ignore
} catch (EndOfFileException e) {
return;
}
if (line == null) {
continue;
}

line = line.trim();
line = line.trim();

if (color) {
terminal.writer().println(
if (color) {
terminal.writer().println(
AttributedString.fromAnsi("\u001B[33m======>\u001B[0m\"" + line + "\"")
.toAnsi(terminal));

} else {
terminal.writer().println("======>\"" + line + "\"");
}
terminal.flush();
} else {
terminal.writer().println("======>\"" + line + "\"");
}
terminal.flush();

// If we input the special word then we will mask
// the next line.
if ((trigger != null) && (line.compareTo(trigger) == 0)) {
line = reader.readLine("password> ", mask);
}
if (line.equalsIgnoreCase("quit") || line.equalsIgnoreCase("exit")) {
break;
}
ParsedLine pl = reader.getParser().parse(line, 0);
String[] argv = pl.words().subList(1, pl.words().size()).toArray(new String[0]);
try {
// If we input the special word then we will mask
// the next line.
if ((trigger != null) && (line.compareTo(trigger) == 0)) {
line = reader.readLine("password> ", mask);
}
if (line.equalsIgnoreCase("quit") || line.equalsIgnoreCase("exit")) {
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 @@ -410,9 +400,6 @@ else if ("cls".equals(pl.word())) {
else if ("sleep".equals(pl.word())) {
Thread.sleep(3000);
}
//
// builtin commands are added in order to test HelpPrinter class
//
else if ("tmux".equals(pl.word())) {
Commands.tmux(terminal, System.out, System.err,
null, //Supplier<Object> getter,
Expand Down Expand Up @@ -465,6 +452,12 @@ else if ("help".equals(pl.word()) || "?".equals(pl.word())) {
catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
}
catch (UserInterruptException e) {
// Ignore
}
catch (EndOfFileException e) {
return;
}
}
}
catch (Throwable t) {
Expand Down
10 changes: 10 additions & 0 deletions reader/src/main/java/org/jline/reader/History.java
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,11 @@ public boolean hasNext() {
public Entry next() {
return it.previous();
}
@Override
public void remove() {
it.remove();
resetIndex();
}
};
}

Expand Down Expand Up @@ -191,4 +196,9 @@ public Entry next() {
* all of the other iterator.
*/
void moveToEnd();

/**
* Reset index after remove
*/
void resetIndex();
}
3 changes: 3 additions & 0 deletions reader/src/main/java/org/jline/reader/LineReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
package org.jline.reader;

import java.io.InputStream;
import java.util.Collection;
import java.util.Map;
import java.util.function.IntConsumer;

Expand Down Expand Up @@ -655,4 +656,6 @@ enum RegionType {

int getRegionMark();

void addCommandsInBuffer(Collection<String> commands);

}
Loading

0 comments on commit 2eb14ad

Please sign in to comment.