mirror of
https://github.com/adelphes/android-dev-ext.git
synced 2025-12-23 01:48:18 +00:00
use exact type signatures for locating types for completion items
This commit is contained in:
@@ -347,7 +347,7 @@ function packageDeclaration(tokens) {
|
|||||||
*/
|
*/
|
||||||
function importDeclaration(tokens, typemap) {
|
function importDeclaration(tokens, typemap) {
|
||||||
tokens.mark();
|
tokens.mark();
|
||||||
tokens.current.loc = 'fqn:';
|
tokens.current.loc = 'fqdi:';
|
||||||
tokens.expectValue('import');
|
tokens.expectValue('import');
|
||||||
const static_token = tokens.getIfValue('static');
|
const static_token = tokens.getIfValue('static');
|
||||||
let asterisk_token = null, dot;
|
let asterisk_token = null, dot;
|
||||||
@@ -359,12 +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) {
|
||||||
name.loc = `fqn:${pkg_name_parts.join('.')}`;
|
name.loc = `fqdi:${pkg_name_parts.join('.')}`;
|
||||||
pkg_token_parts.push(name);
|
pkg_token_parts.push(name);
|
||||||
pkg_name_parts.push(name.value);
|
pkg_name_parts.push(name.value);
|
||||||
}
|
}
|
||||||
if (dot = tokens.getIfValue('.')) {
|
if (dot = tokens.getIfValue('.')) {
|
||||||
dot.loc = `fqn:${pkg_name_parts.join('.')}`;
|
dot.loc = `fqdi:${pkg_name_parts.join('.')}`;
|
||||||
if (!(asterisk_token = tokens.getIfValue('*'))) {
|
if (!(asterisk_token = tokens.getIfValue('*'))) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ class MemberExpression extends Expression {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (instance instanceof PackageNameType) {
|
if (instance instanceof PackageNameType) {
|
||||||
this.dot.loc = `fqs:${instance.package_name}`;
|
this.dot.loc = `fqdi:${instance.package_name}`;
|
||||||
if (!this.member) {
|
if (!this.member) {
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
@@ -60,7 +60,7 @@ class MemberExpression extends Expression {
|
|||||||
return AnyType.Instance;
|
return AnyType.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.dot.loc = `${loc}:${instance.fullyDottedTypeName}`
|
this.dot.loc = `${loc}:${instance.typeSignature}`
|
||||||
if (!this.member) {
|
if (!this.member) {
|
||||||
ri.problems.push(ParseProblem.Error(this.dot, `Identifier expected`));
|
ri.problems.push(ParseProblem.Error(this.dot, `Identifier expected`));
|
||||||
return instance;
|
return instance;
|
||||||
|
|||||||
@@ -345,10 +345,90 @@ connection.onDidChangeWatchedFiles((_change) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param {Map<string,CEIType>} typemap
|
||||||
|
* @param {string} type_signature
|
||||||
|
* @param {{ statics: boolean }} opts
|
||||||
|
* @param {string[]} [typelist]
|
||||||
|
*/
|
||||||
|
function getTypedNameCompletion(typemap, type_signature, opts, typelist) {
|
||||||
|
if (!/^L.+;/.test(type_signature)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const type = typemap.get(type_signature.slice(1,-1));
|
||||||
|
if (!type) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(type instanceof CEIType)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// add inner types, fields and methods
|
||||||
|
class FirstSetMap extends Map {
|
||||||
|
set(key, value) {
|
||||||
|
return this.has(key) ? this : super.set(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const fields = new FirstSetMap(), methods = new FirstSetMap();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string[]} modifiers
|
||||||
|
* @param {JavaType} t
|
||||||
|
*/
|
||||||
|
function shouldInclude(modifiers, t) {
|
||||||
|
if (opts.statics !== modifiers.includes('static')) return;
|
||||||
|
if (modifiers.includes('public')) return true;
|
||||||
|
if (modifiers.includes('protected')) return true;
|
||||||
|
if (modifiers.includes('private') && t === type) return true;
|
||||||
|
// @ts-ignore
|
||||||
|
return t.packageName === type.packageName;
|
||||||
|
}
|
||||||
|
function sortByName(a,b) {
|
||||||
|
return a.name.localeCompare(b.name, undefined, {sensitivity: 'base'})
|
||||||
|
}
|
||||||
|
|
||||||
|
getTypeInheritanceList(type).forEach((t,idx) => {
|
||||||
|
t.fields.sort(sortByName).filter(f => shouldInclude(f.modifiers, t)).forEach(f => fields.set(f.name, {f, sortText: `${idx+100}${f.name}`}));
|
||||||
|
t.methods.sort(sortByName).filter(f => shouldInclude(f.modifiers, t)).forEach(m => methods.set(m.methodSignature, {m, sortText: `${idx+100}${m.name}`}));
|
||||||
|
});
|
||||||
|
|
||||||
|
const subtype_search = type.shortSignature + '$';
|
||||||
|
|
||||||
|
return [
|
||||||
|
...(typelist || [...typemap.keys()]).map(t => {
|
||||||
|
if (!opts.statics) return;
|
||||||
|
if (!t.startsWith(subtype_search)) return;
|
||||||
|
return {
|
||||||
|
label: t.slice(subtype_search.length).replace(/\$/g,'.'),
|
||||||
|
kind: CompletionItemKind.Class,
|
||||||
|
data: -1,
|
||||||
|
}
|
||||||
|
}).filter(x => x),
|
||||||
|
// fields
|
||||||
|
...[...fields.values()].map(f => ({
|
||||||
|
label: `${f.f.name}: ${f.f.type.simpleTypeName}`,
|
||||||
|
insertText: f.f.name,
|
||||||
|
kind: CompletionItemKind.Field,
|
||||||
|
sortText: f.sortText,
|
||||||
|
data: -1,
|
||||||
|
})),
|
||||||
|
// methods
|
||||||
|
...[...methods.values()].map(m => ({
|
||||||
|
label: m.m.shortlabel,
|
||||||
|
kind: CompletionItemKind.Method,
|
||||||
|
insertText: m.m.name,
|
||||||
|
sortText: m.sortText,
|
||||||
|
data: -1,
|
||||||
|
}))
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Map<string,CEIType>} typemap
|
||||||
* @param {string} dotted_name
|
* @param {string} dotted_name
|
||||||
* @param {{ statics: boolean }} opts
|
* @param {{ statics: boolean }} opts
|
||||||
*/
|
*/
|
||||||
function getFullyQualifiedNameCompletion(dotted_name, opts) {
|
function getFullyQualifiedDottedIdentCompletion(typemap, dotted_name, opts) {
|
||||||
if (dotted_name === '') {
|
if (dotted_name === '') {
|
||||||
return getPackageCompletion('');
|
return getPackageCompletion('');
|
||||||
}
|
}
|
||||||
@@ -376,62 +456,7 @@ function getFullyQualifiedNameCompletion(dotted_name, opts) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (type) {
|
if (type) {
|
||||||
if (!(type instanceof CEIType)) {
|
return getTypedNameCompletion(typemap, type.typeSignature, opts, typelist);
|
||||||
return [];
|
|
||||||
}
|
|
||||||
// add inner types, fields and methods
|
|
||||||
class FirstSetMap extends Map {
|
|
||||||
set(key, value) {
|
|
||||||
return this.has(key) ? this : super.set(key, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const fields = new FirstSetMap(), methods = new FirstSetMap();
|
|
||||||
/**
|
|
||||||
* @param {string[]} modifiers
|
|
||||||
* @param {JavaType} t
|
|
||||||
*/
|
|
||||||
function shouldInclude(modifiers, t) {
|
|
||||||
if (opts.statics !== modifiers.includes('static')) return;
|
|
||||||
if (modifiers.includes('public')) return true;
|
|
||||||
if (modifiers.includes('protected')) return true;
|
|
||||||
if (modifiers.includes('private') && t === type) return true;
|
|
||||||
// @ts-ignore
|
|
||||||
return t.packageName === type.packageName;
|
|
||||||
}
|
|
||||||
function sortByName(a,b) { return a.name.localeCompare(b.name, undefined, {sensitivity: 'base'}) }
|
|
||||||
getTypeInheritanceList(type).forEach((t,idx) => {
|
|
||||||
t.fields.sort(sortByName).filter(f => shouldInclude(f.modifiers, t)).forEach(f => fields.set(f.name, {f, sortText: `${idx+100}${f.name}`}));
|
|
||||||
t.methods.sort(sortByName).filter(f => shouldInclude(f.modifiers, t)).forEach(m => methods.set(m.methodSignature, {m, sortText: `${idx+100}${m.name}`}));
|
|
||||||
});
|
|
||||||
return [
|
|
||||||
...typelist.map(t => {
|
|
||||||
if (!opts.statics) return;
|
|
||||||
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),
|
|
||||||
// fields
|
|
||||||
...[...fields.values()].map(f => ({
|
|
||||||
label: `${f.f.name}: ${f.f.type.simpleTypeName}`,
|
|
||||||
insertText: f.f.name,
|
|
||||||
kind: CompletionItemKind.Field,
|
|
||||||
sortText: f.sortText,
|
|
||||||
data: -1,
|
|
||||||
})),
|
|
||||||
// methods
|
|
||||||
...[...methods.values()].map(m => ({
|
|
||||||
label: m.m.shortlabel,
|
|
||||||
kind: CompletionItemKind.Method,
|
|
||||||
insertText: m.m.name,
|
|
||||||
sortText: m.sortText,
|
|
||||||
data: -1,
|
|
||||||
}))
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// sub-package or type
|
// sub-package or type
|
||||||
@@ -519,17 +544,17 @@ connection.onCompletion(
|
|||||||
if (/^pkgname:/.test(options.loc)) {
|
if (/^pkgname:/.test(options.loc)) {
|
||||||
return getPackageCompletion(options.loc.split(':').pop());
|
return getPackageCompletion(options.loc.split(':').pop());
|
||||||
}
|
}
|
||||||
if (/^fqn:/.test(options.loc)) {
|
if (/^fqdi:/.test(options.loc)) {
|
||||||
// fully-qualified type/field name
|
// fully-qualified type/field name
|
||||||
return getFullyQualifiedNameCompletion(options.loc.split(':').pop(), { statics: true });
|
return getFullyQualifiedDottedIdentCompletion(parsed.typemap, options.loc.split(':').pop(), { statics: true });
|
||||||
}
|
}
|
||||||
if (/^fqs:/.test(options.loc)) {
|
if (/^fqs:/.test(options.loc)) {
|
||||||
// fully-qualified expression
|
// fully-qualified expression
|
||||||
return getFullyQualifiedNameCompletion(options.loc.split(':').pop(), { statics: true });
|
return getTypedNameCompletion(parsed.typemap, options.loc.split(':').pop(), { statics: true });
|
||||||
}
|
}
|
||||||
if (/^fqi:/.test(options.loc)) {
|
if (/^fqi:/.test(options.loc)) {
|
||||||
// fully-qualified expression
|
// fully-qualified expression
|
||||||
return getFullyQualifiedNameCompletion(options.loc.split(':').pop(), { statics: false });
|
return getTypedNameCompletion(parsed.typemap, options.loc.split(':').pop(), { statics: false });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const typeKindMap = {
|
const typeKindMap = {
|
||||||
|
|||||||
Reference in New Issue
Block a user