
This reverts almost all my recent changes to use curved quotes in docstrings and/or strings used for error diagnostics. There are a few exceptions, e.g., Bahá’í proper names. * admin/unidata/unidata-gen.el (unidata-gen-table): * lisp/abbrev.el (expand-region-abbrevs): * lisp/align.el (align-region): * lisp/allout.el (allout-mode, allout-solicit-alternate-bullet) (outlineify-sticky): * lisp/apropos.el (apropos-library): * lisp/bookmark.el (bookmark-default-annotation-text): * lisp/button.el (button-category-symbol, button-put) (make-text-button): * lisp/calc/calc-aent.el (math-read-if, math-read-factor): * lisp/calc/calc-embed.el (calc-do-embedded): * lisp/calc/calc-ext.el (calc-user-function-list): * lisp/calc/calc-graph.el (calc-graph-show-dumb): * lisp/calc/calc-help.el (calc-describe-key) (calc-describe-thing, calc-full-help): * lisp/calc/calc-lang.el (calc-c-language) (math-parse-fortran-vector-end, math-parse-tex-sum) (math-parse-eqn-matrix, math-parse-eqn-prime) (calc-yacas-language, calc-maxima-language, calc-giac-language) (math-read-giac-subscr, math-read-math-subscr) (math-read-big-rec, math-read-big-balance): * lisp/calc/calc-misc.el (calc-help, report-calc-bug): * lisp/calc/calc-mode.el (calc-auto-why, calc-save-modes) (calc-auto-recompute): * lisp/calc/calc-prog.el (calc-fix-token-name) (calc-read-parse-table-part, calc-user-define-invocation) (math-do-arg-check): * lisp/calc/calc-store.el (calc-edit-variable): * lisp/calc/calc-units.el (math-build-units-table-buffer): * lisp/calc/calc-vec.el (math-read-brackets): * lisp/calc/calc-yank.el (calc-edit-mode): * lisp/calc/calc.el (calc, calc-do, calc-user-invocation): * lisp/calendar/appt.el (appt-display-message): * lisp/calendar/diary-lib.el (diary-check-diary-file) (diary-mail-entries, diary-from-outlook): * lisp/calendar/icalendar.el (icalendar-export-region) (icalendar--convert-float-to-ical) (icalendar--convert-date-to-ical) (icalendar--convert-ical-to-diary) (icalendar--convert-recurring-to-diary) (icalendar--add-diary-entry): * lisp/calendar/time-date.el (format-seconds): * lisp/calendar/timeclock.el (timeclock-mode-line-display) (timeclock-make-hours-explicit, timeclock-log-data): * lisp/calendar/todo-mode.el (todo-prefix, todo-delete-category) (todo-item-mark, todo-check-format) (todo-insert-item--next-param, todo-edit-item--next-key) (todo-mode): * lisp/cedet/ede/pmake.el (ede-proj-makefile-insert-dist-rules): * lisp/cedet/mode-local.el (describe-mode-local-overload) (mode-local-print-binding, mode-local-describe-bindings-2): * lisp/cedet/semantic/complete.el (semantic-displayor-show-request): * lisp/cedet/srecode/srt-mode.el (srecode-macro-help): * lisp/cus-start.el (standard): * lisp/cus-theme.el (describe-theme-1): * lisp/custom.el (custom-add-dependencies, custom-check-theme) (custom--sort-vars-1, load-theme): * lisp/descr-text.el (describe-text-properties-1, describe-char): * lisp/dired-x.el (dired-do-run-mail): * lisp/dired.el (dired-log): * lisp/emacs-lisp/advice.el (ad-read-advised-function) (ad-read-advice-class, ad-read-advice-name, ad-enable-advice) (ad-disable-advice, ad-remove-advice, ad-set-argument) (ad-set-arguments, ad--defalias-fset, ad-activate) (ad-deactivate): * lisp/emacs-lisp/byte-opt.el (byte-compile-inline-expand) (byte-compile-unfold-lambda, byte-optimize-form-code-walker) (byte-optimize-while, byte-optimize-apply): * lisp/emacs-lisp/byte-run.el (defun, defsubst): * lisp/emacs-lisp/bytecomp.el (byte-compile-lapcode) (byte-compile-log-file, byte-compile-format-warn) (byte-compile-nogroup-warn, byte-compile-arglist-warn) (byte-compile-cl-warn) (byte-compile-warn-about-unresolved-functions) (byte-compile-file, byte-compile--declare-var) (byte-compile-file-form-defmumble, byte-compile-form) (byte-compile-normal-call, byte-compile-check-variable) (byte-compile-variable-ref, byte-compile-variable-set) (byte-compile-subr-wrong-args, byte-compile-setq-default) (byte-compile-negation-optimizer) (byte-compile-condition-case--old) (byte-compile-condition-case--new, byte-compile-save-excursion) (byte-compile-defvar, byte-compile-autoload) (byte-compile-lambda-form) (byte-compile-make-variable-buffer-local, display-call-tree) (batch-byte-compile): * lisp/emacs-lisp/cconv.el (cconv-convert, cconv--analyze-use): * lisp/emacs-lisp/chart.el (chart-space-usage): * lisp/emacs-lisp/check-declare.el (check-declare-scan) (check-declare-warn, check-declare-file) (check-declare-directory): * lisp/emacs-lisp/checkdoc.el (checkdoc-this-string-valid-engine) (checkdoc-message-text-engine): * lisp/emacs-lisp/cl-extra.el (cl-parse-integer) (cl--describe-class): * lisp/emacs-lisp/cl-generic.el (cl-defgeneric) (cl--generic-describe, cl-generic-generalizers): * lisp/emacs-lisp/cl-macs.el (cl--parse-loop-clause, cl-tagbody) (cl-symbol-macrolet): * lisp/emacs-lisp/cl.el (cl-unload-function, flet): * lisp/emacs-lisp/copyright.el (copyright) (copyright-update-directory): * lisp/emacs-lisp/edebug.el (edebug-read-list): * lisp/emacs-lisp/eieio-base.el (eieio-persistent-read): * lisp/emacs-lisp/eieio-core.el (eieio--slot-override) (eieio-oref): * lisp/emacs-lisp/eieio-opt.el (eieio-help-constructor): * lisp/emacs-lisp/eieio-speedbar.el: (eieio-speedbar-child-make-tag-lines) (eieio-speedbar-child-description): * lisp/emacs-lisp/eieio.el (defclass, change-class): * lisp/emacs-lisp/elint.el (elint-file, elint-get-top-forms) (elint-init-form, elint-check-defalias-form) (elint-check-let-form): * lisp/emacs-lisp/ert.el (ert-get-test, ert-results-mode-menu) (ert-results-pop-to-backtrace-for-test-at-point) (ert-results-pop-to-messages-for-test-at-point) (ert-results-pop-to-should-forms-for-test-at-point) (ert-describe-test): * lisp/emacs-lisp/find-func.el (find-function-search-for-symbol) (find-function-library): * lisp/emacs-lisp/generator.el (iter-yield): * lisp/emacs-lisp/gv.el (gv-define-simple-setter): * lisp/emacs-lisp/lisp-mnt.el (lm-verify): * lisp/emacs-lisp/macroexp.el (macroexp--obsolete-warning): * lisp/emacs-lisp/map-ynp.el (map-y-or-n-p): * lisp/emacs-lisp/nadvice.el (advice--make-docstring) (advice--make, define-advice): * lisp/emacs-lisp/package-x.el (package-upload-file): * lisp/emacs-lisp/package.el (package-version-join) (package-disabled-p, package-activate-1, package-activate) (package--download-one-archive) (package--download-and-read-archives) (package-compute-transaction, package-install-from-archive) (package-install, package-install-selected-packages) (package-delete, package-autoremove, describe-package-1) (package-install-button-action, package-delete-button-action) (package-menu-hide-package, package-menu--list-to-prompt) (package-menu--perform-transaction) (package-menu--find-and-notify-upgrades): * lisp/emacs-lisp/pcase.el (pcase-exhaustive, pcase--u1): * lisp/emacs-lisp/re-builder.el (reb-enter-subexp-mode): * lisp/emacs-lisp/ring.el (ring-previous, ring-next): * lisp/emacs-lisp/rx.el (rx-check, rx-anything) (rx-check-any-string, rx-check-any, rx-check-not, rx-=) (rx-repeat, rx-check-backref, rx-syntax, rx-check-category) (rx-form): * lisp/emacs-lisp/smie.el (smie-config-save): * lisp/emacs-lisp/subr-x.el (internal--check-binding): * lisp/emacs-lisp/tabulated-list.el (tabulated-list-put-tag): * lisp/emacs-lisp/testcover.el (testcover-1value): * lisp/emacs-lisp/timer.el (timer-event-handler): * lisp/emulation/viper-cmd.el (viper-toggle-parse-sexp-ignore-comments) (viper-toggle-search-style, viper-kill-buffer) (viper-brac-function): * lisp/emulation/viper-macs.el (viper-record-kbd-macro): * lisp/env.el (setenv): * lisp/erc/erc-button.el (erc-nick-popup): * lisp/erc/erc.el (erc-cmd-LOAD, erc-handle-login, english): * lisp/eshell/em-dirs.el (eshell/cd): * lisp/eshell/em-glob.el (eshell-glob-regexp) (eshell-glob-entries): * lisp/eshell/em-pred.el (eshell-parse-modifiers): * lisp/eshell/esh-opt.el (eshell-show-usage): * lisp/facemenu.el (facemenu-add-new-face) (facemenu-add-new-color): * lisp/faces.el (read-face-name, read-face-font, describe-face) (x-resolve-font-name): * lisp/files-x.el (modify-file-local-variable): * lisp/files.el (locate-user-emacs-file, find-alternate-file) (set-auto-mode, hack-one-local-variable--obsolete) (dir-locals-set-directory-class, write-file, basic-save-buffer) (delete-directory, copy-directory, recover-session) (recover-session-finish, insert-directory) (file-modes-char-to-who, file-modes-symbolic-to-number) (move-file-to-trash): * lisp/filesets.el (filesets-add-buffer, filesets-remove-buffer): * lisp/find-cmd.el (find-generic, find-to-string): * lisp/finder.el (finder-commentary): * lisp/font-lock.el (font-lock-fontify-buffer): * lisp/format.el (format-write-file, format-find-file) (format-insert-file): * lisp/frame.el (get-device-terminal, select-frame-by-name): * lisp/fringe.el (fringe--check-style): * lisp/gnus/nnmairix.el (nnmairix-widget-create-query): * lisp/help-fns.el (help-fns--key-bindings) (help-fns--compiler-macro, help-fns--parent-mode) (help-fns--obsolete, help-fns--interactive-only) (describe-function-1, describe-variable): * lisp/help.el (describe-mode) (describe-minor-mode-from-indicator): * lisp/image.el (image-type): * lisp/international/ccl.el (ccl-dump): * lisp/international/fontset.el (x-must-resolve-font-name): * lisp/international/mule-cmds.el (prefer-coding-system) (select-safe-coding-system-interactively) (select-safe-coding-system, activate-input-method) (toggle-input-method, describe-current-input-method) (describe-language-environment): * lisp/international/mule-conf.el (code-offset): * lisp/international/mule-diag.el (describe-character-set) (list-input-methods-1): * lisp/mail/feedmail.el (feedmail-run-the-queue): * lisp/mouse.el (minor-mode-menu-from-indicator): * lisp/mpc.el (mpc-playlist-rename): * lisp/msb.el (msb--choose-menu): * lisp/net/ange-ftp.el (ange-ftp-shell-command): * lisp/net/imap.el (imap-interactive-login): * lisp/net/mairix.el (mairix-widget-create-query): * lisp/net/newst-backend.el (newsticker--sentinel-work): * lisp/net/newst-treeview.el (newsticker--treeview-load): * lisp/net/rlogin.el (rlogin): * lisp/obsolete/iswitchb.el (iswitchb-possible-new-buffer): * lisp/obsolete/otodo-mode.el (todo-more-important-p): * lisp/obsolete/pgg-gpg.el (pgg-gpg-process-region): * lisp/obsolete/pgg-pgp.el (pgg-pgp-process-region): * lisp/obsolete/pgg-pgp5.el (pgg-pgp5-process-region): * lisp/org/ob-core.el (org-babel-goto-named-src-block) (org-babel-goto-named-result): * lisp/org/ob-fortran.el (org-babel-fortran-ensure-main-wrap): * lisp/org/ob-ref.el (org-babel-ref-resolve): * lisp/org/org-agenda.el (org-agenda-prepare): * lisp/org/org-clock.el (org-clock-notify-once-if-expired) (org-clock-resolve): * lisp/org/org-ctags.el (org-ctags-ask-rebuild-tags-file-then-find-tag): * lisp/org/org-feed.el (org-feed-parse-atom-entry): * lisp/org/org-habit.el (org-habit-parse-todo): * lisp/org/org-mouse.el (org-mouse-popup-global-menu) (org-mouse-context-menu): * lisp/org/org-table.el (org-table-edit-formulas): * lisp/org/ox.el (org-export-async-start): * lisp/proced.el (proced-log): * lisp/progmodes/ada-mode.el (ada-get-indent-case) (ada-check-matching-start, ada-goto-matching-start): * lisp/progmodes/ada-prj.el (ada-prj-display-page): * lisp/progmodes/ada-xref.el (ada-find-executable): * lisp/progmodes/ebrowse.el (ebrowse-tags-apropos): * lisp/progmodes/etags.el (etags-tags-apropos-additional): * lisp/progmodes/flymake.el (flymake-parse-err-lines) (flymake-start-syntax-check-process): * lisp/progmodes/python.el (python-shell-get-process-or-error) (python-define-auxiliary-skeleton): * lisp/progmodes/sql.el (sql-comint): * lisp/progmodes/verilog-mode.el (verilog-load-file-at-point): * lisp/progmodes/vhdl-mode.el (vhdl-widget-directory-validate): * lisp/recentf.el (recentf-open-files): * lisp/replace.el (query-replace-read-from) (occur-after-change-function, occur-1): * lisp/scroll-bar.el (scroll-bar-columns): * lisp/server.el (server-get-auth-key): * lisp/simple.el (execute-extended-command) (undo-outer-limit-truncate, list-processes--refresh) (compose-mail, set-variable, choose-completion-string) (define-alternatives): * lisp/startup.el (site-run-file, tty-handle-args, command-line) (command-line-1): * lisp/subr.el (noreturn, define-error, add-to-list) (read-char-choice, version-to-list): * lisp/term/common-win.el (x-handle-xrm-switch) (x-handle-name-switch, x-handle-args): * lisp/term/x-win.el (x-handle-parent-id, x-handle-smid): * lisp/textmodes/reftex-ref.el (reftex-label): * lisp/textmodes/reftex-toc.el (reftex-toc-rename-label): * lisp/textmodes/two-column.el (2C-split): * lisp/tutorial.el (tutorial--describe-nonstandard-key) (tutorial--find-changed-keys): * lisp/type-break.el (type-break-noninteractive-query): * lisp/wdired.el (wdired-do-renames, wdired-do-symlink-changes) (wdired-do-perm-changes): * lisp/whitespace.el (whitespace-report-region): Prefer grave quoting in source-code strings used to generate help and diagnostics. * lisp/faces.el (face-documentation): No need to convert quotes, since the result is a docstring. * lisp/info.el (Info-virtual-index-find-node) (Info-virtual-index, info-apropos): Simplify by generating only curved quotes, since info files are typically that ways nowadays anyway. * lisp/international/mule-diag.el (list-input-methods): Don’t assume text quoting style is curved. * lisp/org/org-bibtex.el (org-bibtex-fields): Revert my recent changes, going back to the old quoting style.
504 lines
19 KiB
EmacsLisp
504 lines
19 KiB
EmacsLisp
;;; eieio-base.el --- Base classes for EIEIO. -*- lexical-binding:t -*-
|
||
|
||
;;; Copyright (C) 2000-2002, 2004-2005, 2007-2015 Free Software
|
||
;;; Foundation, Inc.
|
||
|
||
;; Author: Eric M. Ludlam <zappo@gnu.org>
|
||
;; Keywords: OO, lisp
|
||
;; Package: eieio
|
||
|
||
;; This file is part of GNU Emacs.
|
||
|
||
;; GNU Emacs is free software: you can redistribute it and/or modify
|
||
;; it under the terms of the GNU General Public License as published by
|
||
;; the Free Software Foundation, either version 3 of the License, or
|
||
;; (at your option) any later version.
|
||
|
||
;; GNU Emacs 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
|
||
|
||
;;; Commentary:
|
||
;;
|
||
;; Base classes for EIEIO. These classes perform some basic tasks
|
||
;; but are generally useless on their own. To use any of these classes,
|
||
;; inherit from one or more of them.
|
||
|
||
;;; Code:
|
||
|
||
(require 'eieio)
|
||
(eval-when-compile (require 'cl-lib))
|
||
|
||
;;; eieio-instance-inheritor
|
||
;;
|
||
;; Enable instance inheritance via the `clone' method.
|
||
;; Works by using the `slot-unbound' method which usually throws an
|
||
;; error if a slot is unbound.
|
||
(defclass eieio-instance-inheritor ()
|
||
((parent-instance :initarg :parent-instance
|
||
:type eieio-instance-inheritor
|
||
:documentation
|
||
"The parent of this instance.
|
||
If a slot of this class is referenced, and is unbound, then the parent
|
||
is checked for a value.")
|
||
)
|
||
"This special class can enable instance inheritance.
|
||
Use `clone' to make a new object that does instance inheritance from
|
||
a parent instance. When a slot in the child is referenced, and has
|
||
not been set, use values from the parent."
|
||
:abstract t)
|
||
|
||
(cl-defmethod slot-unbound ((object eieio-instance-inheritor)
|
||
_class slot-name _fn)
|
||
"If a slot OBJECT in this CLASS is unbound, try to inherit, or throw a signal.
|
||
SLOT-NAME is the offending slot. FN is the function signaling the error."
|
||
(if (slot-boundp object 'parent-instance)
|
||
;; It may not look like it, but this line recurses back into this
|
||
;; method if the parent instance's slot is unbound.
|
||
(eieio-oref (oref object parent-instance) slot-name)
|
||
;; Throw the regular signal.
|
||
(cl-call-next-method)))
|
||
|
||
(cl-defmethod clone ((obj eieio-instance-inheritor) &rest _params)
|
||
"Clone OBJ, initializing `:parent' to OBJ.
|
||
All slots are unbound, except those initialized with PARAMS."
|
||
(let ((nobj (cl-call-next-method)))
|
||
(oset nobj parent-instance obj)
|
||
nobj))
|
||
|
||
(cl-defmethod eieio-instance-inheritor-slot-boundp ((object eieio-instance-inheritor)
|
||
slot)
|
||
"Return non-nil if the instance inheritor OBJECT's SLOT is bound.
|
||
See `slot-boundp' for details on binding slots.
|
||
The instance inheritor uses unbound slots as a way of cascading cloned
|
||
slot values, so testing for a slot being bound requires extra steps
|
||
for this kind of object."
|
||
(if (slot-boundp object slot)
|
||
;; If it is regularly bound, return t.
|
||
t
|
||
(if (slot-boundp object 'parent-instance)
|
||
(eieio-instance-inheritor-slot-boundp (oref object parent-instance)
|
||
slot)
|
||
nil)))
|
||
|
||
|
||
;;; eieio-instance-tracker
|
||
;;
|
||
;; Track all created instances of this class.
|
||
;; The class must initialize the `tracking-symbol' slot, and that
|
||
;; symbol is then used to contain these objects.
|
||
(defclass eieio-instance-tracker ()
|
||
((tracking-symbol :type symbol
|
||
:allocation :class
|
||
:documentation
|
||
"The symbol used to maintain a list of our instances.
|
||
The instance list is treated as a variable, with new instances added to it.")
|
||
)
|
||
"This special class enables instance tracking.
|
||
Inheritors from this class must overload `tracking-symbol' which is
|
||
a variable symbol used to store a list of all instances."
|
||
:abstract t)
|
||
|
||
(cl-defmethod initialize-instance :after ((this eieio-instance-tracker)
|
||
&rest _slots)
|
||
"Make sure THIS is in our master list of this class.
|
||
Optional argument SLOTS are the initialization arguments."
|
||
;; Theoretically, this is never called twice for a given instance.
|
||
(let ((sym (oref this tracking-symbol)))
|
||
(if (not (memq this (symbol-value sym)))
|
||
(set sym (append (symbol-value sym) (list this))))))
|
||
|
||
(cl-defmethod delete-instance ((this eieio-instance-tracker))
|
||
"Remove THIS from the master list of this class."
|
||
(set (oref this tracking-symbol)
|
||
(delq this (symbol-value (oref this tracking-symbol)))))
|
||
|
||
;; In retrospect, this is a silly function.
|
||
(defun eieio-instance-tracker-find (key slot list-symbol)
|
||
"Find KEY as an element of SLOT in the objects in LIST-SYMBOL.
|
||
Returns the first match."
|
||
(object-assoc key slot (symbol-value list-symbol)))
|
||
|
||
;;; eieio-singleton
|
||
;;
|
||
;; The singleton Design Pattern specifies that there is but one object
|
||
;; of a given class ever created. The EIEIO singleton base class defines
|
||
;; a CLASS allocated slot which contains the instance used. All calls to
|
||
;; `make-instance' will either create a new instance and store it in this
|
||
;; slot, or it will just return what is there.
|
||
(defclass eieio-singleton ()
|
||
((singleton :type eieio-singleton
|
||
:allocation :class
|
||
:documentation
|
||
"The only instance of this class that will be instantiated.
|
||
Multiple calls to `make-instance' will return this object."))
|
||
"This special class causes subclasses to be singletons.
|
||
A singleton is a class which will only ever have one instance."
|
||
:abstract t)
|
||
|
||
(cl-defmethod make-instance ((class (subclass eieio-singleton)) &rest _slots)
|
||
"Constructor for singleton CLASS.
|
||
NAME and SLOTS initialize the new object.
|
||
This constructor guarantees that no matter how many you request,
|
||
only one object ever exists."
|
||
;; NOTE TO SELF: In next version, make `slot-boundp' support classes
|
||
;; with class allocated slots or default values.
|
||
(let ((old (oref-default class singleton)))
|
||
(if (eq old eieio-unbound)
|
||
(oset-default class singleton (cl-call-next-method))
|
||
old)))
|
||
|
||
|
||
;;; eieio-persistent
|
||
;;
|
||
;; For objects which must save themselves to disk. Provides an
|
||
;; `object-write' method to save an object to disk, and a
|
||
;; `eieio-persistent-read' function to call to read an object
|
||
;; from disk.
|
||
;;
|
||
;; Also provide the method `eieio-persistent-path-relative' to
|
||
;; calculate path names relative to a given instance. This will
|
||
;; make the saved object location independent by converting all file
|
||
;; references to be relative to the directory the object is saved to.
|
||
;; You must call `eieio-persistent-path-relative' on each file name
|
||
;; saved in your object.
|
||
(defclass eieio-persistent ()
|
||
((file :initarg :file
|
||
:type string
|
||
:documentation
|
||
"The save file for this persistent object.
|
||
This must be a string, and must be specified when the new object is
|
||
instantiated.")
|
||
(extension :type string
|
||
:allocation :class
|
||
:initform ".eieio"
|
||
:documentation
|
||
"Extension of files saved by this object.
|
||
Enables auto-choosing nice file names based on name.")
|
||
(file-header-line :type string
|
||
:allocation :class
|
||
:initform ";; EIEIO PERSISTENT OBJECT"
|
||
:documentation
|
||
"Header line for the save file.
|
||
This is used with the `object-write' method.")
|
||
(do-backups :type boolean
|
||
:allocation :class
|
||
:initform t
|
||
:documentation
|
||
"Saving this object should make backup files.
|
||
Setting to nil will mean no backups are made."))
|
||
"This special class enables persistence through save files
|
||
Use the `object-save' method to write this object to disk. The save
|
||
format is Emacs Lisp code which calls the constructor for the saved
|
||
object. For this reason, only slots which do not have an `:initarg'
|
||
specified will not be saved."
|
||
:abstract t)
|
||
|
||
(cl-defmethod eieio-persistent-save-interactive ((this eieio-persistent) prompt
|
||
&optional name)
|
||
"Prepare to save THIS. Use in an `interactive' statement.
|
||
Query user for file name with PROMPT if THIS does not yet specify
|
||
a file. Optional argument NAME specifies a default file name."
|
||
(unless (slot-boundp this 'file)
|
||
(oset this file
|
||
(read-file-name prompt nil
|
||
(if name
|
||
(concat name (oref this extension))
|
||
))))
|
||
(oref this file))
|
||
|
||
(defun eieio-persistent-read (filename &optional class allow-subclass)
|
||
"Read a persistent object from FILENAME, and return it.
|
||
Signal an error if the object in FILENAME is not a constructor
|
||
for CLASS. Optional ALLOW-SUBCLASS says that it is ok for
|
||
`eieio-persistent-read' to load in subclasses of class instead of
|
||
being pedantic."
|
||
(unless class
|
||
(message "Unsafe call to `eieio-persistent-read'."))
|
||
(when class (cl-check-type class class))
|
||
(let ((ret nil)
|
||
(buffstr nil))
|
||
(unwind-protect
|
||
(progn
|
||
(with-current-buffer (get-buffer-create " *tmp eieio read*")
|
||
(insert-file-contents filename nil nil nil t)
|
||
(goto-char (point-min))
|
||
(setq buffstr (buffer-string)))
|
||
;; Do the read in the buffer the read was initialized from
|
||
;; so that any initialize-instance calls that depend on
|
||
;; the current buffer will work.
|
||
(setq ret (read buffstr))
|
||
(when (not (child-of-class-p (car ret) 'eieio-persistent))
|
||
(error "Corrupt object on disk: Unknown saved object"))
|
||
(when (and class
|
||
(not (or (eq (car ret) class ) ; same class
|
||
(and allow-subclass
|
||
(child-of-class-p (car ret) class)) ; subclasses
|
||
)))
|
||
(error "Corrupt object on disk: Invalid saved class"))
|
||
(setq ret (eieio-persistent-convert-list-to-object ret))
|
||
(oset ret file filename))
|
||
(kill-buffer " *tmp eieio read*"))
|
||
ret))
|
||
|
||
(defun eieio-persistent-convert-list-to-object (inputlist)
|
||
"Convert the INPUTLIST, representing object creation to an object.
|
||
While it is possible to just `eval' the INPUTLIST, this code instead
|
||
validates the existing list, and explicitly creates objects instead of
|
||
calling eval. This avoids the possibility of accidentally running
|
||
malicious code.
|
||
|
||
Note: This function recurses when a slot of :type of some object is
|
||
identified, and needing more object creation."
|
||
(let* ((objclass (nth 0 inputlist))
|
||
;; (objname (nth 1 inputlist))
|
||
(slots (nthcdr 2 inputlist))
|
||
(createslots nil)
|
||
(class
|
||
(progn
|
||
;; If OBJCLASS is an eieio autoload object, then we need to
|
||
;; load it.
|
||
(eieio-class-un-autoload objclass)
|
||
(eieio--class-object objclass))))
|
||
|
||
(while slots
|
||
(let ((initarg (car slots))
|
||
(value (car (cdr slots))))
|
||
|
||
;; Make sure that the value proposed for SLOT is valid.
|
||
;; In addition, strip out quotes, list functions, and update
|
||
;; object constructors as needed.
|
||
(setq value (eieio-persistent-validate/fix-slot-value
|
||
class (eieio--initarg-to-attribute class initarg) value))
|
||
|
||
(push initarg createslots)
|
||
(push value createslots)
|
||
)
|
||
|
||
(setq slots (cdr (cdr slots))))
|
||
|
||
(apply #'make-instance objclass (nreverse createslots))
|
||
|
||
;;(eval inputlist)
|
||
))
|
||
|
||
(defun eieio-persistent-validate/fix-slot-value (class slot proposed-value)
|
||
"Validate that in CLASS, the SLOT with PROPOSED-VALUE is good, then fix.
|
||
A limited number of functions, such as quote, list, and valid object
|
||
constructor functions are considered valid.
|
||
Second, any text properties will be stripped from strings."
|
||
(cond ((consp proposed-value)
|
||
;; Lists with something in them need special treatment.
|
||
(let* ((slot-idx (- (eieio--slot-name-index class slot)
|
||
(eval-when-compile
|
||
(length (cl-struct-slot-info 'eieio--object)))))
|
||
(type (cl--slot-descriptor-type (aref (eieio--class-slots class)
|
||
slot-idx)))
|
||
(classtype (eieio-persistent-slot-type-is-class-p type)))
|
||
|
||
(cond ((eq (car proposed-value) 'quote)
|
||
(car (cdr proposed-value)))
|
||
|
||
;; An empty list sometimes shows up as (list), which is dumb, but
|
||
;; we need to support it for backward compat.
|
||
((and (eq (car proposed-value) 'list)
|
||
(= (length proposed-value) 1))
|
||
nil)
|
||
|
||
;; We have a slot with a single object that can be
|
||
;; saved here. Recurse and evaluate that
|
||
;; sub-object.
|
||
((and classtype (class-p classtype)
|
||
(child-of-class-p (car proposed-value) classtype))
|
||
(eieio-persistent-convert-list-to-object
|
||
proposed-value))
|
||
|
||
;; List of object constructors.
|
||
((and (eq (car proposed-value) 'list)
|
||
;; 2nd item is a list.
|
||
(consp (car (cdr proposed-value)))
|
||
;; 1st elt of 2nd item is a class name.
|
||
(class-p (car (car (cdr proposed-value))))
|
||
)
|
||
|
||
;; Check the value against the input class type.
|
||
;; If something goes wrong, issue a smart warning
|
||
;; about how a :type is needed for this to work.
|
||
(unless (and
|
||
;; Do we have a type?
|
||
(consp classtype) (class-p (car classtype)))
|
||
(error "In save file, list of object constructors found, but no :type specified for slot %S of type %S"
|
||
slot classtype))
|
||
|
||
;; We have a predicate, but it doesn't satisfy the predicate?
|
||
(dolist (PV (cdr proposed-value))
|
||
(unless (child-of-class-p (car PV) (car classtype))
|
||
(error "Corrupt object on disk")))
|
||
|
||
;; We have a list of objects here. Lets load them
|
||
;; in.
|
||
(let ((objlist nil))
|
||
(dolist (subobj (cdr proposed-value))
|
||
(push (eieio-persistent-convert-list-to-object subobj)
|
||
objlist))
|
||
;; return the list of objects ... reversed.
|
||
(nreverse objlist)))
|
||
(t
|
||
proposed-value))))
|
||
|
||
((stringp proposed-value)
|
||
;; Else, check for strings, remove properties.
|
||
(substring-no-properties proposed-value))
|
||
|
||
(t
|
||
;; Else, just return whatever the constant was.
|
||
proposed-value))
|
||
)
|
||
|
||
(defun eieio-persistent-slot-type-is-class-p (type)
|
||
"Return the class referred to in TYPE.
|
||
If no class is referenced there, then return nil."
|
||
(cond ((class-p type)
|
||
;; If the type is a class, then return it.
|
||
type)
|
||
((and (eq 'list-of (car-safe type)) (class-p (cadr type)))
|
||
;; If it is the type of a list of a class, then return that class and
|
||
;; the type.
|
||
(cons (cadr type) type))
|
||
|
||
((and (symbolp type) (get type 'cl-deftype-handler))
|
||
;; Macro-expand the type according to cl-deftype definitions.
|
||
(eieio-persistent-slot-type-is-class-p
|
||
(funcall (get type 'cl-deftype-handler))))
|
||
|
||
;; FIXME: foo-child should not be a valid type!
|
||
((and (symbolp type) (string-match "-child\\'" (symbol-name type))
|
||
(class-p (intern-soft (substring (symbol-name type) 0
|
||
(match-beginning 0)))))
|
||
(unless eieio-backward-compatibility
|
||
(error "Use of bogus %S type instead of %S"
|
||
type (intern-soft (substring (symbol-name type) 0
|
||
(match-beginning 0)))))
|
||
;; If it is the predicate ending with -child, then return
|
||
;; that class. Unfortunately, in EIEIO, typep of just the
|
||
;; class is the same as if we used -child, so no further work needed.
|
||
(intern-soft (substring (symbol-name type) 0
|
||
(match-beginning 0))))
|
||
;; FIXME: foo-list should not be a valid type!
|
||
((and (symbolp type) (string-match "-list\\'" (symbol-name type))
|
||
(class-p (intern-soft (substring (symbol-name type) 0
|
||
(match-beginning 0)))))
|
||
(unless eieio-backward-compatibility
|
||
(error "Use of bogus %S type instead of (list-of %S)"
|
||
type (intern-soft (substring (symbol-name type) 0
|
||
(match-beginning 0)))))
|
||
;; If it is the predicate ending with -list, then return
|
||
;; that class and the predicate to use.
|
||
(cons (intern-soft (substring (symbol-name type) 0
|
||
(match-beginning 0)))
|
||
type))
|
||
|
||
((eq (car-safe type) 'or)
|
||
;; If type is a list, and is an or, it is possibly something
|
||
;; like (or null myclass), so check for that.
|
||
(let ((ans nil))
|
||
(dolist (subtype (cdr type))
|
||
(setq ans (eieio-persistent-slot-type-is-class-p
|
||
subtype)))
|
||
ans))
|
||
|
||
(t
|
||
;; No match, not a class.
|
||
nil)))
|
||
|
||
(cl-defmethod object-write ((this eieio-persistent) &optional comment)
|
||
"Write persistent object THIS out to the current stream.
|
||
Optional argument COMMENT is a header line comment."
|
||
(cl-call-next-method this (or comment (oref this file-header-line))))
|
||
|
||
(cl-defmethod eieio-persistent-path-relative ((this eieio-persistent) file)
|
||
"For object THIS, make absolute file name FILE relative."
|
||
(file-relative-name (expand-file-name file)
|
||
(file-name-directory (oref this file))))
|
||
|
||
(cl-defmethod eieio-persistent-save ((this eieio-persistent) &optional file)
|
||
"Save persistent object THIS to disk.
|
||
Optional argument FILE overrides the file name specified in the object
|
||
instance."
|
||
(when file (setq file (expand-file-name file)))
|
||
(with-temp-buffer
|
||
(let* ((cfn (or file (oref this file)))
|
||
(default-directory (file-name-directory cfn)))
|
||
(cl-letf ((standard-output (current-buffer))
|
||
((oref this file) ;FIXME: Why change it?
|
||
(if file
|
||
;; FIXME: Makes a name relative to (oref this file),
|
||
;; whereas I think it should be relative to cfn.
|
||
(eieio-persistent-path-relative this file)
|
||
(file-name-nondirectory cfn))))
|
||
(object-write this (oref this file-header-line)))
|
||
(let ((backup-inhibited (not (oref this do-backups)))
|
||
(coding-system-for-write 'utf-8-emacs))
|
||
;; Old way - write file. Leaves message behind.
|
||
;;(write-file cfn nil)
|
||
|
||
;; New way - Avoid the vast quantities of error checking
|
||
;; just so I can get at the special flags that disable
|
||
;; displaying random messages.
|
||
(write-region (point-min) (point-max) cfn nil 1)
|
||
))))
|
||
|
||
;; Notes on the persistent object:
|
||
;; It should also set up some hooks to help it keep itself up to date.
|
||
|
||
|
||
;;; Named object
|
||
|
||
(defclass eieio-named ()
|
||
((object-name :initarg :object-name :initform nil))
|
||
"Object with a name."
|
||
:abstract t)
|
||
|
||
(cl-defmethod eieio-object-name-string ((obj eieio-named))
|
||
"Return a string which is OBJ's name."
|
||
(or (slot-value obj 'object-name)
|
||
(symbol-name (eieio-object-class obj))))
|
||
|
||
(cl-defmethod eieio-object-set-name-string ((obj eieio-named) name)
|
||
"Set the string which is OBJ's NAME."
|
||
(cl-check-type name string)
|
||
(eieio-oset obj 'object-name name))
|
||
|
||
(cl-defmethod clone ((obj eieio-named) &rest params)
|
||
"Clone OBJ, initializing `:parent' to OBJ.
|
||
All slots are unbound, except those initialized with PARAMS."
|
||
(let* ((newname (and (stringp (car params)) (pop params)))
|
||
(nobj (apply #'cl-call-next-method obj params))
|
||
(nm (slot-value obj 'object-name)))
|
||
(eieio-oset obj 'object-name
|
||
(or newname
|
||
(save-match-data
|
||
(if (and nm (string-match "-\\([0-9]+\\)" nm))
|
||
(let ((num (1+ (string-to-number
|
||
(match-string 1 nm)))))
|
||
(concat (substring nm 0 (match-beginning 0))
|
||
"-" (int-to-string num)))
|
||
(concat nm "-1")))))
|
||
nobj))
|
||
|
||
(cl-defmethod make-instance ((class (subclass eieio-named)) &rest args)
|
||
(if (not (stringp (car args)))
|
||
(cl-call-next-method)
|
||
(funcall (if eieio-backward-compatibility #'ignore #'message)
|
||
"Obsolete: name passed without :object-name to %S constructor"
|
||
class)
|
||
(apply #'cl-call-next-method class :object-name args)))
|
||
|
||
|
||
(provide 'eieio-base)
|
||
|
||
;;; eieio-base.el ends here
|