1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301
|
#+OPTIONS: ':t toc:t author:t email:t H:4
#+MACRO: kbd @@texinfo:@kbd{$1}@@ @@html:<kbd>$1</kbd>@@
#+MACRO: ref @@texinfo:@ref{$1}@@
#+MACRO: see @@texinfo:@xref{$1}@@
#+TITLE: Racket Mode
#+AUTHOR: Greg Hendershott
#+EMAIL: racket@greghendershott.com
#+LANGUAGE: en
#+TEXINFO_FILENAME: racket-mode.info
#+TEXINFO_HEADER: @syncodeindex pg cp
#+TEXINFO_DIR_CATEGORY: Emacs
#+TEXINFO_DIR_TITLE: Racket Mode: (racket-mode)
#+TEXINFO_DIR_DESC: Edit and REPL major modes for Racket lang
#+TEXINFO_PRINTED_TITLE: Racket Mode
* Introduction
The [[https://www.racket-mode.com/][Racket Mode]] package consists of a variety of Emacs major and minor modes, including:
- ~racket-mode~: A major mode for editing ~.rkt~ files.
- {{{ref(racket-xp-mode)}}}: An optional minor mode that enhances ~racket-mode~ to explain and explore code.
- ~racket-repl-mode~: A major mode for running programs providing a REPL.
- Various other modes to support specific features:
- {{{ref(racket-logger-mode)}}}
- {{{ref(racket-profile-mode)}}}
- {{{ref(racket-debug-mode)}}}
Racket Mode uses a "back end server" written in Racket, which is responsible for running files and implementing commands that cannot be implemented in Emacs Lisp.[fn:pkg]
For code, issues, and pull requests, see the [[https://github.com/greghendershott/racket-mode][Git repo]].
To fund this work, see [[https://github.com/users/greghendershott/sponsorship][GitHub Sponsors]] or [[https://www.paypal.me/greghendershott][PayPal]].
[fn:pkg] Racket Mode's Racket code is also delivered as part of the Emacs package --- /not/ as a Racket package. Delivering both Emacs and Racket code in one Emacs package simplifies installation and updates. The main drawback is that the Racket code is not automatically byte-compiled, as would normally be done by ~raco pkg install~. To address this: {{{see(racket-mode-start-faster)}}}.
* Install
The recommended way to use Racket Mode is to install the package from [[https://melpa.org/#/racket-mode][MELPA]].
** Configure Emacs to use MELPA
To use MELPA:
- Add the following to your =~/.emacs= or =~/.emacs.d/init.el=:
#+BEGIN_SRC lisp
(require 'package)
(add-to-list 'package-archives
'("melpa" . "https://melpa.org/packages/")
t)
#+END_SRC
- Restart Emacs.
- Type {{{kbd(M-x)}}} ~package-refresh-contents~ {{{kbd(RET)}}}.
** Install Racket Mode
When Emacs is configured to use MELPA, simply type {{{kbd(M-x)}}} ~package-install~ {{{kbd(RET)}}} ~racket-mode~ {{{kbd(RET)}}}.
** Minimal Racket
If you have installed the minimal Racket distribution (for example by using the [[https://github.com/Homebrew/homebrew-core/blob/master/Formula/minimal-racket.rb][homebrew formula]]) Racket Mode needs some additional packages (like ~errortrace~ and ~macro-debugger~). A simple way to get all these packages is to install the ~drracket~ Racket package. In a command shell:
#+BEGIN_SRC shell
raco pkg install drracket
#+END_SRC
** Uninstall
To uninstall Racket Mode, simply type {{{kbd(M-x)}}} ~package-delete~ {{{kbd(RET)}}} ~racket-mode~ {{{kbd(RET)}}}.
You should probably also exit and restart Emacs.
** Update
*** Upgrading all packages
The "easy path" provided by Emacs is to update /all/ packages to their latest versions. Although you might not want to do this --- see next section --- here is how to do so:
1. Use {{{kbd(M-x)}}} ~list-packages~. It should display a message like "42 packages can be upgraded; type āUā to mark them for upgrading.".
2. Press {{{kbd(U)}}} as suggested to mark them all.
3. Press {{{kbd(x)}}} to execute.
After such a mass update, it might be wise to exist restart Emacs.
*** Updating just Racket Mode
Updating all packages sometimes is more than you want. For example, maybe you will discover that some packages have changed in ways that require you to take time to learn about, change customizations, and so on.
To update just Racket Mode:
1. Uninstall Racket Mode: {{{kbd(M-x)}}} ~package-delete~ {{{kbd(RET)}}} ~racket-mode~ {{{kbd(RET)}}}.
2. Optional but most reliable: Exit and restart Emacs.
3. Install Racket Mode: {{{kbd(M-x)}}} ~package-install~ {{{kbd(RET)}}} ~racket-mode~ {{{kbd(RET)}}}. This will install the latest version.
* Configure
Although Racket Mode can be customized with many {{{ref(Variables)}}}, there is only one that you might /need/ to set: {{{ref(racket-program)}}}. This is the name or pathname of the Racket executable. It defaults to ~Racket.exe~ on Windows else ~racket~.
On Windows or Linux, this default will probably work for you.
On macOS, downloading Racket doesn't add its ~bin~ directory to your ~PATH~. Even after you add it, GUI Emacs doesn't automatically use your path (unless you use the handy [[https://melpa.org/#/exec-path-from-shell][exec-path-from-shell]] package). Therefore you might want to set ~racket-program~ to a complete pathname.
You can ~setq~ this directly in your Emacs init file (=~/.emacs= or =~/.emacs.d/init.el=), or, use {{{kbd(M-x)}}} ~customize~, as you prefer.
** Key bindings
To customize things like key bindings, you can use ~racket-mode-hook~ in your Emacs init file to modify ~racket-mode-map~. For example, although {{{kbd(C-c C-c)}}} is bound by default to the ~racket-run~ command, let's say you wanted {{{kbd(F5)}}} to be an additional binding:
#+BEGIN_SRC lisp
(add-hook 'racket-mode-hook
(lambda ()
(define-key racket-mode-map (kbd "<f5>") 'racket-run)))
#+END_SRC
Likewise for ~racket-repl-mode-hook~ and ~racket-repl-mode-map~.
** Font-lock (syntax highlighting)
Font-lock (as Emacs calls syntax highlighting) can be controlled using the variable ~font-lock-maximum-decoration~, which defaults to ~t~ (maximum). You can set it to a number, where ~0~ is the lowest level. You can even supply an association list to specify different values for different major modes.
Historically you might choose a lower level for speed. These days you might do so because you prefer a simpler appearance.
Racket Mode supports four, increasing levels of font-lock:
- ~0~: Just strings, comments, and ~#lang~.
- ~1~: ~#:keyword~ and self-evaluating literals like numbers, quoted symbols (including symbols with spaces delimited by ~|~ characters), and ~#rx~ and ~#px~ regular expressions.
- ~2~: Identifiers in ~define~-like and ~let~-like forms.
- ~3~: Identifiers provided by ~racket~, ~typed/racket~, ~racket/syntax~, and ~syntax/parse~. (This level effectively treats Racket as a language, instead of a language for making languages.).
** Completion
In Emacs, a major mode may supply a "completion-at-point function". This function is used by manual completion commands like ~complete-symbol~ (bound by default to {{{kbd(C-M-i)}}}), as well as by auto-completion packages like ~company-mode~.
- ~racket-mode~ supplies ~racket-complete-at-point~, which simply supplies the same symbols that it knows how to font-lock. This does /not/ require the Racket Mode back end to be running. But of course the completion candidates do not correspond to your program's definitions or those it imports. This is a static, "better than nothing" fallback.
- ~racket-xp-mode~ --- an optional minor mode that enhances ~racket-mode~ --- supplies ~racket-xp-complete-at-point~, which uses a static analysis to find local and imported binding names. Although this requires the Racket Mode back end to be running --- and will automatically start it --- it does /not/ require the edit buffer to be ~racket-run~.
- ~racket-repl-mode~ supplies ~racket-repl-complete-at-point~, which uses the result of ~namespace-mapped-symbols~ on the program currently running in the REPL.
These completion functions are set by default. (However, ~racket-xp-mode~ is not enabled by default. To do so: {{{ref(racket-xp-mode)}}}.)
If you want {{{kbd(TAB)}}} to do completion as well as indent, add the following to your Emacs init file:
#+BEGIN_SRC lisp
(setq tab-always-indent 'complete)
#+END_SRC
This changes the behavior of Emacs' standard ~indent-for-tab-command~, to which {{{kbd(TAB)}}} is bound by default in ~racket-mode~ and ~racket-repl-mode~.
** Xref (definitions and references)
Several modes support the Emacs commands
- {{{kbd(M-.)}}} ~xref-find-definitions~
- {{{kbd(M-?)}}} ~xref-find-references~
- {{{kbd(M-\,)}}} ~xref-pop-marker-stack~
To do so, each mode adds a local hook for ~xref-backend-functions~:
- {{{ref(racket-mode)}}}: ~#'racket-mode-xref-backend-function~
- {{{ref(racket-xp-mode)}}}: ~#'racket-xp-xref-backend-function~
- {{{ref(racket-repl-mode)}}}: ~#'racket-repl-xref-backend-function~
If you prefer, you can remove the local hook --- e.g. for ~racket-mode~: ~(remove-hook 'xref-backend-functions #'racket-mode-xref-function t)~.
You can ~M-x customize-group~ and enter ~xref~ to adjust some other settings. For example, the customization variable ~xref-prompt-for-identifier~ controls which commands prompt you and when. You might prefer to set it to ~nil~.
If you use ~paredit~, by default it binds {{{kbd(M-?)}}} to ~paredit-convolute-sexp~. You can change that binding in ~paredit-mode-map~ allowing the global binding for {{{kbd(M-?)}}} to be used, or, pick some other key for ~xref-find-references~ in the global map.
Finally, what to expect:
- Racket does not have a global or project-wide database of definitions and references.
- Various modules can export identifiers with the same symbolic value -- for example a different "define" is provided by ~racket/base~, ~typed/racket/base~, and other modules.
- A module can import something, then rename, contract, and re-export it.
As a result, to find a definition, it is necessary to know exactly /which/ identifier is meant --- either by expanding the module (as is done by ~racket-xp-mode~) or by actually running it (~racket-repl-mode~). Once known, we can usually find the definition site, even through a chain of renaming and/or contract-wrapping exports. In addition, when point is on a module within ~require~ form, we can usually find the source file. (In plain ~racket-mode~ edit buffers not enhanced by ~racket-xp-mode~, the only thing that ~xref-find-definitions~ does is visit relative requires, e.g. ~foo.rkt~ in ~(require "foo.rkt")~.)
As for finding references, the default xref implementation is used, which greps for strings among a project's files. Although ~racket-xp-mode~ can sometimes do better, using ~drracket/check-syntax~ for definitions and references /within/ the current buffer, beyond those it also falls back to the default implementation.
In any case, using the Emacs xref API allows for consistent command names, shortcut keys, and even a special buffer to navigate among references and visit each source location.
** Indent
Indentation can be customized in a way similar to lisp-mode and scheme-mode: {{{ref(racket-indent-line)}}}.
** paredit
If you use [[https://melpa.org/#/paredit][paredit]], you might want to add keybindings to ~paredit-mode-map~:
- Bind the curly brace keys to ~paredit-open-curly~ and ~paredit-close-curly~.
- Bind whatever keys you prefer for ~paredit-wrap-square~ and ~paredit-wrap-curly~.
For example, with [[https://melpa.org/#/use-package][~use-package~]]:
#+BEGIN_SRC lisp
(use-package paredit
:ensure t
:config
(dolist (m '(emacs-lisp-mode-hook
racket-mode-hook
racket-repl-mode-hook))
(add-hook m #'paredit-mode))
(bind-keys :map paredit-mode-map
("{" . paredit-open-curly)
("}" . paredit-close-curly))
(unless terminal-frame
(bind-keys :map paredit-mode-map
("M-[" . paredit-wrap-square)
("M-{" . paredit-wrap-curly))))
#+END_SRC
** smartparens
If instead of paredit you prefer [[https://melpa.org/#/smartparens][smartparens]], you can use the default configuration it provides for Lisp modes generally and for Racket Mode specifically:
#+BEGIN_SRC lisp
(require 'smartparens-config)
#+END_SRC
** Edit buffers and REPL buffers
By default, all ~racket-mode~ edit buffers share one ~racket-repl-mode~ buffer, named ~*Racket REPL*~. For example, if you run foo.rkt, the REPL prompt changes to ~foo.rkt>~, and the REPL is inside the file module namespace. If you then run bar.rkt, the REPL prompt changes to ~bar.rkt>~, and you are in that namespace.
If you prefer, you can use more than one REPL buffer, by customizing the variable {{{ref(racket-repl-buffer-name-function)}}}:
- Share a REPL buffer among files belonging to the same project; each REPL buffer is named ~*Racket REPL <project-name>*~.
- A unique REPL buffer for each edit buffer, similar to Dr Racket; each REPL buffer is named ~*Racket REPL <file.rkt>*~.
- You can also define your own, custom function.
You can customize where the REPL buffer is displayed by adding an item to the Emacs variable ~display-buffer-alist~. A good regular expression to use for this would be ~\\`\\*Racket REPL~. For example, if you wanted to make the REPL buffer appear in a new frame:
#+BEGIN_SRC lisp
(add-to-list 'display-buffer-alist
'("\\`\\*Racket REPL"
(display-buffer-reuse-window
display-buffer-pop-up-frame)
(reusable-frames . 0)
(inhibit-same-window . t)))
#+END_SRC
** eldoc
By default Racket Mode sets ~eldoc-documentation-function~ to ~nil~ --- no ~eldoc-mode~ support. You may set it to ~racket-eldoc-function~ in a ~racket-mode-hook~ and ~racket-repl-mode-hook~ if you really want to use ~eldoc-mode~ with Racket. But it is not a very satisfying experience because Racket is not a very "eldoc-friendly" language. Although Racket Mode attempts to discover argument lists, contracts, or types this doesn't work in many common cases:
- Many Racket primitives are defined in ~#%kernel~ or ~#%runtime~. There's no easy way to determine their argument lists. Most do not ~provide~ a contract.
- Many of the interesting Racket forms are syntax (macros) not functions. There's no easy way to determine their "argument lists".
- When a form has documentation, Racket Mode can show the \"bluebox\" -- but often that does not fit in a single line as you would normally expect with eldoc.
A more satisfying experience is to use {{{ref(racket-xp-describe)}}} or {{{ref(racket-xp-documentation)}}}.
** Start faster
You can use {{{ref(racket-mode-start-faster)}}} to make the Racket REPL start faster.
** Unicode input method
An optional Emacs input method, ~racket-unicode~, lets you easily type various Unicode symbols that might be useful when writing Racket code.
To automatically enable the ~racket-unicode~ input method in ~racket-mode~ and ~racket-repl-mode~ buffers, put the following code in your Emacs init file:
#+BEGIN_SRC lisp
(add-hook 'racket-mode-hook #'racket-unicode-input-method-enable)
(add-hook 'racket-repl-mode-hook #'racket-unicode-input-method-enable)
#+END_SRC
{{{see(racket-unicode-input-method-enable)}}}.
{{{see(racket-insert-lambda)}}}.
** Ligatures
Prior to Emacs 28.0.50, things like ~auto-composition-mode~ or ~ligature-mode~ that use ~composition-function-table~ to display ligatures can cause Emacs to freeze. This can happen when an Emacs ~overlay~ displays a string containing such a ligature --- this includes the overlays created by ~racket-show-pseudo-tooltip~, as used by ~racket-xp-mode~. The only known work-around is to change the value of ~racket-show-functions~ to something "boring" such as ~(racket-show-echo-area)~.
* Reference
The following sections are generated from the doc strings for each command, variable, or face. (As a result, some of the formatting might not be quite as nice or correct as in the previous sections.)
You can also view these by using the normal Emacs help mechanism:
- {{{kbd(C-h f)}}} and enter the name of a command.
- {{{kbd(C-h v)}}} and enter the name of a variable.
#+INCLUDE: reference.org :minlevel 1
|