238 lines
9.0 KiB
EmacsLisp
238 lines
9.0 KiB
EmacsLisp
;;; ess.el --- Emacs Speaks Statistics -*- lexical-binding: t; -*-
|
||
|
||
;; Copyright (C) 1997-2025 Free Software Foundation, Inc.
|
||
|
||
;; Author: David Smith <dsmith@stats.adelaide.edu.au>
|
||
;; A.J. Rossini <blindglobe@gmail.com>
|
||
;; Richard M. Heiberger <rmh@temple.edu>
|
||
;; Kurt Hornik <Kurt.Hornik@R-project.org>
|
||
;; Martin Maechler <maechler@stat.math.ethz.ch>
|
||
;; Rodney A. Sparapani <rsparapa@mcw.edu>
|
||
;; Stephen Eglen <stephen@gnu.org>
|
||
;; Sebastian P. Luque <spluque@gmail.com>
|
||
;; Henning Redestig <henning.red@googlemail.com>
|
||
;; Vitalie Spinu <spinuvit@gmail.com>
|
||
;; Lionel Henry <lionel.hry@gmail.com>
|
||
;; J. Alexander Branham <alex.branham@gmail.com>
|
||
;;
|
||
;; Maintainer: ESS Core Team <ESS-core@r-project.org>
|
||
;; Created: 7 Jan 1994
|
||
;; Package-Version: 20251015.1619
|
||
;; Package-Revision: a7d685bd9a3d
|
||
;; URL: https://ess.r-project.org/
|
||
;; Package-Requires: ((emacs "25.1"))
|
||
;; ESSR-Version: 1.8
|
||
|
||
;; 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:
|
||
;;
|
||
;; Emacs Speaks Statistics (ESS) is a package designed to support editing of
|
||
;; scripts and interaction with various statistical analysis programs such as R,
|
||
;; S-Plus, SAS, Stata and OpenBUGS/JAGS. For more details please visit ESS home
|
||
;; page at https://ess.r-project.org/
|
||
|
||
;;; Code:
|
||
|
||
(require 'ess-utils)
|
||
(require 'cl-generic)
|
||
(require 'lisp-mnt)
|
||
(defvar reporter-prompt-for-summary-p)
|
||
|
||
|
||
;; Versions
|
||
|
||
(defconst ess-version (eval-when-compile
|
||
(lm-version (or (and (boundp 'load-true-file-name)
|
||
load-true-file-name)
|
||
load-file-name
|
||
buffer-file-name)))
|
||
"Version of ESS currently loaded.")
|
||
|
||
(defconst essr-version (eval-when-compile
|
||
(lm-with-file (or (and (boundp 'load-true-file-name)
|
||
load-true-file-name)
|
||
load-file-name
|
||
buffer-file-name)
|
||
(lm-header "ESSR-Version")))
|
||
"Version of ESSR package.")
|
||
|
||
(defvar ess-revision nil
|
||
"The revision and date of ESS.
|
||
Is set by \\[ess-version-string].")
|
||
|
||
;;;###autoload
|
||
(defun ess-version ()
|
||
"Return a string with ESS version information."
|
||
(interactive)
|
||
(message (format "ess-version: %s (loaded from %s)"
|
||
(ess-version-string)
|
||
(file-name-directory ess-lisp-directory))))
|
||
|
||
(defun ess-version-string ()
|
||
(let* ((ess-dir (file-name-directory ess-lisp-directory)) ; if(<from source>) the top-level 'ess/'
|
||
(is-release (file-exists-p (concat ess-etc-directory ".IS.RELEASE")))
|
||
(rel-string (if is-release "Released "))
|
||
(git-ref-fn (concat ess-dir ".git/HEAD"))
|
||
(git-ref (when (file-exists-p git-ref-fn)
|
||
(with-current-buffer (find-file-noselect git-ref-fn)
|
||
(goto-char (point-min))
|
||
(when (re-search-forward "ref: \\(.*\\)\n" nil t)
|
||
(match-string 1)))))
|
||
(git-fname (if git-ref
|
||
(concat ess-dir ".git/" git-ref)
|
||
;; For release
|
||
(concat ess-etc-directory "git-ref")))
|
||
(git-rev (when (file-exists-p git-fname)
|
||
(with-current-buffer (find-file-noselect git-fname)
|
||
(goto-char (point-min))
|
||
(concat "git: "(buffer-substring
|
||
(point-min) (line-end-position))))))
|
||
(elpa-fname (concat ess-dir "ess-pkg.el"))
|
||
(elpa-rev (when (file-exists-p elpa-fname)
|
||
;; Get it from ELPA dir name, (probably won't work if installed manually)
|
||
(concat "elpa: "
|
||
(replace-regexp-in-string "ess-" ""
|
||
(file-name-nondirectory
|
||
(substring ess-dir 1 -1)))))))
|
||
;; Set the "global" ess-revision:
|
||
(setq ess-revision (format "%s%s%s"
|
||
(or rel-string "")
|
||
(or git-rev "")
|
||
(or elpa-rev "")))
|
||
(when (string= ess-revision "")
|
||
(setq ess-revision "<unknown>"))
|
||
(concat ess-version " [" ess-revision "]")))
|
||
|
||
|
||
;;; Bug Reporting
|
||
|
||
;;;###autoload
|
||
(defun ess-submit-bug-report ()
|
||
"Submit a bug report to the ESS maintainers."
|
||
(interactive)
|
||
(let ((reporter-prompt-for-summary-p 't))
|
||
(reporter-submit-bug-report
|
||
"ess-help@r-project.org"
|
||
(concat "ess-mode " (ess-version-string))
|
||
(list 'ess-language
|
||
'ess-dialect
|
||
'ess-ask-for-ess-directory
|
||
'ess-ask-about-transfile
|
||
'default-directory
|
||
'ess-keep-dump-files
|
||
'ess-source-directory
|
||
'ess-use-ido
|
||
'ess-use-eldoc
|
||
'ess-use-tracebug
|
||
'ess-use-auto-complete
|
||
'ess-use-company
|
||
'ess-eval-visibly-p
|
||
'ess-can-eval-in-background
|
||
'ess-local-process-name)
|
||
nil
|
||
(lambda ()
|
||
;;(goto-char (point-max))
|
||
(rfc822-goto-eoh)
|
||
(forward-line 1)
|
||
(insert "\n\n-------------------------------------------------------------\n")
|
||
(insert "This bug report will be sent to the ESS _help_ email list\n")
|
||
(insert ">>> _INSTEAD_ we strongly recommend you open an issue for this\n")
|
||
(insert " at https://github.com/emacs-ess/ESS/issues .\n\n")
|
||
(insert "If you still prefer to use the ESS help email, press C-c C-c to send your message.\n")
|
||
(insert "-------------------------------------------------------------\n\n")
|
||
(insert (with-current-buffer ess-dribble-buffer
|
||
(goto-char (point-max))
|
||
(forward-line -100)
|
||
(buffer-substring-no-properties (point) (point-max))))))))
|
||
|
||
|
||
|
||
;;; Timer
|
||
|
||
(defcustom ess-idle-timer-interval 1
|
||
"Number of seconds to wait before running function in `ess-idle-timer-functions'."
|
||
:type '(integer)
|
||
:group 'ess)
|
||
|
||
(defvar ess-idle-timer-functions nil
|
||
"A list of functions to run each `ess-idle-timer-interval' idle seconds.
|
||
If your function calls the process, you better use
|
||
`ess-when-new-input' to wrap your call. If you call the
|
||
subprocess please respect `ess-can-eval-in-background' variable.
|
||
|
||
These functions are run with `run-hooks'. Use `add-hook' to add
|
||
symbols to this variable.
|
||
|
||
Most likely you will need a local hook. Then you should specify
|
||
the LOCAL argument to `add-hook' and initialize it in
|
||
`ess-mode-hook' or `ess-post-run-hook', or one of the more
|
||
specialized hooks `ess-r-post-run-hook',`ess-stata-post-run-hook'
|
||
etc.")
|
||
|
||
(defun ess--idle-timer-function nil
|
||
"Internal function executed by `ess--idle-timer'."
|
||
(run-hooks 'ess-idle-timer-functions))
|
||
|
||
(require 'timer)
|
||
(defvar ess--idle-timer
|
||
(run-with-idle-timer ess-idle-timer-interval 'repeat
|
||
#'ess--idle-timer-function)
|
||
"Timer used to run `ess-idle-timer-functions'.")
|
||
|
||
|
||
;;; Dispatch on ess-dialect
|
||
;; Inspired by major-mode specializer in cl-generic.el
|
||
|
||
;; FIXME: Implement a notion of derived dialects. major-mode specializer cannot
|
||
;; be used here as we need same dialect in different major-modes.
|
||
|
||
;; Two parts:
|
||
;; 1) first define a specializer (ess-dialect= DIALECT) to match symbols
|
||
;; representing ess dialects.
|
||
;; 2) then define a context-rewriter so you can write
|
||
;; `&context (ess-dialect "R")` rather than
|
||
;; `&context (ess-dialect (ess-dialect= "R"))`.
|
||
|
||
(cl-generic-define-generalizer ess--generic-dialect-generalizer
|
||
95
|
||
(lambda (name &rest _) `(if (stringp ,name) (intern ,name)
|
||
(if (symbolp ,name) ,name)))
|
||
(lambda (tag &rest _) `((ess-dialect= ,tag))))
|
||
|
||
(cl-defmethod cl-generic-generalizers ((_specializer (head ess-dialect=)))
|
||
"Support for (ess-dialect DIALECT) context specializer."
|
||
(list ess--generic-dialect-generalizer))
|
||
|
||
(cl-generic-define-context-rewriter ess-dialect (dialect)
|
||
`(ess-dialect (ess-dialect= ,(if (stringp dialect)
|
||
(intern dialect)
|
||
dialect))))
|
||
|
||
;; (cl-defgeneric ess-print-dialect ()
|
||
;; (error "unknown dialect %s" ess-dialect))
|
||
;; (cl-defmethod ess-print-dialect (&context (ess-dialect "R"))
|
||
;; (message "dialect: R"))
|
||
;; (cl-defmethod ess-print-dialect (&context (ess-dialect "julia"))
|
||
;; (message "dialect: julia"))
|
||
|
||
(provide 'ess)
|
||
|
||
;;; ess.el ends here
|