1use crate::{
2 core::{
3 cons::Cons,
4 env::Env,
5 error::{Type, TypeError},
6 gc::{Context, Rt, Slot},
7 object::{Gc, ListType, NIL, Object, ObjectType, WithLifetime},
8 },
9 fns::eq,
10 intervals::textget,
11};
12use anyhow::{Result, anyhow, bail};
13use rune_core::macros::list;
14use rune_macros::defun;
15
16use crate::core::object::BufferData;
17
18#[allow(dead_code)]
19#[derive(Clone, Copy, PartialEq, Eq, Debug)]
20pub enum PropertySetType {
21 Replace,
22 Prepend,
23 Append,
24}
25
26pub fn add_properties<'ob>(
38 plist: Object<'ob>,
39 mut obj_i: Object<'ob>,
40 _set_type: PropertySetType,
41 _destructive: bool,
42 cx: &'ob Context,
43) -> Result<Object<'ob>> {
44 let Ok(plist) = Gc::<ListType>::try_from(plist) else { return Ok(obj_i) };
46 let Ok(plist_i) = Gc::<ListType>::try_from(obj_i) else { return Ok(obj_i) };
47 let mut iter = plist.elements();
48 while let Some(key1) = iter.next() {
50 let key1 = key1?;
51 let Some(val1) = iter.next() else { return Ok(obj_i) };
52 let mut found = false;
53
54 let mut iter_i = plist_i.conses();
55 while let Some(key2_cons) = iter_i.next() {
57 let Some(val2_cons) = iter_i.next() else { return Ok(obj_i) };
58 if eq(key1, key2_cons?.car()) {
59 val2_cons?.set_car(val1?)?;
61 found = true;
62 break;
63 }
64 }
65 if !found {
67 let pl = plist_i.untag();
68 let new_cons = Cons::new(key1, Cons::new(val1?, pl, cx), cx);
69 obj_i = new_cons.into();
70 }
71 }
72 Ok(obj_i)
73}
74
75fn modify_buffer_data<'ob, T>(
88 object: Object<'ob>,
89 env: &'ob mut Rt<Env>,
90 func: impl FnOnce(&mut BufferData) -> Result<T>,
91) -> Result<T> {
92 if object.is_nil() {
93 let data = env.current_buffer.get_mut();
94 func(data)
95 } else {
96 let current_buf = env.current_buffer.buf_ref;
97 if let Some(b) = object.buffer() {
98 if b == current_buf {
99 let data = env.current_buffer.get_mut();
100 func(data)
101 } else {
102 let mut open_buf = b.lock()?;
103 func(open_buf.get_mut())
104 }
105 } else {
106 Err(anyhow!(TypeError::new(Type::BufferOrString, object.untag())))
107 }
108 }
109}
110
111#[defun]
125pub fn text_properties_at<'ob>(
126 position: usize,
127 object: Object<'ob>,
128 env: &'ob mut Rt<Env>,
129) -> Result<Object<'ob>> {
130 let tree = if eq(object, NIL) {
131 &env.current_buffer.get().textprops
132 } else {
133 let obj = object.untag();
134 match obj {
135 ObjectType::Buffer(buf) => &buf.lock().unwrap().textprops,
136 ObjectType::String(_str) => {
137 todo!()
138 }
139 _ => {
140 bail!(TypeError::new(Type::BufferOrString, obj))
141 }
142 }
143 };
144 let a = tree.find(position).map(|a| *a.val).unwrap_or(NIL);
145 Ok(unsafe { a.with_lifetime() })
146}
147
148#[defun]
157pub fn get_text_property<'ob>(
158 position: usize,
159 prop: Object<'ob>,
160 object: Object<'ob>,
161 env: &'ob mut Rt<Env>,
162) -> Result<Object<'ob>> {
163 let props = text_properties_at(position, object, env)?;
164 textget(props, prop)
169}
170
171#[defun]
172#[allow(unused)]
173pub fn get_char_property_and_overlay<'ob>(
174 position: usize,
175 prop: Object<'ob>,
176 object: Object<'ob>,
177 env: &'ob mut Rt<Env>,
178) -> Result<Object<'ob>> {
179 todo!()
180}
181
182#[defun]
183#[allow(unused)]
184pub fn get_char_property<'ob>(
185 position: usize,
186 prop: Object<'ob>,
187 object: Object<'ob>,
188 env: &'ob mut Rt<Env>,
189) -> Result<Object<'ob>> {
190 todo!()
191}
192
193#[defun]
202pub fn put_text_property<'ob>(
203 start: usize,
204 end: usize,
205 property: Object<'ob>,
206 value: Object<'ob>,
207 object: Object<'ob>,
208 env: &mut Rt<Env>,
209 cx: &'ob Context,
210) -> Result<()> {
211 let prop = list!(property, value; cx);
212 let prop = Slot::new(prop);
213 modify_buffer_data(object, env, |data| {
214 let tree = &mut data.textprops_with_lifetime();
215 tree.insert(start, end, prop, cx);
216 Ok(())
217 })
218}
219#[defun]
232pub fn next_property_change<'ob>(
233 position: usize,
234 object: Object<'ob>,
235 limit: Option<usize>,
236 env: &'ob mut Rt<Env>,
237 cx: &'ob Context,
238) -> Result<Object<'ob>> {
239 modify_buffer_data(object, env, |data| -> Result<Object<'ob>> {
240 let point_max = data.text.len_chars() + 1;
241 let end = limit.unwrap_or(point_max);
242 let tree = data.textprops_with_lifetime();
243 tree.clean();
245 let prop = tree.tree.find_intersect_min(position..end);
246
247 match prop {
248 Some(p) => {
249 let range = p.key;
250 if range.start <= position {
251 if range.end < end { Ok(cx.add(range.end)) } else { Ok(cx.add(limit)) }
253 } else {
254 Ok(cx.add(range.start))
256 }
257
258 }
260 None => {
261 let result = match limit {
262 Some(n) => cx.add(n),
263 None => NIL,
264 };
265 Ok(result)
266 }
267 }
268 })
269}
270
271#[defun]
285pub fn next_single_property_change<'ob>(
286 position: usize,
287 prop: Object<'ob>,
288 object: Object<'ob>,
289 limit: Option<usize>,
290 env: &'ob mut Rt<Env>,
291 cx: &'ob Context,
292) -> Result<Object<'ob>> {
293 modify_buffer_data(object, env, |data| -> Result<Object<'ob>> {
294 let point_max = data.text.len_chars() + 1;
295 let end = limit.unwrap_or(point_max);
296 let tree = data.textprops_with_lifetime();
297 tree.clean();
299 let iter = tree.iter(position, end);
300
301 let mut val = None;
302 for (interval, props) in iter {
303 let text_prop = textget(props, prop)?;
304 match val {
305 Some(v) => {
306 if eq(v, text_prop) {
307 continue;
308 } else {
309 return Ok(cx.add(interval.start));
310 }
311 }
312 None => val = Some(text_prop),
313 }
314 }
315 Ok(cx.add(limit))
316 })
317}
318
319#[defun]
332pub fn previous_property_change<'ob>(
333 position: usize,
334 object: Object<'ob>,
335 limit: Option<usize>,
336 env: &'ob mut Rt<Env>,
337 cx: &'ob Context,
338) -> Result<Object<'ob>> {
339 modify_buffer_data(object, env, |data| -> Result<Object<'ob>> {
340 let point_min = 1;
341 let start = limit.unwrap_or(point_min);
342 let end = position;
343 let tree = data.textprops_with_lifetime();
344 tree.clean();
346 let prop = tree.tree.find_intersect_max(start..end);
347
348 match prop {
349 Some(p) => {
350 let interval = p.key;
351 if interval.end >= position {
352 if interval.start > start {
354 Ok(cx.add(interval.start))
355 } else {
356 Ok(cx.add(limit))
357 }
358 } else {
359 Ok(cx.add(interval.end))
361 }
362
363 }
365 None => {
366 let result = match limit {
367 Some(n) => cx.add(n),
368 None => NIL,
369 };
370 Ok(result)
371 }
372 }
373 })
374}
375
376#[defun]
390pub fn previous_single_property_change<'ob>(
391 position: usize,
392 prop: Object<'ob>,
393 object: Object<'ob>,
394 limit: Option<usize>,
395 env: &'ob mut Rt<Env>,
396 cx: &'ob Context,
397) -> Result<Object<'ob>> {
398 modify_buffer_data(object, env, |data| -> Result<Object<'ob>> {
399 let point_min = 1;
400 let start = limit.unwrap_or(point_min);
401 let tree = data.textprops_with_lifetime();
402 tree.clean();
404 let iter = tree.iter_reverse(start, position);
405
406 let mut val = None;
407 for (interval, props) in iter {
408 let text_prop = textget(props, prop)?;
409 match val {
410 Some(v) => {
411 if eq(v, text_prop) {
412 continue;
413 } else {
414 return Ok(cx.add(interval.start));
415 }
416 }
417 None => val = Some(text_prop),
418 }
419 }
420 Ok(cx.add(limit))
421 })
422}
423
424#[defun]
432pub fn set_text_properties<'ob>(
433 start: usize,
434 end: usize,
435 properties: Object<'ob>,
436 object: Object<'ob>,
437 env: &mut Rt<Env>,
438) -> Result<()> {
439 modify_buffer_data(object, env, |data| -> Result<()> {
440 let tree = data.textprops_with_lifetime();
441 tree.set_properties(start, end, properties);
442 Ok(())
443 })
444}
445
446#[defun]
457pub fn remove_text_properties<'ob>(
458 start: usize,
459 end: usize,
460 properties: Object<'ob>,
461 object: Object<'ob>,
462 env: &mut Rt<Env>,
463 cx: &'ob Context,
464) -> Result<()> {
465 modify_buffer_data(object, env, |data| -> Result<()> {
466 let tree = data.textprops_with_lifetime();
467 tree.delete(start, end, list![properties; cx])
468 })
469}
470
471#[defun]
478pub fn remove_list_of_text_properties<'ob>(
479 start: usize,
480 end: usize,
481 list_of_properties: Object<'ob>,
482 object: Object<'ob>,
483 env: &mut Rt<Env>,
484) -> Result<()> {
485 modify_buffer_data(object, env, |data| -> Result<()> {
486 let tree = data.textprops_with_lifetime();
487 tree.delete(start, end, list_of_properties)
488 })
489}
490
491#[defun]
498pub fn text_properties_any<'ob>(
499 start: usize,
500 end: usize,
501 property: Object<'ob>,
502 value: Object<'ob>,
503 object: Object<'ob>,
504 env: &mut Rt<Env>,
505 cx: &'ob Context,
506) -> Result<Object<'ob>> {
507 modify_buffer_data(object, env, |data| -> Result<Object<'ob>> {
508 let tree = data.textprops_with_lifetime();
509 let iter = tree.iter(start, end);
510 for (interval, props) in iter {
511 let val = textget(props, property)?;
512 if !eq(val, value) {
513 return Ok(cx.add(interval.start));
514 }
515 }
516 Ok(NIL)
517 })
518}
519
520#[defun]
527pub fn text_properties_not_all<'ob>(
528 start: usize,
529 end: usize,
530 property: Object<'ob>,
531 value: Object<'ob>,
532 object: Object<'ob>,
533 env: &mut Rt<Env>,
534 cx: &'ob Context,
535) -> Result<Object<'ob>> {
536 modify_buffer_data(object, env, |data| -> Result<Object<'ob>> {
537 let tree = data.textprops_with_lifetime();
538 let iter = tree.iter(start, end);
539 for (interval, props) in iter {
540 let val = textget(props, property)?;
541 if eq(val, value) {
542 return Ok(cx.add(interval.start));
543 }
544 }
545 Ok(NIL)
546 })
547}
548
549#[cfg(test)]
550mod tests {
551 use crate::{
552 buffer::{BUFFERS, get_buffer_create},
553 core::{
554 env::intern,
555 gc::{Context, RootSet},
556 },
557 fns::plist_get,
558 };
559 use rune_core::macros::{list, root};
560
561 use super::*;
562
563 #[test]
564 fn test_add_properties() {
565 let roots = &RootSet::default();
566 let mut context = Context::new(roots);
567 let cx = &mut context;
568 let plist_1 = list![intern(":a", cx), 1, intern(":b", cx), 2; cx];
569 let plist_2 = list![intern(":a", cx), 4, intern(":c", cx), 5; cx];
570 let plist_1 =
571 add_properties(plist_2, plist_1, PropertySetType::Replace, false, cx).unwrap();
572 let plist_1 = dbg!(plist_1);
573 let a = plist_get(plist_1, intern(":a", cx).into()).unwrap();
574 let b = plist_get(plist_1, intern(":b", cx).into()).unwrap();
575 let c = plist_get(plist_1, intern(":c", cx).into()).unwrap();
576 assert_eq!(a, 4);
577 assert_eq!(b, 2);
578 assert_eq!(c, 5);
579 }
580
581 #[test]
582 fn test_next_property_change() -> Result<()> {
583 let roots = &RootSet::default();
584 let mut context = Context::new(roots);
585 let cx = &mut context;
586 root!(env, new(Env), cx);
587
588 let buf = get_buffer_create(cx.add("test_next_property_change"), None, cx)?;
589 if let Some(b) = buf.buffer() {
590 b.lock()
591 .unwrap()
592 .get_mut()
593 .text
594 .insert("lorem ipsum quia dolor sit amet, consectetur, adipisci velit.");
595 }
596
597 let a = intern(":a", cx);
598 let a = cx.add(a);
599
600 put_text_property(0, 5, a, cx.add(1), buf, env, cx)?;
602 put_text_property(5, 10, a, cx.add(2), buf, env, cx)?;
603 put_text_property(15, 20, a, cx.add(3), buf, env, cx)?;
604
605 let change = next_property_change(0, buf, None, env, cx)?;
607 assert_eq!(change, cx.add(5)); let change = next_property_change(5, buf, None, env, cx)?;
610 assert_eq!(change, cx.add(10)); let change = next_property_change(10, buf, None, env, cx)?;
613 assert_eq!(change, cx.add(15)); let change = next_property_change(15, buf, None, env, cx)?;
616 assert_eq!(change, cx.add(20)); let change = next_property_change(0, buf, Some(8), env, cx)?;
620 assert_eq!(change, cx.add(5)); let change = next_property_change(5, buf, Some(8), env, cx)?;
623 assert_eq!(change, cx.add(8)); let change = next_property_change(20, buf, None, env, cx)?;
627 assert!(change.is_nil()); BUFFERS.lock().unwrap().clear();
630 Ok(())
631 }
632
633 #[test]
634 fn test_next_single_property_change() -> Result<()> {
635 let roots = &RootSet::default();
636 let mut context = Context::new(roots);
637 let cx = &mut context;
638 root!(env, new(Env), cx);
639
640 let buf = get_buffer_create(cx.add("test_next_single_property_change"), None, cx)?;
641 if let ObjectType::Buffer(b) = buf.untag() {
642 b.lock()
643 .unwrap()
644 .get_mut()
645 .text
646 .insert("lorem ipsum quia dolor sit amet, consectetur, adipisci velit.");
647 }
648
649 let a = intern(":a", cx);
650 let a = cx.add(a);
651 let b = intern(":b", cx);
652 let b = cx.add(b);
653
654 put_text_property(1, 5, a, cx.add(1), buf, env, cx)?;
656 put_text_property(5, 8, a, cx.add(2), buf, env, cx)?;
657 put_text_property(10, 15, b, cx.add(4), buf, env, cx)?;
658 put_text_property(15, 20, a, cx.add(3), buf, env, cx)?;
659
660 let change = next_single_property_change(1, a, buf, None, env, cx)?;
662 assert_eq!(change, cx.add(5)); let change = next_single_property_change(5, a, buf, None, env, cx)?;
665 assert_eq!(change, cx.add(8)); let change = next_single_property_change(10, a, buf, None, env, cx)?;
668 assert_eq!(change, cx.add(15)); let change = next_single_property_change(15, a, buf, None, env, cx)?;
671 assert_eq!(change, cx.add(20)); let change = next_single_property_change(1, a, buf, Some(8), env, cx)?;
675 assert_eq!(change, cx.add(5)); let change = next_single_property_change(5, a, buf, Some(8), env, cx)?;
678 assert_eq!(change, cx.add(8)); let change = next_single_property_change(20, a, buf, None, env, cx)?;
682 assert!(change.is_nil()); let change = next_single_property_change(1, b, buf, None, env, cx)?;
686 assert_eq!(change, cx.add(10)); let change = next_single_property_change(10, b, buf, None, env, cx)?;
689 assert_eq!(change, cx.add(15)); BUFFERS.lock().unwrap().clear();
692 Ok(())
693 }
694
695 #[test]
696 fn test_remove_text_properties() -> Result<()> {
697 let roots = &RootSet::default();
698 let mut context = Context::new(roots);
699 let cx = &mut context;
700 root!(env, new(Env), cx);
701
702 let buf = get_buffer_create(cx.add("test_remove_text_properties"), None, cx)?;
703 if let ObjectType::Buffer(b) = buf.untag() {
704 b.lock().unwrap().get_mut().text.insert("test text");
705 }
706
707 let a = intern(":a", cx);
708 let a = cx.add(a);
709 let b = intern(":b", cx);
710 let b = cx.add(b);
711
712 put_text_property(1, 5, a, cx.add(1), buf, env, cx)?;
714 put_text_property(5, 10, b, cx.add(2), buf, env, cx)?;
715
716 remove_text_properties(1, 10, a, buf, env, cx)?;
718
719 let props = text_properties_at(3, buf, env)?;
721 assert!(props.is_nil());
722
723 let props = text_properties_at(5, buf, env)?;
725 let val = plist_get(props, b)?;
726 assert_eq!(val, cx.add(2));
727
728 remove_text_properties(0, 10, a, buf, env, cx)?;
730
731 BUFFERS.lock().unwrap().clear();
732 Ok(())
733 }
734
735 #[test]
736 fn test_text_properties_at() -> Result<()> {
737 let roots = &RootSet::default();
738 let mut context = Context::new(roots);
739 let cx = &mut context;
740 root!(env, new(Env), cx);
741
742 let buf = get_buffer_create(cx.add("test_text_properties_at"), None, cx)?;
743 let n = text_properties_at(0, buf, env)?;
744 assert!(n.is_nil());
745
746 let a = intern(":a", cx);
747 let a = cx.add(a);
748 put_text_property(0, 1, a, cx.add(3), buf, env, cx)?;
749 let n = text_properties_at(0, buf, env)?;
750 let val = plist_get(n, a)?;
751 assert!(eq(val, cx.add(3)));
752
753 BUFFERS.lock().unwrap().clear();
754 Ok(())
755 }
756}