Warning: this is an htmlized version!
The original is here, and the conversion rules are here. |
;; This file: ;; http://angg.twu.net/emlua/emlua-repl.el.html ;; http://angg.twu.net/emlua/emlua-repl.el ;; (find-angg "emlua/emlua-repl.el") ;; https://raw.githubusercontent.com/edrx/emlua/main/emlua-repl.el ;; https://github.com/edrx/emlua/blob/main/emlua-repl.el ;; Author: Eduardo Ochs <eduardoochs@gmail.com> ;; Version: 2022mar28 ;; License: GPL2 ;; ;; See: <https://github.com/edrx/emlua>. ;; Introduction ;; ============ ;; This file implements `eepitch-emlua' and several variants of it - ;; i.e., it implements "several different `eepitch-emlua's". ;; ;; All these `eepitch-emlua's pitch things to the emlua buffer, and ;; this GENERALLY means that they talk to a Lua interpreter via ;; `emlua-dostring' and then use the emlua buffer as a kind of log of ;; the communication. This screenshot shows how this is _typically_ ;; used: ;; ;; http://angg.twu.net/IMAGES/2022eepitch-emlua-0.png ;; ;; BUT: the emlua buffer is just a log buffer, and to keep the code ;; simple I keep it in fundamental mode. This means that RET there is ;; not special: typing, say, ;; ;; print(2+3) ;; ;; after a prompt in the emlua buffer and then typing RET does not ;; send the "print(2+3)" to the Lua interpreter, or anywhere - the RET ;; just inserts a "\n". ;; ;; Remember that `eepitch-shell' sets up a target buffer running an ;; "inferior shell". See: ;; ;; (find-eev-quick-intro "6. Controlling shell-like programs") ;; (find-enode "Major Modes" "inferior shell process") ;; (find-enode "Interactive Shell") ;; ;; Here are the first two paragraphs of the page "Interactive Shell": ;; ;; To run a subshell interactively, type ‘M-x shell’. This creates ;; (or reuses) a buffer named ‘*shell*’, and runs a shell subprocess ;; with input coming from and output going to that buffer. That is ;; to say, any terminal output from the subshell goes into the ;; buffer, advancing point, and any terminal input for the subshell ;; comes from text in the buffer. To give input to the subshell, go ;; to the end of the buffer and type the input, terminated by <RET>. ;; ;; By default, when the subshell is invoked interactively, the ;; ‘*shell*’ buffer is displayed in a new window, unless the current ;; window already shows the ‘*shell*’ buffer. This behavior can be ;; customized via ‘display-buffer-alist’. ;; ;; In this file the notion "inferior Lua" will be kept deliberately ;; vague: the meaning of "sending (something) the the inferior Lua" ;; will depend on the context, and _can be modified at will_. In most ;; cases it means: ;; ;; send [this] to the REPL running in the Lua interpreter showing ;; [this] in the log buffer, and then show in the log buffer the ;; answer of the Lua interpreter and the next prompt ;; ;; but the details of this "send" can change, and the REPL can change ;; - for example, Technomancy is experimenting with REPLs based on ;; coroutines, and I am making some experiments with REPLs that send ;; text with properties back to Emacs - and in same cases we perform ;; tests using fake REPLs that always answer the same string. ;; ;; ;; `emlua-do' ;; ========== ;; One of the main building blocks of this file is the function ;; `emlua-do', whose semantics can also be changed. In principle ;; ;; (emlua-do '(foo bar)) ;; ;; evals `(foo bar)' inside the emlua buffer, but this can mean ;; something full of add-ons, like "make sure that the emlua buffer ;; exists, is initialized, and is shown in a visible window, and run ;; `(foo bar)' there with the point at the end of the buffer". ;; ;; Right now the behavior of `emlua-do' is changed by defalias-ing it ;; to different functions. ;; An older description: ;; --------------------- ;; Note: this is my N-th attempt (for N big!) of rewriting this file ;; to make its code easy to understand... but it still needs more ;; rewrites, and lots of explanations and tests. In particular, 1) I ;; need to rename some functions to make clear that we are ;; implementing a kind of an "inferior process" running Lua, 2) I need ;; to explain that the buffer "*emlua*" is just a log of our ;; interactions with our "inferior Lua" via `eepitch-emlua-esend' - ;; lines typed there are not automatically sent to the inferior Lua, ;; 3) it _seems_ that the functions that end in "-bw" are not needed, ;; 4) only a few functions defined in eepitch.el use ;; ;; (setq eepitch-line 'eeepitch-line-<suffix>) ;; ;; to set up alternative ways to send lines to the target. I did not ;; remember the details of how they worked, and I improvised a lot, ;; using functions with bad names and weird constructs... I need to ;; clean up that code A LOT. There are some examples of "official" ;; functions that use alternative `eepitch-line's here: ;; ;; (find-eev "eepitch.el" "other-terms") ;; ;; See also: ;; ;; (find-eev "eepitch.el" "eepitch-this-line") ;; «.faces» (to "faces") ;; «.find-emluabuffer» (to "find-emluabuffer") ;; «.emlua-do» (to "emlua-do") ;; «.emlua-insert» (to "emlua-insert") ;; «.eepitch-emlua-fake1» (to "eepitch-emlua-fake1") ;; «.esend» (to "esend") ;; «.eepitch-emlua» (to "eepitch-emlua") ;; «.emlua-dostring+» (to "emlua-dostring+") ;; To test this, compile emlua.cpp, then run: ;; ;; (add-to-list 'load-path default-directory) ;; (require 'emlua-repl) ;; (emlua-init-so) ;; (emlua-init-dofiles) ;; (emlua-init-newrepl) ;; ;; and then run the test blocks in this file. (require 'eepitch) (require 'emlua-data) (require 'emlua-init) ;;; _____ ;;; | ___|_ _ ___ ___ ___ ;;; | |_ / _` |/ __/ _ \/ __| ;;; | _| (_| | (_| __/\__ \ ;;; |_| \__,_|\___\___||___/ ;;; ;; «faces» (to ".faces") ;; (defface emlua-prompt-face '((((background dark)) :foreground "RoyalBlue3") (((background light)) :foreground "RoyalBlue3")) "") (defface emlua-user-input-face '((((background dark)) :foreground "orange1") (((background light)) :foreground "DarkOrange")) "") (defface emlua-output-face '((((background dark)) :foreground "bisque") (((background light)) :foreground "SaddleBrown")) "") (defface emlua-error-face '((((background dark)) :foreground "red") (((background light)) :foreground "red")) "") ;;; __ _ _ _ __ __ ;;; / _(_)_ __ __| | | |__ _ _ / _|/ _| ___ _ __ ;;; | |_| | '_ \ / _` |_____| '_ \| | | | |_| |_ / _ \ '__| ;;; | _| | | | | (_| |_____| |_) | |_| | _| _| __/ | ;;; |_| |_|_| |_|\__,_| |_.__/ \__,_|_| |_| \___|_| ;;; ;; «find-emluabuffer» (to ".find-emluabuffer") (defun emlua-buffer () "Return the *emlua* buffer, or nil if it doesn't exist." (get-buffer "*emlua*")) (defun emlua-buffer-initial-prompt () (propertize "Emlua:\n>>> " 'face 'emlua-prompt-face)) (defun find-emluabuffer () "Go to the *emlua* buffer, and make sure it's initialized." (if (emlua-buffer) (find-ebuffer "*emlua*") (find-ebuffer "*emlua*") (insert (emlua-buffer-initial-prompt)))) ;; Other basic tools: ;; (defun emlua-window () "Return a window with the *emlua* buffer, or nil if it doesn't exist." (get-buffer-window "*emlua*")) (defun emlua-buffer-kill () (if (emlua-buffer) (ee-kill-buffer (emlua-buffer)))) ;;; _ _ ;;; ___ _ __ ___ | |_ _ __ _ __| | ___ ;;; / _ \ '_ ` _ \| | | | |/ _` |_____ / _` |/ _ \ ;;; | __/ | | | | | | |_| | (_| |_____| (_| | (_) | ;;; \___|_| |_| |_|_|\__,_|\__,_| \__,_|\___/ ;;; ;; «emlua-do» (to ".emlua-do") (defun emlua-do-b (code) (find-emluabuffer) (goto-char (point-max)) (eval code)) (defun emlua-do-bs (code) (save-excursion (find-emluabuffer) (goto-char (point-max)) (eval code))) (defun emlua-do-w (code) (if (not (emlua-window)) (find-2a nil '(find-emluabuffer))) (save-selected-window (select-window (emlua-window)) (goto-char (point-max)) (eval code))) (defalias 'emlua-do 'emlua-do-bs) ;;; _ _ ;;; (_)_ __ ___ ___ _ __| |_ ;;; | | '_ \/ __|/ _ \ '__| __| ;;; | | | | \__ \ __/ | | |_ ;;; |_|_| |_|___/\___|_| \__| ;;; ;; «emlua-insert» (to ".emlua-insert") (defun emlua-insert (str &optional face) (emlua-do `(insert ,(propertize str 'face face)))) (defun emlua-insert-prompt (prompt) (emlua-insert prompt 'emlua-prompt-face)) (defun emlua-insert-user-input (line) (emlua-insert (concat line "\n") 'emlua-user-input-face)) (defun emlua-insert-output (output) (emlua-insert output 'emlua-output-face)) (defun emlua-insert-error (err) (emlua-insert err 'emlua-error-face)) '("This is a test block: * (emlua-buffer-kill) * (defalias 'emlua-do 'emlua-do-w) * (emlua-do nil) * (emlua-insert-user-input "foo") * (emlua-insert-output "bar\n") * (emlua-insert-prompt ">-> ") --") ;;; __ _ _ ;;; / _| __ _| | _____/ | ;;; | |_ / _` | |/ / _ \ | ;;; | _| (_| | < __/ | ;;; |_| \__,_|_|\_\___|_| ;;; ;; «eepitch-emlua-fake1» (to ".eepitch-emlua-fake1") (defun eepitch-line-emlua-fake1 (line) (eepitch-eval-at-target-window `(progn (emlua-insert-user-input ,line) (emlua-insert-output "(emlua-fake1 output)\n") (emlua-insert-prompt ">-> ")))) (defun eepitch-emlua-fake1 () (interactive) (defalias 'emlua-do 'emlua-do-b) (prog1 (eepitch '(find-emluabuffer)) (setq eepitch-line 'eepitch-line-emlua-fake1))) '("This is a test block: * (eepitch-emlua-fake1) * (eepitch-kill) * (eepitch-emlua-fake1) foo bar --") ;;; _ ;;; ___ ___ ___ _ __ __| | ;;; / _ \/ __|/ _ \ '_ \ / _` | ;;; | __/\__ \ __/ | | | (_| | ;;; \___||___/\___|_| |_|\__,_| ;;; ;; «esend» (to ".esend") (defvar eepitch-emlua-out nil "The results of the last call to `eepitch-emlua-esend0'.") (defun eepitch-emlua-esend0 (line) "Run REPL:esend(LINE) and save the results in `eepitch-emlua-out'." (setq eepitch-emlua-out (emlua-dostring (emlua-format "return REPL:esend(%s)" line)))) (defun eepitch-emlua-esend1 () "Insert the contents of `eepitch-emlua-out' in the right way." (if (stringp eepitch-emlua-out) (emlua-insert-error (concat eepitch-emlua-out "\n")) (if (= 0 (length eepitch-emlua-out)) "eepitch-emlua-out is []: do nothing" (emlua-insert-output (aref eepitch-emlua-out 0))))) (defun eepitch-emlua-eprompt () "Insert the result of REPL:eprompt() at the end of the *emlua* buffer." (emlua-insert-prompt (aref (emlua-dostring "return REPL:eprompt()") 0))) '("This is a test block: * (defalias 'emlua-do 'emlua-do-w) * (emlua-buffer-kill) * (find-2a nil '(find-emluabuffer))) * (emlua-insert-user-input "= 22+33") * (eepitch-emlua-esend0 "= 22+33") * (eepitch-emlua-esend1) * (eepitch-emlua-eprompt) --") ;;; _ _ _ _ ;;; ___ ___ _ __ (_) |_ ___| |__ ___ _ __ ___ | |_ _ __ _ ;;; / _ \/ _ \ '_ \| | __/ __| '_ \ _____ / _ \ '_ ` _ \| | | | |/ _` | ;;; | __/ __/ |_) | | || (__| | | |_____| __/ | | | | | | |_| | (_| | ;;; \___|\___| .__/|_|\__\___|_| |_| \___|_| |_| |_|_|\__,_|\__,_| ;;; |_| ;; ;; «eepitch-emlua» (to ".eepitch-emlua") (defun eepitch-line-emlua (line) (eepitch-eval-at-target-window `(progn (emlua-insert-user-input ,line) (eepitch-emlua-esend0 ,line) (eepitch-emlua-esend1) (eepitch-emlua-eprompt)))) (defun eepitch-emlua () "Setup eepitch-ing to an inferior Lua. The \"inferior Lua\" is not a process associated to the *emlua* buffer - it is a Lua interpreter. We exchange data with it using `emlua-dostring', and we use the *emlua* buffer as a log of our communications with it. See the source of `eepitch-line-emlua' to understand what happens when we send a line to the inferior Lua." (interactive) (defalias 'emlua-do 'emlua-do-b) (prog1 (eepitch '(find-emluabuffer)) (setq eepitch-line 'eepitch-line-emlua))) '("This is a test block: * (eepitch-emlua) * (eepitch-kill) * (eepitch-emlua) print(2 + 3) print(2 + 3 + 4) = 2, 3, 4 = 2 + nil = EdrxEmacsRepl PPP(EdrxEmacsRepl) PPPV(EdrxEmacsRepl.__index) --") ;;; _ _ _ ;;; __| | ___ ___| |_ _ __(_)_ __ __ _ _ ;;; / _` |/ _ \/ __| __| '__| | '_ \ / _` |_| |_ ;;; | (_| | (_) \__ \ |_| | | | | | | (_| |_ _| ;;; \__,_|\___/|___/\__|_| |_|_| |_|\__, | |_| ;;; |___/ ;; ;; «emlua-dostring+» (to ".emlua-dostring+") ;; This is like emlua-dostring, but it treats the result as elisp code ;; that has to be eval-ed. Note: this is just a demo! Please redefine ;; this function and reuse its name! (defun emlua-dostring+ (luacode) (let ((results (emlua-dostring luacode))) (if (stringp results) (error "%s" results) (let ((result (aref results 0))) (eval (read result)))))) ;; Tests: (emlua-dostring+ "return '(+ 22 33)'") ;; Tests: (emlua-dostring+ "return '(insert \"\\n;; HELLO\")'") ;; Tests: (emlua-dostring+ "return -- no return values") ;; Tests: (emlua-dostring+ "!!! a syntax error !!!") (provide 'emlua-repl) ;; Local Variables: ;; coding: utf-8-unix ;; End: