reddit / emacs
Weekly Tips, Tricks, &c. Thread
AutoModerator · 2022-10-12 08:15
This is a thread for smaller, miscellaneous items that might not warrant a full post on their own.
[See this search for previous "Weekly Tips, Tricks, &c." Threads.](https://www.reddit.com/r/emacs/search?q=Weekly+tips&restrict_sr=on&sort=new&t=all)
Don't feel constrained in regards to what you post, just keep your post vaguely, generally on the topic of emacs.
MitchellMarquez42 · 2022-10-12 08:30
For anyone using linum-relative, I made a fork (https://git.mitchmarq42.xyz/mitch/linum-relative-justify.el) that justifies the current line number to the left (as in vim `set number relativenumber`) . EG:
2
1
10
1
2
It's a small addition, and all pre-existing features are still supported.
In the future I may try putting flycheck arrows in the same column as linum numbers (like in vim `set signcolumn=number`) so maybe stay tuned.
Icommentedtoday · 2022-10-12 13:26
Why are you still using linum-mode instead of display-line-numbers-mode? Display-line-numbers-mode is way faster as it's written partly in C
MitchellMarquez42 · 2022-10-12 14:57
I don't understand C at all, and display-line-numbers-mode doesn't seem to be able to do what I was looking for.
Besides, linum is plenty fast everywhere I've tested it.
If there is a way for display-line-numbers to get the current number to pad itself to the left every time it updates, I'd love to hear it.
Icommentedtoday · 2022-10-12 18:24
Fair enough! I'd have to take a look to be sure that's possible. Cool package though
gusbrs · 2022-10-12 18:38
I've been using `mu4e` for some years now, and I really love it. However, I'm also a Gmail user, don't love it, but consider myself stuck with it (given budget constraints and it being my email for several years already). This makes me always weary of "the next Google shenanigan" which might break my workflow. One of the things I have learned to cherish about using `mu4e` is its integration with Org, with it's `org-capture` support, which enables me not to confuse my Inbox with my todo list, thus keeping my Inbox clean. So I came up with a preventive function, leveraging `org-protocol` to be able to capture a Gmail message from a bookmarklet on the browser. Not particularly pretty code, but functional.
(with-eval-after-load 'org
;; Add org-protocol to capture email in Gmail.
(add-to-list 'org-protocol-protocol-alist
'("org-gmail-capture" :protocol "gmail-capture"
:function gb/org-protocol-gmail-capture))
;; Bookmarklet:
;; javascript:location.href = 'org-protocol://gmail-capture?' +
;; new URLSearchParams({
;; msg: document.getElementById("raw_message_text").innerHTML});
;; Must be called from the "Original message" page.
(defun gb/org-protocol-gmail-capture (info)
"Process an org-protocol://gmail-capture style url with INFO.
This function detects the raw message text element from the \"Original
message\" page of a Gmail message. It then parses relevant message
fields and calls the \"email\" org-capture template.
The location for a browser's bookmark looks like this:
javascript:location.href = \\='org-protocol://gmail-capture?\\=' +
new URLSearchParams({
msg: document.getElementById(\"raw_message_text\").innerHTML});
The sub-protocol used to reach this function is set in
`org-protocol-protocol-alist'."
(when-let* ((parts (org-protocol-parse-parameters info))
(msg (plist-get parts ':msg)))
;; FIXME I'm not sure why this is needed, but the protocol fails on a
;; fresh session (before 'org-capture' is loaded), and the new frame
;; just flashes on the screen.
(require 'org-capture)
(let (subj id to from date
from-name from-email to-name to-email from-to-name)
(with-temp-buffer
(insert msg)
;; 'mu4e~view-render-buffer' is responsible in mu4e to view /
;; display a new message, it handles decoding, fontification
;; etc. However, the message we get from org-protocol is not
;; really the "original" it is rather a html rendered version of
;; it. A "Download original" button exists, but we wouldn't be
;; able to retrieve it, since only the browser is logged into
;; the Gmail account, so we have to do with whatever the page
;; displays. That given, prettifying the buffer is of little
;; use for the fields we are interested in.
(goto-char (point-min))
(let ((case-fold-search))
(while (re-search-forward
(rx
line-start
(group
(or
"From:"
"To:"
"Subject:"
"Date:"
"Message-ID:"))
" "
(group (zero-or-more not-newline))
line-end)
nil t)
(pcase (match-string 1)
("From:" (setq from (match-string 2)))
("To:" (setq to (match-string 2)))
("Subject:" (setq subj (match-string 2)))
("Date:" (setq date (match-string 2)))
("Message-ID:" (setq id (match-string 2)))))))
;; Ensure values
(when (or (not from) (string-blank-p from))
(setq from "<none>"))
(when (or (not to) (string-blank-p to))
(setq to "<none>"))
(when (or (not subj) (string-blank-p subj))
(setq subj "No subject"))
(unless date (setq date ""))
(unless id (setq id ""))
;; Clean fields
(setq from (replace-regexp-in-string "<" "<" from t t))
(setq from (replace-regexp-in-string ">" ">" from t t))
(setq to (replace-regexp-in-string "<" "<" to t t))
(setq to (replace-regexp-in-string ">" ">" to t t))
(setq id (replace-regexp-in-string "<" "" id t t))
(setq id (replace-regexp-in-string ">" "" id t t))
(let ((addr (mail-extract-address-components from)))
(if (car addr)
(progn
(setq from-name (car addr))
(setq from (format "%s <%s>" (car addr) (cadr addr))))
(setq from-name (cadr addr))
(setq from (format "<%s>" (cadr addr))))
(setq from-email (cadr addr)))
(let ((addr (mail-extract-address-components to)))
(if (car addr)
(progn
(setq to-name (car addr))
(setq to (format "%s <%s>" (car addr) (cadr addr))))
(setq to-name (cadr addr))
(setq to (format "<%s>" (cadr addr))))
(setq to-email (cadr addr)))
(setq from-to-name
(if (member from-email
'("myemail1@domain.com"
"myemail2@domain.com"
"myemail3@domain.com"
"myemail4@domain.com"))
to-name
from-name))
(let ((props `(:type "gmail"
:date ,date
:from ,from
:fromname ,from-name
:message-id ,id
:subject ,subj
:to ,to
:toname ,to-name
:annotation ,(org-link-make-string
(concat "gmail:" id) subj)
:link ,(org-link-make-string (concat "gmail:" id))
:description ,(format "%s (%s)" subj from-to-name)
:annotation ,(concat "gmail:" id)))
;; Avoid call to `org-store-link', see 'org-protocol-capture'.
(org-capture-link-is-already-stored t))
(apply #'org-link-store-props props)
(raise-frame)
;; Hard-coding the "e" capture template, since this function is very
;; much tailor made for it.
(org-capture nil "e")))
;; Make sure we do not return a string, see 'org-protocol-capture'.
nil))
(org-link-set-parameters "gmail" :follow #'gb/org-link-gmail-open)
(defun gb/org-link-gmail-open (link _)
(kill-new (concat "rfc822msgid:" link))
(message "Message id copied to clipboard.")))
The corresponding bookmarklet is:
javascript:location.href = 'org-protocol://gmail-capture?' + new URLSearchParams({msg: document.getElementById("raw_message_text").innerHTML});
This must be called from the "Original message" page (which you can get with the "Show original" menu item). It won't work elsewhere.
The link created is a `gmail:` type link which essentially just copies `rfc822msgid:<messageID>` to the kill-ring/clipboard, which you can paste in Gmail's search bar to go to the message of interest.
Not that it matters much, but the corresponding capture template is:
("e" "email" entry
(file+headline (lambda ()
(expand-file-name gb/email-capture-file
gb/org-files-directory))
"Email")
"* TODO %?%:description %(org-set-tags \":email:\")
:PROPERTIES:
:Message: %a
:From: %:from
:To: %:to
:Date: %:date
:END:
:LOGBOOK:
- Created on %U
:END:"
:empty-lines 1)
Charnia · 2022-10-13 16:17
I tried to make the switch from `Ivy/counsel` to `Vertico/consult`. At the start it seemed great, felt way snappier and faster than Ivy, and marginalia is prettier than Ivy-rich. Orderless is way better than Ivy's default search as well.
Got a major annoyance when trying to replace `swiper` with `consult-line`, where I couldn't keep searching with `n` or `N` in evil mode, but found a fix here [https://github.com/minad/consult/issues/318#issuecomment-882067919](https://github.com/minad/consult/issues/318#issuecomment-882067919)
Then I tried to add the ivy function to kill a buffer directly from `counsel-switch-buffer` to `consult-switch-buffer` using a single hotkey, but best I got was this:
(defun kill-selected-buffer-in-consult-buffer ()
"Kills the selected buffer in consult-switch-buffer"
(interactive)
(let ((candidate (vertico--candidate)))
(kill-buffer (cdr (plist-get (text-properties-at 0 candidate) 'multi-category)))))
(define-key vertico-map (kbd "C-d") 'kill-selected-buffer-in-consult-buffer)
My Elisp skills are poor and it shows, as the moment I execute in the switch-buffer minibuffer, things start to break.
So I went back to Ivy, and just used orderless as its completion, and marginalia instead of Ivy-rich with great results (except for some small bugs in the `counsel-switch-buffer` minibuffer, which I can ignore).
*EDIT
Managed to make it (kinda) work thanks to a post by /u/oantolin [here](https://www.reddit.com/r/emacs/comments/pac8kp/comment/ha3xx9j/?utm_source=share&utm_medium=web2x&context=3), so I'm gonna leave it for anyone who needs it.
~~~
(defun kill-selected-buffer-in-consult-buffer ()
"Kills the selected buffer in consult-switch-buffer."
(interactive)
(let ((embark-pre-action-hooks `((nil nil))) ;; prevents embark from asking if you're sure
(old-index vertico--index))
(embark--act #'kill-buffer (car (embark--targets)) nil)
(vertico-next old-index) ;; this line doesn't work
(vertico--goto old-index) ;; this line doesn't work either
))
~~~
squirelpower · 2022-10-13 19:22
I would recommend looking at embark, https://github.com/oantolin/embark. It integrates nicely with completions, and provides a lot of context aware actions. I use it in combination with vertico and consult and I occasionally use it to kill a buffer :)
Charnia · 2022-10-13 20:34
I have looked at it, and it's really useful! However what I really wanted was to skip the `embark-act` step and immediately kill the buffer.
After some more research I found a good post by /u/oantolin to directly call embark functions [here](https://www.reddit.com/r/emacs/comments/pac8kp/comment/ha3xx9j/?utm_source=share&utm_medium=web2x&context=3). I managed to make the code work just now, however I noticed I cant seem to reset the index to the original position.
~~~
(defun kill-selected-buffer-in-consult-buffer ()
"Kills the selected buffer in consult-switch-buffer."
(interactive)
(let ((embark-pre-action-hooks `((nil nil))) ;; prevents embark from asking if you're sure
(old-index vertico--index))
(embark--act #'kill-buffer (car (embark--targets)) nil)
(vertico-next old-index) ;; this line doesn't work
(vertico--goto old-index) ;; this line doesn't work either
))
~~~
Anyway it works decently well, I'm gonna leave it at that :)
holgerschurig · 2022-10-13 21:00
I second the embark suggestion. If you are still unsure what it is, and how it can improve Emacs' workflows, I recommend https://karthinks.com/software/fifteen-ways-to-use-embark/ ... I used Embark before he wrote that already. But due to his post I understand some of the things it does much better.
E.g. "changing what you do while you do it", like killing a file while you are in find-file,is just one of the things it brings it onto the tablet.
Passeride · 2022-10-14 13:00
Ive a couple times wanted to select all the words of a particular "type", like in latex, select everything thats just "text", like the text not affected by "font-lock-keyword-face"
Or every word that's a "comment" in programming.
Anyone know of any guidance here? :)
Sudo_Brew · 2022-10-14 16:16
I've been unhappy with the behavior of `comment-line` moving the point down a line, and sometimes inexplicably commenting an inactive region. I'm also unhappy with `comment-dwim`'s single-line behavior of adding a comment at the end of the line. What I want is a function to comment/uncomment the region if its active, and comment/uncomment the line at point otherwise, so I made this quick little function:
(defun my/comment-dwim ()
"Comment region if active, else comment line.
This avoids the excess region commenting of `comment-line' while also avoiding the weird single-line
behavior of `comment-dwim'."
(interactive)
(save-excursion
(if (use-region-p)
(call-interactively #'comment-or-uncomment-region)
(call-interactively #'comment-line))))
eleven_cupfuls · 2022-10-14 16:43
The `text-property-search-{forward,backward}` functions would probably be a starting point. You can look for a specific face or lack of a face, or if the mode applies other properties to text, look for those.
quokka70 · 2022-10-14 17:24
I was using `(setq show-trailing-whitespace t)` but I didn't like the fact it highlighted trailing whitespace in read-only buffers where I don't care about it. I poked around a bit and came up with this.
(defun my/whitespace-mode-for-writable-buffers ()
"Turn on whitespace mode if the buffer is writable and turn it off otherwise."
(whitespace-mode (if buffer-read-only -1 t)))
(add-hook 'text-mode-hook 'my/whitespace-mode-for-writable-buffers)
(add-hook 'prog-mode-hook 'my/whitespace-mode-for-writable-buffers)
(add-hook 'read-only-mode-hook 'my/whitespace-mode-for-writable-buffers)
I use a value of `(face lines-tail trailing)` for `whitespace-style`.
Turbulent_Swing840 · 2022-10-15 03:51
You can use keyboard macros for that. Create a macro, which first presses the embark-act key and then k.
Charnia · 2022-10-16 04:07
Good tip! I managed to simulate it with `(execute-kbd-macro (read-kbd-macro "C-; k"))`
Weirdly enough, I noticed emacs does not evaluate any code after executing an embark command, for example:
(defun embark-test ()
(interactive)
(let ((embark-quit-after-action nil)
(embark-pre-action-hooks `((nil nil))))
(execute-kbd-macro (read-kbd-macro "C-; k"))) ;; calls embark-act, and kills the selected buffer. Works fine.
(message "test") ;; is never executed
(toggle-frame-maximized) ;; Is never executed
(setq testvar 5) ;; is never executed
)
deaddyfreddy · 2022-10-18 17:35
Doesn't `smart-comment` work the way you want?
Sudo_Brew · 2022-10-18 19:17
It's similar! I don't personally care about adding the comment to the end of the line nor marking for deletion, but it does look to do the rest. I would still have a slight preference toward a simple function like mine over pulling in a package, but good callout.
deaddyfreddy · 2022-10-18 21:18
> I would still have a slight preference toward a simple function like mine over pulling in a package
Agreed, but only unless writing a function requires twice as more LoCs and I will have to maintain it myself.
a-c-d-w · 2022-10-19 18:29
I love this idea, but it doesn't work for me... \`whitespace-mode\` doesn't clean up its own fontifications in the buffer when it's disabled. I've tried \`font-lock-flush\` and \`redisplay\`
quokka70 · 2022-10-19 18:36
If I have whitespace-mode on in a buffer and there is some highlighted trailing whitespace, the highlighting goes away when I execute `(whitespace-mode -1)`.
The highlighting also comes and goes for me as expected if I bounce the buffer in and out of read-only-mode.
And it all works in text-mode (where I'm using font-lock) or in a Ruby source buffer (where I'm using tree-sitter-hl-mode).
I wonder what is causing the difference between our behaviors.
a-c-d-w · 2022-10-20 18:00
Ope, when running with \`emacs -q\` the behavior works ... clearly I've got some other something going on in my config. Time to bisect!
EDIT: I lied -- it works just fine! oops