Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit fc99036

Browse files
Merge pull request #46 from magurotuna/check-display-width
Fix display of annotation for double width characters
2 parents 73d6000 + fbdab5c commit fc99036

File tree

2 files changed

+188
-21
lines changed

2 files changed

+188
-21
lines changed

‎src/display_list/from_snippet.rs

Lines changed: 65 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,11 @@ fn format_body(
374409
});
375410
}
376411
} else {
377-
let range = (start - line_start, start - line_start + 1);
412+
let annotation_start_col = char_widths
413+
.iter()
414+
.take(start - line_start_index)
415+
.sum::<usize>();
416+
let range = (annotation_start_col, annotation_start_col + 1);
378417
body.insert(
379418
body_idx + 1,
380419
DisplayLine::Source {
@@ -398,7 +437,7 @@ fn format_body(
398437
}
399438
true
400439
}
401-
(start, end) if start < line_start && end > line_end => {
440+
(start, end) if start < line_start_index && end > line_end_index => {
402441
if let DisplayLine::Source {
403442
ref mut inline_marks,
404443
..
@@ -413,7 +452,11 @@ fn format_body(
413452
}
414453
true
415454
}
416-
(start, end) if start < line_start && end >= line_start && end <= line_end => {
455+
(start, end)
456+
if start < line_start_index
457+
&& end >= line_start_index
458+
&& end <= line_end_index =>
459+
{
417460
if let DisplayLine::Source {
418461
ref mut inline_marks,
419462
..
@@ -427,11 +470,12 @@ fn format_body(
427470
});
428471
}
429472

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-
);
473+
let end_mark = char_widths
474+
.iter()
475+
.take(end - line_start_index)
476+
.sum::<usize>()
477+
.saturating_sub(1);
478+
let range = (end_mark - margin_left, (end_mark + 1) - margin_left);
435479
body.insert(
436480
body_idx + 1,
437481
DisplayLine::Source {

‎tests/formatter.rs

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,3 +550,126 @@ 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+
}
581+
582+
#[test]
583+
fn test_point_to_double_width_characters_across_lines() {
584+
let snippets = Snippet {
585+
slices: vec![snippet::Slice {
586+
source: "おはよう\nございます",
587+
line_start: 1,
588+
origin: Some("<current file>"),
589+
annotations: vec![snippet::SourceAnnotation {
590+
range: (2, 8),
591+
label: "Good morning",
592+
annotation_type: snippet::AnnotationType::Error,
593+
}],
594+
fold: false,
595+
}],
596+
title: None,
597+
footer: vec![],
598+
opt: Default::default(),
599+
};
600+
601+
let expected = r#" --> <current file>:1:3
602+
|
603+
1 | おはよう
604+
| _____^
605+
2 | | ございます
606+
| |______^ Good morning
607+
|"#;
608+
609+
assert_eq!(DisplayList::from(snippets).to_string(), expected);
610+
}
611+
612+
#[test]
613+
fn test_point_to_double_width_characters_multiple() {
614+
let snippets = Snippet {
615+
slices: vec![snippet::Slice {
616+
source: "お寿司\n食べたい🍣",
617+
line_start: 1,
618+
origin: Some("<current file>"),
619+
annotations: vec![
620+
snippet::SourceAnnotation {
621+
range: (0, 3),
622+
label: "Sushi1",
623+
annotation_type: snippet::AnnotationType::Error,
624+
},
625+
snippet::SourceAnnotation {
626+
range: (6, 8),
627+
label: "Sushi2",
628+
annotation_type: snippet::AnnotationType::Note,
629+
},
630+
],
631+
fold: false,
632+
}],
633+
title: None,
634+
footer: vec![],
635+
opt: Default::default(),
636+
};
637+
638+
let expected = r#" --> <current file>:1:1
639+
|
640+
1 | お寿司
641+
| ^^^^^^ Sushi1
642+
2 | 食べたい🍣
643+
| ---- note: Sushi2
644+
|"#;
645+
646+
assert_eq!(DisplayList::from(snippets).to_string(), expected);
647+
}
648+
649+
#[test]
650+
fn test_point_to_double_width_characters_mixed() {
651+
let snippets = Snippet {
652+
slices: vec![snippet::Slice {
653+
source: "こんにちは、新しいWorld!",
654+
line_start: 1,
655+
origin: Some("<current file>"),
656+
annotations: vec![snippet::SourceAnnotation {
657+
range: (6, 14),
658+
label: "New world",
659+
annotation_type: snippet::AnnotationType::Error,
660+
}],
661+
fold: false,
662+
}],
663+
title: None,
664+
footer: vec![],
665+
opt: Default::default(),
666+
};
667+
668+
let expected = r#" --> <current file>:1:7
669+
|
670+
1 | こんにちは、新しいWorld!
671+
| ^^^^^^^^^^^ New world
672+
|"#;
673+
674+
assert_eq!(DisplayList::from(snippets).to_string(), expected);
675+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /