From b089941df7b0041c8cb02ca881eadce91612987e Mon Sep 17 00:00:00 2001 From: Justin van der Krieken Date: Tue, 20 Jun 2017 10:52:08 +0200 Subject: [PATCH 1/4] Implement interface This class already did that, but without telling java --- .../metaborg/spoofax/shell/client/eclipse/ColorManager.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/org.metaborg.spoofax.shell.eclipse/src/main/java/org/metaborg/spoofax/shell/client/eclipse/ColorManager.java b/org.metaborg.spoofax.shell.eclipse/src/main/java/org/metaborg/spoofax/shell/client/eclipse/ColorManager.java index 6aef3b6a..a54d4ca5 100644 --- a/org.metaborg.spoofax.shell.eclipse/src/main/java/org/metaborg/spoofax/shell/client/eclipse/ColorManager.java +++ b/org.metaborg.spoofax.shell.eclipse/src/main/java/org/metaborg/spoofax/shell/client/eclipse/ColorManager.java @@ -3,6 +3,7 @@ import java.util.HashMap; import java.util.Map; +import org.eclipse.jface.text.source.ISharedTextColors; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.widgets.Display; @@ -17,12 +18,13 @@ * * Note that the ColorManager is not thread-safe. */ -public class ColorManager { +public class ColorManager implements ISharedTextColors { private final Map colors = new HashMap<>(); /** * Dispose all created colors. */ + @Override public void dispose() { colors.values().stream().forEach(Color::dispose); colors.clear(); @@ -35,6 +37,7 @@ public void dispose() { * The color to retrieve. * @return The {@link Color}. */ + @Override public Color getColor(RGB rgb) { Color result = colors.get(rgb); From 5a323e4d74662791ff4f5614d777017a3533c433 Mon Sep 17 00:00:00 2001 From: Justin van der Krieken Date: Tue, 20 Jun 2017 17:45:31 +0200 Subject: [PATCH 2/4] Implement folding initial --- .../metaborg/spoofax/shell/ReplModule.java | 10 ++ .../spoofax/shell/client/IDisplay.java | 24 +-- .../spoofax/shell/functions/FoldFunction.java | 106 ++++++++++++ .../shell/functions/FunctionComposer.java | 14 ++ .../shell/functions/IFunctionFactory.java | 21 +++ .../shell/functions/PrettyPrintFunction.java | 158 ++++++++++++++++++ .../spoofax/shell/output/FoldResult.java | 52 ++++++ .../shell/output/ISpoofaxTermResult.java | 5 + .../spoofax/shell/output/PrintResult.java | 67 ++++++++ .../services/IEditorServicesStrategy.java | 12 ++ .../shell/services/LoadedServices.java | 7 + .../shell/services/SpoofaxEditorServices.java | 7 + .../shell/services/UnloadedServices.java | 7 + .../META-INF/MANIFEST.MF | 4 +- org.metaborg.spoofax.shell.eclipse/pom.xml | 5 + .../shell/client/eclipse/EclipseUtil.java | 24 +-- .../client/eclipse/impl/EclipseDisplay.java | 120 ++++++++++--- 17 files changed, 599 insertions(+), 44 deletions(-) create mode 100644 org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/functions/FoldFunction.java create mode 100644 org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/functions/PrettyPrintFunction.java create mode 100644 org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/output/FoldResult.java create mode 100644 org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/output/PrintResult.java diff --git a/org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/ReplModule.java b/org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/ReplModule.java index 8e16917f..4e0299dd 100644 --- a/org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/ReplModule.java +++ b/org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/ReplModule.java @@ -18,22 +18,26 @@ import org.metaborg.spoofax.shell.functions.AnalyzeFunction; import org.metaborg.spoofax.shell.functions.EvaluateFunction; import org.metaborg.spoofax.shell.functions.FailableFunction; +import org.metaborg.spoofax.shell.functions.FoldFunction; import org.metaborg.spoofax.shell.functions.IFunctionFactory; import org.metaborg.spoofax.shell.functions.InputFunction; import org.metaborg.spoofax.shell.functions.OpenInputFunction; import org.metaborg.spoofax.shell.functions.PTransformFunction; import org.metaborg.spoofax.shell.functions.ParseFunction; +import org.metaborg.spoofax.shell.functions.PrettyPrintFunction; import org.metaborg.spoofax.shell.functions.StyleFunction; import org.metaborg.spoofax.shell.invoker.ICommandInvoker; 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.FoldResult; 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.ISpoofaxTermResult; import org.metaborg.spoofax.shell.output.InputResult; import org.metaborg.spoofax.shell.output.ParseResult; +import org.metaborg.spoofax.shell.output.PrintResult; import org.metaborg.spoofax.shell.output.StyleResult; import org.metaborg.spoofax.shell.output.TransformResult; import org.metaborg.spoofax.shell.services.IEditorServices; @@ -125,6 +129,12 @@ protected void bindFactories() { .implement( new TypeLiteral, EvaluateResult, IResult>>() { }, EvaluateFunction.class) + .implement( + new TypeLiteral, FoldResult, IResult>>() { + }, FoldFunction.class) + .implement( + new TypeLiteral>() { + }, PrettyPrintFunction.class) .implement(new TypeLiteral>() { }, StyleFunction.class).build(IFunctionFactory.class)); // CHECKSTYLE.ON: LineLength diff --git a/org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/client/IDisplay.java b/org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/client/IDisplay.java index 2f9e34f2..9d32769a 100644 --- a/org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/client/IDisplay.java +++ b/org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/client/IDisplay.java @@ -16,20 +16,20 @@ 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. + * 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. + * Display the given {@link StyledText}. How the style information is + * interpreted depends on the client. * - * @param text + * @param styledText * The {@link StyledText} to display. */ - void displayStyledText(StyledText text); + void displayStyledText(StyledText styledText); @Override default void visitMessage(StyledText message) { @@ -54,8 +54,8 @@ default void visitFailure(FailResult errorResult) { } /** - * Highlights the {@link SourceRegion}s of the given {@link IMessage}s in the given source text - * with a red color and bold style. + * 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. @@ -64,8 +64,10 @@ default void visitFailure(FailResult errorResult) { * @return The highlighted {@link StyledText} */ default StyledText highlightMessagesInSource(String sourceText, List messages) { - List regions = messages.stream().map(IMessage::region) - .filter(Objects::nonNull).collect(Collectors.toList()); + List regions = messages.stream() + .map(IMessage::region) + .filter(Objects::nonNull) + .collect(Collectors.toList()); StyledText styled = new StyledText(); IStyle style = new Style(Color.RED, null, true, false, false, false); styled.append(regions, style, sourceText); diff --git a/org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/functions/FoldFunction.java b/org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/functions/FoldFunction.java new file mode 100644 index 00000000..a7a513bc --- /dev/null +++ b/org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/functions/FoldFunction.java @@ -0,0 +1,106 @@ +package org.metaborg.spoofax.shell.functions; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; + +import org.metaborg.core.source.ISourceRegion; +import org.metaborg.core.source.SourceRegion; +import org.metaborg.spoofax.shell.output.FailOrSuccessResult; +import org.metaborg.spoofax.shell.output.FoldResult; +import org.metaborg.spoofax.shell.output.IResult; +import org.metaborg.spoofax.shell.output.ISpoofaxTermResult; +import org.spoofax.interpreter.terms.IStrategoTerm; + +/** + * Creates a {@link FoldResult} from a given {@link ISpoofaxTermResult}. + */ +public class FoldFunction implements FailableFunction, FoldResult, IResult> { + + /** + * Instantiate a {@link FoldFunction}. + * Explicit + */ + public FoldFunction() { + } + + @Override + public FailOrSuccessResult apply(ISpoofaxTermResult input) { + + Helper helper = new Helper(); + + try { + IStrategoTerm term = input.ast().get(); + + term.writeAsString(helper, IStrategoTerm.INFINITE); + + List out = new ArrayList<>(helper.outputRegions); + + return FailOrSuccessResult.successful(new FoldResult(term, out)); + } catch (IOException e) { + // should not happen + throw new RuntimeException(e); + } + } + + /** + * Helper to wrap {@link IStrategoTerm#writeAsString}. + * + * As {@link IStrategoTerm#prettyPrint(org.spoofax.interpreter.terms.ITermPrinter)} is + * deprecated {@link IStrategoTerm#writeAsString(Appendable, int)} is used instead. + * + * As {@link FoldFunction} should be state-less a private static class is used over + * an anonymous class with members or single-length-arrays to contain the state within the + * method scope. + * + * The {@link #Helper} recognizes ( and ) and uses them to + * recognize structures. + */ + private static class Helper implements Appendable { + + private Stack regionStack = new Stack<>(); + private List outputRegions = new ArrayList<>(); + + private int index = 0; + private int lastSeqIndex = -1; + + @Override + public Appendable append(CharSequence csq, int start, int end) throws IOException { + return this.append(csq.subSequence(start, end)); + } + + @Override + public Appendable append(char c) throws IOException { + switch (c) { + case '(': + regionStack.push(lastSeqIndex == -1 ? 0 : lastSeqIndex); + lastSeqIndex = -1; + break; + case ')': + int startOffset = regionStack.pop(); + int endOffset = index; + outputRegions.add(new SourceRegion(startOffset, endOffset)); + lastSeqIndex = -1; + break; + case ',': + break; + default: + if (lastSeqIndex == -1) { + lastSeqIndex = index; + } + break; + } + index++; + return this; + } + + @Override + public Appendable append(CharSequence csq) throws IOException { + lastSeqIndex = index; + index += csq.length(); + return this; + } + } + +} diff --git a/org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/functions/FunctionComposer.java b/org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/functions/FunctionComposer.java index cef77036..e3836b36 100644 --- a/org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/functions/FunctionComposer.java +++ b/org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/functions/FunctionComposer.java @@ -6,8 +6,10 @@ 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.ISpoofaxTermResult; import org.metaborg.spoofax.shell.output.InputResult; import org.metaborg.spoofax.shell.output.ParseResult; +import org.metaborg.spoofax.shell.output.PrintResult; import org.metaborg.spoofax.shell.output.StyleResult; import org.metaborg.spoofax.shell.output.TransformResult; @@ -128,4 +130,16 @@ public FailableFunction pStyleFunction() { return parseFunction() .kleisliCompose(functionFactory.createStyleFunction(project, lang)); } + + /** + * Composes a {@link PrettyPrintFunction}, which provides pretty printing. + * + * @return {@link PrettyPrintFunction} - The pretty print function. + */ + public FailableFunction, PrintResult, IResult> termPrettyPrintFunction() { + return functionFactory.createFoldFunction(project, lang) + .kleisliCompose(functionFactory.createPrettyPrintFunction(project, lang)); + + } + } diff --git a/org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/functions/IFunctionFactory.java b/org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/functions/IFunctionFactory.java index 93fed464..a71bfc89 100644 --- a/org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/functions/IFunctionFactory.java +++ b/org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/functions/IFunctionFactory.java @@ -9,10 +9,12 @@ import org.metaborg.spoofax.shell.commands.IReplCommand; import org.metaborg.spoofax.shell.output.AnalyzeResult; import org.metaborg.spoofax.shell.output.EvaluateResult; +import org.metaborg.spoofax.shell.output.FoldResult; import org.metaborg.spoofax.shell.output.IResult; import org.metaborg.spoofax.shell.output.ISpoofaxTermResult; import org.metaborg.spoofax.shell.output.InputResult; import org.metaborg.spoofax.shell.output.ParseResult; +import org.metaborg.spoofax.shell.output.PrintResult; import org.metaborg.spoofax.shell.output.StyleResult; import org.metaborg.spoofax.shell.output.TransformResult; @@ -101,6 +103,25 @@ public interface IFunctionFactory { FailableFunction createStyleFunction(IProject project, ILanguageImpl lang); + /** + * Factory method for creating a {@link FoldFunction}. + * @param project The associated {@link IProject} + * @param lang The associated {@link ILanguageImpl} + * @return an {@link FoldFunction} + */ + FailableFunction, FoldResult, IResult> + createFoldFunction(IProject project, ILanguageImpl lang); + + /** + * Factory method for creating a {@link PrettyPrintFunction}. + * @param project The associated {@link IProject} + * @param lang The associated {@link ILanguageImpl} + * @return an {@link PrettyPrintFunction} + */ + FailableFunction + createPrettyPrintFunction(IProject project, ILanguageImpl lang); + + /** * Factory method for creating a {@link CommandBuilder}. * The {@link CommandBuilder} composes an {@link IReplCommand} diff --git a/org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/functions/PrettyPrintFunction.java b/org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/functions/PrettyPrintFunction.java new file mode 100644 index 00000000..4a713d1a --- /dev/null +++ b/org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/functions/PrettyPrintFunction.java @@ -0,0 +1,158 @@ +package org.metaborg.spoofax.shell.functions; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.metaborg.core.source.ISourceRegion; +import org.metaborg.core.source.SourceRegion; +import org.metaborg.spoofax.shell.output.ExceptionResult; +import org.metaborg.spoofax.shell.output.FailOrSuccessResult; +import org.metaborg.spoofax.shell.output.FoldResult; +import org.metaborg.spoofax.shell.output.IResult; +import org.metaborg.spoofax.shell.output.PrintResult; +import org.spoofax.interpreter.terms.IStrategoTerm; + +/** + * Creates a {@link PrintResult} from a given {@link FoldResult}. + */ +public class PrettyPrintFunction implements FailableFunction { + + private static final CharSequence INDENTATION = " "; + private static final int START = 0; + private static final int END = 1; + + @Override + public FailOrSuccessResult + apply(FoldResult input) { + + Helper helper = new Helper(); + + IStrategoTerm term = input.getTerm(); + + input.getRegions().forEach(region -> { + helper.regionOffsets.add(new int[] { + region.startOffset(), + region.endOffset() + }); + }); + + try { + term.writeAsString(helper, IStrategoTerm.INFINITE); + List newFolds = new ArrayList<>(); + for (int[] regionInts : helper.regionOffsets) { + newFolds.add(new SourceRegion( + regionInts[START], + regionInts[END])); + } + return FailOrSuccessResult.successful( + new PrintResult(helper.text.toString(), newFolds)); + } catch (IOException e) { + // should not happen + return FailOrSuccessResult.excepted(new ExceptionResult(e)); + } + } + + /** + * Helper to wrap {@link IStrategoTerm#writeAsString}. + * + * As {@link IStrategoTerm#prettyPrint(org.spoofax.interpreter.terms.ITermPrinter)} is + * deprecated {@link IStrategoTerm#writeAsString(Appendable, int)} is used instead. + * + * As {@link PrettyPrintFunction} should be state-less a private static class is used over + * an anonymous class with members or single-length-arrays to contain the state within the + * method scope. + * + * The {@link #Helper} recognizes ( and ) and uses them to + * indent. + */ + private static class Helper implements Appendable { + + private StringBuilder text = new StringBuilder(); + private List regionOffsets = new ArrayList<>(); + + private int indentationLevel = 0; + private boolean shouldIndent = false; + private boolean isClosing = false; + + private void appendAndUpdate(CharSequence csq, boolean updateFolds) { + appendAndUpdate(csq, updateFolds, false); + } + + private void appendAndUpdate(CharSequence csq, boolean updateFolds, boolean inclusive) { + int preOffset = text.length(); + text.append(csq); + if (updateFolds) { + if (inclusive && preOffset > 0) { + preOffset = preOffset - 1; + } + updateOffsets(preOffset, csq.length()); + } + } + @Override + public Appendable append(CharSequence csq, int start, int end) throws IOException { + return this.append(csq.subSequence(start, end)); + } + + private void indent() { + if (shouldIndent) { + isClosing = false; + shouldIndent = false; + appendAndUpdate( + String.join("", Collections.nCopies(indentationLevel, INDENTATION)), + true); + } + } + + private void updateOffsets(int offset, int value) { + for (int[] ints : regionOffsets) { + if (ints[START] >= offset) { + ints[START] += value; + } + if (ints[END] >= offset) { + ints[END] += value; + } + } + } + + @Override + public Appendable append(char c) throws IOException { + switch (c) { + case '(': + indent(); + appendAndUpdate(Character.toString(c), false); + appendAndUpdate(System.lineSeparator(), true); + shouldIndent = true; + indentationLevel++; + break; + case ')': + if (!isClosing) { + indent(); + appendAndUpdate(System.lineSeparator(), true); + } + indentationLevel--; + shouldIndent = true; + indent(); + appendAndUpdate(Character.toString(c), false); + appendAndUpdate(System.lineSeparator(), true, true); + shouldIndent = true; + isClosing = true; + break; + default: + indent(); + appendAndUpdate(Character.toString(c), false); + break; + } + return this; + } + + @Override + public Appendable append(CharSequence csq) throws IOException { + indent(); + appendAndUpdate(csq.toString(), false); + return this; + } + } + +} diff --git a/org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/output/FoldResult.java b/org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/output/FoldResult.java new file mode 100644 index 00000000..2b3582c2 --- /dev/null +++ b/org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/output/FoldResult.java @@ -0,0 +1,52 @@ +package org.metaborg.spoofax.shell.output; + +import java.util.List; + +import org.metaborg.core.source.ISourceRegion; +import org.metaborg.core.style.IRegionStyle; +import org.spoofax.interpreter.terms.IStrategoTerm; + +/** + * Represents a result that is a term and foldable regions. + */ +public class FoldResult implements IResult { + + private final IStrategoTerm term; + private final List regions; + + /** + * Create a Fold Result. + * + * @param term The {@ IStrategoTerm}. + * @param regions + * The {@link IRegionStyle}s in terms of {@link IStrategoTerm}s. + */ + public FoldResult(IStrategoTerm term, List regions) { + this.term = term; + this.regions = regions; + } + + /** + * Gets the term contained in this result. + * @return a {@link IStrategoTerm}. + */ + public IStrategoTerm getTerm() { + return term; + } + + /** + * Gets the regions contained in this result. + * @return a {@link List} of regions. + */ + public List getRegions() { + return regions; + } + + @Override + public void accept(IResultVisitor visitor) { + // FIXME: not nice but as of now not used. + visitor.visitMessage(new StyledText(term.toString(IStrategoTerm.INFINITE))); + } + + +} diff --git a/org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/output/ISpoofaxTermResult.java b/org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/output/ISpoofaxTermResult.java index d4174ee4..68a1c0ca 100644 --- a/org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/output/ISpoofaxTermResult.java +++ b/org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/output/ISpoofaxTermResult.java @@ -16,4 +16,9 @@ public interface ISpoofaxTermResult extends ISpoofaxResult { * @return a {@link IStrategoTerm} or null */ Optional ast(); + + @Override + default void accept(IResultVisitor visitor) { + visitor.visitTermResult(this); + } } diff --git a/org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/output/PrintResult.java b/org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/output/PrintResult.java new file mode 100644 index 00000000..984e4236 --- /dev/null +++ b/org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/output/PrintResult.java @@ -0,0 +1,67 @@ +/** + * + */ +package org.metaborg.spoofax.shell.output; + +import java.util.Collections; +import java.util.List; + +import org.metaborg.core.source.ISourceRegion; + +/** + * A result that represents output that is human readable. + */ +public class PrintResult implements IResult { + + private final StyledText text; + private final List regions; + + /** + * Creates a {@link PrintResult}. + * @param text The original text. + */ + public PrintResult(String text) { + this(text, Collections.emptyList()); + } + + /** + * Creates a {@link PrintResult} with given fold regions. + * @param text The (human readable) text + * @param regions Foldregions that correspond to {@link #text} + */ + public PrintResult(StyledText text, List regions) { + this.text = text; + this.regions = regions; + } + + /** + * Creates a {@link PrintResult} with given fold regions. + * @param text The (human readable) text + * @param regions Foldregions that correspond to {@link #text} + */ + public PrintResult(String text, List regions) { + this(new StyledText(text), regions); + } + + /** + * Gets the fold regions contained in this result (if applicable). + * @return a {@link List} of regions. + */ + public List getRegions() { + return regions; + } + + /** + * Gets the test. This text is human readable. + * @return a {@link StyledText} + */ + public StyledText getText() { + return text; + } + + @Override + public void accept(IResultVisitor visitor) { + visitor.visitMessage(text); + } + +} diff --git a/org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/services/IEditorServicesStrategy.java b/org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/services/IEditorServicesStrategy.java index 114e2129..91a674eb 100644 --- a/org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/services/IEditorServicesStrategy.java +++ b/org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/services/IEditorServicesStrategy.java @@ -3,6 +3,8 @@ import org.metaborg.spoofax.shell.functions.FunctionComposer; import org.metaborg.spoofax.shell.output.FailOrSuccessResult; import org.metaborg.spoofax.shell.output.IResult; +import org.metaborg.spoofax.shell.output.ISpoofaxTermResult; +import org.metaborg.spoofax.shell.output.PrintResult; import org.metaborg.spoofax.shell.output.StyleResult; /** @@ -34,4 +36,14 @@ public interface IEditorServicesStrategy { */ FailOrSuccessResult highlight(String source); + /** + * Attempts to provide pretty printing of the input. + * + * @param input + * the term that must be pretty printed. + * @return {@link FailOrSuccessResult} - A {@link PrintResult} containing a human readable + * text plus fold regions, or a failed result. + */ + FailOrSuccessResult foldAndPrint(ISpoofaxTermResult input); + } diff --git a/org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/services/LoadedServices.java b/org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/services/LoadedServices.java index 5727f496..d32073ae 100644 --- a/org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/services/LoadedServices.java +++ b/org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/services/LoadedServices.java @@ -3,6 +3,8 @@ import org.metaborg.spoofax.shell.functions.FunctionComposer; import org.metaborg.spoofax.shell.output.FailOrSuccessResult; import org.metaborg.spoofax.shell.output.IResult; +import org.metaborg.spoofax.shell.output.ISpoofaxTermResult; +import org.metaborg.spoofax.shell.output.PrintResult; import org.metaborg.spoofax.shell.output.StyleResult; /** @@ -36,4 +38,9 @@ public boolean isLoaded() { public FailOrSuccessResult highlight(String source) { return composer.pStyleFunction().apply(source); } + + @Override + public FailOrSuccessResult foldAndPrint(ISpoofaxTermResult input) { + return composer.termPrettyPrintFunction().apply(input); + } } diff --git a/org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/services/SpoofaxEditorServices.java b/org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/services/SpoofaxEditorServices.java index fcd2d3f4..810cebc0 100644 --- a/org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/services/SpoofaxEditorServices.java +++ b/org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/services/SpoofaxEditorServices.java @@ -3,6 +3,8 @@ import org.metaborg.spoofax.shell.functions.FunctionComposer; import org.metaborg.spoofax.shell.output.FailOrSuccessResult; import org.metaborg.spoofax.shell.output.IResult; +import org.metaborg.spoofax.shell.output.ISpoofaxTermResult; +import org.metaborg.spoofax.shell.output.PrintResult; import org.metaborg.spoofax.shell.output.StyleResult; import com.google.inject.Inject; @@ -40,6 +42,11 @@ public void load(FunctionComposer composer) { strategy = strategyfactory.createLoadedStrategy(composer); } + @Override + public FailOrSuccessResult foldAndPrint(ISpoofaxTermResult input) { + return strategy.foldAndPrint(input); + } + @Override public boolean isLoaded() { return strategy.isLoaded(); diff --git a/org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/services/UnloadedServices.java b/org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/services/UnloadedServices.java index 85fb7b0c..824ff9e5 100644 --- a/org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/services/UnloadedServices.java +++ b/org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/services/UnloadedServices.java @@ -3,6 +3,8 @@ import org.metaborg.spoofax.shell.output.ExceptionResult; import org.metaborg.spoofax.shell.output.FailOrSuccessResult; import org.metaborg.spoofax.shell.output.IResult; +import org.metaborg.spoofax.shell.output.ISpoofaxTermResult; +import org.metaborg.spoofax.shell.output.PrintResult; import org.metaborg.spoofax.shell.output.StyleResult; /** @@ -39,4 +41,9 @@ public boolean isLoaded() { public FailOrSuccessResult highlight(String source) { return FailOrSuccessResult.excepted(createException("Syntax Highlighting")); } + + @Override + public FailOrSuccessResult foldAndPrint(ISpoofaxTermResult input) { + return FailOrSuccessResult.excepted(createException("Pretty printing")); + } } diff --git a/org.metaborg.spoofax.shell.eclipse/META-INF/MANIFEST.MF b/org.metaborg.spoofax.shell.eclipse/META-INF/MANIFEST.MF index 21c8ad41..5973800c 100644 --- a/org.metaborg.spoofax.shell.eclipse/META-INF/MANIFEST.MF +++ b/org.metaborg.spoofax.shell.eclipse/META-INF/MANIFEST.MF @@ -12,7 +12,9 @@ Require-Bundle: org.eclipse.core.runtime, org.metaborg.spoofax.shell.core, org.metaborg.spoofax.shell.eclipse.externaldeps, org.metaborg.spoofax.eclipse.externaldeps, - org.metaborg.spoofax.eclipse + org.metaborg.spoofax.eclipse, + org.eclipse.ui.editors, + org.spoofax.terms Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-Vendor: metaborg.org Export-Package: org.metaborg.spoofax.shell.client.eclipse, diff --git a/org.metaborg.spoofax.shell.eclipse/pom.xml b/org.metaborg.spoofax.shell.eclipse/pom.xml index 1a240fa5..43c1bc47 100644 --- a/org.metaborg.spoofax.shell.eclipse/pom.xml +++ b/org.metaborg.spoofax.shell.eclipse/pom.xml @@ -39,6 +39,11 @@ org.metaborg.spoofax.shell.eclipse.externaldeps 2.3.0-SNAPSHOT + + org.metaborg + org.spoofax.terms + 2.3.0-SNAPSHOT + diff --git a/org.metaborg.spoofax.shell.eclipse/src/main/java/org/metaborg/spoofax/shell/client/eclipse/EclipseUtil.java b/org.metaborg.spoofax.shell.eclipse/src/main/java/org/metaborg/spoofax/shell/client/eclipse/EclipseUtil.java index 4c9cfb0b..41a78de2 100644 --- a/org.metaborg.spoofax.shell.eclipse/src/main/java/org/metaborg/spoofax/shell/client/eclipse/EclipseUtil.java +++ b/org.metaborg.spoofax.shell.eclipse/src/main/java/org/metaborg/spoofax/shell/client/eclipse/EclipseUtil.java @@ -35,17 +35,19 @@ public static StyleRange style(ColorManager colorManager, IStyle style, int offs styleRange.start = offset; styleRange.length = length; - if (style.color() != null) { - styleRange.foreground = colorManager.getColor(awtToRGB(style.color())); - } - if (style.backgroundColor() != null) { - styleRange.background = colorManager.getColor(awtToRGB(style.backgroundColor())); - } - if (style.bold()) { - styleRange.fontStyle |= SWT.BOLD; - } - if (style.italic()) { - styleRange.fontStyle |= SWT.ITALIC; + if (style != null) { + if (style.color() != null) { + styleRange.foreground = colorManager.getColor(awtToRGB(style.color())); + } + if (style.backgroundColor() != null) { + styleRange.background = colorManager.getColor(awtToRGB(style.backgroundColor())); + } + if (style.bold()) { + styleRange.fontStyle |= SWT.BOLD; + } + if (style.italic()) { + styleRange.fontStyle |= SWT.ITALIC; + } } return styleRange; diff --git a/org.metaborg.spoofax.shell.eclipse/src/main/java/org/metaborg/spoofax/shell/client/eclipse/impl/EclipseDisplay.java b/org.metaborg.spoofax.shell.eclipse/src/main/java/org/metaborg/spoofax/shell/client/eclipse/impl/EclipseDisplay.java index 73c30a33..7cec5143 100644 --- a/org.metaborg.spoofax.shell.eclipse/src/main/java/org/metaborg/spoofax/shell/client/eclipse/impl/EclipseDisplay.java +++ b/org.metaborg.spoofax.shell.eclipse/src/main/java/org/metaborg/spoofax/shell/client/eclipse/impl/EclipseDisplay.java @@ -1,19 +1,42 @@ package org.metaborg.spoofax.shell.client.eclipse.impl; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.stream.Collectors; + import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.Document; import org.eclipse.jface.text.IDocument; -import org.eclipse.jface.text.ITextViewer; +import org.eclipse.jface.text.Position; import org.eclipse.jface.text.TextViewer; +import org.eclipse.jface.text.source.Annotation; +import org.eclipse.jface.text.source.CompositeRuler; +import org.eclipse.jface.text.source.IAnnotationAccess; +import org.eclipse.jface.text.source.IOverviewRuler; +import org.eclipse.jface.text.source.OverviewRuler; +import org.eclipse.jface.text.source.projection.ProjectionAnnotation; +import org.eclipse.jface.text.source.projection.ProjectionAnnotationModel; +import org.eclipse.jface.text.source.projection.ProjectionSupport; +import org.eclipse.jface.text.source.projection.ProjectionViewer; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StyleRange; import org.eclipse.swt.widgets.Composite; +import org.eclipse.ui.texteditor.DefaultMarkerAnnotationAccess; +import org.eclipse.ui.texteditor.DefaultRangeIndicator; +import org.metaborg.core.source.ISourceRegion; import org.metaborg.core.style.IStyle; import org.metaborg.spoofax.shell.client.IDisplay; import org.metaborg.spoofax.shell.client.eclipse.ColorManager; import org.metaborg.spoofax.shell.client.eclipse.EclipseUtil; +import org.metaborg.spoofax.shell.output.ExceptionResult; +import org.metaborg.spoofax.shell.output.FailOrSuccessVisitor; +import org.metaborg.spoofax.shell.output.IResult; +import org.metaborg.spoofax.shell.output.ISpoofaxTermResult; +import org.metaborg.spoofax.shell.output.PrintResult; import org.metaborg.spoofax.shell.output.StyledText; +import org.metaborg.spoofax.shell.services.IEditorServices; import com.google.inject.assistedinject.Assisted; import com.google.inject.assistedinject.AssistedInject; @@ -28,8 +51,12 @@ public class EclipseDisplay implements IDisplay { // TODO: Use ReplDocument to provide custom partitioning? Perhaps more something for the output // as opposed to input. Should be relatively easy for output to at least partition different // input/output combinations. - private final ITextViewer output; + private final ProjectionViewer viewer; private final ColorManager colorManager; + private final Document document = new Document(); + private final ProjectionAnnotationModel projectionAnnotationModel; + private final IEditorServices editorServices; + // private final ProjectionAnnotationModel annotationModel; /** * Instantiates a new EclipseDisplay. @@ -39,24 +66,38 @@ public class EclipseDisplay implements IDisplay { * @param parent * A {@link Composite} control which will be the parent of this EclipseDisplay. * (cannot be {@code null}). + * @param editorServices the {@link IEditorServices} used to request editor services. */ @AssistedInject - public EclipseDisplay(ColorManager colorManager, @Assisted Composite parent) { - this.output = new TextViewer(parent, SWT.BORDER | SWT.MULTI | SWT.V_SCROLL | SWT.H_SCROLL); - this.output.getTextWidget().setFont(JFaceResources.getFont(JFaceResources.TEXT_FONT)); - this.output.getTextWidget().setAlwaysShowScrollBars(false); - this.output.setEditable(false); - this.output.setDocument(new Document()); + public EclipseDisplay( + ColorManager colorManager, + @Assisted Composite parent, + IEditorServices editorServices) { + // see AbstractDecoratedTextEditor.VERTICAL_RULER_WIDTH + IAnnotationAccess annotationAccess = new DefaultMarkerAnnotationAccess(); + IOverviewRuler overviewRuler = new OverviewRuler(annotationAccess, 0, colorManager); + CompositeRuler ruler = new CompositeRuler(); - this.colorManager = colorManager; - } + this.editorServices = editorServices; + + this.viewer = new ProjectionViewer(parent, ruler, overviewRuler, true, + SWT.BORDER | SWT.MULTI | SWT.V_SCROLL | SWT.H_SCROLL); + this.viewer.getTextWidget().setFont(JFaceResources.getFont(JFaceResources.TEXT_FONT)); + this.viewer.getTextWidget().setAlwaysShowScrollBars(false); + this.viewer.setEditable(true); + this.viewer.setDocument(document, new ProjectionAnnotationModel()); - private IDocument getDocument() { - return this.output.getDocument(); + ProjectionSupport projectionSupport = new ProjectionSupport(viewer, annotationAccess, + colorManager); + projectionSupport.install(); + viewer.setRangeIndicator(new DefaultRangeIndicator()); + this.viewer.enableProjection(); + this.projectionAnnotationModel = viewer.getProjectionAnnotationModel(); + this.colorManager = colorManager; } private void scrollText() { - output.revealRange(getDocument().getLength(), 0); + viewer.revealRange(viewer.getDocument().getLength(), 0); } private void append(IDocument doc, int offset, String fragment) { @@ -74,13 +115,18 @@ private void append(IDocument doc, int offset, String fragment) { } @Override - public void displayStyledText(StyledText text) { - IDocument doc = getDocument(); + public void displayStyledText(StyledText styledText) { + displayStyledText(styledText, Collections.emptyList()); + } + + private void displayStyledText(StyledText text, List foldingRegions) { + + int offsetPreAppendNewText = document.getLength(); text.getSource().forEach(e -> { - int offset = doc.getLength(); + int offset = document.getLength(); - append(doc, offset, e.fragment()); + append(document, offset, e.fragment()); IStyle style = e.style(); if (style != null) { @@ -90,13 +136,45 @@ public void displayStyledText(StyledText text) { offset, e.region().length()); - output.getTextWidget().setStyleRange(styleRange); + viewer.getTextWidget().setStyleRange(styleRange); } }); - if (doc != null) { - append(doc, doc.getLength(), "\n"); - scrollText(); + append(document, document.getLength(), "\n"); + scrollText(); + List positions = foldingRegions.stream().map(region -> { + return new Position(region.startOffset() + offsetPreAppendNewText, region.length()); + }).collect(Collectors.toList()); + + HashMap newAnnotations = new HashMap<>(); + for (Position position : positions) { + ProjectionAnnotation annotation = new ProjectionAnnotation(); + newAnnotations.put(annotation, position); } + projectionAnnotationModel.modifyAnnotations(null, newAnnotations, null); + } + + @Override + public void visitTermResult(ISpoofaxTermResult result) { + editorServices + .foldAndPrint(result) + .accept(new FailOrSuccessVisitor() { + + @Override + public void visitSuccess(PrintResult result) { + displayStyledText(result.getText(), result.getRegions()); + } + + @Override + public void visitFailure(IResult result) { + result.accept(EclipseDisplay.this); + } + + @Override + public void visitException(ExceptionResult result) { + result.accept(EclipseDisplay.this); + } + }); } + } From 93a14d7be5a7eecfeb29a8af0defa86bfc55273f Mon Sep 17 00:00:00 2001 From: Justin van der Krieken Date: Thu, 29 Jun 2017 13:05:27 +0200 Subject: [PATCH 3/4] Workaround for styling of collapsed annotations Ugly workaround but it works for now --- .../client/eclipse/impl/EclipseDisplay.java | 52 ++++++++++++------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/org.metaborg.spoofax.shell.eclipse/src/main/java/org/metaborg/spoofax/shell/client/eclipse/impl/EclipseDisplay.java b/org.metaborg.spoofax.shell.eclipse/src/main/java/org/metaborg/spoofax/shell/client/eclipse/impl/EclipseDisplay.java index 7cec5143..ede27894 100644 --- a/org.metaborg.spoofax.shell.eclipse/src/main/java/org/metaborg/spoofax/shell/client/eclipse/impl/EclipseDisplay.java +++ b/org.metaborg.spoofax.shell.eclipse/src/main/java/org/metaborg/spoofax/shell/client/eclipse/impl/EclipseDisplay.java @@ -1,8 +1,10 @@ package org.metaborg.spoofax.shell.client.eclipse.impl; +import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; import org.eclipse.jface.resource.JFaceResources; @@ -11,7 +13,6 @@ import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.Position; import org.eclipse.jface.text.TextViewer; -import org.eclipse.jface.text.source.Annotation; import org.eclipse.jface.text.source.CompositeRuler; import org.eclipse.jface.text.source.IAnnotationAccess; import org.eclipse.jface.text.source.IOverviewRuler; @@ -26,6 +27,7 @@ import org.eclipse.ui.texteditor.DefaultMarkerAnnotationAccess; import org.eclipse.ui.texteditor.DefaultRangeIndicator; import org.metaborg.core.source.ISourceRegion; +import org.metaborg.core.style.IRegionStyle; import org.metaborg.core.style.IStyle; import org.metaborg.spoofax.shell.client.IDisplay; import org.metaborg.spoofax.shell.client.eclipse.ColorManager; @@ -56,6 +58,8 @@ public class EclipseDisplay implements IDisplay { private final Document document = new Document(); private final ProjectionAnnotationModel projectionAnnotationModel; private final IEditorServices editorServices; + + private final List projectionAnnotations = new ArrayList<>(); // private final ProjectionAnnotationModel annotationModel; /** @@ -119,38 +123,46 @@ public void displayStyledText(StyledText styledText) { displayStyledText(styledText, Collections.emptyList()); } - private void displayStyledText(StyledText text, List foldingRegions) { + private void applyFragmentToDocument(IRegionStyle fragment) { + int offset = document.getLength(); + append(document, offset, fragment.fragment()); + IStyle style = fragment.style(); + if (style != null) { + StyleRange styleRange = EclipseUtil.style( + colorManager, + fragment.style(), + offset, + fragment.region().length()); + viewer.getTextWidget().setStyleRange(styleRange); + } + } + private void displayStyledText(StyledText text, List foldingRegions) { + Map annotationStates = new HashMap<>(); + for (ProjectionAnnotation annotation : projectionAnnotations) { + annotationStates.put(annotation, annotation.isCollapsed()); + projectionAnnotationModel.expand(annotation); + } int offsetPreAppendNewText = document.getLength(); - - text.getSource().forEach(e -> { - int offset = document.getLength(); - - append(document, offset, e.fragment()); - - IStyle style = e.style(); - if (style != null) { - StyleRange styleRange = EclipseUtil.style( - colorManager, - e.style(), - offset, - e.region().length()); - - viewer.getTextWidget().setStyleRange(styleRange); + text.getSource().forEach(this::applyFragmentToDocument); + annotationStates.forEach((annotation, isCollapsed) -> { + if (isCollapsed) { + projectionAnnotationModel.collapse(annotation); + } else { + projectionAnnotationModel.expand(annotation); } }); - append(document, document.getLength(), "\n"); scrollText(); List positions = foldingRegions.stream().map(region -> { return new Position(region.startOffset() + offsetPreAppendNewText, region.length()); }).collect(Collectors.toList()); - - HashMap newAnnotations = new HashMap<>(); + HashMap newAnnotations = new HashMap<>(); for (Position position : positions) { ProjectionAnnotation annotation = new ProjectionAnnotation(); newAnnotations.put(annotation, position); } + projectionAnnotations.addAll(newAnnotations.keySet()); projectionAnnotationModel.modifyAnnotations(null, newAnnotations, null); } From c3cf8ea1b114df034df0228cbe426464e526b810 Mon Sep 17 00:00:00 2001 From: Justin van der Krieken Date: Mon, 3 Jul 2017 10:00:37 +0200 Subject: [PATCH 4/4] Suppress Findbugs warning --- .../shell/util/SuppressFBWarnings.java | 26 +++++++++++++++++++ .../client/eclipse/impl/EclipseDisplay.java | 5 ++++ 2 files changed, 31 insertions(+) create mode 100644 org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/util/SuppressFBWarnings.java diff --git a/org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/util/SuppressFBWarnings.java b/org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/util/SuppressFBWarnings.java new file mode 100644 index 00000000..ca0957d8 --- /dev/null +++ b/org.metaborg.spoofax.shell.core/src/main/java/org/metaborg/spoofax/shell/util/SuppressFBWarnings.java @@ -0,0 +1,26 @@ +package org.metaborg.spoofax.shell.util; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Used to suppress FindBugs warnings. + */ +@Retention(RetentionPolicy.CLASS) +public @interface SuppressFBWarnings { + + /** + * The set of FindBugs warnings that are to be suppressed in + * annotated element. The value can be a bug category, kind or pattern. + * + * @return warnings to suppress + */ + String[] value() default {}; + + /** + * Optional documentation of the reason why the warning is suppressed. + * + * @return explaination + */ + String justification() default ""; +} diff --git a/org.metaborg.spoofax.shell.eclipse/src/main/java/org/metaborg/spoofax/shell/client/eclipse/impl/EclipseDisplay.java b/org.metaborg.spoofax.shell.eclipse/src/main/java/org/metaborg/spoofax/shell/client/eclipse/impl/EclipseDisplay.java index ede27894..4ed78b62 100644 --- a/org.metaborg.spoofax.shell.eclipse/src/main/java/org/metaborg/spoofax/shell/client/eclipse/impl/EclipseDisplay.java +++ b/org.metaborg.spoofax.shell.eclipse/src/main/java/org/metaborg/spoofax/shell/client/eclipse/impl/EclipseDisplay.java @@ -39,6 +39,7 @@ import org.metaborg.spoofax.shell.output.PrintResult; import org.metaborg.spoofax.shell.output.StyledText; import org.metaborg.spoofax.shell.services.IEditorServices; +import org.metaborg.spoofax.shell.util.SuppressFBWarnings; import com.google.inject.assistedinject.Assisted; import com.google.inject.assistedinject.AssistedInject; @@ -137,6 +138,10 @@ private void applyFragmentToDocument(IRegionStyle fragment) { } } + @SuppressFBWarnings( + value = "UC_USELESS_OBJECT", + justification = "False positive, of course it has side-effect..." + ) private void displayStyledText(StyledText text, List foldingRegions) { Map annotationStates = new HashMap<>(); for (ProjectionAnnotation annotation : projectionAnnotations) {