unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
blob 730725924a34832981d3e822f812b6c9b4cc95f0 8990 bytes (raw)
name: lisp/progmodes/sage.el 	 # note: path name is non-authoritative(*)

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
 
;;; sage.el --- SageMath support for Emacs -*- lexical-binding: t; -*-

;; Copyright (C) 2024 Free Software Foundation, Inc.

;; Author: Rahguzar <rahguzar@zohomail.eu>
;; Maintainer: Rahguzar <rahguzar@zohomail.eu>
;; Keywords: languages

;; This file is part of GNU Emacs.

;; GNU Emacs is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.

;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.

;;; Commentary:

;; Major modes for editing Sage files and interacting with sage process.  These
;; are thin wrappers around `python-mode' and `inferior-python-mode'.  To
;; change the behavior of these modes, the facilities provided by `python.el'
;; can be used in conjunction with `sage-mode-hook' and `sage-shell-mode-hook'.

;;; Code:
(require 'python)

;;;; Variables
(defgroup sage nil
  "Emacs interface to sagemath."
  :group 'languages)

(defcustom sage-shell-interpreter (executable-find "sage")
  "Interpreter for sage code.
Sage version should be at least 9.3."
  :type 'string)

(defcustom sage-shell-interpreter-args '("--simple-prompt")
  "Arguments for the sage interpreter.
Note: \"--simple-promot\" is required for ipython to work."
  :type '(repeat string))

(defvar sage-shell-eval-setup-code
  "\
import warnings

def __PYTHON_EL_eval(source, filename):
    import ast, sys
    from sage.repl.preparse import preparse_file
    if isinstance(source, str):
        source = preparse_file(source)
    else:
        source = preparse_file(source.decode())
    if sys.version_info[0] == 2:
        from __builtin__ import compile, eval, globals
    else:
        from builtins import compile, eval, globals
    try:
        p, e = ast.parse(source, filename), None
    except SyntaxError:
        t, v, tb = sys.exc_info()
        sys.excepthook(t, v, tb.tb_next)
        return
    if p.body and isinstance(p.body[-1], ast.Expr):
        e = p.body.pop()
    try:
        g = globals()
        exec(compile(p, filename, 'exec'), g, g)
        if e:
            return eval(compile(ast.Expression(e.value), filename, 'eval'), g, g)
    except Exception:
        t, v, tb = sys.exc_info()
        sys.excepthook(t, v, tb.tb_next)
"
  "Code for sending code to a sage process.
This is essentially same as `python-shell-eval-setup-code' except the addition
of a preparse step that converts sage code to python code.")

(defvar sage-shell-doc-setup-code
  "\
from IPython.terminal.prompts import Prompts, Token
class __SAGEPrompt(type(get_ipython().prompts)):
     redirect_happening = False
     initial_prompts = get_ipython().prompts
     def in_prompt_tokens(self, cli=None):
         if self.redirect_happening:
             self.redirect_happening = False
             return [(Token.Prompt,'***** COMINT REDIRECT ENDED *****')]
         else:
             return self.initial_prompts.in_prompt_tokens()
get_ipython().prompts = __SAGEPrompt(get_ipython())

def __SAGE_get_doc(str):
    In.pop()
    get_ipython().prompts.redirect_happening = True
    get_ipython().run_line_magic('pinfo', str)
"
  "Code to setup fetching documentation from a Sage process.
It is needed because the documentation somtimes contains bare prompts which
trip up the comint redirection machinery.  This works around that.")

;;;; Sage shell mode
(defun sage-shell-setup-doc ()
  "Send `sage-shell-doc-setup-code' to Sage process."
  (python-shell-send-string-no-output sage-shell-doc-setup-code))

(define-derived-mode sage-shell-mode inferior-python-mode
  "Sage Repl" "Execute Sage commands interactively."
  (setq-local python-shell-eval-setup-code sage-shell-eval-setup-code
              comint-redirect-completed t)
  (add-hook 'python-shell-first-prompt-hook #'sage-shell-setup-doc)
  (add-hook 'comint-redirect-filter-functions #'ansi-color-apply nil t))

;;;###autoload
(defun run-sage (&optional dedicated show)
  "Run an inferior sage process.

The command to run the process is calculated using `sage-shell-interpreter'
and `sage-shell-interpreter-args'.

When called interactively with `prefix-arg', it allows
the user to choose whether the interpreter should be DEDICATED to the current
buffer or project.  When numeric prefix arg is other than 0 or 4 do not SHOW.

For a given buffer and same values of DEDICATED, if a process is
already running for it, it will do nothing.  This means that if
the current buffer is using a global process, the user is still
able to switch it to use a dedicated one.

Type \\[describe-mode] in the process buffer for a list of commands."
  (interactive
   (if current-prefix-arg
       (list
        (alist-get (car (read-multiple-choice "Make dedicated process?"
                                              '((?b "to buffer")
                                                (?p "to project")
                                                (?n "no"))))
                   '((?b . buffer) (?p . project)))
        (= (prefix-numeric-value current-prefix-arg) 4))
     (list python-shell-dedicated t)))
  (let* ((proc (python-shell-get-process-name dedicated))
         (buf (get-buffer-create (format "*%s*" proc))))
    (unless (get-buffer-process buf)
      (with-current-buffer buf
        (let ((inhibit-read-only t))
          (erase-buffer))
        (apply #'make-comint-in-buffer proc buf sage-shell-interpreter nil
               sage-shell-interpreter-args)
        ;; Ideally we should able to bind `python-shell-interpreter' and
        ;; `python-shell-interpreter-args' and call `python-run'. However, that
        ;; runs into problems since `inferior-python-mode' uses
        ;; `python-shell--interpreter' and `python-shell--interpreter-args'
        ;; which can only be let bound from within `python.el'. This makes it
        ;; impossible to call any derived mode of `inferior-python-mode'
        ;; without dealing with these internal variables.
        (dlet ((python-shell--interpreter "sage")
               (python-shell--interpreter-args "--simple-prompt"))
          (let ((python-shell-prompt-detect-enabled nil)
                (python-shell-interpreter-interactive-arg "")
                (python-shell-prompt-regexp "sage: "))
            (sage-shell-mode))
          (setq-local python-shell-prompt-detect-enabled nil
                      python-shell-interpreter-interactive-arg ""
                      python-shell-prompt-regexp "sage: "))))
    (if show (display-buffer buf))
    proc))

;;;; Sage Mode
(defvar-keymap sage-mode-map
  "<remap> <run-python>" #'run-sage
  "<remap> <python-eldoc-at-point>" #'sage-lookup-doc)

;;;###autoload
(define-derived-mode sage-mode python-mode "Sage"
  :group 'sage
  (setq-local python-shell-buffer-name "Sage"
              python-shell-interpreter sage-shell-interpreter
              python-shell-interpreter-args (combine-and-quote-strings
                                             sage-shell-interpreter-args)
              python-shell-eval-setup-code sage-shell-eval-setup-code))

;;;###autoload
(cl-pushnew `(,(rx ".sage" eos) . sage-mode) auto-mode-alist :test #'equal)

;;;; Documentation
(defun sage--redirect-send (command output-buffer process echo no-display)
  "Version of `comint-redirect-send-command-to-process'.
Which see for COMMAND, OUTPUT-BUFFER, PROCESS, ECHO and NO-DISPLAY.
The COMMAND must output ***** COMINT REDIRECT ENDED ***** as the last line."
  (with-current-buffer (process-buffer process)
    (if (and (python-util-comint-end-of-output-p)
             comint-redirect-completed)
        (let ((comint-redirect-perform-sanity-check nil)
              (comint-prompt-regexp (rx bol "***** COMINT REDIRECT ENDED *****")))
          (comint-redirect-send-command-to-process
           command output-buffer process echo no-display))
      (error "Sage process is busy"))))

(defun sage-lookup-doc (symbol &optional proc display)
  "Look up documentation for the SYMBOL.
It is the symbol at point when called interactively.  PROC is the sage process.
If DISPLAY is non-nil, buffer is displayed."
  (interactive (list (python-info-current-symbol) (python-shell-get-process) t))
  (let ((buf (get-buffer-create "*Sage Documentation*"))
        (proc (or proc (python-shell-get-process))))
    (prog1 buf
      (with-current-buffer buf
        (erase-buffer)
        (setq mode-line-format nil)
        (font-lock-mode)
        (visual-line-mode))
      (sage--redirect-send (format "__SAGE_get_doc('%s')" symbol)
                           buf proc nil (not display)))))

;;;; Provide
(provide 'sage)
;;; sage.el ends here

debug log:

solving 730725924a3 ...
found 730725924a3 in https://yhetil.org/emacs-devel/87wmnotrid.fsf@zohomail.eu/
found cba3421cbaa in https://yhetil.org/emacs-devel/87wmnotrid.fsf@zohomail.eu/

applying [1/2] https://yhetil.org/emacs-devel/87wmnotrid.fsf@zohomail.eu/
diff --git a/lisp/progmodes/sage.el b/lisp/progmodes/sage.el
new file mode 100644
index 00000000000..cba3421cbaa


applying [2/2] https://yhetil.org/emacs-devel/87wmnotrid.fsf@zohomail.eu/
diff --git a/lisp/progmodes/sage.el b/lisp/progmodes/sage.el
index cba3421cbaa..730725924a3 100644

Checking patch lisp/progmodes/sage.el...
Applied patch lisp/progmodes/sage.el cleanly.
Checking patch lisp/progmodes/sage.el...
Applied patch lisp/progmodes/sage.el cleanly.

index at:
100644 730725924a34832981d3e822f812b6c9b4cc95f0	lisp/progmodes/sage.el

(*) Git path names are given by the tree(s) the blob belongs to.
    Blobs themselves have no identifier aside from the hash of its contents.^

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).