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

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

[elpa] externals/code-cells 37dce1d908 02/36: Add existing code


From: ELPA Syncer
Subject: [elpa] externals/code-cells 37dce1d908 02/36: Add existing code
Date: Mon, 28 Feb 2022 15:57:41 -0500 (EST)

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

    Add existing code
---
 README.md | 109 ++++++++++++++++++++++++++++++++++++++++++
 cells.el  | 160 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 269 insertions(+)

diff --git a/README.md b/README.md
new file mode 100644
index 0000000000..0ebc7b12d8
--- /dev/null
+++ b/README.md
@@ -0,0 +1,109 @@
+cells.el
+========
+
+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 converting a Jupyter notebook to a
+script in the web interface or with
+
+``` shell
+jupyter nbconvert --to script <FILE.ipynb>
+```
+
+It's also worth mentioning [jupytext] and [ipynb-py-convert], which
+can convert to and from notebook format.
+
+Instead of defining every conceivable command relevant to code with
+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 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:
+
+``` elisp
+(define-key
+  python-mode-map
+  (kbd "C-c C-c")
+  (cells-command 'python-shell-send-region))
+```
+
+See below for a more substantial configuration example, which is
+specifically for Jupyter mode but can be easily adapted to any other
+mode or language with a REPL in Emacs.
+
+Also available in this package are the following; see the
+documentation for more details.
+
+- `cells-do`: a macro that makes the current cells bounds accessible
+  as variables.
+- `cells-mark-cell`: mark current cell and activate region
+- `cells-mode`: for now, just adds font locking to the cell
+  boundaries.  Also provides a keymap where one can add any desired
+  cell-related commands.
+
+Keybindings for [emacs-jupyter]
+-------------------------------
+
+The following configuration snippet sets up cell navigation and
+evaluation functions on buffers associated to a Jupyter kernel:
+
+- Navigate cells with `M-p` and `M-n`.
+- Mark, copy and kill cells by prefixing the usual command with `C-c`.
+- Evaluate a cell with `C-c C-c`, but acts on region instead if it's
+  active.
+
+``` elisp
+(with-eval-after-load "jupyter-repl"
+  (require 'cells)
+  (let ((map jupyter-repl-interaction-mode-map))
+    (define-key map (kbd "M-p") 'cells-backward-cell)
+    (define-key map (kbd "M-n") 'cells-forward-cell)
+    (define-key map (kbd "C-c C-SPC") 'cells-mark-cell)
+    (define-key map (kbd "C-c C-w") (cells-command 'kill-region :use-region))
+    (define-key map (kbd "C-c M-w") (cells-command 'kill-ring-save 
:use-region))
+    (define-key map (kbd "C-c C-c") (cells-command 'jupyter-eval-region 
:use-region :pulse))))
+```
+
+A hydra for [emacs-jupyter]
+---------------------------
+
+The following defines a handy [hydra] for Jupyter mode.  Activate it
+with `M-x notebook-hydra/body RET` or bind that command to a key in
+`jupyter-repl-interaction-mode-map`.
+
+``` elisp
+(defhydra notebook-hydra (:color red :hint nil)
+  "
+_j_/_k_: ↓/↑, _h_ome, _l_ast, _q_uit      \
+Cell: _e_val, mark and e_x_ecute      \
+Kernel: _r_estart, eval _a_bove, _z_: pop to
+"
+  ("h" beginning-of-buffer)
+  ("l" (progn (end-of-buffer)
+              (cells-backward-cell)))
+  ("j" cells-forward-cell)
+  ("k" cells-backward-cell)
+  ("z" jupyter-repl-pop-to-buffer :color blue)
+  ("x" (progn (cells-mark-cell)
+              (call-interactively 'execute-extended-command)))
+  ("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)
+                 (cells-forward-cell)))
+  ("M-w" (cells-do (kill-ring-save beg end)))
+  ("C-w" (cells-do (kill-region beg end)))
+  ("q" nil :exit t))
+```
+
+The head `x` asks for a command like `M-x`, and executes it with the
+current cell as active region.  Thus, for instance, typing `x
+comment-dwim RET` in this hydra will comment out the current cell.
+
+[jupytext]: https://github.com/mwouts/jupytext
+[ipynb-py-convert]: https://github.com/kiwi0fruit/ipynb-py-convert/
+[emacs-jupyter]: https://github.com/dzop/emacs-jupyter
+[hydra]: https://github.com/abo-abo/hydra
diff --git a/cells.el b/cells.el
new file mode 100644
index 0000000000..6ab06639ee
--- /dev/null
+++ b/cells.el
@@ -0,0 +1,160 @@
+;;; cells.el --- Utilities for code split into cells -*- lexical-binding: t; 
-*-
+
+;; Copyright (C) 2020 Augusto Stoffel
+
+;; Version: 0.0
+;; Author: Augusto Stoffel <arstoffel@gmail.com>
+;; Maintainer: Augusto Stoffel <arstoffel@gmail.com>
+;; URL: https://github.com/astoff/cells.el
+;; Keywords: convenience, cells
+;; Package-Requires: ((emacs "26.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
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; 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.
+;;
+;; 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.
+
+;;; Code:
+
+(require 'pulse)
+(require 'rx)
+
+(defgroup cells nil
+  "Utilities for code split into cells."
+  :group 'convenience
+  :prefix "cells-")
+
+(defcustom cells-cell-markers
+  '("%%"
+    (regexp "In\\s-*\\[.*?\\]"))
+  "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."
+  :type '(repeat sexp))
+
+(defface cells-header-line '((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))
+      (* (syntax whitespace))
+      (or (eval (cons 'or cells-cell-markers)))))
+
+(defun cells-forward-cell (&optional arg)
+  "Move to the next cell boundary, or end of buffer.
+With ARG, repeat this that many times.  If ARG is negative, move
+backward."
+  (interactive "p")
+  (or arg (setq arg 1))
+  (when (and (> arg 0) (not (eobp)))
+    (forward-char))
+  (save-match-data
+    (or
+     (when (re-search-forward (cells-boundary-regexp) nil t arg)
+       (goto-char (match-beginning 0)))
+     (when (< arg 0) (goto-char (point-min)))
+     (when (> arg 0) (goto-char (point-max))))))
+
+(defun cells-backward-cell (&optional arg)
+  "Move to the previous cell boundary, or beginning of buffer.
+With ARG, repeat this that many times.  If ARG is negative, move
+forward."
+  (interactive "p")
+  (cells-forward-cell (- (or arg 1))))
+  
+(defmacro cells-do (&rest body)
+  "Find current cell bounds and evaluate BODY.
+Inside BODY, the variables `beg' 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."
+  `(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)
+      ,@body)))
+
+(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)
+   (push-mark end nil t)))
+
+(defun cells-command (fun &optional docstring &rest options)
+  "Returns 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.
+
+If OPTIONS contains the keyword :pulse, provide visual feedback
+via `pulse-momentary-highlight-region'."
+  (declare (doc-string 2))
+  (unless (stringp docstring)
+    (setq options (cons docstring options))
+    (setq docstring (concat
+                     "Call `" (symbol-name fun) "' on the current code cell."
+                     (when (member :use-region options)
+                       "\nIf region is active, use it instead."))))
+  (eval `(lambda ()
+          ,docstring
+          (interactive)
+          (cells-do ,(car (member :use-region options))
+                    ,(when (member :pulse options)
+                       '(pulse-momentary-highlight-region beg end))
+                    (funcall ',fun beg end)))))
+
+(define-minor-mode cells-mode
+  "Minor mode for cell-oriented code."
+  ;; TODO: integrate with outline-mode?
+  :keymap (make-sparse-keymap)
+  (let ((spec `((,(concat "\\(" (cells-boundary-regexp) "\\).*\n")
+                 0 'cells-header-line prepend))))
+    (if cells-mode
+        (progn
+          (font-lock-add-keywords nil spec))
+      (font-lock-remove-keywords nil spec))
+    (font-lock-flush)))
+
+(provide 'cells)
+;;; cells.el ends here



reply via email to

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