A customer came to me asking about Templates and what could be done with them.
The desired use case seemed simple enough:
- The user types a few characters, hits space and the template expands as usual.
- The user fills out one piece of information, and that information is replicated to several other locations around the expanded text.
- The user fills out a 2nd value and that is also replicated to several location.
Normally I would assume the job would be done at this stage. However our customer was after something a little beyond the usual.
They required that Fields used within the template should not be removed once the data had been entered.
I asked what the motivation for this was and was told : “I’d like them to remain, in case a user wanted to come back and alter them at some point in the future.”
Whilst a little unconventional, this didn’t seem unreasonable. However Fields have some limitations…
As it turns out, Fields are not very (read ‘at all’) persistent.
They are removed when:
- They are used (values set and enter pressed).
- Another template is expanded.
- The project\solution is closed\reopened.
As you can see, this leaves a very narrow window in which to make modifications. As such Fields are not suited to the customer’s needs.
Note: This section details some alternative approaches to solving this problem that were considered. If you’re not interested in this, feel free to skip to “The Solution: Refresh Template” (below)
So I sat down to consider what alternatives might be available.
The brief: To allow someone to expand a template which would normally use fields and links, in such a way as they can come back (potentially much later) and edit the fields and, by proxy, the linked text.
- Rename: Suggest user could use our rename refactoring when they come back to change the linked value to something else.
- Reconfigure Field Interaction: Alter CodeRush’s behaviour regarding fields through configuration.
- Persistable Fields: Create new type of persistent field.
- Recreate Fields on Demand: Recreate the fields when user returned to this area.
- Refresh Template: Wipe and replay the template.
If the fields and links created by a template are equivalent to the links generated by the act of triggering a Rename refactoring on some token generated by the template, then this prevents the need to leave fields intact after template expansion.
However this is rarely the case. Fields and links within templates are often used to create related identifiers with the same prefix or suffix. They may also be used to create documentation (standard and non standard). Sometimes these links and fields may affect the types emitted by the template.
In all these cases, the Rename refactoring is incapable of recreating the fields and links from the final static output of the template.
Strategy: Reconfigure Field Interaction
Imagine we take a configuration approach.
We could create a new context (IsInPersistableField) which would determine if we were within a Field which should be considered persistable.
Note: Without further plugin work the template would need to emit something which might mark all fields within it as Persistable.
We would alter the shortcut for FieldAccept (Enter) and enable it only if the condition were not met.
We would create a new shortcut for FieldNext (Enter) and enable it only if the condition were met.
This allows us to redirect the use of Enter to navigate between Fields without killing them assuming that we were in the result of a particular type of template
However this is just the tip of the iceberg. It does not make the field truly persistent. It merely prevents the premature removal of the field due to it receiving confirmed data. Additionally over time this would lead to a growing number of fields to manage.
Strategy: Create Persistable Field
Potentially we could create a plugin which introduces an additional type of Field. One which is persisted between sessions. This new field would need to reproduce all functionality of the existing field except the elimination of itself when enter was pressed. This would also need to be repeated with a persistable version of the Link TextCommand
This is possible, but complex. Additionally it would create an even greater number of fields to manage over time, since none would appear to ever be removed.
Strategy: Recreate Fields on Demand
If there were some way we could infer the prior existence of a field and set of links, from the text its template had previously produced, we could recreate them on demand.
Yeah this struck me as a bit far fetched as well :) It’s a bit like reassembling a egg and some wheat when given a cake.
The Solution: Refresh Template
Refresh (or replay) Template is the idea that you re-execute a template over the top of itself so that you can re-specify the values for any fields within.
If we modify a template so that it emits its own name into the final text, we could then build a plugin that knows how to use that meta data as a way of clearing and re-executing that template at some later point.
Consider the following template called DateNow which outputs the current Date and Time:
In order to allow this template to refresh, we need to augment it with a header and footer.
Now we have all the information we need. If only we had a plugin that could make use of this information…
CR_RefreshTemplate provides a new CodeProvider Refresh Template, which is available when the caret is between an appropriate header and footer.
Once triggered it will…
- Read the name of the template from the header and footer.
- Clear the Text between the header and footer inclusive.
- Re-execute the original template by name.
At this point you, the user, will need to re-enter data for any fields that were re-created by the template.
Thus each time you invoke ‘Refresh Template’ on our example DateNow template(above), the output of the previous template expansion will be wiped and replaced with the latest date and time.
ie The date\time is updated each time you refresh the template.
So how long do you have to wait to get your hands on this little gem?
I guess that depends how quickly you can click a link. CR_RefreshTemplate is fully functional and available now :)
This approach is limited in that it proceeds from the assumption that your template expands into a single block of text. This means that text generated elsewhere in your project (or even file) will not be removed, and may therefore collide with later generated code.
For example you would not be able to engineer this to work with the version of property templates which generates backing store variables elsewhere in the file.
It turns out that there are 1 or 2 interesting bonus features to this plugin.
Bonus1: Code Upgrades
Refresh Template can only execute the latest version of the specified template. Therefore if you have upgraded the template to be faster, more efficient, or just generally more awesome, the act of refreshing it will update the existing code to use the better version rather than the older less awesome version.
You will still have to re-enter any old field values (assuming you don’t want to change them from their defaults), but this is a pretty small trade of for the optimization you could be receiving.
One can even envisage a point in the future where you might issue an instruction to regenerate \ upgrade all Refreshable templates to their latest version throughout a file, project or solution.
Bonus2: Sequenced Templates
It turns out that the header and template don’t need to specify the template that generated the existing output.
Yes that’s right. Instead you could indicate any other template which could be expanded given the same context.
Why would I want to do that?
Let’s imagine you’re in a presentation where you’re demonstrating a series of incremental changes to some code.
Your existing options for this scenario are:
- Rewrite\ adjust the code at each stage including the new incremental changes.
- Load different projects, each one with a slightly improved version
- Load different files, again with slightly differing versions of the code.
- Skip about in source control history and have studio reload everything (hopefully) each time you do.
With CR_RefreshTemplate, you can pre-create a chain of templates, each with a RefreshTemplate link to the next template in sequence.
You could give a mini presentation, jumping from one code sample to another by repeatedly issuing the ‘Refresh Template’ command.
…which could be refreshed to Step2…
…which could be refreshed to Step3…
…which, if you want, could be refreshed back to Step1 again.
So we’ve got a simple little plugin CR_RefreshTemplate which can:
- Refresh code previously generated by an appropriate template.
- Upgrade your code to the latest version of a template.
- Proceed through a sequence of template driven steps in order to demonstrate the evolution of some code during a presentation.
So what are you waiting for?
Download now and let’s see what develops.