diff --git a/src/main.rs b/src/main.rs index 104713b6a..c41c3726b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,7 +12,7 @@ use crate::lines::{ apply_groups, enforce_length, format_line_num, join_overlapping, lhs_printable_width, rhs_printable_width, visible_groups, MaxLine, }; -use crate::parse::{find_lang, parse, read_or_die, ConfigDir}; +use crate::parse::{find_lang, parse, parse_lines, read_or_die, ConfigDir}; use crate::style::apply_colors; use crate::tree_diff::{change_positions, set_changed}; @@ -65,8 +65,7 @@ fn main() { .and_then(OsStr::to_str) .unwrap(); - let lang = find_lang(syntax_toml, lhs_extension) - .expect("todo: handle unsupported languages gracefully"); + let lang = find_lang(syntax_toml, lhs_extension); let terminal_width = match matches.value_of("COLUMNS") { Some(width) => width.parse::().unwrap(), @@ -74,8 +73,11 @@ fn main() { }; let arena = Arena::new(); - let lhs = parse(&arena, &lhs_src, &lang); - let rhs = parse(&arena, &rhs_src, &lang); + + let (lhs, rhs) = match lang { + Some(lang) => (parse(&arena, &lhs_src, &lang), parse(&arena, &rhs_src, &lang)), + None => (parse_lines(&arena, &lhs_src), parse_lines(&arena, &rhs_src)) + }; set_changed(&lhs, &rhs); diff --git a/src/parse.rs b/src/parse.rs index 105fc65ba..ef07eb8c2 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -110,6 +110,54 @@ fn lang_from_value(v: &Value) -> Language { } } +/// Split `s` by lines, and treat each line as an atom. +/// +/// This is a fallback for files that we don't know how to parse. +pub fn parse_lines<'a>(arena: &'a Arena>, s: &str) -> Vec<&'a Node<'a>> { + let mut line_start = 0; + let mut res: Vec<&'a Node<'a>> = vec![]; + + for (i, c) in s.chars().enumerate() { + if c == '\n' { + let line = &s[line_start..i]; + let atom = Node::new_atom( + arena, + vec![SingleLineSpan { + line: res.len().into(), + start_col: 0, + end_col: i - line_start, + }], + line, + AtomKind::Other, + ); + res.push(atom); + + line_start = i + 1; + } + } + + if let Some(last) = s.chars().last() { + if last != '\n' { + let line = &s[line_start..]; + + let atom = Node::new_atom( + arena, + vec![SingleLineSpan { + line: res.len().into(), + start_col: 0, + end_col: s.len() - line_start, + }], + line, + AtomKind::Other, + ); + res.push(atom); + } + } + + res +} + +/// Parse `s` according to `lang`. pub fn parse<'a>(arena: &'a Arena>, s: &str, lang: &Language) -> Vec<&'a Node<'a>> { let nl_pos = NewlinePositions::from(s); parse_from(arena, s, &nl_pos, lang, &mut ParseState::new()) @@ -353,6 +401,37 @@ mod tests { true } + #[test] + fn test_parse_lines() { + let arena = Arena::new(); + + assert_syntaxes( + &parse_lines(&arena, "foo\nbar"), + &[ + Node::new_atom( + &arena, + vec![SingleLineSpan { + line: 0.into(), + start_col: 0, + end_col: 3, + }], + "foo", + AtomKind::Other, + ), + Node::new_atom( + &arena, + vec![SingleLineSpan { + line: 1.into(), + start_col: 0, + end_col: 3, + }], + "bar", + AtomKind::Other, + ), + ], + ); + } + #[test] fn test_parse_integer() { let arena = Arena::new();