Skip to content
This repository was archived by the owner on Dec 19, 2018. It is now read-only.

Design-Time Directive [Language Feature Proposal] #1747

Closed
RandyBuchholz opened this issue Oct 30, 2017 · 4 comments
Closed

Design-Time Directive [Language Feature Proposal] #1747

RandyBuchholz opened this issue Oct 30, 2017 · 4 comments

Comments

@RandyBuchholz
Copy link

This is to open some discussion about an idea for a new language construct in Razor.

Background

While most of what we do is focused on compile and run time resolution, some "dynamic" things are better handled at design time. This proposal is to create a design-time directive in the Razor language. The design time directive indicates that the file contents following the directive will be modified by some process. The design-time directive provides hooks and extension points into a cshtml file to enable actions to take place at design time. These can be trigged manually, by Visual Studio, or other processing.

Design-Time Directive

The design-time directive is used to communicate with parsers, file processors, and the Razor Engine.

  • It takes the general form: @[...]
  • It can take parameters: @[parser, action, parameters]
  • It can have scope: @[...]{ ... }

The design-time directive indicates to a process (external or internal) that an area of the file will/can be modified by the process. The directive has no impact on it's own.

It is removed at compile time by the Razor Engine from the resulting html.

Examples

Substitution

The most trivial use of the directive is to insert a group of characters into the file.
Given - @[myText] = "This is some text to repeat."
Using - @[myText]
Produces => This is some text to repeat.
The directive isn't actually replaced. The text is inserted after the directive.
Any characters can be used - text, tags, code.
In initial cshtml:

  @[myText] 

After the substitution process runs, in cshtml:

  @[myText]
  This is some text to repeat.

In html:

  This is some text to repeat.

Changing the text in the assignment changes the results in all files using the directive when the process runs again. Though the directive doesn't technically have impact on it's own, some features like substitution would be built into the environment.

Merging

Razor is especially interesting because it can be a mix of many languages - cs, css, js, html. Setting aside the "correctness" of putting css or js in an cshtml file, they provide fair examples. In an html/cshtml file, we can include <script></script> and <style></style> blocks. For the sake of argument, let's say we have a compelling reason to include content inside them in the file. Writing the code in the page is messy, and can be problematic. Worse, many of us prefer Less and Typescript, which we can't write in-page. The @[] directive helps.

We can write our css in a Less file - styles.less, that compiles into styles.css, and our js in scripts.ts, that compiles into scripts.js. We get full editor and intellisense support. In our Page.cshml we can include the results.

  @[style, "~/styles.css"]
     ... The contents of styles.css are copied into the page here, in a <style> block.
  @[script, "~/scripts.js]
     ... The contents of scripts.js are copied into the page here in a <script> block.

This happens during design - the file content is modified, triggered manually, by the IDE, or an extension. For example, after Web Essentials compiles a Less file into css, it could also update the pages that reference the css file through design-time directives. Though the end result is much like @render, we get a local copy that provides access to the internals, and allows modification if we turn "auto-update" off.

Selectors help keep files small. If we just need a couple of styles or functions, we can only copy them.

  @[style, "~/styles.css", { .form-controls-block, .nav-link } ]
  @[script, "~/scripts.js", { datepicker } ]

This would pull the two styles and single function out of the files, and insert them into the page inside<style> and <script> blocks. This can be helpful in building small, self-contained, and lightweight components.

Templates

Html Templating is another other possibility. This can be used in regular files or in template/scaffolding engines. An overly simple example is the common label-input-validation set -
<label asp-for="ModelProperty"><input asp-for="ModelProperty"></input><...validationstuff...
There are many ways to approach this, but one could be (in a cshtml)

@[]{
  form-line = "<label asp-for="@[param]" ... " 
}

@[form-line, param="ModelProperty"]
   ... Fragment is expanded here, with replacement.

Templates are like advanced, dynamic Snippets, and can be defined in the page or in shared files. They can auto update, or be manually triggered.

Targeting

When we know we are running a file processor over the file, we can define targets. This can make the processor more efficient, and the file easier to parse. The processor can look for tagged areas to process, or even exclude.

  @[fileProcessorName, processingAction, exclude] {
       ... This section is skipped by fileProcessorName when executing processingAction 
         - if it is aware of design-time directives.
  }

Discussion Scope

This proposal isn't about any of the specific examples, but about the concept of being able to provide information to processes that manipulate code files. And if so, is @[] a good construct to use? More and more, we run processes against these files - from built-in formatting processes, to refactoring, to analysis processes. A standard way to communicate with these processors might be valuable.

Outside the Proposal

The concept can also extend into other languages.
Though advanced, this could allow things like name refactoring for related items in different technologies. For example, a C# class and Typescript class could have a design-time association. Changing one triggers changes to the other. This is a design-time change - the files are modified directly. The association doesn't exist at compile or run time. This would be a long time coming, but shows possible potential of the overall concept.

@mkArtakMSFT
Copy link
Contributor

/cc: @rynowak

@mkArtakMSFT
Copy link
Contributor

Ping @rynowak

@mkArtakMSFT
Copy link
Contributor

/cc @DamianEdwards

@mkArtakMSFT
Copy link
Contributor

mkArtakMSFT commented May 11, 2018

Hi @RandyBuchholz.
Thanks for your feature proposal.
While this may be a good idea, this is not a priority for us for the near future.
If, however, we'll see a good community interest in this issue, we'll reconsider it in the future.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

3 participants