Posts on tag: emacs
Table of contents
Fixing the obscure EMMS user interface
For years I am using a small shell wrapper script and mplayer to play mp3 songs in a loop, because I can’t sit at the computer and not listening to something. However, that script missed a lot of basic command such as “play the previous song” etc. Of yourse there are lots of terminal audio players, but most of them require a new set of key bindings to be learned. And to be honest, all my key binding related memory banks are full, I can’t just memoize yet another set of key bindings.
Then I discovered EMMS to my surprise. So I gave it a try.
And failed miserably.
The user interface of EMMS is not just bad, it is fucking crab. I needed more than an hour just to get it to play a damn song. Then when it finally did, I was unable to get it to stop playing because the buffer with the playlist went missing. A hidden buffer! What the hell were they thinking! Also it was (or rather IS) hard and complicated to create a new playlist and fill it with songs from a directory. Really this is the simplest task an audio player should be able to do. Not EMMS. I haven’t seen anything more fucked up than this.
From the deepest of my heart I really fucking hate EMMS!
On the other hand I truly love emacs. I love it so much that I am already mourning the day I’m dying because then I won’t be able to use emacs anymore.
Using emacs to me means, I am the sole dictator of what happens in my editor. And if I want it to play music and the code that offers such functionality doesn’t work the way I like it, I can and will just change it so that it does.
So, I went and wrote two functions which makes my live with EMMS much
much easier. The first one (and the more complicated one) is
audio-create-playlist
. Here’s the docstring of the function:
Create a new audio playlist for EMMS player.
It asks interactively for NAME. The playlistname will be derived
as this: 'source-file-default-directory + \"playlist-\" + NAME.
If the playlist already exists nothing will be done.
Otherwise the user will be asked wether to add a directory or
file[s] to the playlist. In the first case the user can then
interactively select a directory. In the latter case the user can
add interactively one or more files.
The playlist will be saved when a directory has been selected or
the user declines to add another file.
So when I want to create a new playlist, I just call this function, enter a name, add a directory or a couple of files using the interactive way for this the function provides and be done with it.
When I want to use this playlist the next day, I just call the other
function audio-open-playlist
, which asks me for the playlist
location, opens it in an EMMS buffer and starts playing. And the
buffer will also be properly named so that I can easily find it. Also
it is visible and not hidden somehow.
Inside this buffer I can then just use the usual EMMS keys to navigate the
songs, like p
, n
, <space>
, <ret>
etc. Pretty easy.
If you want to use these functions and also take a look at my EMMS config: init-audio.el can be found on github.
Emacs lisp: which key was pressed?
Sometime you might want to know which key was pressed last in a lisp function. I came up with this little function for this purpose:
(defun last-key () "Return the last key pressed." (car (reverse (append (recent-keys) nil))))
autoscratch - solve the *scratch* buffer problem
I use emacs for more than 20 years now. I love it, I am addicted, I can't even do anything productive without it. However, there was one problem left: the *scratch* buffer. This is a non-file buffer, which always exists always in emacs. By default it has emacs-lisp-mode enabled and you can use it to hack short elisp sexps for testing. In fact I use it for exactly this purpose.
But sometimes I hate it as well! I get a phone call and need to take a note quickly, *scratch* is already open, so I use this. But the mode doesn't really fit. So I switch to text-mode and then enter the notes. I did it this way almost since day one of my emacs usage.
A couple of months ago I came up with a "solution", I just create a *text* buffer on startup with text-mode already enabled in my .emacs. But I still need to switch to it everytime I need it. Still too annoying!
So now, here's my final solution, which tries to fix this mess once and for all: autoscratch.
This major mode is really simple. Basically it consits of an alist with instructions on how to automatically switch the *scratch* buffer mode. Enter an "(" and it switches to emacs-lisp-mode, enter "*" and it switches to org-mode, enter some letter a-z, and it switches to text-mode. You get the idea.
It also solves another problem I (and many other users according to google) have: once I set the *scratch* buffer mode to, say, *text-mode* with some text in there, I don't have an elisp *scratch* buffer left anymore. I'd need another one, but how to create a second *scratch* buffer? Obviously you'll need to rename the current text-mode buffer first and then create a new empty buffer. The emacs wiki contains lots of suggestions for this kind of solution.
No more of this! Autoscratch can just "fork" the buffer, if enabled (set autoscratch-fork-after-trigger to t which is the default). Here's how it works: type a "(" into the empty autoscratch-enabled *scratch* buffer. Autoscratch renames this buffer to *emacs-lisp-scratch*, enables emacs-lisp-mode and creates a new *scratch* buffer in the background. So, if you need some *scratch* space, it'll be there for you waiting bravely for input.
Here's the default trigger list telling autoscratch how to switch modes:
'(("[(;]" . (emacs-lisp-mode)) ("#" . (autoscratch-select '(("perl" . (cperl-mode)) ("ruby" . (ruby-mode)) ("python" . (python-mode)) ("conf" . (conf-unix-mode)) ("shell" . (shell-script-mode))))) ("[-a-zA-Z0-9]" . (text-mode)) ("/" . (c-mode)) ("*" . (progn (insert " ") (org-mode))) ("." . (fundamental-mode)))
Now if you configure autoscratch like this:
(require 'autoscratch-mode) (setq initial-major-mode 'autoscratch-mode) (setq initial-scratch-message "") (setq inhibit-startup-screen t)
then emacs will start with an empty *scratch* buffer as always, but with autoscratch mode enabled. Type in a "(" and emacs-lisp-mode will be started, the *scratch* buffer will be renamed and a new *scratch* created in the background. Or, type in a "#" and you'll be asked what to switch (the autoscratch-select function does this). You can configure almost anything here.
Oh, and just in case you need to manually create a new scratch buffer, just execute "M-x autoscratch-buffer".
Update 2018-08-02:
There was an issue with autoscratch in combination with magit, the original scratch buffer has the same problem as well. The buffer did not alter the default-directory variable. It is buffer-local and inherits the contents of the same global variable, if set to something. So, if you have some file open in a buffer and start a new autoscratch buffer from there, default-directory of this buffer will then be set to the directory of the file visited in the buffer which was last active. Now, if you close all files, except the scratch buffer, and start magit-status AND if the directory is inside of a git repo, then magit will open this repo instead of asking you which repo to open.This is not the behavior I expect from a scratch buffer, so I modified autoscratch-mode to reset the default-directory (if enabled) to $HOME.
Update 2017-07-17:
You can complete this scratch buffer setup with the great persistent-scratch.el mode. Here’s my config for it:(require ‘persistent-scratch) (setq persistent-scratch-save-file (expand-file-name “scratches.el” user-init-dir)) (persistent-scratch-setup-default)(defun tvd-autoscratch-p () “Return non-nil if the current buffer is a scratch buffer” (string-match “scratch*" (buffer-name)))
(setq persistent-scratch-scratch-buffer-p-function ‘tvd-autoscratch-p)
With this setup, scratch buffers will be saved on exit and restored on startup, so you never loose any cool snippet or note you have in a scratch buffer.
Render current HTML buffer with EWW
From time to time I end up with raw HTML content in an emacs buffer and want to know how it looks or just want to read it properly. Until now either I opened firefox to read that file or I opened it with eww-open-file. The problem is, that sometimes there is no file, the HTML buffer is just the output of some generator or something. In such cases I needed to save the buffer as a file and then re-open it with EWW. Very annoying.
With this simple function the nightmare is over. I just use the EWW internal render function to do the job. That way I can view any HTML in emacs without even saving it to disk.
(require 'eww)(defun eww-render-current-buffer () “Render HTML in the current buffer with EWW” (interactive) (beginning-of-buffer) (eww-display-html ‘utf8 (buffer-name)))
(global-set-key (kbd "<C-c C-e C-w C-w>") ’eww-render-current-buffer)
Now when I'm inside a HTML buffer, I can just render it and take a look with EWW. Very handy!
Emacs: Mark, Copy and Yank Things
In a previous post I described how I copy various things at point in emacs to make my daily live easier. Meanwhile the code grew larger and larger and in the end went too large to maintain it in my emacs config.
So, I descided to put it into its own module: mark-copy-yank-things-mode. As always you can find the mode on github.
There are a couple of new features now: I created a copy-comment-block function which is able to copy comment blocks even if they are on the right side of some code. Here's a demo gif:
Also, there is now support for email addresses, ip addresses and urls. The mode provides prefix keys mappings, so, it's very easy to change the key mapping for the various functions just by modifying the prefix key.
Here's a lis of the current default key bindings provided by the mode:
COPY commands (keymap: mcyt-copy-map):
C-c c w mcyt-copy-word C-c c q mcyt-copy-quote C-c c k mcyt-copy-parens C-c c l mcyt-copy-line C-c c p mcyt-copy-paragraph C-c c f mcyt-copy-defun C-c c u mcyt-copy-url C-c c e mcyt-copy-email C-c c c mcyt-copy-comment-block C-c c a mcyt-copy-buffer C-c c i mcyt-copy-ip C-c c s mcyt-copy-sexp
COPY & YANK commands (keymap: mcyt-yank-map):
C-c c y y mcyt-copy-and-yank-line C-c c y l mcyt-copy-and-yank-line C-c c y p mcyt-copy-and-yank-paragraph C-c c y f mcyt-copy-and-yank-defun C-c c y a mcyt-copy-and-yank-buffer C-c c y w mcyt-copy-and-yank-word C-c c y i mcyt-copy-and-yank-ip C-c c y c mcyt-copy-and-yank-comment
MARK commands (keymap: mcyt-mark-map):
C-c c a a mcyt-mark-buffer C-c c a w mcyt-mark-word C-c c a f mcyt-mark-defun C-c c a p mcyt-mark-paragraph C-c c a l mcyt-mark-line C-c c a u mcyt-mark-url C-c c a e mcyt-mark-email C-c c a s mcyt-mark-sexp C-c c a c mcyt-mark-comment-block C-c c a i mcyt-mark-ip
Update 2017-03-28:
From time to time I stumble upon a very annoying emacs behavior: I copy some text and want to insert it into multiple positions somewhere else. However, I need to delete something on those positions before yanking. Now, this deleted stuff will be added to the kill-ring and appears first, when I executeyank
the next time. So, I need to cycle through the kill-ring with M-y
to reach the text I really want to yank. And everytime this text moves further away. So, this is what I type in those situations:
C-<backspace> C-y [..] C-<backspace> C-y M-y [..] C-<backspace> C-y M-y M-y [..] C-<backspace> C-y M-y M-y M-y [..] C-<backspace> C-y M-y M-y M-y M-y [..]
You see, this is very annoying. Here's how I fixed this: I just added a defadvice
to my copying code of the above module, which puts the copied text into a emacs text register. And I created a new key binding to access this text register: C-v
. I do not use this in emacs for cursor movement (its original binding) anyway and its the default PASTE key binding on some OS'es and applications - so I'm already somewhat used to it. Now I can always yank the last copied text and it doesn't matter how many items I added to the kill-ring since copying.
Here's the code:
(advice-add 'mcyt--copy-thing :after '(lambda (&rest args) (with-temp-buffer (yank) (copy-to-register 'c (point-min) (point-max)))))(defun my-insert-c-register () (interactive) (insert-register ‘c))
(global-set-key (kbd “C-v”) ‘my-insert-c-register)