mirror of
https://github.com/ether/etherpad-lite.git
synced 2025-04-25 18:06:15 -04:00
Changeset: Migrate to the new attribute API
This commit is contained in:
parent
f40d285109
commit
f1eb7a25a6
15 changed files with 175 additions and 210 deletions
|
@ -1,7 +1,9 @@
|
|||
'use strict';
|
||||
|
||||
const AttributeMap = require('./AttributeMap');
|
||||
const Changeset = require('./Changeset');
|
||||
const ChangesetUtils = require('./ChangesetUtils');
|
||||
const attributes = require('./attributes');
|
||||
const _ = require('./underscore');
|
||||
|
||||
const lineMarkerAttribute = 'lmkr';
|
||||
|
@ -150,7 +152,7 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({
|
|||
if (!aline) return '';
|
||||
const opIter = Changeset.opIterator(aline);
|
||||
if (!opIter.hasNext()) return '';
|
||||
return Changeset.opAttributeValue(opIter.next(), attributeName, this.rep.apool) || '';
|
||||
return AttributeMap.fromString(opIter.next().attribs, this.rep.apool).get(attributeName) || '';
|
||||
},
|
||||
|
||||
/*
|
||||
|
@ -164,10 +166,7 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({
|
|||
const opIter = Changeset.opIterator(aline);
|
||||
if (!opIter.hasNext()) return [];
|
||||
const op = opIter.next();
|
||||
if (!op.attribs) return [];
|
||||
const attributes = [];
|
||||
Changeset.eachAttribNumber(op.attribs, (n) => attributes.push(this.rep.apool.getAttrib(n)));
|
||||
return attributes;
|
||||
return [...attributes.attribsFromString(op.attribs, this.rep.apool)];
|
||||
},
|
||||
|
||||
/*
|
||||
|
@ -191,9 +190,7 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({
|
|||
}
|
||||
}
|
||||
|
||||
const withIt = Changeset.makeAttribsString('+', [
|
||||
[attributeName, 'true'],
|
||||
], rep.apool);
|
||||
const withIt = new AttributeMap(rep.apool).set(attributeName, 'true').toString();
|
||||
const withItRegex = new RegExp(`${withIt.replace(/\*/g, '\\*')}(\\*|$)`);
|
||||
const hasIt = (attribs) => withItRegex.test(attribs);
|
||||
|
||||
|
@ -274,10 +271,7 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({
|
|||
currentOperation = opIter.next();
|
||||
currentPointer += currentOperation.chars;
|
||||
if (currentPointer <= column) continue;
|
||||
const attributes = [];
|
||||
Changeset.eachAttribNumber(
|
||||
currentOperation.attribs, (n) => attributes.push(this.rep.apool.getAttrib(n)));
|
||||
return attributes;
|
||||
return [...attributes.attribsFromString(currentOperation.attribs, this.rep.apool)];
|
||||
}
|
||||
return [];
|
||||
},
|
||||
|
|
|
@ -22,7 +22,9 @@
|
|||
* https://github.com/ether/pad/blob/master/infrastructure/ace/www/easysync2.js
|
||||
*/
|
||||
|
||||
const AttributeMap = require('./AttributeMap');
|
||||
const AttributePool = require('./AttributePool');
|
||||
const attributes = require('./attributes');
|
||||
const {padutils} = require('./pad_utils');
|
||||
|
||||
/**
|
||||
|
@ -31,6 +33,15 @@ const {padutils} = require('./pad_utils');
|
|||
* @typedef {[string, string]} Attribute
|
||||
*/
|
||||
|
||||
/**
|
||||
* A concatenated sequence of zero or more attribute identifiers, each one represented by an
|
||||
* asterisk followed by a base-36 encoded attribute number.
|
||||
*
|
||||
* Examples: '', '*0', '*3*j*z*1q'
|
||||
*
|
||||
* @typedef {string} AttributeString
|
||||
*/
|
||||
|
||||
/**
|
||||
* This method is called whenever there is an error in the sync process.
|
||||
*
|
||||
|
@ -236,15 +247,19 @@ const copyOp = (op1, op2 = exports.newOp()) => Object.assign(op2, op1);
|
|||
*
|
||||
* @param {('-'|'+'|'=')} opcode - The operator to use.
|
||||
* @param {string} text - The text to remove/add/keep.
|
||||
* @param {(string|Attribute[])} [attribs] - The attributes to apply to the operations. See
|
||||
* `makeAttribsString`.
|
||||
* @param {?AttributePool} [pool] - See `makeAttribsString`.
|
||||
* @param {(Iterable<Attribute>|AttributeString)} [attribs] - The attributes to insert into the pool
|
||||
* (if necessary) and encode. If an attribute string, no checking is performed to ensure that
|
||||
* the attributes exist in the pool, are in the canonical order, and contain no duplicate keys.
|
||||
* If this is an iterable of attributes, `pool` must be non-null.
|
||||
* @param {?AttributePool} pool - Attribute pool. Required if `attribs` is an iterable of
|
||||
* attributes, ignored if `attribs` is an attribute string.
|
||||
* @yields {Op} One or two ops (depending on the presense of newlines) that cover the given text.
|
||||
* @returns {Generator<Op>}
|
||||
*/
|
||||
const opsFromText = function* (opcode, text, attribs = '', pool = null) {
|
||||
const op = exports.newOp(opcode);
|
||||
op.attribs = exports.makeAttribsString(opcode, attribs, pool);
|
||||
op.attribs = typeof attribs === 'string'
|
||||
? attribs : new AttributeMap(pool).update(attribs || [], opcode === '+').toString();
|
||||
const lastNewlinePos = text.lastIndexOf('\n');
|
||||
if (lastNewlinePos < 0) {
|
||||
op.chars = text.length;
|
||||
|
@ -387,9 +402,9 @@ exports.smartOpAssembler = () => {
|
|||
* @deprecated Use `opsFromText` instead.
|
||||
* @param {('-'|'+'|'=')} opcode - The operator to use.
|
||||
* @param {string} text - The text to remove/add/keep.
|
||||
* @param {(string|Attribute[])} attribs - The attributes to apply to the operations. See
|
||||
* `makeAttribsString`.
|
||||
* @param {?AttributePool} pool - See `makeAttribsString`.
|
||||
* @param {(string|Iterable<Attribute>)} attribs - The attributes to apply to the operations.
|
||||
* @param {?AttributePool} pool - Attribute pool. Only required if `attribs` is an iterable of
|
||||
* attribute key, value pairs.
|
||||
*/
|
||||
const appendOpWithText = (opcode, text, attribs, pool) => {
|
||||
padutils.warnWithStack('Changeset.smartOpAssembler().appendOpWithText() is deprecated; ' +
|
||||
|
@ -1109,20 +1124,11 @@ exports.mutateTextLines = (cs, lines) => {
|
|||
mut.close();
|
||||
};
|
||||
|
||||
/**
|
||||
* Sorts an array of attributes by key.
|
||||
*
|
||||
* @param {Attribute[]} attribs - The array of attributes to sort in place.
|
||||
* @returns {Attribute[]} The `attribs` array.
|
||||
*/
|
||||
const sortAttribs =
|
||||
(attribs) => attribs.sort((a, b) => (a[0] > b[0] ? 1 : 0) - (a[0] < b[0] ? 1 : 0));
|
||||
|
||||
/**
|
||||
* Composes two attribute strings (see below) into one.
|
||||
*
|
||||
* @param {string} att1 - first attribute string
|
||||
* @param {string} att2 - second attribue string
|
||||
* @param {AttributeString} att1 - first attribute string
|
||||
* @param {AttributeString} att2 - second attribue string
|
||||
* @param {boolean} resultIsMutation -
|
||||
* @param {AttributePool} pool - attribute pool
|
||||
* @returns {string}
|
||||
|
@ -1149,27 +1155,7 @@ exports.composeAttributes = (att1, att2, resultIsMutation, pool) => {
|
|||
return att2;
|
||||
}
|
||||
if (!att2) return att1;
|
||||
const atts = new Map();
|
||||
att1.replace(/\*([0-9a-z]+)/g, (_, a) => {
|
||||
const [key, val] = pool.getAttrib(exports.parseNum(a));
|
||||
atts.set(key, val);
|
||||
return '';
|
||||
});
|
||||
att2.replace(/\*([0-9a-z]+)/g, (_, a) => {
|
||||
const [key, val] = pool.getAttrib(exports.parseNum(a));
|
||||
if (val || resultIsMutation) {
|
||||
atts.set(key, val);
|
||||
} else {
|
||||
atts.delete(key);
|
||||
}
|
||||
return '';
|
||||
});
|
||||
const buf = exports.stringAssembler();
|
||||
for (const att of sortAttribs([...atts])) {
|
||||
buf.append('*');
|
||||
buf.append(exports.numToString(pool.putAttrib(att)));
|
||||
}
|
||||
return buf.toString();
|
||||
return AttributeMap.fromString(att1, pool).updateFromString(att2, !resultIsMutation).toString();
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -1611,16 +1597,21 @@ exports.makeAttribution = (text) => {
|
|||
* Iterates over attributes in exports, attribution string, or attribs property of an op and runs
|
||||
* function func on them.
|
||||
*
|
||||
* @deprecated Use `attributes.decodeAttribString()` instead.
|
||||
* @param {string} cs - changeset
|
||||
* @param {Function} func - function to call
|
||||
*/
|
||||
exports.eachAttribNumber = (cs, func) => {
|
||||
padutils.warnWithStack('Changeset.eachAttribNumber() is deprecated; ' +
|
||||
'use attributes.decodeAttribString() instead');
|
||||
let dollarPos = cs.indexOf('$');
|
||||
if (dollarPos < 0) {
|
||||
dollarPos = cs.length;
|
||||
}
|
||||
const upToDollar = cs.substring(0, dollarPos);
|
||||
|
||||
// WARNING: The following cannot be replaced with a call to `attributes.decodeAttribString()`
|
||||
// because that function only works on attribute strings, not serialized operations or changesets.
|
||||
upToDollar.replace(/\*([0-9a-z]+)/g, (_, a) => {
|
||||
func(exports.parseNum(a));
|
||||
return '';
|
||||
|
@ -1784,33 +1775,44 @@ exports.isIdentity = (cs) => {
|
|||
return unpacked.ops === '' && unpacked.oldLen === unpacked.newLen;
|
||||
};
|
||||
|
||||
/**
|
||||
* @deprecated Use an AttributeMap instead.
|
||||
*/
|
||||
const attribsAttributeValue = (attribs, key, pool) => {
|
||||
if (!attribs) return '';
|
||||
for (const [k, v] of attributes.attribsFromString(attribs, pool)) {
|
||||
if (k === key) return v;
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns all the values of attributes with a certain key in an Op attribs string.
|
||||
*
|
||||
* @deprecated Use an AttributeMap instead.
|
||||
* @param {Op} op - Op
|
||||
* @param {string} key - string to search for
|
||||
* @param {AttributePool} pool - attribute pool
|
||||
* @returns {string}
|
||||
*/
|
||||
exports.opAttributeValue = (op, key, pool) => exports.attribsAttributeValue(op.attribs, key, pool);
|
||||
exports.opAttributeValue = (op, key, pool) => {
|
||||
padutils.warnWithStack('Changeset.opAttributeValue() is deprecated; use an AttributeMap instead');
|
||||
return attribsAttributeValue(op.attribs, key, pool);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns all the values of attributes with a certain key in an attribs string.
|
||||
*
|
||||
* @param {string} attribs - Attribute string
|
||||
* @deprecated Use an AttributeMap instead.
|
||||
* @param {AttributeString} attribs - Attribute string
|
||||
* @param {string} key - string to search for
|
||||
* @param {AttributePool} pool - attribute pool
|
||||
* @returns {string}
|
||||
*/
|
||||
exports.attribsAttributeValue = (attribs, key, pool) => {
|
||||
if (!attribs) return '';
|
||||
let value = '';
|
||||
exports.eachAttribNumber(attribs, (n) => {
|
||||
if (pool.getAttribKey(n) === key) {
|
||||
value = pool.getAttribValue(n);
|
||||
}
|
||||
});
|
||||
return value;
|
||||
padutils.warnWithStack('Changeset.attribsAttributeValue() is deprecated; ' +
|
||||
'use an AttributeMap instead');
|
||||
return attribsAttributeValue(attribs, key, pool);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -1846,7 +1848,8 @@ exports.builder = (oldLen) => {
|
|||
*/
|
||||
keep: (N, L, attribs, pool) => {
|
||||
o.opcode = '=';
|
||||
o.attribs = (attribs && exports.makeAttribsString('=', attribs, pool)) || '';
|
||||
o.attribs = typeof attribs === 'string'
|
||||
? attribs : new AttributeMap(pool).update(attribs || []).toString();
|
||||
o.chars = N;
|
||||
o.lines = (L || 0);
|
||||
assem.append(o);
|
||||
|
@ -1908,8 +1911,9 @@ exports.builder = (oldLen) => {
|
|||
/**
|
||||
* Constructs an attribute string from a sequence of attributes.
|
||||
*
|
||||
* @deprecated Use `AttributeMap.prototype.toString()` or `attributes.attribsToString()` instead.
|
||||
* @param {string} opcode - The opcode for the Op that will get the resulting attribute string.
|
||||
* @param {?(Attribute[]|AttributeString)} attribs - The attributes to insert into the pool
|
||||
* @param {?(Iterable<Attribute>|AttributeString)} attribs - The attributes to insert into the pool
|
||||
* (if necessary) and encode. If an attribute string, no checking is performed to ensure that
|
||||
* the attributes exist in the pool, are in the canonical order, and contain no duplicate keys.
|
||||
* If this is an iterable of attributes, `pool` must be non-null.
|
||||
|
@ -1918,11 +1922,12 @@ exports.builder = (oldLen) => {
|
|||
* @returns {AttributeString}
|
||||
*/
|
||||
exports.makeAttribsString = (opcode, attribs, pool) => {
|
||||
padutils.warnWithStack(
|
||||
'Changeset.makeAttribsString() is deprecated; ' +
|
||||
'use AttributeMap.prototype.toString() or attributes.attribsToString() instead');
|
||||
if (!attribs || !['=', '+'].includes(opcode)) return '';
|
||||
if (typeof attribs === 'string') return attribs;
|
||||
return sortAttribs(attribs.filter(([k, v]) => v || opcode === '='))
|
||||
.map((a) => `*${exports.numToString(pool.putAttrib(a))}`)
|
||||
.join('');
|
||||
return new AttributeMap(pool).update(attribs, opcode === '+').toString();
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -2085,17 +2090,15 @@ exports.inverse = (cs, lines, alines, pool) => {
|
|||
const csOp = csIter.next();
|
||||
if (csOp.opcode === '=') {
|
||||
if (csOp.attribs) {
|
||||
const csAttribs = [];
|
||||
exports.eachAttribNumber(csOp.attribs, (n) => csAttribs.push(pool.getAttrib(n)));
|
||||
const undoBackToAttribs = cachedStrFunc((attribs) => {
|
||||
const backAttribs = [];
|
||||
for (const [appliedKey, appliedValue] of csAttribs) {
|
||||
const oldValue = exports.attribsAttributeValue(attribs, appliedKey, pool);
|
||||
if (appliedValue !== oldValue) {
|
||||
backAttribs.push([appliedKey, oldValue]);
|
||||
}
|
||||
const attribs = AttributeMap.fromString(csOp.attribs, pool);
|
||||
const undoBackToAttribs = cachedStrFunc((oldAttribsStr) => {
|
||||
const oldAttribs = AttributeMap.fromString(oldAttribsStr, pool);
|
||||
const backAttribs = new AttributeMap(pool);
|
||||
for (const [key, value] of attribs) {
|
||||
const oldValue = oldAttribs.get(key) || '';
|
||||
if (oldValue !== value) backAttribs.set(key, oldValue);
|
||||
}
|
||||
return exports.makeAttribsString('=', backAttribs, pool);
|
||||
return backAttribs.toString();
|
||||
});
|
||||
consumeAttribRuns(csOp.chars, (len, attribs, endsLine) => {
|
||||
builder.keep(len, endsLine ? 1 : 0, undoBackToAttribs(attribs));
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
*/
|
||||
let documentAttributeManager;
|
||||
|
||||
const AttributeMap = require('./AttributeMap');
|
||||
const browser = require('./vendors/browser');
|
||||
const padutils = require('./pad_utils').padutils;
|
||||
const Ace2Common = require('./ace2_common');
|
||||
|
@ -1542,9 +1543,7 @@ function Ace2Inner(editorInfo, cssManagers) {
|
|||
}
|
||||
}
|
||||
|
||||
const withIt = Changeset.makeAttribsString('+', [
|
||||
[attributeName, 'true'],
|
||||
], rep.apool);
|
||||
const withIt = new AttributeMap(rep.apool).set(attributeName, 'true').toString();
|
||||
const withItRegex = new RegExp(`${withIt.replace(/\*/g, '\\*')}(\\*|$)`);
|
||||
const hasIt = (attribs) => withItRegex.test(attribs);
|
||||
|
||||
|
@ -1608,9 +1607,7 @@ function Ace2Inner(editorInfo, cssManagers) {
|
|||
if (!(rep.selStart && rep.selEnd)) return;
|
||||
|
||||
let selectionAllHasIt = true;
|
||||
const withIt = Changeset.makeAttribsString('+', [
|
||||
[attributeName, 'true'],
|
||||
], rep.apool);
|
||||
const withIt = new AttributeMap(rep.apool).set(attributeName, 'true').toString();
|
||||
const withItRegex = new RegExp(`${withIt.replace(/\*/g, '\\*')}(\\*|$)`);
|
||||
|
||||
const hasIt = (attribs) => withItRegex.test(attribs);
|
||||
|
@ -1820,22 +1817,15 @@ function Ace2Inner(editorInfo, cssManagers) {
|
|||
}
|
||||
|
||||
let isNewTextMultiauthor = false;
|
||||
const authorAtt = Changeset.makeAttribsString('+', (thisAuthor ? [
|
||||
['author', thisAuthor],
|
||||
] : []), rep.apool);
|
||||
const authorizer = cachedStrFunc((oldAtts) => {
|
||||
if (isNewTextMultiauthor) {
|
||||
// prefer colors from DOM
|
||||
return Changeset.composeAttributes(authorAtt, oldAtts, true, rep.apool);
|
||||
} else {
|
||||
// use this author's color
|
||||
return Changeset.composeAttributes(oldAtts, authorAtt, true, rep.apool);
|
||||
}
|
||||
const attribs = AttributeMap.fromString(oldAtts, rep.apool);
|
||||
if (!isNewTextMultiauthor || !attribs.has('author')) attribs.set('author', thisAuthor);
|
||||
return attribs.toString();
|
||||
});
|
||||
|
||||
let foundDomAuthor = '';
|
||||
eachAttribRun(newAttribs, (start, end, attribs) => {
|
||||
const a = Changeset.attribsAttributeValue(attribs, 'author', rep.apool);
|
||||
const a = AttributeMap.fromString(attribs, rep.apool).get('author');
|
||||
if (a && a !== foundDomAuthor) {
|
||||
if (!foundDomAuthor) {
|
||||
foundDomAuthor = a;
|
||||
|
@ -2632,8 +2622,8 @@ function Ace2Inner(editorInfo, cssManagers) {
|
|||
const opIter = Changeset.opIterator(alineAttrs);
|
||||
while (opIter.hasNext()) {
|
||||
const op = opIter.next();
|
||||
const authorId = Changeset.opAttributeValue(op, 'author', apool);
|
||||
if (authorId !== '') authorIds.add(authorId);
|
||||
const authorId = AttributeMap.fromString(op.attribs, apool).get('author');
|
||||
if (authorId) authorIds.add(authorId);
|
||||
}
|
||||
}
|
||||
const idToName = new Map(parent.parent.pad.userList().map((a) => [a.userId, a.name]));
|
||||
|
|
|
@ -26,6 +26,7 @@ const makeCSSManager = require('./cssmanager').makeCSSManager;
|
|||
const domline = require('./domline').domline;
|
||||
const AttribPool = require('./AttributePool');
|
||||
const Changeset = require('./Changeset');
|
||||
const attributes = require('./attributes');
|
||||
const linestylefilter = require('./linestylefilter').linestylefilter;
|
||||
const colorutils = require('./colorutils').colorutils;
|
||||
const _ = require('./underscore');
|
||||
|
@ -114,20 +115,18 @@ const loadBroadcastJS = (socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, Bro
|
|||
},
|
||||
|
||||
getActiveAuthors() {
|
||||
const authors = [];
|
||||
const seenNums = {};
|
||||
const alines = this.alines;
|
||||
for (let i = 0; i < alines.length; i++) {
|
||||
Changeset.eachAttribNumber(alines[i], (n) => {
|
||||
if (seenNums[n]) return;
|
||||
seenNums[n] = true;
|
||||
if (this.apool.getAttribKey(n) !== 'author') return;
|
||||
const a = this.apool.getAttribValue(n);
|
||||
if (a) authors.push(a);
|
||||
});
|
||||
const authorIds = new Set();
|
||||
for (const aline of this.alines) {
|
||||
const opIter = Changeset.opIterator(aline);
|
||||
while (opIter.hasNext()) {
|
||||
const op = opIter.next();
|
||||
for (const [k, v] of attributes.attribsFromString(op.attribs, this.apool)) {
|
||||
if (k !== 'author') continue;
|
||||
if (v) authorIds.add(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
authors.sort();
|
||||
return authors;
|
||||
return [...authorIds].sort();
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const AttributeMap = require('./AttributeMap');
|
||||
const AttributePool = require('./AttributePool');
|
||||
const Changeset = require('./Changeset');
|
||||
|
||||
|
@ -141,7 +142,6 @@ const makeChangesetTracker = (scheduler, apool, aceCallbacksProvider) => {
|
|||
|
||||
// Sanitize authorship: Replace all author attributes with this user's author ID in case the
|
||||
// text was copied from another author.
|
||||
const authorAttr = Changeset.numToString(apool.putAttrib(['author', authorId]));
|
||||
const cs = Changeset.unpack(userChangeset);
|
||||
const iterator = Changeset.opIterator(cs.ops);
|
||||
let op;
|
||||
|
@ -150,18 +150,12 @@ const makeChangesetTracker = (scheduler, apool, aceCallbacksProvider) => {
|
|||
while (iterator.hasNext()) {
|
||||
op = iterator.next();
|
||||
if (op.opcode === '+') {
|
||||
let newAttrs = '';
|
||||
|
||||
op.attribs.split('*').forEach((attrNum) => {
|
||||
if (!attrNum) return;
|
||||
const attr = apool.getAttrib(parseInt(attrNum, 36));
|
||||
if (!attr) return;
|
||||
if ('author' === attr[0]) {
|
||||
// replace that author with the current one
|
||||
newAttrs += `*${authorAttr}`;
|
||||
} else { newAttrs += `*${attrNum}`; } // overtake all other attribs as is
|
||||
});
|
||||
op.attribs = newAttrs;
|
||||
const attribs = AttributeMap.fromString(op.attribs, apool);
|
||||
const oldAuthorId = attribs.get('author');
|
||||
if (oldAuthorId != null && oldAuthorId !== authorId) {
|
||||
attribs.set('author', authorId);
|
||||
op.attribs = attribs.toString();
|
||||
}
|
||||
}
|
||||
assem.append(op);
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
|
||||
const _MAX_LIST_LEVEL = 16;
|
||||
|
||||
const AttributeMap = require('./AttributeMap');
|
||||
const UNorm = require('unorm');
|
||||
const Changeset = require('./Changeset');
|
||||
const hooks = require('./pluginfw/hooks');
|
||||
|
@ -227,7 +228,7 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author)
|
|||
};
|
||||
|
||||
const _recalcAttribString = (state) => {
|
||||
const lst = [];
|
||||
const attribs = new AttributeMap(apool);
|
||||
for (const [a, count] of Object.entries(state.attribs)) {
|
||||
if (!count) continue;
|
||||
// The following splitting of the attribute name is a workaround
|
||||
|
@ -241,32 +242,31 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author)
|
|||
if (attributeSplits.length > 1) {
|
||||
// the attribute name follows the convention key::value
|
||||
// so save it as a key value attribute
|
||||
lst.push([attributeSplits[0], attributeSplits[1]]);
|
||||
const [k, v] = attributeSplits;
|
||||
if (v) attribs.set(k, v);
|
||||
} else {
|
||||
// the "normal" case, the attribute is just a switch
|
||||
// so set it true
|
||||
lst.push([a, 'true']);
|
||||
attribs.set(a, 'true');
|
||||
}
|
||||
}
|
||||
if (state.authorLevel > 0) {
|
||||
const authorAttrib = ['author', state.author];
|
||||
if (apool.putAttrib(authorAttrib, true) >= 0) {
|
||||
if (apool.putAttrib(['author', state.author], true) >= 0) {
|
||||
// require that author already be in pool
|
||||
// (don't add authors from other documents, etc.)
|
||||
lst.push(authorAttrib);
|
||||
if (state.author) attribs.set('author', state.author);
|
||||
}
|
||||
}
|
||||
state.attribString = Changeset.makeAttribsString('+', lst, apool);
|
||||
state.attribString = attribs.toString();
|
||||
};
|
||||
|
||||
const _produceLineAttributesMarker = (state) => {
|
||||
// TODO: This has to go to AttributeManager.
|
||||
const attributes = [
|
||||
['lmkr', '1'],
|
||||
['insertorder', 'first'],
|
||||
...Object.entries(state.lineAttributes),
|
||||
];
|
||||
lines.appendText('*', Changeset.makeAttribsString('+', attributes, apool));
|
||||
const attribs = new AttributeMap(apool)
|
||||
.set('lmkr', '1')
|
||||
.set('insertorder', 'first')
|
||||
.update(Object.entries(state.lineAttributes).map(([k, v]) => [k, v || '']), true);
|
||||
lines.appendText('*', attribs.toString());
|
||||
};
|
||||
cc.startNewLine = (state) => {
|
||||
if (state) {
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
// requires: undefined
|
||||
|
||||
const Changeset = require('./Changeset');
|
||||
const attributes = require('./attributes');
|
||||
const hooks = require('./pluginfw/hooks');
|
||||
const linestylefilter = {};
|
||||
const AttributeManager = require('./AttributeManager');
|
||||
|
@ -73,10 +74,8 @@ linestylefilter.getLineStyleFilter = (lineLength, aline, textAndClassFunc, apool
|
|||
let classes = '';
|
||||
let isLineAttribMarker = false;
|
||||
|
||||
// For each attribute number
|
||||
Changeset.eachAttribNumber(attribs, (n) => {
|
||||
const [key, value] = apool.getAttrib(n);
|
||||
if (!key || !value) return;
|
||||
for (const [key, value] of attributes.attribsFromString(attribs, apool)) {
|
||||
if (!key || !value) continue;
|
||||
if (!isLineAttribMarker && AttributeManager.lineAttributes.indexOf(key) >= 0) {
|
||||
isLineAttribMarker = true;
|
||||
}
|
||||
|
@ -93,7 +92,7 @@ linestylefilter.getLineStyleFilter = (lineLength, aline, textAndClassFunc, apool
|
|||
const results = hooks.callAll('aceAttribsToClasses', {linestylefilter, key, value});
|
||||
classes += ` ${results.join(' ')}`;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (isLineAttribMarker) classes += ` ${lineAttributeMarker}`;
|
||||
return classes.substring(1);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue