Changeset: Migrate to the new attribute API

This commit is contained in:
Richard Hansen 2021-11-19 00:51:25 -05:00
parent f40d285109
commit f1eb7a25a6
15 changed files with 175 additions and 210 deletions

View file

@ -19,6 +19,7 @@
* limitations under the License.
*/
const AttributeMap = require('../../static/js/AttributeMap');
const padManager = require('../db/PadManager');
const Changeset = require('../../static/js/Changeset');
const ChatMessage = require('../../static/js/ChatMessage');
@ -32,7 +33,6 @@ const plugins = require('../../static/js/pluginfw/plugin_defs.js');
const log4js = require('log4js');
const messageLogger = log4js.getLogger('message');
const accessLogger = log4js.getLogger('access');
const _ = require('underscore');
const hooks = require('../../static/js/pluginfw/hooks.js');
const stats = require('../stats');
const assert = require('assert').strict;
@ -585,14 +585,6 @@ const handleUserChanges = async (socket, message) => {
// Verify that the changeset has valid syntax and is in canonical form
Changeset.checkRep(changeset);
// Verify that the attribute indexes used in the changeset are all
// defined in the accompanying attribute pool.
Changeset.eachAttribNumber(changeset, (n) => {
if (!wireApool.getAttrib(n)) {
throw new Error(`Attribute pool is missing attribute ${n} for changeset ${changeset}`);
}
});
// Validate all added 'author' attribs to be the same value as the current user
const iterator = Changeset.opIterator(Changeset.unpack(changeset).ops);
let op;
@ -605,19 +597,14 @@ const handleUserChanges = async (socket, message) => {
// - can have attribs, but they are discarded and don't show up in the attribs -
// but do show up in the pool
op.attribs.split('*').forEach((attr) => {
if (!attr) return;
attr = wireApool.getAttrib(Changeset.parseNum(attr));
if (!attr) return;
// the empty author is used in the clearAuthorship functionality so this
// should be the only exception
if ('author' === attr[0] && (attr[1] !== thisSession.author && attr[1] !== '')) {
throw new Error(`Author ${thisSession.author} tried to submit changes as author ` +
`${attr[1]} in changeset ${changeset}`);
}
});
// Besides verifying the author attribute, this serves a second purpose:
// AttributeMap.fromString() ensures that all attribute numbers are valid (it will throw if
// an attribute number isn't in the pool).
const opAuthorId = AttributeMap.fromString(op.attribs, wireApool).get('author');
if (opAuthorId && opAuthorId !== thisSession.author) {
throw new Error(`Author ${thisSession.author} tried to submit changes as author ` +
`${opAuthorId} in changeset ${changeset}`);
}
}
// ex. adoptChangesetAttribs
@ -758,11 +745,8 @@ const _correctMarkersInPad = (atext, apool) => {
let offset = 0;
while (iter.hasNext()) {
const op = iter.next();
const hasMarker = _.find(
AttributeManager.lineAttributes,
(attribute) => Changeset.opAttributeValue(op, attribute, apool)) !== undefined;
const attribs = AttributeMap.fromString(op.attribs, apool);
const hasMarker = AttributeManager.lineAttributes.some((a) => attribs.has(a));
if (hasMarker) {
for (let i = 0; i < op.chars; i++) {
if (offset > 0 && text.charAt(offset - 1) !== '\n') {

View file

@ -19,6 +19,7 @@
* limitations under the License.
*/
const AttributeMap = require('../../static/js/AttributeMap');
const Changeset = require('../../static/js/Changeset');
exports.getPadPlainText = (pad, revNum) => {
@ -54,7 +55,8 @@ exports._analyzeLine = (text, aline, apool) => {
const opIter = Changeset.opIterator(aline);
if (opIter.hasNext()) {
const op = opIter.next();
let listType = Changeset.opAttributeValue(op, 'list', apool);
const attribs = AttributeMap.fromString(op.attribs, apool);
let listType = attribs.get('list');
if (listType) {
lineMarker = 1;
listType = /([a-z]+)([0-9]+)/.exec(listType);
@ -63,7 +65,7 @@ exports._analyzeLine = (text, aline, apool) => {
line.listLevel = Number(listType[2]);
}
}
const start = Changeset.opAttributeValue(op, 'start', apool);
const start = attribs.get('start');
if (start) {
line.start = start;
}

View file

@ -16,6 +16,7 @@
*/
const Changeset = require('../../static/js/Changeset');
const attributes = require('../../static/js/attributes');
const padManager = require('../db/PadManager');
const _ = require('underscore');
const Security = require('../../static/js/security');
@ -206,11 +207,11 @@ const getHTMLFromAtext = async (pad, atext, authorColors) => {
const usedAttribs = [];
// mark all attribs as used
Changeset.eachAttribNumber(o.attribs, (a) => {
for (const a of attributes.decodeAttribString(o.attribs)) {
if (a in anumMap) {
usedAttribs.push(anumMap[a]); // i = 0 => bold, etc.
}
});
}
let outermostTag = -1;
// find the outer most open tag that is no longer used
for (let i = openTags.length - 1; i >= 0; i--) {

View file

@ -20,6 +20,7 @@
*/
const Changeset = require('../../static/js/Changeset');
const attributes = require('../../static/js/attributes');
const padManager = require('../db/PadManager');
const _analyzeLine = require('./ExportHelper')._analyzeLine;
@ -82,7 +83,7 @@ const getTXTFromAtext = (pad, atext, authorColors) => {
const o = iter.next();
let propChanged = false;
Changeset.eachAttribNumber(o.attribs, (a) => {
for (const a of attributes.decodeAttribString(o.attribs)) {
if (a in anumMap) {
const i = anumMap[a]; // i = 0 => bold, etc.
@ -93,7 +94,7 @@ const getTXTFromAtext = (pad, atext, authorColors) => {
propVals[i] = STAY;
}
}
});
}
for (let i = 0; i < propVals.length; i++) {
if (propVals[i] === true) {

View file

@ -1,5 +1,8 @@
'use strict';
const AttributeMap = require('../../static/js/AttributeMap');
const Changeset = require('../../static/js/Changeset');
const attributes = require('../../static/js/attributes');
const exportHtml = require('./ExportHtml');
function PadDiff(pad, fromRev, toRev) {
@ -54,17 +57,11 @@ PadDiff.prototype._isClearAuthorship = function (changeset) {
return false;
}
const attributes = [];
Changeset.eachAttribNumber(changeset, (attrNum) => {
attributes.push(attrNum);
});
const [appliedAttribute, anotherAttribute] =
attributes.attribsFromString(clearOperator.attribs, this._pad.pool);
// check that this changeset uses only one attribute
if (attributes.length !== 1) {
return false;
}
const appliedAttribute = this._pad.pool.getAttrib(attributes[0]);
// Check that the operation has exactly one attribute.
if (appliedAttribute == null || anotherAttribute != null) return false;
// check if the applied attribute is an anonymous author attribute
if (appliedAttribute[0] !== 'author' || appliedAttribute[1] !== '') {
@ -376,27 +373,19 @@ PadDiff.prototype._createDeletionChangeset = function (cs, startAText, apool) {
// If the text this operator applies to is only a star,
// than this is a false positive and should be ignored
if (csOp.attribs && textBank !== '*') {
const deletedAttrib = apool.putAttrib(['removed', true]);
let authorAttrib = apool.putAttrib(['author', '']);
const attribs = [];
Changeset.eachAttribNumber(csOp.attribs, (n) => {
const attrib = apool.getAttrib(n);
attribs.push(attrib);
if (attrib[0] === 'author') authorAttrib = n;
});
const attribs = AttributeMap.fromString(csOp.attribs, apool);
const undoBackToAttribs = cachedStrFunc((oldAttribsStr) => {
const backAttribs = [];
const oldAttribs = AttributeMap.fromString(oldAttribsStr, apool);
const backAttribs = new AttributeMap(apool)
.set('author', '')
.set('removed', 'true');
for (const [key, value] of attribs) {
const oldValue = Changeset.attribsAttributeValue(oldAttribsStr, key, apool);
if (oldValue !== value) backAttribs.push([key, oldValue])
const oldValue = oldAttribs.get(key);
if (oldValue !== value) backAttribs.set(key, oldValue);
}
return Changeset.makeAttribsString('=', backAttribs, apool);
return backAttribs.toString();
});
const oldAttribsAddition =
`*${Changeset.numToString(deletedAttrib)}*${Changeset.numToString(authorAttrib)}`;
let textLeftToProcess = textBank;
while (textLeftToProcess.length > 0) {
@ -429,7 +418,7 @@ PadDiff.prototype._createDeletionChangeset = function (cs, startAText, apool) {
let textBankIndex = 0;
consumeAttribRuns(lengthToProcess, (len, attribs, endsLine) => {
// get the old attributes back
const oldAttribs = (undoBackToAttribs(attribs) || '') + oldAttribsAddition;
const oldAttribs = undoBackToAttribs(attribs);
builder.insert(processText.substr(textBankIndex, len), oldAttribs);
textBankIndex += len;