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 <[email protected]> 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
