Skip to content

Allow specifying variables in include - #553

Merged
yamgent merged 10 commits into
MarkBind:masterfrom
jamos-tay:included-variables
Jan 28, 2019
Merged

Allow specifying variables in include#553
yamgent merged 10 commits into
MarkBind:masterfrom
jamos-tay:included-variables

Conversation

@jamos-tay

@jamos-tayjamos-tay commented Jan 5, 2019

Copy link
Copy Markdown
Contributor

What is the purpose of this pull request? (put "X" next to an item, remove the rest)

• [X] Enhancement to an existing feature

Fixes#545

What is the rationale for this request?

Users may want to create include file templates with variables that can be filled in, allowing reuse. However, they should only apply to each file. For example,

## {{ title }}
This is a level {{ level }} topic.

Allow users to specify variables in an include tag

What changes did you make? (Give an overview)

Allow users to specify Markbind variables in an include tag:

<include src=”foo.md”>
<span id="title"><span>New Title</span></span> // same syntax as variables.md
<span id="level">2</span>
</include>

These add on to (and overwrite) variables in _markbind/variables.md, and only apply to the context of the included file, and any files that the file includes.

We use nunjuck's or (default) syntax to allow for default values in case the variable is missing: {{ title or "Untitled" }}

Is there anything you'd like reviewers to focus on?

Users cannot reference other variables in the include, like this:

<include src=”foo.md”>
<span id="inner">Inner</span>
<span id="outer">Outer and {{ inner }}</span>
</include>

This is because {{ inner }} gets cleared when the original file is rendered. We can have a separate syntax for these, but it's a little unwieldly.

Referencing variables in _markbind/variables.md is okay.

@jamos-tayjamos-tay changed the title Allow specifying variables in include[WIP] Allow specifying variables in includeJan 5, 2019
@jamos-tayjamos-tay changed the title [WIP] Allow specifying variables in includeAllow specifying variables in includeJan 5, 2019
@damithc

Copy link
Copy Markdown
Contributor

Does this work too? It's not related to includes but possibly related to how the feature is implemented.

<spanid="color">red</span><fontcolor="{{ color }}">my text</font>

@yamgent
yamgent self-requested a review January 6, 2019 02:56
@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

@damithc Yes, tested and works

@damithc

Copy link
Copy Markdown
Contributor

This PR affects the following statement, right?
image

Comment threaddocs/userGuide/reusingContents.md Outdated
Comment threaddocs/userGuide/reusingContents.md Outdated
Comment threaddocs/userGuide/reusingContents.md Outdated
Comment threadsrc/lib/markbind/src/parser.js Outdated
Comment threadsrc/lib/markbind/src/parser.js Outdated
Comment threadtest/test_site/index.md Outdated
Comment threadtest/test_site/testIncludeVariableLeak.md Outdated
Comment threadtest/test_site/testIncludeVariables.md Outdated
@acjh

acjh commented Jan 8, 2019

Copy link
Copy Markdown
Contributor

Please use meaningful commit messages instead of "Pr fixes". 🙈

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Sorry =/

I'll do a quick rebase after it's approved

Comment threaddocs/userGuide/reusingContents.md Outdated

<hr><!-- ======================================================================================================= -->

#### Specifying Variables in the Include Tag

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

  1. Let's follow the same heading level as "<include> Inside an Included File".
  2. Let's use "<include>" rather than "Include Tag" to follow the style of the rest of the documentation.
##### Specifying Variables in an `<include>`

Comment threaddocs/userGuide/reusingContents.md Outdated

#### Specifying Variables in the Include Tag

**It is possible to include variables in the include tag.**

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

**It is possible to declare variables in an `<include>`.**

Comment threaddocs/userGuide/reusingContents.md Outdated

<div class="indented">

{{ icon_example }} Specifying `title` and `author` variables in an `include` tag:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

... in an `<include>` tag:

Comment threaddocs/userGuide/reusingContents.md Outdated
</div>


These variables work the same way as variables in `_markbind/variables.md`, except they only apply to the included file. This can be helpful for creating a template for multiple files.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

  1. Fixing the grammar.
  2. Rephrased the explanation of it being a template mechanism.
..., except that they only apply to the included file. They allow the included file to be reused as a template, for different source files using different variable values.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Side question: do they also work as nunjucks variables? e.g., {{ (size | int) - 1 }} would evaluate to 3 if <span id="size">4</span>

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Tested and it works - but do we want to document this? It seems like a small enough case, and it might confuse the user since it introduces another api

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We already claim that MarkBind is generally compatible with nunjucks syntax. Here, we are using nunjucks syntax for a MarkBind feature. It's better to clarify that these variables are in fact equivalent to nunjucks variables declared within the scope of the included file. However, it can be mentioned in https://markbind.github.io/markbind/userGuide/markBindSyntax.html#support-for-nunjucks rather than under this feature. What do you think?

@damithc

Copy link
Copy Markdown
Contributor

Does this work too? It's not related to includes but possibly related to how the feature is implemented.

<span id="color">red</span>
<font color="{{ color }}">my text</font>

@jamos-tay make sure this is reflected somewhere in the docs too. i.e., together with the d-none attribute, this provides a convenient way to declare local variables and use them in a page.

@jamos-tay

jamos-tay commented Jan 10, 2019

Copy link
Copy Markdown
ContributorAuthor

@jamos-tay make sure this is reflected somewhere in the docs too. i.e., together with the d-none attribute, this provides a convenient way to declare local variables and use them in a page.

@damithc Sorry I think I misunderstood, were you referring to

<span id="color">red</span>
<font color="{{ color }}">my text</font>

within the same .md file? Because that doesn't work... I thought you were asking if you could put variables as element attributes so I put the span in variables.md

I don't think there's a way to create a local variable for a page just yet, but it probably should be in its own PR. I'm pretty sure it requires separate logic (such as placing in the frontmatter) because of the way nunjucks is called.

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Updated

@damithc

Copy link
Copy Markdown
Contributor

within the same .md file? Because that doesn't work... I thought you were asking if you could put variables as element attributes.

I don't think there's a way to create a local variable for a page just yet, but it should be put it in a different PR. I'm pretty sure it requires separate logic (such as placing in the frontmatter) because of the way nunjucks is called.

I see. I thought we got that feature for free. Never mind then. It can be a feature to be added in the future.

Comment threaddocs/userGuide/reusingContents.md Outdated

{{ icon_example }} Specifying a variable as an attribute:

<code>\<span id="color">red</span>\</code><br>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The wrong HTML tag is escaped, it didn't render correctly.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Also it is not clear that the two different code exist in two different places, especially when you look at this portion in the live preview. Maybe indicate one is in variables.md and one is in index.md?

Comment threaddocs/userGuide/reusingContents.md Outdated

<code>\<span id="color">red</span>\</code><br>

<code>\<font color="{<span></span>{ color }}">my text</font>\</code>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Same problem (wrong HTML tag escaped).

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Updated, I decided to delete that section since there was a misunderstanding.

Comment threadsrc/lib/markbind/src/parser.js Outdated
if (!child.attribs.id) {
// eslint-disable-next-line no-console
console.warn(`Missing reference in ${element.attribs[ATTRIB_CWF]}\n`
+ `Missing 'id' in variable for ${element.attribs.src} include.`);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

  1. The "Missing 'id'" part should actually be in front, in order to match the style of other error messages that we show as well.
  2. I realise that the "Missing reference..." should be rephrased (because it is not a missing reference, but rather, a missing variable name), apologies about that.

So it should be something like this:

diff --git a/src/lib/markbind/src/parser.js b/src/lib/markbind/src/parser.js
index 69c40e5..b71b50f 100644
--- a/src/lib/markbind/src/parser.js+++ b/src/lib/markbind/src/parser.js@@ -215,8 +215,8 @@ Parser.prototype._preprocess = function (node, context, config) {
}
if (!child.attribs.id) {
// eslint-disable-next-line no-console
- console.warn(`Missing reference in ${element.attribs[ATTRIB_CWF]}\n`- + `Missing 'id' in variable for ${element.attribs.src} include.`);+ console.warn(`Missing 'id' in variable for referenced file: ${element.attribs.src}.\n`+ + `Missing variable name in ${element.attribs[ATTRIB_CWF]}\n`);
return;
}
includedVariables[child.attribs.id] = cheerio.html(child.children);

@damithc

Copy link
Copy Markdown
Contributor

@jamos-tay do look into how variables propagate via nested includes. Ideally, the variable value should apply to nested includes too and outermost value should override values specified in inner includes.

@yamgent

Copy link
Copy Markdown
Member

Ideally, the variable value should apply to nested includes too and outermost value should override values specified in inner includes.

Do you mean that for the following scenario, the final output should be "Original variable"? The behaviour would look very odd to authors, since the inner page that is "closer" to the final included page should be allowed to have a say over the final variable value, rather than the outer page.


index.md

<includesrc="a.md"><spanid="someVariable">Original variable</span></include>

a.md

<includesrc="b.md"><spanid="someVariable">Nested include variable</span></include>

b.md

{{someVariable}}

@damithc

Copy link
Copy Markdown
Contributor

Do you mean that for the following scenario, the final output should be "Original variable"? The behaviour would look very odd to authors, since the inner page that is "closer" to the final included page should be allowed to have a say over the final variable value, rather than the outer page.

Yes, that feels odd but from a reuse perspective that should be the behavior (opposite of how inheritance works). Otherwise, someone who wants to resue this bit of code has no way to customize the code without modifying it directly, right?

@yamgent

Copy link
Copy Markdown
Member

Otherwise, someone who wants to resue this bit of code has no way to customize the code without modifying it directly, right?

That is indeed true, I think that is the desirable behaviour.

In order to make it easier to explain this behaviour, maybe we should consider adopting a different term rather than calling them all "variables"? Maybe "page variables" vs "global variables"? So we can say that "If a page uses a {{variable}}, then:

  1. Page variables override global variables.
  2. Outer page variables override inner page variables.

@damithc

Copy link
Copy Markdown
Contributor

In order to make it easier to explain this behaviour, maybe we should consider adopting a different term rather than calling them all "variables"?

That is a good idea. One issue is that we use the exact syntax used for nunjucks variables. Are we using nunjucks behind the scene to implement variables? This could become a problem because nunjucks variables are likely to behave differently from ours and users don't have a way to tell the two apart (note that a MarkBind file can contain some nunjucks code too). Is it possible to use a slightly different syntax e.g., {< foo >}?

  1. Page variables override global variables.

This should be the other way around too?

Maybe "page variables" vs "global variables"?

Or stay away from variables altogether. customization?

@yamgent

Copy link
Copy Markdown
Member

That is a good idea. One issue is that we use the exact syntax used for nunjucks variables. Are we using nunjucks behind the scene to implement variables? This could become a problem because nunjucks variables are likely to behave differently from ours and users don't have a way to tell the two apart (note that a MarkBind file can contain some nunjucks code too). Is it possible to use a slightly different syntax e.g., {< foo >}?

Yes, we are using nunjucks behind the scene to implement variables.

And yes there is a chance the authors may mix them up. We can explore using a different syntax in another PR (that would be a breaking change)?

This should be the other way around too?

Got it, global ones should have the highest priority. @jamos-tay you will have to rework this. :P

@damithc

Copy link
Copy Markdown
Contributor

And yes there is a chance the authors may mix them up. We can explore using a different syntax in another PR (that would be a breaking change)?

It would break the syntax introduced in this PR, right? :-) If the new syntax is introduced in this PR, it will not break existing features.
Anyway, it is a big change, and I'm not sure how hard it is to implement, especially if we cannot piggyback on nunjucks anymore. We can discuss on Saturday.

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Hmm... I think it might be possible to reuse the same syntax. I just have to check that if a variable already exists (i.e. declared by an outer file), don't overwrite it.

I'll need a way to detect whether a variable is a global or page variable though, but I think it can be done without using a hacky solution.

@damithc

Copy link
Copy Markdown
Contributor

Hmm... I think it might be possible to reuse the same syntax. I just have to check that if a variable already exists (i.e. declared by an outer file), don't overwrite it.

Reusing the same syntax is appropriate only if a normal nunjucks variable behaves exactly the same way as the ones used for includes. Are we going to apply the same outermost-overrides behavior to normal nunjucks variables as well? i.e., those declared using {% set x = 'foo' %}

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Hmm... Nunjucks overwrites, so it'll use the inner most by default. We might be able to write an extension that will overwrite set's behavior. Alternatively we could strip out {% set %} tags and parse them manually.

If not, are we considering moving the entire variable system off nunjucks? i.e. {< variable >} everywhere? That might be a lot of work just for this feature... considering the global variables.md would have to be migrated over, we'd have to write our own parser logic, and also all older sites would break.

@damithc

Copy link
Copy Markdown
Contributor

Hmm... Nunjucks overwrites, so it'll use the inner most by default. We might be able to write an extension that will overwrite set's behavior. Alternatively we could strip out {% set %} tags and parse them manually.

If not, are we considering moving the entire variable system off nunjucks? i.e. {< variable >} everywhere? That might be a lot of work just for this feature... considering the global variables.md would have to be migrated over, we'd have to write our own parser logic, and also all older sites would break.

Yup, this is a big decision. Better to make it now rather than later but we have to think it through. Although the issue came up in this PR, we are actually dealing with a much wider issue, namely, the need to support the outer-overrides-inner behavior that is central to reuse but opposite of the normal template behavior. The problem seem to be that we cannot support two opposing behaviors using the same internal library and/or syntax.

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

After discussion:

We'll go with the original behavior:
Global variables override included variables
Outer include variables override inner include variables
{% set ... %} overrides all variables, but only applies to the page (excluding includes)

E.g.
variables.md: a = 1

a.md

<include src = b.md>
a = 2
b = 2
</include>

b.md

{{ a }} // 1
{{ b }} // 2
{% set a = 4 %}
{{ a }} // 4
<include src = c.md>
a = 3
b = 3
</include>

c.md

{{ a }} // 1
{{ b }} // 2

Comment threadsrc/lib/markbind/src/parser.js Outdated
@jamos-tay

jamos-tay commented Jan 19, 2019

Copy link
Copy Markdown
ContributorAuthor

Updated

Note: We currently do not support include variables referencing variables within the same include:

index.md:

<includesrc="a.md"><spanid="a">a</span><spanid="b">b & {{ a }}</span></include>

This is because {{ a }} gets rendered to an empty string when nunjucks processes index.md. Making this work would require some rework the page generation logic, which should be in another PR. This rework is also necessary to handle certain cases with set, so it can be fixed along with that.

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Rebased on master

Comment threaddocs/userGuide/reusingContents.md Outdated
Comment threaddocs/userGuide/reusingContents.md
Comment threadsrc/lib/markbind/src/parser.js Outdated
Comment threadsrc/lib/markbind/src/parser.js Outdated
Comment threadsrc/lib/markbind/src/parser.js Outdated
Comment threadtest/test_site/index.md Outdated
Comment threadtest/test_site/testIncludeVariables.md Outdated
Comment threaddocs/userGuide/reusingContents.md
Rename element to includeElement
Fix typos in tests
@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Updated

@yamgent
yamgent self-requested a review January 23, 2019 16:11

@yamgentyamgent left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Some more nits.

Comment threaddocs/userGuide/reusingContents.md Outdated
Comment threaddocs/userGuide/reusingContents.md Outdated
Comment threadtest/test_site/_markbind/variables.md Outdated
Comment threadtest/test_site/index.md Outdated
@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Updated

@yamgentyamgent added this to the v1.17.1 milestone Jan 26, 2019
@yamgent
yamgent merged commit 1eae09f into MarkBind:masterJan 28, 2019
@damithc

Copy link
Copy Markdown
Contributor

Applied this in production nus-cs3281/2019@0d04ee4

Nice work @jamos-tay and reviewers @acjh@yamgent 👍

marvinchin pushed a commit to marvinchin/markbind that referenced this pull request Jan 31, 2019
@yamgentyamgent mentioned this pull request Mar 11, 2019
3 tasks
@ang-zeyuang-zeyu mentioned this pull request Sep 13, 2020
Sign up for freeto 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.

Allow customizing at the point of inclusion

4 participants

@jamos-tay@damithc@acjh@yamgent
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Allow specifying variables in include by jamos-tay · Pull Request #553 · MarkBind/markbind · GitHub
Skip to content

Allow specifying variables in include - #553

Merged
yamgent merged 10 commits into
MarkBind:masterfrom
jamos-tay:included-variables
Jan 28, 2019
Merged

Allow specifying variables in include#553
yamgent merged 10 commits into
MarkBind:masterfrom
jamos-tay:included-variables

Conversation

@jamos-tay

@jamos-tayjamos-tay commented Jan 5, 2019

Copy link
Copy Markdown
Contributor

What is the purpose of this pull request? (put "X" next to an item, remove the rest)

• [X] Enhancement to an existing feature

Fixes#545

What is the rationale for this request?

Users may want to create include file templates with variables that can be filled in, allowing reuse. However, they should only apply to each file. For example,

## {{ title }}
This is a level {{ level }} topic.

Allow users to specify variables in an include tag

What changes did you make? (Give an overview)

Allow users to specify Markbind variables in an include tag:

<include src=”foo.md”>
<span id="title"><span>New Title</span></span> // same syntax as variables.md
<span id="level">2</span>
</include>

These add on to (and overwrite) variables in _markbind/variables.md, and only apply to the context of the included file, and any files that the file includes.

We use nunjuck's or (default) syntax to allow for default values in case the variable is missing: {{ title or "Untitled" }}

Is there anything you'd like reviewers to focus on?

Users cannot reference other variables in the include, like this:

<include src=”foo.md”>
<span id="inner">Inner</span>
<span id="outer">Outer and {{ inner }}</span>
</include>

This is because {{ inner }} gets cleared when the original file is rendered. We can have a separate syntax for these, but it's a little unwieldly.

Referencing variables in _markbind/variables.md is okay.

@jamos-tayjamos-tay changed the title Allow specifying variables in include[WIP] Allow specifying variables in includeJan 5, 2019
@jamos-tayjamos-tay changed the title [WIP] Allow specifying variables in includeAllow specifying variables in includeJan 5, 2019
@damithc

Copy link
Copy Markdown
Contributor

Does this work too? It's not related to includes but possibly related to how the feature is implemented.

<spanid="color">red</span><fontcolor="{{ color }}">my text</font>

@yamgent
yamgent self-requested a review January 6, 2019 02:56
@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

@damithc Yes, tested and works

@damithc

Copy link
Copy Markdown
Contributor

This PR affects the following statement, right?
image

Comment threaddocs/userGuide/reusingContents.md Outdated
Comment threaddocs/userGuide/reusingContents.md Outdated
Comment threaddocs/userGuide/reusingContents.md Outdated
Comment threadsrc/lib/markbind/src/parser.js Outdated
Comment threadsrc/lib/markbind/src/parser.js Outdated
Comment threadtest/test_site/index.md Outdated
Comment threadtest/test_site/testIncludeVariableLeak.md Outdated
Comment threadtest/test_site/testIncludeVariables.md Outdated
@acjh

acjh commented Jan 8, 2019

Copy link
Copy Markdown
Contributor

Please use meaningful commit messages instead of "Pr fixes". 🙈

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Sorry =/

I'll do a quick rebase after it's approved

Comment threaddocs/userGuide/reusingContents.md Outdated

<hr><!-- ======================================================================================================= -->

#### Specifying Variables in the Include Tag

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

  1. Let's follow the same heading level as "<include> Inside an Included File".
  2. Let's use "<include>" rather than "Include Tag" to follow the style of the rest of the documentation.
##### Specifying Variables in an `<include>`

Comment threaddocs/userGuide/reusingContents.md Outdated

#### Specifying Variables in the Include Tag

**It is possible to include variables in the include tag.**

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

**It is possible to declare variables in an `<include>`.**

Comment threaddocs/userGuide/reusingContents.md Outdated

<div class="indented">

{{ icon_example }} Specifying `title` and `author` variables in an `include` tag:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

... in an `<include>` tag:

Comment threaddocs/userGuide/reusingContents.md Outdated
</div>


These variables work the same way as variables in `_markbind/variables.md`, except they only apply to the included file. This can be helpful for creating a template for multiple files.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

  1. Fixing the grammar.
  2. Rephrased the explanation of it being a template mechanism.
..., except that they only apply to the included file. They allow the included file to be reused as a template, for different source files using different variable values.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Side question: do they also work as nunjucks variables? e.g., {{ (size | int) - 1 }} would evaluate to 3 if <span id="size">4</span>

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Tested and it works - but do we want to document this? It seems like a small enough case, and it might confuse the user since it introduces another api

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We already claim that MarkBind is generally compatible with nunjucks syntax. Here, we are using nunjucks syntax for a MarkBind feature. It's better to clarify that these variables are in fact equivalent to nunjucks variables declared within the scope of the included file. However, it can be mentioned in https://markbind.github.io/markbind/userGuide/markBindSyntax.html#support-for-nunjucks rather than under this feature. What do you think?

@damithc

Copy link
Copy Markdown
Contributor

Does this work too? It's not related to includes but possibly related to how the feature is implemented.

<span id="color">red</span>
<font color="{{ color }}">my text</font>

@jamos-tay make sure this is reflected somewhere in the docs too. i.e., together with the d-none attribute, this provides a convenient way to declare local variables and use them in a page.

@jamos-tay

jamos-tay commented Jan 10, 2019

Copy link
Copy Markdown
ContributorAuthor

@jamos-tay make sure this is reflected somewhere in the docs too. i.e., together with the d-none attribute, this provides a convenient way to declare local variables and use them in a page.

@damithc Sorry I think I misunderstood, were you referring to

<span id="color">red</span>
<font color="{{ color }}">my text</font>

within the same .md file? Because that doesn't work... I thought you were asking if you could put variables as element attributes so I put the span in variables.md

I don't think there's a way to create a local variable for a page just yet, but it probably should be in its own PR. I'm pretty sure it requires separate logic (such as placing in the frontmatter) because of the way nunjucks is called.

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Updated

@damithc

Copy link
Copy Markdown
Contributor

within the same .md file? Because that doesn't work... I thought you were asking if you could put variables as element attributes.

I don't think there's a way to create a local variable for a page just yet, but it should be put it in a different PR. I'm pretty sure it requires separate logic (such as placing in the frontmatter) because of the way nunjucks is called.

I see. I thought we got that feature for free. Never mind then. It can be a feature to be added in the future.

Comment threaddocs/userGuide/reusingContents.md Outdated

{{ icon_example }} Specifying a variable as an attribute:

<code>\<span id="color">red</span>\</code><br>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The wrong HTML tag is escaped, it didn't render correctly.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Also it is not clear that the two different code exist in two different places, especially when you look at this portion in the live preview. Maybe indicate one is in variables.md and one is in index.md?

Comment threaddocs/userGuide/reusingContents.md Outdated

<code>\<span id="color">red</span>\</code><br>

<code>\<font color="{<span></span>{ color }}">my text</font>\</code>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Same problem (wrong HTML tag escaped).

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Updated, I decided to delete that section since there was a misunderstanding.

Comment threadsrc/lib/markbind/src/parser.js Outdated
if (!child.attribs.id) {
// eslint-disable-next-line no-console
console.warn(`Missing reference in ${element.attribs[ATTRIB_CWF]}\n`
+ `Missing 'id' in variable for ${element.attribs.src} include.`);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

  1. The "Missing 'id'" part should actually be in front, in order to match the style of other error messages that we show as well.
  2. I realise that the "Missing reference..." should be rephrased (because it is not a missing reference, but rather, a missing variable name), apologies about that.

So it should be something like this:

diff --git a/src/lib/markbind/src/parser.js b/src/lib/markbind/src/parser.js
index 69c40e5..b71b50f 100644
--- a/src/lib/markbind/src/parser.js+++ b/src/lib/markbind/src/parser.js@@ -215,8 +215,8 @@ Parser.prototype._preprocess = function (node, context, config) {
}
if (!child.attribs.id) {
// eslint-disable-next-line no-console
- console.warn(`Missing reference in ${element.attribs[ATTRIB_CWF]}\n`- + `Missing 'id' in variable for ${element.attribs.src} include.`);+ console.warn(`Missing 'id' in variable for referenced file: ${element.attribs.src}.\n`+ + `Missing variable name in ${element.attribs[ATTRIB_CWF]}\n`);
return;
}
includedVariables[child.attribs.id] = cheerio.html(child.children);

@damithc

Copy link
Copy Markdown
Contributor

@jamos-tay do look into how variables propagate via nested includes. Ideally, the variable value should apply to nested includes too and outermost value should override values specified in inner includes.

@yamgent

Copy link
Copy Markdown
Member

Ideally, the variable value should apply to nested includes too and outermost value should override values specified in inner includes.

Do you mean that for the following scenario, the final output should be "Original variable"? The behaviour would look very odd to authors, since the inner page that is "closer" to the final included page should be allowed to have a say over the final variable value, rather than the outer page.


index.md

<includesrc="a.md"><spanid="someVariable">Original variable</span></include>

a.md

<includesrc="b.md"><spanid="someVariable">Nested include variable</span></include>

b.md

{{someVariable}}

@damithc

Copy link
Copy Markdown
Contributor

Do you mean that for the following scenario, the final output should be "Original variable"? The behaviour would look very odd to authors, since the inner page that is "closer" to the final included page should be allowed to have a say over the final variable value, rather than the outer page.

Yes, that feels odd but from a reuse perspective that should be the behavior (opposite of how inheritance works). Otherwise, someone who wants to resue this bit of code has no way to customize the code without modifying it directly, right?

@yamgent

Copy link
Copy Markdown
Member

Otherwise, someone who wants to resue this bit of code has no way to customize the code without modifying it directly, right?

That is indeed true, I think that is the desirable behaviour.

In order to make it easier to explain this behaviour, maybe we should consider adopting a different term rather than calling them all "variables"? Maybe "page variables" vs "global variables"? So we can say that "If a page uses a {{variable}}, then:

  1. Page variables override global variables.
  2. Outer page variables override inner page variables.

@damithc

Copy link
Copy Markdown
Contributor

In order to make it easier to explain this behaviour, maybe we should consider adopting a different term rather than calling them all "variables"?

That is a good idea. One issue is that we use the exact syntax used for nunjucks variables. Are we using nunjucks behind the scene to implement variables? This could become a problem because nunjucks variables are likely to behave differently from ours and users don't have a way to tell the two apart (note that a MarkBind file can contain some nunjucks code too). Is it possible to use a slightly different syntax e.g., {< foo >}?

  1. Page variables override global variables.

This should be the other way around too?

Maybe "page variables" vs "global variables"?

Or stay away from variables altogether. customization?

@yamgent

Copy link
Copy Markdown
Member

That is a good idea. One issue is that we use the exact syntax used for nunjucks variables. Are we using nunjucks behind the scene to implement variables? This could become a problem because nunjucks variables are likely to behave differently from ours and users don't have a way to tell the two apart (note that a MarkBind file can contain some nunjucks code too). Is it possible to use a slightly different syntax e.g., {< foo >}?

Yes, we are using nunjucks behind the scene to implement variables.

And yes there is a chance the authors may mix them up. We can explore using a different syntax in another PR (that would be a breaking change)?

This should be the other way around too?

Got it, global ones should have the highest priority. @jamos-tay you will have to rework this. :P

@damithc

Copy link
Copy Markdown
Contributor

And yes there is a chance the authors may mix them up. We can explore using a different syntax in another PR (that would be a breaking change)?

It would break the syntax introduced in this PR, right? :-) If the new syntax is introduced in this PR, it will not break existing features.
Anyway, it is a big change, and I'm not sure how hard it is to implement, especially if we cannot piggyback on nunjucks anymore. We can discuss on Saturday.

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Hmm... I think it might be possible to reuse the same syntax. I just have to check that if a variable already exists (i.e. declared by an outer file), don't overwrite it.

I'll need a way to detect whether a variable is a global or page variable though, but I think it can be done without using a hacky solution.

@damithc

Copy link
Copy Markdown
Contributor

Hmm... I think it might be possible to reuse the same syntax. I just have to check that if a variable already exists (i.e. declared by an outer file), don't overwrite it.

Reusing the same syntax is appropriate only if a normal nunjucks variable behaves exactly the same way as the ones used for includes. Are we going to apply the same outermost-overrides behavior to normal nunjucks variables as well? i.e., those declared using {% set x = 'foo' %}

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Hmm... Nunjucks overwrites, so it'll use the inner most by default. We might be able to write an extension that will overwrite set's behavior. Alternatively we could strip out {% set %} tags and parse them manually.

If not, are we considering moving the entire variable system off nunjucks? i.e. {< variable >} everywhere? That might be a lot of work just for this feature... considering the global variables.md would have to be migrated over, we'd have to write our own parser logic, and also all older sites would break.

@damithc

Copy link
Copy Markdown
Contributor

Hmm... Nunjucks overwrites, so it'll use the inner most by default. We might be able to write an extension that will overwrite set's behavior. Alternatively we could strip out {% set %} tags and parse them manually.

If not, are we considering moving the entire variable system off nunjucks? i.e. {< variable >} everywhere? That might be a lot of work just for this feature... considering the global variables.md would have to be migrated over, we'd have to write our own parser logic, and also all older sites would break.

Yup, this is a big decision. Better to make it now rather than later but we have to think it through. Although the issue came up in this PR, we are actually dealing with a much wider issue, namely, the need to support the outer-overrides-inner behavior that is central to reuse but opposite of the normal template behavior. The problem seem to be that we cannot support two opposing behaviors using the same internal library and/or syntax.

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

After discussion:

We'll go with the original behavior:
Global variables override included variables
Outer include variables override inner include variables
{% set ... %} overrides all variables, but only applies to the page (excluding includes)

E.g.
variables.md: a = 1

a.md

<include src = b.md>
a = 2
b = 2
</include>

b.md

{{ a }} // 1
{{ b }} // 2
{% set a = 4 %}
{{ a }} // 4
<include src = c.md>
a = 3
b = 3
</include>

c.md

{{ a }} // 1
{{ b }} // 2

Comment threadsrc/lib/markbind/src/parser.js Outdated
@jamos-tay

jamos-tay commented Jan 19, 2019

Copy link
Copy Markdown
ContributorAuthor

Updated

Note: We currently do not support include variables referencing variables within the same include:

index.md:

<includesrc="a.md"><spanid="a">a</span><spanid="b">b & {{ a }}</span></include>

This is because {{ a }} gets rendered to an empty string when nunjucks processes index.md. Making this work would require some rework the page generation logic, which should be in another PR. This rework is also necessary to handle certain cases with set, so it can be fixed along with that.

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Rebased on master

Comment threaddocs/userGuide/reusingContents.md Outdated
Comment threaddocs/userGuide/reusingContents.md
Comment threadsrc/lib/markbind/src/parser.js Outdated
Comment threadsrc/lib/markbind/src/parser.js Outdated
Comment threadsrc/lib/markbind/src/parser.js Outdated
Comment threadtest/test_site/index.md Outdated
Comment threadtest/test_site/testIncludeVariables.md Outdated
Comment threaddocs/userGuide/reusingContents.md
Rename element to includeElement
Fix typos in tests
@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Updated

@yamgent
yamgent self-requested a review January 23, 2019 16:11

@yamgentyamgent left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Some more nits.

Comment threaddocs/userGuide/reusingContents.md Outdated
Comment threaddocs/userGuide/reusingContents.md Outdated
Comment threadtest/test_site/_markbind/variables.md Outdated
Comment threadtest/test_site/index.md Outdated
@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Updated

@yamgentyamgent added this to the v1.17.1 milestone Jan 26, 2019
@yamgent
yamgent merged commit 1eae09f into MarkBind:masterJan 28, 2019
@damithc

Copy link
Copy Markdown
Contributor

Applied this in production nus-cs3281/2019@0d04ee4

Nice work @jamos-tay and reviewers @acjh@yamgent 👍

marvinchin pushed a commit to marvinchin/markbind that referenced this pull request Jan 31, 2019
@yamgentyamgent mentioned this pull request Mar 11, 2019
3 tasks
@ang-zeyuang-zeyu mentioned this pull request Sep 13, 2020
Sign up for freeto 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.

Allow customizing at the point of inclusion

4 participants

@jamos-tay@damithc@acjh@yamgent
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Allow specifying variables in include by jamos-tay · Pull Request #553 · MarkBind/markbind · GitHub
Skip to content

Allow specifying variables in include - #553

Merged
yamgent merged 10 commits into
MarkBind:masterfrom
jamos-tay:included-variables
Jan 28, 2019
Merged

Allow specifying variables in include#553
yamgent merged 10 commits into
MarkBind:masterfrom
jamos-tay:included-variables

Conversation

@jamos-tay

@jamos-tayjamos-tay commented Jan 5, 2019

Copy link
Copy Markdown
Contributor

What is the purpose of this pull request? (put "X" next to an item, remove the rest)

• [X] Enhancement to an existing feature

Fixes#545

What is the rationale for this request?

Users may want to create include file templates with variables that can be filled in, allowing reuse. However, they should only apply to each file. For example,

## {{ title }}
This is a level {{ level }} topic.

Allow users to specify variables in an include tag

What changes did you make? (Give an overview)

Allow users to specify Markbind variables in an include tag:

<include src=”foo.md”>
<span id="title"><span>New Title</span></span> // same syntax as variables.md
<span id="level">2</span>
</include>

These add on to (and overwrite) variables in _markbind/variables.md, and only apply to the context of the included file, and any files that the file includes.

We use nunjuck's or (default) syntax to allow for default values in case the variable is missing: {{ title or "Untitled" }}

Is there anything you'd like reviewers to focus on?

Users cannot reference other variables in the include, like this:

<include src=”foo.md”>
<span id="inner">Inner</span>
<span id="outer">Outer and {{ inner }}</span>
</include>

This is because {{ inner }} gets cleared when the original file is rendered. We can have a separate syntax for these, but it's a little unwieldly.

Referencing variables in _markbind/variables.md is okay.

@jamos-tayjamos-tay changed the title Allow specifying variables in include[WIP] Allow specifying variables in includeJan 5, 2019
@jamos-tayjamos-tay changed the title [WIP] Allow specifying variables in includeAllow specifying variables in includeJan 5, 2019
@damithc

Copy link
Copy Markdown
Contributor

Does this work too? It's not related to includes but possibly related to how the feature is implemented.

<spanid="color">red</span><fontcolor="{{ color }}">my text</font>

@yamgent
yamgent self-requested a review January 6, 2019 02:56
@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

@damithc Yes, tested and works

@damithc

Copy link
Copy Markdown
Contributor

This PR affects the following statement, right?
image

Comment threaddocs/userGuide/reusingContents.md Outdated
Comment threaddocs/userGuide/reusingContents.md Outdated
Comment threaddocs/userGuide/reusingContents.md Outdated
Comment threadsrc/lib/markbind/src/parser.js Outdated
Comment threadsrc/lib/markbind/src/parser.js Outdated
Comment threadtest/test_site/index.md Outdated
Comment threadtest/test_site/testIncludeVariableLeak.md Outdated
Comment threadtest/test_site/testIncludeVariables.md Outdated
@acjh

acjh commented Jan 8, 2019

Copy link
Copy Markdown
Contributor

Please use meaningful commit messages instead of "Pr fixes". 🙈

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Sorry =/

I'll do a quick rebase after it's approved

Comment threaddocs/userGuide/reusingContents.md Outdated

<hr><!-- ======================================================================================================= -->

#### Specifying Variables in the Include Tag

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

  1. Let's follow the same heading level as "<include> Inside an Included File".
  2. Let's use "<include>" rather than "Include Tag" to follow the style of the rest of the documentation.
##### Specifying Variables in an `<include>`

Comment threaddocs/userGuide/reusingContents.md Outdated

#### Specifying Variables in the Include Tag

**It is possible to include variables in the include tag.**

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

**It is possible to declare variables in an `<include>`.**

Comment threaddocs/userGuide/reusingContents.md Outdated

<div class="indented">

{{ icon_example }} Specifying `title` and `author` variables in an `include` tag:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

... in an `<include>` tag:

Comment threaddocs/userGuide/reusingContents.md Outdated
</div>


These variables work the same way as variables in `_markbind/variables.md`, except they only apply to the included file. This can be helpful for creating a template for multiple files.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

  1. Fixing the grammar.
  2. Rephrased the explanation of it being a template mechanism.
..., except that they only apply to the included file. They allow the included file to be reused as a template, for different source files using different variable values.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Side question: do they also work as nunjucks variables? e.g., {{ (size | int) - 1 }} would evaluate to 3 if <span id="size">4</span>

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Tested and it works - but do we want to document this? It seems like a small enough case, and it might confuse the user since it introduces another api

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We already claim that MarkBind is generally compatible with nunjucks syntax. Here, we are using nunjucks syntax for a MarkBind feature. It's better to clarify that these variables are in fact equivalent to nunjucks variables declared within the scope of the included file. However, it can be mentioned in https://markbind.github.io/markbind/userGuide/markBindSyntax.html#support-for-nunjucks rather than under this feature. What do you think?

@damithc

Copy link
Copy Markdown
Contributor

Does this work too? It's not related to includes but possibly related to how the feature is implemented.

<span id="color">red</span>
<font color="{{ color }}">my text</font>

@jamos-tay make sure this is reflected somewhere in the docs too. i.e., together with the d-none attribute, this provides a convenient way to declare local variables and use them in a page.

@jamos-tay

jamos-tay commented Jan 10, 2019

Copy link
Copy Markdown
ContributorAuthor

@jamos-tay make sure this is reflected somewhere in the docs too. i.e., together with the d-none attribute, this provides a convenient way to declare local variables and use them in a page.

@damithc Sorry I think I misunderstood, were you referring to

<span id="color">red</span>
<font color="{{ color }}">my text</font>

within the same .md file? Because that doesn't work... I thought you were asking if you could put variables as element attributes so I put the span in variables.md

I don't think there's a way to create a local variable for a page just yet, but it probably should be in its own PR. I'm pretty sure it requires separate logic (such as placing in the frontmatter) because of the way nunjucks is called.

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Updated

@damithc

Copy link
Copy Markdown
Contributor

within the same .md file? Because that doesn't work... I thought you were asking if you could put variables as element attributes.

I don't think there's a way to create a local variable for a page just yet, but it should be put it in a different PR. I'm pretty sure it requires separate logic (such as placing in the frontmatter) because of the way nunjucks is called.

I see. I thought we got that feature for free. Never mind then. It can be a feature to be added in the future.

Comment threaddocs/userGuide/reusingContents.md Outdated

{{ icon_example }} Specifying a variable as an attribute:

<code>\<span id="color">red</span>\</code><br>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The wrong HTML tag is escaped, it didn't render correctly.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Also it is not clear that the two different code exist in two different places, especially when you look at this portion in the live preview. Maybe indicate one is in variables.md and one is in index.md?

Comment threaddocs/userGuide/reusingContents.md Outdated

<code>\<span id="color">red</span>\</code><br>

<code>\<font color="{<span></span>{ color }}">my text</font>\</code>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Same problem (wrong HTML tag escaped).

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Updated, I decided to delete that section since there was a misunderstanding.

Comment threadsrc/lib/markbind/src/parser.js Outdated
if (!child.attribs.id) {
// eslint-disable-next-line no-console
console.warn(`Missing reference in ${element.attribs[ATTRIB_CWF]}\n`
+ `Missing 'id' in variable for ${element.attribs.src} include.`);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

  1. The "Missing 'id'" part should actually be in front, in order to match the style of other error messages that we show as well.
  2. I realise that the "Missing reference..." should be rephrased (because it is not a missing reference, but rather, a missing variable name), apologies about that.

So it should be something like this:

diff --git a/src/lib/markbind/src/parser.js b/src/lib/markbind/src/parser.js
index 69c40e5..b71b50f 100644
--- a/src/lib/markbind/src/parser.js+++ b/src/lib/markbind/src/parser.js@@ -215,8 +215,8 @@ Parser.prototype._preprocess = function (node, context, config) {
}
if (!child.attribs.id) {
// eslint-disable-next-line no-console
- console.warn(`Missing reference in ${element.attribs[ATTRIB_CWF]}\n`- + `Missing 'id' in variable for ${element.attribs.src} include.`);+ console.warn(`Missing 'id' in variable for referenced file: ${element.attribs.src}.\n`+ + `Missing variable name in ${element.attribs[ATTRIB_CWF]}\n`);
return;
}
includedVariables[child.attribs.id] = cheerio.html(child.children);

@damithc

Copy link
Copy Markdown
Contributor

@jamos-tay do look into how variables propagate via nested includes. Ideally, the variable value should apply to nested includes too and outermost value should override values specified in inner includes.

@yamgent

Copy link
Copy Markdown
Member

Ideally, the variable value should apply to nested includes too and outermost value should override values specified in inner includes.

Do you mean that for the following scenario, the final output should be "Original variable"? The behaviour would look very odd to authors, since the inner page that is "closer" to the final included page should be allowed to have a say over the final variable value, rather than the outer page.


index.md

<includesrc="a.md"><spanid="someVariable">Original variable</span></include>

a.md

<includesrc="b.md"><spanid="someVariable">Nested include variable</span></include>

b.md

{{someVariable}}

@damithc

Copy link
Copy Markdown
Contributor

Do you mean that for the following scenario, the final output should be "Original variable"? The behaviour would look very odd to authors, since the inner page that is "closer" to the final included page should be allowed to have a say over the final variable value, rather than the outer page.

Yes, that feels odd but from a reuse perspective that should be the behavior (opposite of how inheritance works). Otherwise, someone who wants to resue this bit of code has no way to customize the code without modifying it directly, right?

@yamgent

Copy link
Copy Markdown
Member

Otherwise, someone who wants to resue this bit of code has no way to customize the code without modifying it directly, right?

That is indeed true, I think that is the desirable behaviour.

In order to make it easier to explain this behaviour, maybe we should consider adopting a different term rather than calling them all "variables"? Maybe "page variables" vs "global variables"? So we can say that "If a page uses a {{variable}}, then:

  1. Page variables override global variables.
  2. Outer page variables override inner page variables.

@damithc

Copy link
Copy Markdown
Contributor

In order to make it easier to explain this behaviour, maybe we should consider adopting a different term rather than calling them all "variables"?

That is a good idea. One issue is that we use the exact syntax used for nunjucks variables. Are we using nunjucks behind the scene to implement variables? This could become a problem because nunjucks variables are likely to behave differently from ours and users don't have a way to tell the two apart (note that a MarkBind file can contain some nunjucks code too). Is it possible to use a slightly different syntax e.g., {< foo >}?

  1. Page variables override global variables.

This should be the other way around too?

Maybe "page variables" vs "global variables"?

Or stay away from variables altogether. customization?

@yamgent

Copy link
Copy Markdown
Member

That is a good idea. One issue is that we use the exact syntax used for nunjucks variables. Are we using nunjucks behind the scene to implement variables? This could become a problem because nunjucks variables are likely to behave differently from ours and users don't have a way to tell the two apart (note that a MarkBind file can contain some nunjucks code too). Is it possible to use a slightly different syntax e.g., {< foo >}?

Yes, we are using nunjucks behind the scene to implement variables.

And yes there is a chance the authors may mix them up. We can explore using a different syntax in another PR (that would be a breaking change)?

This should be the other way around too?

Got it, global ones should have the highest priority. @jamos-tay you will have to rework this. :P

@damithc

Copy link
Copy Markdown
Contributor

And yes there is a chance the authors may mix them up. We can explore using a different syntax in another PR (that would be a breaking change)?

It would break the syntax introduced in this PR, right? :-) If the new syntax is introduced in this PR, it will not break existing features.
Anyway, it is a big change, and I'm not sure how hard it is to implement, especially if we cannot piggyback on nunjucks anymore. We can discuss on Saturday.

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Hmm... I think it might be possible to reuse the same syntax. I just have to check that if a variable already exists (i.e. declared by an outer file), don't overwrite it.

I'll need a way to detect whether a variable is a global or page variable though, but I think it can be done without using a hacky solution.

@damithc

Copy link
Copy Markdown
Contributor

Hmm... I think it might be possible to reuse the same syntax. I just have to check that if a variable already exists (i.e. declared by an outer file), don't overwrite it.

Reusing the same syntax is appropriate only if a normal nunjucks variable behaves exactly the same way as the ones used for includes. Are we going to apply the same outermost-overrides behavior to normal nunjucks variables as well? i.e., those declared using {% set x = 'foo' %}

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Hmm... Nunjucks overwrites, so it'll use the inner most by default. We might be able to write an extension that will overwrite set's behavior. Alternatively we could strip out {% set %} tags and parse them manually.

If not, are we considering moving the entire variable system off nunjucks? i.e. {< variable >} everywhere? That might be a lot of work just for this feature... considering the global variables.md would have to be migrated over, we'd have to write our own parser logic, and also all older sites would break.

@damithc

Copy link
Copy Markdown
Contributor

Hmm... Nunjucks overwrites, so it'll use the inner most by default. We might be able to write an extension that will overwrite set's behavior. Alternatively we could strip out {% set %} tags and parse them manually.

If not, are we considering moving the entire variable system off nunjucks? i.e. {< variable >} everywhere? That might be a lot of work just for this feature... considering the global variables.md would have to be migrated over, we'd have to write our own parser logic, and also all older sites would break.

Yup, this is a big decision. Better to make it now rather than later but we have to think it through. Although the issue came up in this PR, we are actually dealing with a much wider issue, namely, the need to support the outer-overrides-inner behavior that is central to reuse but opposite of the normal template behavior. The problem seem to be that we cannot support two opposing behaviors using the same internal library and/or syntax.

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

After discussion:

We'll go with the original behavior:
Global variables override included variables
Outer include variables override inner include variables
{% set ... %} overrides all variables, but only applies to the page (excluding includes)

E.g.
variables.md: a = 1

a.md

<include src = b.md>
a = 2
b = 2
</include>

b.md

{{ a }} // 1
{{ b }} // 2
{% set a = 4 %}
{{ a }} // 4
<include src = c.md>
a = 3
b = 3
</include>

c.md

{{ a }} // 1
{{ b }} // 2

Comment threadsrc/lib/markbind/src/parser.js Outdated
@jamos-tay

jamos-tay commented Jan 19, 2019

Copy link
Copy Markdown
ContributorAuthor

Updated

Note: We currently do not support include variables referencing variables within the same include:

index.md:

<includesrc="a.md"><spanid="a">a</span><spanid="b">b & {{ a }}</span></include>

This is because {{ a }} gets rendered to an empty string when nunjucks processes index.md. Making this work would require some rework the page generation logic, which should be in another PR. This rework is also necessary to handle certain cases with set, so it can be fixed along with that.

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Rebased on master

Comment threaddocs/userGuide/reusingContents.md Outdated
Comment threaddocs/userGuide/reusingContents.md
Comment threadsrc/lib/markbind/src/parser.js Outdated
Comment threadsrc/lib/markbind/src/parser.js Outdated
Comment threadsrc/lib/markbind/src/parser.js Outdated
Comment threadtest/test_site/index.md Outdated
Comment threadtest/test_site/testIncludeVariables.md Outdated
Comment threaddocs/userGuide/reusingContents.md
Rename element to includeElement
Fix typos in tests
@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Updated

@yamgent
yamgent self-requested a review January 23, 2019 16:11

@yamgentyamgent left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Some more nits.

Comment threaddocs/userGuide/reusingContents.md Outdated
Comment threaddocs/userGuide/reusingContents.md Outdated
Comment threadtest/test_site/_markbind/variables.md Outdated
Comment threadtest/test_site/index.md Outdated
@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Updated

@yamgentyamgent added this to the v1.17.1 milestone Jan 26, 2019
@yamgent
yamgent merged commit 1eae09f into MarkBind:masterJan 28, 2019
@damithc

Copy link
Copy Markdown
Contributor

Applied this in production nus-cs3281/2019@0d04ee4

Nice work @jamos-tay and reviewers @acjh@yamgent 👍

marvinchin pushed a commit to marvinchin/markbind that referenced this pull request Jan 31, 2019
@yamgentyamgent mentioned this pull request Mar 11, 2019
3 tasks
@ang-zeyuang-zeyu mentioned this pull request Sep 13, 2020
Sign up for freeto 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.

Allow customizing at the point of inclusion

4 participants

@jamos-tay@damithc@acjh@yamgent
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Allow specifying variables in include by jamos-tay · Pull Request #553 · MarkBind/markbind · GitHub
Skip to content

Allow specifying variables in include - #553

Merged
yamgent merged 10 commits into
MarkBind:masterfrom
jamos-tay:included-variables
Jan 28, 2019
Merged

Allow specifying variables in include#553
yamgent merged 10 commits into
MarkBind:masterfrom
jamos-tay:included-variables

Conversation

@jamos-tay

@jamos-tayjamos-tay commented Jan 5, 2019

Copy link
Copy Markdown
Contributor

What is the purpose of this pull request? (put "X" next to an item, remove the rest)

• [X] Enhancement to an existing feature

Fixes#545

What is the rationale for this request?

Users may want to create include file templates with variables that can be filled in, allowing reuse. However, they should only apply to each file. For example,

## {{ title }}
This is a level {{ level }} topic.

Allow users to specify variables in an include tag

What changes did you make? (Give an overview)

Allow users to specify Markbind variables in an include tag:

<include src=”foo.md”>
<span id="title"><span>New Title</span></span> // same syntax as variables.md
<span id="level">2</span>
</include>

These add on to (and overwrite) variables in _markbind/variables.md, and only apply to the context of the included file, and any files that the file includes.

We use nunjuck's or (default) syntax to allow for default values in case the variable is missing: {{ title or "Untitled" }}

Is there anything you'd like reviewers to focus on?

Users cannot reference other variables in the include, like this:

<include src=”foo.md”>
<span id="inner">Inner</span>
<span id="outer">Outer and {{ inner }}</span>
</include>

This is because {{ inner }} gets cleared when the original file is rendered. We can have a separate syntax for these, but it's a little unwieldly.

Referencing variables in _markbind/variables.md is okay.

@jamos-tayjamos-tay changed the title Allow specifying variables in include[WIP] Allow specifying variables in includeJan 5, 2019
@jamos-tayjamos-tay changed the title [WIP] Allow specifying variables in includeAllow specifying variables in includeJan 5, 2019
@damithc

Copy link
Copy Markdown
Contributor

Does this work too? It's not related to includes but possibly related to how the feature is implemented.

<spanid="color">red</span><fontcolor="{{ color }}">my text</font>

@yamgent
yamgent self-requested a review January 6, 2019 02:56
@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

@damithc Yes, tested and works

@damithc

Copy link
Copy Markdown
Contributor

This PR affects the following statement, right?
image

Comment threaddocs/userGuide/reusingContents.md Outdated
Comment threaddocs/userGuide/reusingContents.md Outdated
Comment threaddocs/userGuide/reusingContents.md Outdated
Comment threadsrc/lib/markbind/src/parser.js Outdated
Comment threadsrc/lib/markbind/src/parser.js Outdated
Comment threadtest/test_site/index.md Outdated
Comment threadtest/test_site/testIncludeVariableLeak.md Outdated
Comment threadtest/test_site/testIncludeVariables.md Outdated
@acjh

acjh commented Jan 8, 2019

Copy link
Copy Markdown
Contributor

Please use meaningful commit messages instead of "Pr fixes". 🙈

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Sorry =/

I'll do a quick rebase after it's approved

Comment threaddocs/userGuide/reusingContents.md Outdated

<hr><!-- ======================================================================================================= -->

#### Specifying Variables in the Include Tag

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

  1. Let's follow the same heading level as "<include> Inside an Included File".
  2. Let's use "<include>" rather than "Include Tag" to follow the style of the rest of the documentation.
##### Specifying Variables in an `<include>`

Comment threaddocs/userGuide/reusingContents.md Outdated

#### Specifying Variables in the Include Tag

**It is possible to include variables in the include tag.**

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

**It is possible to declare variables in an `<include>`.**

Comment threaddocs/userGuide/reusingContents.md Outdated

<div class="indented">

{{ icon_example }} Specifying `title` and `author` variables in an `include` tag:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

... in an `<include>` tag:

Comment threaddocs/userGuide/reusingContents.md Outdated
</div>


These variables work the same way as variables in `_markbind/variables.md`, except they only apply to the included file. This can be helpful for creating a template for multiple files.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

  1. Fixing the grammar.
  2. Rephrased the explanation of it being a template mechanism.
..., except that they only apply to the included file. They allow the included file to be reused as a template, for different source files using different variable values.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Side question: do they also work as nunjucks variables? e.g., {{ (size | int) - 1 }} would evaluate to 3 if <span id="size">4</span>

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Tested and it works - but do we want to document this? It seems like a small enough case, and it might confuse the user since it introduces another api

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We already claim that MarkBind is generally compatible with nunjucks syntax. Here, we are using nunjucks syntax for a MarkBind feature. It's better to clarify that these variables are in fact equivalent to nunjucks variables declared within the scope of the included file. However, it can be mentioned in https://markbind.github.io/markbind/userGuide/markBindSyntax.html#support-for-nunjucks rather than under this feature. What do you think?

@damithc

Copy link
Copy Markdown
Contributor

Does this work too? It's not related to includes but possibly related to how the feature is implemented.

<span id="color">red</span>
<font color="{{ color }}">my text</font>

@jamos-tay make sure this is reflected somewhere in the docs too. i.e., together with the d-none attribute, this provides a convenient way to declare local variables and use them in a page.

@jamos-tay

jamos-tay commented Jan 10, 2019

Copy link
Copy Markdown
ContributorAuthor

@jamos-tay make sure this is reflected somewhere in the docs too. i.e., together with the d-none attribute, this provides a convenient way to declare local variables and use them in a page.

@damithc Sorry I think I misunderstood, were you referring to

<span id="color">red</span>
<font color="{{ color }}">my text</font>

within the same .md file? Because that doesn't work... I thought you were asking if you could put variables as element attributes so I put the span in variables.md

I don't think there's a way to create a local variable for a page just yet, but it probably should be in its own PR. I'm pretty sure it requires separate logic (such as placing in the frontmatter) because of the way nunjucks is called.

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Updated

@damithc

Copy link
Copy Markdown
Contributor

within the same .md file? Because that doesn't work... I thought you were asking if you could put variables as element attributes.

I don't think there's a way to create a local variable for a page just yet, but it should be put it in a different PR. I'm pretty sure it requires separate logic (such as placing in the frontmatter) because of the way nunjucks is called.

I see. I thought we got that feature for free. Never mind then. It can be a feature to be added in the future.

Comment threaddocs/userGuide/reusingContents.md Outdated

{{ icon_example }} Specifying a variable as an attribute:

<code>\<span id="color">red</span>\</code><br>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The wrong HTML tag is escaped, it didn't render correctly.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Also it is not clear that the two different code exist in two different places, especially when you look at this portion in the live preview. Maybe indicate one is in variables.md and one is in index.md?

Comment threaddocs/userGuide/reusingContents.md Outdated

<code>\<span id="color">red</span>\</code><br>

<code>\<font color="{<span></span>{ color }}">my text</font>\</code>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Same problem (wrong HTML tag escaped).

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Updated, I decided to delete that section since there was a misunderstanding.

Comment threadsrc/lib/markbind/src/parser.js Outdated
if (!child.attribs.id) {
// eslint-disable-next-line no-console
console.warn(`Missing reference in ${element.attribs[ATTRIB_CWF]}\n`
+ `Missing 'id' in variable for ${element.attribs.src} include.`);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

  1. The "Missing 'id'" part should actually be in front, in order to match the style of other error messages that we show as well.
  2. I realise that the "Missing reference..." should be rephrased (because it is not a missing reference, but rather, a missing variable name), apologies about that.

So it should be something like this:

diff --git a/src/lib/markbind/src/parser.js b/src/lib/markbind/src/parser.js
index 69c40e5..b71b50f 100644
--- a/src/lib/markbind/src/parser.js+++ b/src/lib/markbind/src/parser.js@@ -215,8 +215,8 @@ Parser.prototype._preprocess = function (node, context, config) {
}
if (!child.attribs.id) {
// eslint-disable-next-line no-console
- console.warn(`Missing reference in ${element.attribs[ATTRIB_CWF]}\n`- + `Missing 'id' in variable for ${element.attribs.src} include.`);+ console.warn(`Missing 'id' in variable for referenced file: ${element.attribs.src}.\n`+ + `Missing variable name in ${element.attribs[ATTRIB_CWF]}\n`);
return;
}
includedVariables[child.attribs.id] = cheerio.html(child.children);

@damithc

Copy link
Copy Markdown
Contributor

@jamos-tay do look into how variables propagate via nested includes. Ideally, the variable value should apply to nested includes too and outermost value should override values specified in inner includes.

@yamgent

Copy link
Copy Markdown
Member

Ideally, the variable value should apply to nested includes too and outermost value should override values specified in inner includes.

Do you mean that for the following scenario, the final output should be "Original variable"? The behaviour would look very odd to authors, since the inner page that is "closer" to the final included page should be allowed to have a say over the final variable value, rather than the outer page.


index.md

<includesrc="a.md"><spanid="someVariable">Original variable</span></include>

a.md

<includesrc="b.md"><spanid="someVariable">Nested include variable</span></include>

b.md

{{someVariable}}

@damithc

Copy link
Copy Markdown
Contributor

Do you mean that for the following scenario, the final output should be "Original variable"? The behaviour would look very odd to authors, since the inner page that is "closer" to the final included page should be allowed to have a say over the final variable value, rather than the outer page.

Yes, that feels odd but from a reuse perspective that should be the behavior (opposite of how inheritance works). Otherwise, someone who wants to resue this bit of code has no way to customize the code without modifying it directly, right?

@yamgent

Copy link
Copy Markdown
Member

Otherwise, someone who wants to resue this bit of code has no way to customize the code without modifying it directly, right?

That is indeed true, I think that is the desirable behaviour.

In order to make it easier to explain this behaviour, maybe we should consider adopting a different term rather than calling them all "variables"? Maybe "page variables" vs "global variables"? So we can say that "If a page uses a {{variable}}, then:

  1. Page variables override global variables.
  2. Outer page variables override inner page variables.

@damithc

Copy link
Copy Markdown
Contributor

In order to make it easier to explain this behaviour, maybe we should consider adopting a different term rather than calling them all "variables"?

That is a good idea. One issue is that we use the exact syntax used for nunjucks variables. Are we using nunjucks behind the scene to implement variables? This could become a problem because nunjucks variables are likely to behave differently from ours and users don't have a way to tell the two apart (note that a MarkBind file can contain some nunjucks code too). Is it possible to use a slightly different syntax e.g., {< foo >}?

  1. Page variables override global variables.

This should be the other way around too?

Maybe "page variables" vs "global variables"?

Or stay away from variables altogether. customization?

@yamgent

Copy link
Copy Markdown
Member

That is a good idea. One issue is that we use the exact syntax used for nunjucks variables. Are we using nunjucks behind the scene to implement variables? This could become a problem because nunjucks variables are likely to behave differently from ours and users don't have a way to tell the two apart (note that a MarkBind file can contain some nunjucks code too). Is it possible to use a slightly different syntax e.g., {< foo >}?

Yes, we are using nunjucks behind the scene to implement variables.

And yes there is a chance the authors may mix them up. We can explore using a different syntax in another PR (that would be a breaking change)?

This should be the other way around too?

Got it, global ones should have the highest priority. @jamos-tay you will have to rework this. :P

@damithc

Copy link
Copy Markdown
Contributor

And yes there is a chance the authors may mix them up. We can explore using a different syntax in another PR (that would be a breaking change)?

It would break the syntax introduced in this PR, right? :-) If the new syntax is introduced in this PR, it will not break existing features.
Anyway, it is a big change, and I'm not sure how hard it is to implement, especially if we cannot piggyback on nunjucks anymore. We can discuss on Saturday.

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Hmm... I think it might be possible to reuse the same syntax. I just have to check that if a variable already exists (i.e. declared by an outer file), don't overwrite it.

I'll need a way to detect whether a variable is a global or page variable though, but I think it can be done without using a hacky solution.

@damithc

Copy link
Copy Markdown
Contributor

Hmm... I think it might be possible to reuse the same syntax. I just have to check that if a variable already exists (i.e. declared by an outer file), don't overwrite it.

Reusing the same syntax is appropriate only if a normal nunjucks variable behaves exactly the same way as the ones used for includes. Are we going to apply the same outermost-overrides behavior to normal nunjucks variables as well? i.e., those declared using {% set x = 'foo' %}

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Hmm... Nunjucks overwrites, so it'll use the inner most by default. We might be able to write an extension that will overwrite set's behavior. Alternatively we could strip out {% set %} tags and parse them manually.

If not, are we considering moving the entire variable system off nunjucks? i.e. {< variable >} everywhere? That might be a lot of work just for this feature... considering the global variables.md would have to be migrated over, we'd have to write our own parser logic, and also all older sites would break.

@damithc

Copy link
Copy Markdown
Contributor

Hmm... Nunjucks overwrites, so it'll use the inner most by default. We might be able to write an extension that will overwrite set's behavior. Alternatively we could strip out {% set %} tags and parse them manually.

If not, are we considering moving the entire variable system off nunjucks? i.e. {< variable >} everywhere? That might be a lot of work just for this feature... considering the global variables.md would have to be migrated over, we'd have to write our own parser logic, and also all older sites would break.

Yup, this is a big decision. Better to make it now rather than later but we have to think it through. Although the issue came up in this PR, we are actually dealing with a much wider issue, namely, the need to support the outer-overrides-inner behavior that is central to reuse but opposite of the normal template behavior. The problem seem to be that we cannot support two opposing behaviors using the same internal library and/or syntax.

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

After discussion:

We'll go with the original behavior:
Global variables override included variables
Outer include variables override inner include variables
{% set ... %} overrides all variables, but only applies to the page (excluding includes)

E.g.
variables.md: a = 1

a.md

<include src = b.md>
a = 2
b = 2
</include>

b.md

{{ a }} // 1
{{ b }} // 2
{% set a = 4 %}
{{ a }} // 4
<include src = c.md>
a = 3
b = 3
</include>

c.md

{{ a }} // 1
{{ b }} // 2

Comment threadsrc/lib/markbind/src/parser.js Outdated
@jamos-tay

jamos-tay commented Jan 19, 2019

Copy link
Copy Markdown
ContributorAuthor

Updated

Note: We currently do not support include variables referencing variables within the same include:

index.md:

<includesrc="a.md"><spanid="a">a</span><spanid="b">b & {{ a }}</span></include>

This is because {{ a }} gets rendered to an empty string when nunjucks processes index.md. Making this work would require some rework the page generation logic, which should be in another PR. This rework is also necessary to handle certain cases with set, so it can be fixed along with that.

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Rebased on master

Comment threaddocs/userGuide/reusingContents.md Outdated
Comment threaddocs/userGuide/reusingContents.md
Comment threadsrc/lib/markbind/src/parser.js Outdated
Comment threadsrc/lib/markbind/src/parser.js Outdated
Comment threadsrc/lib/markbind/src/parser.js Outdated
Comment threadtest/test_site/index.md Outdated
Comment threadtest/test_site/testIncludeVariables.md Outdated
Comment threaddocs/userGuide/reusingContents.md
Rename element to includeElement
Fix typos in tests
@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Updated

@yamgent
yamgent self-requested a review January 23, 2019 16:11

@yamgentyamgent left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Some more nits.

Comment threaddocs/userGuide/reusingContents.md Outdated
Comment threaddocs/userGuide/reusingContents.md Outdated
Comment threadtest/test_site/_markbind/variables.md Outdated
Comment threadtest/test_site/index.md Outdated
@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Updated

@yamgentyamgent added this to the v1.17.1 milestone Jan 26, 2019
@yamgent
yamgent merged commit 1eae09f into MarkBind:masterJan 28, 2019
@damithc

Copy link
Copy Markdown
Contributor

Applied this in production nus-cs3281/2019@0d04ee4

Nice work @jamos-tay and reviewers @acjh@yamgent 👍

marvinchin pushed a commit to marvinchin/markbind that referenced this pull request Jan 31, 2019
@yamgentyamgent mentioned this pull request Mar 11, 2019
3 tasks
@ang-zeyuang-zeyu mentioned this pull request Sep 13, 2020
Sign up for freeto 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.

Allow customizing at the point of inclusion

4 participants

@jamos-tay@damithc@acjh@yamgent
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' Allow specifying variables in include by jamos-tay · Pull Request #553 · MarkBind/markbind · GitHub
Skip to content

Allow specifying variables in include - #553

Merged
yamgent merged 10 commits into
MarkBind:masterfrom
jamos-tay:included-variables
Jan 28, 2019
Merged

Allow specifying variables in include#553
yamgent merged 10 commits into
MarkBind:masterfrom
jamos-tay:included-variables

Conversation

@jamos-tay

@jamos-tayjamos-tay commented Jan 5, 2019

Copy link
Copy Markdown
Contributor

What is the purpose of this pull request? (put "X" next to an item, remove the rest)

• [X] Enhancement to an existing feature

Fixes#545

What is the rationale for this request?

Users may want to create include file templates with variables that can be filled in, allowing reuse. However, they should only apply to each file. For example,

## {{ title }}
This is a level {{ level }} topic.

Allow users to specify variables in an include tag

What changes did you make? (Give an overview)

Allow users to specify Markbind variables in an include tag:

<include src=”foo.md”>
<span id="title"><span>New Title</span></span> // same syntax as variables.md
<span id="level">2</span>
</include>

These add on to (and overwrite) variables in _markbind/variables.md, and only apply to the context of the included file, and any files that the file includes.

We use nunjuck's or (default) syntax to allow for default values in case the variable is missing: {{ title or "Untitled" }}

Is there anything you'd like reviewers to focus on?

Users cannot reference other variables in the include, like this:

<include src=”foo.md”>
<span id="inner">Inner</span>
<span id="outer">Outer and {{ inner }}</span>
</include>

This is because {{ inner }} gets cleared when the original file is rendered. We can have a separate syntax for these, but it's a little unwieldly.

Referencing variables in _markbind/variables.md is okay.

@jamos-tayjamos-tay changed the title Allow specifying variables in include[WIP] Allow specifying variables in includeJan 5, 2019
@jamos-tayjamos-tay changed the title [WIP] Allow specifying variables in includeAllow specifying variables in includeJan 5, 2019
@damithc

Copy link
Copy Markdown
Contributor

Does this work too? It's not related to includes but possibly related to how the feature is implemented.

<spanid="color">red</span><fontcolor="{{ color }}">my text</font>

@yamgent
yamgent self-requested a review January 6, 2019 02:56
@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

@damithc Yes, tested and works

@damithc

Copy link
Copy Markdown
Contributor

This PR affects the following statement, right?
image

Comment threaddocs/userGuide/reusingContents.md Outdated
Comment threaddocs/userGuide/reusingContents.md Outdated
Comment threaddocs/userGuide/reusingContents.md Outdated
Comment threadsrc/lib/markbind/src/parser.js Outdated
Comment threadsrc/lib/markbind/src/parser.js Outdated
Comment threadtest/test_site/index.md Outdated
Comment threadtest/test_site/testIncludeVariableLeak.md Outdated
Comment threadtest/test_site/testIncludeVariables.md Outdated
@acjh

acjh commented Jan 8, 2019

Copy link
Copy Markdown
Contributor

Please use meaningful commit messages instead of "Pr fixes". 🙈

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Sorry =/

I'll do a quick rebase after it's approved

Comment threaddocs/userGuide/reusingContents.md Outdated

<hr><!-- ======================================================================================================= -->

#### Specifying Variables in the Include Tag

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

  1. Let's follow the same heading level as "<include> Inside an Included File".
  2. Let's use "<include>" rather than "Include Tag" to follow the style of the rest of the documentation.
##### Specifying Variables in an `<include>`

Comment threaddocs/userGuide/reusingContents.md Outdated

#### Specifying Variables in the Include Tag

**It is possible to include variables in the include tag.**

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

**It is possible to declare variables in an `<include>`.**

Comment threaddocs/userGuide/reusingContents.md Outdated

<div class="indented">

{{ icon_example }} Specifying `title` and `author` variables in an `include` tag:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

... in an `<include>` tag:

Comment threaddocs/userGuide/reusingContents.md Outdated
</div>


These variables work the same way as variables in `_markbind/variables.md`, except they only apply to the included file. This can be helpful for creating a template for multiple files.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

  1. Fixing the grammar.
  2. Rephrased the explanation of it being a template mechanism.
..., except that they only apply to the included file. They allow the included file to be reused as a template, for different source files using different variable values.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Side question: do they also work as nunjucks variables? e.g., {{ (size | int) - 1 }} would evaluate to 3 if <span id="size">4</span>

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Tested and it works - but do we want to document this? It seems like a small enough case, and it might confuse the user since it introduces another api

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We already claim that MarkBind is generally compatible with nunjucks syntax. Here, we are using nunjucks syntax for a MarkBind feature. It's better to clarify that these variables are in fact equivalent to nunjucks variables declared within the scope of the included file. However, it can be mentioned in https://markbind.github.io/markbind/userGuide/markBindSyntax.html#support-for-nunjucks rather than under this feature. What do you think?

@damithc

Copy link
Copy Markdown
Contributor

Does this work too? It's not related to includes but possibly related to how the feature is implemented.

<span id="color">red</span>
<font color="{{ color }}">my text</font>

@jamos-tay make sure this is reflected somewhere in the docs too. i.e., together with the d-none attribute, this provides a convenient way to declare local variables and use them in a page.

@jamos-tay

jamos-tay commented Jan 10, 2019

Copy link
Copy Markdown
ContributorAuthor

@jamos-tay make sure this is reflected somewhere in the docs too. i.e., together with the d-none attribute, this provides a convenient way to declare local variables and use them in a page.

@damithc Sorry I think I misunderstood, were you referring to

<span id="color">red</span>
<font color="{{ color }}">my text</font>

within the same .md file? Because that doesn't work... I thought you were asking if you could put variables as element attributes so I put the span in variables.md

I don't think there's a way to create a local variable for a page just yet, but it probably should be in its own PR. I'm pretty sure it requires separate logic (such as placing in the frontmatter) because of the way nunjucks is called.

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Updated

@damithc

Copy link
Copy Markdown
Contributor

within the same .md file? Because that doesn't work... I thought you were asking if you could put variables as element attributes.

I don't think there's a way to create a local variable for a page just yet, but it should be put it in a different PR. I'm pretty sure it requires separate logic (such as placing in the frontmatter) because of the way nunjucks is called.

I see. I thought we got that feature for free. Never mind then. It can be a feature to be added in the future.

Comment threaddocs/userGuide/reusingContents.md Outdated

{{ icon_example }} Specifying a variable as an attribute:

<code>\<span id="color">red</span>\</code><br>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The wrong HTML tag is escaped, it didn't render correctly.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Also it is not clear that the two different code exist in two different places, especially when you look at this portion in the live preview. Maybe indicate one is in variables.md and one is in index.md?

Comment threaddocs/userGuide/reusingContents.md Outdated

<code>\<span id="color">red</span>\</code><br>

<code>\<font color="{<span></span>{ color }}">my text</font>\</code>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Same problem (wrong HTML tag escaped).

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Updated, I decided to delete that section since there was a misunderstanding.

Comment threadsrc/lib/markbind/src/parser.js Outdated
if (!child.attribs.id) {
// eslint-disable-next-line no-console
console.warn(`Missing reference in ${element.attribs[ATTRIB_CWF]}\n`
+ `Missing 'id' in variable for ${element.attribs.src} include.`);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

  1. The "Missing 'id'" part should actually be in front, in order to match the style of other error messages that we show as well.
  2. I realise that the "Missing reference..." should be rephrased (because it is not a missing reference, but rather, a missing variable name), apologies about that.

So it should be something like this:

diff --git a/src/lib/markbind/src/parser.js b/src/lib/markbind/src/parser.js
index 69c40e5..b71b50f 100644
--- a/src/lib/markbind/src/parser.js+++ b/src/lib/markbind/src/parser.js@@ -215,8 +215,8 @@ Parser.prototype._preprocess = function (node, context, config) {
}
if (!child.attribs.id) {
// eslint-disable-next-line no-console
- console.warn(`Missing reference in ${element.attribs[ATTRIB_CWF]}\n`- + `Missing 'id' in variable for ${element.attribs.src} include.`);+ console.warn(`Missing 'id' in variable for referenced file: ${element.attribs.src}.\n`+ + `Missing variable name in ${element.attribs[ATTRIB_CWF]}\n`);
return;
}
includedVariables[child.attribs.id] = cheerio.html(child.children);

@damithc

Copy link
Copy Markdown
Contributor

@jamos-tay do look into how variables propagate via nested includes. Ideally, the variable value should apply to nested includes too and outermost value should override values specified in inner includes.

@yamgent

Copy link
Copy Markdown
Member

Ideally, the variable value should apply to nested includes too and outermost value should override values specified in inner includes.

Do you mean that for the following scenario, the final output should be "Original variable"? The behaviour would look very odd to authors, since the inner page that is "closer" to the final included page should be allowed to have a say over the final variable value, rather than the outer page.


index.md

<includesrc="a.md"><spanid="someVariable">Original variable</span></include>

a.md

<includesrc="b.md"><spanid="someVariable">Nested include variable</span></include>

b.md

{{someVariable}}

@damithc

Copy link
Copy Markdown
Contributor

Do you mean that for the following scenario, the final output should be "Original variable"? The behaviour would look very odd to authors, since the inner page that is "closer" to the final included page should be allowed to have a say over the final variable value, rather than the outer page.

Yes, that feels odd but from a reuse perspective that should be the behavior (opposite of how inheritance works). Otherwise, someone who wants to resue this bit of code has no way to customize the code without modifying it directly, right?

@yamgent

Copy link
Copy Markdown
Member

Otherwise, someone who wants to resue this bit of code has no way to customize the code without modifying it directly, right?

That is indeed true, I think that is the desirable behaviour.

In order to make it easier to explain this behaviour, maybe we should consider adopting a different term rather than calling them all "variables"? Maybe "page variables" vs "global variables"? So we can say that "If a page uses a {{variable}}, then:

  1. Page variables override global variables.
  2. Outer page variables override inner page variables.

@damithc

Copy link
Copy Markdown
Contributor

In order to make it easier to explain this behaviour, maybe we should consider adopting a different term rather than calling them all "variables"?

That is a good idea. One issue is that we use the exact syntax used for nunjucks variables. Are we using nunjucks behind the scene to implement variables? This could become a problem because nunjucks variables are likely to behave differently from ours and users don't have a way to tell the two apart (note that a MarkBind file can contain some nunjucks code too). Is it possible to use a slightly different syntax e.g., {< foo >}?

  1. Page variables override global variables.

This should be the other way around too?

Maybe "page variables" vs "global variables"?

Or stay away from variables altogether. customization?

@yamgent

Copy link
Copy Markdown
Member

That is a good idea. One issue is that we use the exact syntax used for nunjucks variables. Are we using nunjucks behind the scene to implement variables? This could become a problem because nunjucks variables are likely to behave differently from ours and users don't have a way to tell the two apart (note that a MarkBind file can contain some nunjucks code too). Is it possible to use a slightly different syntax e.g., {< foo >}?

Yes, we are using nunjucks behind the scene to implement variables.

And yes there is a chance the authors may mix them up. We can explore using a different syntax in another PR (that would be a breaking change)?

This should be the other way around too?

Got it, global ones should have the highest priority. @jamos-tay you will have to rework this. :P

@damithc

Copy link
Copy Markdown
Contributor

And yes there is a chance the authors may mix them up. We can explore using a different syntax in another PR (that would be a breaking change)?

It would break the syntax introduced in this PR, right? :-) If the new syntax is introduced in this PR, it will not break existing features.
Anyway, it is a big change, and I'm not sure how hard it is to implement, especially if we cannot piggyback on nunjucks anymore. We can discuss on Saturday.

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Hmm... I think it might be possible to reuse the same syntax. I just have to check that if a variable already exists (i.e. declared by an outer file), don't overwrite it.

I'll need a way to detect whether a variable is a global or page variable though, but I think it can be done without using a hacky solution.

@damithc

Copy link
Copy Markdown
Contributor

Hmm... I think it might be possible to reuse the same syntax. I just have to check that if a variable already exists (i.e. declared by an outer file), don't overwrite it.

Reusing the same syntax is appropriate only if a normal nunjucks variable behaves exactly the same way as the ones used for includes. Are we going to apply the same outermost-overrides behavior to normal nunjucks variables as well? i.e., those declared using {% set x = 'foo' %}

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Hmm... Nunjucks overwrites, so it'll use the inner most by default. We might be able to write an extension that will overwrite set's behavior. Alternatively we could strip out {% set %} tags and parse them manually.

If not, are we considering moving the entire variable system off nunjucks? i.e. {< variable >} everywhere? That might be a lot of work just for this feature... considering the global variables.md would have to be migrated over, we'd have to write our own parser logic, and also all older sites would break.

@damithc

Copy link
Copy Markdown
Contributor

Hmm... Nunjucks overwrites, so it'll use the inner most by default. We might be able to write an extension that will overwrite set's behavior. Alternatively we could strip out {% set %} tags and parse them manually.

If not, are we considering moving the entire variable system off nunjucks? i.e. {< variable >} everywhere? That might be a lot of work just for this feature... considering the global variables.md would have to be migrated over, we'd have to write our own parser logic, and also all older sites would break.

Yup, this is a big decision. Better to make it now rather than later but we have to think it through. Although the issue came up in this PR, we are actually dealing with a much wider issue, namely, the need to support the outer-overrides-inner behavior that is central to reuse but opposite of the normal template behavior. The problem seem to be that we cannot support two opposing behaviors using the same internal library and/or syntax.

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

After discussion:

We'll go with the original behavior:
Global variables override included variables
Outer include variables override inner include variables
{% set ... %} overrides all variables, but only applies to the page (excluding includes)

E.g.
variables.md: a = 1

a.md

<include src = b.md>
a = 2
b = 2
</include>

b.md

{{ a }} // 1
{{ b }} // 2
{% set a = 4 %}
{{ a }} // 4
<include src = c.md>
a = 3
b = 3
</include>

c.md

{{ a }} // 1
{{ b }} // 2

Comment threadsrc/lib/markbind/src/parser.js Outdated
@jamos-tay

jamos-tay commented Jan 19, 2019

Copy link
Copy Markdown
ContributorAuthor

Updated

Note: We currently do not support include variables referencing variables within the same include:

index.md:

<includesrc="a.md"><spanid="a">a</span><spanid="b">b & {{ a }}</span></include>

This is because {{ a }} gets rendered to an empty string when nunjucks processes index.md. Making this work would require some rework the page generation logic, which should be in another PR. This rework is also necessary to handle certain cases with set, so it can be fixed along with that.

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Rebased on master

Comment threaddocs/userGuide/reusingContents.md Outdated
Comment threaddocs/userGuide/reusingContents.md
Comment threadsrc/lib/markbind/src/parser.js Outdated
Comment threadsrc/lib/markbind/src/parser.js Outdated
Comment threadsrc/lib/markbind/src/parser.js Outdated
Comment threadtest/test_site/index.md Outdated
Comment threadtest/test_site/testIncludeVariables.md Outdated
Comment threaddocs/userGuide/reusingContents.md
Rename element to includeElement
Fix typos in tests
@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Updated

@yamgent
yamgent self-requested a review January 23, 2019 16:11

@yamgentyamgent left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Some more nits.

Comment threaddocs/userGuide/reusingContents.md Outdated
Comment threaddocs/userGuide/reusingContents.md Outdated
Comment threadtest/test_site/_markbind/variables.md Outdated
Comment threadtest/test_site/index.md Outdated
@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Updated

@yamgentyamgent added this to the v1.17.1 milestone Jan 26, 2019
@yamgent
yamgent merged commit 1eae09f into MarkBind:masterJan 28, 2019
@damithc

Copy link
Copy Markdown
Contributor

Applied this in production nus-cs3281/2019@0d04ee4

Nice work @jamos-tay and reviewers @acjh@yamgent 👍

marvinchin pushed a commit to marvinchin/markbind that referenced this pull request Jan 31, 2019
@yamgentyamgent mentioned this pull request Mar 11, 2019
3 tasks
@ang-zeyuang-zeyu mentioned this pull request Sep 13, 2020
Sign up for freeto 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.

Allow customizing at the point of inclusion

4 participants

@jamos-tay@damithc@acjh@yamgent
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Allow specifying variables in include by jamos-tay · Pull Request #553 · MarkBind/markbind · GitHub
Skip to content

Allow specifying variables in include - #553

Merged
yamgent merged 10 commits into
MarkBind:masterfrom
jamos-tay:included-variables
Jan 28, 2019
Merged

Allow specifying variables in include#553
yamgent merged 10 commits into
MarkBind:masterfrom
jamos-tay:included-variables

Conversation

@jamos-tay

@jamos-tayjamos-tay commented Jan 5, 2019

Copy link
Copy Markdown
Contributor

What is the purpose of this pull request? (put "X" next to an item, remove the rest)

• [X] Enhancement to an existing feature

Fixes#545

What is the rationale for this request?

Users may want to create include file templates with variables that can be filled in, allowing reuse. However, they should only apply to each file. For example,

## {{ title }}
This is a level {{ level }} topic.

Allow users to specify variables in an include tag

What changes did you make? (Give an overview)

Allow users to specify Markbind variables in an include tag:

<include src=”foo.md”>
<span id="title"><span>New Title</span></span> // same syntax as variables.md
<span id="level">2</span>
</include>

These add on to (and overwrite) variables in _markbind/variables.md, and only apply to the context of the included file, and any files that the file includes.

We use nunjuck's or (default) syntax to allow for default values in case the variable is missing: {{ title or "Untitled" }}

Is there anything you'd like reviewers to focus on?

Users cannot reference other variables in the include, like this:

<include src=”foo.md”>
<span id="inner">Inner</span>
<span id="outer">Outer and {{ inner }}</span>
</include>

This is because {{ inner }} gets cleared when the original file is rendered. We can have a separate syntax for these, but it's a little unwieldly.

Referencing variables in _markbind/variables.md is okay.

@jamos-tayjamos-tay changed the title Allow specifying variables in include[WIP] Allow specifying variables in includeJan 5, 2019
@jamos-tayjamos-tay changed the title [WIP] Allow specifying variables in includeAllow specifying variables in includeJan 5, 2019
@damithc

Copy link
Copy Markdown
Contributor

Does this work too? It's not related to includes but possibly related to how the feature is implemented.

<spanid="color">red</span><fontcolor="{{ color }}">my text</font>

@yamgent
yamgent self-requested a review January 6, 2019 02:56
@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

@damithc Yes, tested and works

@damithc

Copy link
Copy Markdown
Contributor

This PR affects the following statement, right?
image

Comment threaddocs/userGuide/reusingContents.md Outdated
Comment threaddocs/userGuide/reusingContents.md Outdated
Comment threaddocs/userGuide/reusingContents.md Outdated
Comment threadsrc/lib/markbind/src/parser.js Outdated
Comment threadsrc/lib/markbind/src/parser.js Outdated
Comment threadtest/test_site/index.md Outdated
Comment threadtest/test_site/testIncludeVariableLeak.md Outdated
Comment threadtest/test_site/testIncludeVariables.md Outdated
@acjh

acjh commented Jan 8, 2019

Copy link
Copy Markdown
Contributor

Please use meaningful commit messages instead of "Pr fixes". 🙈

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Sorry =/

I'll do a quick rebase after it's approved

Comment threaddocs/userGuide/reusingContents.md Outdated

<hr><!-- ======================================================================================================= -->

#### Specifying Variables in the Include Tag

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

  1. Let's follow the same heading level as "<include> Inside an Included File".
  2. Let's use "<include>" rather than "Include Tag" to follow the style of the rest of the documentation.
##### Specifying Variables in an `<include>`

Comment threaddocs/userGuide/reusingContents.md Outdated

#### Specifying Variables in the Include Tag

**It is possible to include variables in the include tag.**

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

**It is possible to declare variables in an `<include>`.**

Comment threaddocs/userGuide/reusingContents.md Outdated

<div class="indented">

{{ icon_example }} Specifying `title` and `author` variables in an `include` tag:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

... in an `<include>` tag:

Comment threaddocs/userGuide/reusingContents.md Outdated
</div>


These variables work the same way as variables in `_markbind/variables.md`, except they only apply to the included file. This can be helpful for creating a template for multiple files.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

  1. Fixing the grammar.
  2. Rephrased the explanation of it being a template mechanism.
..., except that they only apply to the included file. They allow the included file to be reused as a template, for different source files using different variable values.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Side question: do they also work as nunjucks variables? e.g., {{ (size | int) - 1 }} would evaluate to 3 if <span id="size">4</span>

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Tested and it works - but do we want to document this? It seems like a small enough case, and it might confuse the user since it introduces another api

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We already claim that MarkBind is generally compatible with nunjucks syntax. Here, we are using nunjucks syntax for a MarkBind feature. It's better to clarify that these variables are in fact equivalent to nunjucks variables declared within the scope of the included file. However, it can be mentioned in https://markbind.github.io/markbind/userGuide/markBindSyntax.html#support-for-nunjucks rather than under this feature. What do you think?

@damithc

Copy link
Copy Markdown
Contributor

Does this work too? It's not related to includes but possibly related to how the feature is implemented.

<span id="color">red</span>
<font color="{{ color }}">my text</font>

@jamos-tay make sure this is reflected somewhere in the docs too. i.e., together with the d-none attribute, this provides a convenient way to declare local variables and use them in a page.

@jamos-tay

jamos-tay commented Jan 10, 2019

Copy link
Copy Markdown
ContributorAuthor

@jamos-tay make sure this is reflected somewhere in the docs too. i.e., together with the d-none attribute, this provides a convenient way to declare local variables and use them in a page.

@damithc Sorry I think I misunderstood, were you referring to

<span id="color">red</span>
<font color="{{ color }}">my text</font>

within the same .md file? Because that doesn't work... I thought you were asking if you could put variables as element attributes so I put the span in variables.md

I don't think there's a way to create a local variable for a page just yet, but it probably should be in its own PR. I'm pretty sure it requires separate logic (such as placing in the frontmatter) because of the way nunjucks is called.

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Updated

@damithc

Copy link
Copy Markdown
Contributor

within the same .md file? Because that doesn't work... I thought you were asking if you could put variables as element attributes.

I don't think there's a way to create a local variable for a page just yet, but it should be put it in a different PR. I'm pretty sure it requires separate logic (such as placing in the frontmatter) because of the way nunjucks is called.

I see. I thought we got that feature for free. Never mind then. It can be a feature to be added in the future.

Comment threaddocs/userGuide/reusingContents.md Outdated

{{ icon_example }} Specifying a variable as an attribute:

<code>\<span id="color">red</span>\</code><br>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The wrong HTML tag is escaped, it didn't render correctly.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Also it is not clear that the two different code exist in two different places, especially when you look at this portion in the live preview. Maybe indicate one is in variables.md and one is in index.md?

Comment threaddocs/userGuide/reusingContents.md Outdated

<code>\<span id="color">red</span>\</code><br>

<code>\<font color="{<span></span>{ color }}">my text</font>\</code>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Same problem (wrong HTML tag escaped).

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Updated, I decided to delete that section since there was a misunderstanding.

Comment threadsrc/lib/markbind/src/parser.js Outdated
if (!child.attribs.id) {
// eslint-disable-next-line no-console
console.warn(`Missing reference in ${element.attribs[ATTRIB_CWF]}\n`
+ `Missing 'id' in variable for ${element.attribs.src} include.`);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

  1. The "Missing 'id'" part should actually be in front, in order to match the style of other error messages that we show as well.
  2. I realise that the "Missing reference..." should be rephrased (because it is not a missing reference, but rather, a missing variable name), apologies about that.

So it should be something like this:

diff --git a/src/lib/markbind/src/parser.js b/src/lib/markbind/src/parser.js
index 69c40e5..b71b50f 100644
--- a/src/lib/markbind/src/parser.js+++ b/src/lib/markbind/src/parser.js@@ -215,8 +215,8 @@ Parser.prototype._preprocess = function (node, context, config) {
}
if (!child.attribs.id) {
// eslint-disable-next-line no-console
- console.warn(`Missing reference in ${element.attribs[ATTRIB_CWF]}\n`- + `Missing 'id' in variable for ${element.attribs.src} include.`);+ console.warn(`Missing 'id' in variable for referenced file: ${element.attribs.src}.\n`+ + `Missing variable name in ${element.attribs[ATTRIB_CWF]}\n`);
return;
}
includedVariables[child.attribs.id] = cheerio.html(child.children);

@damithc

Copy link
Copy Markdown
Contributor

@jamos-tay do look into how variables propagate via nested includes. Ideally, the variable value should apply to nested includes too and outermost value should override values specified in inner includes.

@yamgent

Copy link
Copy Markdown
Member

Ideally, the variable value should apply to nested includes too and outermost value should override values specified in inner includes.

Do you mean that for the following scenario, the final output should be "Original variable"? The behaviour would look very odd to authors, since the inner page that is "closer" to the final included page should be allowed to have a say over the final variable value, rather than the outer page.


index.md

<includesrc="a.md"><spanid="someVariable">Original variable</span></include>

a.md

<includesrc="b.md"><spanid="someVariable">Nested include variable</span></include>

b.md

{{someVariable}}

@damithc

Copy link
Copy Markdown
Contributor

Do you mean that for the following scenario, the final output should be "Original variable"? The behaviour would look very odd to authors, since the inner page that is "closer" to the final included page should be allowed to have a say over the final variable value, rather than the outer page.

Yes, that feels odd but from a reuse perspective that should be the behavior (opposite of how inheritance works). Otherwise, someone who wants to resue this bit of code has no way to customize the code without modifying it directly, right?

@yamgent

Copy link
Copy Markdown
Member

Otherwise, someone who wants to resue this bit of code has no way to customize the code without modifying it directly, right?

That is indeed true, I think that is the desirable behaviour.

In order to make it easier to explain this behaviour, maybe we should consider adopting a different term rather than calling them all "variables"? Maybe "page variables" vs "global variables"? So we can say that "If a page uses a {{variable}}, then:

  1. Page variables override global variables.
  2. Outer page variables override inner page variables.

@damithc

Copy link
Copy Markdown
Contributor

In order to make it easier to explain this behaviour, maybe we should consider adopting a different term rather than calling them all "variables"?

That is a good idea. One issue is that we use the exact syntax used for nunjucks variables. Are we using nunjucks behind the scene to implement variables? This could become a problem because nunjucks variables are likely to behave differently from ours and users don't have a way to tell the two apart (note that a MarkBind file can contain some nunjucks code too). Is it possible to use a slightly different syntax e.g., {< foo >}?

  1. Page variables override global variables.

This should be the other way around too?

Maybe "page variables" vs "global variables"?

Or stay away from variables altogether. customization?

@yamgent

Copy link
Copy Markdown
Member

That is a good idea. One issue is that we use the exact syntax used for nunjucks variables. Are we using nunjucks behind the scene to implement variables? This could become a problem because nunjucks variables are likely to behave differently from ours and users don't have a way to tell the two apart (note that a MarkBind file can contain some nunjucks code too). Is it possible to use a slightly different syntax e.g., {< foo >}?

Yes, we are using nunjucks behind the scene to implement variables.

And yes there is a chance the authors may mix them up. We can explore using a different syntax in another PR (that would be a breaking change)?

This should be the other way around too?

Got it, global ones should have the highest priority. @jamos-tay you will have to rework this. :P

@damithc

Copy link
Copy Markdown
Contributor

And yes there is a chance the authors may mix them up. We can explore using a different syntax in another PR (that would be a breaking change)?

It would break the syntax introduced in this PR, right? :-) If the new syntax is introduced in this PR, it will not break existing features.
Anyway, it is a big change, and I'm not sure how hard it is to implement, especially if we cannot piggyback on nunjucks anymore. We can discuss on Saturday.

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Hmm... I think it might be possible to reuse the same syntax. I just have to check that if a variable already exists (i.e. declared by an outer file), don't overwrite it.

I'll need a way to detect whether a variable is a global or page variable though, but I think it can be done without using a hacky solution.

@damithc

Copy link
Copy Markdown
Contributor

Hmm... I think it might be possible to reuse the same syntax. I just have to check that if a variable already exists (i.e. declared by an outer file), don't overwrite it.

Reusing the same syntax is appropriate only if a normal nunjucks variable behaves exactly the same way as the ones used for includes. Are we going to apply the same outermost-overrides behavior to normal nunjucks variables as well? i.e., those declared using {% set x = 'foo' %}

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Hmm... Nunjucks overwrites, so it'll use the inner most by default. We might be able to write an extension that will overwrite set's behavior. Alternatively we could strip out {% set %} tags and parse them manually.

If not, are we considering moving the entire variable system off nunjucks? i.e. {< variable >} everywhere? That might be a lot of work just for this feature... considering the global variables.md would have to be migrated over, we'd have to write our own parser logic, and also all older sites would break.

@damithc

Copy link
Copy Markdown
Contributor

Hmm... Nunjucks overwrites, so it'll use the inner most by default. We might be able to write an extension that will overwrite set's behavior. Alternatively we could strip out {% set %} tags and parse them manually.

If not, are we considering moving the entire variable system off nunjucks? i.e. {< variable >} everywhere? That might be a lot of work just for this feature... considering the global variables.md would have to be migrated over, we'd have to write our own parser logic, and also all older sites would break.

Yup, this is a big decision. Better to make it now rather than later but we have to think it through. Although the issue came up in this PR, we are actually dealing with a much wider issue, namely, the need to support the outer-overrides-inner behavior that is central to reuse but opposite of the normal template behavior. The problem seem to be that we cannot support two opposing behaviors using the same internal library and/or syntax.

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

After discussion:

We'll go with the original behavior:
Global variables override included variables
Outer include variables override inner include variables
{% set ... %} overrides all variables, but only applies to the page (excluding includes)

E.g.
variables.md: a = 1

a.md

<include src = b.md>
a = 2
b = 2
</include>

b.md

{{ a }} // 1
{{ b }} // 2
{% set a = 4 %}
{{ a }} // 4
<include src = c.md>
a = 3
b = 3
</include>

c.md

{{ a }} // 1
{{ b }} // 2

Comment threadsrc/lib/markbind/src/parser.js Outdated
@jamos-tay

jamos-tay commented Jan 19, 2019

Copy link
Copy Markdown
ContributorAuthor

Updated

Note: We currently do not support include variables referencing variables within the same include:

index.md:

<includesrc="a.md"><spanid="a">a</span><spanid="b">b & {{ a }}</span></include>

This is because {{ a }} gets rendered to an empty string when nunjucks processes index.md. Making this work would require some rework the page generation logic, which should be in another PR. This rework is also necessary to handle certain cases with set, so it can be fixed along with that.

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Rebased on master

Comment threaddocs/userGuide/reusingContents.md Outdated
Comment threaddocs/userGuide/reusingContents.md
Comment threadsrc/lib/markbind/src/parser.js Outdated
Comment threadsrc/lib/markbind/src/parser.js Outdated
Comment threadsrc/lib/markbind/src/parser.js Outdated
Comment threadtest/test_site/index.md Outdated
Comment threadtest/test_site/testIncludeVariables.md Outdated
Comment threaddocs/userGuide/reusingContents.md
Rename element to includeElement
Fix typos in tests
@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Updated

@yamgent
yamgent self-requested a review January 23, 2019 16:11

@yamgentyamgent left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Some more nits.

Comment threaddocs/userGuide/reusingContents.md Outdated
Comment threaddocs/userGuide/reusingContents.md Outdated
Comment threadtest/test_site/_markbind/variables.md Outdated
Comment threadtest/test_site/index.md Outdated
@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Updated

@yamgentyamgent added this to the v1.17.1 milestone Jan 26, 2019
@yamgent
yamgent merged commit 1eae09f into MarkBind:masterJan 28, 2019
@damithc

Copy link
Copy Markdown
Contributor

Applied this in production nus-cs3281/2019@0d04ee4

Nice work @jamos-tay and reviewers @acjh@yamgent 👍

marvinchin pushed a commit to marvinchin/markbind that referenced this pull request Jan 31, 2019
@yamgentyamgent mentioned this pull request Mar 11, 2019
3 tasks
@ang-zeyuang-zeyu mentioned this pull request Sep 13, 2020
Sign up for freeto 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.

Allow customizing at the point of inclusion

4 participants

@jamos-tay@damithc@acjh@yamgent
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Allow specifying variables in include by jamos-tay · Pull Request #553 · MarkBind/markbind · GitHub
Skip to content

Allow specifying variables in include - #553

Merged
yamgent merged 10 commits into
MarkBind:masterfrom
jamos-tay:included-variables
Jan 28, 2019
Merged

Allow specifying variables in include#553
yamgent merged 10 commits into
MarkBind:masterfrom
jamos-tay:included-variables

Conversation

@jamos-tay

@jamos-tayjamos-tay commented Jan 5, 2019

Copy link
Copy Markdown
Contributor

What is the purpose of this pull request? (put "X" next to an item, remove the rest)

• [X] Enhancement to an existing feature

Fixes#545

What is the rationale for this request?

Users may want to create include file templates with variables that can be filled in, allowing reuse. However, they should only apply to each file. For example,

## {{ title }}
This is a level {{ level }} topic.

Allow users to specify variables in an include tag

What changes did you make? (Give an overview)

Allow users to specify Markbind variables in an include tag:

<include src=”foo.md”>
<span id="title"><span>New Title</span></span> // same syntax as variables.md
<span id="level">2</span>
</include>

These add on to (and overwrite) variables in _markbind/variables.md, and only apply to the context of the included file, and any files that the file includes.

We use nunjuck's or (default) syntax to allow for default values in case the variable is missing: {{ title or "Untitled" }}

Is there anything you'd like reviewers to focus on?

Users cannot reference other variables in the include, like this:

<include src=”foo.md”>
<span id="inner">Inner</span>
<span id="outer">Outer and {{ inner }}</span>
</include>

This is because {{ inner }} gets cleared when the original file is rendered. We can have a separate syntax for these, but it's a little unwieldly.

Referencing variables in _markbind/variables.md is okay.

@jamos-tayjamos-tay changed the title Allow specifying variables in include[WIP] Allow specifying variables in includeJan 5, 2019
@jamos-tayjamos-tay changed the title [WIP] Allow specifying variables in includeAllow specifying variables in includeJan 5, 2019
@damithc

Copy link
Copy Markdown
Contributor

Does this work too? It's not related to includes but possibly related to how the feature is implemented.

<spanid="color">red</span><fontcolor="{{ color }}">my text</font>

@yamgent
yamgent self-requested a review January 6, 2019 02:56
@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

@damithc Yes, tested and works

@damithc

Copy link
Copy Markdown
Contributor

This PR affects the following statement, right?
image

Comment threaddocs/userGuide/reusingContents.md Outdated
Comment threaddocs/userGuide/reusingContents.md Outdated
Comment threaddocs/userGuide/reusingContents.md Outdated
Comment threadsrc/lib/markbind/src/parser.js Outdated
Comment threadsrc/lib/markbind/src/parser.js Outdated
Comment threadtest/test_site/index.md Outdated
Comment threadtest/test_site/testIncludeVariableLeak.md Outdated
Comment threadtest/test_site/testIncludeVariables.md Outdated
@acjh

acjh commented Jan 8, 2019

Copy link
Copy Markdown
Contributor

Please use meaningful commit messages instead of "Pr fixes". 🙈

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Sorry =/

I'll do a quick rebase after it's approved

Comment threaddocs/userGuide/reusingContents.md Outdated

<hr><!-- ======================================================================================================= -->

#### Specifying Variables in the Include Tag

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

  1. Let's follow the same heading level as "<include> Inside an Included File".
  2. Let's use "<include>" rather than "Include Tag" to follow the style of the rest of the documentation.
##### Specifying Variables in an `<include>`

Comment threaddocs/userGuide/reusingContents.md Outdated

#### Specifying Variables in the Include Tag

**It is possible to include variables in the include tag.**

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

**It is possible to declare variables in an `<include>`.**

Comment threaddocs/userGuide/reusingContents.md Outdated

<div class="indented">

{{ icon_example }} Specifying `title` and `author` variables in an `include` tag:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

... in an `<include>` tag:

Comment threaddocs/userGuide/reusingContents.md Outdated
</div>


These variables work the same way as variables in `_markbind/variables.md`, except they only apply to the included file. This can be helpful for creating a template for multiple files.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

  1. Fixing the grammar.
  2. Rephrased the explanation of it being a template mechanism.
..., except that they only apply to the included file. They allow the included file to be reused as a template, for different source files using different variable values.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Side question: do they also work as nunjucks variables? e.g., {{ (size | int) - 1 }} would evaluate to 3 if <span id="size">4</span>

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Tested and it works - but do we want to document this? It seems like a small enough case, and it might confuse the user since it introduces another api

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We already claim that MarkBind is generally compatible with nunjucks syntax. Here, we are using nunjucks syntax for a MarkBind feature. It's better to clarify that these variables are in fact equivalent to nunjucks variables declared within the scope of the included file. However, it can be mentioned in https://markbind.github.io/markbind/userGuide/markBindSyntax.html#support-for-nunjucks rather than under this feature. What do you think?

@damithc

Copy link
Copy Markdown
Contributor

Does this work too? It's not related to includes but possibly related to how the feature is implemented.

<span id="color">red</span>
<font color="{{ color }}">my text</font>

@jamos-tay make sure this is reflected somewhere in the docs too. i.e., together with the d-none attribute, this provides a convenient way to declare local variables and use them in a page.

@jamos-tay

jamos-tay commented Jan 10, 2019

Copy link
Copy Markdown
ContributorAuthor

@jamos-tay make sure this is reflected somewhere in the docs too. i.e., together with the d-none attribute, this provides a convenient way to declare local variables and use them in a page.

@damithc Sorry I think I misunderstood, were you referring to

<span id="color">red</span>
<font color="{{ color }}">my text</font>

within the same .md file? Because that doesn't work... I thought you were asking if you could put variables as element attributes so I put the span in variables.md

I don't think there's a way to create a local variable for a page just yet, but it probably should be in its own PR. I'm pretty sure it requires separate logic (such as placing in the frontmatter) because of the way nunjucks is called.

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Updated

@damithc

Copy link
Copy Markdown
Contributor

within the same .md file? Because that doesn't work... I thought you were asking if you could put variables as element attributes.

I don't think there's a way to create a local variable for a page just yet, but it should be put it in a different PR. I'm pretty sure it requires separate logic (such as placing in the frontmatter) because of the way nunjucks is called.

I see. I thought we got that feature for free. Never mind then. It can be a feature to be added in the future.

Comment threaddocs/userGuide/reusingContents.md Outdated

{{ icon_example }} Specifying a variable as an attribute:

<code>\<span id="color">red</span>\</code><br>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The wrong HTML tag is escaped, it didn't render correctly.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Also it is not clear that the two different code exist in two different places, especially when you look at this portion in the live preview. Maybe indicate one is in variables.md and one is in index.md?

Comment threaddocs/userGuide/reusingContents.md Outdated

<code>\<span id="color">red</span>\</code><br>

<code>\<font color="{<span></span>{ color }}">my text</font>\</code>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Same problem (wrong HTML tag escaped).

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Updated, I decided to delete that section since there was a misunderstanding.

Comment threadsrc/lib/markbind/src/parser.js Outdated
if (!child.attribs.id) {
// eslint-disable-next-line no-console
console.warn(`Missing reference in ${element.attribs[ATTRIB_CWF]}\n`
+ `Missing 'id' in variable for ${element.attribs.src} include.`);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

  1. The "Missing 'id'" part should actually be in front, in order to match the style of other error messages that we show as well.
  2. I realise that the "Missing reference..." should be rephrased (because it is not a missing reference, but rather, a missing variable name), apologies about that.

So it should be something like this:

diff --git a/src/lib/markbind/src/parser.js b/src/lib/markbind/src/parser.js
index 69c40e5..b71b50f 100644
--- a/src/lib/markbind/src/parser.js+++ b/src/lib/markbind/src/parser.js@@ -215,8 +215,8 @@ Parser.prototype._preprocess = function (node, context, config) {
}
if (!child.attribs.id) {
// eslint-disable-next-line no-console
- console.warn(`Missing reference in ${element.attribs[ATTRIB_CWF]}\n`- + `Missing 'id' in variable for ${element.attribs.src} include.`);+ console.warn(`Missing 'id' in variable for referenced file: ${element.attribs.src}.\n`+ + `Missing variable name in ${element.attribs[ATTRIB_CWF]}\n`);
return;
}
includedVariables[child.attribs.id] = cheerio.html(child.children);

@damithc

Copy link
Copy Markdown
Contributor

@jamos-tay do look into how variables propagate via nested includes. Ideally, the variable value should apply to nested includes too and outermost value should override values specified in inner includes.

@yamgent

Copy link
Copy Markdown
Member

Ideally, the variable value should apply to nested includes too and outermost value should override values specified in inner includes.

Do you mean that for the following scenario, the final output should be "Original variable"? The behaviour would look very odd to authors, since the inner page that is "closer" to the final included page should be allowed to have a say over the final variable value, rather than the outer page.


index.md

<includesrc="a.md"><spanid="someVariable">Original variable</span></include>

a.md

<includesrc="b.md"><spanid="someVariable">Nested include variable</span></include>

b.md

{{someVariable}}

@damithc

Copy link
Copy Markdown
Contributor

Do you mean that for the following scenario, the final output should be "Original variable"? The behaviour would look very odd to authors, since the inner page that is "closer" to the final included page should be allowed to have a say over the final variable value, rather than the outer page.

Yes, that feels odd but from a reuse perspective that should be the behavior (opposite of how inheritance works). Otherwise, someone who wants to resue this bit of code has no way to customize the code without modifying it directly, right?

@yamgent

Copy link
Copy Markdown
Member

Otherwise, someone who wants to resue this bit of code has no way to customize the code without modifying it directly, right?

That is indeed true, I think that is the desirable behaviour.

In order to make it easier to explain this behaviour, maybe we should consider adopting a different term rather than calling them all "variables"? Maybe "page variables" vs "global variables"? So we can say that "If a page uses a {{variable}}, then:

  1. Page variables override global variables.
  2. Outer page variables override inner page variables.

@damithc

Copy link
Copy Markdown
Contributor

In order to make it easier to explain this behaviour, maybe we should consider adopting a different term rather than calling them all "variables"?

That is a good idea. One issue is that we use the exact syntax used for nunjucks variables. Are we using nunjucks behind the scene to implement variables? This could become a problem because nunjucks variables are likely to behave differently from ours and users don't have a way to tell the two apart (note that a MarkBind file can contain some nunjucks code too). Is it possible to use a slightly different syntax e.g., {< foo >}?

  1. Page variables override global variables.

This should be the other way around too?

Maybe "page variables" vs "global variables"?

Or stay away from variables altogether. customization?

@yamgent

Copy link
Copy Markdown
Member

That is a good idea. One issue is that we use the exact syntax used for nunjucks variables. Are we using nunjucks behind the scene to implement variables? This could become a problem because nunjucks variables are likely to behave differently from ours and users don't have a way to tell the two apart (note that a MarkBind file can contain some nunjucks code too). Is it possible to use a slightly different syntax e.g., {< foo >}?

Yes, we are using nunjucks behind the scene to implement variables.

And yes there is a chance the authors may mix them up. We can explore using a different syntax in another PR (that would be a breaking change)?

This should be the other way around too?

Got it, global ones should have the highest priority. @jamos-tay you will have to rework this. :P

@damithc

Copy link
Copy Markdown
Contributor

And yes there is a chance the authors may mix them up. We can explore using a different syntax in another PR (that would be a breaking change)?

It would break the syntax introduced in this PR, right? :-) If the new syntax is introduced in this PR, it will not break existing features.
Anyway, it is a big change, and I'm not sure how hard it is to implement, especially if we cannot piggyback on nunjucks anymore. We can discuss on Saturday.

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Hmm... I think it might be possible to reuse the same syntax. I just have to check that if a variable already exists (i.e. declared by an outer file), don't overwrite it.

I'll need a way to detect whether a variable is a global or page variable though, but I think it can be done without using a hacky solution.

@damithc

Copy link
Copy Markdown
Contributor

Hmm... I think it might be possible to reuse the same syntax. I just have to check that if a variable already exists (i.e. declared by an outer file), don't overwrite it.

Reusing the same syntax is appropriate only if a normal nunjucks variable behaves exactly the same way as the ones used for includes. Are we going to apply the same outermost-overrides behavior to normal nunjucks variables as well? i.e., those declared using {% set x = 'foo' %}

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Hmm... Nunjucks overwrites, so it'll use the inner most by default. We might be able to write an extension that will overwrite set's behavior. Alternatively we could strip out {% set %} tags and parse them manually.

If not, are we considering moving the entire variable system off nunjucks? i.e. {< variable >} everywhere? That might be a lot of work just for this feature... considering the global variables.md would have to be migrated over, we'd have to write our own parser logic, and also all older sites would break.

@damithc

Copy link
Copy Markdown
Contributor

Hmm... Nunjucks overwrites, so it'll use the inner most by default. We might be able to write an extension that will overwrite set's behavior. Alternatively we could strip out {% set %} tags and parse them manually.

If not, are we considering moving the entire variable system off nunjucks? i.e. {< variable >} everywhere? That might be a lot of work just for this feature... considering the global variables.md would have to be migrated over, we'd have to write our own parser logic, and also all older sites would break.

Yup, this is a big decision. Better to make it now rather than later but we have to think it through. Although the issue came up in this PR, we are actually dealing with a much wider issue, namely, the need to support the outer-overrides-inner behavior that is central to reuse but opposite of the normal template behavior. The problem seem to be that we cannot support two opposing behaviors using the same internal library and/or syntax.

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

After discussion:

We'll go with the original behavior:
Global variables override included variables
Outer include variables override inner include variables
{% set ... %} overrides all variables, but only applies to the page (excluding includes)

E.g.
variables.md: a = 1

a.md

<include src = b.md>
a = 2
b = 2
</include>

b.md

{{ a }} // 1
{{ b }} // 2
{% set a = 4 %}
{{ a }} // 4
<include src = c.md>
a = 3
b = 3
</include>

c.md

{{ a }} // 1
{{ b }} // 2

Comment threadsrc/lib/markbind/src/parser.js Outdated
@jamos-tay

jamos-tay commented Jan 19, 2019

Copy link
Copy Markdown
ContributorAuthor

Updated

Note: We currently do not support include variables referencing variables within the same include:

index.md:

<includesrc="a.md"><spanid="a">a</span><spanid="b">b & {{ a }}</span></include>

This is because {{ a }} gets rendered to an empty string when nunjucks processes index.md. Making this work would require some rework the page generation logic, which should be in another PR. This rework is also necessary to handle certain cases with set, so it can be fixed along with that.

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Rebased on master

Comment threaddocs/userGuide/reusingContents.md Outdated
Comment threaddocs/userGuide/reusingContents.md
Comment threadsrc/lib/markbind/src/parser.js Outdated
Comment threadsrc/lib/markbind/src/parser.js Outdated
Comment threadsrc/lib/markbind/src/parser.js Outdated
Comment threadtest/test_site/index.md Outdated
Comment threadtest/test_site/testIncludeVariables.md Outdated
Comment threaddocs/userGuide/reusingContents.md
Rename element to includeElement
Fix typos in tests
@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Updated

@yamgent
yamgent self-requested a review January 23, 2019 16:11

@yamgentyamgent left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Some more nits.

Comment threaddocs/userGuide/reusingContents.md Outdated
Comment threaddocs/userGuide/reusingContents.md Outdated
Comment threadtest/test_site/_markbind/variables.md Outdated
Comment threadtest/test_site/index.md Outdated
@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Updated

@yamgentyamgent added this to the v1.17.1 milestone Jan 26, 2019
@yamgent
yamgent merged commit 1eae09f into MarkBind:masterJan 28, 2019
@damithc

Copy link
Copy Markdown
Contributor

Applied this in production nus-cs3281/2019@0d04ee4

Nice work @jamos-tay and reviewers @acjh@yamgent 👍

marvinchin pushed a commit to marvinchin/markbind that referenced this pull request Jan 31, 2019
@yamgentyamgent mentioned this pull request Mar 11, 2019
3 tasks
@ang-zeyuang-zeyu mentioned this pull request Sep 13, 2020
Sign up for freeto 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.

Allow customizing at the point of inclusion

4 participants

@jamos-tay@damithc@acjh@yamgent
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); Allow specifying variables in include by jamos-tay · Pull Request #553 · MarkBind/markbind · GitHub
Skip to content

Allow specifying variables in include - #553

Merged
yamgent merged 10 commits into
MarkBind:masterfrom
jamos-tay:included-variables
Jan 28, 2019
Merged

Allow specifying variables in include#553
yamgent merged 10 commits into
MarkBind:masterfrom
jamos-tay:included-variables

Conversation

@jamos-tay

@jamos-tayjamos-tay commented Jan 5, 2019

Copy link
Copy Markdown
Contributor

What is the purpose of this pull request? (put "X" next to an item, remove the rest)

• [X] Enhancement to an existing feature

Fixes#545

What is the rationale for this request?

Users may want to create include file templates with variables that can be filled in, allowing reuse. However, they should only apply to each file. For example,

## {{ title }}
This is a level {{ level }} topic.

Allow users to specify variables in an include tag

What changes did you make? (Give an overview)

Allow users to specify Markbind variables in an include tag:

<include src=”foo.md”>
<span id="title"><span>New Title</span></span> // same syntax as variables.md
<span id="level">2</span>
</include>

These add on to (and overwrite) variables in _markbind/variables.md, and only apply to the context of the included file, and any files that the file includes.

We use nunjuck's or (default) syntax to allow for default values in case the variable is missing: {{ title or "Untitled" }}

Is there anything you'd like reviewers to focus on?

Users cannot reference other variables in the include, like this:

<include src=”foo.md”>
<span id="inner">Inner</span>
<span id="outer">Outer and {{ inner }}</span>
</include>

This is because {{ inner }} gets cleared when the original file is rendered. We can have a separate syntax for these, but it's a little unwieldly.

Referencing variables in _markbind/variables.md is okay.

@jamos-tayjamos-tay changed the title Allow specifying variables in include[WIP] Allow specifying variables in includeJan 5, 2019
@jamos-tayjamos-tay changed the title [WIP] Allow specifying variables in includeAllow specifying variables in includeJan 5, 2019
@damithc

Copy link
Copy Markdown
Contributor

Does this work too? It's not related to includes but possibly related to how the feature is implemented.

<spanid="color">red</span><fontcolor="{{ color }}">my text</font>

@yamgent
yamgent self-requested a review January 6, 2019 02:56
@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

@damithc Yes, tested and works

@damithc

Copy link
Copy Markdown
Contributor

This PR affects the following statement, right?
image

Comment threaddocs/userGuide/reusingContents.md Outdated
Comment threaddocs/userGuide/reusingContents.md Outdated
Comment threaddocs/userGuide/reusingContents.md Outdated
Comment threadsrc/lib/markbind/src/parser.js Outdated
Comment threadsrc/lib/markbind/src/parser.js Outdated
Comment threadtest/test_site/index.md Outdated
Comment threadtest/test_site/testIncludeVariableLeak.md Outdated
Comment threadtest/test_site/testIncludeVariables.md Outdated
@acjh

acjh commented Jan 8, 2019

Copy link
Copy Markdown
Contributor

Please use meaningful commit messages instead of "Pr fixes". 🙈

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Sorry =/

I'll do a quick rebase after it's approved

Comment threaddocs/userGuide/reusingContents.md Outdated

<hr><!-- ======================================================================================================= -->

#### Specifying Variables in the Include Tag

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

  1. Let's follow the same heading level as "<include> Inside an Included File".
  2. Let's use "<include>" rather than "Include Tag" to follow the style of the rest of the documentation.
##### Specifying Variables in an `<include>`

Comment threaddocs/userGuide/reusingContents.md Outdated

#### Specifying Variables in the Include Tag

**It is possible to include variables in the include tag.**

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

**It is possible to declare variables in an `<include>`.**

Comment threaddocs/userGuide/reusingContents.md Outdated

<div class="indented">

{{ icon_example }} Specifying `title` and `author` variables in an `include` tag:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

... in an `<include>` tag:

Comment threaddocs/userGuide/reusingContents.md Outdated
</div>


These variables work the same way as variables in `_markbind/variables.md`, except they only apply to the included file. This can be helpful for creating a template for multiple files.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

  1. Fixing the grammar.
  2. Rephrased the explanation of it being a template mechanism.
..., except that they only apply to the included file. They allow the included file to be reused as a template, for different source files using different variable values.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Side question: do they also work as nunjucks variables? e.g., {{ (size | int) - 1 }} would evaluate to 3 if <span id="size">4</span>

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Tested and it works - but do we want to document this? It seems like a small enough case, and it might confuse the user since it introduces another api

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We already claim that MarkBind is generally compatible with nunjucks syntax. Here, we are using nunjucks syntax for a MarkBind feature. It's better to clarify that these variables are in fact equivalent to nunjucks variables declared within the scope of the included file. However, it can be mentioned in https://markbind.github.io/markbind/userGuide/markBindSyntax.html#support-for-nunjucks rather than under this feature. What do you think?

@damithc

Copy link
Copy Markdown
Contributor

Does this work too? It's not related to includes but possibly related to how the feature is implemented.

<span id="color">red</span>
<font color="{{ color }}">my text</font>

@jamos-tay make sure this is reflected somewhere in the docs too. i.e., together with the d-none attribute, this provides a convenient way to declare local variables and use them in a page.

@jamos-tay

jamos-tay commented Jan 10, 2019

Copy link
Copy Markdown
ContributorAuthor

@jamos-tay make sure this is reflected somewhere in the docs too. i.e., together with the d-none attribute, this provides a convenient way to declare local variables and use them in a page.

@damithc Sorry I think I misunderstood, were you referring to

<span id="color">red</span>
<font color="{{ color }}">my text</font>

within the same .md file? Because that doesn't work... I thought you were asking if you could put variables as element attributes so I put the span in variables.md

I don't think there's a way to create a local variable for a page just yet, but it probably should be in its own PR. I'm pretty sure it requires separate logic (such as placing in the frontmatter) because of the way nunjucks is called.

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Updated

@damithc

Copy link
Copy Markdown
Contributor

within the same .md file? Because that doesn't work... I thought you were asking if you could put variables as element attributes.

I don't think there's a way to create a local variable for a page just yet, but it should be put it in a different PR. I'm pretty sure it requires separate logic (such as placing in the frontmatter) because of the way nunjucks is called.

I see. I thought we got that feature for free. Never mind then. It can be a feature to be added in the future.

Comment threaddocs/userGuide/reusingContents.md Outdated

{{ icon_example }} Specifying a variable as an attribute:

<code>\<span id="color">red</span>\</code><br>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The wrong HTML tag is escaped, it didn't render correctly.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Also it is not clear that the two different code exist in two different places, especially when you look at this portion in the live preview. Maybe indicate one is in variables.md and one is in index.md?

Comment threaddocs/userGuide/reusingContents.md Outdated

<code>\<span id="color">red</span>\</code><br>

<code>\<font color="{<span></span>{ color }}">my text</font>\</code>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Same problem (wrong HTML tag escaped).

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Updated, I decided to delete that section since there was a misunderstanding.

Comment threadsrc/lib/markbind/src/parser.js Outdated
if (!child.attribs.id) {
// eslint-disable-next-line no-console
console.warn(`Missing reference in ${element.attribs[ATTRIB_CWF]}\n`
+ `Missing 'id' in variable for ${element.attribs.src} include.`);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

  1. The "Missing 'id'" part should actually be in front, in order to match the style of other error messages that we show as well.
  2. I realise that the "Missing reference..." should be rephrased (because it is not a missing reference, but rather, a missing variable name), apologies about that.

So it should be something like this:

diff --git a/src/lib/markbind/src/parser.js b/src/lib/markbind/src/parser.js
index 69c40e5..b71b50f 100644
--- a/src/lib/markbind/src/parser.js+++ b/src/lib/markbind/src/parser.js@@ -215,8 +215,8 @@ Parser.prototype._preprocess = function (node, context, config) {
}
if (!child.attribs.id) {
// eslint-disable-next-line no-console
- console.warn(`Missing reference in ${element.attribs[ATTRIB_CWF]}\n`- + `Missing 'id' in variable for ${element.attribs.src} include.`);+ console.warn(`Missing 'id' in variable for referenced file: ${element.attribs.src}.\n`+ + `Missing variable name in ${element.attribs[ATTRIB_CWF]}\n`);
return;
}
includedVariables[child.attribs.id] = cheerio.html(child.children);

@damithc

Copy link
Copy Markdown
Contributor

@jamos-tay do look into how variables propagate via nested includes. Ideally, the variable value should apply to nested includes too and outermost value should override values specified in inner includes.

@yamgent

Copy link
Copy Markdown
Member

Ideally, the variable value should apply to nested includes too and outermost value should override values specified in inner includes.

Do you mean that for the following scenario, the final output should be "Original variable"? The behaviour would look very odd to authors, since the inner page that is "closer" to the final included page should be allowed to have a say over the final variable value, rather than the outer page.


index.md

<includesrc="a.md"><spanid="someVariable">Original variable</span></include>

a.md

<includesrc="b.md"><spanid="someVariable">Nested include variable</span></include>

b.md

{{someVariable}}

@damithc

Copy link
Copy Markdown
Contributor

Do you mean that for the following scenario, the final output should be "Original variable"? The behaviour would look very odd to authors, since the inner page that is "closer" to the final included page should be allowed to have a say over the final variable value, rather than the outer page.

Yes, that feels odd but from a reuse perspective that should be the behavior (opposite of how inheritance works). Otherwise, someone who wants to resue this bit of code has no way to customize the code without modifying it directly, right?

@yamgent

Copy link
Copy Markdown
Member

Otherwise, someone who wants to resue this bit of code has no way to customize the code without modifying it directly, right?

That is indeed true, I think that is the desirable behaviour.

In order to make it easier to explain this behaviour, maybe we should consider adopting a different term rather than calling them all "variables"? Maybe "page variables" vs "global variables"? So we can say that "If a page uses a {{variable}}, then:

  1. Page variables override global variables.
  2. Outer page variables override inner page variables.

@damithc

Copy link
Copy Markdown
Contributor

In order to make it easier to explain this behaviour, maybe we should consider adopting a different term rather than calling them all "variables"?

That is a good idea. One issue is that we use the exact syntax used for nunjucks variables. Are we using nunjucks behind the scene to implement variables? This could become a problem because nunjucks variables are likely to behave differently from ours and users don't have a way to tell the two apart (note that a MarkBind file can contain some nunjucks code too). Is it possible to use a slightly different syntax e.g., {< foo >}?

  1. Page variables override global variables.

This should be the other way around too?

Maybe "page variables" vs "global variables"?

Or stay away from variables altogether. customization?

@yamgent

Copy link
Copy Markdown
Member

That is a good idea. One issue is that we use the exact syntax used for nunjucks variables. Are we using nunjucks behind the scene to implement variables? This could become a problem because nunjucks variables are likely to behave differently from ours and users don't have a way to tell the two apart (note that a MarkBind file can contain some nunjucks code too). Is it possible to use a slightly different syntax e.g., {< foo >}?

Yes, we are using nunjucks behind the scene to implement variables.

And yes there is a chance the authors may mix them up. We can explore using a different syntax in another PR (that would be a breaking change)?

This should be the other way around too?

Got it, global ones should have the highest priority. @jamos-tay you will have to rework this. :P

@damithc

Copy link
Copy Markdown
Contributor

And yes there is a chance the authors may mix them up. We can explore using a different syntax in another PR (that would be a breaking change)?

It would break the syntax introduced in this PR, right? :-) If the new syntax is introduced in this PR, it will not break existing features.
Anyway, it is a big change, and I'm not sure how hard it is to implement, especially if we cannot piggyback on nunjucks anymore. We can discuss on Saturday.

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Hmm... I think it might be possible to reuse the same syntax. I just have to check that if a variable already exists (i.e. declared by an outer file), don't overwrite it.

I'll need a way to detect whether a variable is a global or page variable though, but I think it can be done without using a hacky solution.

@damithc

Copy link
Copy Markdown
Contributor

Hmm... I think it might be possible to reuse the same syntax. I just have to check that if a variable already exists (i.e. declared by an outer file), don't overwrite it.

Reusing the same syntax is appropriate only if a normal nunjucks variable behaves exactly the same way as the ones used for includes. Are we going to apply the same outermost-overrides behavior to normal nunjucks variables as well? i.e., those declared using {% set x = 'foo' %}

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Hmm... Nunjucks overwrites, so it'll use the inner most by default. We might be able to write an extension that will overwrite set's behavior. Alternatively we could strip out {% set %} tags and parse them manually.

If not, are we considering moving the entire variable system off nunjucks? i.e. {< variable >} everywhere? That might be a lot of work just for this feature... considering the global variables.md would have to be migrated over, we'd have to write our own parser logic, and also all older sites would break.

@damithc

Copy link
Copy Markdown
Contributor

Hmm... Nunjucks overwrites, so it'll use the inner most by default. We might be able to write an extension that will overwrite set's behavior. Alternatively we could strip out {% set %} tags and parse them manually.

If not, are we considering moving the entire variable system off nunjucks? i.e. {< variable >} everywhere? That might be a lot of work just for this feature... considering the global variables.md would have to be migrated over, we'd have to write our own parser logic, and also all older sites would break.

Yup, this is a big decision. Better to make it now rather than later but we have to think it through. Although the issue came up in this PR, we are actually dealing with a much wider issue, namely, the need to support the outer-overrides-inner behavior that is central to reuse but opposite of the normal template behavior. The problem seem to be that we cannot support two opposing behaviors using the same internal library and/or syntax.

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

After discussion:

We'll go with the original behavior:
Global variables override included variables
Outer include variables override inner include variables
{% set ... %} overrides all variables, but only applies to the page (excluding includes)

E.g.
variables.md: a = 1

a.md

<include src = b.md>
a = 2
b = 2
</include>

b.md

{{ a }} // 1
{{ b }} // 2
{% set a = 4 %}
{{ a }} // 4
<include src = c.md>
a = 3
b = 3
</include>

c.md

{{ a }} // 1
{{ b }} // 2

Comment threadsrc/lib/markbind/src/parser.js Outdated
@jamos-tay

jamos-tay commented Jan 19, 2019

Copy link
Copy Markdown
ContributorAuthor

Updated

Note: We currently do not support include variables referencing variables within the same include:

index.md:

<includesrc="a.md"><spanid="a">a</span><spanid="b">b & {{ a }}</span></include>

This is because {{ a }} gets rendered to an empty string when nunjucks processes index.md. Making this work would require some rework the page generation logic, which should be in another PR. This rework is also necessary to handle certain cases with set, so it can be fixed along with that.

@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Rebased on master

Comment threaddocs/userGuide/reusingContents.md Outdated
Comment threaddocs/userGuide/reusingContents.md
Comment threadsrc/lib/markbind/src/parser.js Outdated
Comment threadsrc/lib/markbind/src/parser.js Outdated
Comment threadsrc/lib/markbind/src/parser.js Outdated
Comment threadtest/test_site/index.md Outdated
Comment threadtest/test_site/testIncludeVariables.md Outdated
Comment threaddocs/userGuide/reusingContents.md
Rename element to includeElement
Fix typos in tests
@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Updated

@yamgent
yamgent self-requested a review January 23, 2019 16:11

@yamgentyamgent left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Some more nits.

Comment threaddocs/userGuide/reusingContents.md Outdated
Comment threaddocs/userGuide/reusingContents.md Outdated
Comment threadtest/test_site/_markbind/variables.md Outdated
Comment threadtest/test_site/index.md Outdated
@jamos-tay

Copy link
Copy Markdown
ContributorAuthor

Updated

@yamgentyamgent added this to the v1.17.1 milestone Jan 26, 2019
@yamgent
yamgent merged commit 1eae09f into MarkBind:masterJan 28, 2019
@damithc

Copy link
Copy Markdown
Contributor

Applied this in production nus-cs3281/2019@0d04ee4

Nice work @jamos-tay and reviewers @acjh@yamgent 👍

marvinchin pushed a commit to marvinchin/markbind that referenced this pull request Jan 31, 2019
@yamgentyamgent mentioned this pull request Mar 11, 2019
3 tasks
@ang-zeyuang-zeyu mentioned this pull request Sep 13, 2020
Sign up for freeto 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.

Allow customizing at the point of inclusion

4 participants

@jamos-tay@damithc@acjh@yamgent