2

I am working upon 2 projects that have similar structure. And sometimes occasionally modify a file in the wrong buffer.

So I desided to set different colours for the files in the different projects:

(add-hook 'window-buffer-change-functions
  `(lambda (window)
     (interactive)
     (let ((full-name (buffer-file-name)))
       (cond
         ((string-match ".*/project_1/" full-name)
           (set-face-attribute 'mode-line-buffer-id (selected-frame) :foreground "orange"))
         ((string-match ".*/project_2/" full-name)
           (set-face-attribute 'mode-line-buffer-id (selected-frame) :foreground "violet"))
         (t
           (set-face-attribute 'mode-line-buffer-id (selected-frame) :foreground "white"))))))

Questions:

  1. When the files from different projects are opened side by side, the colour of the last one is used.
  2. When the colour changed for the first time a strange circle appers.
  3. Is is possible to use window parameter, instead of calling buffer-file-name and selected-frame?

enter image description here

6
  • Pose only one question per post, please.
    – Drew
    Apr 4, 2023 at 15:19
  • Have you noticed that the code above gives #<frame Messages - GNU Emacs at OoO 0x55a3ffb484a8>) signaled (wrong-type-argument stringp nil) on all *xxxx* buffers (check out the Messages buffer)? What can be the reason for this? How to eliminate this? By the way: the code works OK also without (interactive).
    – Claudio
    Apr 5, 2023 at 10:47
  • Use: (let ((full-name (if (buffer-file-name) (buffer-file-name) "" ))) to avoid the problem with wrong-type-argument stringp nil.
    – Claudio
    Apr 5, 2023 at 11:00
  • @Claudio Sometimes the buffer has no files attached, and your improvement will fix this.
    – user4035
    Apr 5, 2023 at 11:20
  • @Claudio Unfortunately, I am unable to do the colourizing with individual windows when the frame is split.
    – user4035
    Apr 5, 2023 at 11:20

2 Answers 2

0

Your code seems to work for me (?). I made this change, but it likely doesn't matter:

There's no need to use the prefix .*, and no reason to set the match-data, so you can just use, e.g., (string-match-p "/project_1/" full-name).

Do you see the same problem if you start Emacs with emacs -Q? If not, bisect your init file to find the culprit.


Also, you might just want to set variable mode-line-buffer-identification instead of modifying face mode-line-buffer-id. E.g.:

(let ((strg  (car mode-line-buffer-identification)))
  (when (and (consp strg)  (eq (car strg) ':eval))
    (setq strg  (car (eval (cadr strg)))))
  (setq mode-line-buffer-identification
        (list (propertize strg 'face (if (string-match-p "/project1/" strg)
                                         'highlight
                                       'font-lock-warning-face)))))

Your comment here said:

The code works, but it highlights the names for 2 different projects with the same colour based upon who was opened the latest. Is it possible to make 1 orange and 1 violet? mode-line-buffer-id modifies the colour for entire frame with all the windows.

C-h v window-buffer-change-functions includes this, which explains that, I think -- I think this is happening with your code:

Functions specified by the default value are called for each frame if at least one window on that frame has been added, deleted or changed its buffer since the last redisplay. In this case the frame is passed as argument.

That help output also says this:

Functions specified buffer-locally are called for each window showing the corresponding buffer if and only if that window has been added or changed its buffer since the last redisplay. In this case the window is passed as argument.

So maybe try using add-hook with non-nil 4th argument, LOCAL. In this case, the window, not the frame, is passed to the hook function as the argument.

10
  • The code works, but it highlights the names for 2 different projects with the same colour based upon who was opened the latest. Is it possible to make 1 orange and 1 violet? mode-line-buffer-id modifies the colour for entire frame with all the windows.
    – user4035
    Apr 4, 2023 at 16:09
  • Tried LOCAL. Gives an error if added to (add-hook ... LOCAL) . What do you mean with 4th argument and LOCAL?
    – Claudio
    Apr 4, 2023 at 19:03
  • C-h f add-hook.
    – Drew
    Apr 4, 2023 at 20:32
  • @Claudio No, LOCAL is a formal parameter. Put t there as an actual parameter.
    – user4035
    Apr 5, 2023 at 7:28
  • @user4035 : to put t for LOCAL it is probably necessary to put for example 0 for DEPTH like this : (add-hook 'window-buffer-change-functions (lambda ... ) 0 t right? The result is that the coloring does not work at all, right or wrong?
    – Claudio
    Apr 5, 2023 at 7:35
0

The Emacs elisp set-face-attribute accepts only frames as parameter, not windows. This makes it impossible to use it for setting of different colors in different windows of the same frame.

A work-around it which could be accepted as an intermediate solution (until a way of window-specific change of the font face color is pointed out in another answer) will be to launch Emacs twice and position its frames side by side instead of side by side windows in only once launched Emacs.

Below ready to use in an initialization file code which fixes the problem with throwing warning signals to the *Message* buffer while viewing non-file buffers. The code allows also to set the color in which non-file buffer names are displayed in the modeline.

In order to improve the orientation even more it adds code for displaying the full path of the file (or the buffer name) in the title bar.

;; ----------------------------------------------------------------------
(defun current-buffer-filename-or-the-buffer-name ()
  (let ((buf (if (active-minibuffer-window)
                 (window-buffer (minibuffer-selected-window))
               (current-buffer))))
    ;; use full file name if there one, otherwise just buffer name.
    (or (buffer-file-name buf) (buffer-name buf))))
;; Show the buffer names displayed in the modeline in different colors
;; and display full paths of the files in the title bar
(add-hook 'window-buffer-change-functions
  " Show buffer names in the modeline in different colors "
  `(lambda (&optional window &rest rest)
     (setq frame-title-format '(:eval (concat "Emacs :      " (current-buffer-filename-or-the-buffer-name))))
     (let ((full-file-name (if (buffer-file-name) (buffer-file-name) "")))
       (cond
         ((string-match ".*/project_1/" full-name)
           (set-face-attribute 'mode-line-buffer-id (selected-frame) :foreground "orange"))
         ((string-match ".*/project_2/" full-name)
           (set-face-attribute 'mode-line-buffer-id (selected-frame) :foreground "violet"))
         ((buffer-file-name)  ;; any other files:  white
           (set-face-attribute 'mode-line-buffer-id (selected-frame) :foreground "white"))))))
         (t                   ;; non-file buffers: gray
           (set-face-attribute 'mode-line-buffer-id (selected-frame) :foreground "gray"))))))

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.