(autoload 'ess-turn-on-eldoc "ess-r-d" "" nil)
(autoload 'ess-dump-object-ddeclient "ess-dde" "(autoload)" nil)
(autoload 'SAS "ess-sas-d.el" "(autoload)" t)
(require 'ess-utils)
(defun ess-line-end-position (&optional N)
"return the 'point' at the end of N lines. N defaults to 1, i.e., current line."
(save-excursion
(end-of-line N)
(point)))
(defvar ess-mode-map
(let ((map (make-sparse-keymap)))
(define-key map "\C-m" 'newline-and-indent)
(define-key map "\C-y" '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-and-step)
(define-key map "\C-c\C-j" 'ess-eval-line)
(define-key map [(control return)] 'ess-eval-region-or-line-and-step)
(define-key map "\C-c\M-j" 'ess-eval-line-and-go)
(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)
(define-key map "\C-c\C-y" 'ess-switch-to-ESS-deprecated)
(define-key map "\C-c\C-z" 'ess-switch-to-inferior-or-script-buffer)
(define-key map "\C-c\C-l" 'ess-load-file)
(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\t" 'ess-complete-object-name-deprecated)
(unless (and (featurep 'emacs) (>= emacs-major-version 24))
(define-key map "\M-\t" 'comint-dynamic-complete))
(define-key map "\M-?" 'ess-list-object-completions)
(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-c." (lambda () (interactive) (message "ess-set-style moved to C-c C-e C-s. Sorry for the inconvenience")))
(define-key map "{" 'ess-electric-brace)
(define-key map "}" 'ess-electric-brace)
(define-key map "\C-\M-q" 'ess-indent-exp)
(define-key map "\C-\M-h" 'ess-mark-function)
(if (featurep 'xemacs)
(define-key map [(meta backspace)] 'backward-kill-word))
(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'.")
(defvar ess-eval-map
(let ((map (make-sparse-keymap)))
map)
"Keymap for ess-eval functions.")
(make-obsolete-variable 'ess-eval-map nil "ESS[12.09.1]")
(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-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")
(require 'ess-noweb-mode)
(easy-menu-define
ess-mode-menu ess-mode-map
"Menu for use in `ess-mode'."
'("ESS"
["What is this? (beta)" ess-mouse-me t]
["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-and-step t]
["Enter expression" ess-execute t]
"------"
("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]
("Start Process"
["R" R t]
["S" S t]
["Sqpe" Sqpe ess-microsoft-p]
["S+6-exisiting" S+6-existing ess-microsoft-p]
["SAS" SAS-menu t]
("Other"
["No other R or Sqpe versions" nil nil])
["About"
(ess-goto-info "Starting up") t]
)
("Eval visibly "
:filter ess--generate-eval-visibly-submenu ))
"------"
("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" ess-eval-region-or-line-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]
["Eval chunk" ess-eval-chunk ess-noweb-mode]
["Eval chunk and step" ess-eval-chunk-and-step ess-noweb-mode]
["Eval thread" ess-eval-thread ess-noweb-mode]
["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]
["Eval chunk" ess-eval-chunk-and-go ess-noweb-mode]
["Eval thread" ess-eval-thread-and-go ess-noweb-mode]
["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 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]
))
(defun SAS-menu ()
"Start SAS from the menu."
(interactive)
(if ess-microsoft-p
(error "SAS cannot be started this way in ESS on Windows.")
(SAS)))
(defun ess-mode-xemacs-menu ()
"Hook to install `ess-mode' menu for XEmacs (w/ easymenu)."
(if 'ess-mode
(easy-menu-add ess-mode-menu)
(easy-menu-remove ess-mode-menu)))
(if (featurep 'xemacs)
(add-hook 'ess-mode-hook 'ess-mode-xemacs-menu))
(defun ess-mode (&optional alist proc-name)
"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}
Extra binding to note: 'ESC C-\\' indent-region.
Entry to this mode runs the hooks in ess-mode-hook.
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-beginning-of-function' and `ess-end-of-function' move the point to
the beginning and end of the current ESS function.
`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 ESS grouping following point.
Variables controlling indentation style:
`ess-tab-always-indent'
Non-nil means TAB in ESS mode should always reindent the current line,
regardless of where in the line point is when the TAB command is used.
`ess-auto-newline'
Non-nil means automatically newline before and after braces inserted in S
code.
`ess-indent-level'
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-first-continued-statement-offset'
Extra indentation given to the first continued statement.
`ess-continued-statement-offset'
Extra indentation given to a substatement, such as the then-clause of an
if or body of a while.
`ess-dont-align-on-assignment'
Whether we should align continued statements based on the position of a
`<-` or `=` assignment operator.
`ess-continued-brace-offset'
Extra indentation given to a brace that starts a substatement.
This is in addition to ess-continued-statement-offset.
`ess-brace-offset'
Extra indentation for line if it starts with an open brace.
`ess-arg-function-offset'
Extra indent for internal substatements of function `foo' that called
in `arg=foo(...)' form.
If not number, the statements are indented at open-parenthesis following
`foo'.
`ess-arg-function-offset-new-line'
Extra indent for function arguments when ( is folowed by new line.
`ess-expression-offset'
Extra indent for internal substatements of `expression' that specified
in `obj <- expression(...)' form.
If not number, the statements are indented at open-parenthesis following
`expression'.
`ess-brace-imaginary-offset'
An open brace following other text is treated as if it were
this far to the right of the start of its line.
`ess-else-offset'
Extra indentation for line if it starts with `else'.
`ess-close-brace-offset'
Extra indentation for closing braces.
`ess-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. At present, predefined style are `BSD', `GNU', `K&R', `C++',
`CLB' (quoted from C language style)."
(setq alist (or alist
(buffer-local-value 'ess-local-customize-alist (current-buffer))
(error "Customise alist is not specified, nor ess-local-customize-alist is set.")))
(kill-all-local-variables)
(ess-setq-vars-local alist)
(if ess-r-args-electric-paren (define-key ess-mode-map "(" 'ess-r-args-auto-show))
(ess-write-to-dribble-buffer
(format "(ess-mode-1): ess-language=%s, ess-dialect=%s buf=%s \n"
ess-language
ess-dialect
(current-buffer)))
(ess-write-to-dribble-buffer
(format "(ess-mode-1.5): alist=%s \n" alist))
(setq major-mode 'ess-mode)
(setq mode-name (concat "ESS[" ess-language "]"))
(ess-write-to-dribble-buffer
(format "(ess-mode-1.6): editing-alist=%s \n"
ess-mode-editing-alist))
(ess-setq-vars-local ess-mode-editing-alist)
(ess-set-style ess-style t)
(use-local-map ess-mode-map)
(set-syntax-table ess-mode-syntax-table)
(make-local-variable 'indent-tabs-mode)
(setq indent-tabs-mode nil)
(put 'ess-local-process-name 'permanent-local t)
(setq mode-line-process
'(" ["
(:eval (ess--get-mode-line-indicator))
ess--local-mode-line-process-indicator
"]"))
(if (and (featurep 'emacs)
(>= emacs-major-version 24))
(add-hook 'completion-at-point-functions 'ess-filename-completion nil 'local)
(add-hook 'comint-dynamic-complete-functions 'ess-complete-filename nil 'local)
(delq t comint-dynamic-complete-functions)
)
(set (make-local-variable 'comint-completion-addsuffix)
(cons "/" ""))
(add-hook 'ess-idle-timer-functions 'ess-synchronize-dirs nil 'local)
(ess-load-extras)
(if (> emacs-major-version 21)
(run-mode-hooks 'ess-mode-hook)
(run-hooks 'ess-mode-hook))
(ess-write-to-dribble-buffer "\nFinished setting up ESS-mode.\n"))
(defun ess--get-mode-line-indicator ()
"Get `ess--mode-line-process-indicator' from process buffer.
Internal function to be used for dynamic mode-line dysplay 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"))
(defun ess-execute-in-tb ()
"Like `ess-execute', but always evaluates in temp buffer."
(interactive)
(let ((ess-execute-in-process-buffer nil))
(call-interactively 'ess-execute)))
(defvar ess-set-function-start
"^set[MGAR][Ma-z]+\\s-?("
)
(defvar ess-function-pattern nil
"Regexp to match the beginning of a function in S buffers.")
(let*
((Q "\\s\"")
(repl "\\(<-\\)?")
(Sym-0 "\\(\\sw\\|\\s_\\)")
(Symb (concat Sym-0 "+"))
(xSymb "[^ \t\n\"']+")
(_or_ "\\)\\|\\(")
(space "\\(\\s-\\|\n\\)*")
(part-1 (concat
"\\("
"\\(\\("
Q xSymb Q
_or_
"\\(^\\|[ ]\\)" Symb
"\\)\\)"))
(set-S4-exp
(concat
"^set\\(As\\|Method\\|Generic\\|GroupMethod\\|ReplaceMethod\\)("
Q xSymb Q "," space
".*"
))
(part-2 (concat
"\\|"
set-S4-exp
"\\)"
"\\(" space "\\s<.*\\s>\\)*"
space "function\\s-*("
))
)
(defvar ess-R-function-pattern
(concat part-1
"\\s-*\\(<-\\|=\\)"
part-2)
"The regular expression for matching the beginning of an R function.")
(defvar ess-S-function-pattern
(concat part-1
"\\s-*\\(<-\\|_\\|=\\)"
part-2)
"The regular expression for matching the beginning of an S function.")
)
(defun ess-beginning-of-function (&optional no-error)
"Leave (and return) the point at the beginning of the current ESS function.
If the optional argument NO-ERROR is non-nil, the function returns nil when
it cannot find a function beginning."
(interactive)
(let ((init-point (point))
(in-set-S4 nil)
beg end done)
(if (search-forward "("
(ess-line-end-position 2) t)
(forward-char 1))
(setq end (point))
(ess-write-to-dribble-buffer
(format "ess-BEG-of-fun after 'search-FWD (': Ini-pt %d, (p)-Ini-pt = %d\n"
init-point (- end init-point)))
(if (and (> end 1)
(re-search-backward
ess-set-function-start
(+ 1 (ess-line-end-position -1)) t))
(progn
(setq in-set-S4 t
beg (point))
(ess-write-to-dribble-buffer
(format " set*() function start at position %d" beg))
(goto-char end)
(search-forward
"function" (ess-line-end-position 4) t)
(search-forward "(" (ess-line-end-position) t)
)
(ess-write-to-dribble-buffer "ELSE not in setMethod() header ...\n")
)
(while (not done)
(unless
(and (re-search-backward ess-function-pattern (point-min) t)
(not (ess-inside-string-or-comment-p (point))))
(goto-char init-point)
(if no-error
(setq done t beg nil)
(error "Point is not in a function according to 'ess-function-pattern'.")
))
(unless done
(setq beg (point))
(ess-write-to-dribble-buffer
(format "\tMatch,Pt:(%d,%d),%d\n"
(match-beginning 0) (match-end 0) beg))
(setq in-set-S4 (looking-at ess-set-function-start))
(forward-list 1)
(unless in-set-S4 (forward-sexp 1))
(setq end (point))
(goto-char beg)
(setq done (and (>= end init-point) (<= beg init-point)))))
beg))
(defun ess-end-of-function (&optional beginning no-error)
"Leave the point at the end of the current ESS function.
Optional argument for location of beginning. Return '(beg end)."
(interactive)
(if beginning
(goto-char beginning)
(setq beginning (ess-beginning-of-function no-error)))
(if beginning
(let ((in-set-S4 (looking-at ess-set-function-start))
(end-pos) (npos))
(ess-write-to-dribble-buffer
(format "ess-END-of-fun: S4=%s, beginning = %d\n" in-set-S4 beginning))
(forward-list 1)
(unless in-set-S4 (forward-sexp 1))
(ess-write-to-dribble-buffer
(format "ess-END-of-fun: found #1 : %d\n" (point)))
(setq end-pos (ess-line-end-position))
(while (< (point) end-pos)
(goto-char
(min (save-excursion (forward-sexp 1) (point))
(save-excursion (forward-paragraph 1) (point)))))
(list beginning (point))
)
nil))
(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)
(unless (ess-beginning-of-function 'no-error)
(backward-paragraph)))
(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)
(unless (ess-end-of-function nil 'no-error)
(forward-paragraph)))
(defun ess-mark-function ()
"Put mark at end of ESS function, point at beginning."
(interactive)
(let ((beg (ess-beginning-of-function)))
(push-mark (point))
(ess-end-of-function beg)
(exchange-point-and-mark)))
(defun ess-narrow-to-defun ()
"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-end (ess-end-of-function)))
(narrow-to-region (nth 0 beg-end) (nth 1 beg-end)))))
(defun ess-check-modifications nil
"Check whether loading this file would overwrite some ESS objects
which have been modified more recently than this file, and confirm
if this is the case."
(when (> (length ess-change-sp-regexp) 0)
(and (buffer-file-name) ess-filenames-map
(let ((sourcemod (nth 5 (file-attributes (buffer-file-name))))
(objname))
(save-excursion
(goto-char (point-min))
(setq objname
(and
(re-search-forward
"^\\s *\"?\\(\\(\\sw\\|\\s_\\)+\\)\"?\\s *[<_]"
nil
t)
(buffer-substring (match-beginning 1)
(match-end 1)))))
(and
sourcemod
objname
(ess-modtime-gt (ess-object-modtime objname) sourcemod)
(not (y-or-n-p
(format
"The ESS object %s is newer than this file. Continue?"
objname)))
(error "Aborted"))))))
(defun ess-check-source (fname)
"If file FNAME has an unsaved buffer, offer to save it.
Returns t if the buffer existed and was modified, but was not saved."
(let ((buff (get-file-buffer fname)))
(if (not(not buff))
(let ((deleted (not (file-exists-p fname))))
(if (and deleted (not (buffer-modified-p buff)))
(with-current-buffer buff
(set-buffer-modified-p t)
(save-buffer))
(if (and (buffer-modified-p buff)
(or ess-mode-silently-save
(y-or-n-p
(format "Save buffer %s first? "
(buffer-name buff)))))
(with-current-buffer buff
(save-buffer))))
(buffer-modified-p buff)))))
(defvar ess-error-regexp "^\\(Syntax error: .*\\) at line \\([0-9]*\\), file \\(.*\\)$"
"Regexp to search for errors.")
(defun ess-parse-errors (&optional showerr reset)
"Jump to error in last loaded ESS source file.
With prefix argument, only shows the errors ESS reported."
(interactive "P")
(ess-make-buffer-current)
(let ((errbuff (get-buffer ess-error-buffer-name)))
(if (not errbuff)
(error "You need to do a load first!")
(set-buffer errbuff)
(goto-char (point-max))
(if
(re-search-backward
ess-error-regexp
nil
t)
(let* ((filename (buffer-substring (match-beginning 3) (match-end 3)))
(fbuffer (get-file-buffer filename))
(linenum
(string-to-number
(buffer-substring (match-beginning 2) (match-end 2))))
(errmess (buffer-substring (match-beginning 1) (match-end 1))))
(if showerr
(ess-display-temp-buffer errbuff)
(if fbuffer nil
(setq fbuffer (find-file-noselect filename))
(with-current-buffer fbuffer
(ess-mode)))
(pop-to-buffer fbuffer)
(goto-char (point-min)) (forward-line (1- linenum)))
(princ errmess t))
(message "Not a syntax error.")
(ess-display-temp-buffer errbuff)))))
(defun ess-electric-brace (arg)
"Insert character and correct line's indentation."
(interactive "P")
(if (and (boundp 'skeleton-pair) skeleton-pair (featurep 'skeleton))
(skeleton-pair-insert-maybe "{")
(let (insertpos)
(if (and (not arg)
(eolp)
(or (save-excursion
(skip-chars-backward " \t")
(bolp))
(if ess-auto-newline (progn (ess-indent-line) (newline) t) nil)))
(progn
(insert (if (featurep 'xemacs) (event-to-character last-command-event) last-command-event))
(ess-indent-line)
(if ess-auto-newline
(progn
(newline)
(setq insertpos (- (point) 2))
(ess-indent-line)))
(save-excursion
(if insertpos (goto-char (1+ insertpos)))
(delete-char -1))))
(if insertpos
(save-excursion
(goto-char insertpos)
(self-insert-command (prefix-numeric-value arg)))
(self-insert-command (prefix-numeric-value arg))))))
(defun ess-indent-command (&optional whole-exp)
"Indent current line as ESS code, or in some cases insert a tab character.
If `ess-tab-always-indent' is non-nil (the default), 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.
A numeric argument, regardless of its value, 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."
(interactive "P")
(if whole-exp
(let ((shift-amt (ess-indent-line))
beg end)
(save-excursion
(if ess-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)))
(if (and (not ess-tab-always-indent)
(save-excursion
(skip-chars-backward " \t")
(not (bolp))))
(insert-tab)
(funcall indent-line-function))))
(defun ess-indent-or-complete ()
"When region is selected indent the region, otherwise, if
`ess-tab-complete-in-script' is non-nil, try to indent, if code
is already indented, complete instead.
The default of `ess-tab-complete-in-script' is nil. Also see
`ess-first-tab-never-complete'."
(interactive)
(if (use-region-p)
(indent-region (region-beginning) (region-end))
(let ((shift (ess-indent-command)))
(when (and ess-tab-complete-in-script
(numberp shift)
(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.")))
))
(if (and (featurep 'emacs) (>= emacs-major-version 24))
(completion-at-point)
(comint-dynamic-complete)
)))))
(defun ess-indent-exp ()
"Indent each line of the ESS grouping following point."
(interactive)
(save-excursion
(let ((start (point))
(end (ignore-errors (forward-sexp 1) (point))))
(when end
(indent-region start end)))))
(defun ess-comment-indent ()
(if (or (looking-at "###")
(and (looking-at "#!") (= 1 (line-number-at-pos))))
(current-column)
(if (looking-at "##")
(let ((tem (ess-calculate-indent)))
(if (listp tem) (car tem) tem))
(skip-chars-backward " \t")
(max (if (bolp) 0 (1+ (current-column)))
comment-column))))
(defun ess-indent-line ()
"Indent current line as ESS code.
Return the amount the indentation changed by."
(if (fboundp ess-indent-line-function)
(funcall ess-indent-line-function)
(let ((indent (ess-calculate-indent nil))
beg shift-amt
(case-fold-search nil)
(pos (- (point-max) (point))))
(beginning-of-line)
(setq beg (point))
(cond ((eq indent nil)
(setq indent (current-indentation)))
(t
(skip-chars-forward " \t")
(cond ((and ess-fancy-comments
(or (looking-at "###")
(and (looking-at "#!") (= 1 (line-number-at-pos)))))
(setq indent 0))
((and ess-fancy-comments
(looking-at "#") (not (looking-at "##")) (not (looking-at "#'")))
(setq indent comment-column))
(t
(if (eq indent t) (setq indent 0))
(if (listp indent) (setq indent (car indent)))
(cond ((and (looking-at "else\\b")
(not (looking-at "else\\s_")))
(setq indent (save-excursion
(ess-backward-to-start-of-if)
(+ ess-else-offset (current-column)))))
((= (following-char) ?})
(setq indent
(+ indent
(- ess-close-brace-offset ess-indent-level))))
((= (following-char) ?{)
(setq indent (+ indent ess-brace-offset))))))))
(skip-chars-forward " \t")
(setq shift-amt (- indent (current-column)))
(if (zerop shift-amt)
(if (> (- (point-max) pos) (point))
(goto-char (- (point-max) pos)))
(delete-region beg (point))
(indent-to indent)
(when (> (- (point-max) pos) (point))
(goto-char (- (point-max) pos))))
shift-amt)))
(defun ess-looking-at-last-open-paren-p ()
(looking-at "[[:blank:]]*([[:blank:]]*\\($\\|#\\)"))
(defun ess-calculate-indent--closing-paren ()
(search-forward ")")
(backward-sexp)
(if (ess-looking-at-last-open-paren-p)
(current-indentation)
(+ (current-column) 1)))
(defun ess-calculate-indent--default (&optional parse-start)
(let ((indent-point (point))
(beginning-of-defun-function nil)
(open-paren-in-column-0-is-defun-start t)
(case-fold-search nil)
state ind
containing-sexp)
(if parse-start
(goto-char parse-start)
(beginning-of-defun))
(while (< (point) indent-point)
(setq parse-start (point))
(setq state (parse-partial-sexp (point) indent-point 0))
(setq containing-sexp (car (cdr state))))
(cond ((or (nth 3 state) (nth 4 state))
(nth 4 state))
((and (null containing-sexp)
(= (following-char) ?\{))
0)
((setq ind (ess--continued-block-statement containing-sexp))
(+ ind ess-indent-level))
((setq ind (ess--continued-statement containing-sexp))
(+ ind ess-continued-statement-offset))
((null containing-sexp)
0)
((/= (char-after containing-sexp) ?{)
(goto-char containing-sexp)
(let ((bol (line-beginning-position)))
(cond ((and (numberp ess-expression-offset)
(re-search-backward "[ \t]*expression[ \t]*(" bol t))
(beginning-of-line)
(skip-chars-forward " \t")
(+ (current-column) ess-expression-offset))
((and (numberp ess-arg-function-offset)
(re-search-backward
"=[ \t]*\\s\"*\\(\\w\\|\\s_\\)+\\s\"*[ \t]*"
bol t))
(forward-sexp -1)
(+ (current-column) ess-arg-function-offset))
((and (numberp ess-arg-function-offset-new-line)
(ess-looking-at-last-open-paren-p))
(forward-sexp -1)
(+ (current-column) ess-arg-function-offset-new-line))
((and (listp ess-arg-function-offset-new-line)
(numberp (car ess-arg-function-offset-new-line))
(ess-looking-at-last-open-paren-p))
(beginning-of-line)
(skip-chars-forward " \t")
(+ (current-column) (car ess-arg-function-offset-new-line)))
(t
(progn (goto-char (1+ containing-sexp))
(current-column))))))
(t
(goto-char indent-point)
(ess-backward-to-noncomment containing-sexp)
(while (eq (preceding-char) ?\,)
(ess-backward-to-start-of-continued-exp containing-sexp)
(beginning-of-line)
(ess-backward-to-noncomment containing-sexp))
(forward-line 1)
(if (ess--continued-statement)
(progn
(ess-backward-to-start-of-continued-exp containing-sexp)
(+ ess-continued-statement-offset (current-column)
(if (save-excursion (goto-char indent-point)
(skip-chars-forward " \t")
(eq (following-char) ?{))
ess-continued-brace-offset 0)))
(goto-char containing-sexp)
(or
(save-excursion
(forward-char 1)
(while (progn (skip-chars-forward " \t\n")
(looking-at "#"))
(forward-line 1))
(and (< (point) indent-point)
(current-column)))
(+ (if (and (bolp) (zerop ess-indent-level))
(+ ess-brace-offset ess-continued-statement-offset)
ess-indent-level)
(progn (skip-chars-backward " \t")
(if (bolp) 0 ess-brace-imaginary-offset))
(progn
(if (eq (preceding-char) ?\))
(forward-sexp -1))
(current-indentation)))))))))
(defun ess-calculate-indent (&optional parse-start)
"Return appropriate indentation for current line as ESS code.
In usual case returns an integer: the column to indent to.
Returns nil if line starts inside a string, t if in a comment."
(save-excursion
(beginning-of-line)
(cond
((looking-at "[[:blank:]]*\)")
(ess-calculate-indent--closing-paren))
((looking-at "[[:blank:]]*\,")
(let ((indent (save-excursion
(ess-calculate-indent--default parse-start)))
(unindent (progn (skip-chars-forward " \t")
(skip-chars-forward ", \t"))))
(- indent unindent)))
(t
(ess-calculate-indent--default parse-start)))))
(defun ess--continued-block-statement (&optional containing-sexp)
"If a continuation line of a block, return and indent of this line, otherwise nil."
(save-excursion
(beginning-of-line)
(ess-backward-to-noncomment containing-sexp)
(cond ((memq (preceding-char) '(nil ?\, ?\; ?\} ?\{ ?\] ?\())
nil)
((looking-back "<-")
(current-indentation))
((= (preceding-char) ?\))
(ignore-errors
(forward-sexp -2)
(cond ((looking-at "for\\b[ \t]*(\\|while\\b[ \t]*(")
(current-column))
((looking-at "if\\b[ \t]*(")
(when (looking-back "\\belse[ \t]*")
(backward-sexp 1))
(current-column))
((looking-at "function\\b[ \t]*(")
(current-indentation)))))
((progn (ignore-errors (forward-sexp -1))
(looking-at "else\\b\\|repeat\\b\\([:blank:]*\|\\&\\)"))
(let ((col (current-column)))
(skip-chars-backward " \t")
(if (or (bolp)
(eq (preceding-char) ?\;))
(- col (current-column))
(when (eq ?} (preceding-char))
(- (current-column) 1)))))
)))
(defun ess--continued-statement (&optional containing-sexp)
"If a continuation line, return an indent of this line, otherwise nil."
(save-excursion
(beginning-of-line)
(ess-backward-to-noncomment containing-sexp)
(let ((eol (point)))
(cond ((memq (preceding-char) '(nil ?\, ?\; ?\} ?\{ ?\] ?\())
nil)
((ignore-errors (up-list -1)
(looking-back "if[ \t]*"))
nil)
((progn (goto-char eol)
(skip-chars-backward " \t")
(or (and (> (current-column) 1)
(and (not (looking-back "<-"))
(looking-back "[-:+*/><=&|]")))
(and (> (current-column) 3)
(progn (backward-char 3)
(looking-at "%[^ \t]%")))))
(let ((first-indent
(or (and (/= ess-first-continued-statement-offset 0)
(null (ess--continued-statement containing-sexp))
ess-first-continued-statement-offset)
0)))
(+ first-indent
(if containing-sexp
(if (> (point-at-bol) containing-sexp)
(current-indentation)
(goto-char containing-sexp)
(1+ (current-column)))
(+ (current-indentation))))))
(t nil)))))
(defun ess-backward-to-noncomment (lim)
(let ((lim (or lim (point-min)))
opoint stop)
(while (not stop)
(skip-chars-backward " \t\n\f" lim)
(setq opoint (point))
(beginning-of-line)
(search-forward "#" opoint 'move)
(skip-chars-backward " \t#")
(setq stop (or (/= (preceding-char) ?\n) (<= (point) lim)))
(if stop (point)
(beginning-of-line)))))
(defun ess-backward-to-start-of-continued-exp (lim)
(if (= (preceding-char) ?\))
(forward-sexp -1))
(beginning-of-line)
(if (<= (point) lim)
(goto-char (1+ lim)))
(skip-chars-forward " \t"))
(defun ess-backward-to-start-of-if (&optional limit)
"Move to the start of the last ``unbalanced'' 'if' or 'else if'
expression."
(let ((beginning-of-defun-function nil))
(or limit (setq limit (save-excursion (beginning-of-defun) (point))))
(let ((if-level 1)
(case-fold-search nil))
(while (not (zerop if-level))
(backward-sexp 1)
(cond ((looking-at "else\\b")
(setq if-level (1+ if-level)))
((looking-at "if\\b")
(when (looking-back "\\belse[[:blank:]]*")
(backward-sexp 1))
(setq if-level (1- if-level)))
((< (point) limit)
(setq if-level 0)
(goto-char limit)))))))
(defun ess-set-style (&optional style quiet)
"Set up the `ess-mode' style variables from the `ess-style' variable
or if STYLE argument is given, use that. It makes the ESS indentation
style variables buffer local."
(interactive)
(let ((ess-styles (mapcar 'symbol-name (mapcar 'car ess-style-alist))))
(if (called-interactively-p 'any)
(setq style
(intern (ess-completing-read "Set ESS mode indentation style"
ess-styles nil t nil nil ess-default-style))))
(setq style (or style ess-style))
(make-local-variable 'ess-style)
(if (memq (symbol-name style) ess-styles)
(setq ess-style style)
(error (format "Bad ESS style: %s" style)))
(if (not quiet)
(message "ESS-style: %s" ess-style))
(mapc (lambda (ess-style-pair)
(make-local-variable (car ess-style-pair))
(set (car ess-style-pair)
(eval (cdr ess-style-pair))))
(cdr (assq ess-style ess-style-alist)))
ess-style))
(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: ")
(if (ess-ddeclient-p)
(list (read-string "Object to edit: "))
(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-customize-alist)
(apply ess-source-directory nil)))))
(filename (concat dirname (format ess-dump-filename-template object)))
(old-buff (get-file-buffer filename)))
(if (file-exists-p (directory-file-name dirname)) nil
(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)))
(if current-prefix-arg
(ess-dump-object object filename)
(if old-buff
(progn
(pop-to-buffer old-buff)
(message "Popped to edit buffer."))
(if (file-exists-p filename)
(progn
(ess-find-dump-file-other-window filename)
(message "Read %s" filename))
(ess-dump-object object filename))))))
(defun ess-dump-object (object filename)
"Dump the ESS object OBJECT into file FILENAME."
(let ((complete-dump-command (format inferior-ess-dump-command
object filename)))
(if (file-writable-p filename) nil
(error "Can't dump %s as %f is not writeable." object filename))
(if (ess-ddeclient-p)
(ess-dump-object-ddeclient object filename)
(if (get-file-buffer filename)
(kill-buffer (get-file-buffer filename)))
(ess-command complete-dump-command)
(message "Dumped in %s" filename)
(ess-find-dump-file-other-window filename)
(indent-region (point-min-marker) (point-max-marker) nil)
(set-buffer-modified-p nil)
(unless ess-keep-dump-files
(make-local-variable 'make-backup-files)
(setq make-backup-files nil))
(if (eq ess-keep-dump-files 'check)
(setq ess-keep-dump-files nil))
(if 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."
(if (file-exists-p filename) nil
(ess-write-to-dribble-buffer
(format "%s does not exist. Bad dump, starting fresh." filename)))
(find-file-other-window filename)
(ess-mode ess-customize-alist)
(auto-save-mode 1)
(setq ess-local-process-name ess-current-process-name)
(if ess-function-template
(progn
(goto-char (point-max))
(if (re-search-backward ess-dumped-missing-re nil t)
(progn
(replace-match ess-function-template t t)
(set-buffer-modified-p nil)
(goto-char (point-min))
(condition-case nil
(down-list 1)
(error nil)))))))
(defun ess-dump-object-into-edit-buffer-other-frame (object)
"Edit an ESS object in its own frame."
(switch-to-buffer-other-frame (ess-dump-object-into-edit-buffer object)))
(provide 'ess-mode)