diff --git a/collects/drracket/private/rep.rkt b/collects/drracket/private/rep.rkt index 718056a6c5..04c112ee42 100644 --- a/collects/drracket/private/rep.rkt +++ b/collects/drracket/private/rep.rkt @@ -281,13 +281,6 @@ TODO (send drs-bindings-keymap map-function "c:x;0" "collapse") (send drs-bindings-keymap map-function "c:x;2" "split") - (send drs-bindings-keymap map-function "esc;c:x" "send-toplevel-form-to-repl") - (send drs-bindings-keymap map-function "m:c:x" "send-toplevel-form-to-repl") - (send drs-bindings-keymap map-function "c:c;c:e" "send-toplevel-form-to-repl") - (send drs-bindings-keymap map-function "c:c;c:r" "send-selection-to-repl") - (send drs-bindings-keymap map-function "c:c;m:e" "send-toplevel-form-to-repl-and-go") - (send drs-bindings-keymap map-function "c:c;m:r" "send-selection-to-repl-and-go") - (send drs-bindings-keymap map-function "c:c;c:z" "move-to-interactions") (for ([i (in-range 1 10)]) diff --git a/collects/drracket/private/unit.rkt b/collects/drracket/private/unit.rkt index 3199fe300f..e8630a2cac 100644 --- a/collects/drracket/private/unit.rkt +++ b/collects/drracket/private/unit.rkt @@ -4013,48 +4013,9 @@ module browser threading seems wrong. (send ed set-position (- (srcloc-position srcloc) 1)) (send ed set-caret-owner #f 'global)) - - (define/public (send-toplevel-form-to-repl shift-focus?) - (define defs (get-definitions-text)) - (when (= (send defs get-start-position) - (send defs get-end-position)) - (let loop ([pos (send defs get-start-position)]) - (define next-up (send defs find-up-sexp pos)) - (cond - [next-up (loop next-up)] - [else - (send-range-to-repl pos - (send defs get-forward-sexp pos) - shift-focus?)])))) - (define/public (send-selection-to-repl shift-focus?) - (define defs (get-definitions-text)) - (send-range-to-repl (send defs get-start-position) (send defs get-end-position) shift-focus?)) (define/public (move-to-interactions) (ensure-rep-shown (get-interactions-text)) (send (get-interactions-canvas) focus)) - - (define/private (send-range-to-repl start end shift-focus?) - (unless (= start end) - (define defs (get-definitions-text)) - (define ints (get-interactions-text)) - (send defs move/copy-to-edit ints start end (send ints last-position) #:try-to-move? #f) - - - ;; clear out the whitespace after the copied down thing - (let loop () - (define last-pos (- (send ints last-position) 1)) - (when (last-pos . > . 0) - (define last-char (send ints get-character last-pos)) - (when (char-whitespace? last-char) - (send ints delete last-pos (+ last-pos 1)) - (loop)))) - - ;; insert a newline - (send ints insert "\n" (send ints last-position) (send ints last-position)) - - (ensure-rep-shown ints) - (when shift-focus? (send (get-interactions-canvas) focus)) - (send ints do-submission))) ; diff --git a/collects/framework/test.rkt b/collects/framework/test.rkt index df53fc681a..7847588dcc 100644 --- a/collects/framework/test.rkt +++ b/collects/framework/test.rkt @@ -950,16 +950,15 @@ If conflicting modifiers are provided, the ones later in the list are used.}) - (proc-doc/names + (proc-doc test:menu-select - (string? string? . -> . void?) - (menu item) - @{Selects the menu-item named @racket[item] in the menu named @racket[menu]. + (->i ([menu string?]) () #:rest [items (listof string?)] [res void?]) + @{Selects the menu-item named by the @racket[item]s in the menu named @racket[menu]. @italic{Note:} The string for the menu item does not include its keyboard equivalent. For example, to select ``New'' from the ``File'' menu, - use ``New'', not ``New Ctrl+m n''.}) + use ``New'', not ``New Ctrl+N''.}) (proc-doc/names test:mouse-click diff --git a/collects/scribblings/drracket/common.rkt b/collects/scribblings/drracket/common.rkt index 89135e675b..b52696ac62 100644 --- a/collects/scribblings/drracket/common.rkt +++ b/collects/scribblings/drracket/common.rkt @@ -1,12 +1,14 @@ -#lang scheme/base +#lang racket/base (require scribble/manual - (for-label scheme)) + (for-label racket + racket/gui/base)) (provide HtDP drlang (all-from-out scribble/manual) - (for-label (all-from-out scheme))) + (for-label (all-from-out racket + racket/gui/base))) (define HtDP (italic "How to Design Programs")) diff --git a/collects/scribblings/drracket/incremental-keybindings.rkt b/collects/scribblings/drracket/incremental-keybindings.rkt new file mode 100644 index 0000000000..215aa19bf7 --- /dev/null +++ b/collects/scribblings/drracket/incremental-keybindings.rkt @@ -0,0 +1,61 @@ +#lang s-exp framework/keybinding-lang + +(require drracket/tool-lib) + +(keybinding "c:c;c:e" (lambda (ed evt) (send-toplevel-form ed #f))) +(keybinding "c:c;c:r" (lambda (ed evt) (send-selection ed #f))) +(keybinding "c:c;m:e" (lambda (ed evt) (send-toplevel-form ed #t))) +(keybinding "c:c;m:r" (lambda (ed evt) (send-selection ed #t))) + +(define/contract (send-toplevel-form defs shift-focus?) + (-> any/c boolean? any) + (when (is-a? defs drracket:unit:definitions-text<%>) + (when (= (send defs get-start-position) + (send defs get-end-position)) + (let loop ([pos (send defs get-start-position)]) + (define next-up (send defs find-up-sexp pos)) + (cond + [next-up (loop next-up)] + [else + (send-range-to-repl defs + pos + (send defs get-forward-sexp pos) + shift-focus?)]))))) + +(define/contract (send-selection defs shift-focus?) + (-> any/c boolean? any) + (when (is-a? defs drracket:unit:definitions-text<%>) + (send-range-to-repl defs + (send defs get-start-position) + (send defs get-end-position) + shift-focus?))) + +(define/contract (send-range-to-repl defs start end shift-focus?) + (-> (is-a?/c drracket:unit:definitions-text<%>) + exact-positive-integer? + exact-positive-integer? + boolean? + any) + (unless (= start end) + (define ints (send (send defs get-tab) get-ints)) + (define frame (send (send defs get-tab) get-frame)) + (send defs move/copy-to-edit + ints start end + (send ints last-position) + #:try-to-move? #f) + + (let loop () + (define last-pos (- (send ints last-position) 1)) + (when (last-pos . > . 0) + (define last-char (send ints get-character last-pos)) + (when (char-whitespace? last-char) + (send ints delete last-pos (+ last-pos 1)) + (loop)))) + (send ints insert + "\n" + (send ints last-position) + (send ints last-position)) + + (send frame ensure-rep-shown ints) + (when shift-focus? (send (send ints get-canvas) focus)) + (send ints do-submission))) diff --git a/collects/scribblings/drracket/keybindings.scrbl b/collects/scribblings/drracket/keybindings.scrbl index 86f4918fa4..4f0c800097 100644 --- a/collects/scribblings/drracket/keybindings.scrbl +++ b/collects/scribblings/drracket/keybindings.scrbl @@ -1,7 +1,9 @@ #lang scribble/doc @(require "common.rkt" - scribble/struct scribble/bnf racket/list mrlib/tex-table - (for-label racket/gui/base)) + scribble/struct scribble/bnf + racket/list racket/runtime-path racket/port + mrlib/tex-table + (for-label drracket/tool-lib)) @(define (keybinding key . desc) (let* ([keys (if (string? key) (list key) key)] @@ -91,8 +93,6 @@ inspired by Emacs. @keybinding["A-C-down"]{move down into an embedded editor} @keybinding["C-C C-Z"]{move the cursor to the interactions window} -@keybinding["C-F6"]{move the cursor from the definitions -window to the interactions window (or the search window, if it is open).} ] @section{Editing Operations} @@ -156,18 +156,6 @@ window to the interactions window (or the search window, if it is open).} @itemize[ @keybinding["F5"]{Run} -@keybinding["M-C-x"]{Copy the top-level form surrounding the insertion point to the interactions window - and submit it for evaluation} -@keybinding["C-c C-e"]{Copy the top-level form surrounding the insertion point to the interactions window - and submit it for evaluation} -@keybinding["C-c M-e"]{Copy the top-level form surrounding the insertion point to the interactions window, - submit it for evaluation, and move the focus to the interations window} -@keybinding["C-c C-r"]{Copy the selection to the interactions window - and submit it for evaluation} -@keybinding["C-c C-r"]{Copy the selection to the interactions window - and submit it for evaluation} -@keybinding["C-c M-r"]{Copy the selection to the interactions window, - submit it for evaluation, and move the focus to the interactions window} ] @@ -244,3 +232,32 @@ s-exp framework/keybinding-lang Note that DrRacket does not reload this file automatically when you make a change, so you'll need to restart DrRacket to see changes to the file. + +@section{Sending Program Fragments to the REPL} + +@index['("Emacs keybindings")]Users comfortable with Emacs and the conventional Lisp/Scheme-style +of interaction with an ``inferior process'' commonly request +keybindings in DrRacket that send program fragments to be evaluated +at the prompt. This style of interaction is fraught with difficulty, +especially for beginners, and so DrRacket, by default, does not support +it. Instead, clicking DrRacket's ``Run'' button starts with a clean slate +and sends the entire contents of the definitions window, ensuring that +the state in the REPL matches what you would expect by reading +the source code of the program. + +That said, it is difficult for some people to switch to this new mode and, +in some cases (for example when most of the interesting state is not +in the program but in an external database or in the filesystem), using +the contentional keystrokes may make sense. + +So, the remainder of this section is an example keybindings file that +adds the ability to send expressions piecemeal to the interactions +window. It also demonstrates how to pull together a bunch of pieces +of DrRacket's implementation and its libraries to implement keystrokes. + +@(define-runtime-path incremental-keybindings.rkt "incremental-keybindings.rkt") +@(let ([sp (open-output-string)]) + (call-with-input-file incremental-keybindings.rkt + (λ (port) + (copy-port port sp))) + (codeblock (get-output-string sp))) diff --git a/collects/tests/drracket/incremental-keybindings-test.rkt b/collects/tests/drracket/incremental-keybindings-test.rkt new file mode 100644 index 0000000000..024b0b451a --- /dev/null +++ b/collects/tests/drracket/incremental-keybindings-test.rkt @@ -0,0 +1,47 @@ +#lang racket/base +#| + +Adds the incremental-keybindings.rkt file (also shown in the docs) +to DrRacket and then tries out the keystrokes. + +|# + +(require "private/drracket-test-util.rkt" + framework/test + racket/class) + +(fire-up-drscheme-and-run-tests + (λ () + (define drs-frame (wait-for-drscheme-frame)) + (use-get/put-dialog + (λ () + (test:menu-select "Edit" "Keybindings" "Add User-defined Keybindings...")) + (collection-file-path "incremental-keybindings.rkt" + "scribblings" + "drracket")) + (insert-in-definitions drs-frame "#lang racket/base\n") + (do-execute drs-frame) + + (insert-in-definitions drs-frame "(+ 1 (+ 2 3))") + (queue-callback/res + (λ () + (define defs (send drs-frame get-definitions-text)) + (send defs set-position (+ (send defs paragraph-start-position 1) 5)))) + (test:keystroke #\c '(control)) + (test:keystroke #\e '(control)) + (wait-for-computation drs-frame) + (test:keystroke 'right '(alt shift)) + (test:keystroke #\c '(control)) + (test:keystroke #\r '(control)) + (wait-for-computation drs-frame) + (define got + (queue-callback/res + (λ () + (define ints (send drs-frame get-interactions-text)) + (send ints get-text + (send ints paragraph-start-position 2) + (send ints last-position))))) + + (unless (equal? got "> (+ 1 (+ 2 3))\n6\n> (+ 2 3)\n5\n> ") + (error 'incrementalkeybindings-test.rkt "failed-test; got ~s" got)))) +