You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
133 lines
4.5 KiB
133 lines
4.5 KiB
;;; sage-blocks.el --- Support for structuring Sage code in sheets |
|
|
|
;; Copyright (C) 2013 Johan S. R. Nielsen |
|
|
|
;; Author: Johan S. R. Nielsen <jsrn@jsrn.dk> |
|
;; Keywords: sage |
|
|
|
;;; Commentary: |
|
|
|
;; This file adds functionality which supports structuring experimental Sage |
|
;; code in "sheets", where the code is bundled in "blocks" akin to the boxes of |
|
;; the Notebook. The core is concerned with convenient handling of such |
|
;; blocks. The file injects a few keybindings into `sage-mode' as well as |
|
;; `inferior-sage-mode'. |
|
|
|
;; A block is defined by a line beginning with `sage-block-delimiter'. |
|
|
|
;;; Code: |
|
(defcustom sage-block-delimiter "###" |
|
"Any line matching the regular expression `sage-block-delimiter' at the |
|
beginning of the line is considered a start of a block. |
|
|
|
Note that '^' to match at the beginning of the line should not be added to |
|
`sage-block-delimiter'. |
|
Strange behaviour might arise if `sage-block-delimiter' matches multiple lines |
|
at a time." |
|
:type 'string |
|
:group 'sage) |
|
|
|
(defcustom sage-block-title-decorate " ---- " |
|
"When printing titles of blocks, put this decoration around the |
|
title for easy recognition" |
|
:type 'string |
|
:group 'sage) |
|
|
|
;; |
|
;; Functionality for Sage source files |
|
;; |
|
(defun sage-backward-block (arg) |
|
"Move backwards to the last beginning of a block." |
|
(interactive "p") |
|
(if (< arg 0) |
|
(sage-forward-block (- arg)) |
|
(while (and (> arg 0) |
|
(search-backward-regexp (concat "^" sage-block-delimiter) nil 'move)) |
|
(setq arg (- arg 1))))) |
|
|
|
(defun sage-forward-block (arg) |
|
"Move forwards to the next beginning of a block." |
|
(interactive "p") |
|
(if (< arg 0) |
|
(sage-backward-block (- arg)) |
|
;; If point is on a delimiter, we should skip this, so search from beginning of |
|
;; next line (this will match immediately, if next line is a delimiter) |
|
(let ((re (concat "^" sage-block-delimiter))) |
|
(when (looking-at re) |
|
(forward-line)) |
|
;; search forward: if it worked, move to begin of delimiter, otherwise end of file |
|
(while (and (> arg 0) |
|
(search-forward-regexp re nil 'move)) |
|
(setq arg (- arg 1))) |
|
;; We successfully found something so move to the beginning of the match |
|
(when (= arg 0) |
|
(goto-char (match-beginning 0)))))) |
|
|
|
(defun sage-send-current-block () |
|
"Send the block that the point is currently in to the inferior shell. |
|
Move to end of block sent." |
|
(interactive) |
|
;; Border-case: if we're standing on a delimiter, sage-backward-block will go |
|
;; to previous delimiter, but we should send from this delimiter and forwards. |
|
(sage-forward-block 1) |
|
(let* ((this-buf (current-buffer)) |
|
(enddelim (point)) |
|
(backdelim (save-excursion |
|
(sage-backward-block 1) |
|
(point))) |
|
title) |
|
;; Copy the region to a temp buffer. |
|
;; Possibly change the first line if it contains a title |
|
(with-temp-buffer |
|
(insert-buffer-substring this-buf backdelim enddelim) |
|
(goto-char (point-min)) |
|
(when (looking-at sage-block-delimiter) |
|
(progn |
|
(goto-char (match-end 0)) |
|
(setq title (buffer-substring (point) |
|
(progn (end-of-line) (point)))) |
|
(when (string-match "^ *\\([^ ].*[^ ]\\) *$" title) |
|
(setq title (match-string 1 title))) |
|
(unless (equal title "") |
|
(insert (concat "\nprint(\"" sage-block-title-decorate title sage-block-title-decorate "\")"))))) |
|
(sage-send-region (point-min) (point-max))))) |
|
|
|
(defun sage-blocks-default-keybindings () |
|
"Bind default keys for working with sage blocks. |
|
|
|
These are |
|
C-M-{ `sage-backward-block' |
|
C-M-} `sage-forward-block' |
|
C-<return> `sage-send-current-block' |
|
|
|
in `sage-mode' and in `inferior-sage-mode-map': |
|
|
|
C-<return> `sage-pull-next-block'" |
|
(define-key sage-mode-map (kbd "C-<return>") 'sage-send-current-block) |
|
(define-key sage-mode-map (kbd "C-M-{") 'sage-backward-block) |
|
(define-key sage-mode-map (kbd "C-M-}") 'sage-forward-block) |
|
(define-key inferior-sage-mode-map (kbd "C-<return>") 'sage-pull-next-block)) |
|
|
|
;; |
|
;; Functionality for the inferior shell |
|
;; |
|
(defun sage-pull-next-block () |
|
"Evaluate the next block of the last visited file in Sage mode." |
|
(interactive) |
|
;; Find the first buffer in buffer-list which is in sage-mode |
|
(let* ((lst (buffer-list)) |
|
(buf |
|
(catch 'break |
|
(while lst |
|
(if (with-current-buffer (car lst) (derived-mode-p 'sage-mode)) |
|
(throw 'break (car lst)) |
|
(setq lst (cdr lst))))))) |
|
(if buf |
|
(progn |
|
(switch-to-buffer-other-window buf) |
|
(sage-send-current-block)) |
|
(error "No sage-mode buffer found")))) |
|
|
|
(provide 'sage-blocks) |
|
|
|
;;; sage-blocks.el ends here
|
|
|