Files
emacs/lisp/ess/ess-trns.el
2020-12-05 21:29:49 +01:00

233 lines
9.2 KiB
EmacsLisp
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
;;; ess-trns.el --- Support for manipulating S transcript files -*- lexical-binding: t; -*-
;; Copyright (C) 1989-2020 Free Software Foundation, Inc.
;; Author: David Smith <dsmith@stats.adelaide.edu.au>
;; Maintainer: ESS-core <ESS-core@r-project.org>
;; This file is part of GNU Emacs.
;;; License:
;;
;; 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:
;; Code for dealing with ESS transcripts.
;;; Code:
; Requires and autoloads
(require 'ess-mode)
(require 'ess-inf)
(require 'comint)
(declare-function ess-display-help-on-object "ess-help" (object &optional command))
; ess-transcript-mode
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; In this section:
;;;;
;;;; * The major mode ess-transcript-mode
;;;; * Commands for ess-transcript-mode
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defcustom ess-transcript-mode-hook nil
"Hook for customizing ESS transcript mode."
:group 'ess-hooks
:type 'hook)
;;*;; Major mode definition
(defvar ess-transcript-mode-map
(let ((map (make-sparse-keymap)))
(define-key map "\C-c\C-s" #'ess-switch-process)
(define-key map "\C-c\C-r" #'ess-eval-region)
(define-key map "\C-c\M-r" #'ess-eval-region-and-go)
(define-key map "\C-c\C-k" #'ess-force-buffer-current)
(define-key map "\C-c\C-q" #'ess-quit)
(define-key map "\C-c\C-j" #'ess-transcript-send-command)
(define-key map "\C-c\M-j" #'ess-transcript-send-command-and-move)
(define-key map "\M-\C-a" #'ess-goto-end-of-function-or-para)
(define-key map "\M-\C-e" #'ess-goto-end-of-function-or-para)
(define-key map "\C-c\C-y" #'ess-switch-to-ESS)
(define-key map "\C-c\C-z" #'ess-switch-to-end-of-ESS)
(define-key map "\C-c\C-v" #'ess-display-help-on-object)
(define-key map "\C-c\C-d" #'ess-dump-object-into-edit-buffer)
(define-key map "\C-a" #'comint-bol)
(define-key map "\M-\t" #'comint-replace-by-expanded-filename)
(define-key map "\M-?" #'comint-dynamic-list-completions)
(define-key map "\C-c\C-k" #'ess-request-a-process)
(define-key map "{" #'skeleton-pair-insert-maybe)
(define-key map "}" #'skeleton-pair-insert-maybe)
(define-key map "\e\C-h" #'ess-mark-function-or-para)
(define-key map "\e\C-q" #'ess-indent-exp)
(define-key map "\t" #'ess-indent-command)
(define-key map "\C-c\C-p" #'comint-previous-prompt)
(define-key map "\C-c\C-n" #'comint-next-prompt)
(define-key map "\r" #'ess-transcript-send-command-and-move)
(define-key map "\M-\r" #'ess-transcript-send-command)
(define-key map "\C-c\r" #'ess-transcript-copy-command)
(define-key map "\C-c\C-w" #'ess-transcript-DO-clean-region)
(define-key map "\C-c\M-c" #'ess-transcript-clean-buffer)
map)
"Keymap for `ess-transcript-mode'.")
(easy-menu-define
ess-transcript-mode-menu ess-transcript-mode-map
"Menu for use in S transcript mode."
'("ESS-trans"
["Describe" describe-mode t]
["About" (ess-goto-info "Transcript Mode") t]
["Send bug report" ess-submit-bug-report t]
"------"
["Mark cmd group" mark-paragraph t]
["Previous prompt" comint-previous-prompt t]
["Next prompt" comint-next-prompt t]
"------"
["Send and move" ess-transcript-send-command-and-move t]
["Copy command" ess-transcript-copy-command t]
["Send command" ess-transcript-send-command t]
["Clean Region" ess-transcript-DO-clean-region t]
["Clean Whole Buffer" ess-transcript-clean-buffer t]
["Switch S process" ess-switch-process t]
))
;;;###autoload
(define-derived-mode ess-transcript-mode ess-mode "ESS Transcript"
"Major mode for transcript files.
Type \\[ess-transcript-send-command] to send a command in the
transcript to the current inferior process. \\[ess-transcript-copy-command]
copies the command but does not execute it, allowing you to edit it in
the process buffer first.
Type \\[ess-transcript-clean-region] to delete all outputs and prompts
in the region, leaving only the commands."
:group 'ess
(setq buffer-read-only t
ess-local-process-name nil
mode-line-process '(" [" ess-local-process-name "]"))
;; TODO: Set this more normally in various major modes
(unless inferior-ess-prompt ;; For S languages it is set in custom-alist
(setq inferior-ess-prompt
;; Do not anchor to bol with `^'
(concat "\\("
inferior-ess-primary-prompt
"\\|"
inferior-ess-secondary-prompt
"\\)")))
(setq-local paragraph-start (concat "^" inferior-ess-prompt "\\|^\^L"))
(setq-local paragraph-separate "^\^L")
(setq-local comint-use-prompt-regexp t)
(setq-local comint-prompt-regexp (concat "^" inferior-ess-prompt))
(setq font-lock-defaults '(ess-build-font-lock-keywords
nil nil ((?\. . "w") (?\_ . "w") (?' . ".")))))
;;*;; Commands used in S transcript mode
(defun ess-transcript-send-command ()
"Send the command at point in the transcript to the ESS process.
The line should begin with a prompt. The ESS process buffer is displayed if it
is not already."
(interactive)
(let* ((proc (or ess-local-process-name
(ess-request-a-process "Evaluate into which process? " t)))
(ess-buf (ess-get-process-buffer proc)))
(setq ess-local-process-name proc)
(if (get-buffer-window ess-buf) nil
(display-buffer ess-buf t))
(let ((input (inferior-ess-get-old-input)))
(with-current-buffer ess-buf
(goto-char (point-max))
(ess-eval-linewise input)))))
(defun ess-transcript-send-command-and-move ()
"Send the command on this line, and move point to the next command."
(interactive)
(let* ((proc (or ess-local-process-name
(ess-request-a-process "Evaluate into which process? " t)))
(ess-buf (ess-get-process-buffer proc)))
(setq ess-local-process-name proc)
(if (get-buffer-window ess-buf) nil
(display-buffer ess-buf t))
(let ((input (inferior-ess-get-old-input)))
(with-current-buffer ess-buf
(goto-char (point-max))
(ess-eval-linewise input nil nil nil 1))))
(comint-next-prompt 1))
(defun ess-transcript-copy-command ()
"Copy the command at point to the command line of the ESS process."
(interactive)
(let* ((proc (or ess-local-process-name
(ess-request-a-process "Evaluate into which process? " t)))
(ess-buf (process-buffer (get-process proc)))
(input (inferior-ess-get-old-input)))
(setq ess-local-process-name proc)
(if (get-buffer-window ess-buf) nil
(display-buffer ess-buf t))
(with-current-buffer ess-buf
(goto-char (point-max))
(insert input)))
(ess-switch-to-end-of-ESS))
(defun ess-transcript-clean-region (beg end even-if-read-only)
"Strip the transcript in the region, leaving only (R/S/Lsp/..) commands.
Deletes any lines not beginning with a prompt, and then removes the
prompt from those lines that remain. Prefix argument means to
clean even if the buffer is \\[read-only]."
(interactive "r\nP")
(unless inferior-ess-prompt
(error "Cannot clean ESS transcript region in this mode!
That only works in ess-transcript-mode or inferior-ess-mode ('*R*' etc)."
;; Maybe call ess-clean-region-in-new-transcript ?"))
))
(let ((do-toggle (and buffer-read-only even-if-read-only))
(ess-prompt-rx (if inferior-ess-secondary-prompt
(concat "^\\(\\("
inferior-ess-prompt
"\\)\\|\\("
inferior-ess-secondary-prompt
"\\)\\)")
(concat "^" inferior-ess-prompt))))
(save-excursion
(if do-toggle (setq buffer-read-only nil))
(save-restriction
(deactivate-mark)
(narrow-to-region beg end)
(goto-char (point-min))
(delete-non-matching-lines ess-prompt-rx)
(goto-char (point-min))
;; (replace-regexp * * ) :
(while (re-search-forward ess-prompt-rx nil t)
(replace-match "" nil nil)))
(if do-toggle (setq buffer-read-only t)))))
(defun ess-transcript-DO-clean-region (beg end)
"Clean the current via \\[ess-transcript-clean-region] even if the buffer is read-only."
(interactive "r")
(ess-transcript-clean-region beg end 'In-ANY-case))
(defun ess-transcript-clean-buffer ()
"Cleanup the whole buffer.
Use point-min/max to obey `narrow-to-region'."
(interactive)
(ess-transcript-clean-region (point-min) (point-max) 'In-ANY-case))
(provide 'ess-trns)
;;; ess-trns.el ends here