1use std::{
2 cell::{Cell, RefCell},
3 collections::HashMap,
4 ops::Range,
5 rc::Rc,
6};
7
8use floem_editor_core::{
9 buffer::{Buffer, InvalLines, rope_text::RopeText},
10 command::EditCommand,
11 cursor::Cursor,
12 editor::{Action, EditConf, EditType},
13 mode::{Mode, MotionMode},
14 register::Register,
15 selection::Selection,
16 word::WordCursor,
17};
18use floem_reactive::{
19 RwSignal, Scope, SignalGet, SignalTrack, SignalUpdate, SignalWith, create_effect,
20};
21use lapce_xi_rope::{Rope, RopeDelta};
22use smallvec::{SmallVec, smallvec};
23use ui_events::keyboard::Modifiers;
24
25use super::{
26 Editor, EditorStyle,
27 actions::{CommonAction, handle_command_default},
28 command::{Command, CommandExecuted},
29 id::EditorId,
30 phantom_text::{PhantomText, PhantomTextKind, PhantomTextLine},
31 text::{Document, DocumentPhantom, PreeditData, SystemClipboard},
32};
33
34type PreCommandFn = Box<dyn Fn(PreCommand) -> CommandExecuted>;
35#[derive(Debug, Clone)]
36pub struct PreCommand<'a> {
37 pub editor: &'a Editor,
38 pub cmd: &'a Command,
39 pub count: Option<usize>,
40 pub mods: Modifiers,
41}
42
43type OnUpdateFn = Box<dyn Fn(OnUpdate)>;
44#[derive(Debug, Clone)]
45pub struct OnUpdate<'a> {
46 pub editor: Option<&'a Editor>,
48 deltas: &'a [(Rope, RopeDelta, InvalLines)],
49}
50impl<'a> OnUpdate<'a> {
51 pub fn deltas(&self) -> impl Iterator<Item = &'a RopeDelta> {
52 self.deltas.iter().map(|(_, delta, _)| delta)
53 }
54}
55
56#[derive(Clone)]
60pub struct TextDocument {
61 buffer: RwSignal<Buffer>,
62 cache_rev: RwSignal<u64>,
63 preedit: PreeditData,
64
65 pub keep_indent: Cell<bool>,
67 pub auto_indent: Cell<bool>,
69
70 pub placeholders: RwSignal<HashMap<EditorId, String>>,
71
72 pre_command: Rc<RefCell<HashMap<EditorId, SmallVec<[PreCommandFn; 1]>>>>,
76
77 on_updates: Rc<RefCell<SmallVec<[OnUpdateFn; 1]>>>,
78}
79impl TextDocument {
80 pub fn new(cx: Scope, text: impl Into<Rope>) -> TextDocument {
81 let text = text.into();
82 let buffer = Buffer::new(text);
83 let preedit = PreeditData {
84 preedit: cx.create_rw_signal(None),
85 };
86 let cache_rev = cx.create_rw_signal(0);
87
88 let placeholders = cx.create_rw_signal(HashMap::new());
89
90 create_effect(move |_| {
92 placeholders.track();
93 cache_rev.try_update(|cache_rev| {
94 *cache_rev += 1;
95 });
96 });
97
98 TextDocument {
99 buffer: cx.create_rw_signal(buffer),
100 cache_rev,
101 preedit,
102 keep_indent: Cell::new(true),
103 auto_indent: Cell::new(false),
104 placeholders,
105 pre_command: Rc::new(RefCell::new(HashMap::new())),
106 on_updates: Rc::new(RefCell::new(SmallVec::new())),
107 }
108 }
109
110 fn update_cache_rev(&self) {
111 self.cache_rev.try_update(|cache_rev| {
112 *cache_rev += 1;
113 });
114 }
115
116 fn on_update(&self, ed: Option<&Editor>, deltas: &[(Rope, RopeDelta, InvalLines)]) {
117 let on_updates = self.on_updates.borrow();
118 let data = OnUpdate { editor: ed, deltas };
119 for on_update in on_updates.iter() {
120 on_update(data.clone());
121 }
122 }
123
124 pub fn add_pre_command(
125 &self,
126 id: EditorId,
127 pre_command: impl Fn(PreCommand) -> CommandExecuted + 'static,
128 ) {
129 let pre_command: PreCommandFn = Box::new(pre_command);
130 self.pre_command
131 .borrow_mut()
132 .insert(id, smallvec![pre_command]);
133 }
134
135 pub fn clear_pre_commands(&self) {
136 self.pre_command.borrow_mut().clear();
137 }
138
139 pub fn add_on_update(&self, on_update: impl Fn(OnUpdate) + 'static) {
140 self.on_updates.borrow_mut().push(Box::new(on_update));
141 }
142
143 pub fn clear_on_updates(&self) {
144 self.on_updates.borrow_mut().clear();
145 }
146
147 pub fn add_placeholder(&self, editor_id: EditorId, placeholder: String) {
148 self.placeholders.update(|placeholders| {
149 placeholders.insert(editor_id, placeholder);
150 });
151 }
152
153 fn placeholder(&self, editor_id: EditorId) -> Option<String> {
154 self.placeholders
155 .with_untracked(|placeholders| placeholders.get(&editor_id).cloned())
156 }
157}
158impl Document for TextDocument {
159 fn text(&self) -> Rope {
160 self.buffer.with_untracked(|buffer| buffer.text().clone())
161 }
162
163 fn cache_rev(&self) -> RwSignal<u64> {
164 self.cache_rev
165 }
166
167 fn preedit(&self) -> PreeditData {
168 self.preedit.clone()
169 }
170
171 fn run_command(
172 &self,
173 ed: &Editor,
174 cmd: &Command,
175 count: Option<usize>,
176 modifiers: Modifiers,
177 ) -> CommandExecuted {
178 let pre_commands = self.pre_command.borrow();
179 let pre_commands = pre_commands.get(&ed.id());
180 let pre_commands = pre_commands.iter().flat_map(|c| c.iter());
181 let data = PreCommand {
182 editor: ed,
183 cmd,
184 count,
185 mods: modifiers,
186 };
187
188 for pre_command in pre_commands {
189 if pre_command(data.clone()) == CommandExecuted::Yes {
190 return CommandExecuted::Yes;
191 }
192 }
193
194 handle_command_default(ed, self, cmd, count, modifiers)
195 }
196
197 fn receive_char(&self, ed: &Editor, c: &str) {
198 if ed.read_only.get_untracked() {
199 return;
200 }
201
202 let mode = ed.cursor.with_untracked(|c| c.get_mode());
203 if mode == Mode::Insert {
204 let mut cursor = ed.cursor.get_untracked();
205 {
206 let old_cursor_mode = cursor.mode.clone();
207 let deltas = self
208 .buffer
209 .try_update(|buffer| {
210 Action::insert(
211 &mut cursor,
212 buffer,
213 c,
214 &|_, c, offset| {
215 WordCursor::new(&self.text(), offset).previous_unmatched(c)
216 },
217 false,
219 false,
220 )
221 })
222 .unwrap();
223 self.buffer.update(|buffer| {
224 buffer.set_cursor_before(old_cursor_mode);
225 buffer.set_cursor_after(cursor.mode.clone());
226 });
227 self.update_cache_rev();
229 self.on_update(Some(ed), &deltas);
230 }
231 ed.cursor.set(cursor);
232 }
233 }
234
235 fn edit(&self, iter: &mut dyn Iterator<Item = (Selection, &str)>, edit_type: EditType) {
236 let deltas = self
237 .buffer
238 .try_update(|buffer| buffer.edit(iter, edit_type));
239 let deltas = deltas.map(|x| [x]);
240 let deltas = deltas.as_ref().map(|x| x as &[_]).unwrap_or(&[]);
241
242 self.update_cache_rev();
243 self.on_update(None, deltas);
244 }
245}
246impl DocumentPhantom for TextDocument {
247 fn phantom_text(&self, edid: EditorId, styling: &EditorStyle, line: usize) -> PhantomTextLine {
248 let mut text = SmallVec::new();
249
250 if self.buffer.with_untracked(Buffer::is_empty)
251 && self.preedit.preedit.with_untracked(|p| p.is_none())
252 {
253 if let Some(placeholder) = self.placeholder(edid) {
254 text.push(PhantomText {
255 kind: PhantomTextKind::Placeholder,
256 col: 0,
257 affinity: None,
258 text: placeholder,
259 font_size: None,
260 fg: Some(styling.placeholder_color()),
261 bg: None,
262 under_line: None,
263 });
264 }
265 }
266
267 if let Some(preedit) = self.preedit_phantom(Some(styling.preedit_underline_color()), line) {
268 text.push(preedit);
269 }
270
271 PhantomTextLine { text }
272 }
273
274 fn has_multiline_phantom(&self, edid: EditorId, _styling: &EditorStyle) -> bool {
275 if !self.buffer.with_untracked(Buffer::is_empty) {
276 return false;
277 }
278
279 let placeholder_ml = self.placeholders.with_untracked(|placeholder| {
280 let Some(placeholder) = placeholder.get(&edid) else {
281 return false;
282 };
283
284 placeholder.lines().count() > 1
285 });
286
287 if placeholder_ml {
288 return true;
289 }
290
291 self.preedit.preedit.with_untracked(|preedit| {
292 let Some(preedit) = preedit else {
293 return false;
294 };
295
296 preedit.text.lines().count() > 1
297 })
298 }
299}
300impl CommonAction for TextDocument {
301 fn exec_motion_mode(
302 &self,
303 _ed: &Editor,
304 cursor: &mut Cursor,
305 motion_mode: MotionMode,
306 range: Range<usize>,
307 is_vertical: bool,
308 register: &mut Register,
309 ) {
310 self.buffer.try_update(move |buffer| {
311 Action::execute_motion_mode(cursor, buffer, motion_mode, range, is_vertical, register)
312 });
313 }
314
315 fn do_edit(
316 &self,
317 ed: &Editor,
318 cursor: &mut Cursor,
319 cmd: &EditCommand,
320 modal: bool,
321 register: &mut Register,
322 smart_tab: bool,
323 ) -> bool {
324 if ed.read_only.get_untracked() && !cmd.not_changing_buffer() {
325 return false;
326 }
327
328 let mut clipboard = SystemClipboard::new();
329 let old_cursor = cursor.mode.clone();
330 let deltas = self
332 .buffer
333 .try_update(|buffer| {
334 Action::do_edit(
335 cursor,
336 buffer,
337 cmd,
338 &mut clipboard,
339 register,
340 EditConf {
341 modal,
342 comment_token: "",
343 smart_tab,
344 keep_indent: self.keep_indent.get(),
345 auto_indent: self.auto_indent.get(),
346 },
347 )
348 })
349 .unwrap();
350
351 if !deltas.is_empty() {
352 self.buffer.update(|buffer| {
353 buffer.set_cursor_before(old_cursor);
354 buffer.set_cursor_after(cursor.mode.clone());
355 });
356
357 self.update_cache_rev();
358 self.on_update(Some(ed), &deltas);
359 }
360
361 !deltas.is_empty()
362 }
363}
364
365impl std::fmt::Debug for TextDocument {
366 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
367 let mut s = f.debug_struct("TextDocument");
368 s.field("text", &self.text());
369 s.finish()
370 }
371}