@@ -1237,7 +1237,27 @@ pub(crate) fn plain_text_summary(md: &str, link_names: &[RenderedLink]) -> Strin
1237
1237
pub ( crate ) struct MarkdownLink {
1238
1238
pub kind : LinkType ,
1239
1239
pub link : String ,
1240
- pub range : Range < usize > ,
1240
+ pub range : MarkdownLinkRange ,
1241
+ }
1242
+
1243
+ #[ derive( Clone , Debug ) ]
1244
+ pub ( crate ) enum MarkdownLinkRange {
1245
+ /// Normally, markdown link warnings point only at the destination.
1246
+ Destination ( Range < usize > ) ,
1247
+ /// In some cases, it's not possible to point at the destination.
1248
+ /// Usually, this happens because backslashes `\\` are used.
1249
+ /// When that happens, point at the whole link, and don't provide structured suggestions.
1250
+ WholeLink ( Range < usize > ) ,
1251
+ }
1252
+
1253
+ impl MarkdownLinkRange {
1254
+ /// Extracts the inner range.
1255
+ pub fn inner_range ( & self ) -> & Range < usize > {
1256
+ match self {
1257
+ MarkdownLinkRange :: Destination ( range) => range,
1258
+ MarkdownLinkRange :: WholeLink ( range) => range,
1259
+ }
1260
+ }
1241
1261
}
1242
1262
1243
1263
pub ( crate ) fn markdown_links < R > (
@@ -1257,16 +1277,17 @@ pub(crate) fn markdown_links<R>(
1257
1277
if md_start <= s_start && s_end <= md_end {
1258
1278
let start = s_start. offset_from ( md_start) as usize ;
1259
1279
let end = s_end. offset_from ( md_start) as usize ;
1260
- start..end
1280
+ MarkdownLinkRange :: Destination ( start..end)
1261
1281
} else {
1262
- fallback
1282
+ MarkdownLinkRange :: WholeLink ( fallback)
1263
1283
}
1264
1284
} ;
1265
1285
1266
1286
let span_for_link = |link : & CowStr < ' _ > , span : Range < usize > | {
1267
1287
// For diagnostics, we want to underline the link's definition but `span` will point at
1268
1288
// where the link is used. This is a problem for reference-style links, where the definition
1269
1289
// is separate from the usage.
1290
+
1270
1291
match link {
1271
1292
// `Borrowed` variant means the string (the link's destination) may come directly from
1272
1293
// the markdown text and we can locate the original link destination.
@@ -1275,8 +1296,80 @@ pub(crate) fn markdown_links<R>(
1275
1296
CowStr :: Borrowed ( s) => locate ( s, span) ,
1276
1297
1277
1298
// For anything else, we can only use the provided range.
1278
- CowStr :: Boxed ( _) | CowStr :: Inlined ( _) => span,
1299
+ CowStr :: Boxed ( _) | CowStr :: Inlined ( _) => MarkdownLinkRange :: WholeLink ( span) ,
1300
+ }
1301
+ } ;
1302
+
1303
+ let span_for_offset_backward = |span : Range < usize > , open : u8 , close : u8 | {
1304
+ let mut open_brace = !0 ;
1305
+ let mut close_brace = !0 ;
1306
+ for ( i, b) in md. as_bytes ( ) [ span. clone ( ) ] . iter ( ) . copied ( ) . enumerate ( ) . rev ( ) {
1307
+ let i = i + span. start ;
1308
+ if b == close {
1309
+ close_brace = i;
1310
+ break ;
1311
+ }
1312
+ }
1313
+ if close_brace < span. start || close_brace >= span. end {
1314
+ return MarkdownLinkRange :: WholeLink ( span) ;
1315
+ }
1316
+ let mut nesting = 1 ;
1317
+ for ( i, b) in md. as_bytes ( ) [ span. start ..close_brace] . iter ( ) . copied ( ) . enumerate ( ) . rev ( ) {
1318
+ let i = i + span. start ;
1319
+ if b == close {
1320
+ nesting += 1 ;
1321
+ }
1322
+ if b == open {
1323
+ nesting -= 1 ;
1324
+ }
1325
+ if nesting == 0 {
1326
+ open_brace = i;
1327
+ break ;
1328
+ }
1329
+ }
1330
+ assert ! ( open_brace != close_brace) ;
1331
+ if open_brace < span. start || open_brace >= span. end {
1332
+ return MarkdownLinkRange :: WholeLink ( span) ;
1333
+ }
1334
+ // do not actually include braces in the span
1335
+ let range = ( open_brace + 1 ) ..close_brace;
1336
+ MarkdownLinkRange :: Destination ( range. clone ( ) )
1337
+ } ;
1338
+
1339
+ let span_for_offset_forward = |span : Range < usize > , open : u8 , close : u8 | {
1340
+ let mut open_brace = !0 ;
1341
+ let mut close_brace = !0 ;
1342
+ for ( i, b) in md. as_bytes ( ) [ span. clone ( ) ] . iter ( ) . copied ( ) . enumerate ( ) {
1343
+ let i = i + span. start ;
1344
+ if b == open {
1345
+ open_brace = i;
1346
+ break ;
1347
+ }
1348
+ }
1349
+ if open_brace < span. start || open_brace >= span. end {
1350
+ return MarkdownLinkRange :: WholeLink ( span) ;
1279
1351
}
1352
+ let mut nesting = 0 ;
1353
+ for ( i, b) in md. as_bytes ( ) [ open_brace..span. end ] . iter ( ) . copied ( ) . enumerate ( ) {
1354
+ let i = i + open_brace;
1355
+ if b == close {
1356
+ nesting -= 1 ;
1357
+ }
1358
+ if b == open {
1359
+ nesting += 1 ;
1360
+ }
1361
+ if nesting == 0 {
1362
+ close_brace = i;
1363
+ break ;
1364
+ }
1365
+ }
1366
+ assert ! ( open_brace != close_brace) ;
1367
+ if open_brace < span. start || open_brace >= span. end {
1368
+ return MarkdownLinkRange :: WholeLink ( span) ;
1369
+ }
1370
+ // do not actually include braces in the span
1371
+ let range = ( open_brace + 1 ) ..close_brace;
1372
+ MarkdownLinkRange :: Destination ( range. clone ( ) )
1280
1373
} ;
1281
1374
1282
1375
Parser :: new_with_broken_link_callback (
@@ -1287,11 +1380,20 @@ pub(crate) fn markdown_links<R>(
1287
1380
. into_offset_iter ( )
1288
1381
. filter_map ( |( event, span) | match event {
1289
1382
Event :: Start ( Tag :: Link ( link_type, dest, _) ) if may_be_doc_link ( link_type) => {
1290
- preprocess_link ( MarkdownLink {
1291
- kind : link_type,
1292
- range : span_for_link ( & dest, span) ,
1293
- link : dest. into_string ( ) ,
1294
- } )
1383
+ let range = match link_type {
1384
+ // Link is pulled from the link itself.
1385
+ LinkType :: ReferenceUnknown | LinkType :: ShortcutUnknown => {
1386
+ span_for_offset_backward ( span, b'[' , b']' )
1387
+ }
1388
+ LinkType :: CollapsedUnknown => span_for_offset_forward ( span, b'[' , b']' ) ,
1389
+ LinkType :: Inline => span_for_offset_backward ( span, b'(' , b')' ) ,
1390
+ // Link is pulled from elsewhere in the document.
1391
+ LinkType :: Reference | LinkType :: Collapsed | LinkType :: Shortcut => {
1392
+ span_for_link ( & dest, span)
1393
+ }
1394
+ LinkType :: Autolink | LinkType :: Email => unreachable ! ( ) ,
1395
+ } ;
1396
+ preprocess_link ( MarkdownLink { kind : link_type, range, link : dest. into_string ( ) } )
1295
1397
}
1296
1398
_ => None ,
1297
1399
} )
0 commit comments