add support for docs in source types

This commit is contained in:
Dave Holoway
2020-06-24 23:28:35 +01:00
parent e7e73387aa
commit b45314cc95
4 changed files with 74 additions and 39 deletions

View File

@@ -16,6 +16,7 @@ class TokenList {
/** @type {ParseProblem[]} */ /** @type {ParseProblem[]} */
this.problems = []; this.problems = [];
this.marks = []; this.marks = [];
this.last_mlc = '';
} }
/** /**
@@ -33,8 +34,22 @@ class TokenList {
if (!this.current || this.current.kind !== 'wsc') { if (!this.current || this.current.kind !== 'wsc') {
return this.current; return this.current;
} }
const wsc = this.current.value;
if (wsc.startsWith('/*')) {
this.last_mlc = wsc;
} }
} }
}
clearMLC() {
this.last_mlc = '';
}
getLastMLC() {
const s = this.last_mlc;
this.last_mlc = '';
return s;
}
mark() { mark() {
this.marks.unshift(this.idx); this.marks.unshift(this.idx);

View File

@@ -259,7 +259,7 @@ function parseUnit(tokens, unit, typemap) {
} }
if (tokens.current.value === '@') { if (tokens.current.value === '@') {
tokens.inc().value === 'interface' tokens.inc().value === 'interface'
? sourceType(modifiers, tokens, package_name, '@interface', unit, resolved_imports, typemap) ? sourceType(tokens.getLastMLC(), modifiers, tokens, package_name, '@interface', unit, resolved_imports, typemap)
: annotations.push(annotation(tokens, null, resolved_imports, typemap)); : annotations.push(annotation(tokens, null, resolved_imports, typemap));
continue; continue;
} }
@@ -276,6 +276,7 @@ function parseUnit(tokens, unit, typemap) {
if (modifiers[0]) { if (modifiers[0]) {
addproblem(tokens, ParseProblem.Error(tokens.current, `Unexpected modifier: ${modifiers[0].source}`)); addproblem(tokens, ParseProblem.Error(tokens.current, `Unexpected modifier: ${modifiers[0].source}`));
} }
tokens.clearMLC();
const pkg = packageDeclaration(tokens); const pkg = packageDeclaration(tokens);
if (!package_name) { if (!package_name) {
unit.package_ = pkg; unit.package_ = pkg;
@@ -290,6 +291,7 @@ function parseUnit(tokens, unit, typemap) {
if (modifiers[0]) { if (modifiers[0]) {
addproblem(tokens, ParseProblem.Error(tokens.current, `Unexpected modifier: ${modifiers[0].source}`)); addproblem(tokens, ParseProblem.Error(tokens.current, `Unexpected modifier: ${modifiers[0].source}`));
} }
tokens.clearMLC();
const imprt = importDeclaration(tokens, typemap); const imprt = importDeclaration(tokens, typemap);
unit.imports.push(imprt); unit.imports.push(imprt);
if (imprt.resolved) { if (imprt.resolved) {
@@ -298,7 +300,7 @@ function parseUnit(tokens, unit, typemap) {
continue; continue;
} }
if (tokens.current.kind === 'type-kw') { if (tokens.current.kind === 'type-kw') {
sourceType(modifiers, tokens, package_name, tokens.current.value, unit, resolved_imports, typemap); sourceType(tokens.getLastMLC(), modifiers, tokens, package_name, tokens.current.value, unit, resolved_imports, typemap);
continue; continue;
} }
addproblem(tokens, ParseProblem.Error(tokens.current, 'Type declaration expected')); addproblem(tokens, ParseProblem.Error(tokens.current, 'Type declaration expected'));
@@ -406,7 +408,7 @@ function statement(tokens, mdecls, method, imports, typemap) {
tokens.inc(); tokens.inc();
continue; continue;
case 'type-kw': case 'type-kw':
sourceType(modifiers.splice(0,1e9), tokens, method, tokens.current.value, mdecls, imports, typemap); sourceType('', modifiers.splice(0,1e9), tokens, method, tokens.current.value, mdecls, imports, typemap);
continue; continue;
} }
break; break;
@@ -475,6 +477,7 @@ function statement(tokens, mdecls, method, imports, typemap) {
} }
/** /**
* @param {string} docs
* @param {Token[]} modifiers * @param {Token[]} modifiers
* @param {TokenList} tokens * @param {TokenList} tokens
* @param {Scope|string} scope_or_pkgname * @param {Scope|string} scope_or_pkgname
@@ -483,7 +486,7 @@ function statement(tokens, mdecls, method, imports, typemap) {
* @param {ResolvedImport[]} imports * @param {ResolvedImport[]} imports
* @param {Map<string,CEIType>} typemap * @param {Map<string,CEIType>} typemap
*/ */
function sourceType(modifiers, tokens, scope_or_pkgname, typeKind, owner, imports, typemap) { function sourceType(docs, modifiers, tokens, scope_or_pkgname, typeKind, owner, imports, typemap) {
let package_name, scope; let package_name, scope;
if (typeof scope_or_pkgname === 'string') { if (typeof scope_or_pkgname === 'string') {
package_name = scope_or_pkgname; package_name = scope_or_pkgname;
@@ -493,7 +496,7 @@ function sourceType(modifiers, tokens, scope_or_pkgname, typeKind, owner, import
package_name = scoped_type.packageName; package_name = scoped_type.packageName;
scope = scope_or_pkgname; scope = scope_or_pkgname;
} }
const type = typeDeclaration(package_name, scope, modifiers, typeKind, tokens.current, tokens, imports, typemap); const type = typeDeclaration(package_name, scope, docs, modifiers, typeKind, tokens.current, tokens, imports, typemap);
owner.types.push(type); owner.types.push(type);
if (!(owner instanceof MethodDeclarations)) { if (!(owner instanceof MethodDeclarations)) {
typemap.set(type.shortSignature, type); typemap.set(type.shortSignature, type);
@@ -505,6 +508,7 @@ function sourceType(modifiers, tokens, scope_or_pkgname, typeKind, owner, import
type.implements_types = typeIdentList(tokens, type, imports, typemap); type.implements_types = typeIdentList(tokens, type, imports, typemap);
} }
tokens.expectValue('{'); tokens.expectValue('{');
tokens.clearMLC();
if (type.typeKind === 'enum') { if (type.typeKind === 'enum') {
if (!/[;}]/.test(tokens.current.value)) { if (!/[;}]/.test(tokens.current.value)) {
enumValueList(type, tokens, imports, typemap); enumValueList(type, tokens, imports, typemap);
@@ -537,29 +541,32 @@ function typeBody(type, tokens, owner, imports, typemap) {
switch(tokens.current.kind) { switch(tokens.current.kind) {
case 'ident': case 'ident':
case 'primitive-type': case 'primitive-type':
fmc(modifiers, annotations, [], type, tokens, imports, typemap); fmc(tokens.getLastMLC(), modifiers, annotations, [], type, tokens, imports, typemap);
continue; continue;
case 'type-kw': case 'type-kw':
sourceType(modifiers, tokens, type, tokens.current.value, owner, imports, typemap); sourceType(tokens.getLastMLC(), modifiers, tokens, type, tokens.current.value, owner, imports, typemap);
continue; continue;
} }
switch(tokens.current.value) { switch(tokens.current.value) {
case '<': case '<':
const docs = tokens.getLastMLC();
const type_variables = typeVariableList(type, tokens, type, imports, typemap); const type_variables = typeVariableList(type, tokens, type, imports, typemap);
fmc(modifiers, annotations, type_variables, type, tokens, imports, typemap); fmc(docs, modifiers, annotations, type_variables, type, tokens, imports, typemap);
continue; continue;
case '@': case '@':
tokens.inc().value === 'interface' tokens.inc().value === 'interface'
? sourceType(modifiers, tokens, type, '@interface', owner, imports, typemap) ? sourceType(tokens.getLastMLC(), modifiers, tokens, type, '@interface', owner, imports, typemap)
: annotation(tokens, type, imports, typemap); : annotation(tokens, type, imports, typemap);
continue; continue;
case ';': case ';':
tokens.inc(); tokens.inc();
tokens.clearMLC();
continue; continue;
case '{': case '{':
initer(tokens, type, modifiers.splice(0,1e9)); initer(tokens.getLastMLC(), tokens, type, modifiers.splice(0,1e9));
continue; continue;
case '}': case '}':
tokens.clearMLC();
return; return;
} }
if (!tokens.inc()) { if (!tokens.inc()) {
@@ -569,6 +576,7 @@ function typeBody(type, tokens, owner, imports, typemap) {
} }
/** /**
* @param {string} docs
* @param {Token[]} modifiers * @param {Token[]} modifiers
* @param {SourceAnnotation[]} annotations * @param {SourceAnnotation[]} annotations
* @param {TypeVariable[]} type_vars * @param {TypeVariable[]} type_vars
@@ -577,13 +585,13 @@ function typeBody(type, tokens, owner, imports, typemap) {
* @param {ResolvedImport[]} imports * @param {ResolvedImport[]} imports
* @param {Map<string,CEIType>} typemap * @param {Map<string,CEIType>} typemap
*/ */
function fmc(modifiers, annotations, type_vars, type, tokens, imports, typemap) { function fmc(docs, modifiers, annotations, type_vars, type, tokens, imports, typemap) {
let decl_type_ident = typeIdent(tokens, type, imports, typemap, { no_array_qualifiers: false, type_vars }); let decl_type_ident = typeIdent(tokens, type, imports, typemap, { no_array_qualifiers: false, type_vars });
if (decl_type_ident.resolved.rawTypeSignature === type.rawTypeSignature) { if (decl_type_ident.resolved.rawTypeSignature === type.rawTypeSignature) {
if (tokens.current.value === '(') { if (tokens.current.value === '(') {
// constructor // constructor
const { parameters, throws, body } = methodDeclaration(type_vars, type, tokens, imports, typemap); const { parameters, throws, body } = methodDeclaration(type_vars, type, tokens, imports, typemap);
const ctr = new SourceConstructor(type, type_vars, modifiers, parameters, throws, body); const ctr = new SourceConstructor(type, docs, type_vars, modifiers, parameters, throws, body);
type.constructors.push(ctr); type.constructors.push(ctr);
return; return;
} }
@@ -598,7 +606,7 @@ function fmc(modifiers, annotations, type_vars, type, tokens, imports, typemap)
if (postnamearrdims > 0) { if (postnamearrdims > 0) {
decl_type_ident.resolved = new ArrayType(decl_type_ident.resolved, postnamearrdims); decl_type_ident.resolved = new ArrayType(decl_type_ident.resolved, postnamearrdims);
} }
const method = new SourceMethod(type, type_vars, modifiers, annotations, decl_type_ident, name, parameters, throws, body); const method = new SourceMethod(type, docs, type_vars, modifiers, annotations, decl_type_ident, name, parameters, throws, body);
type.methods.push(method); type.methods.push(method);
} else { } else {
if (name) { if (name) {
@@ -606,7 +614,7 @@ function fmc(modifiers, annotations, type_vars, type, tokens, imports, typemap)
addproblem(tokens, ParseProblem.Error(tokens.current, `Fields cannot declare type variables`)); addproblem(tokens, ParseProblem.Error(tokens.current, `Fields cannot declare type variables`));
} }
const locals = var_ident_list(modifiers, decl_type_ident, name, tokens, new MethodDeclarations(), type, imports, typemap); const locals = var_ident_list(modifiers, decl_type_ident, name, tokens, new MethodDeclarations(), type, imports, typemap);
const fields = locals.map(l => new SourceField(type, modifiers, l.typeIdent, l.decltoken, l.init)); const fields = locals.map(l => new SourceField(type, docs, modifiers, l.typeIdent, l.decltoken, l.init));
type.fields.push(...fields); type.fields.push(...fields);
} }
semicolon(tokens); semicolon(tokens);
@@ -614,13 +622,13 @@ function fmc(modifiers, annotations, type_vars, type, tokens, imports, typemap)
} }
/** /**
* * @param {string} docs
* @param {TokenList} tokens * @param {TokenList} tokens
* @param {SourceType} type * @param {SourceType} type
* @param {Token[]} modifiers * @param {Token[]} modifiers
*/ */
function initer(tokens, type, modifiers) { function initer(docs, tokens, type, modifiers) {
const i = new SourceInitialiser(type, modifiers, skipBody(tokens)); const i = new SourceInitialiser(type, docs, modifiers, skipBody(tokens));
type.initers.push(i); type.initers.push(i);
} }
@@ -671,6 +679,7 @@ function annotation(tokens, scope, imports, typemap) {
/** /**
* @param {string} package_name * @param {string} package_name
* @param {Scope} scope * @param {Scope} scope
* @param {string} docs
* @param {Token[]} modifiers * @param {Token[]} modifiers
* @param {string} typeKind * @param {string} typeKind
* @param {Token} kind_token * @param {Token} kind_token
@@ -678,7 +687,7 @@ function annotation(tokens, scope, imports, typemap) {
* @param {ResolvedImport[]} imports * @param {ResolvedImport[]} imports
* @param {Map<string,CEIType>} typemap * @param {Map<string,CEIType>} typemap
*/ */
function typeDeclaration(package_name, scope, modifiers, typeKind, kind_token, tokens, imports, typemap) { function typeDeclaration(package_name, scope, docs, modifiers, typeKind, kind_token, tokens, imports, typemap) {
let name = tokens.inc(); let name = tokens.inc();
if (!tokens.isKind('ident')) { if (!tokens.isKind('ident')) {
name = null; name = null;
@@ -694,7 +703,7 @@ function typeDeclaration(package_name, scope, modifiers, typeKind, kind_token, t
// update the missing parts // update the missing parts
type.setModifierTokens(modifiers); type.setModifierTokens(modifiers);
} else { } else {
type = new SourceType(package_name, scope, '', modifiers, typeKind, kind_token, name, typemap); type = new SourceType(package_name, scope, docs, modifiers, typeKind, kind_token, name, typemap);
} }
type.typeVariables = tokens.current.value === '<' type.typeVariables = tokens.current.value === '<'
? typeVariableList(type, tokens, scope, imports, typemap) ? typeVariableList(type, tokens, scope, imports, typemap)
@@ -826,6 +835,7 @@ function parameterDeclaration(type_vars, owner, tokens, imports, typemap) {
*/ */
function enumValueList(type, tokens, imports, typemap) { function enumValueList(type, tokens, imports, typemap) {
for (;;) { for (;;) {
const docs = tokens.getLastMLC();
const ident = tokens.getIfKind('ident'); const ident = tokens.getIfKind('ident');
if (!ident) { if (!ident) {
addproblem(tokens, ParseProblem.Error(tokens.current, `Identifier expected`)); addproblem(tokens, ParseProblem.Error(tokens.current, `Identifier expected`));
@@ -850,7 +860,7 @@ function enumValueList(type, tokens, imports, typemap) {
} else tokens.inc(); } else tokens.inc();
} }
} }
type.addEnumValue(ident, ctr_args, anonymousEnumType); type.addEnumValue(docs, ident, ctr_args, anonymousEnumType);
if (tokens.isValue(',')) { if (tokens.isValue(',')) {
continue; continue;
} }

View File

@@ -109,13 +109,13 @@ class SourceType extends CEIType {
} }
/** /**
* * @param {string} docs
* @param {Token} ident * @param {Token} ident
* @param {ResolvedIdent[]} ctr_args * @param {ResolvedIdent[]} ctr_args
* @param {SourceType} anonymousType * @param {SourceType} anonymousType
*/ */
addEnumValue(ident, ctr_args, anonymousType) { addEnumValue(docs, ident, ctr_args, anonymousType) {
this.enumValues.push(new SourceEnumValue(this, ident, ctr_args, anonymousType)); this.enumValues.push(new SourceEnumValue(this, docs, ident, ctr_args, anonymousType));
} }
/** /**
@@ -286,12 +286,13 @@ class SpecialisedSourceType extends CEIType {
class SourceEnumValue extends Field { class SourceEnumValue extends Field {
/** /**
* @param {SourceType} owner * @param {SourceType} owner
* @param {string} docs
* @param {Token} ident * @param {Token} ident
* @param {ResolvedIdent[]} ctr_args * @param {ResolvedIdent[]} ctr_args
* @param {SourceType} anonymousType * @param {SourceType} anonymousType
*/ */
constructor(owner, ident, ctr_args, anonymousType) { constructor(owner, docs, ident, ctr_args, anonymousType) {
super(['public','static','final'], ''); super(['public','static','final'], docs);
this.owner = owner; this.owner = owner;
this.ident = ident; this.ident = ident;
this.value = ctr_args; this.value = ctr_args;
@@ -326,13 +327,14 @@ class SourceTypeIdent {
class SourceField extends Field { class SourceField extends Field {
/** /**
* @param {SourceType} owner * @param {SourceType} owner
* @param {string} docs
* @param {Token[]} modifiers * @param {Token[]} modifiers
* @param {SourceTypeIdent} field_type_ident * @param {SourceTypeIdent} field_type_ident
* @param {Token} name_token * @param {Token} name_token
* @param {ResolvedIdent} init * @param {ResolvedIdent} init
*/ */
constructor(owner, modifiers, field_type_ident, name_token, init) { constructor(owner, docs, modifiers, field_type_ident, name_token, init) {
super(modifiers.map(m => m.value), ''); super(modifiers.map(m => m.value), docs);
this.owner = owner; this.owner = owner;
this.modifierTokens = modifiers; this.modifierTokens = modifiers;
this.fieldTypeIdent = field_type_ident; this.fieldTypeIdent = field_type_ident;
@@ -352,14 +354,15 @@ class SourceField extends Field {
class SourceConstructor extends Constructor { class SourceConstructor extends Constructor {
/** /**
* @param {SourceType} owner * @param {SourceType} owner
* @param {string} docs
* @param {TypeVariable[]} type_vars * @param {TypeVariable[]} type_vars
* @param {Token[]} modifiers * @param {Token[]} modifiers
* @param {SourceParameter[]} parameters * @param {SourceParameter[]} parameters
* @param {JavaType[]} throws * @param {JavaType[]} throws
* @param {Token[]} body * @param {Token[]} body
*/ */
constructor(owner, type_vars, modifiers, parameters, throws, body) { constructor(owner, docs, type_vars, modifiers, parameters, throws, body) {
super(owner, modifiers.map(m => m.value), ''); super(owner, modifiers.map(m => m.value), docs);
this.owner = owner; this.owner = owner;
this.typeVars = type_vars; this.typeVars = type_vars;
this.modifierTokens = modifiers; this.modifierTokens = modifiers;
@@ -392,6 +395,7 @@ class SourceConstructor extends Constructor {
class SourceMethod extends Method { class SourceMethod extends Method {
/** /**
* @param {SourceType} owner * @param {SourceType} owner
* @param {string} docs
* @param {TypeVariable[]} type_vars * @param {TypeVariable[]} type_vars
* @param {Token[]} modifiers * @param {Token[]} modifiers
* @param {SourceAnnotation[]} annotations * @param {SourceAnnotation[]} annotations
@@ -401,8 +405,8 @@ class SourceMethod extends Method {
* @param {JavaType[]} throws * @param {JavaType[]} throws
* @param {Token[]} body * @param {Token[]} body
*/ */
constructor(owner, type_vars, modifiers, annotations, method_type_ident, name_token, parameters, throws, body) { constructor(owner, docs, type_vars, modifiers, annotations, method_type_ident, name_token, parameters, throws, body) {
super(owner, name_token ? name_token.value : '', modifiers.map(m => m.value), ''); super(owner, name_token ? name_token.value : '', modifiers.map(m => m.value), docs);
this.annotations = annotations; this.annotations = annotations;
this.owner = owner; this.owner = owner;
this.typeVars = type_vars; this.typeVars = type_vars;
@@ -442,11 +446,12 @@ class SourceMethod extends Method {
class SourceInitialiser extends MethodBase { class SourceInitialiser extends MethodBase {
/** /**
* @param {SourceType} owner * @param {SourceType} owner
* @param {string} docs
* @param {Token[]} modifiers * @param {Token[]} modifiers
* @param {Token[]} body * @param {Token[]} body
*/ */
constructor(owner, modifiers, body) { constructor(owner, docs, modifiers, body) {
super(owner, modifiers.map(m => m.value), ''); super(owner, modifiers.map(m => m.value), docs);
/** @type {SourceType} */ /** @type {SourceType} */
this.owner = owner; this.owner = owner;
this.modifierTokens = modifiers; this.modifierTokens = modifiers;

View File

@@ -389,8 +389,12 @@ function getTypedNameCompletion(typemap, type_signature, opts, typelist) {
} }
getTypeInheritanceList(type).forEach((t,idx) => { getTypeInheritanceList(type).forEach((t,idx) => {
t.fields.sort(sortByName).filter(f => shouldInclude(f.modifiers, t)).forEach(f => fields.set(f.name, {f, t, sortText: `${idx+100}${f.name}`})); t.fields.sort(sortByName)
t.methods.sort(sortByName).filter(f => shouldInclude(f.modifiers, t)).forEach(m => methods.set(m.methodSignature, {m, t, sortText: `${idx+100}${m.name}`})); .filter(f => shouldInclude(f.modifiers, t))
.forEach(f => fields.set(f.name, {f, t, sortText: `${idx+100}${f.name}`}));
t.methods.sort(sortByName)
.filter(f => shouldInclude(f.modifiers, t))
.forEach(m => methods.set(`${m.name}${m.methodSignature}`, {m, t, sortText: `${idx+100}${m.name}`}));
}); });
const subtype_search = type.shortSignature + '$'; const subtype_search = type.shortSignature + '$';
@@ -604,13 +608,13 @@ connection.onCompletionResolve(
*/ */
(item) => { (item) => {
item.detail = item.documentation = ''; item.detail = item.documentation = '';
if (androidLibrary instanceof Promise) { if (!parsed || !parsed.typemap) {
return item; return item;
} }
if (typeof item.data !== 'object') { if (typeof item.data !== 'object') {
return item; return item;
} }
const t = androidLibrary.get(item.data.type); const t = parsed.typemap.get(item.data.type);
const field = t && t.fields[item.data.fidx]; const field = t && t.fields[item.data.fidx];
const method = t && t.methods[item.data.midx]; const method = t && t.methods[item.data.midx];
if (!t) { if (!t) {
@@ -635,8 +639,9 @@ connection.onCompletionResolve(
kind: 'markdown', kind: 'markdown',
value: `${header}\n\n${ value: `${header}\n\n${
documentation documentation
.replace(/(<p ?.*?>)|(<\/?i>|<\/?em>)|(<\/?b>|<\/?strong>|<\/?dt>)|(<\/?tt>)|(<\/?code>|<\/?pre>)|(\{@link.+?\}|\{@code.+?\})|(<li>)|(<a href="\{@docRoot\}.*?">.+?<\/a>)|(<h\d>)|<\/?dd ?.*?>|<\/p ?.*?>|<\/h\d ?.*?>|<\/?div ?.*?>|<\/?[uo]l ?.*?>/gim, (_,p,i,b,tt,c,lc,li,a,h) => { .replace(/(^\/\*+|(?<=\n)[ \t]*\*+\/?|\*+\/)|(<p ?.*?>)|(<\/?i>|<\/?em>)|(<\/?b>|<\/?strong>|<\/?dt>)|(<\/?tt>)|(<\/?code>|<\/?pre>)|(\{@link.+?\}|\{@code.+?\})|(<li>)|(<a href="\{@docRoot\}.*?">.+?<\/a>)|(<h\d>)|<\/?dd ?.*?>|<\/p ?.*?>|<\/h\d ?.*?>|<\/?div ?.*?>|<\/?[uo]l ?.*?>/gim, (_,cmt,p,i,b,tt,c,lc,li,a,h) => {
return p ? '\n\n' return cmt ? ''
: p ? '\n\n'
: i ? '*' : i ? '*'
: b ? '**' : b ? '**'
: tt ? '`' : tt ? '`'