[slime-devel] Re: Canonical package nicknames patch

Peter Seibel peter at javamonkey.com
Fri Jul 9 17:07:23 UTC 2004


Luke Gorrie <luke at bluetail.com> writes:

> Peter Seibel <peter at javamonkey.com> writes:
>
>> So it mildly annoys me that SLIME does this:
>>
>>   CL-USER> (in-package :foo)
>>   #<The FOO package>
>>   FOO> (in-package :cl-user)
>>   #<The COMMON-LISP-USER package>
>>   USER> 
>>
>> Note the final prompt.
>
> IMO the real problem is that the initial REPL package name is
> hard-coded to "CL-USER" by way of (slime-lisp-package), instead of
> starting by looking up the shortest nickname. This could be fixed in
> slime-init-output-buffer, perhaps with an RPC to `set-package'.
>
> That would fix the inconsistency.

That's true. On the other hand I sort of like that it uses CL-USER
since that is a standard nickname as opposed to, say, USER. As a
programmer I don't care too much one way or another but with my
author/Common Lisp evangelist hat on, one of the benefits of using
SLIME as my standard development environment is that it is so
consistent from implementation to implementation. If in some
implementations the default prompt is USER and in others its CL-USER
then that's another darn thing I have to explain somewhere.

Also, I was hacking around some more with another addition to
shortest-package-nickname to automatically create abbreviations for
dotted package names:

  CL-USER> (in-package :com.gigamonkeys.spam)
  #<The COM.GIGAMONKEYS.SPAM package>
  SPAM> 

This way I can use long package names that won't collide in the global
package namespace yet not have my SLIME prompt take up half the line.
I put this under control of a variable so folks who only want actual
package names or nicknames in their prompt can turn it off.

So here's what I think would be best:

 - As you say, the elisp should get the string to use in the prompt in
   the same way all the time; the initial value should not be
   hard-coded.

 - That way should be to ask SWANK for the string to use. This string
   should be determined by the following algorithm:

    o If there's an entry in the *canonical-packge-nicknames* alist it
      should be used. By default this alist would contain the
      canonical nicknames defined by the language standard: CL-USER
      for COMMON-LISP-USER and CL for COMMON-LISP.

    o Then if the flag *auto-abbreviate-dotted-packages* is T and the
      full package name contains a #\. then the prompt string will be
      the last component of the dotted name. COM.GIGAMONKEYS.SPAM ==>
      SPAM.

    o In all other cases fall back on the strategy of finding the
      shortest name or nickname.

This algorithm has the two benefits I mentioned above:

  - Consistency between implementations.

  - Encourages not cluttering the package namespace with short
    nicknames just to make one's SLIME prompt concise.

A reasonable variation might be to only use the auto-abbreviation of
dotted names if the package has no actual nicknames. Thus folks who
adopt a no-nicknames style of programming will get some helpful
behavior from SLIME while folks who do use nicknames won't be confused
by seeing "names" in the prompt that aren't actual nicknames that can
be passed to IN-PACKAGE. I'd also be fine with having the default
value of the flag that turns on this auto abbreviation to NIL (though
for my rather particular purposes as an author I'd rather it be T.)

Obviously the function that returns this string would need to be named
something that makes it clear that its return value is only a string
to be used in the prompt since it might return a name that is not an
actual name or nickname of the package it can't be used by code that
needs an actual name.

Below is a patch to implement this proposal.

-Peter

Index: swank.lisp
===================================================================
RCS file: /project/slime/cvsroot/slime/swank.lisp,v
retrieving revision 1.210
diff -u -r1.210 swank.lisp
--- swank.lisp	7 Jul 2004 15:09:33 -0000	1.210
+++ swank.lisp	9 Jul 2004 17:06:41 -0000
@@ -54,6 +54,13 @@
 (defconstant keyword-package (find-package :keyword)
   "The KEYWORD package.")
 
+(defvar *canonical-packge-nicknames*
+  '(("COMMON-LISP-USER" . "CL-USER"))
+  "Canonical package names to use instead of shortest name/nickname.")
+
+(defparameter *auto-abbreviate-dotted-packages* t
+  "Automatically abbreviate dotted package names to their last component.")
+
 (defvar *swank-io-package*
   (let ((package (make-package :swank-io-package :use '())))
     (import '(nil t quote) package)
@@ -1309,15 +1316,32 @@
                            (return (values values -)))))
       (when (and package-update-p (not (eq *package* *buffer-package*)))
         (send-to-emacs 
-         (list :new-package (shortest-package-nickname *package*)))))))
+         (list :new-package (package-string-for-prompt *package*)))))))
+
+(defun package-string-for-prompt (package)
+  "Return the shortest nickname (or canonical name) of PACKAGE."
+  (or (canonical-package-nickname package)
+      (auto-abbreviated-package-name package)
+      (shortest-package-nickname package)))
+
+(defun canonical-package-nickname (package)
+  "Return the canonical package nickname, if any, of PACKAGE."
+  (cdr (assoc (package-name package) *canonical-packge-nicknames* :test #'string=)))
+
+(defun auto-abbreviated-package-name (package)
+  "Return an abbreviated 'name' for PACKAGE. N.B. this is not an actual package name or nickname."
+  (when *auto-abbreviate-dotted-packages*
+    (let ((last-dot (position #\. (package-name package) :from-end t)))
+      (when last-dot (subseq (package-name package) (1+ last-dot))))))
 
 (defun shortest-package-nickname (package)
   "Return the shortest nickname (or canonical name) of PACKAGE."
   (loop for name in (cons (package-name package) (package-nicknames package))
         for shortest = name then (if (< (length name) (length shortest))
-                                     name
-                                     shortest)
-        finally (return shortest)))
+                                   name
+                                   shortest)
+              finally (return shortest)))
+
 
 (defslimefun interactive-eval-region (string)
   (with-buffer-syntax ()
@@ -1371,9 +1395,9 @@
     (swank-pprint (multiple-value-list (eval (read-from-string string))))))
 
 (defslimefun set-package (package)
-  "Set *package* to PACKAGE and return its name and shortest nickname."
+  "Set *package* to PACKAGE and return its name and the string to use in the prompt."
   (let ((p (setq *package* (guess-package-from-string package))))
-    (list (package-name p) (shortest-package-nickname p))))
+    (list (package-name p) (package-string-for-prompt p))))
 
 (defslimefun listener-eval (string)
   (clear-user-input)

-- 
Peter Seibel                                      peter at javamonkey.com

         Lisp is the red pill. -- John Fraser, comp.lang.lisp





More information about the slime-devel mailing list