1use std::ops::{Add, AddAssign, Sub};
2
3use crate::{ToOffset, ToPoint};
4
5use super::fold_map::{FoldEdit, FoldOffset, FoldSnapshot};
6use gpui::fonts::HighlightStyle;
7use language::{Bias, Edit, Patch, Rope};
8use parking_lot::Mutex;
9
10pub type SuggestionEdit = Edit<SuggestionOffset>;
11
12#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
13pub struct SuggestionOffset(pub usize);
14
15impl Add for SuggestionOffset {
16 type Output = Self;
17
18 fn add(self, rhs: Self) -> Self::Output {
19 Self(self.0 + rhs.0)
20 }
21}
22
23impl Sub for SuggestionOffset {
24 type Output = Self;
25
26 fn sub(self, rhs: Self) -> Self::Output {
27 Self(self.0 - rhs.0)
28 }
29}
30
31impl AddAssign for SuggestionOffset {
32 fn add_assign(&mut self, rhs: Self) {
33 self.0 += rhs.0;
34 }
35}
36
37#[derive(Clone)]
38pub struct Suggestion<T> {
39 position: T,
40 text: Rope,
41}
42
43pub struct SuggestionMap(Mutex<SuggestionSnapshot>);
44
45impl SuggestionMap {
46 pub fn replace<T>(
47 &mut self,
48 new_suggestion: Option<Suggestion<T>>,
49 fold_snapshot: FoldSnapshot,
50 fold_edits: Vec<FoldEdit>,
51 ) -> (SuggestionSnapshot, Vec<SuggestionEdit>)
52 where
53 T: ToPoint,
54 {
55 let new_suggestion = new_suggestion.map(|new_suggestion| {
56 let buffer_point = new_suggestion
57 .position
58 .to_point(fold_snapshot.buffer_snapshot());
59 let fold_point = fold_snapshot.to_fold_point(buffer_point, Bias::Left);
60 let fold_offset = fold_point.to_offset(&fold_snapshot);
61 Suggestion {
62 position: fold_offset,
63 text: new_suggestion.text,
64 }
65 });
66
67 let (_, edits) = self.sync(fold_snapshot, fold_edits);
68 let mut snapshot = self.0.lock();
69
70 let old = if let Some(suggestion) = snapshot.suggestion.take() {
71 SuggestionOffset(suggestion.position.0)
72 ..SuggestionOffset(suggestion.position.0 + suggestion.text.len())
73 } else if let Some(new_suggestion) = new_suggestion.as_ref() {
74 SuggestionOffset(new_suggestion.position.0)..SuggestionOffset(new_suggestion.position.0)
75 } else {
76 return (snapshot.clone(), edits);
77 };
78
79 let new = if let Some(suggestion) = new_suggestion.as_ref() {
80 SuggestionOffset(suggestion.position.0)
81 ..SuggestionOffset(suggestion.position.0 + suggestion.text.len())
82 } else {
83 old.start..old.start
84 };
85
86 let patch = Patch::new(edits).compose([SuggestionEdit { old, new }]);
87 snapshot.suggestion = new_suggestion;
88 (snapshot.clone(), patch.into_inner())
89 }
90
91 pub fn sync(
92 &self,
93 fold_snapshot: FoldSnapshot,
94 fold_edits: Vec<FoldEdit>,
95 ) -> (SuggestionSnapshot, Vec<SuggestionEdit>) {
96 let mut snapshot = self.0.lock();
97 let mut suggestion_edits = Vec::new();
98
99 let mut suggestion_old_len = 0;
100 let mut suggestion_new_len = 0;
101 for fold_edit in fold_edits {
102 let start = fold_edit.new.start;
103 let end = FoldOffset(start.0 + fold_edit.old_len().0);
104 if let Some(suggestion) = snapshot.suggestion.as_mut() {
105 if end < suggestion.position {
106 suggestion.position.0 += fold_edit.new_len().0;
107 suggestion.position.0 -= fold_edit.old_len().0;
108 } else if start > suggestion.position {
109 suggestion_old_len = suggestion.text.len();
110 suggestion_new_len = suggestion_old_len;
111 } else {
112 suggestion_old_len = suggestion.text.len();
113 snapshot.suggestion.take();
114 suggestion_edits.push(SuggestionEdit {
115 old: SuggestionOffset(fold_edit.old.start.0)
116 ..SuggestionOffset(fold_edit.old.end.0 + suggestion_old_len),
117 new: SuggestionOffset(fold_edit.new.start.0)
118 ..SuggestionOffset(fold_edit.new.end.0),
119 });
120 continue;
121 }
122 }
123
124 suggestion_edits.push(SuggestionEdit {
125 old: SuggestionOffset(fold_edit.old.start.0 + suggestion_old_len)
126 ..SuggestionOffset(fold_edit.old.end.0 + suggestion_old_len),
127 new: SuggestionOffset(fold_edit.new.start.0 + suggestion_new_len)
128 ..SuggestionOffset(fold_edit.new.end.0 + suggestion_new_len),
129 });
130 }
131 snapshot.folds_snapshot = fold_snapshot;
132
133 (snapshot.clone(), suggestion_edits)
134 }
135}
136
137#[derive(Clone)]
138pub struct SuggestionSnapshot {
139 folds_snapshot: FoldSnapshot,
140 suggestion: Option<Suggestion<FoldOffset>>,
141}