1use super::*;
2use std::sync::Arc;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub struct CursorPosition {
11 line: usize,
12 column: usize,
13}
14
15impl Default for CursorPosition {
16 fn default() -> Self {
17 Self::new(1, 1)
18 }
19}
20
21impl CursorPosition {
22 pub fn new(line: usize, column: usize) -> Self {
26 Self {
27 line: line.max(1),
28 column: column.max(1),
29 }
30 }
31
32 pub fn line(&self) -> usize {
34 self.line
35 }
36
37 pub fn column(&self) -> usize {
39 self.column
40 }
41
42 pub fn to_text_position(self) -> TextPosition {
44 TextPosition::new(self.line.saturating_sub(1), self.column.saturating_sub(1))
45 }
46
47 pub fn from_text_position(position: TextPosition) -> Self {
49 Self::new(
50 position.line0().saturating_add(1),
51 position.col0().saturating_add(1),
52 )
53 }
54}
55
56#[derive(Debug, Clone, Copy, PartialEq, Eq)]
58pub enum LoadPhase {
59 Opening,
60 InspectingSource,
61 PreparingIndex,
62 RecoveringSession,
63 Ready,
64}
65
66impl LoadPhase {
67 pub(super) fn as_raw(self) -> u8 {
68 match self {
69 Self::Opening => 0,
70 Self::InspectingSource => 1,
71 Self::PreparingIndex => 2,
72 Self::RecoveringSession => 3,
73 Self::Ready => 4,
74 }
75 }
76
77 pub(super) fn from_raw(raw: u8) -> Self {
78 match raw {
79 0 => Self::Opening,
80 1 => Self::InspectingSource,
81 2 => Self::PreparingIndex,
82 3 => Self::RecoveringSession,
83 4 => Self::Ready,
84 _ => Self::Opening,
85 }
86 }
87}
88
89#[derive(Debug, Clone, PartialEq, Eq)]
91pub struct FileProgress {
92 path: Arc<PathBuf>,
93 completed_bytes: u64,
94 total_bytes: u64,
95 load_phase: Option<LoadPhase>,
96}
97
98impl FileProgress {
99 pub(super) fn new(path: Arc<PathBuf>, completed_bytes: u64, total_bytes: u64) -> Self {
100 Self {
101 path,
102 completed_bytes,
103 total_bytes,
104 load_phase: None,
105 }
106 }
107
108 pub(super) fn loading(
109 path: Arc<PathBuf>,
110 completed_bytes: u64,
111 total_bytes: u64,
112 load_phase: LoadPhase,
113 ) -> Self {
114 Self {
115 path,
116 completed_bytes,
117 total_bytes,
118 load_phase: Some(load_phase),
119 }
120 }
121
122 pub fn path(&self) -> &Path {
124 self.path.as_path()
125 }
126
127 pub fn completed_bytes(&self) -> u64 {
134 self.completed_bytes
135 }
136
137 pub fn total_bytes(&self) -> u64 {
142 self.total_bytes
143 }
144
145 pub fn load_phase(&self) -> Option<LoadPhase> {
148 self.load_phase
149 }
150
151 pub fn fraction(&self) -> f32 {
153 if self.total_bytes == 0 {
154 0.0
155 } else {
156 self.completed_bytes as f32 / self.total_bytes as f32
157 }
158 }
159}
160
161#[derive(Debug, Clone, PartialEq, Eq)]
163pub enum BackgroundActivity {
164 Idle,
165 Loading(FileProgress),
166 Saving(FileProgress),
167}
168
169impl BackgroundActivity {
170 pub fn is_idle(&self) -> bool {
172 matches!(self, Self::Idle)
173 }
174
175 pub fn loading_state(&self) -> Option<&FileProgress> {
177 match self {
178 Self::Loading(progress) => Some(progress),
179 Self::Idle | Self::Saving(_) => None,
180 }
181 }
182
183 pub fn loading_phase(&self) -> Option<LoadPhase> {
185 self.loading_state().and_then(FileProgress::load_phase)
186 }
187
188 pub fn save_state(&self) -> Option<&FileProgress> {
190 match self {
191 Self::Saving(progress) => Some(progress),
192 Self::Idle | Self::Loading(_) => None,
193 }
194 }
195
196 pub fn progress(&self) -> Option<&FileProgress> {
198 match self {
199 Self::Idle => None,
200 Self::Loading(progress) | Self::Saving(progress) => Some(progress),
201 }
202 }
203}
204
205#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
208pub enum BackgroundIssueKind {
209 LoadFailed,
210 SaveFailed,
211 LoadDiscarded,
212 SaveDiscarded,
213}
214
215impl BackgroundIssueKind {
216 pub const fn is_load(self) -> bool {
218 matches!(self, Self::LoadFailed | Self::LoadDiscarded)
219 }
220
221 pub const fn is_save(self) -> bool {
223 matches!(self, Self::SaveFailed | Self::SaveDiscarded)
224 }
225
226 pub const fn is_discarded(self) -> bool {
228 matches!(self, Self::LoadDiscarded | Self::SaveDiscarded)
229 }
230}
231
232#[derive(Debug, Clone, PartialEq, Eq)]
234pub struct BackgroundIssue {
235 kind: BackgroundIssueKind,
236 path: Arc<PathBuf>,
237 message: Arc<str>,
238}
239
240impl BackgroundIssue {
241 pub(super) fn new(kind: BackgroundIssueKind, path: PathBuf, message: String) -> Self {
242 Self {
243 kind,
244 path: Arc::new(path),
245 message: Arc::from(message),
246 }
247 }
248
249 pub fn kind(&self) -> BackgroundIssueKind {
251 self.kind
252 }
253
254 pub fn path(&self) -> &Path {
256 self.path.as_path()
257 }
258
259 pub fn message(&self) -> &str {
261 self.message.as_ref()
262 }
263
264 pub fn is_load(&self) -> bool {
266 self.kind.is_load()
267 }
268
269 pub fn is_save(&self) -> bool {
271 self.kind.is_save()
272 }
273
274 pub fn is_discarded(&self) -> bool {
276 self.kind.is_discarded()
277 }
278}
279
280#[derive(Debug, Clone, PartialEq, Eq)]
282pub struct DocumentSessionStatus {
283 generation: u64,
284 document: DocumentStatus,
285 background_activity: BackgroundActivity,
286 background_issue: Option<BackgroundIssue>,
287 close_pending: bool,
288}
289
290impl DocumentSessionStatus {
291 pub(super) fn new(
293 generation: u64,
294 document: DocumentStatus,
295 background_activity: BackgroundActivity,
296 background_issue: Option<BackgroundIssue>,
297 close_pending: bool,
298 ) -> Self {
299 Self {
300 generation,
301 document,
302 background_activity,
303 background_issue,
304 close_pending,
305 }
306 }
307
308 pub fn generation(&self) -> u64 {
310 self.generation
311 }
312
313 pub fn document(&self) -> &DocumentStatus {
315 &self.document
316 }
317
318 pub fn path(&self) -> Option<&Path> {
320 self.document.path()
321 }
322
323 pub fn is_dirty(&self) -> bool {
325 self.document.is_dirty()
326 }
327
328 pub fn line_count(&self) -> LineCount {
330 self.document.line_count()
331 }
332
333 pub fn display_line_count(&self) -> usize {
335 self.document.display_line_count()
336 }
337
338 pub fn exact_line_count(&self) -> Option<usize> {
340 self.document.exact_line_count()
341 }
342
343 pub fn is_line_count_exact(&self) -> bool {
345 self.document.is_line_count_exact()
346 }
347
348 pub fn file_len(&self) -> usize {
350 self.document.file_len()
351 }
352
353 pub fn line_ending(&self) -> LineEnding {
355 self.document.line_ending()
356 }
357
358 pub fn backing(&self) -> DocumentBacking {
360 self.document.backing()
361 }
362
363 pub fn has_edit_buffer(&self) -> bool {
365 self.document.has_edit_buffer()
366 }
367
368 pub fn has_rope(&self) -> bool {
370 self.document.has_rope()
371 }
372
373 pub fn has_piece_table(&self) -> bool {
375 self.document.has_piece_table()
376 }
377
378 pub fn indexing_state(&self) -> Option<ByteProgress> {
380 self.document.indexing_state()
381 }
382
383 pub fn is_indexing(&self) -> bool {
385 self.document.is_indexing()
386 }
387
388 pub fn background_activity(&self) -> &BackgroundActivity {
390 &self.background_activity
391 }
392
393 pub fn background_issue(&self) -> Option<&BackgroundIssue> {
395 self.background_issue.as_ref()
396 }
397
398 pub fn close_pending(&self) -> bool {
401 self.close_pending
402 }
403
404 pub fn loading_state(&self) -> Option<&FileProgress> {
406 self.background_activity.loading_state()
407 }
408
409 pub fn loading_phase(&self) -> Option<LoadPhase> {
411 self.background_activity.loading_phase()
412 }
413
414 pub fn save_state(&self) -> Option<&FileProgress> {
416 self.background_activity.save_state()
417 }
418
419 pub fn is_busy(&self) -> bool {
421 !matches!(self.background_activity, BackgroundActivity::Idle)
422 }
423
424 pub fn is_loading(&self) -> bool {
426 matches!(self.background_activity, BackgroundActivity::Loading(_))
427 }
428
429 pub fn is_saving(&self) -> bool {
431 matches!(self.background_activity, BackgroundActivity::Saving(_))
432 }
433}
434
435#[derive(Debug, Clone, PartialEq, Eq)]
437pub struct EditorTabStatus {
438 id: u64,
439 session: DocumentSessionStatus,
440 cursor: CursorPosition,
441 pinned: bool,
442}
443
444impl EditorTabStatus {
445 pub(super) fn new(
447 id: u64,
448 session: DocumentSessionStatus,
449 cursor: CursorPosition,
450 pinned: bool,
451 ) -> Self {
452 Self {
453 id,
454 session,
455 cursor,
456 pinned,
457 }
458 }
459
460 pub fn id(&self) -> u64 {
462 self.id
463 }
464
465 pub fn session(&self) -> &DocumentSessionStatus {
467 &self.session
468 }
469
470 pub fn generation(&self) -> u64 {
472 self.session.generation()
473 }
474
475 pub fn document(&self) -> &DocumentStatus {
477 self.session.document()
478 }
479
480 pub fn path(&self) -> Option<&Path> {
482 self.session.path()
483 }
484
485 pub fn is_dirty(&self) -> bool {
487 self.session.is_dirty()
488 }
489
490 pub fn line_count(&self) -> LineCount {
492 self.session.line_count()
493 }
494
495 pub fn display_line_count(&self) -> usize {
497 self.session.display_line_count()
498 }
499
500 pub fn exact_line_count(&self) -> Option<usize> {
502 self.session.exact_line_count()
503 }
504
505 pub fn is_line_count_exact(&self) -> bool {
507 self.session.is_line_count_exact()
508 }
509
510 pub fn file_len(&self) -> usize {
512 self.session.file_len()
513 }
514
515 pub fn line_ending(&self) -> LineEnding {
517 self.session.line_ending()
518 }
519
520 pub fn backing(&self) -> DocumentBacking {
522 self.session.backing()
523 }
524
525 pub fn has_edit_buffer(&self) -> bool {
527 self.session.has_edit_buffer()
528 }
529
530 pub fn has_rope(&self) -> bool {
532 self.session.has_rope()
533 }
534
535 pub fn has_piece_table(&self) -> bool {
537 self.session.has_piece_table()
538 }
539
540 pub fn background_activity(&self) -> &BackgroundActivity {
542 self.session.background_activity()
543 }
544
545 pub fn background_issue(&self) -> Option<&BackgroundIssue> {
547 self.session.background_issue()
548 }
549
550 pub fn close_pending(&self) -> bool {
553 self.session.close_pending()
554 }
555
556 pub fn loading_state(&self) -> Option<&FileProgress> {
558 self.session.loading_state()
559 }
560
561 pub fn loading_phase(&self) -> Option<LoadPhase> {
563 self.session.loading_phase()
564 }
565
566 pub fn save_state(&self) -> Option<&FileProgress> {
568 self.session.save_state()
569 }
570
571 pub fn is_busy(&self) -> bool {
573 self.session.is_busy()
574 }
575
576 pub fn is_loading(&self) -> bool {
578 self.session.is_loading()
579 }
580
581 pub fn is_saving(&self) -> bool {
583 self.session.is_saving()
584 }
585
586 pub fn cursor(&self) -> CursorPosition {
588 self.cursor
589 }
590
591 pub fn is_pinned(&self) -> bool {
593 self.pinned
594 }
595}
596
597#[derive(Debug)]
599pub enum SaveError {
600 NoPath,
602 InProgress,
604 Io(DocumentError),
606}
607
608impl std::fmt::Display for SaveError {
609 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
610 match self {
611 Self::NoPath => write!(f, "no path set for current document"),
612 Self::InProgress => write!(f, "save already in progress"),
613 Self::Io(err) => write!(f, "{err}"),
614 }
615 }
616}
617
618impl std::error::Error for SaveError {
619 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
620 match self {
621 Self::Io(err) => Some(err),
622 Self::NoPath | Self::InProgress => None,
623 }
624 }
625}