Print Markdown to a paper in your terminal

Handle wide characters better

+161 -99
+1
Cargo.lock
··· 437 "structopt", 438 "syncat-stylesheet", 439 "terminal_size", 440 ] 441 442 [[package]]
··· 437 "structopt", 438 "syncat-stylesheet", 439 "terminal_size", 440 + "unicode-width", 441 ] 442 443 [[package]]
+1
Cargo.toml
··· 25 console = "0.13" 26 directories-next = "2.0" 27 syncat-stylesheet = { version = "2.1.4", features = ["ansi_term"] }
··· 25 console = "0.13" 26 directories-next = "2.0" 27 syncat-stylesheet = { version = "2.1.4", features = ["ansi_term"] } 28 + unicode-width = "0.1"
+15 -4
src/main.rs
··· 8 use structopt::StructOpt; 9 use syncat_stylesheet::Stylesheet; 10 use terminal_size::{terminal_size, Width}; 11 12 mod dirs; 13 mod printer; ··· 17 18 use printer::Printer; 19 use words::Words; 20 21 /// Prints papers in your terminal 22 #[derive(StructOpt, Debug)] ··· 113 return; 114 } 115 116 - let centering = " ".repeat((terminal_width - width) / 2); 117 118 let stylesheet = Stylesheet::from_file(dirs::active_color().join("paper.syncat")) 119 .unwrap_or_else(|_| { ··· 153 let mut buffer = String::new(); 154 let mut indent = None; 155 for word in Words::preserving_whitespace(line) { 156 - if buffer.chars().count() + word.chars().count() > available_width { 157 println!( 158 "{}{}{}{}{}{}", 159 centering, 160 margin, 161 paper_style.paint(&buffer), 162 - paper_style.paint(" ".repeat(available_width - buffer.chars().count())), 163 margin, 164 shadow_style.paint(" "), 165 ); ··· 182 centering, 183 margin, 184 paper_style.paint(&buffer), 185 - paper_style.paint(" ".repeat(available_width - buffer.chars().count())), 186 margin, 187 shadow_style.paint(" "), 188 );
··· 8 use structopt::StructOpt; 9 use syncat_stylesheet::Stylesheet; 10 use terminal_size::{terminal_size, Width}; 11 + use unicode_width::UnicodeWidthChar; 12 13 mod dirs; 14 mod printer; ··· 18 19 use printer::Printer; 20 use words::Words; 21 + 22 + fn str_width(s: &str) -> usize { 23 + strip_ansi_codes(s) 24 + .chars() 25 + .flat_map(UnicodeWidthChar::width) 26 + .sum() 27 + } 28 29 /// Prints papers in your terminal 30 #[derive(StructOpt, Debug)] ··· 121 return; 122 } 123 124 + let centering = " ".repeat((terminal_width.saturating_sub(width)) / 2); 125 126 let stylesheet = Stylesheet::from_file(dirs::active_color().join("paper.syncat")) 127 .unwrap_or_else(|_| { ··· 161 let mut buffer = String::new(); 162 let mut indent = None; 163 for word in Words::preserving_whitespace(line) { 164 + if str_width(&buffer) + str_width(&word) > available_width { 165 println!( 166 "{}{}{}{}{}{}", 167 centering, 168 margin, 169 paper_style.paint(&buffer), 170 + paper_style.paint( 171 + " ".repeat(available_width.saturating_sub(str_width(&buffer))) 172 + ), 173 margin, 174 shadow_style.paint(" "), 175 ); ··· 192 centering, 193 margin, 194 paper_style.paint(&buffer), 195 + paper_style 196 + .paint(" ".repeat(available_width.saturating_sub(str_width(&buffer)))), 197 margin, 198 shadow_style.paint(" "), 199 );
+46 -26
src/printer.rs
··· 1 use crate::table::Table; 2 use crate::termpix; 3 use crate::words::Words; 4 use ansi_term::Style; 5 - use console::{measure_text_width, AnsiCodeIterator}; 6 use image::{self, GenericImageView as _}; 7 use pulldown_cmark::{Alignment, CodeBlockKind, Event, Tag}; 8 use std::convert::{TryFrom, TryInto}; ··· 189 let mut all_scopes = scopes.clone(); 190 all_scopes.append(&mut extra_scopes.unwrap_or(&[]).to_vec()); 191 let style = Self::resolve_scopes(&stylesheet, &all_scopes, Some("prefix")); 192 - Some((format!("{}", style.paint(&prefix)), prefix.chars().count())) 193 }) 194 .fold((String::new(), 0), |(s, c), (s2, c2)| (s + &s2, c + c2)) 195 } ··· 208 let mut all_scopes = scopes.clone(); 209 all_scopes.append(&mut extra_scopes.unwrap_or(&[]).to_vec()); 210 let style = Self::resolve_scopes(&stylesheet, &all_scopes, Some("suffix")); 211 - Some((format!("{}", style.paint(&suffix)), suffix.chars().count())) 212 }) 213 .fold((String::new(), 0), |(s, c), (s2, c2)| (s2 + &s, c + c2)) 214 } ··· 272 self.centering, 273 self.margin, 274 prefix, 275 - self.paper_style() 276 - .paint(" ".repeat(self.width - prefix_len - suffix_len)), 277 suffix, 278 self.margin, 279 self.shadow(), ··· 289 self.centering, 290 self.margin, 291 prefix, 292 - self.style() 293 - .paint("─".repeat(self.width - prefix_len - suffix_len)), 294 suffix, 295 self.margin, 296 self.shadow(), ··· 304 return; 305 }; 306 let (heading, rows) = std::mem::replace(&mut self.table, (vec![], vec![])); 307 - let available_width = self.width - self.prefix_len() - self.suffix_len(); 308 let table_str = 309 Table::new(heading, rows, available_width).print(self.paper_style(), alignments); 310 for line in table_str.lines() { ··· 317 line, 318 prefix, 319 self.paper_style() 320 - .paint(" ".repeat(available_width - measure_text_width(line))), 321 suffix, 322 self.margin, 323 self.shadow(), ··· 338 let mut first_prefix = Some(self.prefix2(Some(&[&language_context[..]]))); 339 let mut first_suffix = Some(self.suffix2(Some(&[&language_context[..]]))); 340 341 - let available_width = self.width 342 - - first_prefix.as_ref().unwrap().1 343 - - first_suffix.as_ref().unwrap().1; 344 let buffer = std::mem::replace(&mut self.buffer, String::new()); 345 let buffer = if self.opts.syncat { 346 let syncat = Command::new("syncat") ··· 368 .lines() 369 .map(|mut line| { 370 let mut output = String::new(); 371 - while line.chars().count() > available_width { 372 let prefix = line.chars().take(available_width).collect::<String>(); 373 output = format!("{}{}\n", output, prefix); 374 line = &line[prefix.len()..]; ··· 377 "{}{}{}\n", 378 output, 379 line, 380 - " ".repeat(available_width - line.chars().count()) 381 ) 382 }) 383 .collect() ··· 401 ); 402 403 for line in buffer.lines() { 404 - let width = measure_text_width(line); 405 let (prefix, _) = self.prefix2(Some(&[&language_context[..]])); 406 let (suffix, _) = self.suffix2(Some(&[&language_context[..]])); 407 print!( ··· 424 } 425 println!( 426 "{}{}{}{}", 427 - style.paint(" ".repeat(available_width - width)), 428 suffix, 429 self.margin, 430 self.shadow(), ··· 444 prefix, 445 format!( 446 "{}{}", 447 - style.paint(" ".repeat(available_width - lang.chars().count())), 448 self.style3(Some(&[&language_context[..]]), Some("lang-tag")) 449 .paint(lang) 450 ), ··· 489 suffix, 490 self.paper_style().paint( 491 " ".repeat( 492 - self.width - measure_text_width(&self.content) - prefix_len - suffix_len 493 ) 494 ), 495 self.margin, ··· 529 } 530 let style = self.style(); 531 for word in Words::new(s) { 532 - if measure_text_width(&self.content) 533 - + word.len() 534 - + self.prefix_len() 535 - + self.suffix_len() 536 > self.width 537 { 538 self.flush(); ··· 542 } else { 543 &word 544 }; 545 - let available_len = self.width - self.prefix_len() - self.suffix_len(); 546 - while measure_text_width(&self.content) + word.len() > available_len { 547 let part = word.chars().take(available_len).collect::<String>(); 548 self.target().push_str(&format!("{}", style.paint(&part))); 549 word = &word[part.len()..]; ··· 640 self.flush(); 641 642 if !self.opts.no_images { 643 - let available_width = 644 - self.width - self.prefix_len() - self.suffix_len(); 645 match image::open(destination.as_ref()) { 646 Ok(image) => { 647 let (mut width, mut height) = image.dimensions();
··· 1 + use crate::str_width; 2 use crate::table::Table; 3 use crate::termpix; 4 use crate::words::Words; 5 use ansi_term::Style; 6 + use console::AnsiCodeIterator; 7 use image::{self, GenericImageView as _}; 8 use pulldown_cmark::{Alignment, CodeBlockKind, Event, Tag}; 9 use std::convert::{TryFrom, TryInto}; ··· 190 let mut all_scopes = scopes.clone(); 191 all_scopes.append(&mut extra_scopes.unwrap_or(&[]).to_vec()); 192 let style = Self::resolve_scopes(&stylesheet, &all_scopes, Some("prefix")); 193 + Some((format!("{}", style.paint(&prefix)), str_width(&prefix))) 194 }) 195 .fold((String::new(), 0), |(s, c), (s2, c2)| (s + &s2, c + c2)) 196 } ··· 209 let mut all_scopes = scopes.clone(); 210 all_scopes.append(&mut extra_scopes.unwrap_or(&[]).to_vec()); 211 let style = Self::resolve_scopes(&stylesheet, &all_scopes, Some("suffix")); 212 + Some((format!("{}", style.paint(&suffix)), str_width(&suffix))) 213 }) 214 .fold((String::new(), 0), |(s, c), (s2, c2)| (s2 + &s, c + c2)) 215 } ··· 273 self.centering, 274 self.margin, 275 prefix, 276 + self.paper_style().paint( 277 + " ".repeat( 278 + self.width 279 + .saturating_sub(prefix_len) 280 + .saturating_sub(suffix_len) 281 + ) 282 + ), 283 suffix, 284 self.margin, 285 self.shadow(), ··· 295 self.centering, 296 self.margin, 297 prefix, 298 + self.style().paint( 299 + "─".repeat( 300 + self.width 301 + .saturating_sub(prefix_len) 302 + .saturating_sub(suffix_len) 303 + ) 304 + ), 305 suffix, 306 self.margin, 307 self.shadow(), ··· 315 return; 316 }; 317 let (heading, rows) = std::mem::replace(&mut self.table, (vec![], vec![])); 318 + let available_width = self 319 + .width 320 + .saturating_sub(self.prefix_len()) 321 + .saturating_sub(self.suffix_len()); 322 let table_str = 323 Table::new(heading, rows, available_width).print(self.paper_style(), alignments); 324 for line in table_str.lines() { ··· 331 line, 332 prefix, 333 self.paper_style() 334 + .paint(" ".repeat(available_width.saturating_sub(str_width(line)))), 335 suffix, 336 self.margin, 337 self.shadow(), ··· 352 let mut first_prefix = Some(self.prefix2(Some(&[&language_context[..]]))); 353 let mut first_suffix = Some(self.suffix2(Some(&[&language_context[..]]))); 354 355 + let available_width = self 356 + .width 357 + .saturating_sub(first_prefix.as_ref().unwrap().1) 358 + .saturating_sub(first_suffix.as_ref().unwrap().1); 359 let buffer = std::mem::replace(&mut self.buffer, String::new()); 360 let buffer = if self.opts.syncat { 361 let syncat = Command::new("syncat") ··· 383 .lines() 384 .map(|mut line| { 385 let mut output = String::new(); 386 + while str_width(&line) > available_width { 387 let prefix = line.chars().take(available_width).collect::<String>(); 388 output = format!("{}{}\n", output, prefix); 389 line = &line[prefix.len()..]; ··· 392 "{}{}{}\n", 393 output, 394 line, 395 + " ".repeat(available_width.saturating_sub(str_width(&line))) 396 ) 397 }) 398 .collect() ··· 416 ); 417 418 for line in buffer.lines() { 419 + let width = str_width(line); 420 let (prefix, _) = self.prefix2(Some(&[&language_context[..]])); 421 let (suffix, _) = self.suffix2(Some(&[&language_context[..]])); 422 print!( ··· 439 } 440 println!( 441 "{}{}{}{}", 442 + style.paint(" ".repeat(available_width.saturating_sub(width))), 443 suffix, 444 self.margin, 445 self.shadow(), ··· 459 prefix, 460 format!( 461 "{}{}", 462 + style.paint(" ".repeat(available_width.saturating_sub(str_width(&lang)))), 463 self.style3(Some(&[&language_context[..]]), Some("lang-tag")) 464 .paint(lang) 465 ), ··· 504 suffix, 505 self.paper_style().paint( 506 " ".repeat( 507 + self.width 508 + .saturating_sub(str_width(&self.content)) 509 + .saturating_sub(prefix_len) 510 + .saturating_sub(suffix_len) 511 ) 512 ), 513 self.margin, ··· 547 } 548 let style = self.style(); 549 for word in Words::new(s) { 550 + if str_width(&self.content) + word.len() + self.prefix_len() + self.suffix_len() 551 > self.width 552 { 553 self.flush(); ··· 557 } else { 558 &word 559 }; 560 + let available_len = self 561 + .width 562 + .saturating_sub(self.prefix_len()) 563 + .saturating_sub(self.suffix_len()); 564 + while str_width(&self.content) + str_width(&word) > available_len { 565 let part = word.chars().take(available_len).collect::<String>(); 566 self.target().push_str(&format!("{}", style.paint(&part))); 567 word = &word[part.len()..]; ··· 658 self.flush(); 659 660 if !self.opts.no_images { 661 + let available_width = self 662 + .width 663 + .saturating_sub(self.prefix_len()) 664 + .saturating_sub(self.suffix_len()); 665 match image::open(destination.as_ref()) { 666 Ok(image) => { 667 let (mut width, mut height) = image.dimensions();
+87 -60
src/table.rs
··· 1 - use std::io::Write; 2 use ansi_term::Style; 3 - use pulldown_cmark::Alignment; 4 use console::{measure_text_width, strip_ansi_codes}; 5 - use crate::words::Words; 6 7 pub struct Table { 8 titles: Vec<String>, ··· 20 } 21 22 pub fn print(self, paper_style: Style, alignment: &[Alignment]) -> String { 23 - let Table { titles, rows, width } = self; 24 25 // NOTE: for now, styling is not supported within tables because that gets really hard 26 - let titles = titles.iter() 27 .map(|title| strip_ansi_codes(title).trim().to_string()) 28 .collect::<Vec<_>>(); 29 - let rows = rows.iter() 30 - .map(|row| row.iter() 31 - .map(|cell| strip_ansi_codes(cell).trim().to_string()) 32 - .collect() 33 - ) 34 .collect::<Vec<Vec<_>>>(); 35 36 let num_cols = usize::max( 37 titles.len(), 38 - rows.iter() 39 - .map(|row| row.len()) 40 - .max() 41 - .unwrap_or(0) 42 ); 43 44 - let mut title_longest_words = titles.iter() 45 - .map(|title| Words::new(title) 46 - .map(|word| word.trim().len()) 47 - .max() 48 - .unwrap_or(0) 49 - ) 50 - .collect::<Vec<_>>(); 51 - title_longest_words.resize(num_cols, 0); 52 - let longest_words = rows.iter() 53 - .map(|row| row 54 - .iter() 55 - .map(|cell| Words::new(cell) 56 .map(|word| word.trim().len()) 57 .max() 58 .unwrap_or(0) 59 - ) 60 - .collect::<Vec<_>>() 61 - ) 62 .fold(title_longest_words.clone(), |mut chars, row| { 63 for i in 0..row.len() { 64 chars[i] = usize::max(chars[i], row[i]); ··· 66 chars 67 }); 68 69 - let mut title_chars = titles.iter() 70 - .map(|title| title 71 - .lines() 72 - .map(measure_text_width) 73 - .max() 74 - .unwrap_or(0) 75 - ) 76 .collect::<Vec<_>>(); 77 title_chars.resize(num_cols, 0); 78 - let max_chars_per_col = rows.iter() 79 - .map(|row| row 80 - .iter() 81 - .map(|cell| cell 82 - .lines() 83 - .map(measure_text_width) 84 - .max() 85 - .unwrap_or(0) 86 - ) 87 - .collect::<Vec<_>>() 88 - ) 89 .fold(title_chars.clone(), |mut chars, row| { 90 for i in 0..row.len() { 91 chars[i] = usize::max(1, usize::max(chars[i], row[i])); ··· 101 max_chars_per_col 102 .into_iter() 103 .enumerate() 104 - .map(|(i, chars)| usize::max(longest_words[i], (max_chars_width as f64 * chars as f64 / total_chars as f64) as usize)) 105 .collect() 106 }; 107 if col_widths.iter().sum::<usize>() > max_chars_width { ··· 127 } 128 } 129 130 - fn print_row<W: Write>(w: &mut W, cols: &[usize], alignment: &[Alignment], row: &[String], paper_style: Style) { 131 - let mut row_words = row 132 - .into_iter() 133 - .map(|s| Words::new(s)) 134 - .collect::<Vec<_>>(); 135 loop { 136 let mut done = true; 137 write!(w, "{}", paper_style.paint("│")).unwrap(); ··· 139 let mut line = match words.next() { 140 Some(line) => line.trim().to_string(), 141 None => { 142 - write!(w, "{}", paper_style.paint(format!(" {: <width$} │", " ", width=cols[i]))).unwrap(); 143 continue; 144 } 145 }; ··· 159 } 160 line = line.trim().to_string(); 161 let padded = if alignment[i] == Alignment::Center { 162 - format!(" {: ^width$} │", line, width=cols[i]) 163 } else if alignment[i] == Alignment::Right { 164 - format!(" {: >width$} │", line, width=cols[i]) 165 } else { 166 - format!(" {: <width$} │", line, width=cols[i]) 167 }; 168 write!(w, "{}", paper_style.paint(padded)).unwrap(); 169 } ··· 174 } 175 } 176 177 - fn print_separator<W: Write>(w: &mut W, cols: &[usize], mid: char, left: char, cross: char, right: char, paper_style: Style) { 178 - let line = cols.iter() 179 .map(|width| mid.to_string().repeat(*width)) 180 .collect::<Vec<_>>() 181 .join(&format!("{}{}{}", mid, cross, mid)); 182 - write!(w, "{}\n", paper_style.paint(format!("{}{}{}{}{}", left, mid, line, mid, right))).unwrap(); 183 }
··· 1 + use crate::words::Words; 2 use ansi_term::Style; 3 use console::{measure_text_width, strip_ansi_codes}; 4 + use pulldown_cmark::Alignment; 5 + use std::io::Write; 6 7 pub struct Table { 8 titles: Vec<String>, ··· 20 } 21 22 pub fn print(self, paper_style: Style, alignment: &[Alignment]) -> String { 23 + let Table { 24 + titles, 25 + rows, 26 + width, 27 + } = self; 28 29 // NOTE: for now, styling is not supported within tables because that gets really hard 30 + let titles = titles 31 + .iter() 32 .map(|title| strip_ansi_codes(title).trim().to_string()) 33 .collect::<Vec<_>>(); 34 + let rows = rows 35 + .iter() 36 + .map(|row| { 37 + row.iter() 38 + .map(|cell| strip_ansi_codes(cell).trim().to_string()) 39 + .collect() 40 + }) 41 .collect::<Vec<Vec<_>>>(); 42 43 let num_cols = usize::max( 44 titles.len(), 45 + rows.iter().map(|row| row.len()).max().unwrap_or(0), 46 ); 47 48 + let mut title_longest_words = titles 49 + .iter() 50 + .map(|title| { 51 + Words::new(title) 52 .map(|word| word.trim().len()) 53 .max() 54 .unwrap_or(0) 55 + }) 56 + .collect::<Vec<_>>(); 57 + title_longest_words.resize(num_cols, 0); 58 + let longest_words = rows 59 + .iter() 60 + .map(|row| { 61 + row.iter() 62 + .map(|cell| { 63 + Words::new(cell) 64 + .map(|word| word.trim().len()) 65 + .max() 66 + .unwrap_or(0) 67 + }) 68 + .collect::<Vec<_>>() 69 + }) 70 .fold(title_longest_words.clone(), |mut chars, row| { 71 for i in 0..row.len() { 72 chars[i] = usize::max(chars[i], row[i]); ··· 74 chars 75 }); 76 77 + let mut title_chars = titles 78 + .iter() 79 + .map(|title| title.lines().map(measure_text_width).max().unwrap_or(0)) 80 .collect::<Vec<_>>(); 81 title_chars.resize(num_cols, 0); 82 + let max_chars_per_col = rows 83 + .iter() 84 + .map(|row| { 85 + row.iter() 86 + .map(|cell| cell.lines().map(measure_text_width).max().unwrap_or(0)) 87 + .collect::<Vec<_>>() 88 + }) 89 .fold(title_chars.clone(), |mut chars, row| { 90 for i in 0..row.len() { 91 chars[i] = usize::max(1, usize::max(chars[i], row[i])); ··· 101 max_chars_per_col 102 .into_iter() 103 .enumerate() 104 + .map(|(i, chars)| { 105 + usize::max( 106 + longest_words[i], 107 + (max_chars_width as f64 * chars as f64 / total_chars as f64) as usize, 108 + ) 109 + }) 110 .collect() 111 }; 112 if col_widths.iter().sum::<usize>() > max_chars_width { ··· 132 } 133 } 134 135 + fn print_row<W: Write>( 136 + w: &mut W, 137 + cols: &[usize], 138 + alignment: &[Alignment], 139 + row: &[String], 140 + paper_style: Style, 141 + ) { 142 + let mut row_words = row.into_iter().map(|s| Words::new(s)).collect::<Vec<_>>(); 143 loop { 144 let mut done = true; 145 write!(w, "{}", paper_style.paint("│")).unwrap(); ··· 147 let mut line = match words.next() { 148 Some(line) => line.trim().to_string(), 149 None => { 150 + write!( 151 + w, 152 + "{}", 153 + paper_style.paint(format!(" {: <width$} │", " ", width = cols[i])) 154 + ) 155 + .unwrap(); 156 continue; 157 } 158 }; ··· 172 } 173 line = line.trim().to_string(); 174 let padded = if alignment[i] == Alignment::Center { 175 + format!(" {: ^width$} │", line, width = cols[i]) 176 } else if alignment[i] == Alignment::Right { 177 + format!(" {: >width$} │", line, width = cols[i]) 178 } else { 179 + format!(" {: <width$} │", line, width = cols[i]) 180 }; 181 write!(w, "{}", paper_style.paint(padded)).unwrap(); 182 } ··· 187 } 188 } 189 190 + fn print_separator<W: Write>( 191 + w: &mut W, 192 + cols: &[usize], 193 + mid: char, 194 + left: char, 195 + cross: char, 196 + right: char, 197 + paper_style: Style, 198 + ) { 199 + let line = cols 200 + .iter() 201 .map(|width| mid.to_string().repeat(*width)) 202 .collect::<Vec<_>>() 203 .join(&format!("{}{}{}", mid, cross, mid)); 204 + write!( 205 + w, 206 + "{}\n", 207 + paper_style.paint(format!("{}{}{}{}{}", left, mid, line, mid, right)) 208 + ) 209 + .unwrap(); 210 }
+11 -9
src/words.rs
··· 44 self.position += start; 45 if start == chars.len() { 46 if chars.len() == 0 { 47 - return None 48 } else if self.preserve_whitespace { 49 - return Some(chars[..].into_iter().collect()) 50 } else { 51 - return Some(" ".to_string()) 52 } 53 } 54 let mut len = 0; 55 - while start+len < chars.len() { 56 - if chars[start+len] == '-' { 57 len += 1; 58 break; 59 } 60 - if chars[start+len].is_whitespace() { 61 break; 62 } 63 len += 1; ··· 65 self.position += len; 66 if chars[0].is_whitespace() { 67 if self.preserve_whitespace { 68 - return Some(chars[0..start+len].into_iter().collect::<String>()) 69 } else { 70 - return Some(String::from(" ") + &chars[start..start+len].into_iter().collect::<String>()) 71 } 72 } else { 73 - return Some(chars[start..start+len].into_iter().collect::<String>()) 74 } 75 } 76 }
··· 44 self.position += start; 45 if start == chars.len() { 46 if chars.len() == 0 { 47 + return None; 48 } else if self.preserve_whitespace { 49 + return Some(chars[..].into_iter().collect()); 50 } else { 51 + return Some(" ".to_string()); 52 } 53 } 54 let mut len = 0; 55 + while start + len < chars.len() { 56 + if chars[start + len] == '-' { 57 len += 1; 58 break; 59 } 60 + if chars[start + len].is_whitespace() { 61 break; 62 } 63 len += 1; ··· 65 self.position += len; 66 if chars[0].is_whitespace() { 67 if self.preserve_whitespace { 68 + return Some(chars[0..start + len].into_iter().collect::<String>()); 69 } else { 70 + return Some( 71 + String::from(" ") + &chars[start..start + len].into_iter().collect::<String>(), 72 + ); 73 } 74 } else { 75 + return Some(chars[start..start + len].into_iter().collect::<String>()); 76 } 77 } 78 }