racket-ansi/rmacs/editor.rkt

245 lines
9.9 KiB
Racket
Raw Normal View History

2014-12-22 22:14:13 +00:00
#lang racket/base
(provide (except-out (struct-out editor) editor)
make-editor
open-window
close-other-windows
close-window
resize-window
select-window
2014-12-22 22:14:13 +00:00
visit-file!
render-editor!
editor-next-window
editor-prev-window
editor-command
2014-12-23 16:09:22 +00:00
editor-active-buffer
editor-active-modeset
2014-12-23 06:43:01 +00:00
editor-mainloop
editor-request-shutdown!
2014-12-24 03:01:18 +00:00
editor-force-redisplay!
2014-12-22 22:14:13 +00:00
)
2014-12-23 06:43:01 +00:00
(require racket/match)
2014-12-22 22:14:13 +00:00
(require "buffer.rkt")
(require "display.rkt")
(require "window.rkt")
(require "render.rkt")
2014-12-23 06:43:01 +00:00
(require "mode.rkt")
(require "keys.rkt")
2014-12-23 16:09:22 +00:00
(require "rope.rkt")
(require "circular-list.rkt")
2014-12-22 22:14:13 +00:00
(struct editor (buffers ;; BufferGroup
2014-12-23 06:43:01 +00:00
[tty #:mutable] ;; Tty
[windows #:mutable] ;; (CircularList (List Window SizeSpec)), abstract window layout
2014-12-22 22:14:13 +00:00
[active-window #:mutable] ;; (Option Window)
2014-12-23 06:43:01 +00:00
[running? #:mutable] ;; Boolean
2014-12-23 16:09:22 +00:00
[default-modeset #:mutable] ;; ModeSet
2014-12-22 22:17:05 +00:00
) #:prefab)
2014-12-22 22:14:13 +00:00
2014-12-23 16:09:22 +00:00
(define (make-editor #:tty [tty (stdin-tty)]
#:default-modeset [default-modeset (make-modeset)])
2014-12-22 22:14:13 +00:00
(define g (make-buffergroup))
(define scratch (make-buffer g "*scratch*" #:initial-contents ";; This is the scratch buffer."))
(define w (make-window scratch))
2014-12-23 16:09:22 +00:00
(define e (editor g
tty
(list->circular-list (list (list w (relative-size 1))))
2014-12-23 16:09:22 +00:00
w
#f
default-modeset))
(initialize-buffergroup! g e)
2014-12-23 16:09:22 +00:00
(configure-fresh-buffer! e scratch)
e)
(define (configure-fresh-buffer! editor buffer)
(buffer-apply-modeset! buffer (editor-default-modeset editor))
buffer)
(define (find-buffer editor [title0 #f] #:initial-contents [initial-contents ""])
(define g (editor-buffers editor))
(define title (or title0 (unused-buffer-title g '())))
(or (lookup-buffer g title)
(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)))
2014-12-23 16:09:22 +00:00
(define (open-window editor buffer
#:after-window [after-window (editor-active-window editor)]
#:proportional? [proportional? #f]
2014-12-23 16:09:22 +00:00
#:activate? [activate? #t])
(define existing-w (window-for-buffer editor buffer))
(define existing-size (window->size-spec editor after-window))
(define new-size (if proportional? existing-size (split-size existing-size)))
(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))
2014-12-22 22:14:13 +00:00
(define (visit-file! editor filename)
(set-window-buffer! (editor-active-window editor)
2014-12-23 16:09:22 +00:00
(configure-fresh-buffer! editor
(file->buffer (editor-buffers editor) filename))))
2014-12-22 22:14:13 +00:00
(define (render-editor! editor)
2014-12-23 06:43:01 +00:00
(render-windows! (editor-tty editor)
(circular-list->list (editor-windows editor))
2014-12-22 22:14:13 +00:00
(editor-active-window editor)))
2014-12-23 06:43:01 +00:00
2014-12-23 16:09:22 +00:00
(define (editor-active-buffer editor)
2014-12-23 06:43:01 +00:00
(define w (editor-active-window editor))
(and w (window-buffer w)))
2014-12-23 16:09:22 +00:00
(define (editor-active-modeset editor)
(define b (editor-active-buffer editor))
2014-12-23 06:43:01 +00:00
(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
#:keyseq [keyseq #f]
#:prefix-arg [prefix-arg '#:default])
(window-command selector (editor-active-window editor) #:keyseq keyseq #:prefix-arg prefix-arg))
2014-12-23 06:43:01 +00:00
(define (root-keyseq-handler editor)
2014-12-23 16:09:22 +00:00
(modeset-keyseq-handler (editor-active-modeset editor)))
2014-12-23 06:43:01 +00:00
(define *error-count* 0)
2014-12-23 16:09:22 +00:00
(define (open-debugger editor exc)
(local-require (only-in web-server/private/util exn->string))
(define error-report (exn->string exc))
(log-error "Exception:\n~a\n" error-report)
(set! *error-count* (+ *error-count* 1))
(when (>= *error-count* 3) (exit))
2014-12-23 16:09:22 +00:00
(define b (find-buffer editor "*Error*"))
(buffer-replace-contents! b (string->rope error-report))
(open-window editor b))
2014-12-23 06:43:01 +00:00
(define (editor-mainloop editor)
(when (editor-running? editor) (error 'editor-mainloop "Nested mainloop"))
(set-editor-running?! editor #t)
2014-12-23 16:09:22 +00:00
(with-handlers* ([exn? (lambda (exc)
(set-editor-running?! editor #f)
(open-debugger editor exc)
(editor-mainloop editor))])
(let loop ((total-keyseq '())
(input '())
2014-12-24 03:01:18 +00:00
(handler (root-keyseq-handler editor))
(next-repaint-deadline 0))
(define (request-repaint) (or next-repaint-deadline (+ (current-inexact-milliseconds) 20)))
2014-12-23 06:43:01 +00:00
(define (wait-for-input next-handler)
(when (editor-running? editor)
2014-12-24 03:01:18 +00:00
(sync (if next-repaint-deadline
(handle-evt (alarm-evt next-repaint-deadline)
(lambda (_)
(loop total-keyseq '() next-handler next-repaint-deadline)))
never-evt)
(handle-evt (tty-next-key-evt (editor-tty editor))
2014-12-23 06:43:01 +00:00
(lambda (new-key)
2014-12-23 16:09:22 +00:00
(define new-input (list new-key))
2014-12-24 03:01:18 +00:00
(loop (append total-keyseq new-input)
new-input
next-handler
next-repaint-deadline))))))
(cond
[(and next-repaint-deadline (>= (current-inexact-milliseconds) next-repaint-deadline))
(render-editor! editor)
(loop total-keyseq input handler #f)]
[(null? input)
(wait-for-input handler)]
[else
(match (handler editor input)
[(unbound-key-sequence)
(if (invoke (editor-command 'unbound-key-sequence editor #:keyseq total-keyseq))
2014-12-24 03:01:18 +00:00
(loop '() '() (root-keyseq-handler editor) (request-repaint))
(error 'editor-mainloop "Unbound key sequence: ~a"
(keyseq->keyspec total-keyseq)))]
[(incomplete-key-sequence next-handler)
(wait-for-input next-handler)]
[(command-invocation selector prefix-arg remaining-input)
(define accepted-input
(let remove-tail ((keyseq total-keyseq))
(if (equal? keyseq remaining-input)
'()
(cons (car keyseq) (remove-tail (cdr keyseq))))))
(invoke (editor-command selector editor #:keyseq accepted-input #:prefix-arg prefix-arg))
2014-12-24 03:01:18 +00:00
(loop '() remaining-input (root-keyseq-handler editor) (request-repaint))])]))))
2014-12-23 06:43:01 +00:00
(define (editor-request-shutdown! editor)
(set-editor-running?! editor #f))
2014-12-24 03:01:18 +00:00
(define (editor-force-redisplay! editor)
(tty-reset (editor-tty editor)))
2014-12-23 06:43:01 +00:00
;;---------------------------------------------------------------------------
(define-command kernel-mode (save-buffers-kill-terminal buf)
2014-12-23 06:43:01 +00:00
#:bind-key "C-x C-c"
(editor-request-shutdown! (buffer-editor buf)))
2014-12-24 03:01:18 +00:00
(define-command kernel-mode (force-redisplay buf)
#:bind-key "C-l"
(editor-force-redisplay! (buffer-editor buf)))