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

Add support for importing template files with macros #51

Merged
merged 2 commits into from Sep 26, 2017

Conversation

larros
Copy link
Contributor

@larros larros commented Sep 24, 2017

This is a try at an implementation of #40.

I've intentionally kept this as limited as possible because I'm not sure what functionality that should be included. Compared to Jinja there's no support for "import as" or "from import", instead all macros are added to the same namespace.

I first tried to implement this in generator.rs as some combination of the include and macro functionality but I couldn't get that past the borrow checker. The macros need to exist for the full generation and because they contain slices of the imported sources the imports need to be parsed before the generation starts. In the end I put the ownership of the imported sources and nodes in lib.rs in the same way as for the parent template.

Copy link
Owner

@djc djc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice! Thanks for working on this. Please review my comments and let me know what you think.

@@ -12,8 +12,8 @@ use std::collections::{HashMap, HashSet};
use syn;


pub fn generate(input: &TemplateInput, nodes: &[Node]) -> String {
Generator::default().build(&State::new(input, nodes))
pub fn generate(input: &TemplateInput, nodes: &[Node], imported_nodes: &[Node]) -> String {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: how do you feel about just calling this imported? imported_notes feels a little unwieldy.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good, but I will also change it to containing the macros directly.

@@ -129,6 +131,34 @@ impl<'a> TemplateMeta<'a> {
}
}

pub struct Imports<'a> {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given the way this is used, and the fact it relies on the parser, I think putting it in input is probably not quite right. Can you move it to lib.rs, between build_template() and the errors module?

}

impl <'a> Imports<'a> {
pub fn new(parent_nodes: &'a [Node], parent_path: &'a Path) -> Imports<'a> {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we have three stages here:

  • Imports::new() finds import statements and gets the template sources for them
  • Imports::parse() turns the sources into Vec<Node>s
  • generator::State::new() finds the Node::Macros and puts them into the macro map

Is there any reason not do all in one place, so something like a get_imported_macros() that takes parent_nodes and parent_path and returns a MacroMap?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Upon reflection, I guess the template sources have to be preserved, since the AST nodes depend on them. Still, I think we can extract the Macro nodes earlier?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The first step is needed for ownership reasons but I think the other two can be combined. Then Imports::parse() would return the macros directly.

@@ -0,0 +1,2 @@
{% import "macro.html" -%}
{%- call thrice(s) %}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: please add a trailing newline here.

@@ -0,0 +1,16 @@
#[macro_use]
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: please just append this test case to macro.rs.

@larros
Copy link
Contributor Author

larros commented Sep 25, 2017

I've tried to fix all comments now.

I have one thing that needs to be decided on before completing this.
If namespaces for imported macros should be forced, like they are in Jinja, that should be added now to not cause a backwards incompatible change later.

@djc
Copy link
Owner

djc commented Sep 25, 2017

Looks good! Yes, I don't want to release this without forcing namespacing, but I think it would be nice to merge this into master now so that we can then take the next step separately. Okay?

@larros
Copy link
Contributor Author

larros commented Sep 25, 2017

That's fine with me.
Do you have any ideas of the namespacing syntax? Jinja's "import as" feels very pythonic to me so maybe it's not the correct solution here.

@djc
Copy link
Owner

djc commented Sep 25, 2017

My policy has been to stay close to Jinja's syntax except where it does not make sense because there's no alignment with Rust. We can either do import as here, or maybe use as would be more Rusty. Do you have any alternatives in mind? @anowell / @Eijebong, any opinions?

@larros
Copy link
Contributor Author

larros commented Sep 25, 2017

Now when I think more about this I don't think this is a place where it's worth it to divert from Jinja. There is still a need to express the same intent so just swapping words seems like a bad idea.

@Eijebong
Copy link
Contributor

Eijebong commented Sep 25, 2017

What about doing the same thing as tera ?

{% import "menu" as menu %}
{{ menu::app_menu_item(...) }}

@djc
Copy link
Owner

djc commented Sep 25, 2017

Does tera do something magic with extensions? I definitely wouldn't want that. Using :: instead of . might make sense though, and would actually make parsing easier.

@Eijebong
Copy link
Contributor

Yeah, I don't like the magic extension thingy either (I'm not sure if it's even needed anymore), I just copy/pasted it from one of my templates. I like the :: though, it feels rusty :p

@larros
Copy link
Contributor Author

larros commented Sep 25, 2017

I like ::.

@djc djc merged commit df58dcb into djc:master Sep 26, 2017
@djc
Copy link
Owner

djc commented Sep 26, 2017

Looks like we have consensus on the scoped import syntax. I merged the base part already, since it seems to provide a good basis already.

@djc
Copy link
Owner

djc commented Oct 14, 2017

@larros are you still up for finishing the implementation of the ::-based scoped syntax sometime soon? If not, that's totally fine too -- I can probably just complete it.

@larros
Copy link
Contributor Author

larros commented Oct 14, 2017

I have some time tomorrow so I can make a try.

@jmafc
Copy link

jmafc commented Mar 24, 2020

Was the "import as" syntax shown above, with the :: scoped calling ever implemented? I have a very similar example.

{% import "navigation.html" as navigation -%}
...
{{ navigation.secmenu() }}

If I try to build, I get error[E0609]: no field navigation on type .... If I replace the dot by ::, the message is error: proc-macro derive panicked ... help: message: unable to parse template. If I comment the second line, it builds but of course without the menu.
If it's still not implemented, is there some workaround? I have five import files, each with multiple macros.

@djc
Copy link
Owner

djc commented Mar 24, 2020

@jmafc look at the code from one of the test cases:

{%- import "macro.html" as scope -%}

{% call scope::thrice(s) %}

In other words, it should be :: rather than ., but macros are also called with {% call %} rather than as a simple function expression (exactly because the parser wouldn't be able to distinguish between a Rust function call and the macro invocation).

@jmafc
Copy link

jmafc commented Mar 24, 2020

@djc Thanks

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

Successfully merging this pull request may close these issues.

None yet

4 participants