9

I would like to do the following: load some theme when working on a particular kind of source file (say, Haskell), but elsewhere use a different "default" theme.

I naively imagine that this is achievable by loading the the default theme when exiting haskell-mode, but I don't know how to do things on exiting modes. Is this possible, or is there some better way to achieve what I want?

5
  • You certainly can do things when exiting a mode (change-major-mode-hook for major modes, and for minor modes just using its own mode hook), but from my reading that's not remotely what you want. Emacs maintains an arbitrary number of buffers, each of which can have different modes to any others, and switching buffers does not "exit" any of the modes which are active in the buffer you started in -- so reacting to mode changes doesn't help you with your problem.
    – phils
    Commented Jul 18, 2018 at 11:39
  • 1
    I suggest rephrasing your question in more general terms, to avoid this confusion. It sounds as if you would like your haskell-mode buffers (possibly along with certain other particular modes) to look different to other buffers. Try elaborating on that requirement, and what you would like to change specifically.
    – phils
    Commented Jul 18, 2018 at 11:40
  • ...by which I meant rewrite the question text (not just the title). Never mind. AFAIK you can't do this with the 'theme' system. I'm pretty sure themes are global. You can have buffer-local face remapping however, and that could take place in a mode hook, so I think there's a solution for you -- but this is why I asked you to be specific about what you want to change, so someone might be able to show you how to do those specific things.
    – phils
    Commented Jul 18, 2018 at 21:40
  • This is similar to this question: emacs.stackexchange.com/questions/41772. Commented Jul 19, 2018 at 9:12
  • @phils: Please consider posting your comment (about not being able to do this) as an answer.
    – Drew
    Commented Sep 23, 2018 at 0:04

4 Answers 4

9

AFAIK you can't do this with the 'theme' system. I'm pretty sure themes are global.

You can have buffer-local face remapping however, and that could take place in a mode hook, so I think there's a solution for you.

e.g.:

(defun my-haskell-faces ()
  "Buffer-local face remapping for `haskell-mode-hook'."
  (face-remap-add-relative 'default
                           :background "darkgreen"
                           :foreground "lightblue"))

(add-hook 'haskell-mode-hook #'my-haskell-faces)
3
3

You may use per-buffer-theme.el. For example you could start with a setup like this:

(require 'per-buffer-theme)

(setq per-buffer-theme/use-timer t)
(setq per-buffer-theme/timer-idle-delay 0.1)
(setq per-buffer-theme/default-theme 'notheme)
(setq per-buffer-theme/themes-alist
      '(((:theme . dichromacy)
         (:buffernames nil)
         (:modes haskell-mode
                 haskell-interactive-mode)))) ; Is this the mode the Haskell REPL in Emacs uses? I'm not sure.

In my testing it has worked best with per-buffer-theme/use-timer set to non-nil. Also, you may want to tell per-buffer-theme to avoid changing themes for some buffers (say, Help buffers). You can do it with the per-buffer-theme/ignored-buffernames-regex variable:

(setq per-buffer-theme/ignored-buffernames-regex
      (append ido-ignore-buffers '("*Completions*" "*Help*" "*info*" "*Warnings*")
              per-buffer-theme/ignored-buffernames-regex))

This question had been asked on Stack Overflow a few years ago: Different color themes per mode in Emacs. There are other approaches there but notice that most answers are old and better solutions could have come up since then.

2

A theme is global, but you can cause a given theme to be used when you visit a file with a given major mode: You can use the mode hook of a mode you're interested in to start using the theme you want for that mode.

auto-mode-alist associates file types (extensions) with major modes, and each major mode has a mode hook. So you can associate file types with themes by using mode hooks to turn on given themes.

IOW:

  • file extension => specific major mode
  • major mode's hook => specific theme

The theme you change to because of a mode hook will then be used everywhere (all buffers, all modes), until you visit a file with a different mode, whose mode hook says to use a different theme. That behavior might not be what you want. But it's one possibility to consider.

-1

Check current buffer major-mode at opening new file event and with timer:

(defun my/change-theme ()
  (cl-case major-mode
    (org-mode (load-theme 'wombat t))
    (otherwise (load-theme 'adwaita t))))
;; On file open event:
(add-hook 'find-file-hook 'my/change-theme)
;; With timer every N seconds in case you switch buffer:
(run-with-idle-timer 1 t 'my/change-theme)
2
  • 2
    load-theme is forcibly called every second, even if the theme being loaded was the same theme loaded the previous second, so that's pretty terrible. I couldn't recommend this approach regardless, but at minimum you should fix that.
    – phils
    Commented Jan 22 at 12:11
  • You should also read C-h i g (elisp)Window Hooks -- you can react to things if and when they change, rather than on a timer "just in case" they changed.
    – phils
    Commented Jan 23 at 3:42

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.