Refine types display.

This commit is contained in:
Stephen Chung 2022-01-17 21:51:04 +08:00
parent 58e1094b79
commit 86fc2f7bf1
8 changed files with 162 additions and 52 deletions

View File

@ -60,12 +60,12 @@ impl Engine {
#[cfg(feature = "metadata")]
let mut param_type_names: crate::StaticVec<_> = F::param_names()
.iter()
.map(|ty| format!("_: {}", self.map_type_name(ty)))
.map(|ty| format!("_: {}", self.format_type_name(ty)))
.collect();
#[cfg(feature = "metadata")]
if F::return_type() != TypeId::of::<()>() {
param_type_names.push(self.map_type_name(F::return_type_name()).into());
param_type_names.push(self.format_type_name(F::return_type_name()).into());
}
#[cfg(feature = "metadata")]
@ -122,9 +122,9 @@ impl Engine {
#[cfg(feature = "metadata")]
let param_type_names: crate::StaticVec<_> = F::param_names()
.iter()
.map(|ty| format!("_: {}", self.map_type_name(ty)))
.map(|ty| format!("_: {}", self.format_type_name(ty)))
.chain(std::iter::once(
self.map_type_name(F::return_type_name()).into(),
self.format_type_name(F::return_type_name()).into(),
))
.collect();

View File

@ -329,13 +329,47 @@ impl Engine {
///
/// If a type is registered via [`register_type_with_name`][Engine::register_type_with_name],
/// the type name provided for the registration will be used.
///
/// # Panics
///
/// Panics if the type name is `&mut`.
#[inline]
#[must_use]
pub fn map_type_name<'a>(&'a self, name: &'a str) -> &'a str {
self.type_names
.get(name)
.map(|s| s.as_str())
.unwrap_or_else(|| map_std_type_name(name))
.unwrap_or_else(|| map_std_type_name(name, true))
}
/// Format a type name.
///
/// If a type is registered via [`register_type_with_name`][Engine::register_type_with_name],
/// the type name provided for the registration will be used.
#[cfg(feature = "metadata")]
#[inline]
#[must_use]
pub(crate) fn format_type_name<'a>(&'a self, name: &'a str) -> std::borrow::Cow<'a, str> {
if name.starts_with("&mut ") {
let x = &name[5..];
let r = self.format_type_name(x);
return if x != r {
format!("&mut {}", r).into()
} else {
name.into()
};
}
self.type_names
.get(name)
.map(|s| s.as_str())
.unwrap_or_else(|| match name {
"INT" => return type_name::<crate::INT>(),
#[cfg(not(feature = "no_float"))]
"FLOAT" => return type_name::<crate::FLOAT>(),
_ => map_std_type_name(name, false),
})
.into()
}
/// Make a `Box<`[`EvalAltResult<ErrorMismatchDataType>`][ERR::ErrorMismatchDataType]`>`.

View File

@ -968,18 +968,11 @@ impl Engine {
_ if use_indexers => {
let args = &mut [target, &mut idx];
let hash_get = crate::ast::FnCallHashes::from_native(global.hash_idx_get());
let fn_name = crate::engine::FN_IDX_GET;
let pos = Position::NONE;
self.exec_fn_call(
global,
state,
lib,
crate::engine::FN_IDX_GET,
hash_get,
args,
true,
true,
Position::NONE,
None,
level,
global, state, lib, fn_name, hash_get, args, true, true, pos, None, level,
)
.map(|(v, _)| v.into())
}

View File

@ -107,8 +107,8 @@ macro_rules! def_register {
// ^ function ABI type
// ^ function parameter generic type name (A, B, C etc.)
// ^ call argument(like A, *B, &mut C etc)
// ^ function parameter marker type (T, Ref<T> or Mut<T>)
// ^ function parameter actual type (T, &T or &mut T)
// ^ function parameter marker type (A, Ref<B> or Mut<C>)
// ^ function parameter actual type (A, &B or &mut C)
// ^ argument let statement
impl<
@ -117,7 +117,7 @@ macro_rules! def_register {
RET: Variant + Clone
> RegisterNativeFunction<($($mark,)*), ()> for FN {
#[inline(always)] fn param_types() -> Box<[TypeId]> { vec![$(TypeId::of::<$par>()),*].into_boxed_slice() }
#[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> Box<[&'static str]> { vec![$(std::any::type_name::<$par>()),*].into_boxed_slice() }
#[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> Box<[&'static str]> { vec![$(std::any::type_name::<$param>()),*].into_boxed_slice() }
#[cfg(feature = "metadata")] #[inline(always)] fn return_type() -> TypeId { TypeId::of::<RET>() }
#[cfg(feature = "metadata")] #[inline(always)] fn return_type_name() -> &'static str { std::any::type_name::<RET>() }
#[inline(always)] fn into_callable_function(self) -> CallableFunction {
@ -145,7 +145,7 @@ macro_rules! def_register {
RET: Variant + Clone
> RegisterNativeFunction<(NativeCallContext<'static>, $($mark,)*), ()> for FN {
#[inline(always)] fn param_types() -> Box<[TypeId]> { vec![$(TypeId::of::<$par>()),*].into_boxed_slice() }
#[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> Box<[&'static str]> { vec![$(std::any::type_name::<$par>()),*].into_boxed_slice() }
#[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> Box<[&'static str]> { vec![$(std::any::type_name::<$param>()),*].into_boxed_slice() }
#[cfg(feature = "metadata")] #[inline(always)] fn return_type() -> TypeId { TypeId::of::<RET>() }
#[cfg(feature = "metadata")] #[inline(always)] fn return_type_name() -> &'static str { std::any::type_name::<RET>() }
#[inline(always)] fn into_callable_function(self) -> CallableFunction {
@ -173,7 +173,7 @@ macro_rules! def_register {
RET: Variant + Clone
> RegisterNativeFunction<($($mark,)*), RhaiResultOf<RET>> for FN {
#[inline(always)] fn param_types() -> Box<[TypeId]> { vec![$(TypeId::of::<$par>()),*].into_boxed_slice() }
#[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> Box<[&'static str]> { vec![$(std::any::type_name::<$par>()),*].into_boxed_slice() }
#[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> Box<[&'static str]> { vec![$(std::any::type_name::<$param>()),*].into_boxed_slice() }
#[cfg(feature = "metadata")] #[inline(always)] fn return_type() -> TypeId { TypeId::of::<RhaiResultOf<RET>>() }
#[cfg(feature = "metadata")] #[inline(always)] fn return_type_name() -> &'static str { std::any::type_name::<RhaiResultOf<RET>>() }
#[inline(always)] fn into_callable_function(self) -> CallableFunction {
@ -198,7 +198,7 @@ macro_rules! def_register {
RET: Variant + Clone
> RegisterNativeFunction<(NativeCallContext<'static>, $($mark,)*), RhaiResultOf<RET>> for FN {
#[inline(always)] fn param_types() -> Box<[TypeId]> { vec![$(TypeId::of::<$par>()),*].into_boxed_slice() }
#[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> Box<[&'static str]> { vec![$(std::any::type_name::<$par>()),*].into_boxed_slice() }
#[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> Box<[&'static str]> { vec![$(std::any::type_name::<$param>()),*].into_boxed_slice() }
#[cfg(feature = "metadata")] #[inline(always)] fn return_type() -> TypeId { TypeId::of::<RhaiResultOf<RET>>() }
#[cfg(feature = "metadata")] #[inline(always)] fn return_type_name() -> &'static str { std::any::type_name::<RhaiResultOf<RET>>() }
#[inline(always)] fn into_callable_function(self) -> CallableFunction {

View File

@ -92,18 +92,60 @@ impl FuncInfo {
/// `()` is cleared.
/// [`RhaiResult`][crate::RhaiResult] and [`RhaiResultOf<T>`] are expanded.
#[cfg(feature = "metadata")]
pub fn format_return_type(typ: &str) -> std::borrow::Cow<str> {
pub fn format_type(typ: &str, is_return_type: bool) -> std::borrow::Cow<str> {
const RHAI_RESULT_TYPE: &str = "RhaiResult";
const RHAI_RESULT_TYPE_EXPAND: &str = "Result<Dynamic, Box<EvalAltResult>>";
const RHAI_RESULT_OF_TYPE: &str = "RhaiResultOf<";
const RHAI_RESULT_OF_TYPE_EXPAND: &str = "Result<{}, Box<EvalAltResult>>";
const RHAI_RANGE: &str = "ExclusiveRange";
const RHAI_RANGE_TYPE: &str = "Range<";
const RHAI_RANGE_EXPAND: &str = "Range<{}>";
const RHAI_INCLUSIVE_RANGE: &str = "InclusiveRange";
const RHAI_INCLUSIVE_RANGE_TYPE: &str = "RangeInclusive<";
const RHAI_INCLUSIVE_RANGE_EXPAND: &str = "RangeInclusive<{}>";
let typ = typ.trim();
if typ.starts_with("rhai::") {
return Self::format_type(&typ[6..], is_return_type);
} else if typ.starts_with("&mut ") {
let x = &typ[5..];
let r = Self::format_type(x, false);
return if r == x {
typ.into()
} else {
format!("&mut {}", r).into()
};
}
match typ {
"" | "()" => "".into(),
"" | "()" if is_return_type => "".into(),
"INT" => std::any::type_name::<crate::INT>().into(),
#[cfg(not(feature = "no_float"))]
"FLOAT" => std::any::type_name::<crate::FLOAT>().into(),
RHAI_RANGE => RHAI_RANGE_EXPAND
.replace("{}", std::any::type_name::<crate::INT>())
.into(),
RHAI_INCLUSIVE_RANGE => RHAI_INCLUSIVE_RANGE_EXPAND
.replace("{}", std::any::type_name::<crate::INT>())
.into(),
RHAI_RESULT_TYPE => RHAI_RESULT_TYPE_EXPAND.into(),
ty if ty.starts_with(RHAI_RANGE_TYPE) && ty.ends_with('>') => {
let inner = &ty[RHAI_RANGE_TYPE.len()..ty.len() - 1];
RHAI_RANGE_EXPAND
.replace("{}", Self::format_type(inner, false).trim())
.into()
}
ty if ty.starts_with(RHAI_INCLUSIVE_RANGE_TYPE) && ty.ends_with('>') => {
let inner = &ty[RHAI_INCLUSIVE_RANGE_TYPE.len()..ty.len() - 1];
RHAI_INCLUSIVE_RANGE_EXPAND
.replace("{}", Self::format_type(inner, false).trim())
.into()
}
ty if ty.starts_with(RHAI_RESULT_OF_TYPE) && ty.ends_with('>') => {
let inner = &ty[RHAI_RESULT_OF_TYPE.len()..ty.len() - 1];
RHAI_RESULT_OF_TYPE_EXPAND
.replace("{}", ty[RHAI_RESULT_OF_TYPE.len()..ty.len() - 1].trim())
.replace("{}", Self::format_type(inner, false).trim())
.into()
}
ty => ty.into(),
@ -116,14 +158,27 @@ impl FuncInfo {
pub fn gen_signature(&self) -> String {
let mut sig = format!("{}(", self.metadata.name);
let return_type = Self::format_return_type(&self.metadata.return_type);
let return_type = Self::format_type(&self.metadata.return_type, true);
if !self.metadata.params_info.is_empty() {
let params: StaticVec<_> = self
.metadata
.params_info
.iter()
.map(|s| s.as_str())
.map(|s| {
let mut seg = s.splitn(2, ':');
let name = match seg.next().unwrap().trim() {
"" => "_",
s => s,
};
let result: std::borrow::Cow<str> = match seg.next() {
Some(typ) => {
format!("{}: {}", name, FuncInfo::format_type(typ, false)).into()
}
None => name.into(),
};
result
})
.collect();
sig.push_str(&params.join(", "));
sig.push(')');

View File

@ -481,6 +481,12 @@ def_package! {
// Register string iterator
lib.set_iterator::<CharsStream>();
#[cfg(feature = "metadata")]
let (range_type, range_inclusive_type) = (
format!("range: Range<{}>", std::any::type_name::<INT>()),
format!("range: RangeInclusive<{}>", std::any::type_name::<INT>()),
);
let _hash = lib.set_native_fn("chars", |string, range: ExclusiveRange| {
let from = INT::max(range.start, 0);
let to = INT::max(range.end, from);
@ -489,7 +495,7 @@ def_package! {
#[cfg(feature = "metadata")]
lib.update_fn_metadata_with_comments(
_hash,
["string: &str", "range: Range<INT>", "Iterator<Item=char>"],
["string: &str", &range_type, "Iterator<Item=char>"],
[
"/// Return an iterator over an exclusive range of characters in the string.",
"///",
@ -511,7 +517,7 @@ def_package! {
#[cfg(feature = "metadata")]
lib.update_fn_metadata_with_comments(
_hash,
["string: &str", "range: RangeInclusive<INT>", "Iterator<Item=char>"],
["string: &str", &range_inclusive_type, "Iterator<Item=char>"],
[
"/// Return an iterator over an inclusive range of characters in the string.",
"///",
@ -621,7 +627,7 @@ def_package! {
#[cfg(feature = "metadata")]
lib.update_fn_metadata_with_comments(
_hash,
["value: INT", "range: Range<INT>", "Iterator<Item=bool>"],
["value: INT", &range_type, "Iterator<Item=bool>"],
[
"/// Return an iterator over an exclusive range of bits in the number.",
"///",
@ -645,7 +651,7 @@ def_package! {
#[cfg(feature = "metadata")]
lib.update_fn_metadata_with_comments(
_hash,
["value: INT", "range: RangeInclusive<INT>", "Iterator<Item=bool>"],
["value: INT", &range_inclusive_type, "Iterator<Item=bool>"],
[
"/// Return an iterator over an inclusive range of bits in the number.",
"///",
@ -733,7 +739,7 @@ def_package! {
#[cfg(feature = "metadata")]
lib.update_fn_metadata_with_comments(
_hash,
["value: &mut INT", "range: Range<INT>", "Iterator<Item=bool>"],
["value: &mut INT", "Iterator<Item=bool>"],
[
"/// Return an iterator over all the bits in the number.",
"///",

View File

@ -7,7 +7,7 @@ use crate::{calc_fn_hash, Engine, AST};
use serde::{Deserialize, Serialize};
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
use std::{cmp::Ordering, collections::BTreeMap, iter::empty};
use std::{borrow::Cow, cmp::Ordering, collections::BTreeMap, iter::empty};
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
@ -54,7 +54,7 @@ struct FnParam<'a> {
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<&'a str>,
#[serde(rename = "type", skip_serializing_if = "Option::is_none")]
pub typ: Option<&'a str>,
pub typ: Option<Cow<'a, str>>,
}
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
@ -125,12 +125,12 @@ impl<'a> From<&'a FuncInfo> for FnMetadata<'a> {
"_" => None,
s => Some(s),
};
let typ = seg.next().map(&str::trim);
let typ = seg.next().map(|s| FuncInfo::format_type(s, false));
FnParam { name, typ }
})
.collect(),
_dummy: None,
return_type: FuncInfo::format_return_type(&info.metadata.return_type).into_owned(),
return_type: FuncInfo::format_type(&info.metadata.return_type, true).into_owned(),
signature: info.gen_signature(),
doc_comments: if info.func.is_script() {
#[cfg(feature = "no_function")]

View File

@ -546,44 +546,66 @@ impl Hash for Dynamic {
/// Map the name of a standard type into a friendly form.
#[inline]
#[must_use]
pub(crate) fn map_std_type_name(name: &str) -> &str {
pub(crate) fn map_std_type_name(name: &str, shorthands: bool) -> &str {
let name = name.trim();
if name.starts_with("rhai::") {
return map_std_type_name(&name[6..], shorthands);
}
if name == type_name::<String>() {
return "string";
return if shorthands { "string" } else { "String" };
}
if name == type_name::<ImmutableString>() {
return "string";
return if shorthands {
"string"
} else {
"ImmutableString"
};
}
if name == type_name::<&str>() {
return "string";
}
if name == type_name::<FnPtr>() {
return "Fn";
return if shorthands { "string" } else { "&str" };
}
#[cfg(feature = "decimal")]
if name == type_name::<rust_decimal::Decimal>() {
return "decimal";
return if shorthands { "decimal" } else { "Decimal" };
}
if name == type_name::<FnPtr>() {
return if shorthands { "Fn" } else { "FnPtr" };
}
#[cfg(not(feature = "no_index"))]
if name == type_name::<crate::Array>() {
return "array";
return if shorthands { "array" } else { "Array" };
}
#[cfg(not(feature = "no_index"))]
if name == type_name::<crate::Blob>() {
return "blob";
return if shorthands { "blob" } else { "Blob" };
}
#[cfg(not(feature = "no_object"))]
if name == type_name::<crate::Map>() {
return "map";
return if shorthands { "map" } else { "Map" };
}
#[cfg(not(feature = "no_std"))]
if name == type_name::<Instant>() {
return "timestamp";
return if shorthands { "timestamp" } else { "Instant" };
}
if name == type_name::<ExclusiveRange>() {
return "range";
if name == type_name::<ExclusiveRange>() || name == "ExclusiveRange" {
return if shorthands {
"range"
} else if cfg!(feature = "only_i32") {
"Range<i32>"
} else {
"Range<i64>"
};
}
if name == type_name::<InclusiveRange>() {
return "range=";
if name == type_name::<InclusiveRange>() || name == "InclusiveRange" {
return if shorthands {
"range="
} else if cfg!(feature = "only_i32") {
"RangeInclusive<i32>"
} else {
"RangeInclusive<i64>"
};
}
name