@ -3,6 +3,7 @@ use std::path::Path;
use globset ::{ Candidate , GlobBuilder , GlobMatcher } ;
use crate ::error ::Result ;
use builtin ::BUILTIN_MAPPINGS ;
use ignored_suffixes ::IgnoredSuffixes ;
mod builtin ;
@ -39,201 +40,58 @@ pub enum MappingTarget<'a> {
#[ derive(Debug, Clone, Default) ]
pub struct SyntaxMapping < ' a > {
mappings : Vec < ( GlobMatcher , MappingTarget < ' a > ) > ,
/// User-defined mappings at run time.
custom_mappings : Vec < ( GlobMatcher , MappingTarget < ' a > ) > ,
pub ( crate ) ignored_suffixes : IgnoredSuffixes < ' a > ,
}
impl < ' a > SyntaxMapping < ' a > {
pub fn empty ( ) -> SyntaxMapping < ' a > {
pub fn new ( ) -> SyntaxMapping < ' a > {
Default ::default ( )
}
pub fn builtin ( ) -> SyntaxMapping < ' a > {
let mut mapping = Self ::empty ( ) ;
mapping . insert ( "*.h" , MappingTarget ::MapTo ( "C++" ) ) . unwrap ( ) ;
mapping
. insert ( ".clang-format" , MappingTarget ::MapTo ( "YAML" ) )
. unwrap ( ) ;
mapping . insert ( "*.fs" , MappingTarget ::MapTo ( "F#" ) ) . unwrap ( ) ;
mapping
. insert ( "build" , MappingTarget ::MapToUnknown )
. unwrap ( ) ;
mapping
. insert ( "**/.ssh/config" , MappingTarget ::MapTo ( "SSH Config" ) )
. unwrap ( ) ;
mapping
. insert (
"**/bat/config" ,
MappingTarget ::MapTo ( "Bourne Again Shell (bash)" ) ,
)
. unwrap ( ) ;
mapping
. insert (
"/etc/profile" ,
MappingTarget ::MapTo ( "Bourne Again Shell (bash)" ) ,
)
. unwrap ( ) ;
mapping
. insert (
"os-release" ,
MappingTarget ::MapTo ( "Bourne Again Shell (bash)" ) ,
)
. unwrap ( ) ;
mapping
. insert ( "*.pac" , MappingTarget ::MapTo ( "JavaScript (Babel)" ) )
. unwrap ( ) ;
mapping
. insert ( "fish_history" , MappingTarget ::MapTo ( "YAML" ) )
. unwrap ( ) ;
for glob in [ "*.jsonl" , "*.sarif" ] {
mapping . insert ( glob , MappingTarget ::MapTo ( "JSON" ) ) . unwrap ( ) ;
}
// See #2151, https://nmap.org/book/nse-language.html
mapping
. insert ( "*.nse" , MappingTarget ::MapTo ( "Lua" ) )
. unwrap ( ) ;
// See #1008
mapping
. insert ( "rails" , MappingTarget ::MapToUnknown )
. unwrap ( ) ;
mapping
. insert ( "Containerfile" , MappingTarget ::MapTo ( "Dockerfile" ) )
. unwrap ( ) ;
mapping
. insert ( "*.ksh" , MappingTarget ::MapTo ( "Bourne Again Shell (bash)" ) )
. unwrap ( ) ;
// Nginx and Apache syntax files both want to style all ".conf" files
// see #1131 and #1137
mapping
. insert ( "*.conf" , MappingTarget ::MapExtensionToUnknown )
. unwrap ( ) ;
for glob in & [
"/etc/nginx/**/*.conf" ,
"/etc/nginx/sites-*/**/*" ,
"nginx.conf" ,
"mime.types" ,
] {
mapping . insert ( glob , MappingTarget ::MapTo ( "nginx" ) ) . unwrap ( ) ;
}
for glob in & [
"/etc/apache2/**/*.conf" ,
"/etc/apache2/sites-*/**/*" ,
"httpd.conf" ,
] {
mapping
. insert ( glob , MappingTarget ::MapTo ( "Apache Conf" ) )
. unwrap ( ) ;
}
for glob in & [
"**/systemd/**/*.conf" ,
"**/systemd/**/*.example" ,
"*.automount" ,
"*.device" ,
"*.dnssd" ,
"*.link" ,
"*.mount" ,
"*.netdev" ,
"*.network" ,
"*.nspawn" ,
"*.path" ,
"*.service" ,
"*.scope" ,
"*.slice" ,
"*.socket" ,
"*.swap" ,
"*.target" ,
"*.timer" ,
] {
mapping . insert ( glob , MappingTarget ::MapTo ( "INI" ) ) . unwrap ( ) ;
}
// unix mail spool
for glob in & [ "/var/spool/mail/*" , "/var/mail/*" ] {
mapping . insert ( glob , MappingTarget ::MapTo ( "Email" ) ) . unwrap ( )
}
// pacman hooks
mapping
. insert ( "*.hook" , MappingTarget ::MapTo ( "INI" ) )
. unwrap ( ) ;
mapping
. insert ( "*.ron" , MappingTarget ::MapTo ( "Rust" ) )
. unwrap ( ) ;
// Global git config files rooted in `$XDG_CONFIG_HOME/git/` or `$HOME/.config/git/`
// See e.g. https://git-scm.com/docs/git-config#FILES
match (
std ::env ::var_os ( "XDG_CONFIG_HOME" ) . filter ( | val | ! val . is_empty ( ) ) ,
std ::env ::var_os ( "HOME" )
. filter ( | val | ! val . is_empty ( ) )
. map ( | home | Path ::new ( & home ) . join ( ".config" ) ) ,
) {
( Some ( xdg_config_home ) , Some ( default_config_home ) )
if xdg_config_home = = default_config_home = > {
insert_git_config_global ( & mut mapping , & xdg_config_home )
}
( Some ( xdg_config_home ) , Some ( default_config_home ) ) /* else guard */ = > {
insert_git_config_global ( & mut mapping , & xdg_config_home ) ;
insert_git_config_global ( & mut mapping , & default_config_home )
}
( Some ( config_home ) , None ) = > insert_git_config_global ( & mut mapping , & config_home ) ,
( None , Some ( config_home ) ) = > insert_git_config_global ( & mut mapping , & config_home ) ,
( None , None ) = > ( ) ,
} ;
fn insert_git_config_global ( mapping : & mut SyntaxMapping , config_home : impl AsRef < Path > ) {
let git_config_path = config_home . as_ref ( ) . join ( "git" ) ;
mapping
. insert (
& git_config_path . join ( "config" ) . to_string_lossy ( ) ,
MappingTarget ::MapTo ( "Git Config" ) ,
)
. ok ( ) ;
mapping
. insert (
& git_config_path . join ( "ignore" ) . to_string_lossy ( ) ,
MappingTarget ::MapTo ( "Git Ignore" ) ,
)
. ok ( ) ;
mapping
. insert (
& git_config_path . join ( "attributes" ) . to_string_lossy ( ) ,
MappingTarget ::MapTo ( "Git Attributes" ) ,
)
. ok ( ) ;
}
mapping
}
pub fn insert ( & mut self , from : & str , to : MappingTarget < ' a > ) -> Result < ( ) > {
let matcher = make_glob_matcher ( from ) ? ;
self . mappings. push ( ( matcher , to ) ) ;
self . custom_mappings . push ( ( matcher , to ) ) ;
Ok ( ( ) )
}
pub fn mappings ( & self ) -> & [ ( GlobMatcher , MappingTarget < ' a > ) ] {
& self . mappings
/// Returns all mappings. User-defined mappings are listed before builtin
/// mappings; mappings in front have higher precedence.
///
/// Note that this function ignores builtin mappings that are invalid under
/// the current environment (i.e. their rules require an environment
/// variable that is unset).
pub fn all_mappings ( & self ) -> Vec < ( & GlobMatcher , & MappingTarget < ' a > ) > {
self . custom_mappings ( )
. iter ( )
. map ( | ( matcher , target ) | ( matcher , target ) ) // as_ref
. chain ( self . builtin_mappings ( ) )
. collect ( )
}
/// Returns all valid builtin mappings. Mappings in front have higher
/// precedence.
///
/// If a mapping rule requires an environment variable that is unset, it
/// will be ignored.
pub fn builtin_mappings ( & self ) -> Vec < ( & ' static GlobMatcher , & ' static MappingTarget < ' static > ) > {
BUILTIN_MAPPINGS
. iter ( )
. filter_map ( | ( matcher , target ) | matcher . as_ref ( ) . map ( | glob | ( glob , target ) ) )
. collect ( )
}
/// Returns all user-defined mappings.
pub fn custom_mappings ( & self ) -> & [ ( GlobMatcher , MappingTarget < ' a > ) ] {
& self . custom_mappings
}
pub fn get_syntax_for ( & self , path : impl AsRef < Path > ) -> Option < MappingTarget < ' a > > {
// Try matching on the file name as-is.
let candidate = Candidate ::new ( & path ) ;
let candidate_filename = path . as_ref ( ) . file_name ( ) . map ( Candidate ::new ) ;
for ( ref glob , ref syntax ) in self . mappings . iter ( ) . rev ( ) {
for ( glob , syntax ) in self . all_mappings ( ) . into_iter ( ) {
if glob . is_match_candidate ( & candidate )
| | candidate_filename
. as_ref ( )
@ -261,7 +119,7 @@ mod tests {
use super ::* ;
#[ test ]
fn basic ( ) {
let mut map = SyntaxMapping ::empty ( ) ;
let mut map = SyntaxMapping ::new ( ) ;
map . insert ( "/path/to/Cargo.lock" , MappingTarget ::MapTo ( "TOML" ) )
. ok ( ) ;
map . insert ( "/path/to/.ignore" , MappingTarget ::MapTo ( "Git Ignore" ) )
@ -281,7 +139,7 @@ mod tests {
#[ test ]
fn user_can_override_builtin_mappings ( ) {
let mut map = SyntaxMapping ::builti n( ) ;
let mut map = SyntaxMapping :: new ( ) ;
assert_eq! (
map . get_syntax_for ( "/etc/profile" ) ,
@ -297,35 +155,11 @@ mod tests {
#[ test ]
fn builtin_mappings ( ) {
let map = SyntaxMapping ::builti n( ) ;
let map = SyntaxMapping :: new ( ) ;
assert_eq! (
map . get_syntax_for ( "/path/to/build" ) ,
Some ( MappingTarget ::MapToUnknown )
) ;
}
#[ test ]
/// verifies that SyntaxMapping::builtin() doesn't repeat `Glob`-based keys
fn no_duplicate_builtin_keys ( ) {
let mappings = SyntaxMapping ::builtin ( ) . mappings ;
for i in 0 .. mappings . len ( ) {
let tail = mappings [ i + 1 .. ] . into_iter ( ) ;
let ( dupl , _ ) : ( Vec < _ > , Vec < _ > ) =
tail . partition ( | item | item . 0. glob ( ) = = mappings [ i ] . 0. glob ( ) ) ;
// emit repeats on failure
assert_eq! (
dupl . len ( ) ,
0 ,
"Glob pattern `{}` mapped to multiple: {:?}" ,
mappings [ i ] . 0. glob ( ) . glob ( ) ,
{
let ( _ , mut dupl_targets ) : ( Vec < GlobMatcher > , Vec < MappingTarget > ) =
dupl . into_iter ( ) . cloned ( ) . unzip ( ) ;
dupl_targets . push ( mappings [ i ] . 1 )
} ,
)
}
}
}