Add hint when string is used in place of label (#4330)

This commit is contained in:
+merlan #flirora 2024-06-11 05:08:30 -04:00 committed by GitHub
parent 7fa86eed0e
commit 20475ab0bf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 50 additions and 16 deletions

View File

@ -408,7 +408,7 @@ impl Lexer<'_> {
} }
fn ref_marker(&mut self) -> SyntaxKind { fn ref_marker(&mut self) -> SyntaxKind {
self.s.eat_while(|c| is_id_continue(c) || matches!(c, ':' | '.')); self.s.eat_while(is_valid_in_label_literal);
// Don't include the trailing characters likely to be part of text. // Don't include the trailing characters likely to be part of text.
while matches!(self.s.scout(-1), Some('.' | ':')) { while matches!(self.s.scout(-1), Some('.' | ':')) {
@ -419,7 +419,7 @@ impl Lexer<'_> {
} }
fn label(&mut self) -> SyntaxKind { fn label(&mut self) -> SyntaxKind {
let label = self.s.eat_while(|c| is_id_continue(c) || matches!(c, ':' | '.')); let label = self.s.eat_while(is_valid_in_label_literal);
if label.is_empty() { if label.is_empty() {
return self.error("label cannot be empty"); return self.error("label cannot be empty");
} }
@ -918,3 +918,14 @@ fn is_math_id_start(c: char) -> bool {
fn is_math_id_continue(c: char) -> bool { fn is_math_id_continue(c: char) -> bool {
is_xid_continue(c) && c != '_' is_xid_continue(c) && c != '_'
} }
/// Whether a character can be part of a label literal's name.
#[inline]
fn is_valid_in_label_literal(c: char) -> bool {
is_id_continue(c) || matches!(c, ':' | '.')
}
/// Returns true if this string is valid in a label literal.
pub fn is_valid_label_literal_id(id: &str) -> bool {
!id.is_empty() && id.chars().all(is_valid_in_label_literal)
}

View File

@ -19,7 +19,8 @@ pub use self::file::FileId;
pub use self::highlight::{highlight, highlight_html, Tag}; pub use self::highlight::{highlight, highlight_html, Tag};
pub use self::kind::SyntaxKind; pub use self::kind::SyntaxKind;
pub use self::lexer::{ pub use self::lexer::{
is_id_continue, is_id_start, is_ident, is_newline, link_prefix, split_newlines, is_id_continue, is_id_start, is_ident, is_newline, is_valid_label_literal_id,
link_prefix, split_newlines,
}; };
pub use self::node::{LinkedChildren, LinkedNode, Side, SyntaxError, SyntaxNode}; pub use self::node::{LinkedChildren, LinkedNode, Side, SyntaxError, SyntaxNode};
pub use self::parser::{parse, parse_code, parse_math}; pub use self::parser::{parse, parse_code, parse_math};

View File

@ -3,7 +3,7 @@ use std::fmt::Write;
use std::hash::Hash; use std::hash::Hash;
use std::ops::Add; use std::ops::Add;
use ecow::{eco_format, EcoString}; use ecow::eco_format;
use smallvec::SmallVec; use smallvec::SmallVec;
use unicode_math_class::MathClass; use unicode_math_class::MathClass;
@ -42,7 +42,7 @@ pub trait Reflect {
/// dynamic checks instead of optimized machine code for each type). /// dynamic checks instead of optimized machine code for each type).
fn castable(value: &Value) -> bool; fn castable(value: &Value) -> bool;
/// Produce an error message for an inacceptable value type. /// Produce an error message for an unacceptable value type.
/// ///
/// ```ignore /// ```ignore
/// assert_eq!( /// assert_eq!(
@ -51,7 +51,7 @@ pub trait Reflect {
/// ); /// );
/// ``` /// ```
fn error(found: &Value) -> HintedString { fn error(found: &Value) -> HintedString {
Self::input().error(found).into() Self::input().error(found)
} }
} }
@ -300,7 +300,7 @@ pub enum CastInfo {
impl CastInfo { impl CastInfo {
/// Produce an error message describing what was expected and what was /// Produce an error message describing what was expected and what was
/// found. /// found.
pub fn error(&self, found: &Value) -> EcoString { pub fn error(&self, found: &Value) -> HintedString {
let mut matching_type = false; let mut matching_type = false;
let mut parts = vec![]; let mut parts = vec![];
@ -328,13 +328,26 @@ impl CastInfo {
write!(msg, "{}", found.ty()).unwrap(); write!(msg, "{}", found.ty()).unwrap();
} }
let mut msg: HintedString = msg.into();
if let Value::Int(i) = found { if let Value::Int(i) = found {
if parts.iter().any(|p| p == "length") && !matching_type { if !matching_type && parts.iter().any(|p| p == "length") {
write!(msg, ": a length needs a unit - did you mean {i}pt?").unwrap(); msg.hint(eco_format!("a length needs a unit - did you mean {i}pt?"));
}
} else if let Value::Str(s) = found {
if !matching_type && parts.iter().any(|p| p == "label") {
if typst_syntax::is_valid_label_literal_id(s) {
msg.hint(eco_format!(
"use `<{s}>` or `label({})` to create a label",
s.repr()
));
} else {
msg.hint(eco_format!("use `label({})` to create a label", s.repr()));
}
} }
} }
msg.into() msg
} }
/// Walk all contained non-union infos. /// Walk all contained non-union infos.

View File

@ -610,11 +610,7 @@ macro_rules! primitive {
match value { match value {
Value::$variant(v) => Ok(v), Value::$variant(v) => Ok(v),
$(Value::$other$(($binding))? => Ok($out),)* $(Value::$other$(($binding))? => Ok($out),)*
v => Err(eco_format!( v => Err(<Self as Reflect>::error(&v)),
"expected {}, found {}",
Type::of::<Self>(),
v.ty(),
).into()),
} }
} }
} }

View File

@ -45,7 +45,8 @@
} }
--- length-unit-hint --- --- length-unit-hint ---
// Error: 1:17-1:19 expected length, found integer: a length needs a unit - did you mean 12pt? // Error: 17-19 expected length, found integer
// Hint: 17-19 a length needs a unit - did you mean 12pt?
#set text(size: 12) #set text(size: 12)
--- length-ignore-em-pt-hint --- --- length-ignore-em-pt-hint ---

View File

@ -114,3 +114,15 @@ B #cite(<netwok>) #cite(<arrgh>).
@mcintosh_anxiety @mcintosh_anxiety
#show bibliography: none #show bibliography: none
#bibliography("/assets/bib/works.bib", style: "chicago-author-date") #bibliography("/assets/bib/works.bib", style: "chicago-author-date")
--- cite-type-error-hint ---
// Test hint for cast error from str to label
// Error: 7-15 expected label, found string
// Hint: 7-15 use `<netwok>` or `label("netwok")` to create a label
#cite("netwok")
--- cite-type-error-hint-invalid-literal ---
// Test hint for cast error from str to label
// Error: 7-17 expected label, found string
// Hint: 7-17 use `label("%@&#*!\\")` to create a label
#cite("%@&#*!\\")