mirror of
https://github.com/flynx/pWiki.git
synced 2026-07-13 20:10:57 +00:00
tweaks and fixes...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
parent
97fb6ee856
commit
7b0aab07da
@ -261,25 +261,80 @@ macro at the end of the page._
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
### include (src isolated text)
|
### include (src isolated recursive) / content
|
||||||
|
|
||||||
Include a page. The included page is rendered independently from current
|
```
|
||||||
page and is inserted as-is in macro body.
|
@include(<src>)
|
||||||
|
@include(<src> [recursive=".."] [join=".."] [isolated])
|
||||||
|
@include(<src> [recursive=".."] [join=".."] [isolated="partial"])
|
||||||
|
|
||||||
Note that this will produce a `include` tag in the code that contains
|
<include <src>>
|
||||||
the included page, this makes this tag not suitable for use anywhere
|
...
|
||||||
but an html element body.
|
<content/>
|
||||||
|
...
|
||||||
|
</include>
|
||||||
|
```
|
||||||
|
|
||||||
Arguments:
|
Include a page. The included page is rendered relative to the spurce page
|
||||||
- `src` -- path to source page.
|
resolved from `<src>`, independently from current page, but in the current
|
||||||
- `isolated` -- prevent slots from included page from affecting the including page.
|
namespace, and is inserted as-is.
|
||||||
- `text` -- is used when recursive include is detected and ignored otherwise.
|
|
||||||
|
|
||||||
_For examples see `slot` macro exaples below._
|
If body is not empty, it will render instead of included page content.
|
||||||
|
If body contains `<content/>` macro, it will be replaced with included
|
||||||
|
page content.
|
||||||
|
If more than one page matches `<src>`, the body will be copied once per
|
||||||
|
page and the page's content will be inserted into each respective `<content/>`
|
||||||
|
macro.
|
||||||
|
|
||||||
|
`join` is inserted between pages if more than one page is matched by `<src>`.
|
||||||
|
|
||||||
|
If `isolated` is given, prevent slots from included page from affecting
|
||||||
|
the including page.
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
`/some/page`:
|
||||||
|
```
|
||||||
|
<slot X value/>
|
||||||
|
```
|
||||||
|
|
||||||
|
Normal case: render included page but in view of current namespace:
|
||||||
|
```
|
||||||
|
<slot X original/><include src=/some/page> [ <content/> ] </include>
|
||||||
|
```
|
||||||
|
Will render to: `value [ ]`
|
||||||
|
|
||||||
|
|
||||||
|
Completely isolated:
|
||||||
|
```
|
||||||
|
<slot X original/><include src=/some/page isolated> [ <content/> ] </include>
|
||||||
|
```
|
||||||
|
Will render to: `original [ value ]`
|
||||||
|
|
||||||
|
|
||||||
|
Isolated up, i.e. the included page sees local state but can not affect it:
|
||||||
|
```
|
||||||
|
<slot X original/><include src=/some/page isolated=partial> [ <content/> ] </include>
|
||||||
|
```
|
||||||
|
Will render to: `original [ ]`
|
||||||
|
|
||||||
|
|
||||||
|
### source (src)
|
||||||
|
|
||||||
|
```
|
||||||
|
@source(<src>)
|
||||||
|
|
||||||
|
<source <src>>
|
||||||
|
...
|
||||||
|
<content/>
|
||||||
|
...
|
||||||
|
</source>
|
||||||
|
```
|
||||||
|
|
||||||
|
Load page source into the current page.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### source (src) / quote (src)
|
### quote (src)
|
||||||
|
|
||||||
Insert a page without rendering. This is similar to include but will not
|
Insert a page without rendering. This is similar to include but will not
|
||||||
render the page.
|
render the page.
|
||||||
|
|||||||
@ -443,7 +443,7 @@ module.BaseParser = {
|
|||||||
//
|
//
|
||||||
// NOTE: this internaly uses .macros to check for propper nesting
|
// NOTE: this internaly uses .macros to check for propper nesting
|
||||||
//group: function*(page, lex, to=false){
|
//group: function*(page, lex, to=false){
|
||||||
group: function*(lex, to=false, parent, context){
|
group: function*(lex, to=false, context){
|
||||||
lex = typeof(lex) != 'object' ?
|
lex = typeof(lex) != 'object' ?
|
||||||
this.lex(lex)
|
this.lex(lex)
|
||||||
: lex
|
: lex
|
||||||
@ -511,7 +511,6 @@ module.BaseParser = {
|
|||||||
value.args.body
|
value.args.body
|
||||||
?? value.args.text,
|
?? value.args.text,
|
||||||
false,
|
false,
|
||||||
parent,
|
|
||||||
value.name)] }
|
value.name)] }
|
||||||
delete value.args.body
|
delete value.args.body
|
||||||
delete value.args.text
|
delete value.args.text
|
||||||
@ -1446,7 +1445,7 @@ module.parser = {
|
|||||||
return ((st ?? state).slots ?? {})[name] },
|
return ((st ?? state).slots ?? {})[name] },
|
||||||
{slot: name}) }) })),
|
{slot: name}) }) })),
|
||||||
// XXX do not like this name...
|
// XXX do not like this name...
|
||||||
content: ['slot', 'include'],
|
content: ['slot', 'include', 'quote'],
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -1521,18 +1520,14 @@ module.parser = {
|
|||||||
this.parseNested(page, src, state),
|
this.parseNested(page, src, state),
|
||||||
function(src){
|
function(src){
|
||||||
// check for recursion...
|
// check for recursion...
|
||||||
// XXX do we do this for pattern paths???
|
// XXX will this work for @source(..)???
|
||||||
// XXX this does not catch the /A/A/A/... recursion...
|
|
||||||
var stack = state.include_stack ??= []
|
var stack = state.include_stack ??= []
|
||||||
// XXX is this the right separator???
|
if(stack.includes(src)){
|
||||||
// ...need something that can't be in a path...
|
|
||||||
var base_src = base +'|'+ src
|
|
||||||
if(stack.includes(base_src)){
|
|
||||||
if(recursive){
|
if(recursive){
|
||||||
return that.expand(page, recursive, state) }
|
return that.expand(page, recursive, state) }
|
||||||
throw new Error('Recursion:\n\t'+
|
throw new Error('Recursion:\n\t'+
|
||||||
[...stack, base_src].join('\n\t\t-> ')) }
|
[...stack, src].join('\n\t\t-> ')) }
|
||||||
stack.push(base_src)
|
stack.push(src)
|
||||||
|
|
||||||
var cache = state.cache ??= {}
|
var cache = state.cache ??= {}
|
||||||
if(cache[src]){
|
if(cache[src]){
|
||||||
@ -1550,19 +1545,18 @@ module.parser = {
|
|||||||
|
|
||||||
// content handler...
|
// content handler...
|
||||||
handler ??=
|
handler ??=
|
||||||
function(page, text, state){
|
function(page, src, text, state){
|
||||||
// XXX should this be the difference between
|
page = page.get(src)
|
||||||
// @include(..) and @source(..)???
|
|
||||||
// i.e.
|
|
||||||
// .include(..) -> .resolve(..)
|
|
||||||
// .source(..) -> .expand(..)
|
|
||||||
return args.isolated ?
|
return args.isolated ?
|
||||||
this.resolve(page, text,
|
this.resolve(
|
||||||
args.isolated == 'partial' ?
|
page,
|
||||||
// XXX
|
text,
|
||||||
serialize.partialDeepCopy(state)
|
Object.assign(
|
||||||
: {})
|
args.isolated == 'partial' ?
|
||||||
: this.expand(page, text, state)}
|
serialize.partialDeepCopy(state)
|
||||||
|
: {},
|
||||||
|
{stack: state.stack ?? []}))
|
||||||
|
: this.expand(page, text, state) }
|
||||||
|
|
||||||
var pageHandler =
|
var pageHandler =
|
||||||
function(text){
|
function(text){
|
||||||
@ -1573,18 +1567,18 @@ module.parser = {
|
|||||||
that.expand(page, body, state,
|
that.expand(page, body, state,
|
||||||
{ content: function(){
|
{ content: function(){
|
||||||
content_handled = true
|
content_handled = true
|
||||||
return text = handler.call(that, page, text, state) } }),
|
return text = handler.call(that, page, src, text, state) } }),
|
||||||
function(text){
|
function(text){
|
||||||
// if no <content/> present we still
|
// if no <content/> present we still
|
||||||
// need to handle the included page...
|
// need to handle the included page...
|
||||||
content_handled
|
content_handled
|
||||||
|| handler.call(that, page, text, state)
|
|| handler.call(that, page, src, text, state)
|
||||||
return text })
|
return text })
|
||||||
// place as-is...
|
// place as-is...
|
||||||
: handler.call(that, page, text, state) }
|
: handler.call(that, page, src, text, state) }
|
||||||
var resultHandler =
|
var resultHandler =
|
||||||
function(pages){
|
function(pages){
|
||||||
state.include_stack.at(-1) == base_src
|
state.include_stack.at(-1) == src
|
||||||
&& state.include_stack.pop()
|
&& state.include_stack.pop()
|
||||||
// cleanup...
|
// cleanup...
|
||||||
if(state.include_stack.length == 0){
|
if(state.include_stack.length == 0){
|
||||||
@ -1629,25 +1623,51 @@ module.parser = {
|
|||||||
var that = this
|
var that = this
|
||||||
return this.macros['include'].call(this,
|
return this.macros['include'].call(this,
|
||||||
page, args, body, state,
|
page, args, body, state,
|
||||||
function(page, text, state){
|
function(page, src, text, state){
|
||||||
return that.expand(page, text, state) }) }),
|
return that.expand(page, text, state) }) }),
|
||||||
|
|
||||||
// Load macro and slot definitions but ignore the page text...
|
// Load macro and slot definitions but ignore the page text...
|
||||||
//
|
//
|
||||||
// NOTE: this is essentially the same as @source(..) but returns ''.
|
// NOTE: this is essentially the same as @source(..) but returns ''.
|
||||||
// XXX revise name...
|
|
||||||
load: Macro(
|
load: Macro(
|
||||||
['src'],
|
['src'],
|
||||||
function(args, body, state){
|
function(page, args, body, state){
|
||||||
var that = this
|
var that = this
|
||||||
return Promise.awaitOrRun(
|
return Promise.awaitOrRun(
|
||||||
this.macros['include'].call(this, page, args, body, state),
|
this.macros['include'].call(this, page, args, body, state),
|
||||||
function(){
|
function(){
|
||||||
return '' }) }),
|
return '' }) }),
|
||||||
|
|
||||||
// XXX a naive version, see ._quote(..) for reference...
|
//
|
||||||
|
// @quote(<src>)
|
||||||
|
//
|
||||||
|
// <quote src=<src>[ filter="<filter> ..."]/>
|
||||||
|
//
|
||||||
|
// <quote text=" .. "[ filter="<filter> ..."]/>
|
||||||
|
//
|
||||||
|
// <quote[ filter="<filter> ..."]>
|
||||||
|
// ..
|
||||||
|
// </quote>
|
||||||
|
//
|
||||||
|
// This has two modes of operation, i.e. the body can be treated
|
||||||
|
// in two destinct ways:
|
||||||
|
// - if both src and body are given -- like @include(..) body is
|
||||||
|
// used as a wrapper for the quoted page
|
||||||
|
// - if only body is given -- body is inderted as is without
|
||||||
|
// any processing.
|
||||||
|
// XXX not sure I like this, this might change in the futore...
|
||||||
|
// one way to split the two is to split this into two macros,
|
||||||
|
// but I can't make the split logical/obvious...
|
||||||
|
//
|
||||||
|
// NOTE: src ant text arguments are mutually exclusive, src takes
|
||||||
|
// priority.
|
||||||
|
// NOTE: the filter argument has the same semantics as the filter
|
||||||
|
// macro with one exception, when used in quote, the body is
|
||||||
|
// not expanded...
|
||||||
|
// NOTE: the filter argument uses the same filters as @filter(..)
|
||||||
// XXX might be a good idea to do an auto-filter that would be
|
// XXX might be a good idea to do an auto-filter that would be
|
||||||
// apropriately selected according to format -- md, html, ...
|
// apropriately selected according to format -- md, html, ...
|
||||||
|
// XXX filter...
|
||||||
quote: Macro(
|
quote: Macro(
|
||||||
['src', 'join', 'filter'],
|
['src', 'join', 'filter'],
|
||||||
quoting(
|
quoting(
|
||||||
@ -1666,6 +1686,20 @@ module.parser = {
|
|||||||
return Promise.awaitOrRun(
|
return Promise.awaitOrRun(
|
||||||
text,
|
text,
|
||||||
function(text){
|
function(text){
|
||||||
|
// XXX not sure I like that this has two "modes"...
|
||||||
|
text = src && body ?
|
||||||
|
// XXX do we need to account for generators???
|
||||||
|
(text instanceof Array ?
|
||||||
|
text
|
||||||
|
: [text])
|
||||||
|
.map(function(text){
|
||||||
|
return that.expand(
|
||||||
|
page,
|
||||||
|
that.ast(body.join(''), false, 'quote'),
|
||||||
|
state,
|
||||||
|
{ content: function(){
|
||||||
|
return text }, }) })
|
||||||
|
: text
|
||||||
return that.joinBlocks(
|
return that.joinBlocks(
|
||||||
page,
|
page,
|
||||||
that.filterBlocks(
|
that.filterBlocks(
|
||||||
@ -1676,133 +1710,7 @@ module.parser = {
|
|||||||
args.join,
|
args.join,
|
||||||
state) }) }) })),
|
state) }) }) })),
|
||||||
|
|
||||||
//
|
/* XXX
|
||||||
// @quote(<src>)
|
|
||||||
//
|
|
||||||
// <quote src=<src>[ filter="<filter> ..."]/>
|
|
||||||
//
|
|
||||||
// <quote text=" .. "[ filter="<filter> ..."]/>
|
|
||||||
//
|
|
||||||
// <quote[ filter="<filter> ..."]>
|
|
||||||
// ..
|
|
||||||
// </quote>
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// NOTE: src ant text arguments are mutually exclusive, src takes
|
|
||||||
// priority.
|
|
||||||
// NOTE: the filter argument has the same semantics as the filter
|
|
||||||
// macro with one exception, when used in quote, the body is
|
|
||||||
// not expanded...
|
|
||||||
// NOTE: the filter argument uses the same filters as @filter(..)
|
|
||||||
// NOTE: else argument implies strict mode...
|
|
||||||
// XXX need a way to escape macros -- i.e. include </quote> in a quoted text...
|
|
||||||
// XXX should join/else be sub-tags???
|
|
||||||
// XXX UPDATE...
|
|
||||||
_quote: Macro(
|
|
||||||
['src', 'filter', 'text', 'join', 'else',
|
|
||||||
['s', 'expandactions', 'strict']],
|
|
||||||
async function*(args, body, state){
|
|
||||||
var src = args.src //|| args[0]
|
|
||||||
var base = this.get(this.path.split(/\*/).shift())
|
|
||||||
var text = args.text
|
|
||||||
?? body
|
|
||||||
?? []
|
|
||||||
var strict = !!(args.strict
|
|
||||||
?? args['else']
|
|
||||||
?? false)
|
|
||||||
// parse arg values...
|
|
||||||
src = src ?
|
|
||||||
await base.parse(src, state)
|
|
||||||
: src
|
|
||||||
// XXX INHERIT_ARGS special-case: inherit args by default...
|
|
||||||
if(this.actions_inherit_args
|
|
||||||
&& this.actions_inherit_args.has(pwpath.basename(src))
|
|
||||||
&& this.get(pwpath.dirname(src)).path == this.path){
|
|
||||||
src += ':$ARGS' }
|
|
||||||
var expandactions =
|
|
||||||
args.expandactions
|
|
||||||
?? true
|
|
||||||
// XXX EXPERIMENTAL
|
|
||||||
var strquotes = args.s
|
|
||||||
|
|
||||||
var depends = state.depends =
|
|
||||||
state.depends
|
|
||||||
?? new Set()
|
|
||||||
// XXX DEPENDS_PATTERN
|
|
||||||
depends.add(src)
|
|
||||||
|
|
||||||
var pages = src ?
|
|
||||||
(!expandactions
|
|
||||||
&& await this.get(src).type == 'action' ?
|
|
||||||
base.get(this.QUOTE_ACTION_PAGE)
|
|
||||||
: await this.get(src).asPages(strict))
|
|
||||||
: text instanceof Array ?
|
|
||||||
[text.join('')]
|
|
||||||
: typeof(text) == 'string' ?
|
|
||||||
[text]
|
|
||||||
: text
|
|
||||||
// else...
|
|
||||||
pages = ((!pages
|
|
||||||
|| pages.length == 0)
|
|
||||||
&& args['else']) ?
|
|
||||||
[await base.parse(args['else'], state)]
|
|
||||||
: pages
|
|
||||||
// empty...
|
|
||||||
if(!pages || pages.length == 0){
|
|
||||||
return }
|
|
||||||
|
|
||||||
var join = args.join
|
|
||||||
&& await base.parse(args.join, state)
|
|
||||||
var first = true
|
|
||||||
for await (var page of pages){
|
|
||||||
if(join && !first){
|
|
||||||
yield join }
|
|
||||||
first = false
|
|
||||||
|
|
||||||
text = typeof(page) == 'string' ?
|
|
||||||
page
|
|
||||||
: (!expandactions
|
|
||||||
&& await page.type == 'action') ?
|
|
||||||
base.get(this.QUOTE_ACTION_PAGE).raw
|
|
||||||
: await page.raw
|
|
||||||
text = strquotes ?
|
|
||||||
text
|
|
||||||
.replace(/["']/g, function(c){
|
|
||||||
return '%'+ c.charCodeAt().toString(16) })
|
|
||||||
: text
|
|
||||||
|
|
||||||
page.path
|
|
||||||
&& depends.add(page.path)
|
|
||||||
|
|
||||||
var filters =
|
|
||||||
args.filter
|
|
||||||
&& args.filter
|
|
||||||
.trim()
|
|
||||||
.split(/\s+/g)
|
|
||||||
|
|
||||||
// NOTE: we are delaying .quote_filters handling here to
|
|
||||||
// make their semantics the same as general filters...
|
|
||||||
// ...and since we are internally calling .filter(..)
|
|
||||||
// macro we need to dance around it's architecture too...
|
|
||||||
// NOTE: since the body of quote(..) only has filters applied
|
|
||||||
// to it doing the first stage of .filter(..) as late
|
|
||||||
// as the second stage here will have no ill effect...
|
|
||||||
// NOTE: this uses the same filters as @filter(..)
|
|
||||||
// NOTE: the function wrapper here isolates text in
|
|
||||||
// a closure per function...
|
|
||||||
yield (function(text){
|
|
||||||
return async function(state){
|
|
||||||
// add global quote-filters...
|
|
||||||
filters =
|
|
||||||
(state.quote_filters
|
|
||||||
&& !(filters ?? []).includes(this.ISOLATED_FILTERS)) ?
|
|
||||||
[...state.quote_filters, ...(filters ?? [])]
|
|
||||||
: filters
|
|
||||||
return filters ?
|
|
||||||
await this.__parser__.callMacro(
|
|
||||||
this, 'filter', filters, text, state, false)
|
|
||||||
.call(this, state)
|
|
||||||
: text } })(text) } }),
|
|
||||||
// very similar to @filter(..) but will affect @quote(..) filters...
|
// very similar to @filter(..) but will affect @quote(..) filters...
|
||||||
'quote-filter': function(args, body, state){
|
'quote-filter': function(args, body, state){
|
||||||
var filters = state.quote_filters =
|
var filters = state.quote_filters =
|
||||||
|
|||||||
@ -27,6 +27,19 @@ module.exports.PAGES = {
|
|||||||
'/async/recursive/SelfOther': Promise.resolve('<< @include(/async/recursive/OtherSelf) >>'),
|
'/async/recursive/SelfOther': Promise.resolve('<< @include(/async/recursive/OtherSelf) >>'),
|
||||||
|
|
||||||
'/multi/page': [ 'A', 'B', 'C' ],
|
'/multi/page': [ 'A', 'B', 'C' ],
|
||||||
|
'/async/multi/page': Promise.resolve([ 'A', 'B', 'C' ]),
|
||||||
|
// XXX is this actually possible ???
|
||||||
|
'/multi/async/page': [
|
||||||
|
Promise.resolve('A'),
|
||||||
|
Promise.resolve('B'),
|
||||||
|
Promise.resolve('C'),
|
||||||
|
],
|
||||||
|
// XXX is this actually possible ???
|
||||||
|
'/multi/async/multi/page': Promise.resolve([
|
||||||
|
Promise.resolve('A'),
|
||||||
|
Promise.resolve('B'),
|
||||||
|
Promise.resolve('C'),
|
||||||
|
]),
|
||||||
}
|
}
|
||||||
|
|
||||||
var P =
|
var P =
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user