#lang racket/gui
(require (prefix-in scrbl: scribble/reader)
racket/runtime-path
(for-syntax racket/base)
framework
)
(provide item-callback)
(define scribblings-dirs
'("slideshow"
"scribble"
"gui"
"draw"
"reference"
))
(print-as-expression #f)
(print-reader-abbreviations #t)
(define (read-scrbl in [file ""])
(scrbl:read-inside in))
(define (index-defs dic file)
(define f-in (open-input-file file))
(define all
(with-handlers ([exn:fail?
(λ _
(printf "Problem with file ~a, trying something else...\n" file)
(read-scrbl
(open-input-string
(regexp-replace* #px"#reader\\s*[^'\"\\(\\[\\{\\)\\}\\]\\s]+\\s*" (file->string file)
""))
file))])
(read-scrbl f-in)))
(define (add-entry key l)
(hash-set! dic key
(cons l (hash-ref dic key '()))))
(define (parse-class class-id subs)
(for ([s subs])
(match s
[(list-rest 'defconstructor args text)
(add-entry class-id (list 'defconstructor class-id args))]
[(list-rest 'defmethod '#:mode mode (list-rest id args) cont-out text)
(add-entry id (list 'defmethod class-id id args cont-out))]
[(list-rest 'defmethod (list-rest id args) cont-out text)
(add-entry id (list 'defmethod class-id id args cont-out))]
[(list-rest 'defmethod* (list (list (list-rest ids argss) cont-outs) ...) text)
(for ([id ids][args argss][cont-out cont-outs])
(add-entry id (list 'defmethod class-id id args cont-out)))]
[else #f])))
(define (parse-all subs)
(for ([s subs])
(match s
[(list-rest 'defproc (list-rest name args) cont-out text)
(add-entry name (list 'defproc name args cont-out))]
[(list-rest 'defproc* (list (list (list-rest names argss) cont-outs) ...) text)
(for ([name names] [args argss] [cont-out cont-outs])
(add-entry name (list 'defproc name args cont-out)))]
[(list-rest (or 'defclass 'defclass/title) id super intf-ids subs)
(add-entry id (list 'defclass id super intf-ids))
(parse-class id subs)]
[(list-rest (or 'definterface 'definterface/title) id intf-ids subs)
(add-entry id (list 'definterface id intf-ids))
(parse-class id subs)]
[(list-rest (or 'defform 'defform/subs) (list-rest id args) text) (add-entry id (list 'defform id args))]
[(list-rest (or 'defform* 'defform*/subs) (list (list-rest ids argss) ...) text)
(for ([id ids][args argss])
(add-entry id (list 'defform id args)))]
[(list-rest 'deftogether subs text)
(parse-all subs)]
[else #f]
)))
(parse-all all)
)
(define (frame-message title message [show? #f] #:parent [parent #f])
(define fr (new frame% [parent parent] [label title]))
(new message% [parent fr] [label message])
(when show? (send fr show #t))
fr)
(define-runtime-path idx-file (build-path "def-index" "def-index.rktd"))
(make-directory* (path-only idx-file))
(define (create-index)
(define (scribblings-path subdir)
(build-path (find-system-path 'collects-dir)
"scribblings" subdir))
(if (file-exists? idx-file)
(with-input-from-file idx-file read)
(let* ([dic (make-hash)]
[fr (frame-message "Making index" "Constructing documentation index for the first time.\nPlease wait..." #t)]
[read-scrbl-dir
(λ(dir)
(for ([f (in-directory dir)])
(when (equal? (filename-extension f) #"scrbl")
(with-handlers ([exn:fail? (λ _ (printf "Warning: Could not read file ~a~n" f))])
(index-defs dic f)
))))])
(for ([dir scribblings-dirs])
(read-scrbl-dir (scribblings-path dir)))
(with-output-to-file idx-file
(λ()(write dic)))
(send fr show #f)
dic)))
(define (arg->head-string arg)
(match arg
[(list name cont) (symbol->string name)]
[(list (? keyword? kw) name cont) (format "~v ~v" kw name)]
[(list name cont val) (format "[~v]" name)]
[(list (? keyword? kw) name cont val) (format "[~v ~v]" kw name)]
['... "..."]
['...+ "...+"]
))
(define (arg->sig-string arg)
(match arg
[(list name cont) (format " ~v: ~v" name cont)]
[(list (? keyword? kw) name cont) (format " ~v: ~v" name cont)]
[(list name cont val) (format " ~v: ~v = ~v" name cont val)]
[(list (? keyword? kw) name cont val) (format " ~v: ~v = ~v" name cont val)]
['... #f]
['...+ #f]
))
(define (def-name->string-list dic name)
(define entries (dict-ref dic name #f))
(if entries
(for/list ([entry entries])
(match entry
[(list 'defclass id super intf-ids)
(list (format "~v : class?" id)
(format " superclass: ~v" super)
(string-join (cons " extends:"
(map symbol->string intf-ids))
" "))]
[(list 'definterface id intf-ids)
(list (format "~v : interface?" id)
(string-join (cons " implements:"
(map symbol->string intf-ids))
" "))]
[(list 'defconstructor class-id args)
(list* (string-append
(format "(new ~v " class-id)
(string-join (map arg->head-string args) " ")
")")
(filter values (map arg->sig-string args)))]
[(list 'defmethod class-id id args cont-out)
(list*
(string-append
(format "(send a-~a ~a " class-id id)
(string-join (map arg->head-string args) " ")
") -> "
(format "~v" cont-out)
)
(filter values (map arg->sig-string args))
)]
[(list 'defproc id args cont-out)
(list*
(string-append
"("
(string-join (cons (symbol->string name)
(map arg->head-string args)) " ")
") -> "
(format "~v" cont-out)
)
(filter values (map arg->sig-string args))
)]
[(list 'defform id args)
(list (format "~v" (cons id args)))]
[else (list (format "Unknown parsed form: ~a" entry))]
))
'(("No entry found"))))
(define def-index (create-index))
(define label-font
(send the-font-list find-or-create-font
8 'modern 'normal 'normal #f))
(define inset 2)
(define (calc-min-sizes dc str label-font)
(send dc set-font label-font)
(let-values ([(w h a d) (send dc get-text-extent str label-font)])
(let ([ans-w (max 0 (inexact->exact (ceiling w)))]
[ans-h (max 0 (inexact->exact (ceiling h)))])
(values ans-w ans-h))))
(define (dc-text-size dc text label-font)
(define w-h
(for/list ([str text])
(let-values ([(w h) (calc-min-sizes dc str label-font)])
(list w h))))
(values
(+ inset inset (apply max (map car w-h)))
(+ inset inset (apply + (map cadr w-h)))))
(define (draw-text dc x y text)
(define black-color (make-object color% "black"))
(define bg-color (make-object color% "wheat"))
(define-values (w h)
(dc-text-size dc text label-font))
(send dc set-pen (send the-pen-list find-or-create-pen
bg-color 1 'solid))
(send dc set-brush (send the-brush-list find-or-create-brush
bg-color 'solid))
(send dc draw-rectangle x y w h)
(send dc set-pen (send the-pen-list find-or-create-pen
black-color 1 'solid))
(send dc draw-line x y (+ x w) y)
(send dc draw-line (+ x w) y (+ x w) (+ y h))
(send dc draw-line (+ x w) (+ y h) x (+ y h))
(send dc draw-line x (+ y h) x y)
(send dc set-text-foreground black-color)
(send dc set-text-background bg-color)
(send dc set-font label-font)
(define ytot
(for/fold ([ytot (+ y inset)])
([str text])
(let-values ([(w h) (calc-min-sizes dc str label-font)])
(send dc draw-text str (+ x inset) ytot)
(values (+ h ytot)))))
(values w h))
(define tooltip-frame%
(class frame%
(init-field [text '()])
(super-new [label ""]
[style '(no-resize-border
no-caption
no-system-menu
hide-menu-bar
float)]
[stretchable-width #f]
[stretchable-height #f]
)
(define/override (on-subwindow-char e k)
(when (equal? (send k get-key-code) 'escape)
(send this show #f))
#f)
(define hp (new horizontal-panel% [parent this]
[alignment '(left top)]))
(new button% [parent hp][label "X"]
[horiz-margin 0] [vert-margin 0]
[callback (λ _ (send this show #f))])
(define (this-frame) this)
(define tooltip-canvas%
(class canvas%
(define x-start #f)
(define y-start #f)
(define/override (on-event ev)
(when (send ev get-left-down)
(if (send ev moving?)
(let ([x (send ev get-x)] [y (send ev get-y)])
(let-values ([(x y) (send this client->screen x y)])
(send (this-frame) move (- x x-start) (- y y-start))))
(begin (set! x-start (send ev get-x))
(set! y-start (send ev get-y))))))
(super-new)
))
(define cv (new tooltip-canvas% [parent hp]
[paint-callback
(λ(cv dc)(draw-text dc 0 0 text))]))
(define/public (set-text t)
(set! text t)
(define-values (w h) (dc-text-size (send cv get-dc) text label-font))
(send cv min-width (+ w 1))
(send cv min-height (+ h 1))
(send this reflow-container)
(send this stretchable-width #f)
(send this stretchable-height #f)
(send cv refresh))
(unless (empty? text)
(set-text text))
))
(define (def-name->text sym)
(define defs (def-name->string-list def-index sym))
(append* (add-between defs '(""))))
(define tooltip-frame #f)
(define last-sym #f)
(define (item-callback str #:editor ed)
(define start-pos (send ed get-start-position))
(define end-pos (send ed get-end-position))
(define start-exp-pos
(or (send ed get-backward-sexp start-pos) start-pos))
(define end-exp-pos
(or (send ed get-forward-sexp (- end-pos 1)) end-pos))
(define str
(send ed get-text start-exp-pos end-exp-pos))
(define sym (string->symbol str))
(define text (def-name->text sym))
(define dc (send ed get-dc))
(unless tooltip-frame
(set! tooltip-frame (new tooltip-frame%)))
(if (and (eq? sym last-sym) (send tooltip-frame is-shown?))
(send tooltip-frame show #f)
(let ()
(define &x (box #f))
(define &y (box #f))
(send ed position-location start-exp-pos &x &y #f #t)
(define-values (x y) (send ed editor-location-to-dc-location
(unbox &x) (unbox &y)))
(let-values ([(x y) (send (send ed get-canvas)
client->screen (inexact->exact x) (inexact->exact y))]
[(left top) (get-display-left-top-inset)])
(send tooltip-frame move (- x left) (- y -2 top))
(send tooltip-frame set-text text)
(send tooltip-frame show #t)
(set! last-sym sym)
)))
#f)
(with-output-to-file list->string print error make-module-evaluator make-provide-transformer list->string with-output-to-file open-input-output-file regexp-replace
button% set-label class get-top-level-window min-height refresh on-move get-x get-cursor focus
)