Make autoloads populate a new definition-prefixes table

* lisp/subr.el (definition-prefixes): New hash table.
(register-definition-prefixes): New function.

* lisp/emacs-lisp/autoload.el (autoload-compute-prefixes): New var.
(autoload--split-prefixes-1, autoload--split-prefixes)
(autoload--make-defs-autoload): New functions.
(autoload-defs-autoload-max-size, autoload-popular-prefixes): New vars.
(autoload-generate-file-autoloads): Obey autoload-compute-prefixes.
(update-directory-autoloads): Don't touch loaddefs.el if the set of
autoloads hasn't changed (i.e. if only the timestamp would change).

* lisp/loadup.el: Purify definition-prefixes.

* lisp/w32-fns.el: Keep name space clean.
(w32-set-default-process-coding-system): Rename from
set-default-process-coding-system.
(w32-set-system-coding-system): Rename from set-w32-system-coding-system.
This commit is contained in:
Stefan Monnier 2016-05-25 22:58:18 -04:00
parent 1ee91bf891
commit e971ce6de2
4 changed files with 204 additions and 18 deletions

View file

@ -183,10 +183,12 @@ expression, in which case we want to handle forms differently."
(args (pcase car
((or `defun `defmacro
`defun* `defmacro* `cl-defun `cl-defmacro
`define-overloadable-function) (nth 2 form))
`define-overloadable-function)
(nth 2 form))
(`define-skeleton '(&optional str arg))
((or `define-generic-mode `define-derived-mode
`define-compilation-mode) nil)
`define-compilation-mode)
nil)
(_ t)))
(body (nthcdr (or (function-get car 'doc-string-elt) 3) form))
(doc (if (stringp (car body)) (pop body))))
@ -202,7 +204,8 @@ expression, in which case we want to handle forms differently."
define-global-minor-mode
define-globalized-minor-mode
easy-mmode-define-minor-mode
define-minor-mode)) t)
define-minor-mode))
t)
(eq (car-safe (car body)) 'interactive))
,(if macrop ''macro nil))))
@ -313,7 +316,7 @@ The variable `autoload-print-form-outbuf' specifies the buffer to
put the output in."
(cond
;; If the form is a sequence, recurse.
((eq (car form) 'progn) (mapcar 'autoload-print-form (cdr form)))
((eq (car form) 'progn) (mapcar #'autoload-print-form (cdr form)))
;; Symbols at the toplevel are meaningless.
((symbolp form) nil)
(t
@ -413,6 +416,16 @@ make it writable."
(defun autoload-insert-section-header (outbuf autoloads load-name file time)
"Insert the section-header line,
which lists the file name and which functions are in it, etc."
;; (cl-assert ;Make sure we don't insert it in the middle of another section.
;; (save-excursion
;; (or (not (re-search-backward
;; (concat "\\("
;; (regexp-quote generate-autoload-section-header)
;; "\\)\\|\\("
;; (regexp-quote generate-autoload-section-trailer)
;; "\\)")
;; nil t))
;; (match-end 2))))
(insert generate-autoload-section-header)
(prin1 `(autoloads ,autoloads ,load-name ,file ,time)
outbuf)
@ -471,7 +484,7 @@ which lists the file name and which functions are in it, etc."
;; without checking its content. This makes it generate wrong load
;; names for cases like lisp/term which is not added to load-path.
(setq dir (expand-file-name (pop names) dir)))
(t (setq name (mapconcat 'identity names "/")))))
(t (setq name (mapconcat #'identity names "/")))))
(if (string-match "\\.elc?\\(\\.\\|\\'\\)" name)
(substring name 0 (match-beginning 0))
name)))
@ -487,8 +500,93 @@ Return non-nil in the case where no autoloads were added at point."
(let ((generated-autoload-file buffer-file-name))
(autoload-generate-file-autoloads file (current-buffer))))
(defvar print-readably)
(defun autoload--split-prefixes-1 (strs)
(let ((prefixes ()))
(dolist (str strs)
(string-match "\\`[^-:/_]*[-:/_]*" str)
(let* ((prefix (match-string 0 str))
(tail (substring str (match-end 0)))
(cell (assoc prefix prefixes)))
(cond
((null cell) (push (list prefix tail) prefixes))
((equal (cadr cell) tail) nil)
(t (setcdr cell (cons tail (cdr cell)))))))
prefixes))
(defun autoload--split-prefixes (prefixes)
(apply #'nconc
(mapcar (lambda (cell)
(let ((prefix (car cell)))
(mapcar (lambda (cell)
(cons (concat prefix (car cell)) (cdr cell)))
(autoload--split-prefixes-1 (cdr cell)))))
prefixes)))
(defvar autoload-compute-prefixes t
"If non-nil, autoload will add code to register the prefixes used in a file.
Standard prefixes won't be registered anyway. I.e. if a file \"foo.el\" defines
variables or functions that use \"foo-\" as prefix, that will not be registered.
But all other prefixes will be included.")
(defconst autoload-defs-autoload-max-size 5
"Target length of the list of definition prefixes per file.
If set too small, the prefixes will be too generic (i.e. they'll use little
memory, we'll end up looking in too many files when we need a particular
prefix), and if set too large, they will be too specific (i.e. they will
cost more memory use).")
(defvar autoload-popular-prefixes nil)
(defun autoload--make-defs-autoload (defs file)
;; Remove the defs that obey the rule that file foo.el (or
;; foo-mode.el) uses "foo-" as prefix.
;; FIXME: help--symbol-completion-table still doesn't know how to use
;; the rule that file foo.el (or foo-mode.el) uses "foo-" as prefix.
;;(let ((prefix
;; (concat (substring file 0 (string-match "-mode\\'" file)) "-")))
;; (dolist (def (prog1 defs (setq defs nil)))
;; (unless (string-prefix-p prefix def)
;; (push def defs))))
;; Then compute a small set of prefixes that cover all the
;; remaining definitions.
(let ((prefixes (autoload--split-prefixes-1 defs))
(again t))
;; (message "Initial prefixes %s : %S" file (mapcar #'car prefixes))
(while again
(setq again nil)
(let ((newprefixes
(sort
(mapcar (lambda (cell)
(cons cell
(autoload--split-prefixes-1 (cdr cell))))
prefixes)
(lambda (x y) (< (length (cdr x)) (length (cdr y)))))))
(setq prefixes nil)
(while newprefixes
(let ((x (pop newprefixes)))
(if (or (equal '("") (cdar x))
(and (cddr x)
(not (member (caar x)
autoload-popular-prefixes))
(> (+ (length prefixes) (length newprefixes)
(length (cdr x)))
autoload-defs-autoload-max-size)))
;; Nothing to split or would split too deep.
(push (car x) prefixes)
;; (message "Expand %S to %S" (caar x) (cdr x))
(setq again t)
(setq prefixes
(nconc (mapcar (lambda (cell)
(cons (concat (caar x)
(car cell))
(cdr cell)))
(cdr x))
prefixes)))))))
;; (message "Final prefixes %s : %S" file (mapcar #'car prefixes))
(when prefixes
`(if (fboundp 'register-definition-prefixes)
(register-definition-prefixes ,file ',(mapcar #'car prefixes))))))
(defun autoload--setup-output (otherbuf outbuf absfile load-name)
(let ((outbuf
@ -566,11 +664,11 @@ FILE's modification time."
(let (load-name
(print-length nil)
(print-level nil)
(print-readably t) ; This does something in Lucid Emacs.
(float-output-format nil)
(visited (get-file-buffer file))
(otherbuf nil)
(absfile (expand-file-name file))
(defs '())
;; nil until we found a cookie.
output-start)
(when
@ -629,13 +727,73 @@ FILE's modification time."
;; Don't read the comment.
(forward-line 1))
(t
;; Avoid (defvar <foo>) by requiring a trailing space.
;; Also, ignore this prefix business
;; for ;;;###tramp-autoload and friends.
(when (and (equal generate-autoload-cookie ";;;###autoload")
(looking-at "(\\(def[^ ]+\\) ['(]*\\([^' ()\"\n]+\\)[\n \t]")
(not (member
(match-string 1)
'("define-obsolete-function-alias"
"define-obsolete-variable-alias"
"define-category" "define-key"
"defgroup" "defface" "defadvice"
;; Hmm... this is getting ugly:
"define-widget"
"defun-rcirc-command"))))
(push (match-string 2) defs))
(forward-sexp 1)
(forward-line 1))))))
(when (and autoload-compute-prefixes defs)
;; This output needs to always go in the main loaddefs.el,
;; regardless of generated-autoload-file.
;; FIXME: the files that don't have autoload cookies but
;; do have definitions end up listed twice in loaddefs.el:
;; once for their register-definition-prefixes and once in
;; the list of "files without any autoloads".
(let ((form (autoload--make-defs-autoload defs load-name)))
(cond
((null form)) ;All defs obey the default rule, yay!
((not otherbuf)
(unless output-start
(setq output-start (autoload--setup-output
nil outbuf absfile load-name)))
(let ((autoload-print-form-outbuf
(marker-buffer output-start)))
(autoload-print-form form)))
(t
(let* ((other-output-start
;; To force the output to go to the main loaddefs.el
;; rather than to generated-autoload-file,
;; there are two cases: if outbuf is non-nil,
;; then passing otherbuf=nil is enough, but if
;; outbuf is nil, that won't cut it, so we
;; locally bind generated-autoload-file.
(let ((generated-autoload-file
(default-value 'generated-autoload-file)))
(autoload--setup-output nil outbuf absfile load-name)))
(autoload-print-form-outbuf
(marker-buffer other-output-start)))
(autoload-print-form form)
(with-current-buffer (marker-buffer other-output-start)
(save-excursion
;; Insert the section-header line which lists
;; the file name and which functions are in it, etc.
(goto-char other-output-start)
(let ((relfile (file-relative-name absfile)))
(autoload-insert-section-header
(marker-buffer other-output-start)
"actual autoloads are elsewhere" load-name relfile
(nth 5 (file-attributes absfile)))
(insert ";;; Generated autoloads from " relfile "\n")))
(insert generate-autoload-section-trailer)))))))
(when output-start
(let ((secondary-autoloads-file-buf
(if otherbuf (current-buffer))))
(with-current-buffer (marker-buffer output-start)
(cl-assert (> (point) output-start))
(save-excursion
;; Insert the section-header line which lists the file name
;; and which functions are in it, etc.
@ -827,12 +985,13 @@ write its autoloads into the specified file instead."
(dolist (suf (get-load-suffixes))
(unless (string-match "\\.elc" suf) (push suf tmp)))
(concat "^[^=.].*" (regexp-opt tmp t) "\\'")))
(files (apply 'nconc
(files (apply #'nconc
(mapcar (lambda (dir)
(directory-files (expand-file-name dir)
t files-re))
dirs)))
(done ())
(done ()) ;Files processed; to remove duplicates.
(changed nil) ;Non-nil if some change occured.
(last-time)
;; Files with no autoload cookies or whose autoloads go to other
;; files because of file-local autoload-generated-file settings.
@ -850,7 +1009,7 @@ write its autoloads into the specified file instead."
(save-excursion
;; Canonicalize file names and remove the autoload file itself.
(setq files (delete (file-relative-name buffer-file-name)
(mapcar 'file-relative-name files)))
(mapcar #'file-relative-name files)))
(goto-char (point-min))
(while (search-forward generate-autoload-section-header nil t)
@ -878,6 +1037,7 @@ write its autoloads into the specified file instead."
;; If the file is actually excluded.
(member (expand-file-name file) autoload-excludes))
;; Remove the obsolete section.
(setq changed t)
(autoload-remove-section (match-beginning 0)))
((not (time-less-p (let ((oldtime (nth 4 form)))
(if (member oldtime
@ -889,6 +1049,7 @@ write its autoloads into the specified file instead."
;; File hasn't changed.
nil)
(t
(setq changed t)
(autoload-remove-section (match-beginning 0))
(if (autoload-generate-file-autoloads
;; Passing `current-buffer' makes it insert at point.
@ -908,7 +1069,8 @@ write its autoloads into the specified file instead."
(autoload-generate-file-autoloads file nil buffer-file-name))
(push file no-autoloads)
(if (time-less-p no-autoloads-time file-time)
(setq no-autoloads-time file-time)))))
(setq no-autoloads-time file-time)))
(t (setq changed t))))
(when no-autoloads
;; Sort them for better readability.
@ -922,8 +1084,12 @@ write its autoloads into the specified file instead."
autoload--non-timestamp))
(insert generate-autoload-section-trailer)))
(let ((version-control 'never))
(save-buffer))
;; Don't modify the file if its content has not been changed, so `make'
;; dependencies don't trigger unnecessarily.
(when changed
(let ((version-control 'never))
(save-buffer)))
;; In case autoload entries were added to other files because of
;; file-local autoload-generated-file settings.
(autoload-save-buffers))))
@ -955,7 +1121,7 @@ should be non-nil)."
(push (expand-file-name file) autoload-excludes)))))))
(let ((args command-line-args-left))
(setq command-line-args-left nil)
(apply 'update-directory-autoloads args)))
(apply #'update-directory-autoloads args)))
(provide 'autoload)