diff --git a/Cargo.lock b/Cargo.lock
index 8babb11a..578c315e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -98,6 +98,7 @@ dependencies = [
"path_abs",
"predicates",
"regex",
+ "rlua",
"semver",
"serde",
"serde_yaml",
@@ -916,6 +917,30 @@ dependencies = [
"bytemuck",
]
+[[package]]
+name = "rlua"
+version = "0.19.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "627ae78424400009e889c43b0c188d0b7af3fe7301b68c03d9cfacb29115408a"
+dependencies = [
+ "bitflags",
+ "bstr",
+ "libc",
+ "num-traits",
+ "rlua-lua54-sys",
+]
+
+[[package]]
+name = "rlua-lua54-sys"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4e1fdfc6a5f7acfa1b1fe26c5076b54f5ebd6818b5982460c39844c8b859370"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+]
+
[[package]]
name = "rustversion"
version = "1.0.6"
diff --git a/Cargo.toml b/Cargo.toml
index deac2b5d..4d2a6a44 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -30,6 +30,7 @@ minimal-application = [
"paging",
"regex-onig",
"wild",
+ "rlua"
]
git = ["git2"] # Support indicating git modifications
paging = ["shell-words", "grep-cli"] # Support applying a pager on the output
@@ -65,6 +66,7 @@ grep-cli = { version = "0.1.6", optional = true }
regex = { version = "1.5.5", optional = true }
walkdir = { version = "2.0", optional = true }
bytesize = { version = "1.1.0" }
+rlua = { version = "0.19", optional = true }
[dependencies.git2]
version = "0.14"
diff --git a/plugins/uncompress.lua b/plugins/uncompress.lua
new file mode 100644
index 00000000..303781f3
--- /dev/null
+++ b/plugins/uncompress.lua
@@ -0,0 +1,21 @@
+function tempdir()
+ local stream = assert(io.popen('mktemp --directory'))
+ local output = stream:read('*all')
+ stream:close()
+ return string.gsub(output, "\n", "")
+end
+
+function preprocess(path)
+ prefix = string.match(path, '^(.*)%.gz$')
+ if prefix then
+ local temp_directory = tempdir()
+ local new_path = temp_directory .. "/" .. prefix
+
+ -- TODO: how to prevent shell injection bugs?
+ os.execute("gunzip < '" .. path .. "' > '" .. new_path .. "'")
+
+ return new_path
+ else
+ return path
+ end
+end
\ No newline at end of file
diff --git a/src/bin/bat/app.rs b/src/bin/bat/app.rs
index 78a0df69..242d27f1 100644
--- a/src/bin/bat/app.rs
+++ b/src/bin/bat/app.rs
@@ -241,6 +241,11 @@ impl App {
.map(HighlightedLineRanges)
.unwrap_or_default(),
use_custom_assets: !self.matches.is_present("no-custom-assets"),
+ plugins: self
+ .matches
+ .values_of_os("load-plugin")
+ .unwrap_or_default()
+ .collect(),
})
}
diff --git a/src/bin/bat/clap_app.rs b/src/bin/bat/clap_app.rs
index c2cdb23f..f786ab11 100644
--- a/src/bin/bat/clap_app.rs
+++ b/src/bin/bat/clap_app.rs
@@ -474,6 +474,14 @@ pub fn build_app(interactive_output: bool) -> ClapApp<'static, 'static> {
.help("Display all supported languages.")
.long_help("Display a list of supported languages for syntax highlighting."),
)
+ .arg(
+ Arg::with_name("load-plugin")
+ .long("load-plugin")
+ .takes_value(true)
+ .value_name("name")
+ .help("Load plugin with specified name.")
+ .hidden_short_help(true)
+ )
.arg(
Arg::with_name("unbuffered")
.short("u")
diff --git a/src/bin/bat/main.rs b/src/bin/bat/main.rs
index 6b2daa2b..b7aaaf86 100644
--- a/src/bin/bat/main.rs
+++ b/src/bin/bat/main.rs
@@ -8,6 +8,7 @@ mod directories;
mod input;
use std::collections::{HashMap, HashSet};
+use std::ffi::OsStr;
use std::io;
use std::io::{BufReader, Write};
use std::path::Path;
@@ -218,7 +219,51 @@ pub fn list_themes(cfg: &Config) -> Result<()> {
Ok(())
}
-fn run_controller(inputs: Vec, config: &Config) -> Result {
+fn load_and_run_preprocess_plugins(plugins: &[&OsStr], inputs: &mut Vec) -> Result<()> {
+ use bat::input::InputKind;
+ use rlua::{Function, Lua, Result as LuaResult};
+ use std::fs;
+ use std::path::PathBuf;
+
+ let lua = Lua::new();
+
+ for plugin_name in plugins {
+ // TODO: how to handle plugin priority?
+ let mut plugin_path = PathBuf::from("plugins");
+ plugin_path.push(plugin_name);
+
+ let plugin_source_code = fs::read_to_string(&plugin_path).unwrap(); // TODO: proper error handling
+
+ lua.context::<_, LuaResult<()>>(|lua_ctx| {
+ let globals = lua_ctx.globals();
+
+ lua_ctx.load(&plugin_source_code).exec()?;
+
+ let preprocess: Function = globals.get("preprocess")?;
+
+ for input in inputs.iter_mut() {
+ if let InputKind::OrdinaryFile(ref mut path) = &mut input.kind {
+ let path_str: String = path.to_string_lossy().into();
+ let new_path = preprocess.call::<_, String>(path_str)?;
+
+ *path = PathBuf::from(new_path);
+
+ input.metadata.user_provided_name =
+ Some(PathBuf::from(path.file_name().unwrap())); // TODO
+ }
+ }
+
+ Ok(())
+ })
+ .map_err(|e| format!("Error while executing Lua code: {}", e))?;
+ }
+
+ Ok(())
+}
+
+fn run_controller(mut inputs: Vec, config: &Config) -> Result {
+ load_and_run_preprocess_plugins(&config.plugins, &mut inputs)?;
+
let assets = assets_from_cache_or_binary(config.use_custom_assets)?;
let controller = Controller::new(config, &assets);
controller.run(inputs)
diff --git a/src/config.rs b/src/config.rs
index 76eb3990..e5f15a3e 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -5,6 +5,8 @@ use crate::style::StyleComponents;
use crate::syntax_mapping::SyntaxMapping;
use crate::wrapping::WrappingMode;
+use std::ffi::OsStr;
+
#[derive(Debug, Clone)]
pub enum VisibleLines {
/// Show all lines which are included in the line ranges
@@ -86,6 +88,9 @@ pub struct Config<'a> {
/// Whether or not to allow custom assets. If this is false or if custom assets (a.k.a.
/// cached assets) are not available, assets from the binary will be used instead.
pub use_custom_assets: bool,
+
+ /// List of bat plugins to be loaded
+ pub plugins: Vec<&'a OsStr>,
}
#[cfg(all(feature = "minimal-application", feature = "paging"))]
diff --git a/src/input.rs b/src/input.rs
index 23e21506..6fad2282 100644
--- a/src/input.rs
+++ b/src/input.rs
@@ -69,7 +69,8 @@ impl InputDescription {
}
}
-pub(crate) enum InputKind<'a> {
+pub enum InputKind<'a> {
+ // TODO
OrdinaryFile(PathBuf),
StdIn,
CustomReader(Box),
@@ -86,14 +87,15 @@ impl<'a> InputKind<'a> {
}
#[derive(Clone, Default)]
-pub(crate) struct InputMetadata {
- pub(crate) user_provided_name: Option,
+pub struct InputMetadata {
+ // TODO
+ pub user_provided_name: Option,
pub(crate) size: Option,
}
pub struct Input<'a> {
- pub(crate) kind: InputKind<'a>,
- pub(crate) metadata: InputMetadata,
+ pub kind: InputKind<'a>, // TODO
+ pub metadata: InputMetadata, // TODO
pub(crate) description: InputDescription,
}