ass_editor/core/document/
text_access.rs1use super::EditorDocument;
7use crate::core::errors::{EditorError, Result};
8use crate::core::position::{LineColumn, Position, Range};
9
10#[cfg(not(feature = "std"))]
11use alloc::string::{String, ToString};
12
13impl EditorDocument {
14 #[must_use]
16 pub fn len_bytes(&self) -> usize {
17 #[cfg(feature = "rope")]
18 {
19 self.text_rope.len_bytes()
20 }
21 #[cfg(not(feature = "rope"))]
22 {
23 self.text_content.len()
24 }
25 }
26
27 #[must_use]
29 pub fn len_lines(&self) -> usize {
30 #[cfg(feature = "rope")]
31 {
32 self.text_rope.len_lines()
33 }
34 #[cfg(not(feature = "rope"))]
35 {
36 self.text_content.lines().count().max(1)
37 }
38 }
39
40 #[must_use]
42 pub fn is_empty(&self) -> bool {
43 self.len_bytes() == 0
44 }
45
46 #[must_use]
48 pub fn text(&self) -> String {
49 #[cfg(feature = "rope")]
50 {
51 self.text_rope.to_string()
52 }
53 #[cfg(not(feature = "rope"))]
54 {
55 self.text_content.clone()
56 }
57 }
58
59 #[cfg(feature = "rope")]
61 #[must_use]
62 pub fn rope(&self) -> &ropey::Rope {
63 &self.text_rope
64 }
65
66 #[must_use]
68 pub fn len(&self) -> usize {
69 #[cfg(feature = "rope")]
70 {
71 self.text_rope.len_bytes()
72 }
73 #[cfg(not(feature = "rope"))]
74 {
75 self.text_content.len()
76 }
77 }
78
79 pub fn text_range(&self, range: Range) -> Result<String> {
81 let start = range.start.offset;
82 let end = range.end.offset;
83
84 if end > self.len_bytes() {
85 return Err(EditorError::InvalidRange {
86 start,
87 end,
88 length: self.len_bytes(),
89 });
90 }
91
92 #[cfg(feature = "rope")]
93 {
94 let start_char = self.text_rope.byte_to_char(start);
96 let end_char = self.text_rope.byte_to_char(end);
97 Ok(self.text_rope.slice(start_char..end_char).to_string())
98 }
99 #[cfg(not(feature = "rope"))]
100 {
101 Ok(self.text_content[start..end].to_string())
102 }
103 }
104
105 #[cfg(feature = "rope")]
107 pub fn position_to_line_column(&self, pos: Position) -> Result<LineColumn> {
108 if pos.offset > self.len_bytes() {
109 return Err(EditorError::PositionOutOfBounds {
110 position: pos.offset,
111 length: self.len_bytes(),
112 });
113 }
114
115 let line_idx = self.text_rope.byte_to_line(pos.offset);
116 let line_start = self.text_rope.line_to_byte(line_idx);
117 let col_offset = pos.offset - line_start;
118
119 let line = self.text_rope.line(line_idx);
121 let mut char_col = 0;
122 let mut byte_count = 0;
123
124 for ch in line.chars() {
125 if byte_count >= col_offset {
126 break;
127 }
128 byte_count += ch.len_utf8();
129 char_col += 1;
130 }
131
132 LineColumn::new(line_idx + 1, char_col + 1)
134 }
135
136 #[cfg(not(feature = "rope"))]
138 pub fn position_to_line_column(&self, pos: Position) -> Result<LineColumn> {
139 if pos.offset > self.len_bytes() {
140 return Err(EditorError::PositionOutOfBounds {
141 position: pos.offset,
142 length: self.len_bytes(),
143 });
144 }
145
146 let mut line = 1;
147 let mut col = 1;
148 let mut byte_pos = 0;
149
150 for ch in self.text_content.chars() {
151 if byte_pos >= pos.offset {
152 break;
153 }
154
155 if ch == '\n' {
156 line += 1;
157 col = 1;
158 } else {
159 col += 1;
160 }
161
162 byte_pos += ch.len_utf8();
163 }
164
165 LineColumn::new(line, col)
166 }
167}