From 97460f8c04402f35ff387dc9bf871c425abe25d8 Mon Sep 17 00:00:00 2001 From: Keith Hall Date: Sat, 13 Oct 2018 13:08:35 +0300 Subject: [PATCH 1/3] when searching for a syntax, search in reverse order this will prioritize those loaded last over those loaded first, in case of conflicts. As syntaxes are loaded deterministically in lexicographical order, this matches Sublime Text's behavior --- src/parsing/syntax_set.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/parsing/syntax_set.rs b/src/parsing/syntax_set.rs index 983db710..b5d05b21 100644 --- a/src/parsing/syntax_set.rs +++ b/src/parsing/syntax_set.rs @@ -127,15 +127,15 @@ impl SyntaxSet { /// This and all similar methods below do a linear search of syntaxes, this should be fast /// because there aren't many syntaxes, but don't think you can call it a bajillion times per second. pub fn find_syntax_by_scope(&self, scope: Scope) -> Option<&SyntaxReference> { - self.syntaxes.iter().find(|&s| s.scope == scope) + self.syntaxes.iter().rev().find(|&s| s.scope == scope) } pub fn find_syntax_by_name<'a>(&'a self, name: &str) -> Option<&'a SyntaxReference> { - self.syntaxes.iter().find(|&s| name == &s.name) + self.syntaxes.iter().rev().find(|&s| name == &s.name) } pub fn find_syntax_by_extension<'a>(&'a self, extension: &str) -> Option<&'a SyntaxReference> { - self.syntaxes.iter().find(|&s| s.file_extensions.iter().any(|e| e == extension)) + self.syntaxes.iter().rev().find(|&s| s.file_extensions.iter().any(|e| e == extension)) } /// Searches for a syntax first by extension and then by case-insensitive name @@ -148,7 +148,7 @@ impl SyntaxSet { return ext_res; } } - self.syntaxes.iter().find(|&syntax| syntax.name.eq_ignore_ascii_case(s)) + self.syntaxes.iter().rev().find(|&syntax| syntax.name.eq_ignore_ascii_case(s)) } /// Try to find the syntax for a file based on its first line. @@ -173,7 +173,7 @@ impl SyntaxSet { pub fn find_syntax_by_path<'a>(&'a self, path: &str) -> Option<&'a SyntaxReference> { let mut slash_path = "/".to_string(); slash_path.push_str(&path); - return self.path_syntaxes.iter().find(|t| t.0.ends_with(&slash_path) || t.0 == path).map(|&(_,i)| &self.syntaxes[i]); + return self.path_syntaxes.iter().rev().find(|t| t.0.ends_with(&slash_path) || t.0 == path).map(|&(_,i)| &self.syntaxes[i]); } /// Convenience method that tries to find the syntax for a file path, @@ -572,6 +572,7 @@ impl FirstLineCache { } } } + regexes.reverse(); FirstLineCache { regexes, } From 6a58517758b6ecfa856773684eaa77876ccbe91c Mon Sep 17 00:00:00 2001 From: Keith Hall Date: Mon, 15 Oct 2018 12:44:22 +0300 Subject: [PATCH 2/3] add tests for reverse order syntax search / syntax overrides --- src/parsing/syntax_set.rs | 55 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/src/parsing/syntax_set.rs b/src/parsing/syntax_set.rs index b5d05b21..a5bec54f 100644 --- a/src/parsing/syntax_set.rs +++ b/src/parsing/syntax_set.rs @@ -521,6 +521,7 @@ impl SyntaxSetBuilder { let context_name = sub_context.as_ref().map_or("main", |x| &**x); syntaxes .iter() + .rev() .find(|s| s.scope == scope) .and_then(|s| s.contexts.get(context_name)) } @@ -528,6 +529,7 @@ impl SyntaxSetBuilder { let context_name = sub_context.as_ref().map_or("main", |x| &**x); syntaxes .iter() + .rev() .find(|s| &s.name == name) .and_then(|s| s.contexts.get(context_name)) } @@ -737,6 +739,59 @@ mod tests { check_send::(); } + #[test] + fn can_override_syntaxes() { + let syntax_set = { + let mut builder = SyntaxSetBuilder::new(); + builder.add(syntax_a()); + builder.add(syntax_b()); + + let syntax_a2 = SyntaxDefinition::load_from_str(r#" + name: A improved + scope: source.a + file_extensions: [a] + first_line_match: syntax\s+a + contexts: + main: + - match: a + scope: a2 + - match: go_b + push: scope:source.b#main + "#, true, None).unwrap(); + + builder.add(syntax_a2); + + let syntax_c = SyntaxDefinition::load_from_str(r#" + name: C + scope: source.c + file_extensions: [c] + first_line_match: syntax\s+.* + contexts: + main: + - match: c + scope: c + - match: go_a + push: scope:source.a#main + "#, true, None).unwrap(); + + builder.add(syntax_c); + + builder.build() + }; + + let mut syntax = syntax_set.find_syntax_by_extension("a").unwrap(); + assert_eq!(syntax.name, "A improved"); + syntax = syntax_set.find_syntax_by_scope(Scope::new(&"source.a").unwrap()).unwrap(); + assert_eq!(syntax.name, "A improved"); + syntax = syntax_set.find_syntax_by_first_line(&"syntax a").unwrap(); + assert_eq!(syntax.name, "C"); + + let mut parse_state = ParseState::new(syntax); + let ops = parse_state.parse_line("c go_a a", &syntax_set); + let expected = (7, ScopeStackOp::Push(Scope::new("a2").unwrap())); + assert_ops_contain(&ops, &expected); + } + fn assert_ops_contain( ops: &[(usize, ScopeStackOp)], expected: &(usize, ScopeStackOp) From 5ad53273f30c492dcb0f76c31fb6b15c9b804159 Mon Sep 17 00:00:00 2001 From: Keith Hall Date: Mon, 15 Oct 2018 13:20:35 +0300 Subject: [PATCH 3/3] make the first line match reverse search consistent with the other reverse searches --- src/parsing/syntax_set.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/parsing/syntax_set.rs b/src/parsing/syntax_set.rs index a5bec54f..cf345edf 100644 --- a/src/parsing/syntax_set.rs +++ b/src/parsing/syntax_set.rs @@ -156,7 +156,7 @@ impl SyntaxSet { /// for matching things like shebangs and mode lines like `-*- Mode: C -*-` pub fn find_syntax_by_first_line<'a>(&'a self, s: &str) -> Option<&'a SyntaxReference> { let cache = self.first_line_cache(); - for &(ref reg, i) in &cache.regexes { + for &(ref reg, i) in cache.regexes.iter().rev() { if reg.find(s).is_some() { return Some(&self.syntaxes[i]); } @@ -574,7 +574,6 @@ impl FirstLineCache { } } } - regexes.reverse(); FirstLineCache { regexes, }