@@ -58,7 +58,9 @@ use crate::{column, container, rich_text, row, scrollable, span, text};
58
58
59
59
use std:: borrow:: BorrowMut ;
60
60
use std:: cell:: { Cell , RefCell } ;
61
+ use std:: collections:: { HashMap , HashSet } ;
61
62
use std:: ops:: Range ;
63
+ use std:: rc:: Rc ;
62
64
use std:: sync:: Arc ;
63
65
64
66
pub use core:: text:: Highlight ;
@@ -69,9 +71,16 @@ pub use url::Url;
69
71
#[ derive( Debug , Default ) ]
70
72
pub struct Content {
71
73
items : Vec < Item > ,
74
+ incomplete : HashMap < usize , Section > ,
72
75
state : State ,
73
76
}
74
77
78
+ #[ derive( Debug ) ]
79
+ struct Section {
80
+ content : String ,
81
+ broken_links : HashSet < String > ,
82
+ }
83
+
75
84
impl Content {
76
85
/// Creates a new empty [`Content`].
77
86
pub fn new ( ) -> Self {
@@ -80,10 +89,9 @@ impl Content {
80
89
81
90
/// Creates some new [`Content`] by parsing the given Markdown.
82
91
pub fn parse ( markdown : & str ) -> Self {
83
- let mut state = State :: default ( ) ;
84
- let items = parse_with ( & mut state, markdown) . collect ( ) ;
85
-
86
- Self { items, state }
92
+ let mut content = Self :: new ( ) ;
93
+ content. push_str ( markdown) ;
94
+ content
87
95
}
88
96
89
97
/// Pushes more Markdown into the [`Content`]; parsing incrementally!
@@ -103,8 +111,52 @@ impl Content {
103
111
let _ = self . items . pop ( ) ;
104
112
105
113
// Re-parse last item and new text
106
- let new_items = parse_with ( & mut self . state , & leftover) ;
107
- self . items . extend ( new_items) ;
114
+ for ( item, source, broken_links) in
115
+ parse_with ( & mut self . state , & leftover)
116
+ {
117
+ if !broken_links. is_empty ( ) {
118
+ let _ = self . incomplete . insert (
119
+ self . items . len ( ) ,
120
+ Section {
121
+ content : source. to_owned ( ) ,
122
+ broken_links,
123
+ } ,
124
+ ) ;
125
+ }
126
+
127
+ self . items . push ( item) ;
128
+ }
129
+
130
+ // Re-parse incomplete sections if new references are available
131
+ if !self . incomplete . is_empty ( ) {
132
+ let mut state = State {
133
+ leftover : String :: new ( ) ,
134
+ references : self . state . references . clone ( ) ,
135
+ highlighter : None ,
136
+ } ;
137
+
138
+ self . incomplete . retain ( |index, section| {
139
+ if self . items . len ( ) <= * index {
140
+ return false ;
141
+ }
142
+
143
+ let broken_links_before = section. broken_links . len ( ) ;
144
+
145
+ section
146
+ . broken_links
147
+ . retain ( |link| !self . state . references . contains_key ( link) ) ;
148
+
149
+ if broken_links_before != section. broken_links . len ( ) {
150
+ if let Some ( ( item, _source, _broken_links) ) =
151
+ parse_with ( & mut state, & section. content ) . next ( )
152
+ {
153
+ self . items [ * index] = item;
154
+ }
155
+ }
156
+
157
+ !section. broken_links . is_empty ( )
158
+ } ) ;
159
+ }
108
160
}
109
161
110
162
/// Returns the Markdown items, ready to be rendered.
@@ -285,11 +337,13 @@ impl Span {
285
337
/// ```
286
338
pub fn parse ( markdown : & str ) -> impl Iterator < Item = Item > + ' _ {
287
339
parse_with ( State :: default ( ) , markdown)
340
+ . map ( |( item, _source, _broken_links) | item)
288
341
}
289
342
290
343
#[ derive( Debug , Default ) ]
291
344
struct State {
292
345
leftover : String ,
346
+ references : HashMap < String , String > ,
293
347
#[ cfg( feature = "highlighter" ) ]
294
348
highlighter : Option < Highlighter > ,
295
349
}
@@ -379,12 +433,14 @@ impl Highlighter {
379
433
fn parse_with < ' a > (
380
434
mut state : impl BorrowMut < State > + ' a ,
381
435
markdown : & ' a str ,
382
- ) -> impl Iterator < Item = Item > + ' a {
436
+ ) -> impl Iterator < Item = ( Item , & ' a str , HashSet < String > ) > + ' a {
383
437
struct List {
384
438
start : Option < u64 > ,
385
439
items : Vec < Vec < Item > > ,
386
440
}
387
441
442
+ let broken_links = Rc :: new ( RefCell :: new ( HashSet :: new ( ) ) ) ;
443
+
388
444
let mut spans = Vec :: new ( ) ;
389
445
let mut code = Vec :: new ( ) ;
390
446
let mut strong = false ;
@@ -398,14 +454,40 @@ fn parse_with<'a>(
398
454
#[ cfg( feature = "highlighter" ) ]
399
455
let mut highlighter = None ;
400
456
401
- let parser = pulldown_cmark:: Parser :: new_ext (
457
+ let parser = pulldown_cmark:: Parser :: new_with_broken_link_callback (
402
458
markdown,
403
459
pulldown_cmark:: Options :: ENABLE_YAML_STYLE_METADATA_BLOCKS
404
460
| pulldown_cmark:: Options :: ENABLE_PLUSES_DELIMITED_METADATA_BLOCKS
405
461
| pulldown_cmark:: Options :: ENABLE_TABLES
406
462
| pulldown_cmark:: Options :: ENABLE_STRIKETHROUGH ,
407
- )
408
- . into_offset_iter ( ) ;
463
+ {
464
+ let references = state. borrow ( ) . references . clone ( ) ;
465
+ let broken_links = broken_links. clone ( ) ;
466
+
467
+ Some ( move |broken_link : pulldown_cmark:: BrokenLink < ' _ > | {
468
+ if let Some ( reference) =
469
+ references. get ( broken_link. reference . as_ref ( ) )
470
+ {
471
+ Some ( (
472
+ pulldown_cmark:: CowStr :: from ( reference. to_owned ( ) ) ,
473
+ broken_link. reference . into_static ( ) ,
474
+ ) )
475
+ } else {
476
+ let _ = RefCell :: borrow_mut ( & broken_links)
477
+ . insert ( broken_link. reference . to_string ( ) ) ;
478
+
479
+ None
480
+ }
481
+ } )
482
+ } ,
483
+ ) ;
484
+
485
+ let references = & mut state. borrow_mut ( ) . references ;
486
+
487
+ for reference in parser. reference_definitions ( ) . iter ( ) {
488
+ let _ = references
489
+ . insert ( reference. 0 . to_owned ( ) , reference. 1 . dest . to_string ( ) ) ;
490
+ }
409
491
410
492
let produce = move |state : & mut State ,
411
493
lists : & mut Vec < List > ,
@@ -414,7 +496,11 @@ fn parse_with<'a>(
414
496
if lists. is_empty ( ) {
415
497
state. leftover = markdown[ source. start ..] . to_owned ( ) ;
416
498
417
- Some ( item)
499
+ Some ( (
500
+ item,
501
+ & markdown[ source. start ..source. end ] ,
502
+ broken_links. take ( ) ,
503
+ ) )
418
504
} else {
419
505
lists
420
506
. last_mut ( )
@@ -428,6 +514,8 @@ fn parse_with<'a>(
428
514
}
429
515
} ;
430
516
517
+ let parser = parser. into_offset_iter ( ) ;
518
+
431
519
// We want to keep the `spans` capacity
432
520
#[ allow( clippy:: drain_collect) ]
433
521
parser. filter_map ( move |( event, source) | match event {
0 commit comments