/
marker.ss
113 lines (91 loc) · 3.39 KB
/
marker.ss
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
(module marker mzscheme
(require (lib "class.ss")
(lib "list.ss")
(lib "mred.ss" "mred"))
;; Implementation of emacs marks as an editor text% mixin.
;; Markers provide a position into the text%.
;; Inserts and deletes on text will automatically adjust the mark
;; so that it points at the same characters.
(define-struct marker (pos) #f)
(provide (struct marker (pos)))
(provide marker-mixin)
;; marker-mixin
;; Mixes in marker behavior into a text% editor class.
(define marker-mixin
(let ([text-interface% (class->interface text%)])
(mixin (text-interface%) (text-interface%)
(super-new)
(define boxed-markers empty)
(define/public (add-marker! pos)
(let ([new-marker (make-marker pos)])
(set! boxed-markers (cons (make-weak-box new-marker) boxed-markers))
new-marker))
(define/augment (on-insert start len)
(inner void on-insert start len)
(set! boxed-markers (map/filter-weak
(lambda (m)
(adjust-for-insert! start len m)
m)
boxed-markers)))
(define/augment (on-delete start len)
(inner void on-delete start len)
(set! boxed-markers (map/filter-weak
(lambda (m)
(adjust-for-delete! start len m)
m)
boxed-markers))))))
(define (adjust-for-delete! start length mark)
(cond
[(< start (marker-pos mark) (+ start length)) ;; overlapping case
(set-marker-pos! mark start)]
[(< start (marker-pos mark))
(set-marker-pos! mark (- (marker-pos mark) length))]))
(define (adjust-for-insert! start length mark)
(when (< start (marker-pos mark))
(set-marker-pos! mark (+ (marker-pos mark) length))))
;; map/filter-weak: (listof (weak-box-of X)) -> (listof (weak-box-of X))
;; maps a function across a list of weak boxes. Any boxes that turn
;; into dead ones will be filtered out.
(define (map/filter-weak f l)
(reverse
(foldl
(lambda (x acc)
(let ([val (weak-box-value x)])
(if val
(let ([result (f val)])
(if (eq? result val)
(cons x acc)
(cons (make-weak-box result)
acc)))
acc)))
empty
l)))
;; quick-and-dirty tests
(define (tests)
(define (check label x y)
(unless (equal? x y)
(error label "~s not equal to expected value ~s" x y))
(printf "~a ok~n" label))
(define (make-text)
(new (marker-mixin text%)))
(define (test1)
(define text (make-text))
(send text insert "ello" 0)
(let [(mark1 (send text add-marker! 1))]
(send text insert "h" 0)
(check 'test1 (marker-pos mark1) 2)))
(define (test2)
(define text (make-text))
(send text insert "hello" 0)
(let ([mark1 (send text add-marker! 1)])
(send text insert "world" 5)
(check 'test2 (marker-pos mark1) 1)))
(define (test3)
(define t (make-text))
(send t insert "hello")
(let ([mark1 (send t add-marker! 5)])
(send t delete 0 2)
(check 'test3 (marker-pos mark1) 3)))
(test1)
(test2)
(test3)))