#lang debug racket/base (require (for-syntax racket/base) racket/match racket/path racket/dict pollen/tag racket/system racket/class racket/string syntax/strip-context scribble/reader quadwriter/core txexpr quad/log "log.rkt") (provide (all-defined-out)) (define q (default-tag-function 'q)) (define ((make-read-syntax expander-mod pt-proc) path-string p) ;; peel off any lines of format #:keyword val (bounded by newline) ;; and turn them into qexpr attrs (define kw-attrs (let loop ([kw-val-pairs null]) (cond [(regexp-try-match #px"^\\s+" p) (loop kw-val-pairs)] [(equal? (peek-bytes 2 0 p) #"#:") ; discovered a keyword (parameterize ([current-input-port (open-input-string (read-line p))]) (match (list (read) (read)) [(list (? keyword? kw) val) #:when (not (eof-object? val)) (loop (cons (cons kw val) kw-val-pairs))] [_ (loop kw-val-pairs)]))] ;; reverse in case of multiple values with same keyword, latest takes precedence (by becoming first) [else (reverse (for/list ([(kw val) (in-dict kw-val-pairs)]) (list (string->symbol (string-trim (keyword->string kw) "#:")) (format "~a" val))))]))) (strip-context (with-syntax ([PATH-STRING path-string] [((ATTR-NAME ATTR-VAL) ...) kw-attrs] [PT (pt-proc path-string p)] [EXPANDER-MOD expander-mod]) #'(module _ EXPANDER-MOD PATH-STRING ((ATTR-NAME ATTR-VAL) ...) . PT)))) (define-syntax-rule (make-module-begin DOC-PROC) (begin (provide (rename-out [new-module-begin #%module-begin])) (define-syntax (new-module-begin stx) (syntax-case stx () [(_ PATH-STRING ATTRS . EXPRS) (with-syntax ([DOC (datum->syntax #'PATH-STRING 'doc)] [VIEW-OUTPUT (datum->syntax #'PATH-STRING 'view-output)]) #'(#%module-begin (provide DOC VIEW-OUTPUT) (define attrs (let ([h (make-hasheq)]) (for ([kvlist (in-list 'ATTRS)]) (apply hash-set! h kvlist)) h)) (define pdf-path (hash-ref! attrs 'output-path (λ () (path-string->pdf-path-string 'PATH-STRING)))) (define DOC `(q ,(for/list ([(k v) (in-hash attrs)]) (list k v)) ,(DOC-PROC (list . EXPRS)))) (define (VIEW-OUTPUT) (when (file-exists? pdf-path) (define open-string (case (system-type 'os) [(macosx) "open '~a'"] [(windows) "start '~a'"] [(unix) "xdg-open '~a' &> /dev/null"] [else (error "Unknown platform. Don't know how to view PDF.")])) (void (system (format open-string pdf-path))))) (define (make-pdf [the-pdf-path #false]) (with-logging-to-port (current-output-port) (λ () (with-logging-to-port (current-output-port) (λ () (render-pdf DOC the-pdf-path)) #:logger quadwriter-logger 'debug)) #:logger quad-logger 'debug)) (module+ pdf (define pdf (make-pdf pdf-path)) (provide pdf)) (module+ main (make-pdf pdf-path))))])))) (define (path-string->pdf-path-string path-string) (path->string (match (format "~a" path-string) ;; weird test but sometimes DrRacket calls the unsaved file ;; 'unsaved-editor and sometimes "unsaved editor" [(regexp #rx"unsaved.editor") (build-path (find-system-path 'desk-dir) "untitled.pdf")] [_ (define path-without-ext (path-replace-extension path-string #"")) ;; do the right thing with pollen-style double extension like "foo.pdf.pm" (if (path-has-extension? path-without-ext #".pdf") path-without-ext (path-add-extension path-without-ext #".pdf"))]))) (define quad-at-reader (make-at-reader #:syntax? #t #:inside? #t #:command-char #\◊)) (define (xexpr->parse-tree x) ;; an ordinary txexpr can't serve as a parse tree because of the attrs list fails when passed to #%app. ;; so stick an `attr-list` identifier on it which can hook into the expander. ;; sort of SXML-ish. (let loop ([x x]) (match x [(txexpr tag attrs elems) (list* tag (cons 'attr-list attrs) (map loop elems))] [(? list? xs) (map loop xs)] [_ x]))) (define (get-info-texty in mod line col pos) ;; DrRacket caches source file information per session, ;; so we can do the same to avoid multiple searches for the command char. (define command-char-cache (make-hash)) (define my-command-char #\◊) (λ (key default) (case key [(color-lexer) (match (dynamic-require 'syntax-color/scribble-lexer 'make-scribble-inside-lexer (λ () #false)) [(? procedure? make-lexer) (make-lexer #:command-char my-command-char)] [_ default])] [(drracket:toolbar-buttons) (match (dynamic-require 'pollen/private/drracket-buttons 'make-drracket-buttons (λ () #false)) [(? procedure? make-buttons) (make-buttons my-command-char)])] [(drracket:indentation) (λ (text pos) (define line-idx (send text position-line pos)) (define line-start-pos (send text line-start-position line-idx)) (define line-end-pos (send text line-end-position line-idx)) (define first-vis-pos (or (for/first ([pos (in-range line-start-pos line-end-pos)] #:unless (char-blank? (send text get-character pos))) pos) line-start-pos)) (- first-vis-pos line-start-pos))] [else default]))) (define (get-info-rackety in mod line col pos) (λ (key default) default))