cleanup and tweaking...

Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2022-08-17 23:38:24 +03:00
parent 95f803ce00
commit e3246c7fb2
5 changed files with 144 additions and 117 deletions

View File

@ -505,7 +505,7 @@ object.Constructor('Page', BasePage, {
QUOTING_MACROS: ['quote'], QUOTING_MACROS: ['quote'],
// templates used to render a page via .text // templates used to render a page via .text
PAGE_TEMPLATE: '_text', PAGE_TEMPLATE: '_view',
// NOTE: comment this out to make the system fail when nothing is // NOTE: comment this out to make the system fail when nothing is
// resolved, not even the System/NotFound page... // resolved, not even the System/NotFound page...
@ -658,6 +658,7 @@ object.Constructor('Page', BasePage, {
// the context, this potentially can lead to false positives. // the context, this potentially can lead to false positives.
// //
// XXX should this be lazy??? // XXX should this be lazy???
// XXX should we use .__parser__.expand(..) instead of .parse(..) ???
include: Macro( include: Macro(
['src', 'recursive', 'join', ['isolated']], ['src', 'recursive', 'join', ['isolated']],
async function(args, body, state, key='included', handler){ async function(args, body, state, key='included', handler){
@ -758,6 +759,7 @@ object.Constructor('Page', BasePage, {
// XXX need to handle pattern paths (like include: join=...) // XXX need to handle pattern paths (like include: join=...)
// XXX need a way to escape macros -- i.e. include </quote> in a quoted text... // XXX need a way to escape macros -- i.e. include </quote> in a quoted text...
quote: Macro( quote: Macro(
//['src', 'filter', 'text', 'join'],
['src', 'filter', 'text'], ['src', 'filter', 'text'],
async function(args, body, state){ async function(args, body, state){
var src = args.src //|| args[0] var src = args.src //|| args[0]
@ -866,12 +868,18 @@ object.Constructor('Page', BasePage, {
// show first instance... // show first instance...
: name in slots) : name in slots)
slots[name] = [...await this.__parser__.expand(this, text, state)] // NOTE: we prioritize the nested slots over the current...
delete slots[name]
var slot = await this.__parser__.expand(this, text, state)
slots[name] =
slots[name]
?? slot
return hidden ? return hidden ?
'' ''
: function(state){ : function(state){
return state.slots[name] } }), return state.slots[name] } }),
'slot-content': ['slot'],
// //
// <macro src=<url>> .. </macro> // <macro src=<url>> .. </macro>
@ -969,7 +977,7 @@ object.Constructor('Page', BasePage, {
&& (text || args['else'])){ && (text || args['else'])){
var else_block = _getBlock('else') var else_block = _getBlock('else')
return else_block ? return else_block ?
[...await this.__parser__.expand(this, else_block, state)] await this.__parser__.expand(this, else_block, state)
: undefined } : undefined }
// sort pages... // sort pages...
@ -980,15 +988,13 @@ object.Constructor('Page', BasePage, {
var join_block = _getBlock('join') var join_block = _getBlock('join')
// apply macro text... // apply macro text...
var res = await pages var res = pages
.map(function(page, i){ .map(function(page){
return that.__parser__.expand(page, text, state) }) return that.__parser__.expand(page, text, state) })
return join_block ? return join_block ?
res.between(await that.__parser__.expand( res.between(
// render join block relative to the path before the first '*'... // render join block relative to the path before the first '*'...
that.get(that.path.split(/\*/).shift()), await that.__parser__.expand(base, join_block, state))
join_block,
state))
: res } }), : res } }),
// nesting rules... // nesting rules...
@ -1200,10 +1206,10 @@ module.System = {
// These are used to control how a page is rendered. // These are used to control how a page is rendered.
// //
// pWiki has to have a template appended to any path, if one is not // pWiki has to have a template appended to any path, if one is not
// given then "_text" is used internally. // given then "_view" is used internally.
// //
// A template is rendered in the context of the parent page, e.g. // A template is rendered in the context of the parent page, e.g.
// for /path/to/page, the actual rendered template is /path/to/page/_text // for /path/to/page, the actual rendered template is /path/to/page/_view
// and it is rendered from /path/to/page. // and it is rendered from /path/to/page.
// //
// A template is any page named starting with an underscore ("_") // A template is any page named starting with an underscore ("_")
@ -1217,10 +1223,10 @@ module.System = {
// //
// XXX all of these should support pattern pages... // XXX all of these should support pattern pages...
_text: { _text: {
//text: '@include(. isolated join="@source(file-separator)")' }, text: '@include(. isolated join="@source(file-separator)")' },
// XXX problem: the show slot _view: {
text: object.doc` text: object.doc`
<slot name="header">@source(./path)/_edit</slot> <slot name="header">/list @source(./path)/_edit</slot>
<hr> <hr>
<slot name="content"></slot> <slot name="content"></slot>
<hr> <hr>
@ -1239,7 +1245,22 @@ module.System = {
'<macro src="." join="@source(file-separator)">' '<macro src="." join="@source(file-separator)">'
+'<pre wikiwords="no"><quote filter="quote-tags" src="."/></pre>' +'<pre wikiwords="no"><quote filter="quote-tags" src="."/></pre>'
+'</macro>'}, +'</macro>'},
// XXX can we reuse _text here??? /* XXX can we reuse _view here???
_edit: {
text:
'@include(PageTemplate)'
+'<slot name="header">@source(./path)</slot>'
+'<slot name="content">'
+'<macro src="." join="@source(file-separator)">'
+'<pre class="editor" '
+'wikiwords="no" '
+'contenteditable '
+'oninput="saveContent(\'@source(./path)\', this.innerText)">'
+'<quote filter="quote-tags" src="."/>'
+'</pre>'
+'</macro>'
+'</slot>'},
/*/
_edit: { _edit: {
text: text:
'@source(./path)' '@source(./path)'
@ -1252,13 +1273,30 @@ module.System = {
+'<quote filter="quote-tags" src="."/>' +'<quote filter="quote-tags" src="."/>'
+'</pre>' +'</pre>'
+'</macro>'}, +'</macro>'},
//*/
edit: {
text:
//'@include(PageTemplate)'
'@include(_view)'
+'<slot name="header">@source(../path)</slot>'
+'<slot name="content">'
// XXX for some reason this is not called...
+'<macro src=".." join="@source(file-separator)">'
+'<pre class="editor" '
+'wikiwords="no" '
+'contenteditable '
+'oninput="saveContent(\'@source(./path)\', this.innerText)">'
+'<quote filter="quote-tags" src="."/>'
+'</pre>'
+'</macro>'
+'</slot>'},
// XXX this does not yet work... // XXX this does not yet work...
// XXX "_test" breaks differently than "test" // XXX "_test" breaks differently than "test"
//_test: { //_test: {
test: { test: {
text: object.doc` text: object.doc`
@source(_text) @source(_view)
<slot name="header">HEADER</slot> <slot name="header">HEADER</slot>
<slot name="content">CONTENT</slot> <slot name="content">CONTENT</slot>
<slot name="footer">FOOTER</slot> `}, <slot name="footer">FOOTER</slot> `},
@ -1269,68 +1307,41 @@ module.System = {
list: { list: {
text: `<macro src="../*/path" join="@source(line-separator)">@source(.)</macro>` }, text: object.doc`
<slot name="header">
/list
<a href="#@source(../../path)/list">&#x21D1;</a>
@source(../path)
</slot>
<macro src="../*" join="@source(line-separator)">
<a href="#@source(./path)/list">&#x21B3;</a>
<a href="#@source(./path)">@source(./name)</a>
<a href="#@source(./path)/delete">&times;</a>
</macro>` },
// XXX this is really slow... // XXX this is really slow...
// XXX for some reason replacing both @quote(..) with @source(..) in
// the links will break macro parsing...
// XXX should this be all or tree???
tree: { tree: {
text: object.doc` text: object.doc`
<macro src="../*"> <macro src="../*">
<div> <div>
<a href="#@quote(./path)">@source(./name)</a> <a href="#@source(./path)">@source(./name)</a>
<a href="#@quote(./path)/delete">&times;</a> <a href="#@source(./path)/delete">&times;</a>
<div style="padding-left: 30px"> <div style="padding-left: 30px">
@source(./tree) @source(./tree)
</div> </div>
</div> </div>
</macro>` }, </macro>` },
all: {
// XXX this is somewhat broken... text: `@include(../**/path join="<br>")`},
info: { info: {
text: object.doc` text: object.doc`
# @source(./path) Path: @source(../path)<br>
Resolved path: @source(../resolved)<br>
- Render root: @source(./renderer) Referrer: @source(../referrer)<br>
- Render root: @source(./renderer) Renderer: @source(../renderer)<br>
ctime: @source(../ctime)<br>
` }, mtime: @source(../mtime)<br>
<hr>
// XXX tests... <pre wikiwords="no"><quote filter="quote-tags" src=".."/></pre> ` },
//
test_page: function(){
console.log('--- RENDERER:', this.render_root)
console.log('--- PATH: ', this.path)
console.log('--- REFERRER:', this.referrer)
console.log('--- PAGE:', this)
return this.path },
test_list: function(){
return 'abcdef'.split('') },
// XXX problem: it appears that we can't fill a slot from within a slot...
// ...the "content" slot below does not override the content slot in _text
test_base_slots: {
text: object.doc`OUTER
<slot name="header">HEADER</slot>
<slot name="content">CONTENT</slot>
<slot name="footer">FOOTER</SLOT> `},
// XXX does not work yet...
test_slots: {
text: object.doc`
Sequential:
<slot name="sequential">unfilled</slot>
<slot name="sequential">filled</slot>
<slot name="sequential">refilled</slot>
<br><br>
Nested:
<slot name="nested">
unfilled
<slot name="nested">
filled
<slot name="nested">
refilled
</slot>
</slot>
</slot> ` },
// page parts... // page parts...
// //
@ -1348,15 +1359,24 @@ module.System = {
DeletingPage: { DeletingPage: {
text: 'Deleting: @source(../path)' }, text: 'Deleting: @source(../path)' },
PageTemplate: {
text: object.doc`
<slot name="header">@source(./path)/_edit</slot>
<hr>
<slot name="content"></slot>
<hr>
<slot name="footer"></slot> ` },
// page actions... // page actions...
// //
// metadata... // metadata...
// //
renderer: function(){ renderer: function(){
return (this.render_root || {}).path }, return (this.render_root || {}).path },
referrer: function(){
return this.referrer || this.path },
path: function(){ path: function(){
return this.get('..').path }, return this.get('..').path },
location: function(){ location: function(){
@ -1366,9 +1386,9 @@ module.System = {
name: function(){ name: function(){
return this.get('..').name }, return this.get('..').name },
ctime: function(){ ctime: function(){
return this.get('..').data.ctime }, return this.get('..').data.ctime ?? '' },
mtime: function(){ mtime: function(){
return this.get('..').data.mtime }, return this.get('..').data.mtime ?? '' },
// XXX this can be a list for pattern paths... // XXX this can be a list for pattern paths...
resolved: function(){ resolved: function(){
@ -1412,6 +1432,11 @@ module.System = {
// XXX System/forward // XXX System/forward
// XXX System/sort // XXX System/sort
// XXX System/reverse // XXX System/reverse
// XXX broken...
test_list: function(){
return 'abcdef'.split('') },
} }
var Settings = var Settings =

View File

@ -387,18 +387,13 @@ module.BaseParser = {
// Expand macros... // Expand macros...
// //
// <ast> ::= [ <item>, .. ]
// <item> ::= // <item> ::=
// <string> // <func>
// // returned by .macros.filter(..) // | <promise>
// | { // | <string>
// // XXX is this still relevant... // | { data: <ast> }
// filters: [ // | <ast>
// '<filter>'
// | '-<filter>',
// ...
// ],
// data: [ <item>, .. ],
// }
// //
// XXX macros: we are mixing up ast state and parse state... // XXX macros: we are mixing up ast state and parse state...
// one should only be used for parsing and be forgotten after // one should only be used for parsing and be forgotten after
@ -440,13 +435,37 @@ module.BaseParser = {
} else { } else {
yield res } } }, yield res } } },
// recursively resolve and enumerate the ast...
//
// <ast> ::= [ <item>, .. ]
// <item> ::=
// <string>
// | { data: <ast> }
//
// XXX should this also resolve e.data???
resolve: async function*(page, ast, state={}){
// NOTE: we need to await for ast here as we need stage 2 of
// parsing to happen AFTER everything else completes...
for(var e of await ast){
e = typeof(e) == 'function' ?
e.call(page, state)
: e
if(e instanceof Array){
yield* this.resolve(page, e, state)
} else if(e instanceof Object && 'data' in e){
yield { data: await this.resolve(page, e.data, state) }
} else {
yield e } } },
// Fully parse a page... // Fully parse a page...
// //
// This runs in two stages: // This runs in two stages:
// - expand the page // - resolve the page
// - lex the page -- .lex(..) // - lex the page -- .lex(..)
// - group block elements -- .group(..) // - group block elements -- .group(..)
// - expand macros -- .expand(..) // - expand macros -- .expand(..)
// - resolve ast -- .resolve(..)
// - apply filters // - apply filters
// //
// NOTE: this has to synchronize everything between stage 1 (up to // NOTE: this has to synchronize everything between stage 1 (up to
@ -458,7 +477,6 @@ module.BaseParser = {
// them on demand rather than on encounter (as is now), e.g. // them on demand rather than on encounter (as is now), e.g.
// a slot when loaded will replace the prior occurrences... // a slot when loaded will replace the prior occurrences...
// //
// XXX this should be recursive....
// XXX add a special filter to clear pending filters... (???) // XXX add a special filter to clear pending filters... (???)
parse: async function(page, ast, state={}){ parse: async function(page, ast, state={}){
var that = this var that = this
@ -468,17 +486,7 @@ module.BaseParser = {
this.expand(page, ast, state) this.expand(page, ast, state)
: ast : ast
// NOTE: we need to await for ast here as we need stage 2 of return await this.resolve(page, ast, state)
// parsing to happen AFTER everything else completes...
return await Promise.iter((await ast)
.flat()
// post handlers...
.map(function(section){
return typeof(section) == 'function' ?
// NOTE: this can produce promises...
section.call(page, state)
: section }))
.flat()
// filters... // filters...
.map(function(section){ .map(function(section){
return ( return (

View File

@ -165,7 +165,7 @@ module.BaseStore = {
.replace(/^\/|\/$/g, '') .replace(/^\/|\/$/g, '')
.replace(/\//g, '\\/') .replace(/\//g, '\\/')
.replace(/\*\*/g, '.*') .replace(/\*\*/g, '.*')
.replace(/\*/g, '[^\\/]*') .replace(/(?<=^|[\\\/]+|[^.])\*/g, '[^\\/]*')
}(?=[\\\\\/]|$)`) }(?=[\\\\\/]|$)`)
return [...(await this.paths()) return [...(await this.paths())
// NOTE: we are not using .filter(..) here as wee // NOTE: we are not using .filter(..) here as wee

View File

@ -179,11 +179,9 @@ pwiki.pwiki
text: object.doc` text: object.doc`
Sequential: Sequential:
<slot name="sequential">unfilled</slot> <slot name="sequential">unfilled</slot>
<slot name="sequential">filled</slot> <slot name="sequential">filled</slot>
<slot name="sequential">refilled</slot> <slot name="sequential">refilled</slot>
<br><br>
Nested: Nested:
<slot name="nested"> <slot name="nested">
unfilled unfilled
@ -194,6 +192,18 @@ pwiki.pwiki
</slot> </slot>
</slot> </slot>
</slot> `, }) </slot> `, })
.update({
location: '/test/nesting',
text: object.doc`
Quote in macro:
<macro src=".">
<quote src="./path"/>
</macro>
<hr>
Quote in slot:
<slot name="moo">
<quote src="./path"/>
</slot>` })
.update({ .update({
location: '/test/a', location: '/test/a',
text: 'a', text: 'a',

View File

@ -1,33 +1,17 @@
/********************************************************************** /**********************************************************************
* *
* *
* XXX nested slots do not seem to work... * XXX might be a good idea for slots to support getting previous slot
* XXX BUG: /** /paths -- does not work... * content, e.g.:
* <slot name="header"><slot-content/> new text</slot>
* XXX BUG?: markdown: when parsing chunks each chunk gets an open/closed * XXX BUG?: markdown: when parsing chunks each chunk gets an open/closed
* <p> inserted at start/end -- this breaks stuff returned by macros... * <p> inserted at start/end -- this breaks stuff returned by macros...
* ...there are two ways to dance around this: * ...there are two ways to dance around this:
* - make filters run a bit more globaly -- per block... * - make filters run a bit more globaly -- per block...
* - find a local parser... * - find a local parser...
* XXX BUG?: empty stores do not appear to show up in listings... * XXX add something like /stores to list store info...
* ...this is expected but not intuitive...
* might be a good idea to add a special path like /Stores to list
* sub-stores...
* XXX OPTIMIZE: /tree is really slow... * XXX OPTIMIZE: /tree is really slow...
* ...is the problem purely in the async/await playing ping-pong??? * ...is the problem purely in the async/await playing ping-pong???
* XXX might be a good idea to make things optionally sync via a .sync()
* method, or request a specific set of data (in the parser, after
* collecting all the stuff needed and fetching it in one go)...
* XXX might be a good idea to add page caching (state.page_cache) relative
* to a path on parsing, to avoid re-matching the same page over and
* over again from the same context
* format:
* {
* <basedir>: {
* <path>: <data>,
* ...
* },
* ...
* }
* XXX BUG: FF: conflict between object.run and PouchDB... * XXX BUG: FF: conflict between object.run and PouchDB...
* XXX BUG: browser: .get('/*').raw hangs in the browser context... * XXX BUG: browser: .get('/*').raw hangs in the browser context...
* XXX BUG?: /_tree for some reason does not show anything on lower levels... * XXX BUG?: /_tree for some reason does not show anything on lower levels...