mirror of
https://github.com/martinvonz/jj.git
synced 2024-12-29 16:07:55 +00:00
commands: move config code to config.rs
This commit is contained in:
parent
4d63e0bfa7
commit
8624d39a6b
2 changed files with 250 additions and 226 deletions
243
cli/src/commands/config.rs
Normal file
243
cli/src/commands/config.rs
Normal file
|
@ -0,0 +1,243 @@
|
|||
// Copyright 2020 The Jujutsu Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::io::Write;
|
||||
|
||||
use clap::builder::NonEmptyStringValueParser;
|
||||
use itertools::Itertools;
|
||||
use tracing::instrument;
|
||||
|
||||
use crate::cli_util::{
|
||||
get_new_config_file_path, run_ui_editor, serialize_config_value, user_error,
|
||||
write_config_value_to_file, CommandError, CommandHelper,
|
||||
};
|
||||
use crate::config::{AnnotatedValue, ConfigSource};
|
||||
use crate::ui::Ui;
|
||||
|
||||
#[derive(clap::Args, Clone, Debug)]
|
||||
#[command(group = clap::ArgGroup::new("config_level").multiple(false).required(true))]
|
||||
pub(crate) struct ConfigArgs {
|
||||
/// Target the user-level config
|
||||
#[arg(long, group = "config_level")]
|
||||
user: bool,
|
||||
|
||||
/// Target the repo-level config
|
||||
#[arg(long, group = "config_level")]
|
||||
repo: bool,
|
||||
}
|
||||
|
||||
impl ConfigArgs {
|
||||
fn get_source_kind(&self) -> ConfigSource {
|
||||
if self.user {
|
||||
ConfigSource::User
|
||||
} else if self.repo {
|
||||
ConfigSource::Repo
|
||||
} else {
|
||||
// Shouldn't be reachable unless clap ArgGroup is broken.
|
||||
panic!("No config_level provided");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Manage config options
|
||||
///
|
||||
/// Operates on jj configuration, which comes from the config file and
|
||||
/// environment variables.
|
||||
///
|
||||
/// For file locations, supported config options, and other details about jj
|
||||
/// config, see https://github.com/martinvonz/jj/blob/main/docs/config.md.
|
||||
#[derive(clap::Subcommand, Clone, Debug)]
|
||||
pub(crate) enum ConfigSubcommand {
|
||||
#[command(visible_alias("l"))]
|
||||
List(ConfigListArgs),
|
||||
#[command(visible_alias("g"))]
|
||||
Get(ConfigGetArgs),
|
||||
#[command(visible_alias("s"))]
|
||||
Set(ConfigSetArgs),
|
||||
#[command(visible_alias("e"))]
|
||||
Edit(ConfigEditArgs),
|
||||
}
|
||||
|
||||
/// List variables set in config file, along with their values.
|
||||
#[derive(clap::Args, Clone, Debug)]
|
||||
pub(crate) struct ConfigListArgs {
|
||||
/// An optional name of a specific config option to look up.
|
||||
#[arg(value_parser = NonEmptyStringValueParser::new())]
|
||||
pub name: Option<String>,
|
||||
/// Whether to explicitly include built-in default values in the list.
|
||||
#[arg(long)]
|
||||
pub include_defaults: bool,
|
||||
// TODO(#1047): Support --show-origin using LayeredConfigs.
|
||||
// TODO(#1047): Support ConfigArgs (--user or --repo).
|
||||
}
|
||||
|
||||
/// Get the value of a given config option.
|
||||
///
|
||||
/// Unlike `jj config list`, the result of `jj config get` is printed without
|
||||
/// extra formatting and therefore is usable in scripting. For example:
|
||||
///
|
||||
/// $ jj config list user.name
|
||||
/// user.name="Martin von Zweigbergk"
|
||||
/// $ jj config get user.name
|
||||
/// Martin von Zweigbergk
|
||||
#[derive(clap::Args, Clone, Debug)]
|
||||
#[command(verbatim_doc_comment)]
|
||||
pub(crate) struct ConfigGetArgs {
|
||||
#[arg(required = true)]
|
||||
name: String,
|
||||
}
|
||||
|
||||
/// Update config file to set the given option to a given value.
|
||||
#[derive(clap::Args, Clone, Debug)]
|
||||
pub(crate) struct ConfigSetArgs {
|
||||
#[arg(required = true)]
|
||||
name: String,
|
||||
#[arg(required = true)]
|
||||
value: String,
|
||||
#[clap(flatten)]
|
||||
config_args: ConfigArgs,
|
||||
}
|
||||
|
||||
/// Start an editor on a jj config file.
|
||||
#[derive(clap::Args, Clone, Debug)]
|
||||
pub(crate) struct ConfigEditArgs {
|
||||
#[clap(flatten)]
|
||||
pub config_args: ConfigArgs,
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
pub(crate) fn cmd_config(
|
||||
ui: &mut Ui,
|
||||
command: &CommandHelper,
|
||||
subcommand: &ConfigSubcommand,
|
||||
) -> Result<(), CommandError> {
|
||||
match subcommand {
|
||||
ConfigSubcommand::List(sub_args) => cmd_config_list(ui, command, sub_args),
|
||||
ConfigSubcommand::Get(sub_args) => cmd_config_get(ui, command, sub_args),
|
||||
ConfigSubcommand::Set(sub_args) => cmd_config_set(ui, command, sub_args),
|
||||
ConfigSubcommand::Edit(sub_args) => cmd_config_edit(ui, command, sub_args),
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
pub(crate) fn cmd_config_list(
|
||||
ui: &mut Ui,
|
||||
command: &CommandHelper,
|
||||
args: &ConfigListArgs,
|
||||
) -> Result<(), CommandError> {
|
||||
ui.request_pager();
|
||||
let name_path = args
|
||||
.name
|
||||
.as_ref()
|
||||
.map_or(vec![], |name| name.split('.').collect_vec());
|
||||
let values = command.resolved_config_values(&name_path)?;
|
||||
let mut wrote_values = false;
|
||||
for AnnotatedValue {
|
||||
path,
|
||||
value,
|
||||
source,
|
||||
is_overridden,
|
||||
} in &values
|
||||
{
|
||||
// Remove overridden values.
|
||||
// TODO(#1047): Allow printing overridden values via `--include-overridden`.
|
||||
if *is_overridden {
|
||||
continue;
|
||||
}
|
||||
// Skip built-ins if not included.
|
||||
if !args.include_defaults && *source == ConfigSource::Default {
|
||||
continue;
|
||||
}
|
||||
writeln!(
|
||||
ui.stdout(),
|
||||
"{}={}",
|
||||
path.join("."),
|
||||
serialize_config_value(value)
|
||||
)?;
|
||||
wrote_values = true;
|
||||
}
|
||||
if !wrote_values {
|
||||
// Note to stderr explaining why output is empty.
|
||||
if let Some(name) = &args.name {
|
||||
writeln!(ui.warning(), "No matching config key for {name}")?;
|
||||
} else {
|
||||
writeln!(ui.warning(), "No config to list")?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
pub(crate) fn cmd_config_get(
|
||||
ui: &mut Ui,
|
||||
command: &CommandHelper,
|
||||
args: &ConfigGetArgs,
|
||||
) -> Result<(), CommandError> {
|
||||
let value = command
|
||||
.settings()
|
||||
.config()
|
||||
.get_string(&args.name)
|
||||
.map_err(|err| match err {
|
||||
config::ConfigError::Type {
|
||||
origin,
|
||||
unexpected,
|
||||
expected,
|
||||
key,
|
||||
} => {
|
||||
let expected = format!("a value convertible to {expected}");
|
||||
// Copied from `impl fmt::Display for ConfigError`. We can't use
|
||||
// the `Display` impl directly because `expected` is required to
|
||||
// be a `'static str`.
|
||||
let mut buf = String::new();
|
||||
use std::fmt::Write;
|
||||
write!(buf, "invalid type: {unexpected}, expected {expected}").unwrap();
|
||||
if let Some(key) = key {
|
||||
write!(buf, " for key `{key}`").unwrap();
|
||||
}
|
||||
if let Some(origin) = origin {
|
||||
write!(buf, " in {origin}").unwrap();
|
||||
}
|
||||
CommandError::ConfigError(buf.to_string())
|
||||
}
|
||||
err => err.into(),
|
||||
})?;
|
||||
writeln!(ui.stdout(), "{value}")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
pub(crate) fn cmd_config_set(
|
||||
_ui: &mut Ui,
|
||||
command: &CommandHelper,
|
||||
args: &ConfigSetArgs,
|
||||
) -> Result<(), CommandError> {
|
||||
let config_path = get_new_config_file_path(&args.config_args.get_source_kind(), command)?;
|
||||
if config_path.is_dir() {
|
||||
return Err(user_error(format!(
|
||||
"Can't set config in path {path} (dirs not supported)",
|
||||
path = config_path.display()
|
||||
)));
|
||||
}
|
||||
write_config_value_to_file(&args.name, &args.value, &config_path)
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
pub(crate) fn cmd_config_edit(
|
||||
_ui: &mut Ui,
|
||||
command: &CommandHelper,
|
||||
args: &ConfigEditArgs,
|
||||
) -> Result<(), CommandError> {
|
||||
let config_path = get_new_config_file_path(&args.config_args.get_source_kind(), command)?;
|
||||
run_ui_editor(command.settings(), &config_path)
|
||||
}
|
|
@ -21,6 +21,7 @@ mod cat;
|
|||
mod checkout;
|
||||
mod chmod;
|
||||
mod commit;
|
||||
mod config;
|
||||
mod debug;
|
||||
mod git;
|
||||
mod operation;
|
||||
|
@ -32,7 +33,6 @@ use std::path::Path;
|
|||
use std::sync::Arc;
|
||||
use std::{fmt, fs, io};
|
||||
|
||||
use clap::builder::NonEmptyStringValueParser;
|
||||
use clap::parser::ValueSource;
|
||||
use clap::{ArgGroup, Command, CommandFactory, FromArgMatches, Subcommand};
|
||||
use indexmap::{IndexMap, IndexSet};
|
||||
|
@ -60,13 +60,11 @@ use maplit::{hashmap, hashset};
|
|||
use tracing::instrument;
|
||||
|
||||
use crate::cli_util::{
|
||||
self, check_stale_working_copy, get_new_config_file_path, print_checkout_stats,
|
||||
print_git_import_stats, resolve_multiple_nonempty_revsets,
|
||||
resolve_multiple_nonempty_revsets_default_single, run_ui_editor, serialize_config_value,
|
||||
short_commit_hash, user_error, user_error_with_hint, write_config_value_to_file, Args,
|
||||
CommandError, CommandHelper, LogContentFormat, RevisionArg, WorkspaceCommandHelper,
|
||||
self, check_stale_working_copy, print_checkout_stats, print_git_import_stats,
|
||||
resolve_multiple_nonempty_revsets, resolve_multiple_nonempty_revsets_default_single,
|
||||
run_ui_editor, short_commit_hash, user_error, user_error_with_hint, Args, CommandError,
|
||||
CommandHelper, LogContentFormat, RevisionArg, WorkspaceCommandHelper,
|
||||
};
|
||||
use crate::config::{AnnotatedValue, ConfigSource};
|
||||
use crate::diff_util::{self, DiffFormat, DiffFormatArgs};
|
||||
use crate::formatter::{Formatter, PlainTextFormatter};
|
||||
use crate::graphlog::{get_graphlog, Edge};
|
||||
|
@ -88,7 +86,7 @@ enum Commands {
|
|||
Chmod(chmod::ChmodArgs),
|
||||
Commit(commit::CommitArgs),
|
||||
#[command(subcommand)]
|
||||
Config(ConfigSubcommand),
|
||||
Config(config::ConfigSubcommand),
|
||||
#[command(subcommand)]
|
||||
Debug(debug::DebugCommands),
|
||||
Describe(DescribeArgs),
|
||||
|
@ -165,97 +163,6 @@ struct InitArgs {
|
|||
git_repo: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(clap::Args, Clone, Debug)]
|
||||
#[command(group = clap::ArgGroup::new("config_level").multiple(false).required(true))]
|
||||
struct ConfigArgs {
|
||||
/// Target the user-level config
|
||||
#[arg(long, group = "config_level")]
|
||||
user: bool,
|
||||
|
||||
/// Target the repo-level config
|
||||
#[arg(long, group = "config_level")]
|
||||
repo: bool,
|
||||
}
|
||||
|
||||
impl ConfigArgs {
|
||||
fn get_source_kind(&self) -> ConfigSource {
|
||||
if self.user {
|
||||
ConfigSource::User
|
||||
} else if self.repo {
|
||||
ConfigSource::Repo
|
||||
} else {
|
||||
// Shouldn't be reachable unless clap ArgGroup is broken.
|
||||
panic!("No config_level provided");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Manage config options
|
||||
///
|
||||
/// Operates on jj configuration, which comes from the config file and
|
||||
/// environment variables.
|
||||
///
|
||||
/// For file locations, supported config options, and other details about jj
|
||||
/// config, see https://github.com/martinvonz/jj/blob/main/docs/config.md.
|
||||
#[derive(clap::Subcommand, Clone, Debug)]
|
||||
enum ConfigSubcommand {
|
||||
#[command(visible_alias("l"))]
|
||||
List(ConfigListArgs),
|
||||
#[command(visible_alias("g"))]
|
||||
Get(ConfigGetArgs),
|
||||
#[command(visible_alias("s"))]
|
||||
Set(ConfigSetArgs),
|
||||
#[command(visible_alias("e"))]
|
||||
Edit(ConfigEditArgs),
|
||||
}
|
||||
|
||||
/// List variables set in config file, along with their values.
|
||||
#[derive(clap::Args, Clone, Debug)]
|
||||
struct ConfigListArgs {
|
||||
/// An optional name of a specific config option to look up.
|
||||
#[arg(value_parser = NonEmptyStringValueParser::new())]
|
||||
pub name: Option<String>,
|
||||
/// Whether to explicitly include built-in default values in the list.
|
||||
#[arg(long)]
|
||||
pub include_defaults: bool,
|
||||
// TODO(#1047): Support --show-origin using LayeredConfigs.
|
||||
// TODO(#1047): Support ConfigArgs (--user or --repo).
|
||||
}
|
||||
|
||||
/// Get the value of a given config option.
|
||||
///
|
||||
/// Unlike `jj config list`, the result of `jj config get` is printed without
|
||||
/// extra formatting and therefore is usable in scripting. For example:
|
||||
///
|
||||
/// $ jj config list user.name
|
||||
/// user.name="Martin von Zweigbergk"
|
||||
/// $ jj config get user.name
|
||||
/// Martin von Zweigbergk
|
||||
#[derive(clap::Args, Clone, Debug)]
|
||||
#[command(verbatim_doc_comment)]
|
||||
struct ConfigGetArgs {
|
||||
#[arg(required = true)]
|
||||
name: String,
|
||||
}
|
||||
|
||||
/// Update config file to set the given option to a given value.
|
||||
#[derive(clap::Args, Clone, Debug)]
|
||||
struct ConfigSetArgs {
|
||||
#[arg(required = true)]
|
||||
name: String,
|
||||
#[arg(required = true)]
|
||||
value: String,
|
||||
#[clap(flatten)]
|
||||
config_args: ConfigArgs,
|
||||
}
|
||||
|
||||
/// Start an editor on a jj config file.
|
||||
#[derive(clap::Args, Clone, Debug)]
|
||||
struct ConfigEditArgs {
|
||||
#[clap(flatten)]
|
||||
pub config_args: ConfigArgs,
|
||||
}
|
||||
|
||||
/// Stop tracking specified paths in the working copy
|
||||
#[derive(clap::Args, Clone, Debug)]
|
||||
struct UntrackArgs {
|
||||
|
@ -1169,132 +1076,6 @@ Set `ui.allow-init-native` to allow initializing a repo with the native backend.
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
fn cmd_config(
|
||||
ui: &mut Ui,
|
||||
command: &CommandHelper,
|
||||
subcommand: &ConfigSubcommand,
|
||||
) -> Result<(), CommandError> {
|
||||
match subcommand {
|
||||
ConfigSubcommand::List(sub_args) => cmd_config_list(ui, command, sub_args),
|
||||
ConfigSubcommand::Get(sub_args) => cmd_config_get(ui, command, sub_args),
|
||||
ConfigSubcommand::Set(sub_args) => cmd_config_set(ui, command, sub_args),
|
||||
ConfigSubcommand::Edit(sub_args) => cmd_config_edit(ui, command, sub_args),
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
fn cmd_config_list(
|
||||
ui: &mut Ui,
|
||||
command: &CommandHelper,
|
||||
args: &ConfigListArgs,
|
||||
) -> Result<(), CommandError> {
|
||||
ui.request_pager();
|
||||
let name_path = args
|
||||
.name
|
||||
.as_ref()
|
||||
.map_or(vec![], |name| name.split('.').collect_vec());
|
||||
let values = command.resolved_config_values(&name_path)?;
|
||||
let mut wrote_values = false;
|
||||
for AnnotatedValue {
|
||||
path,
|
||||
value,
|
||||
source,
|
||||
is_overridden,
|
||||
} in &values
|
||||
{
|
||||
// Remove overridden values.
|
||||
// TODO(#1047): Allow printing overridden values via `--include-overridden`.
|
||||
if *is_overridden {
|
||||
continue;
|
||||
}
|
||||
// Skip built-ins if not included.
|
||||
if !args.include_defaults && *source == ConfigSource::Default {
|
||||
continue;
|
||||
}
|
||||
writeln!(
|
||||
ui.stdout(),
|
||||
"{}={}",
|
||||
path.join("."),
|
||||
serialize_config_value(value)
|
||||
)?;
|
||||
wrote_values = true;
|
||||
}
|
||||
if !wrote_values {
|
||||
// Note to stderr explaining why output is empty.
|
||||
if let Some(name) = &args.name {
|
||||
writeln!(ui.warning(), "No matching config key for {name}")?;
|
||||
} else {
|
||||
writeln!(ui.warning(), "No config to list")?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
fn cmd_config_get(
|
||||
ui: &mut Ui,
|
||||
command: &CommandHelper,
|
||||
args: &ConfigGetArgs,
|
||||
) -> Result<(), CommandError> {
|
||||
let value = command
|
||||
.settings()
|
||||
.config()
|
||||
.get_string(&args.name)
|
||||
.map_err(|err| match err {
|
||||
config::ConfigError::Type {
|
||||
origin,
|
||||
unexpected,
|
||||
expected,
|
||||
key,
|
||||
} => {
|
||||
let expected = format!("a value convertible to {expected}");
|
||||
// Copied from `impl fmt::Display for ConfigError`. We can't use
|
||||
// the `Display` impl directly because `expected` is required to
|
||||
// be a `'static str`.
|
||||
let mut buf = String::new();
|
||||
use std::fmt::Write;
|
||||
write!(buf, "invalid type: {unexpected}, expected {expected}").unwrap();
|
||||
if let Some(key) = key {
|
||||
write!(buf, " for key `{key}`").unwrap();
|
||||
}
|
||||
if let Some(origin) = origin {
|
||||
write!(buf, " in {origin}").unwrap();
|
||||
}
|
||||
CommandError::ConfigError(buf.to_string())
|
||||
}
|
||||
err => err.into(),
|
||||
})?;
|
||||
writeln!(ui.stdout(), "{value}")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
fn cmd_config_set(
|
||||
_ui: &mut Ui,
|
||||
command: &CommandHelper,
|
||||
args: &ConfigSetArgs,
|
||||
) -> Result<(), CommandError> {
|
||||
let config_path = get_new_config_file_path(&args.config_args.get_source_kind(), command)?;
|
||||
if config_path.is_dir() {
|
||||
return Err(user_error(format!(
|
||||
"Can't set config in path {path} (dirs not supported)",
|
||||
path = config_path.display()
|
||||
)));
|
||||
}
|
||||
write_config_value_to_file(&args.name, &args.value, &config_path)
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
fn cmd_config_edit(
|
||||
_ui: &mut Ui,
|
||||
command: &CommandHelper,
|
||||
args: &ConfigEditArgs,
|
||||
) -> Result<(), CommandError> {
|
||||
let config_path = get_new_config_file_path(&args.config_args.get_source_kind(), command)?;
|
||||
run_ui_editor(command.settings(), &config_path)
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
fn cmd_untrack(
|
||||
ui: &mut Ui,
|
||||
|
@ -3673,7 +3454,7 @@ pub fn run_command(ui: &mut Ui, command_helper: &CommandHelper) -> Result<(), Co
|
|||
match &derived_subcommands {
|
||||
Commands::Version(sub_args) => cmd_version(ui, command_helper, sub_args),
|
||||
Commands::Init(sub_args) => cmd_init(ui, command_helper, sub_args),
|
||||
Commands::Config(sub_args) => cmd_config(ui, command_helper, sub_args),
|
||||
Commands::Config(sub_args) => config::cmd_config(ui, command_helper, sub_args),
|
||||
Commands::Checkout(sub_args) => checkout::cmd_checkout(ui, command_helper, sub_args),
|
||||
Commands::Untrack(sub_args) => cmd_untrack(ui, command_helper, sub_args),
|
||||
Commands::Files(sub_args) => cmd_files(ui, command_helper, sub_args),
|
||||
|
|
Loading…
Reference in a new issue