Margin width toggle in Emacs? - emacs

I'm using the following snippet to set a 1-character margin to both edges of an Emacs buffer (thanks to zev!):
(add-hook 'window-configuration-change-hook
(lambda ()
(set-window-margins (car (get-buffer-window-list (current-buffer) nil t)) 1 1)))
However, I'd also like to have another configuration that gives me very wide left and right margins (say, 30 chars). How could I assign a hotkey for toggling between these two states, possibly over all buffers?
I tried the following:
(global-set-key [(control f10)]
(add-hook 'window-configuration-change-hook
(lambda ()
(set-window-margins (car (get-buffer-window-list (current-buffer) nil t)) 30 30))))
But got this error:
Wrong type argument: commandp, ((lambda nil (set-window-margins (car (get-buffer-window-list (current-buffer) nil t)) 30 30)) (lambda nil (set-window-margins (car (get-buffer-window-list (current-buffer) nil t)) 1 1)) frame-local-variables-check)
Where am I wrong? Thanks for any explanations. Cheers!
EDIT:
I am well aware of darkroom-mode that provides such wide margins. But it doesn't seem to work flawlessly on my XP.

This should get you going:
(global-set-key [C-f10]
(lambda ()
(interactive)
(set-window-margins (car (get-buffer-window-list (current-buffer) nil t)) 30 30)))
Your example does not work as the global-set-key is expecting the second argument to be a command, hence the error. Also your key definiton is a little odd.
For your comment:
(walk-windows (lambda (window) (set-window-margins window 30 30)) nil t)

Related

How to keep proper visual alignment of marker with window size changes?

I have the following elisp function and its hook...
(defun org-fontify-proof-blocks ()
(interactive)
(add-to-list 'font-lock-extra-managed-props 'display)
(let ((margin-format (format "%%%ds" left-margin-width)))
(font-lock-add-keywords nil
`(
("^#\\+begin_proof.*?\\(\n\\)"
1 '(face nil display " "))
("^\\(#\\+begin_proof.*$\\)"
1 '(face nil display ,(propertize "Proof." 'face '(:inherit bold))))
("\\(#\\+end_proof.*\\)$"
1 '(face nil display ,(propertize (concat (make-string (- (window-width) (string-width "⏹")) ?\ ) "⏹") 'face '(:inherit bold))))
)))
(font-lock-fontify-buffer))
(add-hook 'org-mode-hook #'org-fontify-proof-blocks)
This results in the proof variant of the org-special-block, given by...
#+begin_proof
<contents>
#+end_proof
getting fontified as
Proof. <contents>
⏹
The problem is that the alignment of the QED symbold (⏹) gets messed up when changing window size. How can I go about fixing this issue?
I have tried
(add-hook 'window-size-change-functions #'org-fontify-proof-blocks)
which did not really seem to do anything.
I also tried
(add-hook 'window-configuration-change-hook
(lambda ()
(with-current-buffer (current-buffer)
(org-fontify-proof-blocks))))
and
(add-hook 'window-configuration-change-hook #'org-fontify-proof-blocks)
which only seemed to keep the improper alignment after switching back to the original window size.

Make frames in Emacs GUI behaves like frames in Terminal

In terminal, Emacs manage multiple frames with names like F1, F2.... because it can't create multiple OS windows. I want the GUI version to behave this way, that is, instead of creating multiple OS windows, I want it to create many virtual frames inside a single Emacs window. Is there a way?
There is a way to mimic the frame switching behavior of terminal emacs in GUI. Here is what I did. Basicly it uses make-frame-invisible to hide inactive frames. It works well on archlinux with i3.
(defsubst +amos--is-frame-daemons-frame (f)
(and (daemonp) (eq f terminal-frame)))
(defun +amos--frame-list-without-daemon ()
"Return a list of frames without the daemon's frame."
(if (daemonp)
(filtered-frame-list
#'(lambda (f) (not (+amos--is-frame-daemons-frame f))))
(frame-list)))
(defun +amos/workspace-new ()
(interactive)
(let ((name (frame-parameter nil 'name))
(oframe (selected-frame)))
(select-frame (if (s-starts-with? "F" name)
(make-frame)
(make-frame `((name . ,name)))))
(make-frame-invisible oframe t))
(setq +amos--frame-list (reverse (+amos--frame-list-without-daemon))))
(setq +amos-tmux-need-switch nil)
;; TODO ring lru
(defun +amos/workspace-delete ()
(interactive)
(let ((f (selected-frame)))
(select-frame (previous-frame))
(make-frame-visible)
(delete-frame f))
(setq +amos--frame-list (reverse (+amos--frame-list-without-daemon)))
(+doom-modeline|set-selected-window)
(realign-windows)
(when +amos-tmux-need-switch
(shell-command! "tmux switch-client -t amos\; run-shell -t amos '/home/amos/scripts/setcursor.sh $(tmux display -p \"#{pane_tty}\")'")
(setq +amos-tmux-need-switch nil)))
(defun +amos/workspace-switch-to (index)
(interactive)
(when (< index (length +amos--frame-list))
(let ((frame (nth index +amos--frame-list))
(oframe (selected-frame)))
(select-frame frame)
(raise-frame frame)
(make-frame-invisible oframe t)
(setq +amos-tmux-need-switch nil)
(realign-windows)
(recenter))))
(defun +amos/workspace-switch-to-1 () (interactive) (+amos/workspace-switch-to 0))
(defun +amos/workspace-switch-to-2 () (interactive) (+amos/workspace-switch-to 1))
(defun +amos/workspace-switch-to-3 () (interactive) (+amos/workspace-switch-to 2))
(defun +amos/workspace-switch-to-4 () (interactive) (+amos/workspace-switch-to 3))
(defun +amos/workspace-switch-to-5 () (interactive) (+amos/workspace-switch-to 4))
(defun +amos/workspace-switch-to-6 () (interactive) (+amos/workspace-switch-to 5))
(defun +amos/workspace-switch-to-7 () (interactive) (+amos/workspace-switch-to 6))
(defun +amos/workspace-switch-to-8 () (interactive) (+amos/workspace-switch-to 7))
(defun +amos/workspace-switch-to-9 () (interactive) (+amos/workspace-switch-to 8))
(defun +amos-workspace-cycle (off)
(let* ((n (length +amos--frame-list))
(index (-elem-index (selected-frame) +amos--frame-list))
(i (% (+ off index n) n)))
(+amos/workspace-switch-to i)))
(defun +amos/workspace-switch-left () (interactive) (+amos-workspace-cycle -1))
(defun +amos/workspace-switch-right () (interactive) (+amos-workspace-cycle +1))
(defun +amos|maybe-delete-frame-buffer (frame)
(let ((windows (window-list frame)))
(dolist (window windows)
(let ((buffer (window-buffer (car windows))))
(when (eq 1 (length (get-buffer-window-list buffer nil t)))
(kill-buffer buffer))))))
(add-to-list 'delete-frame-functions #'+amos|maybe-delete-frame-buffer)
If what you mean is that you want to be able to access frames by name, then yes, you can do this with Icicles.
By default, C-x 5 o is bound to multi-command icicle-select-frame. This lets you select one or more frames by name.
A frame's name comes from its name frame parameter. It is suffixed as needed by [NUMBER], to
make it unique. For example, in a context where frames are named for
their buffers and you have two frames showing buffer *Help*, one of
the frames will be called *Help*[2] for use with this command.
Frame selection with C-x 5 o uses completion and cycling. The completion can be vanilla Emacs completion or regexp (including, of course, substring) completion. (It can also be any of several fuzzy completions.)
(If, for some reason, you want the frame names to be just F1, F2, etc., as with terminal Emacs, then you just need to do that at the level of frame parameter name. You can do that using hooks etc.)

Changing margin for emacs text-mode

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.

How can I execute a lisp function while pressing prefix key C-h and then let Emacs continue the normal processing of C-h?

I want to execute a lisp function while pressing prefix key C-h and then let Emacs continue the normal processing of C-h.
How can I do it?
Thank you!
Evaluating
(key-binding [(control h)])
I found out the command bound is help-command. You can use an "around" defadvice to run your code. See manual.
(defvar smart-ime--state 'normal)
(defvar smart-ime--debug nil)
(defvar smart-ime--ena-prefix-override-keymap nil)
(defvar smart-ime--prefix-override-keymap (make-sparse-keymap))
(defvar smart-ime--keymaps-initialized nil)
(defvar smart-ime--keymap-alist
`(
(smart-ime--ena-prefix-override-keymap . ,smart-ime--prefix-override-keymap)
)
)
(defun smart-ime--init-keymaps ()
(define-key smart-ime--prefix-override-keymap [(control x)] 'smart-ime--prefix-override-handler)
(define-key smart-ime--prefix-override-keymap [(control c)] 'smart-ime--prefix-override-handler)
(define-key smart-ime--prefix-override-keymap [(control h)] 'smart-ime--prefix-override-handler)
)
(defun smart-ime--prefix-override-handler (arg)
(interactive "P")
(smart-ime--prefix-override-replay arg))
;; the most important part
(defun smart-ime--prefix-override-replay (arg)
(let* ((keys (this-command-keys))
(i (length keys))
(key (aref keys (1- i))))
(ime-save-and-set-status 0)
(add-hook 'post-command-hook 'smart-ime--post-command-handler)
(setq smart-ime--state 'prefix)
(setq smart-ime--ena-prefix-override-keymap nil)
;; Don't record this command
(setq this-command last-command)
;; Restore the prefix arg
(setq prefix-arg arg)
(reset-this-command-lengths)
;; Push the key back on the event queue
(setq unread-command-events (cons key unread-command-events))))
(defun smart-ime--post-command-handler-1 ()
(cond ((eq smart-ime--state 'prefix)
(setq smart-ime--state 'sequence))
((eq smart-ime--state 'sequence)
(ime-restore-status)
(setq smart-ime--ena-prefix-override-keymap t)
(setq smart-ime--state 'normal)
(remove-hook 'post-command-hook 'smart-ime--post-command-handler)))
(t
(error "error state")))
(defun smart-ime--post-command-handler ()
(when smart-ime-mode
(condition-case nil
(smart-ime--post-command-handler-1)
(error nil))))
(define-minor-mode smart-ime-mode
"Toggle Smart IME mode."
:init-value nil
:lighter " SmartIME"
:global t
(unless smart-ime--keymaps-initialized
(smart-ime--init-keymaps)
(setq smart-ime--keymaps-initialized t))
(unless smart-ime-mode
(remove-hook 'post-command-hook 'smart-ime--post-command-handler))
(if (not smart-ime-mode)
(setq emulation-mode-map-alists (delq 'smart-ime--keymap-alist emulation-mode-map-alists))
(add-to-ordered-list 'emulation-mode-map-alists 'smart-ime--keymap-alist 400)
(setq smart-ime--ena-prefix-override-keymap t))
)
;;; Announce
(provide 'smart-ime)

Intelligent auto closing matching characters

In some of the modes I'm using, emacs helps me by auto closing some elements such as quotes, parenthesis.
However some times, out of habit I type the closing element my self and end up with ()) or """.
How can I set up emacs to ignore the extra key?
While it is fun to roll your own, autopair has emerged as the canonical solution to this problem. It does everything you ask, and a few things, you didn't know you wanted. Emacs wiki entry.
Emacs 24 (currently in pretest) will be prepackaged with an electric pairing package. Autopair is still much more sophisticated than the builtin one.
EDIT: I had had the following in my .emacs for a while, and it worked fine so I didn't think too much about it. As event_jr points out in his answer, the same features (and apparently a bit more) can be had with the autopairs.el package, linked from the same page that I got this code from.
I have the following code in my .emacs to do this, taken from the emacs wiki:
(setq skeleton-pair t)
(setq skeleton-pair-alist
'((?\( _ ?\))
(?[ _ ?])
(?{ _ ?})
(?\" _ ?\")))
(defun autopair-insert (arg)
(interactive "P")
(let (pair)
(cond
((assq last-command-char skeleton-pair-alist)
(autopair-open arg))
(t
(autopair-close arg)))))
(defun autopair-open (arg)
(interactive "P")
(let ((pair (assq last-command-char
skeleton-pair-alist)))
(cond
((and (not mark-active)
(eq (car pair) (car (last pair)))
(eq (car pair) (char-after)))
(autopair-close arg))
(t
(skeleton-pair-insert-maybe arg)))))
(defun autopair-close (arg)
(interactive "P")
(cond
(mark-active
(let (pair open)
(dolist (pair skeleton-pair-alist)
(when (eq last-command-char (car (last pair)))
(setq open (car pair))))
(setq last-command-char open)
(skeleton-pair-insert-maybe arg)))
((looking-at
(concat "[ \t\n]*"
(regexp-quote (string last-command-char))))
(replace-match (string last-command-char))
(indent-according-to-mode))
(t
(self-insert-command (prefix-numeric-value arg))
(indent-according-to-mode))))
(defun autopair-backspace (arg)
(interactive "p")
(if (eq (char-after)
(car (last (assq (char-before) skeleton-pair-alist))))
(and (char-after) (delete-char 1)))
(delete-backward-char arg))
(global-set-key [backspace] 'autopair-backspace)
(define-key isearch-mode-map [backspace] 'isearch-delete-char) ;; required to fix behaviour in isearch
(global-set-key "(" 'autopair-insert)
(global-set-key ")" 'autopair-insert)
(global-set-key "[" 'autopair-insert)
(global-set-key "]" 'autopair-insert)
(global-set-key "{" 'autopair-insert)
(global-set-key "}" 'autopair-insert)
(global-set-key "\"" 'autopair-insert)
I'm not sure if it's Emacs 24 feature only, but electric-pair-mode seems to do what you want.