From 394dcb0e42177eb2f755cd0b8e73d667f1ee45e0 Mon Sep 17 00:00:00 2001 From: David Evans Date: Tue, 16 Jan 2018 22:32:26 +0000 Subject: [PATCH] Add found messages and fix minor rendering issue with groups containing arrows to sides [#37] --- lib/sequence-diagram.js | 318 ++++++++++++--- lib/sequence-diagram.min.js | 2 +- scripts/editor.js | 4 + scripts/sequence/CodeMirrorMode.js | 25 +- scripts/sequence/Generator.js | 116 +++++- scripts/sequence/Generator_spec.js | 384 ++++++++++++++----- scripts/sequence/Parser.js | 34 +- scripts/sequence/Parser_spec.js | 32 ++ scripts/sequence/Renderer.js | 2 + scripts/sequence/components/BaseComponent.js | 2 + scripts/sequence/components/Connect.js | 85 +++- scripts/sequence/themes/Basic.js | 13 + scripts/sequence/themes/Chunky.js | 13 + scripts/sequence/themes/Monospace.js | 13 + scripts/sequence/themes/Sketch.js | 15 +- 15 files changed, 853 insertions(+), 205 deletions(-) diff --git a/lib/sequence-diagram.js b/lib/sequence-diagram.js index edd17c9..58e5241 100644 --- a/lib/sequence-diagram.js +++ b/lib/sequence-diagram.js @@ -645,8 +645,13 @@ define('sequence/CodeMirrorMode',['core/ArrayUtilities'], (array) => { )}; } + const colonTextToEnd = { + type: 'operator', + suggest: true, + then: {'': textToEnd, '\n': hiddenEnd}, + }; const agentListToText = agentListTo({ - ':': {type: 'operator', suggest: true, then: {'': textToEnd}}, + ':': colonTextToEnd, }); const agentList2ToText = {type: 'variable', suggest: 'Agent', then: { '': 0, @@ -656,7 +661,7 @@ define('sequence/CodeMirrorMode',['core/ArrayUtilities'], (array) => { const singleAgentToText = {type: 'variable', suggest: 'Agent', then: { '': 0, ',': CM_ERROR, - ':': {type: 'operator', suggest: true, then: {'': textToEnd}}, + ':': colonTextToEnd, }}; const agentToOptText = {type: 'variable', suggest: 'Agent', then: { '': 0, @@ -700,7 +705,7 @@ define('sequence/CodeMirrorMode',['core/ArrayUtilities'], (array) => { }; } - function makeOpBlock(exit) { + function makeOpBlock(exit, sourceExit) { const op = {type: 'operator', suggest: true, then: { '+': CM_ERROR, '-': CM_ERROR, @@ -729,13 +734,13 @@ define('sequence/CodeMirrorMode',['core/ArrayUtilities'], (array) => { }}, '': exit, }}, - '*': {type: 'operator', suggest: true, then: { + '*': {type: 'operator', suggest: true, then: Object.assign({ '+': op, '-': op, '*': CM_ERROR, '!': CM_ERROR, '': exit, - }}, + }, sourceExit)}, '!': op, '': exit, }; @@ -745,7 +750,10 @@ define('sequence/CodeMirrorMode',['core/ArrayUtilities'], (array) => { const connect = { type: 'keyword', suggest: true, - then: makeOpBlock(agentToOptText), + then: makeOpBlock(agentToOptText, { + ':': colonTextToEnd, + '\n': hiddenEnd, + }), }; const then = {'': 0}; @@ -756,7 +764,10 @@ define('sequence/CodeMirrorMode',['core/ArrayUtilities'], (array) => { override: 'Label', then: {}, }; - return makeOpBlock({type: 'variable', suggest: 'Agent', then}); + return makeOpBlock( + {type: 'variable', suggest: 'Agent', then}, + then + ); } const BASE_THEN = { @@ -1597,10 +1608,10 @@ define('sequence/Parser',[ })()); const CONNECT_AGENT_FLAGS = { - '*': 'begin', - '+': 'start', - '-': 'stop', - '!': 'end', + '*': {flag: 'begin', allowBlankName: true, blankNameFlag: 'source'}, + '+': {flag: 'start'}, + '-': {flag: 'stop'}, + '!': {flag: 'end'}, }; const TERMINATOR_TYPES = [ @@ -1712,7 +1723,7 @@ define('sequence/Parser',[ return -1; } - function readAgentAlias(line, start, end, enableAlias) { + function readAgentAlias(line, start, end, {enableAlias, allowBlankName}) { let aliasSep = -1; if(enableAlias) { aliasSep = findToken(line, 'as', start); @@ -1720,7 +1731,7 @@ define('sequence/Parser',[ if(aliasSep === -1 || aliasSep >= end) { aliasSep = end; } - if(start >= aliasSep) { + if(start >= aliasSep && !allowBlankName) { throw makeError('Missing agent name', errToken(line, start)); } return { @@ -1734,25 +1745,31 @@ define('sequence/Parser',[ aliases = false, } = {}) { const flags = []; + const blankNameFlags = []; let p = start; + let allowBlankName = false; for(; p < end; ++ p) { const token = line[p]; const rawFlag = tokenKeyword(token); const flag = flagTypes[rawFlag]; - if(flag) { - if(flags.includes(flag)) { - throw makeError('Duplicate agent flag: ' + rawFlag, token); - } - flags.push(flag); - } else { + if(!flag) { break; } + if(flags.includes(flag.flag)) { + throw makeError('Duplicate agent flag: ' + rawFlag, token); + } + allowBlankName = allowBlankName || Boolean(flag.allowBlankName); + flags.push(flag.flag); + blankNameFlags.push(flag.blankNameFlag); } - const {name, alias} = readAgentAlias(line, p, end, aliases); + const {name, alias} = readAgentAlias(line, p, end, { + enableAlias: aliases, + allowBlankName, + }); return { name, alias, - flags, + flags: name ? flags : blankNameFlags, }; } @@ -2091,8 +2108,8 @@ define('sequence/Generator',['core/ArrayUtilities'], (array) => { equals: (a, b) => { return a.id === b.id; }, - make: (id, {anchorRight = false} = {}) => { - return {id, anchorRight}; + make: (id, {anchorRight = false, isVirtualSource = false} = {}) => { + return {id, anchorRight, isVirtualSource}; }, indexOf: (list, gAgent) => { return array.indexOf(list, gAgent, GAgent.equals); @@ -2100,6 +2117,14 @@ define('sequence/Generator',['core/ArrayUtilities'], (array) => { hasIntersection: (a, b) => { return array.hasIntersection(a, b, GAgent.equals); }, + addNearby: (target, reference, item, offset) => { + const p = array.indexOf(target, reference, GAgent.equals); + if(p === -1) { + target.push(item); + } else { + target.splice(p + offset, 0, item); + } + }, }; const NOTE_DEFAULT_G_AGENTS = { @@ -2108,6 +2133,8 @@ define('sequence/Generator',['core/ArrayUtilities'], (array) => { 'note right': [GAgent.make(']')], }; + const SPECIAL_AGENT_IDS = ['[', ']']; + const MERGABLE = { 'agent begin': { check: ['mode'], @@ -2282,7 +2309,7 @@ define('sequence/Generator',['core/ArrayUtilities'], (array) => { this.activeGroups = new Map(); this.gAgents = []; this.labelPattern = null; - this.blockCount = 0; + this.nextID = 0; this.nesting = []; this.markers = new Set(); this.currentSection = null; @@ -2311,7 +2338,7 @@ define('sequence/Generator',['core/ArrayUtilities'], (array) => { this.endGroup = this.endGroup.bind(this); } - toGAgent({alias, name}) { + toGAgent({name, alias, flags}) { if(alias) { if(this.agentAliases.has(name)) { throw new Error( @@ -2330,7 +2357,9 @@ define('sequence/Generator',['core/ArrayUtilities'], (array) => { } this.agentAliases.set(alias, name); } - return GAgent.make(this.agentAliases.get(name) || name); + return GAgent.make(this.agentAliases.get(name) || name, { + isVirtualSource: flags.includes('source'), + }); } addStage(stage, isVisible = true) { @@ -2366,7 +2395,12 @@ define('sequence/Generator',['core/ArrayUtilities'], (array) => { } defineGAgents(gAgents) { - array.mergeSets(this.currentNest.gAgents, gAgents, GAgent.equals); + array.mergeSets( + this.currentNest.gAgents, + gAgents.filter((gAgent) => + !SPECIAL_AGENT_IDS.includes(gAgent.id)), + GAgent.equals + ); array.mergeSets(this.gAgents, gAgents, GAgent.equals); } @@ -2390,7 +2424,9 @@ define('sequence/Generator',['core/ArrayUtilities'], (array) => { validateGAgents(gAgents, { allowGrouped = false, rejectGrouped = false, + allowVirtual = false, } = {}) { + /* jshint -W074 */ // agent validity checking requires several steps gAgents.forEach((gAgent) => { const state = this.getGAgentState(gAgent); if(state.covered) { @@ -2404,6 +2440,9 @@ define('sequence/Generator',['core/ArrayUtilities'], (array) => { if(state.blocked && (!allowGrouped || state.group === null)) { throw new Error('Duplicate agent name: ' + gAgent.id); } + if(!allowVirtual && gAgent.isVirtualSource) { + throw new Error('cannot use message source here'); + } if(gAgent.id.startsWith('__')) { throw new Error(gAgent.id + ' is a reserved name'); } @@ -2497,12 +2536,18 @@ define('sequence/Generator',['core/ArrayUtilities'], (array) => { this.replaceGAgentState(rightGAgent, AgentState.LOCKED); this.nesting.push(this.currentNest); - return {gAgents, stages}; + return {stages}; } nextBlockName() { - const name = '__BLOCK' + this.blockCount; - ++ this.blockCount; + const name = '__BLOCK' + this.nextID; + ++ this.nextID; + return name; + } + + nextVirtualAgentName() { + const name = '__' + this.nextID; + ++ this.nextID; return name; } @@ -2706,9 +2751,13 @@ define('sequence/Generator',['core/ArrayUtilities'], (array) => { let ind1 = GAgent.indexOf(this.gAgents, gAgents1[0]); let ind2 = GAgent.indexOf(this.gAgents, gAgents2[0]); if(ind1 === -1) { - ind1 = this.gAgents.length; + // Virtual sources written as '* -> Ref' will spawn to the left, + // not the right (as non-virtual agents would) + ind1 = gAgents1[0].isVirtualSource ? -1 : this.gAgents.length; } if(ind2 === -1) { + // Virtual and non-virtual agents written as 'Ref -> *' will + // spawn to the right ind2 = this.gAgents.length; } if(ind1 === ind2) { @@ -2755,21 +2804,79 @@ define('sequence/Generator',['core/ArrayUtilities'], (array) => { return {beginGAgents, endGAgents, startGAgents, stopGAgents}; } + makeVirtualAgent(anchorRight) { + const virtualGAgent = GAgent.make(this.nextVirtualAgentName(), { + anchorRight, + isVirtualSource: true, + }); + this.replaceGAgentState(virtualGAgent, AgentState.LOCKED); + return virtualGAgent; + } + + addNearbyAgent(gAgentReference, gAgent, offset) { + GAgent.addNearby( + this.currentNest.gAgents, + gAgentReference, + gAgent, + offset + ); + GAgent.addNearby( + this.gAgents, + gAgentReference, + gAgent, + offset + ); + } + + expandVirtualSourceAgents(gAgents) { + if(gAgents[0].isVirtualSource) { + if(gAgents[1].isVirtualSource) { + throw new Error('Cannot connect found messages'); + } + if(SPECIAL_AGENT_IDS.includes(gAgents[1].id)) { + throw new Error( + 'Cannot connect found messages to special agents' + ); + } + const virtualGAgent = this.makeVirtualAgent(true); + this.addNearbyAgent(gAgents[1], virtualGAgent, 0); + return [virtualGAgent, gAgents[1]]; + } + if(gAgents[1].isVirtualSource) { + if(SPECIAL_AGENT_IDS.includes(gAgents[0].id)) { + throw new Error( + 'Cannot connect found messages to special agents' + ); + } + const virtualGAgent = this.makeVirtualAgent(false); + this.addNearbyAgent(gAgents[0], virtualGAgent, 1); + return [gAgents[0], virtualGAgent]; + } + return gAgents; + } + handleConnect({agents, label, options}) { const flags = this.filterConnectFlags(agents); let gAgents = agents.map(this.toGAgent); - this.validateGAgents(gAgents, {allowGrouped: true}); + this.validateGAgents(gAgents, { + allowGrouped: true, + allowVirtual: true, + }); const allGAgents = array.flatMap(gAgents, this.expandGroupedGAgent); - this.defineGAgents(allGAgents); + this.defineGAgents(allGAgents + .filter((gAgent) => !gAgent.isVirtualSource) + ); gAgents = this.expandGroupedGAgentConnection(gAgents); + gAgents = this.expandVirtualSourceAgents(gAgents); const agentIDs = gAgents.map((gAgent) => gAgent.id); const implicitBeginGAgents = (agents .filter(PAgent.hasFlag('begin', false)) .map(this.toGAgent) + .filter((gAgent) => !gAgent.isVirtualSource) ); this.addStage(this.setGAgentVis(implicitBeginGAgents, true, 'box')); @@ -2866,7 +2973,7 @@ define('sequence/Generator',['core/ArrayUtilities'], (array) => { this.agentAliases.clear(); this.activeGroups.clear(); this.gAgents.length = 0; - this.blockCount = 0; + this.nextID = 0; this.nesting.length = 0; this.labelPattern = [{token: 'label'}]; } @@ -3457,6 +3564,7 @@ define('sequence/components/BaseComponent',[],() => { theme, agentInfos, visibleAgentIDs, + momentaryAgentIDs, textSizer, addSpacing, addSeparation, @@ -3469,6 +3577,7 @@ define('sequence/components/BaseComponent',[],() => { theme, agentInfos, visibleAgentIDs, + momentaryAgentIDs, textSizer, addSpacing, addSeparation, @@ -4211,10 +4320,12 @@ define('sequence/components/AgentHighlight',['./BaseComponent'], (BaseComponent) }); define('sequence/components/Connect',[ + 'core/ArrayUtilities', './BaseComponent', 'svg/SVGUtilities', 'svg/SVGShapes', ], ( + array, BaseComponent, svg, SVGShapes @@ -4318,6 +4429,18 @@ define('sequence/components/Connect',[ ]; class Connect extends BaseComponent { + separationPre({agentIDs}, env) { + const r = env.theme.connect.source.radius; + agentIDs.forEach((id) => { + const agentInfo = env.agentInfos.get(id); + if(!agentInfo.isVirtualSource) { + return; + } + agentInfo.currentRad = r; + agentInfo.currentMaxRad = Math.max(agentInfo.currentMaxRad, r); + }); + } + separation({label, agentIDs, options}, env) { const config = env.theme.connect; @@ -4359,6 +4482,8 @@ define('sequence/components/Connect',[ ) * 2 ); } + + array.mergeSets(env.momentaryAgentIDs, agentIDs); } renderSelfConnect({label, agentIDs, options}, env) { @@ -4436,14 +4561,59 @@ define('sequence/components/Connect',[ ); } + renderSimpleLine(x0, x1, options, env) { + const dir = (x0 < x1) ? 1 : -1; + + const config = env.theme.connect; + const line = config.line[options.line]; + const lArrow = ARROWHEADS[options.left]; + const rArrow = ARROWHEADS[options.right]; + + const rendered = line.renderFlat(line.attrs, { + x1: x0, + dx1: lArrow.lineGap(env.theme, line.attrs) * dir, + x2: x1, + dx2: -rArrow.lineGap(env.theme, line.attrs) * dir, + y: env.primaryY, + }); + env.shapeLayer.appendChild(rendered.shape); + return rendered; + } + + renderSimpleArrowheads(options, renderedLine, env, dir) { + const lArrow = ARROWHEADS[options.left]; + const rArrow = ARROWHEADS[options.right]; + + lArrow.render(env.shapeLayer, env.theme, renderedLine.p1, dir); + rArrow.render(env.shapeLayer, env.theme, renderedLine.p2, -dir); + + return {lArrow, rArrow}; + } + + renderVirtualSources(from, to, renderedLine, env) { + const config = env.theme.connect.source; + + if(from.isVirtualSource) { + env.shapeLayer.appendChild(config.render({ + x: renderedLine.p1.x - config.radius, + y: renderedLine.p1.y, + radius: config.radius, + })); + } + if(to.isVirtualSource) { + env.shapeLayer.appendChild(config.render({ + x: renderedLine.p2.x + config.radius, + y: renderedLine.p2.y, + radius: config.radius, + })); + } + } + renderSimpleConnect({label, agentIDs, options}, env) { const config = env.theme.connect; const from = env.agentInfos.get(agentIDs[0]); const to = env.agentInfos.get(agentIDs[1]); - const lArrow = ARROWHEADS[options.left]; - const rArrow = ARROWHEADS[options.right]; - const dir = (from.x < to.x) ? 1 : -1; const height = ( @@ -4469,18 +4639,12 @@ define('sequence/components/Connect',[ SVGTextBlockClass: env.SVGTextBlockClass, }); - const line = config.line[options.line]; - const rendered = line.renderFlat(line.attrs, { - x1: x0, - dx1: lArrow.lineGap(env.theme, line.attrs) * dir, - x2: x1, - dx2: -rArrow.lineGap(env.theme, line.attrs) * dir, - y, - }); - env.shapeLayer.appendChild(rendered.shape); - - lArrow.render(env.shapeLayer, env.theme, rendered.p1, dir); - rArrow.render(env.shapeLayer, env.theme, rendered.p2, -dir); + const rendered = this.renderSimpleLine(x0, x1, options, env); + const { + lArrow, + rArrow + } = this.renderSimpleArrowheads(options, rendered, env, dir); + this.renderVirtualSources(from, to, rendered, env); const arrowSpread = Math.max( lArrow.height(env.theme), @@ -4996,6 +5160,7 @@ define('sequence/Renderer',[ theme: this.theme, agentInfos: this.agentInfos, visibleAgentIDs: this.visibleAgentIDs, + momentaryAgentIDs: agentIDs, textSizer: this.sizer, addSpacing, addSeparation: this.addSeparation, @@ -5200,6 +5365,7 @@ define('sequence/Renderer',[ id: agent.id, formattedLabel: agent.formattedLabel, anchorRight: agent.anchorRight, + isVirtualSource: agent.isVirtualSource, index, x: null, latestYStart: null, @@ -5906,6 +6072,19 @@ define('sequence/themes/Basic',[ 'line-height': LINE_HEIGHT, }, }, + source: { + radius: 2, + render: ({x, y, radius}) => { + return svg.make('circle', { + 'cx': x, + 'cy': y, + 'r': radius, + 'fill': '#000000', + 'stroke': '#000000', + 'stroke-width': 1, + }); + }, + }, mask: { padding: { top: 0, @@ -6227,6 +6406,19 @@ define('sequence/themes/Monospace',[ 'line-height': LINE_HEIGHT, }, }, + source: { + radius: 2, + render: ({x, y, radius}) => { + return svg.make('circle', { + 'cx': x, + 'cy': y, + 'r': radius, + 'fill': '#000000', + 'stroke': '#000000', + 'stroke-width': 1, + }); + }, + }, mask: { padding: { top: 0, @@ -6547,6 +6739,19 @@ define('sequence/themes/Chunky',[ 'line-height': LINE_HEIGHT, }, }, + source: { + radius: 5, + render: ({x, y, radius}) => { + return svg.make('circle', { + 'cx': x, + 'cy': y, + 'r': radius, + 'fill': '#000000', + 'stroke': '#000000', + 'stroke-width': 3, + }); + }, + }, mask: { padding: { top: 1, @@ -7287,6 +7492,19 @@ define('sequence/themes/Sketch',[ 'line-height': LINE_HEIGHT, }, }, + source: { + radius: 1, + render: ({x, y, radius}) => { + return svg.make('circle', { + 'cx': x, + 'cy': y, + 'r': radius, + 'fill': '#000000', + 'stroke': '#000000', + 'stroke-width': 1, + }); + }, + }, mask: { padding: { top: 0, @@ -7722,7 +7940,7 @@ define('sequence/themes/Sketch',[ ); return { shape: svg.make('path', Object.assign({'d': ln.nodes}, attrs)), - p1: {x: ln.p1.x - dx1, y: ln.p2.y}, + p1: {x: ln.p1.x - dx1, y: ln.p1.y}, p2: {x: ln.p2.x - dx2, y: ln.p2.y}, }; } diff --git a/lib/sequence-diagram.min.js b/lib/sequence-diagram.min.js index 8c18dbd..e9a1173 100644 --- a/lib/sequence-diagram.min.js +++ b/lib/sequence-diagram.min.js @@ -1 +1 @@ -!function(){var e,t,n;!function(s){function r(e,t){return y.call(e,t)}function i(e,t){var n,s,r,i,a,o,h,l,d,g,c,u=t&&t.split("/"),p=b.map,f=p&&p["*"]||{};if(e){for(a=(e=e.split("/")).length-1,b.nodeIdCompat&&w.test(e[a])&&(e[a]=e[a].replace(w,"")),"."===e[0].charAt(0)&&u&&(e=u.slice(0,u.length-1).concat(e)),d=0;d0&&(e.splice(d-1,2),d-=2)}e=e.join("/")}if((u||f)&&p){for(d=(n=e.split("/")).length;d>0;d-=1){if(s=n.slice(0,d).join("/"),u)for(g=u.length;g>0;g-=1)if((r=p[u.slice(0,g).join("/")])&&(r=r[s])){i=r,o=d;break}if(i)break;!h&&f&&f[s]&&(h=f[s],l=d)}!i&&h&&(i=h,o=l),i&&(n.splice(0,o,i),e=n.join("/"))}return e}function a(e,t){return function(){var n=k.call(arguments,0);return"string"!=typeof n[0]&&1===n.length&&n.push(null),c.apply(s,n.concat([e,t]))}}function o(e){return function(t){f[e]=t}}function h(e){if(r(m,e)){var t=m[e];delete m[e],x[e]=!0,g.apply(s,t)}if(!r(f,e)&&!r(x,e))throw new Error("No "+e);return f[e]}function l(e){var t,n=e?e.indexOf("!"):-1;return n>-1&&(t=e.substring(0,n),e=e.substring(n+1,e.length)),[t,e]}function d(e){return e?l(e):[]}var g,c,u,p,f={},m={},b={},x={},y=Object.prototype.hasOwnProperty,k=[].slice,w=/\.js$/;u=function(e,t){var n,s=l(e),r=s[0],a=t[1];return e=s[1],r&&(n=h(r=i(r,a))),r?e=n&&n.normalize?n.normalize(e,function(e){return function(t){return i(t,e)}}(a)):i(e,a):(r=(s=l(e=i(e,a)))[0],e=s[1],r&&(n=h(r))),{f:r?r+"!"+e:e,n:e,pr:r,p:n}},p={require:function(e){return a(e)},exports:function(e){var t=f[e];return void 0!==t?t:f[e]={}},module:function(e){return{id:e,uri:"",exports:f[e],config:function(e){return function(){return b&&b.config&&b.config[e]||{}}}(e)}}},g=function(e,t,n,i){var l,g,c,b,y,k,w,A=[],v=typeof n;if(i=i||e,k=d(i),"undefined"===v||"function"===v){for(t=!t.length&&n.length?["require","exports","module"]:t,y=0;y{"use strict";return class{constructor(){this.listeners=new Map,this.forwards=new Set}addEventListener(e,t){const n=this.listeners.get(e);n?n.push(t):this.listeners.set(e,[t])}removeEventListener(e,t){const n=this.listeners.get(e);if(!n)return;const s=n.indexOf(t);-1!==s&&n.splice(s,1)}countEventListeners(e){return(this.listeners.get(e)||[]).length}removeAllEventListeners(e){e?this.listeners.delete(e):this.listeners.clear()}addEventForwarding(e){this.forwards.add(e)}removeEventForwarding(e){this.forwards.delete(e)}removeAllEventForwardings(){this.forwards.clear()}trigger(e,t=[]){(this.listeners.get(e)||[]).forEach(e=>e.apply(null,t)),this.forwards.forEach(n=>n.trigger(e,t))}}}),n("core/ArrayUtilities",[],()=>{"use strict";function e(e,t,n=null){if(null===n)return e.indexOf(t);for(let s=0;s=e.length)return void r.push(s.slice());const i=e[n];if(!Array.isArray(i))return s.push(i),t(e,n+1,s,r),void s.pop();for(let a=0;a{n.push(...t(e))}),n}}}),n("sequence/CodeMirrorMode",["core/ArrayUtilities"],e=>{"use strict";function t(e,t,n,s){return""===t?function(e,t,n){return"object"==typeof n.suggest&&n.suggest.global?[n.suggest]:"string"!=typeof n.suggest||t.suggest===n.suggest?null:e["known"+n.suggest]}(e,n,s):!0===s.suggest?[function(e,t){return Object.keys(t.then).length>0?e+" ":e+"\n"}(t,s)]:Array.isArray(s.suggest)?s.suggest:s.suggest?[s.suggest]:null}function n(n,s){const r=[],i=e.last(s);return Object.keys(i.then).forEach(a=>{let o=i.then[a];"number"==typeof o&&(o=s[s.length-o-1]),e.mergeSets(r,t(n,a,i,o))}),r}function s(t,n,s,{suggest:r,override:i}){n.type&&r!==n.type&&(i&&(n.type=i),e.mergeSets(t["known"+n.type],[n.value+" "]),n.type="",n.value=""),"string"==typeof r&&t["known"+r]&&(n.type=r,n.value&&(n.value+=s.s),n.value+=s.v)}function r(t,r,i){const o={type:"",value:""};let h=i;const l=[h];return t.line.forEach((r,i)=>{i===t.line.length-1&&(t.completions=n(t,l));const d=r.q?"":r.v,g=h.then[d]||h.then[""];"number"==typeof g?l.length-=g:l.push(g||a),h=e.last(l),s(t,o,r,h)}),r&&s(t,o,null,{}),t.nextCompletions=n(t,l),t.valid=Boolean(h.then["\n"])||0===Object.keys(h.then).length,h.type}function i(e){const t=e.baseToken||{};return{value:t.v||"",quoted:t.q||!1}}const a={type:"error line-error",then:{"":0}},o=(()=>{function e(e){return{type:"string",then:Object.assign({"":0},e)}}function t(e){return{type:"variable",suggest:"Agent",then:Object.assign({},e,{"":0,",":{type:"operator",suggest:!0,then:{"":1}}})}}function n(e){return{type:"keyword",suggest:[e+" of ",e+": "],then:{of:{type:"keyword",suggest:!0,then:{"":h}},":":{type:"operator",suggest:!0,then:{"":i}},"":h}}}function s(e){const t={type:"operator",suggest:!0,then:{"+":a,"-":a,"*":a,"!":a,"":e}};return{"+":{type:"operator",suggest:!0,then:{"+":a,"-":a,"*":t,"!":a,"":e}},"-":{type:"operator",suggest:!0,then:{"+":a,"-":a,"*":t,"!":{type:"operator",then:{"+":a,"-":a,"*":a,"!":a,"":e}},"":e}},"*":{type:"operator",suggest:!0,then:{"+":t,"-":t,"*":a,"!":a,"":e}},"!":t,"":e}}const r={type:"",suggest:"\n",then:{}},i=e({"\n":r}),o={type:"variable",suggest:"Agent",then:{"":0,"\n":r,",":{type:"operator",suggest:!0,then:{"":1}},as:{type:"keyword",suggest:!0,then:{"":{type:"variable",suggest:"Agent",then:{"":0,",":{type:"operator",suggest:!0,then:{"":3}},"\n":r}}}}}},h=t({":":{type:"operator",suggest:!0,then:{"":i}}}),l={type:"variable",suggest:"Agent",then:{"":0,",":{type:"operator",suggest:!0,then:{"":h}},":":a}},d={type:"variable",suggest:"Agent",then:{"":0,",":a,":":{type:"operator",suggest:!0,then:{"":i}}}},g={type:"variable",suggest:"Agent",then:{"":0,":":{type:"operator",suggest:!0,then:{"":i,"\n":{type:"",then:{}}}},"\n":r}},c={":":{type:"operator",suggest:!0,then:{"":e({as:{type:"keyword",suggest:!0,then:{"":{type:"variable",suggest:"Agent",then:{"":0,"\n":r}}}}})}}},u={type:"keyword",suggest:!0,then:Object.assign({over:{type:"keyword",suggest:!0,then:{"":t(c)}}},c)},p={title:{type:"keyword",suggest:!0,then:{"":i}},theme:{type:"keyword",suggest:!0,then:{"":{type:"string",suggest:{global:"themes",suffix:"\n"},then:{"":0,"\n":r}}}},headers:{type:"keyword",suggest:!0,then:{none:{type:"keyword",suggest:!0,then:{}},cross:{type:"keyword",suggest:!0,then:{}},box:{type:"keyword",suggest:!0,then:{}},fade:{type:"keyword",suggest:!0,then:{}},bar:{type:"keyword",suggest:!0,then:{}}}},terminators:{type:"keyword",suggest:!0,then:{none:{type:"keyword",suggest:!0,then:{}},cross:{type:"keyword",suggest:!0,then:{}},box:{type:"keyword",suggest:!0,then:{}},fade:{type:"keyword",suggest:!0,then:{}},bar:{type:"keyword",suggest:!0,then:{}}}},define:{type:"keyword",suggest:!0,then:{"":o,as:a}},begin:{type:"keyword",suggest:!0,then:{"":o,reference:u,as:a}},end:{type:"keyword",suggest:!0,then:{"":o,as:a,"\n":r}},if:{type:"keyword",suggest:!0,then:{"":i,":":{type:"operator",suggest:!0,then:{"":i}},"\n":r}},else:{type:"keyword",suggest:["else\n","else if: "],then:{if:{type:"keyword",suggest:"if: ",then:{"":i,":":{type:"operator",suggest:!0,then:{"":i}}}},"\n":r}},repeat:{type:"keyword",suggest:!0,then:{"":i,":":{type:"operator",suggest:!0,then:{"":i}},"\n":r}},note:{type:"keyword",suggest:!0,then:{over:{type:"keyword",suggest:!0,then:{"":h}},left:n("left"),right:n("right"),between:{type:"keyword",suggest:!0,then:{"":l}}}},state:{type:"keyword",suggest:"state over ",then:{over:{type:"keyword",suggest:!0,then:{"":d}}}},text:{type:"keyword",suggest:!0,then:{left:n("left"),right:n("right")}},autolabel:{type:"keyword",suggest:!0,then:{off:{type:"keyword",suggest:!0,then:{}},"":i}},simultaneously:{type:"keyword",suggest:!0,then:{":":{type:"operator",suggest:!0,then:{}},with:{type:"keyword",suggest:!0,then:{"":{type:"variable",suggest:"Label",then:{"":0,":":{type:"operator",suggest:!0,then:{}}}}}}}}};return e=>({type:"error line-error",then:Object.assign({},p,function(e){const t={type:"keyword",suggest:!0,then:s(g)},n={"":0};return e.forEach(e=>n[e]=t),n[":"]={type:"operator",suggest:!0,override:"Label",then:{}},s({type:"variable",suggest:"Agent",then:n})}(e))})})();return class{constructor(e,t){this.tokenDefinitions=e,this.commands=o(t),this.lineComment="#"}startState(){return{currentType:-1,current:"",currentSpace:"",currentQuoted:!1,knownAgent:[],knownLabel:[],beginCompletions:n({},[this.commands]),completions:[],nextCompletions:[],valid:!0,line:[],indent:0}}_matchPattern(e,t,n){return t?(t.lastIndex=0,e.match(t,n)):null}_tokenBegin(e,t){t.currentSpace="";let n="";for(;;){if(e.eol())return!1;t.currentSpace+=n;for(let n=0;n{"use strict";function t(e,t,n){return t.lastIndex=n,t.exec(e)}function n(e){return"n"===e[1]?"\n":e[1]}function s(e,n,s){return s?function(e,n,s){if(s.escape){const r=t(e,s.escape,n);if(r)return{newBlock:null,end:!1,appendSpace:"",appendValue:s.escapeWith(r),skip:r[0].length}}const r=t(e,s.end,n);return r?{newBlock:null,end:!0,appendSpace:"",appendValue:s.includeEnd?r[0]:"",skip:r[0].length}:{newBlock:null,end:!1,appendSpace:"",appendValue:e[n],skip:1}}(e,n,s):function(e,n){for(let s=0;s,])/y,end:/(?=[ \t\r\n:+\-~*!<>,])|$/y},{start:/(?=[\-~<])/y,end:/(?=[^\-~<>x])|[\-~]x|[<>](?=x)|$/y,includeEnd:!0},{start:/,/y,baseToken:{v:","}},{start:/:/y,baseToken:{v:":"}},{start:/!/y,baseToken:{v:"!"}},{start:/\+/y,baseToken:{v:"+"}},{start:/\*/y,baseToken:{v:"*"}},{start:/\n/y,baseToken:{v:"\n"}}];class a{constructor(e){this.src=e,this.block=null,this.token=null,this.pos={i:0,ln:0,ch:0},this.reset()}isOver(){return this.pos.i>this.src.length}reset(){this.token={s:"",v:"",q:!1,b:null,e:null},this.block=null}beginToken(e){this.block=e.newBlock,Object.assign(this.token,this.block.baseToken),this.token.b=r(this.pos)}endToken(){let e=null;return this.block.omit||(this.token.e=r(this.pos),e=this.token),this.reset(),e}advance(){const e=s(this.src,this.pos.i,this.block);return e.newBlock&&this.beginToken(e),this.token.s+=e.appendSpace,this.token.v+=e.appendValue,function(e,t,n){for(let s=0;s{e.q||"\n"!==e.v?n.push(e):n.length>0&&(t.push(n),n=[])}),n.length>0&&t.push(n),t}}}),n("sequence/MarkdownParser",[],()=>{"use strict";function e(e,t,s){const r=" "+e+" ";let i=-1,a=r.length,o=0;return n.forEach(({begin:e,end:n,attrs:h},l)=>{const d=s[l]?n:e;d.lastIndex=t;const g=d.exec(r);g&&(g.indexo)&&(i=l,a=g.index,o=d.lastIndex)}),{styleIndex:i,start:a,end:o-1}}function t(e,t){if(!e)return null;const s={};return t.forEach((e,t)=>{e&&Object.assign(s,n[t].attrs)}),s}const n=[{begin:/[\s_~`]\*(?=\S)/g,end:/\S\*(?=[\s_~`])/g,attrs:{"font-style":"italic"}},{begin:/[\s*~`]_(?=\S)/g,end:/\S_(?=[\s*~`])/g,attrs:{"font-style":"italic"}},{begin:/[\s_~`]\*\*(?=\S)/g,end:/\S\*\*(?=[\s_~`])/g,attrs:{"font-weight":"bolder"}},{begin:/[\s*~`]__(?=\S)/g,end:/\S__(?=[\s*~`])/g,attrs:{"font-weight":"bolder"}},{begin:/[\s_*`]~(?=\S)/g,end:/\S~(?=[\s_*`])/g,attrs:{"text-decoration":"line-through"}},{begin:/[\s_*~.]`(?=\S)/g,end:/\S`(?=[\s_*~.])/g,attrs:{"font-family":"monospace"}}];return function(s){if(!s)return[];const r=n.map(()=>!1);let i=0,a=null;const o=[];return s.split("\n").forEach(n=>{const s=[];let h=0;for(;;){const{styleIndex:o,start:l,end:d}=e(n,h,r);if(-1===o)break;r[o]?(r[o]=!1,--i):(r[o]=!0,++i),l>h&&s.push({text:n.substring(h,l),attrs:a}),a=t(i,r),h=d}h{"use strict";function e(e){const t=s.exec(e);return t&&t[1]?t[1].length:0}function t(t){if("label"===t)return{token:"label"};const n=t.indexOf(" ");let s=null,r=null;return-1===n?(s=t,r=[]):(s=t.substr(0,n),r=t.substr(n+1).split(",")),"inc"===s?function(t){let n=1,s=1,r=0;return t[0]&&(n=Number(t[0]),r=Math.max(r,e(t[0]))),t[1]&&(s=Number(t[1]),r=Math.max(r,e(t[1]))),{start:n,inc:s,dp:r}}(r):"<"+t+">"}const n=/(.*?)<([^<>]*)>/g,s=/\.([0-9]*)/;return function(e){const s=[];let r=null,i=0;for(n.lastIndex=0;r=n.exec(e);)r[1]&&s.push(r[1]),r[2]&&s.push(t(r[2])),i=n.lastIndex;const a=e.substr(i);return a&&s.push(a),s}}),n("sequence/Parser",["core/ArrayUtilities","./Tokeniser","./MarkdownParser","./LabelPatternParser"],(e,t,n,s)=>{"use strict";function r(e,t=null){let n="";return t&&(n=" at line "+(t.b.ln+1)+", character "+t.b.ch),new Error(e+n)}function i(e,t=0,n=null){if(null===n&&(n=e.length),n<=t)return"";let s=e[t].v;for(let r=t+1;r=s)&&(o=s),n>=o)throw r("Missing agent name",function(t,n){if(n{const t=e.combine([[{tok:"",type:0},{tok:"<",type:1},{tok:"<<",type:2}],[{tok:"-",type:"solid"},{tok:"--",type:"dash"},{tok:"~",type:"wave"}],[{tok:"",type:0},{tok:">",type:1},{tok:">>",type:2},{tok:"x",type:3}]]).filter(e=>0!==e[0].type||0!==e[2].type),n=new Map;return t.forEach(e=>{n.set(e.map(e=>e.tok).join(""),{line:e[1].type,left:e[0].type,right:e[2].type})}),n})(),p={"*":"begin","+":"start","-":"stop","!":"end"},f=["none","box","cross","fade","bar"],m={text:{mode:"text",types:{left:{type:"note left",skip:["of"],min:0,max:null},right:{type:"note right",skip:["of"],min:0,max:null}}},note:{mode:"note",types:{over:{type:"note over",skip:[],min:0,max:null},left:{type:"note left",skip:["of"],min:0,max:null},right:{type:"note right",skip:["of"],min:0,max:null},between:{type:"note between",skip:[],min:2,max:null}}},state:{mode:"state",types:{over:{type:"note over",skip:[],min:1,max:1}}}},b={define:{type:"agent define"},begin:{type:"agent begin",mode:"box"},end:{type:"agent end",mode:"cross"}},x=[(e,t)=>"title"!==a(e[0])?null:(t.title=i(e,1),!0),(e,t)=>"theme"!==a(e[0])?null:(t.theme=i(e,1),!0),(e,t)=>{if("terminators"!==a(e[0]))return null;const n=a(e[1]);if(!n)throw r("Unspecified termination",e[0]);if(-1===f.indexOf(n))throw r('Unknown termination "'+n+'"',e[1]);return t.terminators=n,!0},(e,t)=>{if("headers"!==a(e[0]))return null;const n=a(e[1]);if(!n)throw r("Unspecified header",e[0]);if(-1===f.indexOf(n))throw r('Unknown header "'+n+'"',e[1]);return t.headers=n,!0},e=>{if("autolabel"!==a(e[0]))return null;let t=null;return t="off"===a(e[1])?"