From f79e501d060ab194ec9be81cb2779ad8adae4276 Mon Sep 17 00:00:00 2001 From: immerrr Date: Sun, 3 Nov 2019 16:02:14 +0100 Subject: [PATCH] Add lua-send-string-max-size and lua-send-region-chunked --- README.md | 1 + lua-mode.el | 52 +++++++++++++++++++++++++++++++++-- test/test-inferior-process.el | 37 +++++++++++++++++++++++++ 3 files changed, 88 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d2bcee5..19df3cf 100644 --- a/README.md +++ b/README.md @@ -74,3 +74,4 @@ The following variables are available for customization (see more via `M-x custo - Cmd `lua-send-defun`: send current top-level function - Cmd `lua-send-region`: send active region - Cmd `lua-restart-with-whole-file`: restart REPL and send whole buffer +- Var `lua-send-string-max-size` (default `512` for Mac OS, `nil` for other platforms): maximum size of string that can be sent to subprocess in one operation, because some platforms are implicitly limiting the buffer size and the data arrives incomplete diff --git a/lua-mode.el b/lua-mode.el index 67de071..c85369a 100644 --- a/lua-mode.el +++ b/lua-mode.el @@ -257,6 +257,11 @@ Should be a list of strings." :type 'boolean :group 'lua) +(defcustom lua-send-string-max-size (if (string-equal system-type "darwin") 512 nil) + "Maximum size of string that can be sent to inferior shell." + :type '(choice nil string) + :group 'lua) + (defcustom lua-documentation-function 'browse-url "Function used to fetch the Lua reference manual." :type `(radio (function-item browse-url) @@ -1686,8 +1691,13 @@ This function just searches for a `end' at the beginning of a line." "end") " ")) +(defun lua--send-process-init-code () + "Send initialization code to REPL." + ;; XXX: send in chunks if initialization code exceeds send-string-max-size + (lua-send-string lua-process-init-code)) + (defun lua-make-lua-string (str) - "Convert string to Lua literal." + "Convert string STR to Lua literal." (save-match-data (with-temp-buffer (insert str) @@ -1769,6 +1779,8 @@ When called interactively, switch to the process buffer." "Send STR plus a newline to the Lua process. If `lua-process' is nil or dead, start a new process first." + (when (and lua-send-string-max-size (> (length str) lua-send-string-max-size)) + (error (format "Cannot send string of length %s (max length=%s)" (length str) lua-send-string-max-size))) (unless (string-equal (substring str -1) "\n") (setq str (concat str "\n"))) (process-send-string (lua-get-create-process) str)) @@ -1830,9 +1842,45 @@ Otherwise, return START." (lua-make-lua-string region-str) (lua-make-lua-string lua-file) lineno))) - (lua-send-string command) + (if (or (null lua-send-string-max-size) (<= (length command) lua-send-string-max-size)) + (lua-send-string command) + (lua-send-region-chunked region-str lua-file lineno lua-send-string-max-size)) (when lua-always-show (lua-show-process-buffer)))) +(defun lua--split-string-into-lua-literals (str literal-max-length &optional func) + (let* (lua-literals + (func (or func (lambda (s) (setq lua-literals (cons s lua-literals))))) + chunk-begin chunk-end chunk chunk-as-lua-string) + (with-temp-buffer + (insert str) + (goto-char (point-min)) + (while (< (point) (point-max)) + (setq chunk-begin (point) + chunk-end (+ (point) literal-max-length)) + ;; For every special character that has to be quoted with a backslash, + ;; decrease payload to account for the extra character. + (while (and (< (point) (min (point-max) chunk-end)) + (re-search-forward "[\"'\\\t\\\n]" chunk-end 'noerror)) + (setq chunk-end (1- chunk-end))) + (goto-char chunk-end) + (setq chunk (buffer-substring-no-properties chunk-begin (point))) + (funcall func (lua-make-lua-string chunk)))) + (nreverse lua-literals))) + +(defun lua-send-region-chunked (region-str lua-file lineno max-size) + "Send REGION-STR from LUA-FILE at LINENO in chunks of no more than MAX-SIZE." + ;; XXX: make enable/disable-prompt-commands customizable + (let* ((disable-prompt-command "_PROMPT = ''; _PROMPT2 = ''") + (enable-prompt-command "_PROMPT = nil; _PROMPT2 = nil") + (between-quotes-max-size (- max-size 4))) + (lua-send-string disable-prompt-command) + (lua-send-string "print(''); luamode_loadstring(\"\"") + (lua--split-string-into-lua-literals + region-str between-quotes-max-size + (lambda (s) (lua-send-string (format "..%s" s)))) + (lua-send-string (format ",%s,%d)" (lua-make-lua-string lua-file) lineno)) + (lua-send-string enable-prompt-command))) + (defun lua-prompt-line () (save-excursion (save-match-data diff --git a/test/test-inferior-process.el b/test/test-inferior-process.el index 2d160a0..336eab1 100644 --- a/test/test-inferior-process.el +++ b/test/test-inferior-process.el @@ -40,6 +40,43 @@ (expect comint-prompt-regexp :to-equal "^")) (expect comint-prompt-regexp :to-equal "^"))) +(describe "Splitting Lua code into literals of specified size" + (it "splits strings into same-size Lua literals" + (expect (lua--split-string-into-lua-literals "foobarbaz" 3) + :to-equal '("'foo'" "'bar'" "'baz'"))) + + (it "leaves last lua literal shorter if necessary" + (expect (lua--split-string-into-lua-literals "foobarba" 3) + :to-equal '("'foo'" "'bar'" "'ba'")) + (expect (lua--split-string-into-lua-literals "foobarb" 3) + :to-equal '("'foo'" "'bar'" "'b'")) + (expect (lua--split-string-into-lua-literals "foobar" 3) + :to-equal '("'foo'" "'bar'"))) + + (it "escapes special characters and does not exceed max-length" + (expect (lua--split-string-into-lua-literals "foo\nbar" 3) + :to-equal '("'foo'" "'\\nb'" "'ar'"))) + + (it "cuts literal short if it cannot fit a special character" + (expect (lua--split-string-into-lua-literals "fo\nbar" 3) + :to-equal '("'fo'" "'\\nb'" "'ar'")))) + +(describe "Sending region chunked" + (it "does that" + (with-lua-buffer + (insert "x = '|01234567890|1234567890|1234567890|'") + (lua-start-process) + (let ((lua-string-max-size 20)) + (lua-send-buffer)) + (lua-send-string "print(x)") + (while (accept-process-output (lua-get-create-process) 0 200)) + (with-current-buffer lua-process-buffer + (let* ((buf (buffer-substring-no-properties (point-min) (point-max))) + (buf-lines (cdr (split-string buf "\n" nil)))) + (expect (nth 1 (nreverse buf-lines)) + :to-equal "> |01234567890|1234567890|1234567890|")))))) + + (require 'compile) (if (fboundp 'compilation--loc->file-struct)