use super::*;
pub(crate) struct Completer<'run, 'src> {
config: Config,
current: &'run str,
justfile: Justfile<'src>,
}
impl<'run, 'src> Completer<'run, 'src> {
fn candidate_recipes(&self) -> Vec<CompletionCandidate> {
let mut candidates = Vec::new();
for recipe in self.justfile.public_recipes_recursive(&self.config) {
let path = recipe.recipe_path().to_string();
if path.starts_with(self.current) {
candidates.push(CompletionCandidate::new(path).help(recipe.doc.as_ref().map(Into::into)));
}
}
if self.config.complete_aliases {
for (alias, modulepath) in self.justfile.public_aliases_recursive(&self.config) {
let name = modulepath.join(alias.name.lexeme());
if name.starts_with(self.current) {
candidates
.push(CompletionCandidate::new(name).help(alias.target.doc.as_ref().map(Into::into)));
}
}
}
candidates
}
pub(crate) fn complete_argument(current: &OsStr) -> Vec<CompletionCandidate> {
let loader = Loader::new();
let Some(completer) = Completer::new(current, &loader) else {
return Vec::new();
};
let mut candidates = completer.candidate_recipes();
for (name, binding) in &completer.justfile.assignments {
if !binding.private && name.starts_with(completer.current) {
candidates.push(CompletionCandidate::new(format!("{name}=")));
}
}
candidates.extend(PathCompleter::any().complete(current));
candidates
}
pub(crate) fn complete_group(current: &OsStr) -> Vec<CompletionCandidate> {
let loader = Loader::new();
let Some(completer) = Completer::new(current, &loader) else {
return Vec::new();
};
completer
.justfile
.public_groups(&completer.config)
.into_iter()
.filter(|group| group.starts_with(completer.current))
.map(CompletionCandidate::new)
.collect()
}
pub(crate) fn complete_recipe(current: &OsStr) -> Vec<CompletionCandidate> {
let loader = Loader::new();
let Some(completer) = Completer::new(current, &loader) else {
return Vec::new();
};
completer.candidate_recipes()
}
pub(crate) fn complete_variable(current: &OsStr) -> Vec<CompletionCandidate> {
let loader = Loader::new();
let Some(completer) = Completer::new(current, &loader) else {
return Vec::new();
};
completer
.justfile
.assignments
.into_iter()
.filter(|(name, binding)| !binding.private && name.starts_with(completer.current))
.map(|(name, _)| CompletionCandidate::new(name))
.collect()
}
fn config() -> Option<Config> {
let mut args = env::args_os().collect::<Vec<OsString>>();
args.drain(1..3);
let matches = Arguments::command()
.ignore_errors(true)
.try_get_matches_from(args)
.ok()?;
let arguments = Arguments::from_arg_matches(&matches).ok()?;
Config::from_arguments(arguments).ok()
}
fn new(current: &'run OsStr, loader: &'src Loader) -> Option<Self> {
Self::try_new(current.to_str()?, loader).ok()
}
fn try_new(current: &'run str, loader: &'src Loader) -> RunResult<'src, Self> {
let config = if let Some(config) = Self::config() {
config
} else {
Config::new()?
};
let search = Search::search(&config)?;
let compilation = Compiler::compile(&config, loader, &search.justfile)?;
Ok(Completer {
config,
current,
justfile: compilation.justfile,
})
}
}