diff --git a/.travis.yml b/.travis.yml index 3da67a5..bb5d6c4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,18 +1,19 @@ os: - - linux - - osx + - linux + - osx +env: + - RUST_TEST_THREADS=1 language: rust rust: -- stable -- beta -- nightly + - stable + - beta + - nightly script: - cargo build --verbose --release - cargo build --verbose --release --features all - cargo test --verbose --release - cargo test --verbose --release --features all matrix: - allow_failure: - - rust: nightly + allow_failure: + - rust: nightly sudo: false - diff --git a/Cargo.toml b/Cargo.toml index d952e3a..a5bc53c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT/Apache-2.0" name = "tokei" readme = "README.md" repository = "https://github.com/Aaronepower/tokei.git" -version = "4.1.0" +version = "4.2.0" [[bin]] doc = false diff --git a/cli.yml b/cli.yml index a530b7e..3b19a22 100644 --- a/cli.yml +++ b/cli.yml @@ -5,7 +5,7 @@ about: Count Code, Quickly. author: Aaron P. bin_name: Tokei name: Tokei -version: 4.1.0 +version: 4.2.0 args: - exclude: help: Ignore all files & directories containing the word. diff --git a/src/lib/language/language.rs b/src/lib/language/language.rs index 2de04d1..7b33139 100644 --- a/src/lib/language/language.rs +++ b/src/lib/language/language.rs @@ -31,6 +31,12 @@ pub struct Language { /// Whether the language supports nested multi line comments or not. #[serde(skip_deserializing, skip_serializing)] pub nested: bool, + /// A list of specific nested comments if this is empty all `multi_line` comments count. + #[serde(skip_deserializing, skip_serializing)] + pub nested_comments: Vec<(&'static str, &'static str)>, + /// A list of quotes by default it is `""`. + #[serde(skip_deserializing, skip_serializing)] + pub quotes: Vec<(&'static str, &'static str)>, } #[cfg(not(feature = "io"))] @@ -54,6 +60,10 @@ pub struct Language { pub multi_line: Vec<(&'static str, &'static str)>, /// Whether the language supports nested multi line comments or not. pub nested: bool, + /// A list of specific nested comments if this is empty all `multi_line` comments count. + pub nested_comments: Vec<(&'static str, &'static str)>, + /// A list of quotes by default it is `""`. + pub quotes: Vec<(&'static str, &'static str)>, } @@ -101,6 +111,7 @@ impl Language { Language { line_comment: vec!["//"], multi_line: vec![("/*", "*/")], + quotes: vec![("\"", "\"")], ..Self::default() } } @@ -116,7 +127,11 @@ impl Language { /// assert_eq!(ocaml.multi_line, coq.multi_line); /// ``` pub fn new_func() -> Self { - Language { multi_line: vec![("(*", "*)")], ..Self::default() } + Language { + multi_line: vec![("(*", "*)")], + quotes: vec![("\"", "\"")], + ..Self::default() + } } /// Convience constructor for creating a language that has the same commenting syntax as HTML like languages. @@ -130,7 +145,11 @@ impl Language { /// assert_eq!(xml.multi_line, html.multi_line); /// ``` pub fn new_html() -> Self { - Language { multi_line: vec![("")], ..Self::default() } + Language { + multi_line: vec![("")], + quotes: vec![("\"", "\"")], + ..Self::default() + } } /// Convience constructor for creating a language that has the same commenting syntax as Bash. @@ -154,7 +173,11 @@ impl Language { /// let mustache = Language::new_multi(vec![("{{!", "}}")]); /// ``` pub fn new_multi(multi_line: Vec<(&'static str, &'static str)>) -> Self { - Language { multi_line: multi_line, ..Self::default() } + Language { + multi_line: multi_line, + quotes: vec![("\"", "\"")], + ..Self::default() + } } /// Convience constructor for creating a language that has the same commenting syntax as Prolog. @@ -171,6 +194,7 @@ impl Language { Language { line_comment: vec!["%"], multi_line: vec![("/*", "*/")], + quotes: vec![("\"", "\"")], ..Self::default() } } @@ -182,7 +206,11 @@ impl Language { /// let haskell = Language::new_single(vec!["--"]); /// ``` pub fn new_single(line_comment: Vec<&'static str>) -> Self { - Language { line_comment: line_comment, ..Self::default() } + Language { + line_comment: line_comment, + quotes: vec![("\"", "\"")], + ..Self::default() + } } /// Checks if the language is empty. Empty meaning it doesn't have any statistics. @@ -221,7 +249,40 @@ impl Language { self } - /// Sorts each of the `Stats` structs contained in the language based on what category is provided + /// Specify if the the language supports nested multi line comments. + /// And which are nested. If this is specified there is no need to + /// call the `nested` function. + /// + /// ``` + /// # use tokei::*; + /// let mut d = Language::new(vec!["//"], vec![("/*", "*/")]) + /// .nested_comments(vec![("/+", "+/")]); + /// assert!(d.nested); + /// assert_eq!(d.nested_comments, vec![("/+", "+/")]); + /// ``` + pub fn nested_comments(mut self, nested_comments: Vec<(&'static str, &'static str)>) -> Self { + self.nested = true; + self.nested_comments = nested_comments; + self + } + + /// Specifies if the language has a quotes to define a string where + /// the commenting syntax would be ignored. By default it is only + /// `""` quotes that are ignored. + /// + /// ``` + /// # use tokei::*; + /// let mut javascript = Language::new(vec!["//"], vec![("/*", "*/")]) + /// .set_quotes(vec![("\"", "\""), ("'", "'")]); + /// assert!(!javascript.quotes.is_empty()); + /// ``` + pub fn set_quotes(mut self, quotes: Vec<(&'static str, &'static str)>) -> Self { + self.quotes = quotes; + self + } + + /// Sorts each of the `Stats` structs contained in the language based + /// on what category is provided /// panic!'s if given the wrong category. /// /// ``` diff --git a/src/lib/language/language_type.rs b/src/lib/language/language_type.rs index 4eead9b..03a4c19 100644 --- a/src/lib/language/language_type.rs +++ b/src/lib/language/language_type.rs @@ -7,7 +7,7 @@ use std::borrow::Cow; use std::fmt; use std::path::Path; -use utils::*; +use utils::fs; use self::LanguageType::*; #[cfg_attr(feature = "io", derive(Deserialize, Serialize))] @@ -269,7 +269,7 @@ impl LanguageType { /// assert_eq!(rust, Some(LanguageType::Rust)); /// ``` pub fn from_extension>(entry: P) -> Option { - if let Some(extension) = get_extension(entry) { + if let Some(extension) = fs::get_extension(entry) { match &*extension { "as" => Some(ActionScript), "ada" | "adb" | "ads" => Some(Ada), diff --git a/src/lib/language/languages.rs b/src/lib/language/languages.rs index bc41c30..9bc1a16 100644 --- a/src/lib/language/languages.rs +++ b/src/lib/language/languages.rs @@ -19,7 +19,7 @@ use serde_yaml; use toml; use rayon::prelude::*; -use utils::*; +use utils::{fs, multi_line}; use super::{Language, LanguageType}; use super::LanguageType::*; use stats::Stats; @@ -31,6 +31,87 @@ const TOML_ERROR: &'static str = "Tokei was not compiled with the `toml-io` flag #[cfg(not(feature = "yaml"))] const YAML_ERROR: &'static str = "Tokei was not compiled with the `yaml` flag."; +fn count_files(language_tuple: &mut (&LanguageType, &mut Language)) { + + let &mut (name, ref mut language) = language_tuple; + + if language.files.is_empty() { + return; + } + + let is_fortran = name == &FortranModern || name == &FortranLegacy; + + let files: Vec<_> = language.files.drain(..).collect(); + let mut contents = String::new(); + let mut stack = vec![]; + let mut quote = None; + + for file in files { + let mut stats = Stats::new(opt_or_cont!(file.to_str())); + stack.clear(); + contents.clear(); + + rs_or_cont!(rs_or_cont!(File::open(file)).read_to_string(&mut contents)); + + let lines = contents.lines(); + + if language.is_blank() { + stats.code += lines.count(); + continue; + } + + + 'line: for line in lines { + stats.lines += 1; + // FORTRAN has a rule where it only counts as a comment if it's the first + // character in the column, so removing starting whitespace could cause a + // miscount. + let line = if is_fortran { + line + } else { + line.trim() + }; + + if line.trim().is_empty() { + stats.blanks += 1; + continue; + } + + for single in &language.line_comment { + if line.starts_with(single) { + stats.comments += 1; + continue 'line; + } + } + + let mut started_with = false; + if quote.is_none() { + let chain = language.multi_line.iter().chain(language.nested_comments.iter()); + + for &(start, _) in chain { + if line.starts_with(start) { + started_with = true; + } + } + } + + multi_line::handle_multi_line(line, &language, &mut stack, &mut quote); + + if !stack.is_empty() { + if started_with { + stats.code += 1; + } else { + stats.comments += 1; + } + } else { + stats.code += 1; + } + } + + **language += stats; + } +} + /// A collection of existing languages([_List of Languages_](https://github.com/Aaronepower/tokei#supported-languages)) #[derive(Debug, Clone)] pub struct Languages { @@ -168,97 +249,11 @@ impl Languages { where I: Into> { - get_all_files(paths.into(), ignored.into(), &mut self.inner); + fs::get_all_files(paths.into(), ignored.into(), &mut self.inner); let mut language_iter: Vec<_> = self.inner.iter_mut().collect(); - language_iter.par_iter_mut().for_each(|&mut (name, ref mut language)| { - if language.files.is_empty() { - return; - } - - let is_fortran = name == &FortranModern || name == &FortranLegacy; - - let files: Vec<_> = language.files.drain(..).collect(); - let mut contents = String::new(); - - for file in files { - let mut is_in_comments = false; - let mut previous_comment_start = ""; - let mut comment_depth: usize = 0; - let mut stats = Stats::new(opt_or_cont!(file.to_str())); - - let contents = { - contents.clear(); - let _ = rs_or_cont!(rs_or_cont!(File::open(file)) - .read_to_string(&mut contents)); - contents - }; - - let lines = contents.lines(); - - if language.is_blank() { - stats.code += lines.count(); - continue; - } - - 'line: for line in lines { - stats.lines += 1; - - // FORTRAN has a rule where it only counts as a comment if it's the first - // character in the column, so removing starting whitespace could cause a - // miscount. - let line = if is_fortran { - line - } else { - line.trim() - }; - - if line.trim().is_empty() { - stats.blanks += 1; - continue; - } - - if line.starts_with(multi_line) { - if let Some(multi_line) = has_trailing_comments(line, &language) { - previous_comment_start = multi_line; - is_in_comments = true; - if language.nested { - comment_depth += 1; - } - } - } - if is_in_comments { - for &(multi_line, multi_line_end) in &language.multi_line { - if multi_line == previous_comment_start { - if let Some(pos) = line.find(multi_line_end) { - if language.nested { - comment_depth -= 1; - if comment_depth == 0 { - is_in_comments = false; - } - } else { - is_in_comments = false; - } - } - } - } - stats.comments += 1; - continue; - } - - for single in &language.line_comment { - if line.starts_with(single) { - stats.comments += 1; - continue 'line; - } - } - stats.code += 1; - } - - **language += stats; - } - }); + language_iter.par_iter_mut().for_each(count_files); } /// Constructs a new, blank `Languages`. @@ -279,7 +274,8 @@ impl Languages { C => Language::new_c(), CHeader => Language::new_c(), Clojure => Language::new_single(vec![";","#"]), - CoffeeScript => Language::new(vec!["#"], vec![("###", "###")]), + CoffeeScript => Language::new(vec!["#"], vec![("###", "###")]) + .set_quotes(vec![("\"", "\""), ("'", "'")]), ColdFusion => Language::new_multi(vec![("")]), ColdFusionScript => Language::new_c(), Coq => Language::new_func(), @@ -287,8 +283,9 @@ impl Languages { CppHeader => Language::new_c(), CSharp => Language::new_c(), CShell => Language::new_hash(), - Css => Language::new_c(), - D => Language::new(vec!["//"], vec![("/*", "*/"), ("/+", "+/")]).nested(), + Css => Language::new_c() + .set_quotes(vec![("\"", "\""), ("'", "'")]), + D => Language::new(vec!["//"], vec![("/*", "*/")]).nested_comments(vec![("/+", "+/")]), Dart => Language::new_c(), DeviceTree => Language::new_c(), Erlang => Language::new_single(vec!["%"]), @@ -296,9 +293,11 @@ impl Languages { FortranLegacy => Language::new_single(vec!["c","C","!","*"]), FortranModern => Language::new_single(vec!["!"]), Go => Language::new_c(), - Handlebars => Language::new_multi(vec![(""), ("{{!", "}}")]), + Handlebars => Language::new_multi(vec![(""), ("{{!", "}}")]) + .set_quotes(vec![("\"", "\""), ("'", "'")]), Haskell => Language::new_single(vec!["--"]), - Html => Language::new_html(), + Html => Language::new_html() + .set_quotes(vec![("\"", "\""), ("'", "'")]), Idris => Language::new(vec!["--"], vec![("{-", "-}")]), Isabelle => Language::new( vec!["--"], @@ -310,9 +309,11 @@ impl Languages { ), Jai => Language::new_c(), Java => Language::new_c(), - JavaScript => Language::new_c(), + JavaScript => Language::new_c() + .set_quotes(vec![("\"", "\""), ("'", "'")]), Json => Language::new_blank(), - Jsx => Language::new_c(), + Jsx => Language::new_c() + .set_quotes(vec![("\"", "\""), ("'", "'")]), Julia => Language::new(vec!["#"], vec![("#=", "=#")]), Kotlin => Language::new_c(), Less => Language::new_c(), @@ -580,12 +581,14 @@ impl DerefMut for Languages { mod accuracy_tests { use super::*; use std::io; + use language::LanguageType; - fn write(contents: &'static str, extension: &'static str) -> io::Result<()> { + fn write(contents: &'static str, file_name: &str) -> io::Result<()> { use std::io::prelude::*; - use std::fs::File; + use std::fs::{File, create_dir}; - let mut f = try!(File::create(format!("./_temp/_temp_file.{}", extension))); + let _ = create_dir("./_temp/"); + let mut f = try!(File::create(file_name)); try!(f.write_all(&contents.as_bytes())); Ok(()) @@ -596,93 +599,84 @@ mod accuracy_tests { Ok(()) } - fn test_accuracy(ext: &'static str, num: u32, contents: &'static str) { - write(contents, ext).unwrap(); + fn test_accuracy(file_name: &'static str, expected: u32, contents: &'static str) { + let file_name = format!("./_temp/{}", file_name); + write(contents, &*file_name).unwrap(); let mut l = Languages::new(); + let l_type = LanguageType::from_extension(file_name).expect("Can't find language type"); - l.get_statistics(vec!["./temp/"], vec![]); + l.get_statistics(vec!["./_temp/"], vec![]); - assert_eq!(num as usize, - l.get_mut(&::language::LanguageType::from(ext)).unwrap().code); let _ = cleanup(); + let language = l.get_mut(&l_type).expect("Couldn't find language"); + assert_eq!(expected as usize, language.code); } #[test] fn inside_quotes() { - test_accuracy("rs", + test_accuracy("inside_quotes.rs", 8, - "fn main() { - let start = \"/*\"; + r#"fn main() { + let start = "/*"; loop { - \ - if x.len() >= 2 && x[0] == '*' && x[1] == '/' { // found the */ - \ - break; + if x.len() >= 2 && x[0] == '*' && x[1] == '/' { // found the */ + break; } } - } - ") + }"#) } #[test] fn shouldnt_panic() { - test_accuracy("rs", + test_accuracy("shouldnt_panic.rs", 9, - "fn foo() { - let this_ends = \"a \\\"test/*.\"; - \ - call1(); + r#"fn foo() { + let this_ends = "a \"test/*."; + call1(); call2(); - let this_does_not = /* a /* \ - nested */ comment \" */ - \"*/another /*test - \ - call3(); - */\"; - }") + let this_does_not = /* a /* nested */ comment " */ + "*/another /*test + call3(); + */"; + }"#) } #[test] fn all_quotes_no_comment() { - test_accuracy("rs", + test_accuracy("all_quotes_no_comment.rs", 10, - "fn foobar() { - let does_not_start = // \" - \"until here, - \ - test/* - test\"; // a quote: \" - let also_doesnt_start = \ - /* \" */ - \"until here, - test,*/ - test\"; \ - // another quote: \" - }") + r#"fn foobar() { + let does_not_start = // " + "until here, + test/* + test"; // a quote: " + let also_doesnt_start = /* " */ + "until here, + test,*/ + test"; // another quote: " +}"#) } #[test] fn commenting_on_comments() { - test_accuracy("rs", + test_accuracy("commenting_on_comments.rs", 5, - "fn foo() { - let a = 4; // /* - let b = 5; - \ - let c = 6; // */ - }") + r#"fn foo() { + let a = 4; // /* + let b = 5; + let c = 6; // */ +}"#) } #[test] - fn deez_nesting_comments() { - test_accuracy("d", + fn nesting_with_nesting_comments() { + test_accuracy("nesting_with_nesting_comments.d", 5, - "void main() { - auto x = 5; /+ a /+ nested +/ comment /* +/ - \ - writefln(\"hello\"); - auto y = 4; // */ - }") + r#"void main() { + auto x = 5; /+ a /+ nested +/ comment /* +/ + writefln("hello"); + auto y = 4; // */ +}"#) } } diff --git a/src/lib/utils/fs.rs b/src/lib/utils/fs.rs index a5b7837..56b2182 100644 --- a/src/lib/utils/fs.rs +++ b/src/lib/utils/fs.rs @@ -14,78 +14,6 @@ use walkdir::{WalkDir, WalkDirIterator}; use language::{Language, LanguageType}; use language::LanguageType::*; -pub fn handle_multi_line() -> usize { - unreachable!() -} - - -/// This is used to catch lines like "let x = 5; /* Comment */" -pub fn has_trailing_comments(line: &str, language: &Language) -> bool { - let mut is_in_comments = 0u64; - - line = if !language.single.is_empty() { - let found = None; - for single in &language.line_comment { - if let Some(pos) = line.find(single) { - found = Some(pos); - break; - } - } - - if let Some(pos) = found { - &line[0..pos] - } else { - line - } - } else { - line - }; - - let mut unfinished_comments = vec![]; - for &(comment, comment_end) in &language { - let start = match line.find(comment) { - Some(start) => start, - None => continue, - }; - - // This should short circuit 99% of languages. - if !language.nested && language.multi_line.len() == 1 { - if let Some(end) = line.rfind(comment_end) { - if let Some(end_check) = line.rfind(comment) { - if end_check > end { - return true; - } else { - return false; - } - } - } else { - return true; - } - } - - let mut chars = line[start..end + comment_end.len()].chars(); - loop { - let window = chars.as_str(); - - if window.starts_with(comment) { - if nested { - is_in_comments += 1; - } else { - is_in_comments = 1; - } - } else if window.starts_with(comment_end) { - is_in_comments = is_in_comments.saturating_sub(1); - } - - if chars.next().is_none() { - break; - } - } - } - - is_in_comments != 0 -} - pub fn get_all_files<'a>(paths: Cow<'a, [&'a str]>, ignored_directories: Cow<'a, [&'a str]>, languages: &mut BTreeMap) { @@ -186,42 +114,3 @@ pub fn get_filetype_from_shebang>(file: P) -> Option<&'static str _ => None, } } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn both_comments_in_line() { - assert!(!has_trailing_comments("Hello /* /* */ World", "//", ("/*", "*/"), false)); - } - - #[test] - fn comment_hidden_in_single() { - assert!(has_trailing_comments("Hello /* World // */", "//", ("/*", "*/"), true)) - } - - #[test] - fn comment_start_in_line() { - assert!(has_trailing_comments("Hello /* World", "//", ("/*", "*/"), false)); - } - - #[test] - fn both_comments_in_line_nested() { - assert!(has_trailing_comments("Hello (* (* *) World", "--", ("(*", "*)"), true)); - } - - - #[test] - fn comments_of_uneven_length() { - assert!(has_trailing_comments("Hello \\ \\ \\ World", - "", - ("\\", "\\"), - true)); - } - - #[test] - fn comment_start_in_line_nested() { - assert!(has_trailing_comments("Hello (* World", "", ("(*", "*)"), true)); - } -} diff --git a/src/lib/utils/mod.rs b/src/lib/utils/mod.rs index a9a3fd4..bbb142c 100644 --- a/src/lib/utils/mod.rs +++ b/src/lib/utils/mod.rs @@ -2,6 +2,3 @@ mod macros; pub mod fs; pub mod multi_line; - -pub use self::fs::*; -pub use self::multi_line::*; diff --git a/src/lib/utils/multi_line.rs b/src/lib/utils/multi_line.rs index 413cb6d..1c3c300 100644 --- a/src/lib/utils/multi_line.rs +++ b/src/lib/utils/multi_line.rs @@ -1,40 +1,20 @@ -use std::cmp; +use language::Language; /// This is used to catch lines like "let x = 5; /* Comment */" -pub fn has_trailing_comments(line: &str, language: &Language) -> Vec<&'static str> { - let line = slice_to_single(line, language); - let mut is_in_comments = 0u64; - let mut start = None; - let mut stack = vec![]; - - for &(comment, comment_end) in &language.multi_line { - start = line.find(comment).and_then(|x| cmp::min(x, start.unwrap_or(x))); - - // This should short circuit 99% of languages. - if start.is_none() && !language.nested && language.multi_line.len() == 1 { - if let Some(end) = line.rfind(comment_end) { - if let Some(end_check) = line.rfind(comment) { - if end_check > end { - return true; - } else { - return false; - } - } - } else { - return true; - } - } - } - - let start = match start { - Some(pos) => pos, - None => return stack, - }; - - let mut chars = line[start..].chars(); +pub fn handle_multi_line(line: &str, + language: &Language, + stack: &mut Vec<&'static str>, + quote: &mut Option<&'static str>) { + let mut chars = line.chars(); let mut cont = false; - loop { + let nested_is_empty = language.nested_comments.is_empty(); + + 'window: loop { let window = chars.as_str(); + if window.is_empty() { + break; + } + chars.next(); // Prevents counting overlaps like /*/* if cont { @@ -42,90 +22,135 @@ pub fn has_trailing_comments(line: &str, language: &Language) -> Vec<&'static st continue; } + let mut end = false; + + if let &mut Some(quote_str) = quote { + if window.starts_with("\\") { + cont = true; + continue; + } else if window.starts_with(quote_str) { + end = true; + } + } + + if end { + if let &mut Some(quote_str) = quote { + *quote = None; + + if quote_str.chars().count() == 1 { + cont = true + } + continue; + } + } + + if quote.is_some() { + continue; + } + + let mut pop = false; if let Some(last) = stack.last() { if window.starts_with(last) { - stack.pop(); - cont = true; - continue; + pop = true; } } - for &(comment, comment_end) in &language.multi_line { + if pop { + stack.pop(); + cont = true; + continue; + } + + + if stack.is_empty() { + for &(start, end) in &language.quotes { + if window.starts_with(start) { + *quote = Some(end); + cont = true; + continue 'window; + } + } + } + + + for comment in &language.line_comment { if window.starts_with(comment) { - if nested { - stack.push(comment_end); + break 'window; + } + } + + for &(start, end) in &language.nested_comments { + if window.starts_with(start) { + stack.push(end); + cont = true; + continue 'window; + } + } + + for &(start, end) in &language.multi_line { + if window.starts_with(start) { + if language.nested && nested_is_empty { + stack.push(end); } else if stack.len() == 0 { - stack.push(comment_end); + stack.push(end); } cont = true; - continue; + continue 'window; } } - - if chars.next().is_none() { - break; - } - } - - stack -} - -#[inline] -fn slice_to_single(line: &str, language: &language) -> &str { - if !language.single.is_empty() { - let found = None; - for single in &language.line_comment { - if let Some(pos) = line.find(single) { - found = Some(pos); - break; - } - } - - if let Some(pos) = found { - &line[0..pos] - } else { - line - } - } else { - line } } #[cfg(test)] mod tests { use super::*; + use language::Language; #[test] fn both_comments_in_line() { - assert!(!has_trailing_comments("Hello /* /* */ World", "//", ("/*", "*/"), false)); + let mut stack = vec![]; + let mut quote = None; + let language = Language::new_c(); + handle_multi_line("Hello /* /* */ World", &language, &mut stack, &mut quote); + assert_eq!(stack.len(), 0); } #[test] fn comment_hidden_in_single() { - assert!(has_trailing_comments("Hello /* World // */", "//", ("/*", "*/"), true)) + let mut stack = vec![]; + let mut quote = None; + let language = Language::new_c(); + handle_multi_line("Hello World // /*", &language, &mut stack, &mut quote); + assert_eq!(stack.len(), 0); } #[test] - fn comment_start_in_line() { - assert!(has_trailing_comments("Hello /* World", "//", ("/*", "*/"), false)); + fn comment_start() { + let mut stack = vec![]; + let mut quote = None; + let language = Language::new_c(); + handle_multi_line("/*Hello World", &language, &mut stack, &mut quote); + assert_eq!(stack.len(), 1); } #[test] fn both_comments_in_line_nested() { - assert!(has_trailing_comments("Hello (* (* *) World", "--", ("(*", "*)"), true)); + let mut stack = vec![]; + let mut quote = None; + let language = Language::new_func().nested(); + handle_multi_line("Hello (* (* *) World", &language, &mut stack, &mut quote); + assert_eq!(stack.len(), 1); } - #[test] fn comments_of_uneven_length() { - assert!(has_trailing_comments("Hello \\ \\ \\ World", - "", - ("\\", "\\"), - true)); - } - - #[test] - fn comment_start_in_line_nested() { - assert!(has_trailing_comments("Hello (* World", "", ("(*", "*)"), true)); + let mut stack = vec![]; + let mut quote = None; + let language = Language::new(vec![], vec![("\\", "\\")]).nested(); + handle_multi_line("Hello \\ \\ \\ World", + &language, + &mut stack, + &mut quote); + assert_eq!(stack.len(), 1); } }