;;; ess-trns.el --- Support for manipulating S transcript files -*- lexical-binding: t; -*- ;; Copyright (C) 1989--1994 Bates, Kademan, Ritter and Smith ;; Copyright (C) 1997--2010 A.J. Rossini, Richard M. Heiberger, Martin ;; Maechler, Kurt Hornik, Rodney Sparapani, and Stephen Eglen. ;; Copyright (C) 2011--2012 A.J. Rossini, Richard M. Heiberger, Martin Maechler, ;; Kurt Hornik, Rodney Sparapani, Stephen Eglen and Vitalie Spinu. ;; Author: David Smith ;; Maintainer: ESS-core ;; This file is part of ESS ;; This file 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 2, or (at your option) ;; any later version. ;; This file 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. ;; A copy of the GNU General Public License is available at ;; https://www.r-project.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