06aa0c8f创建于 24 天前历史提交
//! Prompt handling for shell instances.

use std::borrow::Cow;

use crate::{Shell, error, extensions, prompt};

impl<SE: extensions::ShellExtensions> Shell<SE> {
	/// Returns the default prompt string for the shell.
	const fn default_prompt(&self) -> &'static str {
		if self.options.sh_mode {
			"$ "
		} else {
			"brush$ "
		}
	}

	/// Composes the shell's post-input, pre-command prompt, applying all
	/// appropriate expansions.
	pub async fn compose_precmd_prompt(&mut self) -> Result<String, error::Error> {
		self.expand_prompt_var("PS0", "").await
	}

	/// Composes the shell's prompt, applying all appropriate expansions.
	pub async fn compose_prompt(&mut self) -> Result<String, error::Error> {
		self.expand_prompt_var("PS1", self.default_prompt()).await
	}

	/// Composes the shell's alternate-side prompt, applying all appropriate
	/// expansions.
	pub async fn compose_alt_side_prompt(&mut self) -> Result<String, error::Error> {
		// This is a brush extension.
		self.expand_prompt_var("BRUSH_PS_ALT", "").await
	}

	/// Composes the shell's continuation prompt.
	pub async fn compose_continuation_prompt(&mut self) -> Result<String, error::Error> {
		self.expand_prompt_var("PS2", "> ").await
	}

	pub(super) async fn expand_prompt_var(
		&mut self,
		var_name: &str,
		default: &str,
	) -> Result<String, error::Error> {
		//
		// TODO(prompt): bash appears to do this in a subshell; we need to investigate
		// if that's required.
		//

		// Retrieve the spec.
		let prompt_spec = self.parameter_or_default(var_name, default);
		if prompt_spec.is_empty() {
			return Ok(String::new());
		}

		// Save (and later restore) the last exit status.
		let prev_last_result = self.last_exit_status();
		let prev_last_pipeline_statuses = self.last_pipeline_statuses.clone();

		// Expand it.
		let params = self.default_exec_params();
		let result = prompt::expand_prompt(self, &params, prompt_spec.into_owned()).await;

		// Restore the last exit status.
		self.last_pipeline_statuses = prev_last_pipeline_statuses;
		self.set_last_exit_status(prev_last_result);

		// Strip out special characters that readline would typically drop:
		// \001 and \002 (start and end of non-printing sequences).
		let mut expanded = result?;
		expanded.retain(|c| c != '\x01' && c != '\x02');

		Ok(expanded)
	}

	fn parameter_or_default<'a>(&'a self, name: &str, default: &'a str) -> Cow<'a, str> {
		self.env_str(name).unwrap_or_else(|| default.into())
	}
}