]> git.eshelyaron.com Git - emacs.git/commitdiff
subr.el: Add multiple inheritance to `derived-mode-p`
authorStefan Monnier <monnier@iro.umontreal.ca>
Mon, 6 Nov 2023 21:57:05 +0000 (16:57 -0500)
committerStefan Monnier <monnier@iro.umontreal.ca>
Thu, 9 Nov 2023 05:33:46 +0000 (00:33 -0500)
Add the ability for a major mode to declare "extra parents" in
addition to the one from which it inherits.

* lisp/subr.el (derived-mode-add-parents): New function.
(derived-mode-all-parents): Adjust accordingly.

lisp/subr.el

index 16f327ff699f03bdef14b708d0f625aaf2872b44..b000787a5d60d4d9b04da07a1271b36852721ea9 100644 (file)
@@ -2688,22 +2688,37 @@ The returned list is not fresh, don't modify it.
       (if (memq mode known-children)
           (error "Cycle in the major mode hierarchy: %S" mode)
         (push mode known-children))
-      (let* ((parent (or (get mode 'derived-mode-parent)
+      ;; The mode hierarchy (or DAG, actually), is very static, but we
+      ;; need to react to changes because `parent' may not be defined
+      ;; yet (e.g. it's still just an autoload), so the recursive call
+      ;; to `derived-mode-all-parents' may return an
+      ;; invalid/incomplete result which we'll need to update when the
+      ;; mode actually gets loaded.
+      (let* ((all-parents
+              (lambda (parent)
+                ;; Can't use `cl-lib' here (nor `gv') :-(
+                ;;(cl-assert (not (equal parent mode)))
+                ;;(cl-pushnew mode (get parent 'derived-mode--followers))
+                (let ((followers (get parent 'derived-mode--followers)))
+                  (unless (memq mode followers)
+                    (put parent 'derived-mode--followers
+                         (cons mode followers))))
+                (derived-mode-all-parents parent known-children)))
+             (parent (or (get mode 'derived-mode-parent)
                          ;; If MODE is an alias, then follow the alias.
                          (let ((alias (symbol-function mode)))
-                           (and (symbolp alias) alias)))))
+                           (and (symbolp alias) alias))))
+             (parents (cons mode (if parent (funcall all-parents parent))))
+             (extras (get mode 'derived-mode-extra-parents)))
         (put mode 'derived-mode--all-parents
-             (cons mode
-                   (when parent
-                     ;; Can't use `cl-lib' here (nor `gv') :-(
-                     ;;(cl-assert (not (equal parent mode)))
-                     ;;(cl-pushnew mode (get parent 'derived-mode--followers))
-                     (let ((followers (get parent 'derived-mode--followers)))
-                       (unless (memq mode followers)
-                         (put parent 'derived-mode--followers
-                              (cons mode followers))))
-                     (derived-mode-all-parents
-                      parent known-children))))))))
+             (if (null extras) ;; Common case.
+                 parents
+               (delete-dups
+                (apply #'append
+                       parents (mapcar (lambda (extra)
+                                         (copy-sequence
+                                          (funcall all-parents extra)))
+                                       extras)))))))))
 
 (defun provided-mode-derived-p (mode &rest modes)
   "Non-nil if MODE is derived from one of MODES.
@@ -2715,8 +2730,7 @@ If you just want to check `major-mode', use `derived-mode-p'."
     (car ps)))
 
 (defun derived-mode-p (&rest modes)
-  "Non-nil if the current major mode is derived from one of MODES.
-Uses the `derived-mode-parent' property of the symbol to trace backwards."
+  "Non-nil if the current major mode is derived from one of MODES."
   (declare (side-effect-free t))
   (apply #'provided-mode-derived-p major-mode modes))
 
@@ -2725,6 +2739,13 @@ Uses the `derived-mode-parent' property of the symbol to trace backwards."
   (put mode 'derived-mode-parent parent)
   (derived-mode--flush mode))
 
+(defun derived-mode-add-parents (mode extra-parents)
+  "Add EXTRA-PARENTS to the parents of MODE.
+Declares the parents of MODE to be its main parent (as defined
+in `define-derived-mode') plus EXTRA-PARENTS."
+  (put mode 'derived-mode-extra-parents extra-parents)
+  (derived-mode--flush mode))
+
 (defun derived-mode--flush (mode)
   (put mode 'derived-mode--all-parents nil)
   (let ((followers (get mode 'derived-mode--followers)))