[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
- [elpa] externals/code-cells d03621b103 19/36: Change screenshot, (continued)
- [elpa] externals/code-cells d03621b103 19/36: Change screenshot, ELPA Syncer, 2022/02/28
- [elpa] externals/code-cells 374edc6be7 20/36: Change package description, ELPA Syncer, 2022/02/28
- [elpa] externals/code-cells 1bd650391a 25/36: Update README, ELPA Syncer, 2022/02/28
- [elpa] externals/code-cells 4e973e0122 26/36: Do not assume Emacs has native JSON parsing available, ELPA Syncer, 2022/02/28
- [elpa] externals/code-cells 185c33b8b6 27/36: Fix cell movement for files without newline at the end, ELPA Syncer, 2022/02/28
- [elpa] externals/code-cells 031f726941 30/36: code-cells-eval: Fix check for a possibly active minor mode, ELPA Syncer, 2022/02/28
- [elpa] externals/code-cells ea7799c447 35/36: Release on ELPA, ELPA Syncer, 2022/02/28
- [elpa] externals/code-cells 37dce1d908 02/36: Add existing code, ELPA Syncer, 2022/02/28
- [elpa] externals/code-cells 30c0359c36 03/36: Simplify motion code, ELPA Syncer, 2022/02/28
- [elpa] externals/code-cells aeddd889c2 04/36: Add ipynb support via Jupytext, ELPA Syncer, 2022/02/28
- [elpa] externals/code-cells e02bda5b68 09/36: Assorted refinements,
ELPA Syncer <=
- [elpa] externals/code-cells aefabc4abf 10/36: Comments about ipynb conversion settings, ELPA Syncer, 2022/02/28
- [elpa] externals/code-cells 7bad8f1cf6 11/36: Speed keys, ELPA Syncer, 2022/02/28
- [elpa] externals/code-cells 2dc51b44a3 12/36: Correct outline level before first cell header, ELPA Syncer, 2022/02/28
- [elpa] externals/code-cells 9500e07f83 15/36: Fix byte-compilation issue, ELPA Syncer, 2022/02/28
- [elpa] externals/code-cells adda62ec82 16/36: Address some packaging issues, ELPA Syncer, 2022/02/28
- [elpa] externals/code-cells 67e1479a75 28/36: Add code-cells-mode-maybe, ELPA Syncer, 2022/02/28
- [elpa] externals/code-cells f93db2d65c 29/36: Refinement in Commentary section, ELPA Syncer, 2022/02/28
- [elpa] externals/code-cells 47305f5439 31/36: Indicate that spaces are allowed before cell boundary marker, ELPA Syncer, 2022/02/28
- [elpa] externals/code-cells 68148cfc1f 33/36: Improve cell boundary face, ELPA Syncer, 2022/02/28
- [elpa] externals/code-cells f5150fc213 34/36: Update README.md, ELPA Syncer, 2022/02/28