emacs lisp, how to get buffer major mode? - emacs

I have tried to search Google and look in the manual, but still cannot find how to get major mode of a buffer object. Can you help me with an example or a reference. Thanks
only solution I could find was to query major-mode after changing the buffer and then changing back to original buffer. Is there a better way to do it?

Is there a problem with that?
(defun buffer-mode (buffer-or-string)
"Returns the major mode associated with a buffer."
(with-current-buffer buffer-or-string
major-mode))
with-current-buffer will restore your buffer when it returns.

For current buffer:
(message "%s" major-mode)

A simple way to do this is to use the buffer-local-value function since major-mode is a buffer-local variable:
(buffer-local-value 'major-mode (get-buffer "*scratch*"))

Just extending from previous answers - call with no arguments to get the current buffer's mode:
(defun buffer-mode (&optional buffer-or-name)
"Returns the major mode associated with a buffer.
If buffer-or-name is nil return current buffer's mode."
(buffer-local-value 'major-mode
(if buffer-or-name (get-buffer buffer-or-name) (current-buffer))))
E.g. in *scratch* buffer:
(buffer-mode) => 'lisp-interaction-mode
(buffer-mode "tasks.org") => 'org-mode

Well, describe-mode takes an optional buffer argument, but that displays the help... and I'm not exactly sure what it returns...
But that's the best I could find in a brief search... sorry...

Simply evaluate this:
(print major-mode)

Another way, apart from directly readind the major-mode variable would be by directly readind the mode-name variable.

Related

Open Org Capture buffer in specific window?

I've been an Emacs user for about a year or so. I routinely have the same window set up each session (four windows).
I've set up capture templates and can capture what I want, but: instead of capture mode temporarily jerking me out of my window setup, I'd like the chosen capture template to open in a new (fifth) window, preserving my existing layout. I typically want the capture template open for a while, so it's disruptive.
This seems like it would be an obvious option, but I can't figure it out. Thanks in advance to all the Emacs heads out there.
I came up with a easier-to-use version of Dan's answer to the linked question:
(defun my-org-capture-place-template-dont-delete-windows (oldfun &rest args)
(cl-letf (((symbol-function 'delete-other-windows) 'ignore))
(apply oldfun args)))
(with-eval-after-load "org-capture"
(advice-add 'org-capture-place-template :around 'my-org-capture-place-template-dont-delete-windows))
That is, instead of having to modify Org-mode code and remove the call to delete-other-windows, this piece of code temporarily redefines delete-other-windows to ignore while org-capture-place-template is being called.
It doesn't do quite what you want: it picks one of the existing windows and puts the capture buffer there. At least it's better than the default behaviour of removing all previous windows but one.
There's probably a way to do what you want by customising the variable display-buffer-alist, but I couldn't figure it out...
You could also use https://github.com/raxod502/el-patch and patch org-capture after loading (look for the (el-patch-remove (delete-other-windows))):
(el-patch-feature org-capture)
(with-eval-after-load 'org-capture
(el-patch-defun org-capture-place-template (&optional inhibit-wconf-store)
"Insert the template at the target location, and display the buffer.
When `inhibit-wconf-store', don't store the window configuration, as it
may have been stored before."
(unless inhibit-wconf-store
(org-capture-put :return-to-wconf (current-window-configuration)))
(el-patch-remove (delete-other-windows))
(org-switch-to-buffer-other-window
(org-capture-get-indirect-buffer (org-capture-get :buffer) "CAPTURE"))
(widen)
(org-show-all)
(goto-char (org-capture-get :pos))
(setq-local outline-level 'org-outline-level)
(pcase (org-capture-get :type)
((or `nil `entry) (org-capture-place-entry))
(`table-line (org-capture-place-table-line))
(`plain (org-capture-place-plain-text))
(`item (org-capture-place-item))
(`checkitem (org-capture-place-item)))
(org-capture-mode 1)
(setq-local org-capture-current-plist org-capture-plist)) )
For some reason, the #legoscia approach fails for me in emacs 28.
So here is the el-patch snippet as suggested previously:
(el-patch-feature org-capture)
(with-eval-after-load 'org-capture
(el-patch-define-and-eval-template
(defun org-capture-place-template)
(el-patch-remove (delete-other-windows))))

Write-only buffer?

I would like to imitate the way how most consoles work in a buffer, is there any way to do that? I.e. only allow appending text to the buffer, but not deleting?
Probably I could disable or advise some built-in functions which delete characters, but I'm afraid this isn't a very scalable solution... I could miss some.
As mentioned, you can set the read-only text-property. Or more simply you can set the buffer-read-only variable and then write a few commands that override it. For example (guaranteed 100% untested code ahead):
(defun append-only-s-i-c ()
(let ((inhibit-read-only (eobp)))
(call-interactively 'self-insert-command)))
(defvar append-only-mode-map
(let ((map (make-sparse-keymap)))
(define-key map [remap self-insert-command] 'append-only-s-i-c)
map))
(define-derived-mode append-only-mode nil "Append-Only"
"Here's the doc."
(set (make-local-variable 'buffer-read-only) t))
As demonstrated by this example, the read-only-ness of text can be overriden by a command, so even the read-only text-property won't guarantee 100% that the text will never be modified, but fairly few commands override the read-only information, so it should not be a serious problem in practice.
Another approach would be to use before-change-functions and signal an error from there if the modification takes place before EOB. Might be a better approach:
(defun append-only-b-c-f (beg end)
(if (< beg (point-max)) (error "Can't modify text: append-only!")))
(define-derived-mode append-only-mode nil "Append-Only"
"Here's the doc."
(add-hook 'before-change-functions #'append-only-b-c-f nil 'local))
I believe you want to set the read-only property on text as it is inserted, see special properties

Iswitchb ignore dired buffer

Is there any way to tell iswitchb to ignore buffers based on mode not string? I would like to exclude the buffers opened in dired mode which are very often the most numerous and it's hard to find opened files in minibuffer among them. Any help on this would be appreciated.
You can add a custom function to iswitchb-buffer-ignore to do this. There is actually an example in iswitchb.el -- based on that, this will do the trick:
(defun iswitchb-ignore-dired-mode (name)
"Ignore all dired mode buffers."
(with-current-buffer name
(derived-mode-p 'dired-mode)))
(setq iswitchb-buffer-ignore '("^ " iswitchb-ignore-dired-mode))
This preserves the default behavior of ignoring buffer names that begin with a space, and adds the filter for dired-mode buffers.
Based on a similar example in the iswitchb source code:
(defun iswitchb-ignore-dired-buffers (buffer)
(with-current-buffer buffer
(eq major-mode 'dired-mode)))
(add-to-list 'iswitchb-buffer-ignore 'iswitchb-ignore-dired-buffers)

How do you list the active minor modes in emacs?

How do you list the active minor modes in emacs?
C-h m or M-x describe-mode shows all the active minor modes (and major mode) and a brief description of each.
A list of all the minor mode commands is stored in the variable minor-mode-list. Finding out whether they're active or not is usually done by checking the variable of the same name. So you can do something like this:
(defun which-active-modes ()
"Give a message of which minor modes are enabled in the current buffer."
(interactive)
(let ((active-modes))
(mapc (lambda (mode) (condition-case nil
(if (and (symbolp mode) (symbol-value mode))
(add-to-list 'active-modes mode))
(error nil) ))
minor-mode-list)
(message "Active modes are %s" active-modes)))
Note: this only works for the current buffer (because the minor modes might be only enabled in certain buffers).
describe-mode can somehow come up with a list of enabled minor modes, why couldn't I? So after reading its source code I realized that it gets the list of active minor modes from both minor-mode-list and minor-mode-alist. Using 3rd-party dash.el list manipulation library I came with this code:
(--filter (and (boundp it) (symbol-value it)) minor-mode-list)
So, for example, to disable all minor modes, use -each:
(--each (--filter (and (boundp it) (symbol-value it)) minor-mode-list)
(funcall it -1))
Don't forget to save the list of minor modes in a variable, otherwise you would have to restart Emacs or enable them by memory.
If you want to programmatically do something with all buffers that have a certain mode active, then the best, most minimalistic, cleanest, built-in solution is as follows:
(dolist ($buf (buffer-list (current-buffer)))
(with-current-buffer $buf
(when some-buffer-local-minor-or-major-mode-variable-you-want-to-find
(message "x %s" $buf))))
It does the following:
Retrieve a list of all buffers via buffer-list, with the currently active buffer at the head of the list (so it's treated first, usually what you want, but leave out the current-buffer parameter if you don't care).
Loop through the buffer list and assign each buffer name to the variable $buf.
Use with-current-buffer $buf to tell Emacs that all code within the body should run as if it was running inside buffer $buf instead of whatever buffer you're really displaying on screen.
when <some mode variable> is the correct way to check if a mode is enabled; you can also use if and other such methods. Either way, the goal is to check if a minor or major-mode's main mode variable is set in the buffer. Almost all modes define a variable via "defining" a mode, which automatically causes them to create a buffer-local variable named after the mode, which is how this works. And if they don't have a standard variable, look at their own source code to see how their "toggle" code determines how to toggle them on and off. 99% of them use the existence of their modename's variable (and if they don't, I suggest reporting that as a bug to the mode's author). For example, to check if a buffer has whitespace-mode active, you would say when whitespace-mode.
After that, it just outputs a message to the Messages buffer, with an "x" and the name of the buffer that had the mode active. That's where you'd put your own code, to do whatever you wanted to do with the discovered buffer.
Enjoy! Onwards to greater and cleaner lisp code!
Here is a simple alternative snippet similar to some of the methods that have already been addressed in other answers:
(delq nil
(mapcar
(lambda (x)
(let ((car-x (car x)))
(when (and (symbolp car-x) (symbol-value car-x))
x)))
minor-mode-alist))
If you just want to know if a particular minor mode (say, evil-mode) is active in the buffer, you could evaluate the following:
(when (member 'evil-mode minor-mode-list)
(message "`evil-mode' is active!"))

How to automatically evaluate certain lisp code after starting an emacsclient?

When starting Emacs, init.el (or .emacs.el) is evaluated. However, when starting emacsclient, no similar lisp code is evaluated.
How can I get a lisp file to be evaluated every time I open a new emacsclient?
(This would be handy for frame specific customizations.)
I assume the answer is to use some hook, but I can't seem to find the correct hook to use.
I look forward to your answers.
You can add a function to the hook 'server-visit-hook, which is run every time the server is called (every time you call emacsclient).
I use the following code to automatically change the behavior of server buffers. I use it especially with the Firefox extension It's All Text. In that extension, buffers are named according to the domain name, so you can figure out which rule to apply by using string-match to match the name of the file.
(defun server-edit-presets ()
(cond
;; When editing mail, set the goal-column to 72.
((string-match "mail\\.google\\.com\\.[0-9a-z]+\\.txt" (buffer-name))
(longlines-mode-off)
(auto-fill-mode 1)
(set-fill-column 72)
(save-excursion
;; Don't know if this is necessary, but it seems to help.
(set-buffer (buffer-name))
(goto-char (point-min))
;; Replace non-breaking strange space characters
(while (search-forward (char-to-string 160) nil t)
(replace-match " "))))))
(add-hook 'server-visit-hook 'server-edit-presets)
(add-hook 'server-visit-hook '(lambda () (longlines-mode 1)))
If you really want new frame customizations, there's create-frame-hook which takes one arg (the new frame)...
If you mean gnuclient, you can use the command-line option "-eval" to evaluate something (and then just make an alias to always eval your customizations).
#LSW:
Try 'window-setup-hook. This addresses the annoyance since it is called even if emacsclient is not passed a file.
It seems that those hooks are no more, so here's the new version.
(add-hook 'server-after-make-frame-hook 'consult-recent-file)