1
1
import 'dart:convert' ;
2
2
3
+ import 'package:flutter/widgets.dart' ;
3
4
import 'package:markdown/markdown.dart' as md;
4
5
import 'package:super_editor/super_editor.dart' ;
5
6
@@ -411,7 +412,7 @@ class _InlineMarkdownToDocument implements md.NodeVisitor {
411
412
}
412
413
}
413
414
414
- extension on AttributedText {
415
+ extension Markdown on AttributedText {
415
416
String toMarkdown () {
416
417
final serializer = AttributedTextMarkdownSerializer ();
417
418
return serializer.serialize (this );
@@ -420,38 +421,29 @@ extension on AttributedText {
420
421
421
422
/// Serializes an [AttributedText] into markdown format
422
423
class AttributedTextMarkdownSerializer extends AttributionVisitor {
423
- late String _text ;
424
- int _spanStart = 0 ;
425
- final _buffer = StringBuffer () ;
424
+ late String _fullText ;
425
+ late StringBuffer _buffer ;
426
+ late int _bufferCursor ;
426
427
427
428
String serialize (AttributedText attributedText) {
428
- _text = attributedText.text;
429
+ _fullText = attributedText.text;
430
+ _buffer = StringBuffer ();
431
+ _bufferCursor = 0 ;
429
432
attributedText.visitAttributions (this );
430
433
return _buffer.toString ();
431
434
}
432
435
433
436
@override
434
- void visitAttributions (AttributedText fullText, int index, Set <Attribution > startingAttributions, Set <Attribution > endingAttributions) {
435
- // Add end markers.
436
- if (endingAttributions.isNotEmpty) {
437
- final markdownStyles = _sortAndSerializeAttributions (endingAttributions, AttributionVisitEvent .end);
438
- // Links are different from the plain styles since they are both not NamedAttributions (and therefore
439
- // can't be checked using equality comparison) and asymmetrical in markdown.
440
- final linkMarker = _encodeLinkMarker (endingAttributions, AttributionVisitEvent .end);
441
-
442
- // +1 on end index because this visitor has inclusive indices
443
- // whereas substring() expects an exclusive ending index.
444
- _buffer
445
- ..write (fullText.text.substring (_spanStart, index + 1 ))
446
- ..write (markdownStyles)
447
- ..write (linkMarker);
448
-
449
- // When we reach the end of an attribution we need to hold the start of the next span,
450
- // because if the last span has no attributions we will not visit any other index with
451
- // a start marker.
452
- // After we visit all the indexes we add the remaining text to the buffer.
453
- _spanStart = index + 1 ;
454
- }
437
+ void visitAttributions (
438
+ AttributedText fullText,
439
+ int index,
440
+ Set <Attribution > startingAttributions,
441
+ Set <Attribution > endingAttributions,
442
+ ) {
443
+ // Write out the text between the end of the last markers, and these new markers.
444
+ _buffer.write (
445
+ fullText.text.substring (_bufferCursor, index),
446
+ );
455
447
456
448
// Add start markers.
457
449
if (startingAttributions.isNotEmpty) {
@@ -461,19 +453,34 @@ class AttributedTextMarkdownSerializer extends AttributionVisitor {
461
453
final linkMarker = _encodeLinkMarker (startingAttributions, AttributionVisitEvent .start);
462
454
463
455
_buffer
464
- ..write (fullText.text.substring (_spanStart, index))
465
456
..write (linkMarker)
466
457
..write (markdownStyles);
458
+ }
459
+
460
+ // Write out the character at this index.
461
+ _buffer.write (_fullText[index]);
462
+ _bufferCursor = index + 1 ;
463
+
464
+ // Add end markers.
465
+ if (endingAttributions.isNotEmpty) {
466
+ final markdownStyles = _sortAndSerializeAttributions (endingAttributions, AttributionVisitEvent .end);
467
+ // Links are different from the plain styles since they are both not NamedAttributions (and therefore
468
+ // can't be checked using equality comparison) and asymmetrical in markdown.
469
+ final linkMarker = _encodeLinkMarker (endingAttributions, AttributionVisitEvent .end);
467
470
468
- _spanStart = index;
471
+ // +1 on end index because this visitor has inclusive indices
472
+ // whereas substring() expects an exclusive ending index.
473
+ _buffer
474
+ ..write (markdownStyles)
475
+ ..write (linkMarker);
469
476
}
470
477
}
471
478
472
479
@override
473
480
void onVisitEnd () {
474
481
// When the last span has no attributions, we still have text that wasn't added to the buffer yet.
475
- if (_spanStart <= _text .length - 1 ) {
476
- _buffer.write (_text .substring (_spanStart ));
482
+ if (_bufferCursor <= _fullText .length - 1 ) {
483
+ _buffer.write (_fullText .substring (_bufferCursor ));
477
484
}
478
485
}
479
486
0 commit comments