diff --git a/Readme.md b/Readme.md index 61e30d1f..ec719d06 100644 --- a/Readme.md +++ b/Readme.md @@ -80,7 +80,7 @@ let syntax = ps.find_syntax_by_extension("rs").unwrap(); let mut h = HighlightLines::new(syntax, &ts.themes["base16-ocean.dark"]); let s = "pub struct Wow { hi: u64 }\nfn blah() -> u64 {}"; for line in LinesWithEndings::from(s) { - let ranges: Vec<(Style, &str)> = h.highlight_line(line, &ps); + let ranges: Vec<(Style, &str)> = h.highlight_line(line, &ps).unwrap(); let escaped = as_24_bit_terminal_escaped(&ranges[..], true); print!("{}", escaped); } diff --git a/benches/highlight_utils/mod.rs b/benches/highlight_utils/mod.rs index eeb1a08a..3883ff72 100644 --- a/benches/highlight_utils/mod.rs +++ b/benches/highlight_utils/mod.rs @@ -7,7 +7,7 @@ pub fn do_highlight(s: &str, syntax_set: &SyntaxSet, syntax: &SyntaxReference, t let mut h = HighlightLines::new(syntax, theme); let mut count = 0; for line in s.lines() { - let regions = h.highlight_line(line, syntax_set); + let regions = h.highlight_line(line, syntax_set).unwrap(); count += regions.len(); } count diff --git a/benches/parsing.rs b/benches/parsing.rs index 420ac7e4..58790201 100644 --- a/benches/parsing.rs +++ b/benches/parsing.rs @@ -8,7 +8,7 @@ fn do_parse(s: &str, ss: &SyntaxSet, syntax: &SyntaxReference) -> usize { let mut state = ParseState::new(syntax); let mut count = 0; for line in s.lines() { - let ops = state.parse_line(line, ss); + let ops = state.parse_line(line, ss).unwrap(); count += ops.len(); } count diff --git a/examples/latex-demo.rs b/examples/latex-demo.rs index ee5cec57..d29cdc2e 100644 --- a/examples/latex-demo.rs +++ b/examples/latex-demo.rs @@ -13,7 +13,7 @@ fn main() { let mut h = HighlightLines::new(syntax, &ts.themes["InspiredGitHub"]); for line in LinesWithEndings::from(s) { // LinesWithEndings enables use of newlines mode - let ranges: Vec<(Style, &str)> = h.highlight_line(line, &ps); + let ranges: Vec<(Style, &str)> = h.highlight_line(line, &ps).unwrap(); let escaped = as_latex_escaped(&ranges[..]); println!("\n{:?}", line); println!("\n{}", escaped); diff --git a/examples/parsyncat.rs b/examples/parsyncat.rs index 83459469..7bbfefb7 100644 --- a/examples/parsyncat.rs +++ b/examples/parsyncat.rs @@ -45,7 +45,7 @@ fn main() { let mut highlighter = HighlightFile::new(filename, &syntax_set, theme).unwrap(); for line in contents { - for region in highlighter.highlight_lines.highlight_line(line, &syntax_set) { + for region in highlighter.highlight_lines.highlight_line(line, &syntax_set).unwrap() { regions.push(region); } } diff --git a/examples/syncat.rs b/examples/syncat.rs index 60da27fc..d21795bc 100644 --- a/examples/syncat.rs +++ b/examples/syncat.rs @@ -103,7 +103,7 @@ fn main() { } { - let regions: Vec<(Style, &str)> = highlighter.highlight_lines.highlight_line(&line, &ss); + let regions: Vec<(Style, &str)> = highlighter.highlight_lines.highlight_line(&line, &ss).unwrap(); print!("{}", as_24_bit_terminal_escaped(®ions[..], true)); } line.clear(); diff --git a/examples/synhtml-css-classes.rs b/examples/synhtml-css-classes.rs index b52a088a..80d8436a 100644 --- a/examples/synhtml-css-classes.rs +++ b/examples/synhtml-css-classes.rs @@ -46,7 +46,7 @@ fn main() { let sr_rs = ss.find_syntax_by_extension("rs").unwrap(); let mut rs_html_generator = ClassedHTMLGenerator::new_with_class_style(sr_rs, &ss, ClassStyle::Spaced); for line in LinesWithEndings::from(code_rs) { - rs_html_generator.parse_html_for_line_which_includes_newline(line); + rs_html_generator.parse_html_for_line_which_includes_newline(line).unwrap(); } let html_rs = rs_html_generator.finalize(); @@ -64,7 +64,7 @@ int main() { let sr_cpp = ss.find_syntax_by_extension("cpp").unwrap(); let mut cpp_html_generator = ClassedHTMLGenerator::new_with_class_style(sr_cpp, &ss, ClassStyle::Spaced); for line in LinesWithEndings::from(code_cpp) { - cpp_html_generator.parse_html_for_line_which_includes_newline(line); + cpp_html_generator.parse_html_for_line_which_includes_newline(line).unwrap(); } let html_cpp = cpp_html_generator.finalize(); diff --git a/examples/synstats.rs b/examples/synstats.rs index 74235e38..92f108b0 100644 --- a/examples/synstats.rs +++ b/examples/synstats.rs @@ -133,7 +133,7 @@ fn count(ss: &SyntaxSet, path: &Path, stats: &mut Stats) { let mut stack = ScopeStack::new(); while reader.read_line(&mut line).unwrap() > 0 { { - let ops = state.parse_line(&line, ss); + let ops = state.parse_line(&line, ss).unwrap(); stats.chars += line.len(); count_line(&ops, &line, &mut stack, stats); } diff --git a/examples/syntest.rs b/examples/syntest.rs index 3e7488eb..a2df166e 100644 --- a/examples/syntest.rs +++ b/examples/syntest.rs @@ -273,7 +273,7 @@ fn test_file( current_line_number, stack ); } - let ops = state.parse_line(&line, ss); + let ops = state.parse_line(&line, ss).unwrap(); if out_opts.debug && !line_only_has_assertion { if ops.is_empty() && !line.is_empty() { println!("no operations for this line..."); diff --git a/src/easy.rs b/src/easy.rs index 98c6f813..80985217 100644 --- a/src/easy.rs +++ b/src/easy.rs @@ -2,6 +2,7 @@ //! files without caring about intermediate semantic representation //! and caching. +use crate::Error; use crate::parsing::{ScopeStack, ParseState, SyntaxReference, SyntaxSet, ScopeStackOp}; use crate::highlighting::{Highlighter, HighlightState, HighlightIterator, Theme, Style}; use std::io::{self, BufReader}; @@ -34,7 +35,7 @@ use std::path::Path; /// let mut h = HighlightLines::new(syntax, &ts.themes["base16-ocean.dark"]); /// let s = "pub struct Wow { hi: u64 }\nfn blah() -> u64 {}"; /// for line in LinesWithEndings::from(s) { // LinesWithEndings enables use of newlines mode -/// let ranges: Vec<(Style, &str)> = h.highlight_line(line, &ps); +/// let ranges: Vec<(Style, &str)> = h.highlight_line(line, &ps).unwrap(); /// let escaped = as_24_bit_terminal_escaped(&ranges[..], true); /// print!("{}", escaped); /// } @@ -58,18 +59,18 @@ impl<'a> HighlightLines<'a> { #[deprecated(since="5.0.0", note="Renamed to `highlight_line` to make it clear it should be passed a single line at a time")] pub fn highlight<'b>(&mut self, line: &'b str, syntax_set: &SyntaxSet) -> Vec<(Style, &'b str)> { - self.highlight_line(line, syntax_set) + self.highlight_line(line, syntax_set).expect("`highlight` is deprecated, use `highlight_line` instead") } /// Highlights a line of a file - pub fn highlight_line<'b>(&mut self, line: &'b str, syntax_set: &SyntaxSet) -> Vec<(Style, &'b str)> { + pub fn highlight_line<'b>(&mut self, line: &'b str, syntax_set: &SyntaxSet) -> Result, Error> { // println!("{}", self.highlight_state.path); - let ops = self.parse_state.parse_line(line, syntax_set); + let ops = self.parse_state.parse_line(line, syntax_set)?; // use util::debug_print_ops; // debug_print_ops(line, &ops); let iter = HighlightIterator::new(&mut self.highlight_state, &ops[..], line, &self.highlighter); - iter.collect() + Ok(iter.collect()) } } @@ -113,7 +114,7 @@ impl<'a> HighlightFile<'a> { /// let mut line = String::new(); /// while highlighter.reader.read_line(&mut line)? > 0 { /// { - /// let regions: Vec<(Style, &str)> = highlighter.highlight_lines.highlight_line(&line, &ss); + /// let regions: Vec<(Style, &str)> = highlighter.highlight_lines.highlight_line(&line, &ss).unwrap(); /// print!("{}", as_24_bit_terminal_escaped(®ions[..], true)); /// } // until NLL this scope is needed so we can clear the buffer after /// line.clear(); // read_line appends so we need to clear between lines @@ -137,7 +138,7 @@ impl<'a> HighlightFile<'a> { /// let mut highlighter = HighlightFile::new("testdata/highlight_test.erb", &ss, &ts.themes["base16-ocean.dark"]).unwrap(); /// for maybe_line in highlighter.reader.lines() { /// let line = maybe_line.unwrap(); - /// let regions: Vec<(Style, &str)> = highlighter.highlight_lines.highlight_line(&line, &ss); + /// let regions: Vec<(Style, &str)> = highlighter.highlight_lines.highlight_line(&line, &ss).unwrap(); /// println!("{}", as_24_bit_terminal_escaped(®ions[..], true)); /// } /// ``` @@ -271,7 +272,7 @@ mod tests { let ts = ThemeSet::load_defaults(); let syntax = ss.find_syntax_by_extension("rs").unwrap(); let mut h = HighlightLines::new(syntax, &ts.themes["base16-ocean.dark"]); - let ranges = h.highlight_line("pub struct Wow { hi: u64 }", &ss); + let ranges = h.highlight_line("pub struct Wow { hi: u64 }", &ss).expect("#[cfg(test)]"); assert!(ranges.len() > 4); } @@ -292,7 +293,7 @@ mod tests { let ss = SyntaxSet::load_defaults_nonewlines(); let mut state = ParseState::new(ss.find_syntax_by_extension("rb").unwrap()); let line = "lol =5+2"; - let ops = state.parse_line(line, &ss); + let ops = state.parse_line(line, &ss).expect("#[cfg(test)]"); let mut stack = ScopeStack::new(); let mut token_count = 0; @@ -320,7 +321,7 @@ mod tests { let mut stack = ScopeStack::new(); for line in lines.iter() { - let ops = state.parse_line(line, &ss); + let ops = state.parse_line(line, &ss).expect("#[cfg(test)]"); println!("{:?}", ops); let mut iterated_ops: Vec<&ScopeStackOp> = Vec::new(); diff --git a/src/highlighting/highlighter.rs b/src/highlighting/highlighter.rs index 0c7c8ec5..d16dcaad 100644 --- a/src/highlighting/highlighter.rs +++ b/src/highlighting/highlighter.rs @@ -389,7 +389,7 @@ mod tests { let mut highlight_state = HighlightState::new(&highlighter, ScopeStack::new()); let line = "module Bob::Wow::Troll::Five; 5; end"; - let ops = state.parse_line(line, &ps); + let ops = state.parse_line(line, &ps).expect("#[cfg(test)]"); let iter = HighlightIterator::new(&mut highlight_state, &ops[..], line, &highlighter); let regions: Vec<(Style, &str)> = iter.collect(); // println!("{:#?}", regions); @@ -426,7 +426,7 @@ mod tests { // We start by parsing a python multiline-comment: """ let mut highlight_state = HighlightState::new(&highlighter, ScopeStack::new()); let line = r#"""""#; - let ops = state.parse_line(line, &ps); + let ops = state.parse_line(line, &ps).expect("#[cfg(test)]"); let iter = HighlightIterator::new(&mut highlight_state, &ops[..], line, &highlighter); assert_eq!(1, iter.count()); let path = highlight_state.path; @@ -434,7 +434,7 @@ mod tests { // We then parse the next line with a highlight state built from the previous state let mut highlight_state = HighlightState::new(&highlighter, path); let line = "multiline comment"; - let ops = state.parse_line(line, &ps); + let ops = state.parse_line(line, &ps).expect("#[cfg(test)]"); let iter = HighlightIterator::new(&mut highlight_state, &ops[..], line, &highlighter); let regions: Vec<(Style, &str)> = iter.collect(); @@ -548,7 +548,7 @@ mod tests { let mut highlight_state = HighlightState::new(&highlighter, ScopeStack::new()); let line = "module Bob::Wow::Troll::Five; 5; end"; - let ops = state.parse_line(line, &ps); + let ops = state.parse_line(line, &ps).expect("#[cfg(test)]"); let iter = RangedHighlightIterator::new(&mut highlight_state, &ops[..], line, &highlighter); let regions: Vec<(Style, &str, Range)> = iter.collect(); // println!("{:#?}", regions); diff --git a/src/highlighting/theme.rs b/src/highlighting/theme.rs index e6ba9a9f..10a23b9e 100644 --- a/src/highlighting/theme.rs +++ b/src/highlighting/theme.rs @@ -31,7 +31,7 @@ pub struct ThemeSettings { /// Color of the caret. pub caret: Option, /// Color of the line the caret is in. - /// Only used when the `higlight_line` setting is set to `true`. + /// Only used when the `highlight_line` setting is set to `true`. pub line_highlight: Option, /// The color to use for the squiggly underline drawn under misspelled words. diff --git a/src/html.rs b/src/html.rs index b1e0de30..28a15da5 100644 --- a/src/html.rs +++ b/src/html.rs @@ -87,8 +87,8 @@ impl<'a> ClassedHTMLGenerator<'a> { /// /// *Note:* This function requires `line` to include a newline at the end and /// also use of the `load_defaults_newlines` version of the syntaxes. - pub fn parse_html_for_line_which_includes_newline(&mut self, line: &str) { - let parsed_line = self.parse_state.parse_line(line, self.syntax_set); + pub fn parse_html_for_line_which_includes_newline(&mut self, line: &str) -> Result<(), Error>{ + let parsed_line = self.parse_state.parse_line(line, self.syntax_set)?; let (formatted_line, delta) = line_tokens_to_classed_spans( line, parsed_line.as_slice(), @@ -97,6 +97,8 @@ impl<'a> ClassedHTMLGenerator<'a> { ); self.open_spans += delta; self.html.push_str(formatted_line.as_str()); + + Ok(()) } /// Parse the line of code and update the internal HTML buffer with tagged HTML @@ -110,7 +112,7 @@ impl<'a> ClassedHTMLGenerator<'a> { /// but this function can't be changed without breaking compatibility so is deprecated. #[deprecated(since="4.5.0", note="Please use `parse_html_for_line_which_includes_newline` instead")] pub fn parse_html_for_line(&mut self, line: &str) { - self.parse_html_for_line_which_includes_newline(line); + self.parse_html_for_line_which_includes_newline(line).expect("Please use `parse_html_for_line_which_includes_newline` instead"); // retain newline self.html.push('\n'); } @@ -274,7 +276,7 @@ pub fn highlighted_html_for_string( let (mut output, bg) = start_highlighted_html_snippet(theme); for line in LinesWithEndings::from(s) { - let regions = highlighter.highlight_line(line, ss); + let regions = highlighter.highlight_line(line, ss)?; append_highlighted_html_for_styled_line( ®ions[..], IncludeBackground::IfDifferent(bg), @@ -302,7 +304,7 @@ pub fn highlighted_html_for_file>( let mut line = String::new(); while highlighter.reader.read_line(&mut line)? > 0 { { - let regions = highlighter.highlight_lines.highlight_line(&line, ss); + let regions = highlighter.highlight_lines.highlight_line(&line, ss)?; append_highlighted_html_for_styled_line( ®ions[..], IncludeBackground::IfDifferent(bg), @@ -433,7 +435,7 @@ fn write_css_color(s: &mut String, c: Color) { /// /// let syntax = ps.find_syntax_by_name("Ruby").unwrap(); /// let mut h = HighlightLines::new(syntax, &ts.themes["base16-ocean.dark"]); -/// let regions = h.highlight_line("5", &ps); +/// let regions = h.highlight_line("5", &ps).unwrap(); /// let html = styled_line_to_highlighted_html(®ions[..], IncludeBackground::No).unwrap(); /// assert_eq!(html, "5"); /// ``` @@ -535,7 +537,7 @@ mod tests { let syntax = ss.find_syntax_by_name("Markdown").unwrap(); let mut state = ParseState::new(syntax); let line = "[w](t.co) *hi* **five**"; - let ops = state.parse_line(line, &ss); + let ops = state.parse_line(line, &ss).expect("#[cfg(test)]"); let mut stack = ScopeStack::new(); // use util::debug_print_ops; @@ -619,7 +621,7 @@ mod tests { let mut html_generator = ClassedHTMLGenerator::new_with_class_style(syntax, &syntax_set, ClassStyle::Spaced); for line in LinesWithEndings::from(current_code) { - html_generator.parse_html_for_line_which_includes_newline(line); + html_generator.parse_html_for_line_which_includes_newline(line).expect("#[cfg(test)]"); } html_generator.finalize(); } @@ -633,7 +635,7 @@ mod tests { let mut html_generator = ClassedHTMLGenerator::new_with_class_style(syntax, &syntax_set, ClassStyle::Spaced); for line in LinesWithEndings::from(current_code) { - html_generator.parse_html_for_line_which_includes_newline(line); + html_generator.parse_html_for_line_which_includes_newline(line).expect("#[cfg(test)]"); } let html = html_generator.finalize(); assert_eq!(html, "x + y\n"); @@ -650,7 +652,7 @@ mod tests { ClassStyle::SpacedPrefixed { prefix: "foo-" }, ); for line in LinesWithEndings::from(current_code) { - html_generator.parse_html_for_line_which_includes_newline(line); + html_generator.parse_html_for_line_which_includes_newline(line).expect("#[cfg(test)]"); } let html = html_generator.finalize(); assert_eq!(html, "x + y\n"); @@ -668,7 +670,7 @@ fn main() { let mut html_generator = ClassedHTMLGenerator::new_with_class_style(syntax, &syntax_set, ClassStyle::Spaced); for line in LinesWithEndings::from(code) { - html_generator.parse_html_for_line_which_includes_newline(line); + html_generator.parse_html_for_line_which_includes_newline(line).expect("#[cfg(test)]"); } let html = html_generator.finalize(); assert_eq!(html, "// Rust source\nfn main() {\n println!("Hello World!");\n}\n"); diff --git a/src/lib.rs b/src/lib.rs index 60d1f62d..29eb02cd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -54,6 +54,10 @@ pub enum Error { /// An error occurred while loading a syntax or theme #[error("Loading error: {0}")] LoadingError(#[from] LoadingError), + /// An error occurred while parsing + #[cfg(feature = "parsing")] + #[error("Parsing error: {0}")] + ParsingError(#[from] crate::parsing::ParsingError), /// Formatting error #[error("Formatting error: {0}")] Fmt(#[from] std::fmt::Error), diff --git a/src/parsing/parser.rs b/src/parsing/parser.rs index bd36f632..c8071217 100644 --- a/src/parsing/parser.rs +++ b/src/parsing/parser.rs @@ -19,6 +19,17 @@ use std::i32; use std::hash::BuildHasherDefault; use fnv::FnvHasher; use crate::parsing::syntax_set::{SyntaxSet, SyntaxReference}; +use crate::parsing::syntax_definition::ContextId; + +/// Errors that can occur while parsing. +#[derive(Debug, thiserror::Error)] +#[non_exhaustive] +pub enum ParsingError { + /// A context is missing. Usually caused by a syntax referencing a another + /// syntax that is not known to syntect. See e.g. + #[error("Missing context with ID '{0:?}'")] + MissingContext(ContextId), +} /// Keeps the current parser state (the internal syntax interpreter stack) between lines of parsing. /// @@ -194,7 +205,11 @@ impl ParseState { /// [`ScopeStack::apply`]: struct.ScopeStack.html#method.apply /// [`SyntaxSet`]: struct.SyntaxSet.html /// [`ParseState`]: struct.ParseState.html - pub fn parse_line(&mut self, line: &str, syntax_set: &SyntaxSet) -> Vec<(usize, ScopeStackOp)> { + pub fn parse_line( + &mut self, + line: &str, + syntax_set: &SyntaxSet, + ) -> Result, ParsingError> { assert!(!self.stack.is_empty(), "Somehow main context was popped from the stack"); let mut match_start = 0; @@ -202,7 +217,7 @@ impl ParseState { if self.first_line { let cur_level = &self.stack[self.stack.len() - 1]; - let context = syntax_set.get_context(&cur_level.context); + let context = syntax_set.get_context(&cur_level.context)?; if !context.meta_content_scope.is_empty() { res.push((0, ScopeStackOp::Push(context.meta_content_scope[0]))); } @@ -223,9 +238,9 @@ impl ParseState { &mut regions, &mut non_consuming_push_at, &mut res - ) {} + )? {} - res + Ok(res) } #[allow(clippy::too_many_arguments)] @@ -238,7 +253,7 @@ impl ParseState { regions: &mut Region, non_consuming_push_at: &mut (usize, usize), ops: &mut Vec<(usize, ScopeStackOp)>, - ) -> bool { + ) -> Result { let check_pop_loop = { let (pos, stack_depth) = *non_consuming_push_at; pos == *start && stack_depth == self.stack.len() @@ -249,7 +264,7 @@ impl ParseState { self.proto_starts.pop(); } - let best_match = self.find_best_match(line, *start, syntax_set, search_cache, regions, check_pop_loop); + let best_match = self.find_best_match(line, *start, syntax_set, search_cache, regions, check_pop_loop)?; if let Some(reg_match) = best_match { if reg_match.would_loop { @@ -268,11 +283,11 @@ impl ParseState { // unicode characters can be more than 1 byte. if let Some((i, _)) = line[*start..].char_indices().nth(1) { *start += i; - return true; + return Ok(true); } else { // End of line, no character to advance and no point trying // any more patterns. - return false; + return Ok(false); } } @@ -301,13 +316,13 @@ impl ParseState { let level_context = { let id = &self.stack[self.stack.len() - 1].context; - syntax_set.get_context(id) + syntax_set.get_context(id)? }; - self.exec_pattern(line, ®_match, level_context, syntax_set, ops); + self.exec_pattern(line, ®_match, level_context, syntax_set, ops)?; - true + Ok(true) } else { - false + Ok(false) } } @@ -319,9 +334,9 @@ impl ParseState { search_cache: &mut SearchCache, regions: &mut Region, check_pop_loop: bool, - ) -> Option> { + ) -> Result>, ParsingError> { let cur_level = &self.stack[self.stack.len() - 1]; - let context = syntax_set.get_context(&cur_level.context); + let context = syntax_set.get_context(&cur_level.context)?; let prototype = if let Some(ref p) = context.prototype { Some(p) } else { @@ -346,7 +361,7 @@ impl ParseState { let mut pop_would_loop = false; for (from_with_proto, ctx, captures) in context_chain { - for (pat_context, pat_index) in context_iter(syntax_set, syntax_set.get_context(ctx)) { + for (pat_context, pat_index) in context_iter(syntax_set, syntax_set.get_context(ctx)?) { let match_pat = pat_context.match_at(pat_index); if let Some(match_region) = self.search( @@ -381,13 +396,13 @@ impl ParseState { if match_start == start && !pop_would_loop { // We're not gonna find a better match after this, // so as an optimization we can stop matching now. - return best_match; + return Ok(best_match); } } } } } - best_match + Ok(best_match) } fn search(&self, @@ -457,7 +472,7 @@ impl ParseState { level_context: &'a Context, syntax_set: &'a SyntaxSet, ops: &mut Vec<(usize, ScopeStackOp)>, - ) -> bool { + ) -> Result { let (match_start, match_end) = reg_match.regions.pos(0).unwrap(); let context = reg_match.context; let pat = context.match_at(reg_match.pat_index); @@ -612,7 +627,7 @@ impl ParseState { regions: &Region, pat: &MatchPattern, syntax_set: &SyntaxSet - ) -> bool { + ) -> Result { let (ctx_refs, old_proto_ids) = match pat.operation { MatchOperation::Push(ref ctx_refs) => (ctx_refs, None), MatchOperation::Set(ref ctx_refs) => { @@ -623,9 +638,9 @@ impl ParseState { } MatchOperation::Pop => { self.stack.pop(); - return true; + return Ok(true); } - MatchOperation::None => return false, + MatchOperation::None => return Ok(false), }; for (i, r) in ctx_refs.iter().enumerate() { let mut proto_ids = if i == 0 { @@ -646,11 +661,11 @@ impl ParseState { } } let context_id = r.id(); - let context = syntax_set.get_context(&context_id); + let context = syntax_set.get_context(&context_id)?; let captures = { let mut uses_backrefs = context.uses_backrefs; if !proto_ids.is_empty() { - uses_backrefs = uses_backrefs || proto_ids.iter().any(|id| syntax_set.get_context(id).uses_backrefs); + uses_backrefs = uses_backrefs || proto_ids.iter().any(|id| syntax_set.get_context(id).unwrap().uses_backrefs); } if uses_backrefs { Some((regions.clone(), line.to_owned())) @@ -664,7 +679,7 @@ impl ParseState { captures, }); } - true + Ok(true) } } @@ -1803,7 +1818,7 @@ contexts: } fn ops(state: &mut ParseState, line: &str, syntax_set: &SyntaxSet) -> Vec<(usize, ScopeStackOp)> { - let ops = state.parse_line(line, syntax_set); + let ops = state.parse_line(line, syntax_set).expect("#[cfg(test)]"); debug_print_ops(line, &ops); ops } diff --git a/src/parsing/syntax_definition.rs b/src/parsing/syntax_definition.rs index 7690dc76..ed32493b 100644 --- a/src/parsing/syntax_definition.rs +++ b/src/parsing/syntax_definition.rs @@ -163,7 +163,7 @@ impl<'a> Iterator for MatchIter<'a> { Pattern::Include(ref ctx_ref) => { let ctx_ptr = match *ctx_ref { ContextReference::Direct(ref context_id) => { - self.syntax_set.get_context(context_id) + self.syntax_set.get_context(context_id).unwrap() } _ => return self.next(), // skip this and move onto the next one }; @@ -205,7 +205,7 @@ impl ContextReference { /// find the pointed to context, panics if ref is not linked pub fn resolve<'a>(&self, syntax_set: &'a SyntaxSet) -> &'a Context { match *self { - ContextReference::Direct(ref context_id) => syntax_set.get_context(context_id), + ContextReference::Direct(ref context_id) => syntax_set.get_context(context_id).unwrap(), _ => panic!("Can only call resolve on linked references: {:?}", self), } } diff --git a/src/parsing/syntax_set.rs b/src/parsing/syntax_set.rs index 8cab17df..7f45485f 100644 --- a/src/parsing/syntax_set.rs +++ b/src/parsing/syntax_set.rs @@ -1,3 +1,4 @@ +use super::ParsingError; use super::syntax_definition::*; use super::scope::*; @@ -351,9 +352,15 @@ impl SyntaxSet { } #[inline(always)] - pub(crate) fn get_context(&self, context_id: &ContextId) -> &Context { - let syntax = &self.syntaxes[context_id.syntax_index]; - &syntax.contexts()[context_id.context_index] + pub(crate) fn get_context(&self, context_id: &ContextId) -> Result<&Context, ParsingError> { + let syntax = &self + .syntaxes + .get(context_id.syntax_index) + .ok_or_else(|| ParsingError::MissingContext(*context_id))?; + syntax + .contexts() + .get(context_id.context_index) + .ok_or_else(|| ParsingError::MissingContext(*context_id)) } fn first_line_cache(&self) -> &FirstLineCache { @@ -909,7 +916,7 @@ mod tests { // println!("{:#?}", syntax); assert_eq!(syntax.scope, rails_scope); // unreachable!(); - let main_context = ps.get_context(&syntax.context_ids()["main"]); + let main_context = ps.get_context(&syntax.context_ids()["main"]).expect("#[cfg(test)]"); let count = syntax_definition::context_iter(&ps, main_context).count(); assert_eq!(count, 109); } @@ -929,7 +936,7 @@ mod tests { let syntax = cloned_syntax_set.find_syntax_by_extension("a").unwrap(); let mut parse_state = ParseState::new(syntax); - let ops = parse_state.parse_line("a go_b b", &cloned_syntax_set); + let ops = parse_state.parse_line("a go_b b", &cloned_syntax_set).expect("#[cfg(test)]"); let expected = (7, ScopeStackOp::Push(Scope::new("b").unwrap())); assert_ops_contain(&ops, &expected); } @@ -975,7 +982,7 @@ mod tests { let syntax = syntax_set.find_syntax_by_extension("c").unwrap(); let mut parse_state = ParseState::new(syntax); - let ops = parse_state.parse_line("c go_a a go_b b", &syntax_set); + let ops = parse_state.parse_line("c go_a a go_b b", &syntax_set).expect("#[cfg(test)]"); let expected = (14, ScopeStackOp::Push(Scope::new("b").unwrap())); assert_ops_contain(&ops, &expected); } @@ -1023,7 +1030,7 @@ mod tests { let syntax = syntax_set.find_syntax_by_extension("z").unwrap(); let mut parse_state = ParseState::new(syntax); - let ops = parse_state.parse_line("z go_x x leave_x z", &syntax_set); + let ops = parse_state.parse_line("z go_x x leave_x z", &syntax_set).unwrap(); let expected_ops = vec![ (0, ScopeStackOp::Push(Scope::new("source.z").unwrap())), (0, ScopeStackOp::Push(Scope::new("z").unwrap())), @@ -1082,7 +1089,7 @@ mod tests { .map(|line| { let syntax = syntax_set.find_syntax_by_extension("a").unwrap(); let mut parse_state = ParseState::new(syntax); - parse_state.parse_line(line, &syntax_set) + parse_state.parse_line(line, &syntax_set).expect("#[cfg(test)]") }) .collect(); @@ -1150,7 +1157,7 @@ mod tests { 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 ops = parse_state.parse_line("c go_a a", &syntax_set).expect("msg"); let expected = (7, ScopeStackOp::Push(Scope::new("a2").unwrap())); assert_ops_contain(&ops, &expected); } @@ -1165,7 +1172,7 @@ mod tests { let syntax = syntax_set.find_syntax_by_extension("yaml").unwrap(); let mut parse_state = ParseState::new(syntax); - let ops = parse_state.parse_line("# test\n", &syntax_set); + let ops = parse_state.parse_line("# test\n", &syntax_set).expect("#[cfg(test)]"); let expected = (0, ScopeStackOp::Push(Scope::new("comment.line.number-sign.yaml").unwrap())); assert_ops_contain(&ops, &expected); } @@ -1242,7 +1249,7 @@ mod tests { // Skip special contexts continue; } - let context = syntax_set.get_context(id); + let context = syntax_set.get_context(id).expect("#[cfg(test)]"); if expected.contains(&name.as_str()) { assert!(context.prototype.is_some(), "Expected context {} to have prototype", name); } else { diff --git a/src/util.rs b/src/util.rs index 8a8c3e1a..9f82268e 100644 --- a/src/util.rs +++ b/src/util.rs @@ -79,7 +79,7 @@ const LATEX_REPLACE: [(&str, &str); 3] = [ /// /// let mut h = HighlightLines::new(syntax, &ts.themes["InspiredGitHub"]); /// for line in LinesWithEndings::from(s) { // LinesWithEndings enables use of newlines mode -/// let ranges: Vec<(Style, &str)> = h.highlight_line(line, &ps); +/// let ranges: Vec<(Style, &str)> = h.highlight_line(line, &ps).unwrap(); /// let escaped = as_latex_escaped(&ranges[..]); /// println!("{}", escaped); /// }