Commands { public static void history(LineReader reader, PrintStream out, PrintStream err, Path currentDir, String[] argv) throws Exception { final String[] usage = { "history - list history of commands", "Usage: history [-dnrfEie] [-m match] [first] [last]", " history -ARWI [filename]", " history -s [old=new] [command]", " history --clear", " history --save", " -? --help Displays command help", " --clear Clear history", " --save Save history", " -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 or -A, only the events that are new since the last incremental operation", " 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.", " -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")) { throw new HelpException(opt.usage()); } History history = reader.getHistory(); boolean done = true; boolean increment = opt.isSet("I"); if (opt.isSet("clear")) { history.purge(); } else if (opt.isSet("save")) { history.save(); } else if (opt.isSet("A")) { Path file = opt.args().size() > 0 ? currentDir.resolve(opt.args().get(0)) : null; history.append(file, increment); } else if (opt.isSet("R")) { Path file = opt.args().size() > 0 ? currentDir.resolve(opt.args().get(0)) : null; history.read(file, increment); } else if (opt.isSet("W")) { Path file = opt.args().size() > 0 ? currentDir.resolve(opt.args().get(0)) : null; history.write(file, increment); } else { done = false; } if (done) { return; } ReExecute execute = new ReExecute(history, opt); int argId = execute.getArgId(); Pattern pattern = null; if (opt.isSet("m") && opt.args().size() > argId) { StringBuilder sb = new StringBuilder(); char prev = '0'; for (char c: opt.args().get(argId++).toCharArray()) { if (c == '*' && prev != '\\' && prev != '.') { sb.append('.'); } sb.append(c); prev = c; } 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()); if (firstId > lastId) { int tmpId = firstId; firstId = lastId; lastId = tmpId; reverse = !reverse; } int tot = lastId - firstId + 1; int listed = 0; final Highlighter highlighter = reader.getHighlighter(); Iterator<History.Entry> iter = null; if (reverse) { 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; } if (execute.isExecute()) { if (execute.isEdit()) { execute.addCommandInFile(entry.line()); } else { 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); } } sb.append(" "); sb.append(highlighter.highlight(reader, entry.line())); out.println(sb.toAnsi(reader.getTerminal())); } } execute.editCommandsAndClose(reader); }  static void tmux(Terminal terminal, PrintStream out, PrintStream err,
                            Supplier<Object> getter,
                            Consumer<Object> setter,
                            Consumer<Terminal> runner,
                            String[] argv); static void nano(Terminal terminal, PrintStream out, PrintStream err,
            Path currentDir,
            String[] argv); static void nano(Terminal terminal, PrintStream out, PrintStream err,
            Path currentDir,
            String[] argv,
            ConfigurationPath configPath); static void less(Terminal terminal, InputStream in, PrintStream out, PrintStream err,
            Path currentDir,
            String[] argv); static void less(Terminal terminal, InputStream in, PrintStream out, PrintStream err,
                            Path currentDir,
                            String[] argv,
                            ConfigurationPath configPath); static void history(LineReader reader, PrintStream out, PrintStream err, Path currentDir,
                               String[] argv); static void complete(LineReader reader, PrintStream out, PrintStream err,
                                Map<String, List<CompletionData>> completions,
                                String[] argv); static void widget(LineReader reader, PrintStream out, PrintStream err,
                              Function<String, Widget> widgetCreator,
                              String[] argv); static void keymap(LineReader reader,
                              PrintStream out,
                              PrintStream err,
                              String[] argv); static void setopt(LineReader reader,
                              PrintStream out,
                              PrintStream err,
                              String[] argv); static void unsetopt(LineReader reader,
                                PrintStream out,
                                PrintStream err,
                                String[] argv); static void setvar(LineReader lineReader, PrintStream out, PrintStream err, String[] argv);  }
@Test public void testHistoryForFileWithMoreHistoryRecordsThanAtHistoryFileSize() { try { final ByteArrayOutputStream os = new ByteArrayOutputStream(); final File tmpHistoryFile = Files.createTempFile("tmpHistory", "temp").toFile(); tmpHistoryFile.deleteOnExit(); try (BufferedWriter bw = new BufferedWriter( new OutputStreamWriter( new FileOutputStream(tmpHistoryFile)))) { bw.write("1536743099591:SELECT \\n CURRENT_TIMESTAMP \\n as \\n c1;\n" + "1536743104551:SELECT \\n 'asd' as \"sdf\", 4 \\n \\n as \\n c2;\\n\n" + "1536743104551:SELECT \\n 'asd' \\n as \\n c2;\\n\n" + "1536743104551:!/ 2\n" + "1536743104551:SELECT \\n 2123 \\n as \\n c2 from dual;\\n\n" + "1536743107526:!history\n" + "1536743115431:SELECT \\n 2 \\n as \\n c2;\n" + "1536743115431:SELECT \\n '213' \\n as \\n c1;\n" + "1536743115431:!/ 8\n"); bw.flush(); } Terminal terminal = TerminalBuilder.builder().streams(System.in, System.out).build(); terminal.setSize(new Size(50, 30)); final History historyFromFile = new DefaultHistory(); final LineReaderBuilder lineReaderBuilder = LineReaderBuilder.builder() .terminal(terminal) .variable(LineReader.HISTORY_FILE, tmpHistoryFile.getAbsolutePath()) .history(historyFromFile); final LineReader lineReader = lineReaderBuilder.build(); historyFromFile.attach(lineReader); final int maxLines = 3; lineReader.setVariable(LineReader.HISTORY_FILE_SIZE, maxLines); lineReader.getHistory().save(); PrintStream out = new PrintStream(os, false); Commands.history(lineReader, out, out, Paths.get(""), new String[] {"-d"}); assertEquals(maxLines + 1, os.toString("UTF8").split("\\s+\\d{2}:\\d{2}:\\d{2}\\s+").length); } catch (Exception e) { throw new RuntimeException("Test failed", e); } }