@atlaskit/editor-plugin-text-formatting
Version:
Text-formatting plugin for @atlaskit/editor-core
311 lines (301 loc) • 15.1 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = exports.codeRegexWithBackwardMatch = exports.codeRegex = exports.ValidCombinations = void 0;
exports.inputRulePlugin = inputRulePlugin;
exports.strongRegex2 = exports.strongRegex1 = exports.strikeRegex = exports.italicRegex2 = exports.italicRegex1 = void 0;
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));
var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));
var _get2 = _interopRequireDefault(require("@babel/runtime/helpers/get"));
var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));
var _wrapNativeSuper2 = _interopRequireDefault(require("@babel/runtime/helpers/wrapNativeSuper"));
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _analytics = require("@atlaskit/editor-common/analytics");
var _safePlugin = require("@atlaskit/editor-common/safe-plugin");
var _utils = require("@atlaskit/editor-common/utils");
var _prosemirrorInputRules = require("@atlaskit/prosemirror-input-rules");
var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
function _callSuper(t, o, e) { return o = (0, _getPrototypeOf2.default)(o), (0, _possibleConstructorReturn2.default)(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], (0, _getPrototypeOf2.default)(t).constructor) : o.apply(t, e)); }
function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
function _superPropGet(t, o, e, r) { var p = (0, _get2.default)((0, _getPrototypeOf2.default)(1 & r ? t.prototype : t), o, e); return 2 & r && "function" == typeof p ? function (t) { return p.apply(e, t); } : p; }
var ValidAutoformatChars = /*#__PURE__*/function (ValidAutoformatChars) {
ValidAutoformatChars["STRONG"] = "__";
ValidAutoformatChars["STRIKE"] = "~~";
ValidAutoformatChars["STRONG_MARKDOWN"] = "**";
ValidAutoformatChars["ITALIC_MARKDOWN"] = "*";
ValidAutoformatChars["ITALIC"] = "_";
ValidAutoformatChars["CODE"] = "`";
return ValidAutoformatChars;
}(ValidAutoformatChars || {});
var ValidCombinations = exports.ValidCombinations = (0, _defineProperty2.default)((0, _defineProperty2.default)((0, _defineProperty2.default)((0, _defineProperty2.default)((0, _defineProperty2.default)((0, _defineProperty2.default)({}, ValidAutoformatChars.STRIKE, [
// e.g: _~~lol~~_
ValidAutoformatChars.ITALIC,
// e.g: __~~lol~~__
ValidAutoformatChars.STRONG,
// e.g: **~~lol~~**
ValidAutoformatChars.STRONG_MARKDOWN,
// e.g: *~~lol~~*
ValidAutoformatChars.ITALIC_MARKDOWN]), ValidAutoformatChars.STRONG, [
// e.g: ~~__lol__~~
ValidAutoformatChars.STRIKE,
// e.g: *__lol__*
ValidAutoformatChars.ITALIC_MARKDOWN]), ValidAutoformatChars.STRONG_MARKDOWN, [
// e.g: _**lol**_
ValidAutoformatChars.ITALIC,
// e.g: ~~**lol**~~
ValidAutoformatChars.STRIKE]), ValidAutoformatChars.ITALIC_MARKDOWN, [
// e.g: ~~*lol*~~
ValidAutoformatChars.STRIKE,
// e.g: __*lol*__
ValidAutoformatChars.STRONG]), ValidAutoformatChars.ITALIC, [
// e.g: ~~_lol_~~
ValidAutoformatChars.STRIKE,
// e.g: **_lol_**
ValidAutoformatChars.STRONG_MARKDOWN]), ValidAutoformatChars.CODE, [
// e.g: loko (`some code`
'( ']);
function addMark(markType, _schema, char, api) {
return function (state, _match, start, end) {
var _schema$marks;
var doc = state.doc,
schema = state.schema,
tr = state.tr;
var textPrefix = state.doc.textBetween(start, start + char.length);
// fixes the following case: my `*name` is *
// expected result: should ignore special characters inside "code"
if (textPrefix !== char || schema !== null && schema !== void 0 && (_schema$marks = schema.marks) !== null && _schema$marks !== void 0 && (_schema$marks = _schema$marks.code) !== null && _schema$marks !== void 0 && _schema$marks.isInSet(doc.resolve(start + 1).marks())) {
if (!(0, _expValEquals.expValEquals)('platform_editor_lovability_inline_code', 'isEnabled', true)) {
return null;
}
// if the prefix is not a character but the suffix is, continue
var suffix = state.doc.textBetween(end - char.length, end);
if (suffix !== char) {
return null;
}
}
// Prevent autoformatting across hardbreaks
var containsHardBreak;
doc.nodesBetween(start, end, function (node) {
if (node.type === schema.nodes.hardBreak) {
containsHardBreak = true;
return false;
}
return !containsHardBreak;
});
if (containsHardBreak) {
return null;
}
// fixes autoformatting in heading nodes: # Heading *bold*
// expected result: should not autoformat *bold*; <h1>Heading *bold*</h1>
var startPosResolved = doc.resolve(start);
var endPosResolved = doc.resolve(end);
if (startPosResolved.sameParent(endPosResolved) && !startPosResolved.parent.type.allowsMarkType(markType)) {
return null;
}
if (markType.name === 'code') {
var _api$base;
api === null || api === void 0 || (_api$base = api.base) === null || _api$base === void 0 || (_api$base = _api$base.actions) === null || _api$base === void 0 || _api$base.resolveMarks(tr.mapping.map(start), tr.mapping.map(end), tr);
}
var mappedStart = tr.mapping.map(start);
var mappedEnd = tr.mapping.map(end);
tr.addMark(mappedStart, mappedEnd, markType.create());
var textSuffix = tr.doc.textBetween(mappedEnd - char.length, mappedEnd);
if (textSuffix === char) {
tr.delete(mappedEnd - char.length, mappedEnd);
}
if (textPrefix === char) {
tr.delete(mappedStart, mappedStart + char.length);
}
return tr.removeStoredMark(markType);
};
}
var ReverseRegexExp = /*#__PURE__*/function (_RegExp) {
function ReverseRegexExp() {
(0, _classCallCheck2.default)(this, ReverseRegexExp);
return _callSuper(this, ReverseRegexExp, arguments);
}
(0, _inherits2.default)(ReverseRegexExp, _RegExp);
return (0, _createClass2.default)(ReverseRegexExp, [{
key: "exec",
value: function exec(str) {
if (!str) {
return null;
}
var reverseStr = (0, _toConsumableArray2.default)(str).reverse().join('');
var result = _superPropGet(ReverseRegexExp, "exec", this, 3)([reverseStr]);
if (!result) {
return null;
}
for (var i = 0; i < result.length; i++) {
if (result[i] && typeof result[i] === 'string') {
result[i] = (0, _toConsumableArray2.default)(result[i]).reverse().join('');
}
}
if (result.input && typeof result.input === 'string') {
result.input = (0, _toConsumableArray2.default)(result.input).reverse().join('');
}
if (result.input && result[0]) {
result.index = result.input.length - result[0].length;
}
return result;
}
}]);
}( /*#__PURE__*/(0, _wrapNativeSuper2.default)(RegExp));
var buildRegex = function buildRegex(char) {
// Ignored via go/ees005
// eslint-disable-next-line require-unicode-regexp
var escapedChar = char.replace(/(\W)/g, '\\$1');
// Ignored via go/ees005
// eslint-disable-next-line require-unicode-regexp, @atlassian/perf-linting/no-expensive-split-replace -- Ignored via go/ees017 (to be fixed)
var combinations = ValidCombinations[char].map(function (c) {
return c.replace(/(\W)/g, '\\$1');
}).join('|');
// Single X - https://regex101.com/r/McT3yq/14/
// Double X - https://regex101.com/r/pQUgjx/1/
var baseRegex = '^X(?=[^X\\s]).*?[^\\sX]X(?=[\\sOBJECT_REPLACEMENT_CHARACTER]COMBINATIONS|$)'.replace('OBJECT_REPLACEMENT_CHARACTER', _prosemirrorInputRules.leafNodeReplacementCharacter).replace('COMBINATIONS', combinations ? "|".concat(combinations) : '');
var replacedRegex = String.prototype.hasOwnProperty('replaceAll') ?
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-explicit-any
baseRegex.replaceAll('X', escapedChar) :
// Ignored via go/ees005
// eslint-disable-next-line require-unicode-regexp
baseRegex.replace(/X/g, escapedChar);
return new ReverseRegexExp(replacedRegex);
};
var buildRegexNew = function buildRegexNew(char) {
var allowsBackwardMatch = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
// Ignored via go/ees005
// eslint-disable-next-line require-unicode-regexp
var escapedChar = char.replace(/(\W)/g, '\\$1');
// Ignored via go/ees005
// eslint-disable-next-line require-unicode-regexp, @atlassian/perf-linting/no-expensive-split-replace -- Ignored via go/ees017 (to be fixed)
var combinations = ValidCombinations[char].map(function (c) {
return c.replace(/(\W)/g, '\\$1');
}).join('|');
// Single X - https://regex101.com/r/McT3yq/14/
// Double X - https://regex101.com/r/pQUgjx/1/
// if backwards matches are allowed, do not prefix the regex with an anchor (^)
var maybeAnchor = allowsBackwardMatch ? '' : '^';
var orCombinations = combinations ? "|".concat(combinations) : '';
var baseRegex = "".concat(maybeAnchor, "X(?=[^X\\s]).*?[^\\sX]X(?=[\\s").concat(_prosemirrorInputRules.leafNodeReplacementCharacter, "]").concat(orCombinations, "|$)");
var replacedRegex = String.prototype.hasOwnProperty('replaceAll') ?
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-explicit-any
baseRegex.replaceAll('X', escapedChar) :
// Ignored via go/ees005
// eslint-disable-next-line require-unicode-regexp
baseRegex.replace(/X/g, escapedChar);
return new ReverseRegexExp(replacedRegex);
};
var strongRegex1 = exports.strongRegex1 = buildRegex(ValidAutoformatChars.STRONG);
var strongRegex2 = exports.strongRegex2 = buildRegex(ValidAutoformatChars.STRONG_MARKDOWN);
var italicRegex1 = exports.italicRegex1 = buildRegex(ValidAutoformatChars.ITALIC);
var italicRegex2 = exports.italicRegex2 = buildRegex(ValidAutoformatChars.ITALIC_MARKDOWN);
var strikeRegex = exports.strikeRegex = buildRegex(ValidAutoformatChars.STRIKE);
var codeRegex = exports.codeRegex = buildRegex(ValidAutoformatChars.CODE);
var codeRegexWithBackwardMatch = exports.codeRegexWithBackwardMatch = buildRegexNew(ValidAutoformatChars.CODE, true);
/**
* Create input rules for strong mark
*
* @param {Schema} schema
* @returns {InputRuleWrapper[]}
*/
function getStrongInputRules(schema, editorAnalyticsAPI) {
var ruleWithStrongAnalytics = (0, _utils.inputRuleWithAnalytics)({
action: _analytics.ACTION.FORMATTED,
actionSubject: _analytics.ACTION_SUBJECT.TEXT,
actionSubjectId: _analytics.ACTION_SUBJECT_ID.FORMAT_STRONG,
eventType: _analytics.EVENT_TYPE.TRACK,
attributes: {
inputMethod: _analytics.INPUT_METHOD.FORMATTING
}
}, editorAnalyticsAPI);
// **string** or __strong__ should bold the text
var doubleUnderscoreRule = (0, _utils.createRule)(strongRegex1, addMark(schema.marks.strong, schema, ValidAutoformatChars.STRONG));
var doubleAsterixRule = (0, _utils.createRule)(strongRegex2, addMark(schema.marks.strong, schema, ValidAutoformatChars.STRONG_MARKDOWN));
return [ruleWithStrongAnalytics(doubleUnderscoreRule), ruleWithStrongAnalytics(doubleAsterixRule)];
}
/**
* Create input rules for em mark
*
* @param {Schema} schema
* @returns {InputRuleWrapper[]}
*/
function getItalicInputRules(schema, editorAnalyticsAPI) {
var ruleWithItalicAnalytics = (0, _utils.inputRuleWithAnalytics)({
action: _analytics.ACTION.FORMATTED,
actionSubject: _analytics.ACTION_SUBJECT.TEXT,
actionSubjectId: _analytics.ACTION_SUBJECT_ID.FORMAT_ITALIC,
eventType: _analytics.EVENT_TYPE.TRACK,
attributes: {
inputMethod: _analytics.INPUT_METHOD.FORMATTING
}
}, editorAnalyticsAPI);
var underscoreRule = (0, _utils.createRule)(italicRegex1, addMark(schema.marks.em, schema, ValidAutoformatChars.ITALIC));
var asterixRule = (0, _utils.createRule)(italicRegex2, addMark(schema.marks.em, schema, ValidAutoformatChars.ITALIC_MARKDOWN));
return [ruleWithItalicAnalytics(underscoreRule), ruleWithItalicAnalytics(asterixRule)];
}
/**
* Create input rules for strike mark
*
* @param {Schema} schema
* @returns {InputRuleWrapper[]}
*/
function getStrikeInputRules(schema, editorAnalyticsAPI) {
var ruleWithStrikeAnalytics = (0, _utils.inputRuleWithAnalytics)({
action: _analytics.ACTION.FORMATTED,
actionSubject: _analytics.ACTION_SUBJECT.TEXT,
actionSubjectId: _analytics.ACTION_SUBJECT_ID.FORMAT_STRIKE,
eventType: _analytics.EVENT_TYPE.TRACK,
attributes: {
inputMethod: _analytics.INPUT_METHOD.FORMATTING
}
}, editorAnalyticsAPI);
var doubleTildeRule = (0, _utils.createRule)(strikeRegex, addMark(schema.marks.strike, schema, ValidAutoformatChars.STRIKE));
return [ruleWithStrikeAnalytics(doubleTildeRule)];
}
/**
* Create input rules for code mark
*
* @param {Schema} schema
* @returns {InputRuleWrapper[]}
*/
function getCodeInputRules(schema, editorAnalyticsAPI, api) {
var ruleWithCodeAnalytics = (0, _utils.inputRuleWithAnalytics)({
action: _analytics.ACTION.FORMATTED,
actionSubject: _analytics.ACTION_SUBJECT.TEXT,
actionSubjectId: _analytics.ACTION_SUBJECT_ID.FORMAT_CODE,
eventType: _analytics.EVENT_TYPE.TRACK,
attributes: {
inputMethod: _analytics.INPUT_METHOD.FORMATTING
}
}, editorAnalyticsAPI);
var allowsBackwardMatch = (0, _expValEquals.expValEquals)('platform_editor_lovability_inline_code', 'isEnabled', true);
var backTickRule = (0, _utils.createRule)(allowsBackwardMatch ? codeRegexWithBackwardMatch : codeRegex, addMark(schema.marks.code, schema, ValidAutoformatChars.CODE, api), allowsBackwardMatch);
return [ruleWithCodeAnalytics(backTickRule)];
}
function inputRulePlugin(schema, editorAnalyticsAPI, api) {
var rules = [];
if (schema.marks.strong) {
rules.push.apply(rules, (0, _toConsumableArray2.default)(getStrongInputRules(schema, editorAnalyticsAPI)));
}
if (schema.marks.em) {
rules.push.apply(rules, (0, _toConsumableArray2.default)(getItalicInputRules(schema, editorAnalyticsAPI)));
}
if (schema.marks.strike) {
rules.push.apply(rules, (0, _toConsumableArray2.default)(getStrikeInputRules(schema, editorAnalyticsAPI)));
}
if (schema.marks.code) {
rules.push.apply(rules, (0, _toConsumableArray2.default)(getCodeInputRules(schema, editorAnalyticsAPI, api)));
}
if (rules.length !== 0) {
return new _safePlugin.SafePlugin((0, _prosemirrorInputRules.createPlugin)('text-formatting', rules));
}
return;
}
var _default = exports.default = inputRulePlugin;