@unified-myst/role-extension
Version:
Extension to support the MyST role syntax (``{name}`content` ``) in unified
100 lines (82 loc) • 2.3 kB
JavaScript
import { codeText } from 'micromark-core-commonmark';
import { tokenTypes } from './constants.js';
/**
* @typedef {import('micromark-util-types').Code} Code
* @typedef {import('micromark-util-types').Extension} Extension
* @typedef {import('micromark-util-types').Tokenizer} Tokenizer
* @typedef {import('micromark-util-types').State} State
*/
/**
* The micromark extension, to parse the source syntax to events.
* Events are token enter/exits, which in-turn are references to slices of the source text.
*
* @type {Extension}
*/
const mystRoleMmarkExt = {
text: {
[123]: {
name: 'mystRole',
tokenize: tokenizeMystRole
}
}
};
const nameCharRegex = /[a-zA-Z0-9_.\-+:]/;
/**
* Check whether a code matches the bound regex.
*
* @param {Code} code Character code
* @returns {code is number} Whether the character code matches the bound regex
*/
function checkNameChar(code) {
return code !== null && nameCharRegex.test(String.fromCharCode(code));
}
/** @type {Tokenizer} */
function tokenizeMystRole(effects, ok, nok) {
let emptyName = true;
return start;
/** @type {State} */
function start(code) {
effects.enter(tokenTypes.mystRole);
effects.enter(tokenTypes.mystRoleMarker);
effects.consume(code);
effects.exit(tokenTypes.mystRoleMarker);
return afterOpen;
}
/** @type {State} */
function afterOpen(code) {
if (!checkNameChar(code)) {
return nok(code);
}
effects.enter(tokenTypes.mystRoleName);
effects.enter("chunkString", {
contentType: "string"
});
return consumeName(code);
}
/** @type {State} */
function consumeName(code) {
if (code === 125) {
if (emptyName) {
return nok(code);
}
effects.exit("chunkString");
effects.exit(tokenTypes.mystRoleName);
effects.enter(tokenTypes.mystRoleMarker);
effects.consume(code);
effects.exit(tokenTypes.mystRoleMarker);
return effects.attempt(codeText, closeRole, nok);
}
if (!checkNameChar(code)) {
return nok(code);
}
effects.consume(code);
emptyName = false;
return consumeName;
}
/** @type {State} */
function closeRole(code) {
effects.exit(tokenTypes.mystRole);
return ok(code);
}
}
export { mystRoleMmarkExt };