diff --git a/src/main/java/com/redhat/devtools/intellij/qute/lang/format/QuteHtmlFormattingModelBuilder.java b/src/main/java/com/redhat/devtools/intellij/qute/lang/format/QuteHtmlFormattingModelBuilder.java
index 8c34fe087..ca852ceeb 100644
--- a/src/main/java/com/redhat/devtools/intellij/qute/lang/format/QuteHtmlFormattingModelBuilder.java
+++ b/src/main/java/com/redhat/devtools/intellij/qute/lang/format/QuteHtmlFormattingModelBuilder.java
@@ -26,142 +26,140 @@
/**
* Template aware formatter which provides formatting for Qute syntax and delegates formatting
* for the templated language to that languages formatter
- *
+ *
* This class is a copy/paste from https://github.com/JetBrains/intellij-plugins/blob/master/Qute/src/com/dmarcotte/Qute/format/HbFormattingModelBuilder.java adapted for Qute.
*/
public class QuteHtmlFormattingModelBuilder extends TemplateLanguageFormattingModelBuilder {
- @Override
- public TemplateLanguageBlock createTemplateLanguageBlock(@NotNull ASTNode node,
- @Nullable Wrap wrap,
- @Nullable Alignment alignment,
- @Nullable List foreignChildren,
- @NotNull CodeStyleSettings codeStyleSettings) {
- final FormattingDocumentModelImpl documentModel = FormattingDocumentModelImpl.createOn(node.getPsi().getContainingFile());
- HtmlPolicy policy = new HtmlPolicy(codeStyleSettings, documentModel);
- return new QuteBlock(node, wrap, alignment, this, codeStyleSettings, foreignChildren, policy);
- }
-
- /**
- * We have to override {@link TemplateLanguageFormattingModelBuilder#createModel}
- * since after we delegate to some templated languages, those languages (xml/html for sure, potentially others)
- * delegate right back to us to format the HbTokenTypes.OUTER_ELEMENT_TYPE token we tell them to ignore,
- * causing a stack-overflowing loop of polite format-delegation.
- */
- @Override
- public @NotNull FormattingModel createModel(@NotNull FormattingContext formattingContext) {
- final PsiFile file = formattingContext.getContainingFile();
- Block rootBlock;
-
- ASTNode node = formattingContext.getNode();
-
- if (node.getElementType() == QuteElementTypes.QUTE_OUTER_ELEMENT_TYPE) {
- // If we're looking at a HbTokenTypes.OUTER_ELEMENT_TYPE element, then we've been invoked by our templated
- // language. Make a dummy block to allow that formatter to continue
- return new SimpleTemplateLanguageFormattingModelBuilder().createModel(formattingContext);
- }
- else {
- rootBlock = getRootBlock(file, file.getViewProvider(), formattingContext.getCodeStyleSettings());
- }
- return new DocumentBasedFormattingModel(
- rootBlock, formattingContext.getProject(), formattingContext.getCodeStyleSettings(), file.getFileType(), file);
- }
-
- /**
- * Do format my model!
- *
- * @return false all the time to tell the {@link TemplateLanguageFormattingModelBuilder}
- * to not-not format our model (i.e. yes please! Format away!)
- */
- @Override
- public boolean dontFormatMyModel() {
- return false;
- }
-
- private static class QuteBlock extends TemplateLanguageBlock {
-
- @NotNull
- protected final HtmlPolicy myHtmlPolicy;
-
-
- QuteBlock(@NotNull ASTNode node,
- Wrap wrap,
- Alignment alignment,
- @NotNull TemplateLanguageBlockFactory blockFactory,
- @NotNull CodeStyleSettings settings,
- @Nullable List foreignChildren,
- @NotNull HtmlPolicy htmlPolicy) {
- super(node, wrap, alignment, blockFactory, settings, foreignChildren);
- myHtmlPolicy = htmlPolicy;
- }
-
- @Override
- public Indent getIndent() {
- // ignore whitespace
- if (myNode.getText().trim().isEmpty()) {
- return Indent.getNoneIndent();
- }
-
- // any element that is the direct descendant of a foreign block gets an indent
- // (unless that foreign element has been configured to not indent its children)
- DataLanguageBlockWrapper foreignParent = getForeignBlockParent(true);
- if (foreignParent != null) {
- if (foreignParent.getNode() instanceof XmlTag
- && !myHtmlPolicy.indentChildrenOf((XmlTag)foreignParent.getNode())) {
- return Indent.getNoneIndent();
- }
- return Indent.getNormalIndent();
- }
-
- return Indent.getNoneIndent();
- }
-
@Override
- protected IElementType getTemplateTextElementType() {
- // we ignore CONTENT tokens since they get formatted by the templated language
- return QuteElementTypes.QUTE_TEXT;
+ public TemplateLanguageBlock createTemplateLanguageBlock(@NotNull ASTNode node,
+ @Nullable Wrap wrap,
+ @Nullable Alignment alignment,
+ @Nullable List foreignChildren,
+ @NotNull CodeStyleSettings codeStyleSettings) {
+ final FormattingDocumentModelImpl documentModel = FormattingDocumentModelImpl.createOn(node.getPsi().getContainingFile());
+ HtmlPolicy policy = new HtmlPolicy(codeStyleSettings, documentModel);
+ return new QuteBlock(node, wrap, alignment, this, codeStyleSettings, foreignChildren, policy);
}
+ /**
+ * We have to override {@link TemplateLanguageFormattingModelBuilder#createModel}
+ * since after we delegate to some templated languages, those languages (xml/html for sure, potentially others)
+ * delegate right back to us to format the HbTokenTypes.OUTER_ELEMENT_TYPE token we tell them to ignore,
+ * causing a stack-overflowing loop of polite format-delegation.
+ */
@Override
- public boolean isRequiredRange(TextRange range) {
- // seems our approach doesn't require us to insert any custom DataLanguageBlockFragmentWrapper blocks
- return false;
+ public @NotNull FormattingModel createModel(@NotNull FormattingContext formattingContext) {
+ final PsiFile file = formattingContext.getContainingFile();
+ Block rootBlock;
+
+ ASTNode node = formattingContext.getNode();
+
+ if (node.getElementType() == QuteElementTypes.QUTE_OUTER_ELEMENT_TYPE) {
+ // If we're looking at a QuteElementTypes.QUTE_OUTER_ELEMENT_TYPE element, then we've been invoked by our templated
+ // language. Make a dummy block to allow that formatter to continue
+ return new SimpleTemplateLanguageFormattingModelBuilder().createModel(formattingContext);
+ } else {
+ rootBlock = getRootBlock(file, file.getViewProvider(), formattingContext.getCodeStyleSettings());
+ }
+ return new DocumentBasedFormattingModel(
+ rootBlock, formattingContext.getProject(), formattingContext.getCodeStyleSettings(), file.getFileType(), file);
}
/**
- *
- * This method handles indent and alignment on Enter.
+ * Do format my model!
+ *
+ * @return false all the time to tell the {@link TemplateLanguageFormattingModelBuilder}
+ * to not-not format our model (i.e. yes please! Format away!)
*/
- @NotNull
@Override
- public ChildAttributes getChildAttributes(int newChildIndex) {
- return new ChildAttributes(Indent.getNoneIndent(), null);
+ public boolean dontFormatMyModel() {
+ return false;
}
+ private static class QuteBlock extends TemplateLanguageBlock {
- /**
- * Returns this block's first "real" foreign block parent if it exists, and null otherwise. (By "real" here, we mean that this method
- * skips SyntheticBlock blocks inserted by the template formatter)
- *
- * @param immediate Pass true to only check for an immediate foreign parent, false to look up the hierarchy.
- */
- private DataLanguageBlockWrapper getForeignBlockParent(boolean immediate) {
- DataLanguageBlockWrapper foreignBlockParent = null;
- BlockWithParent parent = getParent();
-
- while (parent != null) {
- if (parent instanceof DataLanguageBlockWrapper && !(((DataLanguageBlockWrapper)parent).getOriginal() instanceof SyntheticBlock)) {
- foreignBlockParent = (DataLanguageBlockWrapper)parent;
- break;
+ @NotNull
+ protected final HtmlPolicy myHtmlPolicy;
+
+
+ QuteBlock(@NotNull ASTNode node,
+ Wrap wrap,
+ Alignment alignment,
+ @NotNull TemplateLanguageBlockFactory blockFactory,
+ @NotNull CodeStyleSettings settings,
+ @Nullable List foreignChildren,
+ @NotNull HtmlPolicy htmlPolicy) {
+ super(node, wrap, alignment, blockFactory, settings, foreignChildren);
+ myHtmlPolicy = htmlPolicy;
}
- else if (immediate && parent instanceof QuteBlock) {
- break;
+
+ @Override
+ public Indent getIndent() {
+ // ignore whitespace
+ if (myNode.getText().trim().isEmpty()) {
+ return Indent.getNoneIndent();
+ }
+
+ // any element that is the direct descendant of a foreign block gets an indent
+ // (unless that foreign element has been configured to not indent its children)
+ DataLanguageBlockWrapper foreignParent = getForeignBlockParent(true);
+ if (foreignParent != null) {
+ if (foreignParent.getNode() instanceof XmlTag
+ && !myHtmlPolicy.indentChildrenOf((XmlTag) foreignParent.getNode())) {
+ return Indent.getNoneIndent();
+ }
+ return Indent.getNormalIndent();
+ }
+
+ return Indent.getNoneIndent();
+ }
+
+ @Override
+ protected IElementType getTemplateTextElementType() {
+ // we ignore CONTENT tokens since they get formatted by the templated language
+ return QuteElementTypes.QUTE_TEXT;
}
- parent = parent.getParent();
- }
- return foreignBlockParent;
+ @Override
+ public boolean isRequiredRange(TextRange range) {
+ // seems our approach doesn't require us to insert any custom DataLanguageBlockFragmentWrapper blocks
+ return false;
+ }
+
+ /**
+ *
+ * This method handles indent and alignment on Enter.
+ */
+ @NotNull
+ @Override
+ public ChildAttributes getChildAttributes(int newChildIndex) {
+ return new ChildAttributes(Indent.getNoneIndent(), null);
+ }
+
+
+ /**
+ * Returns this block's first "real" foreign block parent if it exists, and null otherwise. (By "real" here, we mean that this method
+ * skips SyntheticBlock blocks inserted by the template formatter)
+ *
+ * @param immediate Pass true to only check for an immediate foreign parent, false to look up the hierarchy.
+ */
+ private DataLanguageBlockWrapper getForeignBlockParent(boolean immediate) {
+ DataLanguageBlockWrapper foreignBlockParent = null;
+ BlockWithParent parent = getParent();
+
+ while (parent != null) {
+ if (parent instanceof DataLanguageBlockWrapper && !(((DataLanguageBlockWrapper) parent).getOriginal() instanceof SyntheticBlock)) {
+ foreignBlockParent = (DataLanguageBlockWrapper) parent;
+ break;
+ } else if (immediate && parent instanceof QuteBlock) {
+ break;
+ }
+ parent = parent.getParent();
+ }
+
+ return foreignBlockParent;
+ }
}
- }
}
\ No newline at end of file
diff --git a/src/main/java/com/redhat/devtools/intellij/qute/lang/psi/QuteLexerForStartTag.java b/src/main/java/com/redhat/devtools/intellij/qute/lang/psi/QuteLexerForStartTag.java
index ccd4fe597..19187d3d3 100644
--- a/src/main/java/com/redhat/devtools/intellij/qute/lang/psi/QuteLexerForStartTag.java
+++ b/src/main/java/com/redhat/devtools/intellij/qute/lang/psi/QuteLexerForStartTag.java
@@ -36,8 +36,6 @@ public class QuteLexerForStartTag extends AbstractQuteSubLexer {
private int myLastState;
private IElementType myLastTokenType;
private int myLastTokenEnd;
- private ExpressionScanner expressionScanner;
-
private AbstractQuteSubLexer currentSubLexer;
public QuteLexerForStartTag(String text, TemplateScanner templateScanner, int startTagOpenOffset) {
diff --git a/src/main/java/com/redhat/devtools/intellij/qute/lang/psi/QuteParsing.java b/src/main/java/com/redhat/devtools/intellij/qute/lang/psi/QuteParsing.java
index a8fbaa6a2..5cba409e2 100644
--- a/src/main/java/com/redhat/devtools/intellij/qute/lang/psi/QuteParsing.java
+++ b/src/main/java/com/redhat/devtools/intellij/qute/lang/psi/QuteParsing.java
@@ -39,24 +39,28 @@ public void parseTemplate() {
}
while (!eof()) {
- final IElementType tt = token();
- if (tt == QUTE_START_EXPRESSION) {
- parseExpression();
- } else if (tt == QUTE_START_TAG_OPEN) {
- parseStartSection();
- } else if (isCommentToken(tt)) {
- parseComment();
- } else if (tt == QUTE_TEXT) {
- parseText();
- } else {
- advance();
- }
+ parseContent();
}
template.done(QUTE_CONTENT);
}
- private void parseStartSection() {
+ private void parseContent() {
+ final IElementType tt = token();
+ if (tt == QUTE_START_EXPRESSION) {
+ parseExpression();
+ } else if (tt == QUTE_START_TAG_OPEN) {
+ parseSection();
+ } else if (isCommentToken(tt)) {
+ parseComment();
+ } else if (tt == QUTE_TEXT) {
+ parseText();
+ } else {
+ advance();
+ }
+ }
+
+ private void parseSection() {
final PsiBuilder.Marker startSection = mark();
advance();
@@ -91,17 +95,18 @@ private void parseStartSection() {
advance();
propertyPart.done(QUTE_EXPRESSION_PROPERTY_PART);
continue;
- } else {
- //final PsiBuilder.Marker error = mark();
+ } else if (tt == QUTE_END_TAG_CLOSE || tt == QUTE_END_TAG_SELF_CLOSE) {
advance();
- // error.error("BAD comments!");
- continue;
+ break;
+ } else {
+ parseContent();
}
break;
}
startSection.done(QUTE_START_SECTION);
}
+
private void parseText() {
final PsiBuilder.Marker text = mark();
advance();
diff --git a/src/main/java/com/redhat/devtools/intellij/qute/psi/internal/template/QuarkusIntegrationForQute.java b/src/main/java/com/redhat/devtools/intellij/qute/psi/internal/template/QuarkusIntegrationForQute.java
index 8fb5ebd24..378bd0a82 100644
--- a/src/main/java/com/redhat/devtools/intellij/qute/psi/internal/template/QuarkusIntegrationForQute.java
+++ b/src/main/java/com/redhat/devtools/intellij/qute/psi/internal/template/QuarkusIntegrationForQute.java
@@ -126,9 +126,6 @@ private static String convertStreamToString(InputStream is) {
}
}
- private static final String JDT_SCHEME = "jdt";
- private static final String CONTENTS_AUTHORITY = "jarentry";
-
// see
// https://github.com/microsoft/vscode-java-dependency/blob/27c306b770c23b1eba1f9a7c3e70d2793baced68/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ExtUtils.java#L39
diff --git a/src/test/java/com/redhat/devtools/intellij/qute/lang/psi/QuteParsingTestCase.java b/src/test/java/com/redhat/devtools/intellij/qute/lang/psi/QuteParsingTestCase.java
new file mode 100644
index 000000000..2462eeaf5
--- /dev/null
+++ b/src/test/java/com/redhat/devtools/intellij/qute/lang/psi/QuteParsingTestCase.java
@@ -0,0 +1,50 @@
+package com.redhat.devtools.intellij.qute.lang.psi;
+
+import com.intellij.lang.ParserDefinition;
+import com.intellij.mock.MockSmartPointerManager;
+import com.intellij.openapi.application.ex.PathManagerEx;
+import com.intellij.psi.SmartPointerManager;
+import com.intellij.testFramework.ParsingTestCase;
+import com.redhat.devtools.intellij.qute.lang.QuteParserDefinition;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.File;
+import java.io.IOException;
+
+public class QuteParsingTestCase extends ParsingTestCase {
+
+ public QuteParsingTestCase() {
+ super("psi", "qute", new QuteParserDefinition());
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ project.registerService(SmartPointerManager.class, new MockSmartPointerManager());
+ }
+
+ @Override
+ protected String getTestDataPath() {
+ return new File("testData/qute").getPath();
+ }
+
+ public void testHelloWorld() throws IOException { doCodeTest("Hello {http:param('name', 'Quarkus')}!
"); }
+
+ public void testSimpleText() throws IOException { doCodeTest("foo"); }
+
+ public void testSectionWithHtml() throws IOException {
+ doCodeTest("""
+
+
+ {#if}
+ {#if}
+ {inject:flash.get("key")}
+
+ {/if}
+ {/if}
+
+
+ """);
+ }
+}
diff --git a/testData/qute/psi/HelloWorld.txt b/testData/qute/psi/HelloWorld.txt
new file mode 100644
index 000000000..93ce8fe72
--- /dev/null
+++ b/testData/qute/psi/HelloWorld.txt
@@ -0,0 +1,22 @@
+QUTE_FILE
+ ASTWrapperPsiElement(QuteElementType.QUTE_CONTENT)
+ ASTWrapperPsiElement(QUTE_TEXT)
+ PsiElement(QUTE_TEXT)('Hello ')
+ ASTWrapperPsiElement(QuteElementType.QUTE_EXPRESSION)
+ PsiElement(QuteElementType.QUTE_START_EXPRESSION)('{')
+ ASTWrapperPsiElement(QuteElementType.QUTE_EXPRESSION_NAMESPACE_PART)
+ PsiElement(QuteElementType.QUTE_EXPRESSION_NAMESPACE_PART)('http')
+ PsiElement(QuteElementType.QUTE_EXPRESSION_COLON_SPACE)(':')
+ PsiElement(QuteElementType.QUTE_EXPRESSION_METHOD_PART)('param')
+ PsiElement(QuteElementType.QUTE_EXPRESSION_OPEN_BRACKET)('(')
+ PsiElement(QuteElementType.QUTE_EXPRESSION_START_STRING)(''')
+ PsiElement(QuteElementType.QUTE_EXPRESSION_STRING)('name')
+ PsiElement(QuteElementType.QUTE_EXPRESSION_END_STRING)(''')
+ PsiElement(QuteElementType.QUTE_EXPRESSION_WHITESPACE)(', ')
+ PsiElement(QuteElementType.QUTE_EXPRESSION_START_STRING)(''')
+ PsiElement(QuteElementType.QUTE_EXPRESSION_STRING)('Quarkus')
+ PsiElement(QuteElementType.QUTE_EXPRESSION_END_STRING)(''')
+ PsiElement(QuteElementType.QUTE_EXPRESSION_CLOSE_BRACKET)(')')
+ PsiElement(QuteElementType.QUTE_END_EXPRESSION)('}')
+ ASTWrapperPsiElement(QUTE_TEXT)
+ PsiElement(QUTE_TEXT)('!
')
\ No newline at end of file
diff --git a/testData/qute/psi/SectionWithHtml.txt b/testData/qute/psi/SectionWithHtml.txt
new file mode 100644
index 000000000..8cc34e93d
--- /dev/null
+++ b/testData/qute/psi/SectionWithHtml.txt
@@ -0,0 +1,43 @@
+QUTE_FILE
+ ASTWrapperPsiElement(QuteElementType.QUTE_CONTENT)
+ ASTWrapperPsiElement(QUTE_TEXT)
+ PsiElement(QUTE_TEXT)('\n
\n ')
+ ASTWrapperPsiElement(QuteElementType.QUTE_START_SECTION)
+ PsiElement(QuteElementType.QUTE_START_TAG_OPEN)('{#')
+ PsiElement(QuteElementType.QUTE_START_TAG)('if')
+ PsiElement(QuteElementType.QUTE_START_TAG_CLOSE)('}')
+ ASTWrapperPsiElement(QUTE_TEXT)
+ PsiElement(QUTE_TEXT)('\n ')
+ ASTWrapperPsiElement(QuteElementType.QUTE_START_SECTION)
+ PsiElement(QuteElementType.QUTE_START_TAG_OPEN)('{#')
+ PsiElement(QuteElementType.QUTE_START_TAG)('if')
+ PsiElement(QuteElementType.QUTE_START_TAG_CLOSE)('}')
+ ASTWrapperPsiElement(QUTE_TEXT)
+ PsiElement(QUTE_TEXT)('\n ')
+ ASTWrapperPsiElement(QuteElementType.QUTE_EXPRESSION)
+ PsiElement(QuteElementType.QUTE_START_EXPRESSION)('{')
+ ASTWrapperPsiElement(QuteElementType.QUTE_EXPRESSION_NAMESPACE_PART)
+ PsiElement(QuteElementType.QUTE_EXPRESSION_NAMESPACE_PART)('inject')
+ PsiElement(QuteElementType.QUTE_EXPRESSION_COLON_SPACE)(':')
+ ASTWrapperPsiElement(QuteElementType.QUTE_EXPRESSION_OBJECT_PART)
+ PsiElement(QuteElementType.QUTE_EXPRESSION_OBJECT_PART)('flash')
+ PsiElement(QuteElementType.QUTE_EXPRESSION_DOT)('.')
+ PsiElement(QuteElementType.QUTE_EXPRESSION_METHOD_PART)('get')
+ PsiElement(QuteElementType.QUTE_EXPRESSION_OPEN_BRACKET)('(')
+ PsiElement(QuteElementType.QUTE_EXPRESSION_START_STRING)('"')
+ PsiElement(QuteElementType.QUTE_EXPRESSION_STRING)('key')
+ PsiElement(QuteElementType.QUTE_EXPRESSION_END_STRING)('"')
+ PsiElement(QuteElementType.QUTE_EXPRESSION_CLOSE_BRACKET)(')')
+ PsiElement(QuteElementType.QUTE_END_EXPRESSION)('}')
+ ASTWrapperPsiElement(QUTE_TEXT)
+ PsiElement(QUTE_TEXT)('\n\n ')
+ PsiElement(QuteElementType.QUTE_END_TAG_OPEN)('{/')
+ PsiElement(QuteElementType.QUTE_END_TAG)('if')
+ PsiElement(QuteElementType.QUTE_END_TAG_CLOSE)('}')
+ ASTWrapperPsiElement(QUTE_TEXT)
+ PsiElement(QUTE_TEXT)('\n ')
+ PsiElement(QuteElementType.QUTE_END_TAG_OPEN)('{/')
+ PsiElement(QuteElementType.QUTE_END_TAG)('if')
+ PsiElement(QuteElementType.QUTE_END_TAG_CLOSE)('}')
+ ASTWrapperPsiElement(QUTE_TEXT)
+ PsiElement(QUTE_TEXT)('\n
\n
\n')
\ No newline at end of file
diff --git a/testData/qute/psi/SimpleText.txt b/testData/qute/psi/SimpleText.txt
new file mode 100644
index 000000000..181478586
--- /dev/null
+++ b/testData/qute/psi/SimpleText.txt
@@ -0,0 +1,4 @@
+QUTE_FILE
+ ASTWrapperPsiElement(QuteElementType.QUTE_CONTENT)
+ ASTWrapperPsiElement(QUTE_TEXT)
+ PsiElement(QUTE_TEXT)('foo')
\ No newline at end of file