summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2023-01-28 23:36:27 +0100
committerLaurenz <laurmaedje@gmail.com>2023-01-28 23:38:03 +0100
commit1e97d5c8cbeb96d35e5a34a8340c4ec1860fa1b6 (patch)
treeaa4a341af10dc0729132a42cdb1cacb1e1d21518 /src
parent76048a8ef45ac5892235f2e69cb7cb6c35a037e4 (diff)
Better analysis for literals
Diffstat (limited to 'src')
-rw-r--r--src/geom/mod.rs2
-rw-r--r--src/ide/analyze.rs33
-rw-r--r--src/ide/tooltip.rs30
-rw-r--r--src/model/eval.rs10
-rw-r--r--src/model/value.rs14
-rw-r--r--src/syntax/ast.rs14
-rw-r--r--src/syntax/lexer.rs6
7 files changed, 78 insertions, 31 deletions
diff --git a/src/geom/mod.rs b/src/geom/mod.rs
index 6161774b..225eb10d 100644
--- a/src/geom/mod.rs
+++ b/src/geom/mod.rs
@@ -131,6 +131,6 @@ pub trait Numeric:
}
/// 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
}
diff --git a/src/ide/analyze.rs b/src/ide/analyze.rs
index 65d9ded8..12576e53 100644
--- a/src/ide/analyze.rs
+++ b/src/ide/analyze.rs
@@ -7,6 +7,22 @@ use crate::World;
/// Try to determine a set of possible values for an expression.
pub fn analyze(world: &(dyn World + 'static), node: &LinkedNode) -> Vec<Value> {
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(_)) => {
if let Some(parent) = node.parent() {
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 mut tracer = Tracer::new(Some(span));
eval(world.track(), route.track(), tracer.track_mut(), source).ok();
- return tracer.finish();
- }
-
- Some(ast::Expr::Str(s)) => return vec![Value::Str(s.get().into())],
-
- 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();
- }
+ tracer.finish()
}
- _ => {}
+ _ => vec![],
}
-
- vec![]
}
diff --git a/src/ide/tooltip.rs b/src/ide/tooltip.rs
index 202efd8e..701c5622 100644
--- a/src/ide/tooltip.rs
+++ b/src/ide/tooltip.rs
@@ -2,6 +2,7 @@ use if_chain::if_chain;
use unicode_segmentation::UnicodeSegmentation;
use super::{analyze, plain_docs_sentence, summarize_font_family};
+use crate::geom::{round_2, Length, Numeric};
use crate::model::{CastInfo, Tracer, Value};
use crate::syntax::ast;
use crate::syntax::{LinkedNode, Source, SyntaxKind};
@@ -22,15 +23,23 @@ pub fn tooltip(
/// Tooltip for a hovered expression.
fn expr_tooltip(world: &(dyn World + 'static), leaf: &LinkedNode) -> Option<String> {
- if !leaf.is::<ast::Expr>() {
- return None;
- }
-
+ let expr = leaf.cast::<ast::Expr>()?;
let values = analyze(world, leaf);
+
if let [value] = values.as_slice() {
if let Some(docs) = value.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();
@@ -61,6 +70,19 @@ fn expr_tooltip(world: &(dyn World + 'static), leaf: &LinkedNode) -> Option<Stri
(!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.
fn named_param_tooltip(
world: &(dyn World + 'static),
diff --git a/src/model/eval.rs b/src/model/eval.rs
index d0751a1f..96e7317d 100644
--- a/src/model/eval.rs
+++ b/src/model/eval.rs
@@ -15,7 +15,6 @@ use super::{
use crate::diag::{
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, Source, SourceId, Span, Spanned, SyntaxKind, SyntaxNode};
use crate::util::PathExt;
@@ -660,14 +659,7 @@ impl Eval for ast::Numeric {
type Output = Value;
fn eval(&self, _: &mut Vm) -> SourceResult<Self::Output> {
- let (v, unit) = 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(),
- })
+ Ok(Value::numeric(self.get()))
}
}
diff --git a/src/model/value.rs b/src/model/value.rs
index ea17349e..b860a3f6 100644
--- a/src/model/value.rs
+++ b/src/model/value.rs
@@ -12,7 +12,7 @@ use super::{
};
use crate::diag::StrResult;
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};
/// A computational value.
@@ -71,6 +71,18 @@ impl Value {
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.
pub fn type_name(&self) -> &'static str {
match self {
diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs
index 78d895ff..08def533 100644
--- a/src/syntax/ast.rs
+++ b/src/syntax/ast.rs
@@ -345,6 +345,20 @@ impl Expr {
_ => 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 {
diff --git a/src/syntax/lexer.rs b/src/syntax/lexer.rs
index cbddabb7..555ced84 100644
--- a/src/syntax/lexer.rs
+++ b/src/syntax/lexer.rs
@@ -499,7 +499,11 @@ impl Lexer<'_> {
// Read the fractional part if not already done.
// 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);
}