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

[sling-whiteboard] 02/18: Started work on dynamic completions

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 fc1bd87e3b39ff7d9159bdfd1891c54d6044c1ec
Author: Robert Munteanu <ro...@apache.org>
AuthorDate: Tue Dec 7 18:15:00 2021 +0100

    Started work on dynamic completions
---
 vscode-htl/README.md         |   8 +-
 vscode-htl/package-lock.json | 194 ++++++++++++++++++++++++++++++++++++++++++-
 vscode-htl/package.json      | 109 ++++++++++++------------
 vscode-htl/src/extension.ts  |  54 ++++++++++++
 4 files changed, 308 insertions(+), 57 deletions(-)

diff --git a/vscode-htl/README.md b/vscode-htl/README.md
index 2489348..4247555 100644
--- a/vscode-htl/README.md
+++ b/vscode-htl/README.md
@@ -1 +1,7 @@
-# HTL extension for bla bla
+# Visual Studio code extension for HTL
+
+TODO:
+- provide completions based on default script bindings
+- provide completions based on known types of script bindings (SlingHttpServletRequest, Resource)
+- provide completions for objects inferred from data-sly-use.$IDENTIFIER
+- provide completions for objects provided by data-sly-repeat and friends (first/last/etc)
\ No newline at end of file
diff --git a/vscode-htl/package-lock.json b/vscode-htl/package-lock.json
index f50cd1d..1d3c707 100644
--- a/vscode-htl/package-lock.json
+++ b/vscode-htl/package-lock.json
@@ -7,6 +7,9 @@
     "": {
       "name": "vscode-hello",
       "version": "0.0.1",
+      "dependencies": {
+        "node-html-parser": "^5.1.0"
+      },
       "devDependencies": {
         "@types/glob": "^7.1.4",
         "@types/mocha": "^9.0.0",
@@ -488,6 +491,11 @@
       "integrity": "sha1-9y12C+Cbf3bQjtj66Ysomo0F+rM=",
       "dev": true
     },
+    "node_modules/boolbase": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
+      "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24="
+    },
     "node_modules/brace-expansion": {
       "version": "1.1.11",
       "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -671,6 +679,32 @@
         "node": ">= 8"
       }
     },
+    "node_modules/css-select": {
+      "version": "4.1.3",
+      "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.1.3.tgz",
+      "integrity": "sha512-gT3wBNd9Nj49rAbmtFHj1cljIAOLYSX1nZ8CB7TBO3INYckygm5B7LISU/szY//YmdiSLbJvDLOx9VnMVpMBxA==",
+      "dependencies": {
+        "boolbase": "^1.0.0",
+        "css-what": "^5.0.0",
+        "domhandler": "^4.2.0",
+        "domutils": "^2.6.0",
+        "nth-check": "^2.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/fb55"
+      }
+    },
+    "node_modules/css-what": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz",
+      "integrity": "sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw==",
+      "engines": {
+        "node": ">= 6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/fb55"
+      }
+    },
     "node_modules/debug": {
       "version": "4.3.3",
       "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
@@ -739,6 +773,57 @@
         "node": ">=6.0.0"
       }
     },
+    "node_modules/dom-serializer": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz",
+      "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==",
+      "dependencies": {
+        "domelementtype": "^2.0.1",
+        "domhandler": "^4.2.0",
+        "entities": "^2.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
+      }
+    },
+    "node_modules/domelementtype": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz",
+      "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/fb55"
+        }
+      ]
+    },
+    "node_modules/domhandler": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.0.tgz",
+      "integrity": "sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==",
+      "dependencies": {
+        "domelementtype": "^2.2.0"
+      },
+      "engines": {
+        "node": ">= 4"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/domhandler?sponsor=1"
+      }
+    },
+    "node_modules/domutils": {
+      "version": "2.8.0",
+      "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
+      "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
+      "dependencies": {
+        "dom-serializer": "^1.0.1",
+        "domelementtype": "^2.2.0",
+        "domhandler": "^4.2.0"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/domutils?sponsor=1"
+      }
+    },
     "node_modules/duplexer2": {
       "version": "0.1.4",
       "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz",
@@ -766,6 +851,14 @@
         "node": ">=8.6"
       }
     },
+    "node_modules/entities": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
+      "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
+      "funding": {
+        "url": "https://github.com/fb55/entities?sponsor=1"
+      }
+    },
     "node_modules/escalade": {
       "version": "3.1.1",
       "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
@@ -1276,7 +1369,6 @@
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
       "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
-      "dev": true,
       "bin": {
         "he": "bin/he"
       }
@@ -1717,6 +1809,15 @@
       "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
       "dev": true
     },
+    "node_modules/node-html-parser": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/node-html-parser/-/node-html-parser-5.1.0.tgz",
+      "integrity": "sha512-l6C1Gf1o7YuxeMGa17PypEez/rj+ii3q4/NZG37nRmWSLDjHyB0WNrlE4h2UW92D0JSfUSfu+lOvxThttVe7Jw==",
+      "dependencies": {
+        "css-select": "^4.1.3",
+        "he": "1.2.0"
+      }
+    },
     "node_modules/normalize-path": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
@@ -1726,6 +1827,17 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/nth-check": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz",
+      "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==",
+      "dependencies": {
+        "boolbase": "^1.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/nth-check?sponsor=1"
+      }
+    },
     "node_modules/once": {
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -2734,6 +2846,11 @@
       "integrity": "sha1-9y12C+Cbf3bQjtj66Ysomo0F+rM=",
       "dev": true
     },
+    "boolbase": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
+      "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24="
+    },
     "brace-expansion": {
       "version": "1.1.11",
       "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -2878,6 +2995,23 @@
         "which": "^2.0.1"
       }
     },
+    "css-select": {
+      "version": "4.1.3",
+      "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.1.3.tgz",
+      "integrity": "sha512-gT3wBNd9Nj49rAbmtFHj1cljIAOLYSX1nZ8CB7TBO3INYckygm5B7LISU/szY//YmdiSLbJvDLOx9VnMVpMBxA==",
+      "requires": {
+        "boolbase": "^1.0.0",
+        "css-what": "^5.0.0",
+        "domhandler": "^4.2.0",
+        "domutils": "^2.6.0",
+        "nth-check": "^2.0.0"
+      }
+    },
+    "css-what": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz",
+      "integrity": "sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw=="
+    },
     "debug": {
       "version": "4.3.3",
       "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
@@ -2923,6 +3057,39 @@
         "esutils": "^2.0.2"
       }
     },
+    "dom-serializer": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz",
+      "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==",
+      "requires": {
+        "domelementtype": "^2.0.1",
+        "domhandler": "^4.2.0",
+        "entities": "^2.0.0"
+      }
+    },
+    "domelementtype": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz",
+      "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A=="
+    },
+    "domhandler": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.0.tgz",
+      "integrity": "sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==",
+      "requires": {
+        "domelementtype": "^2.2.0"
+      }
+    },
+    "domutils": {
+      "version": "2.8.0",
+      "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
+      "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
+      "requires": {
+        "dom-serializer": "^1.0.1",
+        "domelementtype": "^2.2.0",
+        "domhandler": "^4.2.0"
+      }
+    },
     "duplexer2": {
       "version": "0.1.4",
       "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz",
@@ -2947,6 +3114,11 @@
         "ansi-colors": "^4.1.1"
       }
     },
+    "entities": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
+      "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A=="
+    },
     "escalade": {
       "version": "3.1.1",
       "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
@@ -3335,8 +3507,7 @@
     "he": {
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
-      "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
-      "dev": true
+      "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="
     },
     "http-proxy-agent": {
       "version": "4.0.1",
@@ -3666,12 +3837,29 @@
       "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
       "dev": true
     },
+    "node-html-parser": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/node-html-parser/-/node-html-parser-5.1.0.tgz",
+      "integrity": "sha512-l6C1Gf1o7YuxeMGa17PypEez/rj+ii3q4/NZG37nRmWSLDjHyB0WNrlE4h2UW92D0JSfUSfu+lOvxThttVe7Jw==",
+      "requires": {
+        "css-select": "^4.1.3",
+        "he": "1.2.0"
+      }
+    },
     "normalize-path": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
       "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
       "dev": true
     },
+    "nth-check": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz",
+      "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==",
+      "requires": {
+        "boolbase": "^1.0.0"
+      }
+    },
     "once": {
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
diff --git a/vscode-htl/package.json b/vscode-htl/package.json
index 1e12d44..2dfbbb9 100644
--- a/vscode-htl/package.json
+++ b/vscode-htl/package.json
@@ -1,56 +1,59 @@
 {
   "name": "vscode-hello",
-	"displayName": "vscode-hello",
-	"description": "",
-	"version": "0.0.1",
-	"engines": {
-		"vscode": "^1.62.0"
-	},
-	"categories": [
-		"Other"
-	],
-	"activationEvents": [
-        "onCommand:vscode-hello.helloWorld",
-		"onCommand:vscode-hello.now",
-		"onLanguage:html"
-	],
-	"main": "./out/extension.js",
-	"contributes": {
-		"commands": [
-			{
-				"command": "vscode-hello.helloWorld",
-				"title": "Hello, world"
-			},
-			{
-				"command": "vscode-hello.now",
-				"title": "Current time"
-			}
-		],
-		"html": {
-			"customData": [
-				"./data/htl.json"
-			]
-		}	
-	},
-	"scripts": {
-		"vscode:prepublish": "npm run compile",
-		"compile": "tsc -p ./",
-		"watch": "tsc -watch -p ./",
-		"pretest": "npm run compile && npm run lint",
-		"lint": "eslint src --ext ts",
-		"test": "node ./out/test/runTest.js"
-	},
-	"devDependencies": {
-		"@types/vscode": "^1.62.0",
-		"@types/glob": "^7.1.4",
-		"@types/mocha": "^9.0.0",
-		"@types/node": "14.x",
-		"@typescript-eslint/eslint-plugin": "^5.1.0",
-		"@typescript-eslint/parser": "^5.1.0",
-		"eslint": "^8.1.0",
-		"glob": "^7.1.7",
-		"mocha": "^9.1.3",
-		"typescript": "^4.4.4",
-		"@vscode/test-electron": "^1.6.2"
-	}
+  "displayName": "vscode-hello",
+  "description": "",
+  "version": "0.0.1",
+  "engines": {
+    "vscode": "^1.62.0"
+  },
+  "categories": [
+    "Other"
+  ],
+  "activationEvents": [
+    "onCommand:vscode-hello.helloWorld",
+    "onCommand:vscode-hello.now",
+    "onLanguage:html"
+  ],
+  "main": "./out/extension.js",
+  "contributes": {
+    "commands": [
+      {
+        "command": "vscode-hello.helloWorld",
+        "title": "Hello, world"
+      },
+      {
+        "command": "vscode-hello.now",
+        "title": "Current time"
+      }
+    ],
+    "html": {
+      "customData": [
+        "./data/htl.json"
+      ]
+    }
+  },
+  "scripts": {
+    "vscode:prepublish": "npm run compile",
+    "compile": "tsc -p ./",
+    "watch": "tsc -watch -p ./",
+    "pretest": "npm run compile && npm run lint",
+    "lint": "eslint src --ext ts",
+    "test": "node ./out/test/runTest.js"
+  },
+  "devDependencies": {
+    "@types/glob": "^7.1.4",
+    "@types/mocha": "^9.0.0",
+    "@types/node": "14.x",
+    "@types/vscode": "^1.62.0",
+    "@typescript-eslint/eslint-plugin": "^5.1.0",
+    "@typescript-eslint/parser": "^5.1.0",
+    "@vscode/test-electron": "^1.6.2",
+    "eslint": "^8.1.0",
+    "glob": "^7.1.7",
+    "mocha": "^9.1.3",
+    "typescript": "^4.4.4"
+  },
+  "dependencies": {
+    "node-html-parser": "^5.1.0"
+  }
 }
diff --git a/vscode-htl/src/extension.ts b/vscode-htl/src/extension.ts
index 4c13d62..bfa686a 100644
--- a/vscode-htl/src/extension.ts
+++ b/vscode-htl/src/extension.ts
@@ -2,6 +2,11 @@
 // Import the module and reference it with the alias vscode in your code below
 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;
+
 // this method is called when your extension is activated
 // your extension is activated the very first time the command is executed
 export function activate(context: vscode.ExtensionContext) {
@@ -22,6 +27,55 @@ 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 line = document.lineAt(position);
+			let lineUntilPosition = document.getText(new vscode.Range(position.with(undefined, 0), position));
+			let lineAfterPosition = document.getText(new vscode.Range(position, position.with(undefined, line.text.length)));
+			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 generalCompletions = [];
+
+				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);
+	
+				let req = new vscode.CompletionItem('request');
+				req.documentation = new vscode.MarkdownString('The current request. Backed by _org.apache.sling.api.SlingHttpServletRequest_');
+				generalCompletions.push(req);
+
+				// TODO - provide completion for data-sly-use.* objects
+				// if unable to inteligently define context, just parse the whole document and accumulate
+
+				let htmlDoc = parse(document.getText());
+				let elements = htmlDoc.getElementsByTagName("*");
+				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 attrs = e.rawAttrs;
+						for ( const match of attrs.matchAll(slyUseRegexp) ) {
+							generalCompletions.push(new vscode.CompletionItem(match[1]));
+						}
+					});
+
+				return generalCompletions;
+			}
+		}
+	});
 }
 
 // this method is called when your extension is deactivated