Improve behaviour when adding formatting elements from library

This commit is contained in:
David Evans 2020-01-20 21:30:23 +00:00
parent ed1110d74f
commit d103dc0574
5 changed files with 143 additions and 23 deletions

View File

@ -253,51 +253,61 @@
{ {
code: '**{text}**', code: '**{text}**',
preview: 'A -> B: **bold**', preview: 'A -> B: **bold**',
surround: true,
title: 'Bold markdown', title: 'Bold markdown',
}, },
{ {
code: '_{text}_', code: '_{text}_',
preview: 'A -> B: _italic_', preview: 'A -> B: _italic_',
surround: true,
title: 'Italic markdown', title: 'Italic markdown',
}, },
{ {
code: '~{text}~', code: '~{text}~',
preview: 'A -> B: ~strikeout~', preview: 'A -> B: ~strikeout~',
surround: true,
title: 'Strikeout markdown', title: 'Strikeout markdown',
}, },
{ {
code: '<u>{text}</u>', code: '<u>{text}</u>',
preview: 'A -> B: <u>underline</u>', preview: 'A -> B: <u>underline</u>',
surround: true,
title: 'Underline markdown', title: 'Underline markdown',
}, },
{ {
code: '<o>{text}</o>', code: '<o>{text}</o>',
preview: 'A -> B: <o>overline</o>', preview: 'A -> B: <o>overline</o>',
surround: true,
title: 'Overline markdown', title: 'Overline markdown',
}, },
{ {
code: '<sup>{text}</sup>', code: '<sup>{text}</sup>',
preview: 'A -> B: super<sup>script</sup>', preview: 'A -> B: super<sup>script</sup>',
surround: true,
title: 'Superscript markdown', title: 'Superscript markdown',
}, },
{ {
code: '<sub>{text}</sub>', code: '<sub>{text}</sub>',
preview: 'A -> B: sub<sub>script</sub>', preview: 'A -> B: sub<sub>script</sub>',
surround: true,
title: 'Subscript markdown', title: 'Subscript markdown',
}, },
{ {
code: '`{text}`', code: '`{text}`',
preview: 'A -> B: `mono`', preview: 'A -> B: `mono`',
surround: true,
title: 'Monospace markdown', title: 'Monospace markdown',
}, },
{ {
code: '<red>{text}</red>', code: '<red>{text}</red>',
preview: 'A -> B: <red>red</red>', preview: 'A -> B: <red>red</red>',
surround: true,
title: 'Red markdown', title: 'Red markdown',
}, },
{ {
code: '<highlight>{text}</highlight>', code: '<highlight>{text}</highlight>',
preview: 'A -> B: <highlight>highlight</highlight>', preview: 'A -> B: <highlight>highlight</highlight>',
surround: true,
title: 'Highlight markdown', title: 'Highlight markdown',
}, },
{ {
@ -870,6 +880,15 @@
return null; 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 { class CodeEditor extends EventObject {
constructor(dom, container, { constructor(dom, container, {
mode = '', mode = '',
@ -974,8 +993,8 @@
this.code.off('cursorActivity', move); this.code.off('cursorActivity', move);
this.paramMarkers.forEach((m) => m.clear()); this.paramMarkers.forEach((m) => m.clear());
this.paramMarkers = null; this.paramMarkers = null;
this.code.setCursor(endBookmark.find());
endBookmark.clear(); endBookmark.clear();
this.code.setCursor(end);
this.cancelParams = null; this.cancelParams = null;
this.advanceParams = null; this.advanceParams = null;
}; };
@ -1001,12 +1020,12 @@
const doc = this.code.getDoc(); const doc = this.code.getDoc();
const ranges = []; const ranges = [];
let {ch} = start; const end = endBookmark.find();
for(let ln = start.line; ln < endBookmark.find().line; ++ ln) { for(let ln = start.line; ln <= end.line; ++ ln) {
const line = doc.getLine(ln).slice(ch); const {chOffset, line} = lineWithinRange(doc, ln, start, end);
for(let p = 0; (p = line.indexOf(tok, p)) !== -1; p += tok.length) { for(let p = 0; (p = line.indexOf(tok, p)) !== -1; p += tok.length) {
const anchor = {ch: p, line: ln}; const anchor = {ch: chOffset + p, line: ln};
const head = {ch: p + tok.length, line: ln}; const head = {ch: chOffset + p + tok.length, line: ln};
ranges.push({anchor, head}); ranges.push({anchor, head});
this.paramMarkers.push(doc.markText(anchor, head, { this.paramMarkers.push(doc.markText(anchor, head, {
className: 'param', className: 'param',
@ -1015,7 +1034,6 @@
inclusiveRight: true, inclusiveRight: true,
})); }));
} }
ch = 0;
} }
if(ranges.length > 0) { if(ranges.length > 0) {
@ -1025,11 +1043,39 @@
} }
} }
addCodeBlock(block) { hasSelection() {
const lines = block.split('\n').length; 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) { if(this.enhanced) {
const cur = this.code.getCursor('head'); const cur = this.code.getCursor('head');
const pos = {ch: 0, line: cur.line + ((cur.ch > 0) ? 1 : 0)}; const pos = {ch: 0, line: cur.line + ((cur.ch > 0) ? 1 : 0)};
@ -1038,6 +1084,7 @@
replaced = '\n' + replaced; replaced = '\n' + replaced;
} }
this.code.replaceRange(replaced, pos, null, 'library'); this.code.replaceRange(replaced, pos, null, 'library');
const lines = block.split('\n').length;
const end = {ch: 0, line: pos.line + lines}; const end = {ch: 0, line: pos.line + lines};
this.enterParams(pos, end, block); this.enterParams(pos, end, block);
} else { } else {
@ -1055,6 +1102,16 @@
} }
} }
addCodeBlock(block, surround = false) {
this.code.focus();
if(surround) {
this.internalAddSurroundCode(block);
} else {
this.internalAddIndependentCode(block);
}
}
value() { value() {
if(this.enhanced) { if(this.enhanced) {
return this.code.getDoc().getValue(); return this.code.getDoc().getValue();
@ -1645,7 +1702,10 @@
.setClass('library-item') .setClass('library-item')
.add(holdInner) .add(holdInner)
.fastClick() .fastClick()
.on('click', () => this.code.addCodeBlock(lib.code)) .on('click', () => this.code.addCodeBlock(
lib.code,
lib.surround
))
.attach(container); .attach(container);
return this.diagram.clone({ return this.diagram.clone({

File diff suppressed because one or more lines are too long

View File

@ -39,6 +39,15 @@ function findNextToken(block, skip) {
return null; 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),
};
}
export default class CodeEditor extends EventObject { export default class CodeEditor extends EventObject {
constructor(dom, container, { constructor(dom, container, {
mode = '', mode = '',
@ -143,8 +152,8 @@ export default class CodeEditor extends EventObject {
this.code.off('cursorActivity', move); this.code.off('cursorActivity', move);
this.paramMarkers.forEach((m) => m.clear()); this.paramMarkers.forEach((m) => m.clear());
this.paramMarkers = null; this.paramMarkers = null;
this.code.setCursor(endBookmark.find());
endBookmark.clear(); endBookmark.clear();
this.code.setCursor(end);
this.cancelParams = null; this.cancelParams = null;
this.advanceParams = null; this.advanceParams = null;
}; };
@ -170,12 +179,12 @@ export default class CodeEditor extends EventObject {
const doc = this.code.getDoc(); const doc = this.code.getDoc();
const ranges = []; const ranges = [];
let {ch} = start; const end = endBookmark.find();
for(let ln = start.line; ln < endBookmark.find().line; ++ ln) { for(let ln = start.line; ln <= end.line; ++ ln) {
const line = doc.getLine(ln).slice(ch); const {chOffset, line} = lineWithinRange(doc, ln, start, end);
for(let p = 0; (p = line.indexOf(tok, p)) !== -1; p += tok.length) { for(let p = 0; (p = line.indexOf(tok, p)) !== -1; p += tok.length) {
const anchor = {ch: p, line: ln}; const anchor = {ch: chOffset + p, line: ln};
const head = {ch: p + tok.length, line: ln}; const head = {ch: chOffset + p + tok.length, line: ln};
ranges.push({anchor, head}); ranges.push({anchor, head});
this.paramMarkers.push(doc.markText(anchor, head, { this.paramMarkers.push(doc.markText(anchor, head, {
className: 'param', className: 'param',
@ -184,7 +193,6 @@ export default class CodeEditor extends EventObject {
inclusiveRight: true, inclusiveRight: true,
})); }));
} }
ch = 0;
} }
if(ranges.length > 0) { if(ranges.length > 0) {
@ -194,11 +202,39 @@ export default class CodeEditor extends EventObject {
} }
} }
addCodeBlock(block) { hasSelection() {
const lines = block.split('\n').length; 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) { if(this.enhanced) {
const cur = this.code.getCursor('head'); const cur = this.code.getCursor('head');
const pos = {ch: 0, line: cur.line + ((cur.ch > 0) ? 1 : 0)}; const pos = {ch: 0, line: cur.line + ((cur.ch > 0) ? 1 : 0)};
@ -207,6 +243,7 @@ export default class CodeEditor extends EventObject {
replaced = '\n' + replaced; replaced = '\n' + replaced;
} }
this.code.replaceRange(replaced, pos, null, 'library'); this.code.replaceRange(replaced, pos, null, 'library');
const lines = block.split('\n').length;
const end = {ch: 0, line: pos.line + lines}; const end = {ch: 0, line: pos.line + lines};
this.enterParams(pos, end, block); this.enterParams(pos, end, block);
} else { } else {
@ -224,6 +261,16 @@ export default class CodeEditor extends EventObject {
} }
} }
addCodeBlock(block, surround = false) {
this.code.focus();
if(surround) {
this.internalAddSurroundCode(block);
} else {
this.internalAddIndependentCode(block);
}
}
value() { value() {
if(this.enhanced) { if(this.enhanced) {
return this.code.getDoc().getValue(); return this.code.getDoc().getValue();

View File

@ -250,51 +250,61 @@ export default [
{ {
code: '**{text}**', code: '**{text}**',
preview: 'A -> B: **bold**', preview: 'A -> B: **bold**',
surround: true,
title: 'Bold markdown', title: 'Bold markdown',
}, },
{ {
code: '_{text}_', code: '_{text}_',
preview: 'A -> B: _italic_', preview: 'A -> B: _italic_',
surround: true,
title: 'Italic markdown', title: 'Italic markdown',
}, },
{ {
code: '~{text}~', code: '~{text}~',
preview: 'A -> B: ~strikeout~', preview: 'A -> B: ~strikeout~',
surround: true,
title: 'Strikeout markdown', title: 'Strikeout markdown',
}, },
{ {
code: '<u>{text}</u>', code: '<u>{text}</u>',
preview: 'A -> B: <u>underline</u>', preview: 'A -> B: <u>underline</u>',
surround: true,
title: 'Underline markdown', title: 'Underline markdown',
}, },
{ {
code: '<o>{text}</o>', code: '<o>{text}</o>',
preview: 'A -> B: <o>overline</o>', preview: 'A -> B: <o>overline</o>',
surround: true,
title: 'Overline markdown', title: 'Overline markdown',
}, },
{ {
code: '<sup>{text}</sup>', code: '<sup>{text}</sup>',
preview: 'A -> B: super<sup>script</sup>', preview: 'A -> B: super<sup>script</sup>',
surround: true,
title: 'Superscript markdown', title: 'Superscript markdown',
}, },
{ {
code: '<sub>{text}</sub>', code: '<sub>{text}</sub>',
preview: 'A -> B: sub<sub>script</sub>', preview: 'A -> B: sub<sub>script</sub>',
surround: true,
title: 'Subscript markdown', title: 'Subscript markdown',
}, },
{ {
code: '`{text}`', code: '`{text}`',
preview: 'A -> B: `mono`', preview: 'A -> B: `mono`',
surround: true,
title: 'Monospace markdown', title: 'Monospace markdown',
}, },
{ {
code: '<red>{text}</red>', code: '<red>{text}</red>',
preview: 'A -> B: <red>red</red>', preview: 'A -> B: <red>red</red>',
surround: true,
title: 'Red markdown', title: 'Red markdown',
}, },
{ {
code: '<highlight>{text}</highlight>', code: '<highlight>{text}</highlight>',
preview: 'A -> B: <highlight>highlight</highlight>', preview: 'A -> B: <highlight>highlight</highlight>',
surround: true,
title: 'Highlight markdown', title: 'Highlight markdown',
}, },
{ {

View File

@ -381,7 +381,10 @@ export default class Interface {
.setClass('library-item') .setClass('library-item')
.add(holdInner) .add(holdInner)
.fastClick() .fastClick()
.on('click', () => this.code.addCodeBlock(lib.code)) .on('click', () => this.code.addCodeBlock(
lib.code,
lib.surround
))
.attach(container); .attach(container);
return this.diagram.clone({ return this.diagram.clone({