|
;;; ess-mode.el -- Emacs Speaks Statistics root mode. -*- lexical-binding: t; -*-
|
|
;;
|
|
;; Copyright (C) 1994-2018 ESS Core Team
|
|
;; Maintainer: ESS-core <ESS-core@r-project.org>
|
|
;;
|
|
;; Keywords: languages
|
|
;;
|
|
;; 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:
|
|
|
|
(require 'ess)
|
|
|
|
(eval-when-compile
|
|
(require 'cl-lib)
|
|
(require 'subr-x))
|
|
(require 'ess-inf)
|
|
|
|
;; Silence the byte compiler
|
|
(declare-function run-ess-r "ess-r-mode" (&optional start-args))
|
|
(declare-function S+ "ess-sp6-d" (&optional proc-name))
|
|
(declare-function SAS "ess-sas-d" ())
|
|
|
|
;; FIXME:This one should not be necessary
|
|
(declare-function ess-display-help-on-object "ess-help" (object &optional command))
|
|
|
|
;; ESS mode
|
|
;; Major mode definition
|
|
|
|
;;*;; Hooks
|
|
|
|
(defcustom ess-mode-hook nil
|
|
"Hook for customizing ESS each time it is entered."
|
|
:group 'ess-hooks
|
|
:type 'hook)
|
|
|
|
(defvar ess-mode-map
|
|
(let ((map (make-sparse-keymap)))
|
|
(define-key map [remap yank] #'ess-yank)
|
|
(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-b" #'ess-eval-buffer)
|
|
(define-key map "\C-c\M-b" #'ess-eval-buffer-and-go)
|
|
(define-key map (kbd "C-c C-<up>") #'ess-eval-buffer-from-beg-to-here)
|
|
(define-key map (kbd "C-c C-<down>") #'ess-eval-buffer-from-here-to-end)
|
|
(define-key map "\C-c\C-f" #'ess-eval-function)
|
|
(define-key map "\C-c\M-f" #'ess-eval-function-and-go)
|
|
(define-key map "\C-c\C-c" #'ess-eval-region-or-function-or-paragraph-and-step)
|
|
(define-key map "\C-c\C-p" #'ess-eval-paragraph-and-step)
|
|
(define-key map "\C-c\M-p" #'ess-eval-paragraph-and-go)
|
|
(define-key map "\C-\M-x" #'ess-eval-region-or-function-or-paragraph)
|
|
(define-key map "\C-c\C-n" #'ess-eval-line-visibly-and-step)
|
|
(define-key map "\C-c\C-j" #'ess-eval-line)
|
|
(define-key map [(control return)] #'ess-eval-region-or-line-visibly-and-step)
|
|
(define-key map "\C-c\M-j" #'ess-eval-line-and-go)
|
|
;; FIXME: The next three can only work in S/R - mode
|
|
(define-key map "\C-\M-a" #'ess-goto-beginning-of-function-or-para)
|
|
(define-key map "\C-\M-e" #'ess-goto-end-of-function-or-para)
|
|
(define-key map "\C-xnd" #'ess-narrow-to-defun-or-para)
|
|
(define-key map "\C-xnf" #'ess-narrow-to-defun-or-para)
|
|
(define-key map "\C-c\C-z" #'ess-switch-to-inferior-or-script-buffer)
|
|
(define-key map "\C-c\C-l" #'ess-load-file)
|
|
;;; Make an alias because C-c C-l is taken up by comint in inferiors
|
|
(define-key map "\C-c\M-l" #'ess-load-file)
|
|
(define-key map "\C-c\C-v" #'ess-display-help-on-object)
|
|
(define-key map "\C-c\C-s" #'ess-switch-process)
|
|
(define-key map "\C-c\C-k" #'ess-force-buffer-current)
|
|
(define-key map "\C-c`" #'ess-show-traceback)
|
|
(define-key map [(control ?c) ?~] #'ess-show-call-stack)
|
|
(define-key map "\C-\M-q" #'ess-indent-exp)
|
|
(define-key map "{" #'skeleton-pair-insert-maybe)
|
|
(define-key map "}" #'skeleton-pair-insert-maybe)
|
|
(define-key map "\C-\M-h" #'ess-mark-function-or-para)
|
|
(define-key map "\t" #'ess-indent-or-complete)
|
|
(define-key map "\C-c\C-q" #'ess-quit)
|
|
(define-key map "\M-\r" #'ess-use-this-dir)
|
|
(define-key map "," #'ess-smart-comma)
|
|
(define-key map "\C-c\C-d" 'ess-doc-map)
|
|
(define-key map "\C-c\C-e" 'ess-extra-map)
|
|
(define-key map "\C-c\C-t" 'ess-dev-map)
|
|
map)
|
|
"Keymap for `ess-mode'.")
|
|
|
|
;; Redefine substituted commands
|
|
(substitute-key-definition 'indent-new-comment-line
|
|
'ess-indent-new-comment-line
|
|
ess-mode-map global-map)
|
|
|
|
(defvar ess-extra-map
|
|
(let (ess-extra-map)
|
|
(define-prefix-command 'ess-extra-map)
|
|
(define-key ess-extra-map "\C-d" #'ess-dump-object-into-edit-buffer)
|
|
(define-key ess-extra-map "d" #'ess-dump-object-into-edit-buffer)
|
|
(define-key ess-extra-map "\C-e" #'ess-execute)
|
|
(define-key ess-extra-map "e" #'ess-execute)
|
|
(define-key ess-extra-map "\C-i" #'ess-install-library)
|
|
(define-key ess-extra-map "i" #'ess-install-library)
|
|
(define-key ess-extra-map "\C-l" #'ess-load-library)
|
|
(define-key ess-extra-map "l" #'ess-load-library)
|
|
(define-key ess-extra-map "\C-r" #'inferior-ess-reload)
|
|
(define-key ess-extra-map "r" #'inferior-ess-reload)
|
|
(define-key ess-extra-map "\C-s" #'ess-set-style)
|
|
(define-key ess-extra-map "s" #'ess-set-style)
|
|
(define-key ess-extra-map "\C-t" #'ess-build-tags-for-directory)
|
|
(define-key ess-extra-map "t" #'ess-build-tags-for-directory)
|
|
(define-key ess-extra-map "\C-w" #'ess-execute-screen-options)
|
|
(define-key ess-extra-map "w" #'ess-execute-screen-options)
|
|
(define-key ess-extra-map "/" #'ess-set-working-directory)
|
|
ess-extra-map)
|
|
"ESS extra map.")
|
|
|
|
(easy-menu-define
|
|
ess-mode-menu ess-mode-map
|
|
"Menu for use in `ess-mode'."
|
|
'("ESS" ; ESS-mode
|
|
["Load file" ess-load-file t]
|
|
["Eval region | func | para" ess-eval-region-or-function-or-paragraph t]
|
|
["Eval region | func | para & step" ess-eval-region-or-function-or-paragraph-and-step t]
|
|
["Eval region | line" ess-eval-region-or-line-visibly-and-step t]
|
|
["Enter expression" ess-execute t]
|
|
;; sub menus
|
|
"------"
|
|
("Process"
|
|
["Goto end of process buffer" ess-switch-to-end-of-ESS t]
|
|
["Switch to process buffer" ess-switch-to-inferior-or-script-buffer t]
|
|
["Switch process" ess-switch-process t]
|
|
;; ["Recreate R and S versions known to ESS" (ess-r-s-versions-creation+menu) t]
|
|
("Start Process"
|
|
["R" R :help "Start a new R process" :active t]
|
|
["S" S :help "Start a new S process" :active t]
|
|
["Sqpe" Sqpe ess-microsoft-p] ;; :help "Start a new Sqpe process" :active t
|
|
["S+6-exisiting" S+6-existing ess-microsoft-p] ;; :help "Access an existing S process" :active t
|
|
["SAS" SAS-menu t] ;; :help "Start a new SAS process" :active t
|
|
;; The following menu item "Other" is a place-holder that will
|
|
;; be replaced with the other versions of R and Sqpe that can be run.
|
|
;; See `ess-r-define-runners' and ess-site.el
|
|
("Other"
|
|
["No other R or Sqpe versions" nil nil])
|
|
["About"
|
|
(ess-goto-info "Starting up") t]
|
|
;; :help "Read about starting a new ESS process" :active t]
|
|
)
|
|
("Eval visibly "
|
|
:filter ess--generate-eval-visibly-submenu)
|
|
["Quit process" ess-quit t]
|
|
["Reload process" inferior-ess-reload t])
|
|
"------"
|
|
("ESS Eval"
|
|
["Eval region | func | para" ess-eval-region-or-function-or-paragraph t]
|
|
["Eval region | func | para & step" ess-eval-region-or-function-or-paragraph-and-step t]
|
|
["Eval region | line & step" ess-eval-region-or-line-visibly-and-step t]
|
|
"-----"
|
|
["Eval buffer" ess-eval-buffer t]
|
|
["Eval buffer till here" ess-eval-buffer-from-beg-to-here t]
|
|
["Eval buffer from here" ess-eval-buffer-from-here-to-end t]
|
|
["Eval region" ess-eval-region t]
|
|
["Eval function" ess-eval-function t]
|
|
["Eval line" ess-eval-line t]
|
|
["Eval line & step" ess-eval-line-and-step t]
|
|
["Eval paragraph" ess-eval-paragraph t]
|
|
["Eval paragraph & step" ess-eval-paragraph-and-step t]
|
|
["About" (ess-goto-info "Evaluating code") t]
|
|
)
|
|
("Eval and Go"
|
|
["Eval buffer" ess-eval-buffer-and-go t]
|
|
["Eval region" ess-eval-region-and-go t]
|
|
["Eval function" ess-eval-function-and-go t]
|
|
["Eval line" ess-eval-line-and-go t]
|
|
["Eval paragraph" ess-eval-paragraph-and-go t]
|
|
["About" (ess-goto-info "Evaluating code") t]
|
|
)
|
|
("Motion"
|
|
["Beginning of function or para" ess-goto-beginning-of-function-or-para t]
|
|
["End of function or para" ess-goto-end-of-function-or-para t]
|
|
"-----"
|
|
["Backward list" backward-list t]
|
|
["Forward list" forward-list t]
|
|
["Next parenthesis" down-list t]
|
|
["Enclosing parenthesis" backward-up-list t]
|
|
["Backward sexp" backward-sexp t]
|
|
["Forward sexp" forward-sexp t]
|
|
["About" (Info-goto-node "(Emacs)Lists") t]
|
|
)
|
|
("ESS Edit"
|
|
["Edit new object" ess-dump-object-into-edit-buffer t]
|
|
["Complete Filename" comint-replace-by-expanded-filename t]
|
|
["Complete File or Object" ess-indent-or-complete t]
|
|
["Kill sexp" kill-sexp t]
|
|
["Mark function" ess-mark-function-or-para t]
|
|
["Indent expression" ess-indent-exp t]
|
|
["Indent line" ess-indent-command t]
|
|
["Toggle Auto-Fill Mode" auto-fill-mode t]
|
|
["Undo" undo t]
|
|
["About" (ess-goto-info "Edit buffer") t]
|
|
)
|
|
"------"
|
|
("start-dev" :visible nil)
|
|
("end-dev" :visible nil)
|
|
"------"
|
|
("Font Lock"
|
|
:active ess-font-lock-keywords
|
|
:filter ess--generate-font-lock-submenu)
|
|
"------"
|
|
["Describe" describe-mode t]
|
|
["About editing" (ess-goto-info "Editing") t]
|
|
["Read ESS info" (ess-goto-info "") t]
|
|
["Send bug report" ess-submit-bug-report t]))
|
|
|
|
;;;###autoload
|
|
(define-derived-mode ess-mode prog-mode "ESS"
|
|
"Major mode for editing ESS source.
|
|
Optional arg ALIST describes how to customize the editing mode.
|
|
Optional arg PROC-NAME is name of associated inferior process.
|
|
|
|
\\{ess-mode-map}
|
|
|
|
You can send text to the inferior ESS process from other buffers containing
|
|
ESS source.
|
|
`ess-eval-region' sends the current region to the ESS process.
|
|
`ess-eval-buffer' sends the current buffer to the ESS process.
|
|
`ess-eval-function' sends the current function to the ESS process.
|
|
`ess-eval-line' sends the current line to the ESS process.
|
|
`ess-switch-to-ESS' switches the current buffer to the ESS process buffer.
|
|
`ess-switch-to-end-of-ESS' switches the current buffer to the ESS process
|
|
buffer and puts point at the end of it.
|
|
|
|
`ess-eval-region-and-go', `ess-eval-buffer-and-go',
|
|
`ess-eval-function-and-go', and `ess-eval-line-and-go' switch to the S
|
|
process buffer after sending their text.
|
|
|
|
`ess-load-file' sources a file of commands to the ESS process.
|
|
|
|
\\[ess-indent-command] indents for ESS code.
|
|
\\[backward-delete-char-untabify] converts tabs to spaces as it moves back.
|
|
Comments are indented in a similar way to Emacs-lisp mode:
|
|
`###' beginning of line
|
|
`##' the same level of indentation as the code
|
|
`#' the same column on the right, or to the right of such a
|
|
column if that is not possible.(default value 40).
|
|
\\[indent-for-comment] command automatically inserts such a
|
|
`#' in the right place, or aligns such a comment if it is
|
|
already inserted.
|
|
\\[ess-indent-exp] command indents each line of the syntactic unit following point.
|
|
|
|
Variables controlling indentation style:
|
|
`ess-indent-offset'
|
|
Indentation of ESS statements within surrounding block.
|
|
The surrounding block's indentation is the indentation of the line on
|
|
which the open-brace appears.
|
|
`ess-offset-block'
|
|
Indentation of blocks opened with curly braces or anonymous parentheses.
|
|
`ess-offset-arguments'
|
|
Indentation of function arguments or bracket indices.
|
|
`ess-offset-arguments-newline'
|
|
Indentation of function arguments or bracket indices when the opening
|
|
delimiter is immediately followed by a newline.
|
|
`ess-offset-continued'
|
|
Indentation style for continued statements.
|
|
`ess-align-nested-calls'
|
|
Functions whose nested calls should be aligned.
|
|
`ess-align-arguments-in-calls'
|
|
Calls in which arguments should be aligned.
|
|
`ess-align-continuations-in-calls'
|
|
Whether ignore indentation after an operator in calls
|
|
`ess-align-blocks'
|
|
Blocks that should always be aligned vertically.
|
|
`ess-indent-from-lhs'
|
|
Whether function calls given as argument should be indented from the
|
|
parameter name.
|
|
`ess-indent-from-chain-start'
|
|
Whether to indent arguments from the first of several consecutive calls.
|
|
`ess-indent-with-fancy-comments'
|
|
Non-nil means distinguish between #, ##, and ### for indentation.
|
|
|
|
Furthermore, \\[ess-set-style] command enables you to set up predefined ess-mode
|
|
indentation style. See `ess-style-alist' for predefined styles."
|
|
:group 'ess
|
|
;; TODO: get rid of these and rely on modes to set variables properly
|
|
(when-let ((alist (buffer-local-value 'ess-local-customize-alist (current-buffer))))
|
|
(ess-setq-vars-local alist))
|
|
(when-let ((alist ess-mode-editing-alist))
|
|
(ess-setq-vars-local alist))
|
|
|
|
;; Keep <tabs> out of the code.
|
|
(setq-local indent-tabs-mode nil)
|
|
|
|
(setq mode-line-process
|
|
'(" ["
|
|
(:eval (ess--get-mode-line-indicator))
|
|
ess--local-mode-line-process-indicator
|
|
"]"))
|
|
(add-hook 'ess-idle-timer-functions 'ess-synchronize-dirs nil 'local))
|
|
|
|
(defun ess--get-mode-line-indicator ()
|
|
"Get `ess--mode-line-process-indicator' from process buffer.
|
|
Internal function to be used for dynamic mode-line display in
|
|
`ess-mode'."
|
|
(if ess-local-process-name
|
|
(let* ((proc (get-process ess-local-process-name))
|
|
(buff (when proc (process-buffer proc))))
|
|
(if (and proc (buffer-live-p buff))
|
|
(with-current-buffer buff (mapcar 'eval ess--mode-line-process-indicator))
|
|
"none"))
|
|
"none"))
|
|
|
|
|
|
;;; User commands in ess-mode
|
|
|
|
(defun ess-install-library (&optional update package)
|
|
"Install PACKAGE for current dialect.
|
|
With UPDATE, update cached package list."
|
|
(interactive "P")
|
|
(ess-install-library--override update package))
|
|
|
|
(cl-defgeneric ess-install-library--override (update package)
|
|
"See `ess-install-library' for UPDATE, PACKAGE."
|
|
(when update (message "Don't know how to update for %s" ess-dialect))
|
|
(error "Cannot install %s, not available for %s" package ess-dialect))
|
|
|
|
|
|
;; Motion / manipulation commands
|
|
|
|
(defun ess-goto-beginning-of-function-or-para ()
|
|
"If inside a function go to the beginning of it.
|
|
Otherwise go to the beginning of paragraph."
|
|
(interactive)
|
|
(let ((start-pos (point))
|
|
beg end)
|
|
(beginning-of-defun)
|
|
(setq beg (point))
|
|
(end-of-defun)
|
|
(setq end (point))
|
|
(goto-char beg)
|
|
(unless (and (< beg start-pos)
|
|
(> end start-pos))
|
|
(let ((par-pos (save-excursion
|
|
(goto-char start-pos)
|
|
(forward-comment most-negative-fixnum)
|
|
(backward-paragraph)
|
|
(forward-comment most-positive-fixnum)
|
|
(point))))
|
|
(if (< end par-pos)
|
|
(goto-char par-pos)
|
|
(goto-char beg))))))
|
|
|
|
(defun ess-goto-end-of-function-or-para ()
|
|
"If inside a function go to end of it.
|
|
Otherwise go to the end of paragraph."
|
|
(interactive)
|
|
(let ((pos (point))
|
|
beg end)
|
|
(end-of-defun)
|
|
(setq end (point))
|
|
(beginning-of-defun)
|
|
(setq beg (point))
|
|
(goto-char end)
|
|
(when (or (< beg pos)
|
|
(> end pos))
|
|
(let ((par-pos (save-excursion
|
|
(goto-char pos)
|
|
(forward-comment most-positive-fixnum)
|
|
(forward-paragraph)
|
|
(point))))
|
|
(when (<= par-pos beg)
|
|
(goto-char par-pos))))))
|
|
|
|
(defun ess-mark-function-or-para ()
|
|
"Put mark at end of ESS function, point at beginning."
|
|
(interactive)
|
|
(ess-goto-beginning-of-function-or-para)
|
|
(push-mark (point))
|
|
(ess-goto-end-of-function-or-para)
|
|
(exchange-point-and-mark))
|
|
|
|
(define-obsolete-function-alias 'ess-mark-function 'ess-mark-function-or-para "15.09")
|
|
|
|
(defun ess-narrow-to-defun-or-para ()
|
|
"Make text outside current function invisible.
|
|
If text is already narrowed, this is removed before narrowing to the
|
|
current function."
|
|
(interactive)
|
|
(save-excursion
|
|
(widen)
|
|
(let* ((beg (progn (ess-goto-beginning-of-function-or-para)
|
|
(point)))
|
|
(end (progn (ess-goto-end-of-function-or-para)
|
|
(point))))
|
|
(narrow-to-region beg end))))
|
|
|
|
(define-obsolete-function-alias 'ess-narrow-to-defun 'ess-narrow-to-defun-or-para "15.09")
|
|
|
|
(defun ess-indent-new-comment-line ()
|
|
"Like `indent-new-comment-line' but accounts for roxygen comments."
|
|
(interactive)
|
|
(cond ((and (fboundp 'ess-roxy-indent-new-comment-line)
|
|
(string= ess-dialect "R"))
|
|
(ess-roxy-indent-new-comment-line))
|
|
(t
|
|
(indent-new-comment-line))))
|
|
|
|
|
|
;;; Formatting / indentation
|
|
(defvar-local ess--installed-style-vars nil
|
|
"A cons of the form (STYLE . VARS).
|
|
VARS is a list of all style vars which were not set explicitly to
|
|
buffer local values by the user in mode hooks.")
|
|
|
|
(defun ess-set-style (&optional style _quiet)
|
|
"Set up the `ess-mode' style variables from the `ess-style' variable.
|
|
If STYLE argument is given, use that instead. It makes the ESS
|
|
indentation style variables buffer local. QUIET is for backward
|
|
compatibility and is ignored.
|
|
|
|
In programs, when STYLE is nil, the `ess-style' is installed. In
|
|
this case, if `ess-style' is buffer local, all settings are
|
|
overwritten, otherwise only those settings which are not already
|
|
buffer local. For example, `ess-style' is buffer local when it is
|
|
set in .dir-locals and thus must have priority over the user
|
|
settings in the mode hook."
|
|
(interactive
|
|
(list (let ((styles (mapcar (lambda (x) (symbol-name (car x)))
|
|
ess-style-alist)))
|
|
(intern (ess-completing-read
|
|
"Set ESS mode indentation style"
|
|
styles nil t nil nil ess-style)))))
|
|
(let* ((keep-local (and (null style)
|
|
(not (local-variable-p 'ess-style))))
|
|
(style (or style ess-style))
|
|
(style-alist (or (cdr (assq style ess-style-alist))
|
|
(error "Bad ESS style: %s" style)))
|
|
(vars (if keep-local
|
|
;; Install, but Keep user's buffer-local settings.
|
|
(cl-loop for (var . _) in (cdr (assq 'DEFAULT ess-style-alist))
|
|
unless (local-variable-p var)
|
|
collect var)
|
|
(mapcar #'car style-alist))))
|
|
(when (called-interactively-p 'any)
|
|
(message "Set indentation style to %s" style))
|
|
(mapc (lambda (var)
|
|
(make-local-variable var)
|
|
(set var (cdr (assq var style-alist))))
|
|
vars)
|
|
style))
|
|
|
|
(defun ess-indent-command (&optional whole-exp)
|
|
"Indent current line as ESS code, or in some cases insert a tab character.
|
|
If `tab-always-indent' is non-nil, always indent current line.
|
|
Otherwise, indent the current line only if point is at the left
|
|
margin or in the line's indentation; otherwise insert a tab. If
|
|
given, WHOLE-EXP means indent rigidly all the lines of the
|
|
expression starting after point so that this line becomes
|
|
properly indented. The relative indentation among the lines of
|
|
the expression are preserved.
|
|
|
|
If in a roxygen block at the beginning of the line with
|
|
`ess-roxy-hide-show-p' non-nil, call `ess-roxy-toggle-hiding'
|
|
instead of indenting."
|
|
(interactive "P")
|
|
(cond ((and (fboundp 'ess-roxy-entry-p)
|
|
(fboundp 'ess-roxy-toggle-hiding)
|
|
(bolp)
|
|
(ess-roxy-entry-p)
|
|
ess-roxy-hide-show-p)
|
|
(ess-roxy-toggle-hiding))
|
|
(whole-exp
|
|
;; If arg, always indent this line as S
|
|
;; and shift remaining lines of expression the same amount.
|
|
(let ((shift-amt (funcall indent-line-function))
|
|
beg end)
|
|
(save-excursion
|
|
(if tab-always-indent
|
|
(beginning-of-line))
|
|
(setq beg (point))
|
|
(backward-up-list 1)
|
|
(forward-list 1)
|
|
(setq end (point))
|
|
(goto-char beg)
|
|
(forward-line 1)
|
|
(setq beg (point)))
|
|
(if (> end beg)
|
|
(indent-code-rigidly beg end shift-amt))))
|
|
((and (not tab-always-indent)
|
|
(save-excursion
|
|
(skip-chars-backward " \t")
|
|
(not (bolp))))
|
|
(insert-tab))
|
|
(t (funcall indent-line-function))))
|
|
|
|
(defun ess-indent-or-complete ()
|
|
"When region is selected indent the region.
|
|
Otherwise, if `tab-always-indent' is 'complete, try to indent, if
|
|
code is already indented, complete instead. Also see
|
|
`ess-first-tab-never-complete'."
|
|
(interactive)
|
|
(if (use-region-p)
|
|
(indent-region (region-beginning) (region-end))
|
|
(let ((shift (let ((indent (current-indentation)))
|
|
(ess-indent-command)
|
|
(- (current-indentation) indent))))
|
|
(when (and (or (equal tab-always-indent 'complete)
|
|
ess-tab-complete-in-script)
|
|
(numberp shift) ;; can be nil if tab-always-indent is nil
|
|
(equal shift 0)
|
|
(or (eq last-command 'ess-indent-or-complete)
|
|
(null ess-first-tab-never-complete)
|
|
(and (eq ess-first-tab-never-complete 'unless-eol)
|
|
(looking-at "\\s-*$"))
|
|
(and (eq ess-first-tab-never-complete 'symbol)
|
|
(not (looking-at "\\w\\|\\s_")))
|
|
(and (eq ess-first-tab-never-complete 'symbol-or-paren)
|
|
(not (looking-at "\\w\\|\\s_\\|\\s)")))
|
|
(and (eq ess-first-tab-never-complete 'symbol-or-paren-or-punct)
|
|
(not (looking-at "\\w\\|\\s_\\|\\s)\\|\\s.")))))
|
|
(completion-at-point)))))
|
|
|
|
(defun ess-indent-exp ()
|
|
"Indent each line of the ESS grouping following point."
|
|
(interactive)
|
|
(cond ((and (fboundp 'ess-r-indent-exp)
|
|
(string= ess-dialect "R"))
|
|
(ess-r-indent-exp))
|
|
(t
|
|
(save-excursion
|
|
(let ((start (point))
|
|
(end (ignore-errors (forward-sexp 1) (point))))
|
|
(when end
|
|
(indent-region start end)))))))
|
|
|
|
(defun ess-indent-line ()
|
|
"Indent current line as ESS code.
|
|
Return the amount the indentation changed by."
|
|
(declare (obsolete 'indent-line-function "ESS 19.04"))
|
|
(funcall indent-line-function))
|
|
|
|
|
|
;;; Dump Objects
|
|
|
|
(defun ess-dump-object-into-edit-buffer (object)
|
|
"Edit an ESS OBJECT in its own buffer.
|
|
Without a prefix argument, this simply finds the file pointed to by
|
|
`ess-source-directory'. If this file does not exist, or if a
|
|
prefix argument is given, a dump() command is sent to the ESS process to
|
|
generate the source buffer."
|
|
(interactive
|
|
(progn
|
|
(ess-force-buffer-current "Process to dump from: ")
|
|
(ess-read-object-name "Object to edit")))
|
|
(let* ((dirname (file-name-as-directory
|
|
(if (stringp ess-source-directory)
|
|
ess-source-directory
|
|
(with-current-buffer (process-buffer (ess-get-process
|
|
ess-local-process-name))
|
|
(ess-setq-vars-local ess-local-customize-alist)
|
|
(apply ess-source-directory nil)))))
|
|
(filename (concat dirname (convert-standard-filename (format ess-dump-filename-template object))))
|
|
(old-buff (get-file-buffer filename)))
|
|
;; If the directory doesn't exist, offer to create it
|
|
(unless (file-exists-p (directory-file-name dirname))
|
|
(if (y-or-n-p (format "Directory %s does not exist. Create it? " dirname))
|
|
(make-directory (directory-file-name dirname))
|
|
(error "Directory %s does not exist" dirname)))
|
|
;; Three options:
|
|
;; (1) Pop to an existing buffer containing the file in question
|
|
;; (2) Find an existing file
|
|
;; (3) Create a new file by issuing a dump() command to S
|
|
;; Force option (3) if there is a prefix arg
|
|
(cond (current-prefix-arg
|
|
(ess-dump-object object filename))
|
|
(old-buff
|
|
(pop-to-buffer old-buff))
|
|
((file-exists-p filename)
|
|
(ess-find-dump-file-other-window filename)
|
|
(message "Read %s" filename))
|
|
(t (ess-dump-object object filename)))))
|
|
|
|
(defun ess-dump-object (object filename)
|
|
"Dump the ESS object OBJECT into file FILENAME."
|
|
(unless (file-writable-p filename)
|
|
(error "Can't dump %s as %f is not writeable" object filename))
|
|
(let ((dump-cmd (format inferior-ess-dump-command object filename)))
|
|
;; Make sure we start fresh
|
|
(when (get-file-buffer filename)
|
|
(kill-buffer (get-file-buffer filename)))
|
|
(ess-command dump-cmd)
|
|
(message "Dumped in %s" filename)
|
|
(ess-find-dump-file-other-window filename)
|
|
;; PD, 1Apr97
|
|
;;This ensures that the object gets indented according to ess-mode,
|
|
;;not as the R/S deparser does it. At the same time, it gets rid
|
|
;;of the mess generated by sending TAB characters to the readline
|
|
;;functions in R when you eval-buffer-*.
|
|
(indent-region (point-min-marker) (point-max-marker) nil)
|
|
(set-buffer-modified-p nil) ; no need to safe just because of indenting
|
|
;; Don't make backups for temporary files; it only causes clutter.
|
|
;; The ESS object itself is a kind of backup, anyway.
|
|
(unless ess-keep-dump-files
|
|
(make-local-variable 'make-backup-files)
|
|
(setq make-backup-files nil))
|
|
;; Don't get confirmation to delete dumped files when loading
|
|
(when (eq ess-keep-dump-files 'check)
|
|
(setq ess-keep-dump-files nil))
|
|
;; Delete the file if necessary
|
|
(when ess-delete-dump-files
|
|
(delete-file (buffer-file-name)))))
|
|
|
|
(defun ess-find-dump-file-other-window (filename)
|
|
"Find ESS source file FILENAME in another window."
|
|
(unless (file-readable-p filename)
|
|
(ess-write-to-dribble-buffer
|
|
(format "%s does not exist. Bad dump, starting fresh." filename)))
|
|
;; Generate a buffer with the dumped data
|
|
(find-file-other-window filename)
|
|
(auto-save-mode 1) ; Auto save in this buffer
|
|
(when (and ess-function-template
|
|
(goto-char (point-max))
|
|
(re-search-backward ess-dumped-missing-re nil t))
|
|
(replace-match ess-function-template t t)
|
|
(set-buffer-modified-p nil) ; Don't offer to save if killed now
|
|
(goto-char (point-min))
|
|
(ignore-errors
|
|
;; This may fail if there are no opens
|
|
(down-list 1))))
|
|
|
|
|
|
;;; Runners
|
|
|
|
(defun ess-define-runner (name dialect &optional path)
|
|
"Create a function NAME.
|
|
This function starts the inferior process with the specified
|
|
version. DIALECT can be \"R,\" \"S,\", \"SAS.\" If given, PATH
|
|
should be the absolute path to the program. It defaults to NAME."
|
|
(let ((name name)
|
|
(dialect dialect)
|
|
(path path))
|
|
(fset (intern name)
|
|
(lambda (&optional start-args)
|
|
"Start this process version in an inferior ESS buffer.
|
|
Function defined using `ess-define-runner'."
|
|
(interactive "P")
|
|
(cond ((string= dialect "R")
|
|
(let ((inferior-ess-r-program (or path name)))
|
|
(require 'ess-r-mode)
|
|
(run-ess-r start-args)))
|
|
((string= dialect "S")
|
|
(let ((inferior-S+-program (or path name)))
|
|
(require 'ess-sp6-d)
|
|
(S+)))
|
|
((string= dialect "SAS")
|
|
(let ((inferior-SAS-program (or path name)))
|
|
(require 'ess-sas-d)
|
|
(SAS))))))))
|
|
|
|
|
|
|
|
(provide 'ess-mode)
|
|
|
|
;;; ess-mode.el ends here
|