#lang racket/gui
(require (prefix-in scrbl: scribble/reader)
racket/runtime-path
(for-syntax racket/base)
framework
(only-in html read-html-as-xml)
(prefix-in x: xml)
setup/dirs srfi/13
)
(provide item-callback)
(define text-size
(- (preferences:get 'framework:standard-style-list:font-size) 2))
(define scribblings-dirs
'("raco"
"framework"
"slideshow"
"scribble"
"gui"
"draw"
"reference"
))
(define srfi-files
'("srfi-13.html"
"srfi-14.html"
))
(define rkt-files
(list
(build-path (collection-path "framework") "main.rkt")
(build-path (collection-path "framework") "preferences.rkt")
))
(print-as-expression #f)
(print-reader-abbreviations #t)
(define (scribblings-path subdir)
(collection-path "scribblings" subdir))
(define (read-scrbl in [file ""])
(scrbl:read-inside in))
(define (read-rkt in)
(void (read-language in)) (let loop ([l '()])
(define s (scrbl:read in))
(if (eof-object? s)
l
(loop (cons s l)))))
(define (add-dict-entry dic key l)
(hash-set! dic key
(cons l (hash-ref dic key '()))))
(read-accept-lang #t)
(read-accept-reader #t)
(define (index-defs dic file)
(define f-in (open-input-file file))
(define all
(read-scrbl f-in file ))
(close-input-port f-in)
(parse-list dic all))
(define (parse-list dic all)
(define (add-entry key l)
(add-dict-entry dic key l))
(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 (add-doc/names id cont-args args args+vals cont-out)
(add-entry id (list 'defproc id
(map (λ(a c)
(if (and (list? c) (keyword? (first c)))
(list* (first c) (first a)
(second c) (rest a))
(list* (first a) c (rest a))))
(append (map list args) args+vals)
cont-args)
cont-out)))
(define parse-cont-args
(match-lambda
[(list-rest (? keyword? k) c r)
(cons (list k c) (parse-cont-args r))]
[(list-rest c r)
(cons c (parse-cont-args r))]
[(list)
'()]))
(define (parse-doc subs)
(for ([s subs])
(match s
[(list 'proc-doc/names id
(list '->* cont-args cont-opt-args cont-out)
(list (list args ...)
args+vals)
text)
(add-doc/names id
(append (parse-cont-args cont-args)
(parse-cont-args cont-opt-args))
args args+vals
cont-out)]
[(list 'proc-doc/names id
(list '-> cont-args ... cont-out)
(list args ...) text)
(add-doc/names id (parse-cont-args cont-args) args '() cont-out)]
[(list 'proc-doc id cont text)
(add-entry id (list 'thing-doc id cont))]
[(list thing-doc id cont text)
(add-entry id (list 'thing-doc id cont))]
[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)]
[(list-rest 'provide/doc subs)
(parse-doc subs)]
[(list-rest 'provide subs)
(parse-doc subs)]
[else #f]
)))
(parse-all all)
)
(define replace-dict
'((" " . " ")
(">" . ">")
("<" . "<")
("&" . "&")
("<sub>" . "")
("</sub>" . "")
))
(define (html-string->string str)
(for/fold ([str str]) ([(k v) (in-dict replace-dict)])
(regexp-replace* (regexp-quote k) str (regexp-replace-quote v))))
(define (parse-srfi-file dic file)
(define lines (file->lines file))
(for ([line lines])
(define l (regexp-match
(pregexp
(string-append
"<code class=\"?proc-def\"?>"
"([^" (regexp-quote "([{}])\"'") "]*)"
"</code>\\s*<var>"
"(.*)"
"</var>"))
line))
(when l
(let ([id-str (string-trim-both (html-string->string (second l)))])
(add-dict-entry
dic
(string->symbol id-str)
(list 'srfi id-str (string-trim-both (html-string->string (third l)))))))))
(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-syntax-rule (with-parse-handler file body ...)
(with-handlers ([exn:fail? (λ _ (printf "Warning: Could not parse file ~a~n" file))])
body ...))
(define-syntax (this-file stx)
(with-syntax ([file (syntax-source stx)])
#'file))
(define (create-index)
(when (file-exists? idx-file)
(if (and
(> (file-or-directory-modify-seconds (this-file))
(file-or-directory-modify-seconds idx-file))
(eq? 'yes
(message-box "Recreate doc"
"Script def-signatures:
The documentation index looks older than the script file.
Do you want to recreate the index?"
#f '(caution yes-no))))
(delete-file idx-file)
(file-or-directory-modify-seconds idx-file (current-seconds))
))
(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-parse-handler f
(index-defs dic f)
))))])
(for ([col scribblings-dirs])
(let ([dir (scribblings-path col)])
(if (directory-exists? dir)
(read-scrbl-dir dir)
(printf "Warning: directory ~a not found." dir))))
(read-scrbl-dir (find-collects-dir))
(for ([f rkt-files])
(with-parse-handler f
(parse-list dic (read-rkt (open-input-file f)))))
(for ([f srfi-files])
(let ([f (build-path (find-doc-dir) "srfi-std" f)])
(with-parse-handler f
(parse-srfi-file dic f))))
(printf "~a identifiers found\n" (dict-count dic))
(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)))]
[(list 'srfi id-str args)
(list (string-append id-str " " args))]
[(list 'doc-thing id cont)
(list (format "~v : ~v" id cont))]
[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
text-size
'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]
[stretchable-width #f] [stretchable-height #f]
[callback (λ _ (send (this-frame) 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 (round x) (round 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 (round (inexact->exact x)) (round (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 open-input-output-file regexp-replace
button% set-label class get-top-level-window min-height refresh on-move get-x get-cursor focus
finder:common-put-file preferences:set-default
)