Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/sagemath/sage-shell-mode

Emacs front end for SageMath
https://github.com/sagemath/sage-shell-mode

Last synced: about 1 month ago
JSON representation

Emacs front end for SageMath

Awesome Lists containing this project

README

        

* sage-shell-mode
[[http://melpa.org/#/sage-shell-mode][file:http://melpa.org/packages/sage-shell-mode-badge.svg]]
[[http://stable.melpa.org/#/sage-shell-mode][file:http://stable.melpa.org/packages/sage-shell-mode-badge.svg]]
[[https://travis-ci.org/sagemath/sage-shell-mode][https://travis-ci.org/sagemath/sage-shell-mode.svg]]

** Overview

=sage-shell-mode= is an elisp package and provides an Emacs front
end for [[http://www.sagemath.org/][Sage]].

=sage-shell-mode= provides two main features:

1. =sage-shell-mode= to run the Sage terminal inside Emacs.

1. =sage-shell:sage-mode= for editing =.sage= source files and sending their contents directly to the Sage terminal. This major mode is derived from =python-mode=.

The package supports extensions with [[https://github.com/stakemori/auto-complete-sage][auto-complete-sage]], [[https://github.com/stakemori/helm-sage][helm-sage]],
[[https://github.com/stakemori/anything-sage][anything-sage]] and [[https://github.com/stakemori/ob-sagemath][ob-sagemath]].

** Table of Contents :TOC:
- [[#requirements][Requirements]]
- [[#installation-and-setup][Installation and Setup]]
- [[#aliases][Aliases]]
- [[#basic-usage][Basic Usage]]
- [[#input-history][Input History]]
- [[#emulating-worksheets-code-blocks][Emulating Worksheets: code blocks]]
- [[#inline-display-of-latex-outputs-and-plots-a-port-of-sage-view][Inline display of LaTeX outputs and plots (a port of =sage-view=)]]
- [[#sagetex][SageTeX]]
- [[#customization][Customization]]
- [[#extensions][Extensions]]
- [[#screenshots][Screenshots]]
- [[#workaround-for-flycheck][Workaround for Flycheck]]
- [[#license][License]]

** Requirements

1. GNU Emacs 24.4 later.

2. Local installation of [[http://www.sagemath.org/][Sage]].

3. The Emacs package [[https://github.com/kiwanami/emacs-deferred][deferred]]. This will be installed automatically if you follow the guide below.

** Installation and Setup

The most convenient is to use the Emacs package manager to install =sage-shell-mode= from [[https://github.com/milkypostman/melpa.git][MELPA]]:

1. See http://melpa.org/#/getting-started if you do not have a
configuration for MELPA.

2. Install =sage-shell-mode= by
- =M-x package-refresh-contents=
- =M-x package-install RET sage-shell-mode=.

(Alternatively, you can use [[https://github.com/jwiegley/use-package][=use-package=]] (available from MELPA) for installation and setup).

# 3. If you have Sage 7.3 or earlier, then add the following to your Emacs init file (=~/.emacs.d/init.el=):

# #+BEGIN_SRC elisp
# (setq sage-shell:use-prompt-toolkit nil)
# #+END_SRC

3. Configuration.

Sage internally uses ~IPython~ to manage user interaction at the console, ~Jupyter~ cell or, in our case, ~emacs~ buffer. Due to IPython evolution (governed /upstream/ Sage), this interaction has to be managed differently by =sage-shell-mode= according to the ~Ipython~ version available, itself depending on Sage's version. This configuration is governed by two customizable variables :

* =sage-shell:use-prompt-toolkit=, which must be =t= for Sage versions >= 7.4.beta0 and < 9.2.beta8, and =nil= otherwise ;

* =sage-shell:use-simple-prompt=, which must be =t= for Sage versions >= 9.2.beta9, and =nil= otherwise.

(Note : Sage 9.2.beta8 is a lost cause ; complain fiercely, then upgrade to something newer...).

The function =sage-shell:set-ipython-version= does this automatically, but takes a couple of seconds to execute at startup. Its use is controlled by the custom variable =sage-shell:set-ipython-version-on-startup= (=t= by default).

Similarly, =sage-shell:check-ipython-version= (controlled by the custom variable =sage-shell:check-ipython-version-on-startup=) checks that your options are acceptable to your IPython version, and will yell at you (in the =*Messages*= buffer) if this is not the case.

By default, both functions are enabled at startup. A (reckless) user of (say) Sage 9.2.beta10 could shave a couple seconds startup time by setting :

#+begin_src elisp
(custom-set-variables
'(sage-shell:use-prompt-toolkit nil)
'(sage-shell:use-simple-prompt t)
'(sage-shell:set-ipython-version-on-startup nil)
'(sage-shell:check-ipython-version-on-startup nil))
#+end_src

in its =init= file (or in the =:init= clause of its =sage-shell-mode= =use-package= call).

6. You can now run Sage inside Emacs by =M-x sage-shell:run-sage=, if Emacs
can find the executable file of Sage.

If Emacs cannot find the executable file, add the following line to your Emacs init file:

#+BEGIN_SRC elisp
(setq sage-shell:sage-root "/path/to/sage/root_directory")
#+END_SRC

And replace =/path/to/sage_root_directory= by the root directory of
Sage, i.e. =$SAGE_ROOT=. If you do not know the root directory of
Sage, evaluate the following code in a Sage terminal:

#+BEGIN_SRC python
import os; print os.environ["SAGE_ROOT"]
#+END_SRC

Alternatively, instead of setting =sage-shell:sage-root=, you may
set the variable =sage-shell:sage-executable=.

#+BEGIN_SRC elisp
(setq sage-shell:sage-executable "/path/to/sage/executable")
#+END_SRC

Here =/path/to/sage/executable= is the path of the executable file
of Sage. This may be a symbolic link.

*** Sample configuration
Here is a sample configuration.

#+BEGIN_SRC elisp
;; Run SageMath by M-x run-sage instead of M-x sage-shell:run-sage
(sage-shell:define-alias)

;; Turn on eldoc-mode in Sage terminal and in Sage source files
(add-hook 'sage-shell-mode-hook #'eldoc-mode)
(add-hook 'sage-shell:sage-mode-hook #'eldoc-mode)
#+END_SRC

** Aliases

The official Emacs major mode for Sage used to be [[https://bitbucket.org/gvol/sage-mode/src][sage-mode]]. To avoid name conflicts
with this package, =sage-shell-mode= prefixes all names with =sage-shell=.

If you are not using =sage-mode= at all, you can define more convenient
aliases for =sage-shell-mode= by adding the following to your Emacs init file
after the line =(require 'sage-shell-mode)=:

#+BEGIN_SRC elisp
(sage-shell:define-alias)
#+END_SRC

The following aliases will be defined:

| Original name | Alias |
|---------------------------+----------------|
| sage-shell:run-sage | run-sage |
| sage-shell:run-new-sage | run-new-sage |
| sage-shell:sage-mode | sage-mode |
| sage-shell:sage-mode-map | sage-mode-map |
| sage-shell:sage-mode-hook | sage-mode-hook |

This means e.g. that you can do =M-x run-sage= to run Sage, instead of =M-x
sage-shell:run-sage=.

** Basic Usage

*** Running a Sage Process

You can start a Sage process by =M-x sage-shell:run-sage=. If you need
to open multiple Sage processes simultaneously, you can start new ones by
=M-x sage-shell:run-new-sage=. You can restart the current process by
=M-x sage-shell:restart-sage=.

| Command | Alias | Description |
|-------------------------+--------------+-----------------------------------|
| sage-shell:run-sage | run-sage | Run a Sage process. |
| sage-shell:run-new-sage | run-new-sage | Run another Sage process. |
| sage-shell:restart-sage | None | Restart the current Sage process. |

The major-mode of the Sage process buffer is =sage-shell-mode=.

*** The Sage Process as a terminal

The primary element of =sage-shell-mode= is interacting with the Sage process
you just started. The Sage process buffer communicates directly with a Sage
shell in the background and behaves very much like it. You just type and send
the command with ==:

#+BEGIN_SRC python
sage: 2+2
4
sage: (x^2 + 2*x + 1).factor()
(x + 1)^2
sage:
#+END_SRC

The buffer behaves like an Emacs shell:

- =M-p= or =C-up= goes through earlier input.
- Previous input and output is retained earlier in the buffer. You can move
around just as usual and e.g. copy from it or search.
- To exit, you can enter =quit= at the prompt or type =C-d= (bound to =sage-shell:delchar-or-maybe-eof=) at a blank line.

The buffer also behaves much like the Sage terminal:

**** Tab completion
== at the prompt completes the current word. It understands all Sage and
Python functions currently in scope, and it also completes attributes of
objects. If there are multiple possibilities, they are presented in another
window.

#+BEGIN_SRC python
sage: G = graphs.PetersenGraph()
sage: G.

sage: G.charp

#+END_SRC

By default, Tab completion uses =completion-at-point=. Alternatively, you can
use =pcomplete= by adding the following to your Emacs init file:

#+BEGIN_SRC elisp
(setq sage-shell:completion-function 'pcomplete)
#+END_SRC

You can also use =auto-complete=, =anything= or =helm= for
completion. This requires installing those extensions, see [[#extensions][Extensions]].

**** =?= Help

By writing the name of an object at the prompt, followed by =?= and then =RET=,
you are shown the documentation of that object:

#+BEGIN_SRC python
sage: G = graphs.PetersenGraph()
sage: G.charpoly?

#+END_SRC

This is identical to running =C-c C-h= and then typing the name of the object.

**** =??= Source Lookup

If you use =??= instead =?= after a Sage object, then the *source code* for that object will be opened in a new buffer:

#+BEGIN_SRC python
sage: G = graphs.PetersenGraph()
sage: G.charpoly??

#+END_SRC

**** Most important key-bindings

| Key Stroke | Command | Description |
|------------+----------------------------------------------+----------------------------------------------------------------------------|
| RET | sage-shell:send-input | Evaluate the expression written at the prompt. |
| TAB | sage-shell-tab-command | Complete a partially written word or indent a line. |
| C-d | sage-shell:delchar-or-maybe-eof | Delete the next input character. End the Sage process if nothing is input. |
| C-c C-c | sage-shell:interrupt-subjob | Interrupt the current computation. |
| M-p | comint-previous-input | Go backward through input history. |
| M-n | sage-shell:next-input | Go forward through input history. |
| C-c C-o | sage-shell:delete-output | Remove all output from Sage since last input prompt. |
| C-c M-o | sage-shell:clear-current-buffer | Clear the entire Sage process buffer, leaving just the prompt. |
| C-c C-l | sage-shell:load-file | Asks for a file and loads it into Sage |
| C-c C-h | sage-shell:help | Ask for the name of a Sage object and show its documentation. |
| ? RET | sage-shell-help::describe-symbol | Show the documentation of the object directly preceding the =?=. |
| ?? RET | sage-shell:find-source-in-view-mode | Visits the source code of the object directly preceding the =??=. |
| C-c o | sage-shell:list-outputs | List inputs and outputs in a buffer. |
| C-c M-w | sage-shell:copy-previous-output-to-kill-ring | Copy the previous output to =kill-ring= |
For more commands and key-bindings see the help using =M-x describe-mode
sage-shell-mode=.

*** Editing a Sage File

When you visit a file with the suffix =.sage=, then
=sage-shell:sage-mode= will be the major-mode of the buffer
automatically.

To switch to =sage-shell:sage-mode= on a =.py= file, run =M-x
sage-shell:sage-mode=. To use =sage-shell:sage-mode= every time you visit
that file, you can add the following magic comment at the first line of the
file:

#+BEGIN_SRC python
# -*- mode: sage-shell:sage -*-
#+END_SRC

If you've activated [[#aliases][Aliases]] you can instead use the following magic comment:

#+BEGIN_SRC python
# -*- mode: sage -*-
#+END_SRC

The major mode =sage-shell:sage-mode= is almost the same as
=python-mode=. The following new key-bindings are added:

| Key | Command | Description |
|---------+---------------------------------------+------------------------------------------------------------------|
| C-c C-c | sage-shell-edit:send-buffer | Evaluate the contents of the current buffer in the Sage process. |
| C-c C-r | sage-shell-edit:send-region | Evaluate the currently marked region in the Sage process. |
| C-c C-j | sage-shell-edit:send-line* | Evaluate the current line in the Sage process. |
| C-c C-l | sage-shell-edit:load-file | Load the current file in the Sage process. |
| C-c C-z | sage-shell-edit:pop-to-process-buffer | Select the Sage process buffer. |

If you run multiple Sage processes, use =M-x sage-shell:set-process-buffer=
to change which one will be used for the above functions.

** Input History

To save the history of input evaluated in a Sage process and use in future
Sage process (using the =M-p= keybinding), add the following to your Emacs
init file:

#+BEGIN_SRC elisp
(setq sage-shell:input-history-cache-file "~/.emacs.d/.sage_shell_input_history")
#+END_SRC

The file name in the above line is the path for storing the inputs and you can
change it to what you prefer.

** Emulating Worksheets: code blocks

Worksheets is a popular paradigm for structuring experiments in computer algebra systems, seen in Jupyter, the Sage Notebook, Maple and many other softwares.
=sage-shell-mode= supports a lightweight type of this workflow using "code blocks".

Essentially, you structure your source file in logical blocks of code, representing both your library code and your experiments.
For instance:

#+BEGIN_SRC python
### Implement the new algorithm
def my_helper(a):
return a*2

def my_new_algorithm(x, y):
return my_helper(x) + my_helper(y)

### Check the new algorithm on small input
print my_new_algorithm(1, 2)

### Check the new algorithm on big input
print my_new_algorithm(100, 300)

### Check that my algorithm is commutative using random input
def my_random_number():
return randint(100, 200)

a, b = my_random_number(), my_random_number()
assert my_new_algorithm(a, b) == my_new_algorithm(b, a)

#+END_SRC

The blocks of code are logically delimited by lines starting with =###=.
In this case =load(experiment.sage)= is not a good alternative to the way one works with the Jupyter Notebook: rather, you want to evaluate the code block by block.
You also want to be able to modifying an earlier or later block, run that, and then return to the block in the middle, etc.

=sage-shell-mode= comes with a small set of functions for accommodating this. In =sage-shell:sage-mode=, the following functions are provided:

| Key | Command | Description |
|------------+--------------------------------+-------------------------------------------------------------------|
| C-M-{ | sage-shell-blocks:backward | Move backward one block, i.e. to previous =###= delimiter. |
| C-M-} | sage-shell-blocks:forward | Move forward one block, i.e. to next =###= delimiter. |
| C- | sage-shell-blocks:send-current | Send the block that the point is currently in to the Sage process |

In the Sage process buffer, the following functions are provided:

| Key | Command | Description |
|------------+-----------------------------+------------------------------------------------------------|
| C- | sage-shell-blocks:pull-next | Take the block from the last visited =sage-shell:sage-mode= buffer and send to the Sage process. |

As an example, if the point is in the body of =my_new_algorithm=, then =C-= (or =M-x sage-shell-blocks:send-current=) would send the definitions of =my_helper= and =my_new_algorithm= to the Sage shell. Furthermore, it would print the "title" of the block:

#+BEGIN_SRC python
sage: load('/tmp/sage_shell_mode3946wC1/sage_shell_mode_temp.sage')
--- Implement the new algorithm ---
sage:
#+END_SRC

The delimiter =###= can be changed by =setq= the variable =sage-shell-blocs:delimiter=.

** Inline display of LaTeX outputs and plots (a port of =sage-view=)
This feature is a port of [[https://bitbucket.org/gvol/sage-mode/src/44eb95cb6b27a05af68e11253649ddf8ae364a5b/emacs/sage-view.el?at=default&fileviewer=file-view-default][sage-view]]. This requires [[https://www.ctan.org/pkg/dvipng][dvipng]] and [[https://www.ctan.org/tex-archive/macros/latex/contrib/preview][preview.sty]].

To enable inline display of LaTeX outputs and plots in =sage-shell-mode= buffer,
add the following code to your Emacs init file:

#+BEGIN_SRC elisp
;; If you want to enable inline display of LaTeX outputs only,
;; uncomment the following line.
;; (setq sage-shell-view-default-commands 'output)

;; If you want to enable inline display of plots only,
;; uncomment the following line.
;; (setq sage-shell-view-default-commands 'plot)

(add-hook 'sage-shell-after-prompt-hook #'sage-shell-view-mode)
#+END_SRC

You can use the following commands to enable/disable inline display of LaTeX outputs and plots.

| Command | Description |
|-----------------------------------------+---------------------------------------------------------------------------|
| =sage-shell-view-toggle-inline-output= | Toggle inline display of LaTeX outputs while SageMath process is running. |
| =sage-shell-view-toggle-inline-plots= | Toggle inline display of plots while SageMath process is running. |

The following table shows some of customizable variables for =sage-shell-view-mode=.
For further customization, run =M-x customize-group RET sage-shell-view RET=.

| Customizable variable | Description | Default value |
| =sage-shell-view-default-commands= | If equal to the symbol =plots= then will start inline plotting. If equal to the symbol =output= then will start typesetting output. Otherwise, if non-nil will start both. | =t= |
| =sage-shell-view-default-resolution= | Resolution used when converting from PDF to PNG. This value is passed to the =-r= option of the ghostscript command. | 125 |
| =sage-shell-view-latex-foreground-color= | Foreground color used in LaTeX image as string (e.g. "black", "white", "#de935f") or =nil=. If it is =nil=, then the default foreground color will be used. | =nil= |
| =sage-shell-view-latex-background-color= | Similar to =sage-shell-view-latex-foreground-color= for background color. | =nil= |

** SageTeX

=sage-shell-mode= can be conveniently used when writing Sage-powered LaTeX files
using [[https://github.com/dandrake/sagetex][SageTeX]].

*** TEXINPUTS

When a Sage process is spawned by =sage-shell:run-sage= or
=sage-shell:run-new-sage=, then =sage-shell-mode= adds
=$SAGE_ROOT/local/share/texmf/tex/generic/sagetex/= to the
environment variable =TEXINPUTS= in Emacs. If you do not want to
change the environment variable, set
=sage-shell-sagetex:add-to-texinputs-p= to =nil=.

*** Commands for SageTeX

Here is a list of commands for =SageTeX=. These commands load a
=.sagetex.sage= file generated by =SageTeX= to the existing Sage
process.

| Command | Run =latex= before loading | Run =latex= after loading |
|--------------------------------------------+----------------------------+---------------------------|
| sage-shell-sagetex:load-file | No | No |
| sage-shell-sagetex:run-latex-and-load-file | Yes | No |
| sage-shell-sagetex:compile-file | Yes | Yes |

There are similar commands to above,
=sage-shell-sagetex:load-current-file=,
=sage-shell-sagetex:run-latex-and-load-current-file= and
=sage-shell-sagetex:compile-current-file=.

Here is a sample setting for =AUCTeX= users.

#+BEGIN_SRC elisp
(eval-after-load "latex"
'(mapc (lambda (key-cmd) (define-key LaTeX-mode-map (car key-cmd) (cdr key-cmd)))
`((,(kbd "C-c s c") . sage-shell-sagetex:compile-current-file)
(,(kbd "C-c s C") . sage-shell-sagetex:compile-file)
(,(kbd "C-c s r") . sage-shell-sagetex:run-latex-and-load-current-file)
(,(kbd "C-c s R") . sage-shell-sagetex:run-latex-and-load-file)
(,(kbd "C-c s l") . sage-shell-sagetex:load-current-file)
(,(kbd "C-c s L") . sage-shell-sagetex:load-file)
(,(kbd "C-c C-z") . sage-shell-edit:pop-to-process-buffer))))
#+END_SRC

For example, you can run =sage-shell-sagetex:compile-current-file=
by =C-c s c= in a =LaTeX-mode= buffer with this setting.

*** Customize the =latex= Command

You can change a =latex= command used by
=sage-shell-sagetex:compile-file= and
=sage-shell-sagetex:compile-current-file= by setting
=sage-shell-sagetex:latex-command= or
=sage-shell-sagetex:auctex-command-name=.

If you are an =AUCTeX= user, then customize
=sage-shell-sagetex:auctex-command-name= to change the =latex=
command. The value of =sage-shell-sagetex:auctex-command-name=
should be a =name= of a command in =TeX-command-list= (i.e =car= of
an element of the list =TeX-command-list=), e.g.:

#+BEGIN_SRC elisp
(setq sage-shell-sagetex:auctex-command-name "LaTeX")
#+END_SRC

You can also use the variable =sage-shell-sagetex:latex-command= to
change the =latex= command. For example, if you want to run
=latexmk= after loading a =.sagetex.sage= file, then use the
following setting:

#+BEGIN_SRC elisp
(setq sage-shell-sagetex:latex-command "latexmk")
#+END_SRC

The default value of =sage-shell-sagetex:latex-command= is =latex
-interaction=nonstopmode=. If
=sage-shell-sagetex:auctex-command-name= is =non-nil=, then the
value of =sage-shell-sagetex:latex-command= is ignored.

** Customization

To customize =sage-shell-mode=, =M-x customize-group RET sage-shell=,
=M-x customize-group RET sage-shell-sagetex= or
=M-x customize-group RET sage-shell-view=.

** Extensions

- [[https://github.com/stakemori/auto-complete-sage][auto-complete-sage]] provides an [[https://github.com/auto-complete/auto-complete][auto-complete]] source for
=sage-shell-mode=.
- [[https://github.com/stakemori/helm-sage][helm-sage]] provides a [[https://github.com/emacs-helm/helm][helm]] source for =sage-shell-mode=.

- [[https://github.com/stakemori/anything-sage][anything-sage]] provides an [[http://www.emacswiki.org/Anything][anything]] source for =sage-shell-mode=.

- [[https://github.com/stakemori/ob-sagemath][ob-sagemath]] provides [[http://orgmode.org/worg/org-contrib/babel/][org-babel]] functions for Sage.

** Screenshots

Automatic indentation and syntax highlighting work.

#+CAPTION: alt text

[[./images/indent.png]]

Completion with [[https://github.com/stakemori/auto-complete-sage][auto-complete-sage]].

#+CAPTION: alt text

[[./images/ac.png]]

Completion with [[https://github.com/stakemori/helm-sage][helm-sage]].

#+CAPTION: alt text

[[./images/helm.png]]
#+CAPTION: alt text

[[./images/helm1.png]]

** Workaround for Flycheck

To use =flycheck-mode= in a =sage-shell:sage-mode= buffer and a
=python-mode= buffer, try the following code.

#+BEGIN_SRC elisp

(dolist (ckr '(python-pylint python-flake8))
(flycheck-add-mode ckr 'sage-shell:sage-mode))

(defun sage-shell:flycheck-turn-on ()
"Enable flycheck-mode only in a file ended with py."
(when (let ((bfn (buffer-file-name)))
(and bfn (string-match (rx ".py" eol) bfn)))
(flycheck-mode 1)))

(add-hook 'python-mode-hook 'sage-shell:flycheck-turn-on)
#+END_SRC

** License

Licensed under the [[http://www.gnu.org/licenses/gpl.html][GPL]].