;;;; Emacs Speaks Statistics: supplemental functions (Sven Hartenstein)

;;;;== "http://www.svenhartenstein.de/emacs-ess.php

;;; (Last update: 2007-04-02)

;; I wrote some Emacs lisp code that I find very useful as a
;; little supplement to the fantastic <a href="http://ess.r-project.org/">ESS mode</a> that makes <a href="http://www.gnu.org/software/emacs/emacs.html">Emacs</a> interact
;; with <a href="http://www.r-project.org/">R, the free software for
;; statistical computing</a>.

;; Feel free to use the code (i.e. copy it into your ~/.emacs file and
;; modify it if you want to), but be aware that it comes with absolutely
;; no warranty. If you encounter any problems, please <a href="http://www.svenhartenstein.de/contact.php">let me know</a>. In fact, any feedback is highly
;; appreciated.

;;; Showing a function's arguments and their default values
;;; -------------------------------------------------------

;; *** Screenshots

;; [.............]

;; *** Requirements

;; - ESS mode must be loaded and running.</li>
;; - A R process must be running within ESS.</li>


;; *** The Emacs lisp function

;; The following function should be called when point (text cursor) is
;; between two parentheses of a R function call (see screenshots above).
;; It will then (invisibly) query R for which arguments the respective
;; function knows as well as their default values and show the
;; result.

;; <pre>

;; MM: The following is pretty error-prone
;; --     `` uplist: Scan-error: "Unbalanced parentheses", ... ''
;;    when used via <F2> {i.e. anywhere in R-mode}, but works like a charm
;;    when used via electric "("
(defun my-r-show-args ()
  "Show arguments and their default values of function in minibuffer."
  (interactive "*")
  (let ((pointposition (point)))
    (up-list -1)
    (let ((posend (point)))
	(backward-sexp 1)
	(setq object (buffer-substring-no-properties posend (point)))
	(ess-command (concat "try(args(" object "), silent=TRUE)\n")
		     (get-buffer-create "*my-r-args-completion*"))
	)
    (goto-char pointposition)
    )
  (with-current-buffer "*my-r-args-completion*"
    (goto-char (point-min))
    (if (equal nil (search-forward "function" 10 t))
	  (message my-r-noargsmsg)
	(goto-char (point-min))
	(zap-to-char 1 (string-to-char "("))
	(goto-char (point-max))
	(zap-to-char -1 (string-to-char ")"))
	(delete-trailing-whitespace)
	(if (equal my-r-show-as "tooltip")
	    (tooltip-show (concat "ARGS: " (buffer-string)))
	  (message (concat "ARGS: " (buffer-string)))
	  )))
  (kill-buffer "*my-r-args-completion*")
)

(defvar my-r-noargsmsg ""
  "The message that is returned if my-r-show-args does not find a list
of arguments.")
(defvar my-r-show-as nil
  "How my-r-show-args should show the argument list. Possible values
are: 'message' (the default) or 'tooltip'.")
;;</pre>

;;;*** Variables

;; As you can see in the above code there are two variables that
;; control the behavior of the above function. First,
;; <tt>my-r-noargsmsg</tt> holds the message that is returned, if R does
;; not know about arguments (e.g. because the function itself is not
;; known). Second, <tt>my-r-show-as</tt> determines, how (where) the
;; information is displayed. Possible values are nil for using Emacs'
;; echo area (this is the minibuffer's place) or 'tooltip' for little
;; tooltip windows that will show up at the mouse point.

;; It might be preferable to not change the variables' values in the
;; above code but as follows:

;; <pre>
(setq my-r-noargsmsg "") ; no message when no arguments found
(setq my-r-show-as "tooltip") ; show arguments as tooltip
;; </pre>


;;;*** Calling the function (automatically)

;; You can bind the <tt>my-r-show-args</tt> function to a key and/or
;; let Emacs call it automatically whenever you type an opening
;; parenthesis ("(").
;; <pre>
;; bind my-r-show-args to F2
(define-key ess-mode-map [f2] 'my-r-show-args)

;; call my-r-show-args automatically  on "(" :
(define-key ess-mode-map "("
  '(lambda () (interactive)
     (skeleton-pair-insert-maybe nil)
     (my-r-show-args)))
;; </pre>


;; *** Setting the tooltip position

;; Unfortunately, tooltips are by default shown at the mouse pointer's
;; position, which is not necessarily where you are looking at. If you
;; use Emacs' mouse-avoidance-mode with option "banish" then the mouse
;; pointer will automatically be put at the upper right corner of the
;; Emacs window so that you know where to look for the tooltip. Emacs
;; also allows for setting the tooltip position relative to the upper
;; left corner of your screen. If you know how to let Emacs determine the
;; point (text cursor) position in pixels from the upper left corner of
;; the screen, please let me know. It would then be possible to show the
;; tooltip near the point, which I would consider preferably.

;; <pre>
;; Put mouse away when using keyboard
(mouse-avoidance-mode 'banish)

;; Set the tooltip position in absolute pixels from the upper left
;; corner of the screen
(setq tooltip-frame-parameters
  '((name . "tooltip")
    (left . 20)
    (top . 20)))
;; </pre>


;; *** Todo

;; These are things that I would like to see improved. Please let me
;; know if you know how it could be done.

;; <ul>
;; <li>As mentioned above, I would like to place the tooltip near the
;; point (text cursor) but I do not see how this could be done.</li>
;; <li>Both the message in the echo area and the tooltip automatically
;; disappear as soon as a key is pressed. That is, you will need to call
;; the function again if you have entered the first parameter(s) and
;; wonder what additional parameters are possible. I would prefer the
;; information to be shown, say, five seconds or so.</li>
;; </ul>

;;; Showing R's help on the current function
;;; ----------------------------------------


;; *** The Emacs lisp function

;; ESS already ships with a handy possibility to call R's help()
;; function (simply press C-c C-v). It uses the last function name before the point or
;; so to run help on. I however prefer the following code, which (a) also
;; deals nicely with nested function calls and (b) opens the help buffer
;; in the current window so that I do not have to change window focus to
;; scroll the help buffer and to return to the buffer from which I called
;; for help.

;; <pre>

(defun my-r-call-help ()
  "Call R's help page for the function in which's head the point is.
 (Kill the help buffer by pressing 'k' to return to the transcript
 buffer in the same window.)"
  (interactive "*")
  (let* ((pointposition (point))
	 (posend (progn
		   (up-list -1)	; move backward out of 1 level of "(..)"
		   (point)))
	 (help-obj (progn
		     (backward-sexp 1)
		     (buffer-substring-no-properties posend (point))))
	 (start-buffer (current-buffer)))
    (goto-char pointposition)
    (other-window 1)
    (ess-display-help-on-object help-obj)
    (other-window 1)
    ;;MM: this does NOTHING: (setq helpbuffer (current-buffer))
    ))

;; </pre>

;; *** Defining a keystroke

;;<pre>
(define-key ess-mode-map [f1] 'my-r-call-help)
;; </pre>					;


;;; Some additional tips
;;; --------------------

;; Here some further tips, not related to the above function.

;; bind this useful function to a less painful key (M-TAB by default
;; tries to expand a filename, but I do hardly need that)
;; MM: The above is not true: The following has been *default* for a long time!
(when nil
  (define-key ess-mode-map (kbd "M-TAB") 'ess-complete-object-name)
)


(when nil ;; not doing this; not from this file in any case :

  (setq inferior-ess-program "R")
  (setq ess-ask-for-ess-directory nil)
  (setq ess-local-process-name "R"); ?

  (setq comint-scroll-to-bottom-on-input t)
  (setq comint-scroll-to-bottom-on-output t)
  (setq comint-move-point-for-output t);; <<- not there in Emacs 21.x.x
)


;; See also the <a href="http://www.emacswiki.org/cgi-bin/wiki/EmacsSpeaksStatistics">ESS
;; page of the Emacs wiki</a> for a cool tip on how to send code to the R
;; process intelligently with Shift-Enter.

;; </div>

