Open Jach opened 2 years ago
Thank you for the detailed analysis, I really appreciate it. I checked it in emacs and indeed, it uses swank:autodoc
(as I recall, older slime versions used swank:operator-arglist
). I'm going to check how can I replicate it in slimv.
In Common Lisp, you might define a class:
Here it has two slots, with :name being required. So if you try to do
(make-instance 'foo)
, that is, don't supply a :name, then you'll enter the debugger.(make-instance 'foo :name "Bob")
works because :favorite-color is optional.It would be nice to know in the displayed arglist that when you type "(make-instance 'foo " that :name is a required argument. Instead you just get the default arglist for make-instance, which is the actual
(make-instance CLASS &REST INITARGS &KEY &ALLOW-OTHER-KEYS)
Emacs with Sly (and possibly with Slime) however seems to support this and should show something like the expected
(make-instance 'person &rest initargs &key favorite-color (name (alexandria:required-argument :name)) &allow-other-keys)
i.e. the two possible keyword args are explicitly listed and you even see the default value for the second one indicating it's required.From here I'll just share some digging I did and some functions I found that may help resolve this, but I am unsure what a good fix would look like here, and I haven't yet even hacked my own local fix yet.
The flow starts at slimv.vim's
SlimvArglist
function which finds out the function name of the current form after e.g. pressing space, and passes this down to swank.py'sswank_op_arglist
function. This calls back into Lisp (e.g. for make-instance) something like(swank:operator-arglist "make-instance" *package*)
and evaluating that in the REPL normally will give the same echoed string that SlimvArglist then shows.Digging into swank.lisp's
operator-arglist
function shows it to be a little wrapper around an implementation-specific introspection call, in SBCL's case it's justsb-introspect:function-lambda-list
. e.g.:So this code flow at least cannot ever take into account the context of the class name. How does emacs do it then? I'm still not entirely sure but I did look around and found the file swank-arglists.lisp and suspect emacs makes use of that. In particular the file uses and manipulates an
arglist
structure and there's anextra-keywords
generic function that is defined for things like make-instance, make-condition, change-class, ... basically things that have this same dynamic behavior of expected keywords.There are a few ways to create the structure given code snippets, here's a REPL output for using
swank:arglist-dispatch
:More intriguing is the unexported function
arglist-from-form
that makes this even easier:and these can be printed giving what I'm after:
The optional :operator keyword arg just puts whatever string given at the first of the printed list. This even works if we dynamically extend what args constructing a person can have (e.g. supporting 'British spelling'):
It seems like it may be possible (at least for CL) to just swap out slimv's use of swank:operator-arglist on the operator name with the above decoded-arglist-to-string around the arglist-from-form call, and just send the entire form down without having to parse it. I think my next step is to hack this in and I'll share it on this issue if/when I do, but there may be good reasons for using the simpler function and perhaps there are edge cases where this stuff in swank-arglists doesn't work and provide the current string.
Another implementation might be to use the exported function
swank:autodoc
which constructs the arglist and calls decoded-arglist-to-string, however this expects an emacs-y "raw-form" rather than a lisp form and so maybe is more difficult to use from vim...