mirror of
https://github.com/adelphes/android-dev-ext.git
synced 2025-12-23 01:48:18 +00:00
support package, type and static field import completion
This commit is contained in:
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user