Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix all whitespace -- alternative approach #216

Closed
wants to merge 13 commits into from
5 changes: 3 additions & 2 deletions compiler/src/main/java/com/github/mustachejava/Code.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.github.mustachejava;

import com.github.mustachejava.util.IndentWriter;
import com.github.mustachejava.util.Node;

import java.io.Writer;
Expand All @@ -11,9 +12,9 @@
* Code objects that are executed in order to evaluate the template
*/
public interface Code {
Writer execute(Writer writer, List<Object> scopes);
IndentWriter execute(IndentWriter writer, List<Object> scopes);

void identity(Writer writer);
void identity(IndentWriter writer);

void append(String text);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public class DefaultMustacheFactory implements MustacheFactory {
/**
* This parser should work with any MustacheFactory
*/
protected final MustacheParser mc = new MustacheParser(this);
protected final MustacheParser mc = createParser();

/**
* New templates that are generated at runtime are cached here. The template key
Expand Down Expand Up @@ -259,6 +259,10 @@ public Mustache compilePartial(String s) {
}
}

protected MustacheParser createParser() {
return new MustacheParser(this);
}

protected Function<String, Mustache> getMustacheCacheFunction() {
return mc::compile;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public void checkName(TemplateContext templateContext, String variable, Mustache
}

@Override
public void partial(TemplateContext tc, final String variable) {
public void partial(TemplateContext tc, final String variable, String indent) {
TemplateContext partialTC = new TemplateContext("{{", "}}", tc.file(), tc.line(), tc.startOfLine());
list.add(new PartialCode(partialTC, df, variable));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.github.mustachejava.codes.PartialCode;
import com.github.mustachejava.util.GuardException;
import com.github.mustachejava.util.IndentWriter;
import com.github.mustachejava.util.InternalArrayList;
import com.github.mustachejava.util.Wrapper;

Expand Down Expand Up @@ -70,14 +71,14 @@ public MustacheVisitor createMustacheVisitor() {
final AtomicLong id = new AtomicLong(0);
return new DefaultMustacheVisitor(this) {
@Override
public void partial(TemplateContext tc, final String variable) {
public void partial(TemplateContext tc, final String variable, final String indent) {
TemplateContext partialTC = new TemplateContext("{{", "}}", tc.file(), tc.line(), tc.startOfLine());
final Long divid = id.incrementAndGet();
list.add(new PartialCode(partialTC, df, variable) {
Wrapper deferredWrapper;

@Override
public Writer execute(Writer writer, final List<Object> scopes) {
public IndentWriter execute(IndentWriter writer, final List<Object> scopes) {

final Object object = get(scopes);
final DeferredCallable deferredCallable = getDeferred(scopes);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.github.mustachejava;

import com.github.mustachejava.util.IndentWriter;

import java.io.Writer;
import java.util.List;

Expand All @@ -8,5 +10,5 @@
* method in an ObjectHandler to change the types recognized by mustache.java as iterable.
*/
public interface Iteration {
Writer next(Writer writer, Object next, List<Object> scopes);
IndentWriter next(IndentWriter writer, Object next, List<Object> scopes);
}
16 changes: 13 additions & 3 deletions compiler/src/main/java/com/github/mustachejava/Mustache.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.github.mustachejava;

import com.github.mustachejava.util.BaseIndentWriter;
import com.github.mustachejava.util.IndentWriter;
import com.github.mustachejava.util.InternalArrayList;
import com.github.mustachejava.util.Node;

Expand Down Expand Up @@ -53,7 +55,11 @@ default Writer execute(Writer writer, Object[] scopes) {
* @param scopes an ordered list of scopes for variable resolution
* @return the new writer
*/
Writer execute(Writer writer, List<Object> scopes);
default Writer execute(Writer writer, List<Object> scopes) {
return execute(new BaseIndentWriter(writer), scopes);
}

IndentWriter execute(IndentWriter writer, List<Object> scopes);

/**
* Get the underlying code objects.
Expand All @@ -67,7 +73,11 @@ default Writer execute(Writer writer, Object[] scopes) {
*
* @param writer write the output of the executed template here
*/
void identity(Writer writer);
void identity(IndentWriter writer);

default void identity(Writer writer) {
this.identity(new BaseIndentWriter(writer));
}

/**
* Initialize the mustache before executing. This is must be called at least once
Expand All @@ -89,7 +99,7 @@ default Writer execute(Writer writer, Object[] scopes) {
* @param scopes the array of scopes to execute
* @return the replacement writer
*/
Writer run(Writer writer, List<Object> scopes);
IndentWriter run(IndentWriter writer, List<Object> scopes);

/**
* Invert this mustache given output text.
Expand Down
101 changes: 93 additions & 8 deletions compiler/src/main/java/com/github/mustachejava/MustacheParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,21 @@
public class MustacheParser {
public static final String DEFAULT_SM = "{{";
public static final String DEFAULT_EM = "}}";

/**
* For legacy reasons we keep the non-spec conform parsing of whitespace unless this flag is true.
*/
private final boolean specConformWhitespace;

private MustacheFactory mf;

protected MustacheParser(MustacheFactory mf) {
protected MustacheParser(MustacheFactory mf, boolean specConformWhitespace) {
this.mf = mf;
this.specConformWhitespace = specConformWhitespace;
}

protected MustacheParser(MustacheFactory mf) {
this(mf, false);
}

public Mustache compile(String file) {
Expand Down Expand Up @@ -74,7 +85,7 @@ protected Mustache compile(final Reader reader, String tag, final AtomicInteger
// Increment the line
if (c == '\n') {
currentLine.incrementAndGet();
if (!iterable || (iterable && !onlywhitespace)) {
if (specConformWhitespace || !iterable || (iterable && !onlywhitespace)) {
if (sawCR) out.append("\r");
out.append("\n");
}
Expand Down Expand Up @@ -140,13 +151,19 @@ protected Mustache compile(final Reader reader, String tag, final AtomicInteger
case '$': {
boolean oldStartOfLine = startOfLine;
startOfLine = startOfLine & onlywhitespace;

boolean nextStartOfLine = specConformWhitespace && trimNewline(startOfLine, br);

int line = currentLine.get();
final Mustache mustache = compile(br, variable, currentLine, file, sm, em, startOfLine);
int lines = currentLine.get() - line;
if (!onlywhitespace || lines == 0) {

if ((specConformWhitespace && !nextStartOfLine) ||
(!specConformWhitespace && (!onlywhitespace || lines == 0))) {
write(mv, out, file, currentLine.intValue(), oldStartOfLine);
}
out = new StringBuilder();

switch (ch) {
case '#':
mv.iterable(new TemplateContext(sm, em, file, line, startOfLine), variable, mustache);
Expand All @@ -164,14 +181,21 @@ protected Mustache compile(final Reader reader, String tag, final AtomicInteger
mv.checkName(new TemplateContext(sm, em, file, line, startOfLine), variable, mustache);
break;
}

startOfLine = nextStartOfLine;
iterable = lines != 0;
break;
}
case '/': {
// Tag end
if (!startOfLine || !onlywhitespace) {
if (specConformWhitespace) {
if (!trimNewline(onlywhitespace & startOfLine, br)) {
write(mv, out, file, currentLine.intValue(), startOfLine);
}
} else if (!startOfLine || !onlywhitespace) {
write(mv, out, file, currentLine.intValue(), startOfLine);
}

if (!variable.equals(tag)) {
TemplateContext tc = new TemplateContext(sm, em, file, currentLine.get(), startOfLine);
throw new MustacheException(
Expand All @@ -181,9 +205,13 @@ protected Mustache compile(final Reader reader, String tag, final AtomicInteger
return mv.mustache(new TemplateContext(sm, em, file, 0, startOfLine));
}
case '>': {
String indent = (onlywhitespace && startOfLine) ? out.toString() : "";
out = write(mv, out, file, currentLine.intValue(), startOfLine);
startOfLine = startOfLine & onlywhitespace;
mv.partial(new TemplateContext(sm, em, file, currentLine.get(), startOfLine), variable);
mv.partial(new TemplateContext(sm, em, file, currentLine.get(), startOfLine), variable, indent);

// a new line following a partial is dropped
startOfLine = specConformWhitespace && trimNewline(startOfLine, br);
break;
}
case '{': {
Expand All @@ -200,12 +228,16 @@ protected Mustache compile(final Reader reader, String tag, final AtomicInteger
}
}
mv.value(new TemplateContext(sm, em, file, currentLine.get(), false), name, false);

startOfLine = false;
break;
}
case '&': {
// Not escaped
out = write(mv, out, file, currentLine.intValue(), startOfLine);
mv.value(new TemplateContext(sm, em, file, currentLine.get(), false), variable, false);

startOfLine = false;
break;
}
case '%':
Expand All @@ -222,15 +254,17 @@ protected Mustache compile(final Reader reader, String tag, final AtomicInteger
args = variable.substring(index + 1);
}
mv.pragma(new TemplateContext(sm, em, file, currentLine.get(), startOfLine), pragma, args);

startOfLine = false;
break;
case '!':
// Comment
mv.comment(new TemplateContext(sm, em, file, currentLine.get(), startOfLine), variable);
out = write(mv, out, file, currentLine.intValue(), startOfLine);
startOfLine = false;
break;
case '=':
// Change delimiters
out = write(mv, out, file, currentLine.intValue(), startOfLine);
String trimmed = command.substring(1).trim();
String[] split = trimmed.split("\\s+");
if (split.length != 2) {
Expand All @@ -239,6 +273,23 @@ protected Mustache compile(final Reader reader, String tag, final AtomicInteger
}
sm = split[0];
em = split[1];


if (specConformWhitespace) {
boolean sol = trimNewline(startOfLine & onlywhitespace, br);

if (!sol) {
write(mv, out, file, currentLine.intValue(), startOfLine);
}

startOfLine = sol;
} else {
write(mv, out, file, currentLine.intValue(), startOfLine);
startOfLine = false;
}
out = new StringBuilder();


break;
default: {
if (c == -1) {
Expand All @@ -248,11 +299,16 @@ protected Mustache compile(final Reader reader, String tag, final AtomicInteger
// Reference
out = write(mv, out, file, currentLine.intValue(), startOfLine);
mv.value(new TemplateContext(sm, em, file, currentLine.get(), false), command.trim(), true);
startOfLine = false;
break;
}
}
// Additional text is no longer at the start of the line
startOfLine = false;
if (!specConformWhitespace) {
// Additional text is no longer at the start of the line
// in spec-conform whitespace parsing we sometimes chop a whole line so we let the individual commands
// decide wether we are at the start of a line
startOfLine = false;
}
continue;
} else {
// Only one
Expand Down Expand Up @@ -297,4 +353,33 @@ private StringBuilder write(MustacheVisitor mv, StringBuilder out, String file,
return new StringBuilder();
}

/**
* Some statements such as partials are treated as "standalone".
* This means that if they are the only content on this line (except whitespace) then the following newline is
* chopped.
* For backwards compatibility we only do this if the parser is explicitly configured so.
* @param firstStmt If the statement that was just read was at the start of line with only whitespace preceding it
* @param br The reader
* @return true if trimming was allowed and a following new line was removed or the buffer was finished;
* @throws IOException
*/
private boolean trimNewline(boolean firstStmt, Reader br) throws IOException {
boolean trimmed = false;

if (firstStmt) {
br.mark(2);
int ca = br.read();
if (ca == '\r') {
ca = br.read();
}

if (ca == '\n' || ca == -1) {
trimmed = true;
} else {
br.reset();
}
}

return trimmed;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public interface MustacheVisitor {

void notIterable(TemplateContext templateContext, String variable, Mustache mustache);

void partial(TemplateContext templateContext, String variable);
void partial(TemplateContext templateContext, String variable, String indent);

void value(TemplateContext templateContext, String variable, boolean encoded);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.github.mustachejava;

import com.github.mustachejava.util.IndentWriter;
import com.github.mustachejava.util.InternalArrayList;
import com.github.mustachejava.util.Wrapper;

Expand Down Expand Up @@ -37,7 +38,7 @@ public interface ObjectHandler {
* @param scopes the scopes present
* @return the current writer
*/
Writer iterate(Iteration iteration, Writer writer, Object object, List<Object> scopes);
IndentWriter iterate(Iteration iteration, IndentWriter writer, Object object, List<Object> scopes);

/**
* Call Iteration.next() either 0 (true) or 1 (fale) times.
Expand All @@ -48,7 +49,7 @@ public interface ObjectHandler {
* @param scopes the scopes present
* @return the current writer
*/
Writer falsey(Iteration iteration, Writer writer, Object object, List<Object> scopes);
IndentWriter falsey(Iteration iteration, IndentWriter writer, Object object, List<Object> scopes);

/**
* Each call site has its own binding to allow for fine grained caching without
Expand Down
Loading