From 9d2edca24922db4e630066d86f2ff1f99f74f7e4 Mon Sep 17 00:00:00 2001 From: Neil Brayfield Date: Fri, 14 Jan 2022 09:29:37 +0000 Subject: [PATCH 1/4] Update README.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index ec78cb6..bce1eed 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,6 @@ We now have a set of unit tests and some full coverage on the parsing of signatu ## Features * Completion snippet after `/**` above a class, function, class property -* Continuation of DocBlock when pressing enter when in a DocBlock * Completion of DocBlock tags such as `@param`, `@return`, `@throws` * Inferring of param and return types from signatures * Configuration of template for each type of docblock completion From 806d1ff94040d8ea92ed3631d7bb0dfaf3bd71dc Mon Sep 17 00:00:00 2001 From: Neil Brayfield Date: Fri, 11 Feb 2022 00:38:14 +0000 Subject: [PATCH 2/4] Fix coverage reports not firing (#219) --- CHANGELOG.md | 1 + package.json | 3 ++- test/index.ts | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 58dd42b..0ac856b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to the "php-docblocker" extension will be documented in this file. ## [Unreleased] +- Fix issue with coverage reports not firing ## [2.6.1] - 2021-10-12 - Fix double start delimeter when vscode setting `editor.autoClosingBrackets` is set to `never` diff --git a/package.json b/package.json index e5a8567..e601d4a 100644 --- a/package.json +++ b/package.json @@ -142,6 +142,7 @@ } }, "scripts": { + "clean": "rm -rf ./coverage/* ./.nyc_output ./out", "verify-pat": "vsce verify-pat", "vscode:prepublish": "npm run -S esbuild-base -- --minify", "lint": "tslint -p ./", @@ -166,7 +167,7 @@ "esbuild": "^0.12.29", "markdown-table-ts": "^1.0.3", "mocha": "^9.1.1", - "nyc": "^15.0.0", + "nyc": "^15.1.0", "typescript": "^3.9.10", "vsce": "^1.100.0", "vscode-test": "^1.6.1" diff --git a/test/index.ts b/test/index.ts index 201b053..36f55d0 100644 --- a/test/index.ts +++ b/test/index.ts @@ -53,6 +53,6 @@ export async function run(): Promise { }); } finally { nyc.writeCoverageFile(); - nyc.report(); + await nyc.report(); } } From e81a78822c4a163468027d31cdc03d943f4bf324 Mon Sep 17 00:00:00 2001 From: tianyiw Date: Fri, 11 Feb 2022 08:54:24 +0800 Subject: [PATCH 3/4] Fix full class names in use statements (#215) --- CHANGELOG.md | 3 ++ src/util/TypeUtil.ts | 68 ++++++++++++++++++++++++++++++++----- test/TypeUtil.test.ts | 50 +++++++++++++++++++++++++++ test/fixtures/namespace.php | 13 +++++++ 4 files changed, 125 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ac856b..51d9747 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,9 @@ All notable changes to the "php-docblocker" extension will be documented in this file. ## [Unreleased] +- Fixed fully qualifies class namespace detection can be incorrect with _ [#214](https://github.com/neild3r/vscode-php-docblocker/issues/214) +- Supported fully qualifies class namespace use with bracket `use some\namespace\{ ClassA, ClassB, ... }` +- Supported fully qualifies class namespace use with comma `use some\namespace\ClassA, some\namespace\ClassB, ...` - Fix issue with coverage reports not firing ## [2.6.1] - 2021-10-12 diff --git a/src/util/TypeUtil.ts b/src/util/TypeUtil.ts index cac44c5..edb59d4 100644 --- a/src/util/TypeUtil.ts +++ b/src/util/TypeUtil.ts @@ -1,4 +1,4 @@ -import { workspace, TextEditor, Range, Position, TextDocument } from "vscode"; +import { TextEditor, Range, Position, TextDocument } from "vscode"; import Config from "./config"; /** @@ -50,28 +50,78 @@ export default class TypeUtil { */ public getFullyQualifiedType(type:string, head:string):string { + if (!head) { + return type; + } if (!Config.instance.get('qualifyClassNames')) { return type; } - let useEx = new RegExp("use\\s+([^ ]*?)((?:\\s+as\\s+))?("+type+");", 'gm'); - let full = useEx.exec(head); + let useEx = /[\s;]?use\s+(?:(const|function)\s*)?([\s\S]*?)\s*;/gmi; + let exec: RegExpExecArray; + while (exec = useEx.exec(head)) { + let isConstOrFunc = exec[1]; + let use = exec[2]; - if (full != null && full[3] == type) { - if (full[1].charAt(0) != '\\') { - full[1] = '\\' + full[1]; + if (isConstOrFunc) { + continue; } - if (full[2] != null) { - return full[1]; + let clazz = this.getClassesFromUse(use)[type]; + if (clazz === undefined) { + continue; } - return full[1] + type; + if (clazz.charAt(0) != '\\') { + clazz = '\\' + clazz; + } + return clazz; } return type; } + /** + * Returns the classes from the use + * + * @param use + * @returns + */ + public getClassesFromUse(use: string): { [index: string]: string } { + let namespace: string; + let classes: string[]; + let hasBracket = use.indexOf('{') !== -1; + if (hasBracket) { + let bracketBegin = use.indexOf('{'); + let bracketEnd = (use + '}').indexOf('}'); + namespace = use.substring(0, bracketBegin).trim(); + classes = use.substring(bracketBegin + 1, bracketEnd).split(','); + } else { + namespace = ''; + classes = use.split(','); + } + + var results: { [index: string]: string } = {}; + for (let index = 0; index < classes.length; index++) { + let alias: string; + let clazz = classes[index].trim(); + if (clazz === '') { + continue; + } + + clazz = namespace + clazz; + + [clazz, alias] = clazz.split(/\s+as\s+/gmi, 2); + + if (alias === undefined || alias === '') { + alias = clazz.substring(clazz.lastIndexOf('\\') + 1); + } + + results[alias] = clazz; + } + return results; + } + /** * Returns the user configuration based name for the given type * diff --git a/test/TypeUtil.test.ts b/test/TypeUtil.test.ts index 95adffa..0229212 100644 --- a/test/TypeUtil.test.ts +++ b/test/TypeUtil.test.ts @@ -23,6 +23,14 @@ suite("TypeUtil tests: ", () => { }); }); + test("Test the classes returned from use is correct", () => { + let type = new TypeUtil; + assert.deepEqual(type.getClassesFromUse('ClassA'), { 'ClassA': 'ClassA' }); + assert.deepEqual(type.getClassesFromUse('ClassB as B'), { 'B': 'ClassB' }); + assert.deepEqual(type.getClassesFromUse('ClassA, namespace\\ClassB as B'), { 'ClassA': 'ClassA', 'B': 'namespace\\ClassB' }); + assert.deepEqual(type.getClassesFromUse('namespace\\{ ClassA, ClassB as B }'), { 'ClassA': 'namespace\\ClassA', 'B': 'namespace\\ClassB' }); + }); + test("Ensure typehint is not mismatched", () => { let type = new TypeUtil; Helper.setConfig({qualifyClassNames: true}); @@ -47,6 +55,48 @@ suite("TypeUtil tests: ", () => { assert.equal(type.getFullyQualifiedType('BaseExample', head), '\\App\\Test\\Model\\Example'); }); + test("Fully qualify typehint from namespace use with _", () => { + let type = new TypeUtil; + Helper.setConfig({qualifyClassNames: true}); + assert.equal(type.getFullyQualifiedType('ExampleInterface', head), '\\App\\Example\\ExampleInterface'); + }); + + test("Fully qualify typehint from namespace use with bracket", () => { + let type = new TypeUtil; + Helper.setConfig({qualifyClassNames: true}); + assert.equal(type.getFullyQualifiedType('ClassA', head), '\\some\\namespace\\ClassA'); + }); + + test("Fully qualify typehint from namespace use with bracket+alias", () => { + let type = new TypeUtil; + Helper.setConfig({qualifyClassNames: true}); + assert.equal(type.getFullyQualifiedType('ClassB_alias', head), '\\some\\namespace\\ClassB'); + }); + + test("Fully qualify typehint from namespace use with comma", () => { + let type = new TypeUtil; + Helper.setConfig({qualifyClassNames: true}); + assert.equal(type.getFullyQualifiedType('ClassD', head), '\\some\\namespace\\ClassD'); + }); + + test("Fully qualify typehint from namespace use with comma+alias", () => { + let type = new TypeUtil; + Helper.setConfig({qualifyClassNames: true}); + assert.equal(type.getFullyQualifiedType('ClassE_alias', head), '\\some\\namespace\\ClassE'); + }); + + test("Fully qualify typehint from namespace use const", () => { + let type = new TypeUtil; + Helper.setConfig({qualifyClassNames: true}); + assert.equal(type.getFullyQualifiedType('myconst', head), 'myconst'); + }); + + test("Fully qualify typehint from namespace use function", () => { + let type = new TypeUtil; + Helper.setConfig({qualifyClassNames: true}); + assert.equal(type.getFullyQualifiedType('myfunction', head), 'myfunction'); + }); + test("With default settings the integer type formatted is integer", () => { let type = new TypeUtil; assert.equal(type.getFormattedTypeByName('int'), 'integer'); diff --git a/test/fixtures/namespace.php b/test/fixtures/namespace.php index 21c0637..30eb49c 100644 --- a/test/fixtures/namespace.php +++ b/test/fixtures/namespace.php @@ -12,6 +12,19 @@ use Psr\Log\LoggerInterface; use Psr\Log\InvalidArgumentException; +use Example_ExampleInterface; +use App\Example\ExampleInterface; + +use some\namespace\{ + ClassA, + ClassB as ClassB_alias, +}; +use some\namespace\ClassD, + some\namespace\ClassE as ClassE_alias; + +use const some\namespace\myconst; +use function some\namespace\myfunction; + /** * Example class here */ From 7ae106341bc7c011fccb7378de463b91778b9bdc Mon Sep 17 00:00:00 2001 From: Neil Brayfield Date: Fri, 11 Feb 2022 00:56:27 +0000 Subject: [PATCH 4/4] Allow configuration of default type (#212) --- CHANGELOG.md | 1 + README.md | 1 + package.json | 5 +++++ src/block/function.ts | 2 +- src/block/property.ts | 2 +- src/util/TypeUtil.ts | 20 +++++++++++++++----- test/TypeUtil.test.ts | 5 +++++ 7 files changed, 29 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 51d9747..e724910 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to the "php-docblocker" extension will be documented in this file. ## [Unreleased] +- Allow configuration of default type - Fixed fully qualifies class namespace detection can be incorrect with _ [#214](https://github.com/neild3r/vscode-php-docblocker/issues/214) - Supported fully qualifies class namespace use with bracket `use some\namespace\{ ClassA, ClassB, ... }` - Supported fully qualifies class namespace use with comma `use some\namespace\ClassA, some\namespace\ClassB, ...` diff --git a/README.md b/README.md index bce1eed..ddebd7a 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ This extension contributes the following settings: * `php-docblocker.gap`: set to `false` to disable the gap between the description and tags * `php-docblocker.returnGap`: set to `true` to add a gap between the param and return tags * `php-docblocker.returnVoid`: set to `false` to turn off the automatic void return type when it can't be determined +* `php-docblocker.defaultType`: default type to use if a type wasn't able to to be determined * `php-docblocker.extra`: an array of extra tags to add to each DocBlock (These can include tabstops and snippet variables) * `php-docblocker.useShortNames`: Whether we should use short type names. e.g. bool or boolean * `php-docblocker.qualifyClassNames`: When adding type hints for class names search namespace use statements and qualify the class diff --git a/package.json b/package.json index e601d4a..a3794b8 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,11 @@ "default": false, "description": "If there should be a gap between params and return" }, + "php-docblocker.defaultType": { + "type": "string", + "default": "[type]", + "description": "Default type to use if a type wasn't able to be detected" + }, "php-docblocker.returnVoid": { "type": "boolean", "default": true, diff --git a/src/block/function.ts b/src/block/function.ts index 743b30e..c91111d 100644 --- a/src/block/function.ts +++ b/src/block/function.ts @@ -41,7 +41,7 @@ export default class FunctionBlock extends Block for (let index = 0; index < args.length; index++) { let arg:string = args[index]; let parts:string[] = arg.match(/^\s*(?:(?:public|protected|private)\s+)?(\?)?\s*([A-Za-z0-9|_\\]+)?\s*\&?((?:[.]{3})?\$[A-Za-z0-9_]+)\s*\=?\s*(.*)\s*/im); - var type:string = '[type]'; + var type:string = TypeUtil.instance.getDefaultType(); if (parts[2] != null) { type = TypeUtil.instance.getResolvedTypeHints(parts[2], head); diff --git a/src/block/property.ts b/src/block/property.ts index 0beaf47..f29fca5 100644 --- a/src/block/property.ts +++ b/src/block/property.ts @@ -43,7 +43,7 @@ export default class Property extends Block } else if (params[6]) { doc.var = TypeUtil.instance.getTypeFromValue(params[6]); } else { - doc.var = '[type]'; + doc.var = TypeUtil.instance.getDefaultType(); } return doc; diff --git a/src/util/TypeUtil.ts b/src/util/TypeUtil.ts index edb59d4..26611ae 100644 --- a/src/util/TypeUtil.ts +++ b/src/util/TypeUtil.ts @@ -23,15 +23,15 @@ export default class TypeUtil { /** * Resolve a type string that may contain union types - * - * @param {string} types - * @param {string} head + * + * @param {string} types + * @param {string} head * @returns {string} */ public getResolvedTypeHints(types:string, head:string = null): string { let union:string[] = types.split("|"); - + for (let index = 0; index < union.length; index++) { union[index] = this.getFullyQualifiedType(union[index], head); union[index] = this.getFormattedTypeByName(union[index]); @@ -182,6 +182,16 @@ export default class TypeUtil { return 'array'; } - return '[type]'; + return this.getDefaultType(); + } + + /** + * Get the default type + * + * @returns {string} + */ + public getDefaultType(): string + { + return Config.instance.get('defaultType'); } } diff --git a/test/TypeUtil.test.ts b/test/TypeUtil.test.ts index 0229212..6035765 100644 --- a/test/TypeUtil.test.ts +++ b/test/TypeUtil.test.ts @@ -124,6 +124,11 @@ suite("TypeUtil tests: ", () => { assert.equal(type.getFormattedTypeByName('helloWorld'), 'helloWorld'); }); + test("Undefined type uses config", () => { + let type = new TypeUtil; + Helper.setConfig({defaultType: 'mixed'}); + assert.equal(type.getDefaultType(), 'mixed'); + }); }); suite("TypeUtil issue test: ", () => {