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:25 UTC

[sling-whiteboard] 05/18: Move global completions to a JSON file, add some more of them.

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 ea71cc4d5713b8ba097d7d70e4135428200e833d
Author: Robert Munteanu <ro...@apache.org>
AuthorDate: Wed Dec 8 17:48:34 2021 +0100

    Move global completions to a JSON file, add some more of them.
---
 vscode-htl/data/completions-sling.json      | 132 ++++++++++++++++++++++++++++
 vscode-htl/src/extension.ts                 |  62 ++-----------
 vscode-htl/src/htlCompletionItemProvider.ts |  66 +++++++++++++-
 vscode-htl/src/test/suite/extension.test.ts |  46 +++++++++-
 4 files changed, 242 insertions(+), 64 deletions(-)

diff --git a/vscode-htl/data/completions-sling.json b/vscode-htl/data/completions-sling.json
new file mode 100644
index 0000000..f35545c
--- /dev/null
+++ b/vscode-htl/data/completions-sling.json
@@ -0,0 +1,132 @@
+{
+    "globalCompletions": [
+        {
+            "name": "properties",
+            "javaType": "org.apache.sling.api.resource.ValueMap",
+            "description": "List of properties of the current Resource. Backed by _org.apache.sling.api.resource.ValueMap_"
+        },        
+        {
+            "name": "request",
+            "javaType": "org.apache.sling.api.SlingHttpServletRequest",
+            "description": "The current request. Backed by _org.apache.sling.api.SlingHttpServletRequest_"
+        },
+        {
+            "name": "resolver",
+            "javaType": "org.apache.sling.api.resource.ResourceResolver"
+        },
+        {
+            "name": "resource",
+            "javaType": "org.apache.sling.api.resource.Resource"
+        },
+        {
+            "name": "response",
+            "javaType": "org.apache.sling.api.SlingHttpServletResponse"
+        }
+    ],
+    "completionProperties": [{
+        "javaType": "org.apache.sling.api.SlingHttpServletRequest",
+        "nestedCompletions": [
+            { 
+                "name": "resource",
+                "javaType": "org.apache.sling.api.Resource"
+            },
+            {
+                "name": "resourceResolver",
+                "javaType": "org.apache.sling.api.ResourceResolver"
+            },
+            {
+                "name": "requestPathInfo",
+                "javaType": "org.apache.sling.api.request.RequestPathInfo"
+            },
+            {
+                "name": "requestParameterMap",
+                "javaType": "org.apache.sling.api.request.RequestParameterMap"
+            },
+            {
+                "name": "requestParameterList",
+                "javaType": "java.util.List<String>"
+            },
+            {
+                "name": "responseContentType",
+                "javaType": "java.lang.String"
+            },
+            {
+                "name": "requestProgressTracker",
+                "javaType": "java.util.Enumeration<String>"
+            },
+            {
+                "name": "authType",
+                "javaType": "java.lang.String"
+            },
+            {
+                "name": "cookies",
+                "javaType": "javax.http.servlet.Cookie[]"
+            },
+            {
+                "name": "headerNames",
+                "javaType": "java.util.Enumeration<String>"
+            },
+            {
+                "name": "method",
+                "javaType": "java.lang.String"
+            },
+            {
+                "name": "pathInfo",
+                "javaType": "java.lang.String"
+            },
+            {
+                "name": "pathTranslated",
+                "javaType": "java.lang.String"
+            },
+            {
+                "name": "contextPath",
+                "javaType": "java.lang.String"
+            },
+            {
+                "name": "queryString",
+                "javaType": "java.lang.String"
+            },
+            {
+                "name": "remoteUser",
+                "javaType": "java.lang.String"
+            },
+            {
+                "name": "userPrincipal",
+                "javaType": "java.security.Principal"
+            },
+            {
+                "name": "requestedSessionId",
+                "javaType": "java.lang.String"
+            },
+            {
+                "name": "requestURI",
+                "javaType": "java.lang.String"
+            },
+            {
+                "name": "requestURL",
+                "javaType": "java.lang.StringBuffer"
+            },
+            {
+                "name": "servletPath",
+                "javaType": "java.lang.String"
+            },
+            {
+                "name": "session",
+                "javaType": "javax.servlet.http.HttpSession"
+            },
+            {
+                "name": "requestedSessionIdValid",
+                "javaType": "boolean"
+            },
+            {
+                "name": "requestedSessionIdFromCookie",
+                "javaType": "boolean"
+            },
+            {
+                "name": "requestedSessionIdFromURL",
+                "javaType": "boolean"
+            }
+
+        ]
+    }]
+}
\ No newline at end of file
diff --git a/vscode-htl/src/extension.ts b/vscode-htl/src/extension.ts
index 8361de9..2e7bae5 100644
--- a/vscode-htl/src/extension.ts
+++ b/vscode-htl/src/extension.ts
@@ -1,11 +1,7 @@
-// The module 'vscode' contains the VS Code extensibility API
-// Import the module and reference it with the alias vscode in your code below
+// the module 'vscode' contains the VS Code extensibility API
 import * as vscode from 'vscode';
-
-// HTML parser module used to provide context-sensitive completion
-import { parse } from 'node-html-parser';
-
-const slyUseRegexp = /data-sly-use\.([a-zA-Z0-9]+)=/g;
+// import our own completion provider
+import { HtlCompletionItemProvider } from './htlCompletionItemProvider';
 
 // this method is called when your extension is activated
 // your extension is activated the very first time the command is executed
@@ -28,57 +24,9 @@ export function activate(context: vscode.ExtensionContext) {
 
 	context.subscriptions.push(helloWorld, now);
 
-	vscode.languages.registerCompletionItemProvider('html', {
-		provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext) {
-			
-			let lineUntilPosition = document.getText(new vscode.Range(position.with(undefined, 0), position));
-			if ( lineUntilPosition.indexOf('${') === -1 ) {
-				return null;
-			}
-			// request-specific branch
-			if ( lineUntilPosition.endsWith('request.') ) {
-				return [
-					new vscode.CompletionItem('resource'),
-					new vscode.CompletionItem('resourceResolver'),
-					new vscode.CompletionItem('requestPathInfo'),
-					new vscode.CompletionItem('contextPath')
-				];
-			} else {
+	let completionsPath = vscode.Uri.joinPath(context.extensionUri, "data");
 
-				let generalCompletions = [];
-
-				// TODO - provide completions for all global bindings
-				let props = new vscode.CompletionItem('properties');
-				props.documentation = new vscode.MarkdownString('List of properties of the current Resource. Backed by _org.apache.sling.api.resource.ValueMap_');
-				generalCompletions.push(props);
-	
-				// TODO - deep auto-completion for resource and request
-				let req = new vscode.CompletionItem('request');
-				req.documentation = new vscode.MarkdownString('The current request. Backed by _org.apache.sling.api.SlingHttpServletRequest_');
-				generalCompletions.push(req);
-
-				let htmlDoc = parse(document.getText());
-				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) ) {
-							generalCompletions.push(new vscode.CompletionItem(match[1]));
-						}
-						if ( rawAttrs.indexOf('data-sly-repeat=') >= 0 )  {
-							generalCompletions.push(new vscode.CompletionItem("item"));
-							generalCompletions.push(new vscode.CompletionItem("itemList")); // TODO - expand completions for itemList
-						}
-						// TODO - support named data-sly-repeat completions, e.g. data-sly-repeat.meh=...
-					});
-
-				return generalCompletions;
-			}
-		}
-	});
+	vscode.languages.registerCompletionItemProvider('html', new HtlCompletionItemProvider(completionsPath));
 }
 
 // this method is called when your extension is deactivated
diff --git a/vscode-htl/src/htlCompletionItemProvider.ts b/vscode-htl/src/htlCompletionItemProvider.ts
index 121914f..7f9d494 100644
--- a/vscode-htl/src/htlCompletionItemProvider.ts
+++ b/vscode-htl/src/htlCompletionItemProvider.ts
@@ -1,10 +1,70 @@
 'use strict';
 
 import * as vscode from 'vscode';
+// HTML parser module used to provide context-sensitive completion
+import { parse } from 'node-html-parser';
+import { readFileSync } from 'fs';
+
+const slyUseRegexp = /data-sly-use\.([a-zA-Z0-9]+)=/g;
 
 export class HtlCompletionItemProvider implements vscode.CompletionItemProvider {
-    
-    public provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext): vscode.ProviderResult<vscode.CompletionItem[] | vscode.CompletionList<vscode.CompletionItem>> {
-        throw new Error('Method not implemented.');
+
+    completions: any;
+
+    constructor(completionsPath: vscode.Uri) {
+        const slingCompletions = vscode.Uri.joinPath(completionsPath, "completions-sling.json");
+        console.log("Reading completions from {}", slingCompletions.fsPath);
+        this.completions = JSON.parse(readFileSync(slingCompletions.fsPath, 'utf-8'));
+        
+    }
+           
+    provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext) {
+        return this.provideCompletionItems0(document.getText(new vscode.Range(position.with(undefined, 0), position)), document.getText());
+    }
+
+    provideCompletionItems0(linePrefix: string, doc: string) {
+        if ( linePrefix.indexOf('${') === -1 ) {
+            return null;
+        }
+        // request-specific branch
+        if ( linePrefix.endsWith('request.') ) {
+            return [
+                new vscode.CompletionItem('resource'),
+                new vscode.CompletionItem('resourceResolver'),
+                new vscode.CompletionItem('requestPathInfo'),
+                new vscode.CompletionItem('contextPath')
+            ];
+        } else {
+
+            let generalCompletions: vscode.CompletionItem[] = [];
+
+            this.completions.globalCompletions.forEach( (globalCompletion: any) => {
+                let vsCodeCompletion = new vscode.CompletionItem(globalCompletion.name);
+                if ( globalCompletion.description ) {
+                    vsCodeCompletion.documentation = new vscode.MarkdownString(globalCompletion.description);
+                }
+                generalCompletions.push(vsCodeCompletion);
+            });
+
+            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) ) {
+                        generalCompletions.push(new vscode.CompletionItem(match[1]));
+                    }
+                    if ( rawAttrs.indexOf('data-sly-repeat=') >= 0 )  {
+                        generalCompletions.push(new vscode.CompletionItem("item"));
+                        generalCompletions.push(new vscode.CompletionItem("itemList")); // TODO - expand completions for itemList
+                    }
+                    // TODO - support named data-sly-repeat completions, e.g. data-sly-repeat.meh=...
+                });
+
+            return generalCompletions;
+        }
     }
 }
\ No newline at end of file
diff --git a/vscode-htl/src/test/suite/extension.test.ts b/vscode-htl/src/test/suite/extension.test.ts
index 4ca0ab4..b42e6bc 100644
--- a/vscode-htl/src/test/suite/extension.test.ts
+++ b/vscode-htl/src/test/suite/extension.test.ts
@@ -1,15 +1,53 @@
 import * as assert from 'assert';
+import { HtlCompletionItemProvider } from '../../htlCompletionItemProvider';
 
 // You can import and use all API from the 'vscode' module
 // as well as import your extension to test it
 import * as vscode from 'vscode';
 // import * as myExtension from '../../extension';
 
-suite('Extension Test Suite', () => {
+suite('Extension Test Suite',  () => {
 	vscode.window.showInformationMessage('Start all tests.');
+	const workingDir = vscode.Uri.parse("file://" + __dirname, true);
+	const completionsPath = vscode.Uri.joinPath(workingDir, "..", "..", "..", "data");
+	const completionProvider = new HtlCompletionItemProvider(completionsPath);
+	
+	test('completion test with no additional structures', () => {
+		let document = `
+			<html>
+				<body>
+					\${ }
+				</body>
+			</html>
+		`;
+		let completions = completionProvider.provideCompletionItems0('${', document);
+		let completionVariables = completions?.map ( c => c.label.toString());
+		assert.deepStrictEqual(completionVariables?.sort(), ["properties", "request", "resolver", "resource", "response"]);
+	});
+
+	test('completion test with data-sly-use', () => {
+		let document = `
+			<html>
+				<body data-sly-use.foo="foo.js">
+					\${ }
+				</body>
+			</html>
+		`;
+		let completions = completionProvider.provideCompletionItems0('${', document);
+		let completionVariables = completions?.map ( c => c.label.toString());
+		assert.deepStrictEqual(completionVariables?.sort(), ["foo", "properties", "request", "resolver", "resource", "response"]);
+	});
 
-	test('Sample test', () => {
-		assert.strictEqual(-1, [1, 2, 3].indexOf(5));
-		assert.strictEqual(-1, [1, 2, 3].indexOf(0));
+	test('completion test with data-sly-repeat', () => {
+		let document = `
+			<html>
+				<body data-sly-repeat="\${pageItems}">
+					\${ }
+				</body>
+			</html>
+		`;
+		let completions = completionProvider.provideCompletionItems0('${', document);
+		let completionVariables = completions?.map ( c => c.label.toString());
+		assert.deepStrictEqual(completionVariables?.sort(), ["item", "itemList", "properties", "request", "resolver", "resource", "response"]);
 	});
 });