Skip to content

Commit 7760f65

Browse files
committed
Special keys support for user tag
Signed-off-by: azerr <azerr@redhat.com>
1 parent c757aaf commit 7760f65

File tree

8 files changed

+197
-6
lines changed

8 files changed

+197
-6
lines changed

qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/parser/template/SectionMetadata.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
*******************************************************************************/
1212
package com.redhat.qute.parser.template;
1313

14+
import com.redhat.qute.commons.JavaElementInfo;
15+
1416
/**
1517
* Metadata for Qute section.
1618
*
@@ -82,4 +84,9 @@ public Node getJavaTypeOwnerNode() {
8284
return null;
8385
}
8486

87+
public String getSimpleType() {
88+
String type = getType();
89+
return JavaElementInfo.getSimpleType(type);
90+
}
91+
8592
}

qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/QuteHover.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
import com.redhat.qute.settings.SharedSettings;
4848
import com.redhat.qute.utils.DocumentationUtils;
4949
import com.redhat.qute.utils.QutePositionUtility;
50+
import com.redhat.qute.utils.UserTagUtils;
5051

5152
/**
5253
* Qute hover support.
@@ -191,6 +192,20 @@ private CompletableFuture<Hover> doHoverForExpressionPart(Part part, Template te
191192
}
192193

193194
private CompletableFuture<Hover> doHoverForObjectPart(Part part, String projectUri, HoverRequest hoverRequest) {
195+
if (UserTagUtils.isUserTag(hoverRequest.getTemplate())) {
196+
// It's an user tag
197+
SectionMetadata specialKey = UserTagUtils.getSpecialKey(part.getPartName());
198+
if (specialKey != null) {
199+
// its a special key for user tag ({it} or {nested-content), display the special
200+
// key documentation.
201+
boolean hasMarkdown = hoverRequest.canSupportMarkupKind(MarkupKind.MARKDOWN);
202+
MarkupContent content = DocumentationUtils.getDocumentation(specialKey, hasMarkdown);
203+
Range range = QutePositionUtility.createRange(part);
204+
Hover hover = new Hover(content, range);
205+
return CompletableFuture.completedFuture(hover);
206+
}
207+
}
208+
194209
// Check if part is a literal (ex: true, null, 123, 'abc', etc)
195210
Expression expression = part.getParent().getParent();
196211
String literalJavaType = expression.getLiteralJavaType();

qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/services/completions/QuteCompletionsForExpression.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import static com.redhat.qute.services.QuteCompletions.EMPTY_FUTURE_COMPLETION;
1616

1717
import java.util.ArrayList;
18+
import java.util.Collection;
1819
import java.util.HashSet;
1920
import java.util.List;
2021
import java.util.Set;
@@ -57,6 +58,7 @@
5758
import com.redhat.qute.settings.QuteFormattingSettings;
5859
import com.redhat.qute.utils.QutePositionUtility;
5960
import com.redhat.qute.utils.StringUtils;
61+
import com.redhat.qute.utils.UserTagUtils;
6062

6163
/**
6264
* Qute completion inside Qute expression.
@@ -425,6 +427,26 @@ private CompletableFuture<CompletionList> doCompleteExpressionForObjectPart(Comp
425427
completionForTagSection.doCompleteTagSection(completionRequest, previous == '#' ? "#" : "{",
426428
completionSettings, formattingSettings, cancelChecker, list);
427429
}
430+
431+
if (UserTagUtils.isUserTag(template)) {
432+
// provide completion for 'it' and 'nested-content'
433+
Collection<SectionMetadata> metadatas = UserTagUtils.getSpecialKeys();
434+
for (SectionMetadata metadata : metadatas) {
435+
String name = metadata.getName();
436+
if (!existingVars.contains(name)) {
437+
existingVars.add(name);
438+
CompletionItem item = new CompletionItem();
439+
item.setLabel(name);
440+
item.setKind(CompletionItemKind.Keyword);
441+
// Display metadata section (ex : count for #each) after declared variables
442+
item.setSortText("Za" + name);
443+
TextEdit textEdit = new TextEdit(range, name);
444+
item.setTextEdit(Either.forLeft(textEdit));
445+
item.setDetail(metadata.getDescription());
446+
list.getItems().add(item);
447+
}
448+
}
449+
}
428450
return CompletableFuture.completedFuture(list);
429451
}
430452

qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/utils/DocumentationUtils.java

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import com.redhat.qute.commons.ResolvedJavaTypeInfo;
2727
import com.redhat.qute.commons.ValueResolver;
2828
import com.redhat.qute.ls.commons.snippets.Snippet;
29+
import com.redhat.qute.parser.template.SectionMetadata;
2930
import com.redhat.qute.project.tags.UserTag;
3031

3132
/**
@@ -211,7 +212,7 @@ public static MarkupContent getDocumentation(UserTag userTag, boolean markdown)
211212

212213
public static MarkupContent getDocumentation(Snippet snippet, boolean markdown) {
213214
StringBuilder documentation = new StringBuilder();
214-
215+
215216
if (markdown) {
216217
documentation.append("**");
217218
}
@@ -220,13 +221,36 @@ public static MarkupContent getDocumentation(Snippet snippet, boolean markdown)
220221
documentation.append("**");
221222
}
222223
documentation.append(" section tag ");
223-
224+
224225
if (snippet.getDescription() != null) {
225226
documentation.append(System.lineSeparator());
226227
documentation.append(System.lineSeparator());
227228
documentation.append(snippet.getDescription());
228229
}
229-
230+
231+
return createMarkupContent(documentation, markdown);
232+
}
233+
234+
public static MarkupContent getDocumentation(SectionMetadata metadata, boolean markdown) {
235+
StringBuilder documentation = new StringBuilder();
236+
237+
// Title
238+
if (markdown) {
239+
documentation.append("```java");
240+
documentation.append(System.lineSeparator());
241+
}
242+
documentation.append(metadata.getSimpleType());
243+
if (markdown) {
244+
documentation.append(System.lineSeparator());
245+
documentation.append("```");
246+
}
247+
248+
String description = metadata.getDescription();
249+
if (description != null) {
250+
documentation.append(System.lineSeparator());
251+
documentation.append(description);
252+
}
253+
230254
return createMarkupContent(documentation, markdown);
231255
}
232256
}

qute.ls/com.redhat.qute.ls/src/main/java/com/redhat/qute/utils/UserTagUtils.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,11 @@
1111
*******************************************************************************/
1212
package com.redhat.qute.utils;
1313

14+
import java.util.Collection;
15+
import java.util.HashMap;
1416
import java.util.HashSet;
1517
import java.util.List;
18+
import java.util.Map;
1619
import java.util.Set;
1720
import java.util.function.Consumer;
1821

@@ -24,16 +27,33 @@
2427
import com.redhat.qute.parser.template.NodeKind;
2528
import com.redhat.qute.parser.template.Parameter;
2629
import com.redhat.qute.parser.template.Section;
30+
import com.redhat.qute.parser.template.SectionMetadata;
2731
import com.redhat.qute.parser.template.Template;
2832

2933
/**
3034
* User tag utilities.
3135
*
3236
* @author Angelo ZERR
37+
*
38+
* @see https://quarkus.io/guides/qute-reference#user_tags
3339
*
3440
*/
3541
public class UserTagUtils {
3642

43+
private static final Map<String, SectionMetadata> SPECIAL_KEYS;
44+
45+
static {
46+
SPECIAL_KEYS = new HashMap<String, SectionMetadata>();
47+
register(new SectionMetadata("it", Object.class.getName(),
48+
"`it` is a special key that is replaced with the first unnamed parameter of the tag."));
49+
register(new SectionMetadata("nested-content", Object.class.getName(),
50+
"`nested-content` is a special key that will be replaced by the content of the tag"));
51+
}
52+
53+
private static void register(SectionMetadata metadata) {
54+
SPECIAL_KEYS.put(metadata.getName(), metadata);
55+
}
56+
3757
public static final String TAGS_DIR = "tags";
3858

3959
public static boolean isUserTag(Template template) {
@@ -113,4 +133,24 @@ private static void collectUserTagParameters(Expression expression, Set<String>
113133
}
114134
}
115135
}
136+
137+
/**
138+
* Returns the special keys ('it' and 'nested-content') allowed in an user tag.
139+
*
140+
* @return the special keys ('it' and 'nested-content') allowed in an user tag.
141+
*/
142+
public static Collection<SectionMetadata> getSpecialKeys() {
143+
return SPECIAL_KEYS.values();
144+
}
145+
146+
/**
147+
* Return the special key from the given object part name and null otherwise.
148+
*
149+
* @param objectName the object part name.
150+
*
151+
* @return the special key from the given object part name and null otherwise.
152+
*/
153+
public static SectionMetadata getSpecialKey(String objectName) {
154+
return SPECIAL_KEYS.get(objectName);
155+
}
116156
}

qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/QuteAssert.java

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,13 +127,27 @@ public static void testCompletionFor(String value, boolean snippetSupport, Integ
127127
expectedItems);
128128
}
129129

130+
public static void testCompletionFor(String value, String fileUri, String templateId, Integer expectedCount,
131+
CompletionItem... expectedItems) throws Exception {
132+
testCompletionFor(value, false, fileUri, templateId, PROJECT_URI, TEMPLATE_BASE_DIR, expectedCount,
133+
expectedItems);
134+
}
135+
130136
public static void testCompletionFor(String value, boolean snippetSupport, String fileUri, String projectUri,
131137
String templateBaseDir, Integer expectedCount, CompletionItem... expectedItems) throws Exception {
138+
testCompletionFor(value, snippetSupport, fileUri, null, projectUri, templateBaseDir, expectedCount,
139+
expectedItems);
140+
}
141+
142+
public static void testCompletionFor(String value, boolean snippetSupport, String fileUri, String templateId,
143+
String projectUri, String templateBaseDir, Integer expectedCount, CompletionItem... expectedItems)
144+
throws Exception {
132145
int offset = value.indexOf('|');
133146
value = value.substring(0, offset) + value.substring(offset + 1);
134147

135148
QuteProjectRegistry projectRegistry = new MockQuteProjectRegistry();
136149
Template template = createTemplate(value, fileUri, projectUri, templateBaseDir, projectRegistry);
150+
template.setTemplateId(templateId);
137151

138152
Position position = template.positionAt(offset);
139153

@@ -357,20 +371,27 @@ public static void assertHover(String value, String expectedHoverLabel, Range ex
357371

358372
public static void assertHover(String value, String fileURI, String expectedHoverLabel, Range expectedHoverRange)
359373
throws Exception {
374+
assertHover(value, fileURI, null, expectedHoverLabel, expectedHoverRange);
375+
}
376+
377+
public static void assertHover(String value, String fileURI, String templateId, String expectedHoverLabel,
378+
Range expectedHoverRange) throws Exception {
360379
SharedSettings sharedSettings = new SharedSettings();
361380
HoverCapabilities capabilities = new HoverCapabilities(Arrays.asList(MarkupKind.MARKDOWN), false);
362381
sharedSettings.getHoverSettings().setCapabilities(capabilities);
363-
assertHover(value, fileURI, PROJECT_URI, TEMPLATE_BASE_DIR, expectedHoverLabel, expectedHoverRange,
382+
assertHover(value, fileURI, templateId, PROJECT_URI, TEMPLATE_BASE_DIR, expectedHoverLabel, expectedHoverRange,
364383
sharedSettings);
365384
}
366385

367-
public static void assertHover(String value, String fileUri, String projectUri, String templateBaseDir,
368-
String expectedHoverLabel, Range expectedHoverRange, SharedSettings sharedSettings) throws Exception {
386+
private static void assertHover(String value, String fileUri, String templateId, String projectUri,
387+
String templateBaseDir, String expectedHoverLabel, Range expectedHoverRange, SharedSettings sharedSettings)
388+
throws Exception {
369389
int offset = value.indexOf("|");
370390
value = value.substring(0, offset) + value.substring(offset + 1);
371391

372392
QuteProjectRegistry projectRegistry = new MockQuteProjectRegistry();
373393
Template template = createTemplate(value, fileUri, projectUri, templateBaseDir, projectRegistry);
394+
template.setTemplateId(templateId);
374395

375396
Position position = template.positionAt(offset);
376397

qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/completions/QuteCompletionInUserTagTest.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,4 +81,25 @@ public void parameterExpressionInUserTag() throws Exception {
8181
c("review2 : Review", "review2", r(1, 17, 1, 17)), //
8282
c("getReview2() : Review", "getReview2", r(1, 17, 1, 17)));
8383
}
84+
85+
@Test
86+
public void specialKeys() throws Exception {
87+
String template = "{@org.acme.Item item}\r\n" + //
88+
"{|}";
89+
90+
// In qute template
91+
testCompletionFor(template, //
92+
SECTION_SNIPPET_SIZE + 1, //
93+
c("item", "item", r(1, 1, 1, 1)));
94+
95+
// In user tag
96+
testCompletionFor(template, //
97+
"src/main/resources/templates/tags/form.html", //
98+
"tags/form", //
99+
SECTION_SNIPPET_SIZE + 1 /* item */ + 2 /* it, nested-content */, //
100+
c("item", "item", r(1, 1, 1, 1)), //
101+
c("it", "it", r(1, 1, 1, 1)), //
102+
c("nested-content", "nested-content", r(1, 1, 1, 1)));
103+
104+
}
84105
}

qute.ls/com.redhat.qute.ls/src/test/java/com/redhat/qute/services/hover/QuteHoverInUserTag.java

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,45 @@ public void parameterPropertyPart() throws Exception {
4848
r(1, 17, 1, 21));
4949
}
5050

51+
@Test
52+
public void it() throws Exception {
53+
String template = "{i|t}";
54+
55+
// In a qute template
56+
assertHover(template);
57+
58+
// In a user tag
59+
assertHover(template, //
60+
"src/main/resources/templates/tags/form.html", //
61+
"tags/form", //
62+
"```java" + //
63+
System.lineSeparator() + //
64+
"Object" + //
65+
System.lineSeparator() + //
66+
"```" + //
67+
System.lineSeparator() + //
68+
"`it` is a special key that is replaced with the first unnamed parameter of the tag.", //
69+
r(0, 1, 0, 3));
70+
}
71+
72+
@Test
73+
public void nestedContent() throws Exception {
74+
String template = "{nested-con|tent}";
75+
76+
// In a qute template
77+
assertHover(template);
78+
79+
// In a user tag
80+
assertHover(template, //
81+
"src/main/resources/templates/tags/form.html", //
82+
"tags/form", //
83+
"```java" + //
84+
System.lineSeparator() + //
85+
"Object" + //
86+
System.lineSeparator() + //
87+
"```" + //
88+
System.lineSeparator() + //
89+
"`nested-content` is a special key that will be replaced by the content of the tag", //
90+
r(0, 1, 0, 15));
91+
}
5192
}

0 commit comments

Comments
 (0)