I am using the Nyxt web browser which is an interesting Common Lisp application. Nyxt is designed to be an infinitely extensible browser. Thus, the user can change the code and/or create extensions while the program is running. This is live hackability by design.
One of the possible extensions is to create a new command for the web browser. There is more than one way to create a new command. One of them is by using a bookmarklet command. It must be highlighted the macro function responsible for defining this command:
(defmacro nyxt::define-bookmarklet-command (name documentation source)
"Define a bookmarklet command, the source can either be a JavaScript string to
evaluate, or a file:// URL with a file path to a JavaScript source file."
`(define-command-global ,name (&optional (buffer (current-buffer)))
,documentation
(let* ((source ,source)
(source (if (nyxt::file-url-p source)
(nyxt::read-file-string source)
source)))
(ffi-buffer-evaluate-javascript-async buffer source))))
(sera:export-always 'nyxt::define-bookmarklet-command :nyxt)
This definition is placed here on the source code. I managed to create a bookmarklet command for Nyxt before. Basically, a translation from this Javascript snippet:
(function() { const rate = prompt('Set the
new playback rate', 2.5); if (rate != null) { const video =
document.getElementsByTagName('video')[0]; video.playbackRate =
parseFloat(rate); } })();
Converted to the following common-lisp snippet using Nyxt definitions:
(define-bookmarklet-command live-hack-youtube-speed "Change youtube
videos speed" "(function() { const rate = prompt('Set the
new playback rate', 2.5); if (rate != null) { const video =
document.getElementsByTagName('video')[0]; video.playbackRate =
parseFloat(rate); } })();")
Now, I am trying a new bookmarklet customization for Nyxt. Basically, finding the 'next' page and click on it. This would work on pages like the GNU manuals. In Javascript, this is:
(document.querySelectorAll('[rel="next"]'))[0].click()
Thus, I tried the following using Nyxt's macro:
(define-bookmarklet-command goNext "Follow the link labeled next"
"(function() {(document.querySelectorAll('[rel="next"]'))[0].click()})();")
Awkwardly enough, I receive this error message:
Error while parsing arguments to DEFMACRO DEFINE-BOOKMARKLET-COMMAND: too many elements in (GONEXT "Follow the link labeled next" "(function() {(document.querySelectorAll('[rel=" NEXT "]'))[0].click()})();") to satisfy lambda list (NAME DOCUMENTATION NYXT/WEB-MODE::SOURCE): exactly 3 expected, but got 5 [Condition of type SB-KERNEL::ARG-COUNT-ERROR]
I am not sure why this is happening. I am passing on the macro 3 arguments. However, the error message indicates 5.
I have a knowledge gap on CL macros. Why is this happening?
It turns out the problem was not my comprehesion of the Common Lisp macro. Actually, it was something simple. I needed to escape the quotes around "next"
with \"next\"
:
(define-bookmarklet-command go-next "no documentation yet" "(function() {(document.querySelectorAll('[rel=\"next\"]'))[0].click()})();")
Now, the problem is solved and the customization works as expected on Nyxt :) The REPL returns T after receiving the definition and it works on Nyxt's GUI.
NYXT>(define-bookmarklet-command go-next "no documentation yet" "(function() {(document.querySelectorAll('[rel=\"next\"]'))[0].click()})();")
T