; main.ss ; Defines main MrSpidey class ; ---------------------------------------------------------------------- ; Copyright (C) 1995-97 Cormac Flanagan ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; version 2 as published by the Free Software Foundation. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to the Free Software ; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ; ---------------------------------------------------------------------- (define-structure (fileinfo filename frame thunk-port annotations)) ;; annotations may also be (-> zodiac:parsed) (define NUM-EXTRA-LINES 20) (define-struct exn:flow-arrow-exists ()) (define (wrap-busy-cursor thunk) (dynamic-wind (lambda () (wx:begin-busy-cursor)) thunk (lambda () (wx:end-busy-cursor)))) ; ---------------------------------------------------------------------- (define tm (void)) (define MrSpidey% (class null () (public [fileinfo* '()] [multiple-files (lambda () (not (= (length fileinfo*) 1)))] ;; ------------------------------ ;; Progress frame [progress-frame 'progress-frame] [progress-canvas 'progress-canvas] [progress-edit 'progress-edit] [init-progress-frame! (lambda () (pretty-debug-gui `(init-progress-frame)) (let* ( [f (parameterize ([wx:current-eventspace (wx:make-eventspace)]) (make-object mred:frame% '() (format "~a Progress" st:name)))] [p (make-object mred:vertical-panel% f)] [c (make-object mred:wrapping-canvas% p)] [ph (make-object mred:horizontal-panel% p)] [_ (send ph stretchable-in-y #f)] [_ (make-object mred:horizontal-panel% ph)] [b (make-object mred:button% ph (lambda _ (send f show #f)) "Hide")] [e (make-object mred:media-edit%)]) (set! progress-frame f) (set! progress-canvas c) (set! progress-edit e) (send p add-child c) ;;(send c set-media e) (send progress-frame set-size -1 -1 400 200);; ht was 130 (send progress-frame show #f)))] [new-progress-frame! (lambda () (pretty-debug-gui `(new-progress-frame)) (set! progress-edit (make-object mred:media-edit%)) (send progress-edit lock #t) (send progress-canvas set-media progress-edit) (send progress-frame show #t) (send progress-frame iconize #f) (pretty-debug-gui `(new-progress-frame-done)))] [with-progress-frame (lambda (thunk) (let* ( [old-progress-handler (mrspidey:progress-handler)] [current '()] [current-start-time 0] [width 6] [total-width 18]) (letrec ([insert-numbers (lambda (n) (send progress-edit insert (format "~a ~ams" (padl (if (eq? n 'done) "" (format "~a" n)) width) (padl (- (current-process-milliseconds) (current-gc-milliseconds) current-start-time) width)) (send progress-edit last-position)))] [f (match-lambda* [((? string? name) line) (if (equal? name current) (let ([end (send progress-edit last-position)]) (send progress-edit delete (- end total-width) end) (insert-numbers line)) (begin (f 'fresh-line) (set! current name) (set! current-start-time (- (current-process-milliseconds) (current-gc-milliseconds))) (send progress-edit insert (padr name 30)) (insert-numbers line)))] [((? string? str)) (f 'fresh-line) (send progress-edit insert str) (f #\newline)] [(#\newline) (send progress-edit insert (format "~n")) (set! current '())] [('fresh-line) (unless (null? current) (f #\newline))])] [g (lambda args (send progress-edit lock #f) (apply f args) (send progress-edit lock #t))]) (parameterize ([mrspidey:progress-handler (lambda args ;;(apply old-progress-handler args) (apply g args) (wx:flush-display))]) (send progress-frame show #t) (begin0 (thunk) (mrspidey:progress "=Done======================================"))))))] ;; ------------------------------ [filename->fileinfo (lambda (file) (pretty-debug-gui `(filename->fileinfo ,file)) (or (ormap (match-lambda [(and fi ($ fileinfo filename frame)) (and (string? file) (string=? filename file) fi)]) fileinfo*) (assert #f 'filename->fileinfo file)))] [filename->frame (lambda (file) (let ([x (filename->fileinfo file)]) (and x (fileinfo-frame x))))] [filename->edit (lambda (file) (let ([x (filename->frame file)]) (and x (ivar x program-edit))))] [for-each-frame (lambda (f) (for-each (match-lambda [($ fileinfo filename frame) (when frame (f frame))]) fileinfo*))] [focus-def (lambda (loc) (assert (zodiac:location? loc) 'focus-def loc) (let* ([file (zodiac:location-file loc)] [frame (filename->frame file)]) (if frame (send frame focus-def (zodiac:location-offset loc)) (wx:message-box (format "File ~s is not loaded" (file-name-from-path file)) "Error" (bitwise-ior wx:const-ok)))))] ;; ------------------------------ [open-analyzed-file-choice (lambda () (let ([choice (wx:get-single-choice "Select referenced unit file to open" "Open Unit" (sort stringfileinfo choice) #t))))] [open-fileinfo (match-lambda* [((and fi ($ fileinfo filename frame)) show) (if frame (when show (send frame show #t) (send frame iconize #f)) (add-frame fi show))])] [open-all (lambda (show) (for-each (lambda (fi) (open-fileinfo fi show)) fileinfo*))] [add-frame (match-lambda* [( (and fi ($ fileinfo filename frame thunk-port thunk-expression)) show . first-frame-locs) (pretty-debug-gui `(add-no-show-frame ,filename ,fi ,@first-frame-locs)) (assert (not frame)) (let ([summary-edit (make-object mred:media-edit%)]) (initialize-summary summary-edit) (pretty-debug-gui `(summary-initialized)) (parameterize ([mrspidey:add-summary-handler (add-summary summary-edit)]) (with-progress-frame (lambda () (pretty-debug-gui '(progress-frame-initialized)) (mrspidey:progress "===========================================") '(mrspidey:progress (format "Analyzing ~a" (file-name-from-path filename))) (let* ([annotations (calc-annotations (thunk-expression))] [_ (set-fileinfo-annotations! fi annotations)] [_ (pretty-debug-gui `(calling-Tframe ,filename))] [frame (apply make-object spidey:frame% ;; only use margin this filename summary-edit first-frame-locs)]) (pretty-debug-gui `(Have-Tframe ,filename)) (when show (send frame show #t)) (unless show (send frame show #t) (send frame show #f)) (set-fileinfo-frame! fi frame) (update-arrows) )))))])] [initialize-summary (lambda (edit) (let* ([delta (make-object wx:style-delta% wx:const-change-family wx:const-decorative)] [click-delta (make-object wx:style-delta%)]) (send delta set-delta wx:const-change-size 10) (send click-delta copy delta) (send click-delta set-delta-foreground "BLUE") (send click-delta set-delta wx:const-change-underline 1) (let ( [insert (lambda (s) (let ([before (send edit get-end-position)]) (send edit insert s) (let ([after (send edit get-end-position)]) (values before after))))]) (let*-values ( [(s1 e1) (insert "Welcome to ")] [(s2 e2) (insert "MrSpidey")] [(s3 e3) (insert (format ", version ~a." (mred:version)))] [(s4 e4) (insert (format "~n"))]) (send edit change-style delta s1 e1) (send edit change-style click-delta s2 e2) (send edit change-style delta s3 e3) (send edit set-clickback s2 e2 (lambda args (make-object mred:hyper-view-frame% (string-append "file:" (build-path (collection-path "mrspidey") ; MATTHEW: got rid of plt-home "about.html")))) click-delta)))))] [local-record-analyzed-file (lambda (filename thunk-port thunk-expression) (pretty-debug-gui `(local-record-analyzed-file ,filename ,thunk-port ,thunk-expression)) (set! fileinfo* (append fileinfo* (list (make-fileinfo filename #f thunk-port thunk-expression)))))] ;; ------------------------------ [add-summary (lambda (summary-edit) (lambda line (send summary-edit lock #f) (match line [(str loc word-ofs) (cond [(zodiac:zodiac? loc) ((add-summary summary-edit) str (zodiac:zodiac-start loc) word-ofs)] [(zodiac:location? loc) (let* ( [click (lambda ignore (focus-def loc))] [init-pos-box (box 0)] [_ (send summary-edit get-position init-pos-box)] [_ (send summary-edit insert str)] [_ (send summary-edit insert (format " in file ~s line ~s" (file-name-from-path (zodiac:location-file loc)) (zodiac:location-line loc)))] [_ (send summary-edit insert #\newline)] ;; Find start and end of word [end (unbox init-pos-box)] [_ (for i 0 (add1 word-ofs) (set! end (mred:scheme-forward-match summary-edit end (send summary-edit last-position))))] [start (mred:scheme-backward-match summary-edit end 0)]) '(pretty-debug-gui `(send summary-edit change-style check-link-delta ,(send check-link-delta get-underlined-on) ,start ,end)) ;; Paranoia - check have proper locations (if (and start end) (begin (send summary-edit set-clickback start end click) (send summary-edit change-style check-link-delta start end)) ;; Error (pretty-print `(Error: annotate-summary ,str ,loc ,word-ofs ,start ,end))))] [else (begin (printf "Bad location in main:add-summary-handler ~s~n" loc) (mrspidey:add-summary str))])] [(str) (send summary-edit insert str) (send summary-edit insert #\newline)] [x (printf "add-summary, can't handle ~s~n" x)]) (send summary-edit lock #t)))] ;; ------------------------------ [on-frame-close (lambda (filename) (void))] [close-all-frames (lambda () (send progress-frame show #f) (close-all-frames-except-progress) )] [close-all-frames-except-progress (lambda () (for-each-frame (lambda (frame) (send frame on-close))) )] ;; ------------------------------ [annotated-edit (lambda (mode filename canvas) ;; create edit buffer, load file and add annotations (pretty-debug-gui `(annotate-edit ,mode ,filename)) (match (filename->fileinfo filename) [($ fileinfo filename frame thunk-port annotations) (let* ( [port (thunk-port)] [edit (make-object (mode-edit-class mode) (if (multiple-files) " " "") this canvas)]) (send edit set-filename filename) (send edit edit-sequence (lambda () (pretty-debug-gui "loading!") (let ([s (format "Loading ~a: " (file-name-from-path filename))]) (recur loop ([n 1]) (when (zero? (modulo n 50)) (mrspidey:progress s n)) (let ([r (read-line port)]) ;;(pretty-debug-gui `(inserting ,r)) (if (eof-object? r) (mrspidey:progress s (sub1 n)) (begin (send edit insert-line r) (loop (add1 n)))))) (when (multiple-files) (for i 0 NUM-EXTRA-LINES (send edit insert-line "")))) (close-input-port port) (pretty-debug-gui `(last-line ,(send edit last-line))) (send edit change-style base-delta 0 (send edit last-position)) (pretty-debug-gui "annotating!") (annotate! filename edit mode annotations) (send edit set-position 0) (pretty-debug-gui `(annotate-buffer done)))) edit)]))] [annotate! (lambda (filename edit mode annotations) (let ([s (format "Annotating ~a:" (file-name-from-path filename))]) (mrspidey:progress s '...) (pretty-debug-gui `(annotate! ,filename ,edit ,mode ,annotations)) (let ([annotation-fn-list (mode-annotation-fn-list mode)]) (for-each (lambda (annotation-fn annotations) (when annotation-fn (for-each (let ([fn (uq-ivar edit annotation-fn)]) (match-lambda [(and annotation ($ annotation loc)) (when (string=? (zodiac:location-file loc) filename) ;; Call the method named func to annotate etc. (fn annotation))])) annotations))) annotation-fn-list (vector->list annotations))) (mrspidey:progress s 'done)))] ;; ------------------------------ [shake-it '(lambda () (let* ([n (random (vector-length fileinfo*))] [fi (vector-ref fileinfo* n)] [frame (fileinfo-frame fi)]) (send frame shake-it)))] ;; ------------------------------ ;; Arrows [list-flow-arrows '()] [add-flow-arrow (lambda (src dest) (assert (and (FlowType? src) (FlowType? dest))) (pretty-debug-gui `(main:add-flow-arrow ,(FlowType->pretty src) ,(FlowType->pretty dest))) (with-handlers ([exn:flow-arrow-exists? (lambda (exn) (void))]) (let ([flow-arrow (make-object flow-arrow% this src dest)]) (when flow-arrow (set! list-flow-arrows (cons flow-arrow list-flow-arrows))))))] [draw-arrows (lambda () (pretty-debug-gui `(main:draw-arrows)) (for-each-frame (lambda (frame) (send (ivar frame program-edit) draw-arrows))))] [update-arrows (lambda () (pretty-debug-gui `(main:update-arrows)) (for-each (lambda (arrow) (send arrow update)) list-flow-arrows) (draw-arrows))] [delete-arrow (lambda (arrow) (send arrow delete-local) (set! list-flow-arrows (remv arrow list-flow-arrows)))] [delete-arrow-refresh (lambda (arrow) (delete-arrow arrow) (draw-arrows))] [delete-arrows (lambda () (for-each (lambda (arrow) (send arrow delete-local)) list-flow-arrows) (set! list-flow-arrows '()) (draw-arrows))] ;; ------------------------------ [reanalyze 'reanalyze] [run-mrspidey (lambda (file . first-frame-locs) (pretty-debug-gui `(run-mrspidey ,file)) (set! reanalyze (lambda () (apply run-mrspidey file first-frame-locs))) (close-all-frames-except-progress) (pretty-debug-gui `(frames-closed)) (set! fileinfo* '()) (new-progress-frame!) (let ([file (normalize-path (normalize-path file))]) ;; Following calls record-anlyzed-file (st:analyze-and-make-annotations file) (apply add-frame (filename->fileinfo file) #t first-frame-locs) ))]) ;; ---------------------------------------------------------------------- (sequence (set! tm this) (record-analyzed-file-hook local-record-analyzed-file) (init-progress-frame!)))) (define spidey (make-object MrSpidey%)) ;; ---------------------------------------------------------------------- ;; mode says for each set of annotations, ;; either #f or fn to handle annotation. (define-const-structure (mode name edit-class annotation-fn-list)) (define modes (list (make-mode "Types and Checks" flow-arrow:media-edit% (list 'add-type-annotation 'add-check-annotation 'add-uncheck-annotation)) (make-mode "Normal" spidey:static-edit% (list #f #f #f)))) ;; ----------------------------------------------------------------------