support package, type and static field import completion

This commit is contained in:
Dave Holoway
2020-06-23 18:57:07 +01:00
parent 591907f523
commit db865825d0
2 changed files with 121 additions and 35 deletions

View File

@@ -318,8 +318,7 @@ function parseUnit(tokens, unit, typemap) {
*/ */
function packageDeclaration(tokens) { function packageDeclaration(tokens) {
tokens.mark(); tokens.mark();
const package_token = tokens.current; tokens.current.loc = 'pkgname:';
package_token.loc = 'pkgname:';
tokens.expectValue('package'); tokens.expectValue('package');
let pkg_name_parts = [], dot; let pkg_name_parts = [], dot;
for (;;) { for (;;) {
@@ -348,10 +347,11 @@ function packageDeclaration(tokens) {
*/ */
function importDeclaration(tokens, typemap) { function importDeclaration(tokens, typemap) {
tokens.mark(); tokens.mark();
tokens.current.loc = 'fqn:';
tokens.expectValue('import'); tokens.expectValue('import');
const static_token = tokens.getIfValue('static'); const static_token = tokens.getIfValue('static');
let asterisk_token = null; let asterisk_token = null, dot;
const pkg_name_parts = []; const pkg_token_parts = [], pkg_name_parts = [];
for (;;) { for (;;) {
let name = tokens.current; let name = tokens.current;
if (!tokens.isKind('ident')) { if (!tokens.isKind('ident')) {
@@ -359,9 +359,12 @@ function importDeclaration(tokens, typemap) {
addproblem(tokens, ParseProblem.Error(tokens.current, `Package identifier expected`)); addproblem(tokens, ParseProblem.Error(tokens.current, `Package identifier expected`));
} }
if (name) { if (name) {
pkg_name_parts.push(name); name.loc = `fqn:${pkg_name_parts.join('.')}`;
pkg_token_parts.push(name);
pkg_name_parts.push(name.value);
} }
if (tokens.isValue('.')) { if (dot = tokens.getIfValue('.')) {
dot.loc = `fqn:${pkg_name_parts.join('.')}`;
if (!(asterisk_token = tokens.getIfValue('*'))) { if (!(asterisk_token = tokens.getIfValue('*'))) {
continue; continue;
} }
@@ -369,10 +372,10 @@ function importDeclaration(tokens, typemap) {
const decl_tokens = tokens.markEnd(); const decl_tokens = tokens.markEnd();
semicolon(tokens); semicolon(tokens);
const pkg_name = pkg_name_parts.map(x => x.source).join('.'); const pkg_name = pkg_name_parts.join('.');
const resolved = resolveSingleImport(typemap, pkg_name, !!static_token, !!asterisk_token, 'import'); const resolved = resolveSingleImport(typemap, pkg_name, !!static_token, !!asterisk_token, 'import');
return new SourceImport(decl_tokens, pkg_name_parts, pkg_name, static_token, asterisk_token, resolved); return new SourceImport(decl_tokens, pkg_token_parts, pkg_name, static_token, asterisk_token, resolved);
} }
} }

View File

@@ -17,7 +17,7 @@ const {
const { TextDocument } = require('vscode-languageserver-textdocument'); const { TextDocument } = require('vscode-languageserver-textdocument');
const { loadAndroidLibrary, CEIType } = require('java-mti'); const { loadAndroidLibrary, JavaType, CEIType } = require('java-mti');
const { ParseProblem } = require('./java/parser'); const { ParseProblem } = require('./java/parser');
const { parse } = require('./java/body-parser3'); const { parse } = require('./java/body-parser3');
@@ -343,6 +343,110 @@ connection.onDidChangeWatchedFiles((_change) => {
connection.console.log('We received a file change event'); connection.console.log('We received a file change event');
}); });
function getFullyQualifiedNameCompletion(name) {
if (name === '') {
return getPackageCompletion('');
}
// name is a fully dotted name, possibly including a static member
let typelist = [...parsed.typemap.keys()];
const split_name = name.split('.');
let pkgname = '';
/** @type {JavaType} */
let type = null, typename = '';
for (let name_part of split_name) {
if (type) {
if (typelist.includes(`${typename}$${name_part}`)) {
type = parsed.typemap.get(typename = `${typename}$${name_part}`);
continue;
}
return [];
}
typename = pkgname + name_part;
if (typelist.includes(typename)) {
type = parsed.typemap.get(typename);
continue;
}
pkgname = `${pkgname}${name_part}/`;
}
if (type) {
// add inner types and static fields
return [
...typelist.map(t => {
if (!t.startsWith(typename)) return;
const m = t.slice(typename.length).match(/^\$.+/);
if (!m) return;
return {
label: m[0].slice(1).replace(/\$/g,'.'),
kind: CompletionItemKind.Class,
data: -1,
}
}).filter(x => x),
...type.fields.filter(m => m.modifiers.includes('static')).map(f => ({
label: f.name,
kind: CompletionItemKind.Field,
data: -1,
}))
]
}
// sub-package or type
const search_pkg = pkgname;
return typelist.reduce((arr,typename) => {
if (typename.startsWith(search_pkg)) {
const m = typename.slice(search_pkg.length).match(/^(.+?)(\/|$)/);
if (m) {
if (m[2]) {
// package name
if (!arr.find(x => x.label === m[1])) {
arr.push({
label: m[1],
kind: CompletionItemKind.Unit,
data: -1,
})
}
} else {
// type name
arr.push({
label: m[1].replace(/\$/g,'.'),
kind: CompletionItemKind.Class,
data: -1,
})
}
}
}
return arr;
}, []);
}
function getPackageCompletion(pkg) {
let pkgs;
if (pkg === '') {
// root packages
pkgs = [...parsed.typemap.keys()].reduce((set,typename) => {
const m = typename.match(/(.+?)\//);
m && set.add(m[1]);
return set;
}, new Set());
} else {
// sub-package
const search_pkg = pkg + '/';
pkgs = [...parsed.typemap.keys()].reduce((arr,typename) => {
if (typename.startsWith(search_pkg)) {
const m = typename.slice(search_pkg.length).match(/^(.+?)\//);
if (m) arr.add(m[1]);
}
return arr;
}, new Set());
}
return [...pkgs].filter(x => x).sort().map(pkg => ({
label: pkg,
kind: CompletionItemKind.Unit,
data: -1,
}));
}
// This handler provides the initial list of the completion items. // This handler provides the initial list of the completion items.
let allCompletionTypes = null; let allCompletionTypes = null;
connection.onCompletion( connection.onCompletion(
@@ -363,32 +467,11 @@ connection.onCompletion(
const options = parsed.result.unit.getCompletionOptionsAt(index); const options = parsed.result.unit.getCompletionOptionsAt(index);
console.log(options); console.log(options);
if (/^pkgname:/.test(options.loc)) { if (/^pkgname:/.test(options.loc)) {
const pkg = options.loc.split(':').pop(); return getPackageCompletion(options.loc.split(':').pop());
let pkgs; }
if (pkg === '') { if (/^fqn:/.test(options.loc)) {
// root packages return getFullyQualifiedNameCompletion(options.loc.split(':').pop());
pkgs = [...parsed.typemap.keys()].reduce((set,typename) => { }
const m = typename.match(/(.+?)\//);
m && set.add(m[1]);
return set;
}, new Set());
} else {
// sub-package
const search_pkg = pkg + '/';
pkgs = [...parsed.typemap.keys()].reduce((arr,typename) => {
if (typename.startsWith(search_pkg)) {
const m = typename.slice(search_pkg.length).match(/^(.+?)\//);
if (m) arr.add(m[1]);
}
return arr;
}, new Set());
}
return [...pkgs].filter(x => x).sort().map(pkg => ({
label: pkg,
kind: CompletionItemKind.Unit,
data: -1,
}));
}
} }
const typeKindMap = { const typeKindMap = {
class: CompletionItemKind.Class, class: CompletionItemKind.Class,