798 lines
28 KiB
Racket
798 lines
28 KiB
Racket
#lang racket/base
|
|
(require racket/class
|
|
"../syntax.rkt"
|
|
"private.rkt"
|
|
racket/snip/private/private
|
|
racket/snip/private/snip
|
|
"editor-data.rkt"
|
|
(only-in "cycle.rkt"
|
|
set-editor-stream-in%!
|
|
set-editor-stream-out%!))
|
|
|
|
(provide editor-stream-in%
|
|
editor-stream-out%
|
|
editor-stream-in-base%
|
|
editor-stream-in-bytes-base%
|
|
editor-stream-in-file-base%
|
|
editor-stream-out-base%
|
|
editor-stream-out-bytes-base%
|
|
editor-stream-out-file-base%)
|
|
|
|
;; ----------------------------------------
|
|
|
|
(defclass editor-stream% object%
|
|
|
|
(super-new)
|
|
|
|
(define scl (get-the-snip-class-list))
|
|
(define bdl (get-the-editor-data-class-list))
|
|
(define/public (get-s-scl) scl)
|
|
(define/public (get-s-bdl) bdl)
|
|
|
|
(define sl null)
|
|
(define dl null)
|
|
|
|
(define/public (get-sl) sl)
|
|
(define/public (get-dl) dl)
|
|
(define/public (set-sl n) (set! sl n))
|
|
(define/public (set-dl n) (set! dl n))
|
|
(define/public (add-sl v) (set! sl (cons v sl)))
|
|
(define/public (add-dl v) (set! dl (cons v dl)))
|
|
|
|
(define sll null)
|
|
(define style-count 0)
|
|
(define/public (get-s-sll) sll)
|
|
(define/public (set-s-sll v) (set! sll v))
|
|
(define/public (get-s-style-count) style-count)
|
|
(define/public (set-s-style-count v) (set! style-count v))
|
|
|
|
(define/public (do-reading-version sclass)
|
|
(or (ormap (lambda (scl)
|
|
(and (eq? (snip-class-link-c scl) sclass)
|
|
(snip-class-link-reading-version scl)))
|
|
sl)
|
|
;; Class didn't show up in the header?
|
|
;; Assume we're reading the current version.
|
|
(send sclass get-version)))
|
|
|
|
(define/public (do-map-position sclass-or-dclass)
|
|
(if (sclass-or-dclass . is-a? . snip-class%)
|
|
(or (ormap (lambda (scl)
|
|
(and (eq? (snip-class-link-c scl) sclass-or-dclass)
|
|
(snip-class-link-map-position scl)))
|
|
sl)
|
|
-1)
|
|
(or (ormap (lambda (dcl)
|
|
(and (eq? (editor-data-class-link-c dcl) sclass-or-dclass)
|
|
(editor-data-class-link-map-position dcl)))
|
|
dl)
|
|
-1)))
|
|
|
|
(define/public (do-get-header-flag sclass)
|
|
(or (ormap (lambda (scl)
|
|
(and (eq? (snip-class-link-c scl) sclass)
|
|
(snip-class-link-header-flag scl)))
|
|
sl)
|
|
0))
|
|
|
|
(define/public (do-set-header-flag sclass)
|
|
(ormap (lambda (scl)
|
|
(and (eq? (snip-class-link-c scl) sclass)
|
|
(begin
|
|
(set-snip-class-link-header-flag! scl #t)
|
|
#t)))
|
|
sl)
|
|
(void)))
|
|
|
|
;; ----------------------------------------
|
|
|
|
(defclass editor-stream-in-base% object%
|
|
(super-new)
|
|
(def/public (tell) 0)
|
|
(def/public (seek [exact-nonnegative-integer? i]) (void))
|
|
(def/public (skip [exact-nonnegative-integer? i]) (void))
|
|
(def/public (bad?) #t)
|
|
(def/public (read [vector? v])
|
|
(let ([s (make-bytes (vector-length v))])
|
|
(let ([n (read-bytes s)])
|
|
(for ([i (in-range n)])
|
|
(vector-set! v i (integer->char (bytes-ref s i))))
|
|
n)))
|
|
(def/public (read-bytes [bytes? v]
|
|
[exact-nonnegative-integer? [start 0]]
|
|
[exact-nonnegative-integer? [end (bytes-length v)]])
|
|
0)
|
|
(def/public (read-byte)
|
|
(let ([s (make-bytes 1)])
|
|
(and (= 1 (read-bytes s 0 1))
|
|
(bytes-ref s 0)))))
|
|
|
|
(defclass editor-stream-out-base% object%
|
|
(super-new)
|
|
(def/public (tell) 0)
|
|
(def/public (seek [exact-nonnegative-integer? i]) (void))
|
|
(def/public (skip [exact-nonnegative-integer? i]) (void))
|
|
(def/public (bad?) #t)
|
|
(def/public (write [(make-list char?) v])
|
|
(write-bytes (string->bytes/latin-1 (list->string v) (char->integer #\?))))
|
|
(def/public (write-bytes [bytes? v]
|
|
[exact-nonnegative-integer? [start 0]]
|
|
[exact-nonnegative-integer? [end (bytes-length v)]])
|
|
(void)))
|
|
|
|
;; ----------------------------------------
|
|
|
|
(define mz:read-byte read-byte)
|
|
|
|
(defclass editor-stream-in-port-base% editor-stream-in-base%
|
|
(init-field port)
|
|
(super-new)
|
|
|
|
(def/override (tell)
|
|
(file-position port))
|
|
|
|
(def/override (seek [exact-nonnegative-integer? i])
|
|
(file-position port i))
|
|
|
|
(def/override (skip [exact-nonnegative-integer? i])
|
|
(file-position port (+ i (file-position port))))
|
|
|
|
(def/override (bad?) #f)
|
|
|
|
(def/override (read-bytes [bytes? v]
|
|
[exact-nonnegative-integer? [start 0]]
|
|
[exact-nonnegative-integer? [end (bytes-length v)]])
|
|
(let ([r (read-bytes! v port start end)])
|
|
(if (eof-object? r)
|
|
0
|
|
r)))
|
|
|
|
(def/override (read-byte)
|
|
(let ([v (mz:read-byte port)])
|
|
(if (eof-object? v) #f v))))
|
|
|
|
(defclass editor-stream-in-file-base% editor-stream-in-port-base%
|
|
(super-new))
|
|
|
|
(defclass editor-stream-in-bytes-base% editor-stream-in-port-base%
|
|
(init s)
|
|
(super-new [port (open-input-bytes s)]))
|
|
|
|
;; ----------------------------------------
|
|
|
|
(define write-bytes-proc write-bytes)
|
|
|
|
(defclass editor-stream-out-port-base% editor-stream-out-base%
|
|
(init-field port)
|
|
(super-new)
|
|
|
|
(def/override (tell)
|
|
(file-position port))
|
|
|
|
(def/override (seek [exact-nonnegative-integer? i])
|
|
(file-position port i))
|
|
|
|
(def/override (skip [exact-nonnegative-integer? i])
|
|
(file-position port (+ i (file-position port))))
|
|
|
|
(def/override (bad?) #f)
|
|
|
|
(def/override (write-bytes [bytes? v]
|
|
[exact-nonnegative-integer? [start 0]]
|
|
[exact-nonnegative-integer? [end (bytes-length v)]])
|
|
(write-bytes-proc v port start end)
|
|
(void)))
|
|
|
|
(defclass editor-stream-out-file-base% editor-stream-out-port-base%
|
|
(super-new))
|
|
|
|
(defclass editor-stream-out-bytes-base% editor-stream-out-port-base%
|
|
(define s (open-output-bytes))
|
|
(super-new [port s])
|
|
|
|
(def/public (get-bytes)
|
|
(get-output-bytes s)))
|
|
|
|
;; ----------------------------------------
|
|
|
|
(define in-read-byte (generic editor-stream-in-base% read-byte))
|
|
|
|
(defclass editor-stream-in% editor-stream%
|
|
(init-rest args)
|
|
|
|
(define f
|
|
(case-args
|
|
args
|
|
[([editor-stream-in-base% base]) base]
|
|
(init-name 'editor-stream-in%)))
|
|
|
|
(define boundaries null)
|
|
(define is-bad? #f)
|
|
(define items 0)
|
|
(define pos-map (make-hash))
|
|
|
|
(define read-version 8)
|
|
(define s-read-version #"08")
|
|
|
|
(super-new)
|
|
|
|
(define/public (set-s-read-version bstr)
|
|
(set! s-read-version bstr)
|
|
(set! read-version (or (string->number (bytes->string/utf-8 bstr)) 0)))
|
|
(define/public (get-wxme-version) read-version)
|
|
|
|
(define s-read-format #"WXME")
|
|
(define/public (set-s-read-format bstr)
|
|
(set! s-read-format bstr))
|
|
(define/public (get-s-read-format)
|
|
s-read-format)
|
|
|
|
(define/private (do-skip-whitespace)
|
|
(define (bad!) (set! is-bad? #t) 0)
|
|
(if is-bad?
|
|
0
|
|
(let loop ([prev-byte 0])
|
|
(let ([b (send-generic f in-read-byte)])
|
|
(if (not b)
|
|
(bad!)
|
|
(case (integer->char b)
|
|
[(#\#)
|
|
(let ([pos (send f tell)]
|
|
[b (send-generic f in-read-byte)])
|
|
(if (and b
|
|
(= b (char->integer #\|)))
|
|
;; skip to end of comment
|
|
(let cloop ([saw-bar? #f]
|
|
[saw-hash? #f]
|
|
[nesting 0])
|
|
(let ([b (send-generic f in-read-byte)])
|
|
(if (not b)
|
|
(bad!)
|
|
(cond
|
|
[(and saw-bar? (= b (char->integer #\#)))
|
|
(if (zero? nesting)
|
|
(loop (char->integer #\space))
|
|
(cloop #f #f (sub1 nesting)))]
|
|
[(and saw-hash? (= b (char->integer #\|)))
|
|
(cloop #t #f (add1 nesting))]
|
|
[else (cloop (= b (char->integer #\|))
|
|
(= b (char->integer #\#))
|
|
nesting)]))))
|
|
(begin
|
|
(send f seek pos)
|
|
(char->integer #\#))))]
|
|
[(#\;)
|
|
;; skip to end of comment
|
|
(let cloop ()
|
|
(let ([b (send-generic f in-read-byte)])
|
|
(if (not b)
|
|
(bad!)
|
|
(if (or (= b (char->integer #\newline))
|
|
(= b (char->integer #\return)))
|
|
(loop (char->integer #\space))
|
|
(cloop)))))]
|
|
[else
|
|
(if (char-whitespace? (integer->char b))
|
|
(loop b)
|
|
b)]))))))
|
|
|
|
(define/private (skip-whitespace [buf #f])
|
|
(let ([c (do-skip-whitespace)])
|
|
(when buf
|
|
(bytes-set! buf 0 c))
|
|
c))
|
|
|
|
(define/private (is-delim? b)
|
|
(cond
|
|
[(char-whitespace? (integer->char b)) #t]
|
|
[(= b (char->integer #\#))
|
|
(let ([pos (send f tell)]
|
|
[b (send-generic f in-read-byte)])
|
|
(let ([d? (= b (char->integer #\|))])
|
|
(send f seek (if d? (sub1 pos) pos))
|
|
d?))]
|
|
[(= b (char->integer #\;))
|
|
(send f seek (sub1 (send f tell)))
|
|
#t]
|
|
[else #f]))
|
|
|
|
(define/private (get-number get-exact?)
|
|
(let ([c0 (skip-whitespace)])
|
|
(if (check-boundary)
|
|
(if get-exact? 0 0.0)
|
|
(let* ([l
|
|
;; As fast path, accum integer result
|
|
(let loop ([counter 50][c c0][v 0])
|
|
(if (zero? counter)
|
|
null
|
|
(if (or (not c)
|
|
(is-delim? c))
|
|
(or v null)
|
|
(let ([rest (loop (sub1 counter)
|
|
(send-generic f in-read-byte)
|
|
(and v
|
|
(c . >= . (char->integer #\0))
|
|
(c . <= . (char->integer #\9))
|
|
(+ (* v 10) (- c (char->integer #\0)))))])
|
|
(if (exact-integer? rest)
|
|
rest
|
|
(cons (integer->char c) rest))))))])
|
|
(inc-item-count)
|
|
(let ([n (if (exact-integer? l)
|
|
l
|
|
(string->number (list->string l)))])
|
|
(cond
|
|
[(and get-exact? (exact-integer? n)) n]
|
|
[(real? n) (exact->inexact n)]
|
|
[else
|
|
(set! is-bad? #t)
|
|
(if get-exact? 0 0.0)]))))))
|
|
|
|
(define/private (get-a-string limit recur?)
|
|
(let* ([orig-len (if recur?
|
|
(if (limit . < . 16)
|
|
limit
|
|
16)
|
|
(let ([v (get-exact)])
|
|
(if (check-boundary)
|
|
0
|
|
v)))]
|
|
[buf (make-bytes 32)]
|
|
[fail (lambda ()
|
|
(set! is-bad? #t)
|
|
#"")])
|
|
(if recur?
|
|
(bytes-set! buf 0 (char->integer #\#))
|
|
(begin
|
|
(skip-whitespace buf)
|
|
(when is-bad?
|
|
(bytes-set! buf 0 0))))
|
|
(cond
|
|
[(= (bytes-ref buf 0) (char->integer #\#))
|
|
(if (and (= (send f read-bytes buf 1 2) 1)
|
|
(= (bytes-ref buf 1) (char->integer #\")))
|
|
(let-values ([(si s) (make-pipe)]
|
|
[(tmp) (make-bytes (+ orig-len 2))])
|
|
(display "#\"" s)
|
|
(let loop ([get-amt (add1 orig-len)]) ;; add 1 for closing quote
|
|
(let ([got-amt (send f read-bytes tmp 0 get-amt)])
|
|
(if (not (= got-amt get-amt))
|
|
(fail)
|
|
(begin
|
|
(write-bytes tmp s 0 got-amt)
|
|
(let ([done?
|
|
(let loop ([i 0])
|
|
(cond
|
|
[(= i got-amt) #f]
|
|
[(= (bytes-ref tmp i) (char->integer #\")) #t]
|
|
[(= (bytes-ref tmp i) (char->integer #\\))
|
|
(if (= (add1 i) got-amt)
|
|
;; need to read escaped character
|
|
(if (not (= (send f read-bytes tmp got-amt (add1 got-amt)) 1))
|
|
(fail)
|
|
(begin
|
|
(write-bytes tmp s got-amt (add1 got-amt))
|
|
#f))
|
|
(loop (+ i 2)))]
|
|
[else (loop (+ i 1))]))])
|
|
(if done?
|
|
(begin
|
|
(close-output-port s)
|
|
(unless recur? (inc-item-count))
|
|
(let ([s (with-handlers ([exn:fail:read? (lambda (x) #f)])
|
|
(read si))])
|
|
(when (and recur? s)
|
|
;; It's ok to have extra whitespace when reading a byte
|
|
;; string in a sequence
|
|
(let loop ()
|
|
(define c (peek-byte si))
|
|
(unless (eof-object? c)
|
|
(when (char-whitespace? (integer->char c))
|
|
(read-byte si)
|
|
(loop)))))
|
|
(if (or (not s)
|
|
(not (eof-object? (read-byte si))))
|
|
(fail)
|
|
(if (if recur?
|
|
((bytes-length s) . <= . limit)
|
|
(= (bytes-length s) orig-len))
|
|
s
|
|
(fail)))))
|
|
(loop 1))))))))
|
|
(fail))]
|
|
[(and (not recur?) (= (bytes-ref buf 0) (char->integer #\()))
|
|
;; read a sequence of strings
|
|
(let loop ([accum null]
|
|
[left-to-get orig-len])
|
|
(skip-whitespace buf)
|
|
(if (or is-bad?
|
|
(negative? left-to-get))
|
|
(fail)
|
|
(cond
|
|
[(= (bytes-ref buf 0) (char->integer #\)))
|
|
;; got all byte strings
|
|
(if (zero? left-to-get)
|
|
(begin
|
|
(inc-item-count)
|
|
(apply bytes-append (reverse accum)))
|
|
(fail))]
|
|
[(= (bytes-ref buf 0) (char->integer #\#))
|
|
(let ([v (get-a-string left-to-get #t)])
|
|
(if is-bad?
|
|
(fail)
|
|
(loop (cons v accum)
|
|
(- left-to-get (bytes-length v)))))]
|
|
[else (fail)])))]
|
|
[else (fail)])))
|
|
|
|
(define/private (inc-item-count)
|
|
(set! items (add1 items))
|
|
(tell))
|
|
|
|
(define/private (skip-one recur?)
|
|
(let ([buf (make-bytes 1)]
|
|
[fail (lambda () (set! is-bad? #t) (void))]
|
|
[success (lambda () (unless recur? (inc-item-count)))])
|
|
(if recur?
|
|
(bytes-set! buf 0 (char->integer #\#))
|
|
(skip-whitespace buf))
|
|
(unless is-bad?
|
|
(cond
|
|
[(= (bytes-ref buf 0) (char->integer #\#))
|
|
;; byte string
|
|
(if (and (= 1 (send f read-bytes buf))
|
|
(= (bytes-ref buf 0) (char->integer #\")))
|
|
(let loop ()
|
|
(if (= 1 (send f read-bytes buf))
|
|
(cond
|
|
[(= (bytes-ref buf 0) (char->integer #\\))
|
|
(if (= 1 (send f read-bytes buf))
|
|
(loop)
|
|
(fail))]
|
|
[(= (bytes-ref buf 0) (char->integer #\"))
|
|
(success)]
|
|
[else (loop)])
|
|
(fail)))
|
|
(fail))]
|
|
[(= (bytes-ref buf 0) (char->integer #\)))
|
|
;; list of byte strings
|
|
(let loop ()
|
|
(if is-bad?
|
|
(fail)
|
|
(if (not (= (send f read-bytes buf) 1))
|
|
(fail)
|
|
(if (is-delim? (bytes-ref buf 0))
|
|
(cond
|
|
[(= (bytes-ref buf 0) (char->integer #\)))
|
|
(success)]
|
|
[(= (bytes-ref buf 0) (char->integer #\#))
|
|
(skip-one #t)
|
|
(loop)]
|
|
[else (fail)])
|
|
(loop)))))]
|
|
[else
|
|
;; number -- skip anything delimited
|
|
(let loop ()
|
|
(if (not (= (send f read-bytes buf) 1))
|
|
(fail)
|
|
(if (is-delim? (bytes-ref buf 0))
|
|
(success)
|
|
(loop))))]))))
|
|
|
|
(def/public (get-fixed-exact)
|
|
(if (check-boundary)
|
|
0
|
|
(if (read-version . < . 8)
|
|
(let ([buf (make-bytes 4)])
|
|
(send f read-bytes buf)
|
|
(integer-bytes->integer
|
|
buf
|
|
#t
|
|
(if (= read-version 1)
|
|
(system-big-endian?)
|
|
#t)))
|
|
(get-exact))))
|
|
|
|
(def/public (get-fixed [box? vb])
|
|
(set-box! vb (get-fixed-exact)))
|
|
|
|
#|
|
|
integer format specified by first byte:
|
|
bit 8: 0 - read 7-bit (positive) number
|
|
bit 8: 1 - ...
|
|
bit 7: 0 - read abother byte for 15-bit (positive) number
|
|
bit 7: 1 - negative and long numbers...
|
|
bit 1: 1 - read another 8-bit (signed) number
|
|
bit 1: 0 - ...
|
|
bit 2: 1 - read another 16-bit (signed) number
|
|
bit 2: 0 - read another 32-bit (signed) number
|
|
|#
|
|
|
|
(def/public (get-exact)
|
|
(if (check-boundary)
|
|
0
|
|
(if (read-version . < . 8)
|
|
(let ([buf (make-bytes 4)]
|
|
[fail (lambda () (set! is-bad? #t) 0)])
|
|
(if (not (= 1 (send f read-bytes buf 0 1)))
|
|
(fail)
|
|
(let ([b (bytes-ref buf 0)])
|
|
(if (positive? (bitwise-and b #x80))
|
|
(if (positive? (bitwise-and b #x40))
|
|
(cond
|
|
[(positive? (bitwise-and b #x01))
|
|
(if (= 1 (send f read-bytes buf 0 1))
|
|
(let ([b (bytes-ref buf 0)])
|
|
(if (b . > . 127)
|
|
(- b 256)
|
|
b))
|
|
(fail))]
|
|
[(positive? (bitwise-and b #x02))
|
|
(if (= 2 (send f read-bytes buf 0 2))
|
|
(integer-bytes->integer buf #t #t 0 2)
|
|
(fail))]
|
|
[else
|
|
(if (= 4 (send f read-bytes buf 0 4))
|
|
(integer-bytes->integer buf #t #t)
|
|
(fail))])
|
|
(if (= 1 (send f read-bytes buf 0 1))
|
|
(+ (arithmetic-shift (bitwise-and b #x3F) 8)
|
|
(bytes-ref buf 0))
|
|
(fail)))
|
|
b))))
|
|
(get-number #t))))
|
|
|
|
(def/public (get-inexact)
|
|
(if (check-boundary)
|
|
0
|
|
(if (read-version . < . 8)
|
|
(let ([buf (make-bytes 8)])
|
|
(send f read-bytes buf)
|
|
(floating-point-bytes->real
|
|
buf
|
|
(if (= read-version 1)
|
|
(system-big-endian?)
|
|
#t)))
|
|
(get-number #f))))
|
|
|
|
(define/private (do-get-bytes)
|
|
(if (check-boundary)
|
|
#""
|
|
(if (read-version . < . 8)
|
|
(let* ([len (get-exact)]
|
|
[s (make-bytes len)])
|
|
(send f read-bytes s)
|
|
s)
|
|
(get-a-string #f #f))))
|
|
|
|
(def/public (get-bytes [maybe-box? [len #f]])
|
|
(let ([s (do-get-bytes)])
|
|
(when len
|
|
(set-box! len (max 1 (bytes-length s))))
|
|
(subbytes s 0 (max 0 (sub1 (bytes-length s))))))
|
|
|
|
(def/public (get-unterminated-bytes [maybe-box? [len #f]])
|
|
(let ([s (do-get-bytes)])
|
|
(when len
|
|
(set-box! len (bytes-length s)))
|
|
s))
|
|
|
|
(def/public (get-unterminated-bytes! [(make-box exact-nonnegative-integer?) len]
|
|
[(lambda (s) (and (bytes? s) (not (immutable? s)))) s])
|
|
(let ([s2 (do-get-bytes)])
|
|
(if ((bytes-length s2) . <= . (unbox len))
|
|
(begin
|
|
(bytes-copy! s 0 s2)
|
|
(set-box! len (bytes-length s2)))
|
|
(set! is-bad? #t))))
|
|
|
|
(def/public (get [(make-box real?) b])
|
|
(unless (check-boundary)
|
|
(if (exact-integer? (unbox b))
|
|
(set-box! b (get-exact))
|
|
(set-box! b (get-inexact)))))
|
|
|
|
(def/public (set-boundary [exact-nonnegative-integer? n])
|
|
(set! boundaries (cons (+ (tell) n) boundaries)))
|
|
|
|
(def/public (remove-boundary)
|
|
(set! boundaries (cdr boundaries)))
|
|
|
|
(define/private (check-boundary)
|
|
(if is-bad?
|
|
#t
|
|
(cond
|
|
[(and (pair? boundaries)
|
|
(items . >= . (car boundaries)))
|
|
(set! is-bad? #t)
|
|
(error 'editor-stream-in%
|
|
"overread (caused by file corruption?; ~a vs ~a)" items (car boundaries))]
|
|
[(send f bad?)
|
|
(set! is-bad? #t)
|
|
(error 'editor-stream-in% "stream error")]
|
|
[else #f])))
|
|
|
|
(def/public (skip [exact-nonnegative-integer? n])
|
|
(if (read-version . < . 8)
|
|
(send f skip n)
|
|
(jump-to (+ n items))))
|
|
|
|
(def/public (tell)
|
|
(if (read-version . < . 8)
|
|
(send f tell)
|
|
(let ([pos (send f tell)])
|
|
(when (not (equal? (hash-ref pos-map items pos) pos))
|
|
(error "again"))
|
|
(hash-set! pos-map items pos)
|
|
items)))
|
|
|
|
(def/public (jump-to [exact-nonnegative-integer? pos])
|
|
(if (read-version . < . 8)
|
|
(send f seek pos)
|
|
(let ([p (hash-ref pos-map pos #f)])
|
|
(if (not p)
|
|
(begin
|
|
(let loop ()
|
|
(when (and (items . < . pos) (not is-bad?))
|
|
(skip-one #f)
|
|
(loop)))
|
|
(unless (= items pos)
|
|
(set! is-bad? #t)))
|
|
(begin
|
|
(send f seek p)
|
|
(set! items pos))))))
|
|
|
|
(def/public (ok?) (not is-bad?)))
|
|
|
|
(set-editor-stream-in%! editor-stream-in%)
|
|
|
|
;; ----------------------------------------
|
|
|
|
(defclass editor-stream-out% editor-stream%
|
|
(init-rest args)
|
|
|
|
(define f
|
|
(case-args
|
|
args
|
|
[([editor-stream-out-base% base]) base]
|
|
(init-name 'editor-stream-out%)))
|
|
|
|
(define is-bad? #f)
|
|
(define col 72)
|
|
(define items 0)
|
|
(define pos-map (make-hash))
|
|
|
|
(super-new)
|
|
|
|
(define/private (check-ok)
|
|
(unless is-bad?
|
|
(when (send f bad?)
|
|
(error 'editor-stream-out% "stream error"))))
|
|
|
|
(def/public (put-fixed [exact-integer? v])
|
|
(check-ok)
|
|
(let-values ([(new-col spc)
|
|
(if ((+ col 12) . > . 72)
|
|
(values 11 #"\n")
|
|
(values (+ col 12) #" "))])
|
|
(let ([s (number->string v)])
|
|
(send f
|
|
write-bytes
|
|
(bytes-append spc
|
|
(make-bytes (- 11 (string-length s)) (char->integer #\space))
|
|
(string->bytes/latin-1 s))))
|
|
(set! col new-col)
|
|
(set! items (add1 items)))
|
|
this)
|
|
|
|
(define/public (put . args)
|
|
(case-args
|
|
args
|
|
[([exact-nonnegative-integer? n][bytes? s])
|
|
(do-put-bytes (subbytes s 0 n))]
|
|
[([bytes? s])
|
|
(do-put-bytes (bytes-append s #"\0"))]
|
|
[([exact-integer? n])
|
|
(do-put-number n)]
|
|
[([real? n])
|
|
(do-put-number (exact->inexact n))]
|
|
(method-name 'editor-stream-out% 'put)))
|
|
|
|
(def/public (put-unterminated [bytes? s])
|
|
(do-put-bytes s))
|
|
|
|
(define/private (do-put-bytes orig-s)
|
|
(define (single-string)
|
|
(if ((bytes-length orig-s) . < . 72)
|
|
(let ([s (open-output-bytes)])
|
|
(write orig-s s)
|
|
(let* ([v (get-output-bytes s)]
|
|
[len (bytes-length v)])
|
|
(if (len . >= . 72)
|
|
(multiple-strings)
|
|
(begin
|
|
(if ((+ col len 1) . > . 72)
|
|
(send f write-bytes #"\n")
|
|
(send f write-bytes #" "))
|
|
(send f write-bytes v)
|
|
(set! col 72))))) ;; forcing a newline after every string makes the file more readable
|
|
(multiple-strings)))
|
|
(define (multiple-strings)
|
|
(send f write-bytes #"\n(")
|
|
(let loop ([offset 0][remain (bytes-length orig-s)])
|
|
(unless (zero? remain)
|
|
(let lloop ([amt (min 50 remain)][retry? #t])
|
|
(let ([s (open-output-bytes)])
|
|
(write (subbytes orig-s offset (+ offset amt)) s)
|
|
(let* ([v (get-output-bytes s #t)]
|
|
[len (bytes-length v)])
|
|
(if (len . <= . 71)
|
|
(if (and (len . < . 71)
|
|
retry?
|
|
(amt . < . remain))
|
|
(lloop (add1 amt) #t)
|
|
(begin
|
|
(send f write-bytes #"\n ")
|
|
(send f write-bytes v)
|
|
(loop (+ offset amt) (- remain amt))))
|
|
(lloop (quotient amt 2) #f)))))))
|
|
(send f write-bytes #"\n)")
|
|
(set! col 1))
|
|
|
|
(check-ok)
|
|
(do-put-number (bytes-length orig-s))
|
|
(single-string)
|
|
(set! items (add1 items))
|
|
this)
|
|
|
|
(define/private (do-put-number v)
|
|
(check-ok)
|
|
(let* ([s (string->bytes/latin-1 (format " ~a" v))]
|
|
[len (bytes-length s)])
|
|
(if ((+ col len) . > . 72)
|
|
(begin
|
|
(set! col (sub1 len))
|
|
(bytes-set! s 0 (char->integer #\newline)))
|
|
(set! col (+ col len)))
|
|
(send f write-bytes s)
|
|
(set! items (add1 items))
|
|
this))
|
|
|
|
(def/public (tell)
|
|
(let ([pos (send f tell)])
|
|
(hash-set! pos-map items (cons pos col))
|
|
items))
|
|
|
|
(def/public (jump-to [exact-nonnegative-integer? icount])
|
|
(unless is-bad?
|
|
(let ([p (hash-ref pos-map icount #f)])
|
|
(when p
|
|
(send f seek (car p))
|
|
(set! col (cdr p))
|
|
(set! items icount)))))
|
|
|
|
(def/public (ok?) (not is-bad?))
|
|
|
|
(def/public (pretty-finish)
|
|
(unless is-bad?
|
|
(when (positive? col)
|
|
(send f write-bytes #"\n")
|
|
(set! col 0))))
|
|
|
|
(def/public (pretty-start)
|
|
(define (show s)
|
|
(send f write-bytes (if (string? s) (string->bytes/latin-1 s) s)))
|
|
(when (positive? col)
|
|
(show #"\n"))
|
|
(show #"#|\n This file uses the GRacket editor format.\n")
|
|
(show (format " Open this file in DrRacket version ~a or later to read it.\n" (version)))
|
|
(show #"\n")
|
|
(show #" Most likely, it was created by saving a program in DrRacket,\n")
|
|
(show #" and it probably contains a program with non-text elements\n")
|
|
(show #" (such as images or comment boxes).\n")
|
|
(show #"\n")
|
|
(show #" http://racket-lang.org/\n|#\n")
|
|
(set! col 0)))
|
|
|
|
(set-editor-stream-out%! editor-stream-out%)
|