diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..a5f550d --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,20 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Run Extension", + "type": "extensionHost", + "request": "launch", + "runtimeExecutable": "${execPath}", + "args": [ + "--extensionDevelopmentPath=${workspaceFolder}" + ], + "outFiles": [ + "${workspaceFolder}/out/**/*.js" + ] + } + ] +} diff --git a/src/Property.ts b/src/Property.ts index 3014d83..718dea5 100644 --- a/src/Property.ts +++ b/src/Property.ts @@ -3,8 +3,12 @@ import * as vscode from 'vscode'; +// remove the ` * ` at the begining of the line +const leftTrimLine = (line: string) => line.replace(/^\s*\*\s*/, ''); + export default class Property { private description: string = null; + private descriptionLines: string[] = []; private indentation: string; private name: string; private type: string = null; @@ -50,15 +54,14 @@ export default class Property { } for (let line = previousLineNumber - 1; line > 0; line--) { - // Everything found - if (property.name && property.type && property.description) { - break; - } - const text = editor.document.lineAt(line).text; // Reached the end of the doc block if (text.includes('/**') || !text.includes('*')) { + if (!property.description && property.descriptionLines.length > 0) { + property.description = property.stringifyDescriptionLines(); + } + break; } @@ -70,22 +73,22 @@ export default class Property { const varPosition = lineParts.indexOf('@var'); // Found @var line - if (-1 !== varPosition) { + if (varPosition !== -1) { property.setType(lineParts[varPosition + 1]); var descriptionParts = lineParts.slice(varPosition + 2); if (descriptionParts.length) { - property.description = descriptionParts.join(` `); + // sync the `description` string with the `descriptionLines` array + property.description = property.descriptionLines[0] = descriptionParts.join(` `); } continue; } - const posibleDescription = lineParts.join(` `); - - if (posibleDescription[0] !== '@') { - property.description = posibleDescription; + let possibleDescription = lineParts.join(` `); + if (possibleDescription[0] !== '@') { + property.descriptionLines = [text, ...property.descriptionLines]; } } @@ -105,13 +108,39 @@ export default class Property { } generateMethodName(prefix : string) : string { - return prefix + this.name.charAt(0).toUpperCase() + this.name.substring(1); + const inflectedPropertyName = (this.name.charAt(0) === '_') + ? this.name.charAt(1).toUpperCase() + this.name.substring(2) + : this.name.charAt(0).toUpperCase() + this.name.substring(1); + + return prefix + inflectedPropertyName; } getDescription() : string { return this.description; } + getArgumentDescription(): string|null { + const linesCount = this.descriptionLines.length; + if (linesCount === 0) { + return null; + } + + const firstLine = leftTrimLine(this.descriptionLines[0]); + + // single line description + if (linesCount === 1) { + return firstLine; + } + + // multiline description, check if the second line isn't empty + const secondLine = leftTrimLine(this.descriptionLines[1]); + if (secondLine !== '') { + return firstLine + '\n' + this.descriptionLines[1]; + } + + return firstLine; + } + getIndentation() : string { return this.indentation; } @@ -120,6 +149,10 @@ export default class Property { return this.name; } + getArgumentName(): string { + return this.name.charAt(0) === '_' ? this.name.slice(1) : this.name; + } + getterDescription() : string { return this.generateMethodDescription('Get '); } @@ -155,4 +188,26 @@ export default class Property { this.typeHint = type; } } + + stringifyDescriptionLines(): string|null { + if (this.descriptionLines.length === 0) { + return null; + } + + // remove the ` * ` from the first description line. + const firstLine = leftTrimLine(this.descriptionLines[0]); + + // it can be a single line description too ! + if (this.descriptionLines.length === 1) { + return firstLine; + } + + // remove the last line if it's empty + const lastLine = leftTrimLine(this.descriptionLines[this.descriptionLines.length -1]); + const descriptionLines = (lastLine === '') ? this.descriptionLines.slice(0, -1) : this.descriptionLines; + // skip the first line, we will use the left trimmed version(without ` * `) + const [head, ...tail] = descriptionLines; + + return firstLine + '\n' + tail.join('\n'); + } } diff --git a/src/extension.ts b/src/extension.ts index ed66c6a..eda13ac 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -140,7 +140,8 @@ class Resolver { setterTemplate(prop: Property) { const name = prop.getName(); - const description = prop.getDescription(); + const argumentName = prop.getArgumentName(); + const argumentDescription = prop.getArgumentDescription(); const tab = prop.getIndentation(); const type = prop.getType(); const typeHint = prop.getTypeHint(); @@ -161,13 +162,13 @@ class Resolver { + tab + `/**\n` + tab + ` * ` + prop.setterDescription() + `\n` + (type ? tab + ` *\n` : ``) - + (type ? tab + ` * @param` + spacesAfterParam + type + spacesAfterParamVar + `$` + name + (description ? ` ` + description : ``) + `\n` : ``) + + (type ? tab + ` * @param` + spacesAfterParam + type + spacesAfterParamVar + `$` + argumentName + (argumentDescription ? ` ` + argumentDescription : ``) + `\n` : ``) + tab + ` *\n` + tab + ` * @return` + spacesAfterReturn + `self\n` + tab + ` */\n` - + tab + `public function ` + prop.setterName() + `(` + (typeHint ? typeHint + ` ` : ``) + `$` + name + `)\n` + + tab + `public function ` + prop.setterName() + `(` + (typeHint ? typeHint + ` ` : ``) + `$` + argumentName + `)\n` + tab+ `{\n` - + tab + tab + `$this->` + name + ` = $` + name + `;\n` + + tab + tab + `$this->` + name + ` = $` + argumentName + `;\n` + `\n` + tab + tab + `return $this;\n` + tab + `}\n`