2014-12-22 22:14:13 +00:00
|
|
|
#lang racket/base
|
|
|
|
|
2014-12-28 04:59:12 +00:00
|
|
|
(provide (struct-out absolute-size)
|
|
|
|
(struct-out relative-size)
|
2014-12-28 13:16:09 +00:00
|
|
|
(struct-out layout)
|
|
|
|
layout-windows
|
2014-12-28 04:59:12 +00:00
|
|
|
render-windows!)
|
2014-12-22 22:14:13 +00:00
|
|
|
|
|
|
|
(require racket/match)
|
|
|
|
|
|
|
|
(require "buffer.rkt")
|
|
|
|
(require "window.rkt")
|
|
|
|
(require "display.rkt")
|
|
|
|
(require "rope.rkt")
|
2014-12-28 16:25:54 +00:00
|
|
|
(require "wrap.rkt")
|
2014-12-22 22:14:13 +00:00
|
|
|
|
2014-12-28 04:59:12 +00:00
|
|
|
;; 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)
|
2014-12-22 22:14:13 +00:00
|
|
|
|
2014-12-28 13:16:09 +00:00
|
|
|
;; A Layout is a (layout Window SizeSpec Nat Nat)
|
|
|
|
(struct layout (window ;; Window
|
|
|
|
size-spec ;; SizeSpec
|
|
|
|
top ;; Nat, a row
|
|
|
|
left ;; Nat, a column
|
|
|
|
width ;; Nat
|
|
|
|
height ;; Nat
|
|
|
|
) #:prefab)
|
|
|
|
|
2014-12-22 22:14:13 +00:00
|
|
|
(define (newline? c) (equal? c #\newline))
|
|
|
|
|
|
|
|
;; Finseth's book defines a C routine, Framer(), which is intended to
|
|
|
|
;; ensure that the `top_of_window` mark, denoting the position where
|
|
|
|
;; display should begin for the current window, is in a sane position.
|
|
|
|
;; The mark is left alone unless the cursor is outside the currently
|
|
|
|
;; displayed window, either above or below. If the mark needs to be
|
|
|
|
;; moved, it is moved to a line such that the cursor, after redisplay,
|
|
|
|
;; will end up at a configurable percentage of the way down the
|
|
|
|
;; window.
|
|
|
|
;;
|
2014-12-28 16:25:54 +00:00
|
|
|
;; It is here that we perform soft-wrapping of lines.
|
|
|
|
;;
|
|
|
|
;; Window Nat -> (List (Pair Nat Nat))
|
2014-12-28 04:59:12 +00:00
|
|
|
;; Ensures that window-top is sanely positioned with respect to
|
2014-12-28 16:25:54 +00:00
|
|
|
;; window-point. Returns wrapped line spans starting at the new
|
|
|
|
;; window-top.
|
|
|
|
(define (frame! win available-line-count window-width
|
2014-12-28 04:59:12 +00:00
|
|
|
#:preferred-position-fraction [preferred-position-fraction 1/2])
|
|
|
|
(define buf (window-buffer win))
|
|
|
|
(define old-top-of-window-pos (or (buffer-mark-pos* buf (window-top win)) 0))
|
2014-12-23 06:43:01 +00:00
|
|
|
(define preferred-distance-from-bottom
|
|
|
|
(ceiling (* available-line-count (- 1 preferred-position-fraction))))
|
2014-12-28 16:25:54 +00:00
|
|
|
(define g (buffer-lines-reverse/wrap buf (window-point win) basic-wrap window-width))
|
|
|
|
(define spans
|
|
|
|
(let loop ((line-count 0)
|
|
|
|
(all-spans '())
|
|
|
|
(preferred-spans '()))
|
|
|
|
(define-values (pos eol-pos) (g))
|
|
|
|
(define span (cons pos eol-pos))
|
|
|
|
(define new-all-spans (cons span all-spans))
|
|
|
|
(define new-preferred-spans (if (= line-count preferred-distance-from-bottom)
|
|
|
|
new-all-spans
|
|
|
|
preferred-spans))
|
|
|
|
(cond
|
|
|
|
[(not pos) all-spans] ;; we hit buffer top before our preferred distance. NB all-spans
|
|
|
|
[(= pos old-top-of-window-pos) new-all-spans]
|
|
|
|
[(>= line-count (- available-line-count 1)) new-preferred-spans]
|
|
|
|
[else (loop (+ line-count 1) new-all-spans new-preferred-spans)])))
|
|
|
|
(buffer-mark! buf (window-top win) (caar spans))
|
|
|
|
spans)
|
2014-12-22 22:14:13 +00:00
|
|
|
|
|
|
|
(define (tty-body-style t is-active?)
|
2014-12-27 00:38:01 +00:00
|
|
|
(tty-set-pen! t tty-default-pen))
|
2014-12-22 22:14:13 +00:00
|
|
|
|
|
|
|
(define (tty-statusline-style t is-active?)
|
2014-12-24 03:01:18 +00:00
|
|
|
(tty-set-pen! t (pen color-black color-white #f #f)))
|
2014-12-22 22:14:13 +00:00
|
|
|
|
2014-12-28 13:16:09 +00:00
|
|
|
(define (render-window! t win window-top window-width window-height is-active?)
|
2014-12-28 04:59:12 +00:00
|
|
|
(define buf (window-buffer win))
|
2014-12-23 06:43:01 +00:00
|
|
|
(define available-line-count (- window-height 1))
|
2014-12-28 16:25:54 +00:00
|
|
|
(define spans (frame! win available-line-count window-width))
|
2014-12-28 04:59:12 +00:00
|
|
|
(define cursor-pos (buffer-mark-pos buf (window-point win)))
|
2014-12-22 22:14:13 +00:00
|
|
|
(tty-goto t window-top 0)
|
|
|
|
(tty-body-style t is-active?)
|
2014-12-28 16:25:54 +00:00
|
|
|
|
|
|
|
(define (render-span sol-pos eol-pos line-count cursor-coordinates)
|
|
|
|
(define line (rope->string (buffer-region buf sol-pos eol-pos)))
|
|
|
|
(tty-display t line)
|
|
|
|
(tty-newline t)
|
|
|
|
(if (<= sol-pos cursor-pos eol-pos)
|
|
|
|
(list (+ line-count window-top)
|
|
|
|
(let ((line-to-cursor (substring line 0 (- cursor-pos sol-pos))))
|
|
|
|
(buffer-string-column-count buf 0 line-to-cursor)))
|
|
|
|
cursor-coordinates))
|
|
|
|
|
|
|
|
(define (render-top-spans spans line-count cursor-coordinates)
|
|
|
|
(cond
|
|
|
|
[(>= line-count available-line-count) cursor-coordinates]
|
|
|
|
[(null? spans)
|
|
|
|
(define g (buffer-lines-forward/wrap buf (window-point win) basic-wrap window-width))
|
|
|
|
(g) ;; discard first span, since it has already been covered
|
|
|
|
(render-bottom-spans g line-count cursor-coordinates)]
|
|
|
|
[else
|
|
|
|
(render-top-spans (cdr spans)
|
|
|
|
(+ line-count 1)
|
|
|
|
(render-span (caar spans) (cdar spans) line-count cursor-coordinates))]))
|
|
|
|
|
|
|
|
(define (render-bottom-spans g line-count cursor-coordinates)
|
|
|
|
(if (>= line-count available-line-count)
|
|
|
|
cursor-coordinates
|
|
|
|
(let-values (((sol-pos eol-pos) (g)))
|
|
|
|
(if sol-pos
|
|
|
|
(render-bottom-spans g
|
|
|
|
(+ line-count 1)
|
|
|
|
(render-span sol-pos eol-pos line-count cursor-coordinates))
|
|
|
|
(begin (for ((i (- available-line-count line-count))) (tty-newline t))
|
|
|
|
cursor-coordinates)))))
|
|
|
|
|
|
|
|
(define cursor-coordinates (render-top-spans spans 0 #f))
|
|
|
|
|
2014-12-22 22:14:13 +00:00
|
|
|
(tty-statusline-style t is-active?)
|
2014-12-28 05:21:06 +00:00
|
|
|
(tty-display t "-- " (buffer-title buf) " ")
|
2014-12-28 04:59:12 +00:00
|
|
|
(let ((remaining-length (- (tty-columns t) 4 (string-length (buffer-title buf)))))
|
2014-12-24 03:01:18 +00:00
|
|
|
(when (positive? remaining-length)
|
2014-12-28 05:21:06 +00:00
|
|
|
(tty-display t (make-string remaining-length #\-))))
|
2014-12-28 16:25:54 +00:00
|
|
|
|
2014-12-22 22:14:13 +00:00
|
|
|
cursor-coordinates)
|
|
|
|
|
2014-12-28 13:16:09 +00:00
|
|
|
(define (layout-windows ws total-width total-height [minimum-height 4])
|
2014-12-22 22:14:13 +00:00
|
|
|
(define total-weight (foldl + 0 (map (lambda (e)
|
|
|
|
(match (cadr e)
|
|
|
|
[(absolute-size _) 0]
|
|
|
|
[(relative-size w) w])) ws)))
|
|
|
|
(define reserved-lines (foldl + 0 (map (lambda (e)
|
|
|
|
(match (cadr e)
|
|
|
|
[(absolute-size lines) lines]
|
|
|
|
[(relative-size _) 0])) ws)))
|
|
|
|
(define proportional-lines (- total-height reserved-lines))
|
|
|
|
(let loop ((ws ws) (offset 0) (remaining proportional-lines))
|
|
|
|
(match ws
|
|
|
|
['() '()]
|
2014-12-28 13:16:09 +00:00
|
|
|
[(cons (list w (and spec (absolute-size lines))) rest)
|
|
|
|
(cons (layout w spec offset 0 total-width lines)
|
|
|
|
(loop rest (+ offset lines) remaining))]
|
|
|
|
[(cons (list w (and spec (relative-size weight))) rest)
|
2014-12-22 22:14:13 +00:00
|
|
|
(define height (max minimum-height
|
|
|
|
(inexact->exact
|
|
|
|
(round (* proportional-lines (/ weight total-weight))))))
|
|
|
|
(if (>= remaining height)
|
|
|
|
(if (null? rest)
|
2014-12-28 13:16:09 +00:00
|
|
|
(list (layout w spec offset 0 total-width remaining))
|
|
|
|
(cons (layout w spec offset 0 total-width height)
|
|
|
|
(loop rest (+ offset height) (- remaining height))))
|
2014-12-22 22:14:13 +00:00
|
|
|
(if (>= remaining minimum-height)
|
2014-12-28 13:16:09 +00:00
|
|
|
(list (layout w spec offset 0 total-width remaining))
|
2014-12-22 22:14:13 +00:00
|
|
|
'()))])))
|
|
|
|
|
2014-12-28 13:16:09 +00:00
|
|
|
(define (render-windows! t layouts active-window)
|
2014-12-22 22:14:13 +00:00
|
|
|
(tty-body-style t #f)
|
2014-12-23 06:43:01 +00:00
|
|
|
(tty-goto t 0 0)
|
2014-12-22 22:14:13 +00:00
|
|
|
(define active-cursor-position
|
2014-12-28 13:16:09 +00:00
|
|
|
(for/fold [(cursor-position #f)] [(e layouts)]
|
|
|
|
(match-define (layout w _spec window-top _left window-width window-height) e)
|
2014-12-22 22:14:13 +00:00
|
|
|
(define is-active? (eq? w active-window))
|
2014-12-28 13:16:09 +00:00
|
|
|
(define window-cursor-position
|
|
|
|
(render-window! t w window-top window-width window-height is-active?))
|
2014-12-22 22:14:13 +00:00
|
|
|
(if is-active? window-cursor-position cursor-position)))
|
|
|
|
(when active-cursor-position
|
2014-12-24 03:01:18 +00:00
|
|
|
(tty-goto t (car active-cursor-position) (cadr active-cursor-position)))
|
|
|
|
(tty-flush t))
|