Automatically edit library parameters when selected [#35]
This commit is contained in:
parent
5eb20c9e45
commit
44de7f27e2
|
@ -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
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue