From d103dc05747cca52f0e229d40876ac0c8d2b41e3 Mon Sep 17 00:00:00 2001 From: David Evans Date: Mon, 20 Jan 2020 21:30:23 +0000 Subject: [PATCH] Improve behaviour when adding formatting elements from library --- web/lib/editor.js | 82 ++++++++++++++++++--- web/lib/editor.min.js | 2 +- web/scripts/interface/CodeEditor.mjs | 67 ++++++++++++++--- web/scripts/interface/ComponentsLibrary.mjs | 10 +++ web/scripts/interface/Interface.mjs | 5 +- 5 files changed, 143 insertions(+), 23 deletions(-) diff --git a/web/lib/editor.js b/web/lib/editor.js index 586796a..6bdf17a 100644 --- a/web/lib/editor.js +++ b/web/lib/editor.js @@ -253,51 +253,61 @@ { code: '**{text}**', preview: 'A -> B: **bold**', + surround: true, title: 'Bold markdown', }, { code: '_{text}_', preview: 'A -> B: _italic_', + surround: true, title: 'Italic markdown', }, { code: '~{text}~', preview: 'A -> B: ~strikeout~', + surround: true, title: 'Strikeout markdown', }, { code: '{text}', preview: 'A -> B: underline', + surround: true, title: 'Underline markdown', }, { code: '{text}', preview: 'A -> B: overline', + surround: true, title: 'Overline markdown', }, { code: '{text}', preview: 'A -> B: superscript', + surround: true, title: 'Superscript markdown', }, { code: '{text}', preview: 'A -> B: subscript', + surround: true, title: 'Subscript markdown', }, { code: '`{text}`', preview: 'A -> B: `mono`', + surround: true, title: 'Monospace markdown', }, { code: '{text}', preview: 'A -> B: red', + surround: true, title: 'Red markdown', }, { code: '{text}', preview: 'A -> B: highlight', + surround: true, title: 'Highlight markdown', }, { @@ -870,6 +880,15 @@ return null; } + function lineWithinRange(doc, ln, start, end) { + const full = doc.getLine(ln); + const begin = (ln === start.line) ? start.ch : 0; + return { + chOffset: begin, + line: (ln === end.line) ? full.slice(begin, end.ch) : full.slice(begin), + }; + } + class CodeEditor extends EventObject { constructor(dom, container, { mode = '', @@ -974,8 +993,8 @@ this.code.off('cursorActivity', move); this.paramMarkers.forEach((m) => m.clear()); this.paramMarkers = null; + this.code.setCursor(endBookmark.find()); endBookmark.clear(); - this.code.setCursor(end); this.cancelParams = null; this.advanceParams = null; }; @@ -1001,12 +1020,12 @@ const doc = this.code.getDoc(); const ranges = []; - let {ch} = start; - for(let ln = start.line; ln < endBookmark.find().line; ++ ln) { - const line = doc.getLine(ln).slice(ch); + const end = endBookmark.find(); + for(let ln = start.line; ln <= end.line; ++ ln) { + const {chOffset, line} = lineWithinRange(doc, ln, start, end); for(let p = 0; (p = line.indexOf(tok, p)) !== -1; p += tok.length) { - const anchor = {ch: p, line: ln}; - const head = {ch: p + tok.length, line: ln}; + const anchor = {ch: chOffset + p, line: ln}; + const head = {ch: chOffset + p + tok.length, line: ln}; ranges.push({anchor, head}); this.paramMarkers.push(doc.markText(anchor, head, { className: 'param', @@ -1015,7 +1034,6 @@ inclusiveRight: true, })); } - ch = 0; } if(ranges.length > 0) { @@ -1025,11 +1043,39 @@ } } - addCodeBlock(block) { - const lines = block.split('\n').length; + hasSelection() { + const from = this.code.getCursor('from'); + const to = this.code.getCursor('to'); + return from.line !== to.line || from.ch !== to.ch; + } - this.code.focus(); + internalAddSurroundCode(block) { + if(this.enhanced) { + if(this.hasSelection()) { + this.code.replaceSelection( + block.replace(/\{.*\}/, this.code.getSelection()), + 'end', + 'library' + ); + } else { + const cur = this.code.getCursor('head'); + this.code.replaceSelection(block, null, 'library'); + const end = {ch: cur.ch + block.length, line: cur.line}; + this.enterParams(cur, end, block); + } + } else { + const value = this.value(); + const s1 = this.code.element.selectionStart; + const s2 = this.code.element.selectionEnd; + const wrapped = block.replace(/\{.*\}/, value.substring(s1, s2)); + this.code + .val(value.substr(0, s1) + wrapped + value.substr(s2)) + .select(s1 + wrapped.length); + this.trigger('change'); + } + } + internalAddIndependentCode(block) { if(this.enhanced) { const cur = this.code.getCursor('head'); const pos = {ch: 0, line: cur.line + ((cur.ch > 0) ? 1 : 0)}; @@ -1038,6 +1084,7 @@ replaced = '\n' + replaced; } this.code.replaceRange(replaced, pos, null, 'library'); + const lines = block.split('\n').length; const end = {ch: 0, line: pos.line + lines}; this.enterParams(pos, end, block); } else { @@ -1055,6 +1102,16 @@ } } + addCodeBlock(block, surround = false) { + this.code.focus(); + + if(surround) { + this.internalAddSurroundCode(block); + } else { + this.internalAddIndependentCode(block); + } + } + value() { if(this.enhanced) { return this.code.getDoc().getValue(); @@ -1645,7 +1702,10 @@ .setClass('library-item') .add(holdInner) .fastClick() - .on('click', () => this.code.addCodeBlock(lib.code)) + .on('click', () => this.code.addCodeBlock( + lib.code, + lib.surround + )) .attach(container); return this.diagram.clone({ diff --git a/web/lib/editor.min.js b/web/lib/editor.min.js index 75306bf..cfbefa3 100644 --- a/web/lib/editor.min.js +++ b/web/lib/editor.min.js @@ -1 +1 @@ -!function(){"use strict";function e(){const e=window.location.hash;return e?e.substr(1):""}function t(e){return null===e?null:e.element?e.element:e}function i(e){return e.length>0&&"\n"!==e.charAt(e.length-1)?e+"\n":e}function s(e,t){let i=0,s=0;for(;;){const n=e.indexOf("\n",i)+1;if(ti.line||e.line===i.line&&e.ch>i.ch)}function r(e,t){const i=e.toString(),s=i.indexOf(".");return-1===s||i.length-s-1<=t?i:e.toFixed(t)}function o(e=null){return null!==e&&!Number.isNaN(e)}var l=[{code:"{Agent1} -> {Agent2}: {Message}",title:"Simple arrow (synchronous)"},{code:"{Agent1} --\x3e {Agent2}: {Message}",title:"Arrow with dotted line (response)"},{code:"{Agent1} ->> {Agent2}: {Message}",title:"Open arrow (asynchronous)"},{code:"{Agent1} -x {Agent2}: {Message}",title:"Lost message"},{code:"{Agent1} ~> {Agent2}: {Message}",title:"Wavy line"},{code:"{Agent1} -> {Agent1}: {Message}",title:"Self-connection"},{code:"{Agent1} -> ...{id}\n...{id} -> {Agent2}: {Message}",preview:"begin A, B\nA -> ...x\n...x -> B: Message",title:"Asynchronous message"},{code:"* -> {Agent1}: {Message}",title:"Found message"},{code:"{Agent1} -> {Agent2}\n& {Agent1} -> {Agent3}: {Broadcast}",title:"Broadcast message"},{code:"{Agent1} -> +{Agent2}: {Request}\n{Agent1} <-- -{Agent2}: {Response}",title:"Request/response pair"},{code:"{Agent1} -> *{Agent2}: {Request}\n{Agent1} <-- !{Agent2}: {Response}",title:"Inline agent creation / destruction"},{code:"{Agent1} -> {Agent2}: {Request}\n{Agent1} <-- {Agent2}: {Response}\nend {Agent2}",preview:"begin A\n::\nA -> B: Request\nA <-- B: Response\nend B",title:"Agent creation / destruction"},{code:'autolabel "[]