##// END OF EJS Templates
contrib: add a partial-merge tool for sorted lists (such as Python imports)...
contrib: add a partial-merge tool for sorted lists (such as Python imports) This is a pretty naive tool that uses a regular expression for matching lines. It is based on a Google-internal tool that worked in a similar way. For now, the regular expression is hard-coded to attempt to match single-line Python imports. The only commit I've found in the hg core repo where the tool helped was commit 9cd6292abfdf. I think that's because we often use multiple imports per import statement. I think this tool is still a decent first step (especially once the regex is made configurable in the next patch). The merging should ideally use a proper Python parser and do the merge at the AST (or CST?) level, but that's significantly harder, especially if you want to preserve comments and whitespace. It's also less generic. Differential Revision: https://phab.mercurial-scm.org/D12380

File last commit:

r49622:f19be290 default
r49874:681b25ea default
Show More
ui.rs
246 lines | 7.3 KiB | application/rls-services+xml | RustLexer
Simon Sapin
rhg: Add support for colored output...
r49584 use crate::color::ColorConfig;
use crate::color::Effect;
Raphaël Gomès
rhg: use `format_bytes!` for error messages...
r46598 use format_bytes::format_bytes;
Simon Sapin
rhg: Add support for colored output...
r49584 use format_bytes::write_bytes;
Simon Sapin
rhg: Pass a &Config to Ui::new...
r49581 use hg::config::Config;
Simon Sapin
rhg: Make Ui::new falliable, add Ui::new_infallible...
r49582 use hg::errors::HgError;
Simon Sapin
rhg: Add support for HGPLAINEXPECT...
r49580 use hg::utils::files::get_bytes_from_os_string;
Antoine Cezar
rhg: add a `DebugData` `Command` to prepare the `rhg debugdata` subcommand...
r46099 use std::borrow::Cow;
Pulkit Goyal
rhg: add ui.plain() and check it before showing relative paths in status...
r48990 use std::env;
Antoine Cezar
rhg: add RootCommand using hg-core FindRoot operation to prepare `hg root`...
r45592 use std::io;
Antoine Cezar
rhg: add buffered stdout writing possibility...
r45921 use std::io::{ErrorKind, Write};
Antoine Cezar
rhg: add RootCommand using hg-core FindRoot operation to prepare `hg root`...
r45592
Antoine Cezar
rhg: add buffered stdout writing possibility...
r45921 pub struct Ui {
stdout: std::io::Stdout,
stderr: std::io::Stderr,
Simon Sapin
rhg: Add support for colored output...
r49584 colors: Option<ColorConfig>,
Antoine Cezar
rhg: add buffered stdout writing possibility...
r45921 }
Antoine Cezar
rhg: add RootCommand using hg-core FindRoot operation to prepare `hg root`...
r45592
/// The kind of user interface error
pub enum UiError {
/// The standard output stream cannot be written to
StdoutError(io::Error),
/// The standard error stream cannot be written to
StderrError(io::Error),
}
/// The commandline user interface
impl Ui {
Simon Sapin
rhg: Add support for colored output...
r49584 pub fn new(config: &Config) -> Result<Self, HgError> {
Simon Sapin
rhg: Make Ui::new falliable, add Ui::new_infallible...
r49582 Ok(Ui {
Simon Sapin
rhg: Add support for colored output...
r49584 // If using something else, also adapt `isatty()` below.
Simon Sapin
rhg: Make Ui::new falliable, add Ui::new_infallible...
r49582 stdout: std::io::stdout(),
Simon Sapin
rhg: Add support for colored output...
r49584
Simon Sapin
rhg: Make Ui::new falliable, add Ui::new_infallible...
r49582 stderr: std::io::stderr(),
Simon Sapin
rhg: Add support for colored output...
r49584 colors: ColorConfig::new(config)?,
Simon Sapin
rhg: Make Ui::new falliable, add Ui::new_infallible...
r49582 })
}
/// Default to no color if color configuration errors.
///
/// Useful when we’re already handling another error.
Simon Sapin
rhg: Add support for colored output...
r49584 pub fn new_infallible(config: &Config) -> Self {
Antoine Cezar
rhg: add buffered stdout writing possibility...
r45921 Ui {
Simon Sapin
rhg: Add support for colored output...
r49584 // If using something else, also adapt `isatty()` below.
Antoine Cezar
rhg: add buffered stdout writing possibility...
r45921 stdout: std::io::stdout(),
Simon Sapin
rhg: Add support for colored output...
r49584
Antoine Cezar
rhg: add buffered stdout writing possibility...
r45921 stderr: std::io::stderr(),
Simon Sapin
rhg: Add support for colored output...
r49584 colors: ColorConfig::new(config).unwrap_or(None),
Antoine Cezar
rhg: add buffered stdout writing possibility...
r45921 }
}
/// Returns a buffered handle on stdout for faster batch printing
/// operations.
pub fn stdout_buffer(&self) -> StdoutBuffer<std::io::StdoutLock> {
StdoutBuffer::new(self.stdout.lock())
Antoine Cezar
rhg: add RootCommand using hg-core FindRoot operation to prepare `hg root`...
r45592 }
/// Write bytes to stdout
pub fn write_stdout(&self, bytes: &[u8]) -> Result<(), UiError> {
Antoine Cezar
rhg: add buffered stdout writing possibility...
r45921 let mut stdout = self.stdout.lock();
Antoine Cezar
rhg: add RootCommand using hg-core FindRoot operation to prepare `hg root`...
r45592
Antoine Cezar
rhg: fix `clippy` warnings...
r46010 stdout.write_all(bytes).or_else(handle_stdout_error)?;
Antoine Cezar
rhg: add RootCommand using hg-core FindRoot operation to prepare `hg root`...
r45592
Antoine Cezar
rhg: fix `clippy` warnings...
r46010 stdout.flush().or_else(handle_stdout_error)
Antoine Cezar
rhg: add RootCommand using hg-core FindRoot operation to prepare `hg root`...
r45592 }
/// Write bytes to stderr
pub fn write_stderr(&self, bytes: &[u8]) -> Result<(), UiError> {
Antoine Cezar
rhg: add buffered stdout writing possibility...
r45921 let mut stderr = self.stderr.lock();
Antoine Cezar
rhg: add RootCommand using hg-core FindRoot operation to prepare `hg root`...
r45592
Antoine Cezar
rhg: fix `clippy` warnings...
r46010 stderr.write_all(bytes).or_else(handle_stderr_error)?;
Antoine Cezar
rhg: add RootCommand using hg-core FindRoot operation to prepare `hg root`...
r45592
Antoine Cezar
rhg: fix `clippy` warnings...
r46010 stderr.flush().or_else(handle_stderr_error)
Antoine Cezar
rhg: add RootCommand using hg-core FindRoot operation to prepare `hg root`...
r45592 }
Pulkit Goyal
rhg: add ui.plain() and check it before showing relative paths in status...
r48990
Simon Sapin
rhg: Add support for colored output...
r49584 /// Write bytes to stdout with the given label
///
/// Like the optional `label` parameter in `mercurial/ui.py`,
/// this label influences the color used for this output.
pub fn write_stdout_labelled(
&self,
bytes: &[u8],
label: &str,
) -> Result<(), UiError> {
if let Some(colors) = &self.colors {
if let Some(effects) = colors.styles.get(label.as_bytes()) {
if !effects.is_empty() {
return self
.write_stdout_with_effects(bytes, effects)
.or_else(handle_stdout_error);
}
}
}
self.write_stdout(bytes)
}
fn write_stdout_with_effects(
&self,
bytes: &[u8],
effects: &[Effect],
) -> io::Result<()> {
let stdout = &mut self.stdout.lock();
let mut write_line = |line: &[u8], first: bool| {
// `line` does not include the newline delimiter
if !first {
stdout.write_all(b"\n")?;
}
if line.is_empty() {
return Ok(());
}
/// 0x1B == 27 == 0o33
const ASCII_ESCAPE: &[u8] = b"\x1b";
write_bytes!(stdout, b"{}[0", ASCII_ESCAPE)?;
for effect in effects {
write_bytes!(stdout, b";{}", effect)?;
}
write_bytes!(stdout, b"m")?;
stdout.write_all(line)?;
write_bytes!(stdout, b"{}[0m", ASCII_ESCAPE)
};
let mut lines = bytes.split(|&byte| byte == b'\n');
if let Some(first) = lines.next() {
write_line(first, true)?;
for line in lines {
write_line(line, false)?
}
}
stdout.flush()
}
Simon Sapin
rhg: Fall back to Python if verbose status is requested by config...
r49344 /// Return whether plain mode is active.
Pulkit Goyal
rhg: add ui.plain() and check it before showing relative paths in status...
r48990 ///
/// Plain mode means that all configuration variables which affect
/// the behavior and output of Mercurial should be
/// ignored. Additionally, the output should be stable,
/// reproducible and suitable for use in scripts or applications.
///
/// The only way to trigger plain mode is by setting either the
/// `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
///
/// The return value can either be
/// - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
/// - False if feature is disabled by default and not included in HGPLAIN
/// - True otherwise
Simon Sapin
rhg: Add support for HGPLAINEXPECT...
r49580 pub fn plain(&self, feature: Option<&str>) -> bool {
plain(feature)
}
}
Simon Sapin
rhg: Add support for colored output...
r49584 pub fn plain(opt_feature: Option<&str>) -> bool {
Simon Sapin
rhg: Add support for HGPLAINEXPECT...
r49580 if let Some(except) = env::var_os("HGPLAINEXCEPT") {
opt_feature.map_or(true, |feature| {
get_bytes_from_os_string(except)
.split(|&byte| byte == b',')
.all(|exception| exception != feature.as_bytes())
})
} else {
Pulkit Goyal
rhg: add ui.plain() and check it before showing relative paths in status...
r48990 env::var_os("HGPLAIN").is_some()
}
Antoine Cezar
rhg: add RootCommand using hg-core FindRoot operation to prepare `hg root`...
r45592 }
Antoine Cezar
rhg: add buffered stdout writing possibility...
r45921
/// A buffered stdout writer for faster batch printing operations.
pub struct StdoutBuffer<W: Write> {
buf: io::BufWriter<W>,
}
impl<W: Write> StdoutBuffer<W> {
pub fn new(writer: W) -> Self {
let buf = io::BufWriter::new(writer);
Self { buf }
}
/// Write bytes to stdout buffer
pub fn write_all(&mut self, bytes: &[u8]) -> Result<(), UiError> {
Antoine Cezar
rhg: fix `clippy` warnings...
r46010 self.buf.write_all(bytes).or_else(handle_stdout_error)
Antoine Cezar
rhg: add buffered stdout writing possibility...
r45921 }
/// Flush bytes to stdout
pub fn flush(&mut self) -> Result<(), UiError> {
Antoine Cezar
rhg: fix `clippy` warnings...
r46010 self.buf.flush().or_else(handle_stdout_error)
Antoine Cezar
rhg: add buffered stdout writing possibility...
r45921 }
}
Antoine Cezar
rhg: extract function handle_stdout_error...
r45925
/// Sometimes writing to stdout is not possible, try writing to stderr to
/// signal that failure, otherwise just bail.
fn handle_stdout_error(error: io::Error) -> Result<(), UiError> {
if let ErrorKind::BrokenPipe = error.kind() {
// This makes `| head` work for example
return Ok(());
}
let mut stderr = io::stderr();
stderr
Raphaël Gomès
rhg: use `format_bytes!` for error messages...
r46598 .write_all(&format_bytes!(
b"abort: {}\n",
error.to_string().as_bytes()
))
Antoine Cezar
rhg: fix `clippy` warnings...
r46010 .map_err(UiError::StderrError)?;
Antoine Cezar
rhg: extract function handle_stdout_error...
r45925
Antoine Cezar
rhg: fix `clippy` warnings...
r46010 stderr.flush().map_err(UiError::StderrError)?;
Antoine Cezar
rhg: extract function handle_stdout_error...
r45925
Err(UiError::StdoutError(error))
}
Antoine Cezar
rhg: handle broken pipe error for stderr...
r45926
/// Sometimes writing to stderr is not possible.
fn handle_stderr_error(error: io::Error) -> Result<(), UiError> {
// A broken pipe should not result in a error
// like with `| head` for example
if let ErrorKind::BrokenPipe = error.kind() {
return Ok(());
}
Err(UiError::StdoutError(error))
}
Antoine Cezar
rhg: add a `DebugData` `Command` to prepare the `rhg debugdata` subcommand...
r46099
/// Encode rust strings according to the user system.
pub fn utf8_to_local(s: &str) -> Cow<[u8]> {
// TODO encode for the user's system //
let bytes = s.as_bytes();
Cow::Borrowed(bytes)
}
Simon Sapin
rhg: Add support for colored output...
r49584
Raphaël Gomès
rhg: signal when falling back in logs...
r49622 /// Decode user system bytes to Rust string.
pub fn local_to_utf8(s: &[u8]) -> Cow<str> {
// TODO decode from the user's system
String::from_utf8_lossy(s)
}
Simon Sapin
rhg: Add support for colored output...
r49584 /// Should formatted output be used?
///
/// Note: rhg does not have the formatter mechanism yet,
/// but this is also used when deciding whether to use color.
pub fn formatted(config: &Config) -> Result<bool, HgError> {
if let Some(formatted) = config.get_option(b"ui", b"formatted")? {
Ok(formatted)
} else {
isatty(config)
}
}
fn isatty(config: &Config) -> Result<bool, HgError> {
Ok(if config.get_bool(b"ui", b"nontty")? {
false
} else {
atty::is(atty::Stream::Stdout)
})
}