diff --git a/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/commons/binary/BinaryTemplate.java b/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/commons/binary/BinaryTemplate.java index 910a4fbf8..74128ea43 100644 --- a/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/commons/binary/BinaryTemplate.java +++ b/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/commons/binary/BinaryTemplate.java @@ -12,7 +12,8 @@ package com.redhat.qute.commons.binary; /** - * Binary template loaded from a JAR entry (e.g. templates/hello.html). + * Binary template loaded from a JAR entry (e.g. + * templates/hello.html). * * @author Angelo ZERR * @@ -25,6 +26,8 @@ public class BinaryTemplate { private String content; + private boolean altSyntaxExpr; + /** * Returns the template path relative to the templates/ entry of * the JAR (e.g. tags/search-button.html, @@ -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; + } + } \ No newline at end of file diff --git a/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/internal/template/QuarkusIntegrationForQute.java b/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/internal/template/QuarkusIntegrationForQute.java index 74efa481f..88bbf17ce 100644 --- a/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/internal/template/QuarkusIntegrationForQute.java +++ b/qute.jdt/com.redhat.qute.jdt/src/main/java/com/redhat/qute/jdt/internal/template/QuarkusIntegrationForQute.java @@ -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()); @@ -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); + } } } } @@ -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 templates) + private static void collectJarEntry(IJarEntryResource resource, String currentPath, boolean altSyntaxExpr, List templates) throws CoreException { if (resource.isFile()) { @@ -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; } @@ -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); } } @@ -319,6 +328,18 @@ private static Map 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. diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/commons/TemplateRootPath.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/commons/TemplateRootPath.java index 1a95c8807..a8bc3c146 100644 --- a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/commons/TemplateRootPath.java +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/commons/TemplateRootPath.java @@ -56,6 +56,8 @@ public void setPath(String path) { private boolean namespacedTagSupported; + private Boolean altExprSyntax; + private transient Path basePath; private transient Path tagsDir; @@ -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. */ diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/commons/binary/BinaryTemplate.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/commons/binary/BinaryTemplate.java index 910a4fbf8..74128ea43 100644 --- a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/commons/binary/BinaryTemplate.java +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/commons/binary/BinaryTemplate.java @@ -12,7 +12,8 @@ package com.redhat.qute.commons.binary; /** - * Binary template loaded from a JAR entry (e.g. templates/hello.html). + * Binary template loaded from a JAR entry (e.g. + * templates/hello.html). * * @author Angelo ZERR * @@ -25,6 +26,8 @@ public class BinaryTemplate { private String content; + private boolean altSyntaxExpr; + /** * Returns the template path relative to the templates/ entry of * the JAR (e.g. tags/search-button.html, @@ -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; + } + } \ No newline at end of file diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/ls/template/TemplateFileTextDocumentService.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/ls/template/TemplateFileTextDocumentService.java index 38f88dc19..529307428 100644 --- a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/ls/template/TemplateFileTextDocumentService.java +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/ls/template/TemplateFileTextDocumentService.java @@ -110,9 +110,11 @@ public TemplateFileTextDocumentService(QuteLanguageService quteLanguageService, this.quteLanguageService = quteLanguageService; this.projectRegistry = quteLanguageService.getProjectRegistry(); this.openedDocuments = new QuteOpenedTextDocuments((document, cancelChecker) -> { - Collection injectionDetectors = ((QuteOpenedTextDocument) document) - .getInjectionDetectors(); - return TemplateParser.parse(document, injectionDetectors, () -> cancelChecker.checkCanceled()); + QuteOpenedTextDocument openedDocument = (QuteOpenedTextDocument) document; + Collection injectionDetectors = openedDocument.getInjectionDetectors(); + Character expressionCommand = openedDocument.getExpressionCommand(); + return TemplateParser.parse(document, expressionCommand, injectionDetectors, + () -> cancelChecker.checkCanceled()); }, projectInfoProvider, projectRegistry); this.validatorDelayer = new ValidatorDelayer>((template) -> { triggerValidationFor((QuteTextDocument) template); diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/parser/template/Expression.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/parser/template/Expression.java index fb7f72c05..0836b0374 100644 --- a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/parser/template/Expression.java +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/parser/template/Expression.java @@ -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; } diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/parser/template/Template.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/parser/template/Template.java index 978765011..aefdb09b5 100644 --- a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/parser/template/Template.java +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/parser/template/Template.java @@ -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; @@ -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; + } } diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/parser/template/TemplateParser.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/parser/template/TemplateParser.java index cbbba0789..d90fb6e78 100644 --- a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/parser/template/TemplateParser.java +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/parser/template/TemplateParser.java @@ -42,30 +42,40 @@ 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 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 injectionDetectors) { + return parse(content, uri, expressionCommand, injectionDetectors, CancelChecker.NO_CANCELLABLE); } - public static Template parse(String text, String uri, Collection injectionDetectors, - CancelChecker cancelChecker) { - return parse(new TextDocument(text, uri), injectionDetectors, cancelChecker); + public static Template parse(String text, String uri, Character expressionCommand, + Collection injectionDetectors, CancelChecker cancelChecker) { + return parse(new TextDocument(text, uri), expressionCommand, injectionDetectors, cancelChecker); } - public static Template parse(TextDocument textDocument, Collection injectionDetectors, - CancelChecker cancelChecker) { - return parse(textDocument, DEFAULT_SECTION_FACTORY, injectionDetectors, cancelChecker); + public static Template parse(TextDocument textDocument, Character expressionCommand, + Collection 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 injectionDetectors, CancelChecker cancelChecker) { if (cancelChecker == null) { cancelChecker = CancelChecker.NO_CANCELLABLE; } Template template = new Template(textDocument); template.setCancelChecker(cancelChecker); + template.setExpressionCommand(expressionCommand); Node curr = template; @@ -73,7 +83,7 @@ public static Template parse(TextDocument textDocument, SectionFactory sectionFa int endTagOpenOffset = -1; int startSectionOffset = -1; int endSectionOffset = -1; - ScannerWithInjection scanner = TemplateScanner.createScanner(content, + ScannerWithInjection scanner = TemplateScanner.createScanner(content, expressionCommand, injectionDetectors); TokenType token = scanner.scan(); while (token != TokenType.EOS) { diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/parser/template/scanner/TemplateScanner.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/parser/template/scanner/TemplateScanner.java index a6536ed8b..62ae2789b 100644 --- a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/parser/template/scanner/TemplateScanner.java +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/parser/template/scanner/TemplateScanner.java @@ -39,39 +39,38 @@ public class TemplateScanner extends AbstractScannerWithInjection TAG_NAME_PREDICATE = ch -> { return Character.isLetterOrDigit(ch) || ch == '_' || ch == '-' || ch == '/'; }; + + private final Character expressionCommand; public static ScannerWithInjection createScanner(String input) { - return createScanner(input, Collections.emptyList()); + return createScanner(input, null, Collections.emptyList()); } public static ScannerWithInjection createScanner(String input, Collection injectionDetectors) { - return createScanner(input, 0, injectionDetectors); + return createScanner(input, null, injectionDetectors); } - public static ScannerWithInjection createScanner(String input, int initialOffset) { - return createScanner(input, initialOffset, Collections.emptyList()); - } - - public static ScannerWithInjection createScanner(String input, int initialOffset, + public static ScannerWithInjection createScanner(String input, Character expressionCommand, Collection injectionDetectors) { - return createScanner(input, initialOffset, ScannerState.WithinContent, injectionDetectors); + return createScanner(input, 0, expressionCommand, injectionDetectors); } public static ScannerWithInjection createScanner(String input, int initialOffset, - ScannerState initialState) { - return createScanner(input, initialOffset, initialState, Collections.emptyList()); + Character expressionCommand, Collection injectionDetectors) { + return createScanner(input, initialOffset, ScannerState.WithinContent, expressionCommand, injectionDetectors); } public static ScannerWithInjection createScanner(String input, int initialOffset, - ScannerState initialState, Collection injectionDetectors) { - return new TemplateScanner(input, initialOffset, initialState, injectionDetectors); + ScannerState initialState, Character expressionCommand, Collection 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 injectionDetectors) { super(input, initialOffset, initialState, TokenType.Unknown, TokenType.EOS, TokenType.LanguageInjectionStart, TokenType.LanguageInjectionContent, TokenType.LanguageInjectionEnd, injectionDetectors); + this.expressionCommand = expressionCommand; } @Override @@ -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 @@ -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 == '_'; } diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/project/QuteProject.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/project/QuteProject.java index d3ae2a73f..0637a3bfd 100644 --- a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/project/QuteProject.java +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/project/QuteProject.java @@ -56,6 +56,7 @@ import com.redhat.qute.commons.binary.BinaryTemplate; import com.redhat.qute.commons.binary.BinaryTemplateInfo; import com.redhat.qute.commons.binary.QuteBinaryTemplateParams; +import com.redhat.qute.commons.config.PropertyConfig; import com.redhat.qute.commons.datamodel.DataModelParameter; import com.redhat.qute.commons.datamodel.DataModelProject; import com.redhat.qute.commons.datamodel.DataModelTemplate; @@ -64,10 +65,7 @@ import com.redhat.qute.commons.datamodel.resolvers.ValueResolverKind; import com.redhat.qute.commons.jaxrs.JaxRsParamKind; import com.redhat.qute.commons.jaxrs.RestParam; -import com.redhat.qute.parser.expression.InfixNotationMethodPart; -import com.redhat.qute.parser.expression.MethodPart; import com.redhat.qute.parser.expression.Part; -import com.redhat.qute.parser.expression.Parts.PartKind; import com.redhat.qute.parser.injection.InjectionDetector; import com.redhat.qute.parser.template.LiteralSupport; import com.redhat.qute.parser.template.Parameter; @@ -101,7 +99,9 @@ import com.redhat.qute.project.extensions.InlayHintParticipant; import com.redhat.qute.project.extensions.MemberResolutionParticipant; import com.redhat.qute.project.extensions.ProjectExtension; +import com.redhat.qute.project.extensions.ProjectExtensionContext; import com.redhat.qute.project.extensions.TemplateLanguageInjectionParticipant; +import com.redhat.qute.project.extensions.config.ApplicationPropertiesProjectExtension; import com.redhat.qute.project.tags.UserTag; import com.redhat.qute.project.tags.UserTagRegistry; import com.redhat.qute.project.usages.IncludeUsagesRegistry; @@ -191,6 +191,8 @@ public class QuteProject implements JavaTypeResolver { private CompletableFuture loadQuteProjectFuture; + private final ApplicationPropertiesProjectExtension applicationProperties; + public QuteProject(ProjectInfo projectInfo, QuteProjectRegistry projectRegistry) { this.uri = projectInfo.getUri(); this.projectFolder = FileUtils.createPath(projectInfo.getProjectFolder()); @@ -228,6 +230,8 @@ public QuteProject(ProjectInfo projectInfo, QuteProjectRegistry projectRegistry) LOGGER.log(Level.SEVERE, "Error while instantiating extension", e); } } + this.applicationProperties = (ApplicationPropertiesProjectExtension) this + .getExtension(ApplicationPropertiesProjectExtension.APPLICATION_PROPERTIES_PROJECT_EXTENSION_ID); } void registerExtension(ProjectExtension extension) { @@ -804,14 +808,16 @@ public CompletableFuture getDataModelProject() { if (!isFutureLoaded(dataModelProjectFuture)) { dataModelProjectFuture = loadDataModelProject() // .thenApply(model -> { + ProjectExtensionContext context = new ProjectExtensionContext(); for (ProjectExtension extension : getExtensions()) { try { - extension.init(model); + extension.initialize(model, context); } catch (Exception e) { LOGGER.log(Level.SEVERE, "Error while loading project extension '" + extension.getId() + "'", e); } } + context.reparseTemplates(); return model; }); @@ -1148,7 +1154,8 @@ public JavaMemberResult findProperty(Part part, ResolvedJavaTypeInfo baseType, b for (ProjectExtension extension : getExtensions()) { if (extension.isEnabled() && extension instanceof MemberResolutionParticipant) { MemberResolutionParticipant participant = (MemberResolutionParticipant) extension; - List additionalTypes = participant.getAdditionalTypes(baseType, previousPart, part, template); + List additionalTypes = participant.getAdditionalTypes(baseType, + previousPart, part, template); if (additionalTypes != null) { for (ResolvedJavaTypeInfo additionalType : additionalTypes) { member = findPropertyWithJavaReflection(additionalType, part.getPartName()); @@ -1819,7 +1826,8 @@ public JavaMemberInfo findMember(ResolvedJavaTypeInfo baseType, Part part) { for (ProjectExtension extension : getExtensions()) { if (extension.isEnabled() && extension instanceof MemberResolutionParticipant) { MemberResolutionParticipant participant = (MemberResolutionParticipant) extension; - List additionalTypes = participant.getAdditionalTypes(baseType, previousPart, part, template); + List additionalTypes = participant.getAdditionalTypes(baseType, previousPart, + part, template); if (additionalTypes != null) { for (ResolvedJavaTypeInfo additionalType : additionalTypes) { member = findPropertyWithJavaReflection(additionalType, part.getPartName()); @@ -2214,7 +2222,8 @@ protected void registerBinaryTemplates(List binaryTemplates) Map properties = binaryTemplate.getProperties(); List templates = binaryTemplate.getTemplates(); for (BinaryTemplate template : templates) { - QuteBinaryTextDocument document = new QuteBinaryTextDocument(template, binaryName, properties, this); + QuteBinaryTextDocument document = new QuteBinaryTextDocument(template, binaryName, properties, + template.isAltSyntaxExpr() ? '=' : null, this); registerBinaryDocument(document); } } @@ -2297,4 +2306,23 @@ public IncludeUsagesRegistry getIncludeUsagesRegistry() { public String getFullyQualifiedName(String shortName) { return javaCache.getFullyQualifiedName(shortName); } + + public String getConfig(PropertyConfig property) { + if (applicationProperties != null) { + return applicationProperties.getConfig(property); + } + return property.getDefaultValue(); + } + + public boolean getConfigAsBoolean(PropertyConfig property) { + String value = getConfig(property); + return "true".equals(value); + } + + public Character getExpressionCommand() { + if (getConfigAsBoolean(ApplicationPropertiesProjectExtension.ALT_EXPR_SYNTAX)) { + return '='; + } + return null; + } } \ No newline at end of file diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/project/QuteProjectRegistry.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/project/QuteProjectRegistry.java index a906c5a45..3966f15db 100644 --- a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/project/QuteProjectRegistry.java +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/project/QuteProjectRegistry.java @@ -62,6 +62,7 @@ import com.redhat.qute.project.documents.QuteOpenedTextDocument; import com.redhat.qute.project.documents.TemplateValidator; import com.redhat.qute.project.extensions.DidChangeWatchedFilesParticipant; +import com.redhat.qute.project.extensions.ProjectExtensionContext; import com.redhat.qute.services.nativemode.JavaTypeFilter; import com.redhat.qute.services.nativemode.ReflectionJavaTypeFilter; import com.redhat.qute.settings.QuteNativeSettings; @@ -385,13 +386,15 @@ public void didChangeWatchedFiles(DidChangeWatchedFilesParams params) { } } } + ProjectExtensionContext context = new ProjectExtensionContext(); for (DidChangeWatchedFilesParticipant participant : project.getDidChangeWatchedFilesParticipants()) { if (participant.isEnabled()) { - if (participant.didChangeWatchedFile(filePath, changeTypes)) { + if (participant.didChangeWatchedFile(filePath, changeTypes, context)) { projectsToValidate.add(project); } } } + context.reparseTemplates(); } } diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/project/QuteTextDocument.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/project/QuteTextDocument.java index 5851340f6..d97a20eff 100644 --- a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/project/QuteTextDocument.java +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/project/QuteTextDocument.java @@ -184,11 +184,13 @@ default String getUserTagsFolder() { String getOrigin(); String getRelativePath(); - + default String getProperty(String name) { return null; } + TemplateRootPath getTemplateRootPath(); + /** * Re-parse template from the current template content and update usages * {@link UsagesRegistry} (call of user tag parameters, include parameters). @@ -197,6 +199,8 @@ default void reparseTemplate() { // Do nothing } + Character getExpressionCommand(); + T getUserData(Key key); void putUserData(Key key, T data); diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/project/datamodel/ExtendedDataModelProject.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/project/datamodel/ExtendedDataModelProject.java index c64187ed7..9b0fc9ead 100644 --- a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/project/datamodel/ExtendedDataModelProject.java +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/project/datamodel/ExtendedDataModelProject.java @@ -28,6 +28,7 @@ import com.redhat.qute.commons.JavaParameterInfo; import com.redhat.qute.commons.JavaTypeInfo; import com.redhat.qute.commons.ProjectFeature; +import com.redhat.qute.commons.TemplateRootPath; import com.redhat.qute.commons.config.PropertyConfig; import com.redhat.qute.commons.datamodel.DataModelParameter; import com.redhat.qute.commons.datamodel.DataModelProject; @@ -46,7 +47,6 @@ import com.redhat.qute.project.datamodel.resolvers.TypeValueResolver; import com.redhat.qute.project.extensions.DataModelTemplateParticipant; import com.redhat.qute.project.extensions.ProjectExtension; -import com.redhat.qute.project.extensions.config.ApplicationPropertiesProjectExtension; import com.redhat.qute.utils.JSONUtility; import com.redhat.qute.utils.StringUtils; @@ -70,13 +70,9 @@ public class ExtendedDataModelProject extends DataModelProject javaTypesSupportedInNativeMode; - private final ApplicationPropertiesProjectExtension applicationProperties; - public ExtendedDataModelProject(DataModelProject> dataModelProject, QuteProject project) { this.project = project; - this.applicationProperties = (ApplicationPropertiesProjectExtension) project - .getExtension(ApplicationPropertiesProjectExtension.APPLICATION_PROPERTIES_PROJECT_EXTENSION_ID); super.setTemplates(createTemplates(dataModelProject.getTemplates())); super.setNamespaceResolverInfos(dataModelProject.getNamespaceResolverInfos()); @@ -302,10 +298,11 @@ public Path getConfigAsPath(PropertyConfig property) { } public String getConfig(PropertyConfig property) { - if (applicationProperties != null) { - return applicationProperties.getConfig(property); - } - return property.getDefaultValue(); + return project.getConfig(property); + } + + public boolean getConfigAsBoolean(PropertyConfig property) { + return project.getConfigAsBoolean(property); } public ExtendedDataModelTemplate findDataModelTemplate(String templateUri, boolean userTags) { @@ -373,4 +370,12 @@ public void unregisterNamespaceResolver(NamespaceResolverInfo namespaceResolverI } super.unregisterNamespaceResolver(namespaceResolverInfo); } + + public List getTemplateRootPaths() { + return project.getTemplateRootPaths(); + } + + public QuteTextDocument findDocumentFor(String templateId) { + return project.findDocumentByTemplateId(templateId); + } } \ No newline at end of file diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/project/documents/QuteBinaryTextDocument.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/project/documents/QuteBinaryTextDocument.java index a55f97655..24b09fae3 100644 --- a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/project/documents/QuteBinaryTextDocument.java +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/project/documents/QuteBinaryTextDocument.java @@ -14,6 +14,7 @@ import java.nio.file.Path; import java.util.Map; +import com.redhat.qute.commons.TemplateRootPath; import com.redhat.qute.commons.binary.BinaryTemplate; import com.redhat.qute.project.QuteProject; @@ -31,12 +32,15 @@ public class QuteBinaryTextDocument extends QuteReadOnlyTextDocument { private final Map properties; + private final Character expressionCommand; + public QuteBinaryTextDocument(BinaryTemplate binaryTemplate, String binaryName, Map properties, - QuteProject project) { + Character expressionCommand, QuteProject project) { super(binaryTemplate.getUri(), binaryTemplate.getPath(), binaryTemplate.getContent(), project); this.binaryTemplate = binaryTemplate; this.binaryName = binaryName; this.properties = properties; + this.expressionCommand = expressionCommand; } @Override @@ -71,4 +75,14 @@ public String getProperty(String name) { public void reparseTemplate() { super.template = loadTemplate(binaryTemplate.getUri(), binaryTemplate.getPath(), binaryTemplate.getContent()); } + + @Override + public Character getExpressionCommand() { + return expressionCommand; + } + + @Override + public TemplateRootPath getTemplateRootPath() { + return null; + } } \ No newline at end of file diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/project/documents/QuteClosedTextDocument.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/project/documents/QuteClosedTextDocument.java index b545a8667..e6aaa5e8c 100644 --- a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/project/documents/QuteClosedTextDocument.java +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/project/documents/QuteClosedTextDocument.java @@ -16,6 +16,7 @@ import java.util.logging.Logger; import com.redhat.qute.commons.FileUtils; +import com.redhat.qute.commons.TemplateRootPath; import com.redhat.qute.project.QuteProject; import com.redhat.qute.utils.IOUtils; @@ -33,9 +34,12 @@ public class QuteClosedTextDocument extends QuteReadOnlyTextDocument { private String relativePath; + private TemplateRootPath templateRootPath; + public QuteClosedTextDocument(Path templatePath, QuteProject project) { super(FileUtils.toUri(templatePath), project.getTemplateId(templatePath), getContent(templatePath), project); this.templatePath = templatePath; + this.templateRootPath = project.findTemplateRootPathFor(templatePath); } private static String getContent(Path templatePath) { @@ -66,4 +70,16 @@ public String getRelativePath() { return relativePath; } + @Override + public Character getExpressionCommand() { + if (templateRootPath != null && templateRootPath.getAltExprSyntax() != null) { + return templateRootPath.getAltExprSyntax() ? '=' : null; + } + return getProject().getExpressionCommand(); + } + + @Override + public TemplateRootPath getTemplateRootPath() { + return templateRootPath; + } } \ No newline at end of file diff --git a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/project/documents/QuteOpenedTextDocument.java b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/project/documents/QuteOpenedTextDocument.java index efe31e210..953f3464b 100644 --- a/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/project/documents/QuteOpenedTextDocument.java +++ b/qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/project/documents/QuteOpenedTextDocument.java @@ -28,6 +28,7 @@ import com.redhat.qute.commons.ProjectInfo; import com.redhat.qute.commons.QuteProjectParams; +import com.redhat.qute.commons.TemplateRootPath; import com.redhat.qute.ls.api.QuteProjectInfoProvider; import com.redhat.qute.ls.commons.ModelTextDocument; import com.redhat.qute.ls.commons.TextDocument; @@ -84,6 +85,8 @@ public class QuteOpenedTextDocument extends ModelTextDocument