Give windows each their own top/point/mark marks. Basic window split/close/switch commands.

This commit is contained in:
Tony Garnock-Jones 2014-12-27 23:59:12 -05:00
parent a512da0d7b
commit 1cdf0900b6
9 changed files with 356 additions and 218 deletions

View File

@ -1,6 +1,6 @@
Make it reloadable Make it reloadable
Windows need their own top-of-window-mtype and point location The status line isn't cleared away when you C-x 2.
Preserve column on up/down better. This includes dealing with tab expansion Preserve column on up/down better. This includes dealing with tab expansion

View File

@ -6,9 +6,11 @@
(require "buffer.rkt") (require "buffer.rkt")
(require "keys.rkt") (require "keys.rkt")
(require "rope.rkt") (require "rope.rkt")
(require "window.rkt")
(provide (all-from-out "mode.rkt" (provide (all-from-out "mode.rkt"
"editor.rkt" "editor.rkt"
"buffer.rkt" "buffer.rkt"
"keys.rkt" "keys.rkt"
"rope.rkt")) "rope.rkt"
"window.rkt"))

View File

@ -1,8 +1,8 @@
#lang racket/base #lang racket/base
(provide make-buffergroup (provide (struct-out buffer-mark-type)
make-buffergroup
initialize-buffergroup! initialize-buffergroup!
main-mark-type
buffer? buffer?
make-buffer make-buffer
register-buffer! register-buffer!
@ -13,7 +13,6 @@
buffer-reorder! buffer-reorder!
buffer-next buffer-next
buffer-prev buffer-prev
buffer-pos
buffer-title buffer-title
buffer-rope buffer-rope
buffer-group buffer-group
@ -25,21 +24,23 @@
buffer-remove-mode! buffer-remove-mode!
buffer-toggle-mode! buffer-toggle-mode!
buffer-size buffer-size
buffer-move-to!
buffer-move-by!
buffer-start-of-line buffer-start-of-line
buffer-end-of-line buffer-end-of-line
buffer-move-to-start-of-line! buffer-mark-types
buffer-move-to-end-of-line! buffer-mark*
buffer-mark
buffer-mark-pos*
buffer-mark-pos
buffer-mark! buffer-mark!
buffer-clear-mark! buffer-clear-mark!
buffer-mark-pos buffer-move-mark!
buffer-move-mark-to-start-of-line!
buffer-move-mark-to-end-of-line!
buffer-region-split buffer-region-split
buffer-region buffer-region
buffer-region-update! buffer-region-update!
buffer-insert! buffer-insert!
buffer-replace-contents! buffer-replace-contents!
call-with-excursion
buffer-search buffer-search
buffer-findf buffer-findf
@ -67,14 +68,16 @@
(require "mode.rkt") (require "mode.rkt")
(require "keys.rkt") (require "keys.rkt")
(define main-mark-type (mark-type "main" 'right)) (struct buffer-mark-type (kind ;; Symbol
window-id ;; Symbol
preserve? ;; Boolean
) #:prefab)
(struct buffergroup ([members #:mutable] ;; (CircularList Buffer) (struct buffergroup ([members #:mutable] ;; (CircularList Buffer)
[editor #:mutable] ;; (Option Editor), for bidirectional editor/group linkage [editor #:mutable] ;; (Option Editor), for bidirectional editor/group linkage
) #:prefab) ) #:prefab)
(struct buffer ([rope #:mutable] (struct buffer ([rope #:mutable]
[pos #:mutable]
[title #:mutable] [title #:mutable]
[group #:mutable] ;; (Option BufferGroup) [group #:mutable] ;; (Option BufferGroup)
[modeset #:mutable] ;; ModeSet [modeset #:mutable] ;; ModeSet
@ -108,7 +111,6 @@
title ;; String title ;; String
#:initial-contents [initial-contents ""]) #:initial-contents [initial-contents ""])
(register-buffer! group (buffer (initial-contents-rope initial-contents) (register-buffer! group (buffer (initial-contents-rope initial-contents)
0
title title
#f #f
kernel-modeset))) kernel-modeset)))
@ -129,7 +131,8 @@
(define (title->buffer* group title) (define (title->buffer* group title)
(and group (and group
(circular-list-memf (lambda (b) (equal? (buffer-title b) title)) (buffergroup-members group)))) (circular-list-memf (lambda (b) (equal? (buffer-title b) title))
(buffergroup-members group))))
(define (buffer->buffer* group b) (define (buffer->buffer* group b)
(and group (and group
@ -170,8 +173,7 @@
(let* ((filename (normalize-path (simplify-path filename))) (let* ((filename (normalize-path (simplify-path filename)))
(title (filename->unique-buffer-title group filename)) (title (filename->unique-buffer-title group filename))
(b (make-buffer group title))) (b (make-buffer group title)))
(buffer-replace-contents! b (string->rope (file->string filename))) (buffer-replace-contents! b (string->rope (file->string filename)))))
(buffer-move-to! b 0)))
(define (buffer-rename! b new-title) (define (buffer-rename! b new-title)
(if (title-exists-in-group? (buffer-group b) new-title) (if (title-exists-in-group? (buffer-group b) new-title)
@ -197,8 +199,10 @@
(define g (buffer-group b)) (define g (buffer-group b))
(and g (buffergroup-editor g))) (and g (buffergroup-editor g)))
(define (buffer-column buf) (define (buffer-column buf pos-or-mtype)
(- (buffer-pos buf) (buffer-start-of-line buf))) ;; TODO: count actual columns!
(define pos (->pos buf pos-or-mtype 'buffer-column))
(- pos (buffer-start-of-line buf pos)))
(define (buffer-apply-modeset! buf modeset) (define (buffer-apply-modeset! buf modeset)
(set-buffer-modeset! buf modeset)) (set-buffer-modeset! buf modeset))
@ -213,106 +217,101 @@
(define (clamp pos buf) (define (clamp pos buf)
(max 0 (min (buffer-size buf) pos))) (max 0 (min (buffer-size buf) pos)))
(define (buffer-move-to! buf pos0)
(define pos (clamp pos0 buf))
(set-buffer-pos! buf pos)
(buffer-seek! buf pos))
(define (buffer-seek! buf pos) (define (buffer-seek! buf pos)
(buffer-lift rope-seek buf pos)) (buffer-lift rope-seek buf (clamp pos buf)))
(define (buffer-move-by! buf delta) (define (buffer-start-of-line buf pos-or-mtype)
(buffer-move-to! buf (+ (buffer-pos buf) delta))) (buffer-findf buf pos-or-mtype (lambda (ch) (equal? ch #\newline)) #:forward? #f))
(define (buffer-start-of-line buf) (define (buffer-end-of-line buf pos-or-mtype)
(buffer-findf buf (lambda (ch) (equal? ch #\newline)) #:forward? #f)) (buffer-findf buf pos-or-mtype (lambda (ch) (equal? ch #\newline)) #:forward? #t))
(define (buffer-end-of-line buf) (define (->pos buf pos-or-mtype what)
(buffer-findf buf (lambda (ch) (equal? ch #\newline)) #:forward? #t)) (clamp (if (number? pos-or-mtype)
pos-or-mtype
(buffer-mark-pos buf pos-or-mtype what))
buf))
(define (buffer-move-to-start-of-line! buf) (define (buffer-mark-types buf)
(buffer-move-to! buf (buffer-start-of-line buf))) (rope-marks (buffer-rope buf)))
(define (buffer-move-to-end-of-line! buf) (define (buffer-mark* buf mtype)
(buffer-move-to! buf (buffer-end-of-line buf))) (find-mark (buffer-rope buf) mtype))
(define (buffer-mark! buf [pos (buffer-pos buf)] #:mark-type [mtype main-mark-type] #:value [value #t]) (define (buffer-mark buf mtype [what 'buffer-mark])
(buffer-lift replace-mark buf mtype pos value)) (or (buffer-mark* buf mtype)
(error what "Mark type ~v not found; available mark types ~v" mtype (buffer-mark-types buf))))
(define (buffer-clear-mark! buf #:mark-type [mtype main-mark-type]) (define (buffer-mark-pos* buf mtype)
(define pos (find-mark-pos (buffer-rope buf) mtype)) (find-mark-pos (buffer-rope buf) mtype))
(define (buffer-mark-pos buf mtype [what 'buffer-mark-pos])
(or (buffer-mark-pos* buf mtype)
(error what "Mark type ~v not found; available mark types ~v" mtype (buffer-mark-types buf))))
(define (buffer-mark! buf mtype pos-or-mtype #:value [value #t])
(buffer-lift replace-mark buf mtype (->pos buf pos-or-mtype 'buffer-mark!) value))
(define (buffer-clear-mark! buf mtype)
(define pos (buffer-mark-pos* buf mtype))
(if pos (if pos
(buffer-lift clear-mark buf mtype pos) (buffer-lift clear-mark buf mtype pos)
buf)) buf))
(define (buffer-mark-pos buf [mtype main-mark-type]) (define (buffer-move-mark! buf mtype delta)
(find-mark-pos (buffer-rope buf) mtype)) (match-define (cons pos val) (buffer-mark buf mtype 'buffer-move-mark!))
(buffer-mark! buf mtype (+ pos delta) #:value val))
(define (buffer-region-split* buf pos mark) (define (buffer-move-mark-to-start-of-line! buf mtype)
(define lo (clamp (min pos mark) buf)) (define pos (buffer-mark-pos buf mtype 'buffer-move-mark-to-start-of-line!))
(define hi (clamp (max pos mark) buf)) (buffer-mark! buf mtype (buffer-start-of-line buf pos)))
(define (buffer-move-mark-to-end-of-line! buf mtype)
(define pos (buffer-mark-pos buf mtype 'buffer-move-mark-to-end-of-line!))
(buffer-mark! buf mtype (buffer-end-of-line buf pos)))
(define (buffer-region-split buf pm1 pm2)
(define p1 (->pos buf pm1 'buffer-region-split))
(define p2 (->pos buf pm2 'buffer-region-split))
(define lo (min p1 p2))
(define hi (max p1 p2))
(define-values (l mr) (rope-split (buffer-rope buf) lo)) (define-values (l mr) (rope-split (buffer-rope buf) lo))
(define-values (m r) (rope-split mr (- hi lo))) (define-values (m r) (rope-split mr (- hi lo)))
(values l lo m hi r)) (values l lo m hi r))
(define (buffer-region-split buf (define (buffer-region buf pm1 pm2)
#:point [pos (buffer-pos buf)] (define-values (_l _lo m _hi _r) (buffer-region-split buf pm1 pm2))
#:mark [mark (buffer-mark-pos buf)])
(buffer-region-split* buf pos mark))
(define (buffer-region buf
#:point [pos (buffer-pos buf)]
#:mark [mark (buffer-mark-pos buf)])
(define-values (_l _lo m _hi _r) (buffer-region-split* buf pos mark))
m) m)
(define (buffer-region-update! buf updater (define (transfer-marks ro rn)
#:point [pos (buffer-pos buf)] (define mtypes-to-transfer
#:mark [mark (buffer-mark-pos buf)]) (for/list ((mtype (rope-marks ro))
(define-values (l lo old-m hi r) (buffer-region-split* buf pos mark)) #:when (buffer-mark-type-preserve? (mark-type-info mtype)))
(define new-m (updater old-m)) mtype))
(for/fold [(rn rn)] [(mtype mtypes-to-transfer)]
(define pos (case (mark-type-stickiness mtype)
[(left) 0]
[(right) (rope-size rn)]))
(set-mark rn mtype pos #t)))
(define (buffer-region-update! buf pm1 pm2 updater)
(define-values (l lo old-m hi r) (buffer-region-split buf pm1 pm2))
(define new-m (transfer-marks old-m (updater old-m)))
(define delta (- (rope-size new-m) (rope-size old-m))) (define delta (- (rope-size new-m) (rope-size old-m)))
(set-buffer-rope! buf (rope-append (rope-append l new-m) r)) (set-buffer-rope! buf (rope-append (rope-append l new-m) r))
(cond buf)
[(<= lo (buffer-pos buf) hi) (buffer-move-to! buf (+ hi delta))]
[(> (buffer-pos buf) hi) (buffer-move-by! buf delta)]
[else buf]))
(define (buffer-insert! buf content-rope (define (buffer-insert! buf pos-or-mtype content-rope)
#:point [pos0 (buffer-pos buf)] (define pos (->pos buf pos-or-mtype 'buffer-insert!))
#:move? [move? #t])
(define pos (clamp pos0 buf))
(define-values (l r) (rope-split (buffer-rope buf) pos)) (define-values (l r) (rope-split (buffer-rope buf) pos))
(set-buffer-rope! buf (rope-append (rope-append l content-rope) r)) (set-buffer-rope! buf (rope-append (rope-append l content-rope) r))
(when (>= (buffer-pos buf) pos)
(set-buffer-pos! buf (+ (buffer-pos buf) (rope-size content-rope))))
buf) buf)
(define (buffer-replace-contents! buf content-rope) (define (buffer-replace-contents! buf content-rope)
(buffer-region-update! buf (lambda (_dontcare) content-rope) #:point 0 #:mark (buffer-size buf))) (buffer-region-update! buf 0 (buffer-size buf) (lambda (_dontcare) content-rope)))
(define (call-with-excursion buf f) (define (buffer-search* buf start-pos-or-mtype forward? find-delta)
(define excursion (gensym 'excursion)) (define start-pos (->pos buf start-pos-or-mtype 'buffer-search*))
(define saved-mark-type (mark-type (format "Saved mark ~a" excursion) 'right))
(define saved-point-type (mark-type (format "Saved point ~a" excursion) 'right))
(buffer-mark! buf (buffer-mark-pos buf) #:mark-type saved-mark-type)
(buffer-mark! buf (buffer-pos buf) #:mark-type saved-point-type)
(define (restore!)
(define restore-mark-pos (buffer-mark-pos buf saved-mark-type))
(define restore-point-pos (buffer-mark-pos buf saved-point-type))
(when restore-mark-pos (buffer-mark! buf restore-mark-pos))
(when restore-point-pos (buffer-move-to! buf restore-point-pos))
(buffer-clear-mark! buf #:mark-type saved-mark-type)
(buffer-clear-mark! buf #:mark-type saved-point-type))
(with-handlers [(exn? (lambda (e)
(restore!)
(raise e)))]
(define result (f))
(restore!)
result))
(define (buffer-search* buf start-pos0 forward? move? find-delta)
(define start-pos (clamp start-pos0 buf))
(define-values (l r) (rope-split (buffer-rope buf) start-pos)) (define-values (l r) (rope-split (buffer-rope buf) start-pos))
(define delta (find-delta (if forward? r l))) (define delta (find-delta (if forward? r l)))
(and delta (and delta
@ -320,23 +319,15 @@
[forward? delta] [forward? delta]
[else (- delta (rope-size l))])) [else (- delta (rope-size l))]))
buf))) buf)))
(if move? (buffer-seek! buf new-pos)
(buffer-move-to! buf new-pos)
(buffer-seek! buf new-pos))
new-pos))) new-pos)))
(define (buffer-search buf needle (define (buffer-search buf start-pos-or-mtype needle #:forward? [forward? #t])
#:position [start-pos (buffer-pos buf)] (buffer-search* buf start-pos-or-mtype forward?
#:forward? [forward? #t]
#:move? [move? #f])
(buffer-search* buf start-pos forward? move?
(lambda (piece) (search-rope needle piece #:forward? forward?)))) (lambda (piece) (search-rope needle piece #:forward? forward?))))
(define (buffer-findf buf f (define (buffer-findf buf start-pos-or-mtype f #:forward? [forward? #t])
#:position [start-pos (buffer-pos buf)] (buffer-search* buf start-pos-or-mtype forward?
#:forward? [forward? #t]
#:move? [move? #f])
(buffer-search* buf start-pos forward? move?
(lambda (piece) (findf-in-rope f piece #:forward? forward?)))) (lambda (piece) (findf-in-rope f piece #:forward? forward?))))
(define (buffer-lift f buf . args) (define (buffer-lift f buf . args)

View File

@ -15,9 +15,11 @@
circular-list-map circular-list-map
circular-list-filter circular-list-filter
circular-list-remove circular-list-remove
circular-list-memf) circular-list-memf
circular-list-replacef)
(require racket/match) (require racket/match)
(require (only-in racket/list splitf-at))
(struct circular-list ([front #:mutable] (struct circular-list ([front #:mutable]
[back #:mutable] [back #:mutable]
@ -130,6 +132,18 @@
(append seen (circular-list-back xs))) (append seen (circular-list-back xs)))
(loop (cons a seen) (circular-cdr xs))))))) (loop (cons a seen) (circular-cdr xs)))))))
(define (circular-list-replacef xs finder replacer)
(define (rejecter e) (not (finder e)))
(define-values (head tail) (splitf-at (circular-list-front xs) rejecter))
(if (null? tail)
(let-values (((head tail) (splitf-at (reverse (circular-list-back xs)) rejecter)))
(if (null? tail)
xs
(circular-list (circular-list-front xs)
(reverse (append head (replacer (car tail)) (cdr tail))))))
(circular-list (append head (replacer (car tail)) (cdr tail))
(circular-list-back xs))))
(module+ test (module+ test
(require rackunit) (require rackunit)
@ -149,6 +163,27 @@
(check-abcdef (circular-list '(a b c d e f) '())) (check-abcdef (circular-list '(a b c d e f) '()))
(check-abcdef (circular-list '() '(f e d c b a))) (check-abcdef (circular-list '() '(f e d c b a)))
(check-equal? (circular-list->list (circular-list-replacef (circular-list '(a b c) '(f e d))
(lambda (x) (eq? x 'e))
(lambda (x) (list 111))))
'(a b c d 111 f))
(check-equal? (circular-list->list (circular-list-replacef (circular-list '(a b c) '(f e d))
(lambda (x) (eq? x 'e))
(lambda (x) (list 111 222))))
'(a b c d 111 222 f))
(check-equal? (circular-list->list (circular-list-replacef (circular-list '(a b c) '(f e d))
(lambda (x) (eq? x 'b))
(lambda (x) (list 111))))
'(a 111 c d e f))
(check-equal? (circular-list->list (circular-list-replacef (circular-list '(a b c) '(f e d))
(lambda (x) (eq? x 'b))
(lambda (x) (list 111 222))))
'(a 111 222 c d e f))
(check-equal? (circular-list->list (circular-list-replacef (circular-list '(a b c) '(f e d))
(lambda (x) (eq? x 'x))
(lambda (x) (list 111 222))))
'(a b c d e f))
(check-equal? (match (circular-cons 1 circular-empty) (check-equal? (match (circular-cons 1 circular-empty)
[(circular-cons a d) (cons a d)]) [(circular-cons a d) (cons a d)])
(cons 1 circular-empty)) (cons 1 circular-empty))

View File

@ -2,8 +2,15 @@
(provide (except-out (struct-out editor) editor) (provide (except-out (struct-out editor) editor)
make-editor make-editor
open-window
close-other-windows
close-window
resize-window
select-window
visit-file! visit-file!
render-editor! render-editor!
editor-next-window
editor-prev-window
editor-command editor-command
editor-active-buffer editor-active-buffer
editor-active-modeset editor-active-modeset
@ -21,10 +28,11 @@
(require "mode.rkt") (require "mode.rkt")
(require "keys.rkt") (require "keys.rkt")
(require "rope.rkt") (require "rope.rkt")
(require "circular-list.rkt")
(struct editor (buffers ;; BufferGroup (struct editor (buffers ;; BufferGroup
[tty #:mutable] ;; Tty [tty #:mutable] ;; Tty
[windows #:mutable] ;; (List (List Window SizeSpec)), abstract window layout [windows #:mutable] ;; (CircularList (List Window SizeSpec)), abstract window layout
[active-window #:mutable] ;; (Option Window) [active-window #:mutable] ;; (Option Window)
[running? #:mutable] ;; Boolean [running? #:mutable] ;; Boolean
[default-modeset #:mutable] ;; ModeSet [default-modeset #:mutable] ;; ModeSet
@ -37,7 +45,7 @@
(define w (make-window scratch)) (define w (make-window scratch))
(define e (editor g (define e (editor g
tty tty
(list (list w (relative-size 1))) (list->circular-list (list (list w (relative-size 1))))
w w
#f #f
default-modeset)) default-modeset))
@ -55,13 +63,68 @@
(or (lookup-buffer g title) (or (lookup-buffer g title)
(configure-fresh-buffer! editor (make-buffer g title #:initial-contents initial-contents)))) (configure-fresh-buffer! editor (make-buffer g title #:initial-contents initial-contents))))
(define (split-size s)
(match s
[(absolute-size _) s] ;; can't scale fixed-size windows
[(relative-size w) (relative-size (/ w 2))]))
(define (merge-sizes surviving disappearing)
(match* (surviving disappearing)
[((relative-size a) (relative-size b)) (relative-size (+ a b))]
[(_ _) surviving]))
(define (window-for-buffer editor buffer)
(cond [(circular-list-memf (lambda (e) (eq? (window-buffer (car e)) buffer))
(editor-windows editor)) => (compose car circular-car)]
[else #f]))
(define (entry-for? window) (lambda (e) (eq? (car e) window)))
(define (window->size-spec editor window)
(cond [(circular-list-memf (entry-for? window)
(editor-windows editor)) => (compose cadr circular-car)]
[else #f]))
(define (update-window-entry editor win updater)
(set-editor-windows! editor (circular-list-replacef (editor-windows editor)
(entry-for? win)
updater)))
(define (open-window editor buffer (define (open-window editor buffer
#:size [size (relative-size 1)] #:after-window [after-window (editor-active-window editor)]
#:proportional? [proportional? #f]
#:activate? [activate? #t]) #:activate? [activate? #t])
(define w (make-window buffer)) (define existing-w (window-for-buffer editor buffer))
(set-editor-windows! editor (append (editor-windows editor) (list (list w size)))) (define existing-size (window->size-spec editor after-window))
(when activate? (set-editor-active-window! editor w)) (define new-size (if proportional? existing-size (split-size existing-size)))
w) (define new-point (or (and existing-w (buffer-mark-pos* buffer (window-point existing-w))) 0))
(define new-window (make-window buffer new-point))
(update-window-entry editor after-window
(lambda (e) (list (list after-window new-size)
(list new-window new-size))))
(when activate? (set-editor-active-window! editor new-window))
new-window)
(define (close-other-windows editor win)
(for ((entry (circular-list->list (editor-windows editor))) #:when (not (eq? (car entry) win)))
(set-window-buffer! (car entry) #f))
(set-editor-windows! editor (list->circular-list (list (list win (relative-size 1)))))
(set-editor-active-window! editor win))
(define (close-window editor win)
(define prev (editor-prev-window editor win))
(define prev-size (window->size-spec editor prev))
(define win-size (window->size-spec editor win))
(when (and prev (> (circular-length (editor-windows editor)) 1))
(when (eq? (editor-active-window editor) win) (set-editor-active-window! editor prev))
(update-window-entry editor win (lambda (e) '()))
(resize-window editor prev (merge-sizes prev-size win-size))))
(define (resize-window editor win size)
(update-window-entry editor win (lambda (e) (list (list win size)))))
(define (select-window editor win)
(set-editor-active-window! editor win))
(define (visit-file! editor filename) (define (visit-file! editor filename)
(set-window-buffer! (editor-active-window editor) (set-window-buffer! (editor-active-window editor)
@ -70,7 +133,7 @@
(define (render-editor! editor) (define (render-editor! editor)
(render-windows! (editor-tty editor) (render-windows! (editor-tty editor)
(editor-windows editor) (circular-list->list (editor-windows editor))
(editor-active-window editor))) (editor-active-window editor)))
(define (editor-active-buffer editor) (define (editor-active-buffer editor)
@ -81,6 +144,20 @@
(define b (editor-active-buffer editor)) (define b (editor-active-buffer editor))
(and b (buffer-modeset b))) (and b (buffer-modeset b)))
(define (editor-next-window editor win)
(cond [(circular-list-memf (entry-for? win)
(editor-windows editor)) => (compose car
circular-car
circular-list-rotate-forward)]
[else #f]))
(define (editor-prev-window editor win)
(cond [(circular-list-memf (entry-for? win)
(editor-windows editor)) => (compose car
circular-car
circular-list-rotate-backward)]
[else #f]))
(define (editor-command selector editor (define (editor-command selector editor
#:keyseq [keyseq #f] #:keyseq [keyseq #f]
#:prefix-arg [prefix-arg '#:default]) #:prefix-arg [prefix-arg '#:default])

View File

@ -8,10 +8,10 @@
(define fundamental-mode (make-mode "fundamental")) (define fundamental-mode (make-mode "fundamental"))
(define-command fundamental-mode (self-insert-command buf #:keyseq keyseq) (define-command fundamental-mode (self-insert-command buf #:window win #:keyseq keyseq)
(match keyseq (match keyseq
[(list (key (? char? ch) modifiers)) #:when (set-empty? (set-remove modifiers 'shift)) [(list (key (? char? ch) modifiers)) #:when (set-empty? (set-remove modifiers 'shift))
(buffer-insert! buf (string->rope (string ch)))] (buffer-insert! buf (window-point win) (string->rope (string ch)))]
[_ #f])) [_ #f]))
(define-command fundamental-mode (unbound-key-sequence buf #:keyseq keyseq) (define-command fundamental-mode (unbound-key-sequence buf #:keyseq keyseq)
@ -19,101 +19,119 @@
(define-key fundamental-mode (list "C-q" '#:default) self-insert-command) (define-key fundamental-mode (list "C-q" '#:default) self-insert-command)
(define-command fundamental-mode (newline buf) (define-command fundamental-mode (newline buf #:window win)
#:bind-key "C-m" #:bind-key "C-m"
#:bind-key "C-j" #:bind-key "C-j"
(buffer-insert! buf (string->rope "\n"))) (buffer-insert! buf (window-point win) (string->rope "\n")))
(define (move-forward-n-lines buf count) (define (move-forward-n-lines win count)
(define buf (window-buffer win))
(for ((i count)) (for ((i count))
(buffer-move-to-end-of-line! buf) (buffer-move-mark-to-end-of-line! buf (window-point win))
(buffer-move-by! buf 1))) (buffer-move-mark! buf (window-point win) 1)))
(define (move-backward-n-lines buf count) (define (move-backward-n-lines win count)
(define buf (window-buffer win))
(for ((i count)) (for ((i count))
(buffer-move-to-start-of-line! buf) (buffer-move-mark-to-start-of-line! buf (window-point win))
(buffer-move-by! buf -1))) (buffer-move-mark! buf (window-point win) -1)))
(define (move-to-column buf col) (define (move-to-column win col)
(define eol-pos (buffer-end-of-line buf)) (define buf (window-buffer win))
(buffer-move-to-start-of-line! buf) (define eol-pos (buffer-end-of-line buf (window-point win)))
(buffer-move-by! buf (min col (- eol-pos (buffer-pos buf))))) (define sol-pos (buffer-start-of-line buf (window-point win)))
(buffer-mark! buf (window-point win) (+ sol-pos (min col (- eol-pos sol-pos)))))
(define-command fundamental-mode (forward-char buf #:prefix-arg [count 1]) (define-command fundamental-mode (forward-char buf #:window win #:prefix-arg [count 1])
#:bind-key "C-f" #:bind-key "C-f"
#:bind-key "<right>" #:bind-key "<right>"
(buffer-move-by! buf count)) (buffer-move-mark! buf (window-point win) count))
(define-command fundamental-mode (backward-char buf #:prefix-arg [count 1]) (define-command fundamental-mode (backward-char buf #:window win #:prefix-arg [count 1])
#:bind-key "C-b" #:bind-key "C-b"
#:bind-key "<left>" #:bind-key "<left>"
(buffer-move-by! buf (- count))) (buffer-move-mark! buf (window-point win) (- count)))
(define-command fundamental-mode (next-line buf #:prefix-arg [count 1]) (define-command fundamental-mode (next-line buf #:window win #:prefix-arg [count 1])
#:bind-key "C-n" #:bind-key "C-n"
#:bind-key "<down>" #:bind-key "<down>"
(define col (buffer-column buf)) (define col (buffer-column buf (window-point win)))
(move-forward-n-lines buf count) (move-forward-n-lines win count)
(move-to-column buf col)) (move-to-column win col))
(define-command fundamental-mode (prev-line buf #:prefix-arg [count 1]) (define-command fundamental-mode (prev-line buf #:window win #:prefix-arg [count 1])
#:bind-key "C-p" #:bind-key "C-p"
#:bind-key "<up>" #:bind-key "<up>"
(define col (buffer-column buf)) (define col (buffer-column buf (window-point win)))
(move-backward-n-lines buf count) (move-backward-n-lines win count)
(move-to-column buf col)) (move-to-column win col))
(define-command fundamental-mode (move-end-of-line buf #:prefix-arg [count 1]) (define-command fundamental-mode (move-end-of-line buf #:window win #:prefix-arg [count 1])
#:bind-key "C-e" #:bind-key "C-e"
#:bind-key "<end>" #:bind-key "<end>"
(when (positive? count) (move-forward-n-lines buf (- count 1))) (when (positive? count) (move-forward-n-lines win (- count 1)))
(buffer-move-to-end-of-line! buf)) (buffer-move-mark-to-end-of-line! buf (window-point win)))
(define-command fundamental-mode (move-beginning-of-line buf #:prefix-arg [count 1]) (define-command fundamental-mode (move-beginning-of-line buf #:window win #:prefix-arg [count 1])
#:bind-key "C-a" #:bind-key "C-a"
#:bind-key "<home>" #:bind-key "<home>"
(when (positive? count) (move-forward-n-lines buf (- count 1))) (when (positive? count) (move-forward-n-lines win (- count 1)))
(buffer-move-to-start-of-line! buf)) (buffer-move-mark-to-start-of-line! buf (window-point win)))
(define-command fundamental-mode (delete-backward-char buf #:prefix-arg [count 1]) (define-command fundamental-mode (delete-backward-char buf #:window win #:prefix-arg [count 1])
#:bind-key "<backspace>" #:bind-key "<backspace>"
#:bind-key "C-h" ;; differs from GNU emacs #:bind-key "C-h" ;; differs from GNU emacs
(buffer-region-update! buf (define pos (buffer-mark-pos buf (window-point win)))
(lambda (_deleted) (empty-rope)) (buffer-region-update! buf (- pos 1) pos (lambda (_deleted) (empty-rope))))
#:mark (- (buffer-pos buf) count)))
(define-command fundamental-mode (delete-forward-char buf #:prefix-arg [count 1]) (define-command fundamental-mode (delete-forward-char buf #:window win #:prefix-arg [count 1])
#:bind-key "<delete>" #:bind-key "<delete>"
#:bind-key "C-d" #:bind-key "C-d"
(buffer-region-update! buf (define pos (buffer-mark-pos buf (window-point win)))
(lambda (_deleted) (empty-rope)) (buffer-region-update! buf pos (+ pos 1) (lambda (_deleted) (empty-rope))))
#:mark (+ (buffer-pos buf) count)))
(define-command fundamental-mode (beginning-of-buffer buf #:prefix-arg [tenths 0]) (define-command fundamental-mode (beginning-of-buffer buf #:window win #:prefix-arg [tenths 0])
#:bind-key "M-<" #:bind-key "M-<"
#:bind-key "C-<home>" #:bind-key "C-<home>"
#:bind-key "<begin>" #:bind-key "<begin>"
(if (eq? tenths '#:prefix) (set! tenths 0) (buffer-mark! buf)) (if (eq? tenths '#:prefix) (set! tenths 0) (window-mark! win))
(buffer-move-to! buf (* (buffer-size buf) (max 0 (min 10 tenths)) 1/10))) (window-move-to! win (* (buffer-size buf) (max 0 (min 10 tenths)) 1/10)))
(define-command fundamental-mode (end-of-buffer buf #:prefix-arg [tenths 0]) (define-command fundamental-mode (end-of-buffer buf #:window win #:prefix-arg [tenths 0])
#:bind-key "M->" #:bind-key "M->"
#:bind-key "C-<end>" #:bind-key "C-<end>"
(if (eq? tenths '#:prefix) (set! tenths 0) (buffer-mark! buf)) (if (eq? tenths '#:prefix) (set! tenths 0) (window-mark! win))
(buffer-move-to! buf (* (buffer-size buf) (- 10 (max 0 (min 10 tenths))) 1/10))) (window-move-to! win (* (buffer-size buf) (- 10 (max 0 (min 10 tenths))) 1/10)))
(define-command fundamental-mode (exchange-point-and-mark buf) (define-command fundamental-mode (exchange-point-and-mark buf #:window win)
#:bind-key "C-x C-x" #:bind-key "C-x C-x"
(define m (buffer-mark-pos buf)) (define m (buffer-mark-pos* buf (window-mark win)))
(when m (when m
(define p (buffer-pos buf)) (define p (buffer-mark-pos buf (window-point win)))
(buffer-mark! buf p) (window-mark! win p)
(buffer-move-to! buf m))) (window-move-to! win m)))
(define-command fundamental-mode (set-mark-command buf #:prefix-arg arg) (define-command fundamental-mode (set-mark-command buf #:window win #:prefix-arg arg)
#:bind-key "C-@" #:bind-key "C-@"
#:bind-key "C-space" #:bind-key "C-space"
(if (eq? arg '#:prefix) (if (eq? arg '#:prefix)
(let ((m (buffer-mark-pos buf))) (let ((m (buffer-mark-pos* buf (window-mark win))))
(and m (buffer-move-to! buf m))) (and m (window-move-to! win m)))
(buffer-mark! buf))) (window-mark! win (window-point win))))
(define-command fundamental-mode (split-window-below buf #:window win #:editor ed)
#:bind-key "C-x 2"
(open-window ed buf #:after-window win))
(define-command fundamental-mode (delete-other-windows buf #:window win #:editor ed)
#:bind-key "C-x 1"
(close-other-windows ed win))
(define-command fundamental-mode (delete-window buf #:window win #:editor ed)
#:bind-key "C-x 0"
(close-window ed win))
(define-command fundamental-mode (other-window buf #:window win #:editor ed)
#:bind-key "C-tab"
#:bind-key "C-x o"
(select-window ed (editor-next-window ed win)))

View File

@ -1,6 +1,8 @@
#lang racket/base #lang racket/base
(provide render-windows!) (provide (struct-out absolute-size)
(struct-out relative-size)
render-windows!)
(require racket/match) (require racket/match)
@ -9,7 +11,11 @@
(require "display.rkt") (require "display.rkt")
(require "rope.rkt") (require "rope.rkt")
(define top-of-window-mtype (mark-type "top-of-window" 'left)) ;; A SizeSpec is either
;; -- (absolute-size PositiveInteger), a specific size in screen rows
;; -- (relative-size PositiveReal), a weighted window size
(struct absolute-size (lines) #:prefab)
(struct relative-size (weight) #:prefab)
(define (newline? c) (equal? c #\newline)) (define (newline? c) (equal? c #\newline))
(define (not-newline? c) (not (newline? c))) (define (not-newline? c) (not (newline? c)))
@ -23,16 +29,16 @@
;; will end up at a configurable percentage of the way down the ;; will end up at a configurable percentage of the way down the
;; window. ;; window.
;; ;;
;; Buffer Nat -> Nat ;; Window Nat -> Nat
;; Ensures the given mark is sanely positioned as a top-of-window mark ;; Ensures that window-top is sanely positioned with respect to
;; with respect to the given cursor position. Returns the ;; window-point. Returns the new position of window-top.
;; top-of-window position. (define (frame! win available-line-count
(define (frame-buffer! buf available-line-count #:preferred-position-fraction [preferred-position-fraction 1/2])
#:preferred-position-fraction [preferred-position-fraction 1/2]) (define buf (window-buffer win))
(define old-top-of-window-pos (or (buffer-mark-pos buf top-of-window-mtype) 0)) (define old-top-of-window-pos (or (buffer-mark-pos* buf (window-top win)) 0))
(define preferred-distance-from-bottom (define preferred-distance-from-bottom
(ceiling (* available-line-count (- 1 preferred-position-fraction)))) (ceiling (* available-line-count (- 1 preferred-position-fraction))))
(let loop ((pos (buffer-findf buf newline? #:forward? #f)) (let loop ((pos (buffer-findf buf (window-point win) newline? #:forward? #f))
(line-count 0) (line-count 0)
(top-of-window-pos old-top-of-window-pos)) (top-of-window-pos old-top-of-window-pos))
(define new-top-of-window-pos (define new-top-of-window-pos
@ -41,10 +47,10 @@
[(= pos old-top-of-window-pos) [(= pos old-top-of-window-pos)
old-top-of-window-pos] old-top-of-window-pos]
[(>= line-count (- available-line-count 1)) [(>= line-count (- available-line-count 1))
(buffer-mark! buf new-top-of-window-pos #:mark-type top-of-window-mtype) (buffer-mark! buf (window-top win) new-top-of-window-pos)
new-top-of-window-pos] new-top-of-window-pos]
[else [else
(loop (buffer-findf buf newline? #:forward? #f #:position (- pos 1)) (loop (buffer-findf buf (- pos 1) newline? #:forward? #f)
(+ line-count 1) (+ line-count 1)
new-top-of-window-pos)]))) new-top-of-window-pos)])))
@ -84,10 +90,11 @@
[_ [_
(emit (string c))])]))) (emit (string c))])])))
(define (render-buffer! t b window-top window-height is-active?) (define (render-window! t win window-top window-height is-active?)
(define buf (window-buffer win))
(define available-line-count (- window-height 1)) (define available-line-count (- window-height 1))
(define top-of-window-pos (frame-buffer! b available-line-count)) (define top-of-window-pos (frame! win available-line-count))
(define cursor-pos (buffer-pos b)) (define cursor-pos (buffer-mark-pos buf (window-point win)))
(tty-goto t window-top 0) (tty-goto t window-top 0)
(tty-body-style t is-active?) (tty-body-style t is-active?)
(define cursor-coordinates (define cursor-coordinates
@ -98,8 +105,8 @@
[(>= line-count available-line-count) [(>= line-count available-line-count)
cursor-coordinates] cursor-coordinates]
[else [else
(define eol-pos (buffer-findf b newline? #:position sol-pos)) (define eol-pos (buffer-findf buf sol-pos newline?))
(define line (rope->string (buffer-region b #:point eol-pos #:mark sol-pos))) (define line (rope->string (buffer-region buf sol-pos eol-pos)))
(define-values (formatted-line cursor-offset) (define-values (formatted-line cursor-offset)
(format-line line (tty-columns t) (- cursor-pos sol-pos))) (format-line line (tty-columns t) (- cursor-pos sol-pos)))
(tty-display t formatted-line) (tty-display t formatted-line)
@ -111,8 +118,8 @@
(list (+ line-count window-top) cursor-offset) (list (+ line-count window-top) cursor-offset)
cursor-coordinates))]))) cursor-coordinates))])))
(tty-statusline-style t is-active?) (tty-statusline-style t is-active?)
(tty-display t (if is-active? "== " "-- ") (buffer-title b) " ") (tty-display t (if is-active? "== " "-- ") (buffer-title buf) " ")
(let ((remaining-length (- (tty-columns t) 4 (string-length (buffer-title b))))) (let ((remaining-length (- (tty-columns t) 4 (string-length (buffer-title buf)))))
(when (positive? remaining-length) (when (positive? remaining-length)
(tty-display t (make-string remaining-length (if is-active? #\= #\-))))) (tty-display t (make-string remaining-length (if is-active? #\= #\-)))))
cursor-coordinates) cursor-coordinates)
@ -152,8 +159,7 @@
(for/fold [(cursor-position #f)] [(e layout)] (for/fold [(cursor-position #f)] [(e layout)]
(match-define (list w window-top window-height) e) (match-define (list w window-top window-height) e)
(define is-active? (eq? w active-window)) (define is-active? (eq? w active-window))
(define b (window-buffer w)) (define window-cursor-position (render-window! t w window-top window-height is-active?))
(define window-cursor-position (render-buffer! t b window-top window-height is-active?))
(if is-active? window-cursor-position cursor-position))) (if is-active? window-cursor-position cursor-position)))
(when active-cursor-position (when active-cursor-position
(tty-goto t (car active-cursor-position) (cadr active-cursor-position))) (tty-goto t (car active-cursor-position) (cadr active-cursor-position)))

View File

@ -48,9 +48,9 @@
;; Stickiness adheres. What Finseth calls a "normal mark" has 'right ;; Stickiness adheres. What Finseth calls a "normal mark" has 'right
;; stickiness, and what he calls a "fixed mark" has 'left stickiness. ;; stickiness, and what he calls a "fixed mark" has 'left stickiness.
;; A MarkType is a (mark-type String Stickiness). MarkTypes can be ;; A MarkType is a (mark-type Any Stickiness). MarkTypes can be
;; associated with a set of Any values at each position in the rope. ;; associated with a set of Any values at each position in the rope.
(struct mark-type (name stickiness) #:prefab) (struct mark-type (info stickiness) #:prefab)
;; A Strand is a (strand String Number Number), representing a ;; A Strand is a (strand String Number Number), representing a
;; substring of a string. ;; substring of a string.

View File

@ -1,46 +1,55 @@
#lang racket/base #lang racket/base
(provide (except-out (struct-out window) window) (provide (except-out (struct-out window) window set-window-buffer!)
(struct-out absolute-size) (rename-out [set-window-buffer!* set-window-buffer!])
(struct-out relative-size)
make-window make-window
window-split
window-command window-command
window-mark!
window-move-to!
) )
(require racket/match) (require racket/match)
(require "buffer.rkt") (require "buffer.rkt")
(require "lists.rkt") (require "rope.rkt")
;; A SizeSpec is either
;; -- (absolute-size PositiveInteger), a specific size in screen rows
;; -- (relative-size PositiveReal), a weighted window size
(struct absolute-size (lines) #:prefab)
(struct relative-size (weight) #:prefab)
(struct window (id ;; Symbol (struct window (id ;; Symbol
[buffer #:mutable] ;; Buffer top ;; MarkType
point ;; MarkType
mark ;; MarkType
[buffer #:mutable] ;; (Option Buffer)
) #:prefab) ) #:prefab)
(define (make-window initial-buffer) (define (make-window initial-buffer [initial-point-or-mark 0])
(window (gensym 'window) (define id (gensym 'window))
initial-buffer)) (define w (window id
(mark-type (buffer-mark-type 'top id #f) 'left)
(mark-type (buffer-mark-type 'point id #t) 'right)
(mark-type (buffer-mark-type 'mark id #f) 'left)
#f))
(set-window-buffer!* w initial-buffer initial-point-or-mark) ;; sets initial marks
w)
(define (scale-size s) (define (set-window-buffer!* win new [point-or-mark 0])
(match s (define old (window-buffer win))
[(absolute-size _) s] ;; can't scale fixed-size windows (when old
[(relative-size w) (relative-size (/ w 2))])) (buffer-clear-mark! old (window-top win))
(buffer-clear-mark! old (window-point win))
(define (window-split w ws #:proportional? [proportional? #f]) (buffer-clear-mark! old (window-mark win)))
(replacef ws (set-window-buffer! win new)
(lambda (e) (eq? (car e) w)) (when new
(lambda (e) (buffer-mark! new (window-point win) point-or-mark))
(define new-size (if proportional? (cadr e) (scale-size (cadr e)))) (void))
(list (list w new-size)
(list (make-window (window-buffer w)) new-size)))))
(define (window-command selector window (define (window-command selector window
#:keyseq [keyseq #f] #:keyseq [keyseq #f]
#:prefix-arg [prefix-arg '#:default]) #:prefix-arg [prefix-arg '#:default])
(command selector (window-buffer window) #:window window #:keyseq keyseq #:prefix-arg prefix-arg)) (command selector (window-buffer window) #:window window #:keyseq keyseq #:prefix-arg prefix-arg))
(define (window-mark! win [pos (window-point win)])
(buffer-mark! (window-buffer win) (window-mark win) pos)
win)
(define (window-move-to! win pos)
(buffer-mark! (window-buffer win) (window-point win) pos)
win)