-
Notifications
You must be signed in to change notification settings - Fork 137
Description
There is some debate about making first-class dartdoc directives for tools like snippets, and I was thinking why don't we have "template tools", or rather tools that don't involve running dart code, but maybe just evaluating a template.
This is an extremely ugly attempt at defining what that could look like, without us inventing a new language and still giving template authors some power.
Warning
Please don't think of this as a serious proposal, it's more of an exploratory idea 🤣
dartdoc:
# What if we allowed "template tools"?
template_tools:
# We could almost define something like snippets that way.
snippets:
# Snippets don't have flags or options, so this is just illustrative
flags:
- warning # ArgParser.addFlag('warning') , allowing --[no]-warning (defaulting to false)
options:
- language # ArgParser.addOption('language') , allowing --language=foo
# We have a context dictionary as follows:
# source.line
# source.column
# source.path
# package.name
# element.name
# invocationIndex
# input # text between {@tool snippets} and {@end-tool}
# arguments.raw # space separated arguments given in {@tool} invocation
# arguments.warning # true, if --warning was given in {@tool} invocation
# arguments.language # value, if --language=value was given in {@tool} invocation
#
# Using this context we can do some limited steps like:
# - extract variables from context using regular expressions and save them to context again.
# - assert values from context and throw an error instead of rendering a faulty template!
steps:
# Create a new "description" field in the context dictionary, running pattern as regex against text
- extract: description
text: '{{input}}'
pattern: '^(?<content>.*)^```'
multiLine: true
caseSensitive: true
dotAll: true
- extract: code
text: '{{input}}'
pattern: '^```(?<language>.*)\n(?<source>.*)^```'
multiLine: true
caseSensitive: true
dotAll: true
# Evaluate assertion with package:boolean_selector (already used in package:test)
# Giving template tool authors an option to sanity check data before the template is rendered.
- assert: 'code.hasMatch && description.hasMatch'
message: 'Failed to extract code and language from snippet'
# After running "steps" we've expanded context with:
# description.hasMatch # True, if regex was matched
# description.content # capture group content
# code.hasMatch # True, if regex was matched!
# code.language # capture group language
# code.source # capture group source
#
# We can access context in the template using mustache as illustrated below:
template: |
{@inject-html}
<a name="{{element.name}}.{{invocationIndex}}"></a>
<div class="snippet snippet-container anchor-container">
{{description}}
<a class="anchor-button-overlay anchor-button" title="Copy link to clipboard"
onmouseenter="fixHref(this, '{{element.name}}.{{invocationIndex}}');" onclick="fixHref(this, '{{element.name}}.{{invocationIndex}}'); copyStringToClipboard(this.href);"
href="#">
<i class="material-icons anchor-image">link</i>
</a>
<div class="copyable-container">
<button class="copy-button-overlay copy-button" title="Copy to clipboard"
onclick="copyTextToClipboard(findSiblingWithId(this, 'sample-code'));">
<i class="material-icons copy-image">content_copy</i>
</button>
<pre class="language-{{code.language}}" id="sample-code"><code class="language-{{code.language}}">{{code.source}}</code></pre>
</div>
</div>
{@end-inject-html}Note that context can probably just be Map<String, String>, we dont' have to treat foo.bar as sub-property access :D
Note:
- You can still do some attacks by using degenerate regular expressions
- We could probably accept that since it'll just loop indefinitely (or be slow), or,
- We could evaluate the regular expressions in an isolate (which is cheap).
- Regexes gives template tool authors a lot of power to do at-least something.
- The steps of repeated regex evaluation is a powerful, but also horrific programming environment 🤣
I'm not sure this is the way, we could also implement a small expression language that can be safely evaluated in templates. Options range form jinja-like things, to implementing a safe subset Dart in a small interpreter. To porting json-e to Dart and feeding the output into mustache.
But we could probably get far with regexes, boolean_selector for assertions and mustache for templating.
All this being explored: We should perhaps ask the question if we want people inventing their own HTML section types. Or whether it'd be better to argue that:
- (A) users should be using existing markdown constructs (there aren't much missing).
- (B) We should introduce dartdoc directives when good markdown constructs are missing.
In the case of snippets, it could be argued that:
- Using a
## Sectiontitle in markdown is better than a snippet macro. - All sections in dartdoc markdown should have a perma-link.
- All code blocks should have a "copy to clipboard" button.
Though, we'd have to give up the special background color for snippet section blocks.