mirror of
https://github.com/erg-lang/erg.git
synced 2025-09-30 21:01:10 +00:00
Change: add main_msg
and SubMessage
This commit is contained in:
parent
22652cb174
commit
823773def1
1 changed files with 223 additions and 248 deletions
|
@ -14,6 +14,7 @@ use crate::style::StyledStr;
|
|||
use crate::style::StyledString;
|
||||
use crate::style::StyledStrings;
|
||||
use crate::style::Theme;
|
||||
use crate::style::THEME;
|
||||
use crate::traits::{Locational, Stream};
|
||||
use crate::{impl_display_from_debug, switch_lang};
|
||||
|
||||
|
@ -313,70 +314,6 @@ impl Location {
|
|||
}
|
||||
}
|
||||
|
||||
/// In Erg, common parts used by error.
|
||||
/// Must be wrap when to use.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct ErrorCore {
|
||||
pub errno: usize,
|
||||
pub kind: ErrorKind,
|
||||
pub loc: Location,
|
||||
pub desc: AtomicStr,
|
||||
pub hint: Option<AtomicStr>,
|
||||
}
|
||||
|
||||
impl ErrorCore {
|
||||
pub fn new<S: Into<AtomicStr>>(
|
||||
errno: usize,
|
||||
kind: ErrorKind,
|
||||
loc: Location,
|
||||
desc: S,
|
||||
hint: Option<AtomicStr>,
|
||||
) -> Self {
|
||||
Self {
|
||||
errno,
|
||||
kind,
|
||||
loc,
|
||||
desc: desc.into(),
|
||||
hint,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dummy(errno: usize) -> Self {
|
||||
Self::new(
|
||||
errno,
|
||||
DummyError,
|
||||
Location::Line(errno as usize),
|
||||
"<dummy>",
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn unreachable(fn_name: &str, line: u32) -> Self {
|
||||
Self::bug(line as usize, Location::Line(line as usize), fn_name, line)
|
||||
}
|
||||
|
||||
pub fn bug(errno: usize, loc: Location, fn_name: &str, line: u32) -> Self {
|
||||
const URL: StyledStr = StyledStr::new(
|
||||
"https://github.com/erg-lang/erg",
|
||||
Some(Color::White),
|
||||
Some(Attribute::Underline),
|
||||
);
|
||||
|
||||
Self::new(
|
||||
errno,
|
||||
CompilerSystemError,
|
||||
loc,
|
||||
switch_lang!(
|
||||
"japanese" => format!("これはErgのバグです、開発者に報告して下さい({URL})\n{fn_name}:{line}より発生"),
|
||||
"simplified_chinese" => format!("这是Erg的bug,请报告给{URL}\n原因来自: {fn_name}:{line}"),
|
||||
"traditional_chinese" => format!("这是Erg的bug,请报告给{URL}\n原因来自: {fn_name}:{line}"),
|
||||
"english" => format!("this is a bug of Erg, please report it to {URL}\ncaused from: {fn_name}:{line}"),
|
||||
),
|
||||
None,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn format_context<E: ErrorDisplay + ?Sized>(
|
||||
e: &E,
|
||||
|
@ -390,6 +327,8 @@ fn format_context<E: ErrorDisplay + ?Sized>(
|
|||
chars: &Characters,
|
||||
// kinds of error for specify the color
|
||||
mark: char,
|
||||
sub_msg: Option<&AtomicStr>,
|
||||
hint: Option<&AtomicStr>,
|
||||
) -> String {
|
||||
let mark = mark.to_string();
|
||||
let codes = if e.input().is_repl() {
|
||||
|
@ -429,12 +368,201 @@ fn format_context<E: ErrorDisplay + ?Sized>(
|
|||
}
|
||||
context.push_str("\n");
|
||||
}
|
||||
if let Some(msg) = sub_msg {
|
||||
context.push_str_with_color(&offset, gutter_color);
|
||||
context.push_str(&" ".repeat(col_end - 1));
|
||||
context.push_str_with_color(&chars.left_bottom_line(), err_color);
|
||||
context.push_str(msg);
|
||||
}
|
||||
if let Some(hint) = hint {
|
||||
context.push_str_with_color(&offset, gutter_color);
|
||||
context.push_str(&" ".repeat(col_end - 1));
|
||||
context.push_str_with_color(&chars.left_bottom_line(), err_color);
|
||||
context.push_str(hint);
|
||||
}
|
||||
context.to_string()
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct SubMessage {
|
||||
pub loc: Location,
|
||||
msg: Option<AtomicStr>,
|
||||
hint: Option<AtomicStr>,
|
||||
}
|
||||
|
||||
impl SubMessage {
|
||||
pub fn new<S: Into<AtomicStr>>(loc: Location, msg: S, hint: S) -> Self {
|
||||
Self {
|
||||
loc,
|
||||
msg: Some(msg.into()),
|
||||
hint: Some(hint.into()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ambiguous_new(loc: Location, msg: Option<AtomicStr>, hint: Option<AtomicStr>) -> Self {
|
||||
Self { loc, msg, hint }
|
||||
}
|
||||
|
||||
pub fn only_loc(loc: Location) -> Self {
|
||||
Self {
|
||||
loc,
|
||||
msg: None,
|
||||
hint: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_hint<S: Into<AtomicStr>>(&mut self, hint: S) {
|
||||
self.hint = Some(hint.into());
|
||||
}
|
||||
|
||||
pub fn get_hint(self) -> Option<AtomicStr> {
|
||||
self.hint
|
||||
}
|
||||
|
||||
fn format_code_and_pointer<E: ErrorDisplay + ?Sized>(
|
||||
&self,
|
||||
e: &E,
|
||||
err_color: Color,
|
||||
gutter_color: Color,
|
||||
mark: char,
|
||||
chars: &Characters,
|
||||
) -> String {
|
||||
match self.loc {
|
||||
Location::Range {
|
||||
ln_begin,
|
||||
col_begin,
|
||||
ln_end,
|
||||
col_end,
|
||||
} => format_context(
|
||||
e,
|
||||
ln_begin,
|
||||
ln_end,
|
||||
col_begin,
|
||||
col_end,
|
||||
err_color,
|
||||
gutter_color,
|
||||
chars,
|
||||
mark,
|
||||
self.msg.as_ref(),
|
||||
self.hint.as_ref(),
|
||||
),
|
||||
Location::LineRange(ln_begin, ln_end) => {
|
||||
let input = e.input();
|
||||
let (_, vbar) = chars.gutters();
|
||||
let mut cxt = StyledStrings::default();
|
||||
let codes = if input.is_repl() {
|
||||
vec![input.reread()]
|
||||
} else {
|
||||
input.reread_lines(ln_begin, ln_end)
|
||||
};
|
||||
let mark = mark.to_string();
|
||||
for (i, lineno) in (ln_begin..=ln_end).enumerate() {
|
||||
cxt.push_str_with_color(&format!("{lineno} {}", vbar), err_color);
|
||||
cxt.push_str(&codes[i]);
|
||||
cxt.push_str(&" ".repeat(lineno.to_string().len() + 3)); // +3 means ` | `
|
||||
cxt.push_str_with_color(&mark.repeat(cmp::max(1, codes[i].len())), gutter_color)
|
||||
}
|
||||
cxt.to_string()
|
||||
}
|
||||
Location::Line(lineno) => {
|
||||
let input = e.input();
|
||||
let (_, vbar) = chars.gutters();
|
||||
let code = if input.is_repl() {
|
||||
input.reread()
|
||||
} else {
|
||||
input.reread_lines(lineno, lineno).remove(0)
|
||||
};
|
||||
let mut cxt = StyledStrings::default();
|
||||
cxt.push_str_with_color(&format!(" {lineno} {} ", vbar), gutter_color);
|
||||
cxt.push_str(&code);
|
||||
cxt.push_str("\n");
|
||||
cxt.to_string()
|
||||
}
|
||||
Location::Unknown => match e.input() {
|
||||
Input::File(_) => "\n".to_string(),
|
||||
|
||||
other => {
|
||||
let (_, vbar) = chars.gutters();
|
||||
let mut cxt = StyledStrings::default();
|
||||
cxt.push_str_with_color(&format!(" ? {}", vbar), gutter_color);
|
||||
cxt.push_str(&other.reread());
|
||||
cxt.to_string()
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// In Erg, common parts used by error.
|
||||
/// Must be wrap when to use.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct ErrorCore {
|
||||
pub sub_messages: Vec<SubMessage>,
|
||||
pub main_message: AtomicStr,
|
||||
pub errno: usize,
|
||||
pub kind: ErrorKind,
|
||||
theme: Theme,
|
||||
}
|
||||
|
||||
impl ErrorCore {
|
||||
pub fn new<S: Into<AtomicStr>>(
|
||||
sub_messages: Vec<SubMessage>,
|
||||
main_message: S,
|
||||
errno: usize,
|
||||
kind: ErrorKind,
|
||||
) -> Self {
|
||||
Self {
|
||||
sub_messages,
|
||||
main_message: main_message.into(),
|
||||
errno,
|
||||
kind,
|
||||
theme: THEME,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sub_messages(&self) -> &Vec<SubMessage> {
|
||||
&self.sub_messages
|
||||
}
|
||||
|
||||
pub fn dummy(errno: usize) -> Self {
|
||||
Self::new(
|
||||
vec![SubMessage::only_loc(Location::Line(errno as usize))],
|
||||
"<dummy>",
|
||||
errno,
|
||||
DummyError,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn unreachable(fn_name: &str, line: u32) -> Self {
|
||||
Self::bug(line as usize, Location::Line(line as usize), fn_name, line)
|
||||
}
|
||||
|
||||
pub fn bug(errno: usize, loc: Location, fn_name: &str, line: u32) -> Self {
|
||||
const URL: StyledStr = StyledStr::new(
|
||||
"https://github.com/erg-lang/erg",
|
||||
Some(Color::White),
|
||||
Some(Attribute::Underline),
|
||||
);
|
||||
|
||||
let m_msg = switch_lang!(
|
||||
"japanese" => format!("これはErgのバグです、開発者に報告して下さい({URL})\n{fn_name}:{line}より発生"),
|
||||
"simplified_chinese" => format!("这是Erg的bug,请报告给{URL}\n原因来自: {fn_name}:{line}"),
|
||||
"traditional_chinese" => format!("这是Erg的bug,请报告给{URL}\n原因来自: {fn_name}:{line}"),
|
||||
"english" => format!("this is a bug of Erg, please report it to {URL}\ncaused from: {fn_name}:{line}"),
|
||||
);
|
||||
Self::new(
|
||||
vec![SubMessage::only_loc(loc)],
|
||||
&m_msg,
|
||||
errno,
|
||||
CompilerSystemError,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn fmt_main_message(&self, kind: StyledString) -> String {
|
||||
format!("{}\n{}\n\n", kind, self.main_message)
|
||||
}
|
||||
}
|
||||
|
||||
/// format:
|
||||
/// ```txt
|
||||
/// Error[#{.errno}]: File {file}, line {.loc (as line)}, in {.caused_by}
|
||||
|
@ -460,11 +588,8 @@ fn format_context<E: ErrorDisplay + ?Sized>(
|
|||
pub trait ErrorDisplay {
|
||||
fn core(&self) -> &ErrorCore;
|
||||
fn input(&self) -> &Input;
|
||||
/// Colors and indication char for each type(error, warning, exception)
|
||||
fn theme(&self) -> &Theme;
|
||||
/// The block name the error caused.
|
||||
/// This will be None if the error occurred before semantic analysis.
|
||||
/// As for the internal error, do not put the fn name here.
|
||||
fn caused_by(&self) -> &str;
|
||||
/// the previous error that caused this error.
|
||||
fn ref_inner(&self) -> Option<&Self>;
|
||||
|
@ -484,107 +609,53 @@ pub trait ErrorDisplay {
|
|||
}
|
||||
|
||||
fn show(&self) -> String {
|
||||
let theme = self.theme();
|
||||
let ((color, mark), kind) = if self.core().kind.is_error() {
|
||||
(theme.error(), "Error")
|
||||
} else if self.core().kind.is_warning() {
|
||||
(theme.warning(), "Warning")
|
||||
let core = self.core();
|
||||
let ((color, mark), kind) = if core.kind.is_error() {
|
||||
(core.theme.error(), "Error")
|
||||
} else if core.kind.is_warning() {
|
||||
(core.theme.warning(), "Warning")
|
||||
} else {
|
||||
(theme.exception(), "Exception")
|
||||
(core.theme.exception(), "Exception")
|
||||
};
|
||||
|
||||
let (gutter_color, chars) = theme.characters();
|
||||
let (gutter_color, chars) = core.theme.characters();
|
||||
let kind = StyledString::new(
|
||||
&chars.error_kind_format(kind, self.core().errno),
|
||||
&chars.error_kind_format(kind, core.errno),
|
||||
Some(color),
|
||||
Some(Attribute::Bold),
|
||||
);
|
||||
|
||||
// When hint is None, hint desc is "" and empty line is displayed, but hint is Some(...), hint desc is "..." and filled by text
|
||||
if let Some(hint) = self.core().hint.as_ref() {
|
||||
let (hint_color, _) = theme.hint();
|
||||
let mut hints = StyledStrings::default();
|
||||
hints.push_str_with_color_and_attribute("hint: ", hint_color, Attribute::Bold);
|
||||
hints.push_str(hint);
|
||||
format!(
|
||||
"\
|
||||
{}
|
||||
{}{}: {}
|
||||
|
||||
{}
|
||||
|
||||
",
|
||||
self.format_header(kind),
|
||||
self.format_code_and_pointer(color, gutter_color, mark, chars),
|
||||
self.core().kind,
|
||||
self.core().desc,
|
||||
hints,
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"\
|
||||
{}
|
||||
{}{}: {}
|
||||
|
||||
",
|
||||
self.format_header(kind),
|
||||
self.format_code_and_pointer(color, gutter_color, mark, chars),
|
||||
self.core().kind,
|
||||
self.core().desc,
|
||||
)
|
||||
let sub_messages = self.core().sub_messages();
|
||||
let mut msg = String::new();
|
||||
msg += &core.fmt_main_message(kind);
|
||||
for sub_msg in sub_messages {
|
||||
msg += &sub_msg.format_code_and_pointer(self, color, gutter_color, mark, chars)
|
||||
}
|
||||
msg += "\n";
|
||||
msg
|
||||
}
|
||||
|
||||
/// for fmt::Display
|
||||
fn format(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let theme = self.theme();
|
||||
let ((color, mark), kind) = if self.core().kind.is_error() {
|
||||
(theme.error(), "Error")
|
||||
} else if self.core().kind.is_warning() {
|
||||
(theme.warning(), "Warning")
|
||||
let core = self.core();
|
||||
let ((color, mark), kind) = if core.kind.is_error() {
|
||||
(core.theme.error(), "Error")
|
||||
} else if core.kind.is_warning() {
|
||||
(core.theme.warning(), "Warning")
|
||||
} else {
|
||||
(theme.exception(), "Exception")
|
||||
(core.theme.exception(), "Exception")
|
||||
};
|
||||
let (gutter_color, chars) = theme.characters();
|
||||
let (gutter_color, chars) = core.theme.characters();
|
||||
let kind = StyledString::new(
|
||||
&chars.error_kind_format(kind, self.core().errno),
|
||||
&chars.error_kind_format(kind, core.errno),
|
||||
Some(color),
|
||||
Some(Attribute::Bold),
|
||||
);
|
||||
|
||||
// When hint is None, hint desc is "" and empty line is displayed, but hint is Some(...), hint desc is "..." and filled by text
|
||||
if let Some(hint) = self.core().hint.as_ref() {
|
||||
let (hint_color, _) = theme.hint();
|
||||
let mut hints = StyledStrings::default();
|
||||
hints.push_str_with_color_and_attribute("hint: ", hint_color, Attribute::Bold);
|
||||
hints.push_str(hint);
|
||||
writeln!(f, "{}", core.fmt_main_message(kind))?;
|
||||
let sub_messages = self.core().sub_messages();
|
||||
for sub_msg in sub_messages {
|
||||
writeln!(
|
||||
f,
|
||||
"\
|
||||
{}
|
||||
{}{}: {}
|
||||
|
||||
{}
|
||||
|
||||
",
|
||||
self.format_header(kind),
|
||||
self.format_code_and_pointer(color, gutter_color, mark, chars),
|
||||
self.core().kind,
|
||||
self.core().desc,
|
||||
hints,
|
||||
)?;
|
||||
} else {
|
||||
writeln!(
|
||||
f,
|
||||
"\
|
||||
{}
|
||||
{}{}: {}
|
||||
|
||||
",
|
||||
self.format_header(kind),
|
||||
self.format_code_and_pointer(color, gutter_color, mark, chars),
|
||||
self.core().kind,
|
||||
self.core().desc,
|
||||
"{}",
|
||||
&sub_msg.format_code_and_pointer(self, color, gutter_color, mark, chars)
|
||||
)?;
|
||||
}
|
||||
if let Some(inner) = self.ref_inner() {
|
||||
|
@ -593,102 +664,6 @@ pub trait ErrorDisplay {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn format_header(&self, kind: StyledString) -> String {
|
||||
let loc = match self.core().loc {
|
||||
Location::Range {
|
||||
ln_begin, ln_end, ..
|
||||
} if ln_begin == ln_end => format!(", line {ln_begin}"),
|
||||
Location::Range {
|
||||
ln_begin, ln_end, ..
|
||||
}
|
||||
| Location::LineRange(ln_begin, ln_end) => format!(", line {ln_begin}..{ln_end}"),
|
||||
Location::Line(lineno) => format!(", line {lineno}"),
|
||||
Location::Unknown => "".to_string(),
|
||||
};
|
||||
let caused_by = if self.caused_by() != "" {
|
||||
format!(", in {}", self.caused_by())
|
||||
} else {
|
||||
"".to_string()
|
||||
};
|
||||
format!(
|
||||
"{kind}: File {input}{loc}{caused_by}\n",
|
||||
input = self.input().enclosed_name(),
|
||||
)
|
||||
}
|
||||
|
||||
fn format_code_and_pointer(
|
||||
&self,
|
||||
err_color: Color,
|
||||
gutter_color: Color,
|
||||
mark: char,
|
||||
chars: &Characters,
|
||||
) -> String {
|
||||
match self.core().loc {
|
||||
Location::Range {
|
||||
ln_begin,
|
||||
col_begin,
|
||||
ln_end,
|
||||
col_end,
|
||||
} => format_context(
|
||||
self,
|
||||
ln_begin,
|
||||
ln_end,
|
||||
col_begin,
|
||||
col_end,
|
||||
err_color,
|
||||
gutter_color,
|
||||
chars,
|
||||
mark,
|
||||
),
|
||||
Location::LineRange(ln_begin, ln_end) => {
|
||||
let (_, vbar) = chars.gutters();
|
||||
let mut cxt = StyledStrings::default();
|
||||
let codes = if self.input().is_repl() {
|
||||
vec![self.input().reread()]
|
||||
} else {
|
||||
self.input().reread_lines(ln_begin, ln_end)
|
||||
};
|
||||
let mark = mark.to_string();
|
||||
for (i, lineno) in (ln_begin..=ln_end).enumerate() {
|
||||
cxt.push_str_with_color(&format!("{lineno} {}", vbar), err_color);
|
||||
cxt.push_str(&codes[i]);
|
||||
cxt.push_str("\n");
|
||||
cxt.push_str(&" ".repeat(lineno.to_string().len() + 3)); // +3 means ` | `
|
||||
cxt.push_str_with_color(
|
||||
&mark.repeat(cmp::max(1, codes[i].len())),
|
||||
gutter_color,
|
||||
);
|
||||
cxt.push_str("\n");
|
||||
}
|
||||
cxt.to_string()
|
||||
}
|
||||
Location::Line(lineno) => {
|
||||
let (_, vbar) = chars.gutters();
|
||||
let code = if self.input().is_repl() {
|
||||
self.input().reread()
|
||||
} else {
|
||||
self.input().reread_lines(lineno, lineno).remove(0)
|
||||
};
|
||||
let mut cxt = StyledStrings::default();
|
||||
cxt.push_str_with_color(&format!(" {lineno} {} ", vbar), gutter_color);
|
||||
cxt.push_str(&code);
|
||||
cxt.push_str("\n");
|
||||
cxt.to_string()
|
||||
}
|
||||
Location::Unknown => match self.input() {
|
||||
Input::File(_) => "\n".to_string(),
|
||||
|
||||
other => {
|
||||
let (_, vbar) = chars.gutters();
|
||||
let mut cxt = StyledStrings::default();
|
||||
cxt.push_str_with_color(&format!(" ? {}", vbar), gutter_color);
|
||||
cxt.push_str(&other.reread());
|
||||
cxt.to_string()
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue