Skip to content

Commit ac31f8a

Browse files
committed
Fix display of annotation for double width characters
1 parent a0e10a8 commit ac31f8a

File tree

2 files changed

+85
-21
lines changed

2 files changed

+85
-21
lines changed

src/display_list/from_snippet.rs

Lines changed: 57 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -293,11 +293,23 @@ fn format_body(
293293
let mut body = vec![];
294294
let mut current_line = slice.line_start;
295295
let mut current_index = 0;
296-
let mut line_index_ranges = vec![];
296+
let mut line_info = vec![];
297+
298+
struct LineInfo {
299+
line_start_index: usize,
300+
line_end_index: usize,
301+
// How many spaces each character in the line take up when displayed
302+
char_widths: Vec<usize>,
303+
}
297304

298305
for (line, end_line) in CursorLines::new(slice.source) {
299306
let line_length = line.chars().count();
300307
let line_range = (current_index, current_index + line_length);
308+
let char_widths = line
309+
.chars()
310+
.map(|c| unicode_width::UnicodeWidthChar::width(c).unwrap_or(0))
311+
.chain(std::iter::once(1)) // treat the end of line as signle-width
312+
.collect::<Vec<_>>();
301313
body.push(DisplayLine::Source {
302314
lineno: Some(current_line),
303315
inline_marks: vec![],
@@ -306,16 +318,28 @@ fn format_body(
306318
range: line_range,
307319
},
308320
});
309-
line_index_ranges.push(line_range);
321+
line_info.push(LineInfo {
322+
line_start_index: line_range.0,
323+
line_end_index: line_range.1,
324+
char_widths,
325+
});
310326
current_line += 1;
311327
current_index += line_length + end_line as usize;
312328
}
313329

314330
let mut annotation_line_count = 0;
315331
let mut annotations = slice.annotations;
316-
for (idx, (line_start, line_end)) in line_index_ranges.into_iter().enumerate() {
332+
for (
333+
idx,
334+
LineInfo {
335+
line_start_index,
336+
line_end_index,
337+
char_widths,
338+
},
339+
) in line_info.into_iter().enumerate()
340+
{
317341
let margin_left = margin
318-
.map(|m| m.left(line_end - line_start))
342+
.map(|m| m.left(line_end_index - line_start_index))
319343
.unwrap_or_default();
320344
// It would be nice to use filter_drain here once it's stable.
321345
annotations = annotations
@@ -328,15 +352,22 @@ fn format_body(
328352
_ => DisplayAnnotationType::from(annotation.annotation_type),
329353
};
330354
match annotation.range {
331-
(start, _) if start > line_end => true,
355+
(start, _) if start > line_end_index => true,
332356
(start, end)
333-
if start >= line_start && end <= line_end
334-
|| start == line_end && end - start <= 1 =>
357+
if start >= line_start_index && end <= line_end_index
358+
|| start == line_end_index && end - start <= 1 =>
335359
{
336-
let range = (
337-
(start - line_start) - margin_left,
338-
(end - line_start) - margin_left,
339-
);
360+
let annotation_start_col = char_widths
361+
.iter()
362+
.take(start - line_start_index)
363+
.sum::<usize>()
364+
- margin_left;
365+
let annotation_end_col = char_widths
366+
.iter()
367+
.take(end - line_start_index)
368+
.sum::<usize>()
369+
- margin_left;
370+
let range = (annotation_start_col, annotation_end_col);
340371
body.insert(
341372
body_idx + 1,
342373
DisplayLine::Source {
@@ -359,8 +390,12 @@ fn format_body(
359390
annotation_line_count += 1;
360391
false
361392
}
362-
(start, end) if start >= line_start && start <= line_end && end > line_end => {
363-
if start - line_start == 0 {
393+
(start, end)
394+
if start >= line_start_index
395+
&& start <= line_end_index
396+
&& end > line_end_index =>
397+
{
398+
if start - line_start_index == 0 {
364399
if let DisplayLine::Source {
365400
ref mut inline_marks,
366401
..
@@ -374,7 +409,7 @@ fn format_body(
374409
});
375410
}
376411
} else {
377-
let range = (start - line_start, start - line_start + 1);
412+
let range = (start - line_start_index, start - line_start_index + 1);
378413
body.insert(
379414
body_idx + 1,
380415
DisplayLine::Source {
@@ -398,7 +433,7 @@ fn format_body(
398433
}
399434
true
400435
}
401-
(start, end) if start < line_start && end > line_end => {
436+
(start, end) if start < line_start_index && end > line_end_index => {
402437
if let DisplayLine::Source {
403438
ref mut inline_marks,
404439
..
@@ -413,7 +448,11 @@ fn format_body(
413448
}
414449
true
415450
}
416-
(start, end) if start < line_start && end >= line_start && end <= line_end => {
451+
(start, end)
452+
if start < line_start_index
453+
&& end >= line_start_index
454+
&& end <= line_end_index =>
455+
{
417456
if let DisplayLine::Source {
418457
ref mut inline_marks,
419458
..
@@ -427,11 +466,8 @@ fn format_body(
427466
});
428467
}
429468

430-
let end_mark = (end - line_start).saturating_sub(1);
431-
let range = (
432-
end_mark - margin_left,
433-
(end_mark + 1) - margin_left,
434-
);
469+
let end_mark = (end - line_start_index).saturating_sub(1);
470+
let range = (end_mark - margin_left, (end_mark + 1) - margin_left);
435471
body.insert(
436472
body_idx + 1,
437473
DisplayLine::Source {

tests/formatter.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,3 +550,31 @@ fn test_i_29() {
550550

551551
assert_eq!(DisplayList::from(snippets).to_string(), expected);
552552
}
553+
554+
#[test]
555+
fn test_point_to_double_width_characters() {
556+
let snippets = Snippet {
557+
slices: vec![snippet::Slice {
558+
source: "こんにちは、世界",
559+
line_start: 1,
560+
origin: Some("<current file>"),
561+
annotations: vec![snippet::SourceAnnotation {
562+
range: (6, 8),
563+
label: "world",
564+
annotation_type: snippet::AnnotationType::Error,
565+
}],
566+
fold: false,
567+
}],
568+
title: None,
569+
footer: vec![],
570+
opt: Default::default(),
571+
};
572+
573+
let expected = r#" --> <current file>:1:7
574+
|
575+
1 | こんにちは、世界
576+
| ^^^^ world
577+
|"#;
578+
579+
assert_eq!(DisplayList::from(snippets).to_string(), expected);
580+
}

0 commit comments

Comments
 (0)