use clap_complete::ArgValueCandidates;
use jj_lib::config::ConfigNamePathBuf;
use jj_lib::config::ConfigSource;
use jj_lib::settings::UserSettings;
use tracing::instrument;
use super::ConfigLevelArgs;
use crate::cli_util::CommandHelper;
use crate::command_error::CommandError;
use crate::complete;
use crate::config::AnnotatedValue;
use crate::config::resolved_config_values;
use crate::generic_templater;
use crate::generic_templater::GenericTemplateLanguage;
use crate::templater::TemplatePropertyExt as _;
use crate::templater::TemplateRenderer;
use crate::ui::Ui;
#[derive(clap::Args, Clone, Debug)]
#[command(mut_group("config_level", |g| g.required(false)))]
pub struct ConfigListArgs {
#[arg(add = ArgValueCandidates::new(complete::config_keys))]
pub name: Option<ConfigNamePathBuf>,
#[arg(long, conflicts_with = "config_level")]
pub include_defaults: bool,
#[arg(long)]
pub include_overridden: bool,
#[command(flatten)]
pub level: ConfigLevelArgs,
#[arg(long, short = 'T', verbatim_doc_comment)]
#[arg(add = ArgValueCandidates::new(complete::template_aliases))]
template: Option<String>,
}
#[instrument(skip_all)]
pub async fn cmd_config_list(
ui: &mut Ui,
command: &CommandHelper,
args: &ConfigListArgs,
) -> Result<(), CommandError> {
let template: TemplateRenderer<AnnotatedValue> = {
let language = config_template_language(command.settings());
let text = match &args.template {
Some(value) => value.to_owned(),
None => command.settings().get_string("templates.config_list")?,
};
command
.parse_template(ui, &language, &text)?
.labeled(["config_list"])
};
let name_path = args.name.clone().unwrap_or_else(ConfigNamePathBuf::root);
let mut annotated_values = resolved_config_values(command.settings().config(), &name_path);
if let Some(target_source) = args.level.get_source_kind() {
annotated_values.retain(|annotated| annotated.source == target_source);
} else if !args.include_defaults {
annotated_values.retain(|annotated| annotated.source != ConfigSource::Default);
}
if !args.include_overridden {
annotated_values.retain(|annotated| !annotated.is_overridden);
}
if !annotated_values.is_empty() {
ui.request_pager();
let mut formatter = ui.stdout_formatter();
for annotated in &annotated_values {
template.format(annotated, formatter.as_mut())?;
}
} else {
if let Some(name) = &args.name {
writeln!(ui.warning_default(), "No matching config key for {name}")?;
} else {
writeln!(ui.warning_default(), "No config to list")?;
}
}
Ok(())
}
type ConfigTemplateLanguage = GenericTemplateLanguage<'static, AnnotatedValue>;
generic_templater::impl_self_property_wrapper!(AnnotatedValue);
fn config_template_language(settings: &UserSettings) -> ConfigTemplateLanguage {
let mut language = ConfigTemplateLanguage::new(settings);
language.add_keyword("name", |self_property| {
let out_property = self_property.map(|annotated| annotated.name.to_string());
Ok(out_property.into_dyn_wrapped())
});
language.add_keyword("value", |self_property| {
let out_property = self_property.map(|annotated| annotated.value.decorated("", ""));
Ok(out_property.into_dyn_wrapped())
});
language.add_keyword("source", |self_property| {
let out_property = self_property.map(|annotated| annotated.source.to_string());
Ok(out_property.into_dyn_wrapped())
});
language.add_keyword("path", |self_property| {
let out_property = self_property.map(|annotated| {
annotated
.path
.as_ref()
.map_or_else(String::new, |path| path.to_string_lossy().into_owned())
});
Ok(out_property.into_dyn_wrapped())
});
language.add_keyword("overridden", |self_property| {
let out_property = self_property.map(|annotated| annotated.is_overridden);
Ok(out_property.into_dyn_wrapped())
});
language
}