define(['core/ArrayUtilities'], (array) => { 'use strict'; const CM_ERROR = {type: 'error line-error', then: {'': 0}}; function suggestionsEqual(a, b) { return ( (a.v === b.v) && (a.prefix === b.prefix) && (a.suffix === b.suffix) && (a.q === b.q) ); } 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. const end = {type: '', suggest: '\n', then: {}}; const hiddenEnd = {type: '', then: {}}; function textTo(exit, suggest) { return { type: 'string', suggest, then: Object.assign({'': 0}, exit), }; } const textToEnd = textTo({'\n': end}); const aliasListToEnd = { type: 'variable', suggest: {known: 'Agent'}, then: { '': 0, '\n': end, ',': {type: 'operator', suggest: true, then: {'': 1}}, 'as': {type: 'keyword', suggest: true, then: { '': {type: 'variable', suggest: {known: 'Agent'}, then: { '': 0, ',': {type: 'operator', suggest: true, then: {'': 3}}, '\n': end, }}, }}, }, }; function agentListTo(exit) { return { type: 'variable', suggest: {known: 'Agent'}, then: Object.assign({}, exit, { '': 0, ',': {type: 'operator', suggest: true, then: {'': 1}}, } ), }; } const colonTextToEnd = { type: 'operator', suggest: true, then: {'': textToEnd, '\n': hiddenEnd}, }; const agentListToText = agentListTo({ ':': colonTextToEnd, }); const agentList2ToText = { type: 'variable', suggest: {known: 'Agent'}, then: { '': 0, ',': {type: 'operator', suggest: true, then: { '': agentListToText, }}, ':': CM_ERROR, }, }; const singleAgentToText = { type: 'variable', suggest: {known: 'Agent'}, then: { '': 0, ',': CM_ERROR, ':': colonTextToEnd, }, }; const agentToOptText = { type: 'variable', suggest: {known: 'Agent'}, then: { '': 0, ':': {type: 'operator', suggest: true, then: { '': textToEnd, '\n': hiddenEnd, }}, '\n': end, }, }; const referenceName = { ':': {type: 'operator', suggest: true, then: { '': textTo({ 'as': {type: 'keyword', suggest: true, then: { '': { type: 'variable', suggest: {known: 'Agent'}, then: { '': 0, '\n': end, }, }, }}, }), }}, }; const refDef = {type: 'keyword', suggest: true, then: Object.assign({ 'over': {type: 'keyword', suggest: true, then: { '': agentListTo(referenceName), }}, }, referenceName)}; const divider = { '\n': end, ':': {type: 'operator', suggest: true, then: { '': textToEnd, '\n': hiddenEnd, }}, 'with': {type: 'keyword', suggest: ['with height '], then: { 'height': {type: 'keyword', suggest: true, then: { '': {type: 'number', suggest: ['6 ', '30 '], then: { '\n': end, ':': {type: 'operator', suggest: true, then: { '': textToEnd, '\n': hiddenEnd, }}, }}, }}, }}, }; function makeSideNote(side) { return { type: 'keyword', suggest: [side + ' of ', side + ': '], then: { 'of': {type: 'keyword', suggest: true, then: { '': agentListToText, }}, ':': {type: 'operator', suggest: true, then: { '': textToEnd, }}, '': agentListToText, }, }; } function makeOpBlock(exit, sourceExit) { const op = {type: 'operator', suggest: true, then: { '+': CM_ERROR, '-': CM_ERROR, '*': CM_ERROR, '!': CM_ERROR, '': exit, }}; return { '+': {type: 'operator', suggest: true, then: { '+': CM_ERROR, '-': CM_ERROR, '*': op, '!': CM_ERROR, '': exit, }}, '-': {type: 'operator', suggest: true, then: { '+': CM_ERROR, '-': CM_ERROR, '*': op, '!': {type: 'operator', then: { '+': CM_ERROR, '-': CM_ERROR, '*': CM_ERROR, '!': CM_ERROR, '': exit, }}, '': exit, }}, '*': {type: 'operator', suggest: true, then: Object.assign({ '+': op, '-': op, '*': CM_ERROR, '!': CM_ERROR, '': exit, }, sourceExit)}, '!': op, '': exit, }; } function makeCMConnect(arrows) { const connect = { type: 'keyword', suggest: true, then: Object.assign({}, makeOpBlock(agentToOptText, { ':': colonTextToEnd, '\n': hiddenEnd, }), { '...': {type: 'operator', suggest: true, then: { '': { type: 'variable', suggest: {known: 'DelayedAgent'}, then: { '': 0, ':': CM_ERROR, '\n': end, }, }, }}, }), }; const connectors = {}; arrows.forEach((arrow) => (connectors[arrow] = connect)); const labelIndicator = { type: 'operator', suggest: true, override: 'Label', then: {}, }; const hiddenLabelIndicator = { type: 'operator', suggest: false, override: 'Label', then: {}, }; const firstAgent = { type: 'variable', suggest: {known: 'Agent'}, then: Object.assign({ '': 0, ':': labelIndicator, }, connectors), }; const firstAgentDelayed = { type: 'variable', suggest: {known: 'DelayedAgent'}, then: Object.assign({ '': 0, ':': hiddenLabelIndicator, }, connectors), }; return Object.assign({ '...': {type: 'operator', suggest: true, then: { '': firstAgentDelayed, }}, }, makeOpBlock(firstAgent, Object.assign({ '': firstAgent, ':': hiddenLabelIndicator, }, connectors))); } const BASE_THEN = { 'title': {type: 'keyword', suggest: true, then: { '': textToEnd, }}, 'theme': {type: 'keyword', suggest: true, then: { '': { type: 'string', suggest: { global: 'themes', suffix: '\n', }, then: { '': 0, '\n': end, }, }, }}, 'headers': {type: 'keyword', suggest: true, then: { 'none': {type: 'keyword', suggest: true, then: {}}, 'cross': {type: 'keyword', suggest: true, then: {}}, 'box': {type: 'keyword', suggest: true, then: {}}, 'fade': {type: 'keyword', suggest: true, then: {}}, 'bar': {type: 'keyword', suggest: true, then: {}}, }}, 'terminators': {type: 'keyword', suggest: true, then: { 'none': {type: 'keyword', suggest: true, then: {}}, 'cross': {type: 'keyword', suggest: true, then: {}}, 'box': {type: 'keyword', suggest: true, then: {}}, 'fade': {type: 'keyword', suggest: true, then: {}}, 'bar': {type: 'keyword', suggest: true, then: {}}, }}, 'divider': {type: 'keyword', suggest: true, then: Object.assign({ 'line': {type: 'keyword', suggest: true, then: divider}, 'space': {type: 'keyword', suggest: true, then: divider}, 'delay': {type: 'keyword', suggest: true, then: divider}, 'tear': {type: 'keyword', suggest: true, then: divider}, }, divider)}, 'define': {type: 'keyword', suggest: true, then: { '': aliasListToEnd, 'as': CM_ERROR, }}, 'begin': {type: 'keyword', suggest: true, then: { '': aliasListToEnd, 'reference': refDef, 'as': CM_ERROR, }}, 'end': {type: 'keyword', suggest: true, then: { '': aliasListToEnd, 'as': CM_ERROR, '\n': end, }}, 'if': {type: 'keyword', suggest: true, then: { '': textToEnd, ':': {type: 'operator', suggest: true, then: { '': textToEnd, }}, '\n': end, }}, 'else': {type: 'keyword', suggest: ['else\n', 'else if: '], then: { 'if': {type: 'keyword', suggest: 'if: ', then: { '': textToEnd, ':': {type: 'operator', suggest: true, then: { '': textToEnd, }}, }}, '\n': end, }}, 'repeat': {type: 'keyword', suggest: true, then: { '': textToEnd, ':': {type: 'operator', suggest: true, then: { '': textToEnd, }}, '\n': end, }}, 'note': {type: 'keyword', suggest: true, then: { 'over': {type: 'keyword', suggest: true, then: { '': agentListToText, }}, 'left': makeSideNote('left'), 'right': makeSideNote('right'), 'between': {type: 'keyword', suggest: true, then: { '': agentList2ToText, }}, }}, 'state': {type: 'keyword', suggest: 'state over ', then: { 'over': {type: 'keyword', suggest: true, then: { '': singleAgentToText, }}, }}, 'text': {type: 'keyword', suggest: true, then: { 'left': makeSideNote('left'), 'right': makeSideNote('right'), }}, 'autolabel': {type: 'keyword', suggest: true, then: { 'off': {type: 'keyword', suggest: true, then: {}}, '': textTo({'\n': end}, [ {v: '