diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c60b8e6ba5f..5bbcc69f85dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ Compatibility: * Define `Process::{CLOCK_BOOTTIME,CLOCK_BOOTTIME_ALARM,CLOCK_REALTIME_ALARM}` (#1480, @eregon). * Improve support of `:chomp` keyword argument in `IO` and `StringIO` methods (#2650, @andrykonchin). * Implement specializations for immutable ruby objects for ObjectSpace methods (@bjfish). +* Use `$PAGER` for `--help` and `--help*`, similar to CRuby (#2542, @Strech). Performance: diff --git a/src/launcher/java/org/truffleruby/launcher/CommandLineParser.java b/src/launcher/java/org/truffleruby/launcher/CommandLineParser.java index d66263648ec8..c56ac7d3ae54 100644 --- a/src/launcher/java/org/truffleruby/launcher/CommandLineParser.java +++ b/src/launcher/java/org/truffleruby/launcher/CommandLineParser.java @@ -432,8 +432,10 @@ private void processArgument() throws CommandLineException { disallowedInRubyOpts(argument); warnInternalDebugTool(argument); break FOR; - } else if (rubyOpts && argument.equals("--help")) { + } else if (argument.equals("--help")) { disallowedInRubyOpts(argument); + // --help is handled by org.graalvm.launcher.Launcher#printDefaultHelp + config.getUnknownArguments().add(argument); break FOR; } else if (argument.equals("--version")) { disallowedInRubyOpts(argument); diff --git a/src/launcher/java/org/truffleruby/launcher/RubyLauncher.java b/src/launcher/java/org/truffleruby/launcher/RubyLauncher.java index 981222e4e5ee..4a38ee7a51b2 100644 --- a/src/launcher/java/org/truffleruby/launcher/RubyLauncher.java +++ b/src/launcher/java/org/truffleruby/launcher/RubyLauncher.java @@ -10,12 +10,14 @@ package org.truffleruby.launcher; import java.io.PrintStream; +import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; +import java.lang.ProcessBuilder.Redirect; import org.graalvm.launcher.AbstractLanguageLauncher; import org.graalvm.nativeimage.ProcessProperties; @@ -33,6 +35,7 @@ public class RubyLauncher extends AbstractLanguageLauncher { private CommandLineOptions config; private String implementationName = null; + private boolean helpOptionUsed = false; // Any --help* option public static void main(String[] args) { new RubyLauncher().launch(args); @@ -54,8 +57,8 @@ protected void validateArguments(Map polyglotOptions) { @Override protected void printVersion() { - System.out.println(TruffleRuby.getVersionString(getImplementationNameFromEngine())); - System.out.println(); + getOutput().println(TruffleRuby.getVersionString(getImplementationNameFromEngine())); + getOutput().println(); printPolyglotVersions(); } @@ -109,9 +112,9 @@ protected List preprocessArguments(List args, Map options) { @Override protected void printHelp(OptionCategory maxCategory) { - printHelp(System.out); + printHelp(getOutput()); } @Override @@ -173,6 +176,43 @@ protected AbortException abortUnrecognizedArgument(String argument) { "truffleruby: invalid option " + argument + " (Use --help for usage instructions.)"); } + @Override + protected boolean parseCommonOption(String defaultOptionPrefix, Map polyglotOptions, + boolean experimentalOptions, String arg) { + if (arg.startsWith("--help")) { + helpOptionUsed = true; + } + + return super.parseCommonOption(defaultOptionPrefix, polyglotOptions, experimentalOptions, arg); + } + + @Override + protected boolean runLauncherAction() { + String pager; + if (helpOptionUsed && System.console() != null && !(pager = getPagerFromEnv()).isEmpty()) { + try { + Process process = new ProcessBuilder(pager.split(" ")) + .redirectOutput(Redirect.INHERIT) // set the output of the pager to the terminal and not a pipe + .redirectError(Redirect.INHERIT) // set the error of the pager to the terminal and not a pipe + .start(); + PrintStream out = new PrintStream(process.getOutputStream()); + + setOutput(out); + boolean code = super.runLauncherAction(); + + out.flush(); + out.close(); + process.waitFor(); + + return code; + } catch (IOException | InterruptedException e) { + throw abort(e); + } + } else { + return super.runLauncherAction(); + } + } + private int runRubyMain(Context.Builder contextBuilder, CommandLineOptions config) { if (config.executionAction == ExecutionAction.UNSET) { switch (config.defaultExecutionAction) { @@ -184,7 +224,7 @@ private int runRubyMain(Context.Builder contextBuilder, CommandLineOptions confi case IRB: config.executionAction = ExecutionAction.PATH; if (System.console() != null) { - System.err.println( + getError().println( "[ruby] WARNING: truffleruby starts IRB when stdin is a TTY instead of reading from stdin, use '-' to read from stdin"); config.executionAction = ExecutionAction.PATH; config.toExecute = "irb"; @@ -203,7 +243,7 @@ private int runRubyMain(Context.Builder contextBuilder, CommandLineOptions confi // Apply options to run gem/bundle more efficiently contextBuilder.option("engine.Mode", "latency"); if (Boolean.getBoolean("truffleruby.launcher.log")) { - System.err.println("[ruby] CONFIG: detected gem or bundle command, using --engine.Mode=latency"); + getError().println("[ruby] CONFIG: detected gem or bundle command, using --engine.Mode=latency"); } } @@ -241,7 +281,7 @@ private int runContext(Context.Builder builder, CommandLineOptions config) { if (file.isString()) { config.toExecute = file.asString(); } else { - System.err + getError() .println("truffleruby: No such file or directory -- " + config.toExecute + " (LoadError)"); return 1; } @@ -270,9 +310,9 @@ private int runContext(Context.Builder builder, CommandLineOptions config) { return exitCode; } catch (PolyglotException e) { if (e.isHostException()) { // GR-22071 - System.err.println("truffleruby: a host exception reached the top level:"); + getError().println("truffleruby: a host exception reached the top level:"); } else { - System.err.println( + getError().println( "truffleruby: an exception escaped out of the interpreter - this is an implementation bug"); } e.printStackTrace(); @@ -299,24 +339,36 @@ private static List getPathListFromEnvVariable(String name) { return Collections.emptyList(); } + private static String getPagerFromEnv() { + String pager = System.getenv("RUBY_PAGER"); + if (pager != null) { + return pager.strip(); + } + + pager = System.getenv("PAGER"); + if (pager != null) { + return pager.strip(); + } + + return ""; + } + private void printPreRunInformation(CommandLineOptions config) { if (config.showVersion) { - System.out.println(TruffleRuby.getVersionString(getImplementationNameFromEngine())); + getOutput().println(TruffleRuby.getVersionString(getImplementationNameFromEngine())); } if (config.showCopyright) { - System.out.println(TruffleRuby.RUBY_COPYRIGHT); + getOutput().println(TruffleRuby.RUBY_COPYRIGHT); } switch (config.showHelp) { case NONE: break; case SHORT: - printShortHelp(System.out); - break; - case LONG: - printHelp(System.out); + printShortHelp(getOutput()); break; + // --help is handled by org.graalvm.launcher.Launcher#printDefaultHelp } } diff --git a/src/launcher/java/org/truffleruby/launcher/ShowHelp.java b/src/launcher/java/org/truffleruby/launcher/ShowHelp.java index 7eee8864d14e..c669d1dbe651 100644 --- a/src/launcher/java/org/truffleruby/launcher/ShowHelp.java +++ b/src/launcher/java/org/truffleruby/launcher/ShowHelp.java @@ -11,6 +11,6 @@ public enum ShowHelp { NONE, - SHORT, - LONG + SHORT // -h + // LONG // --help is handled by org.graalvm.launcher.Launcher#printDefaultHelp }