com.redhat.ceylon.common.tools.help.CeylonHelpTool Maven / Gradle / Ivy
/*
* Copyright Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the authors tag. All rights reserved.
*
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU General Public License version 2.
*
* This particular file is subject to the "Classpath" exception as provided in the
* LICENSE file that accompanied this code.
*
* This program is distributed in the hope that it will be useful, but WITHOUT A
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU General Public License for more details.
* You should have received a copy of the GNU General Public License,
* along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
package com.redhat.ceylon.common.tools.help;
import java.awt.Desktop;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.lang.ProcessBuilder.Redirect;
import java.util.Arrays;
import java.util.HashSet;
import com.redhat.ceylon.common.Constants;
import com.redhat.ceylon.common.OSUtil;
import com.redhat.ceylon.common.config.CeylonConfig;
import com.redhat.ceylon.common.tool.Argument;
import com.redhat.ceylon.common.tool.Description;
import com.redhat.ceylon.common.tool.Hidden;
import com.redhat.ceylon.common.tool.Option;
import com.redhat.ceylon.common.tool.OptionArgument;
import com.redhat.ceylon.common.tool.RemainingSections;
import com.redhat.ceylon.common.tool.Summary;
import com.redhat.ceylon.common.tool.Tool;
import com.redhat.ceylon.common.tool.ToolLoader;
import com.redhat.ceylon.common.tool.ToolModel;
import com.redhat.ceylon.common.tool.ToolUsageError;
import com.redhat.ceylon.common.tool.WordWrap;
import com.redhat.ceylon.common.tools.CeylonTool;
import com.redhat.ceylon.common.tools.help.model.Doc;
import com.redhat.ceylon.common.tools.help.model.Visitor;
/**
* A plugin which provides help about other plugins
* @author tom
*/
@Summary("Displays help information about other Ceylon tools")
@Description(
"If a `` is given, displays help about that ceylon tool on the standard output.\n\n" +
"If no `` is given, displays the synopsis of the top level `ceylon` command. "
)
@RemainingSections(
"## SEE ALSO\n\n" +
"* `ceylon doc-tool` for generating documentation about ceylon tools\n"
)
public class CeylonHelpTool implements Tool {
private Appendable out;
private boolean includeHidden;
private ToolLoader toolLoader;
private DocBuilder docBuilder;
private ToolModel> tool;
private boolean synopsis = false;
private String options = null;
private boolean wantsPager;
public final void setToolLoader(ToolLoader toolLoader) {
this.toolLoader = toolLoader;
this.docBuilder = new DocBuilder(toolLoader);
}
@Option
public void setIncludeHidden(boolean includeHidden) {
this.includeHidden = includeHidden;
}
@Hidden
@Option
@Description("Used to generate a synopsis when another tool was invoked incorrectly")
public void setSynopsis(boolean synopsis) {
this.synopsis = synopsis;
}
@Hidden
@Option
@OptionArgument
@Description("Used to generate doc on a given option (or options)")
public void setOptions(String options) {
this.options = options;
}
@Argument(argumentName="tool", multiplicity="?")
public void setTool(ToolModel> tool) {
this.tool = tool;
}
public void setOut(Appendable out) {
this.out = out;
}
@Override
public void initialize(CeylonTool mainTool) {
Boolean wantsPager = mainTool.getWantsPager();
// tool-specific option
if(wantsPager == null){
String option = CeylonConfig.get().getOption("help.pager");
if(option != null){
if(CeylonConfig.isFalsish(option))
wantsPager = Boolean.FALSE;
else // any other value is either true or a pager command
wantsPager = Boolean.TRUE;
}
}
// general option
if(wantsPager == null){
String option = CeylonConfig.get().getOption("defaults.pager");
if(option != null){
if(CeylonConfig.isFalsish(option))
wantsPager = Boolean.FALSE;
else // any other value is either true or a pager command
wantsPager = Boolean.TRUE;
}
}
// allow pager if it's unspecified (default) or if it's not explicitly disabled
if((wantsPager == null
|| wantsPager.booleanValue())
// and if stdout is on a tty
// on this we differ with git which only tests isatty(stdout) and System.console() will return null
// if stdout is on a tty but stdin on a pipe, but I've to say that I can't understand what a pager
// is good for if stdin is not on a tty...
&& System.console() != null){
this.wantsPager = true;
}
}
@Override
public void run() {
if(wantsPager && OSUtil.isWindows() && tool != null){
if (openHelpInBrowser()){
return;
}
}
docBuilder.setIncludeHidden(includeHidden);
Doc doc;
if (tool != null) {
doc = docBuilder.buildDoc(tool);
} else {
final ToolModel root = toolLoader.loadToolModel("");
doc = docBuilder.buildDoc(root, true);
}
Process pagerProcess = null;
if(wantsPager && !OSUtil.isWindows()){
pagerProcess = startPager();
}
OutputStream pagerPipe = null;
if(pagerProcess != null)
pagerPipe = pagerProcess.getOutputStream();
try{
final WordWrap wrap = getWrap(pagerPipe);
Visitor plain = new PlainVisitor(wrap);
if (synopsis) {
plain = new SynopsisOnlyVisitor(plain);
} else if (options != null) {
plain = new OptionsOnlyVisitor(plain,
new HashSet(Arrays.asList(options.trim().split("\\s*,\\s*"))));
}
doc.accept(plain);
wrap.flush();
}finally{
// shutdown pager
if(pagerPipe != null){
try {
pagerPipe.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// wait for pager to be done, there's no point doing anything else meanwhile
try {
int errorCode = pagerProcess.waitFor();
if(errorCode != 0){
throw new ToolUsageError("Pager process returned an error exit code: "+errorCode+". Try fixing your $CEYLON_PAGER or $PAGER environment variable or invoke with the --no-pager command-line option.");
}
} catch (InterruptedException e) {
throw new ToolUsageError("Pager process interrupted. Try fixing your $CEYLON_PAGER or $PAGER environment variable or invoke with the --no-pager command-line option.");
}
}
}
}
/** this is only for windows **/
private boolean openHelpInBrowser() {
String ceylonHome = System.getProperty(Constants.PROP_CEYLON_HOME_DIR);
if(ceylonHome == null || ceylonHome.isEmpty())
return false;
String toolDoc = "ceylon";
if(tool.getName() != null && !tool.getName().isEmpty())
toolDoc += "-"+tool.getName();
toolDoc += ".html";
File docHome = new File(ceylonHome, "doc/en/toolset/");
File toolDocFile = new File(docHome, toolDoc);
if(toolDocFile.exists() && toolDocFile.isFile()){
try {
Desktop.getDesktop().browse(toolDocFile.toURI());
} catch (Exception e) {
throw new ToolUsageError("Could not open browser for uri '"+toolDocFile.toURI()+"'. Please invoke with the --no-pager command-line option.");
}
return true;
}else
return false;
}
private Process startPager() {
// find out the pager process, same process as in git
// first try ceylon-specific pager env var
String pager = System.getenv("CEYLON_PAGER");
if(pager == null){
// first try tool-specific option
String option = CeylonConfig.get().getOption("help.pager");
// if it's neither true nor false it's a pager program
if(option != null
&& !CeylonConfig.isTrueish(option)
&& !CeylonConfig.isFalsish(option))
pager = option;
}
if(pager == null){
// then try general option
String option = CeylonConfig.get().getOption("defaults.pager");
// if it's neither true nor false it's a pager program
if(option != null
&& !CeylonConfig.isTrueish(option)
&& !CeylonConfig.isFalsish(option))
pager = option;
}
// then try general pager env var
if(pager == null)
pager = System.getenv("PAGER");
// default to less
if(pager == null)
pager = "less";
// make sure we don't page for cat
if(!pager.equals("cat")){
// start the pager process
ProcessBuilder pb = new ProcessBuilder(pager);
pb.redirectError(Redirect.INHERIT);
pb.redirectOutput(Redirect.INHERIT);
try {
return pb.start();
} catch (IOException e) {
throw new ToolUsageError("Could not start pager process '"+pager+"'. Try fixing your $CEYLON_PAGER or $PAGER environment variable or invoke with the --no-pager command-line option.", e);
}
}
return null;
}
private WordWrap getWrap(OutputStream pagerPipe) {
Appendable destination = null;
if(pagerPipe != null)
destination = new PrintStream(pagerPipe);
if(destination == null)
destination = out;
if(destination == null)
destination = synopsis || options != null ? System.err : System.out;
return new WordWrap(destination);
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy