emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[elpa] externals/drepl b79b71d39b 06/10: Refactoring and documentation


From: ELPA Syncer
Subject: [elpa] externals/drepl b79b71d39b 06/10: Refactoring and documentation
Date: Tue, 7 Nov 2023 03:58:04 -0500 (EST)

branch: externals/drepl
commit b79b71d39be7a24798a624aadba6070741fdf3f3
Author: Augusto Stoffel <arstoffel@gmail.com>
Commit: Augusto Stoffel <arstoffel@gmail.com>

    Refactoring and documentation
---
 drepl-ipython.el | 19 ++++-------
 drepl-ipython.py | 74 +++++++++++++++++++++++-------------------
 drepl-lua.el     | 19 +++--------
 drepl.el         | 98 +++++++++++++++++++++++++++++++++++++-------------------
 4 files changed, 118 insertions(+), 92 deletions(-)

diff --git a/drepl-ipython.el b/drepl-ipython.el
index a825a0827d..7faabcdd52 100644
--- a/drepl-ipython.el
+++ b/drepl-ipython.el
@@ -31,7 +31,7 @@
 ;;; Customization options
 
 (defgroup drepl-ipython nil
-  "IPython shell implemented via dREPL"
+  "IPython shell implemented via dREPL."
   :group 'drepl
   :group 'python
   :link '(url-link "https://github.com/astoff/drepl";))
@@ -56,28 +56,23 @@ substring \"{}\" is replaced by the execution count."
                       default-directory))
   "File name of the startup script.")
 
-(define-derived-mode drepl-ipython-mode drepl-mode "IPython"
-  "Major mode for the IPython shell.
-
-\\<drepl-ipython-mode-map>"
-  :syntax-table python-mode-syntax-table
-  :interactive nil
-  (setq-local comint-indirect-setup-function #'python-mode)
-  (push '("5151" . comint-mime-osc-handler) ansi-osc-handlers))
-
 (defclass drepl-ipython (drepl-base) nil)
+(put 'drepl-ipython 'drepl--buffer-name "IPython")
 
 ;;;###autoload
 (defun drepl-run-ipython ()
+  "Start the IPython interpreter."
   (interactive)
   (drepl--run 'drepl-ipython t))
 
 (cl-defmethod drepl--command ((_ drepl-ipython))
   `(,python-interpreter "-c"
-    "import sys; exec(''.join(sys.stdin)); DRepl.instance().mainloop()"))
+    "import sys; exec(''.join(sys.stdin)); Drepl.instance().mainloop()"))
 
 (cl-defmethod drepl--init ((_ drepl-ipython))
-  (drepl-ipython-mode)
+  (drepl-mode)
+  (setq-local comint-indirect-setup-function #'python-mode)
+  (push '("5151" . comint-mime-osc-handler) ansi-osc-handlers)
   (let ((buffer (current-buffer)))
     (with-temp-buffer
       (insert-file-contents drepl-ipython--start-file)
diff --git a/drepl-ipython.py b/drepl-ipython.py
index cb90028db3..a4895c4c1f 100644
--- a/drepl-ipython.py
+++ b/drepl-ipython.py
@@ -3,6 +3,8 @@
 import base64
 import json
 import sys
+from pathlib import Path
+from tempfile import mkstemp
 
 from IPython.core.completer import provisionalcompleter
 from IPython.core.displayhook import DisplayHook
@@ -29,9 +31,12 @@ def reply(**data):
     print(f"\033]5161;{json.dumps(data)}\033\\", end="")
 
 
-class DReplDisplayHook(DisplayHook):
+class DreplError(Exception):
+    pass
+
+
+class DreplDisplayHook(DisplayHook):
     def write_output_prompt(self):
-        """Write the output prompt."""
         print(self.shell.separate_out, end="")
         outprompt = sys.ps3.format(self.shell.execution_count)
         if self.do_full_cache:
@@ -46,7 +51,7 @@ class DReplDisplayHook(DisplayHook):
 
 
 @InteractiveShellABC.register
-class DRepl(InteractiveShell):
+class Drepl(InteractiveShell):
     def __init__(self, *args, **kwargs) -> None:
         super().__init__(*args, **kwargs)
         self.current_ps1 = None
@@ -66,7 +71,7 @@ class DRepl(InteractiveShell):
         print(self.banner)
 
     system = InteractiveShell.system_raw
-    displayhook_class = DReplDisplayHook
+    displayhook_class = DreplDisplayHook
 
     def make_mime_renderer(self, type, encoder):
         def renderer(data, meta=None):
@@ -74,9 +79,6 @@ class DRepl(InteractiveShell):
                 data = encoder(data)
             header = json.dumps({**(meta or {}), "type": type})
             if len(data) > self.mime_size_limit:
-                from pathlib import Path
-                from tempfile import mkstemp
-
                 fdesc, fname = mkstemp()
                 with open(fdesc, "wb") as f:
                     f.write(data)
@@ -105,46 +107,52 @@ class DRepl(InteractiveShell):
     def mainloop(self):
         while self.keep_running:
             try:
-                if self.current_ps1 is None:
-                    reply(op="getoptions")
-                    self.current_ps1, separate_in = "", ""
-                else:
-                    reply(op="status", status="ready")
-                    separate_in = self.separate_in if self.current_ps1 else ""
-                    self.current_ps1 = sys.ps1.format(self.execution_count)
-                line = input(separate_in + self.current_ps1)
-                while line.startswith("\033%"):
-                    data = json.loads(line[2:])
-                    op = data.pop("op")
-                    fun = getattr(self, "drepl_{}".format(op), None)
-                    if fun is None:
-                        print("Invalid op: {}".format(op))
-                        continue
-                    fun(**data)
-                    if op in ("eval", "setoptions"):
-                        break  # Allow execution count to increment.
-                    reply(op="status", status="ready")
-                    line = input()
-                else:
-                    print("Invalid input")
-            except KeyboardInterrupt as e:
-                print(e.__class__.__name__)
+                self.run_repl()
             except EOFError:
                 reply(op="status", status="busy")
                 if (not self.confirm_exit) or self.ask_yes_no(
                     "Do you really want to exit ([y]/n)?", "y", "n"
                 ):
                     self.ask_exit()
+            except (DreplError, KeyboardInterrupt) as e:
+                print(str(e) or e.__class__.__name__)
+
+    def run_repl(self):
+        "Print prompt, run REPL until a new prompt is needed."
+        if self.current_ps1 is None:
+            reply(op="getoptions")
+            self.current_ps1, separate_in = "", ""
+        else:
+            reply(op="status", status="ready")
+            separate_in = self.separate_in if self.current_ps1 else ""
+            self.current_ps1 = sys.ps1.format(self.execution_count)
+        line = input(separate_in + self.current_ps1)
+        while True:
+            if not line.startswith("\033%"):
+                raise DreplError("Invalid input")
+            data = json.loads(line[2:])
+            op = data.pop("op")
+            fun = getattr(self, "drepl_{}".format(op), None)
+            if fun is None:
+                raise DreplError("Invalid op: {}".format(op))
+            fun(**data)
+            if op == "eval":
+                self.execution_count += 1
+                break
+            elif op == "setoptions":
+                break
+            else:
+                reply(op="status", status="ready")
+                line = input()
 
     def drepl_eval(self, id, code):
         r = self.run_cell(code)
-        #reply(id=id, result=repr(r.result))
         reply(id=id)
 
     def drepl_complete(self, id, code, offset):
         with provisionalcompleter():
             r = [
-                {"text": c.text, "annotation": c.signature}
+                {"text": c.text, "annot": c.signature}
                 for c in self.Completer.completions(code, offset)
             ]
         reply(id=id, candidates=r or None)
diff --git a/drepl-lua.el b/drepl-lua.el
index e13e13c755..855db81c1e 100644
--- a/drepl-lua.el
+++ b/drepl-lua.el
@@ -29,15 +29,11 @@
 (require 'lua-mode)
 
 (defgroup drepl-lua nil
-  "Lua shell implemented via dREPL"
+  "Lua shell implemented via dREPL."
   :group 'drepl
   :group 'lua
   :link '(url-link "https://github.com/astoff/drepl";))
 
-(defcustom drepl-lua-buffer-name "*Lua*"
-  "Name of Lua shell buffer."
-  :type 'string)
-
 (defvar drepl-lua--start-file
   (expand-file-name "drepl-lua.lua"
                     (if load-file-name
@@ -46,25 +42,20 @@
   "File name of the startup script.")
 
 (defclass drepl-lua (drepl-base) nil)
+(put 'drepl-lua 'drepl--buffer-name "Lua")
 
 ;;;###autoload
 (defun drepl-run-lua ()
+  "Start the Lua interpreter."
   (interactive)
   (drepl--run 'drepl-lua t))
 
-(define-derived-mode drepl-lua-mode drepl-mode "Lua"
-  "Major mode for the Lua shell.
-
-\\<drepl-lua-mode-map>"
-  :syntax-table lua-mode-syntax-table
-  :interactive nil
-  (setq-local comint-indirect-setup-function #'lua-mode))
-
 (cl-defmethod drepl--command ((_ drepl-lua))
   '("lua" "-v" "-e" "loadfile()():main()"))
 
 (cl-defmethod drepl--init ((_ drepl-lua))
-  (drepl-lua-mode)
+  (drepl-mode)
+  (setq-local comint-indirect-setup-function #'lua-mode)
   (let ((buffer (current-buffer)))
     (with-temp-buffer
       (insert-file-contents drepl-lua--start-file)
diff --git a/drepl.el b/drepl.el
index 9d7a2b464c..6b9b1325eb 100644
--- a/drepl.el
+++ b/drepl.el
@@ -51,9 +51,11 @@ which determines whether to ask or return nil when in doubt."
   :type '(choice string function))
 
 (defvar-local drepl--current nil
-  "dREPL associated to the current buffer.")
+  "The dREPL associated to the current buffer.
+In dREPL buffers, this is the dREPL object itself.  In all other
+buffers, this is the dREPL buffer or nil.")
 
-(defvar drepl--verbose t)
+(defvar drepl--verbose nil)
 
 ;;; Basic definitions
 
@@ -67,25 +69,28 @@ which determines whether to ask or return nil when in 
doubt."
   :documentation "Base dREPL class.")
 
 (defun drepl--process (repl)
+  "The underlying process of dREPL object REPL."
   (get-buffer-process (drepl--buffer repl)))
 
-(defun drepl--get-repl (&optional status)
-  (let ((repl drepl--current))          ;TODO: choose one interactively, maybe
-    (when (or (not status)
-              (and (memq (process-status (drepl--process repl)) '(run open))
-                   (eq status (drepl--status repl))))
-      repl)))
-
-(defsubst drepl--message (format-string &rest args)
-  (when drepl--verbose
-    (apply #'message format-string args)))
+(defmacro drepl--message (fmtstring &rest args)
+  "Write a message to *drepl-log* buffer if `drepl--verbose' is non-nil.
+The message is formed by calling `format' with FMTSTRING and ARGS."
+  (let ((msg (lambda (&rest args)
+               (with-current-buffer (get-buffer-create "*drepl-log*")
+                 (goto-char (point-max))
+                 (when-let ((w (get-buffer-window)))
+                   (set-window-point w (point)))
+                 (insert (propertize (format-time-string "[%T] ") 'face 'error)
+                         (apply #'format args)
+                         ?\n)))))
+    `(when drepl--verbose (funcall ,msg ,fmtstring ,@args))))
 
 ;;; Communication protocol
 
 (defalias 'drepl--json-decode
   (if (json-available-p)
       (lambda (s)
-        (json-parse-string s :object-type 'alist))
+        (json-parse-string s :object-type 'alist :null-object nil))
     (error "Not implemented")))
 
 (defalias 'drepl--json-encode
@@ -94,19 +99,24 @@ which determines whether to ask or return nil when in 
doubt."
     (error "Not implemented")))
 
 (cl-defgeneric drepl--send-request (repl data)
-  (drepl--message "OUT: %s" (json-serialize data))
+  "Send request data to REPL.
+REPL must be in `ready' state and transitions to `busy' state.
+DATA is a plist containing the request arguments, as well as :op
+and :id entries."
   (setf (drepl--status repl) 'busy)
-  (process-send-string (drepl--process repl)
-                       (format "\e%%%s\n" (json-serialize data))))
+  (let ((encoded (drepl--json-encode data)))
+    (drepl--message "send %s" encoded)
+    (process-send-string (drepl--process repl)
+                         (format "\e%%%s\n" encoded))))
 
 (cl-defgeneric drepl--communicate (repl callback op &rest args)
   (if (eq callback 'sync)
       (progn (unless (eq (drepl--status repl) 'ready)
                (user-error "%s is busy" repl))
-             (let* ((result :none)
+             (let* ((result :pending)
                     (cb (lambda (data) (setq result data))))
                (apply #'drepl--communicate repl cb op args)
-               (while (eq result :none) (accept-process-output))
+               (while (eq result :pending) (accept-process-output))
                result))
     (let* ((id (cl-incf (oref repl last-request-id)))
            (data `(:id ,id :op ,(symbol-name op) ,@args)))
@@ -122,15 +132,15 @@ which determines whether to ask or return nil when in 
doubt."
   (pcase (alist-get 'op data)
     ("status" (setf (drepl--status repl)
                     (intern (alist-get 'status data))))
-    ("log" (drepl--message "dREPL buffer %s: %s"
     ("getoptions"
      (setf (drepl--status repl) 'ready)
      (drepl--set-options repl data))
+    ("log" (drepl--message "log:%s: %s"
                            (buffer-name)
                            (alist-get 'text data)))))
 
 (defun drepl--osc-handler (_cmd text)
-  (drepl--message " IN: %s" text)
+  (drepl--message "read %s" text)
   (let* ((data (drepl--json-decode text))
          (id (alist-get 'id data))
          (callback (if id
@@ -163,7 +173,7 @@ which determines whether to ask or return nil when in 
doubt."
                                         :offset offset))))
     (mapcar (lambda (c)
               (let-alist c
-                (propertize .text 'drepl--annot .annotation)))
+                (propertize .text 'drepl--annot .annot)))
             (alist-get 'candidates response))))
 
 (defun drepl--complete ()
@@ -191,18 +201,19 @@ which determines whether to ask or return nil when in 
doubt."
   (drepl--communicate repl #'ignore 'eval :code code))
 
 (defun drepl--send-string (proc string)
-  "Like `comint-send-string', but checks whether PROC's status is `ready'.
+  "Like `comint-send-string', but check whether PROC's status is `ready'.
 If it is, then make an eval request, otherwise just send the raw
 STRING to the process."
-  (drepl--message "SND: %s" string)
   (let ((repl (with-current-buffer
                   (if proc (process-buffer proc) (current-buffer))
                 (drepl--get-repl 'ready))))
     (if repl
         (drepl--eval repl string)
+      (drepl--message "send %s" string)
       (comint-simple-send proc string))))
 
 (defun drepl-eval (code)
+  "Evaluate CODE string in the current buffer's REPL."
   (interactive (list (read-from-minibuffer "Evaluate: ")))
   (drepl--eval (drepl--get-repl) code))
 
@@ -212,7 +223,7 @@ If the input is incomplete or invalid code and FORCE is nil,
 insert start a continuation line instead."
   (interactive "P")
   (unless (derived-mode-p 'drepl-mode)
-    (user-error "Can't run this command here."))
+    (user-error "Can't run this command here"))
   (let-alist (when-let ((repl (unless force (drepl--get-repl 'ready)))
                         (pmark (process-mark (drepl--process repl)))
                         (code (and (>= (point) pmark)
@@ -256,6 +267,7 @@ insert start a continuation line instead."
           (insert (ansi-color-apply .text)))))))
 
 (defun drepl-describe-thing-at-point ()
+  "Pop up help on the thing at point."
   (interactive)
   (when-let ((repl (when (derived-mode-p 'drepl-mode)
                      (drepl--get-repl 'ready))))
@@ -271,16 +283,17 @@ otherwise fall back to `default-directory'."
       (project-root proj)
     default-directory))
 
-(defun drepl--buffer-name (class dir)
+(defun drepl--buffer-name (class directory)
+  "Buffer name for a REPL of the given CLASS running in DIRECTORY."
   (format "%s/*%s*"
           (file-name-nondirectory
-           (directory-file-name dir))
-          class))
+           (directory-file-name directory))
+          (or (get class 'drepl--buffer-name) class)))
 
 (defun drepl--get-buffer-create (class ask)
   "Get or create a dREPL buffer of the given CLASS.
 The directory of the buffer is determined by
-`drepl-directory'. If ASK is non-nil, allow an interactive query
+`drepl-directory'.  If ASK is non-nil, allow an interactive query
 if needed."
   (if (eq (type-of drepl--current) class)
       (drepl--buffer drepl--current)
@@ -290,29 +303,46 @@ if needed."
       (get-buffer-create (drepl--buffer-name class default-directory)))))
 
 (cl-defgeneric drepl--command (repl)
+  "The command to start the REPL interpreter as a list of strings."
   (ignore repl)
   (error "This needs an implementation"))
 
 (cl-defgeneric drepl--init (repl)
+  "Initialization code for REPL.
+This function is called in the REPL buffer after a Comint has
+been started in it.  It should call `drepl-mode' or a derived
+mode and perform all other desired initialization procedures."
   (ignore repl)
   (error "This needs an implementation"))
 
 (cl-defgeneric drepl--set-options (repl data)
+  "Implementation of the `setoptions' operation.
+This method is called when the REPL sends a `getoptions'
+notification.  The REPL is in `ready' state when this happens.
+The notification message is passed as DATA."
   (ignore repl data)
   (error "This needs an implementation"))
 
-(cl-defgeneric drepl--restart (repl _hard)
-  "Restart the REPL."
+(cl-defgeneric drepl--restart (repl hard)
+  "Generic method to restart a REPL.
+HARD should be as described in `drepl-restart', but it not used
+in the default implementation."
+  (ignore hard)
   (with-current-buffer (drepl--buffer repl)
     (kill-process (drepl--process repl))
     (while (accept-process-output (drepl--process repl)))
     (drepl--run (type-of repl) nil)))
 
 (defun drepl-restart (&optional hard)
+  "Restart the current REPL.
+Some REPLs by default perform a soft reset by deleting all user
+variables without killing the interpreter.  In those cases, a
+prefix argument or non-nil HARD argument can be used to force a
+hard reset."
   (interactive "P")
-  (if-let ((repl (drepl--get-repl)))
-      (drepl--restart repl hard)
-    (user-error "No REPL to restart")))
+  (drepl--restart (or (drepl--get-repl)
+                      (user-error "No REPL"))
+                  hard))
 
 (defun drepl--run (class may-prompt)
   (let ((buffer (drepl--get-buffer-create class may-prompt)))
@@ -322,6 +352,7 @@ if needed."
                  (repl (make-instance class :buffer buffer))
                  (command (drepl--command repl)))
         (with-current-buffer buffer
+          (drepl--message "starting %s" buffer)
           (apply #'make-comint-in-buffer
                (buffer-name buffer) buffer
                (car command) nil
@@ -351,4 +382,5 @@ if needed."
 
 (provide 'drepl)
 
+; LocalWords:  dREPL
 ;;; drepl.el ends here



reply via email to

[Prev in Thread] Current Thread [Next in Thread]