1
2
use crate::{LineNumber, Row, RowIndex};
2
use serde::Serialize;
3
use std::cmp::Ordering;
4
use std::collections::hash_map::DefaultHasher;
5
use std::fmt;
6
use std::fs;
7
use std::hash::{Hash, Hasher};
8
use std::io::{Error, Write};
9
use std::path::{Path, PathBuf};
10
use std::slice::{Iter, IterMut};
11
use unicode_segmentation::UnicodeSegmentation;
12

            
13
1
#[derive(Serialize)]
14
pub struct Document {
15
    rows: Vec<Row>,
16
    pub filename: Option<PathBuf>,
17
}
18

            
19
impl fmt::Debug for Document {
20
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21
        f.debug_struct(self.filename.as_ref().unwrap().to_str().unwrap_or_default())
22
            .finish()
23
    }
24
}
25

            
26
impl Default for Document {
27
47
    fn default() -> Self {
28
47
        Self {
29
47
            rows: vec![Row::from("")],
30
47
            filename: None,
31
        }
32
47
    }
33
}
34

            
35
impl Hash for Document {
36
113
    fn hash<H: Hasher>(&self, state: &mut H) {
37
1135
        for row in self.iter() {
38
2044
            row.hash(state);
39
        }
40
113
    }
41
}
42

            
43
impl Document {
44
    #[must_use]
45
77
    pub fn new(rows: Vec<Row>, filename: PathBuf) -> Self {
46
77
        Self {
47
77
            rows,
48
154
            filename: Some(filename),
49
        }
50
77
    }
51

            
52
    #[must_use]
53
3
    pub fn new_empty(filename: PathBuf) -> Self {
54
3
        Self {
55
3
            rows: vec![Row::from("")],
56
3
            filename: Some(filename),
57
        }
58
3
    }
59

            
60
    /// # Panics
61
    ///
62
    /// This function will panic if the path contains a non UTF-8 character
63
    #[must_use]
64
9
    pub fn swap_filename(filename: &Path) -> PathBuf {
65
9
        let parent = filename.parent().unwrap();
66
9
        let stripped_filename = filename.file_name().unwrap();
67
9
        let new_filename = format!(".{}.swp", stripped_filename.to_str().unwrap());
68
9
        let joined_os_str = parent.join(new_filename);
69
9
        let out = joined_os_str.as_os_str().to_str().unwrap_or_default();
70
9
        PathBuf::from(out)
71
9
    }
72

            
73
    /// # Errors
74
    /// # Panics
75
    /// Returns an error if a file bearing the provided filename
76
    /// cannot be open.
77
5
    pub fn open(filename: PathBuf) -> Result<Self, Error> {
78
5
        if !filename.is_file() {
79
2
            return Ok(Self::new_empty(filename));
80
        }
81
6
        let file_contents = if (&Self::swap_filename(&filename)).is_file() {
82
            fs::read_to_string(Self::swap_filename(&filename))?
83
        } else {
84
3
            fs::read_to_string(&filename)?
85
        };
86

            
87
3
        let mut rows = Vec::new();
88
6
        for line in file_contents.lines() {
89
6
            rows.push(Row::from(line));
90
        }
91
3
        Ok(Self {
92
3
            rows,
93
3
            filename: Some(filename),
94
        })
95
5
    }
96

            
97
    /// # Errors
98
    /// # Panics
99
    /// Can return an error if the file can't be created or written to.
100
    pub fn save_to_swap_file(&self) -> Result<(), Error> {
101
        if self.filename.is_some() {
102
            let mut file = fs::File::create(Self::swap_filename(self.filename.as_ref().unwrap()))?;
103
            for row in &self.rows {
104
                file.write_all(row.as_bytes())?;
105
                file.write_all(b"\n")?;
106
            }
107
        }
108
        Ok(())
109
    }
110

            
111
5
    pub fn trim_trailing_spaces(&mut self) {
112
15
        for row in self.iter_mut() {
113
20
            row.trim_end_inplace();
114
        }
115
5
    }
116

            
117
    /// # Errors
118
    /// # Panics
119
    /// Can return an error if the file can't be created or written to.
120
4
    pub fn save(&self) -> Result<(), Error> {
121
4
        if self.filename.is_some() {
122
4
            let filename = &self.filename.as_ref().unwrap();
123
4
            let mut file = fs::File::create(filename)?;
124

            
125
13
            for row in &self.rows {
126
9
                file.write_all(row.as_bytes())?;
127
9
                file.write_all(b"\n")?;
128
            }
129
4
            if fs::remove_file(Self::swap_filename(filename)).is_ok() {
130
                // pass
131
            }
132
4
        }
133
4
        Ok(())
134
4
    }
135

            
136
    /// # Errors
137
    /// # Panics
138
    /// Can return an error if the file can't be created or written to.
139
    pub fn save_as(&mut self, new_name: &str) -> Result<(), Error> {
140
        if self.filename.is_some() && !new_name.is_empty() {
141
            fs::rename(self.filename.as_ref().unwrap(), new_name)?;
142
        }
143
        self.filename = Some(PathBuf::from(new_name));
144
        self.save()
145
    }
146

            
147
    #[must_use]
148
150
    pub fn get_row(&self, index: RowIndex) -> Option<&Row> {
149
150
        self.rows.get(index.value)
150
150
    }
151

            
152
14
    pub fn remove_row(&mut self, index: RowIndex) -> Row {
153
14
        self.rows.remove(index.value)
154
14
    }
155

            
156
    #[must_use]
157
6
    pub fn row_lengths(&self) -> Vec<usize> {
158
6
        self.rows.iter().map(Row::len).collect()
159
6
    }
160

            
161
    #[must_use]
162
2
    pub fn is_empty(&self) -> bool {
163
2
        self.rows.len() == 0
164
2
    }
165

            
166
    #[must_use]
167
227
    pub fn num_rows(&self) -> usize {
168
227
        self.rows.len()
169
227
    }
170

            
171
    #[must_use]
172
2
    pub fn num_words(&self) -> usize {
173
2
        self.iter().map(Row::num_words).sum()
174
2
    }
175

            
176
    /// Get the document row corresponding to a given line number
177
    #[must_use]
178
11
    pub fn row_for_line_number(&self, line_number: LineNumber) -> Option<&Row> {
179
11
        self.get_row(RowIndex::from(line_number))
180
11
    }
181

            
182
    /// Return the line number of the last line in the file
183
    #[must_use]
184
70
    pub fn last_line_number(&self) -> LineNumber {
185
70
        LineNumber::new(self.num_rows())
186
70
    }
187

            
188
116
    pub fn iter(&self) -> Iter<Row> {
189
116
        self.rows.iter()
190
116
    }
191

            
192
5
    pub fn iter_mut(&mut self) -> IterMut<Row> {
193
5
        self.rows.iter_mut()
194
5
    }
195

            
196
4
    pub fn insert_string(&mut self, text: &str, x: usize, y: RowIndex) {
197
4
        let mut delta_x: usize = 0;
198
4
        let mut delta_y: usize = 0;
199
39
        for c in text.graphemes(true) {
200
65
            if c == "\n" {
201
3
                self.insert_newline(x + delta_x, RowIndex::new(y.value + delta_y));
202
3
                delta_x = 0;
203
3
                delta_y += 1;
204
28
            } else if let Some(current_row) = self.rows.get_mut(y.value + delta_y) {
205
28
                current_row.append_str(c);
206
28
                delta_x += 1;
207
            }
208
        }
209
4
    }
210

            
211
13
    pub fn insert_newline(&mut self, x: usize, y: RowIndex) {
212
13
        if y.value > self.num_rows() {
213
            return;
214
        }
215
13
        let current_row = self.rows.get_mut(y.value);
216
13
        if let Some(current_row) = current_row {
217
26
            if x < current_row.len().saturating_sub(1) {
218
4
                let split_row = current_row.split(x);
219
4
                self.rows.insert(y.next().value, split_row);
220
                // newline inserted in the middle of the row
221
            } else {
222
9
                let new_row = Row::default();
223
9
                if y.value == self.num_rows() || y.next().value == self.num_rows() {
224
4
                    self.rows.push(new_row);
225
                } else {
226
5
                    self.rows.insert(y.next().value, new_row);
227
                }
228
            }
229
        }
230
13
    }
231

            
232
36
    pub fn insert(&mut self, c: char, x: usize, y: RowIndex) {
233
36
        match y.value.cmp(&self.num_rows()) {
234
3
            Ordering::Equal | Ordering::Greater => {
235
3
                let mut row = Row::default();
236
3
                row.insert(0, c);
237
3
                self.rows.push(row);
238
3
            }
239
            Ordering::Less => {
240
33
                if let Some(row) = self.rows.get_mut(y.value) {
241
33
                    row.insert(x, c);
242
                }
243
            }
244
        }
245
36
    }
246

            
247
2
    pub fn delete_string(&mut self, text: &str, x: usize, y: RowIndex) {
248
2
        let mut x = x;
249
2
        let mut delta_x: usize = 0;
250
2
        let mut delta_y: usize = 0;
251
34
        for c in text.chars() {
252
88
            if c == '\n' {
253
2
                self.join_row_with_previous_one(0, RowIndex::new(y.value - delta_y), None);
254
2
                delta_y += 1;
255
2
                delta_x = 0;
256
4
                if let Some(prev_row) = self.rows.get(y.value - delta_y) {
257
2
                    x = prev_row.len().saturating_sub(1);
258
                };
259
            } else {
260
28
                self.delete(
261
28
                    x - delta_x,
262
28
                    x - delta_x + 1,
263
28
                    RowIndex::new(y.value - delta_y),
264
                );
265
28
                delta_x += 1;
266
            }
267
        }
268
2
    }
269

            
270
37
    pub fn delete(&mut self, x: usize, from_x: usize, y: RowIndex) {
271
37
        if y.value >= self.num_rows() {
272
            return;
273
        }
274
37
        if let Some(row) = self.rows.get_mut(y.value) {
275
            // Deletion at the very start of a line means we append the current line to the previous one
276
37
            if x == 0 && from_x == 0 && y.value > 0 {
277
4
                self.join_row_with_previous_one(x, y, None);
278
            } else {
279
33
                row.delete(x);
280
            }
281
        }
282
37
    }
283

            
284
7
    pub fn delete_row(&mut self, index: RowIndex) {
285
7
        if index.value > self.num_rows() {
286
7
        } else if self.num_rows() == 1 {
287
2
            if let Some(row) = self.rows.get_mut(0) {
288
1
                row.string = "".to_string();
289
            }
290
6
        } else if self.get_row(index).is_some() {
291
6
            self.remove_row(index);
292
        }
293
7
    }
294

            
295
8
    pub fn join_row_with_previous_one(&mut self, x: usize, y: RowIndex, join_with: Option<char>) {
296
8
        let current_row = self.remove_row(y);
297
8
        if let Some(previous_row) = self.rows.get_mut(y.value.saturating_sub(1)) {
298
8
            if let Some(join_char) = join_with {
299
2
                previous_row.insert(x.saturating_add(1), join_char);
300
            }
301
8
            previous_row.append(&current_row);
302
        }
303
8
    }
304

            
305
    #[must_use]
306
113
    pub fn hashed(&self) -> u64 {
307
113
        let mut s = DefaultHasher::new();
308
113
        self.hash(&mut s);
309
113
        s.finish()
310
113
    }
311
}
312

            
313
#[cfg(test)]
314
#[path = "./document_test.rs"]
315
mod document_test;