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

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

[elpa] externals/code-cells e02bda5b68 09/36: Assorted refinements


From: ELPA Syncer
Subject: [elpa] externals/code-cells e02bda5b68 09/36: Assorted refinements
Date: Mon, 28 Feb 2022 15:57:41 -0500 (EST)

branch: externals/code-cells
commit e02bda5b68f707875c562672346df643a8ced456
Author: Augusto Stoffel <astoff@users.noreply.github.com>
Commit: Augusto Stoffel <astoff@users.noreply.github.com>

    Assorted refinements
---
 README.md |  76 +++++++++++++++++++----------------
 cells.el  | 136 ++++++++++++++++++++++++++++++--------------------------------
 2 files changed, 107 insertions(+), 105 deletions(-)

diff --git a/README.md b/README.md
index 09a3d89082..ab431114d3 100644
--- a/README.md
+++ b/README.md
@@ -2,15 +2,13 @@ cells.el
 ========
 
 This package lets you efficiently navigate, edit and execute code
-split into cells according to certain magic comments.
+split into cells according to certain magic comments.  If you have
+[Jupytext] or [Pandoc] installed, you can also open ipynb notebook
+files directly in Emacs.  They will be automatically converted to a
+script for editing, and converted back to notebook format when saving.
 
 
![Screenshot](https://user-images.githubusercontent.com/6500902/102713720-8c52dd80-42ca-11eb-8ea6-4e96a814be2b.png)
 
-If you have [Jupytext] or [Pandoc] installed, you can also directly
-open ipynb notebook files in Emacs.  They will be automatically
-converted to a script for editing, and converted back to notebook
-format when saving.
-
 By default, three styles of comments are recognized as cell boundaries:
 
 ```
@@ -22,10 +20,12 @@ By default, three styles of comments are recognized as cell 
boundaries:
 ```
 
 The first is what you get by exporting a notebook to a script on
-Jupyter's web interface or with the command `jupyter nbconvert --to
-python <FILE.ipynb>`.  The second style is compatible with Jupytext,
-among several other tools.  The third is in the spirit of Emacs's
-outline mode.
+Jupyter's web interface or with the command `jupyter nbconvert`.  The
+second style is compatible with Jupytext, among several other tools.
+The third is in the spirit of Emacs's outline mode.  Further percent
+signs or asterisks signify nested cells.  In fact, `cells-mode`
+doubles as a general-purpose enhancement of `outline-minor-mode`; see
+below for details.
 
 Editing commands
 ----------------
@@ -35,14 +35,15 @@ cells, this package provides a simple way to turn ordinary 
editing
 commands into “cell-aware” ones.  The `cells-command` function takes
 as argument a function that acts on a region, and returns an anonymous
 command that acts on the current cell.  Thus, one can redefine `C-c
-C-c` in Python mode to evaluate the current cell by doing the
+C-r` in Python mode to evaluate the current cell by doing the
 following:
 
 ``` elisp
 (define-key python-mode-map
-            (kbd "C-c C-c")
+            (kbd "C-c C-r")
             (cells-command 'python-shell-send-region))
 ```
+
 There is also a `cells-do` macro, which evaluates its body with the
 current cell bounds accessible as variables.  See the documentation
 for details.
@@ -54,9 +55,9 @@ Besides that, only three editing commands are provided:
 - `cells-mark-cell` marks the current cell and activates the region.
 
 Everything else is left up for the user to define with the mechanism
-explained above.  See below for a more substantial configuration
-example, which is specific for Jupyter mode but can be easily adapted
-to any other mode or language with a REPL in Emacs.
+explained above.  See below for more substantial configuration
+examples, some specific to Jupyter and others applicable to any mode
+or language with a REPL in Emacs.
 
 Minor mode
 ----------
@@ -67,10 +68,17 @@ A minor-mode, `cells-mode`, provides the following things:
 - The `cells-mode-map` keymap, a good place for your very own cell
   commands.
 - Outline mode integration: cell headers have outline level determined
-  by the number of percent signs or asterisks; within a cell, the
-  outline level is as determined by the major mode.  This provides
-  code folding and hierarchical navigation, among other things, when
-  `outline-minor-mode` is active.
+  by the number of percent signs or asterisks; within a cell, outline
+  headings are as determined by the major mode, but they are demoted
+  by an amount corresponding to the level of the containing cell.
+  This provides code folding and hierarchical navigation, among other
+  things, when `outline-minor-mode` is active.
+
+Many major modes provide an outline hierarchy based on code structure,
+and some people prefer to replace this with a hierarchy based on
+sectioning comments.  With `cells-mode` you get both things at the
+same time.  This may be useful even for code that is not organized as
+a notebook.
 
 Handling Jupyter notebook files
 -------------------------------
@@ -81,16 +89,16 @@ JSON-based ipynb format is done by an external tool, 
[Jupytext] by
 default, which needs to be installed separately.
 
 **Important notice:** The automatic format conversion on save hasn't
-been thoroughly tested.  In particular, it (probably) doesn't handle
-backups in the expected way.  Do not rely on its correctness!
+been thoroughly tested.  In particular, it might not handle backups in
+the expected way.  Do not rely on its correctness!
 
 Note also that the result cells of ipynb files are not retained in the
 conversion to script format.  This means that opening and then saving
 an ipynb file clears all cell outputs.
 
-Within an ipynb buffer, you can use the regular `write-file` command
-(`C-x C-w`) to save a copy in script format, as displayed on the
-screen.  Moreover, from any script file with cell separators
+With a converted ipynb buffer, you can use the regular `write-file`
+command (`C-x C-w`) to save a copy in script format, as displayed on
+the screen.  Moreover, from any script file with cell separators
 understood by Jupytext, you can call `cells-write-ipynb` to save a
 copy in notebook format.
 
@@ -100,7 +108,7 @@ Configuration examples
 ### Keybindings for cells-mode
 
 The following configuration snippet sets up cell navigation and
-evaluation functions when `cells-modes` is enabled.  Just adapt it to
+evaluation functions when `cells-mode` is enabled.  Just adapt it to
 your liking.
 
 - Navigate cells with `M-p` and `M-n`.
@@ -108,7 +116,7 @@ your liking.
 - Evaluate a cell with the same key combinations that would otherwise
   evaluate the region (but still acting on the region instead if it is
   active, as stipulated by the `:use-region` flag).  This is done for
-  a couple of different major modes via key remaps, which see.
+  a couple of different modes via key remaps, which see.
 
 ``` elisp
 (require 'cells)
@@ -147,15 +155,15 @@ Kernel: _r_estart, eval _a_bove, _z_: pop to
   ("z" jupyter-repl-pop-to-buffer :color blue)
   ("x" (progn (cells-mark-cell)
               (call-interactively 'execute-extended-command)))
-  ("SPC" cells-mark-cell)
+  ("C-SPC" cells-mark-cell)
   ("r" jupyter-repl-restart-kernel)
-  ("a" (cells-do (pulse-momentary-highlight-region (point-min) beg)
-                 (jupyter-eval-region (point-min) beg)))
-  ("e" (cells-do (pulse-momentary-highlight-region beg end)
-                 (jupyter-eval-region beg end)
+  ("a" (cells-do (pulse-momentary-highlight-region (point-min) start)
+                 (jupyter-eval-region (point-min) start)))
+  ("e" (cells-do (pulse-momentary-highlight-region start end)
+                 (jupyter-eval-region start end)
                  (cells-forward-cell)))
-  ("M-w" (cells-do (kill-ring-save beg end)))
-  ("C-w" (cells-do (kill-region beg end)))
+  ("M-w" (cells-do (kill-ring-save start end)))
+  ("C-w" (cells-do (kill-region start end)))
   ("q" nil :exit t))
 ```
 
@@ -164,7 +172,7 @@ the current cell as active region.  Thus, for instance, 
typing `x
 comment-dwim RET` in this hydra will comment out the current cell.
 
 Note that since `defhydra` is a macro and wraps the definition of a
-key in an interactive lambda when it's a sexp, we need to use
+key in an interactive lambda when it is a sexp, we need to use
 `cells-do` instead of `cells-command` above.
 
 ### Tweaking the ipynb conversion
diff --git a/cells.el b/cells.el
index b84a49c1d9..e650996ba4 100644
--- a/cells.el
+++ b/cells.el
@@ -1,13 +1,11 @@
-;;; cells.el --- Utilities for code split into cells -*- lexical-binding: t; 
-*-
+;;; cells.el --- Work with code split into cells and Jupyter notebooks -*- 
lexical-binding: t; -*-
 
 ;; Copyright (C) 2020 Augusto Stoffel
 
-;; Version: 0.0
 ;; Author: Augusto Stoffel <arstoffel@gmail.com>
-;; Maintainer: Augusto Stoffel <arstoffel@gmail.com>
+;; Keywords: convenience, outlines
 ;; URL: https://github.com/astoff/cells.el
-;; Keywords: convenience, cells
-;; Package-Requires: ((emacs "26.1"))
+;; Package-Requires: ((emacs "27.1"))
 
 ;; This program is free software; you can redistribute it and/or modify
 ;; it under the terms of the GNU General Public License as published by
@@ -24,24 +22,20 @@
 
 ;;; Commentary:
 
-;; This package lets you efficiently navigate, edit and execute code
-;; split into cells according to certain magic comments.  Such files
-;; can be obtained, for instance, by exporting a Jupyter notebook to a
-;; script.
+;; With this package, you can efficiently navigate, edit and execute
+;; code split into cells according to certain magic comments.  It also
+;; allows to open ipynb notebook files directly in Emacs.  They will
+;; be automatically converted to a script for editing, and converted
+;; back to notebook format when saving.  An external tool, Jupytext by
+;; default, is required for this.
 ;;
-;; The simplest entry point of the package is the `cells-command'
-;; function.  It takes as argument a command that can act on a region,
-;; and returns an anonymous command that acts on the current cell.
-;; Thus, one can redefine C-c C-c in Python mode to evaluate the
-;; current cell by doing the following:
-;;
-;;     (define-key
-;;       python-mode-map
-;;       (kbd "C-c C-c")
-;;       (cells-command 'python-shell-send-region))
-;;
-;; See the README for more examples, including a ready-to-use setup
-;; for Jupyter and a handy hydra.
+;; Out of the box, there are no keybindings and, in fact, only a small
+;; number of editing commands is provided.  Rather, the idea is that
+;; you can create your own cell-aware commands from regular ones
+;; through the `cells-command' function and the `cells-do' macro.  See
+;; the README for configuration examples.  There is also a
+;; `cells-mode' minor mode, which, among other things, provides
+;; outline support.
 
 ;;; Code:
 
@@ -56,10 +50,10 @@
 
 ;;* Cell navigation
 
-(defcustom cells-cell-markers
-  '((regexp "\\s-*%\\(%+\\)")
-    (regexp "\\(\\*+\\)")
-    (regexp " In\\s-*\\[.*?\\]"))
+(defcustom cells-boundary-markers
+  '((seq (* space) "%" (group (+ "%")))
+    (group (+ "*"))
+    (seq " In[" (* (any space digit)) "]:"))
   "A list of regular expressions in sexp form (see `rx').
 Each of regexp should match the content of a comment line which
 introduces a cell break.
@@ -67,14 +61,11 @@ introduces a cell break.
 The length of the first capture determines the outline level."
   :type '(repeat sexp))
 
-(defface cells-header-line '((t :extend t :inherit header-line))
-  "Face used by `cells-mode' to highlight cell boundaries.")
-
 (defun cells-boundary-regexp ()
   "Return a regexp matching comment lines that serve as cell boundary."
   (rx line-start
       (+ (syntax comment-start))
-      (eval (cons 'or cells-cell-markers))))
+      (eval (cons 'or cells-boundary-markers))))
 
 ;;;###autoload
 (defun cells-forward-cell (&optional arg)
@@ -97,39 +88,39 @@ forward."
 ;;;###autoload
 (defmacro cells-do (&rest body)
   "Find current cell bounds and evaluate BODY.
-Inside BODY, the variables `beg' and `end' are bound to the
+Inside BODY, the variables `start' and `end' are bound to the
 limits of the current cell.
 
 If the first element of BODY is the keyword `:use-region' and the
-region is active, use its bounds instead."
+region is active, use its bounds instead.  In this case,
+`using-region' is non-nil in BODY."
   `(pcase (if (and ,(eq (car body) :use-region) (use-region-p))
               (list t (region-end) (region-beginning))
             (save-excursion
               (list nil
                     (progn (cells-forward-cell) (point))
                     (progn (cells-backward-cell) (point)))))
-     (`(,using-region ,end ,beg)
+     (`(,using-region ,end ,start)
       ,@body)))
 
 ;;;###autoload
 (defun cells-mark-cell ()
   "Put point at the beginning of this cell, mark at end."
-  ;; TODO: add arg; extend region when active
   (interactive)
   (cells-do
-   (goto-char beg)
+   (goto-char start)
    (push-mark end nil t)))
 
 ;;;###autoload
 (defun cells-command (fun &optional docstring &rest options)
-  "Returns an anonymous command that calls FUN on the current cell.
+  "Return an anonymous command that calls FUN on the current cell.
 
 FUN is a function that takes two character positions as argument.
 Most interactive commands that act on a region are of this form
 and can be used here.
 
 If OPTIONS contains the keyword :use-region, the command will act
-on the region instead of the current cell if appropriate.
+on the region instead of the current cell when appropriate.
 
 If OPTIONS contains the keyword :pulse, provide visual feedback
 via `pulse-momentary-highlight-region'."
@@ -145,22 +136,22 @@ via `pulse-momentary-highlight-region'."
           (interactive)
           (cells-do ,(car (member :use-region options))
                     ,(when (member :pulse options)
-                       '(pulse-momentary-highlight-region beg end))
-                    (funcall ',fun beg end)))))
+                       '(pulse-momentary-highlight-region start end))
+                    (funcall ',fun start end)))))
 
-;;* Outline support
+;;* Minor mode
 
-(defvar-local cells--previous-state nil
+(defvar-local cells--saved-vars nil
   "A place to save variables before activating `cells-mode'.")
 
 (defun cells--outline-level ()
   "The `outline-level' function used by `cells-mode'.
 At a cell boundary, returns the cell outline level, as determined
-by `cells-cell-markers'.  Otherwise, returns the sum of the
+by `cells-boundary-markers'.  Otherwise, returns the sum of the
 outline level as determined by the major mode and the current
 cell level."
   (let* ((at-boundary (looking-at-p (cells-boundary-regexp)))
-         (mm-level (if at-boundary 0 (funcall (car cells--previous-state))))
+         (mm-level (if at-boundary 0 (funcall (car cells--saved-vars))))
          (cell-level (save-excursion
                        (unless at-boundary (cells-backward-cell))
                        (if (match-string 1)
@@ -168,30 +159,34 @@ cell level."
                          1))))
     (+ cell-level mm-level)))
 
-;;* Minor mode
+(defface cells-header-line '((t :extend t :inherit header-line))
+  "Face used by `cells-mode' to highlight cell boundaries.")
+
+(defun cells--font-lock-keywords ()
+  "Font lock keywords to highlight cell boundaries."
+  `((,(concat "\\(" (cells-boundary-regexp) "\\).*\n")
+     0 'cells-header-line append)))
 
 ;;;###autoload
 (define-minor-mode cells-mode
   "Minor mode for cell-oriented code."
   :keymap (make-sparse-keymap)
-  (let ((spec `((,(concat "\\(" (cells-boundary-regexp) "\\).*\n")
-                 0 'cells-header-line append))))
-    (if cells-mode
-        (progn
-          (require 'outline)
-          (setq-local cells--previous-state (list outline-level
-                                                  outline-regexp
-                                                  outline-heading-end-regexp)
-                      outline-level 'cells--outline-level
-                      outline-regexp (rx (or (regexp (cells-boundary-regexp))
-                                             (regexp outline-regexp)))
-                      outline-heading-end-regexp "\n")
-          (font-lock-add-keywords nil spec))
-      (setq-local outline-level (nth 0 cells--previous-state)
-                  outline-regexp (nth 1 cells--previous-state)
-                  outline-heading-end-regexp (nth 2 cells--previous-state))
-      (font-lock-remove-keywords nil spec))
-    (font-lock-flush)))
+  (if cells-mode
+      (progn
+        (require 'outline)
+        (setq-local cells--saved-vars (list outline-level
+                                            outline-regexp
+                                            outline-heading-end-regexp)
+                    outline-level 'cells--outline-level
+                    outline-regexp (rx (or (regexp (cells-boundary-regexp))
+                                           (regexp outline-regexp)))
+                    outline-heading-end-regexp "\n")
+        (font-lock-add-keywords nil (cells--font-lock-keywords)))
+    (setq-local outline-level (nth 0 cells--saved-vars)
+                outline-regexp (nth 1 cells--saved-vars)
+                outline-heading-end-regexp (nth 2 cells--saved-vars))
+    (font-lock-remove-keywords nil (cells--font-lock-keywords)))
+  (font-lock-flush))
 
 ;;* Jupyter notebook conversion
 
@@ -238,14 +233,13 @@ program name followed by arguments."
 (defun cells-convert-ipynb ()
   "Convert buffer from ipynb format to a regular script."
   (goto-char (point-min))
-  (let* ((file (buffer-file-name))
-         (nb (json-parse-buffer))
+  (let* ((nb (json-parse-buffer))
          (pt (point))
          (lang (or (map-nested-elt nb '("metadata" "kernelspec" "language"))
                    (map-nested-elt nb '("metadata" "jupytext" 
"main_language"))))
-         (mode (or (caddr cells-convert-ipynb-style)
+         (mode (or (nth 2 cells-convert-ipynb-style)
                    (intern (concat lang "-mode"))))
-         (exit (cells--call-process t (cadr cells-convert-ipynb-style))))
+         (exit (cells--call-process t (nth 1 cells-convert-ipynb-style))))
     (unless (eq 0 exit)
       (delete-region pt (point-max))
       (error "Error converting notebook (exit code %s)" exit))
@@ -254,17 +248,17 @@ program name followed by arguments."
     (setq-local write-file-functions '(cells-write-ipynb))
     (when (fboundp mode)
       (funcall mode)
-      (run-hooks (cadddr cells-convert-ipynb-style)))))
+      (run-hooks (nth 3 cells-convert-ipynb-style)))))
 
 ;;;###autoload
 (defun cells-write-ipynb (&optional file)
-  "Convert buffer to ipynb file and write to FILE.
-Interactively, asks for the file name.  Called from Lisp, FILE
-defaults to the current buffer file name."
+  "Convert buffer to ipynb format and write to FILE.
+Interactively, asks for the file name.  When called from Lisp,
+FILE defaults to the current buffer file name."
   (interactive "F")
   (let* ((file (or file buffer-file-name))
          (temp (generate-new-buffer " *cells--call-process output*"))
-         (exit (cells--call-process temp (car cells-convert-ipynb-style))))
+         (exit (cells--call-process temp (nth 0 cells-convert-ipynb-style))))
     (unless (eq 0 exit)
         (error "Error converting notebook (exit code %s)" exit))
     (with-current-buffer temp



reply via email to

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