summaryrefslogtreecommitdiff
path: root/lisp/emacs-lisp
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/emacs-lisp')
-rw-r--r--lisp/emacs-lisp/advice.el1
-rw-r--r--lisp/emacs-lisp/authors.el1
-rw-r--r--lisp/emacs-lisp/autoload.el54
-rw-r--r--lisp/emacs-lisp/backquote.el1
-rw-r--r--lisp/emacs-lisp/byte-opt.el1
-rw-r--r--lisp/emacs-lisp/byte-run.el2
-rw-r--r--lisp/emacs-lisp/bytecomp.el35
-rw-r--r--lisp/emacs-lisp/cl-compat.el10
-rw-r--r--lisp/emacs-lisp/cl-extra.el1
-rw-r--r--lisp/emacs-lisp/cl-indent.el1
-rw-r--r--lisp/emacs-lisp/cl-loaddefs.el21
-rw-r--r--lisp/emacs-lisp/cl-macs.el3
-rw-r--r--lisp/emacs-lisp/cl-seq.el13
-rw-r--r--lisp/emacs-lisp/cl-specs.el1
-rw-r--r--lisp/emacs-lisp/copyright.el22
-rw-r--r--lisp/emacs-lisp/debug.el2
-rw-r--r--lisp/emacs-lisp/derived.el1
-rw-r--r--lisp/emacs-lisp/easy-mmode.el31
-rw-r--r--lisp/emacs-lisp/easymenu.el4
-rw-r--r--lisp/emacs-lisp/eieio-base.el1
-rw-r--r--lisp/emacs-lisp/eieio-comp.el3
-rw-r--r--lisp/emacs-lisp/eieio-custom.el1
-rw-r--r--lisp/emacs-lisp/eieio-datadebug.el1
-rw-r--r--lisp/emacs-lisp/eieio-opt.el1
-rw-r--r--lisp/emacs-lisp/eieio-speedbar.el1
-rw-r--r--lisp/emacs-lisp/eieio.el3
-rw-r--r--lisp/emacs-lisp/float-sup.el1
-rw-r--r--lisp/emacs-lisp/generic.el1
-rw-r--r--lisp/emacs-lisp/helper.el1
-rw-r--r--lisp/emacs-lisp/lisp-mnt.el4
-rw-r--r--lisp/emacs-lisp/lisp-mode.el17
-rw-r--r--lisp/emacs-lisp/lisp.el9
-rw-r--r--lisp/emacs-lisp/macroexp.el139
-rw-r--r--lisp/emacs-lisp/package-x.el1
-rw-r--r--lisp/emacs-lisp/package.el871
-rw-r--r--lisp/emacs-lisp/pcase.el38
-rw-r--r--lisp/emacs-lisp/regexp-opt.el2
-rw-r--r--lisp/emacs-lisp/rx.el7
-rw-r--r--lisp/emacs-lisp/smie.el782
-rw-r--r--lisp/emacs-lisp/syntax.el250
-rw-r--r--lisp/emacs-lisp/tcover-ses.el1
-rw-r--r--lisp/emacs-lisp/tcover-unsafep.el1
-rw-r--r--lisp/emacs-lisp/timer.el4
43 files changed, 1555 insertions, 790 deletions
diff --git a/lisp/emacs-lisp/advice.el b/lisp/emacs-lisp/advice.el
index 9267bc8ac91..578e0877d30 100644
--- a/lisp/emacs-lisp/advice.el
+++ b/lisp/emacs-lisp/advice.el
@@ -7,6 +7,7 @@
;; Maintainer: FSF
;; Created: 12 Dec 1992
;; Keywords: extensions, lisp, tools
+;; Package: emacs
;; This file is part of GNU Emacs.
diff --git a/lisp/emacs-lisp/authors.el b/lisp/emacs-lisp/authors.el
index 5aea033fc78..3bfa076d71c 100644
--- a/lisp/emacs-lisp/authors.el
+++ b/lisp/emacs-lisp/authors.el
@@ -6,6 +6,7 @@
;; Author: Gerd Moellmann <gerd@gnu.org>
;; Maintainer: Kim F. Storm <storm@cua.dk>
;; Keywords: maint
+;; Package: emacs
;; This file is part of GNU Emacs.
diff --git a/lisp/emacs-lisp/autoload.el b/lisp/emacs-lisp/autoload.el
index c5316d06429..30c384aff91 100644
--- a/lisp/emacs-lisp/autoload.el
+++ b/lisp/emacs-lisp/autoload.el
@@ -6,6 +6,7 @@
;; Author: Roland McGrath <roland@gnu.org>
;; Keywords: maint
+;; Package: emacs
;; This file is part of GNU Emacs.
@@ -109,29 +110,48 @@ or macro definition or a defcustom)."
(let* ((macrop (memq car '(defmacro defmacro*)))
(name (nth 1 form))
(args (case car
- ((defun defmacro defun* defmacro*
- define-overloadable-function) (nth 2 form))
- ((define-skeleton) '(&optional str arg))
- ((define-generic-mode define-derived-mode
- define-compilation-mode) nil)
- (t)))
+ ((defun defmacro defun* defmacro*
+ define-overloadable-function) (nth 2 form))
+ ((define-skeleton) '(&optional str arg))
+ ((define-generic-mode define-derived-mode
+ define-compilation-mode) nil)
+ (t)))
(body (nthcdr (get car 'doc-string-elt) form))
(doc (if (stringp (car body)) (pop body))))
(when (listp args)
;; Add the usage form at the end where describe-function-1
;; can recover it.
(setq doc (help-add-fundoc-usage doc args)))
- ;; `define-generic-mode' quotes the name, so take care of that
- (list 'autoload (if (listp name) name (list 'quote name)) file doc
- (or (and (memq car '(define-skeleton define-derived-mode
- define-generic-mode
- easy-mmode-define-global-mode
- define-global-minor-mode
- define-globalized-minor-mode
- easy-mmode-define-minor-mode
- define-minor-mode)) t)
- (eq (car-safe (car body)) 'interactive))
- (if macrop (list 'quote 'macro) nil))))
+ (let ((exp
+ ;; `define-generic-mode' quotes the name, so take care of that
+ (list 'autoload (if (listp name) name (list 'quote name))
+ file doc
+ (or (and (memq car '(define-skeleton define-derived-mode
+ define-generic-mode
+ easy-mmode-define-global-mode
+ define-global-minor-mode
+ define-globalized-minor-mode
+ easy-mmode-define-minor-mode
+ define-minor-mode)) t)
+ (eq (car-safe (car body)) 'interactive))
+ (if macrop (list 'quote 'macro) nil))))
+ (when macrop
+ ;; Special case to autoload some of the macro's declarations.
+ (let ((decls (nth (if (stringp (nth 3 form)) 4 3) form))
+ (exps '()))
+ (when (eq (car decls) 'declare)
+ ;; FIXME: We'd like to reuse macro-declaration-function,
+ ;; but we can't since it doesn't return anything.
+ (dolist (decl decls)
+ (case (car-safe decl)
+ (indent
+ (push `(put ',name 'lisp-indent-function ',(cadr decl))
+ exps))
+ (doc-string
+ (push `(put ',name 'doc-string-elt ',(cadr decl)) exps))))
+ (when exps
+ (setq exp `(progn ,exp ,@exps))))))
+ exp)))
;; For defclass forms, use `eieio-defclass-autoload'.
((eq car 'defclass)
diff --git a/lisp/emacs-lisp/backquote.el b/lisp/emacs-lisp/backquote.el
index 998cee15342..96e2fb41e89 100644
--- a/lisp/emacs-lisp/backquote.el
+++ b/lisp/emacs-lisp/backquote.el
@@ -6,6 +6,7 @@
;; Author: Rick Sladkey <jrs@world.std.com>
;; Maintainer: FSF
;; Keywords: extensions, internal
+;; Package: emacs
;; This file is part of GNU Emacs.
diff --git a/lisp/emacs-lisp/byte-opt.el b/lisp/emacs-lisp/byte-opt.el
index 4c0094dd78b..9ce3c2eb323 100644
--- a/lisp/emacs-lisp/byte-opt.el
+++ b/lisp/emacs-lisp/byte-opt.el
@@ -7,6 +7,7 @@
;; Hallvard Furuseth <hbf@ulrik.uio.no>
;; Maintainer: FSF
;; Keywords: internal
+;; Package: emacs
;; This file is part of GNU Emacs.
diff --git a/lisp/emacs-lisp/byte-run.el b/lisp/emacs-lisp/byte-run.el
index dbbf057ae22..6ce141eb8e6 100644
--- a/lisp/emacs-lisp/byte-run.el
+++ b/lisp/emacs-lisp/byte-run.el
@@ -7,6 +7,7 @@
;; Hallvard Furuseth <hbf@ulrik.uio.no>
;; Maintainer: FSF
;; Keywords: internal
+;; Package: emacs
;; This file is part of GNU Emacs.
@@ -65,7 +66,6 @@ The return value of this function is not used."
;; Redefined in byte-optimize.el.
;; This is not documented--it's not clear that we should promote it.
(fset 'inline 'progn)
-(put 'inline 'lisp-indent-function 0)
;;; Interface to inline functions.
diff --git a/lisp/emacs-lisp/bytecomp.el b/lisp/emacs-lisp/bytecomp.el
index df93528683c..e1b5b402b28 100644
--- a/lisp/emacs-lisp/bytecomp.el
+++ b/lisp/emacs-lisp/bytecomp.el
@@ -1,12 +1,14 @@
;;; bytecomp.el --- compilation of Lisp code into byte code
;; Copyright (C) 1985, 1986, 1987, 1992, 1994, 1998, 2000, 2001, 2002,
-;; 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+;; 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
+;; Free Software Foundation, Inc.
;; Author: Jamie Zawinski <jwz@lucid.com>
;; Hallvard Furuseth <hbf@ulrik.uio.no>
;; Maintainer: FSF
;; Keywords: lisp
+;; Package: emacs
;; This file is part of GNU Emacs.
@@ -1665,6 +1667,9 @@ that already has a `.elc' file."
(if (and (string-match emacs-lisp-file-regexp bytecomp-source)
(file-readable-p bytecomp-source)
(not (auto-save-file-name-p bytecomp-source))
+ (not (string-equal dir-locals-file
+ (file-name-nondirectory
+ bytecomp-source)))
(setq bytecomp-dest
(byte-compile-dest-file bytecomp-source))
(if (file-exists-p bytecomp-dest)
@@ -1811,17 +1816,25 @@ The value is non-nil if there were no errors, nil if errors."
(insert "\n") ; aaah, unix.
(if (file-writable-p target-file)
;; We must disable any code conversion here.
- (let ((coding-system-for-write 'no-conversion))
+ (let ((coding-system-for-write 'no-conversion)
+ ;; Write to a tempfile so that if another Emacs
+ ;; process is trying to load target-file (eg in a
+ ;; parallel bootstrap), it does not risk getting a
+ ;; half-finished file. (Bug#4196)
+ (tempfile (make-temp-name target-file)))
(if (memq system-type '(ms-dos 'windows-nt))
(setq buffer-file-type t))
- (when (file-exists-p target-file)
- ;; Remove the target before writing it, so that any
- ;; hard-links continue to point to the old file (this makes
- ;; it possible for installed files to share disk space with
- ;; the build tree, without causing problems when emacs-lisp
- ;; files in the build tree are recompiled).
- (delete-file target-file))
- (write-region (point-min) (point-max) target-file))
+ (write-region (point-min) (point-max) tempfile nil 1)
+ ;; This has the intentional side effect that any
+ ;; hard-links to target-file continue to
+ ;; point to the old file (this makes it possible
+ ;; for installed files to share disk space with
+ ;; the build tree, without causing problems when
+ ;; emacs-lisp files in the build tree are
+ ;; recompiled). Previously this was accomplished by
+ ;; deleting target-file before writing it.
+ (rename-file tempfile target-file t)
+ (message "Wrote %s" target-file))
;; This is just to give a better error message than write-region
(signal 'file-error
(list "Opening output file"
@@ -4648,6 +4661,8 @@ and corresponding effects."
(defvar byte-code-meter)
(defun byte-compile-report-ops ()
+ (or (boundp 'byte-metering-on)
+ (error "You must build Emacs with -DBYTE_CODE_METER to use this"))
(with-output-to-temp-buffer "*Meter*"
(set-buffer "*Meter*")
(let ((i 0) n op off)
diff --git a/lisp/emacs-lisp/cl-compat.el b/lisp/emacs-lisp/cl-compat.el
index 68d7c0ae3ba..f4923b6f8c6 100644
--- a/lisp/emacs-lisp/cl-compat.el
+++ b/lisp/emacs-lisp/cl-compat.el
@@ -6,6 +6,7 @@
;; Author: Dave Gillespie <daveg@synaptics.com>
;; Version: 2.02
;; Keywords: extensions
+;; Package: emacs
;; This file is part of GNU Emacs.
@@ -70,11 +71,6 @@
;;; by capitalizing the first letter: Values, Multiple-value-*,
;;; to avoid conflict with the new-style definitions in cl-macs.
-(put 'Multiple-value-bind 'lisp-indent-function 2)
-(put 'Multiple-value-setq 'lisp-indent-function 2)
-(put 'Multiple-value-call 'lisp-indent-function 1)
-(put 'Multiple-value-prog1 'lisp-indent-function 1)
-
(defvar *mvalues-values* nil)
(defun Values (&rest val-forms)
@@ -90,18 +86,22 @@
(list *mvalues-temp*))))
(defmacro Multiple-value-call (function &rest args)
+ (declare (indent 1))
(list 'apply function
(cons 'append
(mapcar (function (lambda (x) (list 'Multiple-value-list x)))
args))))
(defmacro Multiple-value-bind (vars form &rest body)
+ (declare (indent 2))
(list* 'multiple-value-bind vars (list 'Multiple-value-list form) body))
(defmacro Multiple-value-setq (vars form)
+ (declare (indent 2))
(list 'multiple-value-setq vars (list 'Multiple-value-list form)))
(defmacro Multiple-value-prog1 (form &rest body)
+ (declare (indent 1))
(list 'prog1 form (list* 'let '((*mvalues-values* nil)) body)))
diff --git a/lisp/emacs-lisp/cl-extra.el b/lisp/emacs-lisp/cl-extra.el
index c6aae373589..b7c908882ed 100644
--- a/lisp/emacs-lisp/cl-extra.el
+++ b/lisp/emacs-lisp/cl-extra.el
@@ -5,6 +5,7 @@
;; Author: Dave Gillespie <daveg@synaptics.com>
;; Keywords: extensions
+;; Package: emacs
;; This file is part of GNU Emacs.
diff --git a/lisp/emacs-lisp/cl-indent.el b/lisp/emacs-lisp/cl-indent.el
index e4f605d4fd0..4e7ada8851f 100644
--- a/lisp/emacs-lisp/cl-indent.el
+++ b/lisp/emacs-lisp/cl-indent.el
@@ -7,6 +7,7 @@
;; Created: July 1987
;; Maintainer: FSF
;; Keywords: lisp, tools
+;; Package: emacs
;; This file is part of GNU Emacs.
diff --git a/lisp/emacs-lisp/cl-loaddefs.el b/lisp/emacs-lisp/cl-loaddefs.el
index b14c879fcf7..db2ae88b8b7 100644
--- a/lisp/emacs-lisp/cl-loaddefs.el
+++ b/lisp/emacs-lisp/cl-loaddefs.el
@@ -10,7 +10,7 @@
;;;;;; ceiling* floor* isqrt lcm gcd cl-progv-before cl-set-frame-visible-p
;;;;;; cl-map-overlays cl-map-intervals cl-map-keymap-recursively
;;;;;; notevery notany every some mapcon mapcan mapl maplist map
-;;;;;; cl-mapcar-many equalp coerce) "cl-extra" "cl-extra.el" "deb3495d75c36a222e5238eadb8e347c")
+;;;;;; cl-mapcar-many equalp coerce) "cl-extra" "cl-extra.el" "20c8c875ff1d11dd819e15a1f25afd73")
;;; Generated autoloads from cl-extra.el
(autoload 'coerce "cl-extra" "\
@@ -277,12 +277,12 @@ Not documented
;;;;;; assert check-type typep deftype cl-struct-setf-expander defstruct
;;;;;; define-modify-macro callf2 callf letf* letf rotatef shiftf
;;;;;; remf cl-do-pop psetf setf get-setf-method defsetf define-setf-method
-;;;;;; declare the locally multiple-value-setq multiple-value-bind
-;;;;;; lexical-let* lexical-let symbol-macrolet macrolet labels
-;;;;;; flet progv psetq do-all-symbols do-symbols dotimes dolist
-;;;;;; do* do loop return-from return block etypecase typecase ecase
-;;;;;; case load-time-value eval-when destructuring-bind function*
-;;;;;; defmacro* defun* gentemp gensym) "cl-macs" "cl-macs.el" "36cafd5054969b5bb0b1ce6a21605fed")
+;;;;;; declare locally multiple-value-setq multiple-value-bind lexical-let*
+;;;;;; lexical-let symbol-macrolet macrolet labels flet progv psetq
+;;;;;; do-all-symbols do-symbols dotimes dolist do* do loop return-from
+;;;;;; return block etypecase typecase ecase case load-time-value
+;;;;;; eval-when destructuring-bind function* defmacro* defun* gentemp
+;;;;;; gensym) "cl-macs" "cl-macs.el" "c10b5cbebb5267291ef15c782c0271a6")
;;; Generated autoloads from cl-macs.el
(autoload 'gensym "cl-macs" "\
@@ -535,11 +535,6 @@ Not documented
\(fn &rest BODY)" nil (quote macro))
-(autoload 'the "cl-macs" "\
-Not documented
-
-\(fn TYPE FORM)" nil (quote macro))
-
(autoload 'declare "cl-macs" "\
Not documented
@@ -759,7 +754,7 @@ surrounded by (block NAME ...).
;;;;;; find nsubstitute-if-not nsubstitute-if nsubstitute substitute-if-not
;;;;;; substitute-if substitute delete-duplicates remove-duplicates
;;;;;; delete-if-not delete-if delete* remove-if-not remove-if remove*
-;;;;;; replace fill reduce) "cl-seq" "cl-seq.el" "ec3ea1c77742734db8496272fe5721be")
+;;;;;; replace fill reduce) "cl-seq" "cl-seq.el" "7b7531276ddf8457abecdd487d3cf0b7")
;;; Generated autoloads from cl-seq.el
(autoload 'reduce "cl-seq" "\
diff --git a/lisp/emacs-lisp/cl-macs.el b/lisp/emacs-lisp/cl-macs.el
index 694a06f8338..f6d66c64c7a 100644
--- a/lisp/emacs-lisp/cl-macs.el
+++ b/lisp/emacs-lisp/cl-macs.el
@@ -6,6 +6,7 @@
;; Author: Dave Gillespie <daveg@synaptics.com>
;; Version: 2.02
;; Keywords: extensions
+;; Package: emacs
;; This file is part of GNU Emacs.
@@ -1818,8 +1819,6 @@ Example:
(defsetf window-start set-window-start)
(defsetf window-width () (store)
(list 'progn (list 'enlarge-window (list '- store '(window-width)) t) store))
-(defsetf x-get-cutbuffer x-store-cutbuffer t)
-(defsetf x-get-cut-buffer x-store-cut-buffer t) ; groan.
(defsetf x-get-secondary-selection x-own-secondary-selection t)
(defsetf x-get-selection x-own-selection t)
diff --git a/lisp/emacs-lisp/cl-seq.el b/lisp/emacs-lisp/cl-seq.el
index a823e9015db..a5070e4acea 100644
--- a/lisp/emacs-lisp/cl-seq.el
+++ b/lisp/emacs-lisp/cl-seq.el
@@ -6,6 +6,7 @@
;; Author: Dave Gillespie <daveg@synaptics.com>
;; Version: 2.02
;; Keywords: extensions
+;; Package: emacs
;; This file is part of GNU Emacs.
@@ -47,6 +48,7 @@
;;; this file independent from cl-macs.
(defmacro cl-parsing-keywords (kwords other-keys &rest body)
+ (declare (indent 2) (debug (sexp sexp &rest form)))
(cons
'let*
(cons (mapcar
@@ -83,13 +85,13 @@
(car cl-keys-temp)))
'(setq cl-keys-temp (cdr (cdr cl-keys-temp)))))))
body))))
-(put 'cl-parsing-keywords 'lisp-indent-function 2)
-(put 'cl-parsing-keywords 'edebug-form-spec '(sexp sexp &rest form))
(defmacro cl-check-key (x)
+ (declare (debug edebug-forms))
(list 'if 'cl-key (list 'funcall 'cl-key x) x))
(defmacro cl-check-test-nokey (item x)
+ (declare (debug edebug-forms))
(list 'cond
(list 'cl-test
(list 'eq (list 'not (list 'funcall 'cl-test item x))
@@ -100,20 +102,17 @@
(list 'equal item x) (list 'eq item x)))))
(defmacro cl-check-test (item x)
+ (declare (debug edebug-forms))
(list 'cl-check-test-nokey item (list 'cl-check-key x)))
(defmacro cl-check-match (x y)
+ (declare (debug edebug-forms))
(setq x (list 'cl-check-key x) y (list 'cl-check-key y))
(list 'if 'cl-test
(list 'eq (list 'not (list 'funcall 'cl-test x y)) 'cl-test-not)
(list 'if (list 'numberp x)
(list 'equal x y) (list 'eq x y))))
-(put 'cl-check-key 'edebug-form-spec 'edebug-forms)
-(put 'cl-check-test 'edebug-form-spec 'edebug-forms)
-(put 'cl-check-test-nokey 'edebug-form-spec 'edebug-forms)
-(put 'cl-check-match 'edebug-form-spec 'edebug-forms)
-
(defvar cl-test) (defvar cl-test-not)
(defvar cl-if) (defvar cl-if-not)
(defvar cl-key)
diff --git a/lisp/emacs-lisp/cl-specs.el b/lisp/emacs-lisp/cl-specs.el
index acfd3504ec7..776ce5e9ca1 100644
--- a/lisp/emacs-lisp/cl-specs.el
+++ b/lisp/emacs-lisp/cl-specs.el
@@ -4,6 +4,7 @@
;; 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
;; Author: Daniel LaLiberte <liberte@holonexus.org>
;; Keywords: lisp, tools, maint
+;; Package: emacs
;; LCD Archive Entry:
;; cl-specs.el|Daniel LaLiberte|liberte@holonexus.org
diff --git a/lisp/emacs-lisp/copyright.el b/lisp/emacs-lisp/copyright.el
index 6f7a43af844..43eb61b0bee 100644
--- a/lisp/emacs-lisp/copyright.el
+++ b/lisp/emacs-lisp/copyright.el
@@ -158,13 +158,15 @@ When this is `function', only ask when called non-interactively."
(unless (string= (buffer-substring (- (match-end 3) 2) (match-end 3))
(substring copyright-current-year -2))
(if (or noquery
- ;; Fixes some point-moving oddness (bug#2209).
- (save-excursion
- (y-or-n-p (if replace
- (concat "Replace copyright year(s) by "
- copyright-current-year "? ")
- (concat "Add " copyright-current-year
- " to copyright? ")))))
+ (save-window-excursion
+ (switch-to-buffer (current-buffer))
+ ;; Fixes some point-moving oddness (bug#2209).
+ (save-excursion
+ (y-or-n-p (if replace
+ (concat "Replace copyright year(s) by "
+ copyright-current-year "? ")
+ (concat "Add " copyright-current-year
+ " to copyright? "))))))
(if replace
(replace-match copyright-current-year t t nil 3)
(let ((size (save-excursion (skip-chars-backward "0-9"))))
@@ -224,8 +226,10 @@ version \\([0-9]+\\), or (at"
(string-to-number copyright-current-gpl-version))
(or noquery
(save-match-data
- (y-or-n-p (format "Replace GPL version by %s? "
- copyright-current-gpl-version))))
+ (save-window-excursion
+ (switch-to-buffer (current-buffer))
+ (y-or-n-p (format "Replace GPL version by %s? "
+ copyright-current-gpl-version)))))
(progn
(if (match-end 2)
;; Esperanto bilingual comment in two-column.el
diff --git a/lisp/emacs-lisp/debug.el b/lisp/emacs-lisp/debug.el
index b8ff3c03ee9..17fcf7ad6c5 100644
--- a/lisp/emacs-lisp/debug.el
+++ b/lisp/emacs-lisp/debug.el
@@ -514,9 +514,9 @@ Applies to the frame whose line point is on in the backtrace."
(insert ? )))
(beginning-of-line))
-(put 'debugger-env-macro 'lisp-indent-function 0)
(defmacro debugger-env-macro (&rest body)
"Run BODY in original environment."
+ (declare (indent 0))
`(save-excursion
(if (null (buffer-name debugger-old-buffer))
;; old buffer deleted
diff --git a/lisp/emacs-lisp/derived.el b/lisp/emacs-lisp/derived.el
index d6f717ccda7..3456d1a63fb 100644
--- a/lisp/emacs-lisp/derived.el
+++ b/lisp/emacs-lisp/derived.el
@@ -7,6 +7,7 @@
;; Author: David Megginson (dmeggins@aix1.uottawa.ca)
;; Maintainer: FSF
;; Keywords: extensions
+;; Package: emacs
;; This file is part of GNU Emacs.
diff --git a/lisp/emacs-lisp/easy-mmode.el b/lisp/emacs-lisp/easy-mmode.el
index 5a21946183e..e11572dfc62 100644
--- a/lisp/emacs-lisp/easy-mmode.el
+++ b/lisp/emacs-lisp/easy-mmode.el
@@ -5,6 +5,7 @@
;; Author: Georges Brun-Cottan <Georges.Brun-Cottan@inria.fr>
;; Maintainer: Stefan Monnier <monnier@gnu.org>
+;; Package: emacs
;; Keywords: extensions lisp
@@ -86,25 +87,23 @@ replacing its case-insensitive matches with the literal string in LIGHTER."
;;;###autoload
(defmacro define-minor-mode (mode doc &optional init-value lighter keymap &rest body)
"Define a new minor mode MODE.
-This function defines the associated control variable MODE, keymap MODE-map,
-and toggle command MODE.
-
+This defines the control variable MODE and the toggle command MODE.
DOC is the documentation for the mode toggle command.
+
Optional INIT-VALUE is the initial value of the mode's variable.
Optional LIGHTER is displayed in the modeline when the mode is on.
-Optional KEYMAP is the default (defvar) keymap bound to the mode keymap.
- If it is a list, it is passed to `easy-mmode-define-keymap'
- in order to build a valid keymap. It's generally better to use
- a separate MODE-map variable than to use this argument.
-The above three arguments can be skipped if keyword arguments are
-used (see below).
-
-BODY contains code to execute each time the mode is activated or deactivated.
- It is executed after toggling the mode,
- and before running the hook variable `MODE-hook'.
- Before the actual body code, you can write keyword arguments (alternating
- keywords and values). These following keyword arguments are supported (other
- keywords will be passed to `defcustom' if the minor mode is global):
+Optional KEYMAP is the default keymap bound to the mode keymap.
+ If non-nil, it should be a variable name (whose value is a keymap),
+ a keymap, or a list of arguments for `easy-mmode-define-keymap'.
+ If KEYMAP is a keymap or list, this also defines the variable MODE-map.
+
+BODY contains code to execute each time the mode is enabled or disabled.
+ It is executed after toggling the mode, and before running MODE-hook.
+ Before the actual body code, you can write keyword arguments, i.e.
+ alternating keywords and values. These following special keywords
+ are supported (other keywords are passed to `defcustom' if the minor
+ mode is global):
+
:group GROUP Custom group name to use in all generated `defcustom' forms.
Defaults to MODE without the possible trailing \"-mode\".
Don't use this default group name unless you have written a
diff --git a/lisp/emacs-lisp/easymenu.el b/lisp/emacs-lisp/easymenu.el
index 470f0f67779..9992861fc3c 100644
--- a/lisp/emacs-lisp/easymenu.el
+++ b/lisp/emacs-lisp/easymenu.el
@@ -5,6 +5,7 @@
;; Keywords: emulations
;; Author: Richard Stallman <rms@gnu.org>
+;; Package: emacs
;; This file is part of GNU Emacs.
@@ -43,8 +44,6 @@ menus, turn this variable off, otherwise it is probably better to keep it on.")
(if (stringp s) (intern s) s))
;;;###autoload
-(put 'easy-menu-define 'lisp-indent-function 'defun)
-;;;###autoload
(defmacro easy-menu-define (symbol maps doc menu)
"Define a menu bar submenu in maps MAPS, according to MENU.
@@ -150,6 +149,7 @@ unselectable text. A string consisting solely of hyphens is displayed
as a solid horizontal line.
A menu item can be a list with the same format as MENU. This is a submenu."
+ (declare (indent defun))
`(progn
,(if symbol `(defvar ,symbol nil ,doc))
(easy-menu-do-define (quote ,symbol) ,maps ,doc ,menu)))
diff --git a/lisp/emacs-lisp/eieio-base.el b/lisp/emacs-lisp/eieio-base.el
index b573af29ee2..91cb5642fb7 100644
--- a/lisp/emacs-lisp/eieio-base.el
+++ b/lisp/emacs-lisp/eieio-base.el
@@ -6,6 +6,7 @@
;; Author: Eric M. Ludlam <zappo@gnu.org>
;; Version: 0.2
;; Keywords: OO, lisp
+;; Package: eieio
;; This file is part of GNU Emacs.
diff --git a/lisp/emacs-lisp/eieio-comp.el b/lisp/emacs-lisp/eieio-comp.el
index a2b955a280b..0e76f4bb331 100644
--- a/lisp/emacs-lisp/eieio-comp.el
+++ b/lisp/emacs-lisp/eieio-comp.el
@@ -5,7 +5,8 @@
;; Author: Eric M. Ludlam <zappo@gnu.org>
;; Version: 0.2
-;; Keywords: oop, lisp, tools
+;; Keywords: lisp, tools
+;; Package: eieio
;; This file is part of GNU Emacs.
diff --git a/lisp/emacs-lisp/eieio-custom.el b/lisp/emacs-lisp/eieio-custom.el
index 268d60fc196..12ff23b311f 100644
--- a/lisp/emacs-lisp/eieio-custom.el
+++ b/lisp/emacs-lisp/eieio-custom.el
@@ -6,6 +6,7 @@
;; Author: Eric M. Ludlam <zappo@gnu.org>
;; Version: 0.2
;; Keywords: OO, lisp
+;; Package: eieio
;; This file is part of GNU Emacs.
diff --git a/lisp/emacs-lisp/eieio-datadebug.el b/lisp/emacs-lisp/eieio-datadebug.el
index 5dc54f5c35e..b58fbfd3f08 100644
--- a/lisp/emacs-lisp/eieio-datadebug.el
+++ b/lisp/emacs-lisp/eieio-datadebug.el
@@ -4,6 +4,7 @@
;; Author: Eric M. Ludlam <zappo@gnu.org>
;; Keywords: OO, lisp
+;; Package: eieio
;; This file is part of GNU Emacs.
diff --git a/lisp/emacs-lisp/eieio-opt.el b/lisp/emacs-lisp/eieio-opt.el
index 375ce0bc6d6..ca3850562c8 100644
--- a/lisp/emacs-lisp/eieio-opt.el
+++ b/lisp/emacs-lisp/eieio-opt.el
@@ -6,6 +6,7 @@
;; Author: Eric M. Ludlam <zappo@gnu.org>
;; Version: 0.2
;; Keywords: OO, lisp
+;; Package: eieio
;; This file is part of GNU Emacs.
diff --git a/lisp/emacs-lisp/eieio-speedbar.el b/lisp/emacs-lisp/eieio-speedbar.el
index e4c1c50aa8f..e16c3a17438 100644
--- a/lisp/emacs-lisp/eieio-speedbar.el
+++ b/lisp/emacs-lisp/eieio-speedbar.el
@@ -6,6 +6,7 @@
;; Author: Eric M. Ludlam <zappo@gnu.org>
;; Version: 0.2
;; Keywords: OO, tools
+;; Package: eieio
;; This file is part of GNU Emacs.
diff --git a/lisp/emacs-lisp/eieio.el b/lisp/emacs-lisp/eieio.el
index f5e684e1323..34fb5b9c9fc 100644
--- a/lisp/emacs-lisp/eieio.el
+++ b/lisp/emacs-lisp/eieio.el
@@ -1610,6 +1610,7 @@ SPEC-LIST is of a form similar to `let'. For example:
Where each VAR is the local variable given to the associated
SLOT. A slot specified without a variable name is given a
variable name of the same name as the slot."
+ (declare (indent 2))
;; Transform the spec-list into a symbol-macrolet spec-list.
(let ((mappings (mapcar (lambda (entry)
(let ((var (if (listp entry) (car entry) entry))
@@ -1618,8 +1619,6 @@ variable name of the same name as the slot."
spec-list)))
(append (list 'symbol-macrolet mappings)
body)))
-(put 'with-slots 'lisp-indent-function 2)
-
;;; Simple generators, and query functions. None of these would do
;; well embedded into an object.
diff --git a/lisp/emacs-lisp/float-sup.el b/lisp/emacs-lisp/float-sup.el
index 6a05bda82ae..6bdc9073ddf 100644
--- a/lisp/emacs-lisp/float-sup.el
+++ b/lisp/emacs-lisp/float-sup.el
@@ -5,6 +5,7 @@
;; Maintainer: FSF
;; Keywords: internal
+;; Package: emacs
;; This file is part of GNU Emacs.
diff --git a/lisp/emacs-lisp/generic.el b/lisp/emacs-lisp/generic.el
index b6e8427ea1c..51b23c3f402 100644
--- a/lisp/emacs-lisp/generic.el
+++ b/lisp/emacs-lisp/generic.el
@@ -6,6 +6,7 @@
;; Author: Peter Breton <pbreton@cs.umb.edu>
;; Created: Fri Sep 27 1996
;; Keywords: generic, comment, font-lock
+;; Package: emacs
;; This file is part of GNU Emacs.
diff --git a/lisp/emacs-lisp/helper.el b/lisp/emacs-lisp/helper.el
index b7cb8b93c2f..6a597429328 100644
--- a/lisp/emacs-lisp/helper.el
+++ b/lisp/emacs-lisp/helper.el
@@ -6,6 +6,7 @@
;; Author: K. Shane Hartman
;; Maintainer: FSF
;; Keywords: help
+;; Package: emacs
;; This file is part of GNU Emacs.
diff --git a/lisp/emacs-lisp/lisp-mnt.el b/lisp/emacs-lisp/lisp-mnt.el
index 10b7baf294f..7df65acb283 100644
--- a/lisp/emacs-lisp/lisp-mnt.el
+++ b/lisp/emacs-lisp/lisp-mnt.el
@@ -298,6 +298,7 @@ The returned value is a list of strings, one per line."
(defmacro lm-with-file (file &rest body)
"Execute BODY in a buffer containing the contents of FILE.
If FILE is nil, execute BODY in the current buffer."
+ (declare (indent 1) (debug t))
(let ((filesym (make-symbol "file")))
`(let ((,filesym ,file))
(if ,filesym
@@ -311,9 +312,6 @@ If FILE is nil, execute BODY in the current buffer."
(with-syntax-table emacs-lisp-mode-syntax-table
,@body))))))
-(put 'lm-with-file 'lisp-indent-function 1)
-(put 'lm-with-file 'edebug-form-spec t)
-
;; Fixme: Probably this should be amalgamated with copyright.el; also
;; we need a check for ranges in copyright years.
diff --git a/lisp/emacs-lisp/lisp-mode.el b/lisp/emacs-lisp/lisp-mode.el
index 1185f79806f..b4ac0eebf6d 100644
--- a/lisp/emacs-lisp/lisp-mode.el
+++ b/lisp/emacs-lisp/lisp-mode.el
@@ -5,6 +5,7 @@
;; Maintainer: FSF
;; Keywords: lisp, languages
+;; Package: emacs
;; This file is part of GNU Emacs.
@@ -85,7 +86,7 @@
(let ((table (copy-syntax-table emacs-lisp-mode-syntax-table)))
(modify-syntax-entry ?\[ "_ " table)
(modify-syntax-entry ?\] "_ " table)
- (modify-syntax-entry ?# "' 14b" table)
+ (modify-syntax-entry ?# "' 14" table)
(modify-syntax-entry ?| "\" 23bn" table)
table)
"Syntax table used in `lisp-mode'.")
@@ -1217,31 +1218,17 @@ This function also returns nil meaning don't specify the indentation."
(put 'prog2 'lisp-indent-function 2)
(put 'save-excursion 'lisp-indent-function 0)
(put 'save-window-excursion 'lisp-indent-function 0)
-(put 'save-selected-window 'lisp-indent-function 0)
(put 'save-restriction 'lisp-indent-function 0)
(put 'save-match-data 'lisp-indent-function 0)
(put 'save-current-buffer 'lisp-indent-function 0)
-(put 'with-current-buffer 'lisp-indent-function 1)
-(put 'combine-after-change-calls 'lisp-indent-function 0)
-(put 'with-output-to-string 'lisp-indent-function 0)
-(put 'with-temp-file 'lisp-indent-function 1)
-(put 'with-temp-buffer 'lisp-indent-function 0)
-(put 'with-temp-message 'lisp-indent-function 1)
-(put 'with-syntax-table 'lisp-indent-function 1)
(put 'let 'lisp-indent-function 1)
(put 'let* 'lisp-indent-function 1)
(put 'while 'lisp-indent-function 1)
(put 'if 'lisp-indent-function 2)
-(put 'read-if 'lisp-indent-function 2)
(put 'catch 'lisp-indent-function 1)
(put 'condition-case 'lisp-indent-function 2)
(put 'unwind-protect 'lisp-indent-function 1)
(put 'with-output-to-temp-buffer 'lisp-indent-function 1)
-(put 'eval-after-load 'lisp-indent-function 1)
-(put 'dolist 'lisp-indent-function 1)
-(put 'dotimes 'lisp-indent-function 1)
-(put 'when 'lisp-indent-function 1)
-(put 'unless 'lisp-indent-function 1)
(defun indent-sexp (&optional endpos)
"Indent each line of the list starting just after point.
diff --git a/lisp/emacs-lisp/lisp.el b/lisp/emacs-lisp/lisp.el
index 4ef6dab8968..e799dcd77c1 100644
--- a/lisp/emacs-lisp/lisp.el
+++ b/lisp/emacs-lisp/lisp.el
@@ -5,6 +5,7 @@
;; Maintainer: FSF
;; Keywords: lisp, languages
+;; Package: emacs
;; This file is part of GNU Emacs.
@@ -142,7 +143,13 @@ This command assumes point is not in a string or comment."
(or arg (setq arg 1))
(let ((inc (if (> arg 0) 1 -1)))
(while (/= arg 0)
- (goto-char (or (scan-lists (point) inc 1) (buffer-end arg)))
+ (if forward-sexp-function
+ (condition-case err
+ (while (let ((pos (point)))
+ (forward-sexp inc)
+ (/= (point) pos)))
+ (scan-error (goto-char (nth 2 err))))
+ (goto-char (or (scan-lists (point) inc 1) (buffer-end arg))))
(setq arg (- arg inc)))))
(defun kill-sexp (&optional arg)
diff --git a/lisp/emacs-lisp/macroexp.el b/lisp/emacs-lisp/macroexp.el
index 876b9a468ac..6dfd47b4ad1 100644
--- a/lisp/emacs-lisp/macroexp.el
+++ b/lisp/emacs-lisp/macroexp.el
@@ -52,6 +52,7 @@ possible (for instance, when BODY just returns VAR unchanged, the
result will be eq to LIST).
\(fn (VAR LIST) BODY...)"
+ (declare (indent 1))
(let ((var (car var+list))
(list (cadr var+list))
(shared (make-symbol "shared"))
@@ -72,7 +73,6 @@ result will be eq to LIST).
(push ,new-el ,unshared))
(setq ,tail (cdr ,tail)))
(nconc (nreverse ,unshared) ,shared))))
-(put 'macroexp-accumulate 'lisp-indent-function 1)
(defun macroexpand-all-forms (forms &optional skip)
"Return FORMS with macros expanded. FORMS is a list of forms.
@@ -107,80 +107,69 @@ Assumes the caller has bound `macroexpand-all-environment'."
macroexpand-all-environment)
;; Normal form; get its expansion, and then expand arguments.
(setq form (macroexpand form macroexpand-all-environment))
- (if (consp form)
- (let ((fun (car form)))
- (cond
- ((eq fun 'cond)
- (maybe-cons fun (macroexpand-all-clauses (cdr form)) form))
- ((eq fun 'condition-case)
- (maybe-cons
- fun
- (maybe-cons (cadr form)
- (maybe-cons (macroexpand-all-1 (nth 2 form))
- (macroexpand-all-clauses (nthcdr 3 form) 1)
- (cddr form))
- (cdr form))
- form))
- ((eq fun 'defmacro)
- (push (cons (cadr form) (cons 'lambda (cddr form)))
- macroexpand-all-environment)
- (macroexpand-all-forms form 3))
- ((eq fun 'defun)
- (macroexpand-all-forms form 3))
- ((memq fun '(defvar defconst))
- (macroexpand-all-forms form 2))
- ((eq fun 'function)
- (if (and (consp (cadr form)) (eq (car (cadr form)) 'lambda))
- (maybe-cons fun
- (maybe-cons (macroexpand-all-forms (cadr form) 2)
- nil
- (cdr form))
- form)
- form))
- ((memq fun '(let let*))
- (maybe-cons fun
- (maybe-cons (macroexpand-all-clauses (cadr form) 1)
- (macroexpand-all-forms (cddr form))
- (cdr form))
- form))
- ((eq fun 'quote)
- form)
- ((and (consp fun) (eq (car fun) 'lambda))
- ;; Embedded lambda in function position.
- (maybe-cons (macroexpand-all-forms fun 2)
- (macroexpand-all-forms (cdr form))
- form))
- ;; The following few cases are for normal function calls that
- ;; are known to funcall one of their arguments. The byte
- ;; compiler has traditionally handled these functions specially
- ;; by treating a lambda expression quoted by `quote' as if it
- ;; were quoted by `function'. We make the same transformation
- ;; here, so that any code that cares about the difference will
- ;; see the same transformation.
- ;; First arg is a function:
- ((and (memq fun '(apply mapcar mapatoms mapconcat mapc))
- (consp (cadr form))
- (eq (car (cadr form)) 'quote))
- ;; We don't use `maybe-cons' since there's clearly a change.
- (cons fun
- (cons (macroexpand-all-1 (cons 'function (cdr (cadr form))))
- (macroexpand-all-forms (cddr form)))))
- ;; Second arg is a function:
- ((and (eq fun 'sort)
- (consp (nth 2 form))
- (eq (car (nth 2 form)) 'quote))
- ;; We don't use `maybe-cons' since there's clearly a change.
- (cons fun
- (cons (macroexpand-all-1 (cadr form))
- (cons (macroexpand-all-1
- (cons 'function (cdr (nth 2 form))))
- (macroexpand-all-forms (nthcdr 3 form))))))
- (t
- ;; For everything else, we just expand each argument (for
- ;; setq/setq-default this works alright because the variable names
- ;; are symbols).
- (macroexpand-all-forms form 1))))
- form)))
+ (pcase form
+ (`(cond . ,clauses)
+ (maybe-cons 'cond (macroexpand-all-clauses clauses) form))
+ (`(condition-case . ,(or `(,err ,body . ,handlers) dontcare))
+ (maybe-cons
+ 'condition-case
+ (maybe-cons err
+ (maybe-cons (macroexpand-all-1 body)
+ (macroexpand-all-clauses handlers 1)
+ (cddr form))
+ (cdr form))
+ form))
+ (`(defmacro ,name . ,args-and-body)
+ (push (cons name (cons 'lambda args-and-body))
+ macroexpand-all-environment)
+ (macroexpand-all-forms form 3))
+ (`(defun . ,_) (macroexpand-all-forms form 3))
+ (`(,(or `defvar `defconst) . ,_) (macroexpand-all-forms form 2))
+ (`(function ,(and f `(lambda . ,_)))
+ (maybe-cons 'function
+ (maybe-cons (macroexpand-all-forms f 2)
+ nil
+ (cdr form))
+ form))
+ (`(,(or `function `quote) . ,_) form)
+ (`(,(and fun (or `let `let*)) . ,(or `(,bindings . ,body) dontcare))
+ (maybe-cons fun
+ (maybe-cons (macroexpand-all-clauses bindings 1)
+ (macroexpand-all-forms body)
+ (cdr form))
+ form))
+ (`(,(and fun `(lambda . ,_)) . ,args)
+ ;; Embedded lambda in function position.
+ (maybe-cons (macroexpand-all-forms fun 2)
+ (macroexpand-all-forms args)
+ form))
+ ;; The following few cases are for normal function calls that
+ ;; are known to funcall one of their arguments. The byte
+ ;; compiler has traditionally handled these functions specially
+ ;; by treating a lambda expression quoted by `quote' as if it
+ ;; were quoted by `function'. We make the same transformation
+ ;; here, so that any code that cares about the difference will
+ ;; see the same transformation.
+ ;; First arg is a function:
+ (`(,(and fun (or `apply `mapcar `mapatoms `mapconcat `mapc)) ',f . ,args)
+ ;; We don't use `maybe-cons' since there's clearly a change.
+ (cons fun
+ (cons (macroexpand-all-1 (list 'function f))
+ (macroexpand-all-forms args))))
+ ;; Second arg is a function:
+ (`(,(and fun (or `sort)) ,arg1 ',f . ,args)
+ ;; We don't use `maybe-cons' since there's clearly a change.
+ (cons fun
+ (cons (macroexpand-all-1 arg1)
+ (cons (macroexpand-all-1
+ (list 'function f))
+ (macroexpand-all-forms args)))))
+ (`(,_ . ,_)
+ ;; For every other list, we just expand each argument (for
+ ;; setq/setq-default this works alright because the variable names
+ ;; are symbols).
+ (macroexpand-all-forms form 1))
+ (t form))))
;;;###autoload
(defun macroexpand-all (form &optional environment)
diff --git a/lisp/emacs-lisp/package-x.el b/lisp/emacs-lisp/package-x.el
index b93950049e0..38c4d5bbe35 100644
--- a/lisp/emacs-lisp/package-x.el
+++ b/lisp/emacs-lisp/package-x.el
@@ -6,6 +6,7 @@
;; Created: 10 Mar 2007
;; Version: 0.9
;; Keywords: tools
+;; Package: package
;; This file is part of GNU Emacs.
diff --git a/lisp/emacs-lisp/package.el b/lisp/emacs-lisp/package.el
index 2e8c7dc7d4f..54c6a09dd9d 100644
--- a/lisp/emacs-lisp/package.el
+++ b/lisp/emacs-lisp/package.el
@@ -216,6 +216,7 @@ If VERSION is nil, the package is not loaded (it is \"disabled\")."
(declare-function lm-header "lisp-mnt" (header))
(declare-function lm-commentary "lisp-mnt" (&optional file))
(declare-function dired-delete-file "dired" (file &optional recursive trash))
+(defvar url-http-end-of-headers)
(defcustom package-archives '(("gnu" . "http://elpa.gnu.org/packages/"))
"An alist of archives from which to fetch.
@@ -259,8 +260,9 @@ packages in `package-directory-list'."
;; Defaults are subdirs named "elpa" in the site-lisp dirs.
(let (result)
(dolist (f load-path)
- (if (equal (file-name-nondirectory f) "site-lisp")
- (push (expand-file-name "elpa" f) result)))
+ (and (stringp f)
+ (equal (file-name-nondirectory f) "site-lisp")
+ (push (expand-file-name "elpa" f) result)))
(nreverse result))
"List of additional directories containing Emacs Lisp packages.
Each directory name should be absolute.
@@ -272,46 +274,35 @@ contrast, `package-user-dir' contains packages for personal use."
:group 'package
:version "24.1")
-(defconst package--builtins-base
- ;; We use package-version split here to make sure to pick up the
- ;; minor version.
- `((emacs . [,(version-to-list emacs-version) nil
- "GNU Emacs"])
- (package . [,(version-to-list package-el-version)
- nil "Simple package system for GNU Emacs"]))
- "Packages which are always built-in.")
-
-(defvar package--builtins
- (delq nil
- (append
- package--builtins-base
- (if (>= emacs-major-version 22)
- ;; FIXME: emacs 22 includes tramp, rcirc, maybe
- ;; other things...
- '((erc . [(5 2) nil "Internet Relay Chat client"])
- ;; The external URL is version 1.15, so make sure the
- ;; built-in one looks newer.
- (url . [(1 16) nil "URL handling libary"])))
- (if (>= emacs-major-version 23)
- '(;; Strangely, nxml-version is missing in Emacs 23.
- ;; We pick the merge date as the version.
- (nxml . [(20071123) nil "Major mode for XML documents"])
- (bubbles . [(0 5) nil "A puzzle game"])))))
- "Alist of all built-in packages.
-Maps the package name to a vector [VERSION REQS DOCSTRING].")
+;; The value is precomputed in finder-inf.el, but don't load that
+;; until it's needed (i.e. when `package-intialize' is called).
+(defvar package--builtins nil
+ "Alist of built-in packages.
+Each element has the form (PKG . DESC), where PKG is a package
+name (a symbol) and DESC is a vector that describes the package.
+
+The vector DESC has the form [VERSION REQS DOCSTRING].
+ VERSION is a version list.
+ REQS is a list of packages (symbols) required by the package.
+ DOCSTRING is a brief description of the package.")
(put 'package--builtins 'risky-local-variable t)
-(defvar package-alist package--builtins
+(defvar package-alist nil
"Alist of all packages available for activation.
-This maps the package name to a vector [VERSION REQS DOCSTRING].
+Each element has the form (PKG . DESC), where PKG is a package
+name (a symbol) and DESC is a vector that describes the package.
-The value is generated by `package-load-descriptor', usually
-called via `package-initialize'. For user customizations of
-which packages to load/activate, see `package-load-list'.")
+The vector DESC has the form [VERSION REQS DOCSTRING].
+ VERSION is a version list.
+ REQS is a list of packages (symbols) required by the package.
+ DOCSTRING is a brief description of the package.
+
+This variable is set automatically by `package-load-descriptor',
+called via `package-initialize'. To change which packages are
+loaded and/or activated, customize `package-load-list'.")
(put 'package-archive-contents 'risky-local-variable t)
-(defvar package-activated-list
- (mapcar #'car package-alist)
+(defvar package-activated-list nil
"List of the names of currently activated packages.")
(put 'package-activated-list 'risky-local-variable t)
@@ -416,16 +407,15 @@ updates `package-alist' and `package-obsolete-alist'."
(error "Internal error: could not find directory for %s-%s"
name version-str))
;; Add info node.
- (if (file-exists-p (expand-file-name "dir" pkg-dir))
- (progn
- ;; FIXME: not the friendliest, but simple.
- (require 'info)
- (info-initialize)
- (setq Info-directory-list (cons pkg-dir Info-directory-list))))
+ (when (file-exists-p (expand-file-name "dir" pkg-dir))
+ ;; FIXME: not the friendliest, but simple.
+ (require 'info)
+ (info-initialize)
+ (push pkg-dir Info-directory-list))
;; Add to load path, add autoloads, and activate the package.
- (setq load-path (cons pkg-dir load-path))
+ (push pkg-dir load-path)
(load (expand-file-name (concat name "-autoloads") pkg-dir) nil t)
- (setq package-activated-list (cons package package-activated-list))
+ (push package package-activated-list)
;; Don't return nil.
t))
@@ -476,22 +466,22 @@ Return nil if the package could not be activated."
(setcdr elt (cons (cons (package-desc-vers pkg-vec) pkg-vec)
(cdr elt))))
;; Make a new association.
- (setq package-obsolete-alist
- (cons (cons package (list (cons (package-desc-vers pkg-vec)
- pkg-vec)))
- package-obsolete-alist)))))
+ (push (cons package (list (cons (package-desc-vers pkg-vec)
+ pkg-vec)))
+ package-obsolete-alist))))
-;; (define-package "emacs" "21.4.1" "GNU Emacs core package.")
-;; (define-package "erc" "5.1" "ERC - irc client" '((emacs "21.0")))
(defun define-package (name-str version-string
- &optional docstring requirements)
+ &optional docstring requirements
+ &rest extra-properties)
"Define a new package.
NAME is the name of the package, a string.
VERSION-STRING is the version of the package, a dotted sequence
of integers.
DOCSTRING is the optional description.
REQUIREMENTS is a list of requirements on other packages.
-Each requirement is of the form (OTHER-PACKAGE \"VERSION\")."
+Each requirement is of the form (OTHER-PACKAGE \"VERSION\").
+
+EXTRA-PROPERTIES is currently unused."
(let* ((name (intern name-str))
(pkg-desc (assq name package-alist))
(new-version (version-to-list version-string))
@@ -514,7 +504,7 @@ Each requirement is of the form (OTHER-PACKAGE \"VERSION\")."
(setq package-alist (delq pkg-desc package-alist))
(package-mark-obsolete (car pkg-desc) (cdr pkg-desc)))
;; Add package to the alist.
- (setq package-alist (cons new-pkg-desc package-alist)))
+ (push new-pkg-desc package-alist))
;; You can have two packages with the same version, for instance
;; one in the system package directory and one in your private
;; directory. We just let the first one win.
@@ -672,7 +662,19 @@ It will move point to somewhere in the headers."
(version-list-<= min-version
(package-desc-vers (cdr pkg-desc))))))
-(defun package-compute-transaction (result requirements)
+(defun package-compute-transaction (package-list requirements)
+ "Return a list of packages to be installed, including PACKAGE-LIST.
+PACKAGE-LIST should be a list of package names (symbols).
+
+REQUIREMENTS should be a list of additional requirements; each
+element in this list should have the form (PACKAGE VERSION),
+where PACKAGE is a package name and VERSION is the required
+version of that package (as a list).
+
+This function recursively computes the requirements of the
+packages in REQUIREMENTS, and returns a list of all the packages
+that must be installed. Packages that are already installed are
+not included in this list."
(dolist (elt requirements)
(let* ((next-pkg (car elt))
(next-version (cadr elt)))
@@ -703,25 +705,25 @@ but version %s required"
(symbol-name next-pkg) (package-version-join next-version)
(package-version-join (package-desc-vers (cdr pkg-desc)))))
;; Only add to the transaction if we don't already have it.
- (unless (memq next-pkg result)
- (setq result (cons next-pkg result)))
- (setq result
- (package-compute-transaction result
+ (unless (memq next-pkg package-list)
+ (push next-pkg package-list))
+ (setq package-list
+ (package-compute-transaction package-list
(package-desc-reqs
(cdr pkg-desc))))))))
- result)
+ package-list)
(defun package-read-from-string (str)
"Read a Lisp expression from STR.
Signal an error if the entire string was not used."
(let* ((read-data (read-from-string str))
- (more-left
- (condition-case nil
- ;; The call to `ignore' suppresses a compiler warning.
- (progn (ignore (read-from-string
- (substring str (cdr read-data))))
- t)
- (end-of-file nil))))
+ (more-left
+ (condition-case nil
+ ;; The call to `ignore' suppresses a compiler warning.
+ (progn (ignore (read-from-string
+ (substring str (cdr read-data))))
+ t)
+ (end-of-file nil))))
(if more-left
(error "Can't read whole string")
(car read-data))))
@@ -731,48 +733,33 @@ Signal an error if the entire string was not used."
Will return the data from the file, or nil if the file does not exist.
Will throw an error if the archive version is too new."
(let ((filename (expand-file-name file package-user-dir)))
- (if (file-exists-p filename)
- (with-temp-buffer
- (insert-file-contents-literally filename)
- (let ((contents (package-read-from-string
- (buffer-substring-no-properties (point-min)
- (point-max)))))
- (if (> (car contents) package-archive-version)
- (error "Package archive version %d is greater than %d - upgrade package.el"
- (car contents) package-archive-version))
- (cdr contents))))))
+ (when (file-exists-p filename)
+ (with-temp-buffer
+ (insert-file-contents-literally filename)
+ (let ((contents (read (current-buffer))))
+ (if (> (car contents) package-archive-version)
+ (error "Package archive version %d is higher than %d"
+ (car contents) package-archive-version))
+ (cdr contents))))))
(defun package-read-all-archive-contents ()
- "Re-read `archive-contents' and `builtin-packages', if they exist.
-Set `package-archive-contents' and `package--builtins' if successful.
-Throw an error if the archive version is too new."
+ "Re-read `archive-contents', if it exists.
+If successful, set `package-archive-contents'."
(dolist (archive package-archives)
- (package-read-archive-contents (car archive)))
- (let ((builtins (package--read-archive-file "builtin-packages")))
- (if builtins
- ;; Version 1 of 'builtin-packages' is a list where the car is
- ;; a split emacs version and the cdr is an alist suitable for
- ;; package--builtins.
- (let ((our-version (version-to-list emacs-version))
- (result package--builtins-base))
- (setq package--builtins
- (dolist (elt builtins result)
- (if (version-list-<= (car elt) our-version)
- (setq result (append (cdr elt) result)))))))))
+ (package-read-archive-contents (car archive))))
(defun package-read-archive-contents (archive)
- "Re-read `archive-contents' and `builtin-packages' for ARCHIVE.
-If successful, set `package-archive-contents' and `package--builtins'.
+ "Re-read archive contents for ARCHIVE.
+If successful, set the variable `package-archive-contents'.
If the archive version is too new, signal an error."
- (let ((archive-contents (package--read-archive-file
- (concat "archives/" archive
- "/archive-contents"))))
- (if archive-contents
- ;; Version 1 of 'archive-contents' is identical to our
- ;; internal representation.
- ;; TODO: merge archive lists
- (dolist (package archive-contents)
- (package--add-to-archive-contents package archive)))))
+ ;; Version 1 of 'archive-contents' is identical to our internal
+ ;; representation.
+ (let* ((dir (concat "archives/" archive))
+ (contents-file (concat dir "/archive-contents"))
+ contents)
+ (when (setq contents (package--read-archive-file contents-file))
+ (dolist (package contents)
+ (package--add-to-archive-contents package archive)))))
(defun package--add-to-archive-contents (package archive)
"Add the PACKAGE from the given ARCHIVE if necessary.
@@ -786,9 +773,13 @@ Also, add the originating archive to the end of the package vector."
(version-list-< (aref existing-package 0) version))
(add-to-list 'package-archive-contents entry))))
-(defun package-download-transaction (transaction)
- "Download and install all the packages in the given transaction."
- (dolist (elt transaction)
+(defun package-download-transaction (package-list)
+ "Download and install all the packages in PACKAGE-LIST.
+PACKAGE-LIST should be a list of package names (symbols).
+This function assumes that all package requirements in
+PACKAGE-LIST are satisfied, i.e. that PACKAGE-LIST is computed
+using `package-compute-transaction'."
+ (dolist (elt package-list)
(let* ((desc (cdr (assq elt package-archive-contents)))
;; As an exception, if package is "held" in
;; `package-load-list', download the held version.
@@ -839,61 +830,60 @@ Otherwise return nil."
v-str))))
(defun package-buffer-info ()
- "Return a vector of information about the package in the current buffer.
-The vector looks like [FILENAME REQUIRES DESCRIPTION VERSION COMMENTARY]
-FILENAME is the file name, a string. It does not have the \".el\" extension.
+ "Return a vector describing the package in the current buffer.
+The vector has the form
+
+ [FILENAME REQUIRES DESCRIPTION VERSION COMMENTARY]
+
+FILENAME is the file name, a string, sans the \".el\" extension.
REQUIRES is a requires list, or nil.
-DESCRIPTION is the package description (a string).
+DESCRIPTION is the package description, a string.
VERSION is the version, a string.
COMMENTARY is the commentary section, a string, or nil if none.
-Throws an exception if the buffer does not contain a conforming package.
-If there is a package, narrows the buffer to the file's boundaries.
-May narrow buffer or move point even on failure."
+
+If the buffer does not contain a conforming package, signal an
+error. If there is a package, narrow the buffer to the file's
+boundaries."
(goto-char (point-min))
- (if (re-search-forward "^;;; \\([^ ]*\\)\\.el --- \\(.*\\)$" nil t)
- (let ((file-name (match-string 1))
- (desc (match-string 2))
- (start (progn (beginning-of-line) (point))))
- (if (search-forward (concat ";;; " file-name ".el ends here"))
- (progn
- ;; Try to include a trailing newline.
- (forward-line)
- (narrow-to-region start (point))
- (require 'lisp-mnt)
- ;; Use some headers we've invented to drive the process.
- (let* ((requires-str (lm-header "package-requires"))
- (requires (if requires-str
- (package-read-from-string requires-str)))
- ;; Prefer Package-Version, because if it is
- ;; defined the package author probably wants us
- ;; to use it. Otherwise try Version.
- (pkg-version
- (or (package-strip-rcs-id (lm-header "package-version"))
- (package-strip-rcs-id (lm-header "version"))))
- (commentary (lm-commentary)))
- (unless pkg-version
- (error
- "Package does not define a usable \"Version\" or \"Package-Version\" header"))
- ;; Turn string version numbers into list form.
- (setq requires
- (mapcar
- (lambda (elt)
- (list (car elt)
- (version-to-list (car (cdr elt)))))
- requires))
- (set-text-properties 0 (length file-name) nil file-name)
- (set-text-properties 0 (length pkg-version) nil pkg-version)
- (set-text-properties 0 (length desc) nil desc)
- (vector file-name requires desc pkg-version commentary)))
- (error "Package missing a terminating comment")))
- (error "No starting comment for package")))
+ (unless (re-search-forward "^;;; \\([^ ]*\\)\\.el --- \\(.*\\)$" nil t)
+ (error "Packages lacks a file header"))
+ (let ((file-name (match-string-no-properties 1))
+ (desc (match-string-no-properties 2))
+ (start (line-beginning-position)))
+ (unless (search-forward (concat ";;; " file-name ".el ends here"))
+ (error "Package lacks a terminating comment"))
+ ;; Try to include a trailing newline.
+ (forward-line)
+ (narrow-to-region start (point))
+ (require 'lisp-mnt)
+ ;; Use some headers we've invented to drive the process.
+ (let* ((requires-str (lm-header "package-requires"))
+ (requires (if requires-str
+ (package-read-from-string requires-str)))
+ ;; Prefer Package-Version; if defined, the package author
+ ;; probably wants us to use it. Otherwise try Version.
+ (pkg-version
+ (or (package-strip-rcs-id (lm-header "package-version"))
+ (package-strip-rcs-id (lm-header "version"))))
+ (commentary (lm-commentary)))
+ (unless pkg-version
+ (error
+ "Package lacks a \"Version\" or \"Package-Version\" header"))
+ ;; Turn string version numbers into list form.
+ (setq requires
+ (mapcar
+ (lambda (elt)
+ (list (car elt)
+ (version-to-list (car (cdr elt)))))
+ requires))
+ (vector file-name requires desc pkg-version commentary))))
(defun package-tar-file-info (file)
"Find package information for a tar file.
FILE is the name of the tar file to examine.
The return result is a vector like `package-buffer-info'."
(unless (string-match "^\\(.+\\)-\\([0-9.]+\\)\\.tar$" file)
- (error "`%s' doesn't have a package-ish name" file))
+ (error "Invalid package name `%s'" file))
(let* ((pkg-name (file-name-nondirectory (match-string-no-properties 1 file)))
(pkg-version (match-string-no-properties 2 file))
;; Extract the package descriptor.
@@ -904,20 +894,19 @@ The return result is a vector like `package-buffer-info'."
pkg-name "-pkg.el")))
(pkg-def-parsed (package-read-from-string pkg-def-contents)))
(unless (eq (car pkg-def-parsed) 'define-package)
- (error "%s-pkg.el doesn't contain `define-package' sexp" pkg-name))
- (let ((name-str (nth 1 pkg-def-parsed))
+ (error "No `define-package' sexp is present in `%s-pkg.el'" pkg-name))
+ (let ((name-str (nth 1 pkg-def-parsed))
(version-string (nth 2 pkg-def-parsed))
- (docstring (nth 3 pkg-def-parsed))
- (requires (nth 4 pkg-def-parsed))
-
+ (docstring (nth 3 pkg-def-parsed))
+ (requires (nth 4 pkg-def-parsed))
(readme (shell-command-to-string
;; Requires GNU tar.
(concat "tar -xOf " file " "
pkg-name "-" pkg-version "/README"))))
(unless (equal pkg-version version-string)
- (error "Inconsistent versions!"))
+ (error "Package has inconsistent versions"))
(unless (equal pkg-name name-str)
- (error "Inconsistent names!"))
+ (error "Package has inconsistent names"))
;; Kind of a hack.
(if (string-match ": Not found in archive" readme)
(setq readme nil))
@@ -925,18 +914,27 @@ The return result is a vector like `package-buffer-info'."
(if (eq (car requires) 'quote)
(setq requires (car (cdr requires))))
(setq requires
- (mapcar
- (lambda (elt)
- (list (car elt)
- (version-to-list (car (cdr elt)))))
- requires))
+ (mapcar (lambda (elt)
+ (list (car elt)
+ (version-to-list (cadr elt))))
+ requires))
(vector pkg-name requires docstring version-string readme))))
-(defun package-install-buffer-internal (pkg-info type)
+;;;###autoload
+(defun package-install-from-buffer (pkg-info type)
+ "Install a package from the current buffer.
+When called interactively, the current buffer is assumed to be a
+single .el file that follows the packaging guidelines; see info
+node `(elisp)Packaging'.
+
+When called from Lisp, PKG-INFO is a vector describing the
+information, of the type returned by `package-buffer-info'; and
+TYPE is the package type (either `single' or `tar')."
+ (interactive (list (package-buffer-info) 'single))
(save-excursion
(save-restriction
(let* ((file-name (aref pkg-info 0))
- (requires (aref pkg-info 1))
+ (requires (aref pkg-info 1))
(desc (if (string= (aref pkg-info 2) "")
"No description available."
(aref pkg-info 2)))
@@ -956,15 +954,6 @@ The return result is a vector like `package-buffer-info'."
(package-initialize)))))
;;;###autoload
-(defun package-install-from-buffer ()
- "Install a package from the current buffer.
-The package is assumed to be a single .el file which
-follows the elisp comment guidelines; see
-info node `(elisp)Library Headers'."
- (interactive)
- (package-install-buffer-internal (package-buffer-info) 'single))
-
-;;;###autoload
(defun package-install-file (file)
"Install a package from a file.
The file can either be a tar file or an Emacs Lisp file."
@@ -972,9 +961,10 @@ The file can either be a tar file or an Emacs Lisp file."
(with-temp-buffer
(insert-file-contents-literally file)
(cond
- ((string-match "\\.el$" file) (package-install-from-buffer))
+ ((string-match "\\.el$" file)
+ (package-install-from-buffer (package-buffer-info) 'single))
((string-match "\\.tar$" file)
- (package-install-buffer-internal (package-tar-file-info file) 'tar))
+ (package-install-from-buffer (package-tar-file-info file) 'tar))
(t (error "Unrecognized extension `%s'" (file-name-extension file))))))
(defun package-delete (name version)
@@ -1001,22 +991,27 @@ The file can either be a tar file or an Emacs Lisp file."
(re-search-forward "^$" nil 'move)
(forward-char)
(delete-region (point-min) (point))
- (make-directory dir t)
- (setq buffer-file-name (expand-file-name file dir))
- (let ((version-control 'never))
- (save-buffer)))
+ ;; Read the retrieved buffer to make sure it is valid (e.g. it
+ ;; may fetch a URL redirect page).
+ (when (listp (read buffer))
+ (make-directory dir t)
+ (setq buffer-file-name (expand-file-name file dir))
+ (let ((version-control 'never))
+ (save-buffer))))
(kill-buffer buffer)))
(defun package-refresh-contents ()
"Download the ELPA archive description if needed.
-Invoking this will ensure that Emacs knows about the latest versions
-of all packages. This will let Emacs make them available for
-download."
+This informs Emacs about the latest versions of all packages, and
+makes them available for download."
(interactive)
(unless (file-exists-p package-user-dir)
(make-directory package-user-dir t))
(dolist (archive package-archives)
- (package--download-one-archive archive "archive-contents"))
+ (condition-case nil
+ (package--download-one-archive archive "archive-contents")
+ (error (message "Failed to download `%s' archive."
+ (car archive)))))
(package-read-all-archive-contents))
;;;###autoload
@@ -1024,6 +1019,9 @@ download."
"Load Emacs Lisp packages, and activate them.
The variable `package-load-list' controls which packages to load."
(interactive)
+ (require 'finder-inf nil t)
+ (setq package-alist package--builtins)
+ (setq package-activated-list (mapcar #'car package-alist))
(setq package-obsolete-alist nil)
(package-load-all-descriptors)
(package-read-all-archive-contents)
@@ -1052,9 +1050,7 @@ The variable `package-load-list' controls which packages to load."
guess)
"Describe package: ")
packages nil t nil nil guess))
- (list (if (equal val "")
- guess
- (intern val)))))
+ (list (if (equal val "") guess (intern val)))))
(if (or (null package) (null (symbolp package)))
(message "You did not specify a package")
(help-setup-xref (list #'describe-package package)
@@ -1064,38 +1060,65 @@ The variable `package-load-list' controls which packages to load."
(describe-package-1 package)))))
(defun describe-package-1 (package)
- (let ((desc (cdr (assq package package-alist)))
- reqs version installable)
+ (require 'lisp-mnt)
+ (let ((package-name (symbol-name package))
+ (built-in (assq package package--builtins))
+ desc pkg-dir reqs version installable)
(prin1 package)
(princ " is ")
- (cond
- (desc
- ;; This package is loaded (i.e. in `package-alist').
- (let (pkg-dir)
- (setq version (package-version-join (package-desc-vers desc)))
- (if (assq package package--builtins)
- (princ "a built-in package.\n\n")
- (setq pkg-dir (package--dir (symbol-name package) version))
- (if pkg-dir
- (progn
- (insert "a package installed in `")
- (help-insert-xref-button (file-name-as-directory pkg-dir)
- 'help-package-def pkg-dir)
- (insert "'.\n\n"))
- ;; This normally does not happen.
- (insert "a deleted package.\n\n")
- (setq version nil)))))
- (t
- ;; An uninstalled package.
- (setq desc (cdr (assq package package-archive-contents))
+ (if (setq desc (cdr (assq package package-alist)))
+ ;; This package is loaded (i.e. in `package-alist').
+ (progn
+ (setq version (package-version-join (package-desc-vers desc)))
+ (cond (built-in
+ (princ "a built-in package.\n\n"))
+ ((setq pkg-dir (package--dir package-name version))
+ (insert "an installed package.\n\n"))
+ (t ;; This normally does not happen.
+ (insert "a deleted package.\n\n")
+ (setq version nil))))
+ ;; This package is not installed.
+ (setq desc (cdr (assq package package-archive-contents))
version (package-version-join (package-desc-vers desc))
installable t)
- (insert "an installable package.\n\n")))
- (if version
- (insert " Version: " version "\n"))
+ (insert "an uninstalled package.\n\n"))
+
+ (insert " " (propertize "Status" 'font-lock-face 'bold) ": ")
+ (cond (pkg-dir
+ (insert (propertize "Installed"
+ 'font-lock-face 'font-lock-comment-face))
+ (insert " in `")
+ ;; Todo: Add button for uninstalling.
+ (help-insert-xref-button (file-name-as-directory pkg-dir)
+ 'help-package-def pkg-dir)
+ (insert "'."))
+ (installable
+ (insert "Available -- ")
+ (let ((button-text (if (display-graphic-p)
+ "Install"
+ "[Install]"))
+ (button-face (if (display-graphic-p)
+ '(:box (:line-width 2 :color "dark grey")
+ :background "light grey"
+ :foreground "black")
+ 'link)))
+ (insert-text-button button-text
+ 'face button-face
+ 'follow-link t
+ 'package-symbol package
+ 'action 'package-install-button-action)))
+ (built-in
+ (insert (propertize "Built-in"
+ 'font-lock-face 'font-lock-builtin-face) "."))
+ (t (insert "Deleted.")))
+ (insert "\n")
+ (and version
+ (> (length version) 0)
+ (insert " "
+ (propertize "Version" 'font-lock-face 'bold) ": " version "\n"))
(setq reqs (package-desc-reqs desc))
(when reqs
- (insert " Requires: ")
+ (insert " " (propertize "Requires" 'font-lock-face 'bold) ": ")
(let ((first t)
name vers text)
(dolist (req reqs)
@@ -1110,28 +1133,53 @@ The variable `package-load-list' controls which packages to load."
(t (insert ", ")))
(help-insert-xref-button text 'help-package name))
(insert "\n")))
- (insert " Description: " (package-desc-doc desc) "\n")
- ;; Todo: button for uninstalling a package.
- (when installable
- (let ((button-text (if (display-graphic-p)
- "Install"
- "[Install]"))
- (button-face (if (display-graphic-p)
- '(:box (:line-width 2 :color "dark grey")
- :background "light grey"
- :foreground "black")
- 'link)))
- (insert "\n")
- (insert-text-button button-text
- 'face button-face
- 'follow-link t
- 'package-symbol package
- 'action (lambda (button)
- (package-install
- (button-get button 'package-symbol))
- (revert-buffer nil t)
- (goto-char (point-min))))
- (insert "\n")))))
+ (insert " " (propertize "Summary" 'font-lock-face 'bold)
+ ": " (package-desc-doc desc) "\n\n")
+
+ (if (assq package package--builtins)
+ ;; For built-in packages, insert the commentary.
+ (let ((fn (locate-file (concat package-name ".el") load-path
+ load-file-rep-suffixes))
+ (opoint (point)))
+ (insert (or (lm-commentary fn) ""))
+ (save-excursion
+ (goto-char opoint)
+ (when (re-search-forward "^;;; Commentary:\n" nil t)
+ (replace-match ""))
+ (while (re-search-forward "^\\(;+ ?\\)" nil t)
+ (replace-match ""))))
+ (let ((readme (expand-file-name (concat package-name "-readme.txt")
+ package-user-dir)))
+ ;; For elpa packages, try downloading the commentary. If that
+ ;; fails, try an existing readme file in `package-user-dir'.
+ (cond ((let ((buffer (ignore-errors
+ (url-retrieve-synchronously
+ (concat (package-archive-url package)
+ package-name "-readme.txt"))))
+ response)
+ (when buffer
+ (with-current-buffer buffer
+ (setq response (url-http-parse-response))
+ (if (or (< response 200) (>= response 300))
+ (setq response nil)
+ (setq buffer-file-name
+ (expand-file-name readme package-user-dir))
+ (delete-region (point-min) (1+ url-http-end-of-headers))
+ (save-buffer)))
+ (when response
+ (insert-buffer-substring buffer)
+ (kill-buffer buffer)
+ t))))
+ ((file-readable-p readme)
+ (insert-file-contents readme)
+ (goto-char (point-max))))))))
+
+(defun package-install-button-action (button)
+ (let ((package (button-get button 'package-symbol)))
+ (when (y-or-n-p (format "Install package `%s'? " package))
+ (package-install package)
+ (revert-buffer nil t)
+ (goto-char (point-min)))))
;;;; Package menu mode.
@@ -1148,12 +1196,14 @@ The variable `package-load-list' controls which packages to load."
(define-key map "\177" 'package-menu-backup-unmark)
(define-key map "d" 'package-menu-mark-delete)
(define-key map "i" 'package-menu-mark-install)
- (define-key map "g" 'package-menu-revert)
+ (define-key map "g" 'revert-buffer)
(define-key map "r" 'package-menu-refresh)
(define-key map "~" 'package-menu-mark-obsolete-for-deletion)
(define-key map "x" 'package-menu-execute)
(define-key map "h" 'package-menu-quick-help)
- (define-key map "?" 'package-menu-view-commentary)
+ (define-key map "?" 'package-menu-describe-package)
+ (define-key map [follow-link] 'mouse-face)
+ (define-key map [mouse-2] 'mouse-select-window)
(define-key map [menu-bar package-menu] (cons "Package" menu-map))
(define-key menu-map [mq]
'(menu-item "Quit" quit-window
@@ -1180,7 +1230,7 @@ The variable `package-load-list' controls which packages to load."
:help "Mark a package for installation and move to the next line"))
(define-key menu-map [s3] '("--"))
(define-key menu-map [mg]
- '(menu-item "Update package list" package-menu-revert
+ '(menu-item "Update package list" revert-buffer
:help "Update the list of packages"))
(define-key menu-map [mr]
'(menu-item "Refresh package list" package-menu-refresh
@@ -1205,6 +1255,7 @@ The variable `package-load-list' controls which packages to load."
(defvar package-menu-sort-button-map
(let ((map (make-sparse-keymap)))
(define-key map [header-line mouse-1] 'package-menu-sort-by-column)
+ (define-key map [header-line mouse-2] 'package-menu-sort-by-column)
(define-key map [follow-link] 'mouse-face)
map)
"Local keymap for package menu sort buttons.")
@@ -1222,25 +1273,52 @@ Letters do not insert themselves; instead, they are commands.
(setq mode-name "Package Menu")
(setq truncate-lines t)
(setq buffer-read-only t)
- ;; Support Emacs 21.
- (if (fboundp 'run-mode-hooks)
- (run-mode-hooks 'package-menu-mode-hook)
- (run-hooks 'package-menu-mode-hook)))
+ (setq revert-buffer-function 'package-menu-revert)
+ (setq header-line-format
+ (mapconcat
+ (lambda (pair)
+ (let ((column (car pair))
+ (name (cdr pair)))
+ (concat
+ ;; Insert a space that aligns the button properly.
+ (propertize " " 'display (list 'space :align-to column)
+ 'face 'fixed-pitch)
+ ;; Set up the column button.
+ (propertize name
+ 'column-name name
+ 'help-echo "mouse-1: sort by column"
+ 'mouse-face 'highlight
+ 'keymap package-menu-sort-button-map))))
+ ;; We take a trick from buff-menu and have a dummy leading
+ ;; space to align the header line with the beginning of the
+ ;; text. This doesn't really work properly on Emacs 21, but
+ ;; it is close enough.
+ '((0 . "")
+ (2 . "Package")
+ (20 . "Version")
+ (32 . "Status")
+ (43 . "Description"))
+ ""))
+ (run-mode-hooks 'package-menu-mode-hook))
(defun package-menu-refresh ()
- "Download the ELPA archive.
-This fetches the file describing the current contents of
-the Emacs Lisp Package Archive, and then refreshes the
-package menu. This lets you see what new packages are
-available for download."
+ "Download the Emacs Lisp package archive.
+This fetches the contents of each archive specified in
+`package-archives', and then refreshes the package menu."
(interactive)
+ (unless (eq major-mode 'package-menu-mode)
+ (error "The current buffer is not a Package Menu"))
(package-refresh-contents)
- (package-list-packages-internal))
+ (package--generate-package-list))
-(defun package-menu-revert ()
- "Update the list of packages."
+(defun package-menu-revert (&optional arg noconfirm)
+ "Update the list of packages.
+This function is the `revert-buffer-function' for Package Menu
+buffers. The arguments are ignored."
(interactive)
- (package-list-packages-internal))
+ (unless (eq major-mode 'package-menu-mode)
+ (error "The current buffer is not a Package Menu"))
+ (package--generate-package-list))
(defun package-menu-describe-package ()
"Describe the package in the current line."
@@ -1297,32 +1375,8 @@ available for download."
(interactive)
(message "n-ext, i-nstall, d-elete, u-nmark, x-ecute, r-efresh, h-elp"))
-(defun package-menu-view-commentary ()
- "Display information about this package.
-For single-file packages, shows the commentary section from the header.
-For larger packages, shows the README file."
- (interactive)
- (let* ((pkg-name (package-menu-get-package))
- (buffer (url-retrieve-synchronously
- (concat (package-archive-url pkg-name)
- pkg-name
- "-readme.txt")))
- start-point ok)
- (with-current-buffer buffer
- ;; FIXME: it would be nice to work with any URL type.
- (setq start-point url-http-end-of-headers)
- (setq ok (eq (url-http-parse-response) 200)))
- (let ((new-buffer (get-buffer-create "*Package Info*")))
- (with-current-buffer new-buffer
- (let ((buffer-read-only nil))
- (erase-buffer)
- (insert "Package information for " pkg-name "\n\n")
- (if ok
- (insert-buffer-substring buffer start-point)
- (insert "This package lacks a README file or commentary.\n"))
- (goto-char (point-min))
- (view-mode)))
- (display-buffer new-buffer t))))
+(define-obsolete-function-alias
+ 'package-menu-view-commentary 'package-menu-describe-package "24.1")
;; Return the name of the package on the current line.
(defun package-menu-get-package ()
@@ -1405,151 +1459,161 @@ Emacs."
(defun package-list-maybe-add (package version status description result)
(unless (assoc (cons package version) result)
- (setq result (cons (list (cons package version) status description)
- result)))
+ (push (list (cons package version) status description) result))
result)
-;; This decides how we should sort; nil means by package name.
-(defvar package-menu-sort-key nil)
+(defvar package-menu-package-list nil
+ "List of packages to display in the Package Menu buffer.
+A value of nil means to display all packages.")
-(defun package-list-packages-internal ()
- (package-initialize) ; FIXME: do this here?
- (with-current-buffer (get-buffer-create "*Packages*")
+(defvar package-menu-sort-key nil
+ "Sort key for the current Package Menu buffer.")
+
+(defun package--generate-package-list ()
+ "Populate the current Package Menu buffer."
+ (package-initialize)
+ (let ((inhibit-read-only t)
+ info-list name desc hold builtin)
(setq buffer-read-only nil)
(erase-buffer)
- (let ((info-list)
- name desc hold
- builtin)
- ;; List installed packages
- (dolist (elt package-alist)
- ;; Ignore the Emacs package.
- (setq name (car elt)
- desc (cdr elt)
- hold (assq name package-load-list))
- (unless (eq name 'emacs)
- (setq info-list
- (package-list-maybe-add
- name (package-desc-vers desc)
- ;; FIXME: it turns out to be tricky to see if this
- ;; package is presently activated.
- (cond ((stringp (cadr hold))
- "held")
- ((and (setq builtin (assq name package--builtins))
- (version-list-=
- (package-desc-vers (cdr builtin))
- (package-desc-vers desc)))
- "built-in")
- (t "installed"))
- (package-desc-doc desc)
- info-list))))
- ;; List available packages
- (dolist (elt package-archive-contents)
- (setq name (car elt)
- desc (cdr elt)
- hold (assq name package-load-list))
- (unless (and hold (stringp (cadr hold))
- (package-installed-p
- name (version-to-list (cadr hold))))
- (setq info-list
- (package-list-maybe-add name
- (package-desc-vers desc)
- (if (and hold (null (cadr hold)))
- "disabled"
- "available")
- (package-desc-doc (cdr elt))
- info-list))))
- ;; List obsolete packages
- (mapc (lambda (elt)
- (mapc (lambda (inner-elt)
- (setq info-list
- (package-list-maybe-add (car elt)
- (package-desc-vers
- (cdr inner-elt))
- "obsolete"
- (package-desc-doc
- (cdr inner-elt))
- info-list)))
- (cdr elt)))
- package-obsolete-alist)
- (let ((selector (cond
- ((string= package-menu-sort-key "Version")
- ;; FIXME this doesn't work.
- #'(lambda (e) (cdr (car e))))
- ((string= package-menu-sort-key "Status")
- #'(lambda (e) (car (cdr e))))
- ((string= package-menu-sort-key "Description")
- #'(lambda (e) (car (cdr (cdr e)))))
- (t ; "Package" is default.
- #'(lambda (e) (symbol-name (car (car e))))))))
+ ;; List installed packages
+ (dolist (elt package-alist)
+ (setq name (car elt))
+ (when (and (not (eq name 'emacs)) ; Hide the `emacs' package.
+ (or (null package-menu-package-list)
+ (memq name package-menu-package-list)))
+ (setq desc (cdr elt)
+ hold (cadr (assq name package-load-list))
+ builtin (cdr (assq name package--builtins)))
+ (setq info-list
+ (package-list-maybe-add
+ name (package-desc-vers desc)
+ ;; FIXME: it turns out to be tricky to see if this
+ ;; package is presently activated.
+ (cond ((stringp hold) "held")
+ ((and builtin
+ (version-list-=
+ (package-desc-vers builtin)
+ (package-desc-vers desc)))
+ "built-in")
+ (t "installed"))
+ (package-desc-doc desc)
+ info-list))))
+
+ ;; List available and disabled packages
+ (dolist (elt package-archive-contents)
+ (setq name (car elt)
+ desc (cdr elt)
+ hold (assq name package-load-list))
+ (when (or (null package-menu-package-list)
+ (memq name package-menu-package-list))
(setq info-list
- (sort info-list
- (lambda (left right)
- (let ((vleft (funcall selector left))
- (vright (funcall selector right)))
- (string< vleft vright))))))
- (mapc (lambda (elt)
- (package-print-package (car (car elt))
- (cdr (car elt))
- (car (cdr elt))
- (car (cdr (cdr elt)))))
- info-list))
+ (package-list-maybe-add name
+ (package-desc-vers desc)
+ (if (and hold (null (cadr hold)))
+ "disabled"
+ "available")
+ (package-desc-doc (cdr elt))
+ info-list))))
+ ;; List obsolete packages
+ (mapc (lambda (elt)
+ (mapc (lambda (inner-elt)
+ (setq info-list
+ (package-list-maybe-add (car elt)
+ (package-desc-vers
+ (cdr inner-elt))
+ "obsolete"
+ (package-desc-doc
+ (cdr inner-elt))
+ info-list)))
+ (cdr elt)))
+ package-obsolete-alist)
+
+ (setq info-list
+ (sort info-list
+ (cond ((string= package-menu-sort-key "Package")
+ 'package-menu--name-predicate)
+ ((string= package-menu-sort-key "Version")
+ 'package-menu--version-predicate)
+ ((string= package-menu-sort-key "Description")
+ 'package-menu--description-predicate)
+ (t ; By default, sort by package status
+ 'package-menu--status-predicate))))
+
+ (dolist (elt info-list)
+ (package-print-package (car (car elt))
+ (cdr (car elt))
+ (car (cdr elt))
+ (car (cdr (cdr elt)))))
(goto-char (point-min))
+ (set-buffer-modified-p nil)
(current-buffer)))
+(defun package-menu--version-predicate (left right)
+ (let ((vleft (or (cdr (car left)) '(0)))
+ (vright (or (cdr (car right)) '(0))))
+ (if (version-list-= vleft vright)
+ (package-menu--name-predicate left right)
+ (version-list-< vleft vright))))
+
+(defun package-menu--status-predicate (left right)
+ (let ((sleft (cadr left))
+ (sright (cadr right)))
+ (cond ((string= sleft sright)
+ (package-menu--name-predicate left right))
+ ((string= sleft "available") t)
+ ((string= sright "available") nil)
+ ((string= sleft "installed") t)
+ ((string= sright "installed") nil)
+ ((string= sleft "held") t)
+ ((string= sright "held") nil)
+ ((string= sleft "built-in") t)
+ ((string= sright "built-in") nil)
+ ((string= sleft "obsolete") t)
+ ((string= sright "obsolete") nil)
+ (t (string< sleft sright)))))
+
+(defun package-menu--description-predicate (left right)
+ (let ((sleft (car (cddr left)))
+ (sright (car (cddr right))))
+ (if (string= sleft sright)
+ (package-menu--name-predicate left right)
+ (string< sleft sright))))
+
+(defun package-menu--name-predicate (left right)
+ (string< (symbol-name (caar left))
+ (symbol-name (caar right))))
+
(defun package-menu-sort-by-column (&optional e)
- "Sort the package menu by the last column clicked on."
- (interactive (list last-input-event))
- (if e (mouse-select-window e))
+ "Sort the package menu by the column of the mouse click E."
+ (interactive "e")
(let* ((pos (event-start e))
(obj (posn-object pos))
(col (if obj
(get-text-property (cdr obj) 'column-name (car obj))
- (get-text-property (posn-point pos) 'column-name))))
- (setq package-menu-sort-key col))
- (package-list-packages-internal))
-
-(defun package--list-packages ()
- "Display a list of packages.
-Helper function that does all the work for the user-facing functions."
- (with-current-buffer (package-list-packages-internal)
+ (get-text-property (posn-point pos) 'column-name)))
+ (buf (window-buffer (posn-window (event-start e)))))
+ (with-current-buffer buf
+ (when (eq major-mode 'package-menu-mode)
+ (setq package-menu-sort-key col)
+ (package--generate-package-list)))))
+
+(defun package--list-packages (&optional packages)
+ "Generate and pop to the *Packages* buffer.
+Optional PACKAGES is a list of names of packages (symbols) to
+list; the default is to display everything in `package-alist'."
+ (with-current-buffer (get-buffer-create "*Packages*")
(package-menu-mode)
- ;; Set up the header line.
- (setq header-line-format
- (mapconcat
- (lambda (pair)
- (let ((column (car pair))
- (name (cdr pair)))
- (concat
- ;; Insert a space that aligns the button properly.
- (propertize " " 'display (list 'space :align-to column)
- 'face 'fixed-pitch)
- ;; Set up the column button.
- (if (string= name "Version")
- name
- (propertize name
- 'column-name name
- 'help-echo "mouse-1: sort by column"
- 'mouse-face 'highlight
- 'keymap package-menu-sort-button-map)))))
- ;; We take a trick from buff-menu and have a dummy leading
- ;; space to align the header line with the beginning of the
- ;; text. This doesn't really work properly on Emacs 21,
- ;; but it is close enough.
- '((0 . "")
- (2 . "Package")
- (20 . "Version")
- (32 . "Status")
- (43 . "Description"))
- ""))
-
+ (set (make-local-variable 'package-menu-package-list) packages)
+ (set (make-local-variable 'package-menu-sort-key) nil)
+ (package--generate-package-list)
;; It's okay to use pop-to-buffer here. The package menu buffer
- ;; has keybindings, and the user just typed 'M-x
- ;; package-list-packages', suggesting that they might want to use
- ;; them.
+ ;; has keybindings, and the user just typed `M-x list-packages',
+ ;; suggesting that they might want to use them.
(pop-to-buffer (current-buffer))))
;;;###autoload
-(defun package-list-packages ()
+(defun list-packages ()
"Display a list of packages.
Fetches the updated list of packages before displaying.
The list is displayed in a buffer named `*Packages*'."
@@ -1557,6 +1621,9 @@ The list is displayed in a buffer named `*Packages*'."
(package-refresh-contents)
(package--list-packages))
+;;;###autoload
+(defalias 'package-list-packages 'list-packages)
+
(defun package-list-packages-no-fetch ()
"Display a list of packages.
Does not fetch the updated list of packages before displaying.
diff --git a/lisp/emacs-lisp/pcase.el b/lisp/emacs-lisp/pcase.el
index 03d760b2df5..b2b27a0e0d6 100644
--- a/lisp/emacs-lisp/pcase.el
+++ b/lisp/emacs-lisp/pcase.el
@@ -1,6 +1,6 @@
;;; pcase.el --- ML-style pattern-matching macro for Elisp
-;; Copyright (C) 2010 Stefan Monnier
+;; Copyright (C) 2010 Free Software Foundation, Inc.
;; Author: Stefan Monnier <monnier@iro.umontreal.ca>
;; Keywords:
@@ -290,9 +290,13 @@ MATCH is the pattern that needs to be matched, of the form:
(defun pcase-split-memq (elems pat)
;; Based on pcase-split-eq.
(cond
- ;; The same match will give the same result.
+ ;; The same match will give the same result, but we don't know how
+ ;; to check it.
+ ;; (???
+ ;; (cons :pcase-succeed nil))
+ ;; A match for one of the elements may succeed or fail.
((and (eq (car-safe pat) '\`) (member (cadr pat) elems))
- (cons :pcase-succeed nil))
+ nil)
;; A different match will fail if this one succeeds.
((and (eq (car-safe pat) '\`)
;; (or (integerp (cadr pat)) (symbolp (cadr pat))
@@ -383,18 +387,20 @@ and otherwise defers to REST which is a list of branches of the form
`(,(cadr upat) ,sym)
(let* ((exp (cadr upat))
;; `vs' is an upper bound on the vars we need.
- (vs (pcase-fgrep (mapcar #'car vars) exp)))
- (if vs
- ;; Let's not replace `vars' in `exp' since it's
- ;; too difficult to do it right, instead just
- ;; let-bind `vars' around `exp'.
- `(let ,(mapcar (lambda (var)
- (list var (cdr (assq var vars))))
- vs)
- ;; FIXME: `vars' can capture `sym'. E.g.
- ;; (pcase x ((and `(,x . ,y) (pred (fun x)))))
- (,@exp ,sym))
- `(,@exp ,sym))))
+ (vs (pcase-fgrep (mapcar #'car vars) exp))
+ (call (if (functionp exp)
+ `(,exp ,sym) `(,@exp ,sym))))
+ (if (null vs)
+ call
+ ;; Let's not replace `vars' in `exp' since it's
+ ;; too difficult to do it right, instead just
+ ;; let-bind `vars' around `exp'.
+ `(let ,(mapcar (lambda (var)
+ (list var (cdr (assq var vars))))
+ vs)
+ ;; FIXME: `vars' can capture `sym'. E.g.
+ ;; (pcase x ((and `(,x . ,y) (pred (fun x)))))
+ ,call))))
(pcase-u1 matches code vars then-rest)
(pcase-u else-rest))))
((symbolp upat)
@@ -483,7 +489,7 @@ and if not, defers to REST which is a list of branches of the form
(pcase-u1 matches code vars then-rest)
(pcase-u else-rest))))
(t (error "Unkown QPattern %s" qpat))))
-
+
(provide 'pcase)
;;; pcase.el ends here
diff --git a/lisp/emacs-lisp/regexp-opt.el b/lisp/emacs-lisp/regexp-opt.el
index 78eba19a253..a1494741572 100644
--- a/lisp/emacs-lisp/regexp-opt.el
+++ b/lisp/emacs-lisp/regexp-opt.el
@@ -120,7 +120,7 @@ This means the number of non-shy regexp grouping constructs
(string-match regexp "")
;; Count the number of open parentheses in REGEXP.
(let ((count 0) start last)
- (while (string-match "\\\\(\\(\\?:\\)?" regexp start)
+ (while (string-match "\\\\(\\(\\?[0-9]*:\\)?" regexp start)
(setq start (match-end 0)) ; Start of next search.
(when (and (not (match-beginning 1))
(subregexp-context-p regexp (match-beginning 0) last))
diff --git a/lisp/emacs-lisp/rx.el b/lisp/emacs-lisp/rx.el
index 85fe3514b01..522d452c2dc 100644
--- a/lisp/emacs-lisp/rx.el
+++ b/lisp/emacs-lisp/rx.el
@@ -427,7 +427,7 @@ Only both edges of each range is checked."
(mapcar (lambda (e)
(cond
((= (car e) (cdr e)) (list (car e)))
- ;; ((= (1+ (car e)) (cdr e)) (list (car e) (cdr e)))
+ ((= (1+ (car e)) (cdr e)) (list (car e) (cdr e)))
((list e))))
l))
(delete-dups str))))
@@ -545,7 +545,10 @@ ARG is optional."
((numberp e) (string e))
((consp e)
(if (and (= (1+ (car e)) (cdr e))
- (null (memq (car e) '(?\] ?-))))
+ ;; rx-any-condense-range should
+ ;; prevent this case from happening.
+ (null (memq (car e) '(?\] ?-)))
+ (null (memq (cdr e) '(?\] ?-))))
(string (car e) (cdr e))
(string (car e) ?- (cdr e))))
(e)))
diff --git a/lisp/emacs-lisp/smie.el b/lisp/emacs-lisp/smie.el
index fb1e4737d39..c6df851b0e5 100644
--- a/lisp/emacs-lisp/smie.el
+++ b/lisp/emacs-lisp/smie.el
@@ -65,6 +65,9 @@
;;; Code:
+;; FIXME: I think the behavior on empty lines is wrong. It shouldn't
+;; look at the next token on subsequent lines.
+
(eval-when-compile (require 'cl))
(defvar comment-continue)
@@ -72,6 +75,26 @@
;;; Building precedence level tables from BNF specs.
+;; We have 4 different representations of a "grammar":
+;; - a BNF table, which is a list of BNF rules of the form
+;; (NONTERM RHS1 ... RHSn) where each RHS is a list of terminals (tokens)
+;; or nonterminals. Any element in these lists which does not appear as
+;; the `car' of a BNF rule is taken to be a terminal.
+;; - A list of precedences (key word "precs"), is a list, sorted
+;; from lowest to highest precedence, of precedence classes that
+;; have the form (ASSOCIATIVITY TERMINAL1 .. TERMINALn), where
+;; ASSOCIATIVITY can be `assoc', `left', `right' or `nonassoc'.
+;; - a 2 dimensional precedence table (key word "prec2"), is a 2D
+;; table recording the precedence relation (can be `<', `=', `>', or
+;; nil) between each pair of tokens.
+;; - a precedence-level table (key word "levels"), while is a alist
+;; giving for each token its left and right precedence level (a
+;; number or nil). This is used in `smie-op-levels'.
+;; The prec2 tables are only intermediate data structures: the source
+;; code normally provides a mix of BNF and precs tables, and then
+;; turns them into a levels table, which is what's used by the rest of
+;; the SMIE code.
+
(defun smie-set-prec2tab (table x y val &optional override)
(assert (and x y))
(let* ((key (cons x y))
@@ -155,9 +178,9 @@ one of those elements share the same precedence level and associativity."
(if (not (member (car shr) nts))
(pushnew (car shr) last-ops)
(pushnew (car shr) last-nts)
- (when (consp (cdr shr))
- (assert (not (member (cadr shr) nts)))
- (pushnew (cadr shr) last-ops)))))
+ (when (consp (cdr shr))
+ (assert (not (member (cadr shr) nts)))
+ (pushnew (cadr shr) last-ops)))))
(push (cons nt first-ops) first-ops-table)
(push (cons nt last-ops) last-ops-table)
(push (cons nt first-nts) first-nts-table)
@@ -203,13 +226,105 @@ one of those elements share the same precedence level and associativity."
(setq rhs (cdr rhs)))))
prec2))
+;; (defun smie-prec2-closer-alist (prec2 include-inners)
+;; "Build a closer-alist from a PREC2 table.
+;; The return value is in the same form as `smie-closer-alist'.
+;; INCLUDE-INNERS if non-nil means that inner keywords will be included
+;; in the table, e.g. the table will include things like (\"if\" . \"else\")."
+;; (let* ((non-openers '())
+;; (non-closers '())
+;; ;; For each keyword, this gives the matching openers, if any.
+;; (openers (make-hash-table :test 'equal))
+;; (closers '())
+;; (done nil))
+;; ;; First, find the non-openers and non-closers.
+;; (maphash (lambda (k v)
+;; (unless (or (eq v '<) (member (cdr k) non-openers))
+;; (push (cdr k) non-openers))
+;; (unless (or (eq v '>) (member (car k) non-closers))
+;; (push (car k) non-closers)))
+;; prec2)
+;; ;; Then find the openers and closers.
+;; (maphash (lambda (k _)
+;; (unless (member (car k) non-openers)
+;; (puthash (car k) (list (car k)) openers))
+;; (unless (or (member (cdr k) non-closers)
+;; (member (cdr k) closers))
+;; (push (cdr k) closers)))
+;; prec2)
+;; ;; Then collect the matching elements.
+;; (while (not done)
+;; (setq done t)
+;; (maphash (lambda (k v)
+;; (when (eq v '=)
+;; (let ((aopeners (gethash (car k) openers))
+;; (dopeners (gethash (cdr k) openers))
+;; (new nil))
+;; (dolist (o aopeners)
+;; (unless (member o dopeners)
+;; (setq new t)
+;; (push o dopeners)))
+;; (when new
+;; (setq done nil)
+;; (puthash (cdr k) dopeners openers)))))
+;; prec2))
+;; ;; Finally, dump the resulting table.
+;; (let ((alist '()))
+;; (maphash (lambda (k v)
+;; (when (or include-inners (member k closers))
+;; (dolist (opener v)
+;; (unless (equal opener k)
+;; (push (cons opener k) alist)))))
+;; openers)
+;; alist)))
+
+(defun smie-bnf-closer-alist (bnf &optional no-inners)
+ ;; We can also build this closer-alist table from a prec2 table,
+ ;; but it takes more work, and the order is unpredictable, which
+ ;; is a problem for smie-close-block.
+ ;; More convenient would be to build it from a levels table since we
+ ;; always have this table (contrary to the BNF), but it has all the
+ ;; disadvantages of the prec2 case plus the disadvantage that the levels
+ ;; table has lost some info which would result in extra invalid pairs.
+ "Build a closer-alist from a BNF table.
+The return value is in the same form as `smie-closer-alist'.
+NO-INNERS if non-nil means that inner keywords will be excluded
+from the table, e.g. the table will not include things like (\"if\" . \"else\")."
+ (let ((nts (mapcar #'car bnf)) ;non terminals.
+ (alist '()))
+ (dolist (nt bnf)
+ (dolist (rhs (cdr nt))
+ (unless (or (< (length rhs) 2) (member (car rhs) nts))
+ (if no-inners
+ (let ((last (car (last rhs))))
+ (unless (member last nts)
+ (pushnew (cons (car rhs) last) alist :test #'equal)))
+ ;; Reverse so that the "real" closer gets there first,
+ ;; which is important for smie-close-block.
+ (dolist (term (reverse (cdr rhs)))
+ (unless (member term nts)
+ (pushnew (cons (car rhs) term) alist :test #'equal)))))))
+ (nreverse alist)))
+
+
(defun smie-prec2-levels (prec2)
+ ;; FIXME: Rather than only return an alist of precedence levels, we should
+ ;; also extract other useful data from it:
+ ;; - matching sets of block openers&closers (which can otherwise become
+ ;; collapsed into a single equivalence class in smie-op-levels) for
+ ;; smie-close-block as well as to detect mismatches in smie-next-sexp
+ ;; or in blink-paren (as well as to do the blink-paren for inner
+ ;; keywords like the "in" of "let..in..end").
+ ;; - better default indentation rules (i.e. non-zero indentation after inner
+ ;; keywords like the "in" of "let..in..end") for smie-indent-after-keyword.
+ ;; Of course, maybe those things would be even better handled in the
+ ;; bnf->prec function.
"Take a 2D precedence table and turn it into an alist of precedence levels.
PREC2 is a table as returned by `smie-precs-precedence-table' or
`smie-bnf-precedence-table'."
;; For each operator, we create two "variables" (corresponding to
;; the left and right precedence level), which are represented by
- ;; cons cells. Those are the vary cons cells that appear in the
+ ;; cons cells. Those are the very cons cells that appear in the
;; final `table'. The value of each "variable" is kept in the `car'.
(let ((table ())
(csts ())
@@ -268,7 +383,7 @@ PREC2 is a table as returned by `smie-precs-precedence-table' or
;; distinguish associative operators (which will have
;; left = right).
(unless (caar cst)
- (setcar (car cst) i)
+ (setcar (car cst) i)
(incf i))
(setq csts (delq cst csts))))
(unless progress
@@ -321,32 +436,30 @@ it should move backward to the beginning of the previous token.")
(defun smie-default-backward-token ()
(forward-comment (- (point)))
- (buffer-substring (point)
- (progn (if (zerop (skip-syntax-backward "."))
- (skip-syntax-backward "w_'"))
- (point))))
+ (buffer-substring-no-properties
+ (point)
+ (progn (if (zerop (skip-syntax-backward "."))
+ (skip-syntax-backward "w_'"))
+ (point))))
(defun smie-default-forward-token ()
(forward-comment (point-max))
- (buffer-substring (point)
- (progn (if (zerop (skip-syntax-forward "."))
- (skip-syntax-forward "w_'"))
- (point))))
+ (buffer-substring-no-properties
+ (point)
+ (progn (if (zerop (skip-syntax-forward "."))
+ (skip-syntax-forward "w_'"))
+ (point))))
(defun smie-associative-p (toklevels)
;; in "a + b + c" we want to stop at each +, but in
- ;; "if a then b else c" we don't want to stop at each keyword.
+ ;; "if a then b elsif c then d else c" we don't want to stop at each keyword.
;; To distinguish the two cases, we made smie-prec2-levels choose
;; different levels for each part of "if a then b else c", so that
;; by checking if the left-level is equal to the right level, we can
;; figure out that it's an associative operator.
- ;; This is not 100% foolproof, tho, since a grammar like
- ;; (exp ("A" exp "C") ("A" exp "B" exp "C"))
- ;; will cause "B" to have equal left and right levels, even though
- ;; it is not an associative operator.
- ;; A better check would be the check the actual previous operator
- ;; against this one to see if it's the same, but we'd have to change
- ;; `levels' to keep a stack of operators rather than only levels.
+ ;; This is not 100% foolproof, tho, since the "elsif" will have to have
+ ;; equal left and right levels (since it's optional), so smie-next-sexp
+ ;; has to be careful to distinguish those different cases.
(eq (smie-op-left toklevels) (smie-op-right toklevels)))
(defun smie-next-sexp (next-token next-sexp op-forw op-back halfsexp)
@@ -371,51 +484,71 @@ Possible return values:
(let* ((pos (point))
(token (funcall next-token))
(toklevels (cdr (assoc token smie-op-levels))))
-
(cond
((null toklevels)
(when (zerop (length token))
- (condition-case err
- (progn (goto-char pos) (funcall next-sexp 1) nil)
- (scan-error (throw 'return (list t (caddr err)))))
+ (condition-case err
+ (progn (goto-char pos) (funcall next-sexp 1) nil)
+ (scan-error (throw 'return
+ (list t (caddr err)
+ (buffer-substring-no-properties
+ (caddr err)
+ (+ (caddr err)
+ (if (< (point) (caddr err))
+ -1 1)))))))
(if (eq pos (point))
;; We did not move, so let's abort the loop.
(throw 'return (list t (point))))))
((null (funcall op-back toklevels))
;; A token like a paren-close.
(assert (funcall op-forw toklevels)) ;Otherwise, why mention it?
- (push (funcall op-forw toklevels) levels))
+ (push toklevels levels))
(t
- (while (and levels (< (funcall op-back toklevels) (car levels)))
+ (while (and levels (< (funcall op-back toklevels)
+ (funcall op-forw (car levels))))
(setq levels (cdr levels)))
(cond
((null levels)
(if (and halfsexp (funcall op-forw toklevels))
- (push (funcall op-forw toklevels) levels)
+ (push toklevels levels)
(throw 'return
(prog1 (list (or (car toklevels) t) (point) token)
(goto-char pos)))))
(t
- (if (and levels (= (funcall op-back toklevels) (car levels)))
- (setq levels (cdr levels)))
- (cond
- ((null levels)
+ (let ((lastlevels levels))
+ (if (and levels (= (funcall op-back toklevels)
+ (funcall op-forw (car levels))))
+ (setq levels (cdr levels)))
+ ;; We may have found a match for the previously pending
+ ;; operator. Is this the end?
(cond
+ ;; Keep looking as long as we haven't matched the
+ ;; topmost operator.
+ (levels
+ (if (funcall op-forw toklevels)
+ (push toklevels levels)))
+ ;; We matched the topmost operator. If the new operator
+ ;; is the last in the corresponding BNF rule, we're done.
((null (funcall op-forw toklevels))
+ ;; It is the last element, let's stop here.
(throw 'return (list nil (point) token)))
- ((smie-associative-p toklevels)
+ ;; If the new operator is not the last in the BNF rule,
+ ;; ans is not associative, it's one of the inner operators
+ ;; (like the "in" in "let .. in .. end"), so keep looking.
+ ((not (smie-associative-p toklevels))
+ (push toklevels levels))
+ ;; The new operator is associative. Two cases:
+ ;; - it's really just an associative operator (like + or ;)
+ ;; in which case we should have stopped right before.
+ ((and lastlevels
+ (smie-associative-p (car lastlevels)))
(throw 'return
(prog1 (list (or (car toklevels) t) (point) token)
(goto-char pos))))
- ;; We just found a match to the previously pending operator
- ;; but this new operator is still part of a larger RHS.
- ;; E.g. we're now looking at the "then" in
- ;; "if a then b else c". So we have to keep parsing the
- ;; rest of the construct.
- (t (push (funcall op-forw toklevels) levels))))
- (t
- (if (funcall op-forw toklevels)
- (push (funcall op-forw toklevels) levels))))))))
+ ;; - it's an associative operator within a larger construct
+ ;; (e.g. an "elsif"), so we should just ignore it and keep
+ ;; looking for the closing element.
+ (t (setq levels lastlevels))))))))
levels)
(setq halfsexp nil)))))
@@ -430,11 +563,11 @@ Possible return values:
(t POS TOKEN): same thing but for an open-paren or the beginning of buffer.
(nil POS TOKEN): we skipped over a paren-like pair.
nil: we skipped over an identifier, matched parentheses, ..."
- (smie-next-sexp
- (indirect-function smie-backward-token-function)
- (indirect-function 'backward-sexp)
- (indirect-function 'smie-op-left)
- (indirect-function 'smie-op-right)
+ (smie-next-sexp
+ (indirect-function smie-backward-token-function)
+ (indirect-function 'backward-sexp)
+ (indirect-function 'smie-op-left)
+ (indirect-function 'smie-op-right)
halfsexp))
(defun smie-forward-sexp (&optional halfsexp)
@@ -448,44 +581,196 @@ Possible return values:
(t POS TOKEN): same thing but for an open-paren or the beginning of buffer.
(nil POS TOKEN): we skipped over a paren-like pair.
nil: we skipped over an identifier, matched parentheses, ..."
- (smie-next-sexp
- (indirect-function smie-forward-token-function)
- (indirect-function 'forward-sexp)
- (indirect-function 'smie-op-right)
- (indirect-function 'smie-op-left)
+ (smie-next-sexp
+ (indirect-function smie-forward-token-function)
+ (indirect-function 'forward-sexp)
+ (indirect-function 'smie-op-right)
+ (indirect-function 'smie-op-left)
halfsexp))
+;;; Miscellanous commands using the precedence parser.
+
(defun smie-backward-sexp-command (&optional n)
"Move backward through N logical elements."
- (interactive "p")
- (if (< n 0)
- (smie-forward-sexp-command (- n))
- (let ((forward-sexp-function nil))
- (while (> n 0)
- (decf n)
- (let ((pos (point))
- (res (smie-backward-sexp 'halfsexp)))
- (if (and (car res) (= pos (point)) (not (bolp)))
- (signal 'scan-error
- (list "Containing expression ends prematurely"
- (cadr res) (cadr res)))
- nil))))))
+ (interactive "^p")
+ (smie-forward-sexp-command (- n)))
(defun smie-forward-sexp-command (&optional n)
"Move forward through N logical elements."
+ (interactive "^p")
+ (let ((forw (> n 0))
+ (forward-sexp-function nil))
+ (while (/= n 0)
+ (setq n (- n (if forw 1 -1)))
+ (let ((pos (point))
+ (res (if forw
+ (smie-forward-sexp 'halfsexp)
+ (smie-backward-sexp 'halfsexp))))
+ (if (and (car res) (= pos (point)) (not (if forw (eobp) (bobp))))
+ (signal 'scan-error
+ (list "Containing expression ends prematurely"
+ (cadr res) (cadr res)))
+ nil)))))
+
+(defvar smie-closer-alist nil
+ "Alist giving the closer corresponding to an opener.")
+
+(defun smie-close-block ()
+ "Close the closest surrounding block."
+ (interactive)
+ (let ((closer
+ (save-excursion
+ (backward-up-list 1)
+ (if (looking-at "\\s(")
+ (string (cdr (syntax-after (point))))
+ (let* ((open (funcall smie-forward-token-function))
+ (closer (cdr (assoc open smie-closer-alist)))
+ (levels (list (assoc open smie-op-levels)))
+ (seen '())
+ (found '()))
+ (cond
+ ;; Even if we improve the auto-computation of closers,
+ ;; there are still cases where we need manual
+ ;; intervention, e.g. for Octave's use of `until'
+ ;; as a pseudo-closer of `do'.
+ (closer)
+ ((or (equal levels '(nil)) (nth 1 (car levels)))
+ (error "Doesn't look like a block"))
+ (t
+ ;; FIXME: With grammars like Octave's, every closer ("end",
+ ;; "endif", "endwhile", ...) has the same level, so we'd need
+ ;; to look at the BNF or at least at the 2D prec-table, in
+ ;; order to find the right closer for a given opener.
+ (while levels
+ (let ((level (pop levels)))
+ (dolist (other smie-op-levels)
+ (when (and (eq (nth 2 level) (nth 1 other))
+ (not (memq other seen)))
+ (push other seen)
+ (if (nth 2 other)
+ (push other levels)
+ (push (car other) found))))))
+ (cond
+ ((null found) (error "No known closer for opener %s" open))
+ ;; FIXME: what should we do if there are various closers?
+ (t (car found))))))))))
+ (unless (save-excursion (skip-chars-backward " \t") (bolp))
+ (newline))
+ (insert closer)
+ (if (save-excursion (skip-chars-forward " \t") (eolp))
+ (indent-according-to-mode)
+ (reindent-then-newline-and-indent))))
+
+(defun smie-down-list (&optional arg)
+ "Move forward down one level paren-like blocks. Like `down-list'.
+With argument ARG, do this that many times.
+A negative argument means move backward but still go down a level.
+This command assumes point is not in a string or comment."
(interactive "p")
- (if (< n 0)
- (smie-backward-sexp-command (- n))
- (let ((forward-sexp-function nil))
- (while (> n 0)
- (decf n)
- (let ((pos (point))
- (res (smie-forward-sexp 'halfsexp)))
- (if (and (car res) (= pos (point)) (not (bolp)))
- (signal 'scan-error
- (list "Containing expression ends prematurely"
- (cadr res) (cadr res)))
- nil))))))
+ (let ((start (point))
+ (inc (if (< arg 0) -1 1))
+ (offset (if (< arg 0) 1 0))
+ (next-token (if (< arg 0)
+ smie-backward-token-function
+ smie-forward-token-function)))
+ (while (/= arg 0)
+ (setq arg (- arg inc))
+ (while
+ (let* ((pos (point))
+ (token (funcall next-token))
+ (levels (assoc token smie-op-levels)))
+ (cond
+ ((zerop (length token))
+ (if (if (< inc 0) (looking-back "\\s(\\|\\s)" (1- (point)))
+ (looking-at "\\s(\\|\\s)"))
+ ;; Go back to `start' in case of an error. This presumes
+ ;; none of the token we've found until now include a ( or ).
+ (progn (goto-char start) (down-list inc) nil)
+ (forward-sexp inc)
+ (/= (point) pos)))
+ ((and levels (null (nth (+ 1 offset) levels))) nil)
+ ((and levels (null (nth (- 2 offset) levels)))
+ (let ((end (point)))
+ (goto-char start)
+ (signal 'scan-error
+ (list "Containing expression ends prematurely"
+ pos end))))
+ (t)))))))
+
+(defvar smie-blink-matching-triggers '(?\s ?\n)
+ "Chars which might trigger `blink-matching-open'.
+These can include the final chars of end-tokens, or chars that are
+typically inserted right after an end token.
+I.e. a good choice can be:
+ (delete-dups
+ (mapcar (lambda (kw) (aref (cdr kw) (1- (length (cdr kw)))))
+ smie-closer-alist))")
+
+(defcustom smie-blink-matching-inners t
+ "Whether SMIE should blink to matching opener for inner keywords.
+If non-nil, it will blink not only for \"begin..end\" but also for \"if...else\"."
+ :type 'boolean)
+
+(defun smie-blink-matching-check (start end)
+ (save-excursion
+ (goto-char end)
+ (let ((ender (funcall smie-backward-token-function)))
+ (cond
+ ((not (and ender (rassoc ender smie-closer-alist)))
+ ;; This not is one of the begin..end we know how to check.
+ (blink-matching-check-mismatch start end))
+ ((not start) t)
+ (t
+ (goto-char start)
+ (let ((starter (funcall smie-forward-token-function)))
+ (not (member (cons starter ender) smie-closer-alist))))))))
+
+(defun smie-blink-matching-open ()
+ "Blink the matching opener when applicable.
+This uses SMIE's tables and is expected to be placed on `post-self-insert-hook'."
+ (when (and blink-matching-paren
+ smie-closer-alist ; Optimization.
+ (eq (char-before) last-command-event) ; Sanity check.
+ (memq last-command-event smie-blink-matching-triggers)
+ (save-excursion
+ ;; FIXME: Here we assume that closers all end
+ ;; with a word-syntax char.
+ (unless (eq ?\w (char-syntax last-command-event))
+ (forward-char -1))
+ (and (looking-at "\\>")
+ (not (nth 8 (syntax-ppss))))))
+ (save-excursion
+ (let ((pos (point))
+ (token (funcall smie-backward-token-function)))
+ (if (= 1 (length token))
+ ;; The trigger char is itself a token but is not
+ ;; one of the closers (e.g. ?\; in Octave mode),
+ ;; so go back to the previous token
+ (setq token (save-excursion
+ (funcall smie-backward-token-function)))
+ (goto-char pos))
+ ;; Here we assume that smie-backward-token-function
+ ;; returns a token that is a string and whose content
+ ;; match the buffer's representation of this token.
+ (when (and (> (length token) 1) (stringp token)
+ (memq (aref token (1- (length token)))
+ smie-blink-matching-triggers)
+ (not (eq (aref token (1- (length token)))
+ last-command-event)))
+ ;; Token ends with a trigger char, so don't blink for
+ ;; anything else than this trigger char, lest we'd blink
+ ;; both when inserting the trigger char and when inserting a
+ ;; subsequent SPC.
+ (setq token nil))
+ (when (and (rassoc token smie-closer-alist)
+ (or smie-blink-matching-inners
+ (null (nth 2 (assoc token smie-op-levels)))))
+ ;; The major mode might set blink-matching-check-function
+ ;; buffer-locally so that interactive calls to
+ ;; blink-matching-open work right, but let's not presume
+ ;; that's the case.
+ (let ((blink-matching-check-function #'smie-blink-matching-check))
+ (blink-matching-open)))))))
;;; The indentation engine.
@@ -505,24 +790,36 @@ Possible return values:
"Rules of the following form.
\((:before . TOK) . OFFSET-RULES) how to indent TOK itself.
\(TOK . OFFSET-RULES) how to indent right after TOK.
-\((T1 . T2) . OFFSET) how to indent token T2 w.r.t T1.
-\((t . TOK) . OFFSET) how to indent TOK with respect to its parent.
\(list-intro . TOKENS) declare TOKENS as being followed by what may look like
a funcall but is just a sequence of expressions.
\(t . OFFSET) basic indentation step.
\(args . OFFSET) indentation of arguments.
+\((T1 . T2) OFFSET) like ((:before . T2) (:parent T1 OFFSET)).
OFFSET-RULES is a list of elements which can each either be:
\(:hanging . OFFSET-RULES) if TOK is hanging, use OFFSET-RULES.
\(:parent PARENT . OFFSET-RULES) if TOK's parent is PARENT, use OFFSET-RULES.
\(:next TOKEN . OFFSET-RULES) if TOK is followed by TOKEN, use OFFSET-RULES.
-\(:prev TOKEN . OFFSET-RULES) if TOK is preceded by TOKEN, use OFFSET-RULES.
-a number the offset to use.
+\(:prev TOKEN . OFFSET-RULES) if TOK is preceded by TOKEN, use
+\(:bolp . OFFSET-RULES) If TOK is first on a line, use OFFSET-RULES.
+OFFSET the offset to use.
+
+PARENT can be either the name of the parent or a list of such names.
+
+OFFSET can be of the form:
`point' align with the token.
`parent' align with the parent.
+NUMBER offset by NUMBER.
+\(+ OFFSETS...) use the sum of OFFSETS.
+VARIABLE use the value of VARIABLE as offset.
+
+The precise meaning of `point' depends on various details: it can
+either mean the position of the token we're indenting, or the
+position of its parent, or the position right after its parent.
-A nil offset for indentation after a token defaults to `smie-indent-basic'.")
+A nil offset for indentation after an opening token defaults
+to `smie-indent-basic'.")
(defun smie-indent-hanging-p ()
;; A hanging keyword is one that's at the end of a line except it's not at
@@ -543,21 +840,33 @@ A nil offset for indentation after a token defaults to `smie-indent-basic'.")
(cdr (assq t smie-indent-rules))
smie-indent-basic))
-(defun smie-indent-offset-rule (tokinfo &optional after)
+(defvar smie-indent-debug-log)
+
+(defun smie-indent-offset-rule (tokinfo &optional after parent)
"Apply the OFFSET-RULES in TOKINFO.
Point is expected to be right in front of the token corresponding to TOKINFO.
If computing the indentation after the token, then AFTER is the position
-after the token."
+after the token, otherwise it should be nil.
+PARENT if non-nil should be the parent info returned by `smie-backward-sexp'."
(let ((rules (cdr tokinfo))
- parent next prev
+ next prev
offset)
(while (consp rules)
(let ((rule (pop rules)))
(cond
((not (consp rule)) (setq offset rule))
+ ((eq (car rule) '+) (setq offset rule))
((eq (car rule) :hanging)
(when (smie-indent-hanging-p)
(setq rules (cdr rule))))
+ ((eq (car rule) :bolp)
+ (when (smie-bolp)
+ (setq rules (cdr rule))))
+ ((eq (car rule) :eolp)
+ (unless after
+ (error "Can't use :eolp in :before indentation rules"))
+ (when (> after (line-end-position))
+ (setq rules (cdr rule))))
((eq (car rule) :prev)
(unless prev
(save-excursion
@@ -578,12 +887,63 @@ after the token."
(save-excursion
(if after (goto-char after))
(setq parent (smie-backward-sexp 'halfsexp))))
- (when (equal (nth 2 parent) (cadr rule))
+ (when (if (listp (cadr rule))
+ (member (nth 2 parent) (cadr rule))
+ (equal (nth 2 parent) (cadr rule)))
(setq rules (cddr rule))))
(t (error "Unknown rule %s for indentation of %s"
rule (car tokinfo))))))
+ ;; If `offset' is not set yet, use `rules' to handle the case where
+ ;; the tokinfo uses the old-style ((PARENT . TOK). OFFSET).
+ (unless offset (setq offset rules))
+ (when (boundp 'smie-indent-debug-log)
+ (push (list (point) offset tokinfo) smie-indent-debug-log))
offset))
+(defun smie-indent-column (offset &optional base parent virtual-point)
+ "Compute the actual column to use for a given OFFSET.
+BASE is the base position to use, and PARENT is the parent info, if any.
+If VIRTUAL-POINT is non-nil, then `point' is virtual."
+ (cond
+ ((eq (car-safe offset) '+)
+ (apply '+ (mapcar (lambda (offset) (smie-indent-column offset nil parent))
+ (cdr offset))))
+ ((integerp offset)
+ (+ offset
+ (case base
+ ((nil) 0)
+ (parent (goto-char (cadr parent))
+ (smie-indent-virtual))
+ (t
+ (goto-char base)
+ ;; For indentation after "(let" in SML-mode, we end up accumulating
+ ;; the offset of "(" and the offset of "let", so we use `min' to try
+ ;; and get it right either way.
+ (min (smie-indent-virtual) (current-column))))))
+ ((eq offset 'point)
+ ;; In indent-keyword, if we're indenting `then' wrt `if', we want to use
+ ;; indent-virtual rather than use just current-column, so that we can
+ ;; apply the (:before . "if") rule which does the "else if" dance in SML.
+ ;; But in other cases, we do not want to use indent-virtual
+ ;; (e.g. indentation of "*" w.r.t "+", or ";" wrt "("). We could just
+ ;; always use indent-virtual and then have indent-rules say explicitly
+ ;; to use `point' after things like "(" or "+" when they're not at EOL,
+ ;; but you'd end up with lots of those rules.
+ ;; So we use a heuristic here, which is that we only use virtual if
+ ;; the parent is tightly linked to the child token (they're part of
+ ;; the same BNF rule).
+ (if (and virtual-point (null (car parent))) ;Black magic :-(
+ (smie-indent-virtual) (current-column)))
+ ((eq offset 'parent)
+ (unless parent
+ (setq parent (or (smie-backward-sexp 'halfsexp) :notfound)))
+ (if (consp parent) (goto-char (cadr parent)))
+ (smie-indent-virtual))
+ ((eq offset nil) nil)
+ ((and (symbolp offset) (boundp 'offset))
+ (smie-indent-column (symbol-value offset) base parent virtual-point))
+ (t (error "Unknown indentation offset %s" offset))))
+
(defun smie-indent-forward-token ()
"Skip token forward and return it, along with its levels."
(let ((tok (funcall smie-forward-token-function)))
@@ -620,13 +980,13 @@ in order to figure out the indentation of some other (further down) point."
;; Obey the `fixindent' special comment.
(and (smie-bolp)
(save-excursion
- (comment-normalize-vars)
- (re-search-forward (concat comment-start-skip
- "fixindent"
- comment-end-skip)
- ;; 1+ to account for the \n comment termination.
- (1+ (line-end-position)) t))
- (current-column)))
+ (comment-normalize-vars)
+ (re-search-forward (concat comment-start-skip
+ "fixindent"
+ comment-end-skip)
+ ;; 1+ to account for the \n comment termination.
+ (1+ (line-end-position)) t))
+ (current-column)))
(defun smie-indent-bob ()
;; Start the file at column 0.
@@ -655,85 +1015,130 @@ in order to figure out the indentation of some other (further down) point."
(toklevels (smie-indent-forward-token))
(token (pop toklevels)))
(if (null (car toklevels))
- ;; Different case:
- ;; - smie-bolp: "indent according to others".
- ;; - common hanging: "indent according to others".
- ;; - SML-let hanging: "indent like parent".
- ;; - if-after-else: "indent-like parent".
- ;; - middle-of-line: "trust current position".
- (cond
- ((null (cdr toklevels)) nil) ;Not a keyword.
- ((smie-bolp)
- ;; For an open-paren-like thingy at BOL, always indent only
- ;; based on other rules (typically smie-indent-after-keyword).
- nil)
- (t
- (let* ((tokinfo (or (assoc (cons :before token) smie-indent-rules)
- ;; By default use point unless we're hanging.
- (cons (cons :before token)
- '((:hanging nil) point))))
- (after (prog1 (point) (goto-char pos)))
- (offset (smie-indent-offset-rule tokinfo)))
- (cond
- ((eq offset 'point) (current-column))
- ((eq offset 'parent)
- (let ((parent (smie-backward-sexp 'halfsexp)))
- (if parent (goto-char (cadr parent))))
- (smie-indent-virtual))
- ((eq offset nil) nil)
- (t (error "Unhandled offset %s in %s"
- offset (cons :before token)))))))
+ (save-excursion
+ (goto-char pos)
+ ;; Different cases:
+ ;; - smie-bolp: "indent according to others".
+ ;; - common hanging: "indent according to others".
+ ;; - SML-let hanging: "indent like parent".
+ ;; - if-after-else: "indent-like parent".
+ ;; - middle-of-line: "trust current position".
+ (cond
+ ((null (cdr toklevels)) nil) ;Not a keyword.
+ ((smie-bolp)
+ ;; For an open-paren-like thingy at BOL, always indent only
+ ;; based on other rules (typically smie-indent-after-keyword).
+ nil)
+ (t
+ ;; We're only ever here for virtual-indent, which is why
+ ;; we can use (current-column) as answer for `point'.
+ (let* ((tokinfo (or (assoc (cons :before token)
+ smie-indent-rules)
+ ;; By default use point unless we're hanging.
+ `((:before . ,token) (:hanging nil) point)))
+ ;; (after (prog1 (point) (goto-char pos)))
+ (offset (smie-indent-offset-rule tokinfo)))
+ (smie-indent-column offset)))))
;; FIXME: This still looks too much like black magic!!
;; FIXME: Rather than a bunch of rules like (PARENT . TOKEN), we
;; want a single rule for TOKEN with different cases for each PARENT.
- (let ((res (smie-backward-sexp 'halfsexp)) tmp)
+ (let* ((parent (smie-backward-sexp 'halfsexp))
+ (tokinfo
+ (or (assoc (cons (caddr parent) token)
+ smie-indent-rules)
+ (assoc (cons :before token) smie-indent-rules)
+ ;; Default rule.
+ `((:before . ,token)
+ ;; (:parent open 0)
+ point)))
+ (offset (save-excursion
+ (goto-char pos)
+ (smie-indent-offset-rule tokinfo nil parent))))
+ ;; Different behaviors:
+ ;; - align with parent.
+ ;; - parent + offset.
+ ;; - after parent's column + offset (actually, after or before
+ ;; depending on where backward-sexp stopped).
+ ;; ? let it drop to some other indentation function (almost never).
+ ;; ? parent + offset + parent's own offset.
+ ;; Different cases:
+ ;; - bump into a same-level operator.
+ ;; - bump into a specific known parent.
+ ;; - find a matching open-paren thingy.
+ ;; - bump into some random parent.
+ ;; ? borderline case (almost never).
+ ;; ? bump immediately into a parent.
(cond
((not (or (< (point) pos)
- (and (cadr res) (< (cadr res) pos))))
+ (and (cadr parent) (< (cadr parent) pos))))
;; If we didn't move at all, that means we didn't really skip
- ;; what we wanted.
+ ;; what we wanted. Should almost never happen, other than
+ ;; maybe when an infix or close-paren is at the beginning
+ ;; of a buffer.
nil)
- ((eq (car res) (car toklevels))
+ ((eq (car parent) (car toklevels))
;; We bumped into a same-level operator. align with it.
- (goto-char (cadr res))
- ;; Don't use (smie-indent-virtual :not-hanging) here, because we
- ;; want to jump back over a sequence of same-level ops such as
- ;; a -> b -> c
- ;; -> d
- ;; So as to align with the earliest appropriate place.
- (smie-indent-virtual))
- ((setq tmp (assoc (cons (caddr res) token)
- smie-indent-rules))
- (goto-char (cadr res))
- (+ (cdr tmp) (smie-indent-virtual))) ;:not-hanging
- ;; FIXME: The rules ((t . TOK) . OFFSET) either indent
- ;; relative to "before the parent" or "after the parent",
- ;; depending on details of the grammar.
- ((null (car res))
- (assert (eq (point) (cadr res)))
- (goto-char (cadr res))
- (+ (or (cdr (assoc (cons t token) smie-indent-rules)) 0)
- (smie-indent-virtual))) ;:not-hanging
- ((and (= (point) pos) (smie-bolp))
- ;; Since we started at BOL, we're not computing a virtual
- ;; indentation, and we're still at the starting point, so the
- ;; next (default) rule can't be used since it uses `current-column'
- ;; which would cause. indentation to depend on itself.
- ;; We could just return nil, but OTOH that's not good enough in
- ;; some cases. Instead, we want to combine the offset-rules for
- ;; the current token with the offset-rules of the previous one.
- (+ (or (cdr (assoc (cons t token) smie-indent-rules)) 0)
- ;; FIXME: This is odd. Can't we make it use
- ;; smie-indent-(calculate|virtual) somehow?
- (smie-indent-after-keyword)))
- (t
- (+ (or (cdr (assoc (cons t token) smie-indent-rules)) 0)
- (current-column)))))))))
+ (if (and (smie-bolp) (/= (point) pos)
+ (save-excursion
+ (goto-char (goto-char (cadr parent)))
+ (not (smie-bolp)))
+ ;; Check the offset of `token' rather then its parent
+ ;; because its parent may have used a special rule. E.g.
+ ;; function foo;
+ ;; line2;
+ ;; line3;
+ ;; The ; on the first line had a special rule, but when
+ ;; indenting line3, we don't care about it and want to
+ ;; align with line2.
+ (memq offset '(point nil)))
+ ;; If the parent is at EOL and its children are indented like
+ ;; itself, then we can just obey the indentation chosen for the
+ ;; child.
+ ;; This is important for operators like ";" which
+ ;; are usually at EOL (and have an offset of 0): otherwise we'd
+ ;; always go back over all the statements, which is
+ ;; a performance problem and would also mean that fixindents
+ ;; in the middle of such a sequence would be ignored.
+ ;;
+ ;; This is a delicate point!
+ ;; Even if the offset is not 0, we could follow the same logic
+ ;; and subtract the offset from the child's indentation.
+ ;; But that would more often be a bad idea: OT1H we generally
+ ;; want to reuse the closest similar indentation point, so that
+ ;; the user's choice (or the fixindents) are obeyed. But OTOH
+ ;; we don't want this to affect "unrelated" parts of the code.
+ ;; E.g. a fixindent in the body of a "begin..end" should not
+ ;; affect the indentation of the "end".
+ (current-column)
+ (goto-char (cadr parent))
+ ;; Don't use (smie-indent-virtual :not-hanging) here, because we
+ ;; want to jump back over a sequence of same-level ops such as
+ ;; a -> b -> c
+ ;; -> d
+ ;; So as to align with the earliest appropriate place.
+ (smie-indent-virtual)))
+ (tokinfo
+ (if (and (= (point) pos) (smie-bolp)
+ (or (eq offset 'point)
+ (and (consp offset) (memq 'point offset))))
+ ;; Since we started at BOL, we're not computing a virtual
+ ;; indentation, and we're still at the starting point, so
+ ;; we can't use `current-column' which would cause
+ ;; indentation to depend on itself.
+ nil
+ (smie-indent-column offset 'parent parent
+ ;; If we're still at pos, indent-virtual
+ ;; will inf-loop.
+ (unless (= (point) pos) 'virtual))))))))))
(defun smie-indent-comment ()
- ;; Indentation of a comment.
- (and (looking-at comment-start-skip)
+ "Compute indentation of a comment."
+ ;; Don't do it for virtual indentations. We should normally never be "in
+ ;; front of a comment" when doing virtual-indentation anyway. And if we are
+ ;; (as can happen in octave-mode), moving forward can lead to inf-loops.
+ (and (smie-bolp)
+ (looking-at comment-start-skip)
(save-excursion
(forward-comment (point-max))
(skip-chars-forward " \t\r\n")
@@ -745,12 +1150,12 @@ in order to figure out the indentation of some other (further down) point."
(comment-string-strip comment-continue t t))))
(and (< 0 (length continue))
(looking-at (regexp-quote continue)) (nth 4 (syntax-ppss))
- (let ((ppss (syntax-ppss)))
- (save-excursion
- (forward-line -1)
- (if (<= (point) (nth 8 ppss))
- (progn (goto-char (1+ (nth 8 ppss))) (current-column))
- (skip-chars-forward " \t")
+ (let ((ppss (syntax-ppss)))
+ (save-excursion
+ (forward-line -1)
+ (if (<= (point) (nth 8 ppss))
+ (progn (goto-char (1+ (nth 8 ppss))) (current-column))
+ (skip-chars-forward " \t")
(if (looking-at (regexp-quote continue))
(current-column))))))))
@@ -761,26 +1166,25 @@ in order to figure out the indentation of some other (further down) point."
(toklevel (smie-indent-backward-token))
(tok (car toklevel))
(tokinfo (assoc tok smie-indent-rules)))
+ ;; Set some default indent rules.
(if (and toklevel (null (cadr toklevel)) (null tokinfo))
(setq tokinfo (list (car toklevel))))
;; (if (and tokinfo (null toklevel))
;; (error "Token %S has indent rule but has no parsing info" tok))
(when toklevel
+ (unless tokinfo
+ ;; The default indentation after a keyword/operator is 0 for
+ ;; infix and t for prefix.
+ ;; Using the BNF syntax, we could come up with better
+ ;; defaults, but we only have the precedence levels here.
+ (setq tokinfo (list tok 'default-rule
+ (if (cadr toklevel) 0 (smie-indent-offset t)))))
(let ((offset
- (cond
- (tokinfo (or (smie-indent-offset-rule tokinfo pos)
- (smie-indent-offset t)))
- ;; The default indentation after a keyword/operator
- ;; is 0 for infix and t for prefix.
- ;; Using the BNF syntax, we could come up with
- ;; better defaults, but we only have the
- ;; precedence levels here.
- ((null (cadr toklevel)) (smie-indent-offset t))
- (t 0))))
- ;; For indentation after "(let" in SML-mode, we end up accumulating
- ;; the offset of "(" and the offset of "let", so we use `min' to try
- ;; and get it right either way.
- (+ (min (smie-indent-virtual) (current-column)) offset))))))
+ (or (smie-indent-offset-rule tokinfo pos)
+ (smie-indent-offset t))))
+ (let ((before (point)))
+ (goto-char pos)
+ (smie-indent-column offset before)))))))
(defun smie-indent-exps ()
;; Indentation of sequences of simple expressions without
@@ -828,6 +1232,7 @@ in order to figure out the indentation of some other (further down) point."
(positions
;; We're the first arg.
(goto-char (car positions))
+ ;; FIXME: Use smie-indent-column.
(+ (smie-indent-offset 'args)
;; We used to use (smie-indent-virtual), but that
;; doesn't seem right since it might then indent args less than
@@ -836,8 +1241,8 @@ in order to figure out the indentation of some other (further down) point."
(defvar smie-indent-functions
'(smie-indent-fixindent smie-indent-bob smie-indent-close smie-indent-comment
- smie-indent-comment-continue smie-indent-keyword smie-indent-after-keyword
- smie-indent-exps)
+ smie-indent-comment-continue smie-indent-keyword smie-indent-after-keyword
+ smie-indent-exps)
"Functions to compute the indentation.
Each function is called with no argument, shouldn't move point, and should
return either nil if it has no opinion, or an integer representing the column
@@ -851,7 +1256,7 @@ to which that point should be aligned, if we were to reindent it.")
"Indent current line using the SMIE indentation engine."
(interactive)
(let* ((savep (point))
- (indent (condition-case nil
+ (indent (condition-case-no-debug nil
(save-excursion
(forward-line 0)
(skip-chars-forward " \t")
@@ -866,7 +1271,14 @@ to which that point should be aligned, if we were to reindent it.")
(save-excursion (indent-line-to indent))
(indent-line-to indent)))))
-;;;###autoload
+(defun smie-indent-debug ()
+ "Show the rules used to compute indentation of current line."
+ (interactive)
+ (let ((smie-indent-debug-log '()))
+ (smie-indent-calculate)
+ ;; FIXME: please improve!
+ (message "%S" smie-indent-debug-log)))
+
(defun smie-setup (op-levels indent-rules)
(set (make-local-variable 'smie-indent-rules) indent-rules)
(set (make-local-variable 'smie-op-levels) op-levels)
diff --git a/lisp/emacs-lisp/syntax.el b/lisp/emacs-lisp/syntax.el
index 5cc89596ef5..ad0166e7af0 100644
--- a/lisp/emacs-lisp/syntax.el
+++ b/lisp/emacs-lisp/syntax.el
@@ -34,7 +34,6 @@
;; - do something about the case where the syntax-table is changed.
;; This typically happens with tex-mode and its `$' operator.
-;; - move font-lock-syntactic-keywords in here. Then again, maybe not.
;; - new functions `syntax-state', ... to replace uses of parse-partial-state
;; with something higher-level (similar to syntax-ppss-context).
;; - interaction with mmm-mode.
@@ -47,6 +46,249 @@
(defvar font-lock-beginning-of-syntax-function)
+;;; Applying syntax-table properties where needed.
+
+(defvar syntax-propertize-function nil
+ ;; Rather than a -functions hook, this is a -function because it's easier
+ ;; to do a single scan than several scans: with multiple scans, one cannot
+ ;; assume that the text before point has been propertized, so syntax-ppss
+ ;; gives unreliable results (and stores them in its cache to boot, so we'd
+ ;; have to flush that cache between each function, and we couldn't use
+ ;; syntax-ppss-flush-cache since that would not only flush the cache but also
+ ;; reset syntax-propertize--done which should not be done in this case).
+ "Mode-specific function to apply the syntax-table properties.
+Called with 2 arguments: START and END.")
+
+(defvar syntax-propertize-chunk-size 500)
+
+(defvar syntax-propertize-extend-region-functions
+ '(syntax-propertize-wholelines)
+ "Special hook run just before proceeding to propertize a region.
+This is used to allow major modes to help `syntax-propertize' find safe buffer
+positions as beginning and end of the propertized region. Its most common use
+is to solve the problem of /identification/ of multiline elements by providing
+a function that tries to find such elements and move the boundaries such that
+they do not fall in the middle of one.
+Each function is called with two arguments (START and END) and it should return
+either a cons (NEW-START . NEW-END) or nil if no adjustment should be made.
+These functions are run in turn repeatedly until they all return nil.
+Put first the functions more likely to cause a change and cheaper to compute.")
+;; Mark it as a special hook which doesn't use any global setting
+;; (i.e. doesn't obey the element t in the buffer-local value).
+(make-variable-buffer-local 'syntax-propertize-extend-region-functions)
+
+(defun syntax-propertize-wholelines (start end)
+ (goto-char start)
+ (cons (line-beginning-position)
+ (progn (goto-char end)
+ (if (bolp) (point) (line-beginning-position 2)))))
+
+(defun syntax-propertize-multiline (beg end)
+ "Let `syntax-propertize' pay attention to the syntax-multiline property."
+ (when (and (> beg (point-min))
+ (get-text-property (1- beg) 'syntax-multiline))
+ (setq beg (or (previous-single-property-change beg 'syntax-multiline)
+ (point-min))))
+ ;;
+ (when (get-text-property end 'font-lock-multiline)
+ (setq end (or (text-property-any end (point-max)
+ 'syntax-multiline nil)
+ (point-max))))
+ (cons beg end))
+
+(defvar syntax-propertize--done -1
+ "Position upto which syntax-table properties have been set.")
+(make-variable-buffer-local 'syntax-propertize--done)
+
+(defun syntax-propertize--shift-groups (re n)
+ (replace-regexp-in-string
+ "\\\\(\\?\\([0-9]+\\):"
+ (lambda (s)
+ (replace-match
+ (number-to-string (+ n (string-to-number (match-string 1 s))))
+ t t s 1))
+ re t t))
+
+(defmacro syntax-propertize-rules (&rest rules)
+ "Make a function that applies RULES for use in `syntax-propertize-function'.
+The function will scan the buffer, applying the rules where they match.
+The buffer is scanned a single time, like \"lex\" would, rather than once
+per rule.
+
+Each rule has the form (REGEXP HIGHLIGHT1 ... HIGHLIGHTn), where REGEXP
+is an expression (evaluated at time of macro-expansion) that returns a regexp,
+and where HIGHLIGHTs have the form (NUMBER SYNTAX) which means to
+apply the property SYNTAX to the chars matched by the subgroup NUMBER
+of the regular expression, if NUMBER did match.
+SYNTAX is an expression that returns a value to apply as `syntax-table'
+property. Some expressions are handled specially:
+- if SYNTAX is a string, then it is converted with `string-to-syntax';
+- if SYNTAX has the form (prog1 EXP . EXPS) then the value returned by EXP
+ will be applied to the buffer before running EXPS and if EXP is a string it
+ is also converted with `string-to-syntax'.
+The SYNTAX expression is responsible to save the `match-data' if needed
+for subsequent HIGHLIGHTs.
+Also SYNTAX is free to move point, in which case RULES may not be applied to
+some parts of the text or may be applied several times to other parts.
+
+Note: back-references in REGEXPs do not work."
+ (declare (debug (&rest (form &rest
+ (numberp
+ [&or stringp
+ ("prog1" [&or stringp def-form] def-body)
+ def-form])))))
+ (let* ((offset 0)
+ (branches '())
+ ;; We'd like to use a real DFA-based lexer, usually, but since Emacs
+ ;; doesn't have one yet, we fallback on building one large regexp
+ ;; and use groups to determine which branch of the regexp matched.
+ (re
+ (mapconcat
+ (lambda (rule)
+ (let ((re (eval (car rule))))
+ (when (and (assq 0 rule) (cdr rules))
+ ;; If there's more than 1 rule, and the rule want to apply
+ ;; highlight to match 0, create an extra group to be able to
+ ;; tell when *this* match 0 has succeeded.
+ (incf offset)
+ (setq re (concat "\\(" re "\\)")))
+ (setq re (syntax-propertize--shift-groups re offset))
+ (let ((code '())
+ (condition
+ (cond
+ ((assq 0 rule) (if (zerop offset) t
+ `(match-beginning ,offset)))
+ ((null (cddr rule))
+ `(match-beginning ,(+ offset (car (cadr rule)))))
+ (t
+ `(or ,@(mapcar
+ (lambda (case)
+ `(match-beginning ,(+ offset (car case))))
+ (cdr rule))))))
+ (nocode t)
+ (offset offset))
+ ;; If some of the subgroup rules include Elisp code, then we
+ ;; need to set the match-data so it's consistent with what the
+ ;; code expects. If not, then we can simply use shifted
+ ;; offset in our own code.
+ (unless (zerop offset)
+ (dolist (case (cdr rule))
+ (unless (stringp (cadr case))
+ (setq nocode nil)))
+ (unless nocode
+ (push `(let ((md (match-data 'ints)))
+ ;; Keep match 0 as is, but shift everything else.
+ (setcdr (cdr md) (nthcdr ,(* (1+ offset) 2) md))
+ (set-match-data md))
+ code)
+ (setq offset 0)))
+ ;; Now construct the code for each subgroup rules.
+ (dolist (case (cdr rule))
+ (assert (null (cddr case)))
+ (let* ((gn (+ offset (car case)))
+ (action (nth 1 case))
+ (thiscode
+ (cond
+ ((stringp action)
+ `((put-text-property
+ (match-beginning ,gn) (match-end ,gn)
+ 'syntax-table
+ ',(string-to-syntax action))))
+ ((eq (car-safe action) 'ignore)
+ (cdr action))
+ ((eq (car-safe action) 'prog1)
+ (if (stringp (nth 1 action))
+ `((put-text-property
+ (match-beginning ,gn) (match-end ,gn)
+ 'syntax-table
+ ',(string-to-syntax (nth 1 action)))
+ ,@(nthcdr 2 action))
+ `((let ((mb (match-beginning ,gn))
+ (me (match-end ,gn))
+ (syntax ,(nth 1 action)))
+ (if syntax
+ (put-text-property
+ mb me 'syntax-table syntax))
+ ,@(nthcdr 2 action)))))
+ (t
+ `((let ((mb (match-beginning ,gn))
+ (me (match-end ,gn))
+ (syntax ,action))
+ (if syntax
+ (put-text-property
+ mb me 'syntax-table syntax))))))))
+
+ (if (or (not (cddr rule)) (zerop gn))
+ (setq code (nconc (nreverse thiscode) code))
+ (push `(if (match-beginning ,gn)
+ ;; Try and generate clean code with no
+ ;; extraneous progn.
+ ,(if (null (cdr thiscode))
+ (car thiscode)
+ `(progn ,@thiscode)))
+ code))))
+ (push (cons condition (nreverse code))
+ branches))
+ (incf offset (regexp-opt-depth re))
+ re))
+ rules
+ "\\|")))
+ `(lambda (start end)
+ (goto-char start)
+ (while (and (< (point) end)
+ (re-search-forward ,re end t))
+ (cond ,@(nreverse branches))))))
+
+(defun syntax-propertize-via-font-lock (keywords)
+ "Propertize for syntax in START..END using font-lock syntax.
+KEYWORDS obeys the format used in `font-lock-syntactic-keywords'.
+The return value is a function suitable for `syntax-propertize-function'."
+ (lexical-let ((keywords keywords))
+ (lambda (start end)
+ (with-no-warnings
+ (let ((font-lock-syntactic-keywords keywords))
+ (font-lock-fontify-syntactic-keywords-region start end)
+ ;; In case it was eval'd/compiled.
+ (setq keywords font-lock-syntactic-keywords))))))
+
+(defun syntax-propertize (pos)
+ "Ensure that syntax-table properties are set upto POS."
+ (when (and syntax-propertize-function
+ (< syntax-propertize--done pos))
+ ;; (message "Needs to syntax-propertize from %s to %s"
+ ;; syntax-propertize--done pos)
+ (set (make-local-variable 'parse-sexp-lookup-properties) t)
+ (save-excursion
+ (with-silent-modifications
+ (let* ((start (max syntax-propertize--done (point-min)))
+ (end (max pos
+ (min (point-max)
+ (+ start syntax-propertize-chunk-size))))
+ (funs syntax-propertize-extend-region-functions))
+ (while funs
+ (let ((new (funcall (pop funs) start end)))
+ (if (or (null new)
+ (and (>= (car new) start) (<= (cdr new) end)))
+ nil
+ (setq start (car new))
+ (setq end (cdr new))
+ ;; If there's been a change, we should go through the
+ ;; list again since this new position may
+ ;; warrant a different answer from one of the funs we've
+ ;; already seen.
+ (unless (eq funs
+ (cdr syntax-propertize-extend-region-functions))
+ (setq funs syntax-propertize-extend-region-functions)))))
+ ;; Move the limit before calling the function, so the function
+ ;; can use syntax-ppss.
+ (setq syntax-propertize--done end)
+ ;; (message "syntax-propertizing from %s to %s" start end)
+ (remove-text-properties start end
+ '(syntax-table nil syntax-multiline nil))
+ (funcall syntax-propertize-function start end))))))
+
+;;; Incrementally compute and memoize parser state.
+
(defsubst syntax-ppss-depth (ppss)
(nth 0 ppss))
@@ -92,6 +334,8 @@ point (where the PPSS is equivalent to nil).")
(defalias 'syntax-ppss-after-change-function 'syntax-ppss-flush-cache)
(defun syntax-ppss-flush-cache (beg &rest ignored)
"Flush the cache of `syntax-ppss' starting at position BEG."
+ ;; Set syntax-propertize to refontify anything past beg.
+ (setq syntax-propertize--done (min beg syntax-propertize--done))
;; Flush invalid cache entries.
(while (and syntax-ppss-cache (> (caar syntax-ppss-cache) beg))
(setq syntax-ppss-cache (cdr syntax-ppss-cache)))
@@ -128,6 +372,7 @@ the 2nd and 6th values of the returned state cannot be relied upon.
Point is at POS when this function returns."
;; Default values.
(unless pos (setq pos (point)))
+ (syntax-propertize pos)
;;
(let ((old-ppss (cdr syntax-ppss-last))
(old-pos (car syntax-ppss-last))
@@ -209,7 +454,8 @@ Point is at POS when this function returns."
(funcall syntax-begin-function)
;; Make sure it's better.
(> (point) pt-best))
- ;; Simple sanity check.
+ ;; Simple sanity checks.
+ (< (point) pos) ; backward-paragraph can fail here.
(not (memq (get-text-property (point) 'face)
'(font-lock-string-face font-lock-doc-face
font-lock-comment-face))))
diff --git a/lisp/emacs-lisp/tcover-ses.el b/lisp/emacs-lisp/tcover-ses.el
index cf5e79d2a26..8df70f4d979 100644
--- a/lisp/emacs-lisp/tcover-ses.el
+++ b/lisp/emacs-lisp/tcover-ses.el
@@ -6,6 +6,7 @@
;; Author: Jonathan Yavner <jyavner@engineer.com>
;; Maintainer: Jonathan Yavner <jyavner@engineer.com>
;; Keywords: spreadsheet lisp utility
+;; Package: testcover
;; GNU Emacs is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
diff --git a/lisp/emacs-lisp/tcover-unsafep.el b/lisp/emacs-lisp/tcover-unsafep.el
index b300ee6dcef..47f931bf9d3 100644
--- a/lisp/emacs-lisp/tcover-unsafep.el
+++ b/lisp/emacs-lisp/tcover-unsafep.el
@@ -5,6 +5,7 @@
;; Author: Jonathan Yavner <jyavner@engineer.com>
;; Maintainer: Jonathan Yavner <jyavner@engineer.com>
;; Keywords: safety lisp utility
+;; Package: testcover
;; This file is part of GNU Emacs.
diff --git a/lisp/emacs-lisp/timer.el b/lisp/emacs-lisp/timer.el
index 94f39940b66..6ae6a86857e 100644
--- a/lisp/emacs-lisp/timer.el
+++ b/lisp/emacs-lisp/timer.el
@@ -4,6 +4,7 @@
;; 2009, 2010 Free Software Foundation, Inc.
;; Maintainer: FSF
+;; Package: emacs
;; This file is part of GNU Emacs.
@@ -442,8 +443,6 @@ This function returns a timer object which you can use in `cancel-timer'."
"This is the timer function used for the timer made by `with-timeout'."
(throw tag 'timeout))
-(put 'with-timeout 'lisp-indent-function 1)
-
(defvar with-timeout-timers nil
"List of all timers used by currently pending `with-timeout' calls.")
@@ -455,6 +454,7 @@ event (such as keyboard input, input from subprocesses, or a certain time);
if the program loops without waiting in any way, the timeout will not
be detected.
\n(fn (SECONDS TIMEOUT-FORMS...) BODY)"
+ (declare (indent 1))
(let ((seconds (car list))
(timeout-forms (cdr list)))
`(let ((with-timeout-tag (cons nil nil))