diff --git a/mdsf/src/filetype/generated_file_types.rs b/mdsf/src/filetype/generated_file_types.rs index a864c41cf..b40ae2ef6 100644 --- a/mdsf/src/filetype/generated_file_types.rs +++ b/mdsf/src/filetype/generated_file_types.rs @@ -336,6 +336,7 @@ pub fn language_to_ext(language: &str) -> Option<&'static str> { "fortran free form" => Some(".f90"), "fortran" => Some(".f"), "foxpro" => Some(".prg"), + "fpp" => Some(".fpp"), "freebasic" => Some(".bi"), "freemarker" => Some(".ftl"), "frege" => Some(".fr"), @@ -851,6 +852,7 @@ pub fn language_to_ext(language: &str) -> Option<&'static str> { "qsharp" => Some(".qs"), "qt script" => Some(".qs"), "quakec" => Some(".qc"), + "quartus simulation ip" => Some(".sip"), "quickbasic" => Some(".bas"), "quint" => Some(".qnt"), "r" => Some(".r"), @@ -888,6 +890,7 @@ pub fn language_to_ext(language: &str) -> Option<&'static str> { "restructuredtext" => Some(".rst"), "rexx" => Some(".rexx"), "rez" => Some(".r"), + "rhai" => Some(".rhai"), "rhtml" => Some(".erb"), "rich text format" => Some(".rtf"), "ring" => Some(".ring"), @@ -946,6 +949,7 @@ pub fn language_to_ext(language: &str) -> Option<&'static str> { "shen" => Some(".shen"), "sieve" => Some(".sieve"), "simple file verification" => Some(".sfv"), + "sip" => Some(".sip"), "slang" => Some(".slang"), "slash" => Some(".sl"), "slice" => Some(".ice"), @@ -1085,6 +1089,8 @@ pub fn language_to_ext(language: &str) -> Option<&'static str> { "vento" => Some(".vto"), "verilog" => Some(".v"), "verse" => Some(".verse"), + "vespa schema definition" => Some(".sd"), + "vespa" => Some(".sd"), "vhdl" => Some(".vhdl"), "vhs" => Some(".tape"), "vim help file" => Some(".txt"), @@ -1520,6 +1526,7 @@ mod test_language_to_ext { ("fortran free form", ".f90"), ("fortran", ".f"), ("foxpro", ".prg"), + ("fpp", ".fpp"), ("freebasic", ".bi"), ("freemarker", ".ftl"), ("frege", ".fr"), @@ -2035,6 +2042,7 @@ mod test_language_to_ext { ("qsharp", ".qs"), ("qt script", ".qs"), ("quakec", ".qc"), + ("quartus simulation ip", ".sip"), ("quickbasic", ".bas"), ("quint", ".qnt"), ("r", ".r"), @@ -2072,6 +2080,7 @@ mod test_language_to_ext { ("restructuredtext", ".rst"), ("rexx", ".rexx"), ("rez", ".r"), + ("rhai", ".rhai"), ("rhtml", ".erb"), ("rich text format", ".rtf"), ("ring", ".ring"), @@ -2130,6 +2139,7 @@ mod test_language_to_ext { ("shen", ".shen"), ("sieve", ".sieve"), ("simple file verification", ".sfv"), + ("sip", ".sip"), ("slang", ".slang"), ("slash", ".sl"), ("slice", ".ice"), @@ -2269,6 +2279,8 @@ mod test_language_to_ext { ("vento", ".vto"), ("verilog", ".v"), ("verse", ".verse"), + ("vespa schema definition", ".sd"), + ("vespa", ".sd"), ("vhdl", ".vhdl"), ("vhs", ".tape"), ("vim help file", ".txt"), diff --git a/mdsf/src/parser/mod.rs b/mdsf/src/parser/mod.rs index 41fcc286c..16ce0c36d 100644 --- a/mdsf/src/parser/mod.rs +++ b/mdsf/src/parser/mod.rs @@ -32,17 +32,39 @@ pub fn parse_generic_codeblock(lines: &mut Enumerate) -> (bool, String, u pub fn parse_go_codeblock(lines: &mut Enumerate) -> (bool, String, usize) { let (is_snippet, mut code_snippet, snippet_lines) = parse_generic_codeblock(lines); - if is_snippet && !GO_PACKAGE_RE.is_match(&code_snippet) { + if is_snippet && !has_go_package(&code_snippet) { code_snippet.insert_str(0, GO_TEMPORARY_PACKAGE_NAME); } (is_snippet, code_snippet, snippet_lines) } -// TODO: check for multiline comments pub static GO_PACKAGE_RE: std::sync::LazyLock = std::sync::LazyLock::new(|| Regex::new(r"^\s*package\s+\w").unwrap()); +/// A Go package clause is the first token of a file that is neither a comment +/// nor whitespace, so leading comments are skipped before looking for it. +#[inline] +pub fn has_go_package(snippet: &str) -> bool { + let mut rest = snippet.trim_start(); + + loop { + if let Some(after) = rest.strip_prefix("//") { + rest = after + .split_once('\n') + .map_or("", |(_, tail)| tail) + .trim_start(); + } else if let Some(after) = rest.strip_prefix("/*") { + rest = after + .split_once("*/") + .map_or("", |(_, tail)| tail) + .trim_start(); + } else { + return GO_PACKAGE_RE.is_match(rest); + } + } +} + #[inline] pub fn remove_go_package(snippet: String) -> String { if snippet.contains(GO_TEMPORARY_PACKAGE_NAME) { @@ -99,3 +121,33 @@ mod test_go_package_re { } } } + +#[cfg(test)] +mod test_has_go_package { + use crate::parser::has_go_package; + + #[test] + fn it_should_match() { + for s in [ + "package mdsf", + "// mdsf.go\npackage mdsf", + "//go:build integration\n\npackage mdsf", + "// mdsf.go\n// second line\npackage mdsf", + "/*\nI like my\npackage manager\n*/\npackage mdsf", + ] { + assert!(has_go_package(s), "'{s}' did not match"); + } + } + + #[test] + fn it_should_not_match() { + for s in [ + "const foo = 1", + "// package mdsf", + "// mdsf.go\nconst foo = 1", + "/*\nI like my\npackage manager\n*/\n\nconst foo = 1", + ] { + assert!(!has_go_package(s), "'{s}' matched"); + } + } +}