/
kopython
/
_hyperscript-node
Обзор
Документация
Войти
/
kopython
/
_hyperscript-node
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
lib/_hyperscript/hdb.js
498 строк
14 KB
Drew McDonald
* real init commit
29 ноя 2022, 02:51
29 ноя 2022, 02:51
43e7026
Код
Авторство
О чём код?
const jsdom = require('jsdom'); const { JSDOM } = jsdom; const dom = new JSDOM(`<!DOCTYPE html><p>Hello world</p>`); global.window = dom.window; const { EventTarget, Event } = global.window; (function (self, factory) { const plugin = factory(self) if (typeof exports === 'object' && typeof exports['nodeName'] !== 'string') { module.exports = plugin } else { if ('_hyperscript' in self) self._hyperscript.use(plugin) } })(typeof self !== 'undefined' ? self : this, self => { return _hyperscript => { function HDB(ctx, runtime, breakpoint) { this.ctx = ctx; this.runtime = runtime; this.cmd = breakpoint; this._hyperscript = _hyperscript; this.cmdMap = []; this.bus = new EventTarget(); } // See below for methods _hyperscript.addCommand("breakpoint", function (parser, runtime, tokens) { if (!tokens.matchToken("breakpoint")) return; var hdb; return { op: function (ctx) { global.hdb = hdb = new HDB(ctx, runtime, this); try { return hdb.break(ctx); } catch (e) { console.error(e, e.stack); } }, }; }); HDB.prototype.break = function (ctx) { console.log("=== HDB///_hyperscript/debugger ==="); console.log(ctx); // this.ui(); return new Promise((resolve, reject) => { this.bus.addEventListener( "continue", () => { if (this.ctx !== ctx) { // Context switch for (var attr in ctx) { delete ctx[attr]; } Object.assign(ctx, this.ctx); } delete window['hdb']; resolve(this.runtime.findNext(this.cmd, this.ctx)); }, { once: true } ); }); }; HDB.prototype.continueExec = function () { this.bus.dispatchEvent(new Event("continue")); }; HDB.prototype.stepOver = function () { if (!this.cmd) return this.continueExec(); var result = this.cmd && this.cmd.type === "breakpointCommand" ? this.runtime.findNext(this.cmd, this.ctx) : this.runtime.unifiedEval(this.cmd, this.ctx); if (result.type === "implicitReturn") return this.stepOut(); if (result && result.then instanceof Function) { return result.then(next => { this.cmd = next; this.bus.dispatchEvent(new Event("step")); this.logCommand(); }); } else if (result.halt_flag) { this.bus.dispatchEvent(new Event("continue")); } else { this.cmd = result; this.bus.dispatchEvent(new Event("step")); this.logCommand(); } }; HDB.prototype.stepOut = function () { if (!this.ctx.meta.caller) return this.continueExec(); var callingCmd = this.ctx.meta.callingCommand; var oldMe = this.ctx.me; this.ctx = this.ctx.meta.caller; console.log( "[hdb] stepping out into " + this.ctx.meta.feature.displayName) if (this.ctx.me instanceof Element && this.ctx.me !== oldMe) { console.log("[hdb] me: ", this.ctx.me) } this.cmd = this.runtime.findNext(callingCmd, this.ctx); this.cmd = this.runtime.findNext(this.cmd, this.ctx); this.logCommand(); this.bus.dispatchEvent(new Event("step")); }; HDB.prototype.skipTo = function (toCmd) { this.cmd = toCmd.cmd this.bus.dispatchEvent(new Event("skip")); } HDB.prototype.rewrite = function (command, newCode) { console.log('##', command) const parent = command.cmd.parent let prev for (prev of parent.children) { if (prev.next === command.cmd) break; } const next = command.next const tok = _hyperscript.internals.lexer.tokenize(newCode) const newcmd = _hyperscript.internals.parser.requireElement('command', tok) console.log(newcmd) newcmd.startToken = command.startToken newcmd.endToken = command.endToken newcmd.programSource = command.programSource newcmd.sourceFor = function () { return newCode } prev.next = newcmd newcmd.next = next newcmd.parent = parent this.bus.dispatchEvent(new Event('step')) } HDB.prototype.logCommand = function () { var hasSource = this.cmd.sourceFor instanceof Function; var cmdSource = hasSource ? this.cmd.sourceFor() : '-- ' + this.cmd.type; console.log("[hdb] current command: " + cmdSource) } HDB.prototype.traverse = function (ge) { const rv = []; (function recurse(ge) { rv.push(ge); if ('children' in ge) for (const child of ge.children) recurse(child); })(ge); return rv; } // var ui = ` // <div class="hdb" _=" // on load trigger update end // on step from hdb.bus trigger update end // on skip from hdb.bus trigger update end // on continue from hdb.bus log 'done' then remove me.getRootNode().host"> // <script type="text/hyperscript"> // def escapeHTML(unsafe) // js(unsafe) return unsafe // .replace(/&/g, "&") // .replace(/</g, "<") // .replace(/>/g, ">") // .replace(/\\x22/g, """) // .replace(/\\x27/g, "'") end // return it // end // def makeCommandWidget(i) // get \`<span data-cmd=\${i}><button class=skip data-cmd=\${i}>⤷</button>\` // if hdb.EXPERIMENTAL // append \`<button class=rewrite data-cmd=\${i}>Rewrite</button></span>\` // end // return it // end // def renderCode // set hdb.cmdMap to [] // set src to hdb.cmd.programSource // -- Find feature // set feat to hdb.cmd // repeat until no feat.parent or feat.isFeature set feat to feat.parent end // -- Traverse, finding starts // for cmd in hdb.traverse(feat) // if no cmd.startToken continue end // append { // index: cmd.startToken.start, // widget: makeCommandWidget(hdb.cmdMap.length), // cmd: cmd // } to hdb.cmdMap // end // set rv to src.slice(0, hdb.cmdMap[0].index) // for obj in hdb.cmdMap index i // if obj.cmd is hdb.cmd // append obj.widget + '<u class=current>' + // escapeHTML(src.slice(obj.index, hdb.cmdMap[i+1].index)) + '</u>' to rv // else // append obj.widget + escapeHTML(src.slice(obj.index, hdb.cmdMap[i+1].index)) to rv // end // end // return rv // end // def truncate(str, len) // if str.length <= len return str end // return str.substr(0, len) + '…' // def prettyPrint(obj) // if obj is null return 'null' end // if Element.prototype.isPrototypeOf(obj) // set rv to '<<span class="token tagname">' + // obj.tagName.toLowerCase() + "</span>" // for attr in Array.from(obj.attributes) // if attr.specified // set rv to rv + // ' <span class="token attr">' + attr.nodeName + // '</span>=<span class="token string">"' + truncate(attr.textContent, 10) + // '"</span>' // end // end // set rv to rv + '>' // return rv // else if obj.call // if obj.hyperfunc // get "def " + obj.hypername + ' ...' // else // get "function "+obj.name+"(...) {...}" // end // else if obj.toString // call obj.toString() // end // return escapeHTML((it or 'undefined').trim()) // end // </script> // <header _=" // on pointerdown(clientX, clientY) // halt the event // call event.stopPropagation() // get first .hdb // measure its x, y // set xoff to clientX - x // set yoff to clientY - y // repeat until event pointerup from document // wait for pointermove or pointerup from document // add { // left: \${its clientX - xoff}px; // top: \${its clientY - yoff}px; // } to .hdb // end // "> // <h2 class="titlebar">HDB</h2> // <ul role="toolbar" class="toolbar" _="on pointerdown halt"> // <li><button _="on click call hdb.continueExec()"> // ⏵ Continue // </button> // <li><button _="on click call hdb.stepOver()"> // ↷ Step Over // </button> // </ul> // </header> // <section class="sec-code"> // <div class="code-container"> // <pre class="code language-hyperscript" _=" // on update from .hdb if hdb.cmd.programSource // put renderCode() into me // if Prism // call Prism.highlightAllUnder(me) // end // go to bottom of .current in me // end // on click // if target matches .skip // get (target's @data-cmd) as Int // call hdb.skipTo(hdb.cmdMap[result]) // end // if target matches .rewrite // set cmdNo to (target's @data-cmd) as Int // set span to the first <span[data-cmd='\${cmdNo}'] /> // put \`<form class=rewrite><input id=cmd></form>\` into the span // end // end // on submit // halt the event // get (closest @data-cmd to target) as Int // call hdb.rewrite(hdb.cmdMap[result], #cmd's value) // end // "><code></code></pre> // </div> // </section> // <section class="sec-console" _=" // -- Print context at startup // init repeat for var in Object.keys(hdb.ctx) if var is not 'meta' // send hdbUI:consoleEntry(input: var, output: hdb.ctx[var]) to #console"> // <ul id="console" role="list" _=" // on hdbUI:consoleEntry(input, output) // if no hdb.consoleHistory set hdb.consoleHistory to [] end // push(input) on hdb.consoleHistory // set node to #tmpl-console-entry.content.cloneNode(true) // put the node at end of me // set entry to my lastElementChild // go to bottom of the entry // put escapeHTML(input) into .input in the entry // if no output // call hdb._hyperscript.parse(input) // if its execute is not undefined then call its execute(hdb.ctx) // else call its evaluate(hdb.ctx) // end // set output to it // end // put prettyPrint(output) as Fragment into .output in the entry // "> // <template id="tmpl-console-entry"> // <li class="console-entry"> // <kbd><code class="input"></code></kbd> // <samp class="output"></samp> // </li> // </template> // </ul> // <form id="console-form" data-hist="0" _="on submit // send hdbUI:consoleEntry(input: #console-input's value) to #console // set #console-input's value to '' // set @data-hist to 0 // set element oldContent to null // halt // on keydown[key is 'ArrowUp' or key is 'ArrowDown'] // if no hdb.consoleHistory or exit end // if element oldContent is null set element oldContent to #console-input.value end // if event.key is 'ArrowUp' and hdb.consoleHistory.length > -@data-hist // decrement @data-hist // else if event.key is 'ArrowDown' and @data-hist < 0 // increment @data-hist // end end // set #console-input.value to hdb.consoleHistory[hdb.consoleHistory.length + @data-hist as Int] // or oldContent // halt default // on input if @data-hist is '0' set element oldContent to #console-input.value"> // <input id="console-input" placeholder="Enter an expression…" // autocomplete="off"> // </form> // </section> // <style> // .hdb { // border: 1px solid #888; // border-radius: .3em; // box-shadow: 0 .2em .3em #0008; // position: fixed; // top: .5em; right: .5em; // width: min(40ch, calc(100% - 1em)); // max-height: calc(100% - 1em); // background-color: white; // font-family: sans-serif; // opacity: .9; // z-index: 2147483647; // color: black; // display: flex; // flex-flow: column; // } // * { // box-sizing: border-box; // } // header { // display: flex; // justify-content: space-between; // align-items: center; // padding: .4em; // } // .titlebar { // margin: 0; // font-size: 1em; // touch-action: none; // } // .toolbar { // display: flex; // gap: .35em; // list-style: none; // padding-left: 0; // margin: 0; // } // .toolbar a, .toolbar button { // background: #2183ff; // border: 1px solid #3465a4; // box-shadow: 0 1px #b3c6ff inset, 0 .06em .06em #000; // border-radius: .2em; // font: inherit; // padding: .2em .3em; // color: white; // text-shadow: 0 1px black; // font-weight: bold; // } // .toolbar a:hover .toolbar a:focus, .toolbar button:hover, .toolbar button:focus { // background: #94c8ff; // } // .toolbar a:active, .toolbar button:active { // background: #3465a4; // } // .sec-code { // border-radius: .3em; // overflow: hidden; // box-shadow: 0 1px white inset, 0 .06em .06em #0008; // background: #bdf; // margin: 0 .4em; // border: 1px solid #3465a4; // } // .hdb h3 { // margin: 0; // font-size: 1em; // padding: .2em .4em 0 .4em; // } // .code-container { // display: grid; // line-height: 1.2em; // height: calc(12 * 1.2em); // border-radius: 0 0 .2em .2em; // overflow: auto; // scrollbar-width: thin; // scrollbar-color: #0003 transparent; // } // .code, #console, #console-input { // font-family: Consolas, "Andale Mono WT", "Andale Mono", "Lucida Console", "Lucida Sans Typewriter", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Liberation Mono", "Nimbus Mono L", Monaco, "Courier New", Courier, monospace; // } // .code { // width: 0; // margin: 0; // padding-left: 1ch; // tab-size: 2; // -moz-tab-size: 2; // -o-tab-size: 2; // } // .current { // font-weight: bold; // background: #abf; // } // .skip { // padding: 0; // margin: 2px; // border: 1px solid #3465a4; // border-radius: 50%; // color: #3465a4; // background: none; // font-weight: bold; // font-size: 1.2em; // width: calc(2ch / 1.2 - 4px); // height: calc(2ch / 1.2 - 4px); // line-height: 0.6; // } // .skip:hover { // background: #3465a4; // color: #bdf; // } // #console { // overflow-y: scroll; // scrollbar-width: thin; // scrollbar-color: #afc2db transparent; // height: calc(12 * 1.2em); // list-style: none; // padding-left: 0; // margin: 0 .4em .4em .4em; // position: relative; // word-wrap: break-word; // } // #console>*+* { // margin-top: .5em; // } // .console-entry>* { // display: block; // } // .console-entry .input { color: #3465a4; } // .console-entry .output { color: #333; } // .console-entry .input:before { content: '>> ' } // .console-entry .output:before { content: '<- ' } // #console-form { // margin: 0 .4em .4em .4em; // } // #console-input { // width: 100%; // font-size: inherit; // } // .token.tagname { font-weight: bold; } // .token.attr, .token.builtin, .token.italic { font-style: italic; } // .token.string { opacity: .8; } // .token.keyword { color: #3465a4; } // .token.bold, .token.punctuation, .token.operator { font-weight: bold; } // </style> // </div> // `; // HDB.prototype.ui = function () { // var node = document.createElement("div"); // var shadow = node.attachShadow({ mode: "open" }); // node.style.cssText = "all: initial"; // shadow.innerHTML = ui; // document.body.appendChild(node); // _hyperscript.processNode(shadow.querySelector(".hdb")); // }; } })