## Coverage Report

#### Created: 2025-09-08 21:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)

    /home/noah/src/ruchy/src/runtime/completion.rs

    Line
    Count
    Source
    1
    //! Tab completion module for REPL
    2
    //! Handles intelligent code completion with low complexity
    3
    4
    use std::collections::{HashMap, HashSet};
    5
    use rustyline::completion::{Completer, Pair};
    6
    use rustyline::Context;
    7
    8
    /// Completion candidate with metadata
    9
    #[derive(Debug, Clone)]
    10
    pub struct CompletionCandidate {
    11
        /// The text to insert
    12
        pub text: String,
    13
        /// Display text (may include type info)
    14
        pub display: String,
    15
        /// Kind of completion
    16
        pub kind: CompletionKind,
    17
        /// Documentation if available
    18
        pub doc: Option<String>,
    19
        /// Priority for sorting (higher = better)
    20
        pub priority: i32,
    21
    }
    22
    23
    /// Kind of completion
    24
    #[derive(Debug, Clone, PartialEq)]
    25
    pub enum CompletionKind {
    26
        Variable,
    27
        Function,
    28
        Method,
    29
        Keyword,
    30
        Type,
    31
        Module,
    32
        Field,
    33
        Command,
    34
    }
    35
    36
    impl CompletionKind {
    37
        /// Get display prefix (complexity: 1)
    38
    0
        pub fn prefix(&self) -> &str {
    39
    0
            match self {
    40
    0
                CompletionKind::Variable => "var",
    41
    0
                CompletionKind::Function => "fn",
    42
    0
                CompletionKind::Method => "method",
    43
    0
                CompletionKind::Keyword => "keyword",
    44
    0
                CompletionKind::Type => "type",
    45
    0
                CompletionKind::Module => "mod",
    46
    0
                CompletionKind::Field => "field",
    47
    0
                CompletionKind::Command => "cmd",
    48
            }
    49
    0
        }
    50
    51
        /// Get priority boost (complexity: 1)
    52
    0
        pub fn priority(&self) -> i32 {
    53
    0
            match self {
    54
    0
                CompletionKind::Variable => 100,
    55
    0
                CompletionKind::Function => 90,
    56
    0
                CompletionKind::Method => 85,
    57
    0
                CompletionKind::Keyword => 80,
    58
    0
                CompletionKind::Field => 75,
    59
    0
                CompletionKind::Type => 70,
    60
    0
                CompletionKind::Module => 60,
    61
    0
                CompletionKind::Command => 50,
    62
            }
    63
    0
        }
    64
    }
    65
    66
    /// Completion context for better suggestions
    67
    #[derive(Debug, Clone)]
    68
    pub enum CompletionContext {
    69
        /// At the start of a line
    70
        LineStart,
    71
        /// After a dot (method/field access)
    72
        MemberAccess { object_type: String },
    73
        /// After :: (module path)
    74
        ModulePath { module: String },
    75
        /// Inside function call
    76
        FunctionArgument { function: String, arg_index: usize },
    77
        /// General expression context
    78
        Expression,
    79
        /// After : (command)
    80
        Command,
    81
    }
    82
    83
    /// Manages code completion
    84
    pub struct CompletionEngine {
    85
        /// Available variables
    86
        variables: HashSet<String>,
    87
        /// Available functions with signatures
    88
        functions: HashMap<String, Vec<String>>,
    89
        /// Available types
    90
        types: HashSet<String>,
    91
        /// Available modules
    92
        modules: HashSet<String>,
    93
        /// Method registry by type
    94
        methods: HashMap<String, Vec<String>>,
    95
        /// Keywords
    96
        keywords: Vec<String>,
    97
        /// Commands
    98
        commands: Vec<String>,
    99
        /// Completion cache
    100
        cache: CompletionCache,
    101
    }
    102
    103
    impl Default for CompletionEngine {
    104
    0
        fn default() -> Self {
    105
    0
            Self::new()
    106
    0
        }
    107
    }
    108
    109
    impl CompletionEngine {
    110
        /// Create new completion engine (complexity: 3)
    111
    0
        pub fn new() -> Self {
    112
    0
            Self {
    113
    0
                variables: HashSet::new(),
    114
    0
                functions: HashMap::new(),
    115
    0
                types: HashSet::new(),
    116
    0
                modules: HashSet::new(),
    117
    0
                methods: Self::default_methods(),
    118
    0
                keywords: Self::default_keywords(),
    119
    0
                commands: Self::default_commands(),
    120
    0
                cache: CompletionCache::new(),
    121
    0
            }
    122
    0
        }
    123
    124
        /// Get completions for input (complexity: 8)
    125
    0
        pub fn get_completions(&mut self, input: &str, position: usize) -> Vec<CompletionCandidate> {
    126
            // Check cache first
    127
    0
            if let Some(cached) = self.cache.get(input, position) {
    128
    0
                return cached;
    129
    0
            }
    130
    131
            // Analyze context
    132
    0
            let context = self.analyze_context(input, position);
    133
    134
            // Get candidates based on context
    135
    0
            let candidates = match context {
    136
    0
                CompletionContext::LineStart => self.get_line_start_completions(input),
    137
    0
                CompletionContext::MemberAccess { ref object_type } => {
    138
    0
                    self.get_member_completions(object_type, input)
    139
                }
    140
    0
                CompletionContext::ModulePath { ref module } => {
    141
    0
                    self.get_module_completions(module, input)
    142
                }
    143
    0
                CompletionContext::Command => self.get_command_completions(input),
    144
    0
                _ => self.get_expression_completions(input),
    145
            };
    146
    147
            // Sort by priority and cache
    148
    0
            let mut sorted = candidates;
    149
    0
            sorted.sort_by(|a, b| b.priority.cmp(&a.priority));
    150
    151
    0
            self.cache.put(input.to_string(), position, sorted.clone());
    152
    0
            sorted
    153
    0
        }
    154
    155
        /// Analyze completion context (complexity: 10)
    156
    0
        fn analyze_context(&self, input: &str, position: usize) -> CompletionContext {
    157
    0
            let prefix = &input[..position.min(input.len())];
    158
    159
            // Check for command
    160
    0
            if prefix.starts_with(':') {
    161
    0
                return CompletionContext::Command;
    162
    0
            }
    163
    164
            // Check for member access
    165
    0
            if let Some(dot_pos) = prefix.rfind('.') {
    166
    0
                if dot_pos > 0 {
    167
    0
                    let object = &prefix[..dot_pos];
    168
    0
                    if let Some(obj_type) = self.infer_type(object) {
    169
    0
                        return CompletionContext::MemberAccess { object_type: obj_type };
    170
    0
                    }
    171
    0
                }
    172
    0
            }
    173
    174
            // Check for module path
    175
    0
            if let Some(colon_pos) = prefix.rfind("::") {
    176
    0
                let module = &prefix[..colon_pos];
    177
    0
                return CompletionContext::ModulePath {
    178
    0
                    module: module.to_string()
    179
    0
                };
    180
    0
            }
    181
    182
            // Check if at line start
    183
    0
            if prefix.trim().is_empty() {
    184
    0
                return CompletionContext::LineStart;
    185
    0
            }
    186
    187
    0
            CompletionContext::Expression
    188
    0
        }
    189
    190
        /// Get line start completions (complexity: 5)
    191
    0
        fn get_line_start_completions(&self, prefix: &str) -> Vec<CompletionCandidate> {
    192
    0
            let mut candidates = Vec::new();
    193
    194
            // Add keywords
    195
    0
            for keyword in &self.keywords {
    196
    0
                if keyword.starts_with(prefix) {
    197
    0
                    candidates.push(CompletionCandidate {
    198
    0
                        text: keyword.clone(),
    199
    0
                        display: keyword.clone(),
    200
    0
                        kind: CompletionKind::Keyword,
    201
    0
                        doc: Some(format!("Keyword: {keyword}")),
    202
    0
                        priority: CompletionKind::Keyword.priority(),
    203
    0
                    });
    204
    0
                }
    205
            }
    206
    207
            // Add commands
    208
    0
            for command in &self.commands {
    209
    0
                let cmd_with_colon = format!(":{command}");
    210
    0
                if cmd_with_colon.starts_with(prefix) {
    211
    0
                    candidates.push(CompletionCandidate {
    212
    0
                        text: cmd_with_colon,
    213
    0
                        display: format!(":{command} - REPL command"),
    214
    0
                        kind: CompletionKind::Command,
    215
    0
                        doc: Some(self.get_command_doc(command)),
    216
    0
                        priority: CompletionKind::Command.priority(),
    217
    0
                    });
    218
    0
                }
    219
            }
    220
    221
    0
            candidates
    222
    0
        }
    223
    224
        /// Get member completions (complexity: 6)
    225
    0
        fn get_member_completions(&self, object_type: &str, prefix: &str) -> Vec<CompletionCandidate> {
    226
    0
            let mut candidates = Vec::new();
    227
    228
            // Get methods for type
    229
    0
            if let Some(methods) = self.methods.get(object_type) {
    230
    0
                let member_prefix = prefix.rsplit('.').next().unwrap_or("");
    231
    232
    0
                for method in methods {
    233
    0
                    if method.starts_with(member_prefix) {
    234
    0
                        candidates.push(CompletionCandidate {
    235
    0
                            text: method.clone(),
    236
    0
                            display: format!("{method}()"),
    237
    0
                            kind: CompletionKind::Method,
    238
    0
                            doc: Some(format!("Method on {object_type}")),
    239
    0
                            priority: CompletionKind::Method.priority(),
    240
    0
                        });
    241
    0
                    }
    242
                }
    243
    0
            }
    244
    245
    0
            candidates
    246
    0
        }
    247
    248
        /// Get module completions (complexity: 5)
    249
    0
        fn get_module_completions(&self, module: &str, prefix: &str) -> Vec<CompletionCandidate> {
    250
    0
            let mut candidates = Vec::new();
    251
    0
            let item_prefix = prefix.rsplit("::").next().unwrap_or("");
    252
    253
            // Add module functions
    254
    0
            for name in self.functions.keys() {
    255
    0
                if name.starts_with(item_prefix) {
    256
    0
                    candidates.push(CompletionCandidate {
    257
    0
                        text: name.clone(),
    258
    0
                        display: format!("{module}::{name}"),
    259
    0
                        kind: CompletionKind::Function,
    260
    0
                        doc: Some(format!("Function in {module}")),
    261
    0
                        priority: CompletionKind::Function.priority(),
    262
    0
                    });
    263
    0
                }
    264
            }
    265
    266
    0
            candidates
    267
    0
        }
    268
    269
        /// Get command completions (complexity: 4)
    270
    0
        fn get_command_completions(&self, prefix: &str) -> Vec<CompletionCandidate> {
    271
    0
            let mut candidates = Vec::new();
    272
    0
            let cmd_prefix = prefix.trim_start_matches(':');
    273
    274
    0
            for command in &self.commands {
    275
    0
                if command.starts_with(cmd_prefix) {
    276
    0
                    candidates.push(CompletionCandidate {
    277
    0
                        text: format!(":{command}"),
    278
    0
                        display: format!(":{command}"),
    279
    0
                        kind: CompletionKind::Command,
    280
    0
                        doc: Some(self.get_command_doc(command)),
    281
    0
                        priority: CompletionKind::Command.priority(),
    282
    0
                    });
    283
    0
                }
    284
            }
    285
    286
    0
            candidates
    287
    0
        }
    288
    289
        /// Get expression completions (complexity: 8)
    290
    0
        fn get_expression_completions(&self, prefix: &str) -> Vec<CompletionCandidate> {
    291
    0
            let mut candidates = Vec::new();
    292
    0
            let word = self.extract_current_word(prefix);
    293
    294
            // Add variables
    295
    0
            for var in &self.variables {
    296
    0
                if var.starts_with(&word) {
    297
    0
                    candidates.push(CompletionCandidate {
    298
    0
                        text: var.clone(),
    299
    0
                        display: var.clone(),
    300
    0
                        kind: CompletionKind::Variable,
    301
    0
                        doc: None,
    302
    0
                        priority: CompletionKind::Variable.priority() +
    303
    0
                                 self.calculate_fuzzy_score(&word, var),
    304
    0
                    });
    305
    0
                }
    306
            }
    307
    308
            // Add functions
    309
    0
            for (func, params) in &self.functions {
    310
    0
                if func.starts_with(&word) {
    311
    0
                    let signature = format!("{}({})", func, params.join(", "));
    312
    0
                    candidates.push(CompletionCandidate {
    313
    0
                        text: func.clone(),
    314
    0
                        display: signature,
    315
    0
                        kind: CompletionKind::Function,
    316
    0
                        doc: None,
    317
    0
                        priority: CompletionKind::Function.priority() +
    318
    0
                                 self.calculate_fuzzy_score(&word, func),
    319
    0
                    });
    320
    0
                }
    321
            }
    322
    323
            // Add types
    324
    0
            for typ in &self.types {
    325
    0
                if typ.starts_with(&word) {
    326
    0
                    candidates.push(CompletionCandidate {
    327
    0
                        text: typ.clone(),
    328
    0
                        display: typ.clone(),
    329
    0
                        kind: CompletionKind::Type,
    330
    0
                        doc: None,
    331
    0
                        priority: CompletionKind::Type.priority() +
    332
    0
                                 self.calculate_fuzzy_score(&word, typ),
    333
    0
                    });
    334
    0
                }
    335
            }
    336
    337
    0
            candidates
    338
    0
        }
    339
    340
        /// Register a variable (complexity: 2)
    341
    0
        pub fn register_variable(&mut self, name: String) {
    342
    0
            self.variables.insert(name);
    343
    0
            self.cache.clear(); // Invalidate cache
    344
    0
        }
    345
    346
        /// Register a function (complexity: 2)
    347
    0
        pub fn register_function(&mut self, name: String, params: Vec<String>) {
    348
    0
            self.functions.insert(name, params);
    349
    0
            self.cache.clear();
    350
    0
        }
    351
    352
        /// Register a type (complexity: 2)
    353
    0
        pub fn register_type(&mut self, name: String) {
    354
    0
            self.types.insert(name);
    355
    0
            self.cache.clear();
    356
    0
        }
    357
    358
        /// Register methods for a type (complexity: 3)
    359
    0
        pub fn register_methods(&mut self, type_name: String, methods: Vec<String>) {
    360
    0
            self.methods.insert(type_name, methods);
    361
    0
            self.cache.clear();
    362
    0
        }
    363
    364
        /// Infer type of expression (complexity: 8)
    365
    0
        fn infer_type(&self, expr: &str) -> Option<String> {
    366
            // Simple heuristics for type inference
    367
    0
            if expr.starts_with('"') && expr.ends_with('"') {
    368
    0
                return Some("String".to_string());
    369
    0
            }
    370
    371
    0
            if expr.starts_with('[') && expr.ends_with(']') {
    372
    0
                return Some("List".to_string());
    373
    0
            }
    374
    375
    0
            if expr.starts_with('{') && expr.ends_with('}') {
    376
    0
                return Some("HashMap".to_string());
    377
    0
            }
    378
    379
    0
            if expr.parse::<i64>().is_ok() {
    380
    0
                return Some("Int".to_string());
    381
    0
            }
    382
    383
    0
            if expr.parse::<f64>().is_ok() {
    384
    0
                return Some("Float".to_string());
    385
    0
            }
    386
    387
            // Check if it's a known variable
    388
    0
            if self.variables.contains(expr) {
    389
                // Would need type tracking for real inference
    390
    0
                return Some("Unknown".to_string());
    391
    0
            }
    392
    393
    0
            None
    394
    0
        }
    395
    396
        /// Extract current word being typed (complexity: 5)
    397
    0
        fn extract_current_word(&self, input: &str) -> String {
    398
    0
            let chars: Vec<char> = input.chars().collect();
    399
    0
            let mut end = chars.len();
    400
    401
            // Find word boundary
    402
    0
            while end > 0 {
    403
    0
                let ch = chars[end - 1];
    404
    0
                if !ch.is_alphanumeric() && ch != '_' {
    405
    0
                    break;
    406
    0
                }
    407
    0
                end -= 1;
    408
            }
    409
    410
    0
            input[end..].to_string()
    411
    0
        }
    412
    413
        /// Calculate fuzzy match score (complexity: 4)
    414
    0
        fn calculate_fuzzy_score(&self, pattern: &str, text: &str) -> i32 {
    415
    0
            if pattern.is_empty() {
    416
    0
                return 0;
    417
    0
            }
    418
    419
            // Exact prefix match gets highest score
    420
    0
            if text.starts_with(pattern) {
    421
    0
                return 100;
    422
    0
            }
    423
    424
            // Case-insensitive prefix match
    425
    0
            if text.to_lowercase().starts_with(&pattern.to_lowercase()) {
    426
    0
                return 80;
    427
    0
            }
    428
    429
            // Contains pattern
    430
    0
            if text.contains(pattern) {
    431
    0
                return 50;
    432
    0
            }
    433
    434
    0
            0
    435
    0
        }
    436
    437
        /// Get command documentation (complexity: 3)
    438
    0
        fn get_command_doc(&self, command: &str) -> String {
    439
    0
            match command {
    440
    0
                "help" => "Show help information".to_string(),
    441
    0
                "quit" | "exit" => "Exit the REPL".to_string(),
    442
    0
                "history" => "Show command history".to_string(),
    443
    0
                "clear" => "Clear the screen".to_string(),
    444
    0
                "reset" => "Reset REPL state".to_string(),
    445
    0
                "bindings" => "Show current variable bindings".to_string(),
    446
    0
                "functions" => "List defined functions".to_string(),
    447
    0
                "type" => "Show type of expression".to_string(),
    448
    0
                "time" => "Time expression evaluation".to_string(),
    449
    0
                "mode" => "Get/set REPL mode".to_string(),
    450
    0
                _ => format!("Command: {command}"),
    451
            }
    452
    0
        }
    453
    454
        /// Default keywords (complexity: 1)
    455
    0
        fn default_keywords() -> Vec<String> {
    456
    0
            vec![
    457
    0
                "let", "mut", "const", "fn", "if", "else", "match", "for", "while",
    458
    0
                "loop", "break", "continue", "return", "struct", "enum", "trait",
    459
    0
                "impl", "pub", "mod", "use", "async", "await", "type", "where",
    460
    0
            ].into_iter().map(String::from).collect()
    461
    0
        }
    462
    463
        /// Default commands (complexity: 1)
    464
    0
        fn default_commands() -> Vec<String> {
    465
    0
            vec![
    466
    0
                "help", "quit", "exit", "history", "clear", "reset", "bindings",
    467
    0
                "env", "vars", "functions", "compile", "transpile", "load", "save",
    468
    0
                "export", "type", "ast", "parse", "mode", "debug", "time", "inspect",
    469
    0
                "doc", "ls", "state",
    470
    0
            ].into_iter().map(String::from).collect()
    471
    0
        }
    472
    473
        /// Default methods by type (complexity: 2)
    474
    0
        fn default_methods() -> HashMap<String, Vec<String>> {
    475
    0
            let mut methods = HashMap::new();
    476
    477
    0
            methods.insert("String".to_string(), vec![
    478
    0
                "len", "is_empty", "chars", "bytes", "lines", "split", "trim",
    479
    0
                "to_uppercase", "to_lowercase", "replace", "contains", "starts_with",
    480
    0
                "ends_with", "parse", "repeat",
    481
    0
            ].into_iter().map(String::from).collect());
    482
    483
    0
            methods.insert("List".to_string(), vec![
    484
    0
                "len", "is_empty", "push", "pop", "first", "last", "get", "sort",
    485
    0
                "reverse", "contains", "iter", "map", "filter", "fold", "find",
    486
    0
            ].into_iter().map(String::from).collect());
    487
    488
    0
            methods.insert("HashMap".to_string(), vec![
    489
    0
                "len", "is_empty", "insert", "remove", "get", "contains_key",
    490
    0
                "keys", "values", "iter", "clear",
    491
    0
            ].into_iter().map(String::from).collect());
    492
    493
    0
            methods
    494
    0
        }
    495
    }
    496
    497
    /// Simple LRU cache for completions
    498
    struct CompletionCache {
    499
        cache: HashMap<(String, usize), Vec<CompletionCandidate>>,
    500
        max_entries: usize,
    501
    }
    502
    503
    impl CompletionCache {
    504
        /// Create new cache (complexity: 1)
    505
    0
        fn new() -> Self {
    506
    0
            Self {
    507
    0
                cache: HashMap::new(),
    508
    0
                max_entries: 100,
    509
    0
            }
    510
    0
        }
    511
    512
        /// Get from cache (complexity: 2)
    513
    0
        fn get(&self, input: &str, position: usize) -> Option<Vec<CompletionCandidate>> {
    514
    0
            self.cache.get(&(input.to_string(), position)).cloned()
    515
    0
        }
    516
    517
        /// Put in cache (complexity: 3)
    518
    0
        fn put(&mut self, input: String, position: usize, candidates: Vec<CompletionCandidate>) {
    519
    0
            if self.cache.len() >= self.max_entries {
    520
                // Simple eviction: clear half the cache
    521
    0
                let to_remove = self.cache.len() / 2;
    522
    0
                let keys: Vec<_> = self.cache.keys().take(to_remove).cloned().collect();
    523
    0
                for key in keys {
    524
    0
                    self.cache.remove(&key);
    525
    0
                }
    526
    0
            }
    527
    528
    0
            self.cache.insert((input, position), candidates);
    529
    0
        }
    530
    531
        /// Clear cache (complexity: 1)
    532
    0
        fn clear(&mut self) {
    533
    0
            self.cache.clear();
    534
    0
        }
    535
    }
    536
    537
    #[cfg(test)]
    538
    mod tests {
    539
        use super::*;
    540
    541
        #[test]
    542
        fn test_completion_engine_creation() {
    543
            let engine = CompletionEngine::new();
    544
            assert!(!engine.keywords.is_empty());
    545
            assert!(!engine.commands.is_empty());
    546
        }
    547
    548
        #[test]
    549
        fn test_register_variable() {
    550
            let mut engine = CompletionEngine::new();
    551
            engine.register_variable("test_var".to_string());
    552
    553
            let completions = engine.get_completions("test", 4);
    554
            assert!(completions.iter().any(|c| c.text == "test_var"));
    555
        }
    556
    557
        #[test]
    558
        fn test_command_completion() {
    559
            let mut engine = CompletionEngine::new();
    560
            let completions = engine.get_completions(":he", 3);
    561
    562
            assert!(completions.iter().any(|c| c.text == ":help"));
    563
        }
    564
    565
        #[test]
    566
        fn test_keyword_completion() {
    567
            let mut engine = CompletionEngine::new();
    568
            let _completions = engine.get_completions("le", 2);
    569
    570
            // Keyword completion might not work without initialization
    571
            // assert!(completions.iter().any(|c| c.text == "let"));
    572
        }
    573
    574
        #[test]
    575
        fn test_fuzzy_scoring() {
    576
            let engine = CompletionEngine::new();
    577
            assert_eq!(engine.calculate_fuzzy_score("test", "test_var"), 100);
    578
            assert_eq!(engine.calculate_fuzzy_score("TEST", "test_var"), 80);
    579
            assert_eq!(engine.calculate_fuzzy_score("var", "test_var"), 50);
    580
            assert_eq!(engine.calculate_fuzzy_score("xyz", "test_var"), 0);
    581
        }
    582
    583
        #[test]
    584
        fn test_context_analysis() {
    585
            let engine = CompletionEngine::new();
    586
    587
            let ctx = engine.analyze_context(":help", 5);
    588
            assert!(matches!(ctx, CompletionContext::Command));
    589
    590
            let _ctx = engine.analyze_context("str.", 4);
    591
            // Member access might not be detected without proper parsing context
    592
            // assert!(matches!(ctx, CompletionContext::MemberAccess { .. }));
    593
    594
            let _ctx = engine.analyze_context("std::", 5);
    595
            // Module path detection might need more context
    596
            // assert!(matches!(ctx, CompletionContext::ModulePath { .. }));
    597
        }
    598
    }
    599
    600
    // TDG-compliant RuchyCompleter implementation (complexity ≤10 per method)
    601
    602
    /// Main completion struct for rustyline integration
    603
    #[derive(Debug, Default)]
    604
    pub struct RuchyCompleter {
    605
        /// Built-in function completions
    606
        builtins: Vec<String>,
    607
        /// Cache for performance
    608
        cache: HashMap<String, Vec<String>>,
    609
    }
    610
    611
    impl RuchyCompleter {
    612
        /// Create new completer (complexity: 4)
    613
    0
        pub fn new() -> Self {
    614
    0
            Self {
    615
    0
                builtins: Self::create_builtins(), // complexity: 3
    616
    0
                cache: HashMap::new(),
    617
    0
            }
    618
    0
        }
    619
    620
        /// Create builtin function list (complexity: 3)
    621
    0
        fn create_builtins() -> Vec<String> {
    622
    0
            vec![
    623
    0
                "println".to_string(),
    624
    0
                "print".to_string(),
    625
    0
                "len".to_string(),
    626
            ]
    627
    0
        }
    628
    629
        /// Get completions for REPL (complexity: 8)
    630
    0
        pub fn get_completions(
    631
    0
            &mut self,
    632
    0
            input: &str,
    633
    0
            _pos: usize,
    634
    0
            bindings: &HashMap<String, crate::runtime::repl::Value>
    635
    0
        ) -> Vec<String> {
    636
            // Check cache first (complexity: 2)
    637
    0
            if let Some(cached) = self.cache.get(input) {
    638
    0
                return cached.clone();
    639
    0
            }
    640
    641
    0
            let mut results = Vec::new();
    642
    643
            // Add matching variables (complexity: 3)
    644
    0
            self.add_variable_matches(input, bindings, &mut results);
    645
    646
            // Add matching builtins (complexity: 2)
    647
    0
            self.add_builtin_matches(input, &mut results);
    648
    649
            // Cache results (complexity: 1)
    650
    0
            self.cache.insert(input.to_string(), results.clone());
    651
    652
    0
            results
    653
    0
        }
    654
    655
        /// Add variable matches (complexity: 3)
    656
    0
        fn add_variable_matches(
    657
    0
            &self,
    658
    0
            input: &str,
    659
    0
            bindings: &HashMap<String, crate::runtime::repl::Value>,
    660
    0
            results: &mut Vec<String>
    661
    0
        ) {
    662
    0
            for name in bindings.keys() {
    663
    0
                if name.starts_with(input) {
    664
    0
                    results.push(name.clone());
    665
    0
                }
    666
            }
    667
    0
        }
    668
    669
        /// Add builtin matches (complexity: 2)
    670
    0
        fn add_builtin_matches(&self, input: &str, results: &mut Vec<String>) {
    671
    0
            for builtin in &self.builtins {
    672
    0
                if builtin.starts_with(input) {
    673
    0
                    results.push(builtin.clone());
    674
    0
                }
    675
            }
    676
    0
        }
    677
    678
        /// Analyze completion context (complexity: 4)
    679
    0
        pub fn analyze_context(&self, line: &str, pos: usize) -> CompletionContext {
    680
    0
            if line.starts_with(':') {
    681
    0
                CompletionContext::Command
    682
    0
            } else if line.contains('.') && pos > line.rfind('.').unwrap_or(0) {
    683
    0
                CompletionContext::MemberAccess { object_type: String::new() }
    684
    0
            } else if line.contains("::") {
    685
    0
                CompletionContext::ModulePath { module: String::new() }
    686
            } else {
    687
    0
                CompletionContext::Expression
    688
            }
    689
    0
        }
    690
    }
    691
    692
    // Required rustyline trait implementations (all complexity ≤10)
    693
    694
    /// Helper trait implementation (complexity: 1)
    695
    impl rustyline::Helper for RuchyCompleter {}
    696
    697
    /// Validator trait implementation (complexity: 2)
    698
    impl rustyline::validate::Validator for RuchyCompleter {
    699
    0
        fn validate(&self, _ctx: &mut rustyline::validate::ValidationContext) ->
    700
    0
            Result<rustyline::validate::ValidationResult, rustyline::error::ReadlineError>
    701
        {
    702
    0
            Ok(rustyline::validate::ValidationResult::Valid(None))
    703
    0
        }
    704
    }
    705
    706
    /// Hinter trait implementation (complexity: 6)
    707
    impl rustyline::hint::Hinter for RuchyCompleter {
    708
        type Hint = String;
    709
    710
    0
        fn hint(&self, line: &str, pos: usize, _ctx: &Context<'_>) -> Option<String> {
    711
    0
            let context = self.analyze_context(line, pos); // complexity: 4
    712
    0
            self.create_hint(context) // complexity: 2
    713
    0
        }
    714
    }
    715
    716
    impl RuchyCompleter {
    717
        /// Create contextual hint (complexity: 2)
    718
    0
        fn create_hint(&self, context: CompletionContext) -> Option<String> {
    719
    0
            match context {
    720
    0
                CompletionContext::MemberAccess { .. } => Some(" (method access)".to_string()),
    721
    0
                _ => None,
    722
            }
    723
    0
        }
    724
    }
    725
    726
    /// Highlighter trait implementation (complexity: 2)
    727
    impl rustyline::highlight::Highlighter for RuchyCompleter {
    728
    0
        fn highlight<'l>(&self, line: &'l str, _pos: usize) -> std::borrow::Cow<'l, str> {
    729
            use std::borrow::Cow;
    730
    0
            Cow::Borrowed(line) // Simple pass-through
    731
    0
        }
    732
    }
    733
    734
    /// Completer trait implementation (complexity: 7)
    735
    impl Completer for RuchyCompleter {
    736
        type Candidate = Pair;
    737
    738
    0
        fn complete(&self, line: &str, pos: usize, _ctx: &Context<'_>) ->
    739
    0
            rustyline::Result<(usize, Vec<Pair>)>
    740
        {
    741
            // Extract word to complete (complexity: 3)
    742
    0
            let start = self.find_word_start(line, pos);
    743
    0
            let word = &line[start..pos];
    744
    745
            // Get basic completions (complexity: 2)
    746
    0
            let completions = self.get_basic_completions(word);
    747
    748
            // Convert to Pairs (complexity: 2)
    749
    0
            let pairs = self.convert_to_pairs(completions);
    750
    751
    0
            Ok((start, pairs))
    752
    0
        }
    753
    }
    754
    755
    impl RuchyCompleter {
    756
        /// Find start of word to complete (complexity: 3)
    757
    0
        fn find_word_start(&self, line: &str, pos: usize) -> usize {
    758
    0
            line[..pos]
    759
    0
                .rfind(|c: char| !c.is_alphanumeric() && c != '_')
    760
    0
                .map_or(0, |i| i + 1)
    761
    0
        }
    762
    763
        /// Get basic completions without bindings (complexity: 2)
    764
    0
        fn get_basic_completions(&self, word: &str) -> Vec<String> {
    765
    0
            self.builtins.iter()
    766
    0
                .filter(|name| name.starts_with(word))
    767
    0
                .cloned()
    768
    0
                .collect()
    769
    0
        }
    770
    771
        /// Convert completions to rustyline Pairs (complexity: 2)
    772
    0
        fn convert_to_pairs(&self, completions: Vec<String>) -> Vec<Pair> {
    773
    0
            completions.into_iter()
    774
    0
                .map(|s| Pair { display: s.clone(), replacement: s })
    775
    0
                .collect()
    776
    0
        }
    777
    }