Sage Development with Emacs

A while back I described my (then current) setup to develop C code with Emacs. The other programming language I tend to spend a lot of time with is Python, specifically Sage’s Python. Here’s my Emacs setup for writing Sage code. For starters, it makes sense to highlight indentation in Python.

(use-package highlight-indentation
   :ensure t)

I use anaconda-mode for auto-completion and stuff, it runs jedi for me. In particular it offers:

  • M-. Goto definition for thing at point.
  • M-, Switch to buffer of most recent marker.
  • M-? Show documentation for context at point.
  • M-r Show usage for thing at point.
(use-package anaconda-mode
  :ensure t
  :diminish anaconda-mode
  :config (bind-key "M-," #'anaconda-nav-pop-marker anaconda-mode-map))

I use sage-shell-mode for running Sage from within Emacs. It’s available on MELPA and hence easier to keep around than sage-mode when we switch Sage installs all the time. Unfortuantely, company-mode didn’t immediately work for sage-shell-mode for me, so I reverted to auto-complete-sage for now. I should look into that.

(use-package sage-shell-mode
  :ensure t
  :init (progn
          (use-package auto-complete
            :init (progn
                    (setq ac-delay 0.3
                          ac-auto-start 2)))

          (eval-after-load "auto-complete"
            '(setq ac-modes (append '(sage-shell-mode sage-shell:sage-mode) ac-modes)))
          (add-hook 'sage-shell:sage-mode-hook 'ac-sage-setup)
          (add-hook 'sage-shell:sage-mode-hook 'auto-complete-mode)
          (add-hook 'sage-shell-mode-hook 'ac-sage-setup)
          (add-hook 'sage-shell-mode-hook 'auto-complete-mode)
          (setq ac-sage-show-quick-help t)))

My files tend to be .py files not .sage, so to edit a file in sage-shell-mode I could put # -*- mode: sage-shell:sage -*- on top. However, I usually don’t do that but use python-mode directly. For writing Sage code in python-mode my setup is as follows.

In .emacs.d I created a directory sage-python/bin which contains a file called python with the following content:

sage -python "$@"

Then, in each project where we want to use Sage, we can add a .dir-locals.el file at the top level with

;;; Directory Local Variables
;;; See Info node `(emacs) Directory Variables' for more information.
((python-mode . ((python-shell-virtualenv-path . "$HOME/.emacs.d/sage-devel-python/"))))

which will tell anaconda-mode to run Sage’s python process instead of the system-wide one. That is, anaconda-mode looks at python-shell-virtualenv-path to determine which Python to run. This way, auto completion and symbol lookup for Sage objects and functions works just as with any other Python code base.

Finally, some pretty generic Python setup stuff.

I use the “onetwo” style to fill docstrings in Python, i.e.:

"""Process foo, return bar."""

Process foo, return bar.

If processing fails throw ProcessingError.


and I use ipython if availabe.

(use-package python-mode
  :init (progn
          (add-hook 'python-mode-hook 'highlight-indentation-mode)
          (add-hook 'python-mode-hook 'anaconda-mode)
          (add-hook 'python-mode-hook 'eldoc-mode)
          (add-hook 'python-mode-hook 'sphinx-doc-mode))

  :config (progn
            (setq-default python-indent 4)
            (setq python-fill-docstring-style 'onetwo)

            (when (executable-find "ipython")
               python-shell-interpreter "ipython"
               python-shell-interpreter-args ""
               python-shell-prompt-regexp "In \\[[0-9]+\\]: "
               python-shell-prompt-output-regexp "Out\\[[0-9]+\\]: "
               "from IPython.core.completerlib import module_completion"

I use mmm-mode to get ReST mode in triple-quoted docstrings and Python everywhere else. It works reasonably well (but is not perfect).

(use-package mmm-mode
  :ensure t
  :init (progn
          (setq mmm-global-mode 'maybe))
  :config  (progn (mmm-add-classes
                      :submode rst-mode
                      :front "^ *[ru]?\"\"\"[^\"]*$"
                      :back "^ *\"\"\""
                      :include-front t
                      :include-back t
                      :end-not-begin t)))
                  (mmm-add-mode-ext-class 'python-mode nil 'python-rst)
                  (mmm-add-mode-ext-class 'sage-shell:sage-mode nil 'python-rst)

Finally, I use cython-mode for Cython.

(use-package cython-mode
  :ensure t
  :mode (("\\.pyx\\'"  . cython-mode)
         ("\\.spyx\\'" . cython-mode)
         ("\\.pxd\\'"  . cython-mode)
         ("\\.pxi\\'"  . cython-mode)))

PS: Here’s my current company-mode config which uses the following extensions:

(use-package company-anaconda
  :ensure t)

(use-package company-math
  :ensure t)

(use-package company-auctex
  :ensure t)

(use-package company
  :ensure t
  :diminish company-mode
  :init (progn
          (setq company-minimum-prefix-length 2)
          (setq company-global-modes '(not sage-shell:sage-mode sage-shell-mode))
          (bind-key "C-<tab>" #'company-complete)          
          (global-company-mode 1)
  :config (progn
            (setq company-tooltip-limit 20) ; bigger popup window
            (setq company-idle-delay 0.5)    ; decrease delay before autocompletion popup shows
            (setq company-echo-delay 0)     ; remove annoying blinking
            (setq company-show-numbers t)   ; show numbers for easy selection

            (add-to-list 'company-backends #'company-c-headers)
            (add-to-list 'company-backends #'company-anaconda)
            (add-to-list 'company-backends #'company-math-symbols-unicode)

            (defun malb/ede-object-system-include-path ()
              "Return the system include path for the current buffer."
              (when ede-object
                (ede-system-include-path ede-object)))

            (setq company-c-headers-path-system #'malb/ede-object-system-include-path)

            (bind-key "C-n" #'company-select-next company-active-map)
            (bind-key "C-p" #'company-select-previous company-active-map)
            (bind-key "<tab>" #'company-complete company-active-map)
            (bind-key "M-?" #'company-show-doc-buffer company-active-map)
            (bind-key "M-." #'company-show-location company-active-map)

Use company-quickhelp to display … quick help.

(use-package company-quickhelp
  :ensure t
  :init (company-quickhelp-mode 1))

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s