mirror of
https://github.com/BurntSushi/ripgrep.git
synced 2025-07-31 20:21:59 -07:00
This commit completely guts all of the color handling code and replaces most of it with two new crates: wincolor and termcolor. wincolor provides a simple API to coloring using the Windows console and termcolor provides a platform independent coloring API tuned for multithreaded command line programs. This required a lot more flexibility than what the `term` crate provided, so it was dropped. We instead switch to writing ANSI escape sequences directly and ignore the TERMINFO database. In addition to fixing several bugs, this commit also permits end users to customize colors to a certain extent. For example, this command will set the match color to magenta and the line number background to yellow: rg --colors 'match:fg:magenta' --colors 'line:bg:yellow' foo For tty handling, we've adopted a hack from `git` to do tty detection in MSYS/mintty terminals. As a result, ripgrep should get both color detection and piping correct on Windows regardless of which terminal you use. Finally, switch to line buffering. Performance doesn't seem to be impacted and it's an otherwise more user friendly option. Fixes #37, Fixes #51, Fixes #94, Fixes #117, Fixes #182, Fixes #231
153 lines
4.5 KiB
Rust
153 lines
4.5 KiB
Rust
/*!
|
|
This atty module contains functions for detecting whether ripgrep is being fed
|
|
from (or to) a terminal. Windows and Unix do this differently, so implement
|
|
both here.
|
|
*/
|
|
|
|
#[cfg(windows)]
|
|
use winapi::minwindef::DWORD;
|
|
#[cfg(windows)]
|
|
use winapi::winnt::HANDLE;
|
|
|
|
#[cfg(unix)]
|
|
pub fn stdin_is_readable() -> bool {
|
|
use std::fs::File;
|
|
use std::os::unix::fs::FileTypeExt;
|
|
use std::os::unix::io::{FromRawFd, IntoRawFd};
|
|
use libc;
|
|
|
|
let file = unsafe { File::from_raw_fd(libc::STDIN_FILENO) };
|
|
let md = file.metadata();
|
|
let _ = file.into_raw_fd();
|
|
let ft = match md {
|
|
Err(_) => return false,
|
|
Ok(md) => md.file_type(),
|
|
};
|
|
ft.is_file() || ft.is_fifo()
|
|
}
|
|
|
|
#[cfg(windows)]
|
|
pub fn stdin_is_readable() -> bool {
|
|
// ???
|
|
true
|
|
}
|
|
|
|
/// Returns true if there is a tty on stdin.
|
|
#[cfg(unix)]
|
|
pub fn on_stdin() -> bool {
|
|
use libc;
|
|
0 < unsafe { libc::isatty(libc::STDIN_FILENO) }
|
|
}
|
|
|
|
/// Returns true if there is a tty on stdout.
|
|
#[cfg(unix)]
|
|
pub fn on_stdout() -> bool {
|
|
use libc;
|
|
0 < unsafe { libc::isatty(libc::STDOUT_FILENO) }
|
|
}
|
|
|
|
/// Returns true if there is a tty on stdin.
|
|
#[cfg(windows)]
|
|
pub fn on_stdin() -> bool {
|
|
use kernel32::GetStdHandle;
|
|
use winapi::winbase::{
|
|
STD_INPUT_HANDLE, STD_OUTPUT_HANDLE, STD_ERROR_HANDLE,
|
|
};
|
|
|
|
unsafe {
|
|
let stdin = GetStdHandle(STD_INPUT_HANDLE);
|
|
if console_on_handle(stdin) {
|
|
// False positives aren't possible. If we got a console then
|
|
// we definitely have a tty on stdin.
|
|
return true;
|
|
}
|
|
// Otherwise, it's possible to get a false negative. If we know that
|
|
// there's a console on stdout or stderr however, then this is a true
|
|
// negative.
|
|
if console_on_fd(STD_OUTPUT_HANDLE)
|
|
|| console_on_fd(STD_ERROR_HANDLE) {
|
|
return false;
|
|
}
|
|
// Otherwise, we can't really tell, so we do a weird hack.
|
|
msys_tty_on_handle(stdin)
|
|
}
|
|
}
|
|
|
|
/// Returns true if there is a tty on stdout.
|
|
#[cfg(windows)]
|
|
pub fn on_stdout() -> bool {
|
|
use kernel32::GetStdHandle;
|
|
use winapi::winbase::{
|
|
STD_INPUT_HANDLE, STD_OUTPUT_HANDLE, STD_ERROR_HANDLE,
|
|
};
|
|
|
|
unsafe {
|
|
let stdout = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
if console_on_handle(stdout) {
|
|
// False positives aren't possible. If we got a console then
|
|
// we definitely have a tty on stdout.
|
|
return true;
|
|
}
|
|
// Otherwise, it's possible to get a false negative. If we know that
|
|
// there's a console on stdin or stderr however, then this is a true
|
|
// negative.
|
|
if console_on_fd(STD_INPUT_HANDLE) || console_on_fd(STD_ERROR_HANDLE) {
|
|
return false;
|
|
}
|
|
// Otherwise, we can't really tell, so we do a weird hack.
|
|
msys_tty_on_handle(stdout)
|
|
}
|
|
}
|
|
|
|
/// Returns true if there is an MSYS tty on the given handle.
|
|
#[cfg(windows)]
|
|
fn msys_tty_on_handle(handle: HANDLE) -> bool {
|
|
use std::ffi::OsString;
|
|
use std::mem;
|
|
use std::os::raw::c_void;
|
|
use std::os::windows::ffi::OsStringExt;
|
|
use std::slice;
|
|
|
|
use kernel32::{GetFileInformationByHandleEx};
|
|
use winapi::fileapi::FILE_NAME_INFO;
|
|
use winapi::minwinbase::FileNameInfo;
|
|
use winapi::minwindef::MAX_PATH;
|
|
|
|
unsafe {
|
|
let size = mem::size_of::<FILE_NAME_INFO>();
|
|
let mut name_info_bytes = vec![0u8; size + MAX_PATH];
|
|
let res = GetFileInformationByHandleEx(
|
|
handle,
|
|
FileNameInfo,
|
|
&mut *name_info_bytes as *mut _ as *mut c_void,
|
|
name_info_bytes.len() as u32);
|
|
if res == 0 {
|
|
return true;
|
|
}
|
|
let name_info: FILE_NAME_INFO =
|
|
*(name_info_bytes[0..size].as_ptr() as *const FILE_NAME_INFO);
|
|
let name_bytes =
|
|
&name_info_bytes[size..size + name_info.FileNameLength as usize];
|
|
let name_u16 = slice::from_raw_parts(
|
|
name_bytes.as_ptr() as *const u16, name_bytes.len() / 2);
|
|
let name = OsString::from_wide(name_u16)
|
|
.as_os_str().to_string_lossy().into_owned();
|
|
name.contains("msys-") || name.contains("-pty")
|
|
}
|
|
}
|
|
|
|
/// Returns true if there is a console on the given file descriptor.
|
|
#[cfg(windows)]
|
|
unsafe fn console_on_fd(fd: DWORD) -> bool {
|
|
use kernel32::GetStdHandle;
|
|
console_on_handle(GetStdHandle(fd))
|
|
}
|
|
|
|
/// Returns true if there is a console on the given handle.
|
|
#[cfg(windows)]
|
|
fn console_on_handle(handle: HANDLE) -> bool {
|
|
use kernel32::GetConsoleMode;
|
|
let mut out = 0;
|
|
unsafe { GetConsoleMode(handle, &mut out) != 0 }
|
|
}
|