(module syntax-snip mzscheme (require (lib "class.ss") (lib "match.ss") (lib "list.ss") (lib "mred.ss" "mred") (lib "framework.ss" "framework") (lib "string.ss") "interfaces.ss" "partition.ss" "typesetter.ss" "widget.ss" "syntax-browser.ss") (provide syntax-snip snip-class syntax-value-snip% syntax-snip%) ;; syntax-snip : syntax -> snip (define (syntax-snip stx) (new syntax-snip% (syntax stx))) (define current-syntax-controller (make-parameter #f)) (define (the-syntax-controller) (let ([controller (current-syntax-controller)]) (or controller (let ([controller (new syntax-controller%)]) (current-syntax-controller controller) controller)))) ;; syntax-value-snip% (define syntax-value-snip% (class* editor-snip% (readable-snip<%>) (init-field ((stx syntax))) (init-field controller) (inherit set-margin set-inset) (define -outer (new text:standard-style-list%)) (super-new (editor -outer) (with-border? #f)) (set-margin 0 0 0 0) (set-inset 2 2 2 2) (refresh) (define/private (refresh) (send -outer begin-edit-sequence) (send -outer erase) (new typesetter-for-text% (syntax stx) (controller controller) (text -outer)) (send -outer lock #t) (send -outer end-edit-sequence) (send -outer hide-caret #t)) (define/private (show-props) (send (send controller get-properties-controller) show #t)) (define/private outer:insert (case-lambda [(obj) (outer:insert obj style:normal)] [(text style) (outer:insert text style #f)] [(text style clickback) (let ([start (send -outer last-position)]) (send -outer insert text) (let ([end (send -outer last-position)]) (send -outer change-style style start end #f) (when clickback (send -outer set-clickback start end clickback))))])) ;; BEGIN COPIED from widget.ss ;; WITH MODIFICATIONS ;; Set up keymap (let ([keymap (send -outer get-keymap)]) (send keymap map-function "rightbutton" "popup-context-window") (send keymap add-function "popup-context-window" (lambda (editor event) (do-popup-context-window editor event)))) (define/private (do-popup-context-window editor event) (define-values (x y) (send editor dc-location-to-editor-location (send event get-x) (send event get-y))) (define admin (send editor get-admin)) (send admin popup-menu context-menu x y)) (define context-menu (let ([context-menu (new popup-menu%)]) (new menu-item% (label "Copy") (parent context-menu) (callback (lambda (i e) (define stx (send controller get-selected-syntax)) (send the-clipboard set-clipboard-string (if stx (format "~s" (syntax-object->datum stx)) "") (send e get-time-stamp))))) ;; ADDED (new menu-item% (label "Copy syntax") (parent context-menu) (callback (lambda (i e) (define stx (send controller get-selected-syntax)) (define t (new text%)) (send t insert (new syntax-snip% (syntax stx) (controller controller))) (send t select-all) (send t copy)))) ;; FIXME: Add option for "formatted" copy/paste? (new menu-item% (label "Clear selection") (parent context-menu) (callback (lambda _ (send controller select-syntax #f)))) (new separator-menu-item% (parent context-menu)) ;; properties (MODIFIED) (new menu-item% (label "Show syntax properties") (parent context-menu) (callback (lambda _ (show-props)))) ;; syntax browser (ADDED) (new menu-item% (label "Show in browser frame") (parent context-menu) (callback (lambda _ (browse-syntax stx)))) ;; primary selection (let ([secondary (new menu% (label "identifier=?") (parent context-menu))]) (for-each (lambda (name func) (let ([this-choice (new checkable-menu-item% (label name) (parent secondary) (callback (lambda (i e) (send controller on-update-identifier=? name func))))]) (send controller add-identifier=?-listener (lambda (new-name new-id=?) (send this-choice check (eq? name new-name)))))) (map car (identifier=-choices)) (map cdr (identifier=-choices)))) context-menu)) ;; END COPIED ;; snip% Methods (define/override (copy) (new syntax-value-snip% (controller controller) (syntax stx))) (define/public (read-special src line col pos) (datum->syntax-object #f `(,#'quote-syntax ,stx) (list src line col pos 1))) )) (define syntax-snip% (class* editor-snip% (readable-snip<%>) (init-field ((stx syntax))) (init-field (controller (the-syntax-controller))) (inherit set-margin set-inset set-snipclass set-tight-text-fit show-border) (define -outer (new text%)) (super-new (editor -outer) (with-border? #f)) (set-margin 2 0 0 0) (set-inset 3 0 0 0) (set-snipclass snip-class) (define the-syntax-snip (new syntax-value-snip% (syntax stx) (controller controller))) (define the-summary (let ([line (syntax-line stx)] [col (syntax-column stx)]) (if (and line col) (format "#" line col) "#"))) (define/private (hide-me) (send* -outer (begin-edit-sequence) (lock #f) (erase)) (set-tight-text-fit #t) (show-border #f) (outer:insert (show-icon) style:hyper (lambda _ (show-me))) (outer:insert the-summary) (send* -outer (lock #t) (end-edit-sequence))) (define/private (show-me) (send* -outer (begin-edit-sequence) (lock #f) (erase)) (set-tight-text-fit #f) (show-border #t) (outer:insert (hide-icon) style:hyper (lambda _ (hide-me))) (outer:insert " ") (outer:insert the-syntax-snip) (send* -outer (lock #t) (end-edit-sequence))) (define/private outer:insert (case-lambda [(obj) (outer:insert obj style:normal)] [(text style) (outer:insert text style #f)] [(text style clickback) (let ([start (send -outer last-position)]) (send -outer insert text) (let ([end (send -outer last-position)]) (send -outer change-style style start end #f) (when clickback (send -outer set-clickback start end clickback))))])) ;; Snip methods (define/override (copy) (new syntax-snip% (controller controller) (syntax stx))) (define/override (write stream) (send stream put (string->bytes/utf-8 (format "~s" (marshall-syntax stx))))) (define/public (read-special src line col pos) (send the-syntax-snip read-special src line col pos)) (hide-me) (send -outer hide-caret #t) (send -outer lock #t) )) (define style:normal (make-object style-delta% 'change-normal)) (define style:hyper (let ([s (make-object style-delta% 'change-normal)]) (send s set-delta 'change-toggle-underline) (send s set-delta-foreground "blue") s)) (define style:bold (let ([s (make-object style-delta% 'change-normal)]) (send s set-delta 'change-bold) s)) (define (show-icon) (make-object image-snip% (build-path (collection-path "icons") "turn-up.png"))) (define (hide-icon) (make-object image-snip% (build-path (collection-path "icons") "turn-down.png"))) ;; COPIED AND MODIFIED from mrlib/syntax-browser.ss (define syntax-snipclass% (class snip-class% (define/override (read stream) (let ([str (send stream get-bytes)]) (make-object syntax-snip% (unmarshall-syntax (read-from-string (bytes->string/utf-8 str)))))) (super-instantiate ()))) (define snip-class (make-object syntax-snipclass%)) (send snip-class set-version 2) (send snip-class set-classname (format "~s" '(lib "syntax-snip.ss" "macro-debugger" "syntax-browser"))) (send (get-the-snip-class-list) add snip-class) ;; marshall-syntax : syntax -> printable (define (marshall-syntax stx) (unless (syntax? stx) (error 'marshall-syntax "not syntax: ~s\n" stx)) `(syntax (source ,(marshall-object (syntax-source stx))) (source-module ,(marshall-object (syntax-source-module stx))) (position ,(syntax-position stx)) (line ,(syntax-line stx)) (column ,(syntax-column stx)) (span ,(syntax-span stx)) (original? ,(syntax-original? stx)) (properties ,@(map (λ (x) `(,x ,(marshall-object (syntax-property stx x)))) (syntax-property-symbol-keys stx))) (contents ,(marshall-object (syntax-e stx))))) ;; marshall-object : any -> printable ;; really only intended for use with marshall-syntax (define (marshall-object obj) (cond [(syntax? obj) (marshall-syntax obj)] [(pair? obj) `(pair ,(cons (marshall-object (car obj)) (marshall-object (cdr obj))))] [(or (symbol? obj) (char? obj) (number? obj) (string? obj) (boolean? obj) (null? obj)) `(other ,obj)] [else (string->symbol (format "unknown-object: ~s" obj))])) (define (unmarshall-syntax stx) (match stx [`(syntax (source ,src) (source-module ,source-module) ;; marshalling (position ,pos) (line ,line) (column ,col) (span ,span) (original? ,original?) (properties ,@(properties ...)) (contents ,contents)) (foldl add-properties (datum->syntax-object #'here ;; ack (unmarshall-object contents) (list (unmarshall-object src) line col pos span)) properties)] [else #'unknown-syntax-object])) ;; add-properties : syntax any -> syntax (define (add-properties prop-spec stx) (match prop-spec [`(,(and sym (? symbol?)) ,prop) (syntax-property stx sym (unmarshall-object prop))] [else stx])) (define (unmarshall-object obj) (let ([unknown (λ () (string->symbol (format "unknown: ~s" obj)))]) (if (and (pair? obj) (symbol? (car obj))) (case (car obj) [(pair) (if (pair? (cdr obj)) (let ([raw-obj (cadr obj)]) (if (pair? raw-obj) (cons (unmarshall-object (car raw-obj)) (unmarshall-object (cdr raw-obj))) (unknown))) (unknown))] [(other) (if (pair? (cdr obj)) (cadr obj) (unknown))] [(syntax) (unmarshall-syntax obj)] [else (unknown)]) (unknown)))))