mirror of
https://github.com/typst/typst
synced 2025-05-13 12:36:23 +08:00
Better analysis for literals
This commit is contained in:
parent
76048a8ef4
commit
1e97d5c8cb
@ -131,6 +131,6 @@ pub trait Numeric:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Round a float to two decimal places.
|
/// Round a float to two decimal places.
|
||||||
fn round_2(value: f64) -> f64 {
|
pub fn round_2(value: f64) -> f64 {
|
||||||
(value * 100.0).round() / 100.0
|
(value * 100.0).round() / 100.0
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,22 @@ use crate::World;
|
|||||||
/// Try to determine a set of possible values for an expression.
|
/// Try to determine a set of possible values for an expression.
|
||||||
pub fn analyze(world: &(dyn World + 'static), node: &LinkedNode) -> Vec<Value> {
|
pub fn analyze(world: &(dyn World + 'static), node: &LinkedNode) -> Vec<Value> {
|
||||||
match node.cast::<ast::Expr>() {
|
match node.cast::<ast::Expr>() {
|
||||||
|
Some(ast::Expr::None(_)) => vec![Value::None],
|
||||||
|
Some(ast::Expr::Auto(_)) => vec![Value::Auto],
|
||||||
|
Some(ast::Expr::Bool(v)) => vec![Value::Bool(v.get())],
|
||||||
|
Some(ast::Expr::Int(v)) => vec![Value::Int(v.get())],
|
||||||
|
Some(ast::Expr::Float(v)) => vec![Value::Float(v.get())],
|
||||||
|
Some(ast::Expr::Numeric(v)) => vec![Value::numeric(v.get())],
|
||||||
|
Some(ast::Expr::Str(v)) => vec![Value::Str(v.get().into())],
|
||||||
|
|
||||||
|
Some(ast::Expr::FieldAccess(access)) => {
|
||||||
|
let Some(child) = node.children().next() else { return vec![] };
|
||||||
|
analyze(world, &child)
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|target| target.field(&access.field()).ok())
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
Some(ast::Expr::Ident(_) | ast::Expr::MathIdent(_) | ast::Expr::FuncCall(_)) => {
|
Some(ast::Expr::Ident(_) | ast::Expr::MathIdent(_) | ast::Expr::FuncCall(_)) => {
|
||||||
if let Some(parent) = node.parent() {
|
if let Some(parent) = node.parent() {
|
||||||
if parent.kind() == SyntaxKind::FieldAccess && node.index() > 0 {
|
if parent.kind() == SyntaxKind::FieldAccess && node.index() > 0 {
|
||||||
@ -19,22 +35,9 @@ pub fn analyze(world: &(dyn World + 'static), node: &LinkedNode) -> Vec<Value> {
|
|||||||
let route = Route::default();
|
let route = Route::default();
|
||||||
let mut tracer = Tracer::new(Some(span));
|
let mut tracer = Tracer::new(Some(span));
|
||||||
eval(world.track(), route.track(), tracer.track_mut(), source).ok();
|
eval(world.track(), route.track(), tracer.track_mut(), source).ok();
|
||||||
return tracer.finish();
|
tracer.finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(ast::Expr::Str(s)) => return vec![Value::Str(s.get().into())],
|
_ => vec![],
|
||||||
|
|
||||||
Some(ast::Expr::FieldAccess(access)) => {
|
|
||||||
if let Some(child) = node.children().next() {
|
|
||||||
return analyze(world, &child)
|
|
||||||
.into_iter()
|
|
||||||
.filter_map(|target| target.field(&access.field()).ok())
|
|
||||||
.collect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
vec![]
|
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ use if_chain::if_chain;
|
|||||||
use unicode_segmentation::UnicodeSegmentation;
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
|
|
||||||
use super::{analyze, plain_docs_sentence, summarize_font_family};
|
use super::{analyze, plain_docs_sentence, summarize_font_family};
|
||||||
|
use crate::geom::{round_2, Length, Numeric};
|
||||||
use crate::model::{CastInfo, Tracer, Value};
|
use crate::model::{CastInfo, Tracer, Value};
|
||||||
use crate::syntax::ast;
|
use crate::syntax::ast;
|
||||||
use crate::syntax::{LinkedNode, Source, SyntaxKind};
|
use crate::syntax::{LinkedNode, Source, SyntaxKind};
|
||||||
@ -22,15 +23,23 @@ pub fn tooltip(
|
|||||||
|
|
||||||
/// Tooltip for a hovered expression.
|
/// Tooltip for a hovered expression.
|
||||||
fn expr_tooltip(world: &(dyn World + 'static), leaf: &LinkedNode) -> Option<String> {
|
fn expr_tooltip(world: &(dyn World + 'static), leaf: &LinkedNode) -> Option<String> {
|
||||||
if !leaf.is::<ast::Expr>() {
|
let expr = leaf.cast::<ast::Expr>()?;
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let values = analyze(world, leaf);
|
let values = analyze(world, leaf);
|
||||||
|
|
||||||
if let [value] = values.as_slice() {
|
if let [value] = values.as_slice() {
|
||||||
if let Some(docs) = value.docs() {
|
if let Some(docs) = value.docs() {
|
||||||
return Some(plain_docs_sentence(docs));
|
return Some(plain_docs_sentence(docs));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let &Value::Length(length) = value {
|
||||||
|
if let Some(tooltip) = length_tooltip(length) {
|
||||||
|
return Some(tooltip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if expr.is_literal() {
|
||||||
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut tooltip = String::new();
|
let mut tooltip = String::new();
|
||||||
@ -61,6 +70,19 @@ fn expr_tooltip(world: &(dyn World + 'static), leaf: &LinkedNode) -> Option<Stri
|
|||||||
(!tooltip.is_empty()).then(|| tooltip)
|
(!tooltip.is_empty()).then(|| tooltip)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Tooltip text for a hovered length.
|
||||||
|
fn length_tooltip(length: Length) -> Option<String> {
|
||||||
|
length.em.is_zero().then(|| {
|
||||||
|
format!(
|
||||||
|
"{}pt = {}mm = {}cm = {}in",
|
||||||
|
round_2(length.abs.to_pt()),
|
||||||
|
round_2(length.abs.to_mm()),
|
||||||
|
round_2(length.abs.to_cm()),
|
||||||
|
round_2(length.abs.to_inches())
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Tooltips for components of a named parameter.
|
/// Tooltips for components of a named parameter.
|
||||||
fn named_param_tooltip(
|
fn named_param_tooltip(
|
||||||
world: &(dyn World + 'static),
|
world: &(dyn World + 'static),
|
||||||
|
@ -15,7 +15,6 @@ use super::{
|
|||||||
use crate::diag::{
|
use crate::diag::{
|
||||||
bail, error, At, SourceError, SourceResult, StrResult, Trace, Tracepoint,
|
bail, error, At, SourceError, SourceResult, StrResult, Trace, Tracepoint,
|
||||||
};
|
};
|
||||||
use crate::geom::{Abs, Angle, Em, Fr, Ratio};
|
|
||||||
use crate::syntax::ast::AstNode;
|
use crate::syntax::ast::AstNode;
|
||||||
use crate::syntax::{ast, Source, SourceId, Span, Spanned, SyntaxKind, SyntaxNode};
|
use crate::syntax::{ast, Source, SourceId, Span, Spanned, SyntaxKind, SyntaxNode};
|
||||||
use crate::util::PathExt;
|
use crate::util::PathExt;
|
||||||
@ -660,14 +659,7 @@ impl Eval for ast::Numeric {
|
|||||||
type Output = Value;
|
type Output = Value;
|
||||||
|
|
||||||
fn eval(&self, _: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(&self, _: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
let (v, unit) = self.get();
|
Ok(Value::numeric(self.get()))
|
||||||
Ok(match unit {
|
|
||||||
ast::Unit::Length(unit) => Abs::with_unit(v, unit).into(),
|
|
||||||
ast::Unit::Angle(unit) => Angle::with_unit(v, unit).into(),
|
|
||||||
ast::Unit::Em => Em::new(v).into(),
|
|
||||||
ast::Unit::Fr => Fr::new(v).into(),
|
|
||||||
ast::Unit::Percent => Ratio::new(v / 100.0).into(),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ use super::{
|
|||||||
};
|
};
|
||||||
use crate::diag::StrResult;
|
use crate::diag::StrResult;
|
||||||
use crate::geom::{Abs, Angle, Color, Em, Fr, Length, Ratio, Rel, RgbaColor};
|
use crate::geom::{Abs, Angle, Color, Em, Fr, Length, Ratio, Rel, RgbaColor};
|
||||||
use crate::syntax::Span;
|
use crate::syntax::{ast, Span};
|
||||||
use crate::util::{format_eco, EcoString};
|
use crate::util::{format_eco, EcoString};
|
||||||
|
|
||||||
/// A computational value.
|
/// A computational value.
|
||||||
@ -71,6 +71,18 @@ impl Value {
|
|||||||
Self::Dyn(Dynamic::new(any))
|
Self::Dyn(Dynamic::new(any))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a numeric value from a number with a unit.
|
||||||
|
pub fn numeric(pair: (f64, ast::Unit)) -> Self {
|
||||||
|
let (v, unit) = pair;
|
||||||
|
match unit {
|
||||||
|
ast::Unit::Length(unit) => Abs::with_unit(v, unit).into(),
|
||||||
|
ast::Unit::Angle(unit) => Angle::with_unit(v, unit).into(),
|
||||||
|
ast::Unit::Em => Em::new(v).into(),
|
||||||
|
ast::Unit::Fr => Fr::new(v).into(),
|
||||||
|
ast::Unit::Percent => Ratio::new(v / 100.0).into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The name of the stored value's type.
|
/// The name of the stored value's type.
|
||||||
pub fn type_name(&self) -> &'static str {
|
pub fn type_name(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
|
@ -345,6 +345,20 @@ impl Expr {
|
|||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Is this a literal?
|
||||||
|
pub fn is_literal(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::None(_) => true,
|
||||||
|
Self::Auto(_) => true,
|
||||||
|
Self::Bool(_) => true,
|
||||||
|
Self::Int(_) => true,
|
||||||
|
Self::Float(_) => true,
|
||||||
|
Self::Numeric(_) => true,
|
||||||
|
Self::Str(_) => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Expr {
|
impl Default for Expr {
|
||||||
|
@ -499,7 +499,11 @@ impl Lexer<'_> {
|
|||||||
|
|
||||||
// Read the fractional part if not already done.
|
// Read the fractional part if not already done.
|
||||||
// Make sure not to confuse a range for the decimal separator.
|
// Make sure not to confuse a range for the decimal separator.
|
||||||
if c != '.' && !self.s.at("..") && self.s.eat_if('.') {
|
if c != '.'
|
||||||
|
&& !self.s.at("..")
|
||||||
|
&& !self.s.scout(1).map_or(false, is_id_start)
|
||||||
|
&& self.s.eat_if('.')
|
||||||
|
{
|
||||||
self.s.eat_while(char::is_ascii_digit);
|
self.s.eat_while(char::is_ascii_digit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user