summaryrefslogtreecommitdiff
path: root/src/ide/tooltip.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2023-07-02 19:59:52 +0200
committerLaurenz <laurmaedje@gmail.com>2023-07-02 20:07:43 +0200
commitebfdb1dafa430786db10dad2ef7d5467c1bdbed1 (patch)
tree2bbc24ddb4124c4bb14dec0e536129d4de37b056 /src/ide/tooltip.rs
parent3ab19185093d7709f824b95b979060ce125389d8 (diff)
Move everything into `crates/` directory
Diffstat (limited to 'src/ide/tooltip.rs')
-rw-r--r--src/ide/tooltip.rs222
1 files changed, 0 insertions, 222 deletions
diff --git a/src/ide/tooltip.rs b/src/ide/tooltip.rs
deleted file mode 100644
index 35125e92..00000000
--- a/src/ide/tooltip.rs
+++ /dev/null
@@ -1,222 +0,0 @@
-use std::fmt::Write;
-
-use ecow::{eco_format, EcoString};
-
-use if_chain::if_chain;
-
-use super::analyze::analyze_labels;
-use super::{analyze_expr, plain_docs_sentence, summarize_font_family};
-use crate::doc::Frame;
-use crate::eval::{CastInfo, Tracer, Value};
-use crate::geom::{round_2, Length, Numeric};
-use crate::syntax::{ast, LinkedNode, Source, SyntaxKind};
-use crate::util::pretty_comma_list;
-use crate::World;
-
-/// Describe the item under the cursor.
-pub fn tooltip(
- world: &(dyn World + 'static),
- frames: &[Frame],
- source: &Source,
- cursor: usize,
-) -> Option<Tooltip> {
- let leaf = LinkedNode::new(source.root()).leaf_at(cursor)?;
- if leaf.kind().is_trivia() {
- return None;
- }
-
- named_param_tooltip(world, &leaf)
- .or_else(|| font_tooltip(world, &leaf))
- .or_else(|| ref_tooltip(world, frames, &leaf))
- .or_else(|| expr_tooltip(world, &leaf))
-}
-
-/// A hover tooltip.
-#[derive(Debug, Clone)]
-pub enum Tooltip {
- /// A string of text.
- Text(EcoString),
- /// A string of Typst code.
- Code(EcoString),
-}
-
-/// Tooltip for a hovered expression.
-fn expr_tooltip(world: &(dyn World + 'static), leaf: &LinkedNode) -> Option<Tooltip> {
- let mut ancestor = leaf;
- while !ancestor.is::<ast::Expr>() {
- ancestor = ancestor.parent()?;
- }
-
- let expr = ancestor.cast::<ast::Expr>()?;
- if !expr.hashtag() && !matches!(expr, ast::Expr::MathIdent(_)) {
- return None;
- }
-
- let values = analyze_expr(world, ancestor);
-
- if let [value] = values.as_slice() {
- if let Some(docs) = value.docs() {
- return Some(Tooltip::Text(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 last = None;
- let mut pieces: Vec<EcoString> = vec![];
- let mut iter = values.iter();
- for value in (&mut iter).take(Tracer::MAX - 1) {
- if let Some((prev, count)) = &mut last {
- if *prev == value {
- *count += 1;
- continue;
- } else if *count > 1 {
- write!(pieces.last_mut().unwrap(), " (x{count})").unwrap();
- }
- }
- pieces.push(value.repr().into());
- last = Some((value, 1));
- }
-
- if let Some((_, count)) = last {
- if count > 1 {
- write!(pieces.last_mut().unwrap(), " (x{count})").unwrap();
- }
- }
-
- if iter.next().is_some() {
- pieces.push("...".into());
- }
-
- let tooltip = pretty_comma_list(&pieces, false);
- (!tooltip.is_empty()).then(|| Tooltip::Code(tooltip.into()))
-}
-
-/// Tooltip text for a hovered length.
-fn length_tooltip(length: Length) -> Option<Tooltip> {
- length.em.is_zero().then(|| {
- Tooltip::Code(eco_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())
- ))
- })
-}
-
-/// Tooltip for a hovered reference.
-fn ref_tooltip(
- world: &(dyn World + 'static),
- frames: &[Frame],
- leaf: &LinkedNode,
-) -> Option<Tooltip> {
- if leaf.kind() != SyntaxKind::RefMarker {
- return None;
- }
-
- let target = leaf.text().trim_start_matches('@');
- for (label, detail) in analyze_labels(world, frames).0 {
- if label.0 == target {
- return Some(Tooltip::Text(detail?));
- }
- }
-
- None
-}
-
-/// Tooltips for components of a named parameter.
-fn named_param_tooltip(
- world: &(dyn World + 'static),
- leaf: &LinkedNode,
-) -> Option<Tooltip> {
- let (info, named) = if_chain! {
- // Ensure that we are in a named pair in the arguments to a function
- // call or set rule.
- if let Some(parent) = leaf.parent();
- if let Some(named) = parent.cast::<ast::Named>();
- if let Some(grand) = parent.parent();
- if matches!(grand.kind(), SyntaxKind::Args);
- if let Some(grand_grand) = grand.parent();
- if let Some(expr) = grand_grand.cast::<ast::Expr>();
- if let Some(ast::Expr::Ident(callee)) = match expr {
- ast::Expr::FuncCall(call) => Some(call.callee()),
- ast::Expr::Set(set) => Some(set.target()),
- _ => None,
- };
-
- // Find metadata about the function.
- if let Some(Value::Func(func)) = world.library().global.scope().get(&callee);
- if let Some(info) = func.info();
- then { (info, named) }
- else { return None; }
- };
-
- // Hovering over the parameter name.
- if_chain! {
- if leaf.index() == 0;
- if let Some(ident) = leaf.cast::<ast::Ident>();
- if let Some(param) = info.param(&ident);
- then {
- return Some(Tooltip::Text(plain_docs_sentence(param.docs)));
- }
- }
-
- // Hovering over a string parameter value.
- if_chain! {
- if let Some(string) = leaf.cast::<ast::Str>();
- if let Some(param) = info.param(&named.name());
- if let Some(docs) = find_string_doc(&param.cast, &string.get());
- then {
- return Some(Tooltip::Text(docs.into()));
- }
- }
-
- None
-}
-
-/// Find documentation for a castable string.
-fn find_string_doc(info: &CastInfo, string: &str) -> Option<&'static str> {
- match info {
- CastInfo::Value(Value::Str(s), docs) if s.as_str() == string => Some(docs),
- CastInfo::Union(options) => {
- options.iter().find_map(|option| find_string_doc(option, string))
- }
- _ => None,
- }
-}
-
-/// Tooltip for font.
-fn font_tooltip(world: &dyn World, leaf: &LinkedNode) -> Option<Tooltip> {
- if_chain! {
- // Ensure that we are on top of a string.
- if let Some(string) = leaf.cast::<ast::Str>();
- let lower = string.get().to_lowercase();
-
- // Ensure that we are in the arguments to the text function.
- if let Some(parent) = leaf.parent();
- if let Some(named) = parent.cast::<ast::Named>();
- if named.name().as_str() == "font";
-
- // Find the font family.
- if let Some((_, iter)) = world
- .book()
- .families()
- .find(|&(family, _)| family.to_lowercase().as_str() == lower.as_str());
-
- then {
- let detail = summarize_font_family(iter);
- return Some(Tooltip::Text(detail));
- }
- };
-
- None
-}