1use crate::code_block_popup::CodeBlock;
2use anyhow::Result;
3use std::io::{BufRead, Write};
4use std::path::PathBuf;
5use std::{fs::File, io::BufReader};
6use tui_textarea::TextArea;
7
8pub fn extract_code_blocks(content: &str) -> Vec<CodeBlock> {
9 let mut code_blocks = Vec::new();
10 let mut in_code_block = false;
11 let mut current_block = String::new();
12 let mut current_language = String::new();
13 let mut start_line = 0;
14
15 for (i, line) in content.lines().enumerate() {
16 if line.trim().starts_with("```") {
17 if in_code_block {
18 code_blocks.push(CodeBlock::new(
20 current_block.trim_end().to_string(),
21 current_language.clone(),
22 start_line,
23 i,
24 ));
25 current_block.clear();
26 current_language.clear();
27 in_code_block = false;
28 } else {
29 in_code_block = true;
31 start_line = i;
32
33 let lang_part = line.trim_start_matches('`').trim();
34 current_language = lang_part.to_string();
35 }
36 } else if in_code_block {
37 current_block.push_str(line);
38 current_block.push('\n');
39 }
40 }
41
42 if in_code_block && !current_block.is_empty() {
43 code_blocks.push(CodeBlock::new(
44 current_block.trim_end().to_string(),
45 current_language,
46 start_line,
47 content.lines().count() - 1,
48 ));
49 }
50
51 code_blocks
52}
53
54pub fn save_textareas(textareas: &[TextArea], titles: &[String], file_path: PathBuf) -> Result<()> {
55 let mut file = File::create(file_path)?;
56 for (textarea, title) in textareas.iter().zip(titles.iter()) {
57 writeln!(file, "# {}", title)?;
58 let content = textarea.lines().join("\n");
59 let mut in_code_block = false;
60 for line in content.lines() {
61 if line.trim().starts_with("```") {
62 in_code_block = !in_code_block;
63 }
64 if in_code_block || !line.starts_with('#') {
65 writeln!(file, "{}", line)?;
66 } else {
67 writeln!(file, "\\{}", line)?;
68 }
69 }
70 }
71 Ok(())
72}
73
74pub fn load_textareas(file_path: PathBuf) -> Result<(Vec<TextArea<'static>>, Vec<String>)> {
75 let file = File::open(file_path)?;
76 let reader = BufReader::new(file);
77 let mut textareas = Vec::with_capacity(10);
78 let mut titles = Vec::with_capacity(10);
79 let mut current_textarea = TextArea::default();
80 let mut current_title = String::new();
81 let mut in_code_block = false;
82 let mut is_first_line = true;
83
84 for line in reader.lines() {
85 let line = line?;
86 if !in_code_block && line.starts_with("# ") && is_first_line {
87 current_title = line[2..].to_string();
88 is_first_line = false;
89 } else {
90 if line.trim().starts_with("```") {
91 in_code_block = !in_code_block;
92 }
93 if in_code_block {
94 current_textarea.insert_str(&line);
95 } else if let Some(strip) = line.strip_prefix('\\') {
96 current_textarea.insert_str(strip);
97 } else if line.starts_with("# ") && !is_first_line {
98 if !current_title.is_empty() {
99 textareas.push(current_textarea);
100 titles.push(current_title);
101 }
102 current_textarea = TextArea::default();
103 current_title = line[2..].to_string();
104 is_first_line = true;
105 continue;
106 } else {
107 current_textarea.insert_str(&line);
108 }
109 current_textarea.insert_newline();
110 is_first_line = false;
111 }
112 }
113
114 if !current_title.is_empty() {
115 textareas.push(current_textarea);
116 titles.push(current_title);
117 }
118
119 Ok((textareas, titles))
120}
121
122#[cfg(test)]
123mod tests {
124 use super::*;
125
126 #[test]
127 fn test_extract_code_blocks() {
128 let content = r#"# Test Document
129
130```rust
131fn main() {
132 println!("Hello, world!");
133}
134```
135
136Some text here
137
138```python
139def hello():
140 print("Hello")
141```"#;
142
143 let blocks = extract_code_blocks(content);
144 assert_eq!(blocks.len(), 2);
145 assert_eq!(blocks[0].language, "rust");
146 assert_eq!(
147 blocks[0].content,
148 "fn main() {\n println!(\"Hello, world!\");\n}"
149 );
150 assert_eq!(blocks[1].language, "python");
151 assert_eq!(blocks[1].content, "def hello():\n print(\"Hello\")");
152 }
153
154 #[test]
155 fn test_extract_empty_code_blocks() {
156 let content = "```rust\n```";
157 let blocks = extract_code_blocks(content);
158 assert_eq!(blocks.len(), 1);
159 assert_eq!(blocks[0].language, "rust");
160 assert_eq!(blocks[0].content, "");
161 }
162
163 #[test]
164 fn test_unclosed_code_block() {
165 let content = "```js\nlet x = 1;";
166 let blocks = extract_code_blocks(content);
167 assert_eq!(blocks.len(), 1);
168 assert_eq!(blocks[0].language, "js");
169 assert_eq!(blocks[0].content, "let x = 1;");
170 }
171}