Linter fixes
This commit is contained in:
parent
2e708ebb4d
commit
c58b8f7a22
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
@ -1,12 +1,18 @@
|
|||
/* eslint-disable max-lines */
|
||||
/* eslint-disable sort-keys */ // Maybe later
|
||||
/* eslint-disable complexity */ // Temporary ignore while switching linter
|
||||
/*
|
||||
* The order of commands inside "then" blocks directly influences the
|
||||
* order they are displayed to the user in autocomplete menus.
|
||||
* This relies on the fact that common JS engines maintain insertion
|
||||
* order in objects, though this is not guaranteed. It could be switched
|
||||
* to use Map objects instead for strict compliance, at the cost of
|
||||
* extra syntax.
|
||||
*/
|
||||
/* eslint-disable sort-keys */
|
||||
|
||||
import {flatMap, last, mergeSets} from '../../core/ArrayUtilities.mjs';
|
||||
|
||||
const CM_ERROR = {type: 'error line-error', suggest: false, then: {'': 0}};
|
||||
const CM_ERROR = {type: 'error line-error', suggest: [], then: {'': 0}};
|
||||
|
||||
function textTo(exit, suggest = false) {
|
||||
function textTo(exit, suggest = []) {
|
||||
return {
|
||||
type: 'string',
|
||||
suggest,
|
||||
|
@ -29,19 +35,10 @@ const AGENT_INFO_TYPES = [
|
|||
];
|
||||
|
||||
const makeCommands = ((() => {
|
||||
/*
|
||||
* The order of commands inside "then" blocks directly influences the
|
||||
* order they are displayed to the user in autocomplete menus.
|
||||
* This relies on the fact that common JS engines maintain insertion
|
||||
* order in objects, though this is not guaranteed. It could be switched
|
||||
* to use Map objects instead for strict compliance, at the cost of
|
||||
* extra syntax.
|
||||
*/
|
||||
|
||||
function agentListTo(exit, next = 1) {
|
||||
return {
|
||||
type: 'variable',
|
||||
suggest: {known: 'Agent'},
|
||||
suggest: [{known: 'Agent'}],
|
||||
then: Object.assign({}, exit, {
|
||||
'': 0,
|
||||
',': {type: 'operator', then: {'': next}},
|
||||
|
@ -49,8 +46,8 @@ const makeCommands = ((() => {
|
|||
};
|
||||
}
|
||||
|
||||
const end = {type: '', suggest: '\n', then: {}};
|
||||
const hiddenEnd = {type: '', suggest: false, then: {}};
|
||||
const end = {type: '', suggest: ['\n'], then: {}};
|
||||
const hiddenEnd = {type: '', suggest: [], then: {}};
|
||||
const textToEnd = textTo({'\n': end});
|
||||
const colonTextToEnd = {
|
||||
type: 'operator',
|
||||
|
@ -59,7 +56,7 @@ const makeCommands = ((() => {
|
|||
const aliasListToEnd = agentListTo({
|
||||
'\n': end,
|
||||
'as': {type: 'keyword', then: {
|
||||
'': {type: 'variable', suggest: {known: 'Agent'}, then: {
|
||||
'': {type: 'variable', suggest: [{known: 'Agent'}], then: {
|
||||
'': 0,
|
||||
',': {type: 'operator', then: {'': 3}},
|
||||
'\n': end,
|
||||
|
@ -70,7 +67,7 @@ const makeCommands = ((() => {
|
|||
|
||||
const agentToOptText = {
|
||||
type: 'variable',
|
||||
suggest: {known: 'Agent'},
|
||||
suggest: [{known: 'Agent'}],
|
||||
then: {
|
||||
'': 0,
|
||||
':': {type: 'operator', then: {
|
||||
|
@ -86,7 +83,7 @@ const makeCommands = ((() => {
|
|||
'as': {type: 'keyword', then: {
|
||||
'': {
|
||||
type: 'variable',
|
||||
suggest: {known: 'Agent'},
|
||||
suggest: [{known: 'Agent'}],
|
||||
then: {
|
||||
'': 0,
|
||||
'\n': end,
|
||||
|
@ -217,7 +214,7 @@ const makeCommands = ((() => {
|
|||
'...': {type: 'operator', then: {
|
||||
'': {
|
||||
type: 'variable',
|
||||
suggest: {known: 'DelayedAgent'},
|
||||
suggest: [{known: 'DelayedAgent'}],
|
||||
then: {
|
||||
'': 0,
|
||||
':': CM_ERROR,
|
||||
|
@ -239,14 +236,14 @@ const makeCommands = ((() => {
|
|||
|
||||
const hiddenLabelIndicator = {
|
||||
type: 'operator',
|
||||
suggest: false,
|
||||
suggest: [],
|
||||
override: 'Label',
|
||||
then: {},
|
||||
};
|
||||
|
||||
const firstAgent = {
|
||||
type: 'variable',
|
||||
suggest: {known: 'Agent'},
|
||||
suggest: [{known: 'Agent'}],
|
||||
then: Object.assign({
|
||||
'': 0,
|
||||
}, connectors, {
|
||||
|
@ -256,7 +253,7 @@ const makeCommands = ((() => {
|
|||
|
||||
const firstAgentDelayed = {
|
||||
type: 'variable',
|
||||
suggest: {known: 'DelayedAgent'},
|
||||
suggest: [{known: 'DelayedAgent'}],
|
||||
then: Object.assign({
|
||||
'': 0,
|
||||
':': hiddenLabelIndicator,
|
||||
|
@ -298,10 +295,7 @@ const makeCommands = ((() => {
|
|||
'theme': {type: 'keyword', then: {
|
||||
'': {
|
||||
type: 'string',
|
||||
suggest: {
|
||||
global: 'themes',
|
||||
suffix: '\n',
|
||||
},
|
||||
suggest: [{global: 'themes', suffix: '\n'}],
|
||||
then: {
|
||||
'': 0,
|
||||
'\n': end,
|
||||
|
@ -344,7 +338,7 @@ const makeCommands = ((() => {
|
|||
}},
|
||||
'if': commonGroup,
|
||||
'else': {type: 'keyword', suggest: ['else\n', 'else if: '], then: {
|
||||
'if': {type: 'keyword', suggest: 'if: ', then: {
|
||||
'if': {type: 'keyword', suggest: ['if: '], then: {
|
||||
'': textToEnd,
|
||||
':': {type: 'operator', then: {
|
||||
'': textToEnd,
|
||||
|
@ -364,11 +358,11 @@ const makeCommands = ((() => {
|
|||
'': agentListTo({':': CM_ERROR}, agentListToText),
|
||||
}},
|
||||
}},
|
||||
'state': {type: 'keyword', suggest: 'state over ', then: {
|
||||
'state': {type: 'keyword', suggest: ['state over '], then: {
|
||||
'over': {type: 'keyword', then: {
|
||||
'': {
|
||||
type: 'variable',
|
||||
suggest: {known: 'Agent'},
|
||||
suggest: [{known: 'Agent'}],
|
||||
then: {
|
||||
'': 0,
|
||||
',': CM_ERROR,
|
||||
|
@ -392,7 +386,7 @@ const makeCommands = ((() => {
|
|||
'simultaneously': {type: 'keyword', then: {
|
||||
':': {type: 'operator', then: {}},
|
||||
'with': {type: 'keyword', then: {
|
||||
'': {type: 'variable', suggest: {known: 'Label'}, then: {
|
||||
'': {type: 'variable', suggest: [{known: 'Label'}], then: {
|
||||
'': 0,
|
||||
':': {type: 'operator', then: {}},
|
||||
}},
|
||||
|
@ -406,33 +400,32 @@ const makeCommands = ((() => {
|
|||
});
|
||||
})());
|
||||
|
||||
/* eslint-enable sort-keys */
|
||||
|
||||
function cmCappedToken(token, current) {
|
||||
if(Object.keys(current.then).length > 0) {
|
||||
return {v: token, suffix: ' ', q: false};
|
||||
return {q: false, suffix: ' ', v: token};
|
||||
} else {
|
||||
return {v: token, suffix: '\n', q: false};
|
||||
return {q: false, suffix: '\n', v: token};
|
||||
}
|
||||
}
|
||||
|
||||
function cmGetSuggestions(state, token, current) {
|
||||
let suggestions = current.suggest;
|
||||
if(!Array.isArray(suggestions)) {
|
||||
suggestions = [suggestions];
|
||||
}
|
||||
const suggestions = current.suggest || [''];
|
||||
|
||||
return flatMap(suggestions, (suggest) => {
|
||||
if(suggest === false) {
|
||||
return [];
|
||||
} else if(typeof suggest === 'object') {
|
||||
if(typeof suggest === 'object') {
|
||||
if(suggest.known) {
|
||||
return state['known' + suggest.known] || [];
|
||||
} else {
|
||||
return [suggest];
|
||||
}
|
||||
} else if(typeof suggest === 'string' && suggest) {
|
||||
return [{v: suggest, q: (token === '')}];
|
||||
} else {
|
||||
} else if(suggest === '') {
|
||||
return [cmCappedToken(token, current)];
|
||||
} else if(typeof suggest === 'string') {
|
||||
return [{q: (token === ''), v: suggest}];
|
||||
} else {
|
||||
throw new Error('Invalid suggestion type ' + suggest);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -454,30 +447,40 @@ function cmMakeCompletions(state, path) {
|
|||
return comp;
|
||||
}
|
||||
|
||||
function updateSuggestion(state, locals, token, {suggest, override}) {
|
||||
let known = null;
|
||||
if(typeof suggest === 'object' && suggest.known) {
|
||||
known = suggest.known;
|
||||
function getSuggestionCategory(suggestions) {
|
||||
for(const suggestion of suggestions) {
|
||||
if(typeof suggestion === 'object' && suggestion.known) {
|
||||
return suggestion.known;
|
||||
}
|
||||
if(locals.type && known !== locals.type) {
|
||||
if(override) {
|
||||
locals.type = override;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function appendToken(base, token) {
|
||||
return base + (base ? token.s : '') + token.v;
|
||||
}
|
||||
|
||||
function storeKnownEntity(state, type, value) {
|
||||
mergeSets(
|
||||
state['known' + locals.type],
|
||||
[{v: locals.value, suffix: ' ', q: true}],
|
||||
state['known' + type],
|
||||
[{q: true, suffix: ' ', v: value}],
|
||||
suggestionsEqual
|
||||
);
|
||||
locals.type = '';
|
||||
}
|
||||
|
||||
function updateKnownEntities(state, locals, token, current) {
|
||||
const known = getSuggestionCategory(current.suggest || ['']);
|
||||
|
||||
if(locals.type && known !== locals.type) {
|
||||
storeKnownEntity(state, current.override || locals.type, locals.value);
|
||||
locals.value = '';
|
||||
}
|
||||
|
||||
if(known) {
|
||||
locals.value = appendToken(locals.value, token);
|
||||
}
|
||||
|
||||
locals.type = known;
|
||||
if(locals.value) {
|
||||
locals.value += token.s;
|
||||
}
|
||||
locals.value += token.v;
|
||||
}
|
||||
}
|
||||
|
||||
function cmCheckToken(state, eol, commands) {
|
||||
|
@ -506,10 +509,10 @@ function cmCheckToken(state, eol, commands) {
|
|||
path.push(found || CM_ERROR);
|
||||
}
|
||||
current = last(path);
|
||||
updateSuggestion(state, suggestions, token, current);
|
||||
updateKnownEntities(state, suggestions, token, current);
|
||||
});
|
||||
if(eol) {
|
||||
updateSuggestion(state, suggestions, null, {});
|
||||
updateKnownEntities(state, suggestions, null, {});
|
||||
}
|
||||
state.nextCompletions = cmMakeCompletions(state, path);
|
||||
state.valid = (
|
||||
|
@ -522,11 +525,13 @@ function cmCheckToken(state, eol, commands) {
|
|||
function getInitialToken(block) {
|
||||
const baseToken = (block.baseToken || {});
|
||||
return {
|
||||
value: baseToken.v || '',
|
||||
quoted: baseToken.q || false,
|
||||
value: baseToken.v || '',
|
||||
};
|
||||
}
|
||||
|
||||
const NO_TOKEN = -1;
|
||||
|
||||
export default class Mode {
|
||||
constructor(tokenDefinitions, arrows) {
|
||||
this.tokenDefinitions = tokenDefinitions;
|
||||
|
@ -536,20 +541,20 @@ export default class Mode {
|
|||
|
||||
startState() {
|
||||
return {
|
||||
currentType: -1,
|
||||
beginCompletions: cmMakeCompletions({}, [this.commands]),
|
||||
completions: [],
|
||||
current: '',
|
||||
currentSpace: '',
|
||||
currentQuoted: false,
|
||||
currentSpace: '',
|
||||
currentType: NO_TOKEN,
|
||||
indent: 0,
|
||||
isVar: true,
|
||||
knownAgent: [],
|
||||
knownDelayedAgent: [],
|
||||
knownLabel: [],
|
||||
beginCompletions: cmMakeCompletions({}, [this.commands]),
|
||||
completions: [],
|
||||
line: [],
|
||||
nextCompletions: [],
|
||||
valid: true,
|
||||
isVar: true,
|
||||
line: [],
|
||||
indent: 0,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -588,14 +593,14 @@ export default class Mode {
|
|||
|
||||
_addToken(state) {
|
||||
state.line.push({
|
||||
v: state.current,
|
||||
s: state.currentSpace,
|
||||
q: state.currentQuoted,
|
||||
s: state.currentSpace,
|
||||
v: state.current,
|
||||
});
|
||||
}
|
||||
|
||||
_tokenEndFound(stream, state, block, match) {
|
||||
state.currentType = -1;
|
||||
state.currentType = NO_TOKEN;
|
||||
if(block.includeEnd) {
|
||||
state.current += match[0];
|
||||
}
|
||||
|
@ -636,21 +641,33 @@ export default class Mode {
|
|||
}
|
||||
}
|
||||
|
||||
_tokenContinueOrBegin(stream, state) {
|
||||
if(state.currentType === NO_TOKEN) {
|
||||
if(stream.sol()) {
|
||||
state.line.length = 0;
|
||||
}
|
||||
if(!this._tokenBegin(stream, state)) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
return this._tokenEnd(stream, state);
|
||||
}
|
||||
|
||||
_isLineTerminated(state) {
|
||||
return state.currentType !== NO_TOKEN || state.valid;
|
||||
}
|
||||
|
||||
token(stream, state) {
|
||||
state.completions = state.nextCompletions;
|
||||
state.isVar = true;
|
||||
if(stream.sol() && state.currentType === -1) {
|
||||
state.line.length = 0;
|
||||
}
|
||||
let type = '';
|
||||
if(state.currentType !== -1 || this._tokenBegin(stream, state)) {
|
||||
type = this._tokenEnd(stream, state);
|
||||
}
|
||||
if(state.currentType === -1 && stream.eol() && !state.valid) {
|
||||
|
||||
const type = this._tokenContinueOrBegin(stream, state);
|
||||
|
||||
if(stream.eol() && !this._isLineTerminated(state)) {
|
||||
return 'line-error ' + type;
|
||||
} else {
|
||||
return type;
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
indent(state) {
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
/* eslint-disable max-lines */
|
||||
/* eslint-disable max-statements */
|
||||
/* eslint-disable sort-keys */ // Maybe later
|
||||
|
||||
import SequenceDiagram from '../SequenceDiagram.mjs';
|
||||
|
||||
|
@ -10,17 +8,17 @@ describe('Code Mirror Mode', () => {
|
|||
SequenceDiagram.registerCodeMirrorMode(CM);
|
||||
|
||||
const cm = new CM(null, {
|
||||
value: '',
|
||||
mode: 'sequence',
|
||||
globals: {
|
||||
themes: ['Theme', 'Other Theme'],
|
||||
},
|
||||
mode: 'sequence',
|
||||
value: '',
|
||||
});
|
||||
|
||||
function getTokens(line) {
|
||||
return cm.getLineTokens(line).map((token) => ({
|
||||
v: token.string,
|
||||
type: token.type,
|
||||
v: token.string,
|
||||
}));
|
||||
}
|
||||
|
||||
|
@ -29,7 +27,7 @@ describe('Code Mirror Mode', () => {
|
|||
cm.getDoc().setValue('# foo');
|
||||
|
||||
expect(getTokens(0)).toEqual([
|
||||
{v: '# foo', type: 'comment'},
|
||||
{type: 'comment', v: '# foo'},
|
||||
]);
|
||||
});
|
||||
|
||||
|
@ -37,8 +35,8 @@ describe('Code Mirror Mode', () => {
|
|||
cm.getDoc().setValue('terminators cross');
|
||||
|
||||
expect(getTokens(0)).toEqual([
|
||||
{v: 'terminators', type: 'keyword'},
|
||||
{v: ' cross', type: 'keyword'},
|
||||
{type: 'keyword', v: 'terminators'},
|
||||
{type: 'keyword', v: ' cross'},
|
||||
]);
|
||||
});
|
||||
|
||||
|
@ -46,9 +44,9 @@ describe('Code Mirror Mode', () => {
|
|||
cm.getDoc().setValue('terminators cross # foo');
|
||||
|
||||
expect(getTokens(0)).toEqual([
|
||||
{v: 'terminators', type: 'keyword'},
|
||||
{v: ' cross', type: 'keyword'},
|
||||
{v: ' # foo', type: 'comment'},
|
||||
{type: 'keyword', v: 'terminators'},
|
||||
{type: 'keyword', v: ' cross'},
|
||||
{type: 'comment', v: ' # foo'},
|
||||
]);
|
||||
});
|
||||
|
||||
|
@ -68,10 +66,10 @@ describe('Code Mirror Mode', () => {
|
|||
cm.getDoc().setValue('title my free text');
|
||||
|
||||
expect(getTokens(0)).toEqual([
|
||||
{v: 'title', type: 'keyword'},
|
||||
{v: ' my', type: 'string'},
|
||||
{v: ' free', type: 'string'},
|
||||
{v: ' text', type: 'string'},
|
||||
{type: 'keyword', v: 'title'},
|
||||
{type: 'string', v: ' my'},
|
||||
{type: 'string', v: ' free'},
|
||||
{type: 'string', v: ' text'},
|
||||
]);
|
||||
});
|
||||
|
||||
|
@ -79,8 +77,8 @@ describe('Code Mirror Mode', () => {
|
|||
cm.getDoc().setValue('title "my free text"');
|
||||
|
||||
expect(getTokens(0)).toEqual([
|
||||
{v: 'title', type: 'keyword'},
|
||||
{v: ' "my free text"', type: 'string'},
|
||||
{type: 'keyword', v: 'title'},
|
||||
{type: 'string', v: ' "my free text"'},
|
||||
]);
|
||||
});
|
||||
|
||||
|
@ -88,9 +86,9 @@ describe('Code Mirror Mode', () => {
|
|||
cm.getDoc().setValue('A -> B');
|
||||
|
||||
expect(getTokens(0)).toEqual([
|
||||
{v: 'A', type: 'variable'},
|
||||
{v: ' ->', type: 'keyword'},
|
||||
{v: ' B', type: 'variable'},
|
||||
{type: 'variable', v: 'A'},
|
||||
{type: 'keyword', v: ' ->'},
|
||||
{type: 'variable', v: ' B'},
|
||||
]);
|
||||
});
|
||||
|
||||
|
@ -98,10 +96,10 @@ describe('Code Mirror Mode', () => {
|
|||
cm.getDoc().setValue('A "->" -> B');
|
||||
|
||||
expect(getTokens(0)).toEqual([
|
||||
{v: 'A', type: 'variable'},
|
||||
{v: ' "->"', type: 'variable'},
|
||||
{v: ' ->', type: 'keyword'},
|
||||
{v: ' B', type: 'variable'},
|
||||
{type: 'variable', v: 'A'},
|
||||
{type: 'variable', v: ' "->"'},
|
||||
{type: 'keyword', v: ' ->'},
|
||||
{type: 'variable', v: ' B'},
|
||||
]);
|
||||
});
|
||||
|
||||
|
@ -109,14 +107,14 @@ describe('Code Mirror Mode', () => {
|
|||
cm.getDoc().setValue('define A as B, C as D');
|
||||
|
||||
expect(getTokens(0)).toEqual([
|
||||
{v: 'define', type: 'keyword'},
|
||||
{v: ' A', type: 'variable'},
|
||||
{v: ' as', type: 'keyword'},
|
||||
{v: ' B', type: 'variable'},
|
||||
{v: ',', type: 'operator'},
|
||||
{v: ' C', type: 'variable'},
|
||||
{v: ' as', type: 'keyword'},
|
||||
{v: ' D', type: 'variable'},
|
||||
{type: 'keyword', v: 'define'},
|
||||
{type: 'variable', v: ' A'},
|
||||
{type: 'keyword', v: ' as'},
|
||||
{type: 'variable', v: ' B'},
|
||||
{type: 'operator', v: ','},
|
||||
{type: 'variable', v: ' C'},
|
||||
{type: 'keyword', v: ' as'},
|
||||
{type: 'variable', v: ' D'},
|
||||
]);
|
||||
});
|
||||
|
||||
|
@ -124,11 +122,11 @@ describe('Code Mirror Mode', () => {
|
|||
cm.getDoc().setValue('Foo Bar -> Zig Zag');
|
||||
|
||||
expect(getTokens(0)).toEqual([
|
||||
{v: 'Foo', type: 'variable'},
|
||||
{v: ' Bar', type: 'variable'},
|
||||
{v: ' ->', type: 'keyword'},
|
||||
{v: ' Zig', type: 'variable'},
|
||||
{v: ' Zag', type: 'variable'},
|
||||
{type: 'variable', v: 'Foo'},
|
||||
{type: 'variable', v: ' Bar'},
|
||||
{type: 'keyword', v: ' ->'},
|
||||
{type: 'variable', v: ' Zig'},
|
||||
{type: 'variable', v: ' Zag'},
|
||||
]);
|
||||
});
|
||||
|
||||
|
@ -136,9 +134,9 @@ describe('Code Mirror Mode', () => {
|
|||
cm.getDoc().setValue('abc->xyz');
|
||||
|
||||
expect(getTokens(0)).toEqual([
|
||||
{v: 'abc', type: 'variable'},
|
||||
{v: '->', type: 'keyword'},
|
||||
{v: 'xyz', type: 'variable'},
|
||||
{type: 'variable', v: 'abc'},
|
||||
{type: 'keyword', v: '->'},
|
||||
{type: 'variable', v: 'xyz'},
|
||||
]);
|
||||
});
|
||||
|
||||
|
@ -146,9 +144,9 @@ describe('Code Mirror Mode', () => {
|
|||
cm.getDoc().setValue('abc-xxyz');
|
||||
|
||||
expect(getTokens(0)).toEqual([
|
||||
{v: 'abc', type: 'variable'},
|
||||
{v: '-x', type: 'keyword'},
|
||||
{v: 'xyz', type: 'variable'},
|
||||
{type: 'variable', v: 'abc'},
|
||||
{type: 'keyword', v: '-x'},
|
||||
{type: 'variable', v: 'xyz'},
|
||||
]);
|
||||
});
|
||||
|
||||
|
@ -156,10 +154,10 @@ describe('Code Mirror Mode', () => {
|
|||
cm.getDoc().setValue('Foo -> *Bar');
|
||||
|
||||
expect(getTokens(0)).toEqual([
|
||||
{v: 'Foo', type: 'variable'},
|
||||
{v: ' ->', type: 'keyword'},
|
||||
{v: ' *', type: 'operator'},
|
||||
{v: 'Bar', type: 'variable'},
|
||||
{type: 'variable', v: 'Foo'},
|
||||
{type: 'keyword', v: ' ->'},
|
||||
{type: 'operator', v: ' *'},
|
||||
{type: 'variable', v: 'Bar'},
|
||||
]);
|
||||
});
|
||||
|
||||
|
@ -167,10 +165,10 @@ describe('Code Mirror Mode', () => {
|
|||
cm.getDoc().setValue('*Foo -> Bar');
|
||||
|
||||
expect(getTokens(0)).toEqual([
|
||||
{v: '*', type: 'operator'},
|
||||
{v: 'Foo', type: 'variable'},
|
||||
{v: ' ->', type: 'keyword'},
|
||||
{v: ' Bar', type: 'variable'},
|
||||
{type: 'operator', v: '*'},
|
||||
{type: 'variable', v: 'Foo'},
|
||||
{type: 'keyword', v: ' ->'},
|
||||
{type: 'variable', v: ' Bar'},
|
||||
]);
|
||||
});
|
||||
|
||||
|
@ -198,11 +196,11 @@ describe('Code Mirror Mode', () => {
|
|||
cm.getDoc().setValue('Foo -> +*Bar');
|
||||
|
||||
expect(getTokens(0)).toEqual([
|
||||
{v: 'Foo', type: 'variable'},
|
||||
{v: ' ->', type: 'keyword'},
|
||||
{v: ' +', type: 'operator'},
|
||||
{v: '*', type: 'operator'},
|
||||
{v: 'Bar', type: 'variable'},
|
||||
{type: 'variable', v: 'Foo'},
|
||||
{type: 'keyword', v: ' ->'},
|
||||
{type: 'operator', v: ' +'},
|
||||
{type: 'operator', v: '*'},
|
||||
{type: 'variable', v: 'Bar'},
|
||||
]);
|
||||
});
|
||||
|
||||
|
@ -210,11 +208,11 @@ describe('Code Mirror Mode', () => {
|
|||
cm.getDoc().setValue('Foo -> Bar: hello');
|
||||
|
||||
expect(getTokens(0)).toEqual([
|
||||
{v: 'Foo', type: 'variable'},
|
||||
{v: ' ->', type: 'keyword'},
|
||||
{v: ' Bar', type: 'variable'},
|
||||
{v: ':', type: 'operator'},
|
||||
{v: ' hello', type: 'string'},
|
||||
{type: 'variable', v: 'Foo'},
|
||||
{type: 'keyword', v: ' ->'},
|
||||
{type: 'variable', v: ' Bar'},
|
||||
{type: 'operator', v: ':'},
|
||||
{type: 'string', v: ' hello'},
|
||||
]);
|
||||
});
|
||||
|
||||
|
@ -236,19 +234,19 @@ describe('Code Mirror Mode', () => {
|
|||
cm.getDoc().setValue('A -> ...x\n...x -> B: hello');
|
||||
|
||||
expect(getTokens(0)).toEqual([
|
||||
{v: 'A', type: 'variable'},
|
||||
{v: ' ->', type: 'keyword'},
|
||||
{v: ' ...', type: 'operator'},
|
||||
{v: 'x', type: 'variable'},
|
||||
{type: 'variable', v: 'A'},
|
||||
{type: 'keyword', v: ' ->'},
|
||||
{type: 'operator', v: ' ...'},
|
||||
{type: 'variable', v: 'x'},
|
||||
]);
|
||||
|
||||
expect(getTokens(1)).toEqual([
|
||||
{v: '...', type: 'operator'},
|
||||
{v: 'x', type: 'variable'},
|
||||
{v: ' ->', type: 'keyword'},
|
||||
{v: ' B', type: 'variable'},
|
||||
{v: ':', type: 'operator'},
|
||||
{v: ' hello', type: 'string'},
|
||||
{type: 'operator', v: '...'},
|
||||
{type: 'variable', v: 'x'},
|
||||
{type: 'keyword', v: ' ->'},
|
||||
{type: 'variable', v: ' B'},
|
||||
{type: 'operator', v: ':'},
|
||||
{type: 'string', v: ' hello'},
|
||||
]);
|
||||
});
|
||||
|
||||
|
@ -270,39 +268,39 @@ describe('Code Mirror Mode', () => {
|
|||
);
|
||||
|
||||
expect(getTokens(0)).toEqual([
|
||||
{v: 'if', type: 'keyword'},
|
||||
{type: 'keyword', v: 'if'},
|
||||
]);
|
||||
|
||||
expect(getTokens(1)).toEqual([
|
||||
{v: 'if', type: 'keyword'},
|
||||
{v: ' something', type: 'string'},
|
||||
{type: 'keyword', v: 'if'},
|
||||
{type: 'string', v: ' something'},
|
||||
]);
|
||||
|
||||
expect(getTokens(2)).toEqual([
|
||||
{v: 'else', type: 'keyword'},
|
||||
{v: ' if', type: 'keyword'},
|
||||
{v: ' another', type: 'string'},
|
||||
{v: ' thing', type: 'string'},
|
||||
{type: 'keyword', v: 'else'},
|
||||
{type: 'keyword', v: ' if'},
|
||||
{type: 'string', v: ' another'},
|
||||
{type: 'string', v: ' thing'},
|
||||
]);
|
||||
|
||||
expect(getTokens(3)).toEqual([
|
||||
{v: 'else', type: 'keyword'},
|
||||
{type: 'keyword', v: 'else'},
|
||||
]);
|
||||
|
||||
expect(getTokens(4)).toEqual([
|
||||
{v: 'end', type: 'keyword'},
|
||||
{type: 'keyword', v: 'end'},
|
||||
]);
|
||||
|
||||
expect(getTokens(5)).toEqual([
|
||||
{v: 'repeat', type: 'keyword'},
|
||||
{v: ' a', type: 'string'},
|
||||
{v: ' few', type: 'string'},
|
||||
{v: ' times', type: 'string'},
|
||||
{type: 'keyword', v: 'repeat'},
|
||||
{type: 'string', v: ' a'},
|
||||
{type: 'string', v: ' few'},
|
||||
{type: 'string', v: ' times'},
|
||||
]);
|
||||
|
||||
expect(getTokens(6)).toEqual([
|
||||
{v: 'group', type: 'keyword'},
|
||||
{v: ' foo', type: 'string'},
|
||||
{type: 'keyword', v: 'group'},
|
||||
{type: 'string', v: ' foo'},
|
||||
]);
|
||||
});
|
||||
|
||||
|
@ -314,25 +312,25 @@ describe('Code Mirror Mode', () => {
|
|||
);
|
||||
|
||||
expect(getTokens(0)).toEqual([
|
||||
{v: 'if', type: 'keyword'},
|
||||
{v: ':', type: 'operator'},
|
||||
{v: ' something', type: 'string'},
|
||||
{type: 'keyword', v: 'if'},
|
||||
{type: 'operator', v: ':'},
|
||||
{type: 'string', v: ' something'},
|
||||
]);
|
||||
|
||||
expect(getTokens(1)).toEqual([
|
||||
{v: 'else', type: 'keyword'},
|
||||
{v: ' if', type: 'keyword'},
|
||||
{v: ':', type: 'operator'},
|
||||
{v: ' another', type: 'string'},
|
||||
{v: ' thing', type: 'string'},
|
||||
{type: 'keyword', v: 'else'},
|
||||
{type: 'keyword', v: ' if'},
|
||||
{type: 'operator', v: ':'},
|
||||
{type: 'string', v: ' another'},
|
||||
{type: 'string', v: ' thing'},
|
||||
]);
|
||||
|
||||
expect(getTokens(2)).toEqual([
|
||||
{v: 'repeat', type: 'keyword'},
|
||||
{v: ':', type: 'operator'},
|
||||
{v: ' a', type: 'string'},
|
||||
{v: ' few', type: 'string'},
|
||||
{v: ' times', type: 'string'},
|
||||
{type: 'keyword', v: 'repeat'},
|
||||
{type: 'operator', v: ':'},
|
||||
{type: 'string', v: ' a'},
|
||||
{type: 'string', v: ' few'},
|
||||
{type: 'string', v: ' times'},
|
||||
]);
|
||||
});
|
||||
|
||||
|
@ -346,53 +344,53 @@ describe('Code Mirror Mode', () => {
|
|||
);
|
||||
|
||||
expect(getTokens(0)).toEqual([
|
||||
{v: 'note', type: 'keyword'},
|
||||
{v: ' over', type: 'keyword'},
|
||||
{v: ' A', type: 'variable'},
|
||||
{v: ':', type: 'operator'},
|
||||
{v: ' hi', type: 'string'},
|
||||
{type: 'keyword', v: 'note'},
|
||||
{type: 'keyword', v: ' over'},
|
||||
{type: 'variable', v: ' A'},
|
||||
{type: 'operator', v: ':'},
|
||||
{type: 'string', v: ' hi'},
|
||||
]);
|
||||
|
||||
expect(getTokens(1)).toEqual([
|
||||
{v: 'note', type: 'keyword'},
|
||||
{v: ' over', type: 'keyword'},
|
||||
{v: ' A', type: 'variable'},
|
||||
{v: ',', type: 'operator'},
|
||||
{v: ' B', type: 'variable'},
|
||||
{v: ':', type: 'operator'},
|
||||
{v: ' hi', type: 'string'},
|
||||
{type: 'keyword', v: 'note'},
|
||||
{type: 'keyword', v: ' over'},
|
||||
{type: 'variable', v: ' A'},
|
||||
{type: 'operator', v: ','},
|
||||
{type: 'variable', v: ' B'},
|
||||
{type: 'operator', v: ':'},
|
||||
{type: 'string', v: ' hi'},
|
||||
]);
|
||||
|
||||
expect(getTokens(2)).toEqual([
|
||||
{v: 'note', type: 'keyword'},
|
||||
{v: ' left', type: 'keyword'},
|
||||
{v: ' of', type: 'keyword'},
|
||||
{v: ' A', type: 'variable'},
|
||||
{v: ',', type: 'operator'},
|
||||
{v: ' B', type: 'variable'},
|
||||
{v: ':', type: 'operator'},
|
||||
{v: ' hi', type: 'string'},
|
||||
{type: 'keyword', v: 'note'},
|
||||
{type: 'keyword', v: ' left'},
|
||||
{type: 'keyword', v: ' of'},
|
||||
{type: 'variable', v: ' A'},
|
||||
{type: 'operator', v: ','},
|
||||
{type: 'variable', v: ' B'},
|
||||
{type: 'operator', v: ':'},
|
||||
{type: 'string', v: ' hi'},
|
||||
]);
|
||||
|
||||
expect(getTokens(3)).toEqual([
|
||||
{v: 'note', type: 'keyword'},
|
||||
{v: ' right', type: 'keyword'},
|
||||
{v: ' of', type: 'keyword'},
|
||||
{v: ' A', type: 'variable'},
|
||||
{v: ',', type: 'operator'},
|
||||
{v: ' B', type: 'variable'},
|
||||
{v: ':', type: 'operator'},
|
||||
{v: ' hi', type: 'string'},
|
||||
{type: 'keyword', v: 'note'},
|
||||
{type: 'keyword', v: ' right'},
|
||||
{type: 'keyword', v: ' of'},
|
||||
{type: 'variable', v: ' A'},
|
||||
{type: 'operator', v: ','},
|
||||
{type: 'variable', v: ' B'},
|
||||
{type: 'operator', v: ':'},
|
||||
{type: 'string', v: ' hi'},
|
||||
]);
|
||||
|
||||
expect(getTokens(4)).toEqual([
|
||||
{v: 'note', type: 'keyword'},
|
||||
{v: ' between', type: 'keyword'},
|
||||
{v: ' A', type: 'variable'},
|
||||
{v: ',', type: 'operator'},
|
||||
{v: ' B', type: 'variable'},
|
||||
{v: ':', type: 'operator'},
|
||||
{v: ' hi', type: 'string'},
|
||||
{type: 'keyword', v: 'note'},
|
||||
{type: 'keyword', v: ' between'},
|
||||
{type: 'variable', v: ' A'},
|
||||
{type: 'operator', v: ','},
|
||||
{type: 'variable', v: ' B'},
|
||||
{type: 'operator', v: ':'},
|
||||
{type: 'string', v: ' hi'},
|
||||
]);
|
||||
});
|
||||
|
||||
|
@ -406,11 +404,11 @@ describe('Code Mirror Mode', () => {
|
|||
cm.getDoc().setValue('state over A: hi');
|
||||
|
||||
expect(getTokens(0)).toEqual([
|
||||
{v: 'state', type: 'keyword'},
|
||||
{v: ' over', type: 'keyword'},
|
||||
{v: ' A', type: 'variable'},
|
||||
{v: ':', type: 'operator'},
|
||||
{v: ' hi', type: 'string'},
|
||||
{type: 'keyword', v: 'state'},
|
||||
{type: 'keyword', v: ' over'},
|
||||
{type: 'variable', v: ' A'},
|
||||
{type: 'operator', v: ':'},
|
||||
{type: 'string', v: ' hi'},
|
||||
]);
|
||||
});
|
||||
|
||||
|
@ -424,13 +422,13 @@ describe('Code Mirror Mode', () => {
|
|||
cm.getDoc().setValue('divider tear with height 60: stuff');
|
||||
|
||||
expect(getTokens(0)).toEqual([
|
||||
{v: 'divider', type: 'keyword'},
|
||||
{v: ' tear', type: 'keyword'},
|
||||
{v: ' with', type: 'keyword'},
|
||||
{v: ' height', type: 'keyword'},
|
||||
{v: ' 60', type: 'number'},
|
||||
{v: ':', type: 'operator'},
|
||||
{v: ' stuff', type: 'string'},
|
||||
{type: 'keyword', v: 'divider'},
|
||||
{type: 'keyword', v: ' tear'},
|
||||
{type: 'keyword', v: ' with'},
|
||||
{type: 'keyword', v: ' height'},
|
||||
{type: 'number', v: ' 60'},
|
||||
{type: 'operator', v: ':'},
|
||||
{type: 'string', v: ' stuff'},
|
||||
]);
|
||||
});
|
||||
|
||||
|
@ -438,11 +436,11 @@ describe('Code Mirror Mode', () => {
|
|||
cm.getDoc().setValue('A is a red database');
|
||||
|
||||
expect(getTokens(0)).toEqual([
|
||||
{v: 'A', type: 'variable'},
|
||||
{v: ' is', type: 'keyword'},
|
||||
{v: ' a', type: 'keyword'},
|
||||
{v: ' red', type: 'keyword'},
|
||||
{v: ' database', type: 'keyword'},
|
||||
{type: 'variable', v: 'A'},
|
||||
{type: 'keyword', v: ' is'},
|
||||
{type: 'keyword', v: ' a'},
|
||||
{type: 'keyword', v: ' red'},
|
||||
{type: 'keyword', v: ' database'},
|
||||
]);
|
||||
});
|
||||
|
||||
|
@ -467,7 +465,7 @@ describe('Code Mirror Mode', () => {
|
|||
|
||||
it('suggests commands when used at the start of a line', () => {
|
||||
cm.getDoc().setValue('');
|
||||
const hints = getHintTexts({line: 0, ch: 0});
|
||||
const hints = getHintTexts({ch: 0, line: 0});
|
||||
|
||||
expect(hints).toContain('theme ');
|
||||
expect(hints).toContain('title ');
|
||||
|
@ -491,7 +489,7 @@ describe('Code Mirror Mode', () => {
|
|||
|
||||
it('ignores indentation', () => {
|
||||
cm.getDoc().setValue(' ');
|
||||
const hints = getHintTexts({line: 0, ch: 2});
|
||||
const hints = getHintTexts({ch: 2, line: 0});
|
||||
|
||||
expect(hints).toContain('theme ');
|
||||
expect(hints).toContain('title ');
|
||||
|
@ -499,7 +497,7 @@ describe('Code Mirror Mode', () => {
|
|||
|
||||
it('suggests known header types', () => {
|
||||
cm.getDoc().setValue('headers ');
|
||||
const hints = getHintTexts({line: 0, ch: 8});
|
||||
const hints = getHintTexts({ch: 8, line: 0});
|
||||
|
||||
expect(hints).toEqual([
|
||||
'none\n',
|
||||
|
@ -512,7 +510,7 @@ describe('Code Mirror Mode', () => {
|
|||
|
||||
it('suggests known terminator types', () => {
|
||||
cm.getDoc().setValue('terminators ');
|
||||
const hints = getHintTexts({line: 0, ch: 12});
|
||||
const hints = getHintTexts({ch: 12, line: 0});
|
||||
|
||||
expect(hints).toEqual([
|
||||
'none\n',
|
||||
|
@ -525,7 +523,7 @@ describe('Code Mirror Mode', () => {
|
|||
|
||||
it('suggests divider types', () => {
|
||||
cm.getDoc().setValue('divider ');
|
||||
const hints = getHintTexts({line: 0, ch: 8});
|
||||
const hints = getHintTexts({ch: 8, line: 0});
|
||||
|
||||
expect(hints).toEqual([
|
||||
'line ',
|
||||
|
@ -540,7 +538,7 @@ describe('Code Mirror Mode', () => {
|
|||
|
||||
it('suggests divider sizes', () => {
|
||||
cm.getDoc().setValue('divider space with height ');
|
||||
const hints = getHintTexts({line: 0, ch: 26});
|
||||
const hints = getHintTexts({ch: 26, line: 0});
|
||||
|
||||
expect(hints).toEqual([
|
||||
'6 ',
|
||||
|
@ -550,7 +548,7 @@ describe('Code Mirror Mode', () => {
|
|||
|
||||
it('suggests useful autolabel values', () => {
|
||||
cm.getDoc().setValue('autolabel ');
|
||||
const hints = getHintTexts({line: 0, ch: 10});
|
||||
const hints = getHintTexts({ch: 10, line: 0});
|
||||
|
||||
expect(hints).toContain('off\n');
|
||||
expect(hints).toContain('"<label>"\n');
|
||||
|
@ -559,7 +557,7 @@ describe('Code Mirror Mode', () => {
|
|||
|
||||
it('suggests note positioning', () => {
|
||||
cm.getDoc().setValue('note ');
|
||||
const hints = getHintTexts({line: 0, ch: 5});
|
||||
const hints = getHintTexts({ch: 5, line: 0});
|
||||
|
||||
expect(hints).toEqual([
|
||||
'over ',
|
||||
|
@ -573,14 +571,14 @@ describe('Code Mirror Mode', () => {
|
|||
|
||||
it('filters suggestions', () => {
|
||||
cm.getDoc().setValue('term');
|
||||
const hints = getHintTexts({line: 0, ch: 4});
|
||||
const hints = getHintTexts({ch: 4, line: 0});
|
||||
|
||||
expect(hints).toEqual(['terminators ']);
|
||||
});
|
||||
|
||||
it('suggests known agent names and flags', () => {
|
||||
cm.getDoc().setValue('Foo -> ');
|
||||
const hints = getHintTexts({line: 0, ch: 7});
|
||||
const hints = getHintTexts({ch: 7, line: 0});
|
||||
|
||||
expect(hints).toEqual([
|
||||
'+ ',
|
||||
|
@ -594,7 +592,7 @@ describe('Code Mirror Mode', () => {
|
|||
|
||||
it('only suggests valid flag combinations', () => {
|
||||
cm.getDoc().setValue('Foo -> + ');
|
||||
const hints = getHintTexts({line: 0, ch: 10});
|
||||
const hints = getHintTexts({ch: 10, line: 0});
|
||||
|
||||
expect(hints).toContain('* ');
|
||||
expect(hints).not.toContain('! ');
|
||||
|
@ -605,7 +603,7 @@ describe('Code Mirror Mode', () => {
|
|||
|
||||
it('suggests known agent names at the start of lines', () => {
|
||||
cm.getDoc().setValue('Foo -> Bar\n');
|
||||
const hints = getHintTexts({line: 1, ch: 0});
|
||||
const hints = getHintTexts({ch: 0, line: 1});
|
||||
|
||||
expect(hints).toContain('Foo ');
|
||||
expect(hints).toContain('Bar ');
|
||||
|
@ -613,21 +611,21 @@ describe('Code Mirror Mode', () => {
|
|||
|
||||
it('suggests known labels', () => {
|
||||
cm.getDoc().setValue('Abc:\nsimultaneously with ');
|
||||
const hints = getHintTexts({line: 1, ch: 20});
|
||||
const hints = getHintTexts({ch: 20, line: 1});
|
||||
|
||||
expect(hints).toEqual(['Abc ']);
|
||||
});
|
||||
|
||||
it('suggests known themes', () => {
|
||||
cm.getDoc().setValue('theme ');
|
||||
const hints = getHintTexts({line: 0, ch: 6});
|
||||
const hints = getHintTexts({ch: 6, line: 0});
|
||||
|
||||
expect(hints).toEqual(['Theme\n', 'Other Theme\n']);
|
||||
});
|
||||
|
||||
it('suggests filtered multi-word themes', () => {
|
||||
cm.getDoc().setValue('theme Other ');
|
||||
const hints = getHintTexts({line: 0, ch: 12});
|
||||
const hints = getHintTexts({ch: 12, line: 0});
|
||||
|
||||
expect(hints).toContain('Other Theme\n');
|
||||
expect(hints).not.toContain('Theme\n');
|
||||
|
@ -635,7 +633,7 @@ describe('Code Mirror Mode', () => {
|
|||
|
||||
it('suggests multi-word agents', () => {
|
||||
cm.getDoc().setValue('Zig Zag -> Meh\nFoo Bar -> ');
|
||||
const hints = getHintTexts({line: 1, ch: 11});
|
||||
const hints = getHintTexts({ch: 11, line: 1});
|
||||
|
||||
expect(hints).toContain('Zig Zag ');
|
||||
expect(hints).toContain('Meh ');
|
||||
|
@ -644,7 +642,7 @@ describe('Code Mirror Mode', () => {
|
|||
|
||||
it('suggests quoted agent names if a quote is typed', () => {
|
||||
cm.getDoc().setValue('Zig Zag -> Meh\nFoo Bar -> "');
|
||||
const hints = getHintTexts({line: 1, ch: 12});
|
||||
const hints = getHintTexts({ch: 12, line: 1});
|
||||
|
||||
expect(hints).toEqual([
|
||||
'"Zig Zag" ',
|
||||
|
@ -655,7 +653,7 @@ describe('Code Mirror Mode', () => {
|
|||
|
||||
it('suggests filtered multi-word agents', () => {
|
||||
cm.getDoc().setValue('Zig Zag -> Meh\nFoo Bar -> Foo ');
|
||||
const hints = getHintTexts({line: 1, ch: 15});
|
||||
const hints = getHintTexts({ch: 15, line: 1});
|
||||
|
||||
expect(hints).toContain('Foo Bar ');
|
||||
expect(hints).not.toContain('Zig Zag ');
|
||||
|
@ -664,33 +662,33 @@ describe('Code Mirror Mode', () => {
|
|||
|
||||
it('suggests quoted names where required', () => {
|
||||
cm.getDoc().setValue('"Zig -> Zag" -> ');
|
||||
const hints = getHintTexts({line: 0, ch: 16});
|
||||
const hints = getHintTexts({ch: 16, line: 0});
|
||||
|
||||
expect(hints).toContain('"Zig -> Zag" ');
|
||||
});
|
||||
|
||||
it('filters quoted names ignoring quotes', () => {
|
||||
cm.getDoc().setValue('"Zig -> Zag" -> Zig');
|
||||
let hints = getHintTexts({line: 0, ch: 19});
|
||||
let hints = getHintTexts({ch: 19, line: 0});
|
||||
|
||||
expect(hints).toContain('"Zig -> Zag" ');
|
||||
|
||||
cm.getDoc().setValue('"Zig -> Zag" -> Zag');
|
||||
hints = getHintTexts({line: 0, ch: 19});
|
||||
hints = getHintTexts({ch: 19, line: 0});
|
||||
|
||||
expect(hints).not.toContain('"Zig -> Zag" ');
|
||||
});
|
||||
|
||||
it('suggests known delayed agents', () => {
|
||||
cm.getDoc().setValue('A -> ...woo\n... ');
|
||||
const hints = getHintTexts({line: 1, ch: 4});
|
||||
const hints = getHintTexts({ch: 4, line: 1});
|
||||
|
||||
expect(hints).toEqual(['woo ']);
|
||||
});
|
||||
|
||||
it('suggests agent properties', () => {
|
||||
cm.getDoc().setValue('A is a ');
|
||||
const hints = getHintTexts({line: 0, ch: 7});
|
||||
const hints = getHintTexts({ch: 7, line: 0});
|
||||
|
||||
expect(hints).toContain('database ');
|
||||
expect(hints).toContain('red ');
|
||||
|
@ -699,7 +697,7 @@ describe('Code Mirror Mode', () => {
|
|||
|
||||
it('suggests indefinite articles for agent properties', () => {
|
||||
cm.getDoc().setValue('A is ');
|
||||
const hints = getHintTexts({line: 0, ch: 5});
|
||||
const hints = getHintTexts({ch: 5, line: 0});
|
||||
|
||||
expect(hints).toContain('database ');
|
||||
expect(hints).toContain('a ');
|
||||
|
@ -709,7 +707,7 @@ describe('Code Mirror Mode', () => {
|
|||
|
||||
it('suggests more agent properties after the first', () => {
|
||||
cm.getDoc().setValue('A is a red ');
|
||||
const hints = getHintTexts({line: 0, ch: 11});
|
||||
const hints = getHintTexts({ch: 11, line: 0});
|
||||
|
||||
expect(hints).toContain('database ');
|
||||
expect(hints).toContain('\n');
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
/* eslint-disable complexity */ // Temporary ignore while switching linter
|
||||
|
||||
import {last, mergeSets} from '../../core/ArrayUtilities.mjs';
|
||||
|
||||
const TRIMMER = /^([ \t]*)(.*)$/;
|
||||
|
@ -12,7 +10,7 @@ const ONGOING_QUOTE = /^"(\\.|[^"])*$/;
|
|||
const REQUIRED_QUOTED = /[\r\n:,"<>\-~]/;
|
||||
const QUOTE_ESCAPE = /["\\]/g;
|
||||
|
||||
function suggestionsEqual(a, b) {
|
||||
function completionsEqual(a, b) {
|
||||
return (
|
||||
(a.v === b.v) &&
|
||||
(a.prefix === b.prefix) &&
|
||||
|
@ -89,12 +87,12 @@ function getGlobals({global, prefix = '', suffix = ''}, globals) {
|
|||
return identified.map((item) => ({prefix, q: true, suffix, v: item}));
|
||||
}
|
||||
|
||||
function populateGlobals(suggestions, globals = {}) {
|
||||
for(let i = 0; i < suggestions.length;) {
|
||||
if(suggestions[i].global) {
|
||||
const identified = getGlobals(suggestions[i], globals);
|
||||
mergeSets(suggestions, identified, suggestionsEqual);
|
||||
suggestions.splice(i, 1);
|
||||
function populateGlobals(completions, globals = {}) {
|
||||
for(let i = 0; i < completions.length;) {
|
||||
if(completions[i].global) {
|
||||
const identified = getGlobals(completions[i], globals);
|
||||
mergeSets(completions, identified, completionsEqual);
|
||||
completions.splice(i, 1);
|
||||
} else {
|
||||
++ i;
|
||||
}
|
||||
|
@ -176,6 +174,18 @@ function partialMatch(v, p) {
|
|||
return p.valid && v.startsWith(p.partial);
|
||||
}
|
||||
|
||||
function getCompletions(cur, token, globals) {
|
||||
let completions = null;
|
||||
if(cur.ch > 0 && token.state.line.length > 0) {
|
||||
completions = token.state.completions.slice();
|
||||
} else {
|
||||
completions = token.state.beginCompletions
|
||||
.concat(token.state.knownAgent);
|
||||
}
|
||||
populateGlobals(completions, globals);
|
||||
return completions;
|
||||
}
|
||||
|
||||
export function getHints(cm, options) {
|
||||
const cur = cm.getCursor();
|
||||
const tokens = getTokensUpTo(cm, cur);
|
||||
|
@ -183,16 +193,7 @@ export function getHints(cm, options) {
|
|||
const pVar = getVariablePartial(tokens, cur);
|
||||
const pKey = getKeywordPartial(token, cur);
|
||||
|
||||
const continuation = (cur.ch > 0 && token.state.line.length > 0);
|
||||
let comp = (continuation ?
|
||||
token.state.completions :
|
||||
token.state.beginCompletions
|
||||
);
|
||||
if(!continuation) {
|
||||
comp = comp.concat(token.state.knownAgent);
|
||||
}
|
||||
|
||||
populateGlobals(comp, cm.options.globals);
|
||||
const completions = getCompletions(cur, token, cm.options.globals);
|
||||
|
||||
const ranges = {
|
||||
fromKey: makeRangeFrom(cm, cur.line, pKey.from),
|
||||
|
@ -200,7 +201,7 @@ export function getHints(cm, options) {
|
|||
to: makeRangeTo(cm, cur.line, token.end),
|
||||
};
|
||||
let selfValid = null;
|
||||
const list = (comp
|
||||
const list = (completions
|
||||
.filter((o) => (
|
||||
(o.q || !pVar.quote) &&
|
||||
partialMatch(o.v, o.q ? pVar : pKey)
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* eslint-disable max-lines */
|
||||
/* eslint-disable sort-keys */ // Maybe later
|
||||
/* eslint-disable complexity */ // Temporary ignore while switching linter
|
||||
|
||||
import {
|
||||
flatMap,
|
||||
|
@ -41,15 +40,6 @@ const PAgent = {
|
|||
|
||||
// Agent from Generator: {id, formattedLabel, anchorRight}
|
||||
const GAgent = {
|
||||
equals: (a, b) => (a.id === b.id),
|
||||
make: (id, {anchorRight = false, isVirtualSource = false} = {}) => ({
|
||||
anchorRight,
|
||||
id,
|
||||
isVirtualSource,
|
||||
options: [],
|
||||
}),
|
||||
indexOf: (list, gAgent) => indexOf(list, gAgent, GAgent.equals),
|
||||
hasIntersection: (a, b) => hasIntersection(a, b, GAgent.equals),
|
||||
addNearby: (target, reference, item, offset) => {
|
||||
const p = indexOf(target, reference, GAgent.equals);
|
||||
if(p === -1) {
|
||||
|
@ -58,11 +48,28 @@ const GAgent = {
|
|||
target.splice(p + offset, 0, item);
|
||||
}
|
||||
},
|
||||
equals: (a, b) => (a.id === b.id),
|
||||
hasIntersection: (a, b) => hasIntersection(a, b, GAgent.equals),
|
||||
indexOf: (list, gAgent) => indexOf(list, gAgent, GAgent.equals),
|
||||
make: (id, {anchorRight = false, isVirtualSource = false} = {}) => ({
|
||||
anchorRight,
|
||||
id,
|
||||
isVirtualSource,
|
||||
options: [],
|
||||
}),
|
||||
};
|
||||
|
||||
function isExpiredGroupAlias(state) {
|
||||
return state.blocked && state.group === null;
|
||||
}
|
||||
|
||||
function isReservedAgentName(name) {
|
||||
return name.startsWith('__');
|
||||
}
|
||||
|
||||
const NOTE_DEFAULT_G_AGENTS = {
|
||||
'note over': [GAgent.make('['), GAgent.make(']')],
|
||||
'note left': [GAgent.make('[')],
|
||||
'note over': [GAgent.make('['), GAgent.make(']')],
|
||||
'note right': [GAgent.make(']')],
|
||||
};
|
||||
|
||||
|
@ -247,25 +254,25 @@ export default class Generator {
|
|||
this.currentNest = null;
|
||||
|
||||
this.stageHandlers = {
|
||||
'block begin': this.handleBlockBegin.bind(this),
|
||||
'block split': this.handleBlockSplit.bind(this),
|
||||
'block end': this.handleBlockEnd.bind(this),
|
||||
'group begin': this.handleGroupBegin.bind(this),
|
||||
'mark': this.handleMark.bind(this),
|
||||
'async': this.handleAsync.bind(this),
|
||||
'agent define': this.handleAgentDefine.bind(this),
|
||||
'agent options': this.handleAgentOptions.bind(this),
|
||||
'agent begin': this.handleAgentBegin.bind(this),
|
||||
'agent define': this.handleAgentDefine.bind(this),
|
||||
'agent end': this.handleAgentEnd.bind(this),
|
||||
'divider': this.handleDivider.bind(this),
|
||||
'label pattern': this.handleLabelPattern.bind(this),
|
||||
'agent options': this.handleAgentOptions.bind(this),
|
||||
'async': this.handleAsync.bind(this),
|
||||
'block begin': this.handleBlockBegin.bind(this),
|
||||
'block end': this.handleBlockEnd.bind(this),
|
||||
'block split': this.handleBlockSplit.bind(this),
|
||||
'connect': this.handleConnect.bind(this),
|
||||
'connect-delay-begin': this.handleConnectDelayBegin.bind(this),
|
||||
'connect-delay-end': this.handleConnectDelayEnd.bind(this),
|
||||
'note over': this.handleNote.bind(this),
|
||||
'note left': this.handleNote.bind(this),
|
||||
'note right': this.handleNote.bind(this),
|
||||
'divider': this.handleDivider.bind(this),
|
||||
'group begin': this.handleGroupBegin.bind(this),
|
||||
'label pattern': this.handleLabelPattern.bind(this),
|
||||
'mark': this.handleMark.bind(this),
|
||||
'note between': this.handleNote.bind(this),
|
||||
'note left': this.handleNote.bind(this),
|
||||
'note over': this.handleNote.bind(this),
|
||||
'note right': this.handleNote.bind(this),
|
||||
};
|
||||
this.expandGroupedGAgent = this.expandGroupedGAgent.bind(this);
|
||||
this.handleStage = this.handleStage.bind(this);
|
||||
|
@ -273,6 +280,14 @@ export default class Generator {
|
|||
this.endGroup = this.endGroup.bind(this);
|
||||
}
|
||||
|
||||
_aliasInUse(alias) {
|
||||
const old = this.agentAliases.get(alias);
|
||||
if(old && old !== alias) {
|
||||
return true;
|
||||
}
|
||||
return this.gAgents.some((gAgent) => (gAgent.id === alias));
|
||||
}
|
||||
|
||||
toGAgent({name, alias, flags}) {
|
||||
if(alias) {
|
||||
if(this.agentAliases.has(name)) {
|
||||
|
@ -280,11 +295,7 @@ export default class Generator {
|
|||
'Cannot alias ' + name + '; it is already an alias'
|
||||
);
|
||||
}
|
||||
const old = this.agentAliases.get(alias);
|
||||
if(
|
||||
(old && old !== alias) ||
|
||||
this.gAgents.some((gAgent) => (gAgent.id === alias))
|
||||
) {
|
||||
if(this._aliasInUse(alias)) {
|
||||
throw new Error(
|
||||
'Cannot use ' + alias +
|
||||
' as an alias; it is already in use'
|
||||
|
@ -325,8 +336,8 @@ export default class Generator {
|
|||
}
|
||||
});
|
||||
this.addStage({
|
||||
type: 'parallel',
|
||||
stages: viableStages,
|
||||
type: 'parallel',
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -361,25 +372,27 @@ export default class Generator {
|
|||
allowCovered = false,
|
||||
allowVirtual = false,
|
||||
} = {}) {
|
||||
/* eslint-disable complexity */ // The checks are quite simple
|
||||
gAgents.forEach((gAgent) => {
|
||||
/* eslint-enable complexity */
|
||||
const state = this.getGAgentState(gAgent);
|
||||
if(state.blocked && state.group === null) {
|
||||
const name = gAgent.id;
|
||||
|
||||
if(isExpiredGroupAlias(state)) {
|
||||
// Used to be a group alias; can never be reused
|
||||
throw new Error('Duplicate agent name: ' + gAgent.id);
|
||||
throw new Error('Duplicate agent name: ' + name);
|
||||
}
|
||||
if(!allowCovered && state.covered) {
|
||||
throw new Error(
|
||||
'Agent ' + gAgent.id + ' is hidden behind group'
|
||||
);
|
||||
throw new Error('Agent ' + name + ' is hidden behind group');
|
||||
}
|
||||
if(!allowGrouped && state.group !== null) {
|
||||
throw new Error('Agent ' + gAgent.id + ' is in a group');
|
||||
throw new Error('Agent ' + name + ' is in a group');
|
||||
}
|
||||
if(!allowVirtual && gAgent.isVirtualSource) {
|
||||
throw new Error('cannot use message source here');
|
||||
throw new Error('Cannot use message source here');
|
||||
}
|
||||
if(gAgent.id.startsWith('__')) {
|
||||
throw new Error(gAgent.id + ' is a reserved name');
|
||||
if(isReservedAgentName(name)) {
|
||||
throw new Error(name + ' is a reserved name');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -410,9 +423,9 @@ export default class Generator {
|
|||
this.defineGAgents(filteredGAgents);
|
||||
|
||||
return {
|
||||
type: (visible ? 'agent begin' : 'agent end'),
|
||||
agentIDs: filteredGAgents.map((gAgent) => gAgent.id),
|
||||
mode,
|
||||
type: (visible ? 'agent begin' : 'agent end'),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -436,16 +449,16 @@ export default class Generator {
|
|||
});
|
||||
|
||||
return {
|
||||
type: 'agent highlight',
|
||||
agentIDs: filteredGAgents.map((gAgent) => gAgent.id),
|
||||
highlighted,
|
||||
type: 'agent highlight',
|
||||
};
|
||||
}
|
||||
|
||||
_makeSection(header, stages) {
|
||||
return {
|
||||
header,
|
||||
delayedConnections: new Map(),
|
||||
header,
|
||||
stages,
|
||||
};
|
||||
}
|
||||
|
@ -467,21 +480,21 @@ export default class Generator {
|
|||
const gAgents = [leftGAgent, rightGAgent];
|
||||
const stages = [];
|
||||
this.currentSection = this._makeSection({
|
||||
type: 'block begin',
|
||||
blockType,
|
||||
tag: this.textFormatter(tag),
|
||||
label: this.textFormatter(label),
|
||||
canHide: true,
|
||||
label: this.textFormatter(label),
|
||||
left: leftGAgent.id,
|
||||
right: rightGAgent.id,
|
||||
ln,
|
||||
right: rightGAgent.id,
|
||||
tag: this.textFormatter(tag),
|
||||
type: 'block begin',
|
||||
}, stages);
|
||||
this.currentNest = {
|
||||
blockType,
|
||||
gAgents,
|
||||
hasContent: false,
|
||||
leftGAgent,
|
||||
rightGAgent,
|
||||
hasContent: false,
|
||||
sections: [this.currentSection],
|
||||
};
|
||||
this.replaceGAgentState(leftGAgent, AgentState.LOCKED);
|
||||
|
@ -505,10 +518,10 @@ export default class Generator {
|
|||
|
||||
handleBlockBegin({ln, blockType, tag, label}) {
|
||||
this.beginNested(blockType, {
|
||||
tag,
|
||||
label,
|
||||
name: this.nextBlockName(),
|
||||
ln,
|
||||
name: this.nextBlockName(),
|
||||
tag,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -521,13 +534,13 @@ export default class Generator {
|
|||
}
|
||||
this._checkSectionEnd();
|
||||
this.currentSection = this._makeSection({
|
||||
type: 'block split',
|
||||
blockType,
|
||||
tag: this.textFormatter(tag),
|
||||
label: this.textFormatter(label),
|
||||
left: this.currentNest.leftGAgent.id,
|
||||
right: this.currentNest.rightGAgent.id,
|
||||
ln,
|
||||
right: this.currentNest.rightGAgent.id,
|
||||
tag: this.textFormatter(tag),
|
||||
type: 'block split',
|
||||
}, []);
|
||||
this.currentNest.sections.push(this.currentSection);
|
||||
}
|
||||
|
@ -554,9 +567,9 @@ export default class Generator {
|
|||
this.currentSection.stages.push(...section.stages);
|
||||
});
|
||||
this.addStage({
|
||||
type: 'block end',
|
||||
left: nested.leftGAgent.id,
|
||||
right: nested.rightGAgent.id,
|
||||
type: 'block end',
|
||||
});
|
||||
} else {
|
||||
throw new Error('Empty block');
|
||||
|
@ -595,10 +608,10 @@ export default class Generator {
|
|||
|
||||
return {
|
||||
gAgents,
|
||||
leftGAgent,
|
||||
rightGAgent,
|
||||
gAgentsContained,
|
||||
gAgentsCovered,
|
||||
leftGAgent,
|
||||
rightGAgent,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -614,13 +627,13 @@ export default class Generator {
|
|||
this.activeGroups.set(alias, details);
|
||||
this.addStage(this.setGAgentVis(details.gAgents, true, 'box'));
|
||||
this.addStage({
|
||||
type: 'block begin',
|
||||
blockType,
|
||||
tag: this.textFormatter(tag),
|
||||
canHide: false,
|
||||
label: this.textFormatter(label),
|
||||
left: details.leftGAgent.id,
|
||||
right: details.rightGAgent.id,
|
||||
tag: this.textFormatter(tag),
|
||||
type: 'block begin',
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -640,23 +653,23 @@ export default class Generator {
|
|||
this.updateGAgentState(GAgent.make(name), {group: null});
|
||||
|
||||
return {
|
||||
type: 'block end',
|
||||
left: details.leftGAgent.id,
|
||||
right: details.rightGAgent.id,
|
||||
type: 'block end',
|
||||
};
|
||||
}
|
||||
|
||||
handleMark({name}) {
|
||||
this.markers.add(name);
|
||||
this.addStage({type: 'mark', name}, false);
|
||||
this.addStage({name, type: 'mark'}, false);
|
||||
}
|
||||
|
||||
handleDivider({mode, height, label}) {
|
||||
this.addStage({
|
||||
type: 'divider',
|
||||
mode,
|
||||
height,
|
||||
formattedLabel: this.textFormatter(label),
|
||||
height,
|
||||
mode,
|
||||
type: 'divider',
|
||||
}, false);
|
||||
}
|
||||
|
||||
|
@ -664,7 +677,7 @@ export default class Generator {
|
|||
if(target !== '' && !this.markers.has(target)) {
|
||||
throw new Error('Unknown marker: ' + target);
|
||||
}
|
||||
this.addStage({type: 'async', target}, false);
|
||||
this.addStage({target, type: 'async'}, false);
|
||||
}
|
||||
|
||||
handleLabelPattern({pattern}) {
|
||||
|
@ -867,15 +880,15 @@ export default class Generator {
|
|||
const tag = {};
|
||||
this.handleConnectDelayBegin({
|
||||
agent: agents[0],
|
||||
tag,
|
||||
options,
|
||||
ln: 0,
|
||||
options,
|
||||
tag,
|
||||
});
|
||||
this.handleConnectDelayEnd({
|
||||
agent: agents[1],
|
||||
tag,
|
||||
label,
|
||||
options,
|
||||
tag,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
@ -886,10 +899,10 @@ export default class Generator {
|
|||
gAgents = this.expandVirtualSourceAgents(gAgents);
|
||||
|
||||
const connectStage = {
|
||||
type: 'connect',
|
||||
agentIDs: gAgents.map((gAgent) => gAgent.id),
|
||||
label: this.textFormatter(this.applyLabelPattern(label)),
|
||||
options,
|
||||
type: 'connect',
|
||||
};
|
||||
|
||||
this.addParallelStages(this._makeConnectParallelStages(
|
||||
|
@ -908,14 +921,14 @@ export default class Generator {
|
|||
const uniqueTag = this.nextVirtualAgentName();
|
||||
|
||||
const connectStage = {
|
||||
type: 'connect-delay-begin',
|
||||
tag: uniqueTag,
|
||||
agentIDs: null,
|
||||
label: null,
|
||||
options,
|
||||
tag: uniqueTag,
|
||||
type: 'connect-delay-begin',
|
||||
};
|
||||
|
||||
dcs.set(tag, {tag, uniqueTag, ln, gAgents, connectStage});
|
||||
dcs.set(tag, {connectStage, gAgents, ln, tag, uniqueTag});
|
||||
|
||||
this.addParallelStages(this._makeConnectParallelStages(
|
||||
flags,
|
||||
|
@ -955,8 +968,8 @@ export default class Generator {
|
|||
});
|
||||
|
||||
const connectEndStage = {
|
||||
type: 'connect-delay-end',
|
||||
tag: dcInfo.uniqueTag,
|
||||
type: 'connect-delay-end',
|
||||
};
|
||||
|
||||
this.addParallelStages(this._makeConnectParallelStages(
|
||||
|
@ -987,18 +1000,18 @@ export default class Generator {
|
|||
this.defineGAgents(gAgents);
|
||||
|
||||
this.addStage({
|
||||
type,
|
||||
agentIDs,
|
||||
mode,
|
||||
label: this.textFormatter(label),
|
||||
mode,
|
||||
type,
|
||||
});
|
||||
}
|
||||
|
||||
handleAgentDefine({agents}) {
|
||||
const gAgents = agents.map(this.toGAgent);
|
||||
this.validateGAgents(gAgents, {
|
||||
allowGrouped: true,
|
||||
allowCovered: true,
|
||||
allowGrouped: true,
|
||||
});
|
||||
mergeSets(this.gAgents, gAgents, GAgent.equals);
|
||||
}
|
||||
|
@ -1007,8 +1020,8 @@ export default class Generator {
|
|||
const gAgent = this.toGAgent(agent);
|
||||
const gAgents = [gAgent];
|
||||
this.validateGAgents(gAgents, {
|
||||
allowGrouped: true,
|
||||
allowCovered: true,
|
||||
allowGrouped: true,
|
||||
});
|
||||
mergeSets(this.gAgents, gAgents, GAgent.equals);
|
||||
|
||||
|
@ -1086,10 +1099,10 @@ export default class Generator {
|
|||
|
||||
this.textFormatter = meta.textFormatter;
|
||||
const globals = this.beginNested('global', {
|
||||
tag: '',
|
||||
label: '',
|
||||
name: '',
|
||||
ln: 0,
|
||||
name: '',
|
||||
tag: '',
|
||||
});
|
||||
|
||||
stages.forEach(this.handleStage);
|
||||
|
@ -1117,12 +1130,12 @@ export default class Generator {
|
|||
swapFirstBegin(globals.stages, meta.headers || 'box');
|
||||
|
||||
return {
|
||||
meta: {
|
||||
title: this.textFormatter(meta.title),
|
||||
theme: meta.theme,
|
||||
code: meta.code,
|
||||
},
|
||||
agents: this.gAgents.slice(),
|
||||
meta: {
|
||||
code: meta.code,
|
||||
theme: meta.theme,
|
||||
title: this.textFormatter(meta.title),
|
||||
},
|
||||
stages: globals.stages,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
/* eslint-disable sort-keys */ // Maybe later
|
||||
|
||||
import {dom, textSizerFactory} from '../../../spec/stubs/TestDOM.mjs';
|
||||
import SVG from '../../svg/SVG.mjs';
|
||||
import parser from './MarkdownParser.mjs';
|
||||
|
@ -8,7 +6,7 @@ describe('Markdown Parser', () => {
|
|||
it('converts simple text', () => {
|
||||
const formatted = parser('hello everybody');
|
||||
|
||||
expect(formatted).toEqual([[{text: 'hello everybody', attrs: null}]]);
|
||||
expect(formatted).toEqual([[{attrs: null, text: 'hello everybody'}]]);
|
||||
});
|
||||
|
||||
it('produces an empty array given an empty input', () => {
|
||||
|
@ -21,8 +19,8 @@ describe('Markdown Parser', () => {
|
|||
const formatted = parser('hello\neverybody');
|
||||
|
||||
expect(formatted).toEqual([
|
||||
[{text: 'hello', attrs: null}],
|
||||
[{text: 'everybody', attrs: null}],
|
||||
[{attrs: null, text: 'hello'}],
|
||||
[{attrs: null, text: 'everybody'}],
|
||||
]);
|
||||
});
|
||||
|
||||
|
@ -30,11 +28,11 @@ describe('Markdown Parser', () => {
|
|||
const formatted = parser('a **b** c __d__ e');
|
||||
|
||||
expect(formatted).toEqual([[
|
||||
{text: 'a ', attrs: null},
|
||||
{text: 'b', attrs: {'font-weight': 'bolder'}},
|
||||
{text: ' c ', attrs: null},
|
||||
{text: 'd', attrs: {'font-weight': 'bolder'}},
|
||||
{text: ' e', attrs: null},
|
||||
{attrs: null, text: 'a '},
|
||||
{attrs: {'font-weight': 'bolder'}, text: 'b'},
|
||||
{attrs: null, text: ' c '},
|
||||
{attrs: {'font-weight': 'bolder'}, text: 'd'},
|
||||
{attrs: null, text: ' e'},
|
||||
]]);
|
||||
});
|
||||
|
||||
|
@ -42,7 +40,7 @@ describe('Markdown Parser', () => {
|
|||
const formatted = parser('a**b**c__d__e');
|
||||
|
||||
expect(formatted).toEqual([[
|
||||
{text: 'a**b**c__d__e', attrs: null},
|
||||
{attrs: null, text: 'a**b**c__d__e'},
|
||||
]]);
|
||||
});
|
||||
|
||||
|
@ -50,11 +48,11 @@ describe('Markdown Parser', () => {
|
|||
const formatted = parser('a **b\nc** d');
|
||||
|
||||
expect(formatted).toEqual([[
|
||||
{text: 'a ', attrs: null},
|
||||
{text: 'b', attrs: {'font-weight': 'bolder'}},
|
||||
{attrs: null, text: 'a '},
|
||||
{attrs: {'font-weight': 'bolder'}, text: 'b'},
|
||||
], [
|
||||
{text: 'c', attrs: {'font-weight': 'bolder'}},
|
||||
{text: ' d', attrs: null},
|
||||
{attrs: {'font-weight': 'bolder'}, text: 'c'},
|
||||
{attrs: null, text: ' d'},
|
||||
]]);
|
||||
});
|
||||
|
||||
|
@ -62,11 +60,11 @@ describe('Markdown Parser', () => {
|
|||
const formatted = parser('a *b* c _d_ e');
|
||||
|
||||
expect(formatted).toEqual([[
|
||||
{text: 'a ', attrs: null},
|
||||
{text: 'b', attrs: {'font-style': 'italic'}},
|
||||
{text: ' c ', attrs: null},
|
||||
{text: 'd', attrs: {'font-style': 'italic'}},
|
||||
{text: ' e', attrs: null},
|
||||
{attrs: null, text: 'a '},
|
||||
{attrs: {'font-style': 'italic'}, text: 'b'},
|
||||
{attrs: null, text: ' c '},
|
||||
{attrs: {'font-style': 'italic'}, text: 'd'},
|
||||
{attrs: null, text: ' e'},
|
||||
]]);
|
||||
});
|
||||
|
||||
|
@ -74,9 +72,9 @@ describe('Markdown Parser', () => {
|
|||
const formatted = parser('a ~b~ c');
|
||||
|
||||
expect(formatted).toEqual([[
|
||||
{text: 'a ', attrs: null},
|
||||
{text: 'b', attrs: {'text-decoration': 'line-through'}},
|
||||
{text: ' c', attrs: null},
|
||||
{attrs: null, text: 'a '},
|
||||
{attrs: {'text-decoration': 'line-through'}, text: 'b'},
|
||||
{attrs: null, text: ' c'},
|
||||
]]);
|
||||
});
|
||||
|
||||
|
@ -84,9 +82,9 @@ describe('Markdown Parser', () => {
|
|||
const formatted = parser('a `b` c');
|
||||
|
||||
expect(formatted).toEqual([[
|
||||
{text: 'a ', attrs: null},
|
||||
{text: 'b', attrs: {'font-family': 'monospace'}},
|
||||
{text: ' c', attrs: null},
|
||||
{attrs: null, text: 'a '},
|
||||
{attrs: {'font-family': 'monospace'}, text: 'b'},
|
||||
{attrs: null, text: ' c'},
|
||||
]]);
|
||||
});
|
||||
|
||||
|
@ -94,9 +92,9 @@ describe('Markdown Parser', () => {
|
|||
const formatted = parser('a.`b`.c');
|
||||
|
||||
expect(formatted).toEqual([[
|
||||
{text: 'a.', attrs: null},
|
||||
{text: 'b', attrs: {'font-family': 'monospace'}},
|
||||
{text: '.c', attrs: null},
|
||||
{attrs: null, text: 'a.'},
|
||||
{attrs: {'font-family': 'monospace'}, text: 'b'},
|
||||
{attrs: null, text: '.c'},
|
||||
]]);
|
||||
});
|
||||
|
||||
|
@ -104,12 +102,12 @@ describe('Markdown Parser', () => {
|
|||
const formatted = parser('a **_b_ c**');
|
||||
|
||||
expect(formatted).toEqual([[
|
||||
{text: 'a ', attrs: null},
|
||||
{text: 'b', attrs: {
|
||||
'font-weight': 'bolder',
|
||||
{attrs: null, text: 'a '},
|
||||
{attrs: {
|
||||
'font-style': 'italic',
|
||||
}},
|
||||
{text: ' c', attrs: {'font-weight': 'bolder'}},
|
||||
'font-weight': 'bolder',
|
||||
}, text: 'b'},
|
||||
{attrs: {'font-weight': 'bolder'}, text: ' c'},
|
||||
]]);
|
||||
});
|
||||
|
||||
|
@ -117,16 +115,16 @@ describe('Markdown Parser', () => {
|
|||
const formatted = parser('_a **b_ ~c~**');
|
||||
|
||||
expect(formatted).toEqual([[
|
||||
{text: 'a ', attrs: {'font-style': 'italic'}},
|
||||
{text: 'b', attrs: {
|
||||
'font-weight': 'bolder',
|
||||
{attrs: {'font-style': 'italic'}, text: 'a '},
|
||||
{attrs: {
|
||||
'font-style': 'italic',
|
||||
}},
|
||||
{text: ' ', attrs: {'font-weight': 'bolder'}},
|
||||
{text: 'c', attrs: {
|
||||
'font-weight': 'bolder',
|
||||
}, text: 'b'},
|
||||
{attrs: {'font-weight': 'bolder'}, text: ' '},
|
||||
{attrs: {
|
||||
'font-weight': 'bolder',
|
||||
'text-decoration': 'line-through',
|
||||
}},
|
||||
}, text: 'c'},
|
||||
]]);
|
||||
});
|
||||
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
/* eslint-disable max-lines */
|
||||
/* eslint-disable sort-keys */ // Maybe later
|
||||
/* eslint-disable complexity */ // Temporary ignore while switching linter
|
||||
/* eslint-disable no-param-reassign */ // Also temporary
|
||||
|
||||
import {combine, last} from '../../core/ArrayUtilities.mjs';
|
||||
import Tokeniser from './Tokeniser.mjs';
|
||||
|
@ -90,17 +87,47 @@ const NOTE_TYPES = {
|
|||
'text': {
|
||||
mode: 'text',
|
||||
types: {
|
||||
'left': {type: 'note left', skip: ['of'], min: 0, max: null},
|
||||
'right': {type: 'note right', skip: ['of'], min: 0, max: null},
|
||||
'left': {
|
||||
type: 'note left',
|
||||
skip: ['of'],
|
||||
min: 0,
|
||||
max: Number.POSITIVE_INFINITY,
|
||||
},
|
||||
'right': {
|
||||
type: 'note right',
|
||||
skip: ['of'],
|
||||
min: 0,
|
||||
max: Number.POSITIVE_INFINITY,
|
||||
},
|
||||
},
|
||||
},
|
||||
'note': {
|
||||
mode: 'note',
|
||||
types: {
|
||||
'over': {type: 'note over', skip: [], min: 0, max: null},
|
||||
'left': {type: 'note left', skip: ['of'], min: 0, max: null},
|
||||
'right': {type: 'note right', skip: ['of'], min: 0, max: null},
|
||||
'between': {type: 'note between', skip: [], min: 2, max: null},
|
||||
'over': {
|
||||
type: 'note over',
|
||||
skip: [],
|
||||
min: 0,
|
||||
max: Number.POSITIVE_INFINITY,
|
||||
},
|
||||
'left': {
|
||||
type: 'note left',
|
||||
skip: ['of'],
|
||||
min: 0,
|
||||
max: Number.POSITIVE_INFINITY,
|
||||
},
|
||||
'right': {
|
||||
type: 'note right',
|
||||
skip: ['of'],
|
||||
min: 0,
|
||||
max: Number.POSITIVE_INFINITY,
|
||||
},
|
||||
'between': {
|
||||
type: 'note between',
|
||||
skip: [],
|
||||
min: 2,
|
||||
max: Number.POSITIVE_INFINITY,
|
||||
},
|
||||
},
|
||||
},
|
||||
'state': {
|
||||
|
@ -135,15 +162,20 @@ function makeError(message, token = null) {
|
|||
return new Error(message + suffix);
|
||||
}
|
||||
|
||||
function joinLabel(line, begin = 0, end = null) {
|
||||
function endIndexInLine(line, end = null) {
|
||||
if(end === null) {
|
||||
end = line.length;
|
||||
return line.length;
|
||||
}
|
||||
if(end <= begin) {
|
||||
return end;
|
||||
}
|
||||
|
||||
function joinLabel(line, begin = 0, end = null) {
|
||||
const e = endIndexInLine(line, end);
|
||||
if(e <= begin) {
|
||||
return '';
|
||||
}
|
||||
let result = line[begin].v;
|
||||
for(let i = begin + 1; i < end; ++ i) {
|
||||
for(let i = begin + 1; i < e; ++ i) {
|
||||
result += line[i].s + line[i].v;
|
||||
}
|
||||
return result;
|
||||
|
@ -179,30 +211,23 @@ function skipOver(line, start, skip, error = null) {
|
|||
return start + skip.length;
|
||||
}
|
||||
|
||||
function findToken(line, tokens, {
|
||||
function findTokens(line, tokens, {
|
||||
start = 0,
|
||||
limit = null,
|
||||
orEnd = false,
|
||||
} = {}) {
|
||||
if(limit === null) {
|
||||
limit = line.length;
|
||||
}
|
||||
if(!Array.isArray(tokens)) {
|
||||
tokens = [tokens];
|
||||
}
|
||||
for(let i = start; i <= limit - tokens.length; ++ i) {
|
||||
const e = endIndexInLine(line, limit);
|
||||
for(let i = start; i <= e - tokens.length; ++ i) {
|
||||
if(skipOver(line, i, tokens) !== i) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return orEnd ? limit : -1;
|
||||
return orEnd ? e : -1;
|
||||
}
|
||||
|
||||
function findFirstToken(line, tokenMap, {start = 0, limit = null} = {}) {
|
||||
if(limit === null) {
|
||||
limit = line.length;
|
||||
}
|
||||
for(let pos = start; pos < limit; ++ pos) {
|
||||
const e = endIndexInLine(line, limit);
|
||||
for(let pos = start; pos < e; ++ pos) {
|
||||
const value = tokenMap.get(tokenKeyword(line[pos]));
|
||||
if(value) {
|
||||
return {pos, value};
|
||||
|
@ -212,12 +237,9 @@ function findFirstToken(line, tokenMap, {start = 0, limit = null} = {}) {
|
|||
}
|
||||
|
||||
function readAgentAlias(line, start, end, {enableAlias, allowBlankName}) {
|
||||
let aliasSep = -1;
|
||||
let aliasSep = end;
|
||||
if(enableAlias) {
|
||||
aliasSep = findToken(line, 'as', {start});
|
||||
}
|
||||
if(aliasSep === -1 || aliasSep >= end) {
|
||||
aliasSep = end;
|
||||
aliasSep = findTokens(line, ['as'], {limit: end, orEnd: true, start});
|
||||
}
|
||||
if(start >= aliasSep && !allowBlankName) {
|
||||
let errPosToken = line[start];
|
||||
|
@ -286,29 +308,17 @@ function readAgentList(line, start, end, readAgentOpts) {
|
|||
}
|
||||
|
||||
const PARSERS = [
|
||||
(line, meta) => { // Title
|
||||
if(tokenKeyword(line[0]) !== 'title') {
|
||||
return null;
|
||||
}
|
||||
|
||||
{begin: ['title'], fn: (line, meta) => { // Title
|
||||
meta.title = joinLabel(line, 1);
|
||||
return true;
|
||||
},
|
||||
|
||||
(line, meta) => { // Theme
|
||||
if(tokenKeyword(line[0]) !== 'theme') {
|
||||
return null;
|
||||
}
|
||||
}},
|
||||
|
||||
{begin: ['theme'], fn: (line, meta) => { // Theme
|
||||
meta.theme = joinLabel(line, 1);
|
||||
return true;
|
||||
},
|
||||
|
||||
(line, meta) => { // Terminators
|
||||
if(tokenKeyword(line[0]) !== 'terminators') {
|
||||
return null;
|
||||
}
|
||||
}},
|
||||
|
||||
{begin: ['terminators'], fn: (line, meta) => { // Terminators
|
||||
const type = tokenKeyword(line[1]);
|
||||
if(!type) {
|
||||
throw makeError('Unspecified termination', line[0]);
|
||||
|
@ -318,13 +328,9 @@ const PARSERS = [
|
|||
}
|
||||
meta.terminators = type;
|
||||
return true;
|
||||
},
|
||||
|
||||
(line, meta) => { // Headers
|
||||
if(tokenKeyword(line[0]) !== 'headers') {
|
||||
return null;
|
||||
}
|
||||
}},
|
||||
|
||||
{begin: ['headers'], fn: (line, meta) => { // Headers
|
||||
const type = tokenKeyword(line[1]);
|
||||
if(!type) {
|
||||
throw makeError('Unspecified header', line[0]);
|
||||
|
@ -334,15 +340,11 @@ const PARSERS = [
|
|||
}
|
||||
meta.headers = type;
|
||||
return true;
|
||||
},
|
||||
}},
|
||||
|
||||
(line) => { // Divider
|
||||
if(tokenKeyword(line[0]) !== 'divider') {
|
||||
return null;
|
||||
}
|
||||
|
||||
const labelSep = findToken(line, ':', {orEnd: true});
|
||||
const heightSep = findToken(line, ['with', 'height'], {
|
||||
{begin: ['divider'], fn: (line) => { // Divider
|
||||
const labelSep = findTokens(line, [':'], {orEnd: true});
|
||||
const heightSep = findTokens(line, ['with', 'height'], {
|
||||
limit: labelSep,
|
||||
orEnd: true,
|
||||
});
|
||||
|
@ -368,13 +370,9 @@ const PARSERS = [
|
|||
height,
|
||||
label: joinLabel(line, labelSep + 1),
|
||||
};
|
||||
},
|
||||
|
||||
(line) => { // Autolabel
|
||||
if(tokenKeyword(line[0]) !== 'autolabel') {
|
||||
return null;
|
||||
}
|
||||
}},
|
||||
|
||||
{begin: ['autolabel'], fn: (line) => { // Autolabel
|
||||
let raw = null;
|
||||
if(tokenKeyword(line[1]) === 'off') {
|
||||
raw = '<label>';
|
||||
|
@ -385,13 +383,16 @@ const PARSERS = [
|
|||
type: 'label pattern',
|
||||
pattern: labelPatternParser(raw),
|
||||
};
|
||||
},
|
||||
}},
|
||||
|
||||
(line) => { // Block
|
||||
if(tokenKeyword(line[0]) === 'end' && line.length === 1) {
|
||||
return {type: 'block end'};
|
||||
{begin: ['end'], fn: (line) => { // Block End
|
||||
if(line.length !== 1) {
|
||||
return null;
|
||||
}
|
||||
return {type: 'block end'};
|
||||
}},
|
||||
|
||||
{begin: [], fn: (line) => { // Block
|
||||
const type = BLOCK_TYPES[tokenKeyword(line[0])];
|
||||
if(!type) {
|
||||
return null;
|
||||
|
@ -407,17 +408,11 @@ const PARSERS = [
|
|||
tag: type.tag,
|
||||
label: joinLabel(line, skip),
|
||||
};
|
||||
},
|
||||
}},
|
||||
|
||||
(line) => { // Begin reference
|
||||
if(
|
||||
tokenKeyword(line[0]) !== 'begin' ||
|
||||
tokenKeyword(line[1]) !== 'reference'
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
{begin: ['begin', 'reference'], fn: (line) => { // Begin reference
|
||||
let agents = [];
|
||||
const labelSep = findToken(line, ':');
|
||||
const labelSep = findTokens(line, [':']);
|
||||
if(tokenKeyword(line[2]) === 'over' && labelSep > 3) {
|
||||
agents = readAgentList(line, 3, labelSep);
|
||||
} else if(labelSep !== 2) {
|
||||
|
@ -440,9 +435,9 @@ const PARSERS = [
|
|||
label: def.name,
|
||||
alias: def.alias,
|
||||
};
|
||||
},
|
||||
}},
|
||||
|
||||
(line) => { // Agent
|
||||
{begin: [], fn: (line) => { // Agent
|
||||
const type = AGENT_MANIPULATION_TYPES[tokenKeyword(line[0])];
|
||||
if(!type || line.length <= 1) {
|
||||
return null;
|
||||
|
@ -450,12 +445,9 @@ const PARSERS = [
|
|||
return Object.assign({
|
||||
agents: readAgentList(line, 1, line.length, {aliases: true}),
|
||||
}, type);
|
||||
},
|
||||
}},
|
||||
|
||||
(line) => { // Async
|
||||
if(tokenKeyword(line[0]) !== 'simultaneously') {
|
||||
return null;
|
||||
}
|
||||
{begin: ['simultaneously'], fn: (line) => { // Async
|
||||
if(tokenKeyword(last(line)) !== ':') {
|
||||
return null;
|
||||
}
|
||||
|
@ -470,11 +462,11 @@ const PARSERS = [
|
|||
type: 'async',
|
||||
target,
|
||||
};
|
||||
},
|
||||
}},
|
||||
|
||||
(line) => { // Note
|
||||
{begin: [], fn: (line) => { // Note
|
||||
const mode = NOTE_TYPES[tokenKeyword(line[0])];
|
||||
const labelSep = findToken(line, ':');
|
||||
const labelSep = findTokens(line, [':']);
|
||||
if(!mode || labelSep === -1) {
|
||||
return null;
|
||||
}
|
||||
|
@ -488,7 +480,7 @@ const PARSERS = [
|
|||
if(agents.length < type.min) {
|
||||
throw makeError('Too few agents for ' + mode.mode, line[0]);
|
||||
}
|
||||
if(type.max !== null && agents.length > type.max) {
|
||||
if(agents.length > type.max) {
|
||||
throw makeError('Too many agents for ' + mode.mode, line[0]);
|
||||
}
|
||||
return {
|
||||
|
@ -497,10 +489,10 @@ const PARSERS = [
|
|||
mode: mode.mode,
|
||||
label: joinLabel(line, labelSep + 1),
|
||||
};
|
||||
},
|
||||
}},
|
||||
|
||||
(line) => { // Connect
|
||||
const labelSep = findToken(line, ':', {orEnd: true});
|
||||
{begin: [], fn: (line) => { // Connect
|
||||
const labelSep = findTokens(line, [':'], {orEnd: true});
|
||||
const connectionToken = findFirstToken(
|
||||
line,
|
||||
CONNECT.types,
|
||||
|
@ -548,9 +540,9 @@ const PARSERS = [
|
|||
options: connectionToken.value,
|
||||
};
|
||||
}
|
||||
},
|
||||
}},
|
||||
|
||||
(line) => { // Marker
|
||||
{begin: [], fn: (line) => { // Marker
|
||||
if(line.length < 2 || tokenKeyword(last(line)) !== ':') {
|
||||
return null;
|
||||
}
|
||||
|
@ -558,10 +550,10 @@ const PARSERS = [
|
|||
type: 'mark',
|
||||
name: joinLabel(line, 0, line.length - 1),
|
||||
};
|
||||
},
|
||||
}},
|
||||
|
||||
(line) => { // Options
|
||||
const sepPos = findToken(line, 'is');
|
||||
{begin: [], fn: (line) => { // Options
|
||||
const sepPos = findTokens(line, ['is']);
|
||||
if(sepPos < 1) {
|
||||
return null;
|
||||
}
|
||||
|
@ -583,13 +575,16 @@ const PARSERS = [
|
|||
agent,
|
||||
options,
|
||||
};
|
||||
},
|
||||
}},
|
||||
];
|
||||
|
||||
function parseLine(line, {meta, stages}) {
|
||||
let stage = null;
|
||||
for(let i = 0; i < PARSERS.length; ++ i) {
|
||||
stage = PARSERS[i](line, meta);
|
||||
for(const {begin, fn} of PARSERS) {
|
||||
if(skipOver(line, 0, begin) !== begin.length) {
|
||||
continue;
|
||||
}
|
||||
stage = fn(line, meta);
|
||||
if(stage) {
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -140,6 +140,16 @@ describe('Sequence Parser', () => {
|
|||
});
|
||||
|
||||
it('propagates aliases', () => {
|
||||
const parsed = parser.parse('define Foo Bar as A');
|
||||
|
||||
expect(parsed.stages).toEqual([
|
||||
{type: 'agent define', ln: jasmine.anything(), agents: [
|
||||
{name: 'Foo Bar', alias: 'A', flags: []},
|
||||
]},
|
||||
]);
|
||||
});
|
||||
|
||||
it('propagates long aliases', () => {
|
||||
const parsed = parser.parse('define Foo Bar as A B');
|
||||
|
||||
expect(parsed.stages).toEqual([
|
||||
|
@ -149,6 +159,16 @@ describe('Sequence Parser', () => {
|
|||
]);
|
||||
});
|
||||
|
||||
it('ignores missing aliases', () => {
|
||||
const parsed = parser.parse('define Foo Bar as');
|
||||
|
||||
expect(parsed.stages).toEqual([
|
||||
{type: 'agent define', ln: jasmine.anything(), agents: [
|
||||
{name: 'Foo Bar', alias: '', flags: []},
|
||||
]},
|
||||
]);
|
||||
});
|
||||
|
||||
it('propagates agent options', () => {
|
||||
const parsed = parser.parse('Foo bar is zig zag');
|
||||
|
||||
|
@ -256,6 +276,12 @@ describe('Sequence Parser', () => {
|
|||
));
|
||||
});
|
||||
|
||||
it('rejects missing agent names with aliases', () => {
|
||||
expect(() => parser.parse('define as A')).toThrow(new Error(
|
||||
'Missing agent name at line 1, character 7'
|
||||
));
|
||||
});
|
||||
|
||||
it('parses source agents', () => {
|
||||
const parsed = parser.parse('A -> *');
|
||||
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
/* eslint-disable max-lines */
|
||||
|
||||
import './components/AgentCap.mjs';
|
||||
import './components/AgentHighlight.mjs';
|
||||
import './components/Block.mjs';
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
/* eslint-disable sort-keys */ // Maybe later
|
||||
|
||||
import {
|
||||
VirtualDocument,
|
||||
textSizerFactory,
|
||||
|
@ -13,8 +11,8 @@ describe('Sequence Renderer', () => {
|
|||
beforeEach(() => {
|
||||
renderer = new Renderer({
|
||||
document: new VirtualDocument(),
|
||||
themes: [new BasicThemeFactory()],
|
||||
textSizerFactory,
|
||||
themes: [new BasicThemeFactory()],
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -28,14 +26,14 @@ describe('Sequence Renderer', () => {
|
|||
|
||||
const GENERATED = {
|
||||
connect: (agentIDs, label = []) => ({
|
||||
type: 'connect',
|
||||
agentIDs,
|
||||
label,
|
||||
options: {
|
||||
line: 'solid',
|
||||
left: 0,
|
||||
line: 'solid',
|
||||
right: 1,
|
||||
},
|
||||
type: 'connect',
|
||||
}),
|
||||
};
|
||||
|
||||
|
@ -49,30 +47,30 @@ describe('Sequence Renderer', () => {
|
|||
describe('.render', () => {
|
||||
it('populates the SVG with content', () => {
|
||||
renderer.render({
|
||||
meta: {title: format('Title')},
|
||||
agents: [
|
||||
{
|
||||
id: '[',
|
||||
formattedLabel: null,
|
||||
anchorRight: true,
|
||||
options: [],
|
||||
}, {
|
||||
id: 'Col 1',
|
||||
formattedLabel: format('Col 1!'),
|
||||
anchorRight: false,
|
||||
options: [],
|
||||
}, {
|
||||
id: 'Col 2',
|
||||
formattedLabel: format('Col 2!'),
|
||||
anchorRight: false,
|
||||
options: [],
|
||||
}, {
|
||||
id: ']',
|
||||
formattedLabel: null,
|
||||
id: '[',
|
||||
options: [],
|
||||
}, {
|
||||
anchorRight: false,
|
||||
formattedLabel: format('Col 1!'),
|
||||
id: 'Col 1',
|
||||
options: [],
|
||||
}, {
|
||||
anchorRight: false,
|
||||
formattedLabel: format('Col 2!'),
|
||||
id: 'Col 2',
|
||||
options: [],
|
||||
}, {
|
||||
anchorRight: false,
|
||||
formattedLabel: null,
|
||||
id: ']',
|
||||
options: [],
|
||||
},
|
||||
],
|
||||
meta: {title: format('Title')},
|
||||
stages: [],
|
||||
});
|
||||
const element = renderer.dom();
|
||||
|
@ -83,20 +81,20 @@ describe('Sequence Renderer', () => {
|
|||
|
||||
it('adds the code as metadata', () => {
|
||||
renderer.render({
|
||||
meta: {title: [], code: 'hello'},
|
||||
agents: [
|
||||
{
|
||||
id: '[',
|
||||
formattedLabel: null,
|
||||
anchorRight: true,
|
||||
formattedLabel: null,
|
||||
id: '[',
|
||||
options: [],
|
||||
}, {
|
||||
id: ']',
|
||||
formattedLabel: null,
|
||||
anchorRight: false,
|
||||
formattedLabel: null,
|
||||
id: ']',
|
||||
options: [],
|
||||
},
|
||||
],
|
||||
meta: {code: 'hello', title: []},
|
||||
stages: [],
|
||||
});
|
||||
const element = renderer.dom();
|
||||
|
@ -111,34 +109,34 @@ describe('Sequence Renderer', () => {
|
|||
*/
|
||||
|
||||
renderer.render({
|
||||
meta: {title: []},
|
||||
agents: [
|
||||
{
|
||||
id: '[',
|
||||
formattedLabel: null,
|
||||
anchorRight: true,
|
||||
options: [],
|
||||
}, {
|
||||
id: 'A',
|
||||
formattedLabel: format('A!'),
|
||||
anchorRight: false,
|
||||
options: [],
|
||||
}, {
|
||||
id: 'B',
|
||||
formattedLabel: format('B!'),
|
||||
anchorRight: false,
|
||||
options: [],
|
||||
}, {
|
||||
id: ']',
|
||||
formattedLabel: null,
|
||||
id: '[',
|
||||
options: [],
|
||||
}, {
|
||||
anchorRight: false,
|
||||
formattedLabel: format('A!'),
|
||||
id: 'A',
|
||||
options: [],
|
||||
}, {
|
||||
anchorRight: false,
|
||||
formattedLabel: format('B!'),
|
||||
id: 'B',
|
||||
options: [],
|
||||
}, {
|
||||
anchorRight: false,
|
||||
formattedLabel: null,
|
||||
id: ']',
|
||||
options: [],
|
||||
},
|
||||
],
|
||||
meta: {title: []},
|
||||
stages: [
|
||||
{type: 'agent begin', agentIDs: ['A', 'B'], mode: 'box'},
|
||||
{agentIDs: ['A', 'B'], mode: 'box', type: 'agent begin'},
|
||||
GENERATED.connect(['A', 'B']),
|
||||
{type: 'agent end', agentIDs: ['A', 'B'], mode: 'none'},
|
||||
{agentIDs: ['A', 'B'], mode: 'none', type: 'agent end'},
|
||||
],
|
||||
});
|
||||
|
||||
|
@ -158,49 +156,49 @@ describe('Sequence Renderer', () => {
|
|||
*/
|
||||
|
||||
renderer.render({
|
||||
meta: {title: []},
|
||||
agents: [
|
||||
{
|
||||
id: '[',
|
||||
formattedLabel: null,
|
||||
anchorRight: true,
|
||||
options: [],
|
||||
}, {
|
||||
id: 'A',
|
||||
formattedLabel: format('A!'),
|
||||
anchorRight: false,
|
||||
options: [],
|
||||
}, {
|
||||
id: 'B',
|
||||
formattedLabel: format('B!'),
|
||||
anchorRight: false,
|
||||
options: [],
|
||||
}, {
|
||||
id: 'C',
|
||||
formattedLabel: format('C!'),
|
||||
anchorRight: false,
|
||||
options: [],
|
||||
}, {
|
||||
id: ']',
|
||||
formattedLabel: null,
|
||||
id: '[',
|
||||
options: [],
|
||||
}, {
|
||||
anchorRight: false,
|
||||
formattedLabel: format('A!'),
|
||||
id: 'A',
|
||||
options: [],
|
||||
}, {
|
||||
anchorRight: false,
|
||||
formattedLabel: format('B!'),
|
||||
id: 'B',
|
||||
options: [],
|
||||
}, {
|
||||
anchorRight: false,
|
||||
formattedLabel: format('C!'),
|
||||
id: 'C',
|
||||
options: [],
|
||||
}, {
|
||||
anchorRight: false,
|
||||
formattedLabel: null,
|
||||
id: ']',
|
||||
options: [],
|
||||
},
|
||||
],
|
||||
meta: {title: []},
|
||||
stages: [
|
||||
{
|
||||
type: 'agent begin',
|
||||
agentIDs: ['A', 'B', 'C'],
|
||||
mode: 'box',
|
||||
type: 'agent begin',
|
||||
},
|
||||
GENERATED.connect(['[', 'A']),
|
||||
GENERATED.connect(['A', 'B']),
|
||||
GENERATED.connect(['B', 'C']),
|
||||
GENERATED.connect(['C', ']']),
|
||||
{
|
||||
type: 'agent end',
|
||||
agentIDs: ['A', 'B', 'C'],
|
||||
mode: 'none',
|
||||
type: 'agent end',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
@ -228,50 +226,50 @@ describe('Sequence Renderer', () => {
|
|||
*/
|
||||
|
||||
renderer.render({
|
||||
meta: {title: []},
|
||||
agents: [
|
||||
{
|
||||
id: '[',
|
||||
formattedLabel: null,
|
||||
anchorRight: true,
|
||||
options: [],
|
||||
}, {
|
||||
id: 'A',
|
||||
formattedLabel: format('A!'),
|
||||
anchorRight: false,
|
||||
options: [],
|
||||
}, {
|
||||
id: 'B',
|
||||
formattedLabel: format('B!'),
|
||||
anchorRight: false,
|
||||
options: [],
|
||||
}, {
|
||||
id: 'C',
|
||||
formattedLabel: format('C!'),
|
||||
anchorRight: false,
|
||||
options: [],
|
||||
}, {
|
||||
id: 'D',
|
||||
formattedLabel: format('D!'),
|
||||
anchorRight: false,
|
||||
options: [],
|
||||
}, {
|
||||
id: ']',
|
||||
formattedLabel: null,
|
||||
id: '[',
|
||||
options: [],
|
||||
}, {
|
||||
anchorRight: false,
|
||||
formattedLabel: format('A!'),
|
||||
id: 'A',
|
||||
options: [],
|
||||
}, {
|
||||
anchorRight: false,
|
||||
formattedLabel: format('B!'),
|
||||
id: 'B',
|
||||
options: [],
|
||||
}, {
|
||||
anchorRight: false,
|
||||
formattedLabel: format('C!'),
|
||||
id: 'C',
|
||||
options: [],
|
||||
}, {
|
||||
anchorRight: false,
|
||||
formattedLabel: format('D!'),
|
||||
id: 'D',
|
||||
options: [],
|
||||
}, {
|
||||
anchorRight: false,
|
||||
formattedLabel: null,
|
||||
id: ']',
|
||||
options: [],
|
||||
},
|
||||
],
|
||||
meta: {title: []},
|
||||
stages: [
|
||||
{type: 'agent begin', agentIDs: ['A', 'B'], mode: 'box'},
|
||||
{agentIDs: ['A', 'B'], mode: 'box', type: 'agent begin'},
|
||||
GENERATED.connect(['A', 'B'], format('short')),
|
||||
{type: 'agent end', agentIDs: ['B'], mode: 'cross'},
|
||||
{type: 'agent begin', agentIDs: ['C'], mode: 'box'},
|
||||
{agentIDs: ['B'], mode: 'cross', type: 'agent end'},
|
||||
{agentIDs: ['C'], mode: 'box', type: 'agent begin'},
|
||||
GENERATED.connect(['A', 'C'], format('long description')),
|
||||
{type: 'agent end', agentIDs: ['C'], mode: 'cross'},
|
||||
{type: 'agent begin', agentIDs: ['D'], mode: 'box'},
|
||||
{agentIDs: ['C'], mode: 'cross', type: 'agent end'},
|
||||
{agentIDs: ['D'], mode: 'box', type: 'agent begin'},
|
||||
GENERATED.connect(['A', 'D'], format('short again')),
|
||||
{type: 'agent end', agentIDs: ['A', 'D'], mode: 'cross'},
|
||||
{agentIDs: ['A', 'D'], mode: 'cross', type: 'agent end'},
|
||||
],
|
||||
});
|
||||
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
/* eslint-disable sort-keys */ // Maybe later
|
||||
|
||||
import BaseComponent, {register} from './BaseComponent.mjs';
|
||||
import {mergeSets} from '../../../core/ArrayUtilities.mjs';
|
||||
|
||||
|
@ -36,11 +34,11 @@ class Arrowhead {
|
|||
const config = this.getConfig(theme);
|
||||
const short = this.short(theme);
|
||||
layer.add(config.render(config.attrs, {
|
||||
dir,
|
||||
height: config.height,
|
||||
width: config.width,
|
||||
x: pt.x + short * dir.dx,
|
||||
y: pt.y + short * dir.dy,
|
||||
width: config.width,
|
||||
height: config.height,
|
||||
dir,
|
||||
}));
|
||||
}
|
||||
|
||||
|
@ -74,9 +72,9 @@ class Arrowcross {
|
|||
render(layer, theme, pt, dir) {
|
||||
const config = this.getConfig(theme);
|
||||
layer.add(config.render({
|
||||
radius: config.radius,
|
||||
x: pt.x + config.short * dir.dx,
|
||||
y: pt.y + config.short * dir.dy,
|
||||
radius: config.radius,
|
||||
}));
|
||||
}
|
||||
|
||||
|
@ -96,10 +94,10 @@ class Arrowcross {
|
|||
|
||||
const ARROWHEADS = [
|
||||
{
|
||||
render: () => null,
|
||||
width: () => 0,
|
||||
height: () => 0,
|
||||
lineGap: () => 0,
|
||||
render: () => null,
|
||||
width: () => 0,
|
||||
},
|
||||
new Arrowhead('single'),
|
||||
new Arrowhead('double'),
|
||||
|
@ -184,12 +182,12 @@ export class Connect extends BaseComponent {
|
|||
const dx1 = lArrow.lineGap(env.theme, line.attrs);
|
||||
const dx2 = rArrow.lineGap(env.theme, line.attrs);
|
||||
const rendered = line.renderRev(line.attrs, {
|
||||
x1: x1 + dx1,
|
||||
y1,
|
||||
x2: x2 + dx2,
|
||||
y2,
|
||||
xR,
|
||||
rad: config.loopbackRadius,
|
||||
x1: x1 + dx1,
|
||||
x2: x2 + dx2,
|
||||
xR,
|
||||
y1,
|
||||
y2,
|
||||
});
|
||||
clickable.add(rendered.shape);
|
||||
|
||||
|
@ -225,9 +223,9 @@ export class Connect extends BaseComponent {
|
|||
);
|
||||
|
||||
const renderedText = env.svg.boxedText({
|
||||
padding: config.mask.padding,
|
||||
boxAttrs: {'fill': '#000000'},
|
||||
labelAttrs: config.label.loopbackAttrs,
|
||||
padding: config.mask.padding,
|
||||
}, label, {
|
||||
x: xL - config.mask.padding.left,
|
||||
y: yBegin - height + config.label.margin.top,
|
||||
|
@ -251,20 +249,20 @@ export class Connect extends BaseComponent {
|
|||
env.lineMaskLayer.add(renderedText.box);
|
||||
const clickable = env.makeRegion().add(
|
||||
env.svg.box(OUTLINE_ATTRS, {
|
||||
'height': raise + env.primaryY - yBegin + arrowDip,
|
||||
'width': xR + config.loopbackRadius - from.x,
|
||||
'x': from.x,
|
||||
'y': yBegin - raise,
|
||||
'width': xR + config.loopbackRadius - from.x,
|
||||
'height': raise + env.primaryY - yBegin + arrowDip,
|
||||
}),
|
||||
renderedText.label
|
||||
);
|
||||
|
||||
this.renderRevArrowLine({
|
||||
x1: from.x + from.currentMaxRad,
|
||||
y1: yBegin,
|
||||
x2: to.x + to.currentMaxRad,
|
||||
y2: env.primaryY,
|
||||
xR,
|
||||
y1: yBegin,
|
||||
y2: env.primaryY,
|
||||
}, options, env, clickable);
|
||||
|
||||
return (
|
||||
|
@ -291,8 +289,8 @@ export class Connect extends BaseComponent {
|
|||
|
||||
const rendered = line.renderFlat(line.attrs, {
|
||||
x1: x1 + d1 * dx,
|
||||
y1: y1 + d1 * dy,
|
||||
x2: x2 - d2 * dx,
|
||||
y1: y1 + d1 * dy,
|
||||
y2: y2 - d2 * dy,
|
||||
});
|
||||
clickable.add(rendered.shape);
|
||||
|
@ -304,9 +302,9 @@ export class Connect extends BaseComponent {
|
|||
rArrow.render(clickable, env.theme, p2, {dx: -dx, dy: -dy});
|
||||
|
||||
return {
|
||||
lArrow,
|
||||
p1,
|
||||
p2,
|
||||
lArrow,
|
||||
rArrow,
|
||||
};
|
||||
}
|
||||
|
@ -316,16 +314,16 @@ export class Connect extends BaseComponent {
|
|||
|
||||
if(from.isVirtualSource) {
|
||||
clickable.add(config.render({
|
||||
radius: config.radius,
|
||||
x: rendered.p1.x - config.radius,
|
||||
y: rendered.p1.y,
|
||||
radius: config.radius,
|
||||
}));
|
||||
}
|
||||
if(to.isVirtualSource) {
|
||||
clickable.add(config.render({
|
||||
radius: config.radius,
|
||||
x: rendered.p2.x + config.radius,
|
||||
y: rendered.p2.y,
|
||||
radius: config.radius,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
@ -352,9 +350,9 @@ export class Connect extends BaseComponent {
|
|||
}
|
||||
|
||||
const text = env.svg.boxedText({
|
||||
padding: config.mask.padding,
|
||||
boxAttrs,
|
||||
labelAttrs: config.label.attrs,
|
||||
padding: config.mask.padding,
|
||||
}, label, {
|
||||
x: midX,
|
||||
y: midY + config.label.margin.top - height,
|
||||
|
@ -382,8 +380,8 @@ export class Connect extends BaseComponent {
|
|||
|
||||
const rendered = this.renderArrowLine({
|
||||
x1,
|
||||
y1: yBegin,
|
||||
x2,
|
||||
y1: yBegin,
|
||||
y2: env.primaryY,
|
||||
}, options, env, clickable);
|
||||
|
||||
|
@ -394,7 +392,7 @@ export class Connect extends BaseComponent {
|
|||
|
||||
const lift = Math.max(height, arrowSpread);
|
||||
|
||||
this.renderVirtualSources({from, to, rendered}, env, clickable);
|
||||
this.renderVirtualSources({from, rendered, to}, env, clickable);
|
||||
|
||||
clickable.add(env.svg.el('path')
|
||||
.attrs(OUTLINE_ATTRS)
|
||||
|
@ -407,12 +405,12 @@ export class Connect extends BaseComponent {
|
|||
)));
|
||||
|
||||
this.renderSimpleLabel(label, {
|
||||
height,
|
||||
layer: clickable,
|
||||
x1,
|
||||
y1: yBegin,
|
||||
x2,
|
||||
y1: yBegin,
|
||||
y2: env.primaryY,
|
||||
height,
|
||||
}, env);
|
||||
|
||||
return env.primaryY + Math.max(
|
||||
|
@ -482,8 +480,8 @@ export class ConnectDelayBegin extends Connect {
|
|||
render(stage, env) {
|
||||
const dc = env.state.delayedConnections;
|
||||
dc.set(stage.tag, {
|
||||
stage,
|
||||
from: Object.assign({}, env.agentInfos.get(stage.agentIDs[0])),
|
||||
stage,
|
||||
y: env.primaryY,
|
||||
});
|
||||
return env.primaryY + env.theme.actionMargin;
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
/* eslint-disable complexity */ // Temporary ignore while switching linter
|
||||
/* eslint-disable no-param-reassign */ // Also temporary
|
||||
|
||||
import BaseComponent, {register} from './BaseComponent.mjs';
|
||||
|
||||
const OUTLINE_ATTRS = {
|
||||
|
@ -26,6 +23,35 @@ function findExtremes(agentInfos, agentIDs) {
|
|||
};
|
||||
}
|
||||
|
||||
function findEdges(fullW, {
|
||||
x0 = null,
|
||||
x1 = null,
|
||||
xMid = null,
|
||||
}) {
|
||||
let xL = x0;
|
||||
let xR = x1;
|
||||
if(xL === null && xMid !== null) {
|
||||
xL = xMid - fullW / 2;
|
||||
}
|
||||
if(xR === null && xL !== null) {
|
||||
xR = xL + fullW;
|
||||
} else if(xL === null) {
|
||||
xL = xR - fullW;
|
||||
}
|
||||
return {xL, xR};
|
||||
}
|
||||
|
||||
function textAnchorX(anchor, {xL, xR}, padding) {
|
||||
switch(anchor) {
|
||||
case 'middle':
|
||||
return (xL + padding.left + xR - padding.right) / 2;
|
||||
case 'end':
|
||||
return xR - padding.right;
|
||||
default:
|
||||
return xL + padding.left;
|
||||
}
|
||||
}
|
||||
|
||||
class NoteComponent extends BaseComponent {
|
||||
prepareMeasurements({mode, label}, env) {
|
||||
const config = env.theme.getNote(mode);
|
||||
|
@ -37,67 +63,36 @@ class NoteComponent extends BaseComponent {
|
|||
}
|
||||
|
||||
renderNote({
|
||||
xMid = null,
|
||||
x0 = null,
|
||||
x1 = null,
|
||||
mode,
|
||||
label,
|
||||
mode,
|
||||
position,
|
||||
}, env) {
|
||||
const config = env.theme.getNote(mode);
|
||||
const {padding} = config;
|
||||
|
||||
const y = env.topY + config.margin.top + config.padding.top;
|
||||
const y = env.topY + config.margin.top + padding.top;
|
||||
const labelNode = env.svg.formattedText(config.labelAttrs, label);
|
||||
const size = env.textSizer.measure(labelNode);
|
||||
|
||||
const fullW = (
|
||||
size.width +
|
||||
config.padding.left +
|
||||
config.padding.right
|
||||
);
|
||||
const fullH = (
|
||||
config.padding.top +
|
||||
size.height +
|
||||
config.padding.bottom
|
||||
);
|
||||
if(x0 === null && xMid !== null) {
|
||||
x0 = xMid - fullW / 2;
|
||||
}
|
||||
if(x1 === null && x0 !== null) {
|
||||
x1 = x0 + fullW;
|
||||
} else if(x0 === null) {
|
||||
x0 = x1 - fullW;
|
||||
}
|
||||
switch(config.labelAttrs['text-anchor']) {
|
||||
case 'middle':
|
||||
const fullW = (size.width + padding.left + padding.right);
|
||||
const fullH = (size.height + padding.top + padding.bottom);
|
||||
const edges = findEdges(fullW, position);
|
||||
|
||||
labelNode.set({
|
||||
x: (
|
||||
x0 + config.padding.left +
|
||||
x1 - config.padding.right
|
||||
) / 2,
|
||||
x: textAnchorX(config.labelAttrs['text-anchor'], edges, padding),
|
||||
y,
|
||||
});
|
||||
break;
|
||||
case 'end':
|
||||
labelNode.set({x: x1 - config.padding.right, y});
|
||||
break;
|
||||
default:
|
||||
labelNode.set({x: x0 + config.padding.left, y});
|
||||
break;
|
||||
}
|
||||
|
||||
const boundingBox = {
|
||||
height: fullH,
|
||||
width: edges.xR - edges.xL,
|
||||
x: edges.xL,
|
||||
y: env.topY + config.margin.top,
|
||||
};
|
||||
|
||||
env.makeRegion().add(
|
||||
config.boxRenderer({
|
||||
height: fullH,
|
||||
width: x1 - x0,
|
||||
x: x0,
|
||||
y: env.topY + config.margin.top,
|
||||
}),
|
||||
env.svg.box(OUTLINE_ATTRS, {
|
||||
height: fullH,
|
||||
width: x1 - x0,
|
||||
x: x0,
|
||||
y: env.topY + config.margin.top,
|
||||
}),
|
||||
config.boxRenderer(boundingBox),
|
||||
env.svg.box(OUTLINE_ATTRS, boundingBox),
|
||||
labelNode
|
||||
);
|
||||
|
||||
|
@ -150,14 +145,16 @@ export class NoteOver extends NoteComponent {
|
|||
return this.renderNote({
|
||||
label,
|
||||
mode,
|
||||
xMid,
|
||||
position: {xMid},
|
||||
}, env);
|
||||
} else {
|
||||
return this.renderNote({
|
||||
label,
|
||||
mode,
|
||||
position: {
|
||||
x0: infoL.x - infoL.currentMaxRad - config.overlap.left,
|
||||
x1: infoR.x + infoR.currentMaxRad + config.overlap.right,
|
||||
},
|
||||
}, env);
|
||||
}
|
||||
}
|
||||
|
@ -204,7 +201,7 @@ export class NoteSide extends NoteComponent {
|
|||
return this.renderNote({
|
||||
label,
|
||||
mode,
|
||||
x0,
|
||||
position: {x0},
|
||||
}, env);
|
||||
} else {
|
||||
const info = env.agentInfos.get(left);
|
||||
|
@ -212,7 +209,7 @@ export class NoteSide extends NoteComponent {
|
|||
return this.renderNote({
|
||||
label,
|
||||
mode,
|
||||
x1,
|
||||
position: {x1},
|
||||
}, env);
|
||||
}
|
||||
}
|
||||
|
@ -251,7 +248,7 @@ export class NoteBetween extends NoteComponent {
|
|||
return this.renderNote({
|
||||
label,
|
||||
mode,
|
||||
xMid,
|
||||
position: {xMid},
|
||||
}, env);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
/* eslint-disable sort-keys */ // Maybe later
|
||||
|
||||
function optionsAttributes(attributes, options) {
|
||||
const attrs = Object.assign({}, attributes['']);
|
||||
options.forEach((opt) => {
|
||||
|
@ -63,14 +61,14 @@ export default class BaseTheme {
|
|||
return optionsAttributes(attributes, options);
|
||||
}
|
||||
|
||||
renderAgentLine({x, y0, y1, width, className, options}) {
|
||||
renderAgentLine({className, options, width, x, y0, y1}) {
|
||||
const attrs = this.optionsAttributes(this.agentLineAttrs, options);
|
||||
if(width > 0) {
|
||||
return this.svg.box(attrs, {
|
||||
height: y1 - y0,
|
||||
width,
|
||||
x: x - width / 2,
|
||||
y: y0,
|
||||
width,
|
||||
height: y1 - y0,
|
||||
}).addClass(className);
|
||||
} else {
|
||||
return this.svg.line(attrs, {
|
||||
|
@ -84,7 +82,7 @@ export default class BaseTheme {
|
|||
|
||||
// INTERNAL HELPERS
|
||||
|
||||
renderArrowHead(attrs, {x, y, width, height, dir}) {
|
||||
renderArrowHead(attrs, {dir, height, width, x, y}) {
|
||||
const wx = width * dir.dx;
|
||||
const wy = width * dir.dy;
|
||||
const hy = height * 0.5 * dir.dx;
|
||||
|
@ -98,7 +96,7 @@ export default class BaseTheme {
|
|||
.attrs(attrs);
|
||||
}
|
||||
|
||||
renderTag(attrs, {x, y, width, height}) {
|
||||
renderTag(attrs, {height, width, x, y}) {
|
||||
const {rx, ry} = attrs;
|
||||
const x2 = x + width;
|
||||
const y2 = y + height;
|
||||
|
@ -148,12 +146,12 @@ export default class BaseTheme {
|
|||
|
||||
renderRef(options, position) {
|
||||
return {
|
||||
shape: this.svg.box(options, position).attrs({'fill': 'none'}),
|
||||
fill: this.svg.box(options, position).attrs({'stroke': 'none'}),
|
||||
mask: this.svg.box(options, position).attrs({
|
||||
'fill': '#000000',
|
||||
'stroke': 'none',
|
||||
}),
|
||||
fill: this.svg.box(options, position).attrs({'stroke': 'none'}),
|
||||
shape: this.svg.box(options, position).attrs({'fill': 'none'}),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -163,6 +161,8 @@ export default class BaseTheme {
|
|||
{x1, y1, x2, y2}
|
||||
) {
|
||||
return {
|
||||
p1: {x: x1, y: y1},
|
||||
p2: {x: x2, y: y2},
|
||||
shape: this.svg.el('path')
|
||||
.attr('d', this.svg.patternedLine(pattern)
|
||||
.move(x1, y1)
|
||||
|
@ -170,15 +170,13 @@ export default class BaseTheme {
|
|||
.cap()
|
||||
.asPath())
|
||||
.attrs(attrs),
|
||||
p1: {x: x1, y: y1},
|
||||
p2: {x: x2, y: y2},
|
||||
};
|
||||
}
|
||||
|
||||
renderRevConnect(
|
||||
pattern,
|
||||
attrs,
|
||||
{x1, y1, x2, y2, xR, rad}
|
||||
{rad, x1, x2, xR, y1, y2}
|
||||
) {
|
||||
const maxRad = (y2 - y1) / 2;
|
||||
const line = this.svg.patternedLine(pattern)
|
||||
|
@ -193,20 +191,20 @@ export default class BaseTheme {
|
|||
line.arc(xR, (y1 + y2) / 2, Math.PI);
|
||||
}
|
||||
return {
|
||||
p1: {x: x1, y: y1},
|
||||
p2: {x: x2, y: y2},
|
||||
shape: this.svg.el('path')
|
||||
.attr('d', line
|
||||
.line(x2, y2)
|
||||
.cap()
|
||||
.asPath())
|
||||
.attrs(attrs),
|
||||
p1: {x: x1, y: y1},
|
||||
p2: {x: x2, y: y2},
|
||||
};
|
||||
}
|
||||
|
||||
renderLineDivider(
|
||||
{lineAttrs},
|
||||
{x, y, labelWidth, width, height}
|
||||
{height, labelWidth, width, x, y}
|
||||
) {
|
||||
let shape = null;
|
||||
const yPos = y + height / 2;
|
||||
|
@ -238,25 +236,25 @@ export default class BaseTheme {
|
|||
|
||||
renderDelayDivider(
|
||||
{dotSize, gapSize},
|
||||
{x, y, width, height}
|
||||
{height, width, x, y}
|
||||
) {
|
||||
const mask = this.svg.el('g');
|
||||
for(let i = 0; i + gapSize <= height; i += dotSize + gapSize) {
|
||||
mask.add(this.svg.box({
|
||||
'fill': '#000000',
|
||||
}, {
|
||||
height: gapSize,
|
||||
width,
|
||||
x,
|
||||
y: y + i,
|
||||
width,
|
||||
height: gapSize,
|
||||
}));
|
||||
}
|
||||
return {mask};
|
||||
}
|
||||
|
||||
renderTearDivider(
|
||||
{fadeBegin, fadeSize, pattern, zigWidth, zigHeight, lineAttrs},
|
||||
{x, y, labelWidth, labelHeight, width, height, env}
|
||||
{fadeBegin, fadeSize, lineAttrs, pattern, zigHeight, zigWidth},
|
||||
{env, height, labelHeight, labelWidth, width, x, y}
|
||||
) {
|
||||
const maskGradID = env.addDef('tear-grad', () => {
|
||||
const px = 100 / width;
|
||||
|
@ -285,24 +283,24 @@ export default class BaseTheme {
|
|||
this.svg.box({
|
||||
'fill': 'url(#' + maskGradID + ')',
|
||||
}, {
|
||||
height: height + 10,
|
||||
width,
|
||||
x,
|
||||
y: y - 5,
|
||||
width,
|
||||
height: height + 10,
|
||||
})
|
||||
);
|
||||
const shapeMaskID = env.addDef(shapeMask);
|
||||
|
||||
if(labelWidth > 0) {
|
||||
shapeMask.add(this.svg.box({
|
||||
'fill': '#000000',
|
||||
'rx': 2,
|
||||
'ry': 2,
|
||||
'fill': '#000000',
|
||||
}, {
|
||||
'height': labelHeight + 2,
|
||||
'width': labelWidth,
|
||||
'x': x + (width - labelWidth) / 2,
|
||||
'y': y + (height - labelHeight) / 2 - 1,
|
||||
'width': labelWidth,
|
||||
'height': labelHeight + 2,
|
||||
}));
|
||||
}
|
||||
|
||||
|
@ -345,6 +343,6 @@ export default class BaseTheme {
|
|||
'fill': '#000000',
|
||||
});
|
||||
}
|
||||
return {shape, mask};
|
||||
return {mask, shape};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
/* eslint-disable complexity */ // Temporary ignore while switching linter
|
||||
|
||||
function makeCanvas(width, height) {
|
||||
window.devicePixelRatio = 1;
|
||||
const canvas = document.createElement('canvas');
|
||||
|
@ -36,6 +34,20 @@ function proportionalSize(
|
|||
}
|
||||
}
|
||||
|
||||
function resize_ranges(l, h) {
|
||||
/* eslint-disable no-bitwise */ // Faster than Math.floor
|
||||
const li = (l | 0);
|
||||
const hi = (h | 0);
|
||||
/* eslint-enable no-bitwise */
|
||||
const lm = (hi === li) ? (h - l) : (li + 1 - l);
|
||||
const hm = h - hi;
|
||||
if(hm < 0.001) {
|
||||
return {hi: hi - 1, hm: 1, li, lm};
|
||||
} else {
|
||||
return {hi, hm, li, lm};
|
||||
}
|
||||
}
|
||||
|
||||
export default class ImageRegion {
|
||||
constructor(width, height, values, {
|
||||
origin = 0,
|
||||
|
@ -296,40 +308,20 @@ export default class ImageRegion {
|
|||
return sum;
|
||||
}
|
||||
|
||||
resize(size) {
|
||||
const {width, height} = this.getProportionalSize(size);
|
||||
if(width === this.width && height === this.height) {
|
||||
return this;
|
||||
}
|
||||
|
||||
_resize(width, height) {
|
||||
const {dim} = this;
|
||||
const values = new Float32Array(width * height * dim);
|
||||
|
||||
const mx = this.width / width;
|
||||
const my = this.height / height;
|
||||
const values = new Float32Array(width * height * dim);
|
||||
const norm = 1 / (mx * my);
|
||||
|
||||
function ranges(l, h) {
|
||||
/* eslint-disable no-bitwise */ // Faster than Math.floor
|
||||
const li = (l | 0);
|
||||
const hi = (h | 0);
|
||||
/* eslint-enable no-bitwise */
|
||||
const lm = (hi === li) ? (h - l) : (li + 1 - l);
|
||||
const hm = h - hi;
|
||||
if(hm < 0.001) {
|
||||
return {hi: hi - 1, hm: 1, li, lm};
|
||||
} else {
|
||||
return {hi, hm, li, lm};
|
||||
}
|
||||
}
|
||||
|
||||
const xrs = [];
|
||||
for(let x = 0; x < width; ++ x) {
|
||||
xrs[x] = ranges(x * mx, (x + 1) * mx);
|
||||
xrs[x] = resize_ranges(x * mx, (x + 1) * mx);
|
||||
}
|
||||
|
||||
for(let y = 0; y < height; ++ y) {
|
||||
const yr = ranges(y * my, (y + 1) * my);
|
||||
const yr = resize_ranges(y * my, (y + 1) * my);
|
||||
for(let x = 0; x < width; ++ x) {
|
||||
const xr = xrs[x];
|
||||
const p = (y * width + x) * dim;
|
||||
|
@ -342,6 +334,15 @@ export default class ImageRegion {
|
|||
return new ImageRegion(width, height, values, {dim});
|
||||
}
|
||||
|
||||
resize(size) {
|
||||
const {width, height} = this.getProportionalSize(size);
|
||||
if(width === this.width && height === this.height) {
|
||||
return this;
|
||||
}
|
||||
|
||||
return this._resize(width, height);
|
||||
}
|
||||
|
||||
getSuggestedChannels({blue = null, green = null, red = null} = {}) {
|
||||
return {
|
||||
blue: (blue === null) ? (this.dim - 1) : blue,
|
||||
|
|
|
@ -83,7 +83,7 @@ module.exports = {
|
|||
'lines-between-class-members': ['error'],
|
||||
'max-depth': ['error', 4],
|
||||
'max-len': ['error', {'ignoreUrls': true}],
|
||||
'max-lines': ['error', 600],
|
||||
'max-lines': ['error', 800],
|
||||
'max-nested-callbacks': ['error', 4], // Includes jasmine blocks
|
||||
'max-params': ['error', 4],
|
||||
'max-statements': ['error', 20],
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
/* eslint-disable max-lines */
|
||||
|
||||
import DOMWrapper from '../../scripts/core/DOMWrapper.mjs';
|
||||
|
||||
const DELAY_AGENTCHANGE = 500;
|
||||
|
|
|
@ -504,8 +504,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
/* eslint-disable max-lines */
|
||||
|
||||
const DELAY_AGENTCHANGE = 500;
|
||||
const DELAY_STAGECHANGE = 250;
|
||||
const PNG_RESOLUTION = 4;
|
||||
|
|
Loading…
Reference in New Issue