Skip to content

Commit

Permalink
add formatTextSegmentBeforeSelectionMarker
Browse files Browse the repository at this point in the history
  • Loading branch information
juliaroldi committed Apr 3, 2024
1 parent f9514d6 commit 80f1eca
Show file tree
Hide file tree
Showing 3 changed files with 255 additions and 55 deletions.
Original file line number Diff line number Diff line change
@@ -1,69 +1,49 @@
import { getSelectedSegmentsAndParagraphs } from 'roosterjs-content-model-dom';
import { formatTextSegmentBeforeSelectionMarker } from '../../pluginUtils/formatTextSegmentBeforeSelectionMarker';
import { splitTextSegment } from '../../pluginUtils/splitTextSegment';
import type { IEditor } from 'roosterjs-content-model-types';

/**
* @internal
*/
export function transformHyphen(editor: IEditor) {
editor.formatContentModel((model, context) => {
const selectedAndParagraphs = getSelectedSegmentsAndParagraphs(
model,
false /*includingFormatHolder*/
);
if (selectedAndParagraphs.length > 0 && selectedAndParagraphs[0][1]) {
const marker = selectedAndParagraphs[0][0];
if (marker.segmentType == 'SelectionMarker') {
const paragraph = selectedAndParagraphs[0][1];
const markerIndex = paragraph.segments.indexOf(marker);
if (markerIndex > 0) {
const previousSegment = paragraph.segments[markerIndex - 1];
if (
previousSegment.segmentType === 'Text' &&
previousSegment.text.indexOf('--') > -1
) {
const segments = previousSegment.text.split(' ');
const dashes = segments[segments.length - 2];
if (dashes === '--') {
const textIndex = previousSegment.text.lastIndexOf('--');
const textSegment = splitTextSegment(
previousSegment,
paragraph,
textIndex,
textIndex + 2
);
formatTextSegmentBeforeSelectionMarker(
editor,
(previousSegment, paragraph, _marker, _markerIndex, context) => {
const segments = previousSegment.text.split(' ');
const dashes = segments[segments.length - 2];
if (dashes === '--') {
const textIndex = previousSegment.text.lastIndexOf('--');
const textSegment = splitTextSegment(
previousSegment,
paragraph,
textIndex,
textIndex + 2
);

textSegment.text = textSegment.text.replace('--', '—');
context.canUndoByBackspace = true;
return true;
} else {
const text = segments.pop();
const hasDashes = text && text?.indexOf('--') > -1;
if (hasDashes && text.trim() !== '--') {
const textIndex = previousSegment.text.indexOf(text);
const textSegment = splitTextSegment(
previousSegment,
paragraph,
textIndex,
textIndex + text.length - 1
);
textSegment.text = textSegment.text.replace('--', '—');
context.canUndoByBackspace = true;
return true;
} else {
const text = segments.pop();
const hasDashes = text && text?.indexOf('--') > -1;
if (hasDashes && text.trim() !== '--') {
const textIndex = previousSegment.text.indexOf(text);
const textSegment = splitTextSegment(
previousSegment,
paragraph,
textIndex,
textIndex + text.length - 1
);

const textLength = textSegment.text.length;
if (
textSegment.text[0] !== '-' &&
textSegment.text[textLength - 1] !== '-'
) {
textSegment.text = textSegment.text.replace('--', '—');
context.canUndoByBackspace = true;
return true;
}
}
}
const textLength = textSegment.text.length;
if (textSegment.text[0] !== '-' && textSegment.text[textLength - 1] !== '-') {
textSegment.text = textSegment.text.replace('--', '—');
context.canUndoByBackspace = true;
return true;
}
}
}
return false;
}

return false;
});
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { getSelectedSegmentsAndParagraphs } from 'roosterjs-content-model-dom';
import type {
ContentModelParagraph,
ContentModelSelectionMarker,
ContentModelText,
FormatContentModelContext,
IEditor,
} from 'roosterjs-content-model-types';

/**
* @internal
*/
export function formatTextSegmentBeforeSelectionMarker(
editor: IEditor,
callback: (
previousSegment: ContentModelText,
paragraph: ContentModelParagraph,
marker: ContentModelSelectionMarker,
markerIndex: number,
context: FormatContentModelContext
) => boolean
) {
editor.formatContentModel((model, context) => {
const selectedSegmentsAndParagraphs = getSelectedSegmentsAndParagraphs(
model,
false /*includeFormatHolder*/
);

if (selectedSegmentsAndParagraphs.length > 0 && selectedSegmentsAndParagraphs[0][1]) {
const marker = selectedSegmentsAndParagraphs[0][0];
const paragraph = selectedSegmentsAndParagraphs[0][1];
const markerIndex = paragraph.segments.indexOf(marker);
if (marker.segmentType === 'SelectionMarker' && markerIndex > 0) {
const previousSegment = paragraph.segments[markerIndex - 1];
if (previousSegment && previousSegment.segmentType === 'Text') {
return callback(previousSegment, paragraph, marker, markerIndex, context);
}
}
}
return false;
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
import { formatTextSegmentBeforeSelectionMarker } from '../../lib/pluginUtils/formatTextSegmentBeforeSelectionMarker';
import {
ContentModelDocument,
ContentModelParagraph,
ContentModelSelectionMarker,
ContentModelText,
FormatContentModelContext,
} from 'roosterjs-content-model-types';

describe('formatTextSegmentBeforeSelectionMarker', () => {
function runTest(
input: ContentModelDocument,
callback: (
previousSegment: ContentModelText,
paragraph: ContentModelParagraph,
marker: ContentModelSelectionMarker,
markerIndex: number,
context: FormatContentModelContext
) => boolean,
expectedModel: ContentModelDocument,
expectedResult: boolean
) {
const formatWithContentModelSpy = jasmine
.createSpy('formatWithContentModel')
.and.callFake((callback, options) => {
const result = callback(input, {
newEntities: [],
deletedEntities: [],
newImages: [],
canUndoByBackspace: true,
});
expect(result).toBe(expectedResult);
});

formatTextSegmentBeforeSelectionMarker(
{
focus: () => {},
formatContentModel: formatWithContentModelSpy,
} as any,
callback
);

expect(formatWithContentModelSpy).toHaveBeenCalled();
expect(input).toEqual(expectedModel);
}

it('no selection marker', () => {
const input: ContentModelDocument = {
blockGroupType: 'Document',
blocks: [
{
blockType: 'Paragraph',
segments: [
{
segmentType: 'Text',
text: 'test',
format: {},
},
],
format: {},
},
],
};
runTest(input, () => true, input, false);
});

it('no previous segment', () => {
const input: ContentModelDocument = {
blockGroupType: 'Document',
blocks: [
{
blockType: 'Paragraph',
segments: [
{
segmentType: 'SelectionMarker',
isSelected: true,
format: {},
},
],
format: {},
},
],
};
runTest(input, () => true, input, false);
});

it('previous segment is not text', () => {
const input: ContentModelDocument = {
blockGroupType: 'Document',
blocks: [
{
blockType: 'Paragraph',
segments: [
{
segmentType: 'Image',
src: 'test',
format: {},
dataset: {},
},
{
segmentType: 'SelectionMarker',
isSelected: true,
format: {},
},
],
format: {},
},
],
};
runTest(input, () => true, input, false);
});

it('format segment', () => {
const input: ContentModelDocument = {
blockGroupType: 'Document',
blocks: [
{
blockType: 'Paragraph',
segments: [
{
segmentType: 'Text',
text: 'first',
format: {},
},
{
segmentType: 'Text',
text: 'second',
format: {},
},
{
segmentType: 'SelectionMarker',
isSelected: true,
format: {},
},
],
format: {},
},
],
};
const expectedModel: ContentModelDocument = {
blockGroupType: 'Document',
blocks: [
{
blockType: 'Paragraph',
segments: [
{
segmentType: 'Text',
text: 'first',
format: {},
},
{
segmentType: 'Text',
text: 'second',
format: {
textColor: 'red',
},
},
{
segmentType: 'SelectionMarker',
isSelected: true,
format: {},
},
],
format: {},
},
],
};
runTest(
input,
previousSegment => {
previousSegment.format = { textColor: 'red' };
return true;
},
expectedModel,
true
);
});
});

0 comments on commit 80f1eca

Please sign in to comment.