diff --git a/inf-clojure.el b/inf-clojure.el index 4d32d06..a97066b 100644 --- a/inf-clojure.el +++ b/inf-clojure.el @@ -683,16 +683,40 @@ HOST is the host the process is running on, PORT is where it's listening." (interactive "shost: \nnport: ") (inf-clojure (cons host port))) +(defun inf-clojure--forms-without-newlines (str) + "Remove newlines between toplevel forms. +STR is a string of contents to be evaluated. When sending +multiple forms to a socket repl, each newline triggers a prompt. +So we replace all newlines between top level forms but not inside +of forms." + (condition-case nil + (with-temp-buffer + (progn + (clojurec-mode) + (insert str) + (whitespace-cleanup) + (goto-char (point-min)) + (while (not (eobp)) + (while (looking-at "\n") + (delete-char 1)) + (unless (eobp) + (clojure-forward-logical-sexp)) + (unless (eobp) + (forward-char))) + (buffer-substring-no-properties (point-min) (point-max)))) + (scan-error str))) + (defun inf-clojure-eval-region (start end &optional and-go) "Send the current region to the inferior Clojure process. Sends substring between START and END. Prefix argument AND-GO means switch to the Clojure buffer afterwards." (interactive "r\nP") - ;; drops newlines at the end of the region - (let ((str (replace-regexp-in-string - "[\n]+\\'" "" - (buffer-substring-no-properties start end)))) - (inf-clojure--send-string (inf-clojure-proc) str)) + (let* ((str (buffer-substring-no-properties start end)) + ;; newlines over a socket repl between top level forms cause + ;; a prompt to be returned. so here we dump the region into a + ;; temp buffer, and delete all newlines between the forms + (formatted (inf-clojure--forms-without-newlines str))) + (inf-clojure--send-string (inf-clojure-proc) formatted)) (when and-go (inf-clojure-switch-to-repl t))) (defun inf-clojure-eval-string (code) diff --git a/test/inf-clojure-tests.el b/test/inf-clojure-tests.el index c35c9b2..635ac2c 100644 --- a/test/inf-clojure-tests.el +++ b/test/inf-clojure-tests.el @@ -48,76 +48,106 @@ (expect (inf-clojure--kw-to-symbol "::keyword") :to-equal "keyword") (expect (inf-clojure--kw-to-symbol nil) :to-equal nil))) -(describe "completion bounds at point" () +(describe "completion bounds at point" (it "computes bounds for plain-text" - (ict-with-assess-buffers - ((a (insert "plain-text"))) - (with-current-buffer a - (expect (ict-bounds-string (inf-clojure-completion-bounds-of-expr-at-point)) - :to-equal "plain-text")))) + (ict-with-assess-buffers + ((a (insert "plain-text"))) + (with-current-buffer a + (expect (ict-bounds-string (inf-clojure-completion-bounds-of-expr-at-point)) + :to-equal "plain-text")))) (it "computes bounds for @deref" - (ict-with-assess-buffers - ((a (insert "@deref"))) - (with-current-buffer a - (expect (ict-bounds-string (inf-clojure-completion-bounds-of-expr-at-point)) - :to-equal "deref")))) + (ict-with-assess-buffers + ((a (insert "@deref"))) + (with-current-buffer a + (expect (ict-bounds-string (inf-clojure-completion-bounds-of-expr-at-point)) + :to-equal "deref")))) (it "computes bounds for ^:keyword" - (ict-with-assess-buffers - ((a (insert "^:keyword"))) - (with-current-buffer a - (expect (ict-bounds-string (inf-clojure-completion-bounds-of-expr-at-point)) - :to-equal ":keyword")))) + (ict-with-assess-buffers + ((a (insert "^:keyword"))) + (with-current-buffer a + (expect (ict-bounds-string (inf-clojure-completion-bounds-of-expr-at-point)) + :to-equal ":keyword")))) (it "computes bounds for ::keyword" - (ict-with-assess-buffers - ((a (insert "::keyword"))) - (with-current-buffer a - (expect (ict-bounds-string (inf-clojure-completion-bounds-of-expr-at-point)) - :to-equal "::keyword")))) + (ict-with-assess-buffers + ((a (insert "::keyword"))) + (with-current-buffer a + (expect (ict-bounds-string (inf-clojure-completion-bounds-of-expr-at-point)) + :to-equal "::keyword")))) (it "computes bounds for [^:keyword (combined break chars and keyword)" - (ict-with-assess-buffers - ((a (insert "[^:keyword"))) - (with-current-buffer a - (expect (ict-bounds-string (inf-clojure-completion-bounds-of-expr-at-point)) - :to-equal ":keyword")))) + (ict-with-assess-buffers + ((a (insert "[^:keyword"))) + (with-current-buffer a + (expect (ict-bounds-string (inf-clojure-completion-bounds-of-expr-at-point)) + :to-equal ":keyword")))) (it "computes no bounds for point directly after a break expression" - (ict-with-assess-buffers - ((a (insert "@"))) - (with-current-buffer a - (expect - (ict-bounds-string (inf-clojure-completion-bounds-of-expr-at-point)) - :not :to-be nil)))) + (ict-with-assess-buffers + ((a (insert "@"))) + (with-current-buffer a + (expect + (ict-bounds-string (inf-clojure-completion-bounds-of-expr-at-point)) + :not :to-be nil)))) (it "computes bounds for [symbol" - (ict-with-assess-buffers - ((a (insert "[symbol"))) - (with-current-buffer a - (expect (ict-bounds-string (inf-clojure-completion-bounds-of-expr-at-point)) - :to-equal "symbol")))) + (ict-with-assess-buffers + ((a (insert "[symbol"))) + (with-current-buffer a + (expect (ict-bounds-string (inf-clojure-completion-bounds-of-expr-at-point)) + :to-equal "symbol")))) (it "computes bounds for (@deref (multiple break chars)" - (ict-with-assess-buffers - ((a (insert "(@deref"))) - (with-current-buffer a - (expect (ict-bounds-string (inf-clojure-completion-bounds-of-expr-at-point)) - :to-equal "deref"))))) + (ict-with-assess-buffers + ((a (insert "(@deref"))) + (with-current-buffer a + (expect (ict-bounds-string (inf-clojure-completion-bounds-of-expr-at-point)) + :to-equal "deref"))))) (describe "inf-clojure--sanitize-command" (it "sanitizes the command correctly" - (expect (inf-clojure--sanitize-command "(doc println)") :to-equal "(doc println)\n")) + (expect (inf-clojure--sanitize-command "(doc println)") :to-equal "(doc println)\n")) (it "trims newline at the right of a command" - (expect (inf-clojure--sanitize-command "(doc println)\n\n\n\n") :to-equal "(doc println)\n")) + (expect (inf-clojure--sanitize-command "(doc println)\n\n\n\n") :to-equal "(doc println)\n")) (it "returns empty string when the command is empty" - (expect (inf-clojure--sanitize-command " ") :to-equal "")) + (expect (inf-clojure--sanitize-command " ") :to-equal "")) (it "only removes whitespace at the end of the command - fix 152" - (expect (inf-clojure--sanitize-command "1 5") :to-equal "1 5\n"))) + (expect (inf-clojure--sanitize-command "1 5") :to-equal "1 5\n"))) + +(describe "inf-clojure--forms-without-newlines" + (it "removes newlines between toplevel forms" + (expect (inf-clojure--forms-without-newlines + "(def foo 3)\n\n\n(def bar 4)") + :to-equal "(def foo 3)\n(def bar 4)")) + (it "doesn't remove newlines inside forms or strings" + (expect (inf-clojure--forms-without-newlines + " + +(defn foo [] + + :foo) + + +(def thing \"this + +is a string\") + +(defn bar [])") + ;; note no leading newline, newlines inside defn remain, + ;; newlines inside string remain + :to-equal "(defn foo [] + + :foo) +(def thing \"this + +is a string\") +(defn bar [])"))) + (describe "inf-clojure--update-feature" (it "updates new forms correctly"