code_block.rs 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. use pulldown_cmark::{Event, Parser, Tag};
  2. use std::ops::Range;
  3. #[derive(Debug)]
  4. pub struct ColorCodeBlocks {
  5. code: Vec<Range<usize>>,
  6. }
  7. impl ColorCodeBlocks {
  8. pub fn new(s: &str) -> ColorCodeBlocks {
  9. let mut code = Vec::new();
  10. let mut parser = Parser::new(s);
  11. let mut before_event = parser.get_offset();
  12. 'outer: while let Some(event) = parser.next() {
  13. if let Event::Start(Tag::Code) | Event::Start(Tag::CodeBlock(_)) = event {
  14. let start = before_event;
  15. loop {
  16. match parser.next() {
  17. Some(Event::End(Tag::Code)) | Some(Event::End(Tag::CodeBlock(_))) => {
  18. let end = parser.get_offset();
  19. code.push(start..end);
  20. break;
  21. }
  22. Some(_) => {}
  23. None => break 'outer,
  24. }
  25. }
  26. }
  27. before_event = parser.get_offset();
  28. }
  29. ColorCodeBlocks { code }
  30. }
  31. pub fn overlaps_code(&self, region: Range<usize>) -> Option<Range<usize>> {
  32. for code in &self.code {
  33. // See https://stackoverflow.com/questions/3269434.
  34. if code.start <= region.end && region.start <= code.end {
  35. return Some(code.clone());
  36. }
  37. }
  38. None
  39. }
  40. }
  41. #[cfg(test)]
  42. #[derive(Debug, PartialEq, Eq)]
  43. enum Code<'a> {
  44. Yes(&'a str),
  45. No(&'a str),
  46. }
  47. #[cfg(test)]
  48. fn bodies(s: &str) -> Vec<Code<'_>> {
  49. let mut bodies = Vec::new();
  50. let cbs = ColorCodeBlocks::new(s);
  51. let mut previous = 0..0;
  52. for range in &cbs.code {
  53. let range = range.clone();
  54. if previous.end != range.start {
  55. bodies.push(Code::No(&s[previous.end..range.start]));
  56. }
  57. bodies.push(Code::Yes(&s[range.clone()]));
  58. previous = range.clone();
  59. }
  60. if let Some(range) = cbs.code.last() {
  61. if range.end != s.len() {
  62. bodies.push(Code::No(&s[range.end..]));
  63. }
  64. }
  65. bodies
  66. }
  67. #[test]
  68. fn cbs_1() {
  69. assert_eq!(
  70. bodies("`hey you`bar me too"),
  71. [Code::Yes("`hey you`"), Code::No("bar me too")]
  72. );
  73. }
  74. #[test]
  75. fn cbs_2() {
  76. assert_eq!(
  77. bodies("`hey you` <b>me too</b>"),
  78. [Code::Yes("`hey you`"), Code::No(" <b>me too</b>")]
  79. );
  80. }
  81. #[test]
  82. fn cbs_3() {
  83. assert_eq!(
  84. bodies(r"`hey you\` <b>`me too</b>"),
  85. [Code::Yes(r"`hey you\`"), Code::No(" <b>`me too</b>")]
  86. );
  87. }
  88. #[test]
  89. fn cbs_4() {
  90. assert_eq!(
  91. bodies(
  92. "
  93. ```language_spec
  94. testing
  95. ```
  96. nope
  97. "
  98. ),
  99. [
  100. Code::No("\n"),
  101. Code::Yes("```language_spec\ntesting\n```\n"),
  102. Code::No("\nnope\n")
  103. ],
  104. );
  105. }
  106. #[test]
  107. fn cbs_5() {
  108. assert_eq!(
  109. bodies(concat!(
  110. "
  111. ``` tag_after_space
  112. testing
  113. ```",
  114. " "
  115. )),
  116. [
  117. Code::No("\n"),
  118. Code::Yes("``` tag_after_space\ntesting\n``` "),
  119. ],
  120. );
  121. }
  122. #[test]
  123. fn cbs_6() {
  124. assert_eq!(
  125. bodies(
  126. "
  127. this is indented
  128. this is indented too
  129. "
  130. ),
  131. [
  132. Code::No("\n"),
  133. Code::Yes(" this is indented\n this is indented too\n"),
  134. ],
  135. );
  136. }
  137. #[test]
  138. fn cbs_7() {
  139. assert_eq!(
  140. bodies(
  141. "
  142. ```
  143. testing unclosed
  144. "
  145. ),
  146. [Code::No("\n"), Code::Yes("```\ntesting unclosed\n"),],
  147. );
  148. }
  149. #[test]
  150. fn cbs_8() {
  151. assert_eq!(
  152. bodies("`one` not `two`"),
  153. [Code::Yes("`one`"), Code::No(" not "), Code::Yes("`two`")]
  154. );
  155. }