Skip to content

Commit 2d255b8

Browse files
authored
Improvement error message (#33)
1 parent 9d377fb commit 2d255b8

40 files changed

+469
-243
lines changed

Cargo.lock

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/uroborosql-fmt/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ repository.workspace = true
1111
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
1212

1313
[dependencies]
14+
annotate-snippets = "0.9.1"
1415
indexmap = "1.9.3"
1516
itertools = "0.10.3"
1617
once_cell = "1.17.0"

crates/uroborosql-fmt/src/cst.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ use tree_sitter::{Node, Point, Range};
3131

3232
use crate::{config::CONFIG, error::UroboroSQLFmtError};
3333

34-
#[derive(Debug, Clone)]
34+
#[derive(Debug, Clone, PartialEq)]
3535
pub(crate) struct Position {
3636
pub(crate) row: usize,
3737
pub(crate) col: usize,
@@ -46,7 +46,7 @@ impl Position {
4646
}
4747
}
4848

49-
#[derive(Debug, Clone)]
49+
#[derive(Debug, Clone, PartialEq)]
5050
pub(crate) struct Location {
5151
pub(crate) start_position: Position,
5252
pub(crate) end_position: Position,

crates/uroborosql-fmt/src/util.rs

Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
use crate::config::CONFIG;
1+
use annotate_snippets::{
2+
display_list::{DisplayList, FormatOptions},
3+
snippet::{AnnotationType, Slice, Snippet, SourceAnnotation},
4+
};
5+
use itertools::Itertools;
6+
7+
use crate::{config::CONFIG, cst::Location, error::UroboroSQLFmtError};
28

39
/// 設定ファイルに合わせて予約後の大文字・小文字を変換する
410
pub(crate) fn convert_keyword_case(keyword: &str) -> String {
@@ -72,3 +78,91 @@ pub(crate) fn is_line_overflow(char_len: usize) -> bool {
7278
char_len >= max_char_per_line as usize
7379
}
7480
}
81+
82+
/// xバイト目が何文字目かを返す
83+
fn byte_to_char_index(input: &str, target_byte_index: usize) -> Result<usize, UroboroSQLFmtError> {
84+
let mut char_index = 0;
85+
let mut byte_index = 0;
86+
87+
for c in input.chars() {
88+
if byte_index == target_byte_index {
89+
return Ok(char_index);
90+
}
91+
char_index += 1;
92+
byte_index += c.len_utf8();
93+
}
94+
95+
if byte_index == target_byte_index {
96+
Ok(char_index)
97+
} else {
98+
Err(UroboroSQLFmtError::Runtime(format!(
99+
"byte_to_char_index: byte_index({}) is out of range",
100+
target_byte_index
101+
)))
102+
}
103+
}
104+
105+
/// エラー注釈を作成する関数
106+
/// 以下の形のエラー注釈を生成
107+
///
108+
/// ```sh
109+
/// |
110+
/// 2 | using tbl_b b
111+
/// | ^^^^^^^^^^^^^ {label}
112+
/// |
113+
/// ```
114+
pub(crate) fn create_error_annotation(
115+
location: &Location,
116+
label: &str,
117+
src: &str,
118+
) -> Result<String, UroboroSQLFmtError> {
119+
// 元のSQLのエラーが発生した行を取得
120+
let source = src
121+
.lines()
122+
.enumerate()
123+
.filter(|(i, _)| (location.start_position.row..=location.end_position.row).contains(i))
124+
.map(|(_, x)| x)
125+
.join("\n");
126+
127+
// エラー発生箇所の開始位置
128+
// locaton.start_position.colはバイト数を指しているので文字数に変換する
129+
let start_point = byte_to_char_index(
130+
src.lines().collect_vec()[location.start_position.row],
131+
location.start_position.col,
132+
)?;
133+
134+
// エラー発生箇所の終了位置
135+
// = (終了位置までの行の文字数合計) + (終了位置の行の終了位置までの文字数)
136+
let end_point = src
137+
.lines()
138+
.enumerate()
139+
.filter(|(i, _)| (location.start_position.row..location.end_position.row).contains(i))
140+
.map(|(_, x)| x.chars().count() + 1) // 改行コードの分1プラスする
141+
.sum::<usize>()
142+
+ byte_to_char_index(
143+
src.lines().collect_vec()[location.end_position.row],
144+
location.end_position.col,
145+
)?; // エラー発生行の終了位置までの文字数 (locaton.end_position.colはバイト数を指しているので文字数に変換する)
146+
147+
let snippet = Snippet {
148+
title: None,
149+
footer: vec![],
150+
slices: vec![Slice {
151+
source: &source,
152+
line_start: location.start_position.row + 1,
153+
origin: None,
154+
fold: true,
155+
annotations: vec![SourceAnnotation {
156+
label,
157+
annotation_type: AnnotationType::Error,
158+
range: (start_point, end_point),
159+
}],
160+
}],
161+
opt: FormatOptions {
162+
color: true,
163+
..Default::default()
164+
},
165+
};
166+
167+
Ok(DisplayList::from(snippet).to_string())
168+
}

0 commit comments

Comments
 (0)