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 stack
58 .get_env_var(engine_state, prompt)
59 .and_then(|v| match v {
60 Value::Closure { val, .. } => {
61 let result = ClosureEvalOnce::new(engine_state, stack, val.as_ref().clone())
62 .run_with_input(PipelineData::empty());
63
64 trace!(
65 "get_prompt_string (block) {}:{}:{}",
66 file!(),
67 line!(),
68 column!()
69 );
70
71 result
72 .map_err(|err| {
73 report_shell_error(None, engine_state, &err);
74 })
75 .ok()
76 }
77 Value::String { .. } => Some(PipelineData::value(v.clone(), None)),
78 _ => None,
79 })
80 .and_then(|pipeline_data| {
81 let output = pipeline_data.collect_string("", config).ok();
82 let ansi_output = output.map(|mut x| {
83 if x.is_empty() && prompt == PROMPT_COMMAND_RIGHT {
86 x.insert_str(0, "\x1b[0m")
87 };
88
89 x
90 });
91 warn!("{}:{}:{} {:?}", file!(), line!(), column!(), ansi_output);
93
94 ansi_output
95 })
96}
97
98pub fn update_prompt(
99 config: &Config,
100 engine_state: &EngineState,
101 stack: &mut Stack,
102 nu_prompt: &mut NushellPrompt,
103) {
104 let left_prompt_string = get_prompt_string(PROMPT_COMMAND, config, engine_state, stack);
106
107 let right_prompt_string = get_prompt_string(PROMPT_COMMAND_RIGHT, config, engine_state, stack);
108
109 let prompt_indicator_string = get_prompt_string(PROMPT_INDICATOR, config, engine_state, stack);
110
111 let prompt_multiline_string =
112 get_prompt_string(PROMPT_MULTILINE_INDICATOR, config, engine_state, stack);
113
114 let prompt_vi_insert_string =
115 get_prompt_string(PROMPT_INDICATOR_VI_INSERT, config, engine_state, stack);
116
117 let prompt_vi_normal_string =
118 get_prompt_string(PROMPT_INDICATOR_VI_NORMAL, config, engine_state, stack);
119
120 nu_prompt.update_all_prompt_strings(
122 left_prompt_string,
123 right_prompt_string,
124 prompt_indicator_string,
125 prompt_multiline_string,
126 (prompt_vi_insert_string, prompt_vi_normal_string),
127 config.render_right_prompt_on_last_line,
128 );
129 trace!("update_prompt {}:{}:{}", file!(), line!(), column!());
130}
131
132pub(crate) fn make_transient_prompt(
136 config: &Config,
137 engine_state: &EngineState,
138 stack: &mut Stack,
139 nu_prompt: &NushellPrompt,
140) -> Box<dyn Prompt> {
141 let mut nu_prompt = nu_prompt.clone();
142
143 if let Some(s) = get_prompt_string(TRANSIENT_PROMPT_COMMAND, config, engine_state, stack) {
144 nu_prompt.update_prompt_left(Some(s))
145 }
146
147 if let Some(s) = get_prompt_string(TRANSIENT_PROMPT_COMMAND_RIGHT, config, engine_state, stack)
148 {
149 nu_prompt.update_prompt_right(Some(s), config.render_right_prompt_on_last_line)
150 }
151
152 if let Some(s) = get_prompt_string(TRANSIENT_PROMPT_INDICATOR, config, engine_state, stack) {
153 nu_prompt.update_prompt_indicator(Some(s))
154 }
155 if let Some(s) = get_prompt_string(
156 TRANSIENT_PROMPT_INDICATOR_VI_INSERT,
157 config,
158 engine_state,
159 stack,
160 ) {
161 nu_prompt.update_prompt_vi_insert(Some(s))
162 }
163 if let Some(s) = get_prompt_string(
164 TRANSIENT_PROMPT_INDICATOR_VI_NORMAL,
165 config,
166 engine_state,
167 stack,
168 ) {
169 nu_prompt.update_prompt_vi_normal(Some(s))
170 }
171
172 if let Some(s) = get_prompt_string(
173 TRANSIENT_PROMPT_MULTILINE_INDICATOR,
174 config,
175 engine_state,
176 stack,
177 ) {
178 nu_prompt.update_prompt_multiline(Some(s))
179 }
180
181 Box::new(nu_prompt)
182}
183
184#[cfg(test)]
185mod tests {
186 use super::*;
187 use nu_protocol::Span;
188
189 #[test]
190 fn update_prompt_does_not_embed_osc_markers() {
191 let mut config = Config::default();
192 config.shell_integration.osc133 = true;
193
194 let engine_state = EngineState::new();
195 let mut stack = Stack::new();
196 stack.add_env_var(
197 PROMPT_COMMAND.into(),
198 Value::string("test", Span::unknown()),
199 );
200
201 let mut nu_prompt = NushellPrompt::new();
202
203 update_prompt(&config, &engine_state, &mut stack, &mut nu_prompt);
204
205 assert_eq!(nu_prompt.render_prompt_left(), "test");
206 }
207}