Irreal: A Little Red Meat for Emacsers

I don’t have anything interesting to say today so here’s a little red meat for Irreal’s Emacs lovers:

-1:-- A Little Red Meat for Emacsers (Post jcs)--L0--C0--August 17, 2017 04:08 PM

Irreal: A Personal Emacs Package Repo

As longtime Irreal readers know, I don’t worry about my init.el very much. Rather than organize my configuration into separate, function-specific files like my more organized colleagues, I just throw everything into a single init.el. I keep thinking I should at least turn it into an Org file but until then, I’ve settled for converting my package configuration to the excellent use-package.

Dave Pearson is the anti-me. Not only has he broken out his configuration into separate files, he also took all those personal functions that most of us accumulate, packaged them up, and installed them in his own personal package repository. He hosts the repository on GitHub so it’s easy for him to sync his configuration to different machines.

If that sort of thing appeals to you, Pearson has written a how-to on building your own personal repository. It’s a straightforward procedure. The hardest part is probably building the packages that hold your functions. The nice thing is that once you’ve set up the archive on GitHub (or your favorite such place) you can start converting your functions to packages and uploading them at your leisure. Again, if this sounds like something you’d like to do, take a look at Pearson’s how-to for the details.

-1:-- A Personal Emacs Package Repo (Post jcs)--L0--C0--August 16, 2017 04:58 PM

Pragmatic Emacs: Set up a shortcut to insert a symbol

Despite living in the UK I am hard-wired from some years spent in the US to use a US keyboard layout. One problem for me is that these keyboards do not have a £ symbol on them. On a Mac, I can insert a £ using OPTION-3 but not in Emacs since I have OPTION set to META. This is easily addressed with a bit of code

(define-key global-map (kbd "C-c M-3") (lambda () (interactive) (insert "£")))

Now C-c M-3 will insert the £ symbol. N.B. I could have just bound this to M-3 to match the behaviour elsewhere on my Mac, but I already use that for something else!

-1:-- Set up a shortcut to insert a symbol (Post Ben Maughan)--L0--C0--August 16, 2017 08:21 AM

Marcin Borkowski: The highlight.el library

Last year, I wrote about a few ways of highlighting things in an Emacs buffer. A few days ago I had a somewhat similar need. This time, however, I didn’t have a regex to match the things I wanted highlighted: I just needed to mark something (as in “region”), and have it permanently highlighted (“permanently” meaning that the highlighting should survive editing commands, but not actually closing the buffer). Emacs being Emacs, it turned out to be possible (and easy).
-1:-- The highlight.el library (Post)--L0--C0--August 14, 2017 07:03 PM

sachachua: 2017-08-14 Emacs news

Links from reddit.com/r/emacs, /r/orgmode, /r/spacemacs, Hacker News, planet.emacsen.org, YouTube, the changes to the Emacs NEWS file, and emacs-devel.

Past Emacs News round-ups

-1:-- 2017-08-14 Emacs news (Post Sacha Chua)--L0--C0--August 14, 2017 06:33 AM

emacshorrors: make-temp-name

Update: Reddit points out that this has been fixed on master by replacing most of the code with a call to gnulib’s gen_tempname.

For someone not terribly experienced in writing safe programs, one can only hope that building blocks like make-temp-file are doing the right thing and cannot be subverted by a malicious third party. The general advice here is that it’s preferable to use the primitive for creating the temporary file instead of the primitive to generate its name. Now, does Emacs reuse mkstemp(3) for this? Or at least tmpnam(3)? Of course not! Where we go, we can just invent our own source of randomness:

make-temp-file looks as follows:

static const char make_temp_name_tbl[64] =
{
  'A','B','C','D','E','F','G','H',
  'I','J','K','L','M','N','O','P',
  'Q','R','S','T','U','V','W','X',
  'Y','Z','a','b','c','d','e','f',
  'g','h','i','j','k','l','m','n',
  'o','p','q','r','s','t','u','v',
  'w','x','y','z','0','1','2','3',
  '4','5','6','7','8','9','-','_'
};

static unsigned make_temp_name_count, make_temp_name_count_initialized_p;

/* Value is a temporary file name starting with PREFIX, a string.

   The Emacs process number forms part of the result, so there is
   no danger of generating a name being used by another process.
   In addition, this function makes an attempt to choose a name
   which has no existing file.  To make this work, PREFIX should be
   an absolute file name.

   BASE64_P means add the pid as 3 characters in base64
   encoding.  In this case, 6 characters will be added to PREFIX to
   form the file name.  Otherwise, if Emacs is running on a system
   with long file names, add the pid as a decimal number.

   This function signals an error if no unique file name could be
   generated.  */

Lisp_Object
make_temp_name (Lisp_Object prefix, bool base64_p)
{
  Lisp_Object val, encoded_prefix;
  ptrdiff_t len;
  printmax_t pid;
  char *p, *data;
  char pidbuf[INT_BUFSIZE_BOUND (printmax_t)];
  int pidlen;

  CHECK_STRING (prefix);

  /* VAL is created by adding 6 characters to PREFIX.  The first
     three are the PID of this process, in base 64, and the second
     three are incremented if the file already exists.  This ensures
     262144 unique file names per PID per PREFIX.  */

  pid = getpid ();

  if (base64_p)
    {
      pidbuf[0] = make_temp_name_tbl[pid & 63], pid >>= 6;
      pidbuf[1] = make_temp_name_tbl[pid & 63], pid >>= 6;
      pidbuf[2] = make_temp_name_tbl[pid & 63], pid >>= 6;
      pidlen = 3;
    }
  else
    {
#ifdef HAVE_LONG_FILE_NAMES
      pidlen = sprintf (pidbuf, "%"pMd, pid);
#else
      pidbuf[0] = make_temp_name_tbl[pid & 63], pid >>= 6;
      pidbuf[1] = make_temp_name_tbl[pid & 63], pid >>= 6;
      pidbuf[2] = make_temp_name_tbl[pid & 63], pid >>= 6;
      pidlen = 3;
#endif
    }

  encoded_prefix = ENCODE_FILE (prefix);
  len = SBYTES (encoded_prefix);
  val = make_uninit_string (len + 3 + pidlen);
  data = SSDATA (val);
  memcpy (data, SSDATA (encoded_prefix), len);
  p = data + len;

  memcpy (p, pidbuf, pidlen);
  p += pidlen;

  /* Here we try to minimize useless stat'ing when this function is
     invoked many times successively with the same PREFIX.  We achieve
     this by initializing count to a random value, and incrementing it
     afterwards.

     We don't want make-temp-name to be called while dumping,
     because then make_temp_name_count_initialized_p would get set
     and then make_temp_name_count would not be set when Emacs starts.  */

  if (!make_temp_name_count_initialized_p)
    {
      make_temp_name_count = time (NULL);
      make_temp_name_count_initialized_p = 1;
    }

  while (1)
    {
      unsigned num = make_temp_name_count;

      p[0] = make_temp_name_tbl[num & 63], num >>= 6;
      p[1] = make_temp_name_tbl[num & 63], num >>= 6;
      p[2] = make_temp_name_tbl[num & 63], num >>= 6;

      /* Poor man's congruential RN generator.  Replace with
         ++make_temp_name_count for debugging.  */
      make_temp_name_count += 25229;
      make_temp_name_count %= 225307;

      if (!check_existing (data))
        {
          /* We want to return only if errno is ENOENT.  */
          if (errno == ENOENT)
            return DECODE_FILE (val);
          else
            /* The error here is dubious, but there is little else we
               can do.  The alternatives are to return nil, which is
               as bad as (and in many cases worse than) throwing the
               error, or to ignore the error, which will likely result
               in looping through 225307 stat's, which is not only
               dog-slow, but also useless since eventually nil would
               have to be returned anyway.  */
            report_file_error ("Cannot create temporary name for prefix",
                               prefix);
          /* not reached */
        }
    }
}

DEFUN ("make-temp-name", Fmake_temp_name, Smake_temp_name, 1, 1, 0,
       doc: /* Generate temporary file name (string) starting with PREFIX (a string).
The Emacs process number forms part of the result, so there is no
danger of generating a name being used by another Emacs process
\(so long as only a single host can access the containing directory...).

This function tries to choose a name that has no existing file.
For this to work, PREFIX should be an absolute file name.

There is a race condition between calling `make-temp-name' and creating the
file, which opens all kinds of security holes.  For that reason, you should
normally use `make-temp-file' instead.  */)
  (Lisp_Object prefix)
{
  return make_temp_name (prefix, 0);
}

The generated file name is therefore a combination of the prefix, the Emacs PID and three characters from the above table. This makes about 200.000 possible temporary files that can be generated with a given prefix in an Emacs session. This range can be traversed in a negligible amount of time to recreate the state of the RNG and accurately predict the next temporary file name.

(defun make-temp-file (prefix &optional dir-flag suffix)
  "Create a temporary file.
The returned file name (created by appending some random characters at the end
of PREFIX, and expanding against `temporary-file-directory' if necessary),
is guaranteed to point to a newly created empty file.
You can then use `write-region' to write new data into the file.

If DIR-FLAG is non-nil, create a new empty directory instead of a file.

If SUFFIX is non-nil, add that at the end of the file name."
  ;; Create temp files with strict access rights.  It's easy to
  ;; loosen them later, whereas it's impossible to close the
  ;; time-window of loose permissions otherwise.
  (with-file-modes ?\700
    (let (file)
      (while (condition-case ()
                 (progn
                   (setq file
                         (make-temp-name
                          (if (zerop (length prefix))
                              (file-name-as-directory
                               temporary-file-directory)
                            (expand-file-name prefix
                                              temporary-file-directory))))
                   (if suffix
                       (setq file (concat file suffix)))
                   (if dir-flag
                       (make-directory file)
                     (write-region "" nil file nil 'silent nil 'excl))
                   nil)
               (file-already-exists t))
        ;; the file was somehow created by someone else between
        ;; `make-temp-name' and `write-region', let's try again.
        nil)
      file)))

It’s interesting that the docstring of this function states that the return value “is guaranteed to point to a newly created empty file.”. If there were to exist a file for every possible combination for a prefix, this function would just fall into an infinite loop and block Emacs for no apparent reason. Both of these issues have been solved in a better way in glibc.

At least the impact of predicting the name is lessened if one uses make-temp-file instead of make-temp-name on its own. An attacker cannot create a symlink pointing to a rogue location with the predicted name as that would trigger a file-already-exists error and make the function use the next random name. All they could do is read out the file afterwards iff they have the same permission as the user Emacs runs with. A symlink attack can only be executed successfully with a careless make-temp-name user, thankfully I’ve not been able to find one worth subverting on GitHub yet.

Thanks to dale on #emacs for bringing this to my attention!

-1:-- make-temp-name (Post Vasilij Schneidermann)--L0--C0--August 13, 2017 06:37 PM

Alex Schroeder: Rcirc Menu

I use Emacs as a glorified IRC client but with Bitlbee, the number of IRC channels I’m in has exploded. So many Discord channels. And some Mastodon channels. And my Twitter accounts. It’s crazy. The Emacs modeline is no longer enough to display IRC activity. I wrote a little Rcirc Menu mode to handle it for me.

This has also revived my interest in submitting packages to ELPA. I still have write access. In order to test the waters, I submitted Rcirc Menu and Rcirc Color to emacs-devel. We’ll see how it goes!

Tags:

-1:-- Rcirc Menu (Post)--L0--C0--August 10, 2017 01:15 PM

sachachua: 2017-08-07 Emacs news

Links from reddit.com/r/emacs, /r/orgmode, /r/spacemacs, Hacker News, planet.emacsen.org, YouTube, the changes to the Emacs NEWS file, and emacs-devel.

Past Emacs News round-ups

-1:-- 2017-08-07 Emacs news (Post Sacha Chua)--L0--C0--August 10, 2017 06:05 AM

Marcin Borkowski: demo-it recording

Some time ago I stumbled across a very nice library by Howard Abrams, called demo-it. It seems that this tool, combined with org-tree-slide, is a perfect way to do presentations on Emacs, Org-mode, or Calc, which I happen to do from time to time.
-1:-- demo-it recording (Post)--L0--C0--August 07, 2017 05:11 PM

Wilfred Hughes: Suggest.el: Synthesising Constants

Suggest.el v0.4 is now out, and it offers some really interesting new ways of making suggestions.

Supplying Constants

Suppose the user gives us the input '(a b c d) and desired output 'a. We would already suggest car, but that only gets the first element of the list. They may have wanted elt or nth, which get elements at a specific position.

We now try adding constants to the user’s inputs, specifically nil, t, -1, 0, 1 and 2. This makes suggest.el much more effective.

Here’s the example we mentioned:

;; Inputs (one per line):
'(a b c d)

;; Desired output:
'a

;; Suggestions:
(car '(a b c d)) 
(elt '(a b c d) 0) ; <- new
(nth 0 '(a b c d)) ; <- new

We can now suggest grouping items in a list pairwise:

;; Inputs (one per line):
'(a b c d e f)

;; Desired output:
'((a b) (c d) (e f))

;; Suggestions:
(-partition 2 '(a b c d e f)) ; <- new

Converting a vector to a list:

;; Inputs (one per line):
(vector 1 2 3)

;; Desired output:
(list 1 2 3)

;; Suggestions:
(string-to-list (vector 1 2 3))
(append (vector 1 2 3) nil) ; <- new
(-rotate 0 (vector 1 2 3))  ; <- new
(-concat (vector 1 2 3) nil)  ; <- new

Truncating lists:

;; Inputs (one per line):
'(a b c d e)

;; Desired output:
'(c d e)

;; Suggestions:
(-drop 2 '(a b c d e)) ; <- new
(-slice '(a b c d e) 2) ; <- new
(cdr (cdr '(a b c d e)))

Choosing good values for constants is difficult, but the current set seems to be a good tradeoff between performance, the likelihood of finding a result, and the number of useful results.

Ranking Suggestions

Now we have more possibilities, ordering our suggestions is more complex. The first prototype didn’t always get the ordering correct:

;; Inputs (one per line):
0

;; Desired output:
1

;; Suggestions:
(+ 0 1) ; <- new
(- 1 0) ; <- new
(1+ 0)

The user is probably looking for the increment function, 1+. (+ 0 1) feels like stating the obvious.

Suggest.el prefers function calls that don’t require extra arguments, giving us a better order:

;; Inputs (one per line):
0

;; Desired output:
1

;; Suggestions:
(1+ 0)
(+ 1 0) ; <- new
(- 1 0) ; <- new

If suggest.el tries a function and it returns, we save it. If it isn’t the value we’re looking for, it’s saved to a list of ‘intermediate’ values. This is important for finding nested function calls.

Some intermediate values are very common, especially when exploring arithmetic. There are many ways you can convert an integer to the same float value, for example:

(sqrt 0) ;=> 0.0
(float 0) ;=> 0.0
(fround 0) ;=> 0.0
(ffloor 0) ;=> 0.0
(fceiling 0) ;=> 0.0
(ftruncate 0) ;=> 0.0

Suggest.el previously considered every single way of generating the same value. v0.4 only tries each unique value 3 times. This allows us to explore more unique possibilities, increasing the likelihood of finding a result before giving up.

Literature Review

Finally, I’ve spent some time researching similar tools, and documented them in the related projects section.

Not only has it been interesting to see other approaches, suggest.el has benefited from the comparisons. The Smalltalk Finder, for example, also explores bitwise operations, so suggest.el does too!

If there are simple code snippets that you think suggest.el should find, please file a bug. I’m routinely surprised by the results it finds, but I’m sure it could be smarter still.

-1:-- Suggest.el: Synthesising Constants (Post Wilfred Hughes (me@wilfred.me.uk))--L0--C0--August 06, 2017 12:00 AM

Manuel Uberti: A better workflow with workspaces

I am not a big fan of workspaces on my desktop environments, maybe because to me Alt+Tab is enough. To be honest, I was using workspaces back in my Ratpoison days, but that is not the case any more.

Nevertheless, my daily workflow in Emacs improved so much with eyebrowse I can’t imagine going back to a configuration without it.

(use-package eyebrowse                  ; Easy workspaces creation and switching
  :ensure t
  :config
  (validate-setq eyebrowse-mode-line-separator " "
                 eyebrowse-new-workspace t)

  (eyebrowse-mode t))

As you can see, I am pretty much leaving eyebrowse with its default settings. I removed the comma that was used as a separator between the workspace numbers and made switching to a new workspace start from the *scratch* buffer.

Now my daily workflow in Emacs is thus organised:

  • Workspace #1: this is dedicated to my GTD project, which simply follows the steps Nicolas Petton describes in Orgmode for GTD.

  • Workspace #2: this is for Clojure and ClojureScript development, so everything from CIDER to project specific Magit buffers and EShells ends up here. In the rare case I need to work on a different project, I create a new workspace.

  • Workspace #3: this is the “I need a break!” place. Elfeed is my preferred choice for a quiet and informative distraction. Otherwise, if I just want to hack on my Emacs configuration I do it here.

Combined with Projectile, eyebrowse is the definitive piece of the puzzle I was missing to have an optimised work environment.

-1:-- A better workflow with workspaces (Post)--L0--C0--August 06, 2017 12:00 AM

(or emacs: Ripgrep

counsel-rg

Lately, due to working with a large code base, I've grown more and more fond of counsel-rg. It's an Elisp wrapper around ripgrep - a relatively new recursive grep tool that aims to be faster than the competition (ag, git grep, pt, ack etc).

Besides being really fast, rg also has some really nice command switches. One such switch is especially useful for Emacs:

-M, --max-columns NUM : Don't print lines longer than this limit in bytes. Longer lines are omitted, and only the number of matches in that line is printed.

The -M switch is useful twofold:

  • Emacs is slow when dealing long lines (by long I mean thousands of chars per line)
  • Emacs is slow at accepting a huge amount of output from a process

For each character you add to your input, counsel-rg starts a new shell command to recalculate the matches with the new input. This means that in order to avoid keyboard lag there's only about 0.1 seconds available for both:

  1. Running the shell command.
  2. Accepting output from the shell command.

So I'm quite happy that rg speeds up both steps. Less time spent on these steps provides for much smoother searching.

counsel-grep-or-swiper

I also work with large log files, one file at a time. For a long time, I've used counsel-grep-or-swiper as my main search command:

(global-set-key (kbd "C-s") 'counsel-grep-or-swiper)

But for a 40Mb log file with really long lines counsel-grep-or-swiper started to lag a bit. I tried counsel-rg, and it was actually faster than grep, although it was searching the whole directory. So I thought, why not use rg instead of grep? The switch is actually really easy and required only a simple user customization:

(setq counsel-grep-base-command
 "rg -i -M 120 --no-heading --line-number --color never '%s' %s")

Outro

If you haven't tried ripgrep so far, I suggest you give it a go. Happy hacking!

And if you're a C hacker and have some free time on your hands, why not look at the long lines and the process output issues in Emacs? I'd be very grateful:)

-1:-- Ripgrep (Post)--L0--C0--August 03, 2017 10:00 PM

Emacs café: beginend.el

Thank you Nicolas for letting me borrow (again) your blog to talk about my work. This time, this will not only be my work, but the one of Matus Goljer too (aka Fuco1). Let me present beginend.

Genesis

Four years ago, I started being really annoyed by the fact that M-< would go to the beginning of the dired buffer instead of the first file as shown in this picture:

img

I then wrote these lines of Emacs-lisp code to fix this.

(defun dired-back-to-top ()
  (interactive)
  (beginning-of-buffer)
  (dired-next-line (if dired-omit-mode 2 4)))
(define-key dired-mode-map
  (vector 'remap 'beginning-of-buffer) 'dired-back-to-top)

At this time, I took the same approach to bind M-< so that point would go to the beginning of an email body instead of before headers:

(defun mu4e-compose-goto-top ()
  (interactive)
  (let ((old-position (point)))
    (message-goto-body)
    (when (equal (point) old-position)
      (beginning-of-buffer))))

(define-key mu4e-compose-mode-map
  (vector 'remap 'beginning-of-buffer) 'mu4e-compose-goto-top)

You can see that this one is a bit smarter. The function moves point to the beginning of a message’s body, but if point is already there, the point is moved to the buffer’s real beginning instead. This makes it possible to press M-< several times to switch between the real buffer beginning and the meaningful one. I called this feature double-tap.

I did the same for M-> and this served me well for the next two years.

Start of the beginend project

Two years ago, I started cleaning my init.el file and decided to extract useful bits of Emacs Lisp code into separate packages. That was the beginning of the beginend project.

I also took the opportunity to improve the code to avoid duplication by introducing a macro:

(defun beginend-message-goto-beginning ()
  "Go to the beginning of an email, after the headers."
  (interactive)
  (beginend--double-tap-begin
   (message-goto-body)))

(defmacro beginend--double-tap-begin (&rest body)
  "Evaluate &BODY and goto real beginning if that did not change point."
  (let ((tempvar (make-symbol "old-position")))
    `(let ((,tempvar (point)))
       ,@body
       (when (equal ,tempvar (point))
         (call-interactively #'beginning-of-buffer)))))

The function beginend-message-goto-beginning is equivalent to the function mu4e-compose-goto-top defined above except it is shorter. The macro beginend--double-tap-begin implements double-tap in a way independent of the kind of buffer being visited. The code handling dired buffers used the same macro.

I released version 1 of the project.

Matus blog post and beginend on steroids

The project did not change for the next two years even though I was using it extensively. One day, I read Matus’ blog post titled:

Enhanced beginning- and end-of-buffer in special mode buffers (dired etc.)

This gave me energy to work on the package again. With Matus, we released version 2 featuring many changes:

  • User visible:
    • Add missing space to the mode lighter
    • Add support for many major modes (magit-status, prog, occur, org-agenda, compilation, notmuch-search, elfeed, prodigy, …)
    • Add a global minor mode
    • Push mark when beginend moves point
    • Make sure beginend is reasonable when buffer is narrowed
    • Update README and include screencasts
    • Make the end position coherent across modes
  • Build process:
    • Add Emacs-25.2 as build target
    • Remove compiler warnings
    • Add automated linting
  • Implementation:
    • Factor out common code into new macro making it easy to support more modes
  • Testing:
    • 84% of the code base is now covered by tests
    • Convert tests to buttercup

Adding support for a new mode is a matter of a few lines of code now. Here is how beginend supports going to the meaningful beginning and end of message buffers now:

(beginend-define-mode message-mode
  (progn
    (message-goto-body))
  (progn
    (when (re-search-backward "^-- $" nil t)
      (beginend--goto-nonwhitespace))))

The first progn specifies how to go to the meaningful beginning (i.e., after message headers) and the second one specifies how to go to the meaningful end (i.e., before the signature). These six lines of code also support double-tap, bind M-< and M->, take care of buffer narrowing and set the mark appropriately.

Here are some screencasts demonstrating the behavior of beginend in some major modes.

Dired mode

The following shows how beginend reacts in dired mode when dired-hide-details or dired-omit is activated.

img

Message mode

This screencast shows how beginend allows ignoring both a message headers and signature.

img

Programming mode

This shows how beginend moves point at start and end of the code block in programming buffers, ignoring comments and blank lines.

img

Conclusion

I hope you enjoy using beginend as much as I enjoyed writing it.

-1:-- beginend.el (Post Damien Cassou)--L0--C0--August 01, 2017 08:38 AM

Manuel Uberti: Every feed in its right place

Writing about Christopher Wellons’ elfeed is the least I can do to describe how thankful I am for this gem, especially since it’s one of the Emacs packages I use the most.

Up until last month, Twitter was useful to keep in touch with the IT and the film criticism communities. However, for a while I had been finding myself in dire need of a vacation from social media. The amount of time lost in pointless time-line scrolling looking for the latest hip comment screamed to be spent otherwise.

I have always been a fan of RSS feeds, because having the news aggregated in one place spares me the time of wandering the Internet. Twitter fought against my feeds, giving the impression it was offering more up-to-date content along with the false promise of an interaction that in fact was rarely happening.

Before elfeed, I tried my luck with different feed readers. Since a carefully tmux-ed terminal window is always part of my daily workflow, I fell in love with Newsbeuter a couple of years ago.

However, as any Emacs fanatic would tell you, why leave the comfort of your favourite text editor for something as mundane as RSS feeds?

elfeed is more than a Newsbeuter replacement, though. Tags, filters and the power of Emacs beneath its lean interface make feeds management easier than ever. It’s also trivially extensible. Look at this simple trick to mark all the feeds read when I am feeling too lazy.

(defun mu-elfeed-mark-all-read ()
  "Mark all feeds as read."
  (interactive)
  (call-interactively 'mark-whole-buffer)
  (elfeed-search-untag-all-unread))

(bind-key "R" #'mu-elfeed-mark-all-read elfeed-search-mode-map)

elfeed sorts feeds by time, with the most recent on top. This had been a bit confusing at first, as I was used to having the feeds grouped by items with Newsbeuter. Nevertheless, after a while this approach felt more natural, maybe because it reminds me of the Twitter time-line.

RSS feeds are an essential tool in this age of perennial distractions. Having them readily available at my fingertips is invaluable.

-1:-- Every feed in its right place (Post)--L0--C0--August 01, 2017 12:00 AM

Timo Geusch: Building Emacs 25.2 on XUbuntu 17.04

I haven’t done much with Ubuntu recently, but had to set up a laptop with XUbuntu 17.04. That came with Emacs 24.5 as the default emacs package, and as skeeto pointed out in the comments, with a separate emacs25 package Read More

The post Building Emacs 25.2 on XUbuntu 17.04 appeared first on The Lone C++ Coder's Blog.

-1:-- Building Emacs 25.2 on XUbuntu 17.04 (Post Timo Geusch)--L0--C0--July 30, 2017 11:03 PM

Wilfred Hughes: Optimising Dash.el

Dash.el is a lovely library, and one of the most popular on MELPA. If we can squeeze every last drop of performance out of it, everyone benefits.

Let’s take a look at the black art of making elisp faster.

Measure First!

Chris Wellons has a great optimisation blog post that discusses the performance overhead of creating lambdas with mapcar.

If we look at --map, it does indeed create anonymous functions:

(defmacro --map (form list)
  "Anaphoric form of `-map'."
  `(mapcar (lambda (it) ,form) ,list))

Creating anonymous functions instantiates a closure, which isn’t free. Let’s write an iterative equivalent:

(defmacro --map-loop (form list)
  (declare (debug (form form)))
  (let ((result-sym (make-symbol "result")))
    `(let (,result-sym)
       (dolist (it ,list)
         (push ,form ,result-sym))
       (nreverse ,result-sym))))
List Length mapcar (seconds) dolist (seconds)
1 0.000010 0.000028
1,000 0.0027 0.0079
100,000 0.74 1.24

(Full benchmark code here.)

Surprisingly, mapcar is consistently faster in this particular benchmark! Other Emacsers have observed dolist outperforming mapcar for short lists.

mapcar is primitive, and primitives tend to be fast. dolist clearly isn’t a speedup in all situations. Let’s try something else.

Matching Primitive Performance

Some dash.el functions are equivalent to primitive functions. For example, -first-item is equivalent to car, -drop is equivalent to nthcdr.

We could write -first-item like this:

(defun -first-item (lst)
  (car lst))

However, this adds the overhead of an extra function call compared with calling car directly. Instead, dash.el does this:

(defalias '-first-item 'car)

Let’s do a small benchmark, to ensure that defalias giving us the peformance we want:

Approach time (seconds)
wrapper function 0.1399
alias 0.0055
use car directly 0.0050

(Full benchmark code here.)

For shame! Our alias still isn’t as fast as using the primitive. Let’s compare the disassembly using M-x disasemble.

(defalias 'car-alias 'car)

(defun use-car-alias (x)
  (car-alias x))
;; byte code for use-car-alias:
;;   args: (x)
;; 0       constant  car-alias
;; 1       varref    x
;; 2       call      1
;; 3       return    

(defun use-car-directly (x)
  (car x))
;; byte code for use-car-directly:
;;   args: (x)
;; 0       varref    x
;; 1       car       
;; 2       return    

Intriguingly, these are not the same. There’s a car bytecode that’s being used with use-car-directly.

With a little help from the Emacs Stack Exchange, we can see that byte-opt.el looks for 'byte-opcode properties on functions. If a function symbol has this property, the byte-compiler will replace the function with custom bytecode.

;; Ensure that calls to `-first-item' are compiled to 
;; a single opcode, just like `car'.
(put '-first-item 'byte-opcode 'byte-car)
(put '-first-item 'byte-compile 'byte-compile-one-arg)

This makes performance of -first-item indistinguishable from car! We do lose the ability to advise -first-item, but that’s not possible with car either.

Leveraging the Byte-Compiler

What about functions that aren’t just aliases? Can the byte-compiler help us here?

It turns out that the byte-compiler can actually calculate values at compile time!

Suppose we define a pure function that drops the first two items of a list:

(defun drop-2 (items)
  (cdr (cdr items)))

(defun use-drop-2 ()
  (message "%S" (drop-2 '(1 2 3 4))))
;; byte code for use-drop-2:
;; args: nil
;; 0       constant  message
;; 1       constant  "%S"
;; 2       constant  drop-2
;; 3       constant  (1 2 3 4)
;; 4       call      1
;; 5       call      2
;; 6       return    

If we annotate our function as pure, the byte-compiler helpfully runs it at compile time:

(defun drop-2-pure (items)
  (declare (pure t))
  (cdr (cdr items)))

(defun use-drop-2-pure ()
  (message "%S" (drop-2-pure '(1 2 3 4))))
;; byte code for use-drop-2-pure:
;;   args: nil
;; 0       constant  message
;; 1       constant  "%S"
;; 2       constant  (3 4)
;; 3       call      2
;; 4       return    

This works because we’re calling drop-2-pure on a literal, and we know the value of literals at compile time.

We can even annotate our functions as having no side effects. In this situation, the byte-compiler removes the call entirely:

;; eval-and-compile to work around Emacs bug #24863.
(eval-and-compile
  (defun drop-2-pure (items)
    (declare (side-effect-free t))
    (cdr (cdr items))))

(defun pointless-call-to-drop-2-pure (x)
  (drop-2-pure x)
  "foo")
;; byte code for pointless-call-to-drop-2-pure:
;;   doc:   ...
;;   args: (arg1)
;; 0       constant  "foo"
;; 1       return    

The byte-compiler helpfully reports a warning here too:

value returned from (drop-2-pure x) is unused

Open Source FTW

The latest version of dash.el includes all these improvements, so you can simply upgrade to take advantage. If you find yourself needing to squeeze every last drop of performance from your elisp, you can follow what we’ve done here:

  • benchmark your code (with benchmark-run or profiler-start)
  • disassemble your functions (with diassemble)
  • ask some friendly Emacsers (e.g. the Emacs Stack Exchange)

Good luck! May your editing experience never be laggy!

-1:-- Optimising Dash.el (Post Wilfred Hughes (me@wilfred.me.uk))--L0--C0--July 29, 2017 12:00 AM

emacspeak: Data-Binding In Emacs Lisp: let-alist When Processing JSON Data

Data-Binding In Emacs Lisp: let-alist When Processing JSON Data

1 Summary

Module json-read consumes JSON data structures and transforms them
into their elisp equivalent, where JSON dictionaries become alists and
JSON arrays become vectors. Accessing that data from lisp would
ordinarily require using lisp accessors such as assoc, car and
cdr. With let-alist, we get data-binding for free — the result
is elisp code that uses dotted-variables to directly access specific
slots in a deeply nested data structure. Thus, processing data
available as JSON via Web APIs is a really good use-case for
let-alist. Long-standing wish — I wish Emacs' JSON parsing were
implemented in native code rather than in elisp.


1.1 A Working Example

I recently implemented myself a NOAA Weather API Client — it pulls
the NOAA Weather Forecast (weekly and hourly) as JSON objects, and
produces an org-mode buffer that renders the data.
Note that though the above is part of a much larger
emacspeak-wizards module, the above function and its dependencies
are themselves mostly independent of Emacspeak, except for the last
two forms in the weather forecast function.
Here is an annotated version of the function that gets NOAA data and
leverages let-alist to process the results:


(defun ems--noaa-get-data (ask)
  "Internal function that gets NOAA data and returns a results buffer."
  (declare (special gweb-my-address))
  (let* ((buffer (get-buffer-create "*NOAA Weather*"))
         (inhibit-read-only  t)
         (date nil)
         (start (point-min))
         (address (when ask (read-from-minibuffer "Address:")))
         (geo  (when ask (gmaps-geocode address))))
    (unless address (setq address gweb-my-address))
    (with-current-buffer buffer
      (erase-buffer)
      (special-mode)
      (orgstruct-mode)
      (setq header-line-format (format "NOAA Weather For %s" address))
      (insert (format "* Weather Forecast For %s\n\n" address))
;;; produce Daily forecast
      (let-alist (g-json-from-url (ems--noaa-url geo))
        (cl-loop
         for p across .properties.periods do
         (let-alist p
           (insert
            (format
             "** Forecast For %s: %s\n\n%s\n\n"
             .name .shortForecast .detailedForecast)))
         (fill-region start (point)))
        (insert
         (format "\nUpdated at %s\n"
                 (ems--noaa-time "%c" .properties.updated))))
      (let-alist ;;; Now produce hourly forecast
          (g-json-from-url (concat (ems--noaa-url geo) "/hourly"))
        (insert
         (format "\n* Hourly Forecast:Updated At %s \n"
                 (ems--noaa-time "%c" .properties.updated)))
        (cl-loop
         for p across .properties.periods do
         (let-alist p
           (unless (and date (string= date (ems--noaa-time "%x" .startTime)))
             (insert (format "** %s\n" (ems--noaa-time "%A %X" .startTime)))
             (setq date (ems--noaa-time "%x" .startTime)))
           (insert
            (format
             "  - %s %s %s:  Wind Speed: %s Wind Direction: %s\n"
             (ems--noaa-time "%R" .startTime)
             .shortForecast
             .temperature .windSpeed .windDirection)))))
      (goto-char (point-min)))
    buffer))


  1. In the above_ /gweb-my-address_ is a Lat/Lng pair as returned by
    gmaps-geocode defined in g-client/gmaps.el. That is used as the
    default location for which we retrieve the forecast.
  2. Parameter ask if non-nil results in the user being prompted
    for the address — that address is then geocoded using
    the Google Maps API.
  3. The weather forecast display will leverage org-mode for
    structured navigation; however we dont want that buffer to be
    editable in general; moreover special-mode gives us nice
    features such as q for quitting that window. So we use
    special-mode as the major mode, and orgstruct-mode as a minor
    mode to get the best of both worlds.
  4. The API call to NOAA results in a JSON data structure where
    result.properties.periods holds an array of forecast
    objects. Using that result in let-alist gives us data binding
    for free! Notice the following:
    1. We can use .properties.periods in the cl-loop as the list
      to iterate over.
    2. Within that loop body, a second let-list enables data
      binding over the forecast object that we are processing in the
      loop body.
    3. Data accesses inside the loop body are again simple given the
      data binding created by the let-alist.

The code for generating the hourly forecast is similar in spirit —
the main take-away here is that let-alist saves a lot of
boiler-plate code that would have been otherwise required to take
apart the nested list structure we got back with our data.

-1:-- Data-Binding In Emacs Lisp: let-alist When Processing JSON Data (Post T. V. Raman (noreply@blogger.com))--L0--C0--July 27, 2017 09:09 PM

emacspeak: Spatial Audio: ALSA Virtual Devices Using LADSPA


Spatial Audio: ALSA Virtual Devices Using LADSPA

1 Overview

I have long wanted to apply HRTF filters to soundscapes on the
Emacspeak Audio Desktop to produce effects that are better
spatialized. I just got this working over the weekend using LADSPA
Plugin ZamHeadX2-ladspa.so from package zam-plugins.


2 Getting ZAM Plugins

git clone https://github.com/zamaudio/zam-plugins.git 

And follow the instructions in the README file.


Next, do

sudo make install

to install the plugins.


Finally, make sure that the install location is on your LADSPA path.


2.1 Adding HRTF Virtual Devices Via ASOUNDRC

After updating Emacspeak from GitHub,
open file servers/linux-outloud/asoundrc
and copy the section marked HRTF to your personal .asoundrc
this defines a number of virtual devices that use the newly installed
LADSPA plugin.
Beware: Back-up your .asoundrc first and make sure you can restore
it even if you lose speech.


3 Spatialized Soundscapes

In a running Emacspeak session, invoke command

soundscape-restart

with an interactive prefix arg and specify one of the available
devices using standard Emacs completion.


For use with Soundscapes, I recommend one of the devices that place
sound directly in front of the listener (azimuth 0) but with a non-0
elevation.


The HRTF devices are named with prefix tts because I would like to
use these with software TTS; but for now the result with TTS is not
as good as it is with Soundscapes.


Notice that command soundscape-restart offers a number of virtual
ALSA devices based on your .asoundrc; see the next section for a
summary.

4 Virtual ALSA Devices For Use As A Soundscape Filter

Here is a list of available LADSPA devices in my setup that can be
used to add additional effects to Soundscapes:


  • crossfeed: Apply a BS2B filter.
  • default: No filters, vanilla audio.
  • tap-reverb: Reverb filter from package tap-plugins.
  • reverb-crossfeed: Reverb filter followed by BS2B.
  • tts-a0-e15: HRTF at (0, 15).
  • tts-a0-e30: HRTF at (0, 30).
  • tts-a0-e60: HRTF at (0, 60).
  • tts-a0-e90: HRTF at (0, 90).
  • tts-a0-em15: HRTF at (0, -15).
  • tts-a0-em30: HRTF at (0, -30).
  • tts-a0-em45: HRTF at (0, -45).
  • tts-a135-e45: HRTF at (135, 45).
  • tts-a135-em45: HRTF at (135, -45).
  • tts-a225-e45: HRTF at (225, 45).
  • tts-a225-em45: HRTF at (225, -45).
  • tts-a45-e45: HRTF at (45, 45).
  • tts-a45-em45: HRTF at (45, -45).
  • tts-am45-e45: HRTF at (-45, 45).
  • tts-am45-em45: HRTF at (-45, -45).

5 Other Uses Of HRTF Devices

You can experiment with these devices using aplay e.g.:

aplay -Dtts_a0_e0 filename.wav

You can also apply the HRTF Ladspa plugin from within MPlayer when
using emacspeak.
To try this, use C-e ; f and pick the Zam effect when prompted.
Invoke that command with an interactive prefix arg — C-u C-e ; f
— to edit the params passed to the Zam filter.


HRTF filters when playing media are mostly useful to position a
radio station in 3d space when playing more than one station
simultaneously.

-1:-- Spatial Audio: ALSA Virtual Devices Using LADSPA (Post T. V. Raman (noreply@blogger.com))--L0--C0--July 25, 2017 01:49 AM

Emacs café: Indium 0.7 is out!

Indium – the JavaScript development environment for Emacs – version 0.7 was released a few days ago!

This release brings a major improvement: support for source maps (see the documentation).

Source maps make it possible to us a source JavaScript file (not built/minified) while a minified file is served. Indium supports source maps both for debugging and setting breakpoints.

Installation and update instructions can be found in the documentation. You can also check out the project on GitHub.

-1:-- Indium 0.7 is out! (Post Nicolas Petton)--L0--C0--July 24, 2017 11:40 AM

(or emacs: hydra.el talk @ london.el

Today I gave a talk on hydra at the London Emacs meet up - london.el. My screen capture is available on youtube, and the slides are here.

Thanks to @dotemacs for organizing the event and inviting me.

-1:-- hydra.el talk @ london.el (Post)--L0--C0--July 23, 2017 10:00 PM

emacshorrors: make-temp-name

For someone not terribly experienced in writing safe programs, one can only hope that building blocks like make-temp-file are doing the right thing and cannot be subverted by a malicious third party. The general advice here is that it’s preferable to use the primitive for creating the temporary file instead of the primitive to generate its name. Now, does Emacs reuse mkstemp(3) for this? Or at least tmpnam(3)? Of course not! Where we go, we can just invent our own source of randomness:

make-temp-file looks as follows:

static const char make_temp_name_tbl[64] =
{
  'A','B','C','D','E','F','G','H',
  'I','J','K','L','M','N','O','P',
  'Q','R','S','T','U','V','W','X',
  'Y','Z','a','b','c','d','e','f',
  'g','h','i','j','k','l','m','n',
  'o','p','q','r','s','t','u','v',
  'w','x','y','z','0','1','2','3',
  '4','5','6','7','8','9','-','_'
};

static unsigned make_temp_name_count, make_temp_name_count_initialized_p;

/* Value is a temporary file name starting with PREFIX, a string.

   The Emacs process number forms part of the result, so there is
   no danger of generating a name being used by another process.
   In addition, this function makes an attempt to choose a name
   which has no existing file.  To make this work, PREFIX should be
   an absolute file name.

   BASE64_P means add the pid as 3 characters in base64
   encoding.  In this case, 6 characters will be added to PREFIX to
   form the file name.  Otherwise, if Emacs is running on a system
   with long file names, add the pid as a decimal number.

   This function signals an error if no unique file name could be
   generated.  */

Lisp_Object
make_temp_name (Lisp_Object prefix, bool base64_p)
{
  Lisp_Object val, encoded_prefix;
  ptrdiff_t len;
  printmax_t pid;
  char *p, *data;
  char pidbuf[INT_BUFSIZE_BOUND (printmax_t)];
  int pidlen;

  CHECK_STRING (prefix);

  /* VAL is created by adding 6 characters to PREFIX.  The first
     three are the PID of this process, in base 64, and the second
     three are incremented if the file already exists.  This ensures
     262144 unique file names per PID per PREFIX.  */

  pid = getpid ();

  if (base64_p)
    {
      pidbuf[0] = make_temp_name_tbl[pid & 63], pid >>= 6;
      pidbuf[1] = make_temp_name_tbl[pid & 63], pid >>= 6;
      pidbuf[2] = make_temp_name_tbl[pid & 63], pid >>= 6;
      pidlen = 3;
    }
  else
    {
#ifdef HAVE_LONG_FILE_NAMES
      pidlen = sprintf (pidbuf, "%"pMd, pid);
#else
      pidbuf[0] = make_temp_name_tbl[pid & 63], pid >>= 6;
      pidbuf[1] = make_temp_name_tbl[pid & 63], pid >>= 6;
      pidbuf[2] = make_temp_name_tbl[pid & 63], pid >>= 6;
      pidlen = 3;
#endif
    }

  encoded_prefix = ENCODE_FILE (prefix);
  len = SBYTES (encoded_prefix);
  val = make_uninit_string (len + 3 + pidlen);
  data = SSDATA (val);
  memcpy (data, SSDATA (encoded_prefix), len);
  p = data + len;

  memcpy (p, pidbuf, pidlen);
  p += pidlen;

  /* Here we try to minimize useless stat'ing when this function is
     invoked many times successively with the same PREFIX.  We achieve
     this by initializing count to a random value, and incrementing it
     afterwards.

     We don't want make-temp-name to be called while dumping,
     because then make_temp_name_count_initialized_p would get set
     and then make_temp_name_count would not be set when Emacs starts.  */

  if (!make_temp_name_count_initialized_p)
    {
      make_temp_name_count = time (NULL);
      make_temp_name_count_initialized_p = 1;
    }

  while (1)
    {
      unsigned num = make_temp_name_count;

      p[0] = make_temp_name_tbl[num & 63], num >>= 6;
      p[1] = make_temp_name_tbl[num & 63], num >>= 6;
      p[2] = make_temp_name_tbl[num & 63], num >>= 6;

      /* Poor man's congruential RN generator.  Replace with
         ++make_temp_name_count for debugging.  */
      make_temp_name_count += 25229;
      make_temp_name_count %= 225307;

      if (!check_existing (data))
        {
          /* We want to return only if errno is ENOENT.  */
          if (errno == ENOENT)
            return DECODE_FILE (val);
          else
            /* The error here is dubious, but there is little else we
               can do.  The alternatives are to return nil, which is
               as bad as (and in many cases worse than) throwing the
               error, or to ignore the error, which will likely result
               in looping through 225307 stat's, which is not only
               dog-slow, but also useless since eventually nil would
               have to be returned anyway.  */
            report_file_error ("Cannot create temporary name for prefix",
                               prefix);
          /* not reached */
        }
    }
}

DEFUN ("make-temp-name", Fmake_temp_name, Smake_temp_name, 1, 1, 0,
       doc: /* Generate temporary file name (string) starting with PREFIX (a string).
The Emacs process number forms part of the result, so there is no
danger of generating a name being used by another Emacs process
\(so long as only a single host can access the containing directory...).

This function tries to choose a name that has no existing file.
For this to work, PREFIX should be an absolute file name.

There is a race condition between calling `make-temp-name' and creating the
file, which opens all kinds of security holes.  For that reason, you should
normally use `make-temp-file' instead.  */)
  (Lisp_Object prefix)
{
  return make_temp_name (prefix, 0);
}

The generated file name is therefore a combination of the prefix, the Emacs PID and three characters from the above table. This makes about 200.000 possible temporary files that can be generated with a given prefix in an Emacs session. This range can be traversed in a negligible amount of time to recreate the state of the RNG and accurately predict the next temporary file name.

(defun make-temp-file (prefix &optional dir-flag suffix)
  "Create a temporary file.
The returned file name (created by appending some random characters at the end
of PREFIX, and expanding against `temporary-file-directory' if necessary),
is guaranteed to point to a newly created empty file.
You can then use `write-region' to write new data into the file.

If DIR-FLAG is non-nil, create a new empty directory instead of a file.

If SUFFIX is non-nil, add that at the end of the file name."
  ;; Create temp files with strict access rights.  It's easy to
  ;; loosen them later, whereas it's impossible to close the
  ;; time-window of loose permissions otherwise.
  (with-file-modes ?\700
    (let (file)
      (while (condition-case ()
                 (progn
                   (setq file
                         (make-temp-name
                          (if (zerop (length prefix))
                              (file-name-as-directory
                               temporary-file-directory)
                            (expand-file-name prefix
                                              temporary-file-directory))))
                   (if suffix
                       (setq file (concat file suffix)))
                   (if dir-flag
                       (make-directory file)
                     (write-region "" nil file nil 'silent nil 'excl))
                   nil)
               (file-already-exists t))
        ;; the file was somehow created by someone else between
        ;; `make-temp-name' and `write-region', let's try again.
        nil)
      file)))

It’s interesting that the docstring of this function states that the return value “is guaranteed to point to a newly created empty file.”. If there were to exist a file for every possible combination for a prefix, this function would just fall into an infinite loop and block Emacs for no apparent reason. Both of these issues have been solved in a better way in glibc.

At least the impact of predicting the name is lessened if one uses make-temp-file instead of make-temp-name on its own. An attacker cannot create a symlink pointing to a rogue location with the predicted name as that would trigger a file-already-exists error and make the function use the next random name. All they could do is read out the file afterwards iff they have the same permission as the user Emacs runs with. A symlink attack can only be executed successfully with a careless make-temp-name user, thankfully I’ve not been able to find one worth subverting on GitHub yet.

Thanks to dale on #emacs for bringing this to my attention!

-1:-- make-temp-name (Post Vasilij Schneidermann)--L0--C0--July 18, 2017 07:14 AM

Alex Schroeder: rcirc omit mode

I’m in some busy fake IRC channels. In real IRC channels, people don’t join and part like crazy. But when you join channels that are actually other networks like Discord, then there is a lot more joining and parting. And you don’t want to enable rcirc-omit-mode for each and every one of them by hand. Luckily, I found this useful snippet by Giorgos Keramidas:

(add-hook 'rcirc-mode-hook
  (lambda ()
    ;; rcirc-omit-mode always *toggles*, so we first 'disable' it
    ;; and then let the function toggle it *and* set things up.
    (setq rcirc-omit-mode nil)
    (rcirc-omit-mode)))

Why doesn’t rcirc-omit-mode take a parameter like all decent minor modes?

Tags:

-1:-- rcirc omit mode (Post)--L0--C0--July 15, 2017 10:20 PM

Modern Emacs: Migrating to Spacemacs Layers

Spacemacs is referred for its evil integration, space-based bindings, and community contributed layers that collect, configure, and integrate groups of packages.

For how much they add to Emacs, motivations for personal layers are largely undocumented.

I introduce layers then discuss benefits, approaches, and gotchas with layer-based configurations.

I've migrated my entire dotspacemacs/user-config into personal layers - now 6 lines vs 1,500.

See https://github.com/ekaschalk/.spacemacs.d for my viewer-friendly configuration .

Introducing Layers

This section is not a replacement for http://spacemacs.org/doc/LAYERS.html.

Layers are directories containing up to 5 files and possibly additional packages.

In load order:

Layers.el

Layer dependencies to load first.


(configuration-layer/declare-layers '(theming))

packages.el

Packages added or configured by the layer.


(setq my-layer-packages
      '(a-pkg
        (github-pkg :location (recipe :fetcher github
                                      :repo "github-user/repo-name"))
        (my-pkg :location local)))
  • Owned Packages: A layer owns a package if it defines layer-name/init-pkg-name. All packages not defined in dotspacemacs/additional/packages should have one and only one owner. It calls use-package. Common options are :init for before load config, :config for after, :if for loading if eg. a certain OS or executable is installed, :after for enforcing load order, and :defer t for deferred loading.

(defun display/init-pretty-outlines ()
  (use-package pretty-outlines
    :after outshine
    :config
    (progn
      (add-hook 'outline-mode-hook 'pretty-outline-set-display-table)
      (add-hook 'outline-minor-mode-hook 'pretty-outline-set-display-table)
      (add-hook 'emacs-lisp-mode-hook 'pretty-outline-add-bullets))))
  • Unowned Packages: A layer that does not own a package can configure it with layer-name/pre-init-pkg-name and layer-name/post-init-pkg-name.

(defun config/pre-init-neotree ()
  (evil-global-set-key 'normal (kbd "M-p")
                       'neotree-find-project-root))

(defun config/post-init-neotree ()
  (setq neo-theme 'icons))
  • Local Packages: Personal packages at local/my-pkg/my-pkg.el.

funcs.el

Layer functions.

Package agnostic functions belong here.


(defmacro with-face (STR &rest PROPS)
  "Return STR propertized with PROPS."
  `(propertize ,STR 'face (list ,@PROPS)))

Guarding against particular packages being installed:


(when (configuration-layer/package-usedp 'some-pkg)
  (defun my-func ()))

config.el

Layer variables.


;; python/config.el
(defvar python-tab-width 4
  "Tab width value for python buffers")

;; init.el in dotspacemacs-configuration-layers
(python :variables python-tab-width 2)

Configuration defined here will be loaded before the package init functions are executed. Layer dependencies are actually loaded prior to config.el.

This can be used for eg. setting theme updates with the theming layer.


(setq theming-modifications
      `((solarized-dark (avy-background-face :foreground "#586e75")
                        (font-lock-doc-face :foreground "#2aa198"))
        (solarized-light ...)))

keybindings.el

Package-agnostic key-bindings.


(global-set-key (kbd "M-d") 'spacemacs/delete-window)

;; Evil will be loaded
(evil-define-key '(normal visual motion) outline-minor-mode-map
  "gh" 'outline-up-heading)

Personal Layers

Structure

While any organization can be used, I recommend at most these 5 layers covering common needs.

A Macros/Base Layer

A base layer that all personal layers inherit packages, macros, and common functions from with (configuration-layer/declare-layers '(base)).

I load dash-functional and define with-dir, with-face, and other useful utilities here.

Config

All packages and their configuration and key-bindings that don't fit into any neat grouping.

When any package's init gets large, consider a local package. I maintain my org-mode setup separately in a local org-config package.

Anything, excluding spacemacs toggles, can be setup here. For instance:


(setq config-packages '(evil ...))

(defun config/post-init-evil ()
  (setq evil-escape-key-sequence "jk")
  (setq evil-escape-unordered-key-sequence "true")
  (advice-add 'evil-ex-search-next :after 'config/scroll-to-center-advice)
  (advice-add 'evil-ex-search-previous :after 'config/scroll-to-center-advice))

I recommend this layer own all additional packages except themes, see gotchas.

Display

Theme updates and display packages like spaceline-all-the-icons.

Due to how Spacemacs loads themes, I highly recommend declaring the theming layer a dependency for theme updates. It is much more efficient should you configure multiple themes, like light and dark versions, and as it is a layer, it will be loaded prior to config.el for proper code isolation.

I integrate and configure my local pretty packages here:

Langs (optional)

I find it useful to separate programming language configuration out from the config layer, though it is not necessary.

Personal (optional)

All personal packages that aren't display related I maintain in a single personal layer. This is only relevant if you write your own packages.

I setup my blogging and outline-jump packages here.

Your init.el

Layers must be declared in your dotspacemacs-configuration-layers to take effect.

I've organized my layers into several sections:


(defvar dotspacemacs/layers/local
  '((macros :location local)
    (config :location local)
    (display :location local)
    (langs :location local)
    (personal :location local))
  "Local layers housed in '~/.spacemacs.d/layers'.")

(defvar dotspacemacs/layers/core
  '(better-defaults
    git
    org
    ...)
  "Layers I consider core to Spacemacs")

(defvar dotspacemacs/layers/langs
  '(emacs-lisp
    ...)
  "Programming and markup language layers")

(defvar dotspacemacs/layers/extra
  '(gnus
    graphviz
    ...)
  "Miscellaneous layers")

(defun dotspacemacs/layers ()
  (setq-default dotspacemacs-configuration-layer-path '("~/.spacemacs.d/layers/")
                dotspacemacs-configuration-layers
                (append dotspacemacs/layers/core
                        dotspacemacs/layers/langs
                        dotspacemacs/layers/extra
                        dotspacemacs/layers/local)
                ...))

Gotchas

Migrating was mostly painless. However when things go wrong you lose access to your setup, an annoying development cycle. I encountered several Spacemacs idiosyncrasies to be aware of when using layers to replace my user-config.

Non-obvious errors to avoid:

Naming

The naming scheme of setq layer-name-packages and defun layer-name/init-pkg-name is strict. Beware when refactoring that you adjust the layer name accordingly. Failure to do so will result in the package's configuration not being loaded or in the case of ownership, not being installed, rather than a direct error.

Spacemacs toggles

Some toggles like spacemacs/toggle-highlight-long-lines-globally-on do not belong in any layer and should be defined in your user-config. Six toggles are now all that compose my dotspacemacs/user-config.

This goes for some toggles not explicitly owned by Spacemacs - trying to setup fringe-mode failed for me even in a config/post-init-fringe block.

OS Configuration

I define is-linuxp and a few other OS utilities that conditionally setup dotspacemacs/init variables like font size. Layers load after these variables are set, so the utilities cannot be moved to a layer. Set them at the top of your init.el.

Additional Themes

Spacemacs layers load ordering causes issues for extra themes. Theme packages cannot be put in a layer. As a result, to use solarized I set:


;; ~/.spacemacs.d/init.el
(defun dotspacemacs/layers ()
  (setq-default dotspacemacs-additional-packages '(solarized-theme)
                ...))
(defun dotspacemacs/init ()
  (setq-default dotspacemacs-themes '(solarized-dark solarized-light)
                ...))

Spacemacs Core Layers

Without doing a deep dive into Spacemacs core, you can expect the following layers to always be loaded before all personal layers. This is how dash is always available and evil-define-key can be used in keybindings files.

Call g d or (spacemacs/jump-to-definition) in emacs lisp mode to jump to that layer's packages.el to check out its packages and configuration.


(configuration-layer/declare-layers
 '(spacemacs-base
   spacemacs-completion
   spacemacs-layouts
   spacemacs-editing
   spacemacs-editing-visual
   spacemacs-evil
   spacemacs-language
   spacemacs-misc
   spacemacs-modeline
   spacemacs-navigation
   spacemacs-org
   spacemacs-purpose
   spacemacs-visual))

These layers follow the same rules and principles as every other layer. If you have the curiosity, these layers make Spacemacs what it is.

Functionality provided here can be made use of by any layer, assuming those packages and layers are not explicitly excluded.

Benefits

Those that value organization and robustness will find Spacemacs layers to improve on other configuration management methods.

Following Spacemacs conventions leads to predictable, friendly configurations.

Once you've become familiar with its conventions, there is no overhead.

-1:-- Migrating to Spacemacs Layers (Post)--L0--C0--July 14, 2017 12:00 AM

Pragmatic Emacs: A workflow to quickly add photos to org-mode notes

I was at a conference this week and a colleague was making notes using Evernote on her laptop and taking photos of key slides on her phone which then appeared in her notes. Of course I was making my notes in org-mode but I was envious of this behaviour so decided to emulate it.

With the function below, I can take a photo on my phone and upload to google drive (I use Photo & Picture Resizer, but you could use anything you like to get the pictures onto your computer). Then with a single command in Emacs, I am prompted with a list of photos in the folder to which they are uploaded, with the most recent first. The selected image is then:

  1. Moved the same directory as my org-mode notes file
  2. Renamed based on the heading of the current section in my notes, with a numeric suffix if there is already a photo with that name
  3. Linked in the notes and then the image is displayed

Here is a demonstration:

insert-slide-image.gif

Here is the code:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; add image from conference phone upload                                 ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; use case is taking a photo of a slide in a conference and uploading
;; it to google drive or dropbox or whatever to get it on your
;; computer. You then want to embed it in an org-mode document by
;; moving it to the same folder and renaming according to the current
;; section of the org file, avoiding name clashes

;; required libraries
(require 'dash)
(require 'swiper)
(require 's)

;; start directory
(defvar bjm/conference-image-dir (expand-file-name "/path/to/image/upload/dir"))

(defun bjm/insert-conference-image ()
  "Insert image from conference directory, rename and add link in current file.

The file is taken from a start directory set by `bjm/conference-image-dir' and moved to the current directory, renamed and embedded at the point as an org-mode link. The user is presented with a list of files in the start directory, from which to select the file to move, sorted by most recent first."
  (interactive)
  (let (file-list target-dir file-list-sorted start-file start-file-full file-ext end-file end-file-base end-file-full file-number)
    ;; clean directories from list but keep times
    (setq file-list
          (-remove (lambda (x) (nth 1 x))
                   (directory-files-and-attributes bjm/conference-image-dir)))

    ;; get target directory
    (setq target-dir (file-name-directory (buffer-file-name)))

    ;; sort list by most recent
  ;; http://stackoverflow.com/questions/26514437/emacs-sort-list-of-directories-files-by-modification-date
  (setq file-list-sorted
        (mapcar #'car
                (sort file-list
                      #'(lambda (x y) (time-less-p (nth 6 y) (nth 6 x))))))

  ;; use ivy to select start-file
  (setq start-file (ivy-read
                    (concat "Move selected file to " target-dir ":")
                    file-list-sorted
                    :re-builder #'ivy--regex
                    :sort nil
                    :initial-input nil))

  ;; add full path to start file and end-file
  (setq start-file-full
        (expand-file-name start-file bjm/conference-image-dir))
  ;; generate target file name from current org section
  ;; (setq file-ext (file-name-extension start-file t))

  ;; my phone app doesn't add an extension to the image so I do it
  ;; here. If you want to keep the existing extension then use the
  ;; line above
  (setq file-ext ".jpg")
  ;; get section heading and clean it up
  (setq end-file-base (s-downcase (s-dashed-words (nth 4 (org-heading-components)))))
  ;; shorten to first 40 chars to avoid long file names
  (setq end-file-base (s-left 40 end-file-base))
  ;; number to append to ensure unique name
  (setq file-number 1)
  (setq end-file (concat
                  end-file-base
                  (format "-%s" file-number)
                  file-ext))

  ;; increment number at end of name if file exists
  (while (file-exists-p end-file)
    ;; increment
    (setq file-number (+ file-number 1))
    (setq end-file (concat
                    end-file-base
                    (format "-%s" file-number)
                    file-ext))
    )

  ;; final file name including path
  (setq end-file-full
        (expand-file-name end-file target-dir))
  ;; rename file
  (rename-file start-file-full end-file-full)
  (message "moved %s to %s" start-file-full end-file)
  ;; insert link
  (insert (org-make-link-string (format "file:%s" end-file)))
  ;; display image
  (org-display-inline-images t t)))
-1:-- A workflow to quickly add photos to org-mode notes (Post Ben Maughan)--L0--C0--July 06, 2017 02:56 PM

Modern Emacs: Fancy Outline Bullets

We improve upon the outline bullets presented in the previous post Managing code with Outlines.

My Headings Editing my config
/img/outline-bullets-fancy.png /img/outline-bullets-config.png

This solution:

  1. Allows for face application to the bullet. The default faces outline-1/2/3...
  2. only apply to the text, not the bullet.
  3. Adds spaces for higher level bullets. So not every outline-level needs to
  4. have its text start at the same column.
  5. Works for any number of outline levels.
  6. Generalized - uses outline regexes, all that is required to add the bullets
  7. to a mode is adding to its hook.

(require 'dash)
(require 'outshine)
(require 's)

(defmacro with-face (STR &rest PROPS)
  "Return STR propertized with PROPS."
  `(propertize ,STR 'face (list ,@PROPS)))

(defun set-icon-fonts (CODE-FONT-ALIST)
  "Utility to associate many unicode points with specified fonts."
  (--each CODE-FONT-ALIST
    (-let (((font . codes) it))
      (--each codes
        (set-fontset-font t `(,it . ,it) font)))))

;; Requires all-the-icons fonts installed
(set-icon-fonts
 '(("material icons" #xe3d0 #xe3d1 #xe3d2 #xe3d4)))

(setq outline-bullets-bullet-list '("" "" "" ""))

(defun font-lock-display-updates (FONT-LOCK-ALIST)
  "Put text property for FONT-LOCK-ALIST for var-width replacements."
  (font-lock-add-keywords
   nil (--map (-let (((rgx uni-point) it))
                `(,rgx (0 (progn
                            (put-text-property
                             (match-beginning 1) (match-end 1)
                             'display
                             ,uni-point)
                            nil))))
              FONT-LOCK-ALIST)))

(defun outline-bullets-rgx-at-level (LEVEL)
  "Calculate regex or outline-bullets at LEVEL."
  (concat "\\(^"
          (-> LEVEL
              outshine-calc-outline-string-at-level
              s-trim-right)
          "\\) "))

(defun propertize-bullet (LEVEL BULLET)
  "Add LEVEL-dependent face to BULLET."
  (with-face BULLET
             (pcase LEVEL
               (0 '(:inherit outline-1 :underline nil))
               (1 '(:inherit outline-2 :underline nil))
               (2 '(:inherit outline-3 :underline nil))
               (3 '(:inherit outline-4 :underline nil))
               (_ nil))))

(defun add-outline-font-locks ()
  "Use with `add-hook' to enable outline-bullets-bullet-list for mode."
  (font-lock-display-updates
   (--map-indexed
    (list
     (outline-bullets-rgx-at-level (+ 1 it-index))
     (concat
      (s-repeat it-index " ")
      (propertize-bullet it-index it)))
    (-take 8 (-cycle outline-bullets-bullet-list)))))

(add-hook 'emacs-lisp-mode-hook 'add-outline-font-locks)
(add-hook 'hy-mode-hook 'add-outline-font-locks)
(add-hook 'python-mode-hook 'add-outline-font-locks)
-1:-- Fancy Outline Bullets (Post)--L0--C0--July 03, 2017 12:00 AM

Raimon Grau: Announcing commit-msg-prefix.

When writing commit messages, it's usual that your company/organisation has some policies and rules about the format and contents in the messages.

Some use an issue number, or name, or start with keywords (or emojis) like Add:/Clean:/Remove:.. (that can be converted to emojis in emacs too). But it's clear that somehow there's value in some kind of standarization.

So I created commit-msg-prefix, which lists the previous git commit messages and lets you pick one of them, and it will insert the relevant part of the commit in your current buffer.

My use case is when I do not remember the issue name/number I'm working on, but I remember keywords of previous commits that belong to the same issue.

There are a few variables to configure, like the exact git (or other) command to fetch logs, and the regex to apply to the log to extract the relevant part to insert.

The variable "commit-msg-prefix-input-method" is one of the symbols ('completing-read 'ido-completing-read 'commit-msg-prefix-helm-read 'ivy-read).

it defaults to ido-completing-read, but the idea is that you use your favourite input method.  ivy or helm, I guess :)




-1:-- Announcing commit-msg-prefix. (Post Raimon Grau (noreply@blogger.com))--L0--C0--July 02, 2017 01:45 PM

Flickr tag 'emacs': Groupie picture with @bbatsov of Clojure and Emacs fame at EuroClojure 2016

insoporzen posted a photo:

Groupie picture with @bbatsov of Clojure and Emacs fame at EuroClojure 2016

Better late than never to post my most important moment during the last EuroClojure and send a big thanks to @bbatsov for all his great work in the Clojure community.
@bbatsov: I worked as a Ruby dev for years, too. I hope you’ll be able to make the switch full time at some point, as well(;



zen-temple.net/2017/06/25/groupie-picture-bbatsov-clojure...

-1:-- Groupie picture with @bbatsov of Clojure and Emacs fame at EuroClojure 2016 (Post insoporzen (nobody@flickr.com))--L0--C0--June 25, 2017 01:00 PM

Matthias Pfeifer: Picking elements from the kill-ring with completing-read

The kill ring is a nice thing to have. Only the navigation is a bit to uncomfortable (I am refering to doing C-y again and again until the to desired element is found). So this is what I came up with to feel more ‚comfy‘: browse-kill-ring.elraw download(defun pre-process-kill-ring-element (element) (replace-regexp-in-string "^[[:space:]]+" "" (replace-regexp-in-string "[[:space:]]+$" "" […]
-1:-- Picking elements from the kill-ring with completing-read (Post Matthias)--L0--C0--June 21, 2017 01:52 PM

Raimon Grau: TIL: evil-buffer-regexps

So I have my evil-mode perfectly adapted to my workflow, activating or deactivating it by default depending on the major-mode, so that I don't get into normal mode on magit buffers, or sly-*, or eww.

BUT, there are some buffers which you cannot easily control, because they reuse some major mode where I usually want evil-normal, but not in those cases.

In my case, it's *sly-macroexpansion* and *sly-description* buffers. They are both "lisp-mode" buffers (for syntax highlighting purposes and such), but I want 'q' to quit them.   As they do not represent any file, I was out of luck binding modes to file extensions. (btw, here's some recent article talking bout the ways emacs chooses your major mode)

So trying to hack something using emacs hooks, I found that, in fact, evil has us covered, and provides a evil-buffer-regexps, that allows us to predefine modes on given buffer names.


(add-to-list 'evil-buffer-regexps
'("\\*sly-macroexpansion\\*" . emacs))

-1:-- TIL: evil-buffer-regexps (Post Raimon Grau (noreply@blogger.com))--L0--C0--June 19, 2017 03:21 PM

Martin R. Albrecht: Handling Email with Emacs

Like many other people, I write, receive and loath a lot of email. Writing it goes something like this:

  1. Create a new draft,
  2. figure out the right address to put into the To: field,
  3. write “Hi <first name>”,
  4. write the actual message,
  5. attach the correct file (if any),
  6. append “Cheers, Martin”.

Also, a lot of email is repetitive and boring but necessary, such as asking seminar speakers for their titles and abstracts, giving people advise on how to claim reimbursement when they visit Royal Holloway, responding to requests of people who’d like to pursue a PhD.

Here is my attempt to semi-automate some of the boring steps in Emacs.

Plumbing

I use mbsync for syncing my e-mail to my local hard disk as I often work offline, e.g. during my commute or while working on airplanes.1 Mbsync does not speak IMAP IDLE, aka push notifications, so I use imapnotify for this; here’s my (sanitised) imapnotify config file:

var child_process = require('child_process');

function getStdout(cmd) {
  var stdout = child_process.execSync(cmd);
  return stdout.toString().trim();
}

exports.host = "imap.gmail.com";
exports.port = 993;
exports.tls = true;
exports.username = "martinralbrecht@gmail.com";
exports.password = // whatever needs doing, e.g. call getStdout()
exports.onNewMail = "mbsync googlemail-minimal";
exports.onNewMailPost = "emacsclient  -e '(mu4e-update-index)'";
exports.boxes = [ "INBOX"];

I only need imapnotify in Emacs, so I use prodigy to start/stop it.

(use-package prodigy
  :ensure t
  :init (prodigy-define-tag
          :name 'email
          :ready-message "Checking Email using IMAP IDLE. Ctrl-C to shutdown.")
  (prodigy-define-service
    :name "imapnotify"
    :command "imapnotify"
    :args (list "-c" (expand-file-name ".config/imapnotify.gmail.js" (getenv "HOME")))
    :tags '(email)
    :kill-signal 'sigkill))

Once arrived, email is parsed by Mu which provides fast, powerful fulltext search. Finally, Mu4e provides that email client experience™ in Emacs.

On the other end, I’m not relying on Emacs’ built-in support for sending email but use opensmtpd.2 Using Emacs’ built-in functionality means that it will hang while sending email (due to lack of multithreading), especially on slow connections, which defeats the purpose of getting email out of the way quickly.

Reading

Mu shines at search, it’s fast and expressive. For example, to search for messages between 2 kilobytes and 2Mb, written in December 2009 with an attachment from Bill, search

size:2k..2m date:20091201..20093112 flag:attach from:bill

Below are some examples for how I use it. Other ideas can be to filter for your project students, by your course number etc.

(add-to-list
 'mu4e-bookmarks
 '("flag:unread NOT flag:trashed AND (flag:list OR from:trac@sagemath.org)"
   "Unread bulk messages" ?l))

(add-to-list
 'mu4e-bookmarks
 '("flag:unread NOT flag:trashed AND NOT flag:list AND (maildir:\"/royal holloway\" OR maildir:/INBOX)"
   "Unread messages addressed to me" ?i))

(add-to-list
 'mu4e-bookmarks
 '("mime:application/* AND NOT mime:application/pgp* AND (maildir:\"/royal holloway\" OR maildir:/INBOX)"
   "Messages with attachments for me." ?d) t)

(add-to-list
 'mu4e-bookmarks
 '("flag:flagged"
   "Flagged messages" ?f) t)

(add-to-list
 'mu4e-bookmarks
 '("(maildir:\"/[Google Mail]/.Sent Mail\" OR maildir:\"/royal holloway/.sent\") AND date:7d..now"
   "Sent in last 7 days" ?s) t)

By default Mu’s search is REPL, i.e. you type a query, press <enter> and look at the results. Sometimes you want real-time updates as you type, e.g. to adapt your search quickly. In this case, helm-mu has you covered. Helm adds a generic search-as-you-type interface to Emacs, here’s a nice intro.

(use-package helm-mu
  :ensure t
  :config (progn
            (bind-key "S" #'helm-mu mu4e-main-mode-map)))

By the way, enabling helm-follow-mode via C-c C-f allows to preview emails as you search.

Sometimes, you might want to file an email with some project notes to be able to find it later without any effort or you might want to refer to it directly from your TODO list. I use Org-Mode for my TODOs and notes. Mu4e comes with Org-Mode support which provides links for messages and search queries. First, enable it

(use-package org-mu4e
  :config (setq org-mu4e-link-query-in-headers-mode nil))

and then add some org-capture templates to make filing and email or creating a TODO based on an email easy:

(use-package org-capture
  :bind ("<f9>" . org-capture)
  :config (setq org-capture-templates
                '(("r" "respond ro email (mu4e)"
                   entry (file+headline malb/inbox-org "Email")
                   "* REPLY to [[mailto:%:fromaddress][%:fromname]] on %a\nDEADLINE: %(org-insert-time-stamp (org-read-date nil t \"+1d\"))\n%U\n\n"
                   :immediate-finish t
                   :prepend t)

                  ("f" "file email (mu4e)"
                   entry (file+headline malb/inbox-org "Email")
                   "* %a by [[mailto:%:fromaddress][%:fromname]]\n%U\n\n%i%?\n"
                   :immediate-finish nil
                   :prepend nil))))

Writing

First, let’s make finding that email address easier. For this, I want an automatically maintained database holding at least

  1. first name,
  2. last name and
  3. email address

which is then used for autocompletion as I type. “Automatically maintained“ here means that this database should be built from our email correspondence, similar to e.g. what Gmail does. Adding email addresses and whatever else is in the From: field to some database isn’t difficult per se and many clients do it. For example, Mu4e comes with this built-in.

However, there are a few different conventions out there for how people write names in a From: field, so this needs a bit of tidying up. For example, Royal Holloway likes “Lastname, Firstname (Year)” for students; some people like to YELL their LASTNAME and then write the first name; some people misspell their own name. The code below canonicalises this.

(defun malb/canonicalise-contact-name (name)
  (let ((case-fold-search nil))
    (setq name (or name ""))
    (if (string-match-p "^[^ ]+@[^ ]+\.[^ ]" name)
        ""
      (progn
        ;; drop email address
        (setq name (replace-regexp-in-string "^\\(.*\\) [^ ]+@[^ ]+\.[^ ]" "\\1" name)) 
        ;; strip quotes
        (setq name (replace-regexp-in-string "^\"\\(.*\\)\"" "\\1" name)) 
        ;; deal with YELL’d last names
        (setq name (replace-regexp-in-string "^\\(\\<[[:upper:]]+\\>\\) \\(.*\\)" "\\2 \\1" name))
        ;; Foo, Bar becomes Bar Foo
        (setq name (replace-regexp-in-string "^\\(.*\\), \\([^ ]+\\).*" "\\2 \\1" name)) 
        ;; look up names and replace from static table, TODO look this up by email
        (setq name (or (cdr (assoc name malb/mu4e-name-replacements)) name)) 
        ))))

(defun malb/mu4e-contact-rewrite-function (contact)
  (let* ((name (or (plist-get contact :name) ""))
         (mail (plist-get contact :mail))
         (case-fold-search nil))
    (plist-put contact :name (malb/canonicalise-contact-name name))
    contact))

(setq mu4e-contact-rewrite-function #'malb/mu4e-contact-rewrite-function)

Now that our addresses are canonicalised, I can use those to fill in a few more bits. Given an email starting with “To: John Doe <john@example.com>” there is no point in typing the name “John” again when I do the customary “Hi …,”. Here, YASnippet comes in. YASnippet is a templating system for Emacs inspired by TextMate, which allows to map short sequences of characters to other sequences of characters, potentially by asking for more user input and/or calling some arbitrary Emacs Lisp function. For example, here’s the template we use to advertise the ISG seminar

# -*- mode: snippet -*-
# name: Announce ISG Research Seminar
# key: isg-announce
# --
${1:Thu}, $2 @ ${3:11:00} in ${4:HLT2}: $5
---

When:   $1, $2, 2016 @ $3
Where:  $4
Why:    Because… reasons!
Who:    $5 ($6)

# Title #

$0

# Abstract #



# Bio #



Cheers,
Lorenzo & Martin

and here’s my “hi” template

# -*- mode: snippet -*-
# name: Say "hi"
# key: Hi
# --
Hi ${1:`(malb/yas-get-names-from-to-fields)`},

$0

Cheers,
Martin

Using this snippet, typing Hi<Tab> triggers email boilerplate to be inserted, with the cursor eventually placed in the position of $0. The name used in the greeting is computed using the following function:

(defun malb/yas-get-names-from-fields (fields)
  (let (names
        ret
        name
        point-end-of-line
        (search-regexp (mapconcat (lambda (arg)
                                    (concat "^" arg ": "))
                                  fields "\\|"))
        (case-fold-search nil))
    (save-excursion
      (goto-char (point-min))
      (while (re-search-forward search-regexp nil t)
        (save-excursion
          (setq point-end-of-line (re-search-forward "$")))
        (setq name (buffer-substring-no-properties (point) point-end-of-line))
        (setq name (split-string name "[^ ]+@[^ ]+," t " ")) ;; split on email@address,
        (setq names (append names name)))
      (dolist (name names)
        (setq name (malb/canonicalise-contact-name name))
        (if (string-match "\\([^ ,]+\\)" name)
            (progn
              (setq name (match-string 1 name))
              (setq name (capitalize name))
              (if ret
                  (setq ret (concat ret ", " name))
                (setq ret name)))))
      (if ret ret "there"))))

(defun malb/yas-get-names-from-to-fields ()
  (interactive)
  (malb/yas-get-names-from-fields '("To")))

Of course, you can create much more elaborate snippets calling all kinds of functions to respond to all kinds of email. Once you created so many snippets that you’re at risk of loosing track, I recommend helm-yasnippet as a nice interactive interface for selecting the right snippet.

To simplify adding attachments — because traversing directory trees is boring — I wrote a small interface to Baloo, which is KDE’s version of OSX’s Spotlight, i.e. desktop search:

(defcustom helm-baloo-file-limit 100
  "Limit number of entries returned by baloo to this number."
  :group 'helm-baloo
  :type '(integer :tag "Limit"))


(defun baloo-search (pattern)
  (start-process "baloosearch" nil "baloosearch" (format "-l %d " helm-baloo-file-limit) pattern))

(defun helm-baloo-search ()
  (baloo-search helm-pattern))

(defun helm-baloo-transform (cs)
  (let '(helm-baloo-clean-up-regexp (rx (or
                                         control
                                         (seq "[0;31m" (+ (not (any "["))) "[0;0m")
                                         "[0;32m"
                                         "[0;0m")))
    (mapcar (function
             (lambda (c)
               (replace-regexp-in-string
                (rx (seq bol (+ space))) ""
                (replace-regexp-in-string helm-baloo-clean-up-regexp "" c))))
            cs)))

(defvar helm-source-baloo
  (helm-build-async-source "Baloo"
    :candidates-process #'helm-baloo-search
    :candidate-transformer #'helm-baloo-transform
    :action '(("Open" . (lambda (x) (find-file x)))
              ("Attach to Email" . (lambda (x) (mml-attach-file x))))))

(defun helm-baloo ()
  (interactive)
  (helm :sources helm-source-baloo
        :buffer "*helm baloo*"))

The line ("Attach to Email" . (lambda (x) (mml-attach-file x)) adds an option to attach any file to an email by pressing <F2>. If you prefer good ol’ locate, you can add this option to helm-locate too:

(helm-add-action-to-source "Attach to Email" #'mml-attach-file helm-source-locate)

Finally, a few more nice-to-have tweaks:

  • I write email in German and English and manually switching spell-checking dictionaries is not on; auto-dictionary-mode allows to pick the right dictionary automatically by looking for key words and their frequency like “the” or “der/die/das”.
  • Footnotes in email can be quite handy.
  • Typo-mode replace quotes and friends with their typographically correct counterparts because UTF-8 is a thing.
  • LaTeX notation like \lambda gets replaced by \lambda because, again, UTF-8 is a thing.
(add-hook 'message-mode-hook #'flyspell-mode)
(add-hook 'message-mode-hook #'typo-mode)
(add-hook 'message-mode-hook #'adict-guess-dictionary)
(add-hook 'message-mode-hook #'footnote-mode)

Footnotes:

1

GMail takes care of all sorting into folders aka labels.

2

Debian GNU/Linux comes with exim4 by default, which isn’t easy to configure. OpenSMTPD, on the other hand, is rather straightforward.


-1:-- Handling Email with Emacs (Post martinralbrecht)--L0--C0--June 09, 2017 04:13 PM