Skip to content

Commit aed54bb

Browse files
committed
fix: Make spans work with single width emojis
1 parent 6c23a41 commit aed54bb

File tree

6 files changed

+85
-64
lines changed

6 files changed

+85
-64
lines changed

Cargo.lock

+2-12
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ maintenance = { status = "actively-developed" }
2525

2626
[dependencies]
2727
anstyle = "1.0.4"
28-
itertools = "0.12.1"
2928
unicode-width = "0.1.11"
3029

3130
[dev-dependencies]

src/renderer/display_list.rs

+30-46
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,6 @@
3232
//!
3333
//! The above snippet has been built out of the following structure:
3434
use crate::snippet;
35-
use itertools::FoldWhile::{Continue, Done};
36-
use itertools::Itertools;
3735
use std::fmt::{Display, Write};
3836
use std::ops::Range;
3937
use std::{cmp, fmt};
@@ -830,20 +828,7 @@ fn format_header<'a>(
830828
} = item
831829
{
832830
if main_range >= range.0 && main_range <= range.1 {
833-
let char_column = text
834-
.chars()
835-
.map(|c| unicode_width::UnicodeWidthChar::width(c).unwrap_or(0))
836-
.chain(std::iter::once(1)) // treat the end of line as single-width
837-
.enumerate()
838-
.fold_while((0, 0), |(count, acc), (i, width)| {
839-
if acc <= main_range - range.0 {
840-
Continue((i, acc + width))
841-
} else {
842-
Done((count, acc))
843-
}
844-
})
845-
.into_inner()
846-
.0;
831+
let char_column = text[0..(main_range - range.0)].chars().count();
847832
col = char_column + 1;
848833
line_offset = lineno.unwrap_or(1);
849834
break;
@@ -984,18 +969,11 @@ fn format_body(
984969
let mut body = vec![];
985970
let mut current_line = snippet.line_start;
986971
let mut current_index = 0;
987-
let mut line_info = vec![];
988972

989-
struct LineInfo {
990-
line_start_index: usize,
991-
line_end_index: usize,
992-
}
993-
994-
for (line, end_line) in CursorLines::new(snippet.source) {
995-
let line_length: usize = line
996-
.chars()
997-
.map(|c| unicode_width::UnicodeWidthChar::width(c).unwrap_or(0))
998-
.sum();
973+
let mut annotation_line_count = 0;
974+
let mut annotations = snippet.annotations;
975+
for (idx, (line, end_line)) in CursorLines::new(snippet.source).enumerate() {
976+
let line_length: usize = line.len();
999977
let line_range = (current_index, current_index + line_length);
1000978
body.push(DisplayLine::Source {
1001979
lineno: Some(current_line),
@@ -1005,24 +983,11 @@ fn format_body(
1005983
range: line_range,
1006984
},
1007985
});
1008-
line_info.push(LineInfo {
1009-
line_start_index: line_range.0,
1010-
line_end_index: line_range.1,
1011-
});
986+
let line_start_index = line_range.0;
987+
let line_end_index = line_range.1;
1012988
current_line += 1;
1013989
current_index += line_length + end_line as usize;
1014-
}
1015990

1016-
let mut annotation_line_count = 0;
1017-
let mut annotations = snippet.annotations;
1018-
for (
1019-
idx,
1020-
LineInfo {
1021-
line_start_index,
1022-
line_end_index,
1023-
},
1024-
) in line_info.into_iter().enumerate()
1025-
{
1026991
let margin_left = margin
1027992
.map(|m| m.left(line_end_index - line_start_index))
1028993
.unwrap_or_default();
@@ -1040,8 +1005,20 @@ fn format_body(
10401005
if start >= line_start_index && end <= line_end_index
10411006
|| start == line_end_index && end - start <= 1 =>
10421007
{
1043-
let annotation_start_col = start - line_start_index - margin_left;
1044-
let annotation_end_col = end - line_start_index - margin_left;
1008+
let annotation_start_col = line[0..(start - line_start_index)]
1009+
.chars()
1010+
.map(|c| unicode_width::UnicodeWidthChar::width(c).unwrap_or(0))
1011+
.sum::<usize>()
1012+
- margin_left;
1013+
// This allows for annotations to be placed one past the
1014+
// last character
1015+
let safe_end = (end - line_start_index).saturating_sub(line_length);
1016+
let annotation_end_col = line[0..(end - line_start_index) - safe_end]
1017+
.chars()
1018+
.map(|c| unicode_width::UnicodeWidthChar::width(c).unwrap_or(0))
1019+
.sum::<usize>()
1020+
+ safe_end
1021+
- margin_left;
10451022
let range = (annotation_start_col, annotation_end_col);
10461023
body.insert(
10471024
body_idx + 1,
@@ -1080,7 +1057,10 @@ fn format_body(
10801057
});
10811058
}
10821059
} else {
1083-
let annotation_start_col = start - line_start_index;
1060+
let annotation_start_col = line[0..(start - line_start_index)]
1061+
.chars()
1062+
.map(|c| unicode_width::UnicodeWidthChar::width(c).unwrap_or(0))
1063+
.sum::<usize>();
10841064
let range = (annotation_start_col, annotation_start_col + 1);
10851065
body.insert(
10861066
body_idx + 1,
@@ -1132,7 +1112,11 @@ fn format_body(
11321112
});
11331113
}
11341114

1135-
let end_mark = (end - line_start_index).saturating_sub(1);
1115+
let end_mark = line[0..(end - line_start_index)]
1116+
.chars()
1117+
.map(|c| unicode_width::UnicodeWidthChar::width(c).unwrap_or(0))
1118+
.sum::<usize>()
1119+
.saturating_sub(1);
11361120
let range = (end_mark - margin_left, (end_mark + 1) - margin_left);
11371121
body.insert(
11381122
body_idx + 1,
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[message]
2+
title = "invalid character ` ` in package name: `haha this isn't a valid name 🐛`, characters must be Unicode XID characters (numbers, `-`, `_`, or most letters)"
3+
level = "Error"
4+
5+
6+
[[message.snippets]]
7+
source = """
8+
"haha this isn't a valid name 🐛" = { package = "libc", version = "0.1" }
9+
"""
10+
line_start = 7
11+
origin = "<file>"
12+
[[message.snippets.annotations]]
13+
label = ""
14+
level = "Error"
15+
range = [0, 35]

tests/formatter.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ fn test_point_to_double_width_characters() {
2525
let snippets = Level::Error.title("").snippet(
2626
Snippet::source("こんにちは、世界")
2727
.origin("<current file>")
28-
.annotation(Level::Error.span(12..16).label("world")),
28+
.annotation(Level::Error.span(18..24).label("world")),
2929
);
3030

3131
let expected = r#"error
@@ -44,7 +44,7 @@ fn test_point_to_double_width_characters_across_lines() {
4444
let snippets = Level::Error.title("").snippet(
4545
Snippet::source("おはよう\nございます")
4646
.origin("<current file>")
47-
.annotation(Level::Error.span(4..15).label("Good morning")),
47+
.annotation(Level::Error.span(6..22).label("Good morning")),
4848
);
4949

5050
let expected = r#"error
@@ -65,8 +65,8 @@ fn test_point_to_double_width_characters_multiple() {
6565
let snippets = Level::Error.title("").snippet(
6666
Snippet::source("お寿司\n食べたい🍣")
6767
.origin("<current file>")
68-
.annotation(Level::Error.span(0..6).label("Sushi1"))
69-
.annotation(Level::Note.span(11..15).label("Sushi2")),
68+
.annotation(Level::Error.span(0..9).label("Sushi1"))
69+
.annotation(Level::Note.span(16..22).label("Sushi2")),
7070
);
7171

7272
let expected = r#"error
@@ -87,7 +87,7 @@ fn test_point_to_double_width_characters_mixed() {
8787
let snippets = Level::Error.title("").snippet(
8888
Snippet::source("こんにちは、新しいWorld!")
8989
.origin("<current file>")
90-
.annotation(Level::Error.span(12..23).label("New world")),
90+
.annotation(Level::Error.span(18..32).label("New world")),
9191
);
9292

9393
let expected = r#"error

0 commit comments

Comments
 (0)