codemirror-lang-mermaid
Version:
Mermaid language support for CodeMirror
130 lines (111 loc) • 2.61 kB
text/typescript
import { ExternalTokenizer } from '@lezer/lr';
import {
_break,
_else,
Activate,
Actor,
alt,
and,
As,
Autonumber,
box,
Create,
critical,
Deactivate,
Destroy,
End,
link,
links,
loop,
messageText,
NodeText,
Note,
opt,
option,
par,
Participant,
Position,
rect,
} from './sequence.grammar.terms';
const skipCodePoints = [-1, 9, 10, 13, 32, 37];
const arrowSuffixCodePoints = [43, 45];
const notAllowedCodePoints = [44, 58, 62];
const notAllowed2Chars = ['->', '-x', '-)', ' -', ' '];
const notAllowed3Chars = ['-->', '->>', '--x', '--)', ' as'];
const keywordMap: { [key: string]: number } = {
'left of': Position,
'right of': Position,
activate: Activate,
actor: Actor,
alt: alt,
and: and,
as: As,
autonumber: Autonumber,
box: box,
break: _break,
create: Create,
critical: critical,
deactivate: Deactivate,
destroy: Destroy,
else: _else,
end: End,
link: link,
links: links,
loop: loop,
note: Note,
opt: opt,
option: option,
over: Position,
par: par,
participant: Participant,
rect: rect,
};
const keywords = Object.keys(keywordMap);
export const messageTextToken = new ExternalTokenizer((input) => {
if (skipCodePoints.includes(input.next)) return;
while (input.next !== 10 && input.next !== -1) {
input.advance();
}
input.acceptToken(messageText);
});
export const textTokens = new ExternalTokenizer((input) => {
if (
skipCodePoints.includes(input.next) ||
arrowSuffixCodePoints.includes(input.next)
)
return;
const isArrowNext = () => {
if (input.peek(0) === -1 || input.peek(1) === -1 || input.peek(2) === -1)
return false;
let result =
String.fromCodePoint(input.peek(0)) + String.fromCodePoint(input.peek(1));
if (notAllowed2Chars.includes(result)) return true;
result += String.fromCodePoint(input.peek(2));
if (notAllowed3Chars.includes(result)) return true;
return false;
};
let tokens = '';
while (
!notAllowedCodePoints.includes(input.next) &&
!isArrowNext() &&
input.next !== 10 &&
input.next !== -1
) {
tokens += String.fromCodePoint(input.next);
input.advance();
}
const activeKeyword = keywords.filter((keyword) => {
if (keyword === tokens) {
return tokens.toLowerCase().startsWith(keyword);
}
return tokens.toLowerCase().startsWith(keyword + ' ');
});
if (activeKeyword.length > 0) {
input.acceptToken(
keywordMap[activeKeyword[0]],
activeKeyword[0].length - tokens.length
);
return;
}
input.acceptToken(NodeText);
});