Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,24 @@ export class MotionDiffService {
return HtmlDiff.getAmendmentParagraphsLines(paragraphNo, origText, newText, lineLength, changes);
}

/**
* Returns the index of the first difference between two texts
*
* @param {string} origText The original text
* @param {string} newText The changed text
* @return {number | null} Token index of first change, or null if no changes
*/
public getFirstChangedTokenIndex(origText: string, newText: string): number | null {
if (origText === newText) return null;

const minLen = Math.min(origText.length, newText.length);
for (let i = 0; i < minLen; i += 1) {
if (origText[i] !== newText[i]) return i;
}

return minLen;
}

/**
* Returns the HTML with the changes, optionally with a highlighted line.
* The original motion needs to be provided.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,8 +242,19 @@ export class MotionControllerService extends BaseMeetingControllerService<ViewMo
includeUnchanged
);
};

viewModel.getAmendmentFirstChangeIndex = (recoMode: ChangeRecoMode): number | null => {
const changeRecos = viewModel.change_recommendations.filter(changeReco => changeReco.showInFinalView());
return this.motionLineNumbering.getAmendmentFirstChangeIndex(
viewModel,
this._lineLength,
recoMode,
changeRecos
);
};
} else {
viewModel.getAmendmentParagraphLines = (): DiffLinesInParagraph[] => [];
viewModel.getAmendmentFirstChangeIndex = (): number | null => null;
}
viewModel.getExtendedStateLabel = (): string => this.getExtendedStateLabel(viewModel);
viewModel.getExtendedRecommendationLabel = (): string => this.getExtendedRecommendationLabel(viewModel);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ export class MotionLineNumberingService {
* @param {number} lineLength
* @returns {string[]}
*/
public getTextParagraphs(motion: ViewMotion, lineBreaks: boolean, lineLength: number): string[] {
public getTextParagraphs(motion: ViewMotion, lineBreaks: boolean, lineLength?: number): string[] {
if (!motion) {
return [];
}
Expand Down Expand Up @@ -307,6 +307,56 @@ export class MotionLineNumberingService {
.filter((para: DiffLinesInParagraph | null) => !!para) as DiffLinesInParagraph[];
}

/**
* Returns the first token that has a change
*
* Should only be called for paragraph-based amendments.
*
* @param {ViewMotion} amendment
* @param {number} lineLength
* @param {ChangeRecoMode} crMode
* @param {ViewMotionChangeRecommendation[]} changeRecommendations
* @returns {DiffLinesInParagraph}
*/
public getAmendmentFirstChangeIndex(
amendment: ViewMotion,
lineLength: number,
crMode: ChangeRecoMode,
changeRecommendations: ViewMotionChangeRecommendation[]
): number | null {
const motion = amendment.lead_motion as ViewMotion;
const baseParagraphs = this.getTextParagraphs(motion, false);

let amendmentParagraphs: string[] = [];
if (crMode === ChangeRecoMode.Original || changeRecommendations.length === 0) {
amendmentParagraphs = baseParagraphs.map(
(_: string, paraNo: number) => amendment.amendment_paragraph_text(paraNo) as string
);
} else {
amendmentParagraphs = this.applyChangesToAmendment(amendment, lineLength, changeRecommendations, true).map(
p => (p ? this.lineNumberingService.stripLineNumbers(p) : p)
) as string[];
}

let paragraphStart = 0;
for (let paraNo = 0; paraNo < baseParagraphs.length; paraNo++) {
const newText = amendmentParagraphs[paraNo];
if (baseParagraphs[paraNo] !== undefined) {
const origText = baseParagraphs[paraNo].replace(/\n/gm, ``);
if (newText !== null) {
const tokenIdx = this.diffService.getFirstChangedTokenIndex(origText, newText);
if (tokenIdx !== null) {
return paragraphStart + tokenIdx;
}
}

paragraphStart += origText.length;
}
}

return null;
}

public getAmendmentParagraphLinesTitle(paragraph: DiffLinesInParagraph): string {
if (paragraph.diffLineTo === paragraph.diffLineFrom) {
return this.translate.instant(`Line`) + ` ` + paragraph.diffLineFrom.toString(10);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export class AmendmentListSortService extends MotionListBaseSortService {

private amendmentSortOptions: OsSortingOption<ViewMotion>[] = [
{
property: `parentAndLineNumber`,
property: `parentAndChangeIndex`,
label: this.translate.instant(`Main motion and line number`),
baseKeys: [`amendment_paragraphs`],
foreignBaseKeys: {
Expand All @@ -28,7 +28,7 @@ export class AmendmentListSortService extends MotionListBaseSortService {

public constructor() {
super({
sortProperty: `parentAndLineNumber`,
sortProperty: `parentAndChangeIndex`,
sortAscending: true
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,14 +217,20 @@ export class ViewMotion extends BaseProjectableViewModel<Motion> {
}

/**
* Get the number of the first diff line, in case a motion is an amendment
* Get the index of the first diff change, in case a motion is an amendment
*/
public get parentAndLineNumber(): string | null {
public get parentAndChangeIndex(): string | null {
if (this.isParagraphBasedAmendment() && this.lead_motion && this.changedAmendmentLines?.length) {
return `${this.lead_motion.number} ${this.changedAmendmentLines[0].diffLineFrom}`;
} else {
return null;
if (this._firstChangeIndex === undefined) {
this._firstChangeIndex = this.getAmendmentFirstChangeIndex(ChangeRecoMode.Changed);
}

if (this._firstChangeIndex !== null) {
return `${this.lead_motion.number} ${this._firstChangeIndex}`;
}
}

return null;
}

public get forwardingStatus(): ForwardingStatus {
Expand All @@ -249,6 +255,7 @@ export class ViewMotion extends BaseProjectableViewModel<Motion> {

private _changedAmendmentLines: DiffLinesInParagraph[] | null = null;
private _affectedAmendmentLines: DiffLinesInParagraph[] | null = null;
private _firstChangeIndex: number | null | undefined = undefined;

public getVotingText(context: VotingTextContext<ViewMotion>): string {
const motionTranslation = context.translateFn(`Motion`);
Expand All @@ -264,6 +271,8 @@ export class ViewMotion extends BaseProjectableViewModel<Motion> {
includeUnchanged?: boolean
) => DiffLinesInParagraph[] = () => [];

public getAmendmentFirstChangeIndex: (recoMode: ChangeRecoMode) => number | null = () => null;

public getParagraphTitleByParagraph!: (paragraph: DiffLinesInParagraph) => string | null;
// This is set by the repository
public getNumberOrTitle!: () => string;
Expand Down
41 changes: 28 additions & 13 deletions packages/openslides-motion-diff/src/diff/internal-diff.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { splitStringKeepSeperator } from "../utils/utils";
import { normalizeHtmlForDiff } from "./internal";

/**
Expand Down Expand Up @@ -143,6 +142,17 @@ function diffArrays(oldArr: any, newArr: any): any {
return { o: oldArr, n: newArr };
}

const TOKENIZE_REGEXES = {
prependLt: /(?=<)/g,
appendGt: /(.*?>)/g,
space: /( )/g,
dot: /(\.)/g,
comma: /(,)/g,
exclaim: /(!)/g,
dash: /(-)/g,
appendNewline: /(.*?\n)/g,
};

/**
* This method splits a string into an array of strings, such as that it can be used by the diff method.
* Mainly it tries to split it into single words, but prevents HTML tags from being split into different elements.
Expand All @@ -151,17 +161,19 @@ function diffArrays(oldArr: any, newArr: any): any {
* @returns {string[]}
*/
function tokenizeHtml(str: string): string[] {
const splitConfigs = [
{ by: `<`, regex: TOKENIZE_REGEXES.prependLt, append: false },
{ by: `>`, regex: TOKENIZE_REGEXES.appendGt, append: true },
{ by: ` `, regex: TOKENIZE_REGEXES.space, append: false },
{ by: `.`, regex: TOKENIZE_REGEXES.dot, append: false },
{ by: `,`, regex: TOKENIZE_REGEXES.comma, append: false },
{ by: `!`, regex: TOKENIZE_REGEXES.exclaim, append: false },
{ by: `-`, regex: TOKENIZE_REGEXES.dash, append: false },
{ by: `\n`, regex: TOKENIZE_REGEXES.appendNewline, append: true },
];

let res = [str];
for (const splitConf of [
{ by: `<`, type: `prepend` },
{ by: `>`, type: `append` },
{ by: ` ` },
{ by: `.` },
{ by: `,` },
{ by: `!` },
{ by: `-` },
{ by: `\n`, type: `append` }
]) {
for (const splitConf of splitConfigs) {
const newArr = [];
for (const str of res) {
// Don't split HTML tags
Expand All @@ -170,10 +182,13 @@ function tokenizeHtml(str: string): string[] {
continue;
}

newArr.push(...splitStringKeepSeperator(str, splitConf.by, splitConf.type));
const parts = str.split(splitConf.regex);
newArr.push(
...(splitConf.append ? parts.filter((el) => el !== ``) : parts),
);
}
res = newArr;
}

return res.filter(el => el !== ``);
return res.filter((el) => el !== ``);
}
37 changes: 0 additions & 37 deletions packages/openslides-motion-diff/src/utils/utils.spec.ts

This file was deleted.

36 changes: 0 additions & 36 deletions packages/openslides-motion-diff/src/utils/utils.ts

This file was deleted.

Loading