From 8c9e29c3dac6dfc930e804618c5ec7b86e613e47 Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Fri, 10 Sep 2021 10:29:50 +0530 Subject: [PATCH] Add back Highlighting. #340 --- src/lib/components/editor/util.ts | 541 +++++++++++++++++++++++++++--- 1 file changed, 502 insertions(+), 39 deletions(-) diff --git a/src/lib/components/editor/util.ts b/src/lib/components/editor/util.ts index dc6e2fc3ba..eb5776df3a 100644 --- a/src/lib/components/editor/util.ts +++ b/src/lib/components/editor/util.ts @@ -5,73 +5,536 @@ export const initEditor = (monacoEditor): void => { monacoEditor.languages.register({ id: 'mermaid' }); - // Register a tokens provider for the language + const requirementDiagrams = [ + 'requirement', + 'functionalRequirement', + 'interfaceRequirement', + 'performanceRequirement', + 'physicalRequirement', + 'designConstraint' + ]; + + const keywords: { + [key: string]: { + typeKeywords: string[]; + blockKeywords: string[]; + keywords: string[]; + }; + } = { + flowchart: { + typeKeywords: ['flowchart', 'flowchart-v2', 'graph'], + blockKeywords: ['subgraph', 'end'], + keywords: [ + 'TB', + 'TD', + 'BT', + 'RL', + 'LR', + 'click', + 'call', + 'href', + '_self', + '_blank', + '_parent', + '_top', + 'linkStyle', + 'style', + 'classDef', + 'class', + 'direction', + 'interpolate' + ] + }, + sequenceDiagram: { + typeKeywords: ['sequenceDiagram'], + blockKeywords: ['alt', 'par', 'and', 'loop', 'else', 'end', 'rect', 'opt', 'alt', 'rect'], + keywords: [ + 'participant', + 'as', + 'Note', + 'note', + 'right of', + 'left of', + 'over', + 'activate', + 'deactivate', + 'autonumber', + 'title' + ] + }, + classDiagram: { + typeKeywords: ['classDiagram', 'classDiagram-v2'], + blockKeywords: ['class'], + keywords: [ + 'link', + 'click', + 'callback', + 'call', + 'href', + 'cssClass', + 'direction', + 'TB', + 'BT', + 'RL', + 'LR' + ] + }, + stateDiagram: { + typeKeywords: ['stateDiagram', 'stateDiagram-v2'], + blockKeywords: ['state', 'note', 'end'], + keywords: ['state', 'as', 'hide empty description', 'direction', 'TB', 'BT', 'RL', 'LR'] + }, + erDiagram: { + typeKeywords: ['erDiagram'], + blockKeywords: [], + keywords: [] + }, + journey: { + typeKeywords: ['journey'], + blockKeywords: ['section'], + keywords: ['title'] + }, + info: { + typeKeywords: ['info'], + blockKeywords: [], + keywords: ['showInfo'] + }, + gantt: { + typeKeywords: ['gantt'], + blockKeywords: [], + keywords: [ + 'title', + 'dateFormat', + 'axisFormat', + 'todayMarker', + 'section', + 'excludes', + 'inclusiveEndDates' + ] + }, + requirementDiagram: { + typeKeywords: ['requirement', 'requirementDiagram'], + blockKeywords: requirementDiagrams.concat('element'), + keywords: [] + }, + gitGraph: { + typeKeywords: ['gitGraph'], + blockKeywords: [], + keywords: ['commit', 'branch', 'merge', 'reset', 'checkout', 'LR', 'BT'] + }, + pie: { + typeKeywords: ['pie'], + blockKeywords: [], + keywords: ['title', 'showData'] + } + }; + + // Register a tokens provider for the mermaid language monacoEditor.languages.setMonarchTokensProvider('mermaid', { - typeKeywords: [ - 'graph', - 'stateDiagram', - 'sequenceDiagram', - 'classDiagram', - 'pie', - 'erDiagram', - 'flowchart', - 'gantt', - 'gitGraph', - 'journey' - ], - keywords: ['participant', 'as'], - arrows: ['---', '===', '-->', '==>', '->>', '->'], + ...Object.entries(keywords) + .map((entry) => + Object.fromEntries( + Object.entries(entry[1]).map((deepEntry) => [ + entry[0] + deepEntry[0][0].toUpperCase() + deepEntry[0].slice(1), + deepEntry[1] + ]) + ) + ) + .reduce((overallKeywords, nextKeyword) => ({ ...overallKeywords, ...nextKeyword }), {}), tokenizer: { root: [ - [/[{}]/, 'delimiter.bracket'], - [/[a-z_$][\w$]*/, { cases: { '@typeKeywords': 'keyword', '@keywords': 'keyword' } }], - [/[-=>ox]+/, { cases: { '@arrows': 'transition' } }], - [/[[{(}]+.+?[)\]}]+/, 'string'], - [/".*"/, 'string'] + [/%%(?=.*%%$)/, { token: 'string', nextEmbedded: 'json' }], + [/%%$/, { token: 'string', nextEmbedded: '@pop' }], + [/^\s*gitGraph/m, 'typeKeyword', 'gitGraph'], + [/^\s*info/m, 'typeKeyword', 'info'], + [/^\s*pie/m, 'typeKeyword', 'pie'], + [/^\s*(flowchart|flowchart-v2|graph)/m, 'typeKeyword', 'flowchart'], + [/^\s*sequenceDiagram/, 'typeKeyword', 'sequenceDiagram'], + [/^\s*classDiagram(-v2)?/, 'typeKeyword', 'classDiagram'], + [/^\s*journey/, 'typeKeyword', 'journey'], + [/^\s*gantt/, 'typeKeyword', 'gantt'], + [/^\s*stateDiagram(-v2)?/, 'typeKeyword', 'stateDiagram'], + [/^\s*erDiagram/, 'typeKeyword', 'erDiagram'], + [/^\s*requirement(Diagram)?/, 'typeKeyword', 'requirementDiagram'] + // [/%%.*(?]+[^\]|[]+?\]+/, 'string'], + [/{+.+?}+/, 'string'], + [/\(+.+?\)+/, 'string'], + [/-\.+->?/, 'transition'], + [/(-[-.])([^->]+?)(-{3,}|-{2,}>|\.-+>)/, ['transition', 'string', 'transition']], + [/(==+)([^=]+?)(={3,}|={2,}>)/, ['transition', 'string', 'transition']], + [/|===+|---+/, 'transition'], + [/:::/, 'transition'], + [/[;&]/, 'delimiter.bracket'], + [/".*?"/, 'string'] + // [/%%.*(??>|--?[)x])[+-]?/, 'transition'], + [/(:)([^:\n]*?$)/, ['delimiter.bracket', 'string']] + // [/%%.*(?|o)?/, 'transition'], + [/^\s*class\s(?!.*\{)/, 'keyword'], + [ + /[a-zA-Z][\w$]*/, + { + cases: { + '@classDiagramBlockKeywords': 'typeKeyword', + '@classDiagramKeywords': 'keyword', + '@default': 'variable' + } + } + ], + // [/%%.*(?>)/, ['delimiter.bracket', 'annotation', 'delimiter.bracket']], + [/".*?"/, 'string'], + [/:::/, 'transition'], + [/:|\+|-|#|~|\*\s*$|\$\s*$|\(|\)|{|}/, 'delimiter.bracket'] + ], + journey: [ + [/(title)(.*)/, ['keyword', 'string']], + [/(section)(.*)/, ['typeKeyword', 'string']], + [ + /[a-zA-Z][\w$]*/, + { + cases: { + '@journeyBlockKeywords': 'typeKeyword', + '@journeyKeywords': 'keyword', + '@default': 'variable' + } + } + ], + [ + /(^\s*.+?)(:)(.*?)(:)(.*?)([,$])/, + [ + 'string', + 'delimiter.bracket', + 'number', + 'delimiter.bracket', + 'variable', + 'delimiter.bracket' + ] + ], + [/,/, 'delimiter.bracket'], + [/(^\s*.+?)(:)([^:]*?)$/, ['string', 'delimiter.bracket', 'variable']] + // [/%%.*(?>)/, 'annotation'], + [/(\[\[)(fork|join|choice)(]])/, ['delimiter.bracket', 'annotation', 'delimiter.bracket']], + [ + /[a-zA-Z][\w$]*/, + { + cases: { + '@stateDiagramBlockKeywords': 'typeKeyword', + '@stateDiagramKeywords': 'keyword', + '@default': 'variable' + } + } + ], + [/".*?"/, 'string'], + [/(:)([^:\n]*?$)/, ['delimiter.bracket', 'string']], + [/{|}/, 'delimiter.bracket'], + // [/%%.*(?/, 'transition'], + [/\[.*?]/, 'string'] + ], + stateDiagramNote: [ + [/^\s*end note$/, { token: 'typeKeyword', next: '@pop' }], + [/.*/, 'string'] + ], + erDiagram: [ + [/[}|][o|](--|\.\.)[o|][{|]/, 'transition'], + [/".*?"/, 'string'], + [/(:)(.*?$)/, ['delimiter.bracket', 'string']], + [/:|{|}/, 'delimiter.bracket'], + [/([a-zA-Z]+)(\s+[a-zA-Z]+)/, ['type', 'variable']], + // [/%%.*(?|<-|-/, 'transition'], + [/(\d+\.)*\d+/, 'number'], + [ + /[a-zA-Z_-][\w$]*/, + { + cases: { + '@requirementDiagramBlockKeywords': 'typeKeyword', + '@default': 'variable' + } + } + ], + [/:|{|}|\//, 'delimiter.bracket'], + // [/%%.*(? { const suggestions = [ { - label: 'simpleText', - kind: monacoEditor.languages.CompletionItemKind.Text, - insertText: 'simpleText' + label: 'loop', + kind: monacoEditor.languages.CompletionItemKind.Snippet, + insertText: ['loop ${1:Loop text}', '\t$0', 'end'].join('\n'), + insertTextRules: monacoEditor.languages.CompletionItemInsertTextRule.InsertAsSnippet, + documentation: 'Sequence Diagram Loops' }, { - label: 'testing', - kind: monacoEditor.languages.CompletionItemKind.Keyword, - insertText: 'testing(${1:condition})', - insertTextRules: monacoEditor.languages.CompletionItemInsertTextRule.InsertAsSnippet + label: 'alt', + kind: monacoEditor.languages.CompletionItemKind.Snippet, + insertText: ['alt ${1:Describing text}', '\t$0', 'else', '\t', 'end'].join('\n'), + insertTextRules: monacoEditor.languages.CompletionItemInsertTextRule.InsertAsSnippet, + documentation: 'Alternative Path' + }, + { + label: 'opt', + kind: monacoEditor.languages.CompletionItemKind.Snippet, + insertText: ['opt ${1:Describing text}', '\t$0', 'end'].join('\n'), + insertTextRules: monacoEditor.languages.CompletionItemInsertTextRule.InsertAsSnippet, + documentation: 'Optional Path' + }, + { + label: 'par', + kind: monacoEditor.languages.CompletionItemKind.Snippet, + insertText: [ + 'par ${1:[Action 1]}', + '\t$0', + 'and ${2:[Action 2]}', + '\t', + 'and ${3:[Action 3]}', + '\t', + 'end' + ].join('\n'), + insertTextRules: monacoEditor.languages.CompletionItemInsertTextRule.InsertAsSnippet, + documentation: 'Parallel Actions' + }, + { + label: 'rect', + kind: monacoEditor.languages.CompletionItemKind.Snippet, + insertText: ['rect ${1:rgb(0, 255, 0)}', '\t$0', 'end'].join('\n'), + insertTextRules: monacoEditor.languages.CompletionItemInsertTextRule.InsertAsSnippet, + documentation: 'Background Color' + }, + { + label: 'subgraph', + kind: monacoEditor.languages.CompletionItemKind.Snippet, + insertText: ['subgraph ${1:title}', '\t$0', 'end'].join('\n'), + insertTextRules: monacoEditor.languages.CompletionItemInsertTextRule.InsertAsSnippet, + documentation: 'Subgraph' + }, + { + label: 'class', + kind: monacoEditor.languages.CompletionItemKind.Snippet, + insertText: ['class ${1:className} {', '\t$0', '}'].join('\n'), + insertTextRules: monacoEditor.languages.CompletionItemInsertTextRule.InsertAsSnippet, + documentation: 'Class' }, { - label: 'ifelse', + label: 'state', kind: monacoEditor.languages.CompletionItemKind.Snippet, - insertText: ['if (${1:condition}) {', '\t$0', '} else {', '\t', '}'].join('\n'), + insertText: ['state ${1:stateName} {', '\t$0', '}'].join('\n'), insertTextRules: monacoEditor.languages.CompletionItemInsertTextRule.InsertAsSnippet, - documentation: 'If-Else Statement' - } + documentation: 'State' + }, + { + label: 'note', + kind: monacoEditor.languages.CompletionItemKind.Snippet, + insertText: ['note ${1:right of State1}', '\t$0', 'end note'].join('\n'), + insertTextRules: monacoEditor.languages.CompletionItemInsertTextRule.InsertAsSnippet, + documentation: 'State' + }, + { + label: 'section', + kind: monacoEditor.languages.CompletionItemKind.Snippet, + insertText: ['section ${1:Go to work}', '\t$0'].join('\n'), + insertTextRules: monacoEditor.languages.CompletionItemInsertTextRule.InsertAsSnippet, + documentation: 'User-journey Section' + }, + { + label: 'element', + kind: monacoEditor.languages.CompletionItemKind.Snippet, + insertText: ['element ${1:test_entity} {', '\t$0', '}'].join('\n'), + insertTextRules: monacoEditor.languages.CompletionItemInsertTextRule.InsertAsSnippet, + documentation: 'User-journey Section' + }, + { + label: 'options', + kind: monacoEditor.languages.CompletionItemKind.Snippet, + insertText: ['options', '{', ' $0', '}', 'end'].join('\n'), + insertTextRules: monacoEditor.languages.CompletionItemInsertTextRule.InsertAsSnippet, + documentation: 'Git Graph Options' + }, + ...requirementDiagrams.map((requirementDiagramType) => ({ + label: requirementDiagramType, + kind: monacoEditor.languages.CompletionItemKind.Snippet, + insertText: [ + requirementDiagramType + ' ${1:test_req} {', + '\tid: 1', + '\ttext: the test text.', + '\trisk: high', + '\tverifyMethod: test', + '}' + ].join('\n'), + insertTextRules: monacoEditor.languages.CompletionItemInsertTextRule.InsertAsSnippet, + documentation: requirementDiagramType + .split(/(?=[A-Z])/) + .map((part) => part[0].toUpperCase() + part.slice(1)) + .join(' ') + })), + ...[ + ...new Set( + Object.values(keywords) + .map((diagramKeywords) => + Object.entries(diagramKeywords) + .filter((keywordType) => keywordType[0] !== 'annotations') + .map((entry) => entry[1]) + ) + .flat(2) + ) + ].map((keyword) => ({ + label: keyword, + kind: monacoEditor.languages.CompletionItemKind.Keyword, + insertText: keyword + })) ]; return { suggestions: suggestions }; } }); + + monacoEditor.languages.setLanguageConfiguration('mermaid', { + autoClosingPairs: [ + { + open: '(', + close: ')' + }, + { + open: '{', + close: '}' + }, + { + open: '[', + close: ']' + } + ], + brackets: [ + ['(', ')'], + ['{', '}'], + ['[', ']'] + ] + }); };