From a9ae0bcef52a62cf7df520756d994162a0570156 Mon Sep 17 00:00:00 2001 From: Nathaniel Nicandro Date: Wed, 8 Apr 2020 03:19:07 -0500 Subject: [PATCH] Replace `jupyter-repl-cell-cond` with `jupyter-repl-map-cells` * jupyter-repl.el (jupyter-repl-map-cells): New function. (jupyter-repl-cell-cond): Remove. Update callers with new function. * jupyter-julia.el: Ditto. * test/jupyter-test.el (jupyter-repl-map-cells): New test. (jupyter-repl-cell-cond): Remove. --- jupyter-julia.el | 7 +-- jupyter-repl.el | 119 ++++++++++++++++++++----------------------- test/jupyter-test.el | 15 +++--- 3 files changed, 65 insertions(+), 76 deletions(-) diff --git a/jupyter-julia.el b/jupyter-julia.el index 037de8b0..e3d92b47 100644 --- a/jupyter-julia.el +++ b/jupyter-julia.el @@ -171,8 +171,8 @@ nil." ;;; REPL font lock (defun jupyter-julia--propertize-repl-mode-char (beg end) - (jupyter-repl-cell-cond - beg end + (jupyter-repl-map-cells beg end + (lambda () ;; Handle Julia package prompt so `syntax-ppss' works properly. (when (and (eq (char-syntax (char-after (point-min))) ?\)) (= (point-min) @@ -182,7 +182,8 @@ nil." ;; which is why the widen is needed here. (jupyter-repl-cell-code-beginning-position)))) (put-text-property - (point-min) (1+ (point-min)) 'syntax-table '(1 . ?.))))) + (point-min) (1+ (point-min)) 'syntax-table '(1 . ?.)))) + #'ignore)) ;;; `jupyter-repl-after-init' diff --git a/jupyter-repl.el b/jupyter-repl.el index b31bc645..43644f7d 100644 --- a/jupyter-repl.el +++ b/jupyter-repl.el @@ -264,42 +264,6 @@ The cell is narrowed to the region between and including (jupyter-repl-cell-code-end-position)) ,@body))) -(defmacro jupyter-repl-cell-cond (beg end input-form &rest output-forms) - "Conditionally evaluate INPUT-FORM or OUTPUT-FORMS between BEG and END. -INPUT-FORM is evaluated for every input cell between BEG and END -with the buffer narrowed to the region of the input cell. - -Similarly, OUTPUT-FORMS is evaluated for every output cell region -with the buffer narrowed to the output cell. - -Note the narrowed regions may not be full input/output cells if -BEG and END are within an input/output cell." - (declare (indent 3) (debug ([&or symbolp form] [&or symbolp form] - form &rest form))) - (let ((start (make-symbol "start")) - (next (make-symbol "next")) - (-end (make-symbol "-end"))) - `(save-excursion - (save-restriction - (let ((,start ,beg) - (,-end ,end) - ,next) - (while (/= ,start ,-end) - (widen) - (cond - ((eq (get-text-property ,start 'field) 'cell-code) - (setq ,next (min ,-end (field-end ,start t))) - (narrow-to-region ,start ,next) - ,input-form) - (t - (setq ,next (or (text-property-any - ,start ,-end 'field 'cell-code) - ,-end)) - ,@(when output-forms - `((narrow-to-region ,start ,next) - ,@output-forms)))) - (setq ,start ,next))))))) - (defmacro jupyter-repl-inhibit-undo-when (cond &rest body) "Evaluate BODY, disabling undo beforehand if COND is non-nil. Undo is re-enabled after BODY is evaluated. @@ -670,6 +634,33 @@ ARG is the number of cells to move and defaults to 1." (jupyter-repl-previous-cell arg) (goto-char (jupyter-repl-cell-code-beginning-position))) +(defun jupyter-repl-map-cells (beg end input output) + "Call INPUT or OUTPUT on the corresponding cells between BEG and END. +For every input or output cell between BEG and END, call INPUT or +OUTPUT, respectively, with the buffer narrowed to the cell. +INPUT and OUTPUT are functions of no arguments. + +Note the narrowed regions may not be full input/output cells if +BEG and END are within an input/output cell." + (declare (indent 2)) + (save-excursion + (save-restriction + (let (next) + (while (/= beg end) + (widen) + (cond + ((eq (get-text-property beg 'field) 'cell-code) + (setq next (min end (field-end beg t))) + (narrow-to-region beg next) + (funcall input)) + (t + (setq next (or (text-property-any + beg end 'field 'cell-code) + end)) + (narrow-to-region beg next) + (funcall output))) + (setq beg next)))))) + ;;; Predicates (defun jupyter-repl-cell-beginning-p (&optional pos) @@ -1779,33 +1770,30 @@ would look like "Use FONTIFY-FUN to fontify input cells between BEG and END. VERBOSE has the same meaning as in `font-lock-fontify-region-function'." - (jupyter-repl-cell-cond - beg end - ;; Ensure that the buffer is narrowed to the actual cell code before - ;; calling the REPL language's `major-mode' specific fontification - ;; functions since those functions don't know anything about input cells - ;; or output cells and may traverse cell boundaries. - ;; - ;; - ;; It is OK that we do not update BEG and END using the return value of - ;; this function as long as the default value of - ;; `font-lock-extend-region-functions' is used since an input cell always - ;; starts at the beginning of a line and ends at the end of a line and - ;; does not use the font-lock-multiline property (2018-12-20). - (funcall fontify-fun (point-min) (point-max) verbose) - ;; Unfontify the region mainly to remove the font-lock-multiline property - ;; in the output, e.g. added by markdown. These regions will get - ;; highlighted syntactically in some scenarios. - (font-lock-unfontify-region (point-min) (point-max))) + (jupyter-repl-map-cells beg end + ;; Ensure that the buffer is narrowed to the actual cell code before calling + ;; the REPL language's `major-mode' specific fontification functions since + ;; those functions don't know anything about input cells or output cells and + ;; may traverse cell boundaries. + ;; + ;; It is OK that we do not update BEG and END using the return value of this + ;; function as long as the default value of + ;; `font-lock-extend-region-functions' is used since an input cell always + ;; starts at the beginning of a line and ends at the end of a line and does + ;; not use the font-lock-multiline property (2018-12-20). + (lambda () (funcall fontify-fun (point-min) (point-max) verbose)) + ;; Unfontify the region mainly to remove the font-lock-multiline property in + ;; the output, e.g. added by markdown. These regions will get highlighted + ;; syntactically in some scenarios. + (lambda () (font-lock-unfontify-region (point-min) (point-max)))) `(jit-lock-bounds ,beg . ,end)) (defun jupyter-repl-syntax-propertize-function (propertize-fun beg end) "Use PROPERTIZE-FUN to syntax propertize text between BEG and END." - (jupyter-repl-cell-cond - beg end - ;; See note in `jupyter-repl-font-lock-fontify-region' on why the buffer - ;; should be narrowed to the input cell before calling this function. - (funcall propertize-fun (point-min) (point-max)) + (jupyter-repl-map-cells beg end + ;; See note in `jupyter-repl-font-lock-fontify-region' on why the buffer + ;; should be narrowed to the input cell before calling this function. + (lambda () (funcall propertize-fun (point-min) (point-max))) ;; Treat parenthesis and string characters as punctuation when parsing the ;; syntax of the output. Although we don't fontify output regions, ;; `syntax-ppss' still looks at the whole contents of the buffer. If there @@ -1813,12 +1801,13 @@ VERBOSE has the same meaning as in ;; interfere with `syntax-ppss'. Note, this requires ;; `parse-sexp-lookup-properties' to be non-nil so that `syntax-ppss' will ;; look at the `syntax-table' property. - (goto-char (point-min)) - (skip-syntax-forward "^()\"") - (while (not (eobp)) - (put-text-property (point) (1+ (point)) 'syntax-table '(1 . ?.)) - (forward-char) - (skip-syntax-forward "^()\"")))) + (lambda () + (goto-char (point-min)) + (skip-syntax-forward "^()\"") + (while (not (eobp)) + (put-text-property (point) (1+ (point)) 'syntax-table '(1 . ?.)) + (forward-char) + (skip-syntax-forward "^()\""))))) (defun jupyter-repl-font-lock-syntactic-face-function (face-fun state) "Narrow to the input cell, use FACE-FUN to obtain the face given STATE." diff --git a/test/jupyter-test.el b/test/jupyter-test.el index 93bb63d2..42f2c6ea 100644 --- a/test/jupyter-test.el +++ b/test/jupyter-test.el @@ -1578,19 +1578,18 @@ last element being the newest element added to the history." (should-error (jupyter-repl-cell-beginning-position)) (should-error (jupyter-repl-cell-end-position))))) -(ert-deftest jupyter-repl-cell-cond () +(ert-deftest jupyter-repl-map-cells () :tags '(repl) (with-temp-buffer (insert "foo") (insert (propertize "bar" 'field 'cell-code)) (insert "baz") - (jupyter-repl-cell-cond - (point-min) (point-max) - (should (equal (buffer-string) "bar")) - (should (member (buffer-string) '("foo" "baz")))) - (jupyter-repl-cell-cond - (point-min) (point-max) - (should (equal (buffer-string) "bar"))))) + (let (input output) + (jupyter-repl-map-cells (point-min) (point-max) + (lambda () (push (buffer-string) input)) + (lambda () (push (buffer-string) output))) + (should (equal input '("bar"))) + (should (equal output '("baz" "foo")))))) (ert-deftest jupyter-repl-restart-kernel () :tags '(repl restart)