mirror of
https://github.com/sharkdp/bat.git
synced 2025-04-16 07:40:36 +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 inputfile::InputFile;
|
||||||
use line_range::LineRange;
|
use line_range::LineRange;
|
||||||
use style::{OutputComponent, OutputComponents, OutputWrap};
|
use style::{OutputComponent, OutputComponents, OutputWrap};
|
||||||
|
use syntax_mapping::SyntaxMapping;
|
||||||
use util::transpose;
|
use util::transpose;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
@ -66,6 +67,9 @@ pub struct Config<'a> {
|
|||||||
|
|
||||||
/// The syntax highlighting theme
|
/// The syntax highlighting theme
|
||||||
pub theme: String,
|
pub theme: String,
|
||||||
|
|
||||||
|
/// File extension/name mappings
|
||||||
|
pub syntax_mapping: SyntaxMapping,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_truecolor_terminal() -> bool {
|
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 {
|
Ok(Config {
|
||||||
true_color: is_truecolor_terminal(),
|
true_color: is_truecolor_terminal(),
|
||||||
language: self.matches.value_of("language"),
|
language: self.matches.value_of("language"),
|
||||||
@ -202,6 +220,7 @@ impl App {
|
|||||||
.unwrap_or(String::from(BAT_THEME_DEFAULT)),
|
.unwrap_or(String::from(BAT_THEME_DEFAULT)),
|
||||||
line_range: transpose(self.matches.value_of("line-range").map(LineRange::from))?,
|
line_range: transpose(self.matches.value_of("line-range").map(LineRange::from))?,
|
||||||
output_components,
|
output_components,
|
||||||
|
syntax_mapping,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ use dirs::PROJECT_DIRS;
|
|||||||
|
|
||||||
use errors::*;
|
use errors::*;
|
||||||
use inputfile::{InputFile, InputFileReader};
|
use inputfile::{InputFile, InputFileReader};
|
||||||
|
use syntax_mapping::SyntaxMapping;
|
||||||
|
|
||||||
pub const BAT_THEME_DEFAULT: &str = "Monokai Extended";
|
pub const BAT_THEME_DEFAULT: &str = "Monokai Extended";
|
||||||
|
|
||||||
@ -167,6 +168,7 @@ impl HighlightingAssets {
|
|||||||
language: Option<&str>,
|
language: Option<&str>,
|
||||||
filename: InputFile,
|
filename: InputFile,
|
||||||
reader: &mut InputFileReader,
|
reader: &mut InputFileReader,
|
||||||
|
mapping: &SyntaxMapping,
|
||||||
) -> &SyntaxReference {
|
) -> &SyntaxReference {
|
||||||
let syntax = match (language, filename) {
|
let syntax = match (language, filename) {
|
||||||
(Some(language), _) => self.syntax_set.find_syntax_by_token(language),
|
(Some(language), _) => self.syntax_set.find_syntax_by_token(language),
|
||||||
@ -174,10 +176,14 @@ impl HighlightingAssets {
|
|||||||
let path = Path::new(filename);
|
let path = Path::new(filename);
|
||||||
let file_name = path.file_name().and_then(|n| n.to_str()).unwrap_or("");
|
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 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
|
let ext_syntax = self
|
||||||
.syntax_set
|
.syntax_set
|
||||||
.find_syntax_by_extension(file_name)
|
.find_syntax_by_extension(&file_name)
|
||||||
.or_else(|| self.syntax_set.find_syntax_by_extension(extension));
|
.or_else(|| self.syntax_set.find_syntax_by_extension(&extension));
|
||||||
let line_syntax = if ext_syntax.is_none() {
|
let line_syntax = if ext_syntax.is_none() {
|
||||||
String::from_utf8(reader.first_line.clone())
|
String::from_utf8(reader.first_line.clone())
|
||||||
.ok()
|
.ok()
|
||||||
|
@ -59,6 +59,22 @@ 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("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(
|
||||||
Arg::with_name("theme")
|
Arg::with_name("theme")
|
||||||
.long("theme")
|
.long("theme")
|
||||||
|
@ -35,6 +35,7 @@ mod output;
|
|||||||
mod preprocessor;
|
mod preprocessor;
|
||||||
mod printer;
|
mod printer;
|
||||||
mod style;
|
mod style;
|
||||||
|
mod syntax_mapping;
|
||||||
mod terminal;
|
mod terminal;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
|
@ -142,7 +142,7 @@ impl<'a> InteractivePrinter<'a> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Determine the type of syntax for highlighting
|
// 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))
|
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…
x
Reference in New Issue
Block a user