mirror of
https://github.com/adelphes/android-dev-ext.git
synced 2025-12-23 01:48:18 +00:00
initial test of context-dependant code completion
This commit is contained in:
@@ -205,7 +205,7 @@ function parse(source, typemap) {
|
|||||||
const timeEnd = name => (timers.delete(name), console.timeEnd(name));
|
const timeEnd = name => (timers.delete(name), console.timeEnd(name));
|
||||||
try {
|
try {
|
||||||
time('tokenize');
|
time('tokenize');
|
||||||
tokens = new TokenList(tokenize(source));
|
tokens = new TokenList(unit.tokens = tokenize(source));
|
||||||
problems = tokens.problems;
|
problems = tokens.problems;
|
||||||
timeEnd('tokenize');
|
timeEnd('tokenize');
|
||||||
|
|
||||||
@@ -318,16 +318,22 @@ function parseUnit(tokens, unit, typemap) {
|
|||||||
*/
|
*/
|
||||||
function packageDeclaration(tokens) {
|
function packageDeclaration(tokens) {
|
||||||
tokens.mark();
|
tokens.mark();
|
||||||
|
const package_token = tokens.current;
|
||||||
|
package_token.loc = 'pkgname:';
|
||||||
tokens.expectValue('package');
|
tokens.expectValue('package');
|
||||||
const pkg_name_parts = [];
|
let pkg_name_parts = [], dot;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
let name = tokens.current;
|
let name = tokens.current;
|
||||||
if (!tokens.isKind('ident')) {
|
if (!tokens.isKind('ident')) {
|
||||||
name = null;
|
name = null;
|
||||||
addproblem(tokens, ParseProblem.Error(tokens.current, `Package identifier expected`));
|
addproblem(tokens, ParseProblem.Error(tokens.current, `Package identifier expected`));
|
||||||
}
|
}
|
||||||
if (name) pkg_name_parts.push(name.value);
|
if (name) {
|
||||||
if (tokens.isValue('.')) {
|
name.loc = `pkgname:${pkg_name_parts.join('/')}`;
|
||||||
|
pkg_name_parts.push(name.value);
|
||||||
|
}
|
||||||
|
if (dot = tokens.getIfValue('.')) {
|
||||||
|
dot.loc = `pkgname:${pkg_name_parts.join('/')}`;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const decl_tokens = tokens.markEnd();
|
const decl_tokens = tokens.markEnd();
|
||||||
|
|||||||
@@ -366,6 +366,7 @@ class SourceConstructor extends Constructor {
|
|||||||
this.sourceParameters = parameters;
|
this.sourceParameters = parameters;
|
||||||
this.throws = throws;
|
this.throws = throws;
|
||||||
this.body = body;
|
this.body = body;
|
||||||
|
this.parsed = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
get hasImplementation() {
|
get hasImplementation() {
|
||||||
@@ -411,6 +412,7 @@ class SourceMethod extends Method {
|
|||||||
this.sourceParameters = parameters;
|
this.sourceParameters = parameters;
|
||||||
this.throws = throws;
|
this.throws = throws;
|
||||||
this.body = body;
|
this.body = body;
|
||||||
|
this.parsed = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
get hasImplementation() {
|
get hasImplementation() {
|
||||||
@@ -449,6 +451,7 @@ class SourceInitialiser extends MethodBase {
|
|||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
this.modifierTokens = modifiers;
|
this.modifierTokens = modifiers;
|
||||||
this.body = body;
|
this.body = body;
|
||||||
|
this.parsed = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -535,12 +538,38 @@ class SourceImport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class SourceUnit {
|
class SourceUnit {
|
||||||
|
/** @type {Token[]} */
|
||||||
|
tokens = [];
|
||||||
/** @type {SourcePackage} */
|
/** @type {SourcePackage} */
|
||||||
package_ = null;
|
package_ = null;
|
||||||
/** @type {SourceImport[]} */
|
/** @type {SourceImport[]} */
|
||||||
imports = [];
|
imports = [];
|
||||||
/** @type {SourceType[]} */
|
/** @type {SourceType[]} */
|
||||||
types = [];
|
types = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {number} char_index
|
||||||
|
*/
|
||||||
|
getCompletionOptionsAt(char_index) {
|
||||||
|
let i = 0;
|
||||||
|
let loc = '';
|
||||||
|
for (let tok of this.tokens) {
|
||||||
|
if (char_index > tok.range.start + tok.range.length) {
|
||||||
|
i++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
while (i > 0 && tok.kind === 'wsc') {
|
||||||
|
tok = this.tokens[--i];
|
||||||
|
}
|
||||||
|
loc = tok.loc;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
index: char_index,
|
||||||
|
loc: loc,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SourceArrayType extends ArrayType {
|
class SourceArrayType extends ArrayType {
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ class Token extends TextBlock {
|
|||||||
constructor(text, start, length, kind) {
|
constructor(text, start, length, kind) {
|
||||||
super(new BlockRange(text, start, length), tokenKindToSimplified(text, start, length, kind));
|
super(new BlockRange(text, start, length), tokenKindToSimplified(text, start, length, kind));
|
||||||
this.kind = kind;
|
this.kind = kind;
|
||||||
|
this.loc = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
get value() {
|
get value() {
|
||||||
|
|||||||
@@ -3,6 +3,28 @@ const { resolveImports } = require('../java/import-resolver');
|
|||||||
const { SourceUnit } = require('./source-types');
|
const { SourceUnit } = require('./source-types');
|
||||||
const { parseBody } = require('./body-parser3');
|
const { parseBody } = require('./body-parser3');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {SourceUnit} unit
|
||||||
|
* @param {Map<string, CEIType>} androidLibrary
|
||||||
|
*/
|
||||||
|
function parseMethodBodies(unit, androidLibrary) {
|
||||||
|
const resolved_types = [
|
||||||
|
...resolveImports(androidLibrary, [], [], null).resolved,
|
||||||
|
...unit.imports.filter(i => i.resolved).map(i => i.resolved),
|
||||||
|
]
|
||||||
|
unit.types.forEach(t => {
|
||||||
|
t.initers.forEach(i => {
|
||||||
|
i.parsed = parseBody(i, resolved_types, androidLibrary);
|
||||||
|
})
|
||||||
|
t.constructors.forEach(c => {
|
||||||
|
c.parsed = parseBody(c, resolved_types, androidLibrary);
|
||||||
|
})
|
||||||
|
t.sourceMethods.forEach(m => {
|
||||||
|
m.parsed = parseBody(m, resolved_types, androidLibrary);
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {SourceUnit} unit
|
* @param {SourceUnit} unit
|
||||||
* @param {Map<string, CEIType>} androidLibrary
|
* @param {Map<string, CEIType>} androidLibrary
|
||||||
@@ -12,41 +34,20 @@ function validate(unit, androidLibrary) {
|
|||||||
console.time('validation');
|
console.time('validation');
|
||||||
|
|
||||||
let probs = [];
|
let probs = [];
|
||||||
const resolved_types = [
|
|
||||||
...resolveImports(androidLibrary, [], [], null).resolved,
|
|
||||||
...unit.imports.filter(i => i.resolved).map(i => i.resolved),
|
|
||||||
]
|
|
||||||
unit.types.forEach(t => {
|
|
||||||
t.initers.forEach(i => {
|
|
||||||
const parsed = parseBody(i, resolved_types, androidLibrary);
|
|
||||||
if (parsed)
|
|
||||||
probs = probs.concat(parsed.problems)
|
|
||||||
})
|
|
||||||
t.constructors.forEach(c => {
|
|
||||||
const parsed = parseBody(c, resolved_types, androidLibrary);
|
|
||||||
if (parsed)
|
|
||||||
probs = probs.concat(parsed.problems)
|
|
||||||
})
|
|
||||||
t.sourceMethods.forEach(m => {
|
|
||||||
const parsed = parseBody(m, resolved_types, androidLibrary);
|
|
||||||
if (parsed)
|
|
||||||
probs = probs.concat(parsed.problems)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
const module_validaters = [
|
const module_validaters = [
|
||||||
// require('./validation/multiple-package-decls'),
|
// require('./validation/multiple-package-decls'),
|
||||||
// require('./validation/unit-decl-order'),
|
// require('./validation/unit-decl-order'),
|
||||||
// require('./validation/duplicate-members'),
|
// require('./validation/duplicate-members'),
|
||||||
// require('./validation/parse-errors'),
|
// require('./validation/parse-errors'),
|
||||||
require('./validation/modifier-errors'),
|
// require('./validation/modifier-errors'),
|
||||||
require('./validation/unresolved-imports'),
|
// require('./validation/unresolved-imports'),
|
||||||
require('./validation/invalid-types'),
|
// require('./validation/invalid-types'),
|
||||||
require('./validation/bad-extends'),
|
// require('./validation/bad-extends'),
|
||||||
require('./validation/bad-implements'),
|
// require('./validation/bad-implements'),
|
||||||
require('./validation/non-implemented-interfaces'),
|
// require('./validation/non-implemented-interfaces'),
|
||||||
require('./validation/bad-overrides'),
|
// require('./validation/bad-overrides'),
|
||||||
require('./validation/missing-constructor'),
|
// require('./validation/missing-constructor'),
|
||||||
//require('./validation/expression-compatibility'),
|
//require('./validation/expression-compatibility'),
|
||||||
];
|
];
|
||||||
let problems = [
|
let problems = [
|
||||||
@@ -73,4 +74,5 @@ function validate(unit, androidLibrary) {
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
validate,
|
validate,
|
||||||
|
parseMethodBodies,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ const { loadAndroidLibrary, 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');
|
||||||
const { SourceUnit } = require('./java/source-types');
|
const { SourceUnit } = require('./java/source-types');
|
||||||
const { validate } = require('./java/validater');
|
const { validate, parseMethodBodies } = require('./java/validater');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Map<string, CEIType>} AndroidLibrary
|
* @typedef {Map<string, CEIType>} AndroidLibrary
|
||||||
@@ -52,6 +52,9 @@ function reparse(uri, content) {
|
|||||||
}
|
}
|
||||||
const typemap = new Map(androidLibrary);
|
const typemap = new Map(androidLibrary);
|
||||||
const result = parse(content, typemap);
|
const result = parse(content, typemap);
|
||||||
|
if (result) {
|
||||||
|
parseMethodBodies(result.unit, typemap);
|
||||||
|
}
|
||||||
parsed = {
|
parsed = {
|
||||||
content,
|
content,
|
||||||
uri,
|
uri,
|
||||||
@@ -261,7 +264,7 @@ async function validateTextDocument(textDocument) {
|
|||||||
|
|
||||||
if (parsed && parsed.result) {
|
if (parsed && parsed.result) {
|
||||||
try {
|
try {
|
||||||
problems = [...parsed.result.problems, ...validate(parsed.result.unit, parsed.typemap)];
|
//problems = [...parsed.result.problems, ...validate(parsed.result.unit, parsed.typemap)];
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
}
|
}
|
||||||
@@ -355,6 +358,38 @@ connection.onCompletion(
|
|||||||
}
|
}
|
||||||
const lib = (parsed && parsed.typemap) || androidLibrary;
|
const lib = (parsed && parsed.typemap) || androidLibrary;
|
||||||
if (!lib) return [];
|
if (!lib) return [];
|
||||||
|
if (parsed.result && parsed.result.unit) {
|
||||||
|
const index = parsed.indexAt(_textDocumentPosition.position);
|
||||||
|
const options = parsed.result.unit.getCompletionOptionsAt(index);
|
||||||
|
console.log(options);
|
||||||
|
if (/^pkgname:/.test(options.loc)) {
|
||||||
|
const pkg = options.loc.split(':').pop();
|
||||||
|
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,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
const typeKindMap = {
|
const typeKindMap = {
|
||||||
class: CompletionItemKind.Class,
|
class: CompletionItemKind.Class,
|
||||||
interface: CompletionItemKind.Interface,
|
interface: CompletionItemKind.Interface,
|
||||||
|
|||||||
Reference in New Issue
Block a user