mirror of
https://github.com/sharkdp/bat.git
synced 2024-12-22 14:30:42 +01:00
Implement syntax mapping
This adds a `-m`/`--map-syntax` option that allows users to (re)map certain file extensions or file names to an existing syntax. For example: ``` bat --map-syntax .config:json ``` The option can be use multiple times. Note that you can easily make these mappings permanent by using `bat`s new configuration file. closes #169
This commit is contained in:
parent
e43d97dc15
commit
10965a6122
19
src/app.rs
19
src/app.rs
@ -19,6 +19,7 @@ use errors::*;
|
||||
use inputfile::InputFile;
|
||||
use line_range::LineRange;
|
||||
use style::{OutputComponent, OutputComponents, OutputWrap};
|
||||
use syntax_mapping::SyntaxMapping;
|
||||
use util::transpose;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
@ -66,6 +67,9 @@ pub struct Config<'a> {
|
||||
|
||||
/// The syntax highlighting theme
|
||||
pub theme: String,
|
||||
|
||||
/// File extension/name mappings
|
||||
pub syntax_mapping: SyntaxMapping,
|
||||
}
|
||||
|
||||
fn is_truecolor_terminal() -> bool {
|
||||
@ -146,6 +150,20 @@ impl App {
|
||||
}
|
||||
};
|
||||
|
||||
let mut syntax_mapping = SyntaxMapping::new();
|
||||
|
||||
if let Some(values) = self.matches.values_of("map-syntax") {
|
||||
for from_to in values {
|
||||
let parts: Vec<_> = from_to.split(":").collect();
|
||||
|
||||
if parts.len() != 2 {
|
||||
return Err("Invalid syntax mapping. The format of the -m/--map-syntax option is 'from:to'.".into());
|
||||
}
|
||||
|
||||
syntax_mapping.insert(parts[0].into(), parts[1].into());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Config {
|
||||
true_color: is_truecolor_terminal(),
|
||||
language: self.matches.value_of("language"),
|
||||
@ -202,6 +220,7 @@ impl App {
|
||||
.unwrap_or(String::from(BAT_THEME_DEFAULT)),
|
||||
line_range: transpose(self.matches.value_of("line-range").map(LineRange::from))?,
|
||||
output_components,
|
||||
syntax_mapping,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -12,6 +12,7 @@ use dirs::PROJECT_DIRS;
|
||||
|
||||
use errors::*;
|
||||
use inputfile::{InputFile, InputFileReader};
|
||||
use syntax_mapping::SyntaxMapping;
|
||||
|
||||
pub const BAT_THEME_DEFAULT: &str = "Monokai Extended";
|
||||
|
||||
@ -167,6 +168,7 @@ impl HighlightingAssets {
|
||||
language: Option<&str>,
|
||||
filename: InputFile,
|
||||
reader: &mut InputFileReader,
|
||||
mapping: &SyntaxMapping,
|
||||
) -> &SyntaxReference {
|
||||
let syntax = match (language, filename) {
|
||||
(Some(language), _) => self.syntax_set.find_syntax_by_token(language),
|
||||
@ -174,10 +176,14 @@ impl HighlightingAssets {
|
||||
let path = Path::new(filename);
|
||||
let file_name = path.file_name().and_then(|n| n.to_str()).unwrap_or("");
|
||||
let extension = path.extension().and_then(|x| x.to_str()).unwrap_or("");
|
||||
|
||||
let file_name = mapping.replace(file_name);
|
||||
let extension = mapping.replace(extension);
|
||||
|
||||
let ext_syntax = self
|
||||
.syntax_set
|
||||
.find_syntax_by_extension(file_name)
|
||||
.or_else(|| self.syntax_set.find_syntax_by_extension(extension));
|
||||
.find_syntax_by_extension(&file_name)
|
||||
.or_else(|| self.syntax_set.find_syntax_by_extension(&extension));
|
||||
let line_syntax = if ext_syntax.is_none() {
|
||||
String::from_utf8(reader.first_line.clone())
|
||||
.ok()
|
||||
|
@ -59,6 +59,22 @@ 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("map-syntax")
|
||||
.short("m")
|
||||
.long("map-syntax")
|
||||
.multiple(true)
|
||||
.takes_value(true)
|
||||
.value_name("from:to")
|
||||
.help("Map a file extension or name to an existing syntax")
|
||||
.long_help(
|
||||
"Map a file extension or file name to an existing syntax. For example, \
|
||||
to highlight *.conf files with the INI syntax, use '-m conf:ini'. \
|
||||
To highlight files named '.myignore' with the Git Ignore syntax, use \
|
||||
'-m .myignore:gitignore'.",
|
||||
)
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("theme")
|
||||
.long("theme")
|
||||
|
@ -35,6 +35,7 @@ mod output;
|
||||
mod preprocessor;
|
||||
mod printer;
|
||||
mod style;
|
||||
mod syntax_mapping;
|
||||
mod terminal;
|
||||
mod util;
|
||||
|
||||
|
@ -142,7 +142,7 @@ impl<'a> InteractivePrinter<'a> {
|
||||
};
|
||||
|
||||
// Determine the type of syntax for highlighting
|
||||
let syntax = assets.get_syntax(config.language, file, reader);
|
||||
let syntax = assets.get_syntax(config.language, file, reader, &config.syntax_mapping);
|
||||
Some(HighlightLines::new(syntax, theme))
|
||||
};
|
||||
|
||||
|
35
src/syntax_mapping.rs
Normal file
35
src/syntax_mapping.rs
Normal file
@ -0,0 +1,35 @@
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SyntaxMapping(HashMap<String, String>);
|
||||
|
||||
impl SyntaxMapping {
|
||||
pub fn new() -> SyntaxMapping {
|
||||
SyntaxMapping(HashMap::new())
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, from: String, to: String) -> Option<String> {
|
||||
self.0.insert(from, to)
|
||||
}
|
||||
|
||||
pub fn replace<'a>(&self, input: &'a str) -> Cow<'a, str> {
|
||||
let mut out = Cow::from(input);
|
||||
if let Some(value) = self.0.get(input) {
|
||||
out = Cow::from(value.clone())
|
||||
}
|
||||
out
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn basic() {
|
||||
let mut map = SyntaxMapping::new();
|
||||
map.insert("Cargo.lock".into(), "toml".into());
|
||||
map.insert(".ignore".into(), ".gitignore".into());
|
||||
|
||||
assert_eq!("toml", map.replace("Cargo.lock"));
|
||||
assert_eq!("other.lock", map.replace("other.lock"));
|
||||
|
||||
assert_eq!(".gitignore", map.replace(".ignore"));
|
||||
}
|
Loading…
Reference in New Issue
Block a user