emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Specifiers (was: face-remapping patch)


From: Stefan Monnier
Subject: Specifiers (was: face-remapping patch)
Date: Thu, 29 May 2008 11:45:25 -0400
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/23.0.60 (gnu/linux)

>>> Miles, could you clarify why you are proposing a remapping list rather than
>>> "buffer-local faces"?
>> 
>> I think adding "buffer-local faces" directly is no better.  The good
>> thing about Miles's patch is that it leverages buffer-local variables to
>> modify faces buffer-locally.  As a result, his patch is pretty small.
>> 
>> To improve on this, I think we'd have to move to something more like
>> XEmacs's specifiers.  But I haven't even seen any proposal for such
>> a thing yet.

> Proposal: allow a third argument for make-local-variable.

For me, any such proposal which uses variables is simply a non-starter.

To undestand why, consider the following, both from the point of view of
the implementation (where variable access needs to be fast) and of the
specification of meaningful semantics:

- what should (setq var val) do?
  currently is magically uses either the global or the current-buffer
  locus depending on whether the variable is already buffer-local.
  but if it's buffer-local and frame-local, which setting should
  be changed?

- how does it interact with make-variable-buffer-local?
  I.e. if a variable is just window-local and you do (setq var val), is
  it going to be made buffer-local?

- what about `let'?  This one is really fun!
  what does
     (let ((var val)) .. (make-local-variable 'var locus) ..)
  do?  how 'bout
    (make-local-variable 'var foo) .. (let ((var val)) .. (setq var foo)?
  or
    (make-local-variable 'var foo)..(let ((var val))..(select-other-foo..)var)?


BTW, for what it's worth, I have played a tiny bit with something
similar to what you propose (except that it is separate from variables,
as you may have guessed ;-).  See sample code below.


        Stefan


;;;; Settings

;; A "setting" is the Emacs equivalent of an XEmacs "specifier".

(defvar settings--table (make-hash-table :weakness 'key))

(defun settings--put (setting value context &optional overwrite)
  (assert (not (hash-table-p value)))
  (let ((table settings--table)
        (selector setting))
    (while context
      (assert (hash-table-p table))
      (let ((entry (gethash selector table)))
        (if (hash-table-p entry)
            (setq table entry)
          (setq table (puthash selector (make-hash-table :weakness 'key) table))
          (if entry (puthash t entry table))))
      (setq selector (pop context)))
    (if overwrite
        (puthash selector value table)
      (let ((prev (gethash selector table)))
        (if (hash-table-p prev)
            (puthash t value prev)
          (puthash selector value table))))))

(defun settings--permute (xs &optional ys xss)
  (if (null xs)
      (cons ys xss)
    (dolist (x xs)
      (setq xss (settings--permute (remq x xs) (cons x ys) xss)))
    xss))
 
(defun settings-put (setting value context)
  (assert (and value (not (hash-table-p value))))
  (assert (listp context))
  (dolist (context (settings--permute context))
    (settings--put setting value context)))

(defun settings--context ()
  ;; The order indicates the precedence: a buffer-local settings takes
  ;; precedence over a window-local setting, ...
  (list (current-buffer)
        (selected-window)
        (selected-frame)
        (selected-terminal)
        major-mode))

(defun settings--get-closest (table)
  (let ((workqueue (list table)))
    (while workqueue
      (setq table (pop workqueue))
      (if (gethash t table)
          (throw 'closest (gethash t table))
        (maphash (lambda (k v)
                   (if (hash-table-p v)
                       ;; Duh!  O(N) insertion into the workqueue :-(
                       (setq worklist (append workqueue (list v)))
                     (throw 'closest v)))
                 table)))))

(defun settings--get (table context closest)
  (let (entry)
    (cond
     ((not (hash-table-p table)) table)
     ((not context)
      (or (gethash t table)
          (when closest
            ;; There is some setting somewhere in a subcontext: use it.
            (catch 'closest
              (settings--get-closest table)))))
     (t
      (or (and (setq entry (gethash (pop context) table))
               (settings--get entry context closest))
          (settings--get table context closest))))))

(defun settings-get (setting &optional context closest)
  (unless context (setq context (settings--context)))
  (let ((table (gethash setting settings--table)))
    (if (not (hash-table-p table))
        table
      (settings--get table context closest))))




reply via email to

[Prev in Thread] Current Thread [Next in Thread]