workspacer_register/
parse_new_top_block_snippet.rs

1// ---------------- [ File: workspacer-register/src/parse_new_top_block_snippet.rs ]
2crate::ix!();
3
4pub fn parse_new_top_block_snippet(new_top_block: &str) -> (Vec<TopBlockMacro>, Vec<String>) {
5    trace!("Entering parse_new_top_block_snippet");
6    debug!("new_top_block length={}", new_top_block.len());
7
8    // (1) Gather macros using RA-based parsing so we pick up leading comments:
9    let new_macros = parse_new_macros_with_comments(new_top_block);
10    debug!("Found {} new macros in new_top_block", new_macros.len());
11
12    // (2) Collect lines that do NOT contain `x!{...}` (so, likely user snippet lines).
13    let mut candidate_lines = extract_non_macro_lines(new_top_block);
14    debug!(
15        "Initially found {} non-macro lines via extract_non_macro_lines",
16        candidate_lines.len()
17    );
18
19    // (3) Remove from `candidate_lines` any lines that appear in a macro’s leading_comments.
20    //     Because we want those comment lines to stay "attached" to the macro, not counted
21    //     as separate snippet lines.
22    for mac in &new_macros {
23        if let Some(comments) = mac.leading_comments() {
24            for comment_line in comments.lines() {
25                // We remove exact matches to that line, ignoring only the trailing newline from the macro text itself.
26                let comment_line = comment_line; // direct, no trimming by default
27                candidate_lines.retain(|cl| cl != comment_line);
28            }
29        }
30    }
31
32    // (4) Filter out any totally empty lines after that.
33    let snippet_lines: Vec<String> = candidate_lines
34        .into_iter()
35        .filter(|ln| !ln.trim().is_empty())
36        .collect();
37
38    debug!(
39        "After removing macro-leading lines and filtering empties, we have {} snippet lines",
40        snippet_lines.len()
41    );
42    trace!("Exiting parse_new_top_block_snippet");
43
44    (new_macros, snippet_lines)
45}
46
47#[cfg(test)]
48mod test_parse_new_top_block_snippet {
49    use super::*;
50    use tracing::{trace, debug};
51
52    #[traced_test]
53    fn test_empty_block() {
54        trace!("test_empty_block for parse_new_top_block_snippet");
55        let (macros, lines) = parse_new_top_block_snippet("");
56        debug!("macros={:?}, lines={:?}", macros, lines);
57        assert!(macros.is_empty(), "Expected no macros");
58        assert!(lines.is_empty(), "Expected no lines");
59    }
60
61    #[traced_test]
62    fn test_mixed_macros_and_lines() {
63        trace!("test_mixed_macros_and_lines for parse_new_top_block_snippet");
64        let snippet = r#"
65// comment
66x!{alpha}
67another line
68x!{beta}
69// trailing doc
70"#;
71        let (macros, lines) = parse_new_top_block_snippet(snippet);
72        debug!("macros={:?}, lines={:?}", macros, lines);
73
74        // We now expect:
75        //   macros => alpha (with leading_comments="// comment\n") 
76        //             beta  (with no leading_comments, because "another line" wasn't a comment)
77        //   lines => ["another line", "// trailing doc"]
78
79        assert_eq!(macros.len(), 2);
80        assert_eq!(macros[0].stem(), "alpha");
81        assert!(macros[0].leading_comments().as_ref().unwrap().contains("comment"));
82        assert_eq!(macros[1].stem(), "beta");
83        assert!(macros[1].leading_comments().is_none());
84
85        assert_eq!(lines.len(), 2);
86        assert_eq!(lines[0], "another line");
87        assert_eq!(lines[1], "// trailing doc");
88    }
89}