Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consider a markup.rs-like alternative syntax or an API to aid compile-time template generation #41

Open
ssokolow opened this issue Jan 7, 2021 · 17 comments
Labels
good first issue Good for newcomers Status: Proposal Request for comments Type: Feature New Feature

Comments

@ssokolow
Copy link

ssokolow commented Jan 7, 2021

With all the iteration I've been doing on my test project, I've become very aware of how much the client-side parts of the project are a hole in the strong typing Actix provides me.

Traditionally, I would mitigate that by using TypeScript for the JavaScript and having development builds use a runtime-loading API for HTML templates to speed iteration when I do run into bugs because, in the end, there will always be some things a type system can't fix. (eg. Sailfish can't protect me from using the wrong kind of element)

However, doing this in Rust and using things like Actix's header-name constants for protecting against typos, I'm starting to feel that it should also be possible to get the templating performance of Sailfish with error-prevention matching or exceeding that of markup.rs by writing a build.rs which converts a syntax like that of markup::define! to Sailfish .stpl before Sailfish sees it... and having to do it in a build.rs to pull the wool over a template system that expects to read directly from a file in a derive macro feels like an ugly hack.

(Back when I was using a Python framework named Pylons years ago, I would have used a templating engine named Genshi, which ran its templates through an XML parser to ensure a minimum level of well-formedness, if it weren't one of the slowest templating engines because it did it at runtime.)

How receptive are you to the idea of either having some kind of alternative syntax which rules out malformed HTML (eg. <h2 title=""my editor's quote/bracket auto-pairing goofed">Oops. This should be an <code>h2<code> element.</h1>) at compile time or providing a proper way to hook in a preprocessor so others can do it?

(Ideally, which can catch typos in tag names, either by distinguishing between standard and custom tags syntactically (eg. h1 vs. "lightbox") or by requiring non-standard tags to be registered with some kind of "add tag" annotation before they'll be accepted or one where it's possible to hook some kind of validator function in between it and the rest of Sailfish.)

Such a syntax would also resolve another issue I have with traditional HTML templating's string-based (as opposed to AST-based) nature... I often find myself in situations where I'm doing stuff like this because the presence of whitespace between two elements will cause a styling bug.

<thing title="blah blah blah long thing that needs to wrap"
>thing that must not have space between it and the previous element

An alternative syntax like that of markup.rs would make it possible to directly and easily generate minified output where the only whitespace is the whitespace you explicitly ask for while still having templates that are formatted for human consumption.

Personally, I'm partial to making it easy to hook in a custom preprocessor, because designing a proper markup.rs-style syntax which does the smartest job of drawing a line between mistakes and useful but hard to validate stuff feels like it'd need a lot of thought and experimentation and I wouldn't want to stabilize the syntax too quickly.

For example, what's the most broadly useful equivalent to this that feels nice to write and still ensures a well-formed tree of elements at compile-time?

<a><b>
<% if foo %><strong><span class="icon_alert"></span><% } %>
<d><e>hello</e></d>
<% if foo %></strong><% } %>
</b></a>

(Yes, I know, in that particular case, it could be done with a CSS class and a ::before. I can't remember which of my projects had the actual example with no better option.)

@ssokolow
Copy link
Author

ssokolow commented Jan 7, 2021

Random brainstorming for the "catches tag typos" part of such an idea.

Markup.rs has @markup::doctype() so, if such a system used the same syntax, it could have:

  • @markup::allow_tag("lightbox") (allow <lightbox> when it would normally be a compile-time error for not being part of the HTML5 standard)
  • @markup::warn_tag("tt") (Make use of <tt> into a compile-time warning, despite it being in the spec... could also be called deprecate_tag)
  • @markup::deny_tag("tt") (Make use of <tt> into a compile-time error, despite it being in the spec)

(If available, a forbid_tag would be better done outside the template.)

@Kogia-sima
Copy link
Member

Kogia-sima commented Jan 7, 2021

@ssokolow Thanks for your feedback and detailed explanation!

(I am not very good at English, so I apologize in advance.)

Actually there was an idea that Sailfish allows Pug syntax before but it was discarded because it requires evaluating JavaScript syntax in conditionals and iterations. Also I found that Pug allows JavaScript expression in many place. Pug renderer achieves this by simple wrapping the generated code by Function object (which requires JavaScript runtime)

Traditionally, I would mitigate that by using TypeScript for the JavaScript and having development builds use a runtime-loading API for HTML templates to speed iteration when I do run into bugs because, in the end, there will always be some things a type system can't fix. (eg. Sailfish can't protect me from using the wrong kind of element)

I think that modern editor can prevent developers from writing invalid tag names. Here is the screenshot from my vim.

html-invalid-tag

In this case the invalid HTML tag name (string, which should be strong) is highlighted in different color. And I believe we can get most of benefits from pug or markup.rs syntax by properly configuring the editor or installing editor extension.

Also markup.rs syntax does not allow inline <script> or <style> as of yet. We need to somewhat extend the original syntax to support these tags.

An alternative syntax like that of markup.rs would make it possible to directly and easily generate minified output where the only whitespace is the whitespace you explicitly ask for while still having templates that are formatted for human consumption.

Sometimes I intentionally insert a whitespaces or line breaks between spans in order to separate them. Stripping this whitespaces is not desirable for me. In fact EJS was reported the bug about this behaviour more than 2 years ago. For this reason I think whitespaces should be kept and not stripped out from rendering results.

If you like to write templates in another format, the better solution would be developing a preprocessor to convert from another format (such as pug, markup.rs) as an external tool. Then you can write the preprocess phase in your build.rs. There is no need to preprocess in the Sailfish compilation phase.

Also If your HTML can be tested on local machine, I think, this RFC can solve all of the issue stated here.

@Kogia-sima Kogia-sima added good first issue Good for newcomers Status: Proposal Request for comments Type: Feature New Feature labels Jan 7, 2021
@ssokolow
Copy link
Author

ssokolow commented Jan 7, 2021

Actually there was an idea that Sailfish allows Pug syntax before but it was discarded because it requires evaluating JavaScript syntax in conditionals and iterations.

I wonder if Pug was inspired by Haml for Ruby. Either way, despite being a Python programmer, I'm not a big fan of things like Haml, YAML, and CoffeeScript. It's too hard to design a syntax with significant indentation without making it a footgun.

I think that modern editor can prevent developers from writing invalid tag names. Here is the screenshot from my vim.

I use Vim too, but I wouldn't want to try to bodge Vim into the CI runs for PRs.

Also markup.rs syntax does not allow inline <script> or <style> as of yet. We need to somewhat extend the original syntax to support these tags.

Of course... bearing in mind that it's not a high priority for me personally, because my Content-Security-Policy would forbid them anyway.

Worst-case scenario, require it to include_str! the JavaScript or CSS from an external file. It'll make for more robust syntax highlighting anyway.

Sometimes I intentionally insert a whitespaces or line breaks between spans in order to separate them. Stripping this whitespaces is not desirable for me.

Same for me, which is why I want something that generates no whitespace unless requested, and then I can use something like span { ... }, " ", span { ... } to explicitly request it.

If you like to write templates in another format, the better solution would be developing a preprocessor to convert from another format (such as pug, markup.rs) as an external tool. Then you can write the preprocess phase in your build.rs. There is no need to preprocess in the Sailfish compilation phase.

I actually addressed that in my initial message. I'll rephrase it slightly:

and having to do it in a build.rs to outwit a template system that expects to read directly from a file in a derive macro feels like an ugly hack.

Also If your HTML can be tested on local machine, I think, this RFC can solve all of the issue stated here.

Unfortunately, testing my templates tends to be highly dependant on the data that's fed into them. I'd much prefer to just have a cfg(debug_assertions) that swaps in RFC 1 for my debug builds so all I have to do is Ctrl+C and re-run the build to pick up the template changes.

@Kogia-sima
Copy link
Member

Kogia-sima commented Jan 8, 2021

I wonder if Pug was inspired by Haml for Ruby. Either way, despite being a Python programmer, I'm not a big fan of things like Haml, YAML, and CoffeeScript. It's too hard to design a syntax with significant indentation without making it a footgun.

I uses Python for my research project, and I'm somewhat dissatisfied for its design (e.g. it can always cause an unexpected behavior without any error message). However, It is also the fact that its code is quite readable and compact compared to another languages.

In like manner, I think Pug syntax is suitable for designing a large HTML structure, because it allow developers to take a view of wide range of contents.

Some people dislikes the indentation-based language, but some people not. For example one of my lab mates is a big fan of Pug syntax. Not only my lab mates; Python is a very popular language for developing a machine learning tool in Japan. It's just a matter of taste. I don't want to finalize the syntax based on your personal preference.

I use Vim too, but I wouldn't want to try to bodge Vim into the CI runs for PRs.

I'm sorry, but I don't understand what you said. Could you explain it in more detail?

Same for me, which is why I want something that generates no whitespace unless requested, and then I can use something like span { ... }, " ", span { ... } to explicitly request it.

Yeah I just misunderstood what you said... Thanks for providing a concrete example.

and having to do it in a build.rs to outwit a template system that expects to read directly from a file in a derive macro feels like an ugly hack.

I don't think it's an ugly hack. Embedding the code generated by build.rs into the application is commonly used strategy. If you don't like it then you can write your own build scripts to precompile your templates before executing cargo build.

Unfortunately, testing my templates tends to be highly dependant on the data that's fed into them. I'd much prefer to just have a cfg(debug_assertions) that swaps in RFC 1 for my debug builds so all I have to do is Ctrl+C and re-run the build to pick up the template changes.

You mean you cannot feed even dummy data into the templates? RFC 5 allows users to design a HTML by feeding dummy data into them. Even if your rendering results depend on the input data, you can avoid many typing issues you listed.

Also RFC 1 is a feature for achieving hot-reloading of the templates in Sailfish. It allows reloading templates at runtime, which means changes to templates are immediately applied to rendering results without recompiling.

@ssokolow
Copy link
Author

ssokolow commented Jan 8, 2021

I don't want to finalize the syntax based on your personal preference.

I just think it runs counter to the goal of improving resistance to human error to use an indentation-based template syntax as the stricter of two options provided by a templating engine if that templating engine is for Rust.

I want the TypeScript of templating, not the CoffeeScript.

I'm sorry, but I don't understand what you said. Could you explain it in more detail?

I want something that can catch those sorts of HTML tag typos on a continuous integration service like Travis-CI or or GitHub Actions, which gets run automatically on any code submitted in a Pull Request. That means it has to be achieved through a non-interactive command, like cargo check or cargo test.

I don't think it's an ugly hack. Embedding the code generated by build.rs into the application is commonly used strategy. If you don't like it then you can write your own build scripts to precompile your templates before executing cargo build.

It's not the preprocessing that's the ugly hack, it's writing the preprocessed stuff back to temporary files just because the template engine only supports reading templates from files.

It's the compilation equivalent of using foo > temp.txt; bar temp.txt; rm temp.txt because bar doesn't support reading from stdin when you should be able to use foo | bar .

You mean you cannot feed even dummy data into the templates? RFC 5 allows users to design a HTML by feeding dummy data into them. Even if your rendering results depend on the input data, you can avoid many typing issues you listed.

The problem isn't feeding data into the templates, it's getting data which replicates the problem in a way that's time-efficient. I don't want to have to derive Serialize on all my templates, rework the innards of my programs, and either have a --dump-template-data in all my builds or wait to rebuild with a dump_template_data feature just so I can redirect what was about to be fed into a template to a "dummy data" file.

Also RFC 1 is a feature for achieving hot-reloading of the templates in Sailfish. It allows reloading templates at runtime, which means changes to templates are immediately applied to rendering results without recompiling.

And hot-reloading as a means of debugging templates is what I want.

I want to have the current state of things when I use cargo run --release and hot-reloading when I use cargo run. My Rust creations start instantly, so template testing with hot-reloading could be as simple as a script like this:

cargo build
while true; do
    echo "To apply changes to templates, press Ctrl+C and then press F5 in the browser"
    ./target/debug/program_name
done

@Kogia-sima
Copy link
Member

I just think it runs counter to the goal of improving resistance to human error to use an indentation-based template syntax as the stricter of two options provided by a templating engine if that templating engine is for Rust.

I don't think markup.rs syntax reduces typo or invalid tags in the template. As I mentioned before, many editors support a lot of features for writing HTML. However, as far as I know, there is no editor supporting markup.rs syntax. Also many people are not familiar with such an unusual syntax.

These factors also have potential to lead human errors such as typo and incorrect HTML structure.

Of course TypeScript is a good choice to reduce human errors, because many editors, language servers, linters, and other external tools support this language.

I want something that can catch those sorts of HTML tag typos on a continuous integration service like Travis-CI or or GitHub Actions, which gets run automatically on any code submitted in a Pull Request. That means it has to be achieved through a non-interactive command, like cargo check or cargo test.

I'll admit that I have no experience to write front-end application as open source software so I couldn't have understood what you meant. However, even in the open-source software you can write layout tests, or deploy PR on the test server to check the result. Gazing at the code diff doesn't fully avoid human errors. If you really want contributors to avoid collapsing layout, I think it is better to check the rendering result out by yourself.

It's not the preprocessing that's the ugly hack, it's writing the preprocessed stuff back to temporary files just because the template engine only supports reading templates from files.

It's the compilation equivalent of using foo > temp.txt; bar temp.txt; rm temp.txt because bar doesn't support reading from stdin when you should be able to use foo | bar .

Shell supports piping the program output to another program. Also even if bar doesn't support reading from stdin you can write script for automate its process. However proc-macro don't support similar features. The only way to pass data to procedural macro is write the contents to a file. This is apple-to-orange comparison.

The problem isn't feeding data into the templates, it's getting data which replicates the problem in a way that's time-efficient. I don't want to have to derive Serialize on all my templates, rework the innards of my programs, and either have a --dump-template-data in all my builds or wait to rebuild with a dump_template_data feature just so I can redirect what was about to be fed into a template to a "dummy data" file.

I don't know why you are going to generate dummy data on every builds. Don't you feed dummy data into your application when you test it? You mean it wastes a lot of time to even generate the small amount of dummy data?

RFC 5 allows developers to re-use the dummy data which is also used for tests, so if it is hard to feed template data in the debugger, then it should be difficult to test such an application.

And hot-reloading as a means of debugging templates is what I want.

I want to have the current state of things when I use cargo run --release and hot-reloading when I use cargo run. My Rust creations start instantly, so template testing with hot-reloading could be as simple as a script like this:

Unfortunately Sailfish is not designed to achieve this feature. It requires calling rustc at runtime for compiling templates into binary file, then dynamically link with it. This is highly unsafe operation because internal memory layout of struct/enum is not specified in Rust. Also procedural macro never knows what the actual type of fields is. For these reasons RFC 1 is unlikely to implement.

@ssokolow
Copy link
Author

I don't think markup.rs syntax reduces typo or invalid tags in the template. As I mentioned before, many editors support a lot of features for writing HTML. However, as far as I know, there is no editor supporting markup.rs syntax.

markup.rs is not ideal, but it has two properties which reduce the chance of mistakes:

  1. Like Pug but unlike HTML, the tag name is not repeated in the closing tag, avoiding mismatches. You can still get the div { wrong, but there's no chance of producing <div></span> when making a change.
  2. Like HTML but unlike Pug, It uses non-whitespace characters to indicate scope. In my experience, it's easy to have a lurking Vim misconfiguration which will mess up indentation (eg. misconfigured behaviour for Enter/Return) but having { and } to denote scope means that such a misconfiguration won't change the semantics and can easily be fixed by an automatic reindent.

Also many people are not familiar with such an unusual syntax.

For that reason, I would want to research all the existing popular "minimal" template engines, Pug, Haml, etc. and design a syntax that's as familiar as possible while still achieving the goals.

However, even in the open-source software you can write layout tests, or deploy PR on the test server to check the result. Gazing at the code diff doesn't fully avoid human errors. If you really want contributors to avoid collapsing layout, I think it is better to check the rendering result out by yourself.

That's like saying "You don't need Rust's type system. Go lets you write unit tests."

Careful design of template syntax can eliminate entire classes of bugs, while tests only check for certain instances of them. I came to Rust from Python for exactly that reason. I was tired of exhausting myself trying to make my Python tests comprehensive enough to satisfy me.

Shell supports piping the program output to another program. Also even if bar doesn't support reading from stdin you can write script for automate its process. However proc-macro don't support similar features. The only way to pass data to procedural macro is write the contents to a file. This is apple-to-orange comparison.

I've seen proc macros which take UFCS paths. Are you telling me there's no way you could support that as an alternative to taking a file path and then call that function to retrieve the text of the template?

I don't know why you are going to generate dummy data on every builds. Don't you feed dummy data into your application when you test it? You mean it wastes a lot of time to even generate the small amount of dummy data?

I don't want to generate dummy data on every build, but I also don't know what part of the data needs to be in the dummy until I've experimented.

Generating dummy data for tests assumes I've already identified the source of the problem and what I need to avoid regressing.

Currently, it's such a hassle to make experimental changes with Sailfish that, even with RFC 5, I'd probably still do what I currently do: Open up the DOM Inspector, manually edit the HTML and CSS until I figure out why the browser is misbehaving, and then manually copy my changes into the Sailfish template and the CSS, hope I didn't forget one of the changes I made, and run a re-build to verify that I got it write. Not a convenient workflow.

Unfortunately Sailfish is not designed to achieve this feature. It requires calling rustc at runtime for compiling templates into binary file, then dynamically link with it. This is highly unsafe operation because internal memory layout of struct/enum is not specified in Rust. Also procedural macro never knows what the actual type of fields is. For these reasons RFC 1 is unlikely to implement.

That's unfortunate.

@Kogia-sima
Copy link
Member

Kogia-sima commented Jan 10, 2021

markup.rs is not ideal, but it has two properties which reduce the chance of mistakes:

I know that mark.rs-like syntax may avoid human errors at some point, but it also has another potential to lead to invalid code. Therefore it doesn't reduce the risk of human errors in total. That's what I meant to say.

I'm sorry that my explanation wasn't good so that it leads to misunderstanding.

For that reason, I would want to research all the existing popular "minimal" template engines, Pug, Haml, etc. and design a syntax that's as familiar as possible while still achieving the goals.

There are many alternative syntax for HTML commonly used. That's one of the reason why I would like to develop a preprocessor as an external tool.

Careful design of template syntax can eliminate entire classes of bugs, while tests only check for certain instances of them. I came to Rust from Python for exactly that reason. I was tired of exhausting myself trying to make my Python tests comprehensive enough to satisfy me.

I don't agree with this opinion. Even syntax itself is valid HTML can cause entire collapse of its design (for example, missing of CSS class, contents not wrapped by div). If you mention about the bug caused by writing invalid HTML, I don't think so many people write such an invalid HTML, and such bugs can easily detected by parsing HTML structure before compilation. Providing an alternative syntax feels over-hacking for me.

I've seen proc macros which take UFCS paths. Are you telling me there's no way you could support that as an alternative to taking a file path and then call that function to retrieve the text of the template?

Procedural macro can't execute functions passed as an attribute. I know there are some crates that accept function as an argument of procedural macro (e.g. structopt, serde), but such crates just forward the passed function to generated code. They doesn't actually call those functions at compilation phrase.

Currently, it's such a hassle to make experimental changes with Sailfish that, even with RFC 5, I'd probably still do what I currently do: Open up the DOM Inspector, manually edit the HTML and CSS until I figure out why the browser is misbehaving, and then manually copy my changes into the Sailfish template and the CSS, hope I didn't forget one of the changes I made, and run a re-build to verify that I got it write. Not a convenient workflow.

If your templates are "misbehaving" due to invalid HTML structure, such a problem can be resolved by developing a linter for sailfish templates. Also such a linter can be easily extended to another Rust template engines (e.g. Askama, Maud, and Yarte).

If I understand your comments correctly, all of the issue you're going to resolve can be avoided by simply checking the HTML syntax before template compilation. So I'm closing this issue and open a new tracking issue for develop a linter as an external tool if you're ok.

@ssokolow
Copy link
Author

Therefore it doesn't reduce the risk of human errors in total.

I think we'll have to agree to disagree on that. Also, for the same reason that JSON has displaced XML for structured data exchange outside web uses, a syntax more like markup.rs is more convenient to hand-write.

There are many alternative syntax for HTML commonly used.

One could say the same thing about Sailfish's current syntax. If I could choose without sacrificing performance, I'd prefer Django/Jinja/Liquid/Twig-style {% if foo %} and {{ var }} template syntax.

That's one of the reason why I would like to develop a preprocessor as an external tool.

I think it should be an external tool too... I just want to make sure that doing so doesn't make the experience suffer.

Does Sailfish currently support reading templates from OUT_DIR to avoid cluttering up src with build.rs-generated artifacts? That would make the use of temporary files much less problematic.

I don't agree with this opinion. Even syntax itself is valid HTML can cause entire collapse of its design (for example, missing of CSS class, contents not wrapped by div). If you mention about the bug caused by writing invalid HTML, I don't think so many people write such an invalid HTML, and such bugs can easily detected by parsing HTML structure before compilation. Providing an alternative syntax feels over-hacking for me.

If I understand your comments correctly, all of the issue you're going to resolve can be avoided by simply checking the HTML syntax before template compilation. So I'm closing this issue and open a new tracking issue for develop a linter as an external tool if you're ok.

Again, that's like saying that Rust is unnecessary because static analyzers exist for C and C++.

Sailfish's syntax is too general to check as thoroughly as I want at compile-time. For example, whether an opening and closing tag are correctly balanced can depend on the value of a variable.

A linter would help, but you need to constrain the input syntax to fully rule out certain classes of bugs and, if you're doing that, you might as well use a syntax which avoids the verbosity of requiring users to type so many closing tags in full.

I'm OK with closing this, but I would still like an answer to my question about OUT_DIR.

Procedural macro can't execute functions passed as an attribute. I know there are some crates that accept function as an argument of procedural macro (e.g. structopt, serde), but such crates just forward the passed function to generated code. They doesn't actually call those functions at compilation phrase.

Ahh. Fair enough.

@Kogia-sima
Copy link
Member

Kogia-sima commented Jan 10, 2021

I think we'll have to agree to disagree on that. Also, for the same reason that JSON has displaced XML for structured data exchange outside web uses, a syntax more like markup.rs is more convenient to hand-write.

Yes, but in this day modern editors automatically generate closing tags, and one may even define snippets for HTML syntax. For example I define snippets for <ol>, <ul>, <table>, <select> etc. img snippets also prevent me from missing alt attribute.

One could say the same thing about Sailfish's current syntax. If I could choose without sacrificing performance, I'd prefer Django/Jinja/Liquid/Twig-style {% if foo %} and {{ var }} template syntax.

We're not talking about separator for embedded code. It's about syntax for non-embedded code. Also, Sailfish's syntax is equivalent to Eruby/EJS/ECT which are all commonly used template engine. Also since Rust frequently use braces, using separators like {% %} {{ }} leads to unreadable and even buggy code.

Does Sailfish currently support reading templates from OUT_DIR to avoid cluttering up src with build.rs-generated artifacts? That would make the use of temporary files much less problematic.

Sailfish haven't supported this yet, but this is an interesting feature! I've opened a new tracking issue to achive this.

A linter would help, but you need to constrain the input syntax to fully rule out certain classes of bugs.

Yes, we can use existing HTML parsers to achieve that.

@ssokolow
Copy link
Author

ssokolow commented Jan 10, 2021

Yes, but in this day modern editors automatically generate closing tags, and one may even define snippets for HTML syntax. For example I define snippets for <ol>, <ul>, <table>, <select> etc. img snippets also prevent me from missing alt attribute.

And I'm of the camp who firmly believe that Rust is not Java and we should not need to rely on the editor to make our language syntaxes usable. That's lazy design. Design the tool to work well enough on its own, then let the editor improve upon that.

Heck, I find that, if you use any closing tag generation enough, you'll always find bugs in it and those bugs are made word by multi-character closing tags.

(And that's before you get into things like "Vim doesn't maintain a Concrete Syntax Tree representation which would be necessary to keep track of tags many lines apart in a fashion that's both performant and reliable".)

Also since Rust frequently use braces, using separators like {% %} {{ }} leads to unreadable and even buggy code.

I meant to say "I understand why you chose it but..." but that got missed somehow, so sorry about that. Maybe I should have used Mako as an example instead, which uses ${...} where you use <%= %>, % ... on a line of its own where you use <% %>, and uses <% ... %> for stuff like setting variables at the top of the template.

My point is that Eruby/EJS/ECT template syntax is hardly universal and, given how popular Django, Jekyll (thanks to GitHub Pages), and various clones of the Django template engine (eg. Twig, Jinja2) are, it may not even be the most popular.

Sailfish haven't supported this yet, but this is an interesting feature! I've opened a new tracking issue to achive this.

Thanks. :)

I hope @Svenskunganka comes through with that PR to switch it to TOML. I don't want to have to add any more YAML to my projects than absolutely necessary and, so far, I've managed to get away with not having a sailfish.yml.

Yes, we can use existing HTML parsers to achieve that.

Do existing HTML parsers deal well with this actual snip from my template?

      <details class="description"
          <% if !folder_meta.collapse_description { %>open<% } %>>

@Svenskunganka
Copy link
Contributor

I hope @Svenskunganka comes through with that PR to switch it to TOML. I don't want to have to add any more YAML to my projects than absolutely necessary and, so far, I've managed to get away with not having a sailfish.yml.

@ssokolow It's on my radar, but some other stuff have to take priority. I'm currently in the process of moving up north, so I'm busy packing things and preparing and will be moving the 29-30th of January and hope to be completely setup at the start of February. To make things worse, a wheel bearing on my car has gone bad and a button used to control the diesel heater is unresponsive which I need so my car will start in those temperatures (it's -10°C to -30°C up there at this time of year). I've ordered replacement parts which should hopefully arrive next week so I can replace them.
If time permits, I might be able to finalize a PR before February, otherwise it'll have to wait. Hopefully that's OK.

@ssokolow
Copy link
Author

Real life is always more important... especially when my use-case is for a hobby project.

Svenskunganka added a commit to Svenskunganka/sailfish that referenced this issue Jan 10, 2021
Closes rust-sailfish#38

`yaml-rust` and its dep `linked-hash-map` (with your PR merged) has been updated. This solves the immediate issue in rust-sailfish#38 but I'm still commited to creating a PR migrating from YAML to TOML when I get more time for it, see [my comment](rust-sailfish#41 (comment)) for why and when that will be.
@Kogia-sima
Copy link
Member

Kogia-sima commented Jan 11, 2021

@ssokolow

And I'm of the camp who firmly believe that Rust is not Java and we should not need to rely on the editor to make our language syntaxes usable. That's lazy design. Design the tool to work well enough on its own, then let the editor improve upon that.

If you're going to develop a new programming language, it is true. There are many Rust users and third party tools to support development.

However Sailfish is a template engine, which is only used in a limited fields. If there were few users writing Rust and lacks many external tools, then you would not choose Rust. Therefore adding a new syntax is over-hacking for now.

Heck, I find that, if you use any closing tag generation enough, you'll always find bugs in it and those bugs are made word by multi-character closing tags.

(And that's before you get into things like "Vim doesn't maintain a Concrete Syntax Tree representation which would be necessary to keep track of tags many lines apart in a fashion that's both performant and reliable".)

Sorry I can't reproduce this. emmet-vim and vim-closetag both seems work well with multi-character tags.

Maybe I should have used Mako as an example instead, which uses ${...} where you use <%= %>, % ... on a line of its own where you use <% %>, and uses <% ... %> for stuff like setting variables at the top of the template.

Oh, I didn't know Mako. It seems Mako is designed for indentation-based language, but I seems Mako is somewhat designed for indentation-based and dynamically-typed languages (e.g. arguments for %include, code block beggining with single '%' character).

My point is that Eruby/EJS/ECT template syntax is hardly universal and, given how popular Django, Jekyll (thanks to GitHub Pages), and various clones of the Django template engine (eg. Twig, Jinja2) are, it may not even be the most popular.

I've never said that Eruby-base syntax is more popular than jinja-based syntax, but at least it is not hardly universal, and also it is traditionally used in various applition since I was child.

Do existing HTML parsers deal well with this actual snip from my template?

Yes. We can process embedded code blocks before feeding templates into HTML parsers.

@ssokolow
Copy link
Author

However Sailfish is a template engine, which is only used in a limited fields. If there were few users writing Rust and lacks many external tools, then you would not choose Rust. Therefore adding a new syntax is over-hacking for now.

Again, we've come to agree that there's too much experimentation left to be done in this space to bless one specific alternative syntax now. I just want Sailfish to be as friendly as possible to preprocessors.

(If Sailfish weren't twice as fast as its closest competitor, I would never have chosen it, because it's much easier to fix the template equivalent of coding in C with a transpiler than to speed up a slow template renderer that already has a more desirable syntax.)

Sorry I can't reproduce this. emmet-vim and vim-closetag both seems work well with multi-character tags.

That's the problem. Same as with KDE 3.x users/developers calling GNOME 2.x a crash-happy mess and GNOME 2.x developers/users calling KDE 3.x a crash-happy mess, it really depends on how you use the tool. If you use the tool the same way the developer does, you're very likely to never see any lurking bugs.

In Vim's case, it's more that, because Vim doesn't have a proper plugin API, it's very easy to create bugs just by using a novel combination of plugins.

Oh, I didn't know Mako. It seems Mako is designed for indentation-based language, but I seems Mako is somewhat designed for indentation-based and dynamically-typed languages (e.g. arguments for %include, code block beggining with single '%' character).

Yes. I was just using it as an example.

I've never said that Eruby-base syntax is more popular than jinja-based syntax, but at least it is not hardly universal, and also it is traditionally used in various applition since I was child.

An appeal to tradition is not a good argument. Pascal, BASIC, and C-style increment/decrement operators also used to be traditionally used in various applications.

Yes. We can process embedded code blocks before feeding templates into HTML parsers.

And now you're back to being Miri or ASan (i.e. you have to actually run that code path to catch the bug) while I want safe Rust (i.e. the constraints posed by the syntax allow the compiler to preclude that entire class of bugs for all possible inputs).

@ahdyt
Copy link

ahdyt commented May 29, 2023

Hi there, is this issue also related to minified output? I would like to use sailfish not only generate the html but also minify it.

@leeola
Copy link

leeola commented Jun 13, 2023

Hi there, is this issue also related to minified output? I would like to use sailfish not only generate the html but also minify it.

@ahdyt No idea to your answer, but i experimentally achieved this by using a minifier (html_minifier specifically) on the templates(!) during the build process. It's seamless, and works.. so far.

With that said this is far from optimal. I've already found an issue where the minifier broke some code due to a <% /* comment */ %> line. .. i kinda think that makes it a bad idea so.. you've been warned, hah.

I agree though, minifying the output optionally would be really great. Probably worth opening an issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
good first issue Good for newcomers Status: Proposal Request for comments Type: Feature New Feature
Projects
None yet
Development

No branches or pull requests

5 participants