I am trying to create a function that modifies a frame's buffer-list by replacing it with the buffer listing of tabs from a Tabbar group displayed on the same frame.
This is the snippet that returns a listing of tabs from the Tabbar group displayed on the selected frame:
(mapcar (lambda (tab)
(buffer-name (tabbar-tab-value tab)))
(tabbar-tabs (tabbar-current-tabset t)))
The following function returns nil instead of the desired Modified Buffer List. Any ideas would be greatly appreciated.
(defun new-buffer-list ()
(interactive)
(message "Original Buffer List: %s" (frame-parameter (selected-frame) 'buffer-list))
(setq new-list
(mapcar (lambda (tab)
(buffer-name (tabbar-tab-value tab)))
(tabbar-tabs (tabbar-current-tabset t))))
(modify-frame-parameters (selected-frame) (list (cons 'buffer-list new-list)))
(message "The variable \"new-list\": %s" new-list)
(message "Modified Buffer List: %s" (frame-parameter (selected-frame) 'buffer-list)))
(frame-parameter 'nil 'buffer-list)
returns a list of buffers, not their names; thus you might want to replace
(lambda (tab) (buffer-name (tabbar-tab-value tab)))
with 'tabbar-tab-value
Related
Does anyone have any ideas, please, regarding how to specify (within a function) switching to a specific tab group using Tabbar 2.0 and a current version of Emacs? For example, If the sky is blue, then switch to tab group "BLUE" (and/or the most recently viewed tab / buffer within that particular tab group).
I have written a few functions that permit me to organize tab groups by frames such that the tabs appear to be associated with a given frame. However, my function cycles through the various tab groups using tabbar-forward-group until the function finally stops at the correct group -- this method is very slow.
The function tabbar-current-tabset is used to determine the name of a current tab group that has focus. The result can be seen when placing it inside a message -- e.g., (message "%s" tabbar-current-tabset). It can also be used inside a function such as . . . (if (not (equal (format "%s" tabbar-current-tabset) "common")) . . . (tabbar-forward-group).
There is only one working function I have found that permits selecting a specific tab group, which is called ido-jump-to-tab-group (set forth below): https://github.com/bamanzi/dotemacs-full/blob/master/init.d/25-tabbar.el I am looking for a way to select a specific tab group (hard-coded into the function), without pausing to manually choose it using ido . . .. I mention this because it may help someone to resolve: If the sky is blue, then switch to tab group "BLUE" (and/or the most recently viewed tab / buffer within that particular tab group).
(defun ido-jump-to-tab-group ()
"Jump to a tabbar group."
(interactive)
(if (< emacs-major-version 24)
(ido-common-initialization))
(unless (and (featurep 'tabbar)
tabbar-mode)
(error "Error: tabbar-mode not turned on."))
(set tabbar-tabsets-tabset (tabbar-map-tabsets 'tabbar-selected-tab)) ;; refresh groups
(let* ( (groups (mapcar #'(lambda (group)
(format "%s" (cdr group)))
(tabbar-tabs tabbar-tabsets-tabset)))
(group-name (ido-completing-read "Groups: " groups)) )
(mapc #'(lambda (group)
(when (string= group-name (format "%s" (cdr group)))
(message "Switch to group '%s', current buffer: %s" (cdr group) (car group))
(switch-to-buffer (car group)) ))
(tabbar-tabs tabbar-tabsets-tabset))) )
During my Google searches, I came across an apparently broken function that does not work with Tabbar 2.0 and a current version of Emacs Trunk -- it is called tabbar+switch-group: https://gist.github.com/Johniel/4324127 I mention this function because it is the only one (other than ido-jump-to-tab-group) that is related to this issue.
(defun goto-tab-group (group-name)
"Jump to a specific tabbar group."
(unless (and (featurep 'tabbar)
tabbar-mode)
(error "Error: tabbar-mode not turned on."))
(set tabbar-tabsets-tabset (tabbar-map-tabsets 'tabbar-selected-tab)) ;; refresh groups
(let* ( (groups (mapcar #'(lambda (group)
(format "%s" (cdr group)))
(tabbar-tabs tabbar-tabsets-tabset))))
(mapc #'(lambda (group)
(when (string= group-name (format "%s" (cdr group)))
(message "Switch to group '%s', current buffer: %s" (cdr group) (car group))
(switch-to-buffer (car group)) ))
(tabbar-tabs tabbar-tabsets-tabset))) )
(defun example-using-goto-tab-group ()
(interactive)
(goto-tab-group "BLUE")) ;; predefined existing tab group
EDIT (September 27, 2014): The function ido-jump-to-tab-group (in the question above) and the function goto-tab-group (in the answer immediately above) are not compatible with a custom tabbar-buffer-groups-function that groups tabs based upon buffers associated with a particular frame, with that special list imbedded in the frame-parameter (independent of the general buffer-list and general buried-buffer-list). The following functions fix that imcompatibility.
(defun ido-switch-tab-group ()
"Switch tab groups using ido."
(interactive)
(let* (
(tab-buffer-list (mapcar
#'(lambda (b)
(with-current-buffer b
(list (current-buffer)
(buffer-name)
(funcall tabbar-buffer-groups-function) )))
(funcall tabbar-buffer-list-function)))
(groups (delete-dups
(mapcar #'(lambda (group)
(car (car (cdr (cdr group))))) tab-buffer-list)))
(group-name (ido-completing-read "Groups: " groups)) )
(catch 'done
(mapc
#'(lambda (group)
(when (equal group-name (car (car (cdr (cdr group)))))
(throw 'done (switch-to-buffer (car (cdr group))))))
tab-buffer-list) )))
(defun switch-tab-group (group-name)
"Switch to a specific tab group."
(let ((tab-buffer-list (mapcar
#'(lambda (b)
(with-current-buffer b
(list (current-buffer)
(buffer-name)
(funcall tabbar-buffer-groups-function) )))
(funcall tabbar-buffer-list-function))))
(catch 'done
(mapc
#'(lambda (group)
(when (equal group-name (format "%s" (car (car (cdr (cdr group))))))
(throw 'done (switch-to-buffer (car (cdr group))))))
tab-buffer-list) )))
(defun switch-to-tab-group-n ()
"Switch to a predefined existing tab group named `N`."
(interactive)
(switch-tab-group "N"))
(defun switch-to-tab-group-a ()
"Switch to a predefined existing tab group named `A`."
(interactive)
(switch-tab-group "A"))
I'd like to create a function that offers me numbered or lettered choices (1, 2, 3, or a, b, c) of available frames to switch to, instead of manually typing the name. Aspell would be the closest example I can think of.
Could someone please share an example of how this might be done? Lines 6 to 14 of the following function creates a list of all available frame names on the fly. Additional functions related to frame switching can be found here
(defun switch-frame (frame-to)
(interactive (list (read-string (format "From: (%s) => To: %s. Select: "
;; From:
(frame-parameter nil 'name)
;; To:
(mapcar
(lambda (frame) "print frame"
(reduce 'concat
(mapcar (lambda (s) (format "%s" s))
(list "|" (frame-parameter frame 'name) "|" )
)
)
)
(frame-list) )
)))) ;; end of interactive statement
(setq frame-from (frame-parameter nil 'name))
(let ((frames (frame-list)))
(catch 'break
(while frames
(let ((frame (car frames)))
(if (equal (frame-parameter frame 'name) frame-to)
(throw 'break (select-frame-set-input-focus frame))
(setq frames (cdr frames)))))) )
(message "Switched -- From: \"%s\" To: \"%s\"." frame-from frame-to) )
EDIT (November 13, 2014): Here is a revised function using ido-completing-read:
(defun ido-switch-frame ()
(interactive)
(when (not (minibufferp))
(let* (
(frames (frame-list))
(frame-to (ido-completing-read "Select Frame: "
(mapcar (lambda (frame) (frame-parameter frame 'name)) frames))))
(catch 'break
(while frames
(let ((frame (car frames)))
(if (equal (frame-parameter frame 'name) frame-to)
(throw 'break (select-frame-set-input-focus frame))
(setq frames (cdr frames)))))))))
I see what you're trying to do. Here's how I've solved this problem:
Part 1
The files that you use every day should be bookmarked.
The reason is that you loose focus when you're reading any sort of menu,
even as short as you describe. After some time with bookmarks,
it becomes like touch-typing: you select the buffer without thinking about it.
You can check out this question
to see my system.
I've got about 20 important files and buffers bookmarked and reachable
in two keystrokes, e.g. μ k for keys.el and μ h for hooks.el.
A nice bonus is that bookmark-bmenu-list shows all this stuff, so I can
add/remove bookmarks easily
rename bookmarks (renaming changes binding)
it's clickable with mouse (sometimes useful)
bookmark+ allows function bookmarks, so I've got org-agenda on μ a
and magit on μ m.
And of course the dired bookmarks: source is on μ s and
org-files are on μ g.
Part 2
For the files that can't be bookmarked, I'm using:
(ido-mode)
(setq ido-enable-flex-matching t)
(global-set-key "η" 'ido-switch-buffer)
This is fast as well: you need one keystroke to call ido-switch-buffer
and around 2-3 letters to find the buffer you need, and RET to select.
I've also recently added this hack:
(add-hook 'ido-setup-hook
(lambda()
(define-key ido-buffer-completion-map "η" 'ido-next-match)))
With this you can use the same key to call ido-switch-buffer and cycle the selection.
Part 3
The actual function with lettered choices has been on my todo list for a while
now. I'll post back here when I get around to implementing it,
or maybe just copy the solution from a different answer:)
This answer describes Icicles command icicle-select-frame, which lets you choose frames by name using completion.
There is also Icicles command icicle-other-window-or-frame (C-x o), which combines commands icicle-select-frame, other-frame, and other-window. It lets you select a window or a frame, by its name or by order.
With no prefix arg or a non-zero numeric prefix arg:
If the selected frame has multiple windows then this is
other-window. Otherwise, it is other-frame.
With a zero prefix arg (e.g. C-0):
If the selected frame has multiple windows then this is
icicle-select-window with windows in the frame as candidates.
Otherwise (single-window frame), this is icicle-select-frame.
With plain C-u:
If the selected frame has multiple windows then this is
icicle-select-window with windows from all visible frames as
candidates. Otherwise, this is icicle-select-frame.
Depending upon the operating system, it may be necessary to use (select-frame-set-input-focus chosen-frame) instead of select-frame / raise-frame towards the end of the function.
(defface frame-number-face
'((t (:background "black" :foreground "red" )))
"Face for `frame-number-face`."
:group 'frame-fn)
(defface frame-name-face
'((t ( :background "black" :foreground "ForestGreen")))
"Face for `frame-name-face`."
:group 'frame-fn)
(defun select-frame-number ()
"Select a frame by number -- a maximum of 9 frames are supported."
(interactive)
(let* (
choice
chosen-frame
(n 0)
(frame-list (frame-list))
(total-frames (safe-length frame-list))
(frame-name-list
(mapcar
(lambda (frame) (cons frame (frame-parameter frame 'name)))
frame-list))
(frame-name-list-sorted
(sort
frame-name-list
#'(lambda (x y) (string< (cdr x) (cdr y)))))
(frame-number-list
(mapcar
(lambda (frame)
(setq n (1+ n))
(cons n (cdr frame)))
frame-name-list-sorted))
(pretty-list
(mapconcat 'identity
(mapcar
(lambda (x) (concat
"["
(propertize (format "%s" (car x)) 'face 'frame-number-face)
"] "
(propertize (format "%s" (cdr x)) 'face 'frame-name-face)))
frame-number-list)
" | ")) )
(message "%s" pretty-list)
(setq choice (read-char-exclusive))
(cond
((eq choice ?1)
(setq choice 1))
((eq choice ?2)
(setq choice 2))
((eq choice ?3)
(setq choice 3))
((eq choice ?4)
(setq choice 4))
((eq choice ?5)
(setq choice 5))
((eq choice ?6)
(setq choice 6))
((eq choice ?7)
(setq choice 7))
((eq choice ?8)
(setq choice 8))
((eq choice ?9)
(setq choice 9))
(t
(setq choice 10)))
(setq chosen-frame (car (nth (1- choice) frame-name-list-sorted)))
(when (> choice total-frames)
(let* (
(debug-on-quit nil)
(quit-message
(format "You must select a number between 1 and %s." total-frames)))
(signal 'quit `(,quit-message ))))
(select-frame chosen-frame)
(raise-frame chosen-frame)
chosen-frame))
How can I display the name of a bookmark (from 'bookmark' or 'bookmark+') in the mode line of emacs, instead of the file name?
A slightly strange request, but here you go (works for files and dired buffers):
(defun show-bookmarks-mode-line ()
(interactive)
(let (bname text)
(and
(setq bname (if (eq major-mode 'dired-mode)
default-directory
(buffer-file-name)))
(setq bname (expand-file-name bname))
(setq text
(delq nil
(mapcar
(lambda (x)
(and (equal bname
(expand-file-name
(bookmark-get-filename x)))
(substring-no-properties (car x))))
bookmark-alist)))
(setq text
(mapconcat
#'identity
text
", "))
(let ((mode-line-buffer-identification
(propertize text 'face 'mode-line-buffer-id)))
(force-mode-line-update)
(sit-for 5))
(force-mode-line-update))))
Could you elaborate on why you need it?
When we use o in dired, it opens a file in "other window". But if there is more than one other window visible, then it appears to choose randomly. Is there a way to choose which window it should open that file in?
I was annoyed by that behaviour of dired too. I like the way ace-window lets you easily select windows with only one key stroke, so a wrote a function that would let me use that to open a file from dired in a window of my choice:
(require 'ace-window)
(defun find-file-ace-window ()
"Use ace window to select a window for opening a file from dired."
(interactive)
(let ((file (dired-get-file-for-visit)))
(if (> (length (aw-window-list)) 1)
(aw-select "" (lambda (window)
(aw-switch-to-window window)
(find-file file)))
(find-file-other-window file))))
To replace dired standard find-file-other-window command add
(define-key dired-mode-map "o" 'find-file-ace-window)
I doubt the behaviour is random, but you can change it by hooking into display-buffer and binding your own command.
Note that most of the code is to sort window list by area.
(defvar le::dired-chosen-window nil
"current chosen window to show buffers for `le::dired-choose-window-action'")
(defun le::window-list-for-completion ()
"Return alist of (BUFFER-NAME . WINDOW) sorted by size,
suitable for completion."
;; Schwartzian transform
(mapcar
(lambda (cell)
(let ((window (car cell)))
(cons (concat
(when (and (boundp 'window-numbering-mode)
window-numbering-mode)
(format "%s: "
(window-numbering-get-number-string window)))
(buffer-name (window-buffer window)))
window)))
(sort (loop with boost-alist = (if (eq major-mode 'dired-mode)
(list (cons (selected-window) -1000000))
nil)
for window in (window-list nil 0 (window-at 0 0))
for index from 0
collect (cons window (+
(* (window-total-width window)
(window-total-height window)
100)
index
(let ((boost (assq window boost-alist)))
(if boost
(cdr boost)
0)))))
(lambda (a b)
(> (cdr a) (cdr b))))))
(defun le::dired-choose-window (window)
"Choose window to display buffer in.
Reset selection with universal prefix (C-u)."
(interactive (let ((completions (le::window-list-for-completion)))
(list (unless current-prefix-arg
(cdr (assoc (completing-read "window holding buffer: " completions) completions))))))
(setq le::dired-chosen-window window))
(defun le::dired-choose-window-action (buffer alist)
"action to display buffer in `le::dired-chosen-window'"
(window--display-buffer buffer le::dired-chosen-window 'reuse))
(defun le::dired-find-file-specified-window ()
"Dired command to display buffer in chosen window"
(interactive)
(let ((display-buffer-overriding-action '((le::dired-choose-window-action))))
(dired-find-file-other-window)))
(define-key dired-mode-map [remap dired-find-file-other-window] 'le::dired-find-file-specified-window)
Evaluate buffer.
choose the window with le::dired-choose-window-action
press o from dired to open file in chosen window.
Basically I want the *Messages* buffer to always scroll to the bottom when a new message arrives.
Can I do that?
I found auto-revert-tail-mode but that works for buffers that are visiting files.
When I tried it in the Messages buffer, it popped an error:
auto-revert-tail-mode: This buffer is not visiting a file
For multiple frames you probably want:
(defadvice message (after message-tail activate)
"goto point max after a message"
(with-current-buffer "*Messages*"
(goto-char (point-max))
(walk-windows (lambda (window)
(if (string-equal (buffer-name (window-buffer window)) "*Messages*")
(set-window-point window (point-max))))
nil
t)))
Just put point at the end of the buffer M->. If you don't manually move it it will stay there -- IOW, you will always see the tail.
This code seems a bit overkill, but a the simple (goto-char (point-max)) wasn't working for me:
(defadvice message (after message-tail activate)
"goto point max after a message"
(with-current-buffer "*Messages*"
(goto-char (point-max))
(let ((windows (get-buffer-window-list (current-buffer) nil t)))
(while windows
(set-window-point (car windows) (point-max))
(setq windows (cdr windows))))))
Here's an implementation that uses the new advice style.
(defun message-buffer-goto-end-of-buffer (&rest args)
(let* ((win (get-buffer-window "*Messages*"))
(buf (and win (window-buffer win))))
(and win (not (equal (current-buffer) buf))
(set-window-point
win (with-current-buffer buf (point-max))))))
(advice-add 'message :after 'message-buffer-goto-end-of-buffer)
i run 23.3 and there were still way too many occasions where the built-in 'solution' and the orginal defadvice on the message function just didn't cut it, so i wrapped that code in a list / toggle / timer set up and it's working beautifully - no more frustration when debugging!
it's generic, so works on any buffer, although i only really use it for..
(toggle-buffer-tail "*Messages*" "on")
..hope it's useful to someone.
;alist of 'buffer-name / timer' items
(defvar buffer-tail-alist nil)
(defun buffer-tail (name)
"follow buffer tails"
(cond ((or (equal (buffer-name (current-buffer)) name)
(string-match "^ \\*Minibuf.*?\\*$" (buffer-name (current-buffer)))))
((get-buffer name)
(with-current-buffer (get-buffer name)
(goto-char (point-max))
(let ((windows (get-buffer-window-list (current-buffer) nil t)))
(while windows (set-window-point (car windows) (point-max))
(with-selected-window (car windows) (recenter -3)) (setq windows (cdr windows))))))))
(defun toggle-buffer-tail (name &optional force)
"toggle tailing of buffer NAME. when called non-interactively, a FORCE arg of 'on' or 'off' can be used to to ensure a given state for buffer NAME"
(interactive (list (cond ((if name name) (read-from-minibuffer
(concat "buffer name to tail"
(if buffer-tail-alist (concat " (" (caar buffer-tail-alist) ")") "") ": ")
(if buffer-tail-alist (caar buffer-tail-alist)) nil nil
(mapcar '(lambda (x) (car x)) buffer-tail-alist)
(if buffer-tail-alist (caar buffer-tail-alist)))) nil)))
(let ((toggle (cond (force force) ((assoc name buffer-tail-alist) "off") (t "on")) ))
(if (not (or (equal toggle "on") (equal toggle "off")))
(error "invalid 'force' arg. required 'on'/'off'")
(progn
(while (assoc name buffer-tail-alist)
(cancel-timer (cdr (assoc name buffer-tail-alist)))
(setq buffer-tail-alist (remove* name buffer-tail-alist :key 'car :test 'equal)))
(if (equal toggle "on")
(add-to-list 'buffer-tail-alist (cons name (run-at-time t 1 'buffer-tail name))))
(message "toggled 'tail buffer' for '%s' %s" name toggle)))))
edit: changed functionality to display tail at the bottom of the window
Here's an amendment over Peter's / Trey's solutions
(defun modi/messages-auto-tail (&rest _)
"Make *Messages* buffer auto-scroll to the end after each message."
(let* ((buf-name "*Messages*")
;; Create *Messages* buffer if it does not exist
(buf (get-buffer-create buf-name)))
;; Activate this advice only if the point is _not_ in the *Messages* buffer
;; to begin with. This condition is required; otherwise you will not be
;; able to use `isearch' and other stuff within the *Messages* buffer as
;; the point will keep moving to the end of buffer :P
(when (not (string= buf-name (buffer-name)))
;; Go to the end of buffer in all *Messages* buffer windows that are
;; *live* (`get-buffer-window-list' returns a list of only live windows).
(dolist (win (get-buffer-window-list buf-name nil :all-frames))
(with-selected-window win
(goto-char (point-max))))
;; Go to the end of the *Messages* buffer even if it is not in one of
;; the live windows.
(with-current-buffer buf
(goto-char (point-max))))))
(advice-add 'message :after #'modi/messages-auto-tail)