1pub mod ast_utils;
6pub mod code_block_utils;
7pub mod document_structure;
8pub mod early_returns;
9pub mod element_cache;
10pub mod markdown_elements;
11pub mod range_utils;
12pub mod regex_cache;
13pub mod string_interner;
14
15pub use ast_utils::AstCache;
16pub use code_block_utils::CodeBlockUtils;
17pub use document_structure::DocumentStructure;
18pub use markdown_elements::{ElementQuality, ElementType, MarkdownElement, MarkdownElements};
19pub use range_utils::LineIndex;
20
21pub trait StrExt {
23 fn replace_trailing_spaces(&self, replacement: &str) -> String;
25
26 fn has_trailing_spaces(&self) -> bool;
28
29 fn trailing_spaces(&self) -> usize;
31}
32
33impl StrExt for str {
34 fn replace_trailing_spaces(&self, replacement: &str) -> String {
35 let (content, ends_with_newline) = if let Some(stripped) = self.strip_suffix('\n') {
39 (stripped, true)
40 } else {
41 (self, false)
42 };
43
44 let mut non_space_len = content.len();
46 for c in content.chars().rev() {
47 if c == ' ' {
48 non_space_len -= 1;
49 } else {
50 break;
51 }
52 }
53
54 let mut result = String::with_capacity(
56 non_space_len + replacement.len() + if ends_with_newline { 1 } else { 0 },
57 );
58 result.push_str(&content[..non_space_len]);
59 result.push_str(replacement);
60 if ends_with_newline {
61 result.push('\n');
62 }
63
64 result
65 }
66
67 fn has_trailing_spaces(&self) -> bool {
68 self.trailing_spaces() > 0
69 }
70
71 fn trailing_spaces(&self) -> usize {
72 let content = self.strip_suffix('\n').unwrap_or(self);
76
77 let mut space_count = 0;
79 for c in content.chars().rev() {
80 if c == ' ' {
81 space_count += 1;
82 } else {
83 break;
84 }
85 }
86
87 space_count
88 }
89}
90
91use std::collections::hash_map::DefaultHasher;
92use std::hash::{Hash, Hasher};
93
94pub fn fast_hash(content: &str) -> u64 {
107 let mut hasher = DefaultHasher::new();
108 content.hash(&mut hasher);
109 hasher.finish()
110}