File: racket-mode.org

package info (click to toggle)
racket-mode 20210916git0-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 1,076 kB
  • sloc: lisp: 10,354; makefile: 58
file content (301 lines) | stat: -rw-r--r-- 15,895 bytes parent folder | download
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