Skip to main content

zsh/extensions/
compinit_bg.rs

1//! Background compinit pre-warm — extension; no zsh C counterpart.
2use crate::compsys::cache::CompsysCache;
3use crate::compsys::CompInitResult;
4#[allow(unused_imports)]
5use crate::ported::vm_helper::ShellExecutor;
6#[allow(unused_imports)]
7use std::{collections::HashMap, env, path::PathBuf};
8
9/// Result from background compinit thread.
10/// Outcome of background `compinit` autoload.
11/// zshrs-original — Src/Modules/complete.c blocks on `compinit`
12/// inline. The Rust port runs it on the worker pool.
13pub struct CompInitBgResult {
14    /// `result` field.
15    pub result: CompInitResult,
16    /// `cache` field.
17    pub cache: CompsysCache,
18}
19
20// ===========================================================
21// Methods moved verbatim from src/ported/vm_helper because their
22// C counterpart's source file maps 1:1 to this Rust module.
23// Phase: drift
24// ===========================================================
25
26// BEGIN moved-from-exec-rs
27impl crate::ported::vm_helper::ShellExecutor {
28    /// Non-blocking drain of background compinit results.
29    /// Call this before any completion lookup (prompt, tab-complete, etc.).
30    /// If the background thread hasn't finished yet, this is a no-op.
31    pub fn drain_compinit_bg(&mut self) {
32        if let Some((rx, start)) = self.compinit_pending.take() {
33            match rx.try_recv() {
34                Ok(bg) => {
35                    let comps = bg.result.comps.len();
36                    self.set_assoc("_comps".to_string(), bg.result.comps.into_iter().collect());
37                    self.set_assoc(
38                        "_services".to_string(),
39                        bg.result.services.into_iter().collect(),
40                    );
41                    self.set_assoc(
42                        "_patcomps".to_string(),
43                        bg.result.patcomps.into_iter().collect(),
44                    );
45                    self.compsys_cache = Some(bg.cache);
46                    tracing::info!(
47                        wall_ms = start.elapsed().as_millis() as u64,
48                        comps,
49                        "compinit: background results merged"
50                    );
51                }
52                Err(std::sync::mpsc::TryRecvError::Empty) => {
53                    // Not ready yet — put the receiver back for next poll
54                    self.compinit_pending = Some((rx, start));
55                }
56                Err(std::sync::mpsc::TryRecvError::Disconnected) => {
57                    tracing::warn!("compinit: background thread died without sending results");
58                }
59            }
60        }
61    }
62    /// Traditional zsh compinit (--zsh-compat mode)
63    /// Uses fpath scanning, .zcompdump files, no SQLite
64    pub(crate) fn compinit_compat(
65        &mut self,
66        quiet: bool,
67        no_dump: bool,
68        dump_file: Option<String>,
69        use_cache: bool,
70    ) -> i32 {
71        let zdotdir = self
72            .scalar("ZDOTDIR")
73            .or_else(|| std::env::var("ZDOTDIR").ok())
74            .unwrap_or_else(|| std::env::var("HOME").unwrap_or_else(|_| "/tmp".to_string()));
75
76        let dump_path = dump_file
77            .map(PathBuf::from)
78            .unwrap_or_else(|| PathBuf::from(&zdotdir).join(".zcompdump"));
79
80        // -C: Try to use existing .zcompdump if valid
81        if use_cache
82            && dump_path.exists()
83            && crate::compsys::check_dump(&dump_path, &self.fpath, "zshrs-0.1.0")
84        {
85            // Valid dump - source it to load _comps
86            // For now, just rescan (proper impl would source the dump file)
87            if !quiet {
88                tracing::info!("compinit: .zcompdump valid, rescanning for compat");
89            }
90        }
91
92        // Full fpath scan (traditional zsh algorithm)
93        let result = crate::compsys::compinit(&self.fpath);
94
95        if !quiet {
96            tracing::info!(
97                functions = result.files_scanned,
98                comps = result.comps.len(),
99                dirs = result.dirs_scanned,
100                ms = result.scan_time_ms,
101                "compinit: fpath scan complete"
102            );
103        }
104
105        // Write .zcompdump unless -D
106        if !no_dump {
107            let _ = crate::compsys::compdump(&result, &dump_path, "zshrs-0.1.0");
108        }
109
110        // Set up _comps associative array
111        self.set_assoc(
112            "_comps".to_string(),
113            result.comps.clone().into_iter().collect(),
114        );
115        self.set_assoc(
116            "_services".to_string(),
117            result.services.clone().into_iter().collect(),
118        );
119        self.set_assoc(
120            "_patcomps".to_string(),
121            result.patcomps.clone().into_iter().collect(),
122        );
123
124        // No SQLite cache in compat mode
125        self.compsys_cache = None;
126
127        0
128    }
129}
130// END moved-from-exec-rs