Skip to content

Commit

Permalink
Merge pull request #43 from spoofax-shell/error-highlight
Browse files Browse the repository at this point in the history
[RDY] Error highlighting of failed results
  • Loading branch information
Hjdskes authored Jun 17, 2016
2 parents 9a15012 + 415a224 commit df954f1
Show file tree
Hide file tree
Showing 44 changed files with 274 additions and 149 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ protected void bindUserInterface() {
bind(JLine2InputHistory.class).to(JLine2PersistentInputHistory.class);

bind(TerminalUserInterface.class).in(Singleton.class);
bind(IResultVisitor.class).to(TerminalUserInterface.class);
bind(IDisplay.class).to(TerminalUserInterface.class);

bind(InputStream.class).annotatedWith(Names.named("in")).toInstance(System.in);
bind(OutputStream.class).annotatedWith(Names.named("out")).toInstance(System.out);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import org.metaborg.core.style.Style;
import org.metaborg.spoofax.shell.client.ConsoleReplModule;
import org.metaborg.spoofax.shell.client.IResultVisitor;
import org.metaborg.spoofax.shell.client.IDisplay;
import org.metaborg.spoofax.shell.client.console.impl.ConsoleRepl;
import org.metaborg.spoofax.shell.output.StyledText;

Expand All @@ -29,12 +29,12 @@ private Main() {
// TODO: make the argument work again.
public static void main(String[] args) {
Injector injector = Guice.createInjector(new ConsoleReplModule());
IResultVisitor visitor = injector.getInstance(IResultVisitor.class);
IDisplay display = injector.getInstance(IDisplay.class);

StyledText message = new StyledText(Color.BLUE, "Welcome to the ")
.append(new Style(Color.GREEN, Color.BLUE, true, true, true), "Spoofax")
.append(Color.BLUE, " REPL");
visitor.visitMessage(message);
display.displayStyledText(message);

ConsoleRepl repl = injector.getInstance(ConsoleRepl.class);
repl.run();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package org.metaborg.spoofax.shell.client.console.commands;

import org.metaborg.spoofax.shell.client.IResult;
import org.metaborg.spoofax.shell.client.console.impl.ConsoleRepl;
import org.metaborg.spoofax.shell.commands.IReplCommand;
import org.metaborg.spoofax.shell.output.IResult;

import com.google.inject.Inject;
import com.google.inject.Provider;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
import java.awt.Color;
import java.io.IOException;

import org.metaborg.spoofax.shell.client.IDisplay;
import org.metaborg.spoofax.shell.client.IRepl;
import org.metaborg.spoofax.shell.client.IResultVisitor;
import org.metaborg.spoofax.shell.invoker.CommandNotFoundException;
import org.metaborg.spoofax.shell.invoker.ICommandInvoker;
import org.metaborg.spoofax.shell.output.StyledText;
Expand All @@ -20,25 +20,24 @@
public class ConsoleRepl implements IRepl {
private final ICommandInvoker invoker;
private final TerminalUserInterface iface;
private final IResultVisitor visitor;
private final IDisplay display;
private boolean running;

/**
* Instantiates a new ConsoleRepl.
*
* @param iface
* The {@link TerminalUserInterface} to retrieve user input from.
* @param visitor
* The {@link IResultVisitor} for visiting the results.
* @param display
* The {@link IDisplay} for displaying the results.
* @param invoker
* The {@link ICommandInvoker} for executing user input.
*/
@Inject
public ConsoleRepl(TerminalUserInterface iface, IResultVisitor visitor,
ICommandInvoker invoker) {
public ConsoleRepl(TerminalUserInterface iface, IDisplay display, ICommandInvoker invoker) {
this.invoker = invoker;
this.iface = iface;
this.visitor = visitor;
this.display = display;
}

/**
Expand Down Expand Up @@ -67,15 +66,15 @@ public void run() {
setRunning(true);
while (running && (input = this.iface.getInput()) != null) {
try {
eval(input).accept(visitor);
eval(input).accept(display);
} catch (CommandNotFoundException e) {
this.visitor.visitMessage(new StyledText(Color.RED, e.getMessage()));
this.display.displayStyledText(new StyledText(Color.RED, e.getMessage()));
}
}

this.iface.history().persistToDisk();
} catch (IOException e) {
this.visitor.visitMessage(new StyledText(Color.RED, e.getMessage()));
this.display.displayStyledText(new StyledText(Color.RED, e.getMessage()));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,8 @@

import org.fusesource.jansi.Ansi;
import org.metaborg.core.style.IStyle;
import org.metaborg.spoofax.shell.client.IDisplay;
import org.metaborg.spoofax.shell.client.IInputHistory;
import org.metaborg.spoofax.shell.client.IResultVisitor;
import org.metaborg.spoofax.shell.output.FailResult;
import org.metaborg.spoofax.shell.output.ISpoofaxResult;
import org.metaborg.spoofax.shell.output.StyledText;

import com.google.inject.Inject;
Expand All @@ -29,10 +27,10 @@
import jline.console.ConsoleReader;

/**
* A terminal UI, offering a way of entering input and implementing {@link IResultVisitor} to
* A terminal UI, offering a way of entering input and implementing {@link IDisplay} to
* display results.
*/
public class TerminalUserInterface implements IResultVisitor {
public class TerminalUserInterface implements IDisplay {
private final ConsoleReader reader;
private final ArrayList<String> lines;
private final PrintWriter out;
Expand Down Expand Up @@ -142,26 +140,11 @@ public IInputHistory history() {
}

@Override
public void visitResult(ISpoofaxResult<?> result) {
visitMessage(result.styled());
}

@Override
public void visitMessage(StyledText message) {
out.println(ansi(message));
public void displayStyledText(StyledText text) {
out.println(ansi(text));
out.flush();
}

@Override
public void visitFailure(FailResult errorResult) {
visitMessage(errorResult.getCause().styled());
}

@Override
public void visitException(Throwable thrown) {
visitMessage(new StyledText(Color.RED, thrown.getMessage()));
}

private <T> void optional(T t, Function<T, Boolean> check, Consumer<T> accept) {
if (check.apply(t)) {
accept.accept(t);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.metaborg.spoofax.shell.client.IResultVisitor;
import org.metaborg.spoofax.shell.client.console.impl.ConsoleRepl;
import org.metaborg.spoofax.shell.output.IResultVisitor;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package org.metaborg.spoofax.shell.client.console.impl;

import org.metaborg.spoofax.shell.client.IResultVisitor;
import org.metaborg.spoofax.shell.client.IDisplay;
import org.metaborg.spoofax.shell.invoker.ICommandInvoker;

import com.google.inject.AbstractModule;
Expand All @@ -11,11 +11,11 @@
public class MockModule extends AbstractModule {
private final ICommandInvoker invokerMock;
private final TerminalUserInterface ifaceMock;
private final IResultVisitor visitorMock;
private final IDisplay displayMock;

/**
* Instantiates a new MockModule with a {@code null} @{link TerminalUserInterface} and a
* {@code null} {@link IResultVisitor}.
* {@code null} {@link IDisplay}.
*
* @param invokerMock
* The mock {@link ICommandInvoker}.
Expand All @@ -25,7 +25,7 @@ public MockModule(ICommandInvoker invokerMock) {
}

/**
* Instantiates a new MockModule with a {@code null} {@link IResultVisitor}.
* Instantiates a new MockModule with a {@code null} {@link IDisplay}.
*
* @param invokerMock
* The mock {@link ICommandInvoker}.
Expand All @@ -41,11 +41,11 @@ public MockModule(ICommandInvoker invokerMock, TerminalUserInterface ifaceMock)
*
* @param ifaceMock
* The mock {@link TerminalUserInterface}.
* @param visitorMock
* The mock {@link IResultVisitor}.
* @param displayMock
* The mock {@link IDisplay}.
*/
public MockModule(TerminalUserInterface ifaceMock, IResultVisitor visitorMock) {
this(null, ifaceMock, visitorMock);
public MockModule(TerminalUserInterface ifaceMock, IDisplay displayMock) {
this(null, ifaceMock, displayMock);
}

/**
Expand All @@ -55,14 +55,14 @@ public MockModule(TerminalUserInterface ifaceMock, IResultVisitor visitorMock) {
* The mock {@link ICommandInvoker}.
* @param ifaceMock
* The mock {@link TerminalUserInterface}.
* @param visitorMock
* The mock {@link IResultVisitor}.
* @param displayMock
* The mock {@link IDisplay}.
*/
public MockModule(ICommandInvoker invokerMock, TerminalUserInterface ifaceMock,
IResultVisitor visitorMock) {
IDisplay displayMock) {
this.invokerMock = invokerMock;
this.ifaceMock = ifaceMock;
this.visitorMock = visitorMock;
this.displayMock = displayMock;
}

@Override
Expand All @@ -73,8 +73,8 @@ protected void configure() {
if (ifaceMock != null) {
bind(TerminalUserInterface.class).toInstance(ifaceMock);
}
if (visitorMock != null) {
bind(IResultVisitor.class).toInstance(visitorMock);
if (displayMock != null) {
bind(IDisplay.class).toInstance(displayMock);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@
import org.metaborg.core.resource.ResourceService;
import org.metaborg.spoofax.core.SpoofaxModule;
import org.metaborg.spoofax.shell.client.IRepl;
import org.metaborg.spoofax.shell.client.IResult;
import org.metaborg.spoofax.shell.client.IResultVisitor;
import org.metaborg.spoofax.shell.commands.HelpCommand;
import org.metaborg.spoofax.shell.commands.IReplCommand;
import org.metaborg.spoofax.shell.commands.LanguageCommand;
Expand All @@ -32,7 +30,9 @@
import org.metaborg.spoofax.shell.invoker.SpoofaxCommandInvoker;
import org.metaborg.spoofax.shell.output.AnalyzeResult;
import org.metaborg.spoofax.shell.output.EvaluateResult;
import org.metaborg.spoofax.shell.output.IResult;
import org.metaborg.spoofax.shell.output.IResultFactory;
import org.metaborg.spoofax.shell.output.IResultVisitor;
import org.metaborg.spoofax.shell.output.InputResult;
import org.metaborg.spoofax.shell.output.ParseResult;
import org.metaborg.spoofax.shell.output.TransformResult;
Expand All @@ -47,7 +47,8 @@

/**
* This class binds the core classes. It is intended to be subclassed by client implementations.
* These subclasses should bind their implementations of {@link IRepl} and {@link IResultVisitor}.
* These subclasses should bind their implementations of {@link IRepl} and either
* {@link IResultVisitor} or {@link IDisplay} (which is also an {@link IResultVisitor}).
*/
public abstract class ReplModule extends SpoofaxModule {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package org.metaborg.spoofax.shell.client;

import java.awt.Color;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import org.metaborg.core.messages.IMessage;
import org.metaborg.core.source.ISourceRegion;
import org.metaborg.core.source.SourceRegion;
import org.metaborg.core.style.IStyle;
import org.metaborg.core.style.Style;
import org.metaborg.spoofax.shell.output.FailResult;
import org.metaborg.spoofax.shell.output.IResultVisitor;
import org.metaborg.spoofax.shell.output.ISpoofaxResult;
import org.metaborg.spoofax.shell.output.StyledText;

/**
* Adapter {@link IResultVisitor} interface for displaying results and errors. An implementation of
* {@link IDisplay} knows how to interpret the style information of a {@link StyledText} and display
* it appropriately.
*/
public interface IDisplay extends IResultVisitor {

/**
* Display the given {@link StyledText}. How the style information is interpreted depends on the
* client.
*
* @param text
* The {@link StyledText} to display.
*/
void displayStyledText(StyledText text);

@Override
default void visitMessage(StyledText message) {
displayStyledText(message);
}

@Override
default void visitResult(ISpoofaxResult<?> result) {
visitMessage(result.styled());
}

@Override
default void visitFailure(FailResult errorResult) {
ISpoofaxResult<?> cause = errorResult.getCause();
String sourceText = cause.sourceText();
List<IMessage> messages = cause.messages();
StyledText styled = highlightMessagesInSource(sourceText, messages);

String concat =
messages.stream().map(message -> message.message()).collect(Collectors.joining("\n"));
visitMessage(styled.append("\n").append(Color.RED, concat));
}

/**
* Highlights the {@link SourceRegion}s of the given {@link IMessage}s in the given source text
* with a red color and bold style.
*
* @param sourceText
* The source text that caused the failure.
* @param messages
* The error messages.
* @return The highlighted {@link StyledText}
*/
default StyledText highlightMessagesInSource(String sourceText, List<IMessage> messages) {
List<ISourceRegion> regions = messages.stream().map(message -> message.region())
.filter(Objects::nonNull).collect(Collectors.toList());
StyledText styled = new StyledText();
IStyle style = new Style(Color.RED, null, true, false, false);
styled.append(regions, style, sourceText);
return styled;
}

@Override
default void visitException(Throwable thrown) {
visitMessage(new StyledText(Color.RED, thrown.getMessage()));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import org.metaborg.spoofax.shell.commands.IReplCommand;
import org.metaborg.spoofax.shell.invoker.CommandNotFoundException;
import org.metaborg.spoofax.shell.invoker.ICommandInvoker;
import org.metaborg.spoofax.shell.output.IResult;
import org.metaborg.spoofax.shell.output.IResultVisitor;

/**
* This interface defines the evaluation part of a REPL (Read-Eval-Print-Loop). The reason for only
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
import org.metaborg.core.action.ITransformAction;
import org.metaborg.core.language.ILanguageImpl;
import org.metaborg.core.project.IProject;
import org.metaborg.spoofax.shell.client.IResult;
import org.metaborg.spoofax.shell.functions.FailableFunction;
import org.metaborg.spoofax.shell.functions.IFunctionFactory;
import org.metaborg.spoofax.shell.output.AnalyzeResult;
import org.metaborg.spoofax.shell.output.EvaluateResult;
import org.metaborg.spoofax.shell.output.FailOrSuccessResult;
import org.metaborg.spoofax.shell.output.IResult;
import org.metaborg.spoofax.shell.output.InputResult;
import org.metaborg.spoofax.shell.output.ParseResult;
import org.metaborg.spoofax.shell.output.TransformResult;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import org.metaborg.spoofax.shell.client.IResult;
import org.metaborg.spoofax.shell.invoker.CommandNotFoundException;
import org.metaborg.spoofax.shell.invoker.ICommandInvoker;
import org.metaborg.spoofax.shell.output.ExceptionResult;
import org.metaborg.spoofax.shell.output.IResult;
import org.metaborg.spoofax.shell.output.StyledText;

import com.google.inject.Inject;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package org.metaborg.spoofax.shell.commands;

import org.metaborg.spoofax.shell.client.IResult;
import org.metaborg.spoofax.shell.client.IResultVisitor;
import org.metaborg.spoofax.shell.invoker.ICommandInvoker;
import org.metaborg.spoofax.shell.output.IResult;
import org.metaborg.spoofax.shell.output.IResultVisitor;

/**
* Interface for REPL commands. Used together with {@link ICommandInvoker}, instances of
Expand Down
Loading

0 comments on commit df954f1

Please sign in to comment.