You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2021/12/09 16:19:33 UTC

[sling-whiteboard] 13/18: Completions: support completion for itemList within data-sly-{list, repeat}

This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to branch feature/vscode-htl
in repository https://gitbox.apache.org/repos/asf/sling-whiteboard.git

commit 44d5b074973e71dc7fa70ca61e12e6dfd3f0779b
Author: Robert Munteanu <ro...@apache.org>
AuthorDate: Thu Dec 9 13:07:24 2021 +0100

    Completions: support completion for itemList within data-sly-{list,repeat}
---
 vscode-htl/data/completions-sling.json      | 39 ++++++++++++++++++
 vscode-htl/src/completionData.ts            | 12 ++++++
 vscode-htl/src/htlCompletionItemProvider.ts | 62 ++++++++++++++---------------
 vscode-htl/src/test/suite/extension.test.ts | 17 ++++++++
 4 files changed, 99 insertions(+), 31 deletions(-)

diff --git a/vscode-htl/data/completions-sling.json b/vscode-htl/data/completions-sling.json
index 2a030ae..67c2dfd 100644
--- a/vscode-htl/data/completions-sling.json
+++ b/vscode-htl/data/completions-sling.json
@@ -211,5 +211,44 @@
                 "description": "A value map for this resource. The value map allows to read the properties of the resource"
             }
         ]
+    },{
+        "javaType": "$io.sightly.ItemList",
+        "nestedCompletions": [
+            {
+                "name": "index",
+                "javaType": "java.lang.Integer",
+                "description": "zero-based counter `(0..length-1)`"
+            },
+            {
+                "name": "count",
+                "javaType": "java.lang.Integer",
+                "description": "one-based counter `(1..length)`"
+            },
+            {
+                "name": "first",
+                "javaType": "java.lang.Boolean",
+                "description": "`true` if the current item is the first item"
+            },
+            {
+                "name": "middle",
+                "javaType": "java.lang.Boolean",
+                "description": "`true` if the current item is neither the first nor the last item"
+            },
+            {
+                "name": "last",
+                "javaType": "java.lang.Boolean",
+                "description": "`true` if the current item is the last item"
+            },
+            {
+                "name": "odd",
+                "javaType": "java.lang.Boolean",
+                "description": "`true` if the index is odd"
+            },
+            {
+                "name": "even",
+                "javaType": "java.lang.Boolean",
+                "description": "`true` if the index is even"
+            }
+        ]
     }]
 }
\ No newline at end of file
diff --git a/vscode-htl/src/completionData.ts b/vscode-htl/src/completionData.ts
index 38b77cf..0b869a3 100644
--- a/vscode-htl/src/completionData.ts
+++ b/vscode-htl/src/completionData.ts
@@ -40,4 +40,16 @@ export class CompletionDataAccess {
         }
         return definition.nestedCompletions;
     }
+}
+
+export class LocalCompletionDefinition implements CompletionDefinition {
+    name: string;
+    javaType: string;
+    description: string;
+
+    constructor(name: string, javaType: string, description: string) {
+        this.javaType = javaType;
+        this.name = name;
+        this.description = description;
+    }
 }
\ No newline at end of file
diff --git a/vscode-htl/src/htlCompletionItemProvider.ts b/vscode-htl/src/htlCompletionItemProvider.ts
index 8324a43..d0601ad 100644
--- a/vscode-htl/src/htlCompletionItemProvider.ts
+++ b/vscode-htl/src/htlCompletionItemProvider.ts
@@ -4,7 +4,7 @@ import * as vscode from 'vscode';
 // HTML parser module used to provide context-sensitive completion
 import { parse } from 'node-html-parser';
 import { readFileSync } from 'fs';
-import {CompletionDataAccess, CompletionDefinition} from './completionData';
+import {CompletionDataAccess, CompletionDefinition, LocalCompletionDefinition} from './completionData';
 
 const slyUseRegexp = /data-sly-use\.([a-zA-Z0-9]+)=/g;
 const identifierAccess = /([a-zA-Z0-9]+)\./g;
@@ -30,9 +30,33 @@ export class HtlCompletionItemProvider implements vscode.CompletionItemProvider
         }
         
         let completionContext = linePrefix.substring(completionStart + 2).trim();
-        let completionProperties = this.completionData.getGlobalCompletions();
+
+        // 1. propose completions based on HTML document
+        let documentCompletions: CompletionDefinition[] = [];
+        let htmlDoc = parse(doc);
+        let elements = htmlDoc.getElementsByTagName("*");
+        // TODO - provide only relevant completions based on the position in the document
+        elements
+            .filter( e => e.rawAttrs.indexOf('data-sly-') >= 0 )
+            .forEach(e => {
+                // element.attributes parses data-sly-use.foo="bar" incorrectly into {data-sly-use="", foo="bar"}
+                let rawAttrs = e.rawAttrs;
+                for ( const match of rawAttrs.matchAll(slyUseRegexp) ) {
+                    documentCompletions.push(new LocalCompletionDefinition(match[1], "java.lang.Object", ""));
+                }
+                if ( rawAttrs.indexOf('data-sly-repeat=') >= 0 || rawAttrs.indexOf('data-sly-list') >= 0)  {
+                    // TODO - resolve item if possible
+                    documentCompletions.push(new LocalCompletionDefinition("item", "java.lang.Object", ""));
+                    documentCompletions.push(new LocalCompletionDefinition("itemList", "$io.sightly.ItemList", ""));
+                }
+                // TODO - support named data-sly-repeat completions, e.g. data-sly-repeat.meh=...
+            });
+
+        let completionProperties = this.completionData.getGlobalCompletions().concat(documentCompletions);
+
         let completionCandidate = "";
 
+        // 2. recursively resolve any nested properties
         for ( const match of completionContext.matchAll(identifierAccess)) {
             completionCandidate = match[1];
             let matchingDefinition = completionProperties.find( e => e.name === completionCandidate );
@@ -44,35 +68,8 @@ export class HtlCompletionItemProvider implements vscode.CompletionItemProvider
             }
         }
 
-        let completions: vscode.CompletionItem[] = [];
-
-        // top-level matches, propose completions based on HTML document
-        if ( !completionCandidate ) {
-            let htmlDoc = parse(doc);
-            let elements = htmlDoc.getElementsByTagName("*");
-            // TODO - provide only relevant completions based on the position in the document
-            elements
-                .filter( e => e.rawAttrs.indexOf('data-sly-') >= 0 )
-                .forEach(e => {
-                    // element.attributes parses data-sly-use.foo="bar" incorrectly into {data-sly-use="", foo="bar"}
-                    let rawAttrs = e.rawAttrs;
-                    for ( const match of rawAttrs.matchAll(slyUseRegexp) ) {
-                        completions.push(new vscode.CompletionItem(match[1]));
-                    }
-                    if ( rawAttrs.indexOf('data-sly-repeat=') >= 0 )  {
-                        completions.push(new vscode.CompletionItem("item"));
-                        completions.push(new vscode.CompletionItem("itemList")); // TODO - expand completions for itemList
-                    }
-                    // TODO - support named data-sly-repeat completions, e.g. data-sly-repeat.meh=...
-                });
-        }
-
         // provide completions based on properties ( top-level bindings or nested ones)
-        completionProperties.forEach ( element => {
-            completions.push( this.toCompletionItem(element) );
-        });
-
-        return completions;
+        return completionProperties.map ( element =>  this.toCompletionItem(element) );
     }
 
     private toCompletionItem(completionDefinition: CompletionDefinition) {
@@ -82,7 +79,10 @@ export class HtlCompletionItemProvider implements vscode.CompletionItemProvider
             description = completionDefinition.description + "\n\n";
         }
 
-        description += "Type: _" + completionDefinition.javaType+"_";
+        // filter out synthetic types
+        if ( completionDefinition.javaType.charAt(0) !== '$') {
+            description += "Type: _" + completionDefinition.javaType+"_";
+        }
         item.documentation = new vscode.MarkdownString(description);
         
         return item;
diff --git a/vscode-htl/src/test/suite/extension.test.ts b/vscode-htl/src/test/suite/extension.test.ts
index d3473aa..706e0f0 100644
--- a/vscode-htl/src/test/suite/extension.test.ts
+++ b/vscode-htl/src/test/suite/extension.test.ts
@@ -51,6 +51,23 @@ suite('Extension Test Suite',  () => {
 		assert.deepStrictEqual(completionVariables?.sort(), ["item", "itemList", "properties", "request", "resolver", "resource", "response"]);
 	});
 
+	test('completion test with data-sly-list', () => {
+		let document = `
+			<html>
+				<body data-sly-list="\${pageItems}">
+					<div>\${ itemList. }</div>
+				</body>
+			</html>
+		`;
+		let completions = completionProvider.provideCompletionItems0('<div>${', document);
+		let completionVariables = completions?.map ( c => c.label.toString());
+		assert.deepStrictEqual(completionVariables?.sort(), ["item", "itemList", "properties", "request", "resolver", "resource", "response"]);
+
+		let itemListCompletions = completionProvider.provideCompletionItems0('<div>${ itemList.', document);
+		let itemListVariables = itemListCompletions?.map ( c => c.label.toString());
+		assert.deepStrictEqual(itemListVariables?.sort(), ["index", "count", "first", "middle", "last", "odd", "even"].sort());
+	});
+
 	test('completion test for request', () => {
 		let document = `
 			<html>