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