mirror of
				https://github.com/sharkdp/bat.git
				synced 2025-10-31 07:04:04 +00:00 
			
		
		
		
	Compare commits
	
		
			6 Commits
		
	
	
		
			update-war
			...
			bat-plugin
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | aa35cb52c4 | ||
|  | 446b9181e6 | ||
|  | 040242c9be | ||
|  | dbf78d280a | ||
|  | bc91af3ee5 | ||
|  | 3811615606 | 
							
								
								
									
										25
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										25
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| @@ -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" | ||||
|   | ||||
| @@ -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" | ||||
|   | ||||
							
								
								
									
										21
									
								
								plugins/curl.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								plugins/curl.lua
									
									
									
									
									
										Normal 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_or_url) | ||||
|     filename_from_url = string.match(path_or_url, '^https?://.*/(.*)$') | ||||
|     if filename_from_url then | ||||
|         local temp_directory = tempdir() | ||||
|         local new_path = temp_directory .. "/" .. filename_from_url | ||||
|  | ||||
|         -- TODO: how to prevent shell injection bugs? | ||||
|         os.execute("curl --silent '" .. path_or_url .. "' --output '" .. new_path .. "'") | ||||
|  | ||||
|         return new_path | ||||
|     else | ||||
|         return path_or_url | ||||
|     end | ||||
| end | ||||
							
								
								
									
										17
									
								
								plugins/directories.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								plugins/directories.lua
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| -- https://stackoverflow.com/a/3254007/704831 | ||||
| function is_dir(path) | ||||
|     local f = io.open(path, "r") | ||||
|     local ok, err, code = f:read(1) | ||||
|     f:close() | ||||
|     return code == 21 | ||||
| end | ||||
|  | ||||
| function preprocess(path) | ||||
|     if is_dir(path) then | ||||
|         tmpfile = os.tmpname() | ||||
|         os.execute("ls -alh --color=always '" .. path .. "' > '" .. tmpfile .. "'") | ||||
|         return tmpfile | ||||
|     else | ||||
|         return path | ||||
|     end | ||||
| end | ||||
							
								
								
									
										21
									
								
								plugins/hexyl.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								plugins/hexyl.lua
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| -- Note: this plugin depends on the existence of 'inspect' [1] and 'hexyl' [2] | ||||
| -- | ||||
| -- [1] https://github.com/sharkdp/content_inspector | ||||
| -- [2] https://github.com/sharkdp/hexyl | ||||
|  | ||||
| function is_binary(path) | ||||
|     local stream = assert(io.popen("inspect '" .. path .. "'")) | ||||
|     local output = stream:read('*all') | ||||
|     stream:close() | ||||
|     return string.find(output, ": binary\n") | ||||
| end | ||||
|  | ||||
| function preprocess(path) | ||||
|     if is_binary(path) then | ||||
|         tmpfile = os.tmpname() | ||||
|         os.execute("hexyl --length 1024 --no-position --border=none --no-squeezing '" .. path .. "' > '" .. tmpfile .. "'") | ||||
|         return tmpfile | ||||
|     else | ||||
|         return path | ||||
|     end | ||||
| end | ||||
							
								
								
									
										21
									
								
								plugins/uncompress.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								plugins/uncompress.lua
									
									
									
									
									
										Normal 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 | ||||
| @@ -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(), | ||||
|         }) | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -474,6 +474,16 @@ 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") | ||||
|             .multiple(true) | ||||
|             .takes_value(true) | ||||
|             .number_of_values(1) | ||||
|             .value_name("name") | ||||
|             .help("Load plugin with specified name.") | ||||
|             .hidden_short_help(true) | ||||
|         ) | ||||
|         .arg( | ||||
|             Arg::with_name("unbuffered") | ||||
|                 .short("u") | ||||
|   | ||||
| @@ -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,66 @@ pub fn list_themes(cfg: &Config) -> Result<()> { | ||||
|     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; | ||||
|  | ||||
|     if plugins.is_empty() { | ||||
|         // Do not create Lua context if there are no plugins | ||||
|         return Ok(()); | ||||
|     } | ||||
|  | ||||
|     let lua = Lua::new(); | ||||
|  | ||||
|     for plugin_name in plugins { | ||||
|         // TODO: properly load plugins from a central directory + user directories | ||||
|         // 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).map_err(|e| { | ||||
|             format!( | ||||
|                 "Could not load bat plugin '{}': {}", | ||||
|                 plugin_path.to_string_lossy(), | ||||
|                 e | ||||
|             ) | ||||
|         })?; | ||||
|  | ||||
|         lua.context::<_, LuaResult<()>>(|lua_ctx| { | ||||
|             let globals = lua_ctx.globals(); | ||||
|  | ||||
|             lua_ctx.load(&plugin_source_code).exec()?; | ||||
|  | ||||
|             // Plugins are expected to have a 'preprocess' function | ||||
|             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); | ||||
|  | ||||
|                     // TODO: the following line overwrites actual user provided names. However, | ||||
|                     // this is necessary to get proper syntax highlighting for the path that | ||||
|                     // is being provided by the plugin. | ||||
|                     input.metadata.user_provided_name = Some(path.clone()); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             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 controller = Controller::new(config, &assets); | ||||
|     controller.run(inputs) | ||||
|   | ||||
| @@ -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"))] | ||||
|   | ||||
							
								
								
									
										12
									
								
								src/input.rs
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								src/input.rs
									
									
									
									
									
								
							| @@ -69,7 +69,8 @@ impl InputDescription { | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub(crate) enum InputKind<'a> { | ||||
| pub enum InputKind<'a> { | ||||
|     // TODO | ||||
|     OrdinaryFile(PathBuf), | ||||
|     StdIn, | ||||
|     CustomReader(Box<dyn Read + 'a>), | ||||
| @@ -86,14 +87,15 @@ impl<'a> InputKind<'a> { | ||||
| } | ||||
|  | ||||
| #[derive(Clone, Default)] | ||||
| pub(crate) struct InputMetadata { | ||||
|     pub(crate) user_provided_name: Option<PathBuf>, | ||||
| pub struct InputMetadata { | ||||
|     // TODO | ||||
|     pub user_provided_name: Option<PathBuf>, | ||||
|     pub(crate) size: Option<u64>, | ||||
| } | ||||
|  | ||||
| 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, | ||||
| } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user