@@ -3,7 +3,7 @@ import {addNodeToTree, createRootTreeNode, updateTreeElementRefs} from './treeUt
33import type { NodeType , TreeNode } from './treeUtils' ;
44import type { PartialMarkdownStyle } from '../../styleUtils' ;
55import { getCurrentCursorPosition , moveCursorToEnd , setCursorPosition } from './cursorUtils' ;
6- import { addStyleToBlock , extendBlockStructure , getFirstBlockMarkdownRange , isBlockMarkdownType } from './blockUtils' ;
6+ import { addStyleToBlock , extendBlockStructure , getFirstBlockMarkdownRange , isBlockMarkdownType , isMultilineMarkdownType } from './blockUtils' ;
77import type { InlineImagesInputProps , MarkdownRange } from '../../commonTypes' ;
88import { getAnimationCurrentTimes , updateAnimationsTime } from './animationUtils' ;
99import { sortRanges , ungroupRanges } from '../../rangeUtils' ;
@@ -31,9 +31,62 @@ function splitTextIntoLines(text: string): Paragraph[] {
3131 return lines ;
3232}
3333
34- /** Merges lines that contain multiline markdown tags into one line */
35- function mergeLinesWithMultilineTags ( lines : Paragraph [ ] , ranges : MarkdownRange [ ] ) {
36- let mergedLines = [ ...lines ] ;
34+ /**
35+ * Merges lines with multiline markdown tags (like `pre`) into a single line.
36+ * The main line will contain the text and all markdown ranges from the other lines.
37+ */
38+ function mergeLinesWithMultilineTags ( lines : Paragraph [ ] , currentLine : Paragraph , range : MarkdownRange , correspondingLineIndexes : number [ ] ) {
39+ const mainLine = currentLine ;
40+ currentLine . markdownRanges . push ( range ) ;
41+
42+ correspondingLineIndexes . forEach ( ( lineIndex ) => {
43+ const otherLine = lines [ lineIndex ] as Paragraph ;
44+ mainLine . text += `\n${ otherLine . text } ` ;
45+ mainLine . length += otherLine . length + 1 ;
46+ mainLine . markdownRanges . push ( ...otherLine . markdownRanges ) ;
47+ } ) ;
48+
49+ if ( correspondingLineIndexes . length > 0 && correspondingLineIndexes [ 0 ] !== undefined ) {
50+ lines . splice ( correspondingLineIndexes [ 0 ] , correspondingLineIndexes . length ) ;
51+ }
52+ }
53+
54+ /**
55+ * Splits a markdown range that spans multiple lines into separate lines.
56+ */
57+ function splitRangeIntoSeparateLines ( lines : Paragraph [ ] , currentLine : Paragraph , range : MarkdownRange , correspondingLineIndexes : number [ ] ) {
58+ const mainLineRangeLength = currentLine . start + currentLine . length - range . start ;
59+ currentLine . markdownRanges . push ( {
60+ ...range ,
61+ length : mainLineRangeLength ,
62+ } ) ;
63+
64+ let rangeLength = range . length - mainLineRangeLength ;
65+ correspondingLineIndexes . forEach ( ( lineIndex ) => {
66+ const otherLine = lines [ lineIndex ] as Paragraph ;
67+ let currentLength = otherLine . length ;
68+ if ( rangeLength <= currentLength ) {
69+ currentLength = rangeLength - 1 ;
70+ }
71+
72+ if ( currentLength > 0 ) {
73+ lines [ lineIndex ] ?. markdownRanges . push ( {
74+ ...range ,
75+ start : otherLine . start ,
76+ length : currentLength ,
77+ } ) ;
78+ }
79+
80+ rangeLength -= currentLength ;
81+ } ) ;
82+ }
83+
84+ /**
85+ * For singleline markdown types, the function splits markdown ranges that spread beyond the line length into separate lines.
86+ * For multiline markdown types (like `pre`), it merges them and corresponding text into one line.
87+ */
88+ function normalizeLines ( lines : Paragraph [ ] , ranges : MarkdownRange [ ] ) {
89+ const mergedLines = [ ...lines ] ;
3790 const lineIndexes = mergedLines . map ( ( _line , index ) => index ) ;
3891
3992 ranges . forEach ( ( range ) => {
@@ -44,19 +97,14 @@ function mergeLinesWithMultilineTags(lines: Paragraph[], ranges: MarkdownRange[]
4497 if ( correspondingLineIndexes . length > 0 ) {
4598 const mainLineIndex = correspondingLineIndexes [ 0 ] as number ;
4699 const mainLine = mergedLines [ mainLineIndex ] as Paragraph ;
47-
48- mainLine . markdownRanges . push ( range ) ;
49-
50100 const otherLineIndexes = correspondingLineIndexes . slice ( 1 ) ;
51- otherLineIndexes . forEach ( ( lineIndex ) => {
52- const otherLine = mergedLines [ lineIndex ] as Paragraph ;
53101
54- mainLine . text += `\n ${ otherLine . text } ` ;
55- mainLine . length += otherLine . length + 1 ;
56- mainLine . markdownRanges . push ( ... otherLine . markdownRanges ) ;
57- } ) ;
58- if ( otherLineIndexes . length > 0 ) {
59- mergedLines = mergedLines . filter ( ( _line , index ) => ! otherLineIndexes . includes ( index ) ) ;
102+ if ( isMultilineMarkdownType ( range . type ) ) {
103+ mergeLinesWithMultilineTags ( mergedLines , mainLine , range , otherLineIndexes ) ;
104+ } else if ( otherLineIndexes . length > 0 ) {
105+ splitRangeIntoSeparateLines ( mergedLines , mainLine , range , otherLineIndexes ) ;
106+ } else {
107+ mainLine . markdownRanges . push ( range ) ;
60108 }
61109 }
62110 } ) ;
@@ -163,7 +211,7 @@ function parseRangesToHTMLNodes(
163211 // Sort all ranges by start position, length, and by tag hierarchy so the styles and text are applied in correct order
164212 const sortedRanges = sortRanges ( ranges ) ;
165213 const markdownRanges = ungroupRanges ( sortedRanges ) ;
166- lines = mergeLinesWithMultilineTags ( lines , markdownRanges ) ;
214+ lines = normalizeLines ( lines , markdownRanges ) ;
167215
168216 let lastRangeEndIndex = 0 ;
169217 while ( lines . length > 0 ) {
@@ -316,4 +364,5 @@ function updateInputStructure(
316364 return { text, cursorPosition : cursorPosition || 0 } ;
317365}
318366
319- export { updateInputStructure , parseRangesToHTMLNodes } ;
367+ export { updateInputStructure , parseRangesToHTMLNodes , normalizeLines } ;
368+ export type { Paragraph } ;
0 commit comments