Skip to content
This repository has been archived by the owner on Feb 12, 2018. It is now read-only.

Commit

Permalink
Support stacks deeper than 16 elements
Browse files Browse the repository at this point in the history
May not give perfectly accurate results past that, but still mostly
works.
  • Loading branch information
trishume committed Jun 12, 2016
1 parent 35bb0a6 commit 0dd65da
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 29 deletions.
2 changes: 1 addition & 1 deletion examples/syncat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ fn main() {
let mut highlight_state = HighlightState::new(&highlighter, ScopeStack::new());
for maybe_line in file.lines() {
let line = maybe_line.unwrap();
// println!("{}", state.scope_stack);
// println!("{}", highlight_state.path);
let ops = state.parse_line(&line);
// debug_print_ops(&line, &ops);
let iter = HighlightIterator::new(&mut highlight_state, &ops[..], &line, &highlighter);
Expand Down
47 changes: 30 additions & 17 deletions src/scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use std::fmt;
use std::str::FromStr;
use std::u64;
use rustc_serialize::{Encodable, Encoder, Decodable, Decoder};
use std::cmp::Ordering;

lazy_static! {
pub static ref SCOPE_REPO: Mutex<ScopeRepository> = Mutex::new(ScopeRepository::new());
Expand Down Expand Up @@ -236,6 +237,17 @@ impl Decodable for Scope {
}
}

/// Wrapper to get around the fact Rust f64 doesn't implement Ord
/// and there is no non-NaN float type
#[derive(Debug, Copy, Clone, PartialOrd, PartialEq)]
pub struct MatchPower(pub f64);
impl Eq for MatchPower {}
impl Ord for MatchPower {
fn cmp(&self, other: &Self) -> Ordering {
self.partial_cmp(other).unwrap()
}
}

impl ScopeStack {
pub fn new() -> ScopeStack {
ScopeStack { scopes: Vec::new() }
Expand Down Expand Up @@ -288,34 +300,35 @@ impl ScopeStack {
/// matches. Scores are ordered according to the rules found
/// at https://manual.macromates.com/en/scope_selectors
///
/// It accomplishes this ordering through some bit manipulation
/// It accomplishes this ordering through some floating point math
/// ensuring deeper and longer matches matter.
/// Unfortunately it currently will only return reasonable results
/// up to stack depths of 16.
/// Unfortunately it is only guaranteed to return perfectly accurate results
/// up to stack depths of 17, but it should be reasonably good even afterwards.
/// Textmate has the exact same limitation, dunno about Sublime.
/// # Examples
/// ```
/// use syntect::scope::ScopeStack;
/// use syntect::scope::{ScopeStack, MatchPower};
/// use std::str::FromStr;
/// assert_eq!(ScopeStack::from_str("a.b c e.f").unwrap()
/// .does_match(ScopeStack::from_str("a.b c.d e.f.g").unwrap().as_slice()),
/// Some(0x212));
/// Some(MatchPower(0o212u64 as f64)));
/// assert_eq!(ScopeStack::from_str("a c.d.e").unwrap()
/// .does_match(ScopeStack::from_str("a.b c.d e.f.g").unwrap().as_slice()),
/// None);
/// ```
pub fn does_match(&self, stack: &[Scope]) -> Option<u64> {
const ATOM_LEN_BITS: u16 = 4;
pub fn does_match(&self, stack: &[Scope]) -> Option<MatchPower> {
const ATOM_LEN_BITS: u16 = 3;
let mut sel_index: usize = 0;
let mut score: u64 = 0;
let mut score: f64 = 0.0;
for (i, scope) in stack.iter().enumerate() {
let sel_scope = self.scopes[sel_index];
if sel_scope.is_prefix_of(*scope) {
let len = sel_scope.len();
// TODO this breaks on stacks larger than 16 things, maybe use doubles?
score |= (len as u64) << (ATOM_LEN_BITS * (i as u16));
// equivalent to score |= len << (ATOM_LEN_BITS*i) on a large unsigned
score += (len as f64) * ((ATOM_LEN_BITS * (i as u16)) as f64).exp2();
sel_index += 1;
if sel_index >= self.scopes.len() {
return Some(score);
return Some(MatchPower(score));
}
}
}
Expand Down Expand Up @@ -411,31 +424,31 @@ mod tests {
assert_eq!(ScopeStack::from_str("string")
.unwrap()
.does_match(ScopeStack::from_str("string.quoted").unwrap().as_slice()),
Some(1));
Some(MatchPower(0o1u64 as f64)));
assert_eq!(ScopeStack::from_str("source")
.unwrap()
.does_match(ScopeStack::from_str("string.quoted").unwrap().as_slice()),
None);
assert_eq!(ScopeStack::from_str("a.b e.f")
.unwrap()
.does_match(ScopeStack::from_str("a.b c.d e.f.g").unwrap().as_slice()),
Some(0x202));
Some(MatchPower(0o202u64 as f64)));
assert_eq!(ScopeStack::from_str("c e.f")
.unwrap()
.does_match(ScopeStack::from_str("a.b c.d e.f.g").unwrap().as_slice()),
Some(0x210));
Some(MatchPower(0o210u64 as f64)));
assert_eq!(ScopeStack::from_str("c.d e.f")
.unwrap()
.does_match(ScopeStack::from_str("a.b c.d e.f.g").unwrap().as_slice()),
Some(0x220));
Some(MatchPower(0o220u64 as f64)));
assert_eq!(ScopeStack::from_str("a.b c e.f")
.unwrap()
.does_match(ScopeStack::from_str("a.b c.d e.f.g").unwrap().as_slice()),
Some(0x212));
Some(MatchPower(0o212u64 as f64)));
assert_eq!(ScopeStack::from_str("a c.d")
.unwrap()
.does_match(ScopeStack::from_str("a.b c.d e.f.g").unwrap().as_slice()),
Some(0x021));
Some(MatchPower(0o021u64 as f64)));
assert_eq!(ScopeStack::from_str("a c.d.e")
.unwrap()
.does_match(ScopeStack::from_str("a.b c.d e.f.g").unwrap().as_slice()),
Expand Down
2 changes: 1 addition & 1 deletion src/theme/highlighter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub struct Highlighter {
#[derive(Debug, Clone)]
pub struct HighlightState {
styles: Vec<Style>,
path: ScopeStack,
pub path: ScopeStack,
}

#[derive(Debug)]
Expand Down
20 changes: 10 additions & 10 deletions src/theme/selector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ pub struct ScopeSelectors {
}

impl ScopeSelector {
pub fn does_match(&self, stack: &[Scope]) -> Option<u64> {
pub fn does_match(&self, stack: &[Scope]) -> Option<MatchPower> {
if self.exclude.as_ref().and_then(|sel| sel.does_match(stack)).is_some() {
return None;
}
Expand Down Expand Up @@ -48,7 +48,7 @@ impl FromStr for ScopeSelector {
}

impl ScopeSelectors {
pub fn does_match(&self, stack: &[Scope]) -> Option<u64> {
pub fn does_match(&self, stack: &[Scope]) -> Option<MatchPower> {
self.selectors.iter().filter_map(|sel| sel.does_match(stack)).max()
}
}
Expand All @@ -62,12 +62,12 @@ impl FromStr for ScopeSelectors {
/// at https://manual.macromates.com/en/scope_selectors
/// # Examples
/// ```
/// use syntect::scope::ScopeStack;
/// use syntect::scope::{ScopeStack, MatchPower};
/// use syntect::theme::selector::ScopeSelectors;
/// use std::str::FromStr;
/// assert_eq!(ScopeSelectors::from_str("a.b, a e.f - c k, e.f - a.b").unwrap()
/// .does_match(ScopeStack::from_str("a.b c.d j e.f").unwrap().as_slice()),
/// Some(0x2001));
/// Some(MatchPower(0o2001u64 as f64)));
/// ```
fn from_str(s: &str) -> Result<ScopeSelectors, ParseScopeError> {
let mut selectors = Vec::new();
Expand Down Expand Up @@ -96,28 +96,28 @@ mod tests {
}
#[test]
fn matching_works() {
use scope::ScopeStack;
use scope::{ScopeStack,MatchPower};
use theme::selector::*;
use std::str::FromStr;
assert_eq!(ScopeSelectors::from_str("a.b, a e, e.f")
.unwrap()
.does_match(ScopeStack::from_str("a.b e.f").unwrap().as_slice()),
Some(0x20));
Some(MatchPower(0o20u64 as f64)));
assert_eq!(ScopeSelectors::from_str("a.b, a e.f, e.f")
.unwrap()
.does_match(ScopeStack::from_str("a.b e.f").unwrap().as_slice()),
Some(0x21));
Some(MatchPower(0o21u64 as f64)));
assert_eq!(ScopeSelectors::from_str("a.b, a e.f - c j, e.f")
.unwrap()
.does_match(ScopeStack::from_str("a.b c.d j e.f").unwrap().as_slice()),
Some(0x2000));
Some(MatchPower(0o2000u64 as f64)));
assert_eq!(ScopeSelectors::from_str("a.b, a e.f - c j, e.f - a.b")
.unwrap()
.does_match(ScopeStack::from_str("a.b c.d j e.f").unwrap().as_slice()),
Some(0x2));
Some(MatchPower(0o2u64 as f64)));
assert_eq!(ScopeSelectors::from_str("a.b, a e.f - c k, e.f - a.b")
.unwrap()
.does_match(ScopeStack::from_str("a.b c.d j e.f").unwrap().as_slice()),
Some(0x2001));
Some(MatchPower(0o2001u64 as f64)));
}
}

0 comments on commit 0dd65da

Please sign in to comment.