├── .gitignore ├── LICENSE ├── README.md ├── info.rkt ├── paredit.rkt └── tool.rkt /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | 3 | compiled/ 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 yjqww6 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Some useful shortcuts for DrRacket. 2 | 3 | Inspired by paredit 4 | 5 | ## Implemented: 6 | 7 | Movement: 8 | * `("c:m:f")` paredit-forward-sexp 9 | * `("c:m:b")` paredit-backward-sexp 10 | * `("c:m:d")` down-sexp ;rebind to `"c:m:d"` 11 | * `("m:right")` forward-atom ;this is not paredit shortcuts, but alternative for forward-word 12 | * `("m:left")` backward-atom ;ditto 13 | 14 | 15 | 16 | Depth-Changing: 17 | * `("m:s")` paredit-splice-sexp 18 | * `("m:(")` paredit-wrap-round 19 | * `("m:up")` paredit-splice-sexp-killing-backward 20 | * `("m:down")` paredit-splice-sexp-killing-forward 21 | * `("m:r")` paredit-raise-sexp 22 | * `("m:?")` paredit-convolute-sexp 23 | 24 | Slurpage & barfage 25 | * `("c:right" "c:)" "c:]")` paredit-slurp-forward 26 | * `("c:m:left" "c:(" "c:[")` paredit-slurp-backward 27 | * `("c:left" "c:}")` paredit-barf-forward 28 | * `("c:m:right" "c:{")` paredit-barf-backward 29 | 30 | ## Note: 31 | 32 | All the key bindings involving meta key `"m:"` can also be accessed 33 | using the Escape key, by pressing and releasing it before proceeding 34 | with the remaining keys, just like in Emacs. This is equivalent to 35 | replacing `"m:"` with prepended `"esc;"`. 36 | 37 | Moreover, `"m:"` can be accessed through `"?:a:"`(the Option Key) on MacOS. 38 | 39 | You can see the up-to-date list of all the key bindings applied on 40 | your platform, by selecting from DrRacket's menu Edit, Keybindings, 41 | Show Active Keybindings, and filtering the list with "paredit." 42 | 43 | ## Install: 44 | ```shell 45 | raco pkg install drracket-paredit 46 | ``` 47 | 48 | or manually: 49 | 1. Download the "paredit.rkt" 50 | 2. Open the 'Edit|Keybindings|Add user-defined-keybindings' dialog, select "paredit.rkt". 51 | -------------------------------------------------------------------------------- /info.rkt: -------------------------------------------------------------------------------- 1 | #lang setup/infotab 2 | 3 | (define drracket-tools '(("tool.rkt"))) 4 | (define drracket-tool-names '("drracket-paredit")) 5 | (define drracket-tool-icons '(#f)) 6 | 7 | (define collection "drracket-paredit") 8 | 9 | (define name "drracket-paredit") 10 | (define blurb '("some paredit shortcuts for DrRacket")) 11 | (define primary-file "paredit.rkt") 12 | (define deps '("base" "drracket" "drracket-plugin-lib" "gui-lib" "srfi-lib")) 13 | 14 | (define test-omit-paths 'all) 15 | -------------------------------------------------------------------------------- /paredit.rkt: -------------------------------------------------------------------------------- 1 | #lang s-exp framework/keybinding-lang 2 | (require drracket/tool-lib srfi/2) 3 | (require (for-syntax racket/list)) 4 | 5 | ;; The raw version of define-shortcut that does not perform any 6 | ;; key processing or body wrapping. 7 | (define-syntax-rule (define-shortcut-internal (key ...) name proc) 8 | (begin 9 | (define (name ed evt . rest) 10 | (when (is-a? ed racket:text<%>) 11 | (send ed begin-edit-sequence) 12 | (apply proc ed evt rest) 13 | (send ed end-edit-sequence))) 14 | (keybinding key name) ...)) 15 | 16 | (define-syntax (define-shortcut stx) 17 | ;; Add esc; equivalent key bindings for all the meta bindings. 18 | (define (add-esc-and-option-key-bindings s-keys) 19 | (define keys (syntax->datum s-keys)) 20 | (define esc-variants 21 | (for/list ([k (in-list keys)] 22 | #:when (regexp-match? #rx"m:" k)) 23 | (string-append "esc;" (regexp-replace* #rx"m:" k "")))) 24 | (define option-variants 25 | (for/list ([k (in-list keys)] 26 | #:when (regexp-match? #rx"m:" k)) 27 | (string-append "?:a:" (regexp-replace* #rx"m:" k "")))) 28 | ;; Use remove-duplicates to combine all key bindings, so that duplicates 29 | ;; are removed. This means that if we add some esc; key bindings manually, 30 | ;; for example by accident, it will not be duplicated, affecting display 31 | ;; of key bindings in DrRacket. 32 | (remove-duplicates (append esc-variants option-variants keys))) 33 | (syntax-case stx () 34 | [(_ key (name . args) body* ...) 35 | #'(define-shortcut key name 36 | (λ args body* ...))] 37 | [(_ (key ...) name proc) 38 | #`(define-shortcut-internal 39 | (#,@(add-esc-and-option-key-bindings #'(key ...))) 40 | name proc)] 41 | [(_ key name proc) 42 | #'(define-shortcut (key) name proc)])) 43 | 44 | ;;; Movement 45 | (define (get-paredit-forward-sexp ed sp) 46 | (cond 47 | [(send ed get-forward-sexp sp) 48 | => (λ (pos) pos)] 49 | [(send ed find-up-sexp sp) 50 | => (λ (pos) 51 | (send ed get-forward-sexp pos))] 52 | [else #f])) 53 | 54 | (define-shortcut ("c:m:f") (paredit-forward-sexp ed evt) 55 | (define sp (send ed get-start-position)) 56 | (and-let* ([dest (get-paredit-forward-sexp ed sp)]) 57 | (send ed set-position dest))) 58 | 59 | (define (get-paredit-backward-sexp ed sp) 60 | (cond 61 | [(send ed get-backward-sexp sp) 62 | => (λ (pos) pos)] 63 | [else (send ed find-up-sexp sp)])) 64 | 65 | (define-shortcut ("c:m:b") (paredit-backward-sexp ed evt) 66 | (define sp (send ed get-start-position)) 67 | (and-let* ([dest (get-paredit-backward-sexp ed sp)]) 68 | (send ed set-position dest))) 69 | 70 | (define-shortcut ("c:m:d") (paredit-down-sexp ed evt) 71 | (send ed down-sexp 72 | (send ed get-start-position))) 73 | 74 | (define (get-forward-atom ed pos) 75 | (define sp (send ed get-start-position)) 76 | (define dests 77 | (filter (λ (x) x) 78 | (list (send ed find-down-sexp sp) 79 | (get-paredit-forward-sexp ed sp)))) 80 | (and (not (null? dests)) (apply min dests))) 81 | 82 | (define-shortcut ("m:right") (forward-atom ed evt) 83 | (and-let* ([dest (get-forward-atom ed (send ed get-start-position))]) 84 | (send ed set-position dest))) 85 | 86 | (define (find-down-sexp-backward ed pos) 87 | (and-let* ([bw (send ed get-backward-sexp pos)] 88 | [down (send ed find-down-sexp bw)]) 89 | (if (or (not down) (> down pos)) 90 | #f 91 | (last-sexp ed down)))) 92 | 93 | (define (get-backward-atom ed pos) 94 | (define sp (send ed get-start-position)) 95 | (define dests 96 | (filter (λ (x) x) 97 | (list (find-down-sexp-backward ed sp) 98 | (get-paredit-backward-sexp ed sp)))) 99 | (and (not (null? dests)) (apply max dests))) 100 | 101 | (define-shortcut ("m:left") (backward-atom ed evt) 102 | (and-let* ([dest (get-backward-atom ed (send ed get-start-position))]) 103 | (send ed set-position dest))) 104 | 105 | ;;; Depth-Changing 106 | (define-shortcut ("m:s") (paredit-splice-sexp ed evt [pos #f] [reindent #t]) 107 | (when (not pos) 108 | (set! pos (send ed get-start-position))) 109 | (and-let* ([begin-outer (send ed find-up-sexp pos)] 110 | [end-outer (send ed get-forward-sexp begin-outer)]) 111 | (send ed delete (- end-outer 1) end-outer) 112 | (send ed delete begin-outer (+ begin-outer 1)) 113 | (when reindent 114 | (send ed tabify-selection begin-outer end-outer)))) 115 | 116 | (define (start-of-sexp ed pos) 117 | (define fw (send ed get-forward-sexp pos)) 118 | (cond [(if fw 119 | (send ed get-backward-sexp fw) 120 | (send ed get-backward-sexp pos)) 121 | => (λ (v) v)] 122 | [else pos])) 123 | 124 | (define (sexp-start ed) 125 | (start-of-sexp ed (send ed get-start-position))) 126 | 127 | (define-shortcut ("m:(") (paredit-wrap-round ed evt) 128 | (send ed insert "(") 129 | (let ([pos (send ed get-start-position)]) 130 | (send ed forward-sexp pos) 131 | (send ed insert ")") 132 | (send ed set-position pos))) 133 | 134 | (define (first-sexp ed sp) 135 | (let loop ([pos sp] [prev sp]) 136 | (if pos 137 | (loop (send ed get-backward-sexp pos) pos) 138 | prev))) 139 | 140 | (define (last-sexp ed sp) 141 | (let loop ([pos sp] [prev sp]) 142 | (if pos 143 | (loop (send ed get-forward-sexp pos) pos) 144 | prev))) 145 | 146 | (define (kill-sexps-backward ed pos) 147 | (send ed delete (first-sexp ed pos) pos)) 148 | 149 | (define (kill-sexps-forward ed pos) 150 | (send ed delete pos (last-sexp ed pos))) 151 | 152 | (define (not-toplevel? ed pos) 153 | (send ed find-up-sexp pos)) 154 | 155 | (define-shortcut ("m:up") (paredit-splice-sexp-killing-backward ed evt) 156 | (define sp (sexp-start ed)) 157 | (when (not-toplevel? ed sp) 158 | (kill-sexps-backward ed sp) 159 | (paredit-splice-sexp ed evt))) 160 | 161 | (define-shortcut ("m:down") (paredit-splice-sexp-killing-forward ed evt) 162 | (define sp (sexp-start ed)) 163 | (when (not-toplevel? ed sp) 164 | (kill-sexps-forward ed sp) 165 | (paredit-splice-sexp ed evt))) 166 | 167 | (define-shortcut ("m:r") (paredit-raise-sexp ed evt) 168 | (define sp (sexp-start ed)) 169 | (when (not-toplevel? ed sp) 170 | (and-let* ([fw (send ed get-forward-sexp sp)]) 171 | (kill-sexps-forward ed fw)) 172 | (kill-sexps-backward ed sp) 173 | (paredit-splice-sexp ed evt))) 174 | 175 | (define-shortcut ("m:?") (paredit-convolute-sexp ed evt) 176 | (define sp (sexp-start ed)) 177 | (and-let* ([r1 (send ed find-up-sexp sp)] 178 | [fw (send ed get-forward-sexp r1)] 179 | [paren (send ed get-text (- fw 1) fw)] 180 | [r2 (send ed find-up-sexp r1)] 181 | [text (send ed get-text r1 sp)] 182 | [end (send ed get-forward-sexp r2)]) 183 | (send ed insert paren end) 184 | (kill-sexps-backward ed sp) 185 | (paredit-splice-sexp ed evt (+ r1 1) #f) 186 | (send ed insert text r2) 187 | (send ed tabify-selection r2 end))) 188 | 189 | 190 | ;;;Barfage & Slurpage 191 | 192 | (define (find-up-sexp-slurp-forward ed sp) 193 | (let loop ([sp (send ed find-up-sexp sp)]) 194 | (cond [(not sp) #f] 195 | [(and-let* ([fw1 (send ed get-forward-sexp sp)]) 196 | (send ed get-forward-sexp fw1)) sp] 197 | [else (loop (send ed find-up-sexp sp))]))) 198 | 199 | (define-shortcut ("c:right" "c:s:0" "c:]") (paredit-slurp-forward ed evt) 200 | (define sp (send ed get-start-position)) 201 | (and-let* ([up (find-up-sexp-slurp-forward ed sp)] 202 | [end (send ed get-forward-sexp up)] 203 | [fw (send ed get-forward-sexp end)] 204 | [paren (send ed get-text (- end 1) end)]) 205 | (send ed insert paren fw) 206 | (send ed delete end) 207 | (send ed tabify-selection up fw))) 208 | 209 | (define (find-up-sexp-slurp-backward ed sp) 210 | (let loop ([sp (send ed find-up-sexp sp)]) 211 | (cond [(not sp) #f] 212 | [(send ed get-backward-sexp sp) sp] 213 | [else (loop (send ed find-up-sexp sp))]))) 214 | 215 | (define-shortcut ("c:m:left" "c:s:9" "c:[") (paredit-slurp-backward ed evt) 216 | (define sp (send ed get-start-position)) 217 | (and-let* ([start (find-up-sexp-slurp-backward ed sp)] 218 | [bw (send ed get-backward-sexp start)] 219 | [paren (send ed get-text start (+ start 1))]) 220 | (send ed delete (+ start 1)) 221 | (send ed insert paren bw) 222 | (send ed tabify-selection bw start))) 223 | 224 | (define-shortcut ("c:left" "c:}") (paredit-barf-forward ed evt) 225 | (define sp (send ed get-start-position)) 226 | (and-let* ([up (send ed find-up-sexp sp)] 227 | [fw (send ed get-forward-sexp up)] 228 | [paren (send ed get-text (- fw 1) fw)] 229 | [last (last-sexp ed sp)] 230 | [bw (send ed get-backward-sexp last)]) 231 | (and-let* ([bw1 (send ed get-backward-sexp bw)] 232 | [x (send ed get-forward-sexp bw1)]) 233 | (set! bw x)) 234 | (send ed delete fw) 235 | (send ed insert paren bw) 236 | (send ed set-position sp) 237 | (send ed tabify-selection up fw))) 238 | 239 | (define-shortcut ("c:m:right" "c:{") (paredit-barf-backward ed evt) 240 | (define sp (send ed get-start-position)) 241 | (and-let* ([up (send ed find-up-sexp sp)] 242 | [paren (send ed get-text up (+ up 1))] 243 | [down (send ed find-down-sexp up)] 244 | [fw (send ed get-forward-sexp down)]) 245 | (and-let* ([fw1 (send ed get-forward-sexp fw)] 246 | [x (send ed get-backward-sexp fw1)]) 247 | (set! fw x)) 248 | (send ed insert paren fw) 249 | (send ed delete (+ up 1)) 250 | (send ed tabify-selection up fw))) -------------------------------------------------------------------------------- /tool.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | (require drracket/tool framework racket/runtime-path) 3 | (provide tool@) 4 | 5 | (define-runtime-path file "paredit.rkt") 6 | 7 | (define tool@ 8 | (unit 9 | (import drracket:tool^) 10 | (export drracket:tool-exports^) 11 | 12 | (define (phase1) (void)) 13 | (define (phase2) (void)) 14 | (keymap:add-user-keybindings-file file))) --------------------------------------------------------------------------------