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:26 UTC
[sling-whiteboard] 06/18: Completions: supported nested 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 97bda15f5f7f0acda389c15a01c482b4d91cf704
Author: Robert Munteanu <ro...@apache.org>
AuthorDate: Thu Dec 9 11:26:53 2021 +0100
Completions: supported nested completions
---
vscode-htl/data/completions-sling.json | 50 ++++++++++++++++++--
vscode-htl/src/completionData.ts | 43 +++++++++++++++++
vscode-htl/src/htlCompletionItemProvider.ts | 71 +++++++++++++++++++----------
vscode-htl/src/test/suite/extension.test.ts | 42 +++++++++++++++++
4 files changed, 178 insertions(+), 28 deletions(-)
diff --git a/vscode-htl/data/completions-sling.json b/vscode-htl/data/completions-sling.json
index f35545c..4565e09 100644
--- a/vscode-htl/data/completions-sling.json
+++ b/vscode-htl/data/completions-sling.json
@@ -28,11 +28,11 @@
"nestedCompletions": [
{
"name": "resource",
- "javaType": "org.apache.sling.api.Resource"
+ "javaType": "org.apache.sling.api.resource.Resource"
},
{
"name": "resourceResolver",
- "javaType": "org.apache.sling.api.ResourceResolver"
+ "javaType": "org.apache.sling.api.resource.ResourceResolver"
},
{
"name": "requestPathInfo",
@@ -44,7 +44,7 @@
},
{
"name": "requestParameterList",
- "javaType": "java.util.List<String>"
+ "javaType": "java.util.List<java.lang.String>"
},
{
"name": "responseContentType",
@@ -52,7 +52,7 @@
},
{
"name": "requestProgressTracker",
- "javaType": "java.util.Enumeration<String>"
+ "javaType": "java.util.Enumeration<java.lang.String>"
},
{
"name": "authType",
@@ -64,7 +64,7 @@
},
{
"name": "headerNames",
- "javaType": "java.util.Enumeration<String>"
+ "javaType": "java.util.Enumeration<java.lang.String>"
},
{
"name": "method",
@@ -128,5 +128,45 @@
}
]
+ },{
+ "javaType": "org.apache.sling.api.resource.Resource",
+ "nestedCompletions": [
+ {
+ "name": "path",
+ "javaType": "java.lang.String"
+ },
+ {
+ "name": "name",
+ "javaType": "java.lang.String"
+ },
+ {
+ "name": "parent",
+ "javaType": "org.apache.sling.api.resource.Resource"
+ },
+ {
+ "name": "children",
+ "javaType": "java.lang.Iterable<org.apache.sling.api.resource.Resource>"
+ },
+ {
+ "name": "resourceType",
+ "javaType": "java.lang.String"
+ },
+ {
+ "name": "resourceSuperType",
+ "javaType": "java.lang.String"
+ },
+ {
+ "name": "resourceMetaData",
+ "javaType": "org.apache.sling.api.resource.ResourceMetadata"
+ },
+ {
+ "name": "resourceResolver",
+ "javaType": "org.apache.sling.api.resource.ResourceResolver"
+ },
+ {
+ "name": "valueMap",
+ "javaType": "org.apache.sling.api.resource.ValueMap"
+ }
+ ]
}]
}
\ No newline at end of file
diff --git a/vscode-htl/src/completionData.ts b/vscode-htl/src/completionData.ts
new file mode 100644
index 0000000..38b77cf
--- /dev/null
+++ b/vscode-htl/src/completionData.ts
@@ -0,0 +1,43 @@
+export interface CompletionData {
+ globalCompletions: CompletionDefinition[];
+ completionProperties: CompletionProperties[];
+}
+
+export interface CompletionDefinition {
+ name: string;
+ javaType: string;
+ description: string | undefined;
+}
+
+export interface CompletionProperties {
+ javaType: string;
+ nestedCompletions: CompletionDefinition[];
+}
+
+export class CompletionDataAccess {
+ completions: CompletionData;
+
+ constructor(completions: CompletionData) {
+ this.completions = completions;
+ }
+
+ getGlobalCompletions() {
+ return this.completions.globalCompletions;
+ }
+
+ findGlobalCompletionDefinition(name: string) {
+ return this.completions.globalCompletions.find( element => {
+ return element.name === name;
+ });
+ }
+
+ findPropertyCompletions(javaType: String) {
+ let definition = this.completions.completionProperties.find( element => {
+ return element.javaType === javaType;
+ });
+ if ( !definition ) {
+ return [];
+ }
+ return definition.nestedCompletions;
+ }
+}
\ No newline at end of file
diff --git a/vscode-htl/src/htlCompletionItemProvider.ts b/vscode-htl/src/htlCompletionItemProvider.ts
index 7f9d494..206cbcc 100644
--- a/vscode-htl/src/htlCompletionItemProvider.ts
+++ b/vscode-htl/src/htlCompletionItemProvider.ts
@@ -4,17 +4,20 @@ 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 { cpuUsage } from 'process';
const slyUseRegexp = /data-sly-use\.([a-zA-Z0-9]+)=/g;
+const identifierAccess = /([a-zA-Z0-9]+)\./g;
export class HtlCompletionItemProvider implements vscode.CompletionItemProvider {
- completions: any;
+ completionData: CompletionDataAccess;
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'));
+ this.completionData = new CompletionDataAccess(JSON.parse(readFileSync(slingCompletions.fsPath, 'utf-8')));
}
@@ -23,27 +26,40 @@ export class HtlCompletionItemProvider implements vscode.CompletionItemProvider
}
provideCompletionItems0(linePrefix: string, doc: string) {
- if ( linePrefix.indexOf('${') === -1 ) {
+ let completionStart = linePrefix.indexOf('${');
+ if ( completionStart === -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 completionContext = linePrefix.substring(completionStart + 2).trim();
+ let completions: vscode.CompletionItem[] = [];
+ let completionCandidate = "";
+ let previousJavaType = "";
+
+ for ( const match of completionContext.matchAll(identifierAccess)) {
+ let completionProperties: CompletionDefinition[];
+ if ( previousJavaType ) {
+ completionProperties = this.completionData.findPropertyCompletions(previousJavaType);
+ } else {
+ completionProperties = this.completionData.getGlobalCompletions();
+ }
+ completionCandidate = match[1];
+ let matchingDefinition = completionProperties.find( e => e.name === completionCandidate );
+ if ( matchingDefinition ) {
+ previousJavaType = matchingDefinition.javaType;
+ }
+ }
- let generalCompletions: vscode.CompletionItem[] = [];
+ if ( completionCandidate ) {
+ let completionProposals = this.completionData.findPropertyCompletions(previousJavaType);
- 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);
+ completionProposals.forEach ( element => {
+ completions.push( this.toCompletionItem(element) );
+ });
+ } else {
+
+ this.completionData.getGlobalCompletions().map( element => {
+ completions.push(this.toCompletionItem(element));
});
let htmlDoc = parse(doc);
@@ -55,16 +71,25 @@ export class HtlCompletionItemProvider implements vscode.CompletionItemProvider
// 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]));
+ completions.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
+ 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=...
});
+ }
- return generalCompletions;
+ return completions;
+ }
+
+ private toCompletionItem(completionDefintion: CompletionDefinition) {
+ let item = new vscode.CompletionItem(completionDefintion.name);
+ if ( completionDefintion.description ) {
+ item.documentation = new vscode.MarkdownString(completionDefintion.description);
}
+ return item;
+
}
}
\ 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 b42e6bc..e3d1849 100644
--- a/vscode-htl/src/test/suite/extension.test.ts
+++ b/vscode-htl/src/test/suite/extension.test.ts
@@ -50,4 +50,46 @@ suite('Extension Test Suite', () => {
let completionVariables = completions?.map ( c => c.label.toString());
assert.deepStrictEqual(completionVariables?.sort(), ["item", "itemList", "properties", "request", "resolver", "resource", "response"]);
});
+
+ test('completion test for request', () => {
+ let document = `
+ <html>
+ <body>
+ \${ request. }
+ </body>
+ </html>
+ `;
+ let completions = completionProvider.provideCompletionItems0('${request.', document);
+ // test a subset, otherwise it's too cumbersome
+ let completionVariables = completions?.map ( c => c.label.toString()).slice(0,5);
+ assert.deepStrictEqual(completionVariables?.sort(), ["requestParameterList", "requestParameterMap", "requestPathInfo", "resource", "resourceResolver"]);
+ });
+
+ test('completion test for resource', () => {
+ let document = `
+ <html>
+ <body>
+ \${ resource. }
+ </body>
+ </html>
+ `;
+ let completions = completionProvider.provideCompletionItems0('${resource.', document);
+ // test a subset, otherwise it's too cumbersome
+ let completionVariables = completions?.map ( c => c.label.toString()).slice(0,5);
+ assert.deepStrictEqual(completionVariables?.sort(), ["children", "name", "parent", "path", "resourceType"]);
+ });
+
+ test('nested completion test for', () => {
+ let document = `
+ <html>
+ <body>
+ \${ request.resource.parent. }
+ </body>
+ </html>
+ `;
+ let completions = completionProvider.provideCompletionItems0('${request.resource.parent.', document);
+ // test a subset, otherwise it's too cumbersome
+ let completionVariables = completions?.map ( c => c.label.toString()).slice(0,5);
+ assert.deepStrictEqual(completionVariables?.sort(), ["children", "name", "parent", "path", "resourceType"]);
+ });
});