Skip to content

Commit 509d2da

Browse files
committed
feat: Add support for hyperlinks in snippet origin
1 parent 5b5b498 commit 509d2da

7 files changed

Lines changed: 311 additions & 19 deletions

src/renderer/render.rs

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use alloc::collections::BTreeMap;
55
use alloc::string::{String, ToString};
66
use alloc::{format, vec, vec::Vec};
77
use core::cmp::{Ordering, Reverse, max, min};
8-
use core::fmt;
8+
use core::fmt::{self, Display, Formatter};
99

1010
use anstyle::Style;
1111

@@ -20,8 +20,8 @@ use crate::renderer::source_map::{
2020
use crate::renderer::styled_buffer::StyledBuffer;
2121
use crate::snippet::Id;
2222
use crate::{
23-
Annotation, AnnotationKind, Element, Group, Message, Origin, Padding, Patch, Report, Snippet,
24-
Title,
23+
Annotation, AnnotationKind, Element, Group, Message, Origin, Padding, Patch, PathUrlFormatter,
24+
Report, Snippet, Title,
2525
};
2626

2727
const ANONYMIZED_LINE_NUM: &str = "LL";
@@ -271,6 +271,13 @@ fn render_short_message(renderer: &Renderer, groups: &[Group<'_>]) -> Result<Str
271271
let mut origin = Origin::path(path.as_ref());
272272

273273
let source_map = SourceMap::new(&cause.source, cause.line_start);
274+
if let Some(primary_span) = cause.primary_span()
275+
&& let Some(path_url_formatter) = &cause.url
276+
{
277+
let (line, byte_col) = source_map.byte_to_line_byte_col(primary_span.start);
278+
let url = path_url_formatter.format_url(line, byte_col);
279+
origin.url = Some(Cow::from(url));
280+
}
274281
let (_depth, annotated_lines) =
275282
source_map.annotated_lines(cause.markers.clone(), cause.fold);
276283

@@ -359,9 +366,7 @@ fn render_title(
359366
label_width += title.level().as_str().len();
360367
if let Some(Id { id: Some(id), url }) = &title.id() {
361368
buffer.append(buffer_msg_line_offset, "[", label_style);
362-
let link = Hyperlink {
363-
url: url.as_deref(),
364-
};
369+
let link = Hyperlink::from(url.as_deref());
365370
buffer.append(
366371
buffer_msg_line_offset,
367372
&format!("{link}{id}{link:#}"),
@@ -485,6 +490,9 @@ fn render_origin(
485490
_ => origin.path.to_string(),
486491
};
487492

493+
let link = Hyperlink::from(origin.url.as_deref());
494+
let str = format!("{link}{str}{link:#}");
495+
488496
buffer.append(buffer_msg_line_offset, &str, ElementStyle::LineAndColumn);
489497
if !renderer.short_message {
490498
for _ in 0..max_line_num_len {
@@ -552,6 +560,15 @@ fn render_snippet_annotations(
552560
}
553561
}
554562
}
563+
564+
if let Some(primary_span) = snippet.primary_span()
565+
&& let Some(url_formatter) = &snippet.url
566+
{
567+
let (line, byte_col) = sm.byte_to_line_byte_col(primary_span.start);
568+
let url = url_formatter.format_url(line, byte_col);
569+
origin.url = Some(Cow::from(url));
570+
}
571+
555572
let buffer_msg_line_offset = buffer.num_lines();
556573
render_origin(
557574
renderer,
@@ -1447,11 +1464,18 @@ fn emit_suggestion_default(
14471464
&& let Some(path) = suggestion.path.as_ref()
14481465
&& !matches_previous_suggestion
14491466
{
1450-
let (loc, _) = sm.span_to_locations(parts[0].span.clone());
1451-
let origin = Origin::path(path.as_ref())
1467+
let span = parts[0].span.clone();
1468+
let (loc, _) = sm.span_to_locations(span.clone());
1469+
let mut origin = Origin::path(path.as_ref())
14521470
.line(loc.line)
14531471
.char_column(loc.char + 1);
14541472

1473+
if let Some(formatter) = &suggestion.url {
1474+
let (line, byte_col) = sm.byte_to_line_byte_col(span.start);
1475+
let url = formatter.format_url(line, byte_col);
1476+
origin.url = Some(Cow::from(url));
1477+
}
1478+
14551479
render_origin(
14561480
renderer,
14571481
buffer,
@@ -2758,18 +2782,18 @@ fn newline_count(body: &str) -> usize {
27582782
}
27592783
}
27602784

2761-
struct Hyperlink<D: fmt::Display> {
2762-
url: Option<D>,
2785+
struct Hyperlink<'a> {
2786+
url: Option<&'a str>,
27632787
}
27642788

2765-
impl<D: fmt::Display> Default for Hyperlink<D> {
2766-
fn default() -> Self {
2767-
Self { url: None }
2789+
impl<'a> From<Option<&'a str>> for Hyperlink<'a> {
2790+
fn from(url: Option<&'a str>) -> Self {
2791+
Hyperlink { url }
27682792
}
27692793
}
27702794

2771-
impl<D: fmt::Display> fmt::Display for Hyperlink<D> {
2772-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2795+
impl<'a> Display for Hyperlink<'a> {
2796+
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
27732797
let Some(url) = self.url.as_ref() else {
27742798
return Ok(());
27752799
};

src/renderer/source_map.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,19 @@ impl<'a> SourceMap<'a> {
566566
Some((buf, trimmed_patches, highlights))
567567
}
568568
}
569+
570+
pub(crate) fn byte_to_line_byte_col(&self, byte: usize) -> (usize, usize) {
571+
let start_info = self
572+
.lines
573+
.iter()
574+
.find(|info| byte >= info.start_byte && byte < info.end_byte)
575+
.unwrap_or(self.lines.last().unwrap());
576+
577+
let line = start_info.line_index;
578+
let byte_col = byte - start_info.start_byte;
579+
580+
(line, byte_col)
581+
}
569582
}
570583

571584
#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]

0 commit comments

Comments
 (0)