-
Notifications
You must be signed in to change notification settings - Fork 56
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
Comments
Random brainstorming for the "catches tag typos" part of such an idea. Markup.rs has
(If available, a |
@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
I think that modern editor can prevent developers from writing invalid tag names. Here is the screenshot from my vim. In this case the invalid HTML tag name ( Also
Sometimes I intentionally insert a whitespaces or line breaks between If you like to write templates in another format, the better solution would be developing a preprocessor to convert from another format (such as Also If your HTML can be tested on local machine, I think, this RFC can solve all of the issue stated here. |
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 use Vim too, but I wouldn't want to try to bodge Vim into the CI runs for PRs.
Of course... bearing in mind that it's not a high priority for me personally, because my Worst-case scenario, require it to
Same for me, which is why I want something that generates no whitespace unless requested, and then I can use something like
I actually addressed that in my initial message. I'll rephrase it slightly:
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 |
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'm sorry, but I don't understand what you said. Could you explain it in more detail?
Yeah I just misunderstood what you said... Thanks for providing a concrete example.
I don't think it's an ugly hack. Embedding the code generated by
You mean you cannot feed even dummy data into the templates? Also |
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 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
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
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
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 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 |
I don't think 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'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.
Shell supports piping the program output to another program. Also even if
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.
Unfortunately Sailfish is not designed to achieve this feature. It requires calling |
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.
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.
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 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.
That's unfortunate. |
I know that I'm sorry that my explanation wasn't good so that it leads to misunderstanding.
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.
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
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.
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. |
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
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
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
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
Ahh. Fair enough. |
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
We're not talking about separator for embedded code. It's about syntax for non-embedded code. Also,
Sailfish haven't supported this yet, but this is an interesting feature! I've opened a new tracking issue to achive this.
Yes, we can use existing HTML parsers to achieve that. |
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".)
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 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.
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
Do existing HTML parsers deal well with this actual snip from my template? <details class="description"
<% if !folder_meta.collapse_description { %>open<% } %>> |
@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. |
Real life is always more important... especially when my use-case is for a hobby project. |
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.
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.
Sorry I can't reproduce this. emmet-vim and vim-closetag both seems work well with multi-character tags.
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).
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.
Yes. We can process embedded code blocks before feeding templates into HTML parsers. |
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.)
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.
Yes. I was just using it as an example.
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.
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). |
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 I agree though, minifying the output optionally would be really great. Probably worth opening an issue. |
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 ofmarkup::define!
to Sailfish.stpl
before Sailfish sees it... and having to do it in abuild.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.
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?
(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.)The text was updated successfully, but these errors were encountered: