Add lost message arrow style [#38]

This commit is contained in:
David Evans 2018-01-15 22:37:27 +00:00
parent 0c988e8658
commit afd505b8fe
16 changed files with 222 additions and 22 deletions

View File

@ -60,6 +60,8 @@ Bar -> Bar: Bar talks to itself
Foo -> +Bar: Foo asks Bar
-Bar --> Foo: and Bar replies
Bar -x Baz: Lost message
# Arrows leaving on the left and right of the diagram
[ -> Foo: From the left
[ <- Foo: To the left

View File

@ -1053,8 +1053,11 @@ define('sequence/CodeMirrorMode',['core/ArrayUtilities'], (array) => {
});
}
_tokenEndFound(stream, state, block) {
_tokenEndFound(stream, state, block, match) {
state.currentType = -1;
if(block.includeEnd) {
state.current += match[0];
}
if(block.omit) {
return 'comment';
}
@ -1077,8 +1080,13 @@ define('sequence/CodeMirrorMode',['core/ArrayUtilities'], (array) => {
while(true) {
const block = this.tokenDefinitions[state.currentType];
this._tokenCheckEscape(stream, state, block);
if(!block.end || this._matchPattern(stream, block.end, true)) {
return this._tokenEndFound(stream, state, block);
if(!block.end) {
return this._tokenEndFound(stream, state, block, null);
} else {
const match = this._matchPattern(stream, block.end, true);
if(match) {
return this._tokenEndFound(stream, state, block, match);
}
}
if(stream.eol()) {
return this._tokenEOLFound(stream, state, block);
@ -1143,7 +1151,11 @@ define('sequence/Tokeniser',['./CodeMirrorMode'], (CMMode) => {
baseToken: {q: true},
},
{start: /(?=[^ \t\r\n:+\-~*!<>,])/y, end: /(?=[ \t\r\n:+\-~*!<>,])|$/y},
{start: /(?=[\-~<>])/y, end: /(?=[^\-~<>])|$/y},
{
start: /(?=[\-~<])/y,
end: /(?=[^\-~<>x])|[\-~]x|[<>](?=x)|$/y,
includeEnd: true,
},
{start: /,/y, baseToken: {v: ','}},
{start: /:/y, baseToken: {v: ':'}},
{start: /!/y, baseToken: {v: '!'}},
@ -1194,7 +1206,7 @@ define('sequence/Tokeniser',['./CodeMirrorMode'], (CMMode) => {
newBlock: null,
end: true,
appendSpace: '',
appendValue: '',
appendValue: block.includeEnd ? match[0] : '',
skip: match[0].length,
};
}
@ -1565,6 +1577,7 @@ define('sequence/Parser',[
{tok: '', type: 0},
{tok: '>', type: 1},
{tok: '>>', type: 2},
{tok: 'x', type: 3},
];
const arrows = (array.combine([lTypes, mTypes, rTypes])
.filter((arrow) => (arrow[0].type !== 0 || arrow[2].type !== 0))
@ -4264,6 +4277,34 @@ define('sequence/components/Connect',[
}
}
class Arrowcross {
getConfig(theme) {
return theme.connect.arrow.cross;
}
render(layer, theme, pt, dir) {
const config = this.getConfig(theme);
layer.appendChild(config.render({
x: pt.x + config.short * dir,
y: pt.y,
radius: config.radius,
}));
}
width(theme) {
const config = this.getConfig(theme);
return config.short + config.radius;
}
height(theme) {
return this.getConfig(theme).radius * 2;
}
lineGap(theme) {
return this.getConfig(theme).short;
}
}
const ARROWHEADS = [
{
render: () => {},
@ -4273,6 +4314,7 @@ define('sequence/components/Connect',[
},
new Arrowhead('single'),
new Arrowhead('double'),
new Arrowcross(),
];
class Connect extends BaseComponent {
@ -5839,6 +5881,15 @@ define('sequence/themes/Basic',[
'stroke-linejoin': 'miter',
},
},
'cross': {
short: 7,
radius: 3,
render: BaseTheme.renderCross.bind(null, {
'fill': 'none',
'stroke': '#000000',
'stroke-width': 1,
}),
},
},
label: {
padding: 6,
@ -6151,6 +6202,15 @@ define('sequence/themes/Monospace',[
'stroke-linejoin': 'miter',
},
},
'cross': {
short: 8,
radius: 4,
render: BaseTheme.renderCross.bind(null, {
'fill': 'none',
'stroke': '#000000',
'stroke-width': 1,
}),
},
},
label: {
padding: 4,
@ -6437,7 +6497,7 @@ define('sequence/themes/Chunky',[
},
},
arrow: {
single: {
'single': {
width: 10,
height: 12,
render: BaseTheme.renderHorizArrowHead,
@ -6448,7 +6508,7 @@ define('sequence/themes/Chunky',[
'stroke-linejoin': 'round',
},
},
double: {
'double': {
width: 10,
height: 12,
render: BaseTheme.renderHorizArrowHead,
@ -6460,6 +6520,17 @@ define('sequence/themes/Chunky',[
'stroke-linecap': 'round',
},
},
'cross': {
short: 10,
radius: 5,
render: BaseTheme.renderCross.bind(null, {
'fill': 'none',
'stroke': '#000000',
'stroke-width': 3,
'stroke-linejoin': 'round',
'stroke-linecap': 'round',
}),
},
},
label: {
padding: 7,
@ -7195,6 +7266,11 @@ define('sequence/themes/Sketch',[
}, PENCIL),
render: null,
},
'cross': {
short: 5,
radius: 3,
render: null,
},
},
label: {
padding: 6,
@ -7432,6 +7508,7 @@ define('sequence/themes/Sketch',[
this.connect.arrow.single.render = this.renderArrowHead;
this.connect.arrow.double.render = this.renderArrowHead;
this.connect.arrow.cross.render = this.renderCross.bind(this);
this.connect.line.solid.renderFlat = this.renderFlatConnector;
this.connect.line.solid.renderRev = this.renderRevConnector;

File diff suppressed because one or more lines are too long

View File

@ -174,6 +174,8 @@ Bar -> Bar: Bar talks to itself
Foo -> +Bar: Foo asks Bar
-Bar --> Foo: and Bar replies
Bar -x Baz: Lost message
# Arrows leaving on the left and right of the diagram
[ -> Foo: From the left
[ <- Foo: To the left

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 62 KiB

View File

@ -33,17 +33,21 @@
);
const library = [
{
title: 'Simple arrow',
title: 'Simple arrow (synchronous)',
code: '{Agent1} -> {Agent2}: {Message}',
},
{
title: 'Arrow with dotted line',
title: 'Arrow with dotted line (response)',
code: '{Agent1} --> {Agent2}: {Message}',
},
{
title: 'Open arrow',
title: 'Open arrow (asynchronous)',
code: '{Agent1} ->> {Agent2}: {Message}',
},
{
title: 'Lost message',
code: '{Agent1} -x {Agent2}: {Message}',
},
{
title: 'Wavy line',
code: '{Agent1} ~> {Agent2}: {Message}',
@ -114,7 +118,7 @@
),
},
{
title: 'Repeated blocks',
title: 'Repeated block',
code: (
'repeat {Condition}\n' +
' {Agent1} -> {Agent2}\n' +
@ -128,7 +132,7 @@
),
},
{
title: 'References',
title: 'Reference',
code: (
'begin reference: {Label} as {Name}\n' +
'{Agent1} -> {Name}\n' +
@ -143,7 +147,7 @@
),
},
{
title: 'References over agents',
title: 'Reference over agents',
code: (
'begin reference over {Covered}: {Label} as {Name}\n' +
'{Agent1} -> {Name}\n' +

View File

@ -450,8 +450,11 @@ define(['core/ArrayUtilities'], (array) => {
});
}
_tokenEndFound(stream, state, block) {
_tokenEndFound(stream, state, block, match) {
state.currentType = -1;
if(block.includeEnd) {
state.current += match[0];
}
if(block.omit) {
return 'comment';
}
@ -474,8 +477,13 @@ define(['core/ArrayUtilities'], (array) => {
while(true) {
const block = this.tokenDefinitions[state.currentType];
this._tokenCheckEscape(stream, state, block);
if(!block.end || this._matchPattern(stream, block.end, true)) {
return this._tokenEndFound(stream, state, block);
if(!block.end) {
return this._tokenEndFound(stream, state, block, null);
} else {
const match = this._matchPattern(stream, block.end, true);
if(match) {
return this._tokenEndFound(stream, state, block, match);
}
}
if(stream.eol()) {
return this._tokenEOLFound(stream, state, block);

View File

@ -47,6 +47,7 @@ define([
{tok: '', type: 0},
{tok: '>', type: 1},
{tok: '>>', type: 2},
{tok: 'x', type: 3},
];
const arrows = (array.combine([lTypes, mTypes, rTypes])
.filter((arrow) => (arrow[0].type !== 0 || arrow[2].type !== 0))

View File

@ -219,6 +219,7 @@ defineDescribe('Sequence Parser', ['./Parser'], (Parser) => {
'A<<-B\n' +
'A<<->B\n' +
'A<<->>B\n' +
'A-xB\n' +
'A-->B\n' +
'A-->>B\n' +
'A<--B\n' +
@ -227,6 +228,7 @@ defineDescribe('Sequence Parser', ['./Parser'], (Parser) => {
'A<<--B\n' +
'A<<-->B\n' +
'A<<-->>B\n' +
'A--xB\n' +
'A~>B\n' +
'A~>>B\n' +
'A<~B\n' +
@ -234,7 +236,8 @@ defineDescribe('Sequence Parser', ['./Parser'], (Parser) => {
'A<~>>B\n' +
'A<<~B\n' +
'A<<~>B\n' +
'A<<~>>B\n'
'A<<~>>B\n' +
'A~xB\n'
);
expect(parsed.stages).toEqual([
PARSED.connect(['A', 'B'], {
@ -250,6 +253,7 @@ defineDescribe('Sequence Parser', ['./Parser'], (Parser) => {
PARSED.connect(['A', 'B'], {line: 'solid', left: 2, right: 0}),
PARSED.connect(['A', 'B'], {line: 'solid', left: 2, right: 1}),
PARSED.connect(['A', 'B'], {line: 'solid', left: 2, right: 2}),
PARSED.connect(['A', 'B'], {line: 'solid', left: 0, right: 3}),
PARSED.connect(['A', 'B'], {line: 'dash', left: 0, right: 1}),
PARSED.connect(['A', 'B'], {line: 'dash', left: 0, right: 2}),
PARSED.connect(['A', 'B'], {line: 'dash', left: 1, right: 0}),
@ -258,6 +262,7 @@ defineDescribe('Sequence Parser', ['./Parser'], (Parser) => {
PARSED.connect(['A', 'B'], {line: 'dash', left: 2, right: 0}),
PARSED.connect(['A', 'B'], {line: 'dash', left: 2, right: 1}),
PARSED.connect(['A', 'B'], {line: 'dash', left: 2, right: 2}),
PARSED.connect(['A', 'B'], {line: 'dash', left: 0, right: 3}),
PARSED.connect(['A', 'B'], {line: 'wave', left: 0, right: 1}),
PARSED.connect(['A', 'B'], {line: 'wave', left: 0, right: 2}),
PARSED.connect(['A', 'B'], {line: 'wave', left: 1, right: 0}),
@ -266,6 +271,7 @@ defineDescribe('Sequence Parser', ['./Parser'], (Parser) => {
PARSED.connect(['A', 'B'], {line: 'wave', left: 2, right: 0}),
PARSED.connect(['A', 'B'], {line: 'wave', left: 2, right: 1}),
PARSED.connect(['A', 'B'], {line: 'wave', left: 2, right: 2}),
PARSED.connect(['A', 'B'], {line: 'wave', left: 0, right: 3}),
]);
});

View File

@ -32,7 +32,11 @@ define(['./CodeMirrorMode'], (CMMode) => {
baseToken: {q: true},
},
{start: /(?=[^ \t\r\n:+\-~*!<>,])/y, end: /(?=[ \t\r\n:+\-~*!<>,])|$/y},
{start: /(?=[\-~<>])/y, end: /(?=[^\-~<>])|$/y},
{
start: /(?=[\-~<])/y,
end: /(?=[^\-~<>x])|[\-~]x|[<>](?=x)|$/y,
includeEnd: true,
},
{start: /,/y, baseToken: {v: ','}},
{start: /:/y, baseToken: {v: ':'}},
{start: /!/y, baseToken: {v: '!'}},
@ -83,7 +87,7 @@ define(['./CodeMirrorMode'], (CMMode) => {
newBlock: null,
end: true,
appendSpace: '',
appendValue: '',
appendValue: block.includeEnd ? match[0] : '',
skip: match[0].length,
};
}

View File

@ -174,6 +174,38 @@ defineDescribe('Sequence Tokeniser', ['./Tokeniser'], (Tokeniser) => {
'Unterminated literal (began at line 1, character 0)'
));
});
it('stops tokenising arrows once they become invalid', () => {
expect(tokeniser.tokenise('foo -> bar')).toEqual([
token({s: '', v: 'foo'}),
token({s: ' ', v: '->'}),
token({s: ' ', v: 'bar'}),
]);
expect(tokeniser.tokenise('foo->bar')).toEqual([
token({s: '', v: 'foo'}),
token({s: '', v: '->'}),
token({s: '', v: 'bar'}),
]);
expect(tokeniser.tokenise('foo-xbar')).toEqual([
token({s: '', v: 'foo'}),
token({s: '', v: '-x'}),
token({s: '', v: 'bar'}),
]);
expect(tokeniser.tokenise('foo-xxyz')).toEqual([
token({s: '', v: 'foo'}),
token({s: '', v: '-x'}),
token({s: '', v: 'xyz'}),
]);
expect(tokeniser.tokenise('foo->xyz')).toEqual([
token({s: '', v: 'foo'}),
token({s: '', v: '->'}),
token({s: '', v: 'xyz'}),
]);
});
});
describe('.splitLines', () => {

View File

@ -65,6 +65,34 @@ define([
}
}
class Arrowcross {
getConfig(theme) {
return theme.connect.arrow.cross;
}
render(layer, theme, pt, dir) {
const config = this.getConfig(theme);
layer.appendChild(config.render({
x: pt.x + config.short * dir,
y: pt.y,
radius: config.radius,
}));
}
width(theme) {
const config = this.getConfig(theme);
return config.short + config.radius;
}
height(theme) {
return this.getConfig(theme).radius * 2;
}
lineGap(theme) {
return this.getConfig(theme).short;
}
}
const ARROWHEADS = [
{
render: () => {},
@ -74,6 +102,7 @@ define([
},
new Arrowhead('single'),
new Arrowhead('double'),
new Arrowcross(),
];
class Connect extends BaseComponent {

View File

@ -125,6 +125,15 @@ define([
'stroke-linejoin': 'miter',
},
},
'cross': {
short: 7,
radius: 3,
render: BaseTheme.renderCross.bind(null, {
'fill': 'none',
'stroke': '#000000',
'stroke-width': 1,
}),
},
},
label: {
padding: 6,

View File

@ -110,7 +110,7 @@ define([
},
},
arrow: {
single: {
'single': {
width: 10,
height: 12,
render: BaseTheme.renderHorizArrowHead,
@ -121,7 +121,7 @@ define([
'stroke-linejoin': 'round',
},
},
double: {
'double': {
width: 10,
height: 12,
render: BaseTheme.renderHorizArrowHead,
@ -133,6 +133,17 @@ define([
'stroke-linecap': 'round',
},
},
'cross': {
short: 10,
radius: 5,
render: BaseTheme.renderCross.bind(null, {
'fill': 'none',
'stroke': '#000000',
'stroke-width': 3,
'stroke-linejoin': 'round',
'stroke-linecap': 'round',
}),
},
},
label: {
padding: 7,

View File

@ -132,6 +132,15 @@ define([
'stroke-linejoin': 'miter',
},
},
'cross': {
short: 8,
radius: 4,
render: BaseTheme.renderCross.bind(null, {
'fill': 'none',
'stroke': '#000000',
'stroke-width': 1,
}),
},
},
label: {
padding: 4,

View File

@ -118,6 +118,11 @@ define([
}, PENCIL),
render: null,
},
'cross': {
short: 5,
radius: 3,
render: null,
},
},
label: {
padding: 6,
@ -355,6 +360,7 @@ define([
this.connect.arrow.single.render = this.renderArrowHead;
this.connect.arrow.double.render = this.renderArrowHead;
this.connect.arrow.cross.render = this.renderCross.bind(this);
this.connect.line.solid.renderFlat = this.renderFlatConnector;
this.connect.line.solid.renderRev = this.renderRevConnector;