Spawn new process with ADDR_NO_RANDOMIZE personality if not already set
[sxemacs] / lisp / package-ui.el
1 ;;; package-ui.el ---
2
3 ;; Copyright (C) 1998 by Darryl Okahata
4
5 ;; Author: Darryl Okahata <darrylo@sr.hp.com>
6 ;; Keywords: internal
7
8 ;; This file is part of SXEmacs.
9
10 ;; SXEmacs is free software: you can redistribute it and/or modify
11 ;; it under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation, either version 3 of the License, or
13 ;; (at your option) any later version.
14
15 ;; SXEmacs is distributed in the hope that it will be useful,
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 ;; GNU General Public License for more details.
19
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
22
23 ;;; Synched up with: Not in FSF
24
25 (require 'package-get)          ;; which, in turn, requires 'package-admin
26
27 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
28 ;; User-changeable variables:
29 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
30
31 (defgroup pui nil
32   "Convenient interface to the package system."
33   :group 'package-tools
34   :tag "Package User interface"
35   :prefix "pui-")
36
37 (defcustom pui-package-install-dest-dir nil
38   "*If non-nil (Automatic) path to package tree to install packages in.
39 Otherwise, use old path for installed packages and make a guess for
40 new ones."
41   :group 'pui
42   :tag "Install Location"
43   :type '(choice (const :tag "Automatic" nil)
44                  (directory)))
45
46 (defcustom pui-list-verbose t
47   "*If non-nil, display verbose info in the package list buffer."
48   :group 'pui
49   :tag "Verbose Listing"
50   :type 'boolean)
51
52 (defcustom pui-up-to-date-package-face nil
53   "*The face to use for packages that are up-to-date."
54   :group 'pui
55   :type 'face)
56
57 (defcustom pui-selected-package-face 'bold
58   "*The face to use for selected packages.
59 Set this to `nil' to use the `default' face."
60   :group 'pui
61   :type 'face)
62
63 (defcustom pui-deleted-package-face 'blue
64   "*The face to use for packages marked for removal.
65 Set this to `nil' to use the `default' face."
66   :group 'pui
67   :type 'face)
68
69 (defcustom pui-outdated-package-face 'red
70   "*The face to use for outdated packages.
71 Set this to `nil' to use the `default' face."
72   :group 'pui
73   :type 'face)
74
75 (defcustom pui-uninstalled-package-face 'italic
76   "*The face to use for uninstalled packages.
77 Set this to `nil' to use the `default' face."
78    :group 'pui
79    :type 'face)
80
81 (defcustom pui-info-buffer "*Packages*"
82   "*Buffer to use for displaying package information."
83   :group 'pui
84   :type 'string)
85
86 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
87 ;; End of user-changeable variables.
88 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
89
90 (defvar pui-selected-packages nil
91   "The list of user-selected packages to install.")
92
93 (defvar pui-deleted-packages nil
94   "The list of user-selected packages to remove.")
95
96 (defvar pui-actual-package "")
97
98 (defvar pui-display-keymap
99   (let ((m (make-keymap)))
100     (suppress-keymap m)
101     (set-keymap-name m 'pui-display-keymap)
102     (define-key m "q" 'pui-quit)
103     (define-key m "g" 'pui-list-packages)
104     (define-key m "i" 'pui-display-info)
105     (define-key m "m" 'pui-display-maintainer)
106     (define-key m "?" 'describe-mode)
107     (define-key m "v" 'pui-toggle-verbosity-redisplay)
108     (define-key m "d" 'pui-toggle-package-delete-key)
109     (define-key m "D" 'pui-toggle-package-delete-key)
110     (define-key m [return] 'pui-toggle-package-key)
111     (define-key m "x" 'pui-install-selected-packages)
112     (define-key m "I" 'pui-install-selected-packages)
113     (define-key m "r" 'pui-add-required-packages)
114     (define-key m "n" 'next-line)
115     (define-key m "+" 'pui-toggle-package-key)
116     (define-key m "p" 'previous-line)
117     (define-key m " " 'scroll-up-command)
118     (define-key m [delete] 'scroll-down-command)
119     m)
120   "Keymap to use in the `pui-info-buffer' buffer")
121
122 (defvar pui-package-keymap
123   (let ((m (make-sparse-keymap)))
124     (set-keymap-name m 'pui-package-keymap)
125     (define-key m 'button2 'pui-toggle-package-event)
126 ;; We use a popup menu
127     (define-key m 'button3 'pui-popup-context-sensitive)
128     m)
129   "Keymap to use over package names/descriptions.")
130
131 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
132 ;; End of variables
133
134
135 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
136 ;; Configuration routines
137
138 ;;;###autoload
139 (defun package-ui-add-site (site)
140   "Add site to package-get-remote and possibly offer to update package list."
141   (let ((had-none (null package-get-remote)))
142     (setq package-get-remote site)
143     (when (and had-none package-get-was-current
144                (y-or-n-p "Update Package list?"))
145       (setq package-get-was-current nil)
146       (package-get-require-base t)
147       (if (get-buffer pui-info-buffer)
148           (save-window-excursion
149             (pui-list-packages))))
150     (set-menubar-dirty-flag)))
151
152 ;;;###autoload
153 (defun package-ui-download-menu ()
154   "Build the `Add Download Site' menu."
155   (mapcar (lambda (site)
156             (vector (car site)
157                     `(if (equal package-get-remote (quote ,(cdr site)))
158                       (setq package-get-remote nil)
159                       (package-ui-add-site (quote ,(cdr site))))
160                     ;; I've used radio buttons so that only a single
161                     ;; site can be selected, but they are in fact
162                     ;; toggles.  SY.
163                     :style 'radio
164                     :selected `(equal package-get-remote (quote ,(cdr site)))))
165           package-get-download-sites))
166
167 ;;;###autoload
168 (defun package-ui-site-release-download-menu ()
169   "Build the 'Site Release Download Sites' menu."
170   (mapcar (lambda (site)
171             (vector (car site)
172                     `(if (equal package-get-remote (quote ,(cdr site)))
173                       (setq package-get-remote nil)
174                       (package-ui-add-site (quote ,(cdr site))))
175                     ;; I've used radio buttons so that only a single
176                     ;; site can be selected, but they are in fact
177                     ;; toggles.  SY.
178                     :style 'radio
179                     :selected `(equal package-get-remote (quote ,(cdr site)))))
180           package-get-site-release-download-sites))
181
182 ;;;###autoload
183 (defun pui-set-local-package-get-directory ()
184   "Set a new package binary directory in `package-get-remote'.
185 Note that no provision is made for saving any changes made by this function.
186 It exists mainly as a convenience for one-time package installations from
187 disk."
188   (interactive)
189   (let ((dir (read-directory-name
190               "New package binary directory to add? "
191               nil nil t)))
192     (setq package-get-remote (list nil dir))
193     (message "Package directory \"%s\" added." dir)))
194
195 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
196 ;; Package list/installer routines
197
198 (defun pui-quit ()
199   (interactive)
200   (kill-buffer nil))
201
202 (defun pui-package-symbol-char (pkg-sym version)
203   (progn
204     (if (package-get-info-find-package packages-package-list pkg-sym)
205         (let ((installed (package-get-key pkg-sym :version)))
206           (if (>= (if (stringp installed)
207                       (string-to-number installed)
208                     installed)
209                   (if (stringp version)
210                       (string-to-number version)
211                     version))
212               (list " " pui-up-to-date-package-face)
213             (list "*" pui-outdated-package-face)))
214       (list "-" pui-uninstalled-package-face))))
215
216 (defun pui-update-package-display (extent &optional pkg-sym version)
217   "Update the package status for EXTENT.
218 If PKG-SYM or VERSION are not given, they are read from the extent.
219 These are used to determine whether or not the package is installed,
220 and whether or not it is up-to-date."
221   (let (buffer-read-only disp sym-char)
222     (if (not pkg-sym)
223         (setq pkg-sym (extent-property extent 'pui-package)))
224     (if (not version)
225         (setq version (package-get-info-prop (extent-property extent 'pui-info)
226                                              'version)))
227     (cond ((member pkg-sym pui-selected-packages)
228              (if pui-selected-package-face
229                  (set-extent-face extent (get-face pui-selected-package-face))
230                (set-extent-face extent (get-face 'default)))
231              (setq sym-char "+"))
232           ((member pkg-sym pui-deleted-packages)
233            (if pui-deleted-package-face
234                  (set-extent-face extent (get-face pui-deleted-package-face))
235                (set-extent-face extent (get-face 'default)))
236              (setq sym-char "D"))
237           (t
238            (setq disp (pui-package-symbol-char pkg-sym version))
239            (setq sym-char (car disp))
240            (if (car (cdr disp))
241                (set-extent-face extent (get-face (car (cdr disp))))
242              (set-extent-face extent (get-face 'default)))))
243     (save-excursion
244       (goto-char (extent-start-position extent))
245       (delete-char 1)
246       (insert sym-char)
247       (set-buffer-modified-p nil))))
248
249 (defun pui-toggle-package (extent)
250   (let (pkg-sym)
251     (setq pkg-sym (extent-property extent 'pui-package))
252     (if (member pkg-sym pui-selected-packages)
253         (setq pui-selected-packages
254               (delete pkg-sym pui-selected-packages))
255       (setq pui-selected-packages
256             (cons pkg-sym pui-selected-packages))
257       (setq pui-deleted-packages
258             (delete pkg-sym pui-deleted-packages)))
259     (pui-update-package-display extent pkg-sym)))
260
261 (defun pui-toggle-package-key ()
262   "Select/unselect package for installation, using the keyboard."
263   (interactive)
264   (let (extent)
265     (if (setq extent (extent-at (point) (current-buffer) 'pui))
266         (progn
267           (pui-toggle-package extent)
268           (forward-line 1))
269       (error 'invalid-operation
270              "No package under cursor!"))))
271
272 (defun pui-toggle-package-delete (extent)
273   (let (pkg-sym)
274     (setq pkg-sym (extent-property extent 'pui-package))
275     (if (member pkg-sym pui-deleted-packages)
276         (setq pui-deleted-packages
277               (delete pkg-sym pui-deleted-packages))
278       (setq pui-deleted-packages
279             (cons pkg-sym pui-deleted-packages))
280       (setq pui-selected-packages
281             (delete pkg-sym pui-selected-packages)))
282     (pui-update-package-display extent pkg-sym)))
283
284
285 (defun pui-toggle-package-delete-key ()
286   "Select/unselect package for removal, using the keyboard."
287   (interactive)
288   (let (extent)
289     (if (setq extent (extent-at (point) (current-buffer) 'pui))
290         (progn
291           (pui-toggle-package-delete extent)
292           (forward-line 1))
293       (error 'invalid-operation
294              "No package under cursor!"))))
295
296 (defun pui-current-package ()
297   (let ((extent (extent-at (point) (current-buffer) 'pui)))
298     (if extent
299         (extent-property extent 'pui-package))))
300
301 (defun pui-toggle-package-event (event)
302   "Select/unselect package for installation, using the mouse."
303   (interactive "e")
304   (let* ((ep (event-point event))
305          (buffer (window-buffer (event-window event)))
306          (extent (extent-at ep buffer 'pui-package)))
307     (pui-toggle-package extent)))
308
309 (defun pui-toggle-verbosity-redisplay ()
310   "Toggle verbose package info."
311   (interactive)
312   (progn
313     (setq pui-list-verbose (not pui-list-verbose))
314     (pui-list-packages)))
315
316 (defun pui-install-selected-packages ()
317   "Install selected packages."
318   (interactive)
319   (let ((tmpbuf "*Packages-To-Remove*")
320         do-delete)
321     (when pui-deleted-packages
322       (save-window-excursion
323         (with-output-to-temp-buffer tmpbuf
324           (display-completion-list (sort
325                                     (mapcar #'symbol-name pui-deleted-packages)
326                                     #'string<)
327                                    :activate-callback nil
328                                    :help-string "Packages selected for removal:\n"
329                                    :completion-string t))
330         (setq tmpbuf (get-buffer-create tmpbuf))
331         (display-buffer tmpbuf)
332         (setq do-delete (yes-or-no-p "Remove these packages? "))
333         (kill-buffer tmpbuf))
334       (when do-delete
335         (message "Deleting selected packages ...") (sit-for 0)
336         (mapcar (lambda (pkg)
337                   (package-admin-delete-binary-package
338                    pkg (package-admin-get-install-dir pkg)))
339                 (nreverse pui-deleted-packages))
340         (message "Packages deleted"))))
341
342   (let ((tmpbuf "*Packages-To-Install*")
343         do-install)
344     (if pui-selected-packages
345         (progn
346           ;; Don't change window config when asking the user if he really
347           ;; wants to install the packages.  We do this to avoid messing up
348           ;; the window configuration if errors occur (we don't want to
349           ;; display random buffers in addition to the error buffer, if
350           ;; errors occur, which would normally be caused by display-buffer).
351           (save-window-excursion
352             (with-output-to-temp-buffer tmpbuf
353               (display-completion-list
354                (sort (mapcar #'symbol-name pui-selected-packages) #'string<)
355                :activate-callback nil
356                :help-string "Packages selected for installation:\n"
357                :completion-string t))
358             (setq tmpbuf (get-buffer-create tmpbuf))
359             (display-buffer tmpbuf)
360             (setq do-install (y-or-n-p "Install these packages? "))
361             (kill-buffer tmpbuf))
362           (if do-install
363               (progn
364                 (save-excursion
365                   ;; Clear old temp buffer history
366                   (set-buffer (get-buffer-create package-admin-temp-buffer))
367                   (buffer-disable-undo package-admin-temp-buffer)
368                   (erase-buffer package-admin-temp-buffer))
369                 (message "Installing selected packages ...") (sit-for 0)
370                 (if (catch 'done
371                       (mapcar (lambda (pkg)
372                                 (if (not (package-get pkg nil nil
373                                                       pui-package-install-dest-dir))
374                                     (throw 'done nil)))
375                               (nreverse pui-selected-packages))
376                       t)
377                     (progn
378                       (pui-list-packages)
379                       (message "Packages installed"))))
380             (clear-message)))
381       (if pui-deleted-packages
382           (pui-list-packages)
383         (error 'invalid-operation
384                "No packages have been selected!")))))
385
386 (defun pui-add-required-packages ()
387   "Select packages required by those already selected for installation."
388   (interactive)
389   (let ((tmpbuf "*Required-Packages*") do-select)
390     (if pui-selected-packages
391         (let ((dependencies
392                (delq nil (mapcar
393                           (lambda (pkg)
394                             (let ((installed
395                                    (package-get-key pkg :version))
396                                   (current
397                                    (package-get-info-prop
398                                     (package-get-info-version
399                                      (package-get-info-find-package
400                                       package-get-base pkg) nil)
401                                     'version)))
402                               (if (or (null installed)
403                                      (< (if (stringp installed)
404                                          (string-to-number installed)
405                                        installed)
406                                      (if (stringp current)
407                                          (string-to-number current)
408                                        current)))
409                                   pkg
410                                 nil)))
411                           (package-get-dependencies pui-selected-packages)))))
412           ;; Don't change window config when asking the user if he really
413           ;; wants to add the packages.  We do this to avoid messing up
414           ;; the window configuration if errors occur (we don't want to
415           ;; display random buffers in addition to the error buffer, if
416           ;; errors occur, which would normally be caused by display-buffer).
417           (save-window-excursion
418             (with-output-to-temp-buffer tmpbuf
419               (display-completion-list (sort
420                                         (mapcar #'(lambda (pkg)
421                                                     (symbol-name pkg))
422                                                 dependencies)
423                                         'string<)
424                                        :activate-callback nil
425                                        :help-string "Required packages:\n"
426                                        :completion-string t))
427             (setq tmpbuf (get-buffer-create tmpbuf))
428             (display-buffer tmpbuf)
429             (setq do-select (y-or-n-p "Select these packages? "))
430             (kill-buffer tmpbuf))
431           (if do-select
432               (progn
433                 (setq pui-selected-packages
434                       (union pui-selected-packages dependencies))
435                 (map-extents #'(lambda (extent maparg)
436                                  (pui-update-package-display extent))
437                              nil nil nil nil nil 'pui)
438                 (message "added dependencies"))
439               (clear-message)))
440       (error 'invalid-operation
441              "No packages have been selected!"))))
442
443 (defun pui-help-echo (extent &optional force-update)
444   "Display additional package info in the modeline.
445 EXTENT determines the package to display (the package information is
446 attached to the extent as properties)."
447   (let (pkg-sym info inst-ver inst-auth-ver auth-ver date maintainer balloon req)
448     (if (or force-update (not (current-message))
449             (string-match ".*: .*: " (current-message)))
450         (progn
451           (setq pkg-sym (extent-property extent 'pui-package)
452                 info (extent-property extent 'pui-info)
453                 inst-ver (package-get-key pkg-sym :version)
454                 inst-auth-ver (package-get-key pkg-sym :author-version)
455                 auth-ver (package-get-info-prop info 'author-version)
456                 date (package-get-info-prop info 'date)
457                 maintainer (package-get-info-prop info 'maintainer)
458                 req (package-get-info-prop info 'requires))
459           (if (not inst-ver)
460               (setq inst-ver 0))
461           (if (featurep 'balloon-help)
462               (progn
463                 (setq balloon (format "
464 Package Information:  [For package: \"%s\"]\n================
465 Installed Upstream Ver: %s  Available Upstream Ver: %s
466 Maintainer : %s
467 Released : %s
468 Required Packages : %s\n\n"
469                                       pkg-sym inst-auth-ver auth-ver maintainer
470                                       date req))
471                 (set-extent-property extent 'balloon-help balloon)))
472           (format
473            "Installed upstream ver: %s  Available upstream ver: %s"
474            inst-auth-ver auth-ver)))))
475
476 (defun pui-display-info (&optional no-error event)
477   "Display additional package info in the modeline.
478 Designed to be called interactively (from a keypress)."
479   (interactive)
480   (let (extent)
481     (save-excursion
482       (beginning-of-line)
483       (if (setq extent  (extent-at (point) (current-buffer) 'pui))
484           (message (pui-help-echo extent t))
485         (if no-error
486             (clear-message nil)
487           (error 'invalid-operation
488                  "No package under cursor!"))))))
489
490 (defun pui-display-maintainer (&optional no-error event)
491   "Display a package's maintainer in the minibuffer."
492   (interactive)
493   (let (extent ;pkg-sym
494         info maintainer)
495     (save-excursion
496       (beginning-of-line)
497       (if (setq extent  (extent-at (point) (current-buffer) 'pui))
498           (progn
499             (setq ;pkg-sym (extent-property extent 'pui-package)
500              info (extent-property extent 'pui-info)
501              maintainer (package-get-info-prop info 'maintainer))
502             (message (format "Maintainer: %s" maintainer)))
503         (if no-error
504             (clear-message nil)
505           (error 'invalid-operation
506                  "No package under cursor!"))))))
507
508 (defvar pui-menu
509   '("Packages"
510     ["Toggle install " pui-toggle-package-key :active (pui-current-package) :suffix (format "`%s'" (or (pui-current-package) "..."))]
511     ["Toggle delete " pui-toggle-package-delete-key :active (pui-current-package) :suffix (format "`%s'" (or (pui-current-package) "..."))]
512     ["Info on" pui-display-info  :active (pui-current-package) :suffix (format "`%s'" (or (pui-current-package) "..."))]
513     "---"
514     ["Add Required" pui-add-required-packages t]
515     ["Install/Remove Selected" pui-install-selected-packages t]
516     "---"
517     ["Verbose" pui-toggle-verbosity-redisplay
518      :active t :style toggle :selected pui-list-verbose]
519     ["Refresh" pui-list-packages t]
520     ["Help" pui-help t]
521     ["Quit" pui-quit t]))
522
523 ;;; "Why is there no standard function to do this?"
524 (defun pui-popup-context-sensitive (event)
525   (interactive "e")
526   (save-excursion
527     (set-buffer (event-buffer event))
528     (goto-char (event-point event))
529     (popup-menu pui-menu event)
530     ;; I agree with dired.el - this is seriously bogus.
531     (while (popup-up-p)
532       (dispatch-event (next-event)))))
533
534 (defun list-packages-mode ()
535     "Symbols in the leftmost column:
536
537   +     The package is marked for installation.
538   -     The package has not been installed.
539   D     The package has been marked for deletion.
540   *     The currently installed package is old, and a newer version is
541         available.
542
543 Useful keys:
544
545   `\\[pui-toggle-package-key]' to select/unselect the current package for installation.
546   `\\[pui-toggle-package-delete-key]' to select/unselect the current package for removal.
547   `\\[pui-add-required-packages]' to add any packages required by those selected.
548   `\\[pui-install-selected-packages]' to install/delete selected packages.
549   `\\[pui-display-info]' to display additional information about the package in the minibuffer.
550   `\\[pui-display-maintainer]' to display the package's maintainer in the minibuffer
551   `\\[pui-list-packages]' to refresh the package list.
552   `\\[pui-toggle-verbosity-redisplay]' to toggle between a verbose and non-verbose display.
553   `\\[pui-quit]' to kill this buffer.
554 "
555   (error 'invalid-operation
556          "You cannot enter this mode directly. Use `pui-list-packages'"))
557
558 (put 'list-packages-mode 'mode-class 'special)
559
560 ;;;###autoload
561 (defun pui-list-packages ()
562   "List all packages and package information.
563 The package name, version, and description are displayed.  From the displayed
564 buffer, the user can see which packages are installed, which are not, and
565 which are out-of-date (a newer version is available).  The user can then
566 select packages for installation via the keyboard or mouse."
567   (interactive)
568   (package-get-require-base t)
569   (let ((outbuf (get-buffer-create pui-info-buffer))
570         (sep-string "===============================================================================\n")
571         start)
572     (message "Creating package list ...") (sit-for 0)
573     (set-buffer outbuf)
574     (setq buffer-read-only nil)
575     (buffer-disable-undo outbuf)
576     (erase-buffer outbuf)
577     (kill-all-local-variables)
578     (use-local-map pui-display-keymap)
579     (setq major-mode 'list-packages-mode)
580     (setq mode-name "Packages")
581     (setq truncate-lines t)
582
583     (unless package-get-remote
584       (insert "
585 Warning: No download sites specified.  Package index may be out of date.
586          If you intend to install packages, specify download sites first.
587
588 "))
589
590     (if pui-list-verbose
591         (insert "                       Latest Installed
592   Package name         Vers.  Vers.   Description
593 ")
594       (insert "                       Latest
595   Package name         Vers.  Description
596 "))
597     (insert sep-string)
598     (setq start (point))
599     (mapcar
600      #'(lambda (pkg)
601          (let (pkg-sym info version desc
602                        b e extent current-vers disp)
603            (setq pkg-sym (car pkg)
604                  info (package-get-info-version (cdr pkg) nil))
605            (setq version (package-get-info-prop info 'version)
606                  desc (package-get-info-prop info 'description))
607
608            (setq disp (pui-package-symbol-char pkg-sym
609                                                version))
610            (setq b (point))
611            (if pui-list-verbose
612                (progn
613                  (setq current-vers (package-get-key pkg-sym :version))
614                  (cond
615                   ((not current-vers)
616                    (setq current-vers "-----"))
617                   ((stringp current-vers)
618                    (setq current-vers
619                          (format "%.2f"
620                                  (string-to-number current-vers))))
621                   ((numberp current-vers)
622                    (setq current-vers (format "%.2f" current-vers))))
623                  (insert
624                   (format "%s %-20s %-5.2f  %-5s  %s\n"
625                           (car disp) pkg-sym
626                           (if (stringp version)
627                               (string-to-number version)
628                             version)
629                           current-vers desc)))
630              (insert (format "%s %-20s %-5s %s\n"
631                              (car disp)
632                              pkg-sym version desc)))
633            (save-excursion
634              (setq e (progn
635                        (forward-line -1)
636                        (end-of-line)
637                        (point))))
638            (setq extent (make-extent b e))
639            (if (car (cdr disp))
640                (set-extent-face extent (get-face (car (cdr disp))))
641              (set-extent-face extent (get-face 'default)))
642            (set-extent-property extent 'highlight t)
643            (set-extent-property extent 'pui t)
644            (set-extent-property extent 'pui-package pkg-sym)
645            (set-extent-property extent 'pui-info info)
646            (set-extent-property extent 'help-echo 'pui-help-echo)
647            (set-extent-property extent 'keymap pui-package-keymap)))
648      (sort (copy-sequence package-get-base)
649            #'(lambda (a b)
650                (string< (symbol-name (car a))
651                         (symbol-name (car b))))))
652     (insert sep-string)
653     (insert (documentation 'list-packages-mode))
654     (set-buffer-modified-p nil)
655     (setq buffer-read-only t)
656     (pop-to-buffer outbuf)
657     (delete-other-windows)
658     (goto-char start)
659     (setq pui-selected-packages nil)    ; Reset list
660     (setq pui-deleted-packages nil)     ; Reset list
661     (when (featurep 'menubar)
662       (set-buffer-menubar current-menubar)
663       (add-submenu '() pui-menu)
664       (setq mode-popup-menu pui-menu))
665     (clear-message)))
666
667 ;;;###autoload
668 (defalias 'list-packages 'pui-list-packages)
669
670 (provide 'package-ui)
671
672 ;;; package-ui.el ends here