Fix VCS markers not showing up in textview (#3530)

Changes:
* Bug fix - bat adds markers only when a file path is passed and it can use git2
on it. It doesn't add markers when bytes are passed. Hence, the code is
adjusted accordingly. The sideeffect is files being opened multiple
times and its content being unnecessarily loaded in memory.
* Refactoring of the crate - Config is extracted to its struct file.
Repetitive blocks of code are dried and nested conditionals are
flattened.
This commit is contained in:
Tomas Koutsky 2021-06-03 08:25:28 +02:00 committed by GitHub
parent 5537dce3cc
commit 8b193db0cb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 236 additions and 189 deletions

85
Cargo.lock generated
View file

@ -446,17 +446,19 @@ checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
[[package]]
name = "bat"
version = "0.17.1"
version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df1e36a14d9603f9799def62aebb9dfece7e1b364aaaa8eba856e64bce1141c5"
checksum = "cc7224a2b5bdebb4762c36c25841694ab90328fe0c59fc8e685577795b9b785b"
dependencies = [
"ansi_colours",
"ansi_term 0.12.1",
"atty",
"bugreport",
"clap",
"clircle",
"console",
"content_inspector",
"dirs 3.0.2",
"dirs-next",
"encoding",
"error-chain",
"git2",
@ -616,6 +618,17 @@ dependencies = [
"serde 1.0.126",
]
[[package]]
name = "bugreport"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0e97e538864a7c95d33accbf64c8d354018ba3b6e032502fd0fe7259cf1aa3d"
dependencies = [
"git-version",
"shell-escape",
"sys-info",
]
[[package]]
name = "bumpalo"
version = "3.7.0"
@ -806,6 +819,18 @@ dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "clircle"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e68bbd985a63de680ab4d1ad77b6306611a8f961b282c8b5ab513e6de934e396"
dependencies = [
"cfg-if 1.0.0",
"libc",
"serde 1.0.125",
"winapi 0.3.9",
]
[[package]]
name = "cloudabi"
version = "0.0.3"
@ -885,9 +910,9 @@ dependencies = [
[[package]]
name = "console"
version = "0.13.0"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a50aab2529019abfabfa93f1e6c41ef392f91fbf179b347a7e96abb524884a08"
checksum = "3993e6445baa160675931ec041a5e03ca84b9c6e32a056150d3aa2bdda0a1f45"
dependencies = [
"encode_unicode",
"lazy_static 1.4.0",
@ -896,7 +921,6 @@ dependencies = [
"terminal_size",
"unicode-width",
"winapi 0.3.9",
"winapi-util",
]
[[package]]
@ -1411,15 +1435,6 @@ dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "dirs"
version = "3.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30baa043103c9d0c2a57cf537cc2f35623889dc0d405e6c3cccfadbc81c71309"
dependencies = [
"dirs-sys",
]
[[package]]
name = "dirs-next"
version = "2.0.0"
@ -2103,6 +2118,28 @@ version = "0.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e4075386626662786ddb0ec9081e7c7eeb1ba31951f447ca780ef9f5d568189"
[[package]]
name = "git-version"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94918e83f1e01dedc2e361d00ce9487b14c58c7f40bab148026fa39d42cb41e2"
dependencies = [
"git-version-macro",
"proc-macro-hack",
]
[[package]]
name = "git-version-macro"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34a97a52fdee1870a34fa6e4b77570cba531b27d1838874fef4429a791a3d657"
dependencies = [
"proc-macro-hack",
"proc-macro2",
"quote 1.0.9",
"syn 1.0.71",
]
[[package]]
name = "git2"
version = "0.13.20"
@ -5722,6 +5759,12 @@ dependencies = [
"git2",
]
[[package]]
name = "shell-escape"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45bb67a18fa91266cc7807181f62f9178a6873bfad7dc788c42e6430db40184f"
[[package]]
name = "shell-words"
version = "1.0.0"
@ -6091,6 +6134,16 @@ dependencies = [
"yaml-rust",
]
[[package]]
name = "sys-info"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33fcecee49339531cf6bd84ecf3ed94f9c8ef4a7e700f2a1cac9cc1ca485383a"
dependencies = [
"cc",
"libc",
]
[[package]]
name = "sys-locale"
version = "0.1.0"
@ -6158,7 +6211,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edd106a334b7657c10b7c540a0106114feadeb4dc314513e97df481d5d966f42"
dependencies = [
"byteorder",
"dirs 1.0.5",
"dirs",
"winapi 0.3.9",
]

View file

@ -17,7 +17,7 @@ nu-protocol = { path = "../nu-protocol", version = "0.32.0" }
nu-source = { path = "../nu-source", version = "0.32.0" }
nu-ansi-term = { version = "0.32.0", path = "../nu-ansi-term" }
bat = { version = "0.17.1", features = ["regex-fancy", "paging"] }
bat = { version = "0.18", features = ["regex-fancy", "paging", "git"] }
term_size = "0.3.2"
url = "2.2.0"

View file

@ -0,0 +1,110 @@
use nu_protocol::Value;
pub struct Config {
pub term_width: usize,
pub tab_width: u64,
pub colored_output: bool,
pub true_color: bool,
pub header: bool,
pub line_numbers: bool,
pub grid: bool,
pub vcs_modification_markers: bool,
pub snip: bool,
pub wrapping_mode: bat::WrappingMode,
pub use_italics: bool,
pub paging_mode: bat::PagingMode,
pub pager: String,
pub line_ranges: bat::line_range::LineRanges,
// TODO: Not configurable
highlight_range: String,
// TODO: Not configurable
pub highlight_range_from: u64,
// TODO: Not configurable
pub highlight_range_to: u64,
pub theme: String,
}
impl From<&Value> for Config {
fn from(value: &Value) -> Self {
let mut config = Config::default();
for (idx, entry) in value.row_entries() {
match idx.as_ref() {
"term_width" => {
config.term_width = entry.as_u64().unwrap_or(config.term_width as u64) as usize;
}
"tab_width" => {
config.tab_width = entry.as_u64().unwrap_or(4_u64);
}
"colored_output" => config.colored_output = entry.as_bool().unwrap_or(true),
"true_color" => config.true_color = entry.as_bool().unwrap_or(true),
"header" => config.header = entry.as_bool().unwrap_or(true),
"line_numbers" => config.line_numbers = entry.as_bool().unwrap_or(true),
"grid" => config.grid = entry.as_bool().unwrap_or(true),
"vcs_modification_markers" => {
config.vcs_modification_markers = entry.as_bool().unwrap_or(true)
}
"snip" => config.snip = entry.as_bool().unwrap_or(true),
"wrapping_mode" => {
config.wrapping_mode = match entry.as_string() {
Ok(s) if s.to_lowercase() == "nowrapping" => {
bat::WrappingMode::NoWrapping(true)
}
Ok(s) if s.to_lowercase() == "character" => bat::WrappingMode::Character,
_ => bat::WrappingMode::NoWrapping(true),
}
}
"use_italics" => config.use_italics = entry.as_bool().unwrap_or(true),
"paging_mode" => {
config.paging_mode = match entry.as_string() {
Ok(s) if s.to_lowercase() == "always" => bat::PagingMode::Always,
Ok(s) if s.to_lowercase() == "never" => bat::PagingMode::Never,
Ok(s) if s.to_lowercase() == "quitifonescreen" => {
bat::PagingMode::QuitIfOneScreen
}
_ => bat::PagingMode::QuitIfOneScreen,
}
}
"pager" => config.pager = entry.as_string().unwrap_or_else(|_| "less".to_string()),
// TODO: not real sure what to do with this
"line_ranges" => config.line_ranges = bat::line_range::LineRanges::all(),
"highlight_range" => config.highlight_range = "0,0".into(),
"theme" => {
config.theme = value
.as_string()
.unwrap_or_else(|_| "OneDarkHalf".to_string())
}
_ => (),
}
}
config
}
}
impl Default for Config {
fn default() -> Self {
let (term_width, _) = term_size::dimensions().unwrap_or((80, 20));
Self {
term_width,
tab_width: 4,
colored_output: true,
true_color: true,
header: true,
line_numbers: true,
grid: true,
vcs_modification_markers: true,
snip: true,
wrapping_mode: bat::WrappingMode::NoWrapping(true),
use_italics: true,
paging_mode: bat::PagingMode::QuitIfOneScreen,
pager: "less".into(),
line_ranges: bat::line_range::LineRanges::all(),
highlight_range: "0.0".into(),
highlight_range_from: 0,
highlight_range_to: 0,
theme: "OneHalfDark".into(),
}
}
}

View file

@ -1,3 +1,4 @@
mod config;
mod nu;
mod textview;

View file

@ -1,3 +1,4 @@
use crate::config::Config;
use nu_protocol::{Primitive, UntaggedValue, Value};
use nu_source::{AnchorLocation, Tag};
use std::path::Path;
@ -11,179 +12,61 @@ impl TextView {
}
}
#[allow(clippy::cognitive_complexity)]
pub fn view_text_value(value: &Value) {
let (mut term_width, _) = term_size::dimensions().unwrap_or((80, 20));
let mut tab_width: u64 = 4;
let mut colored_output = true;
let mut true_color = true;
let mut header = true;
let mut line_numbers = true;
let mut grid = true;
let mut vcs_modification_markers = true;
let mut snip = true;
let mut wrapping_mode = bat::WrappingMode::NoWrapping;
let mut use_italics = true;
let mut paging_mode = bat::PagingMode::QuitIfOneScreen;
let mut pager = "less".to_string();
let mut line_ranges = bat::line_range::LineRanges::all();
let mut _highlight_range = "0,0";
let highlight_range_from: u64 = 0;
let highlight_range_to: u64 = 0;
let mut theme = "OneHalfDark".to_string();
fn get_file_path(source: AnchorLocation) -> Option<String> {
match source {
AnchorLocation::File(file) => {
let path = Path::new(&file);
if let Ok(config) = nu_data::config::config(Tag::unknown()) {
if let Some(batvars) = config.get("textview") {
for (idx, value) in batvars.row_entries() {
match idx.as_ref() {
"term_width" => {
term_width = value.as_u64().unwrap_or(term_width as u64) as usize;
}
"tab_width" => {
tab_width = value.as_u64().unwrap_or(4_u64);
}
"colored_output" => colored_output = value.as_bool().unwrap_or(true),
"true_color" => true_color = value.as_bool().unwrap_or(true),
"header" => header = value.as_bool().unwrap_or(true),
"line_numbers" => line_numbers = value.as_bool().unwrap_or(true),
"grid" => grid = value.as_bool().unwrap_or(true),
"vcs_modification_markers" => {
vcs_modification_markers = value.as_bool().unwrap_or(true)
}
"snip" => snip = value.as_bool().unwrap_or(true),
"wrapping_mode" => {
wrapping_mode = match value.as_string() {
Ok(s) if s.to_lowercase() == "nowrapping" => {
bat::WrappingMode::NoWrapping
}
Ok(s) if s.to_lowercase() == "character" => {
bat::WrappingMode::Character
}
_ => bat::WrappingMode::NoWrapping,
}
}
"use_italics" => use_italics = value.as_bool().unwrap_or(true),
"paging_mode" => {
paging_mode = match value.as_string() {
Ok(s) if s.to_lowercase() == "always" => bat::PagingMode::Always,
Ok(s) if s.to_lowercase() == "never" => bat::PagingMode::Never,
Ok(s) if s.to_lowercase() == "quitifonescreen" => {
bat::PagingMode::QuitIfOneScreen
}
_ => bat::PagingMode::QuitIfOneScreen,
}
}
"pager" => pager = value.as_string().unwrap_or_else(|_| "less".to_string()),
"line_ranges" => line_ranges = bat::line_range::LineRanges::all(), // not real sure what to do with this
"highlight_range" => _highlight_range = "0,0", //ignore config value for now
"theme" => {
theme = value
.as_string()
.unwrap_or_else(|_| "OneDarkHalf".to_string())
}
_ => (),
}
}
}
}
let value_anchor = value.anchor();
if let UntaggedValue::Primitive(Primitive::String(ref s)) = &value.value {
if let Some(source) = value_anchor {
let file_path: Option<String> = match source {
AnchorLocation::File(file) => {
let path = Path::new(&file);
Some(path.to_string_lossy().to_string())
}
AnchorLocation::Url(url) => {
let url = url::Url::parse(&url);
if let Ok(url) = url {
if let Some(mut segments) = url.path_segments() {
if let Some(file) = segments.next_back() {
let path = Path::new(file);
Some(path.to_string_lossy().to_string())
} else {
None
}
} else {
None
}
} else {
None
}
}
//FIXME: this probably isn't correct
AnchorLocation::Source(_source) => None,
};
match file_path {
Some(file_path) => {
// Let bat do it's thing
bat::PrettyPrinter::new()
.input(bat::Input::from_bytes(s.as_bytes()).name(file_path))
.term_width(term_width as usize)
.tab_width(Some(tab_width as usize))
.colored_output(colored_output)
.true_color(true_color)
.header(header)
.line_numbers(line_numbers)
.grid(grid)
.vcs_modification_markers(vcs_modification_markers)
.snip(snip)
.wrapping_mode(wrapping_mode)
.use_italics(use_italics)
.paging_mode(paging_mode)
.pager(&pager)
.line_ranges(line_ranges)
.highlight_range(highlight_range_from as usize, highlight_range_to as usize)
.theme(&theme)
.print()
.expect("Error with bat PrettyPrint");
}
_ => {
bat::PrettyPrinter::new()
.input_from_bytes(s.as_bytes())
.term_width(term_width as usize)
.tab_width(Some(tab_width as usize))
.colored_output(colored_output)
.true_color(true_color)
.header(header)
.line_numbers(line_numbers)
.grid(grid)
.vcs_modification_markers(vcs_modification_markers)
.snip(snip)
.wrapping_mode(wrapping_mode)
.use_italics(use_italics)
.paging_mode(paging_mode)
.pager(&pager)
.line_ranges(line_ranges)
.highlight_range(highlight_range_from as usize, highlight_range_to as usize)
.theme(&theme)
.print()
.expect("Error with bat PrettyPrint");
}
}
} else {
bat::PrettyPrinter::new()
.input_from_bytes(s.as_bytes())
.term_width(term_width as usize)
.tab_width(Some(tab_width as usize))
.colored_output(colored_output)
.true_color(true_color)
.header(header)
.line_numbers(line_numbers)
.grid(grid)
.vcs_modification_markers(vcs_modification_markers)
.snip(snip)
.wrapping_mode(wrapping_mode)
.use_italics(use_italics)
.paging_mode(paging_mode)
.pager(&pager)
.line_ranges(line_ranges)
.highlight_range(highlight_range_from as usize, highlight_range_to as usize)
.theme(&theme)
.print()
.expect("Error with bat PrettyPrint");
Some(path.to_string_lossy().to_string())
}
AnchorLocation::Url(url) => url::Url::parse(&url).ok().and_then(|url| {
url.path_segments().and_then(|mut segments| {
segments
.next_back()
.map(|segment| Path::new(segment).to_string_lossy().to_string())
})
}),
//FIXME: this probably isn't correct
AnchorLocation::Source(_source) => None,
}
}
#[allow(clippy::cognitive_complexity)]
pub fn view_text_value(value: &Value) {
let config = nu_data::config::config(Tag::unknown())
.ok()
.and_then(|config| config.get("textview").map(Config::from))
.unwrap_or_else(Config::default);
if let UntaggedValue::Primitive(Primitive::String(ref s)) = &value.value {
let mut printer = bat::PrettyPrinter::new();
printer
.term_width(config.term_width)
.tab_width(Some(config.tab_width as usize))
.colored_output(config.colored_output)
.true_color(config.true_color)
.header(config.header)
.line_numbers(config.line_numbers)
.grid(config.grid)
.vcs_modification_markers(config.vcs_modification_markers)
.snip(config.snip)
.wrapping_mode(config.wrapping_mode)
.use_italics(config.use_italics)
.paging_mode(config.paging_mode)
.pager(&config.pager)
.line_ranges(config.line_ranges)
.highlight_range(
config.highlight_range_from as usize,
config.highlight_range_to as usize,
)
.theme(&config.theme);
match value.anchor().and_then(get_file_path) {
Some(file_path) => printer.input_file(file_path),
None => printer.input_from_bytes(s.as_bytes()),
};
printer.print().expect("Error with bat PrettyPrint");
}
}