@@ -60,18 +60,20 @@ fn next_bytes(
6060 return None ;
6161 }
6262
63- let style = capture. ready . unwrap_or ( capture. style ) ;
63+ let ( style, url ) = capture. ready . clone ( ) . unwrap_or ( ( capture. style , None ) ) ;
6464 Some ( Element {
6565 text : std:: mem:: take ( & mut capture. printable ) ,
6666 style,
67+ url,
6768 } )
6869}
6970
7071#[ derive( Default , Clone , Debug , PartialEq , Eq ) ]
7172struct AnsiCapture {
7273 style : anstyle:: Style ,
7374 printable : String ,
74- ready : Option < anstyle:: Style > ,
75+ hyperlink : Option < String > ,
76+ ready : Option < ( anstyle:: Style , Option < String > ) > ,
7577}
7678
7779impl AnsiCapture {
@@ -265,16 +267,52 @@ impl anstyle_parse::Perform for AnsiCapture {
265267 }
266268
267269 if style != self . style && !self . printable . is_empty ( ) {
268- self . ready = Some ( self . style ) ;
270+ self . ready = Some ( ( self . style , self . hyperlink . clone ( ) ) ) ;
269271 }
270272 self . style = style;
271273 }
274+
275+ fn osc_dispatch ( & mut self , params : & [ & [ u8 ] ] , _bell_terminated : bool ) {
276+ let mut state = OscState :: Normal ;
277+ for value in params {
278+ match ( state, value) {
279+ ( OscState :: Normal , & [ b'8' ] ) => {
280+ state = OscState :: HyperlinkParams ;
281+ }
282+ ( OscState :: HyperlinkParams , _) => {
283+ state = OscState :: HyperlinkUri ;
284+ }
285+ ( OscState :: HyperlinkUri , & [ ] ) => {
286+ if self . hyperlink . is_some ( ) {
287+ self . ready = Some ( ( self . style , std:: mem:: take ( & mut self . hyperlink ) ) ) ;
288+ }
289+ break ;
290+ }
291+ ( OscState :: HyperlinkUri , uri) => {
292+ let hyperlink = uri. iter ( ) . map ( |b| * b as char ) . collect :: < String > ( ) ;
293+ self . hyperlink = Some ( hyperlink) ;
294+
295+ // Any current text in `self.printable` needs to be
296+ // rendered, so it doesn't get confused with Hyperlink text
297+ if !self . printable . is_empty ( ) {
298+ self . ready = Some ( ( self . style , None ) ) ;
299+ }
300+ break ;
301+ }
302+
303+ _ => {
304+ break ;
305+ }
306+ }
307+ }
308+ }
272309}
273310
274311#[ derive( Clone , Debug , PartialEq , Eq ) ]
275312pub ( crate ) struct Element {
276313 pub ( crate ) text : String ,
277314 pub ( crate ) style : anstyle:: Style ,
315+ pub ( crate ) url : Option < String > ,
278316}
279317
280318#[ derive( Copy , Clone , PartialEq , Eq , Debug ) ]
@@ -286,6 +324,13 @@ enum CsiState {
286324 Underline ,
287325}
288326
327+ #[ derive( Copy , Clone , PartialEq , Eq , Debug ) ]
328+ enum OscState {
329+ Normal ,
330+ HyperlinkParams ,
331+ HyperlinkUri ,
332+ }
333+
289334#[ derive( Copy , Clone , PartialEq , Eq , Debug ) ]
290335enum ColorTarget {
291336 Fg ,
@@ -334,10 +379,12 @@ mod test {
334379 Element {
335380 text: "Hello" . to_owned( ) ,
336381 style: green_on_red,
382+ url: None ,
337383 } ,
338384 Element {
339385 text: " world!" . to_owned( ) ,
340386 style: anstyle:: Style :: default ( ) ,
387+ url: None ,
341388 } ,
342389 ] ;
343390 verify ( & input, expected) ;
@@ -351,14 +398,17 @@ mod test {
351398 Element {
352399 text: "Hello " . to_owned( ) ,
353400 style: anstyle:: Style :: default ( ) ,
401+ url: None ,
354402 } ,
355403 Element {
356404 text: "world" . to_owned( ) ,
357405 style: green_on_red,
406+ url: None ,
358407 } ,
359408 Element {
360409 text: "!" . to_owned( ) ,
361410 style: anstyle:: Style :: default ( ) ,
411+ url: None ,
362412 } ,
363413 ] ;
364414 verify ( & input, expected) ;
@@ -372,10 +422,12 @@ mod test {
372422 Element {
373423 text: "Hello " . to_owned( ) ,
374424 style: anstyle:: Style :: default ( ) ,
425+ url: None ,
375426 } ,
376427 Element {
377428 text: "world!" . to_owned( ) ,
378429 style: green_on_red,
430+ url: None ,
379431 } ,
380432 ] ;
381433 verify ( & input, expected) ;
@@ -390,14 +442,17 @@ mod test {
390442 Element {
391443 text: "Hello " . to_owned( ) ,
392444 style: anstyle:: Style :: default ( ) ,
445+ url: None ,
393446 } ,
394447 Element {
395448 text: "world" . to_owned( ) ,
396449 style: ansi_11,
450+ url: None ,
397451 } ,
398452 Element {
399453 text: "!" . to_owned( ) ,
400454 style: anstyle:: Style :: default ( ) ,
455+ url: None ,
401456 } ,
402457 ] ;
403458 verify ( & input, expected) ;
@@ -414,10 +469,12 @@ mod test {
414469 Element {
415470 text: "Hello" . to_owned( ) ,
416471 style: green_on_red,
472+ url: Some ( URL . to_owned( ) ) ,
417473 } ,
418474 Element {
419475 text: " world!" . to_owned( ) ,
420476 style: anstyle:: Style :: default ( ) ,
477+ url: None ,
421478 } ,
422479 ] ;
423480 verify ( & input, expected) ;
@@ -434,14 +491,17 @@ mod test {
434491 Element {
435492 text: "Hello " . to_owned( ) ,
436493 style: anstyle:: Style :: default ( ) ,
494+ url: None ,
437495 } ,
438496 Element {
439497 text: "world" . to_owned( ) ,
440498 style: green_on_red,
499+ url: Some ( URL . to_owned( ) ) ,
441500 } ,
442501 Element {
443502 text: "!" . to_owned( ) ,
444503 style: anstyle:: Style :: default ( ) ,
504+ url: None ,
445505 } ,
446506 ] ;
447507 verify ( & input, expected) ;
@@ -458,10 +518,12 @@ mod test {
458518 Element {
459519 text: "Hello " . to_owned( ) ,
460520 style: anstyle:: Style :: default ( ) ,
521+ url: None ,
461522 } ,
462523 Element {
463524 text: "world!" . to_owned( ) ,
464525 style: green_on_red,
526+ url: Some ( URL . to_owned( ) ) ,
465527 } ,
466528 ] ;
467529 verify ( & input, expected) ;
@@ -476,14 +538,17 @@ mod test {
476538 Element {
477539 text: "Hello " . to_owned( ) ,
478540 style: anstyle:: Style :: default ( ) ,
541+ url: None ,
479542 } ,
480543 Element {
481544 text: "world" . to_owned( ) ,
482545 style: ansi_11,
546+ url: Some ( URL . to_owned( ) ) ,
483547 } ,
484548 Element {
485549 text: "!" . to_owned( ) ,
486550 style: anstyle:: Style :: default ( ) ,
551+ url: None ,
487552 } ,
488553 ] ;
489554 verify ( & input, expected) ;
@@ -498,14 +563,17 @@ mod test {
498563 Element {
499564 text: "Hello " . to_owned( ) ,
500565 style: anstyle:: Style :: default ( ) ,
566+ url: Some ( URL . to_owned( ) ) ,
501567 } ,
502568 Element {
503569 text: "world" . to_owned( ) ,
504570 style: green_on_red,
571+ url: Some ( URL . to_owned( ) ) ,
505572 } ,
506573 Element {
507574 text: "!" . to_owned( ) ,
508575 style: anstyle:: Style :: default ( ) ,
576+ url: Some ( URL . to_owned( ) ) ,
509577 } ,
510578 ] ;
511579 verify ( & input, expected) ;
@@ -522,10 +590,12 @@ mod test {
522590 Element {
523591 text: "Hello" . to_owned( ) ,
524592 style: green_on_red,
593+ url: None ,
525594 } ,
526595 Element {
527596 text: " world!" . to_owned( ) ,
528597 style: anstyle:: Style :: default ( ) ,
598+ url: None ,
529599 } ,
530600 ] ;
531601 verify ( & input, expected) ;
@@ -541,6 +611,7 @@ mod test {
541611 vec![ Element {
542612 text: s. clone( ) ,
543613 style: anstyle:: Style :: default ( ) ,
614+ url: None ,
544615 } ]
545616 } ;
546617 let mut state = AnsiBytes :: new( ) ;
0 commit comments