Run aspell from Windows Emacs using WSL

posted Sep 7, 2019, 10:45 AM by Juan Jose Garcia-Ripoll   [ updated Sep 7, 2019, 10:46 AM ]

If you have the Windows Subsystem for Linux, you can use its spell checker from Emacs. I have had to resort to this because hunspell.exe does no longer find my dictionaries and Emacs' code for hunspell is broken on Windows.

Step 1: Create a script called aspell.cmd with this content

@echo off
wsl aspell %1 %2 %3 %4 %5 %6 %7

Step 2: In your .emacs file, tell it to use this script for spell checking. Simply enter

(setq ispell-program-name "your/path/to/aspell.cmd")

If you now type M-$ (Esc followed by $ sign) you will enter the ispell mode. However, use of flyspell is more recommended.

Open mime attachments in Emacs

posted Jun 6, 2019, 12:37 AM by Juan Jose Garcia-Ripoll   [ updated Jun 6, 2019, 12:38 AM ]

As shown in a previous post, Emacs has some nice email readers. However, the default handling of most attachments (MIME types in traditional programming speak) is just to save the file. I have configured Emacs to rely on Windows for opening some types of attachments that I find often. As an example, I show below how to open PDFs and Word files, although it can be extended to other file types. Place this code in your Emacs' init.el file.

    (ctree-set-calist-strictly
     'mime-acting-condition
     '((mode . "play")
       (type . application)
       (subtype . pdf)
       (method . jjgr-default-mime-open)))
    (ctree-set-calist-strictly
     'mime-acting-condition
     '((mode . "play")
       (type . application)
       (subtype . vnd.openxmlformats-officedocument.wordprocessingml.document)
       (method . jjgr-default-mime-open)))

    (defun jjgr-default-mime-open (&optional a b)
      (let* ((entity (get-text-property (point) 'mime-view-entity))
             (name (mime-entity-safe-filename entity))
             (filename (concat (getenv "HOMEDRIVE")
                               (getenv "HOMEPATH")
                               "\\Downloads\\"
                               name)))
        (mime-write-entity-content entity filename)
        (print `(start-process "cmd.exe" "*Play*" "cmd.exe" ,filename))
        (start-process "cmd.exe" nil "cmd.exe" "/c" "start" "/b" filename)))

Handling email with Emacs' Wanderlust

posted May 3, 2019, 1:11 PM by Juan Jose Garcia-Ripoll   [ updated May 4, 2019, 3:17 AM ]

After some months using Thunderbird, I got a bit tired of some intrinsic problems with the speed of email synchronization, or the simple fact that saving an email to a folder would constantly fail. I have finally reverted back to handling email in Emacs, which is extremely productive, one you jump the hassle of configuring it.

My interface of choice is Wanderlust. There are some general guides for configuring it scattered through the web, plus a page on Emacs' wiki. In practice you will face some problems in the following fields:
  • Connection configuration: server, ports, type of connection, secrets, etc.
  • Handling and caching of emails.
  • Displaying and sending html emails.
  • Attachments
  • Contacts database.
I have not solved all of them, but my configuration is good enough that I think it is worth sharing. Here is the piece of code that I have in my Emacs' init file.

(use-package wl
  ;; The name of the package in MELPA is "wanderlust" but the
  ;; feature provided by that package is 'wl
  :ensure wanderlust
  :commands (wl)
  :bind
  (:map wl-summary-mode-map
        ("b a" . (lambda () (interactive) (djcb-wl-summary-refile "%Archives.2019")))
        ;;Swap a and A in summary mode, so citing original message is on a and no-cite on A.
        ("A" . wl-summary-reply)
        ("a" . wl-summary-reply-with-citation)
        ("r b" . jjgr-bbdb-mua-auto-update)
        )
  :hook
  ((wl-mail-send-pre . djcb-wl-draft-subject-check)
   (wl-mail-send-pre . djcb-wl-draft-attachment-check)
   (wl-mail-setup . wl-draft-config-exec))

  :config
  (progn
    (print "Wanderlust configured")
    (defun jjgr-bbdb-mua-auto-update ()
      (interactive)
      (wl-summary-enter-handler)
      (bbdb-mua-auto-update nil 'query)
      (mime-preview-quit))

    (defun djcb-wl-summary-refile (&optional folder)
      "refile the current message to FOLDER; if FOLDER is nil, use the default"
      (interactive)
      (wl-summary-refile (wl-summary-message-number) folder)
      (wl-summary-exec))

    ;; Store sent emails in the current folder
    (defun jjgr-determine-fcc-buffer ()
      (if (or (equal wl-message-buffer-cur-folder "%INBOX")
              (null wl-message-buffer-cur-folder))
          "%Sent"
        wl-message-buffer-cur-folder))
    (setq wl-fcc 'jjgr-determine-fcc-buffer)

    ;; Check messages for missing subject or abstract
    (defun djcb-wl-draft-subject-check ()
      "check whether the message has a subject before sending"
      (if (and (< (length (std11-field-body "Subject")) 1)
               (null (y-or-n-p "No subject! Send current draft?")))
          (error "Abort.")))

    (defun djcb-wl-draft-attachment-check ()
      "if attachment is mention but none included, warn the the user"
      (save-excursion
        (goto-char 0)
        (unless ;; don't we have an attachment?

            (re-search-forward "^Content-Disposition: attachment" nil t)
          (when ;; no attachment; did we mention an attachment?
              (or (re-search-forward "attach" nil t)
                  (re-search-forward "adjunt" nil t))
            (unless (y-or-n-p "Possibly missing an attachment. Send current draft?")
              (error "Abort."))))))

    (if (boundp 'mail-user-agent)
        (setq mail-user-agent 'wl-user-agent))
    (if (fboundp 'define-mail-user-agent)
        (define-mail-user-agent
          'wl-user-agent
          'wl-user-agent-compose
          'wl-draft-send
          'wl-draft-kill
          'mail-send-hook))

    (require 'bbdb)
    ) ; progn

  :init
  (setq wl-folders-file "~/OnlineFolder/Library/dot.folders"

        ;; SMTP server for mail posting.
        wl-smtp-posting-server "smtp.some.server.org"
        wl-smtp-posting-port 587
        wl-smtp-posting-user "user-name"
        wl-smtp-authenticate-type "plain"
        wl-smtp-connection-type 'starttls
        wl-from "some.email@some.server.org"
       
        ;; Do not cache passwords. The cache corrupts server
        ;; secrets.
        password-cache nil

        elmo-imap4-default-user "user-name"
        elmo-imap4-default-server "imap.some.server.org"
        elmo-imap4-default-port 993
        elmo-imap4-default-authenticate-type 'clear
        elmo-imap4-default-stream-type 'ssl
        elmo-passwd-storage-type 'auth-source

        ;; Location of archives
        elmo-archive-folder-path "~/OnlineFolder/Mail/"

        ;; Location of MH and Maildir folders
        elmo-localdir-folder-path "~/OnlineFolder/Mail/"
        elmo-maildir-folder-path "~/OnlineFolder/Mail/"

        wl-message-id-domain "some.email@some.server.org"
        wl-from "Some person <some.email@some.server.org>"
        mime-edit-default-signature "~/OnlineFolder/Library/dot.signature"
        wl-forward-subject-prefix "Fwd: "

        wl-default-folder "%INBOX" ;; my main inbox
        wl-biff-check-folder-list '("%INBOX") ;; check every 180 seconds
        wl-biff-check-interval 180

        wl-draft-folder "%Drafts"  ;; store drafts in 'postponed'
        wl-trash-folder "%Trash"   ;; put trash in 'trash'

        wl-stay-folder-window t
        wl-folder-window-width 25
        wl-folder-use-frame nil

        wl-message-ignored-field-list '("^.*")
        wl-message-visible-field-list '("^From:" "^To:" "^Cc:" "^Date:" "^Subject:")
        wl-message-sort-field-list wl-message-visible-field-list
        wl-summary-width 120 ;; No width
        wl-summary-default-sort-spec 'date
        wl-message-window-size '(1 . 2)

        ;; Always download emails without confirmation
        wl-prefetch-threshold nil
        wl-message-buffer-prefetch-threshold nil
        elmo-message-fetch-threshold nil

        ;; Rendering of messages using 'shr', Emacs' simple html
        ;; renderer, but without fancy coloring that distorts the
        ;; looks
        mime-view-text/html-previewer 'shr
        shr-use-colors nil

        wl-draft-config-alist
        '(((string-match "1" "1")
           (bottom . "\n--\n") (bottom-file . "~/OnlineFolder/Library/dot.signature"))
          )

        ;; don't ****ing split large messages
        mime-edit-split-message nil
        )
  ) ; use-package wanderlust

I keep these files in some online storage that I share between computers, which makes synchronizing databases easier. In ~/OnlineFolder/Library/ I create a dot.folders file with the folders that I am interested in from my IMAP server.

%INBOX "Inbox"
%Archives.2019 "Archivo"
%Drafts "Drafts"
%Sent "Sent"
%Trash "Trash"
%/

The final %/ is there to allow me to browse all other folders in the server, leaving them hidden by default.

The next step is to set up the contacts database. I import regularly contacts from Google, because they are kept up-to-date by the mobile phone. However, that requires some tweaking of standard libraries. Another goodie is the use of bbdb to auto-complete addresses when writing the header of an email, just by pressing <tab>. Finally, I have deactivated BBDB3's automatic gathering of contacts. The Wanderlust configuration invokes BBDB to do this when I press the key <r> followed by <b> in the email summary.

(use-package bbdb
  :ensure t
  :commands (bbdb-initialize)
  :hook
  ((mail-setup . bbdb-mail-aliases)
   (message-setup . bbdb-mail-aliases)
   (wl-mail-setup . jjgr-add-bbdb-tab-completion))

  :init
  (setq bbdb-file "~/OnlineFolder/Library/Emacs/bbdb"
        bbdb-mua-pop-up t
        bbdb-mua-pop-up-window-size t)

  :config
  (progn
    (bbdb-initialize 'wl)
    (bbdb-mua-auto-update-init 'wl)

    (defun my-bbdb-complete-mail ()
      "If on a header field, calls `bbdb-complete-mail' to complete the name."
      (interactive)
      (when (< (point)
               (save-excursion
                 (goto-char (point-min))
                 (search-forward (concat "\n" mail-header-separator "\n") nil 0)
                 (point)))
        (bbdb-complete-mail)))

    (defun jjgr-add-bbdb-tab-completion ()
      (define-key (current-local-map) (kbd "<tab>")
        'my-bbdb-complete-mail))
    ) ; progn
  ) ; use-package bbdb

(use-package bbdb-csv-import
  :ensure t
  :defer t
  :commands (bbdb-csv-import-file bbdb-csv-import-buffer)
  :after (bbdb)
  :config
  ;; Remove fields that Google Contacts creates, inserting them at the right
  ;; position.
  ;; 1. additional-name -> prepend it to lastname
  (defun jjgr-fix-bbdb-csv-import ()
    (interactive)
    (dolist (r (bbdb-records))
      (print r)
      (let* ((fields (bbdb-record-xfields r))
             (aname (assoc 'additional-name fields)))
        (when aname
          (print aname)
          (let ((new-lastname (concatenate 'string (cdr aname) " " (bbdb-record-lastname r))))
            ;;(setf (bbdb-record-lastname r) new-lastname)
            (bbdb-record-set-lastname r new-lastname)
            (bbdb-record-set-xfields r (assoc-delete-all 'additional-name fields))
            )))))

  )

Thunderbird shortcuts

posted Feb 8, 2019, 1:48 PM by Juan Jose Garcia-Ripoll

Mozilla Thunderbird
Thunderbird is a commonly used mail client that works on many platforms. It is a bit heavy and requires quite a lot of mouse use, as many graphical programs nowadays, but you can configure it to work better with a few keystrokes.

In order to follow these steps, please download first the add-on Dorando-keyconfig You can then configure Dorando with keybindings, adding the ones you find more suitable, and renaming the ones that are less standard. I list below the ones I use and change, just to remember that in my own machines.
  1. Navigate messages in a 'vim' fashion. You can reassign the "Previous > Message" or "Next > Message" to K and J, so that Thunderbird behaves just like vimium on Firefox.
  2. Zoom in and out conversations. I assign '+' and '-' to 'Conversation > Expand...' and 'Conversation > Contract'.
  3. Jump to specific folders. Right-click on the folder you want to use to find out its URI, the unique identifier or address that is used to find it. Then create a new keybinding with a code like the following one.
    var uri="imap://**the address of your folder**"
    for (i in gFolderTreeView._enumerateFolders) {
      var folder = gFolderTreeView._enumerateFolders[i]
      if (folder.URI==uri) {
        gFolderTreeView.selectFolder(folder)
      }
    }

  4. Archive email to specific folder. Find the URI of the destination folder and assign the following Javascript to the code you desire.
    MsgMoveMessage(MailUtils.getFolderForURI("imap://***@***/**URI to your folder**"))

  5. Save all attachments. Assign the following Javascript to Ctrl+Shift+S to save all the attachments in an email. It will open a dialogue to find a folder
    HandleAllAttachments("save")


Launch Jupyter notebooks automatically in Windows

posted Jan 26, 2019, 1:08 PM by Juan Jose Garcia-Ripoll   [ updated Jan 26, 2019, 1:09 PM ]

In latest editions of Anaconda, the installation wisely hides Python from the global path, but this also means that there are no good file associations for Python files or Jupyter notebooks.

One solution out there is nbopen. This utility launches one server and tries to reuse it with further notebooks. I have tried in the past, but it does not work if you want to work with notebooks in multiple directories. Somehow the tight permissions of Jupyter forbid it to access directories that are not inside the folder where it was initially started.

The only alternative that I have found is to build my own AutoHotKey script, which I can associate to *.ipynb files. This post documents how to do it. I assume that you have installed Miniconda for your own user and that the installation is located under %USERPROFILE%\Miniconda3
  1. Install AutoHotKey, a wonderful extension to map keys and build general scripts for Windows.
  2. Create and save the following script as start-jupyter-notebook.ahk
    #NoEnv
    filename=""
    EnvGet, home, USERPROFILE
    EnvGet, windir, windir
    If (%0% > 0) {
        loop, %1%
        file = %A_LoopFileFullPath%
        SplitPath, file, filename, directory
        filename := """" filename """"
    } Else {
        file := ""
        directory := home
    }
    miniconda := home "\Miniconda3"
    activate  := miniconda "\Scripts\activate.bat"
    command   := activate " " miniconda " && echo Working at directory && cd && jupyter notebook " filename
    Run, %windir%\system32\cmd.exe /c "%command%", %directory%, min

  3. Run the AutoHotKey compiler to convert it into an executable. I like to associate it with the Jupyter notebooks icon because then this icon is used by File Explorer. I call the executable "Open with Jupyter.exe" because it looks nice when I install it in the Start pane. The command line below one single line, sorry if it looks garbled:
    "%ProgramFiles%\AutoHotkey\Compiler\Ahk2Exe.exe" /in start-jupyter-notebook.ahk /out "Open with Jupyter.exe" /icon %USERPROFILE%\Miniconda3\Menu\jupyter.ico

  4. Open the properties for any Jupyter notebook (Right-click and select Properties) and click on "Change" to select the program that opens the notebooks. You may need to select "More programs" and use the explorer to find your compiled script.
The script will launch one server every time it is invoked. You can open futher files from within Jupyter using File -> Open in the Jupyter notebook menu.

When you are done, you can either kill the command line window with the Jupyter notebook or quit the server from the browser. For the latter, use File -> Open to open the Jupyter file browser and then click on Quit. You can then safely close all browser tabs.

A CSS stylesheet for exporting org-mode files

posted Apr 29, 2018, 1:49 PM by Juan Jose Garcia-Ripoll   [ updated Apr 30, 2018, 2:17 AM ]

I have been writing a big document using org-mode, exporting it to HTML and using the browser to generate a PDF from it. It looks pretty nice, similar to what I achieved with Word on an earlier version, and faster to write.

/*
 * CSS for org-mode document. Save with name style.css and include with
 * #+HTML_HEAD: <link rel="stylesheet" type="text/css" href="style.css" />
 * It should be placed on the same directory as the HTML that is exported.
 *
 * Print options
 */

@page {
    margin: 2.54cm;
    size: A4;
    /* Page numbers, does not really work on most browsers. */
    @bottom-right {
        padding-right: 20px;
        content: "Page " counter(page);
    }
}

@media print {
    body {
        font-size: 11pt;
    }
    h2, h3, h4, h5 {
        page-break-after: avoid;
    }
    #table-of-contents {
        /* Page break after table of contents */
        page-break-after: always;
        margin-top: 2em;
        line-height: 1.5em;
        width: 80%;
        height: 40%;
        margin: auto auto;
    }
    #table-of-contents > h2 {
        text-align: center;
        margin-top: 4em;
    }
    .figure {
        /* Figure stays together with caption */
        page-break-inside: avoid;
    }
}

/*
 * For ordinary browsing
 */
@media screen {
    footer {
        display: none;
    }
    body {
        font-size: 12pt;
        margin-left: 2em;
        margin-right: 2em;
        max-width: 50em;
    }
    #table-of-contents {
        margin-top: 2em;
    }
}

/*
 * Common format
 */
body {
    font-family: Calibri;
    line-height: 1.2;
}

h1, h2, h3, h4, h5, h6 {
    margin-top: 0;
    margin-bottom: 6pt;
}

h2 {
    font-size: 1.3;
    text-transform: uppercase;
}

h1 {
    font-size: 1.6;
    font-variant: small-caps;
}

p {
    text-align: justify;
}

.figure > p {
    font-style: italic;
}

a:link, a:visited {
    text-decoration: none;
}

th.org-left {
    text-align: left;
}

/* Mark external links with an icon. */
a[href^="http://"]:after,
a[href^="https://"]:after {
    content: '\2021'; /*'\1F5D7';*/
    font-style: roman;
}

A sample file would be:
#+TITLE: Sample org file
#+STARTUP: inlineimages
#+LANGUAGE: es
#+HTML_HEAD: <link rel="stylesheet" type="text/css" href="style.css" />

* First heading
* Second heading
** Subheading 1
** Subheading 2

#+caption: Some fancy figure
#+attr_html: :width 50%
[[./some-figure.png]]

The file can be simply exported using Ctrl-c Ctrl-e and then pressing 'h' followed by 'o' (HTML and Open in browser). Afterwards, printing can be done from the browser.

Edit: an example with output PDF is attached to this page.

Hope this is useful for you.

My .emacs configuration

posted Apr 9, 2018, 10:46 AM by Juan Jose Garcia-Ripoll   [ updated Apr 9, 2018, 1:06 PM ]

In case you like peeking into other people's configuration file or learn how to configure Emacs in Windows, here is my last set of settings.

Note: this relies on a personal fork of shackle to have LaTeX pop-up windows be automatically closed https://github.com/juanjosegarciaripoll/shackle when they are no longer used (AucTeX kills the buffer with errors but shackle does not close the window which showed the errors)
;;; -*- mode: emacs-lisp; -*-
;;; Different computers have different customizations due to
;;; monitor screen sizes, resolutions, etc.
;;;
(defun tic (&optional n)
  (print (list n (get-internal-run-time))))

;;;
;;; All our files will be located under a Dropbox common folder
;;; whose location may shift between computers. Also, fonts
;;; screen resolutions, etc, may vary.
;;;
(setq dropbox "~/Dropbox/Library/")
(setq dropbox-elpa "~/Dropbox/Library/Emacs/elpa-25")
(cond ((string= system-name "DEUTSCH")
       (setq juanjo:text-font-family "Noto Serif"
	     juanjo:text-font-height 110
	     juanjo:text-line-spacing 0.3
	     juanjo:margin 2))
      ((string= system-name "DESKTOP-HDKKLFG")
       (setq juanjo:text-font-height 140
	     juanjo:text-font-family "Vollkorn"
	     juanjo:text-line-spacing 0.0
	     juanjo:margin 1)
       (setq juanjo:text-font-family "Noto Serif"
	     juanjo:text-font-height 120
	     juanjo:text-line-spacing 0.3
	     juanjo:margin 2))
      (t
       (setq juanjo:text-font-family "DejaVu Serif"
	     juanjo:text-line-spacing 0.3
	     juanjo:text-font-height 110
	     juanjo:margin 1)))

(let ((custom-file (expand-file-name "dot.custom.el" dropbox)))
  (when (file-exists-p custom-file)
    (load custom-file)))

;;;
;;; Change to home directory. When using emacs.exe from Chocolatey,
;;; Emacs tends to start from the location of the shim executable.
;;;
(cd "~/")

(require 'package)
(setq package-user-dir dropbox-elpa)
(add-to-list 'package-archives '("melpa" . "http://melpa.org/packages/"))
(add-to-list 'package-archives '("melpa-stable" . "http://stable.melpa.org/packages/"))
(package-initialize)
;;(package-refresh-contents t)

;;;
;;; Lazy package management with postponed initialization, automatic
;;; installation of those packages.
;;;
(unless (package-installed-p 'use-package)
  (package-refresh-contents)
  (package-install 'use-package))
(require 'use-package)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; Persistence
;;;

;; Remember from session to session all the commands introduced
;; in the minibuffer, files opened, etc.
(setq savehist-file (expand-file-name "Emacs/dot.history" dropbox))
(savehist-mode 1)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; Emacs quirks
;;;

;;; Do not use tabs when editing files
(setq-default indent-tabs-mode nil)
(setq tab-width 4)

;;; Make tabs indent first, then complete
(setq-default tab-always-indent 'complete)

;;; Remove yes-or-no questions, defaulting to single-keystroke
(fset 'yes-or-no-p 'y-or-n-p)

;;; Remove text from *scratch* buffer
(setq initial-scratch-message nil)

;;; Do not disable commands
(setq disabled-command-function nil)
;;(put 'downcase-region 'disabled nil)
;;(put 'scroll-left 'disabled nil)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Visual appearance:
;;
;; My preferred fonts for text and coding. I store them
;; as variables because they are used in buffer-local
;; customizations.
(setq code-face '(:family "Source Code Pro" :foundry "outline"
			  :slant normal :weight normal :height 98 :width normal)
      text-face `(:family ,juanjo:text-font-family :foundry "outline"
			  :slant normal :weight normal
			  :height ,juanjo:text-font-height :width normal)
      bold-text-face (plist-put (copy-sequence text-face) :weight 'bold))
(apply 'set-face-attribute 'default nil code-face)
;;
;; Darker background, softer coloring
(load "~/Dropbox/Library/Emacs/mycarthy-theme.el")
(load-theme 'mycarthy)
;; No blinking cursor
(blink-cursor-mode -1)
;; No fringe bars on both sides
(fringe-mode 0)
;; No continuation character on truncate-line mode
(set-display-table-slot standard-display-table 0 ?\ )

;;;
;;; Text editing with proportional fonts, good interline spacing
;;; and window margins.
;;;
(defun juanjo:text-mode-hooks ()
  ;; Some more spacing between lines
  (setq-local line-spacing juanjo:text-line-spacing)
  ;; Wrap around words
  (visual-line-mode +1)
  ;; Text modes should have proportional fonts
  (buffer-face-set text-face)
  (setq left-margin-width juanjo:margin
	right-margin-width juanjo:margin)
  )
(add-hook 'text-mode-hook 'juanjo:text-mode-hooks)

;;
;; Placement of windows
;;
(use-package shackle
  :load-path "~/Dropbox/Library/Emacs/local/shackle/"
  :commands shackle-mode
  :config
  (setq shackle-rules
	'(("\\*TeX.*\\*" :regexp t :autoclose t :align below :size 10)
	  ("\\*.*Help\\*" :regexp t :autoclose t :align below :size 10)))
  (setq shackle-default-rule '(:select t)))

(shackle-mode)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; My convenience bindings
;;;

;;; Delete whole buffer
(defun mps-clear-all ()
  (interactive)
  (delete-region (point-min) (line-end-position 0)))
(global-set-key [?\C-x ?\C-u] 'mps-clear-all)

;; (add-to-list 'load-path (concat dropbox "Emacs/fakecygpty/"))
;; (require 'fakecygpty)
;; (fakecygpty-activate)
;; (setq fakecygpty-ignored-program-regexps '("[cC][mM][dD]" "[cC][mM][dD][pP][rR][oO][xX][yY]"))
;; ;; We need to force using fakecygpty with tramp
;; (eval-after-load "tramp"
;;     '(progn
;;        (add-to-list 'tramp-methods
;;                     (mapcar
;;                      (lambda (x)
;;                        (cond
;;                         ((equal x "sshx") "cygssh")
;;                         ((eq (car x) 'tramp-login-program)
;; 			 (list 'tramp-login-program "fakecygpty ssh"))
;;                         (t x)))
;;                      (assoc "sshx" tramp-methods)))
;;        (setq tramp-default-method "cygssh")))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; LaTeX with AucTeX
;;;

;;
;; TeXcount setup for TeXcount version 2.3 and later
;;
(defun juanjo:texcount ()
  (interactive)
  (let*
    ((this-file (buffer-file-name))
     (enc-str (symbol-name buffer-file-coding-system))
     (enc-opt
      (cond
       ((string-match "utf-8" enc-str) "-utf8")
       ((string-match "latin" enc-str) "-latin1")
       ("-encoding=guess")
       ))
     (process-environment
      ;; Windows screwes this for me
      (append '("LANG=") process-environment))
     (word-count
      (with-output-to-string
	(with-current-buffer standard-output
	  (call-process "texcount" nil t nil "-0" enc-opt this-file))))
     )
    (message word-count)
    ))

(use-package latex
  :defer t
  :mode ("\\.tex\\'" . latex-mode)
  :bind
  (:map LaTeX-mode-map
	("C-c l" . TeX-error-overview)
        ("C-c w" . juanjo:textcount))

  :config
  ;; Minor changes to the LaTeX mode
  (add-hook 'LaTeX-mode-hook 'juanjo:latex-hooks)
  (add-hook 'LaTeX-mode-hook 'turn-on-reftex)
  (add-hook 'LaTeX-mode-hook 'juanjo:latex-hooks)
  (add-hook 'LaTeX-mode-hook 'flyspell-mode)

  (setq TeX-PDF-mode t
	TeX-save-query nil
	TeX-source-correlate-mode t
	TeX-source-correlate-method 'synctex
	;; Tell SumatraPDF to open the current PDF file, indicating
	;; which is the source file and line number (used by synctex)
	;; and provide a valid command line to edit at the desired
	;; location of a source file that is linked in the PDF.
	TeX-view-program-list
	'(("Sumatra PDF"
	   ("\"SumatraPDF.exe\" -reuse-instance "
	    "-inverse-search \"emacsclientw.exe +%%l \\\"%%f\\\"\" "
	    (mode-io-correlate " -forward-search %a %n ")
	    " %o")))
	TeX-view-program-selection
	'(((output-dvi style-pstricks) "dvips and start")
	  (output-dvi "Yap")
	  (output-pdf "Sumatra PDF")
	  (output-html "start"))
	;; Open TeX error list after compilation
	;; and show all warnings
	TeX-error-overview-open-after-TeX-run t
	TeX-debug-warnings t)
  
  ;; Replace viewer with SumatraPDF
  (assq-delete-all 'output-pdf TeX-view-program-selection)
  (add-to-list 'TeX-view-program-selection
	       '(output-pdf "Sumatra PDF"))

  ) ; use-package latex

;;
;; Hook into font-latex to reduce the size of some section,
;; chapter, subscript and superscript fonts
;;
(use-package font-latex
  :defer t
  :config
  (set-face-attribute 'font-latex-subscript-face nil :height 0.6)
  (set-face-attribute 'font-latex-superscript-face nil :height 0.6)
  ;; I do not like large fonts for the sections, chapters, etc
  (let ((height (round (* 1.1 juanjo:text-font-height))))
    (dolist (face '(font-latex-sectioning-0-face
		    font-latex-sectioning-1-face
		    font-latex-sectioning-2-face
		    font-latex-sectioning-3-face
		    font-latex-sectioning-4-face
		    font-latex-sectioning-5-face))
      (apply 'set-face-attribute face nil
	     (plist-put (copy-sequence bold-text-face)
			:height height))))
  )

(use-package reftex
  :defer t
  :commands turn-on-reftex
  :init
  (setq reftex-plug-into-AUCTeX t
	;; RefTeX list of sections, labels and figures shows as
	;; vertical bar to the left of the window.
	reftex-toc-split-windows-horizontally t
	;; RefTeX table of contents does not indicate which
	;; sections are in which files.
	reftex-toc-include-file-boundaries nil))

(use-package latex-extra
  :defer t
  :ensure t
  :commands latex-extra-mode
  :bind
  (:map LaTeX-mode-map 
	("M-" . latex/next-section-same-level)
	("M-" . latex/previous-section-same-level))
  :init
  (add-hook 'LaTeX-mode-hook (lambda () (latex-extra-mode) (auto-fill-mode -1)))
  ;; Do not override AucTex's font commands
  (setq latex/override-font-map nil))

(defun delatexify ()
  (save-excursion
    (goto-char 0)
    (replace-string "{\'\i}" "í")
    (replace-string "\\'o" "ó")
    (replace-string "\\'a" "á")
    (replace-string "\\'e" "é")
    (replace-string "\\'u" "ú")
    (replace-string "\\\"a" "ä")
    (replace-string "\\\"o" "ö")))

(defun juanjo:html-mode-hooks()
  ;; Wrap around words
  (visual-line-mode +1)
  ;; Text modes should have proportional fonts
  (buffer-face-set code-face))

(add-hook 'html-mode-hook 'juanjo:html-mode-hooks)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Dictionaries, languages and encodings
;;
;;
;; Use hunspell.exe for automatic spell checking. Available
;; from Chocolately as choco install hunspell
;;
(use-package flyspell
  :defer t
  :bind
  (("" . ispell-word)
   ("C-S-" . flyspell-mode)
   ("C-M-" . flyspell-buffer)
   ("C-" . flyspell-check-previous-highlighted-word)
   ("M-" . flyspell-check-next-highlighted-word))
  :init
  (setenv "DICTPATH" "c:\\ProgramData\\chocolatey\\lib\\hunspell.portable\\tools\\share\\hunspell\\")
  (setenv "DICTIONARY" "c:\\ProgramData\\chocolatey\\lib\\hunspell.portable\\tools\\bin\\..\\share\\hunspell\\en_US")
  (setq ispell-program-name "c:\\ProgramData\\chocolatey\\bin\\hunspell.exe"
	;; Save dictionary in common location
	ispell-extra-args `("-p" ,(expand-file-name "hunspell" dropbox))
	;; Save dictionary without asking
	ispell-silently-savep t
	;; Do not issue warnings for all wrong words
	flyspell-issue-message-flag nil)

  (defun flyspell-check-next-highlighted-word ()
    "Custom function to spell check next highlighted word"
    (interactive)
    (flyspell-goto-next-error)
    (ispell-word)
    )

  :config
  (ispell-change-dictionary "en_US" t)

  ) ; use-package flyspell

;;; Encoding for everything
(prefer-coding-system 'utf-8-unix)

;;;
;;; Convenience keybindings for Greek letters
;;;
(progn
  (global-set-key (kbd "C-x C-g a") "α")
  (global-set-key (kbd "C-x C-g b") "β")
  (global-set-key (kbd "C-x C-g g") "γ")
  (global-set-key (kbd "C-x C-g d") "δ")
  (global-set-key (kbd "C-x C-g ep") "ε")
  (global-set-key (kbd "C-x C-g z") "ζ")
  (global-set-key (kbd "C-x C-g et") "η")
  (global-set-key (kbd "C-x C-g Th") "θ")
  (global-set-key (kbd "C-x C-g i") "ι")
  (global-set-key (kbd "C-x C-g í") "ί")
  (global-set-key (kbd "C-x C-g k") "κ")
  (global-set-key (kbd "C-x C-g l") "λ")
  (global-set-key (kbd "C-x C-g m") "μ")
  (global-set-key (kbd "C-x C-g n") "ν")
  (global-set-key (kbd "C-x C-g xi") "ξ")
  (global-set-key (kbd "C-x C-g o") "ο")
  (global-set-key (kbd "C-x C-g ó") "ό")
  (global-set-key (kbd "C-x C-g pi") "π")
  (global-set-key (kbd "C-x C-g r") "ρ")
  (global-set-key (kbd "C-x C-g fs") "ς")
  (global-set-key (kbd "C-x C-g s") "σ")
  (global-set-key (kbd "C-x C-g t") "τ")
  (global-set-key (kbd "C-x C-g y") "υ")
  (global-set-key (kbd "C-x C-g ý") "ύ")
  (global-set-key (kbd "C-x C-g ph") "φ")
  (global-set-key (kbd "C-x C-g chi") "φ")
  (global-set-key (kbd "C-x C-g ps") "ψ")
  (global-set-key (kbd "C-x C-g w") "ω"))


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Windows and unix-like shells
;;
(defun cygwin-shell ()
  "Run cygwin bash in shell mode."
  (interactive)
  (let ((explicit-shell-file-name "bash")
	(explicit-bash-args '("-i")))
    (call-interactively 'shell)))

(defun wunix-shell ()
  "Run Windows' bash in shell mode."
  (interactive)
  (let ((explicit-shell-file-name "cmd.exe")
	(explicit-cmd.exe-args '("/C" "c:\\Windows\\WinSxS\\amd64_microsoft-windows-lxss-bash_31bf3856ad364e35_10.0.16299.15_none_62878a822db68b25\\bash.exe" "-i")))
    (call-interactively 'shell)))

(defun visual-studio-2017-x84-shell ()
  "Run Windows shell with Visual Studio environment."
  (interactive)
  (let ((explicit-shell-file-name "cmd.exe")
	(explicit-cmd.exe-args '("/k" "C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\BuildTools\\VC\\Auxiliary\\Build\\vcvars64.bat")))
    (shell)))

(setq tramp-default-method "ssh")

(defun browser-sync ()
  "Starts a browser-sync script if it exists in this directory"
  (interactive)
  (start-process "browser-sync" "*browser-sync*" (expand-file-name "debug.cmd")))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Email, web and other services
;;

(use-package wunderlist
  :defer t
  :init
  (setq wl-folders-file "~/Dropbox/Library/dot.folders"

        ;; SMTP server for mail posting. Default: nil
        wl-smtp-posting-server "smtpin.xxxxxx.es"
        wl-smtp-posting-port 465
        wl-smtp-posting-user "xxxxx"
        wl-smtp-authenticate-type "plain"
        wl-smtp-connection-type 'ssl
        wl-from "xxxx@xxx.es"
        smtp-fqdn "xxx.xxxx.es"

        elmo-imap4-default-user "xxxxx"
        elmo-imap4-default-server "xxxx.xxx.es"
        elmo-imap4-default-port 993
        elmo-imap4-default-authenticate-type 'clear
        elmo-imap4-default-stream-type 'ssl

        ;; Location of archives
        elmo-archive-folder-path "~/Dropbox/Mail/"

        ;; Location of MH and Maildir folders
        elmo-localdir-folder-path "~/Dropbox/Mail/"
        elmo-maildir-folder-path "~/Dropbox/Mail/"

        wl-message-id-domain "xxxx@xxxx.es"
        wl-from "Juan Jose Garcia-Ripoll "

        wl-stay-folder-window t
        wl-folder-window-width 25
        wl-folder-use-frame nil

        wl-message-ignored-field-list '("^.*")
        wl-message-visible-field-list '("^From:" "^To:" "^Cc:" "^Date:" "^Subject:")
        wl-message-sort-field-list wl-message-visible-field-list
        wl-summary-default-sort-spec 'date
        wl-message-window-size '(1 . 3)
        )

  :config
  (if (boundp 'mail-user-agent)
      (setq mail-user-agent 'wl-user-agent))
  (if (fboundp 'define-mail-user-agent)
      (define-mail-user-agent
        'wl-user-agent
        'wl-user-agent-compose
        'wl-draft-send
        'wl-draft-kill
        'mail-send-hook))

  ) ; use-package wunderlist

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; Auto completion with IDO
;;;

(use-package ido
  :ensure t
  :init  (setq ido-enable-flex-matching t
               ido-ignore-extensions t
               ido-use-virtual-buffers t
               ido-everywhere t)
  :config
  (ido-mode 1)
  (ido-everywhere 1)
  (add-to-list 'completion-ignored-extensions ".pyc"))

(use-package flx-ido
   :ensure t
   :init (setq ido-enable-flex-matching t
               ido-use-faces nil)
   :config (flx-ido-mode 1))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; Version control
;;;

(use-package magit
  :defer t
  :ensure t
  )

Custom skin for Jupyter notebooks

posted Apr 4, 2018, 7:28 AM by Juan Jose Garcia-Ripoll   [ updated Apr 4, 2018, 7:29 AM ]

This is going to be a brief entry. Essentially: I have reading problems, which together with the strain induced by bad quality monitors (wish all were as good as the tiny screen of my Surface Pro), force me to look for ways to improve the quality of text I have to read on screen.

One problem for me as of late are Jupyter notebooks. Their default look in Windows involves narrow lines, bright background and sometimes too broad coding fonts, plus wasted space around prompts, line numbers, etc.

Fortunately, it seems you can customize the look of notebooks by touching one file. Follow these steps:
  1. Guess the location of Jupyter's configuration. For that open a command line and type jupyter --config-dir That should output a directory such as E:\home\JuanJose\.jupyter
  2. Create a file in that directory. More precisely, for my example, it will be E:\home\JuanJose\.jupyter\custom\custom.css
  3. Enter some CSS styles in that file. I copy my own below.
The effects should be visible once you reload the notebook, even if a session is open.

/*
 * Comment out the following line if you have installed
 * the following fonts:
 *   - Source Code Pro,
 *     https://fonts.google.com/?selection.family=Source+Code+Pro
 *   - Vollkorn,
 *     https://fonts.google.com/specimen/Vollkorn
 */
@import url('https://fonts.googleapis.com/css?family=Source+Code+Pro');
@import url('https://fonts.googleapis.com/css?family=Vollkorn');
#notebook, div#notebook {
    font-family: 'Vollkorn', 'DejaVu Serif', 'Palatino Linotype', serif;
    font-size: 16px;
    line-height: 1.5;
}
h1,h2,h3,h4,h5,h6 {
    font-family: 'Palatino Linotype';
}
body {
    font-family: 'Verdana', 'Arial', sans-serif;
}
body, #notebook-container, body > #header, .btn-default {
    background-color: rgb(252,251,251);
}
li, .MathJax_Display {
    margin-bottom: 6px;
}
code, kbd, pre, samp, .CodeMirror {
    font-family: Source Code Pro;
    font-size: 12px;
}
pre {
    font-size: 11px;
}
.prompt {
    font-family: Arial;
    font-size: 11px;
    min-width: 6em;
}
.input-prompt {
    color: rgb(108, 119, 188);
}
.output-prompt {
    color: rgb(205, 129, 106);
}

Some screenshots of my new skin, with Windows Edge and Google Chrome. Windows Edge tends to increase the perceived weight of characters, which is not too bad in my case.



Converting Mathematica to Python

posted Sep 19, 2017, 3:46 AM by Juan Jose Garcia-Ripoll   [ updated Sep 19, 2017, 3:51 AM ]

I like Mathematica for working with symbolic expressions. I find that there are no free software alternatives that achieve its power in the tasks of solving complex integrals, working with limits, and helping me make sense of sophisticated differential equations. Until that is solved, I will find the need to export Mathematica expressions to other software that is more efficient on the numerical computing side. Or which simply does good plots in a less verbose way.

I have created a simple Mathematica notebook which you can download here (see attachment below). The notebook uses Mathematica's pattern matching to build a sophisticated parsing routine that converts Mathematica expressions to Python code.

See for instance the following examples which detect addition, division and exponentiation, and convert those to strings, either directly, or using wrappers that automate the case of binary operators and unitary expressions:

ToPython2[a_ - b_] := ToPythonOp["-", a, b]
ToPython2[a_/b_] := ToPythonOp["/", a, b]
ToPython2[a_^2] := "(" <> PythonWrap[a] <> "**2)"
ToPython2[a_^b_] := PythonWrap[ToPythonOp["**", a, b]]

The actual implementation makes use of ugly hacks, such as defining its own square root function (which accepts negative real arguments), or parenthesising more than needed, but it is fully functional and works nicely for my own purposes. For instance, the following complex Mathematica expression


is converted to 13 lines of Python code:

    π=math.pi;

    def mysqrt(x): return np.sqrt((1.+0j)*x)

    y=π*γ;
    x=mysqrt((-1.+(y**2)));
    z=1.+((2.*(x*y))+(-2.*(y**2)));
    w=-1.+((2.*(x*y))+(2.*(y**2)));
    u=mysqrt((z/w));
    aux0=((Δ**5.)*(np.log(((z**-0.5)/Ω))))+((x-y)*(y*((mysqrt(z))*((Ω**5.)*(np.log((w*(Ω**2))))))));
    aux1=((g**2)*(((2.*(π*(u*(Δ**5.))))+(w*(Ω**3.)))*(np.log(((-z**-0.5)/Ω)))))+(2.*aux0);
    aux2=(((Δ**4.)+((2.*((-1.+(2.*(y**2)))*((Δ**2)*(Ω**2))))+(Ω**4.)))**-2.)*aux1;
    output=np.exp((((-((-1.+(y**2))**-0.5)*((z**-0.5)*(Ω*aux2))))/π));

Ussing Cygwin's ssh-agent in Windows

posted Sep 29, 2016, 6:45 AM by Juan Jose Garcia-Ripoll   [ updated Oct 9, 2016, 3:40 AM ]

When you want to log in to a Unix machine or some other kind of server, one of the favourite protocols is SSH. In public key authentication, you can generate a pair of keys, a private one called id_rsa and a public one id_rsa.pub These keys are usually stored in your HOME directory, with sufficient permissions so that no one can read the private key.

Unfortunately this may not always be the case. I have had machines that have been hacked, or I sometimes like to store my private-public key pairs in some backup device which may be in a not too secure location (for instance Dropbox). Under those circumstances it is useful to store the private key with a password, the so called "passphrase". Then, whenever you want to log-in to another machine using SSH you only have to type the passphrase.

ssh-agent is a program that stores your public and private keys in memories. Using it, you only have to introduce the passphrase once at the beginning of your session. It is convenient, it works under Linux but Windows does not have it built in.

Step 1: install ssh and ssh-agent

Your first stop for these programs in Windows will be cygwin. Get it from https://www.cygwin.com/ I use the 64-bit version, which works fine enough. After installation I recommend the following two changes:
  1. Edit the PATH environment variable to include the directory c:/cygwin64 or c:/cygwin depending on the version you chose.
  2. Create a HOME environment variable pointing to your favorite directory. I normally rely on the Windows one %HOMEDRIVE%%HOMEPATH%
Both changes together have the nice side effect that you can use SSH from anywhere, including Windows terminals.

Step 2: Create your public and private keys

Open a terminal and type
ssh-keygen -t rsa
and enter a long password as your passphrase. The longer the better. I usually rely on stupid sentences, like "the cow jumped over the 27th wall street", which have no meaning but have enough entropy and are easy to remember.

Step 3: Ensure ssh-agent is run when you log in

Press Windows key + R (both keys simultaneously) and enter shell:startup This will open a folder where you can create programs that run at boot time. Click with the right button and create a new "Text document". Enter the following text and save it with the name "run-ssh-agent.cmd" Note the different extension!
@echo off
rem Start ssh-agent in the background. It will not be closed
start /b ssh-agent > %HOME%\.ssh-agent-env
echo We need to ask you the password with which the SSH key is encoded
echo After that we will update the environment variable so that you
echo do not need to enter it again
pause
bash -c ". ~/.ssh-agent-env; setx SSH_AUTH_SOCK "$SSH_AUTH_SOCK"; setx SSH_AGENT_PID "$SSH_AGENT_PID"; ssh-add"

This step is quite critical because it is on charge of starting the agent that keeps the passwords in the background, decoding the SSH keys with the password you provide and notifying the whole of the system (via the environment variables SSH_AUTH_SOCK and SSH_AGENT_PID) that the SSH keys are available for use.

Step 4: Configure cygwin to use ssh-agent

Edit your profile file ~/.bash_profile and add the following lines at the end
SSHAGENT=/usr/bin/ssh-agent
SSHAGENTARGS="-s"
if [ -z "$SSH_AUTH_SOCK" -a -x "$SSHAGENT" ]; then
    if test -f ~/.ssh-agent-env; then
eval `cat ~/.ssh-agent-env`
    else
eval `$SSHAGENT $SSHAGENTARGS`
trap "kill $SSH_AGENT_PID" 0
    fi
fi

Step 5: Optional - Configure Emacs to use cygwin SSH

This is tricky. The problem is that Cygwin's SSH expect to be run from a Cygwin or MS-DOS terminal and does not like to run from within Emacs. There is a simple solution for this, which involves installing a package called fakecygpty from github (https://github.com/d5884/fakecygpty)

Step 6: Optional - Configure Emacs to use ssh-agent

I added the following function to my .emacs file (see here for configuration details) to read the location of ssh-agent and pass it to other functions, such as ssh or tramp.
(let ((filePath "~/.ssh-agent-env"))
  (with-temp-buffer
    (insert-file-contents filePath)
    (let (ssh-auth-sock ssh-agent-pid)
      (and (progn
    (goto-char (point-min))
    (re-search-forward "SSH_AUTH_SOCK=\\([^;]*\\)" nil t))
  (setq ssh-auth-sock (match-string 1))
  (progn
    (goto-char (point-min))
    (re-search-forward "SSH_AGENT_PID=\\([^;]*\\)" nil t))
  (setq ssh-agent-pid (match-string 1))
  (setenv "SSH_AUTH_SOCK" ssh-auth-sock)
  (setenv "SSH_AGENT_PID" ssh-agent-pid)))))

1-10 of 30