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
|
* Trident Mode
This is an [[http://www.gnu.org/software/emacs/][Emacs]] minor mode and collection of commands for working with
[[http://common-lisp.net/project/parenscript/][Parenscript]] code in [[http://common-lisp.net/project/slime/][SLIME]] and sending it to the browser via [[https://github.com/skeeto/skewer-mode][Skewer]]. The goal is
to create an environment for hacking Parenscript which fits as naturally as
possible into the Lisp style of interactive development.
Note that this is very young code and there are certain to be serious problems.
There's at least one other project with related goals, [[https://github.com/3b/slime-proxy][slime-proxy]], though at
the time of writing it's unclear whether it's still being actively developed.
** Installation
Trident has both [[http://en.wikipedia.org/wiki/Common_Lisp][Common Lisp]] and Emacs dependencies.
- Common Lisp dependencies
- Your preferred [[http://en.wikipedia.org/wiki/Common_Lisp][Common Lisp]] implementation
- [[http://common-lisp.net/project/parenscript/][Parenscript]]
You almost certainly want to use [[http://www.quicklisp.org/beta/][Quicklisp]] to install Parenscript.
- Emacs dependencies
- [[http://common-lisp.net/project/slime/][SLIME]]
- [[https://github.com/skeeto/skewer-mode][Skewer]]
- [[https://github.com/magnars/dash.el][dash.el]]
My recommendation is to install SLIME via the [[https://github.com/quicklisp/quicklisp-slime-helper][Quicklisp SLIME Helper]] and
the others through [[http://www.emacswiki.org/emacs/ELPA][ELPA]].
** Setup
To enable =trident-mode= in a SLIME buffer: =M-x trident-mode=.
To have =lisp-mode=, =slime-mode=, and =trident-mode= all enable automatically
for any file with an extension of ".paren":
#+BEGIN_SRC emacs-lisp
(add-to-list 'auto-mode-alist (cons "\\.paren\\'" 'lisp-mode))
(add-hook 'lisp-mode-hook
#'(lambda ()
(when (and buffer-file-name
(s-ends-with? ".paren" buffer-file-name))
(unless (slime-connected-p)
(save-excursion (slime)))
(trident-mode +1))))
#+END_SRC
Parenscript must be loaded in your Common Lisp image, and you'll probably also
want to import its symbols:
#+BEGIN_SRC common-lisp
(ql:quickload :parenscript)
(use-package :parenscript)
#+END_SRC
With the above taken care of it's time to skewer the browser. Trident doesn't
automatically load Skewer, since you could hypothetically use Trident just for
viewing the code generated by Parenscript, so to proceed you should =(require
'skewer-mode)= in Emacs.
See [[https://github.com/skeeto/skewer-mode/blob/master/README.md][Skewer's README]] for detailed information on the multiple ways you can
connect to a site - including sites on servers you don't control.
The fastest way to simply try things out is to run =M-x run-skewer=. Skewer
will load an empty page in your browser and connect to it. You can immediately
begin using Trident's evaluation commands (described below); to additionally
open a JavaScript REPL you can run =M-x skewer-repl=.
** Commands
*** Code expansion commands
These commands generate JavaScript from the Parenscript code and display it but
don't send it to the browser for evaluation:
- =trident-expand-sexp=
- =trident-expand-last-expression=
- =trident-expand-defun=
- =trident-expand-region=
- =trident-expand-buffer=
- =trident-expand-dwim=
From within an expansion buffer you can press =e= to send the JavaScript to the
browser, =w= to copy it to the kill ring, =s= to save it to a file (you'll be
prompted for the destination) or =q= to dismiss the buffer. The copy command,
=w=, acts on the region if it's active or the entire buffer otherwise.
Additionally, you can use =M-x trident-compile-buffer-to-file= to expand the
current buffer and save the generated code directly to a file.
*** Code evaluation commands
These commands first compile the Parenscript code to JavaScript and then
immediately send to it the browser to be evaluated:
- =trident-eval-sexp=
- =trident-eval-last-expression=
- =trident-eval-defun=
- =trident-eval-region=
- =trident-eval-buffer=
- =trident-eval-dwim=
** Key bindings
The traditional set of code evaluation key bindings is a poor fit for Trident,
since they would shadow SLIME's equivalent commands and that's probably not
what you want. That leaves us without a clear convention to follow, so by
default we don't establish any key bindings at all. However, the function
=trident-add-keys-with-prefix= will add two-key key bindings for all commands
behind a prefix of your choice.
For example:
#+BEGIN_SRC emacs-lisp
(trident-add-keys-with-prefix "C-c C-e")
;; The key sequence for trident-eval-region is "e r", so it's now bound to "C-c
;; C-e er"
#+END_SRC
The full list of key bindings =trident-add-keys-with-prefix= will establish is:
- =e RET= -- =trident-eval-sexp=
- =e e= -- =trident-eval-last-expression=
- =e d= -- =trident-eval-defun=
- =e r= -- =trident-eval-region=
- =e b= -- =trident-eval-buffer=
- =e SPC= -- =trident-eval-dwim=
- =x RET= -- =trident-expand-sexp=
- =x e= -- =trident-expand-last-expression=
- =x d= -- =trident-expand-defun=
- =x r= -- =trident-expand-region=
- =x b= -- =trident-expand-buffer=
- =x SPC= -- =trident-expand-dwim=
Evaluation commands begin with an "e", expansion commands with "x". The second
letter is generally mnemonic but not always. The =-sexp= commands use =RET= in
correspondence to =slime-expand-1=, and the =-dwim= commands use the space bar
because it's easy and comfortable to hit.
Please consider these keys provisional, and let me know if you have any ideas
for improving the arrangement.
If you really want to shadow SLIME's key bindings in buffers where
=trident-mode= is active you could do something like this:
#+BEGIN_SRC emacs-lisp
(defun steal-slime-keys-for-trident! ()
;; Don't affect all SLIME buffers, just where invoked
(make-local-variable 'slime-mode-map)
(let ((map slime-mode-map))
(define-key map (kbd "C-x C-e") nil)
(define-key map (kbd "C-c C-r") nil)
(define-key map (kbd "C-M-x") nil)
(define-key map (kbd "C-c C-k") nil)
(define-key map (kbd "C-c C-m") nil))
(let ((map trident-mode-map))
(define-key map (kbd "C-x C-e") 'trident-eval-last-expression)
(define-key map (kbd "C-c C-r") 'trident-eval-region)
(define-key map (kbd "C-M-x") 'trident-eval-defun)
(define-key map (kbd "C-c C-k") 'trident-eval-buffer)
(define-key map (kbd "C-c C-m") 'trident-expand-sexp)))
(add-hook 'trident-mode-hook 'steal-slime-keys-for-trident!)
#+END_SRC
** Other amenities
=slime-selector= is a great feature and Trident can optionally integrate with
it. If you call =trident-add-slime-selector-methods=, two entries related to
=trident-mode= will be added. One, invoked with =p=, will take you to the most
recently visited buffer where =trident-mode= is active (excluding buffers which
are already visible). The other, on =P=, will take you to a scratch buffer with
=trident-mode= enabled, creating the buffer if necessary.
Speaking of the scratch buffer, the =trident-scratch= command will take you
straight there.
** Still do be done
- Test against a wider array of code. Are there problems with quoting?
- Better documentation.
- Look into adding a REPL.
- See if more integration with SLIME is possible.
- Command(s) for compiling to a file.
- Similar support for [[http://weitz.de/cl-who/][CL-WHO]] and/or [[https://github.com/paddymul/css-lite][CSS-LITE]]?
- Get to know ELPA and packaging.
- Add support for Customize.
** Contributing
Contributions are very welcome. Since I've just started working on this and
don't have everything figured out yet, please first contact me on GitHub or
send me an email so we can talk before you start working on something.
|