Feat/changeset ts (#6594)

* Migrated changeset

* Added more tests.

* Fixed test scopes
This commit is contained in:
SamTV12345 2024-08-18 12:14:24 +02:00 committed by GitHub
parent 3dae23a1e5
commit 28e04bdf71
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
37 changed files with 2540 additions and 1310 deletions

View file

@ -21,7 +21,7 @@
import AttributeMap from '../../static/js/AttributeMap';
import AttributePool from "../../static/js/AttributePool";
const Changeset = require('../../static/js/Changeset');
import {deserializeOps, splitAttributionLines, subattribution} from '../../static/js/Changeset';
const { checkValidRev } = require('./checkValidRev');
/*
@ -31,7 +31,7 @@ exports.getPadPlainText = (pad: { getInternalRevisionAText: (arg0: any) => any;
const _analyzeLine = exports._analyzeLine;
const atext = ((revNum !== undefined) ? pad.getInternalRevisionAText(checkValidRev(revNum)) : pad.atext);
const textLines = atext.text.slice(0, -1).split('\n');
const attribLines = Changeset.splitAttributionLines(atext.attribs, atext.text);
const attribLines = splitAttributionLines(atext.attribs, atext.text);
const apool = pad.pool;
const pieces = [];
@ -52,14 +52,14 @@ type LineModel = {
[id:string]:string|number|LineModel
}
exports._analyzeLine = (text:string, aline: LineModel, apool: AttributePool) => {
exports._analyzeLine = (text:string, aline: string, apool: AttributePool) => {
const line: LineModel = {};
// identify list
let lineMarker = 0;
line.listLevel = 0;
if (aline) {
const [op] = Changeset.deserializeOps(aline);
const [op] = deserializeOps(aline);
if (op != null) {
const attribs = AttributeMap.fromString(op.attribs, apool);
let listType = attribs.get('list');
@ -79,7 +79,7 @@ exports._analyzeLine = (text:string, aline: LineModel, apool: AttributePool) =>
}
if (lineMarker) {
line.text = text.substring(1);
line.aline = Changeset.subattribution(aline, 1);
line.aline = subattribution(aline, 1);
} else {
line.text = text;
line.aline = aline;

View file

@ -18,7 +18,7 @@ import {MapArrayType} from "../types/MapType";
* limitations under the License.
*/
const Changeset = require('../../static/js/Changeset');
import {deserializeOps, splitAttributionLines, subattribution} from '../../static/js/Changeset';
const attributes = require('../../static/js/attributes');
const padManager = require('../db/PadManager');
const _ = require('underscore');
@ -28,6 +28,8 @@ const eejs = require('../eejs');
const _analyzeLine = require('./ExportHelper')._analyzeLine;
const _encodeWhitespace = require('./ExportHelper')._encodeWhitespace;
import padutils from "../../static/js/pad_utils";
import {StringIterator} from "../../static/js/StringIterator";
import {StringAssembler} from "../../static/js/StringAssembler";
const getPadHTML = async (pad: PadType, revNum: string) => {
let atext = pad.atext;
@ -44,7 +46,7 @@ const getPadHTML = async (pad: PadType, revNum: string) => {
const getHTMLFromAtext = async (pad:PadType, atext: AText, authorColors?: string[]) => {
const apool = pad.apool();
const textLines = atext.text.slice(0, -1).split('\n');
const attribLines = Changeset.splitAttributionLines(atext.attribs, atext.text);
const attribLines = splitAttributionLines(atext.attribs, atext.text);
const tags = ['h1', 'h2', 'strong', 'em', 'u', 's'];
const props = ['heading1', 'heading2', 'bold', 'italic', 'underline', 'strikethrough'];
@ -80,6 +82,7 @@ const getHTMLFromAtext = async (pad:PadType, atext: AText, authorColors?: string
css += '<style>\n';
for (const a of Object.keys(apool.numToAttrib)) {
// @ts-ignore
const attr = apool.numToAttrib[a];
// skip non author attributes
@ -115,6 +118,7 @@ const getHTMLFromAtext = async (pad:PadType, atext: AText, authorColors?: string
// see hook exportHtmlAdditionalTagsWithData
attrib = propName;
}
// @ts-ignore
const propTrueNum = apool.putAttrib(attrib, true);
if (propTrueNum >= 0) {
anumMap[propTrueNum] = i;
@ -127,8 +131,8 @@ const getHTMLFromAtext = async (pad:PadType, atext: AText, authorColors?: string
// <b>Just bold<b> <b><i>Bold and italics</i></b> <i>Just italics</i>
// becomes
// <b>Just bold <i>Bold and italics</i></b> <i>Just italics</i>
const taker = Changeset.stringIterator(text);
const assem = Changeset.stringAssembler();
const taker = new StringIterator(text);
const assem = new StringAssembler();
const openTags:string[] = [];
const getSpanClassFor = (i: string) => {
@ -204,7 +208,8 @@ const getHTMLFromAtext = async (pad:PadType, atext: AText, authorColors?: string
return;
}
const ops = Changeset.deserializeOps(Changeset.subattribution(attribs, idx, idx + numChars));
// @ts-ignore
const ops = deserializeOps(subattribution(attribs, idx, idx + numChars));
idx += numChars;
// this iterates over every op string and decides which tags to open or to close

View file

@ -22,7 +22,9 @@
import {AText, PadType} from "../types/PadType";
import {MapType} from "../types/MapType";
const Changeset = require('../../static/js/Changeset');
import {deserializeOps, splitAttributionLines, subattribution} from '../../static/js/Changeset';
import {StringIterator} from "../../static/js/StringIterator";
import {StringAssembler} from "../../static/js/StringAssembler";
const attributes = require('../../static/js/attributes');
const padManager = require('../db/PadManager');
const _analyzeLine = require('./ExportHelper')._analyzeLine;
@ -45,13 +47,14 @@ const getPadTXT = async (pad: PadType, revNum: string) => {
const getTXTFromAtext = (pad: PadType, atext: AText, authorColors?:string) => {
const apool = pad.apool();
const textLines = atext.text.slice(0, -1).split('\n');
const attribLines = Changeset.splitAttributionLines(atext.attribs, atext.text);
const attribLines = splitAttributionLines(atext.attribs, atext.text);
const props = ['heading1', 'heading2', 'bold', 'italic', 'underline', 'strikethrough'];
const anumMap: MapType = {};
const css = '';
props.forEach((propName, i) => {
// @ts-ignore
const propTrueNum = apool.putAttrib([propName, true], true);
if (propTrueNum >= 0) {
anumMap[propTrueNum] = i;
@ -69,8 +72,8 @@ const getTXTFromAtext = (pad: PadType, atext: AText, authorColors?:string) => {
// <b>Just bold<b> <b><i>Bold and italics</i></b> <i>Just italics</i>
// becomes
// <b>Just bold <i>Bold and italics</i></b> <i>Just italics</i>
const taker = Changeset.stringIterator(text);
const assem = Changeset.stringAssembler();
const taker = new StringIterator(text);
const assem = new StringAssembler();
let idx = 0;
@ -79,7 +82,7 @@ const getTXTFromAtext = (pad: PadType, atext: AText, authorColors?:string) => {
return;
}
const ops = Changeset.deserializeOps(Changeset.subattribution(attribs, idx, idx + numChars));
const ops = deserializeOps(subattribution(attribs, idx, idx + numChars));
idx += numChars;
for (const o of ops) {

View file

@ -16,10 +16,11 @@
*/
import log4js from 'log4js';
const Changeset = require('../../static/js/Changeset');
import {deserializeOps} from '../../static/js/Changeset';
const contentcollector = require('../../static/js/contentcollector');
import jsdom from 'jsdom';
import {PadType} from "../types/PadType";
import {Builder} from "../../static/js/Builder";
const apiLogger = log4js.getLogger('ImportHtml');
let processor:any;
@ -69,13 +70,13 @@ exports.setPadHTML = async (pad: PadType, html:string, authorId = '') => {
const newAttribs = `${result.lineAttribs.join('|1+1')}|1+1`;
// create a new changeset with a helper builder object
const builder = Changeset.builder(1);
const builder = new Builder(1);
// assemble each line into the builder
let textIndex = 0;
const newTextStart = 0;
const newTextEnd = newText.length;
for (const op of Changeset.deserializeOps(newAttribs)) {
for (const op of deserializeOps(newAttribs)) {
const nextIndex = textIndex + op.chars;
if (!(nextIndex <= newTextStart || textIndex >= newTextEnd)) {
const start = Math.max(newTextStart, textIndex);

View file

@ -4,7 +4,12 @@ import {PadAuthor, PadType} from "../types/PadType";
import {MapArrayType} from "../types/MapType";
import AttributeMap from '../../static/js/AttributeMap';
const Changeset = require('../../static/js/Changeset');
import {applyToAText, checkRep, compose, deserializeOps, pack, splitAttributionLines, splitTextLines, unpack} from '../../static/js/Changeset';
import {Builder} from "../../static/js/Builder";
import {OpAssembler} from "../../static/js/OpAssembler";
import {numToString} from "../../static/js/ChangesetUtils";
import Op from "../../static/js/Op";
import {StringAssembler} from "../../static/js/StringAssembler";
const attributes = require('../../static/js/attributes');
const exportHtml = require('./ExportHtml');
@ -33,7 +38,7 @@ class PadDiff {
}
_isClearAuthorship(changeset: any){
// unpack
const unpacked = Changeset.unpack(changeset);
const unpacked = unpack(changeset);
// check if there is nothing in the charBank
if (unpacked.charBank !== '') {
@ -45,7 +50,7 @@ class PadDiff {
return false;
}
const [clearOperator, anotherOp] = Changeset.deserializeOps(unpacked.ops);
const [clearOperator, anotherOp] = deserializeOps(unpacked.ops);
// check if there is only one operator
if (anotherOp != null) return false;
@ -78,7 +83,7 @@ class PadDiff {
const atext = await this._pad.getInternalRevisionAText(rev);
// build clearAuthorship changeset
const builder = Changeset.builder(atext.text.length);
const builder = new Builder(atext.text.length);
builder.keepText(atext.text, [['author', '']], this._pad.pool);
const changeset = builder.toString();
@ -93,7 +98,7 @@ class PadDiff {
const changeset = await this._createClearAuthorship(rev);
// apply the clearAuthorship changeset
const newAText = Changeset.applyToAText(changeset, atext, this._pad.pool);
const newAText = applyToAText(changeset, atext, this._pad.pool);
return newAText;
}
@ -157,7 +162,7 @@ class PadDiff {
if (superChangeset == null) {
superChangeset = changeset;
} else {
superChangeset = Changeset.compose(superChangeset, changeset, this._pad.pool);
superChangeset = compose(superChangeset, changeset, this._pad.pool);
}
}
@ -171,10 +176,10 @@ class PadDiff {
const deletionChangeset = this._createDeletionChangeset(superChangeset, atext, this._pad.pool);
// apply the superChangeset, which includes all addings
atext = Changeset.applyToAText(superChangeset, atext, this._pad.pool);
atext = applyToAText(superChangeset, atext, this._pad.pool);
// apply the deletionChangeset, which adds a deletions
atext = Changeset.applyToAText(deletionChangeset, atext, this._pad.pool);
atext = applyToAText(deletionChangeset, atext, this._pad.pool);
}
return atext;
@ -209,22 +214,22 @@ class PadDiff {
_extendChangesetWithAuthor(changeset: any, author: any, apool: any){
// unpack
const unpacked = Changeset.unpack(changeset);
const unpacked = unpack(changeset);
const assem = Changeset.opAssembler();
const assem = new OpAssembler();
// create deleted attribs
const authorAttrib = apool.putAttrib(['author', author || '']);
const deletedAttrib = apool.putAttrib(['removed', true]);
const attribs = `*${Changeset.numToString(authorAttrib)}*${Changeset.numToString(deletedAttrib)}`;
const attribs = `*${numToString(authorAttrib)}*${numToString(deletedAttrib)}`;
for (const operator of Changeset.deserializeOps(unpacked.ops)) {
for (const operator of deserializeOps(unpacked.ops)) {
if (operator.opcode === '-') {
// this is a delete operator, extend it with the author
operator.attribs = attribs;
} else if (operator.opcode === '=' && operator.attribs) {
// this is operator changes only attributes, let's mark which author did that
operator.attribs += `*${Changeset.numToString(authorAttrib)}`;
operator.attribs += `*${numToString(authorAttrib)}`;
}
// append the new operator to our assembler
@ -232,26 +237,31 @@ class PadDiff {
}
// return the modified changeset
return Changeset.pack(unpacked.oldLen, unpacked.newLen, assem.toString(), unpacked.charBank);
return pack(unpacked.oldLen, unpacked.newLen, assem.toString(), unpacked.charBank);
}
_createDeletionChangeset(cs: any, startAText: any, apool: any){
const lines = Changeset.splitTextLines(startAText.text);
const alines = Changeset.splitAttributionLines(startAText.attribs, startAText.text);
const lines = splitTextLines(startAText.text);
const alines = splitAttributionLines(startAText.attribs, startAText.text);
// lines and alines are what the exports is meant to apply to.
// They may be arrays or objects with .get(i) and .length methods.
// They include final newlines on lines.
const linesGet = (idx: number) => {
// @ts-ignore
if (lines.get) {
// @ts-ignore
return lines.get(idx);
} else {
// @ts-ignore
return lines[idx];
}
};
const aLinesGet = (idx: number) => {
// @ts-ignore
if (alines.get) {
// @ts-ignore
return alines.get(idx);
} else {
return alines[idx];
@ -263,14 +273,14 @@ class PadDiff {
let curLineOps: { next: () => any; } | null = null;
let curLineOpsNext: { done: any; value: any; } | null = null;
let curLineOpsLine: number;
let curLineNextOp = new Changeset.Op('+');
let curLineNextOp = new Op('+');
const unpacked = Changeset.unpack(cs);
const builder = Changeset.builder(unpacked.newLen);
const unpacked = unpack(cs);
const builder = new Builder(unpacked.newLen);
const consumeAttribRuns = (numChars: number, func: Function /* (len, attribs, endsLine)*/) => {
if (!curLineOps || curLineOpsLine !== curLine) {
curLineOps = Changeset.deserializeOps(aLinesGet(curLine));
curLineOps = deserializeOps(aLinesGet(curLine));
curLineOpsNext = curLineOps!.next();
curLineOpsLine = curLine;
let indexIntoLine = 0;
@ -291,13 +301,13 @@ class PadDiff {
curChar = 0;
curLineOpsLine = curLine;
curLineNextOp.chars = 0;
curLineOps = Changeset.deserializeOps(aLinesGet(curLine));
curLineOps = deserializeOps(aLinesGet(curLine));
curLineOpsNext = curLineOps!.next();
}
if (!curLineNextOp.chars) {
if (curLineOpsNext!.done) {
curLineNextOp = new Changeset.Op();
curLineNextOp = new Op();
} else {
curLineNextOp = curLineOpsNext!.value;
curLineOpsNext = curLineOps!.next();
@ -332,7 +342,7 @@ class PadDiff {
const nextText = (numChars: number) => {
let len = 0;
const assem = Changeset.stringAssembler();
const assem = new StringAssembler();
const firstString = linesGet(curLine).substring(curChar);
len += firstString.length;
assem.append(firstString);
@ -360,7 +370,7 @@ class PadDiff {
};
};
for (const csOp of Changeset.deserializeOps(unpacked.ops)) {
for (const csOp of deserializeOps(unpacked.ops)) {
if (csOp.opcode === '=') {
const textBank = nextText(csOp.chars);
@ -442,7 +452,7 @@ class PadDiff {
}
}
return Changeset.checkRep(builder.toString());
return checkRep(builder.toString());
}
}
@ -450,6 +460,7 @@ class PadDiff {
// this method is 80% like Changeset.inverse. I just changed so instead of reverting,
// it adds deletions and attribute changes to the atext.
// @ts-ignore
PadDiff.prototype._createDeletionChangeset = function (cs, startAText, apool) {
};