All files / roosterjs-content-model-api/lib/modelApi/selection adjustTrailingSpaceSelection.ts

100% Statements 23/23
90% Branches 27/30
100% Functions 7/7
100% Lines 23/23

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 811x                     1x 13x 14x 14x         4x     10x     10x         2x         14x         11x       11x       6x             6x 6x 6x 6x 6x 6x                 6x           6x 6x 6x      
import { createText, iterateSelections, mutateSegment } from 'roosterjs-content-model-dom';
import type {
    ReadonlyContentModelDocument,
    ReadonlyContentModelParagraph,
    ReadonlyContentModelText,
} from 'roosterjs-content-model-types';
 
/**
 * If a format cannot be applied to be applied to a trailing space, split the trailing space into a separate segment
 * @internal
 */
export function adjustTrailingSpaceSelection(model: ReadonlyContentModelDocument) {
    iterateSelections(model, (_, __, block, segments) => {
        Eif (block?.blockType === 'Paragraph' && segments && segments.length > 0) {
            if (
                segments.length === 1 &&
                segments[0].segmentType === 'Text' &&
                shouldSplitTrailingSpace(segments[0])
            ) {
                splitTextSegment(block, segments[0]);
            } else {
                const lastTextSegment =
                    segments[segments.length - 1].segmentType === 'SelectionMarker'
                        ? segments[segments.length - 2]
                        : segments[segments.length - 1];
                if (
                    lastTextSegment &&
                    lastTextSegment.segmentType === 'Text' &&
                    shouldSplitTrailingSpace(lastTextSegment)
                ) {
                    splitTextSegment(block, lastTextSegment);
                }
            }
        }
 
        return false;
    });
}
 
function shouldSplitTrailingSpace(segment: ReadonlyContentModelText) {
    return segment.isSelected && hasTrailingSpace(segment.text) && !isTrailingSpace(segment.text);
}
 
function hasTrailingSpace(text: string) {
    return text.trimRight() !== text;
}
 
function isTrailingSpace(text: string) {
    return text.trimRight().length == 0;
}
 
function splitTextSegment(
    readonlyBlock: ReadonlyContentModelParagraph,
    readonlyTextSegment: ReadonlyContentModelText
) {
    mutateSegment(readonlyBlock, readonlyTextSegment, (textSegment, block) => {
        const text = textSegment.text.trimRight();
        const trailingSpace = textSegment.text.substring(text.length);
        const newText = createText(text, textSegment.format, textSegment.link, textSegment.code);
        newText.isSelected = true;
        const trailingSpaceLink = textSegment.link
            ? {
                  ...textSegment.link,
                  format: {
                      ...textSegment.link?.format,
                      underline: false, // Remove underline for trailing space link
                  },
              }
            : undefined;
        const trailingSpaceSegment = createText(
            trailingSpace,
            undefined,
            trailingSpaceLink,
            textSegment.code
        );
        trailingSpaceSegment.isSelected = true;
        const index = block.segments.indexOf(textSegment);
        block.segments.splice(index, 1, newText, trailingSpaceSegment);
    });
}