fix: replace the selected text with ai response in the multiple lines

This commit is contained in:
Lucas.Xu 2025-04-08 15:54:41 +08:00
parent 18da4f5d2f
commit d471d54252
2 changed files with 303 additions and 1 deletions

View file

@ -358,10 +358,67 @@ class MarkdownTextRobot {
return;
}
final markdownNodes = customMarkdownToDocument(
markdownText,
tableWidth: 250.0,
).root.children;
// Get the selected nodes.
final nodes = editorState.getNodesInSelection(selection);
// Replace the delta of the selected nodes.
// step 1. merge the first selected node and the first node from the ai response
// step 2. merge the last selected node and the last node from the ai response
// step 3. insert the middle nodes from the ai response
// step 4. delete the middle nodes
final transaction = editorState.transaction;
// step 1
final firstNode = nodes.firstOrNull;
final delta = firstNode?.delta;
final firstMarkdownNode = markdownNodes.firstOrNull;
final firstMarkdownDelta = firstMarkdownNode?.delta;
if (firstNode != null &&
delta != null &&
firstMarkdownNode != null &&
firstMarkdownDelta != null) {
final startIndex = selection.startIndex;
final length = delta.length - startIndex;
transaction
..deleteText(firstNode, startIndex, length)
..insertTextDelta(firstNode, startIndex, firstMarkdownDelta);
}
// step 2
final lastNode = nodes.lastOrNull;
final lastDelta = lastNode?.delta;
final lastMarkdownNode = markdownNodes.lastOrNull;
final lastMarkdownDelta = lastMarkdownNode?.delta;
if (lastNode != null &&
lastDelta != null &&
lastMarkdownNode != null &&
lastMarkdownDelta != null) {
final endIndex = selection.endIndex;
transaction
..deleteText(lastNode, 0, endIndex)
..insertTextDelta(lastNode, 0, lastMarkdownDelta);
}
// step 3
final insertedPath = selection.start.path.nextNPath(1);
if (markdownNodes.length > 2) {
transaction.insertNodes(
insertedPath,
markdownNodes.skip(1).take(markdownNodes.length - 2).toList(),
);
}
// step 4
if (nodes.length > 2) {
final middleNodes = nodes.skip(1).take(nodes.length - 2).toList();
transaction.deleteNodes(middleNodes);
}
await editorState.apply(transaction);
}
}

View file

@ -488,6 +488,251 @@ void main() {
expect(d7.attributes, null);
});
});
group('markdown text robot - replace in multiple lines:', () {
final text1 =
'''The introduction of the World Wide Web in the early 1990s marked a turning point. ''';
final text2 =
'''Tim Berners-Lee's invention made the internet accessible to non-technical users, opening the floodgates for mass adoption. ''';
final text3 =
'''Email became widespread, and instant messaging services like ICQ and AOL Instant Messenger gained popularity, allowing for real-time text communication.''';
Document buildTestDocument() {
return Document(
root: pageNode(
children: [
paragraphNode(delta: Delta()..insert(text1)),
paragraphNode(delta: Delta()..insert(text2)),
paragraphNode(delta: Delta()..insert(text3)),
],
),
);
}
// 1. create a document with 3 paragraph nodes
// 2. use the text robot to replace the selected content in the multiple lines
// 3. check the document
test(
'the selection starts with the first paragraph and ends with the middle of second paragraph',
() async {
final document = buildTestDocument();
final editorState = EditorState(document: document);
editorState.selection = Selection(
start: Position(
path: [0],
),
end: Position(
path: [1],
offset: text2.length -
', opening the floodgates for mass adoption. '.length,
),
);
final markdownText =
'''The **introduction** of the World Wide Web in the *early 1990s* marked a significant turning point.
Tim Berners-Lee's **revolutionary invention** made the internet accessible to non-technical users''';
final markdownTextRobot = MarkdownTextRobot(
editorState: editorState,
);
await markdownTextRobot.replace(
selection: editorState.selection!,
markdownText: markdownText,
);
final afterNodes = editorState.document.root.children;
expect(afterNodes.length, 3);
{
// first paragraph
final delta1 = afterNodes[0].delta!.toList();
expect(delta1.length, 5);
final d1 = delta1[0] as TextInsert;
expect(d1.text, 'The ');
expect(d1.attributes, null);
final d2 = delta1[1] as TextInsert;
expect(d2.text, 'introduction');
expect(d2.attributes, {AppFlowyRichTextKeys.bold: true});
final d3 = delta1[2] as TextInsert;
expect(d3.text, ' of the World Wide Web in the ');
expect(d3.attributes, null);
final d4 = delta1[3] as TextInsert;
expect(d4.text, 'early 1990s');
expect(d4.attributes, {AppFlowyRichTextKeys.italic: true});
final d5 = delta1[4] as TextInsert;
expect(d5.text, ' marked a significant turning point.');
expect(d5.attributes, null);
}
{
// second paragraph
final delta2 = afterNodes[1].delta!.toList();
expect(delta2.length, 3);
final d1 = delta2[0] as TextInsert;
expect(d1.text, "Tim Berners-Lee's ");
expect(d1.attributes, null);
final d2 = delta2[1] as TextInsert;
expect(d2.text, "revolutionary invention");
expect(d2.attributes, {AppFlowyRichTextKeys.bold: true});
final d3 = delta2[2] as TextInsert;
expect(
d3.text,
" made the internet accessible to non-technical users, opening the floodgates for mass adoption. ",
);
expect(d3.attributes, null);
}
{
// third paragraph
final delta3 = afterNodes[2].delta!.toList();
expect(delta3.length, 1);
final d1 = delta3[0] as TextInsert;
expect(d1.text, text3);
expect(d1.attributes, null);
}
});
test(
'the selection starts with the middle of the first paragraph and ends with the middle of last paragraph',
() async {
final document = buildTestDocument();
final editorState = EditorState(document: document);
editorState.selection = Selection(
start: Position(
path: [0],
offset: 'The introduction of the World Wide Web'.length,
),
end: Position(
path: [2],
offset:
'Email became widespread, and instant messaging services like ICQ and AOL Instant Messenger gained popularity'
.length,
),
);
final markdownText =
''' in the **early 1990s** marked a *significant turning point* in technological history.
Tim Berners-Lee's **revolutionary invention** made the internet accessible to non-technical users, opening the floodgates for *unprecedented mass adoption*.
Email became **widely prevalent**, and instant messaging services like *ICQ* and *AOL Instant Messenger* gained tremendous popularity
''';
final markdownTextRobot = MarkdownTextRobot(
editorState: editorState,
);
await markdownTextRobot.replace(
selection: editorState.selection!,
markdownText: markdownText,
);
final afterNodes = editorState.document.root.children;
expect(afterNodes.length, 3);
{
// first paragraph
final delta1 = afterNodes[0].delta!.toList();
expect(delta1.length, 5);
final d1 = delta1[0] as TextInsert;
expect(d1.text, 'The introduction of the World Wide Web in the ');
expect(d1.attributes, null);
final d2 = delta1[1] as TextInsert;
expect(d2.text, 'early 1990s');
expect(d2.attributes, {AppFlowyRichTextKeys.bold: true});
final d3 = delta1[2] as TextInsert;
expect(d3.text, ' marked a ');
expect(d3.attributes, null);
final d4 = delta1[3] as TextInsert;
expect(d4.text, 'significant turning point');
expect(d4.attributes, {AppFlowyRichTextKeys.italic: true});
final d5 = delta1[4] as TextInsert;
expect(d5.text, ' in technological history.');
expect(d5.attributes, null);
}
{
// second paragraph
final delta2 = afterNodes[1].delta!.toList();
expect(delta2.length, 5);
final d1 = delta2[0] as TextInsert;
expect(d1.text, "Tim Berners-Lee's ");
expect(d1.attributes, null);
final d2 = delta2[1] as TextInsert;
expect(d2.text, "revolutionary invention");
expect(d2.attributes, {AppFlowyRichTextKeys.bold: true});
final d3 = delta2[2] as TextInsert;
expect(
d3.text,
" made the internet accessible to non-technical users, opening the floodgates for ",
);
expect(d3.attributes, null);
final d4 = delta2[3] as TextInsert;
expect(d4.text, "unprecedented mass adoption");
expect(d4.attributes, {AppFlowyRichTextKeys.italic: true});
final d5 = delta2[4] as TextInsert;
expect(d5.text, ".");
expect(d5.attributes, null);
}
{
// third paragraph
// third paragraph
final delta3 = afterNodes[2].delta!.toList();
expect(delta3.length, 7);
final d1 = delta3[0] as TextInsert;
expect(d1.text, "Email became ");
expect(d1.attributes, null);
final d2 = delta3[1] as TextInsert;
expect(d2.text, "widely prevalent");
expect(d2.attributes, {AppFlowyRichTextKeys.bold: true});
final d3 = delta3[2] as TextInsert;
expect(d3.text, ", and instant messaging services like ");
expect(d3.attributes, null);
final d4 = delta3[3] as TextInsert;
expect(d4.text, "ICQ");
expect(d4.attributes, {AppFlowyRichTextKeys.italic: true});
final d5 = delta3[4] as TextInsert;
expect(d5.text, " and ");
expect(d5.attributes, null);
final d6 = delta3[5] as TextInsert;
expect(d6.text, "AOL Instant Messenger");
expect(d6.attributes, {AppFlowyRichTextKeys.italic: true});
final d7 = delta3[6] as TextInsert;
expect(
d7.text,
" gained tremendous popularity, allowing for real-time text communication.",
);
expect(d7.attributes, null);
}
});
});
}
const _sample1 = '''# The Curious Cat