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
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
package com.redhat.qute.commons.binary;

/**
* Binary template loaded from a JAR entry (e.g. <code>templates/hello.html</code>).
* Binary template loaded from a JAR entry (e.g.
* <code>templates/hello.html</code>).
*
* @author Angelo ZERR
*
Expand All @@ -25,6 +26,8 @@ public class BinaryTemplate {

private String content;

private boolean altSyntaxExpr;

/**
* Returns the template path relative to the <code>templates/</code> entry of
* the JAR (e.g. <code>tags/search-button.html</code>,
Expand Down Expand Up @@ -98,4 +101,12 @@ public void setContent(String content) {
this.content = content;
}

public boolean isAltSyntaxExpr() {
return altSyntaxExpr;
}

public void setAltSyntaxExpr(boolean altSyntaxExpr) {
this.altSyntaxExpr = altSyntaxExpr;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ public class QuarkusIntegrationForQute {

/** The JAR entry name for the application properties file. */
private static final String APPLICATION_PROPERTIES_ENTRY = "application.properties";
private static final String DOT_QUTE_ENTRY = ".qute";
private static final String ALT_EXPR_PROPERTY = "alt-expr-syntax";

private static final Logger LOGGER = Logger.getLogger(QuarkusIntegrationForQute.class.getName());

Expand Down Expand Up @@ -171,12 +173,17 @@ private static BinaryTemplateInfo collectBinaryTemplates(IPackageFragmentRoot ro
// Compute the relative folder path from 'templates/' for this package.
// e.g. 'templates' -> '', 'templates.tags' -> 'tags'
String relativeFolderPath = buildRelativeFolderPath(elementName);

boolean altSyntaxExpr = false;
for (Object object : resources) {
if (object instanceof IJarEntryResource) {
// A resource can be either a file or a sub-directory.
// Recurse into directories to collect nested files.
collectJarEntry((IJarEntryResource) object, relativeFolderPath, templates);
IJarEntryResource jarEntry = (IJarEntryResource) object;
if (DOT_QUTE_ENTRY.equals(jarEntry.getName())) {
altSyntaxExpr = parseAltSyntaxExpr(jarEntry);
} else {
collectJarEntry(jarEntry, relativeFolderPath, altSyntaxExpr, templates);
}
}
}
}
Expand Down Expand Up @@ -235,10 +242,11 @@ private static BinaryTemplateInfo collectBinaryTemplates(IPackageFragmentRoot ro
* @param resource the JAR entry resource (file or directory).
* @param currentPath the relative path of the parent folder from
* {@code templates/} (empty string for the root level).
* @param altSyntaxExpr
* @param templates the list to fill with collected binary templates.
* @throws CoreException if an error occurs while reading the resource.
*/
private static void collectJarEntry(IJarEntryResource resource, String currentPath, List<BinaryTemplate> templates)
private static void collectJarEntry(IJarEntryResource resource, String currentPath, boolean altSyntaxExpr, List<BinaryTemplate> templates)
throws CoreException {

if (resource.isFile()) {
Expand All @@ -252,6 +260,7 @@ private static void collectJarEntry(IJarEntryResource resource, String currentPa
template.setPath(path);
template.setUri(uri);
template.setContent(content);
template.setAltSyntaxExpr(altSyntaxExpr);
templates.add(template);
return;
}
Expand All @@ -260,7 +269,7 @@ private static void collectJarEntry(IJarEntryResource resource, String currentPa
// This ensures the relative path grows correctly at each recursion level.
String childPath = currentPath.isEmpty() ? resource.getName() : currentPath + "/" + resource.getName();
for (IJarEntryResource child : resource.getChildren()) {
collectJarEntry(child, childPath, templates);
collectJarEntry(child, childPath, altSyntaxExpr, templates);
}
}

Expand Down Expand Up @@ -319,6 +328,18 @@ private static Map<String, String> parseProperties(IJarEntryResource application
return map;
}

private static boolean parseAltSyntaxExpr(IJarEntryResource dotQuteFile) throws CoreException {
Properties props = new Properties();
try {
props.load(dotQuteFile.getContents());
} catch (IOException e) {
LOGGER.log(Level.SEVERE, "Error while loading .qute from JAR entry", e);
return false;
}
Object result = props.getOrDefault(ALT_EXPR_PROPERTY, false);
return result instanceof Boolean ? (Boolean) result : false;
}

/**
* Converts the given {@link InputStream} into a String. The stream is closed
* automatically after reading.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ public void setPath(String path) {

private boolean namespacedTagSupported;

private Boolean altExprSyntax;

private transient Path basePath;

private transient Path tagsDir;
Expand Down Expand Up @@ -114,10 +116,28 @@ public void setNamespacedTagSupported(boolean namespacedTagSupported) {
this.namespacedTagSupported = namespacedTagSupported;
}

/**
* Returns the alternative expression syntax configuration from .qute file.
*
* @return true if enabled via .qute, false if disabled via .qute, null if no .qute file
*/
public Boolean getAltExprSyntax() {
return altExprSyntax;
}

/**
* Sets the alternative expression syntax configuration from .qute file.
*
* @param altExprSyntax true to enable, false to disable, null if no .qute file
*/
public void setAltExprSyntax(Boolean altExprSyntax) {
this.altExprSyntax = altExprSyntax;
}

/**
* Returns the base path of the template root path (ex
* :src/main/resources/templates) and null otherwise.
*
*
* @return the base path of the template root path (ex
* :src/main/resources/templates) and null otherwise.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
package com.redhat.qute.commons.binary;

/**
* Binary template loaded from a JAR entry (e.g. <code>templates/hello.html</code>).
* Binary template loaded from a JAR entry (e.g.
* <code>templates/hello.html</code>).
*
* @author Angelo ZERR
*
Expand All @@ -25,6 +26,8 @@ public class BinaryTemplate {

private String content;

private boolean altSyntaxExpr;

/**
* Returns the template path relative to the <code>templates/</code> entry of
* the JAR (e.g. <code>tags/search-button.html</code>,
Expand Down Expand Up @@ -98,4 +101,12 @@ public void setContent(String content) {
this.content = content;
}

public boolean isAltSyntaxExpr() {
return altSyntaxExpr;
}

public void setAltSyntaxExpr(boolean altSyntaxExpr) {
this.altSyntaxExpr = altSyntaxExpr;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,11 @@ public TemplateFileTextDocumentService(QuteLanguageService quteLanguageService,
this.quteLanguageService = quteLanguageService;
this.projectRegistry = quteLanguageService.getProjectRegistry();
this.openedDocuments = new QuteOpenedTextDocuments((document, cancelChecker) -> {
Collection<InjectionDetector> injectionDetectors = ((QuteOpenedTextDocument) document)
.getInjectionDetectors();
return TemplateParser.parse(document, injectionDetectors, () -> cancelChecker.checkCanceled());
QuteOpenedTextDocument openedDocument = (QuteOpenedTextDocument) document;
Collection<InjectionDetector> injectionDetectors = openedDocument.getInjectionDetectors();
Character expressionCommand = openedDocument.getExpressionCommand();
return TemplateParser.parse(document, expressionCommand, injectionDetectors,
() -> cancelChecker.checkCanceled());
}, projectInfoProvider, projectRegistry);
this.validatorDelayer = new ValidatorDelayer<ModelTextDocument<Template>>((template) -> {
triggerValidationFor((QuteTextDocument) template);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,17 @@ public String getNodeName() {
}

/**
* Returns the start offset of the expression content (after '{')
* Returns the start offset of the expression content (after '{' or '{=' for alternative syntax)
*
* @return the start offset of the expression content (after '{')
* @return the start offset of the expression content (after '{' or '{=')
*/
public int getStartContentOffset() {
Character expressionCommand = getOwnerTemplate().getExpressionCommand();
if (expressionCommand != null) {
// Alternative syntax: {=foo} -> content starts after '{='
return getStart() + 2;
}
// Standard syntax: {foo} -> content starts after '{'
return getStart() + 1;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ public class Template extends Node {

private String userTagName;

private Character expressionCommand;

public Template(TextDocument textDocument) {
super(0, textDocument.getText().length());
this.textDocument = textDocument;
Expand Down Expand Up @@ -339,4 +341,22 @@ public String getUserTagName() {
public void setUserTagName(String userTagName) {
this.userTagName = userTagName;
}

/**
* Returns the expression command character for alternative syntax (e.g., '=' for {=foo}) or null for standard syntax.
*
* @return the expression command character or null
*/
public Character getExpressionCommand() {
return expressionCommand;
}

/**
* Sets the expression command character for alternative syntax (e.g., '=' for {=foo}).
*
* @param expressionCommand the expression command character or null for standard syntax
*/
public void setExpressionCommand(Character expressionCommand) {
this.expressionCommand = expressionCommand;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,38 +42,48 @@ public class TemplateParser {
private static final SectionFactory DEFAULT_SECTION_FACTORY = new DefaultSectionFactory();

public static Template parse(String content, String uri) {
return parse(content, uri, Collections.emptyList());
return parse(content, uri, (Character) null);
}

public static Template parse(String content, String uri, Character expressionCommand) {
return parse(content, uri, expressionCommand, Collections.emptyList());
}

public static Template parse(String content, String uri, Collection<InjectionDetector> injectionDetectors) {
return parse(content, uri, injectionDetectors, CancelChecker.NO_CANCELLABLE);
return parse(content, uri, null, injectionDetectors);
}

public static Template parse(String content, String uri, Character expressionCommand,
Collection<InjectionDetector> injectionDetectors) {
return parse(content, uri, expressionCommand, injectionDetectors, CancelChecker.NO_CANCELLABLE);
}

public static Template parse(String text, String uri, Collection<InjectionDetector> injectionDetectors,
CancelChecker cancelChecker) {
return parse(new TextDocument(text, uri), injectionDetectors, cancelChecker);
public static Template parse(String text, String uri, Character expressionCommand,
Collection<InjectionDetector> injectionDetectors, CancelChecker cancelChecker) {
return parse(new TextDocument(text, uri), expressionCommand, injectionDetectors, cancelChecker);
}

public static Template parse(TextDocument textDocument, Collection<InjectionDetector> injectionDetectors,
CancelChecker cancelChecker) {
return parse(textDocument, DEFAULT_SECTION_FACTORY, injectionDetectors, cancelChecker);
public static Template parse(TextDocument textDocument, Character expressionCommand,
Collection<InjectionDetector> injectionDetectors, CancelChecker cancelChecker) {
return parse(textDocument, DEFAULT_SECTION_FACTORY, expressionCommand, injectionDetectors, cancelChecker);
}

public static Template parse(TextDocument textDocument, SectionFactory sectionFactory,
public static Template parse(TextDocument textDocument, SectionFactory sectionFactory, Character expressionCommand,
Collection<InjectionDetector> injectionDetectors, CancelChecker cancelChecker) {
if (cancelChecker == null) {
cancelChecker = CancelChecker.NO_CANCELLABLE;
}
Template template = new Template(textDocument);
template.setCancelChecker(cancelChecker);
template.setExpressionCommand(expressionCommand);

Node curr = template;

String content = textDocument.getText();
int endTagOpenOffset = -1;
int startSectionOffset = -1;
int endSectionOffset = -1;
ScannerWithInjection<TokenType, ScannerState> scanner = TemplateScanner.createScanner(content,
ScannerWithInjection<TokenType, ScannerState> scanner = TemplateScanner.createScanner(content, expressionCommand,
injectionDetectors);
TokenType token = scanner.scan();
while (token != TokenType.EOS) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,39 +39,38 @@ public class TemplateScanner extends AbstractScannerWithInjection<TokenType, Sca
private static final Predicate<Integer> TAG_NAME_PREDICATE = ch -> {
return Character.isLetterOrDigit(ch) || ch == '_' || ch == '-' || ch == '/';
};

private final Character expressionCommand;

public static ScannerWithInjection<TokenType, ScannerState> createScanner(String input) {
return createScanner(input, Collections.emptyList());
return createScanner(input, null, Collections.emptyList());
}

public static ScannerWithInjection<TokenType, ScannerState> createScanner(String input,
Collection<InjectionDetector> injectionDetectors) {
return createScanner(input, 0, injectionDetectors);
return createScanner(input, null, injectionDetectors);
}

public static ScannerWithInjection<TokenType, ScannerState> createScanner(String input, int initialOffset) {
return createScanner(input, initialOffset, Collections.emptyList());
}

public static ScannerWithInjection<TokenType, ScannerState> createScanner(String input, int initialOffset,
public static ScannerWithInjection<TokenType, ScannerState> createScanner(String input, Character expressionCommand,
Collection<InjectionDetector> injectionDetectors) {
return createScanner(input, initialOffset, ScannerState.WithinContent, injectionDetectors);
return createScanner(input, 0, expressionCommand, injectionDetectors);
}

public static ScannerWithInjection<TokenType, ScannerState> createScanner(String input, int initialOffset,
ScannerState initialState) {
return createScanner(input, initialOffset, initialState, Collections.emptyList());
Character expressionCommand, Collection<InjectionDetector> injectionDetectors) {
return createScanner(input, initialOffset, ScannerState.WithinContent, expressionCommand, injectionDetectors);
}

public static ScannerWithInjection<TokenType, ScannerState> createScanner(String input, int initialOffset,
ScannerState initialState, Collection<InjectionDetector> injectionDetectors) {
return new TemplateScanner(input, initialOffset, initialState, injectionDetectors);
ScannerState initialState, Character expressionCommand, Collection<InjectionDetector> injectionDetectors) {
return new TemplateScanner(input, initialOffset, initialState, expressionCommand, injectionDetectors);
}

TemplateScanner(String input, int initialOffset, ScannerState initialState,
TemplateScanner(String input, int initialOffset, ScannerState initialState, Character expressionCommand,
Collection<InjectionDetector> injectionDetectors) {
super(input, initialOffset, initialState, TokenType.Unknown, TokenType.EOS, TokenType.LanguageInjectionStart,
TokenType.LanguageInjectionContent, TokenType.LanguageInjectionEnd, injectionDetectors);
this.expressionCommand = expressionCommand;
}

@Override
Expand Down Expand Up @@ -125,9 +124,12 @@ protected TokenType scanNormal() {
return finishToken(offset, TokenType.StartParameterDeclaration);
} else {
int ch = stream.peekChar();
if (isValidIdentifierStart(ch)) {
if (isValidIdentifierStart(ch, expressionCommand)) {
// Expression
state = ScannerState.WithinExpression;
if (expressionCommand != null) {
stream.advance(1);
}
return finishToken(offset, TokenType.StartExpression);
} else {
// Text node, increment position if needed
Expand Down Expand Up @@ -304,7 +306,10 @@ protected TokenType scanNormal() {
finishToken(offset, TokenType.Unknown, errorMessage);
}

private static boolean isValidIdentifierStart(int ch) {
private static boolean isValidIdentifierStart(int ch, Character expressionCommand) {
if (expressionCommand != null) {
return expressionCommand.charValue() == ch;
}
return Character.isDigit(ch) || Character.isAlphabetic(ch) || ch == '_';
}

Expand Down
Loading
Loading