@atlaskit/editor-plugin-custom-autoformat
Version:
Custom autoformat plugin for @atlaskit/editor-core
103 lines (96 loc) • 4.34 kB
JavaScript
import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
import _regeneratorRuntime from "@babel/runtime/regenerator";
import { processRawValue } from '@atlaskit/editor-common/utils';
import { closeHistory } from '@atlaskit/prosemirror-history';
import { autoformatAction } from './utils';
export var buildHandler = function buildHandler(_regex, handler) {
return /*#__PURE__*/function () {
var _ref = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(view, match, start, end) {
var replacementPromise, replacementData, replacementNode;
return _regeneratorRuntime.wrap(function _callee$(_context) {
while (1) switch (_context.prev = _context.next) {
case 0:
replacementPromise = handler(match.slice(1, match.length - 1)); // queue the position and match pair so that we can remap across transactions
// while we wait for the replacmentPromise to resolve
view.dispatch(autoformatAction(view.state.tr, {
action: 'matched',
match: match,
start: start,
end: end
}));
// ask the provider to give us an ADF node to replace the text with
_context.next = 4;
return replacementPromise;
case 4:
replacementData = _context.sent;
replacementNode = processRawValue(view.state.schema, replacementData);
view.dispatch(autoformatAction(view.state.tr, {
action: 'resolved',
matchString: match[0],
replacement: replacementNode
}));
return _context.abrupt("return", replacementData);
case 8:
case "end":
return _context.stop();
}
}, _callee);
}));
return function (_x, _x2, _x3, _x4) {
return _ref.apply(this, arguments);
};
}();
};
/**
* Shift enter adds an Object Replacement Character (/ufffc) after
* the first word in the soft break line. Text between replaces this
* with a '|' as it's a non-text node. We still want to replaceWith
* on a string starting with an Object Replacement Character.
*/
export var isSoftBreakMatch = function isSoftBreakMatch(docText, match) {
var REPLACEMENT_CHARACTER = "\uFFFC";
var docTextStartChar = docText[0];
var docTextEndSlice = docText.slice(1);
var matchStartChar = match[0][0];
var matchEndSlice = match[0].slice(1);
var slicesMatch = docTextEndSlice === matchEndSlice;
var isAlternator = docTextStartChar === '|';
var isObjReplaceChar = matchStartChar === REPLACEMENT_CHARACTER;
return slicesMatch && isAlternator && isObjReplaceChar;
};
export var completeReplacements = function completeReplacements(view, state) {
var inlineCard = view.state.schema.nodes.inlineCard;
state.matches.forEach(function (completedMatch) {
var matchingRequests = state.resolving.filter(function (candidate) {
return candidate.match[0] === completedMatch.matchString;
});
var tr = view.state.tr;
matchingRequests.forEach(function (request) {
var match = request.match,
start = request.start,
end = request.end;
var replacement = completedMatch.replacement;
var prefix = match[1];
var suffix = match[match.length - 1];
var matchEndPos = end + suffix.length;
// only permit inlineCard as replacement target for now
if (!replacement || replacement.type !== inlineCard && !replacement.isText) {
return;
}
// get the current document text, adding # or | if we cross node boundaries
var docText = view.state.doc.textBetween(start, matchEndPos, '#', '|');
var canAutoformatAfterSoftbreak = isSoftBreakMatch(docText, match);
// only replace if text still remains the same as when typed at the start
if (docText === match[0] || canAutoformatAfterSoftbreak) {
tr = tr.replaceWith(tr.mapping.map(start + prefix.length), tr.mapping.map(end, -1), replacement);
}
});
// clear this match from plugin state now that we've processed it
tr = autoformatAction(tr, {
action: 'finish',
matchString: completedMatch.matchString
});
// and dispatch the replacement, closing history for cmd+z to allow undo separately
view.dispatch(closeHistory(tr));
});
};