Skip to main content

zsh/extensions/
compinit_bg.rs

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