Repository: typst/typst
File: crates/typst-ide/src/complete.rs (Rust)

Code before:

    // Behind a started field access: "emoji.fa|".
    if_chain! {
        if ctx.leaf.kind() == SyntaxKind::Ident;
        if let Some(prev) = ctx.leaf.prev_sibling();
        if prev.kind() == SyntaxKind::Dot;
        if let Some(prev_prev) = prev.prev_sibling();
        if prev_prev.is::<ast::Expr>();
        if let Some((value, styles)) = analyze_expr(ctx.world, &prev_prev).into_iter().next();
        then {
            ctx.from = ctx.leaf.offset();
            field_access_completions(ctx, &value, &styles);
            return true;
        }
    }

    false
}

/// Add completions for all fields on a value.
fn field_access_completions(
    ctx: &mut CompletionContext,
    value: &Value,
    styles: &Option<Styles>,
) {
    for (name, value) in value.ty().scope().iter() {
        ctx.value_completion(Some(name.clone()), value, true, None);
    }

    if let Some(scope) = value.scope() {
        for (name, value) in scope.iter() {
            ctx.value_completion(Some(name.clone()), value, true, None);
        }
    }

    for &(method, args) in mutable_methods_on(value.ty()) {
        ctx.completions.push(Completion {
            kind: CompletionKind::Func,
            label: method.into(),
            apply: Some(if args {
                eco_format!("{method}(${{}})")
            } else {
                eco_format!("{method}()${{}}")
            }),
            detail: None,
        })
    }

    for &field in fields_on(value.ty()) {
        // Complete the field name along with its value. Notes:
        // 1. No parentheses since function fields cannot currently be called

Review comment (suggestion):
There are many patterns like `(name, value, _)`. I think it deserves to create two iterator methods like `HashMap` does.
- `items()`: returns `(name, value)`
- `item_with_spans()`: returns `(name, value, span)`
The method names could have bikeshedding.

Code after:

    // Behind a started field access: "emoji.fa|".
    if_chain! {
        if ctx.leaf.kind() == SyntaxKind::Ident;
        if let Some(prev) = ctx.leaf.prev_sibling();
        if prev.kind() == SyntaxKind::Dot;
        if let Some(prev_prev) = prev.prev_sibling();
        if prev_prev.is::<ast::Expr>();
        if let Some((value, styles)) = analyze_expr(ctx.world, &prev_prev).into_iter().next();
        then {
            ctx.from = ctx.leaf.offset();
            field_access_completions(ctx, &value, &styles);
            return true;
        }
    }

    false
}

/// Add completions for all fields on a value.
fn field_access_completions(
    ctx: &mut CompletionContext,
    value: &Value,
    styles: &Option<Styles>,
) {
    for (name, value, _) in value.ty().scope().iter() {
        ctx.value_completion(Some(name.clone()), value, true, None);
    }

    if let Some(scope) = value.scope() {
        for (name, value, _) in scope.iter() {
            ctx.value_completion(Some(name.clone()), value, true, None);
        }
    }

    for &(method, args) in mutable_methods_on(value.ty()) {
        ctx.completions.push(Completion {
            kind: CompletionKind::Func,
            label: method.into(),
            apply: Some(if args {
                eco_format!("{method}(${{}})")
            } else {
                eco_format!("{method}()${{}}")
            }),
            detail: None,
        })
    }

    for &field in fields_on(value.ty()) {
        // Complete the field name along with its value. Notes:
        // 1. No parentheses since function fields cannot currently be called