Automatically edit library parameters when selected [#35]

This commit is contained in:
David Evans 2018-05-09 07:57:25 +01:00
parent 5eb20c9e45
commit 44de7f27e2
4 changed files with 238 additions and 19 deletions

View File

@ -558,6 +558,8 @@
const DELAY_STAGECHANGE = 250; const DELAY_STAGECHANGE = 250;
const PNG_RESOLUTION = 4; const PNG_RESOLUTION = 4;
const PARAM_PATTERN = /\{[^}]+\}/g;
function addNewline(value) { function addNewline(value) {
if(value.length > 0 && value.charAt(value.length - 1) !== '\n') { if(value.length > 0 && value.charAt(value.length - 1) !== '\n') {
return value + '\n'; return value + '\n';
@ -578,6 +580,23 @@
} }
} }
function cmInRange(pos, {from, to}) {
return !(
pos.line < from.line || (pos.line === from.line && pos.ch < from.ch) ||
pos.line > to.line || (pos.line === to.line && pos.ch > to.ch)
);
}
function findNextToken(block, skip) {
PARAM_PATTERN.lastIndex = 0;
for(let m = null; (m = PARAM_PATTERN.exec(block));) {
if(!skip.includes(m[0])) {
return m[0];
}
}
return null;
}
function simplifyPreview(code) { function simplifyPreview(code) {
return 'headers fade\nterminators fade\n' + code return 'headers fade\nterminators fade\n' + code
.replace(/\{Agent([0-9]*)\}/g, (match, num) => { .replace(/\{Agent([0-9]*)\}/g, (match, num) => {
@ -775,7 +794,7 @@
const [touch] = e.touches; const [touch] = e.touches;
pt.x = touch.pageX; pt.x = touch.pageX;
pt.y = touch.pageY; pt.y = touch.pageY;
}) }, {passive: true})
.on('touchend', (e) => { .on('touchend', (e) => {
if( if(
pt.x === -1 || pt.x === -1 ||
@ -1158,7 +1177,7 @@
buildViewPane() { buildViewPane() {
this.viewPaneInner = this.dom.el('div').setClass('pane-view-inner') this.viewPaneInner = this.dom.el('div').setClass('pane-view-inner')
.add(this.diagram.dom()) .add(this.diagram.dom())
.on('touchstart', () => this._hideURLBuilder()) .on('touchstart', () => this._hideURLBuilder(), {passive: true})
.on('mousedown', () => this._hideURLBuilder()); .on('mousedown', () => this._hideURLBuilder());
this.errorMsg = this.dom.el('div').setClass('msg-error'); this.errorMsg = this.dom.el('div').setClass('msg-error');
@ -1269,19 +1288,107 @@
this._enhanceEditor(); this._enhanceEditor();
} }
enterParams(start, end, block) {
const doc = this.code.getDoc();
const endBookmark = doc.setBookmark(end);
const done = [];
const keydown = (cm, event) => {
switch(event.keyCode) {
case 13:
case 9:
event.preventDefault();
this.advanceParams();
break;
case 27:
event.preventDefault();
this.cancelParams();
break;
}
};
const move = () => {
if(this.paramMarkers.length === 0) {
return;
}
const m = this.paramMarkers[0].find();
const [r] = doc.listSelections();
if(!cmInRange(r.anchor, m) || !cmInRange(r.head, m)) {
this.cancelParams();
this.code.setSelection(r.anchor, r.head);
}
};
this.paramMarkers = [];
this.cancelParams = () => {
this.code.off('keydown', keydown);
this.code.off('cursorActivity', move);
this.paramMarkers.forEach((m) => m.clear());
this.paramMarkers = null;
endBookmark.clear();
this.code.setCursor(end);
this.cancelParams = null;
this.advanceParams = null;
};
this.advanceParams = () => {
this.paramMarkers.forEach((m) => m.clear());
this.paramMarkers.length = 0;
this.nextParams(start, endBookmark, block, done);
};
this.code.on('keydown', keydown);
this.code.on('cursorActivity', move);
this.advanceParams();
}
nextParams(start, endBookmark, block, done) {
const tok = findNextToken(block, done);
if(!tok) {
this.cancelParams();
return;
}
done.push(tok);
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);
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};
ranges.push({anchor, head});
this.paramMarkers.push(doc.markText(anchor, head, {
className: 'param',
clearWhenEmpty: false,
inclusiveLeft: true,
inclusiveRight: true,
}));
}
ch = 0;
}
if(ranges.length > 0) {
doc.setSelections(ranges, 0);
} else {
this.cancelParams();
}
}
addCodeBlock(block) { addCodeBlock(block) {
const lines = block.split('\n').length; const lines = block.split('\n').length;
if(this.code.getCursor) { if(this.code.getCursor) {
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)};
this.code.replaceRange( let replaced = addNewline(block);
addNewline(block), if(pos.line >= this.code.lineCount()) {
pos, replaced = '\n' + replaced;
null, }
'library' this.code.replaceRange(replaced, pos, null, 'library');
); const end = {ch: 0, line: pos.line + lines};
this.code.setCursor({ch: 0, line: pos.line + lines}); this.enterParams(pos, end, block);
} else { } else {
const value = this.value(); const value = this.value();
const cur = this.code.element.selectionStart; const cur = this.code.element.selectionStart;

File diff suppressed because one or more lines are too long

View File

@ -6,6 +6,8 @@ const DELAY_AGENTCHANGE = 500;
const DELAY_STAGECHANGE = 250; const DELAY_STAGECHANGE = 250;
const PNG_RESOLUTION = 4; const PNG_RESOLUTION = 4;
const PARAM_PATTERN = /\{[^}]+\}/g;
function addNewline(value) { function addNewline(value) {
if(value.length > 0 && value.charAt(value.length - 1) !== '\n') { if(value.length > 0 && value.charAt(value.length - 1) !== '\n') {
return value + '\n'; return value + '\n';
@ -26,6 +28,23 @@ function findPos(content, index) {
} }
} }
function cmInRange(pos, {from, to}) {
return !(
pos.line < from.line || (pos.line === from.line && pos.ch < from.ch) ||
pos.line > to.line || (pos.line === to.line && pos.ch > to.ch)
);
}
function findNextToken(block, skip) {
PARAM_PATTERN.lastIndex = 0;
for(let m = null; (m = PARAM_PATTERN.exec(block));) {
if(!skip.includes(m[0])) {
return m[0];
}
}
return null;
}
function simplifyPreview(code) { function simplifyPreview(code) {
return 'headers fade\nterminators fade\n' + code return 'headers fade\nterminators fade\n' + code
.replace(/\{Agent([0-9]*)\}/g, (match, num) => { .replace(/\{Agent([0-9]*)\}/g, (match, num) => {
@ -223,7 +242,7 @@ DOMWrapper.WrappedElement.prototype.fastClick = function() {
const [touch] = e.touches; const [touch] = e.touches;
pt.x = touch.pageX; pt.x = touch.pageX;
pt.y = touch.pageY; pt.y = touch.pageY;
}) }, {passive: true})
.on('touchend', (e) => { .on('touchend', (e) => {
if( if(
pt.x === -1 || pt.x === -1 ||
@ -606,7 +625,7 @@ export default class Interface {
buildViewPane() { buildViewPane() {
this.viewPaneInner = this.dom.el('div').setClass('pane-view-inner') this.viewPaneInner = this.dom.el('div').setClass('pane-view-inner')
.add(this.diagram.dom()) .add(this.diagram.dom())
.on('touchstart', () => this._hideURLBuilder()) .on('touchstart', () => this._hideURLBuilder(), {passive: true})
.on('mousedown', () => this._hideURLBuilder()); .on('mousedown', () => this._hideURLBuilder());
this.errorMsg = this.dom.el('div').setClass('msg-error'); this.errorMsg = this.dom.el('div').setClass('msg-error');
@ -717,19 +736,107 @@ export default class Interface {
this._enhanceEditor(); this._enhanceEditor();
} }
enterParams(start, end, block) {
const doc = this.code.getDoc();
const endBookmark = doc.setBookmark(end);
const done = [];
const keydown = (cm, event) => {
switch(event.keyCode) {
case 13:
case 9:
event.preventDefault();
this.advanceParams();
break;
case 27:
event.preventDefault();
this.cancelParams();
break;
}
};
const move = () => {
if(this.paramMarkers.length === 0) {
return;
}
const m = this.paramMarkers[0].find();
const [r] = doc.listSelections();
if(!cmInRange(r.anchor, m) || !cmInRange(r.head, m)) {
this.cancelParams();
this.code.setSelection(r.anchor, r.head);
}
};
this.paramMarkers = [];
this.cancelParams = () => {
this.code.off('keydown', keydown);
this.code.off('cursorActivity', move);
this.paramMarkers.forEach((m) => m.clear());
this.paramMarkers = null;
endBookmark.clear();
this.code.setCursor(end);
this.cancelParams = null;
this.advanceParams = null;
};
this.advanceParams = () => {
this.paramMarkers.forEach((m) => m.clear());
this.paramMarkers.length = 0;
this.nextParams(start, endBookmark, block, done);
};
this.code.on('keydown', keydown);
this.code.on('cursorActivity', move);
this.advanceParams();
}
nextParams(start, endBookmark, block, done) {
const tok = findNextToken(block, done);
if(!tok) {
this.cancelParams();
return;
}
done.push(tok);
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);
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};
ranges.push({anchor, head});
this.paramMarkers.push(doc.markText(anchor, head, {
className: 'param',
clearWhenEmpty: false,
inclusiveLeft: true,
inclusiveRight: true,
}));
}
ch = 0;
}
if(ranges.length > 0) {
doc.setSelections(ranges, 0);
} else {
this.cancelParams();
}
}
addCodeBlock(block) { addCodeBlock(block) {
const lines = block.split('\n').length; const lines = block.split('\n').length;
if(this.code.getCursor) { if(this.code.getCursor) {
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)};
this.code.replaceRange( let replaced = addNewline(block);
addNewline(block), if(pos.line >= this.code.lineCount()) {
pos, replaced = '\n' + replaced;
null, }
'library' this.code.replaceRange(replaced, pos, null, 'library');
); const end = {ch: 0, line: pos.line + lines};
this.code.setCursor({ch: 0, line: pos.line + lines}); this.enterParams(pos, end, block);
} else { } else {
const value = this.value(); const value = this.value();
const cur = this.code.element.selectionStart; const cur = this.code.element.selectionStart;

View File

@ -148,6 +148,11 @@ html, body {
background: rgba(255, 0, 0, 0.2); background: rgba(255, 0, 0, 0.2);
} }
.param {
border: 1px solid #CC0000;
margin: -1px;
}
.hover { .hover {
background: #FFFF00; background: #FFFF00;
} }