Improve documentation of 'directory-files-and-attributes'
[emacs.git] / lisp / emacs-lisp / eieio-base.el
blobcba6cab1d4fe0074e76e41eed4fe35525cb1dfe2
1 ;;; eieio-base.el --- Base classes for EIEIO. -*- lexical-binding:t -*-
3 ;;; Copyright (C) 2000-2002, 2004-2005, 2007-2018 Free Software
4 ;;; Foundation, Inc.
6 ;; Author: Eric M. Ludlam <zappo@gnu.org>
7 ;; Keywords: OO, lisp
8 ;; Package: eieio
10 ;; This file is part of GNU Emacs.
12 ;; GNU Emacs is free software: you can redistribute it and/or modify
13 ;; it under the terms of the GNU General Public License as published by
14 ;; the Free Software Foundation, either version 3 of the License, or
15 ;; (at your option) any later version.
17 ;; GNU Emacs is distributed in the hope that it will be useful,
18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 ;; GNU General Public License for more details.
22 ;; You should have received a copy of the GNU General Public License
23 ;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
25 ;;; Commentary:
27 ;; Base classes for EIEIO. These classes perform some basic tasks
28 ;; but are generally useless on their own. To use any of these classes,
29 ;; inherit from one or more of them.
31 ;;; Code:
33 (require 'eieio)
34 (require 'seq)
35 (eval-when-compile (require 'cl-lib))
37 ;;; eieio-instance-inheritor
39 ;; Enable instance inheritance via the `clone' method.
40 ;; Works by using the `slot-unbound' method which usually throws an
41 ;; error if a slot is unbound.
42 (defclass eieio-instance-inheritor ()
43 ((parent-instance :initarg :parent-instance
44 :type eieio-instance-inheritor
45 :documentation
46 "The parent of this instance.
47 If a slot of this class is referenced, and is unbound, then the parent
48 is checked for a value.")
50 "This special class can enable instance inheritance.
51 Use `clone' to make a new object that does instance inheritance from
52 a parent instance. When a slot in the child is referenced, and has
53 not been set, use values from the parent."
54 :abstract t)
56 (cl-defmethod slot-unbound ((object eieio-instance-inheritor)
57 _class slot-name _fn)
58 "If a slot OBJECT in this CLASS is unbound, try to inherit, or throw a signal.
59 SLOT-NAME is the offending slot. FN is the function signaling the error."
60 (if (slot-boundp object 'parent-instance)
61 ;; It may not look like it, but this line recurses back into this
62 ;; method if the parent instance's slot is unbound.
63 (eieio-oref (oref object parent-instance) slot-name)
64 ;; Throw the regular signal.
65 (cl-call-next-method)))
67 (cl-defmethod clone ((obj eieio-instance-inheritor) &rest _params)
68 "Clone OBJ, initializing `:parent' to OBJ.
69 All slots are unbound, except those initialized with PARAMS."
70 (let ((nobj (cl-call-next-method)))
71 (oset nobj parent-instance obj)
72 nobj))
74 (cl-defmethod eieio-instance-inheritor-slot-boundp ((object eieio-instance-inheritor)
75 slot)
76 "Return non-nil if the instance inheritor OBJECT's SLOT is bound.
77 See `slot-boundp' for details on binding slots.
78 The instance inheritor uses unbound slots as a way of cascading cloned
79 slot values, so testing for a slot being bound requires extra steps
80 for this kind of object."
81 (if (slot-boundp object slot)
82 ;; If it is regularly bound, return t.
84 (if (slot-boundp object 'parent-instance)
85 (eieio-instance-inheritor-slot-boundp (oref object parent-instance)
86 slot)
87 nil)))
90 ;;; eieio-instance-tracker
92 ;; Track all created instances of this class.
93 ;; The class must initialize the `tracking-symbol' slot, and that
94 ;; symbol is then used to contain these objects.
95 (defclass eieio-instance-tracker ()
96 ((tracking-symbol :type symbol
97 :allocation :class
98 :documentation
99 "The symbol used to maintain a list of our instances.
100 The instance list is treated as a variable, with new instances added to it.")
102 "This special class enables instance tracking.
103 Inheritors from this class must overload `tracking-symbol' which is
104 a variable symbol used to store a list of all instances."
105 :abstract t)
107 (cl-defmethod initialize-instance :after ((this eieio-instance-tracker)
108 &rest _slots)
109 "Make sure THIS is in our master list of this class.
110 Optional argument SLOTS are the initialization arguments."
111 ;; Theoretically, this is never called twice for a given instance.
112 (let ((sym (oref this tracking-symbol)))
113 (if (not (memq this (symbol-value sym)))
114 (set sym (append (symbol-value sym) (list this))))))
116 (cl-defmethod delete-instance ((this eieio-instance-tracker))
117 "Remove THIS from the master list of this class."
118 (set (oref this tracking-symbol)
119 (delq this (symbol-value (oref this tracking-symbol)))))
121 ;; In retrospect, this is a silly function.
122 (defun eieio-instance-tracker-find (key slot list-symbol)
123 "Find KEY as an element of SLOT in the objects in LIST-SYMBOL.
124 Returns the first match."
125 (object-assoc key slot (symbol-value list-symbol)))
127 ;;; eieio-singleton
129 ;; The singleton Design Pattern specifies that there is but one object
130 ;; of a given class ever created. The EIEIO singleton base class defines
131 ;; a CLASS allocated slot which contains the instance used. All calls to
132 ;; `make-instance' will either create a new instance and store it in this
133 ;; slot, or it will just return what is there.
134 (defclass eieio-singleton ()
135 ((singleton :type eieio-singleton
136 :allocation :class
137 :documentation
138 "The only instance of this class that will be instantiated.
139 Multiple calls to `make-instance' will return this object."))
140 "This special class causes subclasses to be singletons.
141 A singleton is a class which will only ever have one instance."
142 :abstract t)
144 (cl-defmethod make-instance ((class (subclass eieio-singleton)) &rest _slots)
145 "Constructor for singleton CLASS.
146 NAME and SLOTS initialize the new object.
147 This constructor guarantees that no matter how many you request,
148 only one object ever exists."
149 ;; NOTE TO SELF: In next version, make `slot-boundp' support classes
150 ;; with class allocated slots or default values.
151 (let ((old (oref-default class singleton)))
152 (if (eq old eieio-unbound)
153 (oset-default class singleton (cl-call-next-method))
154 old)))
157 ;;; eieio-persistent
159 ;; For objects which must save themselves to disk. Provides an
160 ;; `object-write' method to save an object to disk, and a
161 ;; `eieio-persistent-read' function to call to read an object
162 ;; from disk.
164 ;; Also provide the method `eieio-persistent-path-relative' to
165 ;; calculate path names relative to a given instance. This will
166 ;; make the saved object location independent by converting all file
167 ;; references to be relative to the directory the object is saved to.
168 ;; You must call `eieio-persistent-path-relative' on each file name
169 ;; saved in your object.
170 (defclass eieio-persistent ()
171 ((file :initarg :file
172 :type string
173 :documentation
174 "The save file for this persistent object.
175 This must be a string, and must be specified when the new object is
176 instantiated.")
177 (extension :type string
178 :allocation :class
179 :initform ".eieio"
180 :documentation
181 "Extension of files saved by this object.
182 Enables auto-choosing nice file names based on name.")
183 (file-header-line :type string
184 :allocation :class
185 :initform ";; EIEIO PERSISTENT OBJECT"
186 :documentation
187 "Header line for the save file.
188 This is used with the `object-write' method.")
189 (do-backups :type boolean
190 :allocation :class
191 :initform t
192 :documentation
193 "Saving this object should make backup files.
194 Setting to nil will mean no backups are made."))
195 "This special class enables persistence through save files
196 Use the `object-save' method to write this object to disk. The save
197 format is Emacs Lisp code which calls the constructor for the saved
198 object. For this reason, only slots which do not have an `:initarg'
199 specified will not be saved."
200 :abstract t)
202 (cl-defmethod eieio-persistent-save-interactive ((this eieio-persistent) prompt
203 &optional name)
204 "Prepare to save THIS. Use in an `interactive' statement.
205 Query user for file name with PROMPT if THIS does not yet specify
206 a file. Optional argument NAME specifies a default file name."
207 (unless (slot-boundp this 'file)
208 (oset this file
209 (read-file-name prompt nil
210 (if name
211 (concat name (oref this extension))
212 ))))
213 (oref this file))
215 (defun eieio-persistent-read (filename &optional class allow-subclass)
216 "Read a persistent object from FILENAME, and return it.
217 Signal an error if the object in FILENAME is not a constructor
218 for CLASS. Optional ALLOW-SUBCLASS says that it is ok for
219 `eieio-persistent-read' to load in subclasses of class instead of
220 being pedantic."
221 (unless class
222 (warn "`eieio-persistent-read' called without specifying a class"))
223 (when class (cl-check-type class class))
224 (let ((ret nil)
225 (buffstr nil))
226 (unwind-protect
227 (progn
228 (with-current-buffer (get-buffer-create " *tmp eieio read*")
229 (insert-file-contents filename nil nil nil t)
230 (goto-char (point-min))
231 (setq buffstr (buffer-string)))
232 ;; Do the read in the buffer the read was initialized from
233 ;; so that any initialize-instance calls that depend on
234 ;; the current buffer will work.
235 (setq ret (read buffstr))
236 (when (not (child-of-class-p (car ret) 'eieio-persistent))
237 (error
238 "Invalid object: %s is not a subclass of `eieio-persistent'"
239 (car ret)))
240 (when (and class
241 (not (or (eq (car ret) class) ; same class
242 (and allow-subclass ; subclass
243 (child-of-class-p (car ret) class)))))
244 (error
245 "Invalid object: %s is not an object of class %s nor a subclass"
246 (car ret) class))
247 (setq ret (eieio-persistent-convert-list-to-object ret))
248 (oset ret file filename))
249 (kill-buffer " *tmp eieio read*"))
250 ret))
252 (defun eieio-persistent-convert-list-to-object (inputlist)
253 "Convert the INPUTLIST, representing object creation to an object.
254 While it is possible to just `eval' the INPUTLIST, this code instead
255 validates the existing list, and explicitly creates objects instead of
256 calling eval. This avoids the possibility of accidentally running
257 malicious code.
259 Note: This function recurses when a slot of :type of some object is
260 identified, and needing more object creation."
261 (let* ((objclass (nth 0 inputlist))
262 ;; Earlier versions of `object-write' added a string name for
263 ;; the object, now obsolete.
264 (slots (nthcdr
265 (if (stringp (nth 1 inputlist)) 2 1)
266 inputlist))
267 (createslots nil)
268 (class
269 (progn
270 ;; If OBJCLASS is an eieio autoload object, then we need to
271 ;; load it.
272 (eieio-class-un-autoload objclass)
273 (eieio--class-object objclass))))
275 (while slots
276 (let ((initarg (car slots))
277 (value (car (cdr slots))))
279 ;; Make sure that the value proposed for SLOT is valid.
280 ;; In addition, strip out quotes, list functions, and update
281 ;; object constructors as needed.
282 (setq value (eieio-persistent-validate/fix-slot-value
283 class (eieio--initarg-to-attribute class initarg) value))
285 (push initarg createslots)
286 (push value createslots)
289 (setq slots (cdr (cdr slots))))
291 (apply #'make-instance objclass (nreverse createslots))
293 ;;(eval inputlist)
296 (defun eieio-persistent-validate/fix-slot-value (class slot proposed-value)
297 "Validate that in CLASS, the SLOT with PROPOSED-VALUE is good, then fix.
298 A limited number of functions, such as quote, list, and valid object
299 constructor functions are considered valid.
300 Second, any text properties will be stripped from strings."
301 (cond ((consp proposed-value)
302 ;; Lists with something in them need special treatment.
303 (let* ((slot-idx (- (eieio--slot-name-index class slot)
304 (eval-when-compile eieio--object-num-slots)))
305 (type (cl--slot-descriptor-type (aref (eieio--class-slots class)
306 slot-idx)))
307 (classtype (eieio-persistent-slot-type-is-class-p type)))
309 (cond ((eq (car proposed-value) 'quote)
310 (car (cdr proposed-value)))
312 ;; An empty list sometimes shows up as (list), which is dumb, but
313 ;; we need to support it for backward compat.
314 ((and (eq (car proposed-value) 'list)
315 (= (length proposed-value) 1))
316 nil)
318 ;; List of object constructors.
319 ((and (eq (car proposed-value) 'list)
320 ;; 2nd item is a list.
321 (consp (car (cdr proposed-value)))
322 ;; 1st elt of 2nd item is a class name.
323 (class-p (car (car (cdr proposed-value))))
326 ;; Check the value against the input class type.
327 ;; If something goes wrong, issue a smart warning
328 ;; about how a :type is needed for this to work.
329 (unless (and
330 ;; Do we have a type?
331 (consp classtype) (class-p (car classtype)))
332 (error "In save file, list of object constructors found, but no :type specified for slot %S of type %S"
333 slot classtype))
335 ;; We have a predicate, but it doesn't satisfy the predicate?
336 (dolist (PV (cdr proposed-value))
337 (unless (child-of-class-p (car PV) (car classtype))
338 (error "Invalid object: slot member %s does not match class %s"
339 (car PV) (car classtype))))
341 ;; We have a list of objects here. Lets load them
342 ;; in.
343 (let ((objlist nil))
344 (dolist (subobj (cdr proposed-value))
345 (push (eieio-persistent-convert-list-to-object subobj)
346 objlist))
347 ;; return the list of objects ... reversed.
348 (nreverse objlist)))
349 ;; We have a slot with a single object that can be
350 ;; saved here. Recurse and evaluate that
351 ;; sub-object.
352 ((and classtype
353 (seq-some
354 (lambda (elt)
355 (child-of-class-p (car proposed-value) elt))
356 (if (listp classtype) classtype (list classtype))))
357 (eieio-persistent-convert-list-to-object
358 proposed-value))
360 proposed-value))))
361 ;; For hash-tables and vectors, the top-level `read' will not
362 ;; "look inside" member values, so we need to do that
363 ;; explicitly.
364 ((hash-table-p proposed-value)
365 (maphash
366 (lambda (key value)
367 (cond ((class-p (car-safe value))
368 (setf (gethash key proposed-value)
369 (eieio-persistent-convert-list-to-object
370 value)))
371 ((and (consp value)
372 (eq (car value) 'quote))
373 (setf (gethash key proposed-value)
374 (cadr value)))))
375 proposed-value)
376 proposed-value)
378 ((vectorp proposed-value)
379 (dotimes (i (length proposed-value))
380 (let ((val (aref proposed-value i)))
381 (cond ((class-p (car-safe val))
382 (aset proposed-value i
383 (eieio-persistent-convert-list-to-object
384 (aref proposed-value i))))
385 ((and (consp val)
386 (eq (car val) 'quote))
387 (aset proposed-value i
388 (cadr val))))))
389 proposed-value)
391 ((stringp proposed-value)
392 ;; Else, check for strings, remove properties.
393 (substring-no-properties proposed-value))
396 ;; Else, just return whatever the constant was.
397 proposed-value))
400 (defun eieio-persistent-slot-type-is-class-p (type)
401 "Return the class referred to in TYPE.
402 If no class is referenced there, then return nil."
403 (cond ((class-p type)
404 ;; If the type is a class, then return it.
405 type)
406 ((and (eq 'list-of (car-safe type)) (class-p (cadr type)))
407 ;; If it is the type of a list of a class, then return that class and
408 ;; the type.
409 (cons (cadr type) type))
411 ((and (symbolp type) (get type 'cl-deftype-handler))
412 ;; Macro-expand the type according to cl-deftype definitions.
413 (eieio-persistent-slot-type-is-class-p
414 (funcall (get type 'cl-deftype-handler))))
416 ;; FIXME: foo-child should not be a valid type!
417 ((and (symbolp type) (string-match "-child\\'" (symbol-name type))
418 (class-p (intern-soft (substring (symbol-name type) 0
419 (match-beginning 0)))))
420 (unless eieio-backward-compatibility
421 (error "Use of bogus %S type instead of %S"
422 type (intern-soft (substring (symbol-name type) 0
423 (match-beginning 0)))))
424 ;; If it is the predicate ending with -child, then return
425 ;; that class. Unfortunately, in EIEIO, typep of just the
426 ;; class is the same as if we used -child, so no further work needed.
427 (intern-soft (substring (symbol-name type) 0
428 (match-beginning 0))))
429 ;; FIXME: foo-list should not be a valid type!
430 ((and (symbolp type) (string-match "-list\\'" (symbol-name type))
431 (class-p (intern-soft (substring (symbol-name type) 0
432 (match-beginning 0)))))
433 (unless eieio-backward-compatibility
434 (error "Use of bogus %S type instead of (list-of %S)"
435 type (intern-soft (substring (symbol-name type) 0
436 (match-beginning 0)))))
437 ;; If it is the predicate ending with -list, then return
438 ;; that class and the predicate to use.
439 (cons (intern-soft (substring (symbol-name type) 0
440 (match-beginning 0)))
441 type))
443 ((eq (car-safe type) 'or)
444 ;; If type is a list, and is an `or', return all valid class
445 ;; types within the `or' statement.
446 (seq-filter #'eieio-persistent-slot-type-is-class-p (cdr type)))
449 ;; No match, not a class.
450 nil)))
452 (cl-defmethod object-write ((this eieio-persistent) &optional comment)
453 "Write persistent object THIS out to the current stream.
454 Optional argument COMMENT is a header line comment."
455 (cl-call-next-method this (or comment (oref this file-header-line))))
457 (cl-defmethod eieio-persistent-path-relative ((this eieio-persistent) file)
458 "For object THIS, make absolute file name FILE relative."
459 (file-relative-name (expand-file-name file)
460 (file-name-directory (oref this file))))
462 (cl-defmethod eieio-persistent-save ((this eieio-persistent) &optional file)
463 "Save persistent object THIS to disk.
464 Optional argument FILE overrides the file name specified in the object
465 instance."
466 (when file (setq file (expand-file-name file)))
467 (with-temp-buffer
468 (let* ((cfn (or file (oref this file)))
469 (default-directory (file-name-directory cfn)))
470 (cl-letf ((standard-output (current-buffer))
471 ((oref this file) ;FIXME: Why change it?
472 (if file
473 ;; FIXME: Makes a name relative to (oref this file),
474 ;; whereas I think it should be relative to cfn.
475 (eieio-persistent-path-relative this file)
476 (file-name-nondirectory cfn))))
477 (object-write this (oref this file-header-line)))
478 (let ((backup-inhibited (not (oref this do-backups)))
479 (coding-system-for-write 'utf-8-emacs))
480 ;; Old way - write file. Leaves message behind.
481 ;;(write-file cfn nil)
483 ;; New way - Avoid the vast quantities of error checking
484 ;; just so I can get at the special flags that disable
485 ;; displaying random messages.
486 (write-region (point-min) (point-max) cfn nil 1)
487 ))))
489 ;; Notes on the persistent object:
490 ;; It should also set up some hooks to help it keep itself up to date.
493 ;;; Named object
495 (defclass eieio-named ()
496 ((object-name :initarg :object-name :initform nil))
497 "Object with a name."
498 :abstract t)
500 (cl-defmethod eieio-object-name-string ((obj eieio-named))
501 "Return a string which is OBJ's name."
502 (or (slot-value obj 'object-name)
503 (symbol-name (eieio-object-class obj))))
505 (cl-defmethod eieio-object-set-name-string ((obj eieio-named) name)
506 "Set the string which is OBJ's NAME."
507 (cl-check-type name string)
508 (eieio-oset obj 'object-name name))
510 (cl-defmethod clone ((obj eieio-named) &rest params)
511 "Clone OBJ, initializing `:parent' to OBJ.
512 All slots are unbound, except those initialized with PARAMS."
513 (let* ((newname (and (stringp (car params)) (pop params)))
514 (nobj (apply #'cl-call-next-method obj params))
515 (nm (slot-value obj 'object-name)))
516 (eieio-oset obj 'object-name
517 (or newname
518 (save-match-data
519 (if (and nm (string-match "-\\([0-9]+\\)" nm))
520 (let ((num (1+ (string-to-number
521 (match-string 1 nm)))))
522 (concat (substring nm 0 (match-beginning 0))
523 "-" (int-to-string num)))
524 (concat nm "-1")))))
525 nobj))
527 (cl-defmethod make-instance ((class (subclass eieio-named)) &rest args)
528 (if (not (stringp (car args)))
529 (cl-call-next-method)
530 (funcall (if eieio-backward-compatibility #'ignore #'message)
531 "Obsolete: name passed without :object-name to %S constructor"
532 class)
533 (apply #'cl-call-next-method class :object-name args)))
536 (provide 'eieio-base)
538 ;;; eieio-base.el ends here