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. I seem to recall some discussion of this. I have a tiny patch to swank.lisp that causes shortest-package-nickname to check a *canonical-packge-names* alist first to see if there's a known prefered name for a given package. Currently *canonical-packge-names* contains only ("COMMON-LISP-USER" . "CL-USER"). I can check this in if folks think it's a good idea (or at least not a bad one.) If you want I can also change the function name from shortest-package-nickname to canonical-package-nickname which would be more descriptive of the new behavior.
-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 16:15:25 -0000 @@ -54,6 +54,9 @@ (defconstant keyword-package (find-package :keyword) "The KEYWORD package.")
+(defvar *canonical-packge-names* + '(("COMMON-LISP-USER" . "CL-USER"))) + (defvar *swank-io-package* (let ((package (make-package :swank-io-package :use '()))) (import '(nil t quote) package) @@ -1313,11 +1316,12 @@
(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))) + (or (cdr (assoc (package-name package) *canonical-packge-names* :test #'string=)) + (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))))
(defslimefun interactive-eval-region (string) (with-buffer-syntax ()
Peter Seibel peter@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.
Luke Gorrie luke@bluetail.com writes:
Peter Seibel peter@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@javamonkey.com writes:
So here's what I think would be best:
Looks good to me, please commit!
+(defparameter *auto-abbreviate-dotted-packages* t
- "Automatically abbreviate dotted package names to their last component.")
Should that have been defvar?
-Luke
Luke Gorrie luke@bluetail.com writes:
Peter Seibel peter@javamonkey.com writes:
So here's what I think would be best:
Looks good to me, please commit!
+(defparameter *auto-abbreviate-dotted-packages* t
- "Automatically abbreviate dotted package names to their last component.")
Should that have been defvar?
Yes. I'll change that before I commit. Thanks.
-Peter
Peter Seibel wrote:
[...]
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>
How about abbreviating to
C.G.SPAM>
That way, you are possibly less likely to be confused if you have two packages ending with the same name.
Something like:
(defun auto-abbreviated-package-name (package) (when *auto-abbreviate-dotted-packages* (let ((name (make-array '(0) :element-type 'base-char :fill-pointer 0 :adjustable t)) (pname (package-name package)) (dot nil)) (with-output-to-string (s name) (while (setf dot (position #. pname)) (princ (char pname 0) s) (princ #. s) (setf pname (subseq pname (1+ dot)))) (princ pname s)) name)))
perhaps.
Overall, this looks like a good idea to me, but it does have the possibility of showing you a "nickname" that isn't actually a nickname for the package.
Peter Seibel writes:
- (or (cdr (assoc (package-name package) *canonical-packge-names* :test #'string=))
How about this instead:
(or (let ((nick (cdr (assoc (package-name package) *canonical-package-names* :test #'string=)))) (and nick (find nick (list* (package-name package) (package-nicknames package)))))
"Thomas F. Burdick" tfb@OCF.Berkeley.EDU writes:
Overall, this looks like a good idea to me, but it does have the possibility of showing you a "nickname" that isn't actually a nickname for the package.
Peter Seibel writes:
- (or (cdr (assoc (package-name package) *canonical-packge-names* :test #'string=))
How about this instead:
(or (let ((nick (cdr (assoc (package-name package) *canonical-package-names* :test #'string=)))) (and nick (find nick (list* (package-name package) (package-nicknames package)))))
Well, if someone wants the "canonical" name for a package to be something that isn't actually a nickname, it's not clear that we should tell them they can't. If you don't want non-nicknames used as the canonical name, don't put them in *canonical-package-names*.
Besides the other part of the change I made will definitely show you a prompt that is not in fact a nickname if you have *auto-abbreviate-dotted-packages* set to T (as it is by default).
-Peter