1use crate::NushellPrompt;
2use log::{trace, warn};
3use nu_engine::ClosureEvalOnce;
4use nu_protocol::{
5 Config, PipelineData, Value,
6 engine::{EngineState, Stack},
7 report_shell_error,
8};
9use reedline::Prompt;
10
11pub(crate) const PROMPT_COMMAND: &str = "PROMPT_COMMAND";
13pub(crate) const PROMPT_COMMAND_RIGHT: &str = "PROMPT_COMMAND_RIGHT";
14pub(crate) const PROMPT_INDICATOR: &str = "PROMPT_INDICATOR";
15pub(crate) const PROMPT_INDICATOR_VI_INSERT: &str = "PROMPT_INDICATOR_VI_INSERT";
16pub(crate) const PROMPT_INDICATOR_VI_NORMAL: &str = "PROMPT_INDICATOR_VI_NORMAL";
17pub(crate) const PROMPT_MULTILINE_INDICATOR: &str = "PROMPT_MULTILINE_INDICATOR";
18pub(crate) const TRANSIENT_PROMPT_COMMAND: &str = "TRANSIENT_PROMPT_COMMAND";
19pub(crate) const TRANSIENT_PROMPT_COMMAND_RIGHT: &str = "TRANSIENT_PROMPT_COMMAND_RIGHT";
20pub(crate) const TRANSIENT_PROMPT_INDICATOR: &str = "TRANSIENT_PROMPT_INDICATOR";
21pub(crate) const TRANSIENT_PROMPT_INDICATOR_VI_INSERT: &str =
22 "TRANSIENT_PROMPT_INDICATOR_VI_INSERT";
23pub(crate) const TRANSIENT_PROMPT_INDICATOR_VI_NORMAL: &str =
24 "TRANSIENT_PROMPT_INDICATOR_VI_NORMAL";
25pub(crate) const TRANSIENT_PROMPT_MULTILINE_INDICATOR: &str =
26 "TRANSIENT_PROMPT_MULTILINE_INDICATOR";
27
28pub(crate) const PRE_EXECUTION_MARKER: &str = "\x1b]133;C\x1b\\";
36pub(crate) const POST_EXECUTION_MARKER_PREFIX: &str = "\x1b]133;D;";
37pub(crate) const POST_EXECUTION_MARKER_SUFFIX: &str = "\x1b\\";
38
39pub(crate) const VSCODE_PRE_EXECUTION_MARKER: &str = "\x1b]633;C\x1b\\";
41pub(crate) const VSCODE_POST_EXECUTION_MARKER_PREFIX: &str = "\x1b]633;D;";
42pub(crate) const VSCODE_POST_EXECUTION_MARKER_SUFFIX: &str = "\x1b\\";
43pub(crate) const VSCODE_COMMANDLINE_MARKER_PREFIX: &str = "\x1b]633;E;";
44pub(crate) const VSCODE_COMMANDLINE_MARKER_SUFFIX: &str = "\x1b\\";
45pub(crate) const VSCODE_CWD_PROPERTY_MARKER_PREFIX: &str = "\x1b]633;P;Cwd=";
46pub(crate) const VSCODE_CWD_PROPERTY_MARKER_SUFFIX: &str = "\x1b\\";
47
48pub(crate) const RESET_APPLICATION_MODE: &str = "\x1b[?1l";
50
51fn get_prompt_string(
52 prompt: &str,
53 config: &Config,
54 engine_state: &EngineState,
55 stack: &mut Stack,
56) -> Option<String> {
57 let mut output = match stack.get_env_var(engine_state, prompt)? {
58 Value::String { val, .. } => val.clone(),
59 Value::Closure { val, .. } => {
60 let result = ClosureEvalOnce::new(engine_state, stack, val.as_ref().clone())
61 .run_with_input(PipelineData::empty());
62
63 trace!(
64 "get_prompt_string (block) {}:{}:{}",
65 file!(),
66 line!(),
67 column!()
68 );
69
70 let result_string = result
71 .map_err(|err| report_shell_error(None, engine_state, &err))
72 .ok()
73 .and_then(|pd| pd.collect_string("", config).ok());
74
75 result_string?
76 }
77 _ => return None,
78 };
79
80 if output.is_empty() && prompt == PROMPT_COMMAND_RIGHT {
83 output.insert_str(0, "\x1b[0m")
84 };
85
86 warn!("{}:{}:{} {:?}", file!(), line!(), column!(), output);
88
89 Some(output)
90}
91
92pub fn update_prompt(
93 config: &Config,
94 engine_state: &EngineState,
95 stack: &mut Stack,
96 nu_prompt: &mut NushellPrompt,
97) {
98 let left_prompt_string = get_prompt_string(PROMPT_COMMAND, config, engine_state, stack);
100
101 let right_prompt_string = get_prompt_string(PROMPT_COMMAND_RIGHT, config, engine_state, stack);
102
103 let prompt_indicator_string = get_prompt_string(PROMPT_INDICATOR, config, engine_state, stack);
104
105 let prompt_multiline_string =
106 get_prompt_string(PROMPT_MULTILINE_INDICATOR, config, engine_state, stack);
107
108 let prompt_vi_insert_string =
109 get_prompt_string(PROMPT_INDICATOR_VI_INSERT, config, engine_state, stack);
110
111 let prompt_vi_normal_string =
112 get_prompt_string(PROMPT_INDICATOR_VI_NORMAL, config, engine_state, stack);
113
114 nu_prompt.update_all_prompt_strings(
116 left_prompt_string,
117 right_prompt_string,
118 prompt_indicator_string,
119 prompt_multiline_string,
120 (prompt_vi_insert_string, prompt_vi_normal_string),
121 config.render_right_prompt_on_last_line,
122 );
123 trace!("update_prompt {}:{}:{}", file!(), line!(), column!());
124}
125
126pub(crate) fn make_transient_prompt(
130 config: &Config,
131 engine_state: &EngineState,
132 stack: &mut Stack,
133 nu_prompt: &NushellPrompt,
134) -> Box<dyn Prompt> {
135 let mut nu_prompt = nu_prompt.clone();
136
137 if let Some(s) = get_prompt_string(TRANSIENT_PROMPT_COMMAND, config, engine_state, stack) {
138 nu_prompt.update_prompt_left(Some(s))
139 }
140
141 if let Some(s) = get_prompt_string(TRANSIENT_PROMPT_COMMAND_RIGHT, config, engine_state, stack)
142 {
143 nu_prompt.update_prompt_right(Some(s), config.render_right_prompt_on_last_line)
144 }
145
146 if let Some(s) = get_prompt_string(TRANSIENT_PROMPT_INDICATOR, config, engine_state, stack) {
147 nu_prompt.update_prompt_indicator(Some(s))
148 }
149 if let Some(s) = get_prompt_string(
150 TRANSIENT_PROMPT_INDICATOR_VI_INSERT,
151 config,
152 engine_state,
153 stack,
154 ) {
155 nu_prompt.update_prompt_vi_insert(Some(s))
156 }
157 if let Some(s) = get_prompt_string(
158 TRANSIENT_PROMPT_INDICATOR_VI_NORMAL,
159 config,
160 engine_state,
161 stack,
162 ) {
163 nu_prompt.update_prompt_vi_normal(Some(s))
164 }
165
166 if let Some(s) = get_prompt_string(
167 TRANSIENT_PROMPT_MULTILINE_INDICATOR,
168 config,
169 engine_state,
170 stack,
171 ) {
172 nu_prompt.update_prompt_multiline(Some(s))
173 }
174
175 Box::new(nu_prompt)
176}
177
178#[cfg(test)]
179mod tests {
180 use super::*;
181 use nu_protocol::Span;
182
183 #[test]
184 fn update_prompt_does_not_embed_osc_markers() {
185 let mut config = Config::default();
186 config.shell_integration.osc133 = true;
187
188 let engine_state = EngineState::new();
189 let mut stack = Stack::new();
190 stack.add_env_var(
191 PROMPT_COMMAND.into(),
192 Value::string("test", Span::test_data()),
193 );
194
195 let mut nu_prompt = NushellPrompt::new();
196
197 update_prompt(&config, &engine_state, &mut stack, &mut nu_prompt);
198
199 assert_eq!(nu_prompt.render_prompt_left(), "test");
200 }
201}