1;;>| Buffer Interface2;;>3;;> Procedures for creating new buffers and related accessors.45(define-record-type Line-Buffer6 (%make-buffer lines undo? undo-stack)7 line-buffer?8 (lines buffer-lines)9 (undo? buffer-undo? buffer-undo-set!)10 (undo-stack buffer-undo-stack buffer-undo-stack-set!))1112;;> Create a new, initially empty, line buffer.1314(define (make-buffer)15 (%make-buffer (flexvector) #f (make-stack)))1617;;> Returns the element at `index` in the `buffer`, starting at zero.1819(define (buffer-ref buffer index)20 (flexvector-ref (buffer-lines buffer) index))2122;;> Convert the line buffer to a list of lines. Additionally, this23;;> procedure accepts an optional `start` and `end` parameter. If24;;> these parameters are given the list only contains the elements25;;> between `start` and `end`. By default the whole buffer is converted.2627(define (buffer->list buffer . o)28 (apply flexvector->list (buffer-lines buffer) o))2930;;> Length of the buffer, i.e. amount of lines currently stored in it.3132(define (buffer-length buffer)33 (flexvector-length (buffer-lines buffer)))3435;;> Predicate which returns true if the buffer is empty.3637(define (buffer-empty? buffer)38 (zero? (buffer-length buffer)))3940(define (buffer-register-undo buffer proc)41 (when (buffer-undo? buffer)42 (stack-push (buffer-undo-stack buffer) proc)))4344;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4546;;>| Undo Stack47;;>48;;> Procedures for managing the undo stack of the line buffer.49;;> The undo stack does not support multilevel undo.50;;> That is, the last undo can itself be undone using [buffer-undo!](#buffer-undo!).5152;;> Execute the given `thunk` and make all `buffer` operations performed53;;> in thunk undoable.5455(define (buffer-with-undo buffer thunk)56 (stack-clear! (buffer-undo-stack buffer)) ;; no multi-level undo57 (buffer-undo-set! buffer #t)5859 (guard60 (eobj61 (else62 (buffer-undo-set! buffer #f)63 (raise eobj)))64 (let ((r (thunk)))65 (buffer-undo-set! buffer #f)66 r)))6768;;> Predicate to check if the undo stack is empty, returns false if it is.6970(define (buffer-has-undo? buffer)71 (not (stack-empty? (buffer-undo-stack buffer))))7273;;> Revert last operation tracked by [buffer-with-undo](#buffer-with-undo).74;;> The undo is itself reversible via [buffer-undo!](#buffer-undo!).7576(define (buffer-undo! buffer)77 (define (%buffer-undo! buffer procs)78 (buffer-with-undo buffer79 (lambda ()80 (for-each (lambda (proc)81 (proc buffer))82 procs))))8384 (let* ((stk (buffer-undo-stack buffer))85 (stksiz (stack-size stk)))86 (unless (zero? stksiz)87 (let ((procs (stack-pops stk stksiz)))88 (%buffer-undo! buffer procs)))))8990;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;9192;;>| Buffer Operations93;;>94;;> Procedures which modify the buffer content.95;;> All operations can be undone using [buffer-undo!](#buffer-undo!).9697;;> Append the given `text` to the `buffer` after the given `line` number.98;;> The special line number 0 can be used here to add lines to the99;;> beginning of the buffer.100101(define (buffer-append! buffer line text)102 (flexvector-add-all! (buffer-lines buffer) line text)103 (buffer-register-undo buffer104 (lambda (buffer)105 ;; Will add an undo procedure to the stack, thus making106 ;; the undo of the append operation itself reversible.107 (buffer-remove! buffer (inc line) (+ line (length text))))))108109;;> Removes all lines within the `buffer` at the given inclusive range110;;> range between `start` and `end`.111112(define (buffer-remove! buffer start end)113 (let* ((lines (buffer-lines buffer))114 (sline (max (dec start) 0))115 (bkvec (flexvector->list lines sline end)))116 (flexvector-remove-range! (buffer-lines buffer) sline end)117 (buffer-register-undo buffer118 (lambda (buffer)119 ;; Will add an undo procedure to the stack, thus making120 ;; the undo of the remove operation itself reversible.121 (buffer-append! buffer sline bkvec)))))122123;; The following operations are all implemented in terms of124;; buffer-append! and buffer-undo! and are therefore reversible.125126;;> Replace lines in the inclusive range between `start` and `end`127;;> with the data given by `text` which must be a list of lines128;;> (i.e. strings).129130(define (buffer-replace! buffer start end text)131 (let* ((sline (max (dec start) 0))132 (cap (- (buffer-length buffer) sline)))133 (buffer-remove! buffer start end)134 (buffer-append! buffer sline text)))135136;;> Join lines in the inclusive range between `start` and `end`137;;> into a single line by removing all newline characters within138;;> the specified range.139140(define (buffer-join! buffer start end)141 (let* ((lines (buffer-lines buffer))142 (sindex (max (dec start) 0))143 (joined (apply144 string-append145 "" (flexvector->list lines sindex end))))146 (buffer-remove! buffer start end)147 (buffer-append!148 buffer149 sindex150 (list joined))))151152;;> Move lines in the inclusive range between `start` and `end`153;;> to the destination line number `dest`. The destination *must*154;;> always be outside the specified inclusive range.155156(define (buffer-move! buffer start end dest)157 ;; Assumption: dest is always outside [start, end].158 (let* ((lines (buffer-lines buffer))159 (sindex (max (dec start) 0))160 (move (flexvector->list lines sindex end))161162 (remove! (lambda () (buffer-remove! buffer start end)))163 (append! (lambda () (buffer-append! buffer dest move))))164 (if (> dest start)165 (begin (append!) (remove!))166 (begin (remove!) (append!)))))