diff --git a/src/getset.ts b/src/getset.ts index a910318..f6fc25a 100644 --- a/src/getset.ts +++ b/src/getset.ts @@ -1,5 +1,8 @@ import * as vscode from 'vscode'; +import { Variable } from './variable'; +import { findCtorPrivateParams } from './regexutil'; + export enum EType { GETTER, SETTER, BOTH, CONSTRUCTOR } @@ -130,11 +133,12 @@ export function generateClassesList(type: EType): IClass[] { let _class = getClass(classes, brackets.name); const matches = { privateDef: line.text.match(matchers.privateDef), + ctorParams: findCtorPrivateParams(line.text), getMethod: line.text.match(matchers.getMethod), setMethod: line.text.match(matchers.setMethod) }; if (_class && - (matches.getMethod || matches.privateDef || matches.setMethod)) { + (matches.getMethod || matches.privateDef || matches.setMethod || matches.ctorParams)) { // push the found items into the approriate containers if (matches.privateDef) { _class.vars.push({ @@ -143,6 +147,16 @@ export function generateClassesList(type: EType): IClass[] { typeName: matches.privateDef[2] }); } + // add the private constructor parameters + if (matches.ctorParams.length !== 0) { + for (const param of matches.ctorParams) { + _class.vars.push({ + name: param.name, + figure: publicName(param.name), + typeName: param.type + }); + } + } if (matches.getMethod) _class.getters.push(matches.getMethod[1]); if (matches.setMethod) _class.setters.push(matches.setMethod[1]); } @@ -172,7 +186,8 @@ export function generateClassesList(type: EType): IClass[] { break; } } - } else if (type == EType.SETTER || type == EType.BOTH) { + } + if (type == EType.SETTER || type == EType.BOTH) { for (let j = 0; j < _class.setters.length; j++) { if (_class.vars[i].figure.toLowerCase() === _class.setters[j].toLowerCase()) { _class.vars.splice(i, 1); diff --git a/src/regexutil.ts b/src/regexutil.ts new file mode 100644 index 0000000..13085d5 --- /dev/null +++ b/src/regexutil.ts @@ -0,0 +1,26 @@ +import { Variable } from './variable'; + +const matchers = { + ctorDef: /\s*constructor\(\s*([^)]+?)\s*\)/, + ctorParam: /(?:public)?(private)?\s*([a-zA-Z_$][\w$]+)\s*\??:\s*([\.\w$]+<(?:[\.\[\]\w$\s]+,?)+>|[\.\[\]\w$]+)[^,]*,?\s*/y, +}; + +/** + * Finds private constructor parameters and returns them as {@link Variable[]}. + * @param line The line of text in which to try to find private constructor parameters. + */ +export function findCtorPrivateParams(line: string): Variable[] { + const params: Variable[] = []; + let ctor: RegExpMatchArray; + // First match the constructor, then match each param + if (ctor = line.match(matchers.ctorDef)) { + let param: RegExpMatchArray; + while (param = ctor[1].match(matchers.ctorParam)) { + // Check if the param is private + if (param[1]) { + params.push(new Variable(param[2], param[3])); + } + } + } + return params; +} \ No newline at end of file diff --git a/src/variable.ts b/src/variable.ts new file mode 100644 index 0000000..d8bc8d9 --- /dev/null +++ b/src/variable.ts @@ -0,0 +1,21 @@ +export class Variable { + constructor(private _name: string, private _type: string) { + + } + + public get name(): string { + return this._name; + } + + public set name(value: string) { + this._name = value; + } + + public get type(): string { + return this._type; + } + + public set type(value: string) { + this._type = value; + } +} \ No newline at end of file diff --git a/test/regexutil.test.ts b/test/regexutil.test.ts new file mode 100644 index 0000000..2d8e60a --- /dev/null +++ b/test/regexutil.test.ts @@ -0,0 +1,65 @@ +import * as assert from 'assert'; + +import * as regexutil from '../src/regexutil'; + +suite("Regex Util Constructor Tests", () => { + + test("Does match private params in constructor", () => { + const matches = regexutil.findCtorPrivateParams("constructor(private _name: string, private _age: number, _gender: string)"); + assert.equal(2, matches.length); + assert.equal("_name", matches[0].name); + assert.equal("string", matches[0].type); + assert.equal("_age", matches[1].name); + assert.equal("number", matches[1].type); + }); + + test("Does ignore public and default params", () => { + const matches = regexutil.findCtorPrivateParams("constructor(public _name: string, private _age: number, _gender: string)"); + assert.equal(1, matches.length); + assert.equal("_age", matches[0].name); + assert.equal("number", matches[0].type); + }); + + test("Does match private params after public and default params", () => { + const matches = regexutil.findCtorPrivateParams("constructor(public test: boolean, private _name: string, _gender: string, private _age: number)"); + assert.equal(2, matches.length); + assert.equal("_name", matches[0].name); + assert.equal("string", matches[0].type); + assert.equal("_age", matches[1].name); + assert.equal("number", matches[1].type); + }); + + test("Can match generic types with more than one argument", () => { + const matches = regexutil.findCtorPrivateParams("constructor(private _test1: Type, private _test2: Int)"); + assert.equal(2, matches.length); + assert.equal("_test1", matches[0].name); + assert.equal("Type", matches[0].type); + assert.equal("_test2", matches[1].name); + assert.equal("Int", matches[1].type); + }); + + test("Can match params with default value", () => { + const matches = regexutil.findCtorPrivateParams("constructor(private _test1: string = \"test1\", variable: string = 'abc', private _test2: number = 7)"); + assert.equal(2, matches.length); + assert.equal("_test1", matches[0].name); + assert.equal("string", matches[0].type); + assert.equal("_test2", matches[1].name); + assert.equal("number", matches[1].type); + }); + + test("Does not match if not constructor", () => { + const matches = regexutil.findCtorPrivateParams("myMethod(private _name: string, private _age: number)"); + assert.equal(0, matches.length); + }); + + test("Can match zero private params in constructor", () => { + const matches = regexutil.findCtorPrivateParams("constructor(name: string, age: number)"); + assert.equal(0, matches.length); + }); + + test("Does not match if empty constructor", () => { + const matches = regexutil.findCtorPrivateParams("constructor()"); + assert.equal(0, matches.length); + }); + +}); \ No newline at end of file