shannon_nu_command/env/config/
config_.rs1use nu_cmd_base::util::get_editor;
2use nu_engine::{command_prelude::*, env_to_strings, get_full_help};
3use nu_protocol::{PipelineMetadata, shell_error::generic::GenericError, shell_error::io::IoError};
4use nu_system::ForegroundChild;
5use nu_utils::ConfigFileKind;
6
7#[cfg(feature = "os")]
8use nu_protocol::process::PostWaitCallback;
9
10#[derive(Clone)]
11pub struct ConfigMeta;
12
13impl Command for ConfigMeta {
14 fn name(&self) -> &str {
15 "config"
16 }
17
18 fn signature(&self) -> Signature {
19 Signature::build(self.name())
20 .category(Category::Env)
21 .input_output_types(vec![(Type::Nothing, Type::String)])
22 }
23
24 fn description(&self) -> &str {
25 "Edit nushell configuration files."
26 }
27
28 fn extra_description(&self) -> &str {
29 "You must use one of the following subcommands. Using this command as-is will only produce this help message."
30 }
31
32 fn run(
33 &self,
34 engine_state: &EngineState,
35 stack: &mut Stack,
36 call: &Call,
37 _input: PipelineData,
38 ) -> Result<PipelineData, ShellError> {
39 Ok(Value::string(get_full_help(self, engine_state, stack), call.head).into_pipeline_data())
40 }
41
42 fn search_terms(&self) -> Vec<&str> {
43 vec!["options", "setup"]
44 }
45}
46
47#[cfg(not(feature = "os"))]
48pub(super) fn start_editor(
49 _: ConfigFileKind,
50 _: &EngineState,
51 _: &mut Stack,
52 call: &Call,
53) -> Result<PipelineData, ShellError> {
54 Err(ShellError::DisabledOsSupport {
55 msg: "Running external commands is not available without OS support.".to_string(),
56 span: call.head,
57 })
58}
59
60#[cfg(feature = "os")]
61pub(super) fn start_editor(
62 kind: ConfigFileKind,
63 engine_state: &EngineState,
64 stack: &mut Stack,
65 call: &Call,
66) -> Result<PipelineData, ShellError> {
67 let (editor_name, editor_args) = get_editor(engine_state, stack, call.head)?;
70 let paths = nu_engine::env::path_str(engine_state, stack, call.head)?;
71 let cwd = engine_state.cwd(Some(stack))?;
72 let editor_executable =
73 crate::which(&editor_name, &paths, cwd.as_ref()).ok_or(ShellError::ExternalCommand {
74 label: format!("`{editor_name}` not found"),
75 help: "Failed to find the editor executable".into(),
76 span: call.head,
77 })?;
78
79 let nu_const_path = kind.nu_const_path();
80 let Some(config_path) = engine_state.get_config_path(nu_const_path) else {
81 return Err(ShellError::Generic(GenericError::new_internal(
82 format!("Could not find $nu.{nu_const_path}"),
83 format!("Could not find $nu.{nu_const_path}"),
84 )));
85 };
86 let config_path = config_path.to_string_lossy().to_string();
87
88 let mut command = std::process::Command::new(editor_executable);
90
91 command.current_dir(cwd);
93
94 let envs = env_to_strings(engine_state, stack)?;
96 command.env_clear();
97 command.envs(envs);
98
99 command.args(editor_args);
101 command.arg(config_path);
102
103 #[cfg(windows)]
106 let child = ForegroundChild::spawn(command);
107 #[cfg(unix)]
108 let child = ForegroundChild::spawn(
109 command,
110 engine_state.is_interactive,
111 engine_state.is_background_job(),
112 &engine_state.pipeline_externals_state,
113 );
114
115 let child = child.map_err(|err| {
116 IoError::new_with_additional_context(
117 err,
118 call.head,
119 None,
120 "Could not spawn foreground child",
121 )
122 })?;
123
124 let post_wait_callback = PostWaitCallback::for_job_control(engine_state, None, None);
125
126 let child = nu_protocol::process::ChildProcess::new(
128 child,
129 None,
130 false,
131 call.head,
132 Some(post_wait_callback),
133 )?;
134
135 Ok(PipelineData::byte_stream(
136 ByteStream::child(child, call.head),
137 None,
138 ))
139}
140
141pub(super) fn handle_call(
142 kind: ConfigFileKind,
143 engine_state: &EngineState,
144 stack: &mut Stack,
145 call: &Call,
146) -> Result<PipelineData, ShellError> {
147 let default_flag = call.has_flag(engine_state, stack, "default")?;
148 let doc_flag = call.has_flag(engine_state, stack, "doc")?;
149
150 Ok(match (default_flag, doc_flag) {
151 (false, false) => {
152 return super::config_::start_editor(kind, engine_state, stack, call);
153 }
154 (true, true) => {
155 return Err(ShellError::IncompatibleParameters {
156 left_message: "can't use `--default` at the same time".into(),
157 left_span: call.get_flag_span(stack, "default").expect("has flag"),
158 right_message: "because of `--doc`".into(),
159 right_span: call.get_flag_span(stack, "doc").expect("has flag"),
160 });
161 }
162 (true, false) => kind.default(),
163 (false, true) => kind.doc(),
164 }
165 .into_value(call.head)
166 .into_pipeline_data_with_metadata(
167 PipelineMetadata::default().with_content_type(Some("application/x-nuscript".into())),
168 ))
169}