nu_command/env/config/
config_.rs

1use nu_cmd_base::util::get_editor;
2use nu_engine::{command_prelude::*, env_to_strings, get_full_help};
3use nu_protocol::shell_error::io::IoError;
4use nu_system::ForegroundChild;
5
6#[cfg(feature = "os")]
7use nu_protocol::process::PostWaitCallback;
8
9#[derive(Clone)]
10pub struct ConfigMeta;
11
12impl Command for ConfigMeta {
13    fn name(&self) -> &str {
14        "config"
15    }
16
17    fn signature(&self) -> Signature {
18        Signature::build(self.name())
19            .category(Category::Env)
20            .input_output_types(vec![(Type::Nothing, Type::String)])
21    }
22
23    fn description(&self) -> &str {
24        "Edit nushell configuration files."
25    }
26
27    fn extra_description(&self) -> &str {
28        "You must use one of the following subcommands. Using this command as-is will only produce this help message."
29    }
30
31    fn run(
32        &self,
33        engine_state: &EngineState,
34        stack: &mut Stack,
35        call: &Call,
36        _input: PipelineData,
37    ) -> Result<PipelineData, ShellError> {
38        Ok(Value::string(get_full_help(self, engine_state, stack), call.head).into_pipeline_data())
39    }
40
41    fn search_terms(&self) -> Vec<&str> {
42        vec!["options", "setup"]
43    }
44}
45
46#[cfg(not(feature = "os"))]
47pub(super) fn start_editor(
48    _: &'static str,
49    _: &EngineState,
50    _: &mut Stack,
51    call: &Call,
52) -> Result<PipelineData, ShellError> {
53    Err(ShellError::DisabledOsSupport {
54        msg: "Running external commands is not available without OS support.".to_string(),
55        span: Some(call.head),
56    })
57}
58
59#[cfg(feature = "os")]
60pub(super) fn start_editor(
61    config_path: &'static str,
62    engine_state: &EngineState,
63    stack: &mut Stack,
64    call: &Call,
65) -> Result<PipelineData, ShellError> {
66    // Find the editor executable.
67
68    let (editor_name, editor_args) = get_editor(engine_state, stack, call.head)?;
69    let paths = nu_engine::env::path_str(engine_state, stack, call.head)?;
70    let cwd = engine_state.cwd(Some(stack))?;
71    let editor_executable =
72        crate::which(&editor_name, &paths, cwd.as_ref()).ok_or(ShellError::ExternalCommand {
73            label: format!("`{editor_name}` not found"),
74            help: "Failed to find the editor executable".into(),
75            span: call.head,
76        })?;
77
78    let Some(config_path) = engine_state.get_config_path(config_path) else {
79        return Err(ShellError::GenericError {
80            error: format!("Could not find $nu.{config_path}"),
81            msg: format!("Could not find $nu.{config_path}"),
82            span: None,
83            help: None,
84            inner: vec![],
85        });
86    };
87    let config_path = config_path.to_string_lossy().to_string();
88
89    // Create the command.
90    let mut command = std::process::Command::new(editor_executable);
91
92    // Configure PWD.
93    command.current_dir(cwd);
94
95    // Configure environment variables.
96    let envs = env_to_strings(engine_state, stack)?;
97    command.env_clear();
98    command.envs(envs);
99
100    // Configure args.
101    command.arg(config_path);
102    command.args(editor_args);
103
104    // Spawn the child process. On Unix, also put the child process to
105    // foreground if we're in an interactive session.
106    #[cfg(windows)]
107    let child = ForegroundChild::spawn(command);
108    #[cfg(unix)]
109    let child = ForegroundChild::spawn(
110        command,
111        engine_state.is_interactive,
112        engine_state.is_background_job(),
113        &engine_state.pipeline_externals_state,
114    );
115
116    let child = child.map_err(|err| {
117        IoError::new_with_additional_context(
118            err,
119            call.head,
120            None,
121            "Could not spawn foreground child",
122        )
123    })?;
124
125    let post_wait_callback = PostWaitCallback::for_job_control(engine_state, None, None);
126
127    // Wrap the output into a `PipelineData::ByteStream`.
128    let child = nu_protocol::process::ChildProcess::new(
129        child,
130        None,
131        false,
132        call.head,
133        Some(post_wait_callback),
134    )?;
135
136    Ok(PipelineData::ByteStream(
137        ByteStream::child(child, call.head),
138        None,
139    ))
140}