zshrs 0.11.18

The first compiled Unix shell — bytecode VM, worker pool, AOP intercept, Rkyv caching
Documentation
//! Port of `_complete` from `Completion/Base/Completer/_complete`.
//!
//! Full upstream body (144 lines, abridged):
//! ```text
//! sh:  1  #autoload
//! sh:  7  local comp name oldcontext ret=1 service
//! sh: 12  if [[ -n "$compcontext" ]]; then  # custom context dispatch
//! sh: 14    type-of-compcontext (array | assoc | tag:descr:action | scalar)
//! sh: 92    return
//! sh: 95  fi
//! sh: 96  comp="$_comps[-first-]"
//! sh: 97  if [[ -n "$comp" ]]; then … run -first-, exit if _compskip == all …
//! sh:108  [[ -n $compstate[vared] ]] && compstate[context]=vared
//! sh:111  if [[ "$compstate[context]" = command ]]; then
//! sh:112    _normal -s && ret=0
//! sh:113  else
//! sh:115    local cname="-${compstate[context]:s/_/-/}-"
//! sh:117    comp="$_comps[$cname]"
//! sh:122    if [[ -z "$comp" ]]; then
//! sh:127      comp="$_comps[-default-]"
//! sh:130    fi
//! sh:131    [[ -n "$comp" ]] && eval "$comp" && ret=0
//! sh:132  fi
//! sh:134  _compskip=
//! sh:136  return ret
//! ```
//!
//! Top-level completer dispatching to context-specific `$_comps`
//! entries. Heavy `compcontext`-based dispatch left as a thin
//! delegation since most live invocations skip that branch.

use crate::compsys::ported::_message::_message;
use crate::compsys::ported::_normal::_normal;
use crate::ported::exec_hooks::dispatch_function_call;
use crate::ported::params::{getaparam, getsparam, setsparam};
use crate::ported::zle::compcore::{get_compstate_str, set_compstate_str};

/// Helper: flat assoc lookup.
fn assoc_get(name: &str, key: &str) -> Option<String> {
    let arr = getaparam(name)?;
    arr.chunks(2)
        .find(|kv| kv.first().map(|k| k == key).unwrap_or(false))
        .and_then(|kv| kv.get(1).cloned())
}

/// `_complete` — primary `completer` entry: dispatches to per-context
/// `$_comps` entries based on `$compstate[context]`.
pub fn _complete() -> i32 {
    let mut ret: i32 = 1;
    let oldcontext = getsparam("curcontext").unwrap_or_default();

    // sh:12  compcontext custom dispatch
    let compcontext = getsparam("compcontext").unwrap_or_default();
    if !compcontext.is_empty() {
        // sh:30  tag:descr:action form
        if compcontext.matches(':').count() >= 2 {
            let mut parts = compcontext.splitn(3, ':');
            let tag = parts
                .next()
                .filter(|s| !s.is_empty())
                .map(|s| s.to_string())
                .unwrap_or_else(|| "values".to_string());
            let descr = parts
                .next()
                .filter(|s| !s.is_empty())
                .map(|s| s.to_string())
                .unwrap_or_else(|| "value".to_string());
            let action = parts.next().unwrap_or("").to_string();
            if action.trim().is_empty() {
                return _message(&["-e".to_string(), tag, descr]);
            }
            // Fall through to the generic dispatch via _alternative-
            //   style action: treat `action` as a command.
            let parts: Vec<String> = action
                .split_whitespace()
                .map(|s| s.to_string())
                .collect();
            if let Some((cmd, rest)) = parts.split_first() {
                return dispatch_function_call(cmd, rest).unwrap_or(1);
            }
            return 1;
        }
        // sh:86  scalar compcontext — look up $_comps[$compcontext]
        let comp = assoc_get("_comps", &compcontext).unwrap_or_default();
        if !comp.is_empty() {
            return dispatch_function_call(&comp, &[]).unwrap_or(1);
        }
        return 1;
    }

    // sh:96-107  -first- entry
    let first_comp = assoc_get("_comps", "-first-").unwrap_or_default();
    if !first_comp.is_empty() {
        let service =
            assoc_get("_services", "-first-").unwrap_or_else(|| "-first-".to_string());
        let _ = setsparam("service", &service);
        if dispatch_function_call(&first_comp, &[]).unwrap_or(1) == 0 {
            ret = 0;
        }
        if getsparam("_compskip").as_deref() == Some("all") {
            let _ = setsparam("_compskip", "");
            return ret;
        }
    }

    // sh:108  vared override
    let vared = get_compstate_str("vared").unwrap_or_default();
    if !vared.is_empty() {
        set_compstate_str("context", "vared");
    }

    // sh:111-132
    let context = get_compstate_str("context").unwrap_or_default();
    if context == "command" {
        let _ = setsparam("curcontext", &oldcontext);
        if _normal(&["-s".to_string()]) == 0 {
            ret = 0;
        }
    } else {
        // sh:115 — `-${context//_/-}-`
        let cname = format!("-{}-", context.replace('_', "-"));
        let mut comp = assoc_get("_comps", &cname).unwrap_or_default();
        let service = assoc_get("_services", &cname).unwrap_or_else(|| cname.clone());
        let _ = setsparam("service", &service);
        // sh:122
        if comp.is_empty() {
            let cs = getsparam("_compskip").unwrap_or_default();
            if cs.contains("default") {
                let _ = setsparam("_compskip", "");
                return 1;
            }
            comp = assoc_get("_comps", "-default-").unwrap_or_default();
            let default_service =
                assoc_get("_services", "-default-").unwrap_or_else(|| "-default-".to_string());
            let _ = setsparam("service", &default_service);
        }
        if !comp.is_empty() {
            if dispatch_function_call(&comp, &[]).unwrap_or(1) == 0 {
                ret = 0;
            }
        }
    }
    let _ = setsparam("_compskip", "");
    ret
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn returns_one_without_executor() {
        let _g = crate::test_util::global_state_lock();
        let _ = setsparam("compcontext", "");
        set_compstate_str("context", "command");
        crate::ported::params::setaparam("_comps", Vec::new());
        let _r = _complete();
    }
}