@@ -1023,6 +1023,284 @@ describe('executeCompiledPlan: text.rewrite style behavior', () => {
10231023 expect ( tr . doc . child ( 0 ) . textContent ) . toBe ( 'Alpha\n\nBeta' ) ;
10241024 } ) ;
10251025
1026+ it ( 'handles splitBefore with single core block on whole-textblock rewrite' , ( ) => {
1027+ const schema = new Schema ( {
1028+ nodes : {
1029+ doc : { content : 'block+' } ,
1030+ paragraph : { group : 'block' , content : 'text*' } ,
1031+ text : { group : 'inline' } ,
1032+ } ,
1033+ } ) ;
1034+
1035+ const doc = schema . nodes . doc . create ( { } , [
1036+ schema . nodes . paragraph . create ( { } , schema . text ( 'before' ) ) ,
1037+ schema . nodes . paragraph . create ( { } , schema . text ( 'hello world' ) ) ,
1038+ schema . nodes . paragraph . create ( { } , schema . text ( 'after' ) ) ,
1039+ ] ) ;
1040+
1041+ let absFrom = - 1 ;
1042+ doc . descendants ( ( node , pos ) => {
1043+ if ( node . isText && node . text === 'hello world' ) {
1044+ absFrom = pos ;
1045+ return false ;
1046+ }
1047+ return undefined ;
1048+ } ) ;
1049+
1050+ const state = EditorState . create ( { schema, doc } ) ;
1051+ const tr = state . tr ;
1052+ const editor = { state } as unknown as Editor ;
1053+
1054+ mockedDeps . resolveInlineStyle . mockReturnValue ( [ ] ) ;
1055+
1056+ const target = makeTarget ( {
1057+ absFrom,
1058+ absTo : absFrom + 'hello world' . length ,
1059+ from : 0 ,
1060+ to : 'hello world' . length ,
1061+ text : 'hello world' ,
1062+ capturedStyle : { runs : [ ] , isUniform : true } ,
1063+ } ) as any ;
1064+ const step : TextRewriteStep = {
1065+ id : 'step-split-before' ,
1066+ op : 'text.rewrite' ,
1067+ where : { by : 'select' , select : { type : 'text' , pattern : 'hello world' } , require : 'exactlyOne' } ,
1068+ args : { replacement : { text : '\nAlpha' } } ,
1069+ } ;
1070+
1071+ const outcome = executeTextRewrite ( editor , tr , target , step , { map : ( pos : number ) => pos } as any ) ;
1072+
1073+ expect ( outcome ) . toEqual ( { changed : true } ) ;
1074+ expect ( tr . doc . childCount ) . toBe ( 4 ) ;
1075+ expect ( tr . doc . child ( 0 ) . textContent ) . toBe ( 'before' ) ;
1076+ expect ( tr . doc . child ( 1 ) . textContent ) . toBe ( '' ) ;
1077+ expect ( tr . doc . child ( 2 ) . textContent ) . toBe ( 'Alpha' ) ;
1078+ expect ( tr . doc . child ( 3 ) . textContent ) . toBe ( 'after' ) ;
1079+ } ) ;
1080+
1081+ it ( 'handles splitAfter with single core block on whole-textblock rewrite' , ( ) => {
1082+ const schema = new Schema ( {
1083+ nodes : {
1084+ doc : { content : 'block+' } ,
1085+ paragraph : { group : 'block' , content : 'text*' } ,
1086+ text : { group : 'inline' } ,
1087+ } ,
1088+ } ) ;
1089+
1090+ const doc = schema . nodes . doc . create ( { } , [
1091+ schema . nodes . paragraph . create ( { } , schema . text ( 'before' ) ) ,
1092+ schema . nodes . paragraph . create ( { } , schema . text ( 'hello world' ) ) ,
1093+ schema . nodes . paragraph . create ( { } , schema . text ( 'after' ) ) ,
1094+ ] ) ;
1095+
1096+ let absFrom = - 1 ;
1097+ doc . descendants ( ( node , pos ) => {
1098+ if ( node . isText && node . text === 'hello world' ) {
1099+ absFrom = pos ;
1100+ return false ;
1101+ }
1102+ return undefined ;
1103+ } ) ;
1104+
1105+ const state = EditorState . create ( { schema, doc } ) ;
1106+ const tr = state . tr ;
1107+ const editor = { state } as unknown as Editor ;
1108+
1109+ mockedDeps . resolveInlineStyle . mockReturnValue ( [ ] ) ;
1110+
1111+ const target = makeTarget ( {
1112+ absFrom,
1113+ absTo : absFrom + 'hello world' . length ,
1114+ from : 0 ,
1115+ to : 'hello world' . length ,
1116+ text : 'hello world' ,
1117+ capturedStyle : { runs : [ ] , isUniform : true } ,
1118+ } ) as any ;
1119+ const step : TextRewriteStep = {
1120+ id : 'step-split-after' ,
1121+ op : 'text.rewrite' ,
1122+ where : { by : 'select' , select : { type : 'text' , pattern : 'hello world' } , require : 'exactlyOne' } ,
1123+ args : { replacement : { text : 'Alpha\n' } } ,
1124+ } ;
1125+
1126+ const outcome = executeTextRewrite ( editor , tr , target , step , { map : ( pos : number ) => pos } as any ) ;
1127+
1128+ expect ( outcome ) . toEqual ( { changed : true } ) ;
1129+ expect ( tr . doc . childCount ) . toBe ( 4 ) ;
1130+ expect ( tr . doc . child ( 0 ) . textContent ) . toBe ( 'before' ) ;
1131+ expect ( tr . doc . child ( 1 ) . textContent ) . toBe ( 'Alpha' ) ;
1132+ expect ( tr . doc . child ( 2 ) . textContent ) . toBe ( '' ) ;
1133+ expect ( tr . doc . child ( 3 ) . textContent ) . toBe ( 'after' ) ;
1134+ } ) ;
1135+
1136+ it ( 'handles both splitBefore and splitAfter with single core block on whole-textblock rewrite' , ( ) => {
1137+ const schema = new Schema ( {
1138+ nodes : {
1139+ doc : { content : 'block+' } ,
1140+ paragraph : { group : 'block' , content : 'text*' } ,
1141+ text : { group : 'inline' } ,
1142+ } ,
1143+ } ) ;
1144+
1145+ const doc = schema . nodes . doc . create ( { } , [
1146+ schema . nodes . paragraph . create ( { } , schema . text ( 'before' ) ) ,
1147+ schema . nodes . paragraph . create ( { } , schema . text ( 'hello world' ) ) ,
1148+ schema . nodes . paragraph . create ( { } , schema . text ( 'after' ) ) ,
1149+ ] ) ;
1150+
1151+ let absFrom = - 1 ;
1152+ doc . descendants ( ( node , pos ) => {
1153+ if ( node . isText && node . text === 'hello world' ) {
1154+ absFrom = pos ;
1155+ return false ;
1156+ }
1157+ return undefined ;
1158+ } ) ;
1159+
1160+ const state = EditorState . create ( { schema, doc } ) ;
1161+ const tr = state . tr ;
1162+ const editor = { state } as unknown as Editor ;
1163+
1164+ mockedDeps . resolveInlineStyle . mockReturnValue ( [ ] ) ;
1165+
1166+ const target = makeTarget ( {
1167+ absFrom,
1168+ absTo : absFrom + 'hello world' . length ,
1169+ from : 0 ,
1170+ to : 'hello world' . length ,
1171+ text : 'hello world' ,
1172+ capturedStyle : { runs : [ ] , isUniform : true } ,
1173+ } ) as any ;
1174+ const step : TextRewriteStep = {
1175+ id : 'step-split-both' ,
1176+ op : 'text.rewrite' ,
1177+ where : { by : 'select' , select : { type : 'text' , pattern : 'hello world' } , require : 'exactlyOne' } ,
1178+ args : { replacement : { text : '\nAlpha\n' } } ,
1179+ } ;
1180+
1181+ const outcome = executeTextRewrite ( editor , tr , target , step , { map : ( pos : number ) => pos } as any ) ;
1182+
1183+ expect ( outcome ) . toEqual ( { changed : true } ) ;
1184+ expect ( tr . doc . childCount ) . toBe ( 5 ) ;
1185+ expect ( tr . doc . child ( 0 ) . textContent ) . toBe ( 'before' ) ;
1186+ expect ( tr . doc . child ( 1 ) . textContent ) . toBe ( '' ) ;
1187+ expect ( tr . doc . child ( 2 ) . textContent ) . toBe ( 'Alpha' ) ;
1188+ expect ( tr . doc . child ( 3 ) . textContent ) . toBe ( '' ) ;
1189+ expect ( tr . doc . child ( 4 ) . textContent ) . toBe ( 'after' ) ;
1190+ } ) ;
1191+
1192+ it ( 'handles splitBefore with multi-block replacement on whole-textblock rewrite' , ( ) => {
1193+ const schema = new Schema ( {
1194+ nodes : {
1195+ doc : { content : 'block+' } ,
1196+ paragraph : { group : 'block' , content : 'text*' } ,
1197+ text : { group : 'inline' } ,
1198+ } ,
1199+ } ) ;
1200+
1201+ const doc = schema . nodes . doc . create ( { } , [
1202+ schema . nodes . paragraph . create ( { } , schema . text ( 'before' ) ) ,
1203+ schema . nodes . paragraph . create ( { } , schema . text ( 'hello world' ) ) ,
1204+ schema . nodes . paragraph . create ( { } , schema . text ( 'after' ) ) ,
1205+ ] ) ;
1206+
1207+ let absFrom = - 1 ;
1208+ doc . descendants ( ( node , pos ) => {
1209+ if ( node . isText && node . text === 'hello world' ) {
1210+ absFrom = pos ;
1211+ return false ;
1212+ }
1213+ return undefined ;
1214+ } ) ;
1215+
1216+ const state = EditorState . create ( { schema, doc } ) ;
1217+ const tr = state . tr ;
1218+ const editor = { state } as unknown as Editor ;
1219+
1220+ mockedDeps . resolveInlineStyle . mockReturnValue ( [ ] ) ;
1221+
1222+ const target = makeTarget ( {
1223+ absFrom,
1224+ absTo : absFrom + 'hello world' . length ,
1225+ from : 0 ,
1226+ to : 'hello world' . length ,
1227+ text : 'hello world' ,
1228+ capturedStyle : { runs : [ ] , isUniform : true } ,
1229+ } ) as any ;
1230+ const step : TextRewriteStep = {
1231+ id : 'step-split-before-multi' ,
1232+ op : 'text.rewrite' ,
1233+ where : { by : 'select' , select : { type : 'text' , pattern : 'hello world' } , require : 'exactlyOne' } ,
1234+ args : { replacement : { text : '\nAlpha\n\nBeta' } } ,
1235+ } ;
1236+
1237+ const outcome = executeTextRewrite ( editor , tr , target , step , { map : ( pos : number ) => pos } as any ) ;
1238+
1239+ expect ( outcome ) . toEqual ( { changed : true } ) ;
1240+ expect ( tr . doc . childCount ) . toBe ( 5 ) ;
1241+ expect ( tr . doc . child ( 0 ) . textContent ) . toBe ( 'before' ) ;
1242+ expect ( tr . doc . child ( 1 ) . textContent ) . toBe ( '' ) ;
1243+ expect ( tr . doc . child ( 2 ) . textContent ) . toBe ( 'Alpha' ) ;
1244+ expect ( tr . doc . child ( 3 ) . textContent ) . toBe ( 'Beta' ) ;
1245+ expect ( tr . doc . child ( 4 ) . textContent ) . toBe ( 'after' ) ;
1246+ } ) ;
1247+
1248+ it ( 'handles splitAfter with multi-block replacement on whole-textblock rewrite' , ( ) => {
1249+ const schema = new Schema ( {
1250+ nodes : {
1251+ doc : { content : 'block+' } ,
1252+ paragraph : { group : 'block' , content : 'text*' } ,
1253+ text : { group : 'inline' } ,
1254+ } ,
1255+ } ) ;
1256+
1257+ const doc = schema . nodes . doc . create ( { } , [
1258+ schema . nodes . paragraph . create ( { } , schema . text ( 'before' ) ) ,
1259+ schema . nodes . paragraph . create ( { } , schema . text ( 'hello world' ) ) ,
1260+ schema . nodes . paragraph . create ( { } , schema . text ( 'after' ) ) ,
1261+ ] ) ;
1262+
1263+ let absFrom = - 1 ;
1264+ doc . descendants ( ( node , pos ) => {
1265+ if ( node . isText && node . text === 'hello world' ) {
1266+ absFrom = pos ;
1267+ return false ;
1268+ }
1269+ return undefined ;
1270+ } ) ;
1271+
1272+ const state = EditorState . create ( { schema, doc } ) ;
1273+ const tr = state . tr ;
1274+ const editor = { state } as unknown as Editor ;
1275+
1276+ mockedDeps . resolveInlineStyle . mockReturnValue ( [ ] ) ;
1277+
1278+ const target = makeTarget ( {
1279+ absFrom,
1280+ absTo : absFrom + 'hello world' . length ,
1281+ from : 0 ,
1282+ to : 'hello world' . length ,
1283+ text : 'hello world' ,
1284+ capturedStyle : { runs : [ ] , isUniform : true } ,
1285+ } ) as any ;
1286+ const step : TextRewriteStep = {
1287+ id : 'step-split-after-multi' ,
1288+ op : 'text.rewrite' ,
1289+ where : { by : 'select' , select : { type : 'text' , pattern : 'hello world' } , require : 'exactlyOne' } ,
1290+ args : { replacement : { text : 'Alpha\n\nBeta\n' } } ,
1291+ } ;
1292+
1293+ const outcome = executeTextRewrite ( editor , tr , target , step , { map : ( pos : number ) => pos } as any ) ;
1294+
1295+ expect ( outcome ) . toEqual ( { changed : true } ) ;
1296+ expect ( tr . doc . childCount ) . toBe ( 5 ) ;
1297+ expect ( tr . doc . child ( 0 ) . textContent ) . toBe ( 'before' ) ;
1298+ expect ( tr . doc . child ( 1 ) . textContent ) . toBe ( 'Alpha' ) ;
1299+ expect ( tr . doc . child ( 2 ) . textContent ) . toBe ( 'Beta' ) ;
1300+ expect ( tr . doc . child ( 3 ) . textContent ) . toBe ( '' ) ;
1301+ expect ( tr . doc . child ( 4 ) . textContent ) . toBe ( 'after' ) ;
1302+ } ) ;
1303+
10261304 it ( 'keeps partial-range rewrites inline in a real transaction when replacement text stays within one block' , ( ) => {
10271305 const schema = new Schema ( {
10281306 nodes : {
0 commit comments