Skip to main content

praxis_stdlib/
completion.rs

1//! Completion-data generation from the method catalog (§19.8).
2//!
3//! The §19.8 acceptance criterion requires that "method completion data is
4//! generated from the same catalog used by the compiler." This module renders
5//! the [`MethodCatalog`](crate::MethodCatalog) into a serializable completion
6//! table for completion and signature help (§5.7: "The language server uses the
7//! same table"). No LSP wiring here — just the generation, plus a round-trip
8//! test proving the generated data covers the compiler's catalog 1:1.
9
10use crate::{MethodCatalog, MethodEntry};
11
12/// One completion item: the receiver shape, method name, parameter shapes,
13/// result shape, and doc — everything the LSP needs to offer a completion.
14#[derive(Clone, Debug, PartialEq, Eq)]
15pub struct CompletionItem {
16    /// The receiver type as a display string, e.g. `Vec[T]` or `Map[K, V]`.
17    pub receiver: String,
18    /// The method name, e.g. `push`.
19    pub name: String,
20    /// The parameter type-pattern display strings, in order.
21    pub params: Vec<String>,
22    /// The result type-pattern display string.
23    pub result: String,
24    /// The one-line doc.
25    pub doc: String,
26}
27
28/// Generate the full completion table from the catalog, in catalog order.
29/// Every entry becomes one [`CompletionItem`]; the output is a 1:1 rendering.
30#[must_use]
31pub fn completion_data(catalog: &MethodCatalog) -> Vec<CompletionItem> {
32    catalog.entries().iter().map(entry_to_item).collect()
33}
34
35/// Render one catalog entry as a completion item.
36fn entry_to_item(e: &MethodEntry) -> CompletionItem {
37    CompletionItem {
38        receiver: e.receiver.to_string(),
39        name: e.name.to_string(),
40        params: e.params.iter().map(|p| p.to_string()).collect(),
41        result: e.result.to_string(),
42        doc: e.doc.to_string(),
43    }
44}
45
46#[cfg(test)]
47mod tests {
48    use super::*;
49
50    #[test]
51    fn completion_data_covers_every_catalog_entry() {
52        // The §19.8 acceptance criterion: completion data is generated from the
53        // same catalog the compiler uses. Every builtin_catalog() entry must
54        // appear in the generated completion data, 1:1.
55        let cat = crate::builtin_catalog();
56        let items = completion_data(&cat);
57        assert_eq!(
58            items.len(),
59            cat.len(),
60            "completion data must cover every catalog entry"
61        );
62        // Spot-check a Vec and a Map entry (the headline receiver shapes).
63        let has_vec_push = items
64            .iter()
65            .any(|i| i.receiver == "Vec[T]" && i.name == "push");
66        assert!(has_vec_push, "Vec[T].push must be in completion data");
67        let has_map_insert = items
68            .iter()
69            .any(|i| i.receiver == "Map[K, V]" && i.name == "insert");
70        assert!(
71            has_map_insert,
72            "Map[K, V].insert must be in completion data"
73        );
74        let has_grid_neighbors4 = items
75            .iter()
76            .any(|i| i.receiver == "Grid[T]" && i.name == "neighbors4");
77        assert!(
78            has_grid_neighbors4,
79            "Grid[T].neighbors4 must be in completion data"
80        );
81    }
82
83    #[test]
84    fn completion_data_round_trips_receiver_name_arity() {
85        // Every (receiver, name, arity) triple in the catalog is unique (the
86        // builder rejects duplicates), so the completion items' triples must
87        // also be unique — a 1:1 mapping with no loss.
88        let cat = crate::builtin_catalog();
89        let items = completion_data(&cat);
90        let triples: Vec<(String, String, usize)> = items
91            .iter()
92            .map(|i| (i.receiver.clone(), i.name.clone(), i.params.len()))
93            .collect();
94        let unique: std::collections::HashSet<_> = triples.iter().collect();
95        assert_eq!(
96            unique.len(),
97            items.len(),
98            "completion triples must be unique"
99        );
100    }
101}