UNPKG

@atlaskit/editor-plugin-text-formatting

Version:

Text-formatting plugin for @atlaskit/editor-core

311 lines (301 loc) 15.1 kB
"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;