diff --git a/builtins/src/main/java/org/jline/builtins/Less.java b/builtins/src/main/java/org/jline/builtins/Less.java index a92cd8b6a..28314f502 100644 --- a/builtins/src/main/java/org/jline/builtins/Less.java +++ b/builtins/src/main/java/org/jline/builtins/Less.java @@ -23,6 +23,7 @@ import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; +import org.jline.builtins.Source.ResourceSource; import org.jline.keymap.BindingReader; import org.jline.keymap.KeyMap; import org.jline.terminal.Attributes; @@ -113,15 +114,16 @@ public void run(List sources) throws IOException, InterruptedException { if (sources == null || sources.isEmpty()) { throw new IllegalArgumentException("No sources"); } + sources.add(0, new ResourceSource("less-help.txt", "HELP -- Press SPACE for more, or q when done")); this.sources = sources; - sourceIdx = 0; + sourceIdx = 1; openSource(); try { size.copy(terminal.getSize()); - if (quitIfOneScreen && sources.size() == 1) { + if (quitIfOneScreen && sources.size() == 2) { if (display(true)) { return; } @@ -363,13 +365,16 @@ else if (buffer.length() > 0 && (buffer.charAt(0) == '/' || buffer.charAt(0) == } break; case PREV_FILE: - if (sourceIdx > 0) { + if (sourceIdx > 1) { sourceIdx--; openSource(); } else { message = "No previous file"; } break; + case HELP: + help(); + break; } buffer.setLength(0); } @@ -404,16 +409,56 @@ else if (buffer.length() > 0 && (buffer.charAt(0) == '/' || buffer.charAt(0) == } } + private void help() throws IOException { + int saveSourceIdx = sourceIdx; + int saveFirstLineToDisplay = firstLineToDisplay; + int saveFirstColumnToDisplay = firstColumnToDisplay; + int saveOffsetInLine = offsetInLine; + boolean savePrintLineNumbers = printLineNumbers; + + printLineNumbers = false; + sourceIdx = 0; + try { + openSource(); + Operation op = null; + do { + checkInterrupted(); + + op = bindingReader.readBinding(keys, null, false); + if (op != null) { + switch (op) { + case FORWARD_ONE_WINDOW_OR_LINES: + moveForward(getStrictPositiveNumberInBuffer(window)); + break; + case BACKWARD_ONE_WINDOW_OR_LINES: + moveBackward(getStrictPositiveNumberInBuffer(window)); + break; + } + } + display(false); + } while (op != Operation.EXIT); + } catch (IOException|InterruptedException exp) { + // Do nothing + } finally { + sourceIdx = saveSourceIdx; + openSource(); + firstLineToDisplay = saveFirstLineToDisplay; + firstColumnToDisplay = saveFirstColumnToDisplay; + offsetInLine = saveOffsetInLine; + printLineNumbers = savePrintLineNumbers; + } + } + protected void openSource() throws IOException { if (reader != null) { reader.close(); } Source source = sources.get(sourceIdx); InputStream in = source.read(); - if (sources.size() == 1) { + if (sources.size() == 2 || sourceIdx == 0) { message = source.getName(); } else { - message = source.getName() + " (file " + (sourceIdx + 1) + " of " + sources.size() + ")"; + message = source.getName() + " (file " + sourceIdx + " of " + (sources.size() - 1)+ ")"; } reader = new BufferedReader(new InputStreamReader(new InterruptibleInputStream(in))); firstLineInMemory = 0; @@ -531,7 +576,7 @@ void moveBackward(int lines) throws IOException { private void eof() { nbEof++; - if (sourceIdx < sources.size() - 1) { + if (sourceIdx > 0 && sourceIdx < sources.size() - 1) { message = "(END) - Next: " + sources.get(sourceIdx + 1).getName(); } else { message = "(END)"; diff --git a/builtins/src/main/java/org/jline/builtins/Source.java b/builtins/src/main/java/org/jline/builtins/Source.java index 47d52ae47..c26c0826e 100644 --- a/builtins/src/main/java/org/jline/builtins/Source.java +++ b/builtins/src/main/java/org/jline/builtins/Source.java @@ -109,4 +109,28 @@ public StdInSource(InputStream in) { } } + + class ResourceSource implements Source { + final String resource; + final String name; + + public ResourceSource(String resource) { + this(resource, resource); + } + + public ResourceSource(String resource, String name) { + this.resource = Objects.requireNonNull(resource); + this.name = name; + } + + @Override + public String getName() { + return name; + } + + @Override + public InputStream read() throws IOException { + return getClass().getResourceAsStream(resource); + } + } } diff --git a/builtins/src/main/resources/org/jline/builtins/less-help.txt b/builtins/src/main/resources/org/jline/builtins/less-help.txt new file mode 100644 index 000000000..9e2505a4e --- /dev/null +++ b/builtins/src/main/resources/org/jline/builtins/less-help.txt @@ -0,0 +1,77 @@ + + SUMMARY OF LESS COMMANDS + + Commands marked with * may be preceded by a number, N. + Notes in parentheses indicate the behavior if N is given. + A key preceded by a caret indicates the Ctrl key; thus ^K is ctrl-K. + + h H Display this help. + q :q Q :Q ZZ Exit. + --------------------------------------------------------------------------- + + MOVING + + e ^E j ^N CR * Forward one line (or N lines). + y ^Y k ^K ^P * Backward one line (or N lines). + f ^F ^V SPACE * Forward one window (or N lines). + b ^B ESC-v * Backward one window (or N lines). + z * Forward one window (and set window to N). + w * Backward one window (and set window to N). + ESC-SPACE * Forward one window, but don't stop at end-of-file. + d ^D * Forward one half-window (and set half-window to N). + u ^U * Backward one half-window (and set half-window to N). + ESC-) RightArrow * Left one half screen width (or N positions). + ESC-( LeftArrow * Right one half screen width (or N positions). + g < ESC-< * Go to first line in file (or line N). + G > ESC-> * Go to last line in file (or line N). + --------------------------------------------------- + Default "window" is the screen height. + Default "half-window" is half of the screen height. + --------------------------------------------------------------------------- + + SEARCHING + + /pattern * Search forward for (N-th) matching line. + ?pattern * Search backward for (N-th) matching line. + n N * Repeat previous search (for N-th occurrence). + ESC-n ESC-N * Repeat previous search, spanning files. + ESC-u Undo (toggle) search highlighting. + --------------------------------------------------------------------------- + + CHANGING FILES + + :n * Examine the (N-th) next file from the command line. + :p * Examine the (N-th) previous file from the command line. + --------------------------------------------------------------------------- + + MISCELLANEOUS COMMANDS + + - Toggle a command line option [see OPTIONS below]. + -- Toggle a command line option, by name. + --------------------------------------------------------------------------- + + OPTIONS + + Most options may be changed either on the command line, + or from within less by using the - or -- command. + Options may be given in one of two forms: either a single + character preceded by a -, or a name preceded by --. + + -? ........ --help + Display help (from command line). + -e ........ --quit-at-eof + Quit at second end of file. + -E ........ --QUIT-AT-EOF + Quit at end of file. + -i ........ --ignore-case + Ignore case in searches that do not contain uppercase. + -I ........ --IGNORE-CASE + Ignore case in all searches. + -N ........ --LINE-NUMBERS + Display line numbers. + -q -Q .... --quiet --QUIET --silent --SILENT + Quiet the terminal bell. + -S ........ --chop-long-lines + Chop (truncate) long lines rather than wrapping. + -x [N[,...]] --tabs=[N[,...]] + Set tab stops (from command line).