+ (removed *table--cell-conter-region ad-Orig-gdb-invalidate-frames ad-define-subr-args ad-get-subr-args ad-subr-arglist ad-subr-args-defined-p ad-undefine-subr-args ada-after-change-function ada-handle-syntax-table-properties ada-initialize-syntax-table-properties ada-set-syntax-table-properties allout-auto-save-hook-handler allout-create-encryption-passphrase-verifier allout-encrypted-key-info allout-get-encryption-passphrase-verifier allout-obtain-passphrase allout-setup-mode-map allout-update-passphrase-mnemonic-aids allout-verify-passphrase allout-write-file-hook-handler&newer-than-file-p url-cookie-tag vc-default-previous-version verilog-alw-get-outputs verilog-decls-get-regs verilog-decls-get-wires verilog-get-beg-of-line verilog-get-end-of-line verilog-inside-comment-p verilog-modi-cache-add-regs verilog-modi-cache-add-wires vhdl-mode-line-update view-diary-entries webmail-fetch widget-color-complete widget-default-complete widget-file-complete widget-string-complete with-progress-reporter with-timeout-handler x-frob-font-slant x-frob-font-weight x-get-cut-buffer x-make-font-bold x-make-font-bold-italic x-make-font-demibold x-make-font-italic x-make-font-oblique x-make-font-unbold x-make-font-unitalic x-menu-activate x-menu-add-item x-menu-change-associated-buffer x-menu-create-menu x-menu-delete-item x-menu-mode x-menu-pick-entry x-set-cut-buffer x-setup-function-keys)
+ (added *table--cell-center-region 5x5-defvar-local 5x5-grid-to-vec 5x5-log 5x5-log-init 5x5-solve-rotate-left 5x5-solve-rotate-right 5x5-solve-suggest 5x5-solver 5x5-vec-to-grid Footnote-unicode Info-isearch-end Man-bookmark-jump Man-bookmark-make-record Man-default-bookmark-title Man-page-from-arguments abbrev-edit-save-buffer abbrev-edit-save-to-file abbrev-table-empty-p ad-Orig-dnd-insert-text ad-Orig-dnd-open-file ad-Orig-find-tag ad-Orig-idlwave-shell-electric-debug-mode ad-Orig-org-format-latex-as-mathml ad-Orig-outline-end-of-subtree ad-Orig-save-place-find-file-hook ad-Orig-server-visit-files ad-Orig-visit-tags-table align--set-marker allout-auto-activation-helper allout-body-modification-handler allout-called-interactively-p allout-chart-exposure-contour-by-icon allout-compose-and-institute-keymap a-min-size window-state-get window-state-put window-top-child window-top-line window-total-height window-total-size window-total-width window-use-time window-valid-p window-with-parameter with-org-lparse-preserve-paragraph-state woman-bookmark-jump woman-bookmark-make-record word-search-regexp x-dnd-more-than-3-from-flags x-dnd-version-from-flags xmodmap-generic-mode xselect--encode-string xselect-convert-to-save-targets y-or-n-minibuffer))
+ (removed --dolist-tail-- --dotimes-limit-- OOOOscore OOOscore OOscore Oscore XXXXscore XXXscore XXscore Xscore after-find-file-from-revert-buffer allout-keybindings-list allout-passphrase-hint-handling allout-passphrase-verifier-handling allout-v18/19-file-var-hack allout-view-change-hook appt-is url-cache-expired url-current-referer vc-header-alist vc-path verilog-fork-wait-re widget-complete-field x-alternatives-map x-menu-assoc-buffer x-menu-item-spacing x-menu-item-width x-menu-items-alist x-menu-items-per-line x-menu-max-width x-select-enable-clipboard)
+ (added *org-babel-use-quick-and-dirty-noweb-expansion* *org-bibtex-entries* *orgtbl-default-fmt* *orgtbl-efmt* *orgtbl-fmt* *orgtbl-hline* *orgtbl-lend* *orgtbl-lfmt* *orgtbl-llend* *orgtbl-llfmt* *orgtbl-llstart* *orgtbl-lstart* *orgtbl-rtn* *orgtbl-sep* *orgtbl-table* 5x5-solver-output Info-history-skip-intermediate-nodes Info-isearch-initial-history Info-isearch-initial-history-list Info-mode-syntax-table Man-name-local-regexp ada-on-ms-windows allout-after-copy-or-kill-hook allout-auto-save-temporarily-disabl viper-custom-file-name viper-emacs-state-mode-list viper-insert-state-mode-list viper-major-mode-modifier-list viper-mode viper-new-major-mode-buffer-list viper-saved-non-viper-variables viper-vi-state-mode-list w3m-minor-mode-hook w3m-mode-hook window-combination-limit window-combination-resize window-persistent-parameters window-safe-min-height window-safe-min-width window-sides window-sides-slots window-sides-vertical window-state-put-list xesam-all-fields xesam-debug xesam-fulltext-query xesam-hits-per-page xesam-interface-search xesam-path-search xesam-query-type xesam-service-search xesam-user-query))
+ (removed cl-19 earcon eieio-comp font-setting gdb-ui gnus-audio gnus-move gnus-nocem gnus-soup imap-hash mldrag nndb nnkiboze nnlistserv nnslashdot nnsoup nnultimate nnwarchive nnwfm rnews rnewspost sc tramp-fish tramp-imap x-menu)
+ (added allout-widgets cc-guess cconv cfengine3 color cus-theme ede ede/base ede/files ede/speedbar em-alias em-basic em-dirs em-hist em-script em-unix ert ert-x esh-groups esh-mode esh-module eshell filesets gdb-mi gnus-gravatar gnus-html gnus-registry gnus-sync gnutls gravatar gssapi idlw-complete-structtag idlw-shell idlwave idlwave-shell ido image-dired kkc network-stream newst-backend newst-plainview newst-reader newst-ticker newst-treeview newsticker nnregistry notifications ob ob-C ob-R ob-asymptote ob-awk ob-calcnt org-info org-infojs org-inlinetask org-irc org-jsinfo org-latex org-lparse org-mac-message org-mew org-mhe org-mks org-mobile org-mouse org-odt org-pcomplete org-plot org-protocol org-publish org-remember org-rmail org-special-blocks org-table org-taskjuggler org-timer org-vm org-w3m org-wl org-xoxo package package-x pcase pcvs pcvs-defs plstore ps-bdf quickurl recentf registry remember rtree savehist saveplace server shr shr-color soap-client soap-inspect strokes tabulated-list thumbs timeclock todo-mode tramp-loaddefs tramp-sh type-break url url-cache url-future url-http url-queue vc-dav vip viper)))
+ (removed Buffer-menu-buffer+size Buffer-menu-make-sort-button Buffer-menu-revert-function Buffer-menu-sort-by-column ad-Orig-comment-region ad-Orig-move-overlay ad-Orig-process-filter ad-Orig-set-process-filter ad-Orig-sit-for ad-copy-tree ad-do-return ad-dolist ad-prognify ansi-colivate-input-method-action w32-init-info window--display-buffer-1 window--display-buffer-2 window--size-ignore window-point-1 with-connection-property with-file-property xml-parse-fragment xscheme-modeline-initialize zone-hiding-modeline)
+ (added Buffer-menu--pretty-file-name Buffer-menu--pretty-name Buffer-menu-info-node-description ange-ftp-ignore-errors-if-non-essential ansi-color--find-face ansi-color-parse-sequence archive-extract-by-file auth-source--aget auth-source--aput auth-source--aput-1 auth-source-backend-list-p auth-source-macos-keychain-create auth-source-macos-keychain-result-append auth-source-macos-keychain-search auth-source-macos-keychain-search-items autoload-do-load autoloadp bbdb-buffer bbdb-create-internal bbdb-gethash bindings--define-key bovine-grammar-mode buffer-narrowed-p&nbence-1006 xterm-mouse-translate-1 xterm-mouse-translate-extended yank-handle-category-property yank-handle-font-lock-face-property zone-hiding-mode-line))
+ (removed *gensym-counter* *org-bibtex-entries* *random-state* Buffer-menu--buffers Buffer-menu-buffer-column Buffer-menu-short-ellipsis Buffer-menu-sort-button-map Buffer-menu-sort-column Info-next-link-keymap Info-prev-link-keymap Info-up-link-keymap Man-overstrike-face Man-refpages-alist Man-reverse-face Man-sections-alist Man-underline-face activate-menubar-hook apropos-keybinding-face apropos-label-face apropos-property-face apropos-symbol-face autoload-make-program byte-debug-flag bytecomp-load-hook check-markers-debug-flag cl-active-block-names cl-compiling-file cl-hacked-flag cl-macroexline unread-command-char url-cookie-two-dot-domains vc-bzr-error-regex-alist vc-bzr-sha1-program vc-checkout-carefully vc-ignore-vc-files vc-master-templates vhdl-93-attributes vhdl-93-enum-values vhdl-93-functions vhdl-93-keywords vhdl-93-packages vhdl-93-types xml-name-regexp)
+ (added Buffer-menu-name-width Buffer-menu-size-width Info-file-attributes Man--last-refpage Man--last-section Man--refpages Man--sections Man-ansi-color-map allout-exposure-change-functions allout-structure-added-functions allout-structure-deleted-functions allout-structure-shifted-functions archive-extract-hook async-shell-command-buffer battery--linux-sysfs-regexp byte-compile-root-dir c-parse-state-point c-prepare-bug-report-hook c-state-old-cpp-beg-marker c-state-old-cpp-end-marker cal-html-holidays cal-tex-p;verilog-warn-fatal vhdl-02-attributes vhdl-02-enum-values vhdl-02-functions vhdl-02-keywords vhdl-02-packages vhdl-02-types vhdl-ams-constants vhdl-array-index-record-field-in-sensitivity-list vhdl-comment-display-line-char vhdl-compile-absolute-path vhdl-compile-post-command vhdl-constants-regexp vhdl-indent-comment-like-next-code-line vhdl-makefile-default-targets wdired-keep-marker-rename winner-mode-off-hook x-last-selected-text xml-default-ns xml-entity-expansion-limit xml-entity-or-char-ref-re xml-parameter-entity-alist yank-handled-properties))
+ (removed ob-C ob-asymptote ob-awk ob-clojure ob-fortran ob-haskell ob-java ob-latex org-install python-21 reftex-vcr)
+ (added cl-lib cl-macs erc-desktop-notifications gnus-notifications gv mm-archive ob-scala org-element org-loaddefs org-version profiler url-domsuf w32-common-fns)))
+ (removed Command-history-setup ad-Orig-copy-region-as-kill ad-Orig-create-file-buffer ad-Orig-dnd-insert-text ad-Orig-dnd-open-file ad-Orig-find-tag ad-Orig-idlwave-shell-electric-debug-mode ad-Orig-newline ad-Orig-org-format-latex-as-mathml ad-Orig-outline-end-of-subtree ad-Orig-rename-buffer ad-Orig-save-place-find-file-hook ad-Orig-server-visit-files ad-Orig-visit-tags-table ad-Orig-yank ad-activate-internal-off ad-advised-definition-p ad-clear-orig-definition ad-handle-definition ad-macro-p ad-make-advised-definition-docstring ad-make-advised-docstring ad-mals-p skeleton-newline sql-oracle-show-reserved-words sql-read-passwd sql-signum sql-try-completion text-clone-maintain thumbs-gensym trace-call-tree trace-unsafe trace-use-tree tramp-cleanup tramp-compat-call-process tramp-detect-ssh-controlmaster tramp-sh-handle-call-process-region tramp-sh-handle-executable-find tramp-sh-handle-make-auto-save-file-name universal-argument-minus universal-argument-other-key untranslated-canonical-name untranslated-file-p vc-process-sentinel verilog-get-beg-of-defun verilog-get-list verilog-sk-uvm-class verilog-type-completion vhdl-mode-syntax-table-init viper-envelop-ESC-key widget-remove-if winner-hook-installed-p with-org-lparse-preserve-paragraph-state woman-mark-horizonal-position xmltok-add-dependent xmltok-semi-closed-reparse-p xmltok-unclosed-reparse-p xterm-mouse-event-read)
+ (added Buffer-menu--unmark Buffer-menu-multi-occur Info--search-loop Info-build-node-completions-1 Info-next-reference-or-link Info-prev-reference-or-link Man-parse-man-k Man-start-calling Man-update-manpage abbrev--default-expand abbrev--symbol ad--defalias-fset ad--make-advised-docstring ad-Advice-c-backward-sws ad-Advice-c-beginning-of-macro ad-Advice-c-forward-sws ad-Advice-dnd-insert-text ad-Advice-dnd-open-file ad-Advice-find-tag ad-Advice-idlwave-shell-electric-debug-mode ad-Advice-save-place-find-file-hook ad-Advice-server-visit-files ad-Advice-visit-tags-table ad-clear-advicefunname-definition ad-make-advicefunname add-face-text-property add-function advice--add-function advice--buffer-local advice--called-interactively-skip advice--car advice--cd*r advice--cdr advice--defalias-fset advice--interactive-ize-apply-total window-resize-no-error window-right-divider-width window-safe-min-pixel-height window-safe-min-pixel-size window-safe-min-pixel-width window-safe-min-size window-screen-lines window-scroll-bar-width window-size window-text-pixel-size window-text-width with-current-buffer-window with-eval-after-load with-isearch-suspended woman-mark-horizontal-position x-focus-frame x-popup-dialog xesam-buffer-name xesam-dbus-call-method xesam-delete-search-engine xesam-get-cached-property xesam-get-hits xesam-get-property xesam-highlight-string xesam-kill-buffer-function xesam-minor-mode xesam-mode xesam-new-search xesam-refresh-entry xesam-refresh-search-buffer xesam-search-engines xesam-set-cached-property xesam-set-property xesam-signal-handler))
+ &nrk-default-inactive show-paren-idle-timer show-paren-overlay show-paren-overlay-1 sieve-manage-stream-alist sieve-manage-streams sql-output-by-send thing-at-point-url-regexp tramp-completion-file-name-regexp-url tramp-file-name-regexp-url tramp-message-show-progress-reporter-message universal-argument-num-events vc-diff-knows-L verilog-mode-release-date vhdl-speedbar-key-map whitespace-font-lock whitespace-font-lock-mode xmltok-dependent-regions)
+ (added *org-babel-gnuplot-missing* *org-babel-gnuplot-terms* 5x5-mode-abbrev-table 5x5-mode-syntax-table Buffer-menu-mode-hook Edit-options-mode-abbrev-table Edit-options-mode-hook Edit-options-mode-syntax-table Info-edit-mode-abbrev-table Info-edit-mode-map Info-edit-mode-syntax-table Info-file-completions Man-man-k-use-anchor Man-mode-abbrev-table Man-mode-syntaok window-resize-pixelwise window-state-put-stale-windows wordstar-mode-abbrev-table wordstar-mode-hook wordstar-mode-syntax-table xesam-count xesam-current xesam-dbus-unique-names xesam-engine xesam-minibuffer-query-history xesam-minibuffer-vendor-history xesam-minor-mode xesam-minor-mode-hook xesam-mode-abbrev-table xesam-mode-hook xesam-mode-map xesam-mode-syntax-table xesam-notify-function xesam-objects xesam-query xesam-refreshing xesam-search xesam-search-engines xesam-to xesam-type xesam-vendor xesam-xml-string xterm-mouse-mode-hook))
+ (removed advice-preload delphi find-gc octave-inf octave-mod org-ascii org-beamer org-docbook org-exp org-exp-blocks org-export-latex org-freemind org-html org-icalendar org-infojs org-jsinfo org-latex org-lparse org-mac-message org-mew org-mks org-odt org-publish org-remember org-special-blocks org-taskjuggler org-vm org-wl org-xoxo)
+ (added bat-mode cl-indent cl-loaddefs cus-dep dos-vars eieio-core elec-pair em-tramp erc-list eudcb-bbdb eww filenotify frameset gnus-icalendar gnus-vm inotify nadvice ob-C ob-asymptote ob-awk ob-clojure ob-core ob-fortran ob-haskell ob-io ob-java ob-latex ob-makefile octave opascal org-macro ox ox-ascii ox-beamer ox-html ox-icalendar ox-latex ox-man ox-md ox-odt ox-org ox-publish ox-texinfo pcmpl-x prog-mode smiley subr-x sup-mouse superword tpu-extras tramp-adb xesam)))
+ (removed browse-url-firefox-sentinel cua-repeat-replace-region python-indent-toggle-levels tramp-time-less-p tramp-time-subtract)
+ (added c--macroexpand-all c-macro-is-genuine-p delete-selection-repeat-replace-region erc-channel-user-admin erc-channel-user-admin-p erc-channel-user-halfop erc-channel-user-halfop-p erc-channel-user-owner erc-channel-user-owner-p erc-get-user-mode-prefix erc-quit/part-reason-default file-notify--descriptor python-indent--calculate-indentation python-indent--calculate-levels python-indent--previous-level python-info-encoding python-info-encoding-from-cookie python-syntax-closing-paren-p reftex--prepare-syntax-tables todo-revert-buffer))
+ (removed cua--last-deleted-region-pos cua--last-deleted-region-text cua--repeat-replace-text ispell-hunspell-dictionary-equivs-alist python--use-fake-loc)
+ (added delete-selection-save-to-register delsel--replace-text-or-position erc-rename-buffers ispell-dicts-name2locale-equivs-alist mml2015-display-key-image python-shell-unbuffered vc-hg-log-graph))
+ (removed ada-convert-file-name add-to-diary-list american-calendar authors authors-add authors-add-fixed-entries authors-add-to-auselection-owner-p x-valid-simple-selection-p xterm-mouse--read-event-sequence-1000 xterm-mouse--read-event-sequence-1006)
+ (added \(setf\ ede-object-keybindings\) \(setf\ ede-object-menu\) \(setf\ ede-object-sourcecode\) \(setf\ gnus-icalendar-event:description\) \(setf\ gnus-icalendar-event:end-time\) \(setf\ gnus-icalendar-event:location\) \(setf\ gnus-icalendar-event:method\) \(setf\ gnus-icalendar-event:opt-participants\) \(setf\ gnus-icalendar-event:organizer\) \(setf\ gnus-icalendar-event:participation-type\) \(setf\ gnus-icalendar-event:recur\) \(setf\ gnus-icalendar-event:req-participants\) \(setf\ gnus-icalendar-event:rsvp\) \(setf\ gnus-icalendar-event:start-time\) \(setf\ gnus-icalendar-event:summary\) \(setf\ gnus-icalendar-event:uid\) \(setf\ seq-elt\) -cvs-create-widget-webkit-goto-url xwidget-webkit-insert-string xwidget-webkit-last-session xwidget-webkit-mode xwidget-webkit-new-session xwidget-webkit-reload xwidget-webkit-scroll-backward xwidget-webkit-scroll-down xwidget-webkit-scroll-forward xwidget-webkit-scroll-up xwidget-webkit-show-id-element xwidget-webkit-show-id-or-named-element xwidget-webkit-show-named-element zerop--anon-cmacro))
+ (removed abbreviated-calendar-year all-bahai-calendar-holidays all-christian-calendar-holidays all-hebrew-calendar-holidays all-islamic-calendar-holidays american-calendar-display-form american-date-diary-pattern authors-aliases authors-ambiguous-files authors-coding-system authors-fixed-case authors-fixed-entries authors-ignored-files authors-lax-changelogs authors-many-files authors-no-stave-else-keywords octave-end-keywords octave-operator-table octave-smie-bnf-table oriental-holidays other-holidays parse-time-digits print-diary-entries-hook ps-mode-auto-indent python-shell-output-syntax-table sexp-diary-entry-symbol shr-inhibit-decoration shr-state solar-holidays table-cell-entered-state tildify-count today-invisible-calendar-hook today-visible-calendar-hook tool-bar-originally-present uniquify-ask-about-buffer-names-p vc-default-init-revision vc-disable-async-diff vc-keep-workfiles vc-mcvs-diff-switches vc-mcvs-global-switches vc-mcvs-header vc-mcvs-register-switches vc-mcvs-use-edit vc-mistrust-permissions vc-stay-local view-calendar-holidays-initially view-diary-entries-initially window-system-initialization-alist x-last-selected-text)
+ (added Info-mode-font-lock-keywords&nbe xwidget-webkit-mode-hook xwidget-webkit-mode-map xwidget-webkit-mode-syntax-table))
+ (added bruce char-fold charscript cl-generic cl-preloaded cl-seq cp51932 cursor-sensor dom ede/detect eieio-compat elisp-mode eucjp-ms generator gnus-cloud inline let-alist map nsm obarray pinentry project sasl-scram-rfc sasl-scram-sha-1 seq thunk url-tramp vc-filewise vc-src xref xwidget)))
+ (removed 8859-1-map 8859-to-swascii-buffer Buffer-menu-info-node-description Man-quit ad-Advice-find-tag auto-source--symbol-keyword autoload-ensure-default-file c-clear-cpp-delimiters c-comment-out-cpps c-neutralize-syntax-in-and-mark-CPP c-set-cpp-delimiters c-state-semi-safe-place c-uncomment-out-cpps c-with-all-but-one-cpps-commented-out c-with-cpps-commented-out check-declare-errmsg cl-struct-avl-tree- cl-struct-avl-tree--node cl-struct-avl-tree--stack cl-struct-cl--class cl-struct-cl--generic cl-struct-cl--generic-generalizer cl-struct-cl--generic-method cl-struct-cl-slot-descriptor cl-struct-cl-structure-class cl-struct-cl-structure-object cl-struct-compilation--message cl-struct-cvs-fileinfo cl-struct-cvs-flags cl-struct-cvs-qtypedesc cl-struct-cvs-tag cl-struct-edebug--form-data&nbsecursive-delete-directory tramp-sh-handle-insert-file-contents-literally tramp-user-error undo-auto--undoable-change-no-timer url-cookie-quit verilog-save-font-mods verilog-save-no-change-functions whitespace-ensure-local-variables window--major-non-side-window window--major-side-window window--side-check window--side-window-p window-dot woman-pop woman-push xmltok-forward-special xwidget-webkit-execute-script-rv y-or-n-minibuffer)
+ (added Buffer-menu-unmark-all Buffer-menu-unmark-all-buffers Info-node-description ad-Advice-exchange-point-and-mark ad-Advice-pop-global-mark ad-Advice-pop-to-mark-command ad-Advice-xref-find-definitions add-variable-watcher advice--where all-threads allout-count-trailing-whitespace-region allout-nullify-prefix-data allout-produce-mode-menubar-entries allout-regexp-sans-escapes p;window-toggle-side-windows with-connection-local-profiles with-mutex xdg--dir-home xdg--substitute-home-env xdg--user-dirs-parse-file xdg--user-dirs-parse-line xdg-cache-home xdg-config-dirs xdg-config-home xdg-data-dirs xdg-data-home xdg-desktop-read-file xdg-desktop-read-group xdg-desktop-strings xdg-runtime-dir xdg-thumb-mtime xdg-thumb-name xdg-thumb-uri xdg-user-dir xref--regexp-syntax-dependent-p xref-quit-and-goto-xref xwidget-webkit-adjust-size-in-frame xwidget-webkit-auto-adjust-size xwidget-webkit-scroll-bottom xwidget-webkit-scroll-top xwidget-webkit-show-element xwidget-webkit-zoom-in xwidget-webkit-zoom-out))
+ (removed *orgtbl-default-fmt* *orgtbl-efmt* *orgtbl-fmt* *orgtbl-hline* *orgtbl-lend* *orgtbl-lfmt* *orgtbl-llend*&nbsw)
+ (added Buffer-menu-del-char Buffer-menu-marker-char Man-coding-system align-regexp-history ansi-color-control-seq-regexp arabic-shape-log arabic-shaper-ZWNJ-handling async-shell-command-display-buffer attempt-orderly-shutdown-on-fatal-signal attempt-stack-overflow-recovery auth-source-backend-parser-functions auth-source-pass-backend auto-save--timer auto-save-visited-interval auto-save-visited-mode auto-save-visited-mode-hook autoload--non-timestamp autoload-compute-prefixes autoload-def-prefixes-max-entries autoload-def-prefixes-max-length autoload-timestamps battery-linux-sysfs-regexp battery-upower-device bidi-paragraph-separate-re bidi-paragraph-start-re bookmark-file-coding-system browse-url-man-function byte-compile-cond-use-jump-table byte-compile-jump-tables byte-compile-log-warning-function byte-swision-history verilog-auto-inst-param-value-type verilog-auto-wire-comment verilog-default-clocking-re verilog-diff-ignore-regexp verilog-font-lock-grouping-keywords-face viper--advice-list viper--undo-change-group-handle wdired-create-parent-directories while-no-input-ignore-events window--sides-inhibit-check window--sides-shown window-sides-reversed woman-justify-styles woman-mode-abbrev-table woman-mode-hook woman-mode-syntax-table xdg-desktop-entry-regexp xdg-desktop-group-regexp xdg-line-regexp xdg-user-dirs xref--original-window xref--original-window-intent))
+ (removed awk-mode gnus-ems gnus-sync iso-acc iso-insert iso-swed messcompat mh-acros nxml-glyph nxml-uchnm ob-scala ob-sh pinentry resume scribe spell swedish sym-comp tramp-gw)
+ (added auth-source-pass charprop cl-print composite dired-loaddefs display-line-numbers eieio-loaddefs flymake-proc gnus-rfc1843 htmlfontify-loaddefs ibuffer-loaddefs isearch less-css-mode mhtml-mode ob-J ob-abc ob-coq ob-ebnf ob-forth ob-groovy ob-hledger ob-lua ob-processing ob-sed ob-shell ob-stan ob-vala org-duration org-eww org-lint pixel-scroll ps-print-loaddefs puny radix-tree reftex-loaddefs replace rmail-loaddefs rmc svg term/tty-colors text-mode timer-list xdg)))
+ (removed dired--yes-no-all-quit-help shadow-get-user shadow-make-cluster shadow-parse-fullname)
+ (added assoc-delete-all calc-graph-w32-p copy-shadow-cluster ibuffer-do-toggle-lock make-shadow-cluster make-shadow-cluster--cmacro package--user-installed-p read-answer shadow-cluster-name--cmacro shadow-cluster-p shadow-cluster-p--cmacro shadow-cluster-primary--cmacro shadow-cluster-regexp--cmacro shadow-name-site shadow-site-name soap-type-of term--bash-needs-EMACSp term-watch-for-password-prompt thread-live-p))
+ (removed dired-delete-help last--line-number-width prolog-hungry-delete-key-flag)
+ (added byte-compile--default-val cl-print--depth cl-struct-shadow-cluster-tags ido-fallback read-answer-map--memoize read-answer-short term--bash-needs-EMACS-status xft-ignore-color-fonts))
+ (added dir-locals--get-sort-score dir-locals--sort-variables gnus-summary-sort-by-marks package--buffer-string package--cs))
+ (removed Edit-options-mode Edit-options-modify Edit-options-nil Edit-options-set Edit-options-t Edit-options-toggle Footnote-add-footnote Footnote-assoc-index Footnote-back-to-message Footnote-current-regexp Footnote-cycle-style Footnote-delete-footnote Footnote-english-lower Footnote-english-upper Footnote-goto-char-point-max Footnote-goto-footnote Footnote-index-to-string Footnote-insert-footnote Footnote-insert-numbered-footnote Footnote-insert-pointer-marker Footnote-insert-text-marker Footnote-latin Footnote-make-hole Footnote-narrow-to-footnotes Footnote-numeric Footnote-refresh-footnotes Footnote-renumber Footnote-renumber-footnotes Footnote-roman-common Footnote-roman-lower Footnote-roman-upper Footnote-set-style Footnote-sort Footnote-style-p Footnote-text-unbsp;xesam-minor-mode xesam-mode xesam-new-search xesam-refresh-entry xesam-refresh-search-buffer xesam-search xesam-search-engines xesam-set-cached-property xesam-set-property xesam-signal-handler xref--find-buffer-visiting xref--pop-to-location)
+ (added \(setf\ jsonrpc--deferred-actions\) \(setf\ jsonrpc--events-buffer\) \(setf\ jsonrpc--events-buffer-scrollback-size\) \(setf\ jsonrpc--expected-bytes\) \(setf\ jsonrpc--next-request-id\) \(setf\ jsonrpc--notification-dispatcher\) \(setf\ jsonrpc--on-shutdown\) \(setf\ jsonrpc--process\) \(setf\ jsonrpc--request-continuations\) \(setf\ jsonrpc--request-dispatcher\) \(setf\ jsonrpc-last-error\) \(setf\ jsonrpc-name\) Info-history-back-menu Info-history-forward-menu Info-history-menu Man--window-state-change Man-build-siations xml-remove-comments xref--create-fetcher xref--display-buffer-in-other-window xref--display-buffer-in-window xref--find-file-buffer xref--imenu-extract-index-name xref--imenu-prev-index-position xref--not-found-error xref--push-markers xref--show-common-initialize xref--show-defs xref--show-defs-buffer xref--show-defs-buffer-at-bottom xref--transient-buffer-mode xref-backend-identifier-completion-ignore-case xref-find-definitions-at-mouse xref-matches-in-directory xref-matches-in-files xref-pop-to-location xref-references-in-directory xref-revert-buffer xref-set-marker-ring-length y-or-n-p-insert-n y-or-n-p-insert-other y-or-n-p-insert-y))
+ (removed * ** *** *1 *2 *3 Edit-options-mode-abbrev-table Edit-options-mode-hook Edit-options-mode-xesam-to xesam-type xesam-user-query xesam-vendor xesam-xml-string xref--last-visiting-buffer)
+ (added Man--window-state-change-timer Man-columns Man-width-max add-log-dont-create-changelog-file after-delete-frame-functions after-focus-change-function after-set-visited-file-name-hook amalgamating-undo-limit apropos--current async-shell-command-width auth-source--session-nonce auth-source-pass-filename auth-source-pass-port-separator authinfo-hidden authinfo-mode-abbrev-table authinfo-mode-hook authinfo-mode-map authinfo-mode-syntax-table auto-raise-tab-bar-buttons auto-resize-tab-bars auto-revert--buffers-by-watch-descriptor auto-revert--global-mode auto-revert--lockout-interval auto-revert--lockout-timer auto-revert-avoid-polling auto-revert-debug auto-save-no-message autodoc-bright-comment-start-re&nevert-switches vc-retrieve-tag-hook vc-tor verilog-auto-reg-input-assigned-ignore-regexp verilog-auto-simplify-expressions vhdl-project-autoload viper-ViperBell warning-fill-column what-cursor-show-names widget-link-keymap windmove-create-window windmove-display-no-select window-buffer-change-functions window-selection-change-functions window-state-change-functions window-state-change-hook winner-boring-buffers-regexp wordstar-mode wordstar-mode-lighter wordstar-mode-major-mode wordstar-mode-set-explicitly xdg-mime-table xref--fetcher xref--last-file-buffer xref--transient-buffer-mode-abbrev-table xref--transient-buffer-mode-hook xref--transient-buffer-mode-map xref--transient-buffer-mode-syntax-table xref-file-name-display xref-show-definitions-function y-or-n-p-history-variable y-or-n-p-map zeroconf-service-type-browser-handler-running))
+ (removed ada-mode ada-prj ada-stmt ada-xref ob-keys options org-bbdb org-bibtex org-docview org-eshell org-eww org-gnus org-info org-irc org-mhe org-rmail org-w3m xesam)
+ (added backtrace display-fill-column-indicator erc-loaddefs exif faceup fileloop flymake-cc image-converter info-edit iso8601 jsonrpc ob-eshell ol ol-bbdb ol-bibtex ol-docview ol-eshell ol-eww ol-gnus ol-info ol-irc ol-mhe ol-rmail ol-w3m org-attach-git org-goto org-keys org-num org-tempo pinyin so-long tab-bar tab-line text-property-search thread tramp-archive tramp-gvfs tramp-integration tramp-rclone tramp-sudoedit)))
+ (removed org--property-global-value org--setup-collect-keywords org-attach-expand-link org-attach-export-link org-attach-open-link org-attach-store-link org-babel-clojure-cider-current-ns org-babel-clojure-initiate-session org-babel-clojure-var-to-clojure org-babel-prep-session:clojure org-babel-variable-assignments:clojure org-columns-set-tags-or-toggle org-encrypt-string)
+ (added backtrace--to-string dbus-get-other-registered-property dbus-get-this-registered-property debug-help-follow debugger-insert-backtrace debugger-toggle-locals fit-frame-to-buffer-1 fit-mini-frame-to-buffer ob-clojure-eval-with-cider ob-clojure-eval-with-inf-clojure ob-clojure-eval-with-slime ob-clojure-inf-clojure-output obtring org-toggle-radio-button org-up-heading-or-point-min tramp-adb-get-signal-strings tramp-get-remote-awk tramp-get-remote-busybox tramp-get-remote-hexdump tramp-get-remote-od tramp-sh-get-signal-strings tramp-unquote-file-local-name))
+ (removed org-babel-clojure-sync-nrepl-timeout org-dcst org-export-special-keywords org-f org-file-properties org-hide-leading-stars-before-indent-mode org-l org-m org-remove-priority-next-time tramp-awk-coding-test)
+ (added c-recognize-bare-brace-inits finder-buffer org-agenda-filter-hook org-agenda-loop-over-headlines-in-active-region org-agenda-sort-noeffort-is-high org-agenda-sort-notime-is-late org-archive-subtree-save-file-p org-babel-default-header-args:clojurescript org-babel-haskell-compier-line-mode org-table-header-line-mode-hook org-table-header-line-p org-table-header-overlay org-ts--internal-regexp tramp-compat-emacs-compiled-version tramp-gvfs-unload-hook tramp-hexdump-awk-encode tramp-hexdump-encode tramp-od-awk-encode tramp-od-encode tramp-smb-options tramp-system-name))
+ (removed Math-natnum-lessp add-menu add-menu-item add-menu-item-1 advertised-xscheme-send-previous-expression allocate-event allout-init allout-run-unit-tests allout-setup-menubar allout-test-range-overlaps allout-test-resumptions allout-tests-obliterate-variable allout-widf-etags-location--anon-cmacro xref-etags-location--eieio-childp xref-etags-location-child-p xref-etags-location-list-p xref-file-location xref-file-location--anon-cmacro xref-file-location--eieio-childp xref-file-location-child-p xref-file-location-list-p xref-item xref-item--anon-cmacro xref-item--eieio-childp xref-item-child-p xref-item-list-p xref-location xref-location--anon-cmacro xref-location--eieio-childp xref-location-child-p xref-location-list-p xref-location-p xref-match-item xref-match-item--anon-cmacro xref-match-item--eieio-childp xref-match-item-child-p xref-match-item-list-p)
+ (added \(setf\ xref-item-location\) \(setf\ xref-item-summary\) \(setf\ xref-match-item-length\) \(setf\ xref-match-item-location\) \(setf\ xref-match-item-summary\) Buffer-menu--dynamic-name-width e-etags-location--cmacro xref-make-file-location--cmacro xref-match-item-length xref-match-item-location xref-match-item-summary xref-next-group xref-next-line-no-show xref-prev-group xref-prev-line-no-show xref-quit-and-pop-marker-stack xref-select-and-show-xref xref-show-definitions-buffer xref-show-definitions-buffer-at-bottom xref-show-definitions-completing-read xterm-mouse--handle-mouse-movement xwidget-webkit-clone-and-split-below xwidget-webkit-clone-and-split-right xwidget-webkit-forward xwidget-webkit-save-as-file xwidget-webkit-scroll-down-line xwidget-webkit-scroll-up-line xwidget-window-inside-pixel-height xwidget-window-inside-pixel-width yank-from-kill-ring))
--- /dev/null
+;;; package-lint.el --- A linting library for elisp package authors -*- lexical-binding: t -*-
+
+;; Copyright (C) 2014-2020 Steve Purcell, Fanael Linithien
+
+;; Author: Steve Purcell <steveNOSPAMsanityinc.com>
+;; Fanael Linithien <fanael4NOSPAMgmail.com>
+;; URL: https://github.com/purcell/package-lint
+;; Keywords: lisp
+;; Package-Version: 20250418.1425
+;; Package-Revision: 2dc48e5fb9c3
+;; Package-Requires: ((emacs "24.4") (let-alist "1.0.6"))
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Provides a list of issues with the package metadata of a file,
+;; e.g. the package dependencies it requires.
+
+;; See function `package-lint-buffer'.
+
+;; Checks will currently be enabled only if a "Package-Requires:" or
+;; "Package-Version:" header is present in the file.
+
+;;; Code:
+
+(eval-when-compile (require 'pcase)) ; `pcase-dolist' is not autoloaded
+(require 'cl-lib)
+(require 'package)
+(require 'lisp-mnt)
+(require 'finder)
+(require 'imenu)
+(require 'let-alist)
+(require 'rx)
+
+
+(defvar package-lint-main-file nil
+ "For multi-file packages, set this to the main .el or -pkg.el file.
+
+When set, the package prefix and dependencies are obtained from
+that file instead of the buffer being linted. This allows
+package-lint to operate on secondary files in a package.
+
+The path can be absolute or relative to that of the linted file.")
+(put 'package-lint-main-file 'safe-local-variable #'stringp)
+
+\f
+;;; Compatibility
+
+(defalias 'package-lint--package-desc-summary
+ (if (fboundp 'package-desc-summary)
+ 'package-desc-summary
+ 'package-desc-doc))
+
+(defalias 'package-lint--package-desc-name
+ (if (fboundp 'package-desc-name)
+ 'package-desc-name
+ (lambda (desc) (intern (elt desc 0)))))
+
+(defalias 'package-lint--package-desc-from-define
+ (if (fboundp 'package-desc-from-define)
+ 'package-desc-from-define
+ (lambda (_name-string version-string &optional docstring requirements &rest _args)
+ (vector
+ (version-to-list version-string)
+ (mapcar
+ (lambda (x)
+ (list (nth 0 x) (version-to-list (nth 1 x))))
+ (nth 1 requirements))
+ docstring))))
+
+\f
+;;; Machinery
+
+(defvar package-lint--errors nil
+ "List of errors and warnings for the current buffer.
+This is bound dynamically while the checks run.")
+
+(defconst package-lint-emacs-head-version '(31)
+ "Version of Emacs HEAD.")
+
+(defconst package-lint-backport-libraries
+ (list (list 'cl-lib "\\`cl-")
+ (list 'cl-generic "\\`cl-\\(?:def\\)?generic")
+ (list 'cl-print "\\`cl-print")
+ (list 'compat "\\`compat-'")
+ (list 'jsonrpc "\\`jsonrpc-")
+ (list 'map "\\`map-")
+ (list 'nadvice "\\`advice-")
+ (list 'ol "\\`org-link-" 'org "9.3")
+ (list 'seq "\\`seq-")
+ (list 'transient "\\`transient-")
+ (list 'hierarchy "\\`hierarchy-")
+ (list 'let-alist "\\`let-alist"))
+ "A sequence of (FEATURE SYMBOL-NAME-MATCH) for backport libraries.
+These are libraries that are built into newer Emacsen and also
+published in ELPA for use by older Emacsen.
+
+Alternatively, items can take a form of (FEATURE
+SYMBOL-NAME-MATCH PACKAGE VERSION?) where PACKAGE is the name of
+a backport library shipping the feature and VERSION is an
+optional minimum version containing the feature.")
+
+(defun package-lint--load-data (file)
+ "Load sexp data from FILE."
+ (with-temp-buffer
+ (insert-file-contents
+ (expand-file-name file
+ (if load-file-name
+ (file-name-directory load-file-name)
+ default-directory)))
+ (read (current-buffer))))
+
+(defconst package-lint-symbol-info
+ (let* ((stdlib-changes (package-lint--load-data "data/stdlib-changes"))
+ (info (make-hash-table)))
+ (pcase-dolist (`(,version . ,data) stdlib-changes)
+ (pcase-dolist (`(,syms . ,action)
+ (let-alist data
+ (list (cons .variables.added 'variable-added)
+ (cons .variables.removed 'variable-removed)
+ (cons .features.added 'library-added)
+ (cons .features.removed 'library-removed)
+ (cons .functions.added 'function-added)
+ (cons .functions.removed 'function-removed))))
+ (dolist (sym syms)
+ (puthash sym (cons (cons action version) (gethash sym info)) info))))
+ info)
+ "A hash table from SYMBOL to a list of events in its history.
+Each event is of the form (ACTION . EMACS-VER), where ACTION is a
+symbol such as `variable-added'.")
+
+(defun package-lint-symbol-info (sym)
+ "Retrieve information about SYM, as an alist of (ACTION . EMACS-VER)."
+ (gethash (cl-etypecase sym
+ (string (intern sym))
+ (symbol sym))
+ package-lint-symbol-info))
+
+(defun package-lint--read-known-symbol (prompt)
+ "Read a symbol `package-lint-symbol-info' with PROMPT."
+ (let* ((at-point (symbol-at-point))
+ (s (completing-read
+ prompt
+ (cl-loop for k being the hash-keys of package-lint-symbol-info collect k)
+ nil
+ t
+ (when at-point (symbol-name at-point)))))
+ (when s (intern s))))
+
+;;;###autoload
+(defun package-lint-describe-symbol-history (sym)
+ "Show the version history of SYM, if any."
+ (interactive (list (package-lint--read-known-symbol "Show history of symbol: ")))
+ (message
+ (concat (format "History of %s:\n" sym)
+ (mapconcat (lambda (info)
+ (pcase-let ((`(,action . ,version) info))
+ (format "* %s in Emacs %s"
+ action (mapconcat 'number-to-string version "."))))
+ (sort (package-lint-symbol-info sym)
+ (lambda (a b) (version-list-<= (cdr a) (cdr b))))
+ "\n"))))
+
+(defun package-lint--added-or-removed-function-p (sym)
+ "Return t if SYM is a function added/removed in any known Emacs version."
+ (let-alist (package-lint-symbol-info sym)
+ (or .function-added .function-removed)))
+
+(defconst package-lint--compat-symbols
+ (package-lint--load-data "data/compat-symbols")
+ "A cons cell of (VARS . FUNCTIONS) supported by \"compat\".")
+
+(defun package-lint--supported-by-compat (type sym)
+ "Return non-nil if SYM is supported by the \"compat\" package.
+TYPE is `function' or `variable'."
+ (memq sym (pcase type
+ (`function (cdr package-lint--compat-symbols))
+ (_ nil))))
+
+(defconst package-lint--sane-prefixes
+ (rx
+ string-start
+ (or
+ "org-dblock-write:"
+ "org-babel-execute:"
+ "org-babel-expand-body:"
+ "org-babel-header-args:"
+ "org-babel-prep-session:"
+ "org-babel-variable-assignments:"
+ "org-babel-default-header-args:"
+ "pcomplete/"
+ "use-package-normalize/"
+ "use-package-handler/"
+ "use-package-autoloads/"))
+ "A regexp matching whitelisted non-standard symbol prefixes.")
+
+(defvar package-lint--allowed-prefix-mappings
+ '(("ob-" . ("org-"))
+ ("oc-" . ("org-"))
+ ("ol-" . ("org-"))
+ ("ox-" . ("org-")))
+ "Alist containing mappings of package prefixes to symbol prefixes.")
+
+(defun package-lint--main-file-p ()
+ "Return non-nil if the current buffer corresponds to the package's main file."
+ (or (null package-lint-main-file)
+ (null (buffer-file-name))
+ (string= (file-truename (expand-file-name package-lint-main-file))
+ (file-truename (buffer-file-name)))))
+
+(defun package-lint--check-all ()
+ "Return a list of errors/warnings for the current buffer."
+ (let ((package-lint--errors '())
+ (case-fold-search nil))
+ (save-match-data
+ (save-excursion
+ (save-restriction
+ (widen)
+ (let (prefix deps)
+ (if (package-lint--main-file-p)
+ (progn
+ ;; TODO: handle when the main file is a -pkg.el file
+ (setq prefix (package-lint--get-package-prefix))
+ (let ((desc (package-lint--check-package-el-can-parse)))
+ (when desc
+ (package-lint--check-package-summary desc)
+ (package-lint--check-provide-form desc)
+ (package-lint--check-no-emacs-in-package-name desc)))
+ (setq deps (package-lint--check-dependency-list))
+
+ (package-lint--check-url-header)
+ (package-lint--check-package-version-present)
+ (package-lint--check-commentary-existence))
+ ;; Need to look at the main file to find prefix and dependencies
+ (setq prefix (replace-regexp-in-string
+ "\\(-mode\\)?\\(-pkg\\)?\\'" ""
+ (file-name-sans-extension
+ (file-name-nondirectory package-lint-main-file))))
+ (let ((main-file package-lint-main-file))
+ (if (string-match-p "-pkg\\.el\\'" main-file)
+ (let ((expr (with-temp-buffer
+ (insert-file-contents (expand-file-name main-file))
+ (read (current-buffer)))))
+ (if (eq (car-safe expr) 'define-package)
+ (setq deps (package-desc-reqs (apply #'package-lint--package-desc-from-define (cdr expr))))
+ (package-lint--error-at-bob 'error (format "Malformed package descriptor file \"%s\"" main-file))))
+ (when (package-lint--goto-header "Package-Requires")
+ (package-lint--error-at-bol 'error "Package-Requires outside the main file have no effect."))
+ (package-lint--check-provide-form-secondary-file)
+ (condition-case err
+ (with-temp-buffer
+ (insert-file-contents (expand-file-name main-file))
+ (setq deps (package-desc-reqs (package-lint--liberal-package-buffer-info))))
+ (error
+ (package-lint--error-at-bob
+ 'error
+ (format "Error parsing main package file \"%s\": %s" main-file err)))))))
+
+ ;; Source code checks
+ (package-lint--check-keywords-list)
+ (package-lint--check-lexical-binding-is-on-first-line)
+ (package-lint--check-reserved-keybindings)
+ (when prefix
+ (package-lint--check-objects-by-regexp
+ (concat "(" (regexp-opt '("defalias" "defvaralias")) "\\s-")
+ (apply-partially #'package-lint--check-defalias prefix)))
+ (package-lint--check-objects-by-regexp
+ "(define-minor-mode\\s-"
+ #'package-lint--check-minor-mode)
+ (package-lint--check-objects-by-regexp
+ "(define-global\\(?:ized\\)?-minor-mode\\s-"
+ #'package-lint--check-globalized-minor-mode)
+ (package-lint--check-objects-by-regexp
+ "(defgroup\\s-" #'package-lint--check-defgroup)
+
+ (package-lint--check-no-use-of-cl)
+ (package-lint--check-no-use-of-cl-lib-sublibraries)
+ (package-lint--check-eval-after-load)
+ (package-lint--check-lexical-binding-requires-emacs-24 deps)
+ (package-lint--check-libraries-available-in-emacs deps)
+ (package-lint--check-libraries-removed-from-emacs)
+ (package-lint--check-macros-functions-available-in-emacs deps)
+ (package-lint--check-macros-functions-removed-from-emacs deps)
+ (package-lint--check-objects-by-regexp
+ (concat "(" (regexp-opt '("format" "message" "error")) "\\s-")
+ (apply-partially #'package-lint--check-format-string deps))
+ (package-lint--check-for-literal-emacs-path)
+ (let ((definitions (package-lint--get-defs)))
+ (package-lint--check-autoloads-on-private-functions definitions)
+ (when prefix
+ (package-lint--check-defs-prefix prefix definitions))
+ (package-lint--check-symbol-separators definitions)))
+ (package-lint--check-lonely-parens))))
+ (sort package-lint--errors
+ (lambda (a b)
+ (pcase-let ((`(,a-line ,a-column ,_ ,a-message) a)
+ (`(,b-line ,b-column ,_ ,b-message) b))
+ (cond
+ ((/= a-line b-line) (< a-line b-line))
+ ((/= a-column b-column) (< a-column b-column))
+ (t
+ (string-lessp a-message b-message))))))))
+
+(defun package-lint--error (line col type message)
+ "Construct a datum for error at LINE and COL with TYPE and MESSAGE."
+ (push (list line col type message) package-lint--errors))
+
+(defun package-lint--error-at-point (type message &optional pos)
+ "Construct a datum for error at POS with TYPE and MESSAGE.
+POS defaults to `point'."
+ (save-excursion
+ (when pos
+ (goto-char pos))
+ (package-lint--error (line-number-at-pos) (- (point) (line-beginning-position)) type message)))
+
+(defun package-lint--error-at-bol (type message)
+ "Construct an error at the beginning of the current line with TYPE and MESSAGE."
+ (package-lint--error (line-number-at-pos) 0 type message))
+
+(defun package-lint--error-at-bob (type message)
+ "Construct an error at the beginning of the buffer with TYPE and MESSAGE."
+ (package-lint--error 1 0 type message))
+
+\f
+;;; Checks
+
+(defun package-lint--check-reserved-keybindings ()
+ "Warn about reserved keybindings."
+ (let ((re (rx "(" (*? space) (or "kbd" "global-set-key" "local-set-key" "define-key") symbol-end)))
+ (goto-char (point-min))
+ (while (re-search-forward re nil t)
+ (unless (nth 8 (save-match-data (syntax-ppss)))
+ ;; Read form and get key-sequence
+ (goto-char (match-beginning 0))
+ (let ((seq (package-lint--extract-key-sequence
+ (read (current-buffer)))))
+ (when seq
+ (let ((message (package-lint--test-keyseq seq)))
+ (when message
+ (package-lint--error-at-point 'error message)))))))))
+
+(defun package-lint--check-commentary-existence ()
+ "Warn about nonexistent or empty commentary section."
+ (let ((start (lm-commentary-start)))
+ (if (null start)
+ (package-lint--error-at-bob
+ 'error
+ "Package should have a ;;; Commentary section.")
+ ;; Skip over the section header.
+ (goto-char start)
+ (forward-line)
+ (when (package-lint--region-empty-p (point) (lm-commentary-end))
+ (package-lint--error-at-point
+ 'error
+ "Package should have a non-empty ;;; Commentary section."
+ start)))))
+
+(defun package-lint--check-autoloads-on-private-functions (definitions)
+ "Verify that private functions DEFINITIONS don't have autoload cookies."
+ (pcase-dolist (`(,symbol . ,position) definitions)
+ (when (string-match-p (rx "--") symbol)
+ (goto-char position)
+ (forward-line -1)
+ (when (looking-at-p (rx ";;;###autoload"))
+ (package-lint--error-at-point
+ 'warning
+ "Private functions generally should not be autoloaded.")))))
+
+(defun package-lint--check-for-literal-emacs-path ()
+ "Verify package does not refer to \"\.emacs\.d\" literally.
+Instead it should use `user-emacs-directory' or `locate-user-emacs-file'."
+ (goto-char (point-min))
+ ;; \b won't find a word boundary between a symbol and the "." in
+ ;; ".emacs.d". / is a valid symbol constituent in Emacs Lisp, so
+ ;; must be explicitly blacklisted.
+ (while (re-search-forward "\\(?:\\_<\\|/\\)\\.emacs\\.d\\b" nil t)
+ (unless (nth 4 (syntax-ppss))
+ ;; Not in a comment
+ (package-lint--error-at-point
+ 'warning
+ "Use variable `user-emacs-directory' or function `locate-user-emacs-file' instead of a literal path to the Emacs user directory or files."))))
+
+(defun package-lint--check-keywords-list ()
+ "Verify that package keywords are listed in `finder-known-keywords'."
+ (when (package-lint--goto-header "Keywords")
+ (let ((err-pos (point)))
+ (let ((keywords (lm-keywords-list)))
+ (unless (cl-some (lambda (keyword) (assoc (intern keyword) finder-known-keywords)) keywords)
+ (package-lint--error-at-point
+ 'warning
+ (format "You should include standard keywords: see the variable `finder-known-keywords'.")
+ err-pos))))))
+
+(defun package-lint--check-url-header ()
+ "Verify that the package has an HTTPS or HTTP Homepage/URL header."
+ (if (package-lint--goto-header "\\(?:URL\\|Homepage\\)")
+ (progn
+ (when (looking-at "<.*>$")
+ (forward-char 1))
+ (let ((url (thing-at-point 'url)))
+ (unless (and url (string-match-p "^https?://" url))
+ (package-lint--error-at-point
+ 'error
+ "Package URLs should be a single HTTPS or HTTP URL."
+ (point)))))
+ (package-lint--error-at-bob
+ 'error
+ "Package should have a Homepage or URL header.")))
+
+(defun package-lint--check-dependency-list ()
+ "Check the contents of the \"Package-Requires\" header.
+Return a list of well-formed dependencies, same as
+`package-lint--check-well-formed-dependencies'."
+ (let ((deps (package-lint--goto-header "Package-Requires")))
+ (when deps
+ (let ((position (point)))
+ (condition-case err
+ (pcase-let ((`(,parsed-deps . ,parse-end-pos) (read-from-string deps)))
+ (unless (= parse-end-pos (length deps))
+ (package-lint--error-at-bol
+ 'error
+ "More than one expression provided."))
+ (let ((deps (package-lint--check-well-formed-dependencies position parsed-deps)))
+ (package-lint--check-emacs-version deps)
+ (package-lint--check-packages-installable deps)
+ (package-lint--check-deps-use-non-snapshot-version deps)
+ (package-lint--check-deps-do-not-use-zero-versions deps)
+ (package-lint--check-cl-lib-version deps)
+ deps))
+ (error
+ (package-lint--error-at-bol
+ 'error
+ (format "Couldn't parse \"Package-Requires\" header: %s" (error-message-string err)))
+ nil))))))
+
+(defun package-lint--check-well-formed-dependencies (position parsed-deps)
+ "Check that dependencies listed at POSITION are well-formed.
+These PARSED-DEPS must have the format (name \"version\").
+Return a list of well-formed dependencies, where each element is of
+the form (PACKAGE-NAME PACKAGE-VERSION DEP-POSITION)."
+ (let (valid-deps)
+ (dolist (entry parsed-deps)
+ (pcase entry
+ ((or (and `(,package-name ,package-version)
+ (guard (symbolp package-name))
+ (guard (stringp package-version)))
+ (and `(,package-name)
+ (guard (symbolp package-name)))
+ (and package-name
+ (guard (symbolp package-name))))
+ ;; Find the column at which the dependency is declared so we can
+ ;; properly report the position of errors.
+ (let ((dep-pos
+ (save-excursion
+ (goto-char position)
+ (let* ((symbol-pattern
+ (format "\\(%s\\)"
+ (regexp-quote (symbol-name package-name))))
+ (list-pattern
+ (format "( *%s\\(?:[^[:alnum:]_\\-].*?\\)?)"
+ symbol-pattern)))
+ (if (or (re-search-forward list-pattern (line-end-position) t)
+ (re-search-forward symbol-pattern (line-end-position) t))
+ (match-beginning 1)
+ position)))))
+ (unless package-version
+ (setq package-version "0"))
+ (if (ignore-errors (version-to-list package-version))
+ (push (list package-name
+ (version-to-list package-version)
+ dep-pos)
+ valid-deps)
+ (package-lint--error-at-point
+ 'error
+ (format "%S is not a valid version string: see `version-to-list'."
+ package-version)
+ dep-pos))))
+ (_
+ (package-lint--error-at-bol
+ 'error
+ (format "Expected (package-name \"version-num\"), but found %S." entry)))))
+ valid-deps))
+
+(defun package-lint--check-package-installable (archive-entry package-version dep-pos)
+ "Check that ARCHIVE-ENTRY is installable from a configured package archive.
+
+Check that package described by ARCHIVE-ENTRY can be installed at
+required version PACKAGE-VERSION. If not, raise an error for DEP-POS."
+ (let* ((package-name (car archive-entry))
+ (best-version (package-lint--highest-installable-version-of package-name)))
+ (when (version-list-< best-version package-version)
+ (package-lint--error-at-point
+ 'warning
+ (format "Version dependency for %s appears too high: try %s" package-name
+ (package-version-join best-version))
+ dep-pos))))
+
+(defun package-lint--check-emacs-version (valid-deps)
+ "Check that all VALID-DEPS are available for installation."
+ (pcase-dolist (`(,package-name ,package-version ,dep-pos) valid-deps)
+ (when (eq 'emacs package-name)
+ (cond
+ ((version-list-< package-version '(24))
+ (package-lint--error-at-point
+ 'error
+ "You can only depend on Emacs version 24 or greater: package.el for Emacs 23 does not support the \"emacs\" pseudopackage."
+ dep-pos))
+ ((version-list-<= package-lint-emacs-head-version package-version)
+ (package-lint--error-at-point
+ 'warning
+ "This makes the package uninstallable in all released Emacs versions."
+ dep-pos))))))
+
+(defun package-lint--check-packages-installable (valid-deps)
+ "Check that all VALID-DEPS are available for installation."
+ (pcase-dolist (`(,package-name ,package-version ,dep-pos) valid-deps)
+ (unless (eq 'emacs package-name)
+ (let ((archive-entry (append (assq package-name package-archive-contents)
+ (assq package-name package-alist))))
+ (if archive-entry
+ (package-lint--check-package-installable archive-entry package-version dep-pos)
+ (package-lint--error-at-point
+ 'error
+ (format "Package %S is not installable." package-name)
+ dep-pos))))))
+
+(defun package-lint--check-deps-use-non-snapshot-version (valid-deps)
+ "Warn about any VALID-DEPS on snapshot versions of packages."
+ (pcase-dolist (`(,package-name ,package-version ,dep-pos) valid-deps)
+ (unless (version-list-< package-version '(19001201 1))
+ (package-lint--error-at-point
+ 'warning
+ (format "Use a non-snapshot version number for dependency on \"%S\" if possible."
+ package-name)
+ dep-pos))))
+
+(defun package-lint--check-deps-do-not-use-zero-versions (valid-deps)
+ "Warn about VALID-DEPS on \"0\" versions of packages."
+ (pcase-dolist (`(,package-name ,package-version ,dep-pos) valid-deps)
+ (when (equal package-version '(0))
+ (package-lint--error-at-point
+ 'warning
+ (format "Use a properly versioned dependency on \"%S\" if possible."
+ package-name)
+ dep-pos))))
+
+(defun package-lint--check-lexical-binding-requires-emacs-24 (valid-deps)
+ "Warn about use of `lexical-binding' when Emacs 24.1 is not among VALID-DEPS."
+ (goto-char (point-min))
+ (when (package-lint--lexical-binding-declared-in-header-line-p)
+ (unless (assq 'emacs valid-deps)
+ (package-lint--error-at-point
+ 'warning
+ "You should depend on (emacs \"24.1\") if you need lexical-binding."
+ (match-beginning 1)))))
+
+(defun package-lint--inside-comment-or-string-p ()
+ "Return non-nil if point is inside a comment or string."
+ (let ((ppss (save-match-data (syntax-ppss))))
+ (or (nth 3 ppss) (nth 4 ppss))))
+
+(defun package-lint--seen-fboundp-check-for (sym)
+ "Return non-nil if a `fboundp' check for SYM is present before point."
+ (save-excursion
+ (save-match-data
+ (and (re-search-backward
+ (concat "(" (regexp-opt '("fboundp" "macrop")) "\\s-+#?'" (regexp-quote sym) "\\_>") (point-min) t)
+ (not (package-lint--inside-comment-or-string-p))))))
+
+(defun package-lint--map-regexp-match (regexp callback)
+ "For every match of REGEXP, call CALLBACK with the first match group.
+If callback returns non-nil, the return value - which must be a
+list - will be applied to `package-lint--error-at-point'. If
+REGEXP doesn't produce a match group 1, then match group
+0 (ie. the whole match string string) will be passed to
+CALLBACK."
+ (save-excursion
+ (goto-char (point-min))
+ (while (re-search-forward regexp nil t)
+ (let ((sym (or (match-string-no-properties 1)
+ (match-string-no-properties 0))))
+ (save-excursion
+ (goto-char (or (match-beginning 1)
+ (match-beginning 0)))
+ (let ((err (funcall callback sym)))
+ (when err
+ ;; Check this as late as possible, just before reporting,
+ ;; because otherwise the checking process is extremely slow,
+ ;; being bottlenecked by `syntax-ppss'.
+ (unless (package-lint--inside-comment-or-string-p)
+ (apply #'package-lint--error-at-point err)))))))))
+
+(defun package-lint--quick-up-list ()
+ (if (eq ?\( (char-before))
+ (backward-char 1)
+ (backward-up-list)))
+
+(defun package-lint--is-a-let-binding ()
+ "Return non-nil if point is on a symbol being introduced by a let binding."
+ (let ((binding-pat (rx "(" (or "let" "let*" "cl-let") symbol-end)))
+ (condition-case _
+ (save-excursion
+ (save-match-data
+ (let ((was-first (eq ?\( (char-before))))
+ (package-lint--quick-up-list)
+ (package-lint--quick-up-list)
+ (or (looking-at binding-pat)
+ (when was-first
+ (package-lint--quick-up-list)
+ (looking-at binding-pat))))))
+ (scan-error nil))))
+
+(defun package-lint--check-version-regexp-list (valid-deps symbol-regexp type)
+ "Warn if symbols matched by SYMBOL-REGEXP are unavailable in the target Emacs.
+The target Emacs version is taken from VALID-DEPS, which are the
+declared dependencies of this package. TYPE is the
+type of the symbol, either FUNCTION or FEATURE."
+ (let ((emacs-version-dep (or (cadr (assq 'emacs valid-deps)) '(0))))
+ (package-lint--map-regexp-match
+ symbol-regexp
+ (lambda (sym)
+ (let-alist (package-lint-symbol-info sym)
+ (let ((added-in-version (pcase type
+ (`function .function-added)
+ (`feature .library-added))))
+ (when (and added-in-version (version-list-< emacs-version-dep added-in-version))
+ (let* ((available-backport-with-ver
+ (pcase type
+ (`feature
+ (cl-some (lambda (bp)
+ (when (string= (car bp) sym)
+ (or (cddr bp)
+ (list (car bp)))))
+ package-lint-backport-libraries))
+ (`function
+ (cl-some (lambda (bp)
+ (when (string-match-p (nth 1 bp) sym)
+ (or (cddr bp)
+ (list (car bp)))))
+ package-lint-backport-libraries))))
+ (available-backport (car available-backport-with-ver))
+ (required-backport-version (cadr available-backport-with-ver))
+ (matching-dep (when available-backport
+ (assoc available-backport valid-deps)))
+ (compat-support (package-lint--supported-by-compat type (intern sym)))
+ (compat-in-deps (assoc 'compat valid-deps)))
+ (unless (or
+ (and compat-support compat-in-deps)
+ (and matching-dep
+ (or (not required-backport-version)
+ (version-list-<= (version-to-list required-backport-version)
+ (cadr matching-dep))))
+ (and (eq type 'function)
+ (or (package-lint--seen-fboundp-check-for sym)
+ (package-lint--is-a-let-binding))))
+ (list
+ 'error
+ (format "You should depend on (emacs \"%s\")%s%s if you need `%s'."
+ (mapconcat #'number-to-string added-in-version ".")
+ (if available-backport
+ (format " or the %s package"
+ (if required-backport-version
+ (format "(%s \"%s\")"
+ available-backport
+ required-backport-version)
+ available-backport))
+ "")
+ (if compat-support " or the compat package" "")
+ sym)))))))))))
+
+(defun package-lint--check-eval-after-load ()
+ "Warn about use of `eval-after-load' and co."
+ (package-lint--map-regexp-match
+ "(\\s-*?\\(\\(?:with-\\)?eval-after-load\\)\\_>"
+ (lambda (match)
+ (list 'warning
+ (format "`%s' is for use in configurations, and should rarely be used in packages." match)))))
+
+(defun package-lint--check-no-use-of-cl ()
+ "Warn about use of deprecated `cl' library."
+ (package-lint--map-regexp-match
+ "(\\s-*?require\\s-*?'\\(cl\\)\\_>"
+ (lambda (_)
+ (list
+ 'warning
+ "Replace deprecated `cl' with `cl-lib'. The `cl-libify' package can help with this."))))
+
+(defun package-lint--check-no-use-of-cl-lib-sublibraries ()
+ "Warn about use of `cl-macs', `cl-seq' etc."
+ (package-lint--map-regexp-match
+ "(\\s-*?require\\s-*?'\\(cl-macs\\|cl-seq\\)\\_>"
+ (lambda (_)
+ (list
+ 'warning
+ "This file is not in the `cl-lib' ELPA compatibility package: require `cl-lib' instead."))))
+
+(defconst package-lint--unconditional-require-regexp
+ ;; Match the ending paren so we can be sure it's a single argument
+ ;; `require'. If there are additional arguments, we don't want to warn,
+ ;; because (require 'foo nil t) indicates an optional dependency and
+ ;; (require 'foo "filename") is very uncommon.
+ "(\\s-*?require\\s-*?'\\_<\\(.*?\\)\\_>\\s-*?)"
+ "Regexp to match unconditional `require' forms.")
+
+(defun package-lint--check-libraries-available-in-emacs (valid-deps)
+ "Warn about use of libraries that are not available.
+The check is performed against the Emacs version in VALID-DEPS."
+ (package-lint--check-version-regexp-list
+ valid-deps
+ package-lint--unconditional-require-regexp
+ 'feature))
+
+(defun package-lint--check-libraries-removed-from-emacs ()
+ "Warn about use of libraries that have been removed from Emacs."
+ (package-lint--map-regexp-match
+ package-lint--unconditional-require-regexp
+ (lambda (sym)
+ (let-alist (package-lint-symbol-info sym)
+ (let ((removed-in-version .library-removed))
+ (when removed-in-version
+ (list
+ 'error
+ (format "The `%s' library was removed in Emacs version %s."
+ sym (mapconcat #'number-to-string removed-in-version ".")))))))))
+
+(defconst package-lint--function-name-regexp
+ (rx
+ (seq
+ (or "#'"
+ (seq "(" (* (syntax whitespace))
+ (? (seq
+ (or "funcall" "apply" "advice-add")
+ (+
+ (syntax whitespace))
+ "'"))))
+ (group
+ (*\? nonl))
+ symbol-end))
+ "Regexp to match function names.")
+
+(defun package-lint--check-macros-functions-available-in-emacs (valid-deps)
+ "Warn about use of unavailable functions/macros that are not available.
+The check is performed against the Emacs version in VALID-DEPS."
+ (package-lint--check-version-regexp-list
+ valid-deps
+ package-lint--function-name-regexp
+ 'function))
+
+(defun package-lint--check-macros-functions-removed-from-emacs (valid-deps)
+ "Warn about use of functions/macros that have been removed from Emacs.
+If an Emacs version is specified in VALID-DEPS, don't warn about
+functions/macros that were removed and later re-added, as long as
+the Emacs dependency matches the re-addition."
+ (package-lint--map-regexp-match
+ package-lint--function-name-regexp
+ (lambda (sym)
+ (let-alist (package-lint-symbol-info sym)
+ (let ((removed-in-version .function-removed))
+ (when removed-in-version
+ (let ((emacs-version-dep (or (cadr (assq 'emacs valid-deps)) '(0))))
+ (unless (or (package-lint--seen-fboundp-check-for sym)
+ (and .function-added (version-list-<= .function-added emacs-version-dep)))
+ (list
+ 'error
+ (format "`%s' was removed in Emacs version %s."
+ sym (mapconcat #'number-to-string removed-in-version ".")))))))))))
+
+(defun package-lint--check-lexical-binding-is-on-first-line ()
+ "Check that any `lexical-binding' declaration is on the first line of the file."
+ (cl-block return
+ (let ((original-buffer (current-buffer)))
+ (with-temp-buffer
+ (let ((lexical-binding-found-at-end nil))
+ (insert-buffer-substring-no-properties original-buffer)
+ (condition-case err
+ (cl-letf (((symbol-function #'hack-local-variables-apply) #'ignore)
+ ((symbol-function #'hack-local-variables-filter)
+ (lambda (variables _dir-name)
+ (setq file-local-variables-alist variables)))
+ ;; Silence any messages Emacs may want to share with the user.
+ ;; There's no user.
+ ((symbol-function #'display-warning) #'ignore)
+ ((symbol-function #'message) #'ignore))
+ ;; HACK: this is an internal variable!
+ ;; Unfortunately, Emacsen that have this variable also have
+ ;; `hack-local-variables' that doesn't store `lexical-binding'
+ ;; in `file-local-variables-alist'.
+ (defvar enable-dir-local-variables)
+ (defvar hack-local-variables--warned-lexical)
+ (let ((hack-local-variables--warned-lexical nil)
+ (enable-dir-local-variables nil)
+ (enable-local-variables t)
+ (local-enable-local-variables t))
+ (hack-local-variables)
+ (setq lexical-binding-found-at-end
+ hack-local-variables--warned-lexical)))
+ (error
+ (package-lint--error-at-bob 'error (error-message-string err))
+ (cl-return-from return nil)))
+ (when (or lexical-binding-found-at-end
+ ;; In case this is an Emacs from before `hack-local-variables'
+ ;; started to warn about `lexical-binding' on a line other
+ ;; than the first.
+ (and (cdr (assq 'lexical-binding file-local-variables-alist))
+ (not (package-lint--lexical-binding-declared-in-header-line-p))))
+ (package-lint--error-at-bob
+ 'error
+ "`lexical-binding' must be set in the first line.")))))))
+
+(defun package-lint--check-cl-lib-version (valid-deps)
+ "Check dependencies in VALID-DEPS on \"cl-lib\".
+The specified versions must be remotely available for installation."
+ (let ((emacs-version-dep (or (nth 1 (assq 'emacs valid-deps)) '(0)))
+ (cl-lib-dep (assq 'cl-lib valid-deps)))
+ (when cl-lib-dep
+ (let ((cl-lib-version (nth 1 cl-lib-dep)))
+ (cond ((and emacs-version-dep
+ (version-list-<= '(24 3) emacs-version-dep)
+ (version-list-<= cl-lib-version '(1 0)))
+ (package-lint--error-at-point
+ 'warning
+ "An explicit dependency on cl-lib <= 1.0 is not needed on Emacs >= 24.3."))
+ ((version-list-<= '(1) cl-lib-version)
+ (package-lint--error-at-point
+ 'error
+ (format "Depend on the latest 0.x version of cl-lib rather than on version \"%S\".
+Alternatively, depend on (emacs \"24.3\") or greater, in which cl-lib is bundled."
+ cl-lib-version)
+ (nth 2 cl-lib-dep))))))))
+
+(defun package-lint--check-package-version-present ()
+ "Check that a valid \"Version\" header is present."
+ (let ((version (package-lint--goto-header (rx (? "Package-") "Version"))))
+ (if version
+ (unless (ignore-errors (version-to-list version))
+ (package-lint--error-at-point
+ 'warning
+ (format "\"%s\" is not a valid version. MELPA will handle this, but other archives will not." version)
+ (point)))
+ (package-lint--error-at-bob
+ 'warning
+ "\"Version:\" or \"Package-Version:\" header is missing. MELPA will handle this, but other archives will not."))))
+
+(defun package-lint--liberal-package-buffer-info ()
+ "Like `package-buffer-info', but tolerate missing version header."
+ (let ((orig-buffer (current-buffer)))
+ ;; We've reported version header issues separately, so rule them out here
+ (with-temp-buffer
+ (insert-buffer-substring-no-properties orig-buffer)
+ (goto-char (point-min))
+ (package-lint--update-or-insert-version "0")
+ (let ((info (package-buffer-info)))
+ ;; Pedantically check for the footer comment
+ ;; In Emacs 29 this became a soft failure, but for packages to
+ ;; be installable with the package.el in older Emacsen, the
+ ;; footer comment must be there nonetheless.
+ (goto-char (point-min))
+ (search-forward (format ";;; %s.el ends here" (package-lint--package-desc-name info)))
+ info))))
+
+(defun package-lint--check-package-el-can-parse ()
+ "Check that `package-buffer-info' can read metadata from this file.
+If it can, return the read metadata."
+ (condition-case err
+ (package-lint--liberal-package-buffer-info)
+ (error
+ (package-lint--error-at-bob
+ 'error
+ (format "package.el cannot parse this buffer: %s" (error-message-string err)))
+ nil)))
+
+(defun package-lint--check-package-summary (desc)
+ "Check the summary for package with descriptor DESC.
+DESC is a struct as returned by `package-buffer-info'."
+ (let ((summary (package-lint--package-desc-summary desc)))
+ (cond
+ ((string= summary "")
+ (package-lint--error-at-bob
+ 'error
+ "Package should have a non-empty summary."))
+ (t
+ (unless (let ((case-fold-search nil))
+ (string-match-p "^[A-Z0-9]" summary))
+ (package-lint--error-at-bob
+ 'warning
+ "The package summary should start with an uppercase letter or a digit."))
+ (when (> (length summary) 60)
+ (package-lint--error-at-bob
+ 'warning
+ "The package summary is too long. It should be at most 60 characters."))
+ (when (string-match "\\.\\'" summary)
+ (package-lint--error-at-bob
+ 'warning
+ "The package summary should not end with a period."))
+ (when (save-match-data
+ (let ((case-fold-search t))
+ (and (string-match "[^.]\\<emacs\\>" summary)
+ (not (string-match-p "[[:space:]]+lisp"
+ summary (match-end 0))))))
+ (package-lint--error-at-bob
+ 'warning
+ "Including \"Emacs\" in the package summary is usually redundant."))))))
+
+(defun package-lint--check-provide-form (desc)
+ "Check the provide form for package with descriptor DESC.
+DESC is a struct as returned by `package-buffer-info'."
+ (let ((name (symbol-name (package-lint--package-desc-name desc))))
+ (if (string-match-p "-theme\\'" name)
+ (let ((theme-name (replace-regexp-in-string "-theme\\'" "" name))
+ (provided-theme (package-lint--provided-theme)))
+ (unless (string-equal theme-name provided-theme)
+ (package-lint--error-at-bob
+ 'error
+ (format "There is no (provide-theme '%s) form." theme-name))))
+ (let ((feature (package-lint--provided-feature)))
+ (unless (string-equal name feature)
+ (package-lint--error-at-bob
+ 'error
+ (format "There is no (provide '%s) form." name)))))))
+
+(defun package-lint--check-provide-form-secondary-file ()
+ "Check there is a provide form."
+ ;; We don't require that the provided feature have a name consistent
+ ;; with the overall package prefix, but this check may later be added.
+ (unless (package-lint--provided-feature)
+ (package-lint--error-at-bob
+ 'error
+ "There is no `provide' form.")))
+
+(defun package-lint--check-no-emacs-in-package-name (desc)
+ "Check that the package name doesn't contain \"emacs\".
+DESC is a struct as returned by `package-buffer-info'."
+ (let ((name (package-lint--package-desc-name desc)))
+ (when (string-match-p "emacs" (symbol-name name))
+ (package-lint--error-at-bob
+ 'warning
+ "The word \"emacs\" is redundant in Emacs package names."))))
+
+(defun package-lint--check-symbol-separators (definitions)
+ "Check that symbol DEFINITIONS don't contain non-standard separators."
+ (pcase-dolist (`(,name . ,position) definitions)
+ (when (and (string-match "[:/]" name)
+ (not (string-match-p package-lint--sane-prefixes name)))
+ (let ((match-pos (match-beginning 0)))
+ ;; As a special case, allow `/=' or `/' when at the end of a symbol.
+ (when (or (not (string-match (rx "/" (optional "=") string-end) name))
+ (/= match-pos (match-beginning 0)))
+ (goto-char position)
+ (package-lint--error
+ (line-number-at-pos) 0 'error
+ (format "`%s' contains a non-standard separator `%s', use hyphens instead (see Elisp Coding Conventions)."
+ name (substring-no-properties name match-pos (1+ match-pos)))))))))
+
+(defun package-lint--valid-prefix-mapping-p (short-prefix prefix name)
+ "Check if NAME corresponds to a valid mappings for PREFIX.
+Check is done according to the definitions in
+`package-lint--allowed-prefix-mappings' for SHORT-PREFIX."
+ (let ((regexp (concat "^" (regexp-opt
+ (cdr (assoc short-prefix package-lint--allowed-prefix-mappings))))))
+ (string-prefix-p prefix (replace-regexp-in-string regexp short-prefix name))))
+
+(defun package-lint--valid-definition-name-p (name prefix &optional position)
+ "Return non-nil if NAME denotes a valid definition name.
+
+Valid definition names are:
+
+- a NAME starting with PREFIX, a string representing the current
+ package prefix,
+
+- a NAME matching `package-lint--sane-prefixes', or
+
+- a NAME whose POSITION in the buffer denotes a global definition."
+ (or (string-match-p (rx-to-string `(seq string-start ,prefix (or string-end (not alpha)))) name)
+ (string-match-p package-lint--sane-prefixes name)
+ (string-match-p (rx-to-string `(seq string-start (or "define" "defun" "defvar" "defface" "with") "-" ,prefix)) name)
+ (string-match-p (rx-to-string `(seq string-start "global-" ,prefix (or "-mode" (seq "-" (* any) "-mode")) string-end)) name)
+ (let ((short-prefix (car (cl-remove-if-not (lambda (e) (string-prefix-p e prefix))
+ (mapcar #'car package-lint--allowed-prefix-mappings)))))
+ (when short-prefix
+ (package-lint--valid-prefix-mapping-p short-prefix prefix name)))
+ (when position
+ (goto-char position)
+ (looking-at-p (rx (*? space) "(" (*? space)
+ (or "defadvice" "cl-defmethod" "define-advice" "defalias" "defvaralias" (seq "define-obsolete-" (or "face" "function" "variable") "-alias"))
+ symbol-end)))))
+
+(defun package-lint--check-defs-prefix (prefix definitions)
+ "Verify that symbol DEFINITIONS start with package PREFIX."
+ (pcase-dolist (`(,name . ,position) definitions)
+ (unless (package-lint--valid-definition-name-p name prefix position)
+ (if (package-lint--added-or-removed-function-p name)
+ (package-lint--error-at-point
+ 'error
+ (format "Define compatibility functions with a prefix, e.g. \"%s--%s\", and use `defalias' where they exist."
+ prefix name)
+ position)
+ (package-lint--error-at-point
+ 'error
+ (format "\"%s\" doesn't start with package's prefix \"%s\"."
+ name prefix)
+ position)))))
+
+(defun package-lint--check-minor-mode (def)
+ "Offer up concerns about the minor mode definition DEF."
+ (when (cl-search '(:global t) def)
+ (package-lint--check-globalized-minor-mode def)))
+
+(defun package-lint--check-globalized-minor-mode (def)
+ "Offer up concerns about the global minor mode definition DEF."
+ (let ((feature-name (package-lint--provided-feature)))
+ (when feature-name
+ (let ((feature (intern feature-name))
+ (autoloaded (save-excursion
+ (forward-line -1)
+ (beginning-of-line)
+ (looking-at ";;;###autoload"))))
+ (unless (or autoloaded
+ (cl-search `(:require ',feature) def :test #'equal))
+ (package-lint--error-at-point
+ 'error
+ (format
+ "Global minor modes should be autoloaded or, rarely, `:require' their defining file (i.e. \":require '%s\"), to support the customization variable of the same name." feature)))))))
+
+(defun package-lint--check-defgroup (def)
+ "Offer up concerns about the customization group definition DEF."
+ (when (symbolp (cadr def))
+ (let ((group-name (symbol-name (cadr def))))
+ (when (string-match "\\(.*\\)-mode$" group-name)
+ (let ((parent (intern (match-string 1 group-name))))
+ (unless (cl-search `(:group ',parent) def :test #'equal)
+ (package-lint--error-at-point
+ 'error
+ "Customization groups should not end in \"-mode\" unless that name would conflict with their parent group."))))))
+
+ (unless (memq :group def)
+ (package-lint--error-at-point
+ 'error
+ "Customization groups should specify a parent via `:group'.")))
+
+(defun package-lint--check-defalias (prefix def)
+ "Offer up concerns about the customization group definition DEF.
+PREFIX is the package prefix."
+ (pcase (cadr def)
+ ((and `(quote ,alias) (guard (symbolp alias)))
+ (unless (package-lint--valid-definition-name-p (symbol-name alias) prefix)
+ (package-lint--error-at-point
+ 'error
+ (concat "Aliases should start with the package's prefix \"" prefix "\"."))))))
+
+(defun package-lint--check-format-string (valid-deps def)
+ "Offer up concerns about the format string used in DEF, depending on VALID-DEPS."
+ (let ((emacs-version-dep (or (cadr (assq 'emacs valid-deps)) '(0))))
+ (when (consp (cdr def))
+ (let ((fmt-str (cadr def)))
+ (when (and (version-list-< emacs-version-dep '(26 1))
+ ;; We give up on trying to warn about format strings that are
+ ;; evaluated at runtime.
+ (stringp fmt-str)
+ ;; The usual regexp strategy for finding unescaped matches
+ ;; requires negative lookbehind:
+ ;; (?<!%)(?:%%)*[0-9]+\$
+ ;; So instead we make sure the count of escape chars is odd
+ (string-match "\\(%+\\)[0-9]+\\$" fmt-str)
+ (cl-oddp (length (match-string 1 fmt-str))))
+ (package-lint--error-at-point
+ 'error
+ "You should depend on (emacs \"26.1\") if you need format field numbers."))))))
+
+(defun package-lint--check-lonely-parens ()
+ "Warn about dangling closing parens."
+ (package-lint--map-regexp-match
+ "^\\s-*?\\()\\)"
+ (lambda (_)
+ ;; Allow dangling parentheses if the preceding line ends with a comment, as
+ ;; it's not uncommon even in idiomatic lisp.
+ (when (save-excursion
+ (end-of-line 0)
+ (not (nth 4 (syntax-ppss))))
+ (list 'warning
+ "Closing parens should not be wrapped onto new lines.")))))
+
+
+\f
+;;; Helpers
+
+(defun package-lint--extract-key-sequence (form)
+ "Extract the key sequence from FORM."
+ (pcase form
+ (`(kbd ,seq)
+ (package-lint--extract-key-sequence seq))
+ ((or `(global-set-key ,seq ,_) `(local-set-key ,seq ,_))
+ (package-lint--extract-key-sequence seq))
+ (`(define-key ,_ ,seq ,_)
+ (package-lint--extract-key-sequence seq))
+ ((pred stringp)
+ (listify-key-sequence (read-kbd-macro form)))
+ ((pred vectorp)
+ (unless (listp (elt form 0))
+ (listify-key-sequence form)))))
+
+(defun package-lint--test-keyseq (lks)
+ "Return a message if the listified key sequence LKS is invalid, otherwise nil."
+ (let* ((modifiers (event-modifiers lks))
+ (basic-type (event-basic-type lks)))
+ (when (or (and (> (length lks) 1) (equal (car (last lks)) ?\C-g))
+ (and (equal (car (last lks)) ?\e)
+ (not (equal (nthcdr (- (length lks) 2) lks)
+ '(?\e ?\e))))
+ (equal (car (last lks)) ?\C-h)
+ (and (equal modifiers '(control))
+ (equal ?c basic-type)
+ (cdr lks)
+ (let ((v (event-basic-type (cdr lks)))
+ (m (event-modifiers (cdr lks))))
+ (and (numberp v)
+ (<= v ?z)
+ (>= v ?a)
+ (or (null m) (equal '(shift) m)))))
+ (member basic-type '(f5 f6 f7 f8 f9)))
+ "This key sequence is reserved (see Key Binding Conventions in the Emacs Lisp manual)")))
+
+(defun package-lint--region-empty-p (start end)
+ "Return t iff the region between START and END has no non-empty lines.
+
+Lines consisting only of whitespace or empty comments are considered empty."
+ (save-excursion
+ (save-restriction
+ (save-match-data
+ (narrow-to-region start end)
+ (goto-char start)
+ (while (and (looking-at "^[[:space:]]*;*[[:space:]]*$")
+ (= 0 (forward-line))))
+ (eobp)))))
+
+(defun package-lint--highest-installable-version-of (package)
+ "Return the highest version of PACKAGE available for installation."
+ (let ((descriptors (append (cdr (assq package package-archive-contents))
+ (cdr (assq package package-alist)))))
+ (if (fboundp 'package-desc-version)
+ (car (sort (mapcar 'package-desc-version descriptors)
+ (lambda (v1 v2) (not (version-list-< v1 v2)))))
+ (aref descriptors 0))))
+
+(defun package-lint--goto-header (header-name &optional multiline)
+ "Move to the first occurrence of HEADER-NAME in the file.
+If the return value is non-nil, then point will be at the
+beginning of the header's value, and the second and third match
+groups will contain the name and value of the header with any
+leading or trailing whitespace removed.
+
+If MULTILINE is non-nil, allow the header value to span lines, and return
+them as a list of strings."
+ (goto-char (point-min))
+ (when (re-search-forward (concat (lm-get-header-re header-name) "\\(.*?\\) *$") (lm-code-mark) t)
+ (let ((start-pos (match-beginning 3))
+ (val (match-string-no-properties 3)))
+ (when multiline
+ (setq val (list val))
+ (forward-line 1)
+ (while (looking-at "^;+\\(\t\\|[\t\s]\\{2,\\}\\)\\(.+\\)")
+ (push (match-string-no-properties 2) val)
+ (forward-line 1))
+ (nreverse val))
+ (goto-char start-pos)
+ val)))
+
+(defun package-lint--update-or-insert-version (version)
+ "Ensure current buffer has a \"Version: VERSION\" header."
+ (if (package-lint--goto-header "Version")
+ (move-beginning-of-line nil)
+ (forward-line))
+ (insert (format ";; Version: %s" version))
+ (newline))
+
+(defun package-lint--get-header-line-file-local-variables ()
+ "Return local variables specified in the -*- line.
+Returns an alist of elements (VAR . VAL), where VAR is a variable
+and VAL is the specified value.
+
+For details, see `hack-local-variables-prop-line'."
+ (cl-letf (((symbol-function #'message) #'ignore))
+ (hack-local-variables-prop-line)))
+
+(defun package-lint--lexical-binding-declared-in-header-line-p ()
+ "Test if `lexical-binding' is declared in the -*- line."
+ ;; Test the `cdr' to see if it's actually true, because
+ ;; -*- lexical-binding: nil -*-
+ ;; is legal, if silly.
+ (cdr (assq 'lexical-binding (package-lint--get-header-line-file-local-variables))))
+
+(defvar semantic-imenu-summary-function)
+
+(defun package-lint--get-defs ()
+ "Return a list of all variables and functions defined in the current buffer.
+
+The returned list is of the form (SYMBOL-NAME . POSITION)."
+ ;; We probably could use Semantic instead, but it's a *global* minor mode and
+ ;; it tends to be quite heavy, so use Imenu instead; if the user has Semantic
+ ;; enabled, Imenu will use its index anyway.
+ (let ((result '())
+ (index
+ (save-excursion
+ ;; Use the default imenu expression list so that we're not confused
+ ;; by user customizations.
+ (let ((imenu-generic-expression (default-value 'lisp-imenu-generic-expression))
+ ;; In case it's actually Semantic, tell it not to decorate
+ ;; symbol names.
+ (semantic-imenu-summary-function 'semantic-format-tag-name))
+ (funcall imenu-create-index-function)))))
+ (dolist (entry index)
+ (pcase entry
+ ((and `(,submenu-name . ,submenu-elements)
+ (guard (consp submenu-elements)))
+ (when (member submenu-name '("Variables" "Defuns" "Types"))
+ (setq result (nconc (reverse submenu-elements) result))))
+ (_
+ (push entry result))))
+ ;; If it's Semantic, then it returns overlays, not positions. Convert
+ ;; them.
+ (dolist (entry result)
+ (when (overlayp (cdr entry))
+ (setcdr entry (overlay-start (cdr entry)))))
+ (nreverse result)))
+
+(defun package-lint--provided-feature ()
+ "Return the last-provided feature name, as a string, or nil if none."
+ (save-excursion
+ (goto-char (point-max))
+ (cond ((re-search-backward (rx "(provide '" (group (1+ (or (syntax word) (syntax symbol))))) nil t)
+ (match-string-no-properties 1))
+ ((re-search-backward "(provide-me)" nil t)
+ (file-name-sans-extension (file-name-nondirectory buffer-file-name))))))
+
+(defun package-lint--provided-theme ()
+ "Return the last-provided theme name, as a string, or nil if none."
+ (save-excursion
+ (goto-char (point-max))
+ (when (re-search-backward (rx "(provide-theme '" (group (1+ (or (syntax word) (syntax symbol))))) nil t)
+ (match-string-no-properties 1))))
+
+(defun package-lint--get-package-prefix ()
+ "Return package prefix string (i.e. the symbol the package `provide's).
+Prefix is returned without any `-mode' suffix."
+ (let ((feature (package-lint--provided-feature)))
+ (when feature
+ (replace-regexp-in-string "-mode\\'" "" feature))))
+
+(defun package-lint--check-objects-by-regexp (regexp function)
+ "Check all objects with the literal printed form matching REGEXP.
+
+The objects are parsed with `read'. The FUNCTION is passed the
+read object, with the point at the beginning of the match.
+
+S-expressions in comments or comments, partial s-expressions, or
+otherwise invalid read forms are ignored."
+ (goto-char (point-min))
+ (while (re-search-forward regexp nil t)
+ (save-excursion
+ (goto-char (match-beginning 0))
+ (let ((obj (unless (package-lint--inside-comment-or-string-p)
+ (save-excursion
+ (ignore-errors (read (current-buffer)))))))
+ (when obj (funcall function obj))))))
+
+\f
+;;; Public interface
+
+;;;###autoload
+(defun package-lint-buffer (&optional buffer)
+ "Get linter errors and warnings for BUFFER.
+
+Returns a list, each element of which is list of
+
+ (LINE COL TYPE MESSAGE)
+
+where TYPE is either `warning' or `error'.
+
+Current buffer is used if none is specified."
+ (with-current-buffer (or buffer (current-buffer))
+ (unless (derived-mode-p 'emacs-lisp-mode)
+ (error "Buffer must be in emacs-lisp-mode"))
+ (package-lint--check-all)))
+
+;;;###autoload
+(defun package-lint-current-buffer ()
+ "Display lint errors and warnings for the current buffer."
+ (interactive)
+ (let ((errs (package-lint-buffer))
+ (buf "*Package-Lint*"))
+ (with-current-buffer (get-buffer-create buf)
+ (let ((buffer-read-only nil))
+ (erase-buffer)
+ (cond
+ ((null errs) (insert "No issues found."))
+ ((null (cdr errs)) (insert "1 issue found:\n\n"))
+ (t (insert (format "%d issues found:\n\n" (length errs)))))
+ (pcase-dolist (`(,line ,col ,type ,message) errs)
+ (insert (format "%d:%d: %s: %s\n" line col type message))))
+ (special-mode)
+ (view-mode 1))
+ (display-buffer buf)))
+
+(defgroup package-lint nil
+ "A linting library for elisp package authors."
+ :group 'development)
+
+(defcustom package-lint-batch-fail-on-warnings t
+ "When non-nil, make warnings fatal for `package-lint-batch-and-exit'."
+ :group 'package-lint
+ :type 'boolean)
+
+(defun package-lint-batch-and-exit-1 (filenames)
+ "Internal helper function for `package-lint-batch-and-exit'.
+
+Checks FILENAMES using package-lint.
+
+The main loop is this separate function so it's easier to test."
+ ;; Make sure package.el is initialized so we can query its database.
+ (package-initialize)
+ (let ((success t) (last-directory nil) (text-quoting-style 'grave))
+ (dolist (file filenames success)
+ (let* ((file (expand-file-name file))
+ (file-directory (file-name-directory file))
+ (base (file-name-nondirectory file)))
+ (with-temp-buffer
+ (insert-file-contents file t)
+ (emacs-lisp-mode)
+ (let ((checking-result (package-lint-buffer))
+ (fail-on (cons 'error (when package-lint-batch-fail-on-warnings '(warning)))))
+ (when (cl-some (lambda (err) (memq (nth 2 err) fail-on)) checking-result)
+ (setq success nil)
+ (unless (equal last-directory file-directory)
+ (setq last-directory file-directory)
+ (message "Entering directory '%s'" file-directory))
+ (pcase-dolist (`(,line ,col ,type ,message) checking-result)
+ (message "%s:%d:%d: %s: %s"
+ base line col type message)))))))))
+
+;;;###autoload
+(defun package-lint-batch-and-exit ()
+ "Run `package-lint-buffer' on the files remaining on the command line.
+Use this only with -batch, it won't work interactively.
+
+When done, exit Emacs with status 1 in case of any errors, otherwise exit
+with status 0. The variable `package-lint-batch-fail-on-warnings' controls
+whether or not warnings alone produce a non-zero exit code."
+ (unless noninteractive
+ (error "`package-lint-batch-and-exit' is to be used only with -batch"))
+ (let ((success (package-lint-batch-and-exit-1 command-line-args-left)))
+ (kill-emacs (if success 0 1))))
+
+;;;###autoload
+(defun package-lint-looks-like-a-package-p ()
+ "Return non-nil if the current buffer appears to be intended as a package."
+ (or package-lint-main-file
+ (save-match-data
+ (save-excursion
+ (save-restriction
+ (widen)
+ (goto-char (point-min))
+ (re-search-forward
+ (lm-get-header-re (rx (or "Version" "Package-Version" "Package-Requires")))
+ nil t))))))
+
+(provide 'package-lint)
+;; Local Variables:
+;; indent-tabs-mode: nil
+;; End:
+;;; package-lint.el ends here