1use std::ops::Range;
4
5use gpui::{Context, EntityInputHandler, UTF16Selection, Window};
6use markdown::{Affinity, Cursor, Selection};
7use ui::input::{composition_selection, offset_to_utf16, range_from_utf16, range_to_utf16};
8
9use crate::{
10 document::Change,
11 editor::{Editor, Reveal},
12};
13
14impl EntityInputHandler for Editor {
18 fn text_for_range(
19 &mut self,
20 range: Range<usize>,
21 adjusted: &mut Option<Range<usize>>,
22 _: &mut Window,
23 _: &mut Context<Self>,
24 ) -> Option<String> {
25 let text = self.caret_text()?;
26 let range = range_from_utf16(&text.text, range);
27 *adjusted = Some(range_to_utf16(&text.text, range.clone()));
28 Some(text.text.get(range)?.to_string())
29 }
30
31 fn selected_text_range(
32 &mut self,
33 _: bool,
34 _: &mut Window,
35 _: &mut Context<Self>,
36 ) -> Option<UTF16Selection> {
37 let (start, end) = self.selection.ordered();
41 let spans_one = start.block == end.block && start.part == end.part;
42 let head = self.selection.head;
43 let range = if spans_one {
44 start.offset..end.offset
45 } else {
46 head.offset..head.offset
47 };
48 Some(UTF16Selection {
49 reversed: spans_one && self.selection.head == start,
50 range: range_to_utf16(&self.caret_text()?.text, range),
51 })
52 }
53
54 fn marked_text_range(&self, _: &mut Window, _: &mut Context<Self>) -> Option<Range<usize>> {
55 Some(range_to_utf16(
56 &self.caret_text()?.text,
57 self.marked.clone()?,
58 ))
59 }
60
61 fn unmark_text(&mut self, _: &mut Window, _: &mut Context<Self>) {
62 self.marked = None;
63 }
64
65 fn replace_text_in_range(
66 &mut self,
67 range: Option<Range<usize>>,
68 text: &str,
69 _: &mut Window,
70 cx: &mut Context<Self>,
71 ) {
72 if range.is_none() && self.marked.is_none() && markdown::is_link(text.trim()) {
79 return self.paste_url(text.trim().to_string(), cx);
80 }
81 if let Some(range) = range
84 .and_then(|range| {
85 self.caret_text()
86 .map(|text| range_from_utf16(&text.text, range))
87 })
88 .or_else(|| self.marked.clone())
89 {
90 let at = self.cursor();
91 self.selection = Selection::new(
92 Cursor {
93 offset: range.start,
94 ..at
95 },
96 Cursor {
97 offset: range.end,
98 ..at
99 },
100 );
101 }
102 self.marked = None;
103 self.insert(text, cx);
104 }
105
106 fn replace_and_mark_text_in_range(
107 &mut self,
108 range: Option<Range<usize>>,
109 text: &str,
110 marked: Option<Range<usize>>,
111 _: &mut Window,
112 cx: &mut Context<Self>,
113 ) {
114 if let Some(range) = range
115 .and_then(|range| {
116 self.caret_text()
117 .map(|text| range_from_utf16(&text.text, range))
118 })
119 .or_else(|| self.marked.clone())
120 {
121 let at = self.cursor();
122 self.doc
123 .edit()
124 .edit_at(at, |body| body.remove(range.clone()));
125 self.place(Cursor {
126 offset: range.start,
127 ..at
128 });
129 }
130 let at = self.cursor();
134 let start = at.offset;
135 self.doc.edit().edit_at(at, |body| body.insert(start, text));
136 self.marked = (!text.is_empty()).then_some(start..start + text.len());
137 let selected = composition_selection(text, start, marked);
138 self.selection = Selection::new(
139 Cursor {
140 offset: selected.start,
141 ..at
142 },
143 Cursor {
144 offset: selected.end,
145 ..at
146 },
147 );
148 self.reveal = Some(Reveal::Nearest);
149 self.caret_moved();
150 self.publish(Change::Composing, cx);
151 cx.notify();
152 }
153
154 fn bounds_for_range(
157 &mut self,
158 range: Range<usize>,
159 _: gpui::Bounds<gpui::Pixels>,
160 _: &mut Window,
161 _: &mut Context<Self>,
162 ) -> Option<gpui::Bounds<gpui::Pixels>> {
163 let range = range_from_utf16(&self.caret_text()?.text, range);
164 let at = self.cursor();
165 let start = Cursor {
166 offset: range.start,
167 ..at
168 };
169 let (origin, line_height) = self.layouts.position(start, Affinity::Downstream)?;
170 let end = self
171 .layouts
172 .position(
173 Cursor {
174 offset: range.end,
175 ..at
176 },
177 Affinity::Upstream,
178 )
179 .map(|(point, _)| point)
180 .filter(|point| point.y == origin.y);
181 let width = end.map_or(gpui::px(0.0), |point| point.x - origin.x);
182 Some(gpui::Bounds::new(origin, gpui::size(width, line_height)))
183 }
184
185 fn character_index_for_point(
186 &mut self,
187 point: gpui::Point<gpui::Pixels>,
188 _: &mut Window,
189 _: &mut Context<Self>,
190 ) -> Option<usize> {
191 let (hit, _) = self.layouts.hit(point)?;
192 let at = self.cursor();
193 (hit.block == at.block && hit.part == at.part)
194 .then_some(offset_to_utf16(&self.caret_text()?.text, hit.offset))
195 }
196}