I'm trying to write a function that makes all the windows in an Emacs frame 120 characters wide. So far I have this:
(defun standard-width ()
"makes the widht of the window 120, our coding standard"
(interactive)
(delete-other-windows)
(set-frame-width (selected-frame) 120 )
)
However I'd like to have this work without the delete-other-windows call. Unfortunately, without this call the total frame width is 120, shared among windows. How can I make the total frame width (maximum number of windows in the horizontal direction) * 120?
I don't know of a simple way to do this. I would use (window-tree (selected-frame)), and parse the return value to find the row with the maximum number of windows laid out horizontally, and use the count to calculate frame width and hope that the windows would scale correctly. If the windows do not scale right, then I'd attempt to resize them individually (after setting the frame width) using the window-resize function defined in window.el.
The format of the return value for window-tree can be found in Emacs Lisp document.
Something along the lines of the following function should do the trick:
(defun horizontal-window-count (tree)
(if (atom tree)
1
(if (car tree)
(apply 'max (mapcar 'horizontal-window-count (cddr tree)))
(apply '+ (mapcar 'horizontal-window-count (cddr tree))))))
You use it as (horizontal-window-count (car (window-tree))). The code can probably be simplified and it may have some issues, but I am also not very fluent in lisp.
This will set the frame size to the number of windows * 120, but the meaning of a "number of windows horizontally" needs greater definition.
(set-frame-width (selected-frame) (* 120 (length (window-list))))
The current solution is all its ugly glory.
(defun horizontal-window-count (tree)
(if (atom tree)
1
(if (car tree)
(apply 'max (mapcar 'horizontal-window-count (cddr tree)))
(apply '+ (mapcar 'horizontal-window-count (cddr tree))))))
(defun horz-count ()
(horizontal-window-count (car (window-tree))))
(defun standard-width ()
"makes the widht of the window 120, our coding standard"
(interactive)
(set-frame-width (selected-frame) (* 120 (horz-count)))
)
Related
I have an idea for a possibly cool/probably stupid emacs script which would dynamically resize text to fill available space.
One thing I can't seem to figure out is how to query the current buffer to see if any lines are currently being wrapped. How would I do it?
You can check if any lines are wrapped in the current buffer with function like this:
(defun wrapped-lines-p ()
(save-excursion
(goto-char (point-min))
(let ((long-line-regexp
(format "^.\\{%d\\}.+$" (window-body-width))))
(search-forward-regexp long-line-regexp nil t))))
As noted in the comments, this doesn't take into account the buffer's font size. Since buffers can have a mix of different sized fonts, the window text pixel size needs to be tested. Try this:
(defun wrapped-lines-p ()
(let ((window-width-pixels (window-body-width nil t)))
(> (car (window-text-pixel-size nil nil nil (1+ window-width-pixels)))
window-width-pixels)))
Note that "any lines are currently being wrapped" is a property of the window, not the buffer.
Given a window, you can scan it from top visible line to bottom and compare line length to window-width:
(defun window-long-lines-p ()
"Return t is any visible line in the current window is longer than window width."
(save-excursion
(move-to-window-line -1)
(let ((end (point)) here
found-long-line)
(move-to-window-line 0)
(while (and (not found-long-line)
(< (setq here (point)) end))
(when (< (window-width)
(- (progn (forward-line 1)
(point))
here))
(setq found-long-line t)
(message "long line: %d" (1- (line-number-at-pos)))))
found-long-line)))
The only way I found to change margins in emacs to my liking without things acting funny is this:
(add-hook 'window-configuration-change-hook
(lambda ()
(set-window-margins (car (get-buffer-window-list (current-buffer) nil t)) 24 24)))
I would like for this setting to be invoked only in text-mode and change back when I change to other modes. Somewhat naively I tried this:
(add-hook 'text-mode-hook
(lambda ()
(set-window-margins (car (get-buffer-window-list (current-buffer) nil t)) 24 24)))
But it's not working. What would be the right code to have the margins only change for buffers in text-mode?
Even though you can set the margins using set-window-margins, they are lost as soon as you change the window in any way. A better solution is to set the variables left-margin-width and right-margin-width. For example:
(defun my-set-margins ()
"Set margins in current buffer."
(setq left-margin-width 24)
(setq right-margin-width 24))
(add-hook 'text-mode-hook 'my-set-margins)
How about something like this? Your problem likely stems from the fact that many major modes inherit text-mode. You can either eliminate your window-configuration-change-hook, or you can use your new function major-briggs with it -- personally I'd just use the text-mode-hook with the major-briggs function.
(add-hook 'text-mode-hook (lambda ()
(major-briggs)
;; insert additional stuff if so desired
))
(defun major-briggs ()
(when (eq major-mode 'text-mode)
(set-window-margins
(car (get-buffer-window-list (current-buffer) nil t)) 24 24) ))
Here's some code to center your markdown and text files within 80 characters. This adjusts the margins for all your frames automatically.
;; Add left and right margins, when file is markdown or text.
(defun center-window (window) ""
(let* ((current-extension (file-name-extension (or (buffer-file-name) "foo.unknown")))
(max-text-width 80)
(margin (max 0 (/ (- (window-width window) max-text-width) 2))))
(if (and (not (string= current-extension "md"))
(not (string= current-extension "txt")))
;; Do nothing if this isn't an .md or .txt file.
()
(set-window-margins window margin margin))))
;; Adjust margins of all windows.
(defun center-windows () ""
(walk-windows (lambda (window) (center-window window)) nil 1))
;; Listen to window changes.
(add-hook 'window-configuration-change-hook 'center-windows)
With the following code:
(defvar-local my-text-width nil
"Text area width for current buffer, or nil if no attention needed.")
(put 'my-text-width 'safe-local-variable #'integerp)
(defun my--margin-setup ()
"Handle settings of `my-text-width'."
(walk-windows
(lambda (window)
(with-current-buffer (window-buffer window)
(let ((margin (and my-text-width
(/ (max 0 (- (window-total-width)
my-text-width))
2))))
(when (or (not (equal margin left-margin-width))
(not (equal margin right-margin-width)))
(setq left-margin-width margin)
(setq right-margin-width margin)
(set-window-buffer window (current-buffer))))))))
(add-hook 'window-configuration-change-hook #'my--margin-setup)
You can set my-text-width in a file-local or directory-local variable, and Emacs will automatically set the margins accordingly to get that text width. It works even when you resize the frame or introduce splits.
When entering occur mode for example (occur "test") the frame splits into two windows as shown below:
As seen the Occur buffer is taking up too much space on the frame, since there is only two matches (for the text "test"). I would like to shrink that window accordingly.
I tried the following code:
(defun test-occur ()
(interactive)
(occur "test")
(save-window-excursion
(other-window 1)
(let (( win (selected-window))
(n (count-lines (point-min) (point-max)))
(h (window-body-height)))
(let ((delta (- (- h n) 3)))
(window-resize win (- 0 delta) nil)))))
But it does not work (nothing happens with the Occur window)..
Just do this:
(add-hook 'occur-hook
(lambda ()
(save-selected-window
(pop-to-buffer "*Occur*")
(fit-window-to-buffer))))
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))
Could someone please give me hand with a function that detects whether a frame named "xyz" exists, and if so, then switch to that frame. I'm using frame-cmds to give each frame a user-defined name: http://www.emacswiki.org/emacs/frame-cmds.el
I would imagine it is similar to a buffer, but I'm not finding anything on Google. Here is the buffer function:
(defun buffer-exists (bufname)
(not (eq nil (get-buffer bufname))))
(defun lawlist-switch-to-buffer-xyz ()
(interactive)
(if (buffer-exists "xyz")
(switch-to-buffer "xyz") ))
Here is a semi-related post: https://superuser.com/questions/358037/emacsclient-create-a-frame-if-a-frame-does-not-exist
EDIT (September 15, 2014): Modified the function ido-switch-frame to make frame-to a let-bound variable, and removed the message. Removed previous edits as the functions get-a-frame and get-frame-name written by Drew Adams are sufficient when used in conjunction with select-frame-set-input-focus -- see his answer below.
(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)))))))))
There may be more elegant solutions but this gets the job done:
(defun switch-to-frame (frame-name)
(interactive "sFrame name:")
(let ((frames (frame-list)))
(catch 'break
(while frames
(let ((frame (car frames)))
(if (equal (frame-parameter frame 'name) frame-name)
(throw 'break (select-frame-set-input-focus frame))
(setq frames (cdr frames))))))))
Wrt your request for "a function that detects whether a frame named "xyz" exists": You already have that, since you say you are using frame-cmds.el, which requires frame-fns.el --- Function get-a-frame does just that.
Icicles provides multi-command icicle-select-frame, which lets you choose frames by name using completion.