1
0
mirror of https://github.com/sharkdp/bat.git synced 2025-02-07 05:31:09 +00:00

Initial prototype for Lua plugins

This commit is contained in:
David Peter 2022-05-29 20:38:58 +02:00
parent 3339eee2dc
commit 3811615606
8 changed files with 119 additions and 6 deletions

25
Cargo.lock generated
View File

@ -98,6 +98,7 @@ dependencies = [
"path_abs", "path_abs",
"predicates", "predicates",
"regex", "regex",
"rlua",
"semver", "semver",
"serde", "serde",
"serde_yaml", "serde_yaml",
@ -916,6 +917,30 @@ dependencies = [
"bytemuck", "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]] [[package]]
name = "rustversion" name = "rustversion"
version = "1.0.6" version = "1.0.6"

View File

@ -30,6 +30,7 @@ minimal-application = [
"paging", "paging",
"regex-onig", "regex-onig",
"wild", "wild",
"rlua"
] ]
git = ["git2"] # Support indicating git modifications git = ["git2"] # Support indicating git modifications
paging = ["shell-words", "grep-cli"] # Support applying a pager on the output 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 } regex = { version = "1.5.5", optional = true }
walkdir = { version = "2.0", optional = true } walkdir = { version = "2.0", optional = true }
bytesize = { version = "1.1.0" } bytesize = { version = "1.1.0" }
rlua = { version = "0.19", optional = true }
[dependencies.git2] [dependencies.git2]
version = "0.14" version = "0.14"

21
plugins/uncompress.lua Normal file
View File

@ -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

View File

@ -241,6 +241,11 @@ impl App {
.map(HighlightedLineRanges) .map(HighlightedLineRanges)
.unwrap_or_default(), .unwrap_or_default(),
use_custom_assets: !self.matches.is_present("no-custom-assets"), use_custom_assets: !self.matches.is_present("no-custom-assets"),
plugins: self
.matches
.values_of_os("load-plugin")
.unwrap_or_default()
.collect(),
}) })
} }

View File

@ -474,6 +474,14 @@ pub fn build_app(interactive_output: bool) -> ClapApp<'static, 'static> {
.help("Display all supported languages.") .help("Display all supported languages.")
.long_help("Display a list of supported languages for syntax highlighting."), .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(
Arg::with_name("unbuffered") Arg::with_name("unbuffered")
.short("u") .short("u")

View File

@ -8,6 +8,7 @@ mod directories;
mod input; mod input;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::ffi::OsStr;
use std::io; use std::io;
use std::io::{BufReader, Write}; use std::io::{BufReader, Write};
use std::path::Path; use std::path::Path;
@ -218,7 +219,51 @@ pub fn list_themes(cfg: &Config) -> Result<()> {
Ok(()) Ok(())
} }
fn run_controller(inputs: Vec<Input>, config: &Config) -> Result<bool> { fn load_and_run_preprocess_plugins(plugins: &[&OsStr], inputs: &mut Vec<Input>) -> 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<Input>, config: &Config) -> Result<bool> {
load_and_run_preprocess_plugins(&config.plugins, &mut inputs)?;
let assets = assets_from_cache_or_binary(config.use_custom_assets)?; let assets = assets_from_cache_or_binary(config.use_custom_assets)?;
let controller = Controller::new(config, &assets); let controller = Controller::new(config, &assets);
controller.run(inputs) controller.run(inputs)

View File

@ -5,6 +5,8 @@ use crate::style::StyleComponents;
use crate::syntax_mapping::SyntaxMapping; use crate::syntax_mapping::SyntaxMapping;
use crate::wrapping::WrappingMode; use crate::wrapping::WrappingMode;
use std::ffi::OsStr;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum VisibleLines { pub enum VisibleLines {
/// Show all lines which are included in the line ranges /// 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. /// 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. /// cached assets) are not available, assets from the binary will be used instead.
pub use_custom_assets: bool, pub use_custom_assets: bool,
/// List of bat plugins to be loaded
pub plugins: Vec<&'a OsStr>,
} }
#[cfg(all(feature = "minimal-application", feature = "paging"))] #[cfg(all(feature = "minimal-application", feature = "paging"))]

View File

@ -69,7 +69,8 @@ impl InputDescription {
} }
} }
pub(crate) enum InputKind<'a> { pub enum InputKind<'a> {
// TODO
OrdinaryFile(PathBuf), OrdinaryFile(PathBuf),
StdIn, StdIn,
CustomReader(Box<dyn Read + 'a>), CustomReader(Box<dyn Read + 'a>),
@ -86,14 +87,15 @@ impl<'a> InputKind<'a> {
} }
#[derive(Clone, Default)] #[derive(Clone, Default)]
pub(crate) struct InputMetadata { pub struct InputMetadata {
pub(crate) user_provided_name: Option<PathBuf>, // TODO
pub user_provided_name: Option<PathBuf>,
pub(crate) size: Option<u64>, pub(crate) size: Option<u64>,
} }
pub struct Input<'a> { pub struct Input<'a> {
pub(crate) kind: InputKind<'a>, pub kind: InputKind<'a>, // TODO
pub(crate) metadata: InputMetadata, pub metadata: InputMetadata, // TODO
pub(crate) description: InputDescription, pub(crate) description: InputDescription,
} }