diff --git a/langserver/java/body-parser3.js b/langserver/java/body-parser3.js index 3efee20..ad00350 100644 --- a/langserver/java/body-parser3.js +++ b/langserver/java/body-parser3.js @@ -205,7 +205,7 @@ function parse(source, typemap) { const timeEnd = name => (timers.delete(name), console.timeEnd(name)); try { time('tokenize'); - tokens = new TokenList(tokenize(source)); + tokens = new TokenList(unit.tokens = tokenize(source)); problems = tokens.problems; timeEnd('tokenize'); @@ -318,16 +318,22 @@ function parseUnit(tokens, unit, typemap) { */ function packageDeclaration(tokens) { tokens.mark(); + const package_token = tokens.current; + package_token.loc = 'pkgname:'; tokens.expectValue('package'); - const pkg_name_parts = []; + let pkg_name_parts = [], dot; for (;;) { let name = tokens.current; if (!tokens.isKind('ident')) { name = null; addproblem(tokens, ParseProblem.Error(tokens.current, `Package identifier expected`)); } - if (name) pkg_name_parts.push(name.value); - if (tokens.isValue('.')) { + if (name) { + 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; } const decl_tokens = tokens.markEnd(); diff --git a/langserver/java/source-types.js b/langserver/java/source-types.js index cf09d28..887a87d 100644 --- a/langserver/java/source-types.js +++ b/langserver/java/source-types.js @@ -366,6 +366,7 @@ class SourceConstructor extends Constructor { this.sourceParameters = parameters; this.throws = throws; this.body = body; + this.parsed = null; } get hasImplementation() { @@ -411,6 +412,7 @@ class SourceMethod extends Method { this.sourceParameters = parameters; this.throws = throws; this.body = body; + this.parsed = null; } get hasImplementation() { @@ -449,6 +451,7 @@ class SourceInitialiser extends MethodBase { this.owner = owner; this.modifierTokens = modifiers; this.body = body; + this.parsed = null; } /** @@ -535,12 +538,38 @@ class SourceImport { } class SourceUnit { + /** @type {Token[]} */ + tokens = []; /** @type {SourcePackage} */ package_ = null; /** @type {SourceImport[]} */ imports = []; /** @type {SourceType[]} */ 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 { diff --git a/langserver/java/tokenizer.js b/langserver/java/tokenizer.js index 9f00518..e3adc02 100644 --- a/langserver/java/tokenizer.js +++ b/langserver/java/tokenizer.js @@ -49,6 +49,7 @@ class Token extends TextBlock { constructor(text, start, length, kind) { super(new BlockRange(text, start, length), tokenKindToSimplified(text, start, length, kind)); this.kind = kind; + this.loc = ''; } get value() { diff --git a/langserver/java/validater.js b/langserver/java/validater.js index 3ea395e..636df08 100644 --- a/langserver/java/validater.js +++ b/langserver/java/validater.js @@ -3,6 +3,28 @@ const { resolveImports } = require('../java/import-resolver'); const { SourceUnit } = require('./source-types'); const { parseBody } = require('./body-parser3'); +/** + * @param {SourceUnit} unit + * @param {Map} 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 {Map} androidLibrary @@ -12,41 +34,20 @@ function validate(unit, androidLibrary) { console.time('validation'); 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 = [ // require('./validation/multiple-package-decls'), // require('./validation/unit-decl-order'), // require('./validation/duplicate-members'), // require('./validation/parse-errors'), - require('./validation/modifier-errors'), - require('./validation/unresolved-imports'), - require('./validation/invalid-types'), - require('./validation/bad-extends'), - require('./validation/bad-implements'), - require('./validation/non-implemented-interfaces'), - require('./validation/bad-overrides'), - require('./validation/missing-constructor'), + // require('./validation/modifier-errors'), + // require('./validation/unresolved-imports'), + // require('./validation/invalid-types'), + // require('./validation/bad-extends'), + // require('./validation/bad-implements'), + // require('./validation/non-implemented-interfaces'), + // require('./validation/bad-overrides'), + // require('./validation/missing-constructor'), //require('./validation/expression-compatibility'), ]; let problems = [ @@ -73,4 +74,5 @@ function validate(unit, androidLibrary) { module.exports = { validate, + parseMethodBodies, } diff --git a/langserver/server.js b/langserver/server.js index 906ddbd..f1af448 100644 --- a/langserver/server.js +++ b/langserver/server.js @@ -22,7 +22,7 @@ const { loadAndroidLibrary, CEIType } = require('java-mti'); const { ParseProblem } = require('./java/parser'); const { parse } = require('./java/body-parser3'); const { SourceUnit } = require('./java/source-types'); -const { validate } = require('./java/validater'); +const { validate, parseMethodBodies } = require('./java/validater'); /** * @typedef {Map} AndroidLibrary @@ -52,6 +52,9 @@ function reparse(uri, content) { } const typemap = new Map(androidLibrary); const result = parse(content, typemap); + if (result) { + parseMethodBodies(result.unit, typemap); + } parsed = { content, uri, @@ -261,7 +264,7 @@ async function validateTextDocument(textDocument) { if (parsed && parsed.result) { try { - problems = [...parsed.result.problems, ...validate(parsed.result.unit, parsed.typemap)]; + //problems = [...parsed.result.problems, ...validate(parsed.result.unit, parsed.typemap)]; } catch(err) { console.error(err); } @@ -355,6 +358,38 @@ connection.onCompletion( } const lib = (parsed && parsed.typemap) || androidLibrary; 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 = { class: CompletionItemKind.Class, interface: CompletionItemKind.Interface,