Skip to content

Commit

Permalink
Merge pull request #98 from neild3r/templates
Browse files Browse the repository at this point in the history
Add in a templating system to handle more complex set ups
  • Loading branch information
neild3r committed Jun 30, 2019
2 parents 9015b50 + dd42e26 commit a45199b
Show file tree
Hide file tree
Showing 9 changed files with 325 additions and 40 deletions.
82 changes: 82 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ We now have a set of unit tests and some full coverage on the parsing of signatu
* 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

## Requirements

Expand All @@ -28,6 +29,87 @@ This extension contributes the following settings:
* `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
* `php-docblocker.author`: An object containing your default author tag settings
* `php-docblocker.functionTemplate`: See below for how to set up docblock templates
* `php-docblocker.propertyTemplate`: See below for how to set up docblock templates
* `php-docblocker.classTemplate`: See below for how to set up docblock templates

### Templating

If you want more control over the order or gap settings on your docblocks or you want different things for properties vs class templates
you can start customising the template configuration objects. These are the config options `functionTemplate`, `propertyTemplate` and
`classTemplate`.

#### Default set up for function

The below is the default set up for a function. The order of the keys represents the output order. There are no specific options in each
config option per key to add additional control.

```json
{
"message": {},
"param": {},
"return": {},
"extra": {}
}
```

#### Supported template keys

| Key | Aplies to type | Description |
|-----------------|-----------------|-----------------------------------------------------------------------------------|
| message | All | Space for entering a description of your block |
| extra | All | Adds in your custom tags from the extra config |
| param | Function | Function @param items |
| return | Function | Function @return item |
| var | Property | Property @var item |
| * | All | This is for any key that is unmatched you can use the content option to add a tag |

#### Supported template config options

| Option | Aplies to key(s) | Description |
|-----------------|------------------|------------------------------------------------|
| gapBefore | All | Adds a gap before the tag section starts |
| gapAfter | All | Adds a gap after the tag section ends |
| content | * | Adds a gap after the tag section ends |

#### Configured function template example

In the example below we have added some gap configuration and removed the return tag for our template as well as
changing the default order. This means we'll never have a @return tag and extra comes before the params. It's also
worth pointing out that the gapAfter in the message is the same as setting the gap config option in the main config
to true.

```json
{
"message": {
"gapAfter": true
},
"extra": {},
"param": {
"gapBefore": true
},
}
```

#### Configured function with extra content and placeholders

The example below won't have a return tag and will add in an author tag with correct placeholders depending on
how many options you have. You can put in placeholders by using `###` in place of the tab stop number and it
will be calculated at generation time.

```json
{
"message": {
"gapAfter": true
},
"param": {
"gapBefore": true
},
"author": {
"content": "@author ${###:Neil Brayfield} <${###:neil@test.com}>"
}
}
```

## Supported DocBlock tags

Expand Down
27 changes: 27 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,33 @@
"default": false,
"description": "Fully qualifies any data types used in param and returns by reading the namespaces."
},
"php-docblocker.functionTemplate": {
"type": "object",
"default": {
"message": {},
"param": {},
"return": {},
"extra": {}
},
"description": "Specify the default template for functions."
},
"php-docblocker.propertyTemplate": {
"type": "object",
"default": {
"message": {},
"var": {},
"extra": {}
},
"description": "Specify the default template for class variables."
},
"php-docblocker.classTemplate": {
"type": "object",
"default": {
"message": {},
"extra": {}
},
"description": "Specify the default template for classes."
},
"php-docblocker.author": {
"type": "object",
"default": {
Expand Down
2 changes: 2 additions & 0 deletions src/block/class.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Block } from "../block";
import { Doc, Param } from "../doc";
import Config from "../util/config";

/**
* Represents a class block
Expand All @@ -18,6 +19,7 @@ export default class Class extends Block
{
let params = this.match();
let doc = new Doc('Undocumented '+ params[2]);
doc.template = Config.instance.get('classTemplate');

return doc;
}
Expand Down
1 change: 1 addition & 0 deletions src/block/function.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export default class FunctionBlock extends Block
let params = this.match();

let doc = new Doc('Undocumented function');
doc.template = Config.instance.get('functionTemplate');
let argString = this.getEnclosed(params[6], "(", ")");
let head:string;

Expand Down
2 changes: 2 additions & 0 deletions src/block/property.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Block } from "../block";
import { Doc, Param } from "../doc";
import Config from "../util/config";

/**
* Represents an property block
Expand All @@ -20,6 +21,7 @@ export default class Property extends Block
let params = this.match();

let doc = new Doc('Undocumented variable');
doc.template = Config.instance.get('propertyTemplate');

if (params[5]) {
doc.var = this.getTypeFromValue(params[5]);
Expand Down
147 changes: 107 additions & 40 deletions src/doc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ export class Doc
*/
public message:string;

/**
* Define the template for the documentor
*
* @type {Object}
*/
protected _template:Object;

/**
* Creates an instance of Doc.
*
Expand Down Expand Up @@ -78,74 +85,134 @@ export class Doc
*/
public build(isEmpty:boolean = false):SnippetString
{
let snippet = new SnippetString();

let extra = Config.instance.get('extra');
let gap = !Config.instance.get('gap');
let gap = Config.instance.get('gap');
let returnGap = Config.instance.get('returnGap');

let returnString = "";
let varString = "";
let paramString = "";
let extraString = "";
let messageString = "";

if (isEmpty) {
gap = true;
extra = [];
}

let stop = 2;

snippet.appendText("/**");
snippet.appendText("\n * ");
snippet.appendVariable('1', this.message);
messageString = "\${###" + (this.message != "" ? ':' : '') + this.message + "}";

if (this.params.length) {
if (!gap) {
snippet.appendText("\n *");
gap = true;
}
paramString = "";
this.params.forEach(param => {
snippet.appendText("\n * @param ");
snippet.appendVariable(stop++ + '', param.type);
snippet.appendText(" ");
snippet.appendText(param.name);
if (paramString != "") {
paramString += "\n";
}
paramString += "@param \${###:"+param.type+"} " + param.name.replace('$', '\\$');
});
}

if (this.var) {
if (!gap) {
snippet.appendText("\n *");
gap = true;
}
snippet.appendText("\n * @var ");
snippet.appendVariable(stop++ + '', this.var);
varString = "@var \${###:" +this.var + "}";
}

if (this.return && (this.return != 'void' || Config.instance.get('returnVoid'))) {
if (!gap) {
snippet.appendText("\n *");
gap = true;
} else if (returnGap && this.params.length) {
snippet.appendText("\n *");
}
snippet.appendText("\n * @return ");
snippet.appendVariable(stop++ + '', this.return);
returnString = "@return \${###:" +this.return + "}";
}

if (Array.isArray(extra) && extra.length > 0) {
if (!gap) {
snippet.appendText("\n *");
gap = true;
}
for (var index = 0; index < extra.length; index++) {
var element = extra[index];
if (element != "") {
element = " " + element;
extraString = extra.join("\n");
}


let templateArray = [];
for (let key in this.template) {
let propConfig = this.template[key];
let propString:string;
if (key == 'message' && messageString) {
propString = messageString;
if (gap) {
propConfig.gapAfter = true;
}
} else if (key == 'var' && varString) {
propString = varString;
} else if (key == 'return' && returnString) {
propString = returnString;
if (returnGap) {
propConfig.gapBefore = true;
}
snippet.appendText("\n *");
snippet.value += element;
} else if (key == 'param' && paramString) {
propString = paramString;
} else if (key == 'extra' && extraString) {
propString = extraString;
} else if (propConfig.content !== undefined) {
propString = propConfig.content;
}

if (propString && propConfig.gapBefore && templateArray[templateArray.length - 1] != "") {
templateArray.push("");
}

if (propString) {
templateArray.push(propString);
}

if (propString && propConfig.gapAfter) {
templateArray.push("");
}
}

snippet.appendText("\n */");
if (templateArray[templateArray.length - 1] == "") {
templateArray.pop();
}

let templateString:string = templateArray.join("\n");
templateString = "/**\n" + templateString + "\n */";

let stop = 0;
templateString = templateString.replace(/###/gm, function():string {
stop++;
return stop + "";
});

templateString = templateString.replace(/^$/gm, " *");
templateString = templateString.replace(/^(?!(\s\*|\/\*))/gm, " * $1");

let snippet = new SnippetString(templateString);

return snippet;
}

/**
* Set the template for rendering
*
* @param {Object} template
*/
public set template(template:Object)
{
this._template = template;
}

/**
* Get the template
*
* @type {Object}
*/
public get template():Object
{
if (this._template == null) {
return {
message: {},
var: {},
param: {},
return: {},
extra: {}
}
}

return this._template;
}
}

/**
Expand Down
3 changes: 3 additions & 0 deletions test/doc.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ suite("Snippet build tests", () => {
} else {
empty = true;
}
if (Config.instance.get('template')) {
doc.template = Config.instance.get('template');
}
assert.equal(doc.build(empty).value, testData.expected.join("\n"));
});
});
Expand Down
Loading

0 comments on commit a45199b

Please sign in to comment.