(or emacs irrelevant)

Monkeying around with JavaScript

Exciting encounter

Recently, I happened upon a wonderful blog called The Axis of Eval. I knew that I'd love it just when I read the name. (or emacs pales in comparison, but I couldn't just sit on my hands for months or years while thinking up a perfect blog name. If you think of a blog name as awesome as "The Axis of Eval" and are willing to let me use it, I'll probably make the switch.

The blog did not disappoint, featuring gems like this:

In the Lisp world, new languages are built by combining large, battle-tested building blocks, and polishing or updating them when needed, instead of starting over from toothpicks and double-sided duct tape. A large Lisp like Common Lisp is like a toolchain of decades-old tools that have proven their worth, and have been codified in standards, folklore, and implementations.

The only thing in the way of extracting information and enjoyment from this blog was the horrendous theme of black background, white foreground and magenta links. Plus the RSS was kind of quirky, and I couldn't just feed all of it into Elfeed.

Greasemonkey to the rescue!

In the previous post I've mentioned that, in addition to using the best editor, I'm using the best browser. Well, this particular best browser has an extension called Greasemonkey that allows you to automatically run your own JavaScript on certain websites.

I'm not very proficient in JavaScript, the following code I just found by searching around. The part to note is the @include - the pattern of website names for which this script should be run automatically.

// ==UserScript==
// @name        background
// @namespace   abo-abo
// @include     http://axisofeval.blogspot.nl/*
// @version     1
// @grant       none
// ==/UserScript==
(function () {
    document
        .body
        .setAttribute("style",
                      "background-color: #ffffff; color:#000");
    var nodesArray = document.getElementsByTagName('a');
    for (var i = 0; i < nodesArray.length; i++) {
        nodesArray[i].style.color = 'red';
    }
})();

I can barely stand to look at it. How could you take Scheme and turn it into this monstrosity? Such a shame. But it works, so I guess everyone should learn JavaScript. All hail the mighty HypnoToad JavaScript!

Throwing abbrevs into the mix

Currently, I'm using two methods for completing Elisp: company-mode and helm-lisp-completion-at-point. The latter is the cannon, the big gun: it always gets the job done, but I don't want to shoot at sparrows with it. So I only bring it out for hairy cases, like for stuff that starts with LaTeX-. Hence, the company-mode. But too often have I typed region- only to find 7 candidates staring at me, 4 of them useless. Which prompted me to look for an additional completion method.

Enter abbrevs

According to the manual,

A defined "abbrev" is a word which "expands", if you insert it, into some different text

Also,

Abbrevs can have "mode-specific" definitions, active only in one major mode

Sounds like something that could solve my problem with region- commands:

rb -> region-beginning
re -> region-end
ra -> region-active-p

Also, obviously,

Abbrevs expand only when Abbrev mode, a buffer-local minor mode, is enabled

Add it to the mix:

(defun oleh-emacs-lisp-hook ()
  (setq outline-regexp ";; ———")
  (company-mode 1)
  (abbrev-mode 1)
  (set (make-local-variable 'company-backends)
       '((company-elisp :with company-dabbrev-code)))
  (yas-minor-mode-on)
  (lispy-mode 1)
  (auto-compile-mode 1))

Some data acquisition

I had the whole abbrev thing in the back of my mind until I saw a link to the post Abbrevs for the most frequent elisp symbols. That's when I decided to act. That post eventually links to a pastebin, where 1600 abbrevs are defined. With my handy best extension for best browser I've opened the paste in Emacs best editor by just clicking the edit button in the RAW Paste Data section.

I had to M-x emacs-lisp-mode, since the file opened in text-mode. And boy, it's big. In lispy-mode, I usually use 99j to navigate 99 sexps down and therefore to the end of the list. Well, for this file even 999j wasn't enough. I quickly tired of deleting one-by-one the each individual useless abbrev. I mean:

ek -> echo-keystrokes,

when is that ever going to be useful? So I wrote this throw-away code:

(defun foobar ()
  (interactive)
  (lispy-mark-list 2)
  (let ((str (read (lispy--string-dwim)))
        count)
    (other-window 1)
    (goto-char (point-min))
    (setq count (count-matches str))
    (other-window 1)
    (lispy-out-backward 1)
    (deactivate-mark)
    (if (< count 5)
        (lispy-delete 1)
      (message "%d" count))))
(local-set-key (kbd "C-.") 'foobar)

After switching to a two-pane window layout, with point in the pastebin buffer, calling foobar would count the amount of the abbrev matches in my most frequent elisp buffer. If it was less than 5, the abbrev was auto-deleted, otherwise the decision was up to me, as holding C-. would no longer delete. In the end, there were only 56 abbrevs left out of 1600.

The final result

Here's what I have put into my abbrev_defs:

(define-abbrev-table 'emacs-lisp-mode-abbrev-table
    '(("sm" "string-match") ("mm" "major-mode")
      ("rb" "region-beginning") ("ca" "char-after")
      ("smd" "save-match-data") ("mb" "match-beginning")
      ("pm" "point-min") ("ir" "indent-region")
      ("sf" "search-forward") ("ci" "call-interactively")
      ("sn" "symbol-name") ("se" "save-excursion")
      ("scb" "skip-chars-backward") ("fc" "forward-char")
      ("ff" "find-file") ("fs" "forward-sexp")
      ("pa" "prefix-arg") ("re" "region-end")
      ("dc" "delete-char") ("ms" "match-string")
      ("tc" "this-command") ("dd" "default-directory")
      ("bc" "backward-char") ("rsf" "re-search-forward")
      ("snp" "substring-no-properties")
      ("bsnp" "buffer-substring-no-properties")
      ("lep" "line-end-position") ("bs" "buffer-substring")
      ("cc" "condition-case") ("ul" "up-list")
      ("bfn" "buffer-file-name") ("lb" "looking-back")
      ("tap" "thing-at-point") ("rm" "replace-match")
      ("fl" "forward-line") ("df" "declare-function")
      ("ntr" "narrow-to-region") ("dr" "delete-region")
      ("rsb" "re-search-backward") ("scf" "skip-chars-forward")
      ("wcb" "with-current-buffer") ("ie" "ignore-errors")
      ("gc" "goto-char") ("jos" "just-one-space")
      ("la" "looking-at") ("ow" "other-window")
      ("dk" "define-key") ("dm" "deactivate-mark")
      ("bod" "beginning-of-defun") ("sic" "self-insert-command")
      ("eol" "end-of-line") ("me" "match-end")
      ("nai" "newline-and-indent") ("cb" "current-buffer")
      ("atl" "add-to-list") ("rris" "replace-regexp-in-string")))

Mmm... minty

The backstory

In one of the earlier posts, I was discussing the implementation of an Emacs Lisp lexer for Pygments. Here, I'll show how to install the update and get nicely highlighted code in a pdf via the minted LaTeX package.

The install

Assuming that you are on a Debian-related system:

sudo apt-get install mercurial
mkdir ~/git && cd ~/git
hg clone https://bitbucket.org/abo-abo/pygments-main
cd pygments-main
make mapfiles
sudo python setup.py install

And, of course, I'm assuming that you already have TeX Live installed. I'm not too sophisticated about it, so I just install everything:

sudo apt-get install texlive-full

The result

So here I took some code from a previous post and copy-pasted it into minty.org file. And here's the result of the PDF export (C-c C-e lo): minty.pdf.

The red tape

org-mode had trouble exporting on my laptop until I did this:

cd /usr/bin/
sudo ln -s /usr/local/texlive/2013/bin/x86_64-linux/pdflatex

Also be mindful of the -shell-escape flag to pdflatex:

(setq org-latex-pdf-process
      '("pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f"
        "pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f"
        "pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f"))

tiny.el - the little package that could

The Challenge

It all started with a heated discussion with the author of yasnippet over some minor nonsense. In the end, we agreed to disagree, but not before he suggested:

So I hereby challenge you to create this stripped down, no-crap, version of yasnippet. Dub it " tiny is not yasnippet " after your grandiose views and in the glorious unix tradition of recursive acronyms

The Thought Process

Well, doing exactly that would probably be lame, but I really loved the acronym. Somewhere around that time I saw some post about using eval-and-replace, i.e. inserting some Elisp in your non-Elisp buffer and then replacing that code in-place with the result of the eval.

Here's the type of code that I was playing around with:

(mapcar
 (lambda (x) (* x x))
 (number-sequence 1 7))

Then I realized that the code should probably produce a string. Here's a more refined version:

(mapconcat
 (lambda (x)
   (format "hex: 0x%x"
           (* x x)))
 (number-sequence 1 7)
 ";\n")

Loops are a useful thing to have, they are a blind spot of yasnippet, and looping is exactly what the code above does. The parameters for this loop expansion are:

  • integer range start: 1
  • integer range end: 7
  • separator to join the expressions: ";\n"
  • Elisp expression to transform the linear range: (* x x)
  • format expression for the result: "hex: 0x%x"

So ideally, in order to have a package called tiny, I'd like to keep only the parameters and throw away everything else.

The Result

Here's the final result of the shortening, and what tiny-expand would produce:

  • 
    hex: 0x1;
    hex: 0x4;
    hex: 0x9;
    hex: 0x10;
    hex: 0x19;
    hex: 0x24;
    hex: 0x31
    

As you see, it's pretty compact, with only two characters which are not actually the parameters of the template:

  • m signifies the start of the template. I think this way is much better than something like having to mark the template body with a region before expanding. tiny-expand should be called from the end of the snippet, so there's no need to mark the end position.
  • | signifies the end of the Elisp expression and the start of the format string. It can be omitted if your format string starts with a %.

Note also the use of shortened Elisp. You can still use the full thing if you want. Or just use only the closing parens to resolve the ambiguities.

The Demos

Here are some more snippets, you can click on them to see what they expand to. You can also find them and more in the comments section of the source code:

  • 0 1 2 3 4 5 6 7 8 9 10
    

  • 5 6 7 8 9 10
    

  • 5,6,7,8,9,10
    

  • 25 36 49 64 81 100
    

  • 19 24 31 40 51 64
    

  • 0x19 0x24 0x31 0x40 0x51 0x64
    

  • 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
    

  • 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
    

  • aa,bb,cc,dd,ee,ff,gg,hh,ii,jj,kk,ll,mm,nn,oo,pp,qq,rr,ss,tt,uu,vv,ww,xx,yy,zz
    

  • aA,bB,cC,dD,eE,fF,gG,hH,iI,jJ,kK,lL,mM,nN,oO,pP,qQ,rR,sS,tT,uU,vV,wW,xX
    

  • aAa,bBb,cCc,dDd,eEe,fFf,gGg,hHh,iIi,jJj,kKk,lLl,mMm,nNn,oOo,pPp,qQq,rRr,sSs,tTt,uUu,vVv,wWw,xXx
    

  • 0 and 0 and 0
    2 and 1 and 1
    4 and 4 and 2
    6 and 9 and 3
    8 and 16 and 4
    10 and 25 and 5
    12 and 36 and 6
    14 and 49 and 7
    16 and 64 and 8
    18 and 81 and 9
    20 and 100 and 10
    

  • 6 8 10 12 14 16 18 20 22 24 26
    

  • 1.0
    2.718281828459045
    7.38905609893065
    20.085536923187668
    54.598150033144236
    148.4131591025766
    403.4287934927351
    1096.6331584284585
    2980.9579870417283
    8103.083927575384
    22026.465794806718
    

  • 00000000002.72
    00000000007.39
    00000000020.09
    00000000054.60
    00000000148.41
    00000000403.43
    00000001096.63
    00000002980.96
    00000008103.08
    00000022026.47
    00000059874.14
    00000162754.79
    00000442413.39
    00001202604.28
    00003269017.37
    00008886110.52
    00024154952.75
    00065659969.14
    00178482300.96
    00485165195.41
    

  • 1 2 4 8 16 32 64 128
    

  • 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07
    

  • 0.00 1.00 2.00 3.00 4.00 5.00 6.00 7.00 8.00 9.00 10.00
    

  • * TODO http://emacsrocks.com/e01.html
    * TODO http://emacsrocks.com/e02.html
    * TODO http://emacsrocks.com/e03.html
    * TODO http://emacsrocks.com/e04.html
    * TODO http://emacsrocks.com/e05.html
    * TODO http://emacsrocks.com/e06.html
    * TODO http://emacsrocks.com/e07.html
    * TODO http://emacsrocks.com/e08.html
    * TODO http://emacsrocks.com/e09.html
    * TODO http://emacsrocks.com/e10.html
    * TODO http://emacsrocks.com/e11.html
    * TODO http://emacsrocks.com/e12.html
    * TODO http://emacsrocks.com/e13.html
    * TODO http://emacsrocks.com/e14.html
    

  • * TODO Wash dog 2
    DEADLINE: <2015-01-01 Thu>
    * TODO Wash dog 3
    DEADLINE: <2015-01-06 Tue>
    * TODO Wash dog 4
    DEADLINE: <2015-01-11 Sun>
    * TODO Wash dog 5
    DEADLINE: <2015-01-16 Fri>
    * TODO Wash dog 6
    DEADLINE: <2015-01-21 Wed>
    * TODO Wash dog 7
    DEADLINE: <2015-01-26 Mon>
    * TODO Wash dog 8
    DEADLINE: <2015-01-31 Sat>
    * TODO Wash dog 9
    DEADLINE: <2015-02-05 Thu>
    * TODO Wash dog 10
    DEADLINE: <2015-02-10 Tue>
    

    You can expand them one-by-one to see what they do. As you can see, Ruby-style interpolation is available in the format string. There's also one special function called date that you can use there. It takes the start date as a string ("Jan 1" in the example) and an integer shift and prints an org-style date.

    The full syntax

    The full syntax for the snippet is:

    m{range start:=0}{separator:= }{range end}{Lisp expr:=indentity}|{format expr:=%d}
    • You always start with m.
    • Then optional range start that defaults to 0.
    • Then optional separator that defaults to a single space.
    • Then mandatory range end.
    • Then optional Lisp expr, that defaults to identity.
    • Then optional format-style string, that defaults to %d. You have to separate it with | if the format string does not start with %. You can also Ruby-style interpolation here, e.g. %(* x x).
    • With the point at the end of the snippet, M-xtiny-expand.

    The Summary

    In the end, tiny lives up to the name, implementing only one snippet that can be used in a variety of ways.

    tiny is not yasnippet

  • Ode to the toggle

    Man, I just love toggles: the light switches, the f - full-screen key in vlc, and the clicky pens (ooh, those are the best). So I try to model some of my Emacs key bindings as toggles.

    Let me just quantify the two features that make a good toggle:

    • only two states: on and off
    • the state is visible at a glance

    One could argue that with undo most editing commands become toggles. But they're not, since each time you call undo, you mess with Emacs's undo state. And the undo state isn't visible at a glance, so both requirements for a good toggle aren't fulfilled.

    I'll demonstrate the two editing commands that I use every day, capitalize-word-toggle and upcase-word-toggle, that are good toggles.

    capitalize-word-toggle

    Talk is cheap. Show me the code.

    (defun char-upcasep (letter)
      (eq letter (upcase letter)))
    
    (defun capitalize-word-toggle ()
      (interactive)
      (let ((start
             (car
              (save-excursion
                (backward-word)
                (bounds-of-thing-at-point 'symbol)))))
        (if start
            (save-excursion
              (goto-char start)
              (funcall
               (if (char-upcasep (char-after))
                   'downcase-region
                 'upcase-region)
               start (1+ start)))
          (capitalize-word -1))))
    (global-set-key (kbd "C-z") 'capitalize-word-toggle)
    

    I may not have mentioned this before, but you should for the most part ignore the key bindings mentioned on this blog. I'm actually using them, they work for me because of my non-standard layout, but you should assign what works for you.

    Anyway, capitalize-word-toggle clearly has a state that's visible at a glance: the first char of the current symbol. Also, there are only two possible states: the char can either be upper-case or lower-case. Hence, I can toggle this state with C-z for fun and profit.

    Maybe some background on how this command is useful for me. I write a bunch of C++, and the code features a lot of lines like this:

    Triangulation triangulation; // duh
    

    So when I'm using auto-complete, it often eagerly expands to Triangulation when I want triangulation, and the other way around. So capitalize-word-toggle is super-useful there.

    upcase-word-toggle

    (defun upcase-word-toggle ()
      (interactive)
      (let ((bounds (bounds-of-thing-at-point 'symbol))
            beg end
            (regionp
             (if (eq this-command last-command)
                 (get this-command 'regionp)
               (put this-command 'regionp nil))))
        (cond
          ((or (region-active-p) regionp)
           (setq beg (region-beginning)
                 end (region-end))
           (put this-command 'regionp t))
          (bounds
           (setq beg (car bounds)
                 end (cdr bounds)))
          (t
           (setq beg (point)
                 end (1+ beg))))
        (save-excursion
          (goto-char (1- beg))
          (and (re-search-forward "[A-Za-z]" end t)
               (funcall (if (char-upcasep (char-before))
                            'downcase-region
                          'upcase-region)
                        beg end)))))
    (global-set-key (kbd "C->") 'upcase-word-toggle)
    

    upcase-word-toggle's state becomes binary after you call it once, since initially the thing at point could have mixed case. But afterwards, it's either all lowercase or all uppercase. So again, a clearly visible binary state is a good thing.

    This command works either on (bounds-of-thing-at-point 'symbol) or on the active region. Since region-active-p is deactivated after you call the command once, there's some machinery to remember the state and to toggle when called again.