Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion fusioncli/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ tasks.jacocoTestCoverageVerification {
violationRules {
rule {
limit {
minimum = "0.40".toBigDecimal()
minimum = "0.45".toBigDecimal()
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,11 @@
Helper forms for the REPL.
'''

(require "/fusion/ffi/java")
(require
"/fusion/private/syntax")

(provide
help
quote_syntax // Needed by ,doc
)

(define help
'''
(help ident ...)

Prints documentation for the given bindings, if available.
'''
(java_new "dev.ionfusion.fusion._Private_HelpForm"))

)
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import com.amazon.ion.IonException;
import dev.ionfusion.fusioncli.framework.CommandSuite;
import dev.ionfusion.fusioncli.repl.cmd.DocCmd;
import dev.ionfusion.fusioncli.repl.cmd.ExitCmd;
import dev.ionfusion.fusioncli.repl.cmd.ReplHelpCmd;
import dev.ionfusion.runtime.base.FusionException;
Expand Down Expand Up @@ -34,8 +35,10 @@ public abstract class RepLoop
myOut = stdout;

CommandSuite commands = new CommandSuite(new ExitCmd(),
new ReplHelpCmd());
ReplContext context = new ReplContext(commands, stdout);
new ReplHelpCmd(),
new DocCmd());
ReplContext context = new ReplContext(commands, myTopLevel, stdout);

myCli = new ReplCli(context);
}

Expand Down Expand Up @@ -70,7 +73,8 @@ private void welcome()
red("\nWelcome to Fusion!\n\n");
myOut.println("Type...");
myOut.println(" ,exit to exit. ^D should work too.");
myOut.println(" ,help for see all REPL commands. Try `,help help`!");
myOut.println(" ,doc to view documentation for a Fusion feature");
myOut.println(" ,help to view a list of more REPL commands. Try `,help help`!");
myOut.println();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,28 @@

import dev.ionfusion.fusioncli.framework.CommandContext;
import dev.ionfusion.fusioncli.framework.CommandSuite;
import dev.ionfusion.runtime.embed.TopLevel;
import java.io.PrintWriter;

public class ReplContext
extends CommandContext
{
private final TopLevel myTopLevel;
protected final PrintWriter myOut;

public ReplContext(CommandSuite suite, PrintWriter out)
public ReplContext(CommandSuite suite, TopLevel topLevel, PrintWriter out)
{
super(suite);
myTopLevel = topLevel;
myOut = out;
}


public TopLevel top()
{
return myTopLevel;
}

public PrintWriter out()
{
return myOut;
Expand Down
130 changes: 130 additions & 0 deletions fusioncli/src/main/java/dev/ionfusion/fusioncli/repl/cmd/DocCmd.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
// Copyright Ion Fusion contributors. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package dev.ionfusion.fusioncli.repl.cmd;

import static dev.ionfusion.fusion.FusionSyntax.isIdentifier;
import static dev.ionfusion.fusion._Private_Trampoline.findBindingDoc;

import dev.ionfusion.fusioncli.framework.Command;
import dev.ionfusion.fusioncli.framework.Executor;
import dev.ionfusion.fusioncli.framework.UsageException;
import dev.ionfusion.fusioncli.repl.ReplContext;
import dev.ionfusion.fusioncli.repl.ReplExecutor;
import dev.ionfusion.runtime._private.doc.BindingDoc;
import dev.ionfusion.runtime.base.FusionException;
import dev.ionfusion.runtime.embed.TopLevel;
import java.io.PrintWriter;

public class DocCmd
extends Command<ReplContext>
{
private static final String HELP_ONE_LINER =
"Print documentation for a given identifier";

private static final String HELP_USAGE =
"doc IDENTIFIER";

private static final String HELP_BODY =
"Resolves the given identifier in the current namespace and prints any associated\n" +
"documentation.";


public DocCmd()
{
super("doc");
putHelpText(HELP_ONE_LINER, HELP_USAGE, HELP_BODY);
}

@Override
public Executor makeExecutor(ReplContext replContext, String[] args)
throws UsageException
{
assert args.length < 2;
if (args.length == 0)
{
throw usage("Expected an identifier");
}

return new DocExecutor(replContext, args[0]);
}


private class DocExecutor
extends ReplExecutor
{
private final String myArg;

private DocExecutor(ReplContext replContext, String arg)
{
super(replContext);
myArg = arg;
}

@Override
public int execute()
throws Exception
{
Object id = determineIdentifier();
displayDoc(id);

return 0;
}

private Object determineIdentifier()
throws Exception
{
TopLevel top = context().top();

// TODO This assumes the normal binding of `quote_syntax`

Object stx;
try
{
stx = top.eval("(quote_syntax " + myArg + ")");
}
catch (FusionException e)
{
throw usage("Expected an identifier");
}

if (! isIdentifier(top, stx))
{
throw usage("Expected an identifier");
}

return stx;
}

private void displayDoc(Object id)
{
PrintWriter out = context().out();

BindingDoc doc = findBindingDoc(context().top(), id);
if (doc == null)
{
out.println("No documentation available.");
return;
}

if (doc.getKind() != null)
{
out.append("[");
// Using enum toString() allows display name to be changed
out.append(doc.getKind().toString());
out.append("] ");
}
if (doc.getUsage() != null)
{
out.append(doc.getUsage());
}

if (doc.getBody() != null)
{
out.append('\n');
out.append(doc.getBody());
out.append('\n');
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
public class ExitCmd
extends Command<ReplContext>
{
static final String HELP_ONE_LINER =
private static final String HELP_ONE_LINER =
"Exit the REPL";

private static final String HELP_USAGE =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
public class ReplHelpCmd
extends Command<ReplContext>
{
static final String HELP_ONE_LINER =
private static final String HELP_ONE_LINER =
"Describe available REPL commands";

private static final String HELP_USAGE =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Copyright Ion Fusion contributors. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package dev.ionfusion.fusioncli.repl;

import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;

public class ReplDocTest
extends ReplTestCase
{
@Test
public void docWithoutArg()
throws Exception
{
supplyInput(",doc\n");
runRepl();

expectError("Expected an identifier");
}


@Test
public void docForUnbound()
throws Exception
{
supplyInput(",doc unbound_id\n");
runRepl();

// TODO Distinguish between unbound vars and undocumented bindings.
expectError("No documentation available.");
}


@Test
public void docForBuiltin()
throws Exception
{
supplyInput(",doc * \n"); // Extra space is intentional.
runRepl();

expectResponse("Returns the product");
}

@Test
@Disabled("https://github.com/ion-fusion/fusion-java/issues/510")
public void docForLocal()
throws Exception
{
supplyInput("(define (local) '''local docs''' true)\n");
supplyInput(",doc local\n");
runRepl();

expectResponse("local docs");
}

@Test
public void docForBadSyntax()
throws Exception
{
supplyInput(",doc [\n"); // Extra space is intentional.
runRepl();

expectError("Expected an identifier");
}


@Test
public void docForNonIdentifier()
throws Exception
{
supplyInput(",doc 12\n"); // Extra space is intentional.
runRepl();

expectError("Expected an identifier");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,17 +69,6 @@ public void testFusionSyntaxError()
}


@Test
public void testHelpHelp()
throws Exception
{
supplyInput("(help help)\n");
runRepl();

expectResponse("(help ident ...)");
}


//==================================================================================
// Basic comma-commands

Expand Down
Loading
Loading