sagemath / sage-shell-mode

Emacs front end for SageMath
GNU General Public License v3.0
98 stars 16 forks source link

** 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.

  2. =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

  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

    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).

  4. 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:

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]].